commit 91736529d5b747f4810bfd920058465c209f42ac Author: geos_one Date: Sun Aug 10 12:35:43 2025 +0200 New upstream version 2.0pre9.2 diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..c701875 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +patreon: stsp diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..5f52617 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,51 @@ +--- +name: Bug report +about: Create a bug report +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A description of what the problem is. + +**To Reproduce** +Steps to reproduce the behaviour: + +**Attach program/game binaries or provide an URL** +If the problem needs specific DOS files to reproduce, please attach +or provide the download URL. + +**Attach the log** +It is located in ~/.dosemu/boot.log +Unless you get the plain crash of dosemu2, +you may need to enable some logging flags +to make your report more useful. +See description of -D option in `man dosemu.bin`. + +**A regression?** +If you happened to know this problem didn't +exist on dosemu1 or some earlier versions of +dosemu2, please write. + +**dosemu2 origins** +Please describe where do you get dosemu2 +from (PPA, COPR, git sources). In case of a +source build, please describe any configure-time +customizations. In case of binary packages, +please specify your distribution and make sure +to install the debuginfo packages before creating +the log file. + +**Additional info** +Please write here if you did any dosemu2 setup +customizations, like installing any custom DOS +or command.com, or altering the dosemu2 +configuration settings. Note that freedos, even +from dosemu-freedos package of dosemu1, counts +as a custom DOS and should be mentioned here. +Also make a note if it is a protected or real-mode +program. If you suspect any particular dosemu2 +component (DPMI, PIC, PIT etc) to be responsible +for the problem, please write. diff --git a/.github/ISSUE_TEMPLATE/feature-request-or-question.md b/.github/ISSUE_TEMPLATE/feature-request-or-question.md new file mode 100644 index 0000000..87751c6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request-or-question.md @@ -0,0 +1,10 @@ +--- +name: Feature request or question +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml new file mode 100644 index 0000000..c9887c4 --- /dev/null +++ b/.github/workflows/ci-build.yml @@ -0,0 +1,62 @@ +name: Build +on: + workflow_call: + inputs: + jobtype: + required: true + type: string + subtype: + required: true + type: string + runtype: + required: true + type: string + +jobs: + build: + + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v4 + + - name: package install + run: ./ci_prereq.sh + + - name: build + env: + JOBTYPE: ${{ inputs.jobtype }} + SUBTYPE: ${{ inputs.subtype }} + RUNTYPE: ${{ inputs.runtype }} + run: ./ci_build.sh + + - name: cache binaries + uses: actions/cache@v4 + env: + cache-name: test-binaries + with: + path: ~/cache + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + + - name: Enable KVM group perms + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: test + id: test + env: + JOBTYPE: ${{ inputs.jobtype }} + SUBTYPE: ${{ inputs.subtype }} + RUNTYPE: ${{ inputs.runtype }} + run: ./ci_test.sh + + - name: upload failure logs + if: ${{ always() && (steps.test.outcome == 'failure') }} + uses: actions/upload-artifact@v4 + with: + name: failure-logs + path: test_* diff --git a/.github/workflows/ci-manual-asan.yml b/.github/workflows/ci-manual-asan.yml new file mode 100644 index 0000000..f254d89 --- /dev/null +++ b/.github/workflows/ci-manual-asan.yml @@ -0,0 +1,12 @@ +name: Manual ASAN +on: + workflow_dispatch: + +jobs: + manual_asan: + name: Manual (ASAN) + uses: ./.github/workflows/ci-build.yml + with: + jobtype: 'manual' + subtype: 'asan' + runtype: 'simple' diff --git a/.github/workflows/ci-manual-full.yml b/.github/workflows/ci-manual-full.yml new file mode 100644 index 0000000..b83c128 --- /dev/null +++ b/.github/workflows/ci-manual-full.yml @@ -0,0 +1,12 @@ +name: Manual FULL +on: + workflow_dispatch: + +jobs: + manual_full: + name: Manual (FULL) + uses: ./.github/workflows/ci-build.yml + with: + jobtype: 'manual' + subtype: '' + runtype: 'full' diff --git a/.github/workflows/ci-manual-normal.yml b/.github/workflows/ci-manual-normal.yml new file mode 100644 index 0000000..53f62ab --- /dev/null +++ b/.github/workflows/ci-manual-normal.yml @@ -0,0 +1,12 @@ +name: Manual Normal +on: + workflow_dispatch: + +jobs: + manual_normal: + name: Manual (Normal) + uses: ./.github/workflows/ci-build.yml + with: + jobtype: 'manual' + subtype: '' + runtype: 'normal' diff --git a/.github/workflows/ci-master.yml b/.github/workflows/ci-master.yml new file mode 100644 index 0000000..74c81bc --- /dev/null +++ b/.github/workflows/ci-master.yml @@ -0,0 +1,73 @@ +name: Master + +on: + schedule: + - cron: '23 23 * * 0' # 23:23 Every Sunday + pull_request: + types: + - opened + - reopened + - synchronize + push: + +jobs: +# scheduled_asan: +# name: Scheduled (ASAN) +# if: (github.event_name == 'schedule' && +# github.repository_owner == 'dosemu2') + +# uses: ./.github/workflows/ci-build.yml +# with: +# jobtype: 'schedule' +# subtype: 'asan' +# runtype: 'simple' + + scheduled_full: + name: Scheduled (FULL) + if: (github.event_name == 'schedule' && + github.repository_owner == 'dosemu2') + + uses: ./.github/workflows/ci-build.yml + with: + jobtype: 'schedule' + subtype: '' + runtype: 'full' + + triggered: + name: Triggered + if: (github.event_name != 'schedule' && + contains(github.event.head_commit.message, '[skip ci]') == false && + contains(github.event.head_commit.message, '[full ci]') == false && + contains(github.event.head_commit.message, '[asan ci]') == false) + + uses: ./.github/workflows/ci-build.yml + with: + jobtype: 'triggered' + subtype: '' + runtype: 'normal' + + triggered_asan: + name: Triggered (ASAN) + if: (github.event_name != 'schedule' && + contains(github.event.head_commit.message, '[skip ci]') == false && + contains(github.event.head_commit.message, '[full ci]') == false && + contains(github.event.head_commit.message, '[asan ci]') == true) + + uses: ./.github/workflows/ci-build.yml + with: + jobtype: 'triggered' + subtype: 'asan' + runtype: 'simple' + + triggered_full: + name: Triggered (FULL) + if: (github.event_name != 'schedule' && + contains(github.event.head_commit.message, '[skip ci]') == false && + contains(github.event.head_commit.message, '[full ci]') == true && + contains(github.event.head_commit.message, '[asan ci]') == false) + + uses: ./.github/workflows/ci-build.yml + with: + jobtype: 'triggered' + subtype: '' + runtype: 'full' diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..86f9af1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +/install-sh +/config.status +/config.sub +*.d +!dosrc.d +*.o +configure +config.log +aclocal.m4 +autom4te.cache +Makefile.conf +plugin_enable +*.map +*.xxd +_*.h.in +_*.h +*.hh +config.hh.in +*.yy.c +*.zz.c +parser.c +parser.h +parser.output +*.1 +dosemu_c.c +global_c.c +etc/Xfonts +/etc/ttf/.uuid +.tstamp +/2.* +/bin +/commands +/lib +dosemu.desktop +dosdebug +hdinfo +bios_symbols.c +keysym_attributes.c +make_attributes diff --git a/.tito/packages/.readme b/.tito/packages/.readme new file mode 100644 index 0000000..b9411e2 --- /dev/null +++ b/.tito/packages/.readme @@ -0,0 +1,3 @@ +the .tito/packages directory contains metadata files +named after their packages. Each file has the latest tagged +version and the project's relative directory. diff --git a/.tito/packages/dosemu2 b/.tito/packages/dosemu2 new file mode 100644 index 0000000..5a4bd26 --- /dev/null +++ b/.tito/packages/dosemu2 @@ -0,0 +1 @@ +2.0pre9-2 ./ diff --git a/.tito/tito.props b/.tito/tito.props new file mode 100644 index 0000000..9f6fd2b --- /dev/null +++ b/.tito/tito.props @@ -0,0 +1,6 @@ +[buildconfig] +builder = tito.builder.Builder +tagger = tito.tagger.VersionTagger +changelog_do_not_remove_cherrypick = 0 +changelog_format = %s (%ae) + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..11dd5fc --- /dev/null +++ b/.travis.yml @@ -0,0 +1,28 @@ +language: c + +os: linux +dist: focal + +if: type = cron + +cache: + directories: + - $HOME/cache + +before_install: + - ./ci_prereq.sh + +install: + - ./ci_build.sh + +before_script: +# this acl no longer persists in Focal due to /dev/kvm management by systemd +# - sudo setfacl -m u:${USER}:rw /dev/kvm +# so we have to do this instead + - sudo chmod 666 /dev/kvm + +script: + - ./ci_test.sh + +after_script: + - echo "after_script" diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..e744b76 --- /dev/null +++ b/BUGS @@ -0,0 +1,2 @@ +Please report bugs to the Bug Tracking System for DOSEMU2 at +https://github.com/dosemu2/dosemu2/issues diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..eba1266 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,76 @@ +# How to contribute + +## Submitting changes + +Please send a +[GitHub Pull Request to dosemu2](https://github.com/dosemu2/dosemu2/pull/new/master) +with a clear list of what you've done (read more about +[pull requests](http://help.github.com/pull-requests/)). Please follow our +coding conventions (below), make sure all of your commits are atomic (one +feature per commit) and ensure at every commit in a series the code compiles. + +Always write a clear log message for your commits. One-line messages are fine +for small changes, but bigger changes should look like this: + + $ git commit -m "A brief summary of the commit + > + > A paragraph describing what changed and its impact." + +## Coding conventions +Since dosemu2 has a long history, there are many different coding styles in +use. We'd like to standardise on a subtle variant of the Kernighan & Ritchie +style, but mass conversion is not really an option as we don't want to obscure +the commit history. New code should follow the style below, but old code +should be left in the style it is and modifications to it should be in the +same style. If an existing function is so horridly formatted as to make it +unreadable or it will be 80% changed, then it should be reformatted to the new +style and submitted as a separate commit before any changes are done. Changes +are then made in a followup commit. This will allow reviewers to view +significant changes outside of the reformatting. + +Coding style is that which would be produced by +`indent -kr -nut -i2 -cli2` + +```C +/* + * large block comments + * second line + */ + +int main(int argc, char *argv[]) +{ + int x, y, z = 1; + + switch (z) { + case 1: + printf("hello\n"); + break; + + case 0: + printf("there\n"); + break; + + default: + printf("unmatched switch case 0x%02x\n", z); + return 1; + } + + while (x == y) { + a(); + b(); + + if (x) + single_line_1(); + + if (z) { + single_line_2(); + } else { + multiple_line1(); + multiple_line2(); + multiple_line3(); + } + } + + return 0; // no round brackets +} +``` diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..6e9dd7c --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING.DOSEMU, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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. diff --git a/COPYING.DOSEMU b/COPYING.DOSEMU new file mode 100644 index 0000000..7b16464 --- /dev/null +++ b/COPYING.DOSEMU @@ -0,0 +1,364 @@ + + Copyright of DOSEMU2, October 2014 + ================================== + +1. + 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +2. All dosemu2 C sources (*.c) that have no explicit copyright statement, + are copyrighted under GPLv2. Headers (*.h) without explicit + copyrights are copyrighted similar to their respective .c + counterparts, or, if no .c file with that name - under GPLv2+. + +3. Parts of the code not covered by the GPL are marked explicitly + within the code, and/or their copyrights are at the end of this + file. + +4. There are no restrictions to run any (proprietary or free) DOS software + under dosemu2, unless the license of that software says otherwise. + + +--- GPLv2 (used as a default license in older dosemu releases) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +--- The QEMU emulator, before July 2013 + (src/base/dev/pic/*, src/base/dev/ne2k/ne2000.c) + +The following points clarify the QEMU license: + +1) QEMU as a whole is released under the GNU General Public License + +2) Parts of QEMU have specific licenses which are compatible with the +GNU General Public License. Hence each source file contains its own +licensing information. + +Many hardware device emulation sources are released under the BSD license. + +3) The Tiny Code Generator (TCG) is released under the BSD license + (see license headers in files). + +4) QEMU is a trademark of Fabrice Bellard. + +Fabrice Bellard. + +dosemu2 note: in July 2013 QEMU license was updated to state GPLv2-only: +-1) QEMU as a whole is released under the GNU General Public License ++1) QEMU as a whole is released under the GNU General Public License, ++version 2. + +Therefore we take care to remove all changes done since that date, +while porting QEMU sources to dosemu2. + +--- The Mach DOS Emulator (mfs.c, emm.c, emufs.s) + +Copyright (c) 1991 Carnegie Mellon University +All Rights Reserved. + +Permission to use, copy, modify and distribute this software and its +documentation is hereby granted, provided that both the copyright +notice and this permission notice appear in all copies of the +software, derivative works or modified versions, and any portions +thereof, and that both notices appear in supporting documentation. + +CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" +CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR +ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + +Carnegie Mellon requests users of this software to return to + + Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + School of Computer Science + Carnegie Mellon University + Pittsburgh PA 15213-3890 + +any improvements or extensions that they make and grant Carnegie Mellon +the rights to redistribute these changes. + +--- XFree86 (src/base/mouse/mouseint.c) + +Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. +Copyright 1993 by David Dawes + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the names of Thomas Roell and David Dawes not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. Thomas Roell +and David Dawes makes no representations about the suitability of this +software for any purpose. It is provided "as is" without express or +implied warranty. + +THOMAS ROELL AND DAVID DAWES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL THOMAS ROELL OR DAVID DAWES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +--- Doug Lea's malloc in src/base/lib/misc/dlmalloc.c + +This is a version (aka dlmalloc) of malloc/free/realloc written by +Doug Lea and released to the public domain, as explained at +http://creativecommons.org/licenses/publicdomain. Send questions, +comments, complaints, performance data, etc to dl@cs.oswego.edu + +--- The VGA fonts in src/base/dev/vga/vgafonts.c (copyleft_vgafonts.txt) + +This *compilation* is (c) Copyright 1991,1992 Joseph (Yossi) Gil. +Permission is granted to use and redistribute the files comprising +this collection in any way (including conversion to another format), +provided that my name and addresses and this notice is preserved. + +Simple (dare I say trivial?) bitmapped screen fonts such as the ones +included in this collection cannot be copyrighted. In general, one can +only copyright programs that generate fonts. This is why postscript +fonts are copyrightable. For more details refer to discussions various +"legal" newsgroups. In addition, I have included a relevant excerpt +from the FAQ of comp.fonts at the bottom of this document. + +No one can claim any copyright on the fonts in this archive. They +have been collected from numerous sources. Legally speaking, you are +*free* to do with the individual fonts whatever you like. Individual +fonts are in the public domain. I do ask that you will kindly refrain +from causing confusion by distributing modified versions of the fonts +contained in this collection. + +Please send any all your EGA/VGA text mode fonts contributions to me +rather than distributing a modified version of this collection. I +will add your fonts to the next edition of this collection and happily +acknowledge your help. Your cooperation will enable us all to benefit +from your contribution. See the file LOOKING4.TXT for more details. + +I am trying to keep track of the origins of these fonts. See the file +FONTORIG.TXT. Unfortunately, I only started to record this information +on version 1.2. Records of origin of earlier fonts are missing. +If you know the origin of any of the fonts here, please drop me a note. + +Staring on version 1.6 the collection also includes some of the +miscellaneous utilities which I use for preparing it. Among these +you will find programs for loading, viewing, trimming and otherwise +manipulating the fonts. These utilities are also distributed as a +separate archive called fntutlXX.ZIP where XX is the version number. +All the utilities require no shareware payment. Restrictions on +distribution and usage are only to the extent necessary to protect +the free distribution. + +I see this is as my pleasant duty to pay tribute to the following +individuals who communicated and contributed to this archive: + + Dov Grobgeld + Angelos Karageorgiou , + Alexandre (Alex) Khalil <9999SC01@DT3.DT.UH.EDU>, + Patrick Arzul + Mike Threepoint + Glaude David [Glu] + Jean-Marc Lasgouttes + Itamar Even-Zohar + A.Weeks%bath.ac.uk@ib.rl.ac.uk + Miguel Farah. + + +This collection would not have been what it today is without their +help! + + +Author's Address +================ +E-mail internet address: yogi@cs.technion.ac.il + +Alternate E-mail addresses: yogi@cs.ubc.ca, yogi@umiacs.umd.edu. + +Permanent mailing address is: + Joseph Gil, P.O. Box 3148, Jerusalem, Israel. + +Hebrew mailing address (you cannot read the following unless +your screen adapter can display Hebrew character): + ליג יסוי + 3148 .ד.ת + םילשורי + +-------------------------------------------------------------------------- +From comp.fonts Sat Sep 5 11:12:35 1992 + walsh@cs.umass.edu (Norman Walsh) +Newsgroups: comp.fonts Subject: FAQ: Part-I: General Info Message-ID: + Date: 4 Sep 92 19:32:07 GMT +Reply-To: walsh@cs.umass.edu Organization: Dept of Comp and Info Sci, +Univ of Mass (Amherst) + +FAQ for comp.fonts: Part I: General Info + +Maintained by Norm Walsh and + Bharathi Jagadeesh + +Version 0.0.3, Release 04SEP92 + +Welcome to the comp.fonts FAQ. This article, posted monthly, describes +many of the basic questions that seem to be repeated frequently on +comp.fonts. Your comments are both welcome and encouraged. + + Standard disclaimers apply. + +.... + At one level, there are two major sorts of fonts: bitmapped and + outline (scalable). Bitmapped fonts are falling out of fashion + as various outline technologies grow in popularity and support. + + Bitmapped fonts represent each character as a rectangular grid of + pixels. The bitmap for each character indicates precisely what + pixels should be on and off. Printing a bitmapped character is + simply a matter of blasting the right bits out to the printer. + There are a number of disadvantages to this approach. The bitmap + represents a particular instance of the character at a particular + size and resolution. It is very difficult to change the size, + shape, or resolution of a bitmapped character without significant + loss of quality in the image. On the other hand, it's easy to do + things like shading and filling with bitmapped characters. + +..... + +5. Are fonts copyrightable? + + This topic is hotly debated at regular intervals on comp.fonts. + Terry Carroll provides the following + analysis of current [ed: as of 6/92] legislation and regulation + regarding fonts and copyrights. Members of the comp.fonts community + are encouraged to submit other materials that add clarity to the + issue. + + *-[Quote]-----------------------------------------------------------* + + First, the short answer: Typefaces are not copyrightable; bitmapped + fonts are not copyrightable, but scalable fonts are copyrightable. + Authorities for these conclusions follow. + + Before we get started, let's get some terminology down: + + A typeface is a set of letters, numbers, or other symbolic + characters, whose forms are related by repeating design elements + consistently applied in a notational system and are intended to be + embodied in articles whose intrinsic utilitarian function is for use + in composing text or other cognizable combinations of characters. + + A font is the computer file or program that is used to represent + or create the typeface. + + Now, on to the legal authorities: + + Volume 37 of the Code of Federal Regulations specifies this about + the copyrightability of typefaces: + + "The following are examples of works not subject to copyright and + applications for registration of such works cannot be entertained: + . . . typeface as typeface" 37 CFR 202.1(e). + + By the way, you won't find that in the most recent (7/1/91) edition + of the CFR; the addition was enacted 2/21/92. It'll be in the + next edition, though. It's described in the 2/21/92 edition of + the Federal Register, page 6201 (57 FR 6201). The change didn't + actually change the law, it just clarified it, and codified existing + Copyright Office policy. + + The regulation is in accordance with the House of Representatives + report that accompanied the new copyright law, when it was passed + in 1976: + + "The Committee has considered, but chosen to defer, the possibility + of protecting the design of typefaces. A 'typeface' can be defined + as a set of letters, numbers, or other symbolic characters, whose + forms are related by repeating design elements consistently applied + in a notational system and are intended to be embodied in articles + whose intrinsic utilitarian function is for use in composing text + or other cognizable combinations of characters. The Committee + does not regard the design of typeface, as thus defined, to be a + copyrightable 'pictoral, graphic, or sculptural work' within the + meaning of this bill and the application of the dividing line in + section 101." H. R. Rep. No. 94-1476, 94th Congress, 2d Session + at 55 (1976), reprinted in 1978 U.S. Cong. and Admin. News 5659, + 5668. + + It's also in accordance with the one court case I know of that + has considered the matter: Eltra Corp. V. Ringer, 579 F.2d 294, + 208 USPQ 1 (1978, C.A. 4, Va.). + + The Copyright Office holds that a bitmapped font is nothing more + than a computerized representation of a typeface, and as such is + not copyrightable: + + "The [September 29, 1988] Policy Decision [published at 53 FR 38110] + based on the [October 10,] 1986 Notice of Inquiry [published at 51 + FR 36410] reiterated a number of previous registration decisions + made by the [Copyright] Office. First, under existing law, typeface + as such is not registerable. The Policy Decision then went on + to state the Office's position that 'data that merely represents + an electronic depiction of a particular typeface or individual + letterform' [that is, a bitmapped font] is also not registerable." + 57 FR 6201. + + However, scalable fonts are, in the opinion of the Copyright + Office, computer programs, and as such are copyrightable: + + "... the Copyright Office is persuaded that creating scalable + typefonts using already-digitized typeface represents a + significant change in the industry since our previous [September + 29, 1988] Policy Decision. We are also persuaded that computer + programs designed for generating typeface in conjunction with low + resolution and other printing devices may involve original computer + instructions entitled protection under the Copyright Act. For + example, the creation of scalable font output programs to produce + harmonious fonts consisting of hundreds of characters typically + involves many decisions in drafting the instructions that drive the + printer. The expression of these decisions is neither limited by + the unprotectable shape of the letters nor functionally mandated. + This expression, assuming it meets the usual standard of authorship, + is thus registerable as a computer program." 57 FR 6202. + + *-[Unquote]---------------------------------------------------------* + + +------------------------------------------------------------------------------- +FLEXI IBM VGA9 FONTS: Scalable TrueType fonts based on the iconic hardware + VGA character set. + +INCLUDED FONTS: + +* Flexi IBM VGA9 True: Corrected aspect ratio, extended character set +* Flexi IBM VGA9 True 437: Corrected aspect ratio, CP437/DOS encoding +* Flexi IBM VGA9 False: Uncorrected aspect ratio, extended character set +* Flexi IBM VGA9 False 437: Uncorrected aspect ratio, CP437/DOS encoding + +LICENSE: + + These fonts are released under the Creative Commons Attribution-ShareAlike + 4.0 International license: http://creativecommons.org/licenses/by-sa/4.0/ + +_________________________________________ + +// VileR 2018-05 https://int10.org diff --git a/ChangeLog.ancient b/ChangeLog.ancient new file mode 100644 index 0000000..b0e9f05 --- /dev/null +++ b/ChangeLog.ancient @@ -0,0 +1,3380 @@ +96/11/09 ver. 0.64.0.4 + - EMM fix (Hans) + - first round vm86plus (emumodule) (Hans) +96/10/20 ver. 0.64.0.3 + - Priv off/on patches + - mfs.c patch to allow PRN called from mfs drives + - mouse.c modifed calls that reflect mouse visibility and + changed vectoring mouse interrupts to the actual interrupt + (thanks to Josef Pavlik) + - X.c added back mouse mickeys - WP5.1 mouse works now using + internal driver, but not WPs ROF. Also forced mouse reset + on DOSEmu boot if internal mouse being used. + (thanks to Bernd Paysan ) + - Alberto's patches to configure scripts for SBEMU. + - Hans' patches for running on Linux 2.1.5+ +96/09/24 ver. 0.64.0.2 - From Hans, SMP, emumodule, insmod and some minor DPMI-stuff +96/09/23 ver. 0.64.0.1 - JES took over some valuable patches from 0.63 +96/09/18 23:06:34 From : JES - Allow compiling without X support --without-x +96/08/30 01:21:30 From: Eric -- Some more Bugfixes, and a little video cleanup practice +96/08/07 13:56:38 From : Rainer - Video / Keyboard / Misc cleanup + * added runtime MITSHM detection + * fixed a problem with mouse initalisation + * mfs.c: fixed find_first error code if directory doesn't exist +96/08/02 18:45:49 From Alberto - Fix PIC bug even w/o emumodule & leave for vacations +96/08/01 22:39:10 From : JES - Minor changes to pic.[ch] +- Not sure if these make a difference, but since I'm no C guru, it looked + like some variable (pic_sys_time) were defined twice? At least it still + works looking this way ;). +96/07/31 19:57:40 From Alberto - Make debug flag working again & other fixes +96/07/31 18:29:09 From : JES - More IPX fixing and a Video WorkAround +- After all I went through to learn _not_ to call IPX functions + directly... I turn around and do it again ;-(. Always call IPXCallRel(). +96/07/31 13:57:15 From : JES - Fix IPX again ;( +96/07/31 12:03:14 From : JES - The Real Patch(tm) to bring IPX watchdog into DOSEmu +- With many thanks to Tim Bird and JL, here is a patch to get + asynchronos events happening under DOSEmu. This will make DOSEmu + nolonger dependent on the wdogfix.com TSR. The problem before was + making a stack returnable via retf. Also added is an ESR callback + which may give slight IPX speed inprovements (may not), but + prepares DOSEmu for more IPX development ;). +96/07/29 21:33:55 From Alberto - Fixed sig11 in leavedos() and cleaned some warnings + +96/09/15 ******** Release 0.64.0 - Made from 0.63.50 + +96/07/21 09:08:21 From Alberto - patches to serial code & various stuff +96/07/21 00:51:37 From Alberto - patches to serial code to avoid reentrancy + problems - Changes to tracing code in ports.c and dpmi.c - Some spelling + and small bug fixes +96/07/19 22:34:50 From : kan@dcit.cz - Fixes to parsing and Dr? regs. +1) parse_config() used to check the wrong id in order to find out whether + the user is allowed to have dangerous options in the configfile. + Hum.. it looks like my own bug... + (someone already fixed the same thing in parse_dosemu_users()) +2) At least one program (I think it was a game--Winter Olympic Games perhaps) + plays with dr? registers (trying to avoid being debugged?). This problem + made me add some handling for mov dr??, ?? to vm86_GP_fault and make the + type of org_eip correct (actually, I did it in the opposite order ) +96/07/10 11:26:50 From Hans: Dosdebug goes DPMI (+some minor DPMI-fixes) +96/07/07 16:13:11 From : JES - Allow stdin to be non-tty +- NO this is not the do-all-end-all to allow remote control access of DOSEmu. + It is only a stop gap to allow some next steps in testing. +96/07/07 13:27:10 From : JES - Replacment to WatchDog.com TSR for IPX +96/07/07 13:20:14 Cleanup and doc updates +96/07/06 14:19:39 From : John Davis - Proper changes to SLang +96/07/06 11:54:06 From : Peter Wainwright (prw@wainpr.demon.co.uk) - Fix init of IPXOpenSocket +96/07/04 22:03:59 From : Kang-Lee - Misc. patches to dosemu/src/commands +After much effort and many thanks to Kang-Lee I can understand him when he says: +"Well, this patch really deserves the award of most_complicated_patch_story. :-)" +Thanks Kang-Lee ;-) +96/07/04 21:56:58 From Alberto: some goodies for DPMI, DJGPP, GUSPNP, port fixes, e.t.c. +96/07/04 08:26:55 From Hans: upgrade to Linux-2.0.1, fixing LAR +96/07/02 22:08:27 Patches to DOSEmu +96/07/02 12:57:14 From Hans: 1st round towards 2.1.x (DPMI, LDT, emumodule, insmod) +96/06/30 12:45:11 From : Eric W. Biederman - fixups to video, etc... +96/06/29 18:47:14 From Heiko : Reduce compiler warnings +96/06/21 13:41:27 From : Johann Friedrich Heinrichmeyer - Update SLANG +Thu Jun 27 20:11:35 1996 Eric W. Biederman + + * Cleaned up mainy tiny things regarding font loading, screen + resizing, and updating bios variables. Now everything under X + should work under X except font loading and it punts that. + + * Fixed a tiny bug where in emu-i386 where EIP was stored in a + character. Only important to dosdebug + All fixes against 0.63.36 + +96/06/20 22:26:21 From : Markus Kossmann - Fix to configure.in +96/06/18 19:25:53 From : JES - Allow DOSEmu to compile without EMUmod support +96/06/18 09:17:36 From Marc Nijweide (M.Nijweide@et.tudelft.nl): empty ports should return 0xff +96/06/09 15:08:29 From : Uwe - A little bit more security with port access + doc/README.port-io: New file + src/base/init/parser.y, etc/config.dist : New port option "device" + src/emu-i386/cpu.c, src/emu.c, src/include/emu.h : Try to block + multiple access to registered ports +96/06/08 16:43:13 From : JES - Allow DOSEmu to compile when no X available +96/06/08 12:28:52 From Uwe : Port Access for (L)Users +Hallo, + +the patch I posted yesterday was "cut and dropped" in my editor. That way, +parts get mangled. And I didn't apply it to a clean tree, so an error +slipped by. Here is the patch against 0.63.1.30: +96/06/07 20:12:36 From JES : Reduce HMASIZE by 16 bytes as suggested by HJ +96/06/07 17:11:37 From Hans: emumodule & restore_i387; patches from Bernd +From Bernd Paysan : + - The timer initialization in src/base/init/init.c reduced to stay + in 32 bit integer arithmetic. + - The mouse handler fakes an interrupt and pusha on the real mode stack, + regardless if the program is in rm or pm. I added a fake_pm_int(). + - The X_SELECTION isn't well separated from X_MOUSE. I tried to do it + (src/env/video/X.c). + +96/06/03 12:39:57 From Hans: Fix silly vm86.h include loop (oh yeah, it was my fault). +96/06/02 15:58:42 From Hans: more LDT-access rights if running root, back out old, vm86.h hack +96/05/28 21:33:31 From Hans: forbid kerneld to unload emumodule, vm86.h missing fix. +96/05/25 15:14:02 From : JES - Fix mispatch to X.c +96/05/25 14:40:32 From : Various - Many subtle patches to X-mouse, mfs, ipx ... +96/05/25 13:16:30 From : Max H. Parke - Add LDT to mhpdbgc.c +96/05/25 12:05:59 From : JES - Remove need to patch kernel includes +96/05/24 10:35:16 From : Hans - Patch to get floppies active again +96/05/22 18:20:52 From : Adam - Fixes to VGA Emulation et al +96/05/20 05:23:46 From Adam & Erik: new VGAemu patches +96/05/14 07:17:18 From Erik: New EMUsuccess.txt +96/05/13 07:33:39 From Erik: little patch to compile again with Linux >= 1.3.100 +96/05/07 08:35:06 From Erik & Adam: Enhanced configure, Makefiles and VGAemu +96/05/02 07:21:58 From Adam Moss & Erik Mouw: MITSHM and little fixes for VGAemu +96/04/29 19:52:08 From: Alistair - Make SB (& DMA) code work +96/04/27 21:02:23 From: Alistair - SB reorganisation, DMA+SB update +96/04/26 17:11:28 From : Avery Pennarun - dosemu 0.63.1.8 X bugs (and fix) +Avery Pennarun +It changes env/video/X.c to use unsigned chars instead of chars for screen +cells. Due to certain tests in the code, any character >128 (such as +line-drawing characters) would _always_ act as if that line had changed +since the last refresh. So, in full-screen applications like Borland C++, +the flicker (and related CPU usage) was horrible. + +BTW, why on earth is the "byte" typedef a signed char? + +Have fun. +Avery +96/04/24 19:18:40 From : Jon Tombs - Allow modules to be made independently +96/04/22 11:40:02 From Hans: fix stupid typo in emumodule +96/04/21 17:07:29 From : UWE - emm.c switch code fix +96/04/17 20:07:22 From Mike Castle : fix for insmod + binutils 2.6.0.12 +96/04/16 20:50:15 From : Hans - FIX for dosdebug, small -Wall cleanup +96/04/16 19:04:11 From : Hans - As promised: FIX for vgaemu, and new patch for EMS +96/04/09 06:29:47 From Hans: invalidate => flush_tlb (adapt emumodule to Linux-1.3.83) +96/04/08 22:45:19 From : JES - Recovering RDDC from HD Crash +96/04/08 0x:2x:xx TESTING RDDC +96/03/25 05:26:56 From Hans: closeing 'expand down seg' security hole in ldt.c +96/03/18 06:43:03 From Erik: extra manpages & sigsegv enhancement +96/03/16 16:14:36 From : JES - temporarily turn of port array call of portss.c + - From some timing investigations it has been discovered that the + portss.c array calling code seems to be considerable slower + in it's present state. To that end as an interim measure, we + will turn it off and use the older single case statement. +96/03/13 19:50:26 From : Alberto Vignani - Change non-existant port read to 0xff again +96/03/13 18:35:53 From : Michael Leodolter - DPMI work + - With this stack nested PM programs like DJ200's make/compiler work fine + (maybe Borkland's protected mode make/compiler too). +96/03/11 18:59:26 From : alberto.vignani@torino.alpcom.it (Alberto Vignani) - LDFAGS fix +96/03/11 18:30:52 From JES/Uwe - Config fixes and more vga_emu_fault stuff +96/03/11 17:59:51 From JES, John - Add in VGA_EMU_FAULT() function +96/03/10 19:46:58 From : JES - Get Xdos with graphics working again + - Thanks to Uwe for pointing to a problem with Xdos not getting + configured with the latest X_GRAPHICS in config.h + - If 0'd out the VGA_EMU_FAULT() because it is not defined +96/03/07 23:39:37 From: John Kohl: turn on X_GRAPHICS for NetBSD +- turn on X_GRAPHICS support for NetBSD, it seems to work well enough +96/03/07 21:00:17 From : JES - Creating proper behavior in dpmi.c KERNEL_LDTALIAS + - with these diffs, Windows runs again. Need that DPMI guru :-) +96/03/07 20:13:38 From : JES - Supply new configure.in against John's diffs +96/03/07 19:10:38 From : John Kohl - NetBSD diffs for 0.63.1.2 +From: John Kohl +- Anyway, there's a system bug tickled by these changes on NetBSD but it +pretty much works. +- I haven't tried X graphics support yet, but I made +it easy to configure in or out now. +- I think I found a bug with KERNEL_LDTALIAS in the dpmi code, plus a +questionable missing () stuff there. Look inside dpmi.c +Thanks! +==John +96/03/05 22:20:22 From : JES - Up version to 0.63.1.2 +96/03/05 22:02:23 From : (Alberto) a.vignani@crf.it - Change cpu.c ports to size short +I was tracing a DOS program accessing a peripheral card and found dosemu +tried to access ports > 64k. After looking at the generated assembly, I +changed 'int' to 'short' for all port addresses in cpu.c. +Alberto +96/03/04 20:56:24 From: Hans, adapt insmod-HACKER_TOOL to modules-1.3.69 +96/03/04 18:16:54 From : JES - Add config.h to emu.h to force its inclusion +96/03/03 17:34:02 From : Uwe - Add debug to dpmi.c +96/03/03 17:04:31 From : Alberto Vignani - LDFAGS -> LDFLAGS in Makefiles +96/03/03 17:02:58 From : Alberto Vignani - Timers TYPO 40 -> 0x40 +96/03/03 16:31:25 From : Uwe - Remove more -Wall warnings +96/03/03 14:16:52 From : JES - Get USE_MHPDBG back into emumodule.o + - Make emumodule.o only included USE_MHPDBG if make from emumod subdir. +96/03/03 09:09:49 From : JES - Fix FNX calls in timers.c + - Due to the new ports calling convention, these had to be modified. +96/03/03 08:40:51 From : JES - Get IPX working again (and pktdrvr?) + - USING_NET was NOT being carried through all Makes +96/03/02 21:39:20 From : JES - Fix Makefile.main +96/03/02 18:11:46 From : JES - Add X_SUPPORT to mouse subdir +96/03/02 15:14:17 From : JES - Allow make in src subdirectory to _not_ wait for keypress +96/03/02 14:26:47 From : JES - Begin adding Scott's port access methods +96/03/02 12:02:46 From : Erik - Update dos.1 man page +96/03/01 22:13:18 From Hans: adapt emumod to 1.3.70, fix for xdos +96/02/29 18:52:50 From : JES - Bring back Xdos +96/02/29 17:58:19 From : Uwe - Add emumodule loading to QuickStart +96/02/28 20:23:42 from : JES - Fixes to inital ./configure with Uwe's help. +96/02/25 14:07:34 From : JES - Remove async Makefile dup +96/02/25 13:29:08 From : JES - Make MAKE work from each subdir using configure +96/02/24 18:37:17 JES - Add ./configure to DOSEmu via autoconf +96/02/21 13:37:32 From : JES - Correct typdef in cpu.c +96/02/21 11:05:06 From : JES - Remove RCS logs, remove unused, clients, and ipxutils +96/02/20 18:29:33 From Erik: securety fix & logging facilities +96/02/11 12:48:28 From JES - Backout typedefs (thanks Hans) and remove changelog in emu.c +96/02/08 21:52:43 From JES - Update the Release Patchlevel to 0.63.1.1 +96/02/08 21:45:43 From JES - Make Xdos and Windows run again :-( +96/02/07 21:04:26 From JES : New typedefs for emu.c and dos.c +96/02/07 20:43:27 From : JES - Adjust types.h and cpu.h to new layout +96/02/05 21:37:47 From : JES - Documentation from Hans for VM86 flag usage +96/02/04 20:03:25 From Alistair: DANG cleanups ... +Thu Jan 25 1996 +- From Henrik Storner + Fix for malloc/valloc-bug, happens together with libc-5.3.0 + +- From Hans + Fix for syscallmgr, NR_xxx wrong together with newer shared libs + ( found this thanks to Ronald Wahl , who + posted it on de.comp.os.linux ) + +Tue Jan 23 1996 +- Release of dosemu-0.63.0.1 + +xxx Dec 30 1996 +- Release of dosemu-0.63.0 + +Sat Nov 25 1995 +- From Michael Beck : Fix to debugger D command + +Tue Nov 21 1995 +- From whraven@njackn.com : Small fixes to Handle Novell programs +- From Uwe : Updates to README.X + +Sat Nov 18 1995 +- From JES : Close /dev/zero after mmap (DPMI), Fix compile without X + +Fri Nov 10 1995 +- From Hans Lermen (and Kevin Buhr): Fix for timerlock-bug + + This includes the patch from Kevin Buhr (good work), + plus some fixes to make hardware-IRQs work (Sillyint), + plus some other pic fixes (correct now, but wasn't the reason for the lock) + plus THE (one line) fix for the _real_ bug: + + Once again we messed up with VIF flag, hence letting ints pass + through. even if CLI was active. + This apparently was one 'bug-catcher' we left back during the + the great hunt for 'the bug of bugs' some month ago. + (Hmm... I say 'we', but very well may be that it was 'me') + + I also introduced a + #define USE_UNIX_TIME_FOR_INT1A_AH0 + which should be defined at top of dosemu/int.c to get the 'old' code, + which does not use the BIOS 40:6c timer, but got the time from Linux + and overwrites 40:6c. + Currently it is not defined, so INT1A AH=0 gets time from 40:6c, + however, if somone gets 'timerlocks' again (which I don't hope), + then try witch defineing USE_UNIX_TIME_FOR_INT1A_AH0 and report to the + team. + +- removed timerlock-Workaround patch (was added Nov 01) +- added upgrade for Linux-1.3.38 (emumodule, syscallmgr, net/libpacket.c) + +Wed Nov 06 1995 +- From Michael Beck : Patches to SBEmu + +Mon Nov 06 1995 +- From Michael Beck : Enhancements to SB and DMA emulation + +Sun Nov 05 1995 +- Removed priv_on/off patches from Yanming (were added Oct 29 ) + (inhibited port-IO) + +Sat Nov 04 1995 +- From Kang-Jin Lee: Mods to better detect DOSEmu +- From Kevin Buhr : Interim fix to timer stopping. + (removed the same day because of hardware-IRQ conflicts) + +Fri Nov 03 1995 +- X-char patch to cast display to correct sizes + +Wed Nov 01 1995 +- Workaround for boot-up-timer-lock-BUG. (Hans) + +Sun Oct 29 1995 + - From Yanming , + fix iopl/ioperm permission bug: + - remove some wrong priv_on()/priv_off() calls. + - Add 2 parameters to priv_on()/priv_off() calls. + - Minor fix to compile without REQUIRES_EMUMODULE. + +Sat Oct 28 1995 + +- *** pre-release pre0.60.4.4 uploaded from rddc to tsx-11 *** + +Sun Oct 22 22:32:45 1995 John Kohl + + * Update emm.c to avoid release string-induced problems on NetBSD. + +Sun Oct 22 22:32:45 1995 John Kohl + + * Update emm.c to avoid release string-induced problems on NetBSD. + +sat Oct 22 1995 + +- From Hans, Upadates to go elf +- From Purtill , joystick patches to use module. +- Libelf-a added to dist. + +Wed Oct 04 1995 + +- Worked a bit with Uwe's discovery of type conversion + char <-> unsigned int +- Added release-keycode to slang. This may cause problems? + +- From WE Metzenthen +Hi, + Having some dos stuff which I wanted to run, I decided that it was +time to update my dosemu. Unfortunately, I still only run a mono HGA +card so dosemu-0.60.3 was not a stunning success. Anyway, I dug into +the sources to the extent which was necessary to get it to work with +my HGA card and the resulting patches are attached to the end of this +message. The patches also improve the functionality in that dosemu +now does something more sensible with the cursor. + + Not having followed the development of dosemu closely, I don't +claim that these patches represent the correct way to solve the +problems, only that they work for me under admittedly limited testing. + + The patches also apply cleanly to dosemu-0.60.4.3 (apart from a +trivial Makefile change) and seem to compile o.k., but I haven't +checked the result because I don't want to bother with modules yet. + +--Bill + +Tue Oct 03 1995 + +- From: "Pavel Kankovsky" +These my patches are against 0.60.3 (but they seem to still be +usable in 0.61.00 from what I have seen at the first glance). +Here is some description only (usable as ChangeLog entries, I hope), +diffs themselves are enclosed as attached files (dosemu[0-3].diff). + +Patch 0 fixes 3 things in mfs.c: +- Mktime() uses tm_isdst value (unlike stated in the original source code) + leaving it unset causes random mtime shifts by one hour and has funny + effects esp. when running make, value -1 (which meaning is "figure out + the proper value yourself" :> ) seems to be the best one for me. +- Hlist stack management was quite 'kludge-o-matic' in the original code + and caused problems with Quattro Pro 5.0 installation program which + starts a directory search, does NOT finish it and spawns unzip program + that starts another directory search, and upon unzip's exit, both lists + were popped, making the installation die eventually. + I rewrote findfirst/next and hlist_push/pop to allow this case and + do it all in a bit cleaner way. +- Findfirst returns FILE_NOT_FOUND no more because original Dos returns + this value only for EMPTY directories (even no ., .., volume label!), + so NO_MORE_FILES is more convenient in most cases (esp. for Volkov + Commander -- Norton Cmdr. alike -- that used to complain "Can't read + volume in drive X:" every time it got FILE_NOT_FOUND for findfirst) + +Patch 1 brings some improvement I made in dosdbg.c and its counterpart +dyndeb.c to help myself when examining PIC working. The original code +does not accept debug levels (as Dosemu command line options do). Now +you can do something like "dosdbg 3r" when Dos boots and reduce the +debugging log by cca 300 kilobytes. :-) + +Patch 2 makes Dosemu safer as it does not allow ordinary users to do +"dos -F ..." with dangerous (e.g. direct disk access) settings in their +configfiles. + +Patch 3 modifies keyboard driver finalization so as to let keyboard leds +follow in-kernel num/caps/scroll-lock flags again. (I was trying to make +it save led-function setting and restore it later but the kernel does not +seem to provide a straight-forward way of doing it. Bad luck.) + +I hope it will help make Dosemu better Dos than Dos! :-) + + + Yours Sincerely, +----------------------------------------- + | DCIT s.r.o., CDMS, J. Martiho 2/407 + Pavel Kankovsky aka PeaK | 160 41 Praha 6 - Veleslavin, Czech Rep. + | phone/fax: (42 2) 316 42 78 + | web: http://www.dcit.cz/ + +Mon Oct 02 1995 + +- Patches to XMS for better himem compatibility + +Wed Oct 11 22:28:05 1995 John Kohl + + * Update for latest NetBSD stuff + +Wed Sep 27 1995 + +- Included many patches to get NetBSD back on track. +- Added in 2 patches for debugger and other en-Hans-ments :-) + +Tue Sep 12 1995 + +- Added first layer of DMA and SoundBlaster code oringinally + contributed by Joel Weber (age 15), then put into the + distribution by Alistair. + +Sat Sep 09 1995 + +- Added FOSSIL internal support by + pasi@forum.nullnet.fi (Pasi Eronen) +- Added X enhancments and -O option by + Uwe Bonnes bon@elektron.ikp.physik.th-darmstadt.de and Pasi. +- Hans' latest syscallmgr updates. + +Thu Jul 28 1995 + +- Release DOSEMU-0.60.4.tgz +- Hans' patch to dpmi.c +- Added CLTS from rde@tadpole.co.uk (Richard Evans) +- Added .ini extensions from Jon +- From Snow Cat +2) pic/pic.c has some assembler code that doesn't work with -O3 because it's +using global labels that get expanded more than once. Fixing it to use local +labels solves the problem. + +3) mfs/mfs.c doesn't handle "commit file" and "flush all disk buffers". It +causes AH=68H/int 21H to return an error code that confuses database programs. +I changed COMMIT_FILE to do fsync() and FLUSH_ALL_BUFFERS to do nothing and +return success (because sync()ing every time DOS wants to flush buffers makes +too much noise). +.... and some other patches too. + +- Mfs patch from Andi. +- Mouse patches from beck@informatik.hu-berlin.de (Michael Beck) +- Patches for UID settings by Steven P. Crain + +Wed Jun 28 1995 + +- Release DOSEMU-0.60.3.tgz + +From Hans : +This is a summary of my changes since dosemu0.60.2: + +- Upgraded syscallmgr/insmod to modutils-1.2.8 + (full insmod-tree is in insmod-1.2.8-HACKER_TOOL.tar.gz) + Now can (mix) load a.out- or ELF- modules into + a.out- or ELF- kernels. +- New LDT support for emumodule, now can access the *real* LDT + from dosemu user space. How it works can be seen in ldttest.c +- Made emumod ELF compilable + ( patch from Rick Sladkey ) +- Took over some changes to vm86() from Linux 1.2.11 and 1.3.3 + into emumodule. Also now doing full verify_area on all accessed + user space in emumodules' vm86() (USE_VM86_STACKVERIFY). + This fixes some odd DOS register messups. +- Fixed wrong flag handling in bios.S for INT10 .. INT1B + (some of them should IRET). +- Improved return to DOS-space from dosemu redirected INTs + int dosemu/int.c function do_int(). + (did respawn to dosemu immediately, so leaving the DOSapp + no chance to react). +- Fixed interruption of DOS space when interrupts were disabled. + ( check for VIF at entry of pic's run_irqs() ). + This fixes the main trouble together with sillyint (exception 6 bug). + JES did the rest by finding this damned WRITE_FLAGS bug and + fixing various VIF/IF handling. +- Now dosemu will do register dump if -g debugflag is set and + if killed via SIGHUP. So we can see were the DOSapp is hanging. +- Varous fixing for sillyint (SIG), so it now complies with the + new pic-handling. +- Fix startup recognition of loaded emumodule. Now it will exit + with error if no module is loaded and REQUIRES_EMUMODULE is set. +- Made dosemu/sigsegv.c real-mode-386-compatible and fixed wrong + prefix handling (ES,DS,GS,FS,REP). + Moved some remaining 'fprintf(stderr)' to 'i_printf()' +- Fixed overload condition resulting in DOS internal stack overflow. + ( bug while using SLANG terminal). This is done by monitoring + the difference between pic_sys_time and pic_dos_time and avoiding + vm86() respawning to fast when OVERLOAD_THRESHOULD is reached. + This way DOS has a better chance to catch up some additional + CPU time in order to be in schedule with pic_sys_time. +- Fixed dualmon messup (didn't work a lot of last releases). + +So long, +Hans + + +Fri Jun 22 1995 + +- Patches to emumod/vm86.c by Hans to fix some real + odd register bugs. +- Update to READ/WRITE_FLAGS to remove bug of + VIF being cleared when CARRY/NOCARRY used. +- Patches to pic.c from JL. +- Mods to the mfs.c code in an effort to get FCB callouts + working again. +- File locking implemented in mfs.c thanks to Jack. +- Allow booting from a sector image file + +Wed May 31 1995 + +- Mouse patches from Alan Hourihane +- Made priv_on/off into functions. +- CDrom addition for Aztech/Orchid/Okano/Wearnes by + Werner Zimmermann (zimmerma@rz.fht-esslingen.de) + +Mon May 29 1995 + +- JL's latest PIC overflow patch +- Kang-Jin Lee's latest help + +Sat May 27 1995 + +- PIC enhancements. +- More support for CD-ROMs +- Dong's DPMI enhancements +- Change mouse.com to emumouse.com +- Mods to mfs.c for Novell compatibility +- Mods to emumodule by Hans + +Fri May 12 1995 + +- RELEASE of DOSEMU0.60.2. +- Xkeyb.c -> More patches. +- Xkeyb.c changes mostly from Ross Becker (many thanks for + following up on this). +- Hans' latest for SIG and Emumod +- JL's PIC enhancements +- Dong's Mods for Pharlap extender capability which includes an + additional patch in ./dpmi/kernel.diff. This patch + (./dpmi/kernel.diff) will bomb on kernels > 1.2.7 as Linus has + included part of it (the last patch - to vm86.c) into 1.2.8. + Having said that, please choose to either : + 1) Use emumodule which makes this all redundant + 2) Use this full patch to kernels < 1.2.8 + 3) Remove the last part of this patch to vm86.c + if you are running a kernel > 1.2.7 + +Sat Apr 22 1995 + +- Changed disk_init() in ./dosemu/disks.c to allow 'hdimage' + being opened readonly. +- Added Dong's DPMI patches. +- Small patch to mouse.c for non-X compiles + +Fri Apr 14 1995 + +- RELEASE of DOSEMU0.60.1. +- Patches to internal mouse. +- Patches to serial. +- Patches to PIC. +- Set X_keycode back to 0. This will require setting keycode in + your /etc/dosemu.conf for all folks who didn't need it before. + +Sat Apr 08 1995 + +- OFFICIAL RELEASE of DOSEMU0.60.0. +- Last minute cleanup by Lutz. +- Last minute bugfixes by Dong +- Some additions to some docs. + +Tue Apr 04 1995 + +- Upadate to config.dist by Don Parsons +- Speed.com for changing HogThreshold on the fly by + Tim Van der Linden +- Patches to mouse for logitech/mm from David + +Sun Apr 02 1995 + +- Release pl58 +- John's cleanup of SLANG including screen size fix. +- Preparing for the onslaught of user E-mail from above, + I added the file name being parsed to yyerror(). +- JL's PIC patch for serial, etc... +- Hans' latest. It removes the KERNEL_VERSION hack too :-). +- Dong's DPMI patches to get the BCC IDE going. Also includes + small patch to serial.c to dissallow mice from being + initialized if not at the console. + +Thu Mar 30 1995 + +- Release pl57 +- Scott's timer patches for speaker emulation +- Hans' patches to bring emumodule to 1.2.2 +- More cleanup+ by Kang-Jin Lee lee@tengu.in-berlin.de +- Added hack for KERNEL_VERSION to lower E-mail :-) +- Rod May's dummy HDiamge (hddummy) + + +Tue Mar 28 1995 + +- Release pl56 +- Kang-Jin Lee lee@tengu.in-berlin.de patches to + lredir.c, unix.c, mouse.c, etc... +- Addition of tty_locks { } from: + Matthew Grant + +Mon Mar 27 1995 + +- John's patches to add priv_on() and priv_off(). +- Dong's latest DPMI enhancements +- Scott's latest timer code to remove the need for + speaker access to port 0x42 +- Hans' syscallmgr/emumodule combo so now we will not + require kernel patches to run DOSEMU with the extra + DPMI support. There are 2 scripts load_module.sh and + unload_module.sh to make the module stuff easier for + newbies. You'll need to modify the + Makefile if you chose to go with emumodule support + for this release. + +Sat Mar 25 1995 + +- Release pl55. +- Patches by Rod May: +I've got patch for you that will check that partitions +specified for DPA are not already mounted. As it stands you get +an error if the fs is mounted read-write, and DPA read-only is +*not* specified. You get a warning if one is read-write and the +other read-only. Both read-only is accepted. +- Hans' latest patches to bring the syscallmgr compliment + up to 1.2.0. +- Various patches by Marty + +Wed Mar 22 1995 + +- Release pl54 +- Got TankWars going. Did so by removing section of + bios/bios.S that starts at orig 0xfff0. Put that + back into init/init.c as *ptr += code :-(. Now... + Why did this cause this problem? +- Latest and greatest serial patches from Mark. Should + allow DPMI good access to serial interrupts as well as + better throughput for transfers. +- Dong's latest DPMI series. +- Dong's shift to readonly bios parts. + +Sun Mar 19 1995 + +- Release pre0.53.53 +- Latest DPMI from Dong +- Locking serial ttys from: + Matthew Grant +- Little work on ctrl-alt-del rebooting, but no success. + +Fri Mar 17 1995 + +- Release pre0.53.52 +- Larry's mods to use HLT code for IRETS +- Markky's code to make the timer faster +- Scott's latest PIT additions (some probs here) +- Added Dong's kernel patch to ./dpmi/kernel.diff +- Added back in network code :-( +- David's updates for LPT to ./examples/config.dist +- Put the internal mouse call on IRQ12. +- Some of this and a little of that. + +Thr Mar 16 1995 + +- Release pre0.53.51 +- Minor updates to DANG +- lee@tengu.in-berlin.de (Kang-Jin Lee) submitted new + isemu.c/com, dosdbg.c/com, config cleanups. +- Andrew's latest mfs patches to hide hidden files. +- David Hansen patches to add + lpt port configuration to the conf file. +- Marty added chdir, system and getcmd commands. + +Wed Mar 15 1995 + +- Added "Andrew.Tridgell" 's + name mangling patch to mfs.c. Too bad Andrew had to see + his previos work in such a hacked state :-(. +- Dong's latest DPMI patches. + + +Tue Mar 14 1995 + +- Changed Makefile to make;make install +- Replaced 2>debug with -o debug +- New libslang.a (also on tsx.../Incoming) +- Patches from Lutz. At this time we'll leave it at that :-) +- Changes from Marty +- JL's latest PIC enhancements. + +Sun Mar 11 1995 + +- 2 patches from Lutz +- John's latest SLANG. Now does ctrl-alt-del :-) +- Moved to keyboard-{server/client}.c +- Added Daniel's redirection via -o patches. + +Thr Mar 08 1995 + +- Added keyboard fix. Much more to go :-( +- Added Scott's timer fix. +- Added more timer code from Scott and Mark. + +Tue Mar 06 1995 + +- Release pl48 +- Latest from Marty (need to test serial) +- Scott adds new timer code +- Latest DPMI +- Latest Slang, uses ^^ instead of ^@. +- Beginning of keyboard split. +- Alistairs UNIX power additions: +OK - This version of unix.exe allows you to interrogate UNIX environment +variables and then execute a program contained within AND you can now pass +programs to DOSEMU to execute. + +Basics: + +Passing a command to DOSEMU: +============================ + dos -E "command to be passed to DOSEMU" .... + .... + C:\> unix.exe -e + >> RUNS command to be passed to DOSEMU << + +Executing the contents of an UNIX Environment Variable: +======================================================= + C:\> unix.exe -e ENV_VAR_NAME + >> RUNS COMMAND NAMED IN env_var_name << + + NB ENV_VAR_NAME will probably need to be in CAPITALS ! + +Running a UNIX Command: +======================= + (As Before...) + C:\ unix.exe ls -l /dev + >> RUNS "ls -l /dev" under Linux .... << + +---------- +Alistair + +Fri Mar 03 1995 + +- Release pl47. +- Small EMS fix. + +Wed Mar 01 1995 + +- Add break to -v cmdline option :-(. +- Added new slang code from John. Includes: + 1. Online help + "^@?" + 2. Sticky modifier keys. + 3. Easy terminal independent way of simulating function keys, e.g., ^@5 + is F5. + 4. Mechanism for panning screen for terminals with less than 25 lines. + 5. Redraw +- Dong/Lutz latest DPMI +- Kevin's latest BOCHS changes. + +Sat Feb 25 1995 + +- Released pl46. +- Added -v[1-4] option for John to configure cardtype at + DOSEMU boot time. (1=VGA, 2=EGA, 3=CGA, 4=MDA) +- Added in JL's latest keyboard PIC fixes. +- Added in Marty's humungous set of patches. Inncludes + changes to PIC layout. +- Subset of mouse patches sent in and filtered via Alan. + +Thr Feb 16 1995 + +- Release pl45 +- Lutz et Dongs latest +- Hacked ipxutils to compile again. + +Sun Feb 12 1995 + +- Added ipx.h to ./init/init.c +- Fixed VC hangs due to GP on memcpy in vc.c +- Added back in ctrl-G beep in init10.c +- Added some code to get keyboard working in non-keybint mode. + +Fri Feb 10 1995 + +- Release pl44 +- Added Lutz/Dong's latest patches. Includes another possible + fix to the PIC keyboard problem. +- Added Alan's latest mouse patches. +- Added rebuilt run_unix_command(). See ./dosemu/int.c + for details. +- Added Scott's latest cleanup patches. + +Sun Feb 6 1995 + +- release pl43 +- Added Dong/Lutz patch for pic_cli(); +- Set (hardcoded) X window to 80x25. + +Sun Feb 5 1995 + +- Added latest DPMI patches. +- Patched in Scott's latest dosemu cleaning up. + Also from Scott: +James, + +Well, I restarted the patch work that I sent to you some time ago. I +haven't gotten all the way through it yet (sadly), but here goes: + +I propose a naming convention for the initialization procedures: + + xxxx_init() for something that only gets called once + xxxx_setup() for something that gets called at each 'reboot' + (e.g. internal reboot -- the code that doesn't work) + +- Dualmon patches from (mouw@morra.et.tudelft.nl> (Erik Mouw) +- Rewritten unix.c to compile under various DOS C-compilers. + +Mon Jan 30 1995 + +- Must mention that JL has added an interrupt watchdog + timer so that the keyboard behind problem may be resolved. +- Backed out Marty's exchange_uids() as they were slightly + incomplete and causing video switching (etc) problems. +- #if 0'ed Marty's dbg_fd changes until this is complete. + This has caused some problems for folks trying to boot + DOSEMU. +- Added 2 sets of Lutz's DPMI patches. +- Added Ed's mouse garrot enhancements (inc mgarrot.com) to + reduce DOSEMU CPU time even more during mouse activity. +- Dissallowed video segment IPC attachment when Xdos is + used. + +Fri Jan 27 1995 + +- Another week, and quite a few goodies. IPX updates + from Greg Page. Many mouse updates from Alan. Some + DPMI changes from Dong so that jed386 will run. + +- Renamed termio.* to keyboard.c. Attempted to move all + keyboard functions into the keyboard subdir. + +- Well, for better or worse, watch out world, I've started + back again on some of the client/server aspect. Remeber + that :-)? At this time, dosemu now creates a file: + /tmp/dosemu. with basic info and SHM id's. To + see how amazing this is (pun is intended!), + cd clients and make. Then you'll have 2 programs: + video - To just dump the dos processes 80x25 video screen + (Must NOT be in graphics or console mode) + keyboard - To dump what is in the DOSEMU keyboard buffer. + (NOT the DOS 40:?? buffer) + Both programs require a pid as parrameter 1. + +Fri Jan 20 1995 + +- Additions by Denis Julitovto make ctrl-alt-del stuff work. + +Fri Jan 16 1995 + +- Release pl40 +- Upsetting the DPMI apple cart during testing of timer.exe. Now + the following rules have been applied with hacking : + 1) calls to realmode procedures do NOT run handle_signals() so + that DPMI properly deals with them upon return. + 2) hardware int's are first reflected to the possible + protected mode interrupt handle, which may (and usually + does) reflect them back to the realmode handler. + +Fri Jan 13 1995 + +- Added Alan's mouse patches +- Added Scotts big patch to make int.c and move signals into signal.c +- Small patch to bios_emm.c so that seg -> phy requests are properly + handled. +Mon Jan 9 1995 + +- Small change to EMS so that Allocate pages allows allocation + of 0 pages. This allows Windows 3.0 to run small EMS frame. +- Scott has brought garrot into DOSEMU :). +- Got Hans's ./tools/sh.kversion to work. sorry Hans. +- beck@informatik.hu-berlin.de Michael Beck + Here it comes. The patch is very stupid, and add the classname XDosEmu + when dosemu runs with X-Support. Use fvwm's ICON "XDosEmu" + to get the icon. + BTW, perhaps someone should change the config-parsing, so instead of + the Iconname the user can set the classname. + ALSO: + > > And last: I have converted a lot of fonts for DosEmu from + > > MS Wind**s FON files and some other sources (CPI and FEDIT files). + > > Sorryly The FON files are copyrighted, so I think I cannot distribute + > > them. However, perhaps I can distribute the converter, so at + > > least the millions of millions Wind**s owners can convert them ;-). + > > Mail me if you are interested. + > + > Yes, always interested. This means that vga.pcf would not be alone? I + > could just add it into the dosemu directory on tsx-11. + > + + I send you the package, but my english is very bad, so perhaps someone + check it and rewrote my readme (sorry for that, but my time is + limited now, the hollidays are over :-( + +Sun Jan 8 1995 + +- Released pre0.53_39 +- All Hans' patches to get a working kversion.h system in place. + Finally we can easily(?) keep dosemu working with various + releases. +- From Alan: + + Hi James, + + I saw in the code you've been doing a bit of hacking with 2/3 button + mice. Well I modified a bit of the code and hope this helps a little. + + What I've done is provided switchable PC Mouse/Microsoft mice modes + within DOSEMU. I've re-written mouse.S to become mouse.asm (Sorry, I couldn't + suss out gas to compile dos progs, so I had to use a86). Anyway I've also + provided the NEW ! raw mouse.com at the end of this message in uuencoded + format. + + There have been a few more subtle changes to the code that you'll + probably notice too. One of the more obvious, is that the internaldriver + now can have 3 button emulation on 2 button mice. + + Anyway MOUSE.COM now takes some arguments... + + r - resets the iret. Put "mouse r" in your autoexec now.. + i - inquire current configuration of mouse driver. + p - Set PC Mouse mode (3 buttons) + m - Set Microsoft mode (2 buttons) + + I'll be implementing more as we go along... + + Some of the PS/2 Mouse BIOS routines have been deleted/re-written, as the + existing code was wrong. + + Alan. + + Hi again. + + Some more fixes. + + Apparently according to my documentation there are some wrong doings + in the show/hide stuff. There should be a counter which if 0 the cursor shows, + but it is possible to do ... hide(), hide() which will make the counter -2, + then doing a show() with put the counter at -1, but will still not show the + cursor. Doing another show() will make the cursor appear. + +- Alan's patches to get Hans' emumod system compiling again. +- Added new lredir.exe and lredir.c from Tim Josling : + + The problems fixed are: + + Doesn't check that argv[2] exists before using it, causing a loop in some + cases. + + Function that returns nothing is specified to return some type of int, + causing compiler complaints. + + Drive mounted read-only is reported as read-write. + + I tested the fixes pretty thoroughly so they shoudl be OK. I compiled + with Turbo C++ 3.0 when I tested them. + + The file below is the full source in dos format (with ^M's). I tried to + generate a diff file but this didn't work as the original file didn't + have the ^M's on many lines so as my editer put them on every line I + ended up with many spurious diff's. + + The version of lredir was the one from 0.53pl37. + + Regards, + Tim Josling + +- Added Lutz's latest DPMI patches. GO32 now runs and shows + memory. Some go32 DPMI programs are running. Yah Lutz. + +Wed Jan 4 1995 + +- Released pre0.53_38. Happy New Year. +- Added Marty's next set of patches. +- Moved Hans' enhancements form 'make doeverything' to + 'make itall' so that as kernels change, make doeverything + remains OK. +- Jiggled around where ioctl's to acquire_vt were in vc.c in + an effort to stop more of the VC switch hanging bug. + +Tue Jan 3 1995 + +- Added Marty's Unix.exe to send unix commands from DOSEMU. + To use, just ln files to a unix.exe that you wish to run. + I.E: + ln ./commands/unix.exe ls.exe + from within dosemu, just enter ls and the ls command is executed. +- Added two parms to call to show_reg( __FILE__, __LINE__) to + track down where show_regs() are being called from. + +Sun Jan 1 1995 + +- Added return to getKey() function in ./keyboard/termio.c when + screen is not current. Fixed screen lockups here. +- Removed (added #if 0) code setting bs_pos to -1 for root dir deletes + +Mon Dec 19 1994 + +- Added Marty's latest Makefile enhancements. This included 2 new + files (data.c extern.h) and including int.h inside of + emu.c. Marty promises emu.c will get smaller :-) +- Added Lutz's latest DPMI enhancements. + +Sun Dec 11 1994 + +- Changed check for mouse in graphics mode by using video + regs. +- Latest updates from BOCHS project. +- Modified MICKEY concept in mouse.c so that WordPerfect now + works with internal mouse. Doing this blind folks. Anxiously + waiting for Alan's next set of mouse enhancements. + +Tue Jan 3 09:49:22 1995 Jochen Hein (Hein@Informatik.TU-Clausthal.de) + + * keyboard layout gr renamed to de, like keyboard-utils. + +Fri Dec 9 1994 + +- Release pl37 +- Added garrot02 to dosemu. I think it should be separate on + tsx? + +Sat Dec 3 1994 + +- Update to add different X fonts : + + Hi! + + I just added support to change the font used with XDOS. There is a new + keyword, "font", in the X-section of the config file which allows you to + specify the font to be used. If the font is not a monospaced font (or can't + be found) "vga" is still the default. + + There is no translation done, i.e. the font has to be an IBM font! + + So, if someone wants to create a smaller font... + + BTW, the include file "linux/segment.h" changed to "asm/segment.h" somewhere + around linux 1.1.67 (it is used in dpmi.c). + + The diffs are against pre0.53pl36. + + CU + Heiko + Email: | Snail-mail: + | Heiko Schroeder + heiko@pool.informatik.rwth-aachen.de | Lerchenweg 120 + | 52223 Stolberg + | GERMANY +- Modified to check for VGA, then warn and check for 9x16, + then fail. + +Fri Dec 2 1994 + +- Release pl36. + +Mon Nov 28 1994 + +- Added -D__ASSEMBLY__ to get emumodule.o going again in + later kernels :-). +- Some problem in RPT_SYSCALL, so went back to SA_RESTART and + set RPT_SYSCALL to return. +- Added mouse.com which causes int33 to be redirected away from + an iret. Now Foxpro mouse works in X, but still no WPO :(. + +Sun Nov 27 1994 + +- Major -(Thats MAJOR)- restructure of the directories and + dosemu files. Marty is a busy man. +- Got Han's latest bios.S working with Hans help :). +- EMS patches from JL. +- Made Novell access single mode again for default. +- Verified alt keys are working well, as well as alternate + slang keys. +- Latest memory access for cross-platform development thanks + to Kevin for the Bochs team. + +Tue Nov 21 1994 + +- Added RPT_SYSCALL's to some writes. Kinda kludgy for know :-( +- Latest SLANG enhancements: +Hi, + + I added the ability to access ALT, SHIFT, and CONTROL keys for remote +terminals. For example, one can now use alt function keys as well as +Ctrl-Backspace, Shift-tab to get the backtab, Ctrl-Arrow, etc.... + + Below, you will find a uuencoded file called `files.tar.gz'. After +unpacking it, you will see: termio.c, slang-termio.c, and terminal.c.slang. +The latter files should go in the `video' subdir and the other two go in the +`keyboard' subdir. `termio.c' is exactly like the one already there. The +changes are marked by `#ifdef USE_SLANG' stuff. + + Note that in slang-termio.c, you will find 3 global variables: + +static char *Alt_Shiftkey = "^@A"; +static char *Ctrl_Shiftkey = "^@C"; +static char *Shift_Shiftkey = "^@S"; + +These should be initialized by the user by whatever mechanism you prefer. +They represent the keysequence that invokes the appropriate modifier key, +e.g., to get the next key ALT, press `Ctrl-@ A'. Perhaps, it might be +easier to simply use `Ctrl-A' but I wanted to demonstrate that one can use a +multiple character key sequence. When one of these keys are pressed, an +indicator will appear at the bottom of the screen indicating this. (The new +terminal.c.slang does this). + +I had a terrible time getting the ESC key timout to work properly at 2400 +baud. As a result, I have eliminated the ESC timeout from the slang +version. If you want to send an ESC, hit it twice. The alternative simply +does not work and hitting it twice when an escape is needed is alot less +painful. One can always rebind the escape key on the terminal emulator to +send two escapes. + +Obviously, we will want to add some mechanism to rebind keys. I do not +believe that relying on the keys as defined in termcap is adequate. For +example, one might want to rebind Ctrl-E to escape for a DOS program that +uses the ESC alot. + +--John + +- Added ctrl-enter to Xkeyb thanks to Veijo Vilva + + +Sat Nov 19 1994 + +- Latest terminal.c from John Davis. + +Fri Nov 18 1994 + +- Patch for PS/2 mice from Keith Parks . +- Slang code replacement in test. Set USE_SLANG in Makefile. + Slang is a faster terminal control library than ncurses. + Requires slang (amy.tch.harvard.edu in pub/slang , or tsx + in the dosemu Development subdir). Many thanks to + John Davis (davis@amy.tch.harvard.edu) for trying to + win us over. +- Patches to /init/parser.y for /etc/dosemu.users. + +Wed Nov 16 1994 + +- Rod May has noted that due to race conditions in the kernel + during partition access in kernels in the 1.1.51 - 1.1.61 and + I believe 1.1.63. Don't have his E-mail here, but it DOES + CAUSE CORRUPTION and therefore BEWARE of using PARTITION + ACCESS in these kernels! +- First steps towards making DOSEMU platform independant by + the Boch's project, Kevin P. Lawton" . +- JL's latest bios.S. Cleaned up and documented. +- Latest Makefile, DPMI, etc cleanups. Moving towards a + Makefile.common system. + +Sat Nov 12 1994 + +- Release pre0.53_33. +- Han's latest, now he has a self-contained emumodule that includes + SIG and really gives DOSEMU the power it has desired for quite some + time to make use of the kernel. You will need to set the #if 0 to 1 + in emu.h as well as: + /* From Hans: If you you need the emumodules special features + * You also must load the modules as follows: + * login in as root + * cd /usr/src/dosemuXXX/syscallmgr + * ./insmod syscallmgr.o + * ./insmod -m ../emumod/emumodule.o + */ + +More background: + +Hi Jim + +As you didn't show up at IRC, I send you some explanations about the +new emumodule stuff and inbedded sillyint. + +I established two interfaces to the emumodule, which I think can be +expanded for all other features, that we need in the future: + +1. A standard Linux systemcall (over syscallmgr) for all + functions that needs the kernel support functions (like printk). + During within this syscall, the scheduler and/or an interrupt + may take away control from dosemu. + The calling convention of this syscal is defined in + ./include/emusys.h as + extern inline int emusyscall(int mode, int params) + and can be called at this time as + + result = emusyscall(EMUSYS_GETVERSION,0); + result = emusyscall(EMUSYS_GETSUPPORT,0); + result = emusyscall(EMUSYS_REQUEST_IRQ,irqnum); + result = emusyscall(EMUSYS_FREE_IRQ,irqnum); + +2. I implemented a so called fast syscall via an INT 0xe6 to Linux + (not to DOS). + This syscall has not the great overhead that the standard syscall + has and can't be interrupted by Linux's scheduler or interrupts. + With this we can access kernel space much faster then with kmem + or other techniques and also we can set some bits of emumodules + variable. We can check some status, and after return from + fastsyscall we are shure that the status is what it says. + Its also possible to realized some of the DPMI functions with + this kind of syscall. + But, this syscall must run with interrupts disabled, and also + can only use some the kernels macros, but not the support functions + which leads to rescheduling e.t.c. + I used this type of syscall to get and reset the IRQ-bits of + sillyint. + Also the fastsys calls are defined in ./include/emusys.h and + currently there exists: + + extern inline int fastsyscall(int mode) /* generic call */ + extern inline int get_and_reset_irq(int irqnum) + extern inline int get_and_set_irq(int irqnum) + extern inline int get_irq_bits(void) + +To enable dosemu to use the new features there must be REQUIRES_EMUMODULE +defined in emu.h (see at the comments at top of this file). +If it is not defined, sillyint is used as usual. + +One thing at last: I always are on war with "make", so please check +and change the Makefiles, that they do compile emumod/ and syscallmkgr/ +if REQUIRES_EMUMODULE is set. I was to silly to do so. +I also would like to do make from the subdirectories directly during +devellopment phase. + +...Ah, and the patch fits into 0.53.32 with dualmon.patch already in. + +So long +Hans + + +- Small patch to ./mouse/mouse.c which resets the initiated mouse + subroutine on calls to fnx 0x21. This previously wasn't reset and + caused future programs to crash DOSEMU when they loaded over that + space. Now to see why some programs (Foxpro/Word Perfect Office) + don't recognize the internal mouse driver :-(. + +Wed Nov 9 1994 + +- Various patches from lee@tengu.in-berlin.de (Kang-Jin Lee) to + makefiles. + +- From Hans : +# - If you have a dual-monitor configuration (e.g. MDA as second display), +# you then may run CAD programs on 2 displays or let play your debugger +# on the MDA while debugging a graphics program on the VGA (e.g TD -do ). +# You also may switch to the MDA display by using the DOS command +# mode mono (mode co80 returns to your normal display). +# This feature can be enabled by the switch "dualmon" like this: +# video { vga console graphics dualmon } +# and can be used on a xterm and the console, but of course not, if you +# have the MDA as your primary display. +# You also must set USE_DUALMON 1 in include/video.h. +# NOTE: Make shure no more then one process is using this feature ! +# ( you will get funny garbage on your MDA display. ) +# Also, you must NOT have the dualmon-patches for kernel applied +# ( having the MDA as Linux console ) +# + +Tue Nov 8 1994 + +- Small fix to mouse.c + +Tue Nov 8 1994 + +- pre0.53pl32 released +- Fix for Xdos dealing with missing link options with many + Thanks to heiko@POOL.Informatik.RWTH-Aachen.DE. +- From Rob May: +Hi James! + yep, this is an interesting point. It's not just lilo that will +have problems, some other boot sector programs (which rewrite the MBR) +will need this "workaround" and therefore won't work with this patch. +(I would recommend using one of the newer boot sector programs as they +are better than lilo, but this is a different point). + Unfortunately this "workaround" is a very poor long term +solution, as using the current part-table is the only safe option. +This only affects people who boot using DPA with lilo up their MBR, but... +Since we are basically hacking the part-table to only one valid bootable +partition, a solution is to cut out the middle-man and boot the partition +directly, this way we get the current part-table and whatever boot +sector code you have is irrelevant. Patch to emu.c follows.... + Any Comments? + Ciao, + Rod May. + +Sun Nov 6 1994 + +- Various PIC patches from JL, also left PIC disabled. +- Changed some of the automatic X11 code thanks to Jochen. + +Thu Nov 3 1994 + +- Released pred0.53_30. +- Fixed Serial (removed r=0 setting at the end of inb() in + ports.h) +- Removed SA_RESTART for some functions (atleast temporarily) + +Wed Nov 2 23:04:02 1994 Jochen Hein (Hein@Student.TU-Clausthal.de) + + * [Makefile] + guessing in X installation + + * [keyboard/*] + moved all keyboard-related file there, addes a Makefile + termio.h moved to :/include + + * [*/Makefile] + Cleanup in clean targets, CFLAGS simplified + + * [commands/isemu.asm] + New file, disassembled isemu.com + +Tue Nov 01 1994 JES + +- Released pred0.53_29. +- Tested that Helppc ran. +- Added STI to keyboard int16 of bios.S. One fix for PIC, for + now :-). +- Changed && -> & in pic_request() if pic_iret(). + +Wed Oct 25 1994 JES + +- Latest v-net app from Vinod + ipxbridges. + +From: Rod May +Hi there, + here's a patch (to pl28) which makes the use of "mkpartition" +unnecessary for direct partition access. Also the partition number is no +longer required. i.e. instead of specifying: + disk { partition "/dev/hda3" 3 } and running "mkpartition", +you only have to specify: + disk { partition "/dev/hda3" } +The MBR is read from the appropriate hard drive during start-up. +Not only does this simplyfy things but stops potential mistakes +including: out of date MBRs, specifying hda instead of hda3 etc. +These were not flagged before and could lead to "interesting" results. +Using the old format will get you a config warning but otherwise +the partition number will just be ignored, + Ciao, + Rod May, + stoke@melbourne.dialix.oz.au + + +Hope this patch is useful, next effort might be read_sectors() which +needs a bit of work! + bye for now, + Rod. + + +Sat Oct 22 1994 JES + +- More xinstallvgafont.sh fixups with thanks to : + > Kang-Jin Lee + > lee@tengu.in-berlin.de +- Makefile enhancements from Marty. +- Detach updates by n1046128@student.fit.qut.edu.au (WAYNE P MEISSNER) +- Modified ports.h by Markky + +Wed Oct 19 1994 JES + +- Release pre53_28.tgz +- From JL: + +From popserver Thu Oct 19 14:23:31 GMT 1994 +Received: from marlin.ssnet.com (ssnet.com [165.113.8.3]) by Fox.nstn.ns.ca (8.6.8.1/8.6.6) with SMTP id KAA03316 for ; Wed, 19 Oct 1994 10:40:18 -0300 +Received: by marlin.ssnet.com (4.1/SMI-4.1) + id AA21140; Wed, 19 Oct 94 09:39:00 EDT +From: jlarry@marlin.ssnet.com (J. Lawrence Stephan) +Message-Id: <9410191339.AA21140@marlin.ssnet.com> +Subject: pic announcement +To: macleajb@ednet.ns.ca (James MacLean) +Date: Wed, 19 Oct 1994 09:38:57 -0400 (EDT) +X-Mailer: ELM [version 2.4 PL20] +Content-Type: text +Content-Length: 2237 +Status: RO + +Hi Jes - + +Here is the new pic code. It is implemented as contidional code, so you'll +have to un-comment a line in the Makefile if you want to try it. +Look for the string NEW_PIC in the Makefile; the instructions are right +before it. + +Because the serial.c pic mods don't work very well yet, I have provided +two options: The firstone only implements pic for keyboard and timer code. +This stuff is pretty solid, and I would appreciate bug reports, etc. +The second option implements pic for timer, keyboard, and serial interrupts. +The serial code is knows to have bugs, although it is useable. PLEASE, +if all you know is that the serial code doesn't work right, DON'T send +a bug report. I already know that much :-). On the other hand, if you have +a fix, or even just a partial one, please let me know. The serial bug +shows as an extermely low send rate; only half as fast as whennot using pic. + +I would appreciate any bug reports on the keyboard and timer code. They are +not complete, but I believe this code to be bug-free. + +And now, a (very) little technical information for the curious: + +There are two big differences when using pic. First, interrupts are not +queued beyond a depth of 1 for each interrupt. It is up to the interrupt +code to detect that further interrupts are required and reschedule itself. +Second, interrupt handlers are designated at dosemu initialization time. +Triggering them involves merely specifying the appropriate irq. + +Since timer interrupts are always spaced apart, the lack of queueing has no +effect on them. The keyboard interrupts are re-scheduled if there is +anything in the scan_queue. The keyboard pseudo-input register, +*LASTSCAN_ADD, is set to 1 (an otherwise unused value) to indicate that it +has been read, and can therefore be re-filled. Both the keyboard and timer +bios routines now have outb 0x20 to signal the EOI. The OUTB_ADDR is no +longer used. + +This has been slow in coming, I know; and it will be a while before it gets +fully utilized. But, at least when the serial code gets done, it should +help get dpmi on its way. I'll keep working on this; I think it has a lot +of potential to help organize the code. Good luck with it to all, and +thanks in advance for any comments. +- Mods to Makefile, xinstallvgafont, xterm +- pktnew.c uses run_int() instead of do_hard_int() +- Various Makefile mods +- Latest syscallmgr and emumodule from Hans. Remember the + insmod -m switch +- dosnet subdir now v-net. Hey, why not :-). +- Added 'make optionalsubdirs' to keep new test stuff out of + make most. +- Patches received to allow logical partitions to work with partition + access. All that is required is to have partition parm to + disk statement be higher than 4. + +Tue Oct 18 1994 JES + +- Added config.pd and -D+P debug switch for packet driver debugging + +Sun Oct 16 1994 JES + +Hi there! + Here's my patch for allowing DPA access to specific logical +partitions under dosemu. It applies to the patch level 27 code of +disks.[ch]. + Access to specfic logical partitions, rather than entire +extended partitions, is a great idea for the same reason that partition +access is better than wholedisk access: one day a poor linux +partition will suffer. Its used by specifying a part number greater +than 4 e.g.: + disk { partition "/dev/hda5" 5 } +the number itself does mot matter (as long as its greater than 4) as it +is the device name that's used to derive the part table entry info. +The only query is that I raised the MAX_HDISKS in disk.h from 4 to 8, +(I'm using 7 at the moment with no probems). Also I wasn't sure of the +number to pass to leavedos for the BLKGETSIZE error, is there a +convention for this? + This should never never happen though (famous last words). + Hope this is useful to others, + email me with any questions, + + Ciao, + Rod May. + stoke@melbourne.dialix.oz.au + +Fri Oct 14 1994 JES + +- From Marty : + - Added realclean to Makefile + - Changed if STATIC to ifdef STATIC + - #if 0'ed Warnings for running static in dos.c + +Fri Oct 14 1994 JES + +- Release pre53_27.tgz +- Small patch to int10.c so that recursive calls to char_out() + for tab's doesn't become and endless loop :-( +- Patch from Dino Dini to get that -F option working again. +- Patch to emu.c to make clean compiles without X. +- Added adder var to timer code so that 2 reads don't return + the same number. +- More I'm sure... But I forget :-(. + +Mon Oct 10 1994 JES + +- vinod@cse.iitb.ernet.in (Vinod G Kulkarni) has added multiple + protocal/sessions under DOSEMU via dosnet (dsn0) device. Requires + reading of ./dosnet/dosnet.README, loading the dosnet.o module, + ifconfig'ing it with a new IP, and running ipxbridge (ipxbridge0.1). + + +Sat Oct 07 1994 JES + +- andi@andiunx.m.isar.de (Andreas Kies) added the catch for + keystate to HogThreshold. Seems to be a great help for keeping + CPU usage down. + +Fri Oct 07 1994 JES + +- Removed SIGIO for PS2 and busmice. + +Wed Oct 05 1994 JES + +- Even better Configure script from Jan. +- Diffs from Martin. +- Changed Extended Move Block call in xms.c to not move outside + (from/to) of valid memory. + +Tue Oct 04 1994 JES + +- pre53_26 released. +- Added in some timer code from MACH for Scott to play with. + NOTE ****** This is scary stuff so beware of this release + as the code WILL get direct access of certain ports :-(. + You may wish to hold off until pre53_27. +- Latest Configure script from Jan. Keeps getting better, and + funner too :-). +- Added Hans latest SIG enhancements. + +Mon Oct 03 1994 Lutz + +- Placed back DPMI sigio/sigalrm signal calls to the old one ;-( +- Removed dpmi/call.S - now using a task switch via hlt-instruction +- Fixed some small DPMI bugs + +Sun Oct 02 1994 Jmaclean + +- Released pre53_25. +- Added 10 bytes to the malloc for 1 dir_ent struct, as the + dir_emt size seems too small. This fixes some bad mfs.c bugs. +- Added a nonl() by request of Fons Botman . + +Fri Sep 30 1994 Jmaclean + +- Released pre53_24. +- Added Scott's patch to fix EMS attempting to access invalid + memory. Fastest fix for a bug I've seen :-). +- Modified CHECK_HANDLE to deal with non-active handles as it + was previously just checking for ranges. This fixes Hans + finding of mem/d giving extraneous info. +- Moved Lutz's DPMI signal calls to within the signals for Hans. + +Wed Sep 28 1994 Jmaclean + +- Fixed one definite place for Divide/Stack Overflow to occur in int.h. + Problem occured when a default interrupt was called by a redirected + interrupt. This caused a recursion to the never lands. + +Tue Sep 27 1994 Jmaclean + +- Released pre53_23.tgz in diff form only. +- Added Jan's latest Configure script with X support. Modified + it to cp with backups /etc/dosemu.conf to /etc/dosemu.conf.backup. +- Remade first part of convascii of termio.c where esc seq's + are translated. Involved working around select getting + interrupted. +- Fixs to serial woes added in termio.c - keyboard_init(). + Moral of this story? A baud rate of 0 (B0) is equivalent to + sending the process -SIGHUP. Real fix added to ./video/terminal.c + by moving raw() statement to AFTER initscr() as spec'd in ncurses. +- Fixed to memory access errors in vga.c. Funny they seem to have + been OK when handled in a signal?. Now folks should be able to + resume switching VC's :-). + +Mon Sep 26 1994 Jmaclean + +- Pre53_22.tgz released. +- Added isemu.com from dan@fch.wimsey.bc.ca (Dan Fandrich) to give a + return code for testing if DOSEMU is running. +- Added MDA as valid card selection in dosemu.conf (=MGA) +- Some goodies from Hans Lerman: + +Well, here now is the summery of things that are in my patches which +I have tested under Linux-1.1.50. +( the patches come in a separate mail, because my mailer cannot handle +too large mails ) + +1. Loadable driver sillyint.o + It is compiled via a normal "make doeverything" + and put into the directory /boot/modules. + I changed the sig/Makefile, it also needs to know where the kernel + sources are (USRSRCDIR). + The source needs a -DMAKE_LOADABLE_DRIVER to compile sillyint.o + The same source can be used (without the -DMAKE..) to compile the + kernel linked in driver. I changed the sig/HowTo. + + The device major is put into an global variable SIG_MAJOR, so it + can be changed on loadtime by + insmod /boot/modules SIG_MAJOR=31 + + +2. Multiple SIG IRQ's can be defined in /etc/dosemu.conf and + also wether to use SIGIO or not. (see examples/config.dist) + Because of "sillint off", there is no need to disable SIG + via compile time option SIG. I recommend to let it be #define SIG 1. + I did not succeed with setting up a test for two IRQs at the same time, + so me and other people have to test it in the future. + + +3. Mapping of hardware ram is configurable via + + hardware_ram { 0xc8000 } # one 4K page mapped + or + hardware_ram { 0xc8000 range 0xcc000 0xcffff } # multiple mapped pages + + I added an overlap test to avoid wrong mapping, it is done after + configuration is finished, so it should catch all errors. + It relates to EMS, VBIOS and HARDWARE_RAM. + + +4. EMS-frame is now movable (needed for ram conflicts). + The ems keyword is expanded as follows: + ems { 1024 ems_frame 0xe000} + or + ems { ems_size 1024 } # defaults to old size of 0x10000 + + But the old syntax is valid too. + + +5. Size of VBIOS now configurable (most BIOSes at 0xc0000 have only 32K + so I can gain some space for hardware ram and/or UMBs). + /etc/dosemu.conf line: + added flag "vbios_size" + + +6. I corrected a situation (not a bug) concerning the "c" debug flag. + If "c" was switched off in /etc/dosemu.conf, you could not get + the config printout via + dos -D+c + Now this is possible. + + + +That's all for now, ...so long, +Hans + + +Sat Sep 24 1994 Jmaclean + +- Messed more with timer interrupt so that setting date backwards + doesn't cause divide overflow. Seems to fix it for going + ahead and back through time. Only problem is that when DOS + sees it's past 24hrs, it always moves its date ahead a day :-(. +- Changed config.graphics to config.vga in emu.c to control + setting allowvideoportaccess to 0. +- Added vc switching (set_process_control) to new signal_queue. + From conversation with JL I'm not sure this is gonna be solid + since now the signal_queue being run to switch VC's will rely + on returns from vm86(). + Still doesn't fix bad crashing when running Windows3.0 which + takes down my whole UNIX box :-(. +- Stopped DOSEMU from exiting when no diskette in drive. +- Added note to add users to /etc/dosemu.users in QuickStart. +- Fixed parsing of -C option to NOT expect arguements. + +Thr Sep 22 1994 Jmaclean + +- PRE53_21 Released. +- Messed with timer code. Changed tick setting to different + method and relied on time() to give acurate time. + Found what seems to be the 24 hour bug and inilated it!!!!!! + With many thanks to Corey. +- Added backspace/delete patch to Xkeyb.c. Thanks to Scott and Mark. +- Hacked up HogThreshold again. Rule: Closer you set to 0, the + lower the CPU usage. At 0 though, hogThreshold is turned off, + and int16/int2a are run full open. +- Added HogThreshold to int2a. +- Added patches to bring -F config file option back to life + thanks to Dino Dini . +- Added patches to ./video/X.c for better color reps thanks + to Mark. +- Prepared to move vc_acquire/release under new signal + handling... but failed miserably :-(. +- Makefile additions by Mark. + +Tue Sep 20 1994 Jmaclean + +- Removed most coding from SIGIO and SIGALRM signals in an + effort to remove race conditions. Now these signals just + mark that they've occured, save their context, and + continue. Lutz's DPMI will suffer :-(. Probably best to + go through DANG to get an idea of what's going on :-). + +Mon Sep 19 1994 Jmaclean + +- Wrapped lpt opens in exchange_uids() to allow "lpr" to run at + user level. +- Modified Makefile to create libdosemu$(EMUVER)pl$(PATCHL) and link + libdosemu to it. Now folks can compare releases just by changing + symlinks. Probably not what was wanted, but a close facsimile :-). +- Added SIG mods by Hans Lermen (lermen@elserv.ffm.fgan.de). More to + come. +- Cleaned up boot() in emu.c to just boot. Added memory_init() for + all specific memory setups for proper i86 boots. +- Added portuguese keyboard map. + +Sun Sep 18 1994 Jmaclean + +- Removed ioc_fd and replaced all occurances with kbd_fd. +- Started DANGing :-). + +fri Sep 16 1994 Jmaclean + +- Another change to the readonly open error +- Fix to mfs.c for return values for readonly mfs drives. + +Sat Sep 10 1994 Jmaclean + +- Added new dialog script from Jan Vandenburge. + +- Fixed internal Mouse driver not setting up for new SIGIO calls. + Thanks to Alan for providing a fix... But I did it ... + my way :-). Hope you don't mind Alan. + +- Fixed SERIAL not working. Martin's code was returning in + outb before anything was done :-(. + +- And now a little from Alan...: + +Hi James.. + + Not my usual stuff but I had to fix it. + + The parser seemed to be screwed up for me, in that it parsed two +files, both the local .dosrc and the /etc/dosemu.conf, this resulted in +the first linux drive becoming f:. All because the system though I had two +hard disks and one floppy image due to the parsing twice of the configs. + + Anyway attached is a fix so that it parses the local .dosrc first... +If it doesn't find one it takes the /etc/dosemu.conf. + + Secondly, I've implemented an /etc/dosemu.users file, similar to +minicom's way of working. This allows only selected users to execute dosemu. +Create the /etc/dosemu.users file with, on each line, the name of the user +allowed to execute DOSEMU. + This approach could be later expanded to specifying devices in the +dosemu.users file, so that only selected users are able to open specific +devices. + Thirdly, I had to fix up the Makefile a little for ease of use. +Notice I've defined an X11LIBDIR now too. + +Cheers. + +Alan. + +- Also: + +James, + For some reason the gfx_cursor for the mouse was commented out. +I checked the docs and someone commented it out because it was crashing the X +stuff. Well, This fix should get rid of that. As it only call mouse_do_cur() +if we are not running X. Before it was trying to mmap even when X was running. +Bad idea ..... + +Tue Sep 06 1994 Jmaclean + +- Added sig subdir with Silly Interrupt Generator in toe. Did + some preliminary testing with SIG to get it working with + 1.1.x kernels. + +Mon Sep 05 1994 Jmaclean + +- From Rain: + +I guess I let this stuff rust on my harddisk long enough, so here's +another patch ;) + +It includes: + +- support for 28/43/50 line modes +- make xdos exit properly when it fails to open the X server + (!includes a change to the printer code!) +- a color (xpm) icon :-) +- auto-detection of readonly floppies, which newer kernels require. + +It also includes my attempts to make bios scrolls scroll the X window +directly, in the hope that it would make things faster. +However, I found that it's at least as often slower as it's faster... + +I disabled the code and left it in just because Mark might be interested +to use it for the terminal modes. + +bye, +Rainer + +- From Martin Ludwig : + +Hello James! + +Here is my diff of dosemu pre53_12 to use a second (HGA) monitor for +dosemu while using X. I hope it will patch against pre53_15, too. + +Changes against pre53_12: +* new switches -Y and -Z: giving the names of the keyboard and mouse pipe +* keyboard input from pipe instead of STDIN +* mouse input from pipe instead of asking a mouse port +* doesn't install video-bios, uses int10.h instead +* watches port I/O of HGA-card and remapps video-memory if the second + page is used +* uses an own cursor position routine (the one in dosemu 0.52 was removed...) +* new X-prog (xdos) which sends mouse and keyboard events from X + to dosemu, emulating a raw keyboard + +Bugs: +* no semaphore for the HGA-memory +* no mouse in graphics mode +* no test if HGA-card is there +* it leaves rxvt or xterm disturbed if started from there +* problems with msherc-programs and the second page +* large xdos-window +* some others, I think + +Future plans: +* remove the bugs +* data exchange with X-progs +* perhaps an CGA or EGA emulator for HGA + + +Copyright: +* the same conditions as for dosemu + +I hope that's all, here is the diff + +Martin +Martin.Ludwig@ruba.rz.ruhr-uni-bochum.de + +Tue Aug 30 1994 Jmaclean + +- Added dosconfig script by Jan Vandenberghe. Requires DIALOG. I like + it :-). +- Included patches from Heiko for X and timer. +- Fixed emubat config thanks to Scottb. +- Modified io_select() calls to start dividing into 2 camps, + those that use sigio and those that don't. +- Added SIGIO ability to internal mouse driver. + +Fri Aug 26 1994 Jmaclean + +- Fix that HJ already sent in and I missed :-( for the + -T arguement of ld from flebbe@tat.physik.uni-tuebingen.de. + +Wed Aug 24 1994 Jmaclean + +- Added Enhancements to CDROM driver by Karsten Rucker. +- Added Lutz's STI patches and kernel patch sti.pat in .kernel + +Fri Aug 19 1994 Jmaclean + +- Changed OpenKeyboard to keyboard_init and closeKeyboard + to keyboard_close. Moved these out of video init procs and + into there own separate calls. This was causing problems + in video_config_init() as it would map in video bios + illegally :-(. +- Added Conditional for SIG in sigalrm to help io_select() + +Wed Aug 17 1994 Jmaclean + +- Added scan to charcode returned in Xkeyb.c by put_key() +- Tried to get lexer.l back up :-( + +Tue Aug 16 1994 Jmaclean + +- Messed around with Rain's patches until all modes started + to work. Mostly order of routines during initialization + that had minor problems. Now for example, telnet works + again. + +Sun Aug 14 1994 Jmaclean + +- Removed SOME __inline__'s from mfs.c, still memory pig :-(. +- Rain's latest patches. +- Minor fixups to various files. +- New Makefile from Mark. + +Sat Aug 12 1994 Jmaclean + +- Added Rain's massive cleanups and X mouse support. This is + NOT tested and will need some time to smoothen out. +- Added keymaps fixes for sg-latin1 from : + pingu@satu.baboon.ch (Ruedi Kneubuehler). + +Fri Aug 12 1994 Jmaclean + +- Added Scottb's patches to bios_emm.c and lexer.l +- Added lsm to ./doc subdir + +Thr Aug 11 1994 Jmaclean + +- Fixed keyboard latin1 statement and latin1 codes for better + emualtion thanks to vilva@xiron.pc.helsinki.fi (Veijo Vilva). + +Wed Aug 10 1994 Jmaclean + +- Added back some termio ioctl's to set ISTRIP,IGNBRK,OPOST + and such. Seems to remove backspace in non-rawkeyboard + mode. +- Added -lfl to toplevel makefile for fixing new parser not + resolving required libs. +- Added check to NOT add release key for ascii's with high + byte 0x00. + +Mon Aug 08 1994 Jmaclean + +- Thanks to Rob for pointing out the NCURSES raw() command + interference by way of his messages, the ICRNL thing should + be dispersed, and replaced with possible new bugs :-). +- CD-ROM extensions for DOSEMU with thanks to Karsten + Rucker (rucker@astro.uni-bonn.de). +- Parser additions from Scottb. We are back to the NEW + parser. Developer/Tester beware :-). +- DOSEMU-HOWTO updates from Mike. + +Fri Aug 05 1994 Jmaclean + +- Added kludge to keyboard so that keypresses in non-console + keyboard mode send keypress then keyrelease sequences. This + seems to fix atleast WordPerfect double chars. Doesn't + seem to help German aumlets though :-(. +- Scottb's latest parser patches adding back emu(sys/bat) and + a debug { X on } option. + +Thu Aug 04 1994 Lutz + +- dpmi/call.S is now totally rewritten to fix a problem with + returning to DOS code (stack problem). +- CallToInit() deleted +- more dpmi cleaning/preparing + +Wed Aug 03 1994 Jmaclean + +- Added patches from Tomi, Scottb to new parse generator... + but put back in old parse generator fot this release. Well + step back to this one hopefully in the next release. +- Added Rain's Xdos patches. Folks, we're well on our way :-). + Please read README.X and thank Rain for getting everything on + it's way. + +Mon Aug 01 1994 Jmaclean + +- Added new parser combo lexer.l/parser.y from Scott for all + to play with. A bit rough but Scott would enjoy feedback :-). +- Fix for bug in cursor positioning that causes garbage sometimes + This can be a MAJOR improvement to some people because it's appliation- + specific. +- Invisible cursor support in console mode. More visually pleasing. +- Cursor in ANSI and NCURSES is now put at position 0,0 whenever the + cursor is invisible or off-screen. +- Improvements to character blinking disable/enable code (this stops the + annoying blinking when you run PCTools over a telnet line) + +- Note from Markkk about new ansi_xterm : + +"ANSI Xterm 2.0 is a specially modified Xterm that is compatible with DOSEMU +and to enable the use of all 16 colors, and the entire PC character set. +This makes DOSEMU just like xdos in this area! + +Furthermore, just follow the instructions included with ANSI Xterm 2.0 +for an easy compile. Then all you need to do is type "xdosemu" to bring +up an Xwindow running DOSEMU. The xdosemu command automatically detects +for the existence of ansi_xterm. If it does not exist, it detects for +a regular xterm and rxvt next. + +Try it, and let Mark Rejhon know at mdrejhon@csclub.uwaterloo.ca +as he is responsible for this." + +Mon Aug 01 1994 Jmaclean + +- Markks latest and greatest additions to the Xterm capabilities + of DOSEMU. Best to get ansi_xterm from a site at this time :-). +- Detach capability added, thanks to Karl Hakimian. Now with + the optional -d parm, a new VT will be switched to for starting + and running DOSEMU. +- Added __map_page() to fix EMS error caused by remap() calling + __unamp_page() first. FoxPro is running again. + +Fri Jul 29 1994 Jmaclean + +- By Jon's request, set '_''s to '-' in keyboard parms + in examples/config.dist +- By Jon's request, added 'make most' option which does + NOT make the doc subdir. + +Tue Jul 26 1994 Jmaclean + +- Thanks to Scottb which fixed the DOSEMU memory problems + with pointing out that mmap annonymous needed PRIVATE and + not SHARED to work :-) We're back on the road again . + +Sat Jul 23 1994 Rain + + * Largely reworked {re}{un}map_page, removed v_mmap. + COPY/MMAP/IPC_EMS conditionals are now in map_page and friends. + Fixed MMAP_EMS: mmap() mapped the wrong way around (-: + Still doesn't work, mmap() patch is broken :-( + * EMS_IPC is completely screwed up and probably not easy to make work. + The problem is that one cannot do partial mappings of shared + memory regions. Left it in, though. + * added COPY_EMS conditional (for the standard 'mapping' method) + * added UNMAP_BEFORE_MAP conditional to leave out unmapping + of old mappings in map_page for MMAP_EMS. + Shouldn't be necessary because the kernel'll do this for us. + * made some calls to re{un}map_page dependent on COPY_EMS + because they are only there to update the logical memory + * restore_handle_state: unmap pages which were previously unmapped + * reallocate_pages: + reunmap only pages belonging to the current handle. + Fixed a bug: pages above newcount were left mapped. + * some minor optimizations, fixes and cleanups... + +Thr Jul 21 1994 JES + + - Patches from Rainer Zimmermann (zimmerm@Mathematik.UNi-Marburg.DE) + for time-problems with mfs.c + - Added procedure to make dosemu.map which is usefull for + finding where DOSEMU bombs, like kernel. Thanks Daniel :-) + +Sat Jul 14 1994 JES + + - Latest enhancements from Markkk. + +Sat Jul 11 1994 JES + + - Latest termio.c with keyboard enhancements from Markkk + - Added SIGIO conditional on uname(). + +Sun Jul 10 09:38:23 1994 Jochen Hein (jochen@No-MS-DOS) + + * added setlocale-call to dos.c + * reformatted highscan[] in termio.c + +Sat Jul 09 1994 JES + + - DPMI patches from Lutz + - Pktdriver enhancements by Rob + - Mouse patch from Douglas Gleichman p86884@tcville.ES.hac.com + - Changed install dos as per Jochen + - Windows 3.0 mouse is back. + - I added insert esc-seq to gettermcap of termio.c + +Tue Jul 05 1994 JES + + - Finally... Markkk succeeds where so many of us have failed. + NCURSES support is here, and I believe to stay :-). It's + still a bit rough, but it IS. You should truly think about + getting ncurses-1.8.5.tgz from: + netcom.com:pub/zmbenhal/ncurses/1.8.5.tgz + This of course brings color to serial lines and preps for + many more. I couldn't activate it on a telnet or rlogin + connect yet. Thanks Markkk, from many of us :-). + +Sat Jul 02 1994 JES + + - Finally... interrupt routines are called by functions of + an array. I'm certain that someone can come up with a better + method as I'm using 0x100 pointers in interrupt_function(). + +Tue Jun 28 1994 JES + + - Markkk brings actuall character set to DOSEMU + when in non-graphics mode ... Yaaa :-). Pease + read README.ibmset + - Patch to modify timers done in SIGALRM. + +Fri Jun 24 1994 JES + + - Markks console cursor patches, and more speed increses to + serial stuff. + - Small change to mfs.c to correct wrong return codes for + findfirst. Seems t speed up directory searches, but may + just be my imagination :-). + - Messed around with SIGILL a bit to try and track down reported + problem starting DOSEMU for some that causes SIGILL with + instr of 2e:f7 (cs:div?). No solution yet. + - Hacked ./init/parse.c a bit to exchange_uids when running + overide config files. Now /etc/dosemu.conf always runs, then + overrides can be speced in optional config files. + Doesn't work yet :-( + +Tue Jun 14 1994 JES + + - Next set of patches from Markkk. Major overhaul started + that is really improving termcap. Way to go Markkk! + - Alistair has completed in no time flat his automoted + DANG application. At this time I have NOT included his + perl script, but hope to soon. Look in ../doc/DANG[.sgml]. + - Michael provides us with an updated DOSEMU-HOWTO in + .txt,.ps, and .sgml format. + - Alan has another few patches bringing mice support along + even further. + - Tim's IPX patches for 1.1.14 kernels +. + +Mon Jun 13 1994 JES + + - First set of termcap patches from Markkk + +Sat Jun 11 1994 JES + +- Latest additions from Alan for busmice. +- Put CNTRx counters to -= 2. +- Clean up for Thursday release started. + +Fri Jun 10 1994 JES + +- Latest additions from Alan for busmice. +- UMB fixes by Scott. + +Sun Jun 05 1994 JES + +- Alan's latest patches for busmice with a start for graphics + support. +- Alessandro Rubini (rubini@ipvvis.UNIPV.IT) patches to make + Makefiles inherit important common include directory paths. +- By suggestion from Jon Tombs, make default inb return 0xff + and the creation of tempdir be strdup'ed incase it's freed + by a config request. +- Changed CNTR timers to count UP by 0x2221 instead of down. +- Changed a compare in xms.c according to Scott Buchholz. + +Thu Jun 02 1994 JES + +- Patches from Rob including updates to s3 code. +- Daniels idea about malloc problem worked into emu.c by + moving where scrbuf is allocated. Wonder why it was allocated + where it was. + +Sun May 29 1994 JES + +- Stuck in a fix for DIR a: error. It's a KLUDGE for now, but + I hope it will show where the error is. + +Thr May 26 1994 JES + +- More mods to int_queue_run(). Changed where set_leds() is + called. +- Moved int.h and int10.h to emu.c as that is where do_int() + is called mostly now. +- Scott's latest patches against 1.0.x & 1.1.x +- Rob's patches to remove linpkt from DOSEMU tree. + +Sat May 21 1994 JES + +- Lutz's latest patches cleaning up DPMI, etc... + +Thr May 19 1994 JES + +- Added Scotts proc/self/map kernel-diffs to ./kernel and + added his patches to bios_emm.c. Bios_emm.c is NOT using + these by default (See MMAP_EMS in bios_emm.c). +- Changed run_vm86() use of int_queue_running to *OUTB_ADD. +- Added extra sti to int10 bios handler. +- Added Jochen's patches to Makefile. + +Wed May 18 1994 JES + +- Added EMS patch from Scott +- Added diffs from Jochen. +- Started work on keyboard simplifying. Made lastscan into + *LASTSCAN a bios area variable. + +Mon May 17 1994 Jochen + +- Changed int 8&9 to use a memory location in bios for outb(0x20). + This nolonger needs to back off to DOSEMU from an outb20. This + caused vm86() not to return enough to run it's int8's, so + sigalrm has been modified to be called more often. At the same + time I now have the pit controllers decremented. Not sure if + this will be of any use or not. For fun just change the + TIMER_DIVISOR define at the top of emu.c + (Hopefully I'll do this for int9 scancodes next) + +- Fixed bug in EMS coded Sunday that stopped Windows 3.0 from + properly launching programs. + +- Makefile fixed to distribute ./ipxutils. Thanks Jochen. + +Mon May 16 1994 Jochen + + * [ doc/dos.1 ] + Updating the manpage to 0.52, current changes + + * [ init/parse.c ] + Cleanup + + * [ emu.c ] + Cleanup: Prototypes, parsing options, usage + +Mon 16 May 1994 JES + +- Added conditional to outb20 so that pktdrvr does not require + outb20 call. (As should only hard int's) +- Added Lutz's fix for Sigsegv()'s. + +Sun 15 May 1994 JES + +- To EMS added Alternate Map Register Set, OS/E Function Set. + Windows 3.0 now boots without EMS error(All necessary FNX's + are supported). Some other cleanup's to EMS including Handle + Naming. + +Thr 13 May 1994 JES + +- Totally changed int_queue_run(). Now only int's requiring + callback will be forced to. + +Wed 11 May 1994 JES + +- Markkk's latest serial. +- Made ipxutils subdir optionally compile + +Tue 10 May 1994 JES + +- Made int08 inline, still need 0x1c to be inline too. +- Added back outb20 check to fix stacking of interrupts + which included int08 stacking. +- Floppy drive sticking has mysteriously vanished. + +Mon 9 May 1994 JES + +- IPX updates by Tim Bird. Please look in ./ipxutils for more + info. +- Addition of -n pktdrvr option by Rob. Look in + ./examples/config.dist. + +Sat 7 May 1994 Jochen + + * [disk.c disk.h] + creating the name for the partition-decsription-file moved to + parse.c, because we need to check the existance of this file there. + without this file is running further useless. dosemu may hang + silently. + + * [emu.c] + use config.exitearly instead of exitearly and stop dosemu earlier + when set. + + * [parse.c config.dist include/mouse.h mouse/*] + attempt to use cleardtr for some mousesystem/microsoft compatible + mice. mostly unsuccesful. + + * [init/Makefile init/parse.c] + more compiler warnings removed. Error-checking implemented. + +May 4, 1994 JES + +- Jochen moves files around to conform to standards. Please + take care that your config is in the correct /etc/dosemu.conf + place. +- Alan improves on the internal mouse driver to include other + mice. +- Lutz includes patch against 1.1.11. + +May 2, 1994 JES + +- Jochen latest. Makes video bios memory area configurable at + boot time. Brought int10 inline. +- Narrow allowed ports when videoportaccess on, hopefully stop + problem of serial port being touched inadvertantly :-( +- pktnew/libpacket patches from Rob. + +May 3, 1994 Jochen + +- Files installed in places where they may match the FSSTND + +April 29, 1994 JES + +- Lutz's Latest kernel diff's. Now just 1 small one against + 1.1.10 and 1 for the 1.0.x kernels. This has fixed the + EmuBat/Sys boot option problem. + +- First attempt to move net stuff into net subdir and create + 1 ../net.o object for linking. + +April 27, 1994 JES + +- If your kernel is of the 1.0.x nature, patch with + kernel.1.0.x.diff. If your kernel is 1.1.x where x is less + than 9, patch with kernel.1.1.9.diff. If your kernel is + 1.1.9 patch with kernel.post.1.1.9.diff. Have fun :-) + +- Make keyboard stop dupping. Poor kludge until JLarry's + PIC code. + +- Jochen's continuing cleanup code. + +- Lutz's Patches to allow DOSEMU to run in 1.1.9. + +April 23, 1994 JES + +- modified code in DOSEMU to prevent errors due to stack + over/underflow when computations are done on offsets. + +- Lutz's latest kernel diffs to avoid under/overflow in stack + computations + +- Rob Janssen patches to fix int2f returns and understand + deskview giveup timeslices. + +April 19, 1994 JES + +- Rob's pktdrvr enhancements. Ready for testing. + rnzll3!pe1chl!rob@relay.NL.net. Should be possible + to run different stacks on this one. Doesnot require + loading linpkt. + +- Lutz's latest kernel.diff + patches. + +- Lutz's changed inline code back to assembler that's copied in. + +- Only do set_leds() when in RawKeybaord. + +- Moved DPR, DANG, EMUSuccess.txt to ./doc + +April 17, 1994 Hein@Student.TU-Clausthal.De (Jochen Hein) + +- Cleanup continues + * [Makefile] + -ansi -pedantic as a comment :-( + Updated depend-target. Should be done for clients/dpmi also + + * [cpu.c termio.c video.c] + removed unused include linux/mm.h + + * [emu.c video/terminal.c video/Makefile] + moved terminal-video-functions to video/terminal.c + + * [emu.h] + removed warnings for keyboard-layout-pointers. + added some external variables, should be moved later + + * [include/video.h video/video.c emu.c] + moved the installation of the inline int10-handler from emu.c + to video.c + + * [mouse.h mouse.c] + moved mouse-cursor-define from .h to .c + + * [emu.c video/vga.c] + moved some more vga-functions to video/vga.c + +April 16, 1993 JES + +- First draft of JLarry's timer/PIC code. See ./timer/README.pic + +April 14, 1993 Hein@Student.TU-Clausthal.De (Jochen Hein) + +- Major cleanup in video.c + * [Makefile] + changed to reflect other changes, removed unnessecary comments + and other old stuff. + added ./include to the list of include-paths and subdirectories. + added the ./video subdirectory + + * [include/* video/*] + created subdirectories for better code-structure. + + * [include/port.h] + moved inline-port-function from emu.h (smaller emu.h) + + * [video.c video.h s3.c modes.h] + moved to include and video. + + * [cpu.c] + added port.h to the include-files + + * [cpu.h] + made it compile with -ansi -pedantic + + * [emu.h] + made it compile with -ansi -pedantic + moved port funtions to ./include/port.h + + * [emu.c] + keep up with emu.h + + * [machcompat.h] + made it compile with -ansi -pedantic + + * [modes.h] + removed; content moved into video/vga.c + + * [periph/mkpartition] + better error checking and messages. + + * [sigsegv.c] + moved port funtions to ./include/port.h + + * [termio.h] + made it compile with -ansi -pedantic + + * [include/Makefile] + Makefile to do a make dist. All new files compile clean with + -ansi -pedantic, old files not. + + * [include/keymaps.h] + New file: Prototypes for keyboard layouts + + * [include/port.h] + New file: inline-port-functions moved from emu.h + made them compile with -ansi -pedantic + + * [include/video.h] + moved from ./video.h, modified to fit in and compile with -ansi + + * [video/Makefile] + Makefile to do a make dist. All new files compile clean with + -ansi -pedantic, old files not. + + * [s3.c, s3.h] + moved to video/, slightly modified. + + * [video/et4000.[ch] video/trident.[ch] video/vga.[ch] video/console.c] + New files, extracted from video.c and modified to compile with + -ansi -pedantic. + + * [video/video.c] + remainder of splitting video.c, didn't compile with -ansi :-( + +Thu Apr 14 1994, Jochen Hein (Hein@Student.TU-Clausthal.de + + * [periph/mkpartition] + added error-checking and some help-messages. + +April 12, 1994 macleajb@ednet.ns.ca + +- Lutz kernel patches to make ROCKET DOS :-) +- Mark's serial code almost better than the real thing. +- Jochen's 4 miles of patches :-). + - debug is configurable using meaningfull words + - keyboard is configured in /etc/dosemu/config + - work on making more than the first partition accessable + using partition access. + Look in examples/config.dist + +April 07, 1994 macleajb@ednet.ns.ca + +- Minor updates to bios_emm.c +- Modified HMA_MAP to use IPC so that proper mapping takes place +- Marks latest serial. +- Modified OUTBUFSIZE to 80 thanks to Vinod. +- Modified CHOUT thanks to Vinod. +- Fixed 'stick-ctrl-key' after VC switches + +April 06, 1994 macleajb@ednet.ns.ca + +- Added niemann@swt.ruhr-uni-bochum.de S3 code update and fix to + Errors beeping to stdout, not stderr. +- Added Marks latest serial changes. Now we can properly define + particular COM ports and more. Read examples/config.dist for + details. +- Latest enhancements from Lutz. +- Added Spanish keyboard support by jon@gtex02.us.es (Jon Tombs) + +April 04, 1994 macleajb@ednet.ns.ca + +- Added Bill's changes for better MDA support +- Added Alan Hourihane's patches for PS/2 mice, now for + describing mice in /etc/dosemu/config, first spec the port + serial { device /dev/cua0 } then describe the mouse, + mouse { device /dev/cua0 microsoft }. +- Added latest serial from Mark, he's approaching true 16550 :-). +- Added check for video mapped when emulating ports. +- Added more specific checks for allowvideoportaccess. + +March 26, 1994 macleajb@ednet.ns.ca + +- Latest serial from Mark. +- Added Belgium Keyboard thanks to : + jvdbergh@wins.uia.ac.be (Jan.Vandenberghe) + +March 24, 1994 macleajb@ednet.ns.ca + +- Concluded that E000 video bios'es are now working, many thanks + to danpop@cernapo.cern.ch (Dan Pop) +- Broke sigsegv() out of cpu.c into just sigsegv.c as to compile + do_int(), inb/outb(), et al inline now needs a 6meg swap on + my 4 meg system at work :-( + +March 23, 1994 macleajb@ednet.ns.ca + +- Added #def's for E000 video bios testing. +- Added more DPMI from Lutz. +- Added new mouse.c from Alan Hourihane, this should become very + usefull in NCURSES mouse Client implementation. +- Seperated emu.c -> emu.c,ports.c,int.c and moved int.h & ports.h + into cpu.c in first attempt at bringing do_int and inb/outb inline. + +March 12, 1994 macleajb@ednet.ns.ca + +- S3 video code started by niemann@swt.ruhr-uni-bochum.de + (Christoph Niemann) +- More DPMI thanks to LUTZ. +- Serial INT14 updated thanks to Mark Rejhon. +- Modest changes in code to continue clean up. + +March 12, 1994 macleajb@ednet.ns.ca + +- More DPMI thanks to LUTZ. +- Serial INT14 updated thanks to Mark Rejhon. +- Fixed (kludged) bugs in VC switching, Added -Wall + to start cleaning up code. Changed do_int to inline, no + benefit. + +March 9, 1994 macleajb@ednet.ns.ca + +- DOSEMU becomes one again (one process) + +March 4, 1994 macleajb@ednet.ns.ca + +- VC switching in RAW mode now uses CTRL-ALT-x. + +- Ronnies serial patches to allow more mice to roam and modems + to run faster/better. ronnie@epact.se + +- Jochen's patches to straighten up video support and remove + unused DISKS statements. In keeping with the FSSTND-draft + our lib has been moved to /usr/lib. + Thanks hein@centeotl.in.tu-clausthal.de + +- Alanh patches to allow switching between linpkt and IPX + on the fly with the "ipxsupport" option. alanh@metro.co.uk + +- Koenig@nova.tat.physik.uni-tuebingen.de (Harald Koenig) + patch to show last 10 characters of path on volume + requests on the redirected drives in place of LINUX.1, + LINUX.2, etc... + +- Lutz Molgedey has been making great advances in DPMI and + although not ready for household use, is included. + molgedey@theo-physik.uni-kiel.de. + +February 20, 1994 macleajb@ednet.ns.ca + +- Wordperfect HOME key stopped working :-( why ????? + +February 17, 1994 macleajb@ednet.ns.ca + +- Fixed function 0x23 in mfs.c redirector + +February 9, 1994 macleajb@ednet.ns.ca + +- Added config option dosbanner to optionally stop DOSEMU + messages at startup. + +- Added allowvideoportaccess config option to give video card + bios routines access to ports. + +- Fixing Keyboard int15 4f to return AH=0x86. This fixes + WordPerfect HOME key problem, and nothing else :-(. + +February 1, 1994 macleajb@ednet.ns.ca + +- First layer of terminfo patches in from Corey + (corey@amiganet.chi.il.us) + +- Mouse now closes/opens on VC switches. I can now use X and a + mouse in DOSEMU. Serial is still buggy though. + +- gorden@jegnixa.hsc.missouri.edu only came on stream less than + a month ago and has already added a pktdriver interface. Could + we have another Robert S. here? + +- dos_helper() int 0xe5 changed to 0xe6 to allow my FCB program + Direct Access to work. + +- FCB callouts added to redirector. Lahey Fortran now works, + as dos Direct Access. + +- Tim_R_Bird@Novell.COM IPX has been added to dosemu. What's + Tim's next surprise? + +- Tim fixed LREDIR.EXE so that emufs.sys no longer has to + be loaded once at boot. + +- Tim fixed an error in SIGSTACK. + +- Int15 0xc0 callout has been modified to deal with DOS programs + wishing to override scan codes going into the KBD buffer. Still + needs work. + +- Allow USAGE to display before default stderr redirection. + +- Made dos auto-redirect to stderr if no redirection given on + command line. + +- Added simple saytime() function to display message and CLOCK + time. + +- Modified hard_int routine many times to try and make int 9, as + well as other ints stay serial and yet nt have to wait for + program to return to return_address. + +- Added Corey@amiganet.chi.il.us patches to exchange + stderr <-> stdout. This is to prepare for terminfo. + +- Indented all source code according to INDENT.PRO. + +- Deleted int16 function. + +- Added dpmi/dpmi.h include for interrupts. + +- Added code to allow int09 (keyboard) to allow another interrupt by using + outb(20) called by dos programs. + +- Modified inline int09 to pass ALL keys to int15-4f function. + +- Added a far return for DPMI call to go protected. + +- Removed old int16 function. + +- Changed int08 inside of do_int() to return after being called. + +- EMS is very close to full 4.0 specs. I've taken the need for + patching the kernel out at this time so that more folks will + test EMS. Unfortunately it is horribly slow. Many thanks to + mikebat@netcom.com (Mike Batchelor). + +- (Theadore Ts'o's)tytso@ATHENA.MIT.EDU created the booton bootoff + patch. Now a user can boot from a bootdisk a:, type diskimage, + and then return control of a: to /dev/fd0. Alright :-). + +- Added routine to allow Diamond Card to go graphics. + +- Much effort has been put into keyboard enhancements, + which have not been that fruitful. Need to have numlock off + to cut & paste within MSDOS edit, and WordPerfect 5.1 's home + key does not work as expected. + +- Keyboard kinda does a REAL int9. + +- Fixed UMB size 0, and move XMS edx:ebx problem. + +- Added more SS overflow checks. + +- Added call in SIGILL to check if illegal op-code is just another + hard interrupt ending. It calls int_queue_run() and sees if such + was the case. + +- Added WORD macro in push/pop functions. + +- Added check for DPMI far call. + +- Small ET4000 updates from info from Robert Saunders. Thanks. + Needs more testing. + +- Redirector now truncates files properly (with fingers crossed) + Calls to create_truncate existant files is closer to correctly + being handled. + +- Modified findfirst/findnext routine. + +- Added 0x80 flag to tell DOS it's not lanman redirector. + +- Poor excuse for an update to work with ../NUL device. Also + added code to allow mfs.c properly deal the GetRedirection + with Novell-Lite Printers. + +- Added lredir using environment vars by + coosman@asterix.uni-muenster.de + +Wed Dec 28 00:00:00 1993 jmaclean + +- Problem with multiple instances of emufs.sys has been destroyed + I believe. + +- tytso@ATHENA.MIT.EDU (Theodore Ts'o) has added the ability + to boot off a bootimage file as drive a:, then release drive + a: back to dosemu as the actual /dev/fd0 via booton/bootoff + in autoexec.bat. See ./examples/config.dist for parameters. + +- As usual the keyboard routines have been dug up more. + Still MSDOS edit.com needs numlock off to cut&paste. ARG! + Someone could really make my day if they pointed out what + I'm doing wrong. + +- Added video option chipset for diamond for some diamond + cards, specifically Diamond Speedstar 24x with a + Paradise/Western Digital chipset. Many thanks to + patrickm@gas.uug.arizona for putting up with me. + This does no more than allow 'graphics' for + this card type. + +- Overhauled EMS to allow it to work with programs that I + use. It nolonger requires the mmap.diff patch, so please + don't use it. I'm just carrying it along because the way Robert + did EMS mapping was better, and I hope to move back to that + after folks start beating this version around a bit. + +- Debugged XMS a bit more to work with UMB's and passing the + correct XMS address for XMS moves. + +- Some more messing with ET4000, thanks to Robert alias + Nils Rennebarth for the ET4000 stuff. Unfortunately I am + in need of another tester to start relaying bugs. Any takers? + +- Diffed in patch[1-3]_against_pl3.dif applied: + - First patch fixed a timer problem mainly. + - Second patch limited VC switching to left alt key. + - Third patch almost finishes my changes in keyboard + routines. + +- coosman@asterix.uni-muenster.de submitted patches to mfs.c + which allow lredir to access environment variables like are + now used in emufs.sys. + +- coosman@asterix.uni-muenster.de fixed the 15 character limit + on keybaords, some keyboard extenders should now work, + yes :-). + +- coosman@asterix.uni-muenster.de also submitted dumpconf.asm + to try and fix the multiple emufs.sys instance problem. + Still unsuccessful, any takers? + +- Changed dosvga.h to video.h + +- tytso@ATHENA.MIT.EDU (Theodore Ts'o) aided in getting mfs.c + to do the ftruncate thing more like DOS. Still not sure if + it's up to the full 100%. + +Mon Nov 29 00:00:00 1993 jmaclean + +* Another update of multiple bugfixes :-). + + - Keyboard internals have been really rearranged. I'm not sure I'm + keeping them this way, but now cut&paste problems should hopefully + go away. Also any program that looked at the bios memory for + checking ctrl-alt-shift statuses have a better chance of operating. + Still seems to be some lagging (somewhere). + + - German keyboard patches thanks to hein@tlaloc.in.tu-clausthal.de + (Jochen Hein) which should fix that multiple alt key problem a + couple people have asked about, as well as some incorrect mappings. + + - As always mfs.c has been updated again to react to another funny + one on DOS's part. If a file was open write, and actually written to, + it should be O_TRUNC first. If its just opened for writing, and not + actually written to, the file should exist as was when closed. ARG! + The fix is a kludge, and needs some work! + + - Ross Biro gives us some more fixes for + the redirector, and includes a new toy/option for emufs.sys. Now + in you config.sys you can add a device statement like: + - device=c:\emufs.sys \${HOME}/dosstuff + which will allow you to use your home directory as part of the + path. Any environment variable can be used. + + - alan@spri.levels.unisa.edu.au (Alan Modra) fixed some flag setting + problems in the emulated int13 code. + + - bde@kralizec.zeta.org.au (Bruce Evans) noticed some initilization + problems as did - + + - alan@spri.levels.unisa.edu.au (Alan Modra), so we have changes to + the registers at boot time, good for some disks that need everything + just right :-). + + - And not to forget fin!chip@dg-rtp.dg.com (Chip Salzenberg) who got + rid of those annoying errors when bison was running. + + - Makefile has been altered hopefully to cleanup those 'separator + missing messages'. + + - parse/parse.c will not be contained in the distribution and should + limit that parse.tab.h not found error. + +* From announce0.49pl2 + + - This release has some important changes, actually bug fixes, and some + new functionality to emufs.sys. (The hardrive redirector) It allows + us to redirect the redirected drives using a DOS program LREDIR after + booting, thanks to Tim Bird (Tim_R_Bird@Novell.COM) . + + - Video switching is still growing, and has had some fixes for Trident + cards, as well as changing routines for saving and restoring video + memory above the first bank. ET4000 code is still giving some problems + so be aware. + + - Keyboard control has been enhanced using a real time int16 interrupt. + Thanks Tim. This is important for WordPerfect users, Dosshell, + Qbasic, and many other problems that have been attributed to keyboard + problems. This has not been proved to fix the 1 key behind problem + yet.:-( + + - Sdh@po.cwru.edu (Scott D. Heavner) has allowed us to specify an + alternate config file using dos -F'filename' etc with his patches. + + - A problem writing files in WordPerfect on emufs.sys drives has been + irraticated. + + - Dir listings that included the volume label attribute were only + returning the volume label, and no dir listing. Fixed. Thanks to + karel@obelix.icce.rug.nl. + + - Finally the keyboard problem has had another important fix thanks to + ronnie@lysator.liu.se (ronnie s.) via Robert (gt8134b@prism.gatech.edu) + that should fix the keyboard behind by 1 problem. + +Tue Jul 13 19:54:13 1993 rsanders + + * Makefile: cleanups... + +Tue Jul 13 19:24:54 1993 root + + * cpu.c, cpu.h, emu.c: + changes for using the new (0.99pl10) signal stacks + +Wed Jul 7 21:44:51 1993 root + + * emu.c: hook for parse_config(name); + + * parse.c: + added support for ~/.dosrc and parse_config() now takes a filename + argument to override .dosrc check. + +Mon Jun 7 20:26:09 1993 (root@hrothgar) + + * emu.c (char_out): more BIOS-like handling of int + 0x29...character attributes are now reserved, and scrollup now + uses the attribute from the previous char (needs more work). + +Fri Jun 4 01:43:39 1993 (root@hrothgar) + + * cpu.c (find_port): added Andrew's patches diff1-6, which include + the maskable port I/O. Began fixes to allow ports beyond 0x3ff. + +Thu Jun 3 23:05:26 1993 (root@hrothgar) + + * emu.c (ms_dos): fixed up the EmuSys and EmuBat directives. They + now take string arguments. + +Thu May 27 21:38:08 1993 (root@hrothgar) + + * bios_emm.c (bios_emm_init): moved into dosipc.c: memory_setup(). + Now checks that /proc/self/mem is mmap()able, and turns off EMS + support if it isn't. + +Wed May 26 01:09:23 1993 (root at hrothgar) + + * moved 0xE000 segment routines to 0xF000. also changed reboot + code to use inte5, ax=0xffff + +Tue May 25 01:41:53 1993 (root at hrothgar) + + * added early support for the EmuSys and EmuBat directives, which + remap CONFIG.SYS to CONFIG.EMU and AUTOEXEC.BAT to AUTOEXEC.EMU. + This works now, but I'm not sure when I should disable it. + +Mon May 24 21:50:33 1993 (root at hrothgar) + + * added CMOS checksum. + + * dosemu 0.49 released! + +Fri May 14 00:41:08 1993 (root at hrothgar) + + * cleaned up the int10h ah=9/0xA calls...they now work correctly + except for possible bounds checking. + + * made floppies safe, but slow...work on this. + +Wed May 5 02:18:23 1993 (root at hrothgar) + + * added mouse support and console switching even in graphics + mode. + +Tue Apr 20 00:49:07 1993 (root at hrothgar) + + * added DMSG_PAUSE for pausing the parent... + + * fixed a lot of int10() problems, including cursor positioning + and int10h 0xa/0x9. + +Fri Apr 16 08:58:43 1993 (root at hrothgar) + + * added VGA BIOS support...user vgaon.com to allow it, vgaoff.com + to stop it. + +Wed Apr 14 03:44:36 1993 (root at hrothgar) + + * now have mfs.c updated all the way to diff9, which has multiple + drive support...(not my doing!). also translated linux.asm into + emufs.S. + +Mon Apr 12 22:08:39 1993 (root at hrothgar) + + * added the 1-second tick in dosipc.c. the child gets a SIGALRM + every second, which child_tick() handles. currently only calls + print_tick(). + +Tue Apr 6 00:20:05 1993 (root at hrothgar) + + * stold the Mach DOS emulator's UMB routines. PCShell no longer + crashes, PKZIP now uses it. PKZIP 2.04e won't use EMS :-( and + neither will debug. + + * ported bios_emm.c (EMS emulation) from the Mach DOS emulator. It + relies on my kernel hack of mmap(/dev/selfmem). Works pretty well, + emulates EMS version 3.2. + + * applied diff1,diff2,and diff3 from Andrew Tridgell. MFS works + better, faster. MFS also adjusts for being run suid root. The + default interrupt (0xfe) conflicts with Turbo Debugger. I suggest + a change to 0xe5. + +Thu Apr 1 03:08:22 1993 (root at hrothgar) + + * translated the bootsect.S code for as86. This is nice for + general interest, and for the distribution hdimage (which requires + fdisk /mbr to fix it, by the way). + +Wed Mar 31 01:00:37 1993 (root at hrothgar) + + * integrated Andrew Tridgell's port of the Mach mfs redirector. + Seems to work okay, if not blazingly fast. Sure beats linux.exe. + + * changed VIRT_SCREEN_BASE and INIT_VIDEO_MODE for MDA to actually + show MDA mode 7 and base of 0xb0000. + + * added PARTITION type. only works with /dev/hd?1. relies upon + the file "/etc/dosemu/partition" to be the first sector (Master + Boot Record) of /dev/hd?. + +Tue Mar 30 02:51:33 1993 (root at hrothgar) + + * changed disks.c a lot, added the IMAGE file header code (to + auto-detect geometry from an image file). + +Mon Mar 29 00:47:41 1993 (root at hrothgar) + + * added the int 0x2f, ax=0x1680 "give up time slice" function. + does a usleep()...is this best? thanks to Andrew Tridgell. + + * changed disks.c to simply accept FLOPPY_A as defined + + * changed -f option to take an argument + +Sun Mar 28 02:07:50 1993 (root at hrothgar) + + * wrote dosconfig.c to replace dosconfig.sh. + + * changed the int16h ah=0x55 to return eax=0 for MS-WORD 5 (check + this with Andrew Tridgell). + + * fixed up some of the CONFIGURATION (int 11h value) stuff. see + emu.c, config_init() + +Mon Mar 8 00:37:07 1993 (root at hrothgar) + + * added dosconfig.sh for "make config", rearranged Makefile to be + a little safer. It tries to keep the user's config.h updated, but + not to recompile too often. (dosconfig.sh uses awk) + + * cleaned up get_video_ram()...Turbo debugger 3.0 now works, at + least the video stuff does. + + * added some fixes so that leavedos() will timeout after 3 + seconds or so, but I still need to find out why/where it hangs. + + * added memory.h and video.c. added "make dep" to Makefile. + +Sun Mar 7 15:05:10 1993 (root at hrothgar) + + * added an ugly hack for multiple screen pages...nasty. works okay + for Turbo Debugger 1.0, though. also began marking hacks with + XXX...most everything new&old is a hack, sadly enough. termio.c & + emu.c need to be robbed of the screen functions, which will be put + in screen.c. also the emu.h and termio.h headers...clean the + get_video_ram & put_video_ram stuff... + +Thu Mar 4 01:23:04 1993 (root at hrothgar) + + * added the proper set-iflag-after-instruction STI emulation. it + doesn't seem worth it, so I disabled it be default (define + PROPER_STI in cpu.c or the Makefile to get it working). + + * got the HMA working perfectly. moved the IPC_RMIDs into + memory_shutdown(), called by stop_dosipc, so that the HMA area, + which isn't always attached, wouldn't go away unexpectedly. + + * used krishna's new ipcdelta stuff (the SHM_REMAP flag) to make + the low page shared, too. I should break this out of the main + segment1. verified that it works. + + * added SIGCHLD handler, fixed up the DMSG_ACK situation. still + need to design a rational, interrupt-like protocol for this. + + * added shared memory to dosemu. I haven't gotten Krishna's new + stuff yet, so I don't share the first page (I NEED TO!). 2 segments, + one from 0x1000-0x9ffff, the other 0xc0000-0xfffff. Because of + stupid mmap(), I can't share the segment from 0xa0000-0xbffff, as + mmap() needs memory under, and IPC can't have memory under. + +Tue Mar 2 02:07:57 1993 (root at hrothgar) + + * apparently, whatever badness in DOS 5.0 that caused the SIGSEGV + when booting has gone away, or I've fixed it, so I took the + boot_in_progress checks out (partially, I'm a little wary). We'll + see how it goes. + + * made the child process munmap() everything from page 2 (address + 4096) to the page before LIBSTART. this should cut down on memory + usage a little, as well as make attaching shm a little easier. + still doesn't fix first page problem, but Krishna is doing that. + + * changed the format of the DOS HELPER function to show interrupts + (now takes low int in BH,high int in BL). Also added a parameter + to show_ints(). also added -2 option for 80286 flags emulation. + (real-mode 80286 keeps upper 4 flag bits (15,14,13,12) clear. + + * added two new switches, -3 and -4, that allow you to choose 386 + or 486 "emulation"...this currently only changes the behavior of + the AC (alignment check) bit of EFLAGS, but since that IS the + Intel-recommended test...I need to add emulation for BSWAP, XADD, + and CMPXCHG instructions for 386 users running in 486 mode (just for + kicks :-). I wonder if PKZIP 2.04 is much faster in 386/486 mode... + +Mon Mar 1 01:35:53 1993 (root at hrothgar) + + * added code to keep virtual IOPL, NT, bit15 of eflags register. + this means I also have to interpret the 0x66 and 0x67 instruction + prefixes (32-bit operand and address, respectively). this must be + done in sigsegv()...it's just ignored now + + * split dosemu into 2 processes, one of which currently only + monitors keypresses. I use UNIX domain sockets--pipe() returns a + uni-directional pipe :-( + + * tried to speed up clear_screen() for non-console video. + +Thu Feb 25 21:19:10 1993 (root at hrothgar) + + * fixed show_regs() to display an iflag-adjusted eflags. I should + make access functions to _regs.eflags so that "virtualized" bits + are always handled correctly. + + * fixed iflag/IF handling...Matthias had forgotten to adjust iflag + on popf and iret. Silly boy. I wasn't doing much better, testing + IF directly from the _regs.eflags after return from vm86(), which + of course corresponded to the system IF...someone should write a + virtual-8086 mode tutorial! + +Wed Feb 24 11:33:59 1993 (root at hrothgar) + + * fixed the key-repeat bug. + +Tue Feb 23 01:36:17 1993 (root at hrothgar) + + * well, the ugliness with indirect far calling can be + skipped...however, I can only get pcshell to work with the mouse, + and it doesn't use callbacks! added lctrl+rctrl+capslock = toggle + kayboard mouse...arrow keys are direction, home is left and end + is right button. + + * made the first brain-dead changes for mouse support. + +Mon Feb 22 16:26:17 1993 (root at hrothgar) + + * cleaned up the show_regs() a bit, better display of flags, shows + 10 bytes before and after CS:EIP. + + * added the define FAST_BUT_WRONG29, which, if defined, makes + int29 fast char output faster, but makes programs that hook int29 + fail. ANSI.SYS is one of these. I leave FAST_... defined, as + ANSI.SYS doesn't work now anyway. + + * added the extended (32-bit) XMS 3.0 API. cleaned up xms.c to be + a little more 32-bit clean (i.e. doesn't fudge upper 16 bits of + register if it's not supposed to). Added the macros LWORD() and + HWORD() to emu.h... + +Sat Feb 20 17:43:50 1993 (root at hrothgar) + + * added Bill Bogstad's (bogstad@cs.jhu.edu) + auto-hd-geometry-detect code. It seems to work okay. + +Thu Feb 18 01:38:21 1993 (root at hrothgar) + + * RELEASE: released these changes as 0.48.patch1. Peter Macdonald + (pmacdona@sanjuan.uvic.ca) says he'll incorporate this into his SLS. + + * changes NUM_HANDLES for XMS to 64. took out handle_mask. + + * added the screen-buffer-and-compare code to restore_screen(). + this ought to really speed up the non-console users + +Wed Feb 17 15:40:54 1993 (root at hrothgar) + + * fixed up the Makefile to make libemu start at 1 GB... this is + really really important, as the dosemu0.48 distribution's MAX_XMS + of 3072 could overrun into libemu...I don't know why it works this + way, but it does (actually, I do know why, I just don't know why + sbrk() lets you move into a shared lib). Anyway, now xms.c checks + that you have space left for MAX_XMS (at compile time, will have + to be dynamic-ized to run time XMS size setting) + + * removed the check for console_keyb before calling + PollKeyboard...non-console users deserve a keyboard, too! + +Tue Feb 16 00:29:58 1993 (root at hrothgar) + + * cmos RTC now returns values in BCD if appropriate (usually is) + + * fixed the keypad number keys. forgot to put scancode in upper + byte. also fixed the F11 and F12 keys. + + * added man page, and the rest is history. + DOSEMU 0.48 DISTRIBUTION + +Mon Feb 15 00:24:50 1993 (root at hrothgar) + + * added all the ctrl and alt cursor keys. + + * added the files cmos.c and cmos.h. these will eventually + contain all the CMOS values needed and provide CMOS real-time + clock and alarm support. current code covers time/date, disk + setup, memory size, CMOS status. + +Sun Feb 14 16:50:28 1993 (root at hrothgar) + + * put the special_nowait call into read Extended keycode also. + I've really gotta verify this. + +Sat Feb 13 15:26:20 1993 (root at hrothgar) + + * uh, fixed the XMS EMB alloc. problem. I swear I checked for + this when I first had problems, but apparently not...I was + malloc()ing the actual K size, instead of the K size * 1024. + + * added some XMS functions, made the XMSControl entry point + conform to the spec (for hookability). + +Fri Feb 12 14:19:30 1993 (root at hrothgar) + + * put the 101-key keyboard flag in the BIOS + + * Added the timer_tick() routine. this fixes a LOAD of + stuff...4DOS beep lockup, 4HELP, Norton utilities, checkit, + autocnf, bunches and bunches of stuff, basically. This still + needs a LOT of work, like keeping track of round off error, higher + frequency, but it'll do for now. + +Thu Feb 11 01:53:27 1993 (root at hrothgar) + + * changed ReadString to accept almost any characters. I'm still + not sure about the ones below space. + + * char_out now treats \n as \n\r. I wonder about this. also, + ctrl-c checking is done a little more properly (look for dos_ctrlc()) + + * added the hold-alt-and-type keypad numbers entry method. still + need to add alt/shift/ctrl-cursor keys. + +Wed Feb 10 13:08:05 1993 (root at hrothgar) + + * WordPerfect works perfectly now :-). almost. + + * added Rshift-scrlock for int8, lshift-scrlock for int9. funny + thing is, these work better than the flags. makes me wonder about + the whole signal handler thing, and maybe I should check the IF. + + * added the -K flag. sorta makes QBASIC/EDIT work. if I use the + -t switch (timer int), then msdos will crash eventually with a GP + fault on 0xe8. + +Tue Feb 9 15:45:54 1993 (root at hrothgar) + + * XMS can now allocate the HMA and a single UMB at 0xc000. I + really need to make sure that 0xc000 isn't BIOS-mapped, but I'm + just playing now, anyway. This is fun! I can put dos=high,umb in + my config.sys and get 702K posible executable size! (with -m 720) + + * began the XMS stuff. Have a driver that can mark its presence + and claim no HMA. :-) + + * fixed the dir /w problem by allowing int 21h to do its own char + out. however, I found an anomaly in the get video combination + code. I can return 4 (color EGA), but not 8 (color VGA). If I + do, qedit freaks and puts the menu up off the screen as if the video + memory started at 0xb000:0000. The same thing happens if I change + BL in video subsystem. + +Mon Feb 8 02:05:07 1993 (root at hrothgar) + + * this (or herabouts) is RELEASE 0.47.7 + + * fixed the heavy-load miss characters thing. The keybuffer code + Stephen Tweedie gave me would advance the buffer even if ReadKeyboard + was called with wait=TEST. + +Fri Feb 5 01:51:08 1993 (root at hrothgar) + + * okay, fixed the lost characters thing for real: Matthias' int29h + handler (fast DOS tty output) was calling int 10h/ah=e. int 29h + output always goes to the current screen, and Matthias wasn't + taking that into account. + +Thu Feb 4 00:35:57 1993 (root at hrothgar) + + * i know why...I'm doing that damn WAITACTIVE ioctl(). damn damn + damn! I see no way around that. (later note: it might in fact + be the fact that I was often deleting libemu out from under + dosemu's feet with rm -f, and now that I've stopped that, + everything's better. Should I keep the ioctl queue?) + + * well, it seems that my ioctl queue hasn't stopped the kernel + panics. here's what I get (raw keyboard mode): + general protection: 0000 + EIP: 0008:00047325 + EFLAGS: 00010286 + fs: 0017 + base: 00000000, limit: C0000000 + Pid: 464, process nr: 16 + 64 88 08 4e 85 ed 74 13 8b 15 + + wait_queue is bad (eip = 00000201) + q = 0017b864 + *q = 00236f6c + tmp = 00236f6c + +(repeat the wait_queue block 7 times) + + Unable to handle kernel paging request at address c0000004 + Oops: 0000 + EIP: 0008:0004748B + EFLAGS: 00010093 + fs: 0017 + base: 00000000, limit: C0000000 + Pid: 31, process nr: 8 + 39 50 04 75 f8 8b 4c 24 1c 89 + + * well, pkzip 2.04e uses int10h/ah=e with a seemingly random + screen page. PC-DOS 4.01 seems to do the same thing. so, in my + infinite wisdom, if it writes to a screen page > 7, i make it + write to the current screen page. this is probably wrong, but it + works for pkzip 2.04e. + + * fixed the PollKeyboard() thing, I think. I use a "queue" for + ioctl()'s started from within an interrupt handler. this is + nasty, but it'll have to do. I can make this a lot pretier. + + * AAARRRRRGH! PollKeyboard() was crashing the emulator (kernel + panics and such) because I was using select()...and looky here, + pg. 278 of the Stevens book, select() is NOT listed as one of the + reentrant functions. I'm a fool. A FOOL, I tell you! + + * these changes I will call dosemu0.47.5 + + * also added choice of PHANTOMDIR and VIDEO_CARD to the Makefile. + VIDEO_CARD only has effect if it is set to MDA: the + PHYS_SCREEN_BASE will then be set to 0xb0000 instead of 0xb8000. + + * Now you can choose your RAW keyboard nationality in the Makefile + (like with a kernel compile). Gotta clean this up later with a + keymaps subdir, and all the keymaps therein. Also noticed that + when 4dos freezes up during a beep, ctrl-break (in RAW mode, of + course), fixes it :-). Added these keys to RAW (-k) mode: + ctrl-alt-pgup ... exit the emulator + ctrl-alt-pgdn ... "reboot" the emulator (not recommended) + ctrl-break ... as in DOS + ctrl-scrollock ... show first 0x30 interrupt vectors + alt-scrollock ... show registers + for some reason, keypad cursor keys (pgup/pgdn/delete) when + combined with the ctrl-alt keys will cause nasty things to happen. + Things like kernel panics, hard system reboots, etc. Is this my + fault, or Linux's? (I'm sure it's mine, but I can't figure out how) + +Wed Feb 3 12:07:16 1993 (root at hrothgar) + + * Added some elementary key-polling in sigalrm() with + PollKeyboard(). This works okay for now, but I really need to make + in general purpose (not just for console_keyb) and make the key + buffer use the BDA for real. Then we've got keyboard about done, + except for the multi-national keyboard maps. + + * Added Stephen Tweedie's small patches for insert-key into + keyboard buffer (InsKeyboard). This needs to be rewritten to use + the real BDA queue. + + * made the parse-debugflags kinda recursive, so a and 0 don't + terminate the parsing. This allows for handy option strings like + "-a+v" or "0+v" for debugging just video. + +Tue Feb 2 15:21:59 1993 (root at hrothgar) + + * note that DOSKEY works now! Nathan says it didn't before. Oh, + well. I thought it didn't. + + * cleaned up the debug-msg flags a bit. now the -D option + specifies a list of options. also cleaned up the int-revectoring + a tad. moved a little of the stuff into emu.h (a lot more needs + to go) + + * added the -f option to flip A: and B: floppies. I also have the + -B flag to boot from the b: drive, but I don't think DOS will ever + be happy with that. might take it out before release. + +Mon Feb 1 23:53:52 1993 (root at hrothgar) + + * NEED to find out what int 16h/ah=0x55 does. it seems to control + timeout for get_char (int 16h/ah=1), and is necessary for dosshell + and qbasic (which freeze occasionally otherwise). + + * added some wacky int-revectoring code. all but int 16h is broke + right now, and that is even special-case, too. + +Wed Jan 27 00:21:06 1993 (root at hrothgar) + + * fixed problem with CloseKeyboard() sometimes restoring flags + that had never been read. now, kbd_fd==-1 at startup, as it should. + + * fixed time problem - now DOS time is set properly at boot to be + in sync w/Linux's clock. After that, DOS can change it. + + * moved the port_[out,in] functions to emu.h + + * fixed a problem in get_leds() arising from the fact that &= is + not the same operator as &. silly me. now it works. + +Tue Jan 26 16:48:31 1993 (root at hrothgar) + + * well, had to put ioperm()s to disable the emulator's I/O + permissions before I re-enter the vm86() call. (of course, the + vm86 "task" inherits the emulator's ioperms). This means that if + I'm not careful, I may leave an I/O port open to DOS :-(. + + * Added two options: -N exits the emulator before booting DOS (but + after all the initialization), and -V allows VGA video. This + presently only includes the changing of the blink attribute bit to + the high intensity background bit, but it will one day also enable + VGA graphics. + +Mon Jan 25 22:19:20 1993 (root at hrothgar) + + * the linux.exe problem was found: linux.exe will fail if there + are more than 2 floppy drives defined. i defaulted the number of + floppies and hard drives to 2 each; this is changeable by the -H + and -F options. + +Thu Jan 21 21:10:06 1993 (root at hrothgar) + + * filled the highscan[] table for all chars <= 0x80 (ASCII). the + rest are alt-chars (from a terminal's point of view, META at least). + + * note that the below change required that I no longer open + /dev/tty, but use STDIN_FILENO and dup() that. whatever that implies. + + * fixed the console-video/keyb code. dosemu can now detect if you + are on a console, and refuse to run if not. it also retrieves the + console number for future use. (very system-dependent checking of the + major/minor numbers! this is dangerous! if dev_t changes from 16 + bits, this breaks.) console_video console switching should now be + completely safe, thanks to WAITACTIVE. currently, it blocks until + the dos console is current. I might want to change this so that + it just doesn't map it if the console isn't current--using + GETSTATE (and wait for later acquire_vt() calls to do so.)] + + * made the int13 disk-sensing functions more sensible. also made + the dp->tracks, dp->heads stuff orthogonal (for some reason, + matthias had made exceptions for floppys??). You can now + format/unformat a floppy, as well as a floppy disk image. added a + default CMOS type to the disk table struct. + +Sat Jan 16 10:28:23 1993 (root at hrothgar) + + * fixed alt-numkey bug in termio.c. for some reason, alt-numkey + scancodes get bumped up by 0x76. also fixed alt-- and alt-= + + * can't find bug that causes repeated extended keystrokes to kill + the emulator: to find it, run tv.com on some file, go the bottom, + and press & hold pgdn. bam, crash. + + * fixed int21h, ah=1,7,or 8 problem with extended keycodes; + (was supposed to return 0 first, then scancode on next call). diff --git a/ChangeLog.old b/ChangeLog.old new file mode 100644 index 0000000..42eb58d --- /dev/null +++ b/ChangeLog.old @@ -0,0 +1,5307 @@ +2004/03/07 ver 1.3.0 released + From Clarence + - etc/dosemu.conf, etc/global.conf, src/base/init/config.c, + src/base/init/lexer.l.in, src/base/init/parser.y.in, + src/env/video/X.c, src/include/emu.h: Add $_X_title_show_appname. + From Stas + - emu.c: Shut up annoying error. + - src/base/dev/pic/pic.c: + Limit the amount of error messages. + - src/dosext/dpmi/: dpmi.c, dpmi.h, memory.h, msdos.c: + * Added retf, iret and jmpf instructions handling in msdos.c. + * Use a more consistent register translation technique. + * First (yet mostly unsuccessfull) attempt on uncommitted memory + support: + - Virtual Support extension added + - DPMI functions 0x506 and 0x507 added + - All the necessary functions for memory requesting/mapping added + - Functions for changing memory attributes added. + Uncommited memory is being allocated with an anonymous mmap(), + and therefore can not be aliased (not from SHM pool). + "Uncommittness" is done via the PROT_NONE protection, which + guarantees a page fault on access. + But only a few programs are working properly with this + implementation right now. It definitely requires more work. + - emm.h, emu.h, dosemu.conf, global.conf, config.c, lexer.l.in, + parser.y.in, emm.h, emm.c, xms.c, dpmi.c, dpmi.h, memory.h, + msdos.c, msdos.h: + * First step to Blinker extender support + * The PM API translator now uses the EMS page frame to save 64K + of conventional memory. + * $_pm_dos_api option introduced which allows to disable the API + translator to preserve the possibility of disabling EMS and + getting extra UMB space without disabling also DPMI). + * First step at solving bug #902742 (most is_dos_selector() + checks removed, but not all). + An exec'd DPMI program is now "forked" as a separate DPMI + client. Memory structures were changed to allow sharing + allocations between parent and child clients. + - src/dosext/mfs/mfs.c: Dont use RPT_SYSCALL on readdir(3) + - src/: include/serial.h, arch/linux/async/signal.c, + base/dev/pic/pic.c, base/serial/ser_irq.c, + base/serial/ser_ports.c, dosext/dpmi/dpmi.c, emu-i386/do_vm86.c, + emu-i386/simx86/trees.c: + Next round of serial low-latency work. + * Remove serial_run() from everywhere, add it to the SIGALRM + handler. With the async notifications, that should be enough. + * Remove serial throttling hack: it doesn't play well with the + async IO. + * Make sure the ASYNC_LOW_LATENCY flag is set. + - src/: arch/linux/mapping/mapping.c, include/mapping.h: + Protect memory pages RW before releaseing to pool. This is to + avoid page-faults on a newly allocated memory. + - src/plugin/kbd_unicode/: keystate.h, serv_xlat.c: + Arrange scancode translation rules as an array rather than as a + linked list. + From Bart + - Remove mapself support + - Remove old keyboard code + - Remove coopthreads and comcom + - src/env/video/X.c: Added dirty_all_vga_colors(); + related calls to + resize_text_mapper(). This fixes the black screen problems with + bitmap fonts. + - Makefile.conf.in, mouse.c, mouseint.c, X.c, X.h, mouse.h, + env_term.h, keyb_slang.c, mouse_gpm.c, mouse_xterm.c: + Mouse cleanup: use a mouse client structure similar to the one + used for the keyboard. +2004/03/06 merged with ver 1.2.1 release + From Pete Cervasio + - allow int 1A function 3 to set the emulated CMOS time of the day + clock. + From Clarence + - Now, you can run your favourite DOS apps like this: + xdosemu "/home/clarence/games/commander keen/keen1.exe" + And it will automatically: + 1. mount / if the specified program is not available from an + already-redirected drive + 2. "cd" to the correct directory + 3. and execute the program automagically, all without _any_ + typing in DOS + (if "unix -e" or "unix -c" is put at the end of your autoexec.bat) + "ported" over some more builtins i.e. I just cut and paste them + (except for com_dossetcurrentdir()). + cosmetic change to GetRedirectionRoot() in mfs.c + - DAC fixes for vgaemu: + * accesses to port 0x3C9 should not change the DAC state + (as reported by a read to 0x3C7) + * DOSEMU's 0x3C7 reads returned inverted bits! + * If you write to 0x3C9 after writing to 0x3C7, the colour that + actually gets set == + 1 (fixes + Starcon2) + From Stas + - NULL pointer fixes: + * Don't call keyb_server_reset() before keyboard client is + initialized, as this causes a NULL ptr deref in + keyb_client_set_leds() with the potential of calling realmode + code + * Avoid NULL ptr deref when SDA is not yet initialized (at DOS + boot-up). + - Certain mouse types (namely, MOUSE_GPM) must never be + closed/re-opened. This makes the gpm mouse to survive the + console switch, and quite accidentally also solves the obscure + lock-ups of Volkov Commander after a console switch. + - Fix sound for the game Screamer + - Improved bpint dosdebug command. Works under DPMI now. + - Clear AC in quite a few places to avoid recursion into an + exception 0x11 handler. + - Disallow exception 0x11. The reason is that AM bit of CR0 is + supposed to be clear under DOS, but it is not under Linux. + - Reduce memory consumption for builtins from 75K to 3K. + From Bart + - Allow fullscreen toggling using xmode. + - Make "unix -e" a hybrid of the old + "unix -e" and "unix -c": if the Linux path cannot be + canonicalized it will be interpreted as a DOS command. This way + "dosemu -E dir" continues to work. Introduced "unix -r" to get + the old behaviour of -e for the purists. + - Count number of times middle button is pressed even for 2 button + mice (will be zero). Fixes bug #891650 + - dpmi.c: dpmi.c, do_int31, case 0x0401: added a missing "break" + causing this function to return rubbish as vendor name + (suggested by japheth at users.sourceforge.net) + - Block SIGALRM instead of turning of the itimer for the MFS. Also + check if one is pending before calling handle_signals(). Without + this change situations that spend lots of time in the MFS + effectively could have SIGALRM disabled (showed up on SMP + machines in a dir loop). + - int.c: Execute dos_post_boot() (mouse_post_boot()) not only from + int28 but also from int21/ah=4b. Fixes problem with wd from + unix -e & mouse. + - Repair the automatic exit for "unix -e" without -E argument. + - Makefiles: Remove all SUBDIR=... defs. They are not necessary + anymore and may produce the wrong .a file if you invoke make in + the subdir. +2004/02/10 merged with ver 1.2.0.1 unofficial pre-release + From Stas + - Add support for MIDI input. + - Fixed a couple of bugs in MPU401 code. + - Alert if handle_signals() is re-entered (happens if, for example, + RPT_SYSCALL is enabled in emu.h). + - Remove glibc calls from SIGALRM and SIGIO handlers. + - Remove (now unnecessary, since we know the reasons) deadlock + "protection". + - dosdebug: + * Parse multiple commands properly + * Stop trace loop in mhp_intercept() + - Detection for the %esp CPU bug (see EMUFailure.txt). Recovery + part is disabled for now. + - Final (hopefully) pass on DPMI 1.0 compliant multiple client + support. Now it works for something more than just building + FreeDOS with bcc and nasm32. + * System descriptor aliases are now per client and are either + inherited or or recreated, depending on whether the IDT is + inherited or not. + * extender-specific structures are now per-client too. + - DPMI: Avoid execution on .data + From Bart + - Add support for the GPM mouse on the console (with a little + help from mc) and avoid conflicts with a serial mouse by moving + internal mouse init before serial init. + - mkbindist now creates the pcf fonts on the fly -- that will + avoid problems with fonts in the binary tarball. + - Add support for $_X_fullscreen and corresponding switch -w. + - Add support for $_mouse_internal and corresponding switch -m to + be able to switch off the DOSEMU internal mouse driver. + - Move the "BUG: dosemu touched protected video mem" recover + logic earlier in sigsegv.c and vgaemu.c (with Stas) + - Mark critical global variables used in signal handlers as + volatile to avoid gcc over-optimizations (hint from Stas) + - Fix bug #866618 rows ower 80 char: a DOS text-mode program + programmed the VGA controller to (logically) have more than + 80 columns per line but vgaemu didn't pick it up. + - Fixed page_size calculation: solved display problems with Duke + Nukem 1 +2004/01/18 merged with ver 1.2.0 release + From Stas + - updates to TUN/TAP README about bridging + From Bart + - mknewyear and adapt various other dates + - miscellenous doc adjustments + (README.txt, Novell-HOWTO with help from Stas and Peter Eckhard), + announce with some help from Claudia Neumann, INSTALL, HOWTO) + - Added $_xterm_title to dosemu.conf to configure or disable + the xterm (or putty or ...) window title with running DOS + program. + - Friendlier message if no DOS is found. + - Fix shift-3 (paragraph sign) on the German keyboard and add + "last resort" keyboard translation from Unicode to < 0x20 values. +2004/01/14 merged with ver 1.2.0rc2 stable pre-release (aka 1.1.99.2) + From Stas: + - src/base/serial/: ser_defs.h, ser_init.c, ser_irq.c, ser_ports.c: + * Switched serial to async IO + * Use tcdrain() for non-FIFO data sends to minimize latencies + * Some other small changes to reduce latencies. This allows to use + dosemu's serial port emulation for time-sensitive tasks. + - src/: dosext/sound/sound.c, include/sound.h: + * Make SB to really produce the interrupts for MPU-401, not only + to pretend to + * Added some support for undocumented MPU-401 commands 0xAC and + 0xAD (stolen from VDMSound) + - src/arch/linux/dosext/sound/midid/timid.c: + Really pass the sysexes to timidity, not only pretend to. + - src/arch/linux/dosext/sound/linux_sound.c: + Fix the OSS initialisation for SB direct DAC writes. + Dont overflow the log with the "buffer overflow" messages. + - src/: base/mouse/mouse.c, include/mouse.h, emu.c, + Switch the mouse driver over to async IO, fixes bug #852077 + - doc/EMUfailure.txt, src/doc/HOWTO/EMUfailure.sgml: + updated to tell more about fundamental CPU problems + - src/: arch/linux/async/signal.c, base/dev/pic/pic.c, + env/video/miscemu.c: solve problem with miscemu.c wrongly + setting pic_sys_time + some debug print improvements. + - src/base/speaker/speaker.c: Allow disabling speaker + - configure, configure.ac: Use have_x instead of no_x + From Bart: + - etc/dosemu.users.example: Comment everything out. + - Makefile, README.bindist, dosemu.spec.in, dist/mkbindist, + etc/global.conf, src/arch/linux/Makefile.main: + * use a safer rm -rf ../tmp (fixes #851514 ; make install + executes in src) + * copy global.conf and dosemu.users to sysconfdir without example + extensions + * remove config.status call from the Makefile. It messes + "prefix" up. + * obsolete /etc/dosemu.conf (etc) unless sysconfdir=/etc; + make install and the rpm install moves them now + * use %config(noreplace) and add missing commands symlink to the rpm. + - etc/: dosemu.conf, global.conf: Allow a completely free-from + printer command (but for LPT1 only for now) + - etc/global.conf: Fix problems with -F for global.conf + - src/arch/linux/mapping/mapshm.c: Added mremap(.,0,..) check for + the 2.6.1 kernel: we can only use mapfile and not mapshm + - src/: arch/linux/async/signal.c, arch/linux/async/sigsegv.c, + include/cpu.h: Restore fs and gs in all signal handlers. Fixes + NPTL problems. + - src/arch/linux/async/sigsegv.c: Don't always quit if DOSEMU + itself touches protected video memory. Adjusting protection (as + opposed to emulating) should be harmless -- perhaps a better way + to solve this is to adjust all the memmoves, but on the other + hand letting the CPU do the dirty work isn't all that bad of a + concept. Fixes bug #817088 + - src/env/video/vgaemu.c: Allow vgaemu_set_text_mode for graphics + modes too. "orbits" needs this. + - src/doc/tools/doSgmlTools.pl: Improve openjade detection + - default-configure: Also use disable-aspi from default-configure. + - configure, configure.ac, dosemu.spec.in, dist/dosemu, + doc/dosemu-HOWTO.txt, src/arch/linux/Makefile.main, + src/base/init/config.c, src/doc/HOWTO/dosemu-HOWTO.sgml: + * Rename enable-aspi to disable-aspi (Clarence suggestion) + * Added --with-slangdir=src/plugin/slang possibility to force + use of the slang plugin without funny workarounds. + * Clean up the rpm spec file in line with the above. + * Set SUDO_UID/GID in the dosemu script to 0 to + allow "sudo dosemu" with full root (unlike dosemu -s) + * Fix secure_option_preparse for sudo runs (it was not secure). + - README.bindist, dist/dosemu, etc/global.conf, + man/dosemu.bin.1.in, src/base/init/config.c, + src/base/init/parser.y.in: Add -n option to be able to completely + bypass dosemu.conf and dosemu.users (unless suid). The $HOME + tarball installation now uses this option by default. + - dist/dosemu: Added some desperate attempts to really convince X + to find the font. + - src/: base/mouse/mouse.c, env/video/X.c, include/mouse.h: + Fix mouse init problem. + - README.bindist, etc/dosemu.conf, etc/global.conf: Add + $_printer_command to be able to solve the CUPS problem without + global.conf. + - src/plugin/commands/builtins.c: Use a more helpful message (of + course this is not always applicable but right now the most + likely cause). + - doc/NOVELL-HOWTO.txt: Correction about PDIPX/IPXPD. + - doc/announce: Add a "what's new" section to the announce file. + - src/plugin/term/keyb_slang.c: Update the special keys display at + strategic places (where it was forgotten before); disable ESC-ESC + (conflicts with alt-f1). + - dist/dosemu, src/arch/linux/Makefile.main: Use sed+ls instead of + readlink for non-system-wide; use dosemu.bin in the bindir + instead of the first one in $PATH for system wide. + - src/doc/README/config: Added note that mapself doesn't work on + Linux kernel 2.2.24 and higher. + - src/: dosext/dpmi/dpmi.h, emu-i386/cpu.c, include/cpu.h: Fix DPMI + problems with 2.6.0test6 by no longer hard coding UCODESEL and + UDATASEL -- they changed from 0x23/0x2b to 0x73/0x7b. + - etc/global.conf: Change to stop DOSEMU complaining if the libdir + doesn't exist. +2003/09/28 merged with ver 1.2.0rc1 stable pre-release (aka 1.1.99.1) + From Stas: + - src/: base/async/int.c, base/misc/disks.c, dosext/dpmi/dpmi.c, + dosext/mfs/lfn.c, dosext/mfs/mangle.c, dosext/mfs/mfs.c, + dosext/mfs/mfs.h, emu-i386/do_vm86.c, env/video/X.h, + include/dos2linux.h, include/int.h: xtitle support + (Bart: extended support to xterms) + - dist/dosemu, src/arch/linux/async/sigsegv.c, signal.c, + src/dosext/dpmi/dpmi.c, dpmi.h: Upon signal handler entry: + Restore the eflags instead of explicitly clearing AC and ID. + Also restore the %fs and %gs registers for compatibility with + NPTL. Remove old LD_ASSUME_KERNEL workaround (Bart) + - src/: dosext/dpmi/dpmi.c, include/pic.h: + * Fixed some log output, adjusted some types. + - src/dosext/dpmi/: dpmi.c, dpmi.h, msdos.c, memory.c: + * Per-client IDT, as required by DPMI 1.0 spec. IDT is inherited + from previous client if possible, to retain the compatibility + with DPMI 0.9. Also added pm_block_root and realModeCallBack + to the per-client structure. + * Started consolidation of the per-client data into a single + structure for the true multi-client support and better + compatibility with DPMI 1.0. This patch consolidates + DPMIclient_is_32 and dpmi_stack_frame, adding other units is in + to-do. This patch allows building a FreeDOS kernel under a + 16-bit BC-3.1 environment, while using a 32-bit nasm. + * Check ss:sp=0 as per specs, sp=0 is not enough. + * Recovery code for Windows which puts 0 into %ss on the exception + stack frame. Escaping via a locked stack seems to help. + * Improve DPMI debugging: + - Properly debug the emulated instructions + - force_early hack is no longer necessary + - More debugging messages + * Limits >1MB are page-granular. + * ConvertSegmentToDescriptor() must look up segments also with the + limit==0xff as this is the limit of PSP segments created at DPMI + initialization. + * Optimisation: READ_DS_COPIED can be #define instead of variable + - src/base/serial/ser_init.c: Make sure the serial device is a tty. + - src/base/: init/init.c, mouse/mouse.c: + Init the mouse IRQ after the mouse client initialisation. + Remove duplicate/unused includes. + - src/: emu.c, base/init/init.c: Call hardware_setup() before + device_init(). This fixes a numlock problem reported by Reinhard. + - src/: arch/linux/async/sigsegv.c, include/emu.h, emu.c: + Dont intercept leavedos() if it was called from within a signal + context. TODO: Calling leavedos() from within a signal context + should be deprecated and replaced with some more elegant solution + that can work with dosdebug. + - src/arch/linux/debugger/Makefile, dosdebug.c: + Dont use -O2 for dosdebug, CFLAGS should be enough. + Exit dosdebug if dosemu unexpectedly terminated. + From Bart: + - src/: emu-i386/cputime.c, base/async/int.c, base/bios/int10.c, + base/misc/userhook.c, base/misc/utilities.c, base/mouse/mouse.c, + env/video/X.c, env/video/X.h, include/video.h, + plugin/commands/comcom.c, plugin/term/env_term.h, + plugin/term/keyb_slang.c, plugin/term/terminal.c: + Localized title support using a new Video function pointer. + - configure, configure.ac: Fix PATCHLEVEL typo in configure.ac; + remove help for enable-mitshm (disable-mitshm is enough) + - src/arch/linux/debugger/mhpdbg.c: (with Stas) + Fix -H1 (stop until dosdebug has connected) + - src/base/async/int.c: Let int 11 and int 12 read their values + from 40:xxx like real BIOSes do. + - README.*, INSTALL, doc/, src/doc/: Documentation updates, + regenerated DANG, add note about bison and yacc; remove note + about static linking (no proofs seen). + - src/doc/HOWTO/dosemu-HOWTO.sgml: + Update the HOWTO -- in line with present DOSEMU, and delete + obsolete sections, and merge formatting changes from Peter + Jay Salzman + - src/: arch/linux/async/signal.c, plugin/term/keyb_slang.c: + Based on a patch from Mrt Laak (martlaak@users.sourceforge.net) + Turn off shiftstates for terminals later. + Bart: do this from the sigalrm handler instead of for the next + key. Also add some more escape sequences for xterm and putty. + Add shift-tab to the xterm keys. + - src/: include/mouse.h, base/mouse/mouse.c, base/mouse/mouseint.c, + plugin/kbd_unicode/include/keyboard.h, plugin/term/keyb_slang.c: + Originally by Mart Laak: xterm mouse support. Works in putty too. + Changed by Bart to run automatically if TERM=xterm* and use the + 1002/1003 modes so that not just button clicks but also drags can be + detected. + - src/: base/mouse/mouse.c, plugin/term/mouse_xterm.c: Delay xterm + mouse init until the first int33 call, so that normal copy/paste + works without shift until the first DOS app wants to use a mouse. + Could be much cleaner -- leaving that for 1.3 + - src/plugin/kbd_unicode/serv_xlat.c: Be more conservative in using + approximations for the unicode keyboard. + - src/plugin/kbd_unicode/keymaps.c: Use Unicode for the Turkish + keyboard! + - src/plugin/term/keyb_slang.c: Remove unnecessary (and wrong on + some keyboards) translations of * and + + - src/arch/linux/Makefile.main: We no longer need to create + /var/lib/dosemu; also adjust a few symbolic links. + - etc/global.conf: Wrap each "foreach" in if/endif blocks; + otherwise the lexer may have problems and may not recognize + $_vbootfloppy. I wasn't able to fix the lexer. + - dist/dosemu: Remove automatic "enter" since FreeDOS supports + switches=/f now + - Makefile, configure, configure.ac, dosemu.spec.in, man/Makefile, + man/dosemu.1, man/dosemu.1.in, man/dosemu.bin.1, + man/dosemu.bin.1.in, man/ru/dosemu.1, man/ru/dosemu.1.in, + man/ru/dosemu.bin.1, man/ru/dosemu.bin.1.in: + Automatically put the right paths into the man pages. + Add dosemu.spec.in for creating an rpm. + - etc/dosemu.conf, etc/global.conf, src/base/init/Makefile: New + clear layout of dosemu.conf. Document the fact that /etc/dosemu + may be used as hdimage base. + - src/base/misc/priv.c: Correct suid-root privilege problem wrt + "mapself". + - src/: env/video/X.c, plugin/kbd_unicode/keyb_clients.c: Fix + copy/paste problems (delay from DOSEMU->xterm and LF->CR + translation for smth->DOSEMU) +2003/08/22 ver 1.1.5.7 unofficial pre-release + From Nerijus Baliunas (nerijus@users.sf.net): + * doc/README-tech.txt, doc/README.txt, etc/dosemu.conf, + src/doc/README/config, src/doc/README/config-tech, + src/plugin/extra_charsets/cp773.c: + This patch adds cp773 (called KBL in Lithuania) + support. Although it is not an official standard, but is + used more than any other DOS cp in Lithuania. + From Clarence: + * emu.c: don't print "funny" characters on the screen when speaker + == NATIVE and video_config_init() aborts (due to e.g. an attempt + to run with -X without $DISPLAY). + leavedos() tries to turn off the speaker before the port server + has been initialised (as extra_port_init() is not called before + device_init()). + * ports.c: check that the portserver is actually up before writing + anything to the fd's. This should fix the "printing random + garbage" bugs once and for all :) + portserver_pid should be set to 0 in port_exit() so that if + there are any accidental port accesses after port_exit(), error() + will remind us to fix them. Also, the "if (port_fd_out[1])" + check is not necessary anymore since portserver_pid is already + tested for. + * src/base/bios/int10.c: (and Bart) (1) int10 should + not affect the carry flag at all (2) if int10/ah=3 is given a page + past the last page, CX should still be set to the cursor and DX + should be set to 0. + From Stas: + * src/dosext/dpmi/: dpmi.c, dpmi.h: + Dont skip the extended access type byte for 16bit clients. + This patch allows Windows (WinOS2) to work... + problem: timer driver (advapi.386) fails to initialize. + Therefore no movie playing :( Might be a problem with a hardware + emulation + Some cleanups and optimisations. + Export selector checking functions. + Allow 4G limits when converting segment to descriptor. (another + part of bug #756570) + Use mhp_intercept() to debug CPU exceptions in DPMI. + * src/dosext/dpmi/msdos.c: + - msdos_fault: Dont try to fix the selector if the program abuses a + perfectly valid descriptor - this is not fixable on dosemu side and + the attempt to only screws up the things even more making it + unfixable also for the program itself. + - msdos_fault: Do all the fixing on a temporary context struct + and apply the fix to the real context only when we are sure + the fix is valid. + - msdos_fault: Allow using any realmode segments as a selectors, + not only the special ones (part of bug #756570) + - Some cleanup + * src/: arch/linux/debugger/mhpdbg.c, dosext/dpmi/dpmi.c, + include/mhpdbg.h: + Introduce mhp_intercept_log() function that allows to redirect the + log messages to a dosdebug terminal. + * src/base/serial/ser_init.c: + If opening serial port fails, remove the lock and dont try to open + it again. + * src/arch/linux/debugger/: dosdebug.c, mhpdbg.c: + - Resume execution if dosdebug detached while in a stopped state. + - Fix dosdebug detaching to make it possible to attach dosdebug + again. + * src/arch/linux/debugger/mhpdbg.c: + Return to dosemu context before starting a dosdebug polling cycle. + * src/: env/video/X.c, plugin/kbd_unicode/keymaps.c: + Fix some annoying error messages. + * src/: arch/linux/debugger/mhpdbg.c, include/mhpdbg.h: + Introduce mhp_intercept(). When called, the control is transferred + to dosdebug if it was attached. + Kill 2 instances of allocating 64K on stack - this is too much and + causes a memory corruptions for me (probably a gcc problem) + From Bart: + * src/env/video/attremu.c: Solve problem with attribute emulator + introduced in 1.1.5.4 + * src/dosext/mfs/: lfn.c, mangle.c, mangle.h, mfs.c, mfs.h: + - implement LFN wildcard delete (int21/ax=7141/si=1) + - check for invalid characters in LFN truename (int21/ax=7160) + - no longer trust stat for vfat; there are some problems with + question marks and characters such as (\'e) + - use underscores instead of ? for unconvertible unicode. + Translate all ?'s to _'s for unix->DOS filename translation. + * src/dosext/mfs/: lfn.c, mfs.c, mfs.h; + src/plugin/translate/include/translate.h: + MFS cleanups, including: + - fix exists() and wildcard delete. + - avoid use of some global variables and pass the drive number + around as a parameter instead. + Make findfirst on a single file more efficient again and + get it to honour devices again. + * src/: dosext/mfs/lfn.c, dosext/mfs/mangle.c, dosext/mfs/mangle.h, + dosext/mfs/mfs.c, dosext/mfs/mfs.h, dosext/mfs/util.c, + plugin/translate/translate_config.c, + plugin/translate/config/plugin_parser, + plugin/translate/include/translate.h: + Add support (using the translate plugin) to the MFS to translate + between the external and internal charset for filenames. + Set $_external_charset (for all purposes except the display) + to the current locale charset by default. + * src/plugin/translate/keysym_approximations.c: Complete commented + out approximations; this is necessary for upcasing filenames + where an equivalent capital letter does not exist in the codepage. + * src/arch/linux/Makefile.main: Fix problem with keymap/[a-z]* + + CVS directories + * configure, configure.ac, src/arch/linux/Makefile.main: Create + src/lib upon make and remove it with "make distclean". + Uncomment detection of INSTALL and AWK. + * Makefile, Makefile.conf.in: Remove any autom4te*.cache varieties + upon distclean. Set BINPATH at the right time (after THISVERSION). + * Makefile, Makefile.conf.in, configure, configure.ac, + src/{Makefile.common,arch/linux/Makefile.main,commands/Makefile}: + Use simply expanded variables in several makefiles and avoid + $(shell) (speeds up make, esp. when most is already built) + * compiletime-settings, compiletime-settings.devel, + compiletime-settings.help, configure, configure.ac, + default-configure, setup/compiletime_setup.menu, + setup/compiletime_setup.sh, setup/compiletime_setup.tk: Configure + cleanups (with Stas and Clarence) + - use $enable_xxx environment variables + - use --disable-x instead of --enable-nox + - use --with-x for libraries + - remove "dodebug" +2003/07/20 ver 1.1.5.6 unofficial pre-release + From Bart + * src/plugin/: kbd_unicode/keymaps.c, keyboard/keymaps.c: Hungarian + keyboard adjustments (anonymous contribution, see bug 687850) + * src/dosext/mfs/: lfn.c, mfs.c, mfs.h: Fix LFN's for VFAT. Make + use of the VFAT_IOCTL_READDIR_BOTH ioctl to obtain the short + aliases for long filenames (Wine provided a good example). + Remove unnecessary scan_dir usage on VFAT (because stat is + already case-insensitive) + * src/dosext/mfs/mfs.c: Fix problem with dir searches that should + *not* find a volume label (such as the attribute 0x3f) + * src/dosext/mfs/lfn.c: Fix problem with LFN "truename" + (int21/ax=0x7160/cl=1), case insensitive open's and mkdir. + * src/base/bios/int10.c: Fix compilation without X development + libraries. + From Stas: + * configure, configure.ac: small configure adjustments. + * src/dosext/sound/sound.c: Use only the current DMA channel on + SB16. This allows the QV to work. QV is an MPEG4/DivX player for + DOS! + * src/base/mouse/mouse.c: Enable mouse at startup in case the + program doesn't do that manually. + * src/base/async/int.c: hogthreshold adjustment for the mouse code. + * src/plugin/kbd_unicode/: Makefile, getfd.c, getfd.h, keymaps.c: + Update the getfd() console detection code with the one from kbd 1.08 +2003/07/15 ver 1.1.5.5 unofficial pre-release + From Bart + - Add support for the Long File Name (int21/ah=71) interface + on redirected drives. For now it's off by default because + it hasn't received much testing yet -- see $_lfn_support. + From Stas + - Fix DPMI to be able to revector interrupts that are called by + DPMI clients (such as the above int21/ah=71). +2003/07/15 ver 1.1.5.4 unofficial pre-release + From Eric Auer, Witold Filpczyk and Bart + - use bitmap fonts by default, rather than X fonts. + - Support 80x100 CGA "text graphics" with 8x2 font. + - allow resizing of the text window. + - VGA attribute emulator cleanups. + - Make the 8x16 font in vgafonts.c equal to the one in vga.pcf. + From Bart + - miscemu.c: + The retrace time is measured in microsecs, not ticks. +2003/07/15 ver 1.1.5.3 unofficial pre-release + From Clarence + - minor config tweaks $_X_backgroundfreeze -> $_X_background_pause, + - set $_joy_latency to (1) by default. + From Stas + - add config options for OSS sound + - disable auto-init DMA when speaker disabled + From Stas and Bart + - implement a lightweight coopthreads replacement and port all the + builtins to it. + From Bart + - Small MFS optimization. + - Some small documentation updates. + - Kill the (bit-rotten) scroll queue. + - Fix non-active setbuf(stdout, NULL) and $_console=(1) without + graphics. + - Use putchar instead of write since the buffered stdio problems + were solved. + - Avoid some dependencies on bash (export foo=bar, echo -e in + configure) + - Kill $_timint, and config.graphics. They were no longer really + used anywhere. + - Fix a problem with "-V" not implying "-c". + - Fix "make install" if pesky CVS directories are present. + - cheap workaround for NPTL: use export LD_ASSUME_KERNEL=2.2.5 in + the dosemu script. + - Support the VGA line compare register: this fixes one of the two + reported problems for Jazz Jackrabbit + - Fix colour problem in chain4 modes (second jazz jackrabbit issue) + - Fix bug 753968: xdos handles mouse events without having a focus + -- don't warp the mouse if the mouse left the window. + - Small makefile fix to help partial builds. + - Misc small configure and Makefile fixes. + - Solve potential gcc problem with a wrong asm constraint: it + needs to be "q" for registers with byte parts, not "r". + This prevents gcc from using "%sil" as low part of %esi%. +2003/06/14 ver 1.1.5.2 unofficial pre-release + From Bart + - enable building in a separate object directory + - enable small test sub-builds (e.g. "make" in src/env/video) + - use relative paths if possible (so you can rename your + build directory on the fly) + - fix lexer.l.in to work with newer versions of flex. We can + no longer fake flex by setting YY_NEVER_INTERACTIVE, + but a workaround using "/dev/null" files was possible. + - fix buglet in doscoopthreads com_dosrenamefile + - remove obsolete file kversion.sh +2003/06/12 ver 1.1.5.1 unofficial pre-release + From Ben Davis + - Implemented SB16 support. + - Fixed sevaral typos in the dcos :) + From Stas + - Leak-free DOS memory management for DPMI. + Additionally the "Allocate DOS memory" routine + was fixed to match the DPMI specs. + - fix SB mixer rates + - pic fix to get SC2000 running with SB16 + - use exit()/config.exitearly=1 instead of leavedos() during init + - don't leavedos (but let the BIOS set CF) if there is no disk + - FCB fix for coopthreads + From Manfred Scherer + - use a per-PSP heap to watch for hlist overflow (and then + delete the oldest list) in the MFS. + From Bart + - clean up MFS a little more; use arrays instead of linked lists + for the hlists; this allows us to avoid duplicate hlists much + more often than before, since the hlists can be walked though + independently with random access. + - fix a problem where the MFS chdir did not remove the trailing + backslash + - use sigaltstack(2) instead of sa_restorer if it is available + - fix memory leaks in the parser and lexer (found by valgrind) + - fix more typos +2003/06/07 ver 1.1.5 released + From Bart: + - documented TUN/TAP support and the port server. + - adjusted man pages + - set int 23 to iret at the right place in dpmi.c + - unmap the first 1 MB of (DOS) memory in the port server. +2003/06/04 ver 1.1.4.16 unofficial pre-release + From Stas: + - midid update + * Compatibility with the latest TiMidity++ (daily + snapshot) + * Moved forgotten bits from midid.h to device.h + * Improved start/stop sequence + * Re-including protection to all headers + - fix the DPMI descriptor leaks in: + 1. dpmi_init() + - fix the environment translation for DPMI (bug #731906) + - sleep in the dosdebug poll cycle + - allow to configure the TAP device before starting DOSEMU + - fix MFS bug for wildcard delete() (bug #732975) + - MFS sigalrm cleanup + - add hint to emumouse about the internal mouse driver config + - adjust some defaults in dosemu.conf + - vga.c: remove redundant ioperm + - linux_sound.c: dont ioctl POST by default + - ioctl.c: init the callback array + - serial: Use TCADRAIN instead of TCSANOW where appropriate + From Oleg Kuimov + - wrong year in rtc emulation + fix last two digits of the CMOS year that is emulated in rtc.c + from tm->tm_year (which is possibly >= 100) + to tm->tm_year%100 + From Grigory Batalov + - ignore a busy disk (don't abort DOSEMU) if it is a floppy. + - corrected and added font descriptions in dosemu.alias + From Ben Davis + - Sound fixes in preparation for SB16 support. + From Clarence + - allow freezing of xdosemu in the background through dosemu.conf. + From Andy Shevchenko + - fix some problems with symbolic links and DESTDIR in + Makefile.main + From Bart + - fix fullscreen X toggling when already in graphics mode + - misc doc fixes (added INSTALL, README, stripped down QuickStart) + (inspired by Ged Haywood's comments on linux-msdos) + - removed some old cruft (keyboard.c.diff, DPR, gnats) + - streamline "make" a little (removed the wait) + - dosemu -install now asks for a boot directory location so + it is easier to set up a different boot directory. + - fix the dosnet init to be before the privilege drop + - fix ugetcwd (it is a heap-eating builtin) + - fix null pointer dereference in remap.c + - replace the DJGPP hack by something a little less intrusive + (an iret handler for int23 when dpmi is active) + - int code cleanup: int routines now return 1 if they want + to return, and 0 if they want to chain through to a real mode + interrupt. The somewhat confusing default_interrupt() function + is eliminated. + - add --enable-debug as alias for --enable-dodebug as configure + switch + - fix gcc 3.3 builds (ELF detection was broken) and the new + warnings it gives. +2003/03/15 ver 1.1.4.15 unofficial pre-release + From Stas + - fix race in do_irq(): see if the pic_ilevel is in sync + with pic_isr and it appears that it is not. + - cputime.c: correctly take idle periods into account for TSC. + - Fix serial delayed-open. + - Fix keyboard delay problem. + - Don't read the mouse device in X. + - (with Herbert Xu) enable use of a pseudo-tty as a serial device. + - Rewrite bios part of the packet driver in C + - Fix do_call_back() and get it to work for DPMI + - VIP related: sigsegv.c: solve kryptegg lockups under X + - Remove the ST3 hack since it doesn't help anymore. + - Use async I/O: async I/O speeds up the I/O dramatically. + Performance of the packet driver is doubled. + This also removes the need for the plugin_ioselect hook since + callbacks can be used instead now. + - Fix SLang keyboard 8-bit handling (Russian capitals in xterm) + From Grigory Batalov (with some help from Andy Shevchenko) + - add Ukranian codepage support (cp1125.c, koi8-u.c, koi8-ru.c) + - add codepage 1251 (cp1251.c) + - adjustments to cp866.c and the Cyrillic BDF files. + From Bart + - (initially suggested by Stas) replace %Ld by the more standard + %lld. + - enable disabling of the X vidmode extension at compiletime + - warn more prominently about a buggy system SLang library. +2003/03/15 ver 1.1.4.14 unofficial pre-release + From Witold + - fix configure.ac for people without X support (changed a + little by Bart) + From Stas + - VIP fixes + - sound code: make sure dsp is closed before detecting SB + From Clarence + - fix compilation problem if one disables CONFIG_X_SPEAKER + - allow ctrl-alt-p to be used to freeze DOSEMU + - always update real window's title instead of fullscreen one + - reverse X title order + - append "[Paused - ]" to the X title if DOSEMU is + paused using a ctrl-alt-key combination + From Bart + - guard includes of env/video/X.h if X_SUPPORT isn't there + - fix "make -jn" compilations + - remove -Wpointer-arith from warnings -- some system headers + don't like it. + - configure.ac cleanups + - fix compilation of the dosnet kernel module + - dosemu script fixes: do not rely on the existence of + ~/.dosemurc anymore. + - (adjusted from Andy Shevchenko's patch) make install cleanups + - (with Stas) more cpuemu stack protection and time system + counter fixes to get cpuemu for vm86 working again. + - fix long long cpu speed multiplication in config.c to fix + a problem with > 2GHz CPUs. + - fix /proc/meminfo parsing for 2.5 kernels: the "MemShared" + part went away. + - XMS "query free memory" needs to set _BL to 0 (success). +2003/02/10 ver 1.1.4.13 unofficial pre-release + From Stas + - fatfs: ignore 0 sized io.sys and ibmbio.com files. + - add some ethernet frame debugging + From Bart + - fix mkpluginhooks to create config directories for plugins + if they don't exist. + - fix the ASCII-7 speaker bell beep +2003/02/09 ver 1.1.4.12 unofficial pre-release + From Stas + - $_pic_watchdog related PIC code adjustments. + From Fran Sabolich + - implement break handling in ser_ports.c (cleaned up by Stas) + From Bart + - Enable/disable all plugins via compiletime-settings. + - By default, only use the supplied S-Lang library if the system + supplied S-Lang library does not have the UTF-8 hack. + - Simplify S-Lang configuration by using DOSEMU's config.h + - Add 64-bit file locking support. + - Add $_full_file_locks option to lock whole files instead of just + one (for DOS invisible) byte. + - Set $_pic_watchdog default to 50 (suggested by Stas). + - Set $_hdimage default to "drives/*". + - Use ~/.dosemu for the $_hdimage basedir, falling back to + /var/lib/dosemu if necessary. + - Use proper long X names for the VGA fonts. + - Add ability to use a DOS command as a DOSEMU option (without + -E). In that case "exitemu" happens automatically when the DOS + command terminates. + - (With help from Reinhard) only apply cleanup_child to the port + server and not to any other children. + - clean up dosdebug; use dynamic memory allocation, and remove the + need for the ~/.dosemu/run/dosemu. files. + - temp file cleanup: the "mapfile" mapping driver and printer now + use tmpfile(3) to create the temporary file. That way the file + is automatically deleted even if DOSEMU crashes, and noone else + can open the file. Because of this, the printer code now uses a + pipe to communicate with lpr. + - DOSEMU is now quiet by default. It now doesn't say it's running + in low feature mode, but instead tells you if it's running with + root privileges. + - (suggested by Stas) Use normal local variables instead of + statics where possible for the serial code. + - Fix bug "xms_EMB_info" in XMS code. + - Set video_ints for vgaemu code, otherwise it will forget about + the fonts later. + - Applied some Debian tweaks from Herbert Xu + * to have DOSEMU working out of the box even if the user doesn't + have a private installation. + * $_hdimage drives adjust according to the setting of DOSDRIVE_D. + * change /etc/dosemu.conf to /etc/dosemu/dosemu.conf to match the + default (we still support /etc/dosemu.conf however). + - Script changes: + * adjust to use ~/.dosemu/drives/* by default; ~/.dosemurc is no + longer compulsary even for private installations. + * do -home by default + * only display "what DOSEMU does" once, be quiet afterwards. + * apply the '\r' keystroke by default to boot DOS quicker. Just + use -input without arguments if you want to use F5 or F8. + * use -install without arguments to force a FreeDOS + re-installation. +2003/02/04 ver 1.1.4.11 unofficial pre-release + From Stas + - prevent IRQ conflicts and fix double registrations of the + keyboard IRQ in init.c and serv_8042.c. + From Bart + - (and Stas) fix comcom problem when environment segment == 0 + - small Makefile cleanups; include only necessary dependency files + - remove all traces of $_secure, $_odd_hosts and $_diskless_hosts + since by default any remote hosts will get root privileges + dropped and diskless host security can be managed via sudo. + - re-add SLang library, but now updated to version 1.4.8, as a + plugin, in stripped down source code form, to avoid problems + with the utf-8 enabled default SLang library that various + distributions ship. + - adjustments to dosemu script: do not silently write to + ~/.dosemurc, run with /bin/sh instead of /bin/bash and only + write $_hdimage to ~/.dosemurc for system-wide installations. + Also adjust "-install" to be able to take no parameters (which + reinstalls FreeDOS). + - quit DOSEMU (via the sigchld handler) if the port server is + killed. + - remove config.usesX setting and any usage of it. It was only set + by the -Y and -Z options which seem to have been unused since + 1995. + - fix for current_iopl in priv.c (misbehaviour spotted by Reinhard + Karcher) + - don't open /dev/gpmdata-style mice in terminal mode. They don't + work then. + - Made the portserver synchronous for port writes -- DOSEMU waits + for an acknowledgement. This already naturally happened for reads. + - use the XFree86 video mode extension if available to switch + resolutions for fullscreen X. + - the X mouse cursor is now always invisible if mouse grab is active. + - center text modes for fullscreen X. + - (with help from Stas) don't pass the ctrl-alt-f keycode to DOS + apps when fullscreen is toggled. Enable mouse grab automatically + for fullscreen X in graphics mode if the X mouse cursor is + invisible. Move fullscreen and mouse grab toggle code to + seperate functions. +2003/02/01 ver 1.1.4.10 unofficial pre-release + From Stas + - pic_force_count->pic_watchdog correction + - fix VIP/IF releated race in DPMI code. + - allow DSP commands in high speed mode (fixed problem with + Pinball Dreams 2) + - fix for AltGr&new keyboard code if X_keycode = (1) + From Stanislav Safronov + - serial transmit fixes + From Clarence + - stop port server a little later. + From Bart + - moved all "extern" declarations to header files and added + warnings for GCC to keep it that way. + - (inspired by an old fix from jt@npdaxp.fuw.edu.pl): + improve error codes for file region locking + - more portable and reliable way for passing parameters in + default-configure and dist/dosemu (thanks to Mozilla) + - (reported by Esa Tikka) Glibc detection has to use -lt not -le. + - reorganized plugins a little bit so that all header files are + symlinked into src/plugin/include and the extern declarations + go into plugin_config.h + - (reported by Grigory Batalov) fix mkbindist and dosemu script + for non-systemwide usage. + - dosdebug now gets a map of DOSEMU symbols on the fly instead + of relying on some dosemu.map file. Hence installing dosemu.map + is no longer necessary. + - added SIGCHLD signal handler to clean up the port server. + - install most signal handlers after forking the port server and + let it ignore VT acquire and release signals. + - the mapself driver needs to open /proc/self/mem before dropping + privileges. + - (reported by Reinhard Karcher) only copy relevant things from + 40:xxxx real memory area when $_vbios_post = (0). + - reorganized port server so that it calls the normal port + handlers, which in turn detect if iopl==3 so they can do in/out's + directly. + - (reported by Claudia Neumann, but not quite fixed for everyone + though) fix Linux console terminal init if the external charset + is "cp437". + - added initial support for full-screen X. Use ctrl+alt+f to + toggle (using the XF86 video mode extension will come later, so + text may still be small and graphics somewhat slow). +2003/01/26 ver 1.1.4.9 unofficial pre-release + From Bart + - More makefile and autoconf fixes, cleanups and improvements: + * use CPP and CPPFLAGS + * check if dependency files are out of date + * collect needed libraries in LIBS + * use autoconf's X include/library findings instead of our own + * fix problem with not defining ASPI_SUPPORT and WANT_WINDOWS + (that broke some aspects of DPMI) + * src/Makefile no longer includes Makefile.main, and + Makefile.main no longer includes Makefile.common. That solved + some problems with overlapping rules. + * put "-D" variables in config.h to reduce the length of the gcc + commands. Adjust some files so that they include "config.h". + Compiletime paths are now stored in confpath.h. + - add -Wstrict-prototypes to the CFLAGS and fixed all new + warnings. Also removed some related "extern" prototypes in C + source files. + - clean up run_caller_func code so that it is as Eric intended + it to be ;) Solve the problem with default_interrupt. + - increase default DPMI memory limit to 20MB. + - start the port server if the speaker is in native mode. + From Clarence + - some more preliminary comcom space corrections +2003/01/24 ver 1.1.4.8 unofficial pre-release + From Bart + - upgrade autoconf & friends + some top level makefile + simplifications + - remove leftover base-configure + - fixed "tap" typo in global.conf + - simplify dosemu.users parsing in parser.y.in and reintroduced + "nosuidroot" (which now simply drops privileges) + - mouse: open r/o if it's a FIFO (like /dev/gpmdata), a PS/2 mouse + or a bus mouse. This solves a few permission problems. + - replace bzero with memset + - port server should always be started if config.pci = 1. + - fix S3 graphics card driver high port problem reported by + Reinhard Karcher + - Fix coopthreads problem (inserted spaces too often) +2003/01/21 ver 1.1.4.7 unofficial pre-release + From Andy Shevchenko and Grigory Batalov + - Add Russian manual page translations. + From Stas and Bart + - rename "$_pic_force_count" to "$_pic_watchdog". + From Bart + - autoconf structure changes: + * configure now calls default-configure, which calls configure + (recursion is avoided using an environment variable). + * use configure.ac instead of base-configure.in and acconfig.h + * ./mkconfscript is obsolete: use "autoreconf" + - security related changes: + * dosemu.users is no longer mandatory for suid operation - + by default a suid binary gives access to console graphics + only and drops privileges immediately if it is not on the + console. Restricting who is able to run DOSEMU with privileges + is better done using sudo. + * Mouse, serial, IPX and direct partition access are no longer + enabled through suid-root privileges -- you have to change the + permissions on the individual devices in /dev. + * Remove all configuration classes which do not affect ports, + hardware ram, direct IRQ's or raw network access. + * added $_console_pci and $_console_ports settings to enable PCI + and I/O port access only if DOSEMU is run on a Linux console. + * move the initialization of the mapping drivers beyond the + point where DOSEMU drops root privileges. + * $_hdimage_r and $_secure are no longer relevant. + * adjusted some of the documentation. + - small coopthreads/comcom adjustment (cd. should work too). + From Witold Filipczyk + - Add $(DESTDIR) prefix to "make install" + - Change "slang.h" to + - add info about the Polish keyboard in dosemu.conf. +2003/01/19 ver 1.1.4.6 unofficial pre-release + From Stas + - enforces the PIC reentrancy protection and fix several related + bugs. + - add a mechanism to force a PIC reschedule, if the watchdog timer + for pending interrupts is invoked n times. + - fault handling fixes: check selectors before loading them into + context. This hopefully removes the possibility of an unhandled + segfault. + - make POST for OSS drivers optional via a compile-time option + From Clarence + - Fix comcom problems: + * a space is now inserted for cd\, dir/p etc so that they work. + * check for things such as "..\ws". Comcom thought that this + meant a file with extension \ws, rather then a file ws in the + parent directory. + From Bart + - remove runasroot compile-time option. It is not relevant anymore + now that DOSEMU drops root privileges. + - add possibility to run DOSEMU through sudo while still dropping + root privileges via the SUDO_UID and SUDO_GID environment + variables. + - remove restrictions on $_dpmi since DPMI is no longer inherently + insecure. + - adjust QuickStart and README.txt to the changed security + situation. + - added a "-s" option to the dosemu script to invoke dosemu.bin + via sudo. + - solve X mouse problem for certain window managers that invoke + LeaveNotify and EnterNotify events whenever you click a mouse + button and not only when the mouse leaves and enters the window. + - introduce $_pic_force_count to allow runtime configuration + of Stas' PIC hack. + - reintroduce the ESP hack which was removed in 1.1.4.5. A "dir" + in FreeDOS/comcom with a hogthreshold of 1 became much slower + without this hack. +2003/01/17 ver 1.1.4.5 unofficial pre-release + From Stas + - remove $_toggle -- it is no longer needed. + - fix for TUN/TAP vnet configuration. + - implement POST less video init. + - Don't sleep inside IRQ handlers or when there is some async + stuff to handle. + - Remove ESP and hogthreshold hack. + From Bart + - Drop root privileges just before booting and fork off a port + server if necessary, that can handle the high port I/O using + pipes. With this in effect, DPMI in suid-root DOSEMU should + no longer be inherently insecure. + - Fix for filename problems in the MFS if parts of the path were + mangled (reported by Clarence). + - Remove unused "$_videoportaccess", introduce "$_vbios_post". + - Fix a few warnings. +2003/01/15 ver 1.1.4.4 unofficial pre-release + From Stas + - implement $_cli_timeout (an official hack that is necessary + to get sound working in DOOM. + - PrintScreen should print to LPT1 instead of LPT2. + - (heavily modified by Bart) add missing instructions (REP MOVSD + et al) to instremu.c and cleanup prefixes a little bit. + From Bart + - Remove precompiled S-Lang library -- please install slang-devel + if you want to compile DOSEMU; new setting "slangdir" to specify + the S-Lang directory. + - Remove libc5 and gcc 2.7 support and associated headers that are + no longer necessary. + - ./mkpluginhooks can now be used to just update parser.y and + lexer.l; this happens automatically for "make". + - Some header/standards related cleanup: don't use , + use instead of , use strerror() instead of + str_errlist[], use (u)int*_t instead of __u*/__s*, avoid valloc(). + - Move code from dosext/dpmi/msdos.h to dosext/dpmi/msdos.c. + - Fix bug with nested findfirst()s in mfs.c + - Fix paste bug for xdosemu (newlines were translated to '0's). + - undo old temporary Insert key fix. + - Fix lexer problem if dosemu.conf misses an EOL at EOF + - parser: distinguish (on) from (1) by making (on) equivalent to (-2) + - use -MMD instead of -MD: we should not explicitly depend on + system header files. +2003/01/09 ver 1.1.4.3 unofficial pre-release + From Bart + - ./mknewyear +2003/01/09 ver 1.1.4.2 unofficial pre-release + From Stas + - A proper fix for the insert key. + - Get the xkb switch working with X_keycode on. + - Start deprecating dosnet. + - Remove root check for IPX route setting. + From Bart + - Re-add $DOSEMU_VERSION setting to global.conf; autoexec.bat + in dosemu-freedos-bin uses it. + - Put coopthreads commands into commands/ together with the + other ones at 'make' time. + - Mouse: init mice->fd to -1 for X. Move mice into config.mouse. + Use char *dev instead of dev[255] to avoid a potential buffer + overflow. Try to move as much mouse handling as possible into + mouse.c, instead of in files outside src/base/mouse. + - Add if_tun.h because we don't want to depend on external + kernel headers. +2003/01/08 ver 1.1.4.1 unofficial pre-release + From Witold Filipczyk + - Fix to get MFS to work more reliably with PTS-DOS (slightly + adapted by Bart) + - Hack to "add support" for Oacute in cp852 and Polish keyboard + layout + - small doc build fixes + From Stas + - fix for DOSEMU hanging the machine if used with -H0 on the + graphics console; option to unfreeze DOSEMU from dosdebug. + - IPX fixes: + * RIP packets have type 1, not 4 (4 is SAP); + * Setsockopt doesn't require root for setting the packet type + when the proper Socket Level is used; + * select() can alter its arguments, so they must be always re-inited; + * Check if we really received a RIP packet and retry if we didn't, + rather than fail. + * remove enter_priv_on() where it is not necessary. + - midid fixes: + * Get rid of in_addr_t as it turned to be not present in some glibc + * Shut up the misleading warnings + * Stubs for all the System midi messages and the Real-Time midi + messages; + * "running status" must be reset by the System/Realtime messages; + * Remove the hack that was used to make the game "stunts" happy in an + absense of the above functionality. + - TUN/TAP support. This is intended to substitute the dosnet + module. dosnet requires root, while TAP doesn't. + Note: set $_vnet=(2) to enable the TAP support. $_vnet=(1) + (still) uses dosnet. + From Jason Eckhardt + - base/bios/int10.c (x_set_video_mode): Do not set INT 1F on a mode change. + (adapted by Bart: initialize INT1F at DOSEMU startup) + From Bart + - disable unprivileged ttylocks changes in ~/.dosemurc. + - ~/.dosemurc can now completely overwrite dosemu.conf + for non-suid-root DOSEMU. + - configuration simplification: DOSEMU now parses the default (as + if it was uncommented) dosemu.conf internally before + global.conf. This made it possible to greatly simplify + global.conf again. Also the initialization in init.c is no + longer necessary for the most part. + - check for lex or flex + - several small 'make install' tweaks + - the dosemu script now does not complain if /proc/ksyms does not + exist and can work without ~/.dosemurc. Also dosemu.bin is now + exec'ed. Systemwide paths are substituted during "make" rather + than "make install" so that a different prefix can be used + for "make install". + - make install_systemwide bare-bones (kept for backwards + compatibility for now) + - XMS cleanup and better error codes + - MFS: solve problem with repeated FindFirsts without FindNexts; + also use a circular buffer instead of a stack. + - some cpuemu code needs to be guarded by #if X_GRAPHICS + - adapted the manpages and "COPYING" a little bit to reflect that + the fact that Hans no longer maintains 1.0.x +2002/12/17 ver 1.1.4 released + From Bart + - Fix X font handling and misc other things in the dosemu script + and make install. + - add vga11x19 font + - some minor manpage and documentation fixes + - fix Makefiles: data.c dependencies and src/tools/periph + - verbose make install + - debug output goes to ~/.dosemu/boot.log by default for dosemu.bin + too + - allow $_dosmem in ~/.dosemurc and warn if more than 640K is used + for X or console graphics. + - install midid by default + - fix compiler warnings in msdos.h and comcom.c. + From Stas + - fix DOSEMU freeze (from ctrl+alt+pause) when $_rdtsc=(off) +2002/12/15 ver 1.1.3.9 unofficial pre-release + From Stas + - protect against stack overflows in dpmi.c. + - improve the boot sector for mkfatimage[16]. + - use ntohs() to correct packet driver debug messages. + - keep track of subsystems and devices they used to avoid clashes. + - implement the PSP and fix the DTA translation for + src/dosext/dpmi/msdos.h + - fix MPU-401 initialisation. + - avoid pthreads DoS + - adjust fatfs.c to support PC-DOS + - dosdebug fix to make the "normal" trace mode to step the real mode + in DPMI + - limit transfer buffer for ASCIIZ strings in msdos.h. (Bart: use + snprintf instead of strncpy). + - do segment register translation only when necessary in msdos.h and + dpmi.c -- the DOS DPMI client translator and not the server is + supposed to be in charge of them. + - don't allow HW interrupts in the dosdebug force trace mode. + - config update for sound (Bart: organized a little differently). + - Temporary fix for the Insert key (the real fix will be post 1.1.4) + From Bart + - Some more global.conf adjustments to get the terminal and keyboard + working correctly by default. + - Based on an idea of Clarence: use Ctrl+Alt+Pause to freeze and + unfreeze xdosemu (he wanted it as a dosemu.conf option to always + do it when switching to the background but I think a keystroke + is more flexible) + - remove llseek check: just use the libless one. + From Reinhard Karcher + - correct the German keyboard. + From Clarence Dang + - comcom fixes: + * fix the < in "more < file" + * fix buffer overrun if the history file is corrupted + * fix buggy execution order (now: .com, .exe, .bat) + * fix bug where it was impossible to overwrite a file with > + because > actually appends (>>) instead of overwrites + * fix bug where pressing DEL key at the end of the line deleted + the character but did not update the display + * fixed a little overrun in delete() + * fix bug where scrolling to the right will scroll one too many + characters + * fix bug where inserting a character actually adds a character + to the end of the string. + * allow "DEL /P FILE", not just "DEL FILE /P" + * also, print an error if the file to be deleted doesn't + actually exist + * fix bug where the prompt was shown even when ECHO_OFF + * EXIT in the toplevel comcom.com calls leavedos() + * implemented TRUENAME + * partially implemented "template"-based command line completion + keys such as F1 and F3 + * print "Invalid drive specification" when changing to an + invalid drive + * allow redirection of PAUSE's output + * DIR now looks like a Win98 DOS DIR + (changed spacing, dirs do not have sizes in DOS, + 12-hour times instead of 24-hour, comma-separated file sizes) + * make TIME, DATE, kbdask() look more like they do in DOS + * newline displayed after every command if ECHO_ON + * make "echo. something" work + * CHOICE is an external command in DOS so the reference to it has been + removed + - joystick fixes and cleanups + * fix bug where joystick axis values from different points in time + are returned, in a single joystick position query from a DOS app + * introduces a "joy_latency" variable which substantially improves + joystick performance by reading less often from linux; + * fix some missing config stuff e.g. in config_defaults() + * fix port word read/write behaviour, code cleanup and doc fixes + - fix EMUVER="VERSION.1" problem + - fix "ERROR: ERROR:" messages in src/base/misc/disks.c + - allow emulated pc speaker sounds to go for longer than 990ms, + but for 30s. + - get freeze_dosemu to "pause" any pc speaker sounds + - don't turn off the speaker in X before generating a new sound + (unnecessary and causes "clickyness") + - ViewDocs: get manpage help working (./man/dos.1 no longer exists) + - Fixed some shell problems for make install. + From Robert Komar and Stas + - major midid update + From Matthew Sullivan + - Check for a tty before using virtual comports. +2002/12/14 ver 1.1.3.8 unofficial pre-release + From Bart + - revive "make install" - this uses a private boot directory setup + using symlinks by default, but users can opt out using "none". + - ~/.dosemurc now replaces the role of ~/dosemu/conf/dosemu.conf + and ~/dosemu/conf/dosemurc + - use ~/.dosemu/boot.log instead of ~/dosemu/boot.log to allow + execution of the dosemu script without a boot directory under + $HOME. + - allow execution of the dosemu script for suid-root dosemu.bin (with + the usual restrictions for ~/.dosemurc). + - add compile-time paths to compiletime-settings; disable setup-dosemu + for now since it cannot handle these. + - allow all dosemu.conf settings to be optional where DOSEMU has + built-in defaults; reflect this situation by commenting out + everything by default + - allow absolute paths in $_hdimage in dosemu.conf and ~/.dosemurc + - remove "restricted" and "guest" restrictions when running DOSEMU + non-suid-root. + - allow change of $_rdtsc, $_hogthreshold, $_rawkeyboard and + $_ttylocks in ~/.dosemurc by non-privileged suid-root users. + - disallow change of $_ports in ~/.dosemurc. + - removed $_keybint: it is no longer used anywhere. + - hide pthreads support since it can be extremely unstable + - fix typo in kbd_unicode keymaps.c so that the "auto" keyboard layout + works again + - first stage of removing externs + - fix newline replacement in bisonpp.pl for compatibility with newer + Bisons + - remove "extern int errno" and use #include instead + - move new keyboard documentation into the mainline. + - removed old old keyboard documentation and some misc doc adjustments + - add option to generate multiple HTML documents for use on dosemu.org + - Use indirect file descriptors in the MFS to avoid the possibility + that DOS programs can close or manipulate random file descriptors + that are in use by DOSEMU. + - MFS: FIND_FIRST should return NO_MORE_FILES and not FILE_NOT_FOUND + if no file is found. + From Emmanuel Jeandel + - add %pure-parser to parser.y.in; else we get a crash with newer + Bisons or gcc's + - remove a strlower to fix an MFS bug for "lredir e: $HOME/Games" + From Stas + - update sound-usage documentation +2002/11/03 ver 1.1.3.7 unofficial pre-release + From Stas + - Change $_sound to on and $_mixer to "" by default (Bart: + same thing in config.c). + - vm86_GP_fault() is no longer a signal handler so the relevant + code could be removed. + - remove the LDT-mangling to allow expand-downs. This allows a lot of + DPMI programs to run in DOSEMU that didn't before (such as + X32 (Zortech/Symantec/Digital Mars) and some others). Note that + "secure on" already disabled DPMI completely for suid-root DOSEMU. + - Fix three bugs in dpmi.c: + * access to dpmi_stack_frame[-1]; + * save_rm_regs() without restore_rm_regs(); + * use of the real mode interrupt stack as a general stack + - ignore RIP advertisements from the local net for non-root IPX + - move some DPMI structs back to private + - Fix dosdebug to properly work with DPMI, even surviving RM->PM + switches. + - dosdebug fix for force trace mode + - Remove old timer #if 0'ed code + From Reinhard + - Fix numlock switching on/off when switching vc's. + From Bart (reported by Reinhard) + - Don't initialize sound if $_sound=(off) (config.sb_irq == 0 is a + signal for that) + - Fix the Turkish keyboard lexer code in the right place now. + - (edited from suggestion by Matthew Sullivan + ): try rmdir before rm -rf when + deleting the per process temp directory. +2002/11/02 ver 1.1.3.6 unofficial pre-release + From Stas + - Remove bogus check for ints >= 0xe0 in dpmi.c. + - Extensively reworked the Sound Blaster Pro emulation code. + From Michael and Reinhard Karcher + - Add missing semicolon for newer yacc's in parser.y.in + From Bart + - Fix old keyboard code compilation and mkbindist (noted + by Grigory Batalov) + - Update sound documentation +2002/10/22 ver 1.1.3.5 unofficial pre-release + From Baurjan Ismagulov and Sergey Suleymanov + - Turkish keyboard layout + From Sergey Suleymanov + - fix KEY_LESSGREAT for non-US layouts + From pesarif + - corrected spelling mistakes in the documentation and various + other tidbits. + From Witold Filipczyk and Stas + - Improved X detection + From Bart and Stas + - make global.conf part of the dosemu binary. Users can still + use an external global.conf using a command line switch or + by editing dosemu.users, but by default the builtin one + is used. + - improved emusys handling + From Bart + - removed emubat and explain the /K autoemu.bat command.com option. + - added the /k switch to comcom. + - various MFS fixes. + - turn linkstatic off for development builds. + - various docs fixes (for global.conf and netdev). + - regenerated the docs and the DANG + - turn dpmi on and set -D+cw by default + From Stas + - correct in_dpmi usages + - add debug info on int return + - use more verbose startup info + - improve the hlt hack: the kernel always keeps the IF flag set so + we have to set it in a hlt hack as well. + - use proper (dpmi/non-dpmi) registers in dosdebug + - corrected incorrect usage of in_dpmi at various places. + - introduce a config_post_process() function that is called after + parsing. Otherwise there is no good place for disabling DPMI if + "secure on". Moved some related stuff from parser to post_process(). + - remove asm run_irqs and other PIC cleanups. + - add "if(REG(eflags) & AC) error()" to avoid a gcc problem. + - Fix the DPMI mouse callback. This was necessary for the DPMI16 + Borland C IDE. + - Fix problem with ints 0x1c, 0x23, 0x24 for DPMI + - introduce a $_netdev option, because the packet driver can + operate outside vnet mode too. It is not necessary to set up + dosnet anymore if you just need the packet driver to talk to, + say, eth0. + - Fault handling fixes for DPMI and instremu. + - release key on losing focus + - changed restore_pm_regs() to not require an extra copy_context() + after it (it does it itself now). + - leavedos() if DPMI stack structures are overflowed. + - a port logging printf fix +2002/09/15 ver 1.1.3.4 unofficial pre-release + From Eric + - better fix for Ins key + From Stas and Bart + - fix bug in mouse fn 0x0020 (enable mouse driver) + - clear AC upon an INT instruction (software and hardware ints). + From Stas and Grigory + - parse /proc/net/ipx_route for existing IPX routes (without + requiring root) + From Stas + - disable AC|ID clearance (leaving it only for the dosemu flags) + at other places. + - ignore bootoff when the real floppy is not accessible + - convert the packet driver callbacks to be DPMI-safe + - add option $_pktdriver to disable/enable the packet driver + (int 0x60) for vnet + - dosnet fix for broadcast (so ping works between two DOSEMU + sessions) + - get the Pause key working under DPMI + - disable PCI if there are no root privileges + - Unconfuse more serial error message like ERROR: \n DOSEMU: + cannot... + - use a wider range of ports for IO-tracing (the "T" debug flag) + - fix buffer overflow in mfs.c + - fix a client's protected mode stack corruption under high IRQ + pressure and in some other cases (notably since 1.1.3) + - use sb_dsp for sound in global.conf + From Bart + - remove the old int code. + - add some version guards to global.conf. + - remove plugin_configure warning. +2002/09/12 ver 1.1.3.3 unofficial pre-release + From Stas and Bart + - fix several potential t_dif (vs. hitimer_t) overflows in + the keyboard code and vgaemu code. The vgaemu overflow + caused problems after ~half an hour of inactivity in some + DOS programs. + - fix problem where Esc needed to be pressed twice. + From Stas + - remove special modifier handling for Ins: Ins is not special + like CapsLock is from the BIOS point of view; now Ins does not + trigger twice (annihilating itself) anymore when you press the + key. + - ignore keys that are already handled by DOSEMU. + - treat the case base==0 correctly for unicode_to_long() in + src/plugin/translate/unicode_utils.c. + - resurrection of DPMI->PIC interaction: + add overall stability for DPMI apps. Also fixes + a number of related bugs for v86 mode. + - Make IPX callbacks DPMI-safe, taking an advantage of + the PIC enchancements (that were integrated into + dosemu-1.1.3.2). + - Memory allocation (pagemalloc.c, used by DPMI) fix: + some DOS extenders expect that newly allocated memory is zeroed! + - Only open the serial port on demand, not as soon as DOSEMU is + started. + - Allow IPX without root privs unless you are going to use RIP which + is trying to add a route and fails. + - preserve flags (pushf/popf) on "Pause". + - select the 386 by default for future unknown CPUs (to avoid part + of the P4 problem - we now have a basic backup). + - dosdebug fixes: one makes dosdebug to correctly destinguish between + real and protected mode, second makes it to correctly disassemble + pushfd, popfd and iretd. + From Bart + - MFS fixes: + Try to improve the file locking behaviour - see the table in + mfs.c for differences between real DOS handling according to + RBIL and our handling. + Determine whether a file a special (a device) or not by + walking the DOS device driver chain instead of comparing a + fixed list of names. Also open/"create" the device driver + when appropriate in the same way as DOS does it. + Misc cleanups: add "const" at various places; use memcpy() and + memset() instead of strncpy(). + Fixed a bug where the hlists were not freed when findfirst was + called for volume labels. + For efficiency: find first without wildcards uses findfile() + instead of needing to build an hlist. + - sleep when dosdebug is in a "stop" state. + - fixed a few trivial compiler warnings. + From Clarence + - conveniently allow DOSEMU to display the name of the currently + running program in the X Window Title (like Win98 does), if the + user is running COMCOM.COM + From Sergey + - set "access denied" if locking a region of a file is impossible. + From Per Jessen + - add a new dos helper: 0x82: returns PID and PPID in ax and bx. +2002/06/16 ver 1.1.3.2 unofficial pre-release + From Stas + - enhancements to PIC and mouse driver. This is intended to fix + crashes (esp. under X) related to mouse moving. + - make hogthreshold *value* to matter, not only the fact + that it is >0. + - Unconfuse serial error message like ERROR: \n DOSEMU: cannot... + It's more clear to merge the two lines. + - enable IPX code to work with doom's ipxsetup: + return status of send_packet in AL (else doom fails) + abort loops on error to not deadlock + correct and more verbose debug messages + - two small fixes to the new keyboard code + From Clarence Dang + - new and working joystick support. Everything is detected at + runtime, except for pthreads: the joystick works a little faster + with pthreads support, but DPMI can become unstable if you + enable pthreads, and hence it is disabled by default for now. + From Bart + - did the autoconf and dosemu.conf bit for the joystick support. + - enabled stripping of a trailing dot for mfs (bug report from + Johan Gill) + - corrected off-by-one error for mfs environment variables. + - made new keyboard code the default in compiletime-settings + From Grigory Batalov + - remove perl dependence from DOSEMU + - unset DOSEMU_STDIN_IS_CONSOLE if "-X" is used + From Sergey Suleymanov + - added fonts.alias + - Cyrillic support: this introduces a Russian keyboard layout as + well as a keyboard layouts switch. It works with both the unicode + and the non-unicode keyboard code. +2002/05/26 ver 1.1.3.1 unofficial pre-release + From Eric and Stas + - pic.c: C run IRQs; bitops.h: find_bit clean-up. + From Stas + - ignore SIGPIPE + - make sure to always clear the AC and ID flags, when returning from + V86 mode and do not log that action by default + - for dosdebug: we want to trace the program, not the HW int handlers + - pagemalloc.c: collect garbage when freeing memory (makes sure + that DPMI memory is in a clean state when DPMI is entered, among + other things); extra NULL check; refuse realloc with newsize==0. + - pci.c: check each priv_iopl() for failure + - leaving DOS before booting calls exit(), not leavedos(), to + prevent a SIGSEGV + - circular log buffer of 50 MB - instead of leavedos() it + truncates and rewrites from the start when the log is full + - initialize mouse speed correctly (modified from Manfred Scherer) + - ports.c - make sure to handle dword ports similarly to word and + byte ports - without this suid-root DOSEMU has full access to + all hardware ports using ind/outd! Another reminder that you + should not let use suid-root DOSEMU if local security is + important. + From Manfred Scherer + - do not center mouse cursor when changing graphic modes + From Sergey Suleymanov + - int10-bell: return from function when beeping. Otherwise + the screen scrolls up. + - MFS fixes: Do not permit file deletion for read-only files. + prevent an hlist stack overload with big directories. + return NO_MORE_FILES if FIND_FIRST does not find a + file. + extend file if zero bytes are written past EOF + (all slightly edited -- Bart) + From Witold Filipczyk + - new Polish keyboard layout (now also applied to the new + keyboard code) + From Bart + - MFS: remove trailing spaces in filenames and extensions in the + Unix paths. ANSIfied mfs.c function definitions. + - enable raw keyboard for non-privileged DOSEMU + - int16: handle ascii 0xe0 and small Cyrillic er correctly. + - comcom.c: "errorlevel" applies to external commands only. + - use Autoconf 2.52 and check to see whether we should link in + libdl for libX11 +2002/03/19 ver 1.1.3 released + From Stas + - We must not sleep() immediately after mouse event happened, we + have to let the program to handle the event (src/base/async/int.c). + - src/command detect.h adjustment and assemble tools which are not + produced for coopthreads by default (isemu.com, mgarrot.com and + fossil.com) (adjusted by Bart) + - remove wrong check for sb for dma_run in dpmi.c, use pic_run macro + in pic.c + From Eric + - keyboard fix for int16.c + From Bart + - re-enable KEYBUF_HACK while taking care of pause key. + - regenerated documentation/DANG + - moved some of the more extensive unicode keyboard debug output + to a higher level + - made compilation with gcc 2.7.2 possible for simx86 +2002/03/10 ver 1.1.2.9 unofficial pre-release + From Stas + - fix for incorrect Wolfenstein 3D Adlib detection + - DMA indent patch to prepare for sound + - support for pause key + - add a little sleep to int16/ah=1,11 - this makes your current + setting of hogthreshold feel differently, however programs + polling the keyboard behave much nicer now + - new serial fixes + From Stas and Bart + - MFS DOS path buffer overflow checks and fixes + - Fix buffer overflow in int10.c for the screen. This caused + a page fault for terminals > 132x60. Enlarged this limit + to 255x128 and enforced it. + From Etienne Lorrain (etienne@masroudeau.com) + - Subject: RBIL problem INT10/AH=0x1B table 00040 offset 22h + off-by-one error: this should report the number of rows (not rows-1). + From Emmanuel Jeandel + - Patch for Lemmings : Lemmings is 16 color, but uses 4 planes mode + (very unusual). It suffices to add few lines in X.c to treat it. + From Daniel Morris + - changed MFS to return FILE_NOT_FOUND instead of PATH_NOT_FOUND for + FIND_FIRST (this isn't always the proper solution but that would + involve more reorganization of mfs.c - Bart) + From Miloslav Trmac + - Disabled signals in the redirector to avoid problems with smbfs. + From Manfred Scherer + - fixed memory leak in mfs.c, enlarged DOS path to 66 characters max. + From Hartmut Richter + - fix to avoid that serial input is lost in special situation + (improved by Stas) + From Herbert Xu + - fix to avoid division by zero for "0 MHz" cpu's - apparently there + are some broken /proc/cpuinfo's out there. + From Witold Filipczyk + - Keyboard ALT 224; Polish_keyboard.patch (only applied to old + keyboard code) + From Bart + - moved int16 code to monitor space, except for a wait-for-key loop + for ah=0,10. + - fixed xms.c to only return BL=0 for successful A20 query: all others + should not touch BL if "success" (referring to XMS spec) + - (with help from Alistair) doSgmlTools.pl adjustment but it still + does not generate the right thing + - fixed all compiler warnings from gcc 2.95.x and gcc 3.0.x +2002/01/23 ver 1.1.2.8 unofficial pre-release + From Bart + - The addresses in crtcemu need to be shifted by the current value + of vga.crtc.addr_mode. Also enabled hardware scrolling for + textmodes. This enables some games and 8086-Minix to display + properly. + - make .sys files (not created from coopthreads) also by default, not just + with make dosbin. + From Stas + - as pit[0].time.td is initialized with GETtickTIME, we have to initialize + tp.td with GETtickTIME also, else time_curr contains garbage and sometimes + goes beyond zero. + - update dosemu flags on popf under CPUemu + This allows doom to work with sound. + - Don't allow direct access to speaker's ports when $_speaker is not "native" + This breaks some kernel stuff (pc-speaker driver in particular) and we + have an emulated PIT anyway. + Eric confirmed that this doesn't break his Matrox. + - pic_untrigger() implemented. + This is for SB detection. + - PrintScreen implemented. + I hope this is now what Eric wanted it to be. + - Fixes for ecpuon, ecpuoff and system commands + From Eric + - Add cp858 support cp850 + EURO + - Add koi8-r support for the russion hackers + - Fix comment in keyb_raw.c + - keymaps.c Corrected $_layout="auto" rename this to $_layout="kernel" + - terminal.c Improve handling of charcter sets that use 0x80 - 0x9f + - Fix incorrect initial numlock state with new keyboard code +2002/01/12 ver 1.1.2.7 unofficial pre-release + From Stas + - set VIP on pic_request() to activate the int ASAP. + Without this, some games fail to detect SoundBlaster IRQ under DPMI. + - DPMI: always return to dosemu code for handling signals. + IF is not set by popf and dosemu has to do some background + job (like DMA transfer) regardless whether IF is set or not. + - Fix cmdline.c. + - remove clear_console_video() from old keyboard code. + also remove scr_state.current init (Bart) + From Bart + - leave interrupts >= 0xc0 untouched. int 0x7a special cased by other + means. Do not special case 0x45-0x5f any more in bios.S (from Stas) + Make slave irq behaviour equal to master irq (iret, not lret 2). + - documentation generation: sgmltools needs --jade-opt not -jade-opt + - remove HACK in vgaemu.c. + - add missing files from keyboard plugin. + - Eric's new keyboard plugin is still disabled by default for non-devel + builds, so relevant plugin_enables should be set to no. + - ./mknewyear + - remove Athlon detection from /proc/cpuinfo in base-configure.in: use + ./default-configure --target=athlon-pc-linux-gnu + if you want Athlon optimizations for now. +2002/01/06 ver 1.1.2.6 unofficial pre-release + From Eric + - Enable new keyboard code in compiletime-settings.devel + - Clean up the kernel revector bug work around. + - removed dead code in int05 left over from when it was called + with REVECT + From Bart (reported by Reinhard Karcher, help from Stas) + - don't zero out int 7a entry (int.c) , unhack and comment out + official JES hack (bios.S) + - remove kernel < 2.0.30 code and adjust requirements + From Stas + - serial code irq fixes + From Thomas Langhammer + - dosemu fossil driver enhancement: support the GetDriverInfo + function (1Bh) without specifying an existing port number + From Emmanuel Jeandel (adjusted: Bart) + - CRT scan_len adjustments and related fixes to get some + Mode-X modes (eg. 360x480) working correctly. + You can see the difference with games like Battle Isle 2 + or Scorched Earth (the latter is a shareware). +2002/01/06 ver 1.1.2.5 unofficial pre-release + From Eric + - compiletime-settings, compiletime-settings.devel + - explictly enables/disables the plugins + - dosemu.conf Added $_external_char_set & $_internal_char_set + - global.conf Added code for $_external_char_set & $_internal_char_set + - wyse60-pckb Added terminfo entry example that uses scancodes + - mouse.c Made mouse_keyboard() update conditional on HAVE_UNICODE_KEYB + - X.c + - Added tests for the XKB extension. + - Made new translation code conditional on HAVE_UNICODE_TRANSLATION + - emu.h Changed HAVE_KBD_UNICODE -> HAVE_UNICODE_KEYB + - video.c Move the remaining video init & cleanup calls from + keyb_raw.c here. +2002/01/06 ver 1.1.2.4 unofficial pre-release + From Eric + - Makefile.main Always link in all of the dosemu .o files + - default-configure + - parses options of the form plugin_ [on|off] + and explictly enables or disables them. + - mkpluginhooks Removed plugin_override code, added plugin configure + plugin configure scripts plugin enable/disable from default-configure + - mkpluginhooks Removed plugin_override code, added plugin configure + plugin configure scripts plugin enable/disable from default-configure + - dosemu_config.h + - config.c + - Added for the config_scrub callbacks + - Added config scrub callbacks so we can make certain the + config options are sane, even from plugins. + - Further killed keybint + - dev_list.c, iodev.h + - Updated to allow registering io_devices +2002/01/06 ver 1.1.2.3 unofficial pre-release + From Eric + - dosemu_debug.h Rework to for the debug print statement rewrite + - Rewrote the debug print statements to allow plugin to + define their own types. + - serv_xlat.c + - Modify Albertos Ctrl-Alt-PgDn hook to work with the new + debug message style. + - init.h + - Added so we can have initialization function in plugins that + don't need a call chain. +2002/01/06 ver 1.1.2.2 unofficial pre-release + From Eric + - mkconfscript: + - Remove base-configure before overwriting it, this solves + with developing with hard links... + - base-configure.in: + - Clean up cpu optimization rules + - Run configure scripts in plugins +2001/11/07 ver. 1.1.2.1 unofficial pre-release + From Alberto + - made it compile with gcc-3.0 under Mandrake 8.0. + You should _not_ use -fschedule-insns2, it makes dosemu crash. + - Reorganized the simx86 directory. Implemented a 'full-emulation' + mode, totally in C like Twin (R.I.P.). + This C-emulator is now working fine, FP is not yet finished + but (apart the abominable speed) it does all what the JIT + version does. + - Prepared simx86 for optimizer by clearly separating the Gen() + function, which generates an intermediate representation into + the IMeta structures, from the actual code generator backend. + - This release is tested on an Athlon 800; there are timing problems + with "slow" (<400MHz) machines. + - There are still some problems with Windows 3.1 and the segment + linker. + - Corrected xms.c: do not touch BH, else Win98 boot failure + - Corrected dpmi.c: WARNING - realmode flags can contain the + dreadful NT flag which will produce an exception 10 as soon + as we return from the callback! - so it's explicitly switched off + now. + From Bart + - added warning about prefetch instruction which does not exist on + i586 or lower (including AMD K6). This should probably be converted + into a compile-time option - otherwise cpuemu crashes on these CPUs. +2001/11/04 ver. 1.1.2 released + From Hans + - get 'make dosbin' working + - "su -" for nobody and space fixes in dist/dosemu + - adjusted COPYING to the current maintainer situation + From Bart + - fix compiler warnings in src/emu.c, src/emu-i386/simx86/protmode.h, + src/base/init/lexer.in + - fix for gcc-3.0 in base-configure.in; let the target_cpu decide which + gcc options to use instead of the host_cpu + - removed restriction to have to run (suid)-root in serial.c for creating + the lock file + From Stas + - remove emusys disabling for lredir'ed drives: at least DRDOS and FreeDOS + already switch from fatfs to lredir before opening config.sys + - graciously terminate on unhandled bound exceptions (int5) + - prevent segfault when DOSEMU_OPTIONS is an empty + string, but not NULL + - SIG_init() must be called *after* sighandler for SIGIO is set, + or dosemu is killed by unhandled SIGIO on startup and you have to + reboot your computer (emu.c, signal.c). +2001/10/28 ver. 1.1.1.11 unofficial pre-release + From Jeff Beardsley + - changes to global.conf to allow com3/4 work on other the irq12 + (this turns out to be the fix for more than 2 serial ports) + Just have a $_com3 = "/dev/ttyS2 irq 5" in dosemu.conf + From Stas + - fixed bug in DPMI prevent calling int > 0xe0 in case + of function 0x0300 (not for 0x0301/0x0302) + - setting unused intvectors to NULL instead iret + From Bart + - fix bug in INT 13 AH=8 altering BL wrongly for harddisks + (base/misc/disks.c) + - updated doc/dosemu.lsm to reflect new maintainer status ;-) + From Steffen + - fix to global.conf: one /var/lib/dosemu forgotten to be + replaced by $DOSEMU_HDIMAGE_DIR + From Steffen + Hans + - fixed bug not booting on Pentium IV (base/init/config.c) + From Hans + - some fixes/enhancements to the dosemu wrapper script + - added '-input' and '-quiet' option + - tweaked args passing to dosemu.bin + - allowed running _as_ root (not suid), if configfiles are protected + - adapted the man page + - disabled obsolete 'root not secure' check in coothreads.h + - fix (again) to VERSION setup in ./baseconfigure + (hopefully this was the last one ;-) + From Eric + Bart + - fix gas -oformat switch: must be --oformat +2001/10/28 ver. 1.1.1.10 unofficial pre-release + From Stas Sergeev + - fixes to xms.c (umb_alloc) to match the LIM's specs. + - make cdrom.S' config.sys line parsable by PC-DOS 7.0 + (patch modied by Hans) + From Bart & Alistair + - fixes in the docs of my bad english;-) + (I'm sure there still are some 'bugs' left) + From Hans + - comcom.c: using INT21,AH=7 instead of INT16,AH=0 for kbd reads + - comcom.c: fixed version number printing + - comcom: command 'cls' was missing + - comcom: command 'break' switched inverse + - further docs updates + - recompiled docs +2001/10/28 ver. 1.1.1.9 unofficial pre-release + From Bart + - fixed a bug in X-speaker code (pitch is short int instead of int) + (src/base/speaker/X_speaker.c) + - fixed crashing DOSEMU if compiled with glibc (close() on already + closed file simply gives error normally, but on glibc it will crash + due to implicit free()). Involved file: plugin/commands/comcom.c + From Hans + - fixed bug in comcom's commandline editor + - fixed bug in comcom's 'DIR' command (reported by Bart) + - disabled use of (real) yacc in base-configure, yacc won't work + any more, we need bison. + - new long options to bypass dosemu.user checks for non-suid runs: + --Flibdir to define DOSEMU_LIB_DIR directly (this also sets + DOSEMU_USERS_FILE=none in which case the config dir moves from + /etc to DOSEMU_LIB_DIR). + --Fimagedir to define a boot path that may differ from libdir + - exporting DOSEMU_HDIMAGE_DIR directly to global.conf + - 'dos' became 'dosemu.bin' to avoid nameclash with win4lin. + there is a symlink ->dos in ./bin, however, distributors should + install the 'dosemu' script as a wrapper which then calls dosemu.bin + - moved generation of the dosemu part of the bindist into the source + distribution: + - ./dist is the directory to contains this stuff + - global.conf and dosemu.conf are common for both, global (/etc) and + private ($HOME) configurations. + - wrote ./dist/mkbindist, which generates dosemu--bin.tgz + - wrote ./install_systemwide script, which creates the systemwide + installation into a configurable directory (root prefix possible + to ease packaging). It accept the following options, which all + may be omitted: + -fd FD_tarball Tarball containing FreeDos part, if omitted we + assume it already to be inplace. + -r root optional prefix for 'instpath' and 'binpath' + -i instpath directory where the systemwide installation + templates reside (default is /opt/dosemu) + -b binpath path where symlinks of the binaries under 'instpath' + have to be placed (default /usr/bin) + If the user accepts the defaults, ./install_systemwide will + just 'update' an existing systemwide installation and all users + on that system will untill than work with the new binaries + while keeping their existing configuration/installation. + - rewrote [x]dosemu script (of bindist) to become the _only_ + executable to start dosemu. On a systemwide install it resides + in 'instpath' (see ./install_systemwide) and is accessed via + a symlink in the private installation (though, because of + 'binpath' also available via $PATH). [x]dosemu will detect when + the user has no private installation yet and will unpack from + the templates in 'instpath', the latter is substituted into + the script itself at time of ./install_systemwide. If the script + stemms from unpacking the original DOSEMU binary distribution + of dosemu.org, the script will know and not try to fiddle with + an eventually existing systemwide install. + The file ~/.dosemu/bindist_path is maintained to find the last + private dosemu directory, even if [x]dosemu is called from + elsewhere. If, however, the user does cd into the private + installed directory and calls the script from there, bindist_path + is changed to this one. This allows switching between different + private installations. + In addition, on a systemwide install, the script accepts the + option '-install ', where 'mydos' is the basename of a + bootdir, that the user has to supply. In this case freedos will + not installed and can be replaced by any proprietary DOS, which + is bootable via fatfs (bootdir). + - updated README.distributors + - renamed ./man/dos.1 to ./man/dosemu.bin.1 and updated it + - wrote new ./man/dosemu.1 + - updated set-permissions + - updated README*.txt, recompiled docs +2001/10/28 ver. 1.1.1.8 unofficial pre-release + From Bart & Eric + - final mouse fix for applications, which do 'mousing' only + by reading mickeys and drawing their own cursors (WP Office). + (replacing 'temp fix' of 1.0.1.5) + From Bart + - fixes to vgaemu: putpixel and hardware cursor + - fixes for dosnet not working under linux-2.4.x + From Hans + - fixed some strange behave when video{none} together with -o/-O + is set ((FILE *)stdout clobbered). In fact this is a workaround, + the real reason is unknown until now. Involved file: bios/int10.c + - a lot of fixes and further enhancements to comcom, such as + implementing missing copy/which command, commandline editor with + history (will be saved/loaded to/from ~/.dosemu/comcom.history), + compatibility should be much better. e.t.c. ... + - updates to README.bindist + - prepared doc/announce for 1.0.2 + - wrote README.distributors + - disabled systemwide installation in Makefiles, + no longer supported. + - removed non-monoton timing stuff, obsolete now, tmonoton has + stabilized +2001/10/28 ver. 1.1.1.7 unofficial pre-release + From Hans + - introduced coopthreads plugin, which supplies non-scheduled (active) + 'task' switching between plain green threads (no new process created). + This has the advantage that _none_ resource locking is needed, + minimal systemoverhead happens and neither DOSEMU nor libc have to + be thread aware. + On init the DOSEMU mainloop becomes 'thread0' with enough stack (1M) + to run as usual. Child-threads are created (256K stack) by stub .com + files from within DOS, have their code in 32-bit DOSEMU and can + (recursively) call DOS and BIOS functions themselves. + Even hooking of interrupt handler (such as INT23/24) is possible + and on start of a stub .com in fact INT23/24 are directed to own + default handlers. + NOTE that except the new doshelper function DOS_HELPER_COOP (0xc0) + there is _no_ hook within the main code, all is handled within + the plugins themselves and you may just copy the plugin directories + to any DOSEMU > 1.0.1 to take over this new code. + Involved files: src/plugin/coopthreads/* + - Ported most of the src/commands/* DOS support programs to + coopthreads, which are: bootoff/on, cmdline, dosdbg, ecpuoff/on, + eject, emumouse, exitemu, lredir, speed, system, uchdir, ugetcwd, + unix, vgaoff/on, xmode. Involved files: src/plugin/commands/* + - Wrote (from scratch) a DOSEMU built-in COMMAND.COM using coopthreads. + Involved file: src/plugin/comcom.com ;-) + This COMCOM only needs 2K lowmem per instance (plus env, of course) + and claims to be as compatible as reasonable, but some + incompatibilities still exist (though I don't think they hurt). + The following DOS internal commands are implemented: + cd, chdir, goto, echo, @echo, pause, rem, if, for, shift, call, + type, del, erase, set, path, prompt, break, mkdir, md, rmdir, rd, + verify, ver, ren, date, time, dir. + "copy" and "choice" are not implemented, because those exist as + external freedos commands. + The following DOSEMU support programs are being invoked internally + (if not present in the PATH), so they are always available: + exitemu, speed, bootoff, booton, ecpuoff, ecpuon, eject, emumouse, + ugetcwd, vgaoff, vgaon. + The following ones eat additional lowmem heap, so its better to + leave them external: lredir, unix, dosdbg, xmode, system, uchdir. + Of course COMCOM has a builtin commandline editor with history + buffer, else who of us would like to work the stupid old DOS way;-) + - updated README.bindist and src/docs/README/config to reflect the + latest changes + - recompiled docs +2001/10/28 ver. 1.1.1.6 unofficial pre-release + From Stas Sergeev + - fix for emusys/emubat/emuini file extension substitution not + working correctly for redirector/PC-DOS (modified by Hans). + Note: the DCONFIG.SYS part is disabled because it can't be handled + consistently in fatfs.c. Involved files: utilities.[ch], async/int.c + - fix for DPMI: run_dpmi() must not be called from timer_int_engine() + (just delay ints until the timer routine is finished) + From Bart + - small fix for mfs to get DRDOS use 'con' without error + - vgaemu_put_pixel patch (int 10h, function 0ch and 0dh) + - temp fix for mouse in applications, which do 'mousing' only + by reading mickeys and drawing their own cursors (WP Office): + In mouse.c, 'mouse_setpos()' disabled for X without mouse grab. + From Hans + - ./mknewyear ;-) + - fixed typo in global.conf, $__debug --> $_debug + - fixed bug in mfs for special devices given as full path by + by FDkernel. Involved files: src/dosext/mfs/{mfs,mangle}.c + - fixed small bug in mappingdriver detection (mapping.c) + - fixed src/base/misc/fatfs.c so 'emusys' and 'emubat' are handled + (emuini isn't needed here, because at that time we are lredir'ed) + - reduced signon banner to be less verbose + (+292 ./src/base/async/int.c) + - implemented A20 handling in 8042 emulation, needed for WinME + involved file: src/base/keyboard/serv_8042.c + - removed binary stuff, we have it now in dosemu--bin.tgz + or drop it. Involved files/directories: + dexe, etc/{config.test,hddummy}, setup-bootdir, setup-hdimage, + first-test, dosemu.spec, src/doc/README/{dexe,oldboot}, + src/tools/{mkbindist,Mkbindist,QuickStart.bindist}, + contrib/dosC, src/commands/precompiled/*.{com,exe,sys,doc} + - adapted the Makefile to reflect the binary remove + - the commands dir now is only compiled on demand, target: dosbin + - adapted the docs to reflect the binary remove + - added a short description about the binary distribution to + doc/README.txt, Quickstart and a larger one in README.bindist + - adapted Quickstart +2001/10/28 ver. 1.1.1.5 unofficial pre-release + From Hans + - fixed typo in emm.c, error code wrongly went into BH instead AH + - changed forgotten vger.rutgers.edu to vger.kernel.org in + async/int.c + - introduced --Fusers option to bypass /etc/dosemu.users in case + the binary is _not_ suid-root. This allows to distribute isolated + packages (other then DEXE), which have their completely own + configuration. + - introduced arg-list preparsing. As some options are dangerous + when parsed too late, those are done in the special function + secure_option_preparse(), which discards all options known + to be dangerous from the arg list (currently only --Fusers). +2001/10/28 ver. 1.1.1.4 unofficial pre-release + From Herbert Xu + - fixed typo in mfs mangling, src/dosext/mfs/util.c + - changed /dev/cuaX to /dev/ttySX in etc/dosemu.conf + - Fixed incorrect partition type sensing (setup-{hdimage,bootdir}) + - glibc-2.2.2 needs (additionally) included in dosdebug.c + From DJ Delorie + - fixed DPMI memory available (dpmi.c) + From Bernd Schueler + - allow file names, which have a DOS reserved basename together + with an extension (e.g. LPTx.PRN as used by CLIPPER programs + for internal spooling purposes). Involved file: mangle.c + From Rob Clark + - fix for mfs.c and mangle.c to proper detect reserved DOS file names, + even if prefixed by full pathes. (patch rework by Hans) + From Bart + - lpt.c printer security patch + - lredir enhancement to "reredirect a drive', such that an + existing redirection is replaced with the new one instead of + returning an error. + - adapt dosnet.c to linux 2.4.x + - fix for internal mouse driver on the console in graphics mode: + sometimes the mouse cursor left a trace behind. Involved file: + src/base/mouse/gcursor.c + From Hans + - removed obsolete (old parser style) etc/config.dist + - fixed dexe/check-mtools to detect version correctly + - adapted dexe/myxcopy to newer mtools (modified fix from Herbert Xu) + - fixed compiler warning in keyb_X_keycode.c (unused variable), + which stemms from the Xu caps/num lock fix + - 'tuned' ./rebuild for my convenience;-) + - removed suid-bit from default install, involved file: + src/arch/linux/Makefile.main + - recompiled lredir.exe due to Bart's "reredirect" patch. +2001/02/25 ver. 1.1.1.3 unofficial pre-release + From Adam J. Richter + - some glibc-2.2.2 fix (to include time.h instead of sys/time.h) + involved file: mfs.c + From Alberto + - fixes for gcc >= 2.96 (the '##' macro thing) + involved files: data.c, msdos.h, cpu.h, mhpdbg.h + - other warning fixes related to gcc >= 2.96 + involved files: pci_bios.c, pic.c, lexer.l, memcheck.c, parser.y, + keymaps.c, prestroke.c, fossil.c, dpmi.c, xms.c, detach.c, remap.c, + screen.c, port.h, dexeconfig.c + From Hans + - fix to gas detection, file '--' was generated instead of + just outputting to stdout. Using 'dummy' and deleting later. + (gas doesn't accept stdout; /dev/null won't work as gas will make + a regular file of it when running as root) + - general 'linux' header rework (this could no longer be avoided) + because we can't rely any longer on proper headers in the system. + - changed all and to "Linux/*" and "Asm/*" + in order to make obvious, that we are including private headers + - included standard headers from instead of + where ever possible possible + - the rest of headers is made 'privat', copied from a linux-2.0.38 + tree and cut down to our needs + - v-net/dosnet.c still contains references to kernel header files, + because this is a kernel module. Compile this one with care + and verify that /usr/include/linux is in fact a symlink to + the kernel source. + - made generation of GLIBC_VERSION_CODE (hopefully) foolproof ;-) +2001/02/09 ver. 1.1.1.2 unofficial pre-release + From Bart + - Converted all as86 code to gas using .code16 + Now we just need binutils > 2.9.1.0.25 and now as86 anymore + From Hans + - made mkpluginhooks restoring the previous status of plugin_enable + if plugin_override took effect and the overriding plugin is + disabled afterwards + - plugin_override now can take a list of overridable plugins. + - updated README.plugin + - better read_file in bisonpp.pl (never stop learning perl;-) + - some further work on Bart's gas stuff to make it better + portable between different binutils and removed warnings + for older binutils. +2000/12/02 ver. 1.1.1.1 unofficial pre-release + From Bart + - fix for base-configure.in w.r.t. glibc 2.2: the awk script got + confused between the definitions + From Eric + - remove/disable keyb-nonint mode (eb1.4) + - Modified the parser&lexer to handle shift-alt, ctrl, and ctrl-alt + shiftstates. (eb1.11) + - Now pass the raw scancode to get_bios_key, this is needed for + correct handling of a few rare cases in keyboard_u stuff. + - fix for linux-2.4.0-test11 "features" of /proc/cpuinfo + From Hans + (in preparation for Eric's keyboard stuff) + - removed USE_OLD_SLANG_KBD + - added new features to plugin handling (mkpluginhooks) + - config/plugin_override can contain name of plugin which _this_ + plugin replaces (plugin_enable of the other will be set to 'no') + - config/plugin_incdirs (same structure as plugin_dirs) will + add directories to the include path. + - config/plugin_config.h is included by src/include/config.h + to give plugins a chance for compile time configuration + - added bison/lexer preprocessor bisonpp.pl (yes, it needs perl) + to add generic hooks into the parser. Moved parser.y + parser.y.in and lexer.l to lexer.l.in. + mkpluginhooks will generate the proper parser.y and lexer.l + using bisonpp.pl and merge files found in + config/plugin_{parser,lexer}. + - updated src/plugin/README.plugin to reflect the changes + - added parser stuff to plugin/demo + - added VERSION_OF and IS_DEVEL_RELEASE macro to configure to ease + checking of DOSEMU_VERSION_CODE such as + #if DOSEMU_VERSION_CODE < VERSION_OF(1,1,0,5) + #if IS_DEVEL_RELEASE + - fixed bug in base-configure not parsing the version numbers + correctly (kludge failed when VERSION was equal PATCHLEVEL_, + e.g. 1.x.x.1, 2.x.x.2 e.t.c) + - moved/changed src/base/keyboard to become src/plugin/keyboard + involved file: src/base/keyboard/*, src/include/key*, + src/arch/linux/Makfile.main, src/plugin/keyboard/config/* + src/env/video/terminal.[ch] + - parser.y, shifted outcommented rules down to avoid comments + within rule label such as 'term_flag /*...*/ :' + (problems with bison preprocessor) + - prepared parser keyboard stuff for Eric's stuff (eb1.11) + Some parser stuff went into src/plugin/keyboard/config/plugin_parser + and plugin_lexer respectively +2000/11/10 ver. 1.1.1 released + From Urban Widmark + - support for mice using the IMPS/2 protocol + (dosemu.conf key word 'imps2') + From Alberto + - cpuemu stuff: keyboard was broken with some programs; fixed + flags handling when entering e_vm86. + - ecpuoff: LDT was deallocated but not zeroed: fixed. + - memory write protection routines moved to new file memory.c; + a bitmap keeps track of the protected pages. Removed previous + hack which used /proc/self/maps. + - runtime checksum (again) on compiled blocks. + Every time a write fault hits a page, the code in that page is + now no more unconditionally deleted; instead, it is marked as + "suspect". When looking for a node, if a "suspect" one is found + a checksum is performed on it and, if nothing has changed, the + "suspect" flag is removed. Doing a checksum on a few bytes is + faster than removing a node and MUCH faster than reparsing it. + - still more macros to speed up compilation + - and that's all for 1.1.1, I would say. + - bug fixes: memory write protection and "suspect" node handling + From Hans + - removed never used USE_INT_QUEUE stuff in order to avoid + future problems with do_call_back(). + - fixed Changelog entry of 1.1.0.2 (Eric's patches were hidden + in Alberto ones) +2000/10/15 ver. 1.1.0.5 unofficial pre-release + From Alberto + - cpuemu stuff: continued moving code generator sequences into macros, + and making speed improvements to the code generator + - made jump optimizations dynamic in an attempt to solve some + self-modifying code sequences (ndiags) + - currently compiled sequence, not yet in the tree, was not + invalidated by write faults; fixed. + - compiled jumps out of sequences can now go also backwards + - added missing decode in vgaemu stuff (bgidemo) + From Hans + - fixed my own stupidness from 1.1.0.4 in base-configure :-( +2000/10/14 ver. 1.1.0.4 unofficial pre-release + From Bart + - svgalib support, needs svgalib >= 1.4.2, for switching + between VCs (saving/restoring of card status) when running + console graphics. + From Hans + - made Bart's svgalib stuff configurable via compiletime-settings + - renamed vga_screenon(), vga_screenoff(), mouse_close(), + mouse_init(), vga_setpalvec(), vga_getpalvec() to dosemu_xxxx(). + This was needed due to name clashes with svgalib. Though you + get some (maybe) correct linkage with shared libs, linkage + abandoned if linking static. + - fixed compiler warning from 1.1.0.1 in keyb_X_keycode.c + (unused variable i) +2000/10/13 ver. 1.1.0.3 unofficial pre-release + From Alberto + - cpuemu stuff: started moving some often used code generator + sequences into macros + - ecpuoff did not restore memory protections and did not + reinitialise the cpuemu; corrected. + - new cached search in the node tree; every node + now stores information about the next one in execution order. + This drastically cuts down search time. + - several speed improvements to the code generator + - src/base/keyboard/serv_xlat.c: missing spaces in a case range, + gcc 2.96 will not like it. +2000/10/06 ver. 1.1.0.2 unofficial pre-release + From Alberto + - cpuemu stuff: reworked the tree structures to use an AVL + tree (with code from libavl-1.4.0 (C) 1998,1999 FSF) + - self-modifying code is now handled via segfaults on + write-protected memory pages. Not perfect but faster than + checksumming or rebuilding a whole tree. Every time code + is generated, it protects its own page; every time we get + a fault, all code on that page is marked as dirty and + the page is unprotected. + - memory overwrites coming from disk also remove any nodes from + the tree over the affected pages. There is currently only + one hook in mfs.c to do that, hoping that there aren't other + ways to overwrite memory... + - added fault handling in cpuemu for div by zero, page and I/O + faults. + - hooks for VGAemu to make the whole thing working under X, + replacing Bart's instremu. + - rewritten comments in code generator in a more consistent + way (gcc-like) + - fixed bugs in POP SP and STOSb instructions + - moved some defines from dpmi.c to dpmi.h for use by cpuemu + From Eric + - Simplified dosemu_debug.h so changes don't need to change as many + lines. + - show_regs in dump.c could segfault (it now avoids bad addresses) + From Hans + - small fixes to compile simx86 on other than Alberto's systems;) + - changed 'sigcontext' to 'sigcontext_struct' in simx86/cpu-emu.c +2000/10/05 ver. 1.1.0.1 unofficial pre-release + From Herbert Xu + - fix for not working caps/num lock with X_keycode + (src/base/keyboard/keyb_X_keycode.c) + From Bart + - added vbioscheck program to get proper $_vbios_seg,$_vbios_size + (src/tools/periph/vbioscheck.c) + - added builtin vbios autodetection to init/parser.y + Any non legal value (e.g. 0) to $_vbios_s* forces autotetection + (patch slightly changed by Hans) + From Hans + - forbidd redistribution of re-packaged DOSEMU distribution + (involved file: COPYING) + Reason: checking the integrity of a DOSEMU distribution must + be possible without (re-)download simply by comparing the + md5sum published at dosemu.org. + - fixed bug for compiling static under glibc: PORTABLE_BINARY + had no effect (Makefile.conf.in, base-configure*) + - added printing kernel version on runtime option -h + - fixed printing of mapping driver type on runtime option -h + - fixed wrong behave, when a not working mapping driver was + configured (defaulted to mapfile always). +2000/07/09 ver. 1.1.0 released, starting new development tree + From Alberto + - new cpuemu stuff: new name: simx86 + (for more details see preliminary src/docs/README/simx86) + - ZeroBaseTime is now in cycles instead of us + - SIGTIME has been un-macroed to its standard value of SIGALRM + - SIGPROF to be reserved/used in the future for cpuemu + From Hans + - small fixes to compile simx86 on other than Alberto's systems;) + - moved changes from config.in to acconfig.h, where they belong. + and rerun ./mkconfscript. + - put missing '#ifdef X86_EMULATOR' around '#include "cpu-emu.h"' + - added macro offsetof() in emu-i386/simx86/syncpu.h + - changed 'sigcontext' to 'sigcontext_struct' in simx86/cpu-emu.c + - adapted dosemu.spec +2000/07/08 ver. 1.0.1.1 unofficial pre-release, development fork + From Hans + - added missing parts of 1.0.0.2 vmodem (doors) patch. + (somehow did patch in a broken way at that time) +2000/07/03 ver. 1.0.1 released + From Hans + - fixed a diabolic bug in mapping/mapfile.c (s/\|/,/) :-( + - made sure we realy have root-only access on the mapfile when + running suid root. On NFS mounted $HOME we will come into trouble, + when we have no root_squash. For security reasons we cannot + allow suid-root DOSEMUs publish its mapped executable space + on a NFS mounted volume, that isn't trusted enough to have + root_squash. + - removed contrib/twin (forgotten in 1.0.0) + - updated dosemu.spec for 1.0.1 +2000/07/02 ver. 1.0.0.11 unofficial pre-release + From Alistair + - new doc stuff, now using DocBook. For good results you need + atleast OpenJade-1.3, SGMLtools-2.0.2, HTML Stylesheets 1.27 + Lynx >= 2.8 + - feed in delayed docs from Eric and Bart + From Hans + - docs recompile + - adapted setup-dosemu's help to reflect the new docs format +2000/06/17 ver. 1.0.0.10 unofficial pre-release + From Eric + - changed Mailaddress (but not in src/doc/*, Alistair will do) + - changes to src/commands/emumouse.c (new options -Mx -My) + From Eric, Alistair & Hans + - fixed global.conf to take care of GNU sh-utils > 1.16 + ( default behaviour of 'who' isn't backward compatible anymore) + From TWISTI (what ever real name he/she has ;-) + - changes to base-configure.in to reflect glibc-2.2 changes + From Alistair + - fixed DEXE for newer FD kernels, that have no IPL.SYS + From Alistair & Hans + - prepare src/base/misc/disks.c for linux-2.4.x + (BLKGETSIZE no long defineable via ) + From Hans + - cleaned up run_vm86() loop for do_call_back, now the same + as the main loop. +2000/06/05 ver. 1.0.0.9 unofficial pre-release + From Bart + - patch to make SiS chipset card run on console + - cleaned up bankswitching in env/video/vga.c + - fix for mouse.c, save/restore bug + - fix for mouse.c (SimCity virtual resolution of 2560x1920) + no restricting the virtual mins and maxes. + From Hans + - made FD kernel 1.1.20 (Build 2020) bootable via the bootdir stuff + (just needs kernel.sys within the boot directory, bootsector + is generated on-the-fly) + BTW: still needing Pat's command.com, the FD one still is buggy and + doesn't allow you write/create files within the redirected drive(s). + - removed forgotten '#if 1' for Bart's XWarpPointer improvement;-) +2000/06/01 ver. 1.0.0.8 unofficial pre-release + From Eric (with hints from Bart) + - mouse fixes along latest xmouse dicussions + - hide the mouse in X when the mouse grab is active. + - made set_curpos work for X + From Bart + - (hopefully) last fixes/enhancements to PL4 stuff + - updated DANG and comments in the source (no src/docs/* yet) + - mouse: trying to minimize the amount of XWarpPointer calls. + From Hans + - fixed above mouse changes, which broke 'autohiding' the X cursor + while DOSapps drawing the cursor itself in graphics (win31,fs5...) + (broke old and new stuff introduced in 1.0.0.4). + - removed 'X_mouse_cursor' (hand), this irritates more than + it serves. We aren't 'DOS compatible' here anyway, so stick + on 'X_standard_cursor' (arrow). + - set COUNT to 150 in instremu.c, 1000 was way to high for + 'animated' graphics ;-) +2000/05/27 ver. 1.0.0.7 unofficial pre-release + From Eric + - patch to limit PL4 emuluation via signals happening instead + of MASTERCOUNT + - support for setting the vertical & horizontal display resolution + for PL4 modes + From Bart + - further speed improvement for instremu.c + - fixed a few bugs: zero/parity/sign flag updates with shift, + some string insruction bugs. + From Hans + - fixed Bart's 'less portable' asm inlines ;-) + (wasn't compilable with < gcc-2.9x) +2000/05/26 ver. 1.0.0.6 unofficial pre-release + From Eric + - patch to src/env/video/remap.c to make bpp24 work + From Hans + - freezing dosemu completely when on console and switched + away while still handling async stuff (e.g. handle signals). + involved files: dpmi.c, do_vm86.c, cputime.c, vc.c, timers.h +2000/05/25 ver. 1.0.0.5 unofficial pre-release + From Bart + - fix to do_vm86.c (vm86_GP_fault()) to simulate INT codes correctly + - make 32-bit code run in instremu.c + From Hans + - re-activated mouse snapping in X.c (no dependency on PL4 modes) +2000/05/23 ver. 1.0.0.4 unofficial pre-release + From Bart + - further work on PL4 stuff + - speeded up Logical_VGA_Write/Read + - font correction + - fixes for 5 bugs in instremu.c + From Steffen + - Mode X fixes (with hints from Eric) + - remap.c fixes + From Hans + - 'autohiding' of mouse cursor und X graphics mode + (relying on the application calling INT33 function 1, + when it doesn't display the cursor itself) +2000/05/19 ver. 1.0.0.3 unofficial pre-release + From David Pinson + From David Hindman + From Bart Oldeman's + From Steffen + - joint effort to make vga PL4/Pl2/Pl1 mode work under X + (This stuff really works, good job guys ;-) + Though this changlog entry is small, the patch is big + and there need not any special usage description, just + run Borlands bgidemo under X. +2000/05/19 ver. 1.0.0.2 unofficial pre-release + From Julia A. Case + - virtual modem support (socalled vmodem or "doors" patch) + See src/doc/README/vmodem for Details (no Alistair, didn't + fiddle with sgml yet;-) + From Steffen + - fix to dosdebug wrongly taking the mafile as pidfile in + ~/.dosemu/run + From Stas Sergeev + - further fix to dosdebug printing a 'smiley' (0x1) and waiting for + the next command instead of terminating on 'kill'. + From Hans + - further kludge for mapself mappingdrive (/proc/self/mem) +2000/03/19 ver. 1.0.0.1 unofficial pre-release + From Alberto (...and Hans) + - fixed gcc-2.95 miscompile (optimized away our code templates + in env/video/remap.c) + - fixed bug in env/video/matrox.c (introduced by mapping rework) + From Hans + - made sure we pass a NULL pointer to mmap() when not mapping fixed. + Though it doesn't hurt to pass -1, the kernel may need some + additional work while searching for a free address. + - Put '#ifndef' around definitions of PAGE_SIZE, reason: + glibc-2.1 is defining PAGE_SIZE in . + - fixed base/init/memcheck.c, which uses the term 'PAGE_SIZE' + with a value of 1024 while actually meaning granularity. + Now we use GRAN_SIZE for this. + - fixed problem with libslang and glibc-2.1 not exporting _IO_std*, + when compiling static and 'slangforce on'. + Now linkable in libc5 as well as glibc (recompiled slang-1.2.2 on + libc5 with patch in contrib/slang/slang-1.2.2-dosemu.patch) + - better comment on 'REQUIREMENTS for DOSEMU' in Makefile.main +2000/03/05 ver. 1.0.0 released ... tadaaa + After a long time (8 years), and many many headache DOSEMU finaly + leaves ALPHA/BETA stage and is ready for production. We could not + realize all wishes, but the stuff which is in should be as stable + as it can be with DOS in the background :-) + Thanks to all people who helped to get this baby on its way, + especially to all the former co-ordinators befor me: + - Matthias Lautner, started the DOSEMU project 1992 + - Robert Sanders, took over government 1993 + - James B. MacLean, who was the longest staying on + this project: 1994 .. 1996 + Since 1997 being the co-ordinator of this exiting project, + I'm happy that it finally reaches its aim. + -- Hans Lermen, 2000/03/05 + From Hans + - removed experimental cpu-emu stuff + - adapted configure stuff +2000/03/01 ver. 0.99.14 FORK-POINT released + NOTE: This is the 1.0/1.1 forkpoint + 1.1 will continue unchanged to form the new Hacker version. + 1.0, the stable version, will be 1.1 with the experimental + stuff removed. + From Alistair + - some fixing soundpatches (mainly DMA stuff, as of 0.98) + From Hans + - wrote new setup-bootdir (should replace setup-hdimage) + (taken from 0.98) + - udated src/tools/mkbindist (taken from 0.98) + - removed obsolete old vm86 interface stuff and the configuration + option 'novm86plus'. Also deleted src/doc/README/vm86plus. + - checked and fixed some compiler warnings + - docs: removed (broken and/or obsolete) video ibmset loglevel + - docs: SECURITY, unix.com and system.com are no longer dangerous + - docs: X, removed obsolete 'todo' list. + - docs: commands, updated to reflect current state + - docs: config, added description of the new mapping stuff + - docs: dexe, binfmt_misc now is in every kernel. + - docs: priv, fixed formatting error at section start + - docs: configuration.sgml, TclTk is the only 'dialog' tool + currently. + - docs: DANG, removed threads and DANG recompile + - docs: removed doc/Known-Bugs (was state 0.53pl58). + We _have_ still bugs, but not those mentioned ;-) + - docs: removed man/xtermdos.1 and updated dos.1 (xtermdos is broken, + docs removed but script kept for compatibility) + - docs: added help entry for $_mapping in runtime_setup.help.patterns + - did docs recompile + - did run mknewyear to update copyright header +2000/02/28 ver. 0.99.13.3 unofficial pre-release + From Hans + - removed threads code (definitively), because no one made real + use of it and we would have to adapt to kernel changes anyway. + (its not worth this work) + - removed NetBSD code (definitively), because it was _totally_ + broken since long time and no one of the NetBSD people bothered + to fix it (now we have a cleaner source and more space). + - prepared some docs for upcoming 1.0 release: + doc/announce, dosemu.spec, dosemu-HOWTO.sgml, README/header*, + dos.1, precompiled/autoexec.bat, +2000/02/26 ver. 0.99.13.2 unofficial pre-release + From Hans + - fixed bug in close_mapping() giving SIGNAL11 on -h option + (thanks to Eric, who pointed it out) + - fixed DPMImallocFixed for the mapself mapping driver driver. + - disabled DPMImallocFixed (dpmi/memory.c) for the mapfile and + mapshm mapping drivers because it makes trouble. + As a DPMI host is not forced to fullfill a function 0x504 request + for a fixed mapping, this seems not to hurt, atleast with the apps + I tested. + - variable order in which the mapping drivers will probe. + It turns out, that binaries produced by gcc > 2.7.x and/or those + linked against glibc are prone to break our /proc/self/mem kludge + while gcc-2.7.2/libc5 binaries work fine in all cases. + For this reason, depending on GCC_VERSION_CODE/GLIBC_VERSION_CODE, + the mapfile or the mapself driver is prefered. + - new dosemu.conf variable: $_mapping, which hold the prefered + mapping driver (dosemu.conf, global.conf) + - updated setup/* to reflect $_mapping + (setup/parser/parser.y, setup/runtime_setup-new.menu) + - added -fno-strict-aliasing in case of gcc-2.95 + (base-configure.in) + - (finally) changed /dev/cua to /dev/ttyS (base/serial/ser_init.c) + - change Larry's address to jlarry@delanet.com + - dosemu-HOWTO.sgml: changed all 'blabla' to `blabla' to avoid + broken formatting (however, no docs recompile yet). + - added 'liability disclaimer' prompt to avoid problems with + laws in some countries. This was pointed out by a british + journalist: "The problem arises from tort, or the ability the sue + someone for negligence. Such liability can be overcome by making + it clear that the software is used at the users own risk... + ...The problem with this for Linux packaging systems is that the + user must be warned of this disclaimer before they install the + software." + As I could verify, the journalist is right, so we print the + disclaimer, ask for confirmation and save the disclaimer + (including the confirmation) to ~/.dosemu/disclaimer. If that + file exists, we won't prompt any more. + From Eric and Michael (both found the same bug synchronously;-) + - fix for mmap_mapping(MAPPING_SHM) in mapself.c not turning + on privs before shmat(). + From Arne + - fix bug in DPMI when using a mov sreg,reg in 32-bit operand mode + (0x40 as sreg doesn't matter in 16 bit, but does in 32 bit, now + the test is correct) + - fix typo in DPMI (FS was popped instad of GS) + From Andris Pavenis + - GNU Pascal (DJGPP port) needs a higher number of DPMI clients, + DPMI_MAX_CLIENTS set to 32 + From Antonio + - addition to user_hook 'lredir' request to get the linux 'mountpoint' + of a DOS drive redirection. + From Frank + - fixes to internal packet driver, involved files: + bios.S, pktnew.c (good spotting, --Hans) +2000/02/11 ver. 0.99.13.1 unofficial pre-release + From Hans + - added INT15,AX=e801 support (get extended mem >64Mb) + - Reworked the mapping stuff completely in order to make DOSEMU + run on kernels >=2.3.27 (mmap(/proc/self/mem) has gone). + - Implemented a generic mapping driver interface, which autodetects + the needed mapping strategie. arch/linux/mapping/* and + include/mapping.h + - wrote three drivers to support the following startegie: + - /proc/self/mem and IPC shm, exactly same behave as before. + - using _one_ big IPC shm segment and mremap(addr, 0 ...) + (Linus agreed on keeping shmat()+mremap(,0,..) functionality) + - using file mapping (no IPC at all), if else fails. + - These drivers also fix an existing (not yet known) security + problem when running suid as user (_any_ user could read DOSEMU's + IPC shm areas, IPC perms where set to 0755). + - changed all instances of any kind of mapping (mmap,shmat) to + use the above driver interface. Involved files are: + base/misc/{dosio,shared,init}.c, dosext/misc/emm.c, + dosext/dpmi/memory.c, + env/video/{video,vc,hgc,dualmon,vgaemu,matrox}.c + - Disabled or deleted mapping stuff, that we in principal + do not need any further. Involved files: signal.c,dosio.c, + shared.c,ioctl.c,dos.c,dpmi/memory.c,emm.c,vc.c + - Introduced a new debug flag (Q) for the mapping interface. + put a comment in on how to add further flags;-) + - New parser command to specify a discrete mapping driver. + Usage: dos ... -I 'mappingdriver mapfile'. Known keywords for + drivers are: mapself, mapshm, mapfile. + - updated man/dos.1 to reflect -D+Q (debugging the mapping stuff) +99/07/09 ver. 0.99.13 released + From Egbert Eich + - implemented PCI-BIOS support (PCI Cfg Type 1 + 2) + All functions are implemented except 'special cycle support', + 'get irq routing options' and 'set pci irq' (don't believe the + missing ones are important for DOSEMU;-). + ( good work, Egbert! --Hans) + From Hans + - changed man/dos.1 to reflect the new -D+Z (PCI) flag. + - DANG recompile + - docs recompile +99/07/07 ver. 0.99.12.3 unofficial pre-release + From Alberto + - dosdebug.c: Add segment to XLAT. Correct order of src/dest for + SHLD/SHRD. + - several assembler fixes (warnings from newest as in binutils 2.9.4). + The first one in vesabios_pm.S was probably a bug too. + - cpuemu bugfixes: SLHD,SHRD were wrong in 16-bit modes. Also jumps + in REP from/to 16_16/32_32 could cause an infinite loop. Adjust + reserved bits in flag word in the case of PUSH. +99/07/03 ver. 0.99.12.2 unofficial pre-release + From Steffen + - workaround for egcs bug (at least up to egcs-2.91.66). + - bootdir stuff abandoning 'lredir', when running under DosC + (can't redirect anyway) + - adapted bootdir for PC/DR/NOVELL-DOS + - allow wildcards (e.g. $_hdimage = "drives/*") in global.conf + Look at src/doc/README/config for details. + - implemented autotetection of used keyboard layout + This involves 2 parts: + 1. $_X_keycode = (auto) + 2. $_layout = "auto" (independent from (1.) + For details see src/doc/README/config. + - adapted setup-dosemu to support keyboard 'auto' + - fixed an 'access bug' in X.c. XOpenDisplay uses access(2) to + check the rights of ~/.Xauthority and access(2) uses the + _real_ uid, hence we need to do enter_priv_on(). Doing this + is no security whole, because access(2) is suid-root aware + and XOpenDisplay doesn't open any other files then ~/.Xauthority. + ... and, if you want a secure DOSEMU you must not run it + suid-root anyway. + From Steffen and Benjamin C. W. Sittler + - work around Linux-2.2.x's NFS locking problems + (in fact ignoring locks on NFS) + From Alberto + - cmos 'BCD' fix (involved files src/base/dev/misc/{cmos,rtc}.c) + From Hans + - fix for multiple IRQs not going through in global.conf + - docs for 'auto' and 'drives/*' as Steffen was too lazy ;-) + - ./first-test now is executing a bootdir, changes in + arch/linux/Makefile.main, tools/periph/Makefile, etc/config.test + No etc/hdimage.test generated anymore. + - fixed '$_vbootfloppy / bootoff' not working (errm, global.conf + used the wrong parser command to set this). + - the 'bootoff' fix leaded to fix an other longstanding bug: + there now is no collision with defining vboot together with + more then one floppy (of course), hence the check is moved + out of the vboot part. + - removed the restriction to define $_hdimage when $_vbootfloppy + was given (was an older security restriction, which now is + obsolete. Atleast this is what I think it was for;-). + - additional note: when $_vbootfloppy is defined, bootA is + default (can be overridden by -C commandline option) else + bootC is default (can be overridden by -A and a real floppy). +99/07/01 ver. 0.99.12.1 unofficial pre-release + From Hans + - first steps to DOSEMU<->DosC cooperation code + - INTe6 helper 0xdc used for DosC (DOS_HELPER_DOSC) + - DOSEMU is now aware if running DosC and gets its version + to check for compatibility. + - DosC now is reporting, when it runs under DOSEMU + - fixed bug in DosC (wrong pointer declaration (SDA)) + - fixed bug in DosC (wrong handling of INT21, AH=30) + - hook in DosC (kernel/inthndlr.c) to support INT21,AH=0x5f, + but just to catch it in DOSEMU and print a debug log message. + (will only work after the redirector is implemented in DosC) + - workaround in MFS to fake DOS-4.1 SDA data layout, when Dosc + is running (DosC has 4.1 SDA data layout, but reports DOS-3.1) + - changed lredir.exe such that it prints a message and exits + when DosC isn't capable of redirection + - changed lredir.exe to print DOS errors in clear text. + - new contrib/dosC/dist/kernel.exe, patch relativ to dosc10b2 + put into contrib/dosC/dosc10b2-1.patch and + contrib/dosC/readme.dosemu updated. +99/05/22 ver. 0.99.12 released + From David Hodges + - reworked dosemu HOWTO + From Hans + - changed Steffen's E-mail address in all sources + - DANG recompile + - docs recompile +99/05/21 ver. 0.99.11.2 unofficial pre-release + From Alberto + - Make several int15 functions that deal with protected mode + dependent on config.dpmi. int.c (INT15) + - avoid reentrancy in log_printf (coming from async signals) + - removed 'void*' from address comparison in mouse.c + - print 'secure off' warning to DOS console instead to Dprintf() + (writing DOS console additionally to the logs), dpmi.c + - return to dosemu_code dependent on IF in dpmi_eflags; + this was the original way. Assuming that 'it helps returning + anyway' implies 'in winos2', added test to do it. I have seen no + side effects, and this helps the mouse under cpuemu. + - make leavedos really leave also when called recursively. + - lot of cpuemu changes, involved files: base/async/int.c, + emu-i386/intp32/{cpu-emu.c,emu-ldt.c,Makefile} + emu-i386/intp32/twin/intp32/{emu-utils.c,fp87.c,hsw_interp.h, + internal.h,interp_16_32.c,interp_32_16.c,interp_32_32.c, + interp_main.c,interp_modrm.c}, emu.c, include/cpu-emu.h + - some typo/syntax fixes (pic/pic.c, dpmi/dpmi.h, do_vm86.c + tools/tools86.c) +99/05/21 ver. 0.99.11.1 unofficial pre-release + From Alberto + - fixed bug in mouse.c checking linaddr instaed of DOSish address + (run_pm_int never was called). src/base/mouse/mouse.c + - Some missing 0x before hex constants in print_ldt() + - EMU_GLOBAL_VAR had no benefit, removed + - (again) some header fiddling for glibc (src/tools/periph/dosctrl.c) + - fixed bug in src/dosext/dpmi/msdos.h, which became obvious + after correction of INT15,AH=C0 (see patch from Jiuming Luo) + From Grant R. Guenther + - added -T debuglog trace for 'rep' IO (src/emu-i386/ports.c) + From Nick Duffek + - fix for mfs.c and INT21 function 3CH together with SHARE.EXE + succeeding though it should not (MS-DOS INT21/3CH would fail if + the file exists and is currently opened by another program). + The fix is: replace "open(O_TRUNC)" with + "open(); check sharing; ftruncate()" + From Dave Coffin + - y2k fix for CMOS byte 0x32 to report the correct century + ( src/base/dev/misc/{cmos,rtc}.c ) + From Michael Klein + - An other y2k bug fix (in mfs.c), though it will hurt us + only after 2012, we have to be correct. + ( I bet there will by some oldfashioned poeple like me + in 2012, who would complain otherwise --Hans ;-) + From Hans + - fixed bug in src/base/misc/utilities.c malloc'ing wrong size + and not checking for valid function pointer. + - fixed (again) a bug in the famous sigalarm_onoff() of + utilities.c (dos -A wasn't working). Hopefully this was the last + one :-( +99/04/22 ver. 0.99.11 released + From Alberto + - major cpuemu cleanup, bugfixes + From Hans + - docs recompile +99/04/21 ver. 0.99.10.3 unofficial pre-release + From Marcus (bug report from gvz@pop.de) + - fix (errm, workaround) for the 57 characters path limit in MFS + (buffer overflow in cds_record). + We block access to directories, that would exceed that limit. + Blocked directories are returned as zero-sized files; we do this + do prevent the user to create a directory with the same name. + (hack, hack ... but it should avoid crashes atleast;-) + From Wojtek Pilorz + - changed dosdebug such that it can (re)read a valid dosemu.map + (type 'rmapfile /anywhere/dosemu.map' at dosdebug prompt) + From Josef Pavlik + - added czech keyboard support: cz-qwerty, cz-qwertz, + both are CP852. +99/04/20 ver. 0.99.10.2 unofficial pre-release + From Josef Pavlik + - fix for MFS creating new files with wrong ownership (mfs.c) + From Wojtek Pilorz + - fix for dosnet having problems to insmod properly + (removed -DMODVERSIONS from src/dosext/net/v-net/Makefile) + From Cyril Slobin + - fix for MFS: under DOS (other then under Unix) renaming to an + existing file must result in ACCESS_DENIED (INT21,AH=56). + From Jiuming Luo + - fix for INT15,AH=C0 returning the status in the wrong register + (must be AH, not AL), src/base/async/int.c. + From Levente Csiszar + - creating directories in MFS did set the group readonly. + e.g. DOSEMU running as user on the group 'dosemu' would not be able + to work on these directories. Changed perms from 755 to 775 + From Kyle Fortin + - fix for mouse_event() not detecting a mouse handler when + CS:IP has CS or IP = 0. (src/base/mouse/mouse.c) + From Alistair + - remove USE_SLCURSES from setup/demudialog/Makefile + (this fixes the socalled 'submit-bug-report bug' ;-) + From Hans + - changed demudialog back to color mode +99/03/26 ver. 0.99.10.1 unofficial pre-release + From Hans + - further kludges to /proc/self/mem mmap kernel bug, making the + address space EMM_PAGE_SIZE big solve the problem in some + other special cases (/src/dosext/misc/emm.c). + - fix for hang on floppy access (was a bug in utilities.c, + sigalarm_onoff() ) + - another security fix: userhook opened with root privs :-( + (src/base/misc/userhook.c) + - introduced do_call_back(), which makes it possible to call + vm86 code from within the dosemu 32bit code and return to + that place afterwards. In volved files: bios.S, misc/ioctl.c + do_vm86.c, emu.h, memory.h. + - new plugin hook 'plugin_ioselect', which hooks into io_select + Updated src/plugin/demo to give an example on how to use it + an the do_call_back() function. + - new doshelper: DOS_HELPER_GET_TERM_TYPE. This allows dosemu aware + DOS applications to adapt to the terminal type (async/int.c, + doshelpers.h). Look at doshelpers.h for details. + - added plugin_poll, this hooks into run_vm86() and run_dpmi() + Involved files: src/plugin/{README.plugin,demo/*),do_vm86.c, + dpmi.c,mkpluginhooks + - close possible security implication: forbidding partition access + when $_hdimage set by .dosemurc _and_ running suid-root. + Those who need it (and have _realy_ thought about alternatives) + have to specify it in /etc/dosemu.conf. + - fixed bug in ./setup-hdimage not doing chmod/chown correctly + - took over doc changes from 0.98.6 (no doc compile yet) + - docs: hint for 2.2.x kernels & nfs mounts (option nolock needed) + (src/doc/README/lredir) +99/02/26 ver. 0.99.10 released + From Hans + - introducing 'plug-ins', though the name may not be exactly + appropriate;-) It means to insert whole trees of code under + the directory src/plugin and configuring them automatically + into DOSEMU at compiletime. Those trees could be distributed + separately, not necessary by DOSEMU itself. + For more details look at src/plugin/README.plugin +99/03/11 ver. 0.99.9.1 unofficial pre-release + From Dima + - fix for MFS (SEEK_FROM_EOF) assembling a wrong offset + From Alberto Olindo + - makeing numpad keys work for 'keystroke' + From Steffen + - fix for emulated FAT hidding some entries + - fix for emulated FAT not updateing the CDS correctly + From Marcus + - typo fixed in peripher/dosctrl.c + From Andris Pavenis + - fix for peripher/dosctrl.c (including bits/time.h for glibc2.1) + From Vladimir Kondratiev + - fix for peripher/dosctrl.c, including features.h to define __GLIBC__. + Now, we won the first price: 3 contributors for 4 lines of code;-) + From Vincent Labie + - fixed a whole bunch of typos/bugs in sigsegv.c,cpu-emu.c,emu-ldt.c + (_very_ small part of a 24806 lines patch, which (sorry) can't go + in as _one_ piece. Waiting for Vincent cutting it into handy + chunks. --Hans) + From Alberto + - typo fixed in userhooks.c + From Hans + - better check for 'being on console', involved files: global.conf + config.c, utilities.c, utilities.h. Function is_console() uses + ioctl() on a FD to detect wether it's a console. + This solves some problems when starting DOSEMU from an _other_ + terminal, but redirecting stdin/out to /dev/ttyX. + - now returning exitcode 123 on Ctrl+ALT+PgDn, this enables wrapper + scripts to detect this case and restart dosemu (speudo-reboot) + - new DOS_HELPER_GET_CPU_SPEED. + - fixed a typo in sigalarm_onoff() (utilities.c) +99/02/26 ver. 0.99.9 released + From Antonio & Steffen + - preparation for 'kdos' DOSEMU frontend (src/env/video/X.c) + Stay tuned for what will come soon ;-) + From bwallac1@san.rr.com (sorry, he didn't mail his name) + - fix bug when running dosemu in background and having to tty + (getting ENOTTY as opposed to expected EINVAL) + Involved file: src/base/keyboard/keyb_slang.c + From Hans + - korrected the -D+T such that it does (mostly) the same now as + under the (former) old ports stuff. As we handle port dispatching + differently, we needed to have a 'trace ports {...}' statement + to restrict logging to a given class of ports. This has the + advantage, that we now also can uses -D+T for software debugging. + (involved files ports.c, config.c, parser.y, lexer.l) + - changed all T_printf, that do not print 'IO-trace' information + to i_printf, which is the proper flag for IO debugging. + - disabled SHOW_TIME for production releases (utilities.c), this + is only usefull for development and otherwise breaks -D+T. + - fix bug in src/tools/mkbindist substituting $variables instead + of taking them verbatim. (tricky bash-ism;-) + - for now disabling sigalarm_onoff() in uhook_config(), is freezing + dos for some seconds. We have to find an other workaraound:-( +99/02/25 ver. 0.99.8.2 unofficial pre-release + From Steffen + - for userhook: CancelDiskRedirection (disk.c) + From Antonio Larrosa + - included '' in dosctrl when glibc used. + From Hans + - built CancelDiskRedirection into userhook.c, such that now + also 'lredir del d:' works from 'outside'. + - fixed double // in uhook_lredir. + - added 'config' uhook command (changes in init/config.c, userhook.c) + This prints the contents of the config.* structure. + - added 'SYN: command' to be outputed before any uhook command + in case 'ack on' is set. This makes handshaking easier. + ( Needed by Antonio to better parse the results ) + - fixed reading from the uhook inpipe, more than one command + in the buffer was ignored. + - fixed strange sig11 while in leavedos(), the bad guy was + run_unix_command() which was used to do 'rm -rf' on exit + while the assumed context was already destroyed :-( + Using privdrop() and system() now because we don't need to regain + root privs here anymore. + - moved sigalarm_onoff() from disks.c to utilities.c + (needing it elsewere too). +99/02/21 ver. 0.99.8.1 unofficial pre-release + From Steffen + - some fixes to the emulated FAT stuff (fatfs.c, disks.c) + From Hans + - fix for bug introduced in 0.99.6.1 which breaks INT10,AX=1130 + (trashing BP in bios.S). Using ESP instead to access the stack. + - introducing 'user hooks', which supply a facility to control + DOSEMU (via -U command line option) from a foreign process, + e.g. a graphical frontend. Look at ./man/dos.1 for more. + Involved files: man/dos.1, mhpdbgc.c, config.c, ioctl.c, + base/misc/userhook.c, utilities.c, dpmi.c, do_vm86.c, emu.c, + emu.h, mhpdbg.h, utilities.h, tools/periph/dosctrl.c + - moved some code mhpdbgc.c to utilities.c in order to use them + also for userhook.c + - fixed append_pre_strokes() such that it can work also _after_ + the first initial use (needed by userhook.c) +99/02/12 ver. 0.99.8 released + From Markus Kossmann + - missing in utilities.c made compile fail for glibc + From Hans + - fix to type_in_pre_strokes() (keyboard/prestroke.c) not doing + 'keystroke "\Fx;"' + - removed HOGTHRESHOLD from README.txt (hopelessly outdated) + - wrote some minimum docs for Steffens 'bootdir' stuff. + - Stated in the docs, that DosC can't cope with lredir + (hope this stops the questions on linux-msdos). + - docs recompile (including DANG) +99/02/11 ver. 0.99.7.1 unofficial pre-release + From Steffen + - introduced booting _directly_ from a (lredir'ed) Linux directory. + No hdimage needed (this one was missing --Hans ;-). Involved files: + src/base/misc/{disks.c,fatfs.c,fatfs.h,fatfs_boot.S} + etc/global.conf, async/int.c, init/config.c, init/parser.y + mfs/mfs.c, include/{disks.h,int.h} + How it works: + - in $_hdimage a directory can be given containing atleast + IO.SYS, MSDOS.SYS (or what the appropriate DOS has as system + files). It also may (but need not) conatin a non-standard + bootsector file (name: boot.blk). + - This directory is partially converted (on-the-fly) to a FAT type + emulated disk (no data, only the directory enrties). A default + bootsector also is faked, and if boot.blk exists + this one is taken. From this emulated disk a boot attempt is done. + When the first _file_ access happens, the redirector is called + and from this point on (if the DOS supports a redirector) it + becomes an lredir'ed drive. + - In principal the emulated drive also works with the DosC kernel + (which not yet has a redirector), but the emulated FAT-FS remains + active and is readonly (writes are ignored). + From Hans + - fix for dosemu crashing on floppy access with no disk inserted + and not able to read writeprotected floppies. +99/01/28 ver. 0.99.7 released + From Steffen + - hercules emulation in vgaemu only when dualmon not set + - further reworks on vgaemu + - changes in mapping strategie. + - vesa_emu_fault in vesa.c removed, now vesa.c is completely replaced + and doesn't contain any old code piece. + From Florian La Roche + - further adaption to newer egcs versions :-( + (src/include/dosemu_select.h) + From Hans + - trying to solve the midnight flag thing correctly + (base/async/int.c, INT1A,AH0), ... though, I may be wrong. + Lets see if it solves the problem. + - removed SMP check from base-configure, obsolete nowerdays + and gets wrong under linux-2.2 +99/01/09 ver. 0.99.6.1 unofficial pre-release + From Alberto + - further cpuemu patches (sync with dose9961.dff) + - further cpuemu patches (sync with h1toh2.dff) + - new README.cpuemu + From Hans + - preset the config structure with zero at the very startup. + (suspecting accesses to uninitialized data, but I'm not sure) + - Oops the 99/01/08 version was wrong :-( corrected 0.99.5 -> 0.99.6 + - fixed global.conf not passing $_cpu = "emulated" correctly. + - fixed rdtsc setting in global.conf (no hidden default any more) + - made $_rdtsc = (on) the default again (wish from Alberto) + - kludge: restricted asm contraints in PACK32_16 (intp32/hsw_interp.h) + to avoid gcc bug in 2.7.2.x and egcs <= 2.91 (signal 11 on cc1) + - put USE_GLOBAL_EBP into cpu-emu.h as a configure option for cpuemu + and made it default (realy increases emulation speed a lot). +99/01/08 ver. 0.99.6 released + From Hans + - new script 'mknewyear' + - executed 'mknewyear' ;-) +99/01/07 ver. 0.99.5.6 unofficial pre-release + From Alberto + - second round cpuemu patches + (changes in the TWIN part) + (Note from Hans: this is not yet working yet, some changes did not + went in or were changed and have to be reworked by Alberto. + This was just a too big patch;-) +99/01/07 ver. 0.99.5.5 unofficial pre-release + From Alberto + - first round cpuemu patches + (changes in common parts) +99/01/07 ver. 0.99.5.4 unofficial pre-release + From Alberto + - decide for INT06-handling by redirection of INT06 and not by + opcode 0f (sigsegv.c) + - removed wrong "we shouldn't be here" in case of newint code + (async/int.c) + - fixed a bug in PIT2 (timers.c) always returning the state of the + output pin instead of the count. + - moved DPMI_show_state to dump.c + - uninlined set_ldt_entry, checking only bit 2 of ss in ldt (dpmi.c) + - introduced 'quiet' mode in cputime.c + - when config.vga is defined, don't try to set a wrong entry in the + port table (port.c) + - further fixes on sig11 recovery (emu.c, emu.h) + - introduced GETTSC() to read the TSC counter (timers.h) + (modified it to be optimized by using "=A" constraint --Hans) +99/01/06 ver. 0.99.5.3 unofficial pre-release + From Marcus + - fix for misc/136 dosnet bug report (e.g. put back old loopback.c + code for kernel 2.0.36) + From A.R.Adams + - fixes for src/arch/linux/debugger/dis8086.c + From Wojtek Pilorz + - fix for dosdebug not finding the pipes, when pid was given + explicitely, but $HOME/.dosemu/run contains other running pids. + - fix for do_mtools not taking filesnames containing whitespaces + into account + From Mislav Korona + - added support for croatian keyboard ('hr' comes from 'Hrvatska'), + iso8859-2 (hr-latin2) and cp852 (hr-cp852) + From Hans + - upgraded to slang-1.2.2 because of buffer overrun exploits + in 1.0.3. Verified, that _both_ exploits are fixed. + - fixed (now) possible buffer overrun in verror (utilities.c), because + slang-1.2.2 fixed its exploit by passing the involved printout + via (*SLang_Exit_Error_Hook)() to _our_ hooking routine. + (well, so we now have it ;-) +99/01/06 ver. 0.99.5.2 unofficial pre-release + From Steffen + - Further work on VGAEMU, involved files: int10.c, timers.c + mousevid.c, xmode.c, dpmi.c, ports.c, video/*, vgaemu.h + New files: video/{gfxemu.c,hercemu.c,miscemu.c} + - all standard VGA registers now are correctly initialized + - monochrom textmode (0x07) functional, correct grayscales + - support for HCG graphics mode (though, this was a jumper + setable feature on old VGA cards, needs to be dosemu.conf + option --Hans) + - magicaly some display errors of doom have vanished + - xmode.exe has a new option '-mode n' to set the desired + graphic mode + - some general cleanups + - integrated Josef Pavlik's patches +99/01/05 ver. 0.99.5.1 unofficial pre-release + From Pat + - new DosC kernel, version 1.0 Beta2, built 1937 + (however, still not supporting redirector) + From Alberto + - FreeDescriptor (dpmi.c) should sync with the real LDT not only + with the LDT cache. + From Hans + - introduced a facility for temporary config options called + 'features'. The purpose of these parameters is to switch between + code or code pieces that in principal do the same, but for some + unknown reasons behave different between machines. + If a 'features' becomes obsolete (problem solved) it will + remain dummy for a while before re-used. + NOTE: 'features' are not subject to permanent documentation! + They should be considered 'temporary hacks' + A feature can be set (or given a value) such as 'feature {0 = 1}' + (in global.conf) or via /etc/dosemu.conf such as $_features="0:1" + $_features can hold a list of multiple 'n:value' pairs. + NOTE: $_features currently isn't used by 0.99, but we need to + to have it here for compatibility with dosemu.conf used by + 0.98. + - fixed a bug in global.conf ($_X_winsize parsed wrongly) + - fixed typo in config.c: LX_KERNEL_VERSION 201126 -> 2001126 + - disabled '#define asmlinkage' in memory.h, because its not + used when not also is included, where it finaly + is defined. +98/12/04 ver. 0.99.5 released + From Manuel Villegas Marin + - Support for up to 4 cdrom drives. You need to load the cdrom.sys + with different args for each drive you want, such as + device=cdrom.sys + device=cdrom.sys 2 + The DOS device names then are MSCD0001, MSCD0002, ... + The preconfigured devices are /dev/cdrom, /dev/cdrom2 ... + From Hans + - updated the parser to reflext Manuel's changes: + Just do cdrom { /dev/xxx } multiple times for each device, + First one means MSCD0001, the second MSCD0002 e.t.c. + - wrote some tiny documentation to reflect the cdrom.sys changes. + - changed the semantic of the 'fast' attribute for ports{} slighly, + The problem was that it was cleared strangely within ports{..}, such + that ports{ fast 0x11 0x22 0x33 range 0x44 0c66 0x66} put the fast + attrib on all above ports except 0x66, because only after 'range' + portspeed was cleared. Now it needs a 'fast' again in front of + 'range' to make the 'range' fast. +98/12/02 ver. 0.99.4.2 unofficial pre-release + From Hans + - re-applied the old slangcode :-( + As it turned out people have difficulties with the new slangcode + and some _real_ terminals seem not to be handled as in 0.66.7. + As long as those problems aren't solved (or we have a good + doc helping those people to use the new code) it will remain + in as a compilation option. If I get more complains, the old + code will be turned on as default. + - fix for console not proper switching when doing graphics but not + using raw keyboard. Many kudos to Andrea Omodeo + who tracked this down. +98/12/01 ver. 0.99.4.1 unofficial pre-release + From Alberto + - fix for TASMX not running (wrong handling of selectors in + INT21,fn38). Good work Alberto! + From Hans + - first temp fix to 8-char-limit-pasting in Slang terminal + (found during IRC by Eric, Alistair and me) + - introduced PORTABLE_BINARY compile condition together with -static + - workaraund for glibc static binaries running on libc5 systems + (parsing /etc/passwd ourselves when /etc/nsswitch.conf not + existing), enabled via PORTABLE_BINARY in base/misc/utilities.c + - some dualmon fixes (global.conf, video/dualmon.c) + - made 2.1.126 timerstuff _run_time instead of compiletime dependend + (init/config.c, emu-i386/cputime.c) + - use --host=i386-any-linux for the binary distribution +98/11/21 ver. 0.99.4 released + From Alberto + - changed all his mail addresses + From Hans + - DANG remake + - Docs recompile (including version update in heeder and header-tech) +98/11/21 ver. 0.99.3.5 unofficial pre-release + From Florian La Roche + - changes/fixes to make DOSEMU compile under all currently + available gcc/egcs/glibc combinations (hopefully). + (This was working 981112, hence what status we have 981113 + isn't predictable --Hans :-)) + From Zoltan Boszormenyi + - patches, which touched the same issue as that of Florian, + Zoltan's additional fixes were worked in by Florian. + From Karl Kiniger + - using pusha/popa instead of pushal/popal (dpmi.c), else + recent binutils would complain. + - fix for llseek, when glibc headers don't define them + From Hans + - changes in base-configure + - generate unique version codes for + gcc and glibc: GCC_VERSION_CODE and GLIBC_VERSION_CODE. + Major/minor is represented as (major * 1000) + minor. + - Also introduced 'ASM_PEDANTIC', which is set when a compiler + would silently produce wrong code on overlapping clobbered + registers in the asm constraints (which worked fine for gcc-2.7.1). + - using -fno-gcse for egcs, because otherwise some realy strange + things happen (last seen in config.c) + All the above are propagated through CFLAGS and are available in the + Makefiles. + - checking for llseek prototype on glibc, defining + NEED_LLSEEK_PROTOTYPE in config.h when needed. + - Adapted Florians Patches to reflect the base-configure changes. + - Adapted remaining GLIBC/GNUC #if's to *_VERSION_CODE. + - removed some gcc/egcs warnings + - removed EXPERIMENTAL from ASPI + - made ASPI default in compiletime-settings + - final finetuning on glibc-2.0.7pre6 stuff +98/11/21 ver. 0.99.3.4 unofficial pre-release + From Shawn Pringle + - small fix to global.conf ($_X_winsize typo) + From Fabrice Bellet + - small fix to global.conf (/dev/sda1 matching /dev/sda11) + From Alistair + - fix for gnats (gnats/get-email.sh, setup/parse-menu-sh) + - better doc for _usage_ of sound within DOSEMU + (doc/sound-usage.txt) + From Hans + - tried to fix a problem with base-configure not setting + proper X11 libpath under certain conditions. + - added some missing chipsets to the docs and setup-dosemu + - some fixes to dosemu.spec (copied keymap twice to rpm) +98/11/20 ver. 0.99.3.3 unofficial pre-release + From Alberto + - added floppy parameter table to the BIOS, just for compatibility + - added the 'atapi' keyword to floppy config. This is because + current code assumes anything different from /dev/fd{01} is not + removable, and dosemu crashes. Modified disks.c accordingly. + - add a disk sector dump for debug purposes. + - small fix for patch from Michael (forgotten to update + struct printer lpt in base/dev/misc/lpt.c) + - Enable 'cpu MHz' check in /proc/cpuinfo for kernels >=2.1.127 + - mfs.c: wrong offset calculation prevented IDA from working + - do_vm86.c: better debug messages around DO_VM86() + - emu.c: found a way to exit dosemu when it gets a signal 11. + As {_}exit() doesn't work in this case, we need to tell the + kernel to kill ourselves. Added a setjmp/longjmp for this + emergency case. Modified the driver closing order in leavedos, + to have keyboard and video removed first. + - added brackets around SETIVEC for safety + - removed unused 2-letter variables in emu.h + - retrace trick in timers.c. Please check if your system has + better timing with games under X, this can avoid part of the + 'too slow' or 'too fast' cases. + - changes to attremu.c: removed (stupid) HAVE_GETTIMEOFDAY check, + make it compile without X, export t_vretrace, moved old CGA + snow code from ports.c and make it slightly better (1 VR every + 256 HR, instead of 1:1) + - the 'emuretrace' patch properly (== emulate video retrace + also in console mode). To be checked, some games can run better. + From Rob Clark + - removed X_GRAPHICS definition from ports.c (CGA snow bugfix) + From Hans + - changed global.conf such that (optionally) a device can be given: + $_floppy_a = "threeinch:/dev/fd0" + - splitted ChangeLog into 'ChangeLog' and 'ChangeLog.ancient' + (wish from Alistair for better parsing it) + - changed src/tools/mkbindist to reflect ChangeLog splitting + - disabled CPUSPEED in parser.y, we don't need it anymore, but we + leave it in for a while for dosemu.conf compatibility. +98/11/06 ver. 0.99.3.2 unofficial pre-release + From Steffen + - implemented the some missing video modes: 1 & 2 bit CGA/VGA +98/11/06 ver. 0.99.3.1 unofficial pre-release + From Michael + - small 'more then one' printer fix (lpt.c) + - fix for memory leak in lpt.c + From Hans + - fixed bug in aspi.c: did malloc() one entry too few + - removed not needed '#include in both scsicheck.c + and aspi.c + - copied a cutdown version of linux/include/scsi/sg.h into + our src/include/scsi/*. Now ASPI should compile regardless wether + /usr/include/scsi is missing (glibc?). + - added "host/channel/ID/LUN" construct to search for the right + sgX device (aspi.c, scsicheck) + - fix (in fact a kludge) for dosemu not running in a cronjob + (base/keyboard/keyb_slang.c). In case of not being able to tcgetattr() + we examine TERM for 'dumb', 'none' or 'config.cardtype==CARD_NONE'. + In this case we set TERM and TERMCAP and ignore the rest. + - updated doc/README/batch to reflect the 'cron kludge'. +98/10/31 ver. 0.99.3 released + From Alberto + - pentium timing: rdtsc is now off by default + - timers and PIC: introduced 64-bit variables, Add return values to + pic_request. Added DEBUG_PIT macro. Removed obsolete definition of + pic_creq. + - int1a: corrected backward time jumps (30 minutes bug) and + midnight transition. + - fixes to (wrongly) zeroed BIOS font tables. Mainly the wrong + placed bios_f000_end label (bios.S). Moved bios_f000_* definitions + into bios.h and deleted them at all other places. + - fixed pic_sys_time calibration + - some optimizations in msdos.h + From Arne + - implemeted int 10h, ah=1bh (src/base/bios/int10.c) + - fix for wrong PIT latch command in src/base/dev/misc/timers.c +98/10/30 ver. 0.99.2.3 unofficial pre-release + From Steffen + - faster palette updates + - VGA compliant treatment of palette registers + - correct implementation of the Attribute Controller interface + - PEL-mask support + - simplification of DAC emulator interface +98/10/30 ver. 0.99.2.2 unofficial pre-release + ( syncing changes from 0.98 to 0.99 ) + From Hans + - (problem reported by Wojtek Pilorz) + Though protected by '$_secure="ngd"', the system.com command + was a permanent security risc when people switched 'secure off' + for some other reasons. Now I made system.com secure, even when + 'secure' is switched off. This may change the expected behave for + some braindammaged usage, but I won't reverse it any more ;-) + - fix for $_X_font in global.conf (was not proper working) + - replaced annoying error() with W_printf() in src/base/misc/disks.c + on writeprotect. + - small doc fixes Quickstart + - added RPM spec file + - changed setup-hdimage to set group 'dosemu' if possible + - disabled parsing of old .dosrc +98/10/25 ver. 0.99.2.1 unofficial pre-release + ( ... well, this is the ASPI release ;-) + From Karl Kiniger + - ASPI driver for DOSEMU, consists of a DOS stubb aspi.sys and + dosemu builtin ASPI helper. Works on any /dev/sgX. + The code was originally taken out of the Wine project but + is wildly hacked to fit our purposes. + ( Many kodos to Karl, who did a good job ;-) + From Hans + - introduced A debugflag for ASPI + - changed Karl's solution such that ASPI doesn't need any special + kernel patch. The only remaining 'patch' for the kernel should + be a bigger SG_BIG_BUF in include/scsi/sg.h if needed (Karl's + CD-burner needed atleast 64k and I for my scanner have + #define SG_BIG_BUFF (128*1024-512) in my kernel). + - changed aspy.sys such that it doesn't load, when no SCSI + is available or not configured. + - made ASPI configurable, we only allow those /dev/sgX, which + are explicitely configured (anything else is inherently dangerous + for naiv users;-). Look at src/doc/README/config for details. + In compile-time-settings its 'aspi on/off' _and_ currently its + protected via 'experimental'. + - tested the ASPI with my JAZ drive, works flawless;-) + - updated src/doc/README/config to reflect the ASPI driver + - updated setup-dosemu stuff to reflect the ASPI driver + - uodated DANG, but did not (yet) recompile the docs + (avoiding the patch becoming too big) + - added src/tools/peripher/scsicheck, which prints /proc/scsi/scsi + showing the needed $_aspi entries for /etc/dosemu.conf + - replaced error() print with d_printf() in src/base/misc/disks.c + as it turns out some DOS-apps are doing installation checks + by just calling their (normally hooked) int13 functions and + just expect CARRY if not installed. + - made show_regs dummy, when d.general (g debugflag) isn't set. + (in src/base/misc/dump.c). It turns out, we don't print anything + anyway in this case. + - fixed a bug in the parser's 'foreach': on stacked foreach loops, + the first run of the loop body was skipped (src/base/init/parser.y). +98/10/04 ver. 0.99.2 released + From Rob Clark + - fixed src/base/speaker/Makefile such that is compiles + with X off even when no X-headers available. + From Hans + - docs recompile using sgmltool-1.0.7 + (oh dear, the patch will be big) +98/10/03 ver. 0.99.1.3 unofficial pre-release + From Oleg V. Zhirov + - (partial) NLS support for MFS + see src/doc/README/mfsnsl for details + From Hans + - converted Oleg's docs to sgml (no docs recompile yet) +98/10/03 ver. 0.99.1.2 unofficial pre-release + From Alexander R.Adams + - Changes to 80x86 disassembler (src/arch/linux/debugger/dis8086.c) + - more (full?) 32 bit support + - lots more fpu ops and a couple of cpu ops + - better readable output +98/09/25 ver. 0.99.1.1 unofficial pre-release + From Alberto + - fix for >=linux-2.1.121 not having inux/head.h + From Han Holl + - 'nolinks' isn't supported by bash-2 anymore, replaced all + instances in the Makefiles with 'pwd -P' + From Steffen + - Continued work on color allocation. + - source cleanup of X.c & friends + - support for 16 color X servers (e.g. XF86_VGA16) + - textmode on all X servers, including 16 color & mono servers. + - implemented int10 - palette functions. + - (mostly) correct processing of palette registers + - Removed quite a lot of DAC/color init code. + From Hans + - forced make to use SHELL=/bin/bash, else 'pwd -P' won't work. +98/09/12 ver. 0.99.1 released + From Hans + - fixed the severe security hole introduced in 0.97.9.2 + (And yes, it was me who produced this glitch) +98/09/07 ver. 0.99.0 FORK-POINT released + NOTE: This is the 0.98/0.99 forkpoint + 0.99 will continue unchanged to form the new Hacker version. + 0.98, the stable version, will be 0.99 with the experimental + stuff removed. + From Hans + - removed old X-mouse stuff + - removed old CMOS stuff + - removed old Slang code + - fixed mkbindist + - removed creation of /var/run for 'make install' + - fixed some tool scripts ("~/" wasn't expanded because quoted) + - fixed src/tools/mkbindist + - more precisely copyright definition (prefix to COPYING) + Prefixed all source files with the proper copyright notice. + (copyright laws are requiring this) + - finally made referred to dosemu.org (docs udapted) +98/09/06 ver. 0.97.10.6 unofficial pre-release + From Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz> + - use of XSetWMProperties() instead of XSetClassHint() in X.c + to properly set WMHints for window managers. + From Hans + - removed old port code + - reworked docs to prepare vor 0.98 +98/09/05 ver. 0.97.10.5 unofficial pre-release + From Maxim Ruchko *McSim* + - fix for cursor hidding on console (FoxPro), cursor is positioned + _past_ right bottom corner. Dosemu did not handle this. + - small fix to previous mfs patch. + From Pablo Saratxaga + - support for code pages 850, 852, 437 conversion to + iso-8859-1, iso-8859-2 and viceversa. + Code page selection via 'charset XXX', where XXX is + one of ibm, latin, latin1, latin2. + - fixed belgian and hungarian keyboard + - added polish keyboard + From Hans + - modified 'code page selection' from Maxim such that it + became a separate overall selection for all types of logins: + moved it out of the 'terminal' statement (though its still tholerated + within for back compatibility) + - Updated the docs and setup-dosemu to reflect the change. + ( within setup-dosemu its now part of the keyboard menu ). + - fixed dead circumflex in de-latin1 map. + - new script to automatically generate ./etc/keymap/* + ... and used it to bring the table uptodate;-) + - docs recompile +98/08/24 ver. 0.97.10.4 unofficial pre-release + From Robert Rendell + - fix for rep_outb in n_ports.c (wrong while loop) + From Hans + - fixed all similar instances of Robert's fix (above) in + n_ports.c. What do we learn from this? Never put more then one + statement (following a conditional) into one line ;-) + - fixed -I 'keyload "keymap/xxx"', which was broken since 0.97.8.2 + - added com3/com3 and ttylocks to {global,dosemu}.conf; + updated setup-dosemu to reflect this addition. + - default lock dir now is /var/lock. + - moved all remaining 'hardcoded' paths (hope I found them all) + into acconfig.h (config,h.in) making them runtime configurable. + - made /var/lib/dosemu movable to ~/.dosemu/lib on a per user basis. + made /var/lib/dosemu (systemwide) changable via dosemu.users + ('default_lib_dir='). With this change only dosemu.users remains + hardcoded (though, look next item). + - Now checking for /etc/dosemu.users, if this doesn't exist, + check if /etc/dosemu/dosemu.users does exist (this makes people + happy, who like to have a dosemu directory in /etc). + - moved /var/run/dosemu-midi to ~/.dosemu/run/dosemu-midi. + - removed /etc/dosemu.loglevel, setting of log level now is done + in dosemu.users ('log_level=2'). + - removed src/dosconfig.c (way too old and obsolete) + - fixed some more compiler warnings. + - removed old 'rcs-IDs' + - updated docs (but did not recompile, have to check my SGML tools). +98/08/23 ver. 0.97.10.3 unofficial pre-release + From Alexander V. Lukyanov + - fix for S3-Trio not saving/restoring all extra regs. + From Klaus Reimann + - better 'odd textutils' detection (gnats misc-120) + From Maxim Ruchko *McSim* + - Patches to the mfs code: + - FoxPro now should work better + - increase dosemu SHARE compatibility. +98/08/22 ver. 0.97.10.2 unofficial pre-release + From Josef Pavlik (gnats misc-122,123) + - fix for possible too small malloc (src/env/video/video.c) + - fix for Text-refresh on non standard TEXT modes (src/env/video/X.c) + From Hans + - removed x2dos, nobody is using it anymore (functionality was longtime + ago replaced by dualmon) + - fixed (possible) /tmp-exploits (in scripts as well as in C-code), + now DOSEMU uses _always_ $HOME/.dosemu/tmp, when needing tempfiles. + All those local ~/.dosemu directories will be created silently, + when not existing. + - Moved /var/run/* usage to $HOME/.dosemu/run/*, regardless wether + running suid-root or not. + - Removed old tempdir stuff and the -T commandline option, + which related to that, because we force our own temps below + $HOME/.dosemu/* (see above). + - Made base/misc/shared.c to create its tempfile 0600 and switched + to priv_off, as we now don't use /var/run/* anymore. + - removed some obsolete '#if 0' stuff + - made 'distclean' and 'mrproper' an alias for 'make pristine' + (some people not reading docs seem to expect it ;-) + - use of autoconf version 2.12 instead of 2.10 leaded to a slightly + different base-configure and include/config.h.in +98/08/05 ver. 0.97.10.1 unofficial pre-release + From Eric + - some X-mouse changes from 0.97.8.x4, which seem to solve + the >25 row problem (BC in 43 line mode) and some games were + the mouse couldn't pass below the middle of the screen. + - fixes to x4 concerning w31 mousesnapping + - mouse fixes along ideas/reports of Josef Pavlik + (gnats misc 121) + From Markus & Michael + - fix for pdether + From Tamminen Eero + - small fix for ./ViewDocs + From Hans + - made newint the default +98/06/27 ver. 0.97.10 released + From George K.Bronnikov + - First step for 'DOSEMU in batch mode', atleast the output side. + When setting 'video {none}' DOSEMU can run without any terminal + and writes the DOS-stdout to Linux-stdout. You may play with it + such as: dos -I 'video {none} keystroke "dir\rexitdos\r"' + (for stdin we haven't yet a reasonable solution} + Note: When 'video {none}' isn't given, the code is absolutely + disabled. + From Hans + - fixed src/tools/mkbindist ( was out of sync ) +98/06/26 ver. 0.97.9.3 unofficial pre-release + From Andreas Kirschbaum + - fix for Borland-C (4.0) tlib not running + - some wrong debug prints. + From rwfries+bobf@dreamscape.com (gnats misc/104) + - fix segfaults when dosdebug+xdos used together with + /usr/include/gnu/types.h of glibc-devel-2.0.6-9 + ( bug doesn't appear on libc-5, but the fix is correct, --Hans ) + From Karl-Max Wagner + - support for japanese jp106 keyboard (and before you ask why + someone with a typical german name can do that: yes he can because + he speaks Japanese and runs DOSEMU on a japanese machine;-) + From Marcus + - fixes to the dosnet stuff, now support new-int (lucky Eric;-) + - Randomized dosnet ID, no clashes when running from the same tty. + - moved srand() out of midid/oss.c to a more global place: emu.c + (Alistair has to verify, --Hans) + From Alistair + - update to debug menu for setu-dosemu + From Hans + - adapted setup-dosemu to know about jp106 + - docs update and recompile +98/06/26 ver. 0.97.9.2 unofficial pre-release + From Hans + - Second round of security fixes: got non-suid-root DOSEMU running. + However, this has reduced features: no port access, no hardware + access e.t.c, on console only pure terminal (Slang, no raw kbd). + On X it does a lot more: I was even able to run win31 + (hence, DPMI is secure now with the s-bit off). + - Moved parse_dosemu_users again, this time _directly_ behind + priv_init at top of main(). This allows checking, wether this + user is allowed to run a suid binary. In /etc/dosemu.users there + is a new keyword ('nosuidroot') which only allows to run + non-suid root (copies of) DOSEMU. + - In case of non-suid root we now use a ~/.dosemu/* directory + (will create it if missing) for the stuff, we usually have + in /var/run/*. Dosdebug was adapted to support that. + - updated the docs to reflect the above changes. +98/06/24 ver. 0.97.9.1 unofficial pre-release + From Alberto + - fix a 'diabolic' missing '*/' in src/base/async/int.c + - some changes to make egcs happy + From Eric + - removed unneeded int10 label/jump in bios.S + From Hans + - removed mailing to root (as it turned out to be a bad idea) + Sysadm can check the logfiles instead. + - moved parse_dosemu_users into init/config.c _before_ any parsing + of commandline arguments. Should be quite securer. + - fixed possible bufferflow in resolve_exec_path (parser.y) +98/06/15 ver. 0.97.9 released + From Markus + - cleanup to dosnet code, involved files: bios.S, pktnew.c + and doc/README/dosnet. Note: this code needs the the old int code. + - removed dosext/net/net/select_packet and its entry in + set-permission, dosext/net/v-net/{dosnet.h,dosnet.c.kernel-2.1.x} + (remark) From Hans + - I too had a second patch from Markus, but this one changed + too much and in addition broke our (just gotten working) setup + stuff. So this has to be delay until after 1.0 + However, I expect to get atleast a patch from Markus such that the + cleaned up dosnet can run together with the new int code. + (... if not, well then the old int code will be the default for 1.0) +98/06/14 ver. 0.97.8.2 unofficial pre-release + From Eric + - x1: quicken mouse fix + - fix a little piece of code without a sanity check + (src/base/mouse/gcursor.c ) + - Added code to correct textmodes, + mousevid.c (get_current_video_mode) + - async/int.c: Readded old call to SETIVEC, mouse_post_boot, + dos_post_boot, moved code to dos_post_boot, and mouse_post_boot + - x2: fixes to keyb_slang.c + - x3: further fixes to EMS&X bug + - x5: emulated speaker fix, base/dev/misc/timers.c + - x6: enhancing X color palette handling, video/X.c, dacemu.c + (but sorry, the palette in 4-bit ega mode is still wrong, + --Hans) + - x9: fixed bugs in global.conf + - fixed line counting and error reporting in lexer.l, parser.y + - x10: readed old new_set_video_mode for 0.97.4, broke non-X compiles + From Hans + - renamed new_set_video_mode to X_set_video_mode to avoid further + confusion. + - some small 'adjustments' to x1,x6,x9 from Eric. +98/06/13 ver. 0.97.8.1 unofficial pre-release + From Alistair + - Tcl/Tk adaption for compiletime stuff in setup-dosemu + From Hans + - fixed .dosemurc stuff/changes in runtime_setup.tk not being relative + to global settings (yes Alistair, it was me who messed it up ;-) + Also, when resulting in an empty .dosemurc, we delete it + (else src/base/init/parser.y can't cope with it) + - changed setup/parser/parser.y such that it can cope with + non-existing/empty files. + - temp. disabled 'Debugging' submenu until it realy works + - added browser.tk, which is sourced by both, compiletime & runtime + This contains functions to popup a brows window and to + show Help. + - wrote the Tk wrapper menu (setup-dosemu can only handle dialog) + - added Help to runtime-setu via viewing the appropriate part + of chapter 2 of README.txt + - fix a bug in compiletime_setup.tk not using the proper file for + default-configure (configure went berserk) + - added to compiletime_setup.tk: + - forced 'make pristine' before doing configure, if Makefile.conf + exists (too dangerous to change settings without it) + - display of ./configure results in a window + - asking for 'compile' and running make, result in a window too + - help is now working + - Changed 'DOSEmu' to 'DOSEMU', as this (all capitals) is the official + form of writing it ;-) +98/05/31 ver. 0.97.8 released + From Alistair (+ Hans) + - new Tcl/Tk based runtime setup. We cuurently only support that + and have disabled the dialog based stuff until we get that + fixed/ported. Maybe we leave it in this state for 1.0. + The main ./setup-dosemu wrapper will only start the Tcl/Tk stuff + when available, else an informative message will be printed. + From Hans + - added dos.1 to ViewDocs +98/05/28 ver. 0.97.7.1 unofficial pre-release + From Eric + - Fixed Ctrl-Alt-Fn for 'video {vga}' and raw keyboard. + (wrong initialization order ) + - Fix for strange EMS bug under X, changed 'xresize' made the + bug disappear, but even the gurus don't know why :-( + From Hans + - fixed bug in mhpdbg.c not setting ownership of debugger pipes + ( running as user wasn't able to use dosdebug ) + - added a script 'ViewDocs' for users, that don't know 'less' ;-) +98/05/03 ver. 0.97.7 released + From Micheal (via Alistair;-) + - newest sound patches +98/05/03 ver. 0.97.6.1 unofficial pre-release + From Eric + - fix for the 'beep' in INT10 AH=13: speaker_on(125, 0x637); + From Arne + - fix for video.c not setting screen_adr + From Hans + - fix for global.conf not correctly detecting an old style dosemu.conf + ( to make Alistair happy ;-) + - first step to remove 'fix pathes' (for config stuff) +98/04/25 ver. 0.97.6 released + From Steffen + - rework of set_video_mode (int10.c) + - using backing store now in text modes (n_X.c, X_USE_BACKING_STORE) + From Hans + - removed old X stuff (no NEW_X_CODE defines any more) + - docs recompile (including DANG) +98/04/24 ver. 0.97.5.1 unofficial pre-release + From Toshio Sengoku + - fixes to INT10 AH=13 (write string). + Now properly handles ^M, ^J and backspace. + From Eric + - fix for typo (/x instead of /dev/null) in base-configure.in + From Hans + - temporary disabled use of setup-dosemu, because our users + get confused and _are_ using it though it doesn't work correctly. + ( we _really_ need to fix that ) + - disabled bug-reporting, when the user missed to give a description + ( realy ugly hack ;-) + - removed trailing ^M^J in src/commands/precompiled/autoexec.bat + ( bogus 'command not found' on startup ). Well, now we have + the problem with diff/patch due to the missing \n :-( + - made 'newport on' the default again (seems to be stable enough) +98/02/29 ver. 0.97.5 released + From Hans + - upgraded to Slang-1.0.3, as we need _this_ version for demudialog + I made 'slangforce on' the default. + From Alistair + - changes to make demudialog compile with Slang (slcurses) + (colors are odd due to bugs in SLang, so BW is the default) +98/03/20 ver. 0.97.4.5 unofficial pre-release + From Steffen + - fix to global.conf not setting mitshm + - fixed/removed RESIZE_KLUDGE + - fix for mousecursor in graphics (was defined on _each_ X-event) + - reworked X-colormanagement: + - not crash any more when out of colors + - switch to private colormap, if the global one gets exausted + sideffect: some colors may be missing in textmode (become black) + ... fix for this is 'in progress' ;-) +98/03/20 ver. 0.97.4.4 unofficial pre-release + From Alistair + - This patch contains (unfortunately) too many changes for me to + remember them all. The main parts are Michaels patches to limit + the size of transfers (and other fixes) and Kenneths patches to + clean up the code, fix some small bugs and make the debug code more + flexible. I have (as usual) hacked about and probably broken large + chunks of it. + From Hans + - some small fixes to global.conf (checked for version) + - Again a _desperate_ try to avoid people using configure directly. + ( a user 'just saw configure.in and executed autoconf' ) + Why the hell do we have a Quickstart ???!!! + Moved configure.in to base-configure.in and changed mkconfscript + accordingly. ... hope this helps :-( +98/03/20 ver. 0.97.4.3 unofficial pre-release + From Hans + - introduced .dosemurc (this replaces .dosrc). If this is found, + the new style is used and this file is included by global.conf + under 'user scope' (hint from Eric). + This means it has all c_* classes undefined and can only change + enviromnent variables in its own isolated name space + (prefixed with 'dosemu_'). Look at global.conf for details. + - changed -f option to replace .dosemurc (instead of .dosrc). + As the -f option is quite new anyway, it should not be a + compatibility problem. + - realized partition access via dosemu.conf + - suffix " +hd" to $_vbootfloppy allows virtual floppy + $_hdimage + - $_printer is now a _list_ of printer names (LPT1 .. LPT3) + - fixed ./first-test (etc/config.test) + - renamed KERNEL_VERSION to LX_KERNEL_VERSION to avoid name clash + with (new) 2.1.x kernels headers. + - udated and recompiled the docs +98/03/12 ver. 0.97.4.2 unofficial pre-release + From Steffen + - fix for NEW_X_MOUSE not snapping win31 mouse + - fix for not handling repnz (f2 prefix) in do_vm86.c, dpmi.c + - new DOSHELPER: DOS_HELPER_XCONFIG. This takes input from + the new xmode.exe to set X configuration from within the DOS session. + (hope Steffen also writes the docs for it ;-) + - hiding the main Xwindow (text) via env.variable DOSEMU_HIDE_WINDOW + ( may need to be moved to some config.* variable) + Purpose: when starting an graphics application as the only app + for this session (maybe via DEXE), only the graphics window + shows up, hiding the fact that it is run under dosemu ;-) + From Hans + - make xmode.c 'norm-conform' ;-) and re-compiled it + - adapted and cleaned up src/commands/Makefile + - made 'newxmouse on' default in both compiletime-settings +98/03/01 ver. 0.97.4.1 unofficial pre-release + From Alistair + - fix for send-pr reporting wrong version when running under buggy + bash2 + From Albert K T Hui + - more glibc fixes + - make use of __USE_* defines (moved by Hans into configure) + (__USE_GNU for MREMAP_MAYMOVE and CRTSCTS, __USE_UNIX98 for XCASE) + - moved stdout inside main() in mkfatimage16.c to make glibc2 happy + From Hans + - fixed bug in configure.in not detecting missing as86/ld86 correctly + - fixed bug in global.conf not setting X when TERM != xterm + - installed glibc check in configure.in and then globally define + __USE_* (from Albert's patch) + - changed yyerror to yywarn for when a macro isn't found on + macro expansion. This may be empty anyway. (in lexer.l) +98/02/28 ver. 0.97.4 released + From Kenneth + - fixed diabolic 'usedoptions[]' in src/base/init/config.c + From Steffen + - patches to mouse support under X-grafic + - new feature: mgrap_key, defines a Keysymbol (after Ctrl+Alt) to use + when giving xdos _exclusively_ the mouse (make playing games easier). + Can be set via $_X_mgrep_key. Default (empty) is off, A good value + is "Home". + From Hans + - did what Steffen missed: building X_mgrep_key into the parser + adapted global.conf and changed the docs ;-) + - made Steffen's mouse patches a compiletime config option + because it breaks win31 mouse completely (newxmouse on|off) + - reworked ./default-configure a bit: + - compiletime-settings now can be multi line. + - './default-configure -d' uses 'development settings' + from file 'compiletime-settings.devel' + - compiletime-settings.stable became compiletime-settings + - fixed the 'old port code'. Why? Well this _was_ intended to be + removed completely, however, seeing the latest strangeness in + dosemu behave, we have to test, wether this may come out of some + things we did not do correctly in new code (this applies + to _all_ new code, not the port one alone). So, when having + problems, first try to isolate by configuring some old code. +98/02/27 ver. 0.97.3.3 unofficial pre-release + From Steffen + - fix for doom not restoring video mode (BIOS_VIDEO_MODE not set + correctly, src/base/bios/int10.c) + From Marcus + - fixes for libpacket.c, pktnew.c, dosnet.c + (... removal of some 'facelifting' hunks by Hans;-) + From Eric + - trying to work around glibc2 header 'philosopy' (again): + libpacket.c, ipxglt.c, ipx.c + From Kenneth + - enhancements to dump_config_status (printers, debug_flags) + From Hans + - made GetDebugFlagsHelper usable for general purpose and + used it for Kenneth's 'debug_flags' ouput. + - fixed '-D+a' also setting CpuEmu's 'e' flag, even when not + configured in. +98/02/27 ver. 0.97.3.2 unofficial pre-release + From Eric + - udaptation to Slang-1.0 + - 'error' from utilities.c now goes over 'verror' to simplify + passing of arglists. (used by sl_exit_error); + - made more room for INT16 code in bios.S (now starts at 0x3500 + behind the IPX code). + - fix for new do_int together with dpmi (src/base/async/int.c) + - fixed a stupid bug in signal handling, (sigaddset expects _one_ SIG + to be added, not a bitmask of signals). + - fixed compiling without X support. + From Eric & Reinhard + - divers fixes to (hopefully) get extended keybord keys right +98/02/15 ver. 0.97.3.1 unofficial pre-release + From Eric + - fix for kernels < 2.0.32 not having ENOMEDIUM in errno.h (cdrom.c) + From Kenneth + - enhancement to INT8-serializer-hack of DPMI (in_dpmi_pm_int==8) + involved files: dpmi.[ch], dev/misc/timers.c + - fixed bug in init/config.c: missplaced brackets for if-clause + From Klaus Reimann + - fix for global.conf (missing bracket) + From Hans + - made dosdebug to capture _any_ leavedos(!=0), normally exceptions + were captured before anyway. + - fixed a small buglet in the recent debugger enhancements + - fixed -D+D debugflag, such that it now _realy_ prints int21 events + (needs setting int21 redirection for that) + - fixed mixed misuse of d.dos (ds_printf) in other places: + 'd.dos' for DOS-ints and 'd.defint' (di_printf) for 'all other' ints. + ... obviously + - put in Pat's recent kernel 1933+, now can handle more then one + drives (which means the '+' above ;-) +98/02/08 ver. 0.97.3 released + From Alberto + - fix for cdrom errors on startup (only kernels 2.1.x) + - added $_hardware_ram to dosemu.conf + From "Alexander V. Lukyanov" + - some mods to make 0.97.2 compile on RedHat-5.0 + From Steffen + - closing a security hole together with 'shell' command + in /var/lib/dosemu/global.conf + From Hans + - cleaned those files in src/doc/README that confused 'patch' + because of missing new line at EOF +98/02/07 ver. 0.97.2.1 unofficial pre-release + From Hans + - some bug fixes to setup-hdimage (those bugs are in 0.66.7 too) + - mkdexe know will accept '/' as target name. I found '/', then + 'dirname path' replaces '/var/lib/dosemu' + - put in Pats new Dosc (Beta1 build 1931) for testing + - made some enhancements to dosdebug to easy further debugging of + DosC: + - with option -H1 dosemu now will wait for the dosdebug terminal + to come up and then goes into 'stop' mode. This allows debugging + from the point were we jump into the bootsector (7c00). + - dosdebug command 'e' enhanced, now accepts a list of values + of different type and lengh. 'ed' defaults to decimal input + and registernames can be used as values. When the ADDR is a + '-', the last used address is take (writing in consecutive + locations + - mhp_pars now handles quoting correctly ("aa bb" will go into + _one_ arg) + - logging can now redirected to the dosdebug terminal + (command: 'log on' and 'log off' respectively. + - introduced 'log break points', which listen on the debuglog + output stream and put the emulation into 'stop state' when + when a reg.expression matches (commands: 'bplog ' and + 'bclog ') + - break point settings are now refused when not in 'stopped' state + because this leads to problems (was always so). + - fixed some logprints, which did print notprintable characters + ( mainly in src/base/keyboard/keyb_slang.c ) + We have now the helpers 'strprintable()' and 'chrprintable()' + in utilities.c. + - docs update are recompile (fixed some sgml typo's too) +98/02/02 ver. 0.97.2 released + From Hans + - Due to arguments from Steffen, decided to rearange dosemu.conf + again. Now dosemu.conf just contains the GLOBAL_SETTINGS part. + The 'script' part is moved to /var/lib/dosemu/global.conf. Hope this + makes it more easy for our (normal) users to fiddle with the + configuration. + - fixed $$_speaker bug in global.conf (dosemu.conf of 0.97.1) + - (fixed) $_secure such that 'secure off' can be set. + - added $_rdtsc, $_cpuspeed, $_pci to dosemu.conf + - fixed cpuspeed setting in parser.y (now 'cpuspeed 166.666' works) + - added some missing printouts for -h opion (config.c) + - updated the docs + - chapter 2 of README.txt -> chapter 2 README-tech.txt + - new chapter 2 of README.txt describes new dosemu.conf + - updated Quickstart and man/dos.1 +98/01/30 ver. 0.97.1.2 unofficial pre-release + From Alberto + - the RTC/CMOS/int1a patches (tested with ndiags). + - removed egcs warnings ('ambiguous else' and 'default to int') + - enhancements to the disassembler + - new way of turning off debug log after console switch + - fixed bug in the definition of CONF_NLPT +98/01/30 ver. 0.97.1.1 unofficial pre-release + From Marcus + - rest of dosnet rework + From Sergey Suleimanov + - fix for FoxPro "Record not available" bug in conjuctions with + lredir'ed drives (mfs.c) + From Kenneth + - fix: Print Shop Delux not recognizing 'printer online'. + From Reinhard + - (first) patch to get the keypad-Ctrl (E0) stuff better. + ( well, atleast this one should be in so we don't forget it, + looking forward to get the rest from Eric ) + From Hans + - docs recompile +98/01/15 ver. 0.97.1 unofficial pre-release + From Marcus Better + - First round of a libpacket / dosnet rework (needed for 2.1.x) + ( note: the chances to dosnet aren't yet in, have to consult Marcus + again ) +98/01/15 ver. 0.97.0.3 unofficial pre-release + From Pasi + - fixed 'missing KeyRelease' when out of window focus in keyb_X.c + ( thanks Pasi, that also fixes 'arrgh, hit wrong Alt-Fx' probl. :-) + From Eric + - no-cursor-bug-fix (console), int10.c, console.c (x26) + From Alberto + - adapt /proc/cpuinfo scanning to kernels 2.1.x + From Hans + - introduced 'runtime' kernel version checking: kernel_version_code. +98/01/15 ver. 0.97.0.2 unofficial pre-release + From Eric + - Changed ubyte_t to Bit8u (in video.h, bios.h ) (x10) + - Changed include of to "bitops.h" ( in mhpdbg.c + mhpdbgc.c, interp_32_16.c, interp_main.c, ports.c ) (x11) + - Updates the cursor in grahics mode now too, mouse/mouse.c (x14) + - Allow threads to compile with glibc (include/lt-threads.h) (x20) + - removed __u*: include/cpu.h (glibc2 problem) (x21) + - further glibc fixes, include/types.h (x22,23) + - docs for Priv-usage (x24) + From Arne de Bruijn + don't set video bios variables for native VBIOS, video.c + From Hans + - 'desperate' try to disable direct execution of ./base-configure + ( '#! /bin/bash' and x-bit removed ) + - fixed console detection in etc/dosemu.conf +98/01/14 ver. 0.97.0.1 unofficial pre-release + From Kenneth Corbin + - fix for >16bit portnumbers in src/emu-i386/n_ports.c + From Pasi Eronen + - fix typo in vga_emu_init(), in src/env/video/vgaemu.c + From Arne de Bruijn + - fix for seek_from_eof fails on lredir'd drives (mfs.c) + From Hans + - small optimization to power_of_2_sqrt. (removed silly compares) + - fixed (longstanding) typo in parser.y: CARD_EGA was treated as CGA + - fixed too late initialization of 'mem_names' in + src/base/init/memcheck.c + - enhancements to parser.y /lexer.l. + After long thinking and hesitating I decided to use real number + during startup /configuration. We won't use math support later + but I verified, it doesn't do any harm during start up. + Hence for future configuration stuff, we can avoid such tricks + as 'cpuspeed 500 3' and can have 'cpuspeed 166.666'. + - full expressions evaluation, automatic real/integer evaluation. + - string expressions + - using environment variables ($XXX) in (string) expressions + - setting environment variables possible + - preset some environment variables before running the parser + (KERNEL_VERSION_CODE, DOSEMU_VERSION_CODE, DOSEMU_EUID, + DOSEMU_UID, DOSEMU_USER, DOSEMU_REAL_USER, DOSEMU_HOST) + - new: 'if ()' statement + - new: 'while () ... done' statement + - new: 'foreach $xxx () ... done' statement + - new: shell() statement + - new: macro expansion ($$xxx) + - ... and lot more, have a look at README.txt + - ./etc/config.dist became ./etc/dosemu.conf + ( rewrote the whole stuff, now checks for a lot more and sets + configuration more correctly in the first place ) + - updated the docs accordingly + - new commandline option to aid debugging of configuration: + dos -h[0-2] -O 2>&1 prints all usefull configuration data and exits. + - new commandline option: '-f dosrcfile', which overwrites reading + of ~/.dosrc + - fixed _some_ (not all) sgml2tex errors in src/doc/README + ( man, are those '...' a mess, difficult to find anyway ) + because they were 'hiding' a lot of valuable info. +97/12/13 ver. 0.97.0 unofficial pre-release, VERSION BUMP + From Pasi + - patch for color allocation problems with -bpp 8 in src/env/video/X.c + ( tries to use existing colors if they're close enough, and even + if allocation fails, some reasonable colors are chosen). + From Hans + - made simple integer_sqrt() to avoid FPU usage in Pasi's patch + ( simple power of 2 iteration algorithme, if some skilled + mathematician can make it better, I wouldn't be angry ;-) + Anyway, for the above purpose it takes max 9 iterations. + NOTE: + This is the release that prepares for the 'fork point' + to 0.98/0.99. If 0.97 has stabilized it'll become (unchanged) + 0.99, and minus some to-back-out-stuff it'll become 0.98. +97/12/12 ver. 0.67.16.8 unofficial pre-release + From Robert de Bath + - new enhanced Slangterminal code + (src/base/keyboard/keyb_slang.c, src/env/video/terminal.c) + From Eric + - some fixes to Robert's code + From Hans + - cleaned up Roberts code such that a diff between + the old and the new code is better readable. + - made compilation of the old Slang code a configure option + (--enable-old-slangcode) and moved the old code to o_keyb_slang.c + - docs recompile +97/12/10 ver. 0.67.16.7 unofficial pre-release + From Karl Kiniger ( ... ASPI-Karl ;-) + - fix for src/dosext/misc/emm.c returning wrong error code + on 'get handle by name'. + From Eric + - patch to make the enter/leav_priv* stuff working with threads + (see README.txt, chapter 'Priveleges and Running as User') + From Hans + - small changes to Eric's patch such that it checks the depth. + - adapted the the newer 0.67.16.x stuff to the new priv stuff + - docs recompile +97/12/07 ver. 0.67.16.6 unofficial pre-release + From Xavier Perramon + - fix to make creation of SOCK_PACKET possible for a user in + src/dosext/net/net/libpacket.c*. + From Hans + - restrictet the above patch for Xavier Perramon to be legal + only, when 'secure' is off. + - new tools in src/base/misc/utilities.c to handle /proc scanning + - changed cpu/rdtsc detection to above scan tools and + realized check for cpuflag 'tsc' to allow use of the cycle + counter. (in src/base/init/config.c) + - '#if 0'ed prep for the a possible Ctrl-PgUp fix ??? + (src/base/keyboard/serv_backend.c). Reinhard promised to + to fix this better ;-) +97/12/06 ver. 0.67.16.5 unofficial pre-release + From Eric + - new interrupt handling stuff (do_int patch). + - do_int changed such that caller_function is called only if an + interrupt vector is not revectored. + - run_int now handles calls pic_cli() + - In do_irq 'do_int' is called instead of run_int. + - Added a function 'run_caller_func'. + - int08, int09, int_a_b_c_d_e_f, int_16 removed. + - Disabled redirection of the default case for int1c. + - Updated mouse code to use the proper address for redirection the + first time. + - int33: Replied to question about post_interrupt handlers. + - src/emu-i386/do_vm86.c (run_vm86): Sync IF, VIF and the + pic_state immediately after returning from the kernel. + Removed bogus IF/VIF handling in vm86_GP_fault.. + - src/dosext/dpmi/dpmi.c (run_pm_int): Never call run_int now, + always use do_int instead. Moved dpmi_cli out of vm86 path. + do_int handles that chore on the vm86 path and doesn't always + clear the interrupt flag. + - src/base/mouse/mouse.c: Disabled unused support for int 0x74 + - src/base/dev/pic/pic.c (do_irq): do_irq noe calls do_int. + Removed the unnecessary run_int sanity check. + Removed pic_cli, on the way to call do_int as do_int will not + call that itself it needed. + - src/base/bios/bios.S: Slightly modified the default interrupt 0x42 + entry point so that it works in the new scheme. + Modified the entry point of int1c to call into dosemu. + From Hans + - due to some problems with X refresh (why?) made the do_int patch + a compile time option (--enable-new-intcode). I don't expect this + problem to stay for ever, so its only put in base-configure. + ( './default-configure --enable-new-intcode; make' ) + - run_int became 'real_run_int' and, depending on new or old int + code, a macro 'run_int' is pointing to 'real_run_int' or 'do_int'. +97/12/05 ver. 0.67.16.4 unofficial pre-release + From Eric + - bug fixer for the disk code, sometimes disk_close_all called + before disk_init and closing stdin. (src/base/misc/disks.c) + - If the hogthreshold code is disabled _don't_ sleep. + (src/base/async/int.c) + - Fix to make pic_sti and pic_cli more robust. + (src/include/pic.h) + - Modifications to cpu.h in preperation for the do_int patch + From "Robert de Bath" + - fix to allow Ctrl-Alt-Fx keys to be pressed on the Slang & the X + keyboards, don't eat them. (base/keyboard/serv_xlat.c) + From Pasi Eronen (note his new E-mail address!) + - fix for serial stuff in ser_irq.c + - changed E-mail address +97/12/04 ver. 0.67.16.3 unofficial pre-release + From Hans + - further working on keyboard cleanup. In principal no functionality + change was done (except keytable loading), the keyboard code behaves + exactly as before. + - moved all key map related config.xxx stuff into one pointer + config.keytable, which is a struct, that holds all needed + information. The keymaps.c has a list those struct for + keytable aivalable (keytable_list[]), such that this list is + searched in parser.c to set the appropriate config.keytable. + - this new struct also contains 'flags' for special handling + currently it only hold KT_USES_ALTMAP, which know is used to + determin wether the R_ALT handling should be done + (former check against KEYB_US) + - cleaned up the various DEAD_* references, know all is defined + in keymaps.h + - removed double definitions of KEYB_* in emu.h, keymaps.h + - introduced a new kbd layout: 'keyb-user', this one is identical + to 'us', but is intended to hold complete user defined keymaps. + Its know the default, when nothing is specified, so the US table + won't be overwritten, when doing a keytable load. + - new dosemu.conf statement: 'keytable', for more see README.txt + It makes it possible to either change an existing key map + or to replace (load) it completely. It is also possible to + dump all keytables to a (asscii) file, the format is suitable + fo direct include into dosemu.conf. + - deleted + - discrete "dumped" keytables now in ./etc/keymap, which will + be copied on install to /var/lib/dosemu/keymap. Here the user + may change them to fit his/her needs and load them such as + -I 'include "keymap/de-latin1"' + - some cleanup in the lexer.l rules. To avoid clashes between + the special characters "|{}=," and the _not_ quoted strings. + Now you need to quote a string, when it contains those + characters, e.g. file,xxx or file+xxx.c has to become + "file,xxx" and "file+xxx.c" respectively. + - the keymap 'no' now is named 'keyb-no' to avoid name clash with + the boolean 'no'. (in lexer.l) +97/11/30 ver. 0.67.16.2 unofficial pre-release + From Eric + - back ported keycodes stuff to new kbd + ( ...thanks, that makes it possible to delete the old kbd code ) + From Hans + - removed old kbd code and renamed src/base/keyboard/n_* to * + - include/speaker.h missed a '#include "types.h"', it compiled ok + in the past, because NEW_KBD wasn't defined (even when) because + also config.h wasn't included ;-( +97/11/29 ver. 0.67.16.1 unofficial pre-release + From From Ulrich Weigand + - (gnats report files/70) + fix for wrong open mode handling in MULTIPURPOSE_OPEN of mfs.c + From Hans + - fixed misplaced 'free()' in src/base/init/parser.y + - fixed not correctly working '-I keystroke' stuff for new keyb. + (recode(), type_in_pre_strokes() in src/base/keyboard/prestroke.c) + - forceing KEYB_US when in X _and_ not X_keycodes also for new kbd. + ( prep for Eric's keycodes backport and fix for '-I keystroke') + - further rework of commands stuff, reworked commands/detect.h. + NOTE: No longer using '-a' flag of as86 because it results + in strange compilation (call far ptr * does generate a near call). + Changed all *.S which relied on the 'asld' syntax. + - changed macros86.h to use 'br' for JMPL, well it turns out that + this (non-Intel) as86-memnonic _does_ in fact generate a near jump. + ( no docs are telling this:-( ). + - rewrote the simple C files to as86 such that we clean up the + 'precompiled' directory. + - converted dumpconf.asm (MASM) to dumpconf.S, know having dumpconf.sys + instead of precompiled/dumpconf.exe + - converted mgarrot.a86 to mgarrot.S. + - renamed 'chdir.com,getcwd.com' to 'uchdir,ugetcwd' respectivly + (the 'u' stands for Unix) the old names were misleading as the + directories involved _are_ Unix ones (not DOS). + - fixed a bug concerning (u)getcwd, the old code used AX to pass + the size of the buffer to the doshelper, but AX is used to pass + the function code for the helper. Now using CX for size. + - removed all _AX _BX .. usage in command/*.c (too dangerous) + and replaced geninterrupt() with intr(); + - made the (hidden) 'unix -s XXX' working (using msetenv), so you know + may 'transport' Unix-env.variables to DOS. + - 'unix -e' (without further param now will be quiet, so you may + have it in your autoexec.bat to be prepared for something like + 'dos -D-a -E dosapp'. This as an alternative to '-I keystroke' + to launch DOS-applications from the linux commandline. + - changed src/commands/Makefile such that it can compile + precompile/*.exe on demand. Need an already installed DOSEMU + and Turbo-C though;-) +97/11/22 ver. 0.67.16 released + From Eric + - found and fixed a diabolic memory overwriting bug + ( src/base/dev/dma/dma.c, loop limit was 5 instead of 4) + From Hans + - new DOSEMU detection stuff (without even trying to INTE6) + (all *.S -> *.com converted to that) + ... to be continued + - to reduce confusion: ./configure becomes ./base-configure + - new script 'mkconfscript', which is used to build proper + configure scripts (runs autoconf, autoheader and replaces + /bin/sh with /bin/bash) +97/11/20 ver. 0.67.15.1 unofficial pre-release + From Marcus Better + - INT13-Support for more than 1024 cylinders + (bit6..7 of DH building 12 bit cylinder number) + - INT13 IBM/MS Extensions, AH=41h-44h and 47h-48h. + This uses 64-bit logical block numbers. It's documented in + the R.Browns Interrupt List. + From Hans + - Making Marcus' INT13-4096 cylinder mode a dosemu.conf option, + such that nothing old could break. + Flag for the disk statement: diskcyl4096 + - docs rebuild +97/11/07 ver. 0.67.15 released + From Alberto + - replace all error("ERROR...) with error("...), because ERROR is + already prefixed in function error(). + From Hans + - fixed 'error()' such that 'error("@xyz...")' will print _without_ + prefixing 'ERROR:'. (print_exception_info() was printing wrong). +97/11/07 ver. 0.67.14.2 unofficial pre-release + From Alberto + - make USING_NET a configuration option + - fixes to lt-threads (atomic_* name clashes, locking stuff) + - make some compiler warnings go away + From Hans + - small fix for configure.in not detecting correctly broken textutils. + - introduced --enable-experimental to avoid playing JoeUser with + not yet working stuff. + - syncing lt-threads with my and Albertos changes (and vice versa) + Version now 0.4.2 + - DANG and docs recompile +97/11/01 ver. 0.67.14.1 unofficial pre-release + From Alberto + - BIG patch to introduce the willows twin based cpu emulator + ( too much to describe the details here, have a look at + src/doc/README/cpuemu). Alberto did a great job in porting the twin + cpu emulator to DOSEMU! We hope that one day (a) DOSEMU can run + on machines other then the Intel and (b) we can use the emulator + to simulate ring0 protected mode (win95;-). + However, currently its _very_ experimental and only few programms + run, hence it is disabled by default. As it now is in the + distribution we can make efforts to get this baby on its way;-) + - cleanup for the old X86_EMULATOR define (now using it for cpuemu) + - small initial documentation + From Hans + - doing the 'slave work' of integrating Alberto's patch ;-) + ( including copyright verification with Willows ) + - new dosemu.conf options: 'cpu emulated' and 'logfilesize xxx' + - reworked log_printf and 'error', now both are functions and call + 'vlog_printf'. This avoids recursive macro expansion, when + redirecting 'fprintf' to 'error' in the twin code. + - making dosdebug to tell us wether we are on a simulated cpu. +97/10/24 ver. 0.67.14 released + From Alberto + - prep for cpuemu in src/arch/linux/debugger/{dis8086.c,mhpdbgc.c} + - isolated log-printf stuff in headers to avoid name clashes for + cpuemu: new file src/include/dosemu_debug.h + - fixed bug/typo in src/include/vgaemu.h: vga_emu_fault was passing + wrong arg. + From Hans + - adapted DANG_CONF, DANG and docs recompile +97/10/23 ver. 0.67.13.2 unofficial pre-release + From Alberto + - fix for Matrox _and_ 'speaker non-native', lots of port61 fixes. + - fixed bugs in dpmi.c (LWORD was used instead of _LWORD) + - replaced [rep]{in,out}s{bwd} in vm86.c and dpmi.c with calls + to a common set of functions in n_ports.c. + - completed prefix handling for ports stuff, all combinations now + are considered. + - aligned some (lot of) code to the Bitxxx type convention. +97/10/22 ver. 0.67.13.1 unofficial pre-release + From Alberto + - rest of ioport_t fixes. + - typedefs in src/include/types.h now based on + - more opcodes 0F fixes in src/emu-i386/cpu.c. + - cleaned a lot of print format errors. (%lx versus %x) + From Hans + - fixed '<>|' keyboard bug. It turned out MSDOS-6.2 was destroying + the CARRY in its INT15 AH=4F hook. The fix was not to rely on + CARRY=1 when called (forcing it to 1) in src/base/async/int.c + - removed confusion '0.49' out of src/tools/peripher/hdinfo.c +97/10/11 ver. 0.67.13 released + From Eric + - some keyboard fixes to solve problems with the sticky shifts. + - corrected some funny typo's ;-) +97/10/10 ver. 0.67.12.2 unofficial pre-release + From Alberto + - enabled 'new' port code, moved new code (from portss.c) to + n_ports.c and integrated code that ist used by the old code + into ports.c. portss.c is no longer used. + The new code can be enabled via ./configure (--enable-new-port). + - Now ioport_t (from types.h) is used all over the code. + However, for compatibility changed to unsigned int. + - PCI code is now fully enabled. + From Hans + - DANG/README update/recompile +97/10/10 ver. 0.67.12.1 unofficial pre-release + From Alberto + - fixes to CDrom driver (undocumented bit 10 in CD status) + look at the comments in src/dosext/drivers/cdrom.c. + - new config variable 'cdrom' added for more than 1 CDrom + From Hans + - (instead of Alberto;-) did update the README/config + to reflect the above config variable 'cdrom'. +97/09/26 ver. 0.67.12 released + From Eric + - new speaker code. Changes against old code: + Xsupport, variable sound duration (limited to 990ms), + turn-off on leavedos. + From Hans + - DANG update/recompile +97/09/23 ver. 0.67.11.2 unofficial pre-release + From Alberto, + - final fixes for CPU detection, SMP now switches off RDTSC timing. + - final AC-flags fixes (make the FoxPro fix obsolete) + From Hans + - disabled FoxPro fix from 0.67.11.1. + - cleared up usage() in config.c. + - max cardtype now in src/include/video.h (MAX_CARDTYPE), + so no need to take care of the constant in config.c ;-) + - DANG recompile +97/09/21 ver. 0.67.11.1 unofficial pre-release + From Alberto + - fix (workaround) for FoxPro + clearing AC flag in dpmi.c (when trapno = 17) + From Hans + - 'cleaned' misleading usage of config.cpu_type and config.realcpu + cpu_type is what we show to DOS, realcpu is the CPU dosemu works on + Hence -3..-6 do _not_ change realcpu and can't be used to disable + the pentium timer. For this config.rdtsc should be used. + - updated src/doc/README/config to reflect the latest changes. +97/09/20 ver. 0.67.11 released + From Michael Karcher + - fixed bug in src/dosext/misc/xms.c allocating unlimited memory. + From Reinhard Karcher + - fixed bug in n_serv_xlat.c concerning numerical ASCII input + over ALT-keypad. A variable sometimes wasn't initialized. + From Eric + - fixed possibe make recursion in Makefile.main, which happens + to occure with some newer make versions. (looks like the order + of making differs between versions). + From Hans + - made Eric happy ;-) 'cd src; make' now works again as expected. + - temporary disabled Alberto's (0.67.10.3) pentium timer until he/we + find the bug which breaks FoxPro (when it uses XMS/EMS memory, + which it does by default) :-( + To re-enable it uncomment TEMP_DISABLED in the top Makefile. + - Updated DANG_CONF such that it now collects all files, which + contain usefull DANG/SIDOC statements. Look at doc/DANG.txt ;-) +97/09/19 ver. 0.67.10.3 unofficial pre-release + From Alberto + final (except for bug fixing) part of timer patches: + - revision of some comments + - speed detection and override: new param config.realcpu taken out of + /proc/cpuinfo, -3, -4 e.t.c. can only downgrade this value. + - CPU speed rounding to multiples of 30 and 33.3 in the range 60 to + 300MHz (is it enough? maybe 500...) + - replaced all gettimeofdays in timers.c and pic.c with the new + interface. Some needed gettimeofdays() are left though. + - Deleted CLOCK_TICK_RATE because it were same value as PIT_TICK_RATE. + - new code in cpu.c to reasonably emulate 'MOV to/from CRx' (0f xx xx) + The previous code (do_vm86.c, msdos.h) was incomplete. + - using call to int0x06 instead of 'leavedos' in sigsegv.c making + happy CPU detection programs. +97/09/16 ver. 0.67.10.2 unofficial pre-release + From Alberto + first part of timer patches: + - the timer driver itself and the new cpu/speed configuration: + emu-i386/cputime.c, include/timers.h, base/init/config.c etc. + - replacement of the delay loops with the new interface: + keyboard, serial, sound (all the loops using RELATIVE timing). + - documentation (refers to the final timer package) + - timers and PIC have NOT been changed, they still use gettimeofday. + If this patch doesn't break anything, the round uses the real stuff. +97/09/16 ver. 0.67.10.1 unofficial pre-release + From Uwe + - fixing ./configure VERSION problem with textutils < 1.19 + - 'nicer' warning in Top-Makefile ;-) + From Eric, Larry, Alberto (all agree with this ;-) + - disable 'could be dangerous' run_irqs() in base/dev/misc/timers.c + From Alberto + - Makefile.main: 'base/' missing before a 'data.o + - speed up compilation of debugger/dis8086.c by uninlining a function + - fix internaldriver for MouseSystems mouse. + - parser.y: if user says '/dev/mouse', trust him and set the mouse flag + - added pci driver, keyword PCI in /etc/dosemu.conf + - changes to 32-bit port access. + - Matrox driver updates + - WDvga(Paradise) support + - updates to vc.c for Matrox and WDvga +97/09/12 ver. 0.67.10 released + From ramon@juguete.quim.ucm.es + - gnats report misc/58, fixing: + 'Dosemu cannot boot DOS from a partition at an origin > 2Gb' + From David Bourgin + - changes to ./setup-hdimage: + - Use /sbin/fdisk, when /sbin not in the path. + - Offer non-bootable partitions too. + - Move -C option of mdexe to the end of the command to avoid + a shell misunderstanding. + From Hans + - get rid of some '#ifdef __linux__' concerning 'loff_t' in disk.* + - small fix for DANG_c.pl not having the right version after the + configure changes. +97/09/09 ver. 0.67.9.1 unofficial pre-release + From Hans + - reworked the ./configure and Makefiles such that now there are + no Makefile*.in anymore except Makefile.conf.in, which gets + substituted by ./configure and holds all needed configuration + variables for the Makefiles. Makefile.conf gets included only in + the top Makefile and exports all variables to the slave-Makefiles. + - a simple 'make' runs ./default-configure if needed. + ( the user can't forget to run ./configure ) + - no path substitution any more in the Makefiles, hence you + now can rename the directory (or copy it) and 'make' + can run. + - The version has been moved into the file VERSION, which + is parsed by ./configure and the top-Makefile, hence I need + only to change this one file ... and ./configure* is untouched. + - Some not used checks in ./configure have been disabled, + hence ./configure runs faster. +97/07/25 ver. 0.67.9 released + From Alistair + - fixed Makefile.main not to compile threads when not configured. + - tests for the presence of 'libgus' in ./configure (SBEMU on). + If it's found then 'midid' will be compiled with that support, + otherwise it doesn't. + - update to sound docs. + From Hans + - full docs recompile (DANG also) + - mtools version check in setup-hdimage + - some minor changes to mkbindist + - introduced compiletime-settings.stable for making stable releases + out of 0.67 for 'joe user'. (0.66 is freezed now) +97/07/25 ver. 0.67.8.2 unofficial pre-release + From Michael Karcher & Alistair + - second round sound&dma patches + SB: + reworked asynchronous handler ('run_sb') + added 'run_sb' into DPMI loop + added support for MIDI & 16-bit IRQs + added missing function _call_ to sb_mixer_data_read + added extra SB16 Mixer commands + added SB16 DAC & ADC (stub) + added driver call 'play_buffer' [linux] + updated 'write_silence' to use 'play_buffer' + updated 0x1C DSP handling (for broken software) + added 'sb_set_rate' (some lengths are HI/LO, others LO/HI) + added automatic downgrade to SB-PRO + DMA: + fixed FF clear (now does!) + fixed Hi/Lo Byte swap (result of previous FF bug) + uses '|' instead of '+' where possible (quicker ?) + added 'dma_transfer_size' to alter handler's blocksize + added underflow checking to transfers +97/07/24 ver. 0.67.8.1 unofficial pre-release + From Alistair + - first round sound&dma&timer patches + From Jan van.de.Werken + - little bug fix for fossil.c + From Alberto + - small fix for n_X.c to compile without mitshm. + From Hans + - fixed the 'Linux-4.0.0' bug in lt-threads ;-) + - corrected src/doc/README/batch, chapter 13 of README.txt was + garbage. + - made config.X_mitshm = 0 default again. When Steffen fixed + the problems with X-color allocation, we'll re-change it. +97/07/19 ver. 0.67.8 released + From Hans + - lot of work on DANG (hey Alistair, hope you don't mind) + - made the keywords also accept 'SIDOC' instead of 'DANG, because + else I would have to maintain two versions of the threads + package (which I mainly use at office). Yeah, using the DANG + compiler now for my work at office ;-) + - Added some new features to DANG (have a look at chapter 12 and + 13 of DANG.txt) + - edited _all_ DANG_*_MODULE entries such that now the contents + _really_ will appear in the 'Infomation' sections of DANG.txt. + (before all those valuable comments were ignored :-() + - new threads version: 0.4 +97/07/18 ver. 0.67.7.1 unofficial pre-release + From Steffen + - new (nearly) full implementation of vesa-2.0 + - real true color support for DOS-applications (note: not to confuse + with emulation of 8bpp on a truecolor X-server) + - LFB support. + implemented special DPMI 0x0800 function (map physical) + to fake our virtual LFB. + (this need more work, too dangerous, --Hans) + - new dosemu.conf option for X {}: + lfb on/off, pm_interface on/off, defining new VESA modes. + look at doc/README.txt chapter 2.2.8. + - made 'X {mitshm}' default on, but can be switched off via + 'X {mitshm off}'. + - gammcorrection now uses _only_ integer, no FPU usage any more. + - integrated old vgaemu_inside.h and vesa.h into vgaemu.h + - fixed a bug in arch/linux/async/signal.c, wrong activation + of resize in text mode. + - updated README/X.sgml and README/config to reflect all recent + X changes. + From Hans + - fixed Makefiles for odd behave on as86 generated objects: + bios.s, vesabios.o (tmp files dependencies went berserk) + - fixed README/Makefile so sgml2tex won't screw up your screen;-) + - changed docs to reflect the fact, that we now need libc-5.4.7 + at minimum (due to mremap) +97/07/05 ver. 0.67.7 released + From Alberto + - fix for n_serv_xlat.c, grey +/- on num keypad. + - fix for mhpdbgc.c, adjust segment limit value following the + granularity when disassembling. + From Marty + - some fixes to ./configure so it doesn't generate '-I' and + and barf the compiles. + From Hans + - kludge in src/dosext/misc/emm.c to work around kernel bug + (/proc/self/mem mapping again) +97/07/03 ver. 0.67.6.1 unofficial pre-release + From Alistair + - big docs rebuild. Nearly all README.s are now 'sgml' and the + source is in ./src/dos/README. In the ./doc directory there + these appear (compiled) as README.txt and README-tech.txt. + From Hans + - udapted the remaining docs, Makfiles and ./configure + to reflecting Alistairs changes. +97/06/29 ver. 0.67.6 released + From Hans + - fixes to EMM code (these bugs were long time in :-( ) + The bug was introduced as HJ Lu recommended to use valloc() instead + of malloc() because of the page alignment of the malloc request. + So far so good, but we forgot that we do a realloc() and even + a free() on these allocations, ... and this _not_ complient with + the GNU specs of valloc(). In the old libc though it nevertheless + seemed to work, but not together with newer libc: The 0.66.6 + binary distribution _is_ compiled against libc-5.4.23 and with + this the old EMM-test program that I once got from Jim is + crashing. + In addition I also saw, that we did no proper handling for cases + when the new size for realloc() was ZERO. The realloc() returned + pointer isn't pagealigned (I even guess that realloc() should + not given a new zero size). + As a fix we now are using mmapp() (/dev/null) and mremap() also + for EMM. Though I could not test it under all conditions it + atleast fixed the bug for me. ... and it should be faster anyway. +97/06/28 ver. 0.67.5.1 unofficial pre-release + From Michael an Reinhard Karcher + - fix for extended key-code (E0) in new and old kbd code. + - fix for mfs.c: a DOS app that wants to read a newly created file + without reopen failed. Fix was to set the openflags in the SFT + to read/write instead of writeonly. ( same open mode mode as for + the linux file). + From Alberto + - fixed typo in bios.S (wrong SIM_INT(15) instead of SIM_INT(0x15)) + From Hans + - some fixes to lt-threads + - kernel version dependent include of + - fixed misaligned force_stack_expand, when using -malign-double + ( ... guess who jumped into this trap and found out what was + wrong? Alberto! ;-) + - changed SIG_ACQUIRE to SIGUSR2 (src/include/emu.h) because + the SIGUSR1 collides with SIG_SUSPEND_WAKEUP in lt-threads. + - made new kbd the default again (in-out-in ... what comes next?;-) + - in src/dosext/dpmi/memory.c DPMIrealloc() use mremap() instead of + mmap()/memmove()/munmap(). Should be faster. +97/06/21 ver. 0.67.5 released + From Hans + - introducing threading into DOSEMU using Linux-clone :-) + Currently its only used for fixing the floppy ioctl that + will return with -EINTR (we normally switch off SIGALRM). + The main reason I put that stuff in is to show the team _how_ it + can be used within dosemu. So, Alistair its your turn now to + make the DMA stuff using it. And Steffen also may look how + we can make the X-update a thread. And of course Alberto now + has a base for his new PIC stuff. + Conclusion: Currently it should be considered as an example and + if we later come to the decision, thats not enhancing DOSEMU + enough, we may back out it. + BTW: its only activated by --enable-threads. + - Made the old kbd again the default, because it breaks some of + the older DOS apps (extended code E0 again, forgot that we didn't + fix that yet, will come next). +97/06/19 ver. 0.67.4.3 unofficial pre-release + From Alberto + - Support for Matrox-Millenium ( video { ... chipset matrox } ) + From Hans + - setting new kbd to default for default-configure +97/06/19 ver. 0.67.4.2 unofficial pre-release + From Steffen + - new X-update code + - gamma correction ( X {gamma 100 } ) + - virtual memsize for vgaemu configurable ( X {vgaemu_memsize 1024} ) + - updating DANG for X and vgaemu + From (multiple sources on linux-msdos ;-) + - fix typo in src/include/linux/vm86.h (missing #endif) +97/06/15 ver. 0.67.4.1 unofficial pre-release + From Steffen + - fix for mitshm autodetect (src/env/video/X.c, .../n_X.c) + From Michael Guennewig + - fixing invalid disk-geometry-information for drives tracks > 256 + - fixing invalid hdimage's for images with more than 65535 sectors or + (>=32MB) in mkfatimage16.c + From Michael Harnois + - some more work to make DOSEMU compile under glibc + From Eric + - fix for the (infamous) have-to-type-ESC-twice bug in the new kbd, + as it turned out, we forgot switch stdin into O_NONBLOCK mode. + (many kudos to Eric;-) + From Hans + - make target 'bindist', so I'll never forget the right params for + mkbindist (especially /usr/doc/dosemu ;-) + - further enhancements to mkfatimage16.c (in addition to Michael's) + It turned out, that the hdimage was too inflexible for bigger + sizes and made a lot of trouble. + - new option -k: give size of hdimage in Kbytes. This adapts + the geometrie automatically to what is needed for the size. + ( heads, sectors_per_track are adjusted ) + - new option -f: give the filename of the hdimage instead of + writing all to stdout. + - new option -p: when option -f also is given, pad the hdimage + with zero up to the exact filesize. This is required for + mtools-3.6 and when mounting the hdimage via /dev/lo. + The actual disk usage will be lower, because padding is realized + by makeing a big hole at the end. + - updated the man page + - fix for IPX (gnats bug report files/46 from kees@schoen.nl) + setsockopt(sock, SOL_SOCKET, IPX_TYPE, ...) wrongly using + SOL_SOCKET instead of SOL_IPX in src/dosext/net/net/ipx.c + - fixed mkdexe so it can cope with DOSAPP not beeing an absolute path + - fixed INT15-AH=4F not spawning a hooked TSR (src/base/bios/bios.S, + src/include/macros86.h), and backing out the incomplete fix of 0.67.4. +97/05/31 ver. 0.67.4 released + From Alberto + - disable ctrl-alt-del. (as its broken, better disable it) + - deleted (unused) dos_ctrlc() + From Ulrich Weigand + - Several bugfixes to new keyboard code + - Raw client: ALT+Letter - Translation on non-US keyboards + - X client: didn't set AltGr flag in BIOS shiftstate (non-US) + - All clients: cursor block keys should yield ascii code 0xE0 + - Hooking INT 15 AH=4F did not work +97/05/31 ver. 0.67.3.4 unofficial pre-release + ( taking over stuff from final 0.66.5 ) + From Hans + - untill we can detect the availability of static libs, we + re-disable static linkage. ('linkstatic off') + From Michael an Reinhard Karcher + - fix for wrongly placed leave_priv_setting() in ipxglt.c + From Christian Kumpf + - fixed bug in dexe/mkdexe not proper handling option -d + From Hans + - changed mkdexe to accept *.tgz + - LDFLAGS now '-Wl,-warn-common' to see were we have name clashes + - repaired the linker warnings resulting from -warn-common by + - properly useing EXTERN in vga.h, serial.h + - removed multiple definitions of 'serial_t com[MAX_SER];' + - removed overriding 'struct debug_flags d' definition in config.c + - fixed IPX not closing sockets on leavedos() + ( gnats report misc/39 from Kees ) + - adapted /proc/meminfo (dpmi/meminfo.c) scanning to Linux-2.1.41 +97/05/24 ver. 0.67.3.3 unofficial pre-release + From Alberto + - timers.c: reorganized pit_latch + - simplified access to pit[latch] with a pointer + - changed mode test from a switch{} to an if..else + - merged code for modes 2 and 3 + - implemented output pin (status bit 7). A request for status + calls pit_latch with mode flag 0x40; the output pin status is + recalculated and the mode flag changed to 0x80. The following + read will get the complete status byte, then the mode flag will + be reset from inside pit_inp. + - this patch also corrects a bug, ndiags now runs fine. + From Hans + - made 'linkstatic' and 'tmonoton' default + (static binaries have better performance and less timer problems) +97/05/23 ver. 0.67.3.2 unofficial pre-release + From Alberto + - timers.c, pit.c: use NEVER instead of -1 in monoton timing + - timers.c: removed duplicate calc of 'ticks' + - timers.c (pit_inp): avoid changing read_state for the special latch + status command; otherwise next read will get MSB instead of LSB. + - cdrom.c: avoid binary sector dumps into log file, use ASCII + reworked debug messages a bit + - patch to serial code to activate transmit FIFO + From Hans + - fixed bug in src/dosext/drivers/cdrom.c (ebx twice set), + that Alberto pointed out (aaah, these eagle eyes ;-) +97/05/23 ver. 0.67.3.1 unofficial pre-release + From Gloriano Frisoni + - patch to make winsocket stuff work under win31 + Using trumpet winsock, netscape, eudora, mirc, free agent e.t.c + now run under dosemu. For more read doc/README.Winnet +97/05/19 ver. 0.67.3 released + From Alberto + - some more comments on DIRECT_DPMI_CONTEXT_SWITCH + - removed bogus RCS printout in src/dosext/misc/xms.c + - some remaining fixes on USE_INT_QUEUE + - some fixes in DPMI + - low 12 bits in limit > 1M was not checked + - forgotten 'volatile' in do_LAR + - logging of ingnored 0x0506-0x0508,0x0700,0x0702,0x0703 + DPMI-functions + - asm code for copy_context under DIRECT_DPMI_CONTEXT_SWITCH + - DPMI page fault should do leavedos() + - dead loop detection for 'cli' in dpmi_fault() + From Steffen + - (some) fix to cut&paste in src/env/video/n_X.c + From Eric + - src/base/keyboard/n_keyb_X.c: Added some missing key + definitions. Needed to get the keypad keys working in X when + numlock is off. +97/05/19 ver. 0.67.2.1 unofficial pre-release + ( taking over stuff from pre-0.66.5 ) + From Alberto + - fixed bug for DIRECT_DPMI_CONTEXT_SWITCH, copy_context was + forgotten at some places. (src/dosext/dpmi/dpmi.c) + From Ingo Brueckl + - bugfix for 'make install' not installing man/*dos*.1 + From Michael an Reinhard Karcher + - fixed bug for DPMI src/base/async/int.c, INT2f AX=1686 not + correctly reporting CPU status. + From Hans + - fixes and enhancement for bootable hdimage generation + - dexe/extract-dos can detect DOS types. + - setup-hdimage makes use of DOS type detection. The following + DOSes now can be made bootable for DOSEMU via setup-hdimage: + PCDOS, NWDOS, MSDOS, WIN95, FreeDos + - fixed extract-dos to take windows/command.com when WIN95 + - new option for mkdexe: '-C comcom', here you can define + an other command.com (e.g.4dos). + - introduced 'write permissions' in DEXE files so that the + DOS-app can (isolated) write to files within the DEXE, + even if the file permissions don't allow that. This is + a workaround for a general problem with DEXEs, look at + ./doc/README.dexe for more details. + For this, dexeconfig has a new option -p {w|W} + - new config option for DEXE: xdosonly. When set it terminates + dosemu, when no X available, else it forces xdos. + - bug fix for 'forcexdos' + - force a BootC unconditionally for DEXE + - increased number of allowed disks to 16 (there was report of + a user who had 11 disks ;-) + - fixed a bug in the 'class' stuff (could not access within scope + of /etc/dosemu.conf) + - fixed parser not aborting, when parserror in -I or DEXE. + - added (implicit) setting of configuration variable h_ + This can be checked in /etc/dosemu.conf to restrict access for + given diskless machines, that mount a dosemu containing FS. + - added two new dosemu.conf statements: + - abort which prints message to stderr and leaves dos. + - warn which just prints a message into the logfiles + using c_printf. + - added the tiny 'ezedit' editor to src/commands/precompiled + - Updating the docs _a lot_ ;-) + - added doc/README.lredir + - added man/mkfatimage16 + - reworked Quickstart to reflect setup-hdimage, moved description + of the old method to doc/README.oldboot + - removed etc/BOGUS-Notes (was quite bogus;-) + - added a src/tools/QuickStart.bindist, which will be used for + making the binary distribution (see below) + - e.t.c + - reworked ./etc/config.dist again and added a bit more comments ;-) + - we now have the following examples: + ./etc/config.dist + ./etc/dosemu.users.easy + ./etc/dosemu.users.secure + - config.dist is generic, i.e. it checks for config variables + set in dosemu.user such as + guest + secure + vbootfloppy + - fixed a bug (report: Hans-Joachim Baader ) + in src/base/init/parser.y, parsing dosemu.users crashed on empty + lines. + - Finally assembled a binary distribution. Put all stuff needed for + that in to src/tools. In principal you just have to execute + ./src/tools/mkbindist -m -s -d usr/doc + and after this will have a complete tree in /tmp/dosemu- + that only needs to be tar'ed. + - changed Makefile.main.in such that you now can specify a directory + into which the installation will be done (needed that for + mkbindist). Example: 'make "INSTROOT=/tmp/dosemu-bin" install' + - changed default-configure so that it accepts further ./configure + options such as './default-configure --host=i386-unknown-linux' +97/05/10 ver. 0.67.2 released + From Eric + - Fixed a bug where shift number pad key only worked if _both_ left + and right shifts were pressed (src/base/keyboard/n_serv_xlat.c) +97/05/10 ver. 0.67.1.3 unofficial pre-release + From Alberto + - isolate and undefine unused code: hlt and int_queue + - missing flush_log() in src/emu.c +97/05/05 ver. 0.67.1.2 unofficial pre-release + From Alberto + - fix for inconsistent (EMU_)MAX_IO_DEVICES + - Microsoft mouse protocol fixes. It was erroneously merged with + the MouseMan 3-4 byte variable length protocol, while Microsoft + has a 3-byte fixed protocol (A.Rubini confirmed). + - some comments & small fixes (continued) +97/05/05 ver. 0.67.1.1 unofficial pre-release + From Alberto + - fixes for typos + - some comments added + - cleaned extra dependencies in makefile, removed pic include dir + - very small bug fixes: + - exception 16 feed to IRQ13, src/arch/linux/async/sigsegv.c + - usleep for hogthreshold changed to INT28_IDLE_USECS + - added INT10 AH=CC (for NC 5.0) in src/base/bios/int10.c + -From Eric + fixed trashing AL in src/base/bios/bios.S (INT09_dummy_start) +97/05/03 ver. 0.67.1 released + From Steffen + - cool new X-stuff: + - X mode support (doom now displays right) + - first simple 4plane mode (very slow) + - 24/32 bpp possible + - linear/bilinear interpolation for bpp >15 + - resizing of graphics windows now possible + - DAC-updates also in textmode possible + - a lot of new config options (see ./doc/README.config) + From Hans + - made the new X-code a configure option (--enable-new-X), + because Steffen apparently optimized it for 'games' ;-) + (which indeed run faster), but the new stuff (atleast for win31) + has performance problems with 'normal' graphics applications. + ... still searching what's causing this trouble :-( + - some small fixes and 'compatibility adjusts' to Steffens patch ;-) + (the new X-code should now work atleast as stable as the old one) + - INT10 now handles AX=1015 and AX=1017 (these missing ones broke + Steffens new DAC-update in X-textmode) +97/05/03 ver. 0.67.0.3 unofficial pre-release + From Doug Dougherty, Chicago, IL + - cmdline DOS tool to feed in info from the commandline + into the DOS-environement. + (see src/command/README) + From Eric + - Enabled the speaker/timer code of the NEW_KBD_CODE in + src/emu-i386/ports.c (inb) (outb) + - Unconditionally remove the original copy of dos before relinking. + in src/arch/linux/Makefile.main.in (dossubdirs) + From Hans ( ... and some ideas from Alistair ;-) + - new cool configuration stuff. Dosemu.conf can now parse + conditionally and include files. + - In dosemu.users variables can be set, which you later can test in + dosemu.conf. For more see ./doc/README.config. + - can define a variable also on the commandline (option -u). You + then may check it in dosemu.conf and behave differently. + - removed all comments in etc/config.dist and moved the information + into ./doc/README.config. +97/05/02 ver. 0.67.0.2 unofficial pre-release + From Alistair + - fixes and enhancements to gnats/send-pr + From Bart + - fix for a multicasting bug in dosnet.c + - warning in libpacket.c.multi, when insufficient privilege + From Karl Kiniger + - some more enter_priv* brackets within src/dosext/net/net/ipx.c + and src/dosext/net/net/ipxglt.c + From Uwe + - some 'secure on' warning, and a errorcode return fix (DPMI) + - (needed tools for developing win31 mouse.drv) some doshelper changes: + - overall symbolic names now + - new DOS_HELPER_DOSEMU_CHECK checks wether running under X + - new DOS_HELPER_PRINT_STRING lets DOSapp print a string into + our logfile. + From Hans + - first round to make a binary distribution (static). + A complete tree gets generated via ./src/tools/mkbindist. + We choose statically linked binaries, because of the many + different libc versions floating around. Not all of them are + down/upward compatible and some even aren't useable for dosemu. + ( ... and surpise, the static binary runs a bit faster ) + - new ./configure option: --enable-linkstatic +97/04/30 ver. 0.67.0.1 unofficial pre-release + From Hans + - further enhancements to DEXE stuff + - hdimages containing DEXE get a magic "\x0eDEXE" + - configuration is now (hidden from file access) put into + cylinder 0 (behind the MBR) + - dosemu now handles DEXE loading itself ( option -L) + a 'dosexec' sym-linked dos sets -L option (like xdos does -X) + - a DEXEs hdimage is always drive C, if not explicitle allowed + via 'dexe { allowdisk }', no other disk will be available + even if defined in /etc/dosemu.conf + - Option -F is forbidden for DEXE, so the security settings + in /etc/dosemu.conf can't be disabled. + - 'secure on' can be forced in /etc/dosemu.conf for DEXE, + even if 'off' otherwise. + - 'dexe { forcexdos }' forces xdos, when DEXE is run on X. + This can be included in DEXE-configurations, when the + application requires graphics and would bomb on Slang otherwise. + - New tool: src/tools/periph/dexeconfig, which inserts/extracts + the configuration file from a DEXE + - updated dexe/mkdexe to reflect the changes +97/04/20 ver. 0.67.0 released + From Hans + - Opening the new development phase + - no further changes except the version number increase +97/04/20 ver. 0.66.3 released + From Hans + - new hdimage generation stuff, need mtools-3.6: + ./setup-hdimage makes a bootable hdimage from the DOS the user + has already installed under native DOS + I tried this with the following OSes: + FreeDos, MSDOS-6.2, WINDOWS'95. + Other DOSes should also work, when the names of the + system files are given on prompt. + (Next step will be to automatically transform the + config.sys and autoexec.bat ;-) + ./dexe/mkdexe generates a direct executable DOS-app (hdimage + containing just one app). The extension is *.dexe + ./dexe/dosexec executes a *.dexe + ./dexe/do_mtools symplifies the access to a hdimage + ./dexe/extract-dos takes the bootsector and the system files + out of a dos partition or a hdimage + All this stuff is described in ./doc/README.dexe and the + Quickstart. +97/04/17 ver. 0.66.2.2 unofficial pre-release + From Alberto + - fix for INT8 rescheduled to fast in DPMI + From Hans + - new dosdebug commands: 'log' and 'dump' + With 'log' you can change/display the debug-logging flags such as + 'log +M-k' on the fly within dosdebug. + With 'dump' you may write any piece of allowed dosemu mem to + a file, such as 'dump cs:100 1234 /dosc/xxx.com' + (wishes from Grant R. Guenther, the great parallel ZIP hacker ;-) +97/04/16 ver. 0.66.2.1 unofficial pre-release + From Hans + - 'keystroke' feature now also available for new-kbd + From Alberto + - fix for src/base/misc/priv.c (forgotten return) +97/04/12 ver. 0.66.2 released + From Alberto + - minimal patch needed to fix the mode-0 timer bug for + MONOTON_MICRO_TIMING + From Hans + - added direct bootable 'tiny' FreeDos etc/hdimage.test. + This can be started via ./first-test. For more have a look + at ./Quickstart and ./contrib/dosC/readme.dosemu. + - Added option -b to src/tools/periph/mkfatimage16.c in order + to allow inserting the FreeDos bootsector. + - fixed a remaining security hole: + 'secure' was off by default and when a user uses -F it + was left 'off' :-( + - udapted docs. +97/04/09 ver. 0.66.1.5 unofficial pre-release + From Hans + - added 'keystroke' feature, you may now feed DOSEMU with key + strokes, that are automagically typed in as if from the keyboard. + dos -D-a -I 'keystroke "\F8;\p200;6" + Will press F8, then wait 2 seconds, then type 6. + For more details see etc/config.dist and doc//README.batch + From Marty + - use gawk instead of awk in setup stuff, else 'next' isn't supported. + ( gnats report misc/13 ) +97/04/04 ver. 0.66.1.4 unofficial pre-release + From Hans + - complete rework of the privs stuff, now we _really_ can + run as user, and this is now the default! + (note: run as user means that dosemu mostly has user privs) + For more details see ./doc/README.runasuser and + src/base/misc/README.privs + - updated the docs concernig 'run as user' and new privs stuff + - Fixed some warnings in lexer.l and parser.y +97/04/04 ver. 0.66.1.3 unofficial pre-release + From Alberto + - (hopefully) final fix for >2G over mfs. + From Marty + - search for slang.h in /usr/local/include + From Hans + - fixed gnats report memory/10 + (rep. by David E. Kingsley ) + set XMS_DRIVER_VERSION to 0x0301 +97/04/04 ver. 0.66.1.2 unofficial pre-release + From Uwe + - cleaned up keyb_layout presetting in src/base/init/parser.y + - defaulting to KEYB_US now, when under X and _no_ X_keycode + From Marky (hi;-) + - changed his non-working mail addresses +97/04/04 ver. 0.66.1.1 unofficial pre-release + From Hans + - added a DOSEMU-special MBR to hdimage. + This is the world premiere of MBR running in 32-bit protected mode;-) + ( no need to use 'fdisk /MBR' any more ) +97/03/28 ver. 0.66.1 released + From Hans + - added a new commandline option: -I + Wow, is that powerfull!! ;-) It makes a lot of people happy + that complained about inflexible configuration (for a more + detailed description read man/dos.1). It makes the following + (and much more) possible: + dos ... -I 'video { mda }' + dos ... -I "`cat my_other_dosemu.conf`" + dos ... -I "`my_special_config_script.sh`" + hmm... waiting for people reporting it has security issues ;-) + (but you before report them: -I runs under the same secure level + as .dosrc) + - fixed a security hole: user could disable 'secure on' :-( + - updated the docs + From Alistair + - fixed gnats report misc/9 (rep. by mharris@blackwidow.saultc.on.ca) + (error while running setup-dosemu) +97/03/27 ver. 0.66.0.4 unofficial pre-release + From Alistair + - made sound a disable option in /etc/dosemu.conf ('sound_emu off') + (else it may clobber an IRQ, if configured but not used) + From Philip.Blundell@pobox.com + - some compile fixes for glibc +97/03/27 ver. 0.66.0.3 unofficial pre-release + From Bart Hartgers + - Made dosnet.c running under 2.0.x kernel (was broken) + From Hans + - again fixes to ./configure & slang.h :( +97/03/26 ver. 0.66.0.2 unofficial pre-release + From Soos Peter + - added support for hungarian keyboard + From Hans + - fixed gnats report misc/8 (rep. by Ambrose Li) + In configure, if 'no X' the XINCLUDES was preset with a comment + (what joker invented that?) such as "# no special path needed". + This made the following -I...slang.h invisible in INCDIR. +97/03/26 ver. 0.66.0.1 unofficial pre-release + From Hans Zoebelein + - fix for error printout, when reading a write protected floppy. + From Steffen + - fix typo in priv_drop() + From Andries.Brouwer@cwi.nl + - fix for ./configure to detect slang.h correctly + - making rm -f `find ...` not complaining, when nothing found + - small 'warning avoid' in src/emu-i386/portss.c + From Hans + - fix for gnats report misc/4 + When no XINCLUDES is set, it left a stale '-I' + - fix wrong gcc check in ./configure (reported by Jeff Epler) + checking now for $GCC=yes +97/03/21 ver. 0.66.0 released + From Hans + - fix to ./configure, -without-x gots messed up in 0.65.99.23 + - Docs update + +-------- + +97/03/20 ver. 0.65.99.26 unofficial pre-release + From Alistair + - DANG cleanup, added DANG-compiler + - recompiled DANG.sgml --> DANG.txt (-- Hans) +97/03/20 ver. 0.65.99.25 unofficial pre-release + From Alistair & Rutger + - For 0.66 we have updated many areas and re-organised others to + make it easier to maintain. + Many thanks to David Brauman for finding so + many bugs + - new sound stuff. 'midid' reworked, + goes into directory src/arch/linux/dosext/sound/midid + Compile it with 'make midid' + - Added in support for the handler to reject the completion. +97/03/19 ver. 0.65.99.24 unofficial pre-release + From Alberto + - fix for 'partitions >500M' part 2 (wrong sector rounding) + - Fixes and correcter calculations for config.freq, which can + be set in dosemu.conf to adjust what dosemu takes for a second. + (units in DOS timer clocks, 18 make one second). TIMER_DIVISOR + is the constant (6) to make one Unix-tick (at 100Hz) out of the + DOS-ticker. +97/03/17 ver. 0.65.99.23 unofficial pre-release + From Hans + - new targets in main Makfile: docs, docsinstall, docsclean + From Pavel Kankovsky + - fixes for X-detection + From Steffen (realized by Hans;) + - emulate a down counting 'diskette motor timeout' + old games seem to rely on that. + From Alberto: + - small mouse fix (320x200) + - discovered that int2f,ax=110c fails for partitions >500M, because + it can't set cluster size>8. Here is my fix, which should also avoid + overflows on >2G disks. +97/03/16 ver. 0.65.99.22 unofficial pre-release + From Hans + - docs move around: + all doc source go into src/doc/DANG, src/doc/HOWTO + made Makfiles to generate 'nice' docs. + You have to 'cd src/doc' then do 'make' + To install the *.txt into ./doc type 'make install' + NOTE: the patch to this release is a patch-*.tgz, what means, + you have have to un-tar it into the ./dosmemu directory and then + type 'sh tmp/do_patch'. + This will _first_ move the files, and then apply the patch. +97/03/15 ver. 0.65.99.21 unofficial pre-release + From Uwe + - take version date out of ./Changelog + From Alberto + - 'make pristine' removes 'gen.log' + From Alstair and Rutger + - new sound stuff +97/03/14 ver. 0.65.99.20 unofficial pre-release + === CLEANUP-Release === + From Hans + - first round 'JES --> Hans' doc changes + - new README.batch + - preparation for 0.66 (version changes in docs) + - check DPR a bit, but the rest is to Alistair ;-) + - removed all RCS headers (not using any more) + - well, and found that compiling without VM86PLUS was broken, + fixed it in parser.y + From Alberto + - lots of typos fixed (collected cleanups) + - some more debug prints in pic.c +97/03/14 ver. 0.65.99.19 unofficial pre-release + From Alberto + - 1st arg of log_printf becomes 'int' + - (some) fix for the inte6 problem (80x50) + - missing HLT_OFF in DPMI 'free realmode call back address' (0x0304) + - Without this CDs can't be changed: + force media_changed, when ejecting (src/dosext/drivers/cdrom.c), + _realy_ do CDROMRESET in cdrom_reset() +97/03/12 ver. 0.65.99.18 unofficial pre-release + From Alistair + - Further gnats enhancements + From Hans + - Because 'midi' always was created in the default directory (which + also is the name of the 'midi' executable ;-) ), I + renamed the 'midi' file to /var/run/dosemu-midi, removed the + 'create' on open and checked for 'is open' when writeing to it. + Updated README.sound accordingly. + From Uwe + - second try (;-) to make compiling on NFS mounted directory + possible +97/03/11 ver. 0.65.99.17 unofficial pre-release + From Alberto + - deleted 'rm modules' in src/arch/linux/Makefile.main.in + (obsolete) + - fixed 'regs' parsing in dosdebugger. + - banner now prints correct time + - fixed relocation of INT42 (must be a copy of INT10) + - call setmode() of video driver only if pointer valid (int10.c) + - disable -Y -Z, when X not configured. + - some 'close mouse' fixes + - temp fix to dpmi.c int31 just to avoid hard reboots + - fix for auto-mitshm-disable stuff in X.c + - corrected wrong (port,value) parameters in s3.c + - do not debug while VC is not active (in src/env/video/vc.c) + - removed some stale files + - avoid pktdrvr.h to be multiple included + From Hans + - removed stale references to emusys.h + - elaborated the description of the 'ttylocks {}' option in + etc/config.dist. +97/03/08 ver. 0.65.99.16 unofficial pre-release + From Steffen + - Added code to turn off MIT-SHM for network connections. + From Uwe + - small additional debug out for DPMI +97/03/07 ver. 0.65.99.15 unofficial pre-release + From Alberto + - cleanup for CFLAGS in configure(.in) + - corrections in etc/config.dist + - missing exception 16 handling in src/arch/linux/async/sigsegv.c + - various fixes in src/base/async/int.c, temp fix for Ctrl-C bug + in Djgpp (int 23 revectored and caputered). + - changed 0/1 to REVECT/NO_REVECT + - cleanup of usage message in src/base/init/config.c + and fix wrong cardtype number + - fixes to buffered debug out (log_printf). + - mouse fix (serial/int14.c) + - fixed 'hlt' optimization in run_dpmi() + - cleaned up 'case 1..2:' to 'case 1: case 2:' in various places + - in_dos_21 warnings fixed + - some mfs fixes. + From Thomas Winder + - struct sigaction fixes for glibc-2 in src/arch/linux/async/signal.c + From Hans + - fix for default-configure + - made 'irqpassing' a synonym for 'sillyint', because users + apparently get confused. Also adapted config.dist +97/03/07 ver. 0.65.99.14 unofficial pre-release + From Alberto + - Fix for the mouse garrot helper: using INT28_IDLE_USECS + - fix for non compileing new kbd (unintentionally reverse patch + at state of 0.65.99.4 in conjunction with priv_* cleanup) + - remove src/dosext/dpmi/kernel.diffs (obsolete) + - remove src/Configure (broken, obsoleted by Alistairs stuff) + - added rebuild script + From Hans + - added ./set-permissions, which sets all exec-bits for all scripts + (so when upgrading per patch, this scripts gets it right) + - added ./default-configure, this script should be used instead + of ./configure, because it interpretes the compiletime-settings + which can be changed by Alistairs setup-dosemu stuff. + - changed setup/compiletime_setup.sh so it uses ./default-configure + to interprete the various menu variables (have now only one place + to take care of it;-) +97/03/07 ver. 0.65.99.13 unofficial pre-release + From Alistair + - fixes for setup & gnats + (Yeah, now it works fine; and Alistair has time for his sound ;-) + From Hans + - send-pr gets dosemu-version out of ./configure.in + - stole lxdialog, named it 'demudialog' and adapted setup gnats + to use that. (normal dialog shows a strange handling, when used + for Alistairs stuff) + I hope Alistair will enhance 'demudialog' to fit our needs + again better ;-) +97/02/29 ver. 0.65.99.12 unofficial pre-release + From Alistair + - setup stuff from 0.65 + - gnats stuff from 0.65 + From Uwe + - New dosemu-HOWTO.*, EMUfailure.txt + (deleted EMUsuccess.txt) +97/02/29 ver. 0.65.99.11 unofficial pre-release + From Alberto + - Fixes for 'unix.com' + - little PIC fix: maybe it is better not to blindly decrement timer + values if they are set to NEVER. +97/02/28 ver. 0.65.99.10 unofficial pre-release + From Alberto + - Use a big buffer for debug messages, this doesn't slow down + dosemu, when debugging. + From Hans + - Made the above 'big buffer' an item in /etc/dosemu.conf: + 'logbufsize ', default is _no_ buffering. + - Fixed ./configure.in, so it now doesn't use -fomit-frame-pointer + when configured for debugging. +97/02/27 ver. 0.65.99.9 unofficial pre-release + From Adrian Sun + - Make dosemu compile under glibc 2.0.1 + - Small IPX bug fix: have the initfile.c functions return on a failed + open. Workaround for missing IPX_TYPE = 1 in glibc headers. + - convert a missed SignalHander -> __sighandler_t +97/02/27 ver. 0.65.99.8 unofficial pre-release + From Uwe: + - base/misc/disks.c, include/disks.h, tools/periph/hdinfo.c + Use llseek on Linux to allow access on Files greater 2.1 GBytes + - made 'make install' on NFS mounted FS possible + (compiling as user, installing as root) +97/02/27 ver. 0.65.99.7 unofficial pre-release + From Alberto + - Fix for debugger, recent 'nm' has 16 hexdigits for addesses + From Hans + - made the path of dosemu.map defineable in /etc/dosemu.conf: + 'dosemumap ' +97/02/25 ver. 0.65.99.6 unofficial pre-release + From Alberto & Hans + - workaround for bug in kernel vm86plus that Alberto found + (fixing kernel patch did reach Linus already;-) + We will remove the workaround when time goes by + - added runtime kernel version check ( currently only used + by the above workaround) +97/02/23 ver. 0.65.99.5 unofficial pre-release + From Alistair & Rutger + - Updated SB DSP code and fixed some bugs in detection - Alistair + - Re-hacked ports.c so the DMA code works again. GRRRRR - Alistair + - Added first pass at MPU-401 code - Rutger +97/02/22 ver. 0.65.99.4 unofficial pre-release + From Eric Biederman + - priv_on/off/default cleanup +97/02/21 ver. 0.65.99.3 unofficial pre-release + From Alberto + - Support for CIRRUS chipsets (some code comon with S3: v_8514) + From Hans + - fixed typos (mine) for ATI: in Makefile.in and lexer.l +97/02/20 ver. 0.65.99.2 unofficial pre-release + From Josef Pavlik + - Added ATI support (untested --Hans) + This patch is for VGA ATI Mach 64 (probably will work with Mach8 + and Mach32 too). Set "video { vga console graphics chipset ati }" + in dosemu.conf. Resolves problem with ioports >0x3ff and saves + registers which contains clock speed. Without this patch dosemu + changes clock speed of VGA. +97/02/20 ver. 0.65.99.1 unofficial pre-release + From Alberto + - Fix PIC bug (replacment for old ALBERTO_KLUDGE) + From Hans + - fixed a small typo (yes, it was mine), + ( did use close(f) instead of fclose(f) :(( ) +97/02/17 ver. 0.65.99 unofficial pre-release, + first round targeting 0.66, 0.67 + From Hans + - Collected the keyboard patches from 0.63.1.56 .. 0.63.1.98 + ( from Rainer Zimmermann ) + and adapted them on top of 0.64.4. + Fixed some bugs (which very likely also bothered 0.65.0), + added the recent Slang and codetable changes, restored the + original filenames for the old files (patch now readable), + and got it finaly working. + The old keyboard stuff remains active per default. + The new stuff (--enable-new-kbd) also runs fine (atleast on the 3 + machines I have tested), except that ESC on Slangkeyboard has + to be typed twiced (result of 'dead keys for accents' ?). + +-------------- + +97/02/04 ver. 0.64.4 released + From Hans + - Adapted Steffen Winterfeld's (graphics) true color patch from + 0.63.1.98 (not his newest from .65) to 0.64.x. + The code now looks a bit kludgy, because it was written for .98, + but works great. Scaling (x2) is set for 320x200, so you may miss + the 'mini window' ;-) also win31 is happy. + - Added optimized XModeRemap functions for 15/16 bit true color. + - Upgraded to Slang0.99-38 and fix the odd situation when the + system supplied libslang was included, but the headerfiles were + taken from dosemu (showing the wrong slang version). + Now it uses _completely_ the dosemu or the system supplied files. + There is a new ./configure option to force use of the dosemu + supplied slanglib: --enable-force-slang + BTW: slang.h is now in src/include/slang/* + - Removed and disabled use of a.out (deleted slang-a.out-lib) + for arch-linux. + - fixed compile warning keyboard-client.c. and changed some + 'error()' to 'v_printf()' in int10.c + - made the 'unexpected CPU exception 0x06' error show the correct + registers. + - Fixed the wrong prefix handling in dpmi.c (dpmi_fault), + Thanks to Alberto who pointed this out. + - Added 'secure' option in /etc/dosemu.conf, which disables + generation of 'whole userspace' descriptors. A DPMI client + otherwise can modify (a suid root running) dosemu, such that it + may execute arbitrary commands. + This option also disables the use of the 'unix' and 'system' + commands (dos-helper functions 0x50...0x53). + From John Davis + - Slang needs -lm with newer libc. + - If the function and arrow key definitions are in the + terminfo library, it will use those + - Lower the TIMEOUT value for the ESC character from 3/4 + of a second to 1/4 of a seconds. + From Andreas Arens + - New support for Avance Logic VGA cards, needs this in dosemu.conf: + video { vga console graphics chipset avance } + From Alberto + - Patch for makeing debug print checks faster + (ifprintf -> log_printf) + - Makefile.main patch to fix odds in newer binutils. +97/01/22 ver. 0.64.3.1 released + From Hans (final fixes) + - Removed rest of 'history' in include/vm86plus + - reinstalled include/linux/vm86.h (to avoid compile errors + when using libc < 5.4.7) + - corrected VM86P* typo + - fixed wrong message on 'make install' + - made Bernd's timer patch a configure option, because apparently + some games don't like it. (--enable-monoton-timing) +97/01/19 ver. 0.64.3 released + From Hans + - removed emumodule (don't need it any more;-) + - cleaned up kernel dependencies + - adapted ./configure and Makefiles + - Made mouse functionable for win31-in-xdos (hiding X-cursor, + calibrating win31 cursor) + - little fix for xtermdos + - Updated the docs + From JES + - basic mouse functionality for win31-in-xdos + From Herbert Xu + - additions to man/dos.1, man/xtermdos.1 +96/12/30 ver. 0.64.2.2 released + From Erik Mouw + - sequencer emulator for VGAemu (compatible with Trident 8900) + This lets WIN31 run in xdos :-), ... but without mouse :-( +96/12/30 ver. 0.64.2.1 released + From Hans + - Finaly got expand-down-fake in DPMI work, so older DJGPP work again. + ( and maybe some games too ) + - Additional checks in ./configure for debian inconsistencies, + Forcing to ELF, if no GCC specs file available. + - ... and this time did not forget to increase the version ;-) + From Erik Mow + - Additional checks in ./configure for bison installed + From Grant R. Guenther + ( hey, Grant is the man who hacked the ZIP and other drives using + dosemu, so he then could write the Linux drivers ;-) + - "T" debug flag and LOG_IO macro + From Bernd Paysan + - fixed the keycodes for AccelX + - fix for micro timing problems, timer now strictly monotonous + ( Bernd's fix _really_ works great, Hans:-) +96/12/07 ver. 0.64.2 released + From Hans: + - vm86plus (2nd version) now is in the kernels >= 2.1.15 + ( it took long, but we finaly succeeded, thanks Linus :-) + - small adaptation and cleanup (docs, configure) + - minor fixes for clean compile under various compile options + - key-board fix for french keyboards (from Alain Knaff) +96/12/07 ver. 0.64.2-pre5 (privat release for JES only) + From Hans: + - vm86plus (emumodule), third round + ( Linus requested to put our stuff into a separate syscall ) + kernel patches now for 2.0.27, 2.1.14 + - small fix for -F bug. + - DPMI: tried to work around illegitime expand-down segments +96/11/15 ver. 0.64.1 + From Hans: + - vm86plus (emumodule), second round + kernel patches 2.0.25, 2.1.8 (fits also in 2.1.10) + see doc/README.kernel + - REQUIRES_EMUMODULE has become REQUIRES_VM86PLUS and now is + independed from emumodule (can also be supported by kernel patch) + - new ./configure options: + --enable-kernelvm86plus, --enable-novm86plus + deleted ./configure options: + --enable-noemumod + - insmod-2.1.8-HACKER_TOOL + - 'dos -F myconfig' checked under priv_off() (closing security hole) + From JES: + - kill_time (select replacement) for int15 diff --git a/ChangeLog.old1 b/ChangeLog.old1 new file mode 100644 index 0000000..b109481 --- /dev/null +++ b/ChangeLog.old1 @@ -0,0 +1,8212 @@ +2008-03-27 Bart + + * [r1853] ChangeLog, NEWS: + DOSEMU 1.4.0.1 + + * [r1852] configure, configure.ac, src/commands/Makefile, + src/plugin/commands/Makefile: + Fix 64-bit compilation on 32-bit systems and vice versa. + + * [r1851] src/base/bios/hlt.c, src/dosext/mfs/mfs.c, + src/plugin/kbd_unicode/keyb_raw.c: + Fix more gcc warnings for 64-bit compilation. + + * [r1850] src/base/bios/int10.c, src/plugin/term/terminal.c: + Cleaner fix for terminal memory map race. + +2008-03-26 Bart + + * [r1849] VERSION: + DOSEMU 1.4.0.1 + + * [r1848] Makefile: + Remove configure.lineno on distclean. + + * [r1847] src/base/init/lexer.l.in: + Avoid gcc-4.3 warning about unused input function in lexer. + + * [r1846] src/base/bios/int10.c, src/plugin/term/terminal.c: + Add a call to video->update_screen to int10/ah=0 modesets to make + sure the terminal is initialized. Fixes a race (though it should be + done more cleanly). + +2008-03-25 Bart + + * [r1845] src/arch/linux/dosext/sound/midid/timid.c, + src/plugin/midimisc/mid_o_tmdty.c: + In timidity plugin and midid code: use socketpair as a bidirectional + pipe: older versions (current as of 2008 :( ) of timidity write to + stdin and we can catch that to avoid waiting 3 seconds at select at + DOSEMU startup. + +2008-03-21 Bart + + * [r1844] src/dosext/mfs/lfn.c: + Add missing "return" for LFN findnext error. Fixes #1801411: Running + File Wizard crashes DOSEMU 1.4.0.0. + + * [r1843] src/plugin/term/keyb_slang.c, src/plugin/term/terminal.c: + Added some diagnostics to check for UTF-8 terminal mismatches; + "fixes" bug #1729556. + +2008-03-18 Bart + + * [r1842] src/emu-i386/simx86/trees.c: + Try to fix #1910153 again: we need to round to PAGE_SIZE for the loop + termination test because of backwards jumps and NOJUMPS. + + * [r1841] src/emu-i386/simx86/codegen-sim.c: + SF-Patch #1683073: handle 16bit address overflow in string + instructions. Fixes overflow in the simulator of cpu-emu. Adjusted + from Michael Karcher's patch. + +2008-03-17 Bart + + * [r1840] src/emu-i386/simx86/trees.c: + Bail out of node-to-invalidate check loop for the sorted list once + the base (instead of the PC-beginning of the block) is past the + affected address. Really fixes #1910153: SkyRoads failure: JIT fails + some self-modifying code (Michael Karcher). + +2008-03-16 Bart + + * [r1839] src/emu-i386/simx86/trees.h: + Fix #1910153 SkyRoads failure: JIT fails some self-modifying code. + The dnpc field needs to be signed because it can be negative and is + compared to negative "int"s in BreakNode. + +2008-03-11 Bart + + * [r1838] src/emu-i386/simx86/sigsegv.c: + Patch #1910535, Michael Karcher: fix for "e_vgaemu_fault corrupts + eip/rip" + + * [r1837] src/env/video/vgaemu.c: + In Super-VGA modes, do *not* wrap memory at 256k. SF patch #1910415, + Michael Karcher; fixes bug #1806787. + +2008-02-18 Stas + + * [r1836] src/base/dev/dma/dmanew.c, src/base/dev/sb16/sb16.c: + OK, the good-bye cannot be that rude any more. My apologies to the + sound.c authors for that silly remark of mine and I hope you can do a + good work on my code. + +2007-12-06 Bart + + * [r1835] src/arch/linux/async/sigsegv.c: + Use fpregset_t instead of _fpstate to avoid problems with Debian's + glibc for x86_64. + +2007-09-24 Bart + + * [r1834] src/emu-i386/simx86/codegen-sim.h, + src/emu-i386/simx86/codegen-x86.h: + Replace BT24 and BTA inline asm by C. + +2007-09-23 Bart + + * [r1833] src/emu-i386/simx86/interp.c: + Michael Karcher: #1800717 cpuemu: BOUND is signed. The current bound + implementation does a signed compare instead of an unsigned one. This + patch should fix it. + + * [r1832] src/base/async/int.c, src/base/dev/misc/lpt.c, + src/emu-i386/simx86/interp.c: + Implement the bound instruction (albeit fully interpreted). Make sure + that int 5 does not try printscreen if there is no printer. + + * [r1831] src/emu-i386/simx86/interp.c: + From Michael Karcher: #1763170 CPU emu: implement INTO correctly. The + INTO instruction should generate the overflow execption only if the + overflow flag is set, not on every invocation. + + * [r1830] src/emu-i386/simx86/interp.c: + From Michael Karcher: SF #1763169 CPU emu: handle prefixed jumps + correctly: The distance in a (not 8-bit) jump instruction is encoded + according to operand size, not address size. There is some wrong + documentation in this regard, even the processor reference manual I + usually use. The size of CX used in JCXZ is depending on the address + size, as correctly implemented in dosemu. + + * [r1829] src/emu-i386/simx86/interp.c: + From Michael Karcher, #1763166 CPU emu: ignore access to unknown VGA + ports Currently, dosemu crashes on read/write access to unhandled + ports in the VGA range when cpuemu is enabled. While this is + acceptable behaviour while developing, it is not helpful for the end + user, who has a program probing for different SVGA cards by direct + hardware access. + +2007-06-11 Bart + + * [r1828] src/dosext/mfs/lfn.c, src/tools/Makefile: + Fixed some warnings with 64-bit builds. + +2007-06-05 Bart + + * [r1827] etc/global.conf, src/plugin/X/X.c: + Fix $_X_winsize; also enable to use it, and X_aspect_43 with text + modes with bitmapped fonts. + +2007-06-04 Bart + + * [r1826] src/dosext/dpmi/msdos.c: + Add DPMI API translation support for int21/ax=71a6. + + * [r1825] src/base/async/int.c, src/dosext/mfs/lfn.c, + src/dosext/mfs/mfs.c, src/dosext/mfs/mfs.h, src/include/dos2linux.h: + Implement the LFN handle functions int21/ax=5704,5,6,7, and 71a6. + Also avoid using the set attribute ioctl on non-FAT filesystems. + +2007-06-03 Bart + + * [r1824] dist/dosemu: + Correct dosemu script. + + * [r1823] src/plugin/sdl/sdl.c: + Fix SDL video mode chooser if only one mode is available. + + * [r1822] src/plugin/X/X.c: + Fix "dosemu -w" for X and error messages for $DISPLAY not set. + + * [r1821] src/plugin/kbd_unicode/keymaps.c, src/plugin/term/terminal.c: + Prompt if the keyboard layout can't be auto-detected. Initialize the + SLang terminal screen draw routines as late as possible (the first + time something needs to be drawn) so that other error messages can + still be seen on the normal (non-SLang) terminal screen. + + * [r1820] dist/dosemu, dist/dosemu.bindist, dist/dosemu.systemwide, + man/dosemu.bin.1.in, src/arch/linux/Makefile.main, + src/base/init/config.c, src/include/emu.h, src/plugin/X/X.c, + src/plugin/X/X_font.c, src/plugin/term/terminal.c: + Move xset functionality to obtain the X fonts and the terminal < 25 + lines warnings from dosemu to dosemu.bin. + +2007-06-01 Bart + + * [r1819] src/base/async/int.c, src/base/bios/int10.c, + src/base/dev/pic/pic.c, src/base/init/config.c, + src/base/init/parser.y.in, src/base/misc/dump.c, + src/dosext/dpmi/dpmi.c, src/dosext/mfs/mfs.c, src/dosext/mfs/mfs.h, + src/dosext/misc/xms.c, src/dosext/net/net/pktnew.c, + src/emu-i386/simx86/cpu-emu.c, src/include/Asm/vm86.h, + src/include/machcompat.h: + Change longs in vm86.h to ints to be consistent between x86-64 and + i386. + + * [r1818] src/base/bios/int10.c, src/env/video/crtcemu.c, + src/env/video/seqemu.c, src/env/video/vgaemu.c: + Fix VESA text modes (fixes SR#1728817). + +2007-05-30 Bart + + * [r1817] src/plugin/commands/generic.S: + Fix preprocessor confusion. + +2007-05-28 Bart + + * [r1816] src/base/async/int.c, src/base/misc/dos2linux.c, + src/dosext/mfs/mangle.h, src/dosext/mfs/util.c, + src/include/dos2linux.h, src/plugin/X/X.c, src/plugin/sdl/sdl.c, + src/plugin/term/terminal.c, src/plugin/translate/include/translate.h, + src/plugin/translate/translate.c: + Better fix for X window titles & UTF-8. The title is now passed to + the plugin using a wchar_t string. + +2007-05-25 Bart + + * [r1815] src/dosext/mfs/lfn.c, src/dosext/mfs/mfs.c, + src/dosext/mfs/mfs.h: + Let find_file report FILE_NOT_FOUND or PATH_NOT_FOUND in an extra + parameter depending on whether the directory where the file to be + searched would be in exists or not. + +2007-05-23 Bart + + * [r1814] src/base/init/parser.y.in, src/dosext/mfs/mfs.c, + src/env/video/vgaemu.c: + Fix a memory leak and some other issues found by Valgrind. + +2007-05-21 Bart + + * [r1813] src/emu-i386/simx86/sigsegv.c: + CPUEMU: do not mix up LDT accesses with emulator page faults, and + translate non-zero based DPMI addresses to the correct pointer. + +2007-05-18 Bart + + * [r1812] etc/global.conf, src/base/init/lexer.l.in, + src/base/init/parser.y.in, src/env/video/vesa.c, src/include/emu.h: + Use vgaemubios_file internally to get the Bochs BIOS to avoid mixing + up with using it with console graphics, and be able to run it in a + non-root console. + + * [r1811] src/doc/README/config, src/plugin/X/screen.c, + src/plugin/kbd_unicode/include/keyb_clients.h, + src/plugin/kbd_unicode/keyb_clients.c, + src/plugin/translate/config/plugin_parser, + src/plugin/translate/include/translate.h, + src/plugin/translate/translate_config.c: + Improve pasting in X into DOSEMU too, trying to ask for UTF-8, then + iso2022, and then iso8859-1. This obsoletes the use of + $_external_char_set for pasting. + + * [r1810] src/base/async/int.c: + Add forgotten init memset of mbstate_t unix_state to 0. Fixes crash + on x86-64. + + * [r1809] src/env/video/text.c, src/include/vgatext.h, + src/plugin/X/screen.c: + Improving pasting text from DOSEMU: follow guidelines about which + character sets to use (in practise we must usually convert to UTF8 + nowadays). + + * [r1808] src/plugin/kbd_unicode/keyb_clients.c: + Check for -1 return from character_count (if an invalid multibyte + character string is pasted for example), so DOSEMU won't crash. + + * [r1807] src/base/bios/int10.c: + Add missing semicolon in int10.c + + * [r1806] src/arch/linux/debugger/mhpdbgc.c, src/base/async/int.c, + src/base/misc/disks.c, src/base/misc/fatfs.c, + src/base/misc/userhook.c, src/base/misc/utilities.c, + src/dosext/mfs/lfn.c, src/dosext/mfs/mangle.c, + src/dosext/mfs/mangle.h, src/dosext/mfs/mfs.c, + src/dosext/mfs/mscdex.c, src/dosext/mfs/util.c, + src/include/dos2linux.h, src/include/utilities.h, + src/plugin/commands/builtins.c, src/plugin/commands/commands.c, + src/plugin/commands/dosdbg.c, src/plugin/commands/lredir.c, + src/plugin/commands/msetenv.c, src/plugin/commands/xmode.c: + Force within-ASCII uppercasing for ASCII letters for toupperDOS and + friends. Consistently use toupperDOS etc. functions instead of + toupper etc. to avoid problems with the dotless i as used in Turkish + and some other languages. In fatfs.c convert from the Linux to the + DOS character set, and vice versa for the X title display. + +2007-05-17 Bart + + * [r1805] src/base/bios/bios.S: + Always covert ax=6cxx to 6c00 for the int21/ah=6c LFN->DOS converter. + +2007-05-16 Bart + + * [r1804] src/env/video/vesa.c: + Allow larger Bochs vgabios'es to be used than 32K. + +2007-05-14 Bart + + * [r1803] src/emu-i386/simx86/interp.c: + Implemented lock prefix (either ignored, or illegal op). + +2007-05-13 Bart + + * [r1802] src/emu-i386/simx86/cpu-emu.c: + SIMX86: Don't try to stretch time using RDTSC if $_rdtsc=(off) + + * [r1801] src/emu-i386/simx86/cpu-emu.c, src/emu-i386/simx86/interp.c: + Push IOPL_MASK on the stack for pushf and int. Fixes vm86 mode + detection of DOS4GW when $_dpmi=(off). + + * [r1800] src/arch/linux/async/signal.c: + Revert DPMI-inability detection change from #1799. + + * [r1799] src/arch/linux/async/signal.c, + src/arch/linux/async/sigsegv.c, src/dosext/dpmi/dpmi.c, + src/dosext/dpmi/dpmi.h, src/emu-i386/simx86/sigsegv.c, + src/env/video/vgaemu.c: + When handling signals, check for a valid DPMI selector, which means + that it is an LDT selector and not reserved by glibc or similar, e.g. + for pthreads. Else, and that includes the whole GDT, assume we + interrupt DOSEMU code. SystemSelector() in DPMI now refers to + anything not covered by the above, plus dpmi_sel16 & dpmi_sel32. This + fixes crashes with Xen-DOM0 on x86-64 where cs is 0x33 on entry to + signal handlers but set to 0xe033 after the first syscall. + +2007-05-11 Bart + + * [r1798] src/emu-i386/simx86/interp.c: + Remove (replace by an e_printf) debug code that aborts CPUEMU if 4 or + more 0's in a row are executed. Some .com programs do this to skip + over data (Reinhard Karcher). + + * [r1797] dist/dosemu, src/emu.c: + Moved DOSDRIVE_D environment manipulation from the dosemu script to + dosemu.bin so it also works with sudo. + + * [r1796] src/arch/linux/async/debug.c, src/arch/linux/async/sigsegv.c, + src/base/init/init.c: + Reorganize some debug reporting a bit. uname and the GCC version + number are now always reported in boot.log. Added some bug report + instructions. + + * [r1795] src/plugin/X/X.c, src/plugin/sdl/sdl.c, + src/plugin/term/term_core.c: + Fix terminal init problem (no terminal input running xdosemu) if no + dynamically linked plugins are used. + + * [r1794] default-configure: + Check the default bitness of gcc, instead of blindly relying on + uname. + + * [r1793] src/dosext/dpmi/dpmi.c, src/emu-i386/cpu.c, + src/emu-i386/simx86/sigsegv.c, src/include/emu.h: + Implement some workarounds for the CPU ESP bug. It is unlikely that + it will be worked around in 64-bit kernels. Compare with segment + limits instead of what is supposed to be kernel space (it isn't on + x86-64). Fixes the ancient MS Linker and the Need For Speed demo on + x86-64. + +2007-05-10 Bart + + * [r1792] src/emu-i386/simx86/sigsegv.c: + Do not patch code that has just tried to overwrite code instead of + data. Fixes a crash with Larry. + +2007-05-09 Bart + + * [r1791] src/dosext/mfs/mfs.c: + As the read-only attribute is mostly ignored for directories in DOS, + the dos_would_allow check for writable directories doesn't make much + sense. Instead simply check if the file is writable. + + * [r1790] src/dosext/mfs/mfs.c: + It is possible to work around the x86-64 FAT compat-ioctl kernel bug. + Thanks to Wine. + +2007-05-08 Bart + + * [r1789] src/arch/linux/async/signal.c, src/emu.c: + Force $_cpu_emu="full", and do not crash, if %cs is not saved for + signal handlers on x86-64 (kernels < 2.6.15, #1713659). + + * [r1788] src/arch/linux/dosext/sound/midid/oss.c, + src/arch/linux/dosext/sound/midid/timid.c, + src/plugin/midimisc/mid_o_oss.c: + Declare *seqbuf_dump static before including soundcard.h. Hopefully + that solves all warnings and errors with the variations out there. + + * [r1787] src/plugin/kbd_unicode/keymaps.c: + Do not attempt to load the X plugin if X isn't compiled. + + * [r1786] src/dosext/mfs/lfn.c: + int21/ax=7160/cl=1,2 must return an error if the file does not exist. + Fixes problems with FreeCOM mkdir not preserving the LFN directory + name. + + * [r1785] src/dosext/mfs/mfs.c: + Ignore attempts to translate the archive and read-only attributes to + Unix permissions for directories. Fixes problem with pkunzip (Joe + Ripley, linux-msdos). + +2007-05-07 Bart + + * [r1784] dist/autoexec.bat, dist/config.sys, src/base/async/int.c, + src/plugin/commands/blaster.c: + Reduce lines of output in config.sys, autoexec.bat, blaster, and the + banner, so everything fits on 25 lines. + + * [r1783] src/dosext/mfs/lfn.c: + Fix more illegal writes in the LFN code (crashed DJGPP gcc with + cpuemu=vm86). + +2007-05-06 Bart + + * [r1782] Makefile, Makefile.conf.in, src/Makefile.common, + src/arch/linux/Makefile.main, src/commands/Makefile: + Speed up 'make' a bit: - don't use bash anymore; remove bash-isms and + eliminate pwd -P - don't use the intermediate tmp directory for make + install anymore - use -rR make flags to ignore built-ins and made + some built-ins explicit + + * [r1781] configure, configure.ac, src/plugin/kbd_unicode/configure, + src/plugin/sdl/configure: + Execute sub-configures by hand to avoid autoconf warnings and + possible future breakage. + + * [r1780] Makefile.conf.in, configure, configure.ac, default-configure: + Allow dash to be used as sh in out-of-tree builds. + + * [r1779] Makefile.conf.in: + Do not forget sndfile and alsa libraries when building without + dynamically loaded plugins. + + * [r1778] src/base/bios/int10.c: + Implement INT10/AH=1c (save/restore video state) + +2007-05-05 Bart + + * [r1776] ChangeLog, NEWS, dist/config.sys, dist/dosemu.bindist, + dist/mkbindist: + Last minute fix for 1.4.0: fix #1713278 COMSPEC setting wrong. Use + config.sys/autoexec.bat distributed from here in the bindist instead + of the one in dosemu-freedos-*-bin*. + + * [r1775] NEWS, ChangeLog: + Release 1.4.0. + + * [r1774] INSTALL, README, README.bindist, doc/README.txt, + doc/announce, src/base/init/install.c, src/doc/README/config, + src/doc/README/header: + More documentation updates; added a quick more basic introduction at + the beginning of README.txt. + +2007-05-04 Bart + + * [r1773] VERSION, doc/DANG.txt, doc/README-tech.txt, doc/README.txt, + doc/announce, doc/dosemu-HOWTO.txt, etc/dosemu.conf, + src/doc/DANG/DANG.sgml, src/doc/HOWTO/dosemu-HOWTO.sgml, + src/doc/README/Priv-usage, src/doc/README/SECURITY, + src/doc/README/config, src/doc/README/port-io, + src/doc/README/recover, src/doc/README/runasuser: + Documentation updates for 1.4.0. + + * [r1772] src/dosext/mfs/lfn.c, src/emu-i386/simx86/codegen-sim.c, + src/plugin/commands/lredir.c: + gcc-2.95 compatibility fixes. + + * [r1771] src/base/init/config.c, src/emu-i386/ports.c: + Convert error about direct port i/o without root into a warning. + Err about using "-s" without enough privileges. + + * [r1770] src/arch/linux/Makefile.main: + Fix man page out-of-tree building. + + * [r1769] (almost all files changed) + Update copyright line to 2007. + + * [r1768] dosemu.spec.in: + Added missing new directory and symbolic link to dosemu.spec. + + * [r1767] src/arch/linux/Makefile.main: + Make man files at "make" instead of "make install" time. + + * [r1766] dist/dosemu, dist/dosemu.bindist, dist/mkbindist, + man/dosemu.bin.1.in, src/arch/linux/Makefile.main, + src/base/init/config.c, src/base/init/install.c, + src/base/init/parser.y.in, src/base/misc/fatfs.c: + Allow --Flibdir for suid configurations, as it hasn't been used to + locate global.conf for years. Document the allowance for + --Flibdir/--Fimagedir. Repair bindist/mkbindist to work with the + new FreeDOS configuration. This means using --Flibdir and + $DOSEMU_LIB_DIR instead of the hardcode DOSEMULIB_DEFAULT. + Run mkfontdir at "make" instead of "make install" time when possible. + + * [r1765] src/plugin/commands/lredir.c: + Fix lredir c: d: + +2007-05-03 Bart + + * [r1764] configure, configure.ac, src/plugin/sdl/Makefile: + Fix forgotten make distclean file in SDL plugin; add -lXext for + Xxf86vm checking on certain systems. + + * [r1763] INSTALL, configure, configure.ac: + Check for the existence of the Xxf86vm and Xext libraries, not just + header files. Adjust INSTALL instructions. + + * [r1762] config.guess, config.sub, configure, configure.ac: + Don't use -mtune by default. It doesn't bear much relation to 'uname + -m' anymore on anything newer than a Pentium Pro. + + * [r1761] etc/dosemu.users.example, src/base/init/config.c, + src/base/init/parser.y.in, src/base/misc/priv.c, src/include/priv.h: + Register the difference between sudo and suid-root. + Use c_all as the default for users who run dosemu via sudo so + dosemu.users does not need to be edited for that case, leaving + the default "restricted" (only console graphics at the console, + no remote direct hardware access) setting for suid-root. + +2007-05-02 Bart + + * [r1760] src/arch/linux/Makefile.main: + Use -rm instead of rm to deal with non-existing directories. + Use bdftopcf at make time instead of make install. + Fixes problems reported at linux-msdos (perry hargrave). + + * [r1759] src/base/init/parser.y.in: + Repair $_sound=(on). You would only get the old sound driver via + $_sound=(1). + +2007-05-01 Bart + + * [r1758] src/env/video/dualmon.c, src/env/video/hgc.c, + src/env/video/matrox.c, src/env/video/vc.c: + Mark all mapped video memory PROT_EXEC, similar to the VGAEMU memory. + Solves a few problems with console video on x86-64. + + * [r1757] src/emu-i386/simx86/fp87-x86.c: + Use aliased memory for emulation of FSTENV/FSAVE. + + * [r1756] src/emu-i386/simx86/cpatch.c: + There is a slight chance that the CPUEMU cpatch stubs hit VGA memory. + For that case there is a simple instruction decoder but we must use + explicit assembly instructions. Fixes another problem with Norton + SysInfo. + +2007-04-30 Bart + + * [r1755] src/emu-i386/simx86/codegen-sim.c: + Fix regression in vgaemu handling from #1754. + Fix problem with rep movs with df set backwards for simulated cpuemu + with planar VGA modes. + + * [r1754] src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/sigsegv.c, src/env/video/vgaemu.c: + Fix problems with programs that try to access unmapped VGA memory or + write to ROM in CPUEMU, such as Norton SysInfo. + +2007-04-29 Bart + + * [r1753] src/dosext/mfs/mfs.c: + Check for FAT filesystem before attempting to use the VFAT ioctls. + Also check if the first entry is ".\0" to avoid an ioctl32 bug in + kernels <= 2.6.21.1. + +2007-04-27 Bart + + * [r1751] VERSION, NEWS, ChangeLog, doc/DOSEMU-HOWTO.txt, + etc/dosemu.conf: + Regenerate documentation. Release 1.3.5. + + * [r1750] src/plugin/commands/lredir.c, src/plugin/commands/msetenv.c: + Avoid CPUEMU problems with msetenv. Only set DOSEMU_LASTREDIR if no + drive letter was given; improve lredir command line processing with C + and R. + + * [r1749] etc/dosemu.conf, src/base/dev/misc/lpt.c: + Flush every second when printing. Fix $_lptx="" to not register any + ports. + + * [r1748] src/dosext/dpmi/msdos.c: + Fix #1638135 (int 21h, ax=6300 not translated currently, japheth) + + * [r1747] src/dosext/mfs/mfs.c, src/dosext/mfs/mfs.h: + Always return . and .. as the first two entries for + findfirst/findnext to be consistent with real DOS. On Linux they + can be somewhere in the middle. + +2007-04-26 Bart + + * [r1746] src/base/misc/dos2linux.c, src/include/dos2linux.h, + src/plugin/commands/lredir.c, src/plugin/commands/unix.c: + Apply #1691712 make lredir able to automatically choose next free + drive II (Stuart Axon) + + * [r1745] INSTALL: + Made a note about libXxf86vm (#1644784) + + * [r1744] man/dosemu.1.in: + [ 1692149 ] Describe --version in man page (fix for 1245033) (Stuart + Axon) + + * [r1743] src/doc/HOWTO/dosemu-HOWTO.sgml: + Some HOWTO updates by Stuart Axon (#1692299). + + * [r1742] src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/codegen-x86.c, src/emu-i386/simx86/codegen.h, + src/emu-i386/simx86/cpatch.c, src/emu-i386/simx86/interp.c, + src/emu-i386/simx86/tables.c: + Handle 16bit address overflow in cpuemu string instructions. + (#1683078, slightly adjusted from Michael Karcher's patch) + +2007-04-23 Bart + + * [r1741] src/base/init/config.c, src/dosext/dpmi/dpmi.c, + src/dosext/dpmi/dpmi.h, src/emu-i386/cpu.c, src/include/cpu.h, + src/include/emu.h: + Use fxsave/fxrstor when available on i386 to deal with SSE fp. + Make sure the fxsave buffer is paragraph aligned (fixes a bug with + DJGPP gcc on x86_64). + +2007-04-22 Bart + + * [r1740] src/dosext/dpmi/dpmi.c: + Check fs/gs values before loading them on x86-64. Preserves 64 bit + segment bases on Intel chips (but apparently makes no difference on + AMD's). + +2007-04-20 Bart + + * [r1739] src/dosext/dpmi/dpmi.c: + Fix LFB w/console graphics for x86-64. + + * [r1738] src/base/async/pci_bios.c, src/base/dev/misc/pci.c, + src/env/video/matrox.c, src/include/pci.h: + Fix PCI support on x86-64. + + * [r1737] src/emu-i386/do_vm86.c, src/include/cpu.h: + Use fnsave instead of fsave for saving the FPU state. Should fix + #1436788. + + * [r1736] src/plugin/term/keyb_slang.c, + src/plugin/translate/charsets/utf8.c: + Fix problems with utf-8 multibyte input in terminals (reported by + Grigory Batalov, linux-msdos). + +2007-03-29 Bart + + * [r1735] dist/dosemu, src/base/init/config.c: + Avoid bash-ism "exec -a"; use an environment variable instead. + + * [r1734] src/emu-i386/simx86/codegen-x86.c: + Fix gcc warning. + + * [r1733] src/emu-i386/simx86/codegen-sim.c: + Fix shift/rotate instructions in simulator (Michael Karcher, + #1687296) + + * [r1732] src/emu-i386/simx86/codegen-x86.c: + Fix spurious crashes with "linker: node busy" (Michael Karcher, + #1687298) + +2007-03-24 Bart + + * [r1731] src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/codegen-x86.c, src/emu-i386/simx86/tables.c: + Fix rep scas/cmps when eCX is 0 (#1683190, with Michael Karcher) + +2007-03-18 Bart + + * [r1730] src/emu-i386/simx86/cpatch.c: + Fix to SVN #1727 stosw fix (Michael Karcher, #1679675) + +2007-03-17 Bart + + * [r1729] src/emu-i386/simx86/interp.c: + Fix problem with push when single stepping in cpuemu (#1682620, M. + Karcher) + + * [r1728] src/emu-i386/simx86/interp.c: + Fix cpuemu bug with sbb, affects GNUish ls and tasm (#1682575) + +2007-03-13 Bart + + * [r1727] src/emu-i386/simx86/cpatch.c: + Save eax in stosw stub (Michael Karcher, #1679675) + +2007-03-11 Bart + + * [r1726] src/emu-i386/simx86/cpu-emu.c, src/emu-i386/simx86/interp.c: + Use leavedos(1) for EMU86 failure to drop into dosdebug; improve + diagnostics. For rep outs, use not_permitted and let the port code + deal with it instead of quitting DOSEMU. + + * [r1725] src/emu-i386/simx86/cpatch.c, src/emu-i386/simx86/trees.c: + Break node if current eip is in a page to be unprotected; fixes bug + #1678581 + +2007-03-10 Bart + + * [r1724] src/dosext/mfs/dosc.c, src/dosext/mfs/lfn.c: + LFN: avoid writing to write-protected memory (thanks Michael Karcher) + + * [r1723] src/base/bios/setup.c, src/base/init/init.c, + src/base/misc/disks.c, src/dosext/dpmi/emu-ldt.c, + src/dosext/dpmi/msdos.c, src/dosext/misc/xms.c, + src/dosext/net/net/ipx.c, src/emu-i386/ports.c, + src/emu-i386/simx86/fp87-x86.c, src/emu-i386/simx86/sigsegv.c, + src/tools/periph/dexeconfig.c, src/tools/tools86.c: + Replaced 'long *' by 'int *' or 'uint32_t *' for 64-bit + compatibility. + +2007-02-28 Bart + + * [r1722] src/env/video/instremu.c: + Fix problem with constraints in inline assembler. + +2006-12-10 Bart + + * [r1721] src/include/sound, src/include/sound/midi.h, + src/include/sound/sndpcm.h, src/include/sound/sound.h, + src/plugin/sdl/snd_o_SDL.c: + Add forgotten files from Stas' new sound code. + + * [r1720] src/arch/linux/dosext/sound/midid/io.c, + src/arch/linux/dosext/sound/midid/midid.c, + src/arch/linux/dosext/sound/midid/timid.c: + Integrated most of the other midid changes by Stas. + + * [r1719] Makefile.conf.in, compiletime-settings, + compiletime-settings.devel, configure, configure.ac, + default-configure, src/base/sound/sndpcm.c, src/include/config.h.in, + src/plugin/alsa, src/plugin/alsa/Makefile, + src/plugin/alsa/mid_o_alsa.c, src/plugin/sndfile, + src/plugin/sndfile/Makefile, src/plugin/sndfile/snd_o_wav.c: + Added ALSA and libsndfile plugins from Stas. + +2006-11-29 Bart + + * [r1718] compiletime-settings, compiletime-settings.devel, + etc/dosemu.conf, etc/global.conf, src/arch/linux/Makefile.main, + src/base/dev/dma/Makefile, src/base/dev/dma/dma.c, + src/base/dev/dma/dmanew.c, src/base/dev/sb16, + src/base/dev/sb16/Makefile, src/base/dev/sb16/adlib.c, + src/base/dev/sb16/adlib.h, src/base/dev/sb16/dspio.c, + src/base/dev/sb16/dspio.h, src/base/dev/sb16/sb16.c, + src/base/dev/sb16/sb16.h, src/base/dev/sb16/ymf262.c, + src/base/dev/sb16/ymf262.h, src/base/init/dev_list.c, + src/base/init/parser.y.in, src/base/sound, src/base/sound/Makefile, + src/base/sound/midi.c, src/base/sound/nullsnd.c, + src/base/sound/nullsnd.h, src/base/sound/sndpcm.c, + src/dosext/sound/sound.c, src/include/dma.h, src/include/sound.h, + src/plugin/midimisc, src/plugin/midimisc/Makefile, + src/plugin/midimisc/mid_i_pipe.c, src/plugin/midimisc/mid_o_midid.c, + src/plugin/midimisc/mid_o_oss.c, src/plugin/midimisc/mid_o_pipe.c, + src/plugin/midimisc/mid_o_tmdty.c, src/plugin/sdl/Makefile: + Integrate most of Stas' new sound code, for now run-time selectable + using $_sound = (2). + +2006-11-25 Bart + + * [r1717] src/dosext/dpmi/dpmi.c: + Fix trap flag handling regression for DPMI (#1602643) + + * [r1716] src/arch/linux/dosext/sound/midid/Makefile, + src/arch/linux/dosext/sound/midid/device.c, + src/arch/linux/dosext/sound/midid/device.h, + src/arch/linux/dosext/sound/midid/midid.c, + src/arch/linux/dosext/sound/midid/midout.c, + src/arch/linux/dosext/sound/midid/null.c, + src/arch/linux/dosext/sound/midid/oss.c, + src/arch/linux/dosext/sound/midid/seqops.h, + src/arch/linux/dosext/sound/midid/timid.c: + Merged trivial changes and makefile improvements from Stas' new midid + code. + + * [r1715] src/arch/linux/Makefile.main: + Do not erase all previous FreeDOS files when upgrading. Remove risky + comment between semicolons. + +2006-11-22 Bart + + * [r1714] src/plugin/sdl/keyb_SDL.c: + Fix AltGr handling with SDL. It was confused with ScrollLock. + + * [r1713] src/plugin/sdl/keyb_SDL.c: + Try to use keysyms where possible in the SDL keyboard code. + Implemented workaround for altgr and broken unicode support in older + SDL versions. + +2006-11-21 Bart + + * [r1712] src/plugin/X/X.c: + Fix double mouse cursor problem with Arachne. + +2006-11-18 Bart + + * [r1710] VERSION, NEWS, ChangeLog, src/doc/DANG/DANG.sgml, + src/doc/README/CDROM, doc/DANG.txt, doc/README.cpuemu, + doc/README.txt, doc/EMUfailure.txt: + Added CDROM lredir documentation. Regenerate documentation. + Release 1.3.4. + + * [r1709] src/arch/linux/async/debug.c, src/base/init/lexer.l.in, + src/base/misc/fatfs_boot.S, src/base/misc/utilities.c, + src/dosext/dpmi/memory.c, src/dosext/dpmi/vxd.c, + src/dosext/dpmi/windefs.h, src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/codegen-x86.c, src/include/cpu.h: + GCC-2.95 and x86-64 VXD compatibility changes. + + * [r1708] INSTALL, src/base/init/install.c: + Fixed -install for other DOSes than the supplied FreeDOS and + adjust README. + +2006-11-16 Bart + + * [r1707] src/emu-i386/simx86/sigsegv.c: + Fixed some 64-bit rip usage, and the division by zero exception. + + * [r1706] configure, configure.ac, src/include/config.h.in, + src/include/dlmalloc.h: + Fixed gcc warning in dlmalloc and removed some obsolete autoconf + macros. + +2006-11-15 Bart + + * [r1705] src/arch/linux/Makefile.main: + Rearranged the messages after make install, so it is more clear + that the FreeDOS tarball is missing. + + * [r1704] src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/interp.c, src/emu-i386/simx86/modrm-gen.c, + src/emu-i386/simx86/protmode.c, src/emu-i386/simx86/protmode.h, + src/emu-i386/simx86/tables.c: + Segment handling and address calculation optimizations for + cpuemu. + +2006-11-14 Bart + + * [r1703] configure, configure.ac: + Added configure checks for bdftopcf and mkfontdir. + + * [r1702] dist/dosemu: + Allow DOSDRIVE_D to be overridden. + + * [r1701] src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/tables.c: + Optimize the O_MOVS_SetA cpuemu op. + +2006-11-13 Bart + + * [r1700] ChangeLog, NEWS: + Updated NEWS and ChangeLog + +2006-11-12 Bart + + * [r1699] COPYING.DOSEMU, src/env/video/vgafonts.c: + Added VGA font and dlmalloc copyrights for completeness. + + * [r1698] dist/autoemu.bat, dist/autoexec.bat, dist/config.emu, + dist/config.sys, src/arch/linux/Makefile.main, + src/base/init/install.c, src/base/misc/fatfs.c: + The previous approach had circular symbolic links for directories + which is bad as it confuses DOS programs. It seems clearer to + create a drive_z directory with contents equal to the old freedos + directory (even though there may not be a freedos present). + + * [r1697] dist/dosemu, src/base/init/config.c, + src/base/init/install.c, src/emu.c, src/include/dos2linux.h, + src/include/emu.h, src/plugin/commands/unix.c: + Create no-prompt initial boot for DOSEMU+FreeDOS with a welcome + screen. The liability disclaimer just needs to be confirmed. + Removed the win4lin check from the dosemu script and moved the + terminal instructions to the main code. + + * [r1696] dist/autoemu.bat, dist/autoexec.bat, dist/config.emu, + dist/config.sys, dosemu.spec.in, src/arch/linux/Makefile.main, + src/base/init/install.c, src/base/misc/fatfs.c: + Remove the tarballs with symlinks. Instead, point d: to the + systemwide FreeDOS directory during config.sys processing and use + z: after that. Install config.sys et al from dosemu. + + * [r1695] etc/dosemu.conf, etc/global.conf, + src/base/dev/misc/lpt.c, src/base/init/lexer.l.in, + src/base/init/parser.y.in: + Use $_lpt1, $_lpt2, and $_lpt3 for printer configuration and + disable the printer IRQ for now since it's not used. Fixes + #1492201. + + * [r1694] src/base/async/int.c, src/base/bios/setup.c, + src/include/int.h: + Fix missing LFN support after ctrl-alt-del (#1487899) + + * [r1693] INSTALL, compiletime-settings, + compiletime-settings.devel, compiletime-settings.help, configure, + configure.ac, default-configure, src/emu.c, + src/plugin/sdl/acinclude.m4, src/plugin/sdl/configure, + src/plugin/sdl/configure.ac: + Use JIT cpuemu by default on x86-64, and don't force -m32 any + longer. Introduce target_bits compile time option to optionally + set -m32 or -m64. Updated SDL configure script and added a link + check for cross-compilation. + +2006-11-11 Bart + + * [r1692] dist/dosemu: + Use `id -ur` instead of $UID for dash compatibility. + + * [r1691] src/arch/linux/async/signal.c, src/dosext/dpmi/dpmi.c, + src/emu-i386/cpu.c, src/include/cpu-emu.h: + Fix compilation with cpuemu off. + + * [r1690] src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/codegen-sim.h, + src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/codegen-x86.h, src/emu-i386/simx86/emu86.h, + src/emu-i386/simx86/sigsegv.c, src/env/video/instremu.c, + src/env/video/vgaemu.c, src/include/vgaemu.h: + Implemented better VGAEMU support for simulated cpuemu and + eliminated the e_vga_base and e_vga_end variables. + + * [r1689] src/arch/linux/async/signal.c, + src/emu-i386/simx86/cpu-emu.c, src/emu.c, src/env/video/vc.c, + src/include/emu.h, src/plugin/term/terminal.c: + Handle all async signals except those that call leavedos using + sigasync() which calls the correct sighandler using a function + pointer array. The real signal handlers are all set in + signal.c:signal_init() now. Add SIGWINCH & SIGPROF to the queue + block list because of their nature. + + * [r1688] src/emu-i386/simx86/sigsegv.c, + src/emu-i386/simx86/trees.c: + Fixed some issues with self-modifying code. + + * [r1687] src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/tables.c, src/emu-i386/simx86/trees.c: + Removed GenBufSize hack and adjusted the op size array. + + * [r1686] src/emu-i386/simx86/interp.c, src/env/video/instremu.c, + src/include/cpu.h: + Avoid "lahf" (may not be available on x86-64) and use READ_BYTE + for IS_IRET. + +2006-11-10 Bart + + * [r1685] src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/cpu-emu.c, src/emu-i386/simx86/interp.c, + src/emu-i386/simx86/trees.c: + Fixed a bug in InvalidateSingleNode and DPMI TheCPU.eflags + handling. Under normal circumstances IOPL should never be set + there. + + * [r1684] configure, configure.ac: + Add -lm to LIBS as DOSEMU now uses a few math functions. + + * [r1683] src/emu-i386/simx86/fp87-sim.c: + Fixed rounding to integer in the FPU simulator. + + * [r1682] src/emu-i386/simx86/codegen-x86.c: + Fix das/aas/aam for x86-64. + + * [r1681] src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/fp87-sim.c, src/emu-i386/simx86/interp.c: + Fixed ENTER/IDIV/DIV/IMUL/DAS and some FPU cases that failed with + the QEMU i386 emulator test program. + +2006-11-09 Bart + + * [r1680] src/emu-i386/simx86/interp.c: + Correct ofsseg array for DPMI + +2006-11-08 Bart + + * [r1679] src/emu.c, src/include/emu.h: + Forgotten sigjmp_buf declarations from r1677. + + * [r1678] src/plugin/sdl/sdl.c: + Enable X background pause for SDL + + * [r1677] src/base/misc/utilities.c, src/emu.c: + Use sigsetjmp/siglongjmp because signal handlers may be involved + in leavedos. + + * [r1676] src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/sigsegv.c: + Fix BSF and BSR, and don't use code patches for DPMI code for + cpuemu="full". Fixes Duke Nukem 3D in this mode. + + * [r1675] src/emu-i386/simx86/interp.c: + Use the operand size instead of the address size for jump + destination masking. Fixes some issues with cpuemu in DPMI. + + * [r1674] src/emu-i386/simx86/codegen-x86.c: + Fix vgaemu in compiled code. + + * [r1673] src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/interp.c: + Fixed IMUL regressions from r1670. + +2006-11-07 Bart + + * [r1672] src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/codegen-x86.c, src/emu-i386/simx86/interp.c: + Use load/op/store for inc&dec, and optimize inc/dec byte reg. + + * [r1671] src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/codegen-x86.c, src/emu-i386/simx86/interp.c: + Use load/op/store for xchg and actually use xchg in generated + code as well. + + * [r1670] src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/codegen-x86.c, src/emu-i386/simx86/interp.c: + Use load/op/store for cpuemu (I)MUL, (I)DIV, NEG, and NOT. + + * [r1669] src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/codegen-x86.c, src/emu-i386/simx86/codegen.h, + src/emu-i386/simx86/interp.c: + Use "op %al,reg" style instructions in cpuemu instead of "op + (%edi),%al" to avoid certain VGA memory reads. + + * [r1668] src/base/misc/dump.c, src/dosext/dpmi/dpmi.c: + Fixed a DPMI ctrl-c problem (missed in_dpmi_dos_int), and avoid + dumping unreachable memory in dump.c, which may double-fault. + +2006-11-06 Bart + + * [r1667] src/dosext/dpmi/memory.c: + Save actual dpmi base pointer in config.dpmi_base. + + * [r1666] src/emu-i386/simx86/cpu-emu.c, + src/emu-i386/simx86/fp87-x86.c: + Changed fp87 absolute addresses to bx-relative so that the FPU + works for x86-64 for JIT cpuemu, and improve x86-64 cpuemu fault + handling. + + * [r1665] src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/trees.c: + Fixed VGA read/write for x86-64. Misc adjustments to deal with + 64-bit pointers. Add forgotten #include. + +2006-11-05 Bart + + * [r1664] src/base/misc/Makefile, src/base/misc/dlmalloc.c, + src/base/misc/smalloc.c, src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/memory.c, src/emu-i386/simx86/trees.c, + src/include/dlmalloc.h, src/include/smalloc.h: + Use Doug Lea's malloc (forced to use mmap with PROT_EXEC) instead + of smalloc for cpuemu. It is a lot faster. + + * [r1663] src/base/misc/smalloc.c, src/emu-i386/simx86/memory.c, + src/include/smalloc.h: + Introduce smalloc_fast and smfree_fast: these clobber the + allocated memory to improve the speed of smfree (eliminating a + linear search). Used by cpuemu: shaves ~25% off DOS boot time. + + * [r1662] src/base/misc/smalloc.c, src/include/smalloc.h: + Changed smalloc to use a double-linked list for a little + performance improvement. + + * [r1661] src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/cpu-emu.c, src/emu-i386/simx86/trees.c, + src/emu-i386/simx86/trees.h: + Clean up HOST_ARCH_X86 use. + + * [r1660] src/emu-i386/simx86/codegen.h, + src/emu-i386/simx86/cpatch.c, src/emu-i386/simx86/cpu-emu.c, + src/emu-i386/simx86/syncpu.h: + Implemented stos/movs patches for x86-64. + + * [r1659] src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/interp.c, src/emu-i386/simx86/trees.h: + Pass clink as a pointer instead of as a possibly truncated int. + + * [r1658] src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/trees.c, src/emu-i386/simx86/trees.h: + Clean up pointer casts in cpuemu tree handling. Use int instead + of long where possible/appropriate. + +2006-11-04 Bart + + * [r1657] src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/codegen-x86.h, src/emu-i386/simx86/codegen.h, + src/emu-i386/simx86/cpatch.c, src/emu-i386/simx86/cpu-emu.c, + src/emu-i386/simx86/syncpu.h, src/emu-i386/simx86/trees.c: + Implemented stack/write code patches for x86-64 and fixed an + associated bug in trees.c. + + * [r1656] src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/codegen.h, src/emu-i386/simx86/cpatch.c, + src/emu-i386/simx86/cpu-emu.c, src/emu-i386/simx86/protmode.c, + src/emu-i386/simx86/sigsegv.c, src/emu-i386/simx86/syncpu.h: + Reorganised the SynCPU structure and rep stos/movs code patches + to use call (%ebx), and enable those on x86-64. + +2006-11-02 Bart + + * [r1655] src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/codegen-x86.h, src/emu-i386/simx86/cpatch.c, + src/emu-i386/simx86/emu86.h: + Cleaned up cpuemu eflags handling. DF is now always handled via + TheCPU.eflags instead of the stack flag image, and always clear + between instructions. Disabled the pushf VIF trick which doesn't + seem to work as advertised. + + * [r1654] src/emu-i386/simx86/interp.c: + Correct cpuemu regression from rev #1652 + +2006-11-01 Bart + + * [r1653] src/emu-i386/simx86/codegen-sim.c: + Correct flag handling in AAA, AAS, DAS, DAA, CMPS*, SCAS* in the + cpuemu interpreter. Fixes problems with FD DEBUG's "a" command. + + * [r1652] src/emu-i386/simx86/cpu-emu.c, + src/emu-i386/simx86/interp.c: + Fix single stepping in cpu-emu (including dosdebug). + +2006-10-31 Bart + + * [r1651] src/env/video/render.c, src/env/video/text.c, + src/include/render.h, src/plugin/X/X.c, + src/plugin/sdl/mouse_SDL.c, src/plugin/sdl/sdl.c: + Fix palette and mouse with SDL for fbdev/fullscreen/256 colors. + + * [r1650] (almost all files changed) + Update year to 2006. COPYING now contains the pure GPL, and + COPYING.DOSEMU the other copyright details. Distribute the GPL + and COPYING.DOSEMU with rpm's from dosemu.spec. Clarified the + clause on redistribution of repackaged official packages. + + * [r1649] src/plugin/X/X.c, src/plugin/sdl/sdl.c: + Don't call SDL_UpdateRects with 0 rectangles: it may crash. Fix + the close (x) window button in xdosemu. + +2006-10-30 Bart + + * [r1648] src/env/video/text.c, src/plugin/X/X.c, src/plugin/X/X.h, + src/plugin/X/X_font.c, src/plugin/sdl/sdl.c: + Don't call X functions directly from SDL code, that won't work + with SDL libraries that are not directly linked with X. Added + more checks for x11.window availability. Fixed mouse/input focus + cursor blinking situation. + + * [r1647] src/plugin/sdl/sdl.c: + Initialize most of the X support before creating the first window + (except for window and font), where it was done previously. + Avoids some issues with dynamic loading. + + * [r1646] src/plugin/X/X_font.c: + Fix crash in SDL if X_font = "" (thanks Reinhard). + +2006-10-29 Bart + + * [r1645] src/env/video/text.c, src/include/vgatext.h, + src/plugin/X/X.c, src/plugin/X/X_font.c, src/plugin/sdl/sdl.c: + Add X font support to the SDL plugin. There's a bit of a trick + involved (a second "Display") to receive all expose events. Did + some associated cleanup. + + * [r1644] src/plugin/X/Makefile, src/plugin/X/X.c, + src/plugin/X/X.h, src/plugin/X/X_font.c: + Moved X font handling into a seperate file with seperate static + variables, so X fonts can be used by SDL later. + + * [r1643] dist/dosemu: + Replace bash-isms in dosemu script with sh constructs. + + * [r1642] src/env/video/text.c, src/plugin/X/X.c: + Introduce a new Window in the X code, drawwindow, that is a + subwindow of either the fullscreen or the normal window, so can + be reparented (technique borrowed from SDL). The main advantage + is that the X server can take care of shift_x/shift_y for text + modes. Also no need to copy gc's any more. Fix crash for a too + eager update_cursor(). + +2006-10-28 Bart + + * [r1641] src/env/video/render.c, src/env/video/text.c, + src/include/render.h, src/include/vgatext.h, src/plugin/X/X.c, + src/plugin/sdl/sdl.c: + Create a "bitmap" text system in render.c that is shared by X and + SDL. Implemented line drawing (for mono underline) on the canvas. + The text cursor and underlining now go through the remapper on X. + Remove scaling from the previously used X calls (impossible with + X fonts). Screen updates now lock the surface during the whole + update for SDL; the SDL code pushes rectangles to be updated and + sends them in one go. Mode changes need to be checked before + locking the surface: moved the mode adjust calls out of the + common update code. + + * [r1640] src/arch/linux/async/signal.c, src/env/video/text.c, + src/include/vgatext.h, src/plugin/X/X.c, src/plugin/sdl/sdl.c, + src/plugin/term/terminal.c: + Remove update function in text_system struct. The only user was X + with XFlush but XFlush is not necessary if XPending is called + often enough. Moved event handler call in signal.c after update + calls to be sure of that. Similarly the mouse cursor update is + best placed before other updates. + + * [r1639] src/env/video/seqemu.c, src/env/video/text.c, + src/env/video/vesa.c, src/include/vgatext.h, src/plugin/X/X.c, + src/plugin/sdl/sdl.c, src/plugin/term/terminal.c: + Remove X_resize_text_screen from text_system structure. Resizes + are done on demand by update_screen(); set vga.reconfig.re_init + to be sure of that in all cases. + + * [r1638] src/emu-i386/simx86/codegen-sim.c: + Correct DAA & DAS in simulator. + + * [r1637] src/emu-i386/simx86/codegen-x86.c: + Implement AAD and AAM replacements for x86-64. + + * [r1636] src/emu-i386/simx86/codegen-x86.c: + Implement JIT code for AAA,AAS,DAA, and DAS for x86-64 as they + are not available in long mode. Fixes FreeDOS debug which uses + DAA, in particular. + +2006-10-26 Bart + + * [r1635] src/base/init/parser.y.in, + src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/codegen-x86.h, src/emu-i386/simx86/codegen.h, + src/emu-i386/simx86/cpu-emu.c, src/emu.c, src/include/cpu-emu.h: + Enable dynamic code generation for x86-64. Almost all generated + code is the same as for i386, but it's still a bit unstable, and + slower than simulation because of excessive page faulting: code + patching isn't working yet. But FreeDOS boots. Only enabled for + $_cpu_emu="vm86", not for the default "off" for now. Marked + InCompiledCode as volatile to simplify asm constraints. + + * [r1634] src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/codegen-sim.h, + src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/codegen-x86.h, src/emu-i386/simx86/codegen.h, + src/emu-i386/simx86/cpu-emu.c, src/emu-i386/simx86/interp.c, + src/emu-i386/simx86/trees.c: + cpuemu: replaced use of inline asm __memcpy by memcpy. GCC does a + good job now and asm __memcpy isn't portable. + + * [r1633] src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/fp87-x86.c: + cpuemu: fix asm compilation on x86-64. + +2006-10-24 Bart + + * [r1632] src/arch/linux/async/sigsegv.c, src/dosext/dpmi/dpmi.c, + src/emu-i386/simx86/sigsegv.c, src/plugin/commands/builtins.h: + Improve cpuemu fault handling and diagnostics; avoid a few more + page faults in DOSEMU code. + + * [r1631] src/base/init/config.c, src/dosext/dpmi/dpmi.c, + src/emu-i386/simx86/codegen-x86.c, src/include/emu.h: + simx86: shortened asm code, check correct CPU feature flags for + prefetcht0, and fix DPMI Jazz Jackrabbit crash with + $_cpu_emu="vm86". + +2006-10-23 Bart + + * [r1630] src/base/init/parser.y.in, + src/emu-i386/simx86/codegen-x86.c, src/emu-i386/simx86/cpatch.c, + src/emu-i386/simx86/sigsegv.c, src/include/cpu-emu.h: + Link in most of the JIT code for x86-64. Small crucial parts + still disabled. + + * [r1629] src/arch/linux/debugger/Makefile, + src/arch/linux/dosext/sound/midid/Makefile, + src/tools/periph/Makefile: + Use LDFLAGS without -rdynamic to link misc binaries. + + * [r1628] etc/dosemu.conf, src/base/init/lexer.l.in, + src/base/init/parser.y.in, src/emu-i386/simx86/Makefile, + src/emu-i386/simx86/codegen-arch.h, + src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/codegen-x86.c, src/emu-i386/simx86/codegen.h, + src/emu-i386/simx86/cpatch.c, src/emu-i386/simx86/cpu-emu.c, + src/emu-i386/simx86/emu86.h, src/emu-i386/simx86/fp87-sim.c, + src/emu-i386/simx86/fp87-x86.c, src/emu-i386/simx86/interp.c, + src/emu-i386/simx86/sigsegv.c, src/emu-i386/simx86/trees.c, + src/include/cpu-emu.h, src/include/emu.h: + Select JIT or simulated CPU emulation at runtime instead of at + compiletime. + +2006-10-22 Bart + + * [r1627] src/arch/linux/async/signal.c, + src/arch/linux/async/sigsegv.c, src/emu-i386/cpu.c, + src/include/emu.h: + Replaced tls_setup hack with something just a little cleaner. The + 64bit fs and gs bases are restored in signal handlers. To avoid + an expensive syscall it is first checked if the bases really need + to be set by comparing the memory fs/gs and the original base + point to. There is a very small chance that that involves a page + fault, which needs to be fixed up. + + * [r1626] src/include/cpu.h: + Fix FPU save/restore on x86-64. + +2006-10-20 Bart + + * [r1625] src/base/misc/Makefile, src/base/misc/fatfs.c, + src/base/misc/fatfs.h, src/base/misc/fatfs_boot.S: + Simplify linking in fatfs_boot.S, similar to bootsect.S before. + + * [r1624] src/tools/periph/Makefile, src/tools/periph/bootsect.S, + src/tools/periph/bootsect.h, src/tools/periph/mkfatimage.c, + src/tools/periph/mkfatimage16.c: + Link compiled bootsect.S directly into mkfatimage* instead of + using hex. Get rid of things that are overwritten in bootsect.S; + I can't find anything left (C) Peter Norton. + +2006-10-19 Bart + + * [r1623] src/base/bios/Makefile, src/env/video/remap_asm.S: + Adjustments for older GCCs and 64-bit noexecstack. + + * [r1622] src/base/bios/Makefile, src/base/bios/bios.S, + src/dosext/dpmi/dpmisel.S, src/env/video/Makefile, + src/env/video/remap_asm.S, src/env/video/vesabios.S, + src/env/video/vesabios_pm.S, src/include/macros86.h: + Added sections to assembly files to mark a non-executable stack. + (suggestion from James Courtier-Dutton, linux-msdos). For bios.S + and vesabios.S it was necessary to compile without intermediate + linking, which adds a few "-bios_f000"'s but simplifies Makefiles + considerably. + + * [r1621] src/dosext/dpmi/dpmi.c: + Fix C problem with wrongly placed label. + + * [r1620] src/arch/linux/async/signal.c, + src/arch/linux/async/sigsegv.c, src/dosext/dpmi/dpmi.c, + src/dosext/dpmi/dpmisel.S, src/dosext/dpmi/dpmisel.h: + Use IRET to jump into DPMI client code on x86-64. It's nice + because it pops up ss:esp even for same-privilege transfers from + long mode. To avoid the infamous 16-bit stack ESP corruption + issue the iret stack frame is allocated at 0x100000000 or a + similar address with bits 16-31 zero. + +2006-10-18 Bart + + * [r1619] src/arch/linux/async/signal.c, + src/arch/linux/async/sigsegv.c, + src/arch/linux/debugger/dis8086.c, + src/arch/linux/mapping/mapping.c, src/base/misc/ioctl.c, + src/dosext/dpmi/dpmi.c, src/dosext/dpmi/dpmi.h, + src/emu-i386/cpu.c, src/emu-i386/do_vm86.c, + src/emu-i386/simx86/cpu-emu.c, src/emu.c, src/env/video/vc.c, + src/include/cpu.h, src/include/emu.h, src/include/vm86plus.h, + src/plugin/term/terminal.c: + Cleaned up vm86plus.h. Created a new init_handler function in + signal.c which takes care of flags, %fs, %gs etc. dis8086 64-bit + compat. Create dpmi_iret_setup for exiting signal handlers to + DPMI client code. + + * [r1618] src/arch/linux/debugger/dis8086.c, + src/arch/linux/debugger/mhpdbgc.c, src/base/misc/dump.c, + src/emu-i386/simx86/cpu-emu.c, src/env/video/instremu.c, + src/include/dis8086.h: + Replaced disassembler of unclearly licensed origin by one adapted + from 2asm via MAME and DOSBox. + +2006-10-17 Bart + + * [r1617] src/plugin/commands/unix.c: + Fixed quiet switching for dosemu in dumb mode with a given + command. + +2006-10-12 Bart + + * [r1616] src/dosext/dpmi/dpmi.c, src/dosext/dpmi/dpmisel.S: + Improve direct DPMI switch jmp for x86-64: don't use instructions + that change flags after popf, eliminate use of r10, and tell gcc + that r8 and r9 are clobbered. + +2006-10-11 Bart + + * [r1615] src/arch/linux/async/sigsegv.c, src/dosext/dpmi/dpmi.c, + src/dosext/dpmi/dpmi.h, src/dosext/dpmi/dpmisel.S, + src/dosext/dpmi/dpmisel.h: + Removed sigsetjmp/longjmp usage for DPMI again. A tiny amount of + assembly can do it more efficiently (saves the sigprocmask + syscall that sigsetjmp makes). + +2006-10-10 Bart + + * [r1614] src/arch/linux/async/sigsegv.c, src/dosext/dpmi/dpmi.c, + src/dosext/dpmi/dpmisel.S, src/dosext/dpmi/dpmisel.h, + src/emu-i386/cpu.c, src/emu-i386/simx86/cpu-emu.c: + Implemented native DPMI code execution for x86-64. Only tested + with a simple Openwatcom "wcc hello.c" but that works. It needed + a few workarounds involving %ss and in particular a nasty hack + for %fs (used for TLS). + + * [r1613] src/emu-i386/simx86/codegen-x86.h, + src/emu-i386/simx86/codegen.h, src/emu-i386/simx86/cpu-emu.c, + src/emu-i386/simx86/protmode.c, src/include/emu-ldt.h: + Change lots of long * to int * in CPUEMU. Now basic DPMI works + for $_cpu_emu="full" on x86-64. + + * [r1612] src/arch/linux/async/debug.c: + Add an extra backtrace without full (in case gdb with full + fails), and a simple backtrace using glibc backtrace(), for + suid-root. + + * [r1611] src/base/async/int.c, src/dosext/dpmi/dpmi.c, + src/include/Asm/ldt.h: + Changed lots of longs to int in the DPMI and LDT code, and a few + for int15(extmem). Now DPMI LDT entry allocation works for 64bit. + + * [r1610] src/arch/linux/mapping/mapping.c: + Map DPMI, LFB, and hardware RAM high memory into the first 2G of + address space on x86-64 using MAP_32BIT. + + * [r1609] src/arch/linux/debugger/mhpdbgc.c, + src/emu-i386/simx86/sigsegv.c, src/include/mhpdbg.h: + Fix dosdebug (ldt, symbol reading) and cpu exception printf for + 64-bit. + + * [r1608] src/dosext/dpmi/dpmi.c, + src/emu-i386/simx86/codegen-sim.c, src/include/bitops.h: + Replaced simulated BITOP code by non-self-modifying code, adding + the used change_bit and find_bit_r inline functions to bitops.h. + Some more long->int conversions for 64bit compatibility. + +2006-10-08 Bart + + * [r1607] src/include/cpu.h, src/include/timers.h: + Fix rdtsc (fixes random slow-downs and hangs) and unix.com in + 64-bit. Getting more usable. + + * [r1606] src/plugin/sdl/sdl.c: + Tweak fullscreen mode selection (for 320x200 you now get 640x400 + instead of 640x350) in SDL, and work around a crash for fbdev + etc. + + * [r1605] src/plugin/X/X.c, src/plugin/X/screen.c, + src/plugin/X/screen.h, src/plugin/sdl/sdl.c: + Move X dependent part of copy/paste support into screen.c. + Support copy/paste with SDL/X11. + + * [r1604] src/plugin/X/X_keysyms.c, src/plugin/X/keyb_X_keycode.c: + Fix Alt-Gr handling with Xorg 7.1 (Reinhard & Michael Karcher) + + * [r1603] src/env/video/vc.c: + Fix i386 compilation. + + * [r1602] default-configure: + Fix configure with environment variables. + + * [r1601] src/arch/linux/async/signal.c, + src/arch/linux/async/sigsegv.c, src/base/misc/dump.c, + src/base/misc/utilities.c, src/emu-i386/cpu.c, + src/emu-i386/simx86/cpu-emu.c, src/emu-i386/simx86/sigsegv.c, + src/emu-i386/simx86/syncpu.h, src/env/video/vc.c, + src/include/cpu-emu.h, src/include/cpu.h, src/include/emu.h: + Implemented proper x86_64 signal handling. Have just one sigalrm + handler. Fix error() to not mysteriously give different output on + stderr compared to boot.log. Very basic stuff works now (freedos + dir/exitemu). + + * [r1600] src/base/misc/dump.c, src/dosext/dpmi/dpmi.c, + src/dosext/dpmi/dpmi.h, src/dosext/dpmi/msdos.c, + src/dosext/mfs/mfs.c, src/dosext/mfs/mfs.h, + src/dosext/net/net/ipx.c, src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/codegen-sim.h, src/emu-i386/simx86/cpu-emu.c, + src/emu-i386/simx86/fp87-sim.c, src/emu-i386/simx86/host.h, + src/emu-i386/simx86/interp.c, src/include/cpu.h, + src/include/dos2linux.h, src/plugin/X/X.c, src/plugin/X/screen.c, + src/plugin/term/keyb_slang.c, src/plugin/term/terminal.c: + More 64-bit compatibility, mostly changing "long" to "int". + +2006-10-07 Bart + + * [r1599] src/env/video/vgaemu.c, src/include/vgaemu.h: + Make vga.mem.scratch_page a void * to avoid having to shift all + the time, and 'unsigned int' did not suffice for x86-64 any more. + + * [r1598] src/arch/linux/debugger/dosdebug.c, + src/arch/linux/debugger/mhpdbg.c, + src/arch/linux/debugger/mhpdbgc.c, src/base/bios/hlt.c, + src/base/bios/int10.c, src/base/bios/setup.c, + src/base/dev/dma/dma.c, src/base/dev/pic/pic.c, + src/base/init/config.c, src/base/init/init.c, + src/base/init/memcheck.c, src/base/mouse/mouse.c, + src/base/speaker/console_speaker.c, src/dosext/dpmi/msdos.c, + src/dosext/drivers/aspi.c, src/dosext/mfs/lfn.c, + src/dosext/mfs/mangle.c, src/dosext/mfs/mfs.c, + src/dosext/mfs/mscdex.c, src/dosext/misc/emm.c, + src/dosext/misc/xms.c, src/dosext/sound/sound.c, src/emu.c, + src/env/video/remap.c, src/env/video/remap_pent.c, + src/env/video/vga.c, src/include/dos2linux.h, + src/include/memory.h, src/plugin/commands/blaster.c, + src/plugin/translate/Makefile, src/plugin/translate/translate.c: + Fix all x86-64 warnings in the main DOSEMU code. 64bit DOSEMU + links now. + + * [r1597] src/emu-i386/simx86/cpatch.c, + src/emu-i386/simx86/cpu-emu.c, src/emu-i386/simx86/host.h, + src/emu-i386/simx86/interp.c, src/emu-i386/simx86/memory.c, + src/emu-i386/simx86/sigsegv.c, src/include/emu-ldt.h: + Fix x86-64 simx86 warnings. + + * [r1596] src/arch/linux/async/signal.c, + src/arch/linux/async/sigsegv.c, src/arch/linux/mapping/mapfile.c, + src/arch/linux/mapping/mapping.c, + src/arch/linux/mapping/mapshm.c, src/base/dev/misc/rtc.c, + src/base/misc/disks.c, src/base/misc/dos2linux.c, + src/base/misc/dump.c, src/base/misc/fatfs.c, + src/base/misc/ioctl.c, src/base/misc/smalloc.c, + src/dosext/dpmi/dpmi.c, src/dosext/dpmi/msdos.c, + src/dosext/dpmi/vxd.c, src/dosext/dpmi/windefs.h, + src/emu-i386/cpu.c, src/emu-i386/do_vm86.c, + src/emu-i386/simx86/Makefile, src/emu-i386/simx86/codegen-sim.c, + src/emu-i386/simx86/codegen-x86.c, src/emu-i386/simx86/cpu-emu.c, + src/emu-i386/simx86/fp87-x86.c, src/emu-i386/simx86/sigsegv.c, + src/emu-i386/simx86/trees.c, src/include/bitops.h, + src/include/cpu-emu.h, src/include/cpu.h, src/include/emu.h, + src/include/vm86plus.h: + Another batch of x86_64 changes. Using the native x86_64 + sigcontext after all, where _rip is EIP or RIP (from DOSEMU + itself) and _eip is always 32-bit. + +2006-10-06 Bart + + * [r1595] src/dosext/dpmi/dpmi.c, src/dosext/dpmi/dpmi.h, + src/env/video/hgc.c, src/env/video/instremu.c, + src/env/video/remap.c, src/env/video/remap_asm.S, + src/env/video/text.c, src/env/video/vbe.c, src/env/video/vc.c, + src/env/video/vesa.c, src/env/video/vesabios_pm.S, + src/env/video/vgaemu.c, src/env/video/video.c, src/include/cpu.h, + src/include/memory.h, src/include/termio.h, src/include/vgaemu.h, + src/include/video.h: + First batch of x86-64 compatibility changes, mostly relating to + integer to pointer conversions. instremu is most affected. + +2006-10-05 Bart + + * [r1594] src/arch/linux/Makefile.main: + Correct "tail +9" command to "tail -n +9". Closes #1516259. + + * [r1593] src/arch/linux/debugger/dis8086.c, src/dosext/misc/emm.c, + src/env/video/vc.c: + Fix gcc-4.2 warnings. + + * [r1592] src/arch/linux/mapping/mapfile.c, + src/arch/linux/mapping/mapping.c, + src/arch/linux/mapping/mapshm.c, src/base/misc/hma.c, + src/dosext/dpmi/memory.c, src/dosext/misc/emm.c, src/emu.c, + src/env/video/dualmon.c, src/env/video/hgc.c, + src/env/video/matrox.c, src/env/video/vc.c, + src/env/video/vgaemu.c, src/include/mapping.h: + Change mapping definitions to be more similar to the libc + prototypes, to save on casting (particularly for x86-64, later). + Handle alias maps via a separate alias_mapping() function because + here are two pointers instead of pointer + offset. + + * [r1591] src/include/memory.h: + Fix gcc warning in cmos.c. + + * [r1590] src/dosext/dpmi/dpmi.c, src/emu-i386/simx86/cpu-emu.c: + Basic cpu-emu DPMI fixes. At least Jazz Jackrabbit starts now, + but it's still not very stable. + + * [r1589] src/arch/linux/async/signal.c, + src/arch/linux/async/sigsegv.c, src/dosext/dpmi/dpmi.c, + src/dosext/dpmi/dpmi.h, src/emu-i386/simx86/cpu-emu.c, + src/include/cpu-emu.h, src/include/cpu.h: + Do not store the dpmi retcode in _eax any more, but return it + using the normal C mechanism. There are also some basic cpuemu + DPMI fixes. + +2006-10-04 Bart + + * [r1588] src/arch/linux/async/sigsegv.c, src/dosext/dpmi/dpmi.c, + src/dosext/dpmi/dpmi.h, src/dosext/dpmi/dpmisel.S, + src/dosext/dpmi/dpmisel.h: + Clean up unused returns, marking functions "noreturn". Move some + some more asm to dpmisel.S, and make dpmisel() as small as + possible. Using dpmisel() as %cs for the intermediate jmp just + confuses the selector checks so this isn't implemented after all. + Avoid various casts. + + * [r1587] src/arch/linux/async/sigsegv.c, src/dosext/dpmi/dpmi.c, + src/dosext/dpmi/dpmi.h, src/emu-i386/cpu.c, + src/emu-i386/simx86/sigsegv.c, src/env/video/vgaemu.c, + src/include/cpu.h: + Remove _emu_stack_frame. Use a special static eflags_fs_gs struct + to save and restore those, and handle the rest using the real CPU + segment registers, using asm macros. + + * [r1586] src/dosext/dpmi/dpmi.c: + Eliminate one-time indirect DPMI switch to save FPU context. It's + not necessary any more after inheriting it from vm86. + +2006-10-03 Bart + + * [r1585] src/dosext/dpmi/dpmi.c, src/emu-i386/cpu.c, + src/emu-i386/do_vm86.c: + Do not save and restore the emulator FPU state (only VM86 and + DPMI states): savefpstate (fsave) also resets the current FPU + state which is good enough for calling FPU-using routines. + + * [r1584] src/base/dev/misc/rtc.c: + Replace 1e6 by 1000000: no need for floating point here. + + * [r1583] src/base/bios/bios.S, src/dosext/dpmi/Makefile, + src/dosext/dpmi/dpmi.c, src/dosext/dpmi/dpmisel.S, + src/dosext/dpmi/dpmisel.h, src/dosext/dpmi/msdos.c, + src/dosext/dpmi/vxd.c, src/include/bios.h: + Move all protected mode HLTs and the intermediate direct DPMI jmp + to a seperate memory area from the BIOS, and point dpmi_sel() to + that. + +2006-10-01 Bart + + * [r1582] configure, configure.ac: + Better fix for configure (the previous one only worked with one + option). + +2006-09-30 Bart + + * [r1581] configure, src/include/config.h.in, + src/plugin/kbd_unicode/configure, + src/plugin/kbd_unicode/include/kbd_unicode_config.h.in, + src/plugin/sdl/configure: + Regenerate autoconf-generated files with autoconf 2.60a. + + * [r1580] configure.ac: + Fix argument issue with calling default-configure from configure. + + * [r1579] src/base/init/lexer.l.in, src/base/init/parser.y.in: + Use strtoul to parse dosemu.conf numbers so that hardware ram etc + work with negative addresses. + + * [r1578] etc/dosemu.conf, src/arch/linux/mapping/mapping.c, + src/base/misc/utilities.c, src/dosext/dpmi/memory.c, + src/include/mapping.h, src/include/utilities.h: + Use non-zero mmap target addresses without MAP_FIXED. This avoids + the need to parse /proc/self/maps. Make (auto) the new default + for $_dpmi_base. Change dpmi_alloc_pool for $_dpmi_base=(auto) to + reject negative addresses; it will then try an address 128MB past + the current brk limit which is fairly close to the old default. + + * [r1577] src/arch/linux/mapping/mapfile.c, src/env/video/vgaemu.c: + Move VGAEMU LFB memory out of the shared memory pool (it can be + anonymous). Now the memory pool can be mmap'ed rw instead of rwx + (better protection with exec-shield); just the DOS-accessible + memory is rwx. + + * [r1576] src/arch/linux/mapping/mapfile.c: + Implemented workaround for failed PROT_EXEC on noexec shm mounts. + + * [r1575] src/arch/linux/async/signal.c, + src/arch/linux/async/sigsegv.c, src/dosext/dpmi/dpmi.c, + src/dosext/dpmi/dpmi.h: + Use sigsetjmp() and siglongjmp() to enter and return from DPMI, + instead of fiddling with CPU registers using assembly language, + greatly simplifying direct_dpmi_switch(). It will also be + portable to x86-64. The changes are as minimal as possible but + more cleanups can certainly be done (most of the _emu_stack_frame + can be eliminated for instance). + +2006-09-28 Bart + + * [r1574] src/base/bios/bios.S, src/dosext/dpmi/dpmi.c, + src/include/memory.h: + Use a special mmap'ed area for direct dpmi switching. This avoids + using the BIOS for data transfers and (wrt Stas' solution) has + the advantage of working with -fpic/pie + +2006-04-29 Bart + + * [r1573] src/plugin/term/terminal.c: + Streamline terminal character write and ACS table construction + routines. Don't use ACS characters when it's not necessary (the + external charset already contains the required characters); this + helps with $_external_charset="cp437" on xterm -k8 -font vga + (Putty makes no difference). Only use ACS characters that are + specified by the relevant terminfo entry. + + * [r1572] src/plugin/X/X.c: + Enable use of unicode fonts for $_X_font, such as uni-vga, but + also misc-fixed*iso10646-1. The logic assumes DOS codepage + encoding for fonts with 256 or less characters, and UCS-16 + otherwise. + + * [r1571] src/base/bios/bios.S, src/dosext/dpmi/dpmi.c, + src/include/memory.h: + Apply %cs segment limit workaround for direct_dpmi_switch on + exec-shield kernels. Fixes #1478658: Crash on every DPMI program + under Linux 2.6.16. + +2006-04-11 Bart + + * [r1570] src/include/pci.h: + Avoid problems with by just defining the PCI_ + constants ourselves. + + * [r1569] src/base/init/config.c: + Moved -2 -3 etc option parsing after config file parsing so they + actually work. + +2006-03-11 Bart + + * [r1568] src/env/video/render.c: + Scale small modes towards ~640x400 (shows testcase for #1443448 + in the right proportions) + +2006-03-05 Bart + + * [r1567] src/emu-i386/cputime.c: + Fix TSC handling during DOSEMU freeze. Fixes #1441256 keyboard + issue (current cvs) + +2006-03-04 Stas + + * [r1566] src/base/bios/bios.S, src/dosext/dpmi/dpmi.c, + src/dosext/dpmi/dpmi.h, src/dosext/dpmi/msdos.c, + src/dosext/dpmi/msdos.h, src/include/bios.h: + Preserve the PM regs unpon the realmode interrupts. That allows + to merge the msdos_pre_exec() with msdos_pre_extender(), and + msdos_post_exec() became completely unnecessary and removed. Also + cleaned up the dpmi_init() and removed the "psp" field from the + DPMI struct (msdos.c now handles PSP) + +2006-03-04 Bart + + * [r1565] src/tools/periph/Makefile: + Fix dependency for bootsect.h after make distclean. + +2006-03-04 Stas + + * [r1564] src/dosext/dpmi/dpmi.c, src/dosext/dpmi/dpmi.h, + src/dosext/dpmi/msdos.c, src/dosext/dpmi/msdos.h: + Remove wrong env-mangling code (#1442375) + +2006-03-04 Bart + + * [r1563] src/include/vc.h, src/tools/tools86.c: + Fix GCC warnings. + + * [r1562] src/commands/Makefile, src/plugin/commands/Makefile, + src/tools/Makefile, src/tools/periph/Makefile: + Use dependencies for commands and tools; using intermediate .o + files was the easiest (and GCC-version-independent) way to do + this. Fixes #1415598 (build deps for commands are missing). + +2006-03-04 Stas + + * [r1561] src/base/misc/utilities.c: + reapplying + + * [r1560] configure.ac: + reapplying + +2006-03-04 Bart + + * [r1559] src/env/video/render.c: + Dirty everything in vgaemu re-inits. Fixes #1407567 (vgaemu + problem with stdemo). + +2006-03-03 Bart + + * [r1558] (all directories) + Set ignore properties on directories. + + * [r1557] src/base/serial/ser_init.c, src/dosext/misc/xms.c: + Replace $Header$ by $ Id: $ (svn only understands $ Id: $) + + * [r1556] src/commands/cmdline.c, src/commands/detect.h, + src/commands/dosdbg.c, src/commands/emulib.h, + src/commands/emumouse.c, src/commands/lredir.c, + src/commands/msetenv.c, src/commands/precompiled/autoexec.bat, + src/commands/turboc.cfg, src/commands/unix.c, + src/commands/xmode.c: + Set svn:eol-style to CRLF for all DOS text files. + +2006-02-28 Bart + + * [r1555] src/include/vm86plus.h: + Don't set force_return_for_pic. I'll leave it in the macro like + this for now, so if we really find a testcase we can re-enable + it. + + * [r1554] src/arch/linux/async/signal.c, src/base/dev/pic/pic.c, + src/dosext/dpmi/dpmi.c: + More correct handling of VIP; only have it set when VIF is not + set, and and a hardware interrupt is requested. Optimize DPMI so + that it does not return to dosemu code when interrupts are + pending but VIF is not set. + + * [r1553] src/include/cpu.h: + Change pointer casts to use unions (to allow -O2 without + -fno-strict-aliasing). This does not allow removal of + -fno-strict-aliasing but is a step in that direction. + +2006-02-27 Stas + + * [r1552] src/dosext/dpmi/vxd.c, src/dosext/dpmi/windefs.h: + Compile in the win32s support. It is still not activated, but at + least it is now clear what functions needs to be implemented (not + too much). + +2006-02-27 Bart + + * [r1551] src/base/init/config.c, src/base/init/init.c, + src/base/init/parser.y.in, src/base/misc/utilities.c, + src/base/serial/ser_init.c, src/env/video/vc.c, + src/env/video/video.c, src/include/emu.h, + src/include/utilities.h, src/include/video.h, + src/plugin/gpm/mouse_gpm.c, src/plugin/kbd_unicode/keyb_raw.c, + src/plugin/term/mouse_xterm.c, src/plugin/term/term_core.c: + Replaced is_console(), check_console() and config.console by a + single on_console() function. Moved console config scrubbing to + config_post_process(). Made scr_state_init() static. + +2006-02-26 Stas + + * [r1550] src/dosext/dpmi/msdos.c: + Never copy >64K + + * [r1549] src/dosext/dpmi/dpmi.c, src/emu.c: + unused includes + +2006-02-24 Bart + + * [r1548] src/base/bios/int10.c: + set cursor shape after font height adjustments in int10/ah=0. + Fixes #1418023 duke3d and cursor at wrong location + + * [r1547] src/base/init/parser.y.in: + Move getpwuid() check to only do this when DOSEMU is executed + suid or sudo: there is no need to access /etc/passwd when running + non-suid. + +2006-02-20 Stas + + * [r1546] src/base/bios/bios.S, src/dosext/dpmi/dpmi.c, + src/dosext/dpmi/msdos.c, src/dosext/dpmi/vxd.c, + src/dosext/dpmi/vxd.h, src/dosext/misc/emm.c, src/include/bios.h: + Cosmetic stuff. Mostly moving the VXD code from dpmi.c to vxd.c. + +2006-02-20 Bart + + * [r1545] src/plugin/commands/emumouse.c: + Fix printf warnings. + +2006-02-16 Stas + + * [r1544] src/base/misc/fatfs.c: + Make sure to add command.com/config.sys/autoexec.bat to FAT image + before the virtual floppy overflows. + +2006-02-14 Stas + + * [r1543] src/emu.c: + Regard config.exitearly better than before. + + * [r1542] src/base/misc/fatfs.c: + This makes PartitionMagic happy again. + + * [r1541] src/base/misc/disks.c: + Replaced a few early leavedos() with "exitearly" to avoid + crashes. + + * [r1540] src/base/bios/bios.S: + Added Ctrl-Alt-Del handling into bios. Dosemu handling still + overrides though, as it seem to work "better" for some reasons. + + * [r1539] src/base/dev/pic/pic.c: + This should fix the keyboard bug of #1430175 + +2006-02-13 Stas + + * [r1538] src/dosext/dpmi/dpmi.c: + Fix corner case with VIF handling (#1430175) + + * [r1537] src/base/dev/pic/pic.c: + This hack is no longer necessary. + + * [r1536] src/dosext/dpmi/msdos.c, src/include/doshelpers.h: + Translation for dosemu PRINT_STRING helper. + +2006-02-11 Stas + + * [r1535] src/include/dos2linux.h, src/plugin/commands/commands.c: + Extended dpmi.com + +2006-02-09 Stas + + * [r1534] src/base/misc/disks.c, src/base/misc/fatfs.c, + src/include/disks.h, src/plugin/commands/lredir.c: + - Made it possible to boot FreeDOS from virtual floppy + - Scan boot dir only once for any DOS, instead of 2 times for FreeDOS + and 5 for other DOSes. (not scanning for FreeDOS at all makes it + impossible to boot from virtual floppy) + - lredir now tries the FAT automatically when source redirection + doesnt exist (for the recent redir-duplication feature) + + * [r1533] src/emu-i386/do_vm86.c, src/emu.c, src/include/emu.h: + Shut up do_call_back() a little. (part of #1422921) + +2006-02-08 Stas + + * [r1532] src/dosext/dpmi/dpmi.c, src/emu-i386/do_vm86.c, + src/emu.c, src/include/emu.h: + Call the hardware stuff from the DPMI too. (part of #1422921) + + * [r1531] src/base/init/parser.y.in: + Print more info about "illegal user" + +2006-02-08 Bart + + * [r1530] src/dosext/mfs/lfn.c, src/dosext/mfs/mfs.c, + src/dosext/mfs/mfs.h: + Merge common rmdir, mkdir, and rename MFS code between SFN and + LFN. Fix problem with opening files in the root directory. Honour + read only drives in the LFN code. Fixes #1426126 (LFN problem + with current DosEmu) + +2006-02-07 Bart + + * [r1529] src/base/init/lexer.l.in, src/base/init/parser.y.in, + src/base/init/parsglob.h: + Remove pointer casts and parsglob.h in the parser for better type + safety. Use the Bison types typed_expr, int_expr, bool_expr, + real_expr, and string_expr instead of the casts in V_VAL, I_VAL, + B_VAL, R_VAL, and S_VAL. An integer "expression" now derives from + a typed_expr (the union); its fp counterpart is a + "real_expression". Remove unused TIMINT and OPTIONS tokens. Moved + string quote removal from the lexer to the parser to be able to + handle 'a' (and not "a" or a) cleanly as a character integer + constant. + +2006-02-06 Stas + + * [r1528] src/dosext/dpmi/msdos.c: + Fix DPMI corrupting the EMS state. (#1415332) + +2006-02-03 Bart + + * [r1527] src/base/init/parser.y.in, src/base/init/parsglob.h: + Made the parser 64-bit clean. + +2006-02-02 Stas + + * [r1526] src/base/dev/pic/pic.c, src/dosext/sound/sound.c, + src/include/pic.h: + Reenabled sound hack. Apparently it won't be replaced by the new + code ever before 1.4. + + * [r1525] src/emu.c: + use LOWMEM() + + * [r1524] src/base/dev/misc/rtc.c: + pic_untrigger() on ACK. + +2006-02-01 Stas + + * [r1523] src/base/misc/smalloc.c, src/dosext/dpmi/memory.c, + src/include/smalloc.h: + - Added uncommitted memory support to smalloc + - Used that to make + the free()'d DPMI memory uncommitted (FR#1417852) + + * [r1522] src/base/bios/bios.S, src/base/bios/setup.c, + src/include/memory.h: + Fix intvectors 1e, 41, 46. This makes it possible to use win98dos + (even with $_lfn_support=(on)), because this fix stops the memory + corruptions which happen when win98dos *writes* to the disk + tables. + + * [r1521] src/base/async/int.c: + This must be error(). + + * [r1520] src/base/bios/hlt.c: + Take start_addr into account for hlt. + +2006-01-31 Bart + + * [r1519] dist/dosemu: + Fix dosemu script (#1420769) + +2006-01-31 Stas + + * [r1518] src/base/async/int.c: + Fixed "bpint" and "tf" of dosdebug. + +2006-01-31 Bart + + * [r1517] src/arch/linux/mapping/mapfile.c, + src/base/async/dyndeb.c, src/base/misc/hma.c, + src/dosext/misc/emm.c, src/env/video/matrox.c, + src/env/video/vgaemu.c: + Remove various to-int-casts of (mostly mmap'ed) pointers. The one + in matrox.c was a bug with flex-mmap. + +2006-01-30 Bart + + * [r1516] src/dosext/misc/xms.c, src/env/video/vgaemu.c: + Fix Bochs vgabios (and manual mode setting in general) + interaction with vga.mem.wrap. Let XMS register all 6 relevant + bytes within the hlt table (that aren't all hlt, but that does + not matter). Fixes #1418856. + + * [r1515] src/emu-i386/cpu.c, src/emu-i386/simx86/cpu-emu.c, + src/emu-i386/simx86/interp.c, src/emu-i386/simx86/protmode.c, + src/emu-i386/simx86/protmode.h, src/emu-i386/simx86/sigsegv.c, + src/emu-i386/simx86/syncpu.h: + Use "int" instead of "long" for most things referring to 32-bit + entities in cpuemu (for 64 bit compatibility), and changed the + related printf()s. + + * [r1514] src/base/serial/ser_init.c, src/dosext/dpmi/msdos.c, + src/dosext/mfs/mfs.c, src/dosext/misc/emm.c, + src/dosext/net/net/ipx.c, src/emu-i386/simx86/interp.c, + src/env/video/vc.c, src/env/video/vesa.c, src/env/video/vgaemu.c, + src/plugin/sdl/sdl.c: + Change %#x with casts to %p without casts in debug printf()s. + + * [r1513] src/env/video/vc.c, src/plugin/kbd_unicode/keyb_raw.c: + Remove ioctl casts that are unnecessary and wrong for 64=bit + code. + +2006-01-28 Stas + + * [r1512] src/doc/HOWTO/EMUfailure.sgml: + Updated. + + * [r1511] src/base/dev/misc/rtc.c, src/emu-i386/do_vm86.c, + src/include/cmos.h: + Implemented the RTC periodic IRQ generator (better later than + never:) Closes FR#1406011. There are not too much of those + urgently missing/missed parts now. dosemu now only lacks the + proper LPT support, and well, the sound. The rest is there. + +2006-01-28 Bart + + * [r1510] src/base/init/install.c: + Fix wrong strlen() in terminal_read(). + + * [r1509] src/base/init/install.c, src/emu.c: + Fix disclaimer file content and behaviour with console_video on. + +2006-01-28 Stas + + * [r1508] src/dosext/dpmi/dpmi.c: + "Linux DOSEMU" must die. ;-) + + * [r1507] src/base/async/int.c: + - Made kill_time() to use sigsuspend() instead of select() + - Removed some #if0'd stuff + - Removed precard_xxx and card_init since they seem to be unused + +2006-01-28 Bart + + * [r1506] src/base/async/int.c, src/base/init/install.c, + src/base/init/parser.y.in, src/base/misc/dos2linux.c, + src/base/misc/utilities.c, src/emu.c, src/include/dos2linux.h, + src/include/dosemu_debug.h: + Move disclaimer and install prompts from dos_post_boot to banner + code, using BIOS calls. + +2006-01-27 Stas + + * [r1505] src/dosext/misc/emm.c: + Release EMS memory upon soft-reboot, or it overflows. + + * [r1504] src/base/misc/disks.c: + Report virtual floppy as read-only. + + * [r1503] src/dosext/dpmi/dpmi.c, src/dosext/dpmi/msdos.c, + src/dosext/dpmi/msdos.h: + Don't let the dpmi_exec code to touch the EMS page. (probably + fixes #1415332) + + * [r1502] src/base/dev/misc/rtc.c: + Next bunch of the copy/paste fixes. :( + + * [r1501] src/base/dev/misc/rtc.c: + Nasty copy/paste bugs of the previous commit. + +2006-01-26 Stas + + * [r1500] src/base/async/int.c, src/base/dev/misc/cmos.c, + src/base/dev/misc/rtc.c, src/include/cmos.h: + - Sorted and applied all the Eric Auer's RTC fixes, except those + that look wrong. + - Did some (rather heavy-handed) cleanups and + reorganizations, along with the minor fixes here and there, in a + preparation to the periodic IRQ generator. + - Got rid of pic_seti()/rtc_int8() usage. + + * [r1499] src/commands/ems.S, src/include/emm.h: + - Use a separate message for ems.sys when the XMS is disabled. - + Made ems.sys to respect the version again. Note that the ver + checking was introduced because ems.sys had a severe bug which + caused the obscure lock-ups for some programs. And besides, what + was the reason behind *that* way of disabling the check? - + Incremented ems.sys version + +2006-01-25 Bart + + * [r1498] src/base/async/int.c: + Correct si and bp in process_master_boot_record(). Make int.c + 64-bit clean. + +2006-01-25 Stas + + * [r1497] src/base/misc/fatfs.c: + Fix a few fat bugs in FAT12 + +2006-01-23 Bart + + * [r1496] src/dosext/mfs/lfn.c, src/dosext/mfs/mfs.c: + Improve UNC support so that it works with environment variables, + LFNs and "dir" (depending on the command.com that is used). + + * [r1495] src/dosext/mfs/mfs.c, src/dosext/mfs/mfs.h: + Added basic support for UNC pathnames in the style + \\LINUX\FS\BIN\LS, using a permanent "drive 32". + +2006-01-22 Stas + + * [r1494] src/plugin/commands/lredir.c: + Convinced myself that &ptr->memb is not a dereference and + therefore is safe to use with the DOS addresses. + +2006-01-22 Bart + + * [r1493] dist/dosemu, dist/dosemu.bindist, dist/dosemu.systemwide, + man/Makefile, man/dosemu.1.in, man/dosemu.bin.1.in, + src/arch/linux/Makefile.main, src/base/async/int.c, + src/base/init/Makefile, src/base/init/config.c, + src/base/init/install.c, src/base/init/parser.y.in, src/emu.c, + src/include/emu.h: + Moved all the dosemu script questions to C. This allows asking in + the DOSEMU window after booting if possible (so window managers + don't need to specify "run in terminal" anymore), and avoiding + question if not necessary (if the user already installed a DOS + manually). Propagate dosemu -install option to dosemu.bin -i + option. Remove X first start info, users probably know how to + operate the "x" button on an X window. + +2006-01-21 Stas + + * [r1492] src/plugin/commands/lredir.c: + Carefull with the DOS memory accesses. + + * [r1491] src/plugin/commands/lredir.c: + Implemented an ability for lredir to replicate the redirection of + an existing drives, even the not redirected ones. + + * [r1490] etc/global.conf, src/base/misc/disks.c, + src/base/misc/fatfs.c, src/base/misc/fatfs.h, + src/base/misc/fatfs_boot.S, src/base/misc/ioctl.c, + src/dosext/mfs/mfs.c, src/include/disks.h, + src/include/dos2linux.h, src/include/fatfs.h: + - Implemented FAT12 emulation. + - Made $_vbootfloppy, $_floppy_a etc to accept the directories. + - Made it possible to boot from a directory as from floppy + ($_vbootfloppy) + + Fixes (the ones from the top of my head): + - vol label must not contain slashes (NDD complains) + - vol label must not contain lowercase letters + - disabled the emusys hackery in fatfs: it was reverting the + effect of the subst_file_ext() and didn't work. + - made serial numbers unique for all drives + - fix crash in some cases when the boot system is unavailable + (fatfs_boot.S) And probably some more I cant recall... + + * [r1489] src/commands/ems.S, src/dosext/misc/xms.c: + Dont use internal XMS if it is disabled in the config. + +2006-01-19 Stas + + * [r1488] src/base/misc/disks.c: + (cosmetic) This used to hide real bugs from gcc. + +2006-01-19 Bart + + * [r1487] dist/dosemu, dist/dosemu.bindist, dist/dosemu.systemwide: + Use a clearer menu system for initial DOSEMU-FreeDOS + installation. I hope to convert most of this to C, to be executed + inside the DOSEMU window later. + + * [r1486] src/base/async/int.c, src/emu.c, src/include/emu.h: + Move liability disclaimer prompt to dos_post_boot in most + circumstances: no console video and using a directory DOS boot. + + * [r1485] dist/dosemu, dist/dosemu.bindist, dist/dosemu.systemwide, + dist/mkbindist, src/arch/linux/Makefile.main: + Split dosemu script into systemwide, non-systemwide, and common + sections, so some processing is simplified and can be done at + compiletime, not runtime. + +2006-01-18 Stas + + * [r1484] src/base/misc/disks.c: + Fix 2 NULL-deref bugs. + +2006-01-17 Bart + + * [r1483] src/dosext/net/net/ipx.c: + Use #define'd constant for IPX. + + * [r1482] src/dosext/drivers/cdrom.c, src/dosext/net/net/ipx.c: + Fix compiler warnings. The gcc-4.1 warning for ipx.c was a real + bug too. + +2006-01-16 Bart + + * [r1481] src/dosext/mfs/lfn.c, src/dosext/mfs/mfs.c: + Store LFN volume label at offset 0x2c, and \0 at offset 0x130 for + int21/ax=714e. Fixes spaces problem of #1400179 + + * [r1480] src/plugin/X/X.c: + Re-init font_width & font_height upon X mode re-init. Fixes + #1406522 xdosemu: cursor in text mode causes slight pollution. + +2006-01-14 Bart + + * [r1479] src/dosext/mfs/lfn.c: + Accept * as wildcard spec for LFN volume labels (#1400179) + + * [r1478] src/env/video/crtcemu.c: + Set blanking start in crtc registers for VESA modes. Fixes + 800x600 modes (etc), #1406001 Quake cannot change gfx resolution + in X + +2006-01-14 Stas + + * [r1477] src/doc/HOWTO/EMUfailure.sgml: + Update EMUfailures. + +2006-01-14 Bart + + * [r1476] src/plugin/gpm/Makefile: + From Hans de Goede: #1405681 patch fixing x86_64 compilation. + + * [r1475] src/base/bios/int10.c: + Remember clear video memory bit correctly. Fixes #1405682 vgaemu + problem with win98dos. + + * [r1474] src/emu-i386/simx86/codegen-x86.c, + src/emu-i386/simx86/emu86.h, src/emu-i386/simx86/memory.c, + src/emu-i386/simx86/trees.c: + Use smalloc for the generated code buffer in simx86 on a + PROT_EXEC mmap. Fixes #1404395. + +2006-01-13 Bart + + * [r1473] src/arch/linux/mapping/mapping.c, + src/emu-i386/simx86/memory.c: + Remove accidentally unremoved alias code from mapping.c simx86: + mark PROT_READ memory as PROT_EXEC as well (#1404395) + +2006-01-12 Stas + + * [r1472] src/base/dev/pic/pic.c, src/dosext/dpmi/dpmi.c: + cosmetic: + - add sanity checks to pic_iret() + - stop calling pic_iret() from dpmi.c + - this was an optimization which looks unsafe now when all the + HLT checks are removed. + - Unhardcode the pic HLT address! The memory.h things should + IMO never be hardcoded in different places. + +2006-01-12 Bart + + * [r1471] src/env/video/miscemu.c: + Replace horizontal retrace logic with the method used in DosBox. + Solves issues with Commander Keen4 reported on linux-msdos. + +2006-01-07 Bart + + * [r1470] src/dosext/misc/xms.c: + Fix the new internal XMS driver's free/largest memory reporting. + +2006-01-06 Bart + + * [r1469] src/base/misc/disks.c, src/dosext/mfs/mfs.c, + src/include/redirect.h: + Make sure that the redirection resetter only clears internal + state and not DOS state. Merge some common code from + Cancel*Redirection into a new function RemoveRedirection. + + * [r1468] src/dosext/dpmi/dpmi.c: + Revert if (Segments[_ss >> 3].is_32) { change which wasn't meant + to be. + +2006-01-04 Bart + + * [r1467] src/base/misc/disks.c, src/dosext/mfs/mscdex.c: + Reset redirections on ctrl-alt-del. Remove a few too vocal debug + printfs. + + * [r1466] src/base/async/int.c, src/commands/cdrom.S, + src/dosext/drivers/cdrom.c, src/dosext/mfs/Makefile, + src/dosext/mfs/lfn.c, src/dosext/mfs/mfs.c, src/dosext/mfs/mfs.h, + src/dosext/mfs/mscdex.c, src/include/emu.h, + src/plugin/commands/lredir.c: + Added support for int2f/ax=15xx (MSCDEX CDROM functions). Thanks + to dosbox. These make it possible to use low-level access with + lredir'ed CDROMs so that MSCDEX/SHSUCDX/DOSLFN are not necessary + (see also FR:1389792 volume label support for cdroms and + redirected drives), using a new 'C' lredir option. Also use + dos_read() in cdrom.c instead of read() because it may go + straight to DOS memory. + +2006-01-03 Bart + + * [r1465] src/arch/linux/mapping/mapping.c, src/base/async/int.c, + src/base/init/memcheck.c, src/base/misc/hma.c, + src/env/video/vgaemu.c, src/include/memory.h: + Implement int15/ax=e820 (necessary for fdxxms.sys), and some + memcheck infrastructure to construct the memory map. Avoid + putting nul characters in the debug log (use 'e' for emulated + hardware RAM). + +2006-01-02 Bart + + * [r1464] src/env/video/render.c, src/env/video/vgaemu.c: + Take the blanking register into account for determining height + and width. Force width/height changes to update the window as + well. This fixes #1124658 Problems with TIM + + * [r1463] src/env/video/render.c, src/env/video/vgaemu.c, + src/include/vgaemu.h: + Implement vgaemu wrapping around the video memory. This fixes the + colour flicker problem in Commander Keen 4. + + * [r1462] src/base/bios/int10.c: + Fix offset to copy font data from in int10/ax=11*0 + (vga_RAM_to_RAM). Fixes #1394280 (X video problems with Cubic and + Fast Tracker) + + * [r1461] src/env/video/vgaemu.c: + Adjust the width when the number of bpp's change. Fixes Fast + Tracker 2 display completely (#1394280) + + * [r1460] src/dosext/dpmi/dpmi.c: + Search and replace DPMI_CLIENT.is_32 by Segments[_cs>>3].is_32 at + the right places for GPF handling. There may be more of these + necessary (or _ss>>3). Fixes part of bug #1394280 (Fast Tracker + problem): the outw's became outd's + + * [r1459] INSTALL, README, dist/dosemu, doc/README.txt, + man/dosemu.1.in, man/dosemu.bin.1.in, src/doc/README/X, + src/doc/README/batch: + Documentation updates, in particular about batch operation and X. + Don't let "dosemu -dumb" complain if less than 25 lines are + present. + +2006-01-01 Stas + + * [r1458] src/doc/README/Windows, src/doc/README/config: + Minor doc fixes. + +2006-01-01 Bart + + * [r1457] doc/README.txt, etc/dosemu.conf, src/doc/README/CDROM, + src/doc/README/Windows, src/doc/README/config, + src/doc/README/header: + Misc README.txt and dosemu.conf documentation updates (also + obsoletes (#1245910 Documentation for timemode) + + * [r1456] etc/dosemu.conf, src/doc/README/mouse: + Adapt and apply #1153944 improve mouse documentation. + + * [r1455] Makefile.conf.in, configure, configure.ac, man/Makefile, + man/dosemu.1.in, man/dosemu.bin.1.in, man/ru/dosemu.1.in, + man/ru/dosemu.bin.1.in: + Set version and date automatically in man pages. + + * [r1454] src/dosext/mfs/mfs.c: + Do not attempt open/ioctl for attributes for FIFOs etc, only for + regular files and directories. + +2005-12-31 Bart + + * [r1453] src/dosext/mfs/lfn.c: + Do not clear carry if we do not support LFN for the drive. Fixes + #1394114 "weird cd problem with dosemu-1.3.3" + + * [r1452] src/commands/ems.S: + Remove comment about himem.sys. It is a personal decision whether + people like to use himem.sys, himem.exe, fdxms.sys, fdxxms.sys or + the internal XMS driver, all of which have their advantages and + disadvantages. + +2005-12-31 Stas + + * [r1451] src/commands/ems.S, src/dosext/misc/xms.c, + src/include/smalloc.h: + - Make sure smpool is always initialized before using + smfree/smdestroy on it. + - Give the hint about the external XMS driver usage, if not + making it a default. + +2005-12-31 Bart + + * [r1450] src/base/init/config.c, src/base/misc/dos2linux.c, + src/include/dos2linux.h, src/plugin/commands/unix.c: + Mostly from Clarence: unix.com patches in + dosemu-exec-friendly.diff, so that "dosemu keen1.exe" works + again. But do not use heuristics that split options from + commands, by making quotes of filenames that contain spaces + compulsory. + +2005-12-30 Bart + + * [r1449] src/env/video/remap.c: + Map white to white for 15->32 and 16->32. Closes #1244513 color + correction while converting 555,565 to 888 color. + + * [r1448] VERSION, etc/dosemu.conf, etc/global.conf, + src/base/async/int.c, src/base/misc/hma.c, + src/base/misc/smalloc.c, src/commands/ems.S, + src/dosext/misc/emm.c, src/dosext/misc/xms.c, + src/include/doshelpers.h, src/include/hma.h, + src/include/smalloc.h, src/include/xms.h: + Let the internal XMS driver use int15 memory and smalloc. ems.sys + can activate it if no other XMS driver is loaded. Merge old $_xms + and $_ext_mem into a single $_xms option. Correct int15/88 and + e801. ems.sys uses less resident memory if the XMS UMB handler is + not hooked. + +2005-12-29 Bart + + * [r1447] etc/dosemu.conf, etc/global.conf, src/base/init/config.c, + src/base/init/parser.y.in: + Introduce $_pci=(auto) to make the meaning of $_pci more clear. + Initialize $_netdev in restricted suid mode. Report VESA in + config.c. + +2005-12-28 Bart + + * [r1446] src/base/dev/misc/pci.c: + From: Michael and Reinhard Karcher. There was a problem with word + and byte accesses to PCI configuration spaces. + + * [r1445] src/base/async/pci_bios.c: + From: Michael and Reinhard Karcher. Fix segfaults in the PCI BIOS + because pciConfigType was a NULL pointer. + + * [r1444] config.guess, config.sub, install-sh: + Update autoconf helper scripts. + +2005-12-27 Bart + + * [r1443] etc/dosemu.conf, etc/global.conf, + src/base/init/parser.y.in: + Allow "wholedisk" access config from dosemu.conf. Remove + $_mounted_devices checks from global.conf (parser.y.in already + checks). Closes FR #993179 Whole disk access. + + * [r1442] src/base/init/lexer.l.in: + Correct and report line counter in the lexer. + +2005-12-26 Bart + + * [r1441] etc/dosemu.conf, etc/global.conf: + Add $_cdrom CDROM configuration to dosemu.conf. + + * [r1440] src/dosext/mfs/mfs.c: + Initialise the "attr" field at two forgotten places. + + * [r1439] src/dosext/mfs/lfn.c, src/dosext/mfs/mfs.c, + src/dosext/mfs/mfs.h: + Add support for FAT_IOCTL_GET/SET_ATTRIBUTES to directly + manipulate attributes on FAT partitions. Consolidate utime/chmod + calls from LFN/SFN to enforce calling dos_would_allow(). + +2005-12-24 Bart + + * [r1438] src/base/init/config.c: + cpu_override() does not know about config.realcpu anymore. The + checks are done later in config_post_process. + + * [r1437] src/include/memory.h: + The BIOS size was reduced but I had not adjusted memory.h. Fixed. + Solves problem with ZIPKEY (reported by Ralph Alvy, linux-msdos) + + * [r1436] src/emu-i386/cputime.c: + Use sigsuspend() instead of usleep() in idle(). Fixes #1387967 + (dosemu 1.3.3: keyboard is "sticky" on some apps) without + resorting to speed 0. + +2005-12-23 Bart + + * [r1435] src/base/misc/priv.c: + Initialize the group vector to the original user's one when using + sudo. + + * [r1434] src/base/init/config.c, src/base/misc/priv.c, + src/base/misc/utilities.c, src/emu-i386/cpu.c, + src/include/dosemu_config.h: + Save a file descriptor to /proc/self/maps for Fedora kernels in + suid mode; otherwise we lose access after dropping privileges. + + * [r1433] src/env/video/vc.c: + Fix dosemu -s -c: set_process_control must unblock + SIG_ACQUIRE/SIG_RELEASE. + +2005-12-22 Bart + + * [r1432] src/base/async/pci_bios.c, src/base/dev/misc/pci.c: + Added some (untested) support for PCI configuration mechanism 2; + fix GCC warnings. + +2005-12-22 Stas + + * [r1431] src/dosext/dpmi/dpmi.c: + Fix flags handling on prot-mode hardware interrupts (#915452) + Also removed some no longer valid comments. + + * [r1430] etc/dosemu.conf: + Clarify comment. + +2005-12-22 Bart + + * configure, configure.ac: + Make svgalib version check more robust (did not work with gcc-2.95). + + * doc/DANG.txt, src/doc/DANG/DANG.sgml, src/doc/DANG/DANG_CONFIG, + src/doc/DANG/Makefile, src/doc/DANG/make_DANG: + Regenerated DANG + + * etc/dosemu.conf, etc/global.conf, src/base/init/config.c, + src/base/init/lexer.l.in, src/base/init/parser.y.in, + src/dosext/dpmi/memory.c, src/include/emu.h, + src/plugin/commands/Makefile, src/plugin/commands/commands.c, + src/plugin/commands/commands.h, src/plugin/commands/unix.c: + Apply #1023178 Option for disabling DJGPP null checks. + Also add a dpmi.com command, with a -x option similar to cwsdpmi. + Shorten unix.com usage help a bit. + +2005-12-22 Stas + + * src/dosext/dpmi/dpmi.c: + Try "verr" before giving up on an arbitrary selector usage. + (Bugs #988992, #1358562) + +2005-12-21 Bart + + * doc/EMUfailure.txt, doc/README.txt, src/doc/DANG/DANG.sgml, + src/doc/HOWTO/EMUfailure.sgml: + Regenerated documentation. + + * INSTALL, VERSION, compiletime-settings, + compiletime-settings.devel, configure, configure.ac, + default-configure, etc/dosemu.conf, src/arch/linux/Makefile.main: + bump up VERSION + put some notes about compiling on x86-64 in INSTALL + mention that full CPU emulation is experimental + add dlplugins option to compiletime-settings* + warn if no S-Lang library is available + allow "make install" without plugins (#1308052) + +2005-12-19 Bart + + * src/base/bios/setup.c: + Release DOS helper memory for ctrl-alt-del, to avoid OOM after a few times. + + * src/env/video/vgaemu.c: + TEXT<->GRAPHICS mode changes via register programming needs adjustment + of some other parts in vgaemu_adj_cfg too. Fixes aladdin/gw interaction. + + * src/plugin/kbd_unicode/serv_backend.c: + Fix #1377671 Pause button doesn't work + +2005-12-18 Bart + + * src/: base/bios/int10.c, env/video/video.c: + Initialize video_mode/0:449 in video_mem_setup instead of scr_state_init. + Fixes #1379838 Ctrl-Alt-Del from graphics mode + + * src/: base/misc/dos2linux.c, base/misc/hma.c, dosext/misc/emm.c, + dosext/misc/xms.c, include/dos2linux.h: + Put some basic checks into dos_read/dos_write, and memmove_dos2dos to + avoid touching the protected video memory. + Fixes #1379806 (gw stopped to work under X). + +2005-12-14 Stas + + * src/base/async/int.c: + Remove debugging that causes "dosemu touched the protected memory" + bug with some apps. + +2005-12-13 Bart + + * doc/README.txt, src/emu.c, src/arch/linux/debugger/dis8086.c, + src/base/async/pci_bios.c, src/commands/bootoff.S, + src/commands/booton.S, src/commands/dosdbg.c, + src/commands/exitemu.S, src/commands/lredir.c, + src/doc/README/CDROM, src/dosext/drivers/cdrom.c, + src/dosext/net/net/ipx.c, src/env/video/hercemu.c, + src/env/video/hgc.c, src/env/video/miscemu.c, + src/env/video/remap.c, src/env/video/remap_asm.S, + src/env/video/s3.c, src/env/video/svgalib.c, + src/env/video/vesa.c, src/env/video/vesabios_pm.S, + src/plugin/commands/dosdbg.c, src/plugin/commands/lredir.c, + src/tools/periph/bootsect.S: + From: Hans de Goede + Clarify copyrights. This is necessary for inclusion of DOSEMU into Fedora + Extras, which is what Hans likes to do. + + * src/arch/linux/Makefile.main: + Correct fresh "make install" (bug reported by Victor Warner, linux-msdos) + +2005-12-10 Bart + + * src/: base/async/pci_bios.c, base/dev/misc/pci.c, include/pci.h: + Move the PCI r/w's that are strictly called by int1a (and may be emulated) + to pci_bios.c; made some configuration static. + +2005-12-09 Bart + + * src/: base/async/pci_bios.c, base/dev/misc/pci.c, + emu-i386/ports.c, include/pci.h, include/port.h: + Handle PCIEMU completely within pci.c. + Direct hardware accesses for extended configuration space are fed + directly to the portserver. + Let the port server handle an address and data PCI i/o access as close + to each other as possible. + INT1A pci_bios accesses are handled via port_in/out*. + +2005-12-03 Clarence + + * src/base/dev/misc/timers.c: + Bart said: "This is about port 0x42 which is set to FAST in pit_init(), + timers.c. Port 0x61 was indeed made slow, (because it needs special + treatment in keyb_io_write()) but that's not what this is about." + +2005-12-03 Bart + + * src/base/: dev/misc/pci.c, async/pci_bios.c: + Explicitly clear 0 bits in CF8 dword writes. Revert PCI_EN handling to what + it was (for some reason I thought PCI_EN=1, but it's 0x80000000). + +2005-12-03 Clarence + + * src/include/emu.h: + * make config.features doc realistic + * bump up number of features to 16 to avoid recompile of every + file that #include's emu.h every time a different vendor wants to + add a different hack - Bart suggested in 2003 to use malloc() + but I think that's overkill + + * src/base/init/config.c: + update intrinsic doc on newly added -t and -s options + + * src/base/dev/misc/timers.c: + remove obsolete comments following the removal of PORT_FAST on + SPRK_NATIVE thanks to src/plugin/kbd_unicode/serv_8042.c:r1.7 + + * src/emu-i386/ports.c: + remove useless statement + +2005-12-02 Bart + + * src/: base/async/pci_bios.c, base/dev/misc/pci.c, include/pci.h: + PCI: let word and byte accesses use inw/outw and inb/outb when appropriate, + instead of ind/outd + masking. Suggested by Stas. + +2005-12-02 Stas + + * NEWS: + minor news + + * src/doc/README/Windows: + Rewrote the "Windows" chapter. + +2005-12-01 Bart + + * src/base/async/pci_bios.c: + Enable R/O emulation of PCI-PCI bridges. + + * dosemu.spec.in, dist/dosemu: + Mark directories in the spec file (fixes #1165147). + Don't look at X fonts for dosemu -dumb. + + * src/dosext/mfs/mfs.c: + Check for errno == ESPIPE for lseeks before read or write, + then they should not fail (#1347960) + + * src/: base/init/dev_list.c, base/misc/disks.c, + base/misc/utilities.c, include/emu.h: + Fix ctrl-alt-del + $_emusys (#1310344) + +2005-11-30 Bart + + * src/: base/dev/misc/pci.c, include/pci.h: + Introduce ext_enabled flag to only allow direct h/w access to the extended + VGA PCI config space, and not to the PCI-HOST bridge. + + * src/base/dev/misc/pci.c: + Fix fatal (for Matrox) typo: > 0x40 should be < 0x40. + +2005-11-30 Stas + + * src/env/video/: vga.c, video.c: + Dont register the video memory twice. + + * src/base/async/pci_bios.c: + Skip bogus PCI memory regions + + * src/arch/linux/mapping/mapping.c: + debug messages + + * src/dosext/dpmi/dpmi.c: + Revert the "dpmi.c can use com_printf" thing. It cant - calling + the realmode code from the signal context is unsafe. + + * src/base/misc/dos2linux.c: + call_msdos() must switch to real mode before calling DOS, if needed. + +2005-11-29 Bart + + * src/: base/async/int.c, base/bios/int10.c, base/misc/dos2linux.c, + env/video/video.c, include/emu.h, plugin/commands/unix.c: + Adjust video.c init so that -dumb does not pop up an X window. + Make -dumb quiet until the command is executed if a command is given. So + dosemu -dumb dir + gives a directory listing and nothing else. + + * dist/dosemu, src/arch/linux/Makefile.main: + Change default C: drive from ~/dosemu/freedos to ~/.dosemu/drive_c + (similar to Wine). + Remove dosemu/freedos/ directory prefixes from the files in the symlink + tarballs. The primary location for the DOSEMU utilities is now + $prefix/share/dosemu/commands. + Make the dosemu script aware of "-X by default". + + * NEWS: + Update NEWS file for 1.3.3. This will be the last developer's release, + almost release candidate, before 1.4.0. + + * ChangeLog: + update ChangeLog + + * src/base/bios/int10.c: + Correct VIDEO_BIOS_COMBO for vbios_post=0. Thanks Michael and Reinhard + Karcher for spotting this. + +2005-11-28 Bart + + * etc/dosemu.conf, src/base/init/config.c, + src/base/init/parser.y.in, src/include/emu.h: + Introduce and set $_rawkeyboard = (auto), so the raw keyboard is only + used when either -s or -k are given as options. + + * dist/dosemu, src/base/init/config.c: + Correct information message about root. Let '-c' disable config.vga so it + can work without editing dosemu.conf. Commit forgotten dist/dosemu change. + +2005-11-28 Stas + + * src/: plugin/kbd_unicode/keyboard.c, base/init/init.c, + plugin/kbd_unicode/include/keyboard.h: + Introduce keyb_priv_init() which must be called before port-server + startup. This fixes $_speaker="native" (suggested by Clarence) + +2005-11-27 Bart + + * etc/dosemu.conf, man/dosemu.bin.1.in, src/base/init/config.c, + src/base/misc/priv.c: + Add new switches: + -s to enable direct hardware access + -t to force terminal mode + use X, graphics, and raw keyboard by default if possible. + Set default time mode to "bios". + +2005-11-22 Stas + + * src/emu-i386/ports.c: + Start portserver for the config.pci_video to make the $_pci=(off) + to finally work with G550. + + * src/base/misc/dos2linux.c: + - Map the DOS<-->unix STDOUT/STDERR properly for unix.com. + - Improved the DOS console reading for unix.com + +2005-11-20 Bart + + * src/: base/dev/misc/pci.c, base/async/pci_bios.c, include/pci.h: + Use pciemu (as before) for the first 64 PCI configuration bytes, and direct + access for the last part. + +2005-11-19 Stas + + * src/: base/misc/dos2linux.c, include/dos2linux.h: + Make unix.com "keyboard-aware" (FR #1360156). It works like a tty + in a -icanon mode. + system() replaced with execlp() to make it possible to emulate ^C + with SIGINT. + Also reworked the output-grabbing again - it was still skipping an + output sometimes. + + * src/: base/misc/dos2linux.c, dosext/dpmi/dpmi.c, + include/dos2linux.h, plugin/commands/builtins.c, + plugin/commands/builtins.h, plugin/commands/unix.c: + Make unix.com to use DOS/stdout instead of the direct video mem access, + so that the file redirection to work. + +2005-11-13 Bart + + * src/base/async/pci_bios.c: + Get PCI resource sizes from /proc/bus/pci/devices instead of writing to + the configuration space, if possible. + +2005-11-11 Stas + + * src/: base/bios/bios.S, dosext/dpmi/msdos.c: + Use "call" instead of "int" to invoke the IO helpers. Eliminates + the remaining "lret $2"s. + +2005-11-08 Bart + + * src/: base/async/pci_bios.c, base/dev/misc/pci.c, include/pci.h: + Use /proc config space instead of the header array for pciemu. Allows write + access to the PCI configuration space, necessary for some video BIOSes. + +2005-11-07 Bart + + * src/: base/async/pci_bios.c, base/dev/misc/pci.c, include/pci.h, + env/video/matrox.c: + Stage 1 of using /proc instead of direct hardware I/O for PCI: + use /proc/bus/pci (when available) for pciemu init when $_pci=(off). + +2005-11-02 Stas + + * src/base/misc/dos2linux.c: + Fix run_unix_command (#1345102) + +2005-10-30 Bart + + * src/plugin/sdl/sdl.c: + Add speaker support for SDL within X11. + + * configure, configure.ac, src/base/init/config.c, + src/env/video/video.c, src/include/config.h.in, + src/plugin/kbd_unicode/keymaps.c, src/plugin/sdl/configure, + src/plugin/sdl/configure.ac, + src/plugin/sdl/include/sdl_config.h.in: + Make DOSEMU without run-time plugins actually usable. + +2005-10-29 Bart + + * Makefile.conf.in, configure, configure.ac, src/arch/linux/Makefile.main, + src/include/config.h.in, src/plugin/X/Makefile, src/plugin/X/X.c, + src/plugin/gpm/Makefile, src/plugin/sdl/Makefile, + src/plugin/sdl/Makefile.conf.in, src/plugin/term/Makefile, + src/base/misc/utilities.c: + Add --disable-dlplugins configure option to be able to avoid run-time + plugins. Use configure generated strings instead of -lSDL and -lgpm + directly in the makefiles. + +2005-10-28 Stas + + * src/plugin/: gpm/Makefile, sdl/Makefile, term/Makefile: + Reduce the dependancies for the run-time plugins. + +2005-10-24 Bart + + * src/: emu-i386/cpu.c, include/cpu.h: + Introduce save and load macros to clean up some inline assembly. + + * src/arch/linux/dosext/sound/midid/oss.c: + #include for close() and write(). + +2005-10-23 Stas + + * src/: emu.c, arch/linux/async/signal.c: + Do signal_init() later to avoid problems with SDL. + But we still can't do the threading right... + + * compiletime-settings.devel: + svgalib got updated, looks cool. + +2005-10-20 Stas + + * src/: base/dev/pic/pic.c, include/pic.h, + plugin/kbd_unicode/serv_backend.c: + Hack to mimic the old keyboard interrupt behaveour (PIC recursion). + Closes #1329881. + +2005-10-17 Stas + + * src/plugin/commands/builtins.c: + Add copyrights and myself, my ego is very big these days. :-) + +2005-10-14 Stas + + * src/emu.c: + Closer debugger earlier (than iodev_term() mainly) to reduce the + chance of having an orphaned pipes. + +2005-10-13 Stas + + * src/dosext/dpmi/dpmi.c: + Cleaned int23 handling by adding an extra parameter to quit_dpmi(). + +2005-10-12 Stas + + * src/arch/linux/async/sigsegv.c: + Print something usefull on a vbios fault, and exit. + +2005-10-09 Stas + + * src/emu-i386/ports.c: + Vacate the ports 0x3bc-0x3bf for printer. (closes #1315913) + + * src/: env/video/video.c, base/bios/int10.c, include/video.h: + Move some stuff from video_mem_setup() to scr_state_init() to fix + the $_console=(1) $_graphics=(0) (part of #1315913) + +2005-10-08 Stas + + * src/arch/linux/dosext/sound/midid/oss.c: + Timer is not needed for sequencer. + +2005-10-06 Stas + + * src/arch/linux/dosext/sound/midid/: device.c, io.c, midid.c, + oss.c, timid.c: + Various updates + +2005-10-04 Stas + + * Makefile.conf.in, configure.ac, + src/arch/linux/dosext/sound/midid/Makefile, + src/arch/linux/dosext/sound/midid/gus.c, + src/arch/linux/dosext/sound/midid/oss.c: + New OSS sequencer driver. + Remove obsolete libgus driver. + +2005-10-01 Bart + + * configure, configure.ac, src/include/config.h.in: + Removed some unnecessary configure checks. + +2005-10-01 Stas + + * src/: commands/ems.S, dosext/misc/emm.c: + Adjustments. + + * src/: base/init/memcheck.c, base/misc/disks.c, + base/misc/dos2linux.c, base/misc/fatfs.c, dosext/mfs/mfs.c, + emu-i386/simx86/trees.c, include/cpu-emu.h, include/dos2linux.h, + include/memory.h: + Avoid using LOWMEM for the non-const addresses. + This fixes iplay (again), as it read()s directly to the EMS frame. + Also, moved dos_read/write to dos2linux.c to make them globally + available. And removed the __builtin_constant_p() check in LINEAR2UNIX - + gcc might still be able to optimize the const addresses, but falling + back for dosaddr_to_unixaddr() will happen less frequently. + + * src/: commands/ems.S, include/emm.h, dosext/misc/emm.c, + dosext/misc/emm.h: + - Implemented the version checking for ems.sys. Whoever forgets + to update one, will now get the deserved punishment. + - Since the bug was in the UMB hook, not in the EMS driver, made + ems.sys not to install the UMB hook too, if dosemu refused EMS. + ("EMS 4.0.1" was very funny :-) + + * src/commands/ems.S: + Avoid using "lret $2" - this doesn't restore the IF flag, causing + the program to lock up (noticed with iplay). + Bumped up the version. Some version checking is really needed here. + Important: bios.S have to be inspected for the like things. + +2005-09-30 Stas + + * src/: base/misc/utilities.c, include/utilities.h: + Very simple, type-unsafe (I could really do with C++/STL here!) + primitives for the ring-buffer management. + Do not help much but to keep my sound patches smaller. + + * src/arch/linux/dosext/sound/midid/: midid.c, timid.c: + Update for more non-existant timidity additions. + +2005-09-26 Bart + + * src/env/video/: Makefile, remap.c, remap.h, remap_pent.c: + Move pentium-optimized remapper functions to a seperate file. + + * src/base/bios/setup.c: + Set int 7a correctly for IPX (bug reported by Reinhard Karcher) + + * src/base/bios/bios.S: + Removed blanks in FILL_OPCODE expressions (#1292257, and R. Karcher) + +2005-09-26 Stas + + * src/arch/linux/dosext/sound/midid/timid.c: + cosmetic (never reached) + + * src/arch/linux/dosext/sound/midid/: midid.c, midid.h, timid.c: + Added the standalone mode - midid can now launch timidity itself. + Added the capture mode - midid can now capture and redirect the + sound generated by timidity. + Nothing of the above works with the current timidity, and so the + usage description of the new params is disabled. + Also changed the default timidity timeout to 0 (no timeout). + +2005-09-25 Bart + + * src/: base/bios/bios.S, dosext/misc/xms.c: + There need to be a short jmp and 3 nops in the XMS handler so just broke + up the HLT table a bit. + +2005-09-24 Bart + + * src/: emu.c, base/bios/bios.S, base/bios/hlt.c, + base/bios/setup.c, dosext/misc/xms.c, include/memory.h, + include/xms.h: + Reset the A20 line for ctrl-alt-del. Moved the XMS handler back to f800:4150 + to avoid having to change ems.sys for different dosemu versions. Register + this address as a HLT handler. + + * src/: include/emu.h, base/dev/misc/cmos.c, base/dev/misc/rtc.c, + base/dev/misc/timers.c, base/bios/setup.c, base/init/dev_list.c, + base/init/init.c, base/mouse/mouse.c, dosext/net/net/ipx.c, + dosext/net/net/pktnew.c, env/video/vga.c, env/video/vgaemu.c, + include/iodev.h, include/ipx.h, include/pic.h, include/pktdrvr.h: + Unmap conventional memory until just before boot; VESA ints temporarily + map it when necessary. + BIOS setup calls iodev_reset. Moved iodev_init after priv_drop() and priv. + needing inits out of it. Add disk, printer, ipx, pktdrvr init/reset to + the iodev list. Eliminated hardware_setup() by calling pic_seti in the + various _init functions. + + * src/: base/bios/int10.c, base/bios/setup.c, base/init/init.c, + env/video/vga.c, include/int.h: + Only use video ints and 0x40:0xa8 from /dev/mem for config.vga. + Set all interrupts (incl. all video ints) in bios/setup.c + Repair config.vbios_post=1 + VESA + VESA pre-boot information gathering uses complete /dev/mem information. + + * src/base/bios/setup.c: + Revamp ctrl-alt-del functionality. It quickly reboots dosemu. + + * src/: env/video/video.c, plugin/term/keyb_slang.c: + Allow running dosemu with a redirected stdin, and without a terminal, for + cron jobs etc. Somehow this functionality was present in the old keyboard + code but got lost in the new one. + + * src/: base/async/int.c, emu.c, base/bios/Makefile, + base/bios/bios.S, base/bios/int17.c, base/dev/misc/lpt.c, + base/init/init.c, dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, + dosext/misc/emm.c, dosext/misc/emm.h, dosext/misc/xms.c, + emu-i386/cpu.c, env/video/vc.c, include/bios.h, include/emu.h, + include/int.h, include/lpt.h, include/video.h, include/xms.h, + plugin/kbd_unicode/dosemu_keys.c: + Revamp ctrl-alt-del functionality. It quickly reboots dosemu. + +2005-09-23 Stas + + * src/arch/linux/dosext/sound/midid/oss.c: + Fix gcc4 warning again, but without using "static" this time. + +2005-09-22 Bart + + * src/arch/linux/dosext/sound/midid/oss.c: + Fix gcc-4.0 compilation error. + + * src/: base/async/int.c, base/bios/bios.S, base/init/init.c, + emu-i386/cpu.c, env/video/video.c, include/memory.h, + plugin/commands/builtins.c: + Use a straightforward BIOS POST jumped to from 0xffff:0x0000 as in real + BIOSes instead of the return call stack to get to 0000:7c00. + +2005-09-21 Stas + + * src/arch/linux/dosext/sound/midid/: midid.c, midout.c, null.c, + oss.c, timid.c: + gcc warnings and indentation fixes. + Bumped version number, ha-ha-ha. + + * src/arch/linux/dosext/sound/midid/midid.c: + Ignore the bad data in midi stream, rather than lock-up. + Also some cleanups and gcc warning fixes. + +2005-09-20 Bart + + * src/base/bios/bios.S: + Moved R/W data in the BIOS to 0xd000, and use indirect jumps/calls instead + of patched jumps/calls, so that the 0xe000-0xffff area is read-only. + Added IBM signature at 0xe000. + + * src/dosext/dpmi/dpmi.c: + Implement debug breakpoints in DPMI (RFE: #937984). This works if a forked + child can ptrace attach us, i.e. if GDB is not already active and if + DOSEMU is not suid-root or ran via sudo. + +2005-09-20 Stas + + * src/: emu-i386/cpu.c, emu-i386/do_vm86.c, include/cpu.h, + include/emu.h: + Use the separate FPU context for vm86. This fixes the recent + regressions in CVS (bug #1292250), as well as an old FPU problems + (bug #1275661). + +2005-09-19 Bart + + * src/dosext/dpmi/dpmi.c: + pic_iret() call in dpmi_realmode_hlt() needs to be pic_iret_dpmi() too. + +2005-09-18 Bart + + * etc/dosemu.conf, src/base/async/int.c, src/base/bios/bios.S, + src/base/bios/hlt.c, src/base/dev/pic/pic.c, + src/base/init/memcheck.c, src/emu-i386/do_vm86.c, + src/include/bios.h, src/include/emu.h, src/include/hlt.h, + src/include/int.h, src/include/memory.h: + Activate hlt.c code, and use its registered handlers for ints and PIC. + Move the BIOS parts under 0xf4000 back beyond 0xfc000. + Change default EMS page frame to 0xe4000 so that the new space is used + efficiently, and 16K more UMB space is available. + + * src/: base/async/int.c, emu-i386/do_vm86.c, include/int.h: + Split off do_int_from_hlt() from do_int(). It simplifies some processing + since we don't need to check cs:ip again, and allows simplification of + run_caller_func(). + + * src/: base/dev/pic/pic.c, dosext/dpmi/dpmi.c, emu-i386/do_vm86.c, + include/pic.h: + Split pic_iret() into pic_iret() and pic_iret_dpmi(): the callers know + enough, there's no need to check again. Remove bogus pic_iret() call + for VM86_STI. + +2005-09-18 Stas + + * src/: base/dev/pic/pic.c, dosext/dpmi/dpmi.c, + dosext/sound/sound.c, emu-i386/do_vm86.c, include/pic.h, + include/vm86plus.h: + Moved the PIC-internal variables into pic.c, converted the pic + macros to functions. + + The small code changes: + - pic_pending() now also takes into account the pic_irqs_active hack + - disabled the "bad HACK" in sound.c to make it compile + +2005-09-17 Bart + + * src/arch/linux/async/: signal.c, sigsegv.c: + Re-introduce SA_NODEFER (kernel version dependent). + +2005-09-17 Stas + + * src/: arch/linux/async/signal.c, base/init/init.c: + Removed some redundant pic_seti() usages. + The subsystems that still (ab)use pic_seti() are: sound, rtc, pit and serial. + New sound won't use it, rtc and serial look easy to fix in the future, + not sure about PIT though. + +2005-09-16 Stas + + * src/base/misc/smalloc.c: + free(NULL) is perfectly valid. + + * src/plugin/commands/blaster.c: + New sound code will allow to use the 8bit DMA for the 16bit + sound when the 16bit DMA is broken/unavail. That's the official + feature of SB16, lets make blaster.c aware of it. + +2005-09-14 Stas + + * src/base/misc/smalloc.c: + Cosmetic (== hopefully non-intrusive) updates to smalloc. + + * src/arch/linux/mapping/mapfile.c: + Fixed a very bogus bug with the SHM permissions: 700!=0700 ! + O_CREAT usually gives an RW access regardless, but add some + security patches here, and that will explain a SIGBUS reported + recently in ML. + +2005-09-13 Stas + + * src/dosext/dpmi/dpmi.c: + It is better to keep the orig client number instead of the pointer. + This allows to avoid saving the state of the already terminated client - + may help if we start doing free() on the client struct when it + terminates. + + * dist/dosemu: + beautify + +2005-09-12 Bart + + * src/base/bios/bios.S: + Fix alt-sysrq handling typo in int9. Thanks to Japheth, #1288107 + +2005-09-12 Stas + + * src/dosext/dpmi/dpmi.c: + Cache the pointer to the DPMI_CLIENT struct where in_dpmi can + change, and pass it to return_to_dosemu(). This fixes the watcom + problem and allows some cleanups. The further cleanups can be + to use that pointer always, not only for return_to_dosemu(), but + I think only return_to_dosemu() really cares. + +2005-09-11 Bart + + * src/emu-i386/do_vm86.c: + Remove recursion check for dpmi ints 0x1c,0x23,0x24 that is no longer + necesary since we use hlt in bios.S. + + * src/plugin/kbd_unicode/serv_xlat.c: + put_preceding_dead_keys entered the wrong dead keys and forgot about alt. + Call put_character_symbol recursively to get it right. This fixes + bug #1262962 + +2005-09-09 Clarence + + * src/arch/linux/Makefile.main: + Fix #1224934 - 1.3.2: make install changes permissions of /tmp: + + https://sourceforge.net/tracker/?func=detail&atid=457447&aid=1224934&group_id=49784 + + tar 1.14 (e.g. FC4) enabled the opposite of "--no-overwrite-dir" by default. + + At "make install" time, a symbolic link + "/usr/local/share/dosemu/freedos/tmp" is made to point to "/tmp". On the + second invocation of "make install", dosemu-freedos-bin.tgz is unpacked to + "/usr/local/share" - however it contains a directory "dosemu/freedos/tmp" + with permissions "rwxr-xr-x". + +2005-09-09 Bart + + * src/base/misc/fatfs.c: + Needs errno. + + * src/: dosext/mfs/mfs.c, include/cpu-emu.h: + Fix compilation failure with cpuemu disabled. + +2005-09-07 Bart + + * src/emu-i386/simx86/sigsegv.c: + Fix simx86 vgaemu movs op handling. This fixes space quest's and + jazz jackrabbit's display in simx86. + + * src/emu-i386/simx86/emu86.h: + simx86: Do not stretch time if the EMUtime goes faster than the TSC. + The negative adjustments caused wrong multiplications and random hangs. + Now it looks like the random hangs are gone. There are only consistent + hangs left, all in DPMI it seems. + +2005-09-06 Bart + + * src/plugin/sdl/sdl.c: + Handle resizing in SDL fullscreen toggling. + + * src/plugin/: X/X.c, kbd_unicode/dosemu_keys.c, + kbd_unicode/serv_xlat.c, sdl/sdl.c, + kbd_unicode/include/keyboard.h: + Moved ctrl-alt-p processing to general keyboard code. It saves duplicate + code and now it works on the console too. + +2005-09-05 Bart + + * src/plugin/sdl/sdl.c: + Always grab if fullscreen is set at init. This fixes the mouse in SDL + fbdev mode. + + * src/plugin/sdl/: mouse_SDL.c, sdl.c, sdl.h: + Added support for fullscreen toggling, mouse and keyboard grab (can't + seperate them), some xmode support, and window title support to the + SDL plugin. + + * src/: base/misc/dos2linux.c, include/dos2linux.h, plugin/X/X.c, + plugin/X/X.h: + Moved much of X_change_config to dos2linux.c. Much of it will be shared + with SDL. + + * src/plugin/sdl/sdl.c: + Allow loading the SDL plugin with Video already set to something else. + +2005-09-04 Bart + + * src/: emu-i386/simx86/cpu-emu.c, emu-i386/simx86/protmode.c, + emu-i386/simx86/protmode.h, include/emu-ldt.h, + dosext/dpmi/Makefile, dosext/dpmi/dpmi.c, dosext/dpmi/emu-ldt.c: + Use ldt_buffer for simx86's LDT. Merge the codes that update the cached + LDT. I thought the simx86 version was the nicer of the two. + + * src/dosext/dpmi/dpmi.c: + Added extra check in get_ldt to make sure that the simx86 LDT matches the + init-time LDT (which comes from the real modify_ldt). + + * src/base/misc/hma.c: + simx86: extmem memmove's can also overwrite code + + * src/: base/misc/fatfs.c, dosext/mfs/mfs.c, dosext/misc/xms.c, + emu-i386/simx86/codegen-x86.h, emu-i386/simx86/cpatch.c, + emu-i386/simx86/emu86.h, emu-i386/simx86/memory.c, + emu-i386/simx86/trees.c, include/cpu-emu.h: + simx86: added e_invalidate: invalidates a memory region, to be used after + a memcpy to DOS space which may overwrite code. e_dos_read can use this + function as well, after a plain "read" into aliased space. + Use a few header adjustments to avoid #ifdef's in the main code. + Make check_munprotect e_check_munprotect, a global function that only + unprotects memory that is not low. + +2005-09-04 Clarence + + * INSTALL: + ok, ok, 64-bit is supported now + +2005-09-03 Bart + + * src/arch/linux/async/sigsegv.c: + Localise variables csp and i in dosemu_fault1. This is a workaround for + a GDB bug (fixed Jan 2005, post 6.3) where it bombs out of a + "backtrace full" as soon as variable "csp" is not available. + +2005-09-01 Bart + + * src/: env/video/vc.c, plugin/kbd_unicode/keyb_raw.c: + Raw keyboard by itself does not require VC process control: the kernel + handles raw<->xlate keyboard changes upon mode switching itself. + +2005-08-31 Bart + + * src/base/init/memcheck.c: + Fix gcc warnings. + +2005-08-28 Bart + + * src/emu-i386/simx86/interp.c: + Allow reads from GFX_INDEX in cpuemu. + + * src/base/async/int.c: + Revert 2UNIX/2DOS changes in int.c (similar to msdos.c) + + * src/emu-i386/simx86/cpatch.c: + Use macros and straight-asm for the rep instructions. + + * src/emu-i386/simx86/cpatch.c: + Use straight asm without function overhead. This allows to eliminate _DEBUG + defines. Use macros to reduce near-duplicate code. + + * src/emu-i386/simx86/cpatch.c: + Use wri_8 etc. functions in stub_movsb etc. This reduces assembly and + number of m(un)protect calls further. + + * src/emu-i386/simx86/interp.c: + Fix warnings for HOST_ARCH_SIM + + * src/emu-i386/simx86/: codegen-arch.h, codegen-x86.c, fp87-sim.c: + Fix typo, asm constraints and used/unused attributes. + + * src/emu-i386/simx86/codegen-arch.h: + Re-add. + + * src/emu-i386/simx86/: Makefile, codegen-arch.c, codegen-arch.h, + codegen-sim.c, codegen-sim.h, codegen-x86.c, fp87-arch.c, usesim, + usex86: + Replace symbolic link system for simx86 by conditional compilation. + Fix a dpmi_eflags and case label range issue with codegen-sim. + + * src/emu-i386/simx86/cpatch.c: + Allow gcc -O2 for newer versions of GCC (need __attribute__((used))) + Converted some of the assembly in the stubs to C + Avoid mprotect/munprotect where possible by making use of the aliased + low memory. This significantly improves simx86 performance. + +2005-08-27 Bart + + * src/: arch/linux/async/sigsegv.c, emu-i386/simx86/cpu-emu.c, + emu-i386/simx86/emu86.h, emu-i386/simx86/sigsegv.c, + include/cpu-emu.h: + Call e_emu_fault from dosemu_fault instead of installing a private SIGSEGV + handler. This way simx86 inherits most of the fault handling infrastructure + around dosemu_fault. + + * src/dosext/dpmi/msdos.c: + Revert 2UNIX/2DOS changes to msdos.c. + + * src/: arch/linux/mapping/mapping.c, base/async/int.c, + base/init/memcheck.c, dosext/mfs/mfs.c, include/bios.h, + include/memory.h: + Replace READ_BYTE macros etc by something more intelligent, which + allows us to eliminate lowmem_alias again. Move them to memory.h as + they are not strictly bios related. + +2005-08-23 Stas + + * src/base/async/int.c: + This allows First Encounter game to work with default hogthreshold. + I actually only changed 0->20 in the idle() call, the rest is just a + white-space noice. + +2005-08-22 Bart + + * src/: base/async/int.c, base/serial/ser_init.c, + dosext/dpmi/msdos.c: + Use WRITE_WORD in ser_init.c. Correct MEMCPY_2DOS etc usage in msdos.c/int.c + (if you interprete "DOS" as below 1MB+64k of course which is all it + manages) + + * src/: arch/linux/async/signal.c, arch/linux/async/sigsegv.c, + env/video/instremu.c: + Don't use SA_NODEFER because it doesn't work as documented, at least, for + the time being. Explicitly unblock the faulting signal instead. + Unblock async signals in instremu. + + * src/arch/linux/async/signal.c: + Use the kernel sigaction unconditionally. As it turned out the Wine + technique required another workaround which is so glibc-internal dependent + that it was not worth it. + +2005-08-21 Bart + + * src/: base/async/int.c, base/bios/int16.c, base/dev/pic/pic.c, + dosext/dpmi/dpmi.c, dosext/mfs/mfs.c, + plugin/kbd_unicode/serv_backend.c, include/cpu-emu.h: + Remove E_M(UN)PROT_STACK and all its users because they are no longer + necesarry. Use fake_call/int_to where possible. + + * src/: arch/linux/mapping/mapping.c, include/bios.h, + env/video/text.c: + Introduce lowmem_alias pointer and mappings in mapping.c. This provides + a mirror map of the 0-1MB+64KB area, but not r/w protected. So we can + access everything from dosemu's main code without causing page faults, + or necessary mprotect adjustments. NULL page debugging also becomes a + little cheaper this way. + + * src/base/bios/int10.c: + Fix not yet existing lowmem_alias. + + * src/base/: init/init.c, mouse/mouse.c: + Use READ/WRITE* when accessing DOS memory. + + * src/: include/cpu.h, emu-i386/simx86/cpu-emu.c: + Replace asm push/pop macros by C code, using READ_BYTE etc. + Use READ/WRITE_WORD for IOFF, ISEG, and SETIVEC. + + * src/: env/video/text.c, include/video.h: + Moved ATTR and CHAR macros from video.h to text.c. They access internal + vgaemu RAM, so no need for READ_BYTE etc. + + * src/base/bios/: int10.c, int17.c: + Use macros to access BIOS variables in int10.c. + int17.c does not need MUNPROT emulator stuff, because it's all read only. + +2005-08-21 Stas + + * src/base/mouse/mouse.c: + Don't disable the mouse driver on ax=0x1f. + RBIL says the driver gets really disabled only if you install + the intvector returned by this function, so it looks like otherwise + this function does nothing. + This solves the problem with mcity, for which I previously had + to use dosbox:) + +2005-08-18 Bart + + * etc/global.conf, src/base/bios/Makefile, src/base/bios/int17.c, + src/base/dev/misc/lpt.c, src/include/lpt.h: + Rewrote int17 (thanks to Bochs) to interface to the parallel port hardware, + so that it works with emulated ports and real i/o ports. + Implemented basic emulated parallel port hardware that deals with the + printer. $_printer_commands should be allowed from .dosemurc for suid-root. + This implements RFE #830601. + +2005-08-18 Stas + + * src/base/async/int.c: + My midnight-accumulation code works better than this. + +2005-08-17 Stas + + * src/include/timers.h: + Compile fix + +2005-08-17 Bart + + * src/base/misc/disks.c: + Added very basic floppy i/o port motor control handling, just to turn it + off for now. + + * src/base/: async/int.c, bios/bios.S: + Let INT 8 increment, instead of just set, the midnight flag when the time + is right. This helps when INT 1A isn't called for longer periods of time, + and is consistent with some other BIOSes. + Use READ_DWORD etc. in int.c; small optimization; flag floppy timeout to + controller (note that the floppy controller port 0x3f2 isn't emulated yet). + + * src/plugin/sdl/keyb_SDL.c: + Handle all SDL keysyms including keypad, prtscr etc. Fixes #1252147 + + * src/base/bios/int10.c: + Set correct page size for int10/ah=5 in graphics modes. Fixes #1255178 + +2005-08-16 Bart + + * src/: base/bios/bios.S, emu-i386/do_vm86.c: + Replace "int xx" in bios.S trampoline by hlt. Minimal change: further + cleanups are possible. + +2005-08-15 Clarence + + * dist/dosemu, src/base/init/config.c: + Wrapper script: don't pass "dosemu.bin" as argv[0], pass the wrapper scr + ipt name instead. + + config.c: Look for --version/--help arg anywhere in the argv[] list + (so as to not complain on "xdosemu --help" which prepends a -X to argv[]). + Don't print out "xdosemu = dosemu -X" if already using xdos/xdosemu. + +2005-08-14 Stas + + * src/include/dosemu_debug.h: + Bump up the gcc version needed for attribute format. + +2005-08-14 Bart + + * src/dosext/mfs/: mangle.c, mangle.h: + Use new min/max macros in mangle.c. + +2005-08-13 Bart + + * src/emu-i386/simx86/interp.c: + Correct case labels for 0xb0, 0xb1, should not be -1 (CMPXCHG). + +2005-08-11 Stas + + * src/: dosext/dpmi/dpmi.c, emu-i386/do_vm86.c: + Remove the ancient pic_resched hacks. + + * src/dosext/dpmi/dpmi.c: + Fixes on a failure path (cosmetic) + +2005-08-06 Stas + + * src/dosext/dpmi/dpmi.c: + Initialize the DPMI FPU context to something sensible. + Also poll the DMA more frequently. + Be more carefull about the FPU context copying. + Do FPU context initialization in indirect DPMI switch. + +2005-08-05 Stas + + * src/: include/smalloc.h, include/utilities.h, + base/misc/smalloc.c: + Use the compatibility FORMAT macro in a few places. + +2005-08-05 Bart + + * src/: arch/linux/mapping/mapfile.c, include/smalloc.h: + Compatibility with older gcc versions. + +2005-08-03 Bart + + * src/env/video/instremu.c: + Made instremu assembly macros compatible with earlier gcc versions. + +2005-08-03 Stas + + * src/plugin/kbd_unicode/serv_backend.c: + Got tired of the ocasional keypress duplications and increased + the keyboard char delay to 800uS. + Actually, the duplicate keypresses have not disappeared in BC IDE + at all. Set the latency to 2000uS - now they seem to really have + gone. + + * src/: emu-i386/cpu.c, dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, + dosext/dpmi/msdos.c, include/cpu.h, include/emu.h: + Implemented FPU state switching. Needed for my new sound code + and is a long-overdue anyway. + +2005-07-29 Stas + + * src/doc/HOWTO/EMUfailure.sgml: + Clarify a comment about x-com game. + +2005-07-26 Bart + + * src/base/async/int.c: + Use new min macro. + + * src/base/dev/dma/dma.c: + Fix warnings. + + * src/: dosext/sound/sound.c, base/dev/dma/dma.c, include/dma.h, + include/sound.h: + Use new min/max macros in sound code, adjust some types to match. + + * src/: include/utilities.h, plugin/X/X.c: + Use the same type-safe min/max macros as the Linux kernel uses and use + them in X.c. + +2005-07-25 Bart + + * src/env/video/remap.c: + Added basic 15->32 and 16->32 remapper functions. Based on a patch by + Mike Nelson, SF #1244195 + +2005-07-24 Bart + + * src/base/init/parser.y.in: + No need (and wrong) to free Path_cdrom[which]. + + * src/: emu.c, emu-i386/simx86/cpu-emu.c: + Allow cpuemu when $_rdtsc=(off). It uses RDTSC nevertheless (so should + check for 586), but now with the timer code just using gettimeofday(). + + * src/: arch/linux/async/signal.c, emu-i386/cputime.c, + include/timers.h, base/init/init.c, dosext/dpmi/memory.c, + emu-i386/simx86/emu86.h, dosext/drivers/aspi.c: + Replaced HZ by the more modern sysconf(_SC_CLK_TCK). + The sigalarm handler now updates the time system counter base, so our + TSC timer is a hybrid just like the one in the Linux kernel. This avoids + DOS time slow downs because of inactive idle cpu's etc. Correct backtracing + in emu86.h. + +2005-07-24 Stas + + * etc/dosemu.conf, etc/global.conf, src/base/async/int.c, + src/base/dev/misc/rtc.c, src/include/iodev.h: + Applied (by popular demand) the new timemode patch from Andrew Brooks. + This should have a positive effect of having the cmos values in + sync with the time in any mode. And it introduces the "linux" + time source. + New option is added to dosemu.conf. + +2005-07-23 Bart + + * Makefile.conf.in, compiletime-settings, configure, configure.ac, + src/emu.c, src/include/cpu.h, src/plugin/sdl/Makefile, + src/plugin/sdl/Makefile.conf.in, src/plugin/translate/Makefile: + Add support for compilation on x86-64. + + * src/env/video/instremu.c: + We must use "q" and not "r" in byte-operating asm instructions to avoid + GCC telling us "Error: bad register name `%sil". + + * src/dosext/dpmi/: dpmi.c, dpmi.h: + Moved ds, es, fs, and gs check selector code into a function. + Allow 1, 2, and 3 as null selectors. They sometimes seem to occur on + x86-64. Simplified ValidSelector(): in practice it only needs to check for + the LDT bit. + + * src/base/mouse/mouse.c: + We should only erase a text mouse cursor if it's native and drawn, not + unconditionally if it's non-native. + + * src/arch/linux/async/signal.c: + Removed unused variable kernel_sa. + + * src/emu-i386/simx86/codegen-arch.c: + Actually enable the hack to get bgidemo (and doom) working. + + * src/include/cpu.h: + Forgotten sys/vm86.h change. + + * src/: emu-i386/simx86/cpu-emu.c, emu-i386/simx86/sigsegv.c, + env/video/vgaemu.c: + Make simx86 more functional with DPMI code (both native and emulated). + Adjusted bogus ((int)_cr2 < 0) test. Signal handlers use the signal stack + and perhaps queue, and restore eflags, fs and gs. Revert the meanings of + config.cpuemu==2 and 3. The fault handler takes better care of VGAEMU. + +2005-07-22 Bart + + * src/emu-i386/simx86/sigsegv.c, etc/dosemu.conf, etc/global.conf, + src/dosext/dpmi/dpmi.c, src/base/init/lexer.l.in, + src/base/init/parser.y.in, src/include/cpu-emu.h: + Introduced new $_cpu_emu option to set to either "off", "vm86" or "full". + config.cpuemu==4 now means full emulation, 3 means vm86 only. + Adjusted DPMI to reflect this. CPUEMU's fault handler temporarily leaves + the vm86-only emulator after a page fault if DPMI is active. + Disabled non-emulated boot and vbios for now. + + * src/: include/vm86plus.h, include/Asm/vm86.h, base/dev/pic/pic.c, + base/misc/ioctl.c, dosext/net/net/ipx.c, emu-i386/do_vm86.c, + emu.c: + Include Asm/vm86.h. Removed #include because this file is not + available on AMD64. + +2005-07-22 Stas + + * src/dosext/dpmi/dpmi.c: + Zero out FS/GS upon raw mode switches (bug #1242171) + Also zero out the "undefined" registers. + +2005-07-22 Clarence + + * src/: base/init/config.c, include/emu.h: + As posted to dosemu-devel put without the usage() change: + + Config documentation cleanup and a little bit of getopt() fixing. + + Complete list of changes (it's smaller than it looks): + - Remove unused, probably out-of-sync "struct debug_flags d" + - Pass the same string to the 2 getopt calls and in sync with reality + (sometimes we were missing ":" or options entirely) + - We need "--help" in addition to "--version" (I finally found out that + "--version" was the correct way to bring up the help, rather than triggering + a syntax error with "-h" :)) + - Remove unused "sizes" variable, group together code for handling obsolete + options + - Print the bad character (not '?') when user types in incorrect option + - usage(): + # Remove "[-ABckballtheseareeitherwrongoroutofdateanyway]" etc. from the + description + # Remove non-existent options, obsolete options + # Document secret until now options like -S (so Bart, your SDL secret is out + :)) + # In other words, document all the options we have and be consistent with + the getopt string + # Move "%s --version" lower since it doesn't accept options + # Indent "%s --version" and "xdosemu [options]" for consistency with primary + command + +2005-07-21 Bart + + * src/base/misc/fatfs.c: + Use e_dos_read() to load the FreeDOS kernel when cpuemu is active. + Allows running dosemu without using vm86 at all, useful for x86-64. + + * src/emu-i386/simx86/: codegen-arch.c, cpu-emu.c, interp.c: + The correct expression for the virtual flags in DPMI is + get_vFLAGS(TheCPU.eflags) + which gets the VIF from the vm86s structure. + + * src/emu-i386/simx86/: codegen-arch.c, interp.c, sigsegv.c: + simx86: Corrected vga_read/write calls, and VGAEMU port list. Removed + is_cli stuff, as a blacklist is not needed here. Set up a hack for + genbufsize. Enough for bgidemo to work. + +2005-07-21 Stas + + * src/base/: init/parser.y.in, serial/ser_init.c: + Init sptr->dev to NULL as parser now does free() and crashes. + Have to check on the other similar bugs. + +2005-07-20 Bart + + * compiletime-settings, compiletime-settings.devel, + default-configure: + Remove linkstatic in compiletime-settings.*. Re-enable cpuemu in .devel. + + * src/emu-i386/simx86/: codegen-arch.c, cpu-emu.c, interp.c: + Replace dpmi_eflags with _EFLAGS in simx86. Not quite sure how correct this + is. Copy the LDT table that dpmi_setup() put up into the simx86 LDT. + With these changes simx86 works again for DPMI (not perfectly but...). + + * src/emu-i386/simx86/sigsegv.c: + s/Logical_VGA/vga/ + + * src/emu-i386/simx86/protmode.c: + Fix a bug in simx86's LAR handling. This was *the* major bug in simx86. + +2005-07-20 Clarence + + * src/: arch/linux/debugger/mhpdbgc.c, emu-i386/ports.c, + env/video/matrox.c: + Many people don't know how to use fgets() and waste 1 byte :) + Avoid using hardcoded constants without changing someone else's style too much. + +2005-07-20 Bart + + * src/: env/video/vgaemu.c, env/video/video.c, base/bios/int10.c: + Do the fake call to the int10 post/video set a bit later, and set a few + more variables in vga_emu_init(). This gets the Bochs vgabios back to work. + +2005-07-20 Stas + + * src/plugin/kbd_unicode/serv_backend.c: + Emulate the bits 0 and 1 of the keyboard_flags_3 (0x40:0x96) + (fixes #1230579) + Respect the state of the keyboard_flags_3 instead of relying on + the one remembered by dosemu (second part of #1230579) + +2005-07-20 Bart + + * src/dosext/mfs/: lfn.c, mfs.c, mfs.h: + LFN MFS fixes: basic volume label for LFN findfirst, give "access denied" + when trying to remove a non empty dir. Fixes VC move problem, #1238148; + and cleanups: header files, replace MAXPATHLEN with PATH_MAX. + +2005-07-19 Bart + + * src/env/video/: crtcemu.c, vgaemu.c: + Modify vga_emu_adj_config to adjust VESA modes that are within bounds, + in general < 2048x1024x8bpp. Setup the CRT mode init that way as well. + + * src/env/video/seqemu.c: + Set vga.seq.data[4] to the correct value for VESA modes. + +2005-07-18 Bart + + * src/base/mouse/mouse.c: + Let mouse_move_absolute only cause a callback to (e.g.) perhaps warp the + X cursor if the mouse was clipped. + + * src/base/bios/int10.c: + Unconditionally set the bios cursor shape variable, even if set to + invisible. This fixes a problem with DN. + + * src/env/video/vga.c: + Don't check for the int10 location if $_vbios_post=(1). + +2005-07-17 Bart + + * src/env/video/: crtcemu.c, vgaemu.c: + Adjust some crtc registers in 8bpp vesa modes, and recognize them in + the adjust config routine. Perhaps that allows a windows vesa driver to + work. + + * src/base/: mouse/mouse.c, mouse/mousevid.c, bios/bios.S: + Fix missing 0x typo in mousevid.c; check if the vesa mode was really set. + The mode is now passed on the stack instead of in cx. + + * src/env/video/: text.c, vgaemu.c: + Set video to off initially so that the screen won't get updated before the + initial mode set. Also don't divide by zero in check_cursor_location. + Fixes #1239421 + +2005-07-17 Stas + + * src/plugin/X/X.c: + Properly track FocusIn/FocusOut X events (further fixing #920216) + + * src/: include/mouse.h, base/mouse/mouse.c: + Replace mouse.rx and mouse.ry with a macros to reduce the + amount of the variables we should keep in sync. + + * src/base/mouse/mouse.c, src/plugin/X/X.c: + Move mouse pointer no matter whether it is visible or not + (part of #920216) + Forgotten piece of the partial #920216 fix. + +2005-07-16 Bart + + * src/: env/video/console.c, env/video/vgaemu.c, include/mouse.h, + base/bios/bios.S, base/mouse/gcursor.c, base/mouse/mouse.c, + base/mouse/mousevid.c, base/mouse/mousevid.h: + fix #1206135 by passing bx from vesa mode sets to the mouse mode set + adjustments. This works for vgaemu and all fixed console vesa modes. + However, don't draw any mouse cursor: gcursor.c doesn't know about + LFBs, >8 bpp and bank switching. + + * src/base/init/parser.y.in: + Removed a free() that was a tad too aggressive. + +2005-07-15 Bart + + * src/: base/init/parser.y.in, + plugin/translate/config/plugin_parser, base/dev/misc/joystick.c, + base/dev/misc/lpt.c, plugin/term/mouse_xterm.c: + Plugged some memory leaks in the parser (thanks to valgrind). Simplify + mouse_xterm detection (which was also a leak). + + * src/: base/misc/priv.c, base/misc/utilities.c, + arch/linux/async/debug.c, include/dosemu_config.h, + arch/linux/debugger/mhpdbgc.c, base/init/config.c: + Reintroduced dosemu_proc_self_exe, as it comes in really handy for dosdebug. + Fall back to dosemu_argv[0] for older valgrinds and qemu if it's NULL. + Plugged a small memory leak. + + * src/arch/linux/async/signal.c: + Use RTLD_NEXT to find glibc's default (non-pthread) sigaction. Saves a + bit of effort on our part. This technique is borrowed from Wine: if they + can do it, we can do it too. + + * src/: env/video/vesa.c, base/bios/int10.c, env/video/console.c, + env/video/hgc.c, env/video/vgaemu.c, env/video/video.c, + include/video.h: + Moved BIOS variable init from video.c to int10.c + Install a small real VGA BIOS for all vgaemu clients, that just contains + the static functionality table, no fonts. + +2005-07-15 Stas + + * src/: arch/linux/async/debug.c, arch/linux/debugger/mhpdbgc.c, + base/init/config.c, include/dosemu_config.h, base/misc/priv.c, + base/misc/utilities.c: + Remove dosemu_proc_self_exe. It doesn't work under qemu and + valgrind, it can have problems with privs, and overall it + looks useless. + +2005-07-14 Bart + + * src/: base/misc/utilities.c, emu-i386/cpu.c, emu-i386/ports.c: + Use fscanf directly instead of fgets and then sscanf. Avoids some + arbitrarily sized buffers. + +2005-07-13 Bart + + * src/base/bios/int10.c: + Moved adjust_font_size into a function of its own instead of fiddling with + the high word of mode in set_video_mode. Implement changing scanlines after + mode sets so that "mode co80,43" really gives you 43 lines in xdosemu. + +2005-07-13 Stas + + * etc/dosemu.conf, src/arch/linux/mapping/mapfile.c, + src/arch/linux/mapping/mapping.c, + src/arch/linux/mapping/mapshm.c, src/include/mapping.h: + - Fixed $_mapping="mapfile" by providing the separate ops for + "mapfile" and "mapshm", otherwise one can't select. + - Added "mapashm" option for the anon-shm mapping. + - "mapshm" is now a default. + + * src/base/misc/smalloc.c: + Don't call any smalloc functions on error path, as they may assert. + + * src/base/init/parser.y.in: + cosmetic + +2005-07-12 Stas + + * etc/dosemu.conf: + typo. + + * src/dosext/dpmi/: dpmi.c, msdos.c: + Small fixes. + +2005-07-12 Bart + + * src/: base/mouse/mouse.c, include/mouse.h, plugin/X/X.c: + Made some mouse functions static, removed some unused mouse code. + Re-enabled Mouse->set_cursor call in mouse_do_cur to be able to warp the + X cursor. However, be more conservative and safer in warping than before: + - don't warp if snap_X is active or if the mouse cursor is off. + - ignore any events caused by pointer warps; they often triggered infinite + actions because the event could be different from what we warped to. + In non grab mode this fixes some issues with SC2000 (dual mouse cursor), + and VPA (vga planets assistant, clicking didn't move the cursor). + + * src/: base/bios/int10.c, include/memory.h: + Program display start via crtc for mode sets in terminal mode; fix TEXT_SIZE + This fixes some new issues with programs that use multiple pages in terminal + mode. + + * src/: base/init/dev_list.c, plugin/term/mouse_xterm.c: + Initialize video before mouse and don't use the native (text) cursor + for the xterm mouse. + +2005-07-11 Bart + + * src/plugin/: X/X.c, sdl/sdl.c: + Revert X and SDL to use video_mode global variable for now. Necessary for + VESA modes, in particular with z80s (spectrum emulator). + + * src/plugin/X/X.c: + Correct MIT-SHM X error handling. It didn't work over remote X via ssh. + Call leavedos from the X error handler, otherwise it leaves DOSEMU + uncleanly. + + * src/base/bios/int10.c: + We can't change the number of screen rows in terminal mode when adjusting + the font size. + +2005-07-10 Bart + + * src/: dosext/dpmi/msdos.c, env/video/instremu.c, + include/vgaemu.h: + From Stas: implements decode_modify_segreg_insn (instruction decoder + for modified segment registers in msdos.c) in instremu. + + * src/: base/bios/int10.c, env/video/video.c, include/video.h: + Eliminated video_subsys, *height and text_scanlines global variables. + set_cursor_shape now takes care of setting the BIOS variable as well. + Cleaned up int10/ah=f to work like a real video bios does. + + * src/: emu.c, base/bios/bios.S, base/init/dev_list.c, + base/init/init.c, env/video/vesa.c, env/video/vga.c, + env/video/video.c, include/video.h, plugin/X/X.c, + plugin/sdl/sdl.c: + Moved video priv_init/init/close to iodev list; the init no longer needs to + map any /dev/mem, it all comes from the register_hardware_ram list. + Always boot via setting a video mode, or POSTing, not just on the console; + use 0:449 for the mode. Remove most other mode setters. + Use fake_call_to() where possible. + Merged init_vga_card() with vga_post_init(). + Eliminated font_height global variable. + + * src/base/init/config.c, src/include/emu.h, src/plugin/translate/translate.c, + etc/global.conf, src/base/init/config.c, + src/base/init/lexer.l.in, src/base/init/parser.y.in, + src/include/video.h, src/plugin/translate/translate_config.c: + Removed old charset configuration. + + * etc/dosemu.conf, etc/global.conf, src/base/dev/misc/lpt.c, + src/base/init/lexer.l.in, src/base/init/parser.y.in, + src/include/lpt.h: + (mostly from Stas) More flexible printer configuration. + +2005-07-10 Stas + + * src/env/video/render.c: + People complain about this leavedos. + + * src/base/misc/utilities.c: + This message, always being in the log, makes people nervous. + + * src/plugin/commands/unix.c: + Even a better heuristic can be done if we check for '/' to be + before ' '. + + * src/plugin/commands/unix.c: + Use strchr() for '/' to destinguish the dos/linux path. + Not convinced that it is a bad solution, and it is better + than now in any case. + +2005-07-09 Stas + + * src/arch/linux/mapping/mapping.c: + realloc() from NULL should malloc(), and also added some + sanity checks. They may probably trigger some false-positives, + but I'd like too see them. + + * src/dosext/misc/emm.c: + Don't pass zero-sized EMS allocations to the memory management + subsystems. smalloc is really against the zero-sized allocations + (deliberate decision, won't change), but the EMS specs explicitly + allows the zero-sized allocations :( + Fixes the problems with Windows 3.0 + +2005-07-08 Clarence + + * src/: base/dev/misc/joystick.c, emu-i386/ports.c: + + Rearrange joystick global init order to match def order somewhat. + Initialise joy_port_[xy] to -1 so that if the port handlers are ever reinstated + for when there is no joystick and the first joystick port access is an inb, + the axis bits will be set high like the buttons (the byte returned will be 0xFF). + + Update comments to emphasise Stas' r1.4 change (don't register joy port handlers + if no joy). + + * etc/dosemu.conf: + spel + +2005-07-08 Bart + + * src/: emu-i386/ports.c, env/video/video.c: + Replace ioperm by set_ioperm in ports.c; otherwise almost all ports are + slow using sudo/suid. No wonder why console graphics were so slow. + Also disable the native speaker when not on the console or you'd get all + kinds of strange effects when running remotely. + + * src/: base/async/int.c, base/bios/int10.c, base/mouse/mouse.c, + env/video/dualmon.c, env/video/vgaemu.c, include/memory.h, + include/vgaemu.h, include/video.h: + Get rid of SCREEN_ADR(), use screen_adr() instead, which uses only BIOS + variables. Use vga_write*, vga_read*, etc. functions to access VGA memory, + and adjust those a bit. Cleaned up set_video_mode, but just slightly. + + * src/: base/bios/int10.c, emu-i386/ports.c, env/video/console.c, + env/video/crtcemu.c, env/video/hgc.c, env/video/text.c, + include/vgaemu.h, include/video.h, plugin/term/terminal.c: + Eliminated global variables cursor_shape, cursor_blink and char_blink. + The BIOS now sets these in vga.* via emulated hardware instead. + + * src/: base/async/int.c, base/bios/int10.c, env/video/console.c, + env/video/vc.c, env/video/vga.c, include/vc.h: + Make set_vc_screen_page emulated-hardware instead of BIOS dependent. + + * src/emu-i386/ports.c: + The port server should ignore SIGINT. + + * src/: base/bios/int10.c, env/video/console.c, + env/video/crtcemu.c, env/video/dualmon.c, env/video/hgc.c, + env/video/text.c, env/video/vgaemu.c, env/video/video.c, + include/video.h, plugin/term/terminal.c: + Eliminated global variables screen_adr, cursor_row, and cursor_col. + +2005-07-07 Bart + + * src/: emu-i386/ports.c, env/video/console.c, env/video/vgaemu.c, + env/video/video.c: + Use a very basic vgaemu for (config.console_video && !config.vga) + + * src/: base/bios/int10.c, env/video/vgaemu.c, env/video/video.c, + include/video.h: + Started merging set_video_mode() and X_set_video_mode() + clear_screen() now clears the whole screen (all pages) + video.c calls set_video_mode(video_mode) instead of clear_screen; this + obsoletes a similar call in vgaemu.c. + bios_scroll now takes the BIOS screen offset into account. + + * src/: emu.c, base/misc/ioctl.c, emu-i386/do_vm86.c, + emu-i386/ports.c, env/video/dualmon.c, env/video/hgc.c, + env/video/video.c, include/hgc.h: + Eliminated hgc.h, made functions in hgc.c static, and some dualmon fixes. + +2005-07-05 Clarence + + * dist/dosemu: + "dosemu -install" fixes: + 1. Allow spaces in private dosemu directory name + 2. actually create a correct "~/.dosemu/drives/c" symlink (needs to be an absolute path) + + Bart, the patch is by no means comprehensive - you should audit the entire file for space + problems e.g. suppose $HOME had a space in it. + + > Support Requests item #1039909, was opened at 2004-10-04 + > https://sourceforge.net/tracker/?func=detail&atid=457448&aid=1039909&group_id=49784 + + * src/base/dev/misc/timers.c: + +debug + + * src/base/init/config.c: + fix crash with -h0 and "auto" keytable; IIRC, we have lots of problems with + dump_config_status() printing before vars have been initialised + + * src/: base/async/dyndeb.c, plugin/X/X.c: + Unbreak -DX when not invoking executable as "xdos" + "Unknown debug-msg mask: X" + + It is bogus putting register_debug_class later than config_init() + as usage() won't show -DX - we're not going to reuse letters anyway + (we have lots of spare letters and it would be confusing otherwise). + + I would say the same about "./src/plugin/translate/translate.c: + register_debug_class('u', 0, "Unicode translation");" + but unfortunately but it's called earlier than even dyndeb.c:init() + so not sure if safe to move into there as well. + + * src/base/async/dyndeb.c: + -double break + + * src/base/mouse/mouse.c: + stop crash on early leavedos of 'dosemu -U ' + + * dist/mkbindist: + spel + + * INSTALL: + too many people ask about x86/64 + +2005-07-05 Bart + + * src/: base/mouse/mouse.c, include/mouse.h, plugin/X/X.c, + plugin/X/X.h, plugin/kbd_unicode/include/keyb_clients.h, + plugin/sdl/sdl.h, plugin/term/env_term.h: + Fix gcc-4.0 build; make some more client structures static. + +2005-07-05 Stas + + * src/arch/linux/mapping/mapping.c: + Fix SIGSEGV when $_mapping set to a wrong value, make dosemu to + promptly terminate instead. + + * src/dosext/dpmi/msdos.c: + cosmetic + +2005-07-04 Bart + + * src/: base/async/int.c, base/bios/int10.c, base/mouse/mouse.c, + env/video/vgaemu.c, env/video/video.c, include/memory.h, + include/vgaemu.h, include/video.h: + Cleaned up int10 cursor positioning code, eliminated some global variables, + and use BIOS variables more often. + + * src/: emu-i386/ports.c, env/video/vc.c: + Re-enabled VID_IO and SPECIAL_IO stuff. It's in the wrong place, but + necessary for console mode without direct port access. Graphical mouse + cursors still work. + + * src/env/video/crtcemu.c: + Reverted MODE_CONTROL CRT init change for now. + +2005-07-03 Bart + + * src/plugin/X/X_keymaps.c: + Add forgotten file X_keymaps.c + + * src/: base/bios/int10.c, env/video/vgaemu.c: + Fix two bugs in terminal mode: int10/ah=0 should set the video memory start + address and we need to force a mode even if vga.text_width > 80. + Fixes #1231686 + + * configure, configure.ac, src/base/misc/utilities.c, + src/base/mouse/Makefile, src/base/mouse/mouse.c, + src/base/mouse/mouse_gpm.c, src/base/serial/ser_init.c, + src/include/config.h.in, src/plugin/gpm/Makefile, + src/plugin/gpm/mouse_gpm.c, src/plugin/term/mouse_xterm.c, + src/plugin/term/term_core.c: + Moved GPM mouse support to a shared-object plugin of its own. + Now the base dosemu.bin only depends on GLIBC libraries. + Added logic in mouse_gpm.c to deal with different binary-incompatible + versions of GPM. + + * Makefile.conf.in, configure, configure.ac, + src/base/misc/utilities.c, src/base/mouse/mouse.c, + src/env/video/dualmon.c, src/env/video/video.c, + src/include/config.h.in, src/include/mouse.h, + src/include/video.h, src/plugin/X/X.c, + src/plugin/kbd_unicode/dosemu_keys.c, + src/plugin/kbd_unicode/keyb_clients.c, + src/plugin/kbd_unicode/keyb_none.c, + src/plugin/kbd_unicode/include/keyb_clients.h, + src/plugin/sdl/Makefile, src/plugin/sdl/sdl.c, + src/plugin/term/Makefile, src/plugin/term/keyb_slang.c, + src/plugin/term/mouse_xterm.c, src/plugin/term/term_core.c: + Convert terminal plugin into a shared object. + Introduce a system to register mouse and keyboard clients instead of + direct assignments -- they now form a linked list instead of an array. + +2005-07-02 Stas + + * src/doc/HOWTO/EMUfailure.sgml: + From Ryan Underwood: + PIT acronym is expanded wrong in documentation. + + * src/arch/linux/dosext/sound/midid/Makefile: + Patch from Ryan Underwood: + Make midid obey make configuration if it is being built in-tree. + +2005-07-02 Bart + + * Makefile.conf.in, configure, configure.ac, + src/base/init/config.c, src/base/misc/utilities.c, + src/include/config.h.in, src/include/utilities.h, + src/plugin/X/Makefile, src/plugin/X/X.c, src/plugin/X/keyb_X.h, + src/plugin/kbd_unicode/keymaps.c: + Convert the X plugin to a shared object. + + * src/: dosext/dpmi/dpmi.c, emu-i386/do_vm86.c, + emu-i386/simx86/codegen-arch.c, emu-i386/simx86/codegen-sim.c, + emu-i386/simx86/codegen-x86.c: + Remove X_GRAPHICS #if's. + + * src/plugin/sdl/: keyb_SDL.c, mouse_SDL.c, sdl.c: + Use instead of in #include. Part of SR #1230661 + + * compiletime-settings, compiletime-settings.devel, src/emu.c, + src/base/async/dyndeb.c, src/base/init/config.c, + src/base/mouse/mouse.c, src/base/speaker/Makefile, + src/base/speaker/X_speaker.c, src/env/video/Makefile, + src/env/video/X.c, src/env/video/X.h, src/env/video/screen.c, + src/env/video/screen.h, src/plugin/X/Makefile, src/plugin/X/X.c, + src/plugin/X/X.h, src/plugin/X/X_keysyms.c, + src/plugin/X/X_speaker.c, src/plugin/X/keyb_X.c, + src/plugin/X/keyb_X.h, src/plugin/X/keyb_X_keycode.c, + src/plugin/X/screen.c, src/plugin/X/screen.h, + src/plugin/kbd_unicode/Makefile, src/plugin/kbd_unicode/keyb_X.c, + src/plugin/kbd_unicode/keyb_X.h, + src/plugin/kbd_unicode/keyb_X_keycode.c, + src/plugin/kbd_unicode/keyb_clients.c, + src/plugin/kbd_unicode/keymaps.c, + src/plugin/translate/charsets/X_keysyms.c: + Converted the X files into a plugin. + + * Makefile.conf.in, compiletime-settings, + compiletime-settings.devel, default-configure, + src/arch/linux/Makefile.main, src/base/init/config.c, + src/base/misc/utilities.c, src/base/mouse/mouse.c, + src/include/utilities.h, src/plugin/kbd_unicode/keyb_clients.c, + src/plugin/sdl/Makefile, src/plugin/sdl/Makefile.conf.in, + src/plugin/sdl/configure, src/plugin/sdl/configure.ac, + src/plugin/sdl/sdl.c, src/plugin/sdl/include/sdl_config.h.in: + Convert the SDL plugin into a real dynamically loaded plugin. + This allows binary distributions to not depend on libsdl; you only need it + if you specify -S. + + * src/env/video/crtcemu.c: + Need to adjust CFG_MODE_CONTROL too, in CRTC_init. + + * src/base/bios/int10.c: + Removed int10_old, and merged some differing pieces with int10_new. + +2005-07-01 Stas + + * etc/global.conf: + Add $_vbios_file to global.conf. + + * src/dosext/dpmi/: dmemory.h, dpmi.c, dpmi.h, memory.c, msdos.c: + Hopefully the last "huge cleanups" part. + - Completed the msdos.c separation by not accessing any of the + dpmi.c variables directly, use the DPMI API instead. + - And there seem to be the off-by-one bugs that the segment + limits were used as a length without +1, fixed. + - Cleaned up the memory.c API again. + With this state of affairs I was able to resync my DJGPP port + of msdos.c rather quickly, so I think the code is now "clean enough". + +2005-06-30 Stas + + * src/dosext/dpmi/dpmi.c: + Cleaned up the hack a little. + + * src/: base/async/int.c, dosext/dpmi/dpmi.c, include/int.h: + First pass on introducing the Windows Hack (tm) that will allow + to run the DPMI apps in a Windows Standard mode DOS prompt (hence + eliminating the need for the DOS prompt support in an Enhanced mode). + Now running the DPMI in DOS prompt apps is possible, but an attempt + to switch back to Windows before the DPMI app terminates, will crash. + + * src/dosext/dpmi/: dmemory.h, dpmi.c, dpmi.h, memory.c, msdos.c, + msdos.h: + Next round of the "huge cleanups": + - Separated memory.c from dpmi.c by passing the root of the memory + list as a parameter to all the memory.c functions. + - The DPMI state structure can now be static. + - Made msdos.c to not use memory.c directly - it now tracks the + memory allocations itself, as it should under real DOS (backported + the appropreate code from my DJGPP playground). This is not very + effective under dosemu, but this is the only possible way under + real DOS, and is also now unavoidable because msdos.c doesn't any + longer have the root of the memory list to pass to the memory.c API. + - Unfortunately the memory.h name is already taken, so the added + header is called dmemory.h :( + +2005-06-29 Stas + + * src/dosext/dpmi/: dpmi.h, msdos.c, msdos.h: + - All the msdos.c data can now be static + - Remove the unused variable on which gcc4 complains it is used uninitialized + + * src/: dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, + dosext/dpmi/msdos.c, dosext/dpmi/msdos.h, base/bios/bios.S, + include/bios.h: + Next step in separating dpmi.c/msdos.c. dpmi.c no longer accesses + the state struct of the API translator, but it now calls its + hooks directly, while normally the translator should register + that hooks via the DPMI API. This will, however, add an extra + context switches under dosemu, so the current state is probably + the most adequate for dosemu. A few extra hooks should not be + a big problem for the DJGPP port either. + + * src/dosext/dpmi/: dpmi.c, dpmi.h, memory.c, vxd.c: + - Remove the pre-client DPMI_SEL selector. It doesn't need to be + per-client, we only have to be care to provide the 16bit and 32bit + version of it separately. This way vxd.c no longer accesses the + DPMI state structure directly. + - Moved dpmi_setup() from memory.c to dpmi.c and make it to + allocate the above two selectors. + - Made LDT_ALIAS and PMSTACK_SEL really per-client this time + (the old code that inherits them was forgotten to remove). + +2005-06-28 Bart + + * src/: env/video/attremu.c, env/video/crtcemu.c, + env/video/gfxemu.c, env/video/miscemu.c, env/video/render.c, + env/video/vesa.c, env/video/vgaemu.c, include/vgaemu.h: + Allow loading the Bochs BIOS (very experimental). + VGAEMU fixes so that it can change modes. It works with standard VGA modes, + not VESA modes. You can use it by using a command line option such as + -I 'video { vga vbios_file /usr/share/vgabios/vgabios.bin }' + + * src/env/video/instremu.c: + Fix typos + + * src/env/video/instremu.c: + Allow instremu to work with read-only code. + +2005-06-28 Stas + + * src/dosext/dpmi/dpmi.c: + Moved env restoration to the better place (after the RSP stuff is + finished). + + * src/dosext/dpmi/: dpmi.c, dpmi.h, msdos.c, msdos.h: + Some large cleanups: + - First step at separating the API translator from DPMI. msdos.c + now has its own state structure, all the translator-related stuff + moved there from the DPMI structure. msdos.c no longer accesses + the DPMI structure, but the dpmi.c still accesses the translator + structure - this can't be addressed without a big functionality + changes. + - Cleaned up old_dos_terminate() to make Clarence happy + - Some cleanups in ConvertSegmentToDescriptor and friends. + +2005-06-27 Stas + + * src/env/video/seqemu.c: + Fix the regression with the Trident code to make Windows happy. + +2005-06-26 Bart + + * etc/dosemu.conf, etc/global.conf, src/base/init/parser.y.in, + src/base/mouse/mouse.c, src/base/serial/ser_init.c: + Some fixes to get the mouse back to work in X when $_mouse_dev happens to + be set to "com1". Avoid talking about X/xterm for $_mouse_dev: it is + irrelevant for those two. + Fix a related potential SIGSEGV in the parser. + + * src/base/async/int.c: + Install a hook function for LFNs via f000:0210 using the new split + interrupt_function functionality. This fixes bug #1211627. + + * src/base/async/int.c: + Split the interrupt_function array into two sets, one for revectored + ints and one for non-revectored ints. This allows using a real DOS mouse + driver without flipping function pointers, and also repairs + $_mouse_dev="com1". + + * src/: dosext/dpmi/msdos.c, env/video/instremu.c: + Use instremu to decode certain segment modifying instructions in msdos.c + + * src/dosext/dpmi/msdos.c: + Added 32-bit address decoding. Maybe some of this can be merged with + instremu later. Fixes bug #1227378 + +2005-06-25 Stas + + * src/: emu.c, env/video/vc.c, plugin/kbd_unicode/keyb_raw.c, + base/dev/misc/timers.c, include/emu.h: + Lets remove the do_ioctl then, sorry for the typo bug... + + * src/dosext/dpmi/dpmi.c: + Use-after-free detected by smalloc. + + * src/: include/smalloc.h, include/utilities.h, base/misc/lowmem.c, + base/misc/smalloc.c, base/misc/utilities.c: + - Shuffled the assertions in smalloc, since I've seen it asserted. + - Added the error handling, now all the memory violations will be + logged with a stack trace. + + * src/base/: bios/int16.c, misc/ioctl.c: + Adjustments to the hogthreshold code in a hope to address those + ramblings in an ML. + +2005-06-25 Bart + + * src/: dosext/mfs/mangle.c, dosext/mfs/mangle.h, dosext/mfs/mfs.c, + dosext/mfs/util.c, env/video/X.c, env/video/X.h, + env/video/text.c, include/emu.h, include/mouse.h, + plugin/translate/make_attributes.c: + Removed now-dead non-unicode variations for MFS and X copy/paste. + + * src/: env/video/console.c, env/video/hgc.c, env/video/vc.c, + env/video/vga.c, env/video/vga.h, env/video/video.c, + include/video.h: + Console code cleanups. + +2005-06-24 Bart + + * src/: base/async/int.c, base/bios/int10.c, base/mouse/mouse.c, + base/mouse/mouse_gpm.c, env/video/X.c, env/video/console.c, + env/video/crtcemu.c, env/video/dualmon.c, env/video/hgc.c, + env/video/text.c, env/video/vc.c, env/video/vgaemu.c, + env/video/video.c, include/memory.h, include/video.h, + plugin/sdl/sdl.c, plugin/term/mouse_xterm.c, + plugin/term/terminal.c: + Eliminate the two-letter global variables "co" and "li". + + * src/include/emu.h: + Fix do_ioctl macro. Why not simply remove it by the way? + +2005-06-23 Stas + + * src/dosext/dpmi/dpmi.c: + Fix DPMI RSP functionality which was ruined by the base_addr stuff. + +2005-06-22 Bart + + * src/env/video/text.c: + Turn off the visible selection if anything in it changes (not just the + attribute but also text), so that *if* something is selected it matches + what is pasted when you press the middle mouse button. + +2005-06-21 Bart + + * src/env/video/: X.c, text.c: + Fix cursor position with fonts that are 8 pixels high. + Fixes #1206525: 1.3.2: text mode cursor uses only upper half of cell @80x50 + +2005-06-21 Stas + + * src/: dosext/net/net/libpacket.c, include/pktdrvr.h, + dosext/net/net/pktnew.c: + Minimal receive mode support for packet driver. Completely untested... + + * src/: emu.c, arch/linux/async/signal.c, base/async/int.c, + base/misc/dos2linux.c, base/misc/ioctl.c, env/video/vc.c, + include/emu.h: + Remove the ancient (unused) ioctl queueing code. + +2005-06-20 Bart + + * src/env/video/text.c: + No selection area is shown if the DOS app changed the colours itself. + Fixes #1206137. + + * src/: include/video.h, plugin/term/terminal.c: + Terminal code: + * blinking instead of back background should be set to "blink" at boot time + * split up utf8 and 8bit character update routines for sanity + * change The_Charset property that signals an ACS character + * for Slang 1 in 8bit mode we can't use ACS if blinking is on + * fix attribute 0 + * for (patched) SLang 1, SLtt_Use_Blink_For_ACS is always set in UTF8 mode, + and in 8 bit mode depends on the bright background setting. + This fixes a new bug with Norton Utilities. + +2005-06-12 Stas + + * src/base/async/int.c: + Set 0x66 ivec to IRET only when Windows is detected, as some + programs expects it to be NULL (bug #1218406) + INT_OFF(0x68) is where we have an IRET (hack) - INT_OFF(0x66) didn't + have one. + +2005-06-04 Stas + + * src/base/mouse/mouse.c: + Remove the *2 Y multiplication from the PS2 code. It looks + more like we need to devide it by 2, not multiple. + The real problem (as it seems to me) is that we do not implement + the sensitivity options for PS2 interface, and use the int33 + speed settings instead, which is completely bogus. One have to + add the sensitivity settings for the PS2 pointer. + +2005-05-31 Stas + + * src/plugin/kbd_unicode/serv_backend.c: + Use the bios's keyboard modifiers instead of always relying on the + internal ones. (bug #1207940) + +2005-05-29 Stas + + * src/dosext/dpmi/msdos.c: + From Japheth: added the translation of int21/ah=43h and ax=71a0h + Make int21/ah=0 use ah=0x4c to work around some bugs in ms-dos. + Path from Japheth. + +2005-05-26 Stas + + * src/base/async/int.c: + This works faster. + +2005-05-25 Stas + + * src/emu.c: + Reset fatalerr to 0 in leavedos() to allow the do_call_back() to do + its final work. + + * src/: base/mouse/mouse.c, base/mouse/mouseint.c, env/video/X.c, + include/mouse.h: + Fixed a diabolical rounding errors in mouse_move_relative(). That + allowed to remove the hacks with Y coord multiplication, which, in + turn, should fix all the recent and old sensitivity problems. Lets see. + + * src/: dosext/drivers/cdrom.c, plugin/commands/commands.c: + - Resetting/reopening cdrom before ejecting helps to eject it more + reliably. + - Implemented "eject -t" for closing the tray. + +2005-05-24 Stas + + * src/base/mouse/mouse_gpm.c: + gpm-mouse can lock up dosemu if Gpm_GetEvent() is called on a + non-blocking fd when there is no data - it then returns the + random values in a mouse coords, which leads to bad results. + Added the select() check to avoid the problem. + + * etc/dosemu.conf: + Some fixes to dosemu.conf. Comments mainly, but also enabled the + $_lfn_support. Any reasons why it was disabled? + +2005-05-23 Stas + + * src/dosext/drivers/cdrom.c: + Use O_NONBLOCK for cd-rom, that makes eject to work more reliably. + (suggested by Robert Komar) + + * src/: dosext/drivers/cdrom.c, plugin/commands/commands.c: + Make eject.com to print some diagnostic on failures. + + * src/env/video/X.c: + Use the modes with the highest dotclock rates. + +2005-05-20 Bart + + * ChangeLog: + Update ChangeLog with last changes. +2005-05-20 Bart + + * NEWS, src/env/video/vga.c: + gcc warning; set date in NEWS + + * (almost all files) + It's 2005 this year. + + * NEWS, doc/EMUfailure.txt, doc/README.txt, man/dosemu.1.in, + man/dosemu.bin.1.in, src/doc/HOWTO/EMUfailure.sgml: + Documentation updates. Mostly grammatical stuff, and a clarification + about framebuffer consoles: it was possible before with some video cards + (e.g. SiS) but with others only with major problems (needing fbset after + exitemu, not being able to switch consoles and then go back etc). + + * configure: + Regenerated + + * etc/dosemu.conf, src/commands/ems.S: + Soften language about external XMS drivers a bit. No need to rush + dosemu-freedos users at the moment since the only known problematic + program is MS MEM. + +2005-05-20 Stas + + * NEWS: + No need to mention svgalib since it doesn't work very well yet + (lacks one small fix in svgalib), but mention the fact that dosemu + can be started from under the fbcon. + + * NEWS: + Some more news added. But do not mention tasmx since it was just a + regression at some point after all (2005-02-24 namely) + + * configure.ac: + Cosmetic about the svgalib detection. + +2005-05-19 Bart + + * ChangeLog, NEWS, VERSION: + NEWS and ChangeLog updates for 1.3.2. + +2005-05-10 Bart + + * src/dosext/misc/xms.c: + Fix the XMS_RET() macro; and making it a function is more efficient. + Added some comments to hopefully make sure the same bugs don't creep in + again. This fixes Aladdin's issues with XMS. + +2005-05-09 Bart + + * src/base/misc/fatfs.c: + Don't scan files on fatfs drives for FreeDOS, as this DOS has these + drives redirected early enough for int13 never to be necessary. + This can save quite a bit of startup time. + + * src/: base/async/int.c, dosext/mfs/mfs.c, include/redirect.h: + If the CDS changes, try to redirect any fatfs drives that did not fit in + the CDS before. This may happen when FreeDOS changes LASTDRIVE from + config.sys, and more than 4 fatfs drives are defined. + +2005-05-08 Bart + + * etc/dosemu.alias, etc/vga10x24.bdf, etc/vga12x30.bdf, + etc/vga8x19.bdf, src/arch/linux/Makefile.main: + Added some more vga fonts, which more or less match 640x480, 800x600 + and 1024x768 resolutions. Thanks to Martin Reuber, OFD Kiel, + German Fiscal Administration + +2005-05-06 Stas + + * src/env/video/: vga.c, vbe.c: + - Make do_int10_setmode() to do something more than just locking up + the video bios. + - Disabled the "special memory mode" - Matrox G550 doesn't honour + the bit15 (even for that mode!), so the memory was cleared before + saving. By the way, Matrox can handle our banked saving/restoring + very well, but Radeon 7500 miserably fails on our bankswitching code. + - Disabled the "set mode before restore" hack - that never worked + because of the both the above bugs, it only makes problems. + - Restoring the ext regs is necessary after saving/restoring the video + memory (well, without using the "special memory mode" it may not be + necessary, but I added it to stay safe). + + This makes the console VESA driver pretty much usable. + + * src/dosext/dpmi/msdos.c: + Really fix int21h/ah=38h this time! tasmx is now finally... yuck + +2005-05-05 Stas + + * etc/dosemu.conf, src/include/emu.h, src/include/vc.h, + src/env/video/avance.c, src/env/video/cirrus.c, + src/env/video/matrox.c, src/env/video/s3.c, src/env/video/sis.c, + src/env/video/vbe.c, src/env/video/vga.c: + Fixed some bugs with $_vmemsize, made it to accept (auto) and (off), + defaulted to 4096 to make the console switching faster. + +2005-05-05 Stas + + * src/arch/linux/dosext/sound/midid/: device.c, device.h, timid.c: + Cosmetics and gcc-4 warning fixes. + +2005-05-04 Stas + + * src/env/video/: svgalib.c, vbe.c: + Cosmetic. + + * src/: include/debug.h, emu.c: + Make the gdb stacktracer interface globally available. + + * src/emu-i386/do_vm86.c: + This sanity check made problems. + +2005-05-02 Stas + + * src/env/video/X.c: + $_X_fixed_aspect can work for the text modes too. + +2005-05-02 Bart + + * src/plugin/sdl/include/sdl_config.h.in: + Added forgotten file sdl_config.h.in + +2005-05-01 Bart + + * configure, configure.ac: + Re-fix auto setting for target-cpu. + Use -Wno-pointer-sign for gcc-4.0 to avoid obscuring really useful warnings + for now. + + * src/plugin/sdl/Makefile: + Fix makefile in previous commit. + + * src/plugin/sdl/: Makefile, configure, configure.ac, + config/plugin_config.h, config/plugin_config.h.in: + Use include/sdl_config.h[.in] included in config/plugin_config.h just like the + kbd_unicode plugin does. This avoids the "mkpluginhooks-before-configure" + problem. + + * configure, configure.ac, default-configure: + #1118031: --target_cpu should be --with-target-cpu or it doesn't work. + + * src/dosext/mfs/mfs.c: + Let MFS "commit" return TRUE if fsync was successful (it was reversed). + Happens to fix the problem with Word's "disk is full" message. + +2005-05-01 Stas + + * etc/dosemu.conf, src/base/serial/ser_init.c: + tty locking can be disabled by $_ttylocks="" + +2005-05-01 Bart + + * etc/global.conf, src/plugin/kbd_unicode/config/plugin_parser: + Fix #1110543 Custom keyboard layout + The underlying foreach parser problem is still a mystery, but as long the + keytables work... + + * compiletime-settings, compiletime-settings.devel, + compiletime-settings.help, configure, configure.ac, + default-configure, src/arch/linux/async/signal.c, + src/base/mouse/mouse.c, src/env/video/console.c, + src/env/video/dualmon.c, src/env/video/video.c, + src/include/config.h.in, src/include/video.h, + src/plugin/kbd_unicode/dosemu_keys.c, + src/plugin/kbd_unicode/keyb_clients.c, + src/plugin/term/terminal.c: + Remove the S-Lang plugin. DOSEMU now cooperates nicely with all SLang + libraries that are present in Linux distributions, so it is no longer + necessary. Made a comment about some minor issues with the various + library versions in existence. + If no slang-devel library is installed, DOSEMU can now compile without + terminal support. + + * doc/README.gdb: + correct typo + + * src/: plugin/term/Makefile, plugin/term/mouse_gpm.c, + base/mouse/Makefile, base/mouse/mouse_gpm.c: + Move mouse_gpm.c from src/plugin/term to src/base/mouse. + + * configure, src/include/config.h.in: + Regenerate autoconf files. + +2005-04-29 Stas + + * src/dosext/dpmi/msdos.c: + Further fixes for translation of int21h/ah=38h and 5dh + + * src/base/mouse/mousevid.c: + for text modes too. + + * src/dosext/mfs/mfs.c: + Debug msg. + + * src/: base/bios/int10.c, base/mouse/mousevid.c, + env/video/vgaemu.c, include/vgaemu.h: + - Fixed 40h:4eh bios value (current video page start) + - Made the gfx-cursor code to take that value into an account + (works in Orbits3 properly now) + - Disabled the X-specific code for gfx-cursor. This can introduce + the regressions, but as the gfx-cursor for X was added just a few + days ago (what was that X-specific code then, if there wasn't a + gfx-cursor under X before?), the regressions on it are not a problem. + + * src/dosext/mfs/mfs.c: + Off-by-one error in MFS code causes lock-ups (bug #1189666) + +2005-04-27 Stas + + * src/dosext/dpmi/msdos.c: + Fixed int21h/ah=38h translation to get tasmx to work. Yes, tasmx + used to work sometime in the past, but the bug was always there, + so it couldn't work properly. + Also fixed 2 gcc4 warnings. + +2005-04-26 Stas + + * src/env/video/X.c: + Fullscreen mode can now always enable mouse grab. + + * src/base/: init/lexer.l.in, init/parser.y.in, misc/disks.c: + Applied patch #845119 - 2.8M floppy support. + The patch was there for 1.5year already - enough! + + * src/: env/video/instremu.c, env/video/vgaemu.c, include/vgaemu.h: + Moved instremu to the new vga helpers, Logical_VGA_xxx can now be static. + + * src/: include/vgaemu.h, base/mouse/gcursor.c, base/mouse/mouse.c, + env/video/vgaemu.c: + - Added the vga memory access helpers + - Moved gfx-cursor code to use that helpers + - Enabled the gfx mouse cursor code under X + + GFX mouse cursor now works under X! + +2005-04-24 Stas + + * src/arch/linux/dosext/sound/linux_sound.c: + Fix recent small regression with sound code (kryptegg works again) + + * src/env/video/vc.c: + Remove unused (and crap) DUMP_VIDEO + + * src/base/mouse/: gcursor.c, mouse.c: + Another small step towards the gfx mouse cursor under X. + + * src/env/video/svgalib.c: + Trying to resurrect the svgalib support. + It really works much faster than our VESA driver (due to the + use of the native driver) but it doesn't save the video memory, + so there may be some problems. + + * configure.ac: + Added proper svgalib checks. New svgalibs are better, but we can't + even link the old ones in. + But not enable it for now - svgalib still misses one very small + patch that I forgot to submit, and so it crashes, damn. Will have + to wait for the next version again :((( + + * src/include/vc.h: + Increase MAX_X_REGS as per the new svgalib. + + * src/env/video/: vc.c, vga.c: + Disable dump_video(). It can crash if the MAX_X_REGS of dosemu + mismatches with MAX_REGS of svgalib. + +2005-04-20 Stas + + * src/emu-i386/ports.c: + set_ioperm() should also set the std-handler. This allows the dosemu + code to call port_outb/port_inb even for the iopermed ports, and the + real I/O will occur. This is necessary for the mouse gfx code to work + under X. + Also disabled HANDLE_VID_IO and HANDLE_SPECIAL. They prevented the + above code from working and also these features looks completely + broken and useless. + +2005-04-19 Stas + + * src/base/init/: config.c, parser.y.in: + Repair $_rdtsc + +2005-04-17 Stas + + * src/arch/linux/dosext/sound/midid/timid.c: + Increase timidity timeout from 1 to 3 seconds - timidity is slow + these days. + + * etc/dosemu.conf, etc/global.conf, src/include/emu.h, + src/arch/linux/dosext/sound/linux_sound.c, + src/arch/linux/dosext/sound/linux_sound.h, + src/base/init/config.c, src/base/init/lexer.l.in, + src/base/init/parser.y.in: + Added the option to control the direct DAC write frequency for OSS. + No longer need to hack linux_sound.h + + * src/: base/mouse/mouse.c, include/memory.h: + Moved Mouse_INT_OFF to memory.h and adjusted it instead of changing + the segment (Stas wake up and stop stumbling around the mouse code!) + + * src/base/mouse/mouse.c: + Mouse_INT now requires BIOSSEG. + + * src/: env/video/X.c, base/mouse/mouse.c, base/mouse/mouseint.c, + plugin/sdl/mouse_SDL.c: + Enable the native mouse cursor by default (to stay compatible), and + the X and SDL frontends disable it manually. + Also "raw" mouse needed the *2 for Y coordinate too. I don't know + why, maybe the screen resolution is not taken into an account somewhere. + +2005-04-16 Stas + + * src/: base/mouse/mouse.c, include/mouse.h: + Move "native_cursor" to config.mouse, where it actually should be. + + * src/: env/video/X.c, plugin/sdl/mouse_SDL.c: + X and SDL frontends were erroneously overriding the mouse sensitivity + values. Also teached X.c about the 2:1 resolution. Native mouse cursor + now have the proper speed. + + * src/base/mouse/mouse.c: + Native cursor state should survive save/restore. + + * src/include/memory.h: + Set 0x33 ivec (mouse) not to 0xf000 segment, as some programs (td) + are refusing to use it in that case, and the offset should not be 0 too. + (bug #818094 of myself) + +2005-04-15 Stas + + * src/: env/video/X.c, base/mouse/mouse.c, include/mouse.h: + Enable the native cursor code. Used under X only for now (not for SDL, + for example). (RFE #1079033 of my own) + + * src/env/video/X.c: + Disable the selection ability when mouse grab is enabled. + +2005-04-14 Stas + + * src/env/video/: X.c, render.c, vbe.c, vga.c: + Abort dosemu on the fatal video errors instead of having the blank screen. + +2005-04-12 Stas + + * src/env/video/X.c: + Added keyboard grab - hardcoded to Ctrl-Alt-k for now. + + * src/env/video/X.c: + Remove some ancient code. + +2005-04-09 Stas + + * src/: emu-i386/ports.c, base/dev/misc/joystick.c, + base/init/parser.y.in: + Dont try to register the joystick if it is disabled/unavail. (SR #1178900) + +2005-04-05 Stas + + * src/env/video/: crtcemu.c, seqemu.c: + Previous patch needed this too. + + * src/env/video/: crtcemu.c, seqemu.c: + Kill the optimization that optimizes nothing. + +2005-04-04 Stas + + * src/env/video/X.c: + Handle the unlikely case of the miltiple KeyRelease events. + + * src/dosext/dpmi/dpmi.c: + This keyboard hack is no longer needed. + + * src/env/video/X.c: + Filter out the fake X KeyRelease events of the autorepeat keyboard mode. + This fixes bug #1113468, all the recent keyboard regressions, and the + number of the old keyboard bugs too. + There are lots of hacks all around dosemu that were trying to work + around that problem with the different level of success. They all now + have to be located and removed. + +2005-04-01 Stas + + * etc/global.conf: + Set $BEING_ON for X too. + +2005-03-28 Stas + + * src/plugin/commands/commands.c: + generic.com should print something usefull too. + +2005-03-27 Stas + + * src/plugin/commands/unix.c: + Nah, use-after-free on error path. + +2005-03-26 Stas + + * src/plugin/commands/unix.c: + Fixes to unix builtin: + - Dont strip the trailing slashes for the DOS directory path, as this + path may consist of only that slash (second part of #1152829) + - Dont terminate if the linux path we wanted to execute, doesn't exist. + For the DOS path, figuring out the existance is more difficult, so left + as is for now. + + * src/plugin/commands/builtins.c: + Small cleanup. + + * src/emu-i386/: cpu.c, ports.c: + Enlarge line buffer for proc scans, remove smiley ":)" from asm + constraints (lets put smiles in a comments, not in the code:) + + * src/base/serial/ser_ports.c: + Update the MSR status before reading it - should make the serial more + robust. + + * src/base/dev/misc/rtc.c: + Properly initialize date in cmos. "bios" timesource should now be + quite usable. + + * src/base/dev/misc/rtc.c: + Multiple bugs with date in RTC. + +2005-03-22 Stas + + * src/include/dos2linux.h, src/base/misc/dos2linux.c, + src/plugin/commands/unix.c, src/base/misc/utilities.c, + etc/dosemu.conf: + Fixed (and re-enabled) the terminate-after-execute feature (bug #1152829). + Also some minor adjustments/leftovers. + +2005-03-21 Stas + + * src/base/misc/utilities.c: + /proc/self/maps can contain lines longer than 100 chars, causing DPMI + to fail (old bug btw). + + * etc/dosemu.conf: + 0x40000000 for the $_dpmi_base was a bad choise for the non-flexmmap + kernels. 0x10000000 looks safer. + +2005-03-20 Stas + + * src/base/async/int.c: + Calling io_select() is too expensive, dont do it more than 1000 times + per second (a cheap fix for the keen4 lockups, but it is still too slow). + + * src/base/mouse/mouse.c: + This improves the mouse responsiveness a lot. + + * src/base/mouse/mouse.c: + Repair the mouse too. + + * src/: arch/linux/dosext/sound/linux_sound.c, + arch/linux/dosext/sound/linux_sound.h, dosext/sound/sound.c, + include/sound.h: + This should make the sound recording to work properly (bug #1074310, + was not confirmed) + + * src/doc/HOWTO/EMUfailure.sgml: + This was fixed. + + * etc/dosemu.conf: + Set $_X_aspect_43 to (off), as we agreed, AFAIR. + This fixes z80s emulator visual artifacts, and many games too. + + * src/base/dev/pic/pic.c: + Remove the PIC recursion. + This will probably break everything again, but at least the keyboard + should survive. + + * src/plugin/kbd_unicode/serv_backend.c: + Preparing (again) the keyboard code for the removal of the PIC + recursion. We have to simulate the proper delay between the chars, + otherwise the nasties races are happening, and whatever is typed, + gets doubled (bug #1037915). + Set the emulated kbd clock frequency to 27.5KHz (that is, actually, + after the set2->set1 translation of 8042; the real frequency may + be slightly different). + I hope this is the last implicit dependancy the keyboard code had + to the PIC code. If all the other subsystems have so many too, then + we'll be in a big troubles:) + +2005-03-18 Stas + + * etc/dosemu.conf, etc/global.conf, src/base/init/config.c, + src/base/init/init.c, src/base/init/lexer.l.in, + src/base/init/parser.y.in, src/base/misc/utilities.c, + src/dosext/dpmi/dpmi.c, src/dosext/dpmi/dpmi.h, + src/dosext/dpmi/memory.c, src/include/emu.h, + src/include/utilities.h: + Introduce the $_dpmi_base option which allows to choose the address + of a DPMI memory pool. The problem with the dynamic allocation is + that some programs can't handle the bit 31 in the pointers, and that's + what being set now in the recent 2.6 kernels. + Fixes Mortal Kombat 2 and SerfCity game saving (bug #1164054) + +2005-03-17 Stas + + * src/base/async/int.c: + Returned the old mouse hack of mine instead of the (currently disabled + anyway) Bart's one. This fixes the mouse for SerfCity game, makes the + mouse pointer to be syncronized at startup again, and (hopefully) + removes the possibility of a crash when the DOS sets int33 to some crap. + This doesn't allow to call into an external mouse driver, but thats + a useless feature anyway. + + * src/doc/HOWTO/EMUfailure.sgml: + Updated to reflect the current state of affairs. + +2005-03-16 Stas + + * src/: base/init/init.c, plugin/kbd_unicode/include/keyb_server.h, + plugin/kbd_unicode/serv_8042.c, + plugin/kbd_unicode/serv_backend.c: + More work on preparing the keyboard code for the PIC recursion removal. + + * src/base/dev/pic/pic.c: + Remove the now harmfull sanity check from pic_request(). This should + fix most of the breakages of the previous commit. + +2005-03-06 Stas + + * src/plugin/commands/: Makefile, blaster.c, blaster.h, commands.c: + New helper: blaster.com. Sets the BLASTER and MIDI variables accordingly + to the dosemu configuration. + Patch from Ryan Underwood, #1157059. + +2005-03-05 Stas + + * src/plugin/kbd_unicode/keymaps.c: + Fix problem with '<' key in non-keycode mode (bug #818145) + +2005-03-02 Stas + + * src/: arch/linux/async/signal.c, base/dev/misc/rtc.c, + base/dev/misc/timers.c, base/dev/pic/pic.c, base/init/init.c, + base/mouse/mouse.c, base/serial/ser_defs.h, + base/serial/ser_irq.c, dosext/net/net/ipx.c, + dosext/net/net/pktnew.c, dosext/sound/sound.c, include/iodev.h, + include/ipx.h, include/mouse.h, include/pic.h, include/pktdrvr.h, + plugin/kbd_unicode/include/keyb_server.h, + plugin/kbd_unicode/serv_8042.c: + Abruptly terminate the PIC recursion usage. Globally, for all drivers. + This will probably break everything at once, but at least those + subsystems that were prepared, should tolerate. + +2005-03-01 Stas + + * src/: include/emu.h, arch/linux/async/signal.c, + base/misc/ioctl.c: + Trying to unbind SillyG (irqpassing) from PIC recursion. I dont have a + test-case for it, hope it still work. + + * src/base/mouse/mouse.c: + Unbind mouse from the PIC recursion. + That looks pretty much it. Next thing would be to try remove the + recursion. Havoc to come... + + * src/: base/serial/ser_init.c, base/serial/ser_irq.c, + base/serial/ser_ports.c, include/serial.h: + Trying to unbind serial from PIC recursion. + Sorry, no way to keep serial functional any longer, lets break it again:) + + * compiletime-settings.devel: + Linking with svgalib can produce a broken build because the svgalib + was linked without -Bsymbolic. + svgalib is probably dead... + +2005-02-28 Bart + + * src/plugin/sdl/sdl.c: + Implemented handling of the SDL_QUIT event so that clicking "x" quits DOSEMU. + + * src/tools/periph/Makefile: + Do not generate hdimage.dist in the default build as we don't need it anymore. + +2005-02-24 Stas + + * src/dosext/dpmi/msdos.c: + Get rid of DS_MAPPED/ES_MAPPED to make the translator code fully + re-entrant (again) + + * src/dosext/dpmi/msdos.c: + Remove some usages of ES_MAPPED/DS_MAPPED. + +2005-02-17 Stas + + * src/dosext/dpmi/dpmi.c: + Call keyb_server_run() from dpmi_fault() to improve the keyboard + response time (bug #1113468). I hope doing that is safe. + + * src/base/async/int.c: + Disable the mouse iret hack (again). + It turned out that DOS initializes the IVT to not always point to irets. + If PC-DOS is booted without DOS=HIGH, then for some reasons it sets + int33 (and others) to some crap, then the crash. + Besides, this code simply asks for being disabled: the recursion have + to be addressed in do_int(), not here. + +2005-02-11 Stas + + * src/base/serial/ser_init.c: + Made serial ports to work *again*. + Lets keep them working at least for some time, pleeease! :) + +2005-02-07 Stas + + * src/: arch/linux/async/signal.c, base/async/int.c, + base/bios/int16.c, base/dev/misc/lpt.c, base/misc/ioctl.c, + dosext/net/net/ipx.c, emu-i386/cputime.c, include/timers.h: + More flexible idle system. For now tuned similar to the current one, + except for the printer and async I/O, which, when active, give more + power to dosemu. + Now we can easily tune the things as per 1.2.1 and better. + Helper would be nice to allow the run-time adjustments, or perhaps + we need a section in dosemu.conf dedicated to the idle stuff. + +2005-02-06 Bart + + * doc/README.gdb: + Add some more info about how to deal with GDB and avoid SIGALRM getting + in the way of single stepping. + + * src/base/bios/int10.c: + Fix problem with cursor drawing with bigger fonts such as vga11x19. + +2005-02-05 Stas + + * src/dosext/dpmi/dpmi.c: + Per-client stack must be freed on a client termination btw. + +2005-02-05 Bart + + * etc/dosemu.conf, etc/global.conf, src/doc/README/config, + src/env/video/avance.c, src/env/video/cirrus.c, + src/env/video/matrox.c, src/env/video/s3.c, src/env/video/sis.c, + src/env/video/vbe.c, src/env/video/vga.c: + $_vmemsize = (0) now means autodetect just like $_vbios_size = (0). + If you specify a fixed amount DOSEMU will only try to save/restore this + amount so console switching does not take such a long time. + + * src/include/emu.h: + gcc-4.0 warning fixes + + * src/doc/HOWTO/EMUfailure.sgml: + Add missing "of". + + * src/env/video/text.c: + Fix gcc-4.0 warning. + + * src/emu-i386/ports.c: + Remove unused devstat struct. + + * src/base/init/: config.c, lexer.l.in: + Fix gcc warnings. + + * src/base/serial/ser_init.c: + ser_init cleanups: use a table instead of switch for default COM settings, + get rid of some gcc-4.0 warnings. + +2005-02-05 Stas + + * src/dosext/dpmi/: dpmi.c, dpmi.h, memory.c: + Make PM stacks per-client. + Borland C IDE can benefit from this. + +2005-02-04 Stas + + * src/doc/README/dosnet: + Networking documentation update. + I am not a doc writer, but I tried to make it short, simple and + informative. Please dont beat me on that:) It should be better + than the old (completely outdated) one anyway. + + * src/base/init/config.c: + Dont parse /proc/cpuinfo if not necessary, plus some cleanups. (SR #1081283) + + * etc/global.conf, src/base/async/int.c, src/base/dev/misc/rtc.c, + src/base/init/lexer.l.in, src/base/init/parser.y.in, + src/include/emu.h, src/include/iodev.h: + First pass on merging the timing patch from Paul Crawford and Andrew Brooks. + This includes BIOS timing and a few fixes and enhancements to the old + code. + Linux timing is not yet. + +2005-02-01 Stas + + * src/base/serial/ser_irq.c: + Completely draining the queue appears to be somewhat slow. + This patch allows up to 2 (QUEUE_THRESHOLD) chars to queue. + This returns the previous speed while still allowing my test-cases + to work. + +2005-01-30 Stas + + * src/base/serial/ser_irq.c: + Get the queue size from linux and dont request the TX interrupt + before it is empty. + This makes my ancient test-case to work and hopefully is not as + slow as my previous attempt of using tcdrain(). + + * src/base/serial/: ser_defs.h, ser_irq.c, ser_ports.c: + Made transmit queue a sliding buffer (like the receive one) so that + the end-start calculation to always give the right result. + +2005-01-28 Stas + + * src/: dosext/misc/emm.c, commands/ems.S: + Added int2f hook to ems.sys to provide the "unhooked" version of + the XMS callback that we hooked for UMB... Windows really demands + that (works unreliably otherwise when the native himem.sys is in + the game). + Also while I was at it, I reworte the ems.S almost entirely to get + rid of the ancient crap that was all there. + + * src/: include/serial.h, base/serial/fossil.c, + base/serial/ser_irq.c, base/serial/ser_ports.c: + Replaced tx_buf_bytes/rx_buf_bytes vars with the macros to reduce + the chances for the counters-out-of-sync bugs. + + * src/base/serial/ser_ports.c: + Properly update the buffer counters when receiving data. Horrible + bug in fact, it used to work by a pure luck before - rx_buffer_slide() + was bringing the counters in sync at some point, but not always, and + not any more. This bug was there since the creation of the world. + This fix whould make the fifo transfers much faster and more reliable. + Closes #1103434. + + * src/base/serial/ser_ports.c: + Be more carefull about the fifo counter. Mostly cosmetic. + +2005-01-26 Stas + + * src/dosext/dpmi/dpmi.c: + Remove RealModeContext and the surrounding stuff. + I dont know why it was introduced. It is stated it helps some DJ200 + compiler (djgpp?), but it looks like the hack to work around some + bugs that are not there any more. If someone know why this stuff + was necessary, let me know. + Bart, please see if djgpp feels well now without the hack. + + * src/dosext/dpmi/dpmi.c: + Made it possible for the 16bit clients to safely access the 32bit API. + This is not defined in a DPMI specs, so this is a small extension. + No 16bit client is allowed to use 32bit API, but it is necessary + for the djgpp-ported API translator to provide the service for 16bit + clients. + + * src/: include/pci.h, base/dev/misc/pci.c, base/async/pci_bios.c: + Made pciemu to do something more than just locking up the card + by fixing the inb/inw. + Also the header space is enlarged (still reading 64bytes only though). + Now it works with some cards, but in most cases it still doesnt. + The cards that reserve an extra PCI ports (like my Radeon) are + fine, but the ones that dont, usually keep the extended registers + in the config space, right after the config header. The emulation + approach cannot fork for them... + The last argument I've heard against just limiting the direct PCI + access, was that it would be too slow. But pci_port_inb/pci_port_outb + already do this! + I think pciemu have to go away. It is useless, at least for its + currently intended usage. + +2005-01-24 Stas + + * man/dosemu.bin.1.in: + the related man adjustment + + * etc/dosemu.conf, man/dosemu.bin.1.in: + Change $_cpu to 586 as people report problems about 386. + Dosemu is beleived to be 586-safe these days. + +2005-01-17 Stas + + * src/env/video/text.c: + Use MB_LEN_MAX + + * etc/dosemu.conf, etc/global.conf, + src/arch/linux/mapping/mapping.c, src/base/async/int.c, + src/base/dev/misc/cmos.c, src/base/init/config.c, + src/base/init/init.c, src/base/init/lexer.l.in, + src/base/init/parser.y.in, src/base/misc/hma.c, + src/commands/ems.S, src/dosext/misc/xms.c, src/include/emu.h, + src/include/hma.h, src/include/mapping.h, src/include/memory.h, + src/include/xms.h: + Implemented int15 block move support and the infrastructure for + using the native himem.sys. + Dont forget to update your ems.sys! + +2005-01-15 Stas + + * src/dosext/dpmi/: dpmi.c, msdos.c, msdos.h: + Got rid of S_xxx macros of msdos.c + + * src/dosext/dpmi/dpmi.c: + Preserve the high word of ESP across the hw interrupts and exceptions + for 16bit DPMI clients. + This fixes the 16bit clients that use the 32bit code with 32bit stack + sometimes. + If the 16bit client is interrupted while on 32bit code/stack, it gets + switched to 16bit stack, where the high word of ESP is lost because of + the CPU bug. We can save/restore it by hands. + The 32bit clients that work on 16bit stack are still vulnirable. + + * src/dosext/dpmi/dpmi.c: + Consolidate the stack switching into enter_lpms()/leave_lpms(). + This saves 8K of the code and will make it easier to make the + per-client stacks. + +2005-01-11 Stas + + * src/base/async/int.c: + - Improved windows hacks to support Standard mode. + - Made xtitle code to properly handle DOS shells of Windows. + +2005-01-10 Stas + + * src/dosext/dpmi/msdos.c: + Small bug in int33/ax=0ch translation: offsets of 0 must be allowed. + (Japheth) + + * src/dosext/dpmi/msdos.c: + Better fix for int33/ax=14h translation. + +2005-01-09 Stas + + * src/env/video/text.c: + Fixed memory corruptions in save_selection(). utf8 seem to use up to + 6 bytes per char, so the allocated buffer was too small. Also it was + not properly tracked. + Are there the charsets that can use more than 6 bytes? + + * src/dosext/dpmi/msdos.c: + Fixed int33/ax=14h translation (noticed by Japheth) + + * src/dosext/dpmi/dpmi.c: + Fixed a diabolical bug in do_LAR asm. It was not taking an argument! + So it operated on whatever random value was in %ax. Sometimes it + worked because %ax happened to contain the selector, but in most + cases it just did crap, corrupting the LDT cache. Now z80s emulator + works (again) and I suppose many other progs do as well. + This bug was here (presumably) since 1996, and noone noticed?!!! + This might explain the most of the oddities I observed for years, + like adding some debugging output makes one program to work and + breaks another... + + * src/base/mouse/mouse.c: + Made ps2mouse enable/disable state to not interfere with int33 state. + Fixes some problems with Windows. + + * src/dosext/dpmi/: dpmi.c, dpmi.h: + Added DPMI functions 0xe and 0xf. + Just because these were easy to code up:) + + * src/dosext/dpmi/dpmi.c: + Added DPMI function 0x801 (free physical mapping). + This function does nothing for us, but it should not fail. + +2005-01-07 Stas + + * src/dosext/dpmi/dpmi.c: + Dont reset in_win31 before LDT is unprotected (bug of RSP patches) + + * src/dosext/dpmi/dpmi.c: + Evil typo in a (rarely used) DPMI function 0x50a + + * src/: dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, base/bios/bios.S, + include/bios.h: + PM RSP functionality added (DPMI functions 0xc00, 0xc01). + No sufficient test-case, but needed for djgpp-ported msdos.c + +2004-12-29 Stas + + * src/base/async/int.c: + gcc warning + +2004-12-27 Stas + + * src/base/init/init.c: + Always report Game port and DMA. + + * src/base/bios/bios.S: + Fixes to bios-reported caps: + - Table length does not count itself, so it is 8, not 9 + - Report extended keyboard in features byte 2. + +2004-12-23 Stas + + * src/dosext/dpmi/: dpmi.c, msdos.c, vxd.c, vxd.h: + Moved the VxD glue code from msdos.c to vxd.c. msdos.c must be kept + clean and perhaps it will be possible to port it to DJGPP one day. + +2004-12-22 Stas + + * src/dosext/dpmi/vxd.c: + VTDAPI function 4 added. Media Player (Video for Windows) now works properly. + +2004-12-19 Stas + + * src/emu-i386/ports.c: + Some more debug messages from ports. + +2004-12-16 Stas + + * src/dosext/dpmi/dpmi.c: + 2 small fixes: + - dont trash BP on return from int24 PM inthandler + - attribute the segments to the proper client for direct LDT writes + +2004-12-14 Bart + + * src/dosext/mfs/lfn.c: + Return FILE_NOT_FOUND if the LFN wildcard search for "delete file" + does not find anything (Bug #1020635) + +2004-12-12 Stas + + * src/dosext/dpmi/dpmi.c: + Missing return. + + * src/dosext/dpmi/dpmi.c: + Re-applied accidentally removed commit. + +2004-12-11 Stas + + * src/dosext/dpmi/dpmi.c: + Reflect the exceptions 1,3,4 to realmode handlers as the last resort, + if neither the exception handler nor the prot.mode interrupt handler + is installed. + + * src/base/serial/ser_ports.c: + Dont try to raise IRQ if read() returns -1. + +2004-12-10 Stas + + * src/dosext/dpmi/dpmi.c: + Unhandled exceptions 6 and >=8 should still terminate the client. + + * src/dosext/dpmi/dpmi.c: + Reflect the unhandled exceptions to protected mode interrupts + handlers. If such a handler is not installed, the client is + terminated. This heavily contradicts with the DPMI specs, but + the specs are nonsense - reflecting the fault exceptions to + realmode inthandlers cant work. + (with the input from Japheth) + + * src/: dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, + dosext/dpmi/memory.c, arch/linux/debugger/mhpdbgc.c: + Scan the LDT at startup and find all the used entries (usually there + are none, sometimes one). These entries are not allowed to be changed + by the DOS code. May help with pthreads. + + * src/plugin/commands/builtins.c: + Made builtins version checking to work again. com_error() must be + called only *after* the memory pool is allocated. Fixes the + "builtin OOM" problem reported by Reinhard. + +2004-12-09 Bart + + * src/include/timers.h: + Replaced all assembly language in timers.h except for "rdtsc" itself by C. + This fixes bug #1080784. + +2004-12-09 Stas + + * src/dosext/dpmi/dpmi.c: + Removed most of the ADD_16_32 usages. + + * src/dosext/dpmi/dpmi.c: + One should not use "m" constraint for the variables that are in + the stack and the stack pointer is altered... set back to "d". + (thanks to Reinhard) + +2004-12-08 Stas + + * src/dosext/dpmi/dpmi.c: + Reworked direct context-switching code. It is now 2 times shorter, + easily readable and more gcc-friendly. + +2004-12-07 Stas + + * src/base/serial/ser_init.c: + Fixed error message + +2004-12-03 Stas + + * src/dosext/dpmi/: dpmi.c, dpmi.h, msdos.c: + - Always use copy_context() instead of direct assignments (bug #1076593) + - Improved int23 handling + + * src/base/mouse/mouse.c: + Reset the PS2 pointer callback on mouse reset. Fixes Windows problem + with mouse. + + * src/dosext/dpmi/dpmi.c: + Made "Map Device" function to always fail. + It turned out we dont support that functionality, it just didn't return + the error. We can't support that before the .nopage method is implemented + for /dev/mem VMAs in the kernel. + Changed the DPMI caps again. + + * src/dosext/dpmi/dpmi.c: + Make sure the realmode callbacks never use an unallocated descriptors. + +2004-12-02 Stas + + * src/arch/linux/dosext/sound/linux_sound.c: + Use blocking sync for "disable speaker" SB function. (part of bug #1074310) + SNDCTL_DSP_SYNC ioctl() call is blocking. We need threading... + + * src/: base/dev/misc/timers.c, dosext/dpmi/dpmi.c: + Preserve the "cli_timeout" hack state across the realmode calls. + Fixes the problem with Death Rally. + + * src/emu-i386/do_vm86.c: + do_intr_call_back should push "vflags", not "eflags". + + * src/base/serial/: ser_irq.c, ser_ports.c: + - Disable the left-over modstat timer code. This makes MSR to + work again (bug #1075088) + - moved TIOCSSERIAL ioctl closer to TIOCGSERIAL, for safety. + +2004-12-01 Stas + + * src/: arch/linux/dosext/sound/linux_sound.c, + dosext/sound/sound.c: + Make sound recording kinda work (part of bug #1074310) + + * src/dosext/mfs/mfs.c: + dont dump uninitialized memory to the log. + +2004-11-29 Stas + + * src/: include/pic.h, include/vm86plus.h, + arch/linux/async/signal.c, base/dev/pic/pic.c, + base/mouse/mouse.c, base/serial/ser_init.c, dosext/sound/sound.c, + base/init/init.c: + Remove never really used pic_maski/pic_unmaski. + + * src/: base/mouse/mouse.c, include/pic.h, base/async/int.c: + Unmask IRQ8 and IRQ12 initially. + +2004-11-28 Bart + + * src/: base/async/int.c, base/dev/misc/lpt.c, include/lpt.h: + Printer fixes, enhancements and cleanups: + * printer_close must reset the write function to the stub. + * distinguish between "pipe" and "dev" files. The function pointers + really make sense now I think. + * use line buffering for the printer files, so we don't need to wait + timeout seconds before output appears + * after printer_timeout seconds the printer dev/pipe is closed + * remove unnecessary flush function, fclose/pclose do that automatically + +2004-11-28 Stas + + * src/arch/linux/async/sigsegv.c: + Route coprocessor exceptions to IRQ13 (hints from Japheth). + This allows Netscape 4.x to work. + + * src/include/pic.h: + Make IRQ13 unmasked on startup. + + * src/base/init/init.c: + Enable IRQ13 on PIC, it can be invoked by FPU. + +2004-11-28 Bart + + * doc/: EMUfailure.txt, dosemu-HOWTO.txt: + Regenerate EMUfailure and HOWTO + +2004-11-27 Stas + + * src/dosext/dpmi/Makefile: + Stop mentioning winemu.bat in Makefile. + + * src/base/mouse/mouse.c: + Added dummy "set rate" function to PS2 mouse. This allows Windows + to use the mouse without the custom driver. + + * src/: dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, emu-i386/do_vm86.c: + Moved dpmi_init() call to do_vm86.c. All other DPMI calls are now + ignored if DPMI is not initialized. + Windows seem to forget to unregister the mouse handler on exit. + This patch avoids the crash afterwards. There might be better fix + of course. + + * src/base/async/int.c: + Setting int66 to iret is a Windows hack. + + * src/: base/bios/bios.S, dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, + dosext/dpmi/msdos.c, dosext/dpmi/msdos.h, emu-i386/do_vm86.c, + include/bios.h: + - Made the API translator fully reentrant, which allowed lots of cleanups. + - Implemented the proper reflection to PM of interrupts 0x1c, 0x23, 0x24. + It never worked properly before. + Windows feels much better now when accessing the missing floppies or CD. + + * src/dosext/dpmi/winemu.bat: + winemu.bat is no longer needed as win.com works perfectly. + + * src/dosext/dpmi/: Makefile, dpmi.c, meminfo.c, meminfo.h, + memory.c: + Dont export the /proc/meminfo to the DPMI client. This is just plain + wrong. It is not supposed to know the real amount of mem or swap on + a system. It is only supposed to know the amount we allow it to use, + which is config.dpmi. + + * src/dosext/dpmi/dpmi.c: + Allow the DPMI API to operate with some reserved descriptors + (like LDT alias, PM stack and DPMI_SEL). + + * src/dosext/dpmi/dpmi.c: + Changed the DPMI caps: we do not support the exception restartability, + and never did. But we do support device mapping - we can map LFB :) + +2004-11-23 Stas + + * src/dosext/dpmi/: dpmi.c, dpmi.h, msdos.c: + Make mouse, ps2 and XMS DPMI callbacks per-client. + + * etc/: dosemu.conf, global.conf: + Remove $_pm_dos_api from dosemu.conf. $_ems now implicitly controls + the API translator (as was discussed). + + * THANKS: + Japheth helped with 32rtm and Windows - thanks. + +2004-11-22 Bart + + * src/dosext/mfs/: lfn.c, mangle.c, mangle.h, mfs.c, mfs.h: + Removed name_ufs_to_dos call from name_convert, so the caller is responsible. + Often the caller did this already, and so we are saving double work this way. + Simplified LFN int21/71a8 to work directly on the DOS character set. + + * src/dosext/mfs/: lfn.c, mangle.c, mangle.h, mfs.c, util.c: + name_convert no longer uppercases. Leave this to the caller if it's + necessary, and if not it can use strcasecmp style functions. + + * src/dosext/mfs/mfs.c: + Moved find_again into a function instead of messing around with goto's. + + * src/dosext/mfs/mfs.c: + Must compare . and .. to the wildcard too. Fixes problem with Rocket Chase + + * src/: base/async/int.c, base/bios/bios.S, dosext/dpmi/msdos.c, + emu-i386/do_vm86.c, include/memory.h, include/mouse.h: + Use a HLT to deal with the hogthreshold code for real mode mouse drivers. + Correct EOI procedure in bios.S (used by IPX and packet driver) + +2004-11-22 Stas + + * src/dosext/dpmi/dpmi.c: + debug output. + + * src/dosext/dpmi/msdos.c: + Respect the returned CX value for int21/ah=65,al=1...7. + (suggested by Japheth) + +2004-11-22 Clarence + + * src/plugin/translate/translate_config.c: + newline + +2004-11-21 Bart + + * src/: base/async/int.c, dosext/dpmi/msdos.c, include/mouse.h: + Call int33 the old way using real_run_int() when it's called from a signal + handler. In this case DPMI's post extender takes care of the hogthreshold + code. + +2004-11-21 Stas + + * src/emu-i386/do_vm86.c: + Make sure do_call_back() never executed from within the signal + context. + + * src/base/async/int.c: + Disable int33 to DOS callouts. This allows windows to be started + in enhanced mode by win.com, otherwise crashes. + The reason is that do_int() can be called from within the signal + context (by DPMI). Calling realmode code from within the signal + context is not possible for many reasons (vm86() can emit the signals + itself, or DPMI may not yet set everything up for realmode code, etc). + So do_int() must *never* use the do_call_back() variations. + + * src/dosext/dpmi/msdos.c: + int21/ah=65h calls added to API translator. This allows Win3.11 + install to go further, but it still doesnt finish. + + * src/: dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, + dosext/dpmi/msdos.c, base/bios/bios.S, include/bios.h, + include/cpu.h, plugin/commands/builtins.c: + - Added XMS calls from DPMI thunk. This allows the Win3.1 installation. + Win3.11 installation still doesn't work. + - Some cleanups in msdos.c + + * src/: include/dos2linux.h, plugin/commands/builtins.c: + Forgotten bits of the previous commit. + + * src/base/async/int.c: + Introducing "Windows Hacks". + With these hacks it is now possible to: + - run Windows 3.1 in an usual way (via win.com) in both the 286 and 386 + mode. The mode will be displayed in the window title. + - install Windows 3.1 under dosemu. But the installation crashes + at the very end :( This is to work out later. + +2004-11-20 Stas + + * compiletime-settings.devel, src/arch/linux/async/signal.c, + src/arch/linux/async/sigsegv.c, src/arch/linux/debugger/mhpdbg.c, + src/base/async/int.c, src/base/dev/misc/timers.c, + src/base/dev/pic/pic.c, src/dosext/dpmi/dpmi.c, + src/dosext/dpmi/dpmi.h, src/dosext/sound/sound.c, + src/emu-i386/do_vm86.c, src/include/cpu.h: + First pass on the IF flag virtualization. + cpuemu is disabled for now - have to be updated somehow. + This patch fixes (probably partially) bugs: #1027805, #855948, #855556, + #811365 and the rest of the world. + Give it some testing, make sure it doesnt lock up you favourite DPMI app. + +2004-11-20 Bart + + * src/base/misc/fatfs.c: + Fixed problem trying to open io.syskernel.sys if io.sys has 0 bytes. + +2004-11-20 Stas + + * src/base/async/int.c: + int21/ah=6 is output only when dl!=0xff, otherwise it is input and + must not reset idle. (Bug #1067680) + +2004-11-18 Bart + + * src/include/bios.h, src/base/misc/disks.c: + For floppies call disk_close() only when the floppy MOTOR_TIMEOUT expired. + Otherwise call fsync(). This improves floppy performance, particularly when + using kernel 2.6. + + * etc/global.conf: + Fix typo in $_X_vesamode + +2004-11-14 Bart + + * src/: base/async/pci_bios.c, base/dev/misc/pci.c, include/pci.h: + Restrict PCI configuration space access to the first 64 bytes (16 longs). + Some machines crash otherwise. Thanks to MIchael and Reinhard Karcher for + reporting and fixing this. + +2004-11-12 Stas + + * etc/dosemu.conf: + IPX is presumed to be fixed and DPMI-safe. + +2004-11-11 Bart + + * src/: arch/linux/mapping/mapping.c, env/video/console.c, + env/video/vga.c, env/video/video.c: + Ignore PCI memory ranges under 1MB. Decouple memcheck_reserve from + map_hardware_ram for video memory. Fix console=1, graphics=0 mode, + by fixing its map_hardware_ram but still having a 128K chunk reserved + at 0xa0000 as virtual hardware RAM. + +2004-11-10 Bart + + * src/plugin/commands/builtins.c: + lowmem_alloc cannot call com_error in case of failure, since com_error + will call lowmem_alloc again (via com_doswrite). This fails and causes + infinite recursion. + +2004-11-09 Bart + + * src/base/async/int.c: + int33() now takes over the int33 vector if it points to an iret. This is still + a bit dirty but not worse than what we did before. I reverted the other + workarounds. + +2004-11-09 Stas + + * src/dosext/dpmi/msdos.c: + - Set MAX_DOS_PATH to 260 and use it instead of some hardcoded vals + - Use sprintf instead of memcpy for ax=7160 + + * src/dosext/dpmi/msdos.c: + Better to use MEMCPY_DOS2DOS macro + + * src/dosext/dpmi/msdos.c: + Silly strncpy vs memcpy confusion. + +2004-11-08 Bart + + * src/env/video/text.c: + Fix invisible mouse selection in colour text programs. This code is what + the Linux console selection code also uses to invert on VGA displays. + + * src/base/async/int.c: + Simplified int code a bit: moved some code from run_caller_func() to + do_int() to avoid checking things twice, and have only one place + where real_run_int() is called. The mouse workarounds aren't necessary + anymore; ctrl-c still works. + +2004-11-08 Stas + + * src/base/async/int.c: + If dosemu have no internal function for interrupt, just call the + DOS interrupt handler. This fixes the problem with ^C. + + * src/dosext/dpmi/dpmi.c: + Increase instremu count to 10 for LDT writes - looks a bit faster. + + * src/: base/async/int.c, dosext/dpmi/dpmi.c: + Revector interrupts 0x1c, 0x23 and 0x24 for DPMI. + This allows windows to handle critical errors like missing diskette + in drive. + + * src/base/async/int.c: + Dont call post_boot() anywhere but from int21/ah=4b and even then + ignore the first invocation. + The reason is that DOS sets int33 vector (and probably some other) + to iret on the very late stages of its startup. This hack makes + mouse to work again. + + * src/dosext/dpmi/dpmi.c: + - Fix bug with LDT segment expanding (hint from Japheth) + - This makes it possible to decrease the initial LDT limit to 0xfff + + * src/dosext/dpmi/msdos.c: + - Increased MAX_DOS_PATH: int21/ax=7160 needs 261 bytes. + - Added API translation for int21/ax=7160 (Japheth) + - Fixed bug in translation for int21/ax=7147 (Japheth) + + * src/: dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, + dosext/dpmi/msdos.c, base/bios/bios.S, base/mouse/mouse.c, + include/bios.h: + Add PS2 mouse support for DPMI. + This allows WinOS2 to work again (otherwise crashes with recent CVS), + but mouse no longer works :( + Maybe there are some bugs, but we need the config switch to choose + between int15 and int33 interfaces. + + * src/base/serial/ser_init.c: + Hi Bart:) + + * src/base/serial/ser_ports.c: + Revert the ser_ports.c patch of 2004-05-07. It seems to be no longer + necessary and causes problems. + +2004-11-05 Bart + + * src/include/serial.h, etc/global.conf, src/base/init/parser.y.in, + src/base/serial/ser_init.c: + Fix parser for $_com1 = "/dev/ttyS0 irq 4" (#1012035) + Don't try to remove a lock file for virtual com ports -- it just gives + a spurious ERROR. + Avoid some buffer overflows and NULL pointer dereferences + (sptr->dev is a pointer now, space for path determined at runtime, + take care of strrchr, remove unused dev_nam array) + + * src/: base/misc/ioctl.c, env/video/matrox.h, include/bitops.h: + gcc-4.0 warning/error fixes + + * src/: base/init/config.c, base/init/dev_list.c, base/init/init.c, + emu.c, include/emu.h: + Removed the remaining old keyboard code that was still around at various + places. + + * src/arch/linux/async/signal.c: + The sigreturn caller really needs to be called __restore and not restore__, + otherwise gdb will still be confused. + +2004-11-04 Bart + + * src/arch/linux/async/signal.c: + Set the SA_RESTORER flag when calling the kernel sigaction (for the wrapper + case), because glibc does the same thing. Hopefully that solves some problems + with GDB on 2.4 kernels. + + * src/: base/bios/bios.S, env/video/X.c, include/mouse.h, + include/pic.h, base/mouse/mouse.c, plugin/sdl/sdl.c, + plugin/term/mouse_xterm.c: + Handle user and int15 mouse event handlers via int74/irq12. + Some DOS mouse drivers hook int74 and only work if we handle events this + way. + + * src/base/mouse/mouse.c: + scale ps2mouse dy by a factor 2; the speed_x and speed_y are already changed + by a video mode switch. Also don't call do_mouse_irq_ps2 if no events occur. + ctmouse is now more usable without the grab active + (if you use ctmouse /r1 to disable acceleration). + +2004-11-03 Bart + + * src/plugin/: term/keyb_slang.c, translate/translate.c: + Compute the shift/alt/ctrl modifier for xterms/rxvt instead of relying on + a very long table that would cover all possibilities. + Assume high bit means ALT when the external charset is ASCII or "" with C + locale. Fix segfaults when $_external_char_set = "ascii". + +2004-11-03 Stas + + * src/emu-i386/do_vm86.c: + Remove the re-entry check from do_call_back(), IPX (Reinhard) have + problems with it. + do_call_back() must be reenterable, but I am worrying about the + case where it is re-entered by the VESA switch, after which + dosemu will be frozen while callback is processing. Probably the + VESA driver have to wait if any callback is active? + +2004-11-03 Bart + + * src/: base/async/int.c, base/mouse/mouse.c, base/bios/bios.S, + include/memory.h, include/mouse.h: + int33() now uses a callback into the mouse driver to make sure the + hogthreshold is done after the call even for DOS mouse drivers. + A DOS mouse driver can now redirect int33; in that case we use NOREVECT + for mouse_int(). To combine this with the callback via the hog code (which + is always revectored) some extra checks and function pointer adjustments + have to be made. + Removed the unused indirect function pointer for the mouse callback. + Enabling the internal driver no longer always sets the int33 vector. + Implemented a second mouse callback for the PS/2 mouse BIOS. + All this combined gives a PS/2 mouse bios that actually works so a + DOS mouse driver (such as CTMOUSE 1.9) can be loaded into xdosemu -- + although to have it usable one needs to grab the mouse. + +2004-11-02 Bart + + * src/doc/HOWTO/dosemu-HOWTO.sgml: + Removed any version information about Windows 3.x and dosemu. + This remark was merely a historical saying when the VGA emulator was + good enough to run Windows 3.x, it doesn't really matter today. + The 1.2.0 fixes were merely to get it back on track, nothing specific + to xdosemu vs. console dosemu. + +2004-11-02 Stas + + * src/doc/HOWTO/dosemu-HOWTO.sgml: + Rip some FUD/crap about Windows support from HOWTO: + - .386 doesnt mean 32bit. There is nearly no 32bit code in Win31. + - dosemu can run Windows since 1.2.0, not 0.64.3 (or it was broken + for ages, at least on 1.0.x it doesnt work). + - Icons do not disappear from Windows desktop, and winos2 doesnt + make progman.exe to use system.ini instead of progman.ini (who wrote + that kind of crap?) + - Some other Windows-related junk ripped. + + * src/dosext/dpmi/dpmi.c: + Support for unmodified Win31 386 kernel (thanks Japheth!): + - Dynamically growing LDT, initial limit set to 0x1fff. This + supercedes the allocation hack I've done for 286 kernel (hack removed). + - Check for eIP to not exceed the CS limit (CheckSelectors()) + - When Windows creates the invalid LDT entries, just free the + entry (type 0). This causes LAR to fail, same as if it would be of + a type 0x0f, which Windows uses. Bingo! + + And oh yeah, there are still no reasons to use the unmodified + Win31, at least before the proper mouse event translation is + implemented. + +2004-11-01 Bart + + * src/plugin/term/keyb_slang.c: + If only "20" function keys are available let "F11" appear as shift-F1 to + DOS apps. This is consistent with older dosemu's; and more DOS apps use + shift-F1 than F11 (because there was no F11 on XT keyboards). + +2004-10-31 Stas + + * src/plugin/commands/: builtins.c, builtins.h: + - Remove no longer used com_int86() + - Duplicate some "builtins" error message to the DOS screen + +2004-10-31 Bart + + * src/plugin/term/keyb_slang.c: + keyb_slang.c cleanups and enhancements: + * Use our own definitions only as fallbacks for the terminfo definitions. + Some definitions could be removed because the terminfo is reliable + enough. + * Added logic to translate esc-key into alt-key so many definitions could + be removed from the table. + * Added some entries for shift/ctrl/alt arrow keys etc in xterm and friends + * Add support for kf1--kf48 as used by recent xterms. Makes it possible + to distinguish shift-f1 from f11 and use ctrl-f1 etc. + * Force application keypad mode to be able to distinguish keypad keys from + others. + +2004-10-31 Stas + + * src/dosext/dpmi/dpmi.c: + Remove some leftover debugging stuff. + + * src/: dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, + dosext/dpmi/memory.c, dosext/dpmi/msdos.c, + arch/linux/debugger/mhpdbgc.c, env/video/instremu.c: + Win3.1 support: + - Added LDT R/W emulation. + - Added several tricks that Windows relies upon. + - Static DPMI initialization is now done only once, in dpmi_memory_init(). + + This allows unmodified Win3.1 to work, but only krnl286.exe so far. + krnl386.exe is not yet. + There are no advantages in running unmodified windows, compared to WinOS2, + only the disadvantages, like that you cant use mouse under X (don't even + try WinOS2 mouse driver under unmodified winkernel, it will crash!). + But it is an imminent step towards win32s, Win95 etc... + +2004-10-30 Bart + + * etc/dosemu.conf, src/plugin/translate/translate_config.c: + $_term_char_set is obsolete (it really has been for over two years now). + +2004-10-30 Stas + + * src/dosext/dpmi/: dpmi.c, dpmi.h: + Enlarge PM locked stack to 0xf000 as needed for Windows. + Also be more carefull calculating the stack offsets. + +2004-10-30 Bart + + * src/plugin/kbd_unicode/keyb_raw.c: + Fix gcc warning. + +2004-10-30 Stas + + * src/: env/video/instremu.c, env/video/vgaemu.c, include/vgaemu.h: + Add the "cnt" arg to instr_emu(), which specifies how many insns + to emulate. 0 means default count. When cnt!=0, signal_pending is ignored. + +2004-10-30 Bart + + * src/plugin/: term/terminal.c, translate/charsets/vt100.c: + Optimize terminal->utf8 support to use a max of 3 bytes: DOS characters + never get beyond the BMP. + Implement support for VT100 drawing characters; add this character set. + Exploit cp437 as the alternate character set on the Linux console. + This makes the Use_IBM_Codes escape sequence trick superfluous, and + allows us to even use most IBM characters < 0x20. + +2004-10-30 Stas + + * src/base/async/int.c: + Set vector 0x66 to iret because Win3.1 calls it and crashes otherwise. + + * src/dosext/dpmi/dpmi.c: + Make FreeAllDescriptors() to actually work. + + * src/: base/mouse/mouse.c, base/async/int.c, include/emu.h, + plugin/commands/emumouse.c: + - Make emumouse to call the mouse_helper() directly, rather than via + DOS interrupt. + - Fix the bug with permanently disabling mouse internaldriver. + + * src/: arch/linux/async/signal.c, base/async/int.c, + base/bios/bios.S, base/init/init.c, dosext/mfs/mfs.h, + dosext/net/net/ipx.c, dosext/net/net/ipxglt.c, include/bios.h, + include/cpu.h, include/doshelpers.h, include/emu.h, + include/ipx.h, include/pic.h: + IPX updates: + - Switch to async IO + - Route all the callback stuff via PIC + - Removed all the horrible asm hackery from bios.S, it is all in C now + + This was tested and benchmarked by Reinhard - thanks! + Benchmarks shows that it is not faster than the old code, but the + CPU load was dropped from 100% to 10%, which is very good. + It also should not have the problems with DPMI, but this remains + unconfirmed. We'll see. + +2004-10-27 Bart + + * src/plugin/kbd_unicode/keyb_raw.c: + The raw keyboard needs to switch back to the original mode when you quit: + it may not be XLATE, but can be UNICODE as well. + +2004-10-27 Stas + + * src/arch/linux/async/signal.c: + Calling io_select() in sigalrm() is still necessary for dosdebug. + +2004-10-27 Bart + + * src/arch/linux/Makefile.main: + Removed some obsolete advise about xmodmap and xset fp rehash. + +2004-10-26 Bart + + * src/env/video/text.c: + Don't use the remapper unless bitmap fonts are used. This fixes a crash in + some text mode programs in terminal mode or when an X font is used. + + * src/dosext/dpmi/: dpmi.c, msdos.c: + Removed more lvalue casts. + + * src/plugin/kbd_unicode/include/keyboard.h: + Added symbol for Altgr lock which is used in the console keyboard detection. + + * src/plugin/kbd_unicode/keymaps.c: + Use a more generic approach to scan for alternate keyboards than using the + KT_ALTERNATE flag. + The console keyboard detector can now deal with alternate layouts, + non-ISO-8859-1 symbols and Unicode symbols (if kbd_mode -u is used). + +2004-10-25 Bart + + * src/plugin/kbd_unicode/keymaps.c: + Detect alternate keymaps by checking keys 2 and 3 from the X server. + Fixes the problem with setxkbmap us,ru + +2004-10-25 Stas + + * src/dosext/net/net/pktnew.c: + Supress error messages. + +2004-10-24 Bart + + * src/plugin/kbd_unicode/keymaps.c: + Only report a mismatch if the X server reports a valid key which is different + from our valid key. This seems to fix both "uk" and "ru" autodetection. + + * src/dosext/dpmi/dpmi.c: + Fix gcc warning, remove unnecessary PAGE_SIZE definition. + + * src/plugin/kbd_unicode/keymaps.c: + Clean up auto detection to use the "X_keysym" character set and to not + compare too many keys. + +2004-10-23 Bart + + * src/: base/init/parser.y.in, plugin/kbd_unicode/keymaps.c, + plugin/kbd_unicode/serv_xlat.c, + plugin/kbd_unicode/include/keymaps.h: + The console keyboard auto detection now merges with the US def, instead + of replacing it. + Changed $_layout="auto" to no longer blindly trust the console keyboard + when running under X. A function was borrowed from Wine which compares + the X keymap with all keymaps that we have (including perhaps the console + keymap) and choose the one that matches best (as there is no way to + obtain scancodes from X; X terminals may not be PCs after all). + Hopefully fixes bug #999831. + + * src/dosext/mfs/mfs.c: + Don't use asprintf() in exists() as there may not be enough space + (fullname can become longer in find_file as names are unmangled). + This caused a very nasty SIGSEGV when trying to delete a mangled name. + Change to cheaper allocation on the stack with NAME_MAX+1 bytes for + the filename component. + + * src/dosext/mfs/mfs.c: + Converted chmod error into a warning; also used for utime() now. + It now causes DOSEMU to ignore the request (not return an error), + but only if errno==EPERM (not e.g. EROFS), the directory is writable, + and chmod would actually change the mode. + + * src/dosext/mfs/mfs.c: + convert_compare() now checks if the name has a ~ or ? at position 5; + otherwise we don't need to mangle. + Simplified extract_filename(), and auspr(): they may assume valid filenames. + Convert * to ? before searching, so we don't need to check '*' in the + comparison routines anymore. + + * Makefile: + Removed the automatic autoconf rule again; it appears to have more drawbacks + than advantages. Sometimes configure.ac changes only the autoheader part + so that configure does not change and autoconf is not necessary (only + autoheader). + +2004-10-22 Bart + + * src/env/video/vgaemu.c: + Fix wrong comparison for character heights. This fixes many fonts in + graphics modes. + + * src/plugin/: sdl/Makefile, kbd_unicode/Makefile: + Clean some generated files with make distclean/realclean. + + * src/plugin/sdl/sdl.c: + Enable key repeat in SDL + +2004-10-21 Stas + + * src/arch/linux/async/signal.c: + Dont trust the "old" action struct, it may contain not what we want. + This patch allows njamd to be used with dosemu, btw. + + * src/include/pic.h: + Revert patch of 11.10.2004. + Reinhard pointed the stupidity of it, and the fact that pic_unmaski() + takes care about that automagically for more than a year already, + makes it even not worth fixing. + + * etc/dosemu.conf, src/dosext/net/net/pktnew.c: + I tested $_novell_hack, it appears to work (for my test-case at least). + So the comment in the code that said it was disabled (that I propagated + to dosemu.conf) appeared to be misleading. Removed. + +2004-10-20 Bart + + * src/dosext/drivers/cdrom.c: + Check for ENOTTY when doing the cdrom ioctl before reading. This allows + using image files with cdrom.sys and *cdex.exe. + +2004-10-20 Stas + + * src/dosext/net/net/pktnew.c: + Dont try to init pkt when disabled in config. + + * src/dosext/mfs/mfs.c: + Mounting FAT without "quiet" causes many obscure problems for dosemu. + + * src/dosext/net/net/pktnew.c: + Return two accidentally removed strcpy() calls to packet driver... + +2004-10-19 Bart + + * src/arch/linux/Makefile.main: + Do the final link using a recursive invocation of make. That seems + to avoid the problems with parallel makes, because this rule can + now reliably depend on a phony target. + +2004-10-18 Bart + + * src/env/video/vbe.c: + removed protected mode bank switch caller again, because we don't handle + cli and sti for it. + +2004-10-18 Stas + + * src/: include/inifile.h, dosext/net/net/ipx.c: + Really really remove the dos.ini support. + + * src/dosext/net/net/inifile.c: + Remove dos.ini again (escaped from previous commit). + + * configure.ac, etc/dosemu.conf, etc/global.conf, + src/base/init/lexer.l.in, src/base/init/parser.y.in, + src/dosext/net/net/Makefile, src/dosext/net/net/ipx.c, + src/include/config.h.in, src/include/emu.h: + - Removed never used (not even mentioned in docs) dos.ini file support. + - Added $_ipx_network option to dosemu.conf (was in dos.ini previously). + +2004-10-18 Bart + + * src/dosext/mfs/lfn.c: + Fix semicolon typo. Should solve Clarence' problem. + +2004-10-17 Bart + + * src/dosext/mfs/: mangle.h, mfs.c, util.c: + Use a dos_to_unicode_table to complement unicode_to_dos_table and make + path_to_ufs faster. + + * src/dosext/mfs/lfn.c: + Fix possible buffer overflow. + + * src/dosext/mfs/mfs.c: + For findfirst delay the filename compare and stat to findnext's. + This makes get_dir() so much faster (70x) that the sigalrm check is no + longer necessary. + Also moved the . and .. in root dir check out of the loop in + scan_dir. + + * src/dosext/mfs/: lfn.c, mangle.c, mangle.h, mfs.c, util.c: + Reverted VFAT optimization. It's not reliable for say \`o (o with accent + grave) where the uppercase equivalent does not exist in CP437. The kernel + does not uppercase it but DOS and DOSEMU convert it to O. So searching + for POT where the filename is p\`ot means the expensive way is necessary. + Introduced a DOS uppercase table to be able to do this quickly, and + let the LFN code use this. Simplify name_ufs_to_dos to no longer do any + uppercasing as strupperDOS does this much more quickly. Some cleanups + in scan_dir to make it more similar to what it was before. + +2004-10-16 Bart + + * src/dosext/mfs/mfs.c: + Simplify last part of loop: the strcasecmp for long and short names could + be the same thing for a non-mangled name on a non-FAT fs. + + * src/dosext/mfs/: mangle.c, mfs.c: + Fixed a regression in mangle.c with the unicode->dos table. + Use the stack instead of malloc in name_convert. + Only mangle all names in the directory if what we are looking for + is a mangled name in the first place! Otherwise a simple strcasecmp + is sufficient. That's another 12x faster for scan_dir(non_mangled_name). + On VFAT (case insensitive fs) we don't need to scan at all. + + * src/: dosext/mfs/lfn.c, dosext/mfs/mangle.c, dosext/mfs/mfs.c, + dosext/mfs/util.c, plugin/translate/translate_config.c, + plugin/translate/config/plugin_parser, + plugin/translate/include/translate.h: + Use current locale for the external filename character set (leaving + keyboard/terminal/paste untouched for now). Allows simply using + strcasecmp instead of slow unicode compare, and mbsrtowcs for the + unix->dos filename translation. Gives a further 30% speed improvement + of readdir searches. + + * src/dosext/mfs/mangle.c: + Use a 64K-sized char table for unicode->DOS lookups. This makes the + readdir searches about 3x faster. + +2004-10-15 Bart + + * src/plugin/kbd_unicode/keyb_clients.c: + Add the SDL keyboard to the keyb_clients struct. + + * src/env/video/X.c: + Properly handle TIMESTAMP requests. Now klipper doesn't cause bad events + anymore. + +2004-10-14 Bart + + * src/env/video/X.c: + Implement support for the TARGETS and TIMESTAMP atoms used in selection. + This fixes the copy/paste problems with KDE 3.2.3. + +2004-10-14 Stas + + * src/: arch/linux/debugger/mhpdbgc.c, emu-i386/do_vm86.c: + Dont unfreeze dosemu when dosdebug is attached, do it only if + explicitly asked via dosdebug. + +2004-10-13 Bart + + * src/arch/linux/mapping/mapping.c: + Allow virtual maps of the video memory at 0xa0000 if !config.console_video + + * src/: arch/linux/mapping/mapping.c, base/init/config.c, + base/init/init.c, base/init/parser.y.in, dosext/dpmi/dpmi.c, + env/video/vbe.c, env/video/vc.c, env/video/vga.c, + env/video/vgaemu.c, env/video/video.c, include/emu.h, + include/mapping.h, include/pci.h: + Extend $_hardware_ram to allow maps over 1MB with a virtual base that + DPMI can get. Implement a new set of hardware RAM functions (register, + map, list, get) and make $_hardware_ram, VGA(PCI), DPMI, and VESA its + users. A special type (0) is used for virtual mappings (used by VGAEMU); + otherwise the memcheck character types are used. + map_hardware_ram tries to map everything that was registered, including + the low VGA display memory. + + * src/dosext/mfs/mfs.c: + Speed up scan_dir by uppercasing the name to search for outside the loop + and only comparing the mangled short name for SFN searches. + LFN searches only compare the long name case insensitively. + Use VFAT_IOCTL_READDIR_SHORT on vfat partitions from non-LFN code to + make sure that LFN's are invisible when they aren't needed. + Plug memory leak and page fault if you lredir'ed a non-existent + directory. + +2004-10-13 Stas + + * src/include/emu.h: + Making config.hogthreshold (and ocasionally a couple of other vars) + signed - needed for my upcoming hogthreshold updates (using the + negative thresholds sometimes). + + * src/: arch/linux/async/signal.c, base/async/int.c: + Moving io_select() from sigalrm to do_periodic_stuff() speeds up the + packet driver twice. + + * src/env/video/X.c: + Set the real resolution for fullscrean, not the scaled one. + +2004-10-12 Bart + + * etc/dosemu.conf, src/arch/linux/async/signal.c, + src/base/init/lexer.l.in, src/env/video/Makefile, + src/env/video/vbe.c, src/env/video/vbe.h, src/env/video/vga.c, + src/env/video/vga.h, src/include/emu.h, src/include/vc.h, + src/include/video.h: + Add a VESA VBE graphics driver and make that the default. This driver + should work with any VBE version; an LFB is not necessary + LFB availability is only possible on PCI boards right now. + If (say) your PCI VGA BIOS only supports VESA 1.2 then it should even be + possible to load a TSR such as UNIVBE in DOSEMU and the driver will use + that. + + Provide a way to force reentry into handle_signals() so that we don't + miss too many SIGALRMs whilst saving the video memory (a thread would + perhaps be a nicer solution). + +2004-10-12 Stas + + * src/dosext/net/net/pktnew.c: + Complain if the pkt driver is not able to handle the bandwidth. + I was not able to trigger that, so I need to know it some queueing + should be implemented or not. + + * src/include/pic.h: + Allow PIC to process EOI from pkt and mouse. They no longer need any + special treatment. + + * src/dosext/net/net/pktnew.c: + With bound socket we dont need that hack any more. + + * src/env/video/X.c: + Restore dotclock rate on switching from fullscreen to windowed mode, + otherwise the very bad things happen. + +2004-10-11 Bart + + * src/: base/async/int.c, base/bios/bios.S, emu-i386/do_vm86.c, + emu-i386/ports.c, env/video/vga.c, include/bios.h, + include/port.h: + Call int10/ax=mode for PLAINVGA before restoring registers. This helps + when the BIOS is buggy or we don't restore enough registers ourselves; + otherwise it doesn't hurt. The BIOS needs a check not to mess with + the mouse driver. + From Stas: introduce in_crit_section to make sure that the BIOS survives + the callback. Allow callbacks when "frozen". Some small cleanups. + +2004-10-11 Stas + + * src/dosext/net/net/: libpacket.c, libpacket.h, pktnew.c: + Packet driver networking updates: + - Use PF_PACKET/SOCK_RAW sockets instead of the deprecated and buggy + AF_INET/SOCK_PACKET ones, changed the interfaces accordingly. + - Put the sockets in a bind state - this allows to use the Async IO, + which makes the direct NIC access mode ~50x faster. + - Use simple read/write for IO - this is possible with the binded + RAW sockets (not with the PACKET sockets though - they insist on + sendto() even when binded, which is a bug). + - Some large cleanups. + +2004-10-10 Stas + + * etc/dosemu.conf, src/dosext/net/net/libpacket.c, + src/dosext/net/net/pktnew.c: + Direct NIC access no longer requires root all the time, only at + startup now, similar to dosnet. + It looks like this was the last thing that required root all the time. + +2004-10-09 Stas + + * src/base/serial/ser_ports.c: + Fix another bug with IIR clearing. (part of #1042020) + +2004-10-09 Bart + + * src/: include/vc.h, env/video/Makefile, env/video/vc.c, + env/video/vga.c, env/video/vga.h, env/video/video.c: + vga.c cleanups and changes: + * made lots of functions static, moved out of vga.h + * use constant PLANE_SIZE of 64K; always save 256K in standard VGA modes + * moved the state save to the end of the video init (init_vga_card) + and don't let it clear the saved Linux video memory (from Stas) + * improved diagnostics + vc.c: repaired dosemu -c + + * src/plugin/kbd_unicode/serv_xlat.c: + Dead keys should not cause the display of question marks. + + * src/plugin/term/: keyb_slang.c, terminal.c: + Make DOSEMU compatible with SLang 2.0. + + * src/emu.c: + Don't call pcibios_init() in emu.c anymore as it's done elsewhere now. + +2004-10-09 Stas + + * src/base/serial/ser_irq.c: + Fix IIR clearing. (part of #1042020) + + * src/base/serial/ser_ports.c: + Fix the problem introduced by the previous patch... + +2004-10-09 Bart + + * etc/dosemu.conf, etc/global.conf, src/base/async/pci_bios.c, + src/base/dev/misc/pci.c, src/base/init/parser.y.in, + src/env/video/matrox.c, src/env/video/vga.c, src/include/pci.h, + src/dosext/dpmi/dpmi.c: + * cleaned up pci_bios.c<->pci.c interface (Stas) + * pci_bios.c now registers i/o and memory areas for all PCI devices + * the real PCI interface can no longer be iopl(3) optimized because + of the port server + * don't allow byte access to port 0xcf9 (can reset the CPU) + * implemented a read-only PCI emulation interface based on the values + found at init. This only applies to selected PCI devices (eg VGA) + * use PCI emulation by default with console graphics if PCI is available, + unless $_pci=(on) + * allow I/O to all ports that PCI reports about VGA (closes #687024) + * changed DPMI fn 0x800 to report about mmap'ed PCI memory areas, so + that LFB's work in the console + * removed now obsolete $_console_pci and $_console_ports + +2004-10-08 Bart + + * src/env/video/console.c: + If the Linux console uses fbcon we can force a complete text redraw + by doing two (round-trip) vc switches; otherwise (vgacon) it doesn't hurt. + + * src/: env/video/vc.c, env/video/vga.c, include/vc.h: + Only reprogram the graphics and sequencer registers like they are in + VGA mode 12 when saving/restoring video memory. Skipping the CRT registers + is sometimes a little easier on the eye, when fbdev causes + dosemu_vga_screenoff() not to do its job. + +2004-10-07 Bart + + * src/emu-i386/do_vm86.c: + 0x66 0xef should be "out dx,eax", NOT "out edx,eax". + +2004-10-03 Stas + + * src/base/init/parser.y.in: + Do not fallback to "direct" for unknown vnet mode, just abort the dosemu. + Wrong configs should not be allowed. + + * src/dosext/net/net/pktnew.c: + Do not fallback to dynamic TAP allocation if the requested static + allocation fails. + + * src/base/misc/smalloc.c: + smalloc: It turned out safer to fail on zero-sized mallocs, rather than + to do nothing. Otherwise people expect realloc() to work on the pointers + returned by malloc(0). + + * etc/dosemu.conf: + Increased LFB size to 4Mb under X. + + * etc/dosemu.conf, src/base/init/parser.y.in, + src/dosext/net/net/libpacket.c: + - Enabled Packet Driver by default. + - Clarified the pkt-related options in dosemu.conf. + - $_vnet="direct" added - same as $_vnet="". + +2004-10-02 Stas + + * src/base/serial/ser_ports.c: + Dont reset "data ready" before the receive queue is empty. + (Should fix #1029819) + +2004-09-30 Bart + + * src/dosext/mfs/mfs.c: + Include lfn.h (compiler warning) + +2004-09-29 Bart + + * src/dosext/misc/xms.c: + Removed some unnecessary #includes and function. + + * src/: dosext/misc/xms.c, env/video/vesa.c, include/vesa.h: + Split off VESA definitions into a global header file. + Always use the "aggressive" way to find UMBs (this is really the clean + way, the real difference for umb_max is in video.c). + For this to work the VGAEMU BIOS has to be registered though (in vesa.c). + + * src/arch/linux/mapping/mapping.c: + Allow kmem mmaps with non-fixed targets. + +2004-09-27 Stas + + * src/: include/smalloc.h, base/misc/smalloc.c: + Made smget_area_size() signed. + + * src/dosext/dpmi/dpmi.c: + Install the realmode callback trampoline only when program allocates + the callback. This fixes some extremely buggy program. (Bug #1033901) + + * src/: dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, emu-i386/do_vm86.c: + More accurate realmode callback address calculations. + + * src/base/misc/smalloc.c: + smalloc: smget_area_size() should return -1 on failure, since 0 can be valid + (found by megath@users.sourceforge.net) + + * src/base/misc/smalloc.c: + smalloc: realloc(NULL, size) should work as malloc(size), rather than to fail. + Bug found by megath@users.sourceforge.net. + I was sure there are no bugs in smalloc... but dosemu never uses that trick. + +2004-09-26 Bart + + * src/base/mouse/mouseint.c: + Don't (un)freeze the mouse if there is no mouse, because if we do that + we get a SIGSEGV. + + * src/env/video/console.c: + Don't clear the Linux console screen for $_graphics=(1). This will happen + later anyway. + + * src/env/video/svgalib.c: + Remove (now) useless #undef SVGALIB_C + +2004-09-26 Stas + + * src/base/async/int.c: + - NULL checks in xtitle code. + - Replaced !strlen(str) with !str[0] - cosmetic. + + * src/emu-i386/ports.c: + Make dosemu to work again. + +2004-09-25 Bart + + * etc/dosemu.conf, etc/global.conf, src/base/init/lexer.l.in, + src/base/init/parser.y.in, src/emu-i386/ports.c, + src/include/port.h: + - export trace_ports from global.conf to dosemu.conf + - ports are now fast by default unless they are mentioned in $_trace_ports + - using explicit "fast" or "slow" it is possible to force the ioperm + independently of any tracing set -- based on patch #1019824 + +2004-09-24 Bart + + * src/dosext/mfs/: lfn.c, lfn.h, mfs.c: + LFN: only explicitly close findfirst handles for ax=71a1, not if no more + files are found. For unknown filehandles, chain through to DOS to give + DOSLFN a chance if that is loaded. + A process termination hook closes all findfirst handles connected to + the PSP that is terminated. + +2004-09-23 Stas + + * src/dosext/dpmi/msdos.c: + Fix 2 silly bugs in msdos_pre_exec() noticed by Japheth (Bug #1028236) + +2004-09-22 Bart + + * src/arch/linux/async/signal.c: + Remove the signal wrapper wrapper again -- it may not work with some old + glibc's. So may be worse after all than simply bypassing the wrapper. + Now detect if glibc installs a wrapper, and if it does, use the kernel + sigaction instead to force the kernel to call our handlers directly. + + * src/dosext/mfs/lfn.c: + LFN: don't return . and .. in findfirst/next for root directories. + don't clobber the getcwd dest more than absolutely necessary. + small cleanup of /. removal -- this is already done by truename now + +2004-09-16 Stas + + * src/base/misc/smalloc.c: + Cosmetic optimization for smalloc. + +2004-09-15 Bart + + * src/env/video/X.c: + Fix X.c to choose a better fitting resolution if (say) both 320x240 and + 320x200 modes are defined in XF86Config. + + * src/: base/bios/bios.S, dosext/mfs/lfn.c, dosext/mfs/mfs.c: + Fix two bugs in the LFN support as per #1020635: + * do not remove embedded spaces before dots and leading spaces. + * fix interface between int21/ax=716c and int21/ax=6c00 to only transform + a "create" into an "open" if a file was really created, not if it already + existed, and to set cx to "created" (2). + +2004-09-15 Stas + + * src/base/misc/smalloc.c: + Small improvements and optimizations to smalloc. + +2004-09-14 Stas + + * src/plugin/commands/builtins.c: + com_dosallocmem()/com_dosfreemem() are no longer necessary. + Why lowmem_alloc()/lowmem_free() are still there is because the + builtins used to have many memory leaks and other mem violations. + So allocating a private 2K pool for them is safer than to allow + them to allocate from the main 32K pool. At least for a time. + + * src/: base/bios/bios.S, base/init/init.c, base/misc/Makefile, + base/misc/lowmem.c, include/lowmem.h, include/memory.h, + plugin/commands/builtins.c: + - Shuffled the bios.S a bit to get a 32Kb of low memory for free. + - Added a management for it - lowmem.[ch]. This is needed for VESA + support. + - Since the VESA support is not there yet, the usual victim is builtins, + which are moved to use that heap instead of the on-fly DOS allocation + that may be not always safe. + +2004-09-13 Stas + + * src/: arch/linux/mapping/mapfile.c, base/misc/Makefile, + base/misc/pagemalloc.c, dosext/dpmi/memory.c, + include/pagemalloc.h: + smalloc replaces pagemalloc. The advantages: + - Allocations are not limited with the page-granularity. + - Doesnt require garbage-collection and therefore probaly has fewer bugs. + - Much simpler and smaller code (4K vs 12K). + + * src/: base/misc/Makefile, base/misc/smalloc.c, + base/misc/zalloc.c, env/video/matrox.c, env/video/vga.c, + include/smalloc.h, include/zalloc.h, plugin/commands/builtins.c: + Introducing the new memory allocator - smalloc. It is intended to + replace both zalloc and pagemalloc and being suitable for the VESA + driver. + - Replaced zalloc allocator by smalloc. smalloc is only 4K of code, + compared to 12K of zalloc. The advantages over zalloc: + -- Has realloc() (smrealloc) + -- Has normal free(), which doesn't require to specify the length to free. + -- Code is much smaller and almost as simple as of zalloc. + + * src/: dosext/mfs/mfs.c, emu-i386/do_vm86.c, include/emu.h, + plugin/commands/builtins.c, plugin/commands/builtins.h, + plugin/commands/commands.c, plugin/commands/dosdbg.c, + plugin/commands/lredir.c, plugin/commands/unix.c, + plugin/commands/xmode.c: + Fix dozens of memory violations in builtins, mostly detected by the + smalloc memory allocator. The fixes include (but not limited to the) + following: + - Cease the usage of the low mem considerably by calling the helpers + directly. + - Protect do_call_back() against re-entering. This was happening + when eg. cmdline is interrupted by ^C, it was not able to recover. + - Track all the allocations of load_and_run_DOS_program() to free it + on termination (leaks). + - On dos_read()/dos_write() set int0x23 to our return point, so that + dosemu can recover even in case of ^C. + - Fix com_strfree() to free the region of a proper size. + - dos_helper_r is no longer necessary. + - com_dosread() was not checking the size specified by caller - + memory corruption. + - memory corruption with builtin_name, no longer use DOS mem for it. + - etc... + + * src/emu.c: + Fix gcc warning. + + * src/include/shared.h: + shared.h must die. + + * src/include/emu.h: + Video memory size should not be of a short type. + +2004-09-11 Bart + + * VERSION, configure, configure.ac, src/plugin/commands/generic.S: + Make generic.S backwards compatible with older DOSEMUs so we can easily + use DOSEMU 1.2 & 1.3 at the same time. Bump version code to 1.3.1.1 + so that generic.S can do a runtime check. Remove obsolete pthread + comment in configure.ac (forces reconfigure). + +2004-09-10 Bart + + * src/: emu-i386/do_vm86.c, arch/linux/async/signal.c: + Force restoration of GS before signal handlers but only if + the libc uses signal wrappers and GS != 0. + Also restore flags/fs/gs after a vm86 call to work around a bug + in 2.4 kernels. + The implementation is hackish -- any beautification improvements + are welcome, but it seems to work. + + * src/: include/emu.h, arch/linux/async/signal.c: + Move common sigaction calling code into one static function + (dosemu_sigaction_wrapper). + + * src/: emu.c, arch/linux/async/signal.c, include/emu.h, + base/misc/ioctl.c, emu-i386/simx86/cpu-emu.c, env/video/vc.c, + plugin/term/terminal.c: + Convert ADDSET_SIGNALS_THAT_QUEUE, SETSIG, NEWSETSIG, and NEWSETQSIG from + macros to functions. + +2004-09-10 Stas + + * etc/dosemu.conf: + $_oss_stalled_frags=(2) - this enables aoss to work. + + * src/emu.c: + Dont close mouse twice, doing that in iodev_term() is enough. + +2004-09-09 Stas + + * src/: include/cpu.h, arch/linux/async/signal.c, + dosext/dpmi/dpmi.h, emu-i386/cpu.c: + Optimization: ucodesel/udatasel are unnecessary, use _emu_stack_frame + instead. + + * src/: arch/linux/async/sigsegv.c, dosext/dpmi/dpmi.c: + Prevent DPMI from trashing the entire LDT at startup! This makes + pthreads much happier, and it was a horrible bug overall. + +2004-09-08 Stas + + * src/: arch/linux/async/signal.c, include/emu.h: + SIGWINCH was hijacked and no longer works for console switching. + Changed to USR1 - now the console switching works again. + + * src/: base/mouse/mouseint.c, env/video/vc.c, include/mouse.h: + closing/reopening mouse on console switch doesnt work: we need to call + DOSEMUMouseSetup() (at least) after opening, but we dont. + It is better to not close it at all, just halt reading it should be enough. + + * src/plugin/kbd_unicode/keyb_raw.c: + set_shiftstate() in raw_keyboard_init() causes NULL deref. + + * src/env/video/: svgalib.c, svgalib.h, vga.h: + There is no conflict anymore between and "vga.h" so svgalib.c + can include both and svgalib.h can have the extern defs removed. + +2004-09-04 Stas + + * src/dosext/dpmi/msdos.c: + From japheth@users.sourceforge.net: + Support for the following LFN functions added to PM API translator: + 3b, 41, 4e, 4f, 47, 6c, a1. + + * src/dosext/dpmi/msdos.c: + Stop assuming 0 being in some regs and converted the mess to use the + prominent macros. + +2004-09-02 Stas + + * src/plugin/commands/: builtins.c, detect.h, generic.S: + dosemu builtins locks up the machine if started under pure DOS. + Fixed detect.h and used it to prevent that. + +2004-09-01 Stas + + * src/plugin/commands/builtins.c: + typo. + + * src/dosext/dpmi/dpmi.c: + A little more info on exception. + + * src/plugin/commands/: Makefile, builtins.c, builtins.h, + generic.S, lredir.c, config/plugin_config.h, + config/plugin_inte6.h: + Builtins plugin work: + - Recursive invocations (unix -r et al) were not handled properly: + every new invocation allocated the new mem pool, making the previous + pool unaccessable. Now only one pool is allocated. Refcounting added + to know when the pool is safe to destroy. + - The above is an incompatible change for generic.S. Introduced the + builtins interface versioning. Now if dosemu detects the outdated + version of builtins, it will not run them and will ask user to upgrade. + - Removed the code that was intended to allow calling builtins with + far call. This code was never used and was broken since generic.S + started to release memory at startup. + - Moved DOS version checking from builtins.c to lredir.c. + + * src/base/serial/ser_init.c: + Unlink the stalled lockfiles for serial and some debug messages cleanup. + + * src/emu.c: + Remove dummy ign_sigs sighandler for leavedos() - our main sighandler + is now mature enough to handle that, and also this will allow to get + the stack traces from within the leavedos. + +2004-08-30 Stas + + * src/: arch/linux/async/signal.c, emu-i386/do_vm86.c, + plugin/kbd_unicode/serv_8042.c, + plugin/kbd_unicode/serv_backend.c: + - Completed the unbinding kbd code from PIC recursion. + - keyb_server_run() must now be called more frequently, so moved from + sigalrm handler to loopstep_run_vm86(). + + * src/plugin/sdl/sdl.c: + Please use -Wdeclaration-after-statement gcc option. + + * src/base/serial/: ser_irq.c, ser_ports.c: + Dont try to clear FIFO when it is not enabled (bug #1012035). + +2004-08-29 Bart + + * etc/global.conf: + Fix problem with in global.conf with $_term_char_set. + + * src/base/misc/Makefile: + Simplify the Makefile for src/base/misc. + + * src/base/misc/Makefile: + Fix possible compilation problem (#1007380) that happened in some + circumstances. + +2004-08-28 Bart + + * configure, configure.ac, etc/global.conf, + src/plugin/term/terminal.c: + Allow the utf8 patched slang to be linked in. + Use "default" (mbrtowc and wcrtomb, following LC_CTYPE) for the output + charset unless $_external_charset is specified. + Switch the console to cp437 mode only if "linux" is in $TERM and the + external character set is single-byte. + The terminal code can now work with multibyte output character sets + (including utf-8), if the patched slang (and hopefully slang 2.0 when it's + released) is used. + + * src/plugin/translate/translate_config.c: + Use the default (multibyte) charset for the unix_charset instead of + manually playing with nl_langinfo. + + * src/plugin/translate/charsets/: multibyte.c, utf8.c: + Fix a bounds check that was the wrong way around in multibyte.c. + Fix unicode to utf8 conversion for 1- and 2- bytes sequences. + + * src/: arch/linux/async/signal.c, plugin/term/terminal.c: + Move SIGWINCH sig init to the terminal init function and correct a + problem where ctrl-alt-fn didn't work on the console with rawkeyboard + in terminmal mode. Also made sure that vga.text* are set to the right + values for non 80x25 terminals. + +2004-08-15 Stas + + * src/: plugin/kbd_unicode/serv_8042.c, env/video/video.c: + Repair $_speaker="native": + - It should not use fast port access, it should go via portserver + - Dont always disable it, the bug with killing linux is already fixed. + + * src/: base/bios/bios.S, plugin/kbd_unicode/serv_8042.c, + plugin/kbd_unicode/serv_backend.c: + Attempt to unbind the keyboard code from the PIC recursion. + +2004-08-15 Bart + + * src/plugin/sdl/sdl.c: + Add support for resizing windows with SDL + + * Makefile.conf.in, compiletime-settings, + compiletime-settings.devel, etc/global.conf, + src/base/init/config.c, src/base/mouse/mouse.c, + src/base/mouse/mouseint.c, src/env/video/X.c, + src/env/video/text.c, src/env/video/video.c, src/include/mouse.h, + src/include/vgatext.h, src/include/video.h, + src/plugin/kbd_unicode/include/keyb_clients.h, + src/plugin/sdl/Makefile, src/plugin/sdl/Makefile.conf.in, + src/plugin/sdl/acinclude.m4, src/plugin/sdl/configure, + src/plugin/sdl/configure.ac, src/plugin/sdl/keyb_SDL.c, + src/plugin/sdl/mouse_SDL.c, src/plugin/sdl/sdl.c, + src/plugin/sdl/sdl.h, src/plugin/sdl/config/plugin_config.h.in: + Added SDL plugin, based on the initial patch by Emmanuel Jeandel. + Mostly functional, tested for X and fbdev. + Start dosemu with the -S switch to use it. + Still missing: dynamic window resizing, ctrl-alt-f/home, keyboard repeat, + OpenGL/Overlay optimizations, PC speaker support, copy/paste, xtitle. + +2004-08-14 Stas + + * src/: base/bios/bios.S, dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, + dosext/dpmi/msdos.c, emu-i386/do_vm86.c, include/bios.h: + Allocate realmode callbacks on a client's data area. This allows the + client to allocate all the 16 callbacks (and avoids any limitations + on the amount of clients), while still being able to use those of its + parents parents parents + +2004-08-14 Bart + + * src/: env/video/X.c, env/video/render.c, include/render.h: + Introduce new function (get_mode_parameters() in render.c), which translates + the vgaemu mode parameters into a convenient display mode. + + * src/: env/video/X.c, env/video/remap.h, env/video/vgaemu.c, + include/vgaemu.h, plugin/kbd_unicode/keyb_X.c, + plugin/term/terminal.c: + Move vgaemu_display_type fill-in of fields from X.c to vgaemu.c (vga_emu_init) + + * src/: env/video/X.c, env/video/text.c, include/vgatext.h: + Move resize_text_mapper() from X.c to text.c. + + * src/: env/video/X.c, env/video/render.c, include/render.h: + Move the graphics update-related functions from X.c to render.c. + Simplify the put_ximage interface as src_x/y are always equal to dest_x/y + the way we call it. + +2004-08-13 Bart + + * src/base/dev/misc/lpt.c: + Fix printer code problem by making all fops members static and the public + printer_* functions wrappers. + + * src/: arch/linux/async/signal.c, env/video/X.c, env/video/text.c, + include/video.h: + Removed X_blink_cursor (use blink_cursor() in text.c instead) and made the + call independent of config.X. + +2004-08-11 Stas + + * src/dosext/net/net/pktnew.c: + Small cleanup. + + * src/dosext/dpmi/msdos.c: + Disable VXDLDR: some DOS progs think they can use it to load vxd + and crash. + +2004-08-09 Stas + + * src/base/dev/pic/pic.c: + Misplaced bracket in do_irq() caused problems for console mouse + driver (probably also in 1.2). + + * src/: base/dev/misc/rtc.c, base/dev/misc/timers.c, + base/dev/pic/pic.c, base/init/init.c, base/misc/ioctl.c, + base/mouse/mouse.c, base/serial/ser_defs.h, + base/serial/ser_irq.c, dosext/net/net/pktnew.c, + dosext/sound/sound.c, include/emu.h, include/iodev.h, + include/mouse.h, include/pic.h, include/pktdrvr.h, + plugin/kbd_unicode/serv_8042.c, + plugin/kbd_unicode/include/keyb_server.h: + - Removed global variable pic_ilevel. ilevel is now passed as an argument + to IRQ handlers. This is another step to upcoming removal of the recursion + from PIC. + Note: This change may yield bugs! + - Fixed packet driver to work with that technique. Other drivers must be + carefully tested (well, whats the use, they'll be broken again anyway + when the recursion is removed:). + + * src/base/dev/pic/pic.c: + Set ISR bits only in do_irq(). We don't need to clear them in + run_irqs() then, which makes it easier to remove recursion from PIC + without fixing all the drivers that produce the spurious interrupts. + +2004-08-08 Stas + + * src/emu-i386/do_vm86.c: + Small cleanup. + + * src/: env/video/Makefile, env/video/X.c, env/video/render.c, + env/video/text.c, include/render.h, include/vgatext.h, + plugin/term/terminal.c: + Introduce render.c and render.h which form the middle layer between + SDL and X and the remapper. One global remap_obj (hopefully a file scope + static later) is defined in render.c. + + * src/dosext/dpmi/memory.c: + Allocate 5 extra pages for DPMI pool to work around the possible + memory fragmentation and potential pagemalloc bugs. + + * src/base/misc/pagemalloc.c: + pagemalloc was refusing the last page. + + * src/dosext/dpmi/dpmi.c: + Dont touch hi words of %eax/%ebx in dos memory functions of DPMI. + (Bug #1004472) + + * src/dosext/dpmi/dpmi.c: + Inherit the realmode callbacks on DPMI client creation. (Bug #1005015) + + * src/base/serial/ser_ports.c: + Dont tcdrain() after transmitting the char as slows the transfer. + There might be better ways to avoid queueing. + +2004-08-07 Bart + + * src/: arch/linux/async/signal.c, arch/linux/async/sigsegv.c, + base/bios/int10.c, env/video/X.c, env/video/console.c, + env/video/dualmon.c, env/video/hgc.c, env/video/video.c, + include/video.h, plugin/term/terminal.c: + Eliminated is_mapped field in Video, using checks whether the + update_screen function pointer is NULL instead. + Since update_screen now always goes through vgaemu we can use this to + check for vgaemu instead of config.X. + Add handle_events function pointer. + +2004-08-01 Bart + + * src/: arch/linux/async/signal.c, arch/linux/async/sigsegv.c, + base/bios/int10.c, base/mouse/mousevid.c, + emu-i386/simx86/interp.c, emu-i386/simx86/sigsegv.c, + env/video/X.c, env/video/X.h, env/video/text.c, + include/vgatext.h: + Remove many X_GRAPHICS and X_SUPPORT #ifdefs that are no longer necessary. + Move (X_)set_textsize from X.c to text.c + +2004-07-31 Bart + + * src/: env/video/X.c, env/video/text.c, plugin/term/terminal.c: + Solve link problem when X is enabled with have_focus. + + * src/: emu-i386/ports.c, env/video/Makefile, env/video/miscemu.c, + env/video/remap.h, env/video/vgaemu.c, include/vgatext.h, + plugin/term/terminal.c: + Use vgaemu for the terminal code and unconditionally compile vgaemu support. + +2004-07-30 Bart + + * src/arch/linux/mapping/mapfile.c: + Fix memory leak in case shm_open fails. + + * configure, configure.ac, src/arch/linux/mapping/mapfile.c, + src/arch/linux/mapping/mapping.c, src/include/config.h.in: + Use shm_open and shm_unlink(). This is the new default for $_mapping = "auto" + (tmpfile is the last resort); if it works out well we can remove mapshm.c later. + + * src/: emu.c, arch/linux/async/signal.c, include/emu.h: + Allocate the signal stack on DOSEMU's stack instead of allocating it + statically. Now it is possible to link with Linuxthreads without + crashing, because the thread handling code is no longer confused + about the thread id of the stack it is using. + + * configure, src/base/mouse/gcursor.c, src/base/mouse/mouse.c, + src/base/mouse/mousevid.c, src/base/mouse/mousevid.h, + src/include/config.h.in: + Rename current_video to mouse_current_video, because of a namespace conflict + with SDL. + +2004-07-29 Bart + + * src/: env/video/X.c, include/vgatext.h, env/video/text.c: + Move conversion from text map to bitmap in bitmap_draw_string() from X.c + to text.c. + +2004-07-27 Bart + + * src/env/video/Makefile: + Adjust env/video/Makefile to text.c split. + +2004-07-26 Bart + + * src/: include/vgatext.h, env/video/X.c, env/video/text.c: + (Originally by Emmanuel Jeandel) Split most of the X-independent + text mode functionality from X.c. That will make it easier to + implement SDL support. + +2004-07-22 Stas + + * src/arch/linux/async/: signal.c, sigsegv.c: + - Invoking gdb in cleanup_child() is useless + - Invoke gdb in dosemu_fault() before printing an exception info, + because printing exception info can sigsegv itself. + +2004-07-21 Stas + + * src/arch/linux/dosext/sound/linux_sound.c: + Write some diagnostic when DSP open fails. + +2004-07-19 Stas + + * src/base/dev/misc/lpt.c: + reset_idle() when printing, and some cleanups. + + * src/dosext/dpmi/memory.c: + Fix return value check. + + * src/dosext/dpmi/: dpmi.c, dpmi.h, memory.c, msdos.c: + Attempt to separate committed and uncommitted memory handling on a + DPMI level. DPMI spec says "Resize Linear Memory Block" (0x505) cannot + be used on a block allocated with "Allocate Memory Block" (0x501), but + only on a block allocated with "Allocate Linear Memory Block" (0x504). + That helps. + + - DPMImalloc() splitted into DPMImalloc() and DPMImallocLinear() + - DPMImallocFixed() merged into DPMImallocLinear() + - DPMIrealloc() splitted into DPMIrealloc() and DPMIreallocLinear() + - "Resize Linear Memory Block" can now create uncommitted pages. + +2004-07-17 Stas + + * src/: dosext/dpmi/dpmi.c, arch/linux/async/sigsegv.c, + dosext/dpmi/dpmi.h: + - Make CheckSelectors() to not terminate dosemu, but just to return + whether the selectors are valid or not. The caller have to decide + what to do. + - Dont CheckSelectors() in direct_dpmi_switch() and indirect_dpmi_switch(). + Do it in dpmi_control() instead, which is within dosemu context. + + This all results in that dosemu now drops to dosdebug when encounters + the invalid selector. A bit easier to debug. + + * src/dosext/dpmi/dpmi.c: + Check stack segment against being read-only. + +2004-07-16 Stas + + * src/dosext/dpmi/msdos.c: + Clear higher word of %ebx when returning the DTA pointer. + (bug #991194, Japheth) + +2004-07-15 Bart + + * src/env/video/X.c: + Add bound checks for text cursor drawing. Fixes #990235 + +2004-07-14 Stas + + * src/dosext/dpmi/vxd.c: + Fixed bug in VTD VxD (timer). It turned out that dos4gw, at least the + one from Tenberry, uses this VxD when available, so some games were + screwed up with the timing. + + * src/emu-i386/do_vm86.c: + Put the DPMI IF hacks exactly where they used to be in dpmi.c, to avoid + regressions. + + * src/: base/dev/pic/pic.c, dosext/dpmi/dpmi.c, emu-i386/do_vm86.c: + Merge the real-mode part of run_dpmi() into run_vm86(). + This allows to remove the "main loop" from dpmi_init(), using the + loopstep_run_vm86() also for DPMI. + There is yet another "main loop" in do_irq(), but that would be more + difficult to remove. + Note: this is a moderately intrusive change. + + * src/: arch/linux/mapping/Makefile, arch/linux/mapping/mapfile.c, + arch/linux/mapping/mapping.c, arch/linux/mapping/pagemalloc.c, + arch/linux/mapping/pagemalloc.h, base/init/init.c, + base/misc/Makefile, base/misc/pagemalloc.c, dosext/dpmi/dpmi.c, + dosext/dpmi/dpmi.h, dosext/dpmi/memory.c, include/pagemalloc.h: + Use pagemalloc for DPMI allocations since the kernel no longer meets the + expectations of DOS progs. See comments in memory.c for details. + This makes GTA to work again. + What a mess in fact... + This cannot be done effectively via mapping system because of the problems + with uncommitted memory. + This cannot be done via zalloc because zalloc lacks realloc. + This patch does: + - Make pagemalloc to manage multiple pools, and move it to src/base/misc + - Allocate a memory pool for the committed DPMI allocations and manage it + with pagemalloc. Uncommitted allocations, even after being committed, are + still handled separately. This probably cannot be generalized. + +2004-07-12 Stas + + * src/base/misc/: fatfs.c, fatfs_boot.S: + Make it possible to terminate dosemu by "any key" when no OS to boot. + + * src/base/misc/fatfs.c: + Specify the directory where's the OS files are expected to be. + +2004-07-11 Stas + + * src/doc/HOWTO/EMUfailure.sgml: + Updated for 1.2.2 (hi Bart :), removed outdated things because of the + following events: + - Windows (winos2) runs fine + - Protected mode "make" runs fine since 1.2.1 + - DPMI call 0x506 is implemented (in -devel only so far) + - Bitmap fonts backported + - Keen4 works, you just need the proper version of it. + (please regenerate docs) + +2004-07-11 Bart + + * ChangeLog: + Update ChangeLog for 1.3.1 + + * NEWS, dosemu.spec.in: + Added one more NEWS entry. Adjust dosemu.spec.in. + + * README, THANKS, doc/EMUfailure.txt, doc/announce, + etc/dosemu.conf, man/dosemu.1.in, man/dosemu.bin.1.in, + src/arch/linux/Makefile.main: + Some minor documentation updates. + + * src/: arch/linux/async/signal.c, base/misc/ioctl.c: + Block all async signals before signal_init. Avoids that the portserver will + catch any -- set_process_control is also called from raw_keyboard_init. + +2004-07-10 Stas + + * src/dosext/dpmi/dpmi.c: + (AllocateSpecificDescriptor): we dont need this anymore. + + * src/: base/mouse/mouse.c, base/mouse/mouseint.c, include/mouse.h: + Dont enable mouse driver before post_boot. + Dont process mouse events when driver is disabled, to avoid crash. + Init mouse.speed_x and mouse.speed_y early to avoid divizion by zero + at startup. + + * NEWS: + Some more news worth to mention. + +2004-07-10 Bart + + * ChangeLog.old, NEWS, ChangeLog, VERSION: + Renamed old ChangeLog to ChangeLog.old + The new ChangeLog is now a more GNU-style changelog based on the raw commit + dates on log messages so we can track more easily when a specific change + was made. + User visible changes go into NEWS. + + * src/base/async/int.c: + reset idle for int21 functions that output + + * src/: emu.c, emu-i386/ports.c, include/emu.h, + arch/linux/async/signal.c, base/misc/ioctl.c: + Block SIGIO until signal_init() is called. That's the easiest way to + avoid problems... + This makes it possible to avoid all the new extra code in ports.c. + While I was at it I could remove some related old cruft. + + * src/env/video/vc.c: + Limit memcpy for VC text mode switching to 32k. Avoids crashes if + more than 25 lines are used. + +2004-07-09 Bart + + * src/emu-i386/ports.c: + Set all signals that dosemu changed either to a default or ignore + handler. Unblock all of them. + + * src/dosext/mfs/mfs.c: + Check for failed lseeks before reads and writes (may happen if the file + offset is negative). Thanks to Jan Zuchhold + +2004-07-08 Bart + + * src/arch/linux/mapping/mapshm.c: + We need string.h for memcpy. + + * src/: base/bios/int10.c, env/video/miscemu.c, env/video/vgaemu.c: + Fix two vgaemu regressions: + fonts were wrong in 640x480 and 640x350 VGA graphics modes + colours were wrong in these modes too + Fixes some new issues with STDEMO. + + * src/: emu.c, emu-i386/ports.c: + Fix for #984975. Easiest was after all to to do signal init before + deviceinit and port server fork. Then block *all* signals in the + port server. + +2004-07-06 Bart + + * src/: emu-i386/ports.c, include/cpu-emu.h: + Remove i/o port restrictions for 0x400, asking ioperm instead. + Will help when the kernel supports a larger bitmap. + + * src/base/bios/int10.c: + Adjust int10 CGA palette support to do brightness correctly. + Now and` old CGA pacman is yellow instead of brown as it should be. + + * src/: include/vgaemu.h, base/bios/int10.c, env/video/miscemu.c, + env/video/vgaemu.c: + Implement CGA register 0x3d9 emulation + BIOS support. Fixes colours + in bug #958717 + +2004-07-05 Bart + + * src/env/video/vgaemu.c: + Correct screen height for CGA modes (part of #958717) + + * configure, src/include/config.h.in: + Regenerate configure and config.h.in + +2004-07-04 Stas + + * src/dosext/dpmi/: dpmi.c, msdos.c: + Small fixes and cleanups from the failed Win3.1 support (R/W LDT is + completely dropped): + - Never allocate the system descriptors for client (UCODESEL, UDATASEL) + - Set ldt_buffer and pm_stack to NULL after free() + - Zero out the entire client struct on DPMI init, rather than some of + its parts. + - Dsable NULL-ptr fixing code again. + + * configure.ac, src/dosext/dpmi/Makefile, src/dosext/dpmi/dpmi.c: + We want windows. + (please re-run autoheader and autoconf) + +2004-07-04 Bart + + * src/base/bios/int10.c: + Fix || which should be && of course to determine if we use a text or + a graphics mode. Fixes #982872 + + * src/base/mouse/mouse.c: + Only init the xterm mouse once or you'll get a lot of traffic! + Closes #959015. + + * etc/vga.bdf, src/env/video/X.c, src/env/video/vgafonts.c: + The XCHAR kludge to translate 0 to 0x20 (' ') is only necessary for + vga.pcf, not bitmap fonts. I've corrected vga.pcf but since people may + use a different vga.pcf than ours (IIRC Debian has a seperate vgafonts + package) I'll leave the kludge for non-bitmap fonts for the time being. + In any case this fixes the zx_emul vertical bar problem (#911174) + +2004-07-03 Stas + + * src/dosext/misc/emm.c: + EMM fixes: + - handle name is 8 bytes ASCII, but dosemu stores it as ASCIIZ, + therefore 9 bytes must be reserved. + - Fix usage of NULL_PAGE, it is not a handle. + +2004-07-02 Stas + + * src/dosext/dpmi/dpmi.c: + OK, but then the ms-linker stops to work again... + Well, with this hack they both work, but no guarantee something + else is not broken (this is an obscure case anyway). + + * src/dosext/dpmi/dpmi.c: + Be more conservative about fixing %esp. Apparently there are the DPMI + clients that put esp above the TASK_SIZE (somewhere to 0xffffff80) and + work perfectly (unless I "fix" %esp). No idea how they work, but... + +2004-06-28 Bart + + * etc/global.conf: + Ignore empty "drives" directories for $_hdimage="drives/*". + +2004-06-27 Stas + + * src/dosext/dpmi/msdos.c: + Re-enabled NULL-ptr fixing in msdos.c. This is required by the unmodified + Win3.1 kernel (not WinOS2 fortunately). + + * src/dosext/dpmi/dpmi.c: + Properly check for selectors allocation limit. Unmodified Win3.1 requests + too many descriptors which crash dosemu. + + * src/: dosext/dpmi/vxd.c, dosext/dpmi/Makefile, + dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, dosext/dpmi/msdos.c, + dosext/dpmi/vxd.h, base/bios/bios.S, include/bios.h: + VxD hooks for WinOS2. + This seems to solve all the remaining problems with Win3.1, namely: + - Alt-Tab now works (SHELL VxD) + - Timer now works (VTDAPI VxD) + + Note: win32s support is disabled for now - it needs some porting efforts and + I have lost my test-cases for it. + Note: Only protected mode entry points are supported. WinOS2 doesnt seem to + be using the real-mode entry points, so I have not added those. + + It seems Win3.1/WinOS2 works perfectly now as far as I can tell, at least + similar to how it works under QDPMI. + +2004-06-26 Stas + + * src/dosext/dpmi/msdos.c: + Dont trash %esi if cwd failed. + +2004-06-22 Stas + + * src/dosext/dpmi/dpmi.c: + Make the error message to work again. + + * src/dosext/dpmi/dpmi.c: + Refer to our docs, not to external. + + * src/dosext/dpmi/memory.c: + DPMIfree() & co must account only the committed pages. +Some cosmetic. + +2004-06-21 Bart + + * src/env/video/s3.c: + + From Reinhard: + There is a small problem with s3.c, so I can't use it with $_console=1 and + $_graphic=1. (thanks to Michael) + +2004-06-12 Bart + + * src/env/video/vgaemu.c: + Hercules port range patch from Ryan Underwood. + + * etc/dosemu.conf, src/plugin/extra_charsets/bg-mik.c, + src/plugin/extra_charsets/cp895.c: + Add cp895 (Czech/Slovak) (ZUB@atlas.cz) + Add bg-mik character set (Ivo Tachev, ivotachev@mail.bg) + +2004-06-11 Bart + + * src/arch/linux/dosext/sound/midid/timid.c: + I really have to make seqbuf_dump non-static to avoid the gcc warning... + +2004-06-09 Stas + + * src/arch/linux/dosext/sound/midid/timid.c: + Fix gcc warning. + + * src/: dosext/dpmi/dpmi.c, dosext/dpmi/dpmi.h, env/video/vc.c, + include/emu.h: + - Move _emu_stack_frame to emu.h - it must be globally available. + - Restore fs/gs in console sighandlers - this way dosemu can survive the + console switch on a NPTL-enabled systems. + - Set NO_VC_NO_DEBUG to 0 - it only eats debug info. + + * src/arch/linux/async/debug.c: + cat maps for the reason this time. + +2004-06-07 Bart + + * src/env/video/instremu.c: + From: dcoffin@cybercom.net + To: bart@dosemu.org + Subject: Incorrect bitshifts in instremu.c + + Hi Bart, + + My fiancee really loves playing her old DOS games + on DOSEMU, so thanks for the hard work! + + Running my Scrabble game in DOSEMU, I saw visual + defects. After many hours of debugging, I traced the + problem to instremu.c. + + My code does "shl al,cl", which should set AL to + zero when CL is 8. But DOSEMU treats the 8 as though + it were a zero. No x86 CPU behaves this way. + + According to the MASM reference: + + "On the 80186-80386 processors, shift counts larger than + 31 are masked off, but on the 8088 and 8086, larger shift + counts are performed despite the inefficiency involved." + + I experimentated with the other shift and rotate + commands, and then made a patch to imitate the behavior + of 80186 and later CPUs. RCL and RCR are especially + interesting... + Dave Coffin 6/3/2004 + +2004-06-03 Stas + + * src/plugin/kbd_unicode/serv_8042.c: + Allow booting with $_xms=(off). + dosemu always maps HMA, regardless of the $_xms setting. This check does + nothing but prevents dosemu from booting with $_xms=(off). + Unfortunaltely Win98/DOS hates our XMS driver, so possibility to disable + it, must work. + + * src/: arch/linux/debugger/mhpdbgc.c, base/data.c, + base/dev/misc/lpt.c, base/misc/hma.c, base/misc/ioctl.c, + dosext/dpmi/dpmi.c, dosext/misc/xms.c, dosext/net/net/pktnew.c, + emu-i386/do_vm86.c, emu.c, env/video/vc.c, include/dosio.h, + include/hma.h, include/xms.h, plugin/kbd_unicode/serv_8042.c: + - dosio.h must die, use hma.h instead + - small cleanups in hma.c + - use separate a20_local and a20_global in xms.c + + * src/arch/linux/async/debug.c: + The more debug info the better. + +2004-05-31 Bart + + * src/dosext/mfs/mfs.c: + Avoid fixed sized buffer and strcpy/strcat in exists() + +2004-05-30 Bart + + * src/env/video/X.c: + Use vga.seq.addr_mode == 2 instead of the number of planes to check for + chain4 modes of planes. This fixes the Windows logo... #962683 + + * src/arch/linux/mapping/mapshm.c: + For realloc_mapping_shm we can't expand shared anonymous memory using mremap + so we must allocate a new region and memcpy to it. Fixes a problem with + WP 5.1's use of EMS + + * src/base/bios/int10.c: + The int10 BIOS now determines whether a mode is text or graphics from + the byte at 40:49. Also reduce use of global co and li by peeking these + values from 40:xx. Eventually co and li should be completely removed. + +2004-05-29 Bart + + * src/base/init/parser.y.in: + Change stop_mouse c_printf to be a little more legible. + +2004-05-29 Stas + + * src/dosext/dpmi/msdos.c: + - Rewrote old_dos_terminate() as per suggestions in bug #909015 + - Disable fix-NULL-deref code as per suggestion in RFE #937984 + +2004-05-29 Bart + + * src/env/video/: X.c, vgaemu.c: + Removed line_compare==0 workaround, since the CRTC init now initializes it + correctly from the CRTC registers. Have to check at a few places in X.c + to compensate. This fixes some display problems with bananoid. + + * src/env/video/X.c: + Fix another scan_len case: fixes problems with resizing zx_emul. Only the + bars remain. + +2004-05-28 Bart + + * src/env/video/X.c: + Force a redraw for bitmap text modes upon resizing. This helps for plain + text modes but not for funny text modes such as the one zx_emul uses. + + * src/emu-i386/ports.c: + Replace kernel 2.6 test as agreed upon during the last IRC + +2004-05-25 Bart + + * src/env/video/crtcemu.c: + Fix occasional wrong display or even page fault after an int10 mode change. + +2004-05-24 Stas + + * src/dosext/dpmi/memory.c: + Two use-after-free bugs in uncommitted mem code :( + +2004-05-22 Bart + + * src/env/video/X.c: + Fix hang when non-bitmap fonts were used. + + * src/env/video/: X.c, vgaemu.c: + Decouple scan_len from co. Sometimes we must use scan_len instead of co... + Since zx_emul has co=40 but scan_len=48 this is necessary. + Now zx_emul displays everything correctly except for some annoying vertical bars + I couldn't get rid off. + + * src/env/video/: X.c, vgaemu.c: + Correct vgaemu height calculations and relax limits on columns on lines + (as long as the product*2 is within 64k) + + * src/env/video/: X.c, seqemu.c, vgaemu.c: + Respect sequencer for determining 8- or 9- pixel wide characters. + X_update_text_screen should respect a changed display and resize. + + * src/: base/bios/int10.c, env/video/vgaemu.c: + Text modes in X now update from the aliased memory (high up in the Linux + address space), instead of 0xa0000-0xc0000; will be necessary to use + instremu for text modes. Corrected one related screen_adr assignment. + + * src/dosext/dpmi/dpmi.c: + Use syscall() for modify_ldt. Not sure if this fixes the Fedora problem yet + but it is consistent with the other syscalls anyway. + + * src/: base/serial/ser_ports.c, arch/linux/async/sigsegv.c, + dosext/dpmi/dpmi.c, dosext/dpmi/memory.c, include/Linux/serial.h: + Avoid the direct inclusion of kernel headers that seem to creep in + by using private copies. + + * src/base/init/parser.y.in: + Reverted Stas' mouse change with respect to the parser. $_mouse_dev="" should + not disable the mouse for X/terminal/gpm, only for console. + +2004-05-20 Bart + + * src/: include/vgaemu.h, env/video/crtcemu.c, env/video/vgaemu.c: + Implement CRTC readonly flag (thanks to the dosbox source code for the hint :-P) + Fixes the myth bug --only 72 out of 200 lines were displayed (#930572) + Stas please close if you can confirm. + +2004-05-20 Stas + + * src/base/serial/ser_ports.c: + Fix transmit IRQ triggering code of ser_ports.c. Closes bug #944346. + +2004-05-19 Bart + + * INSTALL, src/emu-i386/ports.c: + Added sanity check to see if catching port i/o works. + +2004-05-18 Bart + + * src/dosext/net/net/ipxglt.c: + ipx_interface really should get the same treatment as ipx_route... + +2004-05-10 Stas + + * src/base/: mouse/mouseint.c, init/parser.y.in: + Dont try internaldriver if $_mouse_dev="" + +2004-05-09 Stas + + * src/: include/mapping.h, arch/linux/mapping/mapping.c, + arch/linux/mapping/mapshm.c: + Dont use mapshm if MREMAP_FIXED is not available (this was implicitly + checked together with mremap(,0,...), but not any longer). + +2004-05-08 Bart + + * configure, configure.ac: + Respect CFLAGS as set by the user. + + * etc/global.conf: + Repair $_cpu="emulated" + +2004-05-08 Stas + + * src/arch/linux/async/debug.c: + asprintf() is a GNU extension and needs _GNU_SOURCE before + +2004-05-08 Bart + + * etc/global.conf: + Add missing endif. + + * configure.ac, default-configure, configure, + src/base/bios/Makefile, src/env/video/Makefile: + Don't use CFLAGS for the 16bit BIOS asm files. The -g in there confused + binutils/gdb and that was the reason for the -gstabs+. Now we can just + use the standard dwarf-2 debug info in dosemu. + Also default-configure must check for gpm, not libgpm in compiletime-settings. + + * etc/global.conf: + Remove warning that wasn't any more ... + + * etc/global.conf: + Correct new problem with $_chipset + +2004-05-07 Bart + + * src/arch/linux/async/: Makefile, debug.c, debug.h, signal.c, + sigsegv.c: + Added Stas' auto-gdb patch. I changed two small things: avoid buf[255] + (potential buffer overflow), and also output what "ldd" says. + getconf GNU_LIBC_VERSION must be quite recent (GLIBC 2.3+ I think) by + the way, doesn't work here with 2.2.4 + getconf: Unrecognised variable `GNU_LIBC_VERSION' + That doesn't hurt though. + + * etc/global.conf, src/base/init/parser.y.in: + Limit use of strlen in global.conf; move com port processing into a loop. + For some reason foreach doesn't always play nicely with if :( + Don't define a serial port in the parser with an empty device ("virtual" + still works though) + Please check if this doesn't break your favourite configuration. + +2004-05-07 Stas + + * src/base/serial/: ser_init.c, ser_irq.c, ser_ports.c: + Next round of serial low-latency work: disable the internal timers. + Now, with the async notifications, we can sync with the real thing instead. + This gains 20sec improvement here at burning a flash in MC. + + * src/base/serial/ser_ports.c: + Remove wrong IRQ triggering code from ser_ports.c. Part of bug #944346 + +2004-05-05 Bart + + * src/: include/video.h, base/init/parsglob.h, + base/init/parser.y.in: + Hopefully last bunch of gcc-3.4 adjustments. Compiles cleanly and works now. + + * configure, configure.ac: + gcc 3.4 doesn't like -mcpu= anymore. Use -mtune= instead. + + * src/dosext/misc/emm.c: + Avoid lvalue casts. + + * src/dosext/dpmi/dpmi.c: + Avoid lvalue casts... What a mess, probably push/pop style macros would be + cleaner... + + * src/: emu-i386/do_vm86.c, include/cpu.h: + Avoid more deprecated casts. Not sure if prefix66 ^ prefix67 is the right + thing for ecx... Have to look that up. + + * src/emu-i386/cpu.c: + Fixed GCC 3.3.3 warning: + cpu.c:154: warning: use of conditional expressions as lvalues is deprecated + cpu.c:154: warning: use of cast expressions as lvalues is deprecated + + * src/emu-i386/cputime.c: + Don't zero trigger1 after usleep. + +2004-05-02 Bart + + * src/: base/async/int.c, base/bios/int16.c, emu-i386/cputime.c, + include/timers.h: + Consolidated all idling calls that use a trigger into one common function + with a common trigger. All triggers now reset after each usleep(). Also + the MFS resets the trigger. There may be more places where that is useful. + + * src/env/video/: X.c, remap.c: + reconfig.mem should become 0 again after redrawing the text screen. + fixed an x-offset bug in remap.c: now X.c can properly draw rectangles of + texts, and not just screen-wide blocks. + The combination of these two changes makes bitmap fonts much faster. + +2004-05-01 Bart + + * src/arch/linux/mapping/mapshm.c: + From Stas: use MAP_SHARED | MAP_ANONYMOUS instead of IPC. + +2004-04-30 Bart + + * src/: arch/linux/mapping/mapping.c, env/video/vc.c: + Repair !have_mremap_fixed and related vc code. + +2004-04-29 Bart + + * src/base/async/int.c: + Correct int15/ax=e801. + +2004-04-29 Stas + + * src/dosext/dpmi/memory.c: + Fix return value check. + + * src/: arch/linux/mapping/mapping.c, env/video/vc.c: + - Make MAPPING_COPYBACK to work for MAPPING_KMEM and MAPPING_LOWMEM in all + cases. + - Map lowmem instead of unmapping KMEM - this makes the interfaces more + consistent. + +2004-04-26 Bart + + * src/dosext/net/net/ipxglt.c: + From Michael Karcher: also check /proc/net/ipx/route for kernel 2.6 + +2004-04-25 Stas + + * src/: include/emu.h, plugin/kbd_unicode/prestroke.c: + Fix memory corruption in keystroke handling + cleanups. Bug #909588. + +2004-04-16 Stas + + * src/emu-i386/cpu.c: + Fix some in_dpmi misuses. Noticed by japheth@users.sf.net, bug #934921. + +2004-04-14 Stas + + * src/: arch/linux/mapping/mapping.c, dosext/dpmi/memory.c: + Fix mprotect size, cosmetic. + +2004-04-12 Bart + + * src/plugin/term/keyb_slang.c: + Get rid of the "inconsistency in define key" warning message where it's + harmless. + + * src/env/video/: vc.c, vga.c: + Fix a couple of small new bugs encountered in the cleaned up video code. + +2004-04-11 Bart + + * src/: arch/linux/mapping/mapping.c, env/video/vc.c, + include/mapping.h: + Mostly from Stas: use COPYBACK cap to avoid a temporary buffer when + flipping between /dev/mem memory and low memory. + Bart: clean up a bit, use size_t instead of off_t (off_t is a signed + type used for file offsets!) and remove the root checks (if extended + mremap is available). + + * src/: include/vc.h, env/video/console.c, env/video/vc.c, + env/video/vga.c, env/video/video.c: + Clean up vc get/put video ram routines. It should now be clear how and + where to apply Stas' patch. + + * src/: emu.c, env/video/X.c, include/video.h, + plugin/term/terminal.c, env/video/console.c, env/video/dualmon.c, + env/video/hgc.c, env/video/video.c: + Introduced a priv_init field for the video system. priv_init (for now) calls + the old console driver init procedure. init calls the console driver + post_init and for the terminal and X the normal init. Hopefully this doesn't + break too much -- terminal and X still work. + +2004-04-08 Bart + + * src/dosext/mfs/lfn.c: + Implemented our own LFN filematch routine, with a little help from + GLIBC. Special trick is to add a period if it's not there so that: + dir *. + matches all files without an extension. + log and log? match log (==log.), but log?? doesn't match. + + * src/dosext/mfs/lfn.c: + LFN: wildcard delete and findfirst/next need to match both sfn's and lfn's! + -- in Win98 "dir *1.*" will give you almost all LFNs. + Next stage will be to dumb down fnmatch with a custom version. + +2004-04-07 Stas + + * src/dosext/dpmi/dpmi.c: + Sanity check triggers false-positives. + +2004-04-06 Stas + + * src/include/emu.h: + Restart syscalls after EINTR. Should fix #885692. + + * src/: dosext/dpmi/dpmi.c, emu-i386/cpu.c, include/emu.h: + Dont allow client stack to run above the dosemu stack. This is a loosy + attempt to work around a ESP CPU bug. Seems to get the ancient MS linker + to work. (bug #929123) + + * src/include/emu.h: + linux-2.6 needs SA_NODEFER to allow the recursive signal handling. We need + 2 nesting levels of SIGSEGV sometimes. (part of bug #929123) + +2004-04-02 Stas + + * src/dosext/dpmi/dpmi.c: + Dont print debug info twice. Also avoids crash when trying to dump the + content of an uncommitted page after a Page Fault. + + * src/: base/async/int.c, base/bios/bios.S, dosext/dpmi/dpmi.c, + dosext/dpmi/msdos.c, dosext/dpmi/msdos.h, include/doshelpers.h, + include/int.h, include/memory.h: + - Added wrappers for DOS I/O (int21/ah=0x3f,0x40) to allow read/write >64K. + - Added helpers to pass data between the high and low mem. + - PM API Translator adjusted to use the new wrappers for I/O operations. + - READ_DS_COPIED no longer needed, removed. + This patch allows 32rtm to work. This is a noticeable step - 32rtm allows + to run win32 console apps (PE). + Many thanks to japheth@users.sourceforge.net for locating so many problems + and providing a usefull hints. + +2004-03-30 Bart + + * src/: dosext/mfs/lfn.c, dosext/mfs/mfs.c, dosext/mfs/util.c, + include/dos2linux.h: + LFN updates: ported FreeDOS' truename function to be able to handle + int21/ax=7160 better, and to avoid escaping the drive letter "sandbox". + "...." = "..\..\.." etc work too. + Just like dir c:\foo\.. when foo does not exist (another DOS oddity). + +2004-03-29 Bart + + * src/emu-i386/ports.c: + Remove the munmap(low memory) in the portserver. It no longer + makes sense. + +2004-03-29 Stas + + * src/dosext/dpmi/memory.c: + Always map the uncommitted mem to zeropage. + + * src/: arch/linux/mapping/mapping.c, base/init/init.c: + - kmem_map_mapping() was necessary only to supply the lowmem hack. + Since there is nomore a hack, kmem_map_mapping() is unnecessary. + - add missing alloc_mapping() call. + +2004-03-29 Bart + + * src/: emu.c, arch/linux/mapping/mapping.c, env/video/console.c, + env/video/vc.c, env/video/vga.c, env/video/video.c, include/emu.h, + include/video.h: + Introduced video_post_init(): mmaps /dev/mem for video RAM and + sets up VC switch functionality. This allows for the complete + removal of the mapping.c hacks. + + * src/: emu.c, arch/linux/mapping/mapping.c, base/dev/misc/lpt.c, + base/init/dev_list.c, base/init/init.c, base/mouse/mouse.c, + base/mouse/mouseint.c, base/serial/ser_init.c, emu-i386/cputime.c, + emu-i386/ports.c, env/video/X.c, env/video/console.c, + env/video/vc.c, env/video/video.c, include/emu.h, include/mouse.h: + Seperate low memory initialization from the rest of the init and fixed + a few NULL pointer dereferences... + Note: this allows us to remove some hacks but by keeping a temporary + fd to /dev/mem open during init we will also be able to mmap video + ram after dropping privs, so more can be removed in mapping.c... + Dropping privs can then be done much earlier, and also is independent + from memory mapping. + * printer: set BIOS values in printer_mem_setup() + * video: set BIOS values in video_mem_setup() + * dev_list: add mouse_reset and serial_reset handlers which just like + the keyboard reset handler fill in the BIOS values. + * call the reset handler just before booting, not immediately after + the init handler + * move the mouse_reset_to_current_video_mode() during init to + mouse_post_boot() (current_video_mode may be strange for fbdev now, + was 0....) + * mouse.c: fixed sptr null dereference + * mouseint.c: fixed mice->dev NULL deref + * cputime.c: Video may be NULL for first console switches at init while + console code is playing with ioctls + * ports.c -- can derive BIOS_VIDEO_PORT value from dosemu config + * X.c check for config.X_font being NULL + * removed a few superfluous calls to clear_screen() and set_video_bios_size() + * vc.c: can't read the BIOS during the first get_video_ram and console + switch --- this really needs to be cleaned up... + +2004-03-28 Stas + + * src/arch/linux/mapping/mapping.c: + MAPPING_LOWMEM implies MAPPING_ALIAS. + + * src/dosext/dpmi/memory.c: + Account only the committed memory for $_dpmi. Also properly account + committing/uncommitting pages. + +2004-03-28 Bart + + * src/arch/linux/mapping/mapping.c: + Apply mremap /dev/mem areas functionality. + Move old skip code over to new memory structure. + Add #ifdefs for "REQUIRED_KERNEL_VERSION" + +2004-03-27 Bart + + * src/arch/linux/mapping/mapshm.c: + From Stas: replace kernel 2.6.1 check by a cleaner one. + + * src/: arch/linux/mapping/mapping.c, base/init/init.c, + include/mapping.h: + Move my hack from init.c to mapping.c, hoping to merge with Stas' + technique in case mremap is not available. + Only one file left to change now though... + + * src/: arch/linux/mapping/mapping.c, env/video/dualmon.c, + env/video/hgc.c, env/video/matrox.c, env/video/vc.c: + Next chunk of Stas' changes -- this one doesn't really do anything + except for limiting the size of the patch I want to have a closer + look at. + + * src/arch/linux/mapping/mapping.c: + The basic changes need this one too, or DOSEMU is completely broken ... + (sorry, my fault, Bart) + + * etc/global.conf, src/arch/linux/mapping/mapfile.c, + src/arch/linux/mapping/mapping.c, src/arch/linux/mapping/mapshm.c, + src/base/misc/hma.c, src/dosext/dpmi/memory.c, + src/dosext/misc/emm.c, src/include/mapping.h, + src/include/Linux/mman.h: + The small part of Stas' mapping cleanups. + Remove "console" from global.conf for $_graphics=(1) since it already + implies "console" in the parser anyway. + +2004-03-16 Bart + + * src/base/dev/misc/lpt.c: + Fix memory leak. + +2004-03-15 Bart + + * Makefile.conf.in, src/commands/Makefile, + src/arch/linux/Makefile.main: + Define LD at the right place. Remove unnecessary exports in + Makefile.main + +2004-03-13 Stas + + * src/dosext/dpmi/msdos.c: + Fix bug in function 0x56 (rename file), from #909015 + Properly return the canonicalized filename to DPMI client + (from bug #909015) + + * src/dosext/dpmi/: dpmi.c, memory.c: + Escaped bits from previous commit (use RWX, not RW). + +2004-03-12 Bart + + * configure: + autoconf + +2004-03-11 Bart + + * VERSION: + Bump up VERSION in CVS already so people won't confuse it with + 1.3.0 from the tarball. + +2004-03-11 Stas + + * configure.ac: + More escaped bits from the mapping redesign commit. + + * src/base/misc/: dosio.c, shared.c: + Escaped bits from the mapping redesign commit. + +2004-03-11 Bart + + * src/: arch/linux/mapping/mapping.c, base/init/init.c, + include/mapping.h: + Had to create an even bigger hack because the aliased low memory now + overwrites any /dev/mem areas -- this broke console graphics. + So we have to be careful to see which areas were already mapped to + /dev/mem (using a char array that check if a page is /dev/mem or not) + Hopefully with this working base I can start cleaning things up. + +2004-03-10 Bart + + * src/dosext/dpmi/memory.c: + Need to include "emu.h" before other headers to get config.h including + _GNU_SOURCE... + See /usr/include/features.h + #ifdef _GNU_SOURCE + # define __USE_GNU 1 + #endif + _GNU_SOURCE is documented. __USE_GNU is internal to Glibc (AFAICS) + +2004-03-10 Stas + + * src/: arch/linux/mapping/mapping.c, arch/linux/mapping/mapshm.c, + dosext/dpmi/memory.c, dosext/sound/sound.c, + arch/linux/dosext/sound/midid/midid.c, base/dev/dma/dma.c: + - Use RWX permissions for IPC SHM by default instead of RW, + otherwise we end up with the non-executable first meg (but it + didnt hurt since on x86 it is possible to execute regardless). + - Update my e-mail address :) + +2004-03-08 Stas + + * src/: emu-i386/do_vm86.c, include/emu.h, + plugin/commands/builtins.c: + Alowing interrupts during the callbacks proved to be reliable, making a + default. + + * src/plugin/commands/comcom.h, src/plugin/commands/commands.c: + Remove the remaining traces of comcom. + +2004-03-07 Stas + + * src/dosext/dpmi/: dpmi.c, dpmi.h, msdos.c: + Properly free segregs after freeing memory block (from bug #909015) + +2004-03-07 Bart + + * src/: emu.c, arch/linux/mapping/mapfile.c, + arch/linux/mapping/mapping.c, arch/linux/mapping/mapshm.c, + emu-i386/ports.c, emu-i386/simx86/memory.c, include/mapping.h, + include/vm86plus.h: + Removed direct kernel interfaces where possible and replaced inline asm + by syscall() otherwise -- the latter allows glibc to use sysenter and + friends. + The calls for syscall() are extended_mremap, dosemu_sigaction, + vm86_old and vm86_plus. Note however that dosemu_sigaction is only + called when sigaltstack is not supported. + +2004-03-07 Stas + + * src/: base/bios/bios.S, dosext/dpmi/dpmi.c, include/bios.h: + - Implemented extended exception stack frame (DPMI 1.0, needed by rtm32) + - Allow Visible Page Faults (DPMI 1.0, needed by bug #909015 and rtm32) + + * src/: arch/linux/mapping/mapping.c, dosext/misc/emm.c, + include/mapping.h: + Update EMM to new mapping flag. + +2004-03-07 Bart + + * src/: base/misc/disks.c, base/misc/utilities.c, include/disks.h, + include/utilities.h, tools/periph/hdinfo.c: + eliminate libless_llseek. For hdinfo we can use _FILE_OFFSET_BITS==64 + as it's standalone. For disks.c it's safer to explicitly use *64 calls. + #ifdef __linux__ is a little optimistic but doesn't hurt :) + +2004-03-07 Stas + + * src/: arch/linux/async/signal.c, arch/linux/debugger/mhpdbg.c, + arch/linux/mapping/mapfile.c, arch/linux/mapping/mapping.c, + arch/linux/mapping/mapshm.c, base/data.c, base/init/init.c, + base/misc/Makefile, base/misc/hma.c, dosext/dpmi/dpmi.c, + dosext/dpmi/dpmi.h, dosext/dpmi/memory.c, emu-i386/ports.c, emu.c, + include/emu.h, include/mapping.h, include/memory.h: + Mapping system revamp. + - Added support for lowmem region, it is now a part of SHM segment. + - Moved DPMI memory away from SHM segment. + - Updated HMA, DPMI and other subsystems to the new mapping features. + + * src/dosext/dpmi/: dpmi.c, dpmi.h, msdos.c: + Next part of bug #902742: + - remaining issues with is_dos_selector() fixed. + - couple of 16/32bit registers confusion fixed. + dos_read/dos_write case is not yet. + +2004-03-06 Stas + + * src/dosext/dpmi/: dpmi.c, dpmi.h, msdos.c: + Allocate 6 realmode stacks for each DPMI client instead of 16 for + all clients. Blinker extender is now supported (closes SR #855611). diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..232e052 --- /dev/null +++ b/INSTALL @@ -0,0 +1,60 @@ +building and installing dosemu2 from sources +============================================== + +-> REQUIREMENTS for dosemu2: + - fdpp of the latest version, for now from git or from PPA: + https://code.launchpad.net/~dosemu2/+archive/ubuntu/ppa + of from COPR: + https://copr.fedorainfracloud.org/coprs/stsp/dosemu2/ + - gcc >= 8.1 or clang >= 3.7.0 + - glibc >= 2.20 + - linux >= 3.16 for x86-64, >= 4.7 recommended. + With older version than 3.16 there may be some problems + with DPMI, especially make sure to not use 3.14 and 3.15. + linux >= 4.3 for i386. + linux >= 4.11 if you want to run 32bit dosemu under x86_64 kernel + with multilib environment (you likely don't want to do this). + - bison and flex + - SDL >= 2.0.6, >= 2.26.0 is required for copy/paste support. + - libslirp >= 4.1.0 recommended for networking + - json-c >= 0.13 recommended + - development libraries: Xext, slang, gpm, alsa, fluidsynth, + ladspa, libao can be used when available. + +This means that Ubuntu Focal or Fedora 31 are the minimum +recommended systems. + +-------------------------------------------------------------------------- +1. Compile +-------------------------------------------------------------------------- + +First you should run: + +./autogen.sh + +Then either: + +./default-configure +make + +or + +- create a seperate directory and run + $SRCDIR/configure (or $SRCDIR/default-configure; see below) + make + in that directory. + +- If you want dosemu2 executable with debug info, then add -d parameter + to default-configure on the previous step. But expect the debug build + to work considerably slower than the release build! + +- sudo make install + +You can now start dosemu2 by typing "dosemu". But if you didn't install +comcom32, dosemu2 may not find command.com. We recommend to install +the pre-compiled comcom32 package either from PPA or COPR. Or you can +instead download it from here: +https://dosemu2.github.io/comcom32/files/comcom32.zip +unzip to ~/.dosemu/drive_c and symlink as command.com. +You may not want to build comcom32 from sources unless you know how +to install djgpp. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e9ffc30 --- /dev/null +++ b/Makefile @@ -0,0 +1,111 @@ +# Makefile for DOSEMU +# + +all: default + +srcdir=. +top_builddir=. +SUBDIR:=. +ifeq ($(filter deb rpm %/configure configure,$(MAKECMDGOALS)),) + -include Makefile.conf +endif +REALTOPDIR ?= $(abspath $(srcdir)) + +$(REALTOPDIR)/configure: $(REALTOPDIR)/configure.ac + cd $(@D) && autoreconf --install -v -I m4 + +Makefile.conf config.status src/include/config.hh etc/dosemu.desktop: \ + $(REALTOPDIR)/configure +ifeq ($(findstring $(MAKECMDGOALS), clean realclean pristine distclean),) + @echo "Running configure ..." + $< +else + $< || true +endif + +install: changelog + +default install: config.status src/include/config.hh etc/dosemu.desktop + @$(MAKE) -C man $@ + @$(MAKE) -C src $@ + +clean realclean: + @$(MAKE) -C man $@ + @$(MAKE) -C src $@ + +uninstall: + @$(MAKE) -C src uninstall + +docs: + @$(MAKE) -C src/doc all + @$(MAKE) -C src/doc install + +docsclean: + @$(MAKE) -C src/doc clean + +GIT_REV := $(shell $(REALTOPDIR)/git-rev.sh $(REALTOPDIR) $(top_builddir)) +.LOW_RESOLUTION_TIME: $(GIT_REV) + +$(PACKETNAME).tar.gz: $(GIT_REV) changelog + rm -f $(PACKETNAME).tar.gz + (cd $(REALTOPDIR); git archive -o $(abs_top_builddir)/$(PACKETNAME).tar --prefix=$(PACKETNAME)/ HEAD) + tar rf $(PACKETNAME).tar --transform 's,^,$(PACKETNAME)/,' --add-file=changelog; \ + if [ -f "$(fdtarball)" ]; then \ + tar -Prf $(PACKETNAME).tar --transform 's,^$(dir $(fdtarball)),$(PACKETNAME)/,' --add-file=$(fdtarball); \ + fi + gzip $(PACKETNAME).tar + +dist: $(PACKETNAME).tar.gz + +rpm: dosemu2.spec.rpkg + git clean -fd + rpkg local + +deb: + debuild -e CC=clang -i -us -uc -b + +changelog: + if [ -d $(top_srcdir)/.git -o -f $(top_srcdir)/.git ]; then \ + git --git-dir=$(top_srcdir)/.git log >$@ ; \ + else \ + echo "Unofficial build by `whoami`@`hostname`, `date`" >$@ ; \ + fi + +log: changelog + +tests: + python3 test/test_dos.py PPDOSGITTestCase + +pristine distclean mrproper: Makefile.conf docsclean + @$(MAKE) -C src pristine + rm -f Makefile.conf + rm -f $(PACKETNAME).tar.gz + rm -f ChangeLog + rm -f `find . -name config.cache` + rm -f `find . -name config.status` + rm -f `find . -name config.log` + rm -f `find . -name aclocal.m4` + rm -f `find . -name configure` + rm -f `find . -name Makefile.conf` + rm -rf `find . -name autom4te*.cache` + rm -f debian/$(PACKAGE_NAME).* + rm -rf debian/$(PACKAGE_NAME) + rm -f debian/*-stamp + rm -f debian/files + rm -f src/include/config.hh + rm -f src/include/stamp-h1 + rm -f src/include/config.hh.in + rm -f src/include/version.hh + rm -f `find . -name '*~'` + rm -f `find . -name '*[\.]o'` + rm -f `find src -type f -name '*.d'` + rm -f `find . -name '*[\.]orig'` + rm -f `find . -name '*[\.]rej'` + rm -f gen*.log + rm -f config.sub config.guess + rm -rf 2.* + rm -rf autom4te.cache + $(REALTOPDIR)/scripts/mkpluginhooks clean + +tar: distclean + VERSION=`cat VERSION` && cd .. && tar czvf dosemu-$$VERSION.tgz dosemu-$$VERSION diff --git a/Makefile.conf.in b/Makefile.conf.in new file mode 100644 index 0000000..e33cffa --- /dev/null +++ b/Makefile.conf.in @@ -0,0 +1,114 @@ +# Makefile.conf.in for DOSEMU +# +# This file is included by all Makefiles + +DOSBIN = dosemu.bin + +PACKAGE_TARNAME:=@PACKAGE_TARNAME@ +prefix:=@prefix@ +exec_prefix=@exec_prefix@ +bindir:=@bindir@ +sysconfdir:=@sysconfdir@ +confdir:=@confdir@ +libdir:=@libdir@ +plugindir:=@plugindir@ +datarootdir:=@datarootdir@ +datadir:=@datadir@ +mandir:=@mandir@ +docdir:=@docdir@ +x11fontdir:=@x11fontdir@ +ttffontdir:=@ttffontdir@ +fdtarball:=@fdtarball@ +cmdsuff:=@cmdsuff@ +abs_top_srcdir:=@abs_top_srcdir@ +abs_top_builddir:=@abs_top_builddir@ + +INCDIR=-I${top_builddir}/src/include -I${top_builddir}/src/plugin/include \ + -I${top_srcdir}/src/base/bios/x86 -I${top_srcdir}/src/include \ + -I${top_srcdir}/src/base/lib +top_srcdir:=$(abs_top_srcdir) +srcdir = $(patsubst %/,%,$(abs_top_srcdir)/src/$(SUBDIR)) + +CFLAGS:=@CFLAGS@ +ALL_CFLAGS:=@DOSEMU_CFLAGS@ $(CFLAGS) +ASFLAGS:=@ASFLAGS@ +XASFLAGS:=@XASFLAGS@ +CPPFLAGS:=@CPPFLAGS@ +ALL_CPPFLAGS:=@DOSEMU_CPPFLAGS@ $(INCDIR) $(CPPFLAGS) +LDFLAGS:=@LDFLAGS@ +AS_LDFLAGS:=@AS_LDFLAGS@ +ALL_LDFLAGS:=@DOSEMU_LDFLAGS@ $(LDFLAGS) +DOSBIN_LDFLAGS:=@DOSBIN_LDFLAGS@ +LIBS:=@LIBS@ +CC:=@CC@ +CPP:=@CPP@ +LD:=@CC@ +AS:=@AS@ +XAS:=@XAS@ +AS_LD:=@AS_LD@ +XOBJCOPY:=@XOBJCOPY@ +CC_FOR_BUILD:=@CC_FOR_BUILD@ +CFLAGS_FOR_BUILD:=@CFLAGS_FOR_BUILD@ + +YACC:=@YACC@ +# NOTE: we really need bison, yacc won't work any more +#YACC=bison -y +LEX:=@LEX@ +LN_S := @LN_S@ +LN_SFT := @LN_SFT@ + +# This gets defined even if we chose via ./include/config.h NOT to +# use the debugger +DEBUGGER:=@DEBUGGER@ + +OPTIONALSUBDIRS := @OPTIONALSUBDIRS@ +PLUGINSUBDIRS := @PLUGINSUBDIRS@ +ST_PLUGINSUBDIRS := @ST_PLUGINSUBDIRS@ + +HAVE_LIBBFD := @HAVE_LIBBFD@ + +OS=@CONFIG_HOST@ +RANLIB:=@RANLIB@ + +PACKAGE_NAME:=@PACKAGE_TARNAME@ + +USE_DL_PLUGINS := @USE_DL_PLUGINS@ +X86_EMULATOR := @X86_EMULATOR@ +X86_JIT := @X86_JIT@ +DNATIVE := @DNATIVE@ +KVM := @KVM@ +MCONTEXT := @MCONTEXT@ +USE_OFD_LOCKS := @USE_OFD_LOCKS@ +USE_XATTRS := @USE_XATTRS@ +USE_EVTIMER_FD := @USE_EVTIMER_FD@ +USE_OSS := @USE_OSS@ +USE_SOFTFLOAT := @USE_SOFTFLOAT@ + +INSTALL:=@INSTALL@ +REALTOPDIR:=$(top_srcdir) +SRCPATH:=$(top_srcdir)/src + +PACKAGE_VERSION:=$(shell cd $(top_srcdir) && ./getversion) +PACKAGE_VERSION_SPACES:=$(subst ., ,$(PACKAGE_VERSION)) +PACKAGE_VERSION_SPACES:=$(subst -, ,$(PACKAGE_VERSION_SPACES)) +PACKAGE_VERSION_SPACES:=$(subst pre, pre,$(PACKAGE_VERSION_SPACES)) +VERSION:=$(word 1, $(PACKAGE_VERSION_SPACES)) +SUBLEVEL:=$(word 2, $(PACKAGE_VERSION_SPACES)) +PATCHLEVEL1:=$(word 3, $(PACKAGE_VERSION_SPACES)) +PATCHLEVEL2:=$(word 4, $(PACKAGE_VERSION_SPACES)) +ifeq ($(PATCHLEVEL2),) +PACKETNAME:=$(PACKAGE_NAME)-$(VERSION).$(SUBLEVEL)$(PATCHLEVEL1) +else +PACKETNAME:=$(PACKAGE_NAME)-$(VERSION).$(SUBLEVEL)$(PATCHLEVEL1).$(PATCHLEVEL2) +endif +THISVERSION:=$(VERSION).$(SUBLEVEL)-$(PATCHLEVEL1) +PACKVERSION:=$(VERSION).$(SUBLEVEL)$(PATCHLEVEL1) +BINPATH:=$(top_builddir)/$(THISVERSION) +RELEASE_DATE="@RELEASE_DATE@" +REVISION:=$(shell cd $(top_srcdir) && ./getversion -r) + +ifeq ($(USE_DL_PLUGINS),1) +DL_CFLAGS:=-fPIC +else +-include $(top_builddir)/src/plugin/*/Makefile.conf +endif diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..7994082 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,39 @@ +## pre9 + +There are over ~3300 commits since the last preview release (pre8) +and a very large chunks of dosemu2 architecture were written and +stabilized. Hopefully it would be possible to do more frequent +releases in the future and avoid such a long development cycles. + +Note: because of the new architecture in place, we suggest to +re-install dosemu completely. I.e. erase your /etc/dosemu +~/.dosemurc ~/.dosemu/.dosemurc and clean ~/.dosemu/drive_c from +any system/boot files (or remove ~/.dosemu completely if there is +nothing important within). Compatibility mode (to run old setups) +is present but may require some manual adjustments. Also note that +compatibility mode means a reduced feature set. If you insist on +an old setup but dosemu2 doesn't pick it up properly, don't hesitate +to ask for help on our github discussions page. + +Summary of user-visible changes: +* fdpp is now our default OS. It is a 64bit DOS core that boots under + dosemu2 and allows to work without installing any DOS. Of course + you can still install any DOS if you want, but that will in most + cases only cause the features reduction. +* comcom32 is our default shell. comcom32 is a command.com variant that + runs in 32bit space. Eventually we aim for the 64bit shell, but so + far we only have this. You can still use your favourite shell if you + want. +* Worked out the security model. We hope that dosemu2 now provides + a relatively secure sandbox environment, and yet exposes all the + needed features. Exposing features in a secure manner (rather than + to simply declare them insecure and disable) was a challenge. +* Added KVM-assisted acceleration to DPMI (@bartoldeman). +* Added TTF support (@andrewbird) +* Implemented region locking and share support in MFS. One of the + most requested/missed feature of dosemu1 times. +* Implemented $_trace_mmio option (@dos4boss) +* Lots of speed-ups to simx86 (@bartoldeman) +* Lots of i18n work. +* musl support +* Added lots of CI tests (@andrewbird) diff --git a/NEWS.old b/NEWS.old new file mode 100644 index 0000000..f21e62f --- /dev/null +++ b/NEWS.old @@ -0,0 +1,331 @@ +Version dosemu2-2.0pre8 +============= +We have ~300 commits, many of which are bug-fixing, as pre7 was horribly +buggy due to heavy development. As for the newly introduced regressions - +not too much known at this time, except the drop of clang support. We +hope to get clang support back again in the very near future. + +Summary of changes: +* Improved hx-extender support so that the win32 version of quake2 + can now work under dosemu2 (in pre7 it was supposed to but didn't). +* Support for external EMS managers like emm286.exe was (partially) + implemented. As the result, we have a new UMB driver, umb.sys, that + allows to enable UMB without also enabling EMS, leaving this possibility + to an external driver. +* New DOSes supported: RxDOS 7.1.x and 7.2, PC-MOS 5.01. +* fossil driver got updates and missing functionality additions. +* Sound stack got updates to reduce the latencies. +* Passing DOS commands via dosemu's cmdline was changed, see man for + details. Many new possibilities added, like passing environment + variables and more. +* Some no longer used commands (comline.com, ecpuon.com, ecpuoff.com) + were removed. +* Dumb terminal mode now handles the charsets. +* Fixes to all regression bugs of pre7 - there were many of them. + +List of tracker bugs that were closed: + cpu exception in dosemu code when launched from .desktop file #414 + Duplicate src file detect.h #426 + int2f - misplaced brace? #398 + HMA unavailable after a reboot from within DOSemu #427 + VESA test utility vbetest.exe doesn't work since new int_revect logic #419 + Int 14h function 1Bh returns pointer to wrong ID string after reboot #432 + DOSemu becomes slow after certain programs return from a DOS shell #433 + Windows crashes dosemu in cpusim #435 + dosdebug bpint causes crash #442 + Int19 loads the MBR but returns to where it was called #441 + File or directory creation on hdimage fails #443 + BIOS data area entry at 0:0475h "NUMBER OF FIXED DISK DRIVES" is zero #447 + Fatfs has duplicate config.sys file #449 + Compile error: signal.c:370:6: error: conflicting types for 'deinit_handler' #457 + lagging after printing #455 + dosdebug INT tracing oddities #458 + dosdebug: tracing iret doesn't stop after the iret #465 + pause key doesn't work with SDL #472 + instremu runs with async signals blocked #477 + Build engine and VESA modes #456 + dosdebug t misses instruction after sti #480 + Windows 3.1 setup reboot hangs #469 + Windows 3.1 doesn't start after starting+quiting Borland C #488 + MechWarrior crashes dosemu with jit #487 + cpatch crash due to misaligned stack (jit) #489 + dosdebug d repetition should advance pointers #482 + keen4 sometimes crashes in remap.c #491 + dosemu2 complains vm.mmap_min_addr > 0 eventhough it is zero #448 + INSTALL isn't clear about what problems might be with Linux < 3.16 #499 + Int13/04 verify on fatfs #503 + No native chars in dumb terminal mode #505 + in dumb terminal mode, control chars are displayed rather than working #508 + crash or load error after int19 reboot (vm86 jit) #493 + CPU should execute two instructions if the first is "mov ss" #481 + PC-MOS/386 in DOSEMU2? #502 + Bug in modemu.c #527 + ucontext issue when using musl libc #531 + pthread_getname_np and feenableexcept don’t exist in musl #532 + Net header issue when using musl libc #530 + LFN: Creating a file with LFN from djgpp app gives mangled name file #535 + rename with wildcards doesn't work with non-free doses #526 + +Thanks goes to: + Andrew Bird: MFS/LFN work, new DOSes support + C. Masloch: mkfatimage16, int13 work + Stas Sergeev: credit thyself or no one else will :) + @newbluemoon: musl porting help (not quite there yet) + Julius Schwartzenberg: as usual, lots of testing + @bpranoto, @severach, @jharrison022: testing + +Version dosemu2-2.0pre7 +============= +We have over 700 commits, mostly targeted on re-introducing and fixing +the features previously supported in dosemu1, and a couple of new ones. +Unfortunately we also have a few regressions. +Summary below. + +* Reworks to SDL plugin. The HW-accelerated rendering is now disabled + by default, as well as the scaling filters, and full-screen switching + may not work. If you want to use HW-accelerated rendering, try + $_SDL_swrend = (off). If you want scaling filters or full-screen, use + 'dosemu -X'. We will try to improve the SDL support in future releases. +* HX extender now fully supported, quake2 works! +* Mouse wheel support implemented +* Support booting OpenDOS and some old versions of MS-DOS. +* 2 new keyboard plugins implemented ("stdio" and "tty") to allow dumb + terminal mode to work without slang library. Selectable with '-kt', '-ks'. +* VGA pass-through in text-mode console is now fully supported. Hope + no one uses it though. +* Windows-3.1 support is fully re-introduced, and is actually much better + than in dosemu1. We also provide the mouse driver for windows-3.1 that + allows the use of the host's mouse pointer: + https://github.com/stsp/win31-mouse-driver/tree/master/out +* Resurrected basic Windows-3.0 support. +* Foxpro support re-introduced. +* Fixes to SVGA/VESA modes. Support for some missing modes added. +* Many fixes to gfx remapper. The various annoying video artifacts + should now be fixed. +* SB16 fixes to support sound on more games. +* Mouse copy/paste support is fully re-introduced. +* New $_bootdrive option allows to boot from any drive, not only C. +* Net: IPX and packet driver are fully functional again. +* Lots of build system improvements. We have now the debian build support + and "make uninstall". +* X fonts are resurrected and provided again. +* Many fixes and improvements: DPMI support is now much more reliable, + x86 JIT is more reliable, many performance improvements. +* Many updates to interrupt routing code. +* Resurrected emufs.sys. +* Sanitize syntax of lredir2: silly LINUX\FS is no longer needed. + +List of tracker bugs that were closed: + ERROR: Drive C not defined can't boot! with clean installation #232 + How to use vbootfloppy now? #235 + It would be great to have.deb packages for Ubuntu/Debian! #233 + jit: performance regression #239 + Direct VGA regression #222 + fdisk and mkfatimage16 #244 + Radeon on Ubuntu 14.04 (+16.04 HWE stack) doesn't work in SDL mode #248 + OpenDOS 7.02 causes crash when *not* ran from a harddisk image #250 + Strange compiler messages on 32 bit #253 + Underscore keypress is ignored in terminal mode #256 + Creative SB16 driver installer (for Windows 3.1) hangs at SB16 detection #259 + RFC: The flag position with floppy and harddrive directives in dosemu.conf is inconsistent #263 + SciTech Display Doctor 5.3a crashes DOSEMU #264 + dosemu crashes immedately after startup with MS-DOS 3.3 #49 + Mortal Kombat 1 & 2 keyboard issue #277 + SDL: copy/paste from dosemu window with mouse #271 + x86_64 builds failing on Ubuntu Yakkety and Zesty #262 + console switching is unreliable #285 + dos prompt in win31 under console #25 + plainvga does not restore text #286 + windows: krnl286 doesn't work #287 + Idle CPU usage anomaly #211 + Compile error in lexer.l for yywrap #288 + Regression: terminal mode #291 + mouse in grab mode broken #293 + DPMI is broken on 32bit #294 + Typo in vgaemu_modelist.h for VBE mode 1280x1024x16 #295 + VM86 not available on 32bit #297 + windows-3.0 doesn't work #225 + Speedy does not have sound #103 + Soft links and contents of ~/.dosemu being overwritten #289 + Unable to boot #305 + goblins3 gfx and audio problems #304 + Foxpro hangs quickly #309 + Windows 3.1 installer does not work #97 + Norton utilities v8 english causes dosemu crash #311 + UMB not working anymore under FreeDOS #315 + Mouse support for Foxpro in 132 column mode #319 + mouse paste broken #320 + All DOS commands stopped working #318 + erratic mouse behaviour after mode switches #314 + dosemu2 64bit crash on vlm #317 + MS-DOS 7.00 / 7.10 unable to format floppy disk #276 + Can't run make due to git path issue #324 + $_X_font="vga11x19" doesn't work #323 + make: git-rev.sh Bad substitution #326 + Enhance unix command to set to current DOS folder #328 + SDL mouse cursor not hidden #331 + Graphic artefacts in PC/GEOS with a lot of VESA modes #301 + Add uninstall target #334 + Request: ctl-z functionality? #337 + FD tarball path specification #341 + dosemu -S not ended after exitemu #343 + undefined symbol: XLockDisplay #342 + Dosemu failing to start #344 + Dosemu failing to start in SDL mode #345 + X: cursor shape changes by Ctrl-Alt-k #349 + 'dosemu -t' crashed on screen tmux #351 + mouse problems with Master of Orion 2 #354 + Serial port initialization appears to be slow with high CPU usage #348 + Crash in Fedora 25 #358 + Windows 3.1 installation crashes on Fedora 25 #359 + in terminal or SDL mode isn't possible copy|paste text on text screen #361 + Problem with git-rev.sh and old Git versions (Ubuntu 14.04) #356 + Wrong dosemu fonts #364 + dune locks up at intro #370 + sound in quake lags #369 + lredir2 -d c: changes directory listing #362 + win31 installer sometimes crashes at the end #372 + win31 SB16 driver gives a crash #363 + Unmet build dependencies: docbook-style-dsssl #383 + Clipper application hangs - ERROR: coopth: unsafe context switch #384 + Launchpad ppa deb build failiing on Ubuntu 14.04 #387 + key combos broken under X #391 + sdl error even with -x #395 + ERROR: vde_switch failed: sh: 1: vde_switch: not found #396 + Doom 2 and Rise of the Triad nets about 10 FPS #403 + DPMI linear allocations broken #385 + quake2 does not work #404 + Problems with full screen under X #394 + fails on ubuntu14.04: cannot load libbfd-2.26.1-system.so #408 + mouse cursor ghost #407 + Error finding .git/refs/heads/devel after 'git gc' #411 + emufs.sys is broken #347 + put himem.sys in? #413 + make rpm doesn't work #346 + jit locks up with -D9+e #409 + +Thanks goes to: + Andrew Bird for new DOSes support and great fatfs work. + @bolle732 for new remappers, VESA fixes and for the chocolate. :) + Julius Schwartzenberg for a lot of testing and work on wheel support. + Tee-Kiah Chia for work on build improvements + + +Version dosemu2-2.0pre6 +============= +We have around 500 commits since pre5 with the highlights below: + +* KVM is now enabled by default on 64bit builds. A huge speed-up! +* SDL plugin now uses hardware-accelerated texture updates instead + of the (slow) drawing surface +* virtual modem support is added +* serial port fifos now work again (were broken/unsupported in dosemu2 + but worked in dosemu1) +* DPMI context switching is rewritten for speed-up, using a new + linux kernel extensions and the code from libtask and libpcl +* default DPMI memory size enlarged to 128Mb, which is enough for + all known DOS programs +* new builtin command system.com is added that runs DOS commands + specified via the unix env vars. Similar functionality in unix.com + is kept for compatibility. +* DOSDRIVE_D var is removed. Use new DOSDRIVE_EXTRA variable. +* mouse driver got a huge overhaul for improved robustness in non-grab mode +* winos2 mouse extension is implemented for better support of the OS/2 + mouse driver under windows-3.1 +* middle mouse button is now supported (was broken) +* dosdebug fixes +* many fixes to SDL plugin +* the home dir is no longer exposed to DOS by default. Use -home if you + need this. +* added -cdrom option that tries to mount the cdrom and expose it to DOS +* $_hdimage option can now embed environment variables +* many performance bottlenecks were located and fixed +* many fixes and updates + +List of tracker bugs that were closed: + checking for stack protector disabled... no #61 + Signal 11 in dosemu.bin thread in terminal mode under Ubuntu 16.04 #165 + On first run, have to run dosemu twice #73 + comma on numpad isn't recognized #171 + dumb mode is broken when DOS cmd is specified #173 + re-introduce system.com #174 + Cleanup $_pcm_hpf in default dosemu.conf #181 + Installing with DESTDIR set creates bad symlinks #184 + remove DOSDRIVE_D and drive_z? #177 + failed lredir redirects / #176 + cant del symlinked files #180 + REG macro breaks strict aliasing on i386 #179 + modemu support #164 + Compilation error in vgaemu #189 + use html docs by default #183 + INT33 mouse support for higher resolutions (800x600, 1024x768) + in Windows 3.x (possibly other applications as well) #193 + Application crash on x32 #130 + Dosdebug: bpload fails to load command #191 + Regression: booting from hdimage file fails #203 + ERROR: mprotect to lfb? #204 + Use global SDL_Texture (speedup) #205 + PC/GEOS freezes with VM86, KVM ok #209 + Wacky Wheels performance/timing regression #169 + Is it possible to suppress issues like:- #207 + Mouse range restricted again (was #132) #214 + Weird interactions switching between fullscreen and windowed mode #213 + Running an unrecognised command locks up DOSEMU #221 + SimCity mouse issue with title screen at 640x350x4 #216 + In 3D Lemmings the mouse cursor always goes to the upper + left corner when it enters the window #220 + +Known regressions not fixed in this release: + windows-3.0 doesn't work #225 (all dosemu2 releases affected) + Idle CPU usage anomaly #211 (most dosemu2 releases affected) + Windows 3.1 installer does not work #97 (regression since pre5) + +Thanks go to Bart Oldeman and Andrew Bird for their contributions, +and to Julius Schwartzenberg and @bolle732 for continuous testing. + + +Version dosemu2-2.0pre5 +============= +* SDL video backend is now default, which means a GPU-accelerated rendering +* lredir command was deprecated, use lredir2 with the new syntax +* unix.com syntax was slightly changed +* new tool sound.com is added to change sound and MIDI parameters at run-time +* LPT2 is now pre-configured to print to PDF files +* Added preliminary support for old MS-DOSes (>=3.3) and some DR-DOSes +* many improvements to dosdebug +* use logarithmic volume scale for sound +* Preliminary KVM support - a very fast execution on 64bit machines. + Should be manually enabled in config for now. +* Many regressions fixed +* Added valgrind support, which allowed to find many obscure bugs +* Work-arounds to support the newer fluidsynth for MIDI +* More versions of libao are supported for sound (each having its own bugs). + This is good when SDL2 is unavailable. +* munt support for mt32 (you need to download Roland ROMs yourself) +* built-in backtracer is added for a better crash logs. + Used if gdb is not installed. +* clang is now supported to compile dosemu2 + +List of tracker bugs that were closed: + improve clang support [#160] + DR-DOS 7.02 doesn't boot (7.03 works, 7.02 doesn't) [#151] + broken on large terminals [#153] + Speedy does not have sound [#103] + windows doesnt work with vm86sim [#146] + Textmode --- ERROR: SMALLOC: Out Of Memory on alloc [#143] + cursor stuck top left. in textmode dosemu -t [#144] + Current master does not compile (‘in_indirect_dpmi_transfer’ undeclared) [#138] + Mouse range restricted [#132] + trying dosemu2 with svgalib + i915 [#137] + Dosdebug bpint / g results in strange disassembly / location [#136 again] + Dosdebug unintended initial stop [#135] + init.c:375:38: error: macro "__S" passed 2 arguments, but takes just 1 [#111] + enable fixed aspect [#5] + Trying to add support for booting Enhanced DR_DOS [#88] + screamer crash under console [#28] + Where to document config options [#82] + +Contributors: + Stas Sergeev: see log :) + Bart Oldeman: KVM, cpuemu fixes, vgaemu fixes, regression fixes + Andrew Bird: Support for old DOSes, fixes to FAT12 diff --git a/README.md b/README.md new file mode 100644 index 0000000..536c8e7 --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# dosemu2 + +dosemu2 is an emulator for running DOS programs under linux. +It can also serve as a VM to boot various DOSes. + +Binary packages for ubuntu are available here: +https://code.launchpad.net/~dosemu2/+archive/ubuntu/ppa + +Binary packages for fedora are here: +https://copr.fedorainfracloud.org/coprs/stsp/dosemu2 + +Binary packages for OpenSUSE are here: +https://download.opensuse.org/repositories/home:/stsp2/openSUSE_Tumbleweed + +Please send bug reports to +https://github.com/dosemu2/dosemu2/issues + +## Running + +Just type +``` +dosemu +``` +to run an emulator. + +Use +``` +dosemu -E +``` +to run `` and exit (add `-T` to not exit). + +Use +``` +dosemu -K -E +``` +or +``` +dosemu -- +``` +to run DOS programs from unix directory. + +If you want to run the DOS program from a DOS directory, use this syntax: +``` +dosemu -K :C:\\games\\carma -E carma.exe +``` +This will run `carma.exe` from `c:\games\carma`. Note the leading colon +after `-K`: it means that the DOS path, rather than unix path, is specified. +You can actually specify both paths: +``` +dosemu -K ~/dosgames:carma -E carma.exe +``` +This creates the DOS drive for `~/dosgames`, then chdirs to `carma` and +runs `carma.exe`. + +## Configuring + +Per-user configuration file can be created as `~/.dosemu/.dosemurc`. +Add your custom settings there. +Look into the global configuration file `/etc/dosemu/dosemu.conf` for +existing settings, their descriptions and default values, and modify +the local config accordingly. `$_hdimage` is probably the first setting +to look into, as it configures the host fs access. + +Create `c:\userhook.sys` and/or `c:\userhook.bat` files to customize your +boot sequence. userhook.sys can contain the `config.sys` directives and +`userhook.bat` can contain custom boot commands. + +Drive `C:` is usually located at `~/.dosemu/drive_c`. You can add DOS +programs there. Or you can run `dosemu -d ` to mount the +`` as a new DOS drive. diff --git a/THANKS b/THANKS new file mode 100644 index 0000000..7659f38 --- /dev/null +++ b/THANKS @@ -0,0 +1,77 @@ +dosemu2 credits: + +Main contributors (with their github names): +Stas Sergeev @stsp +Bart Oldeman @bartoldeman +Andrew Bird @andrewbird +C. Masloch @ecm-pushbx +Ryan C. Underwood @runderwo +Stuart Axon @stuaxo + +Special thanks for making dosemu2 feasible +by sponsoring the initial development: +Caylan Van Larson +Hans-Christian Koch + +Many thanks to our Patreon patrons: +Jeffrey H. Ingber (@jharrison022) +C. Masloch (@ecm-pushbx) +doctorwhoguy@gmail.com (@PaddyMac) + +Thanks for the good testing and bugreporting: +Julius Schwartzenberg (@jschwartzenberg) + +A big thanks goes to Linus Torvalds and Andy Lutomirski, +who helped in solving many dosemu2-specific problems in +Linux kernel. Yes, Linus personally mentored these efforts +and made sure dosemu2 has the good support both in the +kernel itself, and in the developers community. + +And of course, thanks goes also to all our users! +If you like this project, you are always welcome to contribute. + +=================================================================== + +dosemu1 credits: + +This release could not have gotten out the door without the work of +our relentless development team, friends and contributors, consisting of +at least (sorted by first name): + + Aaron Adam J. Richter Alan Cox + Alberto Vignani Alessandro Rubini Alexander R.Adams + Alexander V. Lukyanov Alistair MacDonald Amit Margalt + Andrew.Tridgell Andries Andy Shevchenko + Antonio Larrosa Arjan Filius Arne de Bruijn + Bart Oldeman Ben Davis Bernd Paysan + Bernd Schueler Christoph Niemann Clarence Dang + Corey Sweeney Daniel R. Barrlow David Brauman + David Etherton David Hansen David Hindman + David Hodges David Pinson Derek Fawcus + DJ Delorie Dong Liu Egbert Eich + Emmanuel Jeandel Eric W. Biederman Erik Mouw + Florian La Roche George K.Bronnikov Grant R. Guenther + Grigory Batalov Hans Lermen Herbert Xu + James Maclean Japheth Jason E Gorden + Jochen Hein John Davis John Kohl + Jon Tombs Josef Pavlik Julia A. Case + Kang-Jin Lee Karl Kiniger Karl-Max Wagner + Kenneth Corbin Kevin P Lawton Lam Lai Yin, Savio + Larry Stephan Lawrence K Mao Linus Torvalds + Lutz Molgedey Manfred Scherer Marcus Better + Mark Rejhon Marty Leisner Matthew Grant + Maxim Ruchko Michael E. Deisher Michael Karcher + Oleg V. Zhirov Pablo Saratxaga Pasi Eronen + Pat Villani Rainer Zimmermann Reinhard Karcher + Rob Clark Robert de Bath Rod May + Ronnie Rutger Nijlunsing Scott Buchholz + Sergey Suleymanov Stas Sergeev Steffen Winterfeld + Theodore T'so Tim Van der Linden Ulrich Weigand + Uwe Bonnes Urban Widmark Vinod G Kulkarni + Wayne P Meissner Witold Filipczyk Wojtek Pilorz + + ... and others too important to mention. + +Of course, all those people involved in the FreeDOS development should be +mentioned here too, but before I fail to mention some of them, I better point +to http://www.freedos.org ;-) diff --git a/TODO b/TODO new file mode 100644 index 0000000..a67979f --- /dev/null +++ b/TODO @@ -0,0 +1,11 @@ +TODO items for the next release: + +- resurrect irqpassing on x86-64 + Unlikely to happen before 2.0 release + +- implement parport support via ppdev (steal from wine) + Unlikely to happen before 2.0 release + +- look into directfb support + +- look into gstreamer support diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..f426227 --- /dev/null +++ b/VERSION @@ -0,0 +1,2 @@ +2.0pre9 +7076 diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..b46a452 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +DIR=$(dirname $0 | xargs realpath) +echo "Generating toplevel configure script in $DIR..." + +check_scr() { + [ -f $DIR/$1 ] || cp $DIR/scripts/$1 $DIR/$1 +} + +rm -f aclocal.m4 +if ! autoreconf -v -I m4 --install --force $DIR; then + echo "Failure!" + exit 1 +fi +check_scr config.sub +check_scr config.guess +check_scr install-sh +echo +echo "Done, now run $DIR/default-configure" diff --git a/ci_build.sh b/ci_build.sh new file mode 100755 index 0000000..3b3e8a1 --- /dev/null +++ b/ci_build.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +set -e + +LOCALFDPP="localfdpp.git" +LOCALFDPPINST="$(pwd)/localfdpp" +FDPPBRANCH="" + +test -d ${LOCALFDPP} && exit 1 + +if [ "${TRAVIS}" = "true" ] ; then + echo "Travis seems to have some old version of clang in its local directory" + PATH=$(echo ${PATH} | sed 's,:/usr/local/clang-7.0.0/bin,,g') + export PATH + echo PATH is ${PATH} +fi + +git clone --depth 1 --no-single-branch https://github.com/dosemu2/fdpp.git ${LOCALFDPP} +( + cd ${LOCALFDPP} || exit 2 + [ -z "$FDPPBRANCH" ] || git checkout "$FDPPBRANCH" + git config user.email "cibuild@example.com" + git config user.name "CI build" + git tag tmp -m "make git-describe happy" + + echo "DEBUG_MODE = 1" > local.mak + echo "EXTRA_DEBUG = 1" >> local.mak + echo "USE_UBSAN = 1" >> local.mak + + # Install the build dependancies based FDPP's debian/control file + sudo add-apt-repository ppa:stsp-0/nasm-segelf + sudo add-apt-repository ppa:stsp-0/thunk-gen + sudo apt update -q + mk-build-deps --install --root-cmd sudo + + # Seems to miss this, perhaps the optional dependency confuses things + if [ "${TRAVIS}" = "true" ] ; then + sudo apt install binutils + fi + + make + sudo make install +) + +# Install the build dependancies based Dosemu's debian/control file +sudo add-apt-repository -y ppa:dosemu2/ppa +mk-build-deps --install --root-cmd sudo + +if [ "${SUBTYPE}" = "asan" ] ; then + sed -i 's/asan off/asan on/g' compiletime-settings.devel +fi +CC=clang ./default-configure -d +make + +# Install the FAT mount helper +sudo cp test/dosemu_fat_mount.sh /bin/. +sudo chown root.root /bin/dosemu_fat_mount.sh +sudo chmod 755 /bin/dosemu_fat_mount.sh + +# Install the TAP helper +sudo cp test/dosemu_tap_interface.sh /bin/. +sudo chown root.root /bin/dosemu_tap_interface.sh +sudo chmod 755 /bin/dosemu_tap_interface.sh diff --git a/ci_prereq.sh b/ci_prereq.sh new file mode 100755 index 0000000..888bf1a --- /dev/null +++ b/ci_prereq.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +sudo apt-get update +sudo apt install -y \ + devscripts \ + equivs \ + git \ + bash diff --git a/ci_test.sh b/ci_test.sh new file mode 100755 index 0000000..cddb7ed --- /dev/null +++ b/ci_test.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +. ./ci_test_prereq.sh + +if [ "${TRAVIS}" = "true" ] ; then + export CI="true" + export CI_BRANCH="${TRAVIS_BRANCH}" + if [ "${TRAVIS_EVENT_TYPE}" = "cron" ] ; then + export RUNTYPE="full" + else + export RUNTYPE="simple" + fi + +elif [ "${GITHUB_ACTIONS}" = "true" ] ; then + # CI is already set + export CI_BRANCH="$(echo ${GITHUB_REF} | cut -d/ -f3)" + if [ "${GITHUB_EVENT_NAME}" = "push" ] && [ "${GITHUB_REPOSITORY_OWNER}" = "dosemu2" ] && [ "${CI_BRANCH}" = "devel" ] ; then + export RUNTYPE="simple" + fi +fi + +TBINS="test-binaries" +if [ "${CI}" = "true" ] ; then + [ -d "${HOME}"/cache ] || mkdir "${HOME}"/cache + [ -h "${TBINS}" ] || ln -s "${HOME}"/cache "${TBINS}" +else + [ -d "${TBINS}"] || mkdir "${TBINS}" +fi +python3 test/test_dos.py --get-test-binaries + +if [ -f /dev/kvm ] ; then + sudo setfacl -m u:${USER}:rw /dev/kvm +fi + +echo +echo "=====================================================" +echo "= Tests run on various flavours of DOS =" +echo "=====================================================" +# all DOS flavours, all tests +# python3 test/test_dos.py +# single DOS example +# python3 test/test_dos.py FRDOS120TestCase +# single test example +# python3 test/test_dos.py FRDOS120TestCase.test_mfs_fcb_rename_wild_1 + +if [ "${TRAVIS}" = "true" ] ; then + ARGS="--require-attr=cputest" +else + ARGS="" +fi + +case "${RUNTYPE}" in + "full") + ARGS="${ARGS} PPDOSGITTestCase MSDOS622TestCase FRDOS130TestCase DRDOS701TestCase" + ;; + "normal") + ARGS="${ARGS} PPDOSGITTestCase MSDOS622TestCase" + export SKIP_UNCERTAIN=1 + ;; + "simple") + ARGS="${ARGS} PPDOSGITTestCase" + export SKIP_EXPENSIVE=1 + export SKIP_UNCERTAIN=1 + ;; +esac + +# CC is set on Travis and can confuse compilation during tests +unset CC + +# Make cpu tests here so that we see any failures +make -C test/cpu clean all + +python3 test/test_dos.py ${ARGS} + +for i in test_*.*.*.log ; do + test -f $i || exit 0 +done + +exit 1 diff --git a/ci_test_prereq.sh b/ci_test_prereq.sh new file mode 100755 index 0000000..74e7225 --- /dev/null +++ b/ci_test_prereq.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +sudo add-apt-repository -y ppa:jwt27/djgpp-toolchain +sudo add-apt-repository -y ppa:tkchia/build-ia16 + +sudo apt update -q + +sudo apt install -y \ + acl \ + comcom64 \ + nasm \ + python3-cpuinfo \ + python3-pexpect \ + mtools \ + gcc-djgpp \ + djgpp-dev \ + qemu-system-common \ + gdb \ + valgrind \ + gcc-ia16-elf \ + libi86-ia16-elf \ + libi86-testsuite-ia16-elf \ + gcc-multilib \ + dos2unix \ + bridge-utils \ + libvirt-daemon \ + libvirt-daemon-system diff --git a/compiletime-settings b/compiletime-settings new file mode 100644 index 0000000..b36eaeb --- /dev/null +++ b/compiletime-settings @@ -0,0 +1,4 @@ +config { + sysconfdir /etc + fdtarball dosemu-freedos-1.0-bin.tgz +} diff --git a/compiletime-settings.devel b/compiletime-settings.devel new file mode 100644 index 0000000..d0f188d --- /dev/null +++ b/compiletime-settings.devel @@ -0,0 +1,12 @@ +config { + experimental on + debug on + asan off + lsan off + tsan off + ubsan on + optimize off + system-wa off + sysconfdir /etc + fdtarball dosemu-freedos-1.0-bin.tgz +} diff --git a/compiletime-settings.help b/compiletime-settings.help new file mode 100644 index 0000000..08a2445 --- /dev/null +++ b/compiletime-settings.help @@ -0,0 +1,142 @@ +This file describes the compiletime tunable settings for DOSEMU + +You have either to edit the ./compiletime-settings file or use the +the setup-dosemu tool. If you edit manually, you _must_ not remove any +of the lines, instead just change 'off' to 'on' and vice versa. + +The options have the following meaning: + +experimental "enable experimental stuff" + + Global switch to prevent experimental stuff from compile. + There are experimental parts in the source, that may do harm to + your system if you use them without care / knowledge. + In order to protect you, those are normally disabled + in ./configure. Even if you enable discrete switches + in ./compiletime-settings, those will not have effect + without the global switch also set. + +sbemu "SB Emulation" + + Code to support sound via DOSEMU. The sound code emulates + a simple SoundBlaster on any sound card supported by the + Linux Kernel. Hence, your DOS-Box sees a SoundBlaster + even if you don't have it. Keep that in mind + when you setup your DOSish software. + +mitshm "Use MIT Shared Memory extensions under X" + + You normal would like to have this activated, though you + will not profit from it, when doing remote X. + If you encounter problems with your X-server, try to switch + this off. + +vidmode "Use video mode extensions under X" + + You normal would like to have this activated, if you want + X to change resolution in fullscreen-mode (Ctrl-Alt-F), + where applicable. + +x "Use X-Windows" + + With this set on, you enable the X-windows support of DOSEMU. + Though, the compiled binary will run without X too. + If you don't have The X development packages install or + if you won't use it and want a smaller DOSEMU binary, + then turn this option off. + +net "enable Linux net code" + + There is network related code in DOSEMU such as IPX support, + a builtin packet driver, e.t.c. This will be compiled in + if you set this switch. You may compile with this option + and later disable it in the runtime configuration. + Normally you will let it enabled. + + +debug "compile with debug info" + + This uses the '-g' switch for GCC, hence you can use GDB + to debug DOSEMU. + +linkstatic "make static binaries" + + With this switch on, a statically linked binary is generated + The size isn't that big (about 30% bigger then a dynamically + linked one) and DOSEMU will run faster and is more portable + between systems (it then depends only on the kernel + version, no (g)libc incompatibilities). The official DOSEMU + binary distribution always is statically linked. + +cpuemu "EXPERIMENTAL, enable CPU emulator" cpuemu + + This is in _no_ case ready for production, this switch is + here just for the developers ;-) + +aspi "compile with ASPI support" aspi + + This enables the dosemu builtin ASPI driver, which also + need the DOS driver ./commands/aspi.sys in config.sys. + You then may be able to use DOS software that accesses + SCSI devices such as CD-burners, Scanners e.t.c. + However, not all SCSI devices available in Linux are + offered to the driver, because this is inherently dangerous. + Instead you define in /etc/dosemu.conf which 'sg' device + you want give to dosemu and you also specify the device + type which the ASPI driver then check to avoid you accessing + a mounted disk. + +svgalib "compile with svgalib support" svgalib + + This enables support for using svgalib >= version 1.4.2 + for switching between consoles when running graphics + on console _and_ if you configured 'svgalib' as 'graphic chip' + in /etc/dosemu.conf. + NOTE: this doesn't help for all svgalib supported graphic + cards and your console may freeze anyway. Currently reported + as 'working' are riva TNT 128 and sis. + +gpm "compile with gpm support" gpm + + This enables support for using GPM, the mouse server for the + Linux console; it is only used when you run DOSEMU on the + Linux console without graphics (unprivileged). + +plugin_kbd_unicode "Use new keyboard code" plugin_kbd_unicode + +plugin_extra_charsets "Use extra character sets" plugin_extra_charsets + +plugin_term "Use new terminal plugin" + +plugin_translate "Use Unicode translation plugin" plugin_translate + +plugin_demo "Use demo plugin" plugin_demo + +target_bits "Target bits" target_bits + + Set to auto for native compilation, to 32 to force compilation + of 32-bit DOSEMU on an x86-64 system, and to 64 to force + 64-bit compilation. + +target_cpu "Target cpu" target_cpu + +prefix "Prefix for DOSEMU system-wide directory structure" prefix + +bindir "Directory for DOSEMU binaries" bindir + +sysconfdir "Directory for system-wide configuration files" sysconfdir + +datadir "Directory for DOSEMU data" datadir + +mandir "Directory for DOSEMU man pages" mandir + +docdir "Directory for DOSEMU documentation" docdir + +syshdimagedir "Default directory for images and boot directories" syshdimagedir + +x11fontdir "Directory for X fonts" x11fontdir + +fdtarball "Name of the FreeDOS tarball" fdtarball + This is normally named dosemu-freedos-bin.tgz, to be obtained from www.dosemu.org + Use "none" if you don't want to use FreeDOS. + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..3ecd739 --- /dev/null +++ b/configure.ac @@ -0,0 +1,792 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT([dosemu2],m4_esyscmd(./getversion -s | tr -d '\n'),[https://github.com/dosemu2/dosemu2/issues],[dosemu2]) +dnl AM_SILENT_RULES and AM_DEP_TRACK should not be used explicitly, +dnl AM_INIT_AUTOMAKE includes them +AM_SILENT_RULES +AM_DEP_TRACK +AM_MAINTAINER_MODE +AC_CONFIG_SRCDIR([src/include/emu.h]) +AC_PREREQ([2.64]) + +AC_CONFIG_HEADERS([src/include/config.hh]) + +dnl Checks for programs. +: ${CFLAGS=""} +AC_PROG_CC +#AC_PROG_CC_C11([AC_DEFINE(HAVE_STD_C11)]) +AC_DEFINE(HAVE_STD_C11) +AC_PROG_CPP +AX_PROG_CC_FOR_BUILD +AC_USE_SYSTEM_EXTENSIONS + +AC_MSG_CHECKING([for $CC actually being clang]) +if $CC -v 2>&1|grep 'clang' >/dev/null; then + AC_MSG_RESULT([yes]) + AC_MSG_CHECKING([for clang >= 3.7]) + clang_ver=`$CC -v 2>&1 | grep version | sed 's/.*version //' | cut -d " " -f 1` + clang_maj=`echo $clang_ver | cut -d "." -f 1` + clang_mid=`echo $clang_ver | cut -d "." -f 2` + if test $clang_maj -ge 3 -a $clang_mid -ge 7 -o $clang_maj -ge 4 ; then + AC_MSG_RESULT([yes, $clang_ver]) + else + AC_MSG_RESULT([no, $clang_ver]) + AC_MSG_ERROR([Need clang-3.7 or newer, or use gcc instead]) + fi + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -Wno-microsoft -Wno-incompatible-pointer-types \ + -Wno-address-of-packed-member" + LDFLAGS="$LDFLAGS -Wno-unused-command-line-argument" + use_clang="yes" + + AS="$CC" + clang_as_as=1 + ASFLAGS="-xassembler -c -" +else + AC_MSG_RESULT([no]) + use_clang="no" + + AC_MSG_CHECKING([for $CC actually being c++]) + if $CC -v 2>&1 | grep "g++" | grep "COLLECT_GCC" >/dev/null; then + AC_MSG_RESULT([yes, using -fpermissive]) + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -fpermissive -Wno-narrowing" + else + AC_MSG_RESULT([no]) + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -fplan9-extensions" + fi +fi + +CONFIG_HOST=`uname -s` +AC_SUBST(CONFIG_HOST) +machine=`$CC -dumpmachine | cut -d- -f1 | sed 's/i.86/i386/'` + +if test -z "$AS"; then + AC_PATH_PROG([AS], [as]) + if test -z "$AS"; then + AC_PATH_PROG([AS], [clang]) + if test -z "$AS"; then + AC_MSG_ERROR(as not found) + fi + AC_MSG_NOTICE(Using clang as as...) + clang_as_as=1 + ASFLAGS="-xassembler -c -" + fi +fi +AC_PATH_PROGS([XAS], [i686-linux-gnu-as i386-elf-as]) +if test -z "$XAS"; then + if test "$CONFIG_HOST" = "Darwin"; then + AC_MSG_ERROR([Please install i386-elf-binutils from https://github.com/nativeos/homebrew-i386-elf-toolchain]) + fi + AC_PATH_PROG([XAS], [x86_64-linux-gnu-as]) + if test -z "$XAS"; then + if test "$machine" = "i386" -o "$machine" = "x86_64"; then + XAS="$AS" + if test "$clang_as_as" = "1"; then + XASFLAGS="$ASFLAGS -m32" + else + XASFLAGS="$ASFLAGS --32" + fi + else + AC_MSG_ERROR([cross-assembler not found, please install binutils-i686-linux-gnu]) + fi + else + XASFLAGS="$ASFLAGS --32" + fi +fi + +AC_PATH_PROGS([LD], [ld ld.lld]) +if test -z "$LD"; then + AC_MSG_ERROR(ld not found) +fi +AC_PATH_PROGS([AS_LD], [i686-linux-gnu-ld i386-elf-ld x86_64-linux-gnu-ld ld.lld ld]) +if test -z "$AS_LD"; then + AS_LD="$LD" +fi + +AC_PATH_PROG([OBJCOPY], [objcopy]) +O_VER=`$OBJCOPY -V | head -n 1 | grep "GNU objcopy"` +if test -z "$O_VER"; then + AC_MSG_WARN([rejecting BSD objcopy]) + OBJCOPY="" +fi +if test -z "$OBJCOPY"; then + AC_PATH_PROG([LOBJCOPY], [llvm-objcopy], [], [$PATH${PATH_SEPARATOR}/usr/local/opt/llvm/bin]) + if test -z "$LOBJCOPY"; then + AC_MSG_ERROR(objcopy not found) + fi + OBJCOPY="$LOBJCOPY" +fi + +AC_PATH_PROGS([XOBJCOPY], [i686-linux-gnu-objcopy x86_64-linux-gnu-objcopy objcopy]) +if test -z "$XOBJCOPY"; then + XOBJCOPY="$OBJCOPY" +fi +AC_SUBST(AS) +AC_SUBST(XAS) +AC_SUBST(AS_LD) +AC_SUBST(XOBJCOPY) + +AC_ARG_WITH(target-bits-32, [AS_HELP_STRING([--with-target-bits-32], + [build for 32bit target (multilib in case of x86_64 host)])], + CPPFLAGS="$CPPFLAGS -m32" + LDFLAGS="$LDFLAGS -m32" + ASFLAGS="--32" + with_target_bits="32" +) +AC_PROG_INSTALL +AC_PROG_LEX([noyywrap]) +if test -z "$LEX"; then + AC_MSG_WARN(Your system doesn't seem to have lex or flex available.) + AC_MSG_ERROR(Install lex or flex and retry.) +fi + +AC_PROG_LN_S +AC_MSG_CHECKING([if $LN_S is a GNU ln]) +if $LN_S --help >/dev/null 2>&1; then + AC_MSG_RESULT([yes, using -T]) + LN_SFT="$LN_S -f -T" +else + AC_MSG_RESULT([no, using -F]) + LN_SFT="$LN_S -f -F" +fi +AC_SUBST(LN_SFT) + +AC_CHECK_PROGS([MAKE], [gmake make]) +if test -z "$MAKE"; then + AC_MSG_ERROR(You don't have make installed) +fi +# export MAKE to run from scripts +export MAKE="$MAKE" +AC_CHECK_PROGS([REALPATH], [realpath]) +if test -z "$REALPATH"; then + AC_MSG_ERROR(You don't have realpath installed) +fi +AC_PROG_RANLIB +AC_PROG_YACC +dnl The bison problem +if test -z "`echo $YACC | grep bison`" ; then + AC_MSG_WARN( ) + AC_MSG_WARN(Your system doesn't seem to have bison available.) + AC_MSG_ERROR(Install bison and retry.) +fi +AC_PROG_GREP +AC_PROG_EGREP +AC_PROG_SED +AC_PROG_AWK +if test "$ac_cv_prog_AWK" != "gawk" ; then + AC_MSG_ERROR(Install gawk and retry.) +fi +AC_CHECK_PROGS([PKG_CONFIG], [pkg-config]) +if test -z "$PKG_CONFIG"; then + AC_MSG_ERROR(Install pkg-config and retry.) +fi + +dnl Checks for libraries. + +dnl Checks for header files. +dnl need largefile check here, dont remove, it defines magic macros +AC_SYS_LARGEFILE + +dnl Checks for library functions. +AC_MSG_CHECKING([for static_assert support]) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ + static_assert(sizeof(char) == 1, "sizeof doesn't work"); +]])], [ + AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_STATIC_ASSERT) +], [ + AC_MSG_RESULT([no]) +]) + +AC_MSG_CHECKING([for assignable stderr]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ + #include + int main(void) + { + stderr = NULL; + return 0; + } + ])], + [ + AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_ASSIGNABLE_STDERR) + ], + [ + AC_MSG_RESULT([no]) + ] +) + +AC_MSG_CHECKING([for optreset]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ + #include + int main(void) + { + optreset = 1; + return 0; + } + ])], + [ + AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_OPTRESET) + ], + [ + AC_MSG_RESULT([no]) + ] +) + +AC_CHECK_LIB(rt, shm_open) +dnl below defines HAVE_xxx so needed even if AC_CHECK_LIB() succeeded +AC_CHECK_FUNCS([shm_open memfd_create fopencookie sigtimedwait closefrom]) +AC_CHECK_FUNCS([setxattr], [ + USE_XATTRS=1 + AC_SUBST(USE_XATTRS) +]) +AC_CHECK_FUNCS([timerfd_create], [ + USE_EVTIMER_FD=1 + AC_SUBST(USE_EVTIMER_FD) +], [AC_CHECK_FUNCS([kqueue], [ + USE_EVTIMER_FD=1 + AC_SUBST(USE_EVTIMER_FD) +])]) +AC_CHECK_FUNCS([mremap]) +AC_CHECK_FUNCS([fls]) + +AC_CHECK_DECLS([F_OFD_SETLK], [ + USE_OFD_LOCKS=1 + AC_SUBST(USE_OFD_LOCKS) + ],, [[#include ]] +) +AC_CHECK_DECLS([MREMAP_MAYMOVE],,, [[#include ]]) +AC_CHECK_DECLS([MADV_POPULATE_WRITE, MADV_COLD],,, + [[#include ]]) + +AC_CHECK_TYPES([__float80],, [ + if test "$machine" != "i386" -a "$machine" != "x86_64"; then + USE_SOFTFLOAT=1 + AC_SUBST(USE_SOFTFLOAT) + fi + ] +) + +AC_CHECK_FUNC(strlcpy,, [ + PKG_CHECK_MODULES([LIBBSD], [libbsd], [ + dnl re-check for proper arch (multilib) + AC_CHECK_LIB(bsd, strlcpy,, [AC_MSG_ERROR(libbsd not found)]) + ] +)]) + +dnl Here is where we do our stuff + +AC_ARG_WITH(confdir, [AS_HELP_STRING([--with-confdir=dir], + [directory suffix under sysconfdir. default: dosemu])], + confdir="$withval", confdir="dosemu") +AC_ARG_WITH(x11fontdir, [AS_HELP_STRING([--with-x11fontdir=dir], + [directory to install the VGA X11 font. default: ${datadir}/dosemu/Xfonts])], + x11fontdir="$withval", x11fontdir="${datadir}/dosemu/Xfonts") +AC_ARG_WITH(ttffontdir, [AS_HELP_STRING([--with-ttffontdir=dir], + [directory to install the ttf font. default: ${datadir}/fonts/oldschool])], + ttffontdir="$withval", ttffontdir="${datadir}/fonts/oldschool") +AC_ARG_WITH(fdtarball, [AS_HELP_STRING([--with-fdtarball=file], + [path to FreeDOS tarball])], + fdtarball="$withval") + +# Hard-code this. It is evaluated at run-time as ../lib/dosemu, +# so we do not use libdir here (which can be lib64 or something unrelated) +plugindir="\${prefix}/lib/dosemu" + +AC_SUBST(confdir) +AC_SUBST(plugindir) +AC_SUBST(x11fontdir) +AC_SUBST(ttffontdir) +AC_SUBST(syshdimagedir) +AC_SUBST(fdtarball) + +AC_DEFINE_DIR([SYSCONFDIR], [sysconfdir], [ System config dir ]) +AC_DEFINE_DIR([CONFSUBDIR], [confdir], [ dosemu-specific config subdir ]) +AC_DEFINE_DIR([PREFIX], [prefix], [ prefix ]) +AC_DEFINE_DIR([LIBEXECDIR], [libexecdir], [ Where extra executables are placed to ]) +AC_DEFINE_DIR([SYSTEM_XFONTS_PATH], [x11fontdir], [ Directory for x11 fonts ]) + +DOSEMU_CPPFLAGS="$DOSEMU_CPPFLAGS -imacros config.hh" +DOSEMU_CFLAGS="$DOSEMU_CFLAGS -Wall -Wstrict-prototypes -Wmissing-declarations \ +-Wnested-externs -fms-extensions -pthread \ +-Wno-unused-result -Wcast-qual -Wwrite-strings -Wstrict-aliasing=2 -Wundef" +AX_CHECK_COMPILE_FLAG([-Waddress-of-packed-member], + [DOSEMU_CFLAGS="$DOSEMU_CFLAGS -Wno-address-of-packed-member"],, [-Werror]) +AX_CHECK_COMPILE_FLAG([-Wstring-plus-int], + [DOSEMU_CFLAGS="$DOSEMU_CFLAGS -Wno-string-plus-int"],, [-Werror]) +DOSEMU_LDFLAGS="-pthread" + +AC_CHECK_LIB(m, pow) + +AC_ARG_ENABLE(dlplugins, + AS_HELP_STRING([--disable-dlplugins], [do NOT use dynamically loaded plugins])) +if test "$enable_dlplugins" != "no"; then + AC_MSG_NOTICE(Using dynamically loaded plugins...) + AC_DEFINE(USE_DL_PLUGINS) + USE_DL_PLUGINS=1 + AC_SUBST(USE_DL_PLUGINS) + DOSBIN_LDFLAGS="-rdynamic" + AC_SUBST(DOSBIN_LDFLAGS) + AC_CHECK_LIB(dl, dlopen) +else + AC_MSG_NOTICE(Not using dynamically loaded plugins...) +fi + +SIG_PROTO_PFX="__attribute__((no_instrument_function))" +dnl check for stack protector disabling functionaity +AC_MSG_CHECKING([for __attribute__((optimize("no-stack-protector")))]) +AX_SAVE_FLAGS([attr_check]) +CFLAGS="-Wall -Werror -fstack-protector-all" +AC_COMPILE_IFELSE([AC_LANG_SOURCE([ + void foo(void); + __attribute__((optimize("no-stack-protector"))) + void foo(void) + { + } + ])], + [ + AC_MSG_RESULT([supported]) + SIG_PROTO_PFX="$SIG_PROTO_PFX __attribute__((optimize(\"no-stack-protector\")))" + ], + [ + AC_MSG_RESULT([unsupported]) + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -fno-stack-protector" + ]) +AX_RESTORE_FLAGS([attr_check]) +AC_DEFINE_UNQUOTED(SIG_PROTO_PFX, $SIG_PROTO_PFX) + +dnl Check whether we have pthreads and whether to use it +AC_CHECK_LIB(pthread, pthread_create,, [ + dnl see if the func is provided nevertheless (not needing -lpthread) + AC_CHECK_FUNC(pthread_create,, [ + AC_MSG_ERROR([No libpthread found, please install glibc-devel package]) + ] +)]) +AC_CHECK_FUNC(pthread_cancel,, [ + AC_CHECK_HEADERS([bthread.h],, [ + AC_MSG_ERROR([pthread_cancel() missing, install libbthread]) + ]) +]) +AC_CHECK_LIB(bthread, pthread_cancel) + +AC_CHECK_FUNCS([pthread_getname_np pthread_setname_np]) +AC_CHECK_FUNCS([pthread_attr_setsigmask_np pthread_setattr_default_np]) + +AC_CHECK_HEADER([wordexp.h],, [ + AC_MSG_ERROR([wordexp.h missing, install libwordexp]) +]) +AC_CHECK_LIB(wordexp, wordexp) + +AC_CHECK_HEADERS([scsi/sg.h linux/cdrom.h sys/io.h]) +AC_CHECK_HEADERS([netipx/ipx.h linux/ipx.h netpacket/packet.h]) +AC_CHECK_HEADERS([asm/ucontext.h],,, [ + #include + #include +]) +AC_CHECK_HEADERS([ucontext.h],, [ + AS_UNSET([ac_cv_header_ucontext_h]) + AC_CHECK_HEADER([ucontext.h], + AC_DEFINE([_XOPEN_SOURCE], [500], [Use X/Open 5 with POSIX 1995]), + AC_MSG_ERROR([Can't use ucontext.h]), [ + #define _XOPEN_SOURCE 500 +])]) +AC_CHECK_HEADERS([linux/signal.h],,, [#include ]) +AC_CHECK_HEADERS([sys/soundcard.h], [ + USE_OSS=1 + AC_SUBST(USE_OSS) +]) +DATE_FMT="%F %T %z" +test -n "$SOURCE_DATE_EPOCH" || SOURCE_DATE_EPOCH=`date +%s` +CONFIG_TIME=`date -u -d "@$SOURCE_DATE_EPOCH" "+$DATE_FMT" 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" "+$DATE_FMT" 2>/dev/null || date -u "+$DATE_FMT"` +AC_DEFINE_UNQUOTED(CONFIG_HOST, "$CONFIG_HOST") +AC_DEFINE_UNQUOTED(CONFIG_TIME, "$CONFIG_TIME") + +dnl enable EXPERIMENTAL stuff +AC_ARG_ENABLE(experimental, + AS_HELP_STRING([--enable-experimental], [enable configuration of EXPERIMENTAL stuff])) +if test "$enable_experimental" = "yes"; then + AC_MSG_NOTICE(Allowing EXPERIMENTAL stuff to be configured...); + AC_DEFINE([EXPERIMENTAL], 1, [ Define this to enable experimental stuff ]) +else + AC_MSG_NOTICE(EXPERIMENTAL stuff disabled...); +fi + +dnl try to hook in available plug-ins +AC_ARG_ENABLE(plugins, + AS_HELP_STRING([--enable-plugins=list], + [comma-separated list of plugins, use + to append])) +def_plugins=`cat $srcdir/plugin_list | sed 's/$/,/g' | tr -d '\n' | sed 's/,$//'` +if test -z "$enable_plugins"; then + enable_plugins="$def_plugins" +fi +if test "${enable_plugins#\+}" != "$enable_plugins"; then + enable_plugins="$def_plugins,${enable_plugins#\+}" +fi +if test "$enable_plugins" = "no"; then + enable_plugins="" +fi +AC_ARG_WITH(plugin-options, + [AS_HELP_STRING([--with-plugin-options=PLUGIN,OPTS], + [Pass options OPTS to plugin PLUGIN])], + [ + plu=`echo $withval |cut -d "," -f 1` + val=`echo $withval |cut -d "," -f 2` + export ${plu}_OPTS="$val" + ] +) + +if ! test -f Makefile.conf.in; then + AC_MSG_NOTICE([Populating makefiles...]) + wd=`pwd` + abssrcdir=`cd $srcdir && pwd` + if test "${wd#$abssrcdir}" != "$wd" ; then + builddir=".${wd#$abssrcdir}" + else + builddir="$wd" + fi + mkdir -p `(cd $abssrcdir; find doc man etc src test -type d -print)` + for i in `(cd $abssrcdir; find . -path $builddir -prune -o -name Makefile -print -o -name '*.mak' -print -o -name '*.bat' -print -o -name '*.sys' -print)`; do + echo $LN_S -f $abssrcdir/${i#./} $i + $LN_S -f $abssrcdir/${i#./} $i + done + for i in `(cd $abssrcdir; find man -name '*.1')`; do + cp $abssrcdir/$i $i + done +fi +AC_MSG_NOTICE([Creating plug-in hooks...]) +PLUGINSUBDIRS= +ST_PLUGINSUBDIRS= +$srcdir/scripts/mkpluginhooks clean +for i in $(echo $enable_plugins | tr ',' ' '); do + if test -z "$i"; then continue; fi + if $srcdir/scripts/plugctl.sh $i yes --disable-option-checking $ac_configure_args; then + PLUGINSUBDIRS="$PLUGINSUBDIRS plugin/$i" + if ! $EGREP 'USE_DL_PLUGINS|NO_LIB' $srcdir/src/plugin/$i/Makefile >/dev/null ; then + ST_PLUGINSUBDIRS="$ST_PLUGINSUBDIRS plugin/$i" + fi + fi +done +PLUGINS=`echo $PLUGINSUBDIRS | $SED 's/plugin\//\n\t/g'` + +FDPP_PLU=`echo $PLUGINS | $EGREP 'fdpp'` +AC_ARG_ENABLE(fdpp, + [AS_HELP_STRING([--disable-fdpp], [compile without fdpp support])],, + enable_fdpp="yes" +) + +AC_ARG_ENABLE(system_wa, + AS_HELP_STRING([--disable-system-wa], [compile without system-specific + work-arounds (mostly for linux kernel) that are not needed on this system])) +AS_IF([test "x$enable_system_wa" = "xno"], [ + AC_MSG_NOTICE(Disabling system-specific work-arounds) + AC_DEFINE(DISABLE_SYSTEM_WA) + AS_IF([test -z "$WARN_OUTDATED_WA" -a -n "$WARN_UNDISABLED_WA"], [ + AC_DEFINE(WARN_UNDISABLED_WA)]) +]) +AS_IF([test -n "$WARN_OUTDATED_WA"], [ + AC_MSG_NOTICE(Enabling compile-time warnings for outdated work-arounds) + AC_DEFINE(WARN_OUTDATED_WA) +]) + +dnl Do compilation for GDB +HAVE_LIBBFD=0 +AC_ARG_ENABLE(debug, + AS_HELP_STRING([--enable-debug], [compile with debug info])) +if test "$enable_debug" != "no"; then + AC_MSG_NOTICE(Compiling with debug info...) + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -ggdb3" + if test "$use_clang" = "yes" ; then + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -fdebug-macro" + fi + AC_ARG_ENABLE(xbacktrace, AS_HELP_STRING([--enable-xbacktrace], + [enable extended backtrace functionality])) + if test "$enable_xbacktrace" = "yes"; then + AC_CHECK_HEADER(bfd.h, [ + AC_CHECK_LIB(z, inflate) + dnl check iberty before bfd - order matters + AC_CHECK_LIB(iberty, lrealpath) + AC_CHECK_LIB(bfd, bfd_init) + if test "$ac_cv_lib_bfd_bfd_init" = "yes"; then + HAVE_LIBBFD=1 + AC_DEFINE(HAVE_LIBBFD) + fi + ]) + if test "$HAVE_LIBBFD" = "1"; then + AC_MSG_NOTICE(Enabling extended backtrace functionality) + else + AC_MSG_WARN(libbfd not available, no extended backtrace functionality) + fi + fi + AC_CHECK_HEADER([execinfo.h], [ + AC_DEFINE(HAVE_EXECINFO) + if test "$HAVE_LIBBFD" != "1"; then + DOSEMU_LDFLAGS="$DOSEMU_LDFLAGS -rdynamic" + fi + AC_CHECK_FUNCS([backtrace backtrace_symbols]) + ]) +else + AC_MSG_NOTICE(Compiling without debug info...) +fi + +AC_ARG_ENABLE(profile, + AS_HELP_STRING([--enable-profile], [compile with profile info])) + +AC_ARG_ENABLE(asan, [AS_HELP_STRING(--enable-asan, [enable address sanitizer])]) +AC_ARG_ENABLE(lsan, [AS_HELP_STRING(--enable-lsan, [enable leak sanitizer])]) +AC_ARG_ENABLE(tsan, [AS_HELP_STRING(--enable-tsan, [enable thread sanitizer])]) +AC_ARG_ENABLE(ubsan, [AS_HELP_STRING(--enable-ubsan, [enable UB sanitizer])]) + +AC_SUBST(OPTIONALSUBDIRS) +AC_SUBST(REQUIRED) +AC_SUBST(PLUGINSUBDIRS) +AC_SUBST(ST_PLUGINSUBDIRS) +RELEASE_DATE=`cd $srcdir && ./getversion -d` +AC_SUBST(RELEASE_DATE) + +dnl CPU emulator +AC_ARG_ENABLE(cpuemu, [AS_HELP_STRING([--disable-cpuemu], + [do NOT compile with optional x86 emulation code])]) +if test "$enable_cpuemu" != "no"; then + AC_MSG_NOTICE(Compiling with x86 emulator) + AC_DEFINE(X86_EMULATOR) + X86_EMULATOR=1 + AC_SUBST(X86_EMULATOR) + + if test "$machine" = "i386" -o "$machine" = "x86_64"; then + AC_ARG_ENABLE(cpuemu_jit, [AS_HELP_STRING([--disable-cpuemu-jit], + [do NOT compile with optional x86 JIT])]) + if test "$enable_cpuemu_jit" != "no"; then + AC_MSG_NOTICE(Compiling with x86 JIT) + AC_DEFINE(X86_JIT) + X86_JIT=1 + AC_SUBST(X86_JIT) + fi + fi +fi + +AC_ARG_WITH(target_cpu, [AS_HELP_STRING([--with-target-cpu=CPU], + [use the specified target CPU. default=auto])]) +if test "$with_target_cpu" = ""; then + AC_MSG_NOTICE(Compiling with default target CPU...) + target_cpu="" +else + AC_MSG_NOTICE(Compiling with specified target CPU...) + target_cpu=$with_target_cpu +fi + +if test "$machine" = "i386" -o "$with_target_bits" = "32" -o \ + "$enable_profile" = "yes"; then + AC_MSG_NOTICE(Compiling as non-PIE...) + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -fno-pie" + AX_CHECK_COMPILE_FLAG([-no-pie], + [DOSEMU_LDFLAGS="$DOSEMU_LDFLAGS -no-pie"]) +else + AC_MSG_NOTICE(Compiling as PIE for $machine...) + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -fpie" + DOSEMU_LDFLAGS="$DOSEMU_LDFLAGS -pie" +fi + +if test "$CONFIG_HOST" = "Linux"; then + if test "$machine" = "i386" -o "$machine" = "x86_64"; then + AC_ARG_ENABLE(dnative, [AS_HELP_STRING([--disable-dnative], + [do NOT compile with native DPMI support])]) + if test "$enable_dnative" != "no"; then + AC_MSG_NOTICE([Enabling native DPMI support...]) + AC_DEFINE(DNATIVE) + DNATIVE=1 + AC_SUBST(DNATIVE) + fi + + AC_DEFINE(USE_KVM) + KVM=1 + AC_SUBST(KVM) + fi +fi + +if test "$CONFIG_HOST" = "Darwin"; then + DOSEMU_LDFLAGS="$DOSEMU_LDFLAGS -Wl,-undefined,dynamic_lookup" +fi + +AC_ARG_ENABLE(mcontext, + AS_HELP_STRING([--disable-mcontext], [disable fast context switch])) +if test "$enable_mcontext" != "no"; then + AC_MSG_NOTICE([Enabling fast context switching...]) + AC_DEFINE(MCONTEXT) + MCONTEXT=1 + AC_SUBST(MCONTEXT) +fi + +if test "$enable_asan" = "yes"; then + AX_CHECK_LINK_FLAG([-fsanitize=address], [ + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -fsanitize=address" + # working around this bug: + # https://github.com/google/sanitizers/issues/934 + # Linking to libstdc++ seems to be the cure. + LDFLAGS="$LDFLAGS -fsanitize=address -lstdc++" + AC_MSG_NOTICE(enabling address sanitizer) + ]) +fi +if test "$enable_lsan" = "yes"; then + AX_CHECK_LINK_FLAG([-fsanitize=leak], [ + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -fsanitize=leak" + LDFLAGS="$LDFLAGS -fsanitize=leak" + AC_MSG_NOTICE(enabling leak sanitizer) + ]) +fi +if test "$enable_tsan" = "yes"; then + AX_CHECK_LINK_FLAG([-fsanitize=thread], [ + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -fsanitize=thread" + LDFLAGS="$LDFLAGS -fsanitize=thread" + AC_MSG_NOTICE(enabling thread sanitizer) + ]) +fi +if test "$enable_ubsan" = "yes"; then + AX_CHECK_LINK_FLAG([-fsanitize=undefined], [ + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -fsanitize=undefined -fno-sanitize=alignment" + LDFLAGS="$LDFLAGS -fsanitize=undefined" + AC_MSG_NOTICE(enabling UB sanitizer) + ]) +fi +if test "$enable_profile" = "yes"; then + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -pg -no-pie" + LDFLAGS="$LDFLAGS -pg -no-pie" + AC_MSG_NOTICE(enabling profiling) +fi + +AC_ARG_ENABLE(optimization, + AS_HELP_STRING([--disable-optimization], [disable optimization])) +if test "$enable_optimization" = "no" ; then + if test "$enable_debug" = "yes"; then + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -Og" + AC_MSG_NOTICE(optimization set to -Og) + else + # simx86 is too slow on gcc without -O + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -O0" + AC_MSG_NOTICE(optimization set to -O0) + fi +elif test -z "$CFLAGS" ; then + DOSEMU_CFLAGS="$DOSEMU_CFLAGS -O2" + AC_MSG_NOTICE(optimization set to -O2) +fi + +DOSEMU_CFLAGS="${DOSEMU_CFLAGS} ${OPT} ${PIPE}" +DOSEMU_CPPFLAGS="${DOSEMU_CPPFLAGS} -MD -DCFLAGS_STR=\"$DOSEMU_CFLAGS $CFLAGS\"" +DOSEMU_VERSION=`cd $srcdir && ./getversion -b` +AS_LDFLAGS="-melf_i386" + +AC_SUBST(XASFLAGS) +AC_SUBST(ASFLAGS) +AC_SUBST(LDFLAGS) +AC_SUBST(AS_LDFLAGS) +AC_SUBST(DOSEMU_CFLAGS) +AC_SUBST(DOSEMU_CPPFLAGS) +AC_SUBST(DOSEMU_LDFLAGS) +AC_SUBST(LIBS) +AC_SUBST(HAVE_LIBBFD) +AC_SUBST(HAVE_LIBBSD) +AC_SUBST(DOSEMU_VERSION) + +cmds_rev=3 +cmds_ver=0.$cmds_rev +cmdsuff=$PACKAGE_TARNAME-cmds-$cmds_ver +AC_DEFINE_UNQUOTED(CMDS_SUFF, "$cmdsuff") +AC_DEFINE_UNQUOTED(CMDS_REV, $cmds_rev) +AC_SUBST(cmdsuff) + +dnl Print some warnings (if neccessary) +if test "$enable_fdpp" != "no" -a -z "$FDPP_PLU"; then + AC_MSG_ERROR([fdpp not available!]) +fi + +dnl Create output files. If you add new ones, please do it in order. +man_files=`cd $srcdir && find man -name '*.in' | sed 's/\.in$//'` +AC_DEFUN([AC_DATAROOTDIR_CHECKED]) +adl_RECURSIVE_EVAL([$bindir], [e_bindir]) +adl_RECURSIVE_EVAL([$datadir], [e_datadir]) +AC_SUBST([e_bindir]) +AC_SUBST([e_datadir]) +AC_CONFIG_FILES([Makefile.conf $man_files etc/dosemu.desktop]) + +AC_OUTPUT + +AC_MSG_RESULT() +AC_MSG_RESULT(Enabling plugins: $PLUGINS) +VID_PLU=`echo $PLUGINS | $EGREP 'sdl|X|term|sdl1'` +if test -z "$VID_PLU"; then + AC_MSG_RESULT() + AC_MSG_WARN([No video plugins available!]) +fi +AUD_PLU=`echo $PLUGINS | $EGREP 'sdl|ao|sdl1'` +if test -z "$AUD_PLU"; then + AC_MSG_RESULT() + AC_MSG_WARN([No audio plugins available!]) +fi +if test -z "$FDPP_PLU"; then + AC_MSG_RESULT() + AC_MSG_WARN([fdpp not available!]) +fi + +AH_TEMPLATE([HAVE_ASSIGNABLE_STDERR], +[ Define if stderr is not const ]) + +AH_TEMPLATE([HAVE_OPTRESET], +[ Define if optreset is available ]) + +AH_TEMPLATE([CONFIG_HOST], +[ Define the host for which Dosemu is configured ]) + +AH_TEMPLATE([CONFIG_TIME], +[ Define the configure time ]) + +AH_TEMPLATE([SIG_PROTO_PFX], +[ Define the signal handling function prototype prefix ]) + +AH_TEMPLATE([HAVE_STD_C11], +[Define this if your compiler supports c11]) + +AH_TEMPLATE([HAVE_STATIC_ASSERT], +[Define this if your glibc defines static_assert]) + +AH_TEMPLATE(HAVE_LIBBFD, +[Define this if you have binutils-devel installed]) + +AH_TEMPLATE(HAVE_LIBBSD, +[Define this if you have bsd-devel installed]) + +AH_TEMPLATE(HAVE_EXECINFO, +[Define this if you have execinfo.h in libc]) + +AH_TEMPLATE([USE_DL_PLUGINS], +[ DEFINE this, if you want dynamically loaded plugins ]) + +AH_TEMPLATE([X86_EMULATOR], +[ Define this to use the X86 CPU emulator ]) + +AH_TEMPLATE([X86_JIT], +[ Define this to use the X86 JIT ]) + +AH_TEMPLATE([DNATIVE], +[ Define this to enable native DPMI back-end ]) + +AH_TEMPLATE([USE_KVM], +[ Define this to enable kvm support ]) + +AH_TEMPLATE([MCONTEXT], +[ Define this to enable libmcontext use ]) + +AH_TEMPLATE([DISABLE_SYSTEM_WA], +[ Define this to disable system-specific work-arounds that are +unneeded on your system ]) + +AH_TEMPLATE([WARN_UNDISABLED_WA], +[ Define this to enable compile-time warnings for system-specific +work-arounds that were failed to disable ]) + +AH_TEMPLATE([WARN_OUTDATED_WA], +[ Define this to enable compile-time warnings for outdated +system-specific work-arounds ]) + +AH_TEMPLATE([CMDS_SUFF], +[ Commands directory suffix ]) + +AH_TEMPLATE([CMDS_REV], +[ Commands revision num ]) diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..c2830db --- /dev/null +++ b/debian/changelog @@ -0,0 +1,29 @@ +dosemu2 (2.0~pre9-1) focal; urgency=low + + * pre9 + + -- Stas Sergeev Sat, 29 Jan 2022 23:00:00 +0300 + +dosemu2 (2.0~pre8-2) disco; urgency=low + + * fixing deps + + -- Stas Sergeev Mon, 25 Feb 2019 15:00:00 +0300 + +dosemu2 (2.0~pre8-1) artful; urgency=low + + * pre8 + + -- Stas Sergeev Mon, 27 Nov 2017 21:00:00 +0300 + +dosemu2 (2.0~pre7-1) xenial; urgency=low + + * pre7 + + -- Stas Sergeev Fri, 31 Mar 2017 23:00:00 +0300 + +dosemu2 (2.0~pre6-1) xenial; urgency=low + + * Initial Release + + -- Stas Sergeev Mon, 12 Sep 2016 18:00:00 +0300 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +12 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..d850d7e --- /dev/null +++ b/debian/control @@ -0,0 +1,58 @@ +Source: dosemu2 +Section: otherosfs +Priority: optional +Maintainer: Stas Sergeev +Standards-Version: 4.1.5 +Build-Depends: + git (>= 2.0), + autoconf, + autotools-dev, + automake, + make, + linuxdoc-tools, + bison, + debhelper (>= 9~), + flex, + gawk, + libx11-dev, + libxext-dev, + libslang2-dev, + xfonts-utils, + libgpm-dev, + libasound2-dev, + libsdl2-dev, + libsdl2-ttf-dev, + libfontconfig1-dev, + ladspa-sdk, + libfluidsynth-dev, + libao-dev, + libieee1284-3-dev, + libslirp-dev, + libbsd-dev, + libreadline-dev, + libjson-c-dev, + libb64-dev, + binutils-dev, + pkg-config, + fdpp-dev, + dj64-dev, + clang, + binutils-i686-linux-gnu +Homepage: https://github.com/dosemu2/dosemu2 + +Package: dosemu2 +Replaces: dosemu +Conflicts: dosemu +Architecture: any +Depends: + ${misc:Depends}, + ${shlibs:Depends}, + fdpp, + dj64, + comcom64 +Recommends:${shlibs-:Recommends}, ladspa-sdk, gdb, kbd, fluid-soundfont-gm, + install-freedos +Suggests: vde2, valgrind, install-otherdos +Description: fast and secure DOS emulator + dosemu2 is an emulator for running DOS programs under linux. + It can also serve as a VM to boot various DOSes. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..3e7430a --- /dev/null +++ b/debian/copyright @@ -0,0 +1,341 @@ + + Copyright of DOSEMU2, October 2014 + ================================== + +1. + 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 2 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +2. All dosemu2 sources that have no explicit copyright statement, + are copyrighted under GPLv2. + +3. Parts of the code not covered by the GPL are marked explicitly + within the code, and/or their copyrights are at the end of this + file. + +4. There are no restrictions to run any (proprietary or free) DOS software + under dosemu, unless the license of that software says otherwise. + +The copyrights referred to in clause 2: + +--- GPLv2 (used as a default license in older dosemu releases) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2, as + published by the Free Software Foundation. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +The copyrights referred to in clause 3 are from: + +--- The Mach DOS Emulator + +Copyright (c) 1991 Carnegie Mellon University +All Rights Reserved. + +Permission to use, copy, modify and distribute this software and its +documentation is hereby granted, provided that both the copyright +notice and this permission notice appear in all copies of the +software, derivative works or modified versions, and any portions +thereof, and that both notices appear in supporting documentation. + +CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" +CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR +ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + +Carnegie Mellon requests users of this software to return to + + Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + School of Computer Science + Carnegie Mellon University + Pittsburgh PA 15213-3890 + +any improvements or extensions that they make and grant Carnegie Mellon +the rights to redistribute these changes. + +--- XFree86 (mouse code) + +Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. +Copyright 1993 by David Dawes + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation, and that the names of Thomas Roell and David Dawes not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. Thomas Roell +and David Dawes makes no representations about the suitability of this +software for any purpose. It is provided "as is" without express or +implied warranty. + +THOMAS ROELL AND DAVID DAWES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS +SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL THOMAS ROELL OR DAVID DAWES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +--- Doug Lea's malloc in src/base/misc/dlmalloc.c + +This is a version (aka dlmalloc) of malloc/free/realloc written by +Doug Lea and released to the public domain, as explained at +http://creativecommons.org/licenses/publicdomain. Send questions, +comments, complaints, performance data, etc to dl@cs.oswego.edu + +--- The VGA fonts in src/env/video/vgafonts.c (copyleft_vgafonts.txt) + +This *compilation* is (c) Copyright 1991,1992 Joseph (Yossi) Gil. +Permission is granted to use and redistribute the files comprising +this collection in any way (including conversion to another format), +provided that my name and addresses and this notice is preserved. + +Simple (dare I say trivial?) bitmapped screen fonts such as the ones +included in this collection cannot be copyrighted. In general, one can +only copyright programs that generate fonts. This is why postscript +fonts are copyrightable. For more details refer to discussions various +"legal" newsgroups. In addition, I have included a relevant excerpt +from the FAQ of comp.fonts at the bottom of this document. + +No one can claim any copyright on the fonts in this archive. They +have been collected from numerous sources. Legally speaking, you are +*free* to do with the individual fonts whatever you like. Individual +fonts are in the public domain. I do ask that you will kindly refrain +from causing confusion by distributing modified versions of the fonts +contained in this collection. + +Please send any all your EGA/VGA text mode fonts contributions to me +rather than distributing a modified version of this collection. I +will add your fonts to the next edition of this collection and happily +acknowledge your help. Your cooperation will enable us all to benefit +from your contribution. See the file LOOKING4.TXT for more details. + +I am trying to keep track of the origins of these fonts. See the file +FONTORIG.TXT. Unfortunately, I only started to record this information +on version 1.2. Records of origin of earlier fonts are missing. +If you know the origin of any of the fonts here, please drop me a note. + +Staring on version 1.6 the collection also includes some of the +miscellaneous utilities which I use for preparing it. Among these +you will find programs for loading, viewing, trimming and otherwise +manipulating the fonts. These utilities are also distributed as a +separate archive called fntutlXX.ZIP where XX is the version number. +All the utilities require no shareware payment. Restrictions on +distribution and usage are only to the extent necessary to protect +the free distribution. + +I see this is as my pleasant duty to pay tribute to the following +individuals who communicated and contributed to this archive: + + Dov Grobgeld + Angelos Karageorgiou , + Alexandre (Alex) Khalil <9999SC01@DT3.DT.UH.EDU>, + Patrick Arzul + Mike Threepoint + Glaude David [Glu] + Jean-Marc Lasgouttes + Itamar Even-Zohar + A.Weeks%bath.ac.uk@ib.rl.ac.uk + Miguel Farah. + + +This collection would not have been what it today is without their +help! + + +Author's Address +================ +E-mail internet address: yogi@cs.technion.ac.il + +Alternate E-mail addresses: yogi@cs.ubc.ca, yogi@umiacs.umd.edu. + +Permanent mailing address is: + Joseph Gil, P.O. Box 3148, Jerusalem, Israel. + +Hebrew mailing address (you cannot read the following unless +your screen adapter can display Hebrew character): + ליג יסוי + 3148 .ד.ת + םילשורי + +-------------------------------------------------------------------------- +From comp.fonts Sat Sep 5 11:12:35 1992 + walsh@cs.umass.edu (Norman Walsh) +Newsgroups: comp.fonts Subject: FAQ: Part-I: General Info Message-ID: + Date: 4 Sep 92 19:32:07 GMT +Reply-To: walsh@cs.umass.edu Organization: Dept of Comp and Info Sci, +Univ of Mass (Amherst) + +FAQ for comp.fonts: Part I: General Info + +Maintained by Norm Walsh and + Bharathi Jagadeesh + +Version 0.0.3, Release 04SEP92 + +Welcome to the comp.fonts FAQ. This article, posted monthly, describes +many of the basic questions that seem to be repeated frequently on +comp.fonts. Your comments are both welcome and encouraged. + + Standard disclaimers apply. + +.... + At one level, there are two major sorts of fonts: bitmapped and + outline (scalable). Bitmapped fonts are falling out of fashion + as various outline technologies grow in popularity and support. + + Bitmapped fonts represent each character as a rectangular grid of + pixels. The bitmap for each character indicates precisely what + pixels should be on and off. Printing a bitmapped character is + simply a matter of blasting the right bits out to the printer. + There are a number of disadvantages to this approach. The bitmap + represents a particular instance of the character at a particular + size and resolution. It is very difficult to change the size, + shape, or resolution of a bitmapped character without significant + loss of quality in the image. On the other hand, it's easy to do + things like shading and filling with bitmapped characters. + +..... + +5. Are fonts copyrightable? + + This topic is hotly debated at regular intervals on comp.fonts. + Terry Carroll provides the following + analysis of current [ed: as of 6/92] legislation and regulation + regarding fonts and copyrights. Members of the comp.fonts community + are encouraged to submit other materials that add clarity to the + issue. + + *-[Quote]-----------------------------------------------------------* + + First, the short answer: Typefaces are not copyrightable; bitmapped + fonts are not copyrightable, but scalable fonts are copyrightable. + Authorities for these conclusions follow. + + Before we get started, let's get some terminology down: + + A typeface is a set of letters, numbers, or other symbolic + characters, whose forms are related by repeating design elements + consistently applied in a notational system and are intended to be + embodied in articles whose intrinsic utilitarian function is for use + in composing text or other cognizable combinations of characters. + + A font is the computer file or program that is used to represent + or create the typeface. + + Now, on to the legal authorities: + + Volume 37 of the Code of Federal Regulations specifies this about + the copyrightability of typefaces: + + "The following are examples of works not subject to copyright and + applications for registration of such works cannot be entertained: + . . . typeface as typeface" 37 CFR 202.1(e). + + By the way, you won't find that in the most recent (7/1/91) edition + of the CFR; the addition was enacted 2/21/92. It'll be in the + next edition, though. It's described in the 2/21/92 edition of + the Federal Register, page 6201 (57 FR 6201). The change didn't + actually change the law, it just clarified it, and codified existing + Copyright Office policy. + + The regulation is in accordance with the House of Representatives + report that accompanied the new copyright law, when it was passed + in 1976: + + "The Committee has considered, but chosen to defer, the possibility + of protecting the design of typefaces. A 'typeface' can be defined + as a set of letters, numbers, or other symbolic characters, whose + forms are related by repeating design elements consistently applied + in a notational system and are intended to be embodied in articles + whose intrinsic utilitarian function is for use in composing text + or other cognizable combinations of characters. The Committee + does not regard the design of typeface, as thus defined, to be a + copyrightable 'pictoral, graphic, or sculptural work' within the + meaning of this bill and the application of the dividing line in + section 101." H. R. Rep. No. 94-1476, 94th Congress, 2d Session + at 55 (1976), reprinted in 1978 U.S. Cong. and Admin. News 5659, + 5668. + + It's also in accordance with the one court case I know of that + has considered the matter: Eltra Corp. V. Ringer, 579 F.2d 294, + 208 USPQ 1 (1978, C.A. 4, Va.). + + The Copyright Office holds that a bitmapped font is nothing more + than a computerized representation of a typeface, and as such is + not copyrightable: + + "The [September 29, 1988] Policy Decision [published at 53 FR 38110] + based on the [October 10,] 1986 Notice of Inquiry [published at 51 + FR 36410] reiterated a number of previous registration decisions + made by the [Copyright] Office. First, under existing law, typeface + as such is not registerable. The Policy Decision then went on + to state the Office's position that 'data that merely represents + an electronic depiction of a particular typeface or individual + letterform' [that is, a bitmapped font] is also not registerable." + 57 FR 6201. + + However, scalable fonts are, in the opinion of the Copyright + Office, computer programs, and as such are copyrightable: + + "... the Copyright Office is persuaded that creating scalable + typefonts using already-digitized typeface represents a + significant change in the industry since our previous [September + 29, 1988] Policy Decision. We are also persuaded that computer + programs designed for generating typeface in conjunction with low + resolution and other printing devices may involve original computer + instructions entitled protection under the Copyright Act. For + example, the creation of scalable font output programs to produce + harmonious fonts consisting of hundreds of characters typically + involves many decisions in drafting the instructions that drive the + printer. The expression of these decisions is neither limited by + the unprotectable shape of the letters nor functionally mandated. + This expression, assuming it meets the usual standard of authorship, + is thus registerable as a computer program." 57 FR 6202. + + *-[Unquote]---------------------------------------------------------* + + +------------------------------------------------------------------------------- +FLEXI IBM VGA9 FONTS: Scalable TrueType fonts based on the iconic hardware + VGA character set. + +INCLUDED FONTS: + +* Flexi IBM VGA9 True: Corrected aspect ratio, extended character set +* Flexi IBM VGA9 True 437: Corrected aspect ratio, CP437/DOS encoding +* Flexi IBM VGA9 False: Uncorrected aspect ratio, extended character set +* Flexi IBM VGA9 False 437: Uncorrected aspect ratio, CP437/DOS encoding + +LICENSE: + + These fonts are released under the Creative Commons Attribution-ShareAlike + 4.0 International license: http://creativecommons.org/licenses/by-sa/4.0/ + +_________________________________________ + +// VileR 2018-05 https://int10.org + +###### separator marking the end of COPYING.DOSEMU ###### + +On Debian systems, the complete text of the GNU General Public License version +2 can be found in `/usr/share/common-licenses/GPL-2'. diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..d7b4711 --- /dev/null +++ b/debian/docs @@ -0,0 +1,7 @@ +BUGS +CONTRIBUTING.md +COPYING +COPYING.DOSEMU +NEWS.md +README.md +THANKS diff --git a/debian/install b/debian/install new file mode 100644 index 0000000..ad80adc --- /dev/null +++ b/debian/install @@ -0,0 +1 @@ +etc/dosemu2.alias etc/X11/fonts/misc/ diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..34785c0 --- /dev/null +++ b/debian/rules @@ -0,0 +1,20 @@ +#!/usr/bin/make -f + +export DH_ALWAYS_EXCLUDE=fonts.dir:fonts.alias +export CC=clang + +%: + dh $@ --parallel --builddirectory=build + +override_dh_autoreconf: + ./autogen.sh + +override_dh_auto_configure: + dh_auto_configure -O--parallel -- \ + --with-x11fontdir=/usr/share/fonts/X11/misc + +override_dh_shlibdeps: + dh_shlibdeps -X.so + dh_shlibdeps -- -dRecommends -pshlibs- + +override_dh_dwz: diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/default-configure b/default-configure new file mode 100755 index 0000000..b376f3d --- /dev/null +++ b/default-configure @@ -0,0 +1,163 @@ +#! /bin/sh + +srcdir="`dirname "$0" | xargs realpath`" +CONF_FILE=compiletime-settings +if [ ! -f configure.ac ]; then + [ -f $CONF_FILE ] || cp "$srcdir"/$CONF_FILE . + [ -f $CONF_FILE.devel ] || cp "$srcdir"/$CONF_FILE.devel . +fi + +if [ "$1" != "" ]; then + if [ "${1#--}" = "$1" -a "${1#*=}" = "$1" ]; then + CONF_FILE=$1 + if [ "$CONF_FILE" = "-d" ]; then + CONF_FILE=compiletime-settings.devel + fi + shift + fi +fi + +if [ ! -f $CONF_FILE ]; then + echo "$CONF_FILE cannot be opened" + exit 1 +fi + +CONF=`cat $CONF_FILE` +CONF=`printf "$CONF"| sed '/^config {/d' | sed '/^}/d' | tr '\n' ' ' ` + +SUFFIX="" +while [ "$#" != "0" ]; do + case "$1" in + --enable-plugin*) + EXTRA_PLUGINS="$EXTRA_PLUGINS,${1#*=}" + ;; + --*) + SUFFIX="$SUFFIX $1" + ;; + *) + CONF="$CONF `echo $1 | tr '=' ' '`" + ;; + esac + shift +done + +set $CONF + +#echo "$*" + +STRING="" +PLUGIN=`cat $srcdir/plugin_list | sed 's/$/,/g' | tr -d '\n' | sed 's/,$//'` + +while [ "$1" != "" ]; do +# echo "$1 $2" + case "$1" in + experimental) + if [ "$2" = "on" ]; then STRING="$STRING --enable-experimental"; fi + ;; + debug) + if [ "$2" = "on" ]; then STRING="$STRING --enable-debug"; fi + ;; + asan) + if [ "$2" = "on" ]; then STRING="$STRING --enable-asan"; fi + ;; + lsan) + if [ "$2" = "on" ]; then STRING="$STRING --enable-lsan"; fi + ;; + tsan) + if [ "$2" = "on" ]; then STRING="$STRING --enable-tsan"; fi + ;; + ubsan) + if [ "$2" = "on" ]; then STRING="$STRING --enable-ubsan"; fi + ;; + optimize) + if [ "$2" = "off" ]; then STRING="$STRING --disable-optimization"; fi + ;; + system-wa) + if [ "$2" = "off" ]; then STRING="$STRING --disable-system-wa"; fi + ;; + cpuemu) + if [ "$2" = "off" ]; then STRING="$STRING --disable-cpuemu"; fi + ;; + dlplugins) + if [ "$2" = "off" ]; then STRING="$STRING --disable-dlplugins"; fi + ;; + x11fontdir) + STRING="$STRING --with-$1=$2" + ;; + fdtarball) + STRING="$STRING --with-$1=$srcdir/$2" + ;; + fdpp-build-path) + FDPP_STR="$FDPP_STR --with-$1=$2" + ;; + fdpp-include-path) + FDPP_STR="$FDPP_STR --with-$1=$2" + ;; + fdpp) + if [ "$2" = "off" ]; then + STRING="$STRING --disable-fdpp"; + PLUGIN=`echo "$PLUGIN" | sed -e s/fdpp//`; + fi + ;; + docdir|mandir|datadir|libdir|sysconfdir|bindir|prefix) + STRING="$STRING --$1=`echo $2`" + ;; + target_bits) + if [ "$2" != "auto" ]; then + if uname -m | grep -q 64; then + otherbits=64 + if cpp -dM /dev/null | grep -q __x86_64__; then + otherbits=32 + fi + if [ "$2" = "$otherbits" ]; then + STRING="$STRING --with-target-bits=$otherbits"; + fi + elif [ "$2" = "64" ]; then + # cross compilation on pure 32-bit machines + STRING="$STRING --with-target-bits=64 --host=x86_64-pc-linux-gnu"; + fi + fi + ;; + target_cpu) + if [ "$2" != "auto" ]; then STRING="$STRING --with-target-cpu=$2"; fi + ;; + plugin_*) + plugin=${1#plugin_} + if [ "$2" = "on" ]; then PLUGIN="$PLUGIN,$plugin"; fi + if [ "$2" = "off" ]; then + PLUGIN=`echo "$PLUGIN" | sed -e s/$plugin// -e s/,,/,/`; + fi + ;; + *) + ;; + esac + + shift; shift; +done + +[ -f "$srcdir"/install-sh ] || "$srcdir"/autogen.sh || exit $? +if [ -z "$MAKE" ]; then + if [ `uname -s` = "FreeBSD" ]; then + MAKE=gmake + else + MAKE=make + fi +fi +if [ `uname -s` = "FreeBSD" ]; then + export CPPFLAGS="${CPPFLAGS} -I/usr/local/include" + export LDFLAGS="${LDFLAGS} -L/usr/local/lib" +fi +${MAKE} "$srcdir"/configure + +STRING="$STRING --enable-plugins=$PLUGIN,$EXTRA_PLUGINS" +if [ -n "$FDPP_STR" ]; then + FDPP_STR=`echo $FDPP_STR | sed 's/^ *//'` + STRING="$STRING --with-plugin-options=fdpp,\"$FDPP_STR\"" +fi + +if [ -z "$CC" ] && which ccache; then + echo "Enabling ccache use" + export CC="ccache cc" +fi +echo exec "$srcdir"/configure $STRING $SUFFIX +eval exec "$srcdir"/configure $STRING $SUFFIX diff --git a/doc/EMUfailure.html b/doc/EMUfailure.html new file mode 100644 index 0000000..c126d79 --- /dev/null +++ b/doc/EMUfailure.html @@ -0,0 +1,607 @@ + +Known dosemu problems

Known dosemu problems

This file lists programs and groups of programs not running or running +only partially under dosemu. The most up-to-date version of this file +may be found on: +http://www.dosemu.org/. +Please report about possible additions to +linux-msdos@vger.kernel.org +or the SourceForge BTS at +http://www.dosemu.org/. +Perhaps your program can be made going +with the help of others. Have a look at the dosemu-howto how to do so.


1. Fundamental problems

Programs that don't work under the MSDOS Emulator and probably won't +ever work, because of fundamental problems. Some of these fundamental +problems result in these programs not being runnable on +Win3.x/Win95/WinNT and under OS/2 DOS box either. These programs +are characterized by using any of these features:


1.1. Virtual Control Program Interface (VCPI)

VCPI allows programs to run in ring 0. This is kernel mode in Linux +and not sensible.

Example: sim2181.exe from Analog Devices DSP Kit


1.2. Programs using older versions of the Pharlap Extender

Older versions of the Pharlap Extender (run286) need ring-0 access +under DOSEMU to install their own DPMI server. The use of proprietary +undocumented extensions to the DPMI protocol makes DOSEMU's DPMI server +unsuitable for this extender.

Example: Autocad Version 12c1 For DOS

Example: the game BioForge by Origin Systems.


1.3. Programs using the JEMM memory manager

The JEMM memory manager provides proprietary extensions to the EMS +protocol. These are not supported by DOSEMU.

Example: Wing Commander Privateer by Origin Systems


1.4. Does my failing program belong to these groups?

Check with "strings <program.exe> | less" if the program +contains some of these keywords: vcpi, RUN286.


1.5. Fundamental problem with the Linux kernel

The Programmable Interval Timer (PIT) can be programmed to produce +interrupts with frequencies up to almost 2MHz. Linux sets this to +only 100Hz (2.6 kernels can set it to 1KHz) and doesn't allow the +software to change that. This limits the minimal interval between subsequent +SIGALRM notifications for software that uses the setitimer(2) syscall. +To emulate the PIT frequencies that are higher than the frequency Linux +sets the PIT to, dosemu uses "interrupt bursts": on every SIGALRM +reception dosemu triggers the timer interrupt as many times as necessary +to compensate the gap since the previous SIGALRM reception. This allows +to keep a precise timing but causes problems for some programs. When +the timer interrupt handler is invoked more than once without letting +the main thread to execute, some programs can lock up. The game "Cosmo" is +one of those.

Another problem is that due to the aforementioned low timer frequency +dosemu is not able to properly emulate the timings for different +emulated hardware. That also causes problems for some programs. +Scream Tracker 3, for example, can lock up at startup because the +interrupt from an emulated SB card can be triggered earlier than it +should be in a real system.

Possibly a workaround may be found in future DOSEMU versions.

Linux kernels prior to 3.16 may have various problems on x86-64. +Make sure to not use 3.14 and 3.15 as they +lack 16bit segments support

3.16 adds support for espfix64 feature.


1.6. Fundamental problems with the CPU

There are several defects in Intel's x86 CPUs that are causing +problems for some software. Below is a description of the defects +that are known to cause problems for software running under dosemu.


1.6.1. Problem with the virtualization of the IF flag

Intel's manual +says:

" A procedure may use the POPF instruction to change the setting of the IF + flag only if the CPL is less than or equal to the current IOPL. An attempt + by a less privileged procedure to change the IF flag does not result in + an exception; the IF flag simply remains unchanged. "

The fact that the exception is not being generated, prevents dosemu from +catching and properly simulating the POPF instruction executed in protected +mode. That, in particular, means that the following code, executed in +protected mode (not in v86 mode) under dosemu, will end up with interrupts +disabled (IF cleared):

    sti
+    pushf
+    cli
+    popf
+[ the interrupts are still disabled here ]

This bug can only affect DPMI programs, as using DPMI is the only way +to execute protected mode code under dosemu. +Known programs that are affected are the games from ID software, namely +Doom2 and Duke Nukem 3D, but only when configured with sound. +An optional work-around was added to dosemu, which just re-enables the +interrupts if they were disabled for too long in protected mode. +Additionally the address of the instruction that disabled the interrupts, +is added to a black-list and this instruction is ignored for subsequent +passes so that it can't disable the interrupts any more. +This is potentially unsafe, but if the timeout is long enough, no harm +was observed so far. +The timeout is configured via the $_cli_timeout option, which is measured +in a 10ms timer ticks. Setting that option to 0 disables the workaround +completely, making Doom2 unplayable with sound enabled.


1.6.2. ESP register corruption

Intel's x86 CPUs have a defect +described here, +chapter "Specification Clarifications" +section 4: "Use Of ESP In 16-Bit Code With 32-Bit Interrupt Handlers", +which reads as follows:

"ISSUE: When a 32-bit IRET is used to return to another privilege level, +and the old level uses a 4G stack (D/B bit in the segment register = 1), +while the new level uses a 64k stack (D/B bit = 0), then only the +lower word of ESP is updated. The upper word remains unchanged. This is +fine for pure 16-bit code, as well as pure 32-bit code. However, when +32-bit interrupt handlers are present, 16-bit code should avoid any +dependence on the upper word of ESP. No changes are necessary in existing +16-bit code, since the only way to access ESP in USE16 segments is +through the 32-bit address size prefix."

Unfortunately, the above quote from Intel is silent about the 32-bit +programs that use 16-bit stack segment - this is where the problem pops us. +The corruption happens when the Linux kernel returns control to the dosemu +process, while a 32-bit DPMI client that uses a 16-bit data segment for +the stack is active. This is not the usual case, but unfortunately some +32-bit DPMI clients are actually using a 16-bit segment for the stack, +and even the dos4gw extender behaves that way sometimes.

Programs that are known to be affected by this issue are: +

  • Need For Speed 1 (demo version at least, when configured with sound)

  • Syndicate Wars (when used with dos4gw 0.8)

  • Open Cubic Player

These programs are crashing shortly after startup, but this problem +is difficult to detect reliably, so there may be many more programs +that experience a random crashes due to this CPU bug.

The reliable work-around was developed and added into linux-2.6.12 +for 32-bit systems, and into linux-3.16 for 64-bit systems.

Note: linux kernels prior to 3.16 may have various problems on x86-64.


2. Known bugs

2.1. Things YOU may help changing

2.1.1. List of currently known bugs in dosemu2

  • Some documentation is known to be well out of date.

  • Some database programs (Clipper, FoxPro) have problems with +locking in certain configurations. smbfs doesn't support +locking. $_full_file_locks=(on) may or may not help.

  • Mortal Kombat 1 and 2 are not producing any sound for unknown reasons.

  • X-COM Apocalypse (DEMO version) locks up at startup if configured with sound.


3. Programs exhibiting graphical problems in xdosemu

The following programs work perfectly on the Linux console +(suid/sudo/root) with graphics enabled but exhibit minor or +major glitches in xdosemu.


3.1. Games with graphical problems

The following games exhibit glitches or don't work at all in +xdosemu. Please let us know when any problems are solved or +even better, help us solving!

  • Commander Keen 1 wobbles like jelly and the window shakes +every time it scrolls.

  • Pinball Dreams 2 takes a long time to start. Once it's past +the startup screen it runs fine though.


4. Differences in behaviour between Dosemu and Dosemu2

The following differences may be apparent if you have previously been +using Dosemu.


4.1. Filesystems

4.1.1. MFS

Network device DOS filesystem that provides read write access to the host filesystem.

  • Dosemu2 does not redirect a drive to arbitrary host paths on the command +line of the emufs.sys driver when it is loaded in config.sys. Use the lredir2 +command to accomplish this in autoexec.bat instead.

  • Dosemu2 does not support LFN on the SUBST / JOIN drives.

  • Dosemu2 now provides enhanced FAT32 disk functions only as a fallback +to the native DOS in the event that the DOS does not implement them itself. +A consequence of being a fallback is that if the DOS has a bug in its +implementation it is not overriden by Dosemu2. Examples of this are +MS-DOS 7.0 and PC-DOS 7.10.

\ No newline at end of file diff --git a/doc/NOVELL-HOWTO.txt b/doc/NOVELL-HOWTO.txt new file mode 100644 index 0000000..03819be --- /dev/null +++ b/doc/NOVELL-HOWTO.txt @@ -0,0 +1,430 @@ + Netware-HOWTO + Lauri Tischler ltischler@fipower.pp.fi + rev.0.2 30 Mar 1995 + updated for DOSEMU 1.2.0, 18 Jan 2004, Bart Oldeman -- contributions + by Peter Eckhardt + + This document tryes to descibe how to connect to Novell Netware + servers from Linux. + + 1. Introduction + + Due to the limited scope of this note, it is not a real HOWTO, not + even a 'mini-HOWTO'. You might call it a 'nano-HOWTO' if you like. + + In most sites the Netware is really just an extension to PC's running + DOS and DOS applications (Windows is JUST another DOS-application), + the Netware providing fileserver and printing support. + + I will concentrate on getting the connection via DOSEMU only. + + My everyday network is a Netware network with 3 servers and appr. 110 + PC's connected to it. It is TOTALLY Dos/Windows environment, running + Novell standard Ethernet_802.3 frames, very ordinary REAL LIFE setup. + + Tested Environment : + + o LinuxBox 486DX2/66, 17Meg Ram + 20 Meg Swap, two ESDI disks 340Mb + and 320 Mb (Linux InSide), Netcard SMC Elite Ultra. + + o Linux 1.2.2, Dosemu pre0.53.55. + + o Netware 3.11 on all servers, SMC Elite 32 EISA on main server. + + The following may or may not work on Your pile of iron. + + + 2. Netware Requirements. + + One of the main questions is What Is the Ethernet Frame Type your + Netware uses. + + + 2.1. Frametype Ethernet_II. + + For IP-connectivity Novell has always used Ethernet_II frametype. + Some sites use Ethernet_II for IPX _and_ IP (good for them). This is + also easiest case to connect to Netware, you also get IP-connectivity + between your linuxboxes if/when they are located in separate segments. + For IP-connectivity you need to load TCPIP.NLM in your server and + define FORWARD=YES on loadline. You also need to BIND the IP to your + server networkcards with proper IP-address. In general if you need + any kind of IP-connection to Netware Server (NFS, BOOTPD, FTPD) you + _must_ use Ethernet_II frame. + + + 2.2. Frametype Ethernet_802.3. + + Traditionally Novell has used Ethernet_802.3 for IPX protocol. That is + _before_ the Netvare 4.0x and various VLM stuff. In this case you + can't communicate with other linuxboxes if they are located on + separate segments because Netware will not route IP-protocol on 802.3 + frames. You can however connect to Netware server as an isolated + workstation. + + + + + + 2.3. Frametype Ethernet_802.2. + + New Novell practice is to recommend the Ethernet_802.2 frame for IPX. + The 802.2 is actually the default frametype unless otherwise declared + (in server autoexec.ncf and workstation net.cfg files). This is also + the worst case because the dosemu packetdrivers do not support this + frametype. You can still connect using direct-IPX approach. + + + I would recommend that you load the Ethernet_II frame in your server + in ANY CASE because that makes the care-and-feeding-and-development + much easier in the longrun. + + There has been some worried noises about messing up the IP-traffic on + Ethernet_II if you run IPX on Ethernet_II frames at the same time. + There is no problem in running both protocols on same frame and cable, + it is done all the time on many sites (RTFM - Novell TCP/IP Docs) 8-). + + This how I do both frames and protocols on single card and cable . + + load SMCE32 port=6810 Name=First Frame=Ethernet_802.3 ; 'novell' frame + load SMCE32 port=6810 Name=Second Frame=Ethernet_II ; 'normal' frame + bind ipx to First Net=E1 + bind ipx to Second Net=E2 + + + So I actually run IPX on both frames, Ethernet_802.3 on logical net E1 + and Ethernet_II logical net E2. All on one card and cable. + + + 3. Making The Connection. + + There are basically two methods for making the connection between the + Linuxbox and Netware server, The Direct-IPX or Packet Drivers. At + the time of writing the Packet Driver method is the most reliable + in particular if you combine it with DPMI programs. Direct-IPX may + just work though (depending on the DOS program in question). + + + 3.1. The Direct-IPX. + + Make sure that you have the IPX support compiled in to your kernel. + + Within DosEmu, in directory ipxutils, there are some utilities which + are necessary. At the time of this writing the compiling of those + utilities was not automatic, so it may be necessary to go to + directory ipxutils and run 'make'. + + Check that in your 'dosemu.conf' file you have 'ipx_support' enabled. + $_ipxsupport = (on) + + Now you need to enable the ipxinterface. To do that you execute + following command : + + ipx_interface add -p eth0 802.3 + + + Instead of 'eth0' you can give some other Id in case your ethernetcard + is somewhere else. + + The last parameter, ie. 802.3, depends on what type of ethernetframe + runs on your network. Possibe values are 802.2, 802.3 and EtherII. + Check with your Netware Administrator if you are not sure. You may + wish to add the above mentioned command into your rc.local file. + + Now start the dosemu session and load the Netware shell, NETX. The + NETX is the only TSR necessary to run the connection, no LSL, no + Packetdrivers nor IPXODI. + + + Pros. + + o Connection is reasonably fast, about 2.41666.. times faster then + packetdrivers. + + o This is the ONLY way to connect if you are using Ethernet_802.2 + frame. + + Cons. + + o SPX support is still missing, this means that some software will + not run, like Intel LanDesk Inventory, Novell Remote Console, + Netware Access Services, I'm sure there are more 8=( + + o The connection drops dead after about 15 min of idletime. I + suspect that it has something to do with 'watchdog packets' from + the server not getting proper answer. Maybe some IPX/SPX guru will + look into this. + + o DPMI in combination with direct IPX is broken as of DOSEMU 1.2.0. + + + 3.2. The Packet Driver (IPX). + + As a driver you should use PDETHER which is an ipx-to-packet driver + shim, but masquerading as an ODI compliant driver. There also exists + an older driver PDIPX, technology represented by PDIPX is no longer + supported by Novell. A driver named IPXPD is more likely to work + than PDIPX. PDETHER and IPXPD are using Ethernet_II frames, while + PDIPX uses 802.3 frames. + + The Packet Driver uses build-in packetdriver interface which means + that the IPX-SUPPORT in Kernel and in DOSEMU is NOT needed. When + configuring the Kernel you can define IPX-SUPPORT (n), this is + actually the default case. + + Corresponding parameter for DOSEMU is found in the NETWORKING SUPPORT + section of dosemu.conf/.dosemurc. There you just leave the line + $_ipxsupport = (off) commented out. (see below) + + The use of the second configuration parameter $_novell_hack is + explained in detail in later paragraphs. + + #************************* NETWORKING SUPPORT ***************************** + # + # Turn the following option 'on' if you require IPX/SPX emulation. + # Therefore, there is no need to load IPX.COM within the DOS session. + # The following option does not emulate LSL.COM, IPXODI.COM, etc. + # NOTE: MUST HAVE IPX PROTOCOL ENABLED IN KERNEL !! + # $_ipxsupport = (off) + # + # Enable Novell 8137->raw 802.3 translation hack in new packet + # driver. + + $_pktdriver = (on) + # $_novell_hack = (off) + + Also set up a working packet driver (eg. TUN/TAP) connection; refer to + README.txt for details. For example (with tunctl) set: + + $_netdev="tap0" + $_vnet="tap" + + There are various versions of the packetdriver PDETHER floating around, but + it is recommended to use version 1.05 or later. Those versions have + support for a "raw packet send" interface. + + PDETHER in its native mode understands only Ethernet_II frames, by + enabling the dosemu.conf parameter pktdriver novell_hack it can be + fooled to use Ethernet_802.3 frames instead. + + Because PDETHER is an ODI driver, you load.. + + LSL (at least version 2.20; older versions may crash dosemu) + PDETHER + IPXODI + NETX + + If you use IPXPD or PDIPX you just load + + IPXPD (for Ethernet_II frames) or PDIPX (for 802.3 frames) + NETX + + Because PDETHER is an ODI driver, there must be corresponding section + in your net.cfg file. Here is a snippet of my NET.CFG + + Link Support + Buffers 4 1514 + MemPool 2048 + + Link Driver PDETHER + Int 60 + FRAME Ethernet_II + + NetWare DOS Requester + FIRST NETWORK DRIVE = F + SHOW DOTS = ON + SET STATION TIME = ON + PREFERRED SERVER = HOME + FILE HANDLES = 40 + LOCAL PRINTERS = 1 + + + + The packetdrivers support only Ethernet_802.3 and Ethernet_II frames. + If you are unlucky enough to use Ethernet_802.2 frame, your only + change is to use direct-IPX interface (unless you can persuade the + system admin to add Ethernet_II frames to your network 8=)). + + Do NOT CHANGE line 'FRAME Ethernet_II' in Link Driver PDETHER section, + instead enable or disable the 'pkdriver novell_hack' in 'dosemu.conf' + + $_pktdriver = (on) + $_novell_hack = (on) If you have Ethernet_802.3 + + # $_novell_hack = (off) If you have Ethernet_II + + Read the PDETHER.DOC for further info. + + Example from Peter Eckhardt: + + Create a startnet.bat and net.cfg in dosemu + + CD C:\NWCLIENT + edit .... + + - startnet.bat - + + SET NWLANGUAGE=DEUTSCH + LH C:\NWCLIENT\LSL /c=C:\NWCLIENT\net.cfg + C:\NWCLIENT\PDETHER.EXE + LH C:\NWCLIENT\IPXODI.COM + rem LH C:\NWCLIENT\NETX + LH C:\NWCLIENT\VLM.EXE + + - net.cfg - + + Link Support + Buffers 4 1514 + MemPool 2048 + + Link Driver PDETHER + Int 60 + FRAME Ethernet_II + USE DEFAULTS=OFF + VLM=CONN.VLM + VLM=IPXNCP.VLM + VLM=TRAN.VLM + VLM=SECURITY.VLM + VLM=NDS.VLM + VLM=NWP.VLM + VLM=FIO.VLM + VLM=BIND.VLM + VLM=PRINT.VLM + VLM=GENERAL.VLM + VLM=REDIR.VLM + VLM=NETX.VLM + + NetWare DOS Requester + FIRST NETWORK DRIVE = F + NETWORK PROTOCOL = BIND + SHOW DOTS = ON + SET STATION TIME = ON + PREFERRED SERVER = EMK1 + FILE HANDLES = 40 + LOCAL PRINTERS = 1 + VLM = AUTO.VLM + + 4. Speed Of Connection + + Here is some benchmarking I did with testprogram TESTNET.EXE, + available somewhere in NetWire. It tests the network transfer speed. + + I can saturate my ethernet with two stations running at full tilt. + Maximum aggregate speed is appr. 900 kilobytes/sec. + + I'm using SMC Elite 32 EISA board in Server and SMC Elite Ultra in + workstation. + + NETX + Dos6.2 620 + DosEmu (directIPX) 290 + DosEmu (pktdrv) 120 + + The figures denote transferspeed in kilobytes/second. + + Few months ago I had a NE2000 clone in my box, with DOS6.2/NETX it + would run to appr. 460 kbs. I could live with that. + + Note: Recently improvements were made that speed up the packet + driver throughput more than twice. The above measurements are no longer + valid. It is expected that the Packet Driver now has the same performance + as the directIPX method, or even better, but no precise measurements + were made. + + + + + 5. NFS and Other Connectivity. + + It is possible to access Netware server and services from Linux + directly by using various commercial supportpackages to get Unix + filesystem and/or printing services. + + o Netware-NFS. + + o Netware Flex-IP. + + o Nov*iX from FireFox. + + o Charon, shareware SMTP-gateway and printservices. + + There also exists a freeware NFS connectivity using SOSS package, + below is a contribution from a fellow netter, Andrew J. Anderson, + andrew@db.erau.edu. + + --- message begins --- + + + I am currently using a package called "soss" (Son of Stan's Server) + that turns a DOS PC into an NFS server. I am using this to export + NetWare volumes to my Linux box so that I can have multi-user access + to several CD-ROM packages. I will continue using this until multiple + logins from DOSEmu becomes a reality. The speed of this setup depends + on the speed of the PC that is running the NFS server package. + Currently, I am using a 286 with 4 Megs of RAM being used as a disk + cache. If I remember correctly, I can get about 50K/s across this + setup. I tested a 486DX-33 with 8 megs and got about 250- 300K/s + transfer. I am hoping to get about 500K/s with a 486DX2-66 with 16 + megs of RAM. Not blazingly fast, but good enough. + + So if you play with drive mappings under DOSEmu using LREDIR, you + could setup a scheme where each user had a mapping to their home + directory on the Novell side. There is potential for security risk in + doing that because SOSS doesn't have much in the way of security built + in, but I am using part of my NetWare volumes as "overflow" space when + my Linux drives fill up -- as they so often do! :) + + --- message ends --- Thanks Andrew.. + + I have no personal experience with any of the packages mentioned + above, I'm sure that there are a lot of other useful packages + floating around. Please mail me, so I can add them to possible future + incarnations of this note. + + + 6. History. + + 6.1. Revision 0.1. + + Written with great haste and enthusiasm. Contained some mistakes for + which I was promptly flamed 8:). Some reports of success were also + received. + + 6.2. Revision 0.2. + + Known errors corrected and new sections added. + + Any additions for this HOWTO are humbly accepted and if relevant to + great cause will be added to later revisions. + + + + + 7. Begin Legalese. + + Unless otherwise stated, Linux HOWTO documents are copyrighted by + their respective authors. Linux HOWTO documents may be reproduced and + distributed in whole or in part, in any medium physical or electronic, + as long as this copyright notice is retained on all copies. Commercial + redistribution is allowed and encouraged; however, the author would + like to be notified of any such distributions. + + All translations, derivative works, or aggregate works incorporating + any Linux HOWTO documents must be covered under this copyright notice. + That is, you may not produce a derivative work from a HOWTO and impose + additional restrictions on its distribution. Exceptions to these rules + may be granted under certain conditions; please contact the Linux + HOWTO coordinator at the address given below. + + In short, we wish to promote dissemination of this information through + as many channels as possible. However, we do wish to retain copyright + on the HOWTO documents, and would like to be notified of any plans to + redistribute the HOWTOs. + + If you have questions, please contact Greg Hankings, the Linux HOWTO + coordinator, at greg.hankings@cc.gatech.edu. You may finger this + address for phone number and additional contact information. + + End Legalese. + + Happy Netting. Lauri Tischler, ltischler@fipower.pp.fi diff --git a/doc/README.gdb b/doc/README.gdb new file mode 100644 index 0000000..12df42c --- /dev/null +++ b/doc/README.gdb @@ -0,0 +1,63 @@ + +From lermen@elserv.ffm.fgan.de Fri Jan 9 02:02:28 1998 +Date: Sun, 4 Jan 1998 16:30:24 +0100 (MET) +From: Hans Lermen +To: Marty Leisner +Cc: Pat Villani , dosemu developers +Subject: Re: Ideas for debugging + +On Sat, 3 Jan 1998, Marty Leisner wrote: + +> +> In order to run gdb under dosemu: +> attach (compile with the -g option) +> do +> handle SIGSEGV nostop noprint +> +> then you're fine + +Yup, that's the trick ;-) +However, to 'compile with -g' one has to do: + + make pristine + ./default-configure --enable-debug + make + +> (but its very difficult to debug programs under +> dosemu). + +its nearly impossible, for that we have 'dosdebug', the bultin debugger. + +> +> Several years we discussed this...has any work been done essentially +> having dosemu act as a gdbserver, which can talk to gdb...?? + +hmm, can gdb handle 16 bit code or even segmented code? + +Hans + + +Bart: +It can handle 16 bit code but you have to handle the segmentation yourself, +e.g. use + set architecture i8086 + x/20i 0x9089*16+0x18e +to dump 9089:019e + +Another issue is that whilst single stepping the SIGALRM handler may +disturb. You can avoid that using hooks, as below, and paste everything +into a .gdbinit file. + +define hook-stop + handle SIGALRM nopass +end + +define hook-run + handle SIGALRM pass +end + +define hook-continue + handle SIGALRM pass +end + +handle SIGSEGV nostop noprint diff --git a/doc/README.html b/doc/README.html new file mode 100644 index 0000000..1512168 --- /dev/null +++ b/doc/README.html @@ -0,0 +1,6096 @@ + +DOSEMU

DOSEMU

The DOSEMU team

Edited by

Alistair MacDonald

For DOSEMU v1.4 pl0.0

This document is the amalgamation of a series of README files which were +created to deal with the lack of DOSEMU documentation.


Table of Contents
1. Introduction
1.1. DOSEMU modes of operation
1.1.1. Terminal mode
1.1.2. Dumb mode
1.1.3. SDL mode
1.1.4. Console graphics mode
1.2. Running a DOS program directly from Linux.
1.3. Using a different DOS
1.4. About this document
2. Runtime Configuration Options
2.1. Format of dosemu.conf and ~/.dosemurc
2.1.1. Disks, boot directories and floppies
2.1.2. Controlling amount of debug output
2.1.3. Basic emulation settings
2.1.4. Code page and character set
2.1.5. Terminals
2.1.6. Keyboard settings
2.1.7. X Support settings
2.1.8. Builtin ASPI SCSI Driver
2.1.9. COM ports and mice
2.1.10. Printers
2.1.11. Sound
2.1.12. Joystick
2.1.13. Networking under DOSEMU
2.1.14. Settings for enabling direct hardware access
2.1.15. Video settings ( console only )
2.1.16. Time settings
3. Security
4. Sound
4.1. Using the MPU-401 "Emulation"
4.2. The MIDI daemon
4.3. Disabling the Emulation at Runtime
5. Using Lredir
5.1. how do you use it?
5.2. Other alternatives using Lredir
6. Running dosemu as a normal user
7. Using CDROMS
7.1. The built-in driver
8. Using X
8.1. Basic information
8.2. More detailed information, hints and tips
8.3. The VGA Emulator
9. Running Windows under DOSEMU
9.1. Mouse in Windows under DOSEMU
9.2. Windows 3.x in SVGA modes
9.3. VxD support
9.4. DOS shell in Windows
10. The DOSEMU mouse
10.1. Setting up the emulated mouse in DOSEMU
10.2. Problems
11. Running a DOS application directly from Unix shell
11.1. Using unix -e in autoexec.bat
11.2. Using the keystroke facility.
11.3. Using an input file
11.4. Running DOSEMU within a cron job
12. Commands & Utilities
12.1. Programs
12.2. Drivers
13. Keymaps
14. Networking using DOSEMU
14.1. Direct NIC access
14.2. Virtual networking
14.2.1. Bridging
14.2.2. IP Routing
14.3. VDE networking backend
15. Using Windows and Winsock
15.1. LIST OF REQUIRED SOFTWARE
15.2. STEP BY STEP OPERATION (LINUX SIDE)
15.3. STEP BY STEP OPERATION (DOS SIDE)

1. Introduction

You can start DOSEMU using + +
     $ dosemu
+ +If you have never used DOSEMU before, and FreeDOS is present, then +DOSEMU will boot, and present you with a welcome screen and a C:\> +command prompt.

If for some reason it does not start, or DOSEMU crashes somewhere, +look at ~/.dosemu/boot.log for details.

Remember, that you can't use <Ctrl>-C +within DOS to exit from DOS. +For this you need to execute exitemu +or, when using the 'DOS in a BOX' <Ctrl><Alt><PgDn>.

Your DOS drives are set up as follows: +
     A: floppy drive (if it exists)
+     C: points to the Linux directory ~/.dosemu/drive_c. It contains the
+        files config.sys, autoexec.bat and a directory for temporary files.
+        It is available for general DOS use.
+     D: points to your Linux home directory
+     E: points to your CD-ROM drive, if it is mounted at /media/cdrom
+     Z: points to the read-only DOSEMU and FreeDOS commands directory
+        It actually points to ~/mydos/dosemu/drive_z; it appears read-only
+        inside DOSEMU.

You can use the LREDIR DOSEMU command to adjust +these settings, or edit +/etc/dosemu/dosemu.conf, ~/.dosemu/.dosemurc, c:\config.sys, or c:\autoexec.bat, +or change the symbolic links in ~/.dosemu/drives.

Enter HELP for more information on DOS and DOSEMU commands. +Note that FreeDOS COMMAND.COM DIR command shows long +file names if you type DIR/LFN.

Other useful keys are: +
    <Ctrl><Alt><F>    toggle full-screen mode in X
+    <Ctrl><Alt><K>    grab the keyboard in X
+    <Ctrl><Alt><Home> grab the mouse in X
+    <Ctrl><Alt><Del>  reboot
+    <Ctrl><^>         use special keys on terminals (dosemu -t)


1.1. DOSEMU modes of operation

There exist various ways of starting DOSEMU, depending on the environment +and certain command line options. By default, in X, it will start using +a special 'DOS in a Box' which provides a usual PC setup, using a 80x25 +text mode. It also supports graphics. The box can be rescaled by dragging +the window borders using the mouse.

However, in certain situation you may want to use a different mode.


1.1.1. Terminal mode

Terminal mode is automatically entered if you do not have X available, for +instance when logging in remotely from a Windows system or at the Linux +console. You can force it using: + +
      $ dosemu -t
+ +In this mode the display of graphics is impossible, but you can use +full-screen DOS text mode applications. It is advisable to give the +terminal window a size of 80 by 25 characters, or use "stty cols 80 rows 25" +on the Linux console, before starting it because many DOS applications +are confused about other sizes.

You can use the $_internal_char_set option in ~/.dosemu/.dosemurc or +dosemu.conf to change the code page that DOSEMU thinks that DOS is using.

Many terminals do not support various function key combinations. On the +Linux console you can work around that by using the raw keyboard mode +(-k flag, or $_rawkeyboard). xterm's support many +key combinations. In +other cases you'll have to work around it using the special +Ctrl-^ shortcut +(Ctrl-6 on US keyboards). Press Ctrl-^ h +for help.


1.1.2. Dumb mode

For DOS applications that only read from standard input and write to +standard output, without any full-screen usage, you can use dumb +mode. To use this you must invoke DOSEMU like + +
      $ dosemu -dumb
+ +this has the advantage that (A) the output of the DOS application stacks +up in your scroll buffer and (B) you can redirect it to a file such as + +
      $ dosemu -dumb dir > listing
+ +Note that editing is often restricted to BACKSPACE'ing.


1.1.3. SDL mode

You can start dosemu with the "-S" option to use the SDL library. In +X it will just look like a regular DOS in a Box but with a different +shaped text mode mouse cursor. You can also use this mode on frame buffer +consoles.


1.1.4. Console graphics mode

Console graphics mode is the hardest to setup and may potentially lock +up your system, but if it works it gives you direct VGA hardware access +which may be quicker and more accurate than the emulation used in X.

You need root rights to use it. To enable it, it is recommended to use +"sudo": + +

  • install sudo if you haven't already done so

  • use visudo as root to add entries such as +
            joeuser   hostname=(root) PASSWD: /usr/local/bin/dosemu.bin
    +to your /etc/sudoers file, where "joeuser" is the user who is +allowed to run privileged DOSEMU and "hostname" is the name of +your current host (use "ALL" for any host).

  • if you change PASSWD to NOPASSWD then joeuser does not need to type +the user's password (not root's password) when invoking DOSEMU +(a little less secure, if somebody hacks into joeuser's account).

  • now invoke DOSEMU using dosemu -s


1.2. Running a DOS program directly from Linux.

You can use something like +
           dosemu "/home/clarence/games/commander keen/keen1.exe"
+which will automatically cause the DOS in DOSEMU to +

  • "cd" to the correct directory,

  • execute the program automagically,

  • and quit DOSEMU when finished.


1.3. Using a different DOS

It is possible to use a different DOS than the supplied FreeDOS in +DOSEMU. A straightforward way is to just copy the relevant system +files (io.sys, msdos.sys, etc.) to ~/.dosemu/drive_c, and then the +next time you run dosemu it will automatically use them. You may +need to edit config.sys and autoexec.bat though, if the DOS complains.

Another way is to boot directly from a Linux mounted FAT partition, +with Windows 9x or any DOS installed. You can change the C: drive +to point to that by using dosemu -i.

In that case the DOSEMU support commands are available on drive D: instead +of drive Z:. You might want to use different config.sys and autoexec.bat +files with your DOS. For example, you can try to copy D:\config.emu +and D:\autoemu.bat to C:\, adjust them, and use the $_emusys option +in ~/.dosemu/.dosemurc or dosemu.conf.

Manual adjustment of the C: drive is also possible, by changing the +~/.dosemu/drives/c symbolic link or by specifying it explicitly +using the $_hdimage run-time option.


1.4. About this document

The rest of this document goes into more detail about all the different +settings and possibilities. +This documentation is derived from a number of smaller documents. This makes it +easier for individuals to maintain the documentation relevant to their area of +expertise. Previous attempts at documenting DOSEMU failed because the +documentation on a large project like this quickly becomes too much for one +person to handle.


2. Runtime Configuration Options

This section of the document by Hans, +<lermen@fgan.de>. Last +updated on May 4, 2007, by Bart Oldeman.

Most of DOSEMU configuration is done during runtime and per default it +can use the system wide configuration file dosemu.conf (which is often +situated in /etc or /etc/dosemu) optionally followed by the users +~/.dosemurc and additional configurations statements on the commandline +(-I option). If /etc/dosemu.users exists then dosemu.conf is searched for +in /etc and otherwise in /etc/dosemu (or an alternative sysconfdir +compiletime-setting).

The default dosemu.conf and ~/.dosemurc have all settings commented +out for documentation purposes; the commented out values are the +ones that DOSEMU uses by default. Note that a non-suid type of installation +does not need the dosemu.users and dosemu.conf files, and the main +per-user configuration file is $HOME/.dosemurc. +However, for security reasons, a suid-root installation will not +run without dosemu.users, and in that case certain dosemu.conf settings +are ignored by ~/.dosemurc.

In fact dosemu.conf and ~/.dosemurc (which have identical syntax) +are included by the systemwide configuration script global.conf which, +by default, is built into the DOSEMU binary. As a normal user +you won't ever think on editing this, only dosemu.conf and your personal +~/.dosemurc. The syntax of global.conf is described in detail in +README-tech.txt, so this is skipped here. However, the option -I string too +uses the same syntax as global.conf, hence, if you are doing some special +stuff (after you got familar with DOSEMU) you may need to have a look there.

The first file expected (and interpreted) before any other configuration +(such as global.conf, dosemu.conf and ~/.dosemurc) is /etc/dosemu.users +or /etc/dosemu/dosemu.users. As mentioned above, this file is entirely +optional for non-suid-root (default) installations. +Within this file the general permissions are set:

  • which users are allowed to use DOSEMU.

  • which users are allowed to use DOSEMU suid root.

  • which users are allowed to have a private lib dir.

  • what kind of access class the user belongs to.

  • what special configuration stuff the users needs

and further more: + +

  • whether the lib dir (DOSEMU_LIB_DIR) resides elsewhere.

  • setting the loglevel.

Except for lines starting with `xxx=' (explanation below), +each line in dosemu.user corresponds to exactly one valid user count, +the special user `all' means any user not mentioned earlier. Format: + +
      [ <login> | all ] [ confvar [ confvar [ ... ] ] ]

The below example is from etc/dosemu.users.secure, which you may copy +to /etc/dosemu.users. + +
      root c_all     # root is allowed to do all weird things
+      nobody nosuidroot guest # variable 'guest' is checked in global.conf
+                              # to allow only DEXE execution
+      guest nosuidroot guest  # login guest treated as `nobody'
+      myfriend c_all unrestricted private_setup
+      myboss nosuidroot restricted private_setup
+      all nosuidroot restricted # all other users have normal user restrictions
+ +Note that the above `restricted' is checked in global.conf and will +disable all secure relevant feature. Setting `guest' will force +setting `restricted' too.

The use of `nosuidroot' will force a suid root dosemu binary to exit, +the user may however use a non-suid root copy of the binary. +For more information on this look at README-tech, +chapter 11.1 `Privileges and Running as User'

Giving the keyword `private_setup' to a user means he/she can have a private +DOSEMU lib under $HOME/.dosemu/lib. If this directory is existing, DOSEMU +will expect all normally under DOSEMU_LIB_DIR within that directory. +As this would be a security risk, it only will be allowed, if the used DOSEMU +binary is non-suid-root. If you really trust a user you may additionally give +the keyword `unrestricted', which will allow this user to execute a suid-root +binary even on a private lib directory (though, be aware).

In addition, dosemu.users can be used to define some global settings, which +must be known before any other file is accessed, such as: + +
      default_lib_dir= /opt/dosemu  # replaces DOSEMU_LIB_DIR
+      log_level= 2                  # highest log level

With `default_lib_dir=' you may move DOSEMU_LIB_DIR elsewhere, this mostly +is interesting for distributors, who want it elsewhere but won't patch the +DOSEMU source just for this purpose. But note, the dosemu supplied scripts +and helpers may need some adaption too in order to fit your new directory.

The `log_level=' can be 0 (never log) or 1 (log only errors) or 2 (log all) +and controls the ammount written to the systems log facility (notice). +This keyword replaces the former /etc/dosemu.loglevel file, which now is +obsolete.

Nevertheless, for a first try of DOSEMU you may prefer etc/dosemu.users.easy, +which just contains + +
      root c_all
+      all c_all
+ +to allow everybody all weird things. For more details on security issues +have a look at chapter 3.

After the file dosemu.users, the file dosemu.conf (via global.conf, which +may be built-in) is interpreted, +and only during global.conf parsing the access to all configuration options is +allowed. dosemu.conf normally lives in the same directory as dosemu.users, +for instance /etc/dosemu or /etc (that is, sysconfdir in compiletime-settings). + +Your personal ~/.dosemurc is included directly after dosemu.conf, +but has less access rights (in fact the lowest level), all variables you +define within ~/.dosemurc transparently are prefixed with `dosemu_' such +that the normal namespace cannot be polluted (and a hacker cannot overwrite +security relevant enviroment variables). Within global.conf only those +~/.dosemurc created variables, that are needed are taken over and may +overwrite those defined in dosemu.conf.

The dosemu.conf (global.conf) may check for the configuration variables, +that are set in dosemu.users and optionaly include further configuration +files. But once dosemu.conf (global.conf) has finished interpretation, +the access to secure relevant configurations is (class-wise) restricted while +the following interpretation of (old) .dosrc and -I statements.

For more details on security settings/issues look at README-tech.txt, for +now (using DOSEMU the first time) you should need only the below description +of dosemu.conf (~/.dosemurc)


2.1. Format of dosemu.conf and ~/.dosemurc

All settings are variables, and have the form of

      $_xxx = (n)
+ +or + +
      $_zzz = "s"

where `n' is a numerical or boolean value and `s' is a string. +Note that the brackets are important, else the parser will not decide +for a number expression. For numbers you may have complete expressions +( such as (2*1024) ) and strings may be concatenated such as

      $_zzz = "This is a string containing '", '"', "' (quotes)"

Hence a comma separated list of strings is concatenated. +All these settings are also environment variables. You can override them +by prefixing with dosemu_, e.g. +
    dosemu__X_title="DOS was in the BOX" dosemu
+temporarily changes the X window title.


2.1.1. Disks, boot directories and floppies

The parameter settings are tailored to fit the recommended +usage of disk and floppy access. There are other methods too, but for these +you have to look at README-tech.txt (and you may need to modify global.conf). +We strongly recommend that you use the proposed techique. Here the normal +setup:

    # List of hdimages or boot directories under 
+    # ~/.dosemu, the system config directory (/etc/dosemu by default), or
+    # syshdimagedir (/var/lib/dosemu by default) assigned in this order
+    # such as "hdimage_c directory_d hdimage_e"
+    # Absolute pathnames are also allowed.
+      $_hdimage = "drives/*"
+      $_floppy_a ="threeinch" # or "fiveinch" or empty, if not existing
+      $_floppy_b = ""       # dito for B:
+      $_cdrom = "/dev/cdrom" # list of CDROM devices

A hdimage is +a file containing a virtual image of a DOS-FAT filesystem. Once you have +booted it, you (or autoexec.bat) can use `lredir' to access any directory +in your Linux tree as DOS drive (a -t msdos mounted too). +Look at chapter 5 (Using Lredir) for more details. +If you want to create your own hdimage use "mkfatimage16" (see the manual +page). To make it bootable you can make it, say, drive F:, and use +"SYS F:" at the DOS prompt. +The DOSEMU-HOWTO explains how to manipulate it using mtools.

You can also specify a Linux directory containing all what you want to have +under your DOS C:. Copy your IO.SYS, MSDOS.SYS or equivalent to that +directory (e.g. DOSEMU_LIB_DIR/bootdir), set + +
       $_hdimage = "bootdir"
+ +and up it goes. Alternatively you can specify +an absolute path such as "/dos" or "/home/username/dosemu/freedos". +DOSEMU makes a lredir'ed drive out of it and can boot from it. +You can edit the config.sys and the autoexec.bat within this directory +before you start dosemu. +Further more, you may have a more sohisticated setup. Given you want to run +the same DOS drive as you normal have when booting into native DOS, +then you just mount you DOS partition under Linux (say to /dos) and +put links to its subdirectories into the boot dir. This way you can decide +which files/directories have to be visible under DOSEMU and which have to be +different. Here a small and not complete example bootdir setup: + +
      config.sys
+      autoexec.bat
+      command.com -> /dos/command.com
+      io.sys -> /dos/io.sys
+      msdos.sys -> /dos/msdos.sys
+      dos -> /dos/dos
+      bc -> /dos/bc
+      windows -> /dos/windows

As a further enhancement of your drives setup you may even use the following +strategy: given you have the following directory structure in one the +directories where the $_hdimage setting applies (this is done by default +in ~/.dosemu and in /etc/dosemu) + +
      drives/c
+      drives/d
+ +where c and d are symbolic +links to appropriate DOS useable directories, then the following single +statement + +
      $_hdimage = "drives/*"
+ +will assign all these directories to drive C: and D: respectively. +Note, however, that the order in which the directories under drives/* +are assigned comes from the order given by /bin/ls. Hence the folling + +
      drives/x
+      drives/a
+ +will assign C: to drives/a and D: to drives/x, keep that in mind.

In some rare cases you may have problems accessing Lredir'ed drives +(especially when your DOS application refuses to run on a 'network drive'), +For this to overcome you may need to use so-called `partition access', +use a floppy, or a special-purpose hdimage. The odd +with partition access is, that you never should have +those partition +mounted in the Linux file system at the same time as you use it in DOSEMU +(which is quite uncomfortable and dangerous on a multitasking OS such as +Linux ). Though DOSEMU checks for mounted partitions, there may be races +that are not caught. In addition, when your DOSEMU crashes, it may leave +some FAT sectors unflushed to the disk, hence destroying the partition. +Anyway, if you think you need it, you must have r/w access to the partition +in /dev, and you have to `assign' real DOS partitions as follows:

      $_hdimage = "hdimage.first /dev/hda1 /dev/sdc4:ro"

The above will have `hdimage.first' as booted drive C:, /dev/hda1 as D: +(read/write) and /dev/sdc4 as E: (readonly). You may have any kind of order +within `$_hdimage', hence

      $_hdimage = "/dev/hda1 hdimage.first /dev/sdc4:ro"

would have /dev/hda1 as booted drive C:. Note that the access to the +/dev/* devices must be exclusive (no other process should use it) +except for `:ro'.


2.1.2. Controlling amount of debug output

DOSEMU will help you find problems, when you enable its debug messages. +These will go into the file, that you defined via the `-o file' or `-O' +commandline option (the latter prints to stderr). If you do not specify +any -O or -o switch, then the log output will be written to ~/.dosemu/boot.log. +You can preset the kind of debug output via

      $_debug = "-a"
+ +where the string contains all you normally may pass to the `-D' commandline +option (look at the man page for details).


2.1.3. Basic emulation settings

Whether a numeric processor should be shown to the DOS space + +
      $_mathco = (on)

Which type of CPU should be emulated (NOTE: this is not the one you +are running on, but your setting may not exeed the capabilities of +the running CPU). Valid values are: 80[345]86 + +
      $_cpu = (80386)

To let DOSEMU use the Pentium cycle counter (if availabe) to do better timing +use the below

      $_rdtsc = (off)   # or on
+ +Note that the RDTSC can be unreliable on SMP systems, and in combination with +power management (APM/ACPI).

For the above `rdtsc' feature DOSEMU needs to know the exact CPU clock, +it normally calibrates it itself, but is you encounter a wrong mesurement +you may overide it such as + +
      $_cpuspeed = (166.666)  # 0 = let DOSEMU calibrate

If you have a PCI board you may allow DOSEMU to access the PCI +configuration space by defining the below + +
      $_pci = (auto)    # or auto, or off

NOTE: `$_pci' can not be overwritten by ~/.dosemurc. +The "on" setting can be very dangerous because it gives DOSEMU complete +write access; you need to edit dosemu.users to enable it. +In console graphics mode, some video card BIOSes need some PCI +configuration space access, +which is enabled by the default (auto) setting. This setting is far more +restricted and less dangerous.

Starting with dosemu-1.0 there is a flexible way to handle the mapping +strategy used by DOSEMU, which is needed by video emulation, EMS, +DPMI and XMS support and other things to map a given page of memory to the +required virtual DOS address space.

Normally DOSEMU will detect the proper mapping driver for the kernel you are +using, however, in some cases you may want to define it explicitely to +overcome eventual problems. For this you can specify + +
      $_mapping= "mapfile"
+ +to force the use of the driver, which uses a temporary file.

If you are using a kernel above 2.3.40, you may use + +
      $_mapping= "mapshm"
+which uses a POSIX shared memory object (the default) or +
      $_mapping= "mapashm"
+which uses anonymous shared memory (in case the above gives problems).

Note, that in case of `mapfile' and `mapshm' the size of the file or the +segment depend on how much memory you configured for XMS, EMS and DPMI +(see below). +You should take care yourself that you have enough diskspace +for 'mapfile'. For 'mapshm' the tmpfs mount option 'size=nbytes' controls +the amount of space; by default it is half of the (real machine) memory.

Defining the memory layout, which DOS should see: + +
      $_xms = (8192)          # in Kbyte
+      $_ems = (2048)          # in Kbyte
+      $_ems_frame = (0xe400)
+      $_dpmi = (0x5000)       # in Kbyte
+      $_dosmem = (640)        # in Kbyte, < 640
+ +Note that (other as in native DOS) each piece of mem is separate, hence +DOS perhaps will show other values for 'extended' memory. To use EMS +memory you must load the supplied ems.sys device driver. For XMS memory +you must either use a DOS XMS driver such as himem.sys, himem.exe, +fdxms.sys, or fdxxms.sys, or the internal XMS driver via ems.sys.

If you want mixed operation on the filesystem, from which you +boot DOSEMU (native and via DOSEMU), it may be necessary to have two +separate sets of config.sys and system.ini. DOSEMU can +fake a different file extension, so DOS will get other files when +running under DOSEMU. Faking autoexec.bat cannot happen in a reliable +fashion, so if you would like to use an autoexec.bat replacement then +just use the SHELL command in config.XXX, like this:

SHELL=COMMAND.COM /P /K AUTOEMU.BAT

      $_emusys = ""    # empty or 3 char., config.sys   -> config.XXX
+      $_emuini = ""    # empty or 3 char., system.ini   -> system.XXX

As you would realize at the first glance: DOS will not have the +the CPU for its own. But how much it gets from Linux, depends on the setting +of `hogthreshold'. +The HogThreshold value determines how nice Dosemu will be about +giving other Linux processes a chance to run.

      $_hogthreshold = (1)   # 0 == all CPU power to DOSEMU
+                             # 1 == max power for Linux
+                             # >1   the higher, the faster DOSEMU will be


2.1.4. Code page and character set

To select the character set and code page for use with DOSEMU you have + +
      $_external_char_set = "XXX"
+where XXX is one of +
    "cp437", "cp737", "cp773", "cp775", "cp850", "cp852", "cp857", "cp860",
+    "cp861", "cp862", "cp863", "cp864", "cp865", "cp866", "cp869", "cp874",
+    "cp1125", "cp1251"
+    "iso8859-1", "iso8859-2", "iso8859-3", "iso8859-4", "iso8859-5", "iso8859-6",
+    "iso8859-7", "iso8859-8", "iso8859_9", "iso8859-14", "iso8859-15", "koi8-r"
+    "koi8-u", "koi8-ru", "utf8"

The external character set is used to: +

  • compute the unicode values of characters coming in from the terminal

  • compute the character set index of unicode characters output to + a terminal display screen.

+The default is to use "", which denotes the current locale, and is usually +the right setting.

If you set a DOS external character set, then it is to the user +to load a proper DOS font (cp437.f16, cp850.f16 or cp852.f16 on the +console).

      $_internal_char_set = "XXX"
+where XXX is one of: +
    "cp437", "cp737", "cp773", "cp775", "cp850", "cp852", "cp857", "cp860",
+    "cp861", "cp862", "cp863", "cp864", "cp865", "cp866", "cp869", "cp874"
+    "cp895", "cp1125", "cp1251", "bg-mik"

The internal character set is used to: +

  • compute the unicode value of characters of video memory, + when using DOSEMU in a terminal or using X with a unicode + font.

  • compute the character set index of unicode characters + returned by bios keyboard translation services.

  • compute the unicode values of characters in file names.


2.1.5. Terminals

This section applies whenever you run DOSEMU remotely, in an xterm or +on the Linux console without graphics. +Color terminal support is now built into DOSEMU. Skip this section for +now to use terminal defaults, until you get DOSEMU to work. + +
      $_term_color = (on)   # terminal with color support
+      $_term_updfreq = (4)  # time between refreshs (units: 20 == 1 second)
+      $_escchar = (30)      # 30 == Ctrl-^, special-sequence prefix
+ +`term_updfreq' is a number indicating the frequency of terminal updates of +the screen. The smaller the number, the more frequent. A value of 20 gives +a frequency of about one per second, which is very slow. +`escchar' is a number (ascii code below 32) that specifies the control +character used as a prefix character for sending alt, shift, ctrl, and +function keycodes. The default value is 30 which is Ctrl-^. So, for +example, + +
      F1 is 'Ctrl-^1', Alt-F7 is 'Ctrl-^s Ctrl-^7'.
+      For online help, press 'Ctrl-^h' or 'Ctrl-^?'.


2.1.6. Keyboard settings

When running DOSEMU from console (also remote from console) or X you +may need to define a proper keyboard layout. It is possible to let +DOSEMU do this work automatically for you (see auto below), however, +this may fail and you'll end up defining it explicitely. This is done either +by choosing one on the internal keytables or by loading an external +keytable from DOSEMU_LIB_DIR/keymap/* (which you may modify according +to your needs). Both sets have identical names (though you may add +any new one to DOSEMU_LIB_DIR/keymap/*): + +
      be              finnish         hu-latin2       sg-latin1
+      de              finnish-latin1  it              sw
+      de-latin1       fr              keyb-no         uk
+      dk              fr-latin1       no-latin1       us
+      dk-latin1       hr-cp852        po
+      dvorak          hr-latin2       sf
+      es              hu              sf-latin1
+      es-latin1       hu-cwi          sg              jp106
+      cz-qwerty       cz-qwertz
+ +You define an internal keytable such as + +
      $_layout = "name"
+ +where `name' is one of the above. To load a keytable you just prefix +the string with "load" such as + +
      $_layout = "load de-latin1"

Note, however, that you have to set + +
      $_X_keycode = (on)
+ +to use this feature under X, because by default the keytable is +forced to be neutral (us). Normally you will have the correct settings +of your keyboard given by the X-server.

The most comfortable method, however, is to first let DOSEMU set the +keyboard layout itself. This involves 2 parts and can be done by setting + +
      $_X_keycode = (auto)
+ +which checks for existence of the X keyboard extension and if yes, +it sets $_X_keycode to 'on', that means the DOSEMU keytables +are active. The second part (which is independent from $_X_keycode) +can be set by + +
      $_layout = "auto"
+ +DOSEMU then queries the keyboard layout from the kernel or X (which +only does work on the console or X, but not in remote text terminals) +and generates a new DOSEMU +keytable out of the kernel information. This currently seems +only to work for latin-1 layouts, the latin-2 type of accents +seem not to exist so far in the kernel (linux/keyboard.h). +The resulting table can be monitor with DOSEMU 'keytable dump' +feature (see README-tech.txt) for details).

When being on console you might wish to use raw keyboard, especially +together with some games, that don't use the BIOS/DOS to get their +keystrokes. + +
      $_rawkeyboard = (1)
+ +However, be carefull, when the application locks, you may not be able +to switch your console and recover from this. For details on recovering +look at README-tech.txt (Recovering the console after a crash).


2.1.7. X Support settings

If DOSEMU is running in its own X-window (not xterm), you may need to +tailor it to your needs. Here a summary of the settings and a brief +description what they mean. A more detailed description of values +one can be found at chapter 2.2.14 (X Support settings) of README-tech.txt

    $_X_updfreq = (5)       # time between refreshs (units: 20 == 1 second)
+    $_X_title = "DOS in a BOX" # Title in the top bar of the window
+    $_X_icon_name = "xdos"  # Text for icon, when minimized
+    $_X_keycode = (off)     # on == translate keybord via dosemu keytables
+    $_X_blinkrate = (8)     # blink rate for the cursor
+    $_X_font = ""           # basename from /usr/X11R6/lib/X11/fonts/misc/*
+                            # (without extension) e.g. "vga"
+    $_X_mitshm = (on)       # Use shared memory extensions
+    $_X_sharecmap = (off)   # share the colormap with other applications
+    $_X_fixed_aspect = (on) # Set fixed aspect for resize the graphics window
+    $_X_aspect_43 = (on)    # Always use an aspect ratio of 4:3 for graphics
+    $_X_lin_filt = (off)    # Use linear filtering for >15 bpp interpol.
+    $_X_bilin_filt = (off)  # Use bi-linear filtering for >15 bpp interpol.
+    $_X_mode13fact = (2)    # initial factor for video mode 0x13 (320x200)
+    $_X_winsize = ""        # "x,y" of initial windows size
+    $_X_gamma = (1.0)       # gamma correction
+    $_X_vgaemu_memsize = (1024) # size (in Kbytes) of the frame buffer
+                            # for emulated vga
+    $_X_lfb = (on)  # use linear frame buffer in VESA modes
+    $_X_pm_interface = (on) # use protected mode interface for VESA modes
+    $_X_mgrab_key = ""      # KeySym name to activate mouse grab, empty == off
+    $_X_vesamode = ""       # "xres,yres ... xres,yres"
+                            # List of vesamodes to add. The list has to contain
+                            # SPACE separated "xres,yres" pairs


2.1.8. Builtin ASPI SCSI Driver

The builtin ASPI driver (a SCSI interface protocol defined by Adaptec) can be +used to run DOS based SCSI drivers that use this standard (most SCSI devices +ship with such a DOS driver). This enables you to run hardware on Linux, that +normally isn't supported otherwise, such as CD writers, Scanners e.t.c. +The driver was successfully tested with Dat-streamers, EXABYTE tapedrives, +JAZ drives (from iomega) and CD writers. To make it work under DOSEMU +you need + +

  • to configure $_aspi to define which +of the /dev/sgX devices you want to show up in DOSEMU.

  • to load the DOSEMU aspi.sys stub driver within config.sys +(e.g. DEVICE=ASPI.SYS) before any ASPI using driver.

The $_aspi variable takes strings listing all generic SCSI +devices, that you want give to DOSEMU. NOTE: You should make sure, +that they are not used by Linux elsewhere, else you would come into +much trouble. To help you not doing the wrong thing, DOSEMU can +check the devicetype of the SCSI device such as + +
    $_aspi = "sg2:WORM"
+ +in which case you define /dev/sg2 being a CD writer device. If you omit +the type, + +
                                                        
+    $_aspi = "sg2 sg3 sg4"
+ +DOSEMU will refuse any device that is a disk drive (imagine, what would happen +if you try to map a CD writer to the disk which contains a mounted Linux FS?). +If you want to map a disk drive to DOSEMU's ASPI driver, you need to +tell it explicitely + +
    $_aspi = "sg1:Direct-Access"
+ +or + +
    $_aspi = "sg1:0"
+ +and as you can see, `Direct-Access' is the devicetype reported by + +
    $ cat /proc/scsi/scsi
+ +which will list all SCSI devices in the order they are assigned to +the /dev/sgX devices (the first being /dev/sg0). You may also use the +DOSEMU supplied tool `scsicheck' (in src/tools/peripher), which helps +a lot to get the configuration right: + +
    $ scsicheck
+    sg0 scsi0 ch0 ID0 Lun0 ansi2 Direct-Access(0) IBM DCAS-34330 S61A
+        $_aspi = "sg0:Direct-Access:0" (or "0/0/0/0:Direct-Access:0")
+    sg1 scsi0 ch0 ID5 Lun0 ansi2 Direct-Access(0) IOMEGA ZIP 100 D.08
+        $_aspi = "sg1:Direct-Access:5" (or "0/0/5/0:Direct-Access:5")
+    sg2 scsi0 ch0 ID6 Lun0 ansi2 CD-ROM(5) TOSHIBA CD-ROM XM-5701TA 0167
+        $_aspi = "sg2:CD-ROM:6" (or "0/0/6/0:CD-ROM:6") <== multiple IDs
+    sg3 scsi1 ch0 ID4 Lun0 ansi2 Sequential-Access(1) HP C1533A 9503
+        $_aspi = "sg3:Sequential-Access:4" (or "1/0/4/0:Sequential-Access:4")
+    sg4 scsi1 ch0 ID6 Lun0 ansi1 WORM(4) IMS CDD522/10 1.07
+        $_aspi = "sg4:WORM:6" (or "1/0/6/0:WORM:6") <== multiple IDs

In the above example there are two scsi hostadapters (scsi0 and scsi1) and +DOSEMU will not show more than one hostadapter to DOS (mapping them +all into one), hence you would get problems accessing sg2 and sg4. For this +you may remap a different targetID such as + +
    $_aspi = "sg2:CD-ROM:5 sg4:WORM"
+ +and all would be fine. From the DOS side the CD-ROM appears as target 5 +and the WORM (CD writer) as target 6. +Also from the above scsicheck output, you can see, that you can opt +to use a `host/channel/ID/LUN' construct in place of `sgX' such as + +
    $_aspi = "0/0/6/0:CD-ROM:5 1/0/6/0:WORM"
+ +which is exactly the same as the above example, exept it will assign +the right device, even if for some reasons you have changed the order +in which sgX device are assigned by the kernel. Those changes happen, if +you turned off power of one device `between' or if you play with dynamic +allocation of scsi devices via the /proc/scsi interface such as + +
    echo "scsi remove-single-device 0 0 5 0" >/proc/scsi/scsi
+ +to delete a device and + +
    echo "scsi add-single-device 0 0 5 0" >/proc/scsi/scsi
+ +to add a device. HOWEVER, we strongly discourage +you to use these kernel feature for temporaryly switching off +power of connected devices or even unplugging them: normal SCSI busses +are not hotpluggable. Damage may happen and uncontroled voltage +bursts during power off/on may lock your system !!!

Coming so far, one big problem remains: the (hard coded) buffersize for +the sg devices in the Linux kernel (default 32k) may be to small for DOS +applications and, if your distributor yet didn't it, you may need to +recompile your kernel with a bigger buffer. The buffer size is defined +in linux/include/scsi/sg.h and to be on the secure side you may define + +
    #define SG_BIG_BUFF (128*1024-512)  /* 128 Kb buffer size */
+ +though, CD writers are reported to work with 64Kb and the `Iomega guest' +driver happily works with the default size of 32k.


2.1.9. COM ports and mice

We have simplified the configuration for mice and serial ports and +check for depencies between them. If all strings in the below example +are empty, then no mouse and/or COM port is available. Note. that you +need no mouse.com driver installed in your DOS environment, DOSEMU +has the mousedriver builtin. The mouse settings below only apply to +DOSEMU in the Linux console; in X and terminals the mouse is detected +automatically. The below example is such a setup

      $_com1 = ""           # e.g. "/dev/mouse" or "/dev/ttyS0"
+      $_com2 = "/dev/modem" # e.g. "/dev/modem" or "/dev/ttyS1"
+    
+      $_mouse = "microsoft" # one of: microsoft, mousesystems, logitech,
+                            # mmseries, mouseman, hitachi, busmouse, ps2
+      $_mouse_dev = "/dev/mouse" # one of: com1, com2 or /dev/mouse
+      $_mouse_flags = ""	# list of none or one or more of:
+      			# "emulate3buttons cleardtr"
+      $_mouse_baud = (0)	# baudrate, 0 == don't set

The above example lets you have your modem on COM2, COM1 is spare (as +you may have your mouse under native DOS there and don't want to change +the configuration of your modem software between boots of native DOS and Linux)

However, you may use your favorite DOS mouse driver and directly let it +drive COM1 by changing the below variables (rest of variables unchanged)

      $_com1 = "/dev/mouse"
+      $_mouse_dev = "com1"

And finaly, when you have a PS/2 or USB mouse on your machine you use +the built-in mouse driver (not your mouse.com) to get it work: +( again leaving the rest of variables unchanged)

      $_mouse = "ps2"
+      $_mouse_dev = "/dev/mouse"  

When using a PS/2 or USB mouse or when having more than 2 serial ports you may +of course assign _any_ free serialdevice to COM1, COM2. The order doesn't +matter:

      $_com1 = "/dev/ttyS2"
+      $_com2 = "/dev/ttyS0"


2.1.10. Printers

Printer is emulated by piping printer data to your normal Linux printer. +The belows tells DOSEMU which printers to use. The `timeout' tells DOSEMU +how long to wait after the last output to LPTx before considering the +print job as `done' and to close it.

    # Print commands to use for LPT1, LPT2 and LPT3.
+    # Default: "lpr -l, lpr -l -P lpt2, lpr -l P lpt3"
+    # Which means: use the default print queue for LPT1, "lpt2" queue for LPT2,
+    # "lpt3" queue for LPT3. "-l" means raw printing mode (no preprocessing).
+    
+    $_lpt1 = "lpr -l"
+    $_lpt2 = "lpr -l -P lpt2"
+    $_lpt3 = "lpr -l -P lpt3"
+    
+    $_printer_timeout = (20)# idle time in seconds before spooling out


2.1.11. Sound

The following settings will tell DOSEMU to emulate an SB16 sound +card using your Linux sound drivers. For more information see +sound-usage.txt.

    $_sound = (on)            # sound support on/off
+    $_sb_base = (0x220)       # base IO-address (HEX)
+    $_sb_irq = (5)            # IRQ
+    $_sb_dma = (1)            # Low 8-bit DMA channel
+    $_sb_hdma = (5)           # High 16-bit DMA channel
+    $_sb_dsp = "/dev/dsp"     # Path to the sound device
+    $_sb_mixer = "" 	  # path to the mixer control
+    $_mpu_base = (0x330)      # base address for the MPU-401 chip (HEX)


2.1.12. Joystick

Here are the settings for Joystick emulation.

    $_joy_device = "/dev/js0 /dev/js1"     
+    			  # 1st and 2nd joystick device 
+                              # e.g. "/dev/js0" or "/dev/js0 /dev/js1"
+                              #      (or "" if you don't want joystick support)
+    			  # 
+    $_joy_dos_min = (1)	  # range for joystick axis readings, must be > 0
+    $_joy_dos_max = (150)	  # avoid setting this to > 250
+    $_joy_granularity = (1)   # the higher, the less sensitive - 
+    			  # useful if you have a wobbly joystick
+                              #
+    $_joy_latency = (1)       # delay between nonblocking linux joystick reads
+                              # increases performance if >0 and processor>=Pentium
+                              # recommended: 1-50ms or 0 if unsure


2.1.13. Networking under DOSEMU

Turn the following option `on' if you require IPX/SPX emulation, +there is no need to load IPX.COM within the DOS session. +( the option does not emulate LSL.COM, IPXODI.COM, etc. ) +And NOTE: You must have IPX protocol configured into the kernel.

      $_ipxsupport = (on)

Enable Novell 8137->raw 802.3 translation hack in new packet driver.

      $_novell_hack = (on)

If you make use of TUN/TAP driver, then you can select it via

      $_vnet = "tap"

But if you just need 'single' packet driver support that talks to, for +instance, your ethernet card eth0 then you need to set

      $_netdev = "eth0"

Note that dosnet and eth0 require raw packet access, and hence +(suid)-root. If $_vnet = "dosnet", then $_netdev will default to "dsn0". +If you would like to use persistent TUN/TAP devices then you +need to specifify the TAP device in $_netdev. +For more on this look at chapter 15 (Networking using DOSEMU).


2.1.14. Settings for enabling direct hardware access

The following settings (together with the direct console video settings +below make it possible for DOSEMU to access your real (non-emulated) +computer hardware directly. Because Linux does not permit this for +ordinary users, DOSEMU needs to be run as root, via sudo or suid-root +to be able to use these settings. They can NOT be overwritten by the +user configuration file ~/.dosemurc. +You must also run dosemu with the "-s" switch. This activates sudo +when appropriate; without it, even root will not get direct hardware +access.

Here you tell DOSEMU what to do when DOS wants let play the speaker: + +
      $_speaker = ""     # or "native" or "emulated"

And with the below may gain control over real ports on you machine. +But:

WARNING: GIVING ACCESS TO PORTS IS BOTH A SECURITY CONCERN AND +SOME PORTS ARE DANGEROUS TO USE. PLEASE SKIP THIS SECTION, AND +DON'T FIDDLE WITH THIS SECTION UNLESS YOU KNOW WHAT YOU'RE DOING.

      $_ports = ""  # list of portnumbers such as "0x1ce 0x1cf 0x238"
+                    # or "0x1ce range 0x280,0x29f 310"
+                    # or "range 0x1a0,(0x1a0+15)"

If you have hardware, that is not supported under Linux but you have +a DOS driver for, it may be necessary to enable IRQ passing to DOS. +Note that IRQ passing does not work on x86-64. + +
      $_irqpassing = ""  # list of IRQ number (2-15) to pass to DOS such as
+                         # "3 8 10"


2.1.15. Video settings ( console only )

!!WARNING!!: IF YOU ENABLE GRAPHICS ON AN INCOMPATIBLE ADAPTOR, +YOU COULD GET A BLANK SCREEN OR MESSY SCREEN EVEN AFTER EXITING DOSEMU. +Read doc/README-tech.txt (Recovering the console after a crash).

Start with only text video using the following setup

      $_video = "vga"         # one of: plainvga, vga, ega, mda, mga, cga
+      $_console = (0)         # use 'console' video
+      $_graphics = (0)        # use the cards BIOS to set graphics
+      $_vbios_seg = (0xc000)  # set the address of your VBIOS (e.g. 0xe000)
+      $_vbios_size = (0x10000)# set the size of your BIOS (e.g. 0x8000)
+      $_vmemsize = (0)        # amount of video RAM to save/restore
+      $_chipset = ""       
+      $_dualmon = (0)         # if you have one vga _plus_ one hgc (2 monitors)

After you get it `somehow' working and you have one of the DOSEMU supported +graphic cards you may switch to graphics mode changing the below

      $_graphics = (1)        # use the cards BIOS to set graphics

If you have a 100% compatible standard VGA card that may work, +however, you get better results, if your card has one of the DOSEMU supported +video chips and you tell DOSEMU to use it such as

      $_chipset = "s3"        # one of: plainvga, trident, et4000, diamond, s3,
+                              # avance, cirrus, matrox, wdvga, paradise, ati, sis,
+    			  # svgalib, vesa

Note, `s3' is only an example, you must set the correct video chip +else it most like will crash your screen.

The 'svgalib' setting uses svgalib 1.9.21 or greater for determining the correct +video chip.

The 'vmemsize' setting's default of 0 causes DOSEMU to try to autodetect +the amount of video memory on the graphics card. This amount is saved +and restored when you switch away from and back to the virtual console DOSEMU +runs in using the Ctrl-Alt-Fn key combination. Since saving video +memory can be a very slow operation you may want to restrict 'vmemsize' +to a value that was more common when DOS was still mainstream. For instance +1024 covers it if you want to be able to save and restore modes up to +1024x768x256.

NOTE: `video setting' can not be overwritten by ~/.dosemurc.


2.1.16. Time settings

By Paul Crawford. This is a short guide to the time modes supported in +DOSEMU; you can get +a more detailed description of how and why they operate at +http://www.sat.dundee.ac.uk/~psc/dosemu_time_advanced.html. There are three modes currently supported +using the $_timemode variable: + +
      $_timemode = "bios"
+      $_timemode = "pit"
+      $_timemode = "linux"
+ +Most users will only ever have a need for either BIOS or LINUX mode, +and the decision comes down to the two basic characteristics: +

  • In BIOS mode, the DOSEMU session begins with the current (local) +date & time, and once running it behaves fully like an emulated PC. +You can set the date & time using the normal DOS methods as you would expect. +However, time keeping accuracy in the long term under DOS was never very good, +and under DOSEMU it is typically poorer.

  • In LINUX mode, the emulated PC always reports the current host time, +and so you no longer have the option to set the DOS time (the date can be +set, which is odd, as this is kept in DOS and not in the emulator). In +this mode you get the long term accuracy of the host, so if you are +running NTP or similar, you always get accurate time.

+ +The PIT mode is an odd compromise, it provides BIOS-like time +(settable, decoupled from host time), but with slightly higher +accuracy. In summary, if you need accurate time, choose LINUX mode, +otherwise if you need time setting capabilities or close 'real' PC +emulation, choose BIOS mode.

A reasonable question for some applications is +"how do I get accurate UTC time?", of course, the more general question is +about running the emulated DOS session in a different timezone and/or with +differing "daylight saving" options to the host computer. +This is quite simple in fact, just start DOSEMU with the environment +variable TZ set to the required zone, and don't forget to have the +corresponding command in the DOS autoexec.bat file (otherwise +Microsoft products all assume -8 hours PST!). The DOSEMU emulation +of file system access is also based on the local timezone it is running in, +so you will get consistent file time stamps compared to the emulated time. +So in the UTC case, you would start DOSEMU in a shell with TZ=GMT0, and +have a line in autoexec.bat with "set TZ=GMT0" before any of your programs +are run.


3. Security

This part of the document by Hans Lermen, +<lermen@fgan.de> +on Apr 6, 1997.

These are the hints we give you, when running dosemu on a machine that is +(even temporary) connected to the internet or other machines, or that +otherwise allows 'foreign' people login to your machine.

  • Don't set the "s" bit, as DOSEMU can run in +lowfeature mode without the "s" bit set. If you want fullfeatures +for some of your users, it is recommended to use "sudo". Alternatively +you can just use the keyword `nosuidroot' in +/etc/dosemu.users to forbid some (or all) users execution of +a suid root running dosemu (they may use a non-suid root copy of +the binary though). +DOSEMU now drops its root privileges before booting; however +there may still be security problems in the initialization code, +and by making DOSEMU suid-root you can give users direct access to +resources they don't normally have access too, such as selected I/O +ports, hardware IRQs and hardware RAM. +For any direct hardware access you must always use the "-s" switch.

    If DOSEMU is invoked via "sudo" then it will automatically switch to +the user who invoked "sudo". An example /etc/sudoers entry is this: +
        joeuser  hostname=(root) NOPASSWD: /usr/local/bin/dosemu.bin
    +If you use PASSWD instead of NOPASSWD then users need to type their +own passwords when sudo asks for it. The "dosemu" script can be +invoked using the "-s" option to automatically use sudo.

  • Use proper file permissions to restrict access to a +suid root DOSEMU binary in addition to /etc/dosemu.users `nosuidroot'. +( double security is better ).

  • NEVER let foreign users execute dosemu under root login !!! +(Starting with dosemu-0.66.1.4 this isn't necessary any more, +all functionality should also be available when running as user)


4. Sound

The SB code is currently in a state of flux. Some changes to the code have been +made which mean that I can separate the DSP handling from the rest of the SB +code, making the main case statements simpler. In the meantime, Rutger +Nijlunsing has provided a method for redirecting access to the MPU-401 chip +into the main OS.


4.1. Using the MPU-401 "Emulation"

The Sound driver opens "/var/run/dosemu-midi" and +writes the Raw MIDI data to this. A daemon is provided which can be can be used +to seletc the instruments required for use on some soundcards. It is also +possible to get various instruments by redirecting '/var/run/dosemu-midi' to +the relevant part of the sound driver eg:

    % ln -s /dev/midi /var/run/dosemu-midi

This will send all output straight to the default midi device and use whatever +instruments happen to be loaded.


4.2. The MIDI daemon

      make midid

This compiles and installs the midi daemon. The daemon currently has support +for the 'ultra' driver and partial support for the 'OSS' driver (as supplied +with the kernel) and for no midi system. Support for the 'ultra' driver will +be compiled in automatically if available on your system.

Copy the executable './bin/midid' so that it is on your path, or somewhere you +can run it easily.

Before you run DOSEMU for the first time, do the following: + +
      mkdir -p ~/.dosemu/run           # if it doesen't exist
+      rm -f ~/.dosemu/run/dosemu-midi
+      mknod ~/.dosemu/run/dosemu-midi p

Then you can use the midi daemon like this: + +
      ./midid < ~/.dosemu/run/dosemu-midi &; dosemu

(Assuming that you put the midid executeable in the directory you run DOSEMU +from.)


4.3. Disabling the Emulation at Runtime

You can disable the SB emulation by changing the 'sound' variable in +/etc/dosemu.conf to 'off'. There is currently no way to specify at runtime +which SB model DOSEMU should emulate; the best you can do is set the T value +of the BLASTER environment variable (see sound-usage.txt), but not all +programs will take note of this.


5. Using Lredir

This section of the document by Hans, +<lermen@fgan.de>. Last +updated on October, 23 2002.

What is it? Well, its simply a small DOS program that tells the +MFS (Mach File System) code what 'network' drives to redirect. +With this you can 'mount' any Linux directory as a virtual drive +into DOS. In addition to this, Linux as well as multiple dosemu sessions +may simultaneously access the same drives, what you can't when using +partition access.


5.1. how do you use it?

Mount your dos hard disk partition as a Linux subdirectory. +For example, you could create a directory in Linux such as /dos (mkdir +-m 755 /dos) and add a line like

       /dev/hda1       /dos     msdos   umask=022

to your /etc/fstab. (In this example, the hard disk is mounted read- +only. You may want to mount it read/write by replacing "022" with +"000" and using the -m 777 option with mkdir). Now mount /dos. Now +you can add a line like

      lredir d: linux\fs/dos

to the AUTOEXEC.BAT file in your hdimage (but see the comments +below). On a multi-user system you may want to use

      lredir d: linux\fs\${home}

where "home" is the name of an environmental variable that contains +the location of the dos directory (/dos in this example)

You may even redirect to a NFS mounted volume on a remote machine with +a /etc/fstab entry like this + +
       otherhost:      /dos     nfs     nolock
+ +Note that the nolock> option might be +needed for 2.2.x kernels, because +apparently the locks do not propagate fast enough and DOSEMU's (MFS code) +share emulation will fail (seeing a lock on its own files).

In addition, you may want to have your native DOS partion as C: under dosemu. +To reach this aim you also can use Lredir to turn off the 'virtual' +hdimage and switch on the real drive C: such as this:

Assuming you have a c:\dosemu directory on both drives (the virtual +and the real one) and have mounted your DOS partition as /dosc, +you then should have the following files on the virtual drive:

autoexec.bat:

      lredir z: linux\fs\dosc
+      copy c:\dosemu\auto2.bat z:\dosemu\auto2.bat
+      lredir del z:
+      c:\dosemu\auto2.bat

dosemu\auto2.bat:

      lredir c: linux\fs\dosc
+      rem further autoexec stuff

To make the reason clear why the batch file (not necessaryly autoexec.bat) +must be identical:

Command.com, which interpretes the batchfile keeps a position pointer +(byte offset) to find the next line within this file. It opens/closes the +batchfile for every new batchline it reads from it. +If the batchfile in which the 'lredir c: ...' happens is on c:, then +command.com suddenly reads the next line from the batchfile of that +newly 'redired' drive. ... you see what is meant?


5.2. Other alternatives using Lredir

To have a redirected drive available at time of config.sys you may +either use emufs.sys such as

       device=c:\emufs.sys /dosc

or make use of the install instruction of config.sys (but not both) such as

       install=c:\lredir.exe c: linux\fs\dosc

The later has the advantage, that you are on your native C: from the +beginning, but, as with autoexec.bat, both config.sys must be identical.

+For information on using 'lredired' drives as a 'user' (ie having the right +permissions), please look at the section on Running dosemu as a normal user.


6. Running dosemu as a normal user

This section of the document by Hans, +<lermen@fgan.de>. Last +updated on Jan 21, 2003.

  1. In the default setup, DOSEMU does not have root privileges. This means it +will not have direct access to ports, external DOSish hardware and won't +use the console other than in normal terminal mode, but is fully capable +to do anything else. See the previous section on how to enable privileged +operation if you really need to.

  2. If a user needs access to privileged resources other than console graphics, +then you may need to explicitly allow the user to do so by editing the file +/etc/dosemu.users (or /etc/dosemu/dosemu.users). +The format is:

             loginname [ c_strict ] [ classes ...] [ other ]

    For example, to allow joeuser full access you can use + +
             joeuser c_all

  3. The msdos partitions, that you want to be accessable through +Section 5 should +be mounted with proper permissions. I recommend doing this via 'group's, +not via user ownership. Given you have a group 'dosemu' for this and want +to give the user 'lermen' access, then the following should be

    • in /etc/passwd: + +
               lermen:x:500:100:Hans Lermen:/home/lermen:/bin/bash
      +                      ^^^-- note: this is NOT the group id of 'dosemu'

    • in /etc/group: + +
               users:x:100:
      +         dosemu:x:200:dosemu,lermen
      +                  ^^^

    • in /etc/fstab: + +
               /dev/hda1 /dosc msdos defaults,gid=200,umask=002 0 0
      +                                            ^^^

    Note: the changes to /etc/passwd and /etc/group only take place the +next time you login, so don't forget to re-login.

    The fstab entry will mount /dosc such that is has the proper permissions + +
           ( drwxrwxr-x  22 root     dosemu      16384 Jan  1  1970 /dosc )

    You can do the same with an explicit mount command: + +
              mount -t msdos -o gid=200,umask=002 /dev/hda1 /dosc 

    Of course normal lredir'ed unix directories should have the same +permissions.

  4. Make sure you have read/write permissions of the devices you +configured (in /etc/dosemu.conf) for serial and mouse.

Starting with dosemu-0.66.1.4 there should be no reason against running +dosemu as a normal user. The privilege stuff has been extensively reworked, +and there was no program that I tested under root, that didn't also run +as user. Normally dosemu will permanently run as user and only temporarily +use root privilege when needed (during initialization) and then drop its +root privileges permanently. In case of non-suid root +(as of dosemu-0.97.10), it will run in lowfeature mode without any privileges.


7. Using CDROMS

7.1. The built-in driver

This documents the cdrom extension rucker@astro.uni-bonn.de has +written for Dosemu.

An easy way to access files on a CDROM is to mount it in Linux and use +Lredir to refer to it. However, any low-level access, often used by +games is impossible that way, unless you use the C option. +For that you need to load some drivers in DOS. CDROM image files (ISOs) +can be used in a similar fashion.

The driver consists of a server on the Linux side +(src/dosext/drivers/cdrom.c, accessed via int 0xe6 handle 0x40) and a +device driver (src/commands/cdrom.S) on the DOS side.

Please send any suggestions and bug reports to <rucker@astro.uni-bonn.de>

To install it: + +

  • Create a (symbolic) link /dev/cdrom to the device file of your drive +or use the cdrom statement in dosemu.conf to define it.

  • Make sure that you have read/write access to the device file of your +drive, otherwise you won't be able to use the cdrom under Dosemu +directly because of security reasons.

  • Load cdrom.sys within your config.sys file with e.g.:

            devicehigh=d:\dosemu\cdrom.sys

  • Mount the CD-ROM in Linux (some distributions do this automatically), and +use

            lredir e: linux\fs/media/cdrom c

    to access the CD-ROM as drive E:. The "C" option specifies that the +low-level access provided via cdrom.sys is used. Or ... +start Microsoft cdrom extension as follows:

            mscdex /d:mscd0001 /l:driveletter
    +or +
            shsucdex /d:mscd0001 /l:driveletter

To change the cd while Dosemu is running, use the DOS program 'eject.com'. +If is not possible to change the disk, when the drive has been opened by +another process (e.g. mounted), then you need to unmount it first!

Lredir has the advantage of supporting long file names, and not using +any DOS memory, whereas MS/SHSUCDX are more low-level and perhaps more +compatible. You would need to use a DOS TSR driver such as DOSLFN to +use long file names with SHSUCDX.

Remarks by zimmerma@rz.fht-esslingen.de:

This driver has been successfully tested with Linux' SCSI CDROMS by the +author, with the Mitsumi driver mcd.c and with the Aztech/Orchid/Okano/Wearnes- +CDROM driver aztcd.c by me. With the latter CDROM-drives changing the CD-ROM +is not recognized correctly by the drive under all circumstances and is +therefore disabled. So eject.com will not work. +For other CD-ROM drives you may enable this feature by setting the variable +'eject_allowed = 1' in file dosemu/drivers/cdrom.c (you'll find it near the +top of the file). With the mcd.c and aztcd.c Linux drivers this may cause your +system to hang for some 30 seconds (or even indefinitely), so don't change the +default value 'eject_allowed = 0'.

Support for up to 4 drives:

If you have more then one cdrom, you can use the cdrom statement +in dosemu.conf like this: + +
      $_cdrom = "/dev/cdrom /dev/cdrom2 image.iso"
+ +and have multiple instancies of the DOS driver: + +
      device=cdrom.sys
+      device=cdrom.sys 2
+      device=cdrom.sys 3

The one and only parameter to the device driver is a digit between 1 and 4, +(if its missing then 1 is assumed) +for the DOS devices MSCD0001, MSCD0002 ... MSCD0004 respectively. You then +also need to use lredir or tell MSCDEX about these drivers such as + +
       
+        lredir e: linux\fs/media/cdrom c
+        lredir f: linux\fs/media/cdrom2 c 2
+        lredir g: linux\fs/media/cdrom3 c 3
+where you need to loop-mount the image file, or +
       
+        mscdex /d:mscd0001 /d:mscd0002 /l:driveletter
+ + +In this case the /l: argument defines the driveletter of the first /d:, +the others will get assigned successive driveletters.

History:

Release with dosemu.0.60.0 +Karsten Rucker (rucker@astro.uni-bonn.de) +April 1995

Additional remarks for mcd.c and aztcd.c +Werner Zimmermann (zimmerma@rz.fht-esslingen.de) +May 30, 1995

Release with dosemu-0.99.5 +Manuel Villegas Marin (manolo@espanet.com) +Support for up to 4 drives +December 4, 1998


8. Using X

This chapter provides some hints and tips for using DOSEMU in X.


8.1. Basic information

If you start dosemu in X it brings up its own window, +in which you can +also execute graphical programs such as games. To force text-only execution +of DOSEMU in an xterm or other terminal (konsole, gnome-terminal, and so on), +you need to run dosemu -t.

Use dosemu -w to start DOSEMU in fullscreen mode. +When running DOSEMU, +you can flip between fullscreen and windowed mode by pressing +<Ctrl><Alt><F>. +The graphics window is resizeable.

Some DOS applications want precise mouse control that is only possible +if the mouse cursor is trapped in the DOSEMU window. To enable this you +need to grab the mouse by pressing <Ctrl><Alt><Home>. +Similarly, you can grab the keyboard by pressing +<Ctrl><Alt><K>, or +<Ctrl><Alt><Shift><K> if your +window manager already grabs <Ctrl><Alt><K>. +After you grab the keyboard +all key combinations (including <Alt><Tab> and so on) +are passed to DOSEMU. +In fullscreen mode the mouse and keyboard are both automatically grabbed.

Use <Ctrl><Alt><Pause> to pause and unpause +the DOSEMU session, which is useful if you want it to sit silently +in the background when it is eating too much CPU time. +Press <Ctrl><Alt><PgDn> or click the close button +of the window to exit DOSEMU.

DOSEMU uses bitmapped internal fonts by default, so it can accurately +simulate a real VGA card text mode. It is also possible to use X fonts. +The advantages of these is that they may be easier on the eyes, and +are faster, in particular if you use DOSEMU remotely. Any native DOS font +setting utilities do not work, however. To set an X font use the +provided xmode.com utility, using +
    xmode -font vga
+at the DOS prompt or +
    $_X_font = "vga"
+in dosemu.conf. The provided fonts are vga, vga8x19, vga11x19, vga10x24, +vga12x30, vga-cp866, and vga10x20-cp866.

If the mouse is not grabbed, then you can copy and paste text if the DOSEMU +window is in text mode. This uses the standard X mechanism: select by +dragging the left mouse button, and paste by pressing the middle mouse +button.


8.2. More detailed information, hints and tips

What you might take care of:

  • If backspace and delete do not work, you can try + 'xmodmap -e "keycode 22 = 0xff08"' to get use of your backspace key, and

  • 'xmodmap -e "keycode 107 = 0xffff"' to get use of your delete key.

  • Make sure DOSEMU has X support compiled in. The configure script + complains loudly if it does not find X development libraries.

  • There are some X-related configuration options for dosemu.conf. +See the file itself for details.

  • Keyboard support of course depends on your X keyboard mappings (xmodmap). +If certain keys don't work (like Pause, Backspace,...), it *might* be +because you haven't defined them in your xmodmap, or defined to something +other than DOSEMU expects.

  • using the provided icon (dosemu.xpm):

    • you need the xpm (pixmaps) package. If you're not sure, look for +a file like /usr/X11R6/lib/libXpm.so.*

    • you also need a window manager which supports pixmaps. Fvwm is fine, +but I can't tell you about others. Twm probaby won't do.

    • copy dosemu.xpm to where you usually keep your pixmap (not bitmap!) +files (perhaps /usr/share/pixmaps)

    • tell your window manager to use it. For fvwm, add the following +line to your fvwmrc file:

               
      +         Icon "xdosemu"   dosemu.xpm

      This assumes you have defined a PixmapPath. Otherwise, specify the +entire pathname.

    • note that if you set a different icon name than "xdosemu" in your +dosemu.conf, you will also have to change the fvwmrc entry.

    • restart the window manager. There's usually an option in the +root menu to do so.

    Now you should see the icon whenever you iconify xdosemu.

    Note that the xdosemu program itself does not include the icon - that's +why you have to tell the window manager. I chose to do it this way +to avoid xdosemu requiring the Xpm library.

  • If anything else does not work as expected, don't panic :-) +Remember the thing is still under construction. +However, if you think it's a real bug, please tell me.


8.3. The VGA Emulator

In X, a VGA card is emulated. The same happens if you use the SDL library +using dosemu -S. This emulation (vgaemu) enables +X to run graphics modes.

Some features: +

  • Video memory. 1 Mb is allocated. It is mapped with mmap() in the VGA +memory region of DOSEMU (0xa00000-0xbfffff) to support bank switching. +This is very i386-Linux specific, don't be surprised if it doesn't work +under NetBSD or another Linux flavour (Alpha/Sparc/MIPS/etc).

  • The DAC (Digital to Analog Converter). The DAC is completely emulated, +except for the pelmask. This is not difficult to implement, but it is +terribly slow because a change in the pelmask requires a complete redraw +of the screen. Fortunately, the pelmask changes aren't used often so +nobody will notice ;-)

  • The attribute controller is emulated.

  • The emulator emulates a basic Trident TVGA8900C graphics card. + All standard VGA modes are emulated, most VGA hardware features (mode-X, + 320x240 and so on), some Trident extensions, and on + top of that many high-resolution VESA 2.0 modes, that were not present + in the original Trident card. + Some (very few) programs, such as Fast Tracker, play intimately with the + VGA hardware and may not work. As vgaemu improves these problems should + disappear.

  • Nearly full VESA 2.0 support.

  • A VESA compatible video BIOS is mapped at 0xc00000. It is small because + it only contains some glue code and the BIOS fonts (8x8, 8x14, 8x16).

  • support for hi- and true-color modes (using Trident SVGA mode numbers +and bank switching)

  • Support for mode-X type graphics modes (non-chain4 modes as used by e.g. DOOM)

  • gamma correction for graphics modes

  • video memory size is configurable via dosemu.conf

  • initial graphics window size is configurable

  • The current hi- (16bpp) and true (24/32bpp) color support does +not allow resizing of the graphics window and gamma correction +is ignored.

As the typical graphics mode with 320x200x8 will be used often +with large scalings and modern graphics boards are pretty fast, +Steffen Winterfeldt added something to eat up your CPU time: you can turn on +the bilinear interpolation. It greatly improves the display +quality (but is rather slow as I haven't had time yet to implement +an optimized version - it's plain C for now). +If the bilinear filter is too slow, you might instead try the linear +filter which interpolates only horizontally.

Note that (bi)linear filtering is not available on all +VGA/X display combinations. The standard drawing routines +are used instead in such cases.

If a VGA mode is not supported on your current X display, the graphics +screen will just remain black. Note that this does not mean +that DOSEMU has crashed.

The only unsupported VBE function is VGA state save/restore. But this +functionality is rarely used and its lack should not cause +too much problems.

VBE allows you to use the horizontal and vertical scrolling +function even in text modes. This feature is not implemented.

If you think it causes problems, the linear frame buffer (LFB) +can be turned of via dosemu.conf as well as the protected mode +interface. Note, however, that LFB modes are faster than +banked modes, even in DOSEMU.

The default VBE mode list defines a lot of medium resolution +modes suitable for games (like Duke3D). You can still +create your own modes via dosemu.conf. Note that you +cannot define the same mode twice; the second (and all +subsequent) definitions will be ignored.

Modes that are defined but cannot be supported due +to lack of video memory or because they cannot be +displayed on your X display, are marked as unsupported +in the VBE mode list (but are still in it).

The current interface between VGAEmu and X will try to update +all invalid video pages at a time. This may, particularly +in hi-res VBE/SVGA modes, considerably disturb DOSEMU's signal +handling. That cannot be helped for the moment, but will +be addressed soon (by running an extra update thread).

If you really think that this is the cause of your problem, you might +try to play with veut.max_max_len in env/video/render.c. +This variable limits the amount of video memory that is updated +during one timer interrupt. This way you can dramatically +reduce the load of screen updates, but at the same rate reduce your +display quality.

Gamma correction works in both 4 and 8 bit modes. It must be specified +as a float value, e.g. $_X_gamma=(1.0). Higher values +give brighter graphics, lower make them darker. Reasonable +values are within a range of 0.5 ... 2.0.

You can specify the video memory size that the VGA emulator +should use in dosemu.conf. The value will be rounded up +to the nearest 256 kbyte boundary. You should stick to typical +values like 1024, 2048 or 4096 as not to confuse DOS applications. +Note that whatever value you give, 4 bit modes are only +supported up to a size of 800x600.

You can influence the initial size of the graphics window in various +ways. Normally it will have the same size (in pixel) as the VGA graphics +mode, except for mode 0x13 (320x200, 256 colors), which will be scaled by +the value of mode13fact (defaults to 2). +Alternatively, you can directly specify a window size in dosemu.conf via +$_X_winsize. You can still resize the window later.

The config option $_X_fixed_aspect allows you to fix the aspect ratio +of the graphics window while resizing it. Alternatively, $_X_aspect_43 +ties the aspect ratio to a value of 4:3. The idea behind this is that, whatever +the actual resolution of a graphics mode is in DOS, it is displayed on +a 4:3 monitor. This way you won't run into problems with modes such +as 640x200 (or even 320x200) which would look somewhat distorted otherwise.

For planar modes (for instance, most 16 colour modes, but also certain +256-colour modes: mode-X), vgaemu has to +switch to partial cpu emulation. This can be slow, but expect it to +improve over time.


9. Running Windows under DOSEMU

DOSEMU can run any 16bit Windows up to 3.1, and has limited +support for Windows 3.11. You should be able to install and run +these versions of Windows without any extra work. There are still +a few caveats however, and if you have some problems, read on.


9.1. Mouse in Windows under DOSEMU

In Windows, the mouse cursor is not always in sync with the native X +mouse cursor. This problem can be easily avoided by enabling mouse grab. +There also exist an +alternative mouse driver +specially written to work in ungrabbed mode under many emulators or +on a real machine.


9.2. Windows 3.x in SVGA modes

If you want to run Windows in SVGA mode, you can either use the +Trident drivers, or the patched SVGA drivers of Windows 3.11.

The Trident drivers for Windows are in the files tvgaw31a.zip +and/or tvgaw31b.zip. Search www.winsite.com to get them. +Unpack the archive. In Windows setup, install the +Trident "800x600 256 color for 512K boards" driver.

Windows 3.11 comes with SVGA drivers. You can also download +them: search www.winsite.com for svga.exe and install the drivers. +Then go to +http://www.japheth.de/dwnload1.html +and get the SVGAPatch tool. This tool causes the above drivers to +work with most of the video cards in a real DOS environment, and +makes them DOSEMU-compatible too.


9.3. VxD support

By the time of writing this, DOSEMU does not have support +for Windows ring-0 device drivers (.vxd, .386). Fortunately, most of +Windows 3.1 drivers are ring-3 ones (.drv), so you can easily +install the Sound Blaster drivers, for instance. This is not the +case with Windows 3.11. Its network drivers are ring-0 ones, so +the native Winsock does not work. In order to get networking +operational, you need to get the Trumpet Winsock package. Refer +to chapter "Using Windows and Winsock" for more info on this.


9.4. DOS shell in Windows

There is probably little use of a DOS shell under Windows under +DOSEMU... But for those who need it, here are some basic hints. +The DOS shell is supported by DOSEMU only if Windows is running in +Standard mode. The Enhanced mode DOS shell is currently unsupported. +Note however, that unlike in a real DOS environment, under DOSEMU +the DOS shell of the Windows in Standard mode allows you to run +protected mode applications (in the real DOS environment only the DOS shell +of the Enhanced mode allows this).


10. The DOSEMU mouse

This section written by Eric Biederman <eric@dosemu.org>


10.1. Setting up the emulated mouse in DOSEMU

For most dos applications you should be able to use the +internal mouse with very little setup, and very little trouble.

Under X, or in terminal mode, you don't need to do anything, unless you want +to use the middle button then you need to add to autoexec.bat: + +
    emumouse 3
+ +On the console, in text mode, without root, the GPM library can be used, +and no extra setup is necessary. +Otherwise, especially with console graphics (sudo/suid/root, the -s switch, +and $_graphics=(1)), it takes just a tad bit more work:

in dosemu.conf: + +
    $_mouse = "mousesystems"
+    $_mouse_dev = "/dev/gpmdata"
+ +And in autoexec.bat: +
    emumouse 3
+ +This sets you up to use the gpm mouse repeater if you don't +use the repeater, you need to set $_mouse and $_mouse_dev to +different values. + +The GPM repeater might be configured to use a different protocol +than the default. If you are having problems, check the 'repeat_type' setting +in your gpm.conf. These are the mappings from the GPM repeat_type to the +DOSEMU $_mouse for common settings: + +
    GPM setting      DOSEMU setting
+    -------------------------------
+    msc (default)    mousesystems
+    ms3              microsoft
+    raw              select type of your real mouse


10.2. Problems

In X there are 2 ways applications can get into trouble.

The most common way is if they don't trust the mouse driver +to keep track of where the mouse is on the screen, and insist +on doing it themselves. win31 & geoworks are too applications +in this category. They read mouse movement from the mouse +driver in turns of mickeys i.e. they read the raw movement data.

To support this mouse driver then tracks where you were and where you +move to, in terms of x,y screen coordinates. Then works the standard +formulas backwards that calculate screen coordinates from mickeys to +generate mickeys from screen coordinates. And it feeds these mickeys +to the application. As long as the application and dosemu agree on +the same formulas for converting mickeys to screen coordinates all is +good.

The only real problem with this is sometimes X mouse and the +application mouse get out of sync. Especially if you take your +mouse cursor out of the dosemu window, and bring it back in again.

To compensate for getting out of sync what we do is whenever we +reenter the Xdos window is send a massive stream of mickeys heading +for the upper left corner of the screen. This should be enough to +kick us any an good mouse drivers screen limits. Then once we know +where we are we simulate movement for 0,0.

In practice this isn't quite perfect but it does work reasonably well.

The tricky part then is to get the application and dosemu to agree on +the algorithm. The algorithm we aim at is one mickey one pixel. +Dosemu does this by default under X but you can force it with. + +
    emumouse x 8 y 8 a
+ +To do this in various various applications generally falls under +the category of disable all mouse accelration.

for win31 you need + +
      MouseThreshold1=0
+      MouseThreshold2=0
+      MouseSpeed=0
+ +in the '[windows]' section of win.ini

The fool proof solution is to take total control of the mouse in X. +This is controlled by the $_X_mgrab_key in /etc/dosemu.conf +$_X_mgrab_key contains an X keysym of a key that when pressed +with both Ctrl & Alt held down will turn on the mouse grab, which +restricts the X mouse to the dosemu window, and gives dosemu complete +control over it. Ctrl-Alt-$_X_mgrab_key will then release the +mouse grab returning things to normal.

I like: $_X_mgrab_key="Scroll_Lock" (Ctrl-Alt-Scroll_Lock) +but $_X_mgrab_key="a" is a good conservative choice. (Ctrl-Alt-A) +You can use xev to see what keysyms a key generates.

Currently the way the X mouse code and the mouse grab are +structured the internal mouse driver cannot display the mouse +when the mouse grab is active. In particular without the grab +active to display the mouse cursor we just let X draw the mouse for +us, (as normal). When the mouse grab is active we restrict the mouse +to our current window, and continually reset it to the center of the +current screeen (allowing us to get relative amounts of movement). +A side effect of this is that the the position of the X cursor and the +dos cursor _not_ the same. So we need a different strategy to display +the dos cursor.

The other way an application can get into trouble in X, and also +on the console for that matter is if it is partially broken. In +particular the mouse driver is allowed to return coordinates that +have little to no connection with the actual screen resolution. So an +application mouse ask the mouse driver it's maximums and then scale +the coordinates it gets into screen positions. The broken +applications don't ask what the maximum & minimum resolutions are +and just assume that they know what is going on.

To keep this problem from being too severe in mouse.c we have +attempted to match the default resolutions used by other mouse +drivers. However since this is up to the choice of an individual +mouse driver there is doubtless code out there developed with +different resolutions in mind.

If you get stuck with such a broken application we have developed a +work around, that is partially effective. The idea being that if the +application draws it's own mouse pointer it really doesn't matter +where the dos mouse driver thinks the mouse is things should work. +So with emumouse it is possible to set a minimum resolution to return +to an application. By setting this minimum resolution to as big or +bigger than the application expect to see it should work. The side +effect of setting a minimum resolution bigger than the application +expects to see in X is that there will be some edges to the of +the screen where the application draws the cursor at the edge of the +window, and yet you need to continue scrolling a ways before the cursor +comes out there. In general this will affect the right and bottom +edges of the screen.

To read the current minimum use: +
    emumouse i
+The default is 640x200

To set the minimum resolution use: +
    emumouse Mx 640 My 200
+If you application doesn't draw it's own mouse cursor a skew of this +kind can be nasty. And there is no real good fix. You can set the +mininum down as well as up and so it may be possible to match what +the app expects as an internal resolution. However there is only +so much we can do to get a borken application to run and that +appears to be the limit.


11. Running a DOS application directly from Unix shell

This part of the document was written by Hans +<lermen@fgan.de>.

This chapter deals with starting DOS commands directly from Linux. +You can use this information to set up icons or menu items in X. +Using the keystroke, input and output redirection facilities you can +use DOS commands in shell scripts, cron jobs, web services, and so on.


11.1. Using unix -e in autoexec.bat

The default autoexec.bat file has a statement unix -e at the end. This +command executes the DOS program or command that was specified on the +dosemu command line.

For example: +
    dosemu "/home/clarence/games/commander keen/keen1.exe"
+will automatically: +

  • Lredir "/" if the specified program is not available from an +already-redirected drive,

  • "cd" to the correct directory,

  • execute the program automagically,

  • and quit DOSEMU when finished, all without any typing in DOS.

+Using "-E" at the command line causes DOSEMU to continue after the +DOS program finishes.

You can also specify a DOS command, such as "dir". A combination with +the -dumb command-line option is useful if you want to retrieve the +output of the DOS command, such as +
    dosemu -dumb dir > listing
+In this case (using -dumb, but not -E) all the startup messages that +DOSEMU and DOS generate are suppressed and you only get the output of "dir". +The output file contains DOS end-of-line markers (CRLF).


11.2. Using the keystroke facility.

Make use of the -input command-line option, such as + +
    dosemu -input 'dir > C:\\garbage\rexitemu\r'
+ +The '...' will be 'typed in' by DOSEMU exactly as if you had them +typed at the keyboard. The advantage of this technique is, that all +DOS applications will accept them, even interactive ones. A '\' is +interpreted as in C and leads in ESC-codes. Here is a list of the +current implemented ones:

    \r     Carriage return == <ENTER>
+    \n     LF
+    \t     tab
+    \b     backspace
+    \f     formfeed
+    \a     bell
+    \v     vertical tab
+    
+    
+    \^x    <Ctrl>x, where X is one of the usual C,M,L,[ ...
+           (e.g.: \^[ == <Ctrl>[ == ESC )
+    
+    \Ax    <Alt>x, hence  \Ad means <Alt>d
+    
+    \Fn;   Function key Fn. Note that the trailing ';' is needed.
+           (e.g.:  \F10;  == F10 )
+    
+    \Pn;   Set the virtual typematic rate, thats the speed for
+           autotyping in. It is given in unix timer ticks to wait
+           between two strokes. A value of 7 for example leads to
+           a rate of 100/7=14 cps.
+    
+    \pn;   Before typing the next stroke wait n unix ticks.
+           This is useful, when the DOS-application flushes the
+           keyboard buffer on startup. Your strokes would be discarded,
+           if you don't wait.
+    


11.3. Using an input file

  • Make a file "FILE" containing all keystrokes you need to boot DOSEMU +and to start your dos-application, ... and don't forget to have CR +(literal ^M) for 'ENTER'. FILE may look like this (as on my machine):

        2^Mdir > C:\garbage^Mexitemu^M              
    +which could choose point 2 of the boot menu, execute 'dir' with output +to 'garbage', and terminate DOSEMU, where the ^M stands for CR.

  • and execute DOSEMU like this: + +
        dosemu -dumb < FILE

In bash you can also use +
    echo -e 'dir \gt; c:\\garbage\rexitemu\r' | dosemu -dumb
+or, when your dos-app does only normal printout (text), then you may +even do this +
    echo -e 'dir\rexitemu\r' | dosemu -dumb > FILE.out

FILE.out then contains the output from the DOS application, but (unlike the +unix -e technique, merged with all startup messages.

You may elaborate this technique by writing a script, which gets the +dos-command to execute from the commandline and generate 'FILE' for you.


11.4. Running DOSEMU within a cron job

When you try to use one of the above to start DOSEMU out of a crontab, +then you have to asure, that the process has a proper environment set up +( especially the TERM and/or TERMCAP variable ).

Normally cron would setup TERM=dumb, this is fine because DOSEMU recognizes +it and internally sets it's own TERMCAP entry and TERM to `dosemu-none'. +You may also configure your video to + +
       dosemu ... -dumb
+ +or have a TERM=none to force the same setting. +In all other crontab run cases you may get nasty error messages either +from DOSEMU or from Slang.


12. Commands & Utilities

These are some utitlies to assist you in using Dosemu.


12.1. Programs

uchdir.com

change the Unix directory for Dosemu (use chdir(2))

dosdbg.com

change the debug setting from within Dosemu + +

  • dosdbg -- show current state

  • dosdbg <string>

  • dosdbg help -- show usage information

eject.com

eject CD-ROM from drive

emumouse.com

fine tune internal mousedriver of Dosemu + +

  • emumouse h -- display help screen

exitemu.com

terminate Dosemu

ugetcwd.com

get the Unix directory for Dosemu (use getcwd(2))

isemu.com

detects Dosemu version and returns greater 0 if running under +Dosemu

lancheck.exe

???

lredir.com

redirect Linux directory to Dosemu + +

  • lredir -- show current redirections

  • lredir D: LINUX\FS\tmp -- redirects /tmp to drive D:

  • lredir help -- display help screen

speed.com

set cpu usage (HogThreshold) from inside Dosemu

system.com

interface to system(2)...

unix.com

execute Unix commands from Dosemu + +

  • unix -- display help screen

  • unix ls -al -- list current Linux directory

  • caution! try "unix" and read the help screen

cmdline.exe

Read /proc/self/cmdline and put strings such as "var=xxx" +found on the commandline into the DOS enviroment. +Example having this as the dosemu commandine: + +
                     dosemu.bin "autoexec=echo This is a test..."
+ +then doing + +
     
+                     C:\cmdline < D:\proc\self\cmdline
+                     %autoexec%
+ +would display "This is a test..." on the DOS-Terminal

vgaoff.com

disable vga option

vgaon.com

enable vga option

xmode.exe

set special X parameter when running as xdos


12.2. Drivers

These are useful drivers for Dosemu

cdrom.sys

allow direct access to CD-ROM drives from Dosemu

ems.sys

enable EMM in Dosemu

emufs.sys

redirect Unix directory to Dosemu

aspi.sys

ASPI conform SCSI driver

fossil.com

FOSSIL serial driver (TSR)


13. Keymaps

This keymap is for using dosemu over telnet, and +having *all* your keys work. This keymap is not complete. But +hopefully with everyones help it will be someday :)

There are a couple of things that are intentionally +broken with this keymap, most noteably F11 and F12. This is because +they are not working though slang correctly at the current instant. +I have them mapped to "greyplus" and "greyminus". Also the scroll +lock is mapped to shift-f3. This is because the scroll lock dosn't +work right at all. Please feel free to send keymap patches in that +fix anything but these.

If you want to patch dosemu to fix either of those problems, i'd be +glad to accept those :)

to figure out how to edit this, read the keystroke-howto.

as of 3/30/95, control/shift/alternate +home/insert/delete/end/pageup/pagedown should work.

Major issues will be:

Do we move "alt-<fkey>" to "control-<fkey>" to switch virtual +consoles?

who is going to fix the linux keyboard device to be able to handle +multiple keymaps at the same time?

--------------------------------------------------------

to use it:

as root type + +
    	loadkeys dosemu.new.keymap

(then run dosemu via telnet, or something in slang mode)

when your done, find your old keymap, and load it back, cause +control-home won't work in emacs anymore (or any other special key in +any applicaion that uses xlate)

if you find a key missing, please add it and send me the patch. (test +it first! :)

if you find a key missing, and don't feel like coding it, don't tell +me! I already know that there are a lot of keys missing.

corey sweeney +<corey@interaccess.com >

Sytron Computers


14. Networking using DOSEMU

14.1. Direct NIC access

The easiest (and not recommended) way to set up the networking +in DOSEMU is to use the direct NIC access. It means that DOSEMU +will exclusively use one of your network interfaces, say eth1. +No other processes will be able to use that interface. If they +try to, the data exchange will became unreliable. So you have +to make sure that this network interface is not used by anything +including the kernel itself, before starting DOSEMU. +The settings for this method are as follows: +
    $_pktdriver = (on)
+    $_ethdev = "eth1"
+    $_vnet = "eth"
+Note that this method requires root privileges.

As you can see, this simple method has many shortcomings. If +you don't have the network card dedicated specially for dosemu, +consider using more advanced method called "Virtual Networking".


14.2. Virtual networking

Virtual networking is a mechanism that allows to overcome all the +limitations of the direct NIC access method, but it requires more +work to set up everything properly. +A special virtual network devices can be created using TUN/TAP interface. +This will enable multiple dosemu sessions and the linux kernel to be on +a separate virtual network. Each dosemu will have its own network device +and ethernet address.

First make sure that your Linux kernel comes with support for TUN/TAP; +for details check Documentation/networking/tuntap.txt +in the Linux kernel source. The user who runs DOSEMU should have +read/write access to /dev/net/tun. Then either: + +

  1. Set +
        $_pktdriver=(on)
    +    $_vnet = "tap"
    +    $_tapdev = ""

    Start DOSEMU as usual and configure the network device while DOSEMU is +running (using ifconfig manually as root, a script, or usernetctl if +your distribution supplies that), e.g. +
        ifconfig dosemu_tap0 up 192.168.74.1

    Configure the DOS TCP/IP network clients to have another IP address in the +subnet you just configured. This address should be unique, i.e. no other +dosemu, or the kernel, should have this address. For the example addresses +given above, 192.168.74.2-192.168.74.254 would be good. +Your network should now be up and running and you can, for example, +use a DOS SSH client to ssh to your own machine, but it will +be down as soon as you exit DOSEMU.

  2. Or set +
        $_pktdriver=(on)
    +    $_vnet = "tap"
    +    $_tapdev = "tap0"

    Obtain tunctl from the user mode linux project. Then set up a persistent +TAP device using tunctl (use the -u owner option if you do that as root). +Configure the network using ifconfig as above, but now before starting +DOSEMU. Now start DOSEMU as often as you like and you can use the network +in the same way as you did above.

    Note, however, that multiple DOSEMU sessions that run at the same time +need to use multiple tapxx devices. $_tapdev can be changed without +editing dosemu.conf or ~/.dosemu/.dosemurc (if you leave it commented out there) +by using dosemu -I "netdev tap1".

With the above you did set up a purely virtual internal network between +the DOSEMU and the real Linux box. This is why in the +above example 192.168.74.1 should *not* be a real IP address of the +Linux box, and the 192.168.74 network should not exist as a real +network. To enable DOS programs to talk to the outside world you have +to set up Ethernet bridging or IP routing.


14.2.1. Bridging

Bridging, using brctl (look for the bridge-utils package if you don't +have it), is somewhat easier to accomplish than the IP routing. +You set up a bridge, for example named "br0" and connect eth0 and +tap0 to it. Suppose the Linux box has IP 192.168.1.10 on eth0, where +192.168.1.x can be a real LAN, and the uid of the user who is +going to use DOSEMU is 500, then you can do (as root): +
    brctl addbr br0
+    ifconfig eth0 0.0.0.0 promisc up
+    brctl addif br0 eth0
+    ifconfig br0 192.168.1.10 netmask 255.255.255.0 up
+    tunctl -u 500
+    ifconfig tap0 0.0.0.0 promisc up
+    brctl addif br0 tap0
+Now the DOSEMU's IP can be set to (for example) 192.168.1.11. +It will appear in the same network your linux machine is.


14.2.2. IP Routing

If you like to use IP routing, note that unlike with bridging, +each DOSEMU box will reside in a separate IP subnet, which consists +only of 2 nodes: DOSEMU itself and the corresponding TAP device on +Linux side. +You have to choose an IP address for that subnet. If your LAN has the +address 192.168.1.0 and the netmask is 255.255.255.0, the dosemu subnet can +have the address 192.168.74.0 and tap0 can have the address 192.168.74.1: +
    ifconfig tap0 192.168.74.1 netmask 255.255.255.0 up
+Choose a valid IP address from that subnet for DOSEMU box. It can be +192.168.74.2. Configure your DOS client to use that IP. Configure your +DOS client to use a gateway, which is the TAP device with IP 192.168.74.1. +Then you have to add the proper entry to the routing table on your Linux +box: +
    route add -net 192.168.74.0 netmask 255.255.255.0 dev tap0
+The resulting entry in the routing table will look like this: +
    Destination   Gateway  Genmask         Flags Metric Ref    Use Iface
+    192.168.74.0  *        255.255.255.0   U     0      0        0 tap0
+ +Then, unless the Linux box on which DOSEMU is running is a default +gateway for the rest of you LAN, you will have to also add an entry +to the routing table on each node of your LAN: +
    route add -net 192.168.74.0 netmask 255.255.255.0 gw 192.168.1.10
+(192.168.1.10 is the IP of the box DOSEMU is running on). +Also you have to check whether IP forwarding is enabled, and if not - +enable it: +
    echo 1 > /proc/sys/net/ipv4/ip_forward
+Now DOSEMU will be able to access any node of your LAN and vice versa. +Sometimes the forwarding is blocked by the firewall rules. In that +case try the following command (as root): +
    iptables -t filter -I FORWARD 1 -i tap0 -j ACCEPT

Yet one more thing have to be done if you want dosemu to be able to +access Internet. Unlike in your LAN, you are not supposed to change +the routing tables on an every Internet host, so how to make them +to direct the IP packets back to dosemu's virtual network? To +accomplish this, you only have to enable the IP Masquerading on +the network interface that looks into Internet. If your machine +serves as a gateway in a LAN, then the masquerading is most likely +already enabled, and no further work is required. Otherwise you +must run the following command +(assuming the eth0 interface serves the Internet connection): +
    iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
+Now you'll find your dosemu session being able to access Internet. +If you want these changes to be permanent, you can use iptables-save +script to save the changes to your iptables configuration file.


14.3. VDE networking backend

If you just need to have a working internet access from within your +DOSEMU environment, then you might want to consider using VDE networking. +slirpvde is a user mode networking utility providing a NAT-like +service, DHCP server, as well as a virtualized network in the 10.0.2.0/24 +range, with a default gateway and a DNS server. +Note that dosemu uses VDE backend as a fallback when other methods are +unavailable. It also tries to start all the vde utilities, so no +configuration efforts are usually needed.

Use the following command to get VDE sources: +
    svn checkout svn://svn.code.sf.net/p/vde/svn/trunk vde-svn
+You may also need to apply the following patches: +http:sourceforge.net/p/vde/bugs/70/attachment/flags.diff> +patch1 + +http:sourceforge.net/p/vde/bugs/71/attachment/atty.diff> +patch2 + +http:sourceforge.net/p/vde/bugs/_discuss/thread/dc88b292/4b26/attachment/msg.diff> +patch3 + +http:sourceforge.net/p/vde/bugs/73/attachment/0001-slirp-Forward-ICMP-echo-requests-via-unprivileged-so.patch> +patch4

VDE-specific config options: +
    $_pktdriver=(on)
+    $_vnet = "vde"
+    $_vdeswitch = "/tmp/switch1
+    $_slirpargs = "--dhcp"

Your wattcp.cfg file will look as simple as this: +
    my_ip=dhcp


15. Using Windows and Winsock

This is the Windows Net Howto by Frisoni Gloriano +<gfrisoni@hi-net.it> +on 15 may 1997

This document tries to describe how to run the Windows trumpet winsock over +the dosemu built-in packet driver, and then run all TCP/IP winsock-based +application (netscape, eudora, mirc, free agent .....) in windows environment.

This is a very long step-by-step list of operation, but you can make +little scripts to do all very quickly ;-)

In this example, I use the dosnet based packet driver. It is very powerful +because you can run a "Virtual net" between your dos-windows session and the +linux, and run tcp/application application without a real (hardware) net.


15.1. LIST OF REQUIRED SOFTWARE

  • The WINPKT.COM virtual packet driver, version 11.2 +I have found this little tsr in the Crynwr packet driver distribution +file PKTD11.ZIP

  • The Trumpet Winsock 2.0 revision B winsock driver for windows.


15.2. STEP BY STEP OPERATION (LINUX SIDE)

  • Enable "dosnet" based dosemu packet driver:

              cd ./src/dosext/net/net
    +          select_packet	 (Ask  single or multi ->  m)
    +

  • Make the dosnet linux module:

              cd ./src/dosext/net/v-net
    +          make

  • Make the new dosemu, with right packet driver support built-in:

              make 
    +          make install 

  • Now you must load the dosnet module:

              insmod ./src/dosext/net/v-net/dosnet.o

  • Some linux-side network setup (activate device, routing). This stuff depends +from your environment, so I write an example setup.

    Here you configure a network interface dsn0 (the dosnet interface) with +the ip address 144.16.112.1 and add a route to this interface.

    This is a good example to make a "virtul network" from your dos/windows +environment and the linux environment.

              ifconfig dsn0 144.16.112.1 broadcast 144.16.112.255 netmask 255.255.255.0
    +          route add -net 144.16.112.0 dsn0


15.3. STEP BY STEP OPERATION (DOS SIDE)

I suppose you know how to run windows in dosemu. You can read the Section 9 +document if you need more information. Windows is not very stable, but works.

  • start dosemu.

  • copy the winpkt.com driver and the trumpet winsock driver in some +dos directory.

  • start the winpkt TSR. (dosemu assign the 0x60 interrupt vector to the +built-in packet driver)

        	winpkt 0x60

  • edit the trumpet winsock setup file trumpwsk.ini. Here is an example of +how to setup this file: +(I think you can use less parameters, if you have the time to play with +this file. You can also setup this stuff from the winsock setup dialog-box).

        	[Trumpet Winsock]
    +    	netmask=255.255.255.0  <-- class C netmask.
    +    	gateway=144.16.112.1   <-- address in the default gateway.
    +    	dns=www.xxx.yyy.zzz    <-- You must use right value for the dns.
    +    	domain=hi-net.it
    +    	ip=144.16.112.10       <-- Windows address in the dosnet.
    +    	vector=60              <-- packet driver interrupt vector.
    +    	mtu=1500
    +    	rwin=4096
    +    	mss=1460
    +    	rtomax=60
    +    	ip-buffers=32
    +    	slip-enabled=0         <--- disable slip
    +    	slip-port=2
    +    	slip-baudrate=57600
    +    	slip-handshake=1
    +    	slip-compressed=0
    +    	dial-option=1
    +    	online-check=0
    +    	inactivity-timeout=5
    +    	slip-timeout=0
    +    	slip-redial=0
    +    	dial-parity=0
    +    	font=Courier,9
    +    	registration-name=""
    +    	registration-password=""
    +    	use-socks=0
    +    	socks-host=0.0.0.0
    +    	socks-port=1080
    +    	socks-id=
    +    	socks-local1=0.0.0.0 0.0.0.0
    +    	socks-local2=0.0.0.0 0.0.0.0
    +    	socks-local3=0.0.0.0 0.0.0.0
    +    	socks-local4=0.0.0.0 0.0.0.0
    +    	ppp-enabled=0            <-------- disable ppp
    +    	ppp-usepap=0
    +    	ppp-username=""
    +    	ppp-password=""
    +    	win-posn=42 220 867 686 -1 -1 -4 -4 1
    +    	trace-options=16392
    +    	
    +    	[default vars]

  • Now you can run windows, startup trumpet winsock and ..... +enjoy with your windoze tcp/ip :-)

Gloriano Frisoni. +<gfrisoni@hi-net.it>

\ No newline at end of file diff --git a/doc/tweaks.html b/doc/tweaks.html new file mode 100644 index 0000000..754efe7 --- /dev/null +++ b/doc/tweaks.html @@ -0,0 +1,1023 @@ + +Known tweaks needed to run programs under dosemu

Known tweaks needed to run programs under dosemu

This file lists programs that needs specific tweaks in order to +run them under dosemu2.


Table of Contents
1. Millenium
1.1. Millenium game fails to detect Sound Blaster, unless the SB IRQ is 5
1.2. Millenium game crashes when entering space combat
2. Need For Speed Special Edition
2.1. Wrong colors in video clips.
2.2. Hangs or glitches in video clips.
2.3. Installer crashes or unstable.
3. Gobliiins
3.1. Goblins halts at startup with "Divide Error" message
4. Prehistorik 2
4.1. Game freezes or crashes shortly after start.
4.2. Sound is crackling.
5. Carmageddon
5.1. Timer is too fast during race
6. Simon 2
6.1. Game freezes at start.
7. Prince of Persia 2
7.1. Game slows down unbearably after some playing.
8. Cosmo game by Apogee Software
8.1. Cosmo's Cosmic Adventure doesn't start
9. Pinball Fantasies
9.1. Long start-up delay (like half a minute)
9.2. Black screen and hang after choosing the table.
9.3. Game doesn't see the Fx keys that should start the game.
10. WordPerfect 6.2
10.1. Hang trying to play the MIDI file embedded in document.
11. LEXICON 1.2 (mod 8.98)
11.1. dosemu crashes after a few minutes of work.
12. Test Drive 2
12.1. Game doesn't start, just returns to command prompt.
13. Tetris Classic, Super Tetris
13.1. Game is too slow.
14. Street Fighter 2
14.1. Game crashes on intro screen.
15. Game Wizard (GW)
15.1. Hangs on start.

1. Millenium

1.1. Millenium game fails to detect Sound Blaster, unless the SB IRQ is 5

Solution: set $_sb_irq=(5). Fortunately this is a default setting, so +in most cases you won't care.


1.2. Millenium game crashes when entering space combat

Solution: to the file 2200gx.exe apply the following patch: +
    000030B2: AA 47


2. Need For Speed Special Edition

2.1. Wrong colors in video clips.

Solution: to file nfs.exe apply the following patch: +
    000607E6: 00 08
+see here for details


2.2. Hangs or glitches in video clips.

Solution: to file nfs.exe apply the following patch: +
    000B8E5A: 78 EB
+    000B8E72: 78 EB


2.3. Installer crashes or unstable.

Solution: to file infsd.exe apply the following patch: +
    0002873E: 7C EB
+see here for details


3. Gobliiins

3.1. Goblins halts at startup with "Divide Error" message

Solution: apply the following patch to gobega.exe: +
    00004A19: F7 90
+    00004A1A: F3 90


4. Prehistorik 2

4.1. Game freezes or crashes shortly after start.

Solution: set +
    $_umb_f0 = (off)
+in your dosemu.conf or .dosemurc.


4.2. Sound is crackling.

Solution: set +
    $_dos_up = (off)
+in your dosemu.conf or .dosemurc. +The only "explanation" we can provide, is a conspiracy theory that +prehistoric is trying to slander third-party DOSes that put LoL/SDA +to UMB.


5. Carmageddon

5.1. Timer is too fast during race

see here for details

Solution: for hi-res mode, apply the following patch to carma.exe: +
    00083549: 89 83
+    0008354A: C5 E8
+    0008354B: 29 04
+    0008354C: F5 90
+    0008354D: 89 90
+    0008354E: 2D A3

For low-res mode, apply the following patch to carma.exe: +
    00083549: 89 83
+    0008354A: C5 E8
+    0008354B: 29 02
+    0008354C: F5 90
+    0008354D: 89 90
+    0008354E: 2D A3

Unfortunately the timer is still a bit unstable, and runs as slower +as faster you drive your car. +To completely disable the timer, apply the following patch to carma.exe: +
    00083549: 89 83
+    0008354A: C5 E8
+    0008354B: 29 00
+    0008354C: F5 90
+    0008354D: 89 90
+    0008354E: 2D A3


6. Simon 2

6.1. Game freezes at start.

Solution: apply the following patch to runflat.exe: +
    000130FF: 75 90
+    00013100: F7 90
+    00013110: 75 90
+    00013111: F6 90
+    00013112: D1 90
+    00013113: E9 90


7. Prince of Persia 2

7.1. Game slows down unbearably after some playing.

Solution: update to v1.1 of the game or apply the following +patch to prince.exe: +
    00016549: CF CB


8. Cosmo game by Apogee Software

8.1. Cosmo's Cosmic Adventure doesn't start

Solution: Unpack the cosmo1.exe which is packed with LZEXE. +Then apply the following patch: +
    00011F8F: 75 90
+    00011F90: F9 90


9. Pinball Fantasies


9.2. Black screen and hang after choosing the table.

Solution: A few driver files needs to be patched. +Or just patch the one that you selected in a sound setup.

Apply the following patch to sb16.sdr: +
    00002007: F7 90
+    00002008: F3 90

Apply the following patch to sbpro.sdr: +
    000020A4: F7 90
+    000020A5: F3 90

Apply the following patch to sb20.sdr: +
    0000206B: F7 90
+    0000206C: F3 90

Apply the following patch to sblaster.sdr: +
    00001F25: F7 90
+    00001F26: F3 90


10. WordPerfect 6.2

10.1. Hang trying to play the MIDI file embedded in document.

Apply the following patch to vmp.com: +
    00002C4B: 72 90
+    00002C4C: 0D 90
+    00002C5B: 01 00
+see here for details


11. LEXICON 1.2 (mod 8.98)

11.1. dosemu crashes after a few minutes of work.

Looking at lexicon code, the crash seems intentional. +Probably some kind of a copy protection. +Apply the following patch to lexicon.exe: +
    0000ED9C: 74 EB
+    00011DAE: 74 EB


12. Test Drive 2

12.1. Game doesn't start, just returns to command prompt.

Test Drive 2 only works from drives up to F:. In most dosemu2 setups, +these drive letters are occupied. Solution is to copy the game to C:.


13. Tetris Classic, Super Tetris

13.1. Game is too slow.

Solution: set +
    $_timer_tweaks = (on)


14. Street Fighter 2

14.1. Game crashes on intro screen.

Solution: set +
    $_timer_tweaks = (on)


15. Game Wizard (GW)

15.1. Hangs on start.

Solution: set +
    $_dos_up = (off)

\ No newline at end of file diff --git a/dosemu2.spec b/dosemu2.spec new file mode 100644 index 0000000..a898847 --- /dev/null +++ b/dosemu2.spec @@ -0,0 +1,119 @@ +# +# spec file template for dosemu2 +# +# Written by Mateusz Viste, stsp +# + +Name: dosemu2 +Version: 2.0pre9 +Release: 2 +Summary: fast and secure DOS emulator + +Group: System/Emulator + +License: GPLv2+ +URL: https://github.com/dosemu2/dosemu2 +VCS: https://github.com/dosemu2/dosemu2.git +Source0: dosemu2.tar.gz + +BuildRequires: SDL2-devel +BuildRequires: SDL2_ttf-devel +BuildRequires: fontconfig-devel +BuildRequires: libXext-devel +BuildRequires: alsa-lib-devel +BuildRequires: fluidsynth-devel +BuildRequires: gpm-devel +BuildRequires: libao-devel +BuildRequires: ladspa-devel +BuildRequires: slang-devel +BuildRequires: libslirp-devel +BuildRequires: libieee1284-devel +BuildRequires: mt32emu-devel +BuildRequires: libbsd-devel +BuildRequires: gcc +BuildRequires: bison +BuildRequires: flex +BuildRequires: gawk +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: make +BuildRequires: sed +BuildRequires: bash +BuildRequires: findutils +BuildRequires: git >= 2.0 +BuildRequires: bdftopcf +BuildRequires: mkfontscale +BuildRequires: linuxdoc-tools +BuildRequires: readline-devel +BuildRequires: json-c-devel +BuildRequires: libb64-devel +BuildRequires: binutils +BuildRequires: binutils-x86_64-linux-gnu +BuildRequires: pkgconf-pkg-config +BuildRequires: fdpp-devel +BuildRequires: dj64-devel + +# our startup script is bash-specific +Requires: bash +Requires: comcom64 +Recommends: fluid-soundfont-gm +Suggests: timidity++ >= 2.14.0 +Recommends: ladspa +# ncurses-base is for terminfo +Recommends: ncurses-base +Recommends: gdb +Recommends: kbd +Suggests: valgrind +Recommends: install-freedos +Suggests: install-otherdos + +# cannot coexist with dosemu1 +Conflicts: dosemu + +%description +dosemu2 is an emulator for running DOS programs under linux. +It can also serve as a VM to boot various DOSes. + +%prep +%setup -T -b 0 -q -n dosemu2 + +%build +./autogen.sh +%configure +make %{?_smp_mflags} + +%check + +%install +mkdir -p %{buildroot}%{_sysconfdir}/X11/fontpath.d +make DESTDIR=%{buildroot} install + +%files +%defattr(-,root,root) +%{_bindir}/* +%{_mandir}/man1/* +%lang(ru) %dir %{_mandir}/ru +%lang(ru) %dir %{_mandir}/ru/man1 +%lang(ru) %{_mandir}/ru/man1/* +# Not using libdir here as we only install plugins, and their path hard-coded +%{_prefix}/lib/dosemu +%{_datadir}/dosemu +%{_datadir}/applications/dosemu.desktop +%{_datadir}/fonts/oldschool +%{_sysconfdir}/X11/fontpath.d/dosemu2* +%doc %{_docdir}/dosemu2 +%dir %{_sysconfdir}/dosemu +%config(noreplace) %{_sysconfdir}/dosemu/dosemu.conf + +%changelog +* Tue Apr 09 2024 Stas Sergeev 2.0pre9-2 +- + +* Tue Apr 09 2024 Stas Sergeev +- + +* Tue Apr 09 2024 Stas Sergeev 2.0pre9-1 +- new package built with tito + +* Sat Aug 20 2016 Stas Sergeev 2.0pre6-dev +(none) diff --git a/dosemu2.spec.rpkg b/dosemu2.spec.rpkg new file mode 100644 index 0000000..7030980 --- /dev/null +++ b/dosemu2.spec.rpkg @@ -0,0 +1,109 @@ +# +# spec file template for dosemu2 +# +# Written by Mateusz Viste, stsp +# + +Name: {{{ git_dir_name }}} +Version: {{{ git_dir_version }}} +Release: 1%{?dist} +Summary: fast and secure DOS emulator + +Group: System/Emulator + +License: GPLv2+ +URL: https://github.com/dosemu2/dosemu2 +VCS: {{{ git_dir_vcs }}} +Source0: {{{ git_dir_archive }}} + +BuildRequires: SDL2-devel +BuildRequires: SDL2_ttf-devel +BuildRequires: fontconfig-devel +BuildRequires: libXext-devel +BuildRequires: alsa-lib-devel +BuildRequires: fluidsynth-devel +BuildRequires: gpm-devel +BuildRequires: libao-devel +BuildRequires: ladspa-devel +BuildRequires: slang-devel +BuildRequires: libslirp-devel +BuildRequires: libieee1284-devel +BuildRequires: mt32emu-devel +BuildRequires: libbsd-devel +BuildRequires: gcc +BuildRequires: bison +BuildRequires: flex +BuildRequires: gawk +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: make +BuildRequires: sed +BuildRequires: bash +BuildRequires: findutils +BuildRequires: git >= 2.0 +BuildRequires: bdftopcf +BuildRequires: mkfontscale +BuildRequires: linuxdoc-tools +BuildRequires: readline-devel +BuildRequires: json-c-devel +BuildRequires: libb64-devel +BuildRequires: binutils +BuildRequires: binutils-x86_64-linux-gnu +BuildRequires: pkgconf-pkg-config +BuildRequires: fdpp-devel +BuildRequires: dj64-devel + +# our startup script is bash-specific +Requires: bash +Requires: comcom64 +Recommends: fluid-soundfont-gm +Suggests: timidity++ >= 2.14.0 +Recommends: ladspa +# ncurses-base is for terminfo +Recommends: ncurses-base +Recommends: gdb +Recommends: kbd +Suggests: valgrind +Recommends: install-freedos +Suggests: install-otherdos + +# cannot coexist with dosemu1 +Conflicts: dosemu + +%description +dosemu2 is an emulator for running DOS programs under linux. +It can also serve as a VM to boot various DOSes. + +%prep +{{{ git_dir_setup_macro }}} + +%build +./autogen.sh +%configure +make %{?_smp_mflags} + +%check + +%install +mkdir -p %{buildroot}%{_sysconfdir}/X11/fontpath.d +make DESTDIR=%{buildroot} install + +%files +%defattr(-,root,root) +%{_bindir}/* +%{_mandir}/man1/* +%lang(ru) %dir %{_mandir}/ru +%lang(ru) %dir %{_mandir}/ru/man1 +%lang(ru) %{_mandir}/ru/man1/* +# Not using libdir here as we only install plugins, and their path hard-coded +%{_prefix}/lib/dosemu +%{_datadir}/dosemu +%{_datadir}/applications/dosemu.desktop +%{_datadir}/fonts/oldschool +%{_sysconfdir}/X11/fontpath.d/dosemu2* +%doc %{_docdir}/dosemu2 +%dir %{_sysconfdir}/dosemu +%config(noreplace) %{_sysconfdir}/dosemu/dosemu.conf + +%changelog +{{{ git_dir_changelog since_tag=dosemu2-2.0pre6-dev }}} diff --git a/etc/cpi/ega.cpi b/etc/cpi/ega.cpi new file mode 100644 index 0000000..447131c Binary files /dev/null and b/etc/cpi/ega.cpi differ diff --git a/etc/cpi/ega10.cpi b/etc/cpi/ega10.cpi new file mode 100644 index 0000000..11e9a2d Binary files /dev/null and b/etc/cpi/ega10.cpi differ diff --git a/etc/cpi/ega11.cpi b/etc/cpi/ega11.cpi new file mode 100644 index 0000000..3bc1aec Binary files /dev/null and b/etc/cpi/ega11.cpi differ diff --git a/etc/cpi/ega12.cpi b/etc/cpi/ega12.cpi new file mode 100644 index 0000000..b92d6f1 Binary files /dev/null and b/etc/cpi/ega12.cpi differ diff --git a/etc/cpi/ega13.cpi b/etc/cpi/ega13.cpi new file mode 100644 index 0000000..480620e Binary files /dev/null and b/etc/cpi/ega13.cpi differ diff --git a/etc/cpi/ega14.cpi b/etc/cpi/ega14.cpi new file mode 100644 index 0000000..1f30817 Binary files /dev/null and b/etc/cpi/ega14.cpi differ diff --git a/etc/cpi/ega15.cpi b/etc/cpi/ega15.cpi new file mode 100644 index 0000000..a39821e Binary files /dev/null and b/etc/cpi/ega15.cpi differ diff --git a/etc/cpi/ega16.cpi b/etc/cpi/ega16.cpi new file mode 100644 index 0000000..55d0f40 Binary files /dev/null and b/etc/cpi/ega16.cpi differ diff --git a/etc/cpi/ega17.cpi b/etc/cpi/ega17.cpi new file mode 100644 index 0000000..57b1e3d Binary files /dev/null and b/etc/cpi/ega17.cpi differ diff --git a/etc/cpi/ega18.cpi b/etc/cpi/ega18.cpi new file mode 100644 index 0000000..a0dbbf9 Binary files /dev/null and b/etc/cpi/ega18.cpi differ diff --git a/etc/cpi/ega2.cpi b/etc/cpi/ega2.cpi new file mode 100644 index 0000000..8dbaf08 Binary files /dev/null and b/etc/cpi/ega2.cpi differ diff --git a/etc/cpi/ega3.cpi b/etc/cpi/ega3.cpi new file mode 100644 index 0000000..6b21b07 Binary files /dev/null and b/etc/cpi/ega3.cpi differ diff --git a/etc/cpi/ega4.cpi b/etc/cpi/ega4.cpi new file mode 100644 index 0000000..a6ee5b7 Binary files /dev/null and b/etc/cpi/ega4.cpi differ diff --git a/etc/cpi/ega5.cpi b/etc/cpi/ega5.cpi new file mode 100644 index 0000000..3d8f58a Binary files /dev/null and b/etc/cpi/ega5.cpi differ diff --git a/etc/cpi/ega6.cpi b/etc/cpi/ega6.cpi new file mode 100644 index 0000000..45b1e1b Binary files /dev/null and b/etc/cpi/ega6.cpi differ diff --git a/etc/cpi/ega7.cpi b/etc/cpi/ega7.cpi new file mode 100644 index 0000000..c4d6fe7 Binary files /dev/null and b/etc/cpi/ega7.cpi differ diff --git a/etc/cpi/ega8.cpi b/etc/cpi/ega8.cpi new file mode 100644 index 0000000..42c40e8 Binary files /dev/null and b/etc/cpi/ega8.cpi differ diff --git a/etc/cpi/ega9.cpi b/etc/cpi/ega9.cpi new file mode 100644 index 0000000..9c78a32 Binary files /dev/null and b/etc/cpi/ega9.cpi differ diff --git a/etc/dosemu.conf b/etc/dosemu.conf new file mode 100644 index 0000000..3c90f41 --- /dev/null +++ b/etc/dosemu.conf @@ -0,0 +1,1068 @@ +############################################################################## +# This file is the system-wide dosemu.conf or the per-user ~/.dosemurc, +# included by global.conf or dosemu.bin. +# +# ./doc/README.txt (chapter 2.) contains a description of the syntax +# and the usage of dosemu.conf and .dosemurc. +# +# The commented-out values are defaults, here for documentation purposes +# only. Options marked [priv] cannot be changed in ~/.dosemurc. +# +# (optional) access rights are defined in +# +# /etc/dosemu/dosemu.users or /etc/dosemu.users +# +############################################################################## + +# Notes for editing this file: +# +# In $_xxx = (n) n is a numerical or boolean value +# = = +# In $_zzz = "s" s is a string +# +# Please note that all options are commented out by default! +# Remove the # in front of the $ to change an option. + +# Path to the local directory. +# Note: this option is only valid in global config file. +# Default: "~/.dosemu" + +# $_local_dir = "~/.dosemu" + +# Name of the local config file under local directory. +# Note: this option is only valid in global config file. +# Default: "dosemurc" + +# $_dosemurc = "dosemurc" + +############################################################################## +## CPU settings: define the CPU features to DOSEMU. + +# CPU shown to DOS, valid values: "80[23456]86" +# Default: 80586 + +# $_cpu = "80586" + +# Select cpu virtualization mode. +# "vm86" - use v86 mode via vm86() syscall. Only available on x86-32. +# "kvm" - use KVM, hardware-assisted in-kernel virtual machine. +# "emulated" - use CPU emulator +# "auto" - select whatever works +# Default: "auto" + +# $_cpu_vm = "auto" + +# Select cpu virtualization mode for DPMI. +# "native" - use native LDT via modify_ldt() syscall (Warning: INSECURE!) +# "kvm" - use KVM, hardware-assisted in-kernel virtual machine +# "emulated" - use CPU emulator +# "auto" - select whatever works +# Default: "auto" + +# $_cpu_vm_dpmi = "auto" + +# CPU emulation mode (if enabled). +# 0 - jit; 1 - interpreter +# jit is faster, interpreter is probably more compatible. +# Default: 0 + +# $_cpuemu = (0) + +# CPU speed, used in conjunction with the TSC +# Default 0 = calibrated by dosemu, else given (e.g.166.666) + +# $_cpuspeed = (0) + +# emulated FPU, (off) or (on), default = (on) + +# $_mathco = (on) + +# 0 = all CPU power to DOSEMU; default = 1 = nicest, then higher:more CPU power + +# $_hogthreshold = (1) + +############################################################################## +## Disk and file system settings + +# List of hdimages or directories under ~/.dosemu, or specified +# with an absolute path. +# +# For older versions of MS-DOS and PC-DOS you may want to limit the size of +# any directory-mode virtual disks, or the OS may fail to recognise them: +# /path/to/dir:hdtype1 - creates 10mb IBM type1 disk with C/H/S of 306/4/17 +# /path/to/dir:hdtype2 - creates 21mb IBM type2 disk with C/H/S of 615/4/17 +# /path/to/dir:hdtype9 - creates 117mb IBM type9 disk with C/H/S of 900/15/17 +# +# You can reference the pre-defined path groups with '+' sign followed +# by the number of the group. Currently the following groups are defined: +# Group 0: user's main drives. This group consists of just one path that +# should normally be mapped to drive C. You can omit that group if you +# want to map your own directory to drive C. +# Group 1: utility/configs & boot drives. This group includes multiple +# paths that you can map to any drive letters. This group provides the +# boot files, dosemu-specific drivers and utilities, and the shell. +# Group 2: utility/configs drive only. This is for non-default setups +# where DOS was manually installed to drive C, and boot drives are +# not needed. +# Warning: You need to map group 1 or the boot may fail. +# +# Example: "+0 drives/* +1" means assign group 0 path to drive C, +# then map the content of ~/.dosemu/drives, then map group 1 to +# the consecutive drive letters. +# dosemu1-compatible setup may look like "drives/*". +# +# Some drive letters can be skipped during boot, for example if you +# want to redirect them later. Use '-' sign followed by the amount +# of drive letters to skip. +# Example: "+0 -2 +1" means assign group 0 path to drive C, skip 2 +# letters (D, E), then map group 1. +# Note that letter skipping only works correctly with fdpp. +# +# Folders can be mapped to drive letters using full paths. +# Example: "+0 /home/ drives/* -3 +1" means +# assign group 0 to drive C; assign your Home folder to drive D; +# assign folders in ~/.dosemu/drives to drive letters (for example, +# ~/.dosemu/drives/drive_e and ~/.dosemu/drives/drive_f will be +# E and F); skip 3 letters (G, H, I); map group 1 to J, K, and L. +# +# Default: "+0 +1" (map both groups of paths to the consecutive drives) + +# $_hdimage = "+0 +1" + +# Floppy drives. +# May be set up to directory or to /dev/fdX device node. +# Optionally the device type may be appended, such as +# "/dev/fd0:threeinch". +# A directory can be used as a read-only virtual floppy: "floppy_dir:threeinch", +# in which case $HOME/.dosemu/floppy_dir will be used as a floppy image. +# To boot from a floppy, use "boot" keyword, such as: +# $_floppy_a = "dos33.img:boot" +# Note that $_bootdrive overrides the floppy boot flag. +# +# Default: "/dev/fd0:threeinch" for A:, "" for B: + +# $_floppy_a = "/dev/fd0:threeinch" +# $_floppy_b = "" + +# select the boot drive +# Default: "" (which means auto, finds the first bootable drive) + +# $_bootdrive = "" + +# swap drives the way the bootdrive to become C +# Needed for DOSes that can't boot from anything other than C drive. +# Default: off + +# $_swap_bootdrive = (off) + +# list of host directories to present as DOS drives. +# These drives are "light-weight": they cannot be used for boot-up and +# do not take the precious start-up time to create ($_hdimage directory +# drives can take noticeable start-up time). +# You can use the "dir:flag" syntax for some extra functionality. +# The following flags are defined: +# "r" - drive will be read-only. +# "c" - drive will emulate a cdrom. +# "g" - define a group of dynamic drives for removable medias. +# +# Example: $_hostfs_drives = "~/dos /run/media/$USER:g" +# creates the DOS drive for $HOME/dos and a drive group for +# removable medias at /run/media/$USER. +# It is recommended to put drive groups at the end of the list. +# +# Default: "" (disabled for security reasons) + +# $_hostfs_drives = "" + +# List of CDROM devices. Up to 4 are supported. You may also specify +# image files. You need to load cdrom.sys and mscdex/nwcdex/shsucdx.exe. +# Default: "" (means auto, usually /dev/cdrom) + +# $_cdrom = "" + +# list of generic SCSI devices to make available for the builtin aspi driver +# (format of an entry is 'device:type:mappedtarget' such as +# "sg2:WORM sg3:Sequential-Access:6 sg4:CD-ROM" or +# "sg2:4 sg3:1:6 sg4:5" (which are equal). Default: "" + +# $_aspi = "" + +# Some dumb programs probe the file lock limit by applying locks +# in a loop and checking for an error. The unlimited amount of +# file locks causes such probe to loop infinitely. +# To avoid that, we set the maximum allowed locks number to 1024. +# Note that this is just a work-around: the limit is not strictly +# enforced and the DOS program can exceed it if it tries hard enough. +# 0 means unlimited. + +# $_file_lock_limit = (1024) + +# enable/disable long filename support for lredired drives; +# default: on + +# $_lfn_support = (on) + +# set interrupt hooks +# Interrupt hooks are needed to work with third-party DOSes +# and provide various services to them, like direct host FS access. +# With FDPP this option can be safely disabled. +# With other DOSes, enable this and make sure you have +# emufs.sys loaded via config.sys. +# default: auto + +# $_set_int_hooks = (auto) + +# force interrupt revectoring +# Use legacy interrupt revector technique to install interrupt hooks. +# You don't need this if you have emufs.sys loaded via config.sys, and +# you don't this if you use FDPP. +# default: auto + +# $_force_int_revect = (auto) + +# trace interrupt returns +# Improves the logging of some interrupts execution (int21 mainly). +# You don't need this unless you are debugging some DOS calls. + +# $_trace_irets = (off) + +# try to enable FS redirector even if it wasn't loaded in config.sys +# default: on + +# $_force_fs_redirect = (on) + +# config.sys -> config.XXX; default="" or 3 char., + +# $_emusys = "" + +############################################################################## +## Memory settings + +# conventional DOS memory size, in Kbytes, <= 768. +# Default: 640 + +# $_dosmem = (640) + +# Extended Memory size. This memory is accessible via int15h and can be +# used by himem.sys. +# Default: 8192 (8Mb) + +# $_ext_mem = (8192) + +# XMS (internal driver) is only needed if you do not load +# himem.sys or another external XMS driver. Size in Kbytes. +# Default: 16384 (16Mb) + +# $_xms = (16384) + +# Enable 64K UMB at 0xa0000. +# This is where the VGA graphics memory resides, so enabling this UMB +# will leave you with text mode only. +# Default: auto (enabled only if dosemu runs in text mode) + +# $_umb_a0 = (auto) + +# Enable 32K UMB at 0xb0000. +# This is where the MDA text memory used to reside, so some programs +# write to that area without hesitation. Therefore it is rather risky +# to enable that UMB: beware even of ansi.sys! +# Default: auto (means: enable only for dumb video mode) + +# $_umb_b0 = (auto) + +# Enable 32K UMB at 0xb8000. +# This is the text-mode video memory. +# It can be used as UMB only in dumb video mode. +# Use "auto" to get it enabled in that mode. +# And even in that case only few well-behaved programs will work. +# Default: off + +# $_umb_b8 = (off) + +# Enable 16K UMB at 0xf0000. +# This is where normally a read-only BIOS resides but the DOSEMU BIOS does +# not need it. Some (very few) programs rely on this memory being read-only +# (Prehistorik 2); to run such programs, disable this option. +# Default: on + +# $_umb_f0 = (on) + +# Enable HMA usage and a20 gating. +# HMA is the ~64K area above 1Mb of RAM. +# Default: on + +# $_hma = (on) + +# Load DOS kernel to upper memory (UMB or HMA). +# 0 or off means load low, 1 means UMB, 2 means UMB+HMA. +# This gives more free conventional memory but may lead to incompatibilities. +# You can freely disable that option and most of the DOS memory will remain +# in UMB nevertheless (given the proper config.sys). +# Note: only relevant for fdpp. +# Default: 1 (use only UMB, minor incompatibilities) + +# $_dos_up = (1) + +# EMS (expanded memory) size in Kbytes; +# Warning: disabling EMS (off) is not recommended. Doing so will +# give you the additional 64K of UMB space, but as a side-effect +# it will also disable the protected mode DOS API translator, +# making many protected-mode apps to crash. +# Default: 8192 + +# $_ems = (8192) + +# DOS segment in UMA where the EMS frame is put. +# Default: 0xe000 + +# $_ems_frame = (0xe000) + +# the amount of EMS-mappable pages in UMA. +# Possible values: 0 to 12 +# Default: 4 + +# $_ems_uma_pages = (4) + +# the amount of EMS-mappable pages in conventional memory. +# Possible values: 0 to 24 +# Default: 24 + +# $_ems_conv_pages = (24) + +# DPMI memory size in Kbytes +# Default: 0x20000 (128Mb) + +# $_dpmi = (0x20000) + +# DPMI base address +# Default: 0x20000000 (at 512Mb) + +# $_dpmi_base = (0x20000000) + +# Some DJGPP-compiled programs have the NULL pointer dereference bugs. +# They may work under Windows or QDPMI as these unfortunately do not +# prevent that kind of errors. +# If you want dosemu to act properly and prevent NULL pointer accesses, +# disable this compatibility option. +# Default: on + +# $_ignore_djgpp_null_derefs = (on) + +############################################################################## +## Debug settings + +# debug switches; same format as -D commandline option, default: ""="-a+cw". +# (but without the -D in front), normally written to ~/.dosemu/boot.log + +# $_debug = "-a+cw" + +# which i/o ports to trace (only displayed if you also use -D+T) +# see $_ports below for the syntax + +# $_trace_ports = "" + +# which memory mapped i/o addresses to trace (only displayed if you also use -D+F) +# list of addresses such as "0xA0000 0xB0000 0xFFFFF" or +# "0xA0000 range 0xB0000 0xB0FFF" + +# $_trace_mmio = "" + +############################################################################## +## Dosemu-specific hacks + +# choose the time source for the RTC emulation. Possible values: +# "pit", "bios" and "linux". The "linux" time source will follow +# the system time changes, but it doesn't allow to set the time +# from DOS. "bios" time source is monotonous (will not follow +# the system time changes), but allows you to set time from DOS. + +# $_timemode = "bios" + +# set this to some positive value (eg. Default: 10) +# if you want to play Doom or Duke3D with sound. + +# $_cli_timeout = (10) + +# apply some tweaks on emulated timers to make some old games more reliable. +# See tweaks.html for more info. + +# $_timer_tweaks = (off) + +############################################################################## +## Terminal related settings + +# Character set used externally to dosemu +# Default: "" == use the character set from the locale or else: +# "cp437", "cp737", "cp773", "cp775", "cp850", "cp852", "cp857", "cp860", +# "cp861", "cp862", "cp863", "cp864", "cp865", "cp866", "cp869", "cp874", +# "cp1125", "cp1251" +# "iso8859-1", "iso8859-2", "iso8859-3", "iso8859-4", "iso8859-5", "iso8859-6", +# "iso8859-7", "iso8859-8", "iso8859_9", "iso8859-14", "iso8859-15", "koi8-r" +# "koi8-u", "koi8-ru", "utf8" + +# $_external_char_set = "" + +# Character set used by dos programs +# Default: "" == use "cp437" or else: +# "cp437", "cp737", "cp773", "cp775", "cp850", "cp852", "cp857", "cp860", +# "cp861", "cp862", "cp863", "cp864", "cp865", "cp866", "cp869", "cp874", +# "cp895", "cp1125", "cp1251", "bg-mik" + +# $_internal_char_set = "" + +# terminal with color support. Default: (on) + +# $_term_color = (on) + +# set terminal size on start-up. +# Example value: "80x25" +# Default: "" (which means, use current terminal size) + +# $_fixed_term_size = "" + +# xterm, putty and compatibles window title. Default: %s - DOSEMU +# where %s is the DOS program's name. Use "" to not change the title + +# $_xterm_title = "%s - dosemu2" + +############################################################################## +## Keyboard related settings + +# Keyboard layout +# default: 'auto' (which tries to generate the table from +# the current Linux console settings) +# or the list of: finnish(-latin1), de(-latin1), be, it, us, uk, dk(-latin1), +# keyb-no, no-latin1, dvorak, pl, po, sg(-latin1), fr(-latin1), sf(-latin1), +# es(-latin1), sw, hu(-latin2), hu-cwi, keyb-user, hr-cp852, hr-latin2, +# cz-qwerty, cz-qwertz, ru, tr. +# No more than 2 layouts can be specified, for example: "us,ru" + +# $_layout = "auto" + +# bypass normal keyboard input on the Linux console, maybe dangerous +# default: (auto), use only with -s or -k, or (0): off, use -k, or (1): on. + +# $_rawkeyboard = (auto) + +# 30 == Ctrl-^ (Ctrl-6 on US keyboards), special-sequence prefix for terminals +# use Ctrl-^ h for help + +# $_escchar = (30) + +############################################################################## +## Mouse settings + +# Use internal mouse driver. Default = (on). + +# $_mouse_internal = (on) + +# Mouse protocol: one of (microsoft, mousesystems, logitech, mmseries, +# mouseman, hitachi, busmouse, ps2, imps2). +# Default: "ps2" + +# $_mouse = "ps2" + +# Mouse device: if you want to use the internal mouse driver, you can give +# the real mouse device such as "/dev/input/mice", "/dev/mouse", "/dev/psaux" +# (for PS/2), or "/dev/gpmdata" (for GPM repeater usage). +# If you intend to use dosemu-dedicated serial mouse, set this to "comX" +# rather than "/dev/ttySx" because the serial port needs a special mouse +# backend. +# "" means: do not read the mouse device directly (no mouse in +# console "$_graphics=(1)" mode, but GPM can still be used in other modes). +# Default: "/dev/input/mice" + +# $_mouse_dev = "/dev/input/mice" + +# Default: "" or one or more of: "emulate3buttons cleardtr" + +# $_mouse_flags = "" + +# baudrate, default: 0 == don't set + +# $_mouse_baud = (0) + +# alternative behaviour in ungrabbed mode +# May fix some problems with cursor positioning, but enabling mouse +# grab is always more reliable. +# Default: off + +# $_mouse_ungrab_tweak = (off) + +############################################################################## +## Joystick config + +# 1st and 2nd joystick device +# e.g. "/dev/input/js0" or default: "/dev/input/js0 /dev/input/js1" +# (or "" if you don't want joystick support) + +# $_joy_device = "/dev/input/js0 /dev/input/js1" + +# range for joystick axis readings, must be > 0, default: 1 + +# $_joy_dos_min = (1) + +# avoid setting the maximum to > 250, default: 150 + +# $_joy_dos_max = (150) + +# the higher, the less sensitive - useful if you have a wobbly joystick. +# default: 1 + +# $_joy_granularity = (1) + +# delay between nonblocking linux joystick reads increases performance if >0 +# and processor>=Pentium recommended: 1-50ms or 0 if unsure. default: 1 + +# $_joy_latency = (1) + +############################################################################## +## Serial port settings + +# use e.g. "/dev/mouse", "/dev/ttyS0", "/dev/ttyS1", ... +# +# All "/dev/ttyXX" may be suffixed by the IRQ +# used (instead of the default one), such as "/dev/ttyS2 irq 5" +# or "/dev/ttyS3 irq 9". +# Note: you can use any file here, eg fifo or even a regular file. +# In case of reading a regular file, don't forget to put ^Z (0x1a) +# at the end or there will be no EOF. +# +# "pseudo" keyword may be used to force the pseudo-TTY mode. +# A wrong but quick way to bypass the hardware flow control. +# +# "rtscts" keyword may be used as a wrong but quick way to enforce the +# hardware flow control. +# +# "virtual" keyword may be used to redirect the COM port to the terminal. +# A wrong but quick way for the sysop to play his BBS door games locally. +# +# "exec " execute command and pipe it to the serial port. +# For example "exec rxvt -pty-fd 0" launches rxvt that can communicate +# with that serial port. Can also be used to play door games locally. +# +# "pts " create pts and attach its pty to serial port. +# For example "pts /tmp/com1" creates /tmp/com1 for communicating +# with the serial port. +# +# "mouse" keyword enables the serial mouse emulation on the specified port. +# You can then use native mouse driver, like mouse.com. +# Note: if you configured this as a second mouse ($_mouse_internal = (on)), +# then this secondary serial mouse will use the device specified in +## $_mouse_dev. In that case the primary mouse source should be device-less, +# i.e. SDL, X, xterm, gpm. The configurations with 2 device-less mices +# or with 2 mices both reading different devices are not supported. +# +# "vmodem" keyword enables the modem emulation on the specified port. +# You can then dial to the IP addresses, which will establish the telnet +# connection. +# +# "nullmodem " - connect this port to port with a +# null-modem cable. You need to also set up port and connect +# it to this port the same way. +# +# "ro" - read-only. Write to the port is ignored unless the option below +# is set. +# +# "wrfile " - use file for com port output. +# +# Default: "" + +# $_com1 = "" +# $_com2 = "" +# $_com3 = "" +# $_com4 = "" + +# $_com5 = "" +# $_com6 = "" +# $_com7 = "" +# $_com8 = "" +# $_com9 = "" +# $_com10 = "" +# $_com11 = "" +# $_com12 = "" +# $_com13 = "" +# $_com14 = "" +# $_com15 = "" +# $_com16 = "" + +# tty lock directory. Empty string "" means no tty locks. +# default: "/var/lock" + +# $_ttylocks = "/var/lock" + +############################################################################## +## Printer and parallel port settings + +# Print commands to use for LPT1, LPT2 and LPT3. +# Default: "lpr -l", "lpr -l -P lpt2", and "" (disabled) +# Which means: use the default print queue for LPT1, "lpt2" queue for LPT2. +# "-l" means raw printing mode (no preprocessing). +# Use "direct /dev/lpX" to print directly to /dev/lpX. +# Accessing /dev/lpX may also be tried with an LPT dongle. +# "lpr -P PDF" requires installation of cups-pdf and PostScript output +# from DOS application. + +# $_lpt1 = "lpr -l" +# $_lpt2 = "lpr -P PDF" +# $_lpt3 = "" +# $_lpt4 = "" +# $_lpt5 = "" +# $_lpt6 = "" +# $_lpt7 = "" +# $_lpt8 = "" +# $_lpt9 = "" + +# idle time in seconds before spooling out. Default: (20) + +# $_printer_timeout = (20) + +############################################################################## +## Speaker and sound settings + +# speaker: default: "emulated", or "native" (console only) or "" (off) + +# $_speaker = "emulated" + +# sound support + +# $_sound = (on) + +# (emulated!) Sound Blaster base i/o port, default: (0x220) + +# $_sb_base = (0x220) + +# Sound Blaster IRQ setting, default: (5) + +# $_sb_irq = (5) + +# Sound Blaster 8 bit DMA setting, default: (1) + +# $_sb_dma = (1) + +# Sound Blaster 16 bit DMA setting, default: (5) + +# $_sb_hdma = (5) + +# MPU-401 base i/o port + +# $_mpu_base = (0x300) + +# MT32 MPU-401 base i/o port + +# $_mpu_base_mt32 = (0x330) + +# MIDI synth mode for MPU-401 at $_mpu_base_mt32 +# Supported values: "gm", "mt32" +# Default: "mt32" + +# $_midi_synth = "mt32" + +# MPU-401 irq for GM synth mode +# Default: auto (which is equal to $_sb_irq) + +# $_mpu_irq = (auto) + +# MPU-401 irq for mt32 synth mode +# Default: 9 + +# $_mpu_irq_mt32 = (9) + +# Sound output driver. +# Supported drivers: ao, sdl (if compiled in) +# Default: "" (which means auto) + +# $_sound_driver = "" + +# MIDI output drivers. +# Comma-separated list of midi drivers to enable. +# Supported GM drivers: +# fluid, timidity, alsa_midi, oss_midi, pipe_midi +# Supported MT-32 drivers: +# munt, alsa_virmidi +# It is better to specify 2 drivers at once, one for GM and one for MT-32. +# Example: "alsa_midi,alsa_virmidi" +# Default: "" (which means auto) +# Note: if you use $_wav_file option to record the sound and want +# the rendered midi to also be recorded there, you need to select +# either "fluid" or "timidity" here. Unfortunately, most existing +# versions of fluidsynth and timidity are incompatible with dosemu. + +# $_midi_driver = "" + +# fluidsynth soundfont path + +# $_fluid_sfont = "" + +# fluidsynth volume +# Values: 0 to 10 (larger than 10 is possible but results in distortion) +# Default: 6 + +# $_fluid_volume = (6) + +# munt ROMs path. +# Path to munt ROM files. +# Default: "roms" (which means ~/.dosemu/roms) + +# $_munt_roms = "roms" + +# opl2lpt device name. +# Example: "parport0" will use /dev/parport0 +# Default: "" (disabled) + +# $_opl2lpt_dev = "" + +# opl2lpt device type. +# "opl2" or "opl3". +# Default: "opl2" + +# $_opl2lpt_type = "opl2" + +# Sound and MIDI plugin parameters. +# Syntax: plugin1:param1=val1 plugin2:param2=val2..." +# Description: dosemu implements many sound and midi plugins. +# Some of them may be selected with the $_sound_driver and $_midi_driver +# options above; some are managed by dosemu automatically. +# This option allows to pass the parameters to any of them. +# Example: "alsa_virmidi:dev_name=munt" +# If your /etc/asound.conf doesn't define the rawmidi device index, +# you can set it like in this example: +# Example: "alsa_midi:dev_name=hw:3,0 alsa_virmidi:dev_name=hw:3,1" +# You can disable some plugins this way: "alsa_virmidi:enabled=0" +# Default: "" + +# $_snd_plugin_params = "" + +# PCM high-pass filter. +# It is needed for better integration with the desktop sound stack. +# In some cases pulseaudio may not be able to properly mix dosemu's +# sound stream with the desktop sounds, so your mp3 music may be +# silenced or distorted while you are playing some DOS game. +# This filter helps pulseaudio to do its work properly. +# With some volume settings and some pulseaudio versions this filter +# is needed even if dosemu is the only active sound source. +# Default: on + +# $_pcm_hpf = (on) + +# midi file to capture midi music to. +# Default: "" + +# $_midi_file = "" + +# wav file to capture sound to. +# Note: this also captures the midi music, so you may not need +# to use the $_midi_file option above. +# Default: "" + +# $_wav_file = "" + +############################################################################## +## Network settings + +# built-in Packet Driver. Default: on + +# $_pktdriver = (on) + +# NE2000 emulation (experimental). Default: on + +# $_ne2k = (on) + +# Virtual networking type. "eth" for exclusive NIC access, "tap" for +# using the TAP virtual device, "vde" for vde2 and "slirp" for slirp. +# NOTE: "eth" and "tap" methods require root privileges. +# "eth" method is deprecated. +# Default: "" (auto, which tries slirp) + +# $_vnet = "" + +# Network device for packet driver in "eth" mode. +# Default: "eth1" + +# $_ethdev = "eth1" + +# Network device for packet driver in "tap" mode. +# "" (empty string) means dynamic TAP allocation, which usually requires +# root privs. The automatically created devices will have the names +# dosemu_tapN where N is a number. +# Default: "tap0" + +# $_tapdev = "tap0" + +# Network daemon socket name (for passt mainly). +# Set to "/tmp/passt_1.socket" to use with passt. +# Default: "" + +# $_netsock = "" + +# vdeswitch socket name for packet driver in "vde" mode. +# If empty string is specified, dosemu will try to set up the VDE switch +# automatically. +# Default: "" + +# $_vdeswitch = "" + +# extra arguments for slirpvde. See "man slirpvde" for details. +# Default: "--dhcp" + +# $_slirpargs = "--dhcp" + +# use Novell 802.3 hack. Default: off + +# $_novell_hack = (off) + +# NOTE: IPX needs root privileges unless you setup /proc/net/ipx_route +# in advance. +# Default: (off) + +# $_ipxsupport = (off) + +# IPX network address to use. One of those listed in /proc/net/ipx/interface. +# Address 0 means use the network to which the primary interface, if exist, +# is attached to. +# Default: 0 + +# $_ipx_network = (0) + +############################################################################## +## Settings for X Window System (xdosemu, dosemu -X) +## Some apply to SDL (dosemu -S) driver as well. + +# Title in the top bar of the window. Default = "DOSEMU2" + +# $_X_title = "dosemu2" + +# Show name of running app in the top bar of the window. Default: on + +# $_X_title_show_appname = (on) + +# Text for icon, when minimized. Default = "xdosemu" + +# $_X_icon_name = "xdosemu" + +# Start DOSEMU in fullscreen mode. Default = "off" + +# $_X_fullscreen = (off) + +# blink rate for the cursor + +# $_X_blinkrate = (12) + +# name of the X font that is used (e.g. "vga") +# dosemu provides the following fonts: +# vga vga8x19 vga11x19 vga10x24 vga12x30 vgacyr vga10x20 vga-ua vga10x20-ua +# vga8x14-cp850 vga10x20-cp850 +# Default: "" (use VGA fonts) + +# $_X_font = "" + +# Use shared memory extensions. Faster, but problematic with remote X. +# Default: on + +# $_X_mitshm = (on) + +# share the colormap with other applications. Default: off + +# $_X_sharecmap = (off) + +# Set fixed aspect for resizing the graphics window. Default: on + +# $_X_fixed_aspect = (on) + +# Always use an aspect ratio of 4:3 for graphics. Default: off + +# $_X_aspect_43 = (off) + +# Use linear filtering for >15 bpp interpolation. Default: off + +# $_X_lin_filt = (off) + +# Use bi-linear filtering for >15 bpp interpolation. Default: off + +# $_X_bilin_filt = (off) + +# initial size factor for video mode 0x13 (320x200) + +# $_X_mode13fact = (2) + +# "x,y" of initial windows size (defaults to ""=float) + +# $_X_winsize = "" + +# gamma correction. Default: 1.0 + +# $_X_gamma = (1.0) + +# size (in Kbytes) of the frame buffer for emulated vga. Default: 4096 + +# $_X_vgaemu_memsize = (4096) + +# use linear frame buffer in VESA modes. Default: on + +# $_X_lfb = (on) + +# use protected mode interface for VESA modes. Default: on + +# $_X_pm_interface = (on) + +# KeySym name to activate mouse grab, ""=off. Default: "Home" (ctrl+alt+home) + +# $_X_mgrab_key = "Home" + +# List of vesamodes to add. The list has to contain SPACE separated +# "xres,yres" pairs, as follows: "xres,yres ... xres,yres". Default: "" + +# $_X_vesamode = "" + +# pause xdosemu if it loses focus + +# $_X_background_pause = (off) + +# Hide the Close button and disable the Close menu item + +# $_X_noclose = (off) + +# Disable resize by dragging window borders + +# $_X_noresize = (off) + +############################################################################## +## Settings specific to SDL video driver (dosemu -S) + +# Use GPU-assisted rendering. +# Default: off (we still have problems with HW acceleration) + +# $_SDL_hwrend = (off) + +# Comma-separated list of TTF fonts to use. +# Default: "Flexi IBM VGA False, Flexi IBM VGA True" + +# $_SDL_fonts = "Flexi IBM VGA False, Flexi IBM VGA True" + +# Show window controls. +# It is not a common need to disable them, and can be done at run-time +# by pressing Ctrl-Alt-b. +# Default: on + +# $_SDL_wcontrols = (on) + +# Enable native clipboard access. +# Can be toggled at run-time by pressing Ctrl-Alt-c. +# Default: off + +# $_SDL_clip_native = (off) + +############################################################################## +## Common video driver settings + +# Force use of VGA fonts instead of native fonts. +# Try this option if some text-mode effects do not look right. +# Default: off + +# $_force_vga_fonts = (off) + +############################################################################## +## Direct hardware access + +# NOTE: the settings below here that are marked [priv] are only valid in +# a system-wide dosemu.conf and cannot be changed by ~/.dosemurc. +# For these settings to take effect, DOSEMU must be run with root privileges; +# run it as root, via sudo, or suid with adjustments in dosemu.users, +# and using the -s switch. + +# [priv] list of portnumbers such as "0x1ce 0x1cf 0x238" or +# "0x1ce range 0x280,0x29f 310" or "range 0x1a0,(0x1a0+15)". Default: "" + +# $_ports = "" + +# [priv] list of IRQ numbers (2-15) to pass to DOS such as "3 8 10".Default: "" +# This does not work on x86-64. + +# $_irqpassing = "" + +# [priv] DOS memory to map directly: list of segment values/ranges such as + +# "0xc8000 range 0xcc000,0xcffff". Default: "" + +# $_hardware_ram = "" + +# [priv] (on): give access to the PCI configuration space. +# (auto): restricted, mostly emulated access if the video card requires it. +# Default: (auto) + +# $_pci = (auto) + +############################################################################## +## Console video + +# The following settings apply to direct console video only (not in X) +# Many are privileged, and need suid/sudo/root and the -s switch. + +# use 'console' video (direct video ram access). Default: +# (auto) -- use only if KMS is not active + +# $_console = (auto) + +# use the cards BIOS to set graphics and allow direct port i/o. Default: +# (auto) -- use only if KMS is not active + +# $_graphics = (auto) + +# Video adapter style used: one of: vga, ega, mda, mga, cga, none +# Default: "vga"; none=dumb terminal mode. + +# $_video = "vga" + +# [priv] run the VGA card's initialization BIOS routine (most cards don't +# need this). Default: (0) + +# $_vbios_post = (0) + +# [priv] set the address of your VBIOS (e.g. 0xc000, 0xe000). +# Default: (0)=autodetect. + +# $_vbios_seg = (0) + +# [priv] set the size of your BIOS (e.g. 0x10000, 0x8000). + +# Default: (0)=autodetect. + +# $_vbios_size = (0) + +# [priv] amount in K of (real) video RAM to save/restore +# when switching virtual consoles. (auto) means all video RAM, +# which can take *too* long if you have a lot of the video memory. +# (off) means no video RAM saving. +# Default: 4096 + +# $_vmemsize = (4096) + +# [priv] real chipset: one of: plainvga, svgalib, vesa +# svgalib is available only if it is compiled in. +# "vesa" will probably work; if not, try "plainvga". Default: "vesa" + +# $_chipset = "vesa" + +# [priv] if you have one vga _plus_ one hgc (2 monitors) + +# $_dualmon = (0) + +############################################################################## +## Host permissions + +# list of commands (by their full paths) allowed to execute via unix.com +# Example: "/bin/ls /bin/echo" will enable commands ls and echo. +# Default: "" (host execution not allowed) + +# $_unix_exec = "" + +# list of paths allowed to be redirected to DOS drives with lredir.com +# Note: sub-paths of the specified paths are allowed, too. +# Note: this does not affect $_hdimage setting or redirections activated +# with -d command-line switch. +# Example: "/home/user/dos/c /home/user/dos/d" +# Note: spaces in paths are not supported, sorry. +# Note: giving full access like "/" or "/home/user" is insecure. +# Default: "" (host redirections with lredir.com not allowed) + +# $_lredir_paths = "" diff --git a/etc/dosemu.desktop.in b/etc/dosemu.desktop.in new file mode 100644 index 0000000..d7d2789 --- /dev/null +++ b/etc/dosemu.desktop.in @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=DOS emulator +Name[lt]=DOS emuliatorius +Comment=DOS emulator "dosemu2" +Comment[lt]=DOS emuliatorius "dosemu2" +Exec=@e_bindir@/dosemu +Icon=@e_datadir@/dosemu/icons/dosemu.xpm +Terminal=false +Type=Application +Categories=System;Emulator; diff --git a/etc/dosemu.xpm b/etc/dosemu.xpm new file mode 100644 index 0000000..3b6d023 --- /dev/null +++ b/etc/dosemu.xpm @@ -0,0 +1,73 @@ +/* XPM */ +static char * dosemu_xpm[] = { +"64 48 22 1", +" c none s mask", +". c #000000000000", +"X c #B2CA6595C30B", +"o c #C71BC71BF7DE", +"O c #0820F7DE0820", +"+ c #D34C75D64924", +"@ c #9A69DF7D9248", +"# c #F7DEF7DEF7DE", +"$ c #6185BAEA2492", +"% c #555555555555", +"& c #4D344D34F3CE", +"* c #4514AAAAE38D", +"= c #08200820F7DE", +"- c #FFFF65956595", +"; c #FFFFBAEA30C2", +": c #FFFFFFFF0000", +"> c #FFFF00000000", +", c #8617FFFF8E38", +"< c #49249A69FFFF", +"1 c #FFFFFFFFFFFF", +"2 c #0000FFFF0000", +"3 c #FFFF0000FFFF", +" ", +" .................................................... ", +" .XXoooooooooooooooooooooooooooooO+O+OOoX@oooooooooo. ", +" .+o####o#####o#o###o@#o#o#o##o##O$OOOOoo@o#o#@#####. ", +" .%&&&&&&&&&&&&&&&oo&&&*o=&&&&&&*#############o#oooo. ", +" .%&&*&*@@XXX===================&o$OOOOOOOOOOO@o%ooo. ", +" .X&&=&&&=&&&&&&&===============&oo###########oo%ooo. ", +" .X&============================&oo###########oo%ooo. ", +" .%&============================&o############o@%ooo. ", +" .X&&&&&&X@@X===================&o##o@o#######oo%ooo. ", +" .X&============================&ooo@#@#o#####oo%ooo. ", +" .%&&&=X@X@&&&=X@@==============&oooo#########o@%ooo. ", +" .%&============================&o############o@%ooo. ", +" .X&===&&=======================&oo###########oo%ooo. ", +" .%&============================&o############oo%ooo. ", +" .%&==&&&&&&&&==================&o############oo%ooo. ", +" .X&==&@&@X@XX@o&X==============&oo@@#########oo%ooo. ", +" .X&==&@=@&@X&==================&ooo@#ooooXooooo%ooo. ", +" .%&==&&=&&&X=&@@X=&=&&==.======&o##o#########oo%ooo. ", +" .X&===..........====&=..-.=====&o############oo%ooo. ", +" .X&...;;;;;;;.;.=====.---.=====&o@@@@@@oooooooX%ooo. ", +" ...;;;;;;;;..;;.&&&..-----.@&&&==......%%%%%%%%%ooo. ", +" .......;....;;;;.&&..------.&&&==...====*%oooooooooo. ", +" ..:::::::.::.;;;;;.=.>>.------.===.,,..===&%oooooooooo. ", +" .:::::..:::::.;;;;.==.>>.------.==.,,,,,..=&%oooooooooo. ", +" .::::..;;..:::.;;..=...>>.-------.=.,,,,,,,.*%oooooooooo. ", +" .::::.;;;;.=..:.;.=&.<<..>>.------..,,,,,,,,.*%oooooooooo. ", +" .:::.;;;;;.==.:..=..<<.<.>>.------.,,,,,,,,,.=%oooooooooo. ", +" .::::.;;;;;.%.%.%%.....<<.>>..----....,,,,,,,.%%oooooooooo. ", +" .:::.;;;;;;.oooooo.111.<.o.>>.---.222..,,,,,,.oooooooooooo. ", +" .::::.;;;;;;.oooooo.111..oo.>>.----.2222..,,,,.oooooooooooo. ", +" .::::.;;;;;;.##o###.111.#o#.>>.-----..2222..,.########o##o#. ", +" .::::.;;;;;;;................>>.----....2222................ ", +" .::::.;;;;;;;;.... .33.. .>>.----. .,..222. ", +" .::::.;;;;;;;;;;;. ..33.3. .>>..--. .,,..222. ", +" .::::.;;;;;;;;... .333.33. .>>.--. .,.2222. ", +" .::::.;;;;;..::......333. .>>.-. ...222.. ", +" .:::::.....:::. .111.33. .>>.-..2222.. ", +" .:::::::::::. .111.3. .>>. .22.. ", +" ..:::::::.. .111.. .>>. .. ", +" ....... ..... .. ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/etc/dosemu2.alias b/etc/dosemu2.alias new file mode 100644 index 0000000..5f54d24 --- /dev/null +++ b/etc/dosemu2.alias @@ -0,0 +1,11 @@ +vga -dosemu-vga-medium-r-normal--17-160-75-75-p-80-ibm-cp437 +vga8x19 -dosemu-vga-medium-r-normal--19-190-75-75-c-80-ibm-cp437 +vga11x19 -dosemu-vga-medium-r-normal--19-190-75-75-c-100-ibm-cp437 +vga10x24 -dosemu-vga-medium-r-normal--24-240-75-75-c-100-ibm-cp437 +vga12x30 -dosemu-vga-medium-r-normal--30-300-75-75-c-120-ibm-cp437 +vgacyr -dosemu-vga-medium-r-normal--17-160-75-75-c-80-ibm-cp866 +vga10x20 -dosemu-vga-medium-r-normal--20-200-75-75-c-100-ibm-cp866 +vga-ua -dosemu-vga-medium-r-normal--17-160-75-75-c-80-ibm-cp1125 +vga10x20-ua -dosemu-vga-medium-r-normal--20-200-75-75-c-100-ibm-cp1125 +vga8x14-cp850 -xos4-Terminus-Bold-R-Normal--14-140-72-72-C-80-IBM-CP850 +vga10x20-cp850 -xos4-Terminus-Bold-R-Normal--20-200-72-72-C-100-IBM-CP850 diff --git a/etc/global.conf b/etc/global.conf new file mode 100644 index 0000000..852bc75 --- /dev/null +++ b/etc/global.conf @@ -0,0 +1,597 @@ +############################################################################## +# WARNING: THIS FILE IS NOT USED BY DEFAULT. ADJUST dosemu.users OR USE -F. +# +# This file is global.conf. By default a compiled-in copy is used. If you +# make custom changes to the settings in this file, then you need to use +# the -F switch or change dosemu.users; the custom file is then parsed +# after the built-in copy. +# +# Linux dosemu configuration for parser versions >= 3 (dosemu-0.97.0.1) +# +# ./doc/README-tech.txt (chapter 2.) contains a description of the syntax +# and the usage. However, you normally won't edit this file !! +# +# NOTES: +# +# 1. The file dosemu.conf (and optionally ~/.dosemurc) contains variable +# settings, that are included by global.conf for doing the +# most reasonable configuration. +# The file dosemu.conf (and optionally ~/.dosemurc) is what gets +# updated by the menu driven 'setup-dosemu' tool. +# +# 2. We strongly recommend you to edit ONLY dosemu.conf. +# If you change global.conf, you are at your own and could break +# 'setup-dosemu'. You really need to know a lot of DOSEMU +# internals before you fiddle with editing global.conf. +# However, some very special cases can only be handled in global.conf. +# +# 3. The file global.conf (this one) is either +# - the one compiled into dosemu.bin. +# - specified by the config_script option in dosemu.users ("builtin" +# by default) +# - or given via the -F option (from root login, else only on +# non-suid-root DOSEMU) +# +# 4. The only compiled-in path is /etc/dosemu.users (or if not found +# /etc/dosemu/dosemu.users), however, this can be overridden by +# --Fusers, if not running suid-root. All other paths are configurable +# and the dosemu binaries can reside everywhere in the system. +# +# This file (global.conf) may also serve as an example file for +# .dosrc ( old style user configuration file ) +# option -I ( configuration via commandline, see man/dos.1 ) +# +# Access rights for suid-root running DOSEMU sessions are defined in +# +# /etc/dosemu.users or /etc/dosemu/dosemu.users +# +############################################################################## + +ifdef u_forceold + undef parser_version_3 + define version_3_style_used +endif + +ifndef parser_version_3 + # normally won't come here, because older DOSEMUs don't use this file + # ... but if using -F option ... + include "/etc/dosemu.conf" +else + # we are on version 3 parser + + ## we set some vital variable + + if (!strlen($DOSEMU_LIB_DIR)) + abort "DOSEMU_LIB_DIR not set"; + endif + if (!strlen($DOSEMU_IMAGE_DIR)) + $DOSEMU_IMAGE_DIR = $DOSEMU_LIB_DIR; + endif + # make sure we have absolute paths + shell("test -d ", $DOSEMU_IMAGE_DIR) + if ($DOSEMU_SHELL_RETURN) + abort "DOSEMU_IMAGE_DIR does not exist ", $DOSEMU_IMAGE_DIR; + endif + $DOSEMU_IMAGE_DIR = shell("cd ", $DOSEMU_IMAGE_DIR, "; pwd -P"); + $DOSEMU_IMAGE_DIR = strdel($DOSEMU_IMAGE_DIR, strlen($DOSEMU_IMAGE_DIR)-1, 1); + shell("test -d ", $DOSEMU_LIB_DIR) + if (!$DOSEMU_SHELL_RETURN) + $DOSEMU_LIB_DIR = shell("cd ", $DOSEMU_LIB_DIR, "; pwd -P"); + $DOSEMU_LIB_DIR = strdel($DOSEMU_LIB_DIR, strlen($DOSEMU_LIB_DIR)-1, 1); + endif + + $CONFIG_VERSION = ( (1 << 24) | (1 << 16) | (4 << 8) | 0) + # ^ ^ ^ ^ + if ( $DOSEMU_VERSION_CODE < $CONFIG_VERSION ) + abort " +*** sorry, your ", $DOSEMU_LIB_DIR, "/global.conf doesn't match this dosemu version +" + endif + + $LIST_DELIM = " , " #delimiters for lists , blank, comma + + # for non-suid-root we can switch off restricted checking. + if (strlen($DOSEMU_LAX_CHECKING)) + undef restricted + undef guest + endif + + if (strlen($_mapping)) $xxx = $_mapping else $xxx = "auto" endif + mappingdriver $xxx; + + if (strlen($_debug)) + debug $_debug ; + else + debug { off } + endif + if (strlen($_trace_ports)) trace ports { $$_trace_ports } endif + if (strlen($_trace_mmio)) trace_mmio { $$_trace_mmio } endif + + cpuspeed $_cpuspeed + + timer 0 + mathco $_mathco + $xxx = "cpu ", $_cpu; + $$xxx + cpuemu $$_cpuemu + $xxx = "cpu_vm ", $_cpu_vm; + $$xxx + $xxx = "cpu_vm_dpmi ", $_cpu_vm_dpmi; + $$xxx + if ($_ems) + ems { + ems_size $_ems + ems_frame $_ems_frame + ems_uma_pages $_ems_uma_pages + ems_conv_pages $_ems_conv_pages + } + else + ems off + endif + umb_a0 $_umb_a0 + umb_b0 $_umb_b0 + umb_b8 $_umb_b8 + umb_f0 $_umb_f0 + hma $_hma + dos_up $_dos_up + dpmi $_dpmi + dpmi_base $_dpmi_base + pm_dos_api 1 + ignore_djgpp_null_derefs $_ignore_djgpp_null_derefs + dosmem $_dosmem + ext_mem $_ext_mem + xms $_xms + + if ($_emusys ne "") emusys $_emusys endif + + ## terminal stuff, we check a lot to insure proper operation + + terminal { color $_term_color escchar $_escchar size $_fixed_term_size } + xterm_title $_xterm_title + video { vga } + if ($_external_char_set ne "") + charset { external $$_external_char_set } + endif + if ($_internal_char_set ne "") + charset { internal $$_internal_char_set } + endif + + ## X param settings + $xxx = "" + if ($_X_sharecmap) $xxx = $xxx, " sharecmap" endif + if ($_X_aspect_43) $xxx = $xxx, " aspect_43" endif + if ($_X_lin_filt) $xxx = $xxx, " lin_filt" endif + if ($_X_bilin_filt) $xxx = $xxx, " bilin_filt" endif + $xxx = $xxx, " mode13fact ", $_X_mode13fact + $xxx = $xxx, " gamma ", (int($_X_gamma * 100)) + $xxx = $xxx, " font '", $_X_font, "'" + if (strlen($_X_winsize)) + $yyy = (strstr($_X_winsize,",")) + $yyy = " winsize (", strdel($_X_winsize,$yyy,999), ") , (", + strsplit($_X_winsize,$yyy+1,999), ")" + $xxx = $xxx, $yyy + endif + + if (strlen($_X_vesamode)) + foreach $yyy (" ", $_X_vesamode) + $zzz = (strchr($yyy,",")) + $xxx = $xxx, " vesamode (", strdel($yyy,$zzz,999), "),(", + strsplit($yyy,$zzz+1,999), ") " + done + endif + $xxx = $xxx, ' mgrab_key "', $_X_mgrab_key, '"' + X { + title $_X_title title_show_appname $_X_title_show_appname + icon_name $_X_icon_name + blinkrate $_X_blinkrate + fixed_aspect $_X_fixed_aspect vgaemu_memsize $_X_vgaemu_memsize + lfb $_X_lfb pm_interface $_X_pm_interface mitshm $_X_mitshm + background_pause $_X_background_pause fullscreen $_X_fullscreen + noclose $_X_noclose + noresize $_X_noresize + $$xxx + } + + ## SDL settings + SDL { sdl_hwrend $_SDL_hwrend sdl_fonts $_SDL_fonts sdl_wcontrols $_SDL_wcontrols sdl_clip_native $_SDL_clip_native } + + # video settings + vga_fonts $$_force_vga_fonts + if ($DOSEMU_STDIN_IS_CONSOLE eq "1") + warn "dosemu running on console" + $xxx = $_video + if ($_console) $xxx = $xxx, " console" + if ($_console == auto) $xxx = $xxx, " auto" endif + endif + if ($_graphics) $xxx = $xxx, " graphics" + if ($_graphics == auto) $xxx = $xxx, " auto" endif + endif + $xxx = $xxx, " vbios_seg ", $_vbios_seg, " vbios_size ", + $_vbios_size, " memsize ", $_vmemsize + if (strlen($_chipset)) $yyy = $_chipset else $yyy = "plainvga" endif + $xxx = $xxx, " chipset ", $yyy + if ($_vbios_post) $xxx = $xxx, " vbios_post " endif + if ($_dualmon) $xxx = $xxx, " dualmon" endif + if (strlen($_vbios_file)) $xxx = $xxx, " vgaemubios_file ", $_vbios_file endif + video { $$xxx } + else + warn "dosemu not running on console" + $xxx = $_video + if (strlen($_vbios_file)) $xxx = $xxx, " vgaemubios_file ", $_vbios_file endif + if ($_dualmon) $xxx = $xxx, " dualmon " endif + if (strlen($xxx)) video { $$xxx } endif + endif + + ## sound settings + sound $_sound + sound_emu { sb_base $_sb_base + sb_irq $_sb_irq + sb_dma $_sb_dma + sb_hdma $_sb_hdma + mpu_base $_mpu_base + mpu_base_mt32 $_mpu_base_mt32 + midi_synth $_midi_synth + mpu_irq $_mpu_irq + mpu_irq_mt32 $_mpu_irq_mt32 + sound_driver $_sound_driver + midi_driver $_midi_driver + fluid_sfont $_fluid_sfont + fluid_volume $_fluid_volume + munt_roms $_munt_roms + opl2lpt_dev $_opl2lpt_dev + opl2lpt_type $_opl2lpt_type + snd_plugin_params $_snd_plugin_params + pcm_hpf $_pcm_hpf + midi_file $_midi_file + wav_file $_wav_file + } + + ## joystick settings + joystick_emu { joy_device $_joy_device joy_dos_min $_joy_dos_min + joy_dos_max $_joy_dos_max joy_granularity $_joy_granularity + joy_latency $_joy_latency } + + ## hacks + cli_timeout $_cli_timeout + timemode $_timemode + timer_tweaks $_timer_tweaks + + file_lock_limit $$_file_lock_limit + lfn_support $_lfn_support + force_int_revect $_force_int_revect + set_int_hooks $_set_int_hooks + trace_irets $_trace_irets + force_fs_redirect $_force_fs_redirect + + ## serial + $xxx = "'", $_ttylocks, "'" + ttylocks { directory $$xxx namestub LCK.. } + if (1) # no idea why but we need this... + foreach $xxx (" ", "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16") + $zzz = "$_com", $xxx + $zzz = $$zzz + if (strlen($zzz)) + $jjj = "" + if (strchr($zzz, " ") > 0) + $yyy = strsplit($zzz, strchr($zzz, " ") + 1, 999) + $zzz = strdel($zzz, strchr($zzz, " "), 999) + else + $yyy = "" + endif + if (strchr($zzz, "/") == 0) + if (strchr($yyy, " ") > 0) + $jjj = strsplit($yyy, strchr($yyy, " ") + 1, 999) + $yyy = strdel($yyy, strchr($yyy, " "), 999) + endif + if (strlen($jjj) > 0) + serial { com $xxx device $zzz $$yyy $jjj } + else + serial { com $xxx device $zzz $$yyy } + endif + else + if (strlen($yyy) > 0) + serial { com $xxx $$zzz $yyy } + else + serial { com $xxx $$zzz } + endif + endif + endif + done + endif + + ## mouse settings + if ($_mouse_internal) + $xxx = "" + if ($_mouse_baud) + $xxx = "baudrate ", $_mouse_baud + endif + $xxx = $xxx, " device '", $_mouse_dev, "'" + mouse { $$_mouse $$_mouse_flags $$xxx internaldriver mouse_ungrab_tweak $_mouse_ungrab_tweak } + else + $xxx = "'", $_mouse_dev, "'" + mouse { $$_mouse device $$xxx } + endif + + hogthreshold $_hogthreshold + + ## keyboard setting + if ($DOSEMU_STDIN_IS_CONSOLE ne "1") $_rawkeyboard = (off) endif + if ( strstr($_layout, "load") <0 ) + # we use the builtin keytables + if (strlen($_layout)) $yyy = $_layout else $yyy = "us" endif + keyboard { layout $yyy rawkeyboard $_rawkeyboard } + else + # we have to load a keytable + $yyy = "" + if (1) # no idea why (see com as well) but we need this... + foreach $zzz ($LIST_DELIM, $_layout) + if ($zzz ne "load") + $yyy = $zzz + endif + done + endif + if (!strlen($yyy)) + abort "no keytable name in $_layout" + endif + shell("test -f ", $DOSEMU_LIB_DIR, "/keymap/", $yyy) + if ( $DOSEMU_SHELL_RETURN) + abort "keytable ",$yyy, " not found in ", $DOSEMU_LIB_DIR, "/keymap/*" + endif + $_layout = 'include "keymap/', $yyy, '"' ; + $$_layout + keyboard { rawkeyboard $_rawkeyboard } + endif + + bootdrive $_bootdrive + swap_bootdrive $_swap_bootdrive + + if (strlen($_floppy_a)) + $fpath = strsplit($_floppy_a, 0, strstr($_floppy_a, ":")) + if (strlen($fpath)) + $ftype = strsplit($_floppy_a, strstr($_floppy_a, ":")+1, 999) + else + $fpath = $_floppy_a + $ftype = "" + endif + + $zzz = $fpath + if (strlen($zzz)) + if (strchr($zzz, "/") != 0) + $izzz = $DOSEMU_IMAGE_DIR, "/", $zzz + $xxx = shell("test -r ", $izzz); + if (!$DOSEMU_SHELL_RETURN) + $zzz = $izzz + endif + endif + $xxx = shell("test -r ", $zzz); + if ($DOSEMU_SHELL_RETURN) + warn "**** Warning: floppy ", $zzz, " not accessible, disabled"; + else + shell("test -d '", $zzz, "'") + if ($DOSEMU_SHELL_RETURN) + floppy { device $$zzz $$ftype } + else + floppy { directory $$zzz $$ftype } + endif + endif + endif + endif + if (strlen($_floppy_b)) + $fpath = strsplit($_floppy_b, 0, strstr($_floppy_b, ":")) + if (strlen($fpath)) + $ftype = strsplit($_floppy_b, strstr($_floppy_b, ":")+1, 999) + else + $fpath = $_floppy_b + $ftype = "" + endif + + $zzz = $fpath + if (strlen($zzz)) + if (strchr($zzz, "/") != 0) + $izzz = $DOSEMU_IMAGE_DIR, "/", $zzz + $xxx = shell("test -r ", $izzz); + if (!$DOSEMU_SHELL_RETURN) + $zzz = $izzz + endif + endif + $xxx = shell("test -r ", $zzz); + if ($DOSEMU_SHELL_RETURN) + warn "**** Warning: floppy ", $zzz, " not accessible, disabled"; + else + shell("test -d '", $zzz, "'") + if ($DOSEMU_SHELL_RETURN) + floppy { device $$zzz $$ftype } + else + floppy { directory $$zzz $$ftype } + endif + endif + endif + endif + fastfloppy 1 + + ## setting up hdimages + $xxx = shell("ls ", $DOSEMU_IMAGE_DIR, "/drives/*.lnk 2>/dev/null") + if (strlen($xxx)) + error "Compatibility warning: found deprecated setup of dosemu2 pre-alpha version." + error " If you do not intend to run such old dosemu2 versions " + error " (dosemu1 is fine), please do:" + error " rm ~/.dosemu/drives/*.lnk" + error " You may also do" + error " rm -rf ~/.dosemu/drives" + error " if you dont intend to run dosemu1." + endif + if (strlen($_hdimage)) + foreach $xxxx ($LIST_DELIM, $_hdimage) + $xxx_pref = "" + $xxx_suff = "" + if (strchr($xxxx, "*") != -1) + $xxx_pref = strdel($xxxx, strchr($xxxx, "*"), 999); + $xxx_suff = strsplit($xxxx, strchr($xxxx, "*") + 1, 999); + if (strchr($xxx_pref, "/") == 0) + $xxxx = shell("cd '", $xxx_pref, "' 2>/dev/null && printf *") + else + $xxxx = shell("cd '", $DOSEMU_IMAGE_DIR, "/", $xxx_pref, "' 2>/dev/null && printf *") + endif + if ($DOSEMU_SHELL_RETURN) + abort "**** directory ", $xxx_pref, " not accessible"; + endif + endif + if (strchr($xxxx, "+") == 0) + default_drives strtol(strdel($xxxx, 0, 1)) + else if (strchr($xxxx, "-") == 0) + skip_drives strtol(strdel($xxxx, 0, 1)) + else if (strlen($xxxx)) + foreach $xxx ($LIST_DELIM, $xxxx) + $xxx = $xxx_pref, $xxx, $xxx_suff + if (!strncmp($xxx, "/dev/", 4)) + $yyy = strdel($xxx, strstr($xxx, ":"), 999); + $zzz = strsplit($xxx, strstr($xxx, ":"), 999); + if (strtol(strdel($xxx,0,8)) > 0) + disk { partition $yyy $$zzz }; + else + disk { wholedisk $yyy $$zzz }; + endif + else + $yyy = strdel($xxx, strstr($xxx, ":"), 999); + $zzz = strsplit($xxx, strstr($xxx, ":"), 999); + if (strchr($yyy, "/") != 0) + $yyyy = $DOSEMU_IMAGE_DIR, "/", $yyy + $yyy = $yyyy + endif + shell("test -d '", $yyy, "'") + if (!$DOSEMU_SHELL_RETURN) + disk { directory $yyy $$zzz }; + else + shell("test -f '", $yyy, "'") + if (!$DOSEMU_SHELL_RETURN) + disk { image $yyy $$zzz }; + else + abort "hdimage ", $yyy, " not found" + endif + endif + endif + done + endif endif endif + done + endif + + ## setting up CDROM devices + if (strlen($_cdrom)) + foreach $xxx ($LIST_DELIM, $_cdrom) + cdrom { $xxx } + done + endif + + ## setting up ASPI devices + ifndef restricted + if (strlen($_aspi)) + foreach $xxx ($LIST_DELIM, $_aspi) + $zz = (1); + $yy2 = ""; $yy3 = (-1); + if(strlen($xxx)) + foreach $yyy (":", $xxx) + $zzz = "$yy", $zz, " = $yyy"; + $zz = ($zz + 1); + $$zzz + done; + endif; + aspi { $yy1 devicetype $yy2 target $yy3 }; + done + endif + endif + + ipxsupport $_ipxsupport + ipx_network $_ipx_network + novell_hack $_novell_hack + if (1) # no idea why but we need this... + foreach $xxx (" ", "1 2 3 4 5 6 7 8 9") + $zzz = "$_lpt", $xxx + $zzz = $$zzz + if ((strstr($zzz, "direct") != -1) && (strchr($zzz, ' ') != -1)) + $zzz = strdel($zzz, 0, strchr($zzz, ' ') + 1) + printer { lpt $xxx file $zzz timeout $_printer_timeout } + else if (strlen($zzz)) + $zzz = "'", $zzz, "'" + printer { lpt $xxx command $$zzz timeout $_printer_timeout } + endif + done + endif + + if (strlen($_speaker)) + $xxx = "speaker ", $_speaker; + $$xxx + else + speaker off + endif + ifdef restricted + ## /etc/dosemu.users defined 'restricted' for this login + define c_normal + undef c_all + if ($_vnet eq "vde" || !$_pktdriver) + vdeswitch $_vdeswitch + slirpargs $_slirpargs + vnet $_vnet + pktdriver $_pktdriver + endif + if ($_pci) + pci auto + endif + else + # here are the root requiring options + if (strlen($_irqpassing)) + $yyy = "irqpassing { " + if (strlen($_irqpassing)) + foreach $xxx (" ", $_irqpassing) + $yyy = $yyy, "use_sigio ", $xxx + done + endif + $yyy = $yyy, " }"; + $$yyy + else + irqpassing off + endif + if (strlen($_hardware_ram)) + hardware_ram { $$_hardware_ram } + endif + ethdev $_ethdev + tapdev $_tapdev + vdeswitch $_vdeswitch + slirpargs $_slirpargs + netsock $_netsock + vnet $_vnet + pktdriver $_pktdriver + ne2k $_ne2k + if (strlen($_ports)) ports { $$_ports } endif + pci $_pci + endif + + if (strlen($_unix_exec)) + unix_exec $_unix_exec + endif + if (strlen($_lredir_paths)) + lredir_paths $_lredir_paths + endif + if (strlen($_hostfs_drives)) + hostfs_drives $_hostfs_drives + endif + + ## setting up the features list + if ( ( ($DOSEMU_VERSION_CODE >= ((98 << 16) | (3 << 8) | 3)) + && ($DOSEMU_VERSION_CODE < (99 << 16)) ) + || ($DOSEMU_VERSION_CODE > ((99 << 16) | (5 << 8))) ) + if (strlen($_features)) + foreach $xxx ($LIST_DELIM, $_features) + $yyy = strdel($xxx, strstr($xxx, ":"), 999); + $zzz = strsplit($xxx, strstr($xxx, ":"), 999); + if (strlen($zzz)) + $zzz = strsplit($zzz, 1, 999); + else + $zzz = (0); + endif + feature { $yyy = $zzz }; + done + endif + endif + +endif +############################################################################# diff --git a/etc/keymap/be b/etc/keymap/be new file mode 100644 index 0000000..b6ec128 --- /dev/null +++ b/etc/keymap/be @@ -0,0 +1,29 @@ +#keytable be +keytable "keyb-user" { + 0= + 0,27,"&",130,34,39,"(",21,138,"!",128,133,")-",127,9, + "azertyuiop",dcircum,"$",13,0,"qs", + "dfghjklm",151,253,0,230,"wxcv", + "bn,;:=",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"1234567890",248,"_",127,9, + "AZERTYUIOP",ddiares,"*",13,0,"QS", + "DFGHJKLM%",252,0,156,"WXCV", + "BN?./+",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,"|@#",0,0,"^",0,0,"{}",dabover,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"[]",13,0,0,0, + 0,0,0,0,0,0,0,0,dacute,dgrave,0,dgrave,0,0,0,0, + 0,0,dcedilla,0,0,dtilde,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,92,0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230." +} diff --git a/etc/keymap/cz-qwerty b/etc/keymap/cz-qwerty new file mode 100644 index 0000000..40b97dc --- /dev/null +++ b/etc/keymap/cz-qwerty @@ -0,0 +1,29 @@ +#keytable cz-qwerty +keytable "keyb-user" { + 0= + 0,27,"+",216,231,159,253,167,236,160,161,130,"=",dacute,127,9, + "qwertyuiop",163,")",13,0,"as", + "dfghjkl",133,245,";",0,ddiares,"zxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"&",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"1234567890%",dcaron,127,9, + "QWERTYUIOP/(",13,0,"AS", + "DFGHJKL",34,"!",248,0,39,"ZXCV", + "BNM?:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"*",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,"~",dcaron,dcircum,dbreve,248,dogonek,dgrave,daboved,dacute,ddacute,ddiares,dcedilla,0,0, + 92,"|",169,0,0,0,0,0,162,0,246,158,0,0,165,208, + 209,"[]",0,0,136,157,"$",225,0,0,207,">#",134,"@", + "{}",0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230." +} diff --git a/etc/keymap/cz-qwertz b/etc/keymap/cz-qwertz new file mode 100644 index 0000000..11200fb --- /dev/null +++ b/etc/keymap/cz-qwertz @@ -0,0 +1,29 @@ +#keytable cz-qwertz +keytable "keyb-user" { + 0= + 0,27,"+",216,231,159,253,167,236,160,161,130,"=",dacute,127,9, + "qwertzuiop",163,")",13,0,"as", + "dfghjkl",133,245,";",0,ddiares,"yxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"&",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"1234567890%",dcaron,127,9, + "QWERTZUIOP/(",13,0,"AS", + "DFGHJKL",34,"!",248,0,39,"YXCV", + "BNM?:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"*",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,"~",dcaron,dcircum,dbreve,248,dogonek,dgrave,daboved,dacute,ddacute,ddiares,dcedilla,0,0, + 92,"|",169,0,0,0,0,0,162,0,246,158,0,0,165,208, + 209,"[]",0,0,136,157,"$",225,0,0,207,">#",134,"@", + "{}",0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/de b/etc/keymap/de new file mode 100644 index 0000000..129cb69 --- /dev/null +++ b/etc/keymap/de @@ -0,0 +1,29 @@ +#keytable de +keytable "keyb-user" { + 0= + 0,27,"1234567890",225,39,127,9, + "qwertzuiop",129,"+",13,0,"as", + "dfghjkl",148,132,"^",0,"#yxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,156,"$%&/()=?",96,127,9, + "QWERTZUIOP",154,"*",13,0,"AS", + "DFGHJKL",153,142,248,0,39,"YXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,253,252,0,0,0,"{[]}",92,0,0,0, + "@",0,0,0,0,0,0,0,0,0,0,"~",13,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,230,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/de-latin1 b/etc/keymap/de-latin1 new file mode 100644 index 0000000..0f9325f --- /dev/null +++ b/etc/keymap/de-latin1 @@ -0,0 +1,29 @@ +#keytable de-latin1 +keytable "keyb-user" { + 0= + 0,27,"1234567890",225,dacute,127,9, + "qwertzuiop",129,"+",13,0,"as", + "dfghjkl",148,132,dcircum,0,"#yxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,21,"$%&/()=?",dgrave,127,9, + "QWERTZUIOP",154,"*",13,0,"AS", + "DFGHJKL",153,142,248,0,39,"YXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,253,252,"$",0,0,"{[]}",92,0,0,0, + "@",0,0,0,0,0,0,0,0,0,0,"~",13,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,230,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/dk b/etc/keymap/dk new file mode 100644 index 0000000..aedfd11 --- /dev/null +++ b/etc/keymap/dk @@ -0,0 +1,29 @@ +#keytable dk +keytable "keyb-user" { + 0= + 0,27,"1234567890+",39,127,9, + "qwertyuiop",134,0,13,0,"as", + "dfghjkl",145,155,0,0,39,"zxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,"#$%&/()=?",96,127,9, + "QWERTYUIOP",143,"^",13,0,"AS", + "DFGHJKL",146,157,0,0,"*ZXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@",163,"$",0,0,"{[]}",0,"|",0,0, + 0,0,0,0,0,0,0,0,0,0,0,"~",13,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,92,0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/dk-latin1 b/etc/keymap/dk-latin1 new file mode 100644 index 0000000..e51fffd --- /dev/null +++ b/etc/keymap/dk-latin1 @@ -0,0 +1,29 @@ +#keytable dk-latin1 +keytable "keyb-user" { + 0= + 0,27,"1234567890+",180,127,9, + "qwertyuiop",229,168,13,0,"as", + "dfghjkl",230,162,189,0,39,"zxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,"#$%&/()=?",96,127,9, + "QWERTYUIOP",197,"^",13,0,"AS", + "DFGHJKL",198,165,167,0,"*ZXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@",163,"$",0,0,"{[]}",0,"|",0,0, + 0,0,0,0,0,0,0,0,0,0,0,"~",13,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,92,0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/dvorak b/etc/keymap/dvorak new file mode 100644 index 0000000..fcabe5a --- /dev/null +++ b/etc/keymap/dvorak @@ -0,0 +1,29 @@ +#keytable dvorak +keytable "keyb-user" { + 0= + 0,27,"1234567890",92,"=",127,9, + 39,",.pyfgcrl/]",13,0,"ao", + "euidhtns-",96,0,"[;qjk", + "xbmwvz",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!@#$%^&*()|+",127,9, + 34,"<>PYFGCRL?}",13,0,"AO", + "EUIDHTNS_~",0,"{:QJK", + "XBMWVZ",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@",0,"$",0,0,"{[]}",92,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,"~",13,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/es b/etc/keymap/es new file mode 100644 index 0000000..9b25b7b --- /dev/null +++ b/etc/keymap/es @@ -0,0 +1,29 @@ +#keytable es +keytable "keyb-user" { + 0= + 0,27,"1234567890",39,0,127,9, + "qwertyuiop",96,"+",13,0,"as", + "dfghjkl",0,39,96,0,0,"zxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,"#$%&/()=?",0,127,9, + "QWERTYUIOP^*",13,0,"AS", + "DFGHJKL",0,0,"~",0,0,"ZXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,"|@#$",0,0,"{[]}",92,"~",0,0, + 0,0,0,0,0,0,0,0,0,0,"[]",13,0,0,0, + 0,0,0,0,0,0,0,0,"{",92,0,"}",0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"~",0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/es-latin1 b/etc/keymap/es-latin1 new file mode 100644 index 0000000..bf5e93f --- /dev/null +++ b/etc/keymap/es-latin1 @@ -0,0 +1,29 @@ +#keytable es-latin1 +keytable "keyb-user" { + 0= + 0,27,"1234567890",39,173,127,9, + "qwertyuiop",dgrave,"+",13,0,"as", + "dfghjkl",164,dacute,167,0,135,"zxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,250,"$%&/()=?",168,127,9, + "QWERTYUIOP",dcircum,"*",13,0,"AS", + "DFGHJKL",165,ddiares,166,0,128,"ZXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,"|@#$",0,172,"{[]}",92,"~",0,0, + 0,0,0,0,0,0,0,0,0,0,"[]",13,0,0,0, + 0,0,0,0,0,0,0,0,"{",92,0,"}",0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"~",0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/finnish b/etc/keymap/finnish new file mode 100644 index 0000000..0e94091 --- /dev/null +++ b/etc/keymap/finnish @@ -0,0 +1,29 @@ +#keytable finnish +keytable "keyb-user" { + 0= + 0,27,"1234567890+",39,127,9, + "qwertyuiop}",0,13,0,"as", + "dfghjkl|{",0,0,39,"zxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,"#$%&/()=?",96,127,9, + "QWERTYUIOP]^",13,0,"AS", + "DFGHJKL",92,"[",0,0,"*ZXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@",163,"$",0,0,"{[]}",92,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,"~",13,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/finnish-latin1 b/etc/keymap/finnish-latin1 new file mode 100644 index 0000000..567fc08 --- /dev/null +++ b/etc/keymap/finnish-latin1 @@ -0,0 +1,29 @@ +#keytable finnish-latin1 +keytable "keyb-user" { + 0= + 0,27,"1234567890+",180,127,9, + "qwertyuiop",134,168,13,0,"as", + "dfghjkl",148,132,167,0,39,"zxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,"#$%&/()=?",96,127,9, + "QWERTYUIOP",143,"^",13,0,"AS", + "DFGHJKL",153,142,171,0,"*ZXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@",156,"$",0,0,"{[]}",92,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,"~",13,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/fr b/etc/keymap/fr new file mode 100644 index 0000000..39444db --- /dev/null +++ b/etc/keymap/fr @@ -0,0 +1,29 @@ +#keytable fr +keytable "keyb-user" { + 0= + 0,27,"&{",34,39,"(-}_/@)=",127,9, + "azertyuiop^$",13,0,"qs", + "dfghjklm|",96,0,"*wxcv", + "bn,;:!",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"1234567890]+",127,9, + "AZERTYUIOP<>",13,0,"QS", + "DFGHJKLM%~",0,"#WXCV", + "BN?./",92,0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"~#{[|",96,92,"^@]}",0,0, + "@",0,0,0,0,0,0,0,0,0,0,"~",13,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230." +} diff --git a/etc/keymap/fr-latin1 b/etc/keymap/fr-latin1 new file mode 100644 index 0000000..5456304 --- /dev/null +++ b/etc/keymap/fr-latin1 @@ -0,0 +1,29 @@ +#keytable fr-latin1 +keytable "keyb-user" { + 0= + 0,27,"&",130,34,39,"(-",138,"_",135,133,")=",127,9, + "azertyuiop",dcircum,"$",13,0,"qs", + "dfghjklm",151,253,0,"*wxcv", + "bn,;:!",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"1234567890",248,"+",127,9, + "AZERTYUIOP",ddiares,156,13,0,"QS", + "DFGHJKLM%~",0,230,"WXCV", + "BN?./",167,0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"~#{[|",96,92,"^@]}",0,0, + "@",0,0,0,0,0,0,0,0,0,0,164,13,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230." +} diff --git a/etc/keymap/hr-cp852 b/etc/keymap/hr-cp852 new file mode 100644 index 0000000..9dd3722 --- /dev/null +++ b/etc/keymap/hr-cp852 @@ -0,0 +1,29 @@ +#keytable hr-cp852 +keytable "keyb-user" { + 0= + 0,27,"1234567890",39,"+",127,9, + "qwertzuiop",231,208,13,0,"as", + "dfghjkl",159,134,0,0,167,"yxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,"#$%&/()=?*",127,9, + "QWERTZUIOP",230,209,13,0,"AS", + "DFGHJKL",172,143,0,0,166,"YXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,"~",183,"^",244,248,242,96,250,239,241,249,247,0,0, + 92,"|",0,0,0,0,0,0,0,0,246,158,13,0,0,0, + 0,"[]",0,0,146,145,92,225,0,0,207,0,0,0,"@", + "{}",245,0,"|/",0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/hr-latin2 b/etc/keymap/hr-latin2 new file mode 100644 index 0000000..6bd887e --- /dev/null +++ b/etc/keymap/hr-latin2 @@ -0,0 +1,29 @@ +#keytable hr-latin2 +keytable "keyb-user" { + 0= + 0,27,"1234567890",39,"+",127,9, + "qwertzuiop",185,240,13,0,"as", + "dfghjkl",232,230,0,0,190,"yxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,"#$%&/()=?*",127,9, + "QWERTZUIOP",169,208,13,0,"AS", + "DFGHJKL",200,198,0,0,174,"YXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,"~",183,"^",162,176,178,96,255,180,189,168,184,0,0, + 92,"|",0,0,0,0,0,0,0,0,247,215,13,0,0,0, + 0,"[]",0,0,179,163,92,223,0,0,164,0,0,0,"@", + "{}",167,0,"|/",0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/hu b/etc/keymap/hu new file mode 100644 index 0000000..18e6ad7 --- /dev/null +++ b/etc/keymap/hu @@ -0,0 +1,29 @@ +#keytable hu +keytable "keyb-user" { + 0= + 0,27,"123456789",148,129,162,127,9, + "qwertzuiop",139,163,13,0,"as", + "dfghjkl",130,160,"0",0,251,"yxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,161,0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,39,34,"+!%/=()",153,154,39,127,9, + "QWERTZUIOP",138,233,13,0,"AS", + "DFGHJKL",144,181,21,"0",235,"YXCV", + "BNM?:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,146,0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,"~",dcaron,dcircum,dbreve,248,dogonek,dgrave,daboved,dacute,ddacute,ddiares,dcedilla,0,0, + 92,"|",0,0,0,0,0,146,0,0,246,158,13,0,0,208, + 209,"[]",0,161,136,157,"$",225,0,0,207,">#&@", + "{}",0,";",0,"*",0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/hu-cwi b/etc/keymap/hu-cwi new file mode 100644 index 0000000..9c3733e --- /dev/null +++ b/etc/keymap/hu-cwi @@ -0,0 +1,29 @@ +#keytable hu-cwi +keytable "keyb-user" { + 0= + 0,27,"123456789",148,129,162,127,9, + "qwertzuiop",147,163,13,0,"as", + "dfghjkl",130,160,"0",0,150,"yxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,161,0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,39,34,"+!%/=()",153,154,149,127,9, + "QWERTZUIOP",167,151,13,0,"AS", + "DFGHJKL",144,143,21,"0",152,"YXCV", + "BNM?:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,141,0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,"~",dcaron,dcircum,dbreve,248,dogonek,dgrave,daboved,dacute,ddacute,ddiares,dcedilla,0,0, + 92,"|",0,0,0,0,0,141,0,0,246,0,13,0,0,0, + 0,"[]",0,161,0,0,"$",225,0,0,0,">#&@", + "{}",0,";",0,"*",0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/hu-latin2 b/etc/keymap/hu-latin2 new file mode 100644 index 0000000..53b29b3 --- /dev/null +++ b/etc/keymap/hu-latin2 @@ -0,0 +1,29 @@ +#keytable hu-latin2 +keytable "keyb-user" { + 0= + 0,27,"123456789",246,252,243,127,9, + "qwertzuiop",245,250,13,0,"as", + "dfghjkl",233,225,"0",0,251,"yxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,237,0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,39,34,"+!%/=()",214,220,211,127,9, + "QWERTZUIOP",213,218,13,0,"AS", + "DFGHJKL",201,193,21,"0",219,"YXCV", + "BNM?:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,205,0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,"~",dcaron,dcircum,dbreve,248,dogonek,dgrave,daboved,dacute,ddacute,ddiares,dcedilla,0,0, + 92,"|",0,0,0,0,0,205,0,0,247,215,13,0,0,240, + 208,"[]",0,237,179,163,"$",223,0,0,164,">#&@", + "{}",0,";",0,"*",0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/it b/etc/keymap/it new file mode 100644 index 0000000..ffc9e30 --- /dev/null +++ b/etc/keymap/it @@ -0,0 +1,29 @@ +#keytable it +keytable "keyb-user" { + 0= + 0,27,"1234567890",39,141,127,9, + "qwertyuiop",138,"+",13,0,"as", + "dfghjkl",149,133,92,0,151,"zxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,156,"$%&/()=?^",127,9, + "QWERTYUIOP",130,"*",13,0,"AS", + "DFGHJKL",135,248,"|0",21,"ZXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@",0,"$",0,0,"{[]}{}",0,0, + 0,0,0,0,0,0,0,0,0,0,"[]",13,0,0,0, + 0,0,0,0,0,0,0,"@#",0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230." +} diff --git a/etc/keymap/jp106 b/etc/keymap/jp106 new file mode 100644 index 0000000..275c680 --- /dev/null +++ b/etc/keymap/jp106 @@ -0,0 +1,35 @@ +#keytable jp106 +keytable "keyb-user" { + 0= + 0,27,"1234567890-^",127,9, + "qwertyuiop@[",13,0,"as", + "dfghjkl;:",96,0,"]zxcv", + "bnm,./",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"0",0,0,92,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,92,0,0,0,0,0,0,0,0,0,92,0,0, + 0 + shift 0= + 0,27,"!",34,"#$%&",39,"()~=~",127,9, + "QWERTYUIOP",96,"{",dgrave,0,"AS", + "DFGHJKL+*~0}ZXCV", + "BNM<>?",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"0",0,0,"_",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,"_",0,0,0,0,0,0,0,0,0,"|",0,0, + 0 + alt 0= + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230." +} diff --git a/etc/keymap/keyb-no b/etc/keymap/keyb-no new file mode 100644 index 0000000..dc81b5b --- /dev/null +++ b/etc/keymap/keyb-no @@ -0,0 +1,29 @@ +#keytable keyb-no +keytable "keyb-user" { + 0= + 0,27,"1234567890+",92,127,9, + "qwertyuiop}~",13,0,"as", + "dfghjkl|{|",0,39,"zxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,"#$%&/()=?",96,127,9, + "QWERTYUIOP]^",13,0,"AS", + "DFGHJKL",92,"[",0,0,"*ZXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@",0,"$",0,0,"{[]}",0,39,0,0, + 0,0,0,0,0,0,0,0,0,0,0,"~",13,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/no-latin1 b/etc/keymap/no-latin1 new file mode 100644 index 0000000..719f74f --- /dev/null +++ b/etc/keymap/no-latin1 @@ -0,0 +1,29 @@ +#keytable no-latin1 +keytable "keyb-user" { + 0= + 0,27,"1234567890+",92,127,9, + "qwertyuiop",134,ddiares,13,0,"as", + "dfghjkl",155,145,"|",0,39,"zxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,"#$%&/()=?",dgrave,127,9, + "QWERTYUIOP",143,dcircum,13,0,"AS", + "DFGHJKL",157,146,245,0,"*ZXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@",156,207,0,0,"{[]}",0,dacute,0,0, + 0,0,0,0,0,0,0,0,0,0,0,dtilde,13,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/pl b/etc/keymap/pl new file mode 100644 index 0000000..b3c5793 --- /dev/null +++ b/etc/keymap/pl @@ -0,0 +1,29 @@ +#keytable pl +keytable "keyb-user" { + 0= + 0,27,"1234567890+",39,127,9, + "qwertzuiop",190,152,13,0,"as", + "dfghjkl",136,165,daboved,0,162,"yxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,"#$%&/()=?*",127,9, + "QWERTZUIOP",228,134,13,0,"AS", + "DFGHJKL",157,169,dogonek,"0",171,"YXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,"~",dcaron,dcircum,dbreve,248,dogonek,dgrave,daboved,dacute,ddacute,ddiares,dcedilla,0,0, + 92,"|",169,0,0,0,0,0,162,0,246,158,0,0,165,208, + 209,"[]",0,0,0,136,"$",225,0,0,dcaron,190,171,134,"@", + "{}",21,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/po b/etc/keymap/po new file mode 100644 index 0000000..46e22a9 --- /dev/null +++ b/etc/keymap/po @@ -0,0 +1,29 @@ +#keytable po +keytable "keyb-user" { + 0= + 0,27,"1234567890",96,174,127,9, + "qwertyuiop+",dacute,13,0,"as", + "dfghjkl",135,167,92,0,dtilde,"zxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,"#$%&/()=?",175,127,9, + "QWERTYUIOP*",dgrave,13,0,"AS", + "DFGHJKL",128,166,"|0",dcircum,"ZXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@",156,21,0,0,"{[]}",0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,ddiares,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230." +} diff --git a/etc/keymap/sf b/etc/keymap/sf new file mode 100644 index 0000000..0861ca7 --- /dev/null +++ b/etc/keymap/sf @@ -0,0 +1,29 @@ +#keytable sf +keytable "keyb-user" { + 0= + 0,27,"1234567890",39,"^",127,9, + "qwertzuiop",0,0,13,0,"as", + "dfghjkl",0,0,0,0,"$yxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"+",34,"*",0,"%&/()=?",96,127,9, + "QWERTZUIOP",0,"!",13,0,"AS", + "DFGHJKL",0,0,0,0,0,"YXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@#",0,0,0,"|",0,0,0,39,"~",0,0, + 0,0,0,0,0,0,0,0,0,0,"[]",13,0,0,0, + 0,0,0,0,0,0,0,0,"{",0,0,"}",0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,92,0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/sf-latin1 b/etc/keymap/sf-latin1 new file mode 100644 index 0000000..4cd1506 --- /dev/null +++ b/etc/keymap/sf-latin1 @@ -0,0 +1,29 @@ +#keytable sf-latin1 +keytable "keyb-user" { + 0= + 0,27,"1234567890",39,"^",127,9, + "qwertzuiop",232,168,13,0,"as", + "dfghjkl",233,224,167,0,"$yxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"+",34,"*",231,"%&/()=?",96,127,9, + "QWERTZUIOP",252,"!",13,0,"AS", + "DFGHJKL",246,228,176,0,163,"YXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@#",0,0,172,"|",162,0,0,180,"~",0,0, + 0,0,0,0,0,0,0,0,0,0,"[]",13,0,0,0, + 0,0,0,0,0,0,0,0,"{",0,0,"}",0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,92,0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/sg b/etc/keymap/sg new file mode 100644 index 0000000..7fc122e --- /dev/null +++ b/etc/keymap/sg @@ -0,0 +1,29 @@ +#keytable sg +keytable "keyb-user" { + 0= + 0,27,"1234567890",39,"^",127,9, + "qwertzuiop",0,0,13,0,"as", + "dfghjkl",0,0,0,0,"$yxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"+",34,"*",0,"%&/()=?",96,127,9, + "QWERTZUIOP",0,"!",13,0,"AS", + "DFGHJKL",0,0,0,0,0,"YXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@#",0,0,0,"|",0,0,0,39,"~",0,0, + 0,0,0,0,0,0,0,0,0,0,"[]",13,0,0,0, + 0,0,0,0,0,0,0,0,"{",0,0,"}",0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,92,0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/sg-latin1 b/etc/keymap/sg-latin1 new file mode 100644 index 0000000..caf29e2 --- /dev/null +++ b/etc/keymap/sg-latin1 @@ -0,0 +1,29 @@ +#keytable sg-latin1 +keytable "keyb-user" { + 0= + 0,27,"1234567890",39,"^",127,9, + "qwertzuiop",192,0,13,0,"as", + "dfghjkl",148,132,167,0,"$yxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"+",34,"*",128,"%&/()=?",96,127,9, + "QWERTZUIOP",154,"!",13,0,"AS", + "DFGHJKL",153,142,176,0,163,"YXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,179,"@#",0,0,170,"|",162,0,0,39,"~",0,0, + 0,0,0,0,0,0,0,0,0,0,"[]",13,0,0,0, + 0,0,0,0,0,0,0,233,"{",0,0,"}",0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,92,0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230," +} diff --git a/etc/keymap/sw b/etc/keymap/sw new file mode 100644 index 0000000..82d81dc --- /dev/null +++ b/etc/keymap/sw @@ -0,0 +1,29 @@ +#keytable sw +keytable "keyb-user" { + 0= + 0,27,"1234567890+",39,127,9, + "qwertyuiop",134,"~",13,0,"as", + "dfghjkl",148,132,21,0,39,"zxcv", + "bnm,.-",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 171,0,"!",34,"#$%&/()=?",96,127,9, + "QWERTYUIOP",143,"^",13,0,"AS", + "DFGHJKL",153,142,171,0,"*ZXCV", + "BNM;:_",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@",156,"$",0,0,"{[]}",92,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,"~",13,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230." +} diff --git a/etc/keymap/tr b/etc/keymap/tr new file mode 100644 index 0000000..7fe908f --- /dev/null +++ b/etc/keymap/tr @@ -0,0 +1,29 @@ +#keytable tr +keytable "keyb-user" { + 0= + 0,27,"1234567890*-",127,9, + "qwertyu",141,"op",167,129,13,0,"as", + "dfghjkl",159,"i",130,0,44,"zxcv", + "bnm",148,135,".",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!'^+%&/()=?_",127,9, + "QWERTYUIOP",166,154,13,0,"AS", + "DFGHJKL",158,152,34,0,";ZXCV", + "BNM",153,128,":",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,0,"#$",0,0,"{[]}\",0,0,0, + "@",0,0,0,0,0,0,0,0,0,0,"~",0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,"`",0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230." +} diff --git a/etc/keymap/uk b/etc/keymap/uk new file mode 100644 index 0000000..ad24dbc --- /dev/null +++ b/etc/keymap/uk @@ -0,0 +1,29 @@ +#keytable uk +keytable "keyb-user" { + 0= + 0,27,"1234567890-=",127,9, + "qwertyuiop[]",13,0,"as", + "dfghjkl;",39,96,0,"#zxcv", + "bnm,./",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,92,0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!",34,156,"$%^&*()_+",127,9, + "QWERTYUIOP{}",13,0,"AS", + "DFGHJKL:@~0~ZXCV", + "BNM<>?",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,"@",0,"$",0,0,"{[]}",92,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,"~",13,0,0,0, + 0,0,0,0,0,0,0,0,dacute,dgrave,0,dtilde,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,"|",0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230." +} diff --git a/etc/keymap/us b/etc/keymap/us new file mode 100644 index 0000000..e1996dd --- /dev/null +++ b/etc/keymap/us @@ -0,0 +1,29 @@ +#keytable us +keytable "keyb-user" { + 0= + 0,27,"1234567890-=",127,9, + "qwertyuiop[]",13,0,"as", + "dfghjkl;",39,96,0,92,"zxcv", + "bnm,./",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,"<",0,0,0,0,0,0,0,0,0, + 0 + shift 0= + 0,27,"!@#$%^&*()_+",127,9, + "QWERTYUIOP{}",13,0,"AS", + "DFGHJKL:",34,"~0|ZXCV", + "BNM<>?",0,"*",0," ",0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,"-",0,0,0,"+",0, + 0,0,0,0,0,0,">",0,0,0,0,0,0,0,0,0, + 0 + alt 0= + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0 + numpad 0= + "789-456+1230." +} diff --git a/etc/locales.conf b/etc/locales.conf new file mode 100644 index 0000000..c8c3bc2 --- /dev/null +++ b/etc/locales.conf @@ -0,0 +1,75 @@ +{ + /* add your mappings below and send a patch */ + "locales": [ + { "lang": "C", "codepage": "cp437", "country": 001 }, + { "lang": "en_US", "codepage": "cp437", "country": 001 }, + { "lang": "cs_CZ", "codepage": "cp852", "country": 042 }, + { "lang": "af_ZA", "codepage": "cp858", "country": 785 }, + { "lang": "ca_ES", "codepage": "cp858", "country": 034 }, + { "lang": "da_DK", "codepage": "cp858", "country": 045 }, + { "lang": "de_AT", "codepage": "cp858", "country": 043 }, + { "lang": "de_CH", "codepage": "cp858", "country": 041 }, + { "lang": "de_DE", "codepage": "cp858", "country": 049 }, + { "lang": "de_LI", "codepage": "cp858" }, + { "lang": "de_LU", "codepage": "cp858" }, + { "lang": "en_AU", "codepage": "cp858", "country": 061 }, + { "lang": "en_BZ", "codepage": "cp858" }, + { "lang": "en_CA", "codepage": "cp858", "country": 004 }, + { "lang": "en_GB", "codepage": "cp858", "country": 044 }, + { "lang": "en_IE", "codepage": "cp858", "country": 353 }, + { "lang": "en_JM", "codepage": "cp858" }, + { "lang": "en_NZ", "codepage": "cp858", "country": 064 }, + { "lang": "en_PH", "codepage": "cp858" }, + { "lang": "en_TT", "codepage": "cp858" }, + { "lang": "en_ZA", "codepage": "cp858", "country": 027 }, + { "lang": "es_AR", "codepage": "cp858", "country": 054 }, + { "lang": "es_BO", "codepage": "cp858" }, + { "lang": "es_CL", "codepage": "cp858", "country": 056 }, + { "lang": "es_CO", "codepage": "cp858", "country": 057 }, + { "lang": "es_CR", "codepage": "cp858" }, + { "lang": "es_DO", "codepage": "cp858" }, + { "lang": "es_EC", "codepage": "cp858", "country": 593 }, + { "lang": "es_ES", "codepage": "cp858", "country": 034 }, + { "lang": "es_GT", "codepage": "cp858" }, + { "lang": "es_HN", "codepage": "cp858" }, + { "lang": "es_MX", "codepage": "cp858", "country": 052 }, + { "lang": "es_NI", "codepage": "cp858" }, + { "lang": "es_PA", "codepage": "cp858" }, + { "lang": "es_PE", "codepage": "cp858" }, + { "lang": "es_PR", "codepage": "cp858" }, + { "lang": "es_PY", "codepage": "cp858" }, + { "lang": "es_SV", "codepage": "cp858" }, + { "lang": "es_UY", "codepage": "cp858" }, + { "lang": "es_VE", "codepage": "cp858", "country": 058 }, + { "lang": "eu_ES", "codepage": "cp858", "country": 034 }, + { "lang": "fi_FI", "codepage": "cp858", "country": 358 }, + { "lang": "fo_DK", "codepage": "cp858", "country": 045 }, + { "lang": "fo_FO", "codepage": "cp858" }, + { "lang": "fr_BE", "codepage": "cp858", "country": 032 }, + { "lang": "fr_CA", "codepage": "cp858", "country": 002 }, + { "lang": "fr_CH", "codepage": "cp858", "country": 041 }, + { "lang": "fr_FR", "codepage": "cp858", "country": 033 }, + { "lang": "fr_LU", "codepage": "cp858" }, + { "lang": "id_ID", "codepage": "cp858" }, + { "lang": "is_IS", "codepage": "cp858", "country": 354 }, + { "lang": "it_CH", "codepage": "cp858", "country": 041 }, + { "lang": "it_IT", "codepage": "cp858", "country": 039 }, + { "lang": "ms_BN", "codepage": "cp858" }, + { "lang": "ms_MY", "codepage": "cp858", "country": 060 }, + { "lang": "nb_NO", "codepage": "cp858", "country": 047 }, + { "lang": "nl_BE", "codepage": "cp858", "country": 032 }, + { "lang": "nl_NL", "codepage": "cp858", "country": 031 }, + { "lang": "nn_NO", "codepage": "cp858", "country": 047 }, + { "lang": "no_NO", "codepage": "cp858", "country": 047 }, + { "lang": "pl_PL", "codepage": "cp852", "country": 048 }, + { "lang": "pt_BR", "codepage": "cp858", "country": 055 }, + { "lang": "pt_PT", "codepage": "cp858", "country": 351 }, + { "lang": "sv_FI", "codepage": "cp858", "country": 358 }, + { "lang": "sv_SE", "codepage": "cp858", "country": 046 }, + { "lang": "sw_CD", "codepage": "cp858" }, + { "lang": "sw_KE", "codepage": "cp858" }, + { "lang": "sw_TZ", "codepage": "cp858" }, + { "lang": "sw_UG", "codepage": "cp858" }, + { "lang": "ru_RU", "codepage": "cp866", "country": 007 }, + ] +} diff --git a/etc/ttf/Flexi_IBM_VGA_False.ttf b/etc/ttf/Flexi_IBM_VGA_False.ttf new file mode 100644 index 0000000..58044df Binary files /dev/null and b/etc/ttf/Flexi_IBM_VGA_False.ttf differ diff --git a/etc/ttf/Flexi_IBM_VGA_True.ttf b/etc/ttf/Flexi_IBM_VGA_True.ttf new file mode 100644 index 0000000..8019224 Binary files /dev/null and b/etc/ttf/Flexi_IBM_VGA_True.ttf differ diff --git a/etc/ttf/README.oldschool b/etc/ttf/README.oldschool new file mode 100644 index 0000000..41d5abb --- /dev/null +++ b/etc/ttf/README.oldschool @@ -0,0 +1,74 @@ + + _ ─ --- ─ _ ─ --- ─ + .∙" ,┬, ~=╥_ .∙. .∙" ~=╥_ + . . .OZZZO╕ ^g, . .■°~°■. ^%╖ .s%ZO┐ + ` └ªqZpª' °∙ `Z, ` _.,· `gZ┐ ∙:ZZ| + _┬g%%oc, ~ _., ` ╘Z; _.,┬⌐y%Z=-:· .s%Z%L, `OZZYi%g┬_ + jOZZÿÿZZOL· jOZZÿ¬, `Zb _.┬⌐y%ZZZZZZZZ%=:∙ jZZZZZZZb `ZZZZÿZZZb + ∙ZZ?~ ~\ZZZ| |¢ZZZZO\ ·?Z.∙%ÿZZZZZZª*╩ⁿ^°"`' /ZZª"~"ªZZ\ ∙T ~!ZZ┤ + Z6f ∙ZZZO∙·|ZZZ^ZZi ]Z1·:-":ZZZ' _. ∙OZ/ \ZZL : |6Z' + . `ZZ¬._ ~"^Z| ∙ZZZ;\ZZ ╞Z╡ ·¢ZZZ· _┬%ZZ· l%! .oZo, ]ZZ·∙ _╥ZZ' . + , ~^╩*╩^~.Z¢··ZZZl ZZ┬ZZ1 · ∙ZZZZ∙ ^ª!ZZ∙ :=l dZZZb ∙ZZ| ~^"~ + ∙ g%ZZZ··ZZZZ `¥ZZZ° : ·ZZZZ: ∙lZZ| ∙;∙ :ZZZF ·ZZ| .' + ~─_ ·ZZZZ| ∙ZZZZ └ZZÿ' ∙ ?ZZZ! :ZZ│· ·∙ "ª" dZg· _─~ + ~"^ :ZZZO∙∙|ZZZ! `^' ' `¥ZZZ ·ªZZ: · ./ZZf ·─--`~ + OZZZ∙ jZª╩~ `. _ _. . ~^ªL, YZZZZOzz┬┬ ·∙:=CO/ + -V! jª╩^~ . ~-║∙-~ . `^╩ªZ¥ÿZF'·∙:%CG' . O R G + ~─_ T _─~ ~' '^"~ + ~"═=|=═"~ + ∙ p r e s e n t s + · + + + FLEXI IBM VGA FONTS / v2.0 + + + Scalable TrueType fonts based on the iconic hardware VGA character set + + ______________________________________________________________________ + + + +INCLUDED FONTS: + +* Flexi IBM VGA True: Corrected aspect ratio, extended character set +* Flexi IBM VGA True 437: Corrected aspect ratio, CP437/DOS encoding +* Flexi IBM VGA False: Uncorrected aspect ratio, extended character set +* Flexi IBM VGA False 437: Uncorrected aspect ratio, CP437/DOS encoding + + +ASPECT RATIO: + +* The 'True' versions have been aspect-corrected to match the appearance of a + good old 4:3 VGA monitor. The default VGA text mode has a resolution of + 720x400, and at 4:3 this produces pixels that are far from square: + (400/3)/(720/4) = 0.740740.... For simplicity's sake I rounded that to 0.75, + meaning that the character cell is really 3/4 the width of an uncorrected, + square-pixel representation. + +* The 'False' versions do stick to the square-pixel assumption. Which isn't + true to the original look, but some may like that appearance or even be used + to it. + + +SUPPORTED SCRIPTS: + +* The unmarked variants (extended character sets) cover a wide selection of + characters, mostly based on various code-pages from DOS (including Greek, + Hebrew, Cyrillic and many Latin scripts, box/block drawing symbols, math and + so on). + +* The "437" variants contain only the characters from the original IBM PC + codepage CP437 ("US-Latin"), and are detected by Windows as 'OEM/DOS'. + Suitable for your favorite .NFO viewer or any other situation where you need + this character set. + + +LICENSE: + + These fonts are released under the Creative Commons Attribution-ShareAlike + 4.0 International license: http://creativecommons.org/licenses/by-sa/4.0/ + +_________________________________________ + +// VileR 2020-11 https://int10.org diff --git a/etc/vga-cp866.bdf b/etc/vga-cp866.bdf new file mode 100644 index 0000000..99df2f9 --- /dev/null +++ b/etc/vga-cp866.bdf @@ -0,0 +1,5914 @@ +STARTFONT 2.1 +FONT -dosemu-VGA-Medium-R-Normal--17-160-75-75-C-80-IBM-CP866 +SIZE 16 75 75 +FONTBOUNDINGBOX 8 16 0 -4 +STARTPROPERTIES 18 +FONT_DESCENT 4 +FONT_ASCENT 12 +DEFAULT_CHAR 0 +FOUNDRY "dosemu" +FAMILY_NAME "VGA" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "" +PIXEL_SIZE 17 +POINT_SIZE 160 +RESOLUTION_X 75 +RESOLUTION_Y 75 +SPACING "C" +AVERAGE_WIDTH 80 +CHARSET_REGISTRY "IBM" +CHARSET_ENCODING "CP866" +_XMBDFED_INFO "Edited with xmbdfed 4.5." +ENDPROPERTIES +CHARS 256 +STARTCHAR C000 +ENCODING 0 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C001 +ENCODING 1 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7E +81 +A5 +81 +81 +BD +99 +81 +81 +7E +00 +00 +00 +00 +ENDCHAR +STARTCHAR C002 +ENCODING 2 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7E +FF +DB +FF +FF +C3 +E7 +FF +FF +7E +00 +00 +00 +00 +ENDCHAR +STARTCHAR C003 +ENCODING 3 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +6C +FE +FE +FE +FE +7C +38 +10 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C004 +ENCODING 4 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +10 +38 +7C +FE +7C +38 +10 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C005 +ENCODING 5 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +18 +3C +3C +E7 +E7 +E7 +18 +18 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C006 +ENCODING 6 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +18 +3C +7E +FF +FF +7E +18 +18 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C007 +ENCODING 7 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +18 +3C +3C +18 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C008 +ENCODING 8 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +FF +FF +FF +FF +FF +FF +E7 +C3 +C3 +E7 +FF +FF +FF +FF +FF +FF +ENDCHAR +STARTCHAR C009 +ENCODING 9 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +3C +66 +42 +42 +66 +3C +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a +ENCODING 10 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +FF +FF +FF +FF +FF +C3 +99 +BD +BD +99 +C3 +FF +FF +FF +FF +FF +ENDCHAR +STARTCHAR C00b +ENCODING 11 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +1E +0E +1A +32 +78 +CC +CC +CC +CC +78 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00c +ENCODING 12 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3C +66 +66 +66 +66 +3C +18 +7E +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00d +ENCODING 13 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3F +33 +3F +30 +30 +30 +30 +70 +F0 +E0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e +ENCODING 14 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7F +63 +7F +63 +63 +63 +63 +67 +E7 +E6 +C0 +00 +00 +00 +ENDCHAR +STARTCHAR C00f +ENCODING 15 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +18 +18 +DB +3C +E7 +3C +DB +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0010 +ENCODING 16 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +80 +C0 +E0 +F0 +F8 +FC +F8 +F0 +E0 +C0 +80 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0011 +ENCODING 17 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +02 +06 +0E +1E +3E +7E +3E +1E +0E +06 +02 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0012 +ENCODING 18 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +3C +7E +18 +18 +18 +7E +3C +18 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0013 +ENCODING 19 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +66 +66 +66 +66 +66 +66 +66 +00 +66 +66 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0014 +ENCODING 20 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7F +DB +DB +DB +7B +1B +1B +1B +1B +1B +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0015 +ENCODING 21 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +7C +C6 +60 +38 +6C +C6 +C6 +6C +38 +0C +C6 +7C +00 +00 +00 +ENDCHAR +STARTCHAR C0016 +ENCODING 22 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +FE +FE +FE +FE +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0017 +ENCODING 23 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +3C +7E +18 +18 +18 +7E +3C +18 +7E +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0018 +ENCODING 24 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +3C +7E +18 +18 +18 +18 +18 +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0019 +ENCODING 25 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +18 +18 +18 +18 +18 +18 +7E +3C +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C001a +ENCODING 26 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +18 +0C +FE +0C +18 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C001b +ENCODING 27 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +30 +60 +FE +60 +30 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C001c +ENCODING 28 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +C0 +C0 +C0 +FE +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C001d +ENCODING 29 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +28 +6C +FE +6C +28 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C001e +ENCODING 30 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +10 +38 +38 +7C +7C +FE +FE +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C001f +ENCODING 31 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +FE +FE +7C +7C +38 +38 +10 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0020 +ENCODING 32 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0021 +ENCODING 33 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +3C +3C +3C +18 +18 +18 +00 +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0022 +ENCODING 34 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +66 +66 +66 +24 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0023 +ENCODING 35 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +6C +6C +FE +6C +6C +6C +FE +6C +6C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0024 +ENCODING 36 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +7C +C6 +C2 +C0 +7C +06 +06 +86 +C6 +7C +18 +18 +00 +00 +ENDCHAR +STARTCHAR C0025 +ENCODING 37 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +C2 +C6 +0C +18 +30 +60 +C6 +86 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0026 +ENCODING 38 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +38 +6C +6C +38 +76 +DC +CC +CC +CC +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0027 +ENCODING 39 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +30 +30 +30 +60 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0028 +ENCODING 40 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +0C +18 +30 +30 +30 +30 +30 +30 +18 +0C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0029 +ENCODING 41 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +30 +18 +0C +0C +0C +0C +0C +0C +18 +30 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C002a +ENCODING 42 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +66 +3C +FF +3C +66 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C002b +ENCODING 43 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +18 +18 +7E +18 +18 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C002c +ENCODING 44 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +18 +18 +18 +30 +00 +00 +00 +ENDCHAR +STARTCHAR C002d +ENCODING 45 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +FE +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C002e +ENCODING 46 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C002f +ENCODING 47 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +02 +06 +0C +18 +30 +60 +C0 +80 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0030 +ENCODING 48 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +38 +6C +C6 +C6 +D6 +D6 +C6 +C6 +6C +38 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0031 +ENCODING 49 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +38 +78 +18 +18 +18 +18 +18 +18 +7E +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0032 +ENCODING 50 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7C +C6 +06 +0C +18 +30 +60 +C0 +C6 +FE +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0033 +ENCODING 51 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7C +C6 +06 +06 +3C +06 +06 +06 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0034 +ENCODING 52 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +0C +1C +3C +6C +CC +FE +0C +0C +0C +1E +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0035 +ENCODING 53 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FE +C0 +C0 +C0 +FC +06 +06 +06 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0036 +ENCODING 54 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +38 +60 +C0 +C0 +FC +C6 +C6 +C6 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0037 +ENCODING 55 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FE +C6 +06 +06 +0C +18 +30 +30 +30 +30 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0038 +ENCODING 56 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7C +C6 +C6 +C6 +7C +C6 +C6 +C6 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0039 +ENCODING 57 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7C +C6 +C6 +C6 +7E +06 +06 +06 +0C +78 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C003a +ENCODING 58 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +18 +18 +00 +00 +00 +18 +18 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C003b +ENCODING 59 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +18 +18 +00 +00 +00 +18 +18 +30 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C003c +ENCODING 60 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +06 +0C +18 +30 +60 +30 +18 +0C +06 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C003d +ENCODING 61 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7E +00 +00 +7E +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C003e +ENCODING 62 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +60 +30 +18 +0C +06 +0C +18 +30 +60 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C003f +ENCODING 63 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7C +C6 +C6 +0C +18 +18 +18 +00 +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0040 +ENCODING 64 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +7C +C6 +C6 +DE +DE +DE +DC +C0 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0041 +ENCODING 65 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +10 +38 +6C +C6 +C6 +FE +C6 +C6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0042 +ENCODING 66 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FC +66 +66 +66 +7C +66 +66 +66 +66 +FC +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0043 +ENCODING 67 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3C +66 +C2 +C0 +C0 +C0 +C0 +C2 +66 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0044 +ENCODING 68 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +F8 +6C +66 +66 +66 +66 +66 +66 +6C +F8 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0045 +ENCODING 69 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FE +66 +62 +68 +78 +68 +60 +62 +66 +FE +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0046 +ENCODING 70 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FE +66 +62 +68 +78 +68 +60 +60 +60 +F0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0047 +ENCODING 71 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3C +66 +C2 +C0 +C0 +DE +C6 +C6 +66 +3A +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0048 +ENCODING 72 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C6 +C6 +C6 +C6 +FE +C6 +C6 +C6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0049 +ENCODING 73 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3C +18 +18 +18 +18 +18 +18 +18 +18 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C004a +ENCODING 74 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +1E +0C +0C +0C +0C +0C +CC +CC +CC +78 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C004b +ENCODING 75 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +E6 +66 +66 +6C +78 +78 +6C +66 +66 +E6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C004c +ENCODING 76 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +F0 +60 +60 +60 +60 +60 +60 +62 +66 +FE +00 +00 +00 +00 +ENDCHAR +STARTCHAR C004d +ENCODING 77 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C6 +EE +FE +FE +D6 +C6 +C6 +C6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C004e +ENCODING 78 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C6 +E6 +F6 +FE +DE +CE +C6 +C6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C004f +ENCODING 79 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0050 +ENCODING 80 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FC +66 +66 +66 +7C +60 +60 +60 +60 +F0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0051 +ENCODING 81 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +D6 +DE +7C +0C +0E +00 +00 +ENDCHAR +STARTCHAR C0052 +ENCODING 82 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FC +66 +66 +66 +7C +6C +66 +66 +66 +E6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0053 +ENCODING 83 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7C +C6 +C6 +60 +38 +0C +06 +C6 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0054 +ENCODING 84 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7E +7E +5A +18 +18 +18 +18 +18 +18 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0055 +ENCODING 85 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0056 +ENCODING 86 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +6C +38 +10 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0057 +ENCODING 87 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C6 +C6 +C6 +C6 +D6 +D6 +D6 +FE +EE +6C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0058 +ENCODING 88 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C6 +C6 +6C +7C +38 +38 +7C +6C +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0059 +ENCODING 89 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +66 +66 +66 +66 +3C +18 +18 +18 +18 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C005a +ENCODING 90 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FE +C6 +86 +0C +18 +30 +60 +C2 +C6 +FE +00 +00 +00 +00 +ENDCHAR +STARTCHAR C005b +ENCODING 91 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3C +30 +30 +30 +30 +30 +30 +30 +30 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C005c +ENCODING 92 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +80 +C0 +E0 +70 +38 +1C +0E +06 +02 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C005d +ENCODING 93 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3C +0C +0C +0C +0C +0C +0C +0C +0C +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C005e +ENCODING 94 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +10 +38 +6C +C6 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C005f +ENCODING 95 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +FF +00 +00 +ENDCHAR +STARTCHAR C0060 +ENCODING 96 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +30 +30 +18 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0061 +ENCODING 97 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +78 +0C +7C +CC +CC +CC +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0062 +ENCODING 98 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +E0 +60 +60 +78 +6C +66 +66 +66 +66 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0063 +ENCODING 99 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7C +C6 +C0 +C0 +C0 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0064 +ENCODING 100 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +1C +0C +0C +3C +6C +CC +CC +CC +CC +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0065 +ENCODING 101 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7C +C6 +FE +C0 +C0 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0066 +ENCODING 102 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +38 +6C +64 +60 +F0 +60 +60 +60 +60 +F0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0067 +ENCODING 103 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +76 +CC +CC +CC +CC +7C +0C +0C +CC +78 +00 +ENDCHAR +STARTCHAR C0068 +ENCODING 104 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +E0 +60 +60 +6C +76 +66 +66 +66 +66 +E6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0069 +ENCODING 105 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +18 +00 +38 +18 +18 +18 +18 +18 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C006a +ENCODING 106 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +06 +06 +00 +0E +06 +06 +06 +06 +06 +06 +66 +66 +3C +00 +ENDCHAR +STARTCHAR C006b +ENCODING 107 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +E0 +60 +60 +66 +6C +78 +78 +6C +66 +E6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C006c +ENCODING 108 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +38 +18 +18 +18 +18 +18 +18 +18 +18 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C006d +ENCODING 109 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +EC +FE +D6 +D6 +D6 +D6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C006e +ENCODING 110 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +DC +66 +66 +66 +66 +66 +66 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C006f +ENCODING 111 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7C +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0070 +ENCODING 112 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +DC +66 +66 +66 +66 +66 +7C +60 +60 +F0 +00 +ENDCHAR +STARTCHAR C0071 +ENCODING 113 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +76 +CC +CC +CC +CC +CC +7C +0C +0C +1E +00 +ENDCHAR +STARTCHAR C0072 +ENCODING 114 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +DC +76 +66 +60 +60 +60 +F0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0073 +ENCODING 115 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7C +C6 +60 +38 +0C +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0074 +ENCODING 116 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +10 +30 +30 +FC +30 +30 +30 +30 +36 +1C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0075 +ENCODING 117 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +CC +CC +CC +CC +CC +CC +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0076 +ENCODING 118 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +66 +66 +66 +66 +66 +3C +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0077 +ENCODING 119 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +C6 +C6 +D6 +D6 +D6 +FE +6C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0078 +ENCODING 120 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +C6 +6C +38 +38 +38 +6C +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0079 +ENCODING 121 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +C6 +C6 +C6 +C6 +C6 +7E +06 +06 +C6 +7C +00 +ENDCHAR +STARTCHAR C007a +ENCODING 122 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +FE +CC +18 +30 +60 +C6 +FE +00 +00 +00 +00 +ENDCHAR +STARTCHAR C007b +ENCODING 123 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +0E +18 +18 +18 +70 +18 +18 +18 +18 +0E +00 +00 +00 +00 +ENDCHAR +STARTCHAR C007c +ENCODING 124 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +18 +18 +18 +00 +18 +18 +18 +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C007d +ENCODING 125 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +70 +18 +18 +18 +0E +18 +18 +18 +18 +70 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C007e +ENCODING 126 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +76 +DC +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C007f +ENCODING 127 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +10 +38 +6C +C6 +C6 +C6 +FE +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0080 +ENCODING 128 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +1E +36 +66 +C6 +C6 +FE +C6 +C6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0081 +ENCODING 129 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FE +62 +62 +60 +7C +66 +66 +66 +66 +FC +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0082 +ENCODING 130 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FC +66 +66 +66 +7C +66 +66 +66 +66 +FC +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0083 +ENCODING 131 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FE +62 +62 +60 +60 +60 +60 +60 +60 +F0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0084 +ENCODING 132 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +1E +36 +66 +66 +66 +66 +66 +66 +66 +FF +C3 +81 +00 +00 +ENDCHAR +STARTCHAR C0085 +ENCODING 133 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FE +66 +62 +68 +78 +68 +60 +62 +66 +FE +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0086 +ENCODING 134 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +D6 +D6 +54 +54 +7C +7C +54 +D6 +D6 +D6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0087 +ENCODING 135 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7C +C6 +06 +06 +3C +06 +06 +06 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0088 +ENCODING 136 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C6 +C6 +CE +CE +D6 +E6 +E6 +C6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0089 +ENCODING 137 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +38 +38 +C6 +C6 +CE +CE +D6 +E6 +E6 +C6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C008a +ENCODING 138 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +E6 +66 +6C +6C +78 +78 +6C +6C +66 +E6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C008b +ENCODING 139 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +1E +36 +66 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C008c +ENCODING 140 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C6 +EE +FE +FE +D6 +C6 +C6 +C6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C008d +ENCODING 141 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C6 +C6 +C6 +C6 +FE +C6 +C6 +C6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C008e +ENCODING 142 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C008f +ENCODING 143 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FE +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0090 +ENCODING 144 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +FC +66 +66 +66 +7C +60 +60 +60 +60 +F0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0091 +ENCODING 145 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3C +66 +C2 +C0 +C0 +C0 +C0 +C2 +66 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0092 +ENCODING 146 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7E +5A +18 +18 +18 +18 +18 +18 +18 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0093 +ENCODING 147 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C6 +C6 +C6 +C6 +C6 +7E +06 +06 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0094 +ENCODING 148 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +3C +18 +7E +DB +DB +DB +DB +DB +7E +18 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0095 +ENCODING 149 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C6 +C6 +6C +7C +38 +38 +7C +6C +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0096 +ENCODING 150 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +CC +CC +CC +CC +CC +CC +CC +CC +CC +FE +06 +06 +00 +00 +ENDCHAR +STARTCHAR C0097 +ENCODING 151 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C6 +C6 +C6 +C6 +C6 +7E +06 +06 +06 +06 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0098 +ENCODING 152 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +DB +DB +DB +DB +DB +DB +DB +DB +DB +FF +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0099 +ENCODING 153 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +DB +DB +DB +DB +DB +DB +DB +DB +DB +FF +03 +03 +00 +00 +ENDCHAR +STARTCHAR C009a +ENCODING 154 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +F8 +B0 +30 +30 +3C +36 +36 +36 +36 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C009b +ENCODING 155 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +C3 +C3 +C3 +C3 +F3 +DB +DB +DB +DB +F3 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C009c +ENCODING 156 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +F0 +60 +60 +60 +7C +66 +66 +66 +66 +FC +00 +00 +00 +00 +ENDCHAR +STARTCHAR C009d +ENCODING 157 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7C +C6 +06 +26 +3E +26 +06 +06 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C009e +ENCODING 158 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +CE +DB +DB +DB +FB +DB +DB +DB +DB +CE +00 +00 +00 +00 +ENDCHAR +STARTCHAR C009f +ENCODING 159 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3F +66 +66 +66 +3E +3E +66 +66 +66 +E7 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a0 +ENCODING 160 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +78 +0C +7C +CC +CC +CC +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a1 +ENCODING 161 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +02 +06 +3C +60 +60 +7C +66 +66 +66 +66 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a2 +ENCODING 162 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +FC +66 +66 +7C +66 +66 +FC +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a3 +ENCODING 163 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7E +32 +32 +30 +30 +30 +78 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a4 +ENCODING 164 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +1E +36 +36 +66 +66 +66 +FF +C3 +C3 +00 +00 +ENDCHAR +STARTCHAR C00a5 +ENCODING 165 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7C +C6 +FE +C0 +C0 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a6 +ENCODING 166 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +D6 +D6 +54 +7C +54 +D6 +D6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a7 +ENCODING 167 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +3C +66 +06 +0C +06 +66 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a8 +ENCODING 168 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +C6 +C6 +CE +D6 +E6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a9 +ENCODING 169 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +38 +38 +C6 +C6 +CE +D6 +E6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00aa +ENCODING 170 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +E6 +6C +78 +78 +6C +66 +E6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ab +ENCODING 171 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +1E +36 +66 +66 +66 +66 +66 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ac +ENCODING 172 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +C6 +EE +FE +FE +D6 +D6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ad +ENCODING 173 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +C6 +C6 +C6 +FE +C6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ae +ENCODING 174 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7C +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00af +ENCODING 175 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +FE +C6 +C6 +C6 +C6 +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00b0 +ENCODING 176 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +11 +44 +11 +44 +11 +44 +11 +44 +11 +44 +11 +44 +11 +44 +11 +44 +ENDCHAR +STARTCHAR C00b1 +ENCODING 177 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +55 +AA +55 +AA +55 +AA +55 +AA +55 +AA +55 +AA +55 +AA +55 +AA +ENDCHAR +STARTCHAR C00b2 +ENCODING 178 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +DD +77 +DD +77 +DD +77 +DD +77 +DD +77 +DD +77 +DD +77 +DD +77 +ENDCHAR +STARTCHAR C00b3 +ENCODING 179 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00b4 +ENCODING 180 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +F8 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00b5 +ENCODING 181 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +F8 +18 +F8 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00b6 +ENCODING 182 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +F6 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00b7 +ENCODING 183 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +FE +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00b8 +ENCODING 184 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +F8 +18 +F8 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00b9 +ENCODING 185 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +F6 +06 +F6 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00ba +ENCODING 186 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00bb +ENCODING 187 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +FE +06 +F6 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00bc +ENCODING 188 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +F6 +06 +FE +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00bd +ENCODING 189 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +FE +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00be +ENCODING 190 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +F8 +18 +F8 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00bf +ENCODING 191 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +F8 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00c0 +ENCODING 192 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +1F +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00c1 +ENCODING 193 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +FF +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00c2 +ENCODING 194 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +FF +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00c3 +ENCODING 195 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +1F +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00c4 +ENCODING 196 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +FF +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00c5 +ENCODING 197 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +FF +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00c6 +ENCODING 198 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +1F +18 +1F +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00c7 +ENCODING 199 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +37 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00c8 +ENCODING 200 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +37 +30 +3F +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00c9 +ENCODING 201 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +3F +30 +37 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00ca +ENCODING 202 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +F7 +00 +FF +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00cb +ENCODING 203 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +FF +00 +F7 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00cc +ENCODING 204 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +37 +30 +37 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00cd +ENCODING 205 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +FF +00 +FF +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ce +ENCODING 206 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +F7 +00 +F7 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00cf +ENCODING 207 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +FF +00 +FF +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00d0 +ENCODING 208 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +FF +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00d1 +ENCODING 209 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +FF +00 +FF +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00d2 +ENCODING 210 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +FF +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00d3 +ENCODING 211 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +3F +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00d4 +ENCODING 212 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +1F +18 +1F +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00d5 +ENCODING 213 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +1F +18 +1F +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00d6 +ENCODING 214 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +3F +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00d7 +ENCODING 215 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +FF +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00d8 +ENCODING 216 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +FF +18 +FF +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00d9 +ENCODING 217 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +F8 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00da +ENCODING 218 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +1F +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00db +ENCODING 219 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +ENDCHAR +STARTCHAR C00dc +ENCODING 220 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +FF +FF +FF +FF +FF +FF +FF +FF +FF +ENDCHAR +STARTCHAR C00dd +ENCODING 221 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +ENDCHAR +STARTCHAR C00de +ENCODING 222 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +ENDCHAR +STARTCHAR C00df +ENCODING 223 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +FF +FF +FF +FF +FF +FF +FF +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e0 +ENCODING 224 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +DC +66 +66 +66 +66 +66 +7C +60 +60 +F0 +00 +ENDCHAR +STARTCHAR C00e1 +ENCODING 225 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7C +C6 +C0 +C0 +C0 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e2 +ENCODING 226 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7E +5A +18 +18 +18 +18 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e3 +ENCODING 227 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +C6 +C6 +C6 +C6 +C6 +7E +06 +06 +C6 +7C +00 +ENDCHAR +STARTCHAR C00e4 +ENCODING 228 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +3C +18 +7E +DB +DB +DB +DB +7E +18 +18 +3C +00 +ENDCHAR +STARTCHAR C00e5 +ENCODING 229 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +C6 +6C +38 +38 +38 +6C +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e6 +ENCODING 230 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +CC +CC +CC +CC +CC +CC +FE +06 +06 +00 +00 +ENDCHAR +STARTCHAR C00e7 +ENCODING 231 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +C6 +C6 +C6 +C6 +7E +06 +06 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e8 +ENCODING 232 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +D6 +D6 +D6 +D6 +D6 +D6 +FE +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e9 +ENCODING 233 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +D6 +D6 +D6 +D6 +D6 +D6 +FE +03 +03 +00 +00 +ENDCHAR +STARTCHAR C00ea +ENCODING 234 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +F8 +B0 +30 +3E +33 +33 +7E +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00eb +ENCODING 235 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +C6 +C6 +C6 +F6 +DE +DE +F6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ec +ENCODING 236 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +F0 +60 +60 +7C +66 +66 +FC +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ed +ENCODING 237 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +3C +66 +06 +1E +06 +66 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ee +ENCODING 238 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +CE +DB +DB +FB +DB +DB +CE +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ef +ENCODING 239 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7E +CC +CC +FC +6C +CC +CE +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f0 +ENCODING 240 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +6C +00 +FE +66 +62 +68 +78 +68 +60 +62 +66 +FE +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f1 +ENCODING 241 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +6C +6C +00 +7C +C6 +FE +C0 +C0 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f2 +ENCODING 242 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7C +C6 +C0 +C8 +F8 +C8 +C0 +C0 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f3 +ENCODING 243 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +3C +66 +60 +78 +60 +66 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f4 +ENCODING 244 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +66 +00 +3C +18 +18 +18 +18 +18 +18 +18 +18 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f5 +ENCODING 245 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +6C +6C +00 +38 +18 +18 +18 +18 +18 +3C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f6 +ENCODING 246 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +7C +00 +C6 +C6 +C6 +C6 +C6 +7E +06 +06 +C6 +7C +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f7 +ENCODING 247 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +38 +38 +00 +C6 +C6 +C6 +C6 +C6 +7E +06 +06 +C6 +7C +00 +ENDCHAR +STARTCHAR C00f8 +ENCODING 248 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +38 +6C +6C +38 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f9 +ENCODING 249 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +18 +18 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00fa +ENCODING 250 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +18 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00fb +ENCODING 251 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +01 +03 +02 +06 +04 +4C +68 +38 +10 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00fc +ENCODING 252 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +07 +07 +C7 +C0 +E6 +E6 +F6 +DE +CE +CE +C6 +C6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00fd +ENCODING 253 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +66 +3C +66 +66 +66 +66 +3C +66 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00fe +ENCODING 254 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +7C +7C +7C +7C +7C +7C +7C +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ff +ENCODING 255 +SWIDTH 480 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +ENDFONT diff --git a/etc/vga.bdf b/etc/vga.bdf new file mode 100644 index 0000000..4514713 --- /dev/null +++ b/etc/vga.bdf @@ -0,0 +1,5900 @@ +COMMENT vga +STARTFONT 2.1 +FONT -dosemu-VGA-Medium-R-Normal--17-160-75-75-P-80-IBM-CP437 +SIZE 16 75 75 +FONTBOUNDINGBOX 8 16 0 -4 +STARTPROPERTIES 3 +FONT_DESCENT 4 +FONT_ASCENT 12 +DEFAULT_CHAR 0 +ENDPROPERTIES +CHARS 256 +STARTCHAR C0000 +ENCODING 0 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0001 +ENCODING 1 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7e +81 +a5 +81 +81 +a5 +99 +81 +81 +7e +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0002 +ENCODING 2 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7e +ff +db +ff +ff +db +e7 +ff +ff +7e +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0003 +ENCODING 3 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +6c +fe +fe +fe +fe +7c +38 +10 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0004 +ENCODING 4 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +10 +38 +7c +fe +7c +38 +10 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0005 +ENCODING 5 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +18 +3c +3c +e7 +e7 +e7 +18 +18 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0006 +ENCODING 6 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +18 +3c +7e +ff +ff +7e +18 +18 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0007 +ENCODING 7 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +18 +3c +3c +18 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0008 +ENCODING 8 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +ff +ff +ff +ff +ff +ff +e7 +c3 +c3 +e7 +ff +ff +ff +ff +ff +ff +ENDCHAR +STARTCHAR C0009 +ENCODING 9 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +3c +66 +42 +42 +66 +3c +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C000a +ENCODING 10 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +ff +ff +ff +ff +ff +c3 +99 +bd +bd +99 +c3 +ff +ff +ff +ff +ff +ENDCHAR +STARTCHAR C000b +ENCODING 11 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +1e +06 +0e +1a +78 +cc +cc +cc +cc +78 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C000c +ENCODING 12 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3c +66 +66 +66 +66 +3c +18 +7e +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C000d +ENCODING 13 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3f +33 +3f +30 +30 +30 +30 +70 +f0 +e0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C000e +ENCODING 14 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7f +63 +7f +63 +63 +63 +63 +67 +e7 +e6 +c0 +00 +00 +00 +ENDCHAR +STARTCHAR C000f +ENCODING 15 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +18 +18 +db +3c +e7 +3c +db +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0010 +ENCODING 16 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +80 +c0 +e0 +f0 +f8 +fe +f8 +f0 +e0 +c0 +80 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0011 +ENCODING 17 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +02 +06 +0e +1e +3e +fe +3e +1e +0e +06 +02 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0012 +ENCODING 18 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +3c +7e +18 +18 +18 +7e +3c +18 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0013 +ENCODING 19 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +66 +66 +66 +66 +66 +66 +66 +00 +66 +66 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0014 +ENCODING 20 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7f +db +db +db +7b +1b +1b +1b +1b +1b +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0015 +ENCODING 21 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +7c +c6 +60 +38 +6c +c6 +c6 +6c +38 +0c +c6 +7c +00 +00 +00 +ENDCHAR +STARTCHAR C0016 +ENCODING 22 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +fe +fe +fe +fe +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0017 +ENCODING 23 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +3c +7e +18 +18 +18 +7e +3c +18 +7e +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0018 +ENCODING 24 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +3c +7e +18 +18 +18 +18 +18 +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0019 +ENCODING 25 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +18 +18 +18 +18 +18 +18 +7e +3c +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C001a +ENCODING 26 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +18 +0c +fe +0c +18 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C001b +ENCODING 27 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +30 +60 +fe +60 +30 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C001c +ENCODING 28 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +c0 +c0 +c0 +fe +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C001d +ENCODING 29 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +28 +6c +fe +6c +28 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C001e +ENCODING 30 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +10 +38 +38 +7c +7c +fe +fe +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C001f +ENCODING 31 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +fe +fe +7c +7c +38 +38 +10 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0020 +ENCODING 32 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0021 +ENCODING 33 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +3c +3c +3c +18 +18 +18 +00 +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0022 +ENCODING 34 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +66 +66 +66 +24 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0023 +ENCODING 35 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +6c +6c +fe +6c +6c +6c +fe +6c +6c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0024 +ENCODING 36 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +7c +c6 +c2 +c0 +7c +06 +06 +86 +c6 +7c +18 +18 +00 +00 +ENDCHAR +STARTCHAR C0025 +ENCODING 37 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +c2 +c6 +0c +18 +30 +60 +c6 +86 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0026 +ENCODING 38 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +38 +6c +6c +38 +76 +dc +cc +cc +cc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0027 +ENCODING 39 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +30 +30 +30 +60 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0028 +ENCODING 40 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +0c +18 +30 +30 +30 +30 +30 +30 +18 +0c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0029 +ENCODING 41 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +30 +18 +0c +0c +0c +0c +0c +0c +18 +30 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C002a +ENCODING 42 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +66 +3c +ff +3c +66 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C002b +ENCODING 43 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +18 +18 +7e +18 +18 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C002c +ENCODING 44 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +18 +18 +18 +30 +00 +00 +00 +ENDCHAR +STARTCHAR C002d +ENCODING 45 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +fe +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C002e +ENCODING 46 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C002f +ENCODING 47 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +02 +06 +0c +18 +30 +60 +c0 +80 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0030 +ENCODING 48 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +38 +6c +c6 +c6 +d6 +d6 +c6 +c6 +6c +38 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0031 +ENCODING 49 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +38 +78 +18 +18 +18 +18 +18 +18 +7e +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0032 +ENCODING 50 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7c +c6 +06 +0c +18 +30 +60 +c0 +c6 +fe +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0033 +ENCODING 51 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7c +c6 +06 +06 +3c +06 +06 +06 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0034 +ENCODING 52 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +0c +1c +3c +6c +cc +fe +0c +0c +0c +1e +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0035 +ENCODING 53 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +fe +c0 +c0 +c0 +fc +06 +06 +06 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0036 +ENCODING 54 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +38 +60 +c0 +c0 +fc +c6 +c6 +c6 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0037 +ENCODING 55 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +fe +c6 +06 +06 +0c +18 +30 +30 +30 +30 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0038 +ENCODING 56 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7c +c6 +c6 +c6 +7c +c6 +c6 +c6 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0039 +ENCODING 57 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7c +c6 +c6 +c6 +7e +06 +06 +06 +0c +78 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C003a +ENCODING 58 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +18 +18 +00 +00 +00 +18 +18 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C003b +ENCODING 59 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +18 +18 +00 +00 +00 +18 +18 +30 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C003c +ENCODING 60 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +06 +0c +18 +30 +60 +30 +18 +0c +06 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C003d +ENCODING 61 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7e +00 +00 +7e +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C003e +ENCODING 62 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +60 +30 +18 +0c +06 +0c +18 +30 +60 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C003f +ENCODING 63 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7c +c6 +c6 +0c +18 +18 +18 +00 +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0040 +ENCODING 64 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +7c +c6 +c6 +de +de +de +dc +c0 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0041 +ENCODING 65 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +10 +38 +6c +c6 +c6 +fe +c6 +c6 +c6 +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0042 +ENCODING 66 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +fc +66 +66 +66 +7c +66 +66 +66 +66 +fc +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0043 +ENCODING 67 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3c +66 +c2 +c0 +c0 +c0 +c0 +c2 +66 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0044 +ENCODING 68 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +f8 +6c +66 +66 +66 +66 +66 +66 +6c +f8 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0045 +ENCODING 69 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +fe +66 +62 +68 +78 +68 +60 +62 +66 +fe +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0046 +ENCODING 70 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +fe +66 +62 +68 +78 +68 +60 +60 +60 +f0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0047 +ENCODING 71 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3c +66 +c2 +c0 +c0 +de +c6 +c6 +66 +3a +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0048 +ENCODING 72 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +c6 +c6 +c6 +c6 +fe +c6 +c6 +c6 +c6 +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0049 +ENCODING 73 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3c +18 +18 +18 +18 +18 +18 +18 +18 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C004a +ENCODING 74 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +1e +0c +0c +0c +0c +0c +cc +cc +cc +78 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C004b +ENCODING 75 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +e6 +66 +66 +6c +78 +78 +6c +66 +66 +e6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C004c +ENCODING 76 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +f0 +60 +60 +60 +60 +60 +60 +62 +66 +fe +00 +00 +00 +00 +ENDCHAR +STARTCHAR C004d +ENCODING 77 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +c6 +ee +fe +fe +d6 +c6 +c6 +c6 +c6 +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C004e +ENCODING 78 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +c6 +e6 +f6 +fe +de +ce +c6 +c6 +c6 +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C004f +ENCODING 79 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7c +c6 +c6 +c6 +c6 +c6 +c6 +c6 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0050 +ENCODING 80 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +fc +66 +66 +66 +7c +60 +60 +60 +60 +f0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0051 +ENCODING 81 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7c +c6 +c6 +c6 +c6 +c6 +c6 +d6 +de +7c +0c +0e +00 +00 +ENDCHAR +STARTCHAR C0052 +ENCODING 82 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +fc +66 +66 +66 +7c +6c +66 +66 +66 +e6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0053 +ENCODING 83 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7c +c6 +c6 +60 +38 +0c +06 +c6 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0054 +ENCODING 84 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +7e +7e +5a +18 +18 +18 +18 +18 +18 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0055 +ENCODING 85 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +c6 +c6 +c6 +c6 +c6 +c6 +c6 +c6 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0056 +ENCODING 86 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +c6 +c6 +c6 +c6 +c6 +c6 +c6 +6c +38 +10 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0057 +ENCODING 87 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +c6 +c6 +c6 +c6 +d6 +d6 +d6 +fe +ee +6c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0058 +ENCODING 88 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +c6 +c6 +6c +7c +38 +38 +7c +6c +c6 +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0059 +ENCODING 89 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +66 +66 +66 +66 +3c +18 +18 +18 +18 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C005a +ENCODING 90 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +fe +c6 +86 +0c +18 +30 +60 +c2 +c6 +fe +00 +00 +00 +00 +ENDCHAR +STARTCHAR C005b +ENCODING 91 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3c +30 +30 +30 +30 +30 +30 +30 +30 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C005c +ENCODING 92 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +80 +c0 +e0 +70 +38 +1c +0e +06 +02 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C005d +ENCODING 93 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3c +0c +0c +0c +0c +0c +0c +0c +0c +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C005e +ENCODING 94 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +10 +38 +6c +c6 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C005f +ENCODING 95 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ff +00 +00 +ENDCHAR +STARTCHAR C0060 +ENCODING 96 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +30 +30 +18 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0061 +ENCODING 97 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +78 +0c +7c +cc +cc +cc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0062 +ENCODING 98 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +e0 +60 +60 +78 +6c +66 +66 +66 +66 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0063 +ENCODING 99 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7c +c6 +c0 +c0 +c0 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0064 +ENCODING 100 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +1c +0c +0c +3c +6c +cc +cc +cc +cc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0065 +ENCODING 101 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7c +c6 +fe +c0 +c0 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0066 +ENCODING 102 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +38 +6c +64 +60 +f0 +60 +60 +60 +60 +f0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0067 +ENCODING 103 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +76 +cc +cc +cc +cc +cc +7c +0c +cc +78 +00 +ENDCHAR +STARTCHAR C0068 +ENCODING 104 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +e0 +60 +60 +6c +76 +66 +66 +66 +66 +e6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0069 +ENCODING 105 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +18 +00 +38 +18 +18 +18 +18 +18 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C006a +ENCODING 106 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +06 +06 +00 +0e +06 +06 +06 +06 +06 +06 +66 +66 +3c +00 +ENDCHAR +STARTCHAR C006b +ENCODING 107 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +e0 +60 +60 +66 +6c +78 +78 +6c +66 +e6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C006c +ENCODING 108 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +38 +18 +18 +18 +18 +18 +18 +18 +18 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C006d +ENCODING 109 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +ec +fe +d6 +d6 +d6 +d6 +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C006e +ENCODING 110 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +dc +66 +66 +66 +66 +66 +66 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C006f +ENCODING 111 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7c +c6 +c6 +c6 +c6 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0070 +ENCODING 112 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +dc +66 +66 +66 +66 +66 +7c +60 +60 +f0 +00 +ENDCHAR +STARTCHAR C0071 +ENCODING 113 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +76 +cc +cc +cc +cc +cc +7c +0c +0c +1e +00 +ENDCHAR +STARTCHAR C0072 +ENCODING 114 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +dc +76 +66 +60 +60 +60 +f0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0073 +ENCODING 115 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7c +c6 +60 +38 +0c +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0074 +ENCODING 116 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +10 +30 +30 +fc +30 +30 +30 +30 +36 +1c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0075 +ENCODING 117 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +cc +cc +cc +cc +cc +cc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0076 +ENCODING 118 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +66 +66 +66 +66 +66 +3c +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0077 +ENCODING 119 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +c6 +c6 +d6 +d6 +d6 +fe +6c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0078 +ENCODING 120 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +c6 +6c +38 +38 +38 +6c +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0079 +ENCODING 121 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +c6 +c6 +c6 +c6 +c6 +c6 +7e +06 +0c +f8 +00 +ENDCHAR +STARTCHAR C007a +ENCODING 122 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +fe +cc +18 +30 +60 +c6 +fe +00 +00 +00 +00 +ENDCHAR +STARTCHAR C007b +ENCODING 123 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +0e +18 +18 +18 +70 +18 +18 +18 +18 +0e +00 +00 +00 +00 +ENDCHAR +STARTCHAR C007c +ENCODING 124 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +18 +18 +18 +00 +18 +18 +18 +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C007d +ENCODING 125 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +70 +18 +18 +18 +0e +18 +18 +18 +18 +70 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C007e +ENCODING 126 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +76 +dc +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C007f +ENCODING 127 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +10 +38 +6c +c6 +c6 +c6 +fe +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0080 +ENCODING 128 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3c +66 +c2 +c0 +c0 +c0 +c2 +66 +3c +0c +06 +7c +00 +00 +ENDCHAR +STARTCHAR C0081 +ENCODING 129 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +cc +00 +00 +cc +cc +cc +cc +cc +cc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0082 +ENCODING 130 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +0c +18 +30 +00 +7c +c6 +fe +c0 +c0 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0083 +ENCODING 131 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +10 +38 +6c +00 +78 +0c +7c +cc +cc +cc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0084 +ENCODING 132 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +cc +00 +00 +78 +0c +7c +cc +cc +cc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0085 +ENCODING 133 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +60 +30 +18 +00 +78 +0c +7c +cc +cc +cc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0086 +ENCODING 134 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +38 +6c +38 +00 +78 +0c +7c +cc +cc +cc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0087 +ENCODING 135 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +3c +66 +60 +60 +66 +3c +0c +06 +3c +00 +00 +00 +ENDCHAR +STARTCHAR C0088 +ENCODING 136 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +10 +38 +6c +00 +7c +c6 +fe +c0 +c0 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0089 +ENCODING 137 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +c6 +00 +00 +7c +c6 +fe +c0 +c0 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C008a +ENCODING 138 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +60 +30 +18 +00 +7c +c6 +fe +c0 +c0 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C008b +ENCODING 139 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +66 +00 +00 +38 +18 +18 +18 +18 +18 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C008c +ENCODING 140 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +18 +3c +66 +00 +38 +18 +18 +18 +18 +18 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C008d +ENCODING 141 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +60 +30 +18 +00 +38 +18 +18 +18 +18 +18 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C008e +ENCODING 142 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +c6 +00 +10 +38 +6c +c6 +c6 +fe +c6 +c6 +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C008f +ENCODING 143 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +38 +6c +38 +00 +38 +6c +c6 +c6 +fe +c6 +c6 +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0090 +ENCODING 144 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +30 +60 +00 +fe +66 +60 +7c +60 +60 +66 +fe +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0091 +ENCODING 145 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +cc +76 +36 +7e +d8 +d8 +6e +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0092 +ENCODING 146 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +3e +6c +cc +cc +fe +cc +cc +cc +cc +ce +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0093 +ENCODING 147 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +10 +38 +6c +00 +7c +c6 +c6 +c6 +c6 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0094 +ENCODING 148 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +c6 +00 +00 +7c +c6 +c6 +c6 +c6 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0095 +ENCODING 149 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +60 +30 +18 +00 +7c +c6 +c6 +c6 +c6 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0096 +ENCODING 150 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +30 +78 +cc +00 +cc +cc +cc +cc +cc +cc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0097 +ENCODING 151 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +60 +30 +18 +00 +cc +cc +cc +cc +cc +cc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C0098 +ENCODING 152 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +c6 +00 +00 +c6 +c6 +c6 +c6 +c6 +c6 +7e +06 +0c +78 +00 +ENDCHAR +STARTCHAR C0099 +ENCODING 153 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +c6 +00 +7c +c6 +c6 +c6 +c6 +c6 +c6 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C009a +ENCODING 154 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +c6 +00 +c6 +c6 +c6 +c6 +c6 +c6 +c6 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C009b +ENCODING 155 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +18 +18 +3c +66 +60 +60 +60 +66 +3c +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C009c +ENCODING 156 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +38 +6c +64 +60 +f0 +60 +60 +60 +60 +e6 +fc +00 +00 +00 +00 +ENDCHAR +STARTCHAR C009d +ENCODING 157 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +66 +66 +3c +18 +7e +18 +7e +18 +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C009e +ENCODING 158 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +f8 +cc +cc +f8 +c4 +cc +de +cc +cc +cc +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C009f +ENCODING 159 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +0e +1b +18 +18 +18 +7e +18 +18 +18 +18 +18 +d8 +70 +00 +00 +ENDCHAR +STARTCHAR C00a0 +ENCODING 160 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +18 +30 +60 +00 +78 +0c +7c +cc +cc +cc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a1 +ENCODING 161 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +0c +18 +30 +00 +38 +18 +18 +18 +18 +18 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a2 +ENCODING 162 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +18 +30 +60 +00 +7c +c6 +c6 +c6 +c6 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a3 +ENCODING 163 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +18 +30 +60 +00 +cc +cc +cc +cc +cc +cc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a4 +ENCODING 164 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +76 +dc +00 +dc +66 +66 +66 +66 +66 +66 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a5 +ENCODING 165 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +76 +dc +00 +c6 +e6 +f6 +fe +de +ce +c6 +c6 +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a6 +ENCODING 166 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +3c +6c +6c +3e +00 +7e +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a7 +ENCODING 167 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +38 +6c +6c +38 +00 +7c +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a8 +ENCODING 168 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +30 +30 +00 +30 +30 +60 +c0 +c6 +c6 +7c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00a9 +ENCODING 169 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +fe +c0 +c0 +c0 +c0 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00aa +ENCODING 170 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +fe +06 +06 +06 +06 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ab +ENCODING 171 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +c0 +c0 +c2 +c6 +cc +18 +30 +60 +dc +86 +0c +18 +3e +00 +00 +ENDCHAR +STARTCHAR C00ac +ENCODING 172 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +c0 +c0 +c2 +c6 +cc +18 +30 +66 +ce +9e +3e +06 +06 +00 +00 +ENDCHAR +STARTCHAR C00ad +ENCODING 173 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +18 +18 +00 +18 +18 +18 +3c +3c +3c +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ae +ENCODING 174 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +36 +6c +d8 +6c +36 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00af +ENCODING 175 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +d8 +6c +36 +6c +d8 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00b0 +ENCODING 176 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +11 +44 +11 +44 +11 +44 +11 +44 +11 +44 +11 +44 +11 +44 +11 +44 +ENDCHAR +STARTCHAR C00b1 +ENCODING 177 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +55 +aa +55 +aa +55 +aa +55 +aa +55 +aa +55 +aa +55 +aa +55 +aa +ENDCHAR +STARTCHAR C00b2 +ENCODING 178 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +dd +77 +dd +77 +dd +77 +dd +77 +dd +77 +dd +77 +dd +77 +dd +77 +ENDCHAR +STARTCHAR C00b3 +ENCODING 179 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00b4 +ENCODING 180 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +f8 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00b5 +ENCODING 181 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +f8 +18 +f8 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00b6 +ENCODING 182 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +f6 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00b7 +ENCODING 183 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +fe +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00b8 +ENCODING 184 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +f8 +18 +f8 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00b9 +ENCODING 185 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +f6 +06 +f6 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00ba +ENCODING 186 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00bb +ENCODING 187 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +fe +06 +f6 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00bc +ENCODING 188 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +f6 +06 +fe +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00bd +ENCODING 189 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +fe +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00be +ENCODING 190 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +f8 +18 +f8 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00bf +ENCODING 191 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +f8 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00c0 +ENCODING 192 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +1f +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00c1 +ENCODING 193 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +ff +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00c2 +ENCODING 194 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +ff +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00c3 +ENCODING 195 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +1f +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00c4 +ENCODING 196 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +ff +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00c5 +ENCODING 197 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +ff +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00c6 +ENCODING 198 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +1f +18 +1f +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00c7 +ENCODING 199 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +37 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00c8 +ENCODING 200 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +37 +30 +3f +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00c9 +ENCODING 201 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +3f +30 +37 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00ca +ENCODING 202 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +f7 +00 +ff +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00cb +ENCODING 203 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +ff +00 +f7 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00cc +ENCODING 204 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +37 +30 +37 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00cd +ENCODING 205 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +ff +00 +ff +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ce +ENCODING 206 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +f7 +00 +f7 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00cf +ENCODING 207 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +ff +00 +ff +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00d0 +ENCODING 208 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +ff +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00d1 +ENCODING 209 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +ff +00 +ff +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00d2 +ENCODING 210 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +ff +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00d3 +ENCODING 211 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +3f +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00d4 +ENCODING 212 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +1f +18 +1f +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00d5 +ENCODING 213 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +1f +18 +1f +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00d6 +ENCODING 214 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +3f +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00d7 +ENCODING 215 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +ff +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR C00d8 +ENCODING 216 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +ff +18 +ff +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00d9 +ENCODING 217 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +f8 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00da +ENCODING 218 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +1f +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00db +ENCODING 219 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +ff +ff +ff +ff +ff +ff +ff +ff +ff +ff +ff +ff +ff +ff +ff +ff +ENDCHAR +STARTCHAR C00dc +ENCODING 220 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +ff +ff +ff +ff +ff +ff +ff +ff +ff +ENDCHAR +STARTCHAR C00dd +ENCODING 221 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +f0 +f0 +f0 +f0 +f0 +f0 +f0 +f0 +f0 +f0 +f0 +f0 +f0 +f0 +f0 +f0 +ENDCHAR +STARTCHAR C00de +ENCODING 222 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +0f +0f +0f +0f +0f +0f +0f +0f +0f +0f +0f +0f +0f +0f +0f +0f +ENDCHAR +STARTCHAR C00df +ENCODING 223 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +ff +ff +ff +ff +ff +ff +ff +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e0 +ENCODING 224 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +76 +dc +d8 +d8 +d8 +dc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e1 +ENCODING 225 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +78 +cc +cc +cc +d8 +cc +c6 +c6 +c6 +cc +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e2 +ENCODING 226 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +fe +c6 +c6 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e3 +ENCODING 227 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +fe +6c +6c +6c +6c +6c +6c +6c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e4 +ENCODING 228 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +fe +c6 +60 +30 +18 +30 +60 +c6 +fe +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e5 +ENCODING 229 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7e +d8 +d8 +d8 +d8 +d8 +70 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e6 +ENCODING 230 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +66 +66 +66 +66 +66 +7c +60 +60 +c0 +00 +00 +00 +ENDCHAR +STARTCHAR C00e7 +ENCODING 231 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +76 +dc +18 +18 +18 +18 +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e8 +ENCODING 232 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +7e +18 +3c +66 +66 +66 +3c +18 +7e +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00e9 +ENCODING 233 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +38 +6c +c6 +c6 +fe +c6 +c6 +6c +38 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ea +ENCODING 234 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +38 +6c +c6 +c6 +c6 +6c +6c +6c +6c +ee +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00eb +ENCODING 235 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +1e +30 +18 +0c +3e +66 +66 +66 +66 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ec +ENCODING 236 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +7e +db +db +db +7e +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ed +ENCODING 237 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +03 +06 +7e +db +db +f3 +7e +60 +c0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ee +ENCODING 238 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +1c +30 +60 +60 +7c +60 +60 +60 +30 +1c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ef +ENCODING 239 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +7c +c6 +c6 +c6 +c6 +c6 +c6 +c6 +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f0 +ENCODING 240 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +fe +00 +00 +fe +00 +00 +fe +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f1 +ENCODING 241 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +18 +18 +7e +18 +18 +00 +00 +ff +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f2 +ENCODING 242 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +30 +18 +0c +06 +0c +18 +30 +00 +7e +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f3 +ENCODING 243 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +0c +18 +30 +60 +30 +18 +0c +00 +7e +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f4 +ENCODING 244 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +0e +1b +1b +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR C00f5 +ENCODING 245 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +d8 +d8 +d8 +70 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f6 +ENCODING 246 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +18 +18 +00 +7e +00 +18 +18 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f7 +ENCODING 247 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +76 +dc +00 +76 +dc +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f8 +ENCODING 248 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +38 +6c +6c +38 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00f9 +ENCODING 249 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +18 +18 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00fa +ENCODING 250 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +18 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00fb +ENCODING 251 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +0f +0c +0c +0c +0c +0c +ec +6c +6c +3c +1c +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00fc +ENCODING 252 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +d8 +6c +6c +6c +6c +6c +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00fd +ENCODING 253 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +70 +d8 +30 +60 +c8 +f8 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00fe +ENCODING 254 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +7c +7c +7c +7c +7c +7c +7c +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR C00ff +ENCODING 255 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 16 0 -4 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +ENDFONT diff --git a/etc/vga10x20-cp850.bdf b/etc/vga10x20-cp850.bdf new file mode 100644 index 0000000..fc2927d --- /dev/null +++ b/etc/vga10x20-cp850.bdf @@ -0,0 +1,7117 @@ +STARTFONT 2.1 +FONT -xos4-Terminus-Bold-R-Normal--20-200-72-72-C-100-IBM-CP850 +SIZE 20 72 72 +FONTBOUNDINGBOX 10 20 0 -4 + +STARTPROPERTIES 22 +FOUNDRY "xos4" +FAMILY_NAME "Terminus" +WEIGHT_NAME "Bold" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "" +PIXEL_SIZE 20 +POINT_SIZE 200 +RESOLUTION_X 72 +RESOLUTION_Y 72 +SPACING "C" +AVERAGE_WIDTH 100 +CHARSET_REGISTRY "IBM" +CHARSET_ENCODING "CP850" +MIN_SPACE 0 +COPYRIGHT "Copyright (C) 2005 Dimitar Toshkov Zhekov" +WEIGHT 10 +X_HEIGHT 16 +QUAD_WIDTH 10 +DEFAULT_CHAR 32 +FONT_DESCENT 4 +FONT_ASCENT 16 +ENDPROPERTIES + +CHARS 253 + +STARTCHAR uni0000 +ENCODING 0 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7380 +6180 +6180 +0000 +0000 +6180 +6180 +6180 +0000 +0000 +6180 +6180 +7380 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR smileface +ENCODING 1 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +8040 +8040 +B340 +B340 +8040 +8040 +BF40 +9E40 +8040 +8040 +8040 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR invsmileface +ENCODING 2 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +FFC0 +FFC0 +CCC0 +CCC0 +FFC0 +FFC0 +C0C0 +E1C0 +FFC0 +FFC0 +FFC0 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR heart +ENCODING 3 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +7380 +F3C0 +FFC0 +FFC0 +FFC0 +FFC0 +7F80 +3F00 +1E00 +0C00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR diamond +ENCODING 4 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0C00 +1E00 +3F00 +7F80 +FFC0 +FFC0 +7F80 +3F00 +1E00 +0C00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR club +ENCODING 5 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3F00 +3F00 +1E00 +0C00 +6D80 +FFC0 +FFC0 +FFC0 +FFC0 +6D80 +0C00 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR spade +ENCODING 6 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +0C00 +1E00 +3F00 +7F80 +FFC0 +FFC0 +FFC0 +FFC0 +6D80 +0C00 +0C00 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR periodcentered +ENCODING 7 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1C00 +1C00 +1C00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR invbullet +ENCODING 8 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +F3C0 +E1C0 +E1C0 +F3C0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR + +STARTCHAR circle +ENCODING 9 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1E00 +3300 +2100 +2100 +3300 +1E00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR invcircle +ENCODING 10 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +E1C0 +CCC0 +DEC0 +DEC0 +CCC0 +E1C0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR + +STARTCHAR male +ENCODING 11 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0F80 +0380 +0680 +0C80 +1880 +3E00 +6300 +6300 +6300 +6300 +6300 +6300 +3E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR female +ENCODING 12 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0C00 +0C00 +7F80 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR musicalnote +ENCODING 13 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F80 +3180 +3180 +3F80 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +F000 +E000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR sun +ENCODING 15 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +0C00 +0C00 +CCC0 +6D80 +3F00 +F3C0 +3F00 +6D80 +CCC0 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR uni25B6 +ENCODING 16 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +4000 +6000 +7000 +7800 +7C00 +7E00 +7F00 +7F00 +7E00 +7C00 +7800 +7000 +6000 +4000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR uni25C0 +ENCODING 17 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0100 +0300 +0700 +0F00 +1F00 +3F00 +7F00 +7F00 +3F00 +1F00 +0F00 +0700 +0300 +0100 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR arrowupdn +ENCODING 18 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +1E00 +3F00 +7F80 +0C00 +0C00 +0C00 +0C00 +0C00 +7F80 +3F00 +1E00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR exclamdbl +ENCODING 19 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +0000 +0000 +3300 +3300 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR filledrect +ENCODING 22 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FF80 +FF80 +FF80 +FF80 +FF80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR arrowupdnbse +ENCODING 23 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +1E00 +3F00 +7F80 +0C00 +0C00 +0C00 +0C00 +0C00 +7F80 +3F00 +1E00 +0C00 +7F80 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR arrowup +ENCODING 24 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +1E00 +3F00 +7F80 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR arrowdown +ENCODING 25 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +7F80 +3F00 +1E00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR arrowright +ENCODING 26 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0400 +0600 +0700 +FF80 +FF80 +0700 +0600 +0400 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR arrowleft +ENCODING 27 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +1000 +3000 +7000 +FF80 +FF80 +7000 +3000 +1000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR orthogonal +ENCODING 28 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +6000 +6000 +6000 +6000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR arrowboth +ENCODING 29 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +1200 +3300 +7380 +FFC0 +FFC0 +7380 +3300 +1200 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR triagup +ENCODING 30 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +1E00 +1E00 +3F00 +3F00 +7F80 +7F80 +FFC0 +FFC0 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR triagdn +ENCODING 31 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +FFC0 +FFC0 +7F80 +7F80 +3F00 +3F00 +1E00 +1E00 +0C00 +0C00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR space +ENCODING 32 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR exclam +ENCODING 33 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR quotedbl +ENCODING 34 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +3300 +3300 +3300 +3300 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR numbersign +ENCODING 35 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3300 +3300 +3300 +3300 +7F80 +3300 +3300 +3300 +7F80 +3300 +3300 +3300 +3300 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR dollar +ENCODING 36 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0C00 +0C00 +3F00 +6D80 +6C00 +6C00 +6C00 +3F00 +0D80 +0D80 +0D80 +6D80 +3F00 +0C00 +0C00 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR percent +ENCODING 37 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +7300 +5300 +7600 +0600 +0C00 +0C00 +1800 +1800 +3000 +3700 +6500 +6700 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR ampersand +ENCODING 38 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3C00 +6600 +6600 +6600 +3C00 +1800 +3C80 +6780 +C300 +C300 +C300 +6780 +3C80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR quotesingle +ENCODING 39 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0C00 +0C00 +0C00 +1800 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR parenleft +ENCODING 40 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0600 +0C00 +0C00 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +0C00 +0C00 +0600 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR parenright +ENCODING 41 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1800 +0C00 +0C00 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0C00 +0C00 +1800 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR asterisk +ENCODING 42 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +6300 +3600 +1C00 +FF80 +1C00 +3600 +6300 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR plus +ENCODING 43 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +0C00 +7F80 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR comma +ENCODING 44 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +0C00 +1800 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR hyphen +ENCODING 45 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F80 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR period +ENCODING 46 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR slash +ENCODING 47 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0300 +0300 +0600 +0600 +0C00 +0C00 +1800 +1800 +3000 +3000 +6000 +6000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR zero +ENCODING 48 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +6180 +6180 +6380 +6780 +6D80 +7980 +7180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR one +ENCODING 49 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +1C00 +3C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR two +ENCODING 50 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +6180 +6180 +0180 +0180 +0300 +0600 +0C00 +1800 +3000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR three +ENCODING 51 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +6180 +0180 +0180 +0180 +1F00 +0180 +0180 +0180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR four +ENCODING 52 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0180 +0380 +0780 +0D80 +1980 +3180 +6180 +6180 +6180 +7F80 +0180 +0180 +0180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR five +ENCODING 53 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +6000 +6000 +6000 +6000 +7F00 +0180 +0180 +0180 +0180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR six +ENCODING 54 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1F00 +3000 +6000 +6000 +6000 +7F00 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR seven +ENCODING 55 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +6180 +6180 +0180 +0300 +0300 +0600 +0600 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR eight +ENCODING 56 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +3F00 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR nine +ENCODING 57 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +3F80 +0180 +0180 +0180 +0300 +3E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR colon +ENCODING 58 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +0000 +0000 +0000 +0000 +0C00 +0C00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR semicolon +ENCODING 59 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +0000 +0000 +0000 +0000 +0C00 +0C00 +0C00 +1800 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR less +ENCODING 60 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0180 +0300 +0600 +0C00 +1800 +3000 +6000 +3000 +1800 +0C00 +0600 +0300 +0180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR equal +ENCODING 61 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F80 +0000 +0000 +0000 +7F80 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR greater +ENCODING 62 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6000 +3000 +1800 +0C00 +0600 +0300 +0180 +0300 +0600 +0C00 +1800 +3000 +6000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR question +ENCODING 63 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6180 +0180 +0300 +0600 +0C00 +0C00 +0000 +0000 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR at +ENCODING 64 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F00 +C180 +C180 +CF80 +D980 +D980 +D980 +D980 +D980 +CF80 +C000 +C000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR A +ENCODING 65 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +7F80 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR B +ENCODING 66 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F00 +6180 +6180 +6180 +6180 +7F00 +6180 +6180 +6180 +6180 +6180 +6180 +7F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR C +ENCODING 67 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +6180 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR D +ENCODING 68 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7E00 +6300 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6300 +7E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR E +ENCODING 69 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +6000 +6000 +6000 +6000 +6000 +7E00 +6000 +6000 +6000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR F +ENCODING 70 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +6000 +6000 +6000 +6000 +6000 +7E00 +6000 +6000 +6000 +6000 +6000 +6000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR G +ENCODING 71 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +6180 +6000 +6000 +6000 +6780 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR H +ENCODING 72 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +7F80 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR I +ENCODING 73 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR J +ENCODING 74 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0780 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +6300 +6300 +6300 +3E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR K +ENCODING 75 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6300 +6600 +6C00 +7800 +7000 +7800 +6C00 +6600 +6300 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR L +ENCODING 76 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR M +ENCODING 77 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +8080 +C180 +E380 +F780 +DD80 +C980 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR N +ENCODING 78 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +6180 +7180 +7980 +6D80 +6780 +6380 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR O +ENCODING 79 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR P +ENCODING 80 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F00 +6180 +6180 +6180 +6180 +6180 +7F00 +6000 +6000 +6000 +6000 +6000 +6000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Q +ENCODING 81 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6780 +3F00 +0300 +0180 +0000 +0000 +ENDCHAR + +STARTCHAR R +ENCODING 82 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F00 +6180 +6180 +6180 +6180 +6180 +7F00 +7800 +6C00 +6600 +6300 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR S +ENCODING 83 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +6180 +6000 +6000 +6000 +3F00 +0180 +0180 +0180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR T +ENCODING 84 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR U +ENCODING 85 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR V +ENCODING 86 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +3300 +3300 +3300 +3300 +1E00 +1E00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR W +ENCODING 87 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C980 +DD80 +F780 +E380 +C180 +8080 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR X +ENCODING 88 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +3300 +3300 +1E00 +0C00 +1E00 +3300 +3380 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Y +ENCODING 89 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +3300 +3300 +3300 +1E00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Z +ENCODING 90 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +0180 +0180 +0180 +0300 +0600 +0C00 +1800 +3000 +6000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR bracketleft +ENCODING 91 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR backslash +ENCODING 92 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +6000 +6000 +3000 +3000 +1800 +1800 +0C00 +0C00 +0600 +0600 +0300 +0300 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR bracketright +ENCODING 93 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR asciicircum +ENCODING 94 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0C00 +1E00 +3300 +6180 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR underscore +ENCODING 95 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F80 +0000 +0000 +ENDCHAR + +STARTCHAR grave +ENCODING 96 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +1800 +1800 +1800 +0C00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR a +ENCODING 97 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +0180 +0180 +3F80 +6180 +6180 +6180 +6180 +3F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR b +ENCODING 98 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6000 +6000 +6000 +6000 +7F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +7F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR c +ENCODING 99 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +6180 +6000 +6000 +6000 +6000 +6000 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR d +ENCODING 100 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0180 +0180 +0180 +0180 +3F80 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR e +ENCODING 101 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +6180 +6180 +6180 +7F80 +6000 +6000 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR f +ENCODING 102 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0780 +0C00 +0C00 +0C00 +3F00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR g +ENCODING 103 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F80 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F80 +0180 +0180 +3F00 +0000 +ENDCHAR + +STARTCHAR h +ENCODING 104 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6000 +6000 +6000 +6000 +7F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR i +ENCODING 105 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +0C00 +0000 +0000 +1C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR j +ENCODING 106 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0300 +0300 +0000 +0000 +0700 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +3300 +3300 +1E00 +0000 +ENDCHAR + +STARTCHAR k +ENCODING 107 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6000 +6000 +6000 +6000 +6180 +6300 +6600 +6C00 +7800 +6C00 +6600 +6300 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR l +ENCODING 108 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR m +ENCODING 109 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F00 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR n +ENCODING 110 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR o +ENCODING 111 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR p +ENCODING 112 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +7F00 +6000 +6000 +6000 +0000 +ENDCHAR + +STARTCHAR q +ENCODING 113 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F80 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F80 +0180 +0180 +0180 +0000 +ENDCHAR + +STARTCHAR r +ENCODING 114 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6F80 +7800 +7000 +6000 +6000 +6000 +6000 +6000 +6000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR s +ENCODING 115 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +6180 +6000 +6000 +3F00 +0180 +0180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR t +ENCODING 116 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1800 +1800 +1800 +1800 +7E00 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +0F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR u +ENCODING 117 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR v +ENCODING 118 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6180 +6180 +6180 +3300 +3300 +3300 +1E00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR w +ENCODING 119 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6180 +6180 +6180 +6D80 +6D80 +6D80 +6D80 +6D80 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR x +ENCODING 120 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6180 +6180 +3300 +1E00 +0C00 +1E00 +3300 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR y +ENCODING 121 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F80 +0180 +0180 +3F00 +0000 +ENDCHAR + +STARTCHAR z +ENCODING 122 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F80 +0180 +0300 +0600 +0C00 +1800 +3000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR braceleft +ENCODING 123 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0700 +0C00 +0C00 +0C00 +0C00 +0C00 +3800 +0C00 +0C00 +0C00 +0C00 +0C00 +0700 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR bar +ENCODING 124 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR braceright +ENCODING 125 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3800 +0C00 +0C00 +0C00 +0C00 +0C00 +0700 +0C00 +0C00 +0C00 +0C00 +0C00 +3800 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR asciitilde +ENCODING 126 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +3980 +6D80 +6D80 +6700 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR house +ENCODING 127 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +1E00 +3300 +3300 +6180 +6180 +6180 +6180 +7F80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Ccedilla +ENCODING 128 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +6180 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6180 +6180 +3F00 +0C00 +0C00 +1800 +0000 +ENDCHAR + +STARTCHAR udieresis +ENCODING 129 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +3300 +3300 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR eacute +ENCODING 130 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0600 +0C00 +0000 +3F00 +6180 +6180 +6180 +7F80 +6000 +6000 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR acircumflex +ENCODING 131 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +1E00 +3300 +0000 +3F00 +0180 +0180 +3F80 +6180 +6180 +6180 +6180 +3F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR adieresis +ENCODING 132 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +3300 +3300 +0000 +3F00 +0180 +0180 +3F80 +6180 +6180 +6180 +6180 +3F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR agrave +ENCODING 133 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +1800 +0C00 +0000 +3F00 +0180 +0180 +3F80 +6180 +6180 +6180 +6180 +3F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR aring +ENCODING 134 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +1E00 +3300 +1E00 +3F00 +0180 +0180 +3F80 +6180 +6180 +6180 +6180 +3F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR ccedilla +ENCODING 135 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +6180 +6000 +6000 +6000 +6000 +6000 +6180 +3F00 +0C00 +0C00 +1800 +0000 +ENDCHAR + +STARTCHAR ecircumflex +ENCODING 136 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +1E00 +3300 +0000 +3F00 +6180 +6180 +6180 +7F80 +6000 +6000 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR edieresis +ENCODING 137 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +3300 +3300 +0000 +3F00 +6180 +6180 +6180 +7F80 +6000 +6000 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR egrave +ENCODING 138 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +1800 +0C00 +0000 +3F00 +6180 +6180 +6180 +7F80 +6000 +6000 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR idieresis +ENCODING 139 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +3300 +3300 +0000 +1C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR icircumflex +ENCODING 140 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +1E00 +3300 +0000 +1C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR igrave +ENCODING 141 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +1800 +0C00 +0000 +1C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Adieresis +ENCODING 142 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +7F80 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Aring +ENCODING 143 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +1E00 +3300 +1E00 +3F00 +6180 +6180 +6180 +6180 +6180 +7F80 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Eacute +ENCODING 144 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0600 +0C00 +0000 +7F80 +6000 +6000 +6000 +6000 +6000 +7E00 +6000 +6000 +6000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR ae +ENCODING 145 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7780 +0CC0 +0CC0 +7CC0 +CFC0 +CC00 +CC00 +CCC0 +7B80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR AE +ENCODING 146 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7FC0 +C600 +C600 +C600 +C600 +C600 +FFC0 +C600 +C600 +C600 +C600 +C600 +C7C0 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR ocircumflex +ENCODING 147 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +1E00 +3300 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR odieresis +ENCODING 148 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +3300 +3300 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR ograve +ENCODING 149 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +1800 +0C00 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR ucircumflex +ENCODING 150 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +1E00 +3300 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR ugrave +ENCODING 151 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +1800 +0C00 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR ydieresis +ENCODING 152 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +3300 +3300 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F80 +0180 +0180 +3F00 +0000 +ENDCHAR + +STARTCHAR Odieresis +ENCODING 153 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Udieresis +ENCODING 154 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR oslash +ENCODING 155 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F40 +6180 +6380 +6780 +6D80 +7980 +7180 +6180 +BF00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR sterling +ENCODING 156 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +3000 +3000 +3000 +3000 +7E00 +3000 +3000 +3000 +3000 +3180 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Oslash +ENCODING 157 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +6180 +61C0 +6180 +6380 +6780 +6D80 +7980 +7180 +6180 +E180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR multiply +ENCODING 158 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +C0C0 +6180 +3300 +1E00 +0C00 +1E00 +3300 +6180 +C0C0 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR florin +ENCODING 159 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0700 +0D80 +0D80 +0C00 +0C00 +3F00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +6C00 +6C00 +3800 +0000 +ENDCHAR + +STARTCHAR aacute +ENCODING 160 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0600 +0C00 +0000 +3F00 +0180 +0180 +3F80 +6180 +6180 +6180 +6180 +3F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR iacute +ENCODING 161 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0600 +0C00 +0000 +1C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR oacute +ENCODING 162 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0600 +0C00 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR uacute +ENCODING 163 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0600 +0C00 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR ntilde +ENCODING 164 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +3B80 +6E00 +0000 +7F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Ntilde +ENCODING 165 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3B80 +6E00 +0000 +6180 +6180 +6180 +6180 +7180 +7980 +6D80 +6780 +6380 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR ordfeminine +ENCODING 166 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +3E00 +0300 +3F00 +6300 +6300 +6300 +3F00 +0000 +7F00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR ordmasculine +ENCODING 167 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +3E00 +6300 +6300 +6300 +6300 +6300 +3E00 +0000 +7F00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR questiondown +ENCODING 168 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +0C00 +0000 +0000 +0C00 +0C00 +1800 +3000 +6000 +6180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR registered +ENCODING 169 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +3F00 +4080 +BE40 +B340 +B340 +BE40 +B640 +B340 +4080 +3F00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR logicalnot +ENCODING 170 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F80 +0180 +0180 +0180 +0180 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR onehalf +ENCODING 171 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +1800 +3800 +1800 +1840 +18C0 +1980 +0300 +0600 +0C00 +1B80 +36C0 +60C0 +C180 +8300 +07C0 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR onequarter +ENCODING 172 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +1800 +3800 +1800 +1840 +18C0 +1980 +0300 +0600 +0C80 +1980 +3380 +6580 +CF80 +8180 +0180 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR exclamdown +ENCODING 173 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +0C00 +0000 +0000 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR guillemotleft +ENCODING 174 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0CC0 +1980 +3300 +6600 +CC00 +6600 +3300 +1980 +0CC0 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR guillemotright +ENCODING 175 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +CC00 +6600 +3300 +1980 +0CC0 +1980 +3300 +6600 +CC00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR ltshade +ENCODING 176 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +AA80 +0000 +AA80 +0000 +AA80 +0000 +AA80 +0000 +AA80 +0000 +AA80 +0000 +AA80 +0000 +AA80 +0000 +AA80 +0000 +AA80 +0000 +ENDCHAR + +STARTCHAR shade +ENCODING 177 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +ENDCHAR + +STARTCHAR dkshade +ENCODING 178 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +FFC0 +AA80 +FFC0 +AA80 +FFC0 +AA80 +FFC0 +AA80 +FFC0 +AA80 +FFC0 +AA80 +FFC0 +AA80 +FFC0 +AA80 +FFC0 +AA80 +FFC0 +AA80 +ENDCHAR + +STARTCHAR SF110000 +ENCODING 179 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR + +STARTCHAR SF090000 +ENCODING 180 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FC00 +FC00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR + +STARTCHAR Aacute +ENCODING 181 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0600 +0C00 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +7F80 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Acircumflex +ENCODING 182 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +1E00 +3300 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +7F80 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Agrave +ENCODING 183 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +1800 +0C00 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +7F80 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR copyright +ENCODING 184 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +3F00 +4080 +9E40 +B340 +B040 +B040 +B340 +9E40 +4080 +3F00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR SF230000 +ENCODING 185 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3600 +3600 +3600 +3600 +3600 +3600 +3600 +F600 +F600 +0600 +F600 +F600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +ENDCHAR + +STARTCHAR SF240000 +ENCODING 186 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +ENDCHAR + +STARTCHAR SF250000 +ENCODING 187 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FE00 +FE00 +0600 +F600 +F600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +ENDCHAR + +STARTCHAR SF260000 +ENCODING 188 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3600 +3600 +3600 +3600 +3600 +3600 +3600 +F600 +F600 +0600 +FE00 +FE00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR cent +ENCODING 189 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +3F00 +6D80 +6C00 +6C00 +6C00 +6C00 +6C00 +6D80 +3F00 +0C00 +0C00 +0000 +0000 +ENDCHAR + +STARTCHAR yen +ENCODING 190 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +3300 +3300 +3300 +1E00 +1E00 +0C00 +3F00 +0C00 +3F00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR SF030000 +ENCODING 191 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FC00 +FC00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR + +STARTCHAR SF020000 +ENCODING 192 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0FC0 +0FC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR SF070000 +ENCODING 193 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FFC0 +FFC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR SF060000 +ENCODING 194 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FFC0 +FFC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR + +STARTCHAR SF080000 +ENCODING 195 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0FC0 +0FC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR + +STARTCHAR SF100000 +ENCODING 196 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FFC0 +FFC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR SF050000 +ENCODING 197 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FFC0 +FFC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR + +STARTCHAR atilde +ENCODING 198 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +3B80 +6E00 +0000 +3F00 +0180 +0180 +3F80 +6180 +6180 +6180 +6180 +3F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Atilde +ENCODING 199 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3B80 +6E00 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +7F80 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR SF380000 +ENCODING 200 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3600 +3600 +3600 +3600 +3600 +3600 +3600 +37C0 +37C0 +3000 +3FC0 +3FC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR SF390000 +ENCODING 201 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3FC0 +3FC0 +3000 +37C0 +37C0 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +ENDCHAR + +STARTCHAR SF400000 +ENCODING 202 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3600 +3600 +3600 +3600 +3600 +3600 +3600 +F7C0 +F7C0 +0000 +FFC0 +FFC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR SF410000 +ENCODING 203 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FFC0 +FFC0 +0000 +F7C0 +F7C0 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +ENDCHAR + +STARTCHAR SF420000 +ENCODING 204 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3600 +3600 +3600 +3600 +3600 +3600 +3600 +37C0 +37C0 +3000 +37C0 +37C0 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +ENDCHAR + +STARTCHAR SF430000 +ENCODING 205 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FFC0 +FFC0 +0000 +FFC0 +FFC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR SF440000 +ENCODING 206 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3600 +3600 +3600 +3600 +3600 +3600 +3600 +F7C0 +F7C0 +0000 +F7C0 +F7C0 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +ENDCHAR + +STARTCHAR currency +ENCODING 207 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +C180 +6300 +3E00 +6300 +6300 +6300 +6300 +3E00 +6300 +C180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR eth +ENCODING 208 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3600 +1800 +6C00 +0600 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Eth +ENCODING 209 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7E00 +6300 +6180 +6180 +6180 +6180 +FD80 +6180 +6180 +6180 +6180 +6300 +7E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Ecircumflex +ENCODING 210 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +1E00 +3300 +0000 +7F80 +6000 +6000 +6000 +6000 +6000 +7E00 +6000 +6000 +6000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Edieresis +ENCODING 211 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +0000 +7F80 +6000 +6000 +6000 +6000 +6000 +7E00 +6000 +6000 +6000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Egrave +ENCODING 212 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +1800 +0C00 +0000 +7F80 +6000 +6000 +6000 +6000 +6000 +7E00 +6000 +6000 +6000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR dotlessi +ENCODING 213 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Iacute +ENCODING 214 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0600 +0C00 +0000 +1E00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Icircumflex +ENCODING 215 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +1E00 +3300 +0000 +1E00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Idieresis +ENCODING 216 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +0000 +1E00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR SF040000 +ENCODING 217 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FC00 +FC00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR SF010000 +ENCODING 218 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0FC0 +0FC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR + +STARTCHAR block +ENCODING 219 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR + +STARTCHAR dnblock +ENCODING 220 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR + +STARTCHAR brokenbar +ENCODING 221 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Igrave +ENCODING 222 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +1800 +0C00 +0000 +1E00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR upblock +ENCODING 223 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Oacute +ENCODING 224 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0600 +0C00 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR germandbls +ENCODING 225 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3E00 +6300 +6300 +6300 +6200 +7F00 +6180 +6180 +6180 +6180 +6180 +7180 +5F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Ocircumflex +ENCODING 226 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +1E00 +3300 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Ograve +ENCODING 227 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +1800 +0C00 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR otilde +ENCODING 228 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +3B80 +6E00 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Otilde +ENCODING 229 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3B80 +6E00 +0000 +3F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR mu +ENCODING 230 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6380 +7D80 +6000 +6000 +6000 +0000 +ENDCHAR + +STARTCHAR thorn +ENCODING 231 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6000 +6000 +6000 +6000 +7F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +7F00 +6000 +6000 +6000 +0000 +ENDCHAR + +STARTCHAR Thorn +ENCODING 232 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6000 +6000 +7F00 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +7F00 +6000 +6000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Uacute +ENCODING 233 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0600 +0C00 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Ucircumflex +ENCODING 234 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +1E00 +3300 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR Ugrave +ENCODING 235 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +1800 +0C00 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR yacute +ENCODING 236 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0600 +0C00 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3F80 +0180 +0180 +3F00 +0000 +ENDCHAR + +STARTCHAR Yacute +ENCODING 237 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0600 +0C00 +0000 +6180 +6180 +6180 +3300 +3300 +3300 +1E00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR macron +ENCODING 238 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +7F80 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR acute +ENCODING 239 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0600 +0C00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR hyphen +ENCODING 240 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR plusminus +ENCODING 241 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +0C00 +7F80 +0C00 +0C00 +0C00 +0000 +0000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR underscoredbl +ENCODING 242 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F80 +0000 +7F80 +ENDCHAR + +STARTCHAR threequarters +ENCODING 243 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +3800 +4C00 +1800 +0C40 +4CC0 +3980 +0300 +0600 +0C80 +1980 +3380 +6580 +CF80 +8180 +0180 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR paragraph +ENCODING 244 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7FC0 +CCC0 +CCC0 +CCC0 +CCC0 +CCC0 +7CC0 +0CC0 +0CC0 +0CC0 +0CC0 +0CC0 +0CC0 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR section +ENCODING 245 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +3E00 +6300 +6000 +6000 +3C00 +6600 +6300 +6300 +6300 +3300 +1E00 +0300 +0300 +6300 +3E00 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR divide +ENCODING 246 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +0000 +0000 +7F80 +0000 +0000 +0C00 +0C00 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR cedilla +ENCODING 247 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +1800 +0000 +ENDCHAR + +STARTCHAR degree +ENCODING 248 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +1E00 +3300 +3300 +3300 +1E00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR dieresis +ENCODING 249 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR periodcentered +ENCODING 250 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR onesuperior +ENCODING 251 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0C00 +1C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR threesuperior +ENCODING 252 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +1C00 +2600 +0C00 +0600 +2600 +1C00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR twosuperior +ENCODING 253 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +1C00 +3600 +0600 +0C00 +1800 +3E00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR filledbox +ENCODING 254 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +3F00 +3F00 +3F00 +3F00 +3F00 +3F00 +3F00 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +STARTCHAR space +ENCODING 255 +SWIDTH 500 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR + +ENDFONT diff --git a/etc/vga10x20-cp866.bdf b/etc/vga10x20-cp866.bdf new file mode 100644 index 0000000..b052d67 --- /dev/null +++ b/etc/vga10x20-cp866.bdf @@ -0,0 +1,6938 @@ +STARTFONT 2.1 +FONT -dosemu-VGA-Medium-R-Normal--20-200-75-75-C-100-IBM-CP866 +SIZE 20 75 75 +FONTBOUNDINGBOX 10 20 0 -4 +STARTPROPERTIES 18 +DEFAULT_CHAR 0 +FONT_DESCENT 4 +FONT_ASCENT 16 +_XMBDFED_INFO "Edited with xmbdfed 4.5." +POINT_SIZE 200 +RESOLUTION_X 75 +RESOLUTION_Y 75 +SPACING "C" +FOUNDRY "dosemu" +FAMILY_NAME "VGA" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "" +PIXEL_SIZE 20 +AVERAGE_WIDTH 100 +CHARSET_REGISTRY "IBM" +CHARSET_ENCODING "CP866" +ENDPROPERTIES +CHARS 256 +STARTCHAR 0 +ENCODING 0 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 1 +ENCODING 1 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +3F00 +6180 +4080 +5280 +5280 +4080 +4080 +5280 +4C80 +6180 +3F00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 2 +ENCODING 2 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +C0C0 +8040 +8040 +B340 +B340 +8040 +8040 +9240 +8C40 +8040 +C0C0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 3 +ENCODING 3 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +6300 +F780 +FF80 +FF80 +FF80 +7F00 +3E00 +1C00 +0800 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 4 +ENCODING 4 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0800 +1C00 +3E00 +7F00 +FF80 +7F00 +3E00 +1C00 +0800 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 5 +ENCODING 5 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +1E00 +3F00 +1E00 +4C80 +EDC0 +FFC0 +FFC0 +EDC0 +4C80 +0C00 +0C00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 6 +ENCODING 6 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0C00 +1E00 +3F00 +7F80 +FFC0 +FFC0 +FFC0 +6D80 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 7 +ENCODING 7 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0C00 +1E00 +1E00 +0C00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 8 +ENCODING 8 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +F3C0 +E1C0 +E1C0 +F3C0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 9 +ENCODING 9 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +1E00 +3F00 +7380 +6180 +6180 +6180 +7380 +3F00 +1E00 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 10 +ENCODING 10 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +E1C0 +C0C0 +8C40 +9E40 +9E40 +9E40 +8C40 +C0C0 +E1C0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 11 +ENCODING 11 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +03C0 +01C0 +03C0 +0740 +0E00 +1E00 +3F00 +7380 +6180 +6180 +7380 +3F00 +1E00 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 12 +ENCODING 12 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +1E00 +3F00 +7380 +6180 +6180 +7380 +3F00 +1E00 +0C00 +0C00 +3F00 +3F00 +0C00 +0C00 +0000 +0000 +ENDCHAR +STARTCHAR 13 +ENCODING 13 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0800 +0C00 +0E00 +0F00 +0D80 +0C80 +0C00 +0C00 +0C00 +0C00 +3C00 +7C00 +7C00 +3800 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 14 +ENCODING 14 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0800 +0C00 +0E00 +0F00 +0D80 +0C80 +0C80 +0C80 +0C80 +3C80 +7C80 +7C80 +3880 +0380 +0780 +0780 +0380 +ENDCHAR +STARTCHAR 15 +ENCODING 15 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0C00 +CCC0 +FFC0 +6180 +4080 +C0C0 +4080 +6180 +FFC0 +CCC0 +0C00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 16 +ENCODING 16 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +2000 +3000 +3800 +3C00 +3E00 +3F00 +3E00 +3C00 +3800 +3000 +2000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 17 +ENCODING 17 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0100 +0300 +0700 +0F00 +1F00 +3F00 +1F00 +0F00 +0700 +0300 +0100 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 18 +ENCODING 18 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +1E00 +3F00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +3F00 +1E00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 19 +ENCODING 19 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +0000 +0000 +3300 +3300 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 20 +ENCODING 20 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1F80 +3C80 +7C80 +7C80 +7C80 +7C80 +3C80 +1C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 21 +ENCODING 21 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +1F00 +3180 +3180 +1800 +1E00 +3300 +3180 +3180 +3180 +1980 +0F00 +0300 +3180 +3180 +1F00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 22 +ENCODING 22 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F80 +7F80 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 23 +ENCODING 23 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +1E00 +3F00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +3F00 +1E00 +0C00 +3F00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 24 +ENCODING 24 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +1E00 +3F00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 25 +ENCODING 25 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +3F00 +1E00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 26 +ENCODING 26 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0100 +0180 +FFC0 +0180 +0100 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 27 +ENCODING 27 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +2000 +6000 +FFC0 +6000 +2000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 28 +ENCODING 28 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +C000 +C000 +C000 +C000 +FFC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 29 +ENCODING 29 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +2100 +6180 +FFC0 +6180 +2100 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 30 +ENCODING 30 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0400 +0E00 +1F00 +3F80 +7FC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 31 +ENCODING 31 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7FC0 +3F80 +1F00 +0E00 +0400 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR space +ENCODING 32 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR exclam +ENCODING 33 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR quotedbl +ENCODING 34 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3300 +3300 +3300 +1200 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR numbersign +ENCODING 35 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0D80 +0D80 +0D80 +3FC0 +1B00 +1B00 +1B00 +7F80 +3600 +3600 +3600 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR dollar +ENCODING 36 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +3F00 +6D80 +6C00 +6C00 +6C00 +3F00 +0D80 +0D80 +0D80 +6D80 +3F00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR percent +ENCODING 37 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +3980 +6D80 +6F00 +3B00 +0600 +0600 +0C00 +0C00 +1B80 +1EC0 +36C0 +3380 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ampersand +ENCODING 38 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1C00 +3600 +3600 +3600 +3C00 +1800 +3800 +6C00 +66C0 +6380 +6300 +7780 +3CC0 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR quotesingle +ENCODING 39 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +0C00 +0C00 +0800 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR parenleft +ENCODING 40 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0300 +0600 +0C00 +0C00 +1800 +1800 +1800 +1800 +1800 +0C00 +0C00 +0600 +0300 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR parenright +ENCODING 41 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3000 +1800 +0C00 +0C00 +0600 +0600 +0600 +0600 +0600 +0C00 +0C00 +1800 +3000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR asterisk +ENCODING 42 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +3300 +3300 +1E00 +7F80 +1E00 +3300 +3300 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR plus +ENCODING 43 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +0C00 +7F80 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR comma +ENCODING 44 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0E00 +0E00 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR hyphen +ENCODING 45 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F80 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR period +ENCODING 46 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0E00 +0E00 +0E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR slash +ENCODING 47 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0180 +0180 +0300 +0300 +0600 +0600 +0C00 +0C00 +1800 +1800 +3000 +3000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR zero +ENCODING 48 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +1E00 +3300 +3300 +6180 +6180 +6180 +6180 +6180 +3300 +3300 +1E00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR one +ENCODING 49 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +1C00 +3C00 +6C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR two +ENCODING 50 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6180 +0180 +0180 +0300 +0E00 +1800 +3000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR three +ENCODING 51 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6180 +0180 +0300 +0E00 +0300 +0180 +6180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR four +ENCODING 52 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0100 +0300 +0700 +0F00 +1B00 +3300 +6300 +6300 +7F80 +0300 +0300 +0300 +0300 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR five +ENCODING 53 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +6000 +6000 +6000 +6000 +6E00 +7300 +0180 +0180 +0180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR six +ENCODING 54 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6100 +6000 +6000 +6E00 +7300 +6180 +6180 +6180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR seven +ENCODING 55 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +0180 +0180 +0300 +0300 +0600 +0600 +0C00 +0C00 +1800 +1800 +3000 +3000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR eight +ENCODING 56 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6180 +6180 +3300 +1E00 +3300 +6180 +6180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR nine +ENCODING 57 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6180 +6180 +6180 +3380 +1D80 +0180 +0180 +2180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR colon +ENCODING 58 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0E00 +0E00 +0000 +0000 +0000 +0000 +0E00 +0E00 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR semicolon +ENCODING 59 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0E00 +0E00 +0000 +0000 +0000 +0000 +0E00 +0E00 +1C00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR less +ENCODING 60 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0100 +0300 +0600 +0C00 +1800 +3000 +6000 +3000 +1800 +0C00 +0600 +0300 +0100 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR equal +ENCODING 61 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F80 +0000 +0000 +0000 +0000 +7F80 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR greater +ENCODING 62 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +2000 +3000 +1800 +0C00 +0600 +0300 +0180 +0300 +0600 +0C00 +1800 +3000 +2000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR question +ENCODING 63 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6180 +6180 +0300 +0600 +0C00 +0C00 +0C00 +0000 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR at +ENCODING 64 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6780 +6F80 +6D80 +6D80 +6D80 +6F00 +6600 +6000 +3180 +1F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR A +ENCODING 65 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +1E00 +3300 +3300 +6180 +6180 +6180 +7F80 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR B +ENCODING 66 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7C00 +6600 +6300 +6300 +6300 +6600 +7E00 +6300 +6180 +6180 +6180 +6300 +7E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C +ENCODING 67 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR D +ENCODING 68 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7E00 +6300 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6300 +7E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR E +ENCODING 69 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +6000 +6000 +6000 +6000 +6000 +7E00 +6000 +6000 +6000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR F +ENCODING 70 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +6000 +6000 +6000 +6000 +6000 +7E00 +6000 +6000 +6000 +6000 +6000 +6000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR G +ENCODING 71 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6000 +6000 +6000 +6780 +6180 +6180 +6180 +6180 +3380 +1E80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR H +ENCODING 72 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +7F80 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR I +ENCODING 73 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR J +ENCODING 74 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0F80 +0180 +0180 +0180 +0180 +0180 +0180 +0180 +0180 +6180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR K +ENCODING 75 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6300 +6300 +6600 +6600 +7C00 +6600 +6600 +6300 +6300 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR L +ENCODING 76 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR M +ENCODING 77 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +7380 +7380 +7F80 +6D80 +6D80 +6D80 +6D80 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR N +ENCODING 78 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +7180 +7180 +7980 +7980 +6D80 +6D80 +6780 +6780 +6380 +6380 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR O +ENCODING 79 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR P +ENCODING 80 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7E00 +6300 +6180 +6180 +6180 +6180 +6300 +7E00 +6000 +6000 +6000 +6000 +6000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Q +ENCODING 81 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6D80 +6780 +3300 +1F00 +0180 +0000 +0000 +0000 +ENDCHAR +STARTCHAR R +ENCODING 82 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7E00 +6300 +6180 +6180 +6180 +6180 +6300 +7E00 +6600 +6300 +6300 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR S +ENCODING 83 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6000 +6000 +3000 +1E00 +0300 +0180 +0180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR T +ENCODING 84 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR U +ENCODING 85 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR V +ENCODING 86 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +6180 +3300 +3300 +3300 +1E00 +1E00 +1E00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR W +ENCODING 87 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6D80 +6D80 +6D80 +6D80 +7380 +7380 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR X +ENCODING 88 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +3300 +3300 +1E00 +1E00 +0C00 +1E00 +1E00 +3300 +3300 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Y +ENCODING 89 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +3300 +3300 +1E00 +1E00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Z +ENCODING 90 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +0180 +0180 +0300 +0600 +0600 +0C00 +1800 +1800 +3000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR bracketleft +ENCODING 91 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR backslash +ENCODING 92 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +3000 +3000 +1800 +1800 +0C00 +0C00 +0600 +0600 +0300 +0300 +0180 +0180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR bracketright +ENCODING 93 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3F00 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR asciicircum +ENCODING 94 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +1E00 +3300 +6180 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR underscore +ENCODING 95 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7FC0 +0000 +0000 +0000 +ENDCHAR +STARTCHAR grave +ENCODING 96 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1800 +0C00 +0600 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR a +ENCODING 97 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +6180 +0180 +7F80 +C180 +C180 +C180 +C180 +7E80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR b +ENCODING 98 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +C000 +C000 +C000 +C000 +DE00 +E300 +C180 +C180 +C180 +C180 +C180 +E300 +DE00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR c +ENCODING 99 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +6180 +C000 +C000 +C000 +C000 +C000 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR d +ENCODING 100 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0180 +0180 +0180 +0180 +3D80 +6380 +C180 +C180 +C180 +C180 +C180 +6380 +3D80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR e +ENCODING 101 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3E00 +6300 +C180 +FF80 +C000 +C000 +C000 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR f +ENCODING 102 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1F00 +3180 +3180 +3000 +3000 +3000 +3000 +FC00 +3000 +3000 +3000 +3000 +3000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR g +ENCODING 103 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7D00 +C700 +C600 +C600 +C600 +7C00 +C000 +C000 +7F00 +C180 +C180 +C180 +7F00 +ENDCHAR +STARTCHAR h +ENCODING 104 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +C000 +C000 +C000 +C000 +C000 +DE00 +E300 +C180 +C180 +C180 +C180 +C180 +C180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR i +ENCODING 105 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0C00 +0C00 +0000 +3C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR j +ENCODING 106 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0300 +0300 +0000 +0F00 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +C300 +C300 +C300 +7E00 +ENDCHAR +STARTCHAR k +ENCODING 107 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +C000 +C000 +C000 +C000 +C300 +C600 +CC00 +F800 +F800 +CC00 +C600 +C300 +C180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR l +ENCODING 108 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR m +ENCODING 109 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +B700 +FF80 +C980 +C980 +C980 +C980 +C980 +C980 +C980 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR n +ENCODING 110 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +DE00 +E300 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR o +ENCODING 111 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3E00 +6300 +C180 +C180 +C180 +C180 +C180 +6300 +3E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR p +ENCODING 112 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +DE00 +E300 +C180 +C180 +C180 +C180 +C180 +E300 +DE00 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR q +ENCODING 113 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3D80 +6380 +C180 +C180 +C180 +C180 +C180 +6380 +3D80 +0180 +0180 +0180 +0180 +ENDCHAR +STARTCHAR r +ENCODING 114 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +DF00 +7180 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR s +ENCODING 115 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F00 +C180 +C000 +C000 +7F00 +0180 +0180 +C180 +7F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR t +ENCODING 116 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +3000 +3000 +3000 +FC00 +3000 +3000 +3000 +3000 +3000 +3000 +3180 +1F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR u +ENCODING 117 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +6380 +3D80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR v +ENCODING 118 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C180 +C180 +6300 +6300 +3600 +3E00 +1C00 +1C00 +0800 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR w +ENCODING 119 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C180 +C180 +C180 +C980 +C980 +C980 +C980 +FF80 +6300 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR x +ENCODING 120 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C180 +6300 +3600 +1C00 +1C00 +1C00 +3600 +6300 +C180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR y +ENCODING 121 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +6380 +3D80 +0180 +C180 +6300 +3E00 +ENDCHAR +STARTCHAR z +ENCODING 122 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F80 +0180 +0300 +0600 +0C00 +1800 +3000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR braceleft +ENCODING 123 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0780 +0C00 +0C00 +0C00 +0C00 +0C00 +7800 +0C00 +0C00 +0C00 +0C00 +0C00 +0780 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR bar +ENCODING 124 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR braceright +ENCODING 125 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7800 +0C00 +0C00 +0C00 +0C00 +0C00 +0780 +0C00 +0C00 +0C00 +0C00 +0C00 +7800 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR asciitilde +ENCODING 126 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3980 +6D80 +6700 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR house +ENCODING 127 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +1E00 +3300 +6180 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10017 +ENCODING 128 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +1E00 +3300 +3300 +6180 +6180 +6180 +7F80 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10018 +ENCODING 129 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +6000 +6000 +6000 +6000 +7E00 +6300 +6180 +6180 +6180 +6180 +6300 +7E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10019 +ENCODING 130 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7C00 +6600 +6300 +6300 +6300 +6600 +7E00 +6300 +6180 +6180 +6180 +6300 +7E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10020 +ENCODING 131 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10021 +ENCODING 132 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0F00 +1B00 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +7F80 +6180 +6180 +0000 +0000 +ENDCHAR +STARTCHAR afii10022 +ENCODING 133 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +6000 +6000 +6000 +6000 +6000 +7E00 +6000 +6000 +6000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10024 +ENCODING 134 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6D80 +6D80 +6D80 +6D80 +6D80 +3F00 +1E00 +3F00 +6D80 +6D80 +6D80 +6D80 +6D80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10025 +ENCODING 135 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +0180 +0180 +0300 +0E00 +0300 +0180 +0180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10026 +ENCODING 136 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6380 +6380 +6780 +6780 +6D80 +6D80 +7980 +7980 +7180 +7180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10027 +ENCODING 137 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +3300 +1E00 +0000 +6180 +6380 +6780 +6780 +6D80 +6D80 +7980 +7980 +7180 +7180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10028 +ENCODING 138 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6300 +6300 +6600 +6600 +7C00 +6600 +6600 +6300 +6300 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10029 +ENCODING 139 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0F80 +1980 +3180 +3180 +3180 +3180 +3180 +3180 +3180 +3180 +3180 +3180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10030 +ENCODING 140 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +7380 +7380 +7F80 +6D80 +6D80 +6D80 +6D80 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10031 +ENCODING 141 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +7F80 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10032 +ENCODING 142 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10033 +ENCODING 143 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10034 +ENCODING 144 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7E00 +6300 +6180 +6180 +6180 +6180 +6300 +7E00 +6000 +6000 +6000 +6000 +6000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10035 +ENCODING 145 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10036 +ENCODING 146 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7F80 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10037 +ENCODING 147 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +3380 +1F80 +0180 +0180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10038 +ENCODING 148 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0C00 +3F00 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +3F00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10039 +ENCODING 149 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +3300 +3300 +1E00 +1E00 +0C00 +1E00 +1E00 +3300 +3300 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10040 +ENCODING 150 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6300 +6300 +6300 +6300 +6300 +6300 +6300 +6300 +6300 +6300 +6300 +6300 +7F80 +0180 +0180 +0000 +0000 +ENDCHAR +STARTCHAR afii10041 +ENCODING 151 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +3180 +1F80 +0180 +0180 +0180 +0180 +0180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10042 +ENCODING 152 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10043 +ENCODING 153 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +7FC0 +00C0 +00C0 +0000 +0000 +ENDCHAR +STARTCHAR afii10044 +ENCODING 154 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +7800 +1800 +1800 +1800 +1800 +1E00 +1B00 +1980 +1980 +1980 +1980 +1B00 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10045 +ENCODING 155 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +7980 +6D80 +6D80 +6D80 +6D80 +6D80 +6D80 +7980 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10046 +ENCODING 156 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6000 +6000 +6000 +6000 +6000 +7E00 +6300 +6180 +6180 +6180 +6180 +6300 +7E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10047 +ENCODING 157 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +0180 +0180 +0180 +0F80 +0180 +0180 +0180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10048 +ENCODING 158 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6700 +6D80 +6D80 +6D80 +6D80 +6D80 +7D80 +6D80 +6D80 +6D80 +6D80 +6D80 +6700 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10049 +ENCODING 159 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1F80 +3180 +6180 +6180 +6180 +6180 +3180 +1F80 +1980 +3180 +3180 +6180 +6180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10065 +ENCODING 160 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +6180 +0180 +7F80 +C180 +C180 +C180 +C180 +7E80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10066 +ENCODING 161 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0180 +3F00 +6000 +C000 +C000 +DE00 +E300 +C180 +C180 +C180 +C180 +6300 +3E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10067 +ENCODING 162 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FE00 +C300 +C300 +FE00 +C300 +C180 +C180 +C180 +FF00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10068 +ENCODING 163 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F80 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10069 +ENCODING 164 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1F00 +3300 +6300 +6300 +6300 +6300 +6300 +6300 +FF80 +C180 +C180 +0000 +0000 +ENDCHAR +STARTCHAR afii10070 +ENCODING 165 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3E00 +6300 +C180 +FF80 +C000 +C000 +C000 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10072 +ENCODING 166 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C980 +C980 +6B00 +3E00 +7F00 +C980 +C980 +C980 +C980 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10073 +ENCODING 167 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F00 +C180 +0180 +1F00 +0180 +0180 +0180 +C180 +7F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10074 +ENCODING 168 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C180 +C180 +C380 +C780 +CD80 +D980 +F180 +E180 +C180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10075 +ENCODING 169 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3600 +1C00 +0000 +0000 +C180 +C180 +C380 +C780 +CD80 +D980 +F180 +E180 +C180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10076 +ENCODING 170 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6700 +6600 +6C00 +7800 +7800 +7C00 +6600 +6300 +6380 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10077 +ENCODING 171 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1F80 +3180 +6180 +6180 +6180 +6180 +6180 +6180 +C180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10078 +ENCODING 172 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C180 +E380 +F780 +FF80 +DD80 +C980 +C180 +C180 +C180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10079 +ENCODING 173 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C180 +C180 +C180 +FF80 +C180 +C180 +C180 +C180 +C180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10080 +ENCODING 174 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3E00 +6300 +C180 +C180 +C180 +C180 +C180 +6300 +3E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10081 +ENCODING 175 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 176 +ENCODING 176 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +8880 +0000 +2200 +0000 +8880 +0000 +2200 +0000 +8880 +0000 +2200 +0000 +8880 +0000 +2200 +0000 +8880 +0000 +2200 +0000 +ENDCHAR +STARTCHAR 177 +ENCODING 177 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +8880 +5540 +2200 +5540 +8880 +5540 +2200 +5540 +8880 +5540 +2200 +5540 +8880 +5540 +2200 +5540 +8880 +5540 +2200 +5540 +ENDCHAR +STARTCHAR 178 +ENCODING 178 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +AAA0 +5550 +AAA0 +5550 +AAA0 +5550 +AAA0 +5550 +AAA0 +5550 +AAA0 +5550 +AAA0 +5550 +AAA0 +5550 +AAA0 +5550 +AAA0 +5550 +ENDCHAR +STARTCHAR 179 +ENCODING 179 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 180 +ENCODING 180 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FC00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 181 +ENCODING 181 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FC00 +0C00 +0C00 +FC00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 182 +ENCODING 182 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +F300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 183 +ENCODING 183 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FE00 +6700 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 184 +ENCODING 184 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +F800 +0C00 +0C00 +0C00 +FC00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 185 +ENCODING 185 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +F300 +3300 +3300 +F300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 186 +ENCODING 186 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 187 +ENCODING 187 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FE00 +0300 +0300 +E300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 188 +ENCODING 188 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +E300 +0300 +0300 +FE00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 189 +ENCODING 189 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +FE00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 190 +ENCODING 190 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FC00 +0C00 +0C00 +F800 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 191 +ENCODING 191 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +F800 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 192 +ENCODING 192 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +07C0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 193 +ENCODING 193 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FFC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 194 +ENCODING 194 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FFC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 195 +ENCODING 195 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0FC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 196 +ENCODING 196 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FFF0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 197 +ENCODING 197 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FFC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 198 +ENCODING 198 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0FC0 +0C00 +0C00 +0C00 +0FC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 199 +ENCODING 199 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +33C0 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 200 +ENCODING 200 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +31C0 +3000 +3000 +1FC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 201 +ENCODING 201 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1FC0 +3000 +3000 +31C0 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 202 +ENCODING 202 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +F3C0 +0000 +0000 +FFC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 203 +ENCODING 203 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FFC0 +0000 +0000 +F3C0 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 204 +ENCODING 204 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +33C0 +3000 +3000 +33C0 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 205 +ENCODING 205 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FFF0 +0000 +0000 +FFF0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 206 +ENCODING 206 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +F3C0 +0000 +0000 +F3C0 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 207 +ENCODING 207 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FFC0 +0000 +0000 +FFC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 208 +ENCODING 208 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +FFC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 209 +ENCODING 209 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FFC0 +0000 +0000 +FFC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 210 +ENCODING 210 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FFC0 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 211 +ENCODING 211 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +1FC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 212 +ENCODING 212 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0FC0 +0C00 +0C00 +07C0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 213 +ENCODING 213 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +07C0 +0C00 +0C00 +0FC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 214 +ENCODING 214 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1FC0 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 215 +ENCODING 215 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +F3C0 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 216 +ENCODING 216 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FFC0 +0000 +0000 +FFC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 217 +ENCODING 217 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +F800 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 218 +ENCODING 218 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +07C0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 219 +ENCODING 219 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 220 +ENCODING 220 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 221 +ENCODING 221 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +F800 +F800 +F800 +F800 +F800 +F800 +F800 +F800 +F800 +F800 +F800 +F800 +F800 +F800 +F800 +F800 +F800 +F800 +F800 +F800 +ENDCHAR +STARTCHAR 222 +ENCODING 222 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +07C0 +ENDCHAR +STARTCHAR 223 +ENCODING 223 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10082 +ENCODING 224 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6E00 +7300 +6180 +6180 +6180 +6180 +6180 +7300 +6E00 +6000 +6000 +6000 +6000 +ENDCHAR +STARTCHAR afii10083 +ENCODING 225 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +6180 +C000 +C000 +C000 +C000 +C000 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10084 +ENCODING 226 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F80 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10085 +ENCODING 227 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +6380 +3D80 +0180 +C180 +6300 +3E00 +ENDCHAR +STARTCHAR afii10086 +ENCODING 228 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0C00 +0C00 +0C00 +0C00 +0C00 +7F00 +ED80 +CC80 +CC80 +CC80 +CC80 +CC80 +ED80 +7F00 +0C00 +0C00 +0C00 +0000 +ENDCHAR +STARTCHAR afii10087 +ENCODING 229 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C180 +6300 +3E00 +1C00 +1C00 +1C00 +3E00 +6300 +C180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10088 +ENCODING 230 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +FF80 +0180 +0180 +0000 +0000 +ENDCHAR +STARTCHAR afii10089 +ENCODING 231 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C180 +C180 +C180 +C180 +C180 +6180 +3F80 +0180 +0180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10090 +ENCODING 232 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C980 +C980 +C980 +C980 +C980 +C980 +C980 +C980 +FF80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10091 +ENCODING 233 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C980 +C980 +C980 +C980 +C980 +C980 +C980 +C980 +FFC0 +00C0 +00C0 +0000 +0000 +ENDCHAR +STARTCHAR afii10092 +ENCODING 234 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6000 +6000 +6000 +6000 +7F00 +6180 +6180 +6180 +7F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10093 +ENCODING 235 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +C180 +C180 +C180 +F980 +CD80 +CD80 +CD80 +CD80 +F980 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10094 +ENCODING 236 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6000 +6000 +6000 +6000 +7F00 +6180 +6180 +6180 +7F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10095 +ENCODING 237 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7E00 +C300 +8180 +0180 +1F80 +0180 +8180 +C300 +7E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10096 +ENCODING 238 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +CF00 +D980 +D980 +F980 +D980 +D980 +D980 +D980 +CF00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10097 +ENCODING 239 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7F00 +C300 +C300 +C300 +7F00 +3300 +6300 +C300 +C300 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10023 +ENCODING 240 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +3300 +3300 +0000 +0000 +7F80 +6000 +6000 +6000 +7E00 +6000 +6000 +6000 +6000 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR afii10071 +ENCODING 241 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6300 +6300 +0000 +0000 +3E00 +6300 +C180 +FF80 +C000 +C000 +C000 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 242 +ENCODING 242 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +1E00 +3300 +6180 +6000 +6000 +6000 +7C00 +6000 +6000 +6000 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 243 +ENCODING 243 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +6180 +C080 +C000 +FC00 +C000 +C080 +6180 +3F00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 244 +ENCODING 244 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +3300 +3300 +0000 +0000 +7F80 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 245 +ENCODING 245 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +6300 +6300 +0000 +0000 +3C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +7F80 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 246 +ENCODING 246 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +3300 +1E00 +0000 +6180 +6180 +6180 +6180 +3380 +1F80 +0180 +0180 +6180 +3300 +1E00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 247 +ENCODING 247 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +3600 +1C00 +0000 +0000 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +6380 +3D80 +0180 +C180 +6300 +3E00 +ENDCHAR +STARTCHAR 248 +ENCODING 248 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +1E00 +3F00 +3300 +3300 +3F00 +1E00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 249 +ENCODING 249 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1E00 +3F00 +3F00 +3F00 +3F00 +1E00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 250 +ENCODING 250 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0C00 +0C00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 251 +ENCODING 251 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +03C0 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +6300 +3300 +1B00 +0F00 +0700 +0300 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 252 +ENCODING 252 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0380 +06C0 +06C0 +C380 +C000 +E7C0 +E000 +F300 +DB00 +DB00 +CF00 +C700 +C700 +C300 +C300 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 253 +ENCODING 253 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +C0C0 +7F80 +3300 +6180 +6180 +6180 +6180 +3300 +7F80 +C0C0 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 254 +ENCODING 254 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3F00 +3F00 +3F00 +3F00 +3F00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 255 +ENCODING 255 +SWIDTH 480 0 +DWIDTH 10 0 +BBX 10 20 0 -4 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +ENDFONT diff --git a/etc/vga10x24.bdf b/etc/vga10x24.bdf new file mode 100644 index 0000000..5c3f97c --- /dev/null +++ b/etc/vga10x24.bdf @@ -0,0 +1,5536 @@ +STARTFONT 2.1 +COMMENT This font was taken from the dosemu project, resized by +COMMENT bdfresize and finally corrected and sharpened by hand. +COMMENT +COMMENT If you choose any 800x600 pix display you'll have a +COMMENT fullscreen mode when using xdosemu and leaving away the +COMMENT X Windows' title bar and border lines. +COMMENT +COMMENT Have a look to the other fonts of this series as well. +COMMENT Written in December 2002 by Martin Reuber, OFD Kiel, +COMMENT German Fiscal Administration. +COMMENT +COMMENT Update 001 in April 2003: +COMMENT The style of the numeral 2 was changed. +COMMENT +COMMENT Update 003 in January 2004: +COMMENT Small corrections in font name, properties and comments. +FONT -dosemu-vga-Medium-R-Normal--24-240-75-75-c-100-IBM-CP437 +SIZE 24 75 75 +FONTBOUNDINGBOX 10 24 0 -6 +STARTPROPERTIES 22 +POINT_SIZE 240 +WEIGHT 10 +RESOLUTION 100 +RESOLUTION_X 75 +RESOLUTION_Y 75 +QUAD_WIDTH 8 +DEFAULT_CHAR 0 +FONT_ASCENT 18 +FONT_DESCENT 6 +_ORIGINAL_FONT_NAME "vga" +_XMBDFED_INFO "Edited with xmbdfed 4.5." +FOUNDRY "dosemu" +FAMILY_NAME "vga" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "800x600pix_screen_max_size" +PIXEL_SIZE 24 +SPACING "c" +AVERAGE_WIDTH 100 +CHARSET_REGISTRY "IBM" +CHARSET_ENCODING "CP437" +ENDPROPERTIES +CHARS 256 +STARTCHAR 000 +ENCODING 0 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR 001 +ENCODING 1 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 16 0 0 +BITMAP +7F80 +8040 +8040 +B340 +B340 +8040 +8040 +8040 +8040 +A140 +A140 +9240 +8C40 +8040 +8040 +7F80 +ENDCHAR +STARTCHAR 002 +ENCODING 2 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 16 0 0 +BITMAP +7F80 +FFC0 +FFC0 +CCC0 +CCC0 +FFC0 +FFC0 +FFC0 +FFC0 +DEC0 +DEC0 +EDC0 +F3C0 +FFC0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 003 +ENCODING 3 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 10 0 2 +BITMAP +2200 +7700 +FF80 +FF80 +FF80 +FF80 +7F00 +3E00 +1C00 +0800 +ENDCHAR +STARTCHAR 004 +ENCODING 4 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 11 0 1 +BITMAP +0800 +1C00 +3E00 +7F00 +FF80 +FF80 +FF80 +7F00 +3E00 +1C00 +0800 +ENDCHAR +STARTCHAR 005 +ENCODING 5 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 14 0 0 +BITMAP +0C00 +1E00 +3F00 +3F00 +6180 +E1C0 +E1C0 +E1C0 +E1C0 +7F80 +1E00 +1E00 +3F00 +3F00 +ENDCHAR +STARTCHAR 006 +ENCODING 6 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 14 0 0 +BITMAP +0C00 +1E00 +3F00 +7F80 +FFC0 +FFC0 +FFC0 +7F80 +3F00 +1E00 +1E00 +1E00 +3F00 +3F00 +ENDCHAR +STARTCHAR 007 +ENCODING 7 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 7 1 3 +BITMAP +38 +38 +FE +FE +FE +38 +38 +ENDCHAR +STARTCHAR 008 +ENCODING 8 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 24 0 -6 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +F3C0 +E1C0 +E1C0 +F3C0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 009 +ENCODING 9 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 9 1 1 +BITMAP +7E +FF +E7 +E7 +C3 +E7 +E7 +FF +7E +ENDCHAR +STARTCHAR 010 +ENCODING 10 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 24 0 -6 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +EDC0 +DEC0 +DEC0 +DEC0 +DEC0 +DEC0 +EDC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 011 +ENCODING 11 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +1F +03 +07 +0D +19 +30 +7C +FE +C6 +C6 +C6 +C6 +C6 +C6 +FE +7C +ENDCHAR +STARTCHAR 012 +ENCODING 12 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 16 2 0 +BITMAP +78 +FC +CC +CC +CC +CC +CC +FC +78 +30 +30 +30 +FC +FC +30 +30 +ENDCHAR +STARTCHAR 013 +ENCODING 13 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +1E +3F +33 +33 +3F +3E +30 +30 +30 +30 +30 +30 +70 +F0 +F0 +60 +ENDCHAR +STARTCHAR 014 +ENCODING 14 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 17 0 -1 +BITMAP +3E +7F +63 +63 +7F +63 +63 +63 +63 +63 +63 +63 +67 +6F +EF +E6 +C0 +ENDCHAR +STARTCHAR 015 +ENCODING 15 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 14 1 1 +BITMAP +18 +18 +DB +DB +E7 +3C +E7 +E7 +3C +E7 +DB +DB +18 +18 +ENDCHAR +STARTCHAR 016 +ENCODING 16 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 16 0 0 +BITMAP +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FF80 +FF00 +FE00 +FC00 +F800 +F000 +E000 +C000 +ENDCHAR +STARTCHAR 017 +ENCODING 17 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 16 0 0 +BITMAP +0180 +0380 +0780 +0F80 +1F80 +3F80 +7F80 +FF80 +FF80 +7F80 +3F80 +1F80 +0F80 +0780 +0380 +0180 +ENDCHAR +STARTCHAR 018 +ENCODING 18 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 15 1 1 +BITMAP +18 +3C +7E +FF +18 +18 +18 +18 +18 +18 +18 +FF +7E +3C +18 +ENDCHAR +STARTCHAR 019 +ENCODING 19 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 16 1 0 +BITMAP +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +00 +00 +C6 +C6 +ENDCHAR +STARTCHAR 020 +ENCODING 20 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +7F +FF +DB +DB +DB +DB +FB +7B +1B +1B +1B +1B +1B +1B +1B +1B +ENDCHAR +STARTCHAR 021 +ENCODING 21 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +7E +FF +C3 +63 +30 +38 +6C +46 +62 +36 +1C +0C +C6 +C3 +FF +7E +ENDCHAR +STARTCHAR 022 +ENCODING 22 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 7 0 0 +BITMAP +FF +FF +FF +FF +FF +FF +FF +ENDCHAR +STARTCHAR 023 +ENCODING 23 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 16 2 0 +BITMAP +30 +78 +FC +30 +30 +30 +30 +30 +30 +30 +30 +FC +78 +30 +FC +FC +ENDCHAR +STARTCHAR 024 +ENCODING 24 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 16 2 0 +BITMAP +30 +78 +FC +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 025 +ENCODING 25 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 16 2 0 +BITMAP +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +FC +78 +30 +ENDCHAR +STARTCHAR 026 +ENCODING 26 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 6 0 3 +BITMAP +04 +06 +FF +FF +06 +04 +ENDCHAR +STARTCHAR 027 +ENCODING 27 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 6 0 3 +BITMAP +20 +60 +FF +FF +60 +20 +ENDCHAR +STARTCHAR 028 +ENCODING 28 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 7 0 5 +BITMAP +C0 +C0 +C0 +C0 +C0 +FF +FF +ENDCHAR +STARTCHAR 029 +ENCODING 29 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 6 0 3 +BITMAP +2200 +6300 +FF80 +FF80 +6300 +2200 +ENDCHAR +STARTCHAR 030 +ENCODING 30 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 11 1 1 +BITMAP +10 +10 +38 +38 +7C +7C +7C +FE +FE +FE +FE +ENDCHAR +STARTCHAR 031 +ENCODING 31 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 11 1 1 +BITMAP +FE +FE +FE +FE +7C +7C +7C +38 +38 +10 +10 +ENDCHAR +STARTCHAR 032 +ENCODING 32 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR 033 +ENCODING 33 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 4 16 3 0 +BITMAP +60 +F0 +F0 +F0 +F0 +F0 +F0 +60 +60 +60 +60 +60 +00 +00 +60 +60 +ENDCHAR +STARTCHAR 034 +ENCODING 34 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 6 2 12 +BITMAP +CC +CC +CC +CC +CC +48 +ENDCHAR +STARTCHAR 035 +ENCODING 35 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 10 0 3 +BITMAP +3600 +3600 +FF80 +FF80 +3600 +3600 +FF80 +FF80 +3600 +3600 +ENDCHAR +STARTCHAR 036 +ENCODING 36 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 20 0 -2 +BITMAP +08 +08 +7E +FF +CB +CB +C8 +C8 +C8 +FE +7F +0B +0B +0B +CB +CB +FF +7E +08 +08 +ENDCHAR +STARTCHAR 037 +ENCODING 37 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 9 0 3 +BITMAP +C1 +C3 +06 +0C +18 +30 +60 +C3 +83 +ENDCHAR +STARTCHAR 038 +ENCODING 38 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +3C +7E +66 +66 +66 +6C +38 +73 +F7 +DE +CC +C6 +C6 +C6 +FF +77 +ENDCHAR +STARTCHAR 039 +ENCODING 39 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 3 6 3 12 +BITMAP +60 +60 +60 +60 +60 +C0 +ENDCHAR +STARTCHAR 040 +ENCODING 40 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 5 16 2 0 +BITMAP +38 +60 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +60 +38 +ENDCHAR +STARTCHAR 041 +ENCODING 41 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 5 16 2 0 +BITMAP +E0 +30 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +30 +E0 +ENDCHAR +STARTCHAR 042 +ENCODING 42 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 10 0 3 +BITMAP +7700 +7700 +1C00 +8880 +FF80 +FF80 +8880 +1C00 +7700 +7700 +ENDCHAR +STARTCHAR 043 +ENCODING 43 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 8 0 4 +BITMAP +18 +18 +18 +FF +FF +18 +18 +18 +ENDCHAR +STARTCHAR 044 +ENCODING 44 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 3 5 3 -1 +BITMAP +60 +60 +60 +60 +C0 +ENDCHAR +STARTCHAR 045 +ENCODING 45 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 2 0 7 +BITMAP +FF +FF +ENDCHAR +STARTCHAR 046 +ENCODING 46 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 2 2 3 0 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR 047 +ENCODING 47 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 9 0 2 +BITMAP +01 +03 +06 +0C +18 +30 +60 +C0 +80 +ENDCHAR +STARTCHAR 048 +ENCODING 48 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +3C +7E +C3 +C3 +C3 +C3 +DB +DB +DB +DB +C3 +C3 +C3 +C3 +7E +3C +ENDCHAR +STARTCHAR 049 +ENCODING 49 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 16 1 0 +BITMAP +08 +18 +38 +78 +D8 +18 +18 +18 +18 +18 +18 +18 +18 +18 +7E +7E +ENDCHAR +STARTCHAR 050 +ENCODING 50 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +7E +FF +C3 +03 +03 +07 +0E +1C +38 +70 +E0 +C0 +C0 +C3 +FF +FE +ENDCHAR +STARTCHAR 051 +ENCODING 51 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +7E +FF +C3 +03 +03 +03 +1E +1E +03 +03 +03 +03 +03 +C3 +FF +7E +ENDCHAR +STARTCHAR 052 +ENCODING 52 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +06 +0E +1E +3E +76 +E6 +C6 +FF +FF +06 +06 +06 +06 +06 +0F +0F +ENDCHAR +STARTCHAR 053 +ENCODING 53 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +FF +FF +C0 +C0 +C0 +C0 +FE +7F +03 +03 +03 +03 +03 +C3 +FF +7E +ENDCHAR +STARTCHAR 054 +ENCODING 54 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +3E +7E +E0 +C0 +C0 +C0 +FC +FE +C3 +C3 +C3 +C3 +C3 +C3 +FF +7E +ENDCHAR +STARTCHAR 055 +ENCODING 55 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +FF +FF +C3 +03 +03 +07 +0E +1C +38 +70 +60 +60 +60 +60 +60 +60 +ENDCHAR +STARTCHAR 056 +ENCODING 56 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +7E +FF +C3 +C3 +C3 +C3 +FF +7E +C3 +C3 +C3 +C3 +C3 +C3 +FF +7E +ENDCHAR +STARTCHAR 057 +ENCODING 57 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +7E +FF +C3 +C3 +C3 +C3 +FF +7F +03 +03 +03 +03 +03 +C3 +FE +7C +ENDCHAR +STARTCHAR 058 +ENCODING 58 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 2 8 4 2 +BITMAP +C0 +C0 +00 +00 +00 +00 +C0 +C0 +ENDCHAR +STARTCHAR 059 +ENCODING 59 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 3 11 3 -1 +BITMAP +60 +60 +00 +00 +00 +00 +60 +60 +60 +60 +C0 +ENDCHAR +STARTCHAR 060 +ENCODING 60 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 15 0 0 +BITMAP +01 +03 +06 +0C +18 +30 +60 +C0 +60 +30 +18 +0C +06 +03 +01 +ENDCHAR +STARTCHAR 061 +ENCODING 61 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 6 0 5 +BITMAP +FF +FF +00 +00 +FF +FF +ENDCHAR +STARTCHAR 062 +ENCODING 62 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 15 1 0 +BITMAP +80 +C0 +60 +30 +18 +0C +06 +03 +06 +0C +18 +30 +60 +C0 +80 +ENDCHAR +STARTCHAR 063 +ENCODING 63 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 1 0 +BITMAP +7E +FF +C3 +C3 +03 +06 +0C +18 +18 +18 +18 +18 +00 +00 +18 +18 +ENDCHAR +STARTCHAR 064 +ENCODING 64 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 14 1 0 +BITMAP +7E +FF +C3 +C3 +C3 +C3 +CF +DF +DF +DF +CE +C0 +FE +7E +ENDCHAR +STARTCHAR 065 +ENCODING 65 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +18 +3C +66 +C3 +C3 +C3 +C3 +FF +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +ENDCHAR +STARTCHAR 066 +ENCODING 66 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +FE +FF +63 +63 +63 +63 +63 +7E +7F +63 +63 +63 +63 +63 +FF +FE +ENDCHAR +STARTCHAR 067 +ENCODING 67 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +7E +FF +C3 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C3 +FF +7E +ENDCHAR +STARTCHAR 068 +ENCODING 68 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +FC +FE +63 +63 +63 +63 +63 +63 +63 +63 +63 +63 +63 +63 +FE +FC +ENDCHAR +STARTCHAR 069 +ENCODING 69 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 16 0 0 +BITMAP +FF80 +FF80 +6180 +6000 +6000 +6000 +6400 +7C00 +7C00 +6400 +6000 +6000 +6000 +6180 +FF80 +FF80 +ENDCHAR +STARTCHAR 070 +ENCODING 70 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +FF +FF +63 +60 +60 +60 +64 +7C +7C +64 +60 +60 +60 +60 +F0 +F0 +ENDCHAR +STARTCHAR 071 +ENCODING 71 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +7E +FF +C3 +C0 +C0 +C0 +C0 +C0 +CF +CF +C3 +C3 +C3 +C3 +FF +7B +ENDCHAR +STARTCHAR 072 +ENCODING 72 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +ENDCHAR +STARTCHAR 073 +ENCODING 73 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 4 16 3 0 +BITMAP +F0 +F0 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +F0 +F0 +ENDCHAR +STARTCHAR 074 +ENCODING 74 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +0F +0F +06 +06 +06 +06 +06 +06 +06 +06 +06 +C6 +C6 +C6 +FE +7C +ENDCHAR +STARTCHAR 075 +ENCODING 75 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +E3 +E3 +63 +63 +63 +67 +6E +7C +7C +6E +67 +63 +63 +63 +E3 +E3 +ENDCHAR +STARTCHAR 076 +ENCODING 76 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +F0 +F0 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +63 +FF +FF +ENDCHAR +STARTCHAR 077 +ENCODING 77 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +C3 +E7 +FF +FF +DB +DB +DB +DB +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +ENDCHAR +STARTCHAR 078 +ENCODING 78 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +C3 +E3 +F3 +FB +DF +CF +C7 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +ENDCHAR +STARTCHAR 079 +ENCODING 79 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +7E +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +7E +ENDCHAR +STARTCHAR 080 +ENCODING 80 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +FE +FF +63 +63 +63 +63 +63 +7F +7E +60 +60 +60 +60 +60 +F0 +F0 +ENDCHAR +STARTCHAR 081 +ENCODING 81 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 20 0 -4 +BITMAP +7E +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +DB +DB +DB +FF +7E +0C +0C +0F +0F +ENDCHAR +STARTCHAR 082 +ENCODING 82 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +FE +FF +63 +63 +63 +63 +63 +7E +7C +66 +63 +63 +63 +63 +E3 +E3 +ENDCHAR +STARTCHAR 083 +ENCODING 83 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +7E +FF +C3 +C0 +C0 +C0 +C0 +FE +7F +03 +03 +03 +03 +C3 +FF +7E +ENDCHAR +STARTCHAR 084 +ENCODING 84 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 1 0 +BITMAP +FF +FF +DB +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +3C +3C +ENDCHAR +STARTCHAR 085 +ENCODING 85 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +7E +ENDCHAR +STARTCHAR 086 +ENCODING 86 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +66 +3C +18 +ENDCHAR +STARTCHAR 087 +ENCODING 87 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +DB +DB +DB +DB +FF +E7 +66 +ENDCHAR +STARTCHAR 088 +ENCODING 88 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +C3 +C3 +C3 +66 +66 +3C +3C +18 +18 +3C +3C +66 +66 +C3 +C3 +C3 +ENDCHAR +STARTCHAR 089 +ENCODING 89 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +C3 +C3 +C3 +C3 +C3 +E7 +7E +3C +18 +18 +18 +18 +18 +18 +3C +3C +ENDCHAR +STARTCHAR 090 +ENCODING 90 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +FF +FF +C3 +03 +03 +07 +0E +1C +38 +70 +E0 +C0 +C0 +C3 +FF +FF +ENDCHAR +STARTCHAR 091 +ENCODING 91 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 5 16 2 0 +BITMAP +F8 +F8 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +F8 +F8 +ENDCHAR +STARTCHAR 092 +ENCODING 92 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 9 0 2 +BITMAP +80 +C0 +60 +30 +18 +0C +06 +03 +01 +ENDCHAR +STARTCHAR 093 +ENCODING 93 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 5 16 2 0 +BITMAP +F8 +F8 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +F8 +F8 +ENDCHAR +STARTCHAR 094 +ENCODING 94 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 4 0 12 +BITMAP +18 +3C +66 +C3 +ENDCHAR +STARTCHAR 095 +ENCODING 95 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 2 0 -3 +BITMAP +FFC0 +FFC0 +ENDCHAR +STARTCHAR 096 +ENCODING 96 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 3 6 3 12 +BITMAP +C0 +C0 +C0 +C0 +C0 +60 +ENDCHAR +STARTCHAR 097 +ENCODING 97 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +7C +7E +06 +06 +7E +FE +C6 +C6 +C6 +FF +77 +ENDCHAR +STARTCHAR 098 +ENCODING 98 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +E0 +E0 +60 +60 +60 +7C +7E +63 +63 +63 +63 +63 +63 +63 +7F +7E +ENDCHAR +STARTCHAR 099 +ENCODING 99 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +7E +FF +C3 +C0 +C0 +C0 +C0 +C0 +C3 +FF +7E +ENDCHAR +STARTCHAR 100 +ENCODING 100 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +0E +0E +06 +06 +06 +3E +7E +C6 +C6 +C6 +C6 +C6 +C6 +C6 +FF +7B +ENDCHAR +STARTCHAR 101 +ENCODING 101 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +7E +FF +C3 +C3 +FF +FF +C0 +C0 +C3 +FF +7E +ENDCHAR +STARTCHAR 102 +ENCODING 102 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 16 1 0 +BITMAP +1C +3E +32 +30 +30 +FC +FC +30 +30 +30 +30 +30 +30 +30 +78 +78 +ENDCHAR +STARTCHAR 103 +ENCODING 103 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 -5 +BITMAP +7B +FF +C6 +C6 +C6 +C6 +C6 +C6 +C6 +FE +7E +06 +06 +C6 +FE +7C +ENDCHAR +STARTCHAR 104 +ENCODING 104 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +E0 +E0 +60 +60 +60 +6E +7F +73 +63 +63 +63 +63 +63 +63 +E3 +E3 +ENDCHAR +STARTCHAR 105 +ENCODING 105 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 4 15 2 0 +BITMAP +60 +60 +00 +00 +E0 +E0 +60 +60 +60 +60 +60 +60 +60 +F0 +F0 +ENDCHAR +STARTCHAR 106 +ENCODING 106 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 20 1 -5 +BITMAP +0C +0C +00 +00 +1C +1C +0C +0C +0C +0C +0C +0C +0C +0C +0C +CC +CC +CC +FC +78 +ENDCHAR +STARTCHAR 107 +ENCODING 107 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +E0 +E0 +60 +60 +63 +63 +66 +7C +7C +66 +63 +63 +63 +63 +E3 +E3 +ENDCHAR +STARTCHAR 108 +ENCODING 108 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 4 16 2 0 +BITMAP +E0 +E0 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +F0 +F0 +ENDCHAR +STARTCHAR 109 +ENCODING 109 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +66 +FF +DB +DB +DB +DB +DB +DB +C3 +C3 +C3 +ENDCHAR +STARTCHAR 110 +ENCODING 110 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +CE +FF +73 +63 +63 +63 +63 +63 +63 +63 +63 +ENDCHAR +STARTCHAR 111 +ENCODING 111 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +7E +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +7E +ENDCHAR +STARTCHAR 112 +ENCODING 112 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 -5 +BITMAP +DE +FF +63 +63 +63 +63 +63 +63 +63 +7F +7E +60 +60 +60 +F0 +F0 +ENDCHAR +STARTCHAR 113 +ENCODING 113 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 -5 +BITMAP +77 +FF +C6 +C6 +C6 +C6 +C6 +C6 +C6 +FE +7E +06 +06 +06 +0F +0F +ENDCHAR +STARTCHAR 114 +ENCODING 114 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +EE +FF +63 +61 +60 +60 +60 +60 +60 +F0 +F0 +ENDCHAR +STARTCHAR 115 +ENCODING 115 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +7E +FF +C3 +C0 +7C +3E +03 +03 +C3 +FF +7E +ENDCHAR +STARTCHAR 116 +ENCODING 116 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 16 1 0 +BITMAP +10 +30 +30 +30 +30 +FC +FC +30 +30 +30 +30 +30 +30 +36 +3E +1C +ENDCHAR +STARTCHAR 117 +ENCODING 117 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +FF +7B +ENDCHAR +STARTCHAR 118 +ENCODING 118 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +66 +3C +18 +ENDCHAR +STARTCHAR 119 +ENCODING 119 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +C3 +C3 +C3 +C3 +C3 +DB +DB +DB +DB +FF +66 +ENDCHAR +STARTCHAR 120 +ENCODING 120 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +C3 +C3 +C3 +66 +3C +18 +3C +66 +C3 +C3 +C3 +ENDCHAR +STARTCHAR 121 +ENCODING 121 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 -5 +BITMAP +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +7F +03 +03 +03 +FE +FC +ENDCHAR +STARTCHAR 122 +ENCODING 122 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +FF +FF +C3 +06 +0C +18 +30 +60 +C3 +FF +FF +ENDCHAR +STARTCHAR 123 +ENCODING 123 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 16 1 0 +BITMAP +0E +1E +30 +30 +30 +30 +30 +E0 +E0 +30 +30 +30 +30 +30 +1E +0E +ENDCHAR +STARTCHAR 124 +ENCODING 124 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 2 16 4 0 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +00 +00 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 125 +ENCODING 125 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 16 2 0 +BITMAP +E0 +F0 +18 +18 +18 +18 +18 +0E +0E +18 +18 +18 +18 +18 +F0 +E0 +ENDCHAR +STARTCHAR 126 +ENCODING 126 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 4 0 12 +BITMAP +3180 +7B00 +CE00 +8400 +ENDCHAR +STARTCHAR 127 +ENCODING 127 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +18 +3C +66 +C3 +C3 +C3 +C3 +C3 +C3 +FF +FF +ENDCHAR +STARTCHAR 128 +ENCODING 128 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 18 0 -3 +BITMAP +7E +FF +C3 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C3 +FF +7E +06 +03 +3F +3E +ENDCHAR +STARTCHAR 129 +ENCODING 129 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 15 0 0 +BITMAP +C6 +C6 +00 +00 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +FF +7B +ENDCHAR +STARTCHAR 130 +ENCODING 130 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +0C +18 +30 +00 +00 +7E +FF +C3 +C3 +FF +FF +C0 +C0 +C3 +FF +7E +ENDCHAR +STARTCHAR 131 +ENCODING 131 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +18 +3C +66 +00 +00 +7C +7E +06 +06 +7E +FE +C6 +C6 +C6 +FF +7B +ENDCHAR +STARTCHAR 132 +ENCODING 132 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 15 0 0 +BITMAP +66 +66 +00 +00 +7C +7E +06 +06 +7E +FE +C6 +C6 +C6 +FF +7B +ENDCHAR +STARTCHAR 133 +ENCODING 133 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +60 +30 +18 +00 +00 +7C +7E +06 +06 +7E +FE +C6 +C6 +C6 +FF +7B +ENDCHAR +STARTCHAR 134 +ENCODING 134 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +3C +66 +66 +3C +00 +7C +7E +06 +06 +7E +FE +C6 +C6 +C6 +FF +7B +ENDCHAR +STARTCHAR 135 +ENCODING 135 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 -5 +BITMAP +7E +FF +C3 +C0 +C0 +C0 +C0 +C0 +C3 +FF +7C +0E +06 +06 +7E +7C +ENDCHAR +STARTCHAR 136 +ENCODING 136 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +18 +3C +66 +00 +00 +7E +FF +C3 +C3 +FF +FF +C0 +C0 +C3 +FF +7E +ENDCHAR +STARTCHAR 137 +ENCODING 137 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 15 0 0 +BITMAP +66 +66 +00 +00 +7E +FF +C3 +C3 +FF +FF +C0 +C0 +C3 +FF +7E +ENDCHAR +STARTCHAR 138 +ENCODING 138 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +30 +18 +0C +00 +00 +7E +FF +C3 +C3 +FF +FF +C0 +C0 +C3 +FF +7E +ENDCHAR +STARTCHAR 139 +ENCODING 139 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 15 1 0 +BITMAP +CC +CC +00 +00 +70 +70 +30 +30 +30 +30 +30 +30 +30 +78 +78 +ENDCHAR +STARTCHAR 140 +ENCODING 140 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 16 1 0 +BITMAP +30 +78 +CC +00 +00 +70 +70 +30 +30 +30 +30 +30 +30 +30 +78 +78 +ENDCHAR +STARTCHAR 141 +ENCODING 141 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 5 16 2 0 +BITMAP +C0 +60 +30 +00 +00 +70 +70 +30 +30 +30 +30 +30 +30 +30 +78 +78 +ENDCHAR +STARTCHAR 142 +ENCODING 142 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 18 0 0 +BITMAP +C3 +C3 +18 +3C +66 +C3 +C3 +C3 +C3 +FF +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +ENDCHAR +STARTCHAR 143 +ENCODING 143 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 18 0 0 +BITMAP +3C +24 +3C +18 +3C +66 +C3 +C3 +C3 +C3 +FF +FF +C3 +C3 +C3 +C3 +C3 +C3 +ENDCHAR +STARTCHAR 144 +ENCODING 144 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 18 0 0 +BITMAP +0C +18 +30 +00 +00 +FF +FF +63 +60 +64 +7C +7C +64 +60 +60 +63 +FF +FF +ENDCHAR +STARTCHAR 145 +ENCODING 145 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +C6 +FF +33 +33 +1B +3F +7E +C6 +C6 +FF +77 +ENDCHAR +STARTCHAR 146 +ENCODING 146 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +3F +7F +C6 +C6 +C6 +FF +FF +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C7 +C7 +ENDCHAR +STARTCHAR 147 +ENCODING 147 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +18 +3C +66 +00 +00 +7E +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +7E +ENDCHAR +STARTCHAR 148 +ENCODING 148 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 15 0 0 +BITMAP +66 +66 +00 +00 +7E +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +7E +ENDCHAR +STARTCHAR 149 +ENCODING 149 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +30 +18 +0C +00 +00 +7E +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +7E +ENDCHAR +STARTCHAR 150 +ENCODING 150 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +10 +38 +6C +00 +00 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +FF +7B +ENDCHAR +STARTCHAR 151 +ENCODING 151 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +60 +38 +0C +00 +00 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +FF +7B +ENDCHAR +STARTCHAR 152 +ENCODING 152 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 20 0 -5 +BITMAP +C3 +C3 +00 +00 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +7F +03 +03 +03 +FE +FC +ENDCHAR +STARTCHAR 153 +ENCODING 153 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 18 0 0 +BITMAP +66 +66 +00 +00 +7E +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +7E +ENDCHAR +STARTCHAR 154 +ENCODING 154 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 18 0 0 +BITMAP +C3 +C3 +00 +00 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +7E +ENDCHAR +STARTCHAR 155 +ENCODING 155 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 20 0 -2 +BITMAP +08 +08 +7E +FF +CB +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +CB +FF +7E +08 +08 +ENDCHAR +STARTCHAR 156 +ENCODING 156 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 17 0 0 +BITMAP +1C +3E +63 +63 +63 +60 +F0 +F0 +60 +60 +60 +60 +60 +63 +63 +FF +FE +ENDCHAR +STARTCHAR 157 +ENCODING 157 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 1 0 +BITMAP +C3 +C3 +C3 +C3 +C3 +E7 +7E +18 +7E +18 +7E +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 158 +ENCODING 158 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 17 0 0 +BITMAP +F8 +FC +C6 +C6 +FC +F8 +C2 +C2 +C6 +C6 +CF +C6 +C6 +C6 +C6 +C6 +C3 +ENDCHAR +STARTCHAR 159 +ENCODING 159 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 19 0 -3 +BITMAP +0E +1F +33 +31 +30 +30 +30 +FC +FC +30 +30 +30 +30 +30 +30 +30 +B0 +F0 +60 +ENDCHAR +STARTCHAR 160 +ENCODING 160 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 17 0 0 +BITMAP +06 +0C +18 +30 +00 +00 +7C +7E +06 +06 +7E +FE +C6 +C6 +C6 +FF +7B +ENDCHAR +STARTCHAR 161 +ENCODING 161 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 4 16 2 0 +BITMAP +30 +60 +C0 +00 +00 +E0 +E0 +60 +60 +60 +60 +60 +60 +60 +F0 +F0 +ENDCHAR +STARTCHAR 162 +ENCODING 162 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +06 +0C +18 +00 +00 +7E +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +7E +ENDCHAR +STARTCHAR 163 +ENCODING 163 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +0C +18 +30 +00 +00 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +FF +7B +ENDCHAR +STARTCHAR 164 +ENCODING 164 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +11 +3B +66 +00 +00 +EE +FF +63 +63 +63 +63 +63 +63 +63 +63 +63 +ENDCHAR +STARTCHAR 165 +ENCODING 165 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 18 0 0 +BITMAP +11 +7B +C6 +00 +C3 +E3 +F3 +DB +CF +C7 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +ENDCHAR +STARTCHAR 166 +ENCODING 166 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 11 1 5 +BITMAP +7C +FC +CC +CC +CC +FE +7E +00 +00 +FE +FE +ENDCHAR +STARTCHAR 167 +ENCODING 167 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 11 1 5 +BITMAP +7C +FE +C6 +C6 +C6 +FE +7C +00 +00 +FE +FE +ENDCHAR +STARTCHAR 168 +ENCODING 168 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +18 +18 +00 +00 +18 +18 +18 +18 +18 +30 +60 +C0 +C3 +C3 +FF +7E +ENDCHAR +STARTCHAR 169 +ENCODING 169 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 9 0 0 +BITMAP +FF +FF +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 170 +ENCODING 170 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 9 0 0 +BITMAP +FF +FF +03 +03 +03 +03 +03 +03 +03 +ENDCHAR +STARTCHAR 171 +ENCODING 171 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 17 0 0 +BITMAP +6000 +E000 +6000 +6000 +6180 +6300 +6600 +6C00 +1800 +3700 +6F80 +C980 +0380 +0700 +0C00 +0F80 +0F80 +ENDCHAR +STARTCHAR 172 +ENCODING 172 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 17 0 0 +BITMAP +6000 +E000 +6000 +6000 +6180 +6300 +6600 +6D00 +1B00 +3700 +6F00 +DB00 +1F80 +1F80 +0300 +0300 +0300 +ENDCHAR +STARTCHAR 173 +ENCODING 173 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 4 16 3 0 +BITMAP +60 +60 +00 +00 +60 +60 +60 +60 +60 +F0 +F0 +F0 +F0 +F0 +F0 +60 +ENDCHAR +STARTCHAR 174 +ENCODING 174 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 8 0 2 +BITMAP +1980 +3300 +6600 +CC00 +CC00 +6600 +3300 +1980 +ENDCHAR +STARTCHAR 175 +ENCODING 175 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 8 0 2 +BITMAP +CC00 +6600 +3300 +1980 +1980 +3300 +6600 +CC00 +ENDCHAR +STARTCHAR 176 +ENCODING 176 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 24 0 -6 +BITMAP +2200 +8880 +2200 +8880 +2200 +8880 +2200 +8880 +2200 +8880 +2200 +8880 +2200 +8880 +2200 +8880 +2200 +8880 +2200 +8880 +2200 +8880 +2200 +8880 +ENDCHAR +STARTCHAR 177 +ENCODING 177 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 24 0 -6 +BITMAP +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +5540 +AA80 +ENDCHAR +STARTCHAR 178 +ENCODING 178 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 24 0 -6 +BITMAP +DDC0 +7740 +DDC0 +7740 +DDC0 +7740 +DDC0 +7740 +DDC0 +7740 +DDC0 +7740 +DDC0 +7740 +DDC0 +7740 +DDC0 +7740 +DDC0 +7740 +DDC0 +7740 +DDC0 +7740 +ENDCHAR +STARTCHAR 179 +ENCODING 179 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 2 24 4 -6 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 180 +ENCODING 180 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 24 0 -6 +BITMAP +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +FC +FC +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +ENDCHAR +STARTCHAR 181 +ENCODING 181 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 24 0 -6 +BITMAP +0C +0C +0C +0C +0C +0C +0C +0C +0C +FC +FC +0C +0C +FC +FC +0C +0C +0C +0C +0C +0C +0C +0C +0C +ENDCHAR +STARTCHAR 182 +ENCODING 182 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 24 0 -6 +BITMAP +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +F3 +F3 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +ENDCHAR +STARTCHAR 183 +ENCODING 183 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 13 0 -6 +BITMAP +FF +FF +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +ENDCHAR +STARTCHAR 184 +ENCODING 184 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 15 0 -6 +BITMAP +FC +FC +0C +0C +FC +FC +0C +0C +0C +0C +0C +0C +0C +0C +0C +ENDCHAR +STARTCHAR 185 +ENCODING 185 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 24 0 -6 +BITMAP +33 +33 +33 +33 +33 +33 +33 +33 +33 +F3 +F3 +03 +03 +F3 +F3 +33 +33 +33 +33 +33 +33 +33 +33 +33 +ENDCHAR +STARTCHAR 186 +ENCODING 186 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 24 2 -6 +BITMAP +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +ENDCHAR +STARTCHAR 187 +ENCODING 187 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 15 0 -6 +BITMAP +FF +FF +03 +03 +F3 +F3 +33 +33 +33 +33 +33 +33 +33 +33 +33 +ENDCHAR +STARTCHAR 188 +ENCODING 188 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 15 0 3 +BITMAP +33 +33 +33 +33 +33 +33 +33 +33 +33 +F3 +F3 +03 +03 +FF +FF +ENDCHAR +STARTCHAR 189 +ENCODING 189 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 13 0 5 +BITMAP +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +33 +FF +FF +ENDCHAR +STARTCHAR 190 +ENCODING 190 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 15 0 3 +BITMAP +0C +0C +0C +0C +0C +0C +0C +0C +0C +FC +FC +0C +0C +FC +FC +ENDCHAR +STARTCHAR 191 +ENCODING 191 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 13 0 -6 +BITMAP +FC +FC +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +ENDCHAR +STARTCHAR 192 +ENCODING 192 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 13 4 5 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FC +FC +ENDCHAR +STARTCHAR 193 +ENCODING 193 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 13 0 5 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 194 +ENCODING 194 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 13 0 -6 +BITMAP +FFC0 +FFC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 195 +ENCODING 195 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 24 4 -6 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FC +FC +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 196 +ENCODING 196 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 2 0 5 +BITMAP +FFC0 +FFC0 +ENDCHAR +STARTCHAR 197 +ENCODING 197 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 24 0 -6 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FFC0 +FFC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 198 +ENCODING 198 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 24 4 -6 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FC +FC +C0 +C0 +FC +FC +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 199 +ENCODING 199 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 24 2 -6 +BITMAP +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CF +CF +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +ENDCHAR +STARTCHAR 200 +ENCODING 200 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 15 2 3 +BITMAP +CC +CC +CC +CC +CC +CC +CC +CC +CC +CF +CF +C0 +C0 +FF +FF +ENDCHAR +STARTCHAR 201 +ENCODING 201 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 15 2 -6 +BITMAP +FF +FF +C0 +C0 +CF +CF +CC +CC +CC +CC +CC +CC +CC +CC +CC +ENDCHAR +STARTCHAR 202 +ENCODING 202 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 15 0 3 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +F3C0 +F3C0 +0000 +0000 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 203 +ENCODING 203 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 15 0 -6 +BITMAP +FFC0 +FFC0 +0000 +0000 +F3C0 +F3C0 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 204 +ENCODING 204 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 24 2 -6 +BITMAP +CC +CC +CC +CC +CC +CC +CC +CC +CC +CF +CF +C0 +C0 +CF +CF +CC +CC +CC +CC +CC +CC +CC +CC +CC +ENDCHAR +STARTCHAR 205 +ENCODING 205 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 6 0 3 +BITMAP +FFC0 +FFC0 +0000 +0000 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 206 +ENCODING 206 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 24 0 -6 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +F3C0 +F3C0 +0000 +0000 +F3C0 +F3C0 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 207 +ENCODING 207 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 15 0 3 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FFC0 +FFC0 +0000 +0000 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 208 +ENCODING 208 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 13 0 5 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 209 +ENCODING 209 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 15 0 -6 +BITMAP +FFC0 +FFC0 +0000 +0000 +FFC0 +FFC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 210 +ENCODING 210 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 13 0 -6 +BITMAP +FFC0 +FFC0 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 211 +ENCODING 211 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 13 2 5 +BITMAP +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +FF +FF +ENDCHAR +STARTCHAR 212 +ENCODING 212 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 15 4 3 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FC +FC +C0 +C0 +FC +FC +ENDCHAR +STARTCHAR 213 +ENCODING 213 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 15 4 -6 +BITMAP +FC +FC +C0 +C0 +FC +FC +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 214 +ENCODING 214 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 13 2 -6 +BITMAP +FF +FF +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +ENDCHAR +STARTCHAR 215 +ENCODING 215 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 24 0 -6 +BITMAP +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +FFC0 +FFC0 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 216 +ENCODING 216 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 24 0 -6 +BITMAP +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FFC0 +FFC0 +0C00 +0C00 +FFC0 +FFC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 217 +ENCODING 217 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 13 0 5 +BITMAP +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +FC +FC +ENDCHAR +STARTCHAR 218 +ENCODING 218 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 13 4 -6 +BITMAP +FC +FC +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 219 +ENCODING 219 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 24 0 -6 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 220 +ENCODING 220 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 12 0 -6 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 221 +ENCODING 221 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 5 24 0 -6 +BITMAP +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +ENDCHAR +STARTCHAR 222 +ENCODING 222 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 5 24 5 -6 +BITMAP +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +ENDCHAR +STARTCHAR 223 +ENCODING 223 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 12 0 6 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 224 +ENCODING 224 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 11 0 0 +BITMAP +7B +FF +CE +CC +CC +CC +CC +CC +CE +FF +7B +ENDCHAR +STARTCHAR 225 +ENCODING 225 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +7C +FE +C6 +C6 +C6 +C6 +DE +DC +C6 +C3 +C3 +C3 +C3 +C3 +CF +CE +ENDCHAR +STARTCHAR 226 +ENCODING 226 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 16 0 0 +BITMAP +FF +FF +C3 +C3 +C3 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 227 +ENCODING 227 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 13 0 0 +BITMAP +FF +FF +66 +66 +66 +66 +66 +66 +66 +66 +66 +66 +66 +ENDCHAR +STARTCHAR 228 +ENCODING 228 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 14 0 0 +BITMAP +FF +FF +C3 +E0 +70 +38 +1C +1C +38 +70 +E0 +C3 +FF +FF +ENDCHAR +STARTCHAR 229 +ENCODING 229 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 12 0 0 +BITMAP +7F +FF +CC +CC +CC +CC +CC +CC +CC +CC +FC +78 +ENDCHAR +STARTCHAR 230 +ENCODING 230 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 14 0 -2 +BITMAP +33 +33 +33 +33 +33 +33 +33 +33 +33 +3E +30 +30 +F0 +E0 +ENDCHAR +STARTCHAR 231 +ENCODING 231 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 13 0 0 +BITMAP +7B +DF +8E +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +ENDCHAR +STARTCHAR 232 +ENCODING 232 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 14 1 0 +BITMAP +FE +FE +38 +7C +C6 +C6 +C6 +C6 +C6 +C6 +7C +38 +FE +FE +ENDCHAR +STARTCHAR 233 +ENCODING 233 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 14 0 0 +BITMAP +3C +7E +E7 +C3 +C3 +C3 +FF +FF +C3 +C3 +C3 +E7 +7E +3C +ENDCHAR +STARTCHAR 234 +ENCODING 234 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 9 16 0 0 +BITMAP +0800 +1C00 +3600 +6300 +C180 +C180 +C180 +6300 +3600 +3600 +3600 +3600 +3600 +3600 +7700 +7700 +ENDCHAR +STARTCHAR 235 +ENCODING 235 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 16 1 0 +BITMAP +3E +7E +40 +70 +38 +0C +3C +7E +C6 +C6 +C6 +C6 +C6 +C6 +FE +7C +ENDCHAR +STARTCHAR 236 +ENCODING 236 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 10 0 2 +BITMAP +7E +FF +DB +DB +DB +DB +DB +DB +FF +7E +ENDCHAR +STARTCHAR 237 +ENCODING 237 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 14 0 0 +BITMAP +03 +06 +7E +FF +DB +DB +DB +DB +DB +DB +FF +7E +60 +C0 +ENDCHAR +STARTCHAR 238 +ENCODING 238 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 16 1 0 +BITMAP +1E +7E +E0 +C0 +C0 +C0 +C0 +FE +FE +C0 +C0 +C0 +C0 +E0 +7E +1E +ENDCHAR +STARTCHAR 239 +ENCODING 239 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 14 0 0 +BITMAP +7E +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +ENDCHAR +STARTCHAR 240 +ENCODING 240 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 10 0 3 +BITMAP +FF +FF +00 +00 +FF +FF +00 +00 +FF +FF +ENDCHAR +STARTCHAR 241 +ENCODING 241 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 12 0 0 +BITMAP +18 +18 +18 +FF +FF +18 +18 +18 +00 +00 +FF +FF +ENDCHAR +STARTCHAR 242 +ENCODING 242 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 14 1 0 +BITMAP +C0 +60 +30 +18 +0C +06 +0C +18 +30 +60 +C0 +00 +FE +FE +ENDCHAR +STARTCHAR 243 +ENCODING 243 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 14 1 0 +BITMAP +06 +0C +18 +30 +60 +C0 +60 +30 +18 +0C +06 +00 +FE +FE +ENDCHAR +STARTCHAR 244 +ENCODING 244 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 22 4 -6 +BITMAP +78 +FC +CC +CC +CC +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 245 +ENCODING 245 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 22 0 -4 +BITMAP +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +CC +CC +CC +FC +78 +ENDCHAR +STARTCHAR 246 +ENCODING 246 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 10 0 3 +BITMAP +18 +18 +00 +00 +FF +FF +00 +00 +18 +18 +ENDCHAR +STARTCHAR 247 +ENCODING 247 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 7 0 4 +BITMAP +73 +DE +84 +00 +73 +DE +84 +ENDCHAR +STARTCHAR 248 +ENCODING 248 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 6 7 2 9 +BITMAP +78 +FC +CC +CC +CC +FC +78 +ENDCHAR +STARTCHAR 249 +ENCODING 249 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 3 4 4 4 +BITMAP +E0 +E0 +E0 +E0 +ENDCHAR +STARTCHAR 250 +ENCODING 250 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 3 3 4 4 +BITMAP +E0 +E0 +E0 +ENDCHAR +STARTCHAR 251 +ENCODING 251 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 10 17 0 0 +BITMAP +03C0 +03C0 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +F300 +F300 +3300 +3300 +3300 +3300 +3F00 +1E00 +ENDCHAR +STARTCHAR 252 +ENCODING 252 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 8 9 0 8 +BITMAP +EE +FF +63 +63 +63 +63 +63 +63 +63 +ENDCHAR +STARTCHAR 253 +ENCODING 253 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 10 0 7 +BITMAP +7C +FE +C6 +0C +18 +30 +60 +C6 +FE +FE +ENDCHAR +STARTCHAR 254 +ENCODING 254 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 7 11 1 1 +BITMAP +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +ENDCHAR +STARTCHAR 255 +ENCODING 255 +SWIDTH 400 0 +DWIDTH 10 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +ENDFONT diff --git a/etc/vga11x19.bdf b/etc/vga11x19.bdf new file mode 100644 index 0000000..60d399d --- /dev/null +++ b/etc/vga11x19.bdf @@ -0,0 +1,5464 @@ +STARTFONT 2.1 +COMMENT This is vga11x19 font, adapted from -B&H-LucidaTypewriter- +COMMENT Medium-R-Normal-Sans-18-180-75-75-M-110-ISO8859-1 and dosemu vga +COMMENT fonts by Pasi Eronen. +FONT -dosemu-VGA-Medium-R-Normal--19-190-75-75-C-100-IBM-CP437 +SIZE 18 75 75 +FONTBOUNDINGBOX 11 19 0 -3 +STARTPROPERTIES 4 +COPYRIGHT "Copyright Bigelow & Holmes 1986, 1985." +DEFAULT_CHAR 0 +FONT_ASCENT 16 +FONT_DESCENT 3 +ENDPROPERTIES +CHARS 256 +STARTCHAR space0 +ENCODING 0 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR vga1 +ENCODING 1 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +7e +81 +a5 +81 +81 +a5 +99 +81 +81 +7e +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga2 +ENCODING 2 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +7e +ff +db +ff +ff +db +e7 +ff +ff +7e +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga3 +ENCODING 3 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +6c +fe +fe +fe +fe +7c +38 +10 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga4 +ENCODING 4 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +10 +38 +7c +fe +7c +38 +10 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga5 +ENCODING 5 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +18 +3c +3c +e7 +e7 +e7 +18 +18 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga6 +ENCODING 6 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +18 +3c +7e +ff +ff +7e +18 +18 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga7 +ENCODING 7 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +00 +00 +18 +3c +3c +18 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga8 +ENCODING 8 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +ff +ff +ff +ff +ff +ff +e7 +c3 +c3 +e7 +ff +ff +ff +ff +ff +ff +ENDCHAR +STARTCHAR vga9 +ENCODING 9 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +00 +3c +66 +42 +42 +66 +3c +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga10 +ENCODING 10 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +ff +ff +ff +ff +ff +c3 +99 +bd +bd +99 +c3 +ff +ff +ff +ff +ff +ENDCHAR +STARTCHAR vga11 +ENCODING 11 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +1e +06 +0e +1a +78 +cc +cc +cc +cc +78 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga12 +ENCODING 12 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +3c +66 +66 +66 +66 +3c +18 +7e +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga13 +ENCODING 13 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +3f +33 +3f +30 +30 +30 +30 +70 +f0 +e0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga14 +ENCODING 14 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +7f +63 +7f +63 +63 +63 +63 +67 +e7 +e6 +c0 +00 +00 +00 +ENDCHAR +STARTCHAR vga15 +ENCODING 15 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +18 +18 +db +3c +e7 +3c +db +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR righttriangle +ENCODING 16 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +2000 +3000 +3800 +3c00 +3e00 +3f00 +3e00 +3c00 +3800 +3000 +2000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR lefttriangle +ENCODING 17 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0080 +0180 +0380 +0780 +0f80 +1f80 +0f80 +0780 +0380 +0180 +0080 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR updownarrow +ENCODING 18 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0400 +0e00 +1500 +2480 +0400 +0400 +0400 +0400 +0400 +2480 +1500 +0e00 +0400 +0000 +0000 +0000 +ENDCHAR +STARTCHAR vga19 +ENCODING 19 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +66 +66 +66 +66 +66 +66 +66 +00 +66 +66 +00 +00 +00 +00 +ENDCHAR +STARTCHAR paragraph +ENCODING 20 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 15 1 -2 +BITMAP +7f +f9 +f9 +f9 +79 +39 +09 +09 +09 +09 +09 +09 +09 +09 +09 +ENDCHAR +STARTCHAR section +ENCODING 21 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 15 2 -2 +BITMAP +7e +e3 +c0 +e0 +78 +5e +c7 +c3 +e3 +7a +1e +07 +03 +c7 +7e +ENDCHAR +STARTCHAR vga22 +ENCODING 22 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +fe +fe +fe +fe +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga23 +ENCODING 23 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +18 +3c +7e +18 +18 +18 +7e +3c +18 +7e +00 +00 +00 +00 +ENDCHAR +STARTCHAR uparrow +ENCODING 24 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0400 +0e00 +1500 +2480 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0000 +0000 +0000 +ENDCHAR +STARTCHAR downarrow +ENCODING 25 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +2480 +1500 +0e00 +0400 +0000 +0000 +0000 +ENDCHAR +STARTCHAR vga26 +ENCODING 26 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +00 +18 +0c +fe +0c +18 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga27 +ENCODING 27 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +00 +30 +60 +fe +60 +30 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga28 +ENCODING 28 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +00 +00 +c0 +c0 +c0 +fe +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga29 +ENCODING 29 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +00 +28 +6c +fe +6c +28 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga30 +ENCODING 30 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +10 +38 +38 +7c +7c +fe +fe +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga31 +ENCODING 31 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +fe +fe +7c +7c +38 +38 +10 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR space +ENCODING 32 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR exclam +ENCODING 33 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 2 13 5 0 +BITMAP +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +40 +00 +00 +c0 +c0 +ENDCHAR +STARTCHAR quotedbl +ENCODING 34 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 6 4 3 10 +BITMAP +cc +cc +cc +88 +ENDCHAR +STARTCHAR numbersign +ENCODING 35 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 13 0 0 +BITMAP +0cc0 +0cc0 +0cc0 +0880 +7fe0 +1100 +3300 +3300 +ffc0 +2200 +6600 +6600 +6600 +ENDCHAR +STARTCHAR dollar +ENCODING 36 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 15 2 -1 +BITMAP +08 +3e +4b +c8 +c8 +e8 +78 +3c +1e +17 +13 +13 +d2 +7c +10 +ENDCHAR +STARTCHAR percent +ENCODING 37 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 13 0 0 +BITMAP +7000 +d840 +8840 +8880 +8900 +da00 +75c0 +0b60 +1220 +2220 +4220 +4360 +01c0 +ENDCHAR +STARTCHAR ampersand +ENCODING 38 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 13 0 0 +BITMAP +3c00 +6600 +6600 +6600 +3400 +3800 +6cc0 +ccc0 +c680 +c300 +c380 +6ec0 +3860 +ENDCHAR +STARTCHAR apostrophe +ENCODING 39 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 3 5 4 9 +BITMAP +e0 +e0 +e0 +60 +c0 +ENDCHAR +STARTCHAR parenleft +ENCODING 40 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 7 16 3 -2 +BITMAP +06 +18 +30 +60 +60 +c0 +c0 +c0 +c0 +c0 +c0 +60 +60 +30 +18 +06 +ENDCHAR +STARTCHAR parenright +ENCODING 41 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 7 16 1 -2 +BITMAP +c0 +30 +18 +0c +0c +06 +06 +06 +06 +06 +06 +0c +0c +18 +30 +c0 +ENDCHAR +STARTCHAR asterisk +ENCODING 42 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 7 7 2 7 +BITMAP +10 +10 +92 +ee +10 +6c +44 +ENDCHAR +STARTCHAR plus +ENCODING 43 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 9 1 1 +BITMAP +0800 +0800 +0800 +0800 +ff80 +0800 +0800 +0800 +0800 +ENDCHAR +STARTCHAR comma +ENCODING 44 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 3 5 4 -2 +BITMAP +e0 +e0 +e0 +60 +c0 +ENDCHAR +STARTCHAR minus +ENCODING 45 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 1 1 5 +BITMAP +ff80 +ENDCHAR +STARTCHAR period +ENCODING 46 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 3 3 4 0 +BITMAP +e0 +e0 +e0 +ENDCHAR +STARTCHAR slash +ENCODING 47 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 16 1 -2 +BITMAP +0080 +0180 +0100 +0300 +0200 +0600 +0400 +0c00 +1800 +1000 +3000 +2000 +6000 +4000 +c000 +8000 +ENDCHAR +STARTCHAR 0 +ENCODING 48 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +3e00 +6300 +4100 +c180 +c180 +c180 +c180 +c180 +c180 +c180 +4100 +6300 +3e00 +ENDCHAR +STARTCHAR 1 +ENCODING 49 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 5 13 2 0 +BITMAP +18 +f8 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 2 +ENCODING 50 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 1 0 +BITMAP +7e +c7 +03 +03 +03 +06 +0c +18 +30 +60 +c0 +ff +ff +ENDCHAR +STARTCHAR 3 +ENCODING 51 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 1 0 +BITMAP +7e +c7 +03 +03 +06 +3c +06 +03 +03 +03 +03 +c7 +7e +ENDCHAR +STARTCHAR 4 +ENCODING 52 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 13 1 0 +BITMAP +0300 +0700 +0f00 +1b00 +1300 +3300 +6300 +c300 +ffc0 +ffc0 +0300 +0300 +0300 +ENDCHAR +STARTCHAR 5 +ENCODING 53 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 2 0 +BITMAP +ff +ff +80 +80 +80 +f8 +1e +07 +03 +03 +03 +c7 +7e +ENDCHAR +STARTCHAR 6 +ENCODING 54 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +1e00 +7300 +6000 +c000 +c000 +de00 +e300 +c180 +c180 +c180 +e180 +7300 +3e00 +ENDCHAR +STARTCHAR 7 +ENCODING 55 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +ff80 +ff80 +0300 +0600 +0c00 +0c00 +1800 +1800 +3000 +3000 +6000 +6000 +6000 +ENDCHAR +STARTCHAR 8 +ENCODING 56 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +3e00 +6300 +c180 +c180 +e100 +7a00 +3e00 +6700 +c380 +c180 +c180 +6300 +3e00 +ENDCHAR +STARTCHAR 9 +ENCODING 57 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +3e00 +6700 +c380 +c180 +c180 +c180 +6380 +3d80 +0180 +0100 +0300 +c600 +7c00 +ENDCHAR +STARTCHAR colon +ENCODING 58 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 3 10 4 0 +BITMAP +e0 +e0 +00 +00 +00 +00 +00 +00 +e0 +e0 +ENDCHAR +STARTCHAR semicolon +ENCODING 59 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 3 12 4 -2 +BITMAP +e0 +e0 +00 +00 +00 +00 +00 +00 +e0 +e0 +60 +c0 +ENDCHAR +STARTCHAR less +ENCODING 60 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 9 0 1 +BITMAP +00c0 +0380 +0e00 +3800 +e000 +3800 +0e00 +0380 +00c0 +ENDCHAR +STARTCHAR equal +ENCODING 61 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 4 1 3 +BITMAP +ff80 +0000 +0000 +ff80 +ENDCHAR +STARTCHAR greater +ENCODING 62 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 9 1 1 +BITMAP +c000 +7000 +1c00 +0700 +01c0 +0700 +1c00 +7000 +c000 +ENDCHAR +STARTCHAR question +ENCODING 63 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 1 0 +BITMAP +7e +c7 +03 +03 +06 +18 +30 +30 +30 +00 +00 +30 +30 +ENDCHAR +STARTCHAR at +ENCODING 64 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 13 1 0 +BITMAP +0f00 +3180 +6180 +4f80 +9980 +9980 +9980 +9980 +9f80 +4dc0 +6000 +3000 +0f80 +ENDCHAR +STARTCHAR A +ENCODING 65 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 13 1 0 +BITMAP +0c00 +0c00 +1e00 +1600 +1300 +3300 +2300 +2180 +7f80 +4180 +c0c0 +c0c0 +c0c0 +ENDCHAR +STARTCHAR B +ENCODING 66 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 2 0 +BITMAP +fe +c7 +c3 +c3 +c3 +c6 +fc +c6 +c3 +c3 +c3 +c7 +fe +ENDCHAR +STARTCHAR C +ENCODING 67 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +1f00 +3180 +6000 +6000 +c000 +c000 +c000 +c000 +c000 +6000 +6000 +3180 +1f00 +ENDCHAR +STARTCHAR D +ENCODING 68 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +fc00 +c700 +c300 +c180 +c180 +c180 +c180 +c180 +c180 +c300 +c300 +ce00 +f800 +ENDCHAR +STARTCHAR E +ENCODING 69 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 2 0 +BITMAP +ff +c0 +c0 +c0 +c0 +c0 +fe +c0 +c0 +c0 +c0 +c0 +ff +ENDCHAR +STARTCHAR F +ENCODING 70 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 2 0 +BITMAP +ff +c0 +c0 +c0 +c0 +c0 +fe +c0 +c0 +c0 +c0 +c0 +c0 +ENDCHAR +STARTCHAR G +ENCODING 71 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +1f00 +3180 +6000 +6000 +c000 +c000 +c000 +c180 +c180 +6180 +6180 +3180 +1f80 +ENDCHAR +STARTCHAR H +ENCODING 72 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +c180 +c180 +c180 +c180 +c180 +c180 +ff80 +c180 +c180 +c180 +c180 +c180 +c180 +ENDCHAR +STARTCHAR I +ENCODING 73 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 2 0 +BITMAP +ff +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ff +ENDCHAR +STARTCHAR J +ENCODING 74 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 7 13 2 0 +BITMAP +7e +06 +06 +06 +06 +06 +06 +06 +06 +06 +86 +8c +f0 +ENDCHAR +STARTCHAR K +ENCODING 75 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 13 1 0 +BITMAP +c180 +c300 +c600 +cc00 +d800 +f000 +f000 +d800 +cc00 +c600 +c300 +c180 +c0c0 +ENDCHAR +STARTCHAR L +ENCODING 76 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 2 0 +BITMAP +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +ff +ENDCHAR +STARTCHAR M +ENCODING 77 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 13 1 0 +BITMAP +e0c0 +e1c0 +e1c0 +e1c0 +b2c0 +b2c0 +b2c0 +b2c0 +9cc0 +9cc0 +88c0 +88c0 +80c0 +ENDCHAR +STARTCHAR N +ENCODING 78 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +c080 +e080 +e080 +b080 +9880 +9880 +8c80 +8680 +8680 +8380 +8380 +8180 +8080 +ENDCHAR +STARTCHAR O +ENCODING 79 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 13 1 0 +BITMAP +1e00 +3300 +6180 +6180 +c0c0 +c0c0 +c0c0 +c0c0 +c0c0 +6180 +6180 +3300 +1e00 +ENDCHAR +STARTCHAR P +ENCODING 80 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 2 0 +BITMAP +fe +c7 +c3 +c3 +c3 +c3 +c6 +f8 +c0 +c0 +c0 +c0 +c0 +ENDCHAR +STARTCHAR Q +ENCODING 81 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 16 1 -3 +BITMAP +1e00 +3300 +6180 +6180 +c0c0 +c0c0 +c0c0 +c0c0 +c0c0 +6180 +6180 +3300 +1e00 +0600 +0380 +01c0 +ENDCHAR +STARTCHAR R +ENCODING 82 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 2 0 +BITMAP +fc +ce +c6 +c6 +c6 +cc +f0 +d8 +cc +cc +c6 +c3 +c3 +ENDCHAR +STARTCHAR S +ENCODING 83 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 2 0 +BITMAP +3e +63 +c0 +c0 +e0 +78 +1e +07 +03 +03 +03 +c6 +7c +ENDCHAR +STARTCHAR T +ENCODING 84 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 13 1 0 +BITMAP +ffc0 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +ENDCHAR +STARTCHAR U +ENCODING 85 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +c180 +c180 +c180 +c180 +c180 +c180 +c180 +c180 +c180 +c180 +6300 +7700 +1c00 +ENDCHAR +STARTCHAR V +ENCODING 86 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 13 1 0 +BITMAP +c0c0 +c0c0 +c0c0 +6080 +6080 +6180 +3100 +3100 +3100 +1a00 +1a00 +0c00 +0c00 +ENDCHAR +STARTCHAR W +ENCODING 87 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 13 0 0 +BITMAP +c020 +c020 +c620 +c660 +6640 +6740 +6f40 +6b40 +7bc0 +3b80 +3180 +3180 +3180 +ENDCHAR +STARTCHAR X +ENCODING 88 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +c180 +c180 +6180 +6300 +3200 +1c00 +1c00 +1c00 +2600 +6300 +c300 +c180 +c180 +ENDCHAR +STARTCHAR Y +ENCODING 89 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 13 1 0 +BITMAP +c0c0 +c0c0 +6180 +3100 +3300 +1a00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +ENDCHAR +STARTCHAR Z +ENCODING 90 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +ff80 +ff80 +0180 +0300 +0600 +0c00 +1800 +3000 +6000 +c000 +c000 +ff80 +ff80 +ENDCHAR +STARTCHAR bracketleft +ENCODING 91 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 6 16 3 -2 +BITMAP +fc +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +fc +ENDCHAR +STARTCHAR backslash +ENCODING 92 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 16 1 -2 +BITMAP +8000 +c000 +4000 +6000 +2000 +3000 +1000 +1800 +0c00 +0400 +0600 +0200 +0300 +0100 +0180 +0080 +ENDCHAR +STARTCHAR bracketright +ENCODING 93 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 6 16 2 -2 +BITMAP +fc +0c +0c +0c +0c +0c +0c +0c +0c +0c +0c +0c +0c +0c +0c +fc +ENDCHAR +STARTCHAR asciicircum +ENCODING 94 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 10 1 2 +BITMAP +0800 +1c00 +1400 +3600 +2200 +6300 +6300 +c180 +c180 +c180 +ENDCHAR +STARTCHAR underscore +ENCODING 95 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 1 0 -1 +BITMAP +ffe0 +ENDCHAR +STARTCHAR grave +ENCODING 96 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 3 5 4 9 +BITMAP +60 +c0 +e0 +e0 +e0 +ENDCHAR +STARTCHAR a +ENCODING 97 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3e00 +6700 +0300 +0300 +3f00 +6300 +c300 +c300 +e700 +7980 +ENDCHAR +STARTCHAR b +ENCODING 98 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +c000 +c000 +c000 +c000 +dc00 +e700 +c300 +c180 +c180 +c180 +c180 +c300 +e700 +dc00 +ENDCHAR +STARTCHAR c +ENCODING 99 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +1f00 +7180 +6000 +c000 +c000 +c000 +c000 +6000 +7180 +1f00 +ENDCHAR +STARTCHAR d +ENCODING 100 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +0180 +0180 +0180 +0180 +1d80 +7380 +6180 +c180 +c180 +c180 +c180 +6180 +7380 +1d80 +ENDCHAR +STARTCHAR e +ENCODING 101 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3e00 +6300 +c180 +c180 +ff80 +c000 +c000 +e000 +7180 +3f00 +ENDCHAR +STARTCHAR f +ENCODING 102 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +0780 +0c00 +1800 +1800 +1800 +ff80 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +ENDCHAR +STARTCHAR g +ENCODING 103 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 -3 +BITMAP +1d80 +7380 +6180 +c180 +c180 +c180 +c180 +6380 +3d80 +0180 +0180 +6300 +3e00 +ENDCHAR +STARTCHAR h +ENCODING 104 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 14 2 0 +BITMAP +c0 +c0 +c0 +c0 +de +e7 +c3 +c3 +c3 +c3 +c3 +c3 +c3 +c3 +ENDCHAR +STARTCHAR i +ENCODING 105 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 5 14 2 0 +BITMAP +18 +18 +00 +00 +f8 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR j +ENCODING 106 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 7 17 1 -3 +BITMAP +06 +06 +00 +00 +3e +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +0c +f8 +ENDCHAR +STARTCHAR k +ENCODING 107 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 14 2 0 +BITMAP +c000 +c000 +c000 +c000 +c300 +c600 +cc00 +d800 +f000 +d800 +cc00 +c600 +c300 +c180 +ENDCHAR +STARTCHAR l +ENCODING 108 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 5 14 2 0 +BITMAP +f8 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR m +ENCODING 109 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 10 1 0 +BITMAP +dd80 +eec0 +ccc0 +ccc0 +ccc0 +ccc0 +ccc0 +ccc0 +ccc0 +ccc0 +ENDCHAR +STARTCHAR n +ENCODING 110 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 10 2 0 +BITMAP +de +e7 +c3 +c3 +c3 +c3 +c3 +c3 +c3 +c3 +ENDCHAR +STARTCHAR o +ENCODING 111 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +1c00 +7700 +6300 +c180 +c180 +c180 +c180 +6300 +7700 +1c00 +ENDCHAR +STARTCHAR p +ENCODING 112 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 -3 +BITMAP +dc00 +e700 +c300 +c180 +c180 +c180 +c180 +c300 +e700 +dc00 +c000 +c000 +c000 +ENDCHAR +STARTCHAR q +ENCODING 113 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 -3 +BITMAP +1d80 +7380 +6180 +c180 +c180 +c180 +c180 +6180 +7380 +1d80 +0180 +0180 +0180 +ENDCHAR +STARTCHAR r +ENCODING 114 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 7 10 3 0 +BITMAP +ce +de +e2 +c2 +c0 +c0 +c0 +c0 +c0 +c0 +ENDCHAR +STARTCHAR s +ENCODING 115 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 7 10 2 0 +BITMAP +7c +c6 +c0 +e0 +78 +3c +0e +06 +c6 +7c +ENDCHAR +STARTCHAR t +ENCODING 116 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 12 1 0 +BITMAP +1800 +1800 +1800 +ff80 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +0f80 +ENDCHAR +STARTCHAR u +ENCODING 117 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 10 1 0 +BITMAP +c3 +c3 +c3 +c3 +c3 +c3 +c3 +c3 +e7 +7b +ENDCHAR +STARTCHAR v +ENCODING 118 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 10 2 0 +BITMAP +c3 +c3 +c3 +62 +66 +66 +34 +34 +18 +18 +ENDCHAR +STARTCHAR w +ENCODING 119 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 10 1 0 +BITMAP +c0c0 +ccc0 +ccc0 +cc80 +6e80 +6e80 +7780 +3700 +3300 +3300 +ENDCHAR +STARTCHAR x +ENCODING 120 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 10 2 0 +BITMAP +c3 +c3 +66 +34 +38 +1c +2c +66 +c3 +c3 +ENDCHAR +STARTCHAR y +ENCODING 121 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 2 -3 +BITMAP +c3 +c3 +c3 +62 +66 +66 +34 +3c +18 +18 +10 +30 +30 +ENDCHAR +STARTCHAR z +ENCODING 122 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 10 2 0 +BITMAP +ff +03 +07 +0e +1c +38 +70 +e0 +c0 +ff +ENDCHAR +STARTCHAR braceleft +ENCODING 123 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +1f +30 +30 +10 +18 +08 +08 +f0 +08 +08 +18 +10 +30 +30 +30 +1f +ENDCHAR +STARTCHAR bar +ENCODING 124 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 2 16 5 -2 +BITMAP +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +ENDCHAR +STARTCHAR braceright +ENCODING 125 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 1 -2 +BITMAP +f8 +0c +0c +08 +18 +10 +10 +0f +10 +10 +18 +08 +0c +0c +0c +f8 +ENDCHAR +STARTCHAR asciitilde +ENCODING 126 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 3 1 4 +BITMAP +7080 +9c80 +8700 +ENDCHAR +STARTCHAR vga127 +ENCODING 127 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +10 +38 +6c +c6 +c6 +c6 +fe +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR Ccedilla +ENCODING 128 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 16 1 -3 +BITMAP +1f00 +3180 +6000 +6000 +c000 +c000 +c000 +c000 +c000 +6000 +6000 +3180 +1f00 +0c00 +0600 +1c00 +ENDCHAR +STARTCHAR udiaeresis +ENCODING 129 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 1 0 +BITMAP +24 +24 +00 +c3 +c3 +c3 +c3 +c3 +c3 +c3 +c3 +e7 +7b +ENDCHAR +STARTCHAR eacute +ENCODING 130 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +0e00 +1800 +0000 +3e00 +6300 +c180 +c180 +ff80 +c000 +c000 +e000 +7180 +3f00 +ENDCHAR +STARTCHAR acircumflex +ENCODING 131 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +0c00 +1600 +0000 +3e00 +6700 +0300 +0300 +3f00 +6300 +c300 +c300 +e700 +7980 +ENDCHAR +STARTCHAR adiaeresis +ENCODING 132 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +1400 +1400 +0000 +3e00 +6700 +0300 +0300 +3f00 +6300 +c300 +c300 +e700 +7980 +ENDCHAR +STARTCHAR agrave +ENCODING 133 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +3800 +0c00 +0000 +3e00 +6700 +0300 +0300 +3f00 +6300 +c300 +c300 +e700 +7980 +ENDCHAR +STARTCHAR aring +ENCODING 134 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +0c00 +1200 +0c00 +3e00 +6700 +0300 +0300 +3f00 +6300 +c300 +c300 +e700 +7980 +ENDCHAR +STARTCHAR ccedilla +ENCODING 135 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 -3 +BITMAP +1f00 +7180 +6000 +c000 +c000 +c000 +c000 +6000 +7180 +1f00 +0c00 +0600 +1c00 +ENDCHAR +STARTCHAR ecircumflex +ENCODING 136 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +0c00 +1600 +0000 +3e00 +6300 +c180 +c180 +ff80 +c000 +c000 +e000 +7180 +3f00 +ENDCHAR +STARTCHAR ediaeresis +ENCODING 137 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +1400 +1400 +0000 +3e00 +6300 +c180 +c180 +ff80 +c000 +c000 +e000 +7180 +3f00 +ENDCHAR +STARTCHAR egrave +ENCODING 138 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +3800 +0c00 +0000 +3e00 +6300 +c180 +c180 +ff80 +c000 +c000 +e000 +7180 +3f00 +ENDCHAR +STARTCHAR idiaeresis +ENCODING 139 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 6 13 1 0 +BITMAP +24 +24 +00 +fc +0c +0c +0c +0c +0c +0c +0c +0c +0c +ENDCHAR +STARTCHAR icircumflex +ENCODING 140 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 6 13 2 0 +BITMAP +18 +2c +00 +f8 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR igrave +ENCODING 141 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 5 13 2 0 +BITMAP +70 +18 +00 +f8 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR Adiaeresis +ENCODING 142 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 16 1 0 +BITMAP +1200 +1200 +0000 +0c00 +0c00 +1e00 +1600 +1300 +3300 +2300 +2180 +7f80 +4180 +c0c0 +c0c0 +c0c0 +ENDCHAR +STARTCHAR Aring +ENCODING 143 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 15 1 0 +BITMAP +0c00 +1200 +0c00 +0c00 +1e00 +1600 +1300 +3300 +2300 +2180 +7f80 +4180 +c0c0 +c0c0 +c0c0 +ENDCHAR +STARTCHAR Eacute +ENCODING 144 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 0 +BITMAP +1c +30 +00 +ff +c0 +c0 +c0 +c0 +c0 +fe +c0 +c0 +c0 +c0 +c0 +ff +ENDCHAR +STARTCHAR ae +ENCODING 145 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 10 1 0 +BITMAP +7b80 +ccc0 +0cc0 +0cc0 +7fc0 +cc00 +cc00 +cc00 +dec0 +6380 +ENDCHAR +STARTCHAR AE +ENCODING 146 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 13 1 0 +BITMAP +07c0 +0e00 +0e00 +1600 +1600 +2600 +27c0 +6600 +7e00 +4600 +c600 +c600 +c7c0 +ENDCHAR +STARTCHAR ocircumflex +ENCODING 147 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +0c00 +1600 +0000 +1c00 +7700 +6300 +c180 +c180 +c180 +c180 +6300 +7700 +1c00 +ENDCHAR +STARTCHAR odiaeresis +ENCODING 148 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +1400 +1400 +0000 +1c00 +7700 +6300 +c180 +c180 +c180 +c180 +6300 +7700 +1c00 +ENDCHAR +STARTCHAR ograve +ENCODING 149 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +3800 +0c00 +0000 +1c00 +7700 +6300 +c180 +c180 +c180 +c180 +6300 +7700 +1c00 +ENDCHAR +STARTCHAR ucircumflex +ENCODING 150 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 1 0 +BITMAP +18 +2c +00 +c3 +c3 +c3 +c3 +c3 +c3 +c3 +c3 +e7 +7b +ENDCHAR +STARTCHAR ugrave +ENCODING 151 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 1 0 +BITMAP +38 +0c +00 +c3 +c3 +c3 +c3 +c3 +c3 +c3 +c3 +e7 +7b +ENDCHAR +STARTCHAR ydiaeresis +ENCODING 152 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -3 +BITMAP +24 +24 +00 +c3 +c3 +c3 +62 +66 +66 +34 +3c +18 +18 +10 +30 +30 +ENDCHAR +STARTCHAR Odiaeresis +ENCODING 153 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 16 1 0 +BITMAP +1200 +1200 +0000 +1e00 +3300 +6180 +6180 +c0c0 +c0c0 +c0c0 +c0c0 +c0c0 +6180 +6180 +3300 +1e00 +ENDCHAR +STARTCHAR Udiaeresis +ENCODING 154 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 16 1 0 +BITMAP +2200 +2200 +0000 +c180 +c180 +c180 +c180 +c180 +c180 +c180 +c180 +c180 +c180 +6300 +7700 +1c00 +ENDCHAR +STARTCHAR cent +ENCODING 155 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 14 2 -1 +BITMAP +08 +08 +1e +6b +68 +c8 +c8 +c8 +c8 +c8 +6b +3e +08 +08 +ENDCHAR +STARTCHAR sterling +ENCODING 156 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 2 0 +BITMAP +0e +13 +30 +30 +30 +30 +fc +30 +30 +20 +40 +ff +ff +ENDCHAR +STARTCHAR yen +ENCODING 157 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 10 13 1 0 +BITMAP +c0c0 +c0c0 +6180 +3100 +3300 +1a00 +0c00 +3f00 +0c00 +3f00 +0c00 +0c00 +0c00 +ENDCHAR +STARTCHAR vga158 +ENCODING 158 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +f8 +cc +cc +f8 +c4 +cc +de +cc +cc +cc +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga159 +ENCODING 159 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +0e +1b +18 +18 +18 +7e +18 +18 +18 +18 +18 +d8 +70 +00 +00 +ENDCHAR +STARTCHAR aacute +ENCODING 160 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +0e00 +1800 +0000 +3e00 +6700 +0300 +0300 +3f00 +6300 +c300 +c300 +e700 +7980 +ENDCHAR +STARTCHAR iacute +ENCODING 161 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 6 13 2 0 +BITMAP +1c +30 +00 +f8 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR oacute +ENCODING 162 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +0e00 +1800 +0000 +1c00 +7700 +6300 +c180 +c180 +c180 +c180 +6300 +7700 +1c00 +ENDCHAR +STARTCHAR uacute +ENCODING 163 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 1 0 +BITMAP +0e +18 +00 +c3 +c3 +c3 +c3 +c3 +c3 +c3 +c3 +e7 +7b +ENDCHAR +STARTCHAR vga164 +ENCODING 164 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +76 +dc +00 +dc +66 +66 +66 +66 +66 +66 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga165 +ENCODING 165 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +76 +dc +00 +c6 +e6 +f6 +fe +de +ce +c6 +c6 +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga166 +ENCODING 166 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +3c +6c +6c +3e +00 +7e +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga167 +ENCODING 167 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +38 +6c +6c +38 +00 +7c +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR questiondown +ENCODING 168 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 13 2 -3 +BITMAP +0c +0c +00 +00 +0c +0c +0c +18 +60 +c0 +c0 +e3 +7e +ENDCHAR +STARTCHAR hook +ENCODING 169 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 4 1 3 +BITMAP +ff80 +8000 +8000 +8000 +ENDCHAR +STARTCHAR notsign +ENCODING 170 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 4 1 3 +BITMAP +ff80 +0080 +0080 +0080 +ENDCHAR +STARTCHAR onehalf +ENCODING 171 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 13 0 0 +BITMAP +e100 +6100 +6200 +6200 +6400 +6400 +69c0 +0a60 +0860 +10c0 +1100 +23e0 +23e0 +ENDCHAR +STARTCHAR onequarter +ENCODING 172 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 13 0 0 +BITMAP +e100 +6100 +6200 +6200 +6400 +6400 +68c0 +11c0 +12c0 +24c0 +27e0 +40c0 +40c0 +ENDCHAR +STARTCHAR exclamdown +ENCODING 173 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 2 13 5 -3 +BITMAP +c0 +c0 +00 +00 +80 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +ENDCHAR +STARTCHAR guillemotleft +ENCODING 174 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 8 1 1 +BITMAP +1980 +3300 +6600 +cc00 +cc00 +6600 +3300 +1980 +ENDCHAR +STARTCHAR guillemotright +ENCODING 175 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 8 1 1 +BITMAP +cc00 +6600 +3300 +1980 +1980 +3300 +6600 +cc00 +ENDCHAR +STARTCHAR raster1 +ENCODING 176 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +8880 +0000 +2220 +0000 +8880 +0000 +2220 +0000 +8880 +0000 +2220 +0000 +8880 +0000 +2220 +0000 +8880 +0000 +2220 +ENDCHAR +STARTCHAR raster2 +ENCODING 177 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +aaa0 +5550 +aaa0 +5550 +aaa0 +5550 +aaa0 +5550 +aaa0 +5550 +aaa0 +5550 +aaa0 +5550 +aaa0 +5550 +aaa0 +5550 +aaa0 +ENDCHAR +STARTCHAR raster3 +ENCODING 178 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +7770 +ffe0 +ddd0 +ffe0 +7770 +ffe0 +ddd0 +ffe0 +7770 +ffe0 +ddd0 +ffe0 +7770 +ffe0 +ddd0 +ffe0 +7770 +ffe0 +ddd0 +ENDCHAR +STARTCHAR udline +ENCODING 179 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ENDCHAR +STARTCHAR udlline +ENCODING 180 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +fc00 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ENDCHAR +STARTCHAR udLline +ENCODING 181 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +fc00 +0400 +fc00 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ENDCHAR +STARTCHAR UDlline +ENCODING 182 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +fa00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ENDCHAR +STARTCHAR Dlline +ENCODING 183 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +fe00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ENDCHAR +STARTCHAR dLline +ENCODING 184 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +fc00 +0400 +fc00 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ENDCHAR +STARTCHAR UDLline +ENCODING 185 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +fa00 +0a00 +fa00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ENDCHAR +STARTCHAR UDline +ENCODING 186 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ENDCHAR +STARTCHAR DLline +ENCODING 187 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +fe00 +0200 +fa00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ENDCHAR +STARTCHAR ULline +ENCODING 188 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +fa00 +0200 +fe00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Ulline +ENCODING 189 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +fe00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR uLline +ENCODING 190 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +fc00 +0400 +fc00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR dlline +ENCODING 191 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +fc00 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ENDCHAR +STARTCHAR urline +ENCODING 192 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +07e0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR urlline +ENCODING 193 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ffe0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR rdlline +ENCODING 194 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ffe0 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ENDCHAR +STARTCHAR urdline +ENCODING 195 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +07e0 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ENDCHAR +STARTCHAR rlline +ENCODING 196 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ffe0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR urdlline +ENCODING 197 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ffe0 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ENDCHAR +STARTCHAR uRdline +ENCODING 198 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +07e0 +0400 +07e0 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ENDCHAR +STARTCHAR UrDline +ENCODING 199 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0be0 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ENDCHAR +STARTCHAR URline +ENCODING 200 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0be0 +0800 +0fe0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR RDline +ENCODING 201 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0fe0 +0800 +0be0 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ENDCHAR +STARTCHAR URLline +ENCODING 202 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +fbe0 +0000 +ffe0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR RDLline +ENCODING 203 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ffe0 +0000 +fbe0 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ENDCHAR +STARTCHAR URDline +ENCODING 204 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0be0 +0800 +0be0 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ENDCHAR +STARTCHAR RLline +ENCODING 205 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ffe0 +0000 +ffe0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR URDLline +ENCODING 206 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +fbe0 +0000 +fbe0 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ENDCHAR +STARTCHAR uRLline +ENCODING 207 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ffe0 +0000 +ffe0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Urlline +ENCODING 208 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ffe0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR RdLline +ENCODING 209 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ffe0 +0000 +ffe0 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ENDCHAR +STARTCHAR rDlline +ENCODING 210 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ffe0 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ENDCHAR +STARTCHAR Urline +ENCODING 211 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0fe0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR uRline +ENCODING 212 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +07e0 +0400 +07e0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Rdline +ENCODING 213 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +07e0 +0400 +07e0 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ENDCHAR +STARTCHAR rDline +ENCODING 214 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0fe0 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ENDCHAR +STARTCHAR UrDlline +ENCODING 215 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ffe0 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +0a00 +ENDCHAR +STARTCHAR uRdLline +ENCODING 216 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ffe0 +0400 +ffe0 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ENDCHAR +STARTCHAR urline +ENCODING 217 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +fc00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR rdline +ENCODING 218 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +07e0 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +0400 +ENDCHAR +STARTCHAR fullblock +ENCODING 219 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ENDCHAR +STARTCHAR bottomblock +ENCODING 220 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ENDCHAR +STARTCHAR leftblock +ENCODING 221 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +fc00 +ENDCHAR +STARTCHAR rightblock +ENCODING 222 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +03e0 +ENDCHAR +STARTCHAR topblock +ENCODING 223 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +ffe0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR vga224 +ENCODING 224 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +00 +76 +dc +d8 +d8 +d8 +dc +76 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga225 +ENCODING 225 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +78 +cc +cc +cc +d8 +cc +c6 +c6 +c6 +cc +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga226 +ENCODING 226 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +fe +c6 +c6 +c0 +c0 +c0 +c0 +c0 +c0 +c0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga227 +ENCODING 227 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +fe +6c +6c +6c +6c +6c +6c +6c +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga228 +ENCODING 228 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +fe +c6 +60 +30 +18 +30 +60 +c6 +fe +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga229 +ENCODING 229 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +00 +7e +d8 +d8 +d8 +d8 +d8 +70 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga230 +ENCODING 230 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +66 +66 +66 +66 +66 +7c +60 +60 +c0 +00 +00 +00 +ENDCHAR +STARTCHAR vga231 +ENCODING 231 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +76 +dc +18 +18 +18 +18 +18 +18 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga232 +ENCODING 232 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +7e +18 +3c +66 +66 +66 +3c +18 +7e +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga233 +ENCODING 233 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +38 +6c +c6 +c6 +fe +c6 +c6 +6c +38 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga234 +ENCODING 234 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +38 +6c +c6 +c6 +c6 +6c +6c +6c +6c +ee +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga235 +ENCODING 235 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +1e +30 +18 +0c +3e +66 +66 +66 +66 +3c +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga236 +ENCODING 236 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +00 +7e +db +db +db +7e +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga237 +ENCODING 237 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +03 +06 +7e +db +db +f3 +7e +60 +c0 +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga238 +ENCODING 238 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +1c +30 +60 +60 +7c +60 +60 +60 +30 +1c +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga239 +ENCODING 239 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +7c +c6 +c6 +c6 +c6 +c6 +c6 +c6 +c6 +00 +00 +00 +00 +ENDCHAR +STARTCHAR isequal +ENCODING 240 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +7fc0 +0000 +0000 +7fc0 +0000 +0000 +7fc0 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR plusminus +ENCODING 241 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 8 1 2 +BITMAP +0800 +0800 +0800 +ff80 +0800 +0800 +0000 +ff80 +ENDCHAR +STARTCHAR vga242 +ENCODING 242 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +30 +18 +0c +06 +0c +18 +30 +00 +7e +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga243 +ENCODING 243 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +0c +18 +30 +60 +30 +18 +0c +00 +7e +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga244 +ENCODING 244 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +0e +1b +1b +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR vga245 +ENCODING 245 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +d8 +d8 +d8 +70 +00 +00 +00 +00 +ENDCHAR +STARTCHAR division +ENCODING 246 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 9 9 1 1 +BITMAP +0800 +0800 +0000 +0000 +ff80 +0000 +0000 +0800 +0800 +ENDCHAR +STARTCHAR vga247 +ENCODING 247 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +00 +00 +00 +00 +76 +dc +00 +76 +dc +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR degree +ENCODING 248 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 5 5 3 9 +BITMAP +70 +88 +88 +88 +70 +ENDCHAR +STARTCHAR smalldot +ENCODING 249 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0e00 +0e00 +0e00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR smallerdot +ENCODING 250 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0800 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR vga251 +ENCODING 251 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +0f +0c +0c +0c +0c +0c +ec +6c +6c +3c +1c +00 +00 +00 +00 +ENDCHAR +STARTCHAR vga252 +ENCODING 252 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 8 16 2 -2 +BITMAP +00 +d8 +6c +6c +6c +6c +6c +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR +STARTCHAR twosuperior +ENCODING 253 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 7 8 2 5 +BITMAP +7c +c6 +06 +1c +70 +c0 +fe +fe +ENDCHAR +STARTCHAR bullet +ENCODING 254 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 11 19 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +3f80 +3f80 +3f80 +3f80 +3f80 +3f80 +3f80 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR space255 +ENCODING 255 +SWIDTH 586 0 +DWIDTH 11 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +ENDFONT diff --git a/etc/vga12x30.bdf b/etc/vga12x30.bdf new file mode 100644 index 0000000..41af0b7 --- /dev/null +++ b/etc/vga12x30.bdf @@ -0,0 +1,6315 @@ +STARTFONT 2.1 +COMMENT This font was taken from the dosemu project, resized by +COMMENT bdfresize and finally corrected and sharpened by hand. +COMMENT +COMMENT This font gives you the maximum window size possible +COMMENT inside of a 1024x768 pix display without breaking the +COMMENT screen borders. +COMMENT +COMMENT Having a closer look to the font, you will find that +COMMENT narrow characters are not always placed in the same +COMMENT positions of their cells. You will also see that some +COMMENT characters like "o" are made less wide. These are not +COMMENT mistakes. It was all done for the purpose of improving +COMMENT the appearance of entire words and sentences. +COMMENT +COMMENT You may change this if you like, but should mention it +COMMENT here (inside of the font comments). +COMMENT +COMMENT Have a look to the other fonts of this series as well. +COMMENT Written in December 2002 by Martin Reuber, OFD Kiel, +COMMENT German Fiscal Administration. +COMMENT +COMMENT Update 002 in May 2003: +COMMENT The style of the characters b and d was changed. +COMMENT +COMMENT Update 003 in January 2004: +COMMENT Small corrections in font name, properties and comments. +FONT -dosemu-vga-Medium-R-Normal--30-300-75-75-C-120-IBM-CP437 +SIZE 30 75 75 +FONTBOUNDINGBOX 12 30 0 -8 +STARTPROPERTIES 22 +POINT_SIZE 300 +RESOLUTION_X 75 +RESOLUTION_Y 75 +QUAD_WIDTH 8 +DEFAULT_CHAR 0 +FONT_ASCENT 22 +FONT_DESCENT 8 +_ORIGINAL_FONT_NAME "vga" +_XMBDFED_INFO "Edited with xmbdfed 4.5." +FOUNDRY "dosemu" +FAMILY_NAME "vga" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "1024x768pix_screen_max_size" +PIXEL_SIZE 30 +SPACING "C" +AVERAGE_WIDTH 120 +CHARSET_REGISTRY "IBM" +CHARSET_ENCODING "CP437" +RESOLUTION 100 +WEIGHT 10 +ENDPROPERTIES +CHARS 256 +STARTCHAR 000 +ENCODING 0 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR 001 +ENCODING 1 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 19 0 0 +BITMAP +7FE0 +FFF0 +C030 +D9B0 +D9B0 +D9B0 +C030 +C030 +D9B0 +D9B0 +D9B0 +D9B0 +D9B0 +DFB0 +CF30 +C030 +C030 +FFF0 +7FE0 +ENDCHAR +STARTCHAR 002 +ENCODING 2 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 19 0 0 +BITMAP +7FE0 +FFF0 +FFF0 +E670 +E670 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +EF70 +EF70 +EF70 +F0F0 +FFF0 +FFF0 +FFF0 +FFF0 +7FE0 +ENDCHAR +STARTCHAR 003 +ENCODING 3 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 12 0 3 +BITMAP +71C0 +FBE0 +FFE0 +FFE0 +FFE0 +FFE0 +FFE0 +7FC0 +3F80 +1F00 +0E00 +0400 +ENDCHAR +STARTCHAR 004 +ENCODING 4 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 12 0 3 +BITMAP +0C00 +1E00 +3F00 +7F80 +FFC0 +FFC0 +FFC0 +FFC0 +7F80 +3F00 +1E00 +0C00 +ENDCHAR +STARTCHAR 005 +ENCODING 5 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 16 0 0 +BITMAP +0C00 +1E00 +1E00 +7F80 +F3C0 +F3C0 +C0C0 +C0C0 +F3C0 +F3C0 +7F80 +0C00 +0C00 +0C00 +1E00 +1E00 +ENDCHAR +STARTCHAR 006 +ENCODING 6 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 16 0 0 +BITMAP +0C00 +0C00 +1E00 +3F00 +7F80 +FFC0 +FFC0 +FFC0 +FFC0 +3F00 +3F00 +0C00 +0C00 +0C00 +3F00 +3F00 +ENDCHAR +STARTCHAR 007 +ENCODING 7 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 6 7 3 4 +BITMAP +30 +78 +FC +FC +FC +78 +30 +ENDCHAR +STARTCHAR 008 +ENCODING 8 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 29 0 -7 +BITMAP +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +F9F0 +F9F0 +E070 +E070 +F9F0 +F9F0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 009 +ENCODING 9 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 12 1 3 +BITMAP +7F00 +FF80 +FF80 +E380 +E380 +C180 +C180 +E380 +E380 +FF80 +FF80 +7F00 +ENDCHAR +STARTCHAR 010 +ENCODING 10 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 30 0 -8 +BITMAP +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +EF70 +EF70 +DFB0 +DFB0 +DFB0 +DFB0 +DFB0 +EF70 +EF70 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 011 +ENCODING 11 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 18 0 -1 +BITMAP +1FC0 +0FC0 +01C0 +03C0 +06C0 +0CC0 +7E40 +FF00 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +FF00 +7E00 +ENDCHAR +STARTCHAR 012 +ENCODING 12 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 19 2 0 +BITMAP +7E +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +7E +18 +18 +18 +FF +FF +18 +18 +18 +ENDCHAR +STARTCHAR 013 +ENCODING 13 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +0780 +0FC0 +0CC0 +0CC0 +0CC0 +0FC0 +0F80 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +7C00 +FC00 +F800 +F000 +ENDCHAR +STARTCHAR 014 +ENCODING 14 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 21 0 -2 +BITMAP +3F80 +7FC0 +60C0 +60C0 +60C0 +7FC0 +7FC0 +60C0 +60C0 +60C0 +60C0 +60C0 +60C0 +60C0 +63C0 +77C0 +F7C0 +F7C0 +F380 +E000 +C000 +ENDCHAR +STARTCHAR 015 +ENCODING 15 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 16 0 0 +BITMAP +0E00 +0E00 +EEE0 +E4E0 +FFE0 +1F00 +FBE0 +F1E0 +FBE0 +1F00 +FFE0 +EEE0 +E4E0 +0E00 +0E00 +0E00 +ENDCHAR +STARTCHAR 016 +ENCODING 16 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +8000 +C000 +E000 +F000 +F800 +FC00 +FE00 +FF00 +FF80 +FFC0 +FF80 +FF00 +FE00 +FC00 +F800 +F000 +E000 +C000 +8000 +ENDCHAR +STARTCHAR 017 +ENCODING 17 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +0040 +00C0 +01C0 +03C0 +07C0 +0FC0 +1FC0 +3FC0 +7FC0 +FFC0 +7FC0 +3FC0 +1FC0 +0FC0 +07C0 +03C0 +01C0 +00C0 +0040 +ENDCHAR +STARTCHAR 018 +ENCODING 18 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 17 2 2 +BITMAP +18 +3C +7E +FF +18 +18 +18 +18 +18 +18 +18 +18 +18 +FF +7E +3C +18 +ENDCHAR +STARTCHAR 019 +ENCODING 19 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 19 2 0 +BITMAP +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +00 +00 +C6 +C6 +C6 +ENDCHAR +STARTCHAR 020 +ENCODING 20 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +7FC0 +FFC0 +C6C0 +C6C0 +C6C0 +C6C0 +C6C0 +C6C0 +FEC0 +7EC0 +06C0 +06C0 +06C0 +06C0 +06C0 +06C0 +06C0 +06C0 +06C0 +ENDCHAR +STARTCHAR 021 +ENCODING 21 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 21 0 -2 +BITMAP +7F80 +FFC0 +C0C0 +C040 +6000 +3000 +3800 +7C00 +E600 +C300 +C180 +6180 +3380 +1F00 +0C00 +0600 +0300 +8180 +C0C0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 022 +ENCODING 22 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 7 0 0 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 023 +ENCODING 23 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 19 2 0 +BITMAP +18 +3C +7E +FF +18 +18 +18 +18 +18 +18 +18 +18 +FF +7E +3C +18 +00 +FF +FF +ENDCHAR +STARTCHAR 024 +ENCODING 24 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 19 2 0 +BITMAP +18 +3C +7E +FF +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 025 +ENCODING 25 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 19 2 0 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +FF +7E +3C +18 +ENDCHAR +STARTCHAR 026 +ENCODING 26 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 8 0 3 +BITMAP +0F00 +0380 +00C0 +FFE0 +FFE0 +00C0 +0380 +0F00 +ENDCHAR +STARTCHAR 027 +ENCODING 27 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 8 0 3 +BITMAP +1E00 +3800 +6000 +FFE0 +FFE0 +6000 +3800 +1E00 +ENDCHAR +STARTCHAR 028 +ENCODING 28 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 8 0 6 +BITMAP +C000 +C000 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 029 +ENCODING 29 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 8 0 3 +BITMAP +1200 +3300 +7380 +FFC0 +FFC0 +7380 +3300 +1200 +ENDCHAR +STARTCHAR 030 +ENCODING 30 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 13 0 3 +BITMAP +0C00 +0C00 +1E00 +1E00 +3F00 +3F80 +7F80 +7F80 +FFC0 +FFC0 +FFC0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 031 +ENCODING 31 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 12 0 2 +BITMAP +FFC0 +FFC0 +FFC0 +FFC0 +7F80 +7F80 +3F00 +3F00 +1E00 +1E00 +0C00 +0C00 +ENDCHAR +STARTCHAR 032 +ENCODING 32 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR 033 +ENCODING 33 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 4 19 4 0 +BITMAP +60 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +60 +60 +60 +60 +60 +00 +00 +00 +60 +60 +60 +ENDCHAR +STARTCHAR 034 +ENCODING 34 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 6 2 16 +BITMAP +C6 +C6 +C6 +C6 +C6 +44 +ENDCHAR +STARTCHAR 035 +ENCODING 35 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 14 0 3 +BITMAP +3300 +3300 +3300 +FFC0 +FFC0 +3300 +3300 +3300 +3300 +FFC0 +FFC0 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 036 +ENCODING 36 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 25 0 -3 +BITMAP +0A00 +0A00 +0A00 +7F80 +FFC0 +CAC0 +CAC0 +CAC0 +CA00 +CA00 +EA00 +7E00 +3F80 +0BC0 +0AC0 +0AC0 +CAC0 +CAC0 +CAC0 +CAC0 +FFC0 +7F80 +0A00 +0A00 +0A00 +ENDCHAR +STARTCHAR 037 +ENCODING 37 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 14 0 0 +BITMAP +C020 +C060 +C0E0 +01C0 +0380 +0700 +0E00 +1C00 +3800 +7000 +E000 +C060 +8060 +0060 +ENDCHAR +STARTCHAR 038 +ENCODING 38 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 -1 +BITMAP +1E00 +3F00 +6300 +6300 +6300 +6340 +36C0 +3D80 +3F00 +6E00 +C700 +C300 +C300 +C300 +C300 +C300 +C300 +FFC0 +7DC0 +ENDCHAR +STARTCHAR 039 +ENCODING 39 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 4 7 3 15 +BITMAP +30 +30 +30 +30 +30 +F0 +E0 +ENDCHAR +STARTCHAR 040 +ENCODING 40 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 6 19 3 0 +BITMAP +1C +3C +70 +E0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +E0 +70 +3C +1C +ENDCHAR +STARTCHAR 041 +ENCODING 41 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 6 19 3 0 +BITMAP +E0 +70 +38 +1C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +1C +38 +70 +E0 +ENDCHAR +STARTCHAR 042 +ENCODING 42 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 12 0 4 +BITMAP +7380 +7380 +7380 +1E00 +CCC0 +FFC0 +FFC0 +CCC0 +1E00 +7380 +7380 +7380 +ENDCHAR +STARTCHAR 043 +ENCODING 43 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 8 1 6 +BITMAP +18 +18 +18 +FF +FF +18 +18 +18 +ENDCHAR +STARTCHAR 044 +ENCODING 44 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 4 7 2 -2 +BITMAP +30 +30 +30 +30 +30 +F0 +E0 +ENDCHAR +STARTCHAR 045 +ENCODING 45 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 2 1 9 +BITMAP +FF80 +FF80 +ENDCHAR +STARTCHAR 046 +ENCODING 46 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 2 3 4 0 +BITMAP +C0 +C0 +C0 +ENDCHAR +STARTCHAR 047 +ENCODING 47 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 10 0 5 +BITMAP +00C0 +0180 +0300 +0600 +0C00 +1800 +3000 +6000 +C000 +8000 +ENDCHAR +STARTCHAR 048 +ENCODING 48 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +1E00 +3F00 +7380 +E1C0 +C0C0 +C0C0 +C0C0 +C0C0 +CCC0 +CCC0 +CCC0 +C0C0 +C0C0 +C0C0 +C0C0 +E1C0 +7380 +3F00 +1E00 +ENDCHAR +STARTCHAR 049 +ENCODING 49 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 19 2 0 +BITMAP +18 +38 +78 +F8 +D8 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +FF +FF +ENDCHAR +STARTCHAR 050 +ENCODING 50 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +7F80 +FFC0 +C0C0 +C0C0 +00C0 +00C0 +00C0 +00C0 +3F80 +7F00 +C000 +C000 +C000 +C000 +C000 +C0C0 +C0C0 +FFC0 +7FC0 +ENDCHAR +STARTCHAR 051 +ENCODING 51 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +7F80 +FFC0 +C0C0 +C0C0 +00C0 +00C0 +00C0 +00C0 +1F80 +1F80 +00C0 +00C0 +00C0 +00C0 +00C0 +C0C0 +C0C0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 052 +ENCODING 52 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +0300 +0700 +0F00 +1F00 +3B00 +7300 +E300 +C300 +FFC0 +FFC0 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0FC0 +0FC0 +ENDCHAR +STARTCHAR 053 +ENCODING 53 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +FFC0 +FFC0 +C000 +C000 +C000 +C000 +C000 +C000 +FF80 +FFC0 +00C0 +00C0 +00C0 +00C0 +00C0 +C0C0 +C0C0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 054 +ENCODING 54 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +1F00 +3F00 +7000 +E000 +C000 +C000 +C000 +C000 +FF00 +FF80 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 055 +ENCODING 55 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +FFC0 +FFC0 +C0C0 +C0C0 +00C0 +00C0 +01C0 +0380 +0700 +0E00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 056 +ENCODING 56 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +7F80 +FFC0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +7F80 +7F80 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 057 +ENCODING 57 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +7F80 +FFC0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +7FC0 +3FC0 +00C0 +00C0 +00C0 +00C0 +00C0 +01C0 +0380 +7F00 +7E00 +ENDCHAR +STARTCHAR 058 +ENCODING 58 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 2 10 5 4 +BITMAP +C0 +C0 +C0 +00 +00 +00 +00 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 059 +ENCODING 59 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 4 13 2 -2 +BITMAP +30 +30 +30 +00 +00 +00 +30 +30 +30 +30 +30 +F0 +E0 +ENDCHAR +STARTCHAR 060 +ENCODING 60 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 12 0 3 +BITMAP +06 +0C +18 +30 +60 +C0 +C0 +60 +30 +18 +0C +06 +ENDCHAR +STARTCHAR 061 +ENCODING 61 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 6 1 7 +BITMAP +FF80 +FF80 +0000 +0000 +FF80 +FF80 +ENDCHAR +STARTCHAR 062 +ENCODING 62 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 12 3 3 +BITMAP +C0 +60 +30 +18 +0C +06 +06 +0C +18 +30 +60 +C0 +ENDCHAR +STARTCHAR 063 +ENCODING 63 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +7F80 +FFC0 +C0C0 +C0C0 +00C0 +01C0 +0380 +0700 +0E00 +0C00 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 064 +ENCODING 64 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 18 0 0 +BITMAP +7F80 +FFC0 +C0C0 +C0C0 +C0C0 +C3C0 +C7C0 +CFC0 +CFC0 +CFC0 +CFC0 +CF80 +C700 +C000 +C000 +C000 +FF80 +7F80 +ENDCHAR +STARTCHAR 065 +ENCODING 65 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 19 1 0 +BITMAP +0800 +1C00 +3E00 +7700 +E380 +C180 +C180 +C180 +FF80 +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +ENDCHAR +STARTCHAR 066 +ENCODING 66 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +FF80 +FFC0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +3FC0 +3FC0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +FFC0 +FF80 +ENDCHAR +STARTCHAR 067 +ENCODING 67 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +1F80 +3FC0 +70C0 +E0C0 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +E0C0 +70C0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 068 +ENCODING 68 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +FF00 +FF80 +31C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +31C0 +FF80 +FF00 +ENDCHAR +STARTCHAR 069 +ENCODING 69 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +FFC0 +FFC0 +30C0 +30C0 +3000 +3000 +3000 +3300 +3F00 +3F00 +3300 +3000 +3000 +3000 +3000 +30C0 +30C0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 070 +ENCODING 70 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +FFC0 +FFC0 +30C0 +30C0 +3000 +3000 +3000 +3300 +3F00 +3F00 +3300 +3000 +3000 +3000 +3000 +3000 +3000 +FC00 +FC00 +ENDCHAR +STARTCHAR 071 +ENCODING 71 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +1F80 +3FC0 +60C0 +C0C0 +C000 +C000 +C000 +C000 +C000 +C000 +C7C0 +C7C0 +C0C0 +C0C0 +C0C0 +C0C0 +E1C0 +7FC0 +3EC0 +ENDCHAR +STARTCHAR 072 +ENCODING 72 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +FFC0 +FFC0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +ENDCHAR +STARTCHAR 073 +ENCODING 73 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 6 19 3 0 +BITMAP +FC +FC +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +FC +FC +ENDCHAR +STARTCHAR 074 +ENCODING 74 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 19 1 0 +BITMAP +1F80 +1F80 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +C600 +C600 +C600 +C600 +C600 +FE00 +7C00 +ENDCHAR +STARTCHAR 075 +ENCODING 75 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +F0C0 +F0C0 +30C0 +30C0 +30C0 +30C0 +30C0 +31C0 +3F80 +3F00 +3380 +31C0 +30C0 +30C0 +30C0 +30C0 +30C0 +F0C0 +F0C0 +ENDCHAR +STARTCHAR 076 +ENCODING 76 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +FC00 +FC00 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +30C0 +30C0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 077 +ENCODING 77 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +6180 +F3C0 +FFC0 +FFC0 +DEC0 +CCC0 +CCC0 +CCC0 +CCC0 +CCC0 +CCC0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +ENDCHAR +STARTCHAR 078 +ENCODING 78 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +C0C0 +C0C0 +E0C0 +F0C0 +F8C0 +DCC0 +CEC0 +C7C0 +C3C0 +C1C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +ENDCHAR +STARTCHAR 079 +ENCODING 79 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 19 1 0 +BITMAP +7F00 +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FF80 +7F00 +ENDCHAR +STARTCHAR 080 +ENCODING 80 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 1 0 +BITMAP +FF80 +FFC0 +30C0 +30C0 +30C0 +30C0 +30C0 +31C0 +3F80 +3F00 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +FC00 +FC00 +ENDCHAR +STARTCHAR 081 +ENCODING 81 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 22 1 -3 +BITMAP +7F00 +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +D980 +D980 +D980 +FF80 +7F00 +1C00 +0F80 +0780 +ENDCHAR +STARTCHAR 082 +ENCODING 82 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +FF80 +FFC0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +3F80 +3F80 +31C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +F0C0 +F0C0 +ENDCHAR +STARTCHAR 083 +ENCODING 83 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +7F80 +FFC0 +C0C0 +C0C0 +C000 +C000 +C000 +C000 +7F00 +3F80 +00C0 +00C0 +00C0 +00C0 +00C0 +C0C0 +C0C0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 084 +ENCODING 84 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 1 0 +BITMAP +FFC0 +FFC0 +CCC0 +CCC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +3F00 +3F00 +ENDCHAR +STARTCHAR 085 +ENCODING 85 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 19 1 0 +BITMAP +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FF80 +7F00 +ENDCHAR +STARTCHAR 086 +ENCODING 86 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +E1C0 +7380 +3F00 +1E00 +0C00 +ENDCHAR +STARTCHAR 087 +ENCODING 87 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +CCC0 +CCC0 +CCC0 +CCC0 +CCC0 +CCC0 +DEC0 +FFC0 +FFC0 +F3C0 +6180 +ENDCHAR +STARTCHAR 088 +ENCODING 88 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +C0C0 +C0C0 +C0C0 +6180 +6180 +3300 +3300 +1E00 +1E00 +0C00 +1E00 +1E00 +3300 +3300 +6180 +6180 +C0C0 +C0C0 +C0C0 +ENDCHAR +STARTCHAR 089 +ENCODING 89 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 19 2 0 +BITMAP +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +FF +7E +18 +18 +18 +18 +18 +18 +18 +7E +7E +ENDCHAR +STARTCHAR 090 +ENCODING 90 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +FFC0 +FFC0 +C0C0 +C0C0 +00C0 +00C0 +01C0 +0380 +0700 +0E00 +1C00 +3800 +7000 +E000 +C000 +C0C0 +C0C0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 091 +ENCODING 91 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 6 19 3 0 +BITMAP +FC +FC +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FC +FC +ENDCHAR +STARTCHAR 092 +ENCODING 92 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 12 0 3 +BITMAP +8000 +C000 +E000 +7000 +3800 +1C00 +0E00 +0700 +0380 +01C0 +00C0 +0040 +ENDCHAR +STARTCHAR 093 +ENCODING 93 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 6 19 3 0 +BITMAP +FC +FC +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +FC +FC +ENDCHAR +STARTCHAR 094 +ENCODING 94 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 6 0 14 +BITMAP +0400 +0E00 +1B00 +3180 +60C0 +C060 +ENDCHAR +STARTCHAR 095 +ENCODING 95 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 2 0 -3 +BITMAP +FFF0 +FFF0 +ENDCHAR +STARTCHAR 096 +ENCODING 96 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 4 7 4 15 +BITMAP +C0 +C0 +C0 +C0 +C0 +F0 +70 +ENDCHAR +STARTCHAR 097 +ENCODING 97 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 13 1 0 +BITMAP +7E00 +7F00 +0300 +0300 +0300 +7F00 +FF00 +C300 +C300 +C300 +C300 +FFC0 +7DC0 +ENDCHAR +STARTCHAR 098 +ENCODING 98 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +F000 +F000 +3000 +3000 +3000 +3000 +3F00 +3F80 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +3FC0 +3F80 +ENDCHAR +STARTCHAR 099 +ENCODING 99 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 13 1 0 +BITMAP +7F00 +FF80 +C180 +C180 +C000 +C000 +C000 +C000 +C000 +C180 +C180 +FF80 +7F00 +ENDCHAR +STARTCHAR 100 +ENCODING 100 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +0F00 +0F00 +0300 +0300 +0300 +0300 +3F00 +7F00 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +FFC0 +7DC0 +ENDCHAR +STARTCHAR 101 +ENCODING 101 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 13 0 0 +BITMAP +7F80 +FFC0 +C0C0 +C0C0 +C0C0 +FFC0 +FFC0 +C000 +C000 +C0C0 +C0C0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 102 +ENCODING 102 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 19 2 0 +BITMAP +0E +1F +33 +31 +31 +30 +FC +FC +30 +30 +30 +30 +30 +30 +30 +30 +30 +FC +FC +ENDCHAR +STARTCHAR 103 +ENCODING 103 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 18 0 -5 +BITMAP +7DC0 +FFC0 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +7F00 +7F00 +0300 +0300 +C300 +FF00 +7E00 +ENDCHAR +STARTCHAR 104 +ENCODING 104 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +F000 +F000 +3000 +3000 +3000 +3000 +3300 +3780 +3CC0 +38C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +F0C0 +F0C0 +ENDCHAR +STARTCHAR 105 +ENCODING 105 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 6 18 3 0 +BITMAP +30 +30 +30 +00 +00 +F0 +F0 +30 +30 +30 +30 +30 +30 +30 +30 +30 +FC +FC +ENDCHAR +STARTCHAR 106 +ENCODING 106 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 23 2 -5 +BITMAP +06 +06 +06 +00 +00 +1E +1E +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +C6 +C6 +C6 +FE +7C +ENDCHAR +STARTCHAR 107 +ENCODING 107 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +F000 +F000 +3000 +3000 +3000 +30C0 +30C0 +31C0 +3380 +3700 +3E00 +3F00 +3380 +31C0 +30C0 +30C0 +30C0 +F0C0 +F0C0 +ENDCHAR +STARTCHAR 108 +ENCODING 108 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 6 19 3 0 +BITMAP +F0 +F0 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +FC +FC +ENDCHAR +STARTCHAR 109 +ENCODING 109 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 13 0 0 +BITMAP +6180 +F3C0 +FFC0 +DEC0 +CCC0 +CCC0 +CCC0 +CCC0 +CCC0 +CCC0 +C0C0 +C0C0 +C0C0 +ENDCHAR +STARTCHAR 110 +ENCODING 110 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 13 0 0 +BITMAP +EF80 +FFC0 +38C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +ENDCHAR +STARTCHAR 111 +ENCODING 111 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 13 1 0 +BITMAP +7F00 +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FF80 +7F00 +ENDCHAR +STARTCHAR 112 +ENCODING 112 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 18 0 -5 +BITMAP +EF80 +FFC0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +3FC0 +3F80 +3000 +3000 +3000 +FC00 +FC00 +ENDCHAR +STARTCHAR 113 +ENCODING 113 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 18 0 -5 +BITMAP +7DC0 +FFC0 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +FF00 +7F00 +0300 +0300 +0300 +0FC0 +0FC0 +ENDCHAR +STARTCHAR 114 +ENCODING 114 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 13 0 0 +BITMAP +EF80 +FFC0 +38C0 +3040 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +FC00 +FC00 +ENDCHAR +STARTCHAR 115 +ENCODING 115 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 13 0 0 +BITMAP +7F80 +FFC0 +C0C0 +C0C0 +E000 +7F00 +3F80 +01C0 +00C0 +C0C0 +C0C0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 116 +ENCODING 116 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 19 1 0 +BITMAP +08 +18 +18 +18 +18 +18 +FF +FF +18 +18 +18 +18 +18 +18 +18 +1B +1B +1F +0E +ENDCHAR +STARTCHAR 117 +ENCODING 117 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 13 0 0 +BITMAP +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +FFC0 +7DC0 +ENDCHAR +STARTCHAR 118 +ENCODING 118 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 13 1 0 +BITMAP +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +E380 +7700 +3E00 +1C00 +0800 +ENDCHAR +STARTCHAR 119 +ENCODING 119 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 13 0 0 +BITMAP +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +CCC0 +CCC0 +CCC0 +CCC0 +CCC0 +DEC0 +FFC0 +7380 +ENDCHAR +STARTCHAR 120 +ENCODING 120 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 13 0 0 +BITMAP +C0C0 +E1C0 +7380 +3F00 +1E00 +0C00 +0C00 +1E00 +3F00 +7380 +E1C0 +C0C0 +C0C0 +ENDCHAR +STARTCHAR 121 +ENCODING 121 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 18 1 -5 +BITMAP +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FF80 +7F80 +0180 +0380 +0700 +FE00 +FC00 +ENDCHAR +STARTCHAR 122 +ENCODING 122 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 13 0 0 +BITMAP +FFC0 +FFC0 +C0C0 +C180 +0300 +0600 +0C00 +1800 +3000 +60C0 +C0C0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 123 +ENCODING 123 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 19 1 0 +BITMAP +0F80 +1F80 +1800 +1800 +1800 +1800 +1800 +1800 +7800 +E000 +7800 +1800 +1800 +1800 +1800 +1800 +1800 +1F80 +0F80 +ENDCHAR +STARTCHAR 124 +ENCODING 124 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 2 19 5 0 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +00 +00 +00 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 125 +ENCODING 125 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 19 2 0 +BITMAP +F800 +FC00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0F00 +0380 +0F00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +FC00 +F800 +ENDCHAR +STARTCHAR 126 +ENCODING 126 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 5 0 14 +BITMAP +3C60 +66C0 +C6C0 +86C0 +8380 +ENDCHAR +STARTCHAR 127 +ENCODING 127 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 14 0 1 +BITMAP +0C00 +1E00 +3F00 +7380 +E1C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 128 +ENCODING 128 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 22 0 -4 +BITMAP +1F80 +3FC0 +70C0 +E0C0 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +E0C0 +70C0 +3FC0 +1F80 +03C0 +00C0 +7FC0 +7F80 +ENDCHAR +STARTCHAR 129 +ENCODING 129 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +C300 +C300 +C300 +0000 +0000 +0000 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +FFC0 +7DC0 +ENDCHAR +STARTCHAR 130 +ENCODING 130 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 20 0 0 +BITMAP +0180 +0300 +0600 +0C00 +1800 +0000 +0000 +7F80 +FFC0 +C0C0 +C0C0 +C0C0 +FFC0 +FFC0 +C000 +C000 +C0C0 +C0C0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 131 +ENCODING 131 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 20 1 0 +BITMAP +0800 +1C00 +3600 +6300 +0000 +0000 +0000 +7E00 +7F00 +0300 +0300 +0300 +7F00 +FF00 +C300 +C300 +C300 +C300 +FFC0 +7DC0 +ENDCHAR +STARTCHAR 132 +ENCODING 132 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 1 0 +BITMAP +6300 +6300 +6300 +0000 +0000 +0000 +7E00 +7F00 +0300 +0300 +0300 +7F00 +FF00 +C300 +C300 +C300 +C300 +FFC0 +7DC0 +ENDCHAR +STARTCHAR 133 +ENCODING 133 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 20 1 0 +BITMAP +6000 +3000 +1800 +0C00 +0600 +0000 +0000 +7E00 +7F00 +0300 +0300 +0300 +7F00 +FF00 +C300 +C300 +C300 +C300 +FFC0 +7DC0 +ENDCHAR +STARTCHAR 134 +ENCODING 134 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 20 1 0 +BITMAP +1E00 +3F00 +3300 +3300 +3F00 +1E00 +0000 +7E00 +7F00 +0300 +0300 +0300 +7F00 +FF00 +C300 +C300 +C300 +C300 +FFC0 +7DC0 +ENDCHAR +STARTCHAR 135 +ENCODING 135 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 17 1 -4 +BITMAP +7F00 +FF80 +C180 +C180 +C000 +C000 +C000 +C000 +C180 +C180 +C700 +FE00 +7F00 +0380 +0180 +7F80 +7F00 +ENDCHAR +STARTCHAR 136 +ENCODING 136 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 20 0 0 +BITMAP +0C00 +1E00 +3300 +6180 +0000 +0000 +0000 +7F80 +FFC0 +C0C0 +C0C0 +C0C0 +FFC0 +FFC0 +C000 +C000 +C0C0 +C0C0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 137 +ENCODING 137 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +6180 +6180 +6180 +0000 +0000 +0000 +7F80 +FFC0 +C0C0 +C0C0 +C0C0 +FFC0 +FFC0 +C000 +C000 +C0C0 +C0C0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 138 +ENCODING 138 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 20 0 0 +BITMAP +6000 +3000 +1800 +0C00 +0600 +0000 +0000 +7F80 +FFC0 +C0C0 +C0C0 +C0C0 +FFC0 +FFC0 +C000 +C000 +C0C0 +C0C0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 139 +ENCODING 139 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 18 2 0 +BITMAP +C6 +C6 +C6 +00 +00 +78 +78 +18 +18 +18 +18 +18 +18 +18 +18 +18 +7E +7E +ENDCHAR +STARTCHAR 140 +ENCODING 140 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 20 2 0 +BITMAP +10 +38 +6C +C6 +00 +00 +00 +78 +78 +18 +18 +18 +18 +18 +18 +18 +18 +18 +7E +7E +ENDCHAR +STARTCHAR 141 +ENCODING 141 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 20 2 0 +BITMAP +C0 +60 +30 +18 +00 +00 +00 +78 +78 +18 +18 +18 +18 +18 +18 +18 +18 +18 +7E +7E +ENDCHAR +STARTCHAR 142 +ENCODING 142 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 22 1 0 +BITMAP +6300 +6300 +6300 +0800 +1C00 +3E00 +7700 +E380 +C180 +C180 +C180 +FF80 +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +ENDCHAR +STARTCHAR 143 +ENCODING 143 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 22 1 0 +BITMAP +1C00 +3E00 +3600 +3E00 +1C00 +0800 +1C00 +3E00 +7700 +E380 +C180 +C180 +FF80 +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +ENDCHAR +STARTCHAR 144 +ENCODING 144 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 22 0 0 +BITMAP +0180 +0300 +0600 +0C00 +0000 +FFC0 +FFC0 +30C0 +30C0 +3000 +3000 +3300 +3F00 +3F00 +3300 +3000 +3000 +3000 +30C0 +30C0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 145 +ENCODING 145 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 14 0 0 +BITMAP +C180 +E3C0 +77C0 +3EC0 +1CC0 +18C0 +30C0 +7FC0 +FF80 +C300 +C300 +C300 +FFC0 +7BC0 +ENDCHAR +STARTCHAR 146 +ENCODING 146 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +1FC0 +3FC0 +7300 +E300 +C300 +C300 +FFC0 +FFC0 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C3C0 +C3C0 +ENDCHAR +STARTCHAR 147 +ENCODING 147 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 20 1 0 +BITMAP +0800 +1C00 +3600 +6300 +0000 +0000 +0000 +7F00 +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FF80 +7F00 +ENDCHAR +STARTCHAR 148 +ENCODING 148 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 19 1 0 +BITMAP +6300 +6300 +6300 +0000 +0000 +0000 +7F00 +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FF80 +7F00 +ENDCHAR +STARTCHAR 149 +ENCODING 149 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 20 1 0 +BITMAP +6000 +3000 +1800 +0C00 +0600 +0000 +0000 +7F00 +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FF80 +7F00 +ENDCHAR +STARTCHAR 150 +ENCODING 150 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 20 0 0 +BITMAP +0800 +1C00 +3600 +6300 +0000 +0000 +0000 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FFC0 +7DC0 +ENDCHAR +STARTCHAR 151 +ENCODING 151 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 20 0 0 +BITMAP +6000 +3000 +1800 +0C00 +0600 +0000 +0000 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FFC0 +7DC0 +ENDCHAR +STARTCHAR 152 +ENCODING 152 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 24 1 -5 +BITMAP +C180 +C180 +C180 +0000 +0000 +0000 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +7F80 +3F80 +0180 +0380 +0700 +FE00 +FC00 +ENDCHAR +STARTCHAR 153 +ENCODING 153 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 22 1 0 +BITMAP +6300 +6300 +6300 +0000 +0000 +7F00 +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FF80 +7F00 +ENDCHAR +STARTCHAR 154 +ENCODING 154 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 22 1 0 +BITMAP +C180 +C180 +C180 +0000 +0000 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FF80 +7F00 +ENDCHAR +STARTCHAR 155 +ENCODING 155 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 25 0 -3 +BITMAP +0A00 +0A00 +0A00 +1F80 +3FC0 +7AC0 +EAC0 +CA00 +CA00 +CA00 +CA00 +CA00 +CA00 +CA00 +CA00 +CA00 +CA00 +CA00 +EAC0 +7AC0 +3FC0 +1F80 +0A00 +0A00 +0A00 +ENDCHAR +STARTCHAR 156 +ENCODING 156 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 21 0 0 +BITMAP +1E00 +3F00 +3180 +3080 +3080 +3080 +3000 +3000 +FC00 +F800 +3000 +3000 +3000 +3000 +3000 +3040 +3040 +3040 +30C0 +FFC0 +FF80 +ENDCHAR +STARTCHAR 157 +ENCODING 157 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 19 2 0 +BITMAP +C3 +C3 +C3 +C3 +66 +3C +18 +18 +FF +FF +18 +18 +FF +FF +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 158 +ENCODING 158 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 21 0 0 +BITMAP +FE00 +FF00 +C180 +C180 +C180 +C180 +FF00 +FE00 +C080 +C180 +C180 +C3C0 +C3C0 +C180 +C180 +C180 +C180 +C180 +C180 +C1C0 +C0C0 +ENDCHAR +STARTCHAR 159 +ENCODING 159 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 23 0 -3 +BITMAP +03E0 +07F0 +0630 +0610 +0610 +0600 +0600 +0600 +0600 +3FC0 +3FC0 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +8600 +8600 +C600 +FE00 +7C00 +ENDCHAR +STARTCHAR 160 +ENCODING 160 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 20 1 0 +BITMAP +0600 +0C00 +1800 +3000 +6000 +0000 +0000 +7E00 +7F00 +0300 +0300 +0300 +7F00 +FF00 +C300 +C300 +C300 +E300 +FFC0 +7DC0 +ENDCHAR +STARTCHAR 161 +ENCODING 161 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 6 20 3 0 +BITMAP +0C +18 +30 +60 +C0 +00 +00 +F0 +F0 +30 +30 +30 +30 +30 +30 +30 +30 +30 +FC +FC +ENDCHAR +STARTCHAR 162 +ENCODING 162 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 20 1 0 +BITMAP +0300 +0600 +0C00 +1800 +3000 +0000 +0000 +7F00 +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FF80 +7F00 +ENDCHAR +STARTCHAR 163 +ENCODING 163 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 20 0 0 +BITMAP +0300 +0600 +0C00 +1800 +3000 +0000 +0000 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +FFC0 +7DC0 +ENDCHAR +STARTCHAR 164 +ENCODING 164 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +38C0 +6C80 +C780 +0000 +0000 +0000 +EF80 +FFC0 +38C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +ENDCHAR +STARTCHAR 165 +ENCODING 165 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 21 0 0 +BITMAP +1EC0 +3380 +E100 +0000 +C0C0 +E0C0 +F0C0 +F8C0 +DCC0 +CEC0 +C7C0 +C3C0 +C1C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +ENDCHAR +STARTCHAR 166 +ENCODING 166 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 12 2 8 +BITMAP +7C +FC +CC +CC +CC +CC +FF +7F +00 +00 +FF +FF +ENDCHAR +STARTCHAR 167 +ENCODING 167 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 12 2 8 +BITMAP +7E +FF +C3 +C3 +C3 +C3 +FF +7E +00 +00 +FF +FF +ENDCHAR +STARTCHAR 168 +ENCODING 168 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 21 0 0 +BITMAP +0600 +0600 +0600 +0000 +0000 +0000 +0600 +0600 +0600 +0E00 +1C00 +3800 +7000 +E000 +C000 +C000 +C000 +C0C0 +C0C0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 169 +ENCODING 169 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 12 0 0 +BITMAP +FFC0 +FFC0 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 170 +ENCODING 170 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 12 0 0 +BITMAP +FFC0 +FFC0 +00C0 +00C0 +00C0 +00C0 +00C0 +00C0 +00C0 +00C0 +00C0 +00C0 +ENDCHAR +STARTCHAR 171 +ENCODING 171 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +C000 +C000 +C040 +C0C0 +C180 +C300 +C600 +CC00 +1800 +3000 +6000 +CF80 +90C0 +00C0 +0180 +0300 +0600 +0C40 +1FC0 +ENDCHAR +STARTCHAR 172 +ENCODING 172 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +C000 +C000 +C040 +C0C0 +C180 +C300 +C600 +CC80 +1980 +3380 +6780 +CD80 +9980 +1FC0 +1FC0 +0180 +0180 +0180 +0180 +ENDCHAR +STARTCHAR 173 +ENCODING 173 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 4 19 4 0 +BITMAP +60 +60 +60 +00 +00 +00 +60 +60 +60 +60 +60 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +60 +ENDCHAR +STARTCHAR 174 +ENCODING 174 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 11 0 2 +BITMAP +0660 +0CC0 +1980 +3300 +6600 +CC00 +6600 +3300 +1980 +0CC0 +0660 +ENDCHAR +STARTCHAR 175 +ENCODING 175 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 11 0 2 +BITMAP +CC00 +6600 +3300 +1980 +0CC0 +0660 +0CC0 +1980 +3300 +6600 +CC00 +ENDCHAR +STARTCHAR 176 +ENCODING 176 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 30 0 -8 +BITMAP +AAAA +5554 +AAAA +5554 +AAAA +5554 +AAAA +5554 +AAAA +5554 +AAAA +5554 +AAAA +5554 +AAAA +5554 +AAAA +5554 +AAAA +5554 +AAAA +5554 +AAAA +5554 +AAAA +5554 +AAAA +5554 +AAAA +5554 +ENDCHAR +STARTCHAR 177 +ENCODING 177 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 30 0 -8 +BITMAP +FFFF +EDB6 +FFFF +DB6D +FFFF +B6DB +FFFF +EDB6 +FFFF +DB6D +FFFF +EDB6 +FFFF +DB6D +FFFF +B6DA +FFFF +EDB6 +FFFF +DB6D +FFFF +B6DB +FFFF +EDB6 +FFFF +DB6D +FFFF +B6DB +FFFF +EB6D +ENDCHAR +STARTCHAR 178 +ENCODING 178 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 30 0 -8 +BITMAP +BEFB +F7DF +FFFF +BEFB +F7DF +FFFF +BEFB +F7DF +FFFF +BEFB +F7DF +FFFF +BEFB +F7DF +FFFF +BEFB +F7DF +FFFF +BEFB +F7DF +FFFF +BEFB +F7DF +FFFF +BEFB +F7DF +FFFF +BEFB +F7DF +FFFF +ENDCHAR +STARTCHAR 179 +ENCODING 179 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 2 30 5 -8 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 180 +ENCODING 180 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 30 0 -8 +BITMAP +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +FE +FE +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +ENDCHAR +STARTCHAR 181 +ENCODING 181 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 30 0 -8 +BITMAP +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +FE +FE +06 +06 +FE +FE +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +ENDCHAR +STARTCHAR 182 +ENCODING 182 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 30 0 -8 +BITMAP +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +F980 +F980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +ENDCHAR +STARTCHAR 183 +ENCODING 183 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 16 0 -8 +BITMAP +FF80 +FF80 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +ENDCHAR +STARTCHAR 184 +ENCODING 184 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 18 0 -8 +BITMAP +FE +FE +06 +06 +FE +FE +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +ENDCHAR +STARTCHAR 185 +ENCODING 185 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 30 0 -8 +BITMAP +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +F980 +F980 +0180 +0180 +F980 +F980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +ENDCHAR +STARTCHAR 186 +ENCODING 186 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 6 30 3 -8 +BITMAP +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +CC +ENDCHAR +STARTCHAR 187 +ENCODING 187 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 18 0 -8 +BITMAP +FF80 +FF80 +0180 +0180 +F980 +F980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +ENDCHAR +STARTCHAR 188 +ENCODING 188 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 18 0 4 +BITMAP +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +F980 +F980 +0180 +0180 +FF80 +FF80 +ENDCHAR +STARTCHAR 189 +ENCODING 189 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 16 0 6 +BITMAP +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +FF80 +FF80 +ENDCHAR +STARTCHAR 190 +ENCODING 190 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 18 0 4 +BITMAP +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +FE +FE +06 +06 +FE +FE +ENDCHAR +STARTCHAR 191 +ENCODING 191 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 16 0 -8 +BITMAP +FE +FE +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +ENDCHAR +STARTCHAR 192 +ENCODING 192 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 16 5 6 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FE +FE +ENDCHAR +STARTCHAR 193 +ENCODING 193 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 16 0 6 +BITMAP +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 194 +ENCODING 194 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 16 0 -8 +BITMAP +FFF0 +FFF0 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 195 +ENCODING 195 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 30 5 -8 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FE +FE +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 196 +ENCODING 196 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 2 0 6 +BITMAP +FFF0 +FFF0 +ENDCHAR +STARTCHAR 197 +ENCODING 197 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 30 0 -8 +BITMAP +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +FFF0 +FFF0 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 198 +ENCODING 198 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 30 5 -8 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FE +FE +C0 +C0 +FE +FE +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 199 +ENCODING 199 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 30 0 -8 +BITMAP +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +19F0 +19F0 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +ENDCHAR +STARTCHAR 200 +ENCODING 200 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 18 3 4 +BITMAP +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CF80 +CF80 +C000 +C000 +FF80 +FF80 +ENDCHAR +STARTCHAR 201 +ENCODING 201 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 18 3 -8 +BITMAP +FF80 +FF80 +C000 +C000 +CF80 +CF80 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +ENDCHAR +STARTCHAR 202 +ENCODING 202 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 18 0 4 +BITMAP +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +F9F0 +F9F0 +0000 +0000 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 203 +ENCODING 203 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 18 0 -8 +BITMAP +FFF0 +FFF0 +0000 +0000 +F9F0 +F9F0 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +ENDCHAR +STARTCHAR 204 +ENCODING 204 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 30 0 -8 +BITMAP +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +19F0 +19F0 +1800 +1800 +19F0 +19F0 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +ENDCHAR +STARTCHAR 205 +ENCODING 205 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 6 0 4 +BITMAP +FFF0 +FFF0 +0000 +0000 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 206 +ENCODING 206 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 30 0 -8 +BITMAP +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +F9F0 +F9F0 +0000 +0000 +F9F0 +F9F0 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +ENDCHAR +STARTCHAR 207 +ENCODING 207 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 18 0 4 +BITMAP +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +FFF0 +FFF0 +0000 +0000 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 208 +ENCODING 208 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 16 0 6 +BITMAP +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 209 +ENCODING 209 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 18 0 -8 +BITMAP +FFF0 +FFF0 +0000 +0000 +FFF0 +FFF0 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 210 +ENCODING 210 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 16 0 -8 +BITMAP +FFF0 +FFF0 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +ENDCHAR +STARTCHAR 211 +ENCODING 211 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 16 3 6 +BITMAP +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +FF80 +FF80 +ENDCHAR +STARTCHAR 212 +ENCODING 212 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 18 5 4 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FE +FE +C0 +C0 +FE +FE +ENDCHAR +STARTCHAR 213 +ENCODING 213 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 18 5 -8 +BITMAP +FE +FE +C0 +C0 +FE +FE +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 214 +ENCODING 214 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 16 3 -8 +BITMAP +FF80 +FF80 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +CC00 +ENDCHAR +STARTCHAR 215 +ENCODING 215 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 30 0 -8 +BITMAP +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +FFF0 +FFF0 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +ENDCHAR +STARTCHAR 216 +ENCODING 216 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 30 0 -8 +BITMAP +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +FFF0 +FFF0 +0600 +0600 +FFF0 +FFF0 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 217 +ENCODING 217 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 16 0 6 +BITMAP +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +FE +FE +ENDCHAR +STARTCHAR 218 +ENCODING 218 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 16 5 -8 +BITMAP +FE +FE +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 219 +ENCODING 219 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 30 0 -8 +BITMAP +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 220 +ENCODING 220 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 15 0 -8 +BITMAP +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 221 +ENCODING 221 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 30 0 -8 +BITMAP +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +ENDCHAR +STARTCHAR 222 +ENCODING 222 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 5 30 7 -8 +BITMAP +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +ENDCHAR +STARTCHAR 223 +ENCODING 223 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 15 0 7 +BITMAP +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 224 +ENCODING 224 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 13 0 0 +BITMAP +7DE0 +FF80 +C700 +C600 +C600 +C600 +C600 +C600 +C600 +C600 +C700 +FF80 +7DE0 +ENDCHAR +STARTCHAR 225 +ENCODING 225 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +7E00 +FF00 +C180 +C180 +C180 +C180 +C180 +DF80 +DF00 +C780 +C1C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +CFC0 +CF80 +ENDCHAR +STARTCHAR 226 +ENCODING 226 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +FFC0 +FFC0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 227 +ENCODING 227 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 16 0 0 +BITMAP +FFC0 +FFC0 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR 228 +ENCODING 228 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 18 0 -1 +BITMAP +FFC0 +FFC0 +C0C0 +C0C0 +C000 +E000 +7000 +3800 +1C00 +3800 +7000 +E000 +C000 +C000 +C0C0 +C0C0 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 229 +ENCODING 229 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 16 0 0 +BITMAP +7FE0 +FFE0 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +C300 +FF00 +7E00 +ENDCHAR +STARTCHAR 230 +ENCODING 230 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 17 0 -2 +BITMAP +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +3FC0 +3F80 +3000 +3000 +F000 +E000 +ENDCHAR +STARTCHAR 231 +ENCODING 231 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 16 0 0 +BITMAP +39E0 +7F60 +E600 +C600 +8600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 232 +ENCODING 232 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 18 1 0 +BITMAP +FF80 +FF80 +1C00 +1C00 +7F00 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +7F00 +1C00 +1C00 +FF80 +FF80 +ENDCHAR +STARTCHAR 233 +ENCODING 233 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 18 1 0 +BITMAP +3E00 +7700 +E380 +C180 +C180 +C180 +C180 +C180 +FF80 +FF80 +C180 +C180 +C180 +C180 +C180 +E380 +7700 +3E00 +ENDCHAR +STARTCHAR 234 +ENCODING 234 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 19 0 0 +BITMAP +1E00 +3F00 +7380 +E1C0 +C0C0 +C0C0 +C0C0 +C0C0 +E1C0 +7380 +3300 +3300 +3300 +3300 +3300 +3300 +3300 +F3C0 +F3C0 +ENDCHAR +STARTCHAR 235 +ENCODING 235 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 19 1 0 +BITMAP +1F80 +3F80 +6000 +7800 +1C00 +0E00 +0600 +7F00 +FF80 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +FF80 +7F00 +ENDCHAR +STARTCHAR 236 +ENCODING 236 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 13 1 2 +BITMAP +7F80 +FFC0 +CCC0 +CCC0 +CCC0 +CCC0 +CCC0 +CCC0 +CCC0 +CCC0 +CCC0 +FFC0 +7F80 +ENDCHAR +STARTCHAR 237 +ENCODING 237 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 19 0 -1 +BITMAP +0070 +00C0 +0180 +3FC0 +7FE0 +6660 +6660 +6660 +6660 +6660 +6660 +6660 +6660 +6660 +7FE0 +3FC0 +1800 +3000 +E000 +ENDCHAR +STARTCHAR 238 +ENCODING 238 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 20 2 0 +BITMAP +1F +3F +60 +C0 +C0 +C0 +C0 +C0 +C0 +FF +FF +C0 +C0 +C0 +C0 +C0 +C0 +60 +3F +1F +ENDCHAR +STARTCHAR 239 +ENCODING 239 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 18 0 0 +BITMAP +7F80 +FFC0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +ENDCHAR +STARTCHAR 240 +ENCODING 240 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 10 0 3 +BITMAP +FFE0 +FFE0 +0000 +0000 +FFE0 +FFE0 +0000 +0000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 241 +ENCODING 241 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 10 14 1 0 +BITMAP +0C00 +0C00 +0C00 +7F80 +7F80 +0C00 +0C00 +0C00 +0000 +0000 +0000 +0000 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 242 +ENCODING 242 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 17 2 0 +BITMAP +C0 +60 +30 +18 +0C +06 +06 +0C +18 +30 +60 +C0 +00 +00 +00 +FE +FE +ENDCHAR +STARTCHAR 243 +ENCODING 243 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 17 2 0 +BITMAP +06 +0C +18 +30 +60 +C0 +C0 +60 +30 +18 +0C +06 +00 +00 +00 +FE +FE +ENDCHAR +STARTCHAR 244 +ENCODING 244 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 29 5 -8 +BITMAP +7C +FE +C6 +C6 +C6 +C6 +C6 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 245 +ENCODING 245 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 28 0 -6 +BITMAP +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +C6 +C6 +C6 +C6 +C6 +FE +7C +ENDCHAR +STARTCHAR 246 +ENCODING 246 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 12 1 4 +BITMAP +18 +18 +18 +00 +00 +FF +FF +00 +00 +18 +18 +18 +ENDCHAR +STARTCHAR 247 +ENCODING 247 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 11 9 0 5 +BITMAP +7820 +FC60 +CFE0 +87C0 +0000 +7820 +FC60 +CFE0 +87C0 +ENDCHAR +STARTCHAR 248 +ENCODING 248 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 8 2 12 +BITMAP +7E +FF +C3 +C3 +C3 +C3 +FF +7E +ENDCHAR +STARTCHAR 249 +ENCODING 249 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 3 4 5 6 +BITMAP +E0 +E0 +E0 +E0 +ENDCHAR +STARTCHAR 250 +ENCODING 250 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 2 3 5 6 +BITMAP +C0 +C0 +C0 +ENDCHAR +STARTCHAR 251 +ENCODING 251 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 12 21 0 0 +BITMAP +01F0 +01F0 +0180 +0180 +0180 +0180 +0180 +0180 +0180 +0180 +F980 +F980 +1980 +1980 +1980 +1980 +1980 +1980 +1D80 +0F80 +0780 +ENDCHAR +STARTCHAR 252 +ENCODING 252 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 9 12 0 8 +BITMAP +6F00 +FF80 +9980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +1980 +ENDCHAR +STARTCHAR 253 +ENCODING 253 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 7 11 0 9 +BITMAP +7C +FE +C6 +C6 +0C +18 +30 +66 +C6 +FE +FE +ENDCHAR +STARTCHAR 254 +ENCODING 254 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 8 14 2 1 +BITMAP +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +ENDCHAR +STARTCHAR 255 +ENCODING 255 +SWIDTH 384 0 +DWIDTH 12 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +ENDFONT diff --git a/etc/vga8x14-cp850.bdf b/etc/vga8x14-cp850.bdf new file mode 100644 index 0000000..f4e8447 --- /dev/null +++ b/etc/vga8x14-cp850.bdf @@ -0,0 +1,5599 @@ +STARTFONT 2.1 +FONT -xos4-Terminus-Bold-R-Normal--14-140-72-72-C-80-IBM-CP850 +SIZE 14 72 72 +FONTBOUNDINGBOX 8 14 0 -2 + +STARTPROPERTIES 22 +FOUNDRY "xos4" +FAMILY_NAME "Terminus" +WEIGHT_NAME "Bold" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "" +PIXEL_SIZE 14 +POINT_SIZE 140 +RESOLUTION_X 72 +RESOLUTION_Y 72 +SPACING "C" +AVERAGE_WIDTH 80 +CHARSET_REGISTRY "IBM" +CHARSET_ENCODING "CP850" +MIN_SPACE 0 +COPYRIGHT "Copyright (C) 2005 Dimitar Toshkov Zhekov" +WEIGHT 10 +X_HEIGHT 12 +QUAD_WIDTH 8 +DEFAULT_CHAR 32 +FONT_DESCENT 2 +FONT_ASCENT 12 +ENDPROPERTIES + +CHARS 253 + +STARTCHAR uni0000 +ENCODING 0 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +EE +C6 +00 +C6 +C6 +C6 +C6 +00 +C6 +EE +00 +00 +ENDCHAR + +STARTCHAR smileface +ENCODING 1 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +82 +AA +82 +82 +BA +92 +82 +82 +7C +00 +00 +ENDCHAR + +STARTCHAR invsmileface +ENCODING 2 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +FE +D6 +FE +FE +C6 +EE +FE +FE +7C +00 +00 +ENDCHAR + +STARTCHAR heart +ENCODING 3 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +6C +FE +FE +FE +FE +7C +38 +10 +00 +00 +00 +ENDCHAR + +STARTCHAR diamond +ENCODING 4 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +18 +3C +7E +FF +7E +3C +18 +00 +00 +00 +ENDCHAR + +STARTCHAR club +ENCODING 5 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +18 +3C +3C +18 +5A +FF +FF +5A +18 +3C +00 +00 +ENDCHAR + +STARTCHAR spade +ENCODING 6 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +18 +18 +3C +7E +FF +FF +7E +18 +18 +3C +00 +00 +ENDCHAR + +STARTCHAR periodcentered +ENCODING 7 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +38 +38 +38 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR invbullet +ENCODING 8 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +FF +FF +FF +FF +FF +E7 +C3 +C3 +E7 +FF +FF +FF +FF +FF +ENDCHAR + +STARTCHAR circle +ENCODING 9 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +3C +66 +42 +42 +66 +3C +00 +00 +00 +00 +ENDCHAR + +STARTCHAR invcircle +ENCODING 10 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +FF +FF +FF +FF +C3 +99 +BD +BD +99 +C3 +FF +FF +FF +FF +ENDCHAR + +STARTCHAR male +ENCODING 11 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +3E +0E +1A +32 +78 +CC +CC +CC +CC +78 +00 +00 +ENDCHAR + +STARTCHAR female +ENCODING 12 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +3C +66 +66 +66 +66 +3C +18 +7E +18 +18 +00 +00 +ENDCHAR + +STARTCHAR musicalnote +ENCODING 13 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7E +66 +7E +60 +60 +60 +60 +60 +E0 +C0 +00 +00 +ENDCHAR + +STARTCHAR sun +ENCODING 15 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +18 +18 +DB +3C +E7 +3C +DB +18 +18 +00 +00 +ENDCHAR + +STARTCHAR uni25B6 +ENCODING 16 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +40 +60 +70 +78 +7C +7E +7E +7C +78 +70 +60 +40 +00 +ENDCHAR + +STARTCHAR uni25C0 +ENCODING 17 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +02 +06 +0E +1E +3E +7E +7E +3E +1E +0E +06 +02 +00 +ENDCHAR + +STARTCHAR arrowupdn +ENCODING 18 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +18 +3C +7E +18 +18 +18 +18 +7E +3C +18 +00 +00 +ENDCHAR + +STARTCHAR exclamdbl +ENCODING 19 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +66 +66 +66 +66 +66 +66 +66 +00 +66 +66 +00 +00 +ENDCHAR + +STARTCHAR filledrect +ENCODING 22 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +FE +FE +FE +FE +00 +00 +ENDCHAR + +STARTCHAR arrowupdnbse +ENCODING 23 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +18 +3C +7E +18 +18 +18 +18 +7E +3C +18 +7E +00 +ENDCHAR + +STARTCHAR arrowup +ENCODING 24 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +18 +3C +7E +18 +18 +18 +18 +18 +18 +18 +00 +00 +ENDCHAR + +STARTCHAR arrowdown +ENCODING 25 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +18 +18 +18 +18 +18 +18 +18 +7E +3C +18 +00 +00 +ENDCHAR + +STARTCHAR arrowright +ENCODING 26 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +08 +0C +FE +FE +0C +08 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR arrowleft +ENCODING 27 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +20 +60 +FE +FE +60 +20 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR orthogonal +ENCODING 28 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +C0 +C0 +C0 +C0 +C0 +FE +00 +00 +00 +00 +ENDCHAR + +STARTCHAR arrowboth +ENCODING 29 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +24 +66 +FF +FF +66 +24 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR triagup +ENCODING 30 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +18 +18 +3C +3C +7E +7E +FF +FF +00 +00 +00 +ENDCHAR + +STARTCHAR triagdn +ENCODING 31 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +FF +FF +7E +7E +3C +3C +18 +18 +00 +00 +00 +ENDCHAR + +STARTCHAR space +ENCODING 32 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR exclam +ENCODING 33 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +18 +18 +18 +18 +18 +18 +18 +00 +18 +18 +00 +00 +ENDCHAR + +STARTCHAR quotedbl +ENCODING 34 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +66 +66 +66 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR numbersign +ENCODING 35 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +6C +6C +6C +FE +6C +6C +FE +6C +6C +6C +00 +00 +ENDCHAR + +STARTCHAR dollar +ENCODING 36 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +10 +10 +7C +D6 +D0 +D0 +7C +16 +16 +D6 +7C +10 +10 +ENDCHAR + +STARTCHAR percent +ENCODING 37 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +66 +D6 +6C +0C +18 +18 +30 +36 +6B +66 +00 +00 +ENDCHAR + +STARTCHAR ampersand +ENCODING 38 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +38 +6C +6C +38 +76 +DC +CC +CC +DC +76 +00 +00 +ENDCHAR + +STARTCHAR quotesingle +ENCODING 39 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +18 +18 +30 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR parenleft +ENCODING 40 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +0C +18 +30 +30 +30 +30 +30 +30 +18 +0C +00 +00 +ENDCHAR + +STARTCHAR parenright +ENCODING 41 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +30 +18 +0C +0C +0C +0C +0C +0C +18 +30 +00 +00 +ENDCHAR + +STARTCHAR asterisk +ENCODING 42 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +6C +38 +FE +38 +6C +00 +00 +00 +00 +ENDCHAR + +STARTCHAR plus +ENCODING 43 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +18 +18 +7E +18 +18 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR comma +ENCODING 44 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +18 +18 +30 +00 +ENDCHAR + +STARTCHAR hyphen +ENCODING 45 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +00 +FE +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR period +ENCODING 46 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +18 +18 +00 +00 +ENDCHAR + +STARTCHAR slash +ENCODING 47 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +06 +06 +0C +0C +18 +18 +30 +30 +60 +60 +00 +00 +ENDCHAR + +STARTCHAR zero +ENCODING 48 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +C6 +CE +DE +F6 +E6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR one +ENCODING 49 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +18 +38 +78 +18 +18 +18 +18 +18 +18 +7E +00 +00 +ENDCHAR + +STARTCHAR two +ENCODING 50 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +C6 +06 +0C +18 +30 +60 +C0 +FE +00 +00 +ENDCHAR + +STARTCHAR three +ENCODING 51 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +C6 +06 +3C +06 +06 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR four +ENCODING 52 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +06 +0E +1E +36 +66 +C6 +FE +06 +06 +06 +00 +00 +ENDCHAR + +STARTCHAR five +ENCODING 53 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +FE +C0 +C0 +C0 +FC +06 +06 +06 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR six +ENCODING 54 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +3C +60 +C0 +C0 +FC +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR seven +ENCODING 55 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +FE +06 +06 +0C +0C +18 +18 +30 +30 +30 +00 +00 +ENDCHAR + +STARTCHAR eight +ENCODING 56 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +C6 +C6 +7C +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR nine +ENCODING 57 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +C6 +C6 +C6 +7E +06 +06 +0C +78 +00 +00 +ENDCHAR + +STARTCHAR colon +ENCODING 58 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +18 +18 +00 +00 +00 +18 +18 +00 +00 +ENDCHAR + +STARTCHAR semicolon +ENCODING 59 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +18 +18 +00 +00 +00 +18 +18 +30 +00 +ENDCHAR + +STARTCHAR less +ENCODING 60 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +06 +0C +18 +30 +60 +30 +18 +0C +06 +00 +00 +ENDCHAR + +STARTCHAR equal +ENCODING 61 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +FE +00 +00 +FE +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR greater +ENCODING 62 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +60 +30 +18 +0C +06 +0C +18 +30 +60 +00 +00 +ENDCHAR + +STARTCHAR question +ENCODING 63 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +C6 +C6 +0C +18 +18 +00 +18 +18 +00 +00 +ENDCHAR + +STARTCHAR at +ENCODING 64 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +CE +D6 +D6 +D6 +D6 +CE +C0 +7E +00 +00 +ENDCHAR + +STARTCHAR A +ENCODING 65 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +C6 +C6 +C6 +FE +C6 +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR B +ENCODING 66 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +FC +C6 +C6 +C6 +FC +C6 +C6 +C6 +C6 +FC +00 +00 +ENDCHAR + +STARTCHAR C +ENCODING 67 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +C6 +C0 +C0 +C0 +C0 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR D +ENCODING 68 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +F8 +CC +C6 +C6 +C6 +C6 +C6 +C6 +CC +F8 +00 +00 +ENDCHAR + +STARTCHAR E +ENCODING 69 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +FE +C0 +C0 +C0 +F8 +C0 +C0 +C0 +C0 +FE +00 +00 +ENDCHAR + +STARTCHAR F +ENCODING 70 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +FE +C0 +C0 +C0 +F8 +C0 +C0 +C0 +C0 +C0 +00 +00 +ENDCHAR + +STARTCHAR G +ENCODING 71 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +C6 +C0 +C0 +DE +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR H +ENCODING 72 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C6 +C6 +C6 +C6 +FE +C6 +C6 +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR I +ENCODING 73 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +3C +18 +18 +18 +18 +18 +18 +18 +18 +3C +00 +00 +ENDCHAR + +STARTCHAR J +ENCODING 74 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +1E +0C +0C +0C +0C +0C +0C +CC +CC +78 +00 +00 +ENDCHAR + +STARTCHAR K +ENCODING 75 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C6 +C6 +CC +D8 +F0 +F0 +D8 +CC +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR L +ENCODING 76 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FE +00 +00 +ENDCHAR + +STARTCHAR M +ENCODING 77 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +82 +C6 +EE +FE +D6 +C6 +C6 +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR N +ENCODING 78 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C6 +C6 +C6 +E6 +F6 +DE +CE +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR O +ENCODING 79 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR P +ENCODING 80 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +FC +C6 +C6 +C6 +C6 +FC +C0 +C0 +C0 +C0 +00 +00 +ENDCHAR + +STARTCHAR Q +ENCODING 81 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +C6 +DE +7C +06 +00 +ENDCHAR + +STARTCHAR R +ENCODING 82 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +FC +C6 +C6 +C6 +C6 +FC +F0 +D8 +CC +C6 +00 +00 +ENDCHAR + +STARTCHAR S +ENCODING 83 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +C0 +C0 +7C +06 +06 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR T +ENCODING 84 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +FF +18 +18 +18 +18 +18 +18 +18 +18 +18 +00 +00 +ENDCHAR + +STARTCHAR U +ENCODING 85 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR V +ENCODING 86 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C6 +C6 +C6 +C6 +C6 +6C +6C +6C +38 +38 +00 +00 +ENDCHAR + +STARTCHAR W +ENCODING 87 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C6 +C6 +C6 +C6 +C6 +D6 +FE +EE +C6 +82 +00 +00 +ENDCHAR + +STARTCHAR X +ENCODING 88 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C6 +C6 +6C +6C +38 +38 +6C +6C +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR Y +ENCODING 89 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C3 +C3 +66 +66 +3C +18 +18 +18 +18 +18 +00 +00 +ENDCHAR + +STARTCHAR Z +ENCODING 90 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +FE +06 +06 +0C +18 +30 +60 +C0 +C0 +FE +00 +00 +ENDCHAR + +STARTCHAR bracketleft +ENCODING 91 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +3C +30 +30 +30 +30 +30 +30 +30 +30 +3C +00 +00 +ENDCHAR + +STARTCHAR backslash +ENCODING 92 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +60 +60 +30 +30 +18 +18 +0C +0C +06 +06 +00 +00 +ENDCHAR + +STARTCHAR bracketright +ENCODING 93 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +3C +0C +0C +0C +0C +0C +0C +0C +0C +3C +00 +00 +ENDCHAR + +STARTCHAR asciicircum +ENCODING 94 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +18 +3C +66 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR underscore +ENCODING 95 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +FE +00 +ENDCHAR + +STARTCHAR grave +ENCODING 96 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +30 +30 +18 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR a +ENCODING 97 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +7C +06 +7E +C6 +C6 +C6 +7E +00 +00 +ENDCHAR + +STARTCHAR b +ENCODING 98 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C0 +C0 +C0 +FC +C6 +C6 +C6 +C6 +C6 +FC +00 +00 +ENDCHAR + +STARTCHAR c +ENCODING 99 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +7C +C6 +C0 +C0 +C0 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR d +ENCODING 100 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +06 +06 +06 +7E +C6 +C6 +C6 +C6 +C6 +7E +00 +00 +ENDCHAR + +STARTCHAR e +ENCODING 101 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +7C +C6 +C6 +FE +C0 +C0 +7C +00 +00 +ENDCHAR + +STARTCHAR f +ENCODING 102 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +1E +30 +30 +FC +30 +30 +30 +30 +30 +30 +00 +00 +ENDCHAR + +STARTCHAR g +ENCODING 103 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +7E +C6 +C6 +C6 +C6 +C6 +7E +06 +7C +ENDCHAR + +STARTCHAR h +ENCODING 104 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C0 +C0 +C0 +FC +C6 +C6 +C6 +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR i +ENCODING 105 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +18 +18 +00 +38 +18 +18 +18 +18 +18 +3C +00 +00 +ENDCHAR + +STARTCHAR j +ENCODING 106 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +06 +06 +00 +0E +06 +06 +06 +06 +06 +66 +66 +3C +ENDCHAR + +STARTCHAR k +ENCODING 107 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C0 +C0 +C0 +C6 +CC +D8 +F0 +D8 +CC +C6 +00 +00 +ENDCHAR + +STARTCHAR l +ENCODING 108 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +38 +18 +18 +18 +18 +18 +18 +18 +18 +3C +00 +00 +ENDCHAR + +STARTCHAR m +ENCODING 109 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +FC +D6 +D6 +D6 +D6 +D6 +D6 +00 +00 +ENDCHAR + +STARTCHAR n +ENCODING 110 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +FC +C6 +C6 +C6 +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR o +ENCODING 111 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +7C +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR p +ENCODING 112 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +FC +C6 +C6 +C6 +C6 +C6 +FC +C0 +C0 +ENDCHAR + +STARTCHAR q +ENCODING 113 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +7E +C6 +C6 +C6 +C6 +C6 +7E +06 +06 +ENDCHAR + +STARTCHAR r +ENCODING 114 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +DE +F0 +E0 +C0 +C0 +C0 +C0 +00 +00 +ENDCHAR + +STARTCHAR s +ENCODING 115 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +7E +C0 +C0 +7C +06 +06 +FC +00 +00 +ENDCHAR + +STARTCHAR t +ENCODING 116 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +30 +30 +30 +FC +30 +30 +30 +30 +30 +1E +00 +00 +ENDCHAR + +STARTCHAR u +ENCODING 117 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +C6 +C6 +C6 +C6 +C6 +C6 +7E +00 +00 +ENDCHAR + +STARTCHAR v +ENCODING 118 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +C6 +C6 +C6 +6C +6C +38 +38 +00 +00 +ENDCHAR + +STARTCHAR w +ENCODING 119 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +C6 +C6 +D6 +D6 +D6 +D6 +7C +00 +00 +ENDCHAR + +STARTCHAR x +ENCODING 120 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +C6 +C6 +6C +38 +6C +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR y +ENCODING 121 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +C6 +C6 +C6 +C6 +C6 +C6 +7E +06 +7C +ENDCHAR + +STARTCHAR z +ENCODING 122 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +FE +0C +18 +30 +60 +C0 +FE +00 +00 +ENDCHAR + +STARTCHAR braceleft +ENCODING 123 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +1C +30 +30 +30 +60 +30 +30 +30 +30 +1C +00 +00 +ENDCHAR + +STARTCHAR bar +ENCODING 124 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +00 +00 +ENDCHAR + +STARTCHAR braceright +ENCODING 125 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +70 +18 +18 +18 +0C +18 +18 +18 +18 +70 +00 +00 +ENDCHAR + +STARTCHAR asciitilde +ENCODING 126 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +73 +DB +CE +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR house +ENCODING 127 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +10 +38 +6C +C6 +C6 +C6 +FE +00 +00 +00 +ENDCHAR + +STARTCHAR Ccedilla +ENCODING 128 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7C +C6 +C6 +C0 +C0 +C0 +C0 +C6 +C6 +7C +30 +60 +ENDCHAR + +STARTCHAR udieresis +ENCODING 129 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +6C +6C +00 +C6 +C6 +C6 +C6 +C6 +C6 +7E +00 +00 +ENDCHAR + +STARTCHAR eacute +ENCODING 130 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +0C +18 +00 +7C +C6 +C6 +FE +C0 +C0 +7C +00 +00 +ENDCHAR + +STARTCHAR acircumflex +ENCODING 131 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +38 +6C +00 +7C +06 +7E +C6 +C6 +C6 +7E +00 +00 +ENDCHAR + +STARTCHAR adieresis +ENCODING 132 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +6C +6C +00 +7C +06 +7E +C6 +C6 +C6 +7E +00 +00 +ENDCHAR + +STARTCHAR agrave +ENCODING 133 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +60 +30 +00 +7C +06 +7E +C6 +C6 +C6 +7E +00 +00 +ENDCHAR + +STARTCHAR aring +ENCODING 134 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +38 +6C +38 +7C +06 +7E +C6 +C6 +C6 +7E +00 +00 +ENDCHAR + +STARTCHAR ccedilla +ENCODING 135 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +7C +C6 +C0 +C0 +C0 +C6 +7C +30 +60 +ENDCHAR + +STARTCHAR ecircumflex +ENCODING 136 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +38 +6C +00 +7C +C6 +C6 +FE +C0 +C0 +7C +00 +00 +ENDCHAR + +STARTCHAR edieresis +ENCODING 137 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +6C +6C +00 +7C +C6 +C6 +FE +C0 +C0 +7C +00 +00 +ENDCHAR + +STARTCHAR egrave +ENCODING 138 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +60 +30 +00 +7C +C6 +C6 +FE +C0 +C0 +7C +00 +00 +ENDCHAR + +STARTCHAR idieresis +ENCODING 139 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +6C +6C +00 +38 +18 +18 +18 +18 +18 +3C +00 +00 +ENDCHAR + +STARTCHAR icircumflex +ENCODING 140 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +38 +6C +00 +38 +18 +18 +18 +18 +18 +3C +00 +00 +ENDCHAR + +STARTCHAR igrave +ENCODING 141 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +30 +18 +00 +38 +18 +18 +18 +18 +18 +3C +00 +00 +ENDCHAR + +STARTCHAR Adieresis +ENCODING 142 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +6C +6C +00 +7C +C6 +C6 +C6 +FE +C6 +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR Aring +ENCODING 143 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +38 +6C +38 +7C +C6 +C6 +C6 +FE +C6 +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR Eacute +ENCODING 144 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +0C +18 +00 +FE +C0 +C0 +C0 +F8 +C0 +C0 +C0 +FE +00 +00 +ENDCHAR + +STARTCHAR ae +ENCODING 145 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +6C +16 +16 +7E +D0 +D0 +6C +00 +00 +ENDCHAR + +STARTCHAR AE +ENCODING 146 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7E +D8 +D8 +D8 +FE +D8 +D8 +D8 +D8 +DE +00 +00 +ENDCHAR + +STARTCHAR ocircumflex +ENCODING 147 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +38 +6C +00 +7C +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR odieresis +ENCODING 148 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +6C +6C +00 +7C +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR ograve +ENCODING 149 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +60 +30 +00 +7C +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR ucircumflex +ENCODING 150 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +38 +6C +00 +C6 +C6 +C6 +C6 +C6 +C6 +7E +00 +00 +ENDCHAR + +STARTCHAR ugrave +ENCODING 151 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +60 +30 +00 +C6 +C6 +C6 +C6 +C6 +C6 +7E +00 +00 +ENDCHAR + +STARTCHAR ydieresis +ENCODING 152 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +6C +6C +00 +C6 +C6 +C6 +C6 +C6 +C6 +7E +06 +7C +ENDCHAR + +STARTCHAR Odieresis +ENCODING 153 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +6C +6C +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR Udieresis +ENCODING 154 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +6C +6C +00 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR oslash +ENCODING 155 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +3D +67 +6E +7E +76 +E6 +BC +00 +00 +ENDCHAR + +STARTCHAR sterling +ENCODING 156 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +38 +6C +60 +60 +F8 +60 +60 +60 +66 +FE +00 +00 +ENDCHAR + +STARTCHAR Oslash +ENCODING 157 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +3D +67 +66 +6E +7E +76 +66 +66 +E6 +BC +00 +00 +ENDCHAR + +STARTCHAR multiply +ENCODING 158 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +C6 +6C +38 +6C +C6 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR florin +ENCODING 159 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +0E +1B +18 +18 +7E +18 +18 +18 +18 +18 +D8 +70 +ENDCHAR + +STARTCHAR aacute +ENCODING 160 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +0C +18 +00 +7C +06 +7E +C6 +C6 +C6 +7E +00 +00 +ENDCHAR + +STARTCHAR iacute +ENCODING 161 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +0C +18 +00 +38 +18 +18 +18 +18 +18 +3C +00 +00 +ENDCHAR + +STARTCHAR oacute +ENCODING 162 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +0C +18 +00 +7C +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR uacute +ENCODING 163 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +0C +18 +00 +C6 +C6 +C6 +C6 +C6 +C6 +7E +00 +00 +ENDCHAR + +STARTCHAR ntilde +ENCODING 164 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +76 +DC +00 +FC +C6 +C6 +C6 +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR Ntilde +ENCODING 165 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +76 +DC +00 +C6 +C6 +E6 +F6 +DE +CE +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR ordfeminine +ENCODING 166 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +3C +06 +3E +66 +3E +00 +7E +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR ordmasculine +ENCODING 167 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +3C +66 +66 +66 +3C +00 +7E +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR questiondown +ENCODING 168 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +30 +30 +00 +30 +30 +60 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR registered +ENCODING 169 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +7E +81 +B9 +A5 +B9 +A9 +A5 +81 +7E +00 +00 +ENDCHAR + +STARTCHAR logicalnot +ENCODING 170 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +FE +06 +06 +06 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR onehalf +ENCODING 171 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +60 +E0 +62 +66 +6C +18 +30 +60 +DC +B6 +0C +18 +3E +00 +ENDCHAR + +STARTCHAR onequarter +ENCODING 172 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +60 +E0 +62 +66 +6C +18 +30 +66 +CE +9A +3E +06 +06 +00 +ENDCHAR + +STARTCHAR exclamdown +ENCODING 173 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +18 +18 +00 +18 +18 +18 +18 +18 +18 +18 +00 +00 +ENDCHAR + +STARTCHAR guillemotleft +ENCODING 174 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +1B +36 +6C +D8 +6C +36 +1B +00 +00 +ENDCHAR + +STARTCHAR guillemotright +ENCODING 175 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +D8 +6C +36 +1B +36 +6C +D8 +00 +00 +ENDCHAR + +STARTCHAR ltshade +ENCODING 176 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +88 +22 +88 +22 +88 +22 +88 +22 +88 +22 +88 +22 +88 +22 +ENDCHAR + +STARTCHAR shade +ENCODING 177 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +AA +55 +AA +55 +AA +55 +AA +55 +AA +55 +AA +55 +AA +55 +ENDCHAR + +STARTCHAR dkshade +ENCODING 178 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +EE +BB +EE +BB +EE +BB +EE +BB +EE +BB +EE +BB +EE +BB +ENDCHAR + +STARTCHAR SF110000 +ENCODING 179 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR + +STARTCHAR SF090000 +ENCODING 180 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +18 +18 +18 +18 +18 +18 +F8 +F8 +18 +18 +18 +18 +18 +18 +ENDCHAR + +STARTCHAR Aacute +ENCODING 181 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +0C +18 +00 +7C +C6 +C6 +C6 +FE +C6 +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR Acircumflex +ENCODING 182 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +38 +6C +00 +7C +C6 +C6 +C6 +FE +C6 +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR Agrave +ENCODING 183 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +60 +30 +00 +7C +C6 +C6 +C6 +FE +C6 +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR copyright +ENCODING 184 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +7E +81 +99 +A5 +A1 +A5 +99 +81 +7E +00 +00 +ENDCHAR + +STARTCHAR SF230000 +ENCODING 185 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +6C +6C +6C +6C +EC +EC +0C +EC +EC +6C +6C +6C +6C +6C +ENDCHAR + +STARTCHAR SF240000 +ENCODING 186 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +6C +6C +6C +6C +6C +6C +6C +6C +6C +6C +6C +6C +6C +6C +ENDCHAR + +STARTCHAR SF250000 +ENCODING 187 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +FC +FC +0C +EC +EC +6C +6C +6C +6C +6C +ENDCHAR + +STARTCHAR SF260000 +ENCODING 188 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +6C +6C +6C +6C +EC +EC +0C +FC +FC +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR cent +ENCODING 189 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +10 +10 +7C +D6 +D0 +D0 +D0 +D6 +7C +10 +10 +ENDCHAR + +STARTCHAR yen +ENCODING 190 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C3 +C3 +66 +3C +18 +7E +18 +7E +18 +18 +00 +00 +ENDCHAR + +STARTCHAR SF030000 +ENCODING 191 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +F8 +F8 +18 +18 +18 +18 +18 +18 +ENDCHAR + +STARTCHAR SF020000 +ENCODING 192 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +18 +18 +18 +18 +18 +18 +1F +1F +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR SF070000 +ENCODING 193 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +18 +18 +18 +18 +18 +18 +FF +FF +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR SF060000 +ENCODING 194 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +FF +FF +18 +18 +18 +18 +18 +18 +ENDCHAR + +STARTCHAR SF080000 +ENCODING 195 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +18 +18 +18 +18 +18 +18 +1F +1F +18 +18 +18 +18 +18 +18 +ENDCHAR + +STARTCHAR SF100000 +ENCODING 196 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +FF +FF +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR SF050000 +ENCODING 197 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +18 +18 +18 +18 +18 +18 +FF +FF +18 +18 +18 +18 +18 +18 +ENDCHAR + +STARTCHAR atilde +ENCODING 198 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +76 +DC +00 +7C +06 +7E +C6 +C6 +C6 +7E +00 +00 +ENDCHAR + +STARTCHAR Atilde +ENCODING 199 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +76 +DC +00 +7C +C6 +C6 +C6 +FE +C6 +C6 +C6 +C6 +00 +00 +ENDCHAR + +STARTCHAR SF380000 +ENCODING 200 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +6C +6C +6C +6C +6F +6F +60 +7F +7F +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR SF390000 +ENCODING 201 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +7F +7F +60 +6F +6F +6C +6C +6C +6C +6C +ENDCHAR + +STARTCHAR SF400000 +ENCODING 202 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +6C +6C +6C +6C +EF +EF +00 +FF +FF +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR SF410000 +ENCODING 203 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +FF +FF +00 +EF +EF +6C +6C +6C +6C +6C +ENDCHAR + +STARTCHAR SF420000 +ENCODING 204 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +6C +6C +6C +6C +6F +6F +60 +6F +6F +6C +6C +6C +6C +6C +ENDCHAR + +STARTCHAR SF430000 +ENCODING 205 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +FF +FF +00 +FF +FF +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR SF440000 +ENCODING 206 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +6C +6C +6C +6C +EF +EF +00 +EF +EF +6C +6C +6C +6C +6C +ENDCHAR + +STARTCHAR currency +ENCODING 207 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +66 +3C +66 +66 +66 +3C +66 +00 +00 +00 +ENDCHAR + +STARTCHAR eth +ENCODING 208 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +68 +30 +58 +7C +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR Eth +ENCODING 209 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +78 +6C +66 +66 +F6 +66 +66 +66 +6C +78 +00 +00 +ENDCHAR + +STARTCHAR Ecircumflex +ENCODING 210 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +38 +6C +00 +FE +C0 +C0 +C0 +F8 +C0 +C0 +C0 +FE +00 +00 +ENDCHAR + +STARTCHAR Edieresis +ENCODING 211 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +6C +6C +00 +FE +C0 +C0 +C0 +F8 +C0 +C0 +C0 +FE +00 +00 +ENDCHAR + +STARTCHAR Egrave +ENCODING 212 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +60 +30 +00 +FE +C0 +C0 +C0 +F8 +C0 +C0 +C0 +FE +00 +00 +ENDCHAR + +STARTCHAR dotlessi +ENCODING 213 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +38 +18 +18 +18 +18 +18 +3C +00 +00 +ENDCHAR + +STARTCHAR Iacute +ENCODING 214 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +0C +18 +00 +3C +18 +18 +18 +18 +18 +18 +18 +3C +00 +00 +ENDCHAR + +STARTCHAR Icircumflex +ENCODING 215 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +38 +6C +00 +3C +18 +18 +18 +18 +18 +18 +18 +3C +00 +00 +ENDCHAR + +STARTCHAR Idieresis +ENCODING 216 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +66 +66 +00 +3C +18 +18 +18 +18 +18 +18 +18 +3C +00 +00 +ENDCHAR + +STARTCHAR SF040000 +ENCODING 217 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +18 +18 +18 +18 +18 +18 +F8 +F8 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR SF010000 +ENCODING 218 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +1F +1F +18 +18 +18 +18 +18 +18 +ENDCHAR + +STARTCHAR block +ENCODING 219 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +ENDCHAR + +STARTCHAR dnblock +ENCODING 220 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +00 +FF +FF +FF +FF +FF +FF +FF +ENDCHAR + +STARTCHAR brokenbar +ENCODING 221 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +18 +18 +18 +18 +00 +00 +18 +18 +18 +18 +00 +00 +ENDCHAR + +STARTCHAR Igrave +ENCODING 222 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +30 +18 +00 +3C +18 +18 +18 +18 +18 +18 +18 +3C +00 +00 +ENDCHAR + +STARTCHAR upblock +ENCODING 223 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +FF +FF +FF +FF +FF +FF +FF +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR Oacute +ENCODING 224 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +0C +18 +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR germandbls +ENCODING 225 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +78 +CC +CC +C8 +FC +C6 +C6 +C6 +E6 +BC +00 +00 +ENDCHAR + +STARTCHAR Ocircumflex +ENCODING 226 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +38 +6C +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR Ograve +ENCODING 227 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +60 +30 +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR otilde +ENCODING 228 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +76 +DC +00 +7C +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR Otilde +ENCODING 229 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +76 +DC +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR mu +ENCODING 230 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +C6 +C6 +C6 +C6 +C6 +CE +F6 +C0 +C0 +ENDCHAR + +STARTCHAR thorn +ENCODING 231 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C0 +C0 +C0 +FC +C6 +C6 +C6 +C6 +FC +C0 +C0 +C0 +ENDCHAR + +STARTCHAR Thorn +ENCODING 232 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +C0 +C0 +FC +C6 +C6 +C6 +C6 +FC +C0 +C0 +00 +00 +ENDCHAR + +STARTCHAR Uacute +ENCODING 233 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +0C +18 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR Ucircumflex +ENCODING 234 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +38 +6C +00 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR Ugrave +ENCODING 235 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +60 +30 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +00 +00 +ENDCHAR + +STARTCHAR yacute +ENCODING 236 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +0C +18 +00 +C6 +C6 +C6 +C6 +C6 +C6 +7E +06 +7C +ENDCHAR + +STARTCHAR Yacute +ENCODING 237 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +0C +18 +C3 +C3 +66 +66 +3C +18 +18 +18 +18 +18 +00 +00 +ENDCHAR + +STARTCHAR macron +ENCODING 238 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +FE +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR acute +ENCODING 239 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +0C +18 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR hyphen +ENCODING 240 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +00 +7C +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR plusminus +ENCODING 241 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +18 +18 +7E +18 +18 +00 +00 +7E +00 +00 +ENDCHAR + +STARTCHAR underscoredbl +ENCODING 242 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +FE +00 +FE +ENDCHAR + +STARTCHAR threequarters +ENCODING 243 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +E0 +30 +62 +36 +EC +18 +30 +66 +CE +9A +3E +06 +06 +00 +ENDCHAR + +STARTCHAR paragraph +ENCODING 244 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +7E +D6 +D6 +D6 +D6 +76 +16 +16 +16 +16 +00 +00 +ENDCHAR + +STARTCHAR section +ENCODING 245 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +3C +66 +60 +38 +6C +66 +66 +36 +1C +06 +66 +3C +00 +ENDCHAR + +STARTCHAR divide +ENCODING 246 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +18 +18 +00 +7E +00 +18 +18 +00 +00 +00 +ENDCHAR + +STARTCHAR cedilla +ENCODING 247 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +30 +30 +60 +ENDCHAR + +STARTCHAR degree +ENCODING 248 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +38 +6C +6C +38 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR dieresis +ENCODING 249 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +6C +6C +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR periodcentered +ENCODING 250 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +18 +18 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR onesuperior +ENCODING 251 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +18 +38 +18 +18 +18 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR threesuperior +ENCODING 252 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +78 +0C +38 +0C +78 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR twosuperior +ENCODING 253 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +38 +6C +18 +30 +7C +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +STARTCHAR filledbox +ENCODING 254 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +7C +7C +7C +7C +7C +7C +00 +00 +00 +00 +ENDCHAR + +STARTCHAR space +ENCODING 255 +SWIDTH 571 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +00 +ENDCHAR + +ENDFONT diff --git a/etc/vga8x19.bdf b/etc/vga8x19.bdf new file mode 100644 index 0000000..720e541 --- /dev/null +++ b/etc/vga8x19.bdf @@ -0,0 +1,4666 @@ +STARTFONT 2.1 +COMMENT This font was taken from the dosemu project, resized by +COMMENT bdfresize (-h 19/16) and finally corrected and sharpened +COMMENT by hand. +COMMENT +COMMENT If you choose any 640x480 pix display you'll have a nearby +COMMENT fullscreen mode when using xdosemu and leaving away the +COMMENT X Windows' title bar and border lines. It'll be exactly +COMMENT 640x475 pix. +COMMENT +COMMENT Have a look to the other fonts of this series as well. +COMMENT Written in December 2002 by Martin Reuber, OFD Kiel, +COMMENT German Fiscal Administration. +COMMENT +COMMENT Update 003 in January 2004: +COMMENT Small corrections in font name, properties and comments. +FONT -dosemu-vga-Medium-R-Normal--19-190-75-75-c-80-IBM-CP437 +SIZE 19 75 75 +FONTBOUNDINGBOX 8 19 0 -5 +STARTPROPERTIES 22 +POINT_SIZE 190 +WEIGHT 10 +RESOLUTION 100 +RESOLUTION_X 75 +RESOLUTION_Y 75 +QUAD_WIDTH 8 +DEFAULT_CHAR 0 +FONT_ASCENT 14 +FONT_DESCENT 5 +_ORIGINAL_FONT_NAME "vga" +_XMBDFED_INFO "Edited with xmbdfed 4.5." +FOUNDRY "dosemu" +FAMILY_NAME "vga" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "640x480pix_screen_max_size" +PIXEL_SIZE 19 +SPACING "c" +AVERAGE_WIDTH 80 +CHARSET_REGISTRY "IBM" +CHARSET_ENCODING "CP437" +ENDPROPERTIES +CHARS 256 +STARTCHAR 000 +ENCODING 0 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR 001 +ENCODING 1 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 12 0 0 +BITMAP +7E +81 +A5 +A5 +81 +81 +A5 +A5 +99 +81 +81 +7E +ENDCHAR +STARTCHAR 002 +ENCODING 2 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 12 0 0 +BITMAP +7E +FF +DB +FF +FF +FF +DB +E7 +FF +FF +FF +7E +ENDCHAR +STARTCHAR 003 +ENCODING 3 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 1 +BITMAP +6C +FE +FE +FE +FE +7C +38 +10 +ENDCHAR +STARTCHAR 004 +ENCODING 4 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 1 +BITMAP +10 +38 +7C +FE +FE +7C +38 +10 +ENDCHAR +STARTCHAR 005 +ENCODING 5 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 11 0 0 +BITMAP +18 +3C +3C +FF +E7 +E7 +E7 +FF +18 +18 +3C +ENDCHAR +STARTCHAR 006 +ENCODING 6 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 11 0 0 +BITMAP +18 +3C +7E +FF +FF +FF +7E +7E +18 +18 +3C +ENDCHAR +STARTCHAR 007 +ENCODING 7 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 4 5 2 2 +BITMAP +60 +F0 +F0 +F0 +60 +ENDCHAR +STARTCHAR 008 +ENCODING 8 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +FF +FF +FF +FF +FF +FF +FF +FF +E7 +C3 +C3 +E7 +FF +FF +FF +FF +FF +FF +FF +ENDCHAR +STARTCHAR 009 +ENCODING 9 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 7 1 1 +BITMAP +78 +CC +CC +84 +CC +CC +78 +ENDCHAR +STARTCHAR 010 +ENCODING 10 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +FF +FF +FF +FF +FF +FF +FF +DB +99 +BD +BD +99 +DB +FF +FF +FF +FF +FF +FF +ENDCHAR +STARTCHAR 011 +ENCODING 11 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +1E +06 +0E +0A +1A +78 +CC +CC +CC +CC +CC +78 +ENDCHAR +STARTCHAR 012 +ENCODING 12 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 12 1 0 +BITMAP +78 +CC +CC +CC +CC +CC +78 +30 +30 +FC +30 +30 +ENDCHAR +STARTCHAR 013 +ENCODING 13 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 12 0 0 +BITMAP +3F +33 +33 +3F +30 +30 +30 +30 +30 +70 +F0 +E0 +ENDCHAR +STARTCHAR 014 +ENCODING 14 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 13 0 -1 +BITMAP +7F +63 +63 +7F +63 +63 +63 +63 +63 +67 +E7 +E6 +C0 +ENDCHAR +STARTCHAR 015 +ENCODING 15 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 11 0 0 +BITMAP +18 +18 +DB +FF +3C +E7 +3C +FF +DB +18 +18 +ENDCHAR +STARTCHAR 016 +ENCODING 16 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +80 +C0 +E0 +F0 +F8 +FC +FE +FC +F8 +F0 +E0 +C0 +80 +ENDCHAR +STARTCHAR 017 +ENCODING 17 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +02 +06 +0E +1E +3E +7E +FE +7E +3E +1E +0E +06 +02 +ENDCHAR +STARTCHAR 018 +ENCODING 18 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 11 1 1 +BITMAP +30 +78 +FC +30 +30 +30 +30 +30 +FC +78 +30 +ENDCHAR +STARTCHAR 019 +ENCODING 19 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 12 1 0 +BITMAP +CC +CC +CC +CC +CC +CC +CC +CC +CC +00 +CC +CC +ENDCHAR +STARTCHAR 020 +ENCODING 20 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 12 0 0 +BITMAP +7F +DB +DB +DB +DB +7B +1B +1B +1B +1B +1B +1B +ENDCHAR +STARTCHAR 021 +ENCODING 21 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +7C +C6 +62 +30 +78 +4C +64 +3C +18 +8C +C6 +7C +ENDCHAR +STARTCHAR 022 +ENCODING 22 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 5 0 0 +BITMAP +FE +FE +FE +FE +FE +ENDCHAR +STARTCHAR 023 +ENCODING 23 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 12 1 0 +BITMAP +30 +78 +FC +30 +30 +30 +30 +30 +FC +78 +30 +FC +ENDCHAR +STARTCHAR 024 +ENCODING 24 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 12 1 0 +BITMAP +30 +78 +FC +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 025 +ENCODING 25 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 12 1 0 +BITMAP +30 +30 +30 +30 +30 +30 +30 +30 +30 +FC +78 +30 +ENDCHAR +STARTCHAR 026 +ENCODING 26 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 5 0 2 +BITMAP +18 +0C +FE +0C +18 +ENDCHAR +STARTCHAR 027 +ENCODING 27 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 5 0 2 +BITMAP +30 +60 +FE +60 +30 +ENDCHAR +STARTCHAR 028 +ENCODING 28 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 5 0 4 +BITMAP +C0 +C0 +C0 +C0 +FE +ENDCHAR +STARTCHAR 029 +ENCODING 29 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 5 0 2 +BITMAP +28 +6C +FE +6C +28 +ENDCHAR +STARTCHAR 030 +ENCODING 30 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 1 +BITMAP +10 +38 +38 +7C +7C +FE +FE +FE +ENDCHAR +STARTCHAR 031 +ENCODING 31 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 1 +BITMAP +FE +FE +FE +7C +7C +38 +38 +10 +ENDCHAR +STARTCHAR 032 +ENCODING 32 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR 033 +ENCODING 33 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 4 12 2 0 +BITMAP +60 +F0 +F0 +F0 +F0 +60 +60 +60 +60 +00 +60 +60 +ENDCHAR +STARTCHAR 034 +ENCODING 34 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 5 1 9 +BITMAP +CC +CC +CC +CC +48 +ENDCHAR +STARTCHAR 035 +ENCODING 35 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 0 +BITMAP +6C +6C +6C +FE +6C +6C +6C +FE +6C +6C +6C +ENDCHAR +STARTCHAR 036 +ENCODING 36 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 16 0 -2 +BITMAP +10 +10 +7C +D6 +D6 +D2 +70 +38 +1C +16 +96 +D6 +D6 +7C +10 +10 +ENDCHAR +STARTCHAR 037 +ENCODING 37 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +C2 +C6 +0C +18 +30 +60 +C6 +86 +ENDCHAR +STARTCHAR 038 +ENCODING 38 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +38 +6C +6C +6C +38 +76 +DC +CC +CC +CC +CC +76 +ENDCHAR +STARTCHAR 039 +ENCODING 39 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 3 5 1 9 +BITMAP +60 +60 +60 +60 +C0 +ENDCHAR +STARTCHAR 040 +ENCODING 40 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 4 12 2 0 +BITMAP +30 +60 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +60 +30 +ENDCHAR +STARTCHAR 041 +ENCODING 41 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 4 12 2 0 +BITMAP +C0 +60 +30 +30 +30 +30 +30 +30 +30 +30 +60 +C0 +ENDCHAR +STARTCHAR 042 +ENCODING 42 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 7 0 2 +BITMAP +66 +24 +3C +FF +3C +24 +66 +ENDCHAR +STARTCHAR 043 +ENCODING 43 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 5 1 3 +BITMAP +30 +30 +FC +30 +30 +ENDCHAR +STARTCHAR 044 +ENCODING 44 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 3 5 2 -1 +BITMAP +60 +60 +60 +60 +C0 +ENDCHAR +STARTCHAR 045 +ENCODING 45 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 1 0 5 +BITMAP +FE +ENDCHAR +STARTCHAR 046 +ENCODING 46 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 2 3 3 0 +BITMAP +C0 +C0 +C0 +ENDCHAR +STARTCHAR 047 +ENCODING 47 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 1 +BITMAP +02 +06 +0C +18 +30 +60 +C0 +80 +ENDCHAR +STARTCHAR 048 +ENCODING 48 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +38 +6C +C6 +C6 +C6 +D6 +D6 +C6 +C6 +C6 +6C +38 +ENDCHAR +STARTCHAR 049 +ENCODING 49 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 12 1 0 +BITMAP +30 +70 +F0 +30 +30 +30 +30 +30 +30 +30 +30 +FC +ENDCHAR +STARTCHAR 050 +ENCODING 50 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +7C +C6 +06 +06 +0C +18 +30 +60 +C0 +C0 +C6 +FE +ENDCHAR +STARTCHAR 051 +ENCODING 51 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +7C +C6 +06 +06 +06 +3C +06 +06 +06 +06 +C6 +7C +ENDCHAR +STARTCHAR 052 +ENCODING 52 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +0C +1C +3C +6C +CC +FE +0C +0C +0C +0C +0C +1E +ENDCHAR +STARTCHAR 053 +ENCODING 53 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +FE +C0 +C0 +C0 +C0 +FC +06 +06 +06 +06 +C6 +7C +ENDCHAR +STARTCHAR 054 +ENCODING 54 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +38 +60 +C0 +C0 +C0 +FC +C6 +C6 +C6 +C6 +C6 +7C +ENDCHAR +STARTCHAR 055 +ENCODING 55 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +FE +C6 +06 +06 +06 +0C +18 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 056 +ENCODING 56 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +7C +C6 +C6 +C6 +C6 +7C +C6 +C6 +C6 +C6 +C6 +7C +ENDCHAR +STARTCHAR 057 +ENCODING 57 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +7C +C6 +C6 +C6 +C6 +7E +06 +06 +06 +06 +0C +78 +ENDCHAR +STARTCHAR 058 +ENCODING 58 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 2 7 3 1 +BITMAP +C0 +C0 +00 +00 +00 +C0 +C0 +ENDCHAR +STARTCHAR 059 +ENCODING 59 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 3 9 2 -1 +BITMAP +60 +60 +00 +00 +00 +60 +60 +60 +C0 +ENDCHAR +STARTCHAR 060 +ENCODING 60 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 0 +BITMAP +06 +0C +18 +30 +60 +C0 +60 +30 +18 +0C +06 +ENDCHAR +STARTCHAR 061 +ENCODING 61 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 3 1 4 +BITMAP +FC +00 +FC +ENDCHAR +STARTCHAR 062 +ENCODING 62 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 0 +BITMAP +C0 +60 +30 +18 +0C +06 +0C +18 +30 +60 +C0 +ENDCHAR +STARTCHAR 063 +ENCODING 63 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +7C +C6 +86 +06 +0C +18 +18 +18 +18 +00 +18 +18 +ENDCHAR +STARTCHAR 064 +ENCODING 64 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 0 +BITMAP +7C +C6 +C6 +CE +DE +DE +DE +DE +CC +C0 +7C +ENDCHAR +STARTCHAR 065 +ENCODING 65 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +10 +38 +6C +C6 +C6 +C6 +FE +C6 +C6 +C6 +C6 +C6 +ENDCHAR +STARTCHAR 066 +ENCODING 66 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +FC +66 +66 +66 +66 +7C +66 +66 +66 +66 +66 +FC +ENDCHAR +STARTCHAR 067 +ENCODING 67 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +3C +66 +C2 +C0 +C0 +C0 +C0 +C0 +C0 +C2 +66 +3C +ENDCHAR +STARTCHAR 068 +ENCODING 68 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +F8 +6C +66 +66 +66 +66 +66 +66 +66 +66 +6C +F8 +ENDCHAR +STARTCHAR 069 +ENCODING 69 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +FE +66 +62 +62 +68 +78 +68 +60 +62 +62 +66 +FE +ENDCHAR +STARTCHAR 070 +ENCODING 70 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +FE +66 +62 +62 +68 +78 +68 +60 +60 +60 +60 +F0 +ENDCHAR +STARTCHAR 071 +ENCODING 71 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +3C +66 +C2 +C0 +C0 +C0 +C0 +DE +C6 +C6 +66 +3A +ENDCHAR +STARTCHAR 072 +ENCODING 72 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +C6 +C6 +C6 +C6 +C6 +FE +C6 +C6 +C6 +C6 +C6 +C6 +ENDCHAR +STARTCHAR 073 +ENCODING 73 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 4 12 2 0 +BITMAP +F0 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +F0 +ENDCHAR +STARTCHAR 074 +ENCODING 74 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +1E +0C +0C +0C +0C +0C +0C +CC +CC +CC +CC +78 +ENDCHAR +STARTCHAR 075 +ENCODING 75 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +E6 +66 +66 +66 +6C +78 +78 +6C +66 +66 +66 +E6 +ENDCHAR +STARTCHAR 076 +ENCODING 76 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +F0 +60 +60 +60 +60 +60 +60 +60 +62 +62 +66 +FE +ENDCHAR +STARTCHAR 077 +ENCODING 77 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +C6 +EE +FE +D6 +D6 +D6 +C6 +C6 +C6 +C6 +C6 +C6 +ENDCHAR +STARTCHAR 078 +ENCODING 78 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +C6 +E6 +F6 +DE +CE +C6 +C6 +C6 +C6 +C6 +C6 +C6 +ENDCHAR +STARTCHAR 079 +ENCODING 79 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +7C +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +ENDCHAR +STARTCHAR 080 +ENCODING 80 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +FC +66 +66 +66 +66 +7C +60 +60 +60 +60 +60 +F0 +ENDCHAR +STARTCHAR 081 +ENCODING 81 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 14 0 -2 +BITMAP +7C +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +D6 +DE +7C +0C +0E +ENDCHAR +STARTCHAR 082 +ENCODING 82 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +FC +66 +66 +66 +66 +7C +6C +66 +66 +66 +66 +E6 +ENDCHAR +STARTCHAR 083 +ENCODING 83 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +7C +C6 +C6 +C2 +60 +38 +0C +06 +86 +C6 +C6 +7C +ENDCHAR +STARTCHAR 084 +ENCODING 84 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 12 1 0 +BITMAP +FC +FC +B4 +30 +30 +30 +30 +30 +30 +30 +30 +78 +ENDCHAR +STARTCHAR 085 +ENCODING 85 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +ENDCHAR +STARTCHAR 086 +ENCODING 86 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +6C +38 +10 +ENDCHAR +STARTCHAR 087 +ENCODING 87 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +C6 +C6 +C6 +C6 +C6 +C6 +D6 +D6 +D6 +FE +EE +6C +ENDCHAR +STARTCHAR 088 +ENCODING 88 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +C6 +C6 +6C +6C +38 +38 +38 +38 +6C +6C +C6 +C6 +ENDCHAR +STARTCHAR 089 +ENCODING 89 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 12 1 0 +BITMAP +CC +CC +CC +CC +CC +78 +30 +30 +30 +30 +30 +78 +ENDCHAR +STARTCHAR 090 +ENCODING 90 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +FE +C6 +86 +86 +0C +18 +30 +60 +C2 +C2 +C6 +FE +ENDCHAR +STARTCHAR 091 +ENCODING 91 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 4 12 2 0 +BITMAP +F0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +F0 +ENDCHAR +STARTCHAR 092 +ENCODING 92 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 1 +BITMAP +80 +C0 +60 +30 +18 +0C +06 +02 +ENDCHAR +STARTCHAR 093 +ENCODING 93 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 4 12 2 0 +BITMAP +F0 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +F0 +ENDCHAR +STARTCHAR 094 +ENCODING 94 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 5 0 8 +BITMAP +10 +38 +6C +C6 +82 +ENDCHAR +STARTCHAR 095 +ENCODING 95 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 1 0 -2 +BITMAP +FF +ENDCHAR +STARTCHAR 096 +ENCODING 96 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 3 5 2 9 +BITMAP +C0 +C0 +C0 +C0 +60 +ENDCHAR +STARTCHAR 097 +ENCODING 97 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +78 +0C +0C +7C +CC +CC +CC +76 +ENDCHAR +STARTCHAR 098 +ENCODING 98 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +E0 +60 +60 +60 +78 +6C +66 +66 +66 +66 +66 +7C +ENDCHAR +STARTCHAR 099 +ENCODING 99 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +7C +C6 +C0 +C0 +C0 +C0 +C6 +7C +ENDCHAR +STARTCHAR 100 +ENCODING 100 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +1C +0C +0C +0C +3C +6C +CC +CC +CC +CC +CC +76 +ENDCHAR +STARTCHAR 101 +ENCODING 101 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +7C +C6 +C6 +FE +C0 +C0 +C6 +7C +ENDCHAR +STARTCHAR 102 +ENCODING 102 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 12 1 0 +BITMAP +38 +6C +64 +60 +F0 +60 +60 +60 +60 +60 +60 +F0 +ENDCHAR +STARTCHAR 103 +ENCODING 103 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 -4 +BITMAP +76 +CC +CC +CC +CC +CC +CC +7C +0C +0C +CC +78 +ENDCHAR +STARTCHAR 104 +ENCODING 104 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +E0 +60 +60 +60 +6C +76 +66 +66 +66 +66 +66 +E6 +ENDCHAR +STARTCHAR 105 +ENCODING 105 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 4 11 2 0 +BITMAP +60 +60 +00 +E0 +60 +60 +60 +60 +60 +60 +F0 +ENDCHAR +STARTCHAR 106 +ENCODING 106 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 15 0 -4 +BITMAP +0C +0C +00 +1C +0C +0C +0C +0C +0C +0C +0C +CC +CC +CC +78 +ENDCHAR +STARTCHAR 107 +ENCODING 107 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +E0 +60 +60 +60 +66 +6C +78 +78 +6C +66 +66 +E6 +ENDCHAR +STARTCHAR 108 +ENCODING 108 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 4 12 2 0 +BITMAP +E0 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +F0 +ENDCHAR +STARTCHAR 109 +ENCODING 109 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +6C +FE +D6 +D6 +D6 +D6 +C6 +C6 +ENDCHAR +STARTCHAR 110 +ENCODING 110 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +DC +66 +66 +66 +66 +66 +66 +66 +ENDCHAR +STARTCHAR 111 +ENCODING 111 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +7C +C6 +C6 +C6 +C6 +C6 +C6 +7C +ENDCHAR +STARTCHAR 112 +ENCODING 112 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 -4 +BITMAP +DC +66 +66 +66 +66 +66 +66 +7C +60 +60 +60 +F0 +ENDCHAR +STARTCHAR 113 +ENCODING 113 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 -4 +BITMAP +76 +CC +CC +CC +CC +CC +CC +7C +0C +0C +0C +1E +ENDCHAR +STARTCHAR 114 +ENCODING 114 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +DC +76 +66 +60 +60 +60 +60 +F0 +ENDCHAR +STARTCHAR 115 +ENCODING 115 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +7C +C6 +C2 +78 +0C +86 +C6 +7C +ENDCHAR +STARTCHAR 116 +ENCODING 116 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +10 +30 +30 +30 +FC +30 +30 +30 +30 +30 +36 +1C +ENDCHAR +STARTCHAR 117 +ENCODING 117 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +CC +CC +CC +CC +CC +CC +CC +76 +ENDCHAR +STARTCHAR 118 +ENCODING 118 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 8 1 0 +BITMAP +CC +CC +CC +CC +CC +CC +78 +30 +ENDCHAR +STARTCHAR 119 +ENCODING 119 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +C6 +C6 +C6 +D6 +D6 +D6 +FE +6C +ENDCHAR +STARTCHAR 120 +ENCODING 120 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +C6 +6C +38 +10 +38 +6C +C6 +82 +ENDCHAR +STARTCHAR 121 +ENCODING 121 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 -4 +BITMAP +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7E +06 +06 +0C +F8 +ENDCHAR +STARTCHAR 122 +ENCODING 122 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +FE +CC +18 +30 +60 +C0 +C6 +FE +ENDCHAR +STARTCHAR 123 +ENCODING 123 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 12 1 0 +BITMAP +1C +30 +30 +30 +30 +E0 +30 +30 +30 +30 +30 +1C +ENDCHAR +STARTCHAR 124 +ENCODING 124 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 2 12 3 0 +BITMAP +C0 +C0 +C0 +C0 +C0 +00 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 125 +ENCODING 125 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 12 1 0 +BITMAP +E0 +30 +30 +30 +30 +1C +30 +30 +30 +30 +30 +E0 +ENDCHAR +STARTCHAR 126 +ENCODING 126 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 3 0 10 +BITMAP +76 +D4 +9C +ENDCHAR +STARTCHAR 127 +ENCODING 127 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 1 +BITMAP +10 +38 +6C +C6 +C6 +C6 +C6 +FE +ENDCHAR +STARTCHAR 128 +ENCODING 128 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 14 0 -2 +BITMAP +3C +66 +C2 +C0 +C0 +C0 +C0 +C0 +C2 +66 +3C +0C +06 +7C +ENDCHAR +STARTCHAR 129 +ENCODING 129 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 0 +BITMAP +CC +CC +00 +CC +CC +CC +CC +CC +CC +CC +76 +ENDCHAR +STARTCHAR 130 +ENCODING 130 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +0C +18 +30 +00 +00 +7C +C6 +C6 +FE +C0 +C0 +C6 +7C +ENDCHAR +STARTCHAR 131 +ENCODING 131 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +10 +38 +6C +00 +00 +78 +0C +0C +7C +CC +CC +CC +76 +ENDCHAR +STARTCHAR 132 +ENCODING 132 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 0 +BITMAP +CC +CC +00 +78 +0C +0C +7C +CC +CC +CC +76 +ENDCHAR +STARTCHAR 133 +ENCODING 133 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +60 +30 +18 +0C +00 +78 +0C +0C +7C +CC +CC +CC +76 +ENDCHAR +STARTCHAR 134 +ENCODING 134 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +38 +6C +6C +38 +00 +78 +0C +0C +7C +CC +CC +CC +76 +ENDCHAR +STARTCHAR 135 +ENCODING 135 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 11 1 -1 +BITMAP +78 +CC +C4 +C0 +C0 +CC +C8 +78 +18 +0C +78 +ENDCHAR +STARTCHAR 136 +ENCODING 136 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +10 +38 +6C +44 +00 +7C +C6 +C6 +FE +C0 +C0 +C6 +7C +ENDCHAR +STARTCHAR 137 +ENCODING 137 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 0 +BITMAP +C6 +C6 +00 +7C +C6 +C6 +FE +C0 +C0 +C6 +7C +ENDCHAR +STARTCHAR 138 +ENCODING 138 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +60 +30 +18 +0C +00 +7C +C6 +C6 +FE +C0 +C0 +C6 +7C +ENDCHAR +STARTCHAR 139 +ENCODING 139 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 11 1 0 +BITMAP +CC +CC +00 +70 +30 +30 +30 +30 +30 +30 +78 +ENDCHAR +STARTCHAR 140 +ENCODING 140 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 13 1 0 +BITMAP +30 +78 +CC +84 +00 +70 +30 +30 +30 +30 +30 +30 +78 +ENDCHAR +STARTCHAR 141 +ENCODING 141 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 13 1 0 +BITMAP +C0 +60 +30 +10 +00 +70 +30 +30 +30 +30 +30 +30 +78 +ENDCHAR +STARTCHAR 142 +ENCODING 142 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 14 0 0 +BITMAP +C6 +C6 +10 +38 +6C +C6 +C6 +C6 +FE +C6 +C6 +C6 +C6 +C6 +ENDCHAR +STARTCHAR 143 +ENCODING 143 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 14 0 0 +BITMAP +38 +28 +38 +10 +38 +6C +C6 +C6 +FE +C6 +C6 +C6 +C6 +C6 +ENDCHAR +STARTCHAR 144 +ENCODING 144 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 14 0 0 +BITMAP +18 +30 +60 +00 +FE +66 +62 +68 +78 +68 +60 +62 +66 +FE +ENDCHAR +STARTCHAR 145 +ENCODING 145 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +CC +76 +36 +36 +7E +D8 +D8 +6E +ENDCHAR +STARTCHAR 146 +ENCODING 146 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +3E +6C +CC +CC +FE +CC +CC +CC +CC +CC +CC +CE +ENDCHAR +STARTCHAR 147 +ENCODING 147 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +10 +38 +6C +44 +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +7C +ENDCHAR +STARTCHAR 148 +ENCODING 148 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 0 +BITMAP +C6 +C6 +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +7C +ENDCHAR +STARTCHAR 149 +ENCODING 149 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +60 +30 +18 +0C +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +7C +ENDCHAR +STARTCHAR 150 +ENCODING 150 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +30 +78 +CC +84 +00 +CC +CC +CC +CC +CC +CC +CC +76 +ENDCHAR +STARTCHAR 151 +ENCODING 151 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +60 +30 +18 +0C +00 +CC +CC +CC +CC +CC +CC +CC +76 +ENDCHAR +STARTCHAR 152 +ENCODING 152 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 15 0 -4 +BITMAP +C6 +C6 +00 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7E +06 +06 +0C +78 +ENDCHAR +STARTCHAR 153 +ENCODING 153 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 14 0 0 +BITMAP +C6 +C6 +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +ENDCHAR +STARTCHAR 154 +ENCODING 154 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 14 0 0 +BITMAP +C6 +C6 +00 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +7C +ENDCHAR +STARTCHAR 155 +ENCODING 155 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 16 0 -2 +BITMAP +08 +08 +3C +6E +CA +C8 +C8 +C8 +C8 +C8 +C8 +CA +6E +3C +08 +08 +ENDCHAR +STARTCHAR 156 +ENCODING 156 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +38 +6C +64 +64 +60 +F0 +60 +60 +60 +60 +62 +E6 +FC +ENDCHAR +STARTCHAR 157 +ENCODING 157 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 12 1 0 +BITMAP +CC +CC +CC +CC +78 +30 +FC +30 +FC +30 +30 +30 +ENDCHAR +STARTCHAR 158 +ENCODING 158 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +F8 +CC +CC +FC +F8 +C4 +CC +DE +CC +CC +CC +CC +C6 +ENDCHAR +STARTCHAR 159 +ENCODING 159 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 15 0 -2 +BITMAP +0E +1B +19 +18 +18 +18 +7E +18 +18 +18 +18 +18 +98 +D8 +70 +ENDCHAR +STARTCHAR 160 +ENCODING 160 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +18 +30 +60 +C0 +00 +78 +0C +0C +7C +CC +CC +CC +76 +ENDCHAR +STARTCHAR 161 +ENCODING 161 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 4 13 2 0 +BITMAP +30 +60 +C0 +80 +00 +E0 +60 +60 +60 +60 +60 +60 +F0 +ENDCHAR +STARTCHAR 162 +ENCODING 162 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +18 +30 +60 +40 +00 +7C +C6 +C6 +C6 +C6 +C6 +C6 +7C +ENDCHAR +STARTCHAR 163 +ENCODING 163 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 13 0 0 +BITMAP +18 +30 +60 +40 +00 +CC +CC +CC +CC +CC +CC +CC +76 +ENDCHAR +STARTCHAR 164 +ENCODING 164 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +36 +D4 +88 +00 +DC +66 +66 +66 +66 +66 +66 +66 +ENDCHAR +STARTCHAR 165 +ENCODING 165 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 14 0 0 +BITMAP +36 +CC +00 +C6 +E6 +F6 +DE +CE +C6 +C6 +C6 +C6 +C6 +C6 +ENDCHAR +STARTCHAR 166 +ENCODING 166 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 7 1 6 +BITMAP +78 +D8 +D8 +D8 +7C +00 +FC +ENDCHAR +STARTCHAR 167 +ENCODING 167 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 7 1 6 +BITMAP +70 +D8 +D8 +D8 +70 +00 +F8 +ENDCHAR +STARTCHAR 168 +ENCODING 168 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +30 +30 +00 +30 +30 +30 +60 +C0 +C6 +C6 +C6 +7C +ENDCHAR +STARTCHAR 169 +ENCODING 169 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 7 0 0 +BITMAP +FE +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 170 +ENCODING 170 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 7 0 0 +BITMAP +FE +06 +06 +06 +06 +06 +06 +ENDCHAR +STARTCHAR 171 +ENCODING 171 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 15 0 -2 +BITMAP +C0 +C0 +C0 +C2 +C6 +0C +18 +30 +60 +C0 +9C +06 +0C +18 +3E +ENDCHAR +STARTCHAR 172 +ENCODING 172 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 15 0 -2 +BITMAP +C0 +C0 +C0 +C2 +C6 +0C +18 +30 +66 +CE +9A +32 +3E +06 +06 +ENDCHAR +STARTCHAR 173 +ENCODING 173 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 4 12 2 0 +BITMAP +60 +60 +00 +60 +60 +60 +60 +F0 +F0 +F0 +F0 +60 +ENDCHAR +STARTCHAR 174 +ENCODING 174 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 6 0 2 +BITMAP +36 +6C +D8 +D8 +6C +36 +ENDCHAR +STARTCHAR 175 +ENCODING 175 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 6 0 2 +BITMAP +D8 +6C +36 +36 +6C +D8 +ENDCHAR +STARTCHAR 176 +ENCODING 176 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +11 +44 +11 +44 +11 +44 +11 +44 +11 +44 +11 +44 +11 +44 +11 +44 +11 +44 +11 +ENDCHAR +STARTCHAR 177 +ENCODING 177 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +55 +AA +55 +AA +55 +AA +55 +AA +55 +AA +55 +AA +55 +AA +55 +AA +55 +AA +55 +ENDCHAR +STARTCHAR 178 +ENCODING 178 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +DD +77 +DD +77 +DD +77 +DD +77 +DD +77 +DD +77 +DD +77 +DD +77 +DD +77 +DD +ENDCHAR +STARTCHAR 179 +ENCODING 179 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 180 +ENCODING 180 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +18 +F8 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 181 +ENCODING 181 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +F8 +18 +F8 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 182 +ENCODING 182 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +36 +36 +36 +36 +36 +36 +36 +36 +36 +F6 +36 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR 183 +ENCODING 183 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 10 0 -5 +BITMAP +FE +36 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR 184 +ENCODING 184 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 11 0 -5 +BITMAP +F8 +18 +F8 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 185 +ENCODING 185 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +36 +36 +36 +36 +36 +36 +36 +36 +F6 +06 +F6 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR 186 +ENCODING 186 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR 187 +ENCODING 187 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 -5 +BITMAP +FE +06 +F6 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR 188 +ENCODING 188 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 3 +BITMAP +36 +36 +36 +36 +36 +36 +36 +36 +F6 +06 +FE +ENDCHAR +STARTCHAR 189 +ENCODING 189 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 10 0 4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +36 +36 +FE +ENDCHAR +STARTCHAR 190 +ENCODING 190 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 11 0 3 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +F8 +18 +F8 +ENDCHAR +STARTCHAR 191 +ENCODING 191 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 10 0 -5 +BITMAP +F8 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 192 +ENCODING 192 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 10 3 4 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +F8 +ENDCHAR +STARTCHAR 193 +ENCODING 193 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 10 0 4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +18 +FF +ENDCHAR +STARTCHAR 194 +ENCODING 194 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 10 0 -5 +BITMAP +FF +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 195 +ENCODING 195 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +18 +1F +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 196 +ENCODING 196 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 1 0 4 +BITMAP +FF +ENDCHAR +STARTCHAR 197 +ENCODING 197 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +18 +FF +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 198 +ENCODING 198 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +1F +18 +1F +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 199 +ENCODING 199 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +36 +36 +36 +36 +36 +36 +36 +36 +36 +37 +36 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR 200 +ENCODING 200 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 11 2 3 +BITMAP +D8 +D8 +D8 +D8 +D8 +D8 +D8 +D8 +DC +C0 +FC +ENDCHAR +STARTCHAR 201 +ENCODING 201 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 11 2 -5 +BITMAP +FC +C0 +DC +D8 +D8 +D8 +D8 +D8 +D8 +D8 +D8 +ENDCHAR +STARTCHAR 202 +ENCODING 202 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 11 0 3 +BITMAP +36 +36 +36 +36 +36 +36 +36 +36 +F7 +00 +FF +ENDCHAR +STARTCHAR 203 +ENCODING 203 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 11 0 -5 +BITMAP +FF +00 +F7 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR 204 +ENCODING 204 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +36 +36 +36 +36 +36 +36 +36 +36 +37 +30 +37 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR 205 +ENCODING 205 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 3 0 3 +BITMAP +FF +00 +FF +ENDCHAR +STARTCHAR 206 +ENCODING 206 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +36 +36 +36 +36 +36 +36 +36 +36 +F7 +00 +F7 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR 207 +ENCODING 207 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 11 0 3 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +FF +00 +FF +ENDCHAR +STARTCHAR 208 +ENCODING 208 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 10 0 4 +BITMAP +36 +36 +36 +36 +36 +36 +36 +36 +36 +FF +ENDCHAR +STARTCHAR 209 +ENCODING 209 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 11 0 -5 +BITMAP +FF +00 +FF +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 210 +ENCODING 210 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 10 0 -5 +BITMAP +FF +36 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR 211 +ENCODING 211 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 10 2 4 +BITMAP +D8 +D8 +D8 +D8 +D8 +D8 +D8 +D8 +D8 +FC +ENDCHAR +STARTCHAR 212 +ENCODING 212 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 11 3 3 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +F8 +C0 +F8 +ENDCHAR +STARTCHAR 213 +ENCODING 213 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 11 3 -5 +BITMAP +F8 +C0 +F8 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 214 +ENCODING 214 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 10 2 -5 +BITMAP +FC +D8 +D8 +D8 +D8 +D8 +D8 +D8 +D8 +D8 +ENDCHAR +STARTCHAR 215 +ENCODING 215 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +36 +36 +36 +36 +36 +36 +36 +36 +36 +FF +36 +36 +36 +36 +36 +36 +36 +36 +36 +ENDCHAR +STARTCHAR 216 +ENCODING 216 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +FF +18 +FF +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 217 +ENCODING 217 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 10 0 4 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +18 +F8 +ENDCHAR +STARTCHAR 218 +ENCODING 218 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 10 3 -5 +BITMAP +F8 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 219 +ENCODING 219 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +ENDCHAR +STARTCHAR 220 +ENCODING 220 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 10 0 -5 +BITMAP +FF +FF +FF +FF +FF +FF +FF +FF +FF +FF +ENDCHAR +STARTCHAR 221 +ENCODING 221 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +F0 +ENDCHAR +STARTCHAR 222 +ENCODING 222 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 19 0 -5 +BITMAP +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +0F +ENDCHAR +STARTCHAR 223 +ENCODING 223 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 9 0 5 +BITMAP +FF +FF +FF +FF +FF +FF +FF +FF +FF +ENDCHAR +STARTCHAR 224 +ENCODING 224 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +76 +DC +D8 +D8 +D8 +D8 +DC +76 +ENDCHAR +STARTCHAR 225 +ENCODING 225 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +78 +CC +CC +CC +CC +D8 +CC +C6 +C6 +C6 +C6 +CC +ENDCHAR +STARTCHAR 226 +ENCODING 226 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +FE +C6 +C6 +C6 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 227 +ENCODING 227 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 10 0 0 +BITMAP +FE +6C +6C +6C +6C +6C +6C +6C +6C +6C +ENDCHAR +STARTCHAR 228 +ENCODING 228 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 0 +BITMAP +FE +C6 +C2 +60 +30 +18 +30 +60 +C2 +C6 +FE +ENDCHAR +STARTCHAR 229 +ENCODING 229 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 9 0 0 +BITMAP +7E +D8 +D8 +D8 +D8 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR 230 +ENCODING 230 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 -1 +BITMAP +66 +66 +66 +66 +66 +66 +66 +7C +60 +60 +C0 +ENDCHAR +STARTCHAR 231 +ENCODING 231 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 10 0 0 +BITMAP +76 +DC +98 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 232 +ENCODING 232 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 11 1 0 +BITMAP +FC +30 +78 +CC +CC +CC +CC +CC +78 +30 +FC +ENDCHAR +STARTCHAR 233 +ENCODING 233 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 0 +BITMAP +38 +6C +C6 +C6 +C6 +FE +C6 +C6 +C6 +6C +38 +ENDCHAR +STARTCHAR 234 +ENCODING 234 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 12 0 0 +BITMAP +38 +6C +C6 +C6 +C6 +C6 +6C +6C +6C +6C +6C +EE +ENDCHAR +STARTCHAR 235 +ENCODING 235 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 12 1 0 +BITMAP +3C +40 +60 +30 +18 +7C +CC +CC +CC +CC +CC +78 +ENDCHAR +STARTCHAR 236 +ENCODING 236 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 7 0 2 +BITMAP +7E +DB +DB +DB +DB +DB +7E +ENDCHAR +STARTCHAR 237 +ENCODING 237 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 11 0 0 +BITMAP +03 +06 +7E +DB +DB +DB +DB +D3 +7E +60 +C0 +ENDCHAR +STARTCHAR 238 +ENCODING 238 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 12 1 0 +BITMAP +38 +60 +C0 +C0 +C0 +F8 +C0 +C0 +C0 +C0 +60 +38 +ENDCHAR +STARTCHAR 239 +ENCODING 239 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 11 0 0 +BITMAP +7C +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +ENDCHAR +STARTCHAR 240 +ENCODING 240 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 7 0 2 +BITMAP +FE +00 +00 +FE +00 +00 +FE +ENDCHAR +STARTCHAR 241 +ENCODING 241 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 8 0 0 +BITMAP +18 +18 +7E +18 +18 +00 +00 +FF +ENDCHAR +STARTCHAR 242 +ENCODING 242 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 11 1 0 +BITMAP +C0 +60 +30 +18 +0C +18 +30 +60 +C0 +00 +FC +ENDCHAR +STARTCHAR 243 +ENCODING 243 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 11 1 0 +BITMAP +0C +18 +30 +60 +C0 +60 +30 +18 +0C +00 +FC +ENDCHAR +STARTCHAR 244 +ENCODING 244 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 17 3 -5 +BITMAP +70 +D8 +D8 +D8 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 245 +ENCODING 245 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 17 0 -3 +BITMAP +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR 246 +ENCODING 246 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 7 1 2 +BITMAP +30 +30 +00 +FC +00 +30 +30 +ENDCHAR +STARTCHAR 247 +ENCODING 247 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 7 5 0 3 +BITMAP +76 +DC +00 +76 +DC +ENDCHAR +STARTCHAR 248 +ENCODING 248 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 5 1 8 +BITMAP +70 +D8 +D8 +D8 +70 +ENDCHAR +STARTCHAR 249 +ENCODING 249 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 2 3 3 3 +BITMAP +C0 +C0 +C0 +ENDCHAR +STARTCHAR 250 +ENCODING 250 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 2 2 3 3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR 251 +ENCODING 251 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 8 13 0 0 +BITMAP +0F +0C +0C +0C +0C +0C +0C +EC +6C +6C +6C +3C +1C +ENDCHAR +STARTCHAR 252 +ENCODING 252 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 6 7 0 6 +BITMAP +D8 +6C +6C +6C +6C +6C +6C +ENDCHAR +STARTCHAR 253 +ENCODING 253 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 7 0 6 +BITMAP +70 +D8 +10 +30 +60 +C8 +F8 +ENDCHAR +STARTCHAR 254 +ENCODING 254 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 5 8 1 1 +BITMAP +F8 +F8 +F8 +F8 +F8 +F8 +F8 +F8 +ENDCHAR +STARTCHAR 255 +ENCODING 255 +SWIDTH 404 0 +DWIDTH 8 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +ENDFONT diff --git a/etc/wyse60-pckb b/etc/wyse60-pckb new file mode 100644 index 0000000..2cdf4a6 --- /dev/null +++ b/etc/wyse60-pckb @@ -0,0 +1,34 @@ +wyse60-pckb|Wyse 60 with ps scancode support, + am, bw, hs, km, mc5i, mir, msgr, + cols#80, lh#1, lines#24, lw#8, nlab#8, wsl#45, + acsc=+/\,.0[Iha2fxgqh1jYk?lZm@nEqDtCu4vAwBx3yszr{c~~, + bel=^G, blink=\EG2, cbt=\EI, civis=\E`0, clear=\E+$<100>, + cnorm=\E`1, cr=^M, cub1=^H, cud1=^J, cuf1=^L, + cup=\E=%p1%{32}%+%c%p2%{32}%+%c, cuu1=^K, + dch1=\EW$<11>, dclk=\E`b, dim=\EGp, dl1=\ER$<5>, dsl=\EF\r, + ed=\EY$<100>, el=\ET, flash=\E`8$<100/>\E`9, fsl=^M, + home=\E{, ht=\011$<1>, hts=\E1, il1=\EE$<4>, ind=\n$<5>, + invis=\EG1, ip=$<3>, is1=\EcB0\EcC1, + is2=\Ed$\EcD\E'\Er\EH\003\Ed/\EO\Ee1\Ed*\E`@\E`9\E`1\016\024\El, + is3=\EwJ\Ew1$<150>, kHOM=\E{, kbs=^H, kcbt=\EI, kcub1=^H, + kcud1=^J, kcuf1=^L, kcuu1=^K, kdch1=\EW, kdl1=\ER, ked=\EY, + kel=\ET, kent=\E7, kf1=^A@\r, kf10=^AI\r, kf11=^AJ\r, + kf12=^AK\r, kf13=^AL\r, kf14=^AM\r, kf15=^AN\r, kf16=^AO\r, + kf2=^AA\r, kf3=^AB\r, kf4=^AC\r, kf5=^AD\r, kf6=^AE\r, + kf7=^AF\r, kf8=^AG\r, kf9=^AH\r, khome=^^, kich1=\EQ, + kil1=\EE, knp=\EK, kpp=\EJ, kprt=\EP, krpl=\Er, ll=\E{^K, + mc0=\EP, mc4=^T, mc5=\Ed#, nel=\r\n$<3>, + pfloc=\EZ2%p1%'?'%+%c%p2%s\177, + pfx=\EZ1%p1%'?'%+%c%p2%s\177, + pln=\Ez%p1%'/'%+%c%p2%s\r, prot=\E), rev=\EG4, + ri=\Ej$<7>, rmacs=\EcD, rmam=\Ed., rmclk=\E`c, rmcup=\Ew1, + rmir=\Er, rmln=\EA11, rmso=\EG0, rmul=\EG0, rmxon=\Ec20, + rs1=\E~!\E~4$<150>, rs2=\EeG$<150>, rs3=\EwG\Ee($<200>, + sgr=%?%p8%t\E)%e\E(%;%?%p9%t\EcE%e\EcD%;\EG%'0'%?%p2%t%{8}%|%;%?%p1%p3%|%p6%|%t%{4}%|%;%?%p4%t%{2}%|%;%?%p1%p5%|%t%{64}%|%;%?%p7%t%{1}%|%;%c, + sgr0=\E(\EH\003\EG0\EcD, smacs=\EcE, smam=\Ed/, + smcup=\Ew0, smir=\Eq, smln=\EA10, smso=\EGt, smul=\EG8, + smxon=\Ec21, tbc=\E0, tsl=\EF, +# enter pc scancode mode S4 + smsc=\E~5, +# exit pc scancode mode S5 + rmsc=\Ev4, diff --git a/getversion b/getversion new file mode 100755 index 0000000..a88489c --- /dev/null +++ b/getversion @@ -0,0 +1,61 @@ +#!/bin/sh + +if git describe --match 'dosemu2*' >/dev/null 2>&1 ; then + USE_GIT=1 +else + USE_GIT=0 +fi + +if [ "$USE_GIT" = "1" ]; then + # need -n 1 for merge commits + DATE=`git log HEAD^..HEAD -n 1 --format=%cd --date=short` + REV=`git describe --match dosemu-1.5.0 | cut -d- -f3` +else + DATE=`date -r VERSION +%F` + REV=`tail -n 1 VERSION` +fi + +if test "$1" = "-r"; then + shift + if [ -n "$1" ]; then + REV=`git rev-list dosemu-1.5.0..$1 --count` + fi + echo "$REV" + exit 0 +fi + +if test "$1" = "-d"; then + echo "$DATE" +else + if [ "$USE_GIT" = "1" ]; then + DATE=`echo "$DATE" | sed -e 's/-//g'` + gd=`git describe --tags --match '*2.*' HEAD 2>/dev/null` + s=`echo $gd | sed -e "s/-\([^-]\+-g\)/-$DATE-\1/"` + ss=`echo $gd | sed -e "s/-[^-]\+-g.*/\.$DATE/"` + fi + v1=`head -n 1 VERSION` + if test -z "$gd"; then + s=$v1 + ss=$v1 + v=$v1 + else + v=`echo $gd | cut -d - -f 1` + fi + if test "$1" = "-s"; then + echo "$v" + exit 0 + fi + if test "$1" = "-b"; then + echo "$ss" + exit 0 + fi + if test "$1" = "-u"; then + r1=`tail -n 1 VERSION` + fi + if [ -n "$v" -a "$v" != "$v1" ] || [ -n "$r1" -a "$REV" != "$r1" ]; then + echo "Update VERSION $v1 -> $v" >&2 + echo $v > VERSION + echo "$REV" >> VERSION + fi + echo $s +fi diff --git a/git-rev.sh b/git-rev.sh new file mode 100755 index 0000000..9b110aa --- /dev/null +++ b/git-rev.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +if [ $# != 2 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi +DATE=`git -C $1 log -1 --format=%cd --date=format-local:'%m%d%H%M.%S'` +if [ $? != 0 ]; then + echo "Non-git builds deprecated" >&2 + exit 1 +fi +TSTAMP=$2/.tstamp +if ! touch -t "$DATE" $TSTAMP 2>/dev/null; then + echo "touch doesnt support -t, build may be incomplete" >&2 + if [ ! -f "$TSTAMP" ]; then + touch $TSTAMP + fi +fi +echo $TSTAMP diff --git a/m4/ac_define_dir.m4 b/m4/ac_define_dir.m4 new file mode 100644 index 0000000..e15cea2 --- /dev/null +++ b/m4/ac_define_dir.m4 @@ -0,0 +1,34 @@ +dnl @synopsis AC_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION]) +dnl +dnl This macro sets VARNAME to the expansion of the DIR variable, +dnl taking care of fixing up ${prefix} and such. +dnl +dnl VARNAME is then offered as both an output variable and a C +dnl preprocessor symbol. +dnl +dnl Example: +dnl +dnl AC_DEFINE_DIR([DATADIR], [datadir], [Where data are placed to.]) +dnl +dnl @category Misc +dnl @author Stepan Kasal +dnl @author Andreas Schwab +dnl @author Guido U. Draheim +dnl @author Alexandre Oliva +dnl @version 2006-10-13 +dnl @license AllPermissive + +AC_DEFUN([AC_DEFINE_DIR], [ + prefix_NONE= + exec_prefix_NONE= + test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix + test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix +dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn +dnl refers to ${prefix}. Thus we have to use `eval' twice. + eval ac_define_dir="\"[$]$2\"" + eval ac_define_dir="\"$ac_define_dir\"" + AC_SUBST($1, "$ac_define_dir") + AC_DEFINE_UNQUOTED($1, "$ac_define_dir", [$3]) + test "$prefix_NONE" && prefix=NONE + test "$exec_prefix_NONE" && exec_prefix=NONE +]) diff --git a/m4/adl_recursive_eval.m4 b/m4/adl_recursive_eval.m4 new file mode 100644 index 0000000..56bf672 --- /dev/null +++ b/m4/adl_recursive_eval.m4 @@ -0,0 +1,15 @@ +dnl adl_RECURSIVE_EVAL(VALUE, RESULT) +dnl ================================= +dnl Interpolate the VALUE in loop until it doesn't change, +dnl and set the result to $RESULT. +dnl WARNING: It's easy to get an infinite loop with some unsane input. +AC_DEFUN([adl_RECURSIVE_EVAL], +[_lcl_receval="$1" +$2=`(test "x$prefix" = xNONE && prefix="$ac_default_prefix" + test "x$exec_prefix" = xNONE && exec_prefix="${prefix}" + _lcl_receval_old='' + while test "[$]_lcl_receval_old" != "[$]_lcl_receval"; do + _lcl_receval_old="[$]_lcl_receval" + eval _lcl_receval="\"[$]_lcl_receval\"" + done + echo "[$]_lcl_receval")`]) diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 0000000..bd753b3 --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,53 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/m4/ax_check_link_flag.m4 b/m4/ax_check_link_flag.m4 new file mode 100644 index 0000000..03a30ce --- /dev/null +++ b/m4/ax_check_link_flag.m4 @@ -0,0 +1,53 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the linker or gives an error. +# (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the linker's default flags +# when the check is done. The check is thus made with the flags: "LDFLAGS +# EXTRA-FLAGS FLAG". This can for example be used to force the linker to +# issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_LINK_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +AC_DEFUN([AX_CHECK_LINK_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl +AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ + ax_check_save_flags=$LDFLAGS + LDFLAGS="$LDFLAGS $4 $1" + AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + LDFLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_LINK_FLAGS diff --git a/m4/ax_restore_flags.m4 b/m4/ax_restore_flags.m4 new file mode 100644 index 0000000..aafd363 --- /dev/null +++ b/m4/ax_restore_flags.m4 @@ -0,0 +1,52 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_restore_flags.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_RESTORE_FLAGS([namespace]) +# +# DESCRIPTION +# +# Restore common compilation flags from temporary variables. +# +# Compilation flags includes: CPPFLAGS, CFLAGS, CXXFLAGS, LDFLAGS, LIBS, +# OBJCFLAGS. +# +# By default these flags are restored to a global (empty) namespace, but +# user could restore from specific NAMESPACE by using +# AX_RESTORE_FLAGS(NAMESPACE) macro. +# +# Typical usage is like: +# +# AX_SAVE_FLAGS(mypackage) +# CPPFLAGS="-Imypackagespath ${CPPFLAGS}" +# dnl ... do some detection ... +# AX_RESTORE_FLAGS(mypackage) +# +# LICENSE +# +# Copyright (c) 2009 Filippo Giunchedi +# Copyright (c) 2011 The Board of Trustees of the Leland Stanford Junior University +# Copyright (c) 2011 Russ Allbery +# Copyright (c) 2013 Bastien ROUCARIES +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +# save one flag in name space +AC_DEFUN([_AX_RESTORE_ONE_FLAG],[dnl + AS_VAR_PUSHDEF([_ax_restore_flag_var], [$2[]_$1[]_ax_save_flags]) + AS_VAR_COPY($2[],_ax_restore_flag_var) + AS_VAR_POPDEF([_ax_restore_flag_var]) +]) + +AC_DEFUN([AX_RESTORE_FLAGS], [dnl + m4_foreach([FLAG], dnl + [_AX_SAVE_FLAGS_LIST()], dnl + [_AX_RESTORE_ONE_FLAG([$1],FLAG)]) +]) diff --git a/m4/ax_save_flags.m4 b/m4/ax_save_flags.m4 new file mode 100644 index 0000000..39f45be --- /dev/null +++ b/m4/ax_save_flags.m4 @@ -0,0 +1,71 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_save_flags.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_SAVE_FLAGS([NAMESPACE]) +# +# DESCRIPTION +# +# Save common compilation flags into temporary variables. +# +# Compilation flags includes: CPPFLAGS, CFLAGS, CXXFLAGS, LDFLAGS, LIBS, +# OBJCFLAGS. +# +# By default these flags are saved to a global (empty) namespace, but user +# could specify a specific NAMESPACE to AX_SAVE_FLAGS macro and latter +# restore it by using AX_RESTORE_FLAGS(NAMESPACE). +# +# AX_SAVE_FLAGS(mypackage) +# CPPFLAGS="-Imypackagespath ${CPPFLAGS}" +# dnl .. do some detection ... +# AX_RESTORE_FLAGS(mypackage) +# +# LICENSE +# +# Copyright (c) 2009 Filippo Giunchedi +# Copyright (c) 2011 The Board of Trustees of the Leland Stanford Junior University +# Copyright (c) 2011 Russ Allbery +# Copyright (c) 2013 Bastien ROUCARIES +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 7 + +# list of flag to save +AC_DEFUN([_AX_SAVE_FLAGS_LIST],[dnl +[CCASFLAGS],dnl +[CFLAGS],dnl +[CPPFLAGS],dnl +[CXXFLAGS],dnl +[ERLCFLAGS],dnl +[FCFLAGS],dnl +[FCLIBS],dnl +[FFLAGS],dnl +[FLIBS],dnl +[GCJFLAGS],dnl +[JAVACFLAGS],dnl +[LDFLAGS],dnl +[LIBS],dnl +[OBJCFLAGS],dnl +[OBJCXXFLAGS],dnl +[UPCFLAGS],dnl +[VALAFLAGS]dnl +]) + +# save one flag in name space +AC_DEFUN([_AX_SAVE_ONE_FLAG],[ + AS_VAR_PUSHDEF([_ax_save_flag_var], [$2[]_$1[]_ax_save_flags]) + AS_VAR_COPY(_ax_save_flag_var, $2[]) + AS_VAR_POPDEF([_ax_save_flag_var]) +]) + +AC_DEFUN([AX_SAVE_FLAGS],[dnl + m4_foreach([FLAG], dnl + [_AX_SAVE_FLAGS_LIST()], dnl + [_AX_SAVE_ONE_FLAG([$1],FLAG)]) +]) diff --git a/m4/c11.m4 b/m4/c11.m4 new file mode 100644 index 0000000..76ddbd2 --- /dev/null +++ b/m4/c11.m4 @@ -0,0 +1,258 @@ +# From autoconf latest git +# http://git.savannah.gnu.org/gitweb/?p=autoconf.git;a=blob_plain;f=lib/autoconf/c.m4;hb=refs/heads/master + +# _AC_C_STD_TRY(STANDARD, TEST-PROLOGUE, TEST-BODY, OPTION-LIST, +# ACTION-IF-AVAILABLE, ACTION-IF-UNAVAILABLE) +# -------------------------------------------------------------- +# Check whether the C compiler accepts features of STANDARD (e.g `c89', `c99') +# by trying to compile a program of TEST-PROLOGUE and TEST-BODY. If this fails, +# try again with each compiler option in the space-separated OPTION-LIST; if one +# helps, append it to CC. If eventually successful, run ACTION-IF-AVAILABLE, +# else ACTION-IF-UNAVAILABLE. +AC_DEFUN([_AC_C_STD_TRY], +[AC_MSG_CHECKING([for $CC option to enable ]m4_translit($1, [c], [C])[ features]) +AC_CACHE_VAL(ac_cv_prog_cc_$1, +[ac_cv_prog_cc_$1=no +ac_save_CC=$CC +AC_LANG_CONFTEST([AC_LANG_PROGRAM([$2], [$3])]) +for ac_arg in '' $4 +do + CC="$ac_save_CC $ac_arg" + _AC_COMPILE_IFELSE([], [ac_cv_prog_cc_$1=$ac_arg]) + test "x$ac_cv_prog_cc_$1" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +])# AC_CACHE_VAL +ac_prog_cc_stdc_options= +case "x$ac_cv_prog_cc_$1" in + x) + AC_MSG_RESULT([none needed]) ;; + xno) + AC_MSG_RESULT([unsupported]) ;; + *) + ac_prog_cc_stdc_options=" $ac_cv_prog_cc_$1" + CC=$CC$ac_prog_cc_stdc_options + AC_MSG_RESULT([$ac_cv_prog_cc_$1]) ;; +esac +AS_IF([test "x$ac_cv_prog_cc_$1" != xno], [$5], [$6]) +])# _AC_C_STD_TRY + +# _AC_C_C99_TEST_HEADER +# --------------------- +# A C header suitable for testing for C99. +AC_DEFUN([_AC_C_C99_TEST_HEADER], +[[#include +#include +#include +#include +#include +#include + +// Check varargs macros. These examples are taken from C99 6.10.3.5. +#define debug(...) fprintf (stderr, __VA_ARGS__) +#define showlist(...) puts (#__VA_ARGS__) +#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) +static void +test_varargs_macros (void) +{ + int x = 1234; + int y = 5678; + debug ("Flag"); + debug ("X = %d\n", x); + showlist (The first, second, and third items.); + report (x>y, "x is %d but y is %d", x, y); +} + +// Check long long types. +#define BIG64 18446744073709551615ull +#define BIG32 4294967295ul +#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) +#if !BIG_OK + your preprocessor is broken; +#endif +#if BIG_OK +#else + your preprocessor is broken; +#endif +static long long int bignum = -9223372036854775807LL; +static unsigned long long int ubignum = BIG64; + +struct incomplete_array +{ + int datasize; + double data[]; +}; + +struct named_init { + int number; + const wchar_t *name; + double average; +}; + +typedef const char *ccp; + +static inline int +test_restrict (ccp restrict text) +{ + // See if C++-style comments work. + // Iterate through items via the restricted pointer. + // Also check for declarations in for loops. + for (unsigned int i = 0; *(text+i) != '\0'; ++i) + continue; + return 0; +} + +// Check varargs and va_copy. +static bool +test_varargs (const char *format, ...) +{ + va_list args; + va_start (args, format); + va_list args_copy; + va_copy (args_copy, args); + + const char *str = ""; + int number = 0; + float fnumber = 0; + + while (*format) + { + switch (*format++) + { + case 's': // string + str = va_arg (args_copy, const char *); + break; + case 'd': // int + number = va_arg (args_copy, int); + break; + case 'f': // float + fnumber = va_arg (args_copy, double); + break; + default: + break; + } + } + va_end (args_copy); + va_end (args); + + return *str && number && fnumber; +}]])# _AC_C_C99_TEST_HEADER + +# _AC_C_C99_TEST_BODY +# ------------------- +# A C body suitable for testing for C99, assuming the corresponding header. +AC_DEFUN([_AC_C_C99_TEST_BODY], +[[ + // Check bool. + _Bool success = false; + + // Check restrict. + if (test_restrict ("String literal") == 0) + success = true; + char *restrict newvar = "Another string"; + + // Check varargs. + success &= test_varargs ("s, d' f .", "string", 65, 34.234); + test_varargs_macros (); + + // Check flexible array members. + struct incomplete_array *ia = + malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); + ia->datasize = 10; + for (int i = 0; i < ia->datasize; ++i) + ia->data[i] = i * 1.234; + + // Check named initializers. + struct named_init ni = { + .number = 34, + .name = L"Test wide string", + .average = 543.34343, + }; + + ni.number = 58; + + int dynamic_array[ni.number]; + dynamic_array[ni.number - 1] = 543; + + // work around unused variable warnings + return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' + || dynamic_array[ni.number - 1] != 543); +]]) + +# AC_PROG_CC_C11 ([ACTION-IF-AVAILABLE], [ACTION-IF-UNAVAILABLE]) +# ---------------------------------------------------------------- +# If the C compiler is not in ISO C11 mode by default, try to add an +# option to output variable CC to make it so. This macro tries +# various options that select ISO C11 on some system or another. It +# considers the compiler to be in ISO C11 mode if it handles _Alignas, +# _Alignof, _Noreturn, _Static_assert, UTF-8 string literals, +# duplicate typedefs, and anonymous structures and unions. +AC_DEFUN([AC_PROG_CC_C11], +[_AC_C_STD_TRY([c11], +[_AC_C_C99_TEST_HEADER[ +// Check _Alignas. +char _Alignas (double) aligned_as_double; +char _Alignas (0) no_special_alignment; +extern char aligned_as_int; +char _Alignas (0) _Alignas (int) aligned_as_int; + +// Check _Alignof. +enum +{ + int_alignment = _Alignof (int), + int_array_alignment = _Alignof (int[100]), + char_alignment = _Alignof (char) +}; +_Static_assert (0 < -_Alignof (int), "_Alignof is signed"); + +// Check _Noreturn. +int _Noreturn does_not_return (void) { for (;;) continue; } + +// Check _Static_assert. +struct test_static_assert +{ + int x; + _Static_assert (sizeof (int) <= sizeof (long int), + "_Static_assert does not work in struct"); + long int y; +}; + +// Check UTF-8 literals. +#define u8 syntax error! +char const utf8_literal[] = u8"happens to be ASCII" "another string"; + +// Check duplicate typedefs. +typedef long *long_ptr; +typedef long int *long_ptr; +typedef long_ptr long_ptr; + +// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. +struct anonymous +{ + union { + struct { int i; int j; }; + struct { int k; long int l; } w; + }; + int m; +} v1; +]], +[_AC_C_C99_TEST_BODY[ + v1.i = 2; + v1.w.k = 5; + _Static_assert ((offsetof (struct anonymous, i) + == offsetof (struct anonymous, w.k)), + "Anonymous union alignment botch"); +]], +dnl Try +dnl GCC -std=gnu11 (unused restrictive mode: -std=c11) +dnl with extended modes being tried first. +dnl +dnl Do not try -qlanglvl=extc1x, because IBM XL C V12.1 (the latest version as +dnl of September 2012) does not pass the C11 test. For now, try extc1x when +dnl compiling the C99 test instead, since it enables _Static_assert and +dnl _Noreturn, which is a win. If -qlanglvl=extc11 or -qlanglvl=extc1x passes +dnl the C11 test in some future version of IBM XL C, we'll add it here, +dnl preferably extc11. +[[-std=gnu11 -std=c11]], [$1], [$2])[]dnl +])# AC_PROG_CC_C11 diff --git a/m4/m4_ax_prog_cc_for_build.m4 b/m4/m4_ax_prog_cc_for_build.m4 new file mode 100644 index 0000000..1db8d73 --- /dev/null +++ b/m4/m4_ax_prog_cc_for_build.m4 @@ -0,0 +1,155 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_CC_FOR_BUILD +# +# DESCRIPTION +# +# This macro searches for a C compiler that generates native executables, +# that is a C compiler that surely is not a cross-compiler. This can be +# useful if you have to generate source code at compile-time like for +# example GCC does. +# +# The macro sets the CC_FOR_BUILD and CPP_FOR_BUILD macros to anything +# needed to compile or link (CC_FOR_BUILD) and preprocess (CPP_FOR_BUILD). +# The value of these variables can be overridden by the user by specifying +# a compiler with an environment variable (like you do for standard CC). +# +# It also sets BUILD_EXEEXT and BUILD_OBJEXT to the executable and object +# file extensions for the build platform, and GCC_FOR_BUILD to `yes' if +# the compiler we found is GCC. All these variables but GCC_FOR_BUILD are +# substituted in the Makefile. +# +# LICENSE +# +# Copyright (c) 2008 Paolo Bonzini +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 21 + +AU_ALIAS([AC_PROG_CC_FOR_BUILD], [AX_PROG_CC_FOR_BUILD]) +AC_DEFUN([AX_PROG_CC_FOR_BUILD], [dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_CPP])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl + +dnl Use the standard macros, but make them use other variable names +dnl +pushdef([ac_cv_prog_CPP], ac_cv_build_prog_CPP)dnl +pushdef([ac_cv_prog_cc_c89], ac_cv_build_prog_cc_c89)dnl +pushdef([ac_cv_prog_cc_c99], ac_cv_build_prog_cc_c99)dnl +pushdef([ac_cv_prog_cc_c11], ac_cv_build_prog_cc_c11)dnl +pushdef([ac_cv_prog_gcc], ac_cv_build_prog_gcc)dnl +pushdef([ac_cv_prog_cc_works], ac_cv_build_prog_cc_works)dnl +pushdef([ac_cv_prog_cc_cross], ac_cv_build_prog_cc_cross)dnl +pushdef([ac_cv_prog_cc_g], ac_cv_build_prog_cc_g)dnl +pushdef([ac_cv_c_compiler_gnu], ac_cv_build_c_compiler_gnu)dnl +pushdef([ac_cv_exeext], ac_cv_build_exeext)dnl +pushdef([ac_cv_objext], ac_cv_build_objext)dnl +pushdef([ac_exeext], ac_build_exeext)dnl +pushdef([ac_objext], ac_build_objext)dnl +pushdef([CC], CC_FOR_BUILD)dnl +pushdef([CPP], CPP_FOR_BUILD)dnl +pushdef([GCC], GCC_FOR_BUILD)dnl +pushdef([CFLAGS], CFLAGS_FOR_BUILD)dnl +pushdef([CPPFLAGS], CPPFLAGS_FOR_BUILD)dnl +pushdef([EXEEXT], BUILD_EXEEXT)dnl +pushdef([LDFLAGS], LDFLAGS_FOR_BUILD)dnl +pushdef([OBJEXT], BUILD_OBJEXT)dnl +pushdef([host], build)dnl +pushdef([host_alias], build_alias)dnl +pushdef([host_cpu], build_cpu)dnl +pushdef([host_vendor], build_vendor)dnl +pushdef([host_os], build_os)dnl +pushdef([ac_cv_host], ac_cv_build)dnl +pushdef([ac_cv_host_alias], ac_cv_build_alias)dnl +pushdef([ac_cv_host_cpu], ac_cv_build_cpu)dnl +pushdef([ac_cv_host_vendor], ac_cv_build_vendor)dnl +pushdef([ac_cv_host_os], ac_cv_build_os)dnl +pushdef([ac_tool_prefix], ac_build_tool_prefix)dnl +pushdef([am_cv_CC_dependencies_compiler_type], am_cv_build_CC_dependencies_compiler_type)dnl +pushdef([am_cv_prog_cc_c_o], am_cv_build_prog_cc_c_o)dnl +pushdef([cross_compiling], cross_compiling_build)dnl + +cross_compiling_build=no + +ac_build_tool_prefix= +AS_IF([test -n "$build"], [ac_build_tool_prefix="$build-"], + [test -n "$build_alias"],[ac_build_tool_prefix="$build_alias-"]) + +AC_LANG_PUSH([C]) + +dnl The pushdef([ac_cv_c_compiler_gnu], ...) currently does not cover +dnl the use of this variable in _AC_LANG_COMPILER_GNU called by +dnl AC_PROG_CC. Unset this cache variable temporarily as a workaround. +was_set_c_compiler_gnu=${[ac_cv_c_compiler_gnu]+y} +AS_IF([test ${was_set_c_compiler_gnu}], + [saved_c_compiler_gnu=$[ac_cv_c_compiler_gnu] + AS_UNSET([[ac_cv_c_compiler_gnu]])]) + +AC_PROG_CC + +dnl Restore ac_cv_c_compiler_gnu +AS_IF([test ${was_set_c_compiler_gnu}], + [[ac_cv_c_compiler_gnu]=$[saved_c_compiler_gnu]]) + +_AC_COMPILER_EXEEXT +_AC_COMPILER_OBJEXT +AC_PROG_CPP + +dnl Restore the old definitions +dnl +popdef([cross_compiling])dnl +popdef([am_cv_prog_cc_c_o])dnl +popdef([am_cv_CC_dependencies_compiler_type])dnl +popdef([ac_tool_prefix])dnl +popdef([ac_cv_host_os])dnl +popdef([ac_cv_host_vendor])dnl +popdef([ac_cv_host_cpu])dnl +popdef([ac_cv_host_alias])dnl +popdef([ac_cv_host])dnl +popdef([host_os])dnl +popdef([host_vendor])dnl +popdef([host_cpu])dnl +popdef([host_alias])dnl +popdef([host])dnl +popdef([OBJEXT])dnl +popdef([LDFLAGS])dnl +popdef([EXEEXT])dnl +popdef([CPPFLAGS])dnl +popdef([CFLAGS])dnl +popdef([GCC])dnl +popdef([CPP])dnl +popdef([CC])dnl +popdef([ac_objext])dnl +popdef([ac_exeext])dnl +popdef([ac_cv_objext])dnl +popdef([ac_cv_exeext])dnl +popdef([ac_cv_c_compiler_gnu])dnl +popdef([ac_cv_prog_cc_g])dnl +popdef([ac_cv_prog_cc_cross])dnl +popdef([ac_cv_prog_cc_works])dnl +popdef([ac_cv_prog_cc_c89])dnl +popdef([ac_cv_prog_gcc])dnl +popdef([ac_cv_prog_CPP])dnl + +dnl restore global variables ac_ext, ac_cpp, ac_compile, +dnl ac_link, ac_compiler_gnu (dependant on the current +dnl language after popping): +AC_LANG_POP([C]) + +dnl Finally, set Makefile variables +dnl +AC_SUBST(BUILD_EXEEXT)dnl +AC_SUBST(BUILD_OBJEXT)dnl +AC_SUBST([CFLAGS_FOR_BUILD])dnl +AC_SUBST([CPPFLAGS_FOR_BUILD])dnl +AC_SUBST([LDFLAGS_FOR_BUILD])dnl +]) diff --git a/m4/pkg.m4 b/m4/pkg.m4 new file mode 100644 index 0000000..c5b26b5 --- /dev/null +++ b/m4/pkg.m4 @@ -0,0 +1,214 @@ +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# serial 1 (pkg-config-0.24) +# +# Copyright © 2004 Scott James Remnant . +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) +AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) +AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +# only at the first occurence in configure.ac, so if the first place +# it's called might be skipped (such as if it is within an "if", you +# have to call PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_default([$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes ], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + m4_default([$4], [AC_MSG_ERROR( +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT])[]dnl + ]) +elif test $pkg_failed = untried; then + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])[]dnl + ]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + $3 +fi[]dnl +])# PKG_CHECK_MODULES + + +# PKG_INSTALLDIR(DIRECTORY) +# ------------------------- +# Substitutes the variable pkgconfigdir as the location where a module +# should install pkg-config .pc files. By default the directory is +# $libdir/pkgconfig, but the default can be changed by passing +# DIRECTORY. The user can override through the --with-pkgconfigdir +# parameter. +AC_DEFUN([PKG_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, + [with_pkgconfigdir=]pkg_default) +AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +]) dnl PKG_INSTALLDIR + + +# PKG_NOARCH_INSTALLDIR(DIRECTORY) +# ------------------------- +# Substitutes the variable noarch_pkgconfigdir as the location where a +# module should install arch-independent pkg-config .pc files. By +# default the directory is $datadir/pkgconfig, but the default can be +# changed by passing DIRECTORY. The user can override through the +# --with-noarch-pkgconfigdir parameter. +AC_DEFUN([PKG_NOARCH_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([noarch-pkgconfigdir], + [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, + [with_noarch_pkgconfigdir=]pkg_default) +AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +]) dnl PKG_NOARCH_INSTALLDIR + + +# PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# ------------------------------------------- +# Retrieves the value of the pkg-config variable for the given module. +AC_DEFUN([PKG_CHECK_VAR], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])# PKG_CHECK_VAR diff --git a/make-tag.sh b/make-tag.sh new file mode 100755 index 0000000..acf0dec --- /dev/null +++ b/make-tag.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +if [ -z "$1" ]; then + echo "Usage: $0 2.0pre20" + exit 1 +fi + +SUFF=${0: -4} +if [ "$SUFF" != ".tmp" ]; then + # need to create a temporary version of the script so that + # changing the branch does not destroy it + TMP="/tmp/$0.tmp" + cp $0 $TMP + exec $TMP $@ +fi + +SUBV="$1" +shift +if [ "$1" = "-d" ]; then + DEV_ONLY=1 + shift +fi +#VER="dosemu2-$SUBV" +VER="$SUBV" +REV=`git rev-list HEAD ^dosemu-1.5.0 --count` +echo $SUBV > VERSION +echo $REV >> VERSION +git commit VERSION -m "update version to $SUBV" +if [ $? != 0 ]; then + exit 1 +fi +git tag -f -a $VER-dev -m "tag devel $VER" +if [ -n "$DEV_ONLY" ]; then + exit 0 +fi +git stash +MWT=`git worktree list --porcelain | grep -B 3 "heads/master" | grep worktree \ + |cut -d " " -f 2` +if [ -n "$MWT" ]; then + # unfortunately git does not allow checking out the branch that + # has a work-tree elsewhere + cd "$MWT" + git stash +else + git checkout master + if [ $? != 0 ]; then + echo Failure! Undoing... + git tag -d $VER-dev + git reset --h HEAD^ + exit 1 + fi +fi +git merge --no-ff --log -m "merge $SUBV release from devel" devel +git tag -f -a $VER -m "tag release $VER" + +# remove temporary script +rm $0 diff --git a/man/Makefile b/man/Makefile new file mode 100644 index 0000000..53592e1 --- /dev/null +++ b/man/Makefile @@ -0,0 +1,29 @@ +top_builddir=.. +include $(top_builddir)/Makefile.conf + +LANGUAGES = ru +vpath %.1.in $(top_srcdir)/man + +MANPAGES = dosemu.1 dosemu.bin.1 ru/dosemu.1 ru/dosemu.bin.1 + +default all: $(MANPAGES) + +.NOTPARALLEL: $(MANPAGES) +$(MANPAGES): $(top_builddir)/config.status $(wildcard $(top_srcdir)/man/*.1.in) \ + $(wildcard $(top_srcdir)/man/ru/*.1.in) + cd $(top_builddir) && ./config.status + +install: all + $(INSTALL) -d $(DESTDIR)$(mandir)/man1 + $(INSTALL) -m 0644 *.1 $(DESTDIR)$(mandir)/man1 + for i in $(LANGUAGES); do \ + $(INSTALL) -d $(DESTDIR)$(mandir)/$$i/man1; \ + $(INSTALL) -m 0644 $$i/*.1 $(DESTDIR)$(mandir)/$$i/man1; \ + $(INSTALL) -m 0644 $(REALTOPDIR)/man/$$i/*.1 \ + $(DESTDIR)$(mandir)/$$i/man1; \ + done + +clean realclean: + for i in `ls $(top_srcdir)/man/*.1.in $(top_srcdir)/man/ru/*.1.in` ; do \ + rm -f `echo $$i | sed -E -e 's=$(top_srcdir)/man/(.+).in$$=\1='`; \ + done diff --git a/man/dosdebug.1 b/man/dosdebug.1 new file mode 100644 index 0000000..1e27982 --- /dev/null +++ b/man/dosdebug.1 @@ -0,0 +1 @@ +.so man1/dosemu.bin.1 diff --git a/man/dosemu.1.in b/man/dosemu.1.in new file mode 100644 index 0000000..d53cea0 --- /dev/null +++ b/man/dosemu.1.in @@ -0,0 +1,124 @@ +.\" -*- nroff -*- (This is for Emacs) +.TH DOSEMU 1 "@RELEASE_DATE@" "Version @PACKAGE_VERSION@" "DOS Emulation" +.SH NAME +dosemu \- run DOS and DOS programs under Linux +.SH SYNOPSIS +.B dosemu +[ +.B \-dumb +] +[ +.B \-input +.I keystroke-string +] +[ +.B \-s +] +[ +.I env1=val1 +.I ... +] +[ +.I unix_path_to_dos_prog +[ +.I -- +] +[ +.I dos_cmd_args +] +] +.PP +.SH DESCRIPTION +.B dosemu +is the wrapper script for +.B dosemu.bin +(1) +which invokes the Linux dos emulator, also known as +.BR DOSEMU . +.br + +.SH OPTIONS +.TP +.I args +any number of options described in +.BR dosemu.bin +(1), please refer to this man page for further details. +However, take care to quote and escape correctly so bash doesn't +mess up arguments containing blanks and backslashes. +.TP +.I -dumb +use `dumb' terminal mode. This will only work for DOS applications, which do +plain text output to stdout, but has the advantage that (A) the output of +the DOS application stacks up in your xterm scroll buffer and (B) you can +redirect it to a file such as +.TP + $ dosemu \-dumb \-exec dir > listing + +Note that DOSEMU command.com's commandline editor/history will also work +here, however, editing is restricted to BACKSPACE'ing. +.TP +.I -input +Do simulated keystrokes as given by +.I keystroke-string +just after DOS has booted. This can be used to autolaunch applications, +bypass any needed boot menus or something similar. For details on the format +of the string look at README.txt. +.TP +.I -s +Invoke dosemu via sudo. This is necessary to get access to certain I/O ports +and to get graphics on the Linux console. Please refer to the documentation +(INSTALL and README.txt) to see which files need to be adjusted before +attempting this. +.TP +.I --version +print version of dosemu and list of available options. +.SH AUTHOR +.B DOSEMU +(comprised of the files +.B dosemu.bin +and +.B dosemu +) is based on version 0.4 of the original program written by Matthias Lautner +(no current address that I know of). +.PP +Robert Sanders was maintaining and enhancing +the incarnation of +.B DOSEMU +with which this man page was originally distributed. During about 4 years +James B. MacLean was the restless leader of the +dosemu team, implementation of DPMI (which made Windows-3.1, dos4gw, djgpp, +e.t.c running) happened during his 'governement' and brought the project +near to Beta-state. Hans Lermen took over and released the +first 1.0 version. Now Bart Oldeman is maintaining this +funny software. + +.SH FILES +.PD 0 +.TP +.I $HOME/.dosemu/.dosemurc +per-user configuration file +.TP +.I @sysconfdir@/dosemu.conf +or (only if /etc/dosemu.users exists) +.TP +.I /etc/dosemu.conf +systemwide configuration file +.TP +.I $HOME/.dosemu/boot.log +default file for debug and log messages +.TP +.I $HOME/.dosemu/drive_c/ +default directories of the local per user DOSEMU instance +.TP +.I @bindir@ +.TP +.I @datadir@/dosemu/ +.TP +.I @datadir@/dosemu/dosemu\-bin.tgz +.TP +.I @datadir@/dosemu/dosemu\-freedos\-bin.tgz +default systemwide installation, containing binaries and templates +(The paths can be changed during creation of the systemwide installation). + +.SH "SEE ALSO" +.BR dosemu.bin "(1) diff --git a/man/dosemu.bin.1.in b/man/dosemu.bin.1.in new file mode 100644 index 0000000..ebe46a1 --- /dev/null +++ b/man/dosemu.bin.1.in @@ -0,0 +1,677 @@ +.\" -*- nroff -*- (This is for Emacs) +.TH DOSEMU.BIN 1 "@RELEASE_DATE@" "Version @PACKAGE_VERSION@" "DOS Emulation" +.SH NAME +dosemu.bin, dosdebug \- run DOS and DOS programs under Linux +.SH SYNOPSIS +.B dosemu.bin +[ +.B \-234ABCcdKkmNnOpSstVwX +] +[ +.B \-h +] +[ +.B \-F +.I file +] +[ +.B \--Fusers +.I file +] +[ +.B \--Flibdir +.I directory +] +[ +.B \--Fimagedir +.I directory +] +[ +.B \-f +.I file +] +[ +.B \-u +.I confvar +] +[ +.B \-D +.I flags +] +[ +.B \-e +.I size +] +[ +.B \-x +.I size +] +[ +.B \-P +.I file +] +[ +.B \-o +.I file +] +[ +2\> +.I debugfile +] +[ +.B \-I +.I config-options +] +[ +.B \-i +.I \h'-1' input_data +] +[ +.B \-E +.I dos-command +] +[ +.B \-K +.I unix_path[:dos_path] +] +[ +.I env1=val1 +.I ... +] +[ +.I unix_path_to_dos_prog +[ +.I -- +] +[ +.I dos_cmd_args +] +] +.sp +.B dosdebug +.SH DESCRIPTION +.B dosemu.bin +is the binary wrapped by the script +.B dosemu +(1) +which invokes the Linux dos emulator, also known as +.BR DOSEMU . +.br +.I debugfile +is an optional file into which all debugging output will be redirected. +.PP +.B dosdebug +is used to control or debug an already running +.BR DOSEMU . +.PP +Although this program is known as the DOS +.I emulator, +it is actually a virtual machine for DOS, allowing DOS and programs +written for DOS to operate in an environment similar to a standard IBM +PC/AT or compatible with an Intel 80x86 processor in real mode. +.B DOSEMU +provides emulation of such services as BIOS video, disk, keyboard, serial, +and printer; CMOS memory for configuration information; a real time clock; +memory allocation through the XMS 3.0 specification, EMS 4.0 and DPMI 0.9. +.PP +Because +.B DOSEMU +is not really a DOS emulator, a copy of FreeDos is required, which now is +pre-configured and part of the official DOSEMU binary distribution. +In addition any currently available proprietary DOS (such as MS-DOS, PC-DOS, +DR-DOS) can be booted, when properly configured. + +.SH KERNEL REQUIREMENTS +At present, you will need Linux 2.0.28 or above with the SYSV IPC option +(System V Inter-Process Communication facilities, see +.BR ipc (2)) +compiled. SYSV IPC is an option in the configuration setup during a Linux +kernel compile. + +.SH OPTIONS +.TP +.I -2,3,4,5 +choose 286, 386, 486 or 586 processor to simulate. +.TP +.I -A +boot from floppy disk A (does not need to be a real floppy disk, see below) +.TP +.I -B +boot from floppy disk B (does not need to be a real floppy disk, see below) +.TP +.I -C[num] +boot from hard disk C. If num is specified, the appropriate drive will +become C first. For example, -C1 means rename D to C, then boot from it. +.TP +.I -c[d] +use direct Console video (must be at the console, requires special +privileges, and +.I -s +). +.br +.I -cd +detach from current virtual console or tty and attach to the first free +virtual console +.TP +.I -d path +mount host path as an extra DOS drive. Can be specified multiple times. +.TP +.I -E dos-command +Run DOS command when starting DOSEMU. +The linux path to the command can be given with \-K option. +For example +.nf +dosemu -K /home/joeuser/dosemu/freedos/bin -E "xcopy my_file my_dir" +.fi +will run /home/joeuser/dosemu/freedos/bin/xcopy.exe with 2 arguments. +.br +Note: this only works if your autoexec.bat contains "system \-e" +command. +.TP +.I -K unix_path[:dos_path] +Specify unix path for the program running with \-E. +You can omit \-E and specify the full unix path with \-K, but it is +not recommended and may be removed in the future. You need to specify +a directory path to \-K. +.br +If dos_path is specified, then it is used as a current directory when +invoking the command. If unix_path is empty then the drive letter is +taken from dos_path: +.nf +dosemu -K :C:\\\\games\\\\carma -E carma.exe +.fi +This runs carma.exe from C:\\games\\carma +.TP +.I -T[flags] +Sets flags for -E and -K commands. "flags" is the string of the +following chars: + +1: don't terminate DOSEMU after running the command specified either +with -E or -K. + +h: try to load the specified command into UMB (loadhigh). + +If -T is specified without flags, then "1h" flags string is assumed. +.TP +.I -q +Quiet mode. Suppress all DOS output except that of the command specified +with -E or -K switches. +.br +Note: only works in dumb terminal mode currently. +.TP +.I -k[str] +Set keyboard input type. Plain -k or -kr sets RAW console Keyboard +(must be at the console). Use -kt for TTY and -ks for STDIO inputs. +.TP +.I --Fusers +Bypass dosemu.users and take this file instead (only accepted when +.B dosemu.bin +is +.I not +suid-root). +.TP +.I --Flibdir +Bypass the default DOSEMU_LIB_DIR (as maybe defined in dosemu.users) +and use this directory instead. +.TP +.I --Fimagedir +Bypass the default directory for bootdirectory and hdimages (DOSEMU_IMAGE_DIR) +and use this directory instead. +.TP +.I -f +Parse this config-file instead of .dosemurc. If -n is also specified, then +this config file is parsed instead of the primary dosemu.conf. +.TP +.I -n +Bypass (don't use) default user's config .dosemurc. The config specified +with -f is still parsed, but in that case it replaces the main dosemu.conf. +.TP +.I -L +Writes provided to a log. +.TP +.I -I +Parse the string behind +.I -I +with the same syntax as global.conf or .dosemurc such as +.TP + dos ... \-I 'video { mda }' + +This is useful if you just want to override a given +configuration parameter temporary. You also may have a generic configuration +by executing a script such as +.TP + dos ... \-I "`myconf.sh`" + +where +.I myconf.sh +is a script writing the configuration to stdout. If you have an alternate +configuration file besides .dosrc, +.TP + dos ... \-I "`cat myother.conf`" + +will do the job. Note however, that you have to quote the parameter behind +.I -I +because it is expected to be +.I one +argument. +.TP +.I -i input_data +Type +.I input_data +on the emulated keyboard. Use \\r for Enter. Various other special escapes +are available, for example you can insert pauses with "\\p10;" where +10 is a delay in tens of milliseconds. +.TP +.I -D +specify which Debugging messages to allow/suppress +.TP +.I -h +display help. +.TP +.I -H +specify the dosdebug support flags, currently only `1' is is reasonable. +with +.I -H1 +you force dosemu to wait until the dosdebug terminal has connected. Hence to +debug a DOS session from the very beginning you first start dosemu +with \-H1 and then start dosdebug. +DOSEMU will then lock before jumping into the loaded bootsector waiting +for dosdebug to connect. Once connected you are in `stopped' state +and can set breakpoints or single-step through the bootstrap code. +.TP +.I -m +toggle internal mouse-support +.TP +.I -O +use stderr for output of Debugging messages +.TP +.I -o +use this file for output of Debugging messages +.TP +.I -P +copy debugging output to FILE +.TP +.I -S +run using SDL +.TP +.I -s +super-user/full feature mode: enable direct hardware access. +Use this switch if you wish to use graphics on the console, +need direct port access, hardware interrupts or raw network access. +Implies +.I -V +and +.I -k +by default. + +WARNING: THIS SWITCH NEEDS ROOT, SUID-ROOT, OR SUDO. GIVING +DIRECT HARDWARE ACCESS TO DOS PROGRAMS MAY GIVE THESE THE ABILITY +TO LOCK YOUR COMPUTER, OR (IN RARE CASES) EVEN DAMAGE HARDWARE. +ONLY TRUSTED LOCAL USERS SHOULD BE GIVEN THIS TYPE OF ACCESS. +.TP +.I -t[de[flag]] +terminal mode: run using the S-Lang library by default, or, if 'd' +is specified - use "dumb" terminal mode. If 'de' is specified, +use stderr in dumb terminal mode, except for the output of the +program started with -E that still goes to stdout. If 'de2' is +specified (flag==2), then also the output of -E-started program +goes to stderr. +.TP +.I -V +use direct console video with VGA graphics and use the native graphics +card's BIOS (must be at the console, requires special privileges, and +.I -s +). +.TP +.I -Y +trace DOS boot ([Y/N] prompt for config.sys lines) +.TP +.I -w +toggle windowed/fullscreen mode in X +.TP +.I -X +run in an X Window +.PD 1 +.SH HARD DISKS +.B DOSEMU +supports four methods of supplying DOS with hard disks: +.IP 1. +a virtual disk file residing on a Linux filesystem which emulates a hard +drive. +.IP 2. +direct access to a DOS partition through a raw disk device (i.e. /dev/hda, +/dev/hdb, /dev/sdX). +.IP 3. +direct access to an DOS partition through single partition access +(i.e. /dev/hda1, /dev/hdb2, /dev/sdxx). +.IP 4. +access to a Linux filesystem as a "network" drive using the driver emufs.sys +supplied with +.B DOSEMU +in commands/emufs.sys or lredir.exe. +.PP +Configuration of +.B DOSEMU's +hard disk resources is done by editing +.B dosemu.conf +or +.B .dosemurc +before running +.BR DOSEMU . +Look at @docdir@/README.txt. + +.SH FLOPPY DISKS +.B DOSEMU +supports two methods of supplying DOS with floppy disks: +.IP 1. +a virtual disk file residing on a Linux filesystem which emulates a floppy +drive +.IP 2. +direct access to a physical floppy through a raw disk device (i.e. /dev/fd0, +/dev/fd1). +.PP +This is also explained more thoroughly in +.B README.txt. +.PP +Configuration of +.B DOSEMU's +floppy disk resources is done by editing the +.B dosemu.conf +before running +.BR DOSEMU . + +.SH VIDEO +.B DOSEMU +may be run on any tty device. However, increased performance and functionality +may be had by taking advantage of special features of the Linux console. +Those running +.B DOSEMU +on the console may wish to investigate the +.I \-c, +.I \-k, +and +.I \-V +switches, explained more thoroughly in +.B README.txt. +There is also some very brief documentation in the file dosemu.conf, +which can be edited for your needs. +.PP +In brief, proper use of the console device and the corresponding switches +allows the user to view a DOS program in its original color and font, +with none of the periodic screen update problems with the generic tty +output code. + +.SH KEYBOARD +Those using +.B DOSEMU +on the Linux console may also wish to use the RAW +keyboard support. This mode of operation, selected by the +.I \-k +switch, provides the user with access to the entire keyboard accessible +under DOS. Any combination of ALT, CTRL, and SHIFT keys may be used to +generate the odd keycodes expected by many DOS programs. + +.SH PRINTING +The BIOS printer services are emulated through standard UNIX file I/O +though temporary files which are then periodically spooled by LPR +or a different print client, as defined by $_printer_commands in dosemu.conf. + +.SH DEBUG MESSAGES +Debug messages can be controlled either at the command line or in the +configuration file. Take a look at the documentation inside the config.dist +file included with +.B DOSEMU +in the examples subdirectory, for debugging +options. At the command line, you may specify which classes of messages +you wish +.B dos +to allow. The syntax of this is +.B DOSEMU +takes an option "\-D FLAGS", where FLAGS is a string of letters +which specify which options to print or suppress. +.B DOSEMU +parses this string from left to right. + + + turns the following options on (initial state) + \- turns the following options off + a turns all the options on/off, depending on flag + 0 turns all options off + 1-9 sets the debug level, the higher, the more output + # where # is a letter from the valid class list, + turns that option off/on depending on the + +/\- state. + +.I Message Classes: + + d disk R disk read W disk write + D int 21h C cdrom v video + X X support k keyboard i port I/O + s serial m mouse # default ints + p printer g general c configuration + w warning h hardware I IPC + E EMS x XMS M DPMI + n IPX network P Pkt-driver S SOUND + r PIC T IO-tracing Z PCI-BIOS + A ASPI driver Q mapping driver F MMIO + +Any debugging classes following a +.I \+ +character, up to a +.I \- +character, will be turned on (non-suppressed). Any after a +.I \- +character, up to a +.I \+ +character, will be suppressed. The character +.I a +acts like a string of all possible debugging classes, so +.I \+a +turns on all debugging messages, and +.I \-a +turns off all debugging messages. The characters +.I 0 +and +.I 1-9 +are also special: +.I 0 +turns off all debugging messages, and +.I 1-9 +turns on all debugging messages, but set the debug level too. + +There is an assumed +.I \+ +at the beginning of the FLAGS string. +Some classes, such as error, can not be turned off. +In case you didn't redirect stderr, nearly all output to stderr goes to +.B /dev/null. + +Some examples: + "\-D+a\-v" or "\-D1\-v" : all messages but video + "\-D+kd" : default + keyboard and disk + "\-D0+RW" : only disk READ and WRITE + +Any option letter can occur in any place. Even pointless combinations, +such as +.I -D01-a-1+0, +will be parsed without error, so be careful. +Some options are set by default, some are clear. This is subject to my +whim, and will probably change between releases. You can ensure +which are set by always explicitly specifying them. + +.SH SPECIAL KEYS +In RAW keyboard mode (see the +.BR \-k +option), +.B DOSEMU +responds to certain key sequences as control functions. +.PP +.PD 0 +.IP +ctrl-scrlock = show 0x32 int vectors +.IP +alt-scrlock = show the vm86 registers +.IP +rshift-scrlock = generate an int8 (timer) +.IP +lshift-scrlock = generate an int9 (keyboard) +.IP +ctrl-break = ctrl-break as under DOS. +.IP +ctrl-alt-pgup = reboot DOS. Don't trust this! +.IP +ctrl-alt-pgdn = exit the emulator +.PD 1 +.PP +Use -- to switch to another virtual +console. + +.SH MEMORY +The XMS memory support in +.B DOSEMU +conforms to Lotus/Intel/Microsoft/AST extended +memory specification 3.0. I have implemented all XMS functions except +function 0x12 (Reallocate Upper Memory Block). +.PP +.B DOSEMU +also supports EMS 4.0 and implements DPMI 0.9 (1.0 partially). + + +.SH AUTHOR +.B DOSEMU +(comprised of the files +.B dosemu.bin +and +.B dosemu +) is based on version 0.4 of the original program written by Matthias Lautner +(no current address that I know of). +.PP +Robert Sanders was maintaining and enhancing +the incarnation of +.B DOSEMU +with which this man page was originally distributed. During about 4 years +James B. MacLean was the restless leader of the +dosemu team, implementation of DPMI (which made Windows-3.1, dos4gw, djgpp, +etc. running) happened during his 'governement' and brought the project +near to Beta-state. Hans Lermen took over and released the +first 1.0 version. Now Bart Oldeman is maintaining this +funny software. + +.SH BUGS +There are too many to count, much less list. +.PP +Please report bugs to the author. +I'd also like to hear about which programs DO work. Just send me a note +detailing what program (and what version) you are using, what works and +what doesn't, etc. + +.SH AVAILABILITY +The most recent public version of +.B DOSEMU +can be obtained from www.dosemu.org; a fast mirror is at +ibiblio.unc.edu:/pub/Linux/system/emulators/dosemu/. +If you want to keep up on private developer pre-releases, join the +.B DOSEMU +developer team - even just good detailed debug reports are all you need! + +.SH FILES +.PD 0 +.TP +.I @bindir@/dosemu.bin +The binary +.TP +.I @bindir@/dosemu +The wrapper script, it is recommended not to invoke dosemu.bin directly. +.TP +.I @bindir@/xdosemu +Same, but invoking DOS in an X window. +.TP +.I $HOME/.dosemu +Per user +.B DOSEMU +local directory. This will be created silently, if not +existing. +.TP +.I $HOME/.dosemu/tmp +All temporary file creation happens here, we do not use /tmp anymore. +.TP +.I /var/run/dosemu.* +or +.TP +.I $HOME/.dosemu/run +Various files used by +.B DOSEMU +including debugger pipes. +.TP +.TP +.I $HOME/dosemu/freedos +Bootdirectory containing the FreeDos part. +.TP +.I @sysconfdir@/dosemu.conf +or (only if /etc/dosemu.users exists) +.I /etc/dosemu.conf +Main configuration file for +.BR DOSEMU . +which is included by +.I global.conf +(global.conf is included in dosemu.bin by default). +.TP +.I $HOME/.dosemurc +Per-user configuration file. +.TP +.I /etc/dosemu.users +or +.TP +.I @sysconfdir@/dosemu.users +For suid-root or sudo running binaries: Defines the access rights to +.BR DOSEMU +on a per user basis and sets some vital configuration. This is the only +fix-location configuration file, +.BR DOSEMU +first looks for +.I /etc/dosemu.users +and, if this is not found, for +.I @sysconfdir@/dosemu.users . +Via the keyword +.I default_lib_dir= +in +.I dosemu.users +the systemwide +.I DOSEMU_LIB_DIR +directory may be moved elsewhere. +For more information see +.I @docdir@/README.txt +.TP +.I @sysconfdir@/dos.ini +IPX configuration file. + +.TP +.I doc/DANG.txt +To help you hack +.B DOSEMU +code. +.TP +.I @docdir@/README.* +Various documentation. +.TP +.I README and INSTALL +To set up +.B DOSEMU +quickly. +.TP +.I ChangeLog +Changes in +.B DOSEMU +since the last release. +.TP +.I @docdir@/README.bindist +Information on how to use the DOSEMU/FreeDos ready-to-use binary +distribution. +.TP +.I MSDOS mailing list +For more information, mail to +.IP linux-msdos@vger.kernel.org + + + +.SH "SEE ALSO" +.BR dosemu "(1), " mkfatimage16 "(1)" diff --git a/man/mkfatimage16.1 b/man/mkfatimage16.1 new file mode 100644 index 0000000..2dee1de --- /dev/null +++ b/man/mkfatimage16.1 @@ -0,0 +1,137 @@ +.\" -*- nroff -*- (This is for Emacs) +.TH MKFATIMAGE16 1 "September, 1998" "Version ALPHA 0.98" "Make HDIMAGE for DOSEMU" +.SH NAME +mkfatimage16 \- generate a virtual drive image suitable for DOSEMU +.SH SYNOPSIS +.B mkfatimage16 +[ +.B \-b bsectfile +] +[{ +.B [\-t tracks] +.B [\-h heads] +| +.B \-k Kbytes +}] +[ +.B \-l volume-label +] +[ +.B \-f outfile +] +[ +.B \-p +] +[ +.B file... +] +.SH DESCRIPTION +.B mkfatimage16 +creates a hdimage file for +.BR DOSEMU +that is pre-loaded with the files specified on the command line. +The output is either written to +.I stdout +(hence do not forget to append\ "\ >\ hdimagefile", else you will see +garbage on the screen) or to the file specified by the +.B \-f +option. For the latter you may also use option +.B \-p +in order to force padding up to the given size. This padding will result +in so-called holes on an ext2-FS, hence the actual disk usage will not be +greater. +The file created by mkfatimage16 then can be used as a virtual drive, when defined in +.I /etc/dosemu.conf. +As long as +.B \-k +is not given, the number of heads defaults to 4 and you have 17 sectors per track +else it is adjusted accordingly. +To vary the size, you may either use the +.B \-t +/ +.B \-h +options or specify the total amount of Kbytes via +.B \-k +option. + +All files given behind the options will be copied onto the +hdimage. In addition a +.BR DOSEMU +suitable master boot record (MBR) is established and via option +.B \-b +you may specify a boot sector that gets inserted as first sector +of the partition. To later access the hdimage outside of +.BR DOSEMU +you should use +.BR mtools +(/etc/mtools.conf parameters +.BR partition=1 " and " offset=128 ). + + + +.SH OPTIONS +.TP +.I \-b file +Insert the first 512 bytes of +.I file +into the bootsector of the partition. +.TP +.I \-t num +Make the virtual disk have +.I num +tracks. +.TP +.I \-h num +Make the virtual disk have +.I num +heads. Using tracks and heads is one way to define the size of the disk. +.TP +.I \-k Kbytes +Make the virtual disk be +.I Kbytes +in size. Using +.I \-t|\-h +and +.I \-k +are mutual exclusive. +.TP +.I \-l label +insert +.I label +as volume label for the disk. +.TP +.I \-f outfile +The hdimage is written to +.I outfile +instead of +.I stdout +.TP +.I \-p +Pad the hdimage with zero up to the total size given by +.I \-t|\-h +or +.I \-k +(only in conjunction with +.IR \-f ). + + + +.SH AUTHOR +Pasi Eronen (pe@iki.fi) and Peter Wainwright. + +.SH BUGS +This program doesn't support name mangling and does very little checking +for non-DOS filenames. +Disk full condition isn't detected (and probably causes +erratic behaviour). +Duplicate files aren't detected. + +.SH AVAILABILITY +Comes with +.B DOSEMU + +.SH "SEE ALSO" +.BR dosemu "(1), " xdosemu "(1), " mtools "(1)" + + + diff --git a/man/ru/dosdebug.1 b/man/ru/dosdebug.1 new file mode 100644 index 0000000..1e27982 --- /dev/null +++ b/man/ru/dosdebug.1 @@ -0,0 +1 @@ +.so man1/dosemu.bin.1 diff --git a/man/ru/dosemu.1.in b/man/ru/dosemu.1.in new file mode 100644 index 0000000..9314f2c --- /dev/null +++ b/man/ru/dosemu.1.in @@ -0,0 +1,134 @@ +.\" -*- nroff -*- (This is for Emacs) +.TH DOSEMU 1 "@RELEASE_DATE@" "Версия @PACKAGE_VERSION@" "Эмуляция DOS" +.SH ИМЯ +dosemu \- запускает DOS и приложения DOS под Linux +.SH СИНТАКСИС +.B dosemu +[ +.B \-dumb +] +[ +.B \-home +] +[ +.B \-input +.I строка_клавиш +] +[ +.I env1=val1 +.I ... +] +[ +.I unix_путь_к_дос_программе +[ +.I -- +] +[ +.I аргументы_дос_команды +] +.PP +.SH ОПИСАНИЕ +.B dosemu +является оберточным скриптом для +.B dosemu.bin +(1), +который обращается к Linux эмулятору dos, также известному как +.BR DOSEMU . +Оберточный скрипт также заботится (событийно) об установке частного варианта +.BR DOSEMU +в домашнем каталоге пользователя, если он там отсутствует. +.br + +.SH ОПЦИИ +.TP +.I аргументы +любое число опций, описанных в +.BR dosemu.bin +(1), пожалуйста, обратитесь к его man руководству для дальнейших подробностей. +Однако, сохраняйте осторожность и корректно экранируйте специальные символы и +берите в кавычки, чтобы bash не портил аргументы, содержащие пустые символы +и обратные дроби. +.TP +.I -dumb +использовать режим `dumb' терминала. Работает только для тех приложений DOS, +которые выводят простой текст на стандартный вывод, но имеет преимущество, что +(А) вывод приложения DOS накапливается в буфере прокрутки xterm и (Б) можно +перенаправить его в файл, типа как +.TP + $ dosemu \-dumb \-exec dir > listing + +Заметьте, что редактор/история командной строки DOSEMU command.com будет также +работать и здесь, однако, редактирование ограничено по части BACKSPACE. +.TP +.I -home +в дополнение к устройству C:, позволяет пользователям получить доступ к их +домашнему каталогу как устройству D:. +Заметьте, однако, что большинство файловых имен Unix будут искаженными. +.TP +.I -input +Симулировать нажатия клавиш по данной +.I строке_клавиш +только после загрузки DOS. Эта опция может использоваться, чтобы автостартовать +приложения, проходя любые необходимые меню загрузки или что-либо подобное. Для +подробностей о формате строки смотрите файл README.txt. Заметьте, однако, +что ядро FreeDos "съедает" одно нажатие клавиши при загрузке в счет ожидания +нажатия на клавишу F8. Так в этом случае всегда необходимо добавлять символ +"\\r" в начало строки_клавиш. Как побочный эффект это даст проход ожидания +"F8", и FreeDos будет загружаться быстрее ;-) + +.SH АВТОРЫ +.B DOSEMU +(содержащий в себе файлы +.B dosemu.bin +и +.B dosemu +) базируется на версии 0.4 оригинальной программы, написанной Matthias Lautner +(текущий адрес которого на данный момент неизвестен). +.PP +Robert Sanders поддерживал и расширял +воплощение +.B DOSEMU, +с которым начало распространяться это man руководство. Втечение около 4-х +лет James B. MacLean был неустанным лидером команды +dosemu, реализация DPMI (которая позволила запускать Windows-3.1, dos4gw, +djgpp и прочее) произошла втечение его 'правления' и перенесла проект ближе к +состоянию Beta. Hans Lermen работал над новой версией и +выпустил первый релиз версии 1.0. Сейчас Bart Oldeman +является основным разработчиком этого прекрасного программного обеспечения. +Перевод выполнен Andy Shevchenko . + +.SH ФАЙЛЫ +.PD 0 +.TP +.I $HOME/.dosemu/.dosemurc +настроечный файл на каждого пользователя +.TP +.I @sysconfdir@/dosemu.conf +или +.TP +.I /etc/dosemu.conf +общесистемный настроечный файл +.TP +.I $HOME/.dosemu/boot.log +файл по умолчанию для сообщений отладки и отчета +.TP +.I $HOME/dosemu/bin/ +.TP +.I $HOME/dosemu/freedos/ +.TP +.I $HOME/dosemu/Xfonts/ +каталоги по умолчанию локального варианта установки DOSEMU на каждого +пользователя +.TP +.I @bindir@ +.TP +.I @datadir@/dosemu/ +.TP +.I @datadir@/dosemu/dosemu-bin.tgz +.TP +.I @datadir@/dosemu/dosemu-freedos-bin.tgz +общесистемная установка по умолчанию, включающая в себя бинарные файлы и +шаблоны (Пути могут быть изменены втечение создания общесистемной установки). + +.SH "СМОТРИ ТАКЖЕ" +.BR dosemu.bin "(1) diff --git a/man/ru/dosemu.bin.1.in b/man/ru/dosemu.bin.1.in new file mode 100644 index 0000000..4d3e529 --- /dev/null +++ b/man/ru/dosemu.bin.1.in @@ -0,0 +1,648 @@ +.\" -*- nroff -*- (This is for Emacs) +.TH DOSEMU.BIN 1 "@RELEASE_DATE@" "Версия @PACKAGE_VERSION@" "Эмуляция DOS" +.SH ИМЯ +dosemu.bin, dosdebug \- запускает DOS и приложения DOS под Linux +.SH СИНТАКСИС +.B dosemu.bin +[ +.B \-ABCcdkVNXtsgKm234OU +] +[ +.B \-h +] +[ +.B \-F +.I файл +] +[ +.B \--Fusers +.I файл +] +[ +.B \--Flibdir +.I каталог +] +[ +.B \--Fimagedir +.I каталог +] +[ +.B \-f +.I файл +] +[ +.B \-u +.I переменная_настройки +] +[ +.B \-D +.I флаги +] +[ +.B \-e +.I объем +] +[ +.B \-x +.I объем +] +[ +.B \-P +.I файл +] +[ +.B \-o +.I файл +] +[ +2\> +.I файл_отладки +] +[ +.B \-I +.I опции_настройки +] +[ +.B \-E +.I dos-команда +] +[ +.B \-K +.I unix_путь[:dos_путь] +] +[ +.I env1=val1 +.I ... +] +[ +.I unix_путь_к_дос_программе +[ +.I -- +] +[ +.I аргументы_дос_команды +] +.sp +.B dosdebug +.SH ОПИСАНИЕ +.B dosemu.bin +является бинарным файлом, который запускается скриптом +.B dosemu +(1), +активизирующим эмулятор DOS под Linux, также известный как +.BR DOSEMU . +.br +.I Файл_отладки +представляет собой опциональный файл, куда будет перенаправляться вся отладочная информация. +.PP +.B dosdebug +используется для управления или отладки уже запущенного +.BR DOSEMU . +.PP +Хотя эта программа известна как +.I эмулятор +DOS, она в действительноти является виртуальной машиной для DOS, позволяющей +оперировать DOS и программами, написанными для DOS, в окружении, подобном +стандартному IBM PC/AT компьютеру или совместимому с процессором Intel 80x86 +в реальном режиме. +.B DOSEMU +предоставляет эмуляцию таких сервисов как видео подсистемы, дисковой, +клавиатурной, последовательного порта и подсистемы принтера, доступных через +BIOS; памяти CMOS для настроечной информации; часов реального времени; +работу с памятью через спецификации XMS 3.0, EMS 4.0 и DPMI 0.9. +.PP +Поскольку +.B DOSEMU +не является действительным эмулятором DOS, требуется копия FreeDos, которая +сейчас преднастроена и включена как часть официального бинарного дистрибутива +DOSEMU. +В дополнение, любой доступный сейчас проприетарный DOS (такой как MS-DOS, +PC-DOS, DR-DOS) может быть запущен, при настройке должным образом. + +.SH ТРЕБОВАНИЯ К ЯДРУ +На текущий момент времени, необходимо иметь Linux 2.0.28 или выше с +вкомпилированной опцией SYSV IPC (System V InterProcess Communication +facilities, смотрите +.BR ipc (2)). +Опция SYSV IPC включается в окне настройки ядра Linux в процессе его +компиляции. + +.SH ОПИЦИИ +.TP +.I -A +загрузка с дисковода A (не требует наличия реального дисковода, смотрите ниже) +.TP +.I -B +загрузка с дисковода B (не требует наличия реального дисковода, смотрите ниже) +.TP +.I -C[num] +загрузка с диска C. Если указан num, то соответствующий диск сначала +становится диском C. Например, -C1 означает переименовать диск D в C, +а потом с него загрузиться. +.TP +.I -c[d] +использвать видео консоль напрямую (должно зыпускаться из консоли, +требуется, чтобы +.B dosemu +был запущен с опцией -s) +.br +.I -cd +отсоединиться от текущей виртуальной консоли или tty устройства и +присоединиться к первой свободной виртуальной консоли +.TP +.I -d path +монтировать путь path как дополнительный диск в DOS. +Можно указывать более одного раза. +.TP +.I -E dos-команда +Запуск команды DOS в DOSEMU. +Путь к бинарнику можно указать с помощью опции \-K. +Например, +.nf +dosemu -K /home/joeuser/dosemu/freedos/bin -E "xcopy my_file my_dir" +.fi +запустит /home/joeuser/dosemu/freedos/bin/xcopy.exe с 2 аргументами. +.br +Замечание: это только работает с дефолтным autoexec.bat, в котором +есть вызов "system \-e". +.TP +.I -K unix_путь[:dos_путь] +Указать путь unix к программе, запускаемой через \-E. +Можно не использовать \-E, а указать полный путь через \-K, но это не +рекомендуется, и может быть удалено в будущем. Указывать в \-K надо +только путь к каталогу. +.br +Если указан dos_путь, тогда он используется в качестве директории запуска +dos-программы. Если unix_путь не указан, то буква диска берётся из dos_пути: +.nf +dosemu -K :C:\\\\games\\\\carma -E carma.exe +.fi +Так запустится carma.exe из C:\\games\\carma +.TP +.I -V +использовать VGA спефицичную оптимизацию видео +.TP +.I -k +использовать прозрачную клавиатуру консоли (должно запускаться из консоли) +.TP +.I --Fusers +Обойти файл dosemu.users и взять предоставленный файл вместо него +(принимается только в случае, когда +.B dosemu.bin +.I не +имеет флага suid-root). +.TP +.I --Flibdir +Обойти каталог по умолчанию DOSEMU_LIB_DIR (как, возможно, определено в +dosemu.users) и использовать предоставленный каталог вместо него +(принимается только в случае, когда +.B dosemu.bin +.I не +имеет флага suid-root). +.TP +.I --Fimagedir +Обойти каталог по умолчанию для загрузочного и каталога образов диска +(DOSEMU_IMAGE_DIR) и использовать предоставленный каталог вместо него +(принимается только в случае, когда +.B dosemu.bin +.I не +имеет флага suid-root). +.TP +.I -f +Разобрать предоставленный конфигурационный файл вместо .dosemurc. Если +задействована опция -n, то указанный здесь конфигурационный файл будет +использован вместо основного dosemu.conf. +.TP +.I -n +Пропустить (не разбирать) пользовательский файл конфигурации .dosemurc. +Конфигурационный файл, указанный в -f, будет разобран, но, в этом случае, +он заменит основной конфиг dosemu.conf. +.TP +.I -L +Записывает в лог-файл. +.TP +.I -I +Обработать строку, следующую за опцией. +.I -I +работает с таким же синтаксисом, как и global.conf или .dosemurc, например, +.TP + dos ... \-I 'video { mda }' + +Эта опция полезна, если необходимо временно заменить предоставленный +настроечный параметр. Также можно использовать общую настройку путем запуска +скрипта как показано ниже +.TP + dos ... \-I "`myconf.sh`" + +где +.I myconf.sh +является скриптом, выдающим настройку на стандартный вывод. Если имеется +альтернативный файл настроек помимо .dosrc, то команда +.TP + dos ... \-I "`cat myother.conf`" + +его будет использовать. Однако заметьте, что необходимо взять в кавычки +параметр за +.I -I, +потому что ожидается +.I один +аргумент. +.TP +.I -i input_data +Вводит +.I input_data +с эмулируемой клавиатуры. Можно использовать \\r в качестве Enter. +Так же обрабатываются другие эскейп-последовательности, например +можно вставлять задержки с помощью конструкций вида "\\p10;", где +10 - величина задержки в десятках миллисекунд. +.TP +.I -D +определяет какие отладочные сообщения позволить, а какие запретить +.TP +.I -h +вывести справку. +.TP +.I -H +определяет флаги, поддерживаемые dosdebug. В настоящий момент только `1' +имеет смысл. С +.I -H1 +происходит принуждение dosemu к ожиданию, пока подсоединен dosdebug терминал. +Отсюда, чтобы отладить DOS сессию с самого начала, необходимо вначале запустить +dosemu с параметром \-H1, а затем запустить dosdebug. +DOSEMU будет затем заблокирован перед переходом в загруженный boot сектор в +ожидании соединения dosdebug. Однажды присоединившись, программа находится +в состоянии `остановлена' и можно установить точки останова или пройтись +пошагово через загрузочный код. +.TP +.I -O +использовать стандартный поток ошибок для вывода отладочных сообщений +.TP +.I -o +использовать предоставленный файл для вывода отладочных сообщений в него +.TP +.I -m +разрешить внутреннюю поддержку мыши +.TP +.I -P +копировать отладочный вывод в предоставленный файл +.TP +.I -2,3,4 +выбрать 286, 386 или 486 процессор (Будьте осторожны! +.B DOSEMU +не является полностью 32-хбитным, +так что при определении программой 386 или 486 процессора и использовании +32-хбитных регистров, возможно сбивание ее с толку из-за функций BIOS. +Если предполагается, что такое случается, используйте \-2 для принужденного +перехода +.B DOSEMU +в режим 286.) +.TP + +.PD 1 +.SH ЖЕСТКИЕ ДИСКИ +.B DOSEMU +поддерживает четыре метода замещения DOS с жесткими дисками: +.IP 1. +виртуальный файл диска, находящийся на файловой системе Linux, который +эмулирует жесткий диск. +.IP 2. +непосредственный доступ к разделу DOS через прозрачное дисковое устройство +(например, /dev/hda, /dev/hdb, /dev/sdX). +.IP 3. +непосредственный доступ к разделу DOS через доступ к единичному разделу +(к примеру, /dev/hda1, /dev/hdb2, /dev/sdxx). Необходимо запустить программу +.B mkpartition +для разрешения +.B DOSEMU +доступа к разделу DOS с SPA (Single Partition Access). +.IP 4. +доступ к файловой системе Linux как к "сетевому" устройству, используя +драйвер emufs.sys, поставляемый с +.B DOSEMU +как commands/emufs.sys. +.PP +Настройка ресурсов жесткого диска +.B DOSEMU +выполняется путем редактирования файла +.B dosemu.conf +перед запуском +.BR DOSEMU . +Смотрите также @docdir@/README.txt. + +.SH ФЛОППИ ДИСКИ +.B DOSEMU +поддерживает два метода замещения DOS с флоппи дисками: +.IP 1. +виртуальный файл диска, находящийся на файловой системе Linux, эмулирующий +флоппи дисковод +.IP 2. +непосредственный доступ к физическому флоппи дисководу через прозрачное +дисковое устройство (к примеру, /dev/fd0, /dev/fd1). +.PP +Настройка ресурсов флоппи диска +.B DOSEMU +выполняется путем редактирования файла +.B dosemu.conf +перед запуском +.BR DOSEMU . + +.SH ВИДЕО +.B DOSEMU +может запускаться на любом tty устройстве. Однако, увеличенная +производительность и функциональность может быть получена с применением +дополнительных особенностей консоли Linux. +Так, для запускаемого в консоли +.B DOSEMU +желательно исследовать ключи +.I \-c, +.I \-k, +и +.I \-V. +Также некоторая укороченная документация находится в файле dosemu.conf, +который можно редактировать, если это необходимо. +.PP +Вкратце, правильное использование консольного устройства и соответствующих +ключей позволяет пользователю работать с DOS программой в оригинальном +цвете и с оригинальным шрифтом, без проблем переодического обновления экрана +при кодах вывода на базовое устройство tty. + +.SH КЛАВИАТУРА +Те, кто желает применять +.B DOSEMU +в консоли Linux, могут также воспользоваться прямым доступом к клавиатуре. +Такой режим действия, выбранный ключом +.I \-k, +предоставляет пользователю доступ ко всей клавиатуре прямо из-под DOS. +Любые комбинации клавиш ALT, CTRL и SHIFT могут использоваться +для генерации необычных кодов клавиш, ожидаемых многими программами. + +.SH ПЕЧАТЬ +Сервисы печати BIOS эмулирутся через стандартный I/O доступ к файлам UNIX +через временные файлы, которые периодически опрашиваются LPR либо другим +клиентом печати как указано в параметре $_printer в файле dosemu.conf. + +.SH ОТЛАДОЧНЫЕ СООБЩЕНИЯ +Отладочные сообщения могут контроллироваться как в командной строке, так и +в настроечном файле. Просмотрите документацию, находящуюся внутри файла +config.dist, включённого в подкаталог примеров +.B DOSEMU, +для информации об опциях отладки. В командной строке можно определить +какие классы сообщений будут доступны +.B dos. +Синтаксис тут передаётся +.B DOSEMU +через опцию "\-D FLAGS", где FLAGS представляет собой строку букв, которые +определяют какие опции будут печатать на экран, а какие подавляться. +.B DOSEMU +разбирает эту строку справа налево. + + + включает следующие опции (начальное состояние) + \- выключает следующие опции + a включает или выключает все опции в зависимости от флага + 0 выключает все опции + 1-9 устанавливает уровень отладки: чем выше, тем больше выводится + # где # - это буква из списка верного класса, + включает или выключает опцию в зависимости от состояния +/\-. + +.I Классы Сообщений: + + d диск R чтение с диска W запись на диск + D int 21h C компакт дисковод v видео + X поддержка X k клавиатура i порты I/O + s послед. порты m мышь # прерывания + p принтер g базовые c настройка + w предупреждения h оборудование I IPC + E EMS x XMS M DPMI + n сеть IPX P пакетный драйвер S ЗВУК + r PIC T трассировка IO Z PCI-BIOS + A драйвер ASPI Q mapping driver + +Любые классы отладки, следующие после символа +.I \+ +до символа +.I \-, +будут включены (неподавляющиеся). Любые, следующие после символа +.I \- +до символа +.I \+, +будут подавлены. Символ +.I a +действует подобно строке со всеми возможными классами отладки, так +.I \+a +включит все отладочные сообщения и +.I \-a +выключит все отладочные сообщения. Символы +.I 0 +и +.I 1-9 +также специального назначения: +.I 0 +выключает все отладочные сообщения, а +.I 1-9 +включает все отладочные сообщения, но также устанавливает и уровень отладки. + +Символ +.I \+ +приписывается в начало строки FLAGS. +Некоторые классы, типа класса ошибок, не могут быть выключаны. +В случае, если перенаправление на стандартный поток ошибок нежелательно, +ближайший путь для достижения результата - перенаправить весь +вывод в устройство +.B /dev/null. + +Несколько примеров: + "\-D+a\-v" или "\-D1\-v" : все сообщения кроме видео + "\-D+kd" : по умолчанию вместе с клавиатурой и диском + "\-D0+RW" : только чтение с диска и запись на диск + +Любая опциональная буква может располагаться в любом месте. Даже +бессмысленные комбинации, типа +.I -D01-a-1+0, +будут разобраны без ошибки, так что будьте осторожны. +Некоторые опции установлены по умолчанию, некоторые наоборот. +Это предмет авторского каприза и возможны изменения между версиями. +Можно обеспечить четкую установку конкретных опций, всегда явно определяя их. + +.SH СПЕЦИАЛЬНЫЕ КЛАВИШИ +В режиме работы с клавиатурой напрямую (смотрите опцию +.BR \-k), +.B DOSEMU +возвращает известные последовательности клавиш как управляющие функции. +.PP +.PD 0 +.IP +ctrl-scrlock = показать 0x32 векторов прерываний +.IP +alt-scrlock = показать регистры vm86 +.IP +rshift-scrlock = генерировать int8 (таймер) +.IP +lshift-scrlock = генерировать int9 (клавиатура) +.IP +ctrl-break = ctrl-break как под DOS. +.IP +ctrl-alt-pgup = перегрузить DOS. Не доверяйте этому! +.IP +ctrl-alt-pgdn = выйти из эмулятора +.PD 1 +.PP +Используйте --<Функциональная клавиша>, чтобы +переключиться на другую виртуальную консоль. + +.SH ПАМЯТЬ +Поддержка памяти XMS в +.B DOSEMU +соответствует расширенной спецификации памяти Lotus/Intel/Microsoft/AST +версии 3.0. Реализованы все функции XMS кроме функции 0x12 +(Перераспределить Верхний Блок Памяти). +.PP +В то время как идет реализация функций UMB, они крайне глупые и +будут почти всегда действовать неоптимально. Очередная версия +.B DOSEMU +будет иметь разумную поддержку UMB. +.PP +.B DOSEMU +также поддерживает EMS 4.0 и DPMI 0.9 (1.0 частично). + + +.SH АВТОРЫ +.B DOSEMU +(содержащий в себе файлы +.B dosemu.bin +и +.B dosemu +) базируется на версии 0.4 оригинальной программы, написанной Matthias Lautner +(текущий адрес которого на данный момент неизвестен). +.PP +Robert Sanders поддерживал и расширял +воплощение +.B DOSEMU, +с которым начало распространяться это man руководство. Втечение около 4-х +лет James B. MacLean был неустанным лидером команды +dosemu, реализация DPMI (которая позволила запускать Windows-3.1, dos4gw, +djgpp и прочее) произошла втечение его 'правления' и перенесла проект ближе к +состоянию Beta. Hans Lermen работал над новой версией и +выпустил первый релиз версии 1.0. Сейчас Bart Oldeman +является основным разработчиком этого прекрасного программного обеспечения. +Перевод выполнен Andy Shevchenko . + +.SH ОШИБКИ +Их число довольно велико, лучше свести список до минимума. +.PP +Пожалуйста, отсылайте отчеты об ошибках автору. +Также автору нравиться слышать о тех программах, которые работают. +Присылайте только подробную заметку о том, какая программа (и какой версии) +используется, что работает, а что нет и тому подобное. + +.SH ДОСТУПНОСТЬ +Большинство последних версий +.B DOSEMU +можно получить с сайта www.dosemu.org; скоростное зеркало расположено по +адресу ibiblio.unc.edu:/pub/Linux/system/emulators/dosemu/. +Если хотите получать частные версии для разработчиков, вступайте в команду +разработчиков +.B DOSEMU, +всего лишь хорошие и подробные отладочные отчеты - всё, что необходимо! + +.SH ФАЙЛЫ +.PD 0 +.TP +.I @bindir@/dosemu.bin +Исполняемый модуль +.TP +.I @bindir@/dosemu +Оберточный скрипт, рекомендуемый для запуска dosemu.bin, который не стоит +запускать непосредственно. +.TP +.I @bindir@/xdosemu +То же самое, но служит для запуска DOS в X window. +.TP +.I $HOME/.dosemu +Локальный каталог на каждого пользователя +.B DOSEMU. +Создание каталога происходит молча, если он отсутствует. +.TP +.I $HOME/.dosemu/tmp +Создание всех временных файлов происходит здесь, /tmp больше не используется. +.TP +.I /var/run/dosemu.* +или +.TP +.I $HOME/.dosemu/run +Различные файлы, используемые +.B DOSEMU, +включая отладочные каналы. +.TP +.TP +.I $HOME/dosemu/freedos +Загрузочный каталог, содержащий FreeDos. +.TP +.I dosemu.conf +Основной настроечный файл для +.BR DOSEMU, +включающийся в +.I global.conf +(global.conf включен в dosemu.bin по умолчанию). +.TP +.I dosemu.users +или +.TP +.I @sysconfdir@/dosemu.users +Для запускаемых бинарников с suid-root: определение прав доступа к +.BR DOSEMU +на базисе каждого пользователя и установка некоторой необходимой +конфигурации. Этот настроечный файл имеет только +фиксированное расположение, +.BR DOSEMU +вначале ищет +.I dosemu.users +и, если он не найден, обращается к +.I @sysconfdir@/dosemu.users. +Через ключевой параметр +.I default_lib_dir= +в файле +.I dosemu.users +системно расширяющий каталог +.I DOSEMU_LIB_DIR +может быть перемещен куда-нибудь. +За более детальной информацией обращайтесь к +.I @docdir@/README.txt +.TP +.I @sysconfdir@/dos.ini +Настроечный файл для IPX. + +.TP +.I @docdir@/DPR +Команда разработчиков dosemu: кто и какую часть проекта +.B DOSEMU +ведет? +.TP +.I doc/DANG.txt +Помощь для изучения кода +.B DOSEMU. +.TP +.I @docdir@/README.* +Различная документация. +.TP +.I ChangeLog +Изменения в +.B DOSEMU +с момента последнего релиза. +.TP +.I README.bindist +Информация о том, как использовать готовый к работе пакет бинарников +DOSEMU/FreeDos. +.TP +.I README.distributors +Информация для дистрибьютеров Linux о том, как паковать сборку +.B DOSEMU, +для расширения возможностей системы. +.TP +.I Список рассылки MSDOS +Для подробной информации пишите на адрес +.IP linux-msdos@vger.kernel.org + + + +.SH "СМОТРИ ТАКЖЕ" +.BR dosemu "(1), " mkfatimage16 "(1)" diff --git a/man/ru/mkfatimage16.1 b/man/ru/mkfatimage16.1 new file mode 100644 index 0000000..7d96993 --- /dev/null +++ b/man/ru/mkfatimage16.1 @@ -0,0 +1,136 @@ +.\" -*- nroff -*- (This is for Emacs) +.TH MKFATIMAGE16 1 "Сентябрь 1998" "Версия ALPHA 0.98" "Создание HDIMAGE для DOSEMU" +.SH ИМЯ +mkfatimage16 \- создает виртуальный образ диска, в удобном для DOSEMU формате +.SH СИНТАКСИС +.B mkfatimage16 +[ +.B \-b файл_загрузочного_сектора +] +[{ +.B \-t дорожки +| +.B \-k КБайты +}] +[ +.B \-l метка_тома +] +[ +.B \-f выходной_файл +] +[ +.B \-p +] +[ +.B файл... +] +.SH ОПИСАНИЕ +.B mkfatimage16 +создает файл образа жесткого диска для +.BR DOSEMU, +который служит для начальной загрузки вместе с файлами, определенными в +командной строке. +Вывод результата записывается либо на +.I стандартный_вывод +( здесь не забывайте добавлять " > hdimagefile", иначе будет загрязнен +экран ошибочными данными), либо в файл, определенный опцией +.B \-f. +Далее можно также использовать опцию +.B \-p +в порядке принуждения дополнения файла до заданной длины. Это дополнение +даст результат в виде так называемых "дыр" на файловой системе ext2, поэтому +действительное использование диска не будет увеличиваться. +Файл, созданный mkfatimage16, затем можно использовать как виртуальное +устройство, если он будет определен в +.I /etc/dosemu.conf. +Если опция +.B \-k +не представлена, количество головок всегда равно 4 и на каждую головку +приходится по 17 секторов, иначе необходимо соответственно подстраивать +эти значения. +Чтобы варьировать размером, можно либо использовать опцию +.B \-t, +либо определить общее количество КБайт памяти опцией +.B \-k . + +Все файлы, следующие за опциями, скопируются на образ диска. В +дополнение, введена удобная для +.BR DOSEMU +основная загрузочная запись (MBR), а посредством опции +.B \-b +можно задать загрузочный сектор, который установится как первый сектор раздела. +Для дальнейшего доступа к образу диска извне +.BR DOSEMU +необходимо использовать +.BR mtools +( параметры файла /etc/mtools.conf +.B partition=1 +и +.B offset=128 +). + + + +.SH ОПЦИИ +.TP +.I \-b файл +Копирует первые 512 байт +.I файла +как загрузочный сектор раздела будущего образа диска. +.TP +.I \-t количество +Создает виртуальный диск, имеющий указанное +.I количество +дорожек. Эта опция является единственным путем для определения размера диска. +.TP +.I \-k КБайты +Создать виртуальный диск, заданный объемом +.I KБайт. +Использование опций +.I \-t +и +.I \-k +взаимоисключающее. +.TP +.I \-l метка +Устанавливает +.I метку +как метку тома для диска. +.TP +.I \-f выходной_файл +Образ диск записывается в +.I выходной_файл +вместо +.I стандартного_вывода +.TP +.I \-p +Расширяет образ диска с нуля до полного объема, переданного +опцией +.I \-t +или +.I \-k +(только в связке с опцией +.I \-f +). + + + +.SH АВТОРЫ +Pasi Eronen (pe@iki.fi) и Peter Wainwright. +Перевод выполнен Andy Shevchenko + +.SH ОШИБКИ +Программа не поддерживает искажение имен и обладает очень малым количеством +проверок для файловых имен не DOS типа. +Условия полноты диска не определяется (и возможно вызывает ошибочное +поведение). Дублирующие файлы не обнаруживаются. + +.SH ДОСТУПНОСТЬ +Содержится в пакете +.B DOSEMU + +.SH "СМОТРИ ТАКЖЕ" +.BR dosemu "(1), " xdosemu "(1), " mtools "(1)" + + + diff --git a/man/ru/xdosemu.1 b/man/ru/xdosemu.1 new file mode 100644 index 0000000..b49f0c0 --- /dev/null +++ b/man/ru/xdosemu.1 @@ -0,0 +1 @@ +.so man1/dosemu.1 diff --git a/mkkeytables b/mkkeytables new file mode 100755 index 0000000..af2f3b4 --- /dev/null +++ b/mkkeytables @@ -0,0 +1,43 @@ +#! /usr/bin/perl + +$fprefix = "./etc/keymap/"; +$infile = $fprefix . "all.tmp"; + +`./bin/dos -I 'keytable dump "$infile"'`; + +open(FIN, "<$infile") || die "Can't open $file"; + +$file ="$fprefix${filecount}.txt"; +$open = 0; + +while () { + $line = $_; + $linecount++; + if ( $line =~ /^keytable (\S+)/) { + $ktable = $1; + $file = "$fprefix$ktable"; + # new packet, first close the old one + if ($open) { + close(FOUT); + $open = 0; + } + if ($ktable ne "keyb-user") { + open(FOUT, ">$file"); + $open = 1; + print FOUT "\#keytable ${ktable}\n"; + print FOUT "keytable keyb-user {\n"; + } + } + else { + if ($open) { + if ( $line =~ /^}/ ) {$open = 0;} + print FOUT $line; + } + } +} + +if ($open) { + close(FOUT); +} +`rm -f $infile`; + diff --git a/mknewyear b/mknewyear new file mode 100755 index 0000000..afabccc --- /dev/null +++ b/mknewyear @@ -0,0 +1,64 @@ +#! /usr/bin/perl + +$newyear = "2014"; +$filelistname = ""; + +$i = 0; + +while ($ARGV[$i] ne "") { + if ($ARGV[$i] eq "-y") { + $i++; + if ($ARGV[$i] ne "") { + $newyear = $ARGV[$i]; + $i++; + } + } + if ($ARGV[$i] eq "-f") { + $i++; + if ($ARGV[$i] ne "") { + $filelistname = $ARGV[$i]; + $i++; + } + } +} + +print "building file list ...\n"; +if ($filelistname eq "") { + @files = `grep -l '(C) Copyright 1992,' \`find -type f -perm -u=w\``; +} +else { + @files = `grep -l '(C) Copyright 1992,' \`cat $filelistname\``; +} +print "...Ok, now processing\n"; + +$total = 0; + +#foreach $i (@files) {print "$i";} exit; + +foreach $i (@files) { + chop $i; + $total++; + print "processing $i\n"; + open(FIN, "<$i") || die "Can't open $i"; + open(FOUT, ">$i.new") || die "Can't open $i.new"; + $changed_it = 0; + LINE: while () { + $line = $_; + if (/\(C\) Copyright 1992,[^0-9]+([0-9]+)\s+the\s+\"DOSEMU/) { + $line =~ s/$1/$newyear/; + $changed_it = 1; + } + print FOUT "$line"; + } + close(FOUT); + close(FIN); + if ($changed_it) { + `mv $i.new $i`; + } + else { + `rm -f $i.new`; + $total--; + } +} + +print "$total files converted\n"; diff --git a/plugin_list b/plugin_list new file mode 100644 index 0000000..444db4e --- /dev/null +++ b/plugin_list @@ -0,0 +1,24 @@ +libao +gpm +alsa +ladspa +extra_charsets +term +X +Xkmaps +sdl +midimisc +fluidsynth +munt +ieee1284 +charsets +json +slirp +console +modemu +dosdrv +doscmd +periph +debugger +fdpp +dj64 diff --git a/rerere-train.sh b/rerere-train.sh new file mode 100755 index 0000000..46a2a6d --- /dev/null +++ b/rerere-train.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# Copyright (c) 2008, Nanako Shiraishi +# Prime rerere database from existing merge commits + +me=rerere-train +USAGE="$me rev-list-args" + +SUBDIRECTORY_OK=Yes +OPTIONS_SPEC= +. `git --exec-path`/git-sh-setup +require_work_tree +cd_to_toplevel + +# Remember original branch +branch=$(git symbolic-ref -q HEAD) || +original_HEAD=$(git rev-parse --verify HEAD) || { + echo >&2 "Not on any branch and no commit yet?" + exit 1 +} + +mkdir -p "$GIT_DIR/rr-cache" || exit + +git rev-list --parents "$@" | +while read commit parent1 other_parents +do + if test -z "$other_parents" + then + # Skip non-merges + continue + fi + git checkout -q "$parent1^0" + if git merge $other_parents >/dev/null 2>&1 + then + # Cleanly merges + continue + fi + if test -s "$GIT_DIR/MERGE_RR" + then + git show -s --pretty=format:"Learning from %h %s" "$commit" + git rerere + git checkout -q $commit -- . + git rerere + fi + git reset -q --hard +done + +if test -z "$branch" +then + git checkout "$original_HEAD" +else + git checkout "${branch#refs/heads/}" +fi diff --git a/scripts/aconf.sh b/scripts/aconf.sh new file mode 100755 index 0000000..3a67749 --- /dev/null +++ b/scripts/aconf.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +if [ $# != 2 ]; then + exit 1 +fi +if [ -d "$2/m4" ]; then + AC_F="-I $2/m4 -I $1/m4" +else + AC_F="-I $1/m4" +fi +autoreconf -v $AC_F +exit $? diff --git a/scripts/buildwithfdpp.sh b/scripts/buildwithfdpp.sh new file mode 100755 index 0000000..1b69bcd --- /dev/null +++ b/scripts/buildwithfdpp.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +# Usage: ./buildwithfdpp.sh /path/to/fdpp.git --matchapi + +# Notes: +# 1/ Copy this script to the top directory of the dosemu source, +# as it won't exist in earlier versions and will be removed +# during bisect checkouts. +# 2/ This script has been tested with dosemu2 / fdpp combinations to +# API 26..13 tags okay to configure, build and run FDPP plugin. +# API 12..11 tags okay to configure, build and run but with FS issues +# API <= 10 tags not tested + +if [ ! -d src ] ; then + echo Run this script from the top directory of the dosemu2 source + exit 3 +fi + +FDPP_LIBRARY_SOURCE=$(realpath "$1") +if [ ! -d ${FDPP_LIBRARY_SOURCE} ] ; then + echo Local FDPP git directory \"${FDPP_LIBRARY_SOURCE}\" does\'t exist + exit 2 +fi + +FDPP_PLUGIN_SOURCE=src/plugin/fdpp/fdpp.c +if [ ! -f ${FDPP_PLUGIN_SOURCE} ] ; then + echo FDPP plugin was not implemented at this version of Dosemu2 + exit 1 +fi + +FDPP_PLUGIN_VER=$(awk -F= '/FDPP_API_VER !=/{printf("%d", $2);}' ${FDPP_PLUGIN_SOURCE}) + +if [ -f "${FDPP_LIBRARY_SOURCE}/fdpp/thunks.h" ] ; then + THUNKS="${FDPP_LIBRARY_SOURCE}/fdpp/thunks.h" +else + THUNKS="${FDPP_LIBRARY_SOURCE}/include/fdpp/thunks.h" +fi +FDPP_LIBRARY_VER=$(awk -F" " '/#define FDPP_API_VER/{printf("%d", $3);}' "${THUNKS}") + +if [ "${FDPP_PLUGIN_VER}" != "${FDPP_LIBRARY_VER}" ] ; then + if [ "$2" != "--matchapi" ] ; then + echo FDPP version mismatch in "${FDPP_LIBRARY_SOURCE}" + echo need API"${FDPP_PLUGIN_VER}" but have API"${FDPP_LIBRARY_VER}" + exit 1 + fi + echo Building FDPP at API${FDPP_PLUGIN_VER} + (cd ${FDPP_LIBRARY_SOURCE} && git checkout -f API${FDPP_PLUGIN_VER} && make clean all) +fi + +# make distclean + +if grep -q fdpp-build-path default-configure ; then + CC=clang ./default-configure -d fdpp-build-path=${FDPP_LIBRARY_SOURCE}/fdpp \ + fdpp-include-path=${FDPP_LIBRARY_SOURCE}/include && make +else + [ -d pkgconfig ] || mkdir pkgconfig + sed \ + -e "s,^fdpplibdir=.*$,fdpplibdir=${FDPP_LIBRARY_SOURCE}/fdpp,g" \ + -e "s,^fdppincdir=.*$,fdppincdir=${FDPP_LIBRARY_SOURCE},g" \ + -e "s,^includedir=.*$,includedir=${FDPP_LIBRARY_SOURCE},g" \ + ${FDPP_LIBRARY_SOURCE}/fdpp/fdpp.pc > pkgconfig/fdpp.pc + + env PKG_CONFIG_PATH=`pwd`/pkgconfig CC=clang ./default-configure -d && make +fi diff --git a/scripts/config.guess b/scripts/config.guess new file mode 100755 index 0000000..f50dcdb --- /dev/null +++ b/scripts/config.guess @@ -0,0 +1,1480 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2018 Free Software Foundation, Inc. + +timestamp='2018-02-24' + +# This file 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 . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +# +# Please send patches to . + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2018 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > "$dummy.c" ; + for c in cc gcc c89 c99 ; do + if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "$UNAME_SYSTEM" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval "$set_cc_for_build" + cat <<-EOF > "$dummy.c" + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" + + # If ldd exists, use it to detect musl libc. + if command -v ldd >/dev/null && \ + ldd --version 2>&1 | grep -q ^musl + then + LIBC=musl + fi + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + "/sbin/$sysctl" 2>/dev/null || \ + "/usr/sbin/$sysctl" 2>/dev/null || \ + echo unknown)` + case "$UNAME_MACHINE_ARCH" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` + machine="${arch}${endian}"-unknown + ;; + *) machine="$UNAME_MACHINE_ARCH"-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently (or will in the future) and ABI. + case "$UNAME_MACHINE_ARCH" in + earm*) + os=netbsdelf + ;; + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval "$set_cc_for_build" + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # Determine ABI tags. + case "$UNAME_MACHINE_ARCH" in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "$UNAME_VERSION" in + Debian*) + release='-gnu' + ;; + *) + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "$machine-${os}${release}${abi}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" + exit ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" + exit ;; + *:MidnightBSD:*:*) + echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" + exit ;; + *:ekkoBSD:*:*) + echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" + exit ;; + *:SolidBSD:*:*) + echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd"$UNAME_RELEASE" + exit ;; + *:MirBSD:*:*) + echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" + exit ;; + *:Sortix:*:*) + echo "$UNAME_MACHINE"-unknown-sortix + exit ;; + *:Redox:*:*) + echo "$UNAME_MACHINE"-unknown-redox + exit ;; + mips:OSF1:*.*) + echo mips-dec-osf1 + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE=alpha ;; + "EV4.5 (21064)") + UNAME_MACHINE=alpha ;; + "LCA4 (21066/21068)") + UNAME_MACHINE=alpha ;; + "EV5 (21164)") + UNAME_MACHINE=alphaev5 ;; + "EV5.6 (21164A)") + UNAME_MACHINE=alphaev56 ;; + "EV5.6 (21164PC)") + UNAME_MACHINE=alphapca56 ;; + "EV5.7 (21164PC)") + UNAME_MACHINE=alphapca57 ;; + "EV6 (21264)") + UNAME_MACHINE=alphaev6 ;; + "EV6.7 (21264A)") + UNAME_MACHINE=alphaev67 ;; + "EV6.8CB (21264C)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8AL (21264B)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8CX (21264D)") + UNAME_MACHINE=alphaev68 ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE=alphaev69 ;; + "EV7 (21364)") + UNAME_MACHINE=alphaev7 ;; + "EV7.9 (21364A)") + UNAME_MACHINE=alphaev79 ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo "$UNAME_MACHINE"-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo "$UNAME_MACHINE"-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix"$UNAME_RELEASE" + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux"$UNAME_RELEASE" + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval "$set_cc_for_build" + SUN_ARCH=i386 + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH=x86_64 + fi + fi + echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos"$UNAME_RELEASE" + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos"$UNAME_RELEASE" + ;; + sun4) + echo sparc-sun-sunos"$UNAME_RELEASE" + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos"$UNAME_RELEASE" + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint"$UNAME_RELEASE" + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint"$UNAME_RELEASE" + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint"$UNAME_RELEASE" + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint"$UNAME_RELEASE" + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint"$UNAME_RELEASE" + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint"$UNAME_RELEASE" + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten"$UNAME_RELEASE" + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten"$UNAME_RELEASE" + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix"$UNAME_RELEASE" + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix"$UNAME_RELEASE" + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix"$UNAME_RELEASE" + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval "$set_cc_for_build" + sed 's/^ //' << EOF > "$dummy.c" +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos"$UNAME_RELEASE" + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] + then + if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ + [ "$TARGET_BINARY_INTERFACE"x = x ] + then + echo m88k-dg-dgux"$UNAME_RELEASE" + else + echo m88k-dg-dguxbcs"$UNAME_RELEASE" + fi + else + echo i586-dg-dgux"$UNAME_RELEASE" + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" + fi + echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval "$set_cc_for_build" + sed 's/^ //' << EOF > "$dummy.c" + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/lslpp ] ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + else + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" + fi + echo "$IBM_ARCH"-ibm-aix"$IBM_REV" + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` + case "$UNAME_MACHINE" in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "$sc_cpu_version" in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "$sc_kernel_bits" in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "$HP_ARCH" = "" ]; then + eval "$set_cc_for_build" + sed 's/^ //' << EOF > "$dummy.c" + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ "$HP_ARCH" = hppa2.0w ] + then + eval "$set_cc_for_build" + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH=hppa2.0w + else + HP_ARCH=hppa64 + fi + fi + echo "$HP_ARCH"-hp-hpux"$HPUX_REV" + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux"$HPUX_REV" + exit ;; + 3050*:HI-UX:*:*) + eval "$set_cc_for_build" + sed 's/^ //' << EOF > "$dummy.c" + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo "$UNAME_MACHINE"-unknown-osf1mk + else + echo "$UNAME_MACHINE"-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi"$UNAME_RELEASE" + exit ;; + *:BSD/OS:*:*) + echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case "$UNAME_PROCESSOR" in + amd64) + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; + esac + echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" + exit ;; + i*:CYGWIN*:*) + echo "$UNAME_MACHINE"-pc-cygwin + exit ;; + *:MINGW64*:*) + echo "$UNAME_MACHINE"-pc-mingw64 + exit ;; + *:MINGW*:*) + echo "$UNAME_MACHINE"-pc-mingw32 + exit ;; + *:MSYS*:*) + echo "$UNAME_MACHINE"-pc-msys + exit ;; + i*:PW*:*) + echo "$UNAME_MACHINE"-pc-pw32 + exit ;; + *:Interix*:*) + case "$UNAME_MACHINE" in + x86) + echo i586-pc-interix"$UNAME_RELEASE" + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix"$UNAME_RELEASE" + exit ;; + IA64) + echo ia64-unknown-interix"$UNAME_RELEASE" + exit ;; + esac ;; + i*:UWIN*:*) + echo "$UNAME_MACHINE"-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + *:GNU:*:*) + # the GNU system + echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" + exit ;; + i*86:Minix:*:*) + echo "$UNAME_MACHINE"-pc-minix + exit ;; + aarch64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + arm*:Linux:*:*) + eval "$set_cc_for_build" + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi + else + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + cris:Linux:*:*) + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" + exit ;; + crisv32:Linux:*:*) + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" + exit ;; + e2k:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + frv:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + hexagon:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + i*86:Linux:*:*) + echo "$UNAME_MACHINE"-pc-linux-"$LIBC" + exit ;; + ia64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + k1om:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + m32r*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + m68*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval "$set_cc_for_build" + sed 's/^ //' << EOF > "$dummy.c" + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`" + test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; } + ;; + mips64el:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-"$LIBC" + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-"$LIBC" + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-"$LIBC" + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; + PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; + *) echo hppa-unknown-linux-"$LIBC" ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-"$LIBC" + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-"$LIBC" + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-"$LIBC" + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-"$LIBC" + exit ;; + riscv32:Linux:*:* | riscv64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" + exit ;; + sh64*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + sh*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + tile*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + vax:Linux:*:*) + echo "$UNAME_MACHINE"-dec-linux-"$LIBC" + exit ;; + x86_64:Linux:*:*) + if objdump -f /bin/sh | grep -q elf32-x86-64; then + echo "$UNAME_MACHINE"-pc-linux-"$LIBC"x32 + else + echo "$UNAME_MACHINE"-pc-linux-"$LIBC" + fi + exit ;; + xtensa*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo "$UNAME_MACHINE"-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo "$UNAME_MACHINE"-unknown-stop + exit ;; + i*86:atheos:*:*) + echo "$UNAME_MACHINE"-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo "$UNAME_MACHINE"-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos"$UNAME_RELEASE" + exit ;; + i*86:*DOS:*:*) + echo "$UNAME_MACHINE"-pc-msdosdjgpp + exit ;; + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" + else + echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}" + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" + else + echo "$UNAME_MACHINE"-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configure will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos"$UNAME_RELEASE" + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos"$UNAME_RELEASE" + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos"$UNAME_RELEASE" + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos"$UNAME_RELEASE" + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv"$UNAME_RELEASE" + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo "$UNAME_MACHINE"-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo "$UNAME_MACHINE"-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux"$UNAME_RELEASE" + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv"$UNAME_RELEASE" + else + echo mips-unknown-sysv"$UNAME_RELEASE" + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux"$UNAME_RELEASE" + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux"$UNAME_RELEASE" + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux"$UNAME_RELEASE" + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux"$UNAME_RELEASE" + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux"$UNAME_RELEASE" + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux"$UNAME_RELEASE" + exit ;; + SX-ACE:SUPER-UX:*:*) + echo sxace-nec-superux"$UNAME_RELEASE" + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody"$UNAME_RELEASE" + exit ;; + *:Rhapsody:*:*) + echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + eval "$set_cc_for_build" + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 + fi + echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = x86; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-*:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSR-*:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSV-*:NONSTOP_KERNEL:*:*) + echo nsv-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSX-*:NONSTOP_KERNEL:*:*) + echo nsx-tandem-nsk"$UNAME_RELEASE" + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = 386; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo "$UNAME_MACHINE"-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux"$UNAME_RELEASE" + exit ;; + *:DragonFly:*:*) + echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "$UNAME_MACHINE" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" + exit ;; + i*86:rdos:*:*) + echo "$UNAME_MACHINE"-pc-rdos + exit ;; + i*86:AROS:*:*) + echo "$UNAME_MACHINE"-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo "$UNAME_MACHINE"-unknown-esx + exit ;; + amd64:Isilon\ OneFS:*:*) + echo x86_64-unknown-onefs + exit ;; +esac + +echo "$0: unable to guess system type" >&2 + +case "$UNAME_MACHINE:$UNAME_SYSTEM" in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <&2 </dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-functions 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/scripts/config.sub b/scripts/config.sub new file mode 100755 index 0000000..1d8e98b --- /dev/null +++ b/scripts/config.sub @@ -0,0 +1,1801 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2018 Free Software Foundation, Inc. + +timestamp='2018-02-22' + +# This file 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 . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS + +Canonicalize a configuration name. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2018 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo "$1" + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ + kopensolaris*-gnu* | cloudabi*-eabi* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + android-linux) + os=-linux-android + basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; + *) + basic_machine=`echo "$1" | sed 's/-[^-]*$//'` + if [ "$basic_machine" != "$1" ] + then os=`echo "$1" | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ + | avr | avr32 \ + | ba \ + | be32 | be64 \ + | bfin \ + | c4x | c8051 | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | epiphany \ + | fido | fr30 | frv | ft32 \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia16 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ + | open8 | or1k | or1knd | or32 \ + | pdp10 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pru \ + | pyramid \ + | riscv32 | riscv64 \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | visium \ + | wasm32 \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + leon|leon[3-9]) + basic_machine=sparc-$basic_machine + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65) + ;; + ms1) + basic_machine=mt-unknown + ;; + + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; + xscaleeb) + basic_machine=armeb-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | ba-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | e2k-* | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ + | ip2k-* | iq2000-* \ + | k1om-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | or1k*-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pru-* \ + | pyramid-* \ + | riscv32-* | riscv64-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | visium-* \ + | wasm32-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-pc + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + asmjs) + basic_machine=asmjs-unknown + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2*) + basic_machine=m68k-bull + os=-sysv3 + ;; + e500v[12]) + basic_machine=powerpc-unknown + os=$os"spe" + ;; + e500v[12]-*) + basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` + os=$os"spe" + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; + i*86v32) + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + leon-*|leon[3-9]-*) + basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'` + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'` + os=-linux + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze*) + basic_machine=microblaze-xilinx + ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i686-pc + os=-msys + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + nsv-tandem) + basic_machine=nsv-tandem + ;; + nsx-tandem) + basic_machine=nsx-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc | ppcbe) basic_machine=powerpc-unknown + ;; + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=-rdos + ;; + rdos32) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + strongarm-* | thumb-*) + basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'` + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tile*) + basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + x64) + basic_machine=x86_64-pc + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + xscale-* | xscalee[bl]-*) + basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'` + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases that might get confused + # with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # es1800 is here to avoid being matched by es* (a different OS) + -es1800*) + os=-ose + ;; + # Now accept the basic system types. + # The portable systems comes first. + # Each alternative MUST end in a * to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* | -plan9* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* | -cloudabi* | -sortix* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \ + | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ + | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-musl* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ + | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \ + | -midnightbsd*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -xray | -os68k* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo "$os" | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo "$os" | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo "$os" | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4*) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -pikeos*) + # Until real need of OS specific support for + # particular features comes up, bare metal + # configurations are quite functional. + case $basic_machine in + arm*) + os=-eabi + ;; + *) + os=-elf + ;; + esac + ;; + -nacl*) + ;; + -ios) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + c8051-*) + os=-elf + ;; + hexagon-*) + os=-elf + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + pru-*) + os=-elf + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"` + ;; +esac + +echo "$basic_machine$os" +exit + +# Local variables: +# eval: (add-hook 'write-file-functions 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/scripts/install-sh b/scripts/install-sh new file mode 100755 index 0000000..ec298b5 --- /dev/null +++ b/scripts/install-sh @@ -0,0 +1,541 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2020-11-14.01; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +tab=' ' +nl=' +' +IFS=" $tab$nl" + +# Set DOITPROG to "echo" to test this script. + +doit=${DOITPROG-} +doit_exec=${doit:-exec} + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +# Create dirs (including intermediate dirs) using mode 755. +# This is like GNU 'install' as of coreutils 8.32 (2020). +mkdir_umask=22 + +backupsuffix= +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +is_target_a_directory=possibly + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -p pass -p to $cpprog. + -s $stripprog installed files. + -S SUFFIX attempt to back up existing files, with suffix SUFFIX. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG + +By default, rm is invoked with -f; when overridden with RMPROG, +it's up to you to specify -f if you want it. + +If -S is not specified, no backups are attempted. + +Email bug reports to bug-automake@gnu.org. +Automake home page: https://www.gnu.org/software/automake/ +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -p) cpprog="$cpprog -p";; + + -s) stripcmd=$stripprog;; + + -S) backupsuffix="$2" + shift;; + + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) is_target_a_directory=never;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + # Don't chown directories that already exist. + if test $dstdir_status = 0; then + chowncmd="" + fi + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename. + if test -d "$dst"; then + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dstbase=`basename "$src"` + case $dst in + */) dst=$dst$dstbase;; + *) dst=$dst/$dstbase;; + esac + dstdir_status=0 + else + dstdir=`dirname "$dst"` + test -d "$dstdir" + dstdir_status=$? + fi + fi + + case $dstdir in + */) dstdirslash=$dstdir;; + *) dstdirslash=$dstdir/;; + esac + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + # The $RANDOM variable is not portable (e.g., dash). Use it + # here however when possible just to lower collision chance. + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + + trap ' + ret=$? + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null + exit $ret + ' 0 + + # Because "mkdir -p" follows existing symlinks and we likely work + # directly in world-writeable /tmp, make sure that the '$tmpdir' + # directory is successfully created first before we actually test + # 'mkdir -p'. + if (umask $mkdir_umask && + $mkdirprog $mkdir_mode "$tmpdir" && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + test_tmpdir="$tmpdir/a" + ls_ld_tmpdir=`ls -ld "$test_tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null + fi + trap '' 0;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + oIFS=$IFS + IFS=/ + set -f + set fnord $dstdir + shift + set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=${dstdirslash}_inst.$$_ + rmtmp=${dstdirslash}_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && + { test -z "$stripcmd" || { + # Create $dsttmp read-write so that cp doesn't create it read-only, + # which would cause strip to fail. + if test -z "$doit"; then + : >"$dsttmp" # No need to fork-exec 'touch'. + else + $doit touch "$dsttmp" + fi + } + } && + $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + set +f && + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # If $backupsuffix is set, and the file being installed + # already exists, attempt a backup. Don't worry if it fails, + # e.g., if mv doesn't support -f. + if test -n "$backupsuffix" && test -f "$dst"; then + $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null + fi + + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/scripts/mkpluginhooks b/scripts/mkpluginhooks new file mode 100755 index 0000000..8053275 --- /dev/null +++ b/scripts/mkpluginhooks @@ -0,0 +1,66 @@ +#! /bin/sh + +HEADERS="config" + +TOP=$(realpath `dirname $0`/..) +PLUG=plugin +SRCDIR=$TOP/src/$PLUG +DESTDIR=src/$PLUG +INC=$DESTDIR/include +PREF=plugin_ +HDR=$PREF$HEADERS.hh +SHDR=$PREF$HEADERS.h +CONF=config +CONFIGURE=configure.ac + +PDIRS="$(cd $SRCDIR; find . -maxdepth 1 ! -name include ! -path . -type d -exec basename {} \; | LC_ALL=C sort)" + +gendummy() { + for i in $HEADERS; do + if [ "$1" = "clean" ]; then + rm -f $INC/$HDR + else + printf "" >$INC/$HDR.$$ + fi + done +} + +if [ "$1" = "clean" ]; then + gendummy clean + rm -rf $INC +fi + +if [ "$1" = "clean" ]; then + rm -f $DESTDIR/*/config/plugin_enable + exit 0 +fi + +mkdir -p $INC + +if [ "$1" = "" ]; then gendummy; fi + +if [ ! -d $SRCDIR ]; then + exit 1 +fi + + +if [ "$PDIRS" != " " ]; then + for d in $PDIRS; do + # Test if this plugin is enabled + if [ -f $DESTDIR/$d/$CONF/${PREF}enable ] && \ + [ -f $SRCDIR/$d/Makefile ]; then + enable=`cat $DESTDIR/$d/$CONF/${PREF}enable` + else + enable=no + fi + if [ "$enable" = "yes" ]; then + # Plugin special headers... + for h in $HEADERS; do + if [ -f $SRCDIR/$d/$CONF/$SHDR ]; then + echo "#include \"../$PLUG/$d/$CONF/$SHDR\"" >>$INC/$HDR.$$ + fi + done + fi + done + mv $INC/$HDR.$$ $INC/$HDR +fi diff --git a/scripts/plugctl.sh b/scripts/plugctl.sh new file mode 100755 index 0000000..6e6fde6 --- /dev/null +++ b/scripts/plugctl.sh @@ -0,0 +1,45 @@ +#! /bin/sh + +TOP=$(realpath `dirname $0`/..) +PLUG=plugin +SRCDIR=$TOP/src/$PLUG +DESTDIR=src/$PLUG +CONF=config +CONFIGURE=configure.ac + +dir=$1 +on=$2 +shift +shift +req_on=$on +DSTDIR=$DESTDIR/$dir +if [ "$on" = "yes" -a -f $SRCDIR/$dir/$CONFIGURE ]; then + wd=`pwd` + [ -d $DSTDIR ] || mkdir -p $DSTDIR + cd $DSTDIR + [ -f $CONFIGURE ] || ln -s $SRCDIR/$dir/$CONFIGURE $CONFIGURE + [ -f Makefile.conf.in ] || [ ! -f $SRCDIR/$dir/Makefile.conf.in ] || \ + ln -s $SRCDIR/$dir/Makefile.conf.in Makefile.conf.in + echo "=== configuring in $dir" + trap "echo ; exit 130" INT + ${MAKE} ./configure REALTOPDIR="$TOP" srcdir="$SRCDIR/$dir" + if [ ! -f ./configure ]; then + on="no" + else + eval OPTS=\$${dir}_OPTS + eval echo ./configure $OPTS $* + if ! eval ./configure $OPTS $*; then + echo "Configuration for $dir failed, disabling" + on="no" + fi + fi + trap - INT + cd $wd +fi +if test -d $SRCDIR/$dir ; then + mkdir -p $DSTDIR/$CONF + echo $on > $DSTDIR/$CONF/plugin_enable +fi +if [ "$req_on" != "$on" ]; then + exit 1 +fi diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..70e205f --- /dev/null +++ b/src/Makefile @@ -0,0 +1,11 @@ +# Makefile for DOSEMU +# +# You should do a "make" to compile and a "make install" as root to +# install DOSEMU. +# + +all: default +top_builddir=.. +include $(top_builddir)/Makefile.conf +include $(REALTOPDIR)/src/Makefile.common +include $(REALTOPDIR)/src/arch/linux/Makefile.main diff --git a/src/Makefile.common b/src/Makefile.common new file mode 100644 index 0000000..17c3b5b --- /dev/null +++ b/src/Makefile.common @@ -0,0 +1,2 @@ +include $(REALTOPDIR)/src/Makefile.common.inc +include $(REALTOPDIR)/src/Makefile.common.post diff --git a/src/Makefile.common.inc b/src/Makefile.common.inc new file mode 100644 index 0000000..88e071e --- /dev/null +++ b/src/Makefile.common.inc @@ -0,0 +1,10 @@ +# if building in subdir, SUBDIR is not passed from outside, so evaluate +SUBDIR ?= $(patsubst /%,%,$(subst $(abs_top_builddir)/src,,$(CURDIR))) +LIBNAME := $(subst /,_,$(SUBDIR)) + +vpath %.c $(srcdir) +vpath %.h $(srcdir) +vpath %.S $(srcdir) +vpath %.SS $(srcdir) +vpath %.s $(srcdir) +vpath %.sgml $(srcdir) diff --git a/src/Makefile.common.post b/src/Makefile.common.post new file mode 100644 index 0000000..8a15939 --- /dev/null +++ b/src/Makefile.common.post @@ -0,0 +1,151 @@ +ifndef OBJS +OBJS=$(CFILES:.c=.o) $(SFILES:.S=.o) $(XSFILES:.s=.o) $(SSFILES:.SS=.o) +endif + +GENOBJS = $(GENSRC:.c=.o) + +ifndef DEPENDS +DEPENDS=$(OBJS:.o=.d) +endif + +GENERATED = $(top_builddir)/src/include/version.hh \ + $(top_builddir)/src/plugin/include/plugin_config.hh + +ifneq ($(GENHDRS),) +GENERATED += $(GENHDRS) +ALL_CPPFLAGS += -iquote . +endif + +LIB:=$(BINPATH)/lib/lib$(LIBNAME).a + +AR=ar + +lib: $(LIB) + +$(BINPATH) $(BINPATH)/bin $(BINPATH)/commands $(BINPATH)/lib: + mkdir -p $@ + +$(BINPATH)/bat: | $(BINPATH) + $(LN_SFT) ../src/bindist/bat $@ + +$(top_builddir)/commands: | $(BINPATH)/commands $(BINPATH)/bat + rm -rf $@ + mkdir $@ + mkdir $@/dosemu + $(LN_S) -f ../src/bindist/fdppconf.sys $@/fdppconf.sys + $(LN_S) -f ../src/bindist/fdppauto.bat $@/fdppauto.bat + for i in `ls $(BINPATH)/bat/`; do $(LN_SFT) ../../$(THISVERSION)/bat/$$i $@/dosemu/$$i; done + +$(top_builddir)/bin: | $(BINPATH)/bin + rm -f $@ + $(LN_S) -f $(THISVERSION)/bin $@ + +$(top_builddir)/lib: | $(BINPATH)/lib + rm -f $@ + $(LN_S) -f $(THISVERSION)/lib $@ + +GIT_REV := $(shell $(REALTOPDIR)/git-rev.sh $(REALTOPDIR) $(top_builddir)) +.LOW_RESOLUTION_TIME: $(GIT_REV) +$(top_builddir)/src/include/version.hh: $(top_builddir)/Makefile.conf $(REALTOPDIR)/VERSION $(GIT_REV) + echo "Updating version.h"; \ + echo "#ifndef VERSION_HH" > $@; \ + echo "#define VERSION_HH" >> $@; \ + echo "#define VERSTR \"$(PACKAGE_VERSION)\"" >> $@; \ + echo "#define VERSION_NUM $(VERSION)" >> $@; \ + echo "#define SUBLEVEL $(SUBLEVEL)" >> $@; \ + echo "#define PATCHLEVEL1 \"$(PATCHLEVEL1)\"" >> $@; \ + echo "#define REVISION $(REVISION)" >> $@; \ + echo "#define VERDATE \"$(RELEASE_DATE)\"" >> $@; \ + echo "#endif /* VERSION_H */" >> $@ + +$(LIB): $(OBJS) $(GENOBJS) | $(BINPATH)/lib + rm -f $@ + $(AR) cr $@ $^ + $(RANLIB) $@ + +.PHONY: clean realclean lib echo all install default + +# Use wildcards instead of OBJS and DEPENDS because they may depend on +# a make target. The reason is that on clean we do not include Makefile.conf. +clean:: + -rm -f *.o $(LIB) $(GENSRC) *.d $(GENHDRS) configure *~ + +realclean:: clean + rm -rf autom4te*.cache + rm -f Makefile.conf config.log config.status configure aclocal.m4 + rm -rf plugin/include + +$(OBJS): $(GENERATED) +$(GENOBJS): $(GENSRC) + +%.o: %.c + $(CC) -c $(ALL_CPPFLAGS) $(ALL_CFLAGS) -o $@ $< + +%.o: %.s $(top_builddir)/src/include/version.hh + $(CPP) $(ALL_CPPFLAGS) -x assembler-with-cpp $< | $(XAS) $(XASFLAGS) -o $@ + +VS = $(subst .,_,$(notdir $(1)))_START + +%.o: %.S $(top_builddir)/src/include/version.hh + $(CPP) $(ALL_CPPFLAGS) -x assembler-with-cpp $< | $(XAS) $(XASFLAGS) -o _$@ + $(AS_LD) $(AS_LDFLAGS) -static $(if $($(call VS,$<)), \ + --section-start .text=$($(call VS,$<))) -o $@.elf _$@ + $(XOBJCOPY) -j .text -O binary $@.elf $@.bin + nm -g -n _$@ | gawk '\ + BEGIN {\ + print "// Warning: autogenerated";\ + print "";\ + print "asm(";\ + print "\".data\\n\"";\ + print "\".globl _binary_$(@:.o=)_o_bin_start\\n\"";\ + print "\"_binary_$(@:.o=)_o_bin_start:\\n\"";\ + print "\".incbin \\\"$@.bin\\\"\\n\"";\ + print "\".globl _binary_$(@:.o=)_o_bin_end\\n\"";\ + print "\"_binary_$(@:.o=)_o_bin_end:\\n\"";\ + print "\".previous\\n\"";\ + print ");";\ + print "#include \"$(srcdir)/$(@:.o=)_offsets.h\"";\ + }\ + {\ + HEXSTR = sprintf("0x%s", $$1);\ + ADDR = strtonum(HEXSTR) $(if $($(call VS,$<)),+ $($(call VS,$<)));\ + if ($$3 != "_start") {\ + printf "const unsigned %s = 0x%04x;\n", $$3, ADDR;\ + }\ + }\ + END {\ + print "";\ + }\ + ' > $@_offsets.c + $(CC) -c -o $@ $@_offsets.c + $(RM) _$@ $@.bin $@.elf $@_offsets.c + +%.o: %.SS + $(CPP) $(ALL_CPPFLAGS) -x assembler-with-cpp $< | $(AS) $(ASFLAGS) -o $@ + +ifndef CLEANING +ifneq "$(wildcard *.d)" "" +-include $(DEPENDS) +endif +endif + +$(top_builddir)/Makefile.conf: $(REALTOPDIR)/configure + cd $(top_builddir) && $(REALTOPDIR)/configure +Makefile.conf: configure + ./configure +configure: configure.ac + $(REALTOPDIR)/scripts/aconf.sh $(REALTOPDIR) $(srcdir) +.SECONDARY: configure +$(top_builddir)/src/plugin/include/plugin_config.hh: + cd $(top_builddir) && $(REALTOPDIR)/scripts/mkpluginhooks + +# this is mainly for debugging the makefile +echo:: + @echo REALTOPDIR=$(REALTOPDIR) + @echo TOPDIR=$(TOPDIR) + @echo LIB=$(LIB) + @echo DEPENDS=$(DEPENDS) + @echo OBJS=$(OBJS) + @echo CFILES = $(wildcard *.c) + @echo DEPENDS FOUND= $(wildcard *.d) + @echo MAKEFLAGS=$(MAKEFLAGS) diff --git a/src/Makefile.common.pre b/src/Makefile.common.pre new file mode 100644 index 0000000..5e685f6 --- /dev/null +++ b/src/Makefile.common.pre @@ -0,0 +1,4 @@ +ifeq ($(findstring $(MAKECMDGOALS), configure),) +include $(top_builddir)/Makefile.conf +endif +include $(REALTOPDIR)/src/Makefile.common.inc diff --git a/src/arch/linux/Makefile.main b/src/arch/linux/Makefile.main new file mode 100644 index 0000000..8ea70af --- /dev/null +++ b/src/arch/linux/Makefile.main @@ -0,0 +1,272 @@ +# Makefile for Linux DOSEMU +# +# You should do a "make" to compile and a "make install" as root to +# install DOSEMU. +SYS = $(top_builddir)/$(THISVERSION)/commands +dosemudir = $(datadir)/dosemu +dosemuxdir = $(datadir)/dosemu2-extras +sysdir = $(DESTDIR)$(dosemudir)/$(cmdsuff) +cmddir = $(sysdir)/dosemu +etcdir = $(sysconfdir)/$(confdir) +FONTS = $(wildcard $(REALTOPDIR)/etc/*.bdf) +TFONTS = $(FONTS:$(REALTOPDIR)/etc/%.bdf=$(top_builddir)/etc/Xfonts/%.pcf.gz) + +NET=dosext/net + +# +# This is defined when the SB Emulation is required. You should re-configure, +# rather than just change this. +# +SBEMU=base/dev/sb16 base/dev/sb16/softmpu base/sound + +# +# This is defined when the CPU emulator is required. You should +# re-configure, rather than just change this. +# +ifeq ($(X86_EMULATOR),1) +XCPUEMU = base/emu-i386/simx86 +ifeq ($(USE_SOFTFLOAT),1) +XCPUEMU += base/emu-i386/simx86/softfloat +endif +endif + +ifeq ($(MCONTEXT),1) +LIBMC = base/lib/mcontext +endif + +DPMI = dosext/dpmi dosext/dpmi/msdos dosext/dpmi/doslib +ifeq ($(DNATIVE),1) +DPMI += dosext/dpmi/dnative +endif + +_LIBSUBDIRS=base/video base/dev/vga base/core base/kbd_unicode \ + arch/linux/async base/lib/mapping $(LIBMC) \ + base/misc base/lib/misc base/lib/libpcl base/lib/timer \ + base/dev/misc \ + base/emu-i386 $(XCPUEMU) base/speaker \ + base/dev/pic base/dev/ne2k \ + dosext/mfs dosext/misc dosext/builtins \ + base/init base/serial base/mouse \ + base/dev/dma base/lib/translate \ + $(NET) $(IPX) $(SBEMU) dosext/drivers base/bios base/bios/x86 \ + $(DPMI) + +LIBSUBDIRS=$(_LIBSUBDIRS) $(PLUGINSUBDIRS) +ST_LIBSUBDIRS=$(_LIBSUBDIRS) $(ST_PLUGINSUBDIRS) + +# call all libraries the name of the directory +LIBS_ := ${addsuffix .a,${addprefix $(top_builddir)/lib/lib,$(subst /,_,$(ST_LIBSUBDIRS))}} + +DOCS= $(top_builddir)/man + +################################################################### + +default: $(top_builddir)/src/include/version.hh doslib \ + $(TFONTS) + @echo "" + @echo "---------------------------------DONE compiling-------------------------------" + @echo "" + @echo " Now you must install DOSEMU2 with the following command:" + @echo " sudo make install" + @echo "" + @echo "" + +$(top_builddir)/etc/Xfonts: + mkdir $@ + +$(top_builddir)/etc/Xfonts/%.pcf.gz: $(REALTOPDIR)/etc/%.bdf | $(top_builddir)/etc/Xfonts + bdftopcf $< | gzip -c -9 -n > $@ + +$(LIBS_): | $(top_builddir)/lib + +$(BINPATH)/bin/$(DOSBIN): $(LIBS_) +ifeq ($(OS),Darwin) + $(LD) $(ALL_LDFLAGS) $(DOSBIN_LDFLAGS) -o $@ \ + -Wl,-all_load $(LIBS_) $(LIBS) \ + -Wl,-map,$(BINPATH)/bin/dosemu.map +else + $(LD) $(ALL_LDFLAGS) $(DOSBIN_LDFLAGS) -o $@ \ + -Wl,--whole-archive $(LIBS_) -Wl,--no-whole-archive $(LIBS) \ + -Wl,-Map=$(BINPATH)/bin/dosemu.map +endif + +$(BINPATH)/bin/dosemu: $(SRCPATH)/dosemu.systemwide $(SRCPATH)/dosemu + cp $< $@ + echo >> $@ + echo "LOCAL_BUILD_PATH=$(abs_top_builddir)" >> $@ + echo "LOCAL_BIN_DIR=$(THISVERSION)/bin" >> $@ + echo "prefix=$(prefix)" >> $@ + echo >> $@ + cat $(SRCPATH)/dosemu >> $@ + chmod +x $@ + +bin: $(top_builddir)/bin $(BINPATH)/bin/$(DOSBIN) $(BINPATH)/bin/dosemu + +# This recursive call seems to be necessary to deal with parallel makes. +# Otherwise the rule for $(DOSBIN) would depend on a phony target, and +# this does not seem to be supported by GNU Make. +dosemu: $(LIBSUBDIRS) + @$(MAKE) bin + +DIRLIST = $(LIBSUBDIRS) $(OPTIONALSUBDIRS) +.PHONY: dossubdirs optionalsubdirs dosemu bin +.PHONY: $(DIRLIST) + +optionalsubdirs: $(OPTIONALSUBDIRS) + +$(DIRLIST): $(GENERATED) $(top_builddir)/commands + @($(MAKE) SUBDIR:=$@ -C $@) + +doslib: dosemu + +installnew: doslib + $(MAKE) install + +install: + $(INSTALL) -d $(DESTDIR)$(dosemudir) + rm -rf $(DESTDIR)$(dosemudir)/commands + rm -rf $(DESTDIR)$(dosemudir)/fdboot + rm -rf $(DESTDIR)$(dosemudir)/freedos + rm -rf $(cmddir) + rm -rf $(sysdir) + $(INSTALL) -d $(DESTDIR)$(etcdir) + + $(INSTALL) -d $(sysdir) + for i in `find $(SRCPATH)/bindist -maxdepth 1 -type f`; do \ + $(INSTALL) -m 0644 $$i $(sysdir); \ + done + $(INSTALL) -d $(sysdir)/c + $(INSTALL) -d $(cmddir) + $(INSTALL) -d $(cmddir)/dosrc.d + $(INSTALL) -m 0644 -D $(SRCPATH)/bindist/c/* $(sysdir)/c + $(INSTALL) -m 0644 -D $(SRCPATH)/bindist/bat/*.bat $(cmddir) + $(INSTALL) -m 0644 -D $(SRCPATH)/bindist/bat/dosrc.d/* $(cmddir)/dosrc.d + cp -f -d $(SYS)/* $(cmddir) + $(LN_S) -f $(cmdsuff) $(DESTDIR)$(dosemudir)/commands + if [ ! -f $(DESTDIR)$(etcdir)/dosemu.conf ]; then \ + $(INSTALL) -m 0644 $(REALTOPDIR)/etc/dosemu.conf $(DESTDIR)$(etcdir); \ + fi + $(INSTALL) -d $(DESTDIR)$(dosemudir)/keymap + $(INSTALL) -m 0644 $(REALTOPDIR)/etc/keymap/* $(DESTDIR)$(dosemudir)/keymap + $(INSTALL) -d $(DESTDIR)$(dosemudir)/cpi + $(INSTALL) -m 0644 $(REALTOPDIR)/etc/cpi/* $(DESTDIR)$(dosemudir)/cpi + $(INSTALL) -d $(DESTDIR)$(dosemudir)/icons + $(INSTALL) -m 0644 $(REALTOPDIR)/etc/*.xpm $(DESTDIR)$(dosemudir)/icons + $(INSTALL) -d $(DESTDIR)$(datadir)/applications + $(INSTALL) -m 0644 $(top_builddir)/etc/*.desktop $(DESTDIR)$(datadir)/applications + $(INSTALL) -m 0644 $(REALTOPDIR)/etc/locales.conf $(DESTDIR)$(dosemudir) + $(INSTALL) -d $(DESTDIR)$(bindir) + $(INSTALL) -m 0755 $(top_builddir)/bin/$(DOSBIN) $(DESTDIR)$(bindir) + $(INSTALL) -m 0755 $(top_builddir)/bin/dosemu $(DESTDIR)$(bindir) + [ ! -f $(top_builddir)/bin/mkfatimage16 ] || $(INSTALL) -m 0755 $(top_builddir)/bin/mkfatimage16 $(DESTDIR)$(bindir) + [ ! -f $(top_builddir)/bin/dosdebug ] || $(INSTALL) -m 0755 $(top_builddir)/bin/dosdebug $(DESTDIR)$(bindir) + $(INSTALL) -d $(DESTDIR)$(plugindir) + for i in $(top_builddir)/bin/*.so; do \ + if [ -f $$i ]; then \ + $(INSTALL) -m 0755 $$i $(DESTDIR)$(plugindir); \ + fi; \ + done + $(INSTALL) -d $(DESTDIR)$(docdir) + for i in NEWS.md THANKS BUGS; do \ + $(INSTALL) -m 0644 $(REALTOPDIR)/$$i $(DESTDIR)$(docdir); \ + done + $(INSTALL) -m 0644 $(top_builddir)/changelog $(DESTDIR)$(docdir) + for i in README EMUfailure tweaks; do \ + $(INSTALL) -m 0644 $(REALTOPDIR)/doc/$$i.html $(DESTDIR)$(docdir); \ + done + $(INSTALL) -m 0644 $(REALTOPDIR)/doc/NOVELL-HOWTO.txt $(DESTDIR)$(docdir) + $(INSTALL) -m 0644 $(REALTOPDIR)/doc/README.gdb $(DESTDIR)$(docdir) + echo "-> Installing fonts..." + rm -rf $(DESTDIR)$(ttffontdir) + rm -rf $(DESTDIR)$(x11fontdir) + $(INSTALL) -d $(DESTDIR)$(x11fontdir) + $(INSTALL) -d $(DESTDIR)$(ttffontdir) + install -m 0644 -D $(top_builddir)/etc/Xfonts/* $(DESTDIR)$(x11fontdir) + $(INSTALL) -m 0644 $(REALTOPDIR)/etc/$(PACKAGE_NAME).alias $(DESTDIR)$(x11fontdir)/fonts.alias + ! which mkfontdir >/dev/null || mkfontdir $(DESTDIR)$(x11fontdir) + if [ -d "$(DESTDIR)$(sysconfdir)/X11/fontpath.d" ]; then \ + rm -f $(DESTDIR)$(sysconfdir)/X11/fontpath.d/$(PACKAGE_NAME):unscaled:pri=20; \ + $(LN_S) -f $(x11fontdir) $(DESTDIR)$(sysconfdir)/X11/fontpath.d/$(PACKAGE_NAME):unscaled:pri=20; \ + fi + + install -m 0644 -D $(REALTOPDIR)/etc/ttf/* $(DESTDIR)$(ttffontdir) + + echo "-> Installing man pages..." + $(MAKE) -C $(top_builddir)/man install + + if [ -n "$(fdtarball)" -a -f "$(fdtarball)" ]; then \ + echo "-> Installing FreeDOS..."; \ + $(INSTALL) -d $(DESTDIR)$(dosemuxdir); \ + rm -rf $(DESTDIR)$(dosemuxdir)/freedos; \ + tar -C $(DESTDIR)$(dosemuxdir) --strip-components=1 --no-same-owner -xpzvf $(fdtarball); \ + rm -f $(DESTDIR)$(dosemuxdir)/FDchange.log; \ + rm -rf $(DESTDIR)$(dosemuxdir)/freedos/tmp; \ + fi + if [ -d $(DESTDIR)$(dosemuxdir)/freedos ]; then \ + rm -rf $(DESTDIR)$(dosemuxdir)/fdboot; \ + $(INSTALL) -d $(DESTDIR)$(dosemuxdir)/fdboot; \ + cd $(DESTDIR)$(dosemuxdir)/fdboot; \ + $(LN_S) ../../dosemu/commands/c/fdconfig.sys fdconfig.sys; \ + $(LN_S) ../freedos/kernel.sys kernel.sys; \ + $(LN_S) ../freedos/command.com command.com; \ + fi + + @if test $(etcdir) != /etc; then \ + if [ -f $(DESTDIR)/etc/dosemu.users ]; then \ + echo ; \ + echo /etc/dosemu.users exists but you did not set etcdir=/etc; \ + echo Deprecated: moving to /etc/dosemu/dosemu.users; \ + echo mv /etc/dosemu.users /etc/dosemu/dosemu.users; \ + mv /etc/dosemu.users /etc/dosemu/dosemu.users; \ + fi; \ + if [ -f $(DESTDIR)/etc/dosemu.conf ]; then \ + echo ; \ + echo /etc/dosemu.conf exists but you did not set etcdir=/etc; \ + echo Deprecated: moving to /etc/dosemu/dosemu.conf; \ + echo mv /etc/dosemu.conf /etc/dosemu/dosemu.conf; \ + mv /etc/dosemu.conf /etc/dosemu/dosemu.conf; \ + fi \ + fi + @echo ""; \ + echo "---------------------------------DONE Installing-------------------------------"; \ + echo "" + @echo " - You can type 'dosemu' to run DOSEMU2." + +uninstall: + rm -rf $(DESTDIR)$(dosemudir) + rm -f $(DESTDIR)$(bindir)/$(DOSBIN) + rm -f $(DESTDIR)$(bindir)/dosemu + rm -f $(DESTDIR)$(bindir)/mkfatimage16 + rm -f $(DESTDIR)$(bindir)/mkhdimage + rm -f $(DESTDIR)$(bindir)/dosdebug + rm -rf $(DESTDIR)$(plugindir) + rm -rf $(DESTDIR)$(docdir) + ls $(DOCS)/*.1 | xargs --max-args=1 basename | xargs -I {} --max-args=1 rm -f $(DESTDIR)$(mandir)/man1/{} + ls $(DOCS)/ru/*.1 | xargs --max-args=1 basename | xargs -I {} --max-args=1 rm -f $(DESTDIR)$(mandir)/ru/man1/{} + +local_clean: + rm -rf $(top_builddir)/bin $(top_builddir)/commands $(top_builddir)/lib + +clean:: local_clean + +realclean:: local_clean + rm -rf $(BINPATH) + +CLEANDIRS=$(addsuffix .clean, $(DIRLIST)) +REALCLEANDIRS=$(addsuffix .realclean, $(DIRLIST)) + +clean:: $(CLEANDIRS) + +realclean:: $(REALCLEANDIRS) + +.PHONY: $(CLEANDIRS) +$(CLEANDIRS): + -@$(MAKE) -C $(subst .clean,,$@) clean CLEANING=true + +.PHONY: $(REALCLEANDIRS) +$(REALCLEANDIRS): + -@$(MAKE) -C $(subst .realclean,,$@) realclean CLEANING=true + +pristine: realclean + -rm -rf $(top_builddir)/lib + -rm -rf $(BINPATH) $(top_builddir)/bin $(top_builddir)/commands $(top_builddir)/etc/Xfonts diff --git a/src/arch/linux/async/Makefile b/src/arch/linux/async/Makefile new file mode 100644 index 0000000..11961a4 --- /dev/null +++ b/src/arch/linux/async/Makefile @@ -0,0 +1,11 @@ +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf + +CFILES = signal.c debug.c +ifeq ($(HAVE_LIBBFD),1) +CFILES += backtrace-symbols.c +endif + +include $(REALTOPDIR)/src/Makefile.common + +all: lib diff --git a/src/arch/linux/async/backtrace-symbols.c b/src/arch/linux/async/backtrace-symbols.c new file mode 100644 index 0000000..a40dca7 --- /dev/null +++ b/src/arch/linux/async/backtrace-symbols.c @@ -0,0 +1,329 @@ +/* + A hacky replacement for backtrace_symbols in glibc + + backtrace_symbols in glibc looks up symbols using dladdr which is limited in + the symbols that it sees. libbacktracesymbols opens the executable and shared + libraries using libbfd and will look up backtrace information using the symbol + table and the dwarf line information. + + It may make more sense for this program to use libelf instead of libbfd. + However, I have not investigated that yet. + + Derived from addr2line.c from GNU Binutils by Jeff Muizelaar + + Copyright 2007 Jeff Muizelaar +*/ + +/* addr2line.c -- convert addresses to line number and function name + Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + Contributed by Ulrich Lauther + + This file was part of GNU Binutils. + + 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 2, 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, write to the Free Software + Foundation, 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. */ + +#define fatal(a, b) exit(1) +#define bfd_fatal(a) exit(1) +#define bfd_nonfatal(a) exit(1) +#define list_matching_formats(a) exit(1) + +/* 2 characters for each byte, plus 1 each for 0, x, and NULL */ +#define PTRSTR_LEN (sizeof(void *) * 2 + 3) +#define true 1 +#define false 0 + +#include +#include +#include +#include +#include "execinfo_wrp.h" +/* hack for some versions of bfd.h that require PACKAGE to be defined */ +#ifndef PACKAGE + #define PACKAGE + #ifndef PACKAGE_VERSION + #define PACKAGE_VERSION + #include + #undef PACKAGE_VERSION + #else + #include + #endif + #undef PACKAGE +#else + #ifndef PACKAGE_VERSION + #define PACKAGE_VERSION + #include + #undef PACKAGE_VERSION + #else + #include + #endif +#endif +#include +#include + + +static asymbol **syms; /* Symbol table. */ + +/* 150 isn't special; it's just an arbitrary non-ASCII char value. */ +#define OPTION_DEMANGLER (150) + +static void slurp_symtab(bfd * abfd); +static void find_address_in_section(bfd *abfd, asection *section, void *data); + +/* Read in the symbol table. */ + +static void slurp_symtab(bfd * abfd) +{ + long symcount; + unsigned int size; + + if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) + return; + + symcount = bfd_read_minisymbols(abfd, false, (PTR) & syms, &size); + if (symcount == 0) + symcount = bfd_read_minisymbols(abfd, true /* dynamic */ , + (PTR) & syms, &size); + + if (symcount < 0) + bfd_fatal(bfd_get_filename(abfd)); +} + +/* These global variables are used to pass information between + translate_addresses and find_address_in_section. */ + +static bfd_vma pc; +static const char *filename; +static const char *functionname; +static unsigned int line; +static int found; + +/* Look for an address in a section. This is called via + bfd_map_over_sections. */ + +static void find_address_in_section(bfd *abfd, asection *section, void *data __attribute__ ((__unused__)) ) +{ + bfd_vma vma; + bfd_size_type size; + + if (found) + return; + + if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) + return; + + vma = bfd_get_section_vma(abfd, section); + if (pc < vma) + return; + + size = bfd_section_size(abfd, section); + if (pc >= vma + size) + return; + + found = bfd_find_nearest_line(abfd, section, syms, pc - vma, + &filename, &functionname, &line); +} + +static char** translate_addresses_buf(bfd * abfd, bfd_vma *addr, int naddr) +{ + int naddr_orig = naddr; + char b; + int total = 0; + enum { Count, Print }; + int state; + char *buf = &b; + int len = 0; + char **ret_buf = NULL; + /* iterate over the formatting twice. + * the first time we count how much space we need + * the second time we do the actual printing */ + for (state=Count; state<=Print; state++) { + if (state == Print) { + ret_buf = malloc(total + sizeof(char*)*naddr); + buf = (char*)(ret_buf + naddr); + len = total; + } + while (naddr) { + if (state == Print) + ret_buf[naddr-1] = buf; + pc = addr[naddr-1]; + + found = false; + bfd_map_over_sections(abfd, find_address_in_section, + (PTR) NULL); + + if (!found) { + total += snprintf(buf, len, "[0x%llx] \?\?() \?\?:0",(long long unsigned int) addr[naddr-1]) + 1; + } else { + const char *name; + + name = functionname; + if (name == NULL || *name == '\0') + name = "??"; + if (filename != NULL) { + char *h; + + h = strrchr(filename, '/'); + if (h != NULL) + filename = h + 1; + } + total += snprintf(buf, len, "%s:%u\t%s()", filename ? filename : "??", + line, name) + 1; + + } + if (state == Print) { + /* set buf just past the end of string */ + buf = buf + total + 1; + } + naddr--; + } + naddr = naddr_orig; + } + return ret_buf; +} +/* Process a file. */ + +static char **process_file(const char *file_name, bfd_vma *addr, int naddr) +{ + bfd *abfd; + char **matching; + char **ret_buf; + + abfd = bfd_openr(file_name, NULL); + + if (abfd == NULL) + bfd_fatal(file_name); + + if (bfd_check_format(abfd, bfd_archive)) + fatal("%s: can not get addresses from archive", file_name); + + if (!bfd_check_format_matches(abfd, bfd_object, &matching)) { + bfd_nonfatal(bfd_get_filename(abfd)); + if (bfd_get_error() == + bfd_error_file_ambiguously_recognized) { + list_matching_formats(matching); + free(matching); + } + exit(1); + } + + slurp_symtab(abfd); + + ret_buf = translate_addresses_buf(abfd, addr, naddr); + + if (syms != NULL) { + free(syms); + syms = NULL; + } + + bfd_close(abfd); + return ret_buf; +} + +#define MAX_DEPTH 16 + +struct file_match { + const char *file; + ElfW(Addr) address; + ElfW(Addr) base; +}; + +static int find_matching_file(struct dl_phdr_info *info, + size_t __attribute__((unused)) size, void *data) +{ + struct file_match *match = data; + /* This code is modeled from Gfind_proc_info-lsb.c:callback() from libunwind */ + long n; + const ElfW(Phdr) *phdr; + ElfW(Addr) load_base = info->dlpi_addr; + phdr = info->dlpi_phdr; + for (n = info->dlpi_phnum; --n >= 0; phdr++) { + if (phdr->p_type == PT_LOAD) { + ElfW(Addr) vaddr = phdr->p_vaddr + load_base; + if (match->address >= vaddr && match->address < vaddr + phdr->p_memsz) { + /* we found a match */ + match->file = info->dlpi_name; + match->base = info->dlpi_addr; + } + } + } + return 0; +} + +char **backtrace_symbols(void *const *buffer, int size) +{ + int stack_depth = size - 1; + int x,y; + /* discard calling function */ + int total = 0; + + char ***locations; + char **final; + char *f_strings; + + locations = malloc(sizeof(char**) * (stack_depth+1)); + + bfd_init(); + for(x=stack_depth, y=0; x>=0; x--, y++){ + struct file_match match = { .address = (ElfW(Addr))buffer[x] }; + char **ret_buf; + bfd_vma addr; + dl_iterate_phdr(find_matching_file, &match); + addr = (ElfW(Addr))buffer[x] - match.base; + if (match.file && match.file[0] == '/') + ret_buf = process_file(match.file, &addr, 1); + else + ret_buf = process_file("/proc/self/exe", &addr, 1); + locations[x] = ret_buf; + total += strlen(ret_buf[0]) + 1; + } + + /* allocate the array of char* we are going to return and extra space for + * all of the strings */ + final = malloc(total + (stack_depth + 1) * sizeof(char*)); + /* get a pointer to the extra space */ + f_strings = (char*)(final + stack_depth + 1); + + /* fill in all of strings and pointers */ + for(x=stack_depth; x>=0; x--){ + strcpy(f_strings, locations[x][0]); + free(locations[x]); + final[x] = f_strings; + f_strings += strlen(f_strings) + 1; + } + + free(locations); + + return final; +} + +void +backtrace_symbols_fd(void *const *buffer, int size, int fd) +{ + int j; + char **strings; + + strings = backtrace_symbols(buffer, size); + if (strings == NULL) { + perror("backtrace_symbols"); + exit(EXIT_FAILURE); + } + + for (j = 0; j < size; j++) { + write(fd, strings[j], strlen(strings[j])); + write(fd, "\n", 1); + } + + free(strings); +} diff --git a/src/arch/linux/async/debug.c b/src/arch/linux/async/debug.c new file mode 100644 index 0000000..9162ecb --- /dev/null +++ b/src/arch/linux/async/debug.c @@ -0,0 +1,260 @@ +#include "emu.h" +#include "dosemu_config.h" +#include "debug.h" +#include "emudpmi.h" +#include "cpu-emu.h" +#include "sig.h" +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif +#include "execinfo_wrp.h" + +static FILE *gdb_f = NULL; + +static void gdb_command(const char *cmd) +{ + printf("%s", cmd); + fflush(stdout); + fprintf(gdb_f, "%s", cmd); + fflush(gdb_f); +} + +static int start_gdb(pid_t dosemu_pid) +{ + char *buf; + int ret; + + printf("Debug info:\n"); + fflush(stdout); + +#ifdef __APPLE__ + ret = asprintf(&buf, "lldb %s", dosemu_proc_self_exe); +#else + ret = asprintf(&buf, "gdb --readnow %s", dosemu_proc_self_exe); +#endif + assert(ret != -1); + + printf("%s", buf); + putchar('\n'); + fflush(stdout); + + if (!(gdb_f = popen(buf, "w"))) { + free(buf); + return 0; + } + free(buf); + + ret = asprintf(&buf, "attach %i\n", dosemu_pid); + assert(ret != -1); + + gdb_command(buf); + free(buf); + + return 1; +} + +static void do_debug(void) +{ +#ifdef __APPLE__ + const char *cmd1 = "register read\n"; +// const char *cmd2 = "bt\n"; + const char *cmd3 = "thread backtrace all\n"; +#else + const char *cmd1 = "info registers\n"; +// const char *cmd2 = "backtrace\n"; + const char *cmd3 = "thread apply all backtrace full\n"; +#endif + + gdb_command(cmd1); +// gdb_command(cmd2); + gdb_command(cmd3); +} + +static int stop_gdb(void) +{ + const char *cmd1 = "detach\n"; + const char *cmd2 = "quit\n"; + int status; + + gdb_command(cmd1); + gdb_command(cmd2); + wait(&status); + pclose(gdb_f); + putchar('\n'); + fflush(stdout); + return !WEXITSTATUS(status); +} + +/* disable as this crashes under DPMI trying to trace through DOS stack */ +/* ... and re-enable because people fuck up instead of installing gdb. + * But run this only if the gdb trace fails. */ +#ifdef HAVE_BACKTRACE +/* Obtain a backtrace and print it to `stdout'. + (derived from 'info libc') + */ +static void print_trace (void) +{ +#define MAX_FRAMES 256 + void *array[MAX_FRAMES]; + int size; + char **strings; + size_t i; + + size = backtrace (array, MAX_FRAMES); + strings = backtrace_symbols (array, size); + fprintf(dbg_fd, "Obtained %d stack frames.\n", size); + + for (i = 0; i < size; i++) + fprintf(dbg_fd, "%s\n", strings[i]); + + free (strings); + fprintf(dbg_fd, "Backtrace finished\n"); +} +#endif + +static void collect_info(pid_t pid) +{ + const char *cmd0 = "ldd %s"; + const char *cmd1 = "getconf GNU_LIBC_VERSION"; + const char *cmd2 = "getconf GNU_LIBPTHREAD_VERSION"; + const char *cmd3 = "cat /proc/%i/maps"; + char *tmp; + int ret; + + printf("System info:\n"); + fflush(stdout); + + ret = asprintf(&tmp, cmd0, dosemu_proc_self_exe); + assert(ret != -1); + + if(system(tmp)) { + printf("command '%s' failed\n", tmp); + } + free(tmp); + + if(system(cmd1)) { + printf("command '%s' failed\n", cmd1); + } + + if(system(cmd2)) { + printf("command '%s' failed\n", cmd2); + } + + ret = asprintf(&tmp, cmd3, pid); + assert(ret != -1); + + if(system(tmp)) { + printf("command '%s' failed\n", tmp); + } + free(tmp); + + fflush(stdout); +} + +static int do_gdb_debug(void) +{ + int ret = 0; + pid_t dosemu_pid = getpid(); + pid_t dbg_pid; + int status; + sigset_t oset; + + if (getuid() != geteuid()) + return 0; + +#ifdef __linux__ + prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY); +#endif + sigprocmask(SIG_BLOCK, &q_mask, &oset); + switch ((dbg_pid = fork())) { + case 0: + signal_done(); + sigprocmask(SIG_SETMASK, &oset, NULL); + + dup2(fileno(dbg_fd), STDOUT_FILENO); + dup2(fileno(dbg_fd), STDERR_FILENO); + + collect_info(dosemu_pid); + + if (!start_gdb(dosemu_pid)) + _exit(1); + do_debug(); + if (!stop_gdb()) + _exit(1); + _exit(0); + break; + case -1: + error("fork failed, %s\n", strerror(errno)); + break; + default: + waitpid(dbg_pid, &status, 0); + if (WEXITSTATUS(status)) { + dbug_printf("backtrace failure\n"); + } else { + ret = 1; + dbug_printf("done backtrace\n"); + } + break; + } + sigprocmask(SIG_SETMASK, &oset, NULL); + return ret; +} + +void gdb_debug(void) +{ + int ret = do_gdb_debug(); +#if 0 + if (!ret) { +#ifdef HAVE_BACKTRACE + print_trace(); +#endif + error("Please install gdb!\n"); + } +#else + /* the problem with the above is that gdb usually doesn't work + * because of the security restrictions */ + error("Please "); + if (!ret) + error("@install gdb, "); + error("@update dosemu from git, compile it with debug\n" + "info and make a bug report with the content of ~/.dosemu/boot.log at\n" +"https://github.com/dosemu2/dosemu2/issues\n"); + error("@Please provide any additional info you can, like the test-cases,\n" + "URLs and all the rest that fits.\n\n"); +#ifdef HAVE_BACKTRACE + print_trace(); +#endif +#endif + + fprintf(dbg_fd, "\n"); + fflush(dbg_fd); + dump_state(); +} + +void siginfo_debug(const siginfo_t *si) +{ + error("@\n"); + error("cpu exception in dosemu code outside of %s!\n", + (in_dpmi_pm() ? "DPMI client" : "VM86()")); +#ifdef __linux__ + psiginfo(si, ""); +#endif + error("@\n"); + dbug_printf("%s\nsig: %i code: 0x%02x errno: 0x%08x fault address: %p\n", + strsignal(si->si_signo), + si->si_signo, si->si_code, si->si_errno, si->si_addr); + +#ifdef X86_EMULATOR + /* gdb_debug() will crash in jit code doing backtrace() */ + if (!(IS_EMU_JIT() && e_in_compiled_code())) +#endif + gdb_debug(); +} diff --git a/src/arch/linux/async/debug.h b/src/arch/linux/async/debug.h new file mode 100644 index 0000000..a61abbb --- /dev/null +++ b/src/arch/linux/async/debug.h @@ -0,0 +1,7 @@ +#ifndef __DEBUG_H +#define __DEBUG_H + +void siginfo_debug(const siginfo_t *si); +void gdb_debug(void); + +#endif diff --git a/src/arch/linux/async/execinfo_wrp.h b/src/arch/linux/async/execinfo_wrp.h new file mode 100644 index 0000000..334d61e --- /dev/null +++ b/src/arch/linux/async/execinfo_wrp.h @@ -0,0 +1,11 @@ +/* execinfo.h compatibility wrapper */ + +#ifdef HAVE_EXECINFO +#include +#else +#ifdef HAVE_LIBBFD +/* we provide our own impl of those */ +char **backtrace_symbols(void *const *buffer, int size); +void backtrace_symbols_fd(void *const *buffer, int size, int fd); +#endif +#endif diff --git a/src/arch/linux/async/signal.c b/src/arch/linux/async/signal.c new file mode 100644 index 0000000..fc4052a --- /dev/null +++ b/src/arch/linux/async/signal.c @@ -0,0 +1,861 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#include +#endif +#ifdef __FreeBSD__ +#include +#endif + +#include "emu.h" +#ifdef __linux__ +#include "sys_vm86.h" +#endif +#include "bios.h" +#include "mouse.h" +#include "video.h" +#include "vgaemu.h" +#include "vgatext.h" +#include "render.h" +#include "timers.h" +#include "int.h" +#include "lowmem.h" +#include "coopth.h" +#include "emudpmi.h" +#include "dnative.h" +#include "pic.h" +#include "ipx.h" +#include "pktdrvr.h" +#include "iodev.h" +#include "serial.h" +#include "debug.h" +#include "mhpdbg.h" +#include "utilities.h" +#include "ringbuf.h" +#include "dosemu_config.h" +#include "sound.h" +#include "cpu-emu.h" +#include "sig.h" + +/* Variables for keeping track of signals */ +#define MAX_SIG_QUEUE_SIZE 50 +#define MAX_SIG_DATA_SIZE 128 +static u_short SIGNAL_head=0; u_short SIGNAL_tail=0; +struct SIGNAL_queue { + void (*signal_handler)(void *); + char arg[MAX_SIG_DATA_SIZE]; + size_t arg_size; + const char *name; +}; +static struct SIGNAL_queue signal_queue[MAX_SIG_QUEUE_SIZE]; + +#define MAX_SIGCHLD_HANDLERS 10 +struct sigchld_hndl { + pid_t pid; + void (*handler)(void*); + void *arg; + int enabled; +}; +static struct sigchld_hndl chld_hndl[MAX_SIGCHLD_HANDLERS]; +static int chd_hndl_num; + +#define MAX_SIGALRM_HANDLERS 50 +struct sigalrm_hndl { + void (*handler)(void); +}; +static struct sigalrm_hndl alrm_hndl[MAX_SIGALRM_HANDLERS]; +static int alrm_hndl_num; + +struct callback_s { + void (*func)(void *); + void *arg; + const char *name; +}; + +sigset_t q_mask; +sigset_t nonfatal_q_mask; +static sigset_t fatal_q_mask; +static int sig_inited; +int sig_threads_wa = 1; + +static int sh_tid; +static int in_handle_signals; +static void handle_signals_force_enter(int tid, int sl_state); +static void handle_signals_force_leave(int tid, void *arg, void *arg2); +static void async_call(void *arg); +static void process_callbacks(void); +static struct rng_s cbks; +#define MAX_CBKS 1000 +static pthread_mutex_t cbk_mtx = PTHREAD_MUTEX_INITIALIZER; + +static void (*sighandlers[SIGMAX])(siginfo_t *); +static void (*qsighandlers[SIGMAX])(int sig, siginfo_t *si, void *uc); +static void (*asighandlers[SIGMAX])(void *arg); + +static void SIGALRM_call(void *arg); +static void SIGIO_call(void *arg); +static void sigasync(int sig, siginfo_t *si, void *uc); +static void sigasync_std(int sig, siginfo_t *si, void *uc); + +static void _newsetqsig(int sig, void (*fun)(int sig, siginfo_t *si, void *uc)) +{ + if (qsighandlers[sig]) + return; + /* collect this mask so that all async (fatal and non-fatal) + * signals can be blocked by threads */ + sigaddset(&q_mask, sig); + qsighandlers[sig] = fun; +} + +static void newsetqsig(int sig, void (*fun)(int sig, siginfo_t *si, void *uc)) +{ + sigaddset(&fatal_q_mask, sig); + _newsetqsig(sig, fun); +} + +static void init_one_sig(int num, void (*fun)(int sig, siginfo_t *si, void *uc)) +{ + struct sigaction sa; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + sa.sa_mask = nonfatal_q_mask; + sa.sa_sigaction = fun; + sigaction(num, &sa, NULL); +} + +static void qsig_init(void) +{ + int i; + for (i = 0; i < SIGMAX; i++) { + if (qsighandlers[i]) + init_one_sig(i, qsighandlers[i]); + } +} + +static void setup_nf_sig(int sig) +{ + /* first need to collect the mask, then register all handlers + * because the same mask of non-emergency async signals + * is used for every handler. */ + sigaddset(&nonfatal_q_mask, sig); + /* Also we block them all until init is completed. */ + sigaddset(&q_mask, sig); +} + +static void do_registersig(int sig, void (*fun)(int sig, siginfo_t *si, void *uc)) +{ + assert(!sig_inited); + sigaddset(&nonfatal_q_mask, sig); + _newsetqsig(sig, fun); +} + +/* registers non-emergency async signals */ +void registersig(int sig, void (*fun)(siginfo_t *)) +{ + assert(fun && !sighandlers[sig]); + sighandlers[sig] = fun; + do_registersig(sig, sigasync); +} + +void registersig_std(int sig, void (*fun)(void *)) +{ + assert(fun && !asighandlers[sig]); + asighandlers[sig] = fun; + do_registersig(sig, sigasync_std); +} + +static void newsetsig(int sig, void (*fun)(int sig, siginfo_t *si, void *uc)) +{ + struct sigaction sa; + + sa.sa_flags = SA_SIGINFO; + /* block all non-fatal async signals */ + sa.sa_mask = nonfatal_q_mask; + sa.sa_sigaction = fun; + sigaction(sig, &sa, NULL); +} + +static void leavedos_call(void *arg) +{ + int *sig = arg; + _leavedos_sig(*sig); +} + +int sigchld_register_handler(pid_t pid, void (*handler)(void*), void *arg) +{ + int i; + for (i = 0; i < chd_hndl_num; i++) { + if (!chld_hndl[i].pid) + break; + /* make sure not yet registered */ + assert(chld_hndl[i].pid != pid); + } + if (i == chd_hndl_num) { + if (chd_hndl_num >= MAX_SIGCHLD_HANDLERS) { + error("too many sigchld handlers\n"); + return -1; + } + chd_hndl_num++; + } + chld_hndl[i].handler = handler; + chld_hndl[i].arg = arg; + chld_hndl[i].pid = pid; + chld_hndl[i].enabled = 1; + return 0; +} + +int sigchld_enable_cleanup(pid_t pid) +{ + return sigchld_register_handler(pid, NULL, NULL); +} + +int sigchld_enable_handler(pid_t pid, int on) +{ + int i; + for (i = 0; i < chd_hndl_num; i++) { + if (chld_hndl[i].pid == pid) + break; + } + if (i >= chd_hndl_num) + return -1; + chld_hndl[i].enabled = on; + return 0; +} + +static void cleanup_child(void *arg) +{ + int i, status; + pid_t pid2, pid = *(pid_t *)arg; + + for (i = 0; i < chd_hndl_num; i++) { + if (chld_hndl[i].pid == pid) + break; + } + if (i >= chd_hndl_num) + return; + if (!chld_hndl[i].enabled) + return; + pid2 = waitpid(pid, &status, WNOHANG); + if (pid2 != pid) + return; + /* SIGCHLD handler is one-shot, disarm */ + chld_hndl[i].pid = 0; + if (chld_hndl[i].handler) + chld_hndl[i].handler(chld_hndl[i].arg); +} + +static void sigbreak(void *uc) +{ + /* let CPUEMU decide what to do, as it can kick in for any backend */ + e_gen_sigalrm(); + if (!in_vm86 && config.cpu_vm_dpmi == CPUVM_NATIVE) + signative_sigbreak(uc); +} + +/* this cleaning up is necessary to avoid the port server becoming + a zombie process */ +static void sig_child(siginfo_t *si) +{ + SIGNAL_save(cleanup_child, &si->si_pid, sizeof(si->si_pid), __func__); +} + +int sigalrm_register_handler(void (*handler)(void)) +{ + assert(alrm_hndl_num < MAX_SIGALRM_HANDLERS); + alrm_hndl[alrm_hndl_num].handler = handler; + alrm_hndl_num++; + return 0; +} + +void leavedos_from_sig(int sig) +{ + coopth_abandon(); + _leavedos_main(0, sig); +} + +void leavedos_sig(int sig) +{ + static int cnt; + /* disallow multiple terminations */ + if (cnt) + return; + cnt++; + /* do not log anything from a sighandler or it may hang */ + SIGNAL_save(leavedos_call, &sig, sizeof(sig), __func__); + /* abort current sighandlers */ + if (in_handle_signals) { + in_handle_signals = 0; + } +} + +static void leavedos_signal(int sig, siginfo_t *si, void *uc) +{ + signal(sig, SIG_DFL); + leavedos_sig(sig); + sigbreak(uc); +} + +#if 0 +/* calling leavedos directly from sighandler may be unsafe - disable */ +SIG_PROTO_PFX +static void leavedos_emerg(int sig, siginfo_t *si, void *uc) +{ + leavedos_from_sig(sig); +} +#endif + +SIG_PROTO_PFX +static void abort_signal(int sig, siginfo_t *si, void *uc) +{ + siginfo_debug(si); + _exit(sig); +} + +SIG_PROTO_PFX +static void minfault(int sig, siginfo_t *si, void *uc) +{ +#ifdef HOST_ARCH_X86 +#if defined(__i386__) || defined(X86_EMULATOR) + ucontext_t *uct = uc; + sigcontext_t *scp = &uct->uc_mcontext; +#endif +#ifdef __i386__ + if (in_vm86 && config.cpu_vm == CPUVM_VM86) { + true_vm86_fault(scp); + return; + } +#endif +#ifdef __FreeBSD__ + /* freebsd fiddles with trapno */ + if (_scp_trapno == T_PAGEFLT) + _scp_trapno = 0xe; +#endif +#ifdef X86_EMULATOR + if (IS_EMU_JIT() && e_emu_fault(scp, in_vm86)) + return; +#endif +#endif + siginfo_debug(si); + leavedos_from_sig(sig); +} + +/* Silly Interrupt Generator Initialization/Closedown */ + +#ifdef SIG +SillyG_t *SillyG = 0; +static SillyG_t SillyG_[16 + 1]; +#endif + +/* + * DANG_BEGIN_FUNCTION SIG_init + * + * description: Allow DOSEMU to be made aware when a hard interrupt occurs + * The IRQ numbers to monitor are taken from config.sillyint, each bit + * corresponding to one IRQ. The higher 16 bit are defining the use of + * SIGIO + * + * DANG_END_FUNCTION + */ +void SIG_init(void) +{ +#if defined(SIG) + PRIV_SAVE_AREA + /* Get in touch with Silly Interrupt Handling */ + if (config.sillyint) { + char prio_table[] = + {8, 9, 10, 11, 12, 14, 15, 3, 4, 5, 6, 7}; + int i, + irq; + SillyG_t *sg = SillyG_; + for (i = 0; i < sizeof(prio_table); i++) { + irq = prio_table[i]; + if (config.sillyint & (1 << irq)) { + int ret; + enter_priv_on(); + ret = vm86_plus(VM86_REQUEST_IRQ, (SIGIO << 8) | irq); + leave_priv_setting(); + if ( ret > 0) { + g_printf("Gonna monitor the IRQ %d you requested\n", irq); + sg->fd = -1; + sg->irq = irq; + g_printf("SIG: IRQ%d\n", irq); + sg++; + } + } + } + sg->fd = 0; + if (sg != SillyG_) + SillyG = SillyG_; + } +#endif +} + +void SIG_close(void) +{ +#if defined(SIG) + if (SillyG) { + SillyG_t *sg = SillyG; + while (sg->fd) { + vm86_plus(VM86_FREE_IRQ, sg->irq); + sg++; + } + g_printf("Closing all IRQ you opened!\n"); + } +#endif +} + +void sig_ctx_prepare(int tid, void *arg, void *arg2) +{ + if(in_dpmi_pm()) + fake_pm_int(); + rm_stack_enter(); + clear_IF(); +} + +void sig_ctx_restore(int tid, void *arg, void *arg2) +{ + rm_stack_leave(); +} + +static void signal_thr_post(int tid, void *arg, void *arg2) +{ + if (!in_handle_signals) { + dbug_printf("in_handle_signals=0\n"); + return; + } + in_handle_signals--; +} + +static void signal_thr(void *arg) +{ + struct SIGNAL_queue *sig = &signal_queue[SIGNAL_head]; + struct SIGNAL_queue sig_c; // local copy for signal-safety + sig_c.signal_handler = signal_queue[SIGNAL_head].signal_handler; + sig_c.arg_size = sig->arg_size; + if (sig->arg_size) + memcpy(sig_c.arg, sig->arg, sig->arg_size); + sig_c.name = sig->name; + SIGNAL_head = (SIGNAL_head + 1) % MAX_SIG_QUEUE_SIZE; + if (debug_level('g') > 5) + g_printf("Processing signal %s\n", sig_c.name); + sig_c.signal_handler(sig_c.arg); +} + +/* DANG_BEGIN_FUNCTION signal_pre_init + * + * description: + * Initialize the signals to have NONE being blocked. + * Currently this is NOT of much use to DOSEMU. + * + * DANG_END_FUNCTION + * + */ +void +signal_pre_init(void) +{ + /* first set up the blocking mask: registersig() and newsetqsig() + * adds to it */ + sigemptyset(&q_mask); + sigemptyset(&nonfatal_q_mask); + registersig_std(SIGALRM, SIGALRM_call); + /* SIGIO is only used for irqs from vm86 */ + if (config.cpu_vm == CPUVM_VM86) + registersig_std(SIGIO, SIGIO_call); + registersig_std(SIG_THREAD_NOTIFY, async_call); + registersig(SIGCHLD, sig_child); + newsetqsig(SIGQUIT, leavedos_signal); + newsetqsig(SIGINT, leavedos_signal); /* for "graceful" shutdown for ^C too*/ + newsetqsig(SIGHUP, leavedos_signal); +// newsetqsig(SIGTERM, leavedos_emerg); + /* below ones are initialized by other subsystems */ +#ifdef USE_CONSOLE_PLUGIN + setup_nf_sig(SIG_ACQUIRE); + setup_nf_sig(SIG_RELEASE); +#endif + setup_nf_sig(SIGWINCH); + setup_nf_sig(SIGPROF); + /* call that after all non-fatal sigs set up */ +#ifdef __i386__ + newsetsig(SIGILL, minfault); + newsetsig(SIGTRAP, minfault); + newsetsig(SIGBUS, minfault); +#else + newsetsig(SIGILL, abort_signal); + newsetsig(SIGTRAP, abort_signal); +#ifdef __APPLE__ + newsetsig(SIGBUS, minfault); +#else + newsetsig(SIGBUS, abort_signal); +#endif +#endif + newsetsig(SIGFPE, minfault); + newsetsig(SIGSEGV, minfault); + newsetsig(SIGABRT, abort_signal); + + /* block async signals so that threads inherit the blockage */ + sigprocmask(SIG_BLOCK, &q_mask, NULL); +#if 0 +#if defined(HAVE_PTHREAD_ATTR_SETSIGMASK_NP) && defined(HAVE_PTHREAD_SETATTR_DEFAULT_NP) + { + sigset_t mask; + pthread_attr_t attr; + sigprocmask(SIG_SETMASK, NULL, &mask); + pthread_getattr_default_np(&attr); + pthread_attr_setsigmask_np(&attr, &mask); + pthread_setattr_default_np(&attr); + sig_threads_wa = 0; + } +#endif +#endif + + signal(SIGPIPE, SIG_IGN); +#ifdef __linux__ + prctl(PR_SET_PDEATHSIG, SIGQUIT); +#endif + + dosemu_pthread_self = pthread_self(); + rng_init(&cbks, MAX_CBKS, sizeof(struct callback_s)); + + if (config.cpu_vm_dpmi == CPUVM_NATIVE) + signative_pre_init(); +} + +void +signal_init(void) +{ + /* signal_init is called after dpmi_setup so this check is safe */ + if (config.cpu_vm_dpmi == CPUVM_NATIVE) + signative_init(); + + sh_tid = coopth_create("signal handling", signal_thr); + /* normally we don't need ctx handlers because the thread is detached. + * But some crazy code (vbe.c) can call coopth_attach() on it, so we + * set up the handlers just in case. */ + coopth_set_ctx_handlers(sh_tid, sig_ctx_prepare, sig_ctx_restore, NULL); + coopth_set_sleep_handlers(sh_tid, handle_signals_force_enter, + handle_signals_force_leave); + coopth_set_permanent_post_handler(sh_tid, signal_thr_post); + coopth_set_detached(sh_tid); + + /* mask is set up, now start using it */ + qsig_init(); + /* unblock signals in main thread */ + pthread_sigmask(SIG_UNBLOCK, &q_mask, NULL); + + sig_inited = 1; +} + +void signal_done(void) +{ + struct itimerval itv; + int i; + + itv.it_interval.tv_sec = itv.it_interval.tv_usec = 0; + itv.it_value = itv.it_interval; + if (setitimer(ITIMER_REAL, &itv, NULL) == -1) + g_printf("can't turn off timer at shutdown: %s\n", strerror(errno)); + if (setitimer(ITIMER_VIRTUAL, &itv, NULL) == -1) + g_printf("can't turn off vtimer at shutdown: %s\n", strerror(errno)); + sigprocmask(SIG_BLOCK, &nonfatal_q_mask, NULL); + for (i = 1; i < SIGMAX; i++) { + if (sigismember(&q_mask, i)) + signal(i, SIG_DFL); + } + SIGNAL_head = SIGNAL_tail; + sig_inited = 0; +} + +static void handle_signals_force_enter(int tid, int sl_state) +{ + if (!in_handle_signals) { + dosemu_error("in_handle_signals=0\n"); + return; + } + in_handle_signals--; +} + +static void handle_signals_force_leave(int tid, void *arg, void *arg2) +{ + in_handle_signals++; +} + +int signal_pending(void) +{ + return (SIGNAL_head != SIGNAL_tail); +} + +/* + * DANG_BEGIN_FUNCTION handle_signals + * + * description: + * Due to signals happening at any time, the actual work to be done + * because a signal occurs is done here in a serial fashion. + * + * The concept, should this eventually work, is that a signal should only + * flag that it has occurred and let DOSEMU deal with it in an orderly + * fashion as it executes the rest of it's code. + * + * DANG_END_FUNCTION + * + */ +void handle_signals(void) +{ + process_callbacks(); + while (signal_pending() && !in_handle_signals) { + in_handle_signals++; + coopth_start(sh_tid, NULL); + coopth_run_tid(sh_tid); + } +} + +/* ============================================================== + * + * This is called by default at around 100Hz. + * (see timer_interrupt_init() in init.c) + * + * The actual formulas, starting with the configurable parameter + * config.freq, are: + * config.freq default=18 + * config.update = 1E6/config.freq default=54945 + * timer tick(us) = config.update/6 default=9157.5us + * = 166667/config.freq + * timer tick(Hz) = 6*config.freq default=100Hz + * + * 6 is the magical TIMER_DIVISOR macro used to get 100Hz + * + * This call should NOT be used if you need timing accuracy - many + * signals can get lost e.g. when kernel accesses disk, and the whole + * idea of timing-by-counting is plain wrong. We'll need the Pentium + * counter here. + * ============================================================== */ + +static void SIGALRM_call(void *arg) +{ + static int first = 0; + static hitimer_t cnt200 = 0; + static hitimer_t cnt1000 = 0; + static hitimer_t cnt10 = 0; + int i; + + if (first==0) { + cnt200 = + cnt1000 = + cnt10 = + pic_sys_time; /* initialize */ + first = 1; + } + + uncache_time(); + timer_tick(); + + if (Video->handle_events && video_initialized) + Video->handle_events(); + + if ((pic_sys_time-cnt10) >= (PIT_TICK_RATE/100) || dosemu_frozen) { + cnt10 = pic_sys_time; + if (video_initialized && !config.vga) + update_screen(); + } + + for (i = 0; i < alrm_hndl_num; i++) + alrm_hndl[i].handler(); + + alarm_idle(); + + /* Here we 'type in' prestrokes from commandline, as long as there are any + * Were won't overkill dosemu, hence we type at a speed of 14cps + */ + if (config.pre_stroke) { + static int count=-1; + if (--count < 0) { + count = type_in_pre_strokes(); + if (count <0) count =7; /* with HZ=100 we have a stroke rate of 14cps */ + } + } + + /* this should be for per-second activities, it is actually at + * 200ms more or less (PARTIALS=5) */ + if ((pic_sys_time-cnt200) >= (PIT_TICK_RATE/PARTIALS)) { + cnt200 = pic_sys_time; +/* g_printf("**** ALRM: %dms\n",(1000/PARTIALS)); */ + + printer_tick(0); + floppy_tick(); + } + +/* We update the RTC from here if it has not been defined as a thread */ + + /* this is for EXACT per-second activities (can produce bursts) */ + if ((pic_sys_time-cnt1000) >= PIT_TICK_RATE) { + cnt1000 += PIT_TICK_RATE; +/* g_printf("**** ALRM: 1sec\n"); */ + rtc_update(); + } +} + +/* DANG_BEGIN_FUNCTION SIGNAL_save + * + * arguments: + * context - signal context to save. + * signal_call - signal handling routine to be called. + * + * description: + * Save into an array structure queue the signal context of the current + * signal as well as the function to call for dealing with this signal. + * This is a queue because any signal may occur multiple times before + * DOSEMU deals with it down the road. + * + * DANG_END_FUNCTION + * + */ +void SIGNAL_save(void (*signal_call)(void *), void *arg, size_t len, + const char *name) +{ + signal_queue[SIGNAL_tail].signal_handler = signal_call; + signal_queue[SIGNAL_tail].arg_size = len; + assert(len <= MAX_SIG_DATA_SIZE); + if (len) + memcpy(signal_queue[SIGNAL_tail].arg, arg, len); + signal_queue[SIGNAL_tail].name = name; + SIGNAL_tail = (SIGNAL_tail + 1) % MAX_SIG_QUEUE_SIZE; +} + + +static void SIGIO_call(void *arg){ + /* Call select to see if any I/O is ready on devices */ + irq_select(); +} + +static void sigasync0(int sig) +{ + pthread_t tid = pthread_self(); + if (!pthread_equal(tid, dosemu_pthread_self)) { +#if defined(HAVE_PTHREAD_GETNAME_NP) && defined(__GLIBC__) + char name[128]; + pthread_getname_np(tid, name, sizeof(name)); + dosemu_error("Async signal %i from thread %s\n", sig, name); +#else + dosemu_error("Async signal %i from thread\n", sig); +#endif + } +} + +static void sigasync(int sig, siginfo_t *si, void *uc) +{ + sigasync0(sig); + if (sighandlers[sig]) + sighandlers[sig](si); +} + +static void sigasync_std(int sig, siginfo_t *si, void *uc) +{ + sigasync0(sig); + if (!asighandlers[sig]) { + error("handler for sig %i not registered\n", sig); + return; + } + SIGNAL_save(asighandlers[sig], NULL, 0, __func__); + sigbreak(uc); +} + + +void do_periodic_stuff(void) +{ + check_leavedos(); + handle_signals(); +#ifdef USE_MHPDBG + /* mhp_debug() must be called exactly after handle_signals() + * and before coopth_run(). handle_signals() feeds input to + * debugger, and coopth_run() runs DPMI (oops!). We need to + * run debugger before DPMI to not lose single-steps. */ + if (mhpdbg.active) + mhp_debug(DBG_POLL, 0, 0); +#endif + coopth_run(); + + if (video_initialized && Video && Video->change_config) + update_xtitle(); + + if (Video->handle_events && video_initialized) + Video->handle_events(); +} + +void add_thread_callback(void (*cb)(void *), void *arg, const char *name) +{ + if (cb) { + struct callback_s cbk; + int i; + cbk.func = cb; + cbk.arg = arg; + cbk.name = name; + pthread_mutex_lock(&cbk_mtx); + i = rng_put(&cbks, &cbk); + g_printf("callback %s added, %i queued\n", name, rng_count(&cbks)); + pthread_mutex_unlock(&cbk_mtx); + if (!i) + error("callback queue overflow, %s\n", name); + } + /* FIXME: remove the hack below */ + if (config.cpu_vm == CPUVM_EMU && config.cpu_vm_dpmi == CPUVM_EMU) + e_gen_sigalrm(); + else + pthread_kill(dosemu_pthread_self, SIG_THREAD_NOTIFY); +} + +static void process_callbacks(void) +{ + struct callback_s cbk; + int i; + g_printf("processing async callbacks\n"); + do { + pthread_mutex_lock(&cbk_mtx); + i = rng_get(&cbks, &cbk); + pthread_mutex_unlock(&cbk_mtx); + if (i) + cbk.func(cbk.arg); + } while (i); +} + +static void async_call(void *arg) +{ + process_callbacks(); +} + + +void signal_unblock_async_sigs(void) +{ + /* unblock only nonfatal, fatals should already be unblocked */ + sigprocmask(SIG_UNBLOCK, &nonfatal_q_mask, NULL); +} + +void signal_restore_async_sigs(void) +{ + /* restore temporarily unblocked async signals in a sig context. + * Just block them even for !sas_wa because if deinit_handler is + * interrupted after changing %fs, we are in troubles */ + sigprocmask(SIG_BLOCK, &nonfatal_q_mask, NULL); +} + +void signal_unblock_fatal_sigs(void) +{ + /* unblock only nonfatal, fatals should already be unblocked */ + sigprocmask(SIG_UNBLOCK, &fatal_q_mask, NULL); +} + +/* similar to above, but to call from non-signal context */ +void signal_block_async_nosig(sigset_t *old_mask) +{ + assert(fault_cnt == 0); + pthread_sigmask(SIG_BLOCK, &nonfatal_q_mask, old_mask); +} diff --git a/src/base/bios/Makefile b/src/base/bios/Makefile new file mode 100644 index 0000000..19d088c --- /dev/null +++ b/src/base/bios/Makefile @@ -0,0 +1,11 @@ +top_builddir=../../.. +include $(top_builddir)/Makefile.conf + +CFILES=int10.c int16.c int17.c setup.c vgabios.c pci_bios.c + +all: lib + +include $(REALTOPDIR)/src/Makefile.common + +clean:: + rm -f *.o *.d diff --git a/src/base/bios/int10.c b/src/base/bios/int10.c new file mode 100644 index 0000000..b69cae6 --- /dev/null +++ b/src/base/bios/int10.c @@ -0,0 +1,1747 @@ +/* + * DANG_BEGIN_MODULE + * + * REMARK + * Video BIOS implementation. + * + * This module handles the int10 video functions. + * Most functions here change only the video memory and status + * variables; the actual screen is then rendered asynchronously + * after these by Video->update_screen. + * + * /REMARK + * DANG_END_MODULE + * + * DANG_BEGIN_CHANGELOG + * + * 5/24/95, Erik Mouw (J.A.K.Mouw@et.tudelft.nl) and + * Arjan Filius (I.A.Filius@et.tudelft.nl) + * changed int10() to make graphics work with X. + * + * 1998/04/05: Put some work into set_video_mode() (made it + * more VGA compatible) and removed new_set_video_mode(). + * Removed (useless) global variable "gfx_mode". + * -- sw (Steffen Winterfeldt ) + * + * Readded new_set_video_mode, its needed for non-X compiles. + * -- EB 3 June 1998 + * + * Renamed new_set_video_mode to X_set_video_mode to avoid confusion + * in the future + * -- Hans 980614 + * + * 1998/12/12: Fixed some bugs and integrated Josef Pavlik's + * patches; improved font/palette changes, get screen mode (ah = 0x0f) fixed, + * cursor shape is now initialized during mode set. + * -- sw + * + * 2000/05/18: Split int10() into a X and non-X part. Reworked to X part so + * that it supports fonts in gfx modes. + * -- sw + * + * 2002/11/30: Started user font support. Needs some more work! + * -- eric@coli.uni-sb.de - Eric + * + * DANG_END_CHANGELOG + */ + +#include +#include +#include +#include + +#include "emu.h" +#include "video.h" +#include "bios.h" +#include "int.h" +#include "port.h" +#include "speaker.h" +#include "utilities.h" +#include "dos2linux.h" +#include "timers.h" +#include "vgaemu.h" +#include "vgatext.h" + +/* + * Activate some debug output. + */ +#define DEBUG_INT10 1 +#define BIOS_CONFIG_SCREEN_MODE (READ_WORD(BIOS_CONFIGURATION) & 0x30) +#define IS_SCREENMODE_MDA (BIOS_CONFIG_SCREEN_MODE == 0x30) + +static const int max_page = 7; +int video_mode; +int video_combo; + +unsigned screen_adr(int page) +{ + /* This is the text screen base, the DOS program actually has to use. + * Programs that support simultaneous dual monitor support rely on + * the fact, that the BIOS takes B0000 for EQUIP-flags 4..5 = 3 + * else B8000 as regenbuffer address. Each compatible PC-BIOS behaves so. + * This is ugly, but there is no screen buffer address in the BIOS-DATA + * at 0x400. (Hans) + */ + unsigned base = IS_SCREENMODE_MDA ? MDA_VIRT_TEXT_BASE : VGA_VIRT_TEXT_BASE; + return base + page * READ_WORD(BIOS_VIDEO_MEMORY_USED); +} + +/* this maps the cursor shape given by int10, fn1 to the actually + displayed cursor start&end values in cursor_shape. This seems + to be typical IBM Black Compatibility Magic. + I modeled it approximately from the behaviour of my own + VGA's BIOS. + I'm not sure if it is correct for start=end and for font_heights + other than 16. +*/ + +#define i10_msg(x...) v_printf("INT10: " x) + +#if DEBUG_INT10 >= 1 +#define i10_deb(x...) v_printf("INT10: " x) +#else +#define i10_deb(x...) +#endif + +static void tty_char_out(unsigned char ch, int s, int attr); +static void vga_ROM_to_RAM(unsigned height, int bank); + +static void crt_outw(unsigned index, unsigned value) +{ + unsigned port = READ_WORD(BIOS_VIDEO_PORT); + port_outw(port, index | (value & 0xff00)); + port_outw(port, (index + 1) | ((value & 0xff) << 8)); +} + +static unsigned do_set_cursor_pos(unsigned page, int x, int y) +{ + unsigned co, old_y; + + old_y = get_bios_cursor_y_position(page); + set_bios_cursor_x_position(page, x); + set_bios_cursor_y_position(page, y); + co = READ_WORD(BIOS_SCREEN_COLUMNS); + crt_outw(0xe, READ_WORD(BIOS_VIDEO_MEMORY_ADDRESS)/2 + y * co + x); + return old_y; +} + +static void set_cursor_pos(unsigned page, int x, int y) +{ + unsigned old_y = do_set_cursor_pos(page, x, y); + + if (config.dumb_video && y > old_y) { + int i; + + if (no_local_video && !config.tty_stderr) + return; + for (i = 0; i < y - old_y; i++) + fputs("\r\n", config.tty_stderr ? stderr : stdout); + } +} + +static void set_cursor_shape(uint16_t shape) +{ + int cs,ce; + cshape cursor_shape; + cursor_shape.w = shape; + + WRITE_WORD(BIOS_CURSOR_SHAPE, cursor_shape.w); + + cs=CURSOR_START(cursor_shape) & 0x1F; + ce=CURSOR_END(cursor_shape) & 0x1F; + + if (cursor_shape.w & 0x6000 || cs>ce) { + i10_deb("no cursor\n"); + crt_outw(0xa, NO_CURSOR); + return; + } + + cs&=0x0F; + ce&=0x0F; + if (ce>3 && ce<12 && (config.cardtype != CARD_MDA)) { + int vga_font_height = READ_WORD(BIOS_FONT_HEIGHT); + if (cs>ce-3) cs+=vga_font_height-ce-1; + else if (cs>3) cs=vga_font_height/2; + ce=vga_font_height-1; + } + i10_msg("mapped cursor: start %d, end %d\n", cs, ce); + CURSOR_START(cursor_shape)=cs; + CURSOR_END(cursor_shape)=ce; + crt_outw(0xa, cursor_shape.w); +} + +/* This is a better scroll routine, mostly for aesthetic reasons. It was + * just too horrible to contemplate a scroll that worked 1 character at a + * time :-) + * + * It may give some performance improvement on some systems (it does + * on mine) (Andrew Tridgell) + */ +static void +bios_scroll(int x0, int y0, int x1, int y1, int l, int att) +{ + int dx = x1 - x0 + 1; + int dy = y1 - y0 + 1; + int x, y, co, li; + uint16_t blank = ' ' | (att << 8); + uint16_t tbuf[MAX_COLUMNS]; + unsigned sadr; + + if (config.dumb_video) + return; + + li= READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1) + 1; + co= READ_WORD(BIOS_SCREEN_COLUMNS); + sadr = screen_adr(0) + READ_WORD(BIOS_VIDEO_MEMORY_ADDRESS); + + if (sadr < VGA_PHYS_TEXT_BASE && ((att & 7) != 0) && ((att & 7) != 7)) + { + blank = ' ' | ((att | 7) << 8); + } + + + if (x1 >= co || y1 >= li) + { + v_printf("VID: Scroll parameters out of bounds, in Scroll!\n"); + v_printf("VID: Attempting to fix with clipping!\n"); + /* kludge for ansi.sys' clear screen - we'd better do real clipping */ + /* Also a cludge to fix list, but in the other dimension */ + if (x1 >= co) x1 = co -1; + if (y1 >= li) y1 = li -1; + dx = x1 - x0 +1; + dy = y1 - x0 +1; + } + if (dx <= 0 || dy <= 0 || x0 < 0 || x1 >= co || y0 < 0 || y1 >= li) + { + v_printf("VID: Scroll parameters impossibly out of bounds, giving up!\n"); + return; + } + + /* make a blank line */ + for (x = 0; x < dx; x++) + tbuf[x] = blank; + + if (l >= dy || l <= -dy) + l = 0; + + if (l == 0) { /* Wipe mode */ + for (y = y0; y <= y1; y++) + memcpy_to_vga(sadr + 2 * (y * co + x0), tbuf, dx * 2); + return; + } + + if (l > 0) { + if (dx == co) + vga_memcpy(sadr + 2 * y0 * co, sadr + 2 * (y0 + l) * co, (dy - l) * dx * 2); + else + for (y = y0; y <= (y1 - l); y++) + vga_memcpy(sadr + 2 * (y * co + x0), sadr + 2 * ((y + l) * co + x0), dx * 2); + + for (y = y1 - l + 1; y <= y1; y++) + memcpy_to_vga(sadr + 2 * (y * co + x0), tbuf, dx * 2); + } + else { + for (y = y1; y >= (y0 - l); y--) + vga_memcpy(sadr + 2 * (y * co + x0), sadr + 2 * ((y + l) * co + x0), dx * 2); + + for (y = y0 - l - 1; y >= y0; y--) + memcpy_to_vga(sadr + 2 * (y * co + x0), tbuf, dx * 2); + } +} + +static int using_text_mode(void) +{ + unsigned char mode = READ_BYTE(BIOS_VDU_CONTROL); + return (!(mode & 2)); +} + +static int using_mono_mode(void) +{ + return ((READ_BYTE(BIOS_VDU_CONTROL) & 0xc) == 0xc); +} + +/* + * Output a character to the screen. + * If attr != -1, set the attribute byte, too. + */ +void tty_char_out(unsigned char ch, int s, int attr) +{ + int xpos, ypos, co, li; + int gfx_mode = 0; + unsigned dst; + +/* i10_deb("tty_char_out: char 0x%02x, page %d, attr 0x%02x\n", ch, s, attr); */ + + if (config.dumb_video) { + struct char_set_state term_state; + t_unicode uni = dos_to_unicode_table[ch]; + unsigned char buff[MB_LEN_MAX + 1]; + int num, i; + + if (no_local_video && !config.tty_stderr) + return; + + init_charset_state(&term_state, trconfig.output_charset); + num = unicode_to_charset(&term_state, uni, buff, MB_LEN_MAX); + if (num <= 0) + return; + for (i = 0; i < num; i++) + fputc(buff[i], config.tty_stderr ? stderr : stdout); + } + + li= READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1) + 1; + co= READ_WORD(BIOS_SCREEN_COLUMNS); + + xpos = get_bios_cursor_x_position(s); + ypos = get_bios_cursor_y_position(s); + + /* check for gfx mode */ + if(!using_text_mode()) gfx_mode = 1; + + switch (ch) { + case '\r': /* Carriage return */ + xpos = 0; + break; + + case '\n': /* Newline */ + ypos++; + break; + + case 8: /* Backspace */ + if (xpos > 0) xpos--; + break; + + case '\t': /* Tab */ + i10_deb("char_out: tab\n"); + do { + tty_char_out(' ', s, -1); + xpos = get_bios_cursor_x_position(s); + } while (xpos % 8 != 0); + break; + + case 7: /* Bell */ + speaker_on(125, 0x637); + return; + + default: /* Printable character */ + if(gfx_mode) { + vgaemu_put_char(ch, s, attr); + } + else + { + dst = screen_adr(s) + 2 * (ypos*co + xpos); + WRITE_BYTE(dst, ch); + if(attr != -1) WRITE_BYTE(dst + 1, attr); + } + xpos++; + } + + if (xpos == co) { + xpos = 0; + ypos++; + } + if (ypos == li) { + ypos--; + if(gfx_mode) + vgaemu_scroll(0, 0, co - 1, li - 1, 1, 0); + else { + /* Scroll with color newline */ + bios_scroll(0,0,co-1,li-1,1, + vga_read(screen_adr(s) + 2*(ypos*co + xpos) + 1)); + } + } + do_set_cursor_pos(s, xpos, ypos); +} + +/* The following clears the screen buffer. It does it only to the screen + * buffer. If in termcap mode, the screen will be cleared the next time + * restore_screen() is called. + */ +static void clear_screen(void) +{ + unsigned schar; + u_short blank = ' ' | (7 << 8); + int lx, s; + + if (config.dumb_video) + return; + + v_printf("INT10: cleared screen: screen_adr %x\n", screen_adr(0)); + + for (schar = screen_adr(0), lx = 0; lx < 16*1024; + WRITE_WORD(schar, blank), lx++, schar+=2); + + for (s = 0; s < 8; s++) + do_set_cursor_pos(s, 0, 0); +} + +/* return number of vertical scanlines based on the bytes at + 40:88 and 40:89 */ +static int get_text_scanlines(void) +{ + int info = READ_BYTE(BIOS_VIDEO_INFO_2); + v_printf("scanlines=%x\n", info); + if ((info & 0x80) && !(info & 0x10)) + return 200; + if (!(info & 0x80) && !(info & 0x10)) + return 350; + if (!(info & 0x80)) + return 400; + return 480; +} + +/* set number of vertical scanlines at the bytes at + 40:88 and 40:89 */ +static void set_text_scanlines(int lines) +{ + int info = READ_BYTE(BIOS_VIDEO_INFO_2) & ~0x90; + if (lines == 200) + info |= 0x80; + else { + if (lines == 400) + info |= 0x10; + else if (lines == 480) + info |= 0x90; + } + v_printf("scanlines=%x %d\n", info, lines); + WRITE_BYTE(BIOS_VIDEO_INFO_2, info); +} + +static int adjust_font_size(int vga_font_height) +{ + /* RBIL says: + Recalculate: BIOS_FONT_HEIGHT, BIOS_ROWS_ON_SCREEN_MINUS_1, and + BIOS_VIDEO_MEMORY_USED. + Update CRTC registers 9 (for font height), A/B (cursor), 12 (display end), + 14 (underline location) */ + int li, text_scanlines; + ioport_t port; + + if(vga_font_height == 0) + return 0; + + i10_msg("adjust_font_size: font size %d lines\n", vga_font_height); + + text_scanlines = (READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1) + 1) * + READ_WORD(BIOS_FONT_HEIGHT); + if (text_scanlines <= 200) text_scanlines = 200; + else if (text_scanlines <= 350) text_scanlines = 350; + else if (text_scanlines <= 400) text_scanlines = 400; + else text_scanlines = 480; + li = text_scanlines / vga_font_height; + if (li > MAX_LINES) + return 0; + text_scanlines = li * vga_font_height; + + port = READ_WORD(BIOS_VIDEO_PORT); + port_outw(port, 0x12 | (((text_scanlines-1) & 0xff) << 8)); + port_outb(port, 0x14); + port_outb(port + 1, (port_inb(port + 1) & ~0x1f) + vga_font_height); + port_outb(port, 9); + port_outb(port + 1, (port_inb(port + 1) & ~0x1f) + vga_font_height -1); + WRITE_WORD(BIOS_FONT_HEIGHT, vga_font_height); + + if (using_mono_mode()) + set_cursor_shape(0x0b0d); + else + set_cursor_shape(0x0607); + + if (Video->setmode != NULL) { + /* otherwise we can't change li */ + WRITE_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1, li - 1); + WRITE_WORD(BIOS_VIDEO_MEMORY_USED, + TEXT_SIZE(READ_WORD(BIOS_SCREEN_COLUMNS), li)); + } + return 1; +} + +/* + * set_video_mode() accepts both (S)VGA and VESA mode numbers + * It should be fully compatible with the old set_video_mode() + * function. + * Note: bit 16 in "mode" is used internally to indicate that + * nothing should be done except adjusting the font size. + * -- 1998/04/04 sw + */ + +bool set_video_mode(int mode) +{ + vga_mode_info *vmi; + int clear_mem = 1; + unsigned u; + int co, li, vga_font_height, orig_mode; + ioport_t port; + + if (config.dumb_video) { + i10_msg("set_video_mode: no video!\n"); + return 0; + } + + i10_msg("set_video_mode: mode 0x%02x\n", mode); + + if((vmi = vga_emu_find_mode(mode, NULL)) == NULL) { + i10_msg("set_video_mode: undefined video mode\n"); + return 0; + } + if (vmi->mode_class == TEXT) { + vga_mode_info *vmi2 = vmi; + vga_mode_info *vmi_best = vmi; + int ts = get_text_scanlines(); + int delta = ts - vmi2->height; + + i10_msg("look for mode with scan=%i font_height=%i\n", + ts, READ_BYTE(BIOS_FONT_HEIGHT)); + while ((vmi2 = vga_emu_find_mode(mode, vmi2))) { + if (vmi2->height > ts || + vmi2->char_height != READ_BYTE(BIOS_FONT_HEIGHT)) + continue; + if (delta < 0 || ts - vmi2->height < delta) { + i10_msg("better mode found: %ix%i %ix%i scan=%i\n", + vmi2->text_width, vmi2->text_height, + vmi2->char_width, vmi2->char_height, + vmi2->height); + vmi_best = vmi2; + delta = ts - vmi2->height; + } + vmi = vmi_best; + } + } + if (vmi->mode_class == GRAPH && config.term) { + error("Cannot set graphics mode under terminal!\n"); + return 0; + } + if (!memcheck_is_reserved(vmi->buffer_start << 4, 0x8000, 'v')) { + error("VGA: cannot set mode %i because of UMB at 0x%x\n", + mode, vmi->buffer_start); + return 0; + } + + orig_mode = mode; + + if(mode >= 0x80 && mode < 0x100) { + mode &= 0x7f; + clear_mem = 0; + } + if(mode & 0x8000) { + mode &= ~0x8000; + clear_mem = 0; + } + + WRITE_BYTE(BIOS_CURRENT_SCREEN_PAGE, 0); + WRITE_WORD(BIOS_VIDEO_MEMORY_ADDRESS, 0); + WRITE_BYTE(BIOS_VIDEO_INFO_0, clear_mem ? 0x60 : 0xe0); + MEMSET_DOS(0x450, 0, 0x10); /* equiv. to set_bios_cursor_(x/y)_position(0..7, 0) */ + + if(config.cardtype == CARD_MDA) mode = 7; + + if(vmi->type == TEXT_MONO) { + WRITE_BYTE(BIOS_CONFIGURATION, READ_BYTE(BIOS_CONFIGURATION) | 0x30); + port = 0x3b4; + } else { + WRITE_BYTE(BIOS_CONFIGURATION, + (READ_BYTE(BIOS_CONFIGURATION) & ~0x30) | 0x20); + port = 0x3d4; + } + WRITE_WORD(BIOS_VIDEO_PORT, port); + + /* + * We store the SVGA mode number (if possible) even when setting + * a VESA mode. + * Note that this gives mode 0x7f if no VGA mode number + * had been assigned. -- sw + */ + WRITE_BYTE(BIOS_VIDEO_MODE, vmi->VGA_mode & 0x7f); + if (mode == 3) + WRITE_BYTE(BIOS_VDU_CONTROL, 9); + else if (vmi->type == TEXT_MONO) + WRITE_BYTE(BIOS_VDU_CONTROL, 0xc); + else if (vmi->mode_class == TEXT) + WRITE_BYTE(BIOS_VDU_CONTROL, 8); + else + WRITE_BYTE(BIOS_VDU_CONTROL, 0xa); + + if (Video->setmode != NULL) { + li = vmi->text_height; + co = vmi->text_width; + } else { + li = READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1) + 1; + co = READ_WORD(BIOS_SCREEN_COLUMNS); + } + + video_mode = orig_mode; + +#if USE_DUALMON + /* + * The following code (& comment) is taken literally + * from the old set_video_mode(). Don't know what it's + * for (or if it works). -- 1998/04/04 sw + */ + + /* + * This to be sure in case of older DOS programs probing HGC. + * There was no secure way to detect a HGC before VGA was invented. + * ( Now we can do INT 10, AX=1A00 ). + * Some older DOS programs do it by modifying EQUIP-flags + * and then let the BIOS say, if it can ?!?!) + * If we have config.dualmon, this happens legally. + */ + if(vmi->type == TEXT_MONO && config.dualmon) + vga_emu_setmode(7, co, li); + else +#endif + + /* setmode needs video_mode to _still have_ the memory-clear bit -- sw */ + vga_emu_setmode(mode, co, li); + + /* + * video_mode is expected to be the mode number _without_ the + * memory-clear bit + * -- sw + */ + video_mode = mode; + + if(clear_mem && using_text_mode()) clear_screen(); + if (mode == 0x6) + WRITE_BYTE(BIOS_VDU_COLOR_REGISTER, 0x3f); + else if (mode <= 0x7 || vmi->type == TEXT_MONO) + WRITE_BYTE(BIOS_VDU_COLOR_REGISTER, 0x30); + + vga_font_height = vmi->char_height; + if (li <= MAX_LINES) { + if(using_text_mode()) { + port_outb(port, 9); + port_outb(port + 1, (port_inb(port + 1) & ~0x1f) + vga_font_height -1); + /* adjust number of scanlines in the CRT; setmode set it at 400 */ + if (vmi->height == 200) { + /* just set doublescan */ + port_outb(port + 1, 0x80 | port_inb(port + 1)); + } else if (vmi->height != 400) { + /* adjust display end */ + port_outw(port, 0x12 | (((vmi->height-1) & 0xff) << 8)); + } + WRITE_WORD(BIOS_VIDEO_MEMORY_USED, TEXT_SIZE(co, li)); + } else { + unsigned page_size = roundUpToNextPowerOfTwo( + (vga.scan_len * vga.height) | 0xfff); + if (page_size > vga.mem.bank_pages * 4096) + page_size = vga.mem.bank_pages * 4096; + WRITE_WORD(BIOS_VIDEO_MEMORY_USED, page_size); + } + WRITE_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1, li - 1); + WRITE_WORD(BIOS_SCREEN_COLUMNS, co); + } + + switch(vga_font_height) { + case 14: + u = vgaemu_bios.font_14; + break; + case 16: + u = vgaemu_bios.font_16; + break; + default: + u = vgaemu_bios.font_8; + } + SETIVEC(0x43, 0xc000, u); + WRITE_WORD(BIOS_FONT_HEIGHT, vga_font_height); // before set_cursor_shape() + set_cursor_shape(vmi->type == TEXT_MONO ? 0x0b0d : 0x0607); + + if (using_text_mode()) { + v_printf("INT10: X_set_video_mode: 8x%d ROM font -> bank 0\n", + vga_font_height); + vga_ROM_to_RAM(vga_font_height, 0); /* 0 is default bank */ + i10_msg("activated font bank 0\n"); + } + + return 1; +} + +/* get the active and alternate display combination code */ +static void get_dcc(int *active_dcc, int *alternate_dcc) +{ + int cur_video_combo = READ_BYTE(BIOS_VIDEO_COMBO); + +#if USE_DUALMON + if (config.dualmon) { + if (IS_SCREENMODE_MDA) { + *active_dcc = MDA_VIDEO_COMBO; /* active display */ + *alternate_dcc = cur_video_combo; + } + else { + *active_dcc = cur_video_combo; /* active display */ + *alternate_dcc = MDA_VIDEO_COMBO; + } + return; + } +#endif + *active_dcc = cur_video_combo; /* active display */ + *alternate_dcc = 0; /* no inactive display */ +} + +/* INT 10 AH=1B - FUNCTIONALITY/STATE INFORMATION (PS,VGA/MCGA) */ +static void return_state(unsigned int statebuf) { + int active_dcc, alternate_dcc; + + WRITE_WORD(statebuf, vgaemu_bios.functionality - 0xc0000); + WRITE_WORD(statebuf + 2, 0xc000); + + /* store bios 0:449-0:466 at ofs 0x04 */ + MEMCPY_DOS2DOS(statebuf + 0x04, 0x449, 0x466 - 0x449 + 1); + /* store bios 0:484-0:486 at ofs 0x22 */ + MEMCPY_DOS2DOS(statebuf + 0x22, 0x484, 0x486 - 0x484 + 1); + /* correct number of rows-1 to number of rows at offset 0x22 */ + WRITE_BYTE(statebuf + 0x22, READ_BYTE(statebuf + 0x22) + 1); + get_dcc(&active_dcc, &alternate_dcc); + WRITE_BYTE(statebuf + 0x25, active_dcc); + WRITE_BYTE(statebuf + 0x26, alternate_dcc); + WRITE_BYTE(statebuf + 0x27, 16); /* XXX number of colors, low byte */ + WRITE_BYTE(statebuf + 0x28, 0); /* XXX number of colors, high byte */ + WRITE_BYTE(statebuf + 0x29, 8); /* XXX number of pages supported */ + WRITE_BYTE(statebuf + 0x2a, 2); /* XXX number of scanlines 0-3=200,350,400,480 */ + WRITE_BYTE(statebuf + 0x2b, 0); /* XXX primary character block */ + WRITE_BYTE(statebuf + 0x2c, 0); /* XXX secondary character block */ + WRITE_BYTE(statebuf + 0x31, 3); /* video memory: 3 = 256K */ + WRITE_BYTE(statebuf + 0x32, 0); /* XXX save pointer state flags */ + MEMSET_DOS(statebuf + 0x33, 0, 13); +} + +/* helpers for font processing - eric@coli.uni-sb.de 11/2002 */ +/* only for TEXT mode: Otherwise, int 0x43 is used... */ + +static void vga_RAM_to_RAM(unsigned height, unsigned char chr, unsigned count, + unsigned seg, unsigned ofs, int bank) +{ + unsigned char *dst; + unsigned i; + unsigned long src; + unsigned bankofs; + if (!count) { + v_printf("Tried to load 0 characters of font data???\n"); + return; + } + src = seg; + src <<= 4; + src += ofs; + bankofs = ((bank & 3) << 1) + ((bank & 4) >> 2); + bankofs <<= 13; /* unit is 8k */ + i10_msg("load 8x%d font (char %d..%d) 0x%04x:0x%04x -> bank %d\n", + height, chr, chr+count-1, seg, ofs, bank); + dst = vga.mem.base + 0x20000 + bankofs; + /* copy count characters of height bytes each to vga_font_mem */ + for(i = chr; i < chr + count; i++) { + MEMCPY_2UNIX(dst + i * 32, src + (i - chr) * height, height); + if (height < 32) + memset(dst + i * 32 + height, 0, 32 - height); + } + vga.reconfig.mem = 1; +} + +static void vga_ROM_to_RAM(unsigned height, int bank) +{ + unsigned seg, ofs; + seg = 0xc000; + switch (height) { /* ALTERNATE ROM fonts not yet usable! */ + case 8: + ofs = vgaemu_bios.font_8; + break; + case 14: + ofs = vgaemu_bios.font_14; + break; + case 16: + ofs = vgaemu_bios.font_16; + break; + default: + v_printf("Error! Tried to load 8x%d ROM font!?\n",height); + ofs = vgaemu_bios.font_16; + } + vga_RAM_to_RAM(height,0,256,seg,ofs,bank); + memcpy(vga.backup_font, vga.mem.base + 0x20000, 256 * 32); +} + +/******************************************************************/ + +/* the actual int10 handler */ + +int int10(void) /* with dualmon */ +{ + /* some code here is copied from Alan Cox ***************/ + int x, y, co, li; + unsigned page, page_size, address; + unsigned sm; + +#if 0 && USE_DUALMON + static int last_equip=-1; + + if (config.dualmon && (last_equip != BIOS_CONFIG_SCREEN_MODE)) { + v_printf("VID: int10 entry, equip-flags=0x%04x\n",READ_WORD(BIOS_CONFIGURATION)); + last_equip = BIOS_CONFIG_SCREEN_MODE; + if (IS_SCREENMODE_MDA) Video->update_screen = NULL; + else Video->update_screen = Video_default->update_screen; + } +#endif + + li= READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1) + 1; + co= READ_WORD(BIOS_SCREEN_COLUMNS); + + if (debug_level('v') >= 3) + { + if (debug_level('v') >= 4) + i10_msg("near %04x:%08x\n", READ_SEG_REG(cs), REG(eip)); + if ( (LO(ax) >= ' ') && (LO(ax) < 0x7f) ) + i10_msg("AH=%02x AL=%02x '%c'\n", + HI(ax), LO(ax), LO(ax)); + else + i10_msg("AH=%02x AL=%02x\n", HI(ax), LO(ax)); + } + +#if 0 + i10_msg("ax %04x, bx %04x\n",LWORD(eax), LWORD(ebx)); +#endif + + switch(HI(ax)) { + case 0x00: /* set video mode */ + i10_msg("set video mode: 0x%x\n", LO(ax)); + if(!set_video_mode(LO(ax))) { + i10_msg("set_video_mode() failed\n"); + } + break; + + + case 0x01: /* set text cursor shape */ + i10_deb("set text cursor shape: %u-%u\n", HI(cx), LO(cx)); + set_cursor_shape(LWORD(ecx)); + break; + + + case 0x02: /* set cursor pos */ + page = HI(bx); + x = LO(dx); + y = HI(dx); + i10_deb("set cursor pos: page %d, x.y %d.%d\n", page, x, y); + if(page > 7) { + i10_msg("set cursor pos: page > 7: %d\n", page); + return 1; + } + if (x >= co || y >= li) { + /* some apps use this to hide the cursor, + * we move it 1 char behind the visible part + */ + x = co; + y = li -1; + } + + set_cursor_pos(page, x, y); + break; + + + case 0x03: /* get cursor pos/shape */ + /* output start & end scanline even if the requested page is invalid */ + LWORD(ecx) = READ_WORD(BIOS_CURSOR_SHAPE); + + page = HI(bx); + if (page > 7) { + LWORD(edx) = 0; + i10_msg("get cursor pos: page > 7: %d\n", page); + } else { + LO(dx) = get_bios_cursor_x_position(page); + HI(dx) = get_bios_cursor_y_position(page); + } + + i10_deb( + "get cursor pos: page %u, x.y %u.%u, shape %u-%u\n", + page, LO(dx), HI(dx), HI(cx), LO(cx) + ); + break; + + + case 0x04: /* read light pen pos */ + i10_msg("read light pen pos: NOT IMPLEMENTED\n"); + HI(ax) = 0; /* "light pen switch not pressed" */ + /* This is how my VGA BIOS behaves [rz] */ + break; + + + case 0x05: /* set active display page */ +#if USE_DUALMON + if (config.dualmon && IS_SCREENMODE_MDA) break; +#endif + page = LO(ax); + i10_deb("set display page: from %d to %d\n", READ_BYTE(BIOS_CURRENT_SCREEN_PAGE), page); + if(page > max_page) { + i10_msg("set display page: bad page %d\n", page); + break; + } + page_size = READ_WORD(BIOS_VIDEO_MEMORY_USED); + address = page_size * page; + crt_outw(0xc, address/(using_text_mode() ? 2 : 1)); + + WRITE_BYTE(BIOS_CURRENT_SCREEN_PAGE, page); + WRITE_WORD(BIOS_VIDEO_MEMORY_ADDRESS, address); + x = get_bios_cursor_x_position(page); + y = get_bios_cursor_y_position(page); + set_cursor_pos(page, x, y); + break; + + + case 0x06: /* scroll up */ + reset_idle(0); + i10_deb( + "scroll up: %u lines, area %u.%u-%u.%u, attr 0x%02x\n", + LO(ax), LO(cx), HI(cx), LO(dx), HI(dx), HI(bx) + ); + if(using_text_mode()) { + bios_scroll(LO(cx), HI(cx), LO(dx), HI(dx), LO(ax), HI(bx)); + } + else { + vgaemu_scroll(LO(cx), HI(cx), LO(dx), HI(dx), LO(ax), HI(bx)); + } + break; + + + case 0x07: /* scroll down */ + reset_idle(0); + i10_deb( + "scroll dn: %u lines, area %u.%u-%u.%u, attr 0x%02x\n", + LO(ax), LO(cx), HI(cx), LO(dx), HI(dx), HI(bx) + ); + if(using_text_mode()) { + bios_scroll(LO(cx), HI(cx), LO(dx), HI(dx), -LO(ax), HI(bx)); + } + else { + vgaemu_scroll(LO(cx), HI(cx), LO(dx), HI(dx), -LO(ax), HI(bx)); + } + break; + + + case 0x08: /* read character & attr at x,y */ + page = HI(bx); + if (page > max_page) { + i10_msg("read char: invalid page %d\n", page); + break; + } + if(using_text_mode()) { + sm = screen_adr(page); + LWORD(eax) = vga_read_word(sm + (co * get_bios_cursor_y_position(page) + + get_bios_cursor_x_position(page)) * 2); + } + else { + LWORD(eax) = 0; + } + i10_deb( + "read char: char(%d.%d) = 0x%02x, attr 0x%02x\n", + get_bios_cursor_x_position(page), get_bios_cursor_y_position(page), + LO(ax), HI(ax) + ); + break; + + + /* these two put literal character codes into memory, and do + * not scroll or move the cursor... + * the difference is that 0xA ignores color for text modes + */ + case 0x09: /* write char & attr */ + reset_idle(0); + i10_deb( + "rep char: page %u, char 0x%02x '%c', attr 0x%02x\n", + HI(bx), LO(ax), LO(ax) > ' ' && LO(ax) < 0x7f ? LO(ax) : ' ', LO(bx) + ); + if (config.dumb_video) { + FILE *f = config.tty_stderr ? stderr : stdout; + int i; + + if (no_local_video && !config.tty_stderr) + break; + for (i = 0; i < LWORD(ecx); i++) + fputc(LO(ax), f); + /* cursor must not move when printing */ + fputc('\r', f); + } else { + vgaemu_repeat_char_attr(LO(ax), HI(bx), LO(bx), LWORD(ecx)); + } + break; + + case 0x0a: /* write char */ + reset_idle(0); + i10_deb( + "rep char: page %u, char 0x%02x '%c'\n", + HI(bx), LO(ax), LO(ax) > ' ' && LO(ax) < 0x7f ? LO(ax) : ' ' + ); + vgaemu_repeat_char(LO(ax), HI(bx), LO(bx), LWORD(ecx)); + break; + + + case 0x0b: /* set palette/bg/border color */ + { + unsigned char currentpalette = READ_BYTE(BIOS_VDU_COLOR_REGISTER); + i10_msg("set palette or bg/border, bx=%x\n",LWORD(ebx)); + if (HI(bx) == 0) { + currentpalette &= ~0x1f; + currentpalette |= LO(bx) & 0x1f; + } else if (HI(bx) == 1) { + if (LO(bx)) + currentpalette |= 0x20; + else + currentpalette &= ~0x20; + } else { + break; + } + Misc_set_color_select(currentpalette); + WRITE_BYTE(BIOS_VDU_COLOR_REGISTER, currentpalette); + } + break; + + case 0x0c: /* write pixel */ + reset_idle(0); + if(!using_text_mode()) + vgaemu_put_pixel(LWORD(ecx), LWORD(edx), HI(bx), LO(ax)); + break; + + + case 0x0d: /* read pixel */ + LO(ax) = 0; + if(!using_text_mode()) { + LO(ax) = vgaemu_get_pixel(LWORD(ecx), LWORD(edx), HI(bx)); + i10_msg("read pixel: 0x%02x\n", LO(ax)); + } + break; + + case 0x0e: /* print char */ + reset_idle(0); + if(using_text_mode()) { + i10_deb( + "tty put char: page %u, char 0x%02x '%c'\n", + HI(bx), LO(ax), LO(ax) > ' ' && LO(ax) < 0x7f ? LO(ax) : ' ' + ); + tty_char_out(LO(ax), READ_BYTE(BIOS_CURRENT_SCREEN_PAGE), -1); + } + else { + i10_deb( + "tty put char: page %u, char 0x%02x '%c', attr 0x%02x\n", + HI(bx), LO(ax), LO(ax) > ' ' && LO(ax) < 0x7f ? LO(ax) : ' ', LO(bx) + ); + tty_char_out(LO(ax), READ_BYTE(BIOS_CURRENT_SCREEN_PAGE), LO(bx)); + } + break; + + case 0x0f: /* get video mode */ +/* EGA, VGA, and UltraVision return either AL=03h (color) or AL=07h + (monochrome) in all extended-row text modes */ + if (using_text_mode() && li > 25) + LO(ax) = (using_mono_mode() ? 7 : 3); + else + LO(ax) = READ_BYTE(BIOS_VIDEO_MODE) | + (READ_BYTE(BIOS_VIDEO_INFO_0) & 0x80); + HI(ax) = co & 0xff; + HI(bx) = READ_BYTE(BIOS_CURRENT_SCREEN_PAGE); + i10_deb( + "get video mode: mode 0x%02x, page %u, %u columns\n", + LO(ax), HI(bx), HI(ax) + ); + break; + + + case 0x10: /* ega/vga palette functions */ + i10_deb("ega/vga palette: sub function 0x%02x\n", LO(ax)); + + /* root@zaphod */ + /* Palette register stuff. Only for the VGA emulator */ + { + int i, count; + unsigned int src; + unsigned char index, m; + DAC_entry rgb; + + switch(LO(ax)) { + case 0x00: /* Set Palette Register */ + Attr_set_entry(LO(bx), HI(bx)); + break; + + case 0x01: /* Set Overscan Color */ + Attr_set_entry(0x11, HI(bx)); + break; + + case 0x02: /* Set Palette & Overscan Color */ + src = SEGOFF2LINEAR(SREG(es), LWORD(edx)); + for(i = 0; i < 0x10; i++) Attr_set_entry(i, READ_BYTE(src + i)); + Attr_set_entry(0x11, READ_BYTE(src + i)); + break; + + case 0x03: /* Toggle Intensity/Blinking Bit */ + m = Attr_get_entry(0x10) & ~(1 << 3); + m |= (LO(bx) & 1) << 3; + Attr_set_entry(0x10, m); + break; + + case 0x07: /* Read Palette Register */ + HI(bx) = Attr_get_entry(LO(bx)); + break; + + case 0x08: /* Read Overscan Color */ + HI(bx) = Attr_get_entry(0x11); + break; + + case 0x09: /* Read Palette & Overscan Color */ + src = SEGOFF2LINEAR(SREG(es), LWORD(edx)); + for(i = 0; i < 0x10; i++) WRITE_BYTE(src + i, Attr_get_entry(i)); + WRITE_BYTE(src + i, Attr_get_entry(0x11)); + break; + + case 0x10: /* Set Individual DAC Register */ + DAC_set_entry(LO(bx), HI(dx), HI(cx), LO(cx)); + break; + + case 0x12: /* Set Block of DAC Registers */ + index = LO(bx); + count = LWORD(ecx); + src = SEGOFF2LINEAR(SREG(es), LWORD(edx)); + for(i = 0; i < count; i++, index++) + DAC_set_entry(index, READ_BYTE(src + 3*i), + READ_BYTE(src + 3*i + 1), READ_BYTE(src + 3*i + 2)); + break; + + case 0x13: /* Select Video DAC Color Page */ + m = Attr_get_entry(0x10); + switch(LO(bx)) { + case 0: /* Select Page Mode */ + m &= ~(1 << 7); + m |= (HI(bx) & 1) << 7; + Attr_set_entry(0x10, m); + break; + case 1: /* Select Page */ + if(m & (1 << 7)) + Attr_set_entry(0x14, HI(bx) & 0xf); + else + Attr_set_entry(0x14, (HI(bx) & 0x3) << 2); + break; + } + break; + + case 0x15: /* Read Individual DAC Register */ + DAC_get_entry(&rgb, LO(bx)); + HI(dx) = rgb.r; HI(cx) = rgb.g; LO(cx) = rgb.b; + break; + + case 0x17: /* Read Block of DAC Registers */ + index = LO(bx); + count = LWORD(ecx); + src = SEGOFF2LINEAR(SREG(es), LWORD(edx)); + for(i = 0; i < count; i++, index++) { + DAC_get_entry(&rgb, index); + WRITE_BYTE(src + 3*i, rgb.r); + WRITE_BYTE(src + 3*i + 1, rgb.g); + WRITE_BYTE(src + 3*i + 2, rgb.b); + } + break; + + case 0x18: /* Set PEL Mask */ + DAC_set_pel_mask(LO(bx)); + break; + + case 0x19: /* Read PEL Mask */ + LO(bx) = DAC_get_pel_mask(); + break; + + case 0x1a: /* Get Video DAC Color Page */ + LO(bx) = m = (Attr_get_entry(0x10) >> 7) & 1; + HI(bx) = (Attr_get_entry(0x14) & 0xf) >> (m ? 0 : 2); + break; + + case 0x1b: /* Convert to Gray */ + for(index = LO(bx), count = LWORD(ecx); count--; index++) + DAC_rgb2gray(index); + break; + + default: + i10_msg("ega/vga palette: invalid sub function 0x%02x\n", LO(ax)); + break; + } + } + break; + + + case 0x11: /* character generator functions */ + { + int vga_font_height; + unsigned ofs, seg; + unsigned rows, char_height; + + i10_msg("char gen: func 0x%02x, bx 0x%04x\n", LO(ax), LWORD(ebx)); + + switch(LO(ax)) { + case 0x03: + /* For EGA: Select fonts xx and yy for attrib bit 3 1/0 */ + /* For VGA: Same, but using Xxx and Yyy. BL is 00XYxxyy */ + /* *** see also vgaemu_put_char() *** */ + /* the X/Y bits mean 8k offset, xx/yy use 16k units */ + /* see: sequencer, map select, data[3] ... sequemu.c */ + Seq_set_index(3); + Seq_write_value(LO(bx)); + i10_msg("sequencer char map select: 0x%02x\n", LO(bx)); + break; + + case 0x01: /* load 8x14 charset */ + case 0x11: + vga_font_height = 14; + vga_ROM_to_RAM(14, LO(bx)); + goto more_lines; + + case 0x02: /* load 8x8 charset */ + case 0x12: + vga_font_height = 8; + vga_ROM_to_RAM(8, LO(bx)); + goto more_lines; + + case 0x04: /* load 8x16 charset */ + case 0x14: + vga_font_height = 16; + vga_ROM_to_RAM(16, LO(bx)); + goto more_lines; + + /* load a custom font */ + case 0x00: + case 0x10: + vga_font_height = HI(bx); + /* ************************************************************* + * func 00 would not change as much as func 0x10, which would * + * reprogram registers 9 = bh-1 (mode 7 only, max scan line), * + * a = bh-2 (cursor start), b = 0 (cursor end), * + * 12 = (rows+1) / (bh-1) (vertical display end), * + * 14 = bh-1 (underline loc). Recalcluates CRT buffer length. * + * Max character rows is also recalculated, as well as by/char * + * ... and page 0 must be active * + * ES:BP -> table CX = count of chars DX = from which char on * + * BL = which block to load into map2 BH = bytes / char * + ************************************************************* */ + vga_RAM_to_RAM(HI(bx), LO(dx), LWORD(ecx), + SREG(es), LWORD(ebp), LO(bx)); + i10_msg("some user font data loaded\n"); + goto more_lines; + + more_lines: + /* Also activating the target bank - some programs */ + /* seem to assume this. Sigh... */ + + Seq_set_index(3); /* sequencer: character map select */ + /* available: x, y, c...!? transforming bitfields... */ + x = (LO(bx) & 3) | ((LO(bx) & 4) << 2); + x |= ((LO(bx) & 3) << 2) | ((LO(bx) & 4) << 3); + Seq_write_value(x); /* set bank N for both fonts */ + i10_msg("activated font bank %d (0x%02x)\n", + (LO(bx) & 7), x); + if(LO(ax) >= 0x10 && !adjust_font_size(vga_font_height)) + v_printf("Problem changing font height %d->%d\n", + READ_WORD(BIOS_FONT_HEIGHT), vga_font_height); + break; + + case 0x20: /* set 8x8 gfx chars */ + SETIVEC(0x1f, SREG(es), LWORD(ebp)); + i10_deb("set 8x8 gfx chars: addr 0x%04x:0x%04x\n", ISEG(0x1f), IOFF(0x1f)); + break; + + case 0x21: /* load user gfx chars */ + case 0x22: /* load 14x8 gfx chars */ + case 0x23: /* load 8x8 gfx chars */ + case 0x24: /* load 16x8 gfx chars */ + /* BL=0/1/2/3 for DL/14/25/43 rows */ + /* CX is byte / char, ES:BP is pointer for case 0x21 */ + seg = 0xc000; + switch(LO(ax)) { + case 0x21: + ofs = LWORD(ebp); + seg = SREG(es); + char_height = LWORD(ecx); + break; + case 0x22: + ofs = vgaemu_bios.font_14; + char_height = 14; + break; + case 0x23: + ofs = vgaemu_bios.font_8; + char_height = 8; + break; + default: /* case 0x24 */ + ofs = vgaemu_bios.font_16; + char_height = 16; + break; + } + rows = LO(dx); /* gets changed for BL != 0 now: */ + switch(LO(bx)) { + case 1: + rows = 14; + break; + case 2: + rows = 25; + break; + case 3: + rows = 43; + break; + } + SETIVEC(0x43, seg, ofs); + WRITE_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1, rows - 1); + WRITE_WORD(BIOS_FONT_HEIGHT, char_height); + /* Does NOT load any video font RAM */ + /* This is for GRAPHICS mode only. No vga_R*... */ + /* No setting of the CRTC font size either... */ + i10_msg( + "load gfx font: height %u, rows %u, addr 0x%04x:0x%04x\n", + char_height, rows, seg, ofs + ); + break; + + case 0x30: /* get current character generator info */ + LWORD(ecx) = READ_WORD(BIOS_FONT_HEIGHT); + LO(dx) = READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1); + seg = 0xc000; + switch((HI(bx))) { + case 0: + ofs = IOFF(0x1f); + seg = ISEG(0x1f); + break; + case 1: + ofs = IOFF(0x43); + seg = ISEG(0x43); + break; + case 2: + ofs = vgaemu_bios.font_14; + break; + case 3: + ofs = vgaemu_bios.font_8; + break; + case 4: + ofs = vgaemu_bios.font_8 + 128 * 8; + break; + case 5: + ofs = vgaemu_bios.font_14_alt; + break; + case 6: + ofs = vgaemu_bios.font_16; + break; + case 7: + ofs = vgaemu_bios.font_16_alt; + break; + default: + seg = ofs = 0; + } + LWORD(ebp) = ofs; + SREG(es) = seg; + i10_deb( + "font info: char height: %u, rows %u, font 0x%x, addr 0x%04x:0x%04x\n", + LWORD(ecx), LO(dx) + 1, HI(bx), SREG(es), LWORD(ebp) + ); + break; + + default: + i10_msg("char gen: func 0x%02x NOT IMPLEMENTED\n", LO(ax)); + } + } + break; + + + case 0x12: /* alternate function select */ + switch(LO(bx)) { + case 0x10: /* get ega info */ + HI(bx) = READ_WORD(BIOS_VIDEO_PORT) == 0x3b4; + LO(bx) = 3; + HI(cx)=0xf; /* feature bits (no feature controller) */ + LO(cx)=READ_BYTE(BIOS_VIDEO_INFO_1) & 0xf; + i10_deb("get ega info: bx 0x%04x, cx 0x%04x\n", LWORD(ebx), LWORD(ecx)); + break; + + case 0x20: /* select alternate print screen */ + /* Would install a handler of the video */ + /* BIOS that can do more modes than the main BIOS */ + /* handler (which is often limited to 80x25 text) */ + i10_deb("select alternate prtsc: NOT IMPLEMENTED\n"); + break; + + case 0x30: /* select vertical resolution */ + { + static int scanlines[4] = {200, 350, 400, 480}; + /* 480 (undocumented) (int 0x10 AH=12 BL=30) */ + if((unsigned) LO(ax) < 4) { + int text_scanlines = scanlines[LO(ax)]; + set_text_scanlines(text_scanlines); + LO(ax) = 0x12; + i10_deb("select vert res: %d lines", text_scanlines); + } + else { + i10_msg("select vert res: invalid arg 0x%02x", LO(ax)); + } + } + break; + + case 0x32: /* enable/disable cpu access to video ram */ + /* FIXME: implement this XXX */ + /* would probably modify port 0x3c2, misc output, which */ + /* also is responsible for 3b4<->3d4, pixelclock, sync */ + /* polarity, odd/even page selection, ... */ + if(LO(ax) == 0) + i10_deb("disable cpu access to video (ignored)\n"); + else + i10_deb("enable cpu access to video (ignored)\n"); + break; + +#if 0 + case 0x34: /* enable/disable cursor emulation */ + cursor_emulation = LO(ax); + LO(ax) = 0x12; + i10_deb("cursor emulation: %s\n", cursor_emulation & 1 ? "disabled" : "enabled"); + break; +#endif + + case 0x36: /* video screen on/off */ + /* VGAEMU oriented (do port 3c0 flag as well?) */ + /* (probably not: port 0x3c0|=0x20 -> all overscan color) */ + /* FIXME: only useful when using VGAEMU, add a #define */ + /* Will influence vga.config.video_off -> X_update_screen */ + Seq_set_index(1); /* sequencer: clocking mode */ + if(LO(ax) == 0) { + i10_deb("turn video screen on (partially ignored)\n"); + Seq_write_value(vga.seq.data[1] & ~0x20); + /* bit 0x20 is screen refresh off */ + } else { + i10_deb("turn video screen off (partially ignored)\n"); + Seq_write_value(vga.seq.data[1] | 0x20); + /* bit 0x20 is screen refresh off */ + } +#if 0 + LO(ax) = 0x12; +#endif + break; + + default: + i10_msg("video subsys config: function 0x%02x NOT IMPLEMENTED\n", LO(bx)); + } + break; + + + case 0x13: /* write string */ + { + int with_attr = (LO(ax) & 2) >> 1; + unsigned page = HI(bx); + unsigned char attr = LO(bx); + unsigned len = LWORD(ecx); + unsigned int str = SEGOFF2LINEAR(SREG(es), LWORD(ebp)); + unsigned old_x, old_y; + + reset_idle(0); + old_x = get_bios_cursor_x_position(page); + old_y = get_bios_cursor_y_position(page); + + set_cursor_pos(page, LO(dx), HI(dx)); + + i10_deb( + "write string: page %u, x.y %d.%d, attr 0x%02x, len %u, addr 0x%04x:0x%04x\n", + page, LO(dx), HI(dx), attr, len, SREG(es), LWORD(ebp) + ); +#if DEBUG_INT10 + i10_deb("write string: str \""); + { + unsigned u; + + for(u = 0; u < len; u++) + v_printf("%c", READ_BYTE(str+u) >= ' ' && + READ_BYTE(str+u) < 0x7f ? READ_BYTE(str+u) : ' '); + v_printf("\"\n"); + } +#endif + + if(with_attr) { + while(len--) { + tty_char_out(READ_BYTE(str), page, READ_BYTE(str+1)); + str += 2; + } + } + else { + while(len--) tty_char_out(READ_BYTE(str++), page, attr); + } + + if(!(LO(ax) & 1)) { /* no cursor update */ + set_cursor_pos(page, old_x, old_y); + } + } + break; + + case 0x1a: /* get/set display combo */ + if(LO(ax) == 0) { + int active_dcc, alternate_dcc; + + LO(ax) = 0x1a; /* valid function=0x1a */ + get_dcc(&active_dcc, &alternate_dcc); + LO(bx) = active_dcc; + HI(bx) = alternate_dcc; + i10_deb("get display combo: active 0x%02x, alternate 0x%2x\n", LO(bx), HI(bx)); + } + else { + i10_msg("set display combo: NOT IMPLEMENTED\n"); + } + break; + + + case 0x1b: /* functionality/state information */ + if(LWORD(ebx) == 0) { + i10_deb("get functionality/state info\n"); + return_state(SEGOFF2LINEAR(SREG(es), LWORD(edi))); + LO(ax) = 0x1b; + } else { + i10_msg("unknown functionality/state request: 0x%04x", LWORD(ebx)); + } + break; + + + case 0x1c: { /* save/restore video state */ + unsigned base = _BX; + if (LO(ax) > 2) + break; + switch(LO(ax)) { + case 0: { + unsigned size = 0; + i10_msg("save/restore: return state buffer size, cl=%x\n", LO(cx)); + if (LO(cx) & 1) { + /* video hardware */ + size += 0x46; + } + if (LO(cx) & 2) { + /* BIOS */ + size += 96; + } + if (LO(cx) & 4) { + /* DAC */ + size += 0x304; + } + LWORD(ebx) = (size + 63)/64; + break; + } + case 1: + i10_msg("save/restore: save state, cl=%x\n", LO(cx)); + if (LO(cx) & 1) { + unsigned char buf[0x46]; + unsigned crtc, ind; + + /* select crtc base address */ + crtc = (port_inb(MISC_OUTPUT_R) & 1) ? 0x3d4 : 0x3b4; + + buf[0x0] = port_inb(SEQUENCER_INDEX); + buf[0x1] = port_inb(crtc); + buf[0x2] = port_inb(GFX_INDEX); + /* feature control */ + buf[0x4] = port_inb(FEATURE_CONTROL_R); + + for (ind = 1; ind < 5; ind++) { + port_outb(SEQUENCER_INDEX, ind); + buf[0x4+ind] = port_inb(SEQUENCER_DATA); + } + port_outb(SEQUENCER_INDEX, 0); + buf[0x9] = port_inb(SEQUENCER_DATA); + + for (ind = 0; ind < 25; ind++) { + port_outb(crtc, ind); + buf[0x0a+ind] = port_inb(crtc + 1); + } + + /* reset flipflop ! */ + port_inb(crtc + 0x6); + buf[0x3] = port_inb(ATTRIBUTE_INDEX); + for (ind = 0; ind < 20; ind++) { + port_inb(crtc + 0x6); + port_outb(ATTRIBUTE_INDEX, ind); + buf[0x23 + ind] = port_inb(ATTRIBUTE_DATA); + } + port_inb(crtc + 0x6); + port_outb(ATTRIBUTE_INDEX, buf[0x3]); + port_inb(crtc + 0x6); + + for (ind = 0; ind < 9; ind++) { + port_outb(GFX_INDEX, ind); + buf[0x37+ind] = port_inb(GFX_DATA); + } + + buf[0x40] = crtc & 0xff; + buf[0x41] = crtc >> 8; + /* VGA latches */ + memcpy(&buf[0x42], vga.latch, 4); + MEMCPY_2DOS(SEGOFF2LINEAR(_ES, base), buf, sizeof(buf)); + base += sizeof(buf); + } + if (LO(cx) & 2) { + MEMCPY_DOS2DOS(SEGOFF2LINEAR(_ES, base), 0x449, 96); + base += 96; + } + if (LO(cx) & 4) { + unsigned char buf[0x304]; + unsigned ind; + buf[0] = port_inb(DAC_STATE); + buf[1] = port_inb(DAC_WRITE_INDEX); + buf[2] = port_inb(DAC_PEL_MASK); + port_outb(DAC_READ_INDEX, 0x00); + for(ind = 0; ind < 768; ind++) + buf[0x3 + ind] = port_inb(DAC_DATA); + buf[0x303] = port_inb(COLOR_SELECT); + MEMCPY_2DOS(SEGOFF2LINEAR(SREG(es), base), buf, sizeof(buf)); + base += sizeof(buf); + } + break; + case 2: + i10_msg("save/restore: restore state, cl=%x\n", LO(cx)); + if (LO(cx) & 1) { + unsigned char buf[0x46]; + unsigned crtc, ind; + MEMCPY_2UNIX(buf, SEGOFF2LINEAR(SREG(es), base), sizeof(buf)); + base += sizeof(buf); + crtc = buf[0x40] | (buf[0x41] << 8); + for (ind = 1; ind < 5; ind++) + port_outw(SEQUENCER_INDEX, ind | (buf[0x04+ind] << 8)); + port_outw(SEQUENCER_INDEX, buf[0x09] << 8); + /* disable write protection to index 0-7 */ + port_outw(crtc, 0x0011); + for (ind = 0; ind < 25; ind++) + port_outw(crtc, ind | (buf[0x0a+ind] << 8)); + /* select crtc base address */ + port_outb(MISC_OUTPUT_W, (port_inb(MISC_OUTPUT_R) & ~0x01) | (crtc == 0x3d4)); + /* reset flipflop ! */ + port_inb(crtc + 0x6); + for (ind = 0; ind < 20; ind++) { + port_outb(ATTRIBUTE_INDEX, ind); + port_outb(ATTRIBUTE_INDEX, buf[0x23+ind]); + } + port_outb(ATTRIBUTE_INDEX, buf[0x3]); + port_inb(crtc + 0x6); + for (ind = 0; ind < 9; ind++) + port_outw(GFX_INDEX, ind | (buf[0x37+ind] << 8)); + + port_outb(SEQUENCER_INDEX, buf[0x0]); + port_outb(crtc, buf[0x1]); + port_outb(GFX_INDEX, buf[0x2]); + /* feature control */ + port_outb(crtc + 0x6, buf[0x4]); + /* VGA latches */ + memcpy(vga.latch, &buf[0x42], 4); + } + if (LO(cx) & 2) { + MEMCPY_DOS2DOS(0x449, SEGOFF2LINEAR(_ES, base), 96); + base += 96; + } + if (LO(cx) & 4) { + unsigned char buf[0x304]; + unsigned ind; + MEMCPY_2UNIX(buf, SEGOFF2LINEAR(_ES, base), sizeof(buf)); + base += sizeof(buf); + port_outb(DAC_PEL_MASK, buf[2]); + port_outb(DAC_WRITE_INDEX, 0x00); + for(ind = 0; ind < 768; ind++) + port_outb(DAC_DATA, buf[0x3 + ind]); + port_outb(COLOR_SELECT, buf[0x303]); + if (buf[0] & 3) + port_outb(DAC_READ_INDEX, buf[1]); + else + port_outb(DAC_WRITE_INDEX, buf[1]); + } + break; + } + LO(ax) = 0x1c; + break; + } + + + case 0x4f: /* vesa interrupt */ + do_vesa_int(); + break; + + + case 0xcc: /* called from NC 5.0 */ + _CX = 0; _AL = 0xff; + i10_deb("obscure function 0x%02x\n", HI(ax)); + break; + + + case 0xfe: /* get shadow buffer..return unchanged */ + case 0xff: /* update shadow buffer...do nothing */ + i10_deb("obscure function 0x%02x\n", HI(ax)); + break; + + + default: + i10_msg("unknown video int 0x%04x\n", LWORD(eax)); + break; + } + return 1; +} + +void video_mem_setup(void) +{ + int co, li; + + WRITE_BYTE(BIOS_CURRENT_SCREEN_PAGE, 0); + + li = LI; + co = CO; + if (config.term) + gettermcap(0, &co, &li); + + WRITE_WORD(BIOS_SCREEN_COLUMNS, co); /* chars per line */ + WRITE_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1, li - 1); /* lines on screen - 1 */ + WRITE_WORD(BIOS_VIDEO_MEMORY_USED, TEXT_SIZE(co,li)); /* size of video regen area in bytes */ + + WRITE_WORD(BIOS_CURSOR_SHAPE, (bios_configuration&MDA_CONF_SCREEN_MODE)?0x0A0B:0x0607); +#if 0 + /* This is needed in the video stuff. Grabbed from boot(). */ + if ((bios_configuration & MDA_CONF_SCREEN_MODE) == MDA_CONF_SCREEN_MODE) { + WRITE_WORD(BIOS_VIDEO_PORT, 0x3b4); /* base port of CRTC - IMPORTANT! */ + video_mode = 7; + } else { + WRITE_WORD(BIOS_VIDEO_PORT, 0x3d4); /* base port of CRTC - IMPORTANT! */ + video_mode = 3; + } + WRITE_BYTE(BIOS_VIDEO_MODE, video_mode); + + WRITE_BYTE(BIOS_VDU_CONTROL, 9); /* current 3x8 (x=b or d) value */ + + WRITE_WORD(BIOS_VIDEO_MEMORY_ADDRESS, 0);/* offset of current page in buffer */ + + WRITE_WORD(BIOS_FONT_HEIGHT, 16); +#endif + /* XXX - these are the values for VGA color! + should reflect the real display hardware. */ + WRITE_BYTE(BIOS_VIDEO_INFO_0, 0x60); + WRITE_BYTE(BIOS_VIDEO_INFO_1, 0xF9); + WRITE_BYTE(BIOS_VIDEO_INFO_2, 0x51); + + /* XXX - This usage isn't quite correct: this byte should be an index + into a table. Requires modification of our BIOS, and setting up + lots of tables*/ + WRITE_BYTE(BIOS_VIDEO_COMBO, video_combo); + + if (!config.vga) { + WRITE_DWORD(BIOS_VIDEO_SAVEPTR, 0); /* pointer to video table */ + /* point int 1f to the default 8x8 graphics font for high characters */ + SETIVEC(0x1f, 0xc000, vgaemu_bios.font_8 + 128 * 8); + } + else if (!config.vbios_post) { + Bit32u p, q; + Bit16u vc; + + i10_msg("Now initialising 0x40:a8-ab\n"); + WRITE_DWORD(BIOS_VIDEO_SAVEPTR, int_bios_area[BIOS_VIDEO_SAVEPTR/4]); + + /* many BIOSes use this: take as fallback value */ + WRITE_BYTE(BIOS_VIDEO_COMBO, 0xb); + /* correct BIOS_VIDEO_COMBO value */ + p = READ_DWORD(BIOS_VIDEO_SAVEPTR) + 0x10; + /* [VGA only] ptr to Secondary Save Pointer Table, must be valid */ + p = rFAR_PTR(Bit32u, p); + p = READ_DWORD(p) + 0x2; + /* ptr to Display Combination Code Table, must be valid */ + p = rFAR_PTR(Bit32u, p); + p = READ_DWORD(p) + 0x4; + /* Each pair of bytes gives a valid display combination */ + q = p = rFAR_PTR(Bit32u, p); + do { + vc = READ_WORD(q); + if (vc == video_combo || vc == (video_combo << 8)) { + WRITE_BYTE(BIOS_VIDEO_COMBO, (q-p)/2); + i10_msg("found video_combo: %x\n", (q-p)/2); + break; + } + q += 2; + } while ((vc & 0xff) < 0xd && vc < 0xd00); + } +} diff --git a/src/base/bios/int16.c b/src/base/bios/int16.c new file mode 100644 index 0000000..ed4fd88 --- /dev/null +++ b/src/base/bios/int16.c @@ -0,0 +1,242 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* KEYBOARD BIOS ROUTINE */ +#include +#include +#include "bios.h" +#include "cpu.h" +#include "emu.h" +#include "timers.h" +#include "int.h" +#include "memory.h" +#include "coopth.h" +#include "keyboard/keyb_server.h" + +#define set_typematic_rate() +#define adjust_keyclick() +enum { + NON_EXTENDED, EXTENDED +}; + +/* SUBROUTINE: Dumb down a key for nonextend keyboards + * on return: + * -1 => ignore/ditch key, otherwise valid + */ +static unsigned do_extended(unsigned key, int extended) +{ + unsigned char scan_code = key >> 8; + + /* Ensure we actually need to do something */ + if (extended == EXTENDED || key == 0x00e0) { + return key; + } + + /* remove 0xE0 from AL for nonextended calls */ + /* is character 0xE0 ? */ + if ((key & 0xff) == 0xe0) { + key &= ~0xff; /* yes, set to 0x00 */ + if (scan_code == 0x23) /* small cyrillic er */ + return 0x00e0; + /* Hide new scan codes */ + return scan_code >= 0x85 ? -1 : key; + } + + /* Not always alt keys but nearly */ + if ((key & 0xff) == 0 && ( + (scan_code == 0x01) || + (scan_code == 0x0e) || + (scan_code >= 0x1a && scan_code < 0x1d) || + (scan_code >= 0x27 && scan_code < 0x2a) || + (scan_code == 0x2b) || + (scan_code >= 0x33 && scan_code < 0x36) || + (scan_code == 0x37) || + (scan_code == 0x4a) || + (scan_code == 0x4c) || + (scan_code == 0x4e))) { + return -1; /* return ditched key */ + } + + /* Handle special cases */ + if (key == 0xe02f) { /* DKY_PAD_SLASH */ + return 0x352f; + } + + if (key == 0xe00d /* DKY_PAD_ENTER */ || + key == 0xe00a) { /* CTRL DKY_PAD_ENTER */ + return (key & 0xff) | 0x1c00; + } + + return (scan_code >= 0x85 ? -1 : key); /* Hide new scan codes */ +} + +/* SUBROUTINE: Get a key from the buffer if there is one. + * Translate that key if we aren't an extended key service. + * returns current value of BIOS_KEYBOARD_BUFFER_HEAD + * and sets ax=key if a key is available, else + * returns -1 + */ +static unsigned get_key(int blocking, int extended) +{ + unsigned key = -1; + unsigned keyptr; + + do { + /* get address of next char */ + while ((keyptr = READ_WORD(BIOS_KEYBOARD_BUFFER_HEAD)) == + READ_WORD(BIOS_KEYBOARD_BUFFER_TAIL)) { + if (!blocking) { + _EFLAGS |= ZF; + return -1; + } + set_IF(); + coopth_wait(); + clear_IF(); + } + /* differences for extended calls */ + key = do_extended(READ_WORD(BIOS_DATA_SEG + keyptr), extended); + if (key == -1) { + keyptr += 2; + /* check for wrap around */ + if (keyptr == READ_WORD(BIOS_KEYBOARD_BUFFER_END)) { + /* wrap - get buffer start */ + keyptr = READ_WORD(BIOS_KEYBOARD_BUFFER_START); + } + /* save it as new pointer */ + WRITE_WORD(BIOS_KEYBOARD_BUFFER_HEAD, keyptr); + } + } while (key == -1); + LWORD(eax) = key; + _EFLAGS &= ~ZF; + return keyptr; +} + +static unsigned check_key_available(int extended) +{ + unsigned keyptr = get_key(0, extended); + if(keyptr == -1) { + if(!port60_ready) + trigger_idle(); + else + reset_idle(0); + idle_enable2(500, 20, 2, "int16"); + } else { + reset_idle(1); + } + return get_key(0, extended); +} + +static void read_key(int extended) +{ + unsigned keyptr = get_key(1, extended); + + if (keyptr == -1) { + /* should not be here - blocking call */ + return; + } + + keyptr += 2; + /* check for wrap around */ + if (keyptr == READ_WORD(BIOS_KEYBOARD_BUFFER_END)) { + /* wrap - get buffer start */ + keyptr = READ_WORD(BIOS_KEYBOARD_BUFFER_START); + } + /* save it as new pointer */ + WRITE_WORD(BIOS_KEYBOARD_BUFFER_HEAD, keyptr); +} + +static void get_shift_flags(void) +{ + unsigned char fl1, fl2, fl3; + + fl1 = READ_BYTE(BIOS_KEYBOARD_FLAGS1); + /* get extended shift flags*/ + fl2 = READ_BYTE(BIOS_KEYBOARD_FLAGS2); + fl3 = READ_BYTE(BIOS_KEYBOARD_FLAGS3); + /* disallow bits 2, 3, & 7 */ + + /* clear everything but sysreq (0x04) in fl2 */ + /* and move it to the correct bit */ + + /* bits 2 & 3 come from fl3 */ + + LWORD(eax) = (((fl2 & 0x73) | ((fl2 & 0x04) << 5) | (fl3 & 0xc)) << 8) | fl1; +} + +/* SUBROUTINE: store keycode in the BIOS keyboard buffer, + * return 1 if buffer is not full. + */ +static int store_key(unsigned keycode) +{ + unsigned keyptr; + + keyptr = READ_WORD(BIOS_KEYBOARD_BUFFER_TAIL) + 2; + if (keyptr == READ_WORD(BIOS_KEYBOARD_BUFFER_END)) + keyptr = READ_WORD(BIOS_KEYBOARD_BUFFER_START); + + if (READ_WORD(BIOS_KEYBOARD_BUFFER_HEAD)==keyptr) { /* full */ + return 0; + } + + WRITE_WORD(BIOS_DATA_SEG + READ_WORD(BIOS_KEYBOARD_BUFFER_TAIL), keycode); + WRITE_WORD(BIOS_KEYBOARD_BUFFER_TAIL, keyptr); + return 1; +} + +static void store_key_in_buffer(void) +{ + /* return al=0 (success) */ + /* return al=1 (buffer full) */ + LO(ax) = (store_key(LWORD(ecx)) ? 0 : 1); +} + +static void get_kbd_features(void) +{ + _AX = 0x20; /* enh kbd functionality, nothing else */ +} + +int ___int16(void) +{ + switch(HI(ax)) + { + case 0: + int_yield(); + read_key(NON_EXTENDED); + break; + case 1: + int_yield(); + check_key_available(NON_EXTENDED); + break; + case 2: + case 0x12: + int_yield(); + get_shift_flags(); + break; + case 3: + set_typematic_rate(); + break; + case 4: + adjust_keyclick(); + break; + case 5: + store_key_in_buffer(); + break; + case 0x9: + get_kbd_features(); + break; + case 0x10: + int_yield(); + read_key(EXTENDED); + break; + case 0x11: + int_yield(); + check_key_available(EXTENDED); + break; + default: + break; + } + return 1; +} diff --git a/src/base/bios/int17.c b/src/base/bios/int17.c new file mode 100644 index 0000000..397ee5f --- /dev/null +++ b/src/base/bios/int17.c @@ -0,0 +1,92 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* This is in implementation of int 17, the printer service interrupt + It is based on Bochs' implementation, and purely communicates + with the BIOS data area and the printer ports */ + +#include "emu.h" +#include "bios.h" +#include "port.h" +#include "lpt.h" +#include "timers.h" +#ifdef X86_EMULATOR +#include "cpu-emu.h" +#endif + +int int17(void) +{ + int timeout, val8; + ioport_t addr; + + CARRY; + if (_DX >= NUM_LPTS) + return 1; + addr = READ_WORD(BIOS_ADDRESS_LPT1 + _DX * 2); + if (addr == 0) + return 1; + timeout = READ_BYTE(BIOS_LPT1_TIMEOUT + _DX) << 8; + + reset_idle(2); + + switch(_AH) + { + case 0: + port_outb(addr, _AL); + val8 = port_inb(addr + 2); + port_outb(addr + 2, val8 | 0x01); // send strobe + port_outb(addr + 2, val8 & ~0x01); + while (((val8 = port_inb(addr + 1)) & LPT_STAT_NOT_ACK) && timeout) + timeout--; + break; + case 1: + val8 = port_inb(addr + 2); + port_outb(addr + 2, val8 & ~0x04); // send init + port_outb(addr + 2, val8 | 0x04); + val8 = port_inb(addr + 1); + break; + case 2: + val8 = port_inb(addr + 1); + break; + default: + return 1; + } + /* Note: RBIL seems to be wrong on Busy bit: Table P0658 states it + * as "busy" and Table 00631 as "not busy". But, according to SeaBIOS + * sources, there is no int17 inversion on Busy bit (the inversion + * mask is 0x48 and Busy is 0x80), so Table P0658 should have stated + * it as "not busy" too. The inversion happens instead in the LPT hardware: + * http://digteh.ru/PC/LPT/ + */ + _AH = val8 ^ (LPT_STAT_NOT_ACK | LPT_STAT_NOIOERR); + /* mask out "unused" bits. Needs to get rid of the no-IRQ flag, which + * is obviously always 1. SeaBIOS doesn't do this, but PRINTFIX.COM does. + * Anderson Vulczak says: + * --- + * I had to modify bios file 17 in order to clipper detect the printer + * as READY, it was not detecting it as function "isprinter" of clipper + * reads directly from bios (i found on xharbour sources) if the bios 17 + * function will return a 0x90 value. + * --- + * So lets not force people to use PRINTFIX.COM. + */ + _AH &= ~0x06; + if (!timeout) _AH |= LPT_STAT_TIMEOUT; + NOCARRY; + + return 1; +} + +void +printer_mem_setup(void) +{ + int i; + for (i = 0; i < NUM_LPTS; i++) { + /* set the port address for each printer in bios */ + WRITE_WORD(BIOS_ADDRESS_LPT1 + i * 2, get_lpt_base(i)); + WRITE_BYTE(BIOS_LPT1_TIMEOUT + i, 20); + } +} diff --git a/src/base/bios/pci_bios.c b/src/base/bios/pci_bios.c new file mode 100644 index 0000000..2253471 --- /dev/null +++ b/src/base/bios/pci_bios.c @@ -0,0 +1,532 @@ +/* + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * PCI BIOS support for dosemu + * + * (C) Copyright 1999, Egbert Eich + */ +#include +#include +#include +#include "pci.h" +#include "emu.h" +#include "cpu.h" +#include "port.h" + +#define PCI_VERSION 0x0210 /* version 2.10 */ +#define PCI_CONFIG_1 0x01 +#define PCI_CONFIG_2 0x02 +#define MAX_DEV_PER_VENDOR_CFG1 32 +#define MAX_PCI_DEVICES 64 +#define PCI_MULTIFUNC_DEV 0x80 +#define PCI_CLASS_MASK 0xff0000 +#define PCI_SUBCLASS_MASK 0xff00 +#define PCI_CLASS_BRIDGE 0x060000 +#define PCI_SUBCLASS_BRIDGE_PCI 0x0400 +#define PCI_SUBCLASS_BRIDGE_HOST 0x0000 +#define PCI_BRIDGE_CLASS(cls) ((cls & PCI_CLASS_MASK) == PCI_CLASS_BRIDGE) +#define PCI_BRIDGE_PCI_CLASS(cls) ((cls & PCI_SUBCLASS_MASK) \ + == PCI_SUBCLASS_BRIDGE_PCI) +#define PCI_BRIDGE_HOST_CLASS(cls) ((cls & PCI_SUBCLASS_MASK) \ + == PCI_SUBCLASS_BRIDGE_HOST) + +/* variables get initialized by pcibios_init() */ +static pciPtr pciList = NULL; +struct pci_funcs *pciConfigType = NULL; +static int lastBus = 0; + +/* functions used by pci_bios() */ +static unsigned short findDevice(unsigned short device, + unsigned short vendor, int num); +static unsigned short findClass(unsigned long cls, int num); +static int interpretCfgSpace(unsigned int *pciheader,unsigned int *pcibuses, + int busidx, unsigned char dev, + unsigned char func); + +static unsigned long readPciCfg1(unsigned long reg, int len); +static void writePciCfg1(unsigned long reg, unsigned long val, int len); +static unsigned long readPciCfg2(unsigned long reg, int len); +static void writePciCfg2(unsigned long reg, unsigned long val, int len); + +static unsigned long (*readPci)(unsigned long reg, int len) = readPciCfg1; +static void (*writePci)(unsigned long reg, unsigned long val, int len) = writePciCfg1; + +static void write_dword(unsigned short loc, + unsigned short reg,unsigned long val); +static void write_word(unsigned short loc, + unsigned short reg,unsigned short val); +static void write_byte(unsigned short loc, + unsigned short reg,unsigned char val); +static unsigned long read_dword(unsigned short loc,unsigned short reg); +static unsigned short read_word(unsigned short loc,unsigned short reg); +static unsigned char read_byte(unsigned short loc,unsigned short reg); + +#define SUCCESS { clear_CF();\ + HI(ax) = PCIBIOS_STAT_SUCCESSFUL;} +#define FAIL(reason) { set_CF();\ + HI(ax) = reason;} + +void +pci_bios(void) +{ + unsigned short val; + + if (!pciConfigType) + return; + + switch (LWORD(eax)) { + case PCIBIOS_PCI_BIOS_PRESENT: + Z_printf("PCIBIOS: pcibios present\n"); + HI(ax) = 0; + clear_CF(); + if (pciConfigType->name[0] != '2') + LO(ax) = PCI_CONFIG_1; /* no special cylce */ + else + LO(ax) = PCI_CONFIG_2; /* no special cylce */ + LWORD(ebx) = PCI_VERSION; /* version 2.10 */ + REG(edx) = PCI_SIGNATURE; + LO(cx) = lastBus; + break; + case PCIBIOS_FIND_PCI_DEVICE: + if (LWORD(edx) == 0xffff) + FAIL(PCIBIOS_BAD_VENDOR_ID) + else if ((val = findDevice(LWORD(ecx),LWORD(edx),LWORD(esi))) + == 0xffff) + FAIL(PCIBIOS_DEVICE_NOT_FOUND) + else { + LWORD(ebx) = val; + SUCCESS + } + break; + case PCIBIOS_FIND_PCI_CLASS_CODE: + if ((val = findClass(REG(ecx),LWORD(esi))) == 0xffff) + FAIL(PCIBIOS_DEVICE_NOT_FOUND) + else { + LWORD(ebx) = val; + SUCCESS + } + break; + case PCIBIOS_GENERATE_SPECIAL_CYCLE: + FAIL(PCIBIOS_FUNC_NOT_SUPPORTED); + break; + case PCIBIOS_READ_CONFIG_BYTE: + if (LWORD(edi) & 0xff00) + FAIL(PCIBIOS_BAD_REGISTER_NUMBER) + LO(cx) = read_byte(LWORD(ebx),LWORD(edi)); + SUCCESS + break; + case PCIBIOS_READ_CONFIG_WORD: + if (LWORD(edi) & 0xff01) + FAIL(PCIBIOS_BAD_REGISTER_NUMBER) + LWORD(ecx) = read_word(LWORD(ebx),LWORD(edi)); + SUCCESS + break; + case PCIBIOS_READ_CONFIG_DWORD: + if (LWORD(edi) & 0xff03) + FAIL(PCIBIOS_BAD_REGISTER_NUMBER) + REG(ecx) = read_dword(LWORD(ebx),LWORD(edi)); + SUCCESS + break; + case PCIBIOS_WRITE_CONFIG_BYTE: + if (LWORD(edi) & 0xff00) + FAIL(PCIBIOS_BAD_REGISTER_NUMBER) + write_byte(LWORD(ebx),LWORD(edi),LO(cx)); + SUCCESS + break; + case PCIBIOS_WRITE_CONFIG_WORD: + if (LWORD(edi) & 0xff01) + FAIL(PCIBIOS_BAD_REGISTER_NUMBER) + write_word(LWORD(ebx),LWORD(edi),LWORD(ecx)); + SUCCESS + break; + case PCIBIOS_WRITE_CONFIG_DWORD: + if (LWORD(edi) & 0xff03) + FAIL(PCIBIOS_BAD_REGISTER_NUMBER) + write_dword(LWORD(ebx),LWORD(edi),REG(ecx)); + SUCCESS + break; + case PCIBIOS_GET_IRQ_ROUTING_OPTIONS: + FAIL(PCIBIOS_FUNC_NOT_SUPPORTED) + break; + case PCIBIOS_SET_PCI_IRQ: + FAIL(PCIBIOS_FUNC_NOT_SUPPORTED) + break; + } + return; +} + +static int numbus = 0; +static int hostbridges = 0; + +int +pcibios_init(void) +{ + unsigned int pcibuses[256]; + unsigned int pciheader[16]; + int busidx = 0; + int idx = 0; + int func = 0; + int cardnum; + int cardnum_lo = 0; + int cardnum_hi = MAX_DEV_PER_VENDOR_CFG1; + int func_hi = 8; + static int ret = -1; + + if (ret != -1) return ret; + ret = 0; + + pcibuses[0] = 0; + + Z_printf("PCI enabled\n"); + + pciConfigType = pci_check_conf(); + + if (!pciConfigType) { + Z_printf("Unable to access PCI config space\n"); + return 0; + } + + Z_printf("PCI config type %s\n",pciConfigType->name); + + if (pciConfigType->name[0] == '2') { + writePci = writePciCfg2; + readPci = readPciCfg2; + cardnum_lo = 0xC0; + cardnum_hi = 0xD0; + func_hi = 1; + } + do { + for (cardnum = cardnum_lo; cardnum < cardnum_hi; cardnum++) { + func = 0; + do { + if (!pciConfigType->check_device_present(pcibuses[busidx], + cardnum,func)) + break; + pciConfigType->read_header(pcibuses[busidx],cardnum,func, + pciheader); + func = interpretCfgSpace(pciheader,pcibuses,busidx, + cardnum,func); + if (idx++ > MAX_PCI_DEVICES) + continue; + } while (func < func_hi); + } + } while (++busidx <= numbus); + + lastBus = numbus; + ret = 1; + return 1; +} + +static unsigned short +findDevice(unsigned short device, unsigned short vendor, int num) +{ + pciPtr pci = pciList; + + Z_printf("PCIBIOS: find device %d id=%04x vend=%04x", num,device,vendor); + while (pci) { + if ((pci->vendor == vendor) && (pci->device == device) && pci->enabled) { + if (num-- == 0) { + Z_printf(" at: %04x\n",pci->bdf); + return pci->bdf; + } + } + pci=pci->next; + } + Z_printf(" not found\n"); + return 0xffff; +} + +pciRec *pcibios_find_class(unsigned long cls, int num) +{ + pciPtr pci = pciList; + + Z_printf("pcibios find device %d class %lx\n", num, cls); + while (pci) { + if ((pci->cls & 0xFFFFFF) == (cls & 0xFFFFFF)) { + if (num-- == 0) { + Z_printf(" at: %04x\n",pci->bdf); + return pci; + } + } + pci=pci->next; + } + Z_printf(" not found\n"); + return NULL; +} + +pciRec *pcibios_find_primary_vga(void) +{ + int i = 0; + pciRec *ret; + + do { + ret = pcibios_find_class(PCI_CLASS_DISPLAY_VGA << 8, i++); + } while (ret && !ret->enabled); + return ret; +} + +pciRec *pcibios_find_bdf(unsigned short bdf) +{ + pciPtr pci = pciList; + + Z_printf("pcibios find bdf %x ", bdf); + while (pci) { + if (pci->enabled && pci->bdf == bdf) { + Z_printf("class: %lx\n",pci->cls); + return pci; + } + pci=pci->next; + } + Z_printf(" not found\n"); + return NULL; +} + +static unsigned short +findClass(unsigned long cls, int num) +{ + pciPtr pci = pcibios_find_class(cls, num); + return (pci && pci->enabled) ? pci->bdf : 0xffff; +} + +static unsigned long readPciCfg1 (unsigned long reg, int len) +{ + unsigned long val; + + port_outd (PCI_CONF_ADDR, reg & ~3); + if (len == 1) + val = port_inb (PCI_CONF_DATA + (reg & 3)); + else if (len == 2) + val = port_inw (PCI_CONF_DATA + (reg & 2)); + else + val = port_ind (PCI_CONF_DATA); + port_outd (PCI_CONF_ADDR, 0); + Z_printf("PCIBIOS: reading 0x%lx from 0x%lx, len=%d\n",val,reg,len); + return val; +} + +static void writePciCfg1 (unsigned long reg, unsigned long val, int len) +{ + Z_printf("PCIBIOS writing: 0x%lx to 0x%lx, len=%d\n",val,reg,len); + port_outd (PCI_CONF_ADDR, reg & ~3); + if (len == 1) + port_outb (PCI_CONF_DATA + (reg & 3), val); + else if (len == 2) + port_outw (PCI_CONF_DATA + (reg & 2), val); + else + port_outd (PCI_CONF_DATA, val); + port_outd (PCI_CONF_ADDR, 0); +} + +static unsigned long readPciCfg2 (unsigned long reg, int len) +{ + unsigned long val; + unsigned char bus, dev, num, fn; + + bus = (reg >> 16) & 0xff; + dev = (reg >> 11) & 0x1f; + fn = (reg >> 8) & 7; + num = reg & 0xff; + + port_outb(PCI_MODE2_ENABLE_REG, (fn << 1) | 0xF0); + port_outb(PCI_MODE2_FORWARD_REG, bus); + if (len == 1) + val = port_inb (0xc000 | (dev << 8) | num); + else if (len == 2) + val = port_inw (0xc000 | (dev << 8) | num); + else + val = port_ind (0xc000 | (dev << 8) | num); + port_outb(PCI_MODE2_ENABLE_REG, 0x00); + Z_printf("PCIBIOS: reading 0x%lx from 0x%lx, len=%d\n",val,reg,len); + return val; +} + +static void writePciCfg2 (unsigned long reg, unsigned long val, int len) +{ + unsigned char bus, dev, num, fn; + + bus = (reg >> 16) & 0xff; + dev = (reg >> 11) & 0x1f; + fn = (reg >> 8) & 7; + num = reg & 0xff; + + Z_printf("PCIBIOS writing: 0x%lx to 0x%lx, len=%d\n",val,reg,len); + port_outb(PCI_MODE2_ENABLE_REG, (fn << 1) | 0xF0); + port_outb(PCI_MODE2_FORWARD_REG, bus); + if (len == 1) + port_outb (0xc000 | (dev << 8) | num, val); + else if (len == 2) + port_outw (0xc000 | (dev << 8) | num, val); + else + port_outd (0xc000 | (dev << 8) | num, val); + port_outb(PCI_MODE2_ENABLE_REG, 0x00); +} + +static void +write_dword(unsigned short loc,unsigned short reg,unsigned long val) +{ + writePci(loc << 8 | (reg & 0xfc) | PCI_EN, val, 4); +} + +static void +write_word(unsigned short loc,unsigned short reg,unsigned short word) +{ + writePci(loc << 8 | (reg & 0xfe) | PCI_EN, word, 2); +} + +static void +write_byte(unsigned short loc,unsigned short reg,unsigned char byte) +{ + writePci(loc << 8 | (reg & 0xff) | PCI_EN, byte, 1); +} + +static unsigned long +read_dword(unsigned short loc,unsigned short reg) +{ + return readPci(loc << 8 | (reg & 0xfc) | PCI_EN, 4); +} + +static unsigned short +read_word(unsigned short loc,unsigned short reg) +{ + return readPci(loc << 8 | (reg & 0xfe) | PCI_EN, 2); +} + +static unsigned char +read_byte(unsigned short loc,unsigned short reg) +{ + return readPci(loc << 8 | (reg & 0xff) | PCI_EN, 1); +} + +static int proc_bus_pci_devices_get_sizes(pciPtr pci) +{ + FILE *f; + char buf[512]; + + f = fopen("/proc/bus/pci/devices", "r"); + if (!f) { + Z_printf("PCI: Cannot open /proc/bus/pci/devices\n"); + return 0; + } + while (fgets(buf, sizeof(buf)-1, f)) { + unsigned int cnt, i, bdf, tmp; + unsigned int lens[7]; + +#define F " %08x" + cnt = sscanf(buf, "%x %x %x" F F F F F F F F F F F F F F, + &bdf, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, + &lens[0], &lens[1], &lens[2], &lens[3], &lens[4], &lens[5], + &lens[6]); +#undef F + if (cnt != 17) { + Z_printf("PCI: /proc: (read only %d items)", cnt); + fclose(f); + return 0; + } + if (pci->bdf == bdf) + for (i = 0; i < 7; i++) + pci->region[i].rawsize = lens[i]; + } + fclose(f); + Z_printf("PCI: proc_bus_pci_get_sizes done\n"); + return 1; +} + +static int +interpretCfgSpace(unsigned int *pciheader,unsigned int *pcibuses,int busidx, + unsigned char dev, unsigned char func) +{ + static const char *typestr[] = { "MEM", "IO", "ROM" }; + int tmp, i; + uint16_t command; + + pciPtr pciTmp = (pciPtr)malloc(sizeof(pciRec)); + pciTmp->next = pciList; + pciList = pciTmp; + pciTmp->bdf = pcibuses[busidx] << 8 | dev << 3 | func; + pciTmp->vendor = pciheader[0] & 0xffff; + pciTmp->device = pciheader[0] >> 16; + command = pciheader[1] & 0xffff; + pciTmp->cls = pciheader[0x02] >> 8; + pciTmp->enabled = ((command & 3) == 3); + if (PCI_BRIDGE_CLASS(pciTmp->cls)) { + if (PCI_BRIDGE_PCI_CLASS(pciTmp->cls)) { /*PCI-PCI*/ + Z_printf("PCI-PCI bridge:\n"); + /* always enable for PCI emulation */ + pciTmp->enabled = 1; + tmp = (pciheader[0x6] >> 8) & 0xff; + if (tmp > 0) + pcibuses[++numbus] = tmp; /* secondary bus */ + } else if (PCI_BRIDGE_HOST_CLASS(pciTmp->cls)) { + Z_printf("PCI-HOST bridge:\n"); + /* always enable for PCI emulation */ + pciTmp->enabled = 1; + if (++hostbridges > 1) {/* HOST-PCI bridges*/ + numbus++; + pcibuses[numbus] = numbus; + } + } + } + memcpy(pciTmp->header, pciheader, sizeof(*pciheader) * 16); + memset(&pciTmp->region, 0, sizeof(pciTmp->region)); + if ((pciheader[3] & 0x007f0000) == 0) { + int got_sizes = proc_bus_pci_devices_get_sizes(pciTmp); + for (i = 0; i < 7; i++) { + unsigned long mask, base, size, pci_val, pci_val1; + unsigned long reg = PCI_BASE_ADDRESS_0 + (i << 2); + int type; + + if (i == 6) reg = PCI_ROM_ADDRESS; + pci_val = pciheader[reg/4]; + if (pci_val == 0xffffffff || pci_val == 0) + continue; + if (i == 6) { + mask = PCI_ROM_ADDRESS_MASK; + type = 2; + } else if (pci_val & PCI_BASE_ADDRESS_SPACE_IO) { + mask = PCI_BASE_ADDRESS_IO_MASK & 0xffff; + type = PCI_BASE_ADDRESS_SPACE_IO; + } else { /* memory */ + mask = PCI_BASE_ADDRESS_MEM_MASK; + type = PCI_BASE_ADDRESS_SPACE_MEMORY; + } + base = pci_val & mask; + if (!base) + continue; + pciTmp->region[i].base = base; + pciTmp->region[i].type = type; + if (!got_sizes) { + pciConfigType->write(pcibuses[busidx], dev, func, reg, 0xffffffff, 4); + pci_val1 = pciConfigType->read(pcibuses[busidx], dev, func, reg, 4); + pciConfigType->write(pcibuses[busidx], dev, func, reg, pci_val, 4); + pciTmp->region[i].rawsize = pci_val1; + } + if (pciTmp->region[i].rawsize == 0) { + pciTmp->region[i].base = 0; + continue; + } + size = pciTmp->region[i].rawsize & mask; + size = (size & ~(size - 1)) - 1; + Z_printf("PCI: found %s region at %#lx [%#lx] (%lx,%lx)\n", + typestr[type], base, size, pci_val, pciTmp->region[i].rawsize); + pciTmp->region[i].size = size; + } + } + + Z_printf("bus:%i dev:%i func:%i vend:0x%x dev:0x%x" + " class:0x%lx bdf:0x%x\n",pcibuses[busidx], + dev,func,pciTmp->vendor,pciTmp->device, + pciTmp->cls,pciTmp->bdf); + if ((func == 0) + && ((((pciheader[0x03] >> 16) & 0xff) + & PCI_MULTIFUNC_DEV)==0)) + func = 8; + else + func++; + return func; +} + diff --git a/src/base/bios/setup.c b/src/base/bios/setup.c new file mode 100644 index 0000000..d1ed2b1 --- /dev/null +++ b/src/base/bios/setup.c @@ -0,0 +1,244 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* This is a C implementation of the BIOS memory setup. The int vector + table and variables at 0040:xxxx are initialized. */ + +#include "emu.h" +#include "bios.h" +#include "memory.h" +#include "hlt.h" +#include "coopth.h" +#include "lowmem.h" +#include "int.h" +#include "iodev.h" +#include "virq.h" +#include "vint.h" +#include "emm.h" +#include "xms.h" +#include "hma.h" +#include "emudpmi.h" +#include "ipx.h" +#include "serial.h" +#include "joystick.h" +#include "utilities.h" +#include "doshelpers.h" +#include "mhpdbg.h" +#include "plugin_config.h" + +static int li_tid; +unsigned int bios_configuration; + +static void install_int_10_handler (void) +{ + unsigned int ptr; + + if (!config.mouse.intdrv) return; + /* grab int10 back from video card for mouse */ + ptr = SEGOFF2LINEAR(BIOSSEG, bios_f000_int10_old); + m_printf("ptr is at %x; ptr[0] = %x, ptr[1] = %x\n",ptr,READ_WORD(ptr),READ_WORD(ptr+2)); + WRITE_WORD(ptr, IOFF(0x10)); + WRITE_WORD(ptr + 2, ISEG(0x10)); + m_printf("after store, ptr[0] = %x, ptr[1] = %x\n",READ_WORD(ptr),READ_WORD(ptr+2)); + /* Otherwise this isn't safe */ + SETIVEC(0x10, INT10_WATCHER_SEG, INT10_WATCHER_OFF); +} + +/* + * DANG_BEGIN_FUNCTION bios_mem_setup + * + * description: + * Set up all memory areas as would be present on a typical i86 during + * the boot phase. + * + * DANG_END_FUNCTION + */ +static inline void bios_mem_setup(void) +{ + int day_rollover; + int b; + + video_mem_setup(); + serial_mem_setup(); + printer_mem_setup(); + + WRITE_DWORD(BIOS_TICK_ADDR, get_linux_ticks(0, &day_rollover)); + WRITE_BYTE(TICK_OVERFLOW_ADDR, day_rollover); + + /* show 0 serial ports and 3 parallel ports, maybe a mouse, game card and the + * configured number of floppy disks + */ + CONF_NFLOP(bios_configuration, config.fdisks); + CONF_NSER(bios_configuration, _min(config.num_ser, NUM_COMS)); + CONF_NLPT(bios_configuration, _min(config.num_lpt, NUM_LPTS)); + if (config.mouse.intdrv) + bios_configuration |= CONF_MOUSE; + + bios_configuration |= CONF_DMA; + if (joy_exist()) + bios_configuration |= CONF_GAME; + + if (config.mathco) + bios_configuration |= CONF_MATHCO; + + g_printf("CONFIG: 0x%04x binary: ", bios_configuration); + for (b = 15; b >= 0; b--) + g_printf("%s%s", (bios_configuration & (1 << b)) ? "1" : "0", (b%4) ? "" : " "); + g_printf("\n"); + + WRITE_WORD(BIOS_CONFIGURATION, bios_configuration); + WRITE_WORD(BIOS_MEMORY_SIZE, config.mem_size); /* size of memory */ + WRITE_BYTE(BIOS_HARDDISK_COUNT, config.hdisks); +} + +static int initialized; +static void dosemu_reset(void); +static void bios_setup(void); + +static void late_init_thr(void *arg) +{ + if (initialized) + return; + /* if something else is to be added here, + * add the "late_init" member into dev_list instead */ + virq_setup(); + vint_setup(); + pit_late_init(); + video_late_init(); + mouse_late_init(); + mouse_client_post_init(); + + initialized = 1; +} + +void post_hook(void) +{ + LWORD(eip)++; // skip hlt + dosemu_reset(); + bios_setup(); + + /* late_init can call int10, so make it a thread */ + coopth_start(li_tid, NULL); +} + +static void bios_setup(void) +{ + int i; + + /* initially, no HMA */ + set_a20(0); + + /* init trapped interrupts called via jump */ + for (i = 0; i < 256; i++) { + if (config.vga) { + uint16_t seg, off; + unsigned int addr; + + seg = int_bios_area[i] >> 16; + off = int_bios_area[i] & 0xffff; + v_printf("int0x%x was 0x%04x:0x%04x\n", i, seg, off); + addr = SEGOFF2LINEAR(seg, off); + if (addr >= VBIOS_START && addr < VBIOS_START + VBIOS_SIZE) { + v_printf("Setting int0x%x to 0x%04x:0x%04x\n", i, seg, off); + SETIVEC(i, seg, off); + continue; + } + } + + switch (i) { + case 0x60 ... 0x67: + case 0x79 ... 0xff: + /* interrupts >= 0xc0 are NULL unless defined by DOSEMU */ + SETIVEC(i, 0, 0); + break; + case 0x68 ... 0x6f: + /* 0x68-0x6f are usually set to iret */ + SETIVEC(i, IRET_SEG, IRET_OFF); + break; + case 0x70 ... 0x78: + SETIVEC(i, BIOSSEG, EOI2_OFF); + break; + case 0 ... 7: + case 0x10 ... 0x5f: + SETIVEC(i, BIOSSEG, INT_OFF(i)); + break; + case 8: // timer + case 9: // kbd + case 11: // com2,4 + case 12: // com1,3 + case 14: // floppy + case 15: // spurious + SETIVEC(i, BIOSSEG, EOI_OFF); + break; + case 10: // re-routed from irq9/int71h (mt32) + case 13: // exception D (GPF) or SB16 IRQ + SETIVEC(i, IRET_SEG, IRET_OFF); + break; + } + } + + SETIVEC(DOS_HELPER_INT, BIOSSEG, INT_OFF(DOS_HELPER_INT)); + SETIVEC(0xe7, BIOSSEG, INT_OFF(0xe7)); + SETIVEC(0x09, INT09_SEG, INT09_OFF); + SETIVEC(0x08, INT08_SEG, INT08_OFF); + /* 0x30 and 0x31 are not vectors, they are the 5-byte long jump. + * While 0x30 is overwritten entirely, only one byte is overwritten + * in 0x31. We need to zero it out so that it at least does not + * point into random bios location. */ + SETIVEC(0x31, 0, 0); + SETIVEC(0x70, INT70_SEG, INT70_OFF); + SETIVEC(0x71, INT71_SEG, INT71_OFF); + SETIVEC(0x1e, INT1E_SEG, INT1E_OFF); + SETIVEC(0x41, INT41_SEG, INT41_OFF); + SETIVEC(0x46, INT46_SEG, INT46_OFF); + SETIVEC(0x75, INT75_SEG, INT75_OFF); + + if (config.ems_size) + SETIVEC(0x67, BIOSSEG, INT_OFF(0x67)); + if (config.pktdrv) + SETIVEC(0x60, PKTDRV_SEG, PKTDRV_OFF); + if (config.ipxsup) + SETIVEC(0x7a, BIOSSEG, INT_OFF(0x7a)); + if (config.mouse.intdrv) + SETIVEC(0x74, BIOSSEG, Mouse_ROUTINE_OFF); + + /* set up PIC */ + port_outb(0x20, 0x10); // ICW1 + port_outb(0x21, 8); // ICW2, set irq to 8 + port_outb(0x21, 1 << 2); // ICW3m, slave on irq2 + port_outb(0xa0, 0x10); // ICW1 + port_outb(0xa1, 0x70); // ICW2, set irq to 0x70 + port_outb(0xa1, 2); // ICW3s, master uses irq2 + /* mask out SB irqs or Blood game crashes */ + if (config.sound) + port_outb(0x21, (1 << config.sb_irq) | port_inb(0x21)); + + /* Install new handler for video-interrupt into bios_f000_int10ptr, + * for video initialization at f800:4200 + * If config_vbios_seg=0xe000 -> e000:3, else c000:3 + * Next will be the call to int0xe6,al=8 which starts video BIOS init + */ + install_int_10_handler(); + + bios_mem_setup(); /* setup values in BIOS area */ +} + +static void dosemu_reset(void) +{ + initialized = 0; + dos_post_boot_reset(); + iodev_reset(); /* reset all i/o devices */ + commands_plugin_inte6_reset(); + lowmem_reset(); /* release memory used by helper utilities */ +#ifdef USE_MHPDBG + mhp_debug(DBG_BOOT, 0, 0); +#endif +} + +void bios_setup_init(void) +{ + li_tid = coopth_create("late_init", late_init_thr); +} diff --git a/src/base/bios/vgabios.c b/src/base/bios/vgabios.c new file mode 100644 index 0000000..aaeed3c --- /dev/null +++ b/src/base/bios/vgabios.c @@ -0,0 +1,993 @@ +// ============================================================================================ +/* + * vgabios.c + */ +// ============================================================================================ +// +// Copyright (C) 2001-2008 the LGPL VGABios developers Team +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// ============================================================================================ +// +// This VGA Bios is specific to the plex86/bochs Emulated VGA card. +// You can NOT drive any physical vga card with it. +// +// ============================================================================================ +// +// This file contains code ripped from : +// - rombios.c of plex86 +// +// This VGA Bios contains fonts from : +// - fntcol16.zip (c) by Joseph Gil available at : +// ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip +// These fonts are public domain +// +// This VGA Bios is based on information taken from : +// - Kevin Lawton's vga card emulation for bochs/plex86 +// - Ralf Brown's interrupts list available at http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html +// - Finn Thogersons' VGADOC4b available at http://home.worldonline.dk/~finth/ +// - Michael Abrash's Graphics Programming Black Book +// - Francois Gervais' book "programmation des cartes graphiques cga-ega-vga" edited by sybex +// - DOSEMU 1.0.1 source code for several tables values and formulas +// +// Thanks for patches, comments and ideas to : +// - techt@pikeonline.net +// +// ============================================================================================ + +/* port (part) of LGPL'd VGABios: + * http://savannah.nongnu.org/projects/vgabios + * Ported to dosemu by stsp */ + +#include +#include "memory.h" +#include "bios.h" +#include "cpu.h" +#include "port.h" +#include "dosemu_debug.h" +#include "vgaemu.h" +#include "vgatables.h" +#include "vgabios.h" + +#define vga_msg(x...) v_printf("VGAEmu: " x) +#define read_byte(seg, off) (vga_read(SEGOFF2LINEAR(seg, off))) +#define write_byte(seg, off, val) (vga_write(SEGOFF2LINEAR(seg, off), val)) +#define read_word(seg, off) (vga_read_word(SEGOFF2LINEAR(seg, off))) +#define write_word(seg, off, val) (vga_write_word(SEGOFF2LINEAR(seg, off), val)) +#define outw port_outw +#define memsetb(seg, off, val, len) vga_memset(SEGOFF2LINEAR(seg, off), val, len) +#define memsetw(seg, off, val, len) vga_memsetw(SEGOFF2LINEAR(seg, off), val, len) +#define memcpyb(seg, off, sseg, soff, len) vga_memcpy(\ + SEGOFF2LINEAR(seg, off), SEGOFF2LINEAR(sseg, soff), len) +#define memcpyw(seg, off, sseg, soff, len) vga_memcpy(\ + SEGOFF2LINEAR(seg, off), SEGOFF2LINEAR(sseg, soff), (len) * 2) + +#define vgafont14 dosaddr_to_unixaddr(SEGOFF2LINEAR(0xc000, vgaemu_bios.font_14)) +#define vgafont16 dosaddr_to_unixaddr(SEGOFF2LINEAR(0xc000, vgaemu_bios.font_16)) +#define vgafont8 dosaddr_to_unixaddr(SEGOFF2LINEAR(0xc000, vgaemu_bios.font_8)) + +#define DEBUG +#define unimplemented() error("vgabios: unimplemented, %s:%i\n", \ + __func__, __LINE__); + +static vga_mode_info *get_vmi(void) +{ + return vga_emu_find_mode(READ_BYTE(BIOS_VIDEO_MODE), NULL); +} + +// -------------------------------------------------------------------------------------------- +static void vgamem_copy_pl4(Bit16u vstart, +Bit8u xstart,Bit8u ysrc,Bit8u ydest,Bit8u cols,Bit8u nbcols,Bit8u cheight) +{ + Bit16u src,dest; + Bit8u i; + + src=ysrc*cheight*nbcols+xstart+vstart; + dest=ydest*cheight*nbcols+xstart+vstart; + port_outw(VGAREG_GRDC_ADDRESS, 0x0105); + for(i=0;i>1)+xstart+vstart; + dest=((ydest*cheight*nbcols)>>1)+xstart+vstart; + for(i=0;i>1)*nbcols,0xb800,0x2000+src+(i>>1)*nbcols,cols); + else + memcpyb(0xb800,dest+(i>>1)*nbcols,0xb800,src+(i>>1)*nbcols,cols); + } +} + +// -------------------------------------------------------------------------------------------- +static void vgamem_fill_cga(Bit16u vstart, +Bit8u xstart,Bit8u ystart,Bit8u cols,Bit8u nbcols,Bit8u cheight,Bit8u attr) +{ + Bit16u dest; + Bit8u i; + + dest=((ystart*cheight*nbcols)>>1)+xstart+vstart; + for(i=0;i>1)*nbcols,attr,cols); + else + memsetb(0xb800,dest+(i>>1)*nbcols,attr,cols); + } +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_scroll( +Bit8u nblines,Bit8u attr,Bit8u rul,Bit8u cul,Bit8u rlr,Bit8u clr,Bit8u page, +Bit8u dir) +{ + Bit8u cheight,bpp,cols; + Bit16u nbcols,nbrows,i; + Bit16u address; + vga_mode_info *vmi = get_vmi(); + if (!vmi) + return; + + // page == 0xFF if current + + if(rul>rlr)return; + if(cul>clr)return; + + // Get the dimensions + nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; + nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + + // Get the current page + if(page==0xFF) + page=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + + if(rlr>=nbrows)rlr=nbrows-1; + if(clr>=nbcols)clr=nbcols-1; + if(nblines>nbrows)nblines=0; + cols=clr-cul+1; + + if(vmi->mode_class==TEXT) + { + // Compute the address + address=SCREEN_MEM_START(nbcols,nbrows,page); +#ifdef DEBUG + printf("Scroll, address %04x (%04x %04x %02x)\n",address,nbrows,nbcols,page); +#endif + + if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1) + { + memsetw(vmi->buffer_start,address,(Bit16u)attr*0x100+' ',nbrows*nbcols); + } + else + {// if Scroll up + if(dir==SCROLL_UP) + {for(i=rul;i<=rlr;i++) + { + if((i+nblines>rlr)||(nblines==0)) + memsetw(vmi->buffer_start,address+(i*nbcols+cul)*2,(Bit16u)attr*0x100+' ',cols); + else + memcpyw(vmi->buffer_start,address+(i*nbcols+cul)*2,vmi->buffer_start,((i+nblines)*nbcols+cul)*2,cols); + } + } + else + {for(i=rlr;i>=rul;i--) + { + if((ibuffer_start,address+(i*nbcols+cul)*2,(Bit16u)attr*0x100+' ',cols); + else + memcpyw(vmi->buffer_start,address+(i*nbcols+cul)*2,vmi->buffer_start,((i-nblines)*nbcols+cul)*2,cols); + if (i>rlr) break; + } + } + } + } + else + { + address=READ_WORD(BIOS_VIDEO_MEMORY_USED)*page; + cheight=read_byte(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT); + switch(vmi->type) + { + case PLANAR4: + case PLANAR1: + if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1) + { + port_outw(VGAREG_GRDC_ADDRESS, 0x0205); + memsetb(vmi->buffer_start,address,attr,nbrows*nbcols*cheight); + port_outw(VGAREG_GRDC_ADDRESS, 0x0005); + } + else + {// if Scroll up + if(dir==SCROLL_UP) + {for(i=rul;i<=rlr;i++) + { + if((i+nblines>rlr)||(nblines==0)) + vgamem_fill_pl4(address,cul,i,cols,nbcols,cheight,attr); + else + vgamem_copy_pl4(address,cul,i+nblines,i,cols,nbcols,cheight); + } + } + else + {for(i=rlr;i>=rul;i--) + { + if((irlr) break; + } + } + } + break; + case CGA: + bpp=vmi->color_bits; + if(nblines==0&&rul==0&&cul==0&&rlr==nbrows-1&&clr==nbcols-1) + { + memsetb(vmi->buffer_start,address,attr,nbrows*nbcols*cheight*bpp); + } + else + { + if(bpp==2) + { + cul<<=1; + cols<<=1; + nbcols<<=1; + } + // if Scroll up + if(dir==SCROLL_UP) + {for(i=rul;i<=rlr;i++) + { + if((i+nblines>rlr)||(nblines==0)) + vgamem_fill_cga(address,cul,i,cols,nbcols,cheight,attr); + else + vgamem_copy_cga(address,cul,i+nblines,i,cols,nbcols,cheight); + } + } + else + {for(i=rlr;i>=rul;i--) + { + if((irlr) break; + } + } + } + break; +#ifdef DEBUG + default: + printf("Scroll in graphics mode "); + unimplemented(); +#endif + } + } +} + +void vgaemu_scroll(int x0, int y0, int x1, int y1, int n, unsigned char attr) +{ + Bit8u dir, nblines; + + vga_msg( + "vgaemu_scroll: %d lines, area %d.%d-%d.%d, attr 0x%02x\n", + n, x0, y0, x1, y1, attr + ); + + if (n >= 0) { + dir = SCROLL_UP; + nblines = n; + } else { + dir = SCROLL_DOWN; + nblines = -n; + } + biosfn_scroll(nblines,attr,y0,x0,y1,x1,0xff,dir); +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_get_cursor_pos (Bit8u page,Bit16u *shape,Bit16u *pos) +{ + // Default + *shape = 0; + *pos = 0; + + if(page>7)return; + // FIXME should handle VGA 14/16 lines + *shape = read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_TYPE); + *pos = read_word(BIOSMEM_SEG,BIOSMEM_CURSOR_POS+page*2); +} + +// -------------------------------------------------------------------------------------------- +static void write_gfx_char_pl4(Bit16u vstart,Bit8u car,Bit8u attr, + Bit8u xcurs,Bit8u ycurs,Bit8u nbcols,Bit8u cheight) +{ + Bit8u i,j,mask; + Bit8u *fdata; + Bit16u addr,dest,src; + + fdata = MEM_BASE32(IVEC(0x43)); + addr=xcurs+ycurs*cheight*nbcols+vstart; + src = car * cheight; + port_outw(VGAREG_SEQU_ADDRESS, 0x0f02); + port_outw(VGAREG_GRDC_ADDRESS, 0x0205); + if(attr&0x80) + { + port_outw(VGAREG_GRDC_ADDRESS, 0x1803); + } + else + { + port_outw(VGAREG_GRDC_ADDRESS, 0x0003); + } + for(i=0;i>j; + port_outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08); + read_byte(0xa000,dest); + if(fdata[src+i]&mask) + { + write_byte(0xa000,dest,attr&0x0f); + } + else + { + write_byte(0xa000,dest,0x00); + } + } + } +#if 0 +ASM_START + mov dx, # VGAREG_GRDC_ADDRESS + mov ax, #0xff08 + out dx, ax + mov ax, #0x0005 + out dx, ax + mov ax, #0x0003 + out dx, ax +ASM_END +#else + port_outw(VGAREG_GRDC_ADDRESS, 0xff08); + port_outw(VGAREG_GRDC_ADDRESS, 0x0005); + port_outw(VGAREG_GRDC_ADDRESS, 0x0003); +#endif +} + +// -------------------------------------------------------------------------------------------- +static void write_gfx_char_cga(Bit16u vstart,Bit8u car,Bit8u attr, + Bit8u xcurs,Bit8u ycurs,Bit8u nbcols,Bit8u bpp) +{ + Bit8u i,j,mask,data; + Bit8u *fdata; + Bit16u addr,dest,src; + + if (car < 0x80) + { + fdata = vgafont8; + } + else + { + fdata = MEM_BASE32(IVEC(0x1f)); + fdata -= 0x80 * 8; + } + addr=(xcurs*bpp)+ycurs*320+vstart; + src = car * 8; + for(i=0;i<8;i++) + { + dest=addr+(i>>1)*80; + if (i & 1) dest += 0x2000; + mask = 0x80; + if (bpp == 1) + { + if (attr & 0x80) + { + data = read_byte(0xb800,dest); + } + else + { + data = 0x00; + } + for(j=0;j<8;j++) + { + if (fdata[src+i] & mask) + { + if (attr & 0x80) + { + data ^= (attr & 0x01) << (7-j); + } + else + { + data |= (attr & 0x01) << (7-j); + } + } + mask >>= 1; + } + write_byte(0xb800,dest,data); + } + else + { + while (mask > 0) + { + if (attr & 0x80) + { + data = read_byte(0xb800,dest); + } + else + { + data = 0x00; + } + for(j=0;j<4;j++) + { + if (fdata[src+i] & mask) + { + if (attr & 0x80) + { + data ^= (attr & 0x03) << ((3-j)*2); + } + else + { + data |= (attr & 0x03) << ((3-j)*2); + } + } + mask >>= 1; + } + write_byte(0xb800,dest,data); + dest += 1; + } + } + } +} + +// -------------------------------------------------------------------------------------------- +static void write_gfx_char_lin(Bit16u vstart,Bit8u car,Bit8u attr, + Bit8u xcurs,Bit8u ycurs,Bit8u nbcols) +{ + Bit8u i,j,mask,data; + Bit8u *fdata; + Bit16u addr,dest,src; + + fdata = MEM_BASE32(IVEC(0x43)); + addr=xcurs*8+ycurs*nbcols*64+vstart; + src = car * 8; + for(i=0;i<8;i++) + { + dest=addr+i*nbcols*8; + mask = 0x80; + for(j=0;j<8;j++) + { + data = 0x00; + if (fdata[src+i] & mask) + { + data = attr; + } + write_byte(0xa000,dest+j,data); + mask >>= 1; + } + } +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_set_cursor_pos(Bit8u page,Bit16u cursor) +{ + Bit8u xcurs,ycurs,current; + Bit16u nbcols,nbrows,address,crtc_addr; + + // Should not happen... + if(page>7)return; + + // Bios cursor pos + write_word(BIOSMEM_SEG, BIOSMEM_CURSOR_POS+2*page, cursor); + + // Set the hardware cursor + current=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + if(page==current) + { + // Get the dimensions + nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; + + xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; + + // Calculate the address knowing nbcols nbrows and page num + address=SCREEN_IO_START(nbcols,nbrows,page)+xcurs+ycurs*nbcols; + + // CRTC regs 0x0e and 0x0f + crtc_addr=read_word(BIOSMEM_SEG,BIOSMEM_CRTC_ADDRESS); + port_outb(crtc_addr,0x0e); + port_outb(crtc_addr+1,(address&0xff00)>>8); + port_outb(crtc_addr,0x0f); + port_outb(crtc_addr+1,address&0x00ff); + } +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_write_teletype(Bit8u car,Bit8u page,Bit8u attr,Bit8u flag) +{// flag = WITH_ATTR / NO_ATTR + + Bit8u cheight,xcurs,ycurs,bpp; + Bit16u nbcols,nbrows,address; + Bit16u cursor,dummy; + vga_mode_info *vmi = get_vmi(); + if (!vmi) + return; + + // special case if page is 0xff, use current page + if(page==0xff) + page=read_byte(BIOSMEM_SEG,BIOSMEM_CURRENT_PAGE); + + // Get the cursor pos for the page + biosfn_get_cursor_pos(page,&dummy,&cursor); + xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; + + // Get the dimensions + nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; + nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + + switch(car) + { + case 7: + //FIXME should beep + break; + + case 8: + if(xcurs>0)xcurs--; + break; + + case '\r': + xcurs=0; + break; + + case '\n': + ycurs++; + break; + + case '\t': + do + { + biosfn_write_teletype(' ',page,attr,flag); + biosfn_get_cursor_pos(page,&dummy,&cursor); + xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; + }while(xcurs%8==0); + break; + + default: + + if(vmi->mode_class==TEXT) + { + // Compute the address + address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2; + + // Write the char + write_byte(vmi->buffer_start,address,car); + + if(flag==WITH_ATTR) + write_byte(vmi->buffer_start,address+1,attr); + } + else + { + address=READ_WORD(BIOS_VIDEO_MEMORY_USED)*page; + cheight=read_byte(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT); + bpp=vmi->color_bits; + switch(vmi->type) + { + case PLANAR4: + case PLANAR1: + write_gfx_char_pl4(address,car,attr,xcurs,ycurs,nbcols,cheight); + break; + case CGA: + write_gfx_char_cga(address,car,attr,xcurs,ycurs,nbcols,bpp); + break; + case LINEAR8: + write_gfx_char_lin(address,car,attr,xcurs,ycurs,nbcols); + break; +#ifdef DEBUG + default: + unimplemented(); +#endif + } + } + xcurs++; + } + + // Do we need to wrap ? + if(xcurs==nbcols) + {xcurs=0; + ycurs++; + } + + // Do we need to scroll ? + if(ycurs==nbrows) + { + if(vmi->mode_class==TEXT) + { + address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+(ycurs-1)*nbcols)*2; + attr=read_byte(vmi->buffer_start,address+1); + biosfn_scroll(0x01,attr,0,0,nbrows-1,nbcols-1,page,SCROLL_UP); + } + else + { + biosfn_scroll(0x01,0x00,0,0,nbrows-1,nbcols-1,page,SCROLL_UP); + } + ycurs-=1; + } + + // Set the cursor for the page + cursor=ycurs; cursor<<=8; cursor+=xcurs; + biosfn_set_cursor_pos(page,cursor); +} + +void vgaemu_put_char(unsigned char c, unsigned char page, unsigned char attr) +{ + vga_msg( + "vgaemu_put_char: page %d, char 0x%02x, attr 0x%02x\n", + page, c, attr + ); + + biosfn_write_teletype(c, page, attr, NO_ATTR); +} + +#if 0 +// -------------------------------------------------------------------------------------------- +static void biosfn_write_string(Bit8u flag,Bit8u page,Bit8u attr,Bit16u count, + Bit8u row,Bit8u col,Bit16u seg,Bit16u offset) +{ + Bit16u newcurs,oldcurs,dummy; + Bit8u car; + + // Read curs info for the page + biosfn_get_cursor_pos(page,&dummy,&oldcurs); + + // if row=0xff special case : use current cursor position + if(row==0xff) + {col=oldcurs&0x00ff; + row=(oldcurs&0xff00)>>8; + } + + newcurs=row; newcurs<<=8; newcurs+=col; + biosfn_set_cursor_pos(page,newcurs); + + while(count--!=0) + { + car=read_byte(seg,offset++); + if((flag&0x02)!=0) + attr=read_byte(seg,offset++); + + biosfn_write_teletype(car,page,attr,WITH_ATTR); + } + + // Set back curs pos + if((flag&0x01)==0) + biosfn_set_cursor_pos(page,oldcurs); +} +#endif + +// -------------------------------------------------------------------------------------------- +static void biosfn_write_char_attr (Bit8u car,Bit8u page,Bit8u attr, + Bit16u count) +{ + Bit8u cheight,xcurs,ycurs,bpp; + Bit16u nbcols,nbrows,address; + Bit16u cursor,dummy; + vga_mode_info *vmi = get_vmi(); + if (!vmi) + return; + + // Get the cursor pos for the page + biosfn_get_cursor_pos(page,&dummy,&cursor); + xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; + + // Get the dimensions + nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; + nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + + if(vmi->mode_class==TEXT) + { + // Compute the address + address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2; + + dummy=((Bit16u)attr<<8)+car; + memsetw(vmi->buffer_start,address,dummy,count); + } + else + { + address=READ_WORD(BIOS_VIDEO_MEMORY_USED)*page; + cheight=read_byte(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT); + bpp=vmi->color_bits; + while((count-->0) && (xcurstype) + { + case PLANAR4: + case PLANAR1: + write_gfx_char_pl4(address,car,attr,xcurs,ycurs,nbcols,cheight); + break; + case CGA: + write_gfx_char_cga(address,car,attr,xcurs,ycurs,nbcols,bpp); + break; + case LINEAR8: + write_gfx_char_lin(address,car,attr,xcurs,ycurs,nbcols); + break; +#ifdef DEBUG + default: + unimplemented(); +#endif + } + xcurs++; + } + } +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_write_char_only (Bit8u car,Bit8u page,Bit8u attr, + Bit16u count) +{ + Bit8u cheight,xcurs,ycurs,bpp; + Bit16u nbcols,nbrows,address; + Bit16u cursor,dummy; + vga_mode_info *vmi = get_vmi(); + if (!vmi) + return; + + // Get the cursor pos for the page + biosfn_get_cursor_pos(page,&dummy,&cursor); + xcurs=cursor&0x00ff;ycurs=(cursor&0xff00)>>8; + + // Get the dimensions + nbrows=read_byte(BIOSMEM_SEG,BIOSMEM_NB_ROWS)+1; + nbcols=read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS); + + if(vmi->mode_class==TEXT) + { + // Compute the address + address=SCREEN_MEM_START(nbcols,nbrows,page)+(xcurs+ycurs*nbcols)*2; + + while(count-->0) + {write_byte(vmi->buffer_start,address,car); + address+=2; + } + } + else + { + address=READ_WORD(BIOS_VIDEO_MEMORY_USED)*page; + cheight=read_byte(BIOSMEM_SEG,BIOSMEM_CHAR_HEIGHT); + bpp=vmi->color_bits; + while((count-->0) && (xcurstype) + { + case PLANAR4: + case PLANAR1: + write_gfx_char_pl4(address,car,attr,xcurs,ycurs,nbcols,cheight); + break; + case CGA: + write_gfx_char_cga(address,car,attr,xcurs,ycurs,nbcols,bpp); + break; + case LINEAR8: + write_gfx_char_lin(address,car,attr,xcurs,ycurs,nbcols); + break; +#ifdef DEBUG + default: + unimplemented(); +#endif + } + xcurs++; + } + } +} + +void vgaemu_repeat_char_attr(unsigned char c, unsigned char page, + unsigned char attr, unsigned char count) +{ + vga_msg( + "vgaemu_repeat_char_attr: page %d, char 0x%02x, attr 0x%02x rep %d\n", + page, c, attr, count + ); + + biosfn_write_char_attr(c, page, attr, count); +} + +void vgaemu_repeat_char(unsigned char c, unsigned char page, + unsigned char attr, unsigned char count) +{ + vga_msg( + "vgaemu_repeat_char: page %d, char 0x%02x, attr 0x%02x rep %d\n", + page, c, attr, count + ); + + biosfn_write_char_only(c, page, attr, count); +} + +// -------------------------------------------------------------------------------------------- +static void biosfn_write_pixel(Bit8u BH,Bit8u AL,Bit16u CX,Bit16u DX) +{ + Bit8u mask,attr,data; + Bit16u addr; + vga_mode_info *vmi = get_vmi(); + if (!vmi) + return; + + switch(vmi->type) + { + case PLANAR4: + case PLANAR1: + addr = CX/8+DX*read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)+ + READ_WORD(BIOS_VIDEO_MEMORY_USED)*BH; + mask = 0x80 >> (CX & 0x07); + port_outw(VGAREG_GRDC_ADDRESS, (mask << 8) | 0x08); + port_outw(VGAREG_GRDC_ADDRESS, 0x0205); + data = read_byte(0xa000,addr); + if (AL & 0x80) + { + port_outw(VGAREG_GRDC_ADDRESS, 0x1803); + } + write_byte(0xa000,addr,AL); +#if 0 +ASM_START + mov dx, # VGAREG_GRDC_ADDRESS + mov ax, #0xff08 + out dx, ax + mov ax, #0x0005 + out dx, ax + mov ax, #0x0003 + out dx, ax +ASM_END +#else + port_outw(VGAREG_GRDC_ADDRESS, 0xff08); + port_outw(VGAREG_GRDC_ADDRESS, 0x0005); + port_outw(VGAREG_GRDC_ADDRESS, 0x0003); +#endif + break; + case CGA: + if(vmi->color_bits==2) + { + addr=(CX>>2)+(DX>>1)*80; + } + else + { + addr=(CX>>3)+(DX>>1)*80; + } + if (DX & 1) addr += 0x2000; + data = read_byte(0xb800,addr); + if(vmi->color_bits==2) + { + attr = (AL & 0x03) << ((3 - (CX & 0x03)) * 2); + mask = 0x03 << ((3 - (CX & 0x03)) * 2); + } + else + { + attr = (AL & 0x01) << (7 - (CX & 0x07)); + mask = 0x01 << (7 - (CX & 0x07)); + } + if (AL & 0x80) + { + data ^= attr; + } + else + { + data &= ~mask; + data |= attr; + } + write_byte(0xb800,addr,data); + break; + case LINEAR8: + addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8)+ + READ_WORD(BIOS_VIDEO_MEMORY_USED)*BH; + write_byte(0xa000,addr,AL); + break; +#ifdef DEBUG + default: + unimplemented(); +#endif + } +} + +void vgaemu_put_pixel(int x, int y, unsigned char page, unsigned char attr) +{ + vga_msg( + "vgaemu_put_pixel: x.y %d.%d, page 0x%02x, attr 0x%02x\n", + x, y, page, attr + ); + biosfn_write_pixel(page, attr, x, y); +} + +// -------------------------------------------------------------------------------------------- +static unsigned char biosfn_read_pixel(Bit8u BH,Bit16u CX,Bit16u DX) +{ + Bit8u mask,attr,data,i; + Bit16u addr; + vga_mode_info *vmi = get_vmi(); + if (!vmi) + return 0xff; + + switch(vmi->type) + { + case PLANAR4: + case PLANAR1: + addr = CX/8+DX*read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)+ + READ_WORD(BIOS_VIDEO_MEMORY_USED)*BH; + mask = 0x80 >> (CX & 0x07); + attr = 0x00; + for(i=0;i<4;i++) + { + port_outw(VGAREG_GRDC_ADDRESS, (i << 8) | 0x04); + data = read_byte(0xa000,addr) & mask; + if (data > 0) attr |= (0x01 << i); + } + break; + case CGA: + addr=(CX>>2)+(DX>>1)*80; + if (DX & 1) addr += 0x2000; + data = read_byte(0xb800,addr); + if(vmi->color_bits==2) + { + attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03; + } + else + { + attr = (data >> (7 - (CX & 0x07))) & 0x01; + } + break; + case LINEAR8: + addr=CX+DX*(read_word(BIOSMEM_SEG,BIOSMEM_NB_COLS)*8)+ + READ_WORD(BIOS_VIDEO_MEMORY_USED)*BH; + attr=read_byte(0xa000,addr); + break; + default: +#ifdef DEBUG + unimplemented(); +#endif + attr = 0; + } +#if 0 + write_word(ss,AX,(read_word(ss,AX) & 0xff00) | attr); +#else + return attr; +#endif +} + +unsigned char vgaemu_get_pixel(int x, int y, unsigned char page) +{ + vga_msg( + "vgaemu_get_pixel: x.y %d.%d, page 0x%02x\n", + x, y, page + ); + return biosfn_read_pixel(page, x, y); +} diff --git a/src/base/bios/vgabios.h b/src/base/bios/vgabios.h new file mode 100644 index 0000000..283cda7 --- /dev/null +++ b/src/base/bios/vgabios.h @@ -0,0 +1,49 @@ +#ifndef vgabios_h_included +#define vgabios_h_included + +/* Types */ +#if 0 +typedef unsigned char Bit8u; +typedef unsigned short Bit16u; +typedef unsigned long Bit32u; +typedef unsigned short Boolean; +#endif + +/* Defines */ + +#define SET_AL(val8) AX = ((AX & 0xff00) | (val8)) +#define SET_BL(val8) BX = ((BX & 0xff00) | (val8)) +#define SET_CL(val8) CX = ((CX & 0xff00) | (val8)) +#define SET_DL(val8) DX = ((DX & 0xff00) | (val8)) +#define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8)) +#define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8)) +#define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8)) +#define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8)) + +#define GET_AL() ( AX & 0x00ff ) +#define GET_BL() ( BX & 0x00ff ) +#define GET_CL() ( CX & 0x00ff ) +#define GET_DL() ( DX & 0x00ff ) +#define GET_AH() ( AX >> 8 ) +#define GET_BH() ( BX >> 8 ) +#define GET_CH() ( CX >> 8 ) +#define GET_DH() ( DX >> 8 ) + +#define SET_CF() FLAGS |= 0x0001 +#define CLEAR_CF() FLAGS &= 0xfffe +#define GET_CF() (FLAGS & 0x0001) + +#define SET_ZF() FLAGS |= 0x0040 +#define CLEAR_ZF() FLAGS &= 0xffbf +#define GET_ZF() (FLAGS & 0x0040) + +#define SCROLL_DOWN 0 +#define SCROLL_UP 1 +#define NO_ATTR 2 +#define WITH_ATTR 3 + +#define SCREEN_SIZE(x,y) (((x*y*2)|0x00ff)+1) +#define SCREEN_MEM_START(x,y,p) ((((x*y*2)|0x00ff)+1)*p) +#define SCREEN_IO_START(x,y,p) ((((x*y)|0x00ff)+1)*p) + +#endif diff --git a/src/base/bios/vgatables.h b/src/base/bios/vgatables.h new file mode 100644 index 0000000..274332a --- /dev/null +++ b/src/base/bios/vgatables.h @@ -0,0 +1,624 @@ +/* + * + * BIOS Memory + * + */ +#define BIOSMEM_SEG 0x40 + +#define BIOSMEM_INITIAL_MODE 0x10 +#define BIOSMEM_CURRENT_MODE 0x49 +#define BIOSMEM_NB_COLS 0x4A +#define BIOSMEM_PAGE_SIZE 0x4C +#define BIOSMEM_CURRENT_START 0x4E +#define BIOSMEM_CURSOR_POS 0x50 +#define BIOSMEM_CURSOR_TYPE 0x60 +#define BIOSMEM_CURRENT_PAGE 0x62 +#define BIOSMEM_CRTC_ADDRESS 0x63 +#define BIOSMEM_CURRENT_MSR 0x65 +#define BIOSMEM_CURRENT_PAL 0x66 +#define BIOSMEM_NB_ROWS 0x84 +#define BIOSMEM_CHAR_HEIGHT 0x85 +#define BIOSMEM_VIDEO_CTL 0x87 +#define BIOSMEM_SWITCHES 0x88 +#define BIOSMEM_MODESET_CTL 0x89 +#define BIOSMEM_DCC_INDEX 0x8A +#define BIOSMEM_VS_POINTER 0xA8 +#define BIOSMEM_VBE_FLAG 0xB9 +#define BIOSMEM_VBE_MODE 0xBA + + +/* + * + * VGA registers + * + */ +#define VGAREG_ACTL_ADDRESS 0x3c0 +#define VGAREG_ACTL_WRITE_DATA 0x3c0 +#define VGAREG_ACTL_READ_DATA 0x3c1 + +#define VGAREG_INPUT_STATUS 0x3c2 +#define VGAREG_WRITE_MISC_OUTPUT 0x3c2 +#define VGAREG_VIDEO_ENABLE 0x3c3 +#define VGAREG_SEQU_ADDRESS 0x3c4 +#define VGAREG_SEQU_DATA 0x3c5 + +#define VGAREG_PEL_MASK 0x3c6 +#define VGAREG_DAC_STATE 0x3c7 +#define VGAREG_DAC_READ_ADDRESS 0x3c7 +#define VGAREG_DAC_WRITE_ADDRESS 0x3c8 +#define VGAREG_DAC_DATA 0x3c9 + +#define VGAREG_READ_FEATURE_CTL 0x3ca +#define VGAREG_READ_MISC_OUTPUT 0x3cc + +#define VGAREG_GRDC_ADDRESS 0x3ce +#define VGAREG_GRDC_DATA 0x3cf + +#define VGAREG_MDA_CRTC_ADDRESS 0x3b4 +#define VGAREG_MDA_CRTC_DATA 0x3b5 +#define VGAREG_VGA_CRTC_ADDRESS 0x3d4 +#define VGAREG_VGA_CRTC_DATA 0x3d5 + +#define VGAREG_MDA_WRITE_FEATURE_CTL 0x3ba +#define VGAREG_VGA_WRITE_FEATURE_CTL 0x3da +#define VGAREG_ACTL_RESET 0x3da + +#define VGAREG_MDA_MODECTL 0x3b8 +#define VGAREG_CGA_MODECTL 0x3d8 +#define VGAREG_CGA_PALETTE 0x3d9 + +/* Video memory */ +#define VGAMEM_GRAPH 0xA000 +#define VGAMEM_CTEXT 0xB800 +#define VGAMEM_MTEXT 0xB000 + +/* + * + * Tables of default values for each mode + * + */ +#define MODE_MAX 15 +//#define TEXT 0x00 +//#define GRAPH 0x01 + +#define CTEXT TEXT +#define MTEXT TEXT_MONO +//#define CGA 0x02 +#define PLANAR1 PL1 +#define PLANAR4 PL4 +#define LINEAR8 P8 + +// for SVGA +#define LINEAR15 P15 +#define LINEAR16 P16 +#define LINEAR24 P24 +#define LINEAR32 P32 + +#if 0 +typedef struct +{Bit8u svgamode; + Bit8u class; /* TEXT, GRAPH */ + Bit8u memmodel; /* CTEXT,MTEXT,CGA,PL1,PL2,PL4,P8,P15,P16,P24,P32 */ + Bit8u pixbits; + Bit16u sstart; + Bit8u pelmask; + Bit8u dacmodel; /* 0 1 2 3 */ +} VGAMODES; + +static VGAMODES vga_modes[MODE_MAX+1]= +{//mode class model bits sstart pelm dac + {0x00, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02}, + {0x01, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02}, + {0x02, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02}, + {0x03, TEXT, CTEXT, 4, 0xB800, 0xFF, 0x02}, + {0x04, GRAPH, CGA, 2, 0xB800, 0xFF, 0x01}, + {0x05, GRAPH, CGA, 2, 0xB800, 0xFF, 0x01}, + {0x06, GRAPH, CGA, 1, 0xB800, 0xFF, 0x01}, + {0x07, TEXT, MTEXT, 4, 0xB000, 0xFF, 0x00}, + {0x0D, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x01}, + {0x0E, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x01}, + {0x0F, GRAPH, PLANAR1, 1, 0xA000, 0xFF, 0x00}, + {0x10, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02}, + {0x11, GRAPH, PLANAR1, 1, 0xA000, 0xFF, 0x02}, + {0x12, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02}, + {0x13, GRAPH, LINEAR8, 8, 0xA000, 0xFF, 0x03}, + {0x6A, GRAPH, PLANAR4, 4, 0xA000, 0xFF, 0x02} +}; + +/* convert index in vga_modes[] to index in video_param_table[] */ +static Bit8u line_to_vpti[MODE_MAX+1]={ + 0x17, 0x17, 0x18, 0x18, 0x04, 0x05, 0x06, 0x07, + 0x0d, 0x0e, 0x11, 0x12, 0x1a, 0x1b, 0x1c, 0x1d, +}; + +/* Default Palette */ +#define DAC_MAX_MODEL 3 + +static Bit8u dac_regs[DAC_MAX_MODEL+1]= +{0x3f,0x3f,0x3f,0xff}; + +/* standard BIOS Video Parameter Table */ +typedef struct { + Bit8u twidth; + Bit8u theightm1; + Bit8u cheight; + Bit8u slength_l; + Bit8u slength_h; + Bit8u sequ_regs[4]; + Bit8u miscreg; + Bit8u crtc_regs[25]; + Bit8u actl_regs[20]; + Bit8u grdc_regs[9]; +} VideoParamTableEntry; + +static VideoParamTableEntry video_param_table[30] = { +{ + /* index=0x00 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x01 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x02 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x03 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x04 vga mode 0x04 */ + 40, 24, 8, 0x00, 0x08, /* tw, th-1, ch, slength */ + 0x09, 0x03, 0x00, 0x02, /* sequ_regs */ + 0x63, /* miscreg */ + 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2, + 0xff, /* crtc_regs */ + 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x01, 0x00, 0x03, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x05 vga mode 0x05 */ + 40, 24, 8, 0x00, 0x08, /* tw, th-1, ch, slength */ + 0x09, 0x03, 0x00, 0x02, /* sequ_regs */ + 0x63, /* miscreg */ + 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2, + 0xff, /* crtc_regs */ + 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x01, 0x00, 0x03, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0f, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x06 vga mode 0x06 */ + 80, 24, 8, 0x00, 0x10, /* tw, th-1, ch, slength */ + 0x01, 0x01, 0x00, 0x06, /* sequ_regs */ + 0x63, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xc2, + 0xff, /* crtc_regs */ + 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x01, 0x00, 0x01, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x07 vga mode 0x07 */ + 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */ + 0x00, 0x03, 0x00, 0x02, /* sequ_regs */ + 0x66, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3, + 0xff, /* crtc_regs */ + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x0e, 0x00, 0x0f, 0x08, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x08 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x09 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x0a no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x0b no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x0c no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x0d vga mode 0x0d */ + 40, 24, 8, 0x00, 0x20, /* tw, th-1, ch, slength */ + 0x09, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0x63, /* miscreg */ + 0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x0e vga mode 0x0e */ + 80, 24, 8, 0x00, 0x40, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0x63, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x0f no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x10 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x11 vga mode 0x0f */ + 80, 24, 14, 0x00, 0x80, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0xa3, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x08, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x12 vga mode 0x10 */ + 80, 24, 14, 0x00, 0x80, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0xa3, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x13 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x14 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x15 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x16 no mode defined */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, +{ + /* index=0x17 vga mode 0x01 */ + 40, 24, 16, 0x00, 0x08, /* tw, th-1, ch, slength */ + 0x08, 0x03, 0x00, 0x02, /* sequ_regs */ + 0x67, /* miscreg */ + 0x2d, 0x27, 0x28, 0x90, 0x2b, 0xa0, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x14, 0x1f, 0x96, 0xb9, 0xa3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x0c, 0x00, 0x0f, 0x08, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x18 vga mode 0x03 */ + 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */ + 0x00, 0x03, 0x00, 0x02, /* sequ_regs */ + 0x67, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x1f, 0x96, 0xb9, 0xa3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x0c, 0x00, 0x0f, 0x08, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0e, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x19 vga mode 0x07 */ + 80, 24, 16, 0x00, 0x10, /* tw, th-1, ch, slength */ + 0x00, 0x03, 0x00, 0x02, /* sequ_regs */ + 0x66, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, + 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3, + 0xff, /* crtc_regs */ + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x0e, 0x00, 0x0f, 0x08, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x1a vga mode 0x11 */ + 80, 29, 16, 0x00, 0x00, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0xe3, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x1b vga mode 0x12 */ + 80, 29, 16, 0x00, 0x00, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0xe3, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x1c vga mode 0x13 */ + 40, 24, 8, 0x00, 0x00, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x0e, /* sequ_regs */ + 0x63, /* miscreg */ + 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, + 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9c, 0x8e, 0x8f, 0x28, 0x40, 0x96, 0xb9, 0xa3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x41, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +{ + /* index=0x1d vga mode 0x6a */ + 100, 36, 16, 0x00, 0x00, /* tw, th-1, ch, slength */ + 0x01, 0x0f, 0x00, 0x06, /* sequ_regs */ + 0xe3, /* miscreg */ + 0x7f, 0x63, 0x63, 0x83, 0x6b, 0x1b, 0x72, 0xf0, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x8d, 0x57, 0x32, 0x00, 0x57, 0x73, 0xe3, + 0xff, /* crtc_regs */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x01, 0x00, 0x0f, 0x00, /* actl_regs */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x0f, 0xff, /* grdc_regs */ +}, +}; + +/* Mono */ +static Bit8u palette0[63+1][3]= +{ + 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, + 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, 0x2a,0x2a,0x2a, + 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f, 0x3f,0x3f,0x3f +}; + +static Bit8u palette1[63+1][3]= +{ + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f +}; + +static Bit8u palette2[63+1][3]= +{ + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x2a,0x00, 0x2a,0x2a,0x2a, + 0x00,0x00,0x15, 0x00,0x00,0x3f, 0x00,0x2a,0x15, 0x00,0x2a,0x3f, 0x2a,0x00,0x15, 0x2a,0x00,0x3f, 0x2a,0x2a,0x15, 0x2a,0x2a,0x3f, + 0x00,0x15,0x00, 0x00,0x15,0x2a, 0x00,0x3f,0x00, 0x00,0x3f,0x2a, 0x2a,0x15,0x00, 0x2a,0x15,0x2a, 0x2a,0x3f,0x00, 0x2a,0x3f,0x2a, + 0x00,0x15,0x15, 0x00,0x15,0x3f, 0x00,0x3f,0x15, 0x00,0x3f,0x3f, 0x2a,0x15,0x15, 0x2a,0x15,0x3f, 0x2a,0x3f,0x15, 0x2a,0x3f,0x3f, + 0x15,0x00,0x00, 0x15,0x00,0x2a, 0x15,0x2a,0x00, 0x15,0x2a,0x2a, 0x3f,0x00,0x00, 0x3f,0x00,0x2a, 0x3f,0x2a,0x00, 0x3f,0x2a,0x2a, + 0x15,0x00,0x15, 0x15,0x00,0x3f, 0x15,0x2a,0x15, 0x15,0x2a,0x3f, 0x3f,0x00,0x15, 0x3f,0x00,0x3f, 0x3f,0x2a,0x15, 0x3f,0x2a,0x3f, + 0x15,0x15,0x00, 0x15,0x15,0x2a, 0x15,0x3f,0x00, 0x15,0x3f,0x2a, 0x3f,0x15,0x00, 0x3f,0x15,0x2a, 0x3f,0x3f,0x00, 0x3f,0x3f,0x2a, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f +}; + +static Bit8u palette3[256][3]= +{ + 0x00,0x00,0x00, 0x00,0x00,0x2a, 0x00,0x2a,0x00, 0x00,0x2a,0x2a, 0x2a,0x00,0x00, 0x2a,0x00,0x2a, 0x2a,0x15,0x00, 0x2a,0x2a,0x2a, + 0x15,0x15,0x15, 0x15,0x15,0x3f, 0x15,0x3f,0x15, 0x15,0x3f,0x3f, 0x3f,0x15,0x15, 0x3f,0x15,0x3f, 0x3f,0x3f,0x15, 0x3f,0x3f,0x3f, + 0x00,0x00,0x00, 0x05,0x05,0x05, 0x08,0x08,0x08, 0x0b,0x0b,0x0b, 0x0e,0x0e,0x0e, 0x11,0x11,0x11, 0x14,0x14,0x14, 0x18,0x18,0x18, + 0x1c,0x1c,0x1c, 0x20,0x20,0x20, 0x24,0x24,0x24, 0x28,0x28,0x28, 0x2d,0x2d,0x2d, 0x32,0x32,0x32, 0x38,0x38,0x38, 0x3f,0x3f,0x3f, + 0x00,0x00,0x3f, 0x10,0x00,0x3f, 0x1f,0x00,0x3f, 0x2f,0x00,0x3f, 0x3f,0x00,0x3f, 0x3f,0x00,0x2f, 0x3f,0x00,0x1f, 0x3f,0x00,0x10, + 0x3f,0x00,0x00, 0x3f,0x10,0x00, 0x3f,0x1f,0x00, 0x3f,0x2f,0x00, 0x3f,0x3f,0x00, 0x2f,0x3f,0x00, 0x1f,0x3f,0x00, 0x10,0x3f,0x00, + 0x00,0x3f,0x00, 0x00,0x3f,0x10, 0x00,0x3f,0x1f, 0x00,0x3f,0x2f, 0x00,0x3f,0x3f, 0x00,0x2f,0x3f, 0x00,0x1f,0x3f, 0x00,0x10,0x3f, + 0x1f,0x1f,0x3f, 0x27,0x1f,0x3f, 0x2f,0x1f,0x3f, 0x37,0x1f,0x3f, 0x3f,0x1f,0x3f, 0x3f,0x1f,0x37, 0x3f,0x1f,0x2f, 0x3f,0x1f,0x27, + + 0x3f,0x1f,0x1f, 0x3f,0x27,0x1f, 0x3f,0x2f,0x1f, 0x3f,0x37,0x1f, 0x3f,0x3f,0x1f, 0x37,0x3f,0x1f, 0x2f,0x3f,0x1f, 0x27,0x3f,0x1f, + 0x1f,0x3f,0x1f, 0x1f,0x3f,0x27, 0x1f,0x3f,0x2f, 0x1f,0x3f,0x37, 0x1f,0x3f,0x3f, 0x1f,0x37,0x3f, 0x1f,0x2f,0x3f, 0x1f,0x27,0x3f, + 0x2d,0x2d,0x3f, 0x31,0x2d,0x3f, 0x36,0x2d,0x3f, 0x3a,0x2d,0x3f, 0x3f,0x2d,0x3f, 0x3f,0x2d,0x3a, 0x3f,0x2d,0x36, 0x3f,0x2d,0x31, + 0x3f,0x2d,0x2d, 0x3f,0x31,0x2d, 0x3f,0x36,0x2d, 0x3f,0x3a,0x2d, 0x3f,0x3f,0x2d, 0x3a,0x3f,0x2d, 0x36,0x3f,0x2d, 0x31,0x3f,0x2d, + 0x2d,0x3f,0x2d, 0x2d,0x3f,0x31, 0x2d,0x3f,0x36, 0x2d,0x3f,0x3a, 0x2d,0x3f,0x3f, 0x2d,0x3a,0x3f, 0x2d,0x36,0x3f, 0x2d,0x31,0x3f, + 0x00,0x00,0x1c, 0x07,0x00,0x1c, 0x0e,0x00,0x1c, 0x15,0x00,0x1c, 0x1c,0x00,0x1c, 0x1c,0x00,0x15, 0x1c,0x00,0x0e, 0x1c,0x00,0x07, + 0x1c,0x00,0x00, 0x1c,0x07,0x00, 0x1c,0x0e,0x00, 0x1c,0x15,0x00, 0x1c,0x1c,0x00, 0x15,0x1c,0x00, 0x0e,0x1c,0x00, 0x07,0x1c,0x00, + 0x00,0x1c,0x00, 0x00,0x1c,0x07, 0x00,0x1c,0x0e, 0x00,0x1c,0x15, 0x00,0x1c,0x1c, 0x00,0x15,0x1c, 0x00,0x0e,0x1c, 0x00,0x07,0x1c, + + 0x0e,0x0e,0x1c, 0x11,0x0e,0x1c, 0x15,0x0e,0x1c, 0x18,0x0e,0x1c, 0x1c,0x0e,0x1c, 0x1c,0x0e,0x18, 0x1c,0x0e,0x15, 0x1c,0x0e,0x11, + 0x1c,0x0e,0x0e, 0x1c,0x11,0x0e, 0x1c,0x15,0x0e, 0x1c,0x18,0x0e, 0x1c,0x1c,0x0e, 0x18,0x1c,0x0e, 0x15,0x1c,0x0e, 0x11,0x1c,0x0e, + 0x0e,0x1c,0x0e, 0x0e,0x1c,0x11, 0x0e,0x1c,0x15, 0x0e,0x1c,0x18, 0x0e,0x1c,0x1c, 0x0e,0x18,0x1c, 0x0e,0x15,0x1c, 0x0e,0x11,0x1c, + 0x14,0x14,0x1c, 0x16,0x14,0x1c, 0x18,0x14,0x1c, 0x1a,0x14,0x1c, 0x1c,0x14,0x1c, 0x1c,0x14,0x1a, 0x1c,0x14,0x18, 0x1c,0x14,0x16, + 0x1c,0x14,0x14, 0x1c,0x16,0x14, 0x1c,0x18,0x14, 0x1c,0x1a,0x14, 0x1c,0x1c,0x14, 0x1a,0x1c,0x14, 0x18,0x1c,0x14, 0x16,0x1c,0x14, + 0x14,0x1c,0x14, 0x14,0x1c,0x16, 0x14,0x1c,0x18, 0x14,0x1c,0x1a, 0x14,0x1c,0x1c, 0x14,0x1a,0x1c, 0x14,0x18,0x1c, 0x14,0x16,0x1c, + 0x00,0x00,0x10, 0x04,0x00,0x10, 0x08,0x00,0x10, 0x0c,0x00,0x10, 0x10,0x00,0x10, 0x10,0x00,0x0c, 0x10,0x00,0x08, 0x10,0x00,0x04, + 0x10,0x00,0x00, 0x10,0x04,0x00, 0x10,0x08,0x00, 0x10,0x0c,0x00, 0x10,0x10,0x00, 0x0c,0x10,0x00, 0x08,0x10,0x00, 0x04,0x10,0x00, + + 0x00,0x10,0x00, 0x00,0x10,0x04, 0x00,0x10,0x08, 0x00,0x10,0x0c, 0x00,0x10,0x10, 0x00,0x0c,0x10, 0x00,0x08,0x10, 0x00,0x04,0x10, + 0x08,0x08,0x10, 0x0a,0x08,0x10, 0x0c,0x08,0x10, 0x0e,0x08,0x10, 0x10,0x08,0x10, 0x10,0x08,0x0e, 0x10,0x08,0x0c, 0x10,0x08,0x0a, + 0x10,0x08,0x08, 0x10,0x0a,0x08, 0x10,0x0c,0x08, 0x10,0x0e,0x08, 0x10,0x10,0x08, 0x0e,0x10,0x08, 0x0c,0x10,0x08, 0x0a,0x10,0x08, + 0x08,0x10,0x08, 0x08,0x10,0x0a, 0x08,0x10,0x0c, 0x08,0x10,0x0e, 0x08,0x10,0x10, 0x08,0x0e,0x10, 0x08,0x0c,0x10, 0x08,0x0a,0x10, + 0x0b,0x0b,0x10, 0x0c,0x0b,0x10, 0x0d,0x0b,0x10, 0x0f,0x0b,0x10, 0x10,0x0b,0x10, 0x10,0x0b,0x0f, 0x10,0x0b,0x0d, 0x10,0x0b,0x0c, + 0x10,0x0b,0x0b, 0x10,0x0c,0x0b, 0x10,0x0d,0x0b, 0x10,0x0f,0x0b, 0x10,0x10,0x0b, 0x0f,0x10,0x0b, 0x0d,0x10,0x0b, 0x0c,0x10,0x0b, + 0x0b,0x10,0x0b, 0x0b,0x10,0x0c, 0x0b,0x10,0x0d, 0x0b,0x10,0x0f, 0x0b,0x10,0x10, 0x0b,0x0f,0x10, 0x0b,0x0d,0x10, 0x0b,0x0c,0x10, + 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00, 0x00,0x00,0x00 +}; + +static Bit8u static_functionality[0x10]= +{ + /* 0 */ 0xff, // All modes supported #1 + /* 1 */ 0xe0, // All modes supported #2 + /* 2 */ 0x0f, // All modes supported #3 + /* 3 */ 0x00, 0x00, 0x00, 0x00, // reserved + /* 7 */ 0x07, // 200, 350, 400 scan lines + /* 8 */ 0x02, // mamimum number of visible charsets in text mode + /* 9 */ 0x08, // total number of charset blocks in text mode + /* a */ 0xe7, // Change to add new functions + /* b */ 0x0c, // Change to add new functions + /* c */ 0x00, // reserved + /* d */ 0x00, // reserved + /* e */ 0x00, // Change to add new functions + /* f */ 0x00 // reserved +}; +#endif diff --git a/src/base/bios/x86/Makefile b/src/base/bios/x86/Makefile new file mode 100644 index 0000000..d6a5a72 --- /dev/null +++ b/src/base/bios/x86/Makefile @@ -0,0 +1,45 @@ +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf + +GENSRC = bios_symbols.c +SFILES = bios.S +bios_S_START = 0xe000 +ALL_CPPFLAGS += -DBIOS_START=$(bios_S_START) + +all: lib + +include $(REALTOPDIR)/src/Makefile.common + +# to support parallel build, we need a separate .o and .elf for every gen target +bios1.o: $(srcdir)/bios.S $(srcdir)/Makefile $(top_builddir)/src/include/version.hh + $(CPP) $(ALL_CPPFLAGS) -x assembler-with-cpp $< | $(XAS) $(XASFLAGS) -o $@ + +.INTERMEDIATE: bios1.o + +bios_symbols.c: bios1.o + nm -g -n $< | gawk '\ + BEGIN {\ + COUNT=0;\ + print "// Warning: autogenerated";\ + print "";\ + print "#include \"bios_sym.h\"";\ + print "";\ + print "struct bios_symbol_entry bios_symbol[] = {";\ + }\ + {\ + HEXSTR = sprintf("0x%s", $$1);\ + ADDR = strtonum(HEXSTR) + $(bios_S_START);\ + if (ADDR <= 0xffff) {\ + COUNT++;\ + printf " { 0x%04x, \"%s\" },\n", ADDR, $$3;\ + }\ + }\ + END {\ + print "};";\ + print "";\ + print "int bios_symbol_num = " COUNT ";";\ + }\ + ' > $@ || (rm -f $@ ; false) + +clean:: + rm -f *.o *.d bios_symbols.c *.map diff --git a/src/base/bios/x86/bios.S b/src/base/bios/x86/bios.S new file mode 100644 index 0000000..d8778c9 --- /dev/null +++ b/src/base/bios/x86/bios.S @@ -0,0 +1,1046 @@ +/*************************************************************************/ +/** **/ +/** This is the virtual PC's Bios (F000:0 .. F000:FFFF) **/ +/** **/ +/** modified from as86 to gas by Bart Oldeman Jan 2001 **/ +/** **/ +/*************************************************************************/ + +#include "version.h" +#include "memory.h" +#include "emudpmi.h" +#include "macros86.h" +#include "doshelpers.h" +#include "keyboard/keyb_server.h" + +/* some other useful definitions */ + +#define BIOSSEG 0xf000 +/* below block is used in int 9 with DS=BIOS_DATA */ +#define BIOS_DATA 0x40 +#define KEYBUF_READ_PTR 0x1a +#define KEYBUF_WRITE_PTR 0x1c +#define KEYBUFFER_START 0x80 +#define KEYBUFFER_END 0x82 +#define KEYSHIFT_FLAGS 0x17 +#define KEYBOARD_FLAGS_2 0x18 +#define KEYBOARD_STATUS_3 0x96 +#define BIOS_FLAGS_1 0x71 +#define BIOS_FLAGS_2 0x72 +/* end int 9 defines */ + +#define BIOS_TIMER 0x46c +#define BIOS_TIMER_OVERFLOW 0x470 +#define HOUR24_ADJUST 0x1800B0 +#define DISKETTE_MOTOR_TIMEOUT 0x440 +#define BIOS_VIDEO_MODE 0x49 + +#define _ORG(x) .org (x) - BIOS_START + +.code16 +.text + .globl bios_data_start +bios_data_start: + +/****************************************************************** + * BIOS CODE BLOCK * + ******************************************************************/ +_ORG(0xe000) +/* COMPAS FE000-FE05A reserved */ + .ascii "..............IBM..............." + .ascii "DOSEMU Custom BIOS r0.01, Copyri" + .ascii "ght 1992-2005.........." + + _ORG(ROM_BIOS_SELFTEST) +/* COMPAS FE05B jmp to POST */ +/* COMPAS FE05E-FE2C2 reserved */ + .global _start +_start: + hlt + sti + +/* ----------------------------------------------------------------- */ +/* This is for the video init - it calls in order: + - first helper function (int0xe6,al=8) + - the video BIOS init entry point at C000:3 or E000:3 + - second helper function (int 0xe6,al=9) + */ + movb $DOS_HELPER_VIDEO_INIT,%al /* Start Video init */ + int $DOS_HELPER_INT + cmpb $0, %al + je no_vbios_post + /* call far 0xc000:3 or call far 0xe000:3 */ + /* More general than just c000 or e000 ??? */ + pushw %ds + lcall *%cs:bios_f000_int10ptr + popw %ds + sti + +/* ----------------------------------------------------------------- */ +/* Post-less video init + */ +no_vbios_post: + movw $3,%ax + int $0x10 +video_init_done: + movb $DOS_HELPER_SHOW_BANNER,%al + int $DOS_HELPER_INT + movb $DOS_HELPER_READ_MBR,%al + int $DOS_HELPER_INT + ljmp $0x0, $0x7c00 /* Some boot sectors require cs=0 */ + + .globl ROM_BIOS_EXIT +ROM_BIOS_EXIT: + /* set up BIOS exit routine */ + movw $DOS_HELPER_REALLY_EXIT,%ax + int $DOS_HELPER_INT + +/* COMPAS FE2C3 jmp to NMI */ + + .globl GET_RETCODE_HELPER +GET_RETCODE_HELPER: + movb $0x4d, %ah + int $0x21 + movw %ax, %bx + movb $DOS_HELPER_SET_RETCODE, %al + int $DOS_HELPER_INT + lret + + /* This is the int74 handler */ + .globl Mouse_ROUTINE_OFF +Mouse_ROUTINE_OFF: + pushw %ax /* save everything */ + pushw %bx + movw $DOS_HELPER_MOUSE_HELPER,%ax /* mouse helper */ + movw $0xf2,%bx /* call the user or PS/2 hook */ + int $DOS_HELPER_INT + popw %bx + popw %ax + ljmp $BIOSSEG, $EOI2_OFF + +/* ----------------------------------------------------------------- */ + /* this is IRET */ + .globl IRET_OFF +IRET_OFF: + iret + +/* ----------------------------------------------------------------- */ + /* This is an int e7 used for FCB opens */ + .globl FCB_HLP_OFF +FCB_HLP_OFF: + pushw %es + pushw %di + pushw %ax + movw $0x120c,%ax + clc /* this func doesnt touch CF but we need it cleared */ + int $0x2f + popw %ax + popw %di + popw %es + lret + +/* This is installed after video init (helper fcn 0x9) when the internal + mouse driver is in use. It watches for mouse set commands and + resets the mouse driver when it sees one. */ +/* Was: Comments and bugs to David Etherton, etherton@netcom.com + * Current Maintainer: Eric W. Biederman + */ + + .globl INT10_WATCHER_OFF +INT10_WATCHER_OFF: +WINT10: + movzwl %sp,%esp /* make sure high of esp is zero */ + cmpb $1, %cs:bios_in_int10_callback + je L10 + or %ah,%ah + jz L9 /* normal mode set */ + cmpb $0x11,%ah + je L9 /* character generator, possibly resize the screen */ + cmpw $0x4F02,%ax + jne L10 /* svga mode set */ + pushw %bx /* vesa mode on stack */ + jmp L9a +L9: + pushw %ax /* normal mode (or 0x110?) on stack */ +L9a: + pushw %ax /* save everything */ + pushw %bx + movw $DOS_HELPER_MOUSE_HELPER,%ax /* mouse helper */ + movw $DOS_SUBHELPER_MOUSE_START_VIDEO_MODE_SET,%bx /* start video mode set */ + int $DOS_HELPER_INT + popw %bx + popw %ax + +/* fake stack frame for iret: push original flags and avoid a GPF from pushf */ + pushw 6(%esp) + pushw %cs + call L10 /* perform the actual mode set */ + +/* since following code doesn't affect flags, we keep current values */ + pushw %ax /* remember everything from int10 call */ + pushw %bx + movw $DOS_HELPER_MOUSE_HELPER,%ax /* mouse helper */ + movw $DOS_SUBHELPER_MOUSE_END_VIDEO_MODE_SET,%bx /* end video mode set */ + int $DOS_HELPER_INT + popw %bx + popw %ax + addw $2,%sp/* pop video mode */ + lret $2 /* keep current flags and avoid another GPF from iret */ + +L10: /* chain to original handler (probably the video bios) */ + pushw 4(%esp) + andw $0xff,(%esp) + popfw /* sync up flags */ + ljmp *%cs:bios_f000_int10_old + + .globl MOUSE_INT33_OFF +MOUSE_INT33_OFF: + jmp 1f + int33_chain: + .word INT_RVC_33_OFF + .word BIOSSEG + .word 0x424B // signature "KB" + .byte 0 // flag + jmp 30f // EB xx for compat + .space 7 // padding + 30: lret + 1: ljmp $BIOSSEG,$int33_cont + int33_cont: + ljmp *%cs:int33_chain + +/* ----------------------------------------------------------------- */ + .globl INT70_OFF +INT70_OFF: /* RTC INTERRUPT ROUTINE */ + pushw %ax + int $0x4a + movb $0x20,%al + outb %al,$0xa0 /* flag interrupt complete */ + outb %al,$0x20 + popw %ax /* restore registers */ + iret /* return to interrupted code */ + .globl INT70_end +INT70_end: + + +/* COMPAS FE3FE jmp to INT13 HD */ + _ORG(((INT41_SEG - BIOSSEG) << 4) + INT41_OFF) + .globl HD_parameter_table0 +HD_parameter_table0: +/* COMPAS FE401-FE6F0 HD parameter table */ + .word 50 /* cyl */ + .byte 255 /* heads */ + .word 0 /* rw_cyl */ + .word 0 /* precomp_cyl */ + .byte 0 /* max_ecc */ + .byte 0 /* contr */ + .byte 10 /* std_timeout */ + .byte 10 /* fmt_timeout */ + .byte 10 /* chk_timeout */ + .word 0 /* lnd_zone */ + .byte 63 /* spt */ + .byte 0xff /* reserved */ + + _ORG(((INT46_SEG - BIOSSEG) << 4) + INT46_OFF) + .globl HD_parameter_table1 +HD_parameter_table1: +/* COMPAS FE401-FE6F0 HD parameter table */ + .word 50 /* cyl */ + .byte 255 /* heads */ + .word 0 /* rw_cyl */ + .word 0 /* precomp_cyl */ + .byte 0 /* max_ecc */ + .byte 0 /* contr */ + .byte 10 /* std_timeout */ + .byte 10 /* fmt_timeout */ + .byte 10 /* chk_timeout */ + .word 0 /* lnd_zone */ + .byte 63 /* spt */ + .byte 0xff /* reserved */ + +/* COMPAS FE6F1 reserved */ +/* COMPAS FE6F2 jmp to INT19 */ +/* ----------------------------------------------------------------- */ + _ORG(ROM_CONFIG_OFF) /* for int15 */ + +/* ======================= Addr = FE6F5 */ + /* from Alan Cox's mods */ + /* we need somewhere for the bios equipment. */ + .word 8 /* 8 bytes follow */ + .byte 0xfc /* PC AT */ + .byte 0x01 /* submodel - DOSEMU */ + .byte 0x04 /* bios revision 4 */ + .byte 0x70 /* no mca, no ebios, no wat, keybint, + rtc, slave 8259, no dma 3 */ + .byte 0x40 /* extended keyboard */ + .byte 0,0,0 /* nothing more is supported */ + +/* COMPAS FE710-FE728 reserved */ +/* COMPAS FE729 baud rate init table */ +/* COMPAS FE73C-FE82D reserved */ + +/* ----------------------------------------------------------------- */ + _ORG(((INT09_SEG-BIOSSEG) << 4)+INT09_OFF) +/* ======================= Addr = FE987 */ + jmp int09_cont +/* COMPAS FE987 jmp to INT09 */ +/* COMPAS FE98A-FEC58 reserved */ +int09_cont: + pushw %ax + pushw %bx + pushw %ds + movw $BIOS_DATA,%ax + movw %ax,%ds + +/* BIOS keyboard intercept */ + +/* get the RAW scancode (used only for int15,4f) */ + movb $0xad,%al + outb %al,$0x64 + inb $0x60,%al + movb $0xae,%ah + xchg %al,%ah + outb %al,$0x64 + xchg %al,%ah + +/* check for Ctrl-Alt-Del */ + cmpb $0x53, %al + jne 1f + movb KEYSHIFT_FLAGS, %bl + andb $0x0c, %bl + cmpb $0x0c, %bl + je kbd_do_CAD +1: + +/*ERIC * src/base/bios/bios.S (INT09_dummy_start): + removed spurious 'mov al,ah' between in al,0x60 and mov ah,$0x4f + it was trashing the value read and would serve no useful purpose. +*/ + movb $0x4f,%ah + stc + int $0x15 + + /* ignore the returned keycode, only skip the pre-translated + bios keycode if CF=0. + this is not completely accurate but hard to improve while + keeping a clean keyboard server design. + */ + jnc kbd_done + + /* get the pre-translated bios key. */ + + movb %al,%ah /* pass the scancode... */ + movb $DOS_HELPER_GET_BIOS_KEY,%al + int $DOS_HELPER_INT /* call get_bios_key helper */ + /* returns ax=keycode or 0, */ + /* also copies new shift state to */ + /* seg 0x40 */ + + testw %ax,%ax + jz kbd_done /* no keycode returned */ + +/* check for "special action" codes + */ + cmpw $SP_PAUSE, %ax + je kbd_do_pause + + testb $PAUSE_MASK,KEYBOARD_FLAGS_2 /* if pause bit not set */ + jz check_special /* continue */ + andb $~PAUSE_MASK,KEYBOARD_FLAGS_2 /* reset pause bit */ + jmp kbd_done /* don't store in buffer */ + +check_special: + cmpw $SP_BREAK, %ax + je kbd_do_break + cmpw $SP_CC, %ax + je kbd_do_cc + cmpw $SP_PRTSCR, %ax + je kbd_do_prtscr + cmpw $SP_SYSRQ_MAKE, %ax + je kbd_do_sysrq_make + cmpw $SP_SYSRQ_BREAK, %ax + je kbd_do_sysrq_break + call store_key +kbd_done: + call kbd_EOI +kbd_done2: + popw %ds + popw %bx + popw %ax /* restore registers */ + iret + +store_key: + movw KEYBUF_WRITE_PTR,%bx + incw %bx + incw %bx + cmpw KEYBUFFER_END,%bx + jne no_wrap + movw KEYBUFFER_START,%bx +no_wrap: + cmpw KEYBUF_READ_PTR,%bx + je buffer_full + xchgw KEYBUF_WRITE_PTR,%bx /* ok, update write pointer */ + movw %ax,(%bx) /* and store key in buffer */ +buffer_full: + ret + +clear_kbd_buffer: + movw KEYBUFFER_START,%bx + movw %bx,KEYBUF_WRITE_PTR + movw %bx,KEYBUF_READ_PTR + ret + +kbd_do_pause: + testb $PAUSE_MASK,KEYBOARD_FLAGS_2 /* already paused? */ + jnz kbd_done /* do nothing */ + orb $PAUSE_MASK,KEYBOARD_FLAGS_2 /* set pause bit */ + call kbd_EOI + sti +1: + movw $0x1680,%ax /* magic machine idle function */ + int $0x2f + testb $PAUSE_MASK,KEYBOARD_FLAGS_2 /* is pause bit still set? */ + jnz 1b + cli + jmp kbd_done2 + +kbd_do_break: /* CTRL-BREAK pressed */ + movw $0,%ax +kbd_do_cc: /* CTRL-c pressed */ + call clear_kbd_buffer + call store_key + orb $0x80,BIOS_FLAGS_1 + call kbd_EOI + sti + int $0x1b /* call BREAK interrupt */ + jmp kbd_done2 + +kbd_do_prtscr: /* PRINT SCREEN pressed */ + call kbd_EOI + sti + int $0x05 + jmp kbd_done2 + +kbd_do_sysrq_make: /* Alt-SYSRQ pressed */ + orb $4,KEYBOARD_FLAGS_2 /* set sysrq bit */ + call kbd_EOI + sti + movw $0x8500,%ax + int $0x15 + jmp kbd_done2 + +kbd_do_sysrq_break: /* ALT-SYSRQ released */ + andb $~4,KEYBOARD_FLAGS_2 /* clear sysrq bit */ + call kbd_EOI + sti + movw $0x8501,%ax + int $0x15 + jmp kbd_done2 + +kbd_do_CAD: + call kbd_EOI + movw $0x1234, 0x72 + ljmp $0xffff, $0 + +kbd_EOI: + inb $0x61,%al /* KBD IRQ ACK code from the */ + movb %al,%ah /* Lukach & Sibiriakov book */ + orb $0x80,%al + outb %al,$0x61 + xchgb %al,%ah + outb %al,$0x61 + movb $0x20,%al + outb %al,$0x20 /* tell pic we're done */ + ret + +/****************************************************************** + * DATA BLOCK + ******************************************************************/ + .globl bios_f000_int10ptr +bios_f000_int10ptr: + .long 0xc0000003 + + .globl bios_f000_int10_old +bios_f000_int10_old: + .long 0 + + .globl bios_in_int10_callback +bios_in_int10_callback: + .byte 0 + + /* this is the paramblock, we told DOS to use for INT21 AX=4B01 */ + .align 16,0 + .globl DBGload_parblock +DBGload_parblock: .word 0 + .long 0,0,0 +DBGload_SSSP: .long 0 + .globl DBGload_CSIP +DBGload_CSIP: .long 0 + + /* parameter packet, filled in by pkt_init */ + .globl PKTDRV_param +PKTDRV_param: + .byte 0,0,0,0 + .word 0,0,0,0,0 + + /* driver statistics structure */ + .globl PKTDRV_stats +PKTDRV_stats: + .long 0,0,0,0,0,0,0 + + .globl LFN_short_name +LFN_short_name: + .space 128 +/****************************************************************** + * END DATA BLOCK + ******************************************************************/ + +/* COMPAS FEC59 jmp to INT13 FDD */ +/* COMPAS FEC5C-FEF56 reserved */ +/* COMPAS FEF57 jmp to INT0E */ +/* COMPAS FEF5A-FEFC6 reserved */ +/* ----------------------------------------------------------------- */ + _ORG(((INT1E_SEG - BIOSSEG) << 4) + INT1E_OFF) +/* COMPAS FEFC7 FDD param table */ +/* Win98/DOS uses it via int0x1e vector */ + .byte 0xaf /* b7-4=step rate b3-0=head unload time */ + .byte 0x02 /* b7=1=head load time b0=0 */ + .byte 0x25 /* motor off delay in clock ticks */ + .byte 0x02 /* bytes per sector 00=128..03=1024 */ + .byte 18 /* sectors per track */ + .byte 0x1b /* gap between sectors */ + .byte 0xff /* ignored */ + .byte 0x6c /* format gap length */ + .byte 0xf6 /* format filler byte */ + .byte 0x0f /* head settle time (ms) */ + .byte 0x08 /* motor start time (1/8") */ + .byte 0xff /* maximum track number */ + .byte 0xff /* data transfer rate */ + .byte 0 /* drive type */ + +/* COMPAS FEFD2 jmp to INT17 */ + _ORG(((INT1E_SEG - BIOSSEG) << 4) + INT1E_OFF + 14) +/* COMPAS FEFD5-FF064 reserved */ + .byte 0xdf + .byte 0x02 + .byte 0x25 + .byte 0x02 + .byte 63 + .byte 0x1b + .byte 0xff + .byte 0x54 + .byte 0xf6 + .byte 0x0f + .byte 0x08 + .byte 0xff /* maximum track number */ + .byte 0xff /* data transfer rate */ + .byte 0 /* drive type */ + +/* ----------------------------------------------------------------- */ + _ORG(((INT42HOOK_SEG - BIOSSEG) << 4) + INT42HOOK_OFF) +/* ======================= Addr = FF065 */ +/* COMPAS FF065 jmp to INT10 */ +/* COMPAS FF068-FF0A3 reserved */ + /* relocated video handler (interrupt 0x42) */ + /* Note: A conforming video-bios will redirect int 42 here if + it doesn't find anything else. Another fun suprise : ( + I'll have to implement this in my video-bios. --EB 13 Jan 97 */ + hlt + +/* COMPAS FF0A4 video param table */ +/* COMPAS FF0FC-FF840 reserved */ + + _ORG(0xf100) // who says we cant use reserved region? + .globl bios_hlt_blk + .align 16 +bios_hlt_blk: + FILL_OPCODE BIOS_HLT_BLK_SIZE,hlt +/* ----------------------------------------------------------------- */ + + .globl DPMI_OFF +DPMI_OFF: + pushw %bx + pushw %ax + movb $0x62,%ah + int $0x21 /* Get PSP */ + popw %ax + pushw %bx + .globl DPMI_dpmi_init +DPMI_dpmi_init: + hlt + .globl DPMI_return_from_dos +DPMI_return_from_dos: + hlt + .globl DPMI_return_from_rmint +DPMI_return_from_rmint: + FILL_OPCODE DPMI_MAX_CLIENTS,hlt + .globl DPMI_return_from_realmode +DPMI_return_from_realmode: + FILL_OPCODE DPMI_MAX_CLIENTS,hlt + .globl DPMI_return_from_dos_memory +DPMI_return_from_dos_memory: + hlt + +.macro ihdr n + .globl \n +\n: +.rept DPMI_MAX_CLIENTS + hlt + jmp \n\()_end +.endr + .globl \n\()_end +\n\()_end: +.endm + +ihdr DPMI_int1c + iret + +ihdr DPMI_int23 + pushw %ax + pushw %bx + movw $0x6200, %ax /* get PSP */ + int $0x21 + .globl DPMI_int23_1 +DPMI_int23_1: + hlt + popw %bx + popw %ax + .globl DPMI_int23_2 +DPMI_int23_2: + hlt + jc 1f + iret +1: /* leave flags on stack */ + lret + +ihdr DPMI_int24 + iret + + .globl DPMI_raw_mode_switch_rm +DPMI_raw_mode_switch_rm: + hlt + .globl DPMI_save_restore_rm +DPMI_save_restore_rm: + hlt + lret + .globl DPMI_exit +DPMI_exit: + jnz 1f + /* last client terminated, reinit FPU */ + fninit +1: + int $0x21 + .globl DPMI_end +DPMI_end: + + +/* ----------------------------------------------------------------- */ + .globl XMSControl_OFF +XMSControl_OFF: + jmp (.+2+3) /* jmp short forward 3 */ + FILL_OPCODE 3,nop + hlt + lret + + .globl EOI_OFF +EOI_OFF: + cli + pushw %ax + movb $0x20,%al + outb %al,$0x20 /* flag interrupt complete */ + popw %ax + iret + + .globl EOI2_OFF +EOI2_OFF: + cli + pushw %ax + movb $0x20,%al + outb %al,$0xa0 + outb %al,$0x20 /* flag interrupt complete */ + popw %ax + iret + +/* ----------------------------------------------------------------- */ + /* the packet driver */ + .globl PKTDRV_OFF +PKTDRV_OFF: +/* jmp to entry point is also used as signature, and therefore + it have to be jmp with word displacement. I've found no way + to tell gas that I want jmp with word displacement, so hardcode + it as 0xe9 */ + .byte 0xe9 + .word PKTDRV_entry-.-2 + +/* The packet driver signature will be written here when the packet + driver is initialized. */ + .asciz "PKT DRVR" + + .globl PKTDRV_driver_name +PKTDRV_driver_name: + .asciz "Linux$" + .byte 0 + +PKTDRV_entry: + ljmp *%cs:PKTDRV_driver_entry + + .align 4,0 +PKTDRV_driver_entry: + .globl PKTDRV_driver_entry_ip +PKTDRV_driver_entry_ip: + .word 0 + .globl PKTDRV_driver_entry_cs +PKTDRV_driver_entry_cs: + .word 0 + +/* FOSSIL driver signature and jump to original handler */ + .globl FOSSIL_isr +FOSSIL_isr: + jmp 1f + .globl FOSSIL_oldisr +FOSSIL_oldisr: + .long -1 + .globl FOSSIL_magic +FOSSIL_magic: + .word 0 + .globl FOSSIL_maxfun +FOSSIL_maxfun: + .byte 0 + .byte 0 +1: + ljmp *%cs:FOSSIL_oldisr + .globl FOSSIL_idstring +FOSSIL_idstring: + .asciz "dosemu FOSSIL emulator" + + .globl LFN_HELPER_OFF +LFN_HELPER_OFF: + pushw %ds + pushw %dx + pushw %si + movw %cs, %si + movw %si, %ds + movw $LFN_short_name, %si + cmpb $0x6c, %ah + je do_int21 + movw %si, %dx +do_int21: + int $0x21 + popw %si + popw %dx + popw %ds + lret + + .globl LFN_42_HELPER_OFF +LFN_42_HELPER_OFF: + movw $0x1142, %ax + jmp LFN_42_A6_HELPER_COMMON + + .globl LFN_A6_HELPER_OFF +LFN_A6_HELPER_OFF: + movw $0x11a6, %ax + +LFN_42_A6_HELPER_COMMON: + pushw %es + pushw %di + pushw %bx + pushw %ax + movw $0x1220, %ax + int $0x2f + jc 1f + movzbw %es:(%di), %bx + cmpb $0xff, %bl + movw $6, %ax /* error code: invalid handle */ + stc /* initialise CY if jumping */ + je 1f + movw $0x1216, %ax + int $0x2f + jc 1f + popw %ax /* restore function number */ + orb $0x80, %al /* add extension bit */ + stc + int $0x2f + jmp 3f +1: + popw %bx /* discard ax on stack */ +3: + popw %bx + popw %di + popw %es + lret +/* ----------------------------------------------------------------- */ + + .globl DBGload_OFF +DBGload_OFF: +/* we come here after we have intercepted INT21 AX=4B00 + * in order to get a breakpoint for the debugger + * (wanting to debug a program from it's very beginning) + */ + cli /* first we set up the users stack */ + movw %cs:DBGload_SSSP+2,%ss + movw %cs:DBGload_SSSP,%sp + mov $0x62,%ah /* we must get the PSP of the loaded program */ + int $0x21 + movw %bx,%es + movw %bx,%ds + xorw %ax,%ax + movw %ax,%bx + movw %ax,%cx + movw %ax,%dx + movw %ax,%si + movw %ax,%di + movw %ax,%bp + popw %ax /* 4b01 puts ax on stack */ + sti + pushf + /* set TF */ + orw $0x100, %ss:(%esp) + /* can use iret here but the trap will be generated by the + * _second_ instruction after iret, not the first one. Since we + * dont want to skip the first instruction, we use 2 instructions + * for control transfer. Then the trap happens at the right place. + */ + popf + /* and give control to the program */ + ljmp *%cs:DBGload_CSIP + +/* ======================= INT_REVECT macro */ +.macro int_rvc inum +/* header start for IBM'S INTERRUPT-SHARING PROTOCOL */ + jmp 31f // EB xx to handler +int_rvc_data_\inum: + .globl int_rvc_ip_\inum +int_rvc_ip_\inum: + .word 0 + .globl int_rvc_cs_\inum +int_rvc_cs_\inum: + .word 0 + .word 0x424B // signature "KB" + .byte 0 // flag + jmp 30f // EB xx to hwreset +int_rvc_ret_\inum: + .globl int_rvc_ret_ip_\inum +int_rvc_ret_ip_\inum: + .word 0 + .globl int_rvc_ret_cs_\inum +int_rvc_ret_cs_\inum: + .word 0 +int_rvc_disp_\inum: + .globl int_rvc_disp_ip_\inum +int_rvc_disp_ip_\inum: + .word 0 + .globl int_rvc_disp_cs_\inum +int_rvc_disp_cs_\inum: + .word 0 +/* header finish for IBM'S INTERRUPT-SHARING PROTOCOL */ +30: /* hwreset */ + lret + +31: /* handler */ + pushl %eax + pushl %ebx + shll $16,%eax + shll $16,%ebx + movb $DOS_HELPER_REVECT_HELPER,%al + movb $DOS_SUBHELPER_RVC_CALL,%bl + movb $0x\inum,%ah + movb $14,%bh /* stack offset */ + int $DOS_HELPER_INT + movzwl %sp,%esp + movw %bx,(%esp) + popl %ebx + movw %ax,(%esp) + popl %eax + jnz 9f /* handled */ + jc 2f /* second_revect */ + pushw 4(%esp) + andw $0xff,(%esp) + popfw /* re-sync flags */ + pushfw + lcall *%cs:int_rvc_disp_\inum + jmp 9f +2: + /* no flag re-sync on second-revect: CF is forced */ + pushw %ax + pushfw + lcall *%cs:int_rvc_data_\inum + jnc 12f /* handled */ + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + shll $16,%eax + shll $16,%ebx + shll $16,%ecx + shll $16,%edx + clc + movw 16(%esp),%cx /* old AX */ + movw 22(%esp),%dx /* old flags */ + movb $DOS_HELPER_REVECT_HELPER,%al + movb $DOS_SUBHELPER_RVC2_CALL,%bl + movb $0x\inum,%ah + movb $24,%bh /* stack offset */ + int $DOS_HELPER_INT + movw %dx,(%esp) + popl %edx + movw %cx,(%esp) + popl %ecx + movw %bx,(%esp) + popl %ebx + movw %ax,(%esp) + popl %eax + /* below replaces addw $2,%sp to not corrupt CF */ + movw %ax,(%esp) + popw %ax + +9: + ljmp *%cs:int_rvc_ret_\inum +12: + addw $2,%sp /* skip saved ax */ + clc + jmp 9b +.endm + + .globl INT_RVC_21_OFF +INT_RVC_21_OFF: + int_rvc 21 + .globl INT_RVC_28_OFF +INT_RVC_28_OFF: + int_rvc 28 + .globl INT_RVC_2f_OFF +INT_RVC_2f_OFF: + int_rvc 2f + .globl INT_RVC_33_OFF +INT_RVC_33_OFF: + int_rvc 33 + +/* COMPAS FF841 jmp to INT12 */ +/* COMPAS FF844-FF84C reserved */ +/* COMPAS FF84D jmp to INT11 */ +/* COMPAS FF850-FF858 reserved */ +/* COMPAS FF859 jmp to INT15 */ +/* COMPAS FF85C-FFA6D reserved */ + +/* COMPAS FFA6E font tables */ + _ORG(0xfa6e) + .globl bios_text_font +bios_text_font: + /* there's no need to allocate the space just move over */ + _ORG(0xfa6e + (128 * 8)) // uint8_t text_font[128*8] + +/* COMPAS FFE6E jmp to INT1A */ +/* COMPAS FFE71-FFEA4 reserved */ + +/* ----------------------------------------------------------------- */ + _ORG(0xfea6) + .globl INT75_OFF +INT75_OFF: + int $2 /* Bochs does this; RBIL says: redirected to INT 02 */ + /* by the BIOS, for compatibility with the PC */ + fnclex /* Clear FP exceptions just in case a hooked + handler hasn't already done so, so we won't + get stuck (in real mode IGNNE would prevent + further exceptions but we don't emulate that) */ + xorb %al, %al + outb %al, $0xf0 + movb $0x20, %al + outb %al, $0xa0 + outb %al, $0x20 + iret + +/* ----------------------------------------------------------------- */ + .globl INT08_OFF +INT08_OFF: + jmp int08_cont +/* COMPAS FFEA5 jmp to INT08 */ +/* COMPAS FFEA8-FFEF2 reserved */ + _ORG(0xfef4) +int08_cont: +#if 0 + int $0x1c +#endif +/* NOTE: The above int 0x1c is a compatibility fault, because + * the original IBM Bios calls the user *after* the timer is + * increased. So, I moved it down. + * THIS NEEDS TO BE CHECKED for side effects in dosemu ! + */ + pushw %ds + pushw %ax + xorw %ax, %ax /* set ax to segment 0 */ + movw %ax,%ds + incl BIOS_TIMER + cmpl $HOUR24_ADJUST, BIOS_TIMER /* 24 hour check */ + jb INT08_L1 + movl $0, BIOS_TIMER + incb BIOS_TIMER_OVERFLOW +INT08_L1: + /* emulate 'diskette motor running */ + /* some old games rely on that --SW, --Hans */ + cmpb $0, DISKETTE_MOTOR_TIMEOUT + jz INT08_L2 + decb DISKETTE_MOTOR_TIMEOUT + jnz INT08_L2 + /* turn floppy motor off (code from Bochs) */ + pushw %dx + movw $0x3f2, %dx + inb %dx, %al + andb $0xcf, %al + outb %al, %dx + popw %dx + +INT08_L2: + int $0x1c /* call int 0x1c, per bios spec */ + /* must do it before EOI, but after count */ + movb $0x20,%al + outb %al,$0x20 /* flag interrupt complete */ + popw %ax /* restore registers */ + popw %ds + iret /* return to interrupted code */ + +/* ----------------------------------------------------------------- */ + .globl INT71_OFF +INT71_OFF: + push %ax + /* EOI to PIC1 */ + movb $0x20, %al + outb %al, $0xa0 + /* Then invoke IRQ2 */ + int $0x0a + /* Specific EOI */ + movb $0x62, %al + outb %al, $0x20 + pop %ax + iret /* return to interrupted code */ + +/* COMPAS FFEF3 vector table for INT08-INT1F */ +/* COMPAS FFF23 vector table for INT70-INT77 */ +/* COMPAS FFF33-FFF53 reserved */ +/* COMPAS FFF54 jmp to INT05 */ +/* COMPAS FFF57-FFFD8 reserved */ +/* COMPAS FFFD9 EISA ident string */ +/* COMPAS FFFDD-FFFEF reserved */ + +/* ----------------------------------------------------------------- */ + _ORG(0xffe0) + + /* DOSEMU magic and version field */ + .ascii "$DOSEMU$" + .long DOSEMU_VERSION_CODE + +/* ----------------------------------------------------------------- */ + _ORG(0xfff0) +/* COMPAS FFFF0 jmp to powerup */ + ljmp $BIOSSEG, $ROM_BIOS_SELFTEST +/* COMPAS FFFF5 ROM BIOS date */ + .ascii "02/25/93" /* our bios date */ +/* COMPAS FFFFD unused */ + hlt +/* COMPAS FFFFE system model ID */ + .byte 0xfc /* model byte = IBM AT */ + + .globl bios_f000_end +bios_f000_end: + +/*--------------------------------------------------------------------------*/ +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/base/bios/x86/bios_offsets.h b/src/base/bios/x86/bios_offsets.h new file mode 100644 index 0000000..6641037 --- /dev/null +++ b/src/base/bios/x86/bios_offsets.h @@ -0,0 +1,88 @@ +extern const unsigned bios_data_start; +extern const unsigned ROM_BIOS_EXIT; +extern const unsigned GET_RETCODE_HELPER; +extern const unsigned Mouse_ROUTINE_OFF; +extern const unsigned IRET_OFF; +extern const unsigned FCB_HLP_OFF; +extern const unsigned INT10_WATCHER_OFF; +extern const unsigned MOUSE_INT33_OFF; +extern const unsigned INT70_OFF; +extern const unsigned INT70_end; +extern const unsigned HD_parameter_table0; +extern const unsigned HD_parameter_table1; +extern const unsigned bios_f000_int10ptr; +extern const unsigned bios_f000_int10_old; +extern const unsigned bios_in_int10_callback; +extern const unsigned DBGload_parblock; +extern const unsigned DBGload_CSIP; +extern const unsigned PKTDRV_param; +extern const unsigned PKTDRV_stats; +extern const unsigned LFN_short_name; +extern const unsigned bios_hlt_blk; +extern const unsigned DPMI_OFF; +extern const unsigned DPMI_dpmi_init; +extern const unsigned DPMI_return_from_dos; +extern const unsigned DPMI_return_from_rmint; +extern const unsigned DPMI_return_from_realmode; +extern const unsigned DPMI_return_from_dos_memory; +extern const unsigned DPMI_int1c; +extern const unsigned DPMI_int1c_end; +extern const unsigned DPMI_int23; +extern const unsigned DPMI_int23_end; +extern const unsigned DPMI_int23_1; +extern const unsigned DPMI_int23_2; +extern const unsigned DPMI_int24; +extern const unsigned DPMI_int24_end; +extern const unsigned DPMI_raw_mode_switch_rm; +extern const unsigned DPMI_save_restore_rm; +extern const unsigned DPMI_exit; +extern const unsigned DPMI_end; +extern const unsigned XMSControl_OFF; +extern const unsigned EOI_OFF; +extern const unsigned EOI2_OFF; +extern const unsigned PKTDRV_OFF; +extern const unsigned PKTDRV_driver_name; +extern const unsigned PKTDRV_driver_entry_ip; +extern const unsigned PKTDRV_driver_entry_cs; +extern const unsigned FOSSIL_isr; +extern const unsigned FOSSIL_oldisr; +extern const unsigned FOSSIL_magic; +extern const unsigned FOSSIL_maxfun; +extern const unsigned FOSSIL_idstring; +extern const unsigned LFN_HELPER_OFF; +extern const unsigned LFN_42_HELPER_OFF; +extern const unsigned LFN_A6_HELPER_OFF; +extern const unsigned DBGload_OFF; +extern const unsigned INT_RVC_21_OFF; +extern const unsigned int_rvc_ip_21; +extern const unsigned int_rvc_cs_21; +extern const unsigned int_rvc_ret_ip_21; +extern const unsigned int_rvc_ret_cs_21; +extern const unsigned int_rvc_disp_ip_21; +extern const unsigned int_rvc_disp_cs_21; +extern const unsigned INT_RVC_28_OFF; +extern const unsigned int_rvc_ip_28; +extern const unsigned int_rvc_cs_28; +extern const unsigned int_rvc_ret_ip_28; +extern const unsigned int_rvc_ret_cs_28; +extern const unsigned int_rvc_disp_ip_28; +extern const unsigned int_rvc_disp_cs_28; +extern const unsigned INT_RVC_2f_OFF; +extern const unsigned int_rvc_ip_2f; +extern const unsigned int_rvc_cs_2f; +extern const unsigned int_rvc_ret_ip_2f; +extern const unsigned int_rvc_ret_cs_2f; +extern const unsigned int_rvc_disp_ip_2f; +extern const unsigned int_rvc_disp_cs_2f; +extern const unsigned INT_RVC_33_OFF; +extern const unsigned int_rvc_ip_33; +extern const unsigned int_rvc_cs_33; +extern const unsigned int_rvc_ret_ip_33; +extern const unsigned int_rvc_ret_cs_33; +extern const unsigned int_rvc_disp_ip_33; +extern const unsigned int_rvc_disp_cs_33; +extern const unsigned bios_text_font; +extern const unsigned INT75_OFF; +extern const unsigned INT08_OFF; +extern const unsigned INT71_OFF; +extern const unsigned bios_f000_end; diff --git a/src/base/core/Makefile b/src/base/core/Makefile new file mode 100644 index 0000000..6d455c3 --- /dev/null +++ b/src/base/core/Makefile @@ -0,0 +1,7 @@ +top_builddir=../../.. +include $(top_builddir)/Makefile.conf + +CFILES = dyndeb.c int.c hlt.c emu.c ports.c coopth.c dump.c lowmem.c priv.c \ + vint.c + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/core/coopth.c b/src/base/core/coopth.c new file mode 100644 index 0000000..6d04a17 --- /dev/null +++ b/src/base/core/coopth.c @@ -0,0 +1,1541 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: cooperative threading between dosemu and DOS code. + * + * Author: Stas Sergeev + * + * This is a V2 implementation of coopthreads in dosemu. + * V1 was too broken and was removed by commit 158ca93963d968fdc + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_EXECINFO +#include +#endif +#include "emu.h" +#include "utilities.h" +#include "libpcl/pcl.h" +#include "coopth.h" +#include "coopth_be.h" + +enum CoopthRet { COOPTH_YIELD, COOPTH_WAIT, COOPTH_SLEEP, COOPTH_SCHED, + COOPTH_DONE, COOPTH_ATTACH, COOPTH_DETACH, COOPTH_LEAVE, + COOPTH_DELETE }; +enum CoopthState { COOPTHS_NONE, COOPTHS_RUNNING, COOPTHS_SLEEPING, + COOPTHS_SWITCH }; +enum CoopthJmp { COOPTH_JMP_NONE, COOPTH_JMP_CANCEL, COOPTH_JMP_EXIT }; + +enum CoopthSw { idx_NONE, idx_SCHED, idx_ATTACH, idx_DETACH, idx_LEAVE, + idx_DONE, idx_AWAKEN, idx_YIELD, idx_WAIT }; + +struct coopth_thrfunc_t { + coopth_func_t func; + void *arg; +}; + +#define MAX_POST_H 5 +#define MAX_UDATA 5 + +struct coopth_thrdata_t { + int *tid; + enum CoopthRet ret; + enum CoopthJmp jret; + void *udata[MAX_UDATA]; + int udata_num; + struct coopth_thrfunc_t post[MAX_POST_H]; + int posth_num; + struct coopth_thrfunc_t sleep; + struct coopth_thrfunc_t clnup; + struct coopth_thrfunc_t uhook; + jmp_buf exit_jmp; + unsigned int canc_disabled:1; + unsigned int attached:1; + unsigned int cancelled:1; + unsigned int left:1; + unsigned int atomic_switch:1; +}; + +struct coopth_ctx_handlers_t { + coopth_hndl_t pre; + coopth_hndl_t post; + void *arg; +}; + +struct coopth_sleep_handlers_t { + coopth_sleep_hndl_t pre; + coopth_hndl_t post; +}; + +struct coopth_starter_args_t { + struct coopth_thrfunc_t thr; + struct coopth_thrdata_t *thrdata; +}; + +struct coopth_t; +struct coopth_per_thread_t; + +struct coopth_state_t { + enum CoopthState state; + enum CoopthSw sw_idx; +}; + +struct coopth_per_thread_t { + coroutine_t thread; + struct coopth_state_t st; + struct coopth_thrdata_t data; + struct coopth_starter_args_t args; + void *stack; + size_t stk_size; + unsigned int quick_sched:1; + void (*retf)(int tid, int idx); +}; + +struct coopth_t { + int tid; + const char *name; + int off; + int len; + int cur_thr; + int max_thr; + unsigned int detached:1; + unsigned int custom:1; + coopth_func_t func; + struct coopth_ctx_handlers_t ctxh; + struct coopth_sleep_handlers_t sleeph; + coopth_hndl_t post; + struct coopth_per_thread_t pth[MAX_COOP_RECUR_DEPTH]; + struct coopth_per_thread_t *post_pth; + const struct coopth_be_ops *ops; + pthread_t pthread; +}; + +static __TLS cohandle_t co_handle; +static struct coopth_t coopthreads[MAX_COOPTHREADS]; +static int coopth_num; +static __TLS int thread_running; +static __TLS int joinable_running; +static __TLS int left_running; +#define DETACHED_RUNNING (thread_running - joinable_running - left_running) +static __TLS int threads_joinable; +static __TLS int threads_left; +static __TLS int threads_total; +#define MAX_ACT_THRS 10 +static __TLS int threads_active; +static __TLS int active_tids[MAX_ACT_THRS]; +static __TLS void (*nothread_notifier)(int); + +static void coopth_callf_chk(struct coopth_t *thr, + struct coopth_per_thread_t *pth); +static void coopth_retf(struct coopth_t *thr, struct coopth_per_thread_t *pth, + void (*retf)(int tid, int idx)); +static void do_del_thread(struct coopth_t *thr, + struct coopth_per_thread_t *pth); +static void do_call_post(struct coopth_t *thr, + struct coopth_per_thread_t *pth); +static void check_tid(int tid); + +#define COOP_STK_SIZE() (512 * getpagesize()) + +#define CIDX(t, i) ((t)*MAX_COOP_RECUR_DEPTH+(i)) +#define CIDX2(t, i) (t),((t)*MAX_COOP_RECUR_DEPTH+(i)) + +void coopth_init(void) +{ + co_handle = co_thread_init(PCL_C_MC); +} + +#define SW_ST(x) (struct coopth_state_t){ COOPTHS_SWITCH, idx_##x } +#define ST(x) (struct coopth_state_t){ COOPTHS_##x, idx_NONE } + +static void sw_SCHED(struct coopth_t *thr, struct coopth_per_thread_t *pth) +{ + pth->st = ST(RUNNING); +} +#define sw_ATTACH sw_SCHED + +static void sw_DETACH(struct coopth_t *thr, struct coopth_per_thread_t *pth) +{ + coopth_retf(thr, pth, thr->ops->retf); + /* entry point should change - do the second switch */ + pth->st.sw_idx = idx_SCHED; +} + +static void sw_LEAVE(struct coopth_t *thr, struct coopth_per_thread_t *pth) +{ + if (pth->data.attached) + coopth_retf(thr, pth, thr->ops->retf); + pth->data.left = 1; + threads_left++; + do_call_post(thr, pth); + /* leaving operation is atomic, without a separate entry point + * but without a DOS context also. */ + pth->st = ST(RUNNING); +} + +static void sw_DONE(struct coopth_t *thr, struct coopth_per_thread_t *pth) +{ + assert(pth->data.attached); + coopth_retf(thr, pth, pth->retf); + do_del_thread(thr, pth); +} + +static void sw_AWAKEN(struct coopth_t *thr, struct coopth_per_thread_t *pth) +{ + if (thr->sleeph.post) + thr->sleeph.post(thr->tid, NULL, pth->args.thr.arg); + pth->st = ST(RUNNING); +} +#define sw_YIELD sw_AWAKEN +#define sw_WAIT sw_AWAKEN +#define sw_NONE NULL + +typedef void (*sw_fn)(struct coopth_t *, struct coopth_per_thread_t *); +static sw_fn sw_list[] = { + #define LST(x) [idx_##x] = sw_##x + LST(NONE), + LST(SCHED), + LST(ATTACH), + LST(DETACH), + LST(LEAVE), + LST(DONE), + LST(AWAKEN), + LST(YIELD), + LST(WAIT), + #undef LST +}; + +static enum CoopthRet do_call(struct coopth_per_thread_t *pth) +{ + enum CoopthRet ret; + co_call(pth->thread); + ret = pth->data.ret; + if (ret == COOPTH_DONE && !pth->data.attached) { + /* delete detached thread ASAP or leavedos() will complain */ + return COOPTH_DELETE; + } + return ret; +} + +static enum CoopthRet do_run_thread(struct coopth_t *thr, + struct coopth_per_thread_t *pth) +{ + enum CoopthRet ret = do_call(pth); + switch (ret) { +#define DO_SWITCH(x) \ + case COOPTH_##x: \ + pth->st = SW_ST(x); \ + break +#define DO_SWITCH2(x, c) \ + case COOPTH_##x: \ + c; \ + pth->st = SW_ST(x); \ + break + DO_SWITCH(YIELD); + DO_SWITCH(WAIT); + DO_SWITCH(SCHED); + DO_SWITCH(DETACH); + DO_SWITCH(LEAVE); + DO_SWITCH(DONE); + DO_SWITCH2(ATTACH, coopth_callf_chk(thr, pth)); + + case COOPTH_SLEEP: + pth->st = ST(SLEEPING); + break; + case COOPTH_DELETE: + /* only detached threads are deleted here */ + assert(!pth->data.attached); + do_del_thread(thr, pth); + break; + } + return ret; +} + +static void do_call_post(struct coopth_t *thr, struct coopth_per_thread_t *pth) +{ + int i; + /* it is important to call permanent handlers before temporary ones. + * The reason is that temporary ones are allowed to do non-local jumps, + * switch stacks and all that. Permanent ones should be called in + * a "predictable" context. For example in int.c they simulate iret. */ + if (thr->post) + thr->post(thr->tid, NULL, pth->args.thr.arg); + /* now can call temporary ones, they may change context */ + for (i = 0; i < pth->data.posth_num; i++) + pth->data.post[i].func(pth->data.post[i].arg); +} + +static void do_del_thread(struct coopth_t *thr, + struct coopth_per_thread_t *pth) +{ + int i; + + /* left threads usually atomic, but can nest due to coopth_join() etc */ + if (pth->data.left) + threads_left--; + pth->st = ST(NONE); + thr->cur_thr--; + if (thr->cur_thr == 0) { + int found = 0; + for (i = 0; i < threads_active; i++) { + if (active_tids[i] == thr->tid) { + assert(!found); + found++; + continue; + } + if (found) + active_tids[i - 1] = active_tids[i]; + } + assert(found); + threads_active--; + } + threads_total--; + + if (!pth->data.cancelled && !pth->data.left) { + if (!thr->custom) { + do_call_post(thr, pth); + } else { + assert(!thr->post_pth); + thr->post_pth = pth; + } + } + if (nothread_notifier) + nothread_notifier(threads_joinable + threads_left); +} + +static void coopth_retf(struct coopth_t *thr, struct coopth_per_thread_t *pth, + void (*retf)(int tid, int idx)) +{ + assert(pth->data.attached); + threads_joinable--; + if (retf) + retf(CIDX2(thr->tid, thr->cur_thr - 1)); + if (thr->ctxh.post) + thr->ctxh.post(thr->tid, thr->ctxh.arg, pth->args.thr.arg); + thr->ops->prep(CIDX2(thr->tid, thr->cur_thr - 1)); + pth->data.attached = 0; +} + +static void coopth_callf(struct coopth_t *thr, struct coopth_per_thread_t *pth) +{ + assert(!pth->data.attached); + if (thr->ctxh.pre) + thr->ctxh.pre(thr->tid, thr->ctxh.arg, pth->args.thr.arg); + threads_joinable++; + pth->data.attached = 1; +} + +static void coopth_callf_op(struct coopth_t *thr, + struct coopth_per_thread_t *pth) +{ + coopth_callf(thr, pth); + thr->ops->callf(CIDX2(thr->tid, thr->cur_thr - 1)); +} + +static void coopth_callf_chk(struct coopth_t *thr, + struct coopth_per_thread_t *pth) +{ + if (!thr->ctxh.pre) + dosemu_error("coopth: unsafe attach\n"); + coopth_callf_op(thr, pth); +} + +static struct coopth_per_thread_t *current_thr(struct coopth_t *thr) +{ + struct coopth_per_thread_t *pth; + assert(thr - coopthreads < MAX_COOPTHREADS); + if (!thr->cur_thr) { + error("coopth: schedule to inactive thread %i\n", thr->tid); + exit(2); + return NULL; + } + pth = &thr->pth[thr->cur_thr - 1]; + /* it must be running */ + assert(pth->st.state > COOPTHS_NONE); + return pth; +} + +static int __thread_run(struct coopth_t *thr, struct coopth_per_thread_t *pth) +{ + int ret = 0; + switch (pth->st.state) { + case COOPTHS_NONE: + error("Coopthreads error switch to inactive thread, exiting\n"); + exit(2); + break; + case COOPTHS_RUNNING: { + int jr, lr; + enum CoopthRet tret; + + /* We have 2 kinds of recursion for joinable threads: + * + * 1. (call it recursive thread invocation) + * main_thread -> coopth_start(thread1_func) -> return + * thread1_func() -> coopth_start(thread2_func) -> return + * (thread 1 returned, became zombie) + * thread2_func() -> return + * thread2 joined + * thread1 joined + * main_thread... + * + * 2. (call it nested thread invocation) + * main_thread -> coopth_start(thread1_func) -> return + * thread1_func() -> do_int_call_back() -> + * run_int_from_hlt() -> + * coopth_start(thread2_func) -> return + * thread2_func() -> return + * thread2 joined + * -> return from do_int_call_back() -> + * return from thread1_func() + * thread1 joined + * main_thread... + * + * Both cases are supported here, but the nested invocation + * is not supposed to be used as being too complex. + * Since do_int_call_back() was converted + * to coopth API, the nesting is avoided. + * If not true, we print an error. + * + * Also do not allow any recursion at all into detached threads + * or the mix of joinable+detached, but the left threads are allowed + * to do any mayhem. + */ + if (thread_running > left_running) { + static int warned; + if (!warned) { + warned = 1; + dosemu_error("Nested thread invocation detected, please fix! " + "(at=%i jr=%i)\n", pth->data.attached, + joinable_running); + } + } + jr = joinable_running; + if (pth->data.attached) + joinable_running++; + lr = left_running; + if (pth->data.left) { + assert(!pth->data.attached); + left_running++; + } + thread_running++; + tret = do_run_thread(thr, pth); + thread_running--; + left_running = lr; + joinable_running = jr; + if (tret == COOPTH_WAIT && pth->data.attached) + thr->ops->sleep(); + if (tret == COOPTH_SLEEP || tret == COOPTH_WAIT || + tret == COOPTH_YIELD) { + if (pth->data.sleep.func) { + /* oneshot sleep handler */ + pth->data.sleep.func(pth->data.sleep.arg); + pth->data.sleep.func = NULL; + } + if (thr->sleeph.pre) { + int sl_state; + switch (tret) { + #define DO_SL(x) \ + case COOPTH_##x: \ + sl_state = COOPTH_SL_##x; \ + break + DO_SL(YIELD); + DO_SL(WAIT); + DO_SL(SLEEP); + default: + assert(0); + break; + } + thr->sleeph.pre(thr->tid, sl_state); + } + } + /* normally we don't exit with RUNNING state any longer. + * this was happening in prev implementations though, so + * remove that assert if it ever hurts. */ + assert(pth->st.state != COOPTHS_RUNNING); + break; + } + case COOPTHS_SLEEPING: + if (pth->data.attached) + thr->ops->sleep(); + break; + case COOPTHS_SWITCH: + pth->data.atomic_switch = 0; + if (pth->st.sw_idx == idx_DONE) + ret = thr->cur_thr; // thread is terminating + sw_list[pth->st.sw_idx](thr, pth); + break; + } + return ret; +} + +static int thread_run(struct coopth_t *thr, struct coopth_per_thread_t *pth) +{ + int ret; + enum CoopthState state; + do { + ret = __thread_run(thr, pth); + state = pth->st.state; + } while (state == COOPTHS_RUNNING || (state == COOPTHS_SWITCH && + pth->data.atomic_switch)); + return ret; +} + +struct crun_ret coopth_run_thread_internal(int tid) +{ + struct coopth_t *thr; + struct coopth_per_thread_t *pth; + struct crun_ret ret = {}; + int term; + + check_tid(tid); + thr = &coopthreads[tid]; + pth = current_thr(thr); + if (!pth->data.attached) { + /* someone used coopth_unsafe_detach()? */ + error("HLT on detached thread\n"); + exit(2); + } + term = thread_run(thr, pth); + if (term) { + ret.term = term; + ret.idx = CIDX(tid, term - 1); + } + return ret; +} + +static void coopth_thread(void *arg) +{ + struct coopth_starter_args_t *volatile args = arg; + enum CoopthJmp jret; + if (args->thrdata->cancelled) { + /* can be cancelled before start - no cleanups set yet */ + args->thrdata->ret = COOPTH_DONE; + return; + } + co_set_data(co_current(co_handle), args->thrdata); + + jret = setjmp(args->thrdata->exit_jmp); + if (jret) { + switch (args->thrdata->jret) { + case COOPTH_JMP_NONE: + dosemu_error("something wrong\n"); + break; + case COOPTH_JMP_CANCEL: + if (args->thrdata->clnup.func) + args->thrdata->clnup.func(args->thrdata->clnup.arg); + break; + case COOPTH_JMP_EXIT: + break; + } + } else { + args->thr.func(args->thr.arg); + } + + args->thrdata->ret = COOPTH_DONE; +} + +static void call_prep(struct coopth_t *thr) +{ + int i; + + for (i = 0; i < MAX_COOP_RECUR_DEPTH; i++) + thr->ops->prep(CIDX2(thr->tid, i)); +} + +int coopth_create_internal(const char *name, coopth_func_t func, + const struct coopth_be_ops *ops) +{ + int num; + struct coopth_t *thr; + + assert(coopth_num < MAX_COOPTHREADS); + num = __sync_fetch_and_add(&coopth_num, 1); + thr = &coopthreads[num]; + thr->name = name; + thr->cur_thr = 0; + thr->off = 0; + thr->tid = num; + thr->len = 1; + thr->func = func; + thr->ops = ops; + thr->pthread = pthread_self(); + call_prep(thr); + return num; +} + +int coopth_create_multi_internal(const char *name, int len, + coopth_func_t func, + const struct coopth_be_ops *ops) +{ + int i, num; + + assert(len && coopth_num + len <= MAX_COOPTHREADS); + num = coopth_num; + __sync_fetch_and_add(&coopth_num, len); + for (i = 0; i < len; i++) { + struct coopth_t *thr = &coopthreads[num + i]; + thr->name = name; + thr->cur_thr = 0; + thr->off = i; + thr->tid = num + i; + thr->len = (i == 0 ? len : 1); + thr->func = func; + thr->ops = ops; + thr->pthread = pthread_self(); + call_prep(thr); + } + return num; +} + +static void check_tid(int tid) +{ + if (tid < 0 || tid >= coopth_num) { + dosemu_error("Wrong tid\n"); + exit(1); + } +} + +void coopth_ensure_sleeping(int tid) +{ + struct coopth_t *thr; + struct coopth_per_thread_t *pth; + check_tid(tid); + thr = &coopthreads[tid]; + pth = current_thr(thr); + assert(pth->st.state == COOPTHS_SLEEPING); +} + +static int do_start(struct coopth_t *thr, struct coopth_state_t st, void *arg) +{ + struct coopth_per_thread_t *pth; + int tn; + + if (thr->cur_thr >= MAX_COOP_RECUR_DEPTH) { + int i; + dosemu_error("Coopthreads recursion depth exceeded, %s off=%x\n", + thr->name, thr->off); + for (i = 0; i < thr->cur_thr; i++) { + error("\tthread %i state %i dbg 0x%016"PRIx64"\n", + i, thr->pth[i].st.state, + thr->ops->get_dbg_val(CIDX2(thr->tid, i))); + } + error("\tthread %i rejected\n", i); + exit(2); + return -1; + } + tn = thr->cur_thr++; + pth = &thr->pth[tn]; + if (thr->cur_thr > thr->max_thr) { + size_t stk_size = COOP_STK_SIZE(); + thr->max_thr = thr->cur_thr; + pth->stack = mmap(NULL, stk_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (pth->stack == MAP_FAILED) { + error("Unable to allocate stack\n"); + exit(21); + return -1; + } + pth->stk_size = stk_size; + } + pth->data.tid = &thr->tid; + pth->data.attached = 0; + pth->data.posth_num = 0; + pth->data.sleep.func = NULL; + pth->data.clnup.func = NULL; + pth->data.uhook.func = NULL; + pth->data.udata_num = 0; + pth->data.cancelled = 0; + pth->data.canc_disabled = 0; + pth->data.left = 0; + pth->data.atomic_switch = 0; + pth->data.jret = COOPTH_JMP_NONE; + pth->args.thr.func = thr->func; + pth->args.thr.arg = arg; + pth->args.thrdata = &pth->data; + pth->quick_sched = 0; + pth->retf = NULL; + pth->thread = co_create(co_handle, coopth_thread, &pth->args, pth->stack, + pth->stk_size); + if (!pth->thread) { + error("Thread create failure\n"); + exit(2); + return -1; + } + pth->st = st; + if (tn == 0) { + assert(threads_active < MAX_ACT_THRS); + active_tids[threads_active++] = thr->tid; + } else if (thr->pth[tn - 1].st.state == COOPTHS_SLEEPING) { + static int logged; + /* will have problems with wake-up by tid. It is possible + * to do a wakeup-specific lookup, but this is nasty, and + * the recursion itself is nasty too. Lets just print an + * error to force the caller to create a separate thread. + * vc.c does this to not sleep in the sighandling thread. + */ + if (!logged) { + dosemu_error("thread %s recursed (%i) over sleep\n", + thr->name, thr->cur_thr); + logged = 1; + } + } + threads_total++; + return tn; +} + +static int do_start_internal(struct coopth_t *thr, void *arg, + void (*retf)(int, int)) +{ + int num; + + num = do_start(thr, ST(RUNNING), arg); + if (num == -1) + return -1; + if (!thr->detached) { + struct coopth_per_thread_t *pth = &thr->pth[num]; + pth->retf = retf; + coopth_callf(thr, pth); + } + return CIDX(thr->tid, num); +} + +struct cstart_ret coopth_start_internal(int tid, void *arg, + void (*retf)(int, int)) +{ + struct cstart_ret ret; + struct coopth_t *thr; + + check_tid(tid); + thr = &coopthreads[tid]; + thr->custom = 0; + ret.idx = do_start_internal(thr, arg, retf); + ret.detached = thr->detached; + return ret; +} + +int coopth_start_custom_internal(int tid, void *arg) +{ + struct coopth_t *thr; + + check_tid(tid); + thr = &coopthreads[tid]; + assert(!thr->detached); + thr->custom = 1; + return do_start_internal(thr, arg, NULL); +} + +void coopth_call_post_internal(int tid) +{ + struct coopth_t *thr; + + check_tid(tid); + thr = &coopthreads[tid]; + assert(thr->custom); + if (!thr->post_pth) + return; + do_call_post(thr, thr->post_pth); + thr->post_pth = NULL; +} + +int coopth_set_ctx_handlers(int tid, coopth_hndl_t pre, coopth_hndl_t post, + void *arg) +{ + struct coopth_t *thr; + int i; + check_tid(tid); + for (i = 0; i < coopthreads[tid].len; i++) { + thr = &coopthreads[tid + i]; + thr->ctxh.pre = pre; + thr->ctxh.post = post; + thr->ctxh.arg = arg; + } + return 0; +} + +int coopth_set_sleep_handlers(int tid, coopth_sleep_hndl_t pre, + coopth_hndl_t post) +{ + struct coopth_t *thr; + int i; + check_tid(tid); + for (i = 0; i < coopthreads[tid].len; i++) { + thr = &coopthreads[tid + i]; + thr->sleeph.pre = pre; + thr->sleeph.post = post; + } + return 0; +} + +int coopth_set_permanent_post_handler(int tid, coopth_hndl_t func) +{ + struct coopth_t *thr; + int i; + check_tid(tid); + for (i = 0; i < coopthreads[tid].len; i++) { + thr = &coopthreads[tid + i]; + thr->post = func; + } + return 0; +} + +int coopth_set_detached(int tid) +{ + struct coopth_t *thr; + check_tid(tid); + thr = &coopthreads[tid]; + thr->detached = 1; + return 0; +} + +static int detach_sw(struct coopth_t *thr, struct coopth_per_thread_t *pth) +{ + switch (pth->st.sw_idx) { + case idx_NONE: + abort(); + break; + case idx_DETACH: + pth->st = ST(RUNNING); + break; + case idx_DONE: + /* this is special: thread already finished, can't be ran */ + do_del_thread(thr, pth); + return 1; + default: + sw_list[pth->st.sw_idx](thr, pth); + break; + } + return 0; +} + +static void do_detach(struct coopth_t *thr, struct coopth_per_thread_t *pth) +{ + /* this is really unsafe and should be used only if + * the DOS side of the thread have disappeared. */ + pth->data.attached = 0; + threads_joinable--; + /* notify back-end */ + thr->ops->prep(CIDX2(thr->tid, thr->cur_thr - 1)); + /* first deal with state switching. As the result of this, + * thread should either terminate or became runable. */ + if (pth->st.state == COOPTHS_SWITCH) { + if (detach_sw(thr, pth)) + return; + } + assert(pth->st.state != COOPTHS_SWITCH); + if (pth->data.cancelled) { + /* run thread so it can reach cancellation point */ + enum CoopthRet tret = do_run_thread(thr, pth); + assert(tret == COOPTH_DELETE); + } +} + +int coopth_unsafe_detach(int tid, const char *who) +{ + struct coopth_t *thr; + struct coopth_per_thread_t *pth; + check_tid(tid); + dbug_printf("coopth_unsafe_detach() called by %s\n", who); + thr = &coopthreads[tid]; + pth = current_thr(thr); + assert(pth->data.attached); + do_detach(thr, pth); + return 0; +} + +static int run_traverser(int (*pred)(struct coopth_per_thread_t *, void*), + void *pred_arg, void (*post)(struct coopth_per_thread_t *)) +{ + int i; + int cnt = 0; + for (i = 0; i < threads_active; i++) { + int tid = active_tids[i]; + struct coopth_t *thr = &coopthreads[tid]; + struct coopth_per_thread_t *pth = current_thr(thr); + /* only run detached threads here */ + if (pth->data.attached) + continue; + if (pth->data.left) { + if (!left_running) + error("coopth: switching to left thread?\n"); + continue; + } + if (pred && !pred(pth, pred_arg)) + continue; + thread_run(thr, pth); + if (post) + post(pth); + cnt++; + } + return cnt; +} + +static int quick_sched_pred(struct coopth_per_thread_t *pth, void *arg) +{ + return pth->quick_sched; +} + +static void quick_sched_post(struct coopth_per_thread_t *pth) +{ + pth->quick_sched = 0; +} + +void coopth_run(void) +{ + assert(DETACHED_RUNNING >= 0); + if (DETACHED_RUNNING) + return; + run_traverser(NULL, NULL, NULL); + while (run_traverser(quick_sched_pred, NULL, quick_sched_post)); +} + +void coopth_run_tid(int tid) +{ + struct coopth_t *thr = &coopthreads[tid]; + struct coopth_per_thread_t *pth = current_thr(thr); + assert(DETACHED_RUNNING >= 0); + if (DETACHED_RUNNING) + return; + assert(!pth->data.attached && !pth->data.left); + thread_run(thr, pth); +} + +static int __coopth_is_in_thread(int warn, const char *f) +{ + if (!thread_running && warn) { + static int warned; + if (!warned) { + warned = 1; + dosemu_error("Coopth: %s: not in thread!\n", f); + } + } + return thread_running; +} + +#define _coopth_is_in_thread() __coopth_is_in_thread(1, __func__) +#define _coopth_is_in_thread_nowarn() __coopth_is_in_thread(0, __func__) + +int coopth_get_tid(void) +{ + struct coopth_thrdata_t *thdata; + if (!_coopth_is_in_thread_nowarn()) + return -1; + thdata = co_get_data(co_current(co_handle)); + return *thdata->tid; +} + +int coopth_add_post_handler(coopth_func_t func, void *arg) +{ + struct coopth_thrdata_t *thdata; + assert(_coopth_is_in_thread()); + thdata = co_get_data(co_current(co_handle)); + assert(thdata->posth_num < MAX_POST_H); + thdata->post[thdata->posth_num].func = func; + thdata->post[thdata->posth_num].arg = arg; + thdata->posth_num++; + return 0; +} + +int coopth_set_sleep_handler(coopth_func_t func, void *arg) +{ + struct coopth_thrdata_t *thdata; + assert(_coopth_is_in_thread()); + thdata = co_get_data(co_current(co_handle)); + thdata->sleep.func = func; + thdata->sleep.arg = arg; + return 0; +} + +int coopth_set_cleanup_handler(coopth_func_t func, void *arg) +{ + struct coopth_thrdata_t *thdata; + assert(_coopth_is_in_thread()); + thdata = co_get_data(co_current(co_handle)); + thdata->clnup.func = func; + thdata->clnup.arg = arg; + return 0; +} + +void coopth_push_user_data(int tid, void *udata) +{ + struct coopth_t *thr; + struct coopth_per_thread_t *pth; + check_tid(tid); + thr = &coopthreads[tid]; + pth = current_thr(thr); + assert(pth->data.udata_num < MAX_UDATA); + pth->data.udata[pth->data.udata_num++] = udata; +} + +void coopth_push_user_data_cur(void *udata) +{ + struct coopth_thrdata_t *thdata; + assert(_coopth_is_in_thread()); + thdata = co_get_data(co_current(co_handle)); + assert(thdata->udata_num < MAX_UDATA); + thdata->udata[thdata->udata_num++] = udata; +} + +void *coopth_pop_user_data(int tid) +{ + struct coopth_t *thr; + struct coopth_per_thread_t *pth; + check_tid(tid); + thr = &coopthreads[tid]; + pth = current_thr(thr); + assert(pth->data.udata_num > 0); + return pth->data.udata[--pth->data.udata_num]; +} + +void *coopth_pop_user_data_cur(void) +{ + struct coopth_thrdata_t *thdata; + assert(_coopth_is_in_thread()); + thdata = co_get_data(co_current(co_handle)); + assert(thdata->udata_num > 0); + return thdata->udata[--thdata->udata_num]; +} + +static void switch_state(enum CoopthRet ret) +{ + struct coopth_thrdata_t *thdata = co_get_data(co_current(co_handle)); + assert(!thdata->cancelled); + assert(!thdata->left); + thdata->ret = ret; + while (1) { + co_resume(co_handle); + if (!thdata->uhook.func) + break; + thdata->uhook.func(thdata->uhook.arg); + thdata->uhook.func = NULL; + } +} + +static void ensure_attached(void) +{ + struct coopth_thrdata_t *thdata = co_get_data(co_current(co_handle)); + if (!thdata->attached) { + dosemu_error("Not allowed for detached thread %i, %s\n", + *thdata->tid, coopthreads[*thdata->tid].name); + exit(2); + } +} + +static int is_detached(void) +{ + struct coopth_thrdata_t *thdata = co_get_data(co_current(co_handle)); + assert(thdata); + return (!thdata->attached); +} + +static void do_ljmp(struct coopth_thrdata_t *thdata, enum CoopthJmp jret) +{ + jmp_buf *jmp = &thdata->exit_jmp; + if (thdata->jret != COOPTH_JMP_NONE) + dosemu_error("coopth: cancel not handled\n"); + thdata->jret = jret; + longjmp(*jmp, 1); +} + +static int check_cancel(void) +{ + /* cancellation point */ + struct coopth_thrdata_t *thdata = co_get_data(co_current(co_handle)); + if (!thdata->cancelled) + return 0; + if (thdata->canc_disabled) + return 1; + do_ljmp(thdata, COOPTH_JMP_CANCEL); + return -1; /* never reached */ +} + +static struct coopth_t *on_thread(unsigned id) +{ + int i; + for (i = 0; i < threads_active; i++) { + int tid = active_tids[i]; + struct coopth_t *thr = &coopthreads[tid]; + assert(thr->cur_thr > 0); + if (thr->ops->id != id) + continue; + if (thr->ops->is_active(CIDX2(tid, thr->cur_thr - 1))) + return thr; + } + return NULL; +} + +static int current_active(void) +{ + int tid = coopth_get_tid(); + struct coopth_t *thr = &coopthreads[tid]; + assert(thr->cur_thr > 0); + return thr->ops->is_active(CIDX2(tid, thr->cur_thr - 1)); +} + +int coopth_yield(void) +{ + assert(_coopth_is_in_thread()); + switch_state(COOPTH_YIELD); + if (check_cancel()) + return -1; + return 1; +} + +int coopth_sched(void) +{ + assert(_coopth_is_in_thread()); + ensure_attached(); + /* the check below means that we switch to DOS code, not dosemu code */ + assert(!current_active()); + switch_state(COOPTH_SCHED); + /* return -1 if canceled */ + return -check_cancel(); +} + +int coopth_sched_cond(void) +{ + assert(_coopth_is_in_thread()); + ensure_attached(); + /* if our thread still active, do nothing */ + if (current_active()) + return 0; + switch_state(COOPTH_SCHED); + if (check_cancel()) + return -1; + return 1; +} + +int coopth_wait(void) +{ + assert(_coopth_is_in_thread()); + ensure_attached(); + switch_state(COOPTH_WAIT); + if (check_cancel()) + return -1; + return 1; +} + +int coopth_sleep(void) +{ + int tid = coopth_get_tid(); + + assert(_coopth_is_in_thread()); + if (!is_detached()) + coopthreads[tid].ops->to_sleep(tid); + switch_state(COOPTH_SLEEP); + if (check_cancel()) + return -1; + return 1; +} + +static void ensure_single(struct coopth_thrdata_t *thdata) +{ + struct coopth_t *thr = &coopthreads[*thdata->tid]; + if (thr->cur_thr != 1) + dosemu_error("coopth: nested=%i (expected 1)\n", thr->cur_thr); +} + +void coopth_ensure_single(int tid) +{ + struct coopth_t *thr = &coopthreads[tid]; + if (thr->cur_thr != 1) + dosemu_error("coopth: nested=%i (expected 1)\n", thr->cur_thr); +} + +/* attach some thread to current context */ +void coopth_attach_to_cur(int tid) +{ + struct coopth_t *thr; + struct coopth_per_thread_t *pth; + check_tid(tid); + thr = &coopthreads[tid]; + pth = current_thr(thr); + assert(!pth->data.attached); + coopth_callf_op(thr, pth); +} + +void coopth_attach(void) +{ + struct coopth_thrdata_t *thdata; + assert(_coopth_is_in_thread()); + thdata = co_get_data(co_current(co_handle)); + ensure_single(thdata); + if (thdata->attached) + return; + switch_state(COOPTH_ATTACH); +} + +void coopth_exit(void) +{ + struct coopth_thrdata_t *thdata; + assert(_coopth_is_in_thread()); + thdata = co_get_data(co_current(co_handle)); + do_ljmp(thdata, COOPTH_JMP_EXIT); +} + +void coopth_detach(void) +{ + struct coopth_thrdata_t *thdata; + assert(_coopth_is_in_thread()); + thdata = co_get_data(co_current(co_handle)); + ensure_single(thdata); + if (!thdata->attached) + return; + switch_state(COOPTH_DETACH); +} + +void coopth_abandon(void) +{ + struct coopth_thrdata_t *thdata; + if (!_coopth_is_in_thread_nowarn()) + return; + thdata = co_get_data(co_current(co_handle)); + if (thdata->left) + return; + thdata->posth_num = 0; + /* leaving detached thread should be atomic even wrt other detached + * threads. This is needed so that DPMI cannot run concurrently with + * leavedos(). + * for joinable threads leaving should be atomic only wrt DOS code, + * but, because of an optimization loop in run_vm86(), it is actually + * also atomic wrt detached threads. + * The detached leave operation calls the permanent post handler + * immediately, and it should be called from the context of the main + * thread. This is the reason why coopth_leave() for detached thread + * cannot be a no-op (non-permanent post handlers are discarded). */ + if (!thdata->attached) + thdata->atomic_switch = 1; + switch_state(COOPTH_LEAVE); +} + +/* for some time coopth_leave() was implemented on top of coopth_detach(). + * This appeared not the best implementation. In particular, the commit + * 551371689 was needed to make leaving operation atomic, but this is + * not needed for detached threads at all. While the detached threads + * has a separate entry point (via coopth_run()), the left thread must + * not have a separate entry point. So it appeared better to return the + * special type "left" threads. */ +static void do_leave(struct coopth_thrdata_t *thdata) +{ + if (thdata->posth_num) + dosemu_error("coopth: leaving thread with active post handlers\n"); + if (!current_active()) + dosemu_error("coopth: leaving descheduled thread\n"); + if (!thdata->attached) + dosemu_error("coopth: leaving detached thread\n"); + switch_state(COOPTH_LEAVE); +} + +void coopth_leave(void) +{ + struct coopth_thrdata_t *thdata; + if (!_coopth_is_in_thread_nowarn()) + return; + thdata = co_get_data(co_current(co_handle)); + if (thdata->left) + return; + do_leave(thdata); +} + +void coopth_leave_internal(void) +{ + struct coopth_thrdata_t *thdata; + if (!_coopth_is_in_thread_nowarn()) + return; + thdata = co_get_data(co_current(co_handle)); + if (thdata->left) + return; + assert(coopthreads[*thdata->tid].custom); + do_leave(thdata); +} + +static void do_awake(struct coopth_per_thread_t *pth) +{ + if (pth->st.state != COOPTHS_SLEEPING) { + dosemu_error("wakeup on non-sleeping thread %i\n", *pth->data.tid); + return; + } + pth->st = SW_ST(AWAKEN); + if (!pth->data.attached) + pth->quick_sched = 1; // optimize DPMI switches +} + +void coopth_wake_up(int tid) +{ + struct coopth_t *thr; + struct coopth_per_thread_t *pth; + check_tid(tid); + thr = &coopthreads[tid]; + pth = current_thr(thr); + do_awake(pth); +} + +static void do_cancel(struct coopth_t *thr, struct coopth_per_thread_t *pth) +{ + pth->data.cancelled = 1; + if (pth->data.attached) { + if (pth->st.state == COOPTHS_SLEEPING) + do_awake(pth); + } else { + /* ignore current state and run the thread. + * It will reach the cancellation point and exit with COOPTH_DONE, + * after which, do_run_thread() will delete it. */ + enum CoopthRet tret = do_run_thread(thr, pth); + assert(tret == COOPTH_DELETE); + } +} + +void coopth_unsafe_shutdown(void) +{ + int i; + struct coopth_thrdata_t *thdata = NULL; + if (_coopth_is_in_thread_nowarn()) { + thdata = co_get_data(co_current(co_handle)); + assert(thdata); + } +again: + for (i = 0; i < threads_active; i++) { + int tid = active_tids[i]; + struct coopth_t *thr = &coopthreads[tid]; + struct coopth_per_thread_t *pth = current_thr(thr); + if (!pth->data.attached) + continue; + /* don't cancel own thread */ + assert(!thdata || *thdata->tid != tid); + do_cancel(thr, pth); + do_detach(thr, pth); + /* array changed, restart the whole loop */ + goto again; + } +} + +void coopth_cancel(int tid) +{ + struct coopth_t *thr; + struct coopth_per_thread_t *pth; + check_tid(tid); + thr = &coopthreads[tid]; + pth = current_thr(thr); + /* see if canceling self */ + if (_coopth_is_in_thread_nowarn() && tid == coopth_get_tid()) { + assert(pth->data.left); + ensure_single(&pth->data); + return; + } + do_cancel(thr, pth); +} + +static void do_join(struct coopth_per_thread_t *pth, void (*helper)(void)) +{ + while (pth->st.state != COOPTHS_NONE) + helper(); +} + +void coopth_join_internal(int tid, void (*helper)(void)) +{ + struct coopth_t *thr; + struct coopth_per_thread_t *pth; + /* since main thread can call this, we have to use helper + * function instead of just coopth_sched(). As a result, + * recursion into run_vm86() can happen. Hope its safe. */ + assert(!_coopth_is_in_thread_nowarn() || is_detached()); + check_tid(tid); + thr = &coopthreads[tid]; + pth = current_thr(thr); + assert(pth->data.attached); + do_join(pth, helper); +} + +/* desperate cleanup attempt, not extremely reliable */ +int coopth_flush_internal(unsigned id, void (*helper)(void)) +{ + struct coopth_t *thr; + assert(!_coopth_is_in_thread_nowarn() || is_detached()); + while (threads_joinable) { + struct coopth_per_thread_t *pth; + /* the sleeping threads are unlikely to be found here. + * This is mainly to flush zombies. */ + thr = on_thread(id); + if (!thr) + break; + pth = current_thr(thr); + assert(pth->data.attached); + do_cancel(thr, pth); + do_join(pth, helper); + } + if (threads_joinable) + g_printf("Coopth: %i threads stalled\n", threads_joinable); + return threads_joinable; +} + +void coopth_done(void) +{ + int i, tt, itd, it; + struct coopth_thrdata_t *thdata = NULL; + it = _coopth_is_in_thread_nowarn(); + itd = it; +// assert(!it || is_detached()); + if (it) { + thdata = co_get_data(co_current(co_handle)); + assert(thdata); + /* unfortunately the shutdown can run from signal handler - + * in this case we can be in a joinable thread interrupted + * by signal, and there is no way to leave that thread. */ + if (!is_detached()) + itd = 0; + } + /* there is no safe way to delete joinable threads without joining, + * so print error only if there are also detached threads left */ + if (threads_total > threads_joinable + itd) + error("Coopth: not all detached threads properly shut down\n"); +again: + tt = threads_total; + for (i = 0; i < threads_active; i++) { + int tid = active_tids[i]; + struct coopth_t *thr = &coopthreads[tid]; + struct coopth_per_thread_t *pth = current_thr(thr); + /* don't cancel own thread */ + if (thdata && *thdata->tid == tid) + continue; + if (!pth->data.attached) { + error("\ttid=%i state=%i name=\"%s\" off=%#x\n", tid, pth->st.state, + thr->name, thr->off); + do_cancel(thr, pth); + assert(threads_total == tt - 1); + /* retry the loop as the array changed */ + goto again; + } else { + dbug_printf("\ttid=%i state=%i name=%s off=%#x\n", tid, pth->st.state, + thr->name, thr->off); + } + } + /* at this point all detached threads should be killed, + * except perhaps current one */ + assert(threads_total == threads_joinable + itd); + + for (i = 0; i < coopth_num; i++) { + struct coopth_t *thr = &coopthreads[i]; + int j; + + if (!pthread_equal(thr->pthread, pthread_self())) + continue; + /* don't free own thread */ + if (thdata && *thdata->tid == i) + continue; + for (j = thr->cur_thr; j < thr->max_thr; j++) { + struct coopth_per_thread_t *pth = &thr->pth[j]; + munmap(pth->stack, pth->stk_size); + } + } + if (!threads_total) + co_thread_cleanup(co_handle); + else + g_printf("coopth: leaked %i threads\n", threads_total); +} + +int coopth_wants_sleep_internal(unsigned id) +{ + struct coopth_t *thr = on_thread(id); + struct coopth_per_thread_t *pth; + if (!thr) + return 0; + pth = current_thr(thr); + return (pth->st.state == COOPTHS_SLEEPING || + pth->st.state == COOPTHS_SWITCH); +} + +void coopth_set_nothread_notifier(void (*notifier)(int)) +{ + nothread_notifier = notifier; +} + +void coopth_cancel_disable_cur(void) +{ + struct coopth_thrdata_t *thdata; + assert(_coopth_is_in_thread()); + thdata = co_get_data(co_current(co_handle)); + thdata->canc_disabled = 1; +} + +void coopth_cancel_enable_cur(void) +{ + struct coopth_thrdata_t *thdata; + assert(_coopth_is_in_thread()); + thdata = co_get_data(co_current(co_handle)); + thdata->canc_disabled = 0; +} + +#define MAX_BT_FRAMES 128 + +struct bt_s { + void **frames; + int size; + int ret_size; +}; + +static void do_bt(void *arg) +{ +#ifdef HAVE_BACKTRACE + struct bt_s *bt = arg; + bt->ret_size = backtrace(bt->frames, bt->size); +#endif +} + +void coopth_dump(int all) +{ + int i; + error("@coopthreads dump (%i total, %i joinable):\n", + threads_total, threads_joinable); + for (i = 0; i < threads_active; i++) { + int tid = active_tids[i]; + struct coopth_t *thr = &coopthreads[tid]; + if (all || !thr->detached) { + int j; + error("@Thread \"%s\" (%i)\n", thr->name, thr->cur_thr); + for (j = 0; j < thr->cur_thr; j++) { + struct coopth_per_thread_t *pth = &thr->pth[j]; + void *bt_buf[MAX_BT_FRAMES]; + struct bt_s bt; + if (pth->st.state != COOPTHS_SWITCH || + pth->st.sw_idx == idx_DONE) + continue; + bt.frames = bt_buf; + bt.size = MAX_BT_FRAMES; + bt.ret_size = 0; + pth->data.uhook.func = do_bt; + pth->data.uhook.arg = &bt; + /* bypass entire state machine */ + co_call(pth->thread); +#ifdef HAVE_BACKTRACE + if (bt.ret_size) { + char **syms = backtrace_symbols(bt_buf, bt.ret_size); + int k; + for (k = 0; k < bt.ret_size; k++) + error("@%s\n", syms[k]); + free(syms); + } +#endif + } + } + } +} diff --git a/src/base/core/dump.c b/src/base/core/dump.c new file mode 100644 index 0000000..c2e398c --- /dev/null +++ b/src/base/core/dump.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include + +#include "memory.h" +#include "emu.h" +#include "cpu.h" +#include "port.h" +#include "emu-ldt.h" +#include "emudpmi.h" +#include "int.h" +#include "mapping.h" +#ifdef X86_EMULATOR +#include "cpu-emu.h" +#endif +#include "dis8086.h" + +char *emu_disasm(unsigned int ip) +{ + static char buf[2048]; +#ifdef USE_MHPDBG + char frmtbuf[1024]; + int rc, i; + unsigned int cp; + char *p; + unsigned int refseg; + unsigned int ref; + + cp = SEGOFF2LINEAR(_CS, _IP); + refseg = SREG(cs); + + rc = dis_8086(cp, frmtbuf, 0, &ref, refseg * 16); + + p = buf; + for (i=0; i src/base/misc/dump.c */ +void show_regs(void) +{ + int i; + unsigned int sp, cp; + + cp = SEGOFF2LINEAR(_CS, _IP); + if (cp < 1024) { + dbug_printf("Ain't gonna do it, cs=0x%x, eip=0x%x\n",SREG(cs),LWORD(eip)); + return; + } + + if (!LWORD(esp)) + sp = SEGOFF2LINEAR(_SS, _SP) + 0x8000; + else + sp = SEGOFF2LINEAR(_SS, _SP); + + dbug_printf("Real-mode state dump:\n"); + dbug_printf("EIP: %04x:%08x", SREG(cs), REG(eip)); + dbug_printf(" ESP: %04x:%08x", SREG(ss), REG(esp)); +#if 1 + dbug_printf(" VFLAGS(b): "); + for (i = (1 << 0x14); i > 0; i = (i >> 1)) { + dbug_printf((vflags & i) ? "1" : "0"); + if (i & 0x10100) dbug_printf(" "); + } +#else + dbug_printf(" VFLAGS(b): "); + for (i = (1 << 0x11); i > 0; i = (i >> 1)) + dbug_printf((vflags & i) ? "1" : "0"); +#endif + dbug_printf("\nEAX: %08x EBX: %08x ECX: %08x EDX: %08x VFLAGS(h): %08lx", + REG(eax), REG(ebx), REG(ecx), REG(edx), (unsigned long)vflags); + dbug_printf("\nESI: %08x EDI: %08x EBP: %08x", + REG(esi), REG(edi), REG(ebp)); + dbug_printf(" DS: %04x ES: %04x FS: %04x GS: %04x\n", + SREG(ds), SREG(es), SREG(fs), SREG(gs)); + + /* display vflags symbolically...the #f "stringizes" the macro name */ +#define PFLAG(f) if (REG(eflags)&(f)) dbug_printf(#f" ") + + dbug_printf("FLAGS: "); + PFLAG(CF); + PFLAG(PF); + PFLAG(AF); + PFLAG(ZF); + PFLAG(SF); + PFLAG(TF); + PFLAG(IF); + PFLAG(DF); + PFLAG(OF); + PFLAG(NT); + PFLAG(RF); + PFLAG(VM); + PFLAG(AC); + PFLAG(VIF); + PFLAG(VIP); + dbug_printf(" IOPL: %u\n", (unsigned) ((vflags & IOPL_MASK) >> 12)); + + /* display the 10 bytes before and after CS:EIP. the -> points + * to the byte at address CS:EIP + */ + if (sp < 0xa0000 && sp > 10) { + dbug_printf("STACK: "); + sp -= 10; + for (i = 0; i < 10; i++) + dbug_printf("%02x ", READ_BYTE(sp++)); + dbug_printf("-> "); + for (i = 0; i < 10; i++) + dbug_printf("%02x ", READ_BYTE(sp++)); + dbug_printf("\n"); + } + dbug_printf("OPS : "); + cp -= 10; + for (i = 0; i < 10; i++) + dbug_printf("%02x ", READ_BYTE(cp++)); + dbug_printf("-> "); + for (i = 0; i < 10; i++) + dbug_printf("%02x ", READ_BYTE(cp++)); + dbug_printf("\n\t%s\n", emu_disasm(0)); +} + +void +show_ints(int min, int max) +{ + int i, b; + + max = (max - min) / 3; + for (i = 0, b = min; i <= max; i++, b += 3) { + g_printf("%02x| %04x:%04x->%06x ", b, ISEG(b), IOFF(b), + IVEC(b)); + g_printf("%02x| %04x:%04x->%06x ", b + 1, ISEG(b + 1), IOFF(b + 1), + IVEC(b + 1)); + g_printf("%02x| %04x:%04x->%06x\n", b + 2, ISEG(b + 2), IOFF(b + 2), + IVEC(b + 2)); + } +} + +#if MAX_SELECTORS != 8192 +#error MAX_SELECTORS needs to be 8192 +#endif + +#define IsSegment32(s) dpmi_segment_is32(s) + +void dump_state(void) +{ + cpuctx_t *scp = dpmi_get_scp(); + show_regs(); + if (scp) + dbug_printf("\nProtected-mode state dump:\n%s\n", DPMI_show_state(scp)); +} diff --git a/src/base/core/dyndeb.c b/src/base/core/dyndeb.c new file mode 100644 index 0000000..31eac75 --- /dev/null +++ b/src/base/core/dyndeb.c @@ -0,0 +1,298 @@ +/* dynamic debug handlers - by Tim Bird */ +/* modified to support debug levels -- peak */ +/* Rehash so we aren't changing the code all of the time. Eric Biederman */ +#include +#include +#include "emu.h" +#include "dosemu_debug.h" +#include "dosemu_config.h" +#include "init.h" +#include "int.h" +#include "port.h" + + +FILE *dbg_fd; +#ifdef DONT_DEBUG_BOOT +static struct debug_class debug_save[DEBUG_CLASSES]; +#endif +static struct debug_class debug[DEBUG_CLASSES]; +unsigned char debug_levels[DEBUG_CLASSES]; +int shut_debug; + +#ifndef NO_DEBUGPRINT_AT_ALL + +int register_debug_class(int letter, void (*change_level)(int level), const char *help_text) +{ + struct debug_class *cls; + if (letter >= DEBUG_CLASSES) { + abort(); + } + if ((letter >= '0') && (letter <= '9')) { + abort(); + } + cls = &debug[letter]; + if (cls->letter) { + abort(); + } + cls->letter = letter; + cls->change_level = change_level; + cls->help_text = help_text; + debug_levels[letter] = 0; + return 0; +} + +int unregister_debug_class(int letter) +{ + struct debug_class *cls; + if (letter >= DEBUG_CLASSES) { + return -1; + } + cls = &debug[letter]; + if (!cls->letter) { + return -1; + } + memset(cls, 0, sizeof(*cls)); + return 0; +} + +int set_debug_level(int letter, int level) +{ + struct debug_class *cls; + if (letter >= DEBUG_CLASSES) { + return -1; + } + cls = &debug[letter]; + if (!cls->letter) { + return -1; + } + if (debug_levels[letter] != level) { + debug_levels[letter] = level; + if (cls->change_level) { + cls->change_level(level); + } + } + return 0; +} + +/* + * DANG_BEGIN_FUNCTION parse_debugflags + * + * arguments: + * s - string of options. + * + * description: + * This part is fairly flexible...you specify the debugging + * flags you wish with -D string. The string consists of the following + * characters: + turns the following options on (initial state) - + * turns the following options off a turns all the options on/off, + * depending on whether +/- is set 0-9 sets debug levels (0 is off, 9 is + * most verbose) # where # is a letter from the valid option list (see + * docs), turns that option off/on depending on the +/- state. + * + * Any option letter can occur in any place. Even meaningless combinations, + * such as "01-a-1+0vk" will be parsed without error, so be careful. Some + * options are set by default, some are clear. This is subject to my whim. + * You can ensure which are set by explicitly specifying. + * + * DANG_END_FUNCTION + */ +int parse_debugflags(const char *s, unsigned char flag) +{ + char c; + int ret = 0; + + dbug_printf("debug flags: %s\n", s); + while ((c = *(s++))) + switch (c) { + case '+': /* begin options to turn on */ + if (!flag) + flag = 1; + break; + case '-': /* begin options to turn off */ + flag = 0; + break; + case '0'...'9': /* set debug level, 0 is off, 9 is most + * verbose */ + flag = c - '0'; + break; + default: + ret = set_debug_level(c, flag); + if (ret >= 0) { + ret = 0; + } else { + fprintf(stderr, "Unknown debug-msg mask: %c\n\r", c); + dbug_printf("Unknown debug-msg mask: %c\n", c); + ret = 1; + } + } + return ret; +} + +int SetDebugFlagsHelper(char *debugStr) +{ + return parse_debugflags(debugStr, 0); +} + +static char DebugFlag(int f) +{ + if (f == 0) + return '-'; + else if (f >= 2 && f <= 9) + return f + '0'; + else + return '+'; +} + +int GetDebugFlagsHelper(char *debugStr, int print) +{ + int i; + struct debug_class *cls; + + if (print) dbug_printf("GetDebugFlagsHelper\n"); + if (print) dbug_printf("debugStr at %p\n", debugStr); + i = 0; + + for(cls = debug; cls <= &debug[DEBUG_CLASSES-1]; cls++) { + if (!cls->letter) { + continue; + } + debugStr[i++] = DebugFlag(debug_levels[cls->letter]); + debugStr[i++] = cls->letter; + } + + debugStr[i++] = '\0'; + if (print) dbug_printf("debugStr is %s\n", debugStr); + + return (0); +} + +int GetDebugInfoHelper(char *buf, int bufsize) +{ + struct debug_class *cls; + int num = 0, col; + char ws; + + for (cls = debug, col = 0; cls <= &debug[DEBUG_CLASSES - 1]; cls++) { + if (!cls->letter) + continue; + + if (col++ % 3 != 0) + ws = ' '; + else + ws = '\n'; + + num += snprintf(buf + num, bufsize - num, "%c%c%c: %-21s", ws, + DebugFlag(debug_levels[cls->letter]), cls->letter, cls->help_text); + + if (num >= bufsize) // snprintf output was truncated + return 0; + } + + num += snprintf(buf + num, bufsize - num, "\n"); + if (num >= bufsize) // snprintf output was truncated + return 0; + + return num; +} + +void print_debug_usage(FILE *stream) +{ + struct debug_class *cls; + int i; + fprintf(stream, + " -D set debug-msg mask to flags {+-}{0-9}{"); + for(cls = debug; cls <= &debug[DEBUG_CLASSES-1]; cls++) { + if (cls->letter) { + putc(cls->letter, stream); + } + } + fprintf(stream, "}\n"); + i = 0; + for(cls = debug; cls <= &debug[DEBUG_CLASSES-1]; cls++) { + if (!cls->letter) { + continue; + } + if ((i & 1) == 0) { + fprintf(stream, " "); + } + fprintf(stream, " %c=%-33.33s", + cls->letter, cls->help_text); + if ((i & 1) == 1) { + fprintf(stream, "\n"); + } + i++; + } + if ((i & 1) == 1) { + fprintf(stream, "\n"); + } +} + +static void all_change_level(int level) +{ + int c; + for (c = 0; c < DEBUG_CLASSES; c++) { + if (c != 'a') { + set_debug_level(c, level); + } + } +} +static void int21_change_level(int level) +{ +} +static void port_trace_change_level(int level) +{ + init_port_traceing(); +} + +static void config_change_level(int level) +{ + if (config_check_only && !level) { + set_debug_level('c', 1); + } +} + +CONSTRUCTOR(static void init(void)) +{ + register_debug_class('#', 0, "default int"); + register_debug_class('A', 0, "ASPI"); + register_debug_class('B', 0, "dosdebug trace"); + register_debug_class('C', 0, "CDROM"); + register_debug_class('D', int21_change_level, "dos int 21h"); + register_debug_class('E', 0, "EMS"); + register_debug_class('F', 0, "MMIO trace"); + register_debug_class('I', 0, "IPC"); + register_debug_class('N', 0, "NE2000 emulation"); + register_debug_class('P', 0, "Packet driver"); + register_debug_class('Q', 0, "Mapping driver"); + register_debug_class('R', 0, "disk READ"); + register_debug_class('S', 0, "SOUND"); + register_debug_class('T', port_trace_change_level, "I/O trace"); + register_debug_class('M', 0, "DPMI"); + register_debug_class('W', 0, "disk WRITE"); + register_debug_class('X', 0, "X support"); + register_debug_class('Z', 0, "PCI"); + register_debug_class('a', all_change_level, "Set all levels"); + register_debug_class('c', config_change_level, "configuration"); + register_debug_class('d', 0, "disk msgs"); +#ifdef X86_EMULATOR + register_debug_class('e', 0, "cpu-emu"); +#endif + register_debug_class('g', 0, "general messages"); + register_debug_class('h', 0, "hardware"); + register_debug_class('i', 0, "I/O instructions"); + register_debug_class('j', 0, "joystick"); + register_debug_class('k', 0, "keyboard"); + register_debug_class('m', 0, "mouse"); + register_debug_class('p', 0, "printer"); + register_debug_class('n', 0, "IPX network"); + register_debug_class('r', 0, "PIC request"); + register_debug_class('s', 0, "serial"); +#ifdef TRACE_DPMI + register_debug_class('t', 0, "dpmi trace"); +#endif + register_debug_class('v', 0, "video"); + register_debug_class('w', 0, "warnings"); + register_debug_class('x', 0, "XMS"); +}; + +#endif /* ! NO_DEBUGPRINT_AT_ALL */ diff --git a/src/base/core/emu.c b/src/base/core/emu.c new file mode 100644 index 0000000..bad94fb --- /dev/null +++ b/src/base/core/emu.c @@ -0,0 +1,582 @@ +/* + * Extensions by Robert Sanders, 1992-93 + * + * DANG_BEGIN_MODULE + * + * REMARK + * Here is where DOSEMU gets booted. From emu.c external calls are made to + * the specific I/O systems (video/keyboard/serial/etc...) to initialize + * them. Memory is cleared/set up and the boot sector is read from the + * boot drive. Many SIGNALS are set so that DOSEMU can exploit things like + * timers, I/O signals, illegal instructions, etc... When every system + * gives the green light, vm86() is called to switch into vm86 mode and + * start executing i86 code. + * + * The vm86() function will return to DOSEMU when certain `exceptions` occur + * as when some interrupt instructions occur (0xcd). + * + * The top level function emulate() is called from dos.c by way of a dll + * entry point. + * + * /REMARK + * DANG_END_MODULE + * + */ + + +/* + * DANG_BEGIN_REMARK + * DOSEMU must not work within the 1 meg DOS limit, so + * start of code is loaded at a higher address, at some time this could + * conflict with other shared libs. If DOSEMU is compiled statically + * (without shared libs), and org instruction is used to provide the jump + * above 1 meg. + * DANG_END_REMARK + */ + +#include +#include +#include +#include +#include +#include +#ifndef EDEADLOCK + #define EDEADLOCK EDEADLK +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" +#include "memory.h" + +#include "emu.h" + +#include "mhpdbg.h" +#include "bios.h" +#include "video.h" +#include "timers.h" +#include "cmos.h" +#include "mouse.h" +#include "disks.h" +#include "xms.h" +#include "ipx.h" /* TRB - add support for ipx */ +#include "serial.h" +#include "int.h" +#include "bitops.h" +#include "pic.h" +#include "emudpmi.h" +#include "priv.h" /* for priv_init */ +#include "port.h" /* for port_init */ +#include "pci.h" +#include "speaker.h" +#include "utilities.h" +#include "dos2linux.h" +#include "iodev.h" +#include "mapping.h" +#include "dosemu_config.h" +#include "libpacket.h" +#include "ne2000.h" +#include "dma.h" +#include "hlt.h" +#include "coopth.h" +#include "keyboard/keyb_server.h" +#include "sig.h" +#include "sound.h" +#include "ioselect.h" +#ifdef X86_EMULATOR +#include "cpu-emu.h" +#endif +#include "kvm.h" + +static int ld_tid; +static int can_leavedos; +static int leavedos_code; +static int leavedos_called; +static pthread_mutex_t ld_mtx = PTHREAD_MUTEX_INITIALIZER; +__TLS union vm86_union vm86u; + +volatile __thread int fault_cnt; +volatile int in_vm86; +int terminal_pipe; +int terminal_fd = -1; +int kernel_version_code; +int console_fd = -1; +int mem_fd = -1; +int fatalerr; +int in_leavedos; +pthread_t dosemu_pthread_self; +char * const *dosemu_envp; +FILE *real_stderr; + +#define MAX_EXIT_HANDLERS 5 +struct exit_hndl { + void (*handler)(void); +}; +static struct exit_hndl exit_hndl[MAX_EXIT_HANDLERS]; +static int exit_hndl_num; + +static void __leavedos_main(int code, int sig); +static void leavedos_thr(void *arg); + +static int find_boot_drive(void) +{ + int i; + for (i = 0; i < config.fdisks; i++) { + if (disktab[i].boot) + return i; + } + FOR_EACH_HDISK(i, + if (disk_is_bootable(&hdisktab[i])) + return HDISK_NUM(i); + ); + return -1; +} + +void boot(void) +{ + unsigned buffer; + struct disk *dp = NULL; + + if (config.try_freedos && config.hdiskboot == -1 && + config.hdisks > 0 && !disk_is_bootable(&hdisktab[0])) { + c_printf("Applying freedos boot work-around\n"); + config.swap_bootdrv = 1; + } + if (config.hdiskboot == -1) + config.hdiskboot = find_boot_drive(); + switch (config.hdiskboot) { + case -1: + error("Bootable drive not found, exiting\n"); + leavedos(16); + return; + case 0: + if (config.fdisks > 0) + dp = &disktab[0]; + else { + error("Drive A: not defined, can't boot!\n"); + leavedos(71); + } + break; + case 1: + { + int d = 1; + if (config.fdisks > 1) { + if (config.swap_bootdrv) { + struct disk tmp = disktab[1]; + disktab[1] = disktab[0]; + disktab[0] = tmp; + disktab[0].drive_num = disktab[1].drive_num; + disktab[1].drive_num = tmp.drive_num; + d = 0; + disk_reset(); + } + dp = &disktab[d]; + } else if (config.fdisks == 1) { + dp = &disktab[0]; + } else { + error("Drive B: not defined, can't boot!\n"); + leavedos(71); + } + break; + } + default: + { + int d = config.hdiskboot - 2; + struct disk *dd = hdisk_find(d | 0x80); + struct disk *cc = hdisk_find(0x80); + if (config.swap_bootdrv && d && dd) { + dd->drive_num = 0x80; + cc->drive_num = d | 0x80; + config.hdiskboot = 2; + d = 0; + disk_reset(); + } + if (dd) + dp = dd; + else { + error("Drive %c not defined, can't boot!\n", d + 'C'); + leavedos(71); + } + if (dp->type != DIR_TYPE && dp->drive_num != 0x80) { + error("Boot from drive %c is not possible.\n", d + 'C'); + error("@Fix the $_hdimage setting or enable $_swap_bootdrive.\n"); + leavedos(72); + } + break; + } + } + + disk_close(); + disk_open(dp); + + buffer = 0x7c00; + + if (dp->type == PARTITION) {/* we boot partition boot record, not MBR! */ + d_printf("Booting partition boot record from part=%s....\n", dp->dev_name); + if (dos_read(dp->fdesc, buffer, SECTOR_SIZE) != SECTOR_SIZE) { + error("reading partition boot sector using partition %s.\n", dp->dev_name); + leavedos(16); + } + } else if (dp->floppy) { + if (read_sectors(dp, buffer, 0, 1) != SECTOR_SIZE) { + error("can't boot from %s, using harddisk\n", dp->dev_name); + dp = hdisktab; + goto mbr; + } + } else { + if (dp->type == DIR_TYPE) { + if (!disk_is_bootable(dp) || !disk_validate_boot_part(dp)) { + error("Drive unbootable, exiting\n"); + leavedos(16); + } + } +mbr: + if (read_mbr(dp, buffer) != SECTOR_SIZE) { + error("can't boot from hard disk\n"); + leavedos(16); + } + } + disk_close(); + + /* put boot drive to dl */ + _DX = dp->drive_num; +} + +static int c_chk(void) +{ + /* return 1 if the context is safe for coopth to do a thread switch */ + return !in_dpmi_pm(); +} + +/* + * DANG_BEGIN_FUNCTION emulate + * + * arguments: + * argc - Argument count. + * argv - Arguments. + * + * description: + * Emulate gets called from dos.c. It initializes DOSEMU to + * prepare it for running in vm86 mode. This involves catching signals, + * preparing memory, calling all the initialization functions for the I/O + * subsystems (video/serial/etc...), getting the boot sector instructions + * and calling vm86(). + * + * DANG_END_FUNCTION + * + */ +int main(int argc, char **argv, char * const *envp) +{ + dosemu_envp = envp; + setlocale(LC_ALL,""); + srand(time(NULL)); + memset(&config, 0, sizeof(config)); + + /* NOW! it is safe to touch the priv code. */ + priv_init(); /* This must come first! */ + + /* Before we even try to give options to the parser, + * we pre-filter some dangerous options and delete them + * from the arguments list + */ + secure_option_preparse(&argc, argv); + + /* the transposal of (config_|stdio_)init allows the addition of -o */ + /* to specify a debug out filename, if you're wondering */ + + port_init(); /* setup port structures, before config! */ + version_init(); /* Check the OS version */ + config_init(argc, argv); /* parse the commands & config file(s) */ +#ifdef X86_EMULATOR +#ifdef DONT_DEBUG_BOOT /* cpuemu only */ + memcpy(&debug_save, &debug, sizeof(debug)); + set_debug_level('e', 0); +#ifdef TRACE_DPMI + set_debug_level('t', 0); +#endif +#endif +#endif + + get_time_init(); + print_version(); /* log version information */ + memcheck_init(); + /* threads can be created only after signal_pre_init() so + * it should be above device_init(), iodev_init(), cpu_setup() etc */ + signal_pre_init(); /* initialize sig's & sig handlers */ + cpu_setup(); /* setup the CPU */ + pci_setup(); + device_init(); /* priv initialization of video etc. */ + extra_port_init(); /* setup ports dependent on config */ + SIG_init(); /* Silly Interrupt Generator */ + LibpacketInit(); /* initialize network packet interfaces */ + + mapping_init(); /* initialize mapping drivers */ + + if (can_do_root_stuff && !under_root_login) { + g_printf("dropping root privileges\n"); + open_kmem(); + } + priv_drop(); + + map_memory_space(); /* maps all DOS memory (low, dpmi, xms...) */ + init_hardware_ram(); /* map the direct hardware ram */ + map_video_bios(); /* map (really: copy) the video bios */ + close_kmem(); + + /* the following duo have to be done before others who use hlt or coopth */ + vm86_hlt_state = hlt_init(BIOS_HLT_BLK_SIZE); + coopth_init(); + coopth_set_ctx_checker_vm86(c_chk); + ld_tid = coopth_create("leavedos", leavedos_thr); + coopth_set_ctx_handlers(ld_tid, sig_ctx_prepare, sig_ctx_restore, NULL); + + vm86_init(); + cputime_late_init(); + HMA_init(); /* HMA can only be done now after mapping + is initialized*/ + memory_init(); /* initialize the memory contents */ + ioselect_init(); + /* iodev_init() can load plugins, like SDL, that can spawn a thread. + * This must be done before initializing signals, or problems ensue. + * This also must be done when the signals are blocked, so after + * the signal_pre_init(), which right now blocks the signals. */ + iodev_init(); /* initialize devices */ + init_all_DOS_tables(); /* longest init function! needs to be optimized */ + signal_init(); /* initialize sig's & sig handlers */ + if (config.exitearly) { + dbug_printf("Leaving DOS before booting\n"); + leavedos(0); + } + g_printf("EMULATE\n"); + + fflush(stdout); + +#ifdef USE_MHPDBG + mhp_debug(DBG_INIT, 0, 0); +#endif + timer_interrupt_init(); /* start sending int 8h int signals */ + + /* map KVM memory */ + if (config.cpu_vm == CPUVM_KVM || config.cpu_vm_dpmi == CPUVM_KVM) + set_kvm_memory_regions(); + + cpu_reset(); + if (config.cpu_vm == CPUVM_KVM) + kvm_enter(0); + can_leavedos = 1; + + while (!fatalerr && !config.exitearly) { + loopstep_run_vm86(); + } + + if (fatalerr) { + sync(); + fprintf(stderr, "Not a good day to die!!!!!\n"); + } + leavedos(99); + return 0; /* just to make gcc happy */ +} + +void +dos_ctrl_alt_del(void) +{ + SETIVEC(0x19, BIOSSEG, INT_OFF(0x19)); + dbug_printf("DOS ctrl-alt-del requested. Rebooting!\n"); + if(in_dpmi_pm()) + fake_pm_int(); + real_run_int(0x19); +} + +int register_exit_handler(void (*handler)(void)) +{ + assert(exit_hndl_num < MAX_EXIT_HANDLERS); + exit_hndl[exit_hndl_num].handler = handler; + exit_hndl_num++; + return 0; +} + +static void leavedos_thr(void *arg) +{ + dbug_printf("leavedos thread started\n"); + /* this may require working vm86() */ + video_early_close(); + dbug_printf("leavedos thread ended\n"); +} + +/* "graceful" shutdown */ +void __leavedos(int code, int sig, const char *s, int num) +{ + int tmp; + dbug_printf("leavedos(%s:%i|%i) called - shutting down\n", s, num, sig); + if (in_leavedos) + { + error("leavedos called recursively, forgetting the graceful exit!\n"); + _exit(1); + } + + if (!can_leavedos) { + config.exitearly = 1; + return; + } + + in_leavedos++; + if (fault_cnt > 0) { + dosemu_error("leavedos() called from within a signal context!\n"); + leavedos_main(sig); + return; + } + +#ifdef USE_MHPDBG + /* try to notify dosdebug */ + mhp_exit_intercept(sig); +#endif + + /* try to regain control of keyboard and video first */ + keyb_close(); + /* abandon current thread if any */ + coopth_abandon(); + /* switch to RM before closing coopthreads-related stuff */ + dpmi_done0(); + if (!config.exitearly) { // in exitearly case nothing to join + /* try to clean up threads */ + tmp = coopth_flush_vm86(); + if (tmp) + dbug_printf("%i threads still active\n", tmp); + coopth_start(ld_tid, NULL); + /* vc switch may require vm86() so call it while waiting for thread */ + coopth_join_vm86(ld_tid); + } + __leavedos_main(code, sig); +} + +static void __leavedos_main(int code, int sig) +{ + int i; + + /* async signals must be disabled first or pthread_cancel() hangs on arm */ + signal_done(); + dpmi_done(); + /* now safe to stop io thread */ + ioselect_done(); + /* then stop device threads, which also stops any remaining vm86() uses */ + iodev_term(); +#ifdef USE_MHPDBG + g_printf("closing debugger pipes\n"); + /* after vm86() is no longer used, we can do this */ + mhp_close(); +#endif + /* now it is safe to shut down coopth. Can be done any later, if need be */ + coopth_done(); + dbug_printf("coopthreads stopped\n"); + + video_close(); + if (config.cpu_vm == CPUVM_KVM || config.cpu_vm_dpmi == CPUVM_KVM) + kvm_done(); + if (config.speaker == SPKR_EMULATED) { + g_printf("SPEAKER: sound off\n"); + speaker_off(); /* turn off any sound */ + } + else if (config.speaker==SPKR_NATIVE) { + g_printf("SPEAKER: sound off\n"); + /* Since the speaker is native hardware use port manipulation, + * we don't know what is actually implementing the kernel's + * ioctls. + * My port logic is actually stolen from kd_nosound in the kernel. + * --EB 21 September 1997 + */ + port_outb(0x61, port_inb(0x61)&0xFC); /* turn off any sound */ + } + + free(vm86_hlt_state); + + SIG_close(); + +#if defined(X86_EMULATOR) + /* if we are here with config.cpuemu>1 something went wrong... */ + if (IS_EMU()) { + leave_cpu_emu(); + } +#endif + show_ints(0, 0x33); + g_printf("calling disk_close_all\n"); + disk_close_all(); + + if (config.emuretrace) { + do_r3da_pending (); + set_ioperm (0x3da, 1, 1); + set_ioperm (0x3c0, 1, 1); + config.emuretrace = 0; + } + + /* terminate port server */ + port_exit(); + + g_printf("releasing ports and blocked devices\n"); + release_ports(); + + g_printf("calling shared memory exit\n"); + g_printf("calling HMA exit\n"); + hma_exit(); + g_printf("calling mapping_close()\n"); + mapping_close(); + + g_printf("calling close_all_printers\n"); + close_all_printers(); + + for (i = 0; i < exit_hndl_num; i++) + exit_hndl[i].handler(); + + flush_log(); + + if (sig < 0) + code = -sig; + else if (sig > 0) + code = sig + 128; + else + code &= 0x7f; + /* We don't need to use _exit() here; this is the graceful exit path. */ + exit(code); +} + +void __leavedos_main_wrp(int code, int sig, const char *s, int num) +{ + dbug_printf("leavedos_main(%s:%i|%i) called - shutting down\n", s, num, sig); + __leavedos_main(code, sig); +} + +void leavedos_from_thread(int code) +{ + pthread_mutex_lock(&ld_mtx); + leavedos_code = code; + leavedos_called++; + pthread_mutex_unlock(&ld_mtx); +} + +void check_leavedos(void) +{ + int ld_code, ld_called; + pthread_mutex_lock(&ld_mtx); + ld_code = leavedos_code; + ld_called = leavedos_called; + leavedos_called = 0; + pthread_mutex_unlock(&ld_mtx); + if (ld_called) + leavedos(ld_code); +} + +void hardware_run(void) +{ + run_sb(); /* Beat Karcher to this one .. 8-) - AM */ + keyb_server_run(); + rtc_run(); +} + diff --git a/src/base/core/hlt.c b/src/base/core/hlt.c new file mode 100644 index 0000000..ef842cc --- /dev/null +++ b/src/base/core/hlt.c @@ -0,0 +1,180 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include "emu.h" +#include "emudpmi.h" +#include "int.h" +#include "utilities.h" +#include "hlt.h" + +#define CONFIG_HLT_TRACE 1 + +/* + * maximum number of halt handlers. + * you can increase this to anything below 256 since an 8-bit handle + * is used for each device + */ +#define MAX_HLT_HANDLERS 50 + +struct hlt_handler { + emu_hlt_t h; + Bit16u start_addr; +}; + +#define MAX_HLT_BLK_SIZE 4096 +struct hlt_struct { + struct hlt_handler hlt_handler[MAX_HLT_HANDLERS]; + int hlt_handler_id[MAX_HLT_BLK_SIZE]; + int hlt_handler_count; + int hlt_block_size; +}; + +/* + * This is the default HLT handler for the HLT block -- assume that + * someone did a CALLF to get to us. + */ +static void hlt_default(Bit16u addr, HLT_ARG(arg)) +{ + if (in_dpmi_pm()) { + dosemu_error("HLT: DPMI hlt_default(0x%04x) called, exiting\n", addr); + leavedos(2); + } else { + /* 32rtm doesn't shut down mouse driver properly, so it may + * execute the no longer valid realmode callback. + * See https://github.com/dosemu2/dosemu2/issues/1563 */ + error_once("HLT: vm86 hlt_default(0x%04x) called, trying retf\n", addr); + warn("HLT: vm86 hlt_default(0x%04x) called, trying retf\n", addr); + fake_retf(); + } +} + +/* + * DANG_BEGIN_FUNCTION hlt_init(void) + * + * description: + * Resets all the HLT handlers + * + * DANG_END_FUNCTION + */ +void *hlt_init(int size) +{ + struct hlt_struct *state; + int i; + + state = malloc(sizeof(*state)); + state->hlt_handler[0].h.func = hlt_default; + state->hlt_handler[0].h.name = "Unmapped HLT instruction"; + + state->hlt_handler_count = 1; + assert(size <= MAX_HLT_BLK_SIZE); + for (i = 0; i < size; i++) + state->hlt_handler_id[i] = 0; /* unmapped HLT handler */ + state->hlt_block_size = size; + return state; +} + +/* + * DANG_BEGIN_FUNCTION hlt_handle() + * + * description: + * Handles a HLT instruction reached inside the dos emulator. + * + * DANG_END_FUNCTION + */ +int hlt_handle(void *arg, Bit16u offs, void *arg2) +{ + struct hlt_struct *state = arg; + struct hlt_handler *hlt = &state->hlt_handler[state->hlt_handler_id[offs]]; +#if CONFIG_HLT_TRACE > 0 + h_printf("HLT: fcn 0x%04x called in HLT block, handler: %s +%#x\n", offs, + hlt->h.name, offs - hlt->start_addr); +#endif + hlt->h.func(offs - hlt->start_addr, arg2, hlt->h.arg); + return hlt->h.ret; +} + +/* + * Register a HLT handler. + */ +Bit16u hlt_register_handler(void *arg, emu_hlt_t handler) +{ + struct hlt_struct *state = arg; + int handle, i, j; + Bit16u start_addr = -1; + + /* initialization check */ + assert(state->hlt_handler_count); + + if (state->hlt_handler_count >= MAX_HLT_HANDLERS) { + error("HLT: too many HLT handlers, increase MAX_HLT_HANDLERS\n"); + config.exitearly = 1; + return -1; + } + + for (i = 0; i + handler.len <= state->hlt_block_size; i++) { + for (j = 0; j < handler.len; j++) { + if (state->hlt_handler_id[i + j]) { + i += j; + break; + } + } + /* see if found free block */ + if (j == handler.len) { + start_addr = i; + break; + } + } + if (start_addr == (Bit16u)-1) { + error("HLT: Cannot find free block of len %i\n", handler.len); + config.exitearly = 1; + return -1; + } + + handle = state->hlt_handler_count++; + + state->hlt_handler[handle].h = handler; + state->hlt_handler[handle].start_addr = start_addr; + + /* change table to reflect new handler id for that address */ + for (j = 0; j < handler.len; j++) + state->hlt_handler_id[start_addr + j] = handle; + + h_printf("HLT: registered %s at %#x,%i\n", + handler.name, start_addr, handler.len); + + return start_addr; +} + +int hlt_unregister_handler(void *arg, Bit16u start_addr) +{ + struct hlt_struct *state = arg; + int handle, i; + emu_hlt_t *h; + + assert(start_addr < state->hlt_block_size); + handle = state->hlt_handler_id[start_addr]; + if (!handle) + return -1; + h = &state->hlt_handler[handle].h; + for (i = 0; i < h->len; i++) + state->hlt_handler_id[start_addr + i] = 0; + h->func = hlt_default; + while (state->hlt_handler_count && + state->hlt_handler[state->hlt_handler_count - 1].h.func == hlt_default) + state->hlt_handler_count--; + return 0; +} diff --git a/src/base/core/int.c b/src/base/core/int.c new file mode 100644 index 0000000..212df1f --- /dev/null +++ b/src/base/core/int.c @@ -0,0 +1,3916 @@ +#include +#include +#ifdef HAVE_LIBBSD +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#else +#include +#include +#endif +#include +#include "Linux/magic.h" +#include +#include +#include +#include + +#include "version.h" + +#include "emu.h" +#include "serial.h" +#include "memory.h" +#include "timers.h" +#include "mouse.h" +#include "disks.h" +#include "bios.h" +#include "iodev.h" +#include "pic.h" +#include "lpt.h" +#include "bitops.h" +#include "hma.h" +#include "xms.h" +#include "int.h" +#include "vint.h" +#include "dos2linux.h" +#include "video.h" +#include "clipboard.h" +#include "priv.h" +#include "doshelpers.h" +#include "lowmem.h" +#include "plugin_config.h" +#include "utilities.h" +#include "redirect.h" +#include "pci.h" +#include "joystick.h" +#include "aspi.h" +#include "vgaemu.h" +#include "hlt.h" +#include "coopth.h" +#include "mhpdbg.h" +#include "ipx.h" +#ifdef X86_EMULATOR +#include "cpu-emu.h" +#endif + +#include "emudpmi.h" + +#include "keyboard/keyb_server.h" + +#undef DEBUG_INT1A + +#if WINDOWS_HACKS +enum win3x_mode_enum win3x_mode; +#endif + +static char win3x_title[256]; + +static void dos_post_boot(void); +static int post_boot; +static int int21_hooked; +static int int28_hooked; +static int int2f_hooked; +static int int33_hooked; + +static int int33(void); +static int _int66_(int, int); +static void do_rvc_chain(int i, int stk_offs); +static void int21_rvc_setup(void); +static void int2f_rvc_setup(void); +static void int33_rvc_setup(void); +static void revect_setup(void); +#define run_caller_func(i, revect, arg) \ + int_handlers[i].interrupt_function[revect](arg, revect) +#define run_secrevect_func(i, arg1, arg2) \ + int_handlers[i].secrevect_function(arg1, arg2) +static void redirect_devices(void); +static int enable_redirect(void); +static int do_redirect(int old_only); +static int redir_it(void); +static void debug_int(const char *s, int i); + +static int msdos_remap_extended_open(void); +static int rehash_redir_groups(void); + +typedef int interrupt_function_t(int, int); +enum { NO_REVECT, REVECT, INTF_MAX }; +typedef void revect_function_t(void); +typedef far_t unrevect_function_t(uint16_t, uint16_t); +typedef void secrevect_function_t(uint16_t, uint16_t); +static struct { + interrupt_function_t *interrupt_function_arr[INTF_MAX][INTF_MAX]; + #define interrupt_function interrupt_function_arr[cur_rvc_setup()] + secrevect_function_t *secrevect_function; + revect_function_t *revect_function; + unrevect_function_t *unrevect_function; +} int_handlers[0x100]; + +/* set if some directories are mounted during startup */ +static int redir_state; +static int redir_state2; + +static char title_hint[9] = ""; +static char title_current[TITLE_APPNAME_MAXLEN]; +static int can_change_title = 0; +static u_short hlt_off; +static int int_tid, int_rvc_tid; + +static int cur_rvc_setup(void) +{ + if (config.int_hooks == -1 || config.int_hooks == 1) + return REVECT; + return NO_REVECT; +} + +u_short INT_OFF(u_char i) +{ + return ((BIOS_HLT_BLK_SEG << 4) + i + hlt_off); +} + +void jmp_to(int cs, int ip) +{ + SREG(cs) = cs; + REG(eip) = ip; +} + +static void change_window_title(char *title) +{ + if (Video->change_config) + Video->change_config(CHG_TITLE_APPNAME, title); +} + +static void kill_time(long usecs) +{ + hitimer_t t_start; + + t_start = GETusTIME(0); + while (GETusTIME(0) - t_start < usecs) { + set_IF(); + coopth_wait(); + clear_IF(); + } +} + +static void mbr_jmp(void *arg) +{ + unsigned offs = (long) arg; + + LWORD(esp) = 0x7c00; + SREG(cs) = SREG(ds) = SREG(es) = SREG(ss) = 0; + LWORD(edi) = 0x7dfe; + LWORD(eip) = 0x7c00; + LWORD(ebp) = LWORD(esi) = offs; +} + +static void process_master_boot_record(void) +{ + /* Ok, _we_ do the MBR code in 32-bit C code, + * so this obviously is _not_ stolen from any DOS code ;-) + * + * Now, what _does_ the original MSDOS MBR? + * 1. It moves itself down to 0:0x600 + * 2. It sets DS,ES,SS to the new segment (0 in this case) + * 3. It sets the stack pinter to just below the loaded MBR (SP=0x7c00) + * 4. It searches for a partition having the bootflag set (=0x80) + * 5. It loads the bootsector of this partition to 0:0x7c00 + * 6. It does a long jump to 0:0x7c00, with following registers set: + * SS:BP,DS:SI pointing to the boot partition entry within 0:600 MBR + * DI = 0x7dfe + */ + struct on_disk_mbr *mbr = LOWMEM(0x600); + struct on_disk_mbr *bootrec = LOWMEM(0x7c00); + int i; + unsigned offs; + + memcpy(mbr, bootrec, 0x200); /* move the mbr down */ + + for (i = 0; i < 4; i++) { + if (mbr->partition[i].bootflag == 0x80) + break; + } + if (i >= 4) { + /* aiee... no bootflags sets */ + error("no bootflag set, Leaving DOS...\n"); + leavedos(99); + } + LO(dx) = 0x80; /* drive C:, DOS boots only from C: */ + if (config.hdiskboot >= 2) + LO(dx) += config.hdiskboot - 2; + HI(dx) = mbr->partition[i].start_head; + LO(cx) = mbr->partition[i].start_sector; + HI(cx) = PTBL_HL_GET(&mbr->partition[i], start_track); + LWORD(eax) = 0x0201; /* read one sector */ + LWORD(ebx) = 0x7c00; /* target offset, ES is 0 */ + do_int_call_back(0x13); + if ((REG(eflags) & CF) || (bootrec->signature != MBR_SIG)) { + /* error while booting */ + error("error on reading bootsector, Leaving DOS...\n"); + leavedos(99); + } + + offs = 0x600 + offsetof(struct on_disk_mbr, partition) + sizeof(mbr->partition[0]) * i; + coopth_add_post_handler(mbr_jmp, (void *) (long) offs); +} + +static void revect_helper(int stk_offs) +{ + int ah = HI(ax); + int subh = LO(bx); + int stk = HI(bx) + stk_offs; + int inum = ah; + uint16_t old_ax; + uint16_t old_flags; + + LWORD(eax) = HWORD(eax); + LWORD(ebx) = HWORD(ebx); + HWORD(eax) = HWORD(ebx) = 0; + NOCARRY; + set_ZF(); + switch (subh) { + case DOS_SUBHELPER_RVC_VERSION_CHECK: + if (ah < DOSEMU_EMUFS_DRIVER_MIN_VERSION) { + CARRY; + error("emufs is too old, ver %i need %i\n", ah, + DOSEMU_EMUFS_DRIVER_VERSION); + break; + } + if (ah < DOSEMU_EMUFS_DRIVER_VERSION) { + error("emufs is too old, ver %i need %i, but trying to continue\n", + ah, DOSEMU_EMUFS_DRIVER_VERSION); + error("@To upgrade you can remove ~/.dosemu/drives and it will be re-created.\n"); + } + break; + case DOS_SUBHELPER_RVC_CALL: + do_rvc_chain(inum, stk); + break; + case DOS_SUBHELPER_RVC2_CALL: + old_ax = LWORD(ecx); + LWORD(ecx) = HWORD(ecx); + HWORD(ecx) = 0; + old_flags = LWORD(edx); + LWORD(edx) = HWORD(edx); + HWORD(edx) = 0; + di_printf("int_rvc 0x%02x, doing second revect call\n", inum); + run_secrevect_func(inum, old_ax, old_flags); + break; + case DOS_SUBHELPER_RVC_NEXT_VEC: { + int start = (ah == 0xff ? 0 : ah + 1); + int i; + for (i = start; i < 256; i++) { + if (int_handlers[i].interrupt_function[REVECT]) + break; + } + if (i == 256) { + set_ZF(); + break; + } + assert(int_handlers[i].unrevect_function); + clear_ZF(); + HI(ax) = i; + break; + } + case DOS_SUBHELPER_RVC_UNREVECT: { + far_t entry; + if (!int_handlers[inum].unrevect_function) { + CARRY; + break; + } + entry = int_handlers[inum].unrevect_function(SREG(es), LWORD(edi)); + if (!entry.segment) { + CARRY; + break; + } + SREG(ds) = entry.segment; + LWORD(esi) = entry.offset; + break; + } + default: + CARRY; + break; + } +} + +static void (*clnup_handler)(void); + +int register_cleanup_handler(void (*call)(void)) +{ +// assert(!clnup_handler); + clnup_handler = call; + return 0; +} + +static void emufs_helper(void) +{ + char *p, *p1, *cmdl; + + switch (LO(bx)) { + case DOS_SUBHELPER_EMUFS_REDIRECT: + NOCARRY; + if (!redir_it()) { + CARRY; + break; + } + p = FAR2PTR(READ_DWORD(SEGOFF2LINEAR(_ES, _DI) + 18)); + p1 = strpbrk(p, "\r\n"); + if (p1) + cmdl = strndup(p, p1 - p); + else + cmdl = strdup(p); + p = cmdl + strlen(cmdl) - 1; + while (*p == ' ') { + *p = 0; + p--; + } + p = strrchr(cmdl, ' '); + if (p) { + p++; + if (strcasecmp(p, "/ALL") == 0) + redirect_devices(); + } + free(cmdl); + break; + case DOS_SUBHELPER_EMUFS_IOCTL: + switch (HI(ax)) { + case EMUFS_HELPER_REDIRECT: + NOCARRY; + if (!enable_redirect()) { + CARRY; + break; + } + redirect_devices(); + break; + case EMUFS_HELPER_REHASH_DYN: + NOCARRY; + rehash_redir_groups(); + break; + default: + CARRY; + break; + } + break; + default: + error("Unsupported emufs helper %i\n", LO(bx)); + CARRY; + break; + } +} + +/* returns 1 if dos_helper() handles it, 0 otherwise */ +/* dos helper and mfs startup (was 0xfe) */ +static int dos_helper(int stk_offs, int revect) +{ + switch (LO(ax)) { + case DOS_HELPER_DOSEMU_CHECK: /* Linux dosemu installation test */ + LWORD(eax) = DOS_HELPER_MAGIC; + LWORD(ebx) = VERSION_NUM * 0x100 + SUBLEVEL; /* major version 0.49 -> 0049 */ + /* The patch level in the form n.n is a float... + * ...we let GCC at compile time translate it to 0xHHLL, HH=major, LL=minor. + * This way we avoid usage of float instructions. + */ + LWORD(ecx) = REVISION; + LWORD(edx) = (config.X) ? 0x1 : 0; /* Return if running under X */ + g_printf("WARNING: dosemu installation check\n"); + if (debug_level('g')) + show_regs(); + break; + + case DOS_HELPER_SHOW_REGS: /* SHOW_REGS */ + show_regs(); + break; + + case DOS_HELPER_SHOW_INTS: /* SHOW INTS, BH-BL */ + show_ints(HI(bx), LO(bx)); + break; + + case DOS_HELPER_PRINT_STRING: /* PRINT STRING ES:DX */ + dbug_printf("DOS to EMU: \"%s\"\n", SEG_ADR((char *), es, dx)); + break; + + case DOS_HELPER_ADJUST_IOPERMS: /* SET IOPERMS: bx=start, cx=range, + carry set for get, clear for release */ + { + int cflag = LWORD(eflags) & CF ? 1 : 0; + + i_printf("I/O perms: 0x%04x 0x%04x %d\n", LWORD(ebx), + LWORD(ecx), cflag); + if (set_ioperm(LWORD(ebx), LWORD(ecx), cflag)) { + error("SET_IOPERMS request failed!!\n"); + CARRY; /* failure */ + } else { + if (cflag) + warn("WARNING! DOS can now access I/O ports 0x%04x to 0x%04x\n", LWORD(ebx), LWORD(ebx) + LWORD(ecx) - 1); + else + warn("Access to ports 0x%04x to 0x%04x clear\n", + LWORD(ebx), LWORD(ebx) + LWORD(ecx) - 1); + NOCARRY; /* success */ + } + } + break; + + case DOS_HELPER_REVECT_HELPER: + revect_helper(stk_offs); + break; + + case DOS_HELPER_CONTROL_VIDEO: /* initialize video card */ + if (LO(bx) == 0) { + if (set_ioperm(0x3b0, 0x3db - 0x3b0, 0)) + warn("couldn't shut off ioperms\n"); + SETIVEC(0x10, BIOSSEG, INT_OFF(0x10)); /* restore our old vector */ + config.vga = 0; + } else { + unsigned int ssp, sp; + + if (!config.mapped_bios) { + error("CAN'T DO VIDEO INIT, BIOS NOT MAPPED!\n"); + return 1; + } + if (set_ioperm(0x3b0, 0x3db - 0x3b0, 1)) + warn("couldn't get range!\n"); + config.vga = 1; + warn("WARNING: jumping to 0[c/e]000:0003\n"); + + ssp = SEGOFF2LINEAR(SREG(ss), 0); + sp = LWORD(esp); + pushw(ssp, sp, SREG(cs)); + pushw(ssp, sp, LWORD(eip)); + LWORD(esp) -= 4; + SREG(cs) = config.vbios_seg; + LWORD(eip) = 3; + show_regs(); + } + break; + + case DOS_HELPER_SHOW_BANNER: /* show banner */ + if (config.fdisks + config.hdisks == 0) { + error("No drives defined, exiting\n"); + leavedos(2); + } + if (config.quiet) + break; + p_dos_str(PACKAGE_NAME " " VERSTR " Configured: " CONFIG_TIME "\n"); +// p_dos_str +// ("Please test against a recent version before reporting bugs and problems.\n"); + p_dos_str + ("Get the latest code at http://dosemu2.github.io/dosemu2\n"); + p_dos_str + ("Submit Bugs via https://github.com/dosemu2/dosemu2/issues\n"); + p_dos_str + ("Ask for help in mail list: linux-msdos@vger.kernel.org\n"); + p_dos_str + ("This program comes with ABSOLUTELY NO WARRANTY.\n" + "This is free software, GPL v2 (or any later version) " + "distribution conditions.\n"); + p_dos_str("\n"); + break; + + case DOS_HELPER_PRESTROKES_START: + start_pre_strokes(); + break; + + case DOS_HELPER_INSERT_INTO_KEYBUFFER: + k_printf + ("KBD: WARNING: outdated keyboard helper fn 6 was called!\n"); + break; + + case DOS_HELPER_GET_BIOS_KEY: /* INT 09 "get bios key" helper */ + _AX = get_bios_key(_AH); + k_printf("HELPER: get_bios_key() returned %04x\n", _AX); + break; + + case DOS_HELPER_VIDEO_INIT: + v_printf("Starting Video initialization\n"); + _AL = config.vbios_post; + break; + + case DOS_HELPER_GET_DEBUG_STRING: + /* TRB - handle dynamic debug flags in dos_helper() */ + LWORD(eax) = + GetDebugFlagsHelper(MK_FP32(_regs.es, _regs.edi & 0xffff), 1); + g_printf("DBG: Get flags\n"); + break; + + case DOS_HELPER_SET_DEBUG_STRING: + g_printf("DBG: Set flags\n"); + LWORD(eax) = + SetDebugFlagsHelper(MK_FP32(_regs.es, _regs.edi & 0xffff)); + g_printf("DBG: Flags set\n"); + break; + + case DOS_HELPER_SET_HOGTHRESHOLD: + g_printf("IDLE: Setting hogthreshold value to %u\n", LWORD(ebx)); + config.hogthreshold = LWORD(ebx); + break; + + case DOS_HELPER_TEST_MODE: + config.test_mode = LO(bx); + return 1; + + case DOS_HELPER_MFS_HELPER: + mfs_inte6(); + return 1; + + case DOS_HELPER_EMUFS_HELPER: + emufs_helper(); + return 1; + +#if 0 + case DOS_HELPER_DOSC: + if (HI(ax) == 0xdc) { + /* install check and notify */ + if (!dosc_interface()) + return 0; + running_DosC = LWORD(ebx); + return 1; + } + if (running_DosC) { + return dosc_interface(); + } + return 0; +#endif + + case DOS_HELPER_EMS_HELPER: + ems_helper(); + return 1; + + case DOS_HELPER_EMS_BIOS: + { + LWORD(eax) = HWORD(eax); + E_printf + ("EMS: in 0xe6,0x22 handler! ax=0x%04x, bx=0x%04x, dx=0x%04x, " + "cx=0x%04x\n", LWORD(eax), LWORD(ebx), LWORD(edx), + LWORD(ecx)); + if (config.ems_size) + ems_fn(®S); + else { + error("EMS: not running ems_fn!\n"); + return 0; + } + break; + } + + case DOS_HELPER_XMS_HELPER: + xms_helper(); + return 1; + + case DOS_HELPER_GARROT_HELPER: /* Mouse garrot helper */ + if (!LWORD(ebx)) /* Wait sub-function requested */ + idle_enable(50, 0, "mouse_garrot"); + else { /* Get Hogthreshold value sub-function */ + LWORD(ebx) = config.hogthreshold; + LWORD(eax) = config.hogthreshold; + } + break; + + case DOS_HELPER_SERIAL_HELPER: /* Serial helper */ + serial_helper(); + break; + + case DOS_HELPER_MOUSE_HELPER:{ + uint8_t *p = MK_FP32(BIOSSEG, bios_in_int10_callback); + + switch (LWORD(ebx)) { + case DOS_SUBHELPER_MOUSE_START_VIDEO_MODE_SET: + /* Note: we hook int10 very late, after display.sys already hooked it. + * So when we call previous handler in bios.S, we actually call + * display.sys's one, which will call us again. + * So have to protect ourselves from re-entrancy. */ + *p = 1; + break; + case DOS_SUBHELPER_MOUSE_END_VIDEO_MODE_SET: + *p = 0; + break; + } +#if WINDOWS_HACKS + if (win3x_mode != INACTIVE) { + /* work around win.com's small stack that gets overflown when + * display.sys's int10 handler calls too many things with hw interrupts + * enabled. */ + uint16_t new_ss, new_sp; + int switched, to_copy; + uint64_t cookie; + uint8_t *stk, *new_stk; + switch (LWORD(ebx)) { + case DOS_SUBHELPER_MOUSE_START_VIDEO_MODE_SET: + to_copy = _min(64, (0x10000 - _SP) & 0xffff); + switched = get_rm_stack(&new_ss, &new_sp, + ((uint64_t)to_copy << 32) | ((unsigned)_SS << 16) | + _SP); + if (switched) { + stk = SEG_ADR((uint8_t *), ss, sp); + SREG(ss) = new_ss; + LWORD(esp) = new_sp - to_copy; + new_stk = SEG_ADR((uint8_t *), ss, sp); + memcpy(new_stk, stk, to_copy); + } + break; + case DOS_SUBHELPER_MOUSE_END_VIDEO_MODE_SET: + new_sp = put_rm_stack(&cookie); + if (new_sp) { + uint16_t old_ss = (cookie >> 16) & 0xffff; + uint16_t old_sp = cookie & 0xffff; + int sp_delta; + to_copy = cookie >> 32; + sp_delta = LWORD(esp) + to_copy - new_sp; + stk = SEG_ADR((uint8_t *), ss, sp); + new_stk = + LINEAR2UNIX(SEGOFF2LINEAR(old_ss, old_sp) + + sp_delta); + memcpy(new_stk, stk, to_copy - sp_delta); + SREG(ss) = old_ss; + LWORD(esp) = old_sp + sp_delta; + } else { + error("SS changed by video mode set\n"); + } + break; + } + } +#endif + mouse_helper(&vm86s.regs); + break; + } + + case DOS_HELPER_CDROM_HELPER:{ + E_printf + ("CDROM: in 0x40 handler! ax=0x%04x, bx=0x%04x, dx=0x%04x, " + "cx=0x%04x\n", LWORD(eax), LWORD(ebx), LWORD(edx), + LWORD(ecx)); + cdrom_helper(NULL, NULL, 0); + break; + } + + case DOS_HELPER_ASPI_HELPER:{ + A_printf + ("ASPI: in 0x41 handler! ax=0x%04x, bx=0x%04x, dx=0x%04x, " + "cx=0x%04x\n", LWORD(eax), LWORD(ebx), LWORD(edx), + LWORD(ecx)); + aspi_helper(HI(ax)); + break; + } + + case DOS_HELPER_GET_UNIX_ENV: { + char *env = SEG_ADR((char *), es, dx); + char *val = getenv(env); + /* Interrogate the UNIX environment in es:dx (a null terminated buffer) */ + g_printf("Interrogating UNIX Environment\n"); + if (val) { + strcpy(env, val); + LWORD(eax) = 0; + } else { + LWORD(eax) = 1; + } + break; + } + + case DOS_HELPER_GET_CPU_SPEED: + { + REG(eax) = 0; + break; + } + + case DOS_HELPER_GET_TERM_TYPE: + { + u_short i; + int co, li; + + i = config.console_keyb; + if (config.X || config.sdl) + i |= 8; + if (config.console_video) + i |= 0x10; + if (config.vga) + i |= 0x20; + if (config.dualmon) + i |= 0x40; + if (config.term) + i |= 0x80; + if (config.dumb_video) + i |= 0x100; + LWORD(eax) = i; + if (config.term) { + gettermcap(0, &co, &li); + } else { + co = vga.text_width; + li = vga.text_height; + } + LWORD(ebx) = co; + LWORD(ecx) = li; + break; + } + + case DOS_HELPER_GETCWD: + { + char *buf = + getcwd(SEG_ADR((char *), es, dx), (size_t) LWORD(ecx)); + LWORD(eax) = + buf == NULL ? 0 : (SEGOFF2LINEAR(_ES, _DX) & 0xffff); + break; + } + case DOS_HELPER_GETPID: { + pid_t pid = getpid(); + LWORD(eax) = pid; + LWORD(ebx) = pid >> 16; + break; + } + + case DOS_HELPER_CHDIR: + LWORD(eax) = chdir(SEG_ADR((char *), es, dx)); + break; +#ifdef X86_EMULATOR + case DOS_HELPER_CPUEMUON: + config.cpu_vm = CPUVM_EMU; + config.cpu_vm_dpmi = CPUVM_EMU; + break; + case DOS_HELPER_CPUEMUOFF: + /* FIXME: dunno to what cpu_vm to switch */ + if (IS_EMU()) { + error("unsupported emuoff helper\n"); + } + break; +#endif + case DOS_HELPER_XCONFIG: + if (Video->change_config) { + LWORD(eax) = + Video->change_config((unsigned) LWORD(edx), + SEG_ADR((void *), es, bx)); + } else { + _AX = -1; + } + break; + case DOS_HELPER_BOOTSECT: + coopth_leave(); + mimic_boot_blk(); + break; + case DOS_HELPER_READ_MBR: + boot(); + break; + case DOS_HELPER_MBR: + process_master_boot_record(); + break; + case DOS_HELPER_EXIT: + if (LWORD(eax) == DOS_HELPER_REALLY_EXIT) { + /* terminate code is in bx */ + dbug_printf("DOS termination requested\n"); + if (!config.dumb_video) + p_dos_str("\n\rLeaving DOS...\n\r"); + leavedos(LO(bx)); + } + break; + +#ifdef USE_COMMANDS_PLUGIN + case DOS_HELPER_COMMANDS: + if (!commands_plugin_inte6()) + return 0; + break; + case DOS_HELPER_COMMANDS_DONE: + if (!commands_plugin_inte6_done()) + return 0; + break; + case DOS_HELPER_SET_RETCODE: + if (!commands_plugin_inte6_set_retcode()) + return 0; + break; +#endif + + default: + error("bad dos helper function: AX=0x%04x\n", LWORD(eax)); + return 0; + } + + return 1; +} + +static int int15(void) +{ + int num; + + if (HI(ax) != 0x4f) + NOCARRY; + + switch (HI(ax)) { + case 0x10: /* TopView/DESQview */ + switch (LO(ax)) { + case 0x00:{ /* giveup timeslice */ + idle_enable(100, 0, "topview"); + break; + } + } + CARRY; + break; + + case 0x24: /* PS/2 A20 gate support */ + switch (LO(ax)) { + case 0: /* disable A20 gate */ + set_a20(0); + HI(ax) = 0; + NOCARRY; + break; + + case 1: /* enable A20 gate */ + set_a20(1); + HI(ax) = 0; + NOCARRY; + break; + + case 2: /* get A20 gate status */ + HI(ax) = 0; + LO(ax) = a20; + LWORD(ecx) = 0; + NOCARRY; + break; + + case 3: /* query A20 gate support */ + HI(ax) = 0; + LWORD(ebx) = 3; + NOCARRY; + break; + + default: + HI(ax) = 0x86; + CARRY; + } + break; + + case 0x41: /* wait on external event */ + break; + case 0x4f: /* Keyboard intercept */ + HI(ax) = 0x86; + /*k_printf("INT15 0x4f CARRY=%x AX=%x\n", (LWORD(eflags) & CF),LWORD(eax)); */ + k_printf("INT15 0x4f CARRY=%x AX=%x\n", (_FLAGS & CF), LWORD(eax)); + CARRY; +/* + if (LO(ax) & 0x80 ) + if (1 || !(LO(ax)&0x80) ){ + fprintf(stderr, "Carrying it out\n"); + CARRY; + } + else + NOCARRY; +*/ + break; + + case 0x80: /* default BIOS hook: device open */ + case 0x81: /* default BIOS hook: device close */ + case 0x82: /* default BIOS hook: program termination */ + HI(ax) = 0; + break; + + case 0x83: + h_printf("int 15h event wait:\n"); + show_regs(); + CARRY; + break; /* no event wait */ + case 0x84: + joy_bios_read(); + break; + case 0x85: + num = LWORD(eax) & 0xFF; /* default bios handler for sysreq key */ + if (num == 0 || num == 1) { + LWORD(eax) &= 0x00FF; + break; + } + LWORD(eax) &= 0xFF00; + LWORD(eax) |= 1; + CARRY; + break; + case 0x86: + /* wait...cx:dx=time in usecs */ + g_printf("doing int15 wait...ah=0x86\n"); + show_regs(); + kill_time((long) ((LWORD(ecx) << 16) | LWORD(edx))); + NOCARRY; + break; + + case 0x87:{ + unsigned int *lp; + unsigned src_addr, dst_addr; + unsigned src_limit, dst_limit; + unsigned int length; + void *s, *d; + unsigned int old_a20 = a20; + + lp = SEG_ADR((unsigned int *), es, si); + lp += 4; + src_addr = (*lp >> 16) & 0x0000FFFF; + src_limit = *lp & 0x0000FFFF; + lp++; + src_addr |= (*lp & 0xFF000000) | ((*lp << 16) & 0x00FF0000); + src_limit |= (*lp & 0x000F0000); + lp++; + dst_addr = (*lp >> 16) & 0x0000FFFF; + dst_limit = *lp & 0x0000FFFF; + lp++; + dst_addr |= (*lp & 0xFF000000) | ((*lp << 16) & 0x00FF0000); + dst_limit |= (*lp & 0x000F0000); + + length = LWORD(ecx) << 1; + + x_printf("int 15: block move: src=%#x dst=%#x len=%#x\n", + src_addr, dst_addr, length); + + if (src_limit < length - 1 || dst_limit < length - 1 || + src_addr + length > LOWMEM_SIZE + EXTMEM_SIZE || + dst_addr + length > LOWMEM_SIZE + EXTMEM_SIZE) { + x_printf("block move failed\n"); + LWORD(eax) = 0x0200; + CARRY; + break; + } + /* Have to enable a20 before translating addresses */ + if (!a20) + set_a20(1); + while (length) { + /* avoid crossing page boundaries */ + int s_al = PAGE_ALIGN(src_addr) - src_addr; + int d_al = PAGE_ALIGN(dst_addr) - dst_addr; + int todo; + + if (!s_al) + s_al += PAGE_SIZE; + if (!d_al) + d_al += PAGE_SIZE; + todo = _min(s_al, d_al); + todo = _min(todo, length); + x_printf("int 15: copy subblock: src=%#x dst=%#x len=%#x\n", + src_addr, dst_addr, todo); + s = physaddr_to_unixaddr(src_addr); + if (s == MAP_FAILED) { + error("error mapping %x to addr\n", src_addr); + break; + } + d = physaddr_to_unixaddr(dst_addr); + if (d == MAP_FAILED) { + error("error mapping %x to addr\n", dst_addr); + break; + } + e_invalidate_pa(dst_addr, todo); + memcpy(d, s, todo); + src_addr += todo; + dst_addr += todo; + length -= todo; + } + if (old_a20 != a20) + set_a20(old_a20); + if (length) { + LWORD(eax) = 0x0200; + CARRY; + } else { + LWORD(eax) = 0; + NOCARRY; + } + break; + } + + case 0x88: + LWORD(eax) = EXTMEM_SIZE >> 10; + NOCARRY; + break; + + case 0x89: /* enter protected mode : kind of tricky! */ + LWORD(eax) |= 0xFF00; /* failed */ + CARRY; + break; + case 0x90: /* no device post/wait stuff */ + CARRY; + break; + case 0x91: + CARRY; + break; + case 0xbf: /* DOS/16M,DOS/4GW */ + switch (REG(eax) &= 0x00FF) { + case 0: + case 1: + case 2: /* installation check */ + default: + REG(edx) = 0; + CARRY; + break; + } + break; + case 0xc0: + SREG(es) = ROM_CONFIG_SEG; + LWORD(ebx) = ROM_CONFIG_OFF; + HI(ax) = 0; + break; + case 0xc1: + CARRY; + break; /* no ebios area */ + case 0xc2: + mouse_ps2bios(); + break; + case 0xc3: + /* no watchdog */ + CARRY; + break; + case 0xc4: + /* no post */ + CARRY; + break; + case 0xc9: + if (LO(ax) == 0x10) { + HI(ax) = 0; + HI(cx) = vm86s.cpu_type; + LO(cx) = 0x20; + break; + } + /* else fall through */ + case 0xd8: /* EISA - should be set in config? */ + case 0xda: + case 0xdb: + HI(ax) = 0x86; + CARRY; + break; + + case 0xe8: +#if 0 + -- -- -- --b - 15E801-- -- -- -- -- -- -- -- -- -- -- -- -- -- -INT 15 - Phoenix BIOS v4 .0 - GET MEMORY SIZE FOR > 64 M CONFIGURATIONS AX = E801h Return:CF clear if + successful + AX = extended memory between 1 M and 16 M, in K(max 3 C00h = + 15 MB) + BX = extended memory above 16 M, in 64 K blocks CX = configured memory 1 M to 16 M, in K DX = configured memory above 16 M, in 64 K blocks CF set on error Notes:supported by the A03 level(6 / 14 / + 94) and later XPS P90 BIOSes, + as well as the Compaq Contura, 3 / 8 / 93 DESKPRO / i, + and 7 / 26 / + 93 LTE Lite 386 ROM BIOS supported by AMI BIOSes dated 8 / + 23 / 94 or later on some systems, the BIOS returns AX = + BX = 0000 h; + in this case, + use CX and DX instead of AX and BX this interface is used by + Windows NT 3.1, OS / 2 v2 .11 / 2.20, + and is used as a fall - back by newer versions if AX = E820h + is not supported SeeAlso: + AH = 8 Ah "Phoenix", AX = E802h, AX = E820h, AX = + E881h "Phoenix"-- -- + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - +#endif + if (LO(ax) == 1) { + Bit32u mem = EXTMEM_SIZE >> 10; + if (mem < 0x3c00) { + LWORD(eax) = mem; + LWORD(ebx) = 0; + } else { + LWORD(eax) = 0x3c00; + LWORD(ebx) = ((mem - 0x3c00) >> 6); + } + LWORD(ecx) = LWORD(eax); + LWORD(edx) = LWORD(ebx); + NOCARRY; + break; + } else if (REG(eax) == 0xe820 && REG(edx) == 0x534d4150) { + REG(eax) = REG(edx); + /* a maximum of 20 bytes will be transferred at one time, even if ECX is + * higher; some BIOSes (e.g. Award Modular BIOS v4.50PG) ignore the + * value of ECX on entry, and always copy 20 bytes (RBIL) */ + if (REG(ebx) < system_memory_map_size && REG(ecx) >= 20) { + REG(ecx) = 20; + MEMCPY_2DOS(SEGOFF2LINEAR(_ES, _DI), + (char *) system_memory_map + REG(ebx), + REG(ecx)); + REG(ebx) += REG(ecx); + if (REG(ebx) >= system_memory_map_size) + REG(ebx) = 0; + NOCARRY; + } else { + REG(eax) = 0x8600; + CARRY; + } + break; + } + /* Fall through !! */ + + default: + g_printf("int 15h error: ax=0x%04x\n", LWORD(eax)); + CARRY; + break; + } + return 1; +} + +/* Set the DOS ticks value in BIOS area, then clear midnight flag */ +static void set_ticks(unsigned long new_ticks) +{ + WRITE_DWORD(BIOS_TICK_ADDR, new_ticks); + /* A timer read/write should reset the overflow flag */ + WRITE_BYTE(TICK_OVERFLOW_ADDR, 0); + h_printf("TICKS: update ticks to %ld\n", new_ticks); +} + +/* + * DANG_BEGIN_FUNCTION int1a + * + * int 0x1A call + * + * This has (among other things) the calls that DOS makes to get/set its sense + * of time. On booting, DOS gets the RTC time and date with AH=2 and AH=4, + * after that it should use AH=0 calls to read the 'tick' counter from BIOS + * memory. Each time this crosses midnight, a flag is set that DOS uses to + * increment its date. + * + * Here we can now change the 'view' of time so the calls either return BIOS + * tick (most DOS like), read the PIT counter (avoids INT-8 changes) or gets + * LINUX time (most accurate for long term NTP-adjusted time keeping). + * + * DANG_END_FUNCTION + */ + +static int int1a(void) +{ + int_yield(); + + switch (HI(ax)) { + +/* +--------B-1A00------------------------------- +INT 1A - TIME - GET SYSTEM TIME + AH = 00h +Return: CX:DX = number of clock ticks since midnight + AL = midnight flag, nonzero if midnight passed since time last read +Notes: there are approximately 18.2 clock ticks per second, 1800B0h per 24 hrs + (except on Tandy 2000, where the clock runs at 20 ticks per second) + IBM and many clone BIOSes set the flag for AL rather than incrementing + it, leading to loss of a day if two consecutive midnights pass + without a request for the time (e.g. if the system is on but idle) +-> since the midnight flag is cleared, if an application calls this +-> function after midnight before DOS does, DOS will not receive the +-> midnight flag and will fail to advance the date + +Note that DOSEMU's int8 (just like Bochs and some others, see bios.S) +increments AL so we *don't* lose a day if two consecutive midnights pass. +*/ + case 0: /* read time counter */ + { + int day_rollover; + idle_enable2(50, 50, 0, "int1a:0"); + if (config.timemode == TM_LINUX) { + /* Set BIOS area flags to LINUX time computed values always */ + last_ticks = get_linux_ticks(0, &day_rollover); + } else if (config.timemode == TM_BIOS) { + /* BIOSTIMER_ONLY_VIEW + * + * We rely on the INT8 routine doing the right thing, + * DOS apps too rely on the relationship between INT1A and 0x46c timer. + * We already do all appropriate things to trigger the simulated INT8 + * correctly (well, sometimes faking it), so the 0x46c timer incremented + * by the (realmode) INT8 handler should be always in sync. + * Therefore, we keep INT1A,AH0 simple instead of trying to be too clever;-) + */ + static int first = 1; + if (first) { + /* take over the correct value _once_ only */ + last_ticks = (unsigned long) (pic_sys_time >> 16) + + (sys_base_ticks + usr_delta_ticks); + set_ticks(last_ticks); + first = 0; + } + + last_ticks = READ_DWORD(BIOS_TICK_ADDR); + day_rollover = READ_BYTE(TICK_OVERFLOW_ADDR); + } else { /* (config.timemode == TM_PIT) assumed */ + + /* not BIOSTIMER_ONLY_VIEW + * pic_sys_time is a zero-based tick (1.19MHz) counter. As such, if we + * shift it right by 16 we get the number of PIT0 overflows, that is, + * the number of 18.2ms timer ticks elapsed since starting dosemu. This + * is independent of any int8 speedup a program can set, since the PIT0 + * counting frequency is fixed. The count overflows after 7 1/2 years. + * usr_delta_ticks is 0 as long as nobody sets a new time (B-1A01) + */ +#ifdef DEBUG_INT1A + g_printf + ("TIMER: sys_base_ticks=%ld usr_delta_ticks=%ld pic_sys_time=%#Lx\n", + sys_base_ticks, usr_delta_ticks, pic_sys_time); +#endif + last_ticks = (unsigned long) (pic_sys_time >> 16); + last_ticks += (sys_base_ticks + usr_delta_ticks); + + /* has the midnight passed? */ + day_rollover = last_ticks / TICKS_IN_A_DAY; + last_ticks %= TICKS_IN_A_DAY; + /* since pic_sys_time continues to increase, avoid further midnight overflows */ + sys_base_ticks -= day_rollover * TICKS_IN_A_DAY; + } + + LWORD(eax) = day_rollover; + LWORD(ecx) = (last_ticks >> 16) & 0xffff; + LWORD(edx) = last_ticks & 0xffff; + +#ifdef DEBUG_INT1A + if (debug_level('g')) { + time_t time_val; + long k = last_ticks / 18.2065; /* sorry */ + time(&time_val); + g_printf("INT1A: read timer = %ld (%ld:%ld:%ld) %s\n", + last_ticks, k / 3600, (k % 3600) / 60, (k % 60), + ctime(&time_val)); + } +#else + g_printf("INT1A: read timer=%ld, midnight=%d\n", last_ticks, + LO(ax)); +#endif + set_ticks(last_ticks); /* Write to BIOS_TICK_ADDR & clear TICK_OVERFLOW_ADDR */ + } + break; + +/* +--------B-1A01------------------------------- +INT 1A - TIME - SET SYSTEM TIME + AH = 01h + CX:DX = number of clock ticks since midnight +Return: nothing +Notes: there are approximately 18.2 clock ticks per second, 1800B0h per 24 hrs + (except on Tandy 2000, where the clock runs at 20 ticks per second) + this call resets the midnight-passed flag +SeeAlso: AH=00h,AH=03h,INT 21/AH=2Dh +*/ + case 1: /* write time counter */ + if (config.timemode == TM_LINUX) { + g_printf("INT1A: can't set DOS timer\n"); /* Allow time set except in 'LINUX view' case. */ + } else { + /* get current system time and check it (previous usr_delta could be != 0) */ + long t; + do { + t = (pic_sys_time >> 16) + sys_base_ticks; + if (t < 0) + sys_base_ticks += TICKS_IN_A_DAY; + } while (t < 0); + + /* get user-requested time */ + last_ticks = ((uint32_t)LWORD(ecx) << 16) | (LWORD(edx) & 0xffff); + + usr_delta_ticks = last_ticks - t; + +#ifdef DEBUG_INT1A + g_printf + ("TIMER: sys_base_ticks=%ld usr_delta_ticks=%ld pic_sys_time=%#Lx\n", + sys_base_ticks, usr_delta_ticks, pic_sys_time); +#endif + g_printf("INT1A: set timer to %ld\n", last_ticks); + + set_ticks(last_ticks); /* Write to BIOS_TICK_ADDR & clear TICK_OVERFLOW_ADDR */ + } + break; + +/* +--------B-1A02------------------------------- +INT 1A - TIME - GET REAL-TIME CLOCK TIME (AT,XT286,PS) + AH = 02h +Return: CF clear if successful + CH = hour (BCD) + CL = minutes (BCD) + DH = seconds (BCD) + DL = daylight savings flag (00h standard time, 01h daylight time) + CF set on error (i.e. clock not running or in middle of update) +Note: this function is also supported by the Sperry PC, which predates the + IBM AT; the data is returned in binary rather than BCD on the Sperry, + and DL is always 00h +SeeAlso: AH=00h,AH=03h,AH=04h,INT 21/AH=2Ch +*/ + case 2: /* get time */ + idle(50, 50, 0, "int1a:2"); + if (config.timemode != TM_BIOS) { + get_linux_ticks(1, NULL); /* Except BIOS view time, force RTC to LINUX time. */ + } + HI(cx) = rtc_read(CMOS_HOUR); + LO(cx) = rtc_read(CMOS_MIN); + HI(dx) = rtc_read(CMOS_SEC); + LO(dx) = 0; /* No daylight saving - yuch */ + g_printf("INT1A: RTC time %02x:%02x:%02x\n", HI(cx), LO(cx), + HI(dx)); + NOCARRY; + break; + +/* +--------B-1A03------------------------------- +INT 1A - TIME - SET REAL-TIME CLOCK TIME (AT,XT286,PS) + AH = 03h + CH = hour (BCD) + CL = minutes (BCD) + DH = seconds (BCD) + DL = daylight savings flag (00h standard time, 01h daylight time) +Return: nothing +Note: this function is also supported by the Sperry PC, which predates the + IBM AT; the data is specified in binary rather than BCD on the + Sperry, and the value of DL is ignored +*/ + case 3: /* set time */ + if (config.timemode != TM_BIOS) { + g_printf("INT1A: RTC: can't set time\n"); + } else { + rtc_write(CMOS_HOUR, HI(cx)); + rtc_write(CMOS_MIN, LO(cx)); + rtc_write(CMOS_SEC, HI(dx)); + g_printf("INT1A: RTC set time %02x:%02x:%02x\n", HI(cx), + LO(cx), HI(dx)); + } + NOCARRY; + break; + +/* +--------B-1A04------------------------------- +INT 1A - TIME - GET REAL-TIME CLOCK DATE (AT,XT286,PS) + AH = 04h +Return: CF clear if successful + CH = century (BCD) + CL = year (BCD) + DH = month (BCD) + DL = day (BCD) + CF set on error +SeeAlso: AH=02h,AH=04h"Sperry",AH=05h,INT 21/AH=2Ah,INT 4B/AH=02h"TI" +*/ + case 4: /* get date */ + idle(50, 50, 0, "int1a:4"); + if (config.timemode != TM_BIOS) { + get_linux_ticks(1, NULL); + } + HI(cx) = rtc_read(CMOS_CENTURY); + LO(cx) = rtc_read(CMOS_YEAR); + HI(dx) = rtc_read(CMOS_MONTH); + LO(dx) = rtc_read(CMOS_DOM); + /* REG(eflags) &= ~CF; */ + g_printf("INT1A: RTC date %04x%02x%02x (DOS format)\n", LWORD(ecx), + HI(dx), LO(dx)); + NOCARRY; + break; + +/* +--------B-1A05------------------------------- +INT 1A - TIME - SET REAL-TIME CLOCK DATE (AT,XT286,PS) + AH = 05h + CH = century (BCD) + CL = year (BCD) + DH = month (BCD) + DL = day (BCD) +Return: nothing +*/ + case 5: /* set date */ + if (config.timemode != TM_BIOS) { + g_printf("INT1A: RTC: can't set date\n"); + } else { + rtc_write(CMOS_CENTURY, HI(cx)); + rtc_write(CMOS_YEAR, LO(cx)); + rtc_write(CMOS_MONTH, HI(dx)); + rtc_write(CMOS_DOM, LO(dx)); + g_printf("INT1A: RTC set date %04x/%02x/%02x\n", LWORD(ecx), + HI(dx), LO(dx)); + } + NOCARRY; + break; + + /* Notes: the alarm occurs every 24 hours until turned off, invoking INT 4A + each time the BIOS does not check for invalid values for the time, so + the CMOS clock chip's "don't care" setting (any values between C0h + and FFh) may be used for any or all three parts. For example, to + create an alarm once a minute, every minute, call with CH=FFh, CL=FFh, + and DH=00h. (R.Brown) + */ + case 6: /* set alarm */ + { + unsigned char h, m, s; + + if (rtc_read(CMOS_STATUSB) & 0x20) { + CARRY; + } else { + rtc_write(CMOS_HOURALRM, (h = _CH)); + rtc_write(CMOS_MINALRM, (m = _CL)); + rtc_write(CMOS_SECALRM, (s = _DH)); + r_printf("RTC: set alarm to %02d:%02d:%02d\n", h, m, s); /* BIN! */ + /* This has been VERIFIED on an AMI BIOS -- AV */ + rtc_write(CMOS_STATUSB, rtc_read(CMOS_STATUSB) | 0x20); + NOCARRY; + } + break; + } + + case 7: /* clear alarm but NOT PIC mask */ + /* This has been VERIFIED on an AMI BIOS -- AV */ + rtc_write(CMOS_STATUSB, rtc_read(CMOS_STATUSB) & ~0x20); + break; + + case 0xb1: /* Intel PCI BIOS v 2.0c */ + pci_bios(); + break; + + default: + g_printf("WARNING: unsupported INT0x1a call 0x%02x\n", HI(ax)); + CARRY; + } /* End switch(HI(ax)) */ + + return 1; +} + +/* ========================================================================= */ +/* + * DANG_BEGIN_FUNCTION ms_dos + * + * int0x21 call + * + * we trap this for two functions: simulating the EMMXXXX0 device and + * fudging the CONFIG.XXX and AUTOEXEC.XXX bootup files. + * + * note that the emulation herein may cause problems with programs + * that like to take control of certain int 21h functions, or that + * change functions that the true int 21h functions use. An example + * of the latter is ANSI.SYS, which changes int 10h, and int 21h + * uses int 10h. for the moment, ANSI.SYS won't work anyway, so it's + * no problem. + * + * DANG_END_FUNCTION + */ +static int msdos(void) +{ + ds_printf + ("INT21 at %04x:%04x: AX=%04x, BX=%04x, CX=%04x, DX=%04x, DS=%04x, ES=%04x\n", + SREG(cs), LWORD(eip), LWORD(eax), LWORD(ebx), + LWORD(ecx), LWORD(edx), SREG(ds), SREG(es)); + +#if 1 + if (HI(ax) == 0x3d) { + char *p = MK_FP32(SREG(ds), LWORD(edx)); + int i; + + ds_printf("INT21: open file \""); + for (i = 0; i < 64 && p[i]; i++) + ds_printf("%c", p[i]); + ds_printf("\"\n"); + } +#endif + + switch (HI(ax)) { +/* the below idle handling moved to int10 */ +#if 0 + case 0x06: + if (LO(dx) == 0xff) + return 0; + /* fallthrough */ + case 0x02: + case 0x04: + case 0x05: + case 0x09: + case 0x40: /* output functions: reset idle */ + reset_idle(0); + return 0; +#endif + + case 0x2C:{ /* get time & date */ + idle_enable(100, 0, "dos_time"); + return 0; + } + + case 0x4B:{ /* program load */ + char *ptr; + const char *tmp_ptr; + char cmdname[256]; + char *cmd = SEG_ADR((char *), ds, dx); + char *str = cmd; + struct param4a *pa4 = SEG_ADR((struct param4a *), es, bx); + struct lowstring *args = FARt_PTR(pa4->cmdline); + + switch (LO(ax)) { + case 0x00: + case 0x01: + snprintf(cmdname, _min(sizeof cmdname, args->len + 1), "%s", args->s); + ds_printf + ("INT21 4B: load/execute program=\"%s\", L(cmdline=\"%s\")=%i\n", + str, cmdname, args->len); + break; + + case 0x03: /* AL=03h:load overlay have no cmdline in EPB */ + snprintf(cmdname, sizeof cmdname, "%s", cmd); + ds_printf("INT21 4B: load overlay=\"%s\"\n", str); + break; + + case 0x80: /* DR-DOS run already loaded kernel file, no cmdline */ + snprintf(cmdname, sizeof cmdname, "%s", cmd); + ds_printf("INT21 4B80: run already loaded file=\"%s\"\n", str); + break; + + default: /* Assume no cmdline, log AL */ + snprintf(cmdname, sizeof cmdname, "%s", cmd); + ds_printf("INT21 4B: AL=%02x, cmdname=\"%s\"\n", LO(ax), str); + break; + } + + /* for old DOSes without INSTALL= support, we need this */ + if (config.force_redir && !redir_state && + strcasestr(cmd, "\\command.com")) { + ds_printf("INT21: open of command processor triggering post_boot\n"); + if (do_redirect(1)) + redir_state++; + } + +#if WINDOWS_HACKS + if (strstrDOS(cmd, "\\SYSTEM\\KRNL386.EXE")) + win3x_mode = ENHANCED; + if (strstrDOS(cmd, "\\SYSTEM\\KRNL286.EXE")) + win3x_mode = STANDARD; + if (strstrDOS(cmd, "\\SYSTEM\\KERNEL.EXE")) + win3x_mode = RM; + if ((ptr = strstrDOS(cmd, "\\SYSTEM\\DOSX.EXE")) || + (ptr = strstrDOS(cmd, "\\SYSTEM\\WIN386.EXE"))) { + int have_args = 0; + tmp_ptr = strstr(cmdname, "krnl386"); + if (!tmp_ptr) + tmp_ptr = strstr(cmdname, "krnl286"); + if (!tmp_ptr) + tmp_ptr = (ptr[8] == 'd' ? "krnl286" : "krnl386"); + else + have_args = 1; +#if 1 + /* ignore everything and use krnl386.exe */ + memcpy(ptr + 8, "krnl386", 7); +#else + memcpy(ptr + 8, tmp_ptr, 7); +#endif + strcpy(ptr + 8 + 7, ".exe"); + win3x_mode = tmp_ptr[4] - '0'; + if (have_args) { + tmp_ptr = strchr(tmp_ptr, ' '); + if (tmp_ptr) { + strcpy(args->s, tmp_ptr); + args->len -= tmp_ptr - cmdname; + } + } + + /* the below is the winos2 mouse driver hook */ + SETIVEC(0x66, BIOSSEG, INT_OFF(0x66)); + int_handlers[0x66].interrupt_function[NO_REVECT] = _int66_; + } + + if (win3x_mode != INACTIVE) { + if ((ptr = strstrDOS(cmd, "\\SYSTEM\\DS")) && + !strstrDOS(cmd, ".EXE")) { + error + ("Windows-3.1 stack corruption detected, fixing dswap.exe\n"); + strcpy(ptr, "\\system\\dswap.exe"); + } + if ((ptr = strstrDOS(cmd, "\\SYSTEM\\WS")) && + !strstrDOS(cmd, ".EXE")) { + error + ("Windows-3.1 stack corruption detected, fixing wswap.exe\n"); + strcpy(ptr, "\\system\\wswap.exe"); + } + + sprintf(win3x_title, "Windows 3.x in %i86 mode", + win3x_mode); + str = win3x_title; + } +#endif + + if (!Video->change_config) + return 0; + if ((!title_hint[0] || strcmp(title_current, title_hint) != 0) + && str != win3x_title) + return 0; + + ptr = strrchr(str, '\\'); + if (!ptr) + ptr = str; + else + ptr++; + ptr += strspn(ptr, " \t"); + if (!ptr[0]) + return 0; + tmp_ptr = ptr; + while (*tmp_ptr) { /* Check whether the name is valid */ + if (iscntrlDOS(*tmp_ptr++)) + return 0; + } + strncpy(cmdname, ptr, TITLE_APPNAME_MAXLEN - 1); + cmdname[TITLE_APPNAME_MAXLEN - 1] = 0; + ptr = strchr(cmdname, '.'); + if (ptr && str != win3x_title) + *ptr = 0; + /* change the title */ + strlcpy(title_current, cmdname, sizeof(title_current)); + change_window_title(title_current); + can_change_title = 0; + return 0; + } + } + return 0; +} + +static void do_ret_from_int(int inum, const char *pfx) +{ + unsigned int ssp, sp; + u_short flgs; + + ssp = SEGOFF2LINEAR(SREG(ss), 0); + sp = LWORD(esp); + + _IP = popw(ssp, sp); + _CS = popw(ssp, sp); + flgs = popw(ssp, sp); + _SP += 6; + if (flgs & IF) + set_IF(); + else + clear_IF(); + REG(eflags) |= (flgs & (TF_MASK | NT_MASK)); + debug_int(pfx, inum); +} + +static void ret_from_int(int tid, void *arg, void *arg2) +{ + do_ret_from_int(tid - int_tid, "RET"); +} + +static void do_int_iret(Bit16u i, HLT_ARG(arg)) +{ + do_ret_from_int((uintptr_t)arg, "iret"); +} + +static void do_int_disp(Bit16u i, HLT_ARG(arg)) +{ + int inum = (uintptr_t)arg; + uint16_t seg, off; + + switch (inum) { +#define SW_I(n) \ + case 0x##n: \ + seg = READ_WORD(SEGOFF2LINEAR(INT_RVC_SEG, int_rvc_cs_##n)); \ + off = READ_WORD(SEGOFF2LINEAR(INT_RVC_SEG, int_rvc_ip_##n)); \ + break + SW_I(21); + SW_I(28); + SW_I(2f); + SW_I(33); + default: + return; + } + /* decide if to trace iret or not. + * We can't trace int2f as it uses stack for data exchange, + * and we can't trace int21h/26h as it uses CS as input. + * We are not interested in tracing int28h and int33h. */ + if (!config.trace_irets || inum != 0x21 || _AH == 0x26 || + _AH == 0x31 || _AH == 0x4c || _AH == 0 || _AH == 0x4b) + fake_iret(); + jmp_to(seg, off); +} + +#define RVC_SETUP(x) \ +static void _int##x##_rvc_setup(uint16_t seg, uint16_t offs) \ +{ \ + WRITE_WORD(SEGOFF2LINEAR(INT_RVC_SEG, int_rvc_cs_##x), seg); \ + WRITE_WORD(SEGOFF2LINEAR(INT_RVC_SEG, int_rvc_ip_##x), offs); \ +} \ +static void int##x##_rvc_setup(void) \ +{ \ + _int##x##_rvc_setup(ISEG(0x##x), IOFF(0x##x)); \ +} \ +static void int##x##_revect(void) \ +{ \ + assert(!int##x##_hooked); \ + int##x##_rvc_setup(); \ + fake_int_to(INT_RVC_SEG, INT_RVC_##x##_OFF); \ +} \ +static uint16_t iret_##x##_hlt_off; \ +static uint16_t disp_##x##_hlt_off; \ +static void int##x##_rvc_init(void) \ +{ \ + emu_hlt_t hlt_hdlr = HLT_INITIALIZER; \ + emu_hlt_t hlt_hdlr2 = HLT_INITIALIZER; \ + hlt_hdlr.name = "int" #x " iret"; \ + hlt_hdlr.func = do_int_iret; \ + hlt_hdlr.arg = (void *)0x##x; \ + iret_##x##_hlt_off = hlt_register_handler_vm86(hlt_hdlr); \ + hlt_hdlr2.name = "int" #x " disp"; \ + hlt_hdlr2.func = do_int_disp; \ + hlt_hdlr2.arg = (void *)0x##x; \ + disp_##x##_hlt_off = hlt_register_handler_vm86(hlt_hdlr2); \ +} \ +static void int##x##_rvc_post_init(void) \ +{ \ + WRITE_WORD(SEGOFF2LINEAR(INT_RVC_SEG, int_rvc_ret_cs_##x), \ + BIOS_HLT_BLK_SEG); \ + WRITE_WORD(SEGOFF2LINEAR(INT_RVC_SEG, int_rvc_ret_ip_##x), \ + iret_##x##_hlt_off); \ + WRITE_WORD(SEGOFF2LINEAR(INT_RVC_SEG, int_rvc_disp_cs_##x), \ + BIOS_HLT_BLK_SEG); \ + WRITE_WORD(SEGOFF2LINEAR(INT_RVC_SEG, int_rvc_disp_ip_##x), \ + disp_##x##_hlt_off); \ +} + +/* + * We support the following cases: + * 1. The ints were already unrevectored by post_boot(), then return error. + * 2. The ints were initially not revectored by vm86.int_revectored + * ($_force_int_revect = (off)). Then we allow setting them up. The + * care must be taken in mfs/lfn to not crash if this happens before + * the init of these subsystems. At the time of writing this, such + * care is taken. Make sure it stays so in the future. :) + * 3. The ints were initially revectored and still are. + * Disable revectoring but set them to our handlers, effectively + * not changing anything. + */ +#define UNREV(x) \ +RVC_SETUP(x) \ +static far_t int##x##_unrevect(uint16_t seg, uint16_t offs) \ +{ \ + far_t ret = {}; \ + if (int##x##_hooked) \ + return ret; \ + int##x##_hooked = 1; \ + di_printf("int_rvc: unrevect 0x%s\n", #x); \ + if (test_bit(0x##x, &vm86s.int_revectored)) { \ + if (!mhp_revectored(0x##x)) \ + clear_bit(0x##x, &vm86s.int_revectored); \ + else \ + mhp_adjust_revectored(0x##x); \ + } else { \ + di_printf("int_rvc: revectoring of 0x%s was not enabled\n", #x); \ + } \ + _int##x##_rvc_setup(seg, offs); \ + ret.segment = INT_RVC_SEG; \ + ret.offset = INT_RVC_##x##_OFF; \ + return ret; \ +} \ +static int int##x##_unrevect_simple(void) \ +{ \ + if (int##x##_hooked || !int_handlers[0x##x].interrupt_function[REVECT]) \ + return 0; \ + int##x##_hooked = 1; \ + di_printf("int_rvc: unrevect 0x%s\n", #x); \ + clear_bit(0x##x, &vm86s.int_revectored); \ + int##x##_rvc_setup(); \ + SETIVEC(0x##x, INT_RVC_SEG, INT_RVC_##x##_OFF); \ + return 1; \ +} + +UNREV(21) +UNREV(28) +UNREV(2f) +UNREV(33) + +static void int_revect_init(void) +{ + int21_rvc_init(); + int28_rvc_init(); + int2f_rvc_init(); + int33_rvc_init(); +} + +static void int_revect_post_init(void) +{ + int21_rvc_post_init(); + int28_rvc_post_init(); + int2f_rvc_post_init(); + int33_rvc_post_init(); +} + +static void post_boot_unrevect(void) +{ + int21_unrevect_simple(); + int28_unrevect_simple(); + int2f_unrevect_simple(); + if (int33_unrevect_simple()) { + /* This is needed here to revectoring the interrupt, after dos + * has revectored it. --EB 1 Nov 1997 */ + SETIVEC(0x33, Mouse_SEG, Mouse_INT_OFF); + } +} + + +static far_t int33_unrevect_fixup(uint16_t seg, uint16_t offs) +{ + far_t ret = int33_unrevect(seg, offs); + if (ret.offset != INT_RVC_33_OFF) + return ret; + ret.segment = Mouse_SEG; + ret.offset = Mouse_INT_OFF; + return ret; +} + +static int msdos_chainrevect(int stk_offs, int revect) +{ + switch (HI(ax)) { + case 0x71: + if (config.lfn) + return I_SECOND_REVECT; + break; + case 0x73: /* fat32 API */ + case 0x6c: /* extended open, needs mostly for LFNs */ + return I_SECOND_REVECT; + } + return msdos(); +} + +static void msdos_xtra(uint16_t old_ax, uint16_t old_flags) +{ + di_printf("int_rvc 0x21 call for ax=0x%04x %x\n", LWORD(eax), old_ax); + + CARRY; + switch (HI_BYTE(old_ax)) { + case 0x71: + if (LWORD(eax) != 0x7100) + break; + if (config.lfn) { + int ret; + LWORD(eax) = old_ax; + if (!(old_flags & CF)) + NOCARRY; + /* mfs_lfn() clears CF on success, sets on failure, preserves + * on unsupported */ + ret = mfs_lfn(); + if (!ret) + LWORD(eax) = 0x7100; + } + break; + case 0x73: + if (LWORD(eax) != 0x7300) + break; + LWORD(eax) = old_ax; + if (!(old_flags & CF)) + NOCARRY; + mfs_fat32(); + break; + case 0x6c: + if (LWORD(eax) != 0x6c00) + break; + LWORD(eax) = old_ax; + if (!(old_flags & CF)) + NOCARRY; + msdos_remap_extended_open(); + break; + } +} + +static int msdos_xtra_norev(int stk_off, int revect) +{ + di_printf("int_norvc 0x21 call for ax=0x%04x\n", LWORD(eax)); + switch (HI(ax)) { + case 0x71: + if (config.lfn) + return mfs_lfn(); + else + CARRY; + break; + case 0x73: + mfs_fat32(); + break; + case 0x6c: + msdos_remap_extended_open(); + break; + } + return 0; +} + +void int42_hook(void) +{ + /* original int10 vector should point here until vbios swaps it with 0x42. + * But our int10 never points here, so I doubt this is of any use. --stsp */ + fake_iret(); + int10(); +} + +/* ========================================================================= */ + +void real_run_int(int i) +{ + unsigned int ssp, sp; + + ssp = SEGOFF2LINEAR(_SS, 0); + sp = _SP; + + pushw(ssp, sp, read_FLAGS()); + pushw(ssp, sp, _CS); + pushw(ssp, sp, _IP); + _SP -= 6; + _CS = ISEG(i); + _IP = IOFF(i); + + /* clear TF (trap flag, singlestep), VIF/IF (interrupt flag), and + * NT (nested task) bits of EFLAGS + * NOTE: IF-flag only, because we are not sure that we will test it in + * some of our own software (...we all are human beings) + * For vm86() 'VIF' is the candidate to reset in order to do CLI ! + */ + clear_TF(); + clear_NT(); + if (IS_CR0_AM_SET()) + clear_AC(); + clear_IF(); +} + +static void do_print_screen(void) +{ + int x_pos, y_pos; + int li = READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1) + 1; + int co = READ_WORD(BIOS_SCREEN_COLUMNS); + unsigned base = screen_adr(READ_BYTE(BIOS_CURRENT_SCREEN_PAGE)); + + g_printf("PrintScreen: base=%x, lines=%i columns=%i\n", base, li, co); + if (printer_open(0) == -1) + return; + for (y_pos = 0; y_pos < li; y_pos++) { + for (x_pos = 0; x_pos < co; x_pos++) { + uint8_t val = vga_read(base + 2 * (y_pos * co + x_pos)); + if (val == 0) + val = ' '; + printer_write(0, val); + } + printer_write(0, 0x0d); + printer_write(0, 0x0a); + } + printer_close(0); +} + +static int int05(void) +{ + /* FIXME does this test actually catch an unhandled bound exception */ + if (*SEG_ADR((Bit8u *), cs, ip) == 0x62) { /* is this BOUND ? */ + /* avoid deadlock: eip is not advanced! */ + error("Unhandled BOUND exception!\n"); + leavedos(54); + } + g_printf("INT 5: PrintScreen\n"); + do_print_screen(); + return 1; +} + +/* CONFIGURATION */ +static int int11(void) +{ + LWORD(eax) = READ_WORD(BIOS_CONFIGURATION); + return 1; +} + +/* MEMORY */ +static int int12(void) +{ + LWORD(eax) = READ_WORD(BIOS_MEMORY_SIZE); + return 1; +} + +/* BASIC */ +static int int18(void) +{ + k_printf("BASIC interrupt being attempted.\n"); + return 1; +} + +/* LOAD SYSTEM */ +static int int19(void) +{ + int stal; + coopth_leave(); + dpmi_done0(); + if (clnup_handler) + clnup_handler(); + clnup_handler = NULL; + stal = coopth_flush_vm86(); + if (stal) { + error("stalled %i threads on reboot\n", stal); + coopth_unsafe_shutdown(); + } + map_custom_bios(); + cpu_reset(); + jmp_to(0xffff, 0); + return 1; +} + +static uint16_t DoRedirectDevice(char *dStr, char *sStr, + uint8_t deviceType, uint16_t deviceOptions, + uint16_t userValue) +{ + uint16_t ret; + + pre_msdos(); + + /* should verify strings before sending them down ??? */ + SREG(ds) = DOSEMU_LMHEAP_SEG; + LWORD(esi) = DOSEMU_LMHEAP_OFFS_OF(dStr); + SREG(es) = DOSEMU_LMHEAP_SEG; + LWORD(edi) = DOSEMU_LMHEAP_OFFS_OF(sStr); + LWORD(edx) = deviceOptions | REDIR_CLIENT_SIGNATURE; + LWORD(ecx) = userValue; + LWORD(ebx) = deviceType; + LWORD(eax) = DOS_REDIRECT_DEVICE; + + call_msdos(); + + ret = (LWORD(eflags) & CF) ? LWORD(eax) : CC_SUCCESS; + + post_msdos(); + return ret; +} + +uint16_t RedirectDevice(char *dStr, char *sStr, + uint8_t deviceType, uint16_t deviceOptions) +{ + return DoRedirectDevice(dStr, sStr, deviceType, deviceOptions, 0); +} + +static int DoRedirectDisk(int dsk, const char *resourceName, int flags, + int uval) +{ + char *dStr = lowmem_alloc(16); + char *rStr = lowmem_alloc(256); + int ret; + + dStr[0] = dsk + 'A'; + dStr[1] = ':'; + dStr[2] = '\0'; + snprintf(rStr, 256, LINUX_RESOURCE "%s", resourceName); + + ret = DoRedirectDevice(dStr, rStr, REDIR_DISK_TYPE, flags, uval); + + lowmem_free(rStr); + lowmem_free(dStr); + return ret; +} + +static int RedirectDisk(int dsk, const char *resourceName, int flags) +{ + return DoRedirectDisk(dsk, resourceName, flags, 0); +} + +static int RedirectPrinter(int lptn) +{ + char *dStr = lowmem_alloc(16); + char *rStr = lowmem_alloc(128); + int ret; + + snprintf(dStr, 16, "LPT%i", lptn); + snprintf(rStr, 128, LINUX_PRN_RESOURCE "\\%i", lptn); + + ret = RedirectDevice(dStr, rStr, REDIR_PRINTER_TYPE, 0); + + lowmem_free(rStr); + lowmem_free(dStr); + return ret; +} + +static int redir_printers(void) +{ + int i; + int max = lpt_get_max(); + + for (i = NUM_LPTS; i < max; i++) { + if (!lpt_is_configured(i)) + continue; + c_printf("redirecting LPT%i\n", i + 1); + if (RedirectPrinter(i + 1) != CC_SUCCESS) { + printf("failure redirecting LPT%i\n", i + 1); + return 1; + } + } + return 0; +} + +/* drive for -K (aka system.com) */ +struct drive_syscom { + char *path; + int mfs_idx; +#define SCUSERS 5 + uint8_t *drv_num[SCUSERS]; + int num_scusers; +}; +static struct drive_syscom syscomdrv; +/* drive for -d */ +struct drive_xtra { + char *path; + unsigned ro:1; + unsigned cdrom:1; + unsigned grp:1; + int mfs_idx; +}; +#define MAX_EXTRA_DRIVES 50 +static struct drive_xtra extra_drives[MAX_EXTRA_DRIVES]; +static int num_x_drives; + +#define MK_R_FLAGS(ro, cdrom, prm, dsb, mfs_idx) ((ro) | ((cdrom) << 1) | \ + ((prm) ? REDIR_DEVICE_PERMANENT : 0) | \ + ((dsb) ? REDIR_DEVICE_DISABLED : 0) | \ + ((mfs_idx) << REDIR_DEVICE_IDX_SHIFT)) + +#define REDIR_F_GRP 1 + +void add_syscom_drive(char *path, uint8_t *user) +{ + assert(syscomdrv.num_scusers == 0); + syscomdrv.path = expand_path(path); + syscomdrv.mfs_idx = mfs_define_drive(syscomdrv.path); + syscomdrv.drv_num[0] = user; +} + +void add_syscom_user(uint8_t *user) +{ + assert(1 + syscomdrv.num_scusers < SCUSERS); + syscomdrv.drv_num[1 + syscomdrv.num_scusers++] = user; +} + +int add_extra_drive(char *path, int ro, int cd, int grp) +{ + struct drive_xtra *drv; + if (num_x_drives >= MAX_EXTRA_DRIVES) { + error("too many drives\n"); + return -1; + } + drv = &extra_drives[num_x_drives++]; + drv->path = expand_path(path); + if (!drv->path) { + error("Path %s does not exist\n", path); + return -1; + } + if (!exists_dir(drv->path)) { + error("Directory %s does not exist\n", drv->path); + free(drv->path); + return -1; + } + drv->ro = ro; + drv->cdrom = cd; + drv->grp = grp; + drv->mfs_idx = mfs_define_drive(drv->path); + return 0; +} + +static int get_redirection_index(int drive, int *r_mfs_idx, uint16_t *r_udata) +{ + uint16_t redirIndex = 0, ccode; + char dStr[MAX_DEVICE_STRING_LENGTH]; + char dStrSrc[MAX_DEVICE_STRING_LENGTH]; + char res_backup[128]; + uint16_t opts; + + snprintf(dStrSrc, MAX_DEVICE_STRING_LENGTH, "%c:", drive + 'A'); + while ((ccode = get_redirection(redirIndex, dStr, sizeof dStr, + res_backup, sizeof(res_backup), + r_udata, &opts, NULL)) == + CC_SUCCESS) { + if (strcmp(dStrSrc, dStr) == 0) { + if (r_mfs_idx) + *r_mfs_idx = REDIR_DEVICE_IDX(opts); + return redirIndex; + } + redirIndex++; + } + + return -1; +} + +static int is_redirection_exist(int drive) +{ + return (get_redirection_index(drive, NULL, NULL) != -1); +} + +static int is_occupied_drive_letter(int drv) +{ + char *fname, *fcb; + int ret; + + pre_msdos(); + + /* Parse filename into FCB (physical, formatted or not, and network) */ + fname = lowmem_alloc(16); + snprintf(fname, 16, "%c:FILENAME.EXT", 'A' + drv); + fcb = lowmem_alloc(0x25); + memset(fcb, 0, 0x25); + + HI(ax) = 0x29; // Parse Filename + LO(ax) = 0x00; // Standard parsing + SREG(ds) = DOSEMU_LMHEAP_SEG; + LWORD(esi) = DOSEMU_LMHEAP_OFFS_OF(fname); + SREG(es) = DOSEMU_LMHEAP_SEG; + LWORD(edi) = DOSEMU_LMHEAP_OFFS_OF(fcb); + call_msdos(); + + lowmem_free(fcb); + lowmem_free(fname); + + ret = (LO(ax) != 0xff); // 0xff == invalid drive + + post_msdos(); + + if (!ret) { + /* may be disabled redirection */ + ret = is_redirection_exist(drv); + } + return ret; +} + +int find_free_drive(void) +{ + int drive; + + for (drive = 2; drive < 26; drive++) { + if (is_occupied_drive_letter(drive)) // 0 = A etc + continue; + return drive; + } + + return -1; +} + +/******************************************** + * get_redirection - get next entry from list of redirected devices + * ON ENTRY: + * redirIndex has the index of the next device to return + * this should start at 0, and be incremented between calls + * to retrieve all elements of the redirection list + * ON EXIT: + * returns CC_SUCCESS if the operation was successful, and + * deviceStr has a string with the device name: + * either disk or printer (ex. 'D:' or 'LPT1') + * resourceStr has a string with the server and name of resource + * (ex. 'TIM\TOOLS') + * deviceType indicates the type of device which was redirected + * 3 = printer, 4 = disk + * deviceUserData has the magic word passed during creation + * deviceOptions has Dosemu specifics (disabled, cdrom unit, read only) + * NOTES: + * + ********************************************/ +static uint16_t do_get_redirection(uint16_t redirIndex, + char *deviceStr, int deviceSize, + char *resourceStr, int resourceSize, + uint16_t *deviceUserData, + uint16_t *deviceOptions, uint8_t *deviceStatus, + uint16_t subfunc) +{ + char *dStr; + char *rStr; + uint16_t ret, deviceUserDataTemp, deviceOptionsTemp; + uint8_t deviceStatusTemp; + + assert(resourceSize <= MAX_RESOURCE_LENGTH_EXT); + dStr = lowmem_alloc(deviceSize); + rStr = lowmem_alloc(resourceSize); + pre_msdos(); + + SREG(ds) = DOSEMU_LMHEAP_SEG; + LWORD(esi) = DOSEMU_LMHEAP_OFFS_OF(dStr); + SREG(es) = DOSEMU_LMHEAP_SEG; + LWORD(edi) = DOSEMU_LMHEAP_OFFS_OF(rStr); + + LWORD(edx) = REDIR_CLIENT_SIGNATURE; + LWORD(ecx) = resourceSize; + LWORD(ebx) = redirIndex; + LWORD(eax) = subfunc; + + call_msdos(); + + ret = (LWORD(eflags) & CF) ? LWORD(eax) : CC_SUCCESS; + + if (LO(bx) != REDIR_DISK_TYPE) + ret = 0x12; // NO_MORE_FILES + deviceStatusTemp = HI(bx); + deviceUserDataTemp = LWORD(ecx); + deviceOptionsTemp = LWORD(edx); + + post_msdos(); + + if (ret == CC_SUCCESS) { + strlcpy(resourceStr, rStr, resourceSize); + strlcpy(deviceStr, dStr, deviceSize); + + if (deviceUserData) + *deviceUserData = deviceUserDataTemp; + if (deviceOptions) + *deviceOptions = deviceOptionsTemp; + if (deviceStatus) + *deviceStatus = deviceStatusTemp; + } + + lowmem_free(rStr); + lowmem_free(dStr); + + return ret; +} + +uint16_t get_redirection(uint16_t redirIndex, + char *deviceStr, int deviceSize, + char *resourceStr, int resourceSize, + uint16_t *deviceUserData, + uint16_t *deviceOptions, uint8_t *deviceStatus) +{ + return do_get_redirection(redirIndex, deviceStr, deviceSize, + resourceStr, resourceSize, deviceUserData, + deviceOptions, deviceStatus, DOS_GET_REDIRECTION_EXT); +} + +uint16_t get_redirection_ux(uint16_t redirIndex, + char *deviceStr, int deviceSize, + char *resourceStr, int resourceSize, + uint16_t *deviceUserData, + uint16_t *deviceOptions, uint8_t *deviceStatus) +{ + return do_get_redirection(redirIndex, deviceStr, deviceSize, + resourceStr, resourceSize, deviceUserData, + deviceOptions, deviceStatus, DOS_GET_REDIRECTION_EX6); +} + +int get_lastdrive(void) +{ + int ld; + pre_msdos(); + LWORD(eax) = 0x0e00; + LWORD(edx) = 0xffff; + call_msdos(); + ld = LO(ax); + post_msdos(); + return ld; +} + +int getCWD_r(int drive, char *rStr, int len) +{ +#define DOS_GET_CWD 0x4700 + char *cwd; + int cf, ax; + + cwd = lowmem_alloc(64); + + pre_msdos(); + LWORD(eax) = DOS_GET_CWD; + LWORD(edx) = drive + 1; + SREG(ds) = DOSEMU_LMHEAP_SEG; + LWORD(esi) = DOSEMU_LMHEAP_OFFS_OF(cwd); + call_msdos(); + cf = isset_CF(); + ax = LWORD(eax); + post_msdos(); + if (cf) { + lowmem_free(cwd); + return (ax ?: -1); + } + + if (cwd[0]) { + snprintf(rStr, len, "%c:\\%s", 'A' + drive, cwd); + } else { + snprintf(rStr, len, "%c:", 'A' + drive); + } + lowmem_free(cwd); + return 0; +} + +int getCWD_cur(char *rStr, int len) +{ +#define DOS_GET_DEFAULT_DRIVE 0x1900 + uint8_t drive; + pre_msdos(); + LWORD(eax) = DOS_GET_DEFAULT_DRIVE; + call_msdos(); + drive = LO(ax); + post_msdos(); + return getCWD_r(drive, rStr, len); +} + +char *getCWD(int drive) +{ + static char dcwd[MAX_PATH_LENGTH]; + int err = getCWD_r(drive, dcwd, MAX_PATH_LENGTH); + if (err) + return NULL; + return dcwd; +} + +int get_redirection_root(int drive, char *presourceStr, int resourceLength) +{ + uint16_t redirIndex = 0, ccode; + char dStr[MAX_DEVICE_STRING_LENGTH]; + char dStrSrc[MAX_DEVICE_STRING_LENGTH]; + char res_backup[128]; + char *resStr = resourceLength > 0 ? presourceStr : res_backup; + int resLen = resourceLength > 0 ? resourceLength : sizeof(res_backup); + + snprintf(dStrSrc, MAX_DEVICE_STRING_LENGTH, "%c:", drive + 'A'); + while ((ccode = do_get_redirection(redirIndex, dStr, sizeof dStr, + resStr, resLen, + NULL, NULL, NULL, + DOS_GET_REDIRECTION_EX6)) == + CC_SUCCESS) { + if (strcmp(dStrSrc, dStr) == 0) + return strlen(resStr); + redirIndex++; + } + + return -1; +} + +static int get_redirection_drive(char *presourceStr) +{ + uint16_t redirIndex = 0, ccode; + char dStr[MAX_DEVICE_STRING_LENGTH]; + char resourceStr[MAX_RESOURCE_LENGTH_EXT]; + + while ((ccode = do_get_redirection(redirIndex, dStr, sizeof(dStr), + resourceStr, sizeof(resourceStr), + NULL, NULL, NULL, + DOS_GET_REDIRECTION_EX6)) == + CC_SUCCESS) { + if (strcmp(resourceStr, presourceStr) == 0) + return (dStr[0] - 'A'); + redirIndex++; + } + + return -1; +} + +int is_redirection_ro(int drive) +{ + uint16_t redirIndex = 0, ccode; + char dStr[MAX_DEVICE_STRING_LENGTH]; + char dStrSrc[MAX_DEVICE_STRING_LENGTH]; + char res_backup[128]; + uint16_t opts; + + snprintf(dStrSrc, MAX_DEVICE_STRING_LENGTH, "%c:", drive + 'A'); + while ((ccode = get_redirection(redirIndex, dStr, sizeof dStr, + res_backup, sizeof(res_backup), + NULL, &opts, NULL)) == + CC_SUCCESS) { + if (strcmp(dStrSrc, dStr) == 0) + return !!(opts & REDIR_DEVICE_READ_ONLY); + redirIndex++; + } + + return -1; +} + +/* + * Turn all simulated FAT devices into network drives. + */ +static void redirect_drives(void) +{ + int i, ret; + + FOR_EACH_HDISK(i, { + if (hdisktab[i].type == DIR_TYPE && hdisktab[i].fatfs) { + ret = RedirectDisk(HDISK_NUM(i) + hdisktab[i].log_offs, + hdisktab[i].dev_name, hdisktab[i].rdonly + + (hdisktab[i].mfs_idx << REDIR_DEVICE_IDX_SHIFT)); + if (ret != CC_SUCCESS) + error("INT21: redirecting %c: failed (err = %d)\n", i + 'C', ret); + else + ds_printf("INT21: redirecting %c: ok\n", i + 'C'); + } + }); +} + +static int redir_one_drive(const char *path, int ro, int cdrom, int prm, + int grp, int mfs_idx) +{ + int ret; + int drv = find_free_drive(); + if (drv < 0) { + error("no free drives\n"); + if (get_lastdrive() < 7) + error("@Set LASTDRIVE=Z in your fdconfig.sys\n"); + else if (config.boot_dos == FATFS_FD_D) { + error("@-d/-K is not supported with this freedos version\n"); + leavedos(26); + } + return -1; + } + ret = DoRedirectDisk(drv, path, MK_R_FLAGS(ro, cdrom, prm, grp, mfs_idx), + grp ? REDIR_F_GRP : 0); + if (ret != CC_SUCCESS) { + error("INT21: redirecting %s failed (err = %d)\n", path, ret); + if (get_lastdrive() < 7) + error("@Set LASTDRIVE=Z in your fdconfig.sys\n"); + else if (config.boot_dos == FATFS_FD_D && (ret == 0x55 /* duplicate redirect */ + || ret == 0xf /* invalid drive */)) { + error("-d/-K is not supported with this freedos version\n"); + leavedos(26); + } + return -1; + } else { + ds_printf("INT21: redirecting %s ok\n", path); + } + return drv; +} + +char *com_strdup(const char *s) +{ + struct lowstring *p; + int len = strlen(s); + if (len > 254) { + error("lowstring too long: %i bytes.\n", len); + len = 254; + } + + p = (void *)lowmem_alloc(len + 1 + sizeof(struct lowstring)); + if (!p) return 0; + p->len = len; + memcpy(p->s, s, len); + p->s[len] = 0; + return p->s; +} + +void com_strfree(char *s) +{ + struct lowstring *p = (void *)(s - 1); + lowmem_free((char *)p); +} + +uint16_t cancel_redirection(const char *deviceStr) +{ + char *dStr = com_strdup(deviceStr); + uint16_t ret; + + pre_msdos(); + + SREG(ds) = DOSEMU_LMHEAP_SEG; + LWORD(esi) = DOSEMU_LMHEAP_OFFS_OF(dStr); + LWORD(eax) = DOS_CANCEL_REDIRECTION; + + call_msdos(); + + ret = (LWORD(eflags) & CF) ? LWORD(eax) : CC_SUCCESS; + + post_msdos(); + + com_strfree(dStr); + + return ret; +} + +static int add_drive_group(const char *path, int ro, int mfs_idx) +{ +#define IS_RO(e, b) ((e ? 0 : !!(b.f_flags & ST_RDONLY)) || ro) +#define IS_CD(e, b) (e ? 0 : !!(b.f_type == ISOFS_SUPER_MAGIC || \ + b.f_type == UDF_SUPER_MAGIC)) + char *wild; + glob_t p; + int i, err; + int cnt = 0; + + asprintf(&wild, "%s/*", path); + glob(wild, 0, NULL, &p); + free(wild); + for (i = 0; i < p.gl_pathc; i++) { + struct statfs sb; + struct stat st; + err = stat(p.gl_pathv[i], &st); + if (err) { + error("error stat %s\n", p.gl_pathv[i]); + break; + } + if (!S_ISDIR(st.st_mode)) + continue; + /* see if drive is already there */ + err = get_redirection_drive(p.gl_pathv[i]); + if (err != -1) + continue; + err = statfs(p.gl_pathv[i], &sb); + err = redir_one_drive(p.gl_pathv[i], IS_RO(err, sb), IS_CD(err, sb), + 0, 0, mfs_idx); + if (err < 0) + break; + cnt++; + } + globfree(&p); + + return cnt; +} + +static int add_redir_group(int redirIdx, int mfs_idx) +{ + char dStr[MAX_DEVICE_STRING_LENGTH]; + char resourceStr[MAX_RESOURCE_LENGTH_EXT]; + uint16_t rc, opts; + + rc = get_redirection_ux(redirIdx, dStr, sizeof(dStr), + resourceStr, sizeof(resourceStr), NULL, &opts, NULL); + if (rc != CC_SUCCESS) + return -1; + return add_drive_group(resourceStr, + !!(opts & REDIR_DEVICE_READ_ONLY), mfs_idx); +} + +static void update_group(uint16_t redirIndex, int mfs_idx) +{ + char dStr[MAX_DEVICE_STRING_LENGTH]; + char rStr[MAX_RESOURCE_LENGTH_EXT]; + uint16_t ccode; + struct stat st; + uint16_t opts, udata; + int err; + int redirIndex2 = redirIndex + 1; + + while ((ccode = get_redirection_ux(redirIndex2, + dStr, sizeof(dStr), + rStr, sizeof(rStr), + &udata, &opts, NULL)) == + CC_SUCCESS) { + if (REDIR_DEVICE_IDX(opts) != mfs_idx) { + redirIndex2++; + continue; + } + err = stat(rStr, &st); + if (err || !S_ISDIR(st.st_mode)) + cancel_redirection(dStr); + else + redirIndex2++; + } + add_redir_group(redirIndex, mfs_idx); +} + +int update_redir_group(int drive) +{ + int mfs_idx; + uint16_t udata; + int redirIdx = get_redirection_index(drive, &mfs_idx, &udata); + if (redirIdx == -1 || !(udata & REDIR_F_GRP)) + return -1; + update_group(redirIdx, mfs_idx); + return 0; +} + +static int rehash_redir_groups(void) +{ + uint16_t redirIndex = 0, ccode; + char dGrpStr[MAX_DEVICE_STRING_LENGTH]; + char grpRes[128]; + uint16_t opts, udata; + int cnt = 0; + + while ((ccode = get_redirection(redirIndex, dGrpStr, sizeof(dGrpStr), + grpRes, sizeof(grpRes), + &udata, &opts, NULL)) == + CC_SUCCESS) { + if (udata & REDIR_F_GRP) { + update_group(redirIndex, REDIR_DEVICE_IDX(opts)); + cnt++; + } + redirIndex++; + } + + return cnt; +} + +static void redir_extra_drives(void) +{ + int i, ret, drv; + + if (syscomdrv.path) { + drv = redir_one_drive(syscomdrv.path, 0, 0, 1, 0, syscomdrv.mfs_idx); + if (drv < 0) { + leavedos(26); + return; + } + /* notify all users about this drive */ + for (i = 0; i < 1 + syscomdrv.num_scusers; i++) + *syscomdrv.drv_num[i] = drv; + } + + for (i = 0; i < num_x_drives; i++) { + struct drive_xtra *d = &extra_drives[i]; + ret = redir_one_drive(d->path, d->ro, d->cdrom, 1, d->grp, d->mfs_idx); + if (ret < 0) + break; + if (d->grp) + add_drive_group(d->path, d->ro, d->mfs_idx); + } +} + +/* + * redirect everything else. + * must be done after config.sys processing. + */ +static void redirect_devices(void) +{ + if (redir_state2) + return; + redir_state2++; + redir_extra_drives(); + redir_printers(); +} + +static int do_redirect(int old_only) +{ + uint16_t lol_lo, lol_hi, sda_lo, sda_hi, sda_size, redver, mosver; + uint8_t major, minor; + int is_MOS; + int is_cf; + + /* + * To start up the redirector we need + * (1) the list of list, + * (2) the DOS version and + * (3) the swappable data area. + */ + pre_msdos(); + + LWORD(eax) = 0x1100; + do_int_call_back(0x2f); + if (LO(ax) != 0xff) { + error("Redirector unavailable\n"); + _post_msdos(); + return 0; + } + + LWORD(eax) = 0x5200; + call_msdos(); + ds_printf + ("INT21 +1 at %04x:%04x: AX=%04x, BX=%04x, CX=%04x, DX=%04x, DS=%04x, ES=%04x\n", + SREG(cs), LWORD(eip), LWORD(eax), LWORD(ebx), + LWORD(ecx), LWORD(edx), SREG(ds), SREG(es)); + lol_lo = LWORD(ebx); + lol_hi = SREG(es); + + LWORD(eax) = LWORD(ebx) = LWORD(ecx) = LWORD(edx) = 0x3099; // MOS version trigger + call_msdos(); + ds_printf + ("INT21 +2b at %04x:%04x: AX=%04x, BX=%04x, CX=%04x, DX=%04x, DS=%04x, ES=%04x\n", + SREG(cs), LWORD(eip), LWORD(eax), LWORD(ebx), + LWORD(ecx), LWORD(edx), SREG(ds), SREG(es)); + mosver = LWORD(eax); + LWORD(eax) = 0x3000; // DOS version std request + call_msdos(); + ds_printf + ("INT21 +2 at %04x:%04x: AX=%04x, BX=%04x, CX=%04x, DX=%04x, DS=%04x, ES=%04x\n", + SREG(cs), LWORD(eip), LWORD(eax), LWORD(ebx), + LWORD(ecx), LWORD(edx), SREG(ds), SREG(es)); + major = LO(ax); + minor = HI(ax); + is_MOS = (LWORD(eax) != mosver); // different result! + + LWORD(eax) = 0x5d06; + call_msdos(); + ds_printf + ("INT21 +3 at %04x:%04x: AX=%04x, BX=%04x, CX=%04x, DX=%04x, DS=%04x, ES=%04x\n", + SREG(cs), LWORD(eip), LWORD(eax), LWORD(ebx), + LWORD(ecx), LWORD(edx), SREG(ds), SREG(es)); + sda_lo = LWORD(esi); + sda_hi = SREG(ds); + sda_size = LWORD(ecx); + + ds_printf("INT21: lol = %04x:%04x\n", lol_hi, lol_lo); + ds_printf("INT21: sda = %04x:%04x, size = 0x%04x\n", sda_hi, sda_lo, sda_size); + ds_printf("INT21: ver = 0x%02x, 0x%02x\n", major, minor); + + /* Figure out the redirector version */ + if (is_MOS) { + redver = REDVER_NONE; + } else { + if (major == 3) + if (minor <= 9) + redver = (sda_size == SDASIZE_CQ30) ? REDVER_CQ30 : REDVER_PC30; + else + redver = REDVER_PC31; + else + redver = REDVER_PC40; /* Most common redirector format */ + } + + if (old_only && redver == REDVER_PC40) { + _post_msdos(); + return 0; + } + /* Try to init the redirector. */ + HI(bx) = redver; + LWORD(ecx) = lol_hi; + LWORD(edx) = lol_lo; + LWORD(esi) = sda_hi; + LWORD(edi) = sda_lo; + LO(bx) = DOS_SUBHELPER_MFS_REDIR_INIT; + LWORD(eax) = DOS_HELPER_MFS_HELPER; + do_int_call_back(DOS_HELPER_INT); + is_cf = isset_CF(); + post_msdos(); + if (!is_cf) + redirect_drives(); /* We have a functioning redirector so use it */ + else + ds_printf("INT21: this DOS has an incompatible redirector\n"); + return !is_cf; +} + +static int redir_it(void) +{ + if (redir_state) + return 0; + if (do_redirect(0)) { + redir_state++; + return 1; + } + return 0; +} + +static int enable_redirect(void) +{ + int is_cf; + uint16_t lol_lo, lol_hi, sda_lo, sda_hi; + pre_msdos(); + + LWORD(eax) = 0x5200; + call_msdos(); + lol_lo = LWORD(ebx); + lol_hi = SREG(es); + + LWORD(eax) = 0x5d06; + call_msdos(); + sda_lo = LWORD(esi); + sda_hi = SREG(ds); + + LWORD(ecx) = lol_hi; + LWORD(edx) = lol_lo; + LWORD(esi) = sda_hi; + LWORD(edi) = sda_lo; + LO(bx) = DOS_SUBHELPER_MFS_REDIR_RESET; + HI(bx) = 0; + LWORD(eax) = DOS_HELPER_MFS_HELPER; + do_int_call_back(DOS_HELPER_INT); + is_cf = isset_CF(); + if (is_cf) + goto done; + LWORD(eax) = DOS_SET_REDIRECTION_MODE; + LO(bx) = REDIR_DISK_TYPE; + HI(bx) = 1; // enable + call_msdos(); + is_cf = isset_CF(); +done: + post_msdos(); + return !is_cf; +} + +void dos_post_boot_reset(void) +{ + revect_setup(); + post_boot = 0; + redir_state = 0; + redir_state2 = 0; + if (clnup_handler) + clnup_handler(); + clnup_handler = NULL; + syscomdrv.num_scusers = 0; +#ifdef USE_MHPDBG + mhp_reset_hma(); +#endif +} + +static void dos_post_boot(void) +{ + if (!post_boot) { + post_boot = 1; + post_boot_unrevect(); + /* call vint_setup() again to avoid DOS STACKS hooks, see + * https://github.com/dosemu2/dosemu2/issues/1607 */ + vint_setup(); + if (config.force_redir) { + if (!redir_state) { + redir_it(); + redirect_devices(); + } else { + enable_redirect(); + } + } +#ifdef USE_MHPDBG + /* + * Let's set up the hma start address for dosdebug use. There's no + * specific reason it should be here other than it needs to be done + * sometime after DOS has started. + */ + mhp_init_hma(); +#endif + start_pre_strokes(); + } +} + +/* KEYBOARD BUSY LOOP */ +static int int28(void) +{ + idle_enable(50, 0, "int28"); + return 1; +} + +/* FAST CONSOLE OUTPUT */ +static int int29(void) +{ + /* char in AL */ + char_out(*(char *) ®(eax), READ_BYTE(BIOS_CURRENT_SCREEN_PAGE)); + return 1; +} + +struct ae00_tab { + uint8_t max_len; + struct lowstring cmd; + char _filler[0]; +} __attribute__((packed)); + +static char *ae00_cmd; + +static int run_program_ae00(const char *command) +{ + free(ae00_cmd); + ae00_cmd = strdup(command); + return 0; +} + +static int run_program_ae01(void) +{ + char *p; + int cmd_len; + struct lowstring *name = SEG_ADR((struct lowstring *), ds, si); + struct ae00_tab *cmd = SEG_ADR((struct ae00_tab *), ds, bx); + + name->len = 0; + if (!ae00_cmd) + return 0; + cmd_len = strlen(ae00_cmd); + if (cmd_len + 2 > cmd->max_len) { + error("too long cmd line, %i have %i\n", cmd_len + 2, cmd->max_len); + goto done; + } + strcpy(cmd->_filler, ae00_cmd); + cmd->_filler[cmd_len] = '\r'; + cmd->cmd.len = cmd_len; + p = strchr(ae00_cmd, ' '); + if (p && strlen(p) > 1) + *p++ = '\0'; + strupperDOS(ae00_cmd); + strcpy(name->s, ae00_cmd); + name->len = strlen(ae00_cmd); + +done: + free(ae00_cmd); + ae00_cmd = NULL; + return 0; +} + +static unsigned short do_get_psp(int parent) +{ + /* don't care about parent, as command.com is primary */ + return sda_cur_psp(sda); +} + +static void do_run_cmd(struct lowstring *str, struct ae00_tab *cmd) +{ + char arg0[256]; + char cmdbuf[256]; + int rc; + + memcpy(arg0, str->s, str->len); + arg0[str->len] = '\0'; + memcpy(cmdbuf, cmd->cmd.s, cmd->cmd.len); + cmdbuf[cmd->cmd.len] = '\0'; + rc = run_command_plugin(arg0, NULL, cmdbuf, run_program_ae00, do_get_psp); + if (rc != 0) + _AL = 0xff; +} + +/* size not propagated through DOS API, so we cache it */ +static int clipb_size; + +static int int2f(int stk_offs, int revect) +{ + reset_idle(0); +#if 1 + ds_printf + ("INT2F at %04x:%04x: AX=%04x, BX=%04x, CX=%04x, DX=%04x, DS=%04x, ES=%04x\n", + SREG(cs), LWORD(eip), LWORD(eax), LWORD(ebx), LWORD(ecx), + LWORD(edx), SREG(ds), SREG(es)); +#endif + + switch (LWORD(eax)) { +#ifdef IPX + case INT2F_DETECT_IPX: /* TRB - detect IPX in int2f() */ + if (config.ipxsup && IPXInt2FHandler()) + return 1; + break; +#endif + + case 0xae00:{ + char cmdname[TITLE_APPNAME_MAXLEN]; + char appname[TITLE_APPNAME_MAXLEN + 5]; + struct lowstring *str = SEG_ADR((struct lowstring *), ds, si); + struct ae00_tab *cmd = SEG_ADR((struct ae00_tab *), ds, bx); + u_short psp_seg; + struct MCB *mcb; + int len, i; + char *ptr, *tmp_ptr; + + dos_post_boot(); + + if (_DX != 0xffff) + break; + + if (!sda) + break; + psp_seg = sda_cur_psp(sda); + if (!psp_seg) + break; + do_run_cmd(str, cmd); + mcb = (struct MCB *) SEG2UNIX(psp_seg - 1); + if (!mcb) + break; + + if (!Video->change_config) + break; + + /* Check whether the program name is invalid (DOS < 4.0) */ + for (i=0; i<8 && mcb->name[i]; i++) { + if (iscntrlDOS(mcb->name[i])) { + snprintf(title_hint, sizeof title_hint, "COMMAND"); + goto hint_done; + } + } + strncpy(title_hint, mcb->name, 8); + title_hint[8] = 0; +hint_done: + + len = _min(str->len, (unsigned char) (TITLE_APPNAME_MAXLEN - 1)); + memcpy(cmdname, str->s, len); + cmdname[len] = 0; + ptr = cmdname + strspn(cmdname, " \t"); + tmp_ptr = ptr; + while (*tmp_ptr) { /* Check whether the name is valid */ + if (iscntrlDOS(*tmp_ptr++)) + return 0; + } + strcpy(title_current, title_hint); + if (!ptr[0]) { + change_window_title(title_current); + return 0; + } + snprintf(appname, sizeof(appname), "%s ( %s )", + title_current, strlowerDOS(ptr)); + change_window_title(appname); + } + break; + case 0xae01: + if (_DX != 0xffff) + break; + run_program_ae01(); + break; + } + + switch (HI(ax)) { + case 0x11: /* redirector call? */ + if (LO(ax) == 0x23) + subst_file_ext(SEG_ADR((char *), ds, si)); + if (mfs_redirector(®S, MK_FP32(_SS, _SP + stk_offs), revect)) + return 1; + break; + + case 0x15: + if (mscdex()) + return 1; + break; + + case 0x16: /* misc PM/Win functions */ + switch (LO(ax)) { + case 0x00: /* WINDOWS ENHANCED MODE INSTALLATION CHECK */ + if (dpmi_active() && win3x_mode != INACTIVE) { + D_printf + ("WIN: WINDOWS ENHANCED MODE INSTALLATION CHECK: %i\n", + win3x_mode); + if (win3x_mode == ENHANCED) + LWORD(eax) = 0x0a03; + else + LWORD(eax) = 0; + return 1; + } + break; + + case 0x05: /* Win95 Initialization Notification */ + LWORD(ecx) = 0xffff; /* say it`s NOT ok to run under Win */ + case 0x06: /* Win95 Termination Notification */ + case 0x07: /* Win95 Device CallOut */ + case 0x08: /* Win95 Init Complete Notification */ + case 0x09: /* Win95 Begin Exit Notification */ + if (dpmi_active()) + return 1; + break; + + case 0x0a: /* IDENTIFY WINDOWS VERSION AND TYPE */ + if (dpmi_active() && win3x_mode != INACTIVE) { + D_printf("WIN: WINDOWS VERSION AND TYPE\n"); + LWORD(eax) = 0; + LWORD(ebx) = 0x030a; /* 3.10 */ + LWORD(ecx) = win3x_mode; + return 1; + } + break; + + case 0x80: /* give up time slice */ + idle_enable(100, 0, "int2f_idle_magic"); + if (config.hogthreshold) { + LO(ax) = 0; + return 1; + } + break; + + + case 0x83: + if (dpmi_active() && win3x_mode != INACTIVE) { + LWORD(ebx) = 1; /* W95: number of virtual machine */ + return 1; + } + break; + case 0x81: /* W95: enter critical section */ + if (dpmi_active() && win3x_mode != INACTIVE) { + D_printf("WIN: enter critical section\n"); + /* LWORD(eax) = 0; W95 DDK says no return value */ + return 1; + } + break; + case 0x82: /* W95: exit critical section */ + if (dpmi_active() && win3x_mode != INACTIVE) { + D_printf("WIN: exit critical section\n"); + /* LWORD(eax) = 0; W95 DDK says no return value */ + return 1; + } + break; + + case 0x84: /* Win95 Get Device Entry Point */ + LWORD(edi) = 0; + WRITE_SEG_REG(es, 0); /* say NO to Win95 ;-) */ + return 1; + case 0x85: /* Win95 Switch VM + Call Back */ + CARRY; + LWORD(eax) = 1; + return 1; + + case 0x86: + D_printf("DPMI CPU mode check in real mode.\n"); + /* for protected-mode counterpart see do_dpmi_int() */ + return 1; + + case 0x87: /* Call for getting DPMI entry point */ + dpmi_get_entry_point(); + return 1; + } + break; + + case 0x17: /* MS Windows WINOLDAP functions */ + switch (LO(ax)) { + case 0x00: /* IDENTIFY WinOldAp VERSION */ + LO(ax) = 0x01; // major version + HI(ax) = 0x00; // minor version + v_printf("Check for WinOldAp\n"); // Installed == (AX != 1700) + return 1; + case 0x01: /* OPEN CLIPBOARD */ + LWORD(eax) = 1; // success (AX != 0) + v_printf("Open clipboard\n"); + return 1; + case 0x02: /* EMPTY CLIPBOARD */ + v_printf("Clear clipboard\n"); + if (Clipboard && Clipboard->clear) + LWORD(eax) = Clipboard->clear(); + else + LWORD(eax) = 0; + return 1; + case 0x03: /* WRITE CLIPBOARD */ + v_printf("Write clipboard\n"); + if (Clipboard && Clipboard->write) { + char *pbuf = MK_FP32(SREG(es), LWORD(ebx)); + unsigned int bsize = LWORD(esi) << 16 | LWORD(ecx); + LWORD(eax) = Clipboard->write(LWORD(edx), pbuf, bsize); + } else + LWORD(eax) = 0; + return 1; + case 0x04: /* GET CLIPBOARD DATA SIZE */ + v_printf("Get clipboard size\n"); + if (Clipboard && Clipboard->getsize) { + clipb_size = Clipboard->getsize(LWORD(edx)); + LWORD(edx) = (clipb_size >> 16); + LWORD(eax) = (clipb_size & 0xffff); + } else { + LWORD(edx) = LWORD(eax) = 0; + } + return 1; + case 0x05: /* GET CLIPBOARD DATA */ + v_printf("Get clipboard data\n"); + if (Clipboard && Clipboard->getdata) { + char *pbuf = MK_FP32(SREG(es), LWORD(ebx)); + LWORD(eax) = Clipboard->getdata(LWORD(edx), pbuf, + clipb_size); + } else + LWORD(eax) = 0; + return 1; + case 0x08: /* CLOSE CLIPBOARD */ + LWORD(eax) = 1; // success (AX != 0) + v_printf("Close clipboard\n"); + return 1; + case 0x09: /* COMPACT CLIPBOARD */ + // SI:CX = desired size in bytes + // DX:AX = number of bytes in largest block of free memory + LWORD(edx) = 0; + LWORD(eax) = 0x1000; // 4K should indicate enough room + v_printf("Compact clipboard\n"); + return 1; + case 0x0a: /* GET DEVICE CAPABILITIES */ + LWORD(eax) = 0; // Not sure if we should "support" this + // function, but doesn't seem possible to + // indicate otherwise. + v_printf("Get device capabilities (0x%02x)\n", LWORD(edx)); + return 1; + default: + v_printf("BAD WinOldAp func int 2f/ax=0x%04x\n", LWORD(eax)); + } + break; + + case INT2F_XMS_MAGIC: + if (!xms_intdrv()) + break; + switch (LO(ax)) { + case 0: /* check for XMS */ + x_printf("Check for XMS\n"); + LWORD(eax) = xms_install_check(); + break; + case 0x10: + x_printf("Get XMSControl address\n"); + /* SREG(es) = XMSControl_SEG; */ + WRITE_SEG_REG(es, XMSControl_SEG); + LWORD(ebx) = XMSControl_OFF; + break; + default: + x_printf("BAD int 0x2f XMS function:0x%02x\n", LO(ax)); + } + return 1; + + case 0x4a: + switch (LO(ax)) { + case 0: + if (HI(dx) >= config.fdisks) { + LWORD(ecx) = 0xffff; // no such floppy + return 1; + } + break; + } + break; + } + + return 0; +} + +static void int33_check_hog(void); + +/* mouse */ +static int int33(void) +{ +/* New code introduced by Ed Sirett (ed@cityscape.co.uk) 26/1/95 to give + * garrot control when the dos app is polling the mouse and the mouse is + * taking a break. */ + +/* Firstly do the actual mouse function. */ +/* N.B. This code lets the real mode mouse driver return at a HLT, so + * after it returns the hogthreshold code can do its job. + */ + mouse_int(); + int33_check_hog(); + return 1; +} + +static void int33_check_hog(void) +{ + static unsigned short int oldx = 0, oldy = 0; + +/* It seems that the only mouse sub-function that could be plausibly used to + * poll the mouse is AX=3 - get mouse buttons and position. + * The mouse driver should have left AX=3 unaltered during its call. + * The correct response should have the buttons in the low 3 bits in BX and + * x,y in CX,DX. + * Some programs seem to interleave calls to read mouse with various other + * sub-functions (Esp. 0x0b 0x05 and 0x06) + * As a result we do not reset the trigger value in these cases. + * Sadly, some programs use the user-specified mouse-event handler function (0x0c) + * after which they then wait for mouse events presumably in a tight loop, I think + * that we won't be able to stop these programs from burning CPU cycles. + */ + if (LWORD(eax) == 0x0003) { + if (LWORD(ebx) == 0 && oldx == LWORD(ecx) && oldy == LWORD(edx)) + trigger_idle(); + else { + reset_idle(0); + oldx = LWORD(ecx); + oldy = LWORD(edx); + } + } + m_printf("Called/ing the mouse with AX=%x \n", LWORD(eax)); + /* Ok now we test to see if the mouse has been taking a break and we can let the + * system get on with some real work. :-) */ + idle(200, 20, 20, "mouse"); +} + +static int int66(void) +{ + switch (_AH) { + case 0x80: + m_printf("mouse: int66 ah=0x80, si=%x\n", _SI); + mouse_set_win31_mode(); + break; + default: + CARRY; + break; + } + return 0; +} + +static int int67(void) +{ + return ems_fn(®S); +} + +static void debug_int(const char *s, int i) +{ + di_printf + ("%s INT0x%02x eax=0x%08x ebx=0x%08x ss=0x%04x esp=0x%08x\n" + " ecx=0x%08x edx=0x%08x ds=0x%04x cs=0x%04x ip=0x%04x\n" + " esi=0x%08x edi=0x%08x es=0x%04x flg=0x%08x\n", s, + i, _EAX, _EBX, _SS, _ESP, _ECX, _EDX, _DS, _CS, _IP, _ESI, + _EDI, _ES, (int) read_EFLAGS()); +} + +static void do_int_from_thr(void *arg) +{ + u_char i = (long) arg; + run_caller_func(i, NO_REVECT, 6); +/* for now dosdebug uses int_revect feature, so this should be disabled + * or it will display the same entry twice */ +#if 0 +#ifdef USE_MHPDBG + mhp_debug(DBG_INTx + (i << 8), 0, 0); +#endif +#endif +} + +/* + * DANG_BEGIN_FUNCTION DO_INT + * + * description: + * DO_INT is used to deal with interrupts returned to DOSEMU by the + * kernel. + * + * DANG_END_FUNCTION + */ + +static void do_int_from_hlt(Bit16u i, HLT_ARG(arg)) +{ + if (debug_level('#') > 2) + debug_int("Do", i); + + /* Always use the caller function: I am calling into the + interrupt table at the start of the dosemu bios */ + if (int_handlers[i].interrupt_function[NO_REVECT]) + coopth_start(int_tid + i, (void *) (long) i); + else + fake_iret(); +} + +static void do_rvc_chain(int i, int stk_offs) +{ + int ret = run_caller_func(i, REVECT, stk_offs); + switch (ret) { + case I_SECOND_REVECT: + di_printf("int_rvc 0x%02x setup\n", i); + if (int_handlers[i].secrevect_function) { + /* if function supported, CF will be cleared on success, but for + * unsupported functions it will stay unchanged */ + CARRY; + } + /* no break */ + case I_NOT_HANDLED: + di_printf("int 0x%02x, ax=0x%04x\n", i, LWORD(eax)); + set_ZF(); + break; + case I_HANDLED: + clear_ZF(); + break; + } +} + +static void do_basic_revect_thr(void *arg) +{ + int i = (long) arg; + run_caller_func(i, REVECT, 0); +} + +void do_int(int i) +{ +#if 1 + /* we must clear the AC flag here since real mode INT instructions + do that too. An IRET (not IRETD) instruction then does not set AC + because the AC flag is in the high part of the eflags. + DOS applications usually set AC to try to detect the presence of + a 486. They hopefully protect this test using cli and sti, or + hardware INTs will mess things up. + */ + if (IS_CR0_AM_SET()) + clear_AC(); +#else + fake_int_to(BIOS_HLT_BLK_SEG, iret_hlt_off); +#endif + +#if 1 + /* try to catch jumps to 0:0 (e.g. uninitialized user interrupt vectors), + which sometimes can crash the whole system, not only dosemu... */ + if (SEGOFF2LINEAR(ISEG(i), IOFF(i)) < 1024) { + error + ("OUCH! attempt to execute interrupt table - quickly dying\n"); + /* unfortunately World-Wide-Rally game puts trampoline into IVT, + * likely because of some NULL deref bug. So do not exit. */ +// leavedos(57); + } +#endif + + if (test_bit(i, &vm86s.int_revectored) && !mhp_revectored(i)) { + assert(int_handlers[i].interrupt_function[REVECT]); + if (debug_level('#') > 2) + debug_int("Do rvc", i); + if (int_handlers[i].revect_function) + int_handlers[i].revect_function(); + else + coopth_start(int_rvc_tid, (void *) (long) i); + } else { + di_printf("int 0x%02x, ax=0x%04x\n", i, LWORD(eax)); + if (IS_IRET(i) +#ifdef USE_MHPDBG + && !mhpdbg.active +#endif + ) { + if ((i != 0x2a) && (i != 0x28)) + g_printf("just an iret 0x%02x\n", i); + } else { + real_run_int(i); + } + } +} + +void fake_int(int cs, int ip) +{ + unsigned int ssp, sp; + + g_printf("fake_int: CS:IP %04x:%04x\n", cs, ip); + ssp = SEGOFF2LINEAR(SREG(ss), 0); + sp = LWORD(esp); + + pushw(ssp, sp, vflags); + pushw(ssp, sp, cs); + pushw(ssp, sp, ip); + LWORD(esp) -= 6; + + clear_TF(); + clear_NT(); + if (IS_CR0_AM_SET()) + clear_AC(); + clear_IF(); +} + +void fake_int_to(int cs, int ip) +{ + fake_int(SREG(cs), LWORD(eip)); + SREG(cs) = cs; + REG(eip) = ip; +} + +void fake_call(int cs, int ip) +{ + unsigned int ssp, sp; + + ssp = SEGOFF2LINEAR(SREG(ss), 0); + sp = LWORD(esp); + + g_printf("fake_call() CS:IP %04x:%04x\n", cs, ip); + pushw(ssp, sp, cs); + pushw(ssp, sp, ip); + LWORD(esp) -= 4; +} + +void fake_call_to(int cs, int ip) +{ + fake_call(SREG(cs), LWORD(eip)); + SREG(cs) = cs; + REG(eip) = ip; +} + +void fake_pusha(void) +{ + unsigned int ssp, sp; + + ssp = SEGOFF2LINEAR(SREG(ss), 0); + sp = LWORD(esp); + + pushw(ssp, sp, LWORD(eax)); + pushw(ssp, sp, LWORD(ecx)); + pushw(ssp, sp, LWORD(edx)); + pushw(ssp, sp, LWORD(ebx)); + pushw(ssp, sp, LWORD(esp)); + pushw(ssp, sp, LWORD(ebp)); + pushw(ssp, sp, LWORD(esi)); + pushw(ssp, sp, LWORD(edi)); + LWORD(esp) -= 16; + pushw(ssp, sp, SREG(ds)); + pushw(ssp, sp, SREG(es)); + LWORD(esp) -= 4; +} + +void fake_retf(void) +{ + unsigned int ssp, sp; + + ssp = SEGOFF2LINEAR(SREG(ss), 0); + sp = LWORD(esp); + + _IP = popw(ssp, sp); + _CS = popw(ssp, sp); + _SP += 4; +} + +void fake_iret(void) +{ + unsigned int ssp, sp; +#ifdef USE_MHPDBG + int old_tf = isset_TF(); +#endif + + ssp = SEGOFF2LINEAR(SREG(ss), 0); + sp = LWORD(esp); + + _IP = popw(ssp, sp); + _CS = popw(ssp, sp); + set_FLAGS(popw(ssp, sp)); + _SP += 6; +#ifdef USE_MHPDBG + if (mhpdbg.active && old_tf) + set_TF(); +#endif +} + +void do_eoi_iret(void) +{ + _CS = BIOSSEG; + _IP = EOI_OFF; +} + +void do_eoi2_iret(void) +{ + _CS = BIOSSEG; + _IP = EOI2_OFF; +} + +void do_iret(void) +{ + _CS = BIOSSEG; + _IP = IRET_OFF; +} + +static void rvc_int_pre(int tid, void *arg, void *arg2) +{ + coopth_push_user_data(tid, (void *) (long) get_FLAGS(REG(eflags))); + clear_TF(); + clear_NT(); +#if 0 + /* AC already cleared in do_int() */ + if (IS_CR0_AM_SET()) + clear_AC(); +#endif + clear_IF(); +} + +static void rvc_int_post(int tid, void *arg, void *arg2) +{ + u_short flgs = (long) coopth_pop_user_data(tid); + if (flgs & IF) + set_IF(); + else + clear_IF(); + REG(eflags) |= (flgs & (TF_MASK | NT_MASK)); +} + +/* dummy sleep handler for sanity checks */ +static void rvc_int_sleep(int tid, int sl_state) +{ + /* new revect code: all CHAIN_REVECT handlers share the same + * coopth tid. This means that deep sleeps should be forbidden + * because we can't awake thread on the same tid. */ + assert(sl_state != COOPTH_SL_SLEEP); +} + +#define INT_WRP(n) \ +static int _int##n##_(int stk_offs, int revect) \ +{ \ + return int##n(); \ +} + +/* Needed for int16, which can clash with type name. + * Under clang, both _int16 and __int16 are also occupied. */ +#define INT_WRP2(n) \ +static int _int##n##_(int stk_offs, int revect) \ +{ \ + return ___int##n(); \ +} + +INT_WRP(05) +INT_WRP(10) +INT_WRP(11) +INT_WRP(12) +INT_WRP(13) +INT_WRP(14) +INT_WRP(15) +INT_WRP2(16) +INT_WRP(17) +INT_WRP(18) +INT_WRP(19) +INT_WRP(1a) +INT_WRP(28) +INT_WRP(29) +INT_WRP(33) +INT_WRP(66) +INT_WRP(67) + +static int _ipx_int7a(int stk_offs, int revect) +{ +#ifdef IPX + return ipx_int7a(); +#else + return 0; +#endif +} + +static void revect_setup(void) +{ + int i; + + memset(&vm86s.int_revectored, 0x00, sizeof(vm86s.int_revectored)); + if (config.force_revect != 0) { + for (i = 0; i < 0x100; i++) { + if (int_handlers[i].interrupt_function[REVECT]) + set_bit(i, &vm86s.int_revectored); + } + } + + int21_hooked = 0; + int28_hooked = 0; + int2f_hooked = 0; + int33_hooked = 0; + int_revect_post_init(); +} + +/* + * DANG_BEGIN_FUNCTION setup_interrupts + * + * description: + * SETUP_INTERRUPTS is used to initialize the interrupt_function + * array which directs handling of interrupts in protected mode and + * also initializes the base vector for interrupts in real mode. + * + * DANG_END_FUNCTION + */ + +void setup_interrupts(void) +{ + int i; + emu_hlt_t hlt_hdlr = HLT_INITIALIZER; + +#define SIFU(n, r, h) do { \ + int_handlers[n].interrupt_function_arr[NO_REVECT][r] = h; \ + int_handlers[n].interrupt_function_arr[REVECT][r] = h; \ +} while (0) +#define SI2FU(n, h) do { \ + int_handlers[n].interrupt_function_arr[NO_REVECT][NO_REVECT] = h; \ + int_handlers[n].interrupt_function_arr[REVECT][REVECT] = h; \ +} while (0) + /* init trapped interrupts called via jump */ + for (i = 0; i < 256; i++) { + SIFU(i, NO_REVECT, NULL); + SIFU(i, REVECT, NULL); + } + + SIFU(5, NO_REVECT, _int05_); + /* This is called only when revectoring int10 */ + SIFU(0x10, NO_REVECT, _int10_); + SIFU(0x11, NO_REVECT, _int11_); + SIFU(0x12, NO_REVECT, _int12_); + SIFU(0x13, NO_REVECT, _int13_); + SIFU(0x14, NO_REVECT, _int14_); + SIFU(0x15, NO_REVECT, _int15_); + SIFU(0x16, NO_REVECT, _int16_); + SIFU(0x17, NO_REVECT, _int17_); + SIFU(0x18, NO_REVECT, _int18_); + SIFU(0x19, NO_REVECT, _int19_); + SIFU(0x1a, NO_REVECT, _int1a_); + SIFU(0x67, NO_REVECT, _int67_); + + int_handlers[0x21].revect_function = int21_revect; + SIFU(0x21, REVECT, msdos_chainrevect); + int_handlers[0x21].secrevect_function = msdos_xtra; + int_handlers[0x21].interrupt_function_arr[NO_REVECT][NO_REVECT] = + msdos_xtra_norev; + int_handlers[0x21].unrevect_function = int21_unrevect; + SI2FU(0x28, _int28_); + int_handlers[0x28].revect_function = int28_revect; + int_handlers[0x28].unrevect_function = int28_unrevect; + SIFU(0x29, NO_REVECT, _int29_); + int_handlers[0x2f].revect_function = int2f_revect; + SI2FU(0x2f, int2f); + int_handlers[0x2f].unrevect_function = int2f_unrevect; + if (config.mouse.intdrv) { + /* only for unrevect_fixup, otherwise unneeded revect */ + int_handlers[0x33].revect_function = int33_revect; + SIFU(0x33, REVECT, _int33_); + int_handlers[0x33].unrevect_function = int33_unrevect_fixup; + } + if (config.ipxsup) + SIFU(0x7a, NO_REVECT, _ipx_int7a); + SIFU(DOS_HELPER_INT, NO_REVECT, dos_helper); + + /* set up relocated video handler (interrupt 0x42) */ + if (config.dualmon == 2) { + int_handlers[0x42] = int_handlers[0x10]; + } + + hlt_hdlr.name = "interrupts"; + hlt_hdlr.len = 256; + hlt_hdlr.func = do_int_from_hlt; + hlt_off = hlt_register_handler_vm86(hlt_hdlr); + + int_tid = coopth_create_multi("ints thread non-revect", 256, + do_int_from_thr); + coopth_set_permanent_post_handler(int_tid, ret_from_int); + int_rvc_tid = coopth_create("ints thread revect", do_basic_revect_thr); + coopth_set_ctx_handlers(int_rvc_tid, rvc_int_pre, rvc_int_post, NULL); + coopth_set_sleep_handlers(int_rvc_tid, rvc_int_sleep, NULL); + + int_revect_init(); +} + +void int_try_disable_revect(void) +{ + int i; + + if (config.force_revect != -1) + return; + config.force_revect = 0; + for (i = 0; i < 256; i++) { + if (test_bit(i, &vm86s.int_revectored) && !mhp_revectored(i)) + clear_bit(i, &vm86s.int_revectored); + } +} + +void update_xtitle(void) +{ + char cmdname[9]; + char *cmd_ptr, *tmp_ptr; + u_short psp_seg; + struct MCB *mcb; + int force_update; + + if (!sda) + return; + psp_seg = sda_cur_psp(sda); + if (!psp_seg) + return; + mcb = (struct MCB *) SEG2UNIX(psp_seg - 1); + if (!mcb) + return; + force_update = !title_hint[0]; + + MEMCPY_P2UNIX(cmdname, (unsigned char *) mcb->name, 8); + cmdname[8] = 0; + cmd_ptr = tmp_ptr = cmdname + strspn(cmdname, " \t"); + while (*tmp_ptr) { /* Check whether the name is valid */ + if (iscntrlDOS(*tmp_ptr++)) + return; + } + + if (win3x_mode != INACTIVE && memcmp(cmd_ptr, "krnl", 4) == 0) { + cmd_ptr = win3x_title; + force_update = 1; + } + + if (force_update || strcmp(title_current, title_hint) != 0) { + if (force_update || strcmp(cmd_ptr, title_hint) == 0) { + if (force_update || can_change_title) { + if (strcmp(title_current, cmd_ptr) == 0) + return; + strlcpy(title_current, cmd_ptr, sizeof(title_current)); + change_window_title(title_current); + } + } else { + can_change_title = 1; + } + } +} + +static int msdos_remap_extended_open_(void) +{ + uint16_t _si_ = LWORD(esi); + uint8_t _bl_ = LO(bx); + uint8_t _dl_ = LO(dx); + char *src = MK_FP32(SREG(ds), _si_); + int found, create_truncate; + + ds_printf("INT21: extended open '%s'\n", src); + + /* Get file attributes */ + LWORD(eax) = 0x4300; + LWORD(edx) = _si_; // Filename passed in DS:DX + call_msdos(); + if ((REG(eflags) & CF) && LWORD(eax) != 0x02) + return 0; + found = !(REG(eflags) & CF); + + ds_printf("INT21: extended open file does%s exist\n", found ? "" : " not"); + ds_printf("INT21: extended open _dl == 0x%02x\n", _dl_); + + /* + * Bitfields for action: + * Bit(s) Description ) + * 7-4 action if file does not exist + * 0000 fail + * 0001 create + * 3-0 action if file exists + * 0000 fail + * 0001 open + * 0010 replace/open + */ + if (!found) { + if (!(_dl_ & 0b00010000)) { // fail if doesn't exist + LWORD(eax) = 0x02; + return 0; + } + } else { + if (!(_dl_ & 0b00000011)) { // fail if exists + LWORD(eax) = 0x50; + return 0; + } + } + + create_truncate = (_dl_ & 0b00010010); + + /* Choose Create/Truncate or Open function */ + HI(ax) = create_truncate ? 0x3c : 0x3d; + + /* Map the extended open into something the underlying DOS can understand */ + /* + low byte of BX (only bit 2 lost) + bit 2 read-only no update access time + + high byte of BX (all bits lost) + bit 8 disable buffering + bit 9 disable file compression + bit 10 use _DI value for file name mangling + bit 13 return error instead of int 24 + bit 14 synchronous writes + */ + LO(ax) = (_bl_ & 0b11110011); + + /* Filename passed in DS:DX */ + LWORD(edx) = _si_; + + call_msdos(); + + if (REG(eflags) & CF) // we failed + return 0; + + /* Tell the caller what action was taken + (According to RBIL these values are lost when open uses redirector) + 0x0001 - file opened + 0x0002 - file created + 0x0003 - file truncated + */ + if (!create_truncate) + LWORD(ecx) = 0x0001; + else + LWORD(ecx) = !found ? 0x0002 : 0x0003; + + return 1; +} + +static int msdos_remap_extended_open(void) +{ + int carry, ret; + + carry = (REG(eflags) & CF); + ret = msdos_remap_extended_open_(); + /* preserve carry if we forward the request */ + if (ret == 0 && carry) + CARRY; + return ret; +} + +far_t get_int_vector(int vec) +{ + far_t addr; + + if (test_bit(vec, &vm86s.int_revectored)) { + addr.segment = INT_RVC_SEG; + switch (vec) { + case 0x21: + int21_rvc_setup(); + addr.offset = INT_RVC_21_OFF; + return addr; + case 0x2f: + int2f_rvc_setup(); + addr.offset = INT_RVC_2f_OFF; + return addr; + case 0x33: + int33_rvc_setup(); + addr.offset = INT_RVC_33_OFF; + return addr; + } + } + addr.segment = ISEG(vec); + addr.offset = IOFF(vec); + return addr; +} diff --git a/src/base/core/lowmem.c b/src/base/core/lowmem.c new file mode 100644 index 0000000..73d031f --- /dev/null +++ b/src/base/core/lowmem.c @@ -0,0 +1,178 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Author: Stas Sergeev + * + * Management for the static 32K heap in a low memory. + * Used by various dosemu internal subsystems. + */ + +#include +#include +#include "emu.h" +#include "memory.h" +#include "smalloc.h" +#include "utilities.h" +#include "emudpmi.h" +#include "lowmem.h" + +static smpool mp; +unsigned char *dosemu_lmheap_base; +static void *rm_stack; +#define RM_STACK_SIZE 0x200 + +static void do_sm_error(int prio, const char *fmt, ...) +{ + char buf[1024]; + va_list al; + + va_start(al, fmt); + vsnprintf(buf, sizeof(buf), fmt, al); + va_end(al); + if (prio) + dosemu_error("%s\n", buf); + else + dbug_printf("%s\n", buf); +} + +int lowmem_init(void) +{ + dosemu_lmheap_base = MK_FP32(DOSEMU_LMHEAP_SEG, DOSEMU_LMHEAP_OFF); + sminit(&mp, dosemu_lmheap_base, DOSEMU_LMHEAP_SIZE); + smregister_error_notifier(&mp, do_sm_error); + return 1; +} + +void * lowmem_alloc(int size) +{ + char *ptr = smalloc(&mp, size); + if (!ptr) { + error("lowmem_heap: OOM, size=%i\n", size); + leavedos(86); + } + return ptr; +} + +void * lowmem_alloc_aligned(int align, int size) +{ + char *ptr = smalloc_aligned(&mp, align, size); + if (!ptr) { + error("lowmem_heap: OOM, size=%i\n", size); + leavedos(86); + } + return ptr; +} + +void lowmem_free(void *p) +{ + smfree(&mp, p); +} + +void lowmem_reset(void) +{ + lowmem_free(rm_stack); + smfree_all(&mp); + rm_stack = lowmem_alloc(RM_STACK_SIZE); +} + +static int in_rm_stack; +static uint16_t rm_sp; +#define MAX_RM_STACKS 10 +static uint64_t userval[MAX_RM_STACKS]; + +int get_rm_stack(Bit16u *ss_p, Bit16u *sp_p, uint64_t cookie) +{ + int ret = 0; + + assert(in_rm_stack < MAX_RM_STACKS); + userval[in_rm_stack] = cookie; + if (!(in_rm_stack++)) { + rm_sp = DOSEMU_LMHEAP_OFFS_OF(rm_stack) + RM_STACK_SIZE; + *ss_p = DOSEMU_LMHEAP_SEG; + *sp_p = rm_sp; + ret = 1; + } + + return ret; +} + +uint16_t put_rm_stack(uint64_t *cookie) +{ + int ret = 0; + + assert(in_rm_stack > 0); + if (!(--in_rm_stack)) { + ret = rm_sp; + } + if (cookie) + *cookie = userval[in_rm_stack]; + return ret; +} + +/* recursion is _very_unlikely, but define an array */ +#define MAX_SAVED_REGS 5 +static struct vm86_regs rm_regs_stack[MAX_SAVED_REGS]; + +static void switch_stack(struct vm86_regs *regs) +{ + Bit16u new_ss, new_sp; + int stk; + stk = get_rm_stack(&new_ss, &new_sp, 0); + if (stk) { + regs->ss = new_ss; + regs->esp = new_sp; + } +} + +void get_rm_stack_regs(struct vm86_regs *regs, struct vm86_regs *saved_regs) +{ + *saved_regs = REGS; + switch_stack(regs); +} + +void rm_stack_enter(void) +{ + assert(in_rm_stack < MAX_SAVED_REGS); + get_rm_stack_regs(®S, &rm_regs_stack[in_rm_stack]); +} + +void rm_stack_leave(void) +{ + int old_tf = isset_TF(); + put_rm_stack(NULL); + REGS = rm_regs_stack[in_rm_stack]; + if (old_tf) + set_TF(); +} + +#define LMHEAP_OFF 0xa000 +#define LMHEAP_SIZE 0x4000 + +static uint16_t lmheap_add(void) +{ + return (config.dos_up == 2 ? FDPP_LMHEAP_ADD : 0); +} + +uint16_t lmheap_off(void) +{ + return LMHEAP_OFF + lmheap_add(); +} + +uint16_t lmheap_size(void) +{ + return LMHEAP_SIZE - lmheap_add(); +} diff --git a/src/base/core/ports.c b/src/base/core/ports.c new file mode 100644 index 0000000..f7054c2 --- /dev/null +++ b/src/base/core/ports.c @@ -0,0 +1,1263 @@ +/* + * SIDOC_BEGIN_MODULE + * + * Description: New port handling code for DOSEMU + * + * Maintainers: Alberto Vignani (vignani@mail.tin.it) + * + * REMARK + * This is the code that allows and disallows port access within DOSEMU. + * The BOCHS port IO code was actually very cleverly done. So the idea + * was stolen from there. + * + * This port I/O code (previously in portss.c, from Scott Bucholz) is based on + * a table access instead of a switch statement. This method is much more + * clean and easy to maintain, while not slower than a switch. + * + * Remains of the old code are emerging here and there, they will + * hopefully be moved back to where they belong, mainly video code. + * /REMARK + * + * SIDOC_END_MODULE + * + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_IO_H +#include +#endif +#include +#include + +#include "emu.h" +#include "port.h" +#include "timers.h" +#include "video.h" +#include "vgaemu.h" /* for video retrace */ +#include "bios.h" +#include "serial.h" +#include "bitops.h" +#include "mapping.h" +#include "dosemu_config.h" +#include "sig.h" +#ifdef X86_EMULATOR +#include "cpu-emu.h" +#include "bitops.h" +#endif + +_port_handler port_handler[EMU_MAX_IO_DEVICES]; +unsigned char port_handle_table[0x10000]; +unsigned char port_andmask[0x10000]; +unsigned char port_ormask[0x10000]; +static unsigned char portfast_map[0x10000/8]; +unsigned char emu_io_bitmap[0x10000/8]; +static pid_t portserver_pid = 0; + +static unsigned char port_handles; /* number of io_handler's */ + +int in_crit_section = 0; +static const char *crit_sect_caller; + +#define SET_HANDLE(p,h) port_handle_table[(Bit16u)(p)]=(h) +#define EMU_HANDLER(port) port_handler[port_handle_table[(Bit16u)(port)]] +enum{TYPE_INB, TYPE_OUTB, TYPE_INW, TYPE_OUTW, TYPE_IND, TYPE_OUTD, TYPE_PCI, TYPE_EXIT}; + +/* ---------------------------------------------------------------------- */ +/* PORT TRACING */ + +#if 0 +static long nyb2bin[16] = +{ + 0x30303030, 0x31303030, 0x30313030, 0x31313030, + 0x30303130, 0x31303130, 0x30313130, 0x31313130, + 0x30303031, 0x31303031, 0x30313031, 0x31313031, + 0x30303131, 0x31303131, 0x30313131, 0x31313131 +}; + +static char * + p2bin(unsigned char c) +{ + static char s[16] = " [00000000]"; + + ((uint32_t *) s)[1] = nyb2bin[(c >> 4) & 15]; + ((uint32_t *) s)[2] = nyb2bin[c & 15]; + + return s + 3; +} +#endif + +#define PORTLOG_MAXBITS 16 +#define PORTLOG_MASK ((1 << PORTLOG_MAXBITS) - 1) +#define SIZE_PORTLOGMAP (1 << (PORTLOG_MAXBITS -3)) +static unsigned long *portlog_map = 0; + +void register_port_traceing(ioport_t firstport, ioport_t lastport) +{ + firstport &= PORTLOG_MASK; + lastport &= PORTLOG_MASK; + if (lastport < firstport) return; + init_port_traceing(); + T_printf ("PORT: traceing 0x%x-0x%x\n",firstport,lastport); + for (; firstport <= lastport; firstport++) { + set_bit(firstport, portlog_map); + } +} + +void clear_port_traceing(void) +{ + if (!portlog_map) portlog_map = malloc(SIZE_PORTLOGMAP); + memset(portlog_map, 0, SIZE_PORTLOGMAP); +} + +void init_port_traceing(void) +{ + if (portlog_map) return; + clear_port_traceing(); +} + +#define TT_printf(p,f,v,m) ({ \ + if (debug_level('T') && (test_bit(p, portlog_map) || debug_level('T') >= 5)) { \ + log_printf(1, "%hx %c %x\n", (unsigned short)p, f, v & m); \ + } \ +}) + +static Bit8u log_port_read(ioport_t port, Bit8u r) +{ + TT_printf(port, '>', r, 0xff); + return r; +} + +static Bit16u log_port_read_w(ioport_t port, Bit16u r) +{ + TT_printf(port, '}', r, 0xffff); + return r; +} + +static Bit32u log_port_read_d(ioport_t port, Bit32u r) +{ + TT_printf(port, ']', r, 0xffffffff); + return r; +} + +static void log_port_write(ioport_t port, Bit8u w) +{ + TT_printf(port, '<', w, 0xff); +} + +static void log_port_write_w(ioport_t port, Bit16u w) +{ + TT_printf(port, '{', w, 0xffff); +} + +static void log_port_write_d(ioport_t port, Bit32u w) +{ + TT_printf(port, '[', w, 0xffffffff); +} + +#define LOG_PORT_READ(port, r) (debug_level('T') ? log_port_read(port, r) : r) +#define LOG_PORT_READ_W(port, r) (debug_level('T') ? log_port_read_w(port, r) : r) +#define LOG_PORT_READ_D(port, r) (debug_level('T') ? log_port_read_d(port, r) : r) + +#define LOG_PORT_WRITE(port, w) do{ if (debug_level('T')) log_port_write(port, w); }while(0) +#define LOG_PORT_WRITE_W(port, w) do{ if (debug_level('T')) log_port_write_w(port, w); }while(0) +#define LOG_PORT_WRITE_D(port, w) do{ if (debug_level('T')) log_port_write_d(port, w); }while(0) + +/* ---------------------------------------------------------------------- */ +/* SIDOC_BEGIN_REMARK + * + * The following port_{in|out}{bwd} functions are the main entry points to + * the port code. They look into the port_handle_table and call the + * appropriate code, usually the std_port_ functions, but each device is + * free to register its own functions which in turn will call std_port or + * directly access I/O (like video code does), or emulate it - AV + * + * SIDOC_END_REMARK + */ +/* + * SIDOC_BEGIN_FUNCTION port_inb(ioport_t port) + * + * Handles/simulates an inb() port IO read + * + * SIDOC_END_FUNCTION + */ +Bit8u port_inb(ioport_t port) +{ + Bit8u res; + res = EMU_HANDLER(port).read_portb(port, EMU_HANDLER(port).arg); + return LOG_PORT_READ(port, res); +} + +/* + * SIDOC_BEGIN_FUNCTION port_outb(ioport_t port, Bit8u byte) + * + * Handles/simulates an outb() port IO write + * + * SIDOC_END_FUNCTION + */ +void port_outb(ioport_t port, Bit8u byte) +{ + LOG_PORT_WRITE(port, byte); + EMU_HANDLER(port).write_portb(port, byte, EMU_HANDLER(port).arg); +} + +/* + * SIDOC_BEGIN_FUNCTION port_inw(ioport_t port) + * + * Handles/simulates an inw() port IO read. Usually this invokes + * port_inb() twice, but it may be necessary to do full word i/o for + * some video boards. + * + * SIDOC_END_FUNCTION + */ +Bit16u port_inw(ioport_t port) +{ + Bit16u res; + + if (EMU_HANDLER(port).read_portw != NULL && + EMU_HANDLER(port).read_portb == EMU_HANDLER(port + 1).read_portb + ) { + res = EMU_HANDLER(port).read_portw(port, EMU_HANDLER(port).arg); + return LOG_PORT_READ_W(port, res); + } + else { + res = (Bit16u) port_inb(port) | (((Bit16u) port_inb(port + 1)) << 8); + } + return res; +} + +/* + * SIDOC_BEGIN_FUNCTION port_outw(ioport_t port, Bit16u word) + * + * Handles/simulates an outw() port IO write + * + * SIDOC_END_FUNCTION + */ +void port_outw(ioport_t port, Bit16u word) +{ + if (EMU_HANDLER(port).write_portw != NULL && + EMU_HANDLER(port).write_portb == EMU_HANDLER(port + 1).write_portb + ) { + LOG_PORT_WRITE_W(port, word); + EMU_HANDLER(port).write_portw(port, word, EMU_HANDLER(port).arg); + } + else { + port_outb(port, word & 0xff); + port_outb(port+1, (word >> 8) & 0xff); + } +} + +/* + * SIDOC_BEGIN_FUNCTION port_ind(ioport_t port) + * SIDOC_BEGIN_FUNCTION port_outd(ioport_t port, Bit32u dword) + * + * Handles/simulates an ind()/outd() port IO read/write. + * + * SIDOC_END_FUNCTION + */ +Bit32u port_ind(ioport_t port) +{ + Bit32u res; + + if (EMU_HANDLER(port).read_portd != NULL && + EMU_HANDLER(port).read_portb == EMU_HANDLER(port + 1).read_portb && + EMU_HANDLER(port).read_portb == EMU_HANDLER(port + 2).read_portb && + EMU_HANDLER(port).read_portb == EMU_HANDLER(port + 3).read_portb + ) { + res = EMU_HANDLER(port).read_portd(port, EMU_HANDLER(port).arg); + } + else { + res = (Bit32u) port_inw(port) | (((Bit32u) port_inw(port + 2)) << 16); + } + return LOG_PORT_READ_D(port, res); +} + +void port_outd(ioport_t port, Bit32u dword) +{ + LOG_PORT_WRITE_D(port, dword); + if (EMU_HANDLER(port).write_portd != NULL && + EMU_HANDLER(port).write_portb == EMU_HANDLER(port + 1).write_portb && + EMU_HANDLER(port).write_portb == EMU_HANDLER(port + 2).write_portb && + EMU_HANDLER(port).write_portb == EMU_HANDLER(port + 3).write_portb + ) { + EMU_HANDLER(port).write_portd(port, dword, EMU_HANDLER(port).arg); + } + else { + port_outw(port, dword & 0xffff); + port_outw(port+2, (dword >> 16) & 0xffff); + } +} + + +/* ---------------------------------------------------------------------- */ +/* the following functions are all static! */ + +static void pna_emsg(ioport_t port, char ch, const char *s) +{ + i_printf("PORT%c: %x not available for %s\n", ch, port, s); +} + +static void check_crit_section(ioport_t port, const char *function) +{ + if (in_crit_section) { + error("Port %#x is not available (%s), \"%s\" failed.\n" + "Adjust your dosemu.conf\n", + port, function, crit_sect_caller); + in_crit_section = 0; + leavedos(46); + } +} + +static Bit8u port_not_avail_inb(ioport_t port, void *arg) +{ +/* it is a fact of (hardware) life that unused locations return all + (or almost all) the bits at 1; some software can try to detect a + card basing on this fact and fail if it reads 0x00 - AV + + The joystick code is dependent on 0xff as joystick.c:r1.4 + (2005-04-08) stopped registering port handlers if no joystick + is initialised - Clarence Dang + + Also used for delays, so add some sleep. - stsp +*/ + if (debug_level('i')) pna_emsg(port,'b',"read"); +// idle(0, 50, 0, "inb"); + return 0xff; +} + +static void port_not_avail_outb(ioport_t port, Bit8u byte, void *arg) +{ + check_crit_section(port, "outb"); + if (debug_level('i')) pna_emsg(port,'b',"write"); +} + +static Bit16u port_not_avail_inw(ioport_t port, void *arg) +{ + if (debug_level('i')) pna_emsg(port,'w',"read"); +// idle(0, 50, 0, "inw"); + return 0xffff; +} + +static void port_not_avail_outw(ioport_t port, Bit16u value, void *arg) +{ + check_crit_section(port, "outw"); + if (debug_level('i')) pna_emsg(port,'w',"write"); +} + +static Bit32u port_not_avail_ind(ioport_t port, void *arg) +{ + if (debug_level('i')) pna_emsg(port,'d',"read"); +// idle(0, 50, 0, "ind"); + return 0xffffffff; +} + +static void port_not_avail_outd(ioport_t port, Bit32u value, void *arg) +{ + check_crit_section(port, "outd"); + if (debug_level('i')) pna_emsg(port,'d',"write"); +} + + +/* ---------------------------------------------------------------------- */ +/* default port I/O access + */ + +struct portreq +{ + ioport_t port; + int type; + unsigned long word; +}; + +static int port_fd_out[2] = {-1, -1}; +static int port_fd_in[2] = {-1, -1}; + +Bit8u std_port_inb(ioport_t port) +{ + struct portreq pr; + + if (current_iopl == 3 || test_bit(port, emu_io_bitmap)) { + return port_real_inb(port); + } + if (!portserver_pid) { + error ("std_port_inb(0x%X): port server unavailable\n", port); + return port_not_avail_inb (port, NULL); + } + pr.port = port; + pr.type = TYPE_INB; + write(port_fd_out[1], &pr, sizeof(pr)); + read(port_fd_in[0], &pr, sizeof(pr)); + return pr.word; +} + +static Bit8u std_port_inb_h(ioport_t port, void *arg) +{ + return std_port_inb(port); +} + +void std_port_outb(ioport_t port, Bit8u byte) +{ + struct portreq pr; + + if (current_iopl == 3 || test_bit(port, emu_io_bitmap)) { + port_real_outb(port, byte); + return; + } + if (!portserver_pid) { + error ("std_port_outb(0x%X,0x%X): port server unavailable\n", + port, byte); + port_not_avail_outb (port, byte, NULL); + return; + } + pr.word = byte; + pr.port = port; + pr.type = TYPE_OUTB; + write(port_fd_out[1], &pr, sizeof(pr)); + read(port_fd_in[0], &pr, sizeof(pr)); +} + +static void std_port_outb_h(ioport_t port, Bit8u byte, void *arg) +{ + std_port_outb(port, byte); +} + +Bit16u std_port_inw(ioport_t port) +{ + struct portreq pr; + + if (current_iopl == 3 || (test_bit(port, emu_io_bitmap) + + test_bit(port + 1, emu_io_bitmap) + == 2)) { + return port_real_inw(port); + } + if (!portserver_pid) { + error ("std_port_inw(0x%X): port server unavailable\n", port); + return port_not_avail_inw (port, NULL); + } + pr.port = port; + pr.type = TYPE_INW; + write(port_fd_out[1], &pr, sizeof(pr)); + read(port_fd_in[0], &pr, sizeof(pr)); + return pr.word; +} + +static Bit16u std_port_inw_h(ioport_t port, void *arg) +{ + return std_port_inw(port); +} + +void std_port_outw(ioport_t port, Bit16u word) +{ + struct portreq pr; + + if (current_iopl == 3 || (test_bit(port, emu_io_bitmap) + + test_bit(port + 1, emu_io_bitmap) + == 2)) { + port_real_outw(port, word); + return; + } + if (!portserver_pid) { + error ("std_port_outw(0x%X,0x%X): port server unavailable\n", + port, word); + port_not_avail_outw (port, word, NULL); + return; + } + pr.word = word; + pr.port = port; + pr.type = TYPE_OUTW; + write(port_fd_out[1], &pr, sizeof(pr)); + read(port_fd_in[0], &pr, sizeof(pr)); +} + +static void std_port_outw_h(ioport_t port, Bit16u word, void *arg) +{ + std_port_outw(port, word); +} + +Bit32u std_port_ind(ioport_t port) +{ + struct portreq pr; + + if (current_iopl == 3 || (test_bit(port, emu_io_bitmap) + + test_bit(port + 1, emu_io_bitmap) + + test_bit(port + 2, emu_io_bitmap) + + test_bit(port + 3, emu_io_bitmap) + == 4)) { + return port_real_ind(port); + } + if (!portserver_pid) { + error ("std_port_ind(0x%X): port server unavailable\n", port); + return port_not_avail_ind (port, NULL); + } + pr.port = port; + pr.type = TYPE_IND; + write(port_fd_out[1], &pr, sizeof(pr)); + read(port_fd_in[0], &pr, sizeof(pr)); + return pr.word; +} + +static Bit32u std_port_ind_h(ioport_t port, void *arg) +{ + return std_port_ind(port); +} + +static int do_port_outd(ioport_t port, Bit32u dword, int pci) +{ + struct portreq pr; + + if (current_iopl == 3 || (test_bit(port, emu_io_bitmap) + + test_bit(port + 1, emu_io_bitmap) + + test_bit(port + 2, emu_io_bitmap) + + test_bit(port + 3, emu_io_bitmap) + == 4)) { + port_real_outd(port, dword); + return 0; + } + if (!portserver_pid) { + error ("std_port_outd(0x%X,0x%X): port server unavailable\n", + port, dword); + port_not_avail_outd (port, dword, NULL); + return 0; + } + pr.word = dword; + pr.port = port; + pr.type = pci ? TYPE_PCI : TYPE_OUTD; + write(port_fd_out[1], &pr, sizeof(pr)); + return 1; +} + +void std_port_outd(ioport_t port, Bit32u dword) +{ + struct portreq pr; + if (do_port_outd(port, dword, 0)) + read(port_fd_in[0], &pr, sizeof(pr)); +} + +static void std_port_outd_h(ioport_t port, Bit32u dword, void *arg) +{ + std_port_outd(port, dword); +} + +void pci_port_outd(ioport_t port, Bit32u dword) +{ + do_port_outd(port, dword, 1); +} + +/* ---------------------------------------------------------------------- */ +/* SIDOC_BEGIN_REMARK + * + * optimized versions for rep - basically we avoid changing privileges + * and iopl on and off lots of times. We are safe letting iopl=3 here + * since we don't exit from this code until finished. + * This code is shared between VM86 and DPMI. + * + * SIDOC_END_REMARK + */ + +int port_rep_inb(ioport_t port, Bit8u *base, int df, Bit32u count) +{ + register int incr = df? -1: 1; + Bit8u *dest = base; + int count_ = count; + + if (count==0) return 0; + i_printf("Doing REP insb(%#x) %d bytes at %p, DF %d\n", port, + count, base, df); + while (count--) { + *dest = port_inb(port); + dest += incr; + } + if (debug_level('T')) { + dest = base; + while (count_--) { + (void)LOG_PORT_READ(port, *dest); + dest += incr; + } + } + return dest-base; +} + +int port_rep_outb(ioport_t port, Bit8u *base, int df, Bit32u count) +{ + register int incr = df? -1: 1; + Bit8u *dest = base; + int count_ = count; + + if (count==0) return 0; + i_printf("Doing REP outsb(%#x) %d bytes at %p, DF %d\n", port, + count, base, df); + while (count--) { + port_outb(port, *dest); + dest += incr; + } + if (debug_level('T')) { + dest = base; + while (count_--) { + LOG_PORT_WRITE(port, *dest); + dest += incr; + } + } + return dest-base; +} + +int port_rep_inw(ioport_t port, Bit16u *base, int df, Bit32u count) +{ + register int incr = df? -1: 1; + Bit16u *dest = base; + int count_ = count; + + if (count==0) return 0; + i_printf("Doing REP insw(%#x) %d words at %p, DF %d\n", port, + count, base, df); + if (EMU_HANDLER(port).read_portw == NULL) { + Bit16u res; + while (count--) { + res = port_inb(port); + *dest = ((Bit16u)port_inb(port+1) <<8) | res; + dest += incr; + } + } + else { + while (count--) { + *dest = port_inw(port); + dest += incr; + } + } + if (debug_level('T')) { + dest = base; + while (count_--) { + (void)LOG_PORT_READ_W(port, *dest); + dest += incr; + } + } + return (Bit8u *)dest-(Bit8u *)base; +} + +int port_rep_outw(ioport_t port, Bit16u *base, int df, Bit32u count) +{ + register int incr = df? -1: 1; + Bit16u *dest = base; + int count_ = count; + + if (count==0) return 0; + i_printf("Doing REP outsw(%#x) %d words at %p, DF %d\n", port, + count, base, df); + if (EMU_HANDLER(port).write_portw == NULL) { + Bit16u res; + while (count--) { + res = *dest, dest += incr; + port_outb(port, res); + port_outb(port+1, res>>8); + } + } + else { + while (count--) { + port_outw(port, *dest); + dest += incr; + } + } + if (debug_level('T')) { + dest = base; + while (count_--) { + LOG_PORT_WRITE_W(port, *dest); + dest += incr; + } + } + return (Bit8u *)dest-(Bit8u *)base; +} + +int port_rep_ind(ioport_t port, Bit32u *base, int df, Bit32u count) +{ + register int incr = df? -1: 1; + Bit32u *dest = base; + + if (count==0) return 0; + while (count--) { + *dest = port_ind(port); + (void)LOG_PORT_READ_D(port, *dest); + dest += incr; + } + return (Bit8u *)dest-(Bit8u *)base; +} + +int port_rep_outd(ioport_t port, Bit32u *base, int df, Bit32u count) +{ + register int incr = df? -1: 1; + Bit32u *dest = base; + + if (count==0) return 0; + while (count--) { + port_outd(port, *dest); + LOG_PORT_WRITE_D(port, *dest); + dest += incr; + } + return (Bit8u *)dest-(Bit8u *)base; +} + +/* + * SIDOC_BEGIN_FUNCTION special_port_inb,special_port_outb + * + * I don't know what to do of this stuff... it was added incrementally to + * port.c and has mainly to do with video code. This is not the right + * place for it... + * Anyway, this implements some HGC stuff for X and the emuretrace + * port access for 0x3c0/0x3da + * + * SIDOC_END_FUNCTION + */ + +static int r3da_pending = 0; + +void do_r3da_pending (void) +{ + if (r3da_pending) { + (void)std_port_inb(r3da_pending); + r3da_pending = 0; + } +} + +static Bit8u special_port_inb(ioport_t port, void *arg) +{ + Bit8u res = 0xff; + + if (current_iopl == 3 || test_bit(port, emu_io_bitmap)) { + return port_real_inb(port); + } + if ((port==0x3ba)||(port==0x3da)) { + res = Misc_get_input_status_1(); + if (!r3da_pending && (config.emuretrace>1)) { + r3da_pending = port; + } + } + else + if (port==0x3db) /* light pen strobe reset */ + res = 0; + return res; +} + +static void special_port_outb(ioport_t port, Bit8u byte, void *arg) +{ + if (current_iopl == 3 || test_bit(port, emu_io_bitmap)) { + port_real_outb(port, byte); + return; + } + /* Port writes for enable/disable blinking character mode */ + if (port == 0x03c0) { + static int last_index = -1; + static int flip_flop = 1; + + /* SIDOC_BEGIN_REMARK + * + * This is the core of the new emuretrace algorithm: + * If a read of port 0x3da is performed we just set it + * as pending and set ioperm OFF for port 0x3c0 + * When a write to port 0x3c0 is then trapped, we perform + * any pending read to 0x3da and reset the ioperm for + * 0x3c0 in the default ON state. + * This way we avoid extra port accesses when the program + * is only looking for the sync bits, and we don't miss + * the case where the read to 0x3da is used to reset the + * index/data flipflop for port 0x3c0. Further accesses to + * port 0x3c0 are handled at full speed. + * + * SIDOC_END_REMARK + */ + if (config.vga && (config.emuretrace>1)) { + if (r3da_pending) { + (void)std_port_inb_h(r3da_pending, arg); + r3da_pending = 0; + std_port_outb_h(0x3c0, byte, arg); + return; + } + goto defout; + } + flip_flop = !flip_flop; + if (flip_flop) { + /* JES This was last_index = 0x10..... WRONG? */ + vga.attr.data[last_index] = byte; + } + else { + last_index = byte; + } + return; + } + +defout: + std_port_outb_h (port, byte, arg); +} + +/* ---------------------------------------------------------------------- */ +/* + * SIDOC_BEGIN_FUNCTION port_init() + * + * Resets all the port port_handler information. + * This must be called before parsing the config file - + * This must NOT be called again when warm booting! + * Can't use debug logging, it is called too early. + * + * SIDOC_END_FUNCTION + */ +int port_init(void) +{ + int i; + + /* set unused elements to appropriate values */ + for (i=0; i < EMU_MAX_IO_DEVICES; i++) { + port_handler[i].read_portb = NULL; + port_handler[i].write_portb = NULL; + port_handler[i].read_portw = NULL; + port_handler[i].write_portw = NULL; + port_handler[i].read_portd = NULL; + port_handler[i].write_portd = NULL; + } + + /* handle 0 maps to the unmapped IO device handler. Basically any + ports which don't map to any other device get mapped to this + handler which does absolutely nothing. + */ + port_handler[NO_HANDLE].read_portb = port_not_avail_inb; + port_handler[NO_HANDLE].write_portb = port_not_avail_outb; + port_handler[NO_HANDLE].read_portw = port_not_avail_inw; + port_handler[NO_HANDLE].write_portw = port_not_avail_outw; + port_handler[NO_HANDLE].read_portd = port_not_avail_ind; + port_handler[NO_HANDLE].write_portd = port_not_avail_outd; + port_handler[NO_HANDLE].handler_name = "unknown port"; + + /* the STD handles will be in use by many devices, and their fd + will always be -1 + */ + port_handler[HANDLE_STD_IO].read_portb = std_port_inb_h; + port_handler[HANDLE_STD_IO].write_portb = std_port_outb_h; + port_handler[HANDLE_STD_IO].read_portw = std_port_inw_h; + port_handler[HANDLE_STD_IO].write_portw = std_port_outw_h; + port_handler[HANDLE_STD_IO].read_portd = std_port_ind_h; + port_handler[HANDLE_STD_IO].write_portd = std_port_outd_h; + port_handler[HANDLE_STD_IO].handler_name = "std port io"; + + port_handler[HANDLE_STD_RD].read_portb = std_port_inb_h; + port_handler[HANDLE_STD_RD].write_portb = port_not_avail_outb; + port_handler[HANDLE_STD_RD].read_portw = std_port_inw_h; + port_handler[HANDLE_STD_RD].write_portw = port_not_avail_outw; + port_handler[HANDLE_STD_RD].read_portd = std_port_ind_h; + port_handler[HANDLE_STD_RD].write_portd = port_not_avail_outd; + port_handler[HANDLE_STD_RD].handler_name = "std port read"; + + port_handler[HANDLE_STD_WR].read_portb = port_not_avail_inb; + port_handler[HANDLE_STD_WR].write_portb = std_port_outb_h; + port_handler[HANDLE_STD_WR].read_portw = port_not_avail_inw; + port_handler[HANDLE_STD_WR].write_portw = std_port_outw_h; + port_handler[HANDLE_STD_WR].read_portd = port_not_avail_ind; + port_handler[HANDLE_STD_WR].write_portd = std_port_outd_h; + port_handler[HANDLE_STD_WR].handler_name = "std port write"; +#if 0 + port_handler[HANDLE_VID_IO].read_portb = video_port_in; + port_handler[HANDLE_VID_IO].write_portb = video_port_out; + port_handler[HANDLE_VID_IO].read_portw = NULL; + port_handler[HANDLE_VID_IO].write_portw = NULL; + port_handler[HANDLE_VID_IO].read_portd = NULL; + port_handler[HANDLE_VID_IO].write_portd = NULL; + port_handler[HANDLE_VID_IO].handler_name = "video port io"; +#else + port_handler[HANDLE_VID_IO].read_portb = std_port_inb_h; + port_handler[HANDLE_VID_IO].write_portb = std_port_outb_h; + port_handler[HANDLE_VID_IO].read_portw = std_port_inw_h; + port_handler[HANDLE_VID_IO].write_portw = std_port_outw_h; + port_handler[HANDLE_VID_IO].read_portd = std_port_ind_h; + port_handler[HANDLE_VID_IO].write_portd = std_port_outd_h; + port_handler[HANDLE_VID_IO].handler_name = "std port io"; +#endif + + port_handler[HANDLE_SPECIAL].read_portb = special_port_inb; + port_handler[HANDLE_SPECIAL].write_portb = special_port_outb; + port_handler[HANDLE_SPECIAL].read_portw = NULL; + port_handler[HANDLE_SPECIAL].write_portw = NULL; + port_handler[HANDLE_SPECIAL].read_portd = NULL; + port_handler[HANDLE_SPECIAL].write_portd = NULL; + port_handler[HANDLE_SPECIAL].handler_name = "extra stuff"; + + port_handles = STD_HANDLES; + + memset (port_handle_table, NO_HANDLE, sizeof(port_handle_table)); + memset (port_andmask, 0xff, sizeof(port_andmask)); + memset (port_ormask, 0, sizeof(port_ormask)); + + return port_handles; /* unused but useful */ +} + +static void portserver_exit(void *arg) +{ + error("port server terminated, exiting\n"); + leavedos(1); +} + +/* port server: this function runs in a separate process from the main + DOSEMU. This enables the main DOSEMU to drop root privileges. The + server can do that as well: by setting iopl(3). + Maybe this server should wrap DOSEMU rather than be forked from + it. +*/ +static void port_server(void) +{ + sigset_t set; + struct portreq pr; + _port_handler *ph, *ph1, *ph2, *ph3; + signal(SIGINT, SIG_DFL); + signal(SIGPIPE, SIG_DFL); + signal(SIGHUP, SIG_DFL); + signal(SIGTERM, SIG_DFL); + sigemptyset(&set); + sigaddset(&set, SIGINT); + sigaddset(&set, SIGPIPE); + sigaddset(&set, SIGHUP); + sigaddset(&set, SIGTERM); + sigprocmask(SIG_UNBLOCK, &set, NULL); + priv_iopl(3); + priv_drop(); + close(port_fd_in[0]); + close(port_fd_out[1]); + g_printf("server started\n"); + for (;;) { + read(port_fd_out[0], &pr, sizeof(pr)); + if (pr.type >= TYPE_EXIT) + _exit(0); + ph = &EMU_HANDLER(pr.port); + ph1 = &EMU_HANDLER(pr.port + 1); + ph2 = &EMU_HANDLER(pr.port + 2); + ph3 = &EMU_HANDLER(pr.port + 3); + if (pr.type == TYPE_PCI) { + /* get addr and data i/o access as close to each other + as possible, both to minimize possible races, and + for speed */ + struct portreq pr2; + read(port_fd_out[0], &pr2, sizeof(pr2)); + ph->write_portd(pr.port, pr.word, ph->arg); + pr = pr2; + } + switch (pr.type) { + case TYPE_INB: + pr.word = ph->read_portb(pr.port, ph->arg); + break; + case TYPE_OUTB: + ph->write_portb(pr.port, pr.word, ph->arg); + break; + case TYPE_INW: + if (ph->read_portb == ph1->read_portb) { + pr.word = ph->read_portw(pr.port, ph->arg); + } else { + i_printf("PORT: splitting inw(0x%x)\n", pr.port); + pr.word = ph->read_portb(pr.port, ph->arg) | + (ph1->read_portb(pr.port + 1, ph->arg) << 8); + } + break; + case TYPE_OUTW: + if (ph->write_portb == ph1->write_portb) { + ph->write_portw(pr.port, pr.word, ph->arg); + } else { + i_printf("PORT: splitting outw(0x%x)\n", pr.port); + ph->write_portb(pr.port, pr.word, ph->arg); + ph1->write_portb(pr.port + 1, pr.word >> 8, ph->arg); + } + break; + case TYPE_IND: + if (ph->read_portb == ph1->read_portb && + ph->read_portb == ph2->read_portb && + ph->read_portb == ph3->read_portb + ) { + pr.word = ph->read_portd(pr.port, ph->arg); + } else { + i_printf("PORT: splitting ind(0x%x)\n", pr.port); + pr.word = ph->read_portb(pr.port, ph->arg) | + (ph1->read_portb(pr.port + 1, ph->arg) << 8) | + (ph2->read_portb(pr.port + 2, ph->arg) << 16) | + (ph3->read_portb(pr.port + 3, ph->arg) << 24); + } + break; + case TYPE_OUTD: + if (ph->write_portb == ph1->write_portb && + ph->write_portb == ph2->write_portb && + ph->write_portb == ph3->write_portb + ) { + ph->write_portd(pr.port, pr.word, ph->arg); + } else { + i_printf("PORT: splitting outd(0x%x)\n", pr.port); + ph->write_portb(pr.port, pr.word, ph->arg); + ph1->write_portb(pr.port + 1, pr.word >> 8, ph->arg); + ph2->write_portb(pr.port + 2, pr.word >> 16, ph->arg); + ph3->write_portb(pr.port + 3, pr.word >> 24, ph->arg); + } + break; + } + write(port_fd_in[1], &pr, sizeof(pr)); + } +} + +/* + * SIDOC_BEGIN_FUNCTION extra_port_init() + * + * Catch all the special cases previously defined in ports.c + * mainly video stuff that should be moved away from here + * This must be called at the end of initialization phase + * + * NOTE: the order in which these inits are done could be significant! + * I tried to keep it the same it was in ports.c but this code surely + * can still have bugs + * + * SIDOC_END_FUNCTION + */ +int extra_port_init(void) +{ + int i; +/* + * DANG_FIXTHIS This stuff should be moved to video code!! + */ + if (portlog_map) { + /* switch off ioperm for $_ports that are traced and not forced fast */ + for (i = 0; i < sizeof(port_handle_table); i++) { + if (test_bit(i, portfast_map)) clear_bit(i, portlog_map); + if (test_bit(i, portlog_map) && + port_handle_table[i] >= HANDLE_STD_IO && + port_handle_table[i] <= HANDLE_STD_WR) { + set_ioperm(i, 1, 0); + i_printf ("PORT: switched off ioperm for traced port 0x%x\n", i); + } + } + } + + if (can_do_root_stuff) { + for (i = 0; i < sizeof(port_handle_table); i++) { + if (config.pci || config.pci_video || + config.speaker == SPKR_NATIVE || ( + port_handle_table[i] >= HANDLE_STD_IO && + port_handle_table[i] <= HANDLE_STD_WR)) { + /* fork the privileged port server */ + g_printf("starting port server\n"); + pipe(port_fd_out); + pipe(port_fd_in); + portserver_pid = fork(); + if (portserver_pid == 0) { + setsid(); + port_server(); + _exit(0); // never come here + } + close(port_fd_in[1]); + close(port_fd_out[0]); + sigchld_register_handler(portserver_pid, + portserver_exit, NULL); + break; + } + } + } + + return 0; +} + +void port_exit(void) +{ + int stat; + struct portreq pr; + if (!portserver_pid) return; + sigchld_enable_handler(portserver_pid, 0); + pr.type = TYPE_EXIT; + write(port_fd_out[1], &pr, sizeof(pr)); + waitpid(portserver_pid, &stat, 0); + portserver_pid = 0; +} + +void release_ports (void) +{ + memset (port_handle_table, NO_HANDLE, sizeof(port_handle_table)); + memset (port_andmask, 0xff, sizeof(port_andmask)); + memset (port_ormask, 0, sizeof(port_ormask)); + +} + +/* ---------------------------------------------------------------------- */ +/* + * SIDOC_BEGIN_FUNCTION port_register_handler + * + * Assigns a handle in the port table to a range of ports with or + * without a device, and registers the ports + * + * SIDOC_END_FUNCTION + */ +int port_register_handler(emu_iodev_t device, int flags) +{ + int handle, i; + + /* first find existing handle for function or create new one */ + for (handle=0; handle < port_handles; handle++) { + if (!strcmp(port_handler[handle].handler_name, device.handler_name)) + break; + } + + if (handle >= port_handles) { + /* no existing handle found, create new one */ + if (port_handles >= EMU_MAX_IO_DEVICES) { + error("PORT: too many IO devices, increase EMU_MAX_IO_DEVICES\n"); + leavedos(77); + } + + port_handles++; + port_handler[handle] = device; + /* + * for byte and double, a NULL function means that the port + * access is not available, while for word means that it will + * be translated into 2 byte accesses + */ + if (!device.read_portb) + port_handler[handle].read_portb = port_not_avail_inb; + if (!device.write_portb) + port_handler[handle].write_portb = port_not_avail_outb; + } + + /* change table to reflect new handler id for that address */ + for (i = device.start_addr; i <= device.end_addr; i++) { + if (port_handle_table[i] != 0) { + error("PORT: conflicting devices: %s & %s for port %#x\n", + port_handler[handle].handler_name, + EMU_HANDLER(i).handler_name, i); + config.exitearly = 1; + return 4; + } + port_handle_table[i] = handle; + if (flags & PORT_FORCE_FAST) /* force fast, no tracing allowed */ + set_bit(i, portfast_map); + } + + i_printf("PORT: registered \"%s\" handle 0x%02x [0x%04x-0x%04x]\n", + port_handler[handle].handler_name, handle, device.start_addr, + device.end_addr); + + if (flags & PORT_FAST) { + i_printf("PORT: trying to give fast access to ports [0x%04x-0x%04x]\n", + device.start_addr, device.end_addr); + if (set_ioperm(device.start_addr, device.end_addr-device.start_addr+1, 1) == -1) { + i_printf("PORT: fast failed: using perm/iopl for ports [0x%04x-0x%04x]\n", + device.start_addr, device.end_addr); + } + } + return 0; +} + + +/* + * SIDOC_BEGIN_FUNCTION port_allow_io + * + * + * SIDOC_END_FUNCTION + */ +Boolean port_allow_io(ioport_t start, Bit16u size, int permission, Bit8u ormask, + Bit8u andmask, int portspeed) +{ + static emu_iodev_t io_device; + int usemasks = 0; + unsigned int flags = 0; + + if (!can_do_root_stuff) { + warn("Direct port I/O in dosemu.conf requires root privs and -s\n"); + return FALSE; + } + + i_printf("PORT: allow_io for port 0x%04x:%d perm=%x or=%x and=%x\n", + start, size, permission, ormask, andmask); + + if ((ormask != 0) || (andmask != 0xff)) { + if (size > 1) + i_printf("PORT: andmask & ormask not supported for multiple ports\n"); + else + usemasks = 1; + } + + if (permission == IO_RDWR) + io_device.handler_name = "std port io"; + else if (permission == IO_READ) + io_device.handler_name = "std port read"; + else + io_device.handler_name = "std port write"; + + io_device.start_addr = start; + io_device.end_addr = start + size - 1; + + if (usemasks) { + port_andmask[start] = andmask; + port_ormask[start] = ormask; + } + if (portspeed >= 0) { + flags |= PORT_FAST; + if (portspeed > 0) + flags |= PORT_FORCE_FAST; + } + port_register_handler(io_device, flags); + return TRUE; +} + +/* + * SIDOC_BEGIN_FUNCTION set_ioperm + * + * wrapper for the ioperm() syscall, returns -1 if not successful. + * + * SIDOC_END_FUNCTION + */ +int +set_ioperm(int start, int size, int flag) +{ +#ifdef HAVE_SYS_IO_H + PRIV_SAVE_AREA + int tmp; + + if ((!can_do_root_stuff && flag == 1)) + return -1; /* don't bother */ + + /* While possibly not the best behavior I figure we ought to, + turn the privilege on here instead of in every caller. + If we want a privileged version of this function we can + call ioperm. + */ + enter_priv_on(); + tmp = ioperm(start, size, flag); + leave_priv_setting(); + + if (tmp==0) { + int i; + for (i=start; i<(start+size); i++) { + if (flag) { + set_bit(i, emu_io_bitmap); + } else { + clear_bit(i, emu_io_bitmap); + } + } + } + i_printf ("nPORT: set_ioperm [%x:%d:%d] returns %d\n",start,size,flag,tmp); + return tmp; +#else + return -1; +#endif +} + +void port_enter_critical_section(const char *caller) +{ + if (in_crit_section) { + error("Critical section conflict for %s and %s\n", + crit_sect_caller, caller); + in_crit_section = 0; + leavedos(49); + } + in_crit_section++; + crit_sect_caller = caller; +} + +void port_leave_critical_section(void) +{ + if (!in_crit_section) { + error("leave_critical_section without enter\n"); + leavedos(49); + } + in_crit_section--; +} + +/* ====================================================================== */ diff --git a/src/base/core/priv.c b/src/base/core/priv.c new file mode 100644 index 0000000..9888871 --- /dev/null +++ b/src/base/core/priv.c @@ -0,0 +1,257 @@ +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_IO_H +#include +#endif +#include "emu.h" +#include "priv.h" +#include "dosemu_config.h" +#include "mapping.h" +#include "utilities.h" +#ifdef X86_EMULATOR +#include "cpu-emu.h" +#endif + +#if 0 +#define PRIV_TESTING +#endif + +/* Some handy information to have around */ +static uid_t uid,euid; +static gid_t gid,egid; +static uid_t cur_uid, cur_euid; +static gid_t cur_gid, cur_egid; + +static int skip_priv_setting = 0; + +int can_do_root_stuff; +int under_root_login; +int using_sudo; +int current_iopl; + +#define PRIVS_ARE_ON (euid == cur_euid) +#define PRIVS_ARE_OFF (uid == cur_euid) +#define PRIVS_WERE_ON(privs) (pop_priv(privs)) + + +static void push_priv(saved_priv_status *privs) +{ + if (!privs || *privs != PRIV_MAGIC) { + error("Aiiiee... not in-sync saved priv status on push_priv\n"); + leavedos(99); + } + *privs = PRIVS_ARE_ON; +#ifdef PRIV_TESTING + c_printf("PRIV: pushing %d privs_ptr=%p\n", *privs, privs); +#endif +} + +static int pop_priv(saved_priv_status *privs) +{ + int ret; + if (!privs || *privs == PRIV_MAGIC) { + error("Aiiiee... not in-sync saved priv status on pop_priv\n"); + leavedos(99); + } +#ifdef PRIV_TESTING + c_printf("PRIV: popping %d privs_ptr=%p\n", *privs, privs); +#endif + ret = (int)*privs; + *privs = PRIV_MAGIC; + return ret; +} + +static int _priv_on(void) +{ + if (PRIVS_ARE_OFF) { /* make sure the privs need to be changed */ +#ifdef PRIV_TESTING + c_printf("PRIV: on-in %d\n", cur_euid); +#endif + if (setreuid(uid,euid)) { + error("Cannot turn privs on!\n"); + return 0; + } + cur_uid = uid; + cur_euid = euid; + if (setregid(gid,egid)) { + error("Cannot turn privs on!\n"); + return 0; + } + cur_gid = gid; + cur_egid = egid; + } +#ifdef PRIV_TESTING + c_printf("PRIV: on-ex %d\n", cur_euid); +#endif + return 1; +} + +static int _priv_off(void) +{ + if (PRIVS_ARE_ON) { /* make sure the privs need to be changed */ +#ifdef PRIV_TESTING + c_printf("PRIV: off-in %d\n", cur_euid); +#endif + if (setreuid(euid,uid)) { + error("Cannot turn privs off!\n"); + return 0; + } + cur_uid = euid; + cur_euid = uid; + if (setregid(egid,gid)) { + error("Cannot turn privs off!\n"); + return 0; + } + cur_gid = egid; + cur_egid = gid; + } +#ifdef PRIV_TESTING + c_printf("PRIV: off-ex %d\n", cur_euid); +#endif + return 1; +} + +int real_enter_priv_on(saved_priv_status *privs) +{ + if (skip_priv_setting) return 1; + push_priv(privs); + return _priv_on(); +} + +int real_enter_priv_off(saved_priv_status *privs) +{ + if (skip_priv_setting) return 1; + push_priv(privs); + return _priv_off(); +} + +int real_leave_priv_setting(saved_priv_status *privs) +{ + if (skip_priv_setting) return 1; + if (PRIVS_WERE_ON(privs)) return _priv_on(); + return _priv_off(); +} + +int priv_iopl(int pl) +{ +#ifdef HAVE_SYS_IO_H + int ret; + if (PRIVS_ARE_OFF) { + _priv_on(); + ret = iopl(pl); + _priv_off(); + } + else ret = iopl(pl); +#ifdef X86_EMULATOR + if (config.cpu_vm == CPUVM_EMU) e_priv_iopl(pl); +#endif + if (ret == 0) + current_iopl = pl; + return ret; +#else + return -1; +#endif +} + +uid_t get_cur_uid(void) +{ + return cur_uid; +} + +uid_t get_cur_euid(void) +{ + return cur_euid; +} + +gid_t get_cur_egid(void) +{ + return cur_egid; +} + +uid_t get_orig_uid(void) +{ + return uid; +} + +uid_t get_orig_euid(void) +{ + return euid; +} + +gid_t get_orig_gid(void) +{ + return gid; +} + +int priv_drop(void) +{ + if (setreuid(uid,uid) || setregid(gid,gid)) + { + error("Cannot drop root uid or gid!\n"); + return 0; + } + cur_euid = euid = cur_uid = uid; + cur_egid = egid = cur_gid = gid; + skip_priv_setting = 1; + if (uid) can_do_root_stuff = 0; + return 1; +} + +void priv_init(void) +{ + const char *sh = getenv("SUDO_HOME"); // theoretical future var + const char *h = getenv("HOME"); + uid = cur_uid = getuid(); + /* suid bit only sets euid & suid but not uid, sudo sets all 3 */ + if (!uid) under_root_login = 1; + euid = cur_euid = geteuid(); + if (!euid) can_do_root_stuff = 1; + if (!uid && !euid) skip_priv_setting = 1; + gid = cur_gid = getgid(); + egid = cur_egid = getegid(); + + /* must store the /proc/self/exe symlink contents before dropping + privs! */ + dosemu_proc_self_exe = readlink_malloc("/proc/self/exe"); + /* For Fedora we must also save a file descriptor to /proc/self/maps */ + dosemu_proc_self_maps_fd = open("/proc/self/maps", O_RDONLY | O_CLOEXEC); + if (!sh) + sh = getenv("DOSEMU_SUDO_HOME"); + /* see if -E was used */ + if (under_root_login && sh && h && strcmp(sh, h) == 0) + { + /* check for sudo and set to original user */ + char *s = getenv("SUDO_GID"); + if (s) { + gid = cur_gid = atoi(s); + if (gid) { + setregid(gid, egid); + } + } + s = getenv("SUDO_UID"); + if (s) { + uid = cur_uid = atoi(s); + if (uid) { + skip_priv_setting = under_root_login = 0; + using_sudo = 1; + s = getenv("SUDO_USER"); + if (s) { + initgroups(s, gid); + setenv("USER", s, 1); + } + setreuid(uid, euid); + } + } + } + + if (!can_do_root_stuff) + { + skip_priv_setting = 1; + } + + if (!skip_priv_setting) _priv_off(); +} diff --git a/src/base/core/vint.c b/src/base/core/vint.c new file mode 100644 index 0000000..7bca4db --- /dev/null +++ b/src/base/core/vint.c @@ -0,0 +1,181 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: virtual interrupt router (another one) + * + * Author: Stas Sergeev. + * + */ +#include +#include +#include "cpu.h" +#include "int.h" +#include "hlt.h" +#include "memory.h" +#include "port.h" +#include "chipset.h" +#include "emu.h" +#include "vint.h" + +#define VINT_MAX 2 +static int vi_used; +static uint16_t vint_hlt; + +#define ON_PIC1(n) (vih[n].orig_irq >= 8) + +struct vihandler { + void (*handler)(int, int); + void (*mask)(int, int); + uint8_t irq; + uint8_t orig_irq; + uint8_t interrupt; + int tweaked; + unsigned tw_flags; +}; +struct vihandler vih[VINT_MAX]; + +static void poll_pic0(uint8_t irq) +{ + port_outb(0x20, 0x0c); // OCW3, enter poll mode + port_outb(0x20, irq); // extension, may not work on real PIC + /* see if it worked */ + assert(pic_get_isr() & (1 << irq)); +} + +static void poll_pic1(uint8_t irq) +{ + port_outb(0x20, 0x0c); // OCW3, enter poll mode + port_outb(0x20, 2); // extension, may not work on real PIC + port_outb(0xa0, 0x0c); + port_outb(0xa0, irq - 8); + /* see if it worked */ + assert((pic_get_isr() & ((1 << irq) | 4)) == ((1 << irq) | 4)); +} + +static void full_eoi(void) +{ + port_outb(0xa0, 0x20); + port_outb(0x20, 0x20); +} + +static void do_ret(int vi_num) +{ + clear_IF(); + vih[vi_num].mask(vi_num, 0); + do_iret(); +} + +int vint_is_masked(int vi_num, uint8_t *imr) +{ + uint16_t real_imr = (imr[1] << 8) | imr[0]; + return !!(real_imr & (1 << vih[vi_num].orig_irq)); +} + +static void vint_handler(uint16_t idx, HLT_ARG(arg)) +{ + uint8_t imr[2]; + int masked; + int vi_num = idx >> 1; + + if (idx & 1) { + do_ret(vi_num); + return; + } + + imr[0] = port_inb(0x21); + imr[1] = port_inb(0xa1); + masked = vint_is_masked(vi_num, imr); + if (masked) { + h_printf("vint: masked, iret\n"); + do_eoi2_iret(); + } else { + uint8_t irq = vih[vi_num].orig_irq; + uint16_t port = (irq >= 8 ? PIC1_VECBASE_PORT : PIC0_VECBASE_PORT); + uint8_t inum = port_inb(port) + (irq & 7); + full_eoi(); + if (ON_PIC1(vi_num)) + poll_pic1(irq); + else + poll_pic0(irq); + if (vih[vi_num].tweaked) { + _IP++; // skip hlt + h_printf("vint: call to inum %x\n", inum); + real_run_int(inum); + vih[vi_num].mask(vi_num, 1); + } else { + h_printf("vint: jump to inum %x\n", inum); + jmp_to(ISEG(inum), IOFF(inum)); + } + } + + if (vih[vi_num].handler) + vih[vi_num].handler(vi_num, masked); +} + +void vint_post_irq_dpmi(int vi_num, int masked) +{ + full_eoi(); + if (!masked) { + uint8_t irq = vih[vi_num].orig_irq; + if (ON_PIC1(vi_num)) + poll_pic1(irq); + else + poll_pic0(irq); + } +} + +void vint_init(void) +{ + emu_hlt_t hlt_hdlr = HLT_INITIALIZER; + + hlt_hdlr.name = "vint"; + hlt_hdlr.func = vint_handler; + hlt_hdlr.len = VINT_MAX * 2; + vint_hlt = hlt_register_handler_vm86(hlt_hdlr); +} + +void vint_setup(void) +{ + int i; + + for (i = 0; i < VINT_MAX; i++) { + if (vih[i].interrupt) + SETIVEC(vih[i].interrupt, BIOS_HLT_BLK_SEG, vint_hlt + 2 * i); + } +} + +int vint_register(void (*ack_handler)(int, int), + void (*mask_handler)(int, int), + int irq, int orig_irq, int inum) +{ + struct vihandler *vi = &vih[vi_used]; + assert(vi_used < VINT_MAX); + vi->handler = ack_handler; + vi->mask = mask_handler; + vi->irq = irq; + vi->orig_irq = orig_irq; + vi->interrupt = inum; + return vi_used++; +} + +void vint_set_tweaked(int vi_num, int on, unsigned flags) +{ + struct vihandler *vi = &vih[vi_num]; + assert(vi_num < VINT_MAX); + vi->tweaked = on; + vi->tw_flags = flags; +} diff --git a/src/base/dev/dma/Makefile b/src/base/dev/dma/Makefile new file mode 100644 index 0000000..5f8f4a3 --- /dev/null +++ b/src/base/dev/dma/Makefile @@ -0,0 +1,14 @@ + +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf + +CFILES = dma.c +ALL = $(CFILES) +OBJS = dma.o + + +all: lib + +install: + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/dev/dma/dma.c b/src/base/dev/dma/dma.c new file mode 100644 index 0000000..752f767 --- /dev/null +++ b/src/base/dev/dma/dma.c @@ -0,0 +1,518 @@ +/* + * Copyright (C) 2006 Stas Sergeev + * + * The below copyright strings have to be distributed unchanged together + * with this file. This prefix can not be modified or separated. + */ + +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * DMA controller emulation. + * + * Author: Stas Sergeev + */ + +#include "emu.h" +#include "init.h" +#include "utilities.h" +#include "port.h" +#include "timers.h" +#include "cpu-emu.h" +#include "dma.h" +#include "dmaregs.h" +#include + +typedef union { + Bit8u byte[2]; + Bit16u value; +} dma_reg16; + +struct dma_channel { + dma_reg16 base_addr; + dma_reg16 base_count; + dma_reg16 cur_addr; + dma_reg16 cur_count; + Bit8u page; + Bit8u mode; +}; + +struct dma_controller { + struct dma_channel chans[4]; +#if 0 + /* looks like we don't need those */ + Bit16u tmp_addr; + Bit16u tmp_count; +#endif + Bit8u tmp_reg; + Bit8u status; + Bit8u command; + Bit8u mask; + Bit8u request; + Bit8u ff; +} dma[2]; + +static Bit8u dma_data_bus[2]; + +#define DMA1 0 +#define DMA2 1 + +/* need this as soon as dosemu is threaded */ +#define DMA_LOCK() +#define DMA_UNLOCK() + +#define DI(c) (((c) & 4) >> 2) +#define CI(c) ((c) & 3) + +#define HAVE_DRQ(contr, chan) (dma[contr].status & (1 << ((chan) + 4))) +#define REACHED_TC(contr, chan) (dma[contr].status & (1 << (chan))) +#define MASKED(contr, chan) (dma[contr].mask & (1 << (chan))) +#define HAVE_SRQ(contr, chan) (dma[contr].request & (1 << (chan))) +#define SW_ACTIVE(contr, chan) \ + (HAVE_SRQ(contr, chan) && \ + (DMA_TRANSFER_MODE(dma[contr].chans[chan].mode) == BLOCK)) + + +static void dma_soft_reset(int dma_idx) +{ + dma[dma_idx].command = 0; + dma[dma_idx].status = 0; + dma[dma_idx].request = 0; + dma[dma_idx].tmp_reg = 0; + dma[dma_idx].ff = 0; + dma[dma_idx].mask = 0x0f; + q_printf("DMA: Soft Reset on controller %i\n", dma_idx); +} + +static void dma_poll_DRQ(int dma_idx, int chan_idx) +{ + /* Since our model is simplified (API has only pulse_DRQ() instead + * of assert_DRQ()/release_DRQ()), poll never sees the DRQ asserted. */ + dma[dma_idx].status &= ~(1 << (chan_idx + 4)); +} + +static void dma_update_DRQ(int dma_idx, int chan_idx) +{ + switch (DMA_TRANSFER_MODE(dma[dma_idx].chans[chan_idx].mode)) { + case DEMAND: + dma_poll_DRQ(dma_idx, chan_idx); + break; + case SINGLE: + dma[dma_idx].status &= ~(1 << (chan_idx + 4)); + break; + case BLOCK: + if (REACHED_TC(dma_idx, chan_idx)) + dma_poll_DRQ(dma_idx, chan_idx); + break; + case CASCADE: + dma_poll_DRQ(dma_idx, chan_idx); + break; + } +} + +static void dma_process_channel(int dma_idx, int chan_idx) +{ + struct dma_channel *chan = &dma[dma_idx].chans[chan_idx]; + unsigned pa = (chan->page << 16) | (chan->cur_addr.value << dma_idx); + void *addr = physaddr_to_unixaddr(pa); + + /* first, do the transfer */ + switch (DMA_TRANSFER_OP(chan->mode)) { + case VERIFY: + q_printf("DMA: verify mode does nothing\n"); + break; + case WRITE: + if (addr != MAP_FAILED) { + e_invalidate_pa(pa, 1 << dma_idx); + memcpy(addr, dma_data_bus, 1 << dma_idx); + } else { + error_once0("DMA: write to unmapped address\n"); + q_printf("DMA: write to unmapped address %#x\n", pa); + } + break; + case READ: + if (addr != MAP_FAILED) + memcpy(dma_data_bus, addr, 1 << dma_idx); + else { + error_once0("DMA: read from unmapped address\n"); + q_printf("DMA: read from unmapped address %#x\n", pa); + memset(dma_data_bus, 0xff, sizeof(dma_data_bus)); + } + break; + case INVALID: + q_printf("DMA: invalid mode does nothing\n"); + break; + } + + /* now advance the address */ + if ((dma[dma_idx].command & 3) != 3) + chan->cur_addr.value += (DMA_ADDR_DEC(chan->mode) ? -1 : 1); + + /* and the counter */ + chan->cur_count.value--; + if (chan->cur_count.value == 0xffff) { /* overflow */ + if (DMA_AUTOINIT(chan->mode)) { + q_printf("DMA: controller %i, channel %i reinitialized\n", + dma_idx, chan_idx); + chan->cur_addr.value = chan->base_addr.value; + chan->cur_count.value = chan->base_count.value; + } else { /* TC */ + q_printf("DMA: controller %i, channel %i TC\n", dma_idx, + chan_idx); + dma[dma_idx].status |= 1 << chan_idx; + dma[dma_idx].request &= ~(1 << chan_idx); + /* the datasheet says it gets automatically masked too */ + dma[dma_idx].mask |= 1 << chan_idx; + } + } +} + +static void dma_run_channel(int dma_idx, int chan_idx) +{ + int done = 0; + long ticks = 0; + while (!done && + (HAVE_DRQ(dma_idx, chan_idx) || SW_ACTIVE(dma_idx, chan_idx))) { + if (!MASKED(dma_idx, chan_idx) && + !REACHED_TC(dma_idx, chan_idx) && + !(dma[dma_idx].command & 4) && + (DMA_TRANSFER_MODE(dma[dma_idx].chans[chan_idx].mode) != CASCADE)) { + dma_process_channel(dma_idx, chan_idx); + ticks++; + } else { + done = 1; + } + dma_update_DRQ(dma_idx, chan_idx); + } + if (ticks > 1) + q_printf("DMA: processed %lu (left %u) cycles on controller %i channel %i\n", + ticks, dma[dma_idx].chans[chan_idx].cur_count.value, dma_idx, + chan_idx); +} + +static void dma_process(void) +{ + int contr_num, chan_num; + + DMA_LOCK(); + for (contr_num = 0; contr_num < 2; contr_num++) { + for (chan_num = 0; chan_num < 4; chan_num++) + dma_run_channel(contr_num, chan_num); + } + DMA_UNLOCK(); +} + +int dma_pulse_DRQ(int ch, Bit8u * buf) +{ + int ret = DMA_DACK; + if (MASKED(DI(ch), CI(ch))) { + q_printf("DMA: channel %i masked, DRQ ignored\n", ch); + ret = DMA_NO_DACK; + } + if ((dma[DI(ch)].status & 0xf0) || dma[DI(ch)].request) { + error("DMA: channel %i already active! (m=%#x s=%#x r=%#x)\n", + ch, dma[DI(ch)].chans[CI(ch)].mode, dma[DI(ch)].status, + dma[DI(ch)].request); + ret = DMA_NO_DACK; + } +#if 0 + q_printf("DMA: pulse DRQ on channel %d\n", ch); +#endif + if (ret == DMA_DACK) { + DMA_LOCK(); + dma[DI(ch)].status |= 1 << (CI(ch) + 4); + memcpy(dma_data_bus, buf, 1 << DI(ch)); + dma_run_channel(DI(ch), CI(ch)); + memcpy(buf, dma_data_bus, 1 << DI(ch)); + DMA_UNLOCK(); + } else { + memset(buf, 0xff, 1 << DI(ch)); + } + return ret; +} + + +/* lets ride on the cpp ass */ +#define d(x) (x-1) +#define HANDLE_X(n) \ + HANDLE_##n(1, 1); \ + HANDLE_##n(1, 2); \ + HANDLE_##n(1, 3); \ + HANDLE_##n(1, 4); \ + HANDLE_##n(2, 1); \ + HANDLE_##n(2, 2); \ + HANDLE_##n(2, 3); \ + HANDLE_##n(2, 4) +static Bit8u dma_io_read(ioport_t port, void *arg) +{ + Bit8u r = 0xff; + switch (port) { + +#define HANDLE_CUR_ADDR_READ(d_n, c_n) \ + case DMA##d_n##_ADDR_##c_n: \ + r = dma[d(d_n)].chans[d(c_n)].cur_addr.byte[dma[d(d_n)].ff]; \ + q_printf("DMA%i: cur_addr read: %#x from Channel %d byte %d\n", \ + d_n, r, d(c_n), dma[d(d_n)].ff); \ + dma[d(d_n)].ff ^= 1; \ + break + HANDLE_X(CUR_ADDR_READ); + +#define HANDLE_CUR_CNT_READ(d_n, c_n) \ + case DMA##d_n##_CNT_##c_n: \ + r = dma[d(d_n)].chans[d(c_n)].cur_count.byte[dma[d(d_n)].ff]; \ + q_printf("DMA%i: cur_cnt read: %#x from Channel %d byte %d\n", \ + d_n, r, d(c_n), dma[d(d_n)].ff); \ + dma[d(d_n)].ff ^= 1; \ + break + HANDLE_X(CUR_CNT_READ); + +#define HANDLE_PAGE_READ(d_n, c_n) \ + case DMA##d_n##_PAGE_##c_n: \ + r = dma[d(d_n)].chans[d(c_n)].page; \ + q_printf("DMA%i: page read: %#x from Channel %d\n", \ + d_n, r, d(c_n)); \ + break + HANDLE_X(PAGE_READ); + + case DMA1_STAT_REG: + r = dma[DMA1].status; + q_printf("DMA1: Read %u from Status reg\n", r); + dma[DMA1].status &= 0xf0; /* clear status bits */ + break; + case DMA2_STAT_REG: + r = dma[DMA2].status; + q_printf("DMA2: Read %u from Status reg\n", r); + dma[DMA2].status &= 0xf0; /* clear status bits */ + break; + + case DMA1_TEMP_REG: + r = dma[DMA1].tmp_reg; + q_printf("DMA1: Read %u from temporary register unimplemented\n", + r); + break; + case DMA2_TEMP_REG: + r = dma[DMA2].tmp_reg; + q_printf("DMA2: Read %u from temporary register unimplemented\n", + r); + break; + + default: + q_printf("DMA: Unhandled Read on 0x%04x\n", (Bit16u) port); + } + + dma_process(); // Not needed in fact + + return r; +} + +static void dma_io_write(ioport_t port, Bit8u value, void *arg) +{ + switch (port) { + +#define HANDLE_ADDR_WRITE(d_n, c_n) \ + case DMA##d_n##_ADDR_##c_n: \ + dma[d(d_n)].chans[d(c_n)].base_addr.byte[dma[d(d_n)].ff] = value; \ + dma[d(d_n)].chans[d(c_n)].cur_addr.byte[dma[d(d_n)].ff] = value; \ + q_printf("DMA%i: addr write: %#x to Channel %d byte %d\n", \ + d_n, value, d(c_n), dma[d(d_n)].ff); \ + dma[d(d_n)].ff ^= 1; \ + break + HANDLE_X(ADDR_WRITE); + +#define HANDLE_CNT_WRITE(d_n, c_n) \ + case DMA##d_n##_CNT_##c_n: \ + dma[d(d_n)].chans[d(c_n)].base_count.byte[dma[d(d_n)].ff] = value; \ + dma[d(d_n)].chans[d(c_n)].cur_count.byte[dma[d(d_n)].ff] = value; \ + q_printf("DMA%i: count write: %#x to Channel %d byte %d\n", \ + d_n, value, d(c_n), dma[d(d_n)].ff); \ + dma[d(d_n)].ff ^= 1; \ + break + HANDLE_X(CNT_WRITE); + +#define HANDLE_PAGE_WRITE(d_n, c_n) \ + case DMA##d_n##_PAGE_##c_n: \ + dma[d(d_n)].chans[d(c_n)].page = value; \ + q_printf("DMA%i: page write: %#x to Channel %d\n", \ + d_n, value, d(c_n)); \ + break + HANDLE_X(PAGE_WRITE); + + case DMA1_MASK_REG: + if (value & 4) { + q_printf("DMA1: mask channel %i\n", value & 3); + dma[DMA1].mask |= 1 << (value & 3); + } else { + q_printf("DMA1: unmask channel %i\n", value & 3); + dma[DMA1].mask &= ~(1 << (value & 3)); + dma[DMA1].status &= ~(1 << (value & 3)); + } + break; + case DMA2_MASK_REG: + if (value & 4) { + q_printf("DMA2: mask channel %i\n", value & 3); + dma[DMA2].mask |= 1 << (value & 3); + } else { + q_printf("DMA2: unmask channel %i\n", value & 3); + dma[DMA2].mask &= ~(1 << (value & 3)); + dma[DMA2].status &= ~(1 << (value & 3)); + } + break; + + case DMA1_MODE_REG: + dma[DMA1].chans[value & 3].mode = value >> 2; + q_printf("DMA1: Write mode 0x%x to Channel %u\n", value >> 2, + value & 3); + break; + case DMA2_MODE_REG: + dma[DMA2].chans[value & 3].mode = value >> 2; + q_printf("DMA2: Write mode 0x%x to Channel %u\n", value >> 2, + value & 3); + break; + + case DMA1_CMD_REG: + dma[DMA1].command = value; + q_printf("DMA1: Write 0x%x to Command reg\n", value); + break; + case DMA2_CMD_REG: + dma[DMA2].command = value; + q_printf("DMA2: Write 0x%x to Command reg\n", value); + break; + + case DMA1_CLEAR_FF_REG: + q_printf("DMA1: Clearing Output Flip-Flop\n"); + dma[DMA1].ff = 0; + break; + case DMA2_CLEAR_FF_REG: + q_printf("DMA2: Clearing Output Flip-Flop\n"); + dma[DMA2].ff = 0; + break; + + case DMA1_RESET_REG: + q_printf("DMA1: Reset\n"); + dma_soft_reset(DMA1); + break; + case DMA2_RESET_REG: + q_printf("DMA2: Reset\n"); + dma_soft_reset(DMA2); + break; + + case DMA1_REQ_REG: + if (value & 4) { + q_printf("DMA1: Setting request state %#x\n", value); + dma[DMA1].request |= 1 << (value & 3); + } else { + q_printf("DMA1: Clearing request state %#x\n", value); + dma[DMA1].request &= ~(1 << (value & 3)); + } + break; + case DMA2_REQ_REG: + if (value & 4) { + q_printf("DMA2: Setting request state %#x\n", value); + dma[DMA2].request |= 1 << (value & 3); + } else { + q_printf("DMA2: Clearing request state %#x\n", value); + dma[DMA2].request &= ~(1 << (value & 3)); + } + break; + + case DMA1_CLR_MASK_REG: + q_printf("DMA1: Clearing masks\n"); + dma[DMA1].mask = 0; + dma[DMA1].status &= 0xf0; + break; + case DMA2_CLR_MASK_REG: + q_printf("DMA2: Clearing masks\n"); + dma[DMA2].mask = 0; + dma[DMA2].status &= 0xf0; + break; + + case DMA1_MASK_ALL_REG: + q_printf("DMA1: Setting masks %#x\n", value); + dma[DMA1].mask = value; + dma[DMA1].status &= 0xf0; + break; + case DMA2_MASK_ALL_REG: + q_printf("DMA2: Setting masks %#x\n", value); + dma[DMA2].mask = value; + dma[DMA2].status &= 0xf0; + break; + + default: + q_printf("DMA: Unhandled Write on 0x%04x\n", (Bit16u) port); + } + + dma_process(); // Not needed in fact +} + +void dma_reset(void) +{ + dma_soft_reset(DMA1); + dma_soft_reset(DMA2); +} + +void dma_init(void) +{ + emu_iodev_t io_device; + + /* 8237 DMA controller */ + io_device.read_portb = dma_io_read; + io_device.write_portb = dma_io_write; + io_device.read_portw = NULL; + io_device.write_portw = NULL; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + + /* + * XT Controller + */ + io_device.start_addr = 0x0000; + io_device.end_addr = 0x000F; + io_device.handler_name = "DMA - XT Controller"; + port_register_handler(io_device, 0); + + /* + * Page Registers (XT) + */ + io_device.start_addr = 0x0081; + io_device.end_addr = 0x0087; + io_device.handler_name = "DMA - XT Pages"; + port_register_handler(io_device, 0); + + /* + * AT Controller + */ + io_device.start_addr = 0x00C0; + io_device.end_addr = 0x00DE; + io_device.handler_name = "DMA - AT Controller"; + port_register_handler(io_device, 0); + + /* + * Page Registers (AT) + */ + io_device.start_addr = 0x0089; + io_device.end_addr = 0x008F; + io_device.handler_name = "DMA - AT Pages"; + port_register_handler(io_device, 0); + + q_printf("DMA: DMA Controller initialized - 8 & 16 bit modes\n"); +} + +CONSTRUCTOR(static void dma_early_init(void)) +{ + /* HACK - putting this into dma_init() should work, but doesn't */ + register_debug_class('q', NULL, "DMA"); +} diff --git a/src/base/dev/dma/dmaregs.h b/src/base/dev/dma/dmaregs.h new file mode 100644 index 0000000..cf63334 --- /dev/null +++ b/src/base/dev/dma/dmaregs.h @@ -0,0 +1,84 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __DMAREGS_H__ +#define __DMAREGS_H__ + +/* 8237 DMA controllers */ +#define IO_DMA1_BASE 0x00 /* 8 bit slave DMA, channels 0..3 */ +#define IO_DMA2_BASE 0xC0 /* 16 bit master DMA, ch 4(=slave input)..7 */ + +/* DMA controller registers */ +#define DMA1_CMD_REG 0x08 /* command register (w) */ +#define DMA1_STAT_REG 0x08 /* status register (r) */ +#define DMA1_REQ_REG 0x09 /* request register (w) */ +#define DMA1_MASK_REG 0x0A /* single-channel mask (w) */ +#define DMA1_MODE_REG 0x0B /* mode register (w) */ +#define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */ +#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */ +#define DMA1_RESET_REG 0x0D /* Master Clear (w) */ +#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */ +#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */ + +#define DMA2_CMD_REG 0xD0 /* command register (w) */ +#define DMA2_STAT_REG 0xD0 /* status register (r) */ +#define DMA2_REQ_REG 0xD2 /* request register (w) */ +#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */ +#define DMA2_MODE_REG 0xD6 /* mode register (w) */ +#define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */ +#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */ +#define DMA2_RESET_REG 0xDA /* Master Clear (w) */ +#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */ +#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */ + +#define DMA1_ADDR_1 0x00 /* DMA address registers */ +#define DMA1_ADDR_2 0x02 +#define DMA1_ADDR_3 0x04 +#define DMA1_ADDR_4 0x06 +#define DMA2_ADDR_1 0xC0 +#define DMA2_ADDR_2 0xC4 +#define DMA2_ADDR_3 0xC8 +#define DMA2_ADDR_4 0xCC + +#define DMA1_CNT_1 0x01 /* DMA count registers */ +#define DMA1_CNT_2 0x03 +#define DMA1_CNT_3 0x05 +#define DMA1_CNT_4 0x07 +#define DMA2_CNT_1 0xC2 +#define DMA2_CNT_2 0xC6 +#define DMA2_CNT_3 0xCA +#define DMA2_CNT_4 0xCE + +#define DMA1_PAGE_1 0x87 /* DMA page registers */ +#define DMA1_PAGE_2 0x83 +#define DMA1_PAGE_3 0x81 +#define DMA1_PAGE_4 0x82 +#define DMA2_PAGE_1 0x8F +#define DMA2_PAGE_2 0x8B +#define DMA2_PAGE_3 0x89 +#define DMA2_PAGE_4 0x8A + +#define DMA_TRANSFER_MODE(m) ((m >> 4) & 3) +enum TRMODE { DEMAND, SINGLE, BLOCK, CASCADE }; + +#define DMA_TRANSFER_OP(m) (m & 3) +enum TROP { VERIFY, WRITE, READ, INVALID }; + +#define DMA_ADDR_DEC(m) ((m >> 3) & 1) + +#define DMA_AUTOINIT(m) ((m >> 2) & 1) + +#endif diff --git a/src/base/dev/misc/8042.c b/src/base/dev/misc/8042.c new file mode 100644 index 0000000..14f82f3 --- /dev/null +++ b/src/base/dev/misc/8042.c @@ -0,0 +1,436 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * DANG_BEGIN_MODULE + * + * Description: 8042 Keyboard controller chip emulation for DOSEMU. + * + * Exports: keyb_8042_init(void), keyb_8042_reset(void) + * + * Maintainers: Scott Buchholz, Rainer Zimmermann + * + * REMARK + * This code provides truly rudimentary 8042 controller emulation. + * Not having any documentation on the 8042 makes it hard to improve. :) + * + * /REMARK + * DANG_END_MODULE + * + */ + +#include "emu.h" +#include "iodev.h" +#include "int.h" +#include "port.h" +#include "memory.h" +#include "keyboard/keyboard.h" +#include "keyboard/keyb_server.h" +#include "keyboard/keyb_clients.h" +#include "speaker.h" +#include "hma.h" + +/* bios-assisted keyboard read hack */ +#define KBD_READ_HACK 0 + +#define RESET_LINE_MASK 1 + +/* accurate emulation of special 8042 and keyboard commands - currently untested... +*/ +#define KEYB_CMD 1 + +Bit8u port60_buffer = 0; +Boolean port60_ready = 0; +#if KBD_READ_HACK +static Bit8u last_read_data; +static Boolean last_read_valid; +#endif +static Boolean kbd_disabled; + +#if KEYB_CMD + +/* variable indicating the command status of the keyboard/8042. + * if non-zero, e.g. a parameter byte to a command is expected. + */ +static int wstate = 0; +static int rstate = 0; + +static int keyb_ctrl_scanmap = 1; +static int keyb_ctrl_typematic = 0x23; +static int keyb_ctrl_enable = 1; +static int keyb_ctrl_isdata = 0; +static Bit8u keyb_ctrl_command = 0x01; + +static inline void keyb_ctrl_clearbuf(void) +{ +/* this probably ought to do something :) */ +} + +/* write byte to the 8042's output buffer */ +void output_byte_8042(Bit8u value) +{ + port60_buffer=value; + port60_ready=1; + if (keyb_ctrl_command & 0x01) { /* if interrupt enabled */ + k_printf("8042: scheduling IRQ1\n"); + pic_request(1); + } + else + k_printf("8042: interrupt flag OFF!\n"); +} + +static void ack(void) +{ + write_queue(&keyb_queue, 0xfa); +} + +static void write_port60(Bit8u value) +{ + switch (wstate) { + case 0x00: + switch (value) { + case 0xed: /* set mode indicators */ + h_printf("8042: write port 0x60 set mode indicators\n"); + ack(); + wstate=0xed; + break; + case 0xee: /* port check */ + h_printf("8042: write port 0x60 test mode 0xee\n"); + write_queue(&keyb_queue, 0xee); + break; + case 0xf0: /* set keyb scan byte */ + h_printf("8042: write port 0x60 set keyb scan type\n"); + ack(); + wstate=0xf0; + break; + case 0xf2: /* get keyb type */ + h_printf("8042: write port 0x60 get keyb type\n"); + ack(); + rstate=0xf2; + break; + case 0xf3: /* set typematic speed */ + h_printf("8042: write port 0x60 set typematic speed\n"); + ack(); + wstate=0xf3; + break; + case 0xf4: /* clear buffer */ + h_printf("8042: write port 0x60 clear buffer\n"); + keyb_ctrl_clearbuf(); + keyb_ctrl_enable=1; + ack(); + break; + case 0xf5: /* default, w/disable */ + h_printf("8042: write port 0x60 set default, w/disable\n"); + keyb_8042_reset(); + keyb_ctrl_enable = 0; + ack(); + break; + case 0xf6: /* set default */ + h_printf("8042: write port 0x60 set default\n"); + keyb_8042_reset(); + ack(); + break; + case 0xf7: /* set all keys to typematic */ + case 0xf8: /* set all keys to make/break */ + case 0xf9: /* set all keys to make */ + case 0xfa: /* set all keys to typematic make/break */ + h_printf("8042: write port 0x60 set mode (0x%02x)\n", value); + keyb_ctrl_clearbuf(); + ack(); + /* set mode ??? */ + break; + case 0xfb: /* set single key to typematic & wait */ + case 0xfc: /* set to make/break & wait */ + case 0xfd: /* set to make & wait */ + h_printf("8042: write port 0x60 set mod (0x%02x)\n", value); + keyb_ctrl_clearbuf(); + ack(); + wstate=value; + break; + case 0xfe: /* resend */ + h_printf("8042: write port 0x60 resend\n"); + write_queue(&keyb_queue, port60_buffer); + break; + case 0xff: /* reset */ + h_printf("8042: write port 0x60 reset\n"); + ack(); + rstate=0xff; /* wait for port 60h read */ + break; + default: + h_printf("8042: write port 0x60 unsupported command 0x%02x =>Error\n", value); + write_queue(&keyb_queue, 0xfe); + break; + } + break; + case 0x60: + h_printf("8042: write 8042 command byte 0x%02x\n", value); + keyb_ctrl_command=value; + /* no ack() */ + wstate=0; + break; + case 0xd1: + h_printf("8042: drive output port lines, value=0x%02x\n", value); + switch (value) { + case 0xdf: /* enable A20 */ + h_printf("8042: enable A20 line\n"); + set_a20(1); + break; + case 0xdd: /* disable A20) */ + h_printf("8042: disable A20 line\n"); + set_a20(0); + break; + } + port60_ready=0; + wstate=0; + break; + case 0xed: /* set LED mode */ + { + t_modifiers modifiers = 0; + h_printf("8042: write port 0x60 set LED mode to 0x%02x\n", value); + /* TESTME this mapping is an educated guess */ + if (value & 0x01) modifiers |= MODIFIER_SCR; + if (value & 0x02) modifiers |= MODIFIER_NUM; + if (value & 0x04) modifiers |= MODIFIER_CAPS; + keyb_client_set_leds(modifiers); + ack(); + wstate=0; + break; + } + case 0xf0: /* get/set keyboard scan map */ + h_printf("8042: write port 0x60 get/set keyboard scan map 0x%02x\n", value); + ack(); + if (value == 0) + write_queue(&keyb_queue, keyb_ctrl_scanmap); + else + keyb_ctrl_scanmap=value; + wstate=0; + break; + + case 0xf3: /* set typematic rate */ + h_printf("8042: write port 0x60 set typematic rate 0x%02x\n", value); + keyb_ctrl_typematic = value; + ack(); + wstate=0; + break; + default: + h_printf("8042: write port 0x60 illegal state (0x%02x), resending\n", + wstate); + wstate=0; + write_port60(value); + } +} + +/* write to port 64h (8042 command register) */ +static void write_port64(Bit8u value) { + k_printf("8042: write port64h, =%02x\n",value); + + switch(value) { + case 0x20: /* read 8042 command byte */ + output_byte_8042(keyb_ctrl_command); + break; + + case 0x60: /* write 8042 command byte */ + wstate=0x60; + break; + +#if 0 /* not sure if these are ok and/or needed. */ + + case 0xa4: /* passwort installed test */ + output_byte_8042(0xfa); /* no password */ + break; + + case 0xa5: /* load password */ + /* XXX ... we should read bytes from port60 until 0 is found */ + break; + + case 0xa9: /* aux interface test */ + output_byte_8042(0x00); /* ok */ + break; + + case 0xaa: /* 8042 self test */ + output_byte_8042(0x55); /* ok */ + break; + + case 0xab: /* keyboard interface test */ + output_byte_8042(0x00); /* ok */ + break; + + case 0xc0: /* read 8042 input port */ + output_byte_8042(0xff); /* just send _something_... */ + break; +#endif + case 0xad: + kbd_disabled = 1; + break; + case 0xae: + kbd_disabled = 0; +#if KBD_READ_HACK + last_read_valid = 0; +#endif + break; + case 0xd1: /* next write to port 0x60 drives hardware port */ + wstate=0xd1; + break; + + case 0xf0 ... 0xff: /* produce 6ms pulse on hardware port */ + wstate=0; + port60_ready=0; + if (!(value & RESET_LINE_MASK)) { + h_printf("8042: produce 6ms pulse on cpu reset line\n"); + cpu_reset(); + } + else + h_printf("8042: produce 6ms pulse on hardware port, ignored\n"); + break; + + default: + h_printf("8042: write port 0x64 unsupported command 0x%02x, ignored\n", + value); + /* various other commands... ignore */ + break; + } +} +#endif + +static Bit8u read_port60(void) +{ + Bit8u r; + +#if KBD_READ_HACK + if (kbd_disabled && last_read_valid) { + r = last_read_data; + if (port60_ready && (keyb_ctrl_command & 0x01)) /* if interrupt enabled */ + pic_request(1); + } else { + r = port60_buffer; + port60_ready = 0; + last_read_data = r; + last_read_valid = 1; + } +#else + r = port60_buffer; + port60_ready = 0; +#endif + + h_printf("8042: read port 0x60 = 0x%02x\n", r); + +#if KEYB_CMD + switch (rstate) { + case 0xf2: /* get keyboard type, MSB */ + h_printf("8042: read port 0x60, getting keyboard type (MSB)\n"); + output_byte_8042(0x83); + rstate = 0x72; + break; + case 0x72: /* get keyboard type, LSB */ + h_printf("8042: read port 0x60, getting keyboard type (LSB)\n"); + output_byte_8042(0xab); + rstate = 0; + break; + case 0xff: /* reset keyboard */ + h_printf("8042: read port 0x60, resetting\n"); + output_byte_8042(0xaa); /* BAT completion code */ + rstate = 0; + break; + default: /* invalid state ?! */ + rstate = 0; + break; + } +#endif + return r; +} + + +Bit8u keyb_io_read(ioport_t port, void *arg) +{ + Bit8u r = 0; + + switch (port) { + case 0x60: + r = read_port60(); + if (!port60_ready) + pic_untrigger(1); + k_printf("8042: read port 0x60 read=0x%02x\n",r); + break; + + case 0x61: + /* Handle only PC-Speaker right now */ + r = spkr_io_read(port); + break; + + case 0x64: + r= 0x1c | (port60_ready ? 0x01 : 0x00); + k_printf("8042: read port 0x64 status check=0x%02x, port60_ready=%d\n", + r, port60_ready); + } + return r; +} + +void keyb_io_write(ioport_t port, Bit8u value, void *arg) +{ + switch (port) { + case 0x60: + k_printf("8042: write port 0x60 outb = 0x%x\n", value); +#if KEYB_CMD + write_port60(value); +#endif + break; + + case 0x61: + if (value & 0x80) { + k_printf("8042: IRQ ACK, %i\n", port60_ready); + int_check_queue(); /* reschedule irq1 if appropriate */ + } + spkr_io_write(port, value); + break; + + case 0x64: + k_printf("8042: write port 0x64 outb = 0x%x\n", value); + write_port64(value); + break; + } +} + +void keyb_8042_init(void) +{ + emu_iodev_t io_device; + + /* 8042 keyboard controller */ + io_device.read_portb = keyb_io_read; + io_device.write_portb = keyb_io_write; + io_device.read_portw = NULL; + io_device.write_portw = NULL; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + io_device.handler_name = "8042 Keyboard data"; + io_device.start_addr = 0x0060; + io_device.end_addr = 0x0060; + port_register_handler(io_device, 0); + + io_device.handler_name = "8042 Keyboard command"; + io_device.start_addr = 0x0064; + io_device.end_addr = 0x0064; + port_register_handler(io_device, 0); + + io_device.handler_name = "Keyboard controller port B"; + io_device.start_addr = 0x0061; + io_device.end_addr = 0x0061; + port_register_handler(io_device, 0); +} + +void keyb_8042_reset(void) +{ +#if KEYB_CMD + rstate = 0; + wstate = 0; + keyb_ctrl_scanmap = 1; + keyb_ctrl_typematic = 0x23; + keyb_ctrl_enable = 1; + keyb_ctrl_isdata = 0; + keyb_ctrl_clearbuf(); +#endif + port60_buffer = 0; + port60_ready = 0; +} diff --git a/src/base/dev/misc/Makefile b/src/base/dev/misc/Makefile new file mode 100644 index 0000000..4f94e40 --- /dev/null +++ b/src/base/dev/misc/Makefile @@ -0,0 +1,19 @@ +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf + +# src/base/misc/cmos.c -> ../dev/misc/cmos.c +# src/base/misc/lpt.c -> ../dev/misc/lpt.c +# src/base/misc/timers.c -> ../dev/misc/timers.c + + +CFILES = cmos.c timers.c lpt.c rtc.c pci.c chipset.c 8042.c kbd.c \ + virq.c vtmr.c +ifeq ($(OS),Linux) +CFILES += joystick.c +endif + +include $(REALTOPDIR)/src/Makefile.common + +all: lib + +install: all diff --git a/src/base/dev/misc/chipset.c b/src/base/dev/misc/chipset.c new file mode 100644 index 0000000..c5ff54f --- /dev/null +++ b/src/base/dev/misc/chipset.c @@ -0,0 +1,70 @@ +/* + * (C) Copyright 2013 the "DOSEMU-Development-Team". + * + * for details see file COPYING.DOSEMU in the DOSEMU distribution + */ + +#include "emu.h" +#include "port.h" +#include "hma.h" +#include "chipset.h" + +#define CONTROL_RESET_MASK 1 +#define CONTROL_A20GATE_MASK 2 + + +static Bit8u port92h_io_read(ioport_t port, void *arg) +{ + Bit8u ret = 0; + if (a20) + ret |= CONTROL_A20GATE_MASK; + return ret; +} + +static void port92h_io_write(ioport_t port, Bit8u val, void *arg) +{ + int enA20 = (val & CONTROL_A20GATE_MASK) ? 1 : 0; + if (val & CONTROL_RESET_MASK) cpu_reset(); + set_a20(enA20); +} + +static Bit8u picext_io_read(ioport_t port, void *arg) +{ + Bit8u val = 0xff; + + switch (port) { + case PIC0_VECBASE_PORT: + val = pic0_get_base(); + break; + case PIC1_VECBASE_PORT: + val = pic1_get_base(); + break; + } + return val; +} + +void chipset_init(void) +{ + emu_iodev_t io_dev = {}; + + io_dev.read_portb = port92h_io_read; + io_dev.write_portb = port92h_io_write; + io_dev.start_addr = 0x92; + io_dev.end_addr = 0x92; + io_dev.handler_name = "Chipset Control Port A"; + port_register_handler(io_dev, 0); + + memset(&io_dev, 0, sizeof(io_dev)); + io_dev.read_portb = picext_io_read; + io_dev.start_addr = PIC0_EXTPORT_START; + io_dev.end_addr = PIC0_EXTPORT_START + PICx_EXT_PORTS - 1; + io_dev.handler_name = "PIC0 extensions"; + port_register_handler(io_dev, 0); + + memset(&io_dev, 0, sizeof(io_dev)); + io_dev.read_portb = picext_io_read; + io_dev.start_addr = PIC1_EXTPORT_START; + io_dev.end_addr = PIC1_EXTPORT_START + PICx_EXT_PORTS - 1; + io_dev.handler_name = "PIC1 extensions"; + port_register_handler(io_dev, 0); +} diff --git a/src/base/dev/misc/cmos.c b/src/base/dev/misc/cmos.c new file mode 100644 index 0000000..d0b0202 --- /dev/null +++ b/src/base/dev/misc/cmos.c @@ -0,0 +1,168 @@ +/* + * SIDOC_BEGIN_MODULE + * + * Description: CMOS handling routines + * + * Originally by Robert Sanders, gt8134b@prism.gatech.edu + * New CMOS code by vignani@mail.tin.it 1997-98 + * + * SIDOC_END_MODULE + * + */ + +#include +#include + +#include "emu.h" +#include "port.h" +#include "iodev.h" +#include "disks.h" + + +#define PEXTMEM_SIZE EXTMEM_SIZE + +struct CMOS cmos; + +static int +cmos_chksum(void) +{ + int i, sum = 0; + + /* return the checksum over bytes 0x10-0x20. These are static values, + * so no need to call cmos_read() + */ + + for (i = 0x10; i < 0x21; i++) + sum += GET_CMOS(i); + return sum; +} + +Bit8u cmos_read(ioport_t port, void *arg) +{ + unsigned char holder = 0; + + if (port != 0x71) + return 0xff; + + switch (cmos.address) { + case 0 ... 0x0d: + holder = rtc_read(cmos.address); + break; + + case CMOS_CHKSUML: + holder = cmos_chksum() & 0xff; + break; + + case CMOS_CHKSUMM: + holder = cmos_chksum() >> 8; + break; + + default: + holder = GET_CMOS(cmos.address); + if (!cmos.flag[cmos.address]) + h_printf("CMOS: unknown CMOS read 0x%x\n", cmos.address); + } + + h_printf("CMOS: read addr 0x%02x = 0x%02x\n", cmos.address, holder); + return holder; +} + +void cmos_write(ioport_t port, Bit8u byte, void *arg) +{ + if (port == 0x70) + cmos.address = byte & ~0xc0;/* get true address */ + else { + h_printf("CMOS: set address 0x%02x to 0x%02x\n", cmos.address, byte); + switch (cmos.address) { + case 0 ... 0x0d: + rtc_write(cmos.address, byte); + break; + + default: + SET_CMOS(cmos.address, byte); + } + } +} + +void cmos_init(void) +{ + emu_iodev_t io_device; + int i; + + /* CMOS RAM & RTC */ + io_device.read_portb = cmos_read; + io_device.write_portb = cmos_write; + io_device.read_portw = NULL; + io_device.write_portw = NULL; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + io_device.handler_name = "CMOS RAM"; + io_device.start_addr = 0x0070; + io_device.end_addr = 0x0071; + port_register_handler(io_device, 0); + + for (i = 0; i < 64; i++) + cmos.subst[i] = cmos.flag[i] = 0; + + rtc_setup(); + + /* CMOS floppies...is this correct? */ + SET_CMOS(CMOS_DISKTYPE, + (config.fdisks ? (disktab[0].default_cmos << 4) : 0) | + ((config.fdisks > 1) ? disktab[1].default_cmos & 0xf : 0)); + + /* CMOS equipment byte..top 2 bits are 01 for 2 drives, 00 for 1 + * bit 1 is 1 for math coprocessor installed + * bit 0 is 1 for floppies installed, 0 for none */ + + cmos.subst[0x14] = ((config.fdisks ? config.fdisks - 1 : 0) << 6) + + (config.fdisks ? 1 : 0); + if (config.mathco) + cmos.subst[0x14] |= 2; + cmos.flag[0x14] = 1; + + /* CMOS hard disks...type 47 for both. */ + SET_CMOS(CMOS_HDTYPE, (config.hdisks ? 0xf0 : 0) + + ((config.hdisks > 1) ? 0xf : 0)); + SET_CMOS(CMOS_HD1EXT, 47); + if (config.hdisks == 2) + SET_CMOS(CMOS_HD2EXT, 47); + else + SET_CMOS(CMOS_HD2EXT, 0); + + /* this is the CMOS status */ + SET_CMOS(CMOS_STATUSA, 0x26); + /* default id BCD,24h,no DST */ + SET_CMOS(CMOS_STATUSB, 2); + + /* 0xc and 0xd are read only */ + SET_CMOS(CMOS_STATUSC, 0); + SET_CMOS(CMOS_STATUSD, 0x80); + + SET_CMOS(CMOS_DIAG, 0); + + /* memory counts */ + SET_CMOS(CMOS_BASEMEML, config.mem_size & 0xff); /* base mem LSB */ + SET_CMOS(CMOS_BASEMEMM, config.mem_size >> 8); /* base mem MSB */ + + SET_CMOS(CMOS_EXTMEML, EXTMEM_SIZE & 0xff); + SET_CMOS(CMOS_EXTMEMM, EXTMEM_SIZE >> 8); + + SET_CMOS(CMOS_PEXTMEML, PEXTMEM_SIZE & 0xff); + SET_CMOS(CMOS_PEXTMEMM, PEXTMEM_SIZE >> 8); + + /* say protected mode test 7 passed (?) */ + SET_CMOS(CMOS_SHUTDOWN, 6); + + /* information flags...my CMOS returns this */ + SET_CMOS(CMOS_INFO, 0xe1); + + /* system operational flags (for fast A20 gate) */ + SET_CMOS(CMOS_SYSOP, 0x3f); + + g_printf("CMOS initialized\n"); +} + +void cmos_reset(void) +{ +} diff --git a/src/base/dev/misc/joystick.c b/src/base/dev/misc/joystick.c new file mode 100644 index 0000000..f04c3a8 --- /dev/null +++ b/src/base/dev/misc/joystick.c @@ -0,0 +1,1401 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * DANG_BEGIN_MODULE + * + * This is a complete software emulation of the Game Port and BIOS joystick + * routines for DOSEMU. + * + * If you have set up the joystick in Linux, everything should work like it + * does in real DOS i.e. you can have two joysticks (untested) or one joystick, + * in which this code will do the appropriate (and correct) axis and button + * mapping or of course, no joysticks, which is also supported :). + * + * maintainer: + * Clarence Dang + * + * DANG_END_MODULE + * + * Copyright (c) 2002 Clarence Dang + * (not to be confused with DANG, which is the Dosemu Alterer Novices Guide :) + * + * + * Last Modified: $Date$ (Version 2) + */ + + +/* + * Compile-time Debugging Options + * + * Specify what debugging code you want to compile in (recommended: all). + * You will still need to activate them at runtime (-Dj) for any visible effect. + */ + +/* DEFINE this if you want initialisation messages, errors and warnings */ +#define JOY_INIT_DEBUG + +/* DEFINE this if you want to debug the joystick port emulation code */ +#define JOY_PORT_DEBUG + +/* DEFINE this if you want to debug the BIOS joystick emulation code */ +#define JOY_BIOS_DEBUG + +/* DEFINE this if you want to debug the Linux joystick-interfacing code */ +#define JOY_LINUX_DEBUG + + +/* + * Runtime Debugging (-D[#]j) + * + * 1 = init messages + * 2 = AND port + bios emulation + * 3 = AND linux interfacing + * + */ +#define joy_init_printf(f,a...) j_printf ("JOY: " f,##a) + +#define joy_port_printf(f,a...) if (debug_level ('j') >= 2) j_printf ("JOY: " f,##a) +#define joy_bios_printf(f,a...) if (debug_level ('j') >= 2) j_printf ("JOY: BIOS: " f,##a) + +#define joy_linux_printf(f,a...) if (debug_level ('j') >= 3) j_printf ("JOY: linux: " f,##a) + + +/* + * Includes + */ + +#include "joystick.h" + +#include +#include +#include +#include +#include + +#define USE_PTHREADS 1 + +#ifdef USE_PTHREADS + #include +#endif + +#include + +#include "cpu.h" +#include "emu.h" +#include "iodev.h" + +#include "types.h" +#include "timers.h" + +/* from linux/version.h */ +#define JOY_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + + +/* + * Internal constants + */ + +/* emulated joystick number */ +#define JOY_0 0 +#define JOY_1 1 + +/* joystick axes */ +#define JOY_X 0 +#define JOY_Y 1 + +#define JOY_AXIS_INVALID (-32800) + + +/* + * Internal variables + */ + +/* joystick device */ +static int joy_fd [2]; +static int joy_status = -1; +static int joy_version = -1; +static int joy_latency_us = 0; /* latency in microseconds */ + +/* joystick stats */ +static char joy_numaxes [2], joy_numbuttons [2]; + +/* current state of joystick */ +static int joy_buttons; +static int joy_axis [2][4]; + +/* axis counters for joystick port */ +static int joy_port_x [2], joy_port_y [2]; + +/* axis range for joystick */ +static int joy_dos_range; + +#ifdef USE_PTHREADS + static pthread_t thread [2]; + static pthread_mutex_t joy_buttons_mutex, joy_axis_mutex; + static int joy [2] = {JOY_0, JOY_1}; + static void *joy_linux_thread_read (void *injoynum); +#endif + + +/* + * Some function prototypes + */ + +/* internal interface functions: Linux->DOS */ +static void joy_emu_button_set (const int joynum, const int button, const int pressed); +static void joy_emu_axis_set (const int joynum, const int axis, const int value); +static int joy_emu_axis_conv (const int linux_val, const int invalid_val); + +/* port emulation */ +Bit8u joy_port_inb (ioport_t port, void *arg); +void joy_port_outb (ioport_t port, Bit8u value, void *arg); + + +/* + * DANG_BEGIN_REMARK + * + * We make a runtime decision based on the detected joystick API version + * and #ifdef USE_PTHREADS, on the way in which we obtain the joystick + * status from Linux (a "driver"): + * + * 1. joy_driver_nojoy: simply tells DOS programs that you have no + * joystick(s) + * + * 2. joy_driver_old: uses old, non-blocking joystick API (<1.0.0); + * limited to 2 axes; supported because DOSEMU + * supports old kernels + * + * 3. joy_driver_new: uses new, non-blocking joystick API (>=1.0.0); + * a (little) slower than joy_driver_new_threaded + * + * 4. joy_driver_new_threaded: uses new, BLOCKING joystick API (>=1.0.0); + * efficient but requires pthreads (which + * is known to make DOSEMU unstable!) + * + * The same driver is used for both joysticks. + * + * DANG_END_REMARK + */ + +typedef struct +{ + int (*read_buttons) (void); + int (*read_axis) (const int joynum, const int axis, + const int invalid_val, const int update); + + int linux_min; + int linux_max; + int linux_range; +} JOY_DRIVER; + +/* no joystick */ +static int joy_linux_read_buttons_nojoy (void); +static int joy_linux_read_axis_nojoy (const int joynum, const int axis, + const int invalid_val, const int update); +static JOY_DRIVER joy_driver_nojoy = +{ + joy_linux_read_buttons_nojoy, + joy_linux_read_axis_nojoy, + + 0, + 0, + 0 +}; + + +/* old, non-blocking API (<1.0.0) */ +static int joy_linux_read_buttons_old (void); +static int joy_linux_read_axis_old (const int joynum, const int axis, + const int invalid_val, const int update); +static JOY_DRIVER joy_driver_old = +{ + joy_linux_read_buttons_old, + joy_linux_read_axis_old, + + +1, + +255, + 255 +}; + +/* new, non-blocking API (>=1.0.0) */ +static int joy_linux_read_buttons_new (void); +static int joy_linux_read_axis_new (const int joynum, const int axis, + const int invalid_val, const int update); +static JOY_DRIVER joy_driver_new = +{ + joy_linux_read_buttons_new, + joy_linux_read_axis_new, + + -32767, + +32767, + 65535 +}; + +/* new, BLOCKING API + threading */ +#ifdef USE_PTHREADS +static int joy_linux_read_buttons_new_threaded (void); +static int joy_linux_read_axis_new_threaded (const int joynum, const int axis, + const int invalid_val, const int update); +static JOY_DRIVER joy_driver_new_threaded = +{ + joy_linux_read_buttons_new_threaded, + joy_linux_read_axis_new_threaded, + + -32767, + +32767, + 65535 +}; +#endif + +/* current joystick driver */ +static JOY_DRIVER *joy_driver = &joy_driver_nojoy; +static int joy_driver_decided; + + +/* + * Init functions + */ + +static void joy_driver_set (JOY_DRIVER *driver) +{ + joy_driver = driver; + joy_driver_decided = 1; +} + +void joy_init (void) +{ + emu_iodev_t io_device; + int joynum; + +#ifdef JOY_INIT_DEBUG + #ifdef USE_PTHREADS + joy_init_printf ("joy_init() [thread-able] CALLED!\n"); + #else + joy_init_printf ("joy_init() [unthreaded] CALLED!\n"); + #endif +#endif + + joy_driver_decided = 0; + + + /* ========================= CHECK CONFIG ========================= */ + + + /* check joystick axis range */ + if (config.joy_dos_min <= 0 || + config.joy_dos_max <= config.joy_dos_min || + config.joy_dos_max > 1000) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("WARNING! joystick axis min (%i) and/or max (%i) out of range\n", + config.joy_dos_min, config.joy_dos_max); + joy_init_printf ("--> setting joystick range to hard-coded default 1-150\n"); + #endif + config.joy_dos_min = 1; + config.joy_dos_max = 150; + } + joy_dos_range = config.joy_dos_max - config.joy_dos_min + 1; + + /* check joystick granularity */ + if (config.joy_granularity <= 0 || config.joy_granularity > joy_dos_range) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("WARNING! joystick granularity (%i) out of range\n", + config.joy_granularity); + joy_init_printf ("--> disabling granularity setting\n"); + #endif + config.joy_granularity = 1; + } + + /* check joystick latency */ + if (config.joy_latency < 0 || config.joy_latency > 200) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("WARNING! joystick latency (%i) out of range (0-200ms)\n", + config.joy_latency); + joy_init_printf ("--> disabling latency setting\n"); + #endif + config.joy_latency = 0; + } + joy_latency_us = config.joy_latency * 1000; + +#ifdef JOY_INIT_DEBUG + joy_init_printf ("joystick range : %i-%i (%i)\n", + config.joy_dos_min, config.joy_dos_max, joy_dos_range); + joy_init_printf ("joystick granularity: %i\n", config.joy_granularity); + joy_init_printf ("joystick latency : %ims\n", config.joy_latency); +#endif + + + /* ========================= INIT GLOBALS ========================= */ + + +/* it doesn't matter if we init them here but never end up using them... */ +#ifdef USE_PTHREADS + pthread_mutex_init (&joy_buttons_mutex, NULL); + pthread_mutex_init (&joy_axis_mutex, NULL); +#endif + + /* initial state of buttons (all off) -- 0x0F is _not_ a typo! */ + joy_buttons = 0x0F; + + /* globals had better be initialised before use... */ + for (joynum = 0; joynum < 2; joynum++) + { + int axis; + + joy_fd [joynum] = -1; + + joy_numaxes [joynum] = 0; + joy_numbuttons [joynum] = 0; + + /* set to initial values */ + for (axis = 0; axis < 4; axis++) + joy_axis [joynum][axis] = JOY_AXIS_INVALID; + + joy_port_x [joynum] = joy_port_y [joynum] = -1; + } + + + /* ========================= INIT 2 JOYSTICKS ========================= */ + + + for (joynum = 0; joynum < 2; joynum++) + { + /* does the user even want this joystick? */ + if (!config.joy_device[joynum] || strlen (config.joy_device [joynum]) == 0) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("joystick 0x%X: will not be initialised\n", joynum); + #endif + continue; + } + + /* open joystick device */ + #ifdef USE_PTHREADS + /* + * 1. old joy API (<1.0.0): only supports NONBLOCK no matter what + * 2. new joy API : we default to BLOCKING + */ + joy_fd [joynum] = open (config.joy_device [joynum], O_RDONLY); + #else + /* + * 1. old joy API (<1.0.0): only supports NONBLOCK no matter what + * 2. new joy API : we default to NONBLOCKING (no pthreads) + */ + joy_fd [joynum] = open (config.joy_device [joynum], O_RDONLY | O_NONBLOCK); + #endif + + if (joy_fd [joynum] >= 0) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("joystick 0x%X: opened \"%s\"\n", + joynum, config.joy_device [joynum]); + #endif + } + else + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("joystick 0x%X: ERROR! can't open \"%s\"\n", + joynum, config.joy_device [joynum]); + #endif + continue; /* try to open next joystick */ + } + + /* + * NOTE: this assumes that both joystick devices are using the same API + * -- if they are not, then you probably deserve the code + * malfunctioning :) + */ + if (!joy_driver_decided) /* set by joy_driver_set() */ + { + if (ioctl (joy_fd [joynum], JSIOCGVERSION, &joy_version)) + { + /* the driver is not 1.0.0+ */ + #ifdef JOY_INIT_DEBUG + joy_init_printf ("joystick 0x%X: WARNING! ioctl (version) failed, assuming old joystick API (< 1.0.0)\n", + joynum); + #endif + + joy_version = JOY_VERSION (0,0,0); /* < 1.0.0 :) */ + } + #ifdef JOY_INIT_DEBUG + else + joy_init_printf ("joystick 0x%X: API Version Detected: 0x%06X\n", + joynum, joy_version); + #endif + + if (joy_version < JOY_VERSION (1,0,0)) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("joystick 0x%X: Using OLD joystick API\n", joynum); + #endif + + /* + * To quote linux/Documentation/input/joystick-api.txt: + * + * "The axis values do not have a defined range in the original 0.x + * driver, except for that the values are non-negative." + * + * Hence, we signify that we cannot depend on values being in a + * defined range by setting it to 0. + * + */ + joy_driver_old.linux_range = 0; + + joy_driver_set (&joy_driver_old); + } + else + { + #ifdef USE_PTHREADS + #ifdef JOY_INIT_DEBUG + joy_init_printf ("joystick 0x%X: Using NEW joystick API (threaded)\n", joynum); + #endif + joy_driver_set (&joy_driver_new_threaded); + #endif + + if (!joy_driver_decided) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("joystick 0x%X: Using NEW joystick API (unthreaded)\n", joynum); + #endif + joy_driver_set (&joy_driver_new); + } + } + } /* if (!joy_driver_decided) { */ + + /* get num axis */ + if (ioctl (joy_fd [joynum], JSIOCGAXES, &joy_numaxes [joynum])) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("joystick 0x%X: ERROR! ioctl (num axes) failed, assuming 2\n", joynum); + #endif + joy_numaxes [joynum] = 2; /* assume 2 axes */ + } + + /* get num buttons */ + if (ioctl (joy_fd [joynum], JSIOCGBUTTONS, &joy_numbuttons [joynum])) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("joystick 0x%X: ERROR! ioctl (num buttons) failed, assuming 2\n", joynum); + #endif + joy_numbuttons [joynum] = 2; /* assume 2 buttons */ + } + + #ifdef JOY_INIT_DEBUG + /* print stats */ + joy_init_printf ("joystick 0x%X: %i axes %i buttons\n", + joynum, joy_numaxes [joynum], joy_numbuttons [joynum]); + #endif + + #ifdef USE_PTHREADS + if (joy_driver == &joy_driver_new_threaded) + { + /* start thread to read joystick events */ + if (pthread_create (&thread [joynum], NULL, joy_linux_thread_read, &joy [joynum])) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("joystick 0x%X: ERROR! Cannot create thread! Falling back to unthreaded code!\n", + joynum); + #endif + + /* if this is the 2nd joystick, cancel the 1st's thread */ + if (joynum == JOY_1) + if (joy_fd [JOY_0] >= 0) + pthread_cancel (thread [JOY_0]); + + if (fcntl (joy_fd [joynum], F_SETFL, O_NONBLOCK)) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("joystick 0x%X: ERROR! Cannot switch to NONBLOCKING mode (%i: %s)! " + "Falling back to OLD joystick API which didn't care about blocking.\n", + joynum, errno, strerror (errno)); + #endif + + /* + * To quote linux/Documentation/input/joystick-api.txt again: + * + * "The axis values do not have a defined range in the original + * 0.x driver, except for that the values are non-negative..." + */ + if (joy_version < JOY_VERSION (1,2,8)) + /* cannot depend on values being in a defined range... */ + joy_driver_old.linux_range = 0; /* ...signify with 0 */ + /* + * "1.2.8+ drivers use a fixed range for reporting the values, + * 1 being the minimum, 128 the center, and 255 maximum value." + */ + else + joy_driver_old.linux_range = 255; + + joy_driver_set (&joy_driver_old); + } + else /* successfully switched device to NONBLOCKing mode... */ + joy_driver_set (&joy_driver_new); + } +#if defined(HAVE_PTHREAD_SETNAME_NP) && defined(__GLIBC__) + pthread_setname_np(thread [joynum], "dosemu: joy"); +#endif + } + #endif /* USE_PTHREADS */ + } + + /* no joysticks at all! */ + if (joy_fd [JOY_0] < 0 && joy_fd [JOY_1] < 0) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("WARNING! No joysticks enabled!\n"); + #endif + joy_status = 0; + + /* + * We don't need to register the port - leave it for direct port + * access (Sourceforge Support Request #1178900). If direct port + * access is not used, the DOSEMU port emulation will return the + * correct value of 0xff (port_not_avail_inb()). + */ + return; + } + else + joy_status = 1; + + /* handle joystick port/game card routines */ + /* DANG_FIXTHIS does this code work for ports other than 0x201? + */ + io_device.read_portb = joy_port_inb; + io_device.write_portb = joy_port_outb; + io_device.read_portw = NULL; + io_device.write_portw = NULL; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + io_device.handler_name = "Joystick Port Emulation"; + io_device.start_addr = 0x200; + io_device.end_addr = 0x20F; + + if (port_register_handler (io_device, 0) != 0) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("ERROR! Couldn't register joystick port handler!\n"); + #endif + } +} + +void joy_uninit (void) +{ + int joynum; + +#ifdef JOY_INIT_DEBUG + joy_init_printf ("joy_uninit() CALLED!\n"); +#endif + + joy_status = 0; + + for (joynum = 0; joynum < 2; joynum++) + { + #ifdef USE_PTHREADS + if (joy_driver == &joy_driver_new_threaded && thread [joynum]) { + pthread_cancel (thread [joynum]); + pthread_join (thread [joynum], NULL); + } + #endif + + if (joy_fd [joynum] >= 0) + close (joy_fd [joynum]); + } + +/* these had been initialised _no matter what_ so they can be safely destroyed */ +#ifdef USE_PTHREADS + pthread_mutex_destroy (&joy_axis_mutex); + pthread_mutex_destroy (&joy_buttons_mutex); +#endif +} + +void joy_term (void) +{ + joy_uninit (); +} + +void joy_reset (void) +{ +#ifdef JOY_INIT_DEBUG + joy_init_printf ("joy_reset() CALLED!\n"); +#endif + +/* DANG_FIXTHIS joy_reset() is called immediately after joy_init(), which is rather inconvenient (anyone heard of a port_unregister_handler()?) so we don't bother resetting at all but in the future this could cause problems + */ + +#if 0 + joy_uninit (); + joy_init (); +#endif +} + +/* called by src/base/init/init.c */ +int joy_exist (void) +{ +#ifdef JOY_INIT_DEBUG + if (joy_status == -1) + joy_init_printf ("WARNING! Call to joy_exist() before joy_init()\n"); + + joy_init_printf ("joy_exist() returning value %i\n", joy_status); +#endif + + /* NOTE: if joy_init() hasn't been called, joy_status == -1, + * therefore we still claim that a joystick exists + */ + return joy_status; +} + + +/* + * Emulation helper functions (DOS->Linux) + */ + +/* + * DANG_BEGIN_FUNCTION joy_latency_over + * + * Tells DOSEMU whether or not it is time to update its internal status + * of the joystick (for nonblocking reads only). + * + * DOS programs read/poll from the joystick port hundreds of thousands of + * times per second so the idea is that we really don't need to read from + * Linux for every such query (increasing performance by about 40%) because: + * + * 1. humans are incapable of changing the status of the joystick + * (moving, pressing buttons) more than about 10 times per second + * + * 2. no one will not notice a delay in DOS registering the joystick status + * (if it is in the order of a few milliseconds) + * + * Of course, this means that you should not set joy_latency in dosemu.conf + * to more than 1000/(#times I can press a button/move joy per second * 2), + * unless you want DOSEMU to miss quick axis/button changes and want to + * wait a ridiculous amount of time before DOSEMU registers any changes at + * all. + * + * DANG_END_FUNCTION + */ +static inline int joy_latency_over (void) +{ + static hitimer_t last_update_time = 0; + + if (joy_latency_us) + { + /* hmm, isn't calling gettimeofday() on every joystick read inefficient? */ + hitimer_t current_time = GETusSYSTIME (); + + if (last_update_time == 0 /* first read ever */ || + current_time - last_update_time >= joy_latency_us) + { + last_update_time = current_time; + return 1; /* read from linux - status too old */ + } + else + return 0; /* don't read from linux - status reasonably up-to-date */ + } + /* no latency... */ + else + return 1; /* read from linux */ +} + +/* + * DANG_BEGIN_FUNCTION joy_emu_button_set + * + * Update the button status for each joystick. + * + * We must perform "button mapping" if only the first joystick is enabled + * i.e. we are required to map the "excessive" buttons (>2) from the first + * joystick onto the second: + * + * a) 3rd button of 1st joy --> 1st button of 2nd joy + * + * b) 4th button of 1st joy --> 2nd button of 2nd joy + * + * DANG_END_FUNCTION + */ +static inline void joy_emu_button_set (const int joynum, const int button, const int pressed) +{ + int bnum; + + /* + * DANG_BEGIN_REMARK + * + * if the 2nd joystick is enabled, we ignore any button >= 2 regardless + * of which joystick it is (if it's the 1st, the 2nd joystick would + * overwrite its buttons; if it's the 2nd, it would be out of range) + * + * DANG_END_REMARK + */ + if (joy_fd [JOY_1] >= 0 && button >= 2) + return; + + /* figure out button number in DOS */ + bnum = joynum * 2 + button; + + /* button in range? */ + if (bnum < 4) + { + #ifdef USE_PTHREADS + pthread_mutex_lock (&joy_buttons_mutex); + #endif + + /* NOTE: this seems reversed but is what happens in real DOS! */ + if (pressed) + joy_buttons &= ~(1 << bnum); /* clear bit */ + else + joy_buttons |= (1 << bnum); /* set bit */ + + #ifdef USE_PTHREADS + pthread_mutex_unlock (&joy_buttons_mutex); + #endif + } +} + +/* + * DANG_BEGIN_FUNCTION joy_emu_axis_set + * + * Update the axis status for each joystick. + * + * We must perform "axis mapping" if only the first joystick is enabled + * i.e. we are required to map the "excessive" axes (>2) from the first + * joystick onto the second: + * + * a) 3rd axis of 1st joy --> 2st axis of 2nd joy + * + * b) 4th axis of 1st joy --> 1st axis of 2nd joy + * (yes, these are reversed deliberately because it's what happens in DOS) + * + * DANG_END_FUNCTION + */ +static inline void joy_emu_axis_set (const int joynum, const int axis, const int value) +{ + int anum = axis; + int jnum = joynum; + + /* 0 <= axis number < 4 */ + if (anum >= 4) return; + + /* need to perform axis mapping? */ + if (anum >= 2) + { + /* + * DANG_BEGIN_REMARK + * + * if the 2nd joystick is enabled, we ignore any axis >= 2 regardless + * of which joystick it is (if it's the 1st, the 2nd joystick would + * overwrite its axes; if it's the 2nd, it would be out of range) + * + * DANG_END_REMARK + */ + if (joy_fd [JOY_1] >= 0) return; + + jnum++; + + /* 0 <= virtual joystick number < 2 */ + if (jnum >= 2) return; + + /* basically swap axis 2 and 3 around (happens in real DOS) + * and set it to an axis in range (0 <= a < 2) */ + anum = 3 - anum; + } + +#ifdef USE_PTHREADS + pthread_mutex_lock (&joy_axis_mutex); +#endif + + joy_axis [jnum][anum] = value; + +#ifdef USE_PTHREADS + pthread_mutex_unlock (&joy_axis_mutex); +#endif +} + +/* + * DANG_BEGIN_FUNCTION joy_emu_axis_conv + * + * Convert a Linux joystick axis reading to a DOS one by making use of + * the differences in the allowable range of axis values. + * + * NOTE: I don't know whether or not Linux returns exponential values + * for the joystick but (apparently) DOS programs expect the values to + * be exponential and so if this is to be fixed, it should probably be + * done in this function. + * + * DANG_END_FUNCTION + */ +#define joy_axis_round(num,gran) ((num) - ((num) % (gran))) +static inline int joy_emu_axis_conv (const int linux_val, const int invalid_val) +{ + /* virtual joystick axis doesn't exist? */ + if (linux_val == JOY_AXIS_INVALID) + return invalid_val; + + /* do the conversion if the linux range is known */ + if (joy_driver->linux_range) + { + int ret = (linux_val - joy_driver->linux_min) + * joy_dos_range / joy_driver->linux_range; + return joy_axis_round (ret, config.joy_granularity) + config.joy_dos_min; + } + /* joystick driver <1.2.8 (_if_ using the old API) doesn't have a defined + * range so we can't do an axis conversion */ + else + /* we add 1 in case the result=0, else any program using BIOS reads + * wouldn't detect the joystick if it was in the upper-left corner + */ + return joy_axis_round (linux_val, config.joy_granularity) + 1; +} + + +/* + * Linux joystick reading functions + */ + +/* + * DANG_BEGIN_FUNCTION joy_linux_process_event + * + * Update global joystick status variables given a Linux joystick event. + * + * DANG_END_FUNCTION + */ +static inline void joy_linux_process_event (const int joynum, const struct js_event *event) +{ + if (event->type & JS_EVENT_INIT) + { + #ifdef JOY_LINUX_DEBUG + joy_linux_printf ("joy 0x%X: init\n", joynum); + #endif + /* hmm, what do I do with this? */ + } + + if (event->type & JS_EVENT_BUTTON) + { + #ifdef JOY_LINUX_DEBUG + joy_linux_printf ("joy 0x%X: button %i: value %i\n", + joynum, event->number, event->value); + #endif + joy_emu_button_set (joynum, event->number, event->value); + #ifdef JOY_LINUX_DEBUG + joy_linux_printf ("joy 0x%X: button: status %i\n", + joynum, joy_buttons); + #endif + } + + if (event->type & JS_EVENT_AXIS) + { + #ifdef JOY_LINUX_DEBUG + joy_linux_printf ("joy 0x%X: axis %i: value %i\n", + joynum, event->number, event->value); + #endif + joy_emu_axis_set (joynum, event->number, event->value); + #ifdef JOY_LINUX_DEBUG + joy_linux_printf ("joy 0x%X: axis: status %i\n", + joynum, (event->number >= 4) ? -1234 : joy_axis [joynum][event->number]); + #endif + } + +#ifdef JOY_LINUX_DEBUG + joy_linux_printf ("joy 0x%X: %i msec\n", joynum, event->time); +#endif +} + +/* the thread (if using joy_driver_new_threaded) */ +#ifdef USE_PTHREADS +static void *joy_linux_thread_read (void *injoynum) +{ + int numread; + struct js_event event; + int joynum = *((int *) injoynum); + + for (;;) + { + /* blocking read of joystick */ + numread = read (joy_fd [joynum], &event, 1 * sizeof (struct js_event)); + if (numread <= 0) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("ERROR! Joystick 0x%X read failed unexpectedly (read: %i; %s)!\n", + joynum, numread, strerror (errno)); + #endif + /* we should _NEVER_ get here but if we do, we would rather quit + * (and lose joystick capability), than possibly suck all of the + * user's CPU... */ + return NULL; + } + + joy_linux_process_event (joynum, &event); + } +} +#endif /* USE_PTHREADS */ + +/* + * DANG_BEGIN_FUNCTION joy_linux_read_events + * + * Process the event queue for _both_ linux joysticks using nonblocking + * reads with the new joystick API (joy_driver_new). + * + * This should be done before (well, actually, not before _every_ + * single read -- see joy_latency_over()) the joystick status is returned + * to DOS as all Linux joystick events are queued until they are processed + * and we want to return a reasonably current state of the joystick + * -- not what it was a long time ago. _Both_ joysticks are processed + * here because of axis/button mapping affecting the status of both emulated + * joysticks (what DOS sees). + * + * DANG_END_FUNCTION + */ +static inline void joy_linux_read_events (void) +{ + int joynum; + + for (joynum = 0; joynum < 2; joynum++) + { + int e; + int numread; + + const int maxread = 255; + struct js_event event [255]; + + if (joy_fd [joynum] < 0) continue; + + numread = read (joy_fd [joynum], event, maxread * sizeof (struct js_event)); + + /* nothing read but the queue is not empty? */ + if (numread <= 0 && errno != EAGAIN) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("ERROR! Joystick 0x%X read failed unexpectedly (read: %i; %s)!\n", + joynum, numread, strerror (errno)); + #endif + continue; /* go on to next joystick */ + } + + /* for some reason, sizeof() returns an unsigned... */ + numread /= (int) sizeof (struct js_event); + if (numread > maxread) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("ERROR! Joystick 0x%X read too much (read: %i; %s)!\n", + joynum, numread, strerror (errno)); + #endif + continue; /* go on to next joystick */ + } + + #ifdef JOY_LINUX_DEBUG + joy_linux_printf ("joystick 0x%X: Processing %i event(s)\n", joynum, numread); + #endif + + for (e = 0; e < numread; e++) + joy_linux_process_event (joynum, &event [e]); + } +} + +/* + * DANG_BEGIN_FUNCTION joy_linux_read_status + * + * Read both the current position and current button status of the joystick + * from Linux (joy_driver_old). + * + * DANG_END_FUNCTION + */ +static inline void joy_linux_read_status (void) +{ + int joynum; + for (joynum = 0; joynum < 2; joynum++) + { + struct JS_DATA_TYPE js; + int b; + + if (joy_fd [joynum] < 0) continue; + + /* non-blocking read */ + if (read (joy_fd [joynum], &js, JS_RETURN) != JS_RETURN) + { + #ifdef JOY_INIT_DEBUG + joy_init_printf ("joystick 0x%X: ERROR! joy_read_status(): %s\n", + joynum, strerror (errno)); + #endif + continue; + } + + #ifdef JOY_LINUX_DEBUG + joy_linux_printf ("joy 0x%X: x=%i y=%i buttons=%i\n", + joynum, js.x, js.y, js.buttons); + #endif + + /* perform axis mapping... */ + joy_emu_axis_set (joynum, JOY_X, js.x); + joy_emu_axis_set (joynum, JOY_Y, js.y); + + #ifdef JOY_LINUX_DEBUG + joy_linux_printf ("joy 0x%X: emu-x=%i emu-y=%i\n", + joynum, joy_axis [joynum][JOY_X], joy_axis [joynum][JOY_Y]); + #endif + + /* perform button mapping... */ + for (b = 0; b < 4; b++) + joy_emu_button_set (joynum, b, js.buttons & (1 << b)); + + #ifdef JOY_LINUX_DEBUG + joy_linux_printf ("joy 0x%X: emu-button=%i\n", joynum, joy_buttons); + #endif + } /* for (joynum = 0; joynum < 2; joynum++) { */ +} + +/* + * DANG_BEGIN_FUNCTION joy_linux_read_buttons_(family) + * + * Eventually called from DOS to get the button status of the joysticks. + * The threaded version will simply get the status from global variables. + * The unthreaded versions will perform non-blocking reads. + * + * DANG_END_FUNCTION + */ + +static int joy_linux_read_buttons_nojoy (void) +{ + return 0x0F; +} + +static int joy_linux_read_buttons_old (void) +{ + if (joy_latency_over ()) + joy_linux_read_status (); + + /* ...and return the virtual button reading */ + return joy_buttons; +} + +static int joy_linux_read_buttons_new (void) +{ + if (joy_latency_over ()) + { + /* process all queued Linux joystick events */ + joy_linux_read_events (); + } + + /* ... and return the virtual button reading */ + return joy_buttons; +} + +#ifdef USE_PTHREADS +static int joy_linux_read_buttons_new_threaded (void) +{ + int ret; + + pthread_mutex_lock (&joy_buttons_mutex); + ret = joy_buttons; + pthread_mutex_unlock (&joy_buttons_mutex); + + return ret; +} +#endif + +/* + * DANG_BEGIN_FUNCTION joy_linux_read_axis_(family) + * + * Eventually called from DOS to get the axis status of the joysticks. + * The threaded version will simply get the values from global variables. + * The unthreaded versions will perform non-blocking reads. + * + * arguments: + * invalid_val - value to return to signify a non-existent axis + * + * update - whether DOSEMU should update its internal axis values from Linux + * (for each read of the joystick position, set this flag _only_ + * on the first of the 4 calls to this function unless you want + * the axis positions to be from different points in time :) + * + * DANG_END_FUNCTION + */ + +static int joy_linux_read_axis_nojoy (const int joynum, const int axis, + const int invalid_val, const int update) +{ + return invalid_val; /* I don't have a joystick... */ +} + +static int joy_linux_read_axis_old (const int joynum, const int axis, + const int invalid_val, const int update) +{ + if (update) + { + if (joy_latency_over ()) + joy_linux_read_status (); + } + + /* ...and return the virtual axis reading */ + return (joy_emu_axis_conv (joy_axis [joynum][axis], invalid_val)); +} + +static int joy_linux_read_axis_new (const int joynum, const int axis, + const int invalid_val, const int update) +{ + if (update) + { + if (joy_latency_over ()) + { + /* process all queued Linux joystick events */ + joy_linux_read_events (); + } + } + + /* ... and return the virtual axis reading */ + return (joy_emu_axis_conv (joy_axis [joynum][axis], invalid_val)); +} + +#ifdef USE_PTHREADS +static int joy_linux_read_axis_new_threaded (const int joynum, const int axis, + const int invalid_val, const int update) +{ + int ret; + + pthread_mutex_lock (&joy_axis_mutex); + ret = joy_axis [joynum][axis]; + pthread_mutex_unlock (&joy_axis_mutex); + + return (joy_emu_axis_conv (ret, invalid_val)); +} +#endif + +/* + * Functions that DOSEMU/DOS call + */ + +/* + * DANG_BEGIN_REMARK + * + * Apparently, the Carry Flag is cleared if int15 function 0x84 exists + * and it is set if it doesn't exist. + * + * But what does this mean? Does the existence of such a BIOS function + * depend on the existence of a Game Card/SoundBlaster, or does it just + * mean that there is such an implemented BIOS function, regardless of + * whether or not you have a joystick? + * + * I have never seen a real BIOS set the Carry Flag on this call, even + * on a computer without a joystick -- so to mimic what happens in the + * real world, I just clear the Carry Flag regardless of whether the user + * has a joystick or not. This could be incorrect behaviour so it may + * have to be changed in the future. + * + * DANG_END_REMARK + */ + +#if 0 + #define JOY_BIOS_API_YES NOCARRY + #define JOY_BIOS_API_NO CARRY +#else + #define JOY_BIOS_API_YES NOCARRY + #define JOY_BIOS_API_NO NOCARRY +#endif + +/* + * DANG_BEGIN_FUNCTION joy_bios_read + * + * This is the int15 function 0x84 handler (i.e. BIOS joystick emulation), + * called from src/base/async/int.c. + * + * The real BIOS actually reads its values straight from the joystick port + * (0x201) but we don't bother because we can do it faster :) + * + * Because of this, it returns the joystick axis values with the same + * range as port 0x201 BUT the range for a real BIOS varies between + * computers as it is dependent on how it reads from the port + * (hopefully this won't cause any problems). + * + * DANG_END_FUNCTION + */ +int joy_bios_read (void) +{ + switch (LWORD (edx)) + { + /* read buttons */ + case 0: + { + #ifdef JOY_BIOS_DEBUG + joy_bios_printf ("read buttons\n"); + #endif + + /* + * NOTE: this shift ensures that the bottom nibble is all '0' bits + * so that the game, Earth Invasion, won't hang on startup + */ + LO(ax) = (joy_driver->read_buttons () << 4); + + JOY_BIOS_API_YES; + break; + } + /* read axis */ + case 1: + { + #ifdef JOY_BIOS_DEBUG + joy_bios_printf ("read axis\n"); + #endif + + /* 1st joystick */ + LWORD(eax) = joy_driver->read_axis (JOY_0, JOY_X, 0, 1); /* x */ + LWORD(ebx) = joy_driver->read_axis (JOY_0, JOY_Y, 0, 0); /* y */ + + /* 2nd joystick */ + LWORD(ecx) = joy_driver->read_axis (JOY_1, JOY_X, 0, 0); /* x */ + LWORD(edx) = joy_driver->read_axis (JOY_1, JOY_Y, 0, 0); /* y */ + + JOY_BIOS_API_YES; + break; + } + /* unknown call */ + default: + #ifdef JOY_INIT_DEBUG + /* DANG_FIXTHIS perhaps we should not have been called to start with? + */ + joy_init_printf ("BIOS: ERROR! unknown joystick call %X\n", LWORD(edx)); + #endif + + JOY_BIOS_API_NO; + return 1; + } + + return 0; +} + +/* + * DANG_BEGIN_FUNCTION joy_port_inb + * + * This function emulates reads from the joystick port (0x201) -- this is + * the most common way of detecting and reading the joystick. + * + * The real implementation of this port sets a bit for each axis for a + * certain period of time, corresponding to analog measurements of the + * position of each axis (so "if you count the analog values in software, + * a faster machine yields different values from a slow machine [unless + * you use a timer]" - DOS 6: A Developer's Guide). + * + * In contrast, this implementation sets the bits high for a certain number + * of port reads, corresponding to the position of each axis (independent + * of time). This means that, for most programs, the axis range will be + * that specified in dosemu.conf (which is rather convenient) and avoids + * the issue of super-fast computers causing DOS program axis counters to + * overflow (e.g. in a real system, if the program used an 8-bit variable + * for storing the position of an axis and the system was fast enough to + * read from the port more than 127 or 255 times, there would be trouble). + * + * DANG_END_FUNCTION + */ +Bit8u joy_port_inb (ioport_t port, void *arg) +{ + Bit8u ret = 0; + int joynum; + +#ifdef JOY_INIT_DEBUG + joy_port_printf ("port 0x%X: inb(): %i %i %i %i\n", + port, + joy_port_x [JOY_0], joy_port_y [JOY_0], + joy_port_x [JOY_1], joy_port_y [JOY_1]); +#endif + + /* + * DANG_BEGIN_REMARK + * + * Here we set bits based on joystick axis counters. + * The code here is particularly tricky and if you try to change it, you + * will probably break it :) + * + * DANG_END_REMARK + */ + for (joynum = 0; joynum < 2; joynum++) + { + /* set the bit if we are counting down the axis reading + * OR if it's an invalid axis + */ + if (joy_port_x [joynum]) + { + ret |= (1 << (0 + joynum * 2)); + + /* if (joy_port_x [joynum] == -1) + * it's an invalid axis + * so we don't decrement the counter so we count forever! + */ + if (joy_port_x [joynum] > 0) joy_port_x [joynum]--; + } + + /* set the bit if we are counting down the axis reading + * OR if it's an invalid axis + */ + if (joy_port_y [joynum]) + { + ret |= (1 << (1 + joynum * 2)); + + /* if (joy_port_y [joynum] == -1) + * it's an invalid axis + * so we don't decrement the counter so we count forever! + */ + if (joy_port_y [joynum] > 0) joy_port_y [joynum]--; + } + } + + /* + * DANG_BEGIN_REMARK + * + * Here we read the button status from Linux (programs can read the + * button status from the port, _without_ making a dummy write to the + * port first so the Linux read must be done _here_) and return it. + * + * DANG_END_REMARK + */ + ret |= (joy_driver->read_buttons () << 4); + + return ret; +} + +void joy_port_outb (ioport_t port, Bit8u value, void *arg) +{ +#ifdef JOY_PORT_DEBUG + joy_port_printf ("port 0x%X: outb()\n", port); +#endif + +/* DEFINE this to fake values from the joystick port (for debugging) */ +/*#define TESTCENTRE*/ + +#ifdef TESTCENTRE + { + int x, y; + + FILE *infp = fopen ("/root/.joystick", "rt"); + if (!infp) + { + perror ("why"); + x = y = -1; + } + else + { + fscanf (infp, "%i %i", &x, &y); + fclose (infp); + } + + joy_port_x [JOY_0] = x; + joy_port_y [JOY_0] = y; + joy_port_x [JOY_1] = x; + joy_port_y [JOY_1] = y; + } +#else + /* + * read in joystick position (preparing for joy_port_inb()) + */ + + /* 1st joystick */ + joy_port_x [JOY_0] = joy_driver->read_axis (JOY_0, JOY_X, -1, 1); /* x */ + joy_port_y [JOY_0] = joy_driver->read_axis (JOY_0, JOY_Y, -1, 0); /* y */ + + /* 2nd joystick */ + joy_port_x [JOY_1] = joy_driver->read_axis (JOY_1, JOY_X, -1, 0); /* x */ + joy_port_y [JOY_1] = joy_driver->read_axis (JOY_1, JOY_Y, -1, 0); /* y */ + +#endif +} + +/* end of joystick.c */ diff --git a/src/base/dev/misc/kbd.c b/src/base/dev/misc/kbd.c new file mode 100644 index 0000000..763f386 --- /dev/null +++ b/src/base/dev/misc/kbd.c @@ -0,0 +1,252 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * DANG_BEGIN_MODULE + * + * Description: Keyboard interface + * + * Maintainer: Eric Biederman + * + * REMARK + * This module handles interfacing to the DOS side both on int9/port60h level, + * or on the bios buffer level. + * Keycodes are buffered in a queue, which, however, has limited depth, so it + * shouldn't be used for pasting. + * + * More information about this module is in doc/README.newkbd + * + * /REMARK + * DANG_END_MODULE + * + */ + +#include +#include + +#include "emu.h" +#include "types.h" +#include "keyboard/keyboard.h" +#include "keyboard/keyb_server.h" +#include "keyboard/keyb_clients.h" +#include "bios.h" +#include "pic.h" +#include "cpu.h" +#include "timers.h" + +/* + * Our keyboard clock rate is 27.5KHz. This looks optimal for dosemu, + * even though the real keyboards are usually clocked to <= 20KHz. + * Anyway, 8042 should give an extra delay. + */ +#define KBD_CHAR_PERIOD 400 + +/* If this is set to 1, the server will check whether the BIOS keyboard buffer is + * full. + * This is somewhat inaccurate emulation, as the state of BIOS variables really + * shouldn't affect 'hardware' behaviour, but this seems the only way of knowing how + * fast DOS is processing keystrokes, in particular for pasting. + */ +#define KEYBUF_HACK 0 + +/* the below hack helps MOS to read arrow keys */ +#define USE_KBD_DELAY 1 +#define KBD_PIC_HACK 1 + +/********** QUEUE ***********/ + +/* + * This is the dosemu keyboard queue. + * Each queue entry holds a data structure corresponding to (mostly) + * one keypress or release event. [The exception are the braindead + * 0xe02a / 0xe0aa shift key emulation codes the keyboard processor + * 'decorates' some kinds of keyboard events, which for convenience + * are treated as separate events.] + * Each queue entry holds a up to 4 bytes of raw keycodes for the + * port 60h emulation, along with a 2-byte translated int16h keycode + * and the shift state after this event was processed. + * Note that the bios_key field can be empty (=0), e.g. for shift keys, + * while the raw field should always contain something. + */ + +struct keyboard_queue keyb_queue = { +0, 0, 0, 0 +}; + +static inline Boolean queue_empty(struct keyboard_queue *q) +{ + return (q->head == q->tail); +} + + +int queue_level(struct keyboard_queue *q) +{ + int n; + /* q->tail is the first item to pop + * q->head is the place to write the next item + */ + n = q->head - q->tail; + return (n < 0) ? n + q->size : n; +} + +static inline Boolean queue_full(struct keyboard_queue *q) +{ + return (q->size == 0) || (queue_level(q) == (q->size - 1)); +} + +/* + * this has to work even if the variables are uninitialized! + */ +void clear_queue(struct keyboard_queue *q) +{ + q->head = q->tail = 0; + k_printf("KBD: clear_queue() queuelevel=0\n"); +} + +void write_queue(struct keyboard_queue *q, t_rawkeycode raw) +{ + int qh; + + k_printf("KBD: writing to queue: scan=%08x\n", + (unsigned int)raw); + + if (queue_full(q)) { + /* If the queue is full grow it */ + t_rawkeycode *_new; + int sweep1, sweep2; + _new = malloc(q->size + KEYB_QUEUE_LENGTH); + if (!_new) { + k_printf("KBD: queue overflow!\n"); + return; + } + k_printf("KBD: resize queue %d->%d head=%d tail=%d level=%d\n", + q->size, q->size + KEYB_QUEUE_LENGTH, q->head, q->tail, queue_level(q)); + if (q->tail <= q->head) { + sweep1 = q->head - q->tail; + sweep2 = 0; + } else { + sweep1 = q->size - q->tail; + sweep2 = q->head; + + } + if (q->queue) { + memcpy(_new, q->queue + q->tail, sweep1); + memcpy(_new + sweep1, q->queue, sweep2); + free(q->queue); + } + q->tail = 0; + q->head = sweep1 + sweep2; + q->size += KEYB_QUEUE_LENGTH; + q->queue = _new; + } + qh = q->head; + if (++qh == q->size) + qh = 0; + if (qh == q->tail) { + k_printf("KBD: queue overflow!\n"); + return; + } + q->queue[q->head] = raw; + q->head = qh; + k_printf("KBD: queuelevel=%d\n", queue_level(q)); +} + + + +t_rawkeycode read_queue(struct keyboard_queue *q) +{ + t_rawkeycode *qp; + t_rawkeycode raw = 0; + + if (!queue_empty(q)) { + qp = &q->queue[q->tail]; + + raw = *qp; + if (++q->tail == q->size) q->tail = 0; + } + return raw; +} + +/****************** END QUEUE *******************/ + +#if USE_KBD_DELAY +static int kbd_period_elapsed(void) +{ + static hitimer_t kbd_time = 0; + hitimer_t delta = GETusTIME(0) - kbd_time; + if (delta >= KBD_CHAR_PERIOD) { + kbd_time = GETusTIME(0); + return 1; + } + return 0; +} +#endif + +/****************** KEYBINT MODE BACKEND *******************/ + +/* run the queue backend in keybint=on mode + * called either periodically from keyb_server_run or, for faster response, + * when writing to the queue and after the IRQ1 handler is finished. + */ +void int_check_queue(void) +{ + t_rawkeycode rawscan; + +#if 0 + k_printf("KBD: int_check_queue(): queue_empty=%d port60_ready=%d\n", + queue_empty(&keyb_queue), port60_ready); +#endif + + if (queue_empty(&keyb_queue)) + return; + +#if 1 + if (port60_ready) { + k_printf("KBD: port60 still has data\n"); + return; + } +#endif + +#if KEYBUF_HACK + if (bios_keybuf_full() && !(READ_BYTE(BIOS_KEYBOARD_FLAGS2) & PAUSE_MASK)) + return; +#endif + +#if USE_KBD_DELAY + if (!kbd_period_elapsed()) + return; +#endif + +#if KBD_PIC_HACK + /* HACK - extra sentinel needed, timing is not + * a reliable measure under heavy loads */ + if (pic_irq_active(1)) + return; +#endif + + rawscan = read_queue(&keyb_queue); + k_printf("KBD: read queue: raw=%02x, queuelevel=%d\n", + rawscan, queue_level(&keyb_queue)); + output_byte_8042(rawscan); +} + +/******************* GENERAL ********************************/ + + +void backend_run(void) +{ + int_check_queue(); +} + + +void backend_reset(void) +{ + clear_queue(&keyb_queue); + +/* initialise keyboard-related BIOS variables */ + + clear_bios_keybuf(); +} diff --git a/src/base/dev/misc/lpt.c b/src/base/dev/misc/lpt.c new file mode 100644 index 0000000..98933b0 --- /dev/null +++ b/src/base/dev/misc/lpt.c @@ -0,0 +1,359 @@ +/* for the Linux dos emulator versions 0.49 and newer + * + */ +#define LPT_C 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "emu.h" +#include "bios.h" +#include "port.h" +#include "timers.h" +#include "lpt.h" +#include "utilities.h" +#include "dos2linux.h" +#include "ioselect.h" + +/* status bits, Centronics */ +#define CTS_STAT_NOIOERR LPT_STAT_NOIOERR +#define CTS_STAT_ONLINE LPT_STAT_ONLINE +#define CTS_STAT_NOPAPER LPT_STAT_NOPAPER +#define CTS_STAT_NOT_ACKing LPT_STAT_NOT_ACK +#define CTS_STAT_BUSY LPT_STAT_NOT_BUSY + +/* control bits, Centronics */ +#define CTS_CTRL_NOT_SELECT LPT_CTRL_SELECT +#define CTS_CTRL_NOT_INIT LPT_CTRL_NOT_INIT +#define CTS_CTRL_NOT_AUTOLF LPT_CTRL_AUTOLF +#define CTS_CTRL_NOT_STROBE LPT_CTRL_STROBE + +/* inversion masks to convert LPT<-->Centronics */ +#define LPT_STAT_INV_MASK (CTS_STAT_BUSY) +#define LPT_CTRL_INV_MASK (CTS_CTRL_NOT_STROBE | CTS_CTRL_NOT_AUTOLF | \ + CTS_CTRL_NOT_SELECT) + +#define DEFAULT_STAT (CTS_STAT_ONLINE | CTS_STAT_NOIOERR | \ + CTS_STAT_NOT_ACKing | LPT_STAT_NOT_IRQ) +#define DEFAULT_CTRL (CTS_CTRL_NOT_INIT | CTS_CTRL_NOT_AUTOLF | \ + CTS_CTRL_NOT_STROBE) + +#define NUM_PRINTERS 9 +static struct printer lpt[NUM_PRINTERS] = +{ + {NULL, NULL, 5, 0x378, .control = DEFAULT_CTRL, .status = DEFAULT_STAT}, + {NULL, NULL, 5, 0x278, .control = DEFAULT_CTRL, .status = DEFAULT_STAT}, + {NULL, NULL, 10, 0x3bc, .control = DEFAULT_CTRL, .status = DEFAULT_STAT} +}; + +ioport_t get_lpt_base(int lptnum) +{ + if (lptnum >= NUM_LPTS) + return -1; + return lpt[lptnum].base_port; +} + +static int get_printer(ioport_t port) +{ + int i; + for (i = 0; i < NUM_LPTS; i++) + if (lpt[i].base_port <= port && port <= lpt[i].base_port + 2) + return i; + return -1; +} + +static Bit8u printer_io_read(ioport_t port, void *arg) +{ + int i = get_printer(port); + Bit8u val; + + if (i == -1) + return 0xff; + + switch (port - lpt[i].base_port) { + case 0: + val = lpt[i].data; /* simple unidirectional port */ + if (debug_level('p') >= 5) + p_printf("LPT%d: Reading data byte %#x\n", i+1, val); + break; + case 1: /* status port, r/o */ + val = lpt[i].status ^ LPT_STAT_INV_MASK; + /* we should really set ACK after 5 us but here we just + use the fact that the BIOS only checks this once */ + lpt[i].status |= CTS_STAT_NOT_ACKing | LPT_STAT_NOT_IRQ; + lpt[i].status &= ~CTS_STAT_BUSY; + if (debug_level('p') >= 5) + p_printf("LPT%d: Reading status byte %#x\n", i+1, val); + break; + case 2: + val = lpt[i].control ^ LPT_CTRL_INV_MASK; + if (debug_level('p') >= 5) + p_printf("LPT%d: Reading control byte %#x\n", i+1, val); + break; + default: + val = 0xff; + break; + } + return val; +} + +static void printer_io_write(ioport_t port, Bit8u value, void *arg) +{ + int i = get_printer(port); + if (i == -1) + return; + switch (port - lpt[i].base_port) { + case 0: + if (debug_level('p') >= 5) + p_printf("LPT%d: Writing data byte %#x\n", i+1, value); + lpt[i].data = value; + break; + case 1: /* status port, r/o */ + break; + case 2: + if (debug_level('p') >= 5) + p_printf("LPT%d: Writing control byte %#x\n", i+1, value); + value ^= LPT_CTRL_INV_MASK; // convert to Centronics + if (((lpt[i].control & (CTS_CTRL_NOT_STROBE | CTS_CTRL_NOT_SELECT)) == 0) + && (value & CTS_CTRL_NOT_STROBE)) { + /* STROBE rising */ + if (debug_level('p') >= 9) + p_printf("LPT%d: STROBE, sending %#x (%c)\n", i+1, lpt[i].data, + lpt[i].data); + printer_write(i, lpt[i].data); + lpt[i].status &= ~CTS_STAT_NOT_ACKing; + lpt[i].status |= CTS_STAT_BUSY; + } + lpt[i].control = value; + break; + } +} + +static int dev_printer_open(int prnum) +{ + lpt[prnum].dev_fd = open(lpt[prnum].dev, O_WRONLY); + if (lpt[prnum].dev_fd == -1) { + error("LPT%i: error opening %s: %s\n", prnum+1, lpt[prnum].dev, + strerror(errno)); + return -1; + } + p_printf("LPT: opened printer %d to %s\n", prnum, lpt[prnum].dev); + return 0; +} + +static void pipe_callback(int fd, void *arg) +{ + char buf[1024]; + int num = (long)arg; + int n = read(lpt[num].file.from_child, buf, sizeof(buf)); + if (n > 0) { + buf[n] = 0; + error("LPT%i: %s\n", num+1, buf); + } + ioselect_complete(fd); +} + +static int pipe_printer_open(int prnum) +{ + int err; + err = popen2(lpt[prnum].prtcmd, &lpt[prnum].file); + if (err) { + error("system(\"%s\") in lpt.c failed, cannot print! " + "Command returned error %s\n", lpt[prnum].prtcmd, strerror(errno)); + return err; + } + p_printf("LPT: doing printer command ..%s..\n", lpt[prnum].prtcmd); + add_to_io_select(lpt[prnum].file.from_child, pipe_callback, (void *)(long)prnum); + return err; +} + +int printer_open(int prnum) +{ + int rc; + + if (!lpt[prnum].initialized) + return -1; + if (lpt[prnum].opened) { + dosemu_error("opening printer %i twice\n", prnum); + return 0; + } + + rc = lpt[prnum].fops.open(prnum); + if (!rc) + lpt[prnum].opened = 1; + else + error("Error opening printer %i\n", prnum); + return rc; +} + +static int dev_printer_close(int prnum) +{ + return close(lpt[prnum].dev_fd); +} + +static int pipe_printer_close(int prnum) +{ + remove_from_io_select(lpt[prnum].file.from_child); + return pclose2(&lpt[prnum].file); +} + +int printer_close(int prnum) +{ + if (lpt[prnum].opened && lpt[prnum].fops.close) { + p_printf("LPT%i: closing printer\n", prnum+1); + lpt[prnum].fops.close(prnum); + lpt[prnum].remaining = 0; + } + lpt[prnum].opened = 0; + return 0; +} + +static int dev_printer_write(int prnum, Bit8u outchar) +{ + return write(lpt[prnum].dev_fd, &outchar, 1); +} + +static int pipe_printer_write(int prnum, Bit8u outchar) +{ + return write(lpt[prnum].file.to_child, &outchar, 1); +} + +int printer_write(int prnum, Bit8u outchar) +{ + if (!lpt[prnum].initialized) + return -1; + if (!lpt[prnum].opened) + printer_open(prnum); + lpt[prnum].remaining = lpt[prnum].delay; + if (debug_level('p') >= 9) + p_printf("LPT%d: writing %#x (%c)\n", prnum+1, outchar, outchar); + return lpt[prnum].fops.write(prnum, outchar); +} + +/* DANG_BEGIN_FUNCTION printer_init + * + * description: + * Initialize printer control structures + * + * DANG_END_FUNCTIONS + */ +static struct p_fops dev_pfops = +{ + dev_printer_open, + dev_printer_write, + dev_printer_close, +}; +static struct p_fops pipe_pfops = +{ + pipe_printer_open, + pipe_printer_write, + pipe_printer_close, +}; + +void +printer_init(void) +{ + int i; + emu_iodev_t io_device; + + io_device.read_portb = printer_io_read; + io_device.write_portb = printer_io_write; + io_device.read_portw = NULL; + io_device.write_portw = NULL; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + io_device.handler_name = "Parallel printer"; + + for (i = 0; i < NUM_PRINTERS; i++) { + lpt[i].initialized = 0; + lpt[i].opened = 0; + lpt[i].remaining = 0; /* mark not accessed yet */ + if (!lpt[i].dev && !lpt[i].prtcmd) + continue; + p_printf("LPT%i: initializing printer %s\n", i+1, + lpt[i].dev ? lpt[i].dev : lpt[i].prtcmd); + if (lpt[i].dev) + lpt[i].fops = dev_pfops; + else if (lpt[i].prtcmd) + lpt[i].fops = pipe_pfops; + if (i >= _min(config.num_lpt, NUM_LPTS)) lpt[i].base_port = 0; + + if (lpt[i].base_port != 0) { + io_device.start_addr = lpt[i].base_port; + io_device.end_addr = lpt[i].base_port + 2; + port_register_handler(io_device, 0); + } + lpt[i].initialized = 1; + } +} + +void +close_all_printers(void) +{ + int loop; + + for (loop = 0; loop < NUM_PRINTERS; loop++) { + if (!lpt[loop].opened) + continue; + p_printf("LPT: closing printer %d (%s)\n", loop, + lpt[loop].dev ? lpt[loop].dev : lpt[loop].prtcmd); + printer_close(loop); + } +} + +int +printer_tick(u_long secno) +{ + int i; + + for (i = 0; i < NUM_PRINTERS; i++) { + if (lpt[i].remaining > 0) { + if (debug_level('p') >= 9) + p_printf("LPT%i: doing tick %d\n", i+1, lpt[i].remaining); + if (lpt[i].remaining) { + reset_idle(2); + lpt[i].remaining--; + if (!lpt[i].remaining) + printer_close(i); + } + } + } + return 0; +} + +void printer_config(int prnum, struct printer *pptr) +{ + struct printer *destptr; + + if (prnum < NUM_PRINTERS) { + destptr = &lpt[prnum]; + destptr->prtcmd = pptr->prtcmd; + destptr->dev = pptr->dev; + destptr->delay = pptr->delay; + } +} + +void printer_print_config(int prnum, void (*print)(const char *, ...)) +{ + struct printer *pptr = &lpt[prnum]; + (*print)("LPT%d command \"%s\" timeout %d device \"%s\" baseport 0x%03x\n", + prnum+1, (pptr->prtcmd ? pptr->prtcmd : ""), pptr->delay, + (pptr->dev ? pptr->dev : ""), pptr->base_port); +} + +int lpt_get_max(void) +{ + return NUM_PRINTERS; +} + +int lpt_is_configured(int num) +{ + return lpt[num].initialized; +} diff --git a/src/base/dev/misc/pci.c b/src/base/dev/misc/pci.c new file mode 100644 index 0000000..5ccfae6 --- /dev/null +++ b/src/base/dev/misc/pci.c @@ -0,0 +1,608 @@ +/* + * SIDOC_BEGIN_MODULE + * + * Description: PCI configuration space access + * (only configuration mechanism 1 is fully implemented so far) + * used by the console video drivers and the PCI BIOS + * + * SIDOC_END_MODULE + */ + +#include +#include +#include +#include +#include "emu.h" +#include "port.h" +#include "pci.h" + +/* SIDOC_BEGIN_FUNCTION pci_read_header + * + * Use standard 32-bit (type 1) access method to read PCI + * configuration space data + * + * SIDOC_END_FUNCTION + */ +/* + * from PCIUTILS and Linux kernel arch/i386/pci/direct.c: + * + * Before we decide to use direct hardware access mechanisms, we try to do some + * trivial checks to ensure it at least _seems_ to be working -- we just test + * whether bus 00 contains a host bridge (this is similar to checking + * techniques used in XFree86, but ours should be more reliable since we + * attempt to make use of direct access hints provided by the PCI BIOS). + * + * This should be close to trivial, but it isn't, because there are buggy + * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. + */ + +static int +intel_sanity_check(struct pci_funcs *m) +{ + int dev; + + Z_printf("PCI: ...sanity check for mechanism %s ", m->name); + for(dev = 0; dev < 32; dev++) { + uint16_t cls, vendor; + cls = m->read(0, dev, 0, PCI_CLASS_DEVICE, 2); + if (cls == PCI_CLASS_BRIDGE_HOST || cls == PCI_CLASS_DISPLAY_VGA) + break; + vendor = m->read(0, dev, 0, PCI_VENDOR_ID, 2); + if (vendor == PCI_VENDOR_ID_INTEL || vendor == PCI_VENDOR_ID_COMPAQ) + break; + } + if (dev < 32) { + Z_printf("succeeded for dev=%x\n", dev); + return 1; + } + Z_printf("not succeeded\n"); + return 0; +} + +static int pci_no_open(unsigned char bus, unsigned char device, + unsigned char fn) +{ + return -1; +} + +/* only called from pci bios init */ +static int pci_read_header_cfg1 (unsigned char bus, unsigned char device, + unsigned char fn, unsigned int *buf) +{ + int i; + unsigned long bx = ((fn&7)<<8) | ((device&31)<<11) | (bus<<16) | + PCI_EN; + + + if (priv_iopl(3)) { + error("iopl(): %s\n", strerror(errno)); + return 0; + } + /* Get only first 64 bytes: See src/linux/drivers/pci/proc.c for + why. They are not joking. My NCR810 crashes the machine on read + of register 0xd8 */ + for (i=0; i<16; i++) { + port_real_outd (PCI_CONF_ADDR, bx|(i<<2)); + buf[i] = port_real_ind (PCI_CONF_DATA); + } + port_real_outd (PCI_CONF_ADDR, 0); + priv_iopl(0); + return 0; +} + +static unsigned long pci_read_cfg1 (unsigned char bus, unsigned char device, + unsigned char fn, unsigned long reg, int len) +{ + unsigned long val; + unsigned long bx = ((fn&7)<<8) | ((device&31)<<11) | (bus<<16) | + PCI_EN; + + if (priv_iopl(3)) { + error("iopl(): %s\n", strerror(errno)); + return 0; + } + port_real_outd (PCI_CONF_ADDR, bx | (reg & ~3)); + if (len == 1) + val = port_real_inb (PCI_CONF_DATA + (reg & 3)); + else if (len == 2) + val = port_real_inw (PCI_CONF_DATA + (reg & 2)); + else + val = port_real_ind (PCI_CONF_DATA); + port_real_outd (PCI_CONF_ADDR, 0); + priv_iopl(0); + return val; +} + +static void pci_write_cfg1 (unsigned char bus, unsigned char device, + unsigned char fn, unsigned long reg, unsigned long val, int len) +{ + unsigned long bx = ((fn&7)<<8) | ((device&31)<<11) | (bus<<16) | + PCI_EN; + + if (priv_iopl(3)) { + error("iopl(): %s\n", strerror(errno)); + return; + } + port_real_outd (PCI_CONF_ADDR, bx | (reg & ~3)); + if (len == 1) + port_real_outb (PCI_CONF_DATA + (reg & 3), val); + else if (len == 2) + port_real_outw (PCI_CONF_DATA + (reg & 2), val); + else + port_real_outd (PCI_CONF_DATA, val); + port_real_outd (PCI_CONF_ADDR, 0); + priv_iopl(0); +} + +/* only called from pci bios init */ +static int pci_check_device_present_cfg1(unsigned char bus, unsigned char device, + unsigned char fn) +{ + unsigned long val; + unsigned long bx = ((fn&7)<<8) | ((device&31)<<11) | (bus<<16) | + PCI_EN; + + if (priv_iopl(3)) { + error("iopl(): %s\n", strerror(errno)); + return 0; + } + port_real_outd (PCI_CONF_ADDR, bx); + val = port_real_ind (PCI_CONF_DATA); + port_real_outd (PCI_CONF_ADDR, 0); + priv_iopl(0); + + if (val == 0xFFFFFFFF) + return 0 ; + else + return 1; +} + +/* only called from pci bios init */ +static int pci_read_header_cfg2 (unsigned char bus, unsigned char device, + unsigned char fn, unsigned int *buf) +{ + int i; + + if (priv_iopl(3)) { + error("iopl(): %s\n", strerror(errno)); + return 0; + } + port_real_outb(PCI_MODE2_ENABLE_REG, (fn << 1) | 0xF0); + port_real_outb(PCI_MODE2_FORWARD_REG, bus); + for (i=0; i<16; i++) { + buf[i] = port_real_ind (0xc000 | (device << 8) | (i << 2)); + } + port_real_outb(PCI_MODE2_ENABLE_REG, 0x00); + priv_iopl(0); + return 0; +} + +static unsigned long pci_read_cfg2 (unsigned char bus, unsigned char device, + unsigned char fn, unsigned long num, int len) +{ + unsigned long val; + + if (priv_iopl(3)) { + error("iopl(): %s\n", strerror(errno)); + return 0; + } + port_real_outb(PCI_MODE2_ENABLE_REG, (fn << 1) | 0xF0); + port_real_outb(PCI_MODE2_FORWARD_REG, bus); + if (len == 1) + val = port_real_inb (0xc000 | (device << 8) | num); + else if (len == 2) + val = port_real_inw (0xc000 | (device << 8) | num); + else + val = port_real_ind (0xc000 | (device << 8) | num); + port_real_outb(PCI_MODE2_ENABLE_REG, 0x00); + priv_iopl(0); + return val; +} + +static void pci_write_cfg2 (unsigned char bus, unsigned char device, + unsigned char fn, unsigned long num, unsigned long val, int len) +{ + if (priv_iopl(3)) { + error("iopl(): %s\n", strerror(errno)); + return; + } + port_real_outb(PCI_MODE2_ENABLE_REG, (fn << 1) | 0xF0); + port_real_outb(PCI_MODE2_FORWARD_REG, bus); + if (len == 1) + port_real_outb (0xc000 | (device << 8) | num, val); + else if (len == 2) + port_real_outw (0xc000 | (device << 8) | num, val); + else + port_real_outd (0xc000 | (device << 8) | num, val); + port_real_outb(PCI_MODE2_ENABLE_REG, 0x00); + priv_iopl(0); +} + +/* only called from pci bios init */ +static int pci_check_device_present_cfg2(unsigned char bus, unsigned char device, + unsigned char fn) +{ + unsigned long val; + + if (priv_iopl(3)) { + error("iopl(): %s\n", strerror(errno)); + return 0; + } + port_real_outb(PCI_MODE2_ENABLE_REG,0xF1); + port_real_outb(PCI_MODE2_FORWARD_REG,bus); + val = port_real_ind ((device << 8)); + port_real_outb(PCI_MODE2_ENABLE_REG, 0x00); + priv_iopl(0); + + if (val == 0xFFFFFFFF || val == 0xf0f0f0f0) + return 0 ; + else + return 1; +} + +static int pci_open_proc(unsigned char bus, unsigned char device, + unsigned char fn) +{ + static char proc_pci_name_buf[] = "/proc/bus/pci/00/00.0"; + int fd; + + PRIV_SAVE_AREA + snprintf(proc_pci_name_buf + 14, sizeof(proc_pci_name_buf) - 14, + "%02hhx/%02hhx.%.1hhx", bus, device, fn); + Z_printf("PCI: opening %s\n", proc_pci_name_buf); + enter_priv_on(); + fd = open(proc_pci_name_buf, O_RDWR); + if (fd == -1) + fd = open(proc_pci_name_buf, O_RDONLY); + leave_priv_setting(); + return fd; +} + +/* only called from pci bios init */ +static int pci_read_header_proc (unsigned char bus, unsigned char device, + unsigned char fn, unsigned int *buf) +{ + int fd = pci_open_proc(bus, device, fn); + if (fd == -1) + return 0; + + /* Get only first 64 bytes: See src/linux/drivers/pci/proc.c for + why. They are not joking. My NCR810 crashes the machine on read + of register 0xd8 */ + + read(fd, buf, 64); + close(fd); + return 0; +} + +static unsigned long pci_read_proc (unsigned char bus, unsigned char device, + unsigned char fn, unsigned long reg, int len) +{ + unsigned long val; + int fd = pci_open_proc(bus, device, fn); + if (fd == -1) + return 0; + Z_printf("PCI: reading reg %ld\n", reg); + pread(fd, &val, len, reg); + close(fd); + return val; +} + +static void pci_write_proc (unsigned char bus, unsigned char device, + unsigned char fn, unsigned long reg, unsigned long val, int len) +{ + int fd = pci_open_proc(bus, device, fn); + if (fd == -1) + return; + Z_printf("PCI: writing reg %ld\n", reg); + pwrite(fd, &val, len, reg); + close(fd); +} + +/* only called from pci bios init */ +static int pci_check_device_present_proc(unsigned char bus, unsigned char device, + unsigned char fn) +{ + int fd = pci_open_proc(bus, device, fn); + if (fd == -1) + return 0; + close(fd); + return 1; +} + +static struct pci_funcs pci_cfg1 = { + "1", + pci_no_open, + pci_read_cfg1, + pci_write_cfg1, + pci_read_header_cfg1, + pci_check_device_present_cfg1 +}; + +static struct pci_funcs pci_cfg2 = { + "2", + pci_no_open, + pci_read_cfg2, + pci_write_cfg2, + pci_read_header_cfg2, + pci_check_device_present_cfg2 +}; + +static struct pci_funcs pci_proc = { + "/proc", + pci_open_proc, + pci_read_proc, + pci_write_proc, + pci_read_header_proc, + pci_check_device_present_proc +}; + +/* + * Return NULL if no PCI is present. + */ +struct pci_funcs *pci_check_conf(void) +{ + unsigned long val; + struct pci_funcs *m; + + if (!config.pci && access("/proc/bus/pci", R_OK) == 0) + return &pci_proc; + + if (!can_do_root_stuff) + return NULL; + if (priv_iopl(3)) { + error("iopl(): %s\n", strerror(errno)); + return NULL; + } + + m = &pci_cfg1; + port_real_outd(PCI_CONF_ADDR,PCI_EN); + val = port_real_ind(PCI_CONF_ADDR); + port_real_outd(PCI_CONF_ADDR, 0); + + if (val != PCI_EN) { + /* from PCIUTILS: */ + /* This is ugly and tends to produce false positives. Beware. */ + + port_real_outb(PCI_MODE2_ENABLE_REG, 0x00); + port_real_outb(PCI_MODE2_FORWARD_REG, 0x00); + m = (port_inb(PCI_MODE2_ENABLE_REG) == 0x00 && + port_inb(PCI_MODE2_FORWARD_REG) == 0x00) ? &pci_cfg2 : NULL; + } + + priv_iopl(0); + if (m && intel_sanity_check(m)) + return m; + return NULL; +} + +static Bit8u pci_port_inb(ioport_t port, void *arg) +{ + if (port != 0xcf9) + return std_port_inb(port); + return 0; +} + +static void pci_port_outb(ioport_t port, Bit8u byte, void *arg) +{ + /* don't allow DOSEMU to reset the CPU */ + if (port != 0xcf9) + std_port_outb(port, byte); +} + +/* SIDOC_BEGIN_FUNCTION pci_setup + * + * Register standard PCI ports 0xcf8-0xcff + * + * SIDOC_END_FUNCTION + */ +int pci_setup (void) +{ + emu_iodev_t io_device = {}; + + if (config.pci) { + pcibios_init(); + /* register PCI ports */ + io_device.read_portb = pci_port_inb; + io_device.write_portb = pci_port_outb; + + if (pciConfigType->name[0] == '1') { + io_device.handler_name = "PCI Config Type 1"; + io_device.start_addr = PCI_CONF_ADDR; + io_device.end_addr = PCI_CONF_DATA+3; + port_register_handler(io_device, 0); + } else { + io_device.handler_name = "PCI Config Type 2"; + io_device.start_addr = PCI_MODE2_ENABLE_REG; + io_device.end_addr = PCI_MODE2_FORWARD_REG + 1; + port_register_handler(io_device, 0); + io_device.start_addr = 0xc000; + io_device.end_addr = 0xcfff; + port_register_handler(io_device, 0); + } + } + return 0; +} + +/* basic r/o PCI emulation, enough for VGA-style classes */ + +/* sets the pci record if the bdf changes */ +static pciRec *set_pcirec(unsigned short bdf) +{ + /* cached pci record */ + static pciRec *pcirec; + pciRec *pci = pcirec; + + if (pci == NULL || bdf != pci->bdf) { + pci = pcibios_find_bdf(bdf); + if(pci != NULL) + pcirec = pci; + } + return pci; +} + +static unsigned long current_pci_reg; + +static unsigned long pciemu_port_read(ioport_t port, int len) +{ + unsigned long val; + unsigned short bdf; + pciRec *pci; + unsigned char num; + + if (!(current_pci_reg & PCI_EN)) + return 0xffffffff; + + bdf = (current_pci_reg >> 8) & 0xffff; + pci = set_pcirec(bdf); + if (pci == NULL) + return 0xffffffff; + /* values >= 0x40 have to go directly to the hardware. + They can be dangerous so are only allowed if pci->ext_enabled + the Linux kernel only lets us read if num < 64 + unless we're root (even if the fd was opened as root) + */ + num = current_pci_reg & 0xff; + if (num < 0x40) { + val = pci->header[num >> 2]; + if (len == 1) + val = (val >> ((port & 3) << 3)) & 0xff; + else if (len == 2) + val = (val >> ((port & 2) << 3)) & 0xffff; + } else if (pci->ext_enabled) { + pci_port_outd(PCI_CONF_ADDR, current_pci_reg); + val = len == 1 ? std_port_inb(port) : len == 2 ? std_port_inw(port) : + std_port_ind(port); + } else + val = 0xffffffff; + Z_printf("PCIEMU: reading 0x%lx from %#x, len=%d\n",val,num,len); + return val; +} + +static void pciemu_port_write(ioport_t port, unsigned long val, int len) +{ + unsigned char num; + unsigned short bdf; + pciRec *pci; + + if (!(current_pci_reg & PCI_EN) || current_pci_reg == PCI_EN) + return; + + bdf = (current_pci_reg >> 8) & 0xffff; + pci = set_pcirec(bdf); + if (pci == NULL) + return; + /* values >= 0x40 have to go directly to the hardware. + They can be dangerous so are only allowed if pci->ext_enabled */ + num = current_pci_reg & 0xff; + if (num < 0x40) { + unsigned long value = pci->header[num >> 2]; + int shift = (port & (4 - len)) << 3; + if (len == 1) + value &= ~(unsigned long)(0xff << shift); + else if (len == 2) + value &= ~(unsigned long)(0xffff << shift); + val = value | (val << shift); + if ((pci->header[3] & 0x007f0000) == 0) { + if (num >= PCI_BASE_ADDRESS_0 && num <= PCI_BASE_ADDRESS_5) + val &= pci->region[num - PCI_BASE_ADDRESS_0].rawsize; + if (num == PCI_ROM_ADDRESS) + val &= pci->region[6].rawsize; + } + pci->header[num >> 2] = val; + } else if (pci->ext_enabled) { + pci_port_outd(PCI_CONF_ADDR, current_pci_reg); + if (len == 1) + std_port_outb(port, val); + else if (len == 2) + std_port_outw(port, val); + else + std_port_outd(port, val); + } + Z_printf("PCIEMU: writing 0x%lx to %#x, len=%d\n",val,num,len); +} + +static Bit8u pciemu_port_inb(ioport_t port, void *arg) +{ + /* 0xcf8 -- 0xcfb as bytes or words don't access sub-parts; + for instance writing to 0xcf9 as a byte may reset the CPU */ + if (port == 0xcf9) /* TURBO/RESET control register */ + return 0; + if (port >= PCI_CONF_DATA) + return pciemu_port_read(port, 1); + return 0xff; +} + +static void pciemu_port_outb(ioport_t port, Bit8u byte, void *arg) +{ + if (port >= PCI_CONF_DATA) + pciemu_port_write(port, byte, 1); +} + +static Bit16u pciemu_port_inw(ioport_t port, void *arg) +{ + if (port == PCI_CONF_DATA || port == PCI_CONF_DATA + 2) + return pciemu_port_read(port, 2); + return 0xffff; +} + +static void pciemu_port_outw(ioport_t port, Bit16u value, void *arg) +{ + if (port == PCI_CONF_DATA || port == PCI_CONF_DATA + 2) + pciemu_port_write(port, value, 2); +} + +static Bit32u pciemu_port_ind(ioport_t port, void *arg) +{ + if (port == PCI_CONF_DATA) + return pciemu_port_read(port, 4); + if (port == PCI_CONF_ADDR) + return current_pci_reg; + return 0xffffffff; +} + +static void pciemu_port_outd(ioport_t port, Bit32u value, void *arg) +{ + if (port == PCI_CONF_ADDR) + current_pci_reg = value & 0x80fffffc; + else if (port == PCI_CONF_DATA) + pciemu_port_write(port, value, 4); +} + +/* set up emulated r/o PCI config space for the given class */ +pciRec *pciemu_setup(unsigned long cls) +{ + static int pciemu_initialized = 0; + pciRec *pci; + + if (!pciemu_initialized) { + Z_printf("PCI: initializing, class=%lx\n", cls); + pcibios_init(); + } +// pci = pcibios_find_class(cls, 0); + pci = pcibios_find_primary_vga(); // XXX + if (pci == NULL) { + Z_printf("PCI: class %lx not found\n", cls); + return pci; + } + pci->enabled = pci->ext_enabled = 1; + if (!pciemu_initialized) { + emu_iodev_t io_device; + + /* register PCI ports */ + io_device.read_portb = pciemu_port_inb; + io_device.write_portb = pciemu_port_outb; + io_device.read_portw = pciemu_port_inw; + io_device.write_portw = pciemu_port_outw; + io_device.read_portd = pciemu_port_ind; + io_device.write_portd = pciemu_port_outd; + + io_device.handler_name = "PCI Emulated Config"; + io_device.start_addr = PCI_CONF_ADDR; + io_device.end_addr = PCI_CONF_DATA+3; + port_register_handler(io_device, 0); + + pciemu_initialized = 1; + } + return pci; +} diff --git a/src/base/dev/misc/rtc.c b/src/base/dev/misc/rtc.c new file mode 100644 index 0000000..83a6dc1 --- /dev/null +++ b/src/base/dev/misc/rtc.c @@ -0,0 +1,451 @@ +/* + * SIDOC_BEGIN_MODULE + * + * Description: CMOS handling and RTC emulation + * + * Maintainers: Alberto Vignani (vignani@tin.it) + * + * SIDOC_END_MODULE + * + */ + +#include +#include + +#include "emu.h" +#include "cmos.h" +#include "disks.h" +#include "timers.h" +#include "int.h" +#include "vtmr.h" +#include "iodev.h" + +long sys_base_ticks = 0; +long usr_delta_ticks = 0; +unsigned long last_ticks = 0; +static unsigned long long q_ticks_m = 0; + +static int rtc_get_rate(Bit8u div) +{ + if (!div) + return 0; + if (div < 3) + div += 7; + return (65536 >> div); +} + +void rtc_run(void) +{ + static hitimer_t last_time = -1; + int rate; + hitimer_t ticks_m, cur_time = GETusTIME(0); + if (last_time == -1 || last_time > cur_time || + !(GET_CMOS(CMOS_STATUSB) & 0x40)) { + last_time = cur_time; + return; + } + rate = rtc_get_rate(GET_CMOS(CMOS_STATUSA) & 0x0f); + ticks_m = (cur_time - last_time) * rate; + q_ticks_m += ticks_m; + last_time = cur_time; + if (debug_level('h') > 8) + h_printf("RTC: A=%hhx B=%hhx C=%hhx rate=%i queued=%lli added=%lli\n", + GET_CMOS(CMOS_STATUSA), GET_CMOS(CMOS_STATUSB), GET_CMOS(CMOS_STATUSC), + rate, (long long)q_ticks_m, (long long)ticks_m); + if (q_ticks_m >= 1000000) { + Bit8u old_c = GET_CMOS(CMOS_STATUSC); + SET_CMOS(CMOS_STATUSC, old_c | 0x40); + if ((GET_CMOS(CMOS_STATUSB) & 0x40) && !(GET_CMOS(CMOS_STATUSC) & 0x80)) { + SET_CMOS(CMOS_STATUSC, GET_CMOS(CMOS_STATUSC) | 0x80); + if (debug_level('h') > 7) + h_printf("RTC: periodic IRQ, queued=%lli, added=%lli\n", + (long long)q_ticks_m, (long long)ticks_m); + /* we only use vtmr in tweaked mode */ + if (config.timer_tweaks) + vtmr_raise(VTMR_RTC); + else + pic_request(8); + } + if (!(old_c & 0x40)) + q_ticks_m -= 1000000; + } +} + +Bit8u rtc_read(Bit8u reg) +{ + Bit8u ret = GET_CMOS(reg); + + switch (reg) { + case CMOS_SEC: + case CMOS_SECALRM: + case CMOS_MIN: + case CMOS_MINALRM: + case CMOS_DOW: + case CMOS_DOM: + case CMOS_MONTH: + case CMOS_YEAR: + case CMOS_CENTURY: + /* Note - the inline function BCD() in cmos.h will check bit 2 of + * status reg B for proper output format */ + ret = BCD(ret); + break; + + case CMOS_HOUR: /* RTC hour...bit 1 of 0xb set=24 hour mode, clear 12 hour */ + case CMOS_HOURALRM: + if (!(GET_CMOS(CMOS_STATUSB) & 2)) { /* 12-hour mode */ + if (ret == 0) + ret = 12; + else if (ret > 12) + ret -= 12; + } + ret = BCD(ret); + break; + + case CMOS_STATUSC: + if (debug_level('h') > 8) + h_printf("RTC: Read C=%hhx\n", ret); + SET_CMOS(CMOS_STATUSC, 0); + pic_untrigger(8); + rtc_run(); + break; + } + + /* below slows down streetfighter2 game so commented out */ +// vtmr_sync(VTMR_RTC); + + return ret; +} + +void rtc_write(Bit8u reg, Bit8u byte) +{ + switch (reg) { + case CMOS_SEC: + case CMOS_MIN: + case CMOS_HOUR: + case CMOS_SECALRM: + case CMOS_MINALRM: + case CMOS_HOURALRM: + case CMOS_DOW: + case CMOS_DOM: + case CMOS_MONTH: + case CMOS_YEAR: + case CMOS_CENTURY: + SET_CMOS(reg, BIN(byte)); + break; + + /* b7=r/o and unused + * b4-6=always 010 (AT standard 32.768kHz) + * b0-3=rate [65536/2^v], default 6, min 3, 0=disable + */ + case CMOS_STATUSA: + h_printf("RTC: Write %hhx to A\n", byte); + SET_CMOS(reg, byte & 0x7f); + break; + + case CMOS_STATUSB: + h_printf("RTC: Write %hhx to B\n", byte); + SET_CMOS(reg, byte); + break; + + case CMOS_STATUSC: + case CMOS_STATUSD: + h_printf("RTC: attempt to write %hhx to %hhx\n", byte, reg); + break; + + default: + SET_CMOS(reg, byte); + } + q_ticks_m = 0; +} + +static void rtc_alarm_check (void) +{ + static u_char last_sec = 0xff; /* any invalid value to begin with */ + u_char h0,m0,s0,h,m,s; + + s0=GET_CMOS(CMOS_SEC); + m0=GET_CMOS(CMOS_MIN); + h0=GET_CMOS(CMOS_HOUR); + + /* this is a test for equality - we can't just call a lib time + * function because a second could be skipped */ + h = GET_CMOS(CMOS_HOURALRM); + if (h>=0xc0 || h0==h) { /* Eric: all c0-ff are don't care */ + m = GET_CMOS(CMOS_MINALRM); + if (m>=0xc0 || m0==m) { + s = GET_CMOS(CMOS_SECALRM); + if (s>=0xc0 || (s0==s && s0!=last_sec)) { + last_sec = s0; + h_printf("RTC: got alarm at %02d:%02d:%02d\n",h,m,s); + SET_CMOS(CMOS_STATUSC, GET_CMOS(CMOS_STATUSC) | 0x20); + if ((GET_CMOS(CMOS_STATUSB) & 0x20) && !(GET_CMOS(CMOS_STATUSC) & 0x80)) { + SET_CMOS(CMOS_STATUSC, GET_CMOS(CMOS_STATUSC) | 0x80); + h_printf("RTC: alarm IRQ\n"); + pic_request(8); + } + } + } + } +} + + +void rtc_update (void) /* called every 1s from SIGALRM */ +{ + u_char h0,m0,s0,D0,M0,Y0,C0,days; + static const u_char dpm[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + if (GET_CMOS(CMOS_STATUSA) & 0x80) + error("RTC: A/UIP set on update\n"); + + if (GET_CMOS(CMOS_STATUSB) & 0x80) { + h_printf("RTC updates inhibited\n"); + return; + } + + SET_CMOS(CMOS_STATUSA, GET_CMOS(CMOS_STATUSA)|0x80); + s0=GET_CMOS(CMOS_SEC); + m0=GET_CMOS(CMOS_MIN); + h0=GET_CMOS(CMOS_HOUR); + D0=GET_CMOS(CMOS_DOM); + M0=GET_CMOS(CMOS_MONTH); + Y0=GET_CMOS(CMOS_YEAR); + C0=GET_CMOS(CMOS_CENTURY); + + if ((++s0)>59) { + s0=0; + if ((++m0)>59) { + m0=0; + if ((++h0)>23) { + h0=0; + + /* Compute days in current month. */ + if (M0>12 || M0<1) M0=1; /* Error! */ + days = dpm[M0]; + if ((Y0&3) == 0 && M0 == 2) days++; /* Leap year & Feb */ + + if (++D0>days) { + D0=1; + if (++M0>12) { + M0=1; + if (++Y0>99) { + Y0=0; + ++C0; /* Only 19->20 transition realistic. */ + SET_CMOS(CMOS_CENTURY, C0); + } + SET_CMOS(CMOS_YEAR, Y0); + } + SET_CMOS(CMOS_MONTH, M0); + } + SET_CMOS(CMOS_DOM, D0); + + /* As well as day-of-month, do day-of-week */ + days=GET_CMOS(CMOS_DOW); + days++; + if(days > 7 || days < 1) days=1; + SET_CMOS(CMOS_DOW, days); + } + SET_CMOS(CMOS_HOUR, h0); + } + SET_CMOS(CMOS_MIN, m0); + } + SET_CMOS(CMOS_SEC, s0); + + h_printf("RTC: cmos update %02d:%02d:%02d, B=%#x\n",h0,m0,s0,GET_CMOS(CMOS_STATUSB)); + + rtc_alarm_check(); + + /* per-second IRQ */ + SET_CMOS(CMOS_STATUSC, GET_CMOS(CMOS_STATUSC) | 0x10); + if ((GET_CMOS(CMOS_STATUSB) & 0x10) && !(GET_CMOS(CMOS_STATUSC) & 0x80)) { + SET_CMOS(CMOS_STATUSC, GET_CMOS(CMOS_STATUSC) | 0x80); + h_printf("RTC: update IRQ\n"); + pic_request(8); + } + + SET_CMOS(CMOS_STATUSA, GET_CMOS(CMOS_STATUSA)&~0x80); +} + +/* + * Initialise the RTC. Key work is now in get_linux_ticks() where we initialise + * all of the RTC time/date fields, and only zero the alarm stuff here. + */ +void rtc_setup(void) +{ + SET_CMOS(CMOS_HOURALRM, 0); + SET_CMOS(CMOS_MINALRM, 0); + SET_CMOS(CMOS_SECALRM, 0); +} + +static int rtc_vint(int masked) +{ + if (masked) + pic_request(8); + return 0; +} + +void rtc_init(void) +{ + usr_delta_ticks = 0; + last_ticks = sys_base_ticks = get_linux_ticks(1, NULL); + + vtmr_register(VTMR_RTC, rtc_vint); + /* NOTE: vtmr is only used in tweaked mode, so we set it to 1 beforehands, + * but may not actually use */ + vtmr_set_tweaked(VTMR_RTC, 1, 0); +} + +/* ========================================================================= */ + +/* > get_linux_ticks.c + * + * 1.03 arb Fri Jul 22 19:13:03 BST 2005 - removed config options for logging + * suspicious time jumps and using re-entrant-safe calls. + * 1.02 arb Fri Aug 6 13:08:21 BST 2004 - tidied up ifdefs + * 1.01 arb Mon Jun 14 17:50:00 BST 2004 - added timelog + * + * Copyright (c) 2004 Paul S. Crawford, All Rights Reserved + * + */ + +/* + * DANG_BEGIN_MODULE + * + * Description: Convert LINUX time to DOS / RTC time. + * + * Maintainers: Paul Crawford + * + * REMARK + * This reads the LINUX time and converts it to the DOS ticks per day + * value, and if necessary sets the RTC registers to match. It attempts + * to put all LINUX->DOS time conversion to one function. Used for RTC init + * and for the INT-1A time calls. + * /REMARK + * DANG_END_MODULE + * + */ +/* + * Configuration: + * Define USE_PIT_TICK_RATE to use the PIT tick rate. + */ +#undef USE_PIT_TICK_RATE + +/* + * Safety check to make sure 'freedos' is not supplied with a tick value greater than one + * day (max = 86399.99 seconds) with its not quite correct 1193180 PIC rate (should be + * 1193182 I gather). Correct value should be 1573042, so a difference of about 0.15s + * just before midnight. + * + */ + +#define FREEDOS_TICKS_IN_A_DAY 1573039 + +/* + * This function attempts to place all code for converting LINUX system time to DOS 'ticks' + * and/or the CMOS RTC time in one place. As DOS is based on local time (stupid, I know) it + * converts the LINUX/UNIX linear UTC time_t in to local time values and from these it computes + * the corresponding number of DOS ~18.2Hz interrupt 'ticks' in the current day. If you want/need + * UTC in DOS then start dosemu with a TZ=GMT0 environment variable. + * + * As the DOS notion of time is based on interrupts per day, and a day counter when this + * overflows at midnight, this function can also supply the roll-over flag based on the day + * having changed since the last time it was called WITH THE FLAG REQUESTED. This allows it + * to be used for both the INT-1A AH=0 function (get ticks) and the AH=2 or 4 (get RTC time/date) + * without confusing the once per day roll-over count. + * + * In addition, should the LINUX time go backwards over midnight (very unfortunate occurrence, but + * it is just possible if ntpdate was used to get things correct at once, rather then the ntm daemon + * running to slew time to correctness slowly) it will hold the DOS notion of time at 00:00:00 + * until the LINUX time crosses the midnight boundary again, but without producing two day counts. + * + * The calling arguments are: + * + * set_cmos : Non-zero to initialise the CMOS RTC date/time fields. Otherwise RTC unchanged. + * + * day_rollover : Non-NULL pointer if checking for the day crossing for INT-1A AH=0 use. + * + * The return value is the DOS 'ticks' from 00:00:00 for this day. + * An error results in -1 return (e.g. from problems converting time, perhaps bad TZ environment). + * + * NOTE: A check in the freedos source shows a *slightly* different value for the nominal tick count + * spanning one day, compared to the PIT_TICK_RATE value in timers.h (which is correct, I think). So + * if you compile with the freedos values for 100% time conversion compatibility with it, that is fine + * but if you compile with the 'correct' PIT_TICK_RATE value, it limits it near midnight so freedos + * never gets a value corresponding equal to, or more than, 24 hours. + * + * NOTE: When setting the RTC it will do this in local time, with any "daylight saving" shift applied, + * but it makes no attempt to set the DST flag in the RTC. That is a horrible idea anyway, and I do + * not see this as being a big problem for anyone. + * + * Finally, if time matters for anything important - set it to UTC and set TZ=GMT0 ! + * + */ +unsigned long get_linux_ticks(int set_cmos, int *day_rollover) +{ + static long last_ds70 = 0; /* For day changing test. */ + + long ds70, isec; + long long tt; /* Needs > 32 bits for intermediate product! */ + struct timeval tv; + int year; + struct tm *tm; + + gettimeofday(&tv, NULL); + + tm = localtime(&tv.tv_sec); + if(tm == NULL) return -1; + + year = 1900 + tm->tm_year; + isec = tm->tm_sec + 60 * (tm->tm_min + 60 * tm->tm_hour); /* Seconds in day. */ + ds70 = tm->tm_yday + (year-1970)*365L + (year-1969)/4; /* Days from 1970 */ + if(last_ds70 == 0) last_ds70 = ds70; + + /* Compute time of day in 1/100 second units, then to DOS ticks. */ + tt = isec * 100 + (tv.tv_usec / 10000); + +#ifdef USE_PIT_TICK_RATE + tt = (tt * PIT_TICK_RATE) / 6553600; /* Correct as per LINUX code, 44-bit intermediate product.*/ + if(tt > FREEDOS_TICKS_IN_A_DAY) tt = FREEDOS_TICKS_IN_A_DAY; +#else + tt = (tt * 59659) / 327680; /* Use same magic numbers as freedos: ke2033/kernel/sysclk.c */ +#endif + + if(day_rollover != NULL) + { + /* Here we deal with INT-1A AH=0 calls. */ + long day_diff = ds70 - last_ds70; + *day_rollover = 0; + + if(day_diff < 0) + { + /* Day change has us go back over midnight - keep 00:00:00 on same day until time caught up */ + tt = 0; + } + else if(day_diff > 0) + { + /* Some DOS assume incrementing flag, others only set. Here we return days passed. */ + if(day_diff > 255) day_diff = 255; + *day_rollover = (int)day_diff; + last_ds70 = ds70; /* Re-set day only if flag requested & forward time. */ + } + } + + if(set_cmos) + { + /* Initialise the date settings of the RTC (CMOS clock) */ + int cent; + SET_CMOS(CMOS_HOUR, tm->tm_hour); + SET_CMOS(CMOS_MIN, tm->tm_min); + SET_CMOS(CMOS_SEC, tm->tm_sec); + + SET_CMOS(CMOS_DOW, tm->tm_wday + 1); + SET_CMOS(CMOS_DOM, tm->tm_mday); + SET_CMOS(CMOS_MONTH, tm->tm_mon + 1); + + cent = year/100; + SET_CMOS(CMOS_YEAR, year - 100*cent); /* mod 100 */ + SET_CMOS(CMOS_CENTURY, cent); + /* Really - we don't want to consider daylight saving in the RTC! */ + } + + return (unsigned long)tt; +} diff --git a/src/base/dev/misc/timers.c b/src/base/dev/misc/timers.c new file mode 100644 index 0000000..8f78326 --- /dev/null +++ b/src/base/dev/misc/timers.c @@ -0,0 +1,717 @@ +/* + * DANG_BEGIN_MODULE + * + * Description: Timer emulation for DOSEMU. + * + * Maintainers: J. Lawrence Stephan + * Scott Buchholz + * + * REMARK + * This is the timer emulation for DOSEMU. It emulates the Programmable + * Interval Timer (PIT), and also handles IRQ0 interrupt events. + * A lot of animation and video game software are dependent on this module + * for high frequency timer interrupts (IRQ0). + * + * This code will actually generate 18.2 DOS interrupts/second (the code + * here itself will be triggered about 100 times per second). It will even + * happily attempt to generate faster clocks, right up to the point where + * it chokes. Since the absolute best case timing we can get out of Linux + * is 100Hz, figure that anything approaching or exceeding that isn't going + * to work well. (The code will attempt to generate up to 10Khz interrupts + * per second at the moment. Too bad that would probably overflow all + * internal queues really fast. :) + * + * Speaker emulation, now including port 61h, is also in here. [rz] + * + * /REMARK + * DANG_END_MODULE + * + */ + +#include +#include + +#include "emu.h" +#include "port.h" +#include "iodev.h" +#include "int.h" +#include "emudpmi.h" +#include "vtmr.h" +#include "evtimer.h" +#include "timers.h" + +#undef DEBUG_PIT +#undef ONE_MINUTE_TEST + +/******************************************************************* + * Programmable Interrupt Timer (PIT) chip * + *******************************************************************/ + +typedef struct { + Bit16u read_state; + Bit16u write_state; + Bit8u mode, outpin; + Bit32u read_latch; + Bit16u write_latch; + Bit32s cntr; + hitimer_u time; + uint32_t q_ticks; + void *evtmr; + int tmr_skip; +} pit_latch_struct; + +static pit_latch_struct pit[PIT_TIMERS]; /* values of 3 PIT counters */ + +hitimer_t pic_sys_time; /* system time set by pic_watch */ +static int irq0_cnt; + +#define NEVER -1 +static hitimer_t pic_itime[33] = /* time to trigger next interrupt */ + {NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, + NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, + NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, + NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, NEVER, + NEVER}; + +static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) +{ + return /*(__int128_t)*/a * b / c; +} + +#define TICKS_TO_NS(t) muldiv64(t, NANOSECONDS_PER_SECOND, PIT_TICK_RATE) +#define NS_TO_TICKS(n) muldiv64(n, PIT_TICK_RATE, NANOSECONDS_PER_SECOND) + +static Bit8u port61 = 0x0c; + +int is_cli; + +/* + * DANG_BEGIN_FUNCTION timer_tick + * + * description: + * Every time we get a TIMER signal from Linux, this procedure is called. + * It checks to see if we should queue a timer interrupt based on the + * current values. + * + * DANG_END_FUNCTION + */ +void timer_tick(void) +{ + pic_sys_time = GETtickTIME(0); + + if (config.cli_timeout && is_cli) { + if (isset_IF()) { + is_cli = 0; + } else if (is_cli++ >= config.cli_timeout) { + g_printf("Warning: Interrupts were disabled for too long, " + "re-enabling.\n"); + set_IF(); + } + } + dpmi_timer(); +} + + +#include "speaker.h" + +/* DANG_BEGIN_FUNCTION do_sound + * + * do_sound handles the _emulated_ mode pc-speaker emulation. + * + * As far as I can determine all cases of the pc-speaker are now + * emulated. But I am not sure where Rainer Zimmerman got his + * (pit[2].mode == 2) || (pit[2].mode == 3) test in the original + * implementation, it doesn't seem to cause problems though. + * + * The implementation of speaker_on & speaker_off can be found in + * src/base/speaker.c + * + * Major Changes from version written by Rainter Zimmerman. + * + * o Added support for programs that control the directly through bit 1 + * of port61. + * + * o Added a generic interface to allow multiple speaker backends. + * + * o Implemented X speaker code so the emulated speaker now works in X. + * + * --EB 21 September 1997 + * DANG_END_FUNCTION + */ +void do_sound(Bit16u period) +{ + /* Note I assume that a sound before after another will kill + * the previous sound I had a hard time getting X to do that. + * If it becomes a problem possibly tuning sound_duration is + * the answer. I suggest 200ms but as I don't have that + * problem now I'm not worring about it. + * + * But if you set it too low, then sounds can be cut off - clarence + */ + static const unsigned sound_duration = 30000; /* in milliseconds */ + switch (port61 & 3) { + case 3: /* speaker on & speaker control through timer channel 2 */ + if ((pit[2].mode == 2) || (pit[2].mode == 3)) { /* is this test needed? */ + speaker_on(sound_duration,period); + } + else { + speaker_off(); /* is this correct? */ + } + break; + case 2: /* speaker on & direct speaker through bit 1 */ + speaker_on(sound_duration, 0xfff); /* on as long as possible */ + break; + case 1: /* speaker off & speaker control through timer channel 2 */ + case 0: /* speaker off & direct speaker through bit 1 */ + speaker_off(); + break; + } +} + +static int _pit_latch(int latch, uint64_t cur) +{ + int ret = 0; + hitimer_u cur_time; + long ticks=0; + pit_latch_struct *p = &pit[latch]; + + /* check for special 'read latch status' mode */ + if (p->mode & 0x80) { + /* + * Latch status: + * bit 7 = state of OUT pin + * bit 6 = null count flag (1 == no cntr set, 0 == cntr available) + * bit 4-5 = read latch format + * bit 1-3 = read latch mode + * bit 0 = BCD flag (1 == BCD, 0 == 16-bit -- always 0) + */ + p->read_latch = (p->read_state << 4) | + ((p->mode & 7) << 1) | + p->outpin; + if (p->cntr == -1) + p->read_latch |= 0x40; + p->mode &= ~0x80; + return ret; /* let bit 7 on */ + } + + cur_time.td = cur; + + if ((p->mode & 2)==0) { + /* non-periodical modes 0,1,4,5 - used mainly by games + * count down to 0, then set output, wrap and continue counting (gate=1) + * only modes 0,4 allow reloading of the counter on the fly + * (thus modes 1,5 are not correctly implemented) + * we are just not interested in the gate/output pins... + */ + /* mode 0 -- interrupt on terminal count, ctr reload=Y */ + /* mode 4 -- software triggered pulse, ctr reload=Y */ + /* mode 1 -- programmable monoflop, ctr reload=N */ + /* mode 5 -- hardware triggered pulse, ctr reload=N */ + /* should have been initialized to the value in write_latch */ + + if (p->cntr != -1) { + ticks = NS_TO_TICKS(cur_time.td - p->time.td); + + if (ticks > p->cntr) { /* time has elapsed */ + if ((p->mode&0x40)==0) + p->cntr = -1; + p->outpin = (p->mode&4? 0x00: 0x80); + } else { + p->read_latch = p->cntr - ticks; + p->outpin = (p->mode&4? 0x80: 0x00); + } + } + if (p->cntr == -1) { + p->read_latch = p->write_latch; + p->outpin = (p->mode&4? 0x00: 0x80); + } + } + else { + /* mode 2 -- rate generator */ + /* mode 6 -- ??? */ + /* mode 3 -- square-wave generator, countdown by 2 */ + /* mode 7 -- ??? */ + if (latch == 0) { +#if 0 + /* when current time is greater than irq time, call pic_request + which will then point pic_itime to next interrupt */ + if (((p->mode&0x40)==0) && (pic_sys_time > pic_itime[PIC_IRQ0])) { + if (pic_request(PIC_IRQ0)==PIC_REQ_OK) + { r_printf("PIT: pit_latch, pic_request IRQ0 mode 2/3\n"); } + } +#endif + /* while current time is less than next irq time, ticks decrease; + * ticks can go out of bounds or negative when the interrupt + * is lost or pending */ + if (cur > pic_itime[latch]) { + ticks = 1; + ret++; // underflow seen + } else { + ticks = NS_TO_TICKS(pic_itime[latch] - cur) + 1; + if (ticks > p->cntr) + ticks = 0; // should not be here + } + } else { + ticks = p->cntr - (cur % p->cntr); + } + + if ((p->mode & 3)==3) { + /* ticks is now a value which decreases from cntr to 0, and + is greater than cntr/2 in the first half of the cycle */ + ticks *= 2; + if (ticks >= p->cntr) { + p->outpin = 0x00; p->read_latch = (ticks-p->cntr) & 0xfffe; + } + else { + p->outpin = 0x80; p->read_latch = ticks; + } + } + else { + p->read_latch = ticks; + p->outpin = (p->read_latch? 0x80: 0x00); + } + } + +#ifdef DEBUG_PIT + i_printf("PIT%d: ticks=%lx latch=%x pin=%d\n",latch,ticks, + p->read_latch,(p->outpin!=0)); +#endif + return ret; +} + +static int do_pit_latch(int latch) +{ + int ret; + uint64_t cur_time; + + evtimer_block(pit[latch].evtmr); + cur_time = evtimer_gettime(pit[latch].evtmr); + /* if timer is lagging we run it by hands */ + if (cur_time > pic_itime[latch] && + __sync_bool_compare_and_swap(&pit[latch].q_ticks, 0, 1)) { + /* timer thread blocked, we can increment vars w/o sync/atomics */ + pit[latch].tmr_skip++; + if (!latch) + vtmr_raise(VTMR_PIT); + else + pit[latch].q_ticks--; + pit[latch].time.td = pic_itime[latch]; + pic_itime[latch] += TICKS_TO_NS(pit[latch].cntr); + } + ret = _pit_latch(latch, cur_time); + vtmr_sync(VTMR_PIT); + evtimer_unblock(pit[latch].evtmr); + return ret; +} + +static int pit_latch_hndl(void) +{ + return do_pit_latch(0); +} + +static void pit_latch(int latch) +{ + if (latch == 0) + vtmr_latch(VTMR_PIT); + else + do_pit_latch(latch); +} + +/* This is called also by port 0x61 - some programs can use timer #2 + * as a GP timer and read bit 5 of port 0x61 (e.g. Matrox BIOS) + */ +Bit8u pit_inp(ioport_t port, void *arg) +{ + int ret = 0; + port -= 0x40; + + if ((port == 2) && (config.speaker == SPKR_NATIVE)) + return std_port_inb(0x42); + else if (port == 1) + i_printf("PIT: someone is reading the CMOS refresh time?!?"); + + if (pit[port].read_latch == -1) + pit_latch(port); + + switch (pit[port].read_state) { + case 0: /* read MSB & return to state 3 */ + ret = (pit[port].read_latch >> 8) & 0xff; + pit[port].read_state = 3; + pit[port].read_latch = -1; + break; + case 3: /* read LSB followed by MSB */ + ret = (pit[port].read_latch & 0xff); + if (pit[port].mode & 0x80) pit[port].mode &= 7; /* moved here */ + else + pit[port].read_state = 0; + break; + case 1: /* read MSB */ + ret = (pit[port].read_latch >> 8) & 0xff; + pit[port].read_latch = -1; + break; + case 2: /* read LSB */ + ret = (pit[port].read_latch & 0xff); + pit[port].read_latch = -1; + break; + } +#ifdef DEBUG_PIT + i_printf("PORT: pit_inp(0x%x) = 0x%x\n", port+0x40, ret); +#endif + return ret; +} + +void pit_outp(ioport_t port, Bit8u val, void *arg) +{ + + port -= 0x40; + if (port == 1) + i_printf("PORT: someone is writing the CMOS refresh time?!?"); + else if (port == 2 && config.speaker == SPKR_NATIVE) { + std_port_outb(0x42, val); + return; + } + + switch (pit[port].write_state) { + case 0: + pit[port].write_latch = pit[port].write_latch | ((val & 0xff) << 8); + /* + * TRICK: some graphics apps use the vertical retrace bit to + * calibrate themselves. AFAIK this is done by starting PIT#0 + * with a value of 0 after detecting the sync, then reading it + * again at next sync pulse. In this special case, synchronizing + * the emulated vertical retrace with the PIT write yields some + * better timing results. It works under X and with emuretrace -- AV + */ + if (pit[port].write_latch==0) t_vretrace = pic_sys_time; + pit[port].write_state = 3; + break; + case 3: + pit[port].write_latch = val & 0xff; + pit[port].write_state = 0; + break; + case 1: + pit[port].write_latch = val & 0xff; + break; + case 2: + pit[port].write_latch = (val & 0xff) << 8; + break; + } +#ifdef DEBUG_PIT + i_printf("PORT: pit_outp(0x%x, 0x%x)\n", port+0x40, val); +#endif + + if (pit[port].write_state != 0) { + if (pit[port].write_latch == 0) + pit[port].cntr = 0xffff; + else + pit[port].cntr = pit[port].write_latch; + + if (!port) + evtimer_set_rel(pit[port].evtmr, TICKS_TO_NS(pit[port].cntr), 1); + else + evtimer_stop(pit[port].evtmr); + h_printf("PIT: timer %i set to %i ticks\n", port, pit[port].cntr); + pit[port].time.td = 0; + pic_itime[port] = TICKS_TO_NS(pit[port].cntr); + if (port == 2 && (port61 & 3) == 3) + do_sound(pit[port].cntr); + } +} + +Bit8u pit_control_inp(ioport_t port, void *arg) +{ + return 0; +} + +void pit_control_outp(ioport_t port, Bit8u val, void *arg) +{ + int latch = (val >> 6) & 0x03; + +#ifdef DEBUG_PIT + i_printf("PORT: outp(0x43, 0x%x)\n",val); +#endif + + /* Timer commands (00-BF): + * bit 6-7 = Timer (0,1,2) + * 3 is not a timer but a special read-back command + * + * bit 4-5 = command 00=latch 01=LSB mode 10=MSB mode 11=16bit mode + * bit 1-3 = mode selection + * mode 0(000) 1(001) 2(010 or 110) 3(011 or 111) + * 4(100) 5(101) + * 6(110) and 7(111) undefined? + * bit 0 = binary(0), BCD(1) + * + * 0xh counter latch timer 0 + * 1xh timer 0 LSB mode + * modes: 0,2,3,4 - 1,5 not applicable + * 2xh timer 0 MSB mode + * modes: 0,2,3,4 - 1,5 not applicable + * 3xh timer 0 16bit mode + * modes: 0,2,4 - 3 typical - 1,5 not applicable + * modes 1,5 applicable only to timer 2 [van Gilluwe,1994] + */ + switch (latch) { + case 2: + if (config.speaker == SPKR_NATIVE) { + std_port_outb(0x43, val); + break; + } + /* nobreak; */ + case 0: + case 1: + if ((val & 0x30) == 0) + pit_latch(latch); + else { + pit[latch].read_state = (val >> 4) & 0x03; + pit[latch].write_state = (val >> 4) & 0x03; + pit[latch].mode = (val >> 1) & 0x07; + if ((val & 4)==0) { /* modes 0,1,4,5 */ + /* set the time base for the counter - safety code for programs + * which use a non-periodical mode without reloading the counter + */ + pit[latch].time.td = evtimer_gettime(pit[latch].evtmr); + } + } +#ifdef DEBUG_PIT + i_printf("PORT: writing outp(0x43, 0x%x)\n", val); +#endif + break; + case 3: + /* I think this code is more or less correct */ + if ((val & 0x20) == 0) { /* latch counts? */ + if (val & 0x02) pit_latch(0); + if (val & 0x04) pit_latch(1); + if (val & 0x08) pit_latch(2); + } + else if ((val & 0x10) == 0) { /* latch status words? */ + int or_mask = ((val & 0x20) == 0 ? 0xc0 : 0x80); + if (val & 0x02) { pit[0].mode |= or_mask; pit_latch(0); } + if (val & 0x04) { pit[1].mode |= or_mask; pit_latch(1); } + if (val & 0x08) { pit[2].mode |= or_mask; pit_latch(2); } + } + break; + } +} + +static void timer_activate(int ticks, void *arg) +{ + int pit_num = (uintptr_t)arg; + uint32_t q; + + if (pit[pit_num].tmr_skip) { + pit[pit_num].tmr_skip--; + return; + } + if (!ticks) { + error("0 ticks on PIT\n"); + return; + } + q = __sync_fetch_and_add(&pit[pit_num].q_ticks, ticks); + h_printf("PIT: timer %i expired, %i\n", pit_num, q); + if (pit_num) { + pit[pit_num].time.td = evtimer_gettime(pit[pit_num].evtmr); + return; + } + if (!q) { + vtmr_raise(VTMR_PIT); + pit[0].time.td = pic_itime[0]; + pic_itime[0] += TICKS_TO_NS(pit[0].cntr); + } +} + +static int timer_irq_ack(int masked) +{ + uint32_t q = __sync_sub_and_fetch(&pit[0].q_ticks, 1); + int ret = 0; + + h_printf("PIT: timer 0 acknowledged, %i\n", q); + + if (q) { + pit[0].time.td = pic_itime[0]; + pic_itime[0] += TICKS_TO_NS(pit[0].cntr); + ret = 1; + } + if (!masked) + irq0_cnt++; + return ret; +} + +/* reads/writes to the speaker control port (0x61) + * Port 0x61 is really more complex than a speaker enable bit... look here: + * [output]: + * Bit(s) Description + * 7 pulse to 1 for IRQ1 reset (PC,XT) + * 6-4 reserved + * 3 I/O channel parity check disable + * 2 RAM parity check disable + * 1 speaker data enable + * 0 timer 2 gate to speaker enable + * + * [input]: + * Bit(s) Description + * 7 RAM parity error occurred + * 6 I/O channel parity error occurred + * 5 mirrors timer 2 output condition + * 4 toggles with each refresh request + * 3 NMI I/O channel check status + * 2 NMI parity check status + * 1 speaker data status + * 0 timer 2 clock gate to speaker status + */ +Bit8u spkr_io_read(ioport_t port) { + if (port==0x61) { + if (config.speaker == SPKR_NATIVE) + return std_port_inb(0x61); + else { + /* keep the connection between port 0x61 and PIT timer#2 */ + pit_latch(2); + return ((*((Bit8u *)&pic_sys_time)&0x10) | /* or anything that toggles quick enough */ + (pit[2].outpin? 0x20:0) | /* outpin: 00 or 80 */ + (port61&0xcf)); + } + } + return 0xff; +} + +void spkr_io_write(ioport_t port, Bit8u value) { + if (port==0x61) { + switch (config.speaker) { + case SPKR_NATIVE: + std_port_outb(0x61, value & 0x03); + break; + + case SPKR_EMULATED: + if ((value & 3) == (port61 & 3)) + break; + port61 = value & 0x0f; + do_sound(pit[2].write_latch & 0xffff); + break; + + case SPKR_OFF: + port61 = value & 0x0c; + break; + } + } +} + +void pit_init(void) +{ + emu_iodev_t io_device; + + /* 8254 PIT (Programmable Interval Timer) */ + io_device.read_portb = pit_inp; + io_device.write_portb = pit_outp; + io_device.read_portw = NULL; + io_device.write_portw = NULL; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + io_device.handler_name = "8254 Timer0"; + io_device.start_addr = 0x0040; + io_device.end_addr = 0x0040; + port_register_handler(io_device, 0); + + io_device.handler_name = "8254 Timer1"; + io_device.start_addr = 0x0041; + io_device.end_addr = 0x0041; + port_register_handler(io_device, 0); + + io_device.handler_name = "8254 Timer2"; + io_device.start_addr = 0x0042; + io_device.end_addr = 0x0042; + port_register_handler(io_device, config.speaker==SPKR_NATIVE? PORT_FAST:0); + + io_device.read_portb = pit_control_inp; + io_device.write_portb = pit_control_outp; + io_device.handler_name = "8254 Ctrl02"; + io_device.start_addr = 0x0043; + io_device.end_addr = 0x0043; + port_register_handler(io_device, 0); + + /* register_handler for port 0x61 is in keyboard code */ + port61 = 0x0c; +#if 0 + io_device.start_addr = 0x0047; + io_device.end_addr = 0x0047; + port_register_handler(io_device, 0); +#endif + + vtmr_register(VTMR_PIT, timer_irq_ack); + vtmr_register_latch(VTMR_PIT, pit_latch_hndl); + vtmr_set_tweaked(VTMR_PIT, config.timer_tweaks, 0); + + pit[0].evtmr = evtimer_create(timer_activate, (void *)(uintptr_t)0); + pit[1].evtmr = evtimer_create(timer_activate, (void *)(uintptr_t)1); + pit[2].evtmr = evtimer_create(timer_activate, (void *)(uintptr_t)2); +} + +void pit_done(void) +{ + evtimer_delete(pit[0].evtmr); + evtimer_delete(pit[1].evtmr); + evtimer_delete(pit[2].evtmr); +} + +void pit_reset(void) +{ + pit[0].mode = 3; + pit[0].outpin = 0; + pit[0].cntr = 0xffff; + pit[0].time.td = 0; + pit[0].read_latch = 0xffffffff; + pit[0].write_latch = 0; + pit[0].read_state = 3; + pit[0].write_state = 3; + pit[0].q_ticks = 0; + evtimer_stop(pit[0].evtmr); + + pit[1].mode = 2; + pit[1].outpin = 0; + pit[1].cntr = 18; + pit[1].time.td = 0; + pit[1].read_latch = 0xffffffff; + pit[1].write_latch = 18; + pit[1].read_state = 3; + pit[1].write_state = 3; + pit[1].q_ticks = 0; + evtimer_stop(pit[1].evtmr); + + pit[2].mode = 0; + pit[2].outpin = 0; + pit[2].cntr = 0xffff; + pit[2].time.td = 0; + pit[2].read_latch = 0xffffffff; + pit[2].write_latch = 0; + pit[2].read_state = 3; + pit[2].write_state = 3; + pit[2].q_ticks = 0; + evtimer_stop(pit[2].evtmr); + + pit[3].mode = 0; + pit[3].outpin = 0; + pit[3].cntr = 0xffff; + pit[3].time.td = 0; + pit[3].read_latch = 0xffffffff; + pit[3].write_latch = 0; + pit[3].read_state = 3; + pit[3].write_state = 3; + + port61 = 0x0c; + + pic_sys_time = GETtickTIME(0); +} + +void pit_late_init(void) +{ + evtimer_set_rel(pit[0].evtmr, TICKS_TO_NS(pit[0].cntr), 1); + pit[0].time.td = 0; + pic_itime[0] = TICKS_TO_NS(pit[0].cntr); +} + +#define TIMER0_FLOOD_THRESHOLD 50 + +int CAN_SLEEP(void) +{ + return (!(pic_get_isr() || (REG(eflags) & VIP) || signal_pending() || + (pit[0].q_ticks > TIMER0_FLOOD_THRESHOLD) || in_leavedos)); +} diff --git a/src/base/dev/misc/virq.c b/src/base/dev/misc/virq.c new file mode 100644 index 0000000..d7d67bc --- /dev/null +++ b/src/base/dev/misc/virq.c @@ -0,0 +1,196 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: virtual IRQ router + * + * Author: Stas Sergeev. + * + */ +#include +#include +#include +#include "port.h" +#include "pic.h" +#include "hlt.h" +#include "cpu.h" +#include "int.h" +#include "bitops.h" +#include "emu.h" +#include "virq.h" + +#define VIRQ_IRR_PORT 0x50a +#define VIRQ_HWC_PORT (VIRQ_IRR_PORT + 2) +#define VIRQ_RST_PORT (VIRQ_IRR_PORT + 3) +#define VIRQ_TOTAL_PORTS 4 +#define VIRQ_IRQ_NUM 0xf +#define VIRQ_INTERRUPT (VIRQ_IRQ_NUM - 8 + 0x70) + +static uint16_t virq_irr; +static pthread_mutex_t irr_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t hndl_mtx = PTHREAD_MUTEX_INITIALIZER; +static uint16_t virq_hlt; + +struct vhandler_s { + enum VirqHwRet (*hw_handler)(void *); + enum VirqSwRet (*sw_handler)(void *); + void *arg; +}; +struct vhandler_s vhandlers[VIRQ_MAX]; + +static void virq_lower(int virq_num); + +static Bit16u virq_irr_read(ioport_t port, void *arg) +{ + uint16_t irr; + + pthread_mutex_lock(&irr_mtx); + irr = virq_irr; + pthread_mutex_unlock(&irr_mtx); + return irr; +} + +static void virq_hwc_write(ioport_t port, Bit8u value, void *arg) +{ + struct vhandler_s *vh; + enum VirqHwRet rc = VIRQ_HWRET_DONE; + + switch (port) { + case VIRQ_RST_PORT: + switch (value) { + case 1: + /* re-assert irqs */ + if (virq_irr) + pic_request(VIRQ_IRQ_NUM); + break; + } + break; + + case VIRQ_HWC_PORT: + assert(value < VIRQ_MAX); + vh = &vhandlers[value]; + pthread_mutex_lock(&hndl_mtx); + if (vh->hw_handler) + rc = vh->hw_handler(vh->arg); + if (rc == VIRQ_HWRET_DONE) + virq_lower(value); + pthread_mutex_unlock(&hndl_mtx); + break; + } +} + +static void virq_handler(uint16_t idx, HLT_ARG(arg)) +{ + uint16_t irr; + + while ((irr = port_inw(VIRQ_IRR_PORT))) { + struct vhandler_s *vh; + int inum = find_bit(irr); + + assert(inum < VIRQ_MAX); + port_outb(VIRQ_HWC_PORT, inum); + vh = &vhandlers[inum]; + if (vh->sw_handler) { + enum VirqSwRet rc = vh->sw_handler(vh->arg); + if (rc == VIRQ_SWRET_BH) { + assert(_IP != virq_hlt); + /* If BH is scheduled, we just return and switch back later. */ + set_IF(); + return; + } + } else { + error("virq: no handler for %i\n", inum); + } + } + assert(_IP == virq_hlt); + do_eoi2_iret(); +} + +void virq_init(void) +{ + emu_iodev_t io_dev = {}; + emu_hlt_t hlt_hdlr = HLT_INITIALIZER; + + io_dev.write_portb = virq_hwc_write; + io_dev.read_portw = virq_irr_read; + io_dev.start_addr = VIRQ_IRR_PORT; + io_dev.end_addr = VIRQ_IRR_PORT + VIRQ_TOTAL_PORTS - 1; + io_dev.handler_name = "virtual IRQ router"; + port_register_handler(io_dev, 0); + + hlt_hdlr.name = "virq"; + hlt_hdlr.func = virq_handler; + virq_hlt = hlt_register_handler_vm86(hlt_hdlr); +} + +void virq_reset(void) +{ + pic_untrigger(VIRQ_IRQ_NUM); +} + +void virq_setup(void) +{ + SETIVEC(VIRQ_INTERRUPT, BIOS_HLT_BLK_SEG, virq_hlt); + port_outb(VIRQ_RST_PORT, 1); +} + +void virq_raise(int virq_num) +{ + uint16_t irr; + uint16_t mask = 1 << virq_num; + + assert(virq_num < VIRQ_MAX); + pthread_mutex_lock(&hndl_mtx); + pthread_mutex_lock(&irr_mtx); + /* __sync_fetch_and_or() */ + irr = virq_irr; + virq_irr |= mask; + if (!irr) + pic_request(VIRQ_IRQ_NUM); + pthread_mutex_unlock(&irr_mtx); + pthread_mutex_unlock(&hndl_mtx); +} + +static void virq_lower(int virq_num) +{ + uint16_t irr; + uint16_t mask = 1 << virq_num; + + assert(virq_num < VIRQ_MAX); + pthread_mutex_lock(&irr_mtx); + /* __sync_and_and_fetch() */ + virq_irr &= ~mask; + irr = virq_irr; + if (!irr) + pic_untrigger(VIRQ_IRQ_NUM); + pthread_mutex_unlock(&irr_mtx); +} + +void virq_register(int virq_num, enum VirqHwRet (*hw_handler)(void *), + enum VirqSwRet (*sw_handler)(void *), void *arg) +{ + if (virq_num >= VIRQ_MAX) + return; + vhandlers[virq_num].hw_handler = hw_handler; + vhandlers[virq_num].sw_handler = sw_handler; + vhandlers[virq_num].arg = arg; +} + +void virq_unregister(int virq_num) +{ + vhandlers[virq_num].hw_handler = NULL; + vhandlers[virq_num].sw_handler = NULL; +} diff --git a/src/base/dev/misc/vtmr.c b/src/base/dev/misc/vtmr.c new file mode 100644 index 0000000..c07d425 --- /dev/null +++ b/src/base/dev/misc/vtmr.c @@ -0,0 +1,496 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: virtual timer device extension + * + * Author: Stas Sergeev. + * + * Note: this code shows how to run coopth in a separate thread. + * Many things are written here just as an example. + * Perhaps in the future the example should be moved elsewhere. + */ +#include +#include +#include +#include +#include "port.h" +#include "pic.h" +#include "cpu.h" +#include "int.h" +#include "bitops.h" +#include "emudpmi.h" +#include "emu.h" +#include "coopth.h" +#if MULTICORE_EXAMPLE +#include "lowmem.h" +#include "hlt.h" +#endif +#include "utilities.h" +#include "timers.h" +#include "chipset.h" +#include "vint.h" +#include "vtmr.h" + +#define VTMR_FIRST_PORT 0x550 +#define VTMR_VPEND_PORT VTMR_FIRST_PORT +#define VTMR_IRR_PORT (VTMR_FIRST_PORT + 2) +#define VTMR_ACK_PORT (VTMR_FIRST_PORT + 3) +#define VTMR_REQUEST_PORT (VTMR_FIRST_PORT + 4) +#define VTMR_MASK_PORT (VTMR_FIRST_PORT + 5) +#define VTMR_UNMASK_PORT (VTMR_FIRST_PORT + 6) +#define VTMR_LATCH_PORT (VTMR_FIRST_PORT + 7) +#define VTMR_TOTAL_PORTS 8 + +static uint16_t vtmr_irr; +static uint16_t vtmr_imr; +static uint16_t vtmr_pirr; + +static pthread_t vtmr_thr; +static sem_t vtmr_sem; +static int latch_tid; +static pthread_mutex_t irr_mtx = PTHREAD_MUTEX_INITIALIZER; +#if MULTICORE_EXAMPLE +static int smi_tid; +static char *rmstack; +static uint16_t hlt_off; +#endif + +struct vthandler { + int (*handler)(int); + int (*latch)(void); + int vint; + int done_pred; + pthread_mutex_t done_mtx; + pthread_cond_t done_cnd; +}; +struct vthandler vth[VTMR_MAX]; + +struct vint_presets { + uint8_t irq; + uint8_t orig_irq; + uint8_t interrupt; +}; +static struct vint_presets vip[VTMR_MAX] = { + [VTMR_PIT] = { .irq = VTMR_IRQ, .orig_irq = 0, + .interrupt = VTMR_INTERRUPT }, + [VTMR_RTC] = { .irq = VRTC_IRQ, .orig_irq = 8, + .interrupt = VRTC_INTERRUPT }, +}; + +static int do_vtmr_raise(int timer); + +static Bit8u vtmr_irr_read(ioport_t port, void *arg) +{ + return vtmr_irr; +} + +static Bit16u vtmr_vpend_read(ioport_t port, void *arg) +{ + /* clang has __atomic_swap() */ + return __atomic_exchange_n(&vtmr_pirr, 0, __ATOMIC_ACQ_REL); +} + +static void post_req(int timer) +{ + if (vth[timer].handler) { + int rc = vth[timer].handler(0); + if (rc) + do_vtmr_raise(timer); + } + h_printf("vtmr: post-REQ on %i, irr=%x\n", timer, vtmr_irr); +} + +static void vtmr_io_write(ioport_t port, Bit8u value, void *arg) +{ + int masked = (value >> 7) & 1; + int timer = value & 0x7f; + uint16_t msk = 1 << timer; + + if (timer >= VTMR_MAX) + return; + switch (port) { + case VTMR_REQUEST_PORT: + if (!masked) { + uint16_t irr; + pthread_mutex_lock(&irr_mtx); + irr = __sync_fetch_and_or(&vtmr_irr, msk); + if (!(irr & msk)) { + if (!(vtmr_imr & msk)) + pic_request(vip[timer].irq); + } else { + error("vtmr %i already requested\n", timer); + } + pthread_mutex_unlock(&irr_mtx); + } else { + pic_untrigger(vip[timer].orig_irq); + pic_request(vip[timer].orig_irq); + post_req(timer); + } + h_printf("vtmr: REQ on %i, irr=%x, pirr=%x masked=%i\n", timer, + vtmr_irr, vtmr_pirr, masked); + break; + case VTMR_MASK_PORT: { + uint16_t imr = __sync_fetch_and_or(&vtmr_imr, msk); + if (!(imr & msk)) { + if (vtmr_irr & msk) + pic_untrigger(vip[timer].irq); + } + break; + } + case VTMR_UNMASK_PORT: { + uint16_t imr = __sync_fetch_and_and(&vtmr_imr, ~msk); + if (imr & msk) { + if (vtmr_irr & msk) + pic_request(vip[timer].irq); + } + break; + } + case VTMR_ACK_PORT: { + uint16_t irr; + pthread_mutex_lock(&irr_mtx); + irr = __sync_fetch_and_and(&vtmr_irr, ~msk); + if (irr & msk) { + pic_untrigger(vip[timer].irq); + if (vth[timer].handler) { + int rc = vth[timer].handler(masked); + if (rc) + do_vtmr_raise(timer); + } + } else { + error("vtmr %i not requested\n", timer); + } + pthread_mutex_unlock(&irr_mtx); + h_printf("vtmr: ACK on %i, irr=%x pirr=%x\n", timer, vtmr_irr, + vtmr_pirr); + break; + } + case VTMR_LATCH_PORT: { + int from_irq = masked; + if (vth[timer].latch) { + int rc = vth[timer].latch(); + if (rc && !from_irq) { // underflow seen not from IRQ + uint16_t irr; + pthread_mutex_lock(&irr_mtx); + irr = __sync_fetch_and_and(&vtmr_irr, ~msk); + if (irr & msk) { + pic_untrigger(vip[timer].irq); + if (vth[timer].handler) { + rc = vth[timer].handler(1); + if (rc) + do_vtmr_raise(timer); + } + } + pthread_mutex_unlock(&irr_mtx); + } + } + h_printf("vtmr: LATCH on %i, irr=%x pirr=%x\n", timer, vtmr_irr, + vtmr_pirr); + break; + } + } + +} + +static void do_ack(int timer, int masked) +{ + port_outb(VTMR_ACK_PORT, timer | (masked << 7)); +} + +static void ack_handler(int vint, int masked) +{ + int i; + + for (i = 0; i < VTMR_MAX; i++) { + if (vth[i].vint == vint) { + do_ack(i, masked); + break; + } + } +} + +static void do_mask(int timer) +{ + port_outb(VTMR_MASK_PORT, timer); +} + +static void do_unmask(int timer) +{ + port_outb(VTMR_UNMASK_PORT, timer); +} + +static void mask_handler(int vint, int masked) +{ + int i; + + for (i = 0; i < VTMR_MAX; i++) { + if (vth[i].vint == vint) { + if (masked) + do_mask(i); + else + do_unmask(i); + break; + } + } +} + +int vtmr_pre_irq_dpmi(uint8_t *imr) +{ + int masked = vint_is_masked(vth[VTMR_PIT].vint, imr); + do_mask(VTMR_PIT); + do_ack(VTMR_PIT, masked); + vint_post_irq_dpmi(vth[VTMR_PIT].vint, masked); + return masked; +} + +void vtmr_post_irq_dpmi(int masked) +{ + do_unmask(VTMR_PIT); +} + +int vrtc_pre_irq_dpmi(uint8_t *imr) +{ + int masked = vint_is_masked(vth[VTMR_RTC].vint, imr); + do_mask(VTMR_RTC); + do_ack(VTMR_RTC, masked); + vint_post_irq_dpmi(vth[VTMR_RTC].vint, masked); + return masked; +} + +void vrtc_post_irq_dpmi(int masked) +{ + do_unmask(VTMR_RTC); +} + +static int vtmr_is_masked(int timer) +{ + uint8_t imr[2] = { [0] = port_inb(0x21), [1] = port_inb(0xa1) }; + uint16_t real_imr = (imr[1] << 8) | imr[0]; + return ((imr[0] & 4) || !!(real_imr & (1 << vip[timer].irq))); +} + +static void vtmr_smi(void *arg) +{ + int timer; + uint16_t pirr = port_inw(VTMR_VPEND_PORT); + + while ((timer = find_bit(pirr)) != -1) { + int masked = vtmr_is_masked(timer); + pirr &= ~(1 << timer); + port_outb(VTMR_REQUEST_PORT, timer | (masked << 7)); + if (!masked) + port_outb(0x4d2, 1); // set fake IRR + pthread_mutex_lock(&vth[timer].done_mtx); + vth[timer].done_pred = 1; + pthread_mutex_unlock(&vth[timer].done_mtx); + pthread_cond_signal(&vth[timer].done_cnd); + } +} + +static void vtmr_latch_smi(void *arg) +{ + uint16_t isr; + int from_irq; + int timer = (uintptr_t)arg; + + assert(timer < VTMR_MAX); + port_outb(0x20, 0xb); + isr = port_inb(0x20); + port_outb(0xa0, 0xb); + isr = (port_inb(0xa0) << 8); + from_irq = !!(isr & (1 << vip[timer].orig_irq)); + port_outb(VTMR_LATCH_PORT, timer | (from_irq << 7)); +} + +#if MULTICORE_EXAMPLE +static void thr_cleanup(void *arg) +{ + coopth_done(); + lowmem_free(rmstack); +} +#endif + +#define RMSTACK_SIZE 32 + +static void *vtmr_thread(void *arg) +{ +#if MULTICORE_EXAMPLE + /* init coopth in new thread */ + coopth_init(); + pthread_cleanup_push(thr_cleanup, NULL); + smi_tid = coopth_create("vtmr smi", vtmr_smi); + cpu_reset(); + _SS = DOSEMU_LMHEAP_SEG; + _SP = DOSEMU_LMHEAP_OFFS_OF(rmstack) + RMSTACK_SIZE; + _CS = BIOS_HLT_BLK_SEG; + _IP = hlt_off; + clear_IF(); + + /* run our fake core */ + while (1) + run_vm86(); + pthread_cleanup_pop(1); +#else + while (1) { + sem_wait(&vtmr_sem); + vtmr_smi(NULL); + } +#endif + return NULL; +} + +#if MULTICORE_EXAMPLE +static void vtmr_hlt(Bit16u idx, HLT_ARG(arg)) +{ + sem_wait(&vtmr_sem); + coopth_start(smi_tid, NULL); +} +#endif + +void vtmr_init(void) +{ + emu_iodev_t io_dev = {}; +#if MULTICORE_EXAMPLE + emu_hlt_t hlt_hdlr = HLT_INITIALIZER; +#endif + int i; + + io_dev.write_portb = vtmr_io_write; + io_dev.read_portb = vtmr_irr_read; + io_dev.read_portw = vtmr_vpend_read; + io_dev.start_addr = VTMR_FIRST_PORT; + io_dev.end_addr = VTMR_FIRST_PORT + VTMR_TOTAL_PORTS - 1; + io_dev.handler_name = "virtual timer"; + port_register_handler(io_dev, 0); + +#if MULTICORE_EXAMPLE + rmstack = lowmem_alloc(RMSTACK_SIZE); + hlt_hdlr.name = "vtmr sleep"; + hlt_hdlr.func = vtmr_hlt; + hlt_off = hlt_register_handler_vm86(hlt_hdlr); +#endif + + latch_tid = coopth_create("vtmr latch smi", vtmr_latch_smi); + coopth_set_ctx_handlers(latch_tid, sig_ctx_prepare, sig_ctx_restore, NULL); + + sem_init(&vtmr_sem, 0, 0); + for (i = 0; i < VTMR_MAX; i++) { + pthread_mutex_init(&vth[i].done_mtx, NULL); + pthread_cond_init(&vth[i].done_cnd, NULL); + vth[i].done_pred = 1; + } + pthread_create(&vtmr_thr, NULL, vtmr_thread, NULL); +#if defined(HAVE_PTHREAD_SETNAME_NP) && defined(__GLIBC__) + pthread_setname_np(vtmr_thr, "dosemu: vtmr"); +#endif +} + +void vtmr_done(void) +{ + int i; + + pthread_cancel(vtmr_thr); + pthread_join(vtmr_thr, NULL); + sem_destroy(&vtmr_sem); + for (i = 0; i < VTMR_MAX; i++) { + pthread_mutex_destroy(&vth[i].done_mtx); + pthread_cond_destroy(&vth[i].done_cnd); + } +#if MULTICORE_EXAMPLE + lowmem_free(rmstack); +#endif +} + +void vtmr_reset(void) +{ + int i; + + vtmr_irr = 0; + vtmr_imr = 0; + vtmr_pirr = 0; + for (i = 0; i < VTMR_MAX; i++) + pic_untrigger(vip[i].irq); +} + +static int do_vtmr_raise(int timer) +{ + uint16_t pirr; + uint16_t mask = 1 << timer; + + assert(timer < VTMR_MAX); + h_printf("vtmr: raise timer %i\n", timer); + pirr = __sync_fetch_and_or(&vtmr_pirr, mask); + if (!(pirr & mask)) { + h_printf("vtmr: posting timer event\n"); + sem_post(&vtmr_sem); + return 1; + } + return 0; +} + +void vtmr_raise(int timer) +{ + int rc; + + pthread_mutex_lock(&vth[timer].done_mtx); + vth[timer].done_pred = 0; + pthread_mutex_unlock(&vth[timer].done_mtx); + + rc = do_vtmr_raise(timer); + if (!rc) { + pthread_mutex_lock(&vth[timer].done_mtx); + vth[timer].done_pred = 1; + pthread_mutex_unlock(&vth[timer].done_mtx); + pthread_cond_signal(&vth[timer].done_cnd); + } +} + +void vtmr_latch(int timer) +{ + if (in_dpmi_pm()) + fake_pm_int(); + coopth_start(latch_tid, (void *)(uintptr_t)timer); +} + +void vtmr_sync(int timer) +{ + pthread_mutex_lock(&vth[timer].done_mtx); + while (!vth[timer].done_pred) + cond_wait(&vth[timer].done_cnd, &vth[timer].done_mtx); + pthread_mutex_unlock(&vth[timer].done_mtx); +} + +void vtmr_register(int timer, int (*handler)(int)) +{ + struct vthandler *vt = &vth[timer]; + struct vint_presets *vp = &vip[timer]; + assert(timer < VTMR_MAX); + vt->handler = handler; + vt->vint = vint_register(ack_handler, mask_handler, vp->irq, + vp->orig_irq, vp->interrupt); +} + +void vtmr_register_latch(int timer, int (*handler)(void)) +{ + struct vthandler *vt = &vth[timer]; + assert(timer < VTMR_MAX); + vt->latch = handler; +} + +void vtmr_set_tweaked(int timer, int on, unsigned flags) +{ + vint_set_tweaked(vth[timer].vint, on, flags); +} diff --git a/src/base/dev/ne2k/Makefile b/src/base/dev/ne2k/Makefile new file mode 100644 index 0000000..953f906 --- /dev/null +++ b/src/base/dev/ne2k/Makefile @@ -0,0 +1,14 @@ + +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf + +CFILES = ne2000.c +ALL = $(CFILES) +OBJS = ne2000.o + + +all: lib + +install: + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/dev/ne2k/ne2000.c b/src/base/dev/ne2k/ne2000.c new file mode 100644 index 0000000..c9fb068 --- /dev/null +++ b/src/base/dev/ne2k/ne2000.c @@ -0,0 +1,948 @@ +/* + * QEMU NE2000 emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include +#include "emu.h" +#include "dosemu_debug.h" +#include "ioselect.h" +#include "pic.h" +#include "port.h" +#include "libpacket.h" +#include "ne2000.h" + +#define DEBUG_NE2000 + +#define MAX_ETH_FRAME_SIZE 1514 + +#define E8390_CMD 0x00 /* The command register (for all pages) */ +/* Page 0 register offsets. */ +#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */ +#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */ +#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */ +#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */ +#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */ +#define EN0_TSR 0x04 /* Transmit status reg RD */ +#define EN0_TPSR 0x04 /* Transmit starting page WR */ +#define EN0_NCR 0x05 /* Number of collision reg RD */ +#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */ +#define EN0_FIFO 0x06 /* FIFO RD */ +#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */ +#define EN0_ISR 0x07 /* Interrupt status reg RD WR */ +#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */ +#define EN0_RSARLO 0x08 /* Remote start address reg 0 */ +#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */ +#define EN0_RSARHI 0x09 /* Remote start address reg 1 */ +#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */ +#define EN0_RTL8029ID0 0x0a /* Realtek ID byte #1 RD */ +#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */ +#define EN0_RTL8029ID1 0x0b /* Realtek ID byte #2 RD */ +#define EN0_RSR 0x0c /* rx status reg RD */ +#define EN0_RXCR 0x0c /* RX configuration reg WR */ +#define EN0_TXCR 0x0d /* TX configuration reg WR */ +#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */ +#define EN0_DCFG 0x0e /* Data configuration reg WR */ +#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */ +#define EN0_IMR 0x0f /* Interrupt mask reg WR */ +#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */ + +#define EN1_PHYS 0x11 +#define EN1_CURPAG 0x17 +#define EN1_MULT 0x18 + +#define EN2_STARTPG 0x21 /* Starting page of ring bfr RD */ +#define EN2_STOPPG 0x22 /* Ending page +1 of ring bfr RD */ + +#define EN3_CONFIG0 0x33 +#define EN3_CONFIG1 0x34 +#define EN3_CONFIG2 0x35 +#define EN3_CONFIG3 0x36 + +/* Register accessed at EN_CMD, the 8390 base addr. */ +#define E8390_STOP 0x01 /* Stop and reset the chip */ +#define E8390_START 0x02 /* Start the chip, clear reset */ +#define E8390_TRANS 0x04 /* Transmit a frame */ +#define E8390_RREAD 0x08 /* Remote read */ +#define E8390_RWRITE 0x10 /* Remote write */ +#define E8390_NODMA 0x20 /* Remote DMA */ +#define E8390_PAGE0 0x00 /* Select page chip registers */ +#define E8390_PAGE1 0x40 /* using the two high-order bits */ +#define E8390_PAGE2 0x80 /* Page 3 is invalid. */ + +/* Bits in EN0_ISR - Interrupt status register */ +#define ENISR_RX 0x01 /* Receiver, no error */ +#define ENISR_TX 0x02 /* Transmitter, no error */ +#define ENISR_RX_ERR 0x04 /* Receiver, with error */ +#define ENISR_TX_ERR 0x08 /* Transmitter, with error */ +#define ENISR_OVER 0x10 /* Receiver overwrote the ring */ +#define ENISR_COUNTERS 0x20 /* Counters need emptying */ +#define ENISR_RDC 0x40 /* remote dma complete */ +#define ENISR_RESET 0x80 /* Reset completed */ +#define ENISR_ALL 0x3f /* Interrupts we will enable */ + +/* Bits in received packet status byte and EN0_RSR*/ +#define ENRSR_RXOK 0x01 /* Received a good packet */ +#define ENRSR_CRC 0x02 /* CRC error */ +#define ENRSR_FAE 0x04 /* frame alignment error */ +#define ENRSR_FO 0x08 /* FIFO overrun */ +#define ENRSR_MPA 0x10 /* missed pkt */ +#define ENRSR_PHY 0x20 /* physical/multicast address */ +#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */ +#define ENRSR_DEF 0x80 /* deferring */ + +/* Transmitted packet status, EN0_TSR. */ +#define ENTSR_PTX 0x01 /* Packet transmitted without error */ +#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */ +#define ENTSR_COL 0x04 /* The transmit collided at least once. */ +#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */ +#define ENTSR_CRS 0x10 /* The carrier sense was lost. */ +#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */ +#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */ +#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */ + +#define NE2000_IRQ 10 +#define NE2000_IOBASE 0x310 + +#define NE2000_PMEM_SIZE (32 * 1024) +#define NE2000_PMEM_START (16 * 1024) +#define NE2000_PMEM_END (NE2000_PMEM_SIZE+NE2000_PMEM_START) +#define NE2000_MEM_SIZE NE2000_PMEM_END + +#define le16_to_cpu(x) x +#define le32_to_cpupu(x) *x +#define cpu_to_le16(x) x +#define cpu_to_le32wu(p,v) *p = v; + +#define NE2000_EADDR0 0x00 /* hard coded address */ +#define NE2000_EADDR1 0x00 /* this will need to be configurable */ +#define NE2000_EADDR2 0x01 +#define NE2000_EADDR3 0x61 +#define NE2000_EADDR4 0x60 +#define NE2000_EADDR5 0x59 + +typedef struct NE2000State { + uint8_t cmd; + uint32_t start; + uint32_t stop; + uint8_t boundary; + uint8_t tsr; + uint8_t tpsr; + uint16_t tcnt; + uint16_t rcnt; + uint32_t rsar; + uint8_t rsr; + uint8_t rxcr; + uint8_t isr; + uint8_t dcfg; + uint8_t imr; + uint8_t phys[6]; /* mac address */ + uint8_t curpag; + uint8_t mult[8]; /* multicast mask array */ + uint8_t mem[NE2000_MEM_SIZE]; + int fdnet; + unsigned long irq; +} NE2000State; + +// Just one instance +static NE2000State ne2000state; + +// For io_device +Bit16u ne2000_io_read16(ioport_t port, void *arg); +void ne2000_io_write16(ioport_t port, Bit16u value, void *arg); +Bit8u ne2000_io_read8(ioport_t port, void *arg); +void ne2000_io_write8(ioport_t port, Bit8u value, void *arg); +static void ne2000_irq_activate(int); + +static void ne2000_receive_req_async(int fd, void *arg); +static size_t ne2000_receive(NE2000State *s, const uint8_t *buf, size_t size_); +static int ne2000_buffer_full(NE2000State *s); + +#ifdef DEBUG_NE2000 +static void N_printhdr(uint8_t *buf); +#endif + +static void init_cbk(int fd, int mode) +{ + ne2000state.fdnet = fd; +} + +void ne2000_init(void) +{ + NE2000State *s = &ne2000state; + emu_iodev_t io_device; + + s->fdnet = -1; + + if (!config.ne2k) + return; + + N_printf("NE2000: ne2000_init()\n"); + + if (OpenNetworkLink(init_cbk) < 0) { + N_printf("NE2000: failed to open network device\n"); + return; + } + + // Setup the IO device within Dosemu + + /* NE2000 Emulation */ + io_device.read_portb = ne2000_io_read8; + io_device.write_portb = ne2000_io_write8; + io_device.read_portw = ne2000_io_read16; + io_device.write_portw = ne2000_io_write16; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + io_device.handler_name = "NE2000 Emulation"; + io_device.start_addr = /* config.ne2000_base */ NE2000_IOBASE; + io_device.end_addr = /* config.ne2000_base */ NE2000_IOBASE + 0x1f; + if (port_register_handler(io_device, 0) != 0) { + N_printf("NE2000: Error registering NE2000 port handler\n"); + ne2000_done(); + return; + } + + /* init control defaults */ + s->irq = NE2000_IRQ; + + N_printf("NE2000: Initialisation - Base 0x%03x, IRQ %d\n", NE2000_IOBASE, NE2000_IRQ); +} + +static void _ne2000_reset(NE2000State *s) +{ + int i; + + if (s->fdnet < 0) { // Not initialised + return; + } + + N_printf("NE2000: ne2000_reset()\n"); + + s->isr = ENISR_RESET; + s->cmd = E8390_STOP; + s->mem[0] = NE2000_EADDR0; + s->mem[1] = NE2000_EADDR1; + s->mem[2] = NE2000_EADDR2; + s->mem[3] = NE2000_EADDR3; + s->mem[4] = NE2000_EADDR4; + s->mem[5] = NE2000_EADDR5; + + // try to get the MAC address from the device and just copy the card id + GetDeviceHardwareAddress(s->mem); + N_printf("NE2000: HWADDR %02x:%02x:%02x:%02x:%02x:%02x\n", + s->mem[0], s->mem[1], s->mem[2], s->mem[3], s->mem[4], s->mem[5]); + + s->mem[14] = 0x57; + s->mem[15] = 0x57; + + /* duplicate prom data */ + for(i = 15;i >= 0; i--) { + s->mem[2 * i] = s->mem[i]; + s->mem[2 * i + 1] = s->mem[i]; + } +} + +void ne2000_reset(void) +{ + _ne2000_reset(&ne2000state); +} + +void ne2000_done(void) +{ + NE2000State *s = &ne2000state; + + if (s->fdnet < 0) { // Not initialised + return; + } + + N_printf("NE2000: ne2000_done()\n"); + + if (!(s->cmd & E8390_STOP)) + remove_from_io_select(s->fdnet); + CloseNetworkLink(s->fdnet); + s->fdnet = -1; +} + +static void ne2000_ether_send(NE2000State *s, uint8_t *buf, int len) +{ + int slen; + +#ifdef DEBUG_NE2000 + N_printf("NE2000: ne2000_ether_send(%p, %d)\n", buf, len); + N_printhdr(buf); +#endif + slen = write(s->fdnet, buf, len); + if (slen < 0) + N_printf("NE2000: write() call failed: %s\n", strerror(errno)); + else if (slen < len) + N_printf("NE2000: write() call underrun: %d/%d\n", slen, len); +} + +static int ne2000_ether_recv(NE2000State *s, uint8_t *buf, int bufsiz) +{ + int ret; + + ret = read(s->fdnet, buf, bufsiz); + ioselect_complete(s->fdnet); + if (ret < 0) { + N_printf("NE2000: ne2000_ether_recv() read failed\n"); + return -1; + } + + N_printf("NE2000: ne2000_ether_recv() read %d bytes\n", ret); + N_printhdr(buf); + return ret; +} + +static void ne2000_receive_req_async(int fd, void *arg) +{ + NE2000State *s = &ne2000state; + uint8_t mybuf[MAX_ETH_FRAME_SIZE]; + int ret; + + if (ne2000_buffer_full(s)) { + N_printf("NE2000: ne2000_receive_req_async() called but buffer full\n"); + return; + } + + N_printf("NE2000: ne2000_receive_req_async() called\n"); + + ret = ne2000_ether_recv(s, mybuf, sizeof mybuf); + if (ret < 0) + return; + + ne2000_receive(s, mybuf, ret); +} + +static void ne2000_update_irq(NE2000State *s) +{ + int isr; + isr = (s->isr & s->imr) & 0x7f; +#if defined(DEBUG_NE2000) + N_printf("NE2000: Set IRQ to %d (%02x %02x)\n", isr ? 1 : 0, s->isr, s->imr); +#endif + ne2000_irq_activate(isr != 0); +} + +static int ne2000_buffer_full(NE2000State *s) +{ + int avail, index, boundary; + + N_printf("NE2000: ne2000_buffer_full()\n"); + + index = s->curpag << 8; + boundary = s->boundary << 8; + if (index < boundary) + avail = boundary - index; + else + avail = (s->stop - s->start) - (index - boundary); + if (avail < (MAX_ETH_FRAME_SIZE + 4)) + return 1; + return 0; +} + +#define MIN_BUF_SIZE 60 + +static size_t ne2000_receive(NE2000State *s, const uint8_t *buf, size_t size_) +{ + size_t size = size_; + uint8_t *p; + unsigned int total_len, next, avail, len, index; + uint8_t buf1[MIN_BUF_SIZE]; + static const uint8_t broadcast_macaddr[6] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + N_printf("NE2000: ne2000_receive()\n"); + +#if defined(DEBUG_NE2000) + N_printf("NE2000: received len=%zd\n", size); +#endif + + if (s->cmd & E8390_STOP || ne2000_buffer_full(s)) + return -1; + + /* XXX: check this */ + if (s->rxcr & 0x10) { + /* promiscuous: receive all */ + } else { + if (!memcmp(buf, broadcast_macaddr, 6)) { + /* broadcast address */ + if (!(s->rxcr & 0x04)) + return size; + } else if (buf[0] & 0x01) { + /* multicast */ + if (!(s->rxcr & 0x08)) + return size; +#if 0 + mcast_idx = compute_mcast_idx(buf); + if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) + return size; +#else + return size; +#endif + } else if (s->mem[0] == buf[0] && + s->mem[2] == buf[1] && + s->mem[4] == buf[2] && + s->mem[6] == buf[3] && + s->mem[8] == buf[4] && + s->mem[10] == buf[5]) { + /* match */ + } else { + return size; + } + } + + + /* if too small buffer, then expand it */ + if (size < MIN_BUF_SIZE) { + memcpy(buf1, buf, size); + memset(buf1 + size, 0, MIN_BUF_SIZE - size); + buf = buf1; + size = MIN_BUF_SIZE; + } + + index = s->curpag << 8; + /* 4 bytes for header */ + total_len = size + 4; + /* address for next packet (4 bytes for CRC) */ + next = index + ((total_len + 4 + 255) & ~0xff); + if (next >= s->stop) + next -= (s->stop - s->start); + /* prepare packet header */ + p = s->mem + index; + s->rsr = ENRSR_RXOK; /* receive status */ + /* XXX: check this */ + if (buf[0] & 0x01) + s->rsr |= ENRSR_PHY; + p[0] = s->rsr; + p[1] = next >> 8; + p[2] = total_len; + p[3] = total_len >> 8; + index += 4; + + /* write packet data */ + while (size > 0) { + if (index <= s->stop) + avail = s->stop - index; + else + avail = 0; + len = size; + if (len > avail) + len = avail; + memcpy(s->mem + index, buf, len); + buf += len; + index += len; + if (index == s->stop) + index = s->start; + size -= len; + } + s->curpag = next >> 8; + + /* now we can signal we have received something */ + s->isr |= ENISR_RX; + ne2000_update_irq(s); + + return size_; +} + +static void ne2000_ioport_write(NE2000State *s, uint32_t addr, uint32_t val) +{ + int offset, page, index; + int old_full = ne2000_buffer_full(s); + + N_printf("NE2000: ne2000_ioport_write()\n"); + + addr &= 0xf; +#ifdef DEBUG_NE2000 + N_printf("NE2000: write addr=0x%x val=0x%02x\n", addr, val); +#endif + if (addr == E8390_CMD) { + uint32_t old_cmd = s->cmd; + /* control register */ + s->cmd = val; + if (!(val & E8390_STOP)) { /* START bit makes no sense on RTL8029... */ + if (old_cmd & E8390_STOP) { + N_printf("NE2000: enable receiver\n"); + add_to_io_select(s->fdnet, ne2000_receive_req_async, NULL); + } + s->isr &= ~ENISR_RESET; + /* test specific case: zero length transfer */ + if ((val & (E8390_RREAD | E8390_RWRITE)) && + s->rcnt == 0) { + s->isr |= ENISR_RDC; + ne2000_update_irq(s); + } + if (val & E8390_TRANS) { + index = (s->tpsr << 8); + /* XXX: next 2 lines are a hack to make netware 3.11 work */ + if (index >= NE2000_PMEM_END) + index -= NE2000_PMEM_SIZE; + /* fail safe: check range on the transmitted length */ + if (index + s->tcnt <= NE2000_PMEM_END) { + ne2000_ether_send(s, s->mem + index, s->tcnt); + } + /* signal end of transfer */ + s->tsr = ENTSR_PTX; + s->isr |= ENISR_TX; + s->cmd &= ~E8390_TRANS; + ne2000_update_irq(s); + } + } else { + if (!(old_cmd & E8390_STOP)) { + N_printf("NE2000: disable receiver\n"); + remove_from_io_select(s->fdnet); + } + } + } else { + page = s->cmd >> 6; + offset = addr | (page << 4); + switch(offset) { + case EN0_STARTPG: + s->start = val << 8; + break; + case EN0_STOPPG: + s->stop = val << 8; + break; + case EN0_BOUNDARY: + s->boundary = val; + break; + case EN0_IMR: + s->imr = val; + ne2000_update_irq(s); + break; + case EN0_TPSR: + s->tpsr = val; + break; + case EN0_TCNTLO: + s->tcnt = (s->tcnt & 0xff00) | val; + break; + case EN0_TCNTHI: + s->tcnt = (s->tcnt & 0x00ff) | (val << 8); + break; + case EN0_RSARLO: + s->rsar = (s->rsar & 0xff00) | val; + break; + case EN0_RSARHI: + s->rsar = (s->rsar & 0x00ff) | (val << 8); + break; + case EN0_RCNTLO: + s->rcnt = (s->rcnt & 0xff00) | val; + break; + case EN0_RCNTHI: + s->rcnt = (s->rcnt & 0x00ff) | (val << 8); + break; + case EN0_RXCR: + s->rxcr = val; + break; + case EN0_DCFG: + s->dcfg = val; + break; + case EN0_ISR: + s->isr &= ~(val & 0x7f); + ne2000_update_irq(s); + break; + case EN1_PHYS ... EN1_PHYS + 5: + s->phys[offset - EN1_PHYS] = val; + break; + case EN1_CURPAG: + s->curpag = val; + break; + case EN1_MULT ... EN1_MULT + 7: + s->mult[offset - EN1_MULT] = val; + break; + } + } + + /* flush if buffer was full */ + if (old_full && !ne2000_buffer_full(s)) + ioselect_complete(s->fdnet); +} + +static uint32_t ne2000_ioport_read(NE2000State *s, uint32_t addr) +{ + int offset, page, ret; + + N_printf("NE2000: ne2000_ioport_read()\n"); + + addr &= 0xf; + if (addr == E8390_CMD) { + ret = s->cmd; + } else { + page = s->cmd >> 6; + offset = addr | (page << 4); + switch(offset) { + case EN0_TSR: + ret = s->tsr; + break; + case EN0_BOUNDARY: + ret = s->boundary; + break; + case EN0_ISR: + ret = s->isr; + break; + case EN0_RSARLO: + ret = s->rsar & 0x00ff; + break; + case EN0_RSARHI: + ret = s->rsar >> 8; + break; + case EN1_PHYS ... EN1_PHYS + 5: + ret = s->phys[offset - EN1_PHYS]; + break; + case EN1_CURPAG: + ret = s->curpag; + break; + case EN1_MULT ... EN1_MULT + 7: + ret = s->mult[offset - EN1_MULT]; + break; + case EN0_RSR: + ret = s->rsr; + break; + case EN2_STARTPG: + ret = s->start >> 8; + break; + case EN2_STOPPG: + ret = s->stop >> 8; + break; + case EN0_RTL8029ID0: + ret = 0x50; + break; + case EN0_RTL8029ID1: + ret = 0x43; + break; + case EN3_CONFIG0: + ret = 0; /* 10baseT media */ + break; + case EN3_CONFIG2: + ret = 0x40; /* 10baseT active */ + break; + case EN3_CONFIG3: + ret = 0x40; /* Full duplex */ + break; + default: + ret = 0x00; + break; + } + } +#ifdef DEBUG_NE2000 + N_printf("NE2000: read addr=0x%x val=%02x\n", addr, ret); +#endif + return ret; +} + +static inline void ne2000_mem_writeb(NE2000State *s, uint32_t addr, + uint32_t val) +{ + N_printf("NE2000: ne2000_mem_writeb()\n"); + + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + s->mem[addr] = val; + } +} + +static inline void ne2000_mem_writew(NE2000State *s, uint32_t addr, + uint32_t val) +{ + N_printf("NE2000: ne2000_mem_writew()\n"); + + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + *(uint16_t *)(s->mem + addr) = cpu_to_le16(val); + } +} + +static inline void ne2000_mem_writel(NE2000State *s, uint32_t addr, + uint32_t val) +{ + N_printf("NE2000: ne2000_mem_writel()\n"); + + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + cpu_to_le32wu((uint32_t *)(s->mem + addr), val); + } +} + +static inline uint32_t ne2000_mem_readb(NE2000State *s, uint32_t addr) +{ + N_printf("NE2000: ne2000_mem_readb()\n"); + + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + return s->mem[addr]; + } else { + return 0xff; + } +} + +static inline uint32_t ne2000_mem_readw(NE2000State *s, uint32_t addr) +{ + N_printf("NE2000: ne2000_mem_readw()\n"); + + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + return le16_to_cpu(*(uint16_t *)(s->mem + addr)); + } else { + return 0xffff; + } +} + +static inline uint32_t ne2000_mem_readl(NE2000State *s, uint32_t addr) +{ + N_printf("NE2000: ne2000_mem_readl()\n"); + + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || + (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { + return le32_to_cpupu((uint32_t *)(s->mem + addr)); + } else { + return 0xffffffff; + } +} + +static inline void ne2000_dma_update(NE2000State *s, int len) +{ + N_printf("NE2000: ne2000_dma_update()\n"); + + s->rsar += len; + /* wrap */ + /* XXX: check what to do if rsar > stop */ + if (s->rsar == s->stop) + s->rsar = s->start; + + if (s->rcnt <= len) { + s->rcnt = 0; + /* signal end of transfer */ + s->isr |= ENISR_RDC; + ne2000_update_irq(s); + } else { + s->rcnt -= len; + } +} + +static void ne2000_asic_ioport_write(NE2000State *s, uint32_t addr, uint32_t val) +{ + N_printf("NE2000: ne2000_asic_ioport_write()\n"); + +#ifdef DEBUG_NE2000 + N_printf("NE2000: asic write val=0x%04x\n", val); +#endif + if (s->rcnt == 0) + return; + if (s->dcfg & 0x01) { + /* 16 bit access */ + ne2000_mem_writew(s, s->rsar, val); + ne2000_dma_update(s, 2); + } else { + /* 8 bit access */ + ne2000_mem_writeb(s, s->rsar, val); + ne2000_dma_update(s, 1); + } +} + +static uint32_t ne2000_asic_ioport_read(NE2000State *s, uint32_t addr) +{ + int ret; + + N_printf("NE2000: ne2000_asic_ioport_read()\n"); + + if (s->dcfg & 0x01) { + /* 16 bit access */ + ret = ne2000_mem_readw(s, s->rsar); + ne2000_dma_update(s, 2); + } else { + /* 8 bit access */ + ret = ne2000_mem_readb(s, s->rsar); + ne2000_dma_update(s, 1); + } +#ifdef DEBUG_NE2000 + N_printf("NE2000: asic read val=0x%04x\n", ret); +#endif + return ret; +} + +static void ne2000_asic_ioport_writel(NE2000State *s, uint32_t addr, uint32_t val) +{ + N_printf("NE2000: ne2000_asic_ioport_writel()\n"); + +#ifdef DEBUG_NE2000 + N_printf("NE2000: asic writel val=0x%04x\n", val); +#endif + if (s->rcnt == 0) + return; + /* 32 bit access */ + ne2000_mem_writel(s, s->rsar, val); + ne2000_dma_update(s, 4); +} + +static uint32_t ne2000_asic_ioport_readl(NE2000State *s, uint32_t addr) +{ + int ret; + + N_printf("NE2000: ne2000_asic_ioport_readl()\n"); + + /* 32 bit access */ + ret = ne2000_mem_readl(s, s->rsar); + ne2000_dma_update(s, 4); +#ifdef DEBUG_NE2000 + N_printf("NE2000: asic readl val=0x%04x\n", ret); +#endif + return ret; +} + +static void ne2000_reset_ioport_write(NE2000State *s, uint32_t addr, uint32_t val) +{ + /* nothing to do (end of reset pulse) */ + N_printf("NE2000: ne2000_reset_ioport_write()\n"); +} + +static uint32_t ne2000_reset_ioport_read(NE2000State *s, uint32_t addr) +{ + N_printf("NE2000: ne2000_reset_ioport_read()\n"); + + _ne2000_reset(s); + return 0; +} + +static uint64_t ne2000_read(NE2000State *s, uint32_t addr, unsigned size) +{ + N_printf("NE2000: ne2000_read()\n"); + + if (addr < 0x10 && size == 1) { + return ne2000_ioport_read(s, addr); + } else if (addr == 0x10) { + if (size <= 2) { + return ne2000_asic_ioport_read(s, addr); + } else { + return ne2000_asic_ioport_readl(s, addr); + } + } else if (addr == 0x1f && size == 1) { + return ne2000_reset_ioport_read(s, addr); + } + return ((uint64_t)1 << (size * 8)) - 1; +} + +static void ne2000_write(NE2000State *s, uint32_t addr, uint64_t data, unsigned size) +{ + N_printf("NE2000: ne2000_write()\n"); + + if (addr < 0x10 && size == 1) { + ne2000_ioport_write(s, addr, data); + } else if (addr == 0x10) { + if (size <= 2) { + ne2000_asic_ioport_write(s, addr, data); + } else { + ne2000_asic_ioport_writel(s, addr, data); + } + } else if (addr == 0x1f && size == 1) { + ne2000_reset_ioport_write(s, addr, data); + } +} + +/* from Scott Pitcher's driver */ + +/* --------------------------------- */ +/* 16 bit io functions - only on data port */ + +Bit16u ne2000_io_read16(ioport_t port, void *arg) +{ + NE2000State *s = &ne2000state; + ioport_t addr = port - NE2000_IOBASE; + + N_printf("\nNE2000: ne2000_io_read16()\n"); + + if (addr == 0x10) + return ne2000_read(s, addr, 2); + else + return ne2000_read(s, addr, 1); +} + +void ne2000_io_write16(ioport_t port, Bit16u value, void *arg) +{ + NE2000State *s = &ne2000state; + ioport_t addr = port - NE2000_IOBASE; + + N_printf("\nNE2000: ne2000_io_write16()\n"); + + if (addr == 0x10) + ne2000_write(s, addr, value, 2); + else + ne2000_write(s, addr, (uint8_t)value, 1); /* default to 8 bit */ +} + +/* --------------------------------- */ + +/* handle io reads from ne2000 */ + +Bit8u ne2000_io_read8(ioport_t port, void *arg) +{ + NE2000State *s = &ne2000state; + ioport_t addr = port - NE2000_IOBASE; + + N_printf("\nNE2000: ne2000_io_read8() %d\n", addr); + return ne2000_read(s, addr, 1); +} + +/* --------------------------------- */ + +/* handle io writes to ne2000 */ + +void ne2000_io_write8(ioport_t port, Bit8u value, void *arg) +{ + NE2000State *s = &ne2000state; + ioport_t addr = port - NE2000_IOBASE; + + N_printf("\nNE2000: ne2000_io_write8() %d, 0x%02x\n", addr, value); + ne2000_write(s, addr, value, 1); +} + +/* activate our irq */ +static void ne2000_irq_activate(int level) +{ + NE2000State *s = &ne2000state; + + N_printf("NE2000: ne2000_irq_activate(%d)\n", level); + + if (level) + pic_request(s->irq); + else + pic_untrigger(s->irq); +} + +/* debug print an ethernet header */ +#ifdef DEBUG_NE2000 +static void N_printhdr(uint8_t *buf) +{ + N_printf("NE2000: dest[%02x,%02x,%02x,%02x,%02x,%02x]\n" + " src[%02x,%02x,%02x,%02x,%02x,%02x]\n" + " prot[%02x,%02x]\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], + buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], + buf[12], buf[13]); +} +#endif diff --git a/src/base/dev/pic/Makefile b/src/base/dev/pic/Makefile new file mode 100644 index 0000000..475e459 --- /dev/null +++ b/src/base/dev/pic/Makefile @@ -0,0 +1,11 @@ + +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf + +CFILES = pic.c irq.c i8259.c i8259_common.c + +all: lib + +install: + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/dev/pic/i8259.c b/src/base/dev/pic/i8259.c new file mode 100644 index 0000000..2224759 --- /dev/null +++ b/src/base/dev/pic/i8259.c @@ -0,0 +1,359 @@ +/* + * QEMU 8259 interrupt controller emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "i8259.h" +#include "i8259_internal.h" + +/* debug PIC */ +//#define DEBUG_PIC + +//#define DEBUG_IRQ_LATENCY + +#define trace_pic_update_irq(...) +#define trace_pic_set_irq(...) +#define trace_pic_interrupt(...) +#define trace_pic_ioport_write(...) +#define trace_pic_ioport_read(...) +#define qemu_log_mask(...) + +#define TYPE_I8259 "isa-i8259" + +#ifdef DEBUG_IRQ_LATENCY +static int64_t irq_time[16]; +#endif +extern PICCommonState *slave_pic; + +/* return the highest priority found in mask (highest = smallest + number). Return 8 if no irq */ +static int get_priority(PICCommonState *s, int mask) +{ + int priority; + + if (mask == 0) { + return 8; + } + priority = 0; + while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) { + priority++; + } + return priority; +} + +/* return the pic wanted interrupt. return -1 if none */ +static int pic_get_irq(PICCommonState *s) +{ + int mask, cur_priority, priority; + + mask = s->irr & ~s->imr; + priority = get_priority(s, mask); + if (priority == 8) { + return -1; + } + /* compute current priority. If special fully nested mode on the + master, the IRQ coming from the slave is not taken into account + for the priority computation. */ + mask = s->isr; + if (s->special_mask) { + mask &= ~s->imr; + } + if (s->special_fully_nested_mode && s->master) { + mask &= ~(1 << 2); + } + cur_priority = get_priority(s, mask); + if (priority < cur_priority) { + /* higher priority found: an irq should be generated */ + return (priority + s->priority_add) & 7; + } else { + return -1; + } +} + +/* Update INT output. Must be called every time the output may have changed. */ +static void pic_update_irq(PICCommonState *s) +{ + int irq; + + irq = pic_get_irq(s); + if (irq >= 0) { + trace_pic_update_irq(s->master, s->imr, s->irr, s->priority_add); + qemu_irq_raise(s->int_out[0]); + } else { + qemu_irq_lower(s->int_out[0]); + } +} + +/* set irq level. If an edge is detected, then the IRR is set to 1 */ +void pic_set_irq(PICCommonState *s, int irq, int level) +{ + int mask = 1 << irq; + int irq_index = s->master ? irq : irq + 8; + + trace_pic_set_irq(s->master, irq, level); + pic_stat_update_irq(irq_index, level); + +#ifdef DEBUG_IRQ_LATENCY + if (level) { + irq_time[irq_index] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + } +#endif + + if (s->elcr & mask) { + /* level triggered */ + if (level) { + s->irr |= mask; + s->last_irr |= mask; + } else { + s->irr &= ~mask; + s->last_irr &= ~mask; + } + } else { + /* edge triggered */ + if (level) { + if ((s->last_irr & mask) == 0) { + s->irr |= mask; + } + s->last_irr |= mask; + } else { + s->irr &= ~mask; // dosemu2 mod: clear IRR bit even in edge mode + s->last_irr &= ~mask; + } + } + pic_update_irq(s); +} + +/* acknowledge interrupt 'irq' */ +static void pic_intack(PICCommonState *s, int irq) +{ + if (s->auto_eoi) { + if (s->rotate_on_auto_eoi) { + s->priority_add = (irq + 1) & 7; + } + } else { + s->isr |= (1 << irq); + } + /* We don't clear a level sensitive interrupt here */ + if (!(s->elcr & (1 << irq))) { + s->irr &= ~(1 << irq); + } + pic_update_irq(s); +} + +int pic_read_irq(PICCommonState *s) +{ + int irq, irq2, intno; + + irq = pic_get_irq(s); + if (irq >= 0) { + if (irq == 2) { + irq2 = pic_get_irq(slave_pic); + if (irq2 >= 0) { + pic_intack(slave_pic, irq2); + } else { + /* spurious IRQ on slave controller */ + irq2 = 7; + } + intno = slave_pic->irq_base + irq2; + } else { + intno = s->irq_base + irq; + } + pic_intack(s, irq); + } else { + /* spurious IRQ on host controller */ + irq = 7; + intno = s->irq_base + irq; + } + + if (irq == 2) { + irq = irq2 + 8; + } + +#ifdef DEBUG_IRQ_LATENCY + printf("IRQ%d latency=%0.3fus\n", + irq, + (double)(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - + irq_time[irq]) * 1000000.0 / NANOSECONDS_PER_SECOND); +#endif + + trace_pic_interrupt(irq, intno); + return intno; +} + +static void pic_init_reset(PICCommonState *s) +{ + pic_reset_common(s); + pic_update_irq(s); +} + +void qemu_pic_reset(PICCommonState *s) +{ + s->elcr = 0; + pic_init_reset(s); +} + +void pic_ioport_write(PICCommonState *s, hwaddr addr64, + uint64_t val64, unsigned size) +{ + uint32_t addr = addr64; + uint32_t val = val64; + int priority, cmd, irq; + + trace_pic_ioport_write(s->master, addr, val); + + if (s->poll) { // dosemu2 mod: poll extension + s->fake_irr &= ~(1 << val); + pic_intack(s, val); + s->poll = 0; + } else if (addr == 0) { + if (val & 0x10) { + pic_init_reset(s); + s->init_state = 1; + s->init4 = val & 1; + s->single_mode = val & 2; + if (val & 0x08) { + qemu_log_mask(LOG_UNIMP, + "i8259: level sensitive irq not supported\n"); + } + } else if (val & 0x08) { + if (val & 0x04) { + s->poll = 1; + } + if (val & 0x02) { + s->read_reg_select = val & 1; + } + if (val & 0x40) { + s->special_mask = (val >> 5) & 1; + } + } else { + cmd = val >> 5; + switch (cmd) { + case 0: + case 4: + s->rotate_on_auto_eoi = cmd >> 2; + break; + case 1: /* end of interrupt */ + case 5: + priority = get_priority(s, s->isr); + if (priority != 8) { + irq = (priority + s->priority_add) & 7; + s->isr &= ~(1 << irq); + if (cmd == 5) { + s->priority_add = (irq + 1) & 7; + } + pic_update_irq(s); + } + break; + case 3: + irq = val & 7; + s->isr &= ~(1 << irq); + pic_update_irq(s); + break; + case 6: + s->priority_add = (val + 1) & 7; + pic_update_irq(s); + break; + case 7: + irq = val & 7; + s->isr &= ~(1 << irq); + s->priority_add = (irq + 1) & 7; + pic_update_irq(s); + break; + default: + /* no operation */ + break; + } + } + } else { + switch (s->init_state) { + case 0: + /* normal mode */ + s->imr = val; + pic_update_irq(s); + break; + case 1: + s->irq_base = val & 0xf8; + s->init_state = s->single_mode ? (s->init4 ? 3 : 0) : 2; + break; + case 2: + if (s->init4) { + s->init_state = 3; + } else { + s->init_state = 0; + } + break; + case 3: + s->special_fully_nested_mode = (val >> 4) & 1; + s->auto_eoi = (val >> 1) & 1; + s->init_state = 0; + break; + } + } +} + +uint64_t pic_ioport_read(PICCommonState *s, hwaddr addr, unsigned size) +{ + int ret; + +#if 0 + if (s->poll) { +#else + if (s->poll && addr == 0) { // dosemu2 mod +#endif + ret = pic_get_irq(s); + if (ret >= 0) { + pic_intack(s, ret); + ret |= 0x80; + } else { + ret = 0; + } + s->poll = 0; + } else { + if (addr == 0) { + if (s->read_reg_select) { + ret = s->isr; + } else { + ret = s->irr | s->fake_irr; + } + } else { + ret = s->imr; + } + } + trace_pic_ioport_read(s->master, addr, ret); + return ret; +} + +int pic_get_output(PICCommonState *s) +{ + return (pic_get_irq(s) >= 0); +} + +void elcr_ioport_write(PICCommonState *s, hwaddr addr, + uint64_t val, unsigned size) +{ + s->elcr = val & s->elcr_mask; +} + +uint64_t elcr_ioport_read(PICCommonState *s, hwaddr addr, unsigned size) +{ + return s->elcr; +} diff --git a/src/base/dev/pic/i8259.h b/src/base/dev/pic/i8259.h new file mode 100644 index 0000000..93d1bdc --- /dev/null +++ b/src/base/dev/pic/i8259.h @@ -0,0 +1,23 @@ +#ifndef HW_I8259_H +#define HW_I8259_H + +#include + +typedef uint32_t hwaddr; + +/* i8259.c */ + +typedef struct PICCommonState PICCommonState; +int pic_get_output(PICCommonState *s); +int pic_read_irq(PICCommonState *s); + +void qemu_pic_reset(PICCommonState *s); +void pic_set_irq(PICCommonState *s, int irq, int level); +void pic_ioport_write(PICCommonState *s, hwaddr addr64, + uint64_t val64, unsigned size); +uint64_t pic_ioport_read(PICCommonState *s, hwaddr addr, unsigned size); +void elcr_ioport_write(PICCommonState *s, hwaddr addr, + uint64_t val, unsigned size); +uint64_t elcr_ioport_read(PICCommonState *s, hwaddr addr, unsigned size); + +#endif diff --git a/src/base/dev/pic/i8259_common.c b/src/base/dev/pic/i8259_common.c new file mode 100644 index 0000000..85c079c --- /dev/null +++ b/src/base/dev/pic/i8259_common.c @@ -0,0 +1,76 @@ +/* + * QEMU 8259 - common bits of emulated and KVM kernel model + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2011 Jan Kiszka, Siemens AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "utilities.h" +#include "i8259.h" +#include "i8259_internal.h" + +static int irq_level[16]; +static uint64_t irq_count[16]; + +void pic_reset_common(PICCommonState *s) +{ + s->last_irr = 0; + s->irr &= s->elcr; + s->imr = 0; + s->isr = 0; + s->priority_add = 0; + s->irq_base = 0; + s->read_reg_select = 0; + s->poll = 0; + s->special_mask = 0; + s->init_state = 0; + s->auto_eoi = 0; + s->rotate_on_auto_eoi = 0; + s->special_fully_nested_mode = 0; + s->init4 = 0; + s->single_mode = 0; + /* Note: ELCR is not reset */ + s->fake_irr = 0; +} + +void pic_stat_update_irq(int irq, int level) +{ + if (level != irq_level[irq]) { + irq_level[irq] = level; + if (level == 1) { + irq_count[irq]++; + } + } +} + +bool pic_get_statistics(PICCommonState *s, + uint64_t **irq_counts, unsigned int *nb_irqs) +{ + if (s->master) { + *irq_counts = irq_count; + *nb_irqs = ARRAY_SIZE(irq_count); + } else { + *irq_counts = NULL; + *nb_irqs = 0; + } + + return true; +} diff --git a/src/base/dev/pic/i8259_internal.h b/src/base/dev/pic/i8259_internal.h new file mode 100644 index 0000000..c803e1b --- /dev/null +++ b/src/base/dev/pic/i8259_internal.h @@ -0,0 +1,64 @@ +/* + * QEMU 8259 - internal interfaces + * + * Copyright (c) 2011 Jan Kiszka, Siemens AG + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef QEMU_I8259_INTERNAL_H +#define QEMU_I8259_INTERNAL_H + +#include +#include +#include "irq.h" +#include "i8259.h" + +struct PICCommonState { + uint8_t last_irr; /* edge detection */ + uint8_t irr; /* interrupt request register */ + uint8_t imr; /* interrupt mask register */ + uint8_t isr; /* interrupt service register */ + uint8_t priority_add; /* highest irq priority */ + uint8_t irq_base; + uint8_t read_reg_select; + uint8_t poll; + uint8_t special_mask; + uint8_t init_state; + uint8_t auto_eoi; + uint8_t rotate_on_auto_eoi; + uint8_t special_fully_nested_mode; + uint8_t init4; /* true if 4 byte init */ + uint8_t single_mode; /* true if slave pic is not initialized */ + uint8_t elcr; /* PIIX edge/trigger selection*/ + uint8_t elcr_mask; + struct IRQState _int_out; + qemu_irq int_out[1]; + uint32_t master; /* reflects /SP input pin */ + uint32_t iobase; + uint32_t elcr_addr; + uint8_t fake_irr; /* dosemu2 extension */ +}; + +void pic_reset_common(PICCommonState *s); +void pic_stat_update_irq(int irq, int level); +bool pic_get_statistics(PICCommonState *s, + uint64_t **irq_counts, unsigned int *nb_irqs); + +#endif /* QEMU_I8259_INTERNAL_H */ diff --git a/src/base/dev/pic/irq.c b/src/base/dev/pic/irq.c new file mode 100644 index 0000000..216bfc6 --- /dev/null +++ b/src/base/dev/pic/irq.c @@ -0,0 +1,32 @@ +/* + * QEMU IRQ/GPIO common code. + * + * Copyright (c) 2007 CodeSourcery. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "irq.h" + +void qemu_set_irq(qemu_irq irq, int level) +{ + if (!irq) + return; + + irq->handler(irq->opaque, irq->n, level); +} diff --git a/src/base/dev/pic/irq.h b/src/base/dev/pic/irq.h new file mode 100644 index 0000000..c199e90 --- /dev/null +++ b/src/base/dev/pic/irq.h @@ -0,0 +1,34 @@ +#ifndef QEMU_IRQ_H +#define QEMU_IRQ_H + +/* Generic IRQ/GPIO pin infrastructure. */ + +#define TYPE_IRQ "irq" + +typedef struct IRQState *qemu_irq; +typedef void (*qemu_irq_handler)(void *opaque, int n, int level); +struct IRQState { + qemu_irq_handler handler; + void *opaque; + int n; +}; + +void qemu_set_irq(qemu_irq irq, int level); + +static inline void qemu_irq_raise(qemu_irq irq) +{ + qemu_set_irq(irq, 1); +} + +static inline void qemu_irq_lower(qemu_irq irq) +{ + qemu_set_irq(irq, 0); +} + +static inline void qemu_irq_pulse(qemu_irq irq) +{ + qemu_set_irq(irq, 1); + qemu_set_irq(irq, 0); +} + +#endif diff --git a/src/base/dev/pic/pic.c b/src/base/dev/pic/pic.c new file mode 100644 index 0000000..a9c9bcf --- /dev/null +++ b/src/base/dev/pic/pic.c @@ -0,0 +1,294 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: glue to qemu PIC impl. + * + * Author: stsp + * + */ +#include +#include "port.h" +#include "sig.h" +#include "dosemu_debug.h" +#include "i8259.h" +#include "i8259_internal.h" +#include "pic.h" + +static PICCommonState pic[2]; +PICCommonState *slave_pic; +static pthread_mutex_t pic_mtx = PTHREAD_MUTEX_INITIALIZER; + +static void write_pic0(ioport_t port, Bit8u value, void *arg) +{ + r_printf("PIC0: write 0x%x --> 0x%x\n", value, port); + pthread_mutex_lock(&pic_mtx); + pic_ioport_write(&pic[0], port - 0x20, value, 1); + pthread_mutex_unlock(&pic_mtx); + r_printf("PIC0: isr=%x imr=%x irr=%x\n", + pic[0].isr, pic[0].imr, pic[0].irr); +} + +static void write_pic1(ioport_t port, Bit8u value, void *arg) +{ + r_printf("PIC1: write 0x%x --> 0x%x\n", value, port); + pthread_mutex_lock(&pic_mtx); + pic_ioport_write(&pic[1], port - 0xa0, value, 1); + pthread_mutex_unlock(&pic_mtx); + r_printf("PIC1: isr=%x imr=%x irr=%x\n", + pic[1].isr, pic[1].imr, pic[1].irr); +} + +static Bit8u read_pic0(ioport_t port, void *arg) +{ + Bit8u ret; + + pthread_mutex_lock(&pic_mtx); + ret = pic_ioport_read(&pic[0], port - 0x20, 1); + pthread_mutex_unlock(&pic_mtx); + r_printf("PIC0: read 0x%x <-- 0x%x\n", ret, port); + return ret; +} + +static Bit8u read_pic1(ioport_t port, void *arg) +{ + Bit8u ret; + + pthread_mutex_lock(&pic_mtx); + ret = pic_ioport_read(&pic[1], port - 0xa0, 1); + pthread_mutex_unlock(&pic_mtx); + r_printf("PIC1: read 0x%x <-- 0x%x\n", ret, port); + return ret; +} + +static Bit8u read_elcr(ioport_t port, void *arg) +{ + Bit8u ret; + + pthread_mutex_lock(&pic_mtx); + ret = elcr_ioport_read(&pic[port & 1], port & 1, 1); + pthread_mutex_unlock(&pic_mtx); + return ret; +} + +static void write_elcr(ioport_t port, Bit8u value, void *arg) +{ + pthread_mutex_lock(&pic_mtx); + elcr_ioport_write(&pic[port & 1], port & 1, value, 1); + pthread_mutex_unlock(&pic_mtx); +} + +static Bit8u read_firr(ioport_t port, void *arg) +{ + Bit8u ret; + + pthread_mutex_lock(&pic_mtx); + ret = pic[port & 1].fake_irr; + pthread_mutex_unlock(&pic_mtx); + return ret; +} + +static void write_firr(ioport_t port, Bit8u value, void *arg) +{ + pthread_mutex_lock(&pic_mtx); + pic[port & 1].fake_irr = value; + pthread_mutex_unlock(&pic_mtx); +} + +void pic_request(int irq) +{ + PICCommonState *p = pic; + + r_printf("PIC: Requested irq lvl %x\n", irq); + if (irq >= 8) { + irq -= 8; + p++; + } + pthread_mutex_lock(&pic_mtx); + pic_set_irq(p, irq, 1); + pthread_mutex_unlock(&pic_mtx); + r_printf("PIC%i: isr=%x imr=%x irr=%x\n", + p->master ? 0 : 1, p->isr, p->imr, p->irr); +} + +void pic_untrigger(int irq) +{ + PICCommonState *p = pic; + + r_printf("PIC: irq lvl %x untriggered\n", irq); + if (irq >= 8) { + irq -= 8; + p++; + } + pthread_mutex_lock(&pic_mtx); + pic_set_irq(p, irq, 0); + pthread_mutex_unlock(&pic_mtx); + r_printf("PIC%i: isr=%x imr=%x irr=%x\n", + p->master ? 0 : 1, p->isr, p->imr, p->irr); +} + +int pic_get_inum(void) +{ + int inum; + + pthread_mutex_lock(&pic_mtx); + if (!slave_pic) + slave_pic = &pic[1]; + inum = pic_read_irq(&pic[0]); + pthread_mutex_unlock(&pic_mtx); + r_printf("PIC: Running interrupt %x\n", inum); + return inum; +} + +static void set_irq_level(void *opaque, int n, int level) +{ + /* running under mutex */ + r_printf("PIC: Cascade irq %i set to %i\n", n, level); + pic_set_irq(opaque, n, level); +} + +static void int_raise(void *arg) +{ + int level = (uintptr_t)arg; + /* If we are here, guest code already interrupted. So nothing to do. */ + r_printf("int level from thread set to %i\n", level); +} + +static void set_int_out(void *opaque, int n, int level) +{ + pthread_t thr = (pthread_t)opaque; + + r_printf("PIC: int out set to %i\n", level); + if (!pthread_equal(thr, pthread_self())) + add_thread_callback(int_raise, (void *)(uintptr_t)level, "pic"); +} + +void pic_init(void) +{ + /* do any one-time initialization of the PIC */ + emu_iodev_t io_device; + + /* 8259 PIC (Programmable Interrupt Controller) */ + io_device.read_portb = read_pic0; + io_device.write_portb = write_pic0; + io_device.read_portw = NULL; + io_device.write_portw = NULL; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + io_device.handler_name = "8259 PIC0"; + io_device.start_addr = 0x0020; + io_device.end_addr = 0x0021; + port_register_handler(io_device, 0); + + io_device.handler_name = "8259 PIC1"; + io_device.start_addr = 0x00A0; + io_device.end_addr = 0x00A1; + io_device.read_portb = read_pic1; + io_device.write_portb = write_pic1; + port_register_handler(io_device, 0); + + io_device.handler_name = "ELCR"; + io_device.start_addr = 0x04D0; + io_device.end_addr = 0x04D1; + io_device.read_portb = read_elcr; + io_device.write_portb = write_elcr; + port_register_handler(io_device, 0); + + io_device.handler_name = "fake irr"; + io_device.start_addr = 0x04D2; + io_device.end_addr = 0x04D3; + io_device.read_portb = read_firr; + io_device.write_portb = write_firr; + port_register_handler(io_device, 0); + + /* set up cascading bits */ + pic[0].master = 1; + pic[0].int_out[0] = &pic[0]._int_out; + pic[0]._int_out.handler = set_int_out; + pic[0]._int_out.opaque = (void *)pthread_self(); + pic[0]._int_out.n = 0; + + pic[1].int_out[0] = &pic[1]._int_out; + pic[1]._int_out.handler = set_irq_level; + pic[1]._int_out.opaque = &pic[0]; + pic[1]._int_out.n = 2; + /* set up qemu extensions */ + pic[0].elcr_mask = 0xf8; + pic[1].elcr_mask = 0xde; +} + +void pic_reset(void) +{ + qemu_pic_reset(&pic[0]); + qemu_pic_reset(&pic[1]); +} + +/* PIC extensions */ + +Bit8u pic0_get_base(void) +{ + Bit8u ret; + + pthread_mutex_lock(&pic_mtx); + ret = pic[0].irq_base; + pthread_mutex_unlock(&pic_mtx); + return ret; +} + +Bit8u pic1_get_base(void) +{ + Bit8u ret; + + pthread_mutex_lock(&pic_mtx); + ret = pic[1].irq_base; + pthread_mutex_unlock(&pic_mtx); + return ret; +} + +unsigned pic_get_isr(void) +{ + unsigned ret; + + pthread_mutex_lock(&pic_mtx); + ret = (pic[0].isr | (pic[1].isr << 8)); + pthread_mutex_unlock(&pic_mtx); + return ret; +} + +int pic_pending(void) +{ + int ret; + + pthread_mutex_lock(&pic_mtx); + ret = pic_get_output(&pic[0]); + pthread_mutex_unlock(&pic_mtx); + return ret; +} + +int pic_irq_active(int irq) +{ + int ret; + PICCommonState *p = pic; + + if (irq >= 8) { + irq -= 8; + p++; + } + pthread_mutex_lock(&pic_mtx); + ret = !!((1 << irq) & p->isr); + pthread_mutex_unlock(&pic_mtx); + return ret; +} diff --git a/src/base/dev/sb16/Makefile b/src/base/dev/sb16/Makefile new file mode 100644 index 0000000..94210ba --- /dev/null +++ b/src/base/dev/sb16/Makefile @@ -0,0 +1,14 @@ +# +# (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". +# +# for details see file COPYING.DOSEMU in the DOSEMU distribution +# + +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf + + +CFILES = sb16.c dspio.c adlib.c opl.c dbadlib.c mpu401.c mt32.c +ALL_CPPFLAGS += -DOPLTYPE_IS_OPL3 + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/dev/sb16/adlib.c b/src/base/dev/sb16/adlib.c new file mode 100644 index 0000000..e81476e --- /dev/null +++ b/src/base/dev/sb16/adlib.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2006 Stas Sergeev + * + * The below copyright strings have to be distributed unchanged together + * with this file. This prefix can not be modified or separated. + */ + +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: glue between dosemu and the DOSBOX/MAME OPL3 emulator. + * + * Author: Stas Sergeev. + */ + +#include "emu.h" +#include "port.h" +#include "timers.h" +#include "utilities.h" +#include "sound/sound.h" +#include "sound/oplplug.h" +#include "sound.h" +#include "dbadlib.h" +#include +#include +#include +#include "adlib.h" + +#define ADLIB_BASE 0x388 +#define OPL3_INTERNAL_FREQ 14400000 // The OPL3 operates at 14.4MHz +#define OPL3_MAX_BUF 512 +#define OPL3_MIN_BUF 128 +#define ADLIB_CHANNELS SNDBUF_CHANS + +#define ADLIB_THRESHOLD 20000000 + +static struct opl_ops *oplops; +static void *opl3_impl; +#define OPL3_SAMPLE_BITS 16 +#if (OPL3_SAMPLE_BITS==16) +typedef Bit16s OPL3SAMPLE; +#elif (OPL3_SAMPLE_BITS==8) +typedef Bit8s OPL3SAMPLE; +#endif +static int adlib_strm; +static int adlib_running; +static double adlib_time_last; +#if OPL3_SAMPLE_BITS==16 +static const int opl3_format = PCM_FORMAT_S16_LE; +#else +static const int opl3_format = PCM_FORMAT_S8; +#endif +static const int opl3_rate = 44100; + +static pthread_mutex_t run_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_t syn_thr; +static sem_t syn_sem; +static void *synth_thread(void *arg); + +Bit8u adlib_io_read_base(ioport_t port) +{ + Bit8u ret; + ret = oplops->PortRead(opl3_impl, port); + if (debug_level('S') >= 9) + S_printf("Adlib: Read %hhx from port %x\n", ret, port); + return ret; +} + +static Bit8u adlib_io_read(ioport_t port, void *arg) +{ + return adlib_io_read_base(port - ADLIB_BASE); +} + +static void opl3_update(void); + +void adlib_io_write_base(ioport_t port, Bit8u value) +{ + adlib_time_last = GETusTIME(0); + if (debug_level('S') >= 9) + S_printf("Adlib: Write %hhx to port %x\n", value, port); + if (port & 1) + opl3_update(); + oplops->PortWrite(opl3_impl, port, value); +} + +static void adlib_io_write(ioport_t port, Bit8u value, void *arg) +{ + adlib_io_write_base(port - ADLIB_BASE, value); +} + +static void adlib_start(void) +{ + pcm_prepare_stream(adlib_strm); + pthread_mutex_lock(&run_mtx); + adlib_running = 1; + pthread_mutex_unlock(&run_mtx); +} + +static void opl3_update(void) +{ + int a_run; + pthread_mutex_lock(&run_mtx); + a_run = adlib_running; + pthread_mutex_unlock(&run_mtx); + if (!a_run) + adlib_start(); +} + +void opl3_init(void) +{ + emu_iodev_t io_device; + + S_printf("SB: OPL3 Initialization\n"); + + /* This is the FM (Adlib + Advanced Adlib) */ + io_device.read_portb = adlib_io_read; + io_device.write_portb = adlib_io_write; + io_device.read_portw = NULL; + io_device.write_portw = NULL; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + io_device.handler_name = "OPL3"; + io_device.start_addr = ADLIB_BASE; + io_device.end_addr = ADLIB_BASE + 3; + if (port_register_handler(io_device, 0) != 0) { + error("ADLIB: Cannot registering port handler\n"); + } + + if (!oplops) + oplops = &dbadlib_ops; + opl3_impl = oplops->Create(opl3_rate); + + if (oplops->Generate) { + sem_init(&syn_sem, 0, 0); + pthread_create(&syn_thr, NULL, synth_thread, NULL); +#if defined(HAVE_PTHREAD_SETNAME_NP) && defined(__GLIBC__) + pthread_setname_np(syn_thr, "dosemu: adlib"); +#endif + adlib_strm = pcm_allocate_stream(ADLIB_CHANNELS, "Adlib", (void*)MC_MIDI); + } +} + +void adlib_reset(void) +{ + adlib_time_last = 0; +} + +void adlib_done(void) +{ + if (!oplops->Generate) + return; + pthread_cancel(syn_thr); + pthread_join(syn_thr, NULL); + sem_destroy(&syn_sem); +} + +static void adlib_process_samples(int nframes, double cur, double per) +{ + sndbuf_t buf[OPL3_MAX_BUF][SNDBUF_CHANS]; + oplops->Generate(nframes, buf, cur, per); + pcm_write_interleaved(buf, nframes, opl3_rate, opl3_format, + ADLIB_CHANNELS, adlib_strm); +} + +static void adlib_run(void) +{ + int nframes; + double period, adlib_time_cur; + long long now; + + adlib_time_cur = pcm_get_stream_time(adlib_strm); + if (adlib_time_cur - adlib_time_last > ADLIB_THRESHOLD) { + pcm_flush(adlib_strm); + pthread_mutex_lock(&run_mtx); + adlib_running = 0; + pthread_mutex_unlock(&run_mtx); + return; + } + if (adlib_running) { + now = GETusTIME(0); + period = pcm_frame_period_us(opl3_rate); + nframes = (now - adlib_time_cur) / period; + if (nframes > OPL3_MAX_BUF) + nframes = OPL3_MAX_BUF; + if (nframes >= OPL3_MIN_BUF) { + adlib_process_samples(nframes, adlib_time_cur, period); + adlib_time_cur = pcm_get_stream_time(adlib_strm); + if (debug_level('S') >= 7) + S_printf("SB: processed %i Adlib samples\n", nframes); + } + } +} + +static void *synth_thread(void *arg) +{ + int a_run; + while (1) { + sem_wait(&syn_sem); + pthread_mutex_lock(&run_mtx); + a_run = adlib_running; + pthread_mutex_unlock(&run_mtx); + if (!a_run) + continue; + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + adlib_run(); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + } + return NULL; +} + +void adlib_timer(void) +{ + if (!oplops->Generate) + return; + sem_post(&syn_sem); +} + +void opl_register_ops(struct opl_ops *ops) +{ + oplops = ops; +} diff --git a/src/base/dev/sb16/adlib.h b/src/base/dev/sb16/adlib.h new file mode 100644 index 0000000..55606aa --- /dev/null +++ b/src/base/dev/sb16/adlib.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2006 Stas Sergeev + * + * The below copyright strings have to be distributed unchanged together + * with this file. This prefix can not be modified or separated. + */ + +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef __ADLIB_H__ +#define __ADLIB_H__ + +void opl3_init(void); +void adlib_done(void); +void adlib_reset(void); +void adlib_timer(void); +Bit8u adlib_io_read_base(ioport_t port); +void adlib_io_write_base(ioport_t port, Bit8u value); + +#endif diff --git a/src/base/dev/sb16/dbadlib.c b/src/base/dev/sb16/dbadlib.c new file mode 100644 index 0000000..48e9668 --- /dev/null +++ b/src/base/dev/sb16/dbadlib.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2002-2011 The DOSBox Team + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* This file contains mostly converted and stripped down code from + DOSBOX src/hardware/adlib.cpp, hence DOSBOX copyright applies. + It contains code that glues dbopl.c to DOSEMU adlib.c. + The only code really added for DOSEMU is the mono->stereo conversion. +*/ + +#include +#include +#include +#include +#include "emu.h" +#include "timers.h" +#include "opl.h" +#include "sound/oplplug.h" +#include "sequencr.h" +#include "dbadlib.h" + +typedef struct _AdlibTimer AdlibTimer; + +struct _AdlibTimer { + long long start; + long long delay; + bool enabled, overflow, masked; + Bit8u counter; +}; + +static void AdlibTimer__AdlibTimer(AdlibTimer *self) { + self->masked = false; + self->overflow = false; + self->enabled = false; + self->counter = 0; + self->delay = 0; +} + +//Call update before making any further changes +static void AdlibTimer__Update(AdlibTimer *self, long long time) { + long long deltaStart; + if ( !self->enabled ) + return; + deltaStart = time - self->start; + //Only set the overflow flag when not masked + if ( deltaStart >= 0 && !self->masked ) { + self->overflow = 1; + } +} + +//On a reset make sure the start is in sync with the next cycle +static void AdlibTimer__Reset(AdlibTimer *self, const long long time) { + long long delta, rem; + self->overflow = false; + if ( !self->enabled ) + return; + delta = time - self->start; + if ( self->delay ) + rem = self->delay - delta % self->delay; + else + rem = 0; + self->start = time + rem; +} + +static void AdlibTimer__Stop(AdlibTimer *self) { + self->enabled = false; +} + +static void AdlibTimer__Start(AdlibTimer *self, const long long time, Bits scale) { + //Don't enable again + if ( self->enabled ) { + return; + } + self->enabled = true; + self->delay = (255 - self->counter ) * scale; + self->start = time + self->delay; +} + +static AdlibTimer opl3_timers[2]; + +static uint8_t dbadlib_PortRead(void *impl, uint16_t port); +static void dbadlib_PortWrite(void *impl, uint16_t port, uint8_t val ); +static void *dbadlib_create(int opl3_rate); +static void dbadlib_generate(int total, int16_t output[][2], double start, + double period); + +//Check for it being a write to the timer +static bool AdlibChip__WriteTimer(AdlibTimer *timer, Bit32u reg, Bit8u val) { + switch ( reg ) { + case 0x02: + timer[0].counter = val; + return true; + case 0x03: + timer[1].counter = val; + return true; + case 0x04: { + long long time = GETusTIME(0); + if ( val & 0x80 ) { + AdlibTimer__Reset(&timer[0], time); + AdlibTimer__Reset(&timer[1], time); + } else { + AdlibTimer__Update(&timer[0], time); + AdlibTimer__Update(&timer[1], time); + if ( val & 0x1 ) { + AdlibTimer__Start(&timer[0], time, 80); + S_printf("Adlib: timer 0 set to %lluus\n", + timer[0].delay); + } else { + AdlibTimer__Stop(&timer[0]); + } + timer[0].masked = (val & 0x40) > 0; + if ( timer[0].masked ) + timer[0].overflow = false; + if ( val & 0x2 ) { + AdlibTimer__Start(&timer[1], time, 320); + S_printf("Adlib: timer 1 set to %lluus\n", + timer[1].delay); + } else { + AdlibTimer__Stop(&timer[1]); + } + timer[1].masked = (val & 0x20) > 0; + if ( timer[1].masked ) + timer[1].overflow = false; + } + return true; } + } + return false; +} + + +//Read the current timer state, will use current double +static Bit8u AdlibChip__Read(AdlibTimer *timer) { + long long time = GETusTIME(0); + AdlibTimer__Update(&timer[0], time); + AdlibTimer__Update(&timer[1], time); + Bit8u ret = 0; + //Overflow won't be set if a channel is masked + if ( timer[0].overflow ) { + ret |= 0x40; + ret |= 0x80; + } + if ( timer[1].overflow ) { + ret |= 0x20; + ret |= 0x80; + } + return ret; + +} + +static void AdlibChip__AdlibChip(AdlibTimer *timer) { + AdlibTimer__AdlibTimer(&timer[0]); + AdlibTimer__AdlibTimer(&timer[1]); +} + +// stripped down from DOSBOX adlib.h: class Adlib::Module +static struct { + //Last selected address in the chip for the different modes + Bit32u normal; +} reg; + +static void *seq; +enum { STAG_PORT, STAG_VAL }; + +// stripped down from DOSBOX adlib.cpp: Adlib::Module::PortWrite +static void dbadlib_PortWrite(void *impl, uint16_t port, uint8_t val ) { + AdlibTimer *timer = impl; + int add_ev = 0; + if ( port&1 ) { + if ( !AdlibChip__WriteTimer( timer, reg.normal, val ) ) { + add_ev++; + } + } else { + add_ev++; + reg.normal = val & 0x1ff; + } + if (add_ev) { + struct seq_item_s *i = sequencer_add(seq, GETusTIME(0)); + sequencer_add_tag(i, STAG_PORT, port); + sequencer_add_tag(i, STAG_VAL, val); + } +} + + +// stripped down from DOSBOX adlib.cpp: Adlib::Module::PortRead +static uint8_t dbadlib_PortRead(void *impl, uint16_t port) { + AdlibTimer *timer = impl; + //We allocated 4 ports, so just return -1 for the higher ones + if ( !(port & 3 ) ) { + return AdlibChip__Read(timer); + } else { + return 0xff; + } +} + +// converted from DOSBOX dbopl.cpp: DBOPL::Handler::Init +static void *dbadlib_create(int opl3_rate) +{ + AdlibChip__AdlibChip(opl3_timers); + opl_init(opl3_rate); + seq = sequencer_init(); + return opl3_timers; +} + +static int opl_idx; + +static void extract_event(void) +{ + struct seq_item_s *i = sequencer_get(seq); + int port, val; + assert(i); + port = sequencer_find(i, STAG_PORT); + val = sequencer_find(i, STAG_VAL); + sequencer_free(i); + if (port & 1) { + opl_write(opl_idx, val); + } else { + opl_write_index(port, val); + opl_idx = val & 0x1ff; + } +} + +static void dbadlib_generate(int total, int16_t output[][2], double start, + double period) +{ + int done = 0; + double end = start + total * period; + long long next = sequencer_get_next(seq); + while (next && start > next) { + extract_event(); + next = sequencer_get_next(seq); + } + while (total > done) { + int todo = total - done; + if (next > end) + next = 0; + if (next) + todo = (next - start) / period; + opl_getsample((Bit16s *)(output + done), todo); + start += todo * period; + done += todo; + if (next) { + long long next1 = next; + do { + extract_event(); + next1 = sequencer_get_next(seq); + } while (next1 == next); // timestamps may duplicate + next = next1; + } + } +} + +struct opl_ops dbadlib_ops = { + .PortRead = dbadlib_PortRead, + .PortWrite = dbadlib_PortWrite, + .Create = dbadlib_create, + .Generate = dbadlib_generate, +}; diff --git a/src/base/dev/sb16/dbadlib.h b/src/base/dev/sb16/dbadlib.h new file mode 100644 index 0000000..6991cea --- /dev/null +++ b/src/base/dev/sb16/dbadlib.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2002-2011 The DOSBox Team + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef DOSBOX_ADLIB_H +#define DOSBOX_ADLIB_H + +#include +#include + +extern struct opl_ops dbadlib_ops; + +#endif diff --git a/src/base/dev/sb16/dspio.c b/src/base/dev/sb16/dspio.c new file mode 100644 index 0000000..5c62fec --- /dev/null +++ b/src/base/dev/sb16/dspio.c @@ -0,0 +1,924 @@ +/* + * Copyright (C) 2006 Stas Sergeev + * + * The below copyright strings have to be distributed unchanged together + * with this file. This prefix can not be modified or separated. + */ + +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: + * DSP I/O layer, DMA and DAC handling. Also MIDI sometimes. + * Currently used by SB16, but may be used with anything, e.g. GUS. + * + * Author: Stas Sergeev. + * + */ + +#include "emu.h" +#include "timers.h" +#include "sig.h" +#include "sound/sound.h" +#include "sound/midi.h" +#include "sound.h" +#include "adlib.h" +#include "dma.h" +#include "sb16.h" +#include "dspio.h" +#include +#include +#include +#include + +#define DAC_BASE_FREQ 8000 +#define PCM_MAX_BUF 512 + +struct dspio_dma { + unsigned int running:1; + int num; + int broken_hdma; + int rate; + int is16bit; + int stereo; + int samp_signed; + int adpcm; + int adpcm_need_ref; + uint8_t adpcm_ref; + int adpcm_step; + int input; + int silence; + int dsp_fifo_enabled; + hitimer_t time_cur; +}; + +struct dspio_state { + double input_time_cur, midi_time_cur; + int dma_strm, dac_strm; + unsigned int input_running:1, output_running:1, dac_running:1, speaker:1; + unsigned int pcm_input_running:1, lin_input_running:1, mic_input_running:1; + int i_handle, i_started; +#define DSP_FIFO_SIZE 64 + struct rng_s fifo_in; + struct rng_s fifo_out; +#define DSP_OUT_FIFO_TRIGGER 32 +#define DSP_IN_FIFO_TRIGGER 32 + struct dspio_dma dma; +}; + +static void dma_get_silence(int is_signed, int is16bit, void *ptr) +{ + if (is16bit) { + Bit16u *tmp16 = ptr; + *tmp16 = is_signed ? 0 : 0x8000; + } else { + Bit8u *tmp8 = ptr; + *tmp8 = is_signed ? 0 : 0x80; + } +} + +void dspio_toggle_speaker(struct dspio_state *dspio, int on) +{ + if (!on && dspio->speaker) { + if (dspio->dac_running) { + pcm_flush(dspio->dac_strm); + dspio->dac_running = 0; + } + /* we don't flush PCM stream here because DSP uses PCM layer + * for timing, and timing is needed even when speaker is disabled... */ + } + dspio->speaker = on; +} + +int dspio_get_speaker_state(struct dspio_state *dspio) +{ + return dspio->speaker; +} + +static void run_sound(void) +{ + if (!config.sound) + return; + dspio_run_synth(); + pcm_timer(); +} + +static int dspio_out_fifo_len(struct dspio_dma *dma) +{ + return dma->dsp_fifo_enabled ? DSP_OUT_FIFO_TRIGGER : 2; +} + +static int dspio_in_fifo_len(struct dspio_dma *dma) +{ + return dma->dsp_fifo_enabled ? DSP_IN_FIFO_TRIGGER : 2; +} + +static int dspio_output_fifo_filled(struct dspio_state *state) +{ + return rng_count(&state->fifo_out) >= dspio_out_fifo_len(&state->dma); +} + +static int dspio_input_fifo_filled(struct dspio_state *state) +{ + return rng_count(&state->fifo_in) >= dspio_in_fifo_len(&state->dma); +} + +static int dspio_input_fifo_empty(struct dspio_state *state) +{ + return !rng_count(&state->fifo_in); +} + +static int dspio_get_dma_data(struct dspio_state *state, void *ptr, int is16bit) +{ + static int warned; + if (sb_get_dma_data(ptr, is16bit)) + return 1; + if (rng_count(&state->fifo_in)) { + if (is16bit) { + rng_get(&state->fifo_in, ptr); + } else { + Bit16u tmp; + rng_get(&state->fifo_in, &tmp); + *(Bit8u *) ptr = tmp; + } + return 1; + } + if (!warned) { + error("SB: input fifo empty, adjust input and volume with SB mixer\n"); + warned++; + } + return 0; +} + +static void dspio_put_dma_data(struct dspio_state *state, void *ptr, int is16bit) +{ + if (dspio_output_fifo_filled(state)) { + error("SB: output fifo overflow\n"); + return; + } + if (is16bit) { + rng_put(&state->fifo_out, ptr); + } else { + Bit16u tmp = *(Bit8u *) ptr; + rng_put(&state->fifo_out, &tmp); + } +} + +/* https://wiki.multimedia.cx/index.php/Creative_8_bits_ADPCM */ +static uint8_t decode_adpcm2(struct dspio_dma *dma, uint8_t val) +{ + int sign = (val & 2) ? -1 : 1; + int value = val & 1; + int sample = dma->adpcm_ref + sign * (value << (dma->adpcm_step + 2)); + if (sample < 0) + sample = 0; + if (sample > 255) + sample = 255; + dma->adpcm_ref = sample; + if (value >= 1) + dma->adpcm_step++; + else if (value == 0) + dma->adpcm_step--; + if (dma->adpcm_step < 0) + dma->adpcm_step = 0; + if (dma->adpcm_step > 3) + dma->adpcm_step = 3; + return sample; +} + +static uint8_t decode_adpcm3(struct dspio_dma *dma, uint8_t val) +{ + int sign = (val & 4) ? -1 : 1; + int value = val & 3; + int sample = dma->adpcm_ref + sign * (value << dma->adpcm_step); + if (sample < 0) + sample = 0; + if (sample > 255) + sample = 255; + dma->adpcm_ref = sample; + if (value >= 3) + dma->adpcm_step++; + else if (value == 0) + dma->adpcm_step--; + if (dma->adpcm_step < 0) + dma->adpcm_step = 0; + if (dma->adpcm_step > 3) + dma->adpcm_step = 3; + return sample; +} + +static uint8_t decode_adpcm4(struct dspio_dma *dma, uint8_t val) +{ + int sign = (val & 8) ? -1 : 1; + int value = val & 7; + int sample = dma->adpcm_ref + sign * (value << dma->adpcm_step); + if (sample < 0) + sample = 0; + if (sample > 255) + sample = 255; + dma->adpcm_ref = sample; + if (value >= 5) + dma->adpcm_step++; + else if (value == 0) + dma->adpcm_step--; + if (dma->adpcm_step < 0) + dma->adpcm_step = 0; + if (dma->adpcm_step > 3) + dma->adpcm_step = 3; + return sample; +} + +static int dspio_get_output_sample(struct dspio_state *state, + sndbuf_t buf[PCM_MAX_BUF][SNDBUF_CHANS], int i, int j) +{ + int k; + uint16_t val; + int cnt = rng_count(&state->fifo_out); + if (!cnt) + return 0; + rng_get(&state->fifo_out, &val); + switch (state->dma.adpcm) { + case 0: + buf[i][j] = val; + return 1; + case 2: + assert(!j); + if (i + 4 > PCM_MAX_BUF) + return 0; + for (k = 0; k < 4; k++) + buf[i + k][j] = decode_adpcm2(&state->dma, + (val >> (6 - k * 2)) & 0x3); + return k; + case 3: + assert(!j); + if (i + 3 > PCM_MAX_BUF) + return 0; + /* 2.6bits, not 3, see dosbox */ + for (k = 0; k < 2; k++) + buf[i + k][j] = decode_adpcm3(&state->dma, + (val >> (5 - k * 3)) & 0x7); + buf[i + k][j] = decode_adpcm3(&state->dma, (val & 0x3) << 1); + return k + 1; + case 4: + assert(!j); + if (i + 2 > PCM_MAX_BUF) + return 0; + for (k = 0; k < 2; k++) + buf[i + k][j] = decode_adpcm4(&state->dma, + (val >> (4 - k * 4)) & 0xf); + return k; + } + error("should not be here, %i\n", state->dma.adpcm); + return 0; +} + +static int dspio_put_input_sample(struct dspio_state *state, void *ptr, + int is16bit) +{ + int ret; + if (!sb_input_enabled()) + return 0; + if (dspio_input_fifo_filled(state)) { + S_printf("SB: ERROR: input fifo overflow\n"); + return 0; + } + if (is16bit) { + ret = rng_put(&state->fifo_in, ptr); + } else { + Bit16u tmp = *(Bit8u *) ptr; + ret = rng_put(&state->fifo_in, &tmp); + } + return ret; +} + +void dspio_clear_fifos(struct dspio_state *dspio) +{ + rng_clear(&dspio->fifo_in); + rng_clear(&dspio->fifo_out); + dspio->dma.dsp_fifo_enabled = 1; +} + +static void dspio_i_start(void *arg) +{ + struct dspio_state *state = arg; + state->i_started = 1; +} + +static void dspio_i_stop(void *arg) +{ + struct dspio_state *state = arg; + state->i_started = 0; +} + +static const struct pcm_player player +#ifdef __cplusplus +{ + "SB REC", + NULL, NULL, NULL, NULL, NULL, + dspio_i_start, + dspio_i_stop, + PCM_F_PASSTHRU, + PCM_ID_R, + 0 +}; +#else += { + .name = "SB REC", + .start = dspio_i_start, + .stop = dspio_i_stop, + .flags = PCM_F_PASSTHRU, + .id = PCM_ID_R, +}; +#endif + +static double dspio_get_volume(int id, int chan_dst, int chan_src, void *arg); +static int dspio_is_connected(int id, void *arg); +static int dspio_checkid2(void *id2, void *arg); + +struct dspio_state *dspio_init(void) +{ + struct dspio_state *state; + state = malloc(sizeof(struct dspio_state)); + if (!state) + return NULL; + memset(&state->dma, 0, sizeof(struct dspio_dma)); + state->input_running = state->pcm_input_running = + state->lin_input_running = state->mic_input_running = + state->output_running = state->dac_running = state->speaker = + state->i_started = 0; + state->dma.dsp_fifo_enabled = 1; + + rng_init(&state->fifo_in, DSP_FIFO_SIZE, 2); + rng_init(&state->fifo_out, DSP_FIFO_SIZE, 2); + + state->i_handle = pcm_register_player(&player, state); + pcm_init(); + + pcm_set_volume_cb(dspio_get_volume); + pcm_set_connected_cb(dspio_is_connected); + pcm_set_checkid2_cb(dspio_checkid2); + state->dac_strm = pcm_allocate_stream(1, "SB DAC", (void*)MC_VOICE); + pcm_set_flag(state->dac_strm, PCM_FLAG_RAW); + state->dma_strm = pcm_allocate_stream(2, "SB DMA", (void*)MC_VOICE); + pcm_set_flag(state->dma_strm, PCM_FLAG_SLTS); + + midi_init(); + + sigalrm_register_handler(run_sound); + return state; +} + +void dspio_reset(struct dspio_state *dspio) +{ +} + +void dspio_done(struct dspio_state *dspio) +{ + midi_done(); + /* shutdown midi before pcm as midi may use pcm */ + pcm_done(); + + rng_destroy(&dspio->fifo_in); + rng_destroy(&dspio->fifo_out); + + free(dspio); +} + +void dspio_stop_midi(struct dspio_state *dspio) +{ + dspio->midi_time_cur = GETusTIME(0); + midi_stop(); +} + +Bit32u dspio_get_midi_in_time(struct dspio_state *dspio) +{ + Bit32u delta = GETusTIME(0) - dspio->midi_time_cur; + S_printf("SB: midi clock, delta=%i\n", delta); + return delta; +} + +static void dspio_start_output(struct dspio_state *state) +{ + if (state->output_running) + return; + S_printf("SB: starting output\n"); + pcm_prepare_stream(state->dma_strm); + state->output_running = 1; +} + +static void dspio_stop_output(struct dspio_state *state) +{ + if (!state->output_running) + return; + S_printf("SB: stopping output\n"); + pcm_flush(state->dma_strm); + state->output_running = 0; +} + +static void dspio_start_input(struct dspio_state *state) +{ + if (state->input_running) + return; + S_printf("SB: starting input\n"); + state->input_time_cur = GETusTIME(0); + state->input_running = 1; + if (!state->dma.rate) { + S_printf("SB: not starting recorder\n"); + return; + } + if (!state->pcm_input_running) { + pcm_reset_player(state->i_handle); + state->pcm_input_running = 1; + } +} + +static void dspio_stop_input(struct dspio_state *state) +{ + if (!state->input_running) + return; + S_printf("SB: stopping input\n"); + state->input_running = 0; + if (!state->dma.rate) { + S_printf("SB: not stopping recorder\n"); + return; + } + if (!sb_dma_active()) + state->pcm_input_running = 0; +} + +int dspio_input_enable(struct dspio_state *dspio, enum MixChan mc) +{ + struct dspio_state *state = dspio; + switch (mc) { + case MC_LINE: + if (state->lin_input_running) + return 0; + pcm_start_input((void *)MC_LINE); + state->lin_input_running = 1; + S_printf("SB: enabled LINE\n"); + break; + case MC_MIC: + if (state->mic_input_running) + return 0; + pcm_start_input((void *)MC_MIC); + state->mic_input_running = 1; + S_printf("SB: enabled MIC\n"); + break; + default: + return 0; + } + return 1; +} + +int dspio_input_disable(struct dspio_state *dspio, enum MixChan mc) +{ + struct dspio_state *state = dspio; + switch (mc) { + case MC_LINE: + if (!state->lin_input_running) + return 0; + pcm_stop_input((void *)MC_LINE); + state->lin_input_running = 0; + S_printf("SB: disabled LINE\n"); + break; + case MC_MIC: + if (!state->mic_input_running) + return 0; + pcm_stop_input((void *)MC_MIC); + state->mic_input_running = 0; + S_printf("SB: disabled MIC\n"); + break; + default: + return 0; + } + return 1; +} + +static int do_run_dma(struct dspio_state *state) +{ + Bit8u dma_buf[2]; + struct dspio_dma *dma = &state->dma; + + dma_get_silence(dma->samp_signed, dma->is16bit, dma_buf); + if (!dma->silence) { + if (dma->input) + dspio_get_dma_data(state, dma_buf, dma->is16bit); + if (dma_pulse_DRQ(dma->num, dma_buf) != DMA_DACK) { + S_printf("SB: DMA %i doesn't DACK!\n", dma->num); + return 0; + } + if (dma->broken_hdma) { + if (dma_pulse_DRQ(dma->num, dma_buf + 1) != DMA_DACK) { + S_printf("SB: DMA (broken) %i doesn't DACK!\n", dma->num); + return 0; + } + } + } + if (!dma->input) { + if (dma->adpcm && dma->adpcm_need_ref) { + dma->adpcm_ref = dma_buf[0]; + dma->adpcm_step = 0; + dma->adpcm_need_ref = 0; + } + dspio_put_dma_data(state, dma_buf, dma->is16bit); + } + return 1; +} + +static int dspio_run_dma(struct dspio_state *state) +{ +#define DMA_TIMEOUT_US 100000 + int ret; + struct dspio_dma *dma = &state->dma; + hitimer_t now = GETusTIME(0); + ret = do_run_dma(state); + if (ret) { + sb_handle_dma(); + dma->time_cur = now; + } else { + sb_dma_nack(); + if (now - dma->time_cur > DMA_TIMEOUT_US) { + S_printf("SB: Warning: DMA busy for too long, releasing\n"); +// error("SB: DMA timeout\n"); + sb_handle_dma_timeout(); + } + } + return ret; +} + +static void get_dma_params(struct dspio_dma *dma) +{ + int dma_16bit = sb_dma_16bit(); + int dma_num = dma_16bit ? sb_get_hdma_num() : sb_get_dma_num(); + int broken_hdma = (dma_16bit && dma_num == -1); + if (broken_hdma) { + dma_num = sb_get_dma_num(); + S_printf("SB: Warning: HDMA is broken, using 8-bit DMA channel %i\n", + dma_num); + } + + dma->num = dma_num; + dma->is16bit = dma_16bit; + dma->broken_hdma = broken_hdma; + dma->rate = sb_get_dma_sampling_rate(); + dma->stereo = sb_dma_samp_stereo(); + dma->samp_signed = sb_dma_samp_signed(); + dma->input = sb_dma_input(); + dma->silence = sb_dma_silence(); + dma->dsp_fifo_enabled = sb_fifo_enabled(); + dma->adpcm = sb_dma_adpcm(); + dma->adpcm_need_ref = sb_dma_adpcm_ref(); +} + +static int dspio_fill_output(struct dspio_state *state) +{ + int dma_cnt = 0; + while (state->dma.running && !dspio_output_fifo_filled(state)) { + if (!dspio_run_dma(state)) + break; + dma_cnt++; + } +#if 0 + if (!state->output_running && !sb_output_fifo_empty()) +#else + /* incomplete fifo needs a timeout, so lets not deal with it at all. + * Instead, deal with the filled fifo only. */ + if (dspio_output_fifo_filled(state)) +#endif + dspio_start_output(state); + return dma_cnt; +} + +static int dspio_drain_input(struct dspio_state *state) +{ + int dma_cnt = 0; + while (state->dma.running && !dspio_input_fifo_empty(state)) { + if (!dspio_run_dma(state)) + break; + dma_cnt++; + } + return dma_cnt; +} + +void dspio_start_dma(struct dspio_state *dspio) +{ + int dma_cnt = 0; + dspio->dma.running = 1; + dspio->dma.time_cur = GETusTIME(0); + get_dma_params(&dspio->dma); + + if (dspio->dma.input) { + dspio_start_input(dspio); + } else { + dma_cnt = dspio_fill_output(dspio); + if (dspio->dma.running && dspio_output_fifo_filled(dspio)) + S_printf("SB: Output filled, processed %i DMA cycles\n", + dma_cnt); + else + S_printf("SB: Output fillup incomplete (%i %i %i)\n", + dspio->dma.running, dspio->output_running, dma_cnt); + } +} + +void dspio_stop_dma(struct dspio_state *dspio) +{ + dspio_stop_input(dspio); + dspio->dma.running = 0; +} + +static int calc_nframes(struct dspio_state *state, + hitimer_t time_beg, hitimer_t time_dst) +{ + int nfr; + + if (time_dst < time_beg) + return 0; + if (state->dma.rate) { + nfr = (time_dst - time_beg) / pcm_frame_period_us(state->dma.rate) + 1; + if (nfr < 0) // happens because of get_stream_time() hack + nfr = 0; + if (nfr > PCM_MAX_BUF) + nfr = PCM_MAX_BUF; + } else { + nfr = 1; + } + return nfr; +} + +static void dspio_process_dma(struct dspio_state *state) +{ + int dma_cnt, nfr, in_fifo_cnt, out_fifo_cnt, i, j; + unsigned long long time_dst; + double output_time_cur = 0; + int n[SNDBUF_CHANS]; + sndbuf_t buf[PCM_MAX_BUF][SNDBUF_CHANS]; + static int warned; + + dma_cnt = in_fifo_cnt = out_fifo_cnt = 0; + + if (state->dma.running) { + state->dma.stereo = sb_dma_samp_stereo(); + state->dma.rate = sb_get_dma_sampling_rate(); + state->dma.samp_signed = sb_dma_samp_signed(); + state->dma.dsp_fifo_enabled = sb_fifo_enabled(); + dma_cnt += state->dma.input ? dspio_drain_input(state) : + dspio_fill_output(state); + } + + if (!state->output_running && !state->input_running) + return; + + time_dst = GETusTIME(0); + if (state->output_running) { + output_time_cur = pcm_get_stream_time(state->dma_strm); + nfr = calc_nframes(state, output_time_cur, time_dst); + } else { + nfr = 0; + } + if (nfr > PCM_MAX_BUF) + nfr = PCM_MAX_BUF; + for (i = 0; i < nfr;) { + memset(n, 0, sizeof(n)); + for (j = 0; j < state->dma.stereo + 1; j++) { + if (state->dma.running && !dspio_output_fifo_filled(state)) { + if (!dspio_run_dma(state)) + break; + dma_cnt++; + } + n[j] = dspio_get_output_sample(state, buf, i, j); + if (!n[j]) { + if (out_fifo_cnt && debug_level('S') >= 5) + S_printf("SB: no output samples\n"); + break; + } + if (j) + assert(n[j] == n[0]); +#if 0 + /* if speaker disabled, overwrite DMA data with silence */ + /* on SB16 is not used */ + if (!state->speaker) + dma_get_silence(state->dma.samp_signed, + state->dma.is16bit, &buf[i][j]); +#endif + } + if (j != state->dma.stereo + 1) + break; + i += n[0]; + } + out_fifo_cnt = i; + if (out_fifo_cnt && state->dma.rate) { + pcm_write_interleaved(buf, out_fifo_cnt, state->dma.rate, + pcm_get_format(state->dma.is16bit, + state->dma.samp_signed), + state->dma.stereo + 1, state->dma_strm); + output_time_cur = pcm_get_stream_time(state->dma_strm); + if (state->dma.running && output_time_cur > time_dst - 1) { + pcm_clear_flag(state->dma_strm, PCM_FLAG_POST); + warned = 0; + } + } + if (out_fifo_cnt < nfr) { + /* not enough samples, see why */ + if (!sb_dma_active()) { + dspio_stop_output(state); + } else { + if (nfr && (!warned || debug_level('S') >= 9)) { + S_printf("SB: Output FIFO exhausted while DMA is still active (ol=%f)\n", + time_dst - output_time_cur); + warned = 1; + } + if (state->dma.running) + S_printf("SB: Output FIFO exhausted while DMA is running (no DACK?)\n"); + /* DMA is active but currently not running and the FIFO is + * already exhausted. Normally we should flush the channel + * and stop the output timing. + * HACK: try to not flush the channel for as long as possible + * in a hope the PCM buffers are large enough to hold till + * the DMA is restarted. */ + pcm_set_flag(state->dma_strm, PCM_FLAG_POST); + /* awake dosemu */ + reset_idle(0); + } + } + + /* TODO: sync also input time with PCM? */ + if (state->input_running) + nfr = calc_nframes(state, state->input_time_cur, time_dst); + else + nfr = 0; + if (nfr && state->i_started && sb_input_enabled()) { + struct player_params params; + params.rate = state->dma.rate; + params.channels = state->dma.stereo + 1; + params.format = pcm_get_format(state->dma.is16bit, + state->dma.samp_signed); + params.handle = state->i_handle; + nfr = pcm_data_get_interleaved(buf, nfr, ¶ms); + } + if (!state->i_started) { + for (i = 0; i < nfr; i++) { + for (j = 0; j < state->dma.stereo + 1; j++) + dma_get_silence(state->dma.samp_signed, + state->dma.is16bit, &buf[i][j]); + } + } + for (i = 0; i < nfr; i++) { + for (j = 0; j < state->dma.stereo + 1; j++) { + if (sb_input_enabled()) { + if (!dspio_put_input_sample(state, &buf[i][j], + state->dma.is16bit)) + break; + } + } + if (j == state->dma.stereo + 1) + in_fifo_cnt++; + for (j = 0; j < state->dma.stereo + 1; j++) { + if (state->dma.running) { + if (!dspio_run_dma(state)) + break; + dma_cnt++; + } + } + if (!state->input_running || (j != state->dma.stereo + 1)) + break; + } + if (in_fifo_cnt) { + if (state->dma.rate) { + state->input_time_cur += in_fifo_cnt * + pcm_frame_period_us(state->dma.rate); + } else { + state->input_time_cur = time_dst; + } + } + + if (debug_level('S') >= 7 && (in_fifo_cnt || out_fifo_cnt || dma_cnt)) + S_printf("SB: Processed %i %i FIFO, %i DMA, or=%i dr=%i\n", + in_fifo_cnt, out_fifo_cnt, dma_cnt, state->output_running, state->dma.running); +} + +void dspio_run_synth(void) +{ + adlib_timer(); + midi_timer(); +} + +void dspio_timer(struct dspio_state *dspio) +{ + dspio_process_dma(dspio); +} + +void dspio_write_dac(struct dspio_state *dspio, Bit8u samp) +{ + sndbuf_t buf[1][SNDBUF_CHANS]; +#if 0 + /* on SB16 speaker control does not exist */ + if (!dspio->speaker) + return; +#endif + buf[0][0] = samp; + dspio->dac_running = 1; + pcm_write_interleaved(buf, 1, DAC_BASE_FREQ, PCM_FORMAT_U8, + 1, dspio->dac_strm); +} + +/* the volume APIs for sb16 and sndpcm are very different. + * We need a lot of glue below to get them to work together. + * I wonder if it is possible to design the APIs with fewer glue in between. */ +static double dspio_get_volume(int id, int chan_dst, int chan_src, void *arg) +{ + double vol; + enum MixSubChan msc; + enum MixRet mr = MR_UNSUP; + enum MixChan mc = (enum MixChan)(intptr_t)arg; + int chans = sb_mixer_get_chan_num(mc); + + if (chan_src >= chans) + return 0; + if (mc == MC_NONE) + return 1.0; + switch (chan_dst) { + case SB_CHAN_L: + switch (chan_src) { + case SB_CHAN_L: + msc = (chans == 1 ? MSC_MONO_L : MSC_L); + break; + case SB_CHAN_R: + msc = MSC_RL; + break; + default: + return 0; + } + break; + case SB_CHAN_R: + switch (chan_src) { + case SB_CHAN_L: + msc = (chans == 1 ? MSC_MONO_R : MSC_LR); + break; + case SB_CHAN_R: + msc = MSC_R; + break; + default: + return 0; + } + break; + default: + return 0; + } + + switch (id) { + case PCM_ID_P: + mr = sb_mixer_get_output_volume(mc, msc, &vol); + break; + case PCM_ID_R: + mr = sb_mixer_get_input_volume(mc, msc, &vol); + break; + } + + if (mr != MR_OK) + return 0; + return vol; +} + +/* FIXME: this is too slow! Needs caching to avoid re-calcs. */ +double dspio_calc_vol(int val, int step, int init_db) +{ +#define LOG_SCALE 0.02 + return pow(10, LOG_SCALE * (val * step + init_db)); +} + +static int dspio_is_connected(int id, void *arg) +{ + enum MixChan mc = (enum MixChan)(intptr_t)arg; + + if (mc == MC_NONE) // connect anonymous streams only to playback (P) + return (id == PCM_ID_P); + switch (id) { + case PCM_ID_P: + return sb_is_output_connected(mc); + case PCM_ID_R: + return sb_is_input_connected(mc); + } + return 0; +} + +static int dspio_checkid2(void *id2, void *arg) +{ + enum MixChan mc = (enum MixChan)(intptr_t)arg; + enum MixChan mc2 = (enum MixChan)(intptr_t)id2; + + return (mc2 == MC_NONE || mc2 == mc); +} diff --git a/src/base/dev/sb16/dspio.h b/src/base/dev/sb16/dspio.h new file mode 100644 index 0000000..edce8a9 --- /dev/null +++ b/src/base/dev/sb16/dspio.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2006 Stas Sergeev + * + * The below copyright strings have to be distributed unchanged together + * with this file. This prefix can not be modified or separated. + */ + +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef __DSPIO_H__ +#define __DSPIO_H__ + +struct dspio_state; + +extern struct dspio_state *dspio_init(void); +extern void dspio_reset(struct dspio_state *dspio); +extern void dspio_done(struct dspio_state *dspio); +extern void dspio_start_dma(struct dspio_state *dspio); +extern void dspio_stop_dma(struct dspio_state *dspio); +extern void dspio_stop_midi(struct dspio_state *dspio); +extern void dspio_toggle_speaker(struct dspio_state *dspio, int on); +extern int dspio_get_speaker_state(struct dspio_state *dspio); +extern Bit32u dspio_get_midi_in_time(struct dspio_state *dspio); +extern void dspio_write_dac(struct dspio_state *dspio, Bit8u samp); +extern void dspio_timer(struct dspio_state *dspio); +extern void dspio_run_synth(void); +extern void dspio_clear_fifos(struct dspio_state *dspio); +extern int dspio_input_enable(struct dspio_state *dspio, enum MixChan mc); +extern int dspio_input_disable(struct dspio_state *dspio, enum MixChan mc); +extern double dspio_calc_vol(int val, int step, int init_db); + +#endif diff --git a/src/base/dev/sb16/mpu401.c b/src/base/dev/sb16/mpu401.c new file mode 100644 index 0000000..90dd7b3 --- /dev/null +++ b/src/base/dev/sb16/mpu401.c @@ -0,0 +1,205 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: MPU401 emulation. + * + * Author: @stsp + * + */ +#include +#include +#include "ringbuf.h" +#include "port.h" +#include "dosemu_debug.h" +#include "sound/midi.h" +#include "mpu401.h" + +struct mpu401_s { +#define MIDI_FIFO_SIZE 32 + struct rng_s fifo_in; + unsigned int uart:1; + ioport_t base; + struct mpu401_ops *ops; +}; + +static void mpu401_stop_midi(struct mpu401_s *mpu) +{ + midi_stop(); +} + +static Bit8u get_midi_in_byte(struct mpu401_s *mpu) +{ + Bit8u val; + int ret = rng_get(&mpu->fifo_in, &val); + assert(ret == 1); + return val; +} + +static int get_midi_in_fillup(struct mpu401_s *mpu) +{ + return rng_count(&mpu->fifo_in); +} + +static void put_midi_in_byte(struct mpu401_s *mpu, Bit8u val) +{ + rng_put_const(&mpu->fifo_in, val); +#define MPU401_IN_FIFO_TRIGGER 1 + if (get_midi_in_fillup(mpu) == MPU401_IN_FIFO_TRIGGER) + mpu->ops->activate_irq(mpu); +} + +void mpu401_put_midi_in_byte(struct mpu401_s *mpu, Bit8u val) +{ + put_midi_in_byte(mpu, val); +} + +void mpu401_clear_midi_in_fifo(struct mpu401_s *mpu) +{ + rng_clear(&mpu->fifo_in); +} + +void mpu401_process(struct mpu401_s *mpu) +{ + Bit8u data; + while (midi_get_data_byte(&data)) + put_midi_in_byte(mpu, data); +} + +static Bit8u mpu401_io_read(ioport_t port, void *arg) +{ + struct mpu401_s *mpu = arg; + ioport_t addr; + Bit8u r = 0xff; + + addr = port - mpu->base; + + switch (addr) { + case 0: + /* Read data port */ + if (get_midi_in_fillup(mpu)) { + r = get_midi_in_byte(mpu); + } else { + S_printf("MPU401: ERROR: No data to read\n"); + r = 0xfe; // ACK + } + if (!mpu->uart && mpu->ops->read_hook) + mpu->ops->read_hook(mpu, r); + S_printf("MPU401: Read data port = 0x%02x, %i bytes still in queue\n", + r, get_midi_in_fillup(mpu)); + if (!get_midi_in_fillup(mpu)) + mpu->ops->deactivate_irq(mpu); + mpu->ops->run_irq(mpu); + break; + case 1: + /* Read status port */ + /* 0x40=OUTPUT_AVAIL; 0x80=INPUT_AVAIL */ + r = 0xff & (~0x40); /* Output is always possible */ + if (get_midi_in_fillup(mpu)) + r &= (~0x80); + S_printf("MPU401: Read status port = 0x%02x\n", r); + break; + } + return r; +} + +static void mpu401_io_write(ioport_t port, Bit8u value, void *arg) +{ + struct mpu401_s *mpu = arg; + uint32_t addr; + addr = port - mpu->base; + + switch (addr) { + case 0: + /* Write data port */ + if (debug_level('S') > 5) + S_printf("MPU401: Write 0x%02x to data port\n", value); + mpu->ops->write_midi(mpu, value); + break; + case 1: + /* Write command port */ + S_printf("MPU401: Write 0x%02x to command port\n", value); + mpu401_clear_midi_in_fifo(mpu); + /* the following doc: + * http://www.piclist.com/techref/io/serial/midi/mpu.html + * says 3f does not need ACK. But dosbox sources say that + * it does. Someone please try on a real HW? */ + put_midi_in_byte(mpu, 0xfe); /* A command is sent: MPU_ACK it next time */ + switch (value) { + case 0x3f: // 0x3F = UART mode + mpu->uart = 1; + break; + case 0xff: // 0xFF = reset MPU + mpu->uart = 0; + mpu401_stop_midi(mpu); + break; + default: + if (mpu->ops->cmd_hook) + mpu->ops->cmd_hook(mpu, value); + break; + } + break; + } +} + +struct mpu401_s *mpu401_init(ioport_t base, struct mpu401_ops *ops) +{ + emu_iodev_t io_device; + struct mpu401_s *mpu; + + S_printf("MPU401: MPU-401 Initialisation\n"); + + mpu = malloc(sizeof(*mpu)); + assert(mpu); + + /* This is the MPU-401 */ + io_device.read_portb = mpu401_io_read; + io_device.write_portb = mpu401_io_write; + io_device.read_portw = NULL; + io_device.write_portw = NULL; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + io_device.handler_name = ops->name; + io_device.start_addr = base; + io_device.end_addr = base + 0x001; + io_device.arg = mpu; + if (port_register_handler(io_device, 0) != 0) + error("MPU-401: Cannot registering port handler\n"); + + S_printf("MPU401: MPU-401 Initialisation - Base 0x%03x \n", base); + + rng_init(&mpu->fifo_in, MIDI_FIFO_SIZE, 1); + mpu->base = base; + mpu->ops = ops; + return mpu; +} + +void mpu401_reset(struct mpu401_s *mpu) +{ + mpu->ops->deactivate_irq(mpu); + mpu->uart = 0; +} + +void mpu401_done(struct mpu401_s *mpu) +{ + rng_destroy(&mpu->fifo_in); + free(mpu); +} + +int mpu401_is_uart(struct mpu401_s *mpu) +{ + return mpu->uart; +} diff --git a/src/base/dev/sb16/mpu401.h b/src/base/dev/sb16/mpu401.h new file mode 100644 index 0000000..8136f84 --- /dev/null +++ b/src/base/dev/sb16/mpu401.h @@ -0,0 +1,42 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef MPU401_H +#define MPU401_H + +#include + +struct mpu401_s; + +struct mpu401_ops { + void (*activate_irq)(struct mpu401_s *mpu); + void (*deactivate_irq)(struct mpu401_s *mpu); + void (*run_irq)(struct mpu401_s *mpu); + void (*write_midi)(struct mpu401_s *mpu, uint8_t data); + void (*cmd_hook)(struct mpu401_s *mpu, uint8_t cmd); + void (*read_hook)(struct mpu401_s *mpu, uint8_t data); + const char *name; +}; + +int mpu401_is_uart(struct mpu401_s *mpu); +void mpu401_process(struct mpu401_s *mpu); +void mpu401_put_midi_in_byte(struct mpu401_s *mpu, Bit8u val); +void mpu401_clear_midi_in_fifo(struct mpu401_s *mpu); + +struct mpu401_s *mpu401_init(ioport_t base, struct mpu401_ops *ops); +void mpu401_reset(struct mpu401_s *mpu); +void mpu401_done(struct mpu401_s *mpu); + +#endif diff --git a/src/base/dev/sb16/mt32.c b/src/base/dev/sb16/mt32.c new file mode 100644 index 0000000..77a7ecd --- /dev/null +++ b/src/base/dev/sb16/mt32.c @@ -0,0 +1,128 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: glue between dosemu2's mpu401 and softmpu. + * + * Author: @stsp + * + */ +#include "pic.h" +#include "dosemu_debug.h" +#include "sound/midi.h" +#include "iodev.h" +#include "emu.h" +#include "softmpu/export.h" +#include "mpu401.h" + +struct mt32state_s { + struct mpu401_s *mpu; + unsigned int irq_active:1; +}; + +static struct mt32state_s mt32; + +static void mpu_activate_irq(struct mpu401_s *mpu) +{ + if (mt32.irq_active) { + S_printf("MT32: Warning: Interrupt already active!\n"); + return; + } + S_printf("MT32: Activating irq %d\n", config.mpu401_irq_mt32); + mt32.irq_active = 1; + pic_request(config.mpu401_irq_mt32); +} + +static void mpu_deactivate_irq(struct mpu401_s *mpu) +{ + S_printf("MT32: Deactivating irq %d\n", config.mpu401_irq_mt32); + if (!mt32.irq_active) { + S_printf("MT32: Warning: Interrupt not active!\n"); + return; + } + mt32.irq_active = 0; + pic_untrigger(config.mpu401_irq_mt32); +} + +static void mpu_run_irq(struct mpu401_s *mpu) +{ + if (!mt32.irq_active) + return; + S_printf("MT32: Run irq %d\n", config.mpu401_irq_mt32); + pic_untrigger(config.mpu401_irq_mt32); + pic_request(config.mpu401_irq_mt32); +} + +static void mpu_write_midi(struct mpu401_s *mpu, uint8_t data) +{ + if (mpu401_is_uart(mpu)) + midi_write(data, ST_ANY); + else + MPU401_WriteData(data); +} + +static void mpu_cmd_hook(struct mpu401_s *mpu, uint8_t cmd) +{ + MPU401_WriteCommand(cmd); +} + +static void mpu_read_hook(struct mpu401_s *mpu, uint8_t data) +{ + MPU401_ReadData(data); +} + +static struct mpu401_ops mops = { + .activate_irq = mpu_activate_irq, + .deactivate_irq = mpu_deactivate_irq, + .run_irq = mpu_run_irq, + .write_midi = mpu_write_midi, + .cmd_hook = mpu_cmd_hook, + .read_hook = mpu_read_hook, + .name = "MT32 MPU401" +}; + +void mt32_init(void) +{ + if (config.mpu401_irq_mt32 == 2) { + error("irq2 for mt32 not supported, using irq 9\n"); + config.mpu401_irq_mt32 = 9; + } + mt32.irq_active = 0; + mt32.mpu = mpu401_init(config.mpu401_base_mt32, &mops); + MPU401_Init(); + S_printf("MT32: Initialisation completed\n"); +} + +void mt32_reset(void) +{ + mpu401_reset(mt32.mpu); // this also deactivates irq +} + +void mt32_done(void) +{ + mpu401_done(mt32.mpu); + MPU401_Done(); +} + +void QueueByte(Bit8u data) +{ + mpu401_put_midi_in_byte(mt32.mpu, data); +} + +void ClrQueue(void) +{ + mpu401_clear_midi_in_fifo(mt32.mpu); +} diff --git a/src/base/dev/sb16/opl.c b/src/base/dev/sb16/opl.c new file mode 100644 index 0000000..5ce6e1f --- /dev/null +++ b/src/base/dev/sb16/opl.c @@ -0,0 +1,1469 @@ +/* + * Copyright (C) 2002-2013 The DOSBox Team + * OPL2/OPL3 emulation library + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +/* + * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman + * Copyright (C) 1998-2001 Ken Silverman + * Ken Silverman's official web site: "http://www.advsys.net/ken" + */ + + +#include +#include // rand() +#include +#include +#include "types.h" +#include "opl_priv.h" +#include "opl.h" + + +static fltype recipsamp; // inverse of sampling rate +static Bit16s wavtable[WAVEPREC*3]; // wave form table + +// vibrato/tremolo tables +static Bit32s vib_table[VIBTAB_SIZE]; +static Bit32s trem_table[TREMTAB_SIZE*2]; + +static Bit32s vibval_const[BLOCKBUF_SIZE]; +static Bit32s tremval_const[BLOCKBUF_SIZE]; + +// vibrato value tables (used per-operator) +static Bit32s vibval_var1[BLOCKBUF_SIZE]; +static Bit32s vibval_var2[BLOCKBUF_SIZE]; +//static Bit32s vibval_var3[BLOCKBUF_SIZE]; +//static Bit32s vibval_var4[BLOCKBUF_SIZE]; + + + +// key scale level lookup table +static const fltype kslmul[4] = { + 0.0, 0.5, 0.25, 1.0 // -> 0, 3, 1.5, 6 dB/oct +}; + +// frequency multiplicator lookup table +static const fltype frqmul_tab[16] = { + 0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15 +}; +// calculated frequency multiplication values (depend on sampling rate) +static fltype frqmul[16]; + +// key scale levels +static Bit8u kslev[8][16]; + +// map a channel number to the register offset of the modulator (=register base) +static const Bit8u modulatorbase[9] = { + 0,1,2, + 8,9,10, + 16,17,18 +}; + +// map a register base to a modulator operator number or operator number +#if defined(OPLTYPE_IS_OPL3) +static const Bit8u regbase2modop[44] = { + 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8, // first set + 18,19,20,18,19,20,0,0,21,22,23,21,22,23,0,0,24,25,26,24,25,26 // second set +}; +static const Bit8u regbase2op[44] = { + 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17, // first set + 18,19,20,27,28,29,0,0,21,22,23,30,31,32,0,0,24,25,26,33,34,35 // second set +}; +#else +static const Bit8u regbase2modop[22] = { + 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8 +}; +static const Bit8u regbase2op[22] = { + 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17 +}; +#endif + + +// start of the waveform +static Bit32u waveform[8] = { + WAVEPREC, + WAVEPREC>>1, + WAVEPREC, + (WAVEPREC*3)>>2, + 0, + 0, + (WAVEPREC*5)>>2, + WAVEPREC<<1 +}; + +// length of the waveform as mask +static Bit32u wavemask[8] = { + WAVEPREC-1, + WAVEPREC-1, + (WAVEPREC>>1)-1, + (WAVEPREC>>1)-1, + WAVEPREC-1, + ((WAVEPREC*3)>>2)-1, + WAVEPREC>>1, + WAVEPREC-1 +}; + +// where the first entry resides +static Bit32u wavestart[8] = { + 0, + WAVEPREC>>1, + 0, + WAVEPREC>>2, + 0, + 0, + 0, + WAVEPREC>>3 +}; + +// envelope generator function constants +static fltype attackconst[4] = { + (fltype)(1/2.82624), + (fltype)(1/2.25280), + (fltype)(1/1.88416), + (fltype)(1/1.59744) +}; +static fltype decrelconst[4] = { + (fltype)(1/39.28064), + (fltype)(1/31.41608), + (fltype)(1/26.17344), + (fltype)(1/22.44608) +}; + + +static void operator_advance(op_type* op_pt, Bit32s vib) { + op_pt->wfpos = op_pt->tcount; // waveform position + + // advance waveform time + op_pt->tcount += op_pt->tinc; + op_pt->tcount += (int64_t)(op_pt->tinc)*vib/FIXEDPT; + + op_pt->generator_pos += generator_add; +} + +static void operator_advance_drums(op_type* op_pt1, Bit32s vib1, op_type* op_pt2, Bit32s vib2, op_type* op_pt3, Bit32s vib3) { + Bit32u c1 = op_pt1->tcount/FIXEDPT; + Bit32u c3 = op_pt3->tcount/FIXEDPT; + Bit32u phasebit = (((c1 & 0x88) ^ ((c1<<5) & 0x80)) | ((c3 ^ (c3<<2)) & 0x20)) ? 0x02 : 0x00; + + Bit32u noisebit = rand()&1; + + Bit32u snare_phase_bit = (((Bitu)((op_pt1->tcount/FIXEDPT) / 0x100))&1); + + //Hihat + Bit32u inttm = (phasebit<<8) | (0x34<<(phasebit ^ (noisebit<<1))); + op_pt1->wfpos = inttm*FIXEDPT; // waveform position + // advance waveform time + op_pt1->tcount += op_pt1->tinc; + op_pt1->tcount += (Bit32s)(op_pt1->tinc)*vib1/FIXEDPT; + op_pt1->generator_pos += generator_add; + + //Snare + inttm = ((1+snare_phase_bit) ^ noisebit)<<8; + op_pt2->wfpos = inttm*FIXEDPT; // waveform position + // advance waveform time + op_pt2->tcount += op_pt2->tinc; + op_pt2->tcount += (Bit32s)(op_pt2->tinc)*vib2/FIXEDPT; + op_pt2->generator_pos += generator_add; + + //Cymbal + inttm = (1+phasebit)<<8; + op_pt3->wfpos = inttm*FIXEDPT; // waveform position + // advance waveform time + op_pt3->tcount += op_pt3->tinc; + op_pt3->tcount += (Bit32s)(op_pt3->tinc)*vib3/FIXEDPT; + op_pt3->generator_pos += generator_add; +} + + +// output level is sustained, mode changes only when operator is turned off (->release) +// or when the keep-sustained bit is turned off (->sustain_nokeep) +static void operator_output(op_type* op_pt, Bit32s modulator, Bit32s trem) { + if (op_pt->op_state != OF_TYPE_OFF) { + op_pt->lastcval = op_pt->cval; + Bit32u i = (Bit32u)((op_pt->wfpos+modulator)/FIXEDPT); + + // wform: -16384 to 16383 (0x4000) + // trem : 32768 to 65535 (0x10000) + // step_amp: 0.0 to 1.0 + // vol : 1/2^14 to 1/2^29 (/0x4000; /1../0x8000) + + op_pt->cval = (Bit32s)(op_pt->step_amp*op_pt->vol*op_pt->cur_wform[i&op_pt->cur_wmask]*trem/16.0); + } +} + + +// no action, operator is off +static void operator_off(op_type* op_pt) { +} + +// output level is sustained, mode changes only when operator is turned off (->release) +// or when the keep-sustained bit is turned off (->sustain_nokeep) +static void operator_sustain(op_type* op_pt) { + Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + Bit32u ct; + for (ct=0; ctcur_env_step++; + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in release mode, if output level reaches zero the operator is turned off +static void operator_release(op_type* op_pt) { + // ??? boundary? + if (op_pt->amp > 0.00000001) { + // release phase + op_pt->amp *= op_pt->releasemul; + } + + Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + Bit32u ct; + for (ct=0; ctcur_env_step++; // sample counter + if ((op_pt->cur_env_step & op_pt->env_step_r)==0) { + if (op_pt->amp <= 0.00000001) { + // release phase finished, turn off this operator + op_pt->amp = 0.0; + if (op_pt->op_state == OF_TYPE_REL) { + op_pt->op_state = OF_TYPE_OFF; + } + } + op_pt->step_amp = op_pt->amp; + } + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in decay mode, if sustain level is reached the output level is either +// kept (sustain level keep enabled) or the operator is switched into release mode +static void operator_decay(op_type* op_pt) { + if (op_pt->amp > op_pt->sustain_level) { + // decay phase + op_pt->amp *= op_pt->decaymul; + } + + Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + Bit32u ct; + for (ct=0; ctcur_env_step++; + if ((op_pt->cur_env_step & op_pt->env_step_d)==0) { + if (op_pt->amp <= op_pt->sustain_level) { + // decay phase finished, sustain level reached + if (op_pt->sus_keep) { + // keep sustain level (until turned off) + op_pt->op_state = OF_TYPE_SUS; + op_pt->amp = op_pt->sustain_level; + } else { + // next: release phase + op_pt->op_state = OF_TYPE_SUS_NOKEEP; + } + } + op_pt->step_amp = op_pt->amp; + } + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in attack mode, if full output level is reached, +// the operator is switched into decay mode +static void operator_attack(op_type* op_pt) { + op_pt->amp = ((op_pt->a3*op_pt->amp + op_pt->a2)*op_pt->amp + op_pt->a1)*op_pt->amp + op_pt->a0; + + Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + Bit32u ct; + for (ct=0; ctcur_env_step++; // next sample + if ((op_pt->cur_env_step & op_pt->env_step_a)==0) { // check if next step already reached + if (op_pt->amp > 1.0) { + // attack phase finished, next: decay + op_pt->op_state = OF_TYPE_DEC; + op_pt->amp = 1.0; + op_pt->step_amp = 1.0; + } + op_pt->step_skip_pos_a <<= 1; + if (op_pt->step_skip_pos_a==0) op_pt->step_skip_pos_a = 1; + if (op_pt->step_skip_pos_a & op_pt->env_step_skip_a) { // check if required to skip next step + op_pt->step_amp = op_pt->amp; + } + } + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + + +typedef void (*optype_fptr)(op_type*); + +optype_fptr opfuncs[6] = { + operator_attack, + operator_decay, + operator_release, + operator_sustain, // sustain phase (keeping level) + operator_release, // sustain_nokeep phase (release-style) + operator_off +}; + +static void change_attackrate(Bitu regbase, op_type* op_pt) { + Bits attackrate = adlibreg[ARC_ATTR_DECR+regbase]>>4; + if (attackrate) { + fltype f = (fltype)(pow(FL2,(fltype)attackrate+(op_pt->toff>>2)-1)*attackconst[op_pt->toff&3]*recipsamp); + // attack rate coefficients + op_pt->a0 = (fltype)(0.0377*f); + op_pt->a1 = (fltype)(10.73*f+1); + op_pt->a2 = (fltype)(-17.57*f); + op_pt->a3 = (fltype)(7.42*f); + + Bits step_skip = attackrate*4 + op_pt->toff; + Bits steps = step_skip >> 2; + op_pt->env_step_a = (1<<(steps<=12?12-steps:0))-1; + + Bits step_num = (step_skip<=48)?(4-(step_skip&3)):0; + static Bit8u step_skip_mask[5] = {0xff, 0xfe, 0xee, 0xba, 0xaa}; + op_pt->env_step_skip_a = step_skip_mask[step_num]; + +#if defined(OPLTYPE_IS_OPL3) + if (step_skip>=60) { +#else + if (step_skip>=62) { +#endif + op_pt->a0 = (fltype)(2.0); // something that triggers an immediate transition to amp:=1.0 + op_pt->a1 = (fltype)(0.0); + op_pt->a2 = (fltype)(0.0); + op_pt->a3 = (fltype)(0.0); + } + } else { + // attack disabled + op_pt->a0 = 0.0; + op_pt->a1 = 1.0; + op_pt->a2 = 0.0; + op_pt->a3 = 0.0; + op_pt->env_step_a = 0; + op_pt->env_step_skip_a = 0; + } +} + +static void change_decayrate(Bitu regbase, op_type* op_pt) { + Bits decayrate = adlibreg[ARC_ATTR_DECR+regbase]&15; + // decaymul should be 1.0 when decayrate==0 + if (decayrate) { + fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*recipsamp); + op_pt->decaymul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(decayrate+(op_pt->toff>>2))))); + Bits steps = (decayrate*4 + op_pt->toff) >> 2; + op_pt->env_step_d = (1<<(steps<=12?12-steps:0))-1; + } else { + op_pt->decaymul = 1.0; + op_pt->env_step_d = 0; + } +} + +static void change_releaserate(Bitu regbase, op_type* op_pt) { + Bits releaserate = adlibreg[ARC_SUSL_RELR+regbase]&15; + // releasemul should be 1.0 when releaserate==0 + if (releaserate) { + fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*recipsamp); + op_pt->releasemul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(releaserate+(op_pt->toff>>2))))); + Bits steps = (releaserate*4 + op_pt->toff) >> 2; + op_pt->env_step_r = (1<<(steps<=12?12-steps:0))-1; + } else { + op_pt->releasemul = 1.0; + op_pt->env_step_r = 0; + } +} + +static void change_sustainlevel(Bitu regbase, op_type* op_pt) { + Bits sustainlevel = adlibreg[ARC_SUSL_RELR+regbase]>>4; + // sustainlevel should be 0.0 when sustainlevel==15 (max) + if (sustainlevel<15) { + op_pt->sustain_level = (fltype)(pow(FL2,(fltype)sustainlevel * (-FL05))); + } else { + op_pt->sustain_level = 0.0; + } +} + +static void change_waveform(Bitu regbase, op_type* op_pt) { +#if defined(OPLTYPE_IS_OPL3) + if (regbase>=ARC_SECONDSET) regbase -= (ARC_SECONDSET-22); // second set starts at 22 +#endif + // waveform selection + op_pt->cur_wmask = wavemask[wave_sel[regbase]]; + op_pt->cur_wform = &wavtable[waveform[wave_sel[regbase]]]; + // (might need to be adapted to waveform type here...) +} + +static void change_keepsustain(Bitu regbase, op_type* op_pt) { + op_pt->sus_keep = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x20)>0; + if (op_pt->op_state==OF_TYPE_SUS) { + if (!op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS_NOKEEP; + } else if (op_pt->op_state==OF_TYPE_SUS_NOKEEP) { + if (op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS; + } +} + +// enable/disable vibrato/tremolo LFO effects +static void change_vibrato(Bitu regbase, op_type* op_pt) { + op_pt->vibrato = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x40)!=0; + op_pt->tremolo = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x80)!=0; +} + +// change amount of self-feedback +static void change_feedback(Bitu chanbase, op_type* op_pt) { + Bits feedback = adlibreg[ARC_FEEDBACK+chanbase]&14; + if (feedback) op_pt->mfbi = (Bit32s)(pow(FL2,(fltype)((feedback>>1)+8))); + else op_pt->mfbi = 0; +} + +static void change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt) { + // frequency + Bit32u frn = ((((Bit32u)adlibreg[ARC_KON_BNUM+chanbase])&3)<<8) + (Bit32u)adlibreg[ARC_FREQ_NUM+chanbase]; + // block number/octave + Bit32u oct = ((((Bit32u)adlibreg[ARC_KON_BNUM+chanbase])>>2)&7); + op_pt->freq_high = (Bit32s)((frn>>7)&7); + + // keysplit + Bit32u note_sel = (adlibreg[8]>>6)&1; + op_pt->toff = ((frn>>9)&(note_sel^1)) | ((frn>>8)¬e_sel); + op_pt->toff += (oct<<1); + + // envelope scaling (KSR) + if (!(adlibreg[ARC_TVS_KSR_MUL+regbase]&0x10)) op_pt->toff >>= 2; + + // 20+a0+b0: + op_pt->tinc = (Bit32u)((((fltype)(frn<>6]*kslev[oct][frn>>6]); + op_pt->vol = (fltype)(pow(FL2,(fltype)(vol_in * -0.125 - 14))); + + // operator frequency changed, care about features that depend on it + change_attackrate(regbase,op_pt); + change_decayrate(regbase,op_pt); + change_releaserate(regbase,op_pt); +} + +static void enable_operator(Bitu regbase, op_type* op_pt, Bit32u act_type) { + // check if this is really an off-on transition + if (op_pt->act_state == OP_ACT_OFF) { + Bits wselbase = regbase; + if (wselbase>=ARC_SECONDSET) wselbase -= (ARC_SECONDSET-22); // second set starts at 22 + + op_pt->tcount = wavestart[wave_sel[wselbase]]*FIXEDPT; + + // start with attack mode + op_pt->op_state = OF_TYPE_ATT; + op_pt->act_state |= act_type; + } +} + +static void disable_operator(op_type* op_pt, Bit32u act_type) { + // check if this is really an on-off transition + if (op_pt->act_state != OP_ACT_OFF) { + op_pt->act_state &= (~act_type); + if (op_pt->act_state == OP_ACT_OFF) { + if (op_pt->op_state != OF_TYPE_OFF) op_pt->op_state = OF_TYPE_REL; + } + } +} + +void opl_init(Bit32u samplerate) { + Bits i, j, oct; + + int_samplerate = samplerate; + + generator_add = (Bit32u)(INTFREQU*FIXEDPT/int_samplerate); + + + memset((void *)adlibreg,0,sizeof(adlibreg)); + memset((void *)op,0,sizeof(op_type)*MAXOPERATORS); + memset((void *)wave_sel,0,sizeof(wave_sel)); + + for (i=0;i=0;i--) { + frqmul[i] = (fltype)(frqmul_tab[i]*INTFREQU/(fltype)WAVEPREC*(fltype)FIXEDPT*recipsamp); + } + + status = 0; + opl_index = 0; + + + // create vibrato table + vib_table[0] = 8; + vib_table[1] = 4; + vib_table[2] = 0; + vib_table[3] = -4; + for (i=4; i -0.5/6 to 0) + for (i=14; i<41; i++) trem_table_int[i] = -i+14; // downwards (26 to 0 -> 0 to -1/6) + for (i=41; i<53; i++) trem_table_int[i] = i-40-26; // upwards (1 to 12 -> -1/6 to -0.5/6) + + for (i=0; i>1);i++) { + wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1) )*PI*2/WAVEPREC)); + wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1)+1)*PI*2/WAVEPREC)); + wavtable[i] = wavtable[(i<<1) +WAVEPREC]; + // alternative: (zero-less) +/* wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+1)*PI/WAVEPREC)); + wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+3)*PI/WAVEPREC)); + wavtable[i] = wavtable[(i<<1)-1+WAVEPREC]; */ + } + for (i=0;i<(WAVEPREC>>3);i++) { + wavtable[i+(WAVEPREC<<1)] = wavtable[i+(WAVEPREC>>3)]-16384; + wavtable[i+((WAVEPREC*17)>>3)] = wavtable[i+(WAVEPREC>>2)]+16384; + } + + // key scale level table verified ([table in book]*8/3) + kslev[7][0] = 0; kslev[7][1] = 24; kslev[7][2] = 32; kslev[7][3] = 37; + kslev[7][4] = 40; kslev[7][5] = 43; kslev[7][6] = 45; kslev[7][7] = 47; + kslev[7][8] = 48; + for (i=9;i<16;i++) kslev[7][i] = (Bit8u)(i+41); + for (j=6;j>=0;j--) { + for (i=0;i<16;i++) { + oct = (Bits)kslev[j+1][i]-8; + if (oct < 0) oct = 0; + kslev[j][i] = (Bit8u)oct; + } + } + } + +} + + + +void opl_write(Bitu idx, Bit8u val) { + Bit32u second_set = idx&0x100; + adlibreg[idx] = val; + + switch (idx&0xf0) { + case ARC_CONTROL: + // here we check for the second set registers, too: + switch (idx) { + case 0x02: // timer1 counter + case 0x03: // timer2 counter + break; + case 0x04: + // IRQ reset, timer mask/start + if (val&0x80) { + // clear IRQ bits in status register + status &= ~0x60; + } else { + status = 0; + } + break; +#if defined(OPLTYPE_IS_OPL3) + case 0x04|ARC_SECONDSET: + // 4op enable/disable switches for each possible channel + op[0].is_4op = (val&1)>0; + op[3].is_4op_attached = op[0].is_4op; + op[1].is_4op = (val&2)>0; + op[4].is_4op_attached = op[1].is_4op; + op[2].is_4op = (val&4)>0; + op[5].is_4op_attached = op[2].is_4op; + op[18].is_4op = (val&8)>0; + op[21].is_4op_attached = op[18].is_4op; + op[19].is_4op = (val&16)>0; + op[22].is_4op_attached = op[19].is_4op; + op[20].is_4op = (val&32)>0; + op[23].is_4op_attached = op[20].is_4op; + break; + case 0x05|ARC_SECONDSET: + break; +#endif + case 0x08: + // CSW, note select + break; + default: + break; + } + break; + case ARC_TVS_KSR_MUL: + case ARC_TVS_KSR_MUL+0x10: { + // tremolo/vibrato/sustain keeping enabled; key scale rate; frequency multiplication + int num = idx&7; + Bitu base = (idx-ARC_TVS_KSR_MUL)&0xff; + if ((num<6) && (base<22)) { + Bitu modop = regbase2modop[second_set?(base+22):base]; + Bitu regbase = base+second_set; + Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; + + // change tremolo/vibrato and sustain keeping of this operator + op_type* op_ptr = &op[modop+((num<3) ? 0 : 9)]; + change_keepsustain(regbase,op_ptr); + change_vibrato(regbase,op_ptr); + + // change frequency calculations of this operator as + // key scale rate and frequency multiplicator can be changed +#if defined(OPLTYPE_IS_OPL3) + if ((adlibreg[0x105]&1) && (op[modop].is_4op_attached)) { + // operator uses frequency of channel + change_frequency(chanbase-3,regbase,op_ptr); + } else { + change_frequency(chanbase,regbase,op_ptr); + } +#else + change_frequency(chanbase,base,op_ptr); +#endif + } + } + break; + case ARC_KSL_OUTLEV: + case ARC_KSL_OUTLEV+0x10: { + // key scale level; output rate + int num = idx&7; + Bitu base = (idx-ARC_KSL_OUTLEV)&0xff; + if ((num<6) && (base<22)) { + Bitu modop = regbase2modop[second_set?(base+22):base]; + Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; + + // change frequency calculations of this operator as + // key scale level and output rate can be changed + op_type* op_ptr = &op[modop+((num<3) ? 0 : 9)]; +#if defined(OPLTYPE_IS_OPL3) + Bitu regbase = base+second_set; + if ((adlibreg[0x105]&1) && (op[modop].is_4op_attached)) { + // operator uses frequency of channel + change_frequency(chanbase-3,regbase,op_ptr); + } else { + change_frequency(chanbase,regbase,op_ptr); + } +#else + change_frequency(chanbase,base,op_ptr); +#endif + } + } + break; + case ARC_ATTR_DECR: + case ARC_ATTR_DECR+0x10: { + // attack/decay rates + int num = idx&7; + Bitu base = (idx-ARC_ATTR_DECR)&0xff; + if ((num<6) && (base<22)) { + Bitu regbase = base+second_set; + + // change attack rate and decay rate of this operator + op_type* op_ptr = &op[regbase2op[second_set?(base+22):base]]; + change_attackrate(regbase,op_ptr); + change_decayrate(regbase,op_ptr); + } + } + break; + case ARC_SUSL_RELR: + case ARC_SUSL_RELR+0x10: { + // sustain level; release rate + int num = idx&7; + Bitu base = (idx-ARC_SUSL_RELR)&0xff; + if ((num<6) && (base<22)) { + Bitu regbase = base+second_set; + + // change sustain level and release rate of this operator + op_type* op_ptr = &op[regbase2op[second_set?(base+22):base]]; + change_releaserate(regbase,op_ptr); + change_sustainlevel(regbase,op_ptr); + } + } + break; + case ARC_FREQ_NUM: { + // 0xa0-0xa8 low8 frequency + Bitu base = (idx-ARC_FREQ_NUM)&0xff; + if (base<9) { + Bits opbase = second_set?(base+18):base; +#if defined(OPLTYPE_IS_OPL3) + if ((adlibreg[0x105]&1) && op[opbase].is_4op_attached) break; +#endif + // regbase of modulator: + Bits modbase = modulatorbase[base]+second_set; + + Bitu chanbase = base+second_set; + + change_frequency(chanbase,modbase,&op[opbase]); + change_frequency(chanbase,modbase+3,&op[opbase+9]); +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are modified to the frequency of the channel + if ((adlibreg[0x105]&1) && op[second_set?(base+18):base].is_4op) { + change_frequency(chanbase,modbase+8,&op[opbase+3]); + change_frequency(chanbase,modbase+3+8,&op[opbase+3+9]); + } +#endif + } + } + break; + case ARC_KON_BNUM: { + if (idx == ARC_PERC_MODE) { +#if defined(OPLTYPE_IS_OPL3) + if (second_set) return; +#endif + + if ((val&0x30) == 0x30) { // BassDrum active + enable_operator(16,&op[6],OP_ACT_PERC); + change_frequency(6,16,&op[6]); + enable_operator(16+3,&op[6+9],OP_ACT_PERC); + change_frequency(6,16+3,&op[6+9]); + } else { + disable_operator(&op[6],OP_ACT_PERC); + disable_operator(&op[6+9],OP_ACT_PERC); + } + if ((val&0x28) == 0x28) { // Snare active + enable_operator(17+3,&op[16],OP_ACT_PERC); + change_frequency(7,17+3,&op[16]); + } else { + disable_operator(&op[16],OP_ACT_PERC); + } + if ((val&0x24) == 0x24) { // TomTom active + enable_operator(18,&op[8],OP_ACT_PERC); + change_frequency(8,18,&op[8]); + } else { + disable_operator(&op[8],OP_ACT_PERC); + } + if ((val&0x22) == 0x22) { // Cymbal active + enable_operator(18+3,&op[8+9],OP_ACT_PERC); + change_frequency(8,18+3,&op[8+9]); + } else { + disable_operator(&op[8+9],OP_ACT_PERC); + } + if ((val&0x21) == 0x21) { // Hihat active + enable_operator(17,&op[7],OP_ACT_PERC); + change_frequency(7,17,&op[7]); + } else { + disable_operator(&op[7],OP_ACT_PERC); + } + + break; + } + // regular 0xb0-0xb8 + Bitu base = (idx-ARC_KON_BNUM)&0xff; + if (base<9) { + Bits opbase = second_set?(base+18):base; +#if defined(OPLTYPE_IS_OPL3) + if ((adlibreg[0x105]&1) && op[opbase].is_4op_attached) break; +#endif + // regbase of modulator: + Bits modbase = modulatorbase[base]+second_set; + + if (val&32) { + // operator switched on + enable_operator(modbase,&op[opbase],OP_ACT_NORMAL); // modulator (if 2op) + enable_operator(modbase+3,&op[opbase+9],OP_ACT_NORMAL); // carrier (if 2op) +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are switched on + if ((adlibreg[0x105]&1) && op[opbase].is_4op) { + // turn on chan+3 operators as well + enable_operator(modbase+8,&op[opbase+3],OP_ACT_NORMAL); + enable_operator(modbase+3+8,&op[opbase+3+9],OP_ACT_NORMAL); + } +#endif + } else { + // operator switched off + disable_operator(&op[opbase],OP_ACT_NORMAL); + disable_operator(&op[opbase+9],OP_ACT_NORMAL); +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are switched off + if ((adlibreg[0x105]&1) && op[opbase].is_4op) { + // turn off chan+3 operators as well + disable_operator(&op[opbase+3],OP_ACT_NORMAL); + disable_operator(&op[opbase+3+9],OP_ACT_NORMAL); + } +#endif + } + + Bitu chanbase = base+second_set; + + // change frequency calculations of modulator and carrier (2op) as + // the frequency of the channel has changed + change_frequency(chanbase,modbase,&op[opbase]); + change_frequency(chanbase,modbase+3,&op[opbase+9]); +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are modified to the frequency of the channel + if ((adlibreg[0x105]&1) && op[second_set?(base+18):base].is_4op) { + // change frequency calculations of chan+3 operators as well + change_frequency(chanbase,modbase+8,&op[opbase+3]); + change_frequency(chanbase,modbase+3+8,&op[opbase+3+9]); + } +#endif + } + } + break; + case ARC_FEEDBACK: { + // 0xc0-0xc8 feedback/modulation type (AM/FM) + Bitu base = (idx-ARC_FEEDBACK)&0xff; + if (base<9) { + Bits opbase = second_set?(base+18):base; + Bitu chanbase = base+second_set; + change_feedback(chanbase,&op[opbase]); +#if defined(OPLTYPE_IS_OPL3) + // OPL3 panning + op[opbase].left_pan = ((val&0x10)>>4); + op[opbase].right_pan = ((val&0x20)>>5); +#endif + } + } + break; + case ARC_WAVE_SEL: + case ARC_WAVE_SEL+0x10: { + int num = idx&7; + Bitu base = (idx-ARC_WAVE_SEL)&0xff; + if ((num<6) && (base<22)) { +#if defined(OPLTYPE_IS_OPL3) + Bits wselbase = second_set?(base+22):base; // for easier mapping onto wave_sel[] + // change waveform + if (adlibreg[0x105]&1) wave_sel[wselbase] = val&7; // opl3 mode enabled, all waveforms accessible + else wave_sel[wselbase] = val&3; + op_type* op_ptr = &op[regbase2modop[wselbase]+((num<3) ? 0 : 9)]; + change_waveform(wselbase,op_ptr); +#else + if (adlibreg[0x01]&0x20) { + // wave selection enabled, change waveform + wave_sel[base] = val&3; + op_type* op_ptr = &op[regbase2modop[base]+((num<3) ? 0 : 9)]; + change_waveform(base,op_ptr); + } +#endif + } + } + break; + default: + break; + } +} + + +Bitu opl_reg_read(Bitu port) { +#if defined(OPLTYPE_IS_OPL3) + // opl3-detection routines require ret&6 to be zero + if ((port&1)==0) { + return status; + } + return 0x00; +#else + // opl2-detection routines require ret&6 to be 6 + if ((port&1)==0) { + return status|6; + } + return 0xff; +#endif +} + +void opl_write_index(Bitu port, Bit8u val) { + opl_index = val; +#if defined(OPLTYPE_IS_OPL3) + if ((port&3)!=0) { + // possibly second set + if (((adlibreg[0x105]&1)!=0) || (opl_index==5)) opl_index |= ARC_SECONDSET; + } +#endif +} + +static void clipit16(Bit32s ival, Bit16s* outval) { + if (ival<32768) { + if (ival>-32769) { + *outval=(Bit16s)ival; + } else { + *outval = -32768; + } + } else { + *outval = 32767; + } +} + + + +// be careful with this +// uses cptr and chanval, outputs into outbufl(/outbufr) +// for opl3 check if opl3-mode is enabled (which uses stereo panning) +#undef CHANVAL_OUT +#if defined(OPLTYPE_IS_OPL3) +#define CHANVAL_OUT \ + if (adlibreg[0x105]&1) { \ + outbufl[i] += chanval*cptr[0].left_pan; \ + outbufr[i] += chanval*cptr[0].right_pan; \ + } else { \ + outbufl[i] += chanval; \ + } +#else +#define CHANVAL_OUT \ + outbufl[i] += chanval; +#endif + +void opl_getsample(Bit16s* sndptr, Bits numsamples) { + Bits i, endsamples; + op_type* cptr; + + Bit32s outbufl[BLOCKBUF_SIZE]; +#if defined(OPLTYPE_IS_OPL3) + // second output buffer (right channel for opl3 stereo) + Bit32s outbufr[BLOCKBUF_SIZE]; +#endif + + // vibrato/tremolo lookup tables (global, to possibly be used by all operators) + Bit32s vib_lut[BLOCKBUF_SIZE]; + Bit32s trem_lut[BLOCKBUF_SIZE]; + // vibrato/trmolo value table pointers + Bit32s *vibval1, *vibval2, *vibval3, *vibval4; + Bit32s *tremval1, *tremval2, *tremval3, *tremval4; + + Bits samples_to_process = numsamples; + Bits cursmp; + for (cursmp=0; cursmpBLOCKBUF_SIZE) endsamples = BLOCKBUF_SIZE; + + memset((void*)&outbufl,0,endsamples*sizeof(Bit32s)); +#if defined(OPLTYPE_IS_OPL3) + // clear second output buffer (opl3 stereo) + if (adlibreg[0x105]&1) memset((void*)&outbufr,0,endsamples*sizeof(Bit32s)); +#endif + + // calculate vibrato/tremolo lookup tables + Bit32s vib_tshift = ((adlibreg[ARC_PERC_MODE]&0x40)==0) ? 1 : 0; // 14cents/7cents switching + for (i=0;i=VIBTAB_SIZE) vibtab_pos-=VIBTAB_SIZE*FIXEDPT_LFO; + vib_lut[i] = vib_table[vibtab_pos/FIXEDPT_LFO]>>vib_tshift; // 14cents (14/100 of a semitone) or 7cents + + // cycle through tremolo table + tremtab_pos += tremtab_add; + if (tremtab_pos/FIXEDPT_LFO>=TREMTAB_SIZE) tremtab_pos-=TREMTAB_SIZE*FIXEDPT_LFO; + if (adlibreg[ARC_PERC_MODE]&0x80) trem_lut[i] = trem_table[tremtab_pos/FIXEDPT_LFO]; + else trem_lut[i] = trem_table[TREMTAB_SIZE+tremtab_pos/FIXEDPT_LFO]; + } + + if (adlibreg[ARC_PERC_MODE]&0x20) { + //BassDrum + cptr = &op[6]; + if (adlibreg[ARC_FEEDBACK+6]&1) { + // additive synthesis + if (cptr[9].op_state != OF_TYPE_OFF) { + if (cptr[9].vibrato) { + vibval1 = vibval_var1; + for (i=0;i=0; cur_ch--) { + // skip drum/percussion operators + if ((adlibreg[ARC_PERC_MODE]&0x20) && (cur_ch >= 6) && (cur_ch < 9)) continue; + + Bitu k = cur_ch; +#if defined(OPLTYPE_IS_OPL3) + if (cur_ch < 9) { + cptr = &op[cur_ch]; + } else { + cptr = &op[cur_ch+9]; // second set is operator18-operator35 + k += (-9+256); // second set uses registers 0x100 onwards + } + // check if this operator is part of a 4-op + if ((adlibreg[0x105]&1) && cptr->is_4op_attached) continue; +#else + cptr = &op[cur_ch]; +#endif + + // check for FM/AM + if (adlibreg[ARC_FEEDBACK+k]&1) { +#if defined(OPLTYPE_IS_OPL3) + if ((adlibreg[0x105]&1) && cptr->is_4op) { + if (adlibreg[ARC_FEEDBACK+k+3]&1) { + // AM-AM-style synthesis (op1[fb] + (op2 * op3) + op4) + if (cptr[0].op_state != OF_TYPE_OFF) { + if (cptr[0].vibrato) { + vibval1 = vibval_var1; + for (i=0;iis_4op) { + if (adlibreg[ARC_FEEDBACK+k+3]&1) { + // FM-AM-style synthesis ((op1[fb] * op2) + (op3 * op4)) + if ((cptr[0].op_state != OF_TYPE_OFF) || (cptr[9].op_state != OF_TYPE_OFF)) { + if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) { + vibval1 = vibval_var1; + for (i=0;i +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; +*/ + + +/* + define attribution that inlines/forces inlining of a function (optional) +*/ +#define OPL_INLINE INLINE + + +#undef NUM_CHANNELS +#if defined(OPLTYPE_IS_OPL3) +#define NUM_CHANNELS 18 +#else +#define NUM_CHANNELS 9 +#endif + +#define MAXOPERATORS (NUM_CHANNELS*2) + + +#define FL05 ((fltype)0.5) +#define FL2 ((fltype)2.0) +#define PI ((fltype)3.1415926535897932384626433832795) + + +#define FIXEDPT 0x10000 // fixed-point calculations using 16+16 +#define FIXEDPT_LFO 0x1000000 // fixed-point calculations using 8+24 + +#define WAVEPREC 1024 // waveform precision (10 bits) + +#define INTFREQU ((fltype)(14318180.0 / 288.0)) // clocking of the chip + + +#define OF_TYPE_ATT 0 +#define OF_TYPE_DEC 1 +#define OF_TYPE_REL 2 +#define OF_TYPE_SUS 3 +#define OF_TYPE_SUS_NOKEEP 4 +#define OF_TYPE_OFF 5 + +#define ARC_CONTROL 0x00 +#define ARC_TVS_KSR_MUL 0x20 +#define ARC_KSL_OUTLEV 0x40 +#define ARC_ATTR_DECR 0x60 +#define ARC_SUSL_RELR 0x80 +#define ARC_FREQ_NUM 0xa0 +#define ARC_KON_BNUM 0xb0 +#define ARC_PERC_MODE 0xbd +#define ARC_FEEDBACK 0xc0 +#define ARC_WAVE_SEL 0xe0 + +#define ARC_SECONDSET 0x100 // second operator set for OPL3 + + +#define OP_ACT_OFF 0x00 +#define OP_ACT_NORMAL 0x01 // regular channel activated (bitmasked) +#define OP_ACT_PERC 0x02 // percussion channel activated (bitmasked) + +#define BLOCKBUF_SIZE 512 + + +// vibrato constants +#define VIBTAB_SIZE 8 +#define VIBFAC 70/50000 // no braces, integer mul/div + +// tremolo constants and table +#define TREMTAB_SIZE 53 +#define TREM_FREQ ((fltype)(3.7)) // tremolo at 3.7hz + + +/* operator struct definition + For OPL2 all 9 channels consist of two operators each, carrier and modulator. + Channel x has operators x as modulator and operators (9+x) as carrier. + For OPL3 all 18 channels consist either of two operators (2op mode) or four + operators (4op mode) which is determined through register4 of the second + adlib register set. + Only the channels 0,1,2 (first set) and 9,10,11 (second set) can act as + 4op channels. The two additional operators for a channel y come from the + 2op channel y+3 so the operatorss y, (9+y), y+3, (9+y)+3 make up a 4op + channel. +*/ +typedef struct operator_struct { + Bit32s cval, lastcval; // current output/last output (used for feedback) + Bit32u tcount, wfpos, tinc; // time (position in waveform) and time increment + fltype amp, step_amp; // and amplification (envelope) + fltype vol; // volume + fltype sustain_level; // sustain level + Bit32s mfbi; // feedback amount + fltype a0, a1, a2, a3; // attack rate function coefficients + fltype decaymul, releasemul; // decay/release rate functions + Bit32u op_state; // current state of operator (attack/decay/sustain/release/off) + Bit32u toff; + Bit32s freq_high; // highest three bits of the frequency, used for vibrato calculations + Bit16s* cur_wform; // start of selected waveform + Bit32u cur_wmask; // mask for selected waveform + Bit32u act_state; // activity state (regular, percussion) + bool sus_keep; // keep sustain level when decay finished + bool vibrato,tremolo; // vibrato/tremolo enable bits + + // variables used to provide non-continuous envelopes + Bit32u generator_pos; // for non-standard sample rates we need to determine how many samples have passed + Bits cur_env_step; // current (standardized) sample position + Bits env_step_a,env_step_d,env_step_r; // number of std samples of one step (for attack/decay/release mode) + Bit8u step_skip_pos_a; // position of 8-cyclic step skipping (always 2^x to check against mask) + Bits env_step_skip_a; // bitmask that determines if a step is skipped (respective bit is zero then) + +#if defined(OPLTYPE_IS_OPL3) + bool is_4op,is_4op_attached; // base of a 4op channel/part of a 4op channel + Bit32s left_pan,right_pan; // opl3 stereo panning amount +#endif +} op_type; + +// per-chip variables +Bitu chip_num; +op_type op[MAXOPERATORS]; + +Bits int_samplerate; + +Bit8u status; +Bit32u opl_index; +#if defined(OPLTYPE_IS_OPL3) +Bit8u adlibreg[512]; // adlib register set (including second set) +Bit8u wave_sel[44]; // waveform selection +#else +Bit8u adlibreg[256]; // adlib register set +Bit8u wave_sel[22]; // waveform selection +#endif + + +// vibrato/tremolo increment/counter +Bit32u vibtab_pos; +Bit32u vibtab_add; +Bit32u tremtab_pos; +Bit32u tremtab_add; + +static Bit32u generator_add; // should be a chip parameter diff --git a/src/base/dev/sb16/sb16.c b/src/base/dev/sb16/sb16.c new file mode 100644 index 0000000..2a023d5 --- /dev/null +++ b/src/base/dev/sb16/sb16.c @@ -0,0 +1,1934 @@ +/* + * Copyright (C) 2006 Stas Sergeev + * + * The below copyright strings have to be distributed unchanged together + * with this file. This prefix can not be modified or separated. + */ + +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: SB16 emulation. + * + * Author: Stas Sergeev. + * + * Some code is taken from an old sound.c by Joel N. Weber II, + * Alistair MacDonald, Michael Karcher and others - thanks. + * Thanks to Vlad Romascanu and VDMSound project for the E2 code and some info. + */ + +#include "emu.h" +#include "utilities.h" +#include "bitops.h" +#include "port.h" +#include "sound/sound.h" +#include "sound/midi.h" +#include "sound.h" +#include "dspio.h" +#include "adlib.h" +#include "mpu401.h" +#include "sb16.h" +#include + +static int sb_irq_tab[] = { 9 /* 2 actually */, 5, 7, 10 }; +static int sb_dma_tab[] = { 0, 1, 3 }; +static int sb_hdma_tab[] = { 5, 6, 7 }; + +/* + * Various Status values + */ + +#define SB_DATA_AVAIL 0x80 +#define SB_DATA_UNAVAIL 0x00 +/* not sure if/when this ever changes */ +#define SB_DSP_STATUS 0x7f + +#define SB_GET_STATUS() ((rng_count(&sb.dsp_queue) ? SB_DATA_AVAIL : \ + SB_DATA_UNAVAIL) | SB_DSP_STATUS) + +/* + * DSP information / states + */ +enum { DMA_RESTART_NONE, DMA_RESTART_PENDING, DMA_RESTART_AUTOINIT }; +struct sb_struct { + uint16_t rate; /* The current sample rate for input */ + uint8_t test; /* Storage for the test value */ + uint8_t reset_val; + unsigned int paused:1; /* is DMA transfer paused? */ + unsigned int reset:1; + uint8_t midi_cmd; + uint8_t dma_cmd; /* Information we need on the DMA transfer */ + uint8_t dma_mode; /* Information we need on the DMA transfer */ + unsigned int dma_exit_ai:1; /* exit DMA autoinit */ + struct { + int val; + unsigned int is_16:1; + unsigned int allow:1; + } dma_restart; /* DMA restart on IRQ ACK */ + uint8_t new_dma_cmd; /* Information we need on the DMA transfer */ + uint8_t new_dma_mode; /* Information we need on the DMA transfer */ + uint16_t dma_init_count; + uint16_t dma_count; + uint8_t mixer_regs[256]; + uint8_t mixer_index; + uint8_t E2Count; + uint8_t asp_regs[256]; + int asp_init; + uint8_t last_data; + int busy; + int irq_pend; + int irq_delay_cnt; +/* All values are imperical! */ +#define SB_DSP_CMD_BUF_SZ 8 + uint8_t command[SB_DSP_CMD_BUF_SZ]; + int command_idx; +#define DSP_QUEUE_SIZE 64 + struct rng_s dsp_queue; + struct dspio_state *dspio; + struct mpu401_s *mpu; +}; + +static struct sb_struct sb; + +static void process_sb_midi_input(Bit8u val); + +static int sb_get_dsp_irq_num(void) +{ + int idx = find_bit(sb.mixer_regs[0x80]); + if (idx == -1 || idx > 3) { + error("SB IRQ wrong (%#x)\n", sb.mixer_regs[0x80]); + return -1; + } + return sb_irq_tab[idx]; +} + +int sb_get_dma_num(void) +{ + int idx = find_bit(sb.mixer_regs[0x81] & 0x0f); + if (idx == -1 || idx == 2) { + error("SB DMA wrong (%#x)\n", sb.mixer_regs[0x81]); + return -1; + } + return idx; +} + +int sb_get_hdma_num(void) +{ + int idx = find_bit(sb.mixer_regs[0x81] & 0xf0); + if (idx == 4) { + error("SB HDMA wrong (%#x)\n", sb.mixer_regs[0x81]); + return -1; + } + return idx; +} + +/* DMA mode decoding functions */ +int sb_dma_active(void) +{ + return (sb.dma_cmd && !sb.paused); +} + +int sb_dma_16bit(void) +{ + if (!sb.dma_cmd) + error("SB: used inactive DMA (16bit)\n"); + switch (sb.dma_cmd) { + case 0xb0 ... 0xbf: + return 1; + } + return 0; +} + +int sb_dma_adpcm(void) +{ + if (!sb.dma_cmd) + error("SB: used inactive DMA (adpcm)\n"); + switch (sb.dma_cmd) { + case 0x74: + case 0x75: + case 0x7d: + return 4; + case 0x76: + case 0x77: + case 0x7f: + return 3; + case 0x16: + case 0x17: + case 0x1f: + return 2; + } + return 0; +} + +int sb_dma_adpcm_ref(void) +{ + if (!sb.dma_cmd) + error("SB: used inactive DMA (adpcm_ref)\n"); + switch (sb.dma_cmd) { + case 0x75: + case 0x77: + case 0x17: + case 0x7d: + case 0x7f: + case 0x1f: + return 1; + } + return 0; +} + +static int sb_dma_sb16mode(void) +{ + if (!sb.dma_cmd) + error("SB: used inactive DMA (sb16)\n"); + switch (sb.dma_cmd) { + case 0xb0 ... 0xcf: + return 1; + } + return 0; +} + +int sb_fifo_enabled(void) +{ + if (!sb.dma_cmd) + error("SB: used inactive DMA (fifo)\n"); + if (sb_dma_sb16mode()) + return (sb.dma_cmd & 2) ? 1 : 0; + /* is this correct? */ + return 1; +} + +int sb_dma_samp_signed(void) +{ + if (!sb.dma_cmd) + error("SB: used inactive DMA (signed)\n"); + if (sb_dma_sb16mode()) + return (sb.dma_mode & 16) ? 1 : 0; + return 0; +} + +int sb_dma_samp_stereo(void) +{ + if (!sb.dma_cmd) + error("SB: used inactive DMA (stereo)\n"); + if (sb_dma_sb16mode()) + return (sb.dma_mode & 32) ? 1 : 0; + return (sb.mixer_regs[0x0e] & 2) ? 1 : 0; +} + +int sb_dma_input(void) +{ + if (!sb.dma_cmd) + error("SB: used inactive DMA (input)\n"); + if (sb_dma_sb16mode()) + return (sb.dma_cmd & 8) ? 1 : 0; + switch (sb.dma_cmd) { + case 0x24: + case 0x2c: + case 0x98: + case 0x99: + case 0xe2: // weird case! + return 1; + } + return 0; +} + +int sb_dma_silence(void) +{ + if (!sb.dma_cmd) + error("SB: used inactive DMA (silence)\n"); + switch (sb.dma_cmd) { + case 0x80: + return 1; + } + return 0; +} + +static int sb_dma_internal(void) +{ + if (!sb.dma_cmd) + error("SB: used inactive DMA (internal)\n"); + switch (sb.dma_cmd) { + case 0xe2: + return 1; + } + return 0; +} + +int sb_input_enabled(void) +{ + return !sb_dma_internal(); +} + +static int sb_dma_autoinit(void) +{ + if (!sb.dma_cmd) + error("SB: used inactive DMA (ai)\n"); +#if 0 + /* if speaker state is not consistent with direction, don't autoinit - correct? */ + if (!(sb_dma_input() ^ sb.speaker)) + return 0; +#endif + if (sb.dma_exit_ai) + return 0; + if (sb_dma_sb16mode()) + return (sb.dma_cmd & 4) ? 1 : 0; + switch (sb.dma_cmd) { + case 0x1c: + case 0x1f: + case 0x2c: + case 0x7d: + case 0x7f: + case 0x90: + case 0x98: + return 1; + } + return 0; +} + +int sb_get_dma_sampling_rate(void) +{ + int sample_rate; + if (sb_dma_internal()) // that's because we don't zero sb.rate at reset... + return 0; // 0 is checked in dspio.c... hacks hacks :(( + sample_rate = sb.rate; + if (!sb_dma_sb16mode()) + sample_rate >>= sb_dma_samp_stereo(); + return sample_rate; +} + +static int sb_dma_high_speed(void) +{ + if (!sb.dma_cmd) + error("SB: used inactive DMA (hs)\n"); + switch (sb.dma_cmd) { + case 0x90: + case 0x91: + case 0x98: + case 0x99: + return 1; + } + return 0; +} + +/* some sb-midi decoding */ +static int sb_midi_input(void) +{ + switch (sb.midi_cmd) { + case 0x30 ... 0x37: + return 1; + } + return 0; +} + +static int sb_midi_uart(void) +{ + switch (sb.midi_cmd) { + case 0x34 ... 0x37: + return 1; + } + return 0; +} + +static int sb_midi_timestamp(void) +{ + switch (sb.midi_cmd) { + case 0x32: + case 0x33: + case 0x36: + case 0x37: + return 1; + } + return 0; +} + +static int sb_midi_int(void) +{ + switch (sb.midi_cmd) { + case 0x31: + case 0x33: + case 0x35: + case 0x37: + return 1; + } + return 0; +} + +static void start_dma_clock(void) +{ + /* this was likely needed for KryptEgg game, see commit 5741ac14 */ + sb.busy = 2; + + dspio_start_dma(sb.dspio); +} + +static void stop_dma_clock(void) +{ + dspio_stop_dma(sb.dspio); + + sb.busy = 1; +} + +static void stop_dma(void) +{ + /* first reset dma_cmd, as dspio may query it from dspio_stop_dma() */ + sb.dma_cmd = 0; + stop_dma_clock(); +} + +static void sb_dma_actualize(void) +{ + if (sb.new_dma_cmd) { + S_printf("SB: Actualized command %#x\n", sb.new_dma_cmd); + sb.dma_cmd = sb.new_dma_cmd; + sb.dma_mode = sb.new_dma_mode; + /* count is reloaded in sb_dma_start() */ + sb.new_dma_cmd = 0; + sb.new_dma_mode = 0; + sb.dma_exit_ai = 0; + } +} + +static void sb_dma_start(void) +{ + sb.dma_restart.val = DMA_RESTART_NONE; + sb.paused = 0; + sb_dma_actualize(); + if (sb_dma_active()) { + sb.dma_count = sb.dma_init_count; + S_printf("SB: DMA transfer started, count=%i\n", + sb.dma_init_count); + S_printf("SB: sample params: rate=%i, stereo=%i, signed=%i 16bit=%i\n", + sb_get_dma_sampling_rate(), sb_dma_samp_stereo(), + sb_dma_samp_signed(), sb_dma_16bit()); + start_dma_clock(); + } +} + +static int sb_irq_active(int type) +{ + return (sb.mixer_regs[0x82] & type); +} + +static void sb_request_irq(int type) +{ + if (type & SB_IRQ_DSP) + pic_request(sb_get_dsp_irq_num()); + if (type & SB_IRQ_MPU401) + pic_request(config.mpu401_irq); +} + +static void sb_activate_irq(int type) +{ + S_printf("SB: Activating irq type %d\n", type); + if (sb_irq_active(type)) { + S_printf("SB: Warning: Interrupt already active!\n"); + return; + } + sb_request_irq(type); + sb.mixer_regs[0x82] |= type; +} + +static void sb_deactivate_irq(int type) +{ + uint32_t act_map; + int mpu_irq = config.mpu401_irq; + + S_printf("SB: Deactivating irq type %d\n", type); + if (!(sb.mixer_regs[0x82] & type)) { + S_printf("SB: Warning: Interrupt not active!\n"); + return; + } + sb.mixer_regs[0x82] &= ~type; + /* if dsp and mpu irqs are the same, untrigger only when + * both are inactive */ + act_map = ((!!(sb.mixer_regs[0x82] & SB_IRQ_DSP)) << + sb_get_dsp_irq_num()) | + ((!!(sb.mixer_regs[0x82] & SB_IRQ_MPU401)) << mpu_irq); + if (type & SB_IRQ_DSP) { + if (!(act_map & (1 << sb_get_dsp_irq_num()))) + pic_untrigger(sb_get_dsp_irq_num()); + } + if (type & SB_IRQ_MPU401) { + if (!(act_map & (1 << mpu_irq))) + pic_untrigger(mpu_irq); + } +} + +static void sb_run_irq(int type) +{ + if (!sb_irq_active(type)) + return; + S_printf("SB: Run irq type %d\n", type); + if (type & SB_IRQ_DSP) + pic_untrigger(sb_get_dsp_irq_num()); + if (type & SB_IRQ_MPU401) + pic_untrigger(config.mpu401_irq); + sb_request_irq(type); +} + +static void sb_dma_activate(void) +{ + sb.new_dma_cmd = sb.command[0]; + sb.new_dma_mode = sb.command[1]; + if (sb_irq_active(SB_IRQ_DSP)) { + sb.dma_restart.val = DMA_RESTART_PENDING; + S_printf("SB: IRQ still active, DMA command %#x pending\n", + sb.new_dma_cmd); + } else + /* a weird logic to fix Speedy game: if DMA have never advanced + * (because channel is masked), forget current autoinit and + * actualize the new settings. To not introduce the race condition + * for the programs that rely on pending settings, we prolong + * the busy status till missing DMA DACK. */ + if (!sb_dma_active() || sb.dma_restart.allow) { + S_printf("SB: starting DMA transfer\n"); + sb_dma_start(); + } else { + S_printf("SB: DMA command %#x pending, current=%#x\n", + sb.new_dma_cmd, sb.dma_cmd); + } +} + +void sb_handle_dma(void) +{ + sb.dma_count--; + sb.dma_restart.allow = 0; + if (sb.dma_count == 0xffff) { + sb.dma_count = sb.dma_init_count; + sb.dma_restart.allow = 1; + /* testsb16 will lock up if IRQ is raised on E2 */ + if (!sb_dma_internal()) { + S_printf("SB: Done block, triggering IRQ\n"); + sb_activate_irq(sb_dma_16bit()? SB_IRQ_16BIT : SB_IRQ_8BIT); + } + if (!sb_dma_autoinit()) { + S_printf("SB: DMA transfer completed\n"); + if (!sb_dma_internal()) + sb.dma_restart.is_16 = sb_dma_16bit(); + stop_dma(); + } else if (sb_fifo_enabled()) { + /* remember autoinit state here coz it may change + * before the ACK code to check it */ + sb.dma_restart.val = DMA_RESTART_AUTOINIT; + sb.dma_restart.is_16 = sb_dma_16bit(); + /* auto-init & FIFO - stop till IRQ-ACK */ + stop_dma_clock(); + } else { + /* auto-init & !FIFO - apply new parameters and continue */ + S_printf("SB: FIFO not enabled, continuing transfer\n"); + sb_dma_actualize(); + } + } + if (sb.busy == 2) + sb.busy = 1; +} + +void sb_dma_nack(void) +{ + /* speedy reprograms DSP without exiting auto-init + * but when the DMA channel is masked. We need to downgrade + * the busy status from 2 to 1. */ + if (sb.busy == 2) + sb.busy = 1; +} + +void sb_handle_dma_timeout(void) +{ + stop_dma(); +} + +static void dsp_write_output(uint8_t value) +{ + rng_put(&sb.dsp_queue, &value); + if (debug_level('S') >= 2) { + S_printf("SB: Insert into output Queue [%u]... (0x%x)\n", + rng_count(&sb.dsp_queue), value); + } +} + +static void sb_dsp_reset(void) +{ + S_printf("SB: Resetting SB DSP\n"); + + stop_dma(); + dspio_toggle_speaker(sb.dspio, 0); + dspio_clear_fifos(sb.dspio); + rng_clear(&sb.dsp_queue); + sb_deactivate_irq(SB_IRQ_ALL); + sb.paused = 0; + sb.midi_cmd = 0; + sb.dma_mode = 0; + sb.new_dma_cmd = 0; + sb.new_dma_mode = 0; + sb.dma_exit_ai = 0; + sb.dma_restart.val = DMA_RESTART_NONE; + sb.dma_restart.is_16 = 0; + sb.dma_restart.allow = 0; + sb.dma_init_count = 0; + sb.dma_count = 0; + sb.command_idx = 0; + sb.E2Count = 0; + sb.last_data = 0; + sb.reset_val = 0xaa; + sb.rate = 32000; // NFS in sbpro mode needs this rate +/* the following must not be zeroed out */ +#if 0 + sb.mixer_index = 0; +#endif +} + +static void sb_dsp_soft_reset(unsigned char value) +{ + if (value & 1) { + if (!sb.reset) { + sb_deactivate_irq(SB_IRQ_ALL); + sb.reset = 1; + + if (sb_dma_active() && sb_dma_high_speed()) { + /* for High-Speed mode reset means only exit High-Speed */ + S_printf("SB: Reset called, exiting High-Speed DMA mode\n"); + stop_dma(); + } else if (sb_midi_uart()) { + S_printf("SB: Reset called, exiting UART midi mode\n"); + sb.midi_cmd = 0; + } else { + sb_dsp_reset(); + sb.busy = 2; + } + } + } else { + if (sb.reset) { + sb.reset = 0; + dsp_write_output(sb.reset_val); + sb.busy = 1; + } + } +} + +static void sb_mixer_reset(void) +{ + memset(sb.mixer_regs, 0, 0x48); + /* Restore values as per Creative specs */ + sb.mixer_regs[0x0a] = 0; /* -48 dB */ + sb.mixer_regs[0x04] = + sb.mixer_regs[0x22] = + sb.mixer_regs[0x26] = 0xcc; /* -12 dB */ + sb.mixer_regs[0x28] = + sb.mixer_regs[0x2e] = 0; /* -60 dB */ + + sb.mixer_regs[0x30] = + sb.mixer_regs[0x31] = + sb.mixer_regs[0x32] = + sb.mixer_regs[0x33] = + sb.mixer_regs[0x34] = + sb.mixer_regs[0x35] = 24 << 3; /* -14 dB */ + + sb.mixer_regs[0x36] = + sb.mixer_regs[0x37] = + sb.mixer_regs[0x38] = + sb.mixer_regs[0x39] = + sb.mixer_regs[0x3a] = 0; /* -62 dB */ + + sb.mixer_regs[0x3b] = 0; /* -18 dB */ + + sb.mixer_regs[0x3c] = 0x1f; /* line, cd, mic output - on */ + sb.mixer_regs[0x3d] = 0x15; /* line.L, cd.L, mic - on */ + sb.mixer_regs[0x3e] = 0x0b; /* line.R, cd.R, mic - on */ + + sb.mixer_regs[0x3f] = + sb.mixer_regs[0x40] = + sb.mixer_regs[0x41] = + sb.mixer_regs[0x42] = 0; /* 0 dB */ + + sb.mixer_regs[0x44] = + sb.mixer_regs[0x45] = + sb.mixer_regs[0x46] = + sb.mixer_regs[0x47] = 8 << 4; /* 0 dB */ +} + +static int num_to_idx(int num, int arr[], int len) +{ + int i; + for (i = 0; i < len; i++) { + if (arr[i] == num) + return 1 << i; + } + return 0; +} + +static void sb_mixer_init(void) +{ + int irq_idx, dma_idx, hdma_idx; + + S_printf("SB: Mixer init\n"); + + memset(sb.mixer_regs, 0, sizeof(sb.mixer_regs)); + + irq_idx = num_to_idx(config.sb_irq, sb_irq_tab, ARRAY_SIZE(sb_irq_tab)); + if (!irq_idx) { + error("Sound Blaster cannot work on IRQ %i\n", config.sb_irq); + config.exitearly = 1; + } + if (!num_to_idx(config.sb_dma, sb_dma_tab, ARRAY_SIZE(sb_dma_tab))) { + error("Sound Blaster cannot work on DMA %i\n", config.sb_dma); + config.exitearly = 1; + } + dma_idx = 1 << config.sb_dma; + if (config.sb_hdma + && !num_to_idx(config.sb_hdma, sb_hdma_tab, + ARRAY_SIZE(sb_hdma_tab))) { + error("Sound Blaster cannot work on HDMA %i\n", config.sb_hdma); + config.exitearly = 1; + } + hdma_idx = config.sb_hdma ? 1 << config.sb_hdma : 0; + sb.mixer_regs[0x80] = irq_idx; + sb.mixer_regs[0x81] = dma_idx | hdma_idx; + sb.mixer_regs[0x82] = SB16_ID82; +} + +static void sb_reset(void) +{ + sb_dsp_reset(); + sb.busy = 1; + sb_mixer_reset(); + adlib_reset(); + mpu401_reset(sb.mpu); +} + +static void do_process_midi(void) +{ + Bit8u data; + + if (sb_midi_input()) { + int cnt = 0; + while (midi_get_data_byte(&data)) { + process_sb_midi_input(data); + cnt++; + } + if (cnt && sb_midi_int()) + sb_activate_irq(SB_IRQ_MIDI); + } +} + +/* + * DANG_BEGIN_FUNCTION sb_dsp_write + * + * arguments: + * value - The value being written to the DSP. + * + * DANG_END_FUNCTION + */ +static void sb_dsp_write(Bit8u value) +{ +#define REQ_PARAMS(i) if (sb.command_idx <= i) return +#define PAR_LSB_MSB(i) (sb.command[i] | (sb.command[i+1] << 8)) +#define PAR_MSB_LSB(i) ((sb.command[i] << 8) | sb.command[i+1]) + + if (sb.command_idx < SB_DSP_CMD_BUF_SZ) { + S_printf("SB: Accepted byte %#x, index=%i\n", value, + sb.command_idx); + sb.command[sb.command_idx++] = value; + sb.busy = 1; + } else { + S_printf("SB: ERROR: command buffer overflowed!\n"); + } + + switch (sb.command[0]) { + /* 0x04: ??? - SB16ASP */ + case 0x04: + REQ_PARAMS(1); + S_printf("SB: Unsupported DSP command 4\n"); + if ((sb.command[1] & 0xf1) == 0xf1) + sb.asp_init = 1; + else + sb.asp_init = 0; + break; + + /* 0x05: ??? - SB16ASP */ + case 0x05: + REQ_PARAMS(2); + S_printf("SB: Unsupported DSP command 5\n"); + break; + + /* 0x08: ??? - SB16ASP */ + case 0x08: + REQ_PARAMS(1); + S_printf("SB: Unsupported DSP command 8\n"); + if (sb.command[1] == 3) + dsp_write_output(0x18); + break; + + /* 0x0e: ??? - SB16ASP */ + case 0x0e: + REQ_PARAMS(2); + S_printf("SB: Unsupported DSP command e\n"); + sb.asp_regs[sb.command[1]] = sb.command[2]; + break; + + /* 0x0f: ??? - SB16ASP */ + case 0x0f: + REQ_PARAMS(1); + S_printf("SB: Unsupported DSP command f\n"); + if (sb.asp_init) + sb.asp_regs[sb.command[1]] = ~sb.asp_regs[sb.command[1]]; + dsp_write_output(sb.asp_regs[sb.command[1]]); + break; + + case 0x10: + /* Direct 8-bit DAC - SB */ + REQ_PARAMS(1); + S_printf("SB: Direct 8-bit DAC write (%u)\n", sb.command[1]); + dspio_write_dac(sb.dspio, sb.command[1]); + break; + + /* == OUTPUT COMMANDS == */ + /* DMA 8-bit DAC - SB */ + case 0x14: + /* DMA 2-bit ADPCM DAC - SB */ + case 0x16: + /* DMA 2-bit ADPCM DAC (Reference) - SB */ + case 0x17: + /* DMA 8-bit ADC - SB */ + case 0x24: + /* DMA 4-bit ADPCM DAC - SB */ + case 0x74: + /* DMA 4-bit ADPCM DAC (Reference) - SB */ + case 0x75: + /* DMA 2.6-bit ADPCM DAC - SB */ + case 0x76: + /* DMA 2.6-bit ADPCM DAC (Reference) - SB */ + case 0x77: + REQ_PARAMS(2); + sb.dma_init_count = PAR_LSB_MSB(1); + sb_dma_activate(); + break; + + /* DMA 8-bit DAC (Auto-Init) - SB2.0 */ + case 0x1C: + /* DMA 8-bit ADC (Auto-Init) - SB2.0 */ + case 0x2C: + /* DMA 8-bit DAC (High Speed, Auto-Init) - SB2.0-Pro2 */ + case 0x90: + /* **CRISK** DMA-8 bit DAC (High Speed) */ + case 0x91: + /* DMA 2-bit ADPCM DAC (Reference, Auto-Init) - SB2.0 */ + case 0x1F: + /* DMA 4-bit ADPCM DAC (Auto-Init, Reference) - SB2.0 */ + case 0x7D: + /* DMA 2.6-bit ADPCM DAC (Auto-Init, Reference) - SB2.0 */ + case 0x7F: + /* DMA 8-bit ADC (High Speed, Auto-Init) - SB2.0-Pro2 */ + case 0x98: + /* DMA 8-bit ADC (High Speed) - SB2.0-Pro2 */ + case 0x99: + if (!sb.dma_init_count) { + REQ_PARAMS(2); + sb.dma_init_count = PAR_LSB_MSB(1); + S_printf("SB: DMA count is now set to %d\n", + sb.dma_init_count); + } else { + S_printf("SB: Re-using DMA count, set to %d\n", + sb.dma_init_count); + } + sb_dma_activate(); + break; + + /* == INPUT COMMANDS == */ + case 0x20: + /* Direct 8-bit ADC - SB */ + S_printf("SB: 8-bit ADC (Unimplemented)\n"); + dsp_write_output(0); + /* I'll implement that, be patient */ + break; + + case 0x28: + /* Direct 8-bit ADC (Burst) - SBPro2 */ + dsp_write_output(0); + break; + + /* == MIDI == */ + case 0x30: /* Enter polling midi input mode */ + case 0x31: /* Enter interruptible midi input mode */ + case 0x32: /* Midi Read Timestamp Poll - SB ? */ + case 0x33: /* Midi Read Timestamp Interrupt - SB ? */ + case 0x34: /* Enter midi polling UART mode */ + case 0x35: /* Enter midi interruptible UART mode */ + case 0x36: /* Enter midi polling UART mode, with timestamping */ + case 0x37: /* Enter midi interruptible UART mode, with timestamping */ + if (sb_midi_input() && sb_midi_int() && !sb_midi_uart() + && sb.command[1] == sb.midi_cmd) { + S_printf("SB: Exiting SB-midi mode %#x\n", sb.midi_cmd); + sb.midi_cmd = 0; + return; + } + S_printf("SB: SB Midi command %#x accepted\n", sb.command[1]); + sb.midi_cmd = sb.command[1]; + break; + + case 0x38: /* Midi Write */ + REQ_PARAMS(1); + S_printf("SB: Write 0x%x to SB Midi Port\n", sb.command[1]); + midi_write(sb.command[1], ST_GM); + break; + + /* == SAMPLE SPEED == */ + case 0x40: + /* Set Time Constant */ + REQ_PARAMS(1); + sb.rate = 1000000 / (256 - sb.command[1]); + S_printf("SB: Time constant set to %u\n", sb.command[1]); + break; + + case 0x41: + /* Set Output Sample Rate - SB16 */ + REQ_PARAMS(2); + /* MSB, LSB values - Karcher */ + sb.rate = PAR_MSB_LSB(1); + S_printf("SB: Output sampling rate set to %u Hz\n", sb.rate); + break; + + case 0x42: + /* Set Input Sample Rate - SB16 */ + REQ_PARAMS(2); + /* MSB, LSB values - Karcher */ + sb.rate = PAR_MSB_LSB(1); + S_printf("SB: Input sampling rate set to %u Hz\n", sb.rate); + break; + + /* == OUTPUT == */ +#if 0 + /* not sb16 */ + /* Continue Auto-Init 8-bit DMA - SB16 */ + case 0x45: + /* Continue Auto-Init 16-bit DMA - SB16 */ + case 0x47: + if (sb.paused) { + S_printf("SB: Unpausing DMA, left=%i\n", sb.dma_count); + sb.paused = 0; + if (sb.dma_cmd) + start_dma_clock(); + } + break; +#endif + case 0x48: + /* Set DMA Block Size - SB2.0 */ + REQ_PARAMS(2); + sb.dma_init_count = PAR_LSB_MSB(1); + S_printf("SB: DMA count is set to %d\n", sb.dma_init_count); + break; + + case 0x80: + /* Silence DAC - SB */ + REQ_PARAMS(2); + sb.dma_init_count = PAR_LSB_MSB(1); + S_printf("SB: Silence count is set to %d\n", + sb.dma_init_count); + sb_dma_activate(); + break; + +#if 0 + /* Those are not available in SB16 */ + /* == STEREO MODE == */ + case 0xA0: + /* Enable Mono Input - SBPro Only */ + S_printf("SB: Disable Stereo Input\n"); + break; + + case 0xA8: + /* Enable Stereo Input - SBPro Only */ + S_printf("SB: Enable Stereo Input\n"); + break; +#endif + + /* == SB16 DMA == */ + case 0xB0 ... 0xCF: + if (sb.command[0] & 1) { + S_printf("SB: Bad command\n"); + break; + } + /* SB16 16-bit DMA */ + REQ_PARAMS(3); + sb.dma_init_count = PAR_LSB_MSB(2); + if (sb.command[0] & 8) { + S_printf("SB: Starting SB16 16-bit DMA input.\n"); + dspio_toggle_speaker(sb.dspio, 0); + } else { + S_printf("SB: Starting SB16 16-bit DMA output.\n"); + dspio_toggle_speaker(sb.dspio, 1); + } + sb_dma_activate(); + break; + + /* == DMA == */ + /* Halt 8-bit DMA - SB */ + case 0xD0: + if (!sb.paused) { + S_printf("SB: Pausing 8bit DMA, left=%i\n", sb.dma_count); + sb_deactivate_irq(SB_IRQ_8BIT); + sb.paused = 1; + if (sb.dma_cmd) + stop_dma_clock(); + } + break; + /* Halt 16-bit DMA - SB16 */ + case 0xD5: + if (!sb.paused) { + S_printf("SB: Pausing 16bit DMA, left=%i\n", sb.dma_count); + sb_deactivate_irq(SB_IRQ_16BIT); + sb.paused = 1; + if (sb.dma_cmd) + stop_dma_clock(); + } + break; + + /* == SPEAKER == */ + case 0xD1: + /* Enable Speaker - SB */ + S_printf("SB: Enabling speaker\n"); + dspio_toggle_speaker(sb.dspio, 1); + break; + + case 0xD3: + /* Disable Speaker - SB */ + S_printf("SB: Disabling speaker\n"); + dspio_toggle_speaker(sb.dspio, 0); + break; + + /* == DMA == */ + /* Continue 8-bit DMA - SB */ + case 0xD4: + /* Continue 16-bit DMA - SB16 */ + case 0xD6: + if (sb.paused) { + S_printf("SB: Unpausing 16bit DMA, left=%i\n", sb.dma_count); + sb.paused = 0; + if (sb.dma_cmd) + start_dma_clock(); + } + break; + + /* == SPEAKER == */ + case 0xD8: + /* Speaker Status */ + dsp_write_output(dspio_get_speaker_state(sb.dspio)); + break; + + /* == DMA == */ + /* Exit Auto-Init 16-bit DMA - SB16 */ + case 0xD9: + /* Exit Auto-Init 8-bit DMA - SB2.0 */ + case 0xDA: + S_printf("SB: Exiting DMA autoinit\n"); + if (sb.dma_cmd) + sb.dma_exit_ai = 1; + break; + + /* == DSP IDENTIFICATION == */ + case 0xE0: + /* DSP Identification - SB2.0 */ + REQ_PARAMS(1); + S_printf("SB: Identify SB2.0 DSP.\n"); + dsp_write_output(~sb.command[1]); + break; + + case 0xE1: + /* DSP Version - SB */ + S_printf("SB: Query Version\n"); + dsp_write_output(SB16_ID >> 8); + dsp_write_output(SB16_ID & 0xFF); + break; + + case 0xE2: { + /* + * Identification via DMA - completely undocumented. + * Based on the code of VDMSound project, + * http://www.sourceforge.net/projects/vdmsound/ + */ + const Bit8u E2_negmsk = 3; + const Bit8u E2_incmagic = 0x96; + int i; + Bit8u negmsk, inctmp, incmagic, incval; + + REQ_PARAMS(1); + S_printf("SB: E2 identification starting\n"); + + negmsk = E2_negmsk; + inctmp = 1; + incval = 0; + for (i = 0; i < 8; i++) { + negmsk <<= (i & 1) + 1; + negmsk |= negmsk >> 4; + negmsk &= 0x0f; + if (test_bit(i, &sb.command[1])) + incval += + ((1 << sb.E2Count) & negmsk) ? -inctmp : inctmp; + inctmp <<= 1; + } + incmagic = E2_incmagic; + if (sb.E2Count & 1) + incmagic += 0x0f; + if (sb.E2Count & 2) + incmagic = (incmagic << 4) | (incmagic >> 4); + incval += incmagic; + sb.E2Count++; + sb.E2Count &= 3; + + sb.reset_val += incval; + sb_dma_activate(); + + break; + } + + case 0xE3: { + char cs[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992."; + rng_add(&sb.dsp_queue, sizeof(cs), cs); + } + break; + + /* == TEST == */ + case 0xE4: + /* Write to Test - SB2.0 */ + REQ_PARAMS(1); + sb.test = sb.command[1]; + S_printf("SB: write 0x%x to test register.\n", sb.test); + break; + + case 0xE8: + /* Read from Test - SB2.0 */ + S_printf("SB: Read 0x%x from test register.\n", sb.test); + dsp_write_output(sb.test); + break; + + case 0xF0: + /* Sine Generator - SB */ + break; + + /* == IRQ == */ + /* 8-bit IRQ - SB */ + case 0xF2: + S_printf("SB: Activating 8bit IRQ\n"); + sb.irq_pend |= SB_IRQ_8BIT; + break; + + /* 16-bit IRQ - SB16 */ + case 0xF3: + S_printf("SB: Activating 16bit IRQ\n"); + sb.irq_pend |= SB_IRQ_8BIT; + break; + + case 0xf9: /* from bochs */ + REQ_PARAMS(1); + S_printf("SB: F9 test\n"); + switch (sb.command[1]) { + case 0x0e: + dsp_write_output(0xff); + break; + case 0x0f: + dsp_write_output(0x07); + break; + case 0x37: + dsp_write_output(0x38); + break; + default: + dsp_write_output(0); + } + break; + + case 0xfa: // Undoc'd SBPro II, from SoundFX + // SB16 puts 0xfb in output buffer but no ready status + S_printf("SB: Undocumented 0xfa\n"); + sb.last_data = 0xfb; + break; + + /* 0xFB: DSP Status - SB16 */ + /* 0xFC: DSP Auxiliary Status - SB16 */ + case 0xfd: /* DSP Command Status - SB16, from SoundFX */ + S_printf("SB: Undocumented 0xfd\n"); + dsp_write_output(sb.command[0]); + break; + + default: + S_printf("SB: ERROR: Unsupported command\n"); + } + + sb.command_idx = 0; +} + +static int line_enabled(void) +{ + return ((sb.mixer_regs[0x3c] | sb.mixer_regs[0x3d] | + sb.mixer_regs[0x3e]) & 0x18); +} + +static int mic_enabled(void) +{ + return ((sb.mixer_regs[0x3c] | sb.mixer_regs[0x3d] | + sb.mixer_regs[0x3e]) & 0x1); +} + +static void sb_mixer_write(Bit8u value) +{ + Bit8u delta = sb.mixer_regs[sb.mixer_index] ^ value; + S_printf("SB: write mixer reg %#x val=%#x\n", sb.mixer_index, value); + sb.mixer_regs[sb.mixer_index] = value; + switch (sb.mixer_index) { + case 0: + sb_mixer_reset(); + break; + + case 0x04: + sb.mixer_regs[0x32] = (value & 0xf0) | 8; + sb.mixer_regs[0x33] = (value << 4) | 8; + break; + + case 0x0A: + sb.mixer_regs[0x3A] = (value << 5) | 0x18; + break; + + case 0x22: + sb.mixer_regs[0x30] = (value & 0xf0) | 8; + sb.mixer_regs[0x31] = (value << 4) | 8; + break; + + case 0x26: + sb.mixer_regs[0x34] = (value & 0xf0) | 8; + sb.mixer_regs[0x35] = (value << 4) | 8; + break; + + case 0x28: + sb.mixer_regs[0x36] = (value & 0xf0) | 8; + sb.mixer_regs[0x37] = (value << 4) | 8; + break; + + case 0x2E: + sb.mixer_regs[0x38] = (value & 0xf0) | 8; + sb.mixer_regs[0x39] = (value << 4) | 8; + break; + + case 0x38: + case 0x39: + if (line_enabled() && (sb.mixer_regs[0x38] || sb.mixer_regs[0x39])) + dspio_input_enable(sb.dspio, MC_LINE); + else + dspio_input_disable(sb.dspio, MC_LINE); + break; + + case 0x3a: + if (mic_enabled() && sb.mixer_regs[0x3a]) + dspio_input_enable(sb.dspio, MC_MIC); + else + dspio_input_disable(sb.dspio, MC_MIC); + break; + + case 0x3c: + case 0x3d: + case 0x3e: + if (delta & 0x18) { + if (line_enabled() && (sb.mixer_regs[0x38] || sb.mixer_regs[0x39])) + dspio_input_enable(sb.dspio, MC_LINE); + else + dspio_input_disable(sb.dspio, MC_LINE); + } + + if (delta & 0x1) { + if (mic_enabled() && sb.mixer_regs[0x3a]) + dspio_input_enable(sb.dspio, MC_MIC); + else + dspio_input_disable(sb.dspio, MC_MIC); + } + break; + + case 0xff: + /* sb16snd.drv insists on 0xff in this register, otherwise it + * starts to reconfigure DMA numbers via 0x81 */ + sb.mixer_regs[sb.mixer_index] = 0xff; + break; + } + + sb.busy = 1; +} + +static Bit8u sb_mixer_read(void) +{ + Bit8u val; + S_printf("SB: Reading Mixer register %#x\n", sb.mixer_index); + switch (sb.mixer_index) { + case 0x04: + val = (sb.mixer_regs[0x32] & 0xf0) | (sb.mixer_regs[0x33] >> 4); + break; + + case 0x0A: + val = (sb.mixer_regs[0x3A] >> 5); + break; + + case 0x22: + val = (sb.mixer_regs[0x30] & 0xf0) | (sb.mixer_regs[0x31] >> 4); + break; + + case 0x26: + val = (sb.mixer_regs[0x34] & 0xf0) | (sb.mixer_regs[0x35] >> 4); + break; + + case 0x28: + val = (sb.mixer_regs[0x36] & 0xf0) | (sb.mixer_regs[0x37] >> 4); + break; + + case 0x2E: + val = (sb.mixer_regs[0x38] & 0xf0) | (sb.mixer_regs[0x39] >> 4); + break; + + default: + val = sb.mixer_regs[sb.mixer_index]; + break; + } + return val; +} + +static double vol5h(int reg) +{ + return dspio_calc_vol(sb.mixer_regs[reg] >> 3, 2, -62); +} + +static double vol2h(int reg) +{ + return dspio_calc_vol(sb.mixer_regs[reg] >> 6, 6, -18); +} + +static double vol3l(int reg) +{ + return dspio_calc_vol(sb.mixer_regs[reg] & 7, 6, -42); +} + +static double gain2h(int reg) +{ + return dspio_calc_vol(sb.mixer_regs[reg] >> 6, 6, 0); +} + +#define ENAB(r, b) \ + if (!(sb.mixer_regs[r] & (1 << (b)))) \ + return MR_DISABLED + +enum MixRet sb_mixer_get_input_volume(enum MixChan ch, enum MixSubChan sc, + double *r_vol) +{ + double vol = 1; + switch (ch) { + case MC_MIDI: + switch (sc) { + case MSC_L: + ENAB(0x3d, 6); + vol *= vol5h(0x34); + break; + case MSC_R: + ENAB(0x3e, 5); + vol *= vol5h(0x35); + break; + case MSC_LR: + ENAB(0x3e, 6); + vol *= vol5h(0x34); + break; + case MSC_RL: + ENAB(0x3d, 5); + vol *= vol5h(0x35); + break; + default: + return MR_UNSUP; + } + break; + case MC_CD: + switch (sc) { + case MSC_L: + ENAB(0x3d, 2); + vol *= vol5h(0x36); + break; + case MSC_R: + ENAB(0x3e, 1); + vol *= vol5h(0x37); + break; + case MSC_LR: + ENAB(0x3e, 2); + vol *= vol5h(0x36); + break; + case MSC_RL: + ENAB(0x3d, 1); + vol *= vol5h(0x37); + break; + default: + return MR_UNSUP; + } + break; + case MC_LINE: + switch (sc) { + case MSC_L: + ENAB(0x3d, 4); + vol *= vol5h(0x38); + break; + case MSC_R: + ENAB(0x3e, 3); + vol *= vol5h(0x39); + break; + case MSC_LR: + ENAB(0x3e, 4); + vol *= vol5h(0x38); + break; + case MSC_RL: + ENAB(0x3d, 3); + vol *= vol5h(0x39); + break; + default: + return MR_UNSUP; + } + break; + case MC_MIC: + switch (sc) { + case MSC_MONO_L: + ENAB(0x3d, 0); + vol *= vol3l(0x3a); + break; + case MSC_MONO_R: + ENAB(0x3e, 0); + vol *= vol3l(0x3a); + break; + default: + return MR_UNSUP; + } + break; + default: + return MR_UNSUP; + } + + switch (sc) { + case MSC_L: + case MSC_RL: + case MSC_MONO_L: + vol *= gain2h(0x3f); + break; + case MSC_R: + case MSC_LR: + case MSC_MONO_R: + vol *= gain2h(0x40); + break; + } + + *r_vol = vol; + return MR_OK; +} + +enum MixRet sb_mixer_get_output_volume(enum MixChan ch, enum MixSubChan sc, + double *r_vol) +{ + double vol = 1; + switch (ch) { + case MC_MIDI: + switch (sc) { + case MSC_L: + vol *= vol5h(0x34); + break; + case MSC_R: + vol *= vol5h(0x35); + break; + default: + return MR_UNSUP; + } + break; + case MC_CD: + switch (sc) { + case MSC_L: + ENAB(0x3c, 2); + vol *= vol5h(0x36); + break; + case MSC_R: + ENAB(0x3c, 1); + vol *= vol5h(0x37); + break; + default: + return MR_UNSUP; + } + break; + case MC_LINE: + switch (sc) { + case MSC_L: + ENAB(0x3c, 4); + vol *= vol5h(0x38); + break; + case MSC_R: + ENAB(0x3c, 3); + vol *= vol5h(0x39); + break; + default: + return MR_UNSUP; + } + break; + case MC_MIC: + switch (sc) { + case MSC_MONO_L: + case MSC_MONO_R: + ENAB(0x3c, 0); + vol *= vol3l(0x3a); + break; + default: + return MR_UNSUP; + } + break; + case MC_VOICE: + switch (sc) { + case MSC_L: + vol *= vol5h(0x32); + break; + case MSC_R: + vol *= vol5h(0x33); + break; + default: + return MR_UNSUP; + } + break; + case MC_PCSP: + switch (sc) { + case MSC_MONO_L: + case MSC_MONO_R: + vol *= vol2h(0x3b); + break; + default: + return MR_UNSUP; + } + break; + default: + return MR_UNSUP; + } + + switch (sc) { + case MSC_L: + case MSC_RL: + case MSC_MONO_L: + vol *= vol5h(0x30); // master + vol *= gain2h(0x41); + break; + case MSC_R: + case MSC_LR: + case MSC_MONO_R: + vol *= vol5h(0x31); // master + vol *= gain2h(0x42); + break; + } + + *r_vol = vol; + return MR_OK; +} + + +#define EN(r, b) (sb.mixer_regs[r] & (1 << (b))) + +int sb_is_input_connected(enum MixChan ch) +{ + switch (ch) { + case MC_MIDI: + return (EN(0x3d, 6) || EN(0x3e, 5) || EN(0x3e, 6) || EN(0x3d, 5)); + case MC_CD: + return (EN(0x3d, 2) || EN(0x3e, 1) || EN(0x3e, 2) || EN(0x3d, 1)); + case MC_LINE: + return (EN(0x3d, 4) || EN(0x3e, 3) || EN(0x3e, 4) || EN(0x3d, 3)); + case MC_MIC: + return (EN(0x3d, 0) || EN(0x3e, 0)); + default: + return 0; + } +} + +int sb_is_output_connected(enum MixChan ch) +{ + switch (ch) { + case MC_CD: + return (EN(0x3c, 2) || EN(0x3c, 1)); + case MC_LINE: + return (EN(0x3c, 4) || EN(0x3c, 3)); + case MC_MIC: + return EN(0x3c, 0); + case MC_VOICE: + case MC_PCSP: + case MC_MIDI: + return 1; + default: + return 0; + } +} + +int sb_mixer_get_chan_num(enum MixChan ch) +{ + switch (ch) { + case MC_MIC: + case MC_PCSP: + return 1; + default: + return 2; + } +} + +/* + * DANG_BEGIN_FUNCTION sb_io_write + * + * arguments: + * port - The I/O port being written to. + * value - The value being output. + * + * DANG_END_FUNCTION + */ + +static void sb_io_write(ioport_t port, Bit8u value, void *arg) +{ + ioport_t addr; + addr = port - config.sb_base; + + if (debug_level('S') >= 2) { + S_printf("SB: Write to port 0x%04x, value 0x%02x\n", (Bit16u) port, + value); + } + + switch (addr) { + /* == FM MUSIC == */ + case 0x00 ... 0x03: + adlib_io_write_base(addr, value); + break; + + case 0x08: + case 0x09: + adlib_io_write_base(addr - 8, value); + break; + + /* == MIXER == */ + case 0x04: /* Mixer Register Address Port */ + sb.mixer_index = value; + break; + + case 0x05: /* Mixer Data Register */ + sb_mixer_write(value); + break; + + case 0x06: /* DSP reset */ + sb_dsp_soft_reset(value); + break; + +#if 0 + case 0x0A: /* Write to DSP queue probably - undocumented */ + S_printf("SB: undocumented write to A\n"); + rng_clear(&sb.dsp_queue); + rng_put(&sb.dsp_queue, &value); + break; +#endif + + /* == DSP == */ + case 0x0C: /* dsp write register */ + if (sb_midi_uart()) { + midi_write(value, ST_GM); + break; + } + if (sb_dma_active() && sb_dma_high_speed()) { + S_printf + ("SB: Commands are not permitted in High-Speed DMA mode!\n"); + /* break; */ + } + sb_dsp_write(value); + break; + + /* 0x0D: Timer Interrupt Clear - SB16 */ + /* 0x10 - 0x13: CD-ROM - SBPro ***IGNORED*** */ + + default: + S_printf("SB: Write to port %#x unimplemented\n", addr); + } +} + +/* + * DANG_BEGIN_FUNCTION sb_io_read + * + * arguments: + * port - The I/O port being read from. + * + * DANG_END_FUNCTION + */ + +static Bit8u sb_io_read(ioport_t port, void *arg) +{ + ioport_t addr; + Bit8u result = 0; + + addr = port - config.sb_base; + + switch (addr) { + + /* == FM Music == */ + case 0x00 ... 0x03: + result = adlib_io_read_base(addr); + break; + case 0x08: + case 0x09: + result = adlib_io_read_base(addr - 8); + break; + + case 0x04: /* Mixer index */ + result = sb.mixer_index; + break; + + case 0x05: /* Mixer Data Register */ + result = sb_mixer_read(); + break; + + case 0x06: /* Reset ? */ + S_printf("SB: read from Reset address\n"); + result = 0; /* Some programs read this whilst resetting */ + break; + + case 0x0A: /* DSP Read Data - SB */ + if (rng_count(&sb.dsp_queue)) + rng_get(&sb.dsp_queue, &sb.last_data); + result = sb.last_data; + S_printf("SB: Read 0x%x from SB DSP\n", result); + if (sb_midi_int()) { + if (!rng_count(&sb.dsp_queue)) + sb_deactivate_irq(SB_IRQ_MIDI); + sb_run_irq(SB_IRQ_MIDI); + } + break; + + case 0x0C: /* DSP Write Buffer Status */ + result = SB_DSP_STATUS; + if (sb.busy) + result |= 0x80; + if (sb.busy == 1) + sb.busy = 0; + S_printf("SB: Read 0x%x from DSP Write Buffer Status Register (%i)\n", + result, sb.busy); + break; + + case 0x0D: /* DSP MIDI Interrupt Clear - SB16 ? */ + S_printf + ("SB: read 16-bit MIDI interrupt status. Not Implemented.\n"); + result = 0xFF; + break; + + case 0x0E: + /* DSP Data Available Status - SB */ + /* DSP 8-bit IRQ Ack - SB */ + result = SB_GET_STATUS(); + S_printf("SB: 8-bit IRQ Ack (%i)\n", sb.dma_count); + if (sb_irq_active(SB_IRQ_8BIT)) + sb_deactivate_irq(SB_IRQ_8BIT); + if (sb.paused && sb.dma_cmd) { + S_printf("SB: drop DMA command %x\n", sb.dma_cmd); + sb.dma_cmd = 0; + sb.dma_restart.val = DMA_RESTART_NONE; + } + if (sb.dma_restart.val && !sb.dma_restart.is_16) { + sb_dma_start(); + if (sb.dma_restart.val == DMA_RESTART_PENDING) + sb.dma_restart.val = DMA_RESTART_NONE; + } + break; + case 0x0F: /* 0x0F: DSP 16-bit IRQ - SB16 */ + result = SB_GET_STATUS(); + S_printf("SB: 16-bit IRQ Ack: (%i)\n", sb.dma_count); + if (sb_irq_active(SB_IRQ_16BIT)) + sb_deactivate_irq(SB_IRQ_16BIT); + if (sb.paused && sb.dma_cmd) { + S_printf("SB: drop DMA command %x\n", sb.dma_cmd); + sb.dma_cmd = 0; + sb.dma_restart.val = DMA_RESTART_NONE; + } + if (sb.dma_restart.val && sb.dma_restart.is_16) { + sb_dma_start(); + if (sb.dma_restart.val == DMA_RESTART_PENDING) + sb.dma_restart.val = DMA_RESTART_NONE; + } + break; + + /* == CD-ROM - UNIMPLEMENTED == */ + case 0x10: /* CD-ROM Data Register - SBPro */ + S_printf("SB: read from CD-ROM Data register.\n"); + result = 0; + break; + + case 0x11: /* CD-ROM Status Port - SBPro */ + S_printf("SB: read from CD-ROM status port.\n"); + result = 0xFE; + break; + + default: + S_printf("SB: %#x is an unhandled read port.\n", port); + result = 0xFF; + } + + if (debug_level('S') >= 3) { + S_printf("SB: port read 0x%x returns 0x%x\n", port, result); + } + + return result; +} + +int sb_get_dma_data(void *ptr, int is16bit) +{ + if (sb_dma_internal()) { + S_printf("SB: E2 value %#x transferred\n", sb.reset_val); + if (is16bit) + *(Bit16u *) ptr = sb.reset_val; + else + *(Bit8u *) ptr = sb.reset_val; + return 1; + } + return 0; +} + +static void process_sb_midi_input(Bit8u val) +{ + if (sb_midi_timestamp()) { + Bit32u time = dspio_get_midi_in_time(sb.dspio); + dsp_write_output(time); + dsp_write_output(time >> 8); + dsp_write_output(time >> 16); + } + dsp_write_output(val); +} + +void run_sb(void) +{ + if (!config.sound) + return; + + /* https://github.com/dosemu2/dosemu2/issues/2172 */ + if (sb.irq_pend /*&& ++sb.irq_delay_cnt > 5*/) { + if (sb.irq_pend & SB_IRQ_8BIT) + sb_activate_irq(SB_IRQ_8BIT); + if (sb.irq_pend & SB_IRQ_16BIT) + sb_activate_irq(SB_IRQ_16BIT); + sb.irq_pend = 0; + sb.irq_delay_cnt = 0; + } + + dspio_timer(sb.dspio); + do_process_midi(); + mpu401_process(sb.mpu); +} + +static void sb_dsp_init(void) +{ + rng_init(&sb.dsp_queue, DSP_QUEUE_SIZE, 1); + + sb.reset_val = 0xaa; +} + +static void sb_dsp_done(void) +{ + rng_destroy(&sb.dsp_queue); +} + +static void mpu_activate_irq(struct mpu401_s *mpu) +{ + sb_activate_irq(SB_IRQ_MPU401); +} + +static void mpu_deactivate_irq(struct mpu401_s *mpu) +{ + sb_deactivate_irq(SB_IRQ_MPU401); +} + +static void mpu_run_irq(struct mpu401_s *mpu) +{ + sb_run_irq(SB_IRQ_MPU401); +} + +static void mpu_write_midi(struct mpu401_s *mpu, uint8_t data) +{ + midi_write(data, ST_GM); +} + +static struct mpu401_ops mops = { + .activate_irq = mpu_activate_irq, + .deactivate_irq = mpu_deactivate_irq, + .run_irq = mpu_run_irq, + .write_midi = mpu_write_midi, + .name = "SB MPU401" +}; + +/* + * Sound Initialisation + * ==================== + */ +static void sb_init(void) +{ + emu_iodev_t io_device; + + S_printf("SB: SB Initialisation\n"); + + /* SB Emulation */ + io_device.read_portb = sb_io_read; + io_device.write_portb = sb_io_write; + io_device.read_portw = NULL; + io_device.write_portw = NULL; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + io_device.handler_name = "SB Emulation"; + io_device.start_addr = config.sb_base; + io_device.end_addr = config.sb_base + 0x013; + if (port_register_handler(io_device, 0) != 0) { + error("SB: Cannot registering DSP port handler\n"); + } + + sb_dsp_init(); + sb_mixer_init(); + opl3_init(); + + if (config.mpu401_irq == -1) { + config.mpu401_irq = config.sb_irq; + S_printf("SB: mpu401 irq set to %i\n", config.mpu401_irq); + } + sb.mpu = mpu401_init(config.mpu401_base, &mops); + + S_printf("SB: Initialisation - Base 0x%03x\n", config.sb_base); +} + +static void sb_done(void) +{ + sb_dsp_done(); + adlib_done(); + mpu401_done(sb.mpu); +} + +void sound_init(void) +{ + if (!config.sound) + return; + sb.dspio = dspio_init(); + if (!sb.dspio) { + error("dspio failed\n"); + leavedos(93); + } + sb_init(); +} + +void sound_reset(void) +{ + if (config.sound) { + dspio_reset(sb.dspio); + sb_reset(); + } +} + +void sound_done(void) +{ + if (config.sound) { + sb_done(); + dspio_done(sb.dspio); + } +} diff --git a/src/base/dev/sb16/sb16.h b/src/base/dev/sb16/sb16.h new file mode 100644 index 0000000..63da504 --- /dev/null +++ b/src/base/dev/sb16/sb16.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2006 Stas Sergeev + * + * The below copyright strings have to be distributed unchanged together + * with this file. This prefix can not be modified or separated. + */ + +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef __SB16_H__ +#define __SB16_H__ + +#include "ringbuf.h" // for rng_s +#include "sound/sound.h" + +#define SB_NONE 0x000 +#define SB_ID 0x105 +#define SB20_ID 0x201 +#define SBPRO_ID 0x300 +#define SB16_ID 0x405 + +/* bochs and the old dosemu code disagree on that value. + * Of course I trust bochs. :) */ +#define SB16_ID82 (2 << 5) + +#define SB_IRQ_8BIT 1 +#define SB_IRQ_16BIT 2 +#define SB_IRQ_MIDI SB_IRQ_8BIT +#define SB_IRQ_MPU401 4 +#define SB_IRQ_DSP (SB_IRQ_8BIT | SB_IRQ_16BIT | SB_IRQ_MIDI) +#define SB_IRQ_ALL (SB_IRQ_DSP | SB_IRQ_MPU401) + +extern int sb_get_dma_num(void); +extern int sb_get_hdma_num(void); +extern int sb_dma_active(void); +extern int sb_dma_16bit(void); +extern int sb_dma_adpcm(void); +extern int sb_dma_adpcm_ref(void); +extern int sb_fifo_enabled(void); +extern int sb_dma_samp_signed(void); +extern int sb_dma_samp_stereo(void); +extern int sb_dma_input(void); +extern int sb_dma_silence(void); +extern int sb_get_dma_sampling_rate(void); +extern int sb_get_dma_data(void *ptr, int is16bit); +extern void sb_handle_dma(void); +extern void sb_dma_nack(void); +extern void sb_handle_dma_timeout(void); +extern int sb_input_enabled(void); + +enum MixRet sb_mixer_get_input_volume(enum MixChan ch, enum MixSubChan sc, + double *r_vol); +enum MixRet sb_mixer_get_output_volume(enum MixChan ch, enum MixSubChan sc, + double *r_vol); +int sb_mixer_get_chan_num(enum MixChan ch); +int sb_is_input_connected(enum MixChan ch); +int sb_is_output_connected(enum MixChan ch); + +#define SB_CHAN_L 0 +#define SB_CHAN_R 1 + +#endif diff --git a/src/base/dev/sb16/softmpu/Makefile b/src/base/dev/sb16/softmpu/Makefile new file mode 100644 index 0000000..6207d9e --- /dev/null +++ b/src/base/dev/sb16/softmpu/Makefile @@ -0,0 +1,13 @@ +# +# (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". +# +# for details see file COPYING.DOSEMU in the DOSEMU distribution +# + +top_builddir=../../../../.. +include $(top_builddir)/Makefile.conf + + +CFILES = mpu401.c pic.c + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/dev/sb16/softmpu/export.h b/src/base/dev/sb16/softmpu/export.h new file mode 100644 index 0000000..ea210ac --- /dev/null +++ b/src/base/dev/sb16/softmpu/export.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2002-2012 The DOSBox Team + * Copyright (C) 2013-2014 bjt, elianda + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * ------------------------------------------ + * SoftMPU by bjt - Software MPU-401 Emulator + * ------------------------------------------ + * + * C types/functions used from ASM + * + */ + +#ifndef EXPORT_H + +typedef unsigned char Bit8u; +typedef unsigned int Bitu; + +typedef enum EventID {MPU_EVENT,RESET_DONE,EOI_HANDLER,NUM_EVENTS} EventID; + +/* Interface functions */ +void MPU401_Init(void); +void MPU401_Done(void); +void MPU401_WriteCommand(Bit8u val); +void MPU401_ReadData(Bit8u val); +void MPU401_WriteData(Bit8u val); + +void MPU401_Event(void); +void MPU401_ResetDone(void); +void MPU401_EOIHandler(void); + +void PIC_Init(void); /* SOFTMPU */ +void PIC_Done(void); +void PIC_AddEvent(EventID event,Bitu delay); +void PIC_RemoveEvents(EventID event); +void PIC_Start(void); +void PIC_Stop(void); + +void QueueByte(Bit8u data); +void ClrQueue(void); + +#define EXPORT_H +#endif diff --git a/src/base/dev/sb16/softmpu/mpu401.c b/src/base/dev/sb16/softmpu/mpu401.c new file mode 100644 index 0000000..5f44393 --- /dev/null +++ b/src/base/dev/sb16/softmpu/mpu401.c @@ -0,0 +1,638 @@ +/* rip-off for dosemu2 project by stsp */ +/* + * Copyright (C) 2002-2012 The DOSBox Team + * Copyright (C) 2013-2014 bjt, elianda + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * ------------------------------------------ + * SoftMPU by bjt - Software MPU-401 Emulator + * ------------------------------------------ + * + * Based on original mpu401.c from DOSBox + * + */ +#include "sound/midi.h" +#include "pic.h" +#include "emu.h" +/* SOFTMPU: Moved exported functions & types to header */ +#include "export.h" + +/* SOFTMPU: Additional defines, typedefs etc. for C */ +typedef unsigned short Bit16u; +typedef int Bits; + +static void MIDI_RawOutByte(Bit8u data) +{ + midi_write(data, ST_MT32); +} + +void MPU401_Event(void); +void MPU401_ResetDone(void); /* SOFTMPU */ +void MPU401_EOIHandler(void); +static void MPU401_Reset(void); +static void MPU401_EOIHandlerDispatch(void); + +#define MPU401_VERSION 0x15 +#define MPU401_REVISION 0x01 +#define MPU401_TIMECONSTANT (60000000 / 1000.0) +#define MPU401_RESETBUSY 14.0 + +enum MpuMode { M_UART,M_INTELLIGENT }; +typedef enum MpuMode MpuMode; /* SOFTMPU */ +enum MpuDataType {T_OVERFLOW,T_MARK,T_MIDI_SYS,T_MIDI_NORM,T_COMMAND}; +typedef enum MpuDataType MpuDataType; /* SOFTMPU */ + +/* Messages sent to MPU-401 from host */ +#define MSG_EOX 0xf7 +#define MSG_OVERFLOW 0xf8 +#define MSG_MARK 0xfc + +/* Messages sent to host from MPU-401 */ +#define MSG_MPU_OVERFLOW 0xf8 +#define MSG_MPU_COMMAND_REQ 0xf9 +#define MSG_MPU_END 0xfc +#define MSG_MPU_CLOCK 0xfd +#define MSG_MPU_ACK 0xfe + +static struct { + bool intelligent; + bool generate_irqs; /* SOFTMPU */ + bool mpu_ver_fix; /* SOFTMPU */ + MpuMode mode; + /*Bitu irq;*/ /* SOFTMPU */ + struct track { + Bits counter; + Bit8u value[8],sys_val; + Bit8u vlength,length; + MpuDataType type; + } playbuf[8],condbuf; + struct { + bool conductor,cond_req,cond_set, block_ack; + bool playing,reset; + bool wsd,wsm,wsd_start; + bool run_irq,irq_pending; + bool send_now; + bool eoi_scheduled; + Bits data_onoff; + Bitu command_byte,cmd_pending; + Bit8u tmask,cmask,amask; + Bit16u midi_mask; + Bit16u req_mask; + Bit8u channel,old_chan; + } state; + struct { + Bit8u timebase; + Bit8u tempo; + Bit8u tempo_rel; + Bit8u tempo_grad; + Bit8u cth_rate,cth_counter; + bool clock_to_host,cth_active; + } clock; +} mpu; + +void MPU401_WriteCommand(Bit8u val) /* SOFTMPU */ +{ + Bitu i; /* SOFTMPU */ + if (mpu.state.reset) { + if (mpu.state.cmd_pending || (val!=0x3f && val!=0xff)) { + mpu.state.cmd_pending=val+1; + return; + } + PIC_RemoveEvents(RESET_DONE); + mpu.state.reset=false; + } + if (val<=0x2f) { + switch (val&3) { /* MIDI stop, start, continue */ + case 1: {MIDI_RawOutByte(0xfc);break;} + case 2: {MIDI_RawOutByte(0xfa);break;} + case 3: {MIDI_RawOutByte(0xfb);break;} + } + /*if (val&0x20) LOG(LOG_MISC,LOG_ERROR)("MPU-401:Unhandled Recording Command %x",val);*/ /* SOFTMPU */ + switch (val&0xc) { + case 0x4: /* Stop */ + PIC_RemoveEvents(MPU_EVENT); + PIC_Stop(); + mpu.state.playing=false; + for (i=0xb0;i<0xbf;i++) { /* All notes off */ + MIDI_RawOutByte((Bit8u)i); + MIDI_RawOutByte(0x7b); + MIDI_RawOutByte(0); + } + break; + case 0x8: /* Play */ + /*LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Intelligent mode playback started");*/ /* SOFTMPU */ + mpu.state.playing=true; + PIC_RemoveEvents(MPU_EVENT); + PIC_AddEvent(MPU_EVENT,(Bitu)MPU401_TIMECONSTANT/(mpu.clock.tempo*mpu.clock.timebase)); + PIC_Start(); + ClrQueue(); + break; + } + } + else if (val>=0xa0 && val<=0xa7) { /* Request play counter */ + if (mpu.state.cmask&(1<<(val&7))) QueueByte((Bit8u)mpu.playbuf[val&7].counter); + } + else if (val>=0xd0 && val<=0xd7) { /* Send data */ + mpu.state.old_chan=mpu.state.channel; + mpu.state.channel=val&7; + mpu.state.wsd=true; + mpu.state.wsm=false; + mpu.state.wsd_start=true; + } + else + switch (val) { + case 0xdf: /* Send system message */ + mpu.state.wsd=false; + mpu.state.wsm=true; + mpu.state.wsd_start=true; + break; + case 0x8e: /* Conductor */ + mpu.state.cond_set=false; + break; + case 0x8f: + mpu.state.cond_set=true; + break; + case 0x94: /* Clock to host */ + mpu.clock.clock_to_host=false; + break; + case 0x95: + mpu.clock.clock_to_host=true; + break; + case 0xc2: /* Internal timebase */ + mpu.clock.timebase=48; + break; + case 0xc3: + mpu.clock.timebase=72; + break; + case 0xc4: + mpu.clock.timebase=96; + break; + case 0xc5: + mpu.clock.timebase=120; + break; + case 0xc6: + mpu.clock.timebase=144; + break; + case 0xc7: + mpu.clock.timebase=168; + break; + case 0xc8: + mpu.clock.timebase=192; + break; + /* Commands with data byte */ + case 0xe0: case 0xe1: case 0xe2: case 0xe4: case 0xe6: + case 0xe7: case 0xec: case 0xed: case 0xee: case 0xef: + mpu.state.command_byte=val; + break; + /* Commands 0xa# returning data */ + case 0xab: /* Request and clear recording counter */ + QueueByte(MSG_MPU_ACK); + QueueByte(0); + return; + case 0xac: /* Request version */ + if (mpu.mpu_ver_fix) + { + /* SOFTMPU: Fix missing music in Gateway by reversing version and ACK */ + QueueByte(MPU401_VERSION); + QueueByte(MSG_MPU_ACK); + } + else + { + QueueByte(MSG_MPU_ACK); + QueueByte(MPU401_VERSION); + } + return; + case 0xad: /* Request revision */ + QueueByte(MSG_MPU_ACK); + QueueByte(MPU401_REVISION); + return; + case 0xaf: /* Request tempo */ + QueueByte(MSG_MPU_ACK); + QueueByte(mpu.clock.tempo); + return; + case 0xb1: /* Reset relative tempo */ + mpu.clock.tempo_rel=40; + break; + case 0xb9: /* Clear play map */ + case 0xb8: /* Clear play counters */ + for (i=0xb0;i<0xbf;i++) { /* All notes off */ + MIDI_RawOutByte((Bit8u)i); + MIDI_RawOutByte(0x7b); + MIDI_RawOutByte(0); + } + for (i=0;i<8;i++) { + mpu.playbuf[i].counter=0; + mpu.playbuf[i].type=T_OVERFLOW; + } + mpu.condbuf.counter=0; + mpu.condbuf.type=T_OVERFLOW; + if (!(mpu.state.conductor=mpu.state.cond_set)) mpu.state.cond_req=0; + mpu.state.amask=mpu.state.tmask; + mpu.state.req_mask=0; + mpu.state.irq_pending=true; + break; + case 0xff: /* Reset MPU-401 */ + /*LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Reset %X",val);*/ /* SOFTMPU */ + PIC_AddEvent(RESET_DONE,MPU401_RESETBUSY); + mpu.state.reset=true; + MPU401_Reset(); + if (mpu.mode==M_UART) return;//do not send ack in UART mode + break; + case 0x3f: /* UART mode */ + /*LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Set UART mode %X",val);*/ /* SOFTMPU */ + mpu.mode=M_UART; + break; + default: + /*LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Unhandled command %X",val);*/ + break; + } + QueueByte(MSG_MPU_ACK); +} + +void MPU401_ReadData(Bit8u data) /* SOFTMPU */ +{ + Bit8u ret=data; + +// if ((mpu.queue_used == 0) && mpu.generate_irqs) PIC_DeActivateIRQ(); /* SOFTMPU */ + + if (ret>=0xf0 && ret<=0xf7) { /* MIDI data request */ + mpu.state.channel=ret&7; + mpu.state.data_onoff=0; + mpu.state.cond_req=false; + } + if (ret==MSG_MPU_COMMAND_REQ) { + mpu.state.data_onoff=0; + mpu.state.cond_req=true; + if (mpu.condbuf.type!=T_OVERFLOW) { + mpu.state.block_ack=true; + MPU401_WriteCommand(mpu.condbuf.value[0]); + if (mpu.state.command_byte) MPU401_WriteData(mpu.condbuf.value[1]); + } + mpu.condbuf.type=T_OVERFLOW; + } + if (ret==MSG_MPU_END || ret==MSG_MPU_CLOCK || ret==MSG_MPU_ACK) { + mpu.state.data_onoff=-1; + MPU401_EOIHandlerDispatch(); + } +} + +void MPU401_WriteData(Bit8u val) { /* SOFTMPU */ + static Bitu length,cnt,posd; /* SOFTMPU */ + if (mpu.mode==M_UART) {MIDI_RawOutByte((Bit8u)val);return;} + switch (mpu.state.command_byte) { /* 0xe# command data */ + case 0x00: + break; + case 0xe0: /* Set tempo */ + mpu.state.command_byte=0; + mpu.clock.tempo=val; + return; + case 0xe1: /* Set relative tempo */ + mpu.state.command_byte=0; + /*if (val!=0x40) //default value + LOG(LOG_MISC,LOG_ERROR)("MPU-401:Relative tempo change not implemented");*/ /* SOFTMPU */ + return; + case 0xe7: /* Set internal clock to host interval */ + mpu.state.command_byte=0; + mpu.clock.cth_rate=val>>2; + return; + case 0xec: /* Set active track mask */ + mpu.state.command_byte=0; + mpu.state.tmask=val; + return; + case 0xed: /* Set play counter mask */ + mpu.state.command_byte=0; + mpu.state.cmask=val; + return; + case 0xee: /* Set 1-8 MIDI channel mask */ + mpu.state.command_byte=0; + mpu.state.midi_mask&=0xff00; + mpu.state.midi_mask|=val; + return; + case 0xef: /* Set 9-16 MIDI channel mask */ + mpu.state.command_byte=0; + mpu.state.midi_mask&=0x00ff; + mpu.state.midi_mask|=((Bit16u)val)<<8; + return; + //case 0xe2: /* Set graduation for relative tempo */ + //case 0xe4: /* Set metronome */ + //case 0xe6: /* Set metronome measure length */ + default: + mpu.state.command_byte=0; + return; + } + if (mpu.state.wsd) { /* Directly send MIDI message */ + if (mpu.state.wsd_start) { + mpu.state.wsd_start=0; + cnt=0; + switch (val&0xf0) { + case 0xc0:case 0xd0: + mpu.playbuf[mpu.state.channel].value[0]=val; + length=2; + break; + case 0x80:case 0x90:case 0xa0:case 0xb0:case 0xe0: + mpu.playbuf[mpu.state.channel].value[0]=val; + length=3; + break; + case 0xf0: + /*LOG(LOG_MISC,LOG_ERROR)("MPU-401:Illegal WSD byte");*/ /* SOFTMPU */ + mpu.state.wsd=0; + mpu.state.channel=mpu.state.old_chan; + return; + default: /* MIDI with running status */ + cnt++; + MIDI_RawOutByte(mpu.playbuf[mpu.state.channel].value[0]); + } + } + if (cnt0xf7) { + mpu.playbuf[mpu.state.channel].type=T_MARK; + mpu.playbuf[mpu.state.channel].sys_val=val; + length=1; + } else { + /*LOG(LOG_MISC,LOG_ERROR)("MPU-401:Illegal message");*/ /* SOFTMPU */ + mpu.playbuf[mpu.state.channel].type=T_MIDI_SYS; + mpu.playbuf[mpu.state.channel].sys_val=val; + length=1; + } + break; + case 0xc0: case 0xd0: /* MIDI Message */ + mpu.playbuf[mpu.state.channel].type=T_MIDI_NORM; + length=mpu.playbuf[mpu.state.channel].length=2; + break; + case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0: + mpu.playbuf[mpu.state.channel].type=T_MIDI_NORM; + length=mpu.playbuf[mpu.state.channel].length=3; + break; + default: /* MIDI data with running status */ + posd++; + mpu.playbuf[mpu.state.channel].vlength++; + mpu.playbuf[mpu.state.channel].type=T_MIDI_NORM; + length=mpu.playbuf[mpu.state.channel].length; + break; + } + } + if (!(posd==1 && val>=0xf0)) mpu.playbuf[mpu.state.channel].value[posd-1]=val; + if (posd==length) MPU401_EOIHandlerDispatch(); + } +} + +static void MPU401_IntelligentOut(Bit8u chan) { + Bitu val, i; /* SOFTMPU */ + switch (mpu.playbuf[chan].type) { + case T_OVERFLOW: + break; + case T_MARK: + val=mpu.playbuf[chan].sys_val; + if (val==0xfc) { + MIDI_RawOutByte((Bit8u)val); + mpu.state.amask&=~(1<= mpu.clock.cth_rate) { + mpu.clock.cth_counter=0; + mpu.state.req_mask|=(1<<13); + } + } + if (!mpu.state.irq_pending && mpu.state.req_mask) MPU401_EOIHandler(); +next_event: + PIC_RemoveEvents(MPU_EVENT); + if ((new_time=mpu.clock.tempo*mpu.clock.timebase)==0) return; + PIC_AddEvent(MPU_EVENT,(Bitu)MPU401_TIMECONSTANT/new_time); +} + +static void MPU401_EOIHandlerDispatch(void) { + if (mpu.state.send_now) { + mpu.state.eoi_scheduled=true; + PIC_AddEvent(EOI_HANDLER,1); // Possible a bit longer + } + else if (!mpu.state.eoi_scheduled) MPU401_EOIHandler(); +} + +//Updates counters and requests new data on "End of Input" +void MPU401_EOIHandler(void) { + Bitu i=0; /* SOFTMPU */ + mpu.state.eoi_scheduled=false; + if (mpu.state.send_now) { + mpu.state.send_now=false; + if (mpu.state.cond_req) UpdateConductor(); + else UpdateTrack(mpu.state.channel); + } + mpu.state.irq_pending=false; + if (!mpu.state.playing || !mpu.state.req_mask) return; + do { + if (mpu.state.req_mask&(1< +#include "sig.h" +#include "evtimer.h" +/* SOFTMPU: Moved exported functions & types to header */ +#include "export.h" + +static void *evtimer; + +static void PIC_Update(int ms_ticks, void *arg); + +static void _MPU401_Event(void *arg) +{ + MPU401_Event(); +} + +static void _MPU401_ResetDone(void *arg) +{ + MPU401_ResetDone(); +} + +static void _MPU401_EOIHandler(void *arg) +{ + MPU401_EOIHandler(); +} + +/* SOFTMPU: Event countdown timers */ +static Bitu event_countdown[NUM_EVENTS]; +static Bitu MIDI_sysex_delay; /* SOFTMPU: Initialised in midi.c */ + +void PIC_AddEvent(EventID event, Bitu delay) +{ + /* Dispatch event immediately on zero delay */ + /* Watch out for blocking loops here... */ + if (delay==0) + { + switch (event) + { + case MPU_EVENT: + /* Don't dispatch immediately as we'll enter an + infinite loop if tempo is high enough */ + delay=1; /* Enforce minimum delay */ + break; + case RESET_DONE: + MPU401_ResetDone(); + break; + case EOI_HANDLER: + MPU401_EOIHandler(); + break; + default: + break; + } + } + + /* SOFTMPU: Set the countdown timer */ + event_countdown[event]=delay; +} + +void PIC_RemoveEvents(EventID event) +{ + /* SOFTMPU: Zero the countdown timer (disable event) */ + event_countdown[event]=0; +} + +void PIC_Start(void) +{ + evtimer_set_rel(evtimer, SCALE_MS, 1); +} + +void PIC_Stop(void) +{ + evtimer_stop(evtimer); +} + +void PIC_Init(void) +{ + Bitu i; + + /* SOFTMPU: Zero countdown timers */ + for (i=0;i= ms_ticks) + { + MIDI_sysex_delay -= ms_ticks; + } + else + MIDI_sysex_delay = 0; + + /* SOFTMPU: Decrement countdown timers and dispatch as needed */ + for (i=0;i 0) { + if (event_countdown[i] >= ms_ticks) + event_countdown[i] -= ms_ticks; + else + event_countdown[i] = 0; + + if (event_countdown[i]==0) + { + /* Dispatch */ + switch (i) + { + case MPU_EVENT: + add_thread_callback(_MPU401_Event, NULL, "mpu401 event"); + break; + case RESET_DONE: + add_thread_callback(_MPU401_ResetDone, NULL, "mpu401 reset done"); + break; + case EOI_HANDLER: + add_thread_callback(_MPU401_EOIHandler, NULL, "mpu401 EOI"); + break; + default: + break; + } + } + } + } +} diff --git a/src/base/dev/vga/Makefile b/src/base/dev/vga/Makefile new file mode 100644 index 0000000..8208759 --- /dev/null +++ b/src/base/dev/vga/Makefile @@ -0,0 +1,18 @@ +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf + +# +# This is the Makefile for the video-subdirectory of the DOS-emulator +# for Linux. + +ifdef USE_SVGALIB +VCFILES = svgalib.c +endif + +SFILES = vesabios_pm.S vesabios.S +CFILES = miscemu.c vgaemu.c vesa.c dacemu.c attremu.c seqemu.c crtcemu.c \ + gfxemu.c hercemu.c vgafonts.c + +all: lib + +include $(SRCPATH)/Makefile.common diff --git a/src/base/dev/vga/attremu.c b/src/base/dev/vga/attremu.c new file mode 100644 index 0000000..5dcb44c --- /dev/null +++ b/src/base/dev/vga/attremu.c @@ -0,0 +1,394 @@ +/* + * attremu.c + * + * Attribute controller emulator for VGAEmu + * + * Copyright (C) 1995 1996, Erik Mouw and Arjan Filius + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * email: J.A.K.Mouw@et.tudelft.nl, I.A.Filius@et.tudelft.nl + * + * + * This code emulates the Atrribute Controller for VGAemu. + * The Attribute Controller is part of the VGA (Video Graphics Array, + * a video adapter for IBM compatible PC's). + * + * For an excellent reference to programming SVGA cards see Finn Thgersen's + * VGADOC4, available at http://www.datashopper.dk/~finth + * + * VGADOC says about the attribute controller: + * Port 3C0h is special in that it is both address and data-write register. + * Data reads happen from port 3C1h. An internal flip-flop remembers whether + * it is currently acting as an address or data register. + * Reading port 3DAh will reset the flip-flop to address mode. + * + * What the DOC does not say, but my card does: + * - index reg, bit 5 affects only the palette regs (index < 16) (Not even the + * overscan color reg!) + * - a read from an inaccessible reg (or a nonexistent reg) returns the index reg + * - all unspecified bits are zero and cannot be set + * -- sw + * + * DANG_BEGIN_MODULE + * + * REMARK + * The Attribute Controller emulator for VGAemu. + * /REMARK + * + * DANG_END_MODULE + * + * DANG_BEGIN_CHANGELOG + * + * 1998/09/20: Added proper init values for all graphics modes. + * -- sw (Steffen Winterfeldt ) + * + * 1998/10/25: Restructured code, removed unnecessary parts, added some code. + * The emulation is now (as far as I could test) identical to my VGA chip's + * controller (S3 968). + * -- sw + * + * 1999/01/05: Moved Attr_get_input_status_1() into a separate file (miscemu.c). + * -- sw + * + * DANG_END_CHANGELOG + */ + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * some configurable options + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Debug level for the Attribute Controller. + * 0 - normal / 1 - useful / 2 - too much + */ +#define DEBUG_ATTR 0 + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#define ATTR_INDEX_FLIPFLOP 0 +#define ATTR_DATA_FLIPFLOP 1 + +#define ATTR_MODE_CTL 0x10 +#define ATTR_OVERSCAN 0x11 +#define ATTR_COL_PLANE 0x12 +#define ATTR_HOR_PAN 0x13 +#define ATTR_COL_SELECT 0x14 + +#if !defined True +#define False 0 +#define True 1 +#endif + +#define attr_msg(x...) v_printf("VGAEmu: " x) + +#if DEBUG_ATTR >= 1 +#define attr_deb(x...) v_printf("VGAEmu: " x) +#else +#define attr_deb(x...) +#endif + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include "emu.h" +#include "vgaemu.h" + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +static unsigned char attr_ival[9][ATTR_MAX_INDEX + 1] = { + { /* 0 TEXT, 4 bits */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x0c, 0x00, 0x0f, 0x08, 0x00 + }, + { /* 1 CGA, 2 bits */ + 0x00, 0x13, 0x15, 0x17, 0x02, 0x04, 0x06, 0x07, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x01, 0x00, 0x03, 0x00, 0x00 + }, + { /* 2 CGA: PL1, 1 bit (mode 0x06) */ + 0x00, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, + 0x01, 0x00, 0x01, 0x00, 0x00 + }, + { /* 3 TEXT, mono */ + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x10, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x0e, 0x00, 0x0f, 0x08, 0x00 + }, + { /* 4 EGA: PL4, 4 bits (modes 0x0d, 0x0e) */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x01, 0x00, 0x0f, 0x00, 0x00 + }, + { /* 5 EGA: PL2, 2 bits (mode 0x0f) */ + 0x00, 0x08, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x0b, 0x00, 0x05, 0x00, 0x00 + }, + { /* 6 VGA: PL4, 4 bit */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x01, 0x00, 0x0f, 0x00, 0x00 + }, + { /* 7 VGA: PL1, 1 bit */ + 0x00, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, + 0x01, 0x00, 0x01, 0x00, 0x00 + }, + { /* 8 P8, 8 bit */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x41, 0x00, 0x0f, 0x00, 0x00 + } +}; + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Set unspecified bits to zero. + */ +static unsigned char clear_undef_bits(unsigned char i, unsigned char v) +{ + unsigned char m; + + if(i > ATTR_MAX_INDEX) return v; + + switch(i) { + case ATTR_MODE_CTL : m = ~0x10; break; + case ATTR_HOR_PAN : + case ATTR_COL_SELECT: m = 0x0f; break; + default : m = 0x3f; + } + + return v & m; +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * DANG_BEGIN_FUNCTION Attr_init + * + * Initializes the attribute controller. + * This is an interface function. + * + * DANG_END_FUNCTION + * + */ +void Attr_init(void) +{ + int i = 0, j; + + switch(vga.VGA_mode) { + case 0x06: i = 2; break; + case 0x0d: + case 0x0e: i = 4; break; + case 0x0f: i = 5; break; + default: + switch(vga.color_bits) { + case 1: if(vga.mode_type == PL1) i = 7; break; + case 2: if(vga.mode_type == CGA) i = 1; break; + case 4: if(vga.mode_type == TEXT) i = 0; + if(vga.mode_type == TEXT_MONO) i = 3; + if(vga.mode_type == PL4) i = 6; + break; + case 8: + case 15: + case 16: + case 24: + case 32: i = 8; + } + } + + for(j = 0; j <= ATTR_MAX_INDEX; j++) { + vga.attr.data[j] = attr_ival[i][j]; + vga.attr.dirty[j] = True; + } + + vga.color_modified = True; + vga.attr.index = 0; + vga.attr.cpu_video = 0x20; + vga.attr.flipflop = ATTR_INDEX_FLIPFLOP; + + attr_msg("Attr_init done\n"); +} + + +/* + * DANG_BEGIN_FUNCTION Attr_get_entry + * + * Directly reads the Attribute Controller's registers. + * This is an interface function. + * + * DANG_END_FUNCTION + * + */ +unsigned char Attr_get_entry(unsigned char index) +{ + unsigned char u; + + u = index <= ATTR_MAX_INDEX ? vga.attr.data[index] : 0xff; + + attr_deb("Attr_get_entry: data[0x%02x] = 0x%02x\n", (unsigned) index, (unsigned) u); + + return u; +} + + +/* + * DANG_BEGIN_FUNCTION Attr_set_entry + * + * Directly sets the Attribute Controller's registers. + * This is an interface function. + * + * DANG_END_FUNCTION + * + */ +void Attr_set_entry(unsigned char index, unsigned char value) +{ + unsigned char old_cpu_video = vga.attr.cpu_video; + attr_deb("Attr_set_entry: data[0x%02x] = 0x%02x\n", (unsigned) index, (unsigned) value); + + if(index > ATTR_MAX_INDEX) return; + + vga.attr.index = index; + vga.attr.cpu_video = 0; + vga.attr.flipflop = ATTR_DATA_FLIPFLOP; + Attr_write_value(value); + vga.attr.cpu_video = old_cpu_video; +} + +/* + * DANG_BEGIN_FUNCTION Attr_read_value + * + * Emulates reads from the attribute controller. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +unsigned char Attr_read_value(void) +{ + unsigned i = vga.attr.index; + unsigned char uc = i | vga.attr.cpu_video; + + if(i <= ATTR_MAX_INDEX && (vga.attr.cpu_video == 0 ||i > 15)) { + uc = vga.attr.data[i]; + + attr_deb("Attr_read_value: attr[0x%02x] = 0x%02x\n", i, (unsigned) uc); + } + else { + attr_deb("Attr_read_value: data reg inaccessible, index = 0x%02x\n", (unsigned) uc); + } + + return uc; +} + + +/* + * DANG_BEGIN_FUNCTION Attr_write_value + * + * Emulates writes to attribute controller combined index and data + * register. Read VGADOC for details. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +void Attr_write_value(unsigned char data) +{ + unsigned i, j; + + if(vga.attr.flipflop == ATTR_INDEX_FLIPFLOP) { + vga.attr.flipflop = ATTR_DATA_FLIPFLOP; + + vga.attr.index = data & 0x1f; + vga.attr.cpu_video = data & 0x20; + + attr_deb( + "Attr_write_value: index = 0x%02x\n", + (unsigned) (vga.attr.index | vga.attr.cpu_video) + ); + + attr_deb("Attr_write_value: %svideo access\n", vga.attr.cpu_video ? "" : "no "); + + i = vga.config.video_off; + vga.config.video_off = (vga.config.video_off & ~1) + (((vga.attr.cpu_video >> 5) & 1) ^ 1); + if(i != vga.config.video_off) { + attr_deb("Attr_write_value: video signal turned %s\n", vga.config.video_off ? "off" : "on"); + } + + } + else { /* Attr_flipflop == ATTR_DATA_FLIPFLOP */ + vga.attr.flipflop = ATTR_INDEX_FLIPFLOP; + + i = vga.attr.index; + data = clear_undef_bits(i, data); + if(i <= ATTR_MAX_INDEX && (vga.attr.cpu_video == 0 || i > 15)) { + if (vga.attr.data[i] != data) { + vga.attr.data[i] = data; + vga.attr.dirty[i] = True; + vga.color_modified = True; + if (i == ATTR_COL_PLANE) + vgaemu_adj_cfg(CFG_MODE_CONTROL, 0); + } + if(i == ATTR_MODE_CTL || i == ATTR_COL_SELECT) { + for(j = 0; j < 16; j++) vga.attr.dirty[j] = True; + } + /* bits: 0x04 - line graphics copy 8th->9th column... */ + /* 0x02 - mono mode / 0x01 - graphics mode */ + /* 0x20 - use line compare pixel panning... */ + /* 0x40 - VGA 8bit colors (not 4bit) */ + /* 0x80 - VGA 16*16 attribs*pages (not 64*4) */ + if ((i == ATTR_MODE_CTL) && (data & 0x20)) + v_printf("Horizontal panning with line compare NOT IMPLEMENTED\n"); + if ((i == ATTR_MODE_CTL) && (data & 0x08)) + v_printf("Blinking ignored, will use 16 color background\n"); + attr_deb("Attr_write_value: attr[0x%02x] = 0x%02x\n", i, (unsigned) data); + } + else { + attr_deb("Attr_write_value: data ignored\n"); + } + } +} + + +/* + * DANG_BEGIN_FUNCTION Attr_get_index + * + * Returns the current index of the attribute controller. + * This is a hardware emulation function, though in fact this function + * is undefined in a real attribute controller. + * Well, it is exactly what my VGA board (S3) does. -- sw + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +unsigned char Attr_get_index(void) +{ + unsigned char uc = vga.attr.index | vga.attr.cpu_video; + + attr_deb("Attr_get_index: index = 0x%02x\n", (unsigned) uc); + + return uc; +} + + diff --git a/src/base/dev/vga/crtcemu.c b/src/base/dev/vga/crtcemu.c new file mode 100644 index 0000000..59c2f89 --- /dev/null +++ b/src/base/dev/vga/crtcemu.c @@ -0,0 +1,341 @@ +/* + * DANG_BEGIN_MODULE + * + * REMARK + * The VGA CRT Controller emulator for VGAEmu. + * /REMARK + * DANG_END_MODULE + * + * DANG_BEGIN_CHANGELOG + * + * 1999/01/05: Correct initial values for standard VGA modes. + * -- sw (Steffen Winterfeldt ) + * + * DANG_END_CHANGELOG + * + */ + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * some configurable options + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Debug level for the CRT Controller. + * 0 - normal / 1 - useful / 2 - too much + */ +#define DEBUG_CRTC 1 + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#if !defined True +#define False 0 +#define True 1 +#endif + +#define crtc_msg(x...) v_printf("VGAEmu: " x) + +#if DEBUG_CRTC >= 1 +#define crtc_deb(x...) v_printf("VGAEmu: " x) +#else +#define crtc_deb(x...) +#endif + +#if DEBUG_CRTC >= 2 +#define crtc_deb2(x...) v_printf("VGAEmu: " x) +#else +#define crtc_deb2(x...) +#endif + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include "emu.h" +#include "vgaemu.h" +#include "video.h" +#include "timers.h" // for reset_idle() +#include "memory.h" + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +static unsigned char crtc_ival[16][CRTC_MAX_INDEX + 1] = { + {0x2d, 0x27, 0x28, 0x90, 0x2b, 0xa0, 0xbf, 0x1f, 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x14, 0x1f, 0x96, 0xb9, 0xa3, 0xff}, + {0x2d, 0x27, 0x28, 0x90, 0x2b, 0xa0, 0xbf, 0x1f, 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x14, 0x1f, 0x96, 0xb9, 0xa3, 0xff}, + {0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x28, 0x1f, 0x96, 0xb9, 0xa3, 0xff}, + {0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x28, 0x1f, 0x96, 0xb9, 0xa3, 0xff}, + {0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2, 0xff}, + {0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xa2, 0xff}, + {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xc2, 0xff}, + {0x5f, 0x4f, 0x50, 0x82, 0x55, 0x81, 0xbf, 0x1f, 0x00, 0x4f, 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x28, 0x0f, 0x96, 0xb9, 0xa3, 0xff}, + + {0x2d, 0x27, 0x28, 0x90, 0x2b, 0x80, 0xbf, 0x1f, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x14, 0x00, 0x96, 0xb9, 0xe3, 0xff}, + {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x28, 0x00, 0x96, 0xb9, 0xe3, 0xff}, + {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3, 0xff}, + {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x85, 0x5d, 0x28, 0x0f, 0x63, 0xba, 0xe3, 0xff}, + {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xc3, 0xff}, + {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3, 0xff}, + {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0xbf, 0x1f, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x8e, 0x8f, 0x28, 0x40, 0x96, 0xb9, 0xa3, 0xff}, + {0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3, 0xff} +}; + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * DANG_BEGIN_FUNCTION CRTC_init + * + * Initializes the CRT Controller. + * This is an interface function. + * + * DANG_END_FUNCTION + * + */ +void CRTC_init(void) +{ + unsigned i, j = 15; + + if(vga.VGA_mode >= 0 && vga.VGA_mode <= 7) + j = vga.VGA_mode; + else if(vga.VGA_mode >= 0x0d && vga.VGA_mode <= 0x13) + j = vga.VGA_mode - 5; + + for(i = 0; i <= CRTC_MAX_INDEX; i++) vga.crtc.data[i] = crtc_ival[j][i]; + + vga.crtc.index = 0; + vga.crtc.readonly = 0; + + if(j == 15) { + /* adjust crtc values for vesa modes that fit certain conditions */ + if (vga.width < 2048 && vga.width % vga.char_width == 0) + vga.crtc.data[0x1] = vga.crtc.data[0x2] = + vga.width / vga.char_width - 1; + vga.crtc.data[0x14] = vga.char_height - 1; + if (vga.height <= 1024) { + int h = vga.height - 1; + vga.crtc.data[0x12] = vga.crtc.data[0x15] = h & 0xff; + vga.crtc.data[0x7] &= ~0x4a; + vga.crtc.data[0x7] |= ((h & 0x100) >> (8 - 1))|((h & 0x200) >> (9 - 6))| + (h & 0x100) >> (8 - 3); + vga.crtc.data[0x9] &= ~0x20; + vga.crtc.data[0x9] |= (h & 0x200) >> (9 - 5); + if (vga.mode_class == TEXT) { + vga.crtc.data[0x9] &= ~0x1f; + vga.crtc.data[0x9] |= (vga.char_height - 1) & 0x1f; + } + } + if (vga.scan_len < 2048 && vga.color_bits == 8) { + vga.crtc.data[0x13] = vga.scan_len / 8; + vga.crtc.data[0x14] |= 0x40; + } else if (vga.scan_len < 1024 && vga.mode_class == TEXT) { + vga.crtc.data[0x13] = vga.scan_len / 4; + vga.crtc.data[0x17] = 0xa3; + } else if (vga.scan_len < 512) { /* 4 bpp */ + vga.crtc.data[0x13] = vga.scan_len / 2; + } + } + + crtc_msg("CRTC_init done\n"); +} + + +void CRTC_set_index(unsigned char index) +{ + crtc_deb2( + "CRTC_set_index: 0x%02x%s\n", + (unsigned) index, + index > CRTC_MAX_INDEX ? " (too large)" : "" + ); + + vga.crtc.index = index; +} + + +unsigned char CRTC_get_index(void) +{ + crtc_deb2("CRTC_get_index: 0x%02x\n", (unsigned) vga.crtc.index); + + return vga.crtc.index; +} + + +void CRTC_write_value(unsigned char data) +{ +#define NEWBITS(a) (delta & (a)) + unsigned u = data, u1, delta, ind = vga.crtc.index; + + if(ind > CRTC_MAX_INDEX) { + crtc_deb("CRTC_write_value: data (0x%02x) ignored\n", u); + return; + } + + crtc_deb2("CRTC_write_value: crtc[0x%02x] = 0x%02x\n", ind, u); + + untrigger_idle(); + + if(vga.crtc.readonly) { + /* read only regs 00h-07h with the exception of bit4 in 07h */ + if (ind <= 6) + return; + if (ind == 7) + data = (vga.crtc.data[ind] & 0xef) | (data & 0x10); + } + + delta = vga.crtc.data[ind] ^ data; + if(!delta) return; + vga.crtc.data[ind] = data; + + switch(ind) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + if(NEWBITS(0xFF)) { + vgaemu_adj_cfg(CFG_CRTC_WIDTH, 0); + } + crtc_deb("CRTC_write_value: crtc[0x%02x] = 0x%02x (guessed)\n", ind, u); + break; + + case 0x06: + if(NEWBITS(0xFF)) { + vgaemu_adj_cfg(CFG_CRTC_HEIGHT, 0); + } + crtc_deb("CRTC_write_value: crtc[0x%02x] = 0x%02x (guessed)\n", ind, u); + break; + + case 0x07: + if(NEWBITS(0x10)) { + vgaemu_adj_cfg(CFG_CRTC_LINE_COMPARE, 0); + } + if(NEWBITS(0xEF)) { + vgaemu_adj_cfg(CFG_CRTC_HEIGHT, 0); + } + crtc_deb("CRTC_write_value: crtc[0x%02x] = 0x%02x (guessed)\n", ind, u); + break; + + case 0x09: + if(NEWBITS(0x40)) { + vgaemu_adj_cfg(CFG_CRTC_LINE_COMPARE, 0); + } + if(NEWBITS(0xBF)) { + vgaemu_adj_cfg(CFG_CRTC_HEIGHT, 0); + } + crtc_deb("CRTC_write_value: crtc[0x%02x] = 0x%02x (guessed)\n", ind, u); + break; + + case 0x0a: /* cursor shape start */ + CURSOR_START(vga.crtc.cursor_shape) = u; + crtc_deb("CRTC_write_value: cursor shape start = 0x%02x\n", u); + break; + + case 0x0b: /* cursor shape end */ + CURSOR_END(vga.crtc.cursor_shape) = u; + crtc_deb("CRTC_write_value: cursor shape end = 0x%02x\n", u); + break; + + case 0x0c: /* Start Address High */ + /* these shifts involving vga.crtc.addr_mode should really be rotations, + depending on mode control bit 5 */ + vga.display_start = (vga.crtc.data[0x0d] + (u << 8)) << vga.crtc.addr_mode; + crtc_deb("CRTC_write_value: Start Address = 0x%04x, high changed\n", vga.display_start); + vga.reconfig.mem = 1; + break; + + case 0x0d: /* Start Address Low */ + vga.display_start = (u + (vga.crtc.data[0x0c] << 8)) << vga.crtc.addr_mode; + /* this shift should really be a rotation, depending on mode control bit 5 */ + crtc_deb("CRTC_write_value: Start Address = 0x%04x, low changed\n", vga.display_start); + vga.reconfig.mem = 1; + break; + + case 0x0e: /* Cursor Location High */ + vga.crtc.cursor_location = (vga.crtc.data[0x0f] + (u << 8)) << vga.crtc.addr_mode; + crtc_deb("CRTC_write_value: Cursor Location = 0x%04x\n", vga.crtc.cursor_location); + break; + + case 0x0f: /* Cursor Location Low */ + vga.crtc.cursor_location = (u + (vga.crtc.data[0x0e] << 8)) << vga.crtc.addr_mode; + crtc_deb("CRTC_write_value: Cursor Location = 0x%04x\n", vga.crtc.cursor_location); + break; + + case 0x11: + if(NEWBITS(0x80)) { + vga.crtc.readonly = (data >= 0x80); + } + if(NEWBITS(0x7F)) { + vgaemu_adj_cfg(CFG_CRTC_HEIGHT, 0); + } + crtc_deb("CRTC_write_value: crtc[0x%02x] = 0x%02x (guessed)\n", ind, u); + break; + + case 0x10: + case 0x12: + if(NEWBITS(0xFF)) { + vgaemu_adj_cfg(CFG_CRTC_HEIGHT, 0); + } + crtc_deb("CRTC_write_value: crtc[0x%02x] = 0x%02x (guessed)\n", ind, u); + break; + + case 0x13: /* Number of bytes in a scanline */ + if(NEWBITS(0xFF)) { + vgaemu_adj_cfg(CFG_CRTC_ADDR_MODE, 0); + } + crtc_deb("CRTC_write_value: crtc[0x%02x] = 0x%02x (guessed)\n", ind, u); + break; + + case 0x14: /* Underline Location */ + if(NEWBITS(0x40)) { + vgaemu_adj_cfg(CFG_CRTC_ADDR_MODE, 0); + } + break; + + case 0x15: + case 0x16: + if(NEWBITS(0xFF)) { + vgaemu_adj_cfg(CFG_CRTC_HEIGHT, 0); + } + crtc_deb("CRTC_write_value: crtc[0x%02x] = 0x%02x (guessed)\n", ind, u); + break; + + case 0x17: /* Mode Control */ + if(NEWBITS(0x03)) { + crtc_deb("CRTC_write_value: crtc[0x%02x] = 0x%02x (guessed)\n", ind, u); + vgaemu_adj_cfg(CFG_MODE_CONTROL, 0); + } + if(NEWBITS(0x40)) { + vgaemu_adj_cfg(CFG_CRTC_ADDR_MODE, 0); + } + if(NEWBITS(0x80)) { + crtc_deb("CRTC_write_value: %svideo access\n", (data & 0x80) ? "" : "no "); + + u1 = vga.config.video_off; + vga.config.video_off = (vga.config.video_off & ~4) + (((data >> 5) & 4) ^ 4); + if(u1 != vga.config.video_off) { + crtc_deb("CRTC_write_value: video signal turned %s\n", vga.config.video_off ? "off" : "on"); + } + } + break; + + case 0x18: /* line compare */ + if(NEWBITS(0xFF)) { + vgaemu_adj_cfg(CFG_CRTC_LINE_COMPARE, 0); + } + break; + + default: + crtc_deb("CRTC_write_value: crtc[0x%02x] = 0x%02x (ignored)\n", ind, u); + } +} + + +unsigned char CRTC_read_value(void) +{ + unsigned char uc; + + uc = vga.crtc.index > CRTC_MAX_INDEX ? 0x00 : vga.crtc.data[vga.crtc.index]; + + crtc_deb2("CRTC_read_value: crtc[0x%02x] = 0x%02x\n", (unsigned) vga.crtc.index, (unsigned) uc); + + return uc; +} + diff --git a/src/base/dev/vga/dacemu.c b/src/base/dev/vga/dacemu.c new file mode 100644 index 0000000..43ef54f --- /dev/null +++ b/src/base/dev/vga/dacemu.c @@ -0,0 +1,558 @@ +/* + * dacemu.c + * + * DAC emulator for VGAemu + * + * Copyright (C) 1995 1996, Erik Mouw and Arjan Filius + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * email: J.A.K.Mouw@et.tudelft.nl, I.A.Filius@et.tudelft.nl + * + * + * This code emulates the DAC (Digital to Analog Converter) on a VGA + * (Video Graphics Array, a video adapter for IBM PC's) for VGAemu. + * + * For an excellent reference to programming SVGA cards see Finn Thgersen's + * VGADOC4, available at http://www.datashopper.dk/~finth + * + * + * DANG_BEGIN_MODULE + * + * REMARK + * The DAC emulator for DOSEMU. + * /REMARK + * + * DANG_END_MODULE + * + * DANG_BEGIN_CHANGELOG + * + * 1998/09/20: Added proper DAC init values for all graphics modes. + * -- sw (Steffen Winterfeldt ) + * + * 1998/10/25: Cleaned up the interface, removed unnecessary parts. + * Working PEL mask support. + * -- sw + * + * 1998/11/01: Reworked DAC init code. + * -- sw + * + * 1998/12/12: Added RGB to gray scale conversion. + * -- sw + * + * DANG_END_CHANGELOG + */ + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * some configurable options + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Debug level for the DAC. + * 0 - normal / 1 - useful / 2 - too much + */ +#define DEBUG_DAC 0 + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#define DAC_READ_MODE 3 +#define DAC_WRITE_MODE 0 + +#if !defined True +#define False 0 +#define True 1 +#endif + +#define dac_msg(x...) v_printf("VGAEmu: " x) + +#if DEBUG_DAC >= 1 +#define dac_deb(x...) v_printf("VGAEmu: " x) +#else +#define dac_deb(x...) +#endif + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include "emu.h" +#include "vgaemu.h" + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +typedef struct { unsigned char r, g, b; } _DAC_entry; + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +static _DAC_entry dac_vga[256] = { + {0x00, 0x00, 0x00}, {0x00, 0x00, 0x2a}, {0x00, 0x2a, 0x00}, {0x00, 0x2a, 0x2a}, + {0x2a, 0x00, 0x00}, {0x2a, 0x00, 0x2a}, {0x2a, 0x15, 0x00}, {0x2a, 0x2a, 0x2a}, + {0x15, 0x15, 0x15}, {0x15, 0x15, 0x3f}, {0x15, 0x3f, 0x15}, {0x15, 0x3f, 0x3f}, + {0x3f, 0x15, 0x15}, {0x3f, 0x15, 0x3f}, {0x3f, 0x3f, 0x15}, {0x3f, 0x3f, 0x3f}, + {0x00, 0x00, 0x00}, {0x05, 0x05, 0x05}, {0x08, 0x08, 0x08}, {0x0b, 0x0b, 0x0b}, + {0x0e, 0x0e, 0x0e}, {0x11, 0x11, 0x11}, {0x14, 0x14, 0x14}, {0x18, 0x18, 0x18}, + {0x1c, 0x1c, 0x1c}, {0x20, 0x20, 0x20}, {0x24, 0x24, 0x24}, {0x28, 0x28, 0x28}, + {0x2d, 0x2d, 0x2d}, {0x32, 0x32, 0x32}, {0x38, 0x38, 0x38}, {0x3f, 0x3f, 0x3f}, + {0x00, 0x00, 0x3f}, {0x10, 0x00, 0x3f}, {0x1f, 0x00, 0x3f}, {0x2f, 0x00, 0x3f}, + {0x3f, 0x00, 0x3f}, {0x3f, 0x00, 0x2f}, {0x3f, 0x00, 0x1f}, {0x3f, 0x00, 0x10}, + {0x3f, 0x00, 0x00}, {0x3f, 0x10, 0x00}, {0x3f, 0x1f, 0x00}, {0x3f, 0x2f, 0x00}, + {0x3f, 0x3f, 0x00}, {0x2f, 0x3f, 0x00}, {0x1f, 0x3f, 0x00}, {0x10, 0x3f, 0x00}, + {0x00, 0x3f, 0x00}, {0x00, 0x3f, 0x10}, {0x00, 0x3f, 0x1f}, {0x00, 0x3f, 0x2f}, + {0x00, 0x3f, 0x3f}, {0x00, 0x2f, 0x3f}, {0x00, 0x1f, 0x3f}, {0x00, 0x10, 0x3f}, + {0x1f, 0x1f, 0x3f}, {0x27, 0x1f, 0x3f}, {0x2f, 0x1f, 0x3f}, {0x37, 0x1f, 0x3f}, + {0x3f, 0x1f, 0x3f}, {0x3f, 0x1f, 0x37}, {0x3f, 0x1f, 0x2f}, {0x3f, 0x1f, 0x27}, + + {0x3f, 0x1f, 0x1f}, {0x3f, 0x27, 0x1f}, {0x3f, 0x2f, 0x1f}, {0x3f, 0x37, 0x1f}, + {0x3f, 0x3f, 0x1f}, {0x37, 0x3f, 0x1f}, {0x2f, 0x3f, 0x1f}, {0x27, 0x3f, 0x1f}, + {0x1f, 0x3f, 0x1f}, {0x1f, 0x3f, 0x27}, {0x1f, 0x3f, 0x2f}, {0x1f, 0x3f, 0x37}, + {0x1f, 0x3f, 0x3f}, {0x1f, 0x37, 0x3f}, {0x1f, 0x2f, 0x3f}, {0x1f, 0x27, 0x3f}, + {0x2d, 0x2d, 0x3f}, {0x31, 0x2d, 0x3f}, {0x36, 0x2d, 0x3f}, {0x3a, 0x2d, 0x3f}, + {0x3f, 0x2d, 0x3f}, {0x3f, 0x2d, 0x3a}, {0x3f, 0x2d, 0x36}, {0x3f, 0x2d, 0x31}, + {0x3f, 0x2d, 0x2d}, {0x3f, 0x31, 0x2d}, {0x3f, 0x36, 0x2d}, {0x3f, 0x3a, 0x2d}, + {0x3f, 0x3f, 0x2d}, {0x3a, 0x3f, 0x2d}, {0x36, 0x3f, 0x2d}, {0x31, 0x3f, 0x2d}, + {0x2d, 0x3f, 0x2d}, {0x2d, 0x3f, 0x31}, {0x2d, 0x3f, 0x36}, {0x2d, 0x3f, 0x3a}, + {0x2d, 0x3f, 0x3f}, {0x2d, 0x3a, 0x3f}, {0x2d, 0x36, 0x3f}, {0x2d, 0x31, 0x3f}, + {0x00, 0x00, 0x1c}, {0x07, 0x00, 0x1c}, {0x0e, 0x00, 0x1c}, {0x15, 0x00, 0x1c}, + {0x1c, 0x00, 0x1c}, {0x1c, 0x00, 0x15}, {0x1c, 0x00, 0x0e}, {0x1c, 0x00, 0x07}, + {0x1c, 0x00, 0x00}, {0x1c, 0x07, 0x00}, {0x1c, 0x0e, 0x00}, {0x1c, 0x15, 0x00}, + {0x1c, 0x1c, 0x00}, {0x15, 0x1c, 0x00}, {0x0e, 0x1c, 0x00}, {0x07, 0x1c, 0x00}, + {0x00, 0x1c, 0x00}, {0x00, 0x1c, 0x07}, {0x00, 0x1c, 0x0e}, {0x00, 0x1c, 0x15}, + {0x00, 0x1c, 0x1c}, {0x00, 0x15, 0x1c}, {0x00, 0x0e, 0x1c}, {0x00, 0x07, 0x1c}, + + {0x0e, 0x0e, 0x1c}, {0x11, 0x0e, 0x1c}, {0x15, 0x0e, 0x1c}, {0x18, 0x0e, 0x1c}, + {0x1c, 0x0e, 0x1c}, {0x1c, 0x0e, 0x18}, {0x1c, 0x0e, 0x15}, {0x1c, 0x0e, 0x11}, + {0x1c, 0x0e, 0x0e}, {0x1c, 0x11, 0x0e}, {0x1c, 0x15, 0x0e}, {0x1c, 0x18, 0x0e}, + {0x1c, 0x1c, 0x0e}, {0x18, 0x1c, 0x0e}, {0x15, 0x1c, 0x0e}, {0x11, 0x1c, 0x0e}, + {0x0e, 0x1c, 0x0e}, {0x0e, 0x1c, 0x11}, {0x0e, 0x1c, 0x15}, {0x0e, 0x1c, 0x18}, + {0x0e, 0x1c, 0x1c}, {0x0e, 0x18, 0x1c}, {0x0e, 0x15, 0x1c}, {0x0e, 0x11, 0x1c}, + {0x14, 0x14, 0x1c}, {0x16, 0x14, 0x1c}, {0x18, 0x14, 0x1c}, {0x1a, 0x14, 0x1c}, + {0x1c, 0x14, 0x1c}, {0x1c, 0x14, 0x1a}, {0x1c, 0x14, 0x18}, {0x1c, 0x14, 0x16}, + {0x1c, 0x14, 0x14}, {0x1c, 0x16, 0x14}, {0x1c, 0x18, 0x14}, {0x1c, 0x1a, 0x14}, + {0x1c, 0x1c, 0x14}, {0x1a, 0x1c, 0x14}, {0x18, 0x1c, 0x14}, {0x16, 0x1c, 0x14}, + {0x14, 0x1c, 0x14}, {0x14, 0x1c, 0x16}, {0x14, 0x1c, 0x18}, {0x14, 0x1c, 0x1a}, + {0x14, 0x1c, 0x1c}, {0x14, 0x1a, 0x1c}, {0x14, 0x18, 0x1c}, {0x14, 0x16, 0x1c}, + {0x00, 0x00, 0x10}, {0x04, 0x00, 0x10}, {0x08, 0x00, 0x10}, {0x0c, 0x00, 0x10}, + {0x10, 0x00, 0x10}, {0x10, 0x00, 0x0c}, {0x10, 0x00, 0x08}, {0x10, 0x00, 0x04}, + {0x10, 0x00, 0x00}, {0x10, 0x04, 0x00}, {0x10, 0x08, 0x00}, {0x10, 0x0c, 0x00}, + {0x10, 0x10, 0x00}, {0x0c, 0x10, 0x00}, {0x08, 0x10, 0x00}, {0x04, 0x10, 0x00}, + + {0x00, 0x10, 0x00}, {0x00, 0x10, 0x04}, {0x00, 0x10, 0x08}, {0x00, 0x10, 0x0c}, + {0x00, 0x10, 0x10}, {0x00, 0x0c, 0x10}, {0x00, 0x08, 0x10}, {0x00, 0x04, 0x10}, + {0x08, 0x08, 0x10}, {0x0a, 0x08, 0x10}, {0x0c, 0x08, 0x10}, {0x0e, 0x08, 0x10}, + {0x10, 0x08, 0x10}, {0x10, 0x08, 0x0e}, {0x10, 0x08, 0x0c}, {0x10, 0x08, 0x0a}, + {0x10, 0x08, 0x08}, {0x10, 0x0a, 0x08}, {0x10, 0x0c, 0x08}, {0x10, 0x0e, 0x08}, + {0x10, 0x10, 0x08}, {0x0e, 0x10, 0x08}, {0x0c, 0x10, 0x08}, {0x0a, 0x10, 0x08}, + {0x08, 0x10, 0x08}, {0x08, 0x10, 0x0a}, {0x08, 0x10, 0x0c}, {0x08, 0x10, 0x0e}, + {0x08, 0x10, 0x10}, {0x08, 0x0e, 0x10}, {0x08, 0x0c, 0x10}, {0x08, 0x0a, 0x10}, + {0x0b, 0x0b, 0x10}, {0x0c, 0x0b, 0x10}, {0x0d, 0x0b, 0x10}, {0x0f, 0x0b, 0x10}, + {0x10, 0x0b, 0x10}, {0x10, 0x0b, 0x0f}, {0x10, 0x0b, 0x0d}, {0x10, 0x0b, 0x0c}, + {0x10, 0x0b, 0x0b}, {0x10, 0x0c, 0x0b}, {0x10, 0x0d, 0x0b}, {0x10, 0x0f, 0x0b}, + {0x10, 0x10, 0x0b}, {0x0f, 0x10, 0x0b}, {0x0d, 0x10, 0x0b}, {0x0c, 0x10, 0x0b}, + {0x0b, 0x10, 0x0b}, {0x0b, 0x10, 0x0c}, {0x0b, 0x10, 0x0d}, {0x0b, 0x10, 0x0f}, + {0x0b, 0x10, 0x10}, {0x0b, 0x0f, 0x10}, {0x0b, 0x0d, 0x10}, {0x0b, 0x0c, 0x10}, + {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00} +}; + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * DANG_BEGIN_FUNCTION DAC_init + * + * Initializes the DAC. + * It depends on a correct value in vga.pixel_size. This function should be + * called during VGA mode initialization. + * This is an interface function. + * + * DANG_END_FUNCTION + * + */ +void DAC_init(void) +{ + DAC_entry dac_zero = {True, 0, 0, 0}, de = dac_zero; + int i; + + if(vga.pixel_size <= 4) { + if(vga.mode_type == TEXT_MONO) { /* mono modes */ + for(i = 0; i < 64; i++) { + switch((i >> 3) & 3) { + case 0: de.r = de.g = de.b = 0x00; break; + case 1: + case 2: de.r = de.g = de.b = 0x2a; break; + case 3: de.r = de.g = de.b = 0x3f; break; + } + vga.dac.rgb[i] = de; + } + } + else if( + vga.VGA_mode == 4 || vga.VGA_mode == 5 || vga.VGA_mode == 6 || + vga.VGA_mode == 13 || vga.VGA_mode == 14 + ) { + for(i = 0; i < 64; i++) { + de.r = (((i & 4) >> 1) + ((i & 0x10) >> 4)) * 0x15; + de.g = (((i & 2) >> 0) + ((i & 0x10) >> 4)) * 0x15; + de.b = (((i & 1) << 1) + ((i & 0x10) >> 4)) * 0x15; + if((i & 0x17) == 6) de.g = 0x15; + vga.dac.rgb[i] = de; + } + } + else { + for(i = 0; i < 64; i++) { + de.r = (((i & 4) >> 1) + ((i & 0x20) >> 5)) * 0x15; + de.g = (((i & 2) >> 0) + ((i & 0x10) >> 4)) * 0x15; + de.b = (((i & 1) << 1) + ((i & 0x08) >> 3)) * 0x15; + vga.dac.rgb[i] = de; + } + } + while(i < 256) vga.dac.rgb[i++] = dac_zero; + } + else { + for(i = 0; i < 256; i++) { + de.r = dac_vga[i].r; + de.g = dac_vga[i].g; + de.b = dac_vga[i].b; + vga.dac.rgb[i] = de; + } + } + + vga.dac.bits = 6; + vga.color_modified = True; + vga.dac.pel_index = 'r'; + vga.dac.pel_mask = 0xff; + vga.dac.state = DAC_READ_MODE; + vga.dac.read_index = 0; + vga.dac.write_index = 0; + + dac_msg("DAC_init done\n"); +} + + +/* + * DANG_BEGIN_FUNCTION DAC_set_width + * + * Sets the DAC width. Typical values are 6 or 8 bits. + * In theory, we support other values as well (untested). + * This is an interface function. + * + * DANG_END_FUNCTION + * + */ +void DAC_set_width(unsigned bits) +{ + int i; + + if(bits > 8) bits = 8; + if(bits < 4) bits = 4; /* it's no use to allow other values than 6 or 8, but anyway... */ + + dac_deb( + "DAC_set_width: width = %u bits%s\n", + bits, vga.dac.bits == bits ? " (unchanged)" : "" + ); + + if(vga.dac.bits != bits) { + vga.reconfig.dac = 1; + vga.dac.bits = bits; + vga.color_modified = True; + for(i = 0; i < 256; i++) vga.dac.rgb[i].dirty = True; /* index = dirty flag ! */ + } +} + + +/* + * DANG_BEGIN_FUNCTION DAC_get_entry + * + * Returns a complete DAC entry (r, g, b). + * Don't forget to set DAC_entry.index first! + * This is an interface function. + * + * DANG_END_FUNCTION + * + */ +void DAC_get_entry(DAC_entry *entry, int index) +{ + *entry = vga.dac.rgb[index]; + + dac_deb( + "DAC_get_entry: dac.rgb[0x%02x] = 0x%02x 0x%02x 0x%02x\n", + index, entry->r, entry->g, entry->b + ); +} + + +/* + * DANG_BEGIN_FUNCTION DAC_set_entry + * + * Sets a complete DAC entry (r,g,b). + * This is an interface function. + * + * DANG_END_FUNCTION + * + */ +void DAC_set_entry(unsigned char index, unsigned char r, unsigned char g, unsigned char b) +{ + unsigned mask = (1 << vga.dac.bits ) - 1; + + r &= mask; g &= mask; b &= mask; + + dac_deb( + "DAC_set_entry: dac.rgb[0x%02x] = 0x%02x 0x%02x 0x%02x\n", + (unsigned) index, (unsigned) r, (unsigned) g, (unsigned) b); + + if( + vga.dac.rgb[index].r != r || + vga.dac.rgb[index].g != g || + vga.dac.rgb[index].b != b + ) { + vga.color_modified = True; + vga.dac.rgb[index].dirty = True; + vga.dac.rgb[index].r = r; + vga.dac.rgb[index].g = g; + vga.dac.rgb[index].b = b; + } +} + + +/* + * DANG_BEGIN_FUNCTION DAC_rgb2gray + * + * Converts a DAC register's RGB values to gray scale. + * This is an interface function. + * + * DANG_END_FUNCTION + * + */ +void DAC_rgb2gray(unsigned char index) +{ + unsigned i, m = (vga.dac.bits << 1) - 1; + + i = 77 * vga.dac.rgb[index].r + + 151 * vga.dac.rgb[index].g + + 28 * vga.dac.rgb[index].b; + + i = (i + 0x80) >> 8; + if(i > m) i = m; + + vga.dac.rgb[index].dirty = True; + vga.dac.rgb[index].r = vga.dac.rgb[index].g = vga.dac.rgb[index].b = i; +} + + +/* + * DANG_BEGIN_FUNCTION DAC_set_read_index + * + * Specifies which palette entry is read. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +void DAC_set_read_index(unsigned char index) +{ + dac_deb("DAC_set_read_index: index = 0x%02x\n", (unsigned) index); + + vga.dac.read_index = index; + vga.dac.pel_index = 'r'; + vga.dac.state = DAC_READ_MODE; + + /* undefined behaviour by Starcon2 (write to 3C9 after 3C7) - clarence */ + vga.dac.write_index = index + 1; +} + + +/* + * DANG_BEGIN_FUNCTION DAC_set_write_index + * + * Specifies which palette entry is written. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +void DAC_set_write_index(unsigned char index) +{ + dac_deb("DAC_set_write_index: index = 0x%02x\n", (unsigned) index); + + vga.dac.write_index = index; + vga.dac.pel_index = 'r'; + vga.dac.state = DAC_WRITE_MODE; +} + + +/* + * DANG_BEGIN_FUNCTION DAC_read_value + * + * Read a value from the DAC. Each read will cycle through the registers for + * red, green and blue. After a ``blue read'' the read index will be + * incremented. Read VGADOC4 if you want to know more about the DAC. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +unsigned char DAC_read_value(void) +{ + unsigned char rv; + +#if DEBUG_DAC >= 1 + char c = vga.dac.pel_index; + unsigned char ri = vga.dac.read_index; +#endif + + switch(vga.dac.pel_index) { + case 'r': + rv = vga.dac.rgb[vga.dac.read_index].r; + vga.dac.pel_index = 'g'; + break; + + case 'g': + rv = vga.dac.rgb[vga.dac.read_index].g; + vga.dac.pel_index = 'b'; + break; + + case 'b': + rv = vga.dac.rgb[vga.dac.read_index].b; + vga.dac.pel_index = 'r'; + vga.dac.read_index++; + + /* observed behaviour on a real system - clarence */ + vga.dac.write_index = vga.dac.read_index + 1; + break; + + default: + dac_msg("DAC_read_value: ERROR: pel_index out of range\n"); + vga.dac.pel_index = 'r'; + rv = 0; + break; + } + + dac_deb( + "DAC_read_value: dac.rgb[0x%02x].%c = 0x%02x\n", + (unsigned) ri, c, (unsigned) rv + ); + + return rv; +} + + +/* + * DANG_BEGIN_FUNCTION DAC_write_value + * + * Write a value to the DAC. Each write will cycle through the registers for + * red, green and blue. After a ``blue write'' the write index will be + * incremented. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +void DAC_write_value(unsigned char value) +{ + value &= (1 << vga.dac.bits) - 1; + + dac_deb( + "DAC_write_value: dac.rgb[0x%02x].%c = 0x%02x\n", + (unsigned) vga.dac.write_index, vga.dac.pel_index, (unsigned) value + ); + + vga.dac.rgb[vga.dac.write_index].dirty = True; /* index = dirty flag ! */ + vga.color_modified = True; + + switch(vga.dac.pel_index) { + case 'r': + vga.dac.rgb[vga.dac.write_index].r = value; + vga.dac.pel_index = 'g'; + break; + + case 'g': + vga.dac.rgb[vga.dac.write_index].g = value; + vga.dac.pel_index = 'b'; + break; + + case 'b': + vga.dac.rgb[vga.dac.write_index].b = value; + vga.dac.pel_index = 'r'; + vga.dac.write_index++; + + /* observed behaviour on a real system - clarence */ + vga.dac.read_index = vga.dac.write_index - 1; + break; + + default: + dac_msg("DAC_write_value: ERROR: pel_index out of range\n"); + vga.dac.pel_index = 'r'; + break; + } +} + + +/* + * DANG_BEGIN_FUNCTION DAC_get_pel_mask + * + * Returns the current PEL mask. Note that changed_vga_colors() already + * applies the PEL mask; so applications should not worry too much about it. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +unsigned char DAC_get_pel_mask(void) +{ + dac_deb("DAC_get_pel_mask: mask = 0x%02x\n", (unsigned) vga.dac.pel_mask); + + return vga.dac.pel_mask; +} + + +/* + * DANG_BEGIN_FUNCTION DAC_set_pel_mask + * + * Sets the PEL mask and marks all DAC entries as dirty. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +void DAC_set_pel_mask(unsigned char mask) +{ + int i; + + dac_deb("DAC_set_pel_mask: mask = 0x%02x\n", (unsigned) mask); + + if(vga.dac.pel_mask != mask) { + vga.dac.pel_mask = mask; + vga.color_modified = True; + for(i = 0; i < 256; i++) { vga.dac.rgb[i].dirty = True; } /* index = dirty flag ! */ + } +} + + +/* + * DANG_BEGIN_FUNCTION DAC_get_state + * + * Returns the current state of the DAC. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +unsigned char DAC_get_state(void) +{ + dac_deb("DAC_get_state: state = 0x%02x\n", vga.dac.state); + + return vga.dac.state; +} + diff --git a/src/base/dev/vga/gfxemu.c b/src/base/dev/vga/gfxemu.c new file mode 100644 index 0000000..f52b516 --- /dev/null +++ b/src/base/dev/vga/gfxemu.c @@ -0,0 +1,333 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * DANG_BEGIN_MODULE + * + * REMARK + * The VGA Graphics Controller emulator for VGAEmu. + * /REMARK + * DANG_END_MODULE + * + * DANG_BEGIN_CHANGELOG + * + * 1999/01/05: Correct initial values; memory mapping regs are emulated + * (as far as possible). + * -- sw (Steffen Winterfeldt ) + * + * 2000/05/10: Apparently gfx.color_dont_care should be initialized + * with 0x0f, not 0x00. + * -- sw + * + * DANG_END_CHANGELOG + * + */ + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * some configurable options + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Debug level for the Graphics Controller. + * 0 - normal / 1 - useful / 2 - too much + */ +#define DEBUG_GFX 1 + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#if !defined True +#define False 0 +#define True 1 +#endif + +#define gfx_msg(x...) v_printf("VGAEmu: " x) + +#if DEBUG_GFX >= 1 +#define gfx_deb(x...) v_printf("VGAEmu: " x) +#else +#define gfx_deb(x...) +#endif + +#if DEBUG_GFX >= 2 +#define gfx_deb2(x...) v_printf("VGAEmu: " x) +#else +#define gfx_deb2(x...) +#endif + +#define NEWBITS(a) ((olddata ^ data) & (a)) + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include "emu.h" +#include "vgaemu.h" + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +static unsigned char gfx_ival[16][2] = { + {0x10, 0x0e}, + {0x10, 0x0e}, + {0x10, 0x0e}, + {0x10, 0x0e}, + {0x30, 0x0f}, + {0x30, 0x0f}, + {0x00, 0x0d}, + {0x10, 0x0a}, + + {0x00, 0x05}, + {0x00, 0x05}, + {0x00, 0x05}, + {0x00, 0x05}, + {0x00, 0x05}, + {0x00, 0x05}, + {0x40, 0x05}, + + {0x00, 0x05} +}; + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * DANG_BEGIN_FUNCTION GFX_init + * + * Initializes the Graphics Controller. + * This is an interface function. + * + * DANG_END_FUNCTION + * + */ +void GFX_init(void) +{ + unsigned i, j = 15; + + if(vga.VGA_mode >= 0 && vga.VGA_mode <= 7) + j = vga.VGA_mode; + else if(vga.VGA_mode >= 0x0d && vga.VGA_mode <= 0x13) + j = vga.VGA_mode - 5; + + for(i = 0; i <= GFX_MAX_INDEX; i++) vga.gfx.data[i] = 0; + vga.gfx.data[5] = gfx_ival[j][0]; + vga.gfx.data[6] = gfx_ival[j][1]; + vga.gfx.data[7] = 0x0f; + vga.gfx.data[8] = 0xff; + + /* initialize non-standard modes */ + if(j == 15) { + if(vga.mode_class == TEXT) { + vga.gfx.data[5] = 0x10; + vga.gfx.data[6] = 0x0e; + } + } + + vga.gfx.index = 0; + + vga.gfx.set_reset = vga.gfx.data[0] & 0x0f; + vga.gfx.enable_set_reset = vga.gfx.data[1] & 0x0f; + vga.gfx.color_compare = vga.gfx.data[2] & 0x0f; + vga.gfx.data_rotate = vga.gfx.data[3] & 0x07; + vga.gfx.raster_op = vga.gfx.data[3] >> 3; + vga.gfx.read_map_select = vga.gfx.data[4] & 0x03; + vga.gfx.write_mode = vga.gfx.data[5] & 0x03; + vga.gfx.read_mode = (vga.gfx.data[5] >> 3) & 0x01; + vga.gfx.color_dont_care = vga.gfx.data[7] & 0x0f; + vga.gfx.bitmask = vga.gfx.data[8]; + + gfx_msg("GFX_init done\n"); +} + + +void GFX_set_index(unsigned char index) +{ + gfx_deb2( + "GFX_set_index: 0x%02x%s\n", + (unsigned) index, + index > GFX_MAX_INDEX ? " (too large)" : "" + ); + + vga.gfx.index = index; +} + + +unsigned char GFX_get_index(void) +{ + gfx_deb2("GFX_get_index: 0x%02x\n", (unsigned) vga.gfx.index); + + return vga.gfx.index; +} + + +void GFX_write_value(unsigned char data) +{ +#if 0 + unsigned u = data; +#endif + unsigned ind = vga.gfx.index; + unsigned char olddata; + + if(ind > GFX_MAX_INDEX) { + gfx_deb("GFX_write_value: data (0x%02x) ignored\n", data); + return; + } + + gfx_deb2("GFX_write_value: gfx[0x%02x] = 0x%02x\n", ind, data); + + if(vga.gfx.data[ind] == data) return; + olddata = vga.gfx.data[ind]; + vga.gfx.data[ind] = data; + + switch(ind) { + case 0x00: /* Set/Reset */ + vga.gfx.set_reset = data & 0x0f; + if(NEWBITS(0x0f)) { + gfx_deb("GFX_write_value: set_reset = 0x%x\n", vga.gfx.set_reset); + } + break; + + case 0x01: /* Enable Set/Reset */ + vga.gfx.enable_set_reset = data & 0x0f; + if(NEWBITS(0x0f)) { + gfx_deb("GFX_write_value: enable_set_reset = 0x%x\n", vga.gfx.enable_set_reset); + } + break; + + case 0x02: /* Color Compare */ + vga.gfx.color_compare = data & 0x0f; + if(NEWBITS(0x0f)) { + gfx_deb("GFX_write_value: color_compare = 0x%x\n", vga.gfx.color_compare); + } + break; + + case 0x03: /* Data Rotate */ + vga.gfx.data_rotate = data & 7; + vga.gfx.raster_op = data >> 3; + if(NEWBITS(0x07)) { + gfx_deb("GFX_write_value: data_rotate = %u\n", vga.gfx.data_rotate); + } + if(NEWBITS(0x0c)) { + gfx_deb("GFX_write_value: raster_op = %u\n", vga.gfx.raster_op); + } + break; + + case 0x04: /* Read Map Select */ + vga.gfx.read_map_select = data & 3; + if(NEWBITS(0x03)) { + gfx_deb("GFX_write_value: read_map_select = %u\n", vga.gfx.read_map_select); + vgaemu_switch_plane(vga.gfx.read_map_select); + } + break; + + case 0x05: /* Mode */ + vga.gfx.write_mode = data & 3; + vga.gfx.read_mode = (data >> 3) & 1; + if(NEWBITS(0x03)) { + gfx_deb("GFX_write_value: write mode = %u\n", vga.gfx.write_mode); + } + if(NEWBITS(0x08)) { + gfx_deb("GFX_write_value: read mode = %u\n", vga.gfx.read_mode); + } + if(NEWBITS(0x10)) { + gfx_deb("GFX_write_value: odd/even = %s (ignored)\n", (data & 0x10) ? "on" : "off"); + } + if(NEWBITS(0x20)) { + gfx_deb("GFX_write_value: CGA 4 color mode = %s\n", (data & 0x20) ? "on" : "off"); + } + if(NEWBITS(0x40)) { + gfx_deb("GFX_write_value: VGA 256 color mode = %s\n", (data & 0x40) ? "on" : "off"); + } + if(NEWBITS(0x60)) { + vgaemu_adj_cfg(CFG_MODE_CONTROL, 0); + } + break; + + case 0x06: /* Miscellaneous */ + if(NEWBITS(0x01)) { + gfx_deb("GFX_write_value: %s mode\n", (data & 1) ? "graphics" : "text"); + vgaemu_adj_cfg(CFG_MODE_CONTROL, 0); + } + if(NEWBITS(0x02)) { + gfx_deb("GFX_write_value: odd/even address mode = %s (ignored)\n", (data & 2) ? "on" : "off"); + } + if(NEWBITS(0x0c)) { + vga.mem.bank = 0; /* reset this? */ + vgaemu_reset_mapping(); + switch((data >> 2) & 3) { + case 0: + if (config.umb_a0) { + error("VGA: avoid touching a000 as it is used for UMB\n"); + vga.mem.bank_pages = 0; + vga.mem.map[VGAEMU_MAP_BANK_MODE].base_page = 0xb0; + } else { + vga.mem.bank_pages = 32; + vga.mem.map[VGAEMU_MAP_BANK_MODE].base_page = 0xa0; + } + break; + + case 1: + if (config.umb_a0) { + error("VGA: avoid touching a000 as it is used for UMB\n"); + vga.mem.bank_pages = 0; + vga.mem.map[VGAEMU_MAP_BANK_MODE].base_page = 0xb0; + } else { + vga.mem.bank_pages = 16; + vga.mem.map[VGAEMU_MAP_BANK_MODE].base_page = 0xa0; + } + break; + + case 2: + if (config.umb_b0) { + error("VGA: avoid touching b000 as it is used for UMB\n"); + vga.mem.bank_pages = 0; + vga.mem.map[VGAEMU_MAP_BANK_MODE].base_page = 0xb8; + } else { + vga.mem.bank_pages = 8; + vga.mem.map[VGAEMU_MAP_BANK_MODE].base_page = 0xb0; + } + break; + + case 3: + vga.mem.bank_pages = 8; + vga.mem.map[VGAEMU_MAP_BANK_MODE].base_page = 0xb8; + break; + } + gfx_deb( + "GFX_write_value: memory map = %dk@0x%x\n", + vga.mem.bank_pages << 2, + vga.mem.map[VGAEMU_MAP_BANK_MODE].base_page << 12 + ); + vgaemu_map_bank(); + } + break; + + case 0x07: /* Color Don't Care */ + vga.gfx.color_dont_care = data & 0x0f; + if(NEWBITS(0x0f)) { + gfx_deb("GFX_write_value: color_dont_care = 0x%x\n", vga.gfx.color_dont_care); + } + break; + + case 0x08: /* Bit Mask */ + vga.gfx.bitmask = data; + if(NEWBITS(0xff)) { + gfx_deb("GFX_write_value: bitmask = 0x%02x\n", vga.gfx.bitmask); + } + break; + + } +} + + +unsigned char GFX_read_value(void) +{ + unsigned char uc; + + uc = vga.gfx.index > GFX_MAX_INDEX ? 0x00 : vga.gfx.data[vga.gfx.index]; + + gfx_deb2("GFX_read_value: gfx[0x%02x] = 0x%02x\n", (unsigned) vga.gfx.index, (unsigned) uc); + + return uc; +} + diff --git a/src/base/dev/vga/hercemu.c b/src/base/dev/vga/hercemu.c new file mode 100644 index 0000000..94b21de --- /dev/null +++ b/src/base/dev/vga/hercemu.c @@ -0,0 +1,212 @@ +/* + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * DANG_BEGIN_MODULE + * + * REMARK + * Emulate the Hercules-specific parts. + * /REMARK + * + * DANG_END_MODULE + * + * DANG_BEGIN_CHANGELOG + * + * 1999/01/05: We emulate just the basics for the moment. + * -- sw () + * + * DANG_END_CHANGELOG + */ + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * some configurable options + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Debug level for the Hercules Card. + * 0 - normal / 1 - useful / 2 - too much + */ +#define DEBUG_HERC 0 + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#if !defined True +#define False 0 +#define True 1 +#endif + +#define herc_msg(x...) v_printf("VGAEmu: " x) + +#if DEBUG_HERC >= 1 +#define herc_deb(x...) v_printf("VGAEmu: " x) +#else +#define herc_deb(x...) +#endif + +#if DEBUG_HERC >= 2 +#define herc_deb2(x...) v_printf("VGAEmu: " x) +#else +#define herc_deb2(x...) +#endif + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include "emu.h" +#include "vgaemu.h" + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * DANG_BEGIN_FUNCTION Misc_init + * + * Initializes the Hercules Emulation. + * This function should be called during VGA mode initialization. + * This is an interface function. + * + * DANG_END_FUNCTION + * + */ +void Herc_init(void) +{ + vga.herc.cfg_switch = 0; + vga.herc.mode_ctrl = 0; + + herc_msg("Herc_init done\n"); +} + + +/* + * DANG_BEGIN_FUNCTION Herc_set_cfg_switch + * + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +void Herc_set_cfg_switch(unsigned char data) +{ + herc_deb2("Herc_set_cfg_switch: 0x%02x\n", (unsigned) data); + + if(((vga.herc.cfg_switch ^ data) & 1)) { + herc_deb("Herc_set_cfg_switch: graphics mode%s allowed\n", (data & 1) ? "" : " not"); + } + + if(((vga.herc.cfg_switch ^ data) & 2)) { + herc_deb( + "Herc_set_cfg_switch: %s mode (ignored)\n", + (data & 2) ? "full (64k)" : "half (32k)" + ); + } + + vga.herc.cfg_switch = data; +} + + +/* + * DANG_BEGIN_FUNCTION Herc_set_mode_ctrl + * + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +void Herc_set_mode_ctrl(unsigned char data) +{ + int m = 0; + unsigned u1; + + herc_deb2("Herc_set_mode_ctrl: 0x%02x\n", (unsigned) data); + + if(((vga.herc.mode_ctrl ^ data) & 2)) { + if((data & 2) && (vga.herc.cfg_switch & 1)) m = 2; + if((data & 2) == 0) m = 1; + herc_deb( + "Herc_set_mode_ctrl: entering %s mode%s\n", + data & 2 ? "graphics" : "text", m == 0 ? " (ignored)" : "" + ); + } + + if(((vga.herc.mode_ctrl ^ data) & 8)) { + herc_deb("Herc_set_mode_ctrl: %svideo access\n", (data & 8) ? "" : "no "); + + u1 = vga.config.video_off; + vga.config.video_off = (vga.config.video_off & ~8) + ((data & 8) ^ 8); + if(u1 != vga.config.video_off) { + herc_deb("Herc_set_mode_ctrl: video signal turned %s\n", vga.config.video_off ? "off" : "on"); + } + } + + if(((vga.herc.mode_ctrl ^ data) & 0x20)) { + herc_deb( + "Herc_set_mode_ctrl: blinking %s (ignored)\n", + (data & 0x20) ? "on" : "off" + ); + } + + if(((vga.herc.mode_ctrl ^ data) & 0x80)) { + herc_deb("Herc_set_mode_ctrl: switched to graphics page %d (ignored)\n", (data >> 7) & 1); + } + + vga.herc.mode_ctrl = data; + + if(m == 1) { /* switch to text mode */ + vga.mode_class = TEXT; + vga.mode_type = TEXT_MONO; + + vga.width = 720; + vga.height = 400; + vga.scan_len = 160; + vga.text_width = 80; + vga.text_height = 25; + vga.char_width = 9; + vga.char_height = 16; + vga.pixel_size = 4; + vga.display_start = 0; + + dirty_all_vga_colors(); + + vga.reconfig.re_init = 1; + } + + if(m == 2) { /* switch to graphics mode */ + vga.mode_class = GRAPH; + vga.mode_type = HERC; + + vga.width = 720; + vga.height = 348; + vga.scan_len = 90; + vga.text_width = 90; + vga.text_height = 25; + vga.char_width = 8; + vga.char_height = 14; + vga.pixel_size = 1; + vga.display_start = 0; + + dirty_all_vga_colors(); + + vga.reconfig.re_init = 1; + } +} + + +/* + * DANG_BEGIN_FUNCTION Herc_get_mode_ctrl + * + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +unsigned char Herc_get_mode_ctrl(void) +{ + herc_deb("Herc_get_mode_ctrl: 0x%02x\n", (unsigned) vga.herc.mode_ctrl); + + return vga.herc.mode_ctrl; +} + diff --git a/src/base/dev/vga/miscemu.c b/src/base/dev/vga/miscemu.c new file mode 100644 index 0000000..1c2ca81 --- /dev/null +++ b/src/base/dev/vga/miscemu.c @@ -0,0 +1,396 @@ +/* + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * DANG_BEGIN_MODULE + * + * REMARK + * Emulate miscellaneous registers for VGAEmu. + * /REMARK + * + * DANG_END_MODULE + * + * DANG_BEGIN_CHANGELOG + * + * 1996/05/06: + * - Changed Misc_get_input_status_1() to get it _slower_. + * Idea from Adam D. Moss (aspirin@tigerden.com). + * + * 1996/05/09: + * - Added horizontal retrace too (--adm) + * + * 1998/01/05: Moved Misc_get_input_status_1() from attr.c into a separate + * file (this one). Added emulation for the VGA regs which have their own + * port addresses. + * -- sw (Steffen Winterfeldt ) + * + * DANG_END_CHANGELOG + */ + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * some configurable options + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Debug level for the Attribute Controller. + * 0 - normal / 1 - useful / 2 - too much + */ +#define DEBUG_MISC 0 + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#if !defined True +#define False 0 +#define True 1 +#endif + +#define misc_msg(x...) v_printf("VGAEmu: " x) + +#if DEBUG_MISC >= 1 +#define misc_deb(x...) v_printf("VGAEmu: " x) +#else +#define misc_deb(x...) +#endif + +#if DEBUG_MISC >= 2 +#define misc_deb2(x...) v_printf("VGAEmu: " x) +#else +#define misc_deb2(x...) +#endif + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include "emu.h" +#include "vgaemu.h" +#include "timers.h" + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +hitimer_t t_vretrace = 0; /* cf. base/dev/misc/timers.c */ + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * DANG_BEGIN_FUNCTION Misc_init + * + * Initializes the Miscellaneous Output Register. + * This function should be called during VGA mode initialization. + * This is an interface function. + * + * DANG_END_FUNCTION + * + */ +void Misc_init(void) +{ + unsigned char u; + + u = 0x23; /* CRTC port = 0x3d4, CPU access enabled */ + + if(vga.mode_type == TEXT_MONO) { + /* mono modes --> CRTC port = 0x3b4 */ + u &= ~1; + } + + /* clock select */ + if(vga.VGA_mode >= 0) { + if(vga.VGA_mode <= 3 || vga.mode_type == TEXT_MONO) + u |= 4; /* clock #1 */ + else if(vga.VGA_mode > 0x13) + u |= 0xc; /* clock #3 */ + } + else { + u |= 0xc; /* clock #3 */ + } + + /* # of lines, aka hsync/vsync polarity */ + if(vga.VGA_mode >= 0 && vga.VGA_mode <= 0x13) { + switch(vga.VGA_mode) { + case 0x0f: + case 0x10: u |= 0x80; break; /* 350 lines */ + case 0x11: + case 0x12: u |= 0xc0; break; /* 480 lines */ + default: u |= 0x40; break; /* 400 lines */ + } + } + else { + u |= 0xc0; /* 480 lines */ + } + + vga.misc.misc_output = u; + vga.misc.feature_ctrl = 0; + + vga.config.mono_port = (vga.misc.misc_output & 1) ^ 1; + + if (vga.VGA_mode == 0x6) + Misc_set_color_select(0x3f); + else if (vga.VGA_mode <= 0x7) + Misc_set_color_select(0x30); + + misc_msg("Misc_init done\n"); +} + + +/* + * DANG_BEGIN_FUNCTION Misc_set_misc_output + * + * Emulate Miscellaneous Output Register. + * For now just stores the value. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +void Misc_set_misc_output(unsigned char data) +{ + unsigned u; + + misc_deb2("Misc_set_misc_output: 0x%02x\n", (unsigned) data); + + vga.misc.misc_output = data; + + u = ~(vga.misc.misc_output & 1); + + u = (vga.misc.misc_output & 1) ^ 1; + + if(u != vga.config.mono_port) { + vga.config.mono_port = u; + misc_msg("Misc_set_misc_output: VGA changed to %s mode\n", u ? "mono" : "color"); + vgaemu_adj_cfg(CFG_MODE_CONTROL, 0); + } +} + + +/* + * DANG_BEGIN_FUNCTION Misc_set_color_select + * + * Emulate CGA color select Register 0x3d9. + * This is a hardware emulation function. + * Don't do background colors for now. + * + * DANG_END_FUNCTION + * + */ +void Misc_set_color_select(unsigned char data) +{ + int i; + unsigned long long colors = 1ULL << vga.color_bits; + + misc_deb2("Misc_set_color_select: 0x%02x\n", (unsigned) data); + if (vga.mode_class == TEXT) { + /* border colour */ + vga.attr.data[0x11] = data & 0xf; + vga.attr.dirty[0x11] = True; + } else { + if (colors == 2) { + vga.attr.data[1] = data & 0xf; + } else if (colors == 4) { + if (data & 0x20) { /* cyan, magenta and white */ + vga.attr.data[1] = 3; + vga.attr.data[2] = 5; + vga.attr.data[3] = 7; + } else { /* green, red and brown (yellow) */ + vga.attr.data[1] = 2; + vga.attr.data[2] = 4; + vga.attr.data[3] = 6; + } + } else { + return; + } + vga.attr.data[0] = 0; + for (i = 0; i < colors; i++) { + vga.attr.dirty[i] = True; + if ((data & 0x10) && i > 0) /* bright colors */ + vga.attr.data[i] |= 0x10; + } + } +} + +/* + * DANG_BEGIN_FUNCTION Misc_get_misc_output + * + * Emulate Miscellaneous Output Register. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +unsigned char Misc_get_misc_output(void) +{ + misc_deb("Misc_get_misc_output: 0x%02x\n", (unsigned) vga.misc.misc_output); + + return vga.misc.misc_output; +} + + +/* + * DANG_BEGIN_FUNCTION Misc_set_feature_ctrl + * + * Emulate Feature Control Register. + * We just store the value. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +void Misc_set_feature_ctrl(unsigned char data) +{ + misc_deb("Misc_set_feature_ctrl: 0x%02x\n", (unsigned) data); + + vga.misc.feature_ctrl = data; +} + + +/* + * DANG_BEGIN_FUNCTION Misc_get_feature_ctrl + * + * Emulate Feature Control Register. + * We simply return what was written into it by Misc_set_feature_ctrl(). + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +unsigned char Misc_get_feature_ctrl(void) +{ + misc_deb("Misc_get_feature_ctrl: 0x%02x\n", (unsigned) vga.misc.feature_ctrl); + + return vga.misc.feature_ctrl; +} + + +/* + * DANG_BEGIN_FUNCTION Misc_get_input_status_0 + * + * Emulate Input Status #0 Register. + * The only bit of (possible) interest could be bit 7 indicating an irq 2 + * due to vertical retrace. + * But we don't do this, so we always return 0x00. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +unsigned char Misc_get_input_status_0(void) +{ + unsigned char u = 0; + + misc_deb( + "Misc_get_input_status_0: 0x%02x%s\n", + (unsigned) u, u & 0x80 ? " (IRQ 2)" : "" + ); + + return u; +} + +/* + * DANG_BEGIN_FUNCTION Misc_get_input_status_1 + * + * Emulate Input Status #1 Register. + * The essential part is to simulate the retrace signals. + * Clears the Attribute Controller's flip-flop. + * This is a hardware emulation function. + * + * DANG_END_FUNCTION + * + */ +unsigned char Misc_get_input_status_1(void) +{ + /* + * Graphic status - many programs will use this port to sync with + * the vert & horz retrace so as not to cause CGA snow. On VGAs this + * register is used to get full (read: fast) access to the video memory + * during the vertical retrace. + * + * bit 0 is Display Enable, bit 3 is Vertical Retrace + * 00=display 01=horiz.retrace 09=vert.retrace + * We're in vertical retrace? If so, set VR and DE flags + * We're in horizontal retrace? If so, just set DE flag, 0 in VR + * + * Idea from Adam Moss: + * Wait 20 milliseconds before we tell the DOS program that the VGA is + * in a vertical retrace. This is to avoid that some programs run too + * _fast_ in Dosemu (yes, I know, this sounds odd, but such programs + * really exist!). This option works only if the system has + * gettimeofday(). + * + * Now simpler and more 'realtime', for better or for worse. Implements + * horizontal retrace too. (--adm) + * + */ + static unsigned char hretrace = 0, vretrace = 0, first = 1; + static hitimer_t t_vretrace = 0; + static int flip = 0; + /* Timings are 'ballpark' guesses and may vary from mode to mode, but + such accuracy is probably not important... I hope. (--adm) */ + static int vvfreq = 17000; /* 70 Hz - but the best we'll get with + * current PIC will be 50 Hz */ + hitimer_t t, tdiff; + unsigned char retval; + + vga.attr.flipflop = 0; /* ATTR_INDEX_FLIPFLOP */ + +#ifdef OLD_CGA_SNOW_CODE + /* old 'cga snow' code with the new variables - looks terrible, + * but since it sometimes works we keep it for emergencies */ + hretrace ^= 0x01; vretrace++; + retval = 0xc6 | hretrace | (vretrace & 0xfc ? 0 : 0x09); +#else + t = GETusTIME(0); + + if(first) { t_vretrace = t; first = 0; } + tdiff = t - t_vretrace; + +#if DEBUG_MISC >= 2 + r_printf("VGAEmu: Misc: VR diff = %ld\n", tdiff); +#endif + + if(vretrace) { + /* We're in 'display' mode and should return 0 in DE and VR */ + /* set display after 1ms from retrace start */ + if(tdiff > 1000) vretrace = hretrace = 0; + } + else { + /* set retrace on timeout */ + if(tdiff > vvfreq) { + /* We're in vertical retrace? If so, set VR and DE flags */ + vretrace = 0x09; t_vretrace = t; + } + else { + /* The timer can't be relied upon for the very short intervals necessary + for horizontal retrace emulation + (such as the old "hretrace = (tdiff%49) > 35;"). + The following is a crude switch-on switch-off approach after + 10 reads taken from DosBox. It seems to work in most cases, + (in particular Commander Keen 4) */ + flip++; + if (flip > 40) flip = 0; + /* We're in horizontal retrace? If so, just set DE flag, 0 in VR */ + hretrace = flip > 20; + } + } + + retval = 0xc4 | hretrace | vretrace; + + /* + * Hercules cards use bit 7 for vertical retrace. This bit is not used + * by VGA cards, so there should be no conflict here. -- sw + */ + if(vga.config.mono_port && vretrace) retval &= ~0x80; + +#endif /* OLD_CGA_SNOW_CODE */ + + misc_deb( + "Misc_get_input_status_1: 0x%02x%s\n", + (unsigned) retval, + vretrace ? " (V_RETRACE)" : hretrace ? " (H_RETRACE)" : "" + ); + + return retval; +} + diff --git a/src/base/dev/vga/seqemu.c b/src/base/dev/vga/seqemu.c new file mode 100644 index 0000000..2868fee --- /dev/null +++ b/src/base/dev/vga/seqemu.c @@ -0,0 +1,354 @@ +/* + * seqemu.c + * + * VGA sequencer emulator for VGAemu + * + * Copyright (C) 1996, Erik Mouw + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * email: J.A.K.Mouw@et.tudelft.nl + * + * + * This code emulates the VGA Sequencer for VGAEmu. + * The sequencer is part of the VGA (Video Graphics Array, a video + * adapter for IBM compatible PC's). The sequencer is modeled after + * the Trident 8900 Super VGA chipset, so a lot of programs with Trident + * compatible video drivers will work with VGAEmu. + * + * DANG_BEGIN_MODULE + * + * REMARK + * The VGA Sequencer emulator for VGAEmu. + * /REMARK + * + * DANG_END_MODULE + * + * DANG_BEGIN_CHANGELOG + * + * 1999/01/05: Correct initial values for standard VGA modes. Nearly + * all standard VGA regs are emulated. + * -- sw (Steffen Winterfeldt ) + * + * DANG_END_CHANGELOG + * + */ + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * some configurable options + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Debug level for the Sequencer. + * 0 - normal / 1 - useful / 2 - too much + */ +#define DEBUG_SEQ 0 + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#if 0 + Trident Chip ID from VGADOC4 + + 1 = TR 8800BR + 2 = TR 8800CS + 3 = TR 8900B + 4 = TVGA8900C + 13h = TVGA8900C + 23h = TR 9000 + 33h = TVGA8900CL, TVGA8900D or TVGA 9000C + 43h = TVGA9000i + 53h = TR 9200CXr + 63h = TLCD9100B + 73h = TGUI9420 + 83h = TR LX8200 + 93h = TGUI9400CXi + A3h = TLCD9320 + C3h = TGUI9420DGi + D3h = TGUI9660XGi + E3h = TGUI9440AGi + F3h = TGUI9430 One source says 9420 ?? + The 63h, 73h, 83h, A3h and F3h entries are still in doubt. +#endif + +#define CHIP_ID 0x13 /* TVGA8900C */ +#define OLD_MODE 0 +#define NEW_MODE 1 + +#if !defined True +#define False 0 +#define True 1 +#endif + +#define seq_msg(x...) v_printf("VGAEmu: Seq: " x) + +#if DEBUG_SEQ >= 1 +#define seq_deb(x...) v_printf("VGAEmu: Seq: " x) +#else +#define seq_deb(x...) +#endif + +#if DEBUG_SEQ >= 2 +#define seq_deb2(x...) v_printf("VGAEmu: Seq: " x) +#else +#define seq_deb2(x...) +#endif + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include "emu.h" +#include "vgaemu.h" + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +static unsigned char seq_ival[16][5] = { + {0x03, 0x08, 0x03, 0x00, 0x02}, + {0x03, 0x08, 0x03, 0x00, 0x02}, + {0x03, 0x00, 0x03, 0x00, 0x02}, + {0x03, 0x00, 0x03, 0x00, 0x02}, + {0x03, 0x09, 0x03, 0x00, 0x02}, + {0x03, 0x09, 0x03, 0x00, 0x02}, + {0x03, 0x01, 0x01, 0x00, 0x06}, + {0x03, 0x00, 0x03, 0x00, 0x02}, + + {0x03, 0x09, 0x0f, 0x00, 0x06}, + {0x03, 0x01, 0x0f, 0x00, 0x06}, + {0x03, 0x01, 0x0f, 0x00, 0x06}, + {0x03, 0x01, 0x0f, 0x00, 0x06}, + {0x03, 0x01, 0x0f, 0x00, 0x06}, + {0x03, 0x01, 0x0f, 0x00, 0x06}, + {0x03, 0x01, 0x0f, 0x00, 0x0e}, + + {0x03, 0x01, 0x0f, 0x00, 0x06} +}; + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* calculate font bank offsets from map select value */ +static void seq_map_select(unsigned data) +{ + /* map select processing: prepare font mem offsets */ + /* actually, the single "8k" bit should be ignored on EGA! */ + vga.seq.fontofs[0] = ((data & 3) << 1) + /* 16k bits */ + ((data & 0x10) >> 4); /* 8k bit */ + vga.seq.fontofs[1] = ((data & 0x0c) >> 1) + /* 16k bits */ + ((data & 0x20) >> 5); /* 8k bit */ + vga.seq.fontofs[0] <<= 13; /* factor is 8k */ + vga.seq.fontofs[1] <<= 13; /* factor is 8k */ + seq_msg("Setting font offsets to 0x%04x/0x%04x (0x%02x)\n", + vga.seq.fontofs[0], vga.seq.fontofs[1], data); + vga.reconfig.display = 1; + vga.reconfig.re_init = 1; +} + +void Seq_init(void) +{ + unsigned i, j = 15; + + if(vga.VGA_mode >= 0 && vga.VGA_mode <= 7) + j = vga.VGA_mode; + else if(vga.VGA_mode >= 0x0d && vga.VGA_mode <= 0x13) + j = vga.VGA_mode - 5; + + for(i = 0; i < 5; i++) { + vga.seq.data[i] = seq_ival[j][i]; + } + if(j == 15 ) { + /* adjust reg 4 for vesa modes */ + vga.seq.data[4] = vga.mode_class == TEXT ? 0x02 : + vga.pixel_size == 4 ? 0x06 : 0x0a; + } + /* font offset processing, see below: */ + seq_map_select(vga.seq.data[3]); + while(i <= SEQ_MAX_INDEX) vga.seq.data[i++] = 0; + vga.seq.data[0x0b] = CHIP_ID; + + /* initialize non-standard modes */ + if(j == 15) { + if(vga.mode_class == TEXT) { + if(vga.char_width == 9) vga.seq.data[1] = 0; + vga.seq.data[2] = 3; + vga.seq.data[4] = 2; + } + } + + vga.seq.mode_ctrl_1_bak = vga.seq.mode_ctrl_2_bak = 0; + vga.seq.mode = NEW_MODE; + + vga.seq.index = 0; + + vga.seq.map_mask = vga.seq.data[2] & 0xf; + + seq_msg("Seq_init done\n"); +} + + +void Seq_set_index(unsigned char index) +{ + seq_deb2( + "Seq_set_index: 0x%02x%s\n", + (unsigned) index, + index > SEQ_MAX_INDEX ? " (too large)" : "" + ); + + vga.seq.index = index; +} + + +unsigned char Seq_get_index(void) +{ + seq_deb2("Seq_get_index: 0x%02x\n", (unsigned) vga.seq.index); + + return vga.seq.index; +} + + +void Seq_write_value(unsigned char data) +{ +#define NEWBITS(a) (delta & (a)) + unsigned u, u1, delta, ind = vga.seq.index; + unsigned char uc1; + + if(ind > SEQ_MAX_INDEX) { + seq_deb("Seq_write_value: data (0x%02x) ignored\n", (unsigned) data); + return; + } + + seq_deb2("Seq_write_value: seq[0x%02x] = 0x%02x\n", ind, (unsigned) data); + + /* Chip Version */ + if(vga.seq.index == 0x0b && vga.seq.mode == NEW_MODE) { + vga.seq.mode = OLD_MODE; + + uc1 = vga.seq.data[0x0d]; + vga.seq.data[0x0d] = vga.seq.mode_ctrl_2_bak; + vga.seq.mode_ctrl_2_bak = uc1; + + uc1 = vga.seq.data[0x0e]; + vga.seq.data[0x0e] = vga.seq.mode_ctrl_1_bak; + vga.seq.mode_ctrl_1_bak = uc1; + } + + /* this one is tricky... :-) -- sw */ + if(ind == 0x0e && vga.seq.mode == NEW_MODE) { + if(vga.seq.data[0x0e] == (data ^ 0x02)) return; + } + else { + if(vga.seq.data[ind] == data) return; + } + + delta = vga.seq.data[ind] ^ data; + vga.seq.data[ind] = data; + + switch(ind) { + case 0x00: /* Reset */ + break; + + case 0x01: /* Clocking Mode */ + if(NEWBITS(0x20)) { + seq_deb("Seq_write_value: %svideo access\n", (data & 0x20) ? "no " : ""); + + u1 = vga.config.video_off; + vga.config.video_off = (vga.config.video_off & ~2) + ((data >> 4) & 2); + if(u1 != vga.config.video_off) { + seq_deb("Seq_write_value: video signal turned %s\n", vga.config.video_off ? "off" : "on"); + } + } + if(NEWBITS(0x9)) { + vgaemu_adj_cfg(CFG_SEQ_ADDR_MODE, 0); + } + break; + + case 0x02: /* Map Mask */ + u = data; + if (vga.color_bits != 8) + u &= 0xf; + vga.seq.map_mask = u; + u1 = 0; + if(u) while(!(u & 1)) u >>= 1, u1++; + seq_deb("Seq_write_value: map mask = 0x%x, write plane = %u\n", + (unsigned) vga.seq.map_mask, u1 + ); + // ##### FIXME: drop this altogether and always use + // the gfx.read_map_select reg? -- sw + vgaemu_switch_plane(u1); + break; + + case 0x03: /* Character Map Select */ + if (NEWBITS(0xff)) + seq_map_select(data); + break; + + + case 0x04: /* Memory Mode */ + if(NEWBITS(0x0c)) { + vgaemu_adj_cfg(CFG_SEQ_ADDR_MODE, 0); + } + break; + + case 0x0b: /* Trident: Chip Version */ + return; /* read-only! */ + break; + + case 0x0c: /* Trident: Power Up Mode 1 */ + if(!(vga.seq.mode == NEW_MODE && (vga.seq.data[0x0e] & 0x80))) return; + break; + + case 0x0d: /* Trident: Old/New Mode Control 2 */ + break; + + case 0x0e: /* Trident: Old/New Mode Control 1 */ + if(vga.seq.mode == NEW_MODE) { + u = (data ^ 0x02) & 0x0f; /* XOR 0x02, used for Trident detection */ + vga_emu_switch_bank(u); + vga.seq.data[0x0e] = data ^ 0x02; + } + else { /* Seq_mode == OLD_MODE */ + /* don't know what to do, we don't support 128K pages -- Erik */ + } + break; + + case 0x0f: /* Trident: Power Up Mode 2 */ + break; + } +} + + +unsigned char Seq_read_value(void) +{ + unsigned char uc, uc1; + + uc = vga.seq.index > SEQ_MAX_INDEX ? 0x00 : vga.seq.data[vga.seq.index]; + + /* Chip Version */ + if(vga.seq.index == 0x0b && vga.seq.mode == OLD_MODE) { + vga.seq.mode = NEW_MODE; + + uc1 = vga.seq.data[0x0d]; + vga.seq.data[0x0d] = vga.seq.mode_ctrl_2_bak; + vga.seq.mode_ctrl_2_bak = uc1; + + uc1 = vga.seq.data[0x0e]; + vga.seq.data[0x0e] = vga.seq.mode_ctrl_1_bak; + vga.seq.mode_ctrl_1_bak = uc1; + } + + seq_deb2("Seq_read_value: seq[0x%02x] = 0x%02x\n", (unsigned) vga.seq.index, (unsigned) uc); + + return uc; +} + diff --git a/src/base/dev/vga/vesa.c b/src/base/dev/vga/vesa.c new file mode 100644 index 0000000..663ea6f --- /dev/null +++ b/src/base/dev/vga/vesa.c @@ -0,0 +1,1242 @@ +/* + * DANG_BEGIN_MODULE + * + * REMARK + * VESA BIOS Extensions for VGAEmu. + * + * Supported are VBE version 2.0, including a linear frame buffer + * and display power management support. + * + * /REMARK + * DANG_END_MODULE + * + * Copyright (c) 1997 Steffen Winterfeldt + * + * DANG_BEGIN_CHANGELOG + * + * 1995/1996: Initial version by Erik Mouw and Arjan Filius + * + * 1997/07/07: Nearly full rewrite. We now support full VBE 2.0 + * functionality, including power management functions (although + * our only frontend, X, doesn't use this info yet). + * -- sw (Steffen Winterfeldt ) + * + * 1998/12/12: Changed (fixed?) VBE set/get palette function. + * -- sw + * + * 1998/12/14: Another attempt to fix the pm version of the + * VBE set palette function. + * -- sw + * + * 1999/01/11: Removed vesa_emu_fault(), the last remaining old code piece. + * -- sw + * + * 2000/05/18: Copy VGA fonts into BIOS ROM area. + * -- sw + * + * DANG_END_CHANGELOG + * + */ + + +/* + * define to enable some debug information + */ + +#define DEBUG_VBE /* general info */ + + +#include +#include +#include +#include "cpu.h" +#include "emu.h" +#include "video.h" +#include "render.h" +#include "vgaemu.h" +#include "mapping.h" +#include "emudpmi.h" +#include "vesabios_pm_offsets.h" +#include "vesabios_offsets.h" +#include "vesa.h" +#include "int.h" + +#define VBE_BIOS_MAXPAGES 4 /* max. 16k BIOS size, more than enough */ + +/* identity of our VBE implementation */ +#define VBE_OEMVendorName "DOSEMU-Development-Team" +#define VBE_OEMProdName "VBE2Emu" +#define VBE_OEMSoftRev 0x100 /* 1.0 */ +#define VBE_OEMProductRev "1.0" + +/* + * Type of display we are attached to. This info is needed to report + * the color masks of hi/true-color displays back to the DOS app. + */ + +static vgaemu_display_type vbe_screen; + + +/* + * function prototypes + */ + +static int vbe_info(unsigned int); +static int vbe_mode_info(unsigned, unsigned int); +static int vbe_set_mode(unsigned); +static int vbe_get_mode(void); +static int vbe_save_restore(unsigned, unsigned, unsigned int); +static int vbe_display_window(unsigned, unsigned, unsigned); +static int vbe_scan_length(unsigned, unsigned); +static int vbe_display_start(unsigned, unsigned, unsigned); +static int vbe_dac_format(unsigned, unsigned); +static int vbe_palette_data(unsigned, unsigned, unsigned, unsigned int); +static int vbe_pm_interface(unsigned); +static int vbe_power_state(unsigned, unsigned); + + +/* + * DANG_BEGIN_FUNCTION vbe_init + * + * description: + * Initializes the VGA/VBE BIOS and the VBE support. + * + * arguments: + * vedt - Pointer to struct describing the type of display we are actually + * attached to. + * + * DANG_END_FUNCTION + * + */ + +void vbe_pre_init(void) +{ + int i; + vga_mode_info *vmi = NULL; + unsigned int dos_vga_bios = SEGOFF2LINEAR(0xc000,0x0000); + int bios_ptr = vgaemu_bios_end - vgaemu_bios_start; + + static struct { + unsigned char modes[3]; + char reserved1[4]; + char scanlines; + char character_blocks; + char max_active_blocks; + short support_flags; + short reserved2; + char save_function_flags; + char reserved3; + } vgaemu_bios_functionality_table = + { .modes = {0xff,0xe0,0x0f}, /* Modes 0-7, 0dh-0fh, 10h-13h supported */ + .scanlines = 7, /* scanlines 200,350,400 supported */ + .character_blocks = 2, /* This all corresponds to a real BIOS */ + .max_active_blocks = 8, /* See Ralf Brown's interrupt list */ + .support_flags = 0xeff, /* INT 10h, AH=1b for documentation */ + .save_function_flags=0x3f + }; + + MEMSET_DOS(dos_vga_bios, 0, VBE_BIOS_MAXPAGES << 12); /* one page */ + MEMCPY_2DOS(dos_vga_bios, _binary_vesabios_o_bin_start, bios_ptr); + + vgaemu_bios.prod_name = vgaemu_bios_prod_name - vgaemu_bios_start; + + if (!config.term) { + i = vgaemu_bios_pm_interface_end - vgaemu_bios_pm_interface; + + if(i + bios_ptr > (VBE_BIOS_MAXPAGES << 12) - 8) { + error("VBE: vbe_init: protected mode interface to large, disabling\n"); + vgaemu_bios.vbe_pm_interface_len = + vgaemu_bios.vbe_pm_interface = 0; + } + + vgaemu_bios.vbe_pm_interface_len = i; + vgaemu_bios.vbe_pm_interface = bios_ptr; + MEMCPY_2DOS(dos_vga_bios + bios_ptr, _binary_vesabios_pm_o_bin_start, i); + bios_ptr += i; + + bios_ptr = (bios_ptr + 3) & ~3; + vgaemu_bios.vbe_mode_list = bios_ptr; + + /* set up video mode list */ + for(i = 0x100; i <= vgaemu_bios.vbe_last_mode; i++) { + if((vmi = vga_emu_find_mode(i, NULL))) { + if(vmi->VESA_mode != -1 && bios_ptr < ((VBE_BIOS_MAXPAGES << 12) - 4)) { + WRITE_WORD(dos_vga_bios + bios_ptr, vmi->VESA_mode); + bios_ptr += 2; + } + } + } + + WRITE_WORD(dos_vga_bios + bios_ptr, -1); + bios_ptr += 2; + + /* add fonts */ + vgaemu_bios.font_8 = bios_ptr; + MEMCPY_2DOS(dos_vga_bios + bios_ptr, vga_rom_08, sizeof vga_rom_08); + bios_ptr += sizeof vga_rom_08; + + vgaemu_bios.font_14 = bios_ptr; + MEMCPY_2DOS(dos_vga_bios + bios_ptr, vga_rom_14, sizeof vga_rom_14); + bios_ptr += sizeof vga_rom_14; + + vgaemu_bios.font_16 = bios_ptr; + MEMCPY_2DOS(dos_vga_bios + bios_ptr, vga_rom_16, sizeof vga_rom_16); + bios_ptr += sizeof vga_rom_16; + + vgaemu_bios.font_14_alt = bios_ptr; + MEMCPY_2DOS(dos_vga_bios + bios_ptr, vga_rom_14_alt, sizeof vga_rom_14_alt); + bios_ptr += sizeof vga_rom_14_alt; + + vgaemu_bios.font_16_alt = bios_ptr; + MEMCPY_2DOS(dos_vga_bios + bios_ptr, vga_rom_16_alt, sizeof vga_rom_16_alt); + bios_ptr += sizeof vga_rom_16_alt; + } else { + /* only support the initial video mode, can't change it */ + vgaemu_bios_functionality_table.modes[0] = 1 << video_mode; + vgaemu_bios_functionality_table.modes[1] = + vgaemu_bios_functionality_table.modes[2] = 0; + } + vgaemu_bios.functionality = bios_ptr; + MEMCPY_2DOS(dos_vga_bios + bios_ptr, &vgaemu_bios_functionality_table, + sizeof vgaemu_bios_functionality_table); + bios_ptr += sizeof vgaemu_bios_functionality_table; + + vgaemu_bios.size = bios_ptr; + + WRITE_BYTE(dos_vga_bios + 2, (bios_ptr + ((1 << 9) - 1)) >> 9); + vgaemu_bios.pages = (bios_ptr + ((1 << 12) - 1)) >> 12; + + if (config.vgaemubios_file) { + /* EXPERIMENTAL: load and boot the Bochs BIOS */ + int fd = open(config.vgaemubios_file, O_RDONLY); + int bytes; + if (fd != -1) { + bytes = read(fd, LINEAR2UNIX(0xc0000), 65536); + close(fd); + vgaemu_bios.pages = (bytes + PAGE_SIZE - 1) / PAGE_SIZE; + config.vbios_post = 1; + } + } + + memcheck_addtype('V', "VGAEMU Video BIOS"); + memcheck_reserve('V', dos_vga_bios, vgaemu_bios.pages << 12); + + if(!config.X_pm_interface) { + v_printf("VBE: vbe_init: protected mode interface disabled\n"); + } + + v_printf( + "VBE: vbe_init: %d pages for VGA BIOS, vga.mem.base = %p\n", + vgaemu_bios.pages, vga.mem.base + ); +} + +void vbe_init(vgaemu_display_type *vedt) +{ + vbe_screen = *vedt; + + v_printf( + "VBE: vbe_init: real display: bits/pixel = %d, color bits (rgb) = %d/%d/%d\n", + vbe_screen.bits, vbe_screen.r_bits, vbe_screen.g_bits, vbe_screen.b_bits + ); + + v_printf("VBE: vbe_init: supported VBE mode types = 0x%x\n", vbe_screen.src_modes); +} + + +/* + * DANG_BEGIN_FUNCTION do_vesa_int + * + * description: + * This is the VESA interrupt handler. + * + * It is called from base/bios/int10.c::int10(). The VESA interrupt is called + * with 0x4f in AH and the function number (0x00 ... 0x10) in AL. + * + * DANG_END_FUNCTION + */ + +void do_vesa_int(void) +{ + int err_code = VBE_ERROR_GENERAL_FAIL; + +#if 0 + v_printf( + "VBE: function 0x%02x, bx = 0x%04x cx = 0x%04x, dx = 0x%04x, es = 0x%04x, di = 0x%04x\n", + (unsigned) _AL, (unsigned) _BX, (unsigned) _CX, (unsigned) _DX, (unsigned) _ES, (unsigned) _DI + ); +#endif + + switch(_AL) { + case 0x00: /* return VBE controller info */ + err_code = vbe_info(SEGOFF2LINEAR(_ES, _DI)); + break; + + case 0x01: /* return VBE mode info */ + err_code = vbe_mode_info(_CX, SEGOFF2LINEAR(_ES, _DI)); + break; + + case 0x02: /* set VBE mode */ + err_code = vbe_set_mode(_BX); + break; + + case 0x03: /* get current VBE mode */ + err_code = vbe_get_mode(); + break; + + case 0x04: /* save/restore state */ + err_code = vbe_save_restore(_DL, _CX, SEGOFF2LINEAR(_ES, _BX)); + break; + + case 0x05: /* display window control (aka set/get bank) */ + err_code = vbe_display_window(_BH, _BL, _DL /* must be _DX !!!*/); + break; + + case 0x06: /* set/get logical scan line length */ + err_code = vbe_scan_length(_BL, _CX); + break; + + case 0x07: /* set/get display start */ + err_code = vbe_display_start(_BL, _CX, _DX); + break; + + case 0x08: /* set/get DAC palette format */ + err_code = vbe_dac_format(_BL, _BH); + break; + + case 0x09: /* set/get palette data */ + err_code = vbe_palette_data(_BL, _CX, _DX, SEGOFF2LINEAR(_ES, _DI)); + break; + + case 0x0a: /* return VBE PM interface */ + err_code = vbe_pm_interface(_BL); + break; + + case 0x10: /* set/get display power state */ + err_code = vbe_power_state(_BL, _BH); + break; + + default: + err_code = VBE_ERROR_UNSUP; +#ifdef DEBUG_VBE + v_printf( + "VBE: unsupported function 0x%02x, retval = %d, bx = 0x%04x cx = 0x%04x, dx = 0x%04x, es = 0x%04x, di = 0x%04x\n", + (unsigned) _AL, err_code, (unsigned) _BX, (unsigned) _CX, (unsigned) _DX, (unsigned) _ES, (unsigned) _DI + ); +#endif + } + + if(err_code >= 0) { + _AL = 0x4f; + _AH = (unsigned char) err_code; + } +} + + +/* + * VBE function 0x00 + * + * Return VBE controller info. + * + * This function never fails (of course). + * + * arguments: + * vbei - pointer to VBE info block + * + */ + +static int vbe_info(unsigned int vbeinfo) +{ + int vbe2 = 0; + int i; + struct VBE_vi *vbei = LINEAR2UNIX(vbeinfo); + size_t size; + +#ifdef DEBUG_VBE + v_printf( + "VBE: [0x%02x] vbe_info: es:di = 0x%04x:0x%04x\n", + (unsigned) _AL, (unsigned) _ES, (unsigned) _DI + ); +#endif + + if(vbei->VBESig == 0x32454256 /* "VBE2" */) vbe2 = 1; + +#ifdef DEBUG_VBE + if(vbe2) v_printf("VBE: [0x%02x] vbe_info: VBE2 info requested\n", (unsigned) _AL); +#endif + + size = vbe2 ? sizeof(*vbei) : offsetof(struct VBE_vi, OEMData); + memset(vbei, 0, size); + + vbei->VBESig = 0x41534556; /* "VESA" */ + vbei->VESAVersion = 0x200; /* 2.0 */ + vbei->OEMID = MK_FP16(0xc000, vgaemu_bios.prod_name); + vbei->Capabilities = 1; /* 6/8 bit switchable DAC */ + vbei->ModeList = MK_FP16(0xc000, vgaemu_bios.vbe_mode_list); + vbei->Memory = vga.mem.pages >> 4; /* multiples of 64 kbyte */ + + if(vbe2) { + i = 0; + vbei->OEMSoftRev = VBE_OEMSoftRev; + + memcpy(&vbei->OEMData[i], VBE_OEMVendorName, sizeof(VBE_OEMVendorName)); + vbei->OEMVendorName = MK_FP16(_ES, _DI + offsetof(struct VBE_vi, OEMData) + i); + i += sizeof(VBE_OEMVendorName); + + memcpy(&vbei->OEMData[i], VBE_OEMProdName, sizeof(VBE_OEMProdName)); + vbei->OEMProdName = MK_FP16(_ES, _DI + offsetof(struct VBE_vi, OEMData) + i); + i += sizeof(VBE_OEMProdName); + + memcpy(&vbei->OEMData[i], VBE_OEMProductRev, sizeof(VBE_OEMProductRev)); + vbei->OEMProductRev = MK_FP16(_ES, _DI + offsetof(struct VBE_vi, OEMData) + i); + } + +#ifdef DEBUG_VBE + v_printf("VBE: [0x%02x] vbe_info: return value 0\n", (unsigned) _AL); +#endif + + return 0; +} + + +/* + * VBE function 0x01 + * + * Return VBE mode info. + * + * Fills out a special VBE mode info block. Fails if `mode' is + * not in our mode list. + * + * arguments: + * mode - VBE mode number + * vbemi - pointer to VBE mode info block + * + */ + +static int vbe_mode_info(unsigned mode, unsigned int vbemodeinfo) +{ + vga_mode_info *vmi; + int err_code = VBE_ERROR_GENERAL_FAIL; + unsigned u; + unsigned mode_size; + unsigned scan_len; + struct VBE_vm vbemi; + + mode &= 0xffff; + +#ifdef DEBUG_VBE + v_printf( + "VBE: [0x%02x] vbe_mode_info: cx = 0x%04x, es:di = 0x%04x:0x%04x\n", + (unsigned) _AL, (unsigned) _CX, (unsigned) _ES, (unsigned) _DI + ); +#endif + + memset(&vbemi, 0, 0x100); /* Do it always or only for valid modes? -- sw */ + + if((vmi = vga_emu_find_mode(mode, NULL))) { + err_code = 0; + + if(vmi->mode_class == GRAPH) { + scan_len = vmi->color_bits; + if(vmi->type == PL4) scan_len = 1; /* 4 planes ! */ + if(vmi->type == P15) scan_len = 16; + scan_len *= vmi->width; + scan_len >>= 3; /* in bytes */ + mode_size = scan_len * vmi->height; + } + else { + scan_len = vmi->text_width * 2; + mode_size = 0; + } + + u = 1 << 1; /* reserved bit, always 1 */ + + if( + (vmi->type == P8 && vbe_screen.src_modes & (MODE_PSEUDO_8 | MODE_TRUE_8)) || + (vmi->type == P15 && vbe_screen.src_modes & MODE_TRUE_15) || + (vmi->type == P16 && vbe_screen.src_modes & MODE_TRUE_16) || + (vmi->type == P24 && vbe_screen.src_modes & MODE_TRUE_24) || + (vmi->type == P32 && vbe_screen.src_modes & MODE_TRUE_32) + /* no PL4 and PL1 modes for now */ + ) { + u |= (1 << 0); /* mode supported */ + } + + if(mode_size > vga.mem.size) u &= ~(1 << 0); /* mode not supported */ + +#if 0 + if(vmi->mode_class == TEXT) u |= 0 << 2; /* TTY support */ +#else + u |= 1 << 2; /* TTY support */ +#endif + if(vmi->type != TEXT_MONO && vmi->type != HERC) + u |= 1 << 3; /* color mode */ + if(vmi->mode_class == GRAPH) u |= 1 << 4; /* graphics mode */ + if(! vmi->buffer_start) u |= 1 << 6; /* window support */ + if(vmi->color_bits >= 8 && vga.mem.lfb_base_page) u |= 1 << 7; /* LFB support */ + vbemi.ModeAttrib = u; + + vbemi.WinAAttrib = 7; + vbemi.WinBAttrib = 0; /* not supported */ + + vbemi.WinGran = vmi->buffer_len; /* in kbyte */ + vbemi.WinSize = vmi->buffer_len; /* in kbyte */ + + vbemi.WinASeg = vmi->buffer_start; /* segment address */ + vbemi.WinBSeg = 0; /* not supported */ + + vbemi.WinFuncPtr = MK_FP16(0xc000, vgaemu_bios_win_func - vgaemu_bios_start); + + vbemi.BytesPLine = scan_len; + if(vmi->mode_class == GRAPH) { + vbemi.XRes = vmi->width; + vbemi.YRes = vmi->height; + } + else { + vbemi.XRes = vmi->text_width; + vbemi.YRes = vmi->text_height; + } + vbemi.XCharSize = vmi->char_width; + vbemi.YCharSize = vmi->char_height; + + vbemi.NumPlanes = (vmi->mode_class == TEXT || vmi->type == PL4 || vmi->type == NONCHAIN4) ? 4 : 1; + vbemi.BitsPPixel = vmi->color_bits; + vbemi.Banks = vmi->type == HERC ? 4 : vmi->type == CGA ? 2 : 1; + vbemi.BankSize = (vmi->type == HERC || vmi->type == CGA) ? 8 : 0; /* in kbyte */ + + vbemi.MemModel = vmi->type >= P15 && vmi->type <= P32 ? DIRECT : vmi->type; + + if(mode_size) { + u = vga.mem.size / mode_size; + if(u > 0x100) u = 0x100; + if(u == 0) u = 1; + vbemi.Pages = u - 1; + } + + if(vbemi.Pages > 7) vbemi.Pages = 7; + + vbemi.Reserved1 = 1; + + if(vmi->type >= P15 && vmi->type <= P32) { + int r_bits, g_bits, b_bits, v_bits = 0; + int r_shift, g_shift, b_shift, v_shift = 0; + int color_bits = 0; + + switch(vmi->type) { + case P15: + r_bits = g_bits = b_bits = 5; + b_shift = 0; g_shift = 5; r_shift = 10; + color_bits = 15; + break; + + case P16: + r_bits = b_bits = 5; g_bits = 6; + b_shift = 0; g_shift = 5; r_shift = 11; + color_bits = 16; + break; + + case P24: + r_bits = g_bits = b_bits = 8; + b_shift = 0; g_shift = 8; r_shift = 16; + color_bits = 24; + break; + + case P32: + r_bits = g_bits = b_bits = 8; + b_shift = 0; g_shift = 8; r_shift = 16; + color_bits = 32; + break; + + default: + r_bits = g_bits = b_bits = r_shift = g_shift = b_shift = 0; + v_printf("VBE: vbe_mode_info: internal error\n"); + } + + /* We cannot (reasonably) detect the position of the reserved field + * here, so we do some simple guessing for now. + */ + v_shift = r_bits + g_bits + b_bits; + v_bits = color_bits - v_shift; + if(v_bits < 0) v_bits = 0; + + vbemi.RedMaskSize = r_bits; + vbemi.RedFieldPos = r_shift; + vbemi.GreenMaskSize = g_bits; + vbemi.GreenFieldPos = g_shift; + vbemi.BlueMaskSize = b_bits; + vbemi.BlueFieldPos = b_shift; + vbemi.RsvdMaskSize = v_bits; + vbemi.RsvdFieldPos = v_shift; + } + + vbemi.DirectColor = 0; + if(vmi->color_bits >= 8 && vga.mem.lfb_base_page) + vbemi.PhysBasePtr = VGAEMU_PHYS_LFB_BASE; /* LFB support */ + + vbemi.OffScreenOfs = 0; + vbemi.OffScreenMem = 0; + } + +#ifdef DEBUG_VBE + v_printf("VBE: [0x%02x] vbe_mode_info: return value %d\n", (unsigned) _AL, err_code); +#endif + + MEMCPY_2DOS(vbemodeinfo, &vbemi, sizeof vbemi); + return err_code; +} + + +/* + * VBE function 0x02 + * + * Set VBE mode. + * + * Calls base/bios/int10.c::set_video_mode() to actually set + * the mode. Accepts standard VGA mode numbers (<= 0x7f) as well. + * + * arguments: + * mode - the mode number + * + */ + +static int vbe_set_mode(unsigned mode) +{ + vga_mode_info *vmi; + int err_code = VBE_ERROR_GENERAL_FAIL; + + mode &= 0xffff; + +#ifdef DEBUG_VBE + v_printf("VBE: [0x%02x] vbe_set_mode: bx = 0x%04x\n", (unsigned) _AL, (unsigned) _BX); +#endif + + if((vmi = vga_emu_find_mode(mode, NULL))) { + if(mode < 0x80 || mode >= 0x100) { + err_code = 0; + if(!set_video_mode(mode)) err_code = 1; + } + } + +#ifdef DEBUG_VBE + v_printf("VBE: [0x%02x] vbe_set_mode: return value %d\n", (unsigned) _AL, err_code); +#endif + + return err_code; +} + + +/* + * VBE function 0x03 + * + * Return the current video mode. + * + * This function will return the very mode number that was used + * to set the mode (including memory-clear & lfb bits). + * + */ + +static int vbe_get_mode(void) +{ +#ifdef DEBUG_VBE + v_printf("VBE: [0x%02x] vbe_get_mode\n", (unsigned) _AL); +#endif + + _BX = (unsigned short) vga.mode; + +#ifdef DEBUG_VBE + v_printf("VBE: [0x%02x] vbe_get_mode: return value 0, bx = 0x%04x\n", (unsigned) _AL, (unsigned) _BX); +#endif + return 0; +} + + +/* + * VBE function 0x04 + * + * Save/restore graphics state. + * - unimplemented for now - + * + * arguments: + * sub_func - save/restore/get info + * mask - select which part to save/restore + * buffer - save/restore buffer + * + */ + +static int vbe_save_restore(unsigned sub_func, unsigned mask, unsigned int buffer) +{ + int err_code = 0; + +#ifdef DEBUG_VBE + v_printf( + "VBE: [0x%02x.%u] vbe_save_restore: dl = 0x%02x, cx = 0x%04x, es:bx = 0x%04x:0x%04x\n", + (unsigned) _AL, sub_func, (unsigned) _DL, (unsigned) _CX, (unsigned) _ES, (unsigned) _BX + ); +#endif + + err_code = VBE_ERROR_UNSUP; /* not implemented */ + +#ifdef DEBUG_VBE + v_printf( + "VBE: [0x%02x.%u]: return value = %d, bx = 0x%04x\n", + (unsigned) _AL, sub_func, err_code, (unsigned) _BX + ); +#endif + + return err_code; +} + + +/* + * VBE function 0x05 + * + * Set or get the currently mapped memory bank. + * This function fails when called in an LFB mode. + * + * arguments: + * sub_func - subfunction (0 = set, 1 = get) + * window - the window, (0 = A, 1 = B; only A is supported) + * bank - the bank number + * + */ + +static int vbe_display_window(unsigned sub_func, unsigned window, unsigned bank) +{ + int err_code = 0; + int direct = 0; + + if(sub_func & 0x40) { + sub_func &= ~0x40; + direct = 1; + } + +#ifdef DEBUG_VBE + if(sub_func) { + v_printf( + "VBE: [0x%02x.%u] vbe_display_window::get: bl = 0x%02x\n", + (unsigned) _AL, sub_func, (unsigned) _BL + ); + } + else { + v_printf( + "VBE: [0x%02x.%u] vbe_display_window::set: bl = 0x%02x, dx = 0x%04x\n", + (unsigned) _AL, sub_func, (unsigned) _BL, (unsigned) _DX + ); + } + if(direct) { + v_printf("VBE: [0x%02x.%u] vbe_display_window: accessed via direct call\n", (unsigned) _AL, sub_func); + } +#endif + + if(vga.mode & (1 << 14)) { + err_code = VBE_ERROR_MODE_FAIL; /* fail in LFB mode */ + } + else if(window != 0) { + err_code = VBE_ERROR_GENERAL_FAIL; /* we support only Window A */ + } + else if(sub_func > 1) { + err_code = VBE_ERROR_GENERAL_FAIL; /* invalid subfunction */ + } + else if(sub_func) { + _DX = vga.mem.bank; + } + else { + if(! vga_emu_switch_bank(bank)) { + err_code = VBE_ERROR_GENERAL_FAIL; /* failed */ + } + } + +#ifdef DEBUG_VBE + if(sub_func) { + v_printf( + "VBE: [0x%02x.%u] vbe_display_window::get: return value %d, dx = 0x%04x\n", + (unsigned) _AL, sub_func, err_code, (unsigned) _DX + ); + } + else { + v_printf( + "VBE: [0x%02x.%u] vbe_display_window::set: return value %d\n", + (unsigned) _AL, sub_func, err_code + ); + } +#endif + + return err_code; +} + + +/* + * VBE function 0x06 + * + * Set or get the length of a logical scan line, query the maximum + * scan line length. + * + * arguments: + * sub_func - subfunction (0 = set, 1 = get, 2 = set, 3 = get max len) + * scan_len - the new length (sub_func = 0: in pixels, sub_func = 2: in bytes) + * + */ + +int vbe_scan_length(unsigned sub_func, unsigned scan_len) +{ + int err_code = 0; + unsigned u0 = 0, u1 = 0, u2 = 0; + unsigned max_pixels, max_bytes, max_mem; + unsigned planes = 1; + +#ifdef DEBUG_VBE + v_printf( + "VBE: [0x%02x.%u] vbe_scan_length: bl = 0x%02x, cx = 0x%04x\n", + (unsigned) _AL, sub_func, (unsigned) _BL, (unsigned) _CX + ); +#endif + + if(vga.mode_class == TEXT) { + max_bytes = (0x8000 / vga.text_height) & ~1; + max_pixels = (max_bytes >> 1) * vga.char_width; + + switch(sub_func) { + case 0: /* set scan len in pixels */ + if(scan_len < vga.width || scan_len % vga.char_width || scan_len > max_pixels) { + err_code = VBE_ERROR_HARDWARE_FAIL; + break; + } + u0 = vga.scan_len = (scan_len / vga.char_width) << 1; + u1 = scan_len; + break; + + case 1: /* get current config */ + u0 = vga.scan_len; + u1 = (vga.scan_len >> 1) * vga.char_width; + break; + + case 2: /* set scan len in bytes */ + if(scan_len < (vga.width / vga.char_width) << 1 || scan_len > max_bytes || scan_len & 1) { + err_code = VBE_ERROR_HARDWARE_FAIL; + break; + } + u0 = vga.scan_len = scan_len; + u1 = (scan_len >> 1) * vga.char_width; + break; + + case 3: /* get maximum config */ + u0 = max_bytes; + u1 = max_pixels; + break; + + default: + err_code = VBE_ERROR_GENERAL_FAIL; /* invalid subfunction */ + } + + if(!err_code) { + u2 = 0x8000 / u0; /* max scan lines */ + u2 *= vga.char_height; + } + } + else { + max_mem = vga.mem.size; + if((vga.mode_type == PL4 || vga.mode_type == NONCHAIN4 || + vga.mode_type == HERC || vga.mode_type == CGA) && max_mem > 256 * 1024 + ) { + max_mem = 256 * 1024; /* to avoid brain damage... ;-) */ + } + + if(vga.mode_type == PL4 || vga.mode_type == NONCHAIN4) planes = 4; + max_bytes = ((max_mem / vga.height) / planes) & ~3; /* 4 byte aligned */ + max_pixels = ((max_bytes << 3) / vga.pixel_size) * planes; + + switch(sub_func) { + case 0: /* set scan len in pixels */ + u0 = (((scan_len * vga.pixel_size) >> 3) / planes + 3) & ~3; + u1 = ((u0 << 3) / vga.pixel_size) * planes; + if(u1 < vga.width || u1 > max_pixels || u1 > 0x7fff || u0 > 0x7fff /* || u1 > vga.width + 100 */) { + err_code = VBE_ERROR_HARDWARE_FAIL; + break; + } + vga.scan_len = u0; + vga.reconfig.display = 1; + vga.reconfig.re_init = 1; + break; + + case 1: /* get current config */ + u0 = vga.scan_len; + u1 = ((u0 << 3) / vga.pixel_size) * planes; + break; + + case 2: /* set scan len in bytes */ + u0 = (scan_len + 3) & ~3; + u1 = ((u0 << 3) / vga.pixel_size) * planes; + if(u1 < vga.width || u1 > max_pixels || u1 > 0x7fff || u0 > 0x7fff) { + err_code = VBE_ERROR_HARDWARE_FAIL; + break; + } + vga.scan_len = u0; + vga.reconfig.display = 1; + vga.reconfig.re_init = 1; + break; + + case 3: /* get maximum config */ + u0 = max_bytes; + u1 = max_pixels; + break; + + default: + err_code = VBE_ERROR_GENERAL_FAIL; /* invalid subfunction */ + } + + if(!err_code) { + u2 = (max_mem / u0) / planes; /* max scan lines */ + } + + } + + if(!err_code) { + _BX = u0; + _CX = u1; + _DX = u2; + } + +#ifdef DEBUG_VBE + v_printf( + "VBE: [0x%02x.%u] vbe_scan_length: return value %d, bx = 0x%04x, cx = 0x%04x, dx = 0x%04x\n", + (unsigned) _AL, sub_func, err_code, (unsigned) _BX, (unsigned) _CX, (unsigned) _DX + ); +#endif + + return err_code; +} + + +/* + * VBE function 0x07 + * + * Set or get the display start. + * - does currently not work in text modes - + * + * arguments: + * sub_func - subfunction (0 = set, 1 = get) + * x, y - display start + * + */ + +int vbe_display_start(unsigned sub_func, unsigned x, unsigned y) +{ + int err_code = 0; + unsigned u; + int direct = 0; + + sub_func &= ~0x80; /* we don't have vertical retrace anyway */ + if(sub_func & 0x40) { + sub_func &= ~0x40; + direct = 1; + } + +#ifdef DEBUG_VBE + if(sub_func) { + v_printf( + "VBE: [0x%02x.%u] vbe_display_start::get: bl = 0x%02x\n", + (unsigned) _AL, sub_func, (unsigned) _BL + ); + } + else { + v_printf( + "VBE: [0x%02x.%u] vbe_display_start::set: bl = 0x%02x, cx = 0x%04x, dx = 0x%04x\n", + (unsigned) _AL, sub_func, (unsigned) _BL, (unsigned) _CX, (unsigned) _DX + ); + } + if(direct) { + v_printf("VBE: [0x%02x.%u] vbe_display_start: accessed via direct call\n", (unsigned) _AL, sub_func); + } +#endif + + if(sub_func > 1) { + err_code = VBE_ERROR_GENERAL_FAIL; /* invalid subfunction */ + } + else if(sub_func) { + u = vga.display_start / vga.scan_len; + _DX = u; + u = vga.display_start % vga.scan_len; + if(vga.pixel_size > 8) u /= (vga.pixel_size + 7) >> 3; + _CX = u; + } + else { + if(direct) { + vga.display_start = ((y << 16) + x) << 2; + } + else { + if(vga.pixel_size > 8) x *= (vga.pixel_size + 7) >> 3; + u = y * vga.scan_len + x; + vga.display_start = u; + } + } + +#ifdef DEBUG_VBE + if(sub_func) { + v_printf( + "VBE: [0x%02x.%u] vbe_display_start::get: return value %d, cx = 0x%04x, dx = 0x%04x\n", + (unsigned) _AL, sub_func, err_code, (unsigned) _CX, (unsigned) _DX + ); + } + else { + v_printf( + "VBE: [0x%02x.%u] vbe_display_start::set: return value %d\n", + (unsigned) _AL, sub_func, err_code + ); + } +#endif + + return err_code; +} + + +/* + * VBE function 0x08 + * + * Set or get DAC palette format. + * + * arguments: + * sub_func - subfunction (0 = set, 1 = get) + * bits - new DAC width (should 6 or 8; our DAC emulation supports 4...8) + * + */ + +int vbe_dac_format(unsigned sub_func, unsigned bits) +{ + int err_code = 0; + +#ifdef DEBUG_VBE + if(sub_func) { + v_printf( + "VBE: [0x%02x.%u] vbe_dac_format::get: bl = 0x%02x\n", + (unsigned) _AL, sub_func, (unsigned) _BL + ); + } + else { + v_printf( + "VBE: [0x%02x.%u] vbe_dac_format::set: bl = 0x%02x, bh = 0x%02x\n", + (unsigned) _AL, sub_func, (unsigned) _BL, (unsigned) _BH + ); + } +#endif + + if(sub_func > 1) { + err_code = VBE_ERROR_GENERAL_FAIL; /* invalid subfunction */ + } + else { + if(vga.mode_type == YUV || vga.mode_type == DIRECT || (vga.mode_type >= P15 && vga.mode_type <= P32)) { + err_code = VBE_ERROR_MODE_FAIL; + } + if(sub_func) { + _BH = vga.dac.bits; + } + else { + DAC_set_width(bits); + _BH = vga.dac.bits; + } + } + +#ifdef DEBUG_VBE + if(sub_func) { + v_printf( + "VBE: [0x%02x.%u] vbe_dac_format::get: return value %d, bh = 0x%02x\n", + (unsigned) _AL, sub_func, err_code, (unsigned) _BH + ); + } + else { + v_printf( + "VBE: [0x%02x.%u] vbe_dac_format::set: return value %d, bh = 0x%02x\n", + (unsigned) _AL, sub_func, err_code, (unsigned) _BH + ); + } +#endif + + return err_code; +} + + +/* + * VBE function 0x09 + * + * Set or get palette data. + * We don't support a secondary palette. + * + * arguments: + * sub_func - subfunction (0 = set, 1 = get) + * len - number of registers to update + * first - the first palette to update + * buffer - table with RGB values (4 bytes per palette, lowest byte for alignment) + * + */ + +int vbe_palette_data(unsigned sub_func, unsigned len, unsigned first, unsigned int buffer) +{ + int err_code = 0; + DAC_entry dac; + + if(sub_func & 0x40) { /* called via pm interface */ + unsigned int base; + unsigned ofs; + + sub_func &= ~0x40; + ofs = (_SI << 16) + _DI; + base = GetSegmentBase(_BP); + buffer = base + ofs; +#ifdef DEBUG_VBE + v_printf( + "VBE: [0x%02x.%u] vbe_palette_data: called via pm interface, es.sel = 0x%04x, es.base = %x, es.ofs = 0x%08x\n", + (unsigned) _AL, sub_func, (unsigned) _BP, base, ofs + ); +#endif + } + +#ifdef DEBUG_VBE + v_printf( + "VBE: [0x%02x.%u] vbe_palette_data: bl = 0x%02x, cx = 0x%04x, dx = 0x%04x, es:di = 0x%04x:0x%04x\n", + (unsigned) _AL, sub_func, (unsigned) _BL, (unsigned) _CX, (unsigned) _DX, (unsigned) _ES, (unsigned) _DI + ); +#endif + + switch(sub_func) { + case 0: + case 0x80: /* set palette */ + while(first < 256 && len) { + DAC_set_entry(first, READ_BYTE(buffer+2), READ_BYTE(buffer+1), + READ_BYTE(buffer+0)); /* index, r, g, b */ + first++; len--; buffer += 4; + } + break; + + case 1: + while(first < 256 && len) { + DAC_get_entry(&dac, first); + WRITE_BYTE(buffer+2, dac.r); + WRITE_BYTE(buffer+1, dac.g); + WRITE_BYTE(buffer+0, dac.b); + first++; len--; buffer += 4; + } + break; + + case 2: + case 3: + err_code = VBE_ERROR_HARDWARE_FAIL; /* no secondary palette */ + break; + + default: + err_code = VBE_ERROR_GENERAL_FAIL; /* invalid subfunction */ + } + +#ifdef DEBUG_VBE + v_printf( + "VBE: [0x%02x.%u] vbe_palette_data: return value %d\n", + (unsigned) _AL, sub_func, err_code + ); +#endif + + return err_code; +} + + +/* + * VBE function 0x0a + * + * Return VBE protected mode interface. + * This function fails if X { pm_interface } in dosemu.conf is 'off'. + * + * arguments: + * sub_func - subfunction (0 = get) + * + */ + +int vbe_pm_interface(unsigned sub_func) +{ + int err_code = 0; + +#ifdef DEBUG_VBE + v_printf( + "VBE: [0x%02x.%u] vbe_pm_interface: bl = 0x%02x\n", + (unsigned) _AL, sub_func, (unsigned) _BL + ); +#endif + + if(sub_func == 0) { + if(vgaemu_bios.vbe_pm_interface_len && config.X_pm_interface) { + _CX = vgaemu_bios.vbe_pm_interface_len; + _ES = 0xc000; + _DI = vgaemu_bios.vbe_pm_interface; + } + else { + err_code = VBE_ERROR_UNSUP; + } + + } else { + err_code = VBE_ERROR_GENERAL_FAIL; /* invalid subfunction */ + } + +#ifdef DEBUG_VBE + v_printf( + "VBE: [0x%02x.%u] vbe_pm_interface: return value %d, cx = 0x%04x, es:di = 0x%04x:0x%04x\n", + (unsigned) _AL, sub_func, err_code, (unsigned) _CX, (unsigned) _ES, (unsigned) _DI + ); +#endif + + return err_code; +} + + +/* + * VBE/PM function 0x10 + * + * Set or get display power state. + * This just sets a variable in the vga state. It's up to + * the frontend to do some useful things. + * + * arguments: + * sub_func - subfunction (0 = get info, 1 = set, 2 = get) + * state - power state (bitmask: 0, 1, 2, 4, 8) + * + */ + +int vbe_power_state(unsigned sub_func, unsigned state) +{ + int err_code = 0; + +#ifdef DEBUG_VBE + v_printf( + "VBE: [0x%02x.%u] vbe_power_state: bl = 0x%02x, bh = 0x%02x\n", + (unsigned) _AL, sub_func, (unsigned) _BL, (unsigned) _BH + ); +#endif + + switch(sub_func) { + case 0: /* get capabilities */ + _BL = 0x20; /* version (2.0) */ + _BH = 0x0f; /* support everything (bit 0 - 3: standby, suspend, off, reduced) */ + break; + + case 1: + if(state == 0 || state == 1 || state == 2 || state == 4 || state == 8) { + vga.power_state = state; + } + else { + err_code = VBE_ERROR_GENERAL_FAIL; + } + break; + + case 2: + _BH = vga.power_state; + break; + + default: + err_code = VBE_ERROR_GENERAL_FAIL; /* invalid subfunction */ + } + +#ifdef DEBUG_VBE + v_printf( + "VBE: [0x%02x.%u] vbe_power_state: return value %d, bl = 0x%02x, bh = 0x%02x\n", + (unsigned) _AL, sub_func, err_code, (unsigned) _BL, (unsigned) _BH + ); +#endif + + return err_code; +} + diff --git a/src/base/dev/vga/vesabios.S b/src/base/dev/vga/vesabios.S new file mode 100644 index 0000000..8e13bfb --- /dev/null +++ b/src/base/dev/vga/vesabios.S @@ -0,0 +1,109 @@ +/* + * vesabios.S + * DANG_BEGIN_MODULE + * + * REMARK + * The VGA/VESA BIOS for VGAEmu. + * + * This is what DOS applications see as their video BIOS. It will be + * mapped at 0xc000:0x0000 and typically be less than one page in size. + * This video BIOS is write-protected, write accesses to this memory + * are emulated by vesa_emu_fault() in env/video/vesa.c. + * + * It doesn't have much functionality, just stores the VESA mode table + * and the protected mode interface necessary for VESA BIOS Extension (VBE) + * support and the VGA fonts. + * + * Note that the video BIOS is extended by vbe_init() in env/video/vesa.c. + * + * /REMARK + * DANG_END_MODULE + * + * This is vgaemu's VESA BIOS (C000:0 .. C800:0) + * + * Copyright (C) 1995 1996, Erik Mouw and Arjan Filius + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * email: J.A.K.Mouw@et.tudelft.nl, I.A.Filius@et.tudelft.nl + * + * + * DANG_BEGIN_CHANGELOG + * + * 1997/07/07: Did some cleaning up and removed lots of #define's. Some + * parts are now dynamically configurable; they are set up in vbe_init() + * in env/video/vesa.c. + * -- sw (Steffen Winterfeldt ) + * + * 2001/01/17: Converted to gas. Bart Oldeman . + * + * DANG_END_CHANGELOG + * + * + * We must compile this with gas/ld as follows: + * gcc -E vesabios.S >vesabios.s + * as -o vesabios.o vesabios.s + * + */ + +#include "memory.h" + +.code16 +.text + .globl _start +_start: + + .globl vgaemu_bios_start + .globl vgaemu_bios_prod_name + .globl vgaemu_bios_win_func + .globl vgaemu_bios_end + +vgaemu_bios_start: + .byte 0x55, 0xaa /* BIOS signature, required for BIOS extensions */ + .byte 0x00 /* BIOS extension length in units of 512 bytes */ + lret /* BIOS init function */ + nop + +vgaemu_bios_prod_name: + .ascii "DOSEMU VGA/VBE 2.0 Emulator" + .byte 0x00 + + .ascii "DOSEMU VGAemu. (C) 1995 1996, " + .ascii "I.A. Filius and J.A.K. Mouw" + .byte 0x00 + + .ascii "VESA BIOS Extensions V2.0 for DOSEMU." + .byte 0x0d, 0x0a + .ascii "Copyright (c) 1997 Steffen Winterfeldt" + .byte 0x00 + +vgaemu_bios_win_func: + pushw %bx + /* 0x40 is our own special code, (cf. vesa.c), + * make it 0x00 if you think it causes problems + */ + orb $0x40,%bh + mov $0x4f05,%ax + int $0x10 + popw %bx + lret + +vgaemu_bios_end: + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/base/dev/vga/vesabios_offsets.h b/src/base/dev/vga/vesabios_offsets.h new file mode 100644 index 0000000..37c9dce --- /dev/null +++ b/src/base/dev/vga/vesabios_offsets.h @@ -0,0 +1,4 @@ +extern const unsigned vgaemu_bios_start; +extern const unsigned vgaemu_bios_prod_name; +extern const unsigned vgaemu_bios_win_func; +extern const unsigned vgaemu_bios_end; diff --git a/src/base/dev/vga/vesabios_pm.S b/src/base/dev/vga/vesabios_pm.S new file mode 100644 index 0000000..7bc24eb --- /dev/null +++ b/src/base/dev/vga/vesabios_pm.S @@ -0,0 +1,91 @@ +/* + * DANG_BEGIN_MODULE + * + * REMARK + * The protected mode code for the VESA BIOS Extensions. + * + * We define some functions here that are required to support the + * VBE 2.0 protected mode interface. + * + * The whole code _must_ be fully relocatable. It will be copied into + * the video BIOS during VBE initialization (cf. vbe_init()). + * + * /REMARK + * DANG_END_MODULE + * + * + * Copyright (c) 1997 Steffen Winterfeldt, + * email: Steffen Winterfeldt + * + * DANG_BEGIN_CHANGELOG + * + * 1998/12/14: Another attempt to fix the pm version of the + * VBE set palette function. Removed some old code blocks. + * -- sw + * + * DANG_END_CHANGELOG + * + * + */ + + + .code32 + .global _start +_start: // dummy + +/* + * We simply call our own BIOS via int 0x10, ax = 0x4fxx but use + * subfunction codes (typically in BH or BL) of 0x40 instead of 0x00 to + * allow some nice DEBUG messages (cf. env/video/vesa.c). If you think + * this causes problems, use 0x00 instead. + */ + + .globl vgaemu_bios_pm_interface + .globl vgaemu_bios_pm_interface_end + + +vgaemu_bios_pm_interface: + .short vbpi_win_func - vgaemu_bios_pm_interface + .short vbpi_display_start - vgaemu_bios_pm_interface + .short vbpi_set_palette - vgaemu_bios_pm_interface + .short 0 /* list of needed memory areas & ports; 0 -> none */ + /* Note that some applications will ignore the PM interface + * if you actually put in a nonempty list! + */ + +vbpi_win_func: + pushl %ebx + movl $0x4000,%ebx + movl $0x4f05,%eax + int $0x10 + popl %ebx + ret + +vbpi_display_start: + pushl %ebx + movl $0x40,%ebx + movl $0x4f07,%eax + int $0x10 + popl %ebx + ret + +vbpi_set_palette: + pushl %ebp + pushl %esi + pushl %ebx + movb $0x40,%bl + movl $0x4f09,%eax + movl %edi,%esi + shrl $0x10,%esi + movw %es,%bp + int $0x10 + popl %ebx + popl %esi + popl %ebp + ret + +vgaemu_bios_pm_interface_end: + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/base/dev/vga/vesabios_pm_offsets.h b/src/base/dev/vga/vesabios_pm_offsets.h new file mode 100644 index 0000000..3ff2b4d --- /dev/null +++ b/src/base/dev/vga/vesabios_pm_offsets.h @@ -0,0 +1,2 @@ +extern const unsigned vgaemu_bios_pm_interface; +extern const unsigned vgaemu_bios_pm_interface_end; diff --git a/src/base/dev/vga/vgaemu.c b/src/base/dev/vga/vgaemu.c new file mode 100644 index 0000000..ab66299 --- /dev/null +++ b/src/base/dev/vga/vgaemu.c @@ -0,0 +1,3180 @@ +/* + * Containing modifications of first step VGA mode 12h support which is + * (C) Copyright 2000 by David Hindman. + * + * Final enhanced implementation of PL4/PL2/PL1 and mode-X vga modes + * (one of it mode 12h) emulation was done by and is + * (C) Copyright 2000 Bart Oldeman + * (C) Copyright 2000 Steffen Winterfeldt + * + * These modifications are distributed under the terms of the + * GNU General Public License. The copyright to the modifications + * is hereby assigned to the "DOSEMU-Development-Team" + * + */ + +/* + * DANG_BEGIN_MODULE + * + * REMARK + * The VGA emulator for DOSEmu. + * + * Emulated are the video memory and the VGA register set (CRTC, DAC, etc.). + * Parts of the hardware emulation is done in separate files (attremu.c, + * crtcemu.c, dacemu.c and seqemu.c). + * + * VGAEmu uses the video BIOS code in base/bios/int10.c and env/video/vesa.c. + * + * For an excellent reference to programming SVGA cards see Finn Thgersen's + * VGADOC4, available at http://www.datashopper.dk/~finth + * + * /REMARK + * DANG_END_MODULE + * + * + * Copyright (C) 1995 1996, Erik Mouw and Arjan Filius + * Copyright (c) 1997 Steffen Winterfeldt + * + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * email: J.A.K.Mouw@et.tudelft.nl, I.A.Filius@et.tudelft.nl + * + * + * DANG_BEGIN_CHANGELOG + * + * 1996/05/06: + * Adam Moss (aspirin@tigerden.com): + * - Fixed method 2 of vgaemu_get_changes_in_pages() + * - Very simplified vgaemu_get_changes_and_update_XImage_0x13() + * Erik Mouw: + * - Split VGAemu in three files + * - Some minor bug fixes + * + * 1996/05/09: + * Adam Moss: + * - Changed the way that vgaemu_get_changes() and + * vgaemu_get_changes_and_update_XImage_0x13() return an area. + * - Minor bug fixes + * + * 1996/05/20: + * Erik: + * - Made VESA modes start to work properly! + * Adam: + * - Fixed method 0 of vgaemu_get_changes_in_pages() + * - Fixed vgaemu_get_changes_in_pages to work faster/better with + * SVGA modes + * + * 28 Dec 1996 + * Erik Mouw (J.A.K.Mouw@et.tudelft.nl): + * - Added sequencer emulation calls + * - Added IO port register calls in vga_emu_init() + * + * 1997/06/15: Major rework; the interface to the outside now uses the + * global variable `vga' which holds all VGA related information. + * The update function now works for all types of modes except for + * Hercules modes. + * The VGA memory is now always executable. + * -- sw (Steffen Winterfeldt ) + * + * 1997/07/07: Added linear frame buffer (LFB) support, simplified and + * generalized video mode definitions (now possible via dosemu.conf). + * vga_emu_init() now gets some info about the display it is actually + * running on (needed for VESA emulation). + * vga_emu_setmode() now accepts VESA mode numbers. + * Hi/true-color modes now work. + * -- sw + * + * 1998/10/25: Added changed_vga_colors(). It replaces the former + * DAC_get_dirty_entry() functions. PEL mask support now works. + * -- sw + * + * 1998/11/01: Added so far unsupported video modes (CGA, 1 bit & 2 bit VGA). + * -- sw + * + * 1998/12/12: Text mode geometry changes are no longer done via mode set. + * -- sw + * + * 1999/01/05: Some changes needed to support Hercules mode and various changes + * due to the improved VGA register compatibility. + * -- sw + * + * 2000/05/07: First step VGA 0x12 mode emulation added by + * David Pinson & David Hindman + * + * 2000/05/09: Reworked the mode 0x12 code to work in all PL4 modes and + * integrated it better into VGAEMU. It works now in (16-bit-)DPMI mode. + * Fixed quite a few bugs in the code. + * -- sw + * + * 2000/05/10: Added Bart Oldeman's patch + * for better instruction emulation. + * -- sw + * + * 2000/05/11: Reworked the mapping/page protection stuff. It now remembers + * each page protection and makes only the necessary *_mapping() calls. + * Also, there is no longer a framebuffer available for modes with + * color depth < 8. The PL2 mode (0x0f) now uses the code emulation code, too. + * And, the 'mapself' mapping works again. Search for 'evil' to see the changes. + * Included Bart's 3rd patch. + * --sw + * + * 2000/05/18: Added functions to display fonts in graphics modes. + * --sw + * + * 2000/05/18: Moved instr_sim and friends to instremu.c. + * --beo + * + * 2000/06/01: Over the last weeks: speeded up Logical_VGA_{read,write}. + * --beo + * + * 2001/03/14: Added vgaemu_put_pixel and vgaemu_get_pixel. + * --beo + * + * DANG_END_CHANGELOG + * + */ + + +/* + Notes on VGA read/write emulation (as used for PL4 modes like 0x12) and mode-X. + + The memory layout always reflects the setting of the GFX Read Map Select + register. So in principle we can safely allow reads to the VGA memory. This + works of course only in read mode 0. + + But even in read mode 0 there is a problem with correct updates of the latch + registers. Instead, we block read accesses as well. Note that even in this + case the VGA memory is always mapped according to GFX Read Map Select. + + 2000/05/09, sw + update + 2000/06/01, bo +*/ + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * some configurable options + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Define some of the following to generate various debug output. + * A useful debug level is 1; level 2 can easily lead to insanely + * large log files. + */ +#define DEBUG_IO 0 /* (<= 2) port emulation */ +#define DEBUG_MAP 1 /* (<= 4) VGA memory mapping */ +#define DEBUG_UPDATE 0 /* (<= 1) screen update process */ +#define DEBUG_BANK 0 /* (<= 2) bank switching */ +#define DEBUG_COL 0 /* (<= 1) color interpretation changes */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#if !defined True +#define False 0 +#define True 1 +#endif + +#define RW VGA_PROT_RW +#define RO VGA_PROT_RO +#define NONE VGA_PROT_NONE +#define DEF_PROT (vga.inst_emu==EMU_ALL_INST ? NONE : RO) + +/* + * We add PROT_EXEC just because pages should be executable. Of course + * Intel's x86 processors do not all support non-executable pages, but anyway... + */ +#define VGA_EMU_RW_PROT (PROT_READ | PROT_WRITE | PROT_EXEC) +#define VGA_EMU_RO_PROT (PROT_READ | PROT_EXEC) +#define VGA_EMU_NONE_PROT 0 + +#define vga_msg(x...) v_printf("VGAEmu: " x) + +#if DEBUG_IO >= 1 +#define vga_deb_io(x...) v_printf("VGAEmu: " x) +#else +#define vga_deb_io(x...) +#endif + +#if DEBUG_IO >= 2 +#define vga_deb2_io(x...) v_printf("VGAEmu: " x) +#else +#define vga_deb2_io(x...) +#endif + +#if DEBUG_MAP >= 1 +#define vga_deb_map(x...) v_printf("VGAEmu: " x) +#else +#define vga_deb_map(x...) +#endif + +#if DEBUG_MAP >= 2 +#define vga_deb2_map(x...) v_printf("VGAEmu: " x) +#else +#define vga_deb2_map(x...) +#endif + +#if DEBUG_UPDATE >= 1 +#define vga_deb_update(x...) v_printf("VGAEmu: " x) +#else +#define vga_deb_update(x...) +#endif + +#if DEBUG_BANK >= 1 +#define vga_deb_bank(x...) v_printf("VGAEmu: " x) +#else +#define vga_deb_bank(x...) +#endif + +#if DEBUG_BANK >= 2 +#define vga_deb2_bank(x...) v_printf("VGAEmu: " x) +#else +#define vga_deb2_bank(x...) +#endif + +#if DEBUG_COL >= 1 +#define vga_deb_col(x...) v_printf("VGAEmu: " x) +#else +#define vga_deb_col(x...) +#endif + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpu.h" /* root@sjoerd: for context structure */ +#include "init.h" +#include "emu.h" +#include "int.h" +#include "emudpmi.h" +#include "port.h" +#include "video.h" +#include "bios.h" +#include "memory.h" +#include "render.h" +#include "vgaemu.h" +#include "priv.h" +#include "mapping.h" +#include "utilities.h" +#include "instremu.h" +#include "cpu-emu.h" +#include "kvm.h" +#include "smalloc.h" + +/* table with video mode definitions */ +#include "vgaemu_modelist.h" + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* + * functions local to this file + */ + +static int vga_emu_protect(unsigned, unsigned, int); +static int vga_emu_map(unsigned, unsigned); +static int _vga_emu_adjust_protection(unsigned page, unsigned mapped_page, + int prot, int dirty); +static void _vgaemu_dirty_page(int page, int dirty); +#if 0 +static int vgaemu_unmap(unsigned); +#endif +#if DEBUG_UPDATE >= 1 +static void print_dirty_map(void); +#endif +#if DEBUG_MAP >= 3 +static void print_prot_map(void); +#endif +static int vga_emu_setup_mode(vga_mode_info *, int, unsigned, unsigned, unsigned); +static void vga_emu_setup_mode_table(void); +static void vgaemu_adjust_instremu(int value); + +static Bit32u rasterop(Bit32u value); +static pthread_mutex_t prot_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_rwlock_t mode_mtx = PTHREAD_RWLOCK_INITIALIZER; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* + * holds all VGA data + */ +vga_type vga; + +/* + * the structure of our VGA BIOS; + * initialized by vbe_init() + */ +vgaemu_bios_type vgaemu_bios; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * DANG_BEGIN_FUNCTION VGA_emulate_outb + * + * description: + * Emulates writes to VGA ports. + * This is a hardware emulation function. + * + * arguments: + * port - The port being written to. + * value - The value written, + * + * DANG_END_FUNCTION + * + */ + +int VGA_emulate_outb(ioport_t port, Bit8u value, void *arg) +{ + vga_deb2_io("VGA_emulate_outb: port[0x%03x] = 0x%02x\n", (unsigned) port, (unsigned) value); + + switch(port) { + case HERC_MODE_CTRL: /* 0x3b8*/ + if(vga.config.mono_port) Herc_set_mode_ctrl(value); + break; + + case HERC_CFG_SWITCH: /* 0x3bf*/ + if(vga.config.mono_port) Herc_set_cfg_switch(value); + break; + + case ATTRIBUTE_INDEX: /* 0x3c0 */ + Attr_write_value(value); + break; + + case ATTRIBUTE_DATA: /* 0x3c1 */ + vga_deb_io("VGA_emulate_outb: Attribute Controller data port is read-only!\n"); + break; + + case MISC_OUTPUT_W: /* 0x3c2 */ + Misc_set_misc_output(value); + break; + + case SEQUENCER_INDEX: /* 0x3c4 */ + Seq_set_index(value); + break; + + case SEQUENCER_DATA: /* 0x3c5 */ + pthread_rwlock_wrlock(&mode_mtx); + Seq_write_value(value); + pthread_rwlock_unlock(&mode_mtx); + break; + + case DAC_PEL_MASK: /* 0x3c6 */ + DAC_set_pel_mask(value); + break; + + case DAC_READ_INDEX: /* 0x3c7 */ + DAC_set_read_index(value); + break; + + case DAC_WRITE_INDEX: /* 0x3c8 */ + DAC_set_write_index(value); + break; + + case DAC_DATA: /* 0x3c9 */ + DAC_write_value(value); + break; + + case GFX_INDEX: /* 0x3ce */ + GFX_set_index(value); + break; + + case GFX_DATA: /* 0x3cf */ + GFX_write_value(value); + break; + + case CRTC_INDEX_MONO: /* 0x3b4 */ + if(vga.config.mono_port) CRTC_set_index(value); + break; + + case CRTC_DATA_MONO: /* 0x3b5 */ + if(vga.config.mono_port) CRTC_write_value(value); + break; + + case FEATURE_CONTROL_W_MONO: /* 0x3ba */ + if(vga.config.mono_port) Misc_set_feature_ctrl(value); + break; + + case CRTC_INDEX: /* 0x3d4 */ + if(!vga.config.mono_port) CRTC_set_index(value); + break; + + case CRTC_DATA: /* 0x3d5 */ + pthread_rwlock_wrlock(&mode_mtx); + if(!vga.config.mono_port) CRTC_write_value(value); + pthread_rwlock_unlock(&mode_mtx); + break; + + case COLOR_SELECT: /* 0x3d9 */ + if(!vga.config.mono_port) Misc_set_color_select(value); + break; + + case FEATURE_CONTROL_W: /* 0x3da */ + if(!vga.config.mono_port) Misc_set_feature_ctrl(value); + break; + + default: + vga_deb_io( + "VGA_emulate_outb: write access not emulated (port[0x%03x] = 0x%02x)\n", + (unsigned) port, (unsigned) value + ); + return -1; + } + return 0; +#if DEBUG_MAP >= 3 + print_prot_map(); +#endif +} + + +static void VGA_emulate_outb_handler(ioport_t port, Bit8u value, void *arg) +{ + VGA_emulate_outb(port, value, arg); +} + +static void VGA_emulate_outw_handler(ioport_t port, Bit16u value, void *arg) +{ + VGA_emulate_outb_handler(port, value, arg); + VGA_emulate_outb_handler(port+1, value>>8, arg); +} + +/* + * DANG_BEGIN_FUNCTION VGA_emulate_inb + * + * description: + * Emulates reads from VGA ports. + * This is a hardware emulation function. + * + * arguments: + * port - The port being read from. + * + * DANG_END_FUNCTION + * + */ + +int VGA_emulate_inb(ioport_t port, void *arg) +{ + Bit8u uc = 0xff; + + switch(port) { + case HERC_MODE_CTRL: /* 0x3b8*/ + if(vga.config.mono_port) uc = Herc_get_mode_ctrl(); + break; + + case ATTRIBUTE_INDEX: /* 0x3c0 */ + uc = Attr_get_index(); + break; + + case ATTRIBUTE_DATA: /* 0x3c1 */ + uc = Attr_read_value(); + break; + + case INPUT_STATUS_0: /* 0x3c2 */ + uc = Misc_get_input_status_0(); + break; + + case SEQUENCER_INDEX: /* 0x3c4 */ + uc = Seq_get_index(); + break; + + case SEQUENCER_DATA: /* 0x3c5 */ + uc = Seq_read_value(); + break; + + case DAC_PEL_MASK: /* 0x3c6 */ + uc = DAC_get_pel_mask(); + break; + + case DAC_STATE: /* 0x3c7 */ + uc = DAC_get_state(); + break; + + case DAC_WRITE_INDEX: /* 0x3c8 */ + /* this is undefined, but we have to do something */ + break; + + case DAC_DATA: /* 0x3c9 */ + uc = DAC_read_value(); + break; + + case FEATURE_CONTROL_R: /* 0x3ca */ + uc = Misc_get_feature_ctrl(); + break; + + case MISC_OUTPUT_R: /* 0x3cc */ + uc = Misc_get_misc_output(); + break; + + case GFX_INDEX: /* 0x3ce */ + uc = GFX_get_index(); + break; + + case GFX_DATA: /* 0x3cf */ + uc = GFX_read_value(); + break; + + case CRTC_INDEX_MONO: /* 0x3b4 */ + if(vga.config.mono_port) uc = CRTC_get_index(); + break; + + case CRTC_DATA_MONO: /* 0x3b5 */ + if(vga.config.mono_port) uc = CRTC_read_value(); + break; + + case INPUT_STATUS_1_MONO: /* 0x3ba */ + if(vga.config.mono_port) uc = Misc_get_input_status_1(); + break; + + case CRTC_INDEX: /* 0x3d4 */ + if(!vga.config.mono_port) uc = CRTC_get_index(); + break; + + case CRTC_DATA: /* 0x3d5 */ + if(!vga.config.mono_port) uc = CRTC_read_value(); + break; + + case INPUT_STATUS_1: /* 0x3da */ + if(!vga.config.mono_port) uc = Misc_get_input_status_1(); + break; + + default: + vga_deb_io( + "VGA_emulate_inb: read access not emulated (port[0x%03x] = 0x%02x)\n", + (unsigned) port, (unsigned) uc + ); + return -1; + } + + vga_deb2_io("VGA_emulate_inb: port[0x%03x] = 0x%02x\n", (unsigned) port, (unsigned) uc); + + return uc; +} + +static Bit8u VGA_emulate_inb_handler(ioport_t port, void *arg) +{ + return VGA_emulate_inb(port, arg); +} + +static Bit16u VGA_emulate_inw_handler(ioport_t port, void *arg) +{ + Bit16u v = VGA_emulate_inb_handler(port, arg); + return v | (VGA_emulate_inb_handler(port+1, arg)<<8); +} + + +/* + * This routine emulates a logical VGA read (CPU read within the + * VGA memory range a0000-bffff). This usually doesn't just read the + * byte from memory. Instead, the VGA controller mediates the + * read in a very complex fashion that depends on the settings of the + * VGA control registers. So we will call this a Logical VGA read. + * + * Much of the code in this routine is adapted from bochs. + * Bochs is Copyright (C) 2000 MandrakeSoft S.A. and distributed under LGPL. + * Bochs was originally authored by Kevin Lawton. + */ +/* converts a 4-bit color to an 8-pixels representation in PL-4 modes */ +static Bit32u color2pixels[16] = {0,0xff,0xff00,0xffff,0xff0000,0xff00ff,0xffff00, + 0x00ffffff,0xff000000,0xff0000ff,0xff00ff00,0xff00ffff, + 0xffff0000,0xffff00ff,0xffffff00,0xffffffff}; + +#define SetReset vga.gfx.set_reset +#define EnableSetReset vga.gfx.enable_set_reset +#define ColorCompare vga.gfx.color_compare +#define DataRotate vga.gfx.data_rotate +#define RasterOp vga.gfx.raster_op +#define ReadMapSelect vga.gfx.read_map_select +#define WriteMode vga.gfx.write_mode +#define ReadMode vga.gfx.read_mode +#define ColorDontCare vga.gfx.color_dont_care +#define BitMask vga.gfx.bitmask +#define MapMask vga.seq.map_mask + +#define VGALatch vga.latch + +static unsigned char Logical_VGA_read(unsigned offset) +{ + instr_emu_sim_reset_count(VGA_EMU_INST_EMU_COUNT); + + switch (ReadMode) { + case 0: /* read mode 0 */ + VGALatch[0] = vga.mem.base[ offset]; + VGALatch[1] = vga.mem.base[1*65536 + offset]; + VGALatch[2] = vga.mem.base[2*65536 + offset]; + VGALatch[3] = vga.mem.base[3*65536 + offset]; + return(VGALatch[ReadMapSelect]); + break; + + case 1: /* read mode 1 */ + { + Bit32u latch; + Bit8u retval; + + latch = (((VGALatch[0] = vga.mem.base[ offset]) | + ((VGALatch[1] = vga.mem.base[1*65536 + offset]) << 8) | + ((VGALatch[2] = vga.mem.base[2*65536 + offset]) << 16) | + ((uint32_t)(VGALatch[3] = vga.mem.base[3*65536 + offset]) << 24) ) & + color2pixels[ColorDontCare & 0xf]) ^ + color2pixels[ColorCompare & ColorDontCare & 0xf]; + /* XORing gives all bits that are different */ + /* after combining through OR the "equal" bits are zero, hence a NOT */ + + retval = ~((latch & 0xff) | ((latch>>8) & 0xff) | + ((latch>>16) & 0xff) | ((latch>>24) & 0xff)); +#if 0 + vga_msg( + "read mode 1: col_cmp 0x%x, col_dont 0x%x, latches = %02x %02x %02x %02x, read = 0x%02x\n", + color_compare, color_dont_care, VGALatch[0], VGALatch[1], + VGALatch[2], VGALatch[3], retval + ); +#endif + return(retval); + } + break; + + default: + return(0); + } +} + +/* + * This routine emulates a logical VGA write (CPU write to within the + * VGA memory range a0000-bffff). This usually doesn't just place the + * byte into memory. Instead, the VGA controller mediates the + * write in a very complex fashion that depends on the settings of the + * VGA control registers. So we will call this a Logical VGA write, + * and the lower level thing which places bytes in video RAM we will + * call a Physical VGA write. + * + * Much of the code in this routine is adapted from bochs. + * Bochs is Copyright (C) 2000 MandrakeSoft S.A. and distributed under LGPL. + * Bochs was originally authored by Kevin Lawton. + */ + +static Bit32u rasterop(Bit32u value) +{ + Bit32u bitmask = BitMask, vga_latch; + memcpy(&vga_latch, VGALatch, 4); + + bitmask |= bitmask << 8; + bitmask |= bitmask << 16; /* bitmask extended over 4 bytes */ + switch (RasterOp) { + case 0: /* replace */ + return (value & bitmask) | (vga_latch & ~bitmask); + case 1: /* AND with latch data */ + return (value | ~bitmask) & vga_latch; + case 2: /* OR with latch data */ + return (value & bitmask) | vga_latch; + case 3: /* XOR with latch data */ + return (value & bitmask) ^ vga_latch; + } + return 0; +} + +static Bit32u Logical_VGA_CalcNewVal (unsigned char value) +{ + Bit32u enable_set_reset, new_val = 0; + Bit8u bitmask; + + switch (WriteMode) { + case 0: /* write mode 0 */ + /* perform rotate on CPU data in case its needed */ + new_val = (value >> DataRotate) | (value << (8 - DataRotate)); + new_val |= new_val<<8; + new_val |= new_val<<16; /* value extended over 4 bytes */ + enable_set_reset = color2pixels[EnableSetReset]; + return rasterop((new_val & ~enable_set_reset) | + (color2pixels[SetReset] & enable_set_reset)); + + case 1: /* write mode 1 */ + memcpy(&new_val, VGALatch, 4); + return new_val; + break; + + case 2: /* write mode 2 */ + return rasterop(color2pixels[value & 0xf]); + + case 3: /* write mode 3 */ + bitmask = BitMask; + /* perform rotate on CPU data */ + BitMask = ((value >> DataRotate) | (value << (8 - DataRotate))) & + bitmask; /* Set Bitmask temporarily */ + new_val = rasterop(color2pixels[SetReset]); + BitMask = bitmask; /* get old bitmask back */ + break; + + default: + break; + } + + return new_val; +} + +static void Logical_VGA_write(unsigned offset, unsigned char value) +{ + unsigned vga_page; + unsigned char *p; + Bit32u new_val; + + instr_emu_sim_reset_count(VGA_EMU_INST_EMU_COUNT); + + new_val = Logical_VGA_CalcNewVal(value); + + vga_page = offset >> 12; + p = (unsigned char *)(vga.mem.base + offset); + + if(MapMask & 0x01) { + *p = new_val; + } + if(MapMask & 0x02) { + p[0x10000] = new_val >> 8; + } + if(MapMask & 0x04) { + p[0x20000] = new_val >> 16; + } + if(MapMask & 0x08) { + p[0x30000] = new_val >> 24; + } + + if(MapMask) { + // not optimal, but works better with update function -- sw + if (debug_level('v') >= 9) + vga_deb_map("LogicalWrite dirty page %i\n", vga_page); + pthread_mutex_lock(&prot_mtx); + vga.mem.dirty_map[vga_page] = 1; + vga.mem.dirty_map[vga_page + 0x10] = 1; + vga.mem.dirty_map[vga_page + 0x20] = 1; + vga.mem.dirty_map[vga_page + 0x30] = 1; + pthread_mutex_unlock(&prot_mtx); + } + +} + +int vga_bank_access(dosaddr_t m) +{ + if (config.console_video) + return 0; + return (unsigned)(m - vga.mem.bank_base) < vga.mem.bank_len; +} + +int vga_read_access(dosaddr_t m) +{ + if (config.console_video) + return 0; + /* Using a planar mode */ + return vga_bank_access(m); +} + +int vga_write_access(dosaddr_t m) +{ + if (config.console_video) + return 0; + /* unmapped VGA memory, VGA BIOS, or a bank. Note that + * the bank can be write-protected even in non-planar mode. */ + if (m >= vga.mem.graph_base && + m < vga.mem.graph_base + vga.mem.graph_size) + return 1; + if (m >= 0xb8000 && m < 0xc0000 + (vgaemu_bios.pages<<12)) + return 1; + if (vga.mem.lfb_base_page && + (m >> PAGE_SHIFT) >= vga.mem.lfb_base_page && + (m >> PAGE_SHIFT) < vga.mem.lfb_base_page + vga.mem.pages) + return 1; + if (!config.umb_f0 && m >= 0xf0000 && m < 0xf4000) + return 1; + return 0; +} + +int vga_access(dosaddr_t r, dosaddr_t w) +{ + if (config.console_video) + return 0; + return (vga_read_access(r) | (vga_write_access(w) << 1)); +} + +unsigned char vga_read(dosaddr_t addr) +{ + if (!vga.inst_emu || !vga_read_access(addr)) + return READ_BYTE(addr); + return Logical_VGA_read(addr - vga.mem.bank_base); +} + +unsigned short vga_read_word(dosaddr_t addr) +{ + if (!vga.inst_emu) + return READ_WORD(addr); + return vga_read(addr) | vga_read(addr + 1) << 8; +} + +unsigned vga_read_dword(dosaddr_t addr) +{ + if (!vga.inst_emu) + return READ_DWORD(addr); + return vga_read_word(addr) | (unsigned)vga_read_word(addr + 2) << 16; +} + +static dosaddr_t vga_get_mem_base_offset(dosaddr_t addr) +{ + int i; + for(i = 0; i < VGAEMU_MAX_MAPPINGS; i++) { + dosaddr_t base = vga.mem.map[i].base_page << PAGE_SHIFT; + dosaddr_t end = base + (vga.mem.map[i].pages << PAGE_SHIFT); + if (addr >= base && addr < end) + return addr - base + (vga.mem.map[i].first_page << PAGE_SHIFT); + } + return (dosaddr_t)-1; +} + +void vga_mark_dirty(dosaddr_t vga_addr, int len) +{ + unsigned vga_page; + for (vga_page = vga_addr >> PAGE_SHIFT; + vga_page <= (vga_addr + len - 1) >> PAGE_SHIFT; vga_page++) + vgaemu_dirty_page(vga_page, 1); +} + +void vga_write(dosaddr_t addr, unsigned char val) +{ + if (!vga.inst_emu || !vga_bank_access(addr)) { + addr = vga_get_mem_base_offset(addr); + if (addr != (dosaddr_t)-1) { + vga.mem.base[addr] = val; + vga_mark_dirty(addr, 1); + } + return; + } + Logical_VGA_write(addr - vga.mem.bank_base, val); +} + +void vga_write_word(dosaddr_t addr, unsigned short val) +{ + if (!vga.inst_emu || !vga_bank_access(addr)) { + addr = vga_get_mem_base_offset(addr); + if (addr != (dosaddr_t)-1) { + UNIX_WRITE_WORD(&vga.mem.base[addr], val); + vga_mark_dirty(addr, 2); + } + return; + } + vga_write(addr, val & 0xff); + vga_write(addr + 1, val >> 8); +} + +void vga_write_dword(dosaddr_t addr, unsigned val) +{ + if (!vga.inst_emu || !vga_bank_access(addr)) { + addr = vga_get_mem_base_offset(addr); + if (addr != (dosaddr_t)-1) { + UNIX_WRITE_DWORD(&vga.mem.base[addr], val); + vga_mark_dirty(addr, 4); + } + return; + } + vga_write_word(addr, val & 0xffff); + vga_write_word(addr + 2, val >> 16); +} + +void memcpy_to_vga(dosaddr_t dst, const void *src, size_t len) +{ + int i; + if (!vga.inst_emu) { + dst = vga_get_mem_base_offset(dst); + if (dst != (dosaddr_t)-1) { + memcpy(&vga.mem.base[dst], src, len); + vga_mark_dirty(dst, len); + } + return; + } + for (i = 0; i < len; i++) + vga_write(dst + i, ((const unsigned char *)src)[i]); +} + +void memcpy_dos_to_vga(dosaddr_t dst, dosaddr_t src, size_t len) +{ + int i; + if (!vga.inst_emu) { + dst = vga_get_mem_base_offset(dst); + if (dst != (dosaddr_t)-1) { + MEMCPY_2UNIX(&vga.mem.base[dst], src, len); + vga_mark_dirty(dst, len); + } + return; + } + for (i = 0; i < len; i++) + vga_write(dst + i, READ_BYTE(src + i)); +} + +void memcpy_from_vga(void *dst, dosaddr_t src, size_t len) +{ + int i; + if (!vga.inst_emu) { + MEMCPY_2UNIX(dst, src, len); + return; + } + for (i = 0; i < len; i++) { + ((unsigned char *)dst)[i] = vga_read(src + i); + } +} + +void memcpy_dos_from_vga(dosaddr_t dst, dosaddr_t src, size_t len) +{ + int i; + if (!vga.inst_emu) { + MEMCPY_DOS2DOS(dst, src, len); + return; + } + for (i = 0; i < len; i++) { + WRITE_BYTE(dst + i, vga_read(src + i)); + } +} + +void vga_memcpy(dosaddr_t dst, dosaddr_t src, size_t len) +{ + int i; + if (!vga.inst_emu) { + dst = vga_get_mem_base_offset(dst); + if (dst != (dosaddr_t)-1) { + src = vga_get_mem_base_offset(src); + assert(src != (dosaddr_t)-1); + memmove(&vga.mem.base[dst], &vga.mem.base[src], len); + vga_mark_dirty(dst, len); + } + return; + } + for (i = 0; i < len; i++) + vga_write(dst + i, vga_read(src + i)); +} + +void vga_memset(dosaddr_t dst, unsigned char val, size_t len) +{ + int i; + if (!vga.inst_emu) { + dst = vga_get_mem_base_offset(dst); + if (dst != (dosaddr_t)-1) { + memset(&vga.mem.base[dst], val, len); + vga_mark_dirty(dst, len); + } + return; + } + for (i = 0; i < len; i++) + vga_write(dst + i, val); +} + +void vga_memsetw(dosaddr_t dst, unsigned short val, size_t len) +{ + if (!vga.inst_emu) { + dst = vga_get_mem_base_offset(dst); + if (dst != (dosaddr_t)-1) { + dosaddr_t dststart = dst; + while (len--) { + UNIX_WRITE_WORD(&vga.mem.base[dst], val); + dst += 2; + } + vga_mark_dirty(dststart, dst - dststart); + } + return; + } + while (len--) { + vga_write_word(dst, val); + dst += 2; + } +} + +void vga_memsetl(dosaddr_t dst, unsigned val, size_t len) +{ + if (!vga.inst_emu) { + dst = vga_get_mem_base_offset(dst); + if (dst != (dosaddr_t)-1) { + dosaddr_t dststart = dst; + while (len--) { + UNIX_WRITE_DWORD(&vga.mem.base[dst], val); + dst += 4; + } + vga_mark_dirty(dststart, dst - dststart); + } + return; + } + while (len--) { + vga_write_dword(dst, val); + dst += 4; + } +} + +/* + * DANG_BEGIN_FUNCTION vga_emu_fault + * + * description: + * vga_emu_fault() is used to catch video access, and handle it. + * This function is called from arch/linux/async/sigsegv.c::dosemu_fault1(). + * Now it catches only changes in a 4K page, but maybe it is useful to + * catch each video access. The problem when you do that is, you have to + * simulate each instruction which could write to the video memory. + * It is easy to get the place where the exception happens (scp->cr2), + * but what are those changes? + * An other problem is, it could eat a lot of time, but it does now also. + * + * MODIFICATION: VGA mode 12h under X is supported in exactly the + * way that was suggested above. Not every instruction needs to be + * simulated in order to make this feature useful, just the ones used to + * access video RAM by key applications (Borland BGI, Protel, etc.). + * + * MODIFICATION: all VGA modes now work and almost all instructions are + * simulated. + * + * arguments: + * scp - A pointer to a sigcontext_t holding some relevant data. + * + * DANG_END_FUNCTION + * + */ + +int vga_emu_fault(dosaddr_t lin_addr, unsigned err, cpuctx_t *scp) +{ + int i, j, pmode = scp != NULL; + unsigned page_fault, vga_page = 0, u; + unsigned char *cs_ip; +#if DEBUG_MAP >= 1 + static const char *txt1[VGAEMU_MAX_MAPPINGS + 1] = { "bank", "lfb", "some" }; + unsigned access_type = (err >> 1) & 1; +#endif + page_fault = lin_addr >> 12; + + for(i = 0; i < VGAEMU_MAX_MAPPINGS; i++) { + j = page_fault - vga.mem.map[i].base_page; + if(j >= 0 && j < vga.mem.map[i].pages) { + vga_page = j + vga.mem.map[i].first_page; + break; + } + } + + vga_deb_map("vga_emu_fault: %s%s access to %s region, address 0x%05x, page 0x%x, vga page 0x%x\n", + pmode ? "dpmi " : "", access_type? "write" : "read", + txt1[i], lin_addr, page_fault, vga_page + ); + + vga_deb2_map( + "vga_emu_fault: in_dpmi %d, err 0x%x, scp->cs:eip %04x:%04x, vm86s->cs:eip %04x:%04x\n", + dpmi_active(), (unsigned) scp->err, (unsigned) _cs, (unsigned) _eip, + (unsigned) SREG(cs), (unsigned) REG(eip) + ); + + if(pmode) { + dosaddr_t daddr; + if (debug_level('v') && DPMIValidSelector(_cs) && + (((daddr = GetSegmentBase(_cs) + _eip) < 0x110000) || + dpmi_is_valid_range(daddr, 15))) { + cs_ip = MEM_BASE32(daddr); + vga_deb_map( + "vga_emu_fault: cs:eip = %04x:%04x, instr: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + (unsigned) _cs, (unsigned) _eip, + cs_ip[ 0], cs_ip[ 1], cs_ip[ 2], cs_ip[ 3], cs_ip[ 4], cs_ip[ 5], cs_ip[ 6], cs_ip[ 7], + cs_ip[ 8], cs_ip[ 9], cs_ip[10], cs_ip[11], cs_ip[12], cs_ip[13], cs_ip[14], cs_ip[15] + ); + } + } + else { + cs_ip = SEG_ADR((unsigned char *), cs, ip); + vga_deb_map( + "vga_emu_fault: cs:eip = %04x:%04x, instr: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + (unsigned) SREG(cs), (unsigned) REG(eip), + cs_ip[ 0], cs_ip[ 1], cs_ip[ 2], cs_ip[ 3], cs_ip[ 4], cs_ip[ 5], cs_ip[ 6], cs_ip[ 7], + cs_ip[ 8], cs_ip[ 9], cs_ip[10], cs_ip[11], cs_ip[12], cs_ip[13], cs_ip[14], cs_ip[15] + ); + } + + if(i == VGAEMU_MAX_MAPPINGS) { + if ((unsigned)((page_fault << 12) - vga.mem.graph_base) < + vga.mem.graph_size) { /* unmapped VGA area */ + if (pmode) { + u = instr_len((unsigned char *)SEL_ADR(_cs, _eip), + dpmi_segment_is32(_cs)); + _eip += u; + } + else { + u = instr_len(SEG_ADR((unsigned char *), cs, ip), 0); + LWORD(eip) += u; + } + if(u) { + vga_msg("vga_emu_fault: attempted write to unmapped area (cs:ip += %u)\n", u); + } + else { + vga_msg("vga_emu_fault: unknown instruction, page at 0x%05x now writable\n", page_fault << 12); + vga_emu_protect_page(page_fault, RW); + } + return True; + } + else if(memcheck_is_rom(page_fault << PAGE_SHIFT)) { /* ROM area */ + if (pmode) { + u = instr_len((unsigned char *)SEL_ADR(_cs, _eip), + dpmi_segment_is32(_cs)); + _eip += u; + } + else { + u = instr_len(SEG_ADR((unsigned char *), cs, ip), 0); + LWORD(eip) += u; + } + if(u) { + vga_msg("vga_emu_fault: attempted write to ROM area (cs:ip += %u)\n", u); + } + else { + vga_msg("vga_emu_fault: unknown instruction, converting ROM to RAM at 0x%05x\n", page_fault << 12); + vga_emu_protect_page(page_fault, RW); + } + return True; + } + else { + vga_msg( + "vga_emu_fault: unhandled page fault (not in 0xa0000 - 0x%05x range)\n", + (0xc0 + vgaemu_bios.pages) << 12 + ); + return False; + } + } + + if(vga_page < vga.mem.pages) { + if(!vga.inst_emu) { + /* Normal: make the display page writeable then mark it dirty */ + vga_emu_adjust_protection(vga_page, page_fault, RW, 1); + } + if(vga.inst_emu) { + int ret; +#if 1 + /* XXX Hack: dosemu touched the protected page of video mem, which is + * a bug. However for the video modes that do not require instremu, we + * can allow that access. For the planar modes we cant and will fail :( + */ + if(pmode && !DPMIValidSelector(_cs)) { + error("BUG: dosemu touched the protected video memory!!!\n"); + return False; + } +#endif + + /* A VGA mode PL4/PL2 video memory access has been trapped + * while we are using X. Leave the display page read/write-protected + * so that each instruction that accesses it can be trapped and + * simulated. */ + ret = instr_emu_sim(scp, pmode, VGA_EMU_INST_EMU_COUNT); + if (!ret) { + if (pmode) + error_once("instruction simulation failure\n%s\n", DPMI_show_state(scp)); + else + error_once0("instruction simulation failure\n"); + vga_emu_adjust_protection(vga_page, page_fault, RW, 1); + } + } + } + return True; +} + +static void vgaemu_update_prot_cache(unsigned page, int prot) +{ + if(page >= 0xa0 && page < 0xc0) { + vga.mem.prot_map0[page - 0xa0] = prot; + } + + if( + vga.mem.lfb_base_page && + page >= vga.mem.lfb_base_page && + page < vga.mem.lfb_base_page + vga.mem.pages) { + vga.mem.prot_map1[page - vga.mem.lfb_base_page] = prot; + } +} + + +static int vgaemu_prot_ok(unsigned page, int prot) +{ + if(page >= 0xa0 && page < 0xc0) { + return vga.mem.prot_map0[page - 0xa0] == prot ? 1 : 0; + } + + if( + vga.mem.lfb_base_page && + page >= vga.mem.lfb_base_page && + page < vga.mem.lfb_base_page + vga.mem.pages) { + return vga.mem.prot_map1[page - vga.mem.lfb_base_page] == prot ? 1 : 0; + } + + return 0; +} + + +/* + * Change protection of a memory page + * + * The protection of `page' is set to RO if `prot' is 0, otherwise to RW. + * The page will always be executable. + * This functions returns the error code of the mprotect() call. + * `page' is an absolute page number. + * + */ + +int vga_emu_protect_page(unsigned page, int prot) +{ + int i; + int sys_prot; + + /* don't call mprotect at all on LFB with KVM */ + if (config.cpu_vm_dpmi == CPUVM_KVM && page >= vga.mem.lfb_base_page) + return 0; + + sys_prot = prot == RW ? VGA_EMU_RW_PROT : prot == RO ? VGA_EMU_RO_PROT : VGA_EMU_NONE_PROT; + + if(vgaemu_prot_ok(page, sys_prot)) { + vga_deb2_map( + "vga_emu_protect_page: 0x%02x = %s (unchanged)\n", + page, prot == RW ? "RW" : prot == RO ? "RO" : "NONE" + ); + + return 0; + } + + vga_deb2_map( + "vga_emu_protect_page: 0x%02x = %s\n", + page, prot == RW ? "RW" : prot == RO ? "RO" : "NONE" + ); + + if( + vga.mem.lfb_base_page && + page >= vga.mem.lfb_base_page && + page < vga.mem.lfb_base_page + vga.mem.pages) { + dosaddr_t p; + p = vga.mem.lfb_base + ((page - vga.mem.lfb_base_page) << 12); + i = mprotect_mapping(MAPPING_VGAEMU, p, 1 << 12, sys_prot); + } + else { + i = mprotect_mapping(MAPPING_VGAEMU | MAPPING_LOWMEM, page << 12, 1 << 12, sys_prot); + } + + if(i == -1) { + sys_prot = 0xfe; + switch(errno) { + case EINVAL: sys_prot = 0xfd; break; + case EFAULT: sys_prot = 0xfc; break; + case EACCES: sys_prot = 0xfb; break; + } + } + + vgaemu_update_prot_cache(page, sys_prot); + + return i; +} + + +/* + * Change protection of a VGA memory page + * + * The protection of `page' is set to `prot' in all places where `page' is + * currently mapped to. `prot' may be either NONE, RO or RW. + * This functions returns 3 if `mapped_page' is not 0 and not one of the + * locations `page' is mapped to (cf. vga_emu_adjust_protection()). + * `page' is relative to the VGA memory. + * `mapped_page' is an absolute page number. + * + */ + +static int vga_emu_protect(unsigned page, unsigned mapped_page, int prot) +{ + int i, j = 0, err, err_1 = 0, map_ok = 0; +#if DEBUG_MAP >= 1 + int k = 0; +#endif + + if(page > vga.mem.pages) { + vga_deb_map("vga_emu_protect: invalid page number; page = 0x%x\n", page); + return 1; + } + + for(i = 0; i < VGAEMU_MAX_MAPPINGS; i++) { + if(vga.mem.map[i].pages) { + j = page - vga.mem.map[i].first_page; + if(j >= 0 && j < vga.mem.map[i].pages) { +#if DEBUG_MAP >= 1 + k = 1; +#endif + if(vga.mem.map[i].base_page + j == mapped_page) map_ok = 1; + err = vga_emu_protect_page(vga.mem.map[i].base_page + j, prot); + if(!err_1) err_1 = err; + vga_deb2_map( + "vga_emu_protect: error = %d, region = %d, page = 0x%x --> map_addr = 0x%x, prot = %s\n", + err, i, page, vga.mem.map[i].base_page + j, prot == RW ? "RW" : prot == RO ? "RO" : "NONE" + ); + } + } + } + +#if DEBUG_MAP >= 1 + if(k == 0) { + vga_deb_map("vga_emu_protect: page not mapped; page = 0x%x\n", page); + } +#endif + + if(!mapped_page) map_ok = 1; + + if(err_1) return 2; + + return map_ok ? 0 : 3; +} + + +/* + * Set protection according to vga.mem.dirty_map[] + * + * `mapped_page' is supposed to be either 0 or one of the pages `page' is + * mapped to. This is just to locate inconsistencies in handling the various + * VGA memory mappings. + * If `mapped_page' is not 0 and turns out not to be one of the mapping + * locations of `page', the protection of `mapped_page' is changed as well + * and a warning is issued. + * `page' is relative to the VGA memory. + * `mapped_page' is an absolute page number. + * + */ + +static int _vga_emu_adjust_protection(const unsigned page, unsigned mapped_page, + int prot, int dirty) +{ + int i, err, k; + unsigned page1 = page; + + if(page >= vga.mem.pages) { + dosemu_error("vga_emu_adjust_protection: invalid page number; page = 0x%x\n", page); + return 1; + } + + i = vga_emu_protect(page, mapped_page, prot); + + if(i == 3) { + vga_msg( + "vga_emu_adjust_protection: mapping inconsistency; page = 0x%x, map_addr != 0x%x\n", + page, mapped_page + ); + err = vga_emu_protect_page(mapped_page, prot); + if(err) { + vga_msg( + "vga_emu_adjust_protection: mapping inconsistency not fixed; page = 0x%x, map_addr = 0x%x\n", + page, mapped_page + ); + } + } + + if(vga.mem.planes == 4) { /* MODE_X or PL4 */ + page1 &= ~0x30; + for(k = 0; k < vga.mem.planes; k++, page1 += 0x10) + vga_emu_protect(page1, 0, prot); + } + + if(vga.mode_type == PL2) { + /* it's actually 4 planes, but we let everyone believe it's a 1-plane mode */ + page1 &= ~0x30; + vga_emu_protect(page1, 0, prot); + page1 += 0x20; + vga_emu_protect(page1, 0, prot); + } + + if(vga.mode_type == CGA) { + /* CGA uses two 8k banks */ + page1 &= ~0x2; + vga_emu_protect(page1, 0, prot); + page1 += 0x2; + vga_emu_protect(page1, 0, prot); + } + + if(vga.mode_type == HERC) { + /* Hercules uses four 8k banks */ + page1 &= ~0x6; + vga_emu_protect(page1, 0, prot); + page1 += 0x2; + vga_emu_protect(page1, 0, prot); + page1 += 0x2; + vga_emu_protect(page1, 0, prot); + page1 += 0x2; + vga_emu_protect(page1, 0, prot); + } + + _vgaemu_dirty_page(page, dirty); + + return i; +} + +int vga_emu_adjust_protection(unsigned page, unsigned mapped_page, int prot, + int dirty) +{ + int ret; + vga_emu_prot_lock(); + ret = _vga_emu_adjust_protection(page, mapped_page, prot, dirty); + vga_emu_prot_unlock(); + return ret; +} + +static void _vga_kvm_sync_dirty_map(unsigned mapping) +{ + unsigned i; + dosaddr_t base; + + if (config.cpu_vm_dpmi != CPUVM_KVM) { + if (config.cpu_vm != CPUVM_KVM) + return; + if (mapping == VGAEMU_MAP_LFB_MODE) + return; + } + + if (vga.inst_emu) + return; + + base = mapping == VGAEMU_MAP_LFB_MODE ? VGAEMU_PHYS_LFB_BASE : + vga.mem.map[mapping].base_page << PAGE_SHIFT; + if (base == 0) + return; + + kvm_get_dirty_map(base, vga.mem.dirty_bitmap); + for (i = 0; i < vga.mem.map[mapping].pages; i++) + if (test_bit(i, vga.mem.dirty_bitmap)) + _vgaemu_dirty_page(vga.mem.map[mapping].first_page + i, 1); +} + +/* + * Map the VGA memory. + * + * `mapping' is an index into vga.mem.map[] describing the mapping in more + * detail. `first_page' is the first page of VGA memory to appear in this + * mapping. + * `first_page' is relative to the VGA memory. + * + */ + +static int vga_emu_map(unsigned mapping, unsigned first_page) +{ + unsigned u; + vga_mapping_type *vmt; + int prot, i; + + if(mapping >= VGAEMU_MAX_MAPPINGS) return 1; + + vmt = vga.mem.map + mapping; + if(vmt->pages == 0) return 0; /* nothing to do */ + + if(vmt->pages + first_page > vga.mem.pages) return 2; + + /* default protection for dirty pages */ + switch(vga.inst_emu) { + case EMU_WRITE_INST: + prot = VGA_EMU_RO_PROT; + break; + case EMU_ALL_INST: + prot = VGA_EMU_NONE_PROT; + break; + default: + prot = VGA_EMU_RW_PROT; + break; + } + + i = 0; + pthread_mutex_lock(&prot_mtx); + _vga_kvm_sync_dirty_map(mapping); + if (mapping == VGAEMU_MAP_BANK_MODE) + i = alias_mapping(MAPPING_VGAEMU, + vmt->base_page << 12, vmt->pages << 12, + prot, vga.mem.base + (first_page << 12)); + else if (config.cpu_vm_dpmi != CPUVM_KVM) + /* LFB: mapped at init, just need to set protection */ + i = mprotect_mapping(MAPPING_VGAEMU, vmt->base_page << 12, + vmt->pages << 12, prot); + + if(i == -1) { + pthread_mutex_unlock(&prot_mtx); + error("VGA: protect page failed\n"); + return 3; + } + + vmt->first_page = first_page; + + for(u = 0; u < vmt->pages; u++) { + vgaemu_update_prot_cache(vmt->base_page + u, prot); + /* need to fix up protection for clean pages */ + if(vga.mode_class == GRAPH && !vga.mem.dirty_map[vmt->first_page + u] && + prot == VGA_EMU_RW_PROT) + _vga_emu_adjust_protection(vmt->first_page + u, 0, VGA_PROT_RO, 0); + } + pthread_mutex_unlock(&prot_mtx); + + return 0; +} + + +#if 0 +/* + * Unmap the VGA memory. + */ + +static int vgaemu_unmap(unsigned page) +{ + int i; + + if( + page < 0xa0 || + page >= 0xc0 || + (!vga.config.mono_support && page >= 0xb0 && page < 0xb8) + ) return 1; + + i = alias_mapping(MAPPING_VGAEMU, + page << 12, 1 << 12, + VGA_EMU_RW_PROT, LOWMEM(page << 12)) + ); + + if (i == -1) return 3; + + return vga_emu_protect_page(page, RO); +} +#endif + +/* + * Put the vga memory mapping into a defined state. + */ +void vgaemu_reset_mapping(void) +{ + int i; + int prot, page, startpage, endpage; + + prot = VGA_EMU_RW_PROT; + startpage = vga.mem.graph_base >> PAGE_SHIFT; + endpage = startpage + (vga.mem.graph_size >> PAGE_SHIFT); + for(page = startpage; page < endpage; page++) { + i = alias_mapping(MAPPING_VGAEMU, + page << 12, 1 << 12, + prot, LOWMEM(page << 12) + ); + if (i == -1) { + error("VGA: map failed at page %x\n", page); + return; + } + vgaemu_update_prot_cache(page, prot); + } + for(page = 0xb8; page < 0xc0; page++) { + i = alias_mapping(MAPPING_VGAEMU, + page << 12, 1 << 12, + prot, LOWMEM(page << 12) + ); + if (i == -1) { + error("VGA: map failed at page %x\n", page); + return; + } + vgaemu_update_prot_cache(page, prot); + } +} + +static void vgaemu_register_ports(void) +{ + emu_iodev_t io_device; + + /* register VGA ports */ + io_device.read_portb = VGA_emulate_inb_handler; + io_device.write_portb = VGA_emulate_outb_handler; + io_device.read_portw = VGA_emulate_inw_handler; + io_device.write_portw = VGA_emulate_outw_handler; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + + /* register VGAEmu */ + io_device.handler_name = "VGAEmu VGA Controller"; + io_device.start_addr = VGA_BASE; + io_device.end_addr = VGA_BASE + 0x0f; + port_register_handler(io_device, 0); + + /* register CRT Controller */ + io_device.handler_name = "VGAEmu CRT Controller"; + io_device.start_addr = CRTC_INDEX; + io_device.end_addr = CRTC_DATA; + port_register_handler(io_device, 0); + + /* register Input Status #1/Feature Control */ + io_device.handler_name = "VGAEmu Input Status #1/Feature Control"; + io_device.start_addr = INPUT_STATUS_1; + io_device.end_addr = INPUT_STATUS_1; + port_register_handler(io_device, 0); + + /* + * Instead of single ports, we take them all - this way we can see + * if something is missing. -- sw + */ + if(vga.config.mono_support) { + io_device.handler_name = "VGAEmu Mono/Hercules Card Range 0"; + io_device.start_addr = 0x3b0; + io_device.end_addr = 0x3bb; + port_register_handler(io_device, 0); + + io_device.handler_name = "VGAEmu Mono/Hercules Card Range 1"; + io_device.start_addr = 0x3bf; + io_device.end_addr = 0x3bf; + port_register_handler(io_device, 0); + } +} + +/* + * DANG_BEGIN_FUNCTION vga_emu_init + * + * description: + * vga_emu_init() must be called before using the VGAEmu functions. + * It is only called from env/video/X.c::X_init() at the moment. + * This function basically initializes the global variable `vga' and + * allocates the VGA memory. + * + * It does in particular *not* map any memory into the range + * 0xa0000 - 0xc0000, this is done as part of a VGA mode switch. + * + * There should be an accompanying vga_emu_done(). + * + * arguments: + * vedt - Pointer to struct describing the type of display we are actually + * attached to. + * + * DANG_END_FUNCTION + * + */ + +int vga_emu_init(int src_modes, ColorSpaceDesc *csd) +{ + vgaemu_display_type vedt; + + vedt.src_modes = src_modes; + vedt.bits = csd->bits; + vedt.r_mask = csd->r_mask; + vedt.g_mask = csd->g_mask; + vedt.b_mask = csd->b_mask; + vedt.r_shift = csd->r_shift; + vedt.g_shift = csd->g_shift; + vedt.b_shift = csd->b_shift; + vedt.r_bits = csd->r_bits; + vedt.g_bits = csd->g_bits; + vedt.b_bits = csd->b_bits; + vbe_init(&vedt); + + return 0; +} + +static int vga_emu_post_init(void); + +int vga_emu_pre_init(void) +{ + int i; + vga_mapping_type vmt = {0, 0, 0}; + + if (config.dumb_video) { + vgaemu_register_ports(); + return 0; + } + + /* clean it up - just in case */ + memset(&vga, 0, sizeof vga); + + vga.mode = vga.VGA_mode = vga.VESA_mode = 0; + vga.mode_class = -1; + vga.config.video_off = 1; + vga.config.standard = 1; + vga.mem.plane_pages = 0x10; /* 16 pages = 64k */ + vga.dac.bits = 6; + vga.inst_emu = 0; + + vga.config.mono_support = config.dualmon ? 0 : 1; + + open_mapping(MAPPING_VGAEMU); + + vga.mem.size = config.vgaemu_memsize << 10; + + /* force 256k granularity to prevent possible problems + * (with 4-plane-modes, to be precise) + */ + vga.mem.size = (vga.mem.size + ((1 << 18) - 1)) & ~((1 << 18) - 1); + vga.mem.pages = vga.mem.size >> 12; + + vga.mem.base = alloc_mapping_huge_page_aligned(MAPPING_VGAEMU, vga.mem.size); + if(vga.mem.base == MAP_FAILED) { + error("vga_emu_init: not enough memory (%u k)\n", vga.mem.size >> 10); + config.exitearly = 1; + return 1; + } + + /* alloc more pages because vgaemu_dirty_page() does weird things */ + if((vga.mem.dirty_map = (unsigned char *) malloc(vga.mem.pages | 0xff)) == NULL) { + error("vga_emu_init: not enough memory for dirty map\n"); + config.exitearly = 1; + return 1; + } + if((vga.mem.dirty_bitmap = (unsigned char *) malloc((vga.mem.pages+CHAR_BIT-1) / CHAR_BIT)) == NULL) { + error("vga_emu_init: not enough memory for dirty bit map\n"); + config.exitearly = 1; + return 1; + } + dirty_all_video_pages(); /* all need an update */ + + if( + (vga.mem.prot_map0 = malloc(vgaemu_bios.pages + 0xc0 - 0xa0)) == NULL || + (vga.mem.prot_map1 = (unsigned char *) malloc(vga.mem.pages)) == NULL + ) { + error("vga_emu_init: not enough memory for protection map\n"); + config.exitearly = 1; + return 1; + } + memset(vga.mem.prot_map0, 0xff, vgaemu_bios.pages + 0x20); + memset(vga.mem.prot_map1, 0xff, vga.mem.pages); + + /* + * vga.mem.map _must_ contain only valid mappings - otherwise _bad_ things will happen! + * cf. vga_emu_protect() + * -- sw + */ + + for(i = 0; i < VGAEMU_MAX_MAPPINGS; i++) vga.mem.map[i] = vmt; + + vga.mem.bank = vga.mem.bank_pages = 0; + + vga.mem.graph_base = config.umb_a0 ? GRAPH_BASE + GRAPH_SIZE : GRAPH_BASE; + vga.mem.graph_size = (config.umb_b0 ? GRAPH_BASE + GRAPH_SIZE : + VGA_PHYS_TEXT_BASE) - vga.mem.graph_base; + + if (config.cpu_vm == CPUVM_KVM || config.cpu_vm_dpmi == CPUVM_KVM) { + if (vga.mem.graph_base + vga.mem.graph_size == VGA_PHYS_TEXT_BASE) { + /* contiguous */ + kvm_set_dirty_log(vga.mem.graph_base, vga.mem.graph_size + VGA_TEXT_SIZE); + } else { + if (vga.mem.graph_size) + kvm_set_dirty_log(vga.mem.graph_base, vga.mem.graph_size); + kvm_set_dirty_log(VGA_PHYS_TEXT_BASE, VGA_TEXT_SIZE); + } + } + + vga.mem.lfb_base = 0; + if (config.X_lfb && config.dpmi) { + void *addr = smalloc_aligned_topdown(&main_pool, NULL, PAGE_SIZE, vga.mem.size); + if (addr) { + vga.mem.lfb_base = DOSADDR_REL(addr); + memcheck_addtype('e', "VGAEMU LFB"); + register_hardware_ram_virtual2('e', VGAEMU_PHYS_LFB_BASE, vga.mem.size, + vga.mem.base, vga.mem.lfb_base); + if (!alias_mapping_pa(MAPPING_VGAEMU, VGAEMU_PHYS_LFB_BASE, + vga.mem.size, VGA_EMU_RW_PROT, vga.mem.base)) + addr = NULL; + } + if (addr && config.cpu_vm_dpmi == CPUVM_KVM) + kvm_set_dirty_log(VGAEMU_PHYS_LFB_BASE, vga.mem.size); + if(addr == NULL || addr == MAP_FAILED) { + error("vga_emu_init: not enough memory (%u k)\n", vga.mem.size >> 10); + config.exitearly = 1; + return 1; + } + } + + if(vga.mem.lfb_base == 0) { + vga_msg("vga_emu_init: linear frame buffer (lfb) disabled\n"); + } + + return vga_emu_post_init(); +} + +static int vga_emu_post_init(void) +{ + if(vga.mem.lfb_base != 0) { + vga.mem.lfb_base_page = vga.mem.lfb_base >> 12; + } + vga_emu_setup_mode_table(); + + vgaemu_register_ports(); + + SETIVEC(0x42, INT42HOOK_SEG, INT42HOOK_OFF); + vbe_pre_init(); + + vga_msg( + "vga_emu_init: memory: %u kbyte at %p (lfb at %#x); %ssupport for mono modes\n", + vga.mem.size >> 10, vga.mem.base, + vga.mem.lfb_base_page << 12, + vga.config.mono_support ? "" : "no " + ); + + return 0; +} + + +void vga_emu_done(void) +{ + if (vga.mem.lfb_base) { + unalias_mapping_pa(MAPPING_DPMI, VGAEMU_PHYS_LFB_BASE, vga.mem.size); + smfree(&main_pool, MEM_BASE32(vga.mem.lfb_base)); + vga.mem.lfb_base = 0; + } +} + + +#if DEBUG_UPDATE >= 1 +static void print_dirty_map() +{ + int i, j; + + vga_msg("dirty_map[0 - 1024k] (0 = RO/NONE, 1 = RW = dirty)\n"); + for(j = 0; j < 256; j += 64) { + v_printf(" "); + for(i = 0; i < 64; i++) { + if(!(i & 15)) + v_printf(" "); + else if(!(i & 3)) + v_printf("."); + v_printf("%d", (int) vga.mem.dirty_map[j + i]); + } + v_printf("\n"); + } +} +#endif + + +#if DEBUG_MAP >= 3 +static char prot2char(unsigned char prot) +{ + if(prot < 0x9) return prot + '0'; + switch(prot) { + case 0xff: return '-'; + case 0xfe: return 'e'; + case 0xfd: return 'i'; + case 0xfc: return 'f'; + case 0xfb: return 'a'; + } + + return '?'; +} + +static void print_prot_map() +{ + int i, j; + + vga_msg("prot_map: 0xa0000-0xc0000 & lfb\n"); + v_printf(" "); + for(i = 0; i < 0x20; i++) { + if(!(i & 15)) + v_printf(" "); + else if(!(i & 3)) + v_printf("."); + v_printf("%c", prot2char(vga.mem.prot_map0[i])); + } + v_printf("\n"); + for(j = 0; j < 256; j += 64){ + v_printf(" "); + for(i = 0; i < 64; i++) { + if(!(i & 15)) + v_printf(" "); + else if(!(i & 3)) + v_printf("."); + v_printf("%c", prot2char(vga.mem.prot_map1[j + i])); + } + v_printf("\n"); + } +} +#endif + + +/* + * DANG_BEGIN_FUNCTION vga_emu_update + * + * description: + * vga_emu_update() scans the VGA memory for dirty (= written to since last + * update) pages and returns the changed area in *veut. See the definition + * of vga_emu_update_type in env/video/vgaemu_inside.h for details. + * + * You will need to call this function repeatedly until it returns 0 to + * grab all changes. You can specify an upper limit for the size of the + * area that will be returned using `veut->max_max_len' and `veut->max_len'. + * See the example in env/video/X.c how this works. + * + * If the return value of vga_emu_update() is >= 0, it is the number of changed + * pages, -1 means there are still changed pages but the maximum update chunk size + * (`veut->max_max_len') was exceeded. + * + * This function does in its current form not work for Hercules modes; it + * does, however work for text modes, although this feature is currently + * not used. + * + * arguments: + * veut - A pointer to a vga_emu_update_type object holding all relevant info. + * + * DANG_END_FUNCTION + * + */ +/* for threaded rendering we need to disable cycling as it can lead + * to lock starvations */ +static int __vga_emu_update(vga_emu_update_type *veut, unsigned display_start, + unsigned display_end, int pos) +{ + int i, j; + unsigned end_page, max_len; + + if (pos == -1) + pos = display_start >> PAGE_SHIFT; + end_page = (display_end - 1) >> PAGE_SHIFT; + if (pos > end_page) + return -1; + + vga_deb_update("vga_emu_update: display = %d (page = %u) - %d (page = %u), update_pos = %d\n", + display_start, + display_start << PAGE_SHIFT, + display_end, + end_page, + pos << PAGE_SHIFT + ); + +#if DEBUG_MAP >= 3 + // just testing + print_prot_map(); +#endif + +#if DEBUG_UPDATE >= 1 + print_dirty_map(); +#endif + + for (i = j = pos; i <= end_page && ! vga.mem.dirty_map[i]; i++); + if(i == end_page + 1) { +#if 0 + /* FIXME: this code not ready yet */ + for (; i < vga.mem.pages; i++) { + if (vga.mem.dirty_map[i]) { + vga.mem.dirty_map[i] = 0; + _vga_emu_adjust_protection(i, 0, DEF_PROT, 0); + } + } +#endif + return -1; + } + + for(j = i; j <= end_page && vga.mem.dirty_map[j]; j++) { + /* if display_start points to the middle of the page, dont clear + * it immediately: it may still have dirty segments in the beginning, + * which will be processed after mem wrap. */ + if (j == pos && pos == (display_start >> PAGE_SHIFT) && + (display_start & (PAGE_SIZE - 1)) && vga.mem.dirty_map[j] == 1) + vga.mem.dirty_map[j] = 2; + else + vga.mem.dirty_map[j] = 0; + if (!vga.mem.dirty_map[j]) + _vga_emu_adjust_protection(j, 0, DEF_PROT, 0); + } + + vga_deb_update("vga_emu_update: update range: i = %d, j = %d\n", i, j); + + if(i == j) + return -1; + + veut->update_start = i << 12; + veut->update_len = (j - i) << 12; + max_len = display_end - veut->update_start; + if (veut->update_len > max_len) { + assert(veut->update_len - max_len < PAGE_SIZE); + veut->update_len = max_len; + } + + vga_deb_update("vga_emu_update: update_start = %d, update_len = %d, update_pos = %d\n", + veut->update_start, + veut->update_len, + pos << PAGE_SHIFT + ); + + return j; +} + +int vga_emu_update(vga_emu_update_type *veut, unsigned display_start, + unsigned display_end, int pos) +{ + int ret; + pthread_mutex_lock(&prot_mtx); + ret = __vga_emu_update(veut, display_start, display_end, pos); + pthread_mutex_unlock(&prot_mtx); + return ret; +} + +void vga_emu_update_lock(void) +{ + pthread_rwlock_rdlock(&mode_mtx); +} + +void vga_emu_update_unlock(void) +{ + pthread_rwlock_unlock(&mode_mtx); +} + +void vga_emu_prot_lock(void) +{ + pthread_mutex_lock(&prot_mtx); +} + +void vga_emu_prot_unlock(void) +{ + pthread_mutex_unlock(&prot_mtx); +} + +/* + * DANG_BEGIN_FUNCTION vgaemu_switch_plane + * + * description: + * vgaemu_switch_plane() maps the specified plane. + * + * This function returns True on success and False on error, usually + * indicating an invalid bank number. + * + * arguments: + * plane (0..3) - The plane to map. + * + * DANG_END_FUNCTION + * + */ + +int vgaemu_switch_plane(unsigned plane) +{ + if(plane > 3) { + vga_msg("vgaemu_switch_plane: plane %d invalid\n", plane); + return False; + } + + vga.mem.write_plane = vga.mem.read_plane = plane; + vga.mem.bank = 0; + + if(vgaemu_map_bank()) { + vga_msg("vgaemu_switch_plane: failed to access plane %u\n", plane); + return False; + } + + vga_deb_bank("vgaemu_switch_plane: switched to plane %u\n", plane); + + return True; +} + + +/* + * DANG_BEGIN_FUNCTION vga_emu_switch_bank + * + * description: + * vga_emu_switch_bank() is used to emulate video-bankswitching. + * + * This function returns True on success and False on error, usually + * indicating an invalid bank number. + * + * arguments: + * bank - The bank to switch to. + * + * DANG_END_FUNCTION + * + */ + +int vga_emu_switch_bank(unsigned bank) +{ + if((bank + 1) * vga.mem.bank_pages > vga.mem.pages) { + vga_msg("vga_emu_switch_bank: invalid bank %d\n", bank); + return False; + } + + vga.mem.bank = bank; + vga.mem.write_plane = vga.mem.read_plane = 0; + + if(vgaemu_map_bank()) { + vga_msg("vga_emu_switch_bank: failed to access bank %u\n", bank); + return False; + } + + vga_deb_bank("vga_emu_switch_bank: switched to bank %u\n", bank); + + return True; +} + + +/* + * Create a new video mode. + * + * This function appends a new mode entry with the required properties to the + * internal mode list, if such a mode didn't already exist. + * + * It returns the number of modes actually created (0 or 1). + * + * NOTE: It does *not* set the buffer size and position! + */ + +int vga_emu_setup_mode(vga_mode_info *vmi, int mode_index, unsigned width, unsigned height, unsigned color_bits) +{ + int i; + + for(i = 0; i < mode_index; i++) { + if(vmi[i].width == width && vmi[i].height == height && vmi[i].color_bits == color_bits) { + if(vmi[i].VESA_mode == -1) vmi[i].VESA_mode = -2; + return 0; + } + } + vmi += mode_index; + + vmi->VGA_mode = -1; + vmi->VESA_mode = -2; + vmi->mode_class = GRAPH; + switch(vmi->color_bits = color_bits) { + case 8: vmi->type = P8; break; + case 15: vmi->type = P15; break; + case 16: vmi->type = P16; break; + case 24: vmi->type = P24; break; + case 32: vmi->type = P32; break; + default: return 0; + } + vmi->width = width; + vmi->height = height; + vmi->char_width = 8; + vmi->char_height = height >= 400 ? 16 : 8; + if(vmi->char_height == 16 && (height & 15) == 8) vmi->char_height = 8; + vmi->text_width = width / vmi->char_width; + vmi->text_height = height / vmi->char_height; + + vga_msg( + "vga_emu_setup_mode: creating VESA mode %d x %d x %d\n", + width, height, color_bits + ); + + return 1; +} + + +/* + * Sets up the internal mode table. This table can then be accessed + * via vga_emu_find_mode(). + * + * It extends the table vga_mode_table[] found in env/video/vgaemu_modelist.h + * using the mode descriptions in vgaemu_simple_mode_list[][] and + * user-defined modes in dosemu.conf (in this order). The location and size of + * this new mode table is then stored in the global variable vgaemu_bios. + * + * The memory that was allocated for config.vesamode_list during the parsing + * of dosemu.conf is freed. + * + * Note: a new mode is added only if it didn't already exist. + * + */ + +static void vga_emu_setup_mode_table(void) +{ + int vbe_modes = (sizeof vga_mode_table) / (sizeof *vga_mode_table); + int vbe_num = VBE_FIRST_OEM_MODE; + int last_mode = 0; + vga_mode_info *vmi_end, *vmi; + vesamode_type *vmt0, *vmt = config.vesamode_list; + int vesa_x_modes = 0; + int i, j, k; + + vgaemu_bios.vbe_last_mode = 0; + + while(vmt) { + vesa_x_modes += vmt->color_bits ? 1 : 5; /* 8, 15, 16, 24, 32 bit */ + vmt = vmt->next; + } + vesa_x_modes += (sizeof vgaemu_simple_mode_list) / (sizeof *vgaemu_simple_mode_list) * 5; + + vmi = malloc((sizeof *vga_mode_table) * (vbe_modes + vesa_x_modes)); + + if(vmi == NULL) { + vgaemu_bios.vga_mode_table = vga_mode_table; + vgaemu_bios.mode_table_length = vbe_modes; + vgaemu_bios.vbe_last_mode = 0; + return; + } + + memcpy(vmi, vga_mode_table, sizeof vga_mode_table); + + vgaemu_bios.vga_mode_table = vmi; + + for(i = 0; i < (sizeof vgaemu_simple_mode_list) / (sizeof *vgaemu_simple_mode_list); i++) { + j = vgaemu_simple_mode_list[i][0]; + k = vgaemu_simple_mode_list[i][1]; + vbe_modes += vga_emu_setup_mode(vmi, vbe_modes, j, k, 8); + vbe_modes += vga_emu_setup_mode(vmi, vbe_modes, j, k, 15); + vbe_modes += vga_emu_setup_mode(vmi, vbe_modes, j, k, 16); + vbe_modes += vga_emu_setup_mode(vmi, vbe_modes, j, k, 24); + vbe_modes += vga_emu_setup_mode(vmi, vbe_modes, j, k, 32); + } + + while((vmt0 = vmt = config.vesamode_list) != NULL) { + if(vmt != NULL) while(vmt->next != NULL) { vmt0 = vmt; vmt = vmt->next; } + if(vmt != NULL) { + if( + vmt->width && vmt->width < (1 << 15) && + vmt->height && vmt->height < (1 << 15) && ( + vmt->color_bits == 0 || vmt->color_bits == 8 || + vmt->color_bits == 15 || vmt->color_bits == 16 || + vmt->color_bits == 24 || vmt->color_bits == 32 + ) + ) { + if(vmt->color_bits) { + vbe_modes += vga_emu_setup_mode(vmi, vbe_modes, vmt->width, vmt->height, vmt->color_bits); + } + else { + vbe_modes += vga_emu_setup_mode(vmi, vbe_modes, vmt->width, vmt->height, 8); + vbe_modes += vga_emu_setup_mode(vmi, vbe_modes, vmt->width, vmt->height, 15); + vbe_modes += vga_emu_setup_mode(vmi, vbe_modes, vmt->width, vmt->height, 16); + vbe_modes += vga_emu_setup_mode(vmi, vbe_modes, vmt->width, vmt->height, 24); + vbe_modes += vga_emu_setup_mode(vmi, vbe_modes, vmt->width, vmt->height, 32); + } + } + else { + vga_msg( + "vga_emu_setup_mode_table: invalid VESA mode %d x %d x %d\n", + vmt->width, vmt->height, vmt->color_bits + ); + } + free(vmt); + if(vmt == config.vesamode_list) + config.vesamode_list = NULL; + else + vmt0->next = NULL; + } + } + + vgaemu_bios.mode_table_length = vbe_modes; + + for(vmi_end = vmi + vbe_modes; vmi < vmi_end; vmi++) { + if(vmi->VESA_mode == -2) vmi->VESA_mode = vbe_num++; + if(vmi->VESA_mode > last_mode) last_mode = vmi->VESA_mode; + } + + vgaemu_bios.vbe_last_mode = last_mode; + + /* + * Fill in appropriate buffer sizes. + * We used to let the user specify this in vgaemu_modelist.h but to force + * consistency with the Miscellaneous Register (index 6) in the Graphics + * Controller we do it here. -- sw + */ + for(vmi = NULL; (vmi = vga_emu_find_mode(-1, vmi)); ) { + if((vmi->VGA_mode >= 0 && vmi->VGA_mode <= 7) || vmi->mode_class == TEXT) { + vmi->buffer_start = vmi->type == TEXT_MONO ? 0xb000 : 0xb800; + vmi->buffer_len = 32; + } + else { + vmi->buffer_start = 0xa000; + vmi->buffer_len = 64; + } + } +} + + +/* + * DANG_BEGIN_FUNCTION vga_emu_find_mode + * + * description: + * Searches a video mode with the requested mode number. + * + * The search starts with the mode *after* the mode `vmi' points to. + * If `vmi' == NULL, starts at the beginning of the internal mode table. + * `mode' may be a standard VGA mode number (0 ... 0x7f) or a + * VESA mode number (>= 0x100). The mode number may have its don't-clear-bit + * (bit 7 or bit 15) or its use-lfb-bit (bit 14) set. + * The special mode number -1 will match any mode and may be used to + * scan through the whole table. + * + * Returns NULL if no mode was found and a pointer into the mode table + * otherwise. The returned pointer is a suitable argument for subsequent + * calls to this function. + * + * You should (and can) access the mode table only through this function. + * + * arguments: + * mode - video mode. + * vmi - pointer into internal mode list + * + * DANG_END_FUNCTION + * + */ + +vga_mode_info *vga_emu_find_mode(int mode, vga_mode_info* vmi) +{ + vga_mode_info *vmi_end; + + if (!vgaemu_bios.vga_mode_table) + return NULL; + vmi_end = vgaemu_bios.vga_mode_table + vgaemu_bios.mode_table_length; + + if(mode != -1) { + mode &= 0x3fff; + if(mode < 0x100) mode &= ~0x80; + } + + if(vmi == NULL) { + vmi = vgaemu_bios.vga_mode_table; + } + else { + if(++vmi >= vmi_end) return NULL; + } + + if(mode == -1) return vmi; + + for(; vmi < vmi_end; vmi++) { + if(vmi->VGA_mode == mode || vmi->VESA_mode == mode) return vmi; + } + + return NULL; +} + + +/* + * DANG_BEGIN_FUNCTION vga_emu_setmode + * + * description: + * Set a video mode. + * + * Switches to `mode' with text sizes `width' and `height' or (if no such + * mode was found) at least `width' and `height'. + * + * arguments: + * mode - The new video mode. + * width - Number of text columns. + * height - Number of text rows. + * + * DANG_END_FUNCTION + * + */ + +static int __vga_emu_setmode(int mode, int width, int height) +{ + unsigned u = -1; + int i; + vga_mode_info *vmi = NULL, *vmi2 = NULL; + + vga_msg("vga_emu_setmode: requested mode: 0x%02x (%d x %d)\n", mode, width, height); + + while((vmi = vga_emu_find_mode(mode, vmi))) { + if(vmi->mode_class == GRAPH || (vmi->text_width == width && vmi->text_height == height)) break; + } + + if(vmi == NULL) { + /* Play it again, Sam! + * This is when we can't find the textmode with the appropriate sizes. + * Use the best matching text mode + */ + + while((vmi = vga_emu_find_mode(mode, vmi))) { + if(vmi->text_width >= width && vmi->text_height >= height && vmi->text_width * vmi->text_height < u) { + u = vmi->text_width * vmi->text_height; + vmi2 = vmi; + } + } + if(vmi == NULL && Video->setmode == NULL) + vmi2 = vga_emu_find_mode(mode, vmi); + vmi = vmi2; + } + + if(vmi == NULL) { /* no mode found */ + vga_msg("vga_emu_setmode: no mode 0x%02x found\n", mode); + return False; + } + + if (vmi->buffer_start == 0xa000 && config.umb_a0) { + error("VGA: avoid touching a000 as it is used for UMB\n"); + return False; + } + if (vmi->buffer_start == 0xb000 && config.umb_b0) { + error("VGA: avoid touching b000 as it is used for UMB\n"); + return False; + } + + vga_msg("vga_emu_setmode: mode found in %s run\n", vmi == vmi2 ? "second" : "first"); + + vga_msg("vga_emu_setmode: mode 0x%02x, (%d x %d x %d, %d x %d, %d x %d, %dk at 0x%04x)\n", + mode, vmi->width, vmi->height, vmi->color_bits, vmi->text_width, vmi->text_height, + vmi->char_width, vmi->char_height, vmi->buffer_len, vmi->buffer_start + ); + + if (!vga.mode) + video_initialized++; + vga.mode = mode; + vga.VGA_mode = vmi->VGA_mode; + vga.VESA_mode = vmi->VESA_mode; + vga.mode_class = vmi->mode_class; + vga.mode_type = vmi->type; + vga.width = vmi->width; + vga.height = vmi->height; + vga.line_compare = vmi->height; + vga.scan_len = (vmi->width + 3) & ~3; /* dword aligned */ + vga.text_width = vmi->text_width; + vga.text_height = vmi->text_height; + vga.char_width = vmi->char_width; + vga.char_height = vmi->char_height; + vga.color_bits = vmi->color_bits; + vga.pixel_size = vmi->color_bits; + if(vga.pixel_size > 8) { + vga.pixel_size = (vga.pixel_size + 7) & ~7; /* assume byte alignment for these modes */ + vga.scan_len *= vga.pixel_size >> 3; + } + vgaemu_adjust_instremu((vga.mode_type==PL4 || vga.mode_type==PL2) + ? EMU_ALL_INST : 0); + + vga_msg("vga_emu_setmode: scan_len = %d\n", vga.scan_len); + i = vga.scan_len; + + vga.config.standard = vga.VGA_mode >= 0 && vga.VGA_mode <= 0x13 ? 1 : 0; + vga.config.mono_port = + vga.config.video_off = 0; + + vga.reconfig.mem = vga.reconfig.display = + vga.reconfig.dac = vga.reconfig.power = + vga.reconfig.re_init = 0; + vga.mem.planes = 1; + if(vga.mode_type == PL4 || vga.mode_type == NONCHAIN4) { + vga.scan_len >>= 3; + vga.mem.planes = 4; + } + if(vga.mode_type == PL1 || vga.mode_type == PL2) vga.scan_len >>= 3; + if(vga.mode_type == CGA) vga.scan_len >>= 4 - vga.pixel_size; /* 1 or 2 bits */ + vga.mem.write_plane = vga.mem.read_plane = 0; + + if(vga.mode_class == TEXT) vga.scan_len = vga.text_width << 1; + + vga.latch[0] = vga.latch[1] = vga.latch[2] = vga.latch[3] = 0; + + vga.display_start = 0; + + if(i != vga.scan_len) { + vga_msg("vga_emu_setmode: scan_len changed to %d\n", vga.scan_len); + } + + /* + * Clear the whole VGA memory. In addition, in text modes, + * set the first 32k to 0x0720... + * -- 1998/04/04 sw + */ + + if(!(mode & 0x8000) && !(mode < 0x100 && (mode & 0x80))) { + if(vga.mode_class != TEXT && vga.mem.base) { + /* should be moved to BIOS */ + memset((void *)vga.mem.base, 0, vga.mem.size); + } + } + + dirty_all_video_pages(); + /* Put the mapping for the range 0xa0000 - 0xc0000 into some intial state: + * the scratch page is mapped RW all over the place. + */ + vgaemu_reset_mapping(); + + vga.mem.bank = 0; + vga.mem.bank_pages = vmi->buffer_len >> 2; + + /* + * As we don't have 'planes' in the VGA's sense, we *emulate* them by + * mapping different parts of our memory. + * This has *nothing* to do with memory banks. It just *looks* similar. + * + * Planes are only really needed for VGA 16 and 4 color modes and for + * non-chain4 256 color modes. And for accessing fonts in text modes. + * + * It is (for now) implicitly assumed at various places (the line below + * being one, for example), that a plane is 64k in size, giving a total + * of 256k 100%-compatible VGA memory. + * + * We could make this a little bit more flexible, but the only reason + * for this would be an emulation of the 1024x768 16 color mode. + * Which would be of no particular value anyway. -- sw + */ + vga.mem.plane_pages = 0x10; /* 16 pages = 64k */ + + vga.buffer_seg = vmi->buffer_start; + vga.mem.map[VGAEMU_MAP_BANK_MODE].base_page = vmi->buffer_start >> 8; + vgaemu_map_bank(); + + vga.mem.map[VGAEMU_MAP_LFB_MODE].base_page = 0; + vga.mem.map[VGAEMU_MAP_LFB_MODE].first_page = 0; + vga.mem.map[VGAEMU_MAP_LFB_MODE].pages = 0; + vga.mem.wrap = vmi->buffer_len * 1024; + // unmap ??? + + /* In Super-VGA modes, do *not* wrap memory at 256k */ + if(vga.color_bits >= 8 && (vga.mode & 0xffff) > 0x13) { + vga.mem.wrap = vga.mem.size; + if(vga.mem.lfb_base_page) { + vga.mem.map[VGAEMU_MAP_LFB_MODE].base_page = vga.mem.lfb_base_page; + vga.mem.map[VGAEMU_MAP_LFB_MODE].pages = vga.mem.pages; + vga_emu_map(VGAEMU_MAP_LFB_MODE, 0); /* map the VGA memory to LFB */ + } + } + + vga_msg("vga_emu_setmode: setting up components...\n"); + + DAC_init(); + Attr_init(); + Seq_init(); + CRTC_init(); + GFX_init(); + Misc_init(); + Herc_init(); + + vgaemu_adj_cfg(CFG_SEQ_ADDR_MODE, 1); + vgaemu_adj_cfg(CFG_CRTC_ADDR_MODE, 1); + vgaemu_adj_cfg(CFG_CRTC_WIDTH, 1); + vgaemu_adj_cfg(CFG_CRTC_HEIGHT, 1); + vgaemu_adj_cfg(CFG_CRTC_LINE_COMPARE, 1); +#if 0 + /* need to fix vgaemu before this is possible */ + vgaemu_adj_cfg(CFG_MODE_CONTROL, 1); +#endif + + vga_msg("vga_emu_setmode: mode initialized\n"); + + return True; +} + +static int _is_dirty(void) +{ + int i, ret = 0; + + for(i = 0; i < VGAEMU_MAX_MAPPINGS; i++) + _vga_kvm_sync_dirty_map(i); + + if (vga.mem.dirty_map) { + for (i = 0; i < vga.mem.pages; i++) { + if (vga.mem.dirty_map[i]) { + ret = 1; + break; + } + } + } + return ret; +} + +int vga_emu_setmode(int mode, int width, int height) +{ + int ret; + pthread_rwlock_wrlock(&mode_mtx); + ret = __vga_emu_setmode(mode, width, height); + pthread_rwlock_unlock(&mode_mtx); +// render_update_vidmode(); + return ret; +} + +int vgaemu_map_bank(void) +{ + int i, first; +#if 0 + int j, k0, k1; +#endif + + if((vga.mem.bank + 1) * vga.mem.bank_pages > vga.mem.pages) { + vga_msg("vgaemu_map_bank: invalid bank %d\n", vga.mem.bank); + return False; + } + + if(vga.mem.write_plane > 3) { + vga_msg("vgaemu_map_bank: invalid plane %d\n", vga.mem.write_plane); + return False; + } + + vga.mem.map[VGAEMU_MAP_BANK_MODE].pages = vga.mem.bank_pages; + vga.mem.bank_base = vga.mem.map[VGAEMU_MAP_BANK_MODE].base_page << 12; + vga.mem.bank_len = vga.mem.map[VGAEMU_MAP_BANK_MODE].pages << 12; + + if(vga.mem.write_plane) { + first = vga.mem.plane_pages * vga.mem.write_plane; + } + else { + first = vga.mem.bank_pages * vga.mem.bank; + } + +#if 0 + /* + * this is too slow !!! -- sw + */ + k0 = vga.mem.map[VGAEMU_MAP_BANK_MODE].base_page; + k1 = k0 + vga.mem.map[VGAEMU_MAP_BANK_MODE].pages; + for(j = 0xa0; j < 0xc0; j++) { + if(j < k0 || j >= k1) { + i = vgaemu_unmap(j); + if(i) { + vga_msg("vgaemu_map_bank: failed to unmap page at 0x%x; reason: %d\n", j << 12, i); + } + else { + vga_deb2_bank("vgaemu_map_bank: page at 0x%x unmapped\n", j << 12); + } + } + } +#endif + + i = vga_emu_map(VGAEMU_MAP_BANK_MODE, first); + e_invalidate_full(0xa0000, 0x20000); + + if(i) { + vga_msg( + "vgaemu_map_bank: failed to map %uk (ofs %uk) at 0x%x; reason: %d\n", + vga.mem.map[VGAEMU_MAP_BANK_MODE].pages << 2, + first << 2, + vga.mem.map[VGAEMU_MAP_BANK_MODE].base_page << 12, + i + ); + } + else { + vga_deb_bank( + "vgaemu_map_bank: mapped %uk (ofs %uk) at 0x%x\n", + vga.mem.map[VGAEMU_MAP_BANK_MODE].pages << 2, + first << 2, + vga.mem.map[VGAEMU_MAP_BANK_MODE].base_page << 12 + ); + } + + return i; +} + + +/* + * DANG_BEGIN_FUNCTION vga_emu_set_textsize + * + * description: + * Sets the text mode resolution. Typically called after + * a font change. + * + * arguments: + * width - Number of text columns. + * height - Number of text rows. + * + * DANG_END_FUNCTION + * + */ + +int vga_emu_set_textsize(int width, int height) +{ + if(vga.mode_class != TEXT) return 1; + + vga.text_width = width; + vga.text_height = height; + vga.scan_len = vga.text_width << 1; + + return 1; +} + + +/* + * DANG_BEGIN_FUNCTION dirty_all_video_pages + * + * description: + * Marks the whole VGA memory as modified. + * + * DANG_END_FUNCTION + * + */ + +void dirty_all_video_pages(void) +{ + pthread_mutex_lock(&prot_mtx); + if (vga.mem.dirty_map) + memset(vga.mem.dirty_map, 1, vga.mem.pages); + pthread_mutex_unlock(&prot_mtx); +} + +static void _vgaemu_dirty_page(int page, int dirty) +{ + int k; + + if (page >= vga.mem.pages) { + dosemu_error("vgaemu: page out of range, %i (%i)\n", page, vga.mem.pages); + return; + } + v_printf("vgaemu: set page %i %s (%i)\n", page, dirty ? "dirty" : "clean", + vga.mem.dirty_map[page]); + /* prot_mtx should be locked by caller */ + vga.mem.dirty_map[page] = dirty; + + if(vga.mem.planes == 4) { /* MODE_X or PL4 */ + page &= ~0x30; + for(k = 0; k < vga.mem.planes; k++, page += 0x10) + vga.mem.dirty_map[page] = dirty; + } + + if(vga.mode_type == PL2) { + /* it's actually 4 planes, but we let everyone believe it's a 1-plane mode */ + page &= ~0x30; + vga.mem.dirty_map[page] = dirty; + page += 0x20; + vga.mem.dirty_map[page] = dirty; + } + + if(vga.mode_type == CGA) { + /* CGA uses two 8k banks */ + page &= ~0x2; + vga.mem.dirty_map[page] = dirty; + page += 0x2; + vga.mem.dirty_map[page] = dirty; + } + + if(vga.mode_type == HERC) { + /* Hercules uses four 8k banks */ + page &= ~0x6; + vga.mem.dirty_map[page] = dirty; + page += 0x2; + vga.mem.dirty_map[page] = dirty; + page += 0x2; + vga.mem.dirty_map[page] = dirty; + page += 0x2; + vga.mem.dirty_map[page] = dirty; + } +} + +void vgaemu_dirty_page(int page, int dirty) +{ + pthread_mutex_lock(&prot_mtx); + _vgaemu_dirty_page(page, dirty); + pthread_mutex_unlock(&prot_mtx); +} + +int vgaemu_is_dirty(void) +{ + int ret; + if (vga.color_modified) + return 1; + pthread_mutex_lock(&prot_mtx); + ret = _is_dirty(); + pthread_mutex_unlock(&prot_mtx); + return ret; +} + +/* + * DANG_BEGIN_FUNCTION dirty_all_vga_colors + * + * description: + * Marks all colors as changed. + * + * DANG_END_FUNCTION + * + */ + +void dirty_all_vga_colors(void) +{ + int i; + + vga.color_modified = True; + + for(i = 0; i < 0x10; i++) vga.attr.dirty[i] = True; /* palette regs */ + vga.attr.dirty[0x11] = True; /* border color */ + + for(i = 0; i < 256; i++) vga.dac.rgb[i].dirty = True; +} + + +/* + * DANG_BEGIN_FUNCTION changed_vga_colors + * + * description: + * Checks DAC and Attribute Controller to find all colors with + * changed RGB-values. + * Returns number of changed colors. + * Note: the list _must_ be large enough, that is, have at least + * min(256, (1 << vga.pixel_size)) entries! + * + * arguments: + * de - list of DAC entries to store changed colors in + * + * DANG_END_FUNCTION + * + * Note: vga.dac.rgb[i].index holds the dirty flag but in the returned + * DAC entries .index is the VGA color number. + * + */ + +int changed_vga_colors(void (*upd_func)(DAC_entry *, int, void *), void *arg) +{ + DAC_entry de; + int i, j, k; + unsigned long long cols; + unsigned char a, m; + +#if 0 /* change attr.c first! */ + if(!vga.color_modified) return 0; +#endif + + cols = 1ULL << vga.pixel_size; + if(cols > 256) cols = 256; /* We do not really support > 8 bit palettes. */ + + /* + * Modes with more than 16 colors will not use palette regs. + * Note: index = dirty flag! + */ + if(vga.pixel_size > 4) { + for(i = j = 0; i < cols; i++) { + if(vga.dac.rgb[i].dirty == True) { + de = vga.dac.rgb[i]; + m = vga.dac.pel_mask; + de.r &= m; de.g &= m; de.b &= m; + if (upd_func) + upd_func(&de, i, arg); + j++; + vga.dac.rgb[i].dirty = False; + vga_deb_col("changed_vga_colors: color 0x%02x\n", i); + } + } + } + else { + for(i = j = 0; i < cols; i++) { + + /* ATTR_MODE_CTL == 0x10 */ + /* ATTR_COL_SELECT == 0x14 */ + + k = i; + if(vga.mode_type == PL2) if(k >= 2) k += 2; + if(vga.mode_type == HERC && k) k = 15; /* use palette 15 for white */ + + /* + * Supply bits 6-7 resp. 4-7 from Attribute Controller; + * see VGADOC for details. + * Note: palette entries have only 6 bits. + */ + if(vga.attr.data[0x10] & 0x80) { + a = (vga.attr.data[k] & 0x0f) | + ((vga.attr.data[0x14] & 0x0f) << 4); /* bits 7-5 */ + } + else { + a = vga.attr.data[k] | + ((vga.attr.data[0x14] & 0x0c) << 4); /* bits 7-6 */ + } + + if( + vga.dac.rgb[a].dirty == True || + vga.attr.dirty[k] == True + ) { + vga.attr.dirty[k] = False; + de = vga.dac.rgb[a]; + m = vga.dac.pel_mask; + de.r &= m; de.g &= m; de.b &= m; + if (upd_func) + upd_func(&de, i, arg); + vga.dac.rgb[a].dirty = False; + j++; + vga_deb_col( + "changed_vga_colors: color 0x%02x, pal 0x%02x, rgb 0x%02x 0x%02x 0x%02x\n", + i, a, + (unsigned) vga.dac.rgb[a].r, + (unsigned) vga.dac.rgb[a].g, + (unsigned) vga.dac.rgb[a].b + ); + } + } + } + + vga.color_modified = False; + return j; +} + +static void vgaemu_adjust_instremu(int value) +{ + int i; + vga_mapping_type *vmt = &vga.mem.map[VGAEMU_MAP_BANK_MODE]; + + if (value == EMU_ALL_INST) { + if (vga.inst_emu != EMU_ALL_INST) { + v_printf("Seq_write_value: instemu on\n"); + pthread_mutex_lock(&prot_mtx); + for (i = 0; i < vga.mem.pages; i++) + _vga_emu_adjust_protection(i, 0, NONE, 1); + pthread_mutex_unlock(&prot_mtx); + } + } else { + if (vga.inst_emu != 0) { + v_printf("Seq_write_value: instemu off\n"); + dirty_all_video_pages(); + } + } + if (vga.inst_emu != value && + (config.cpu_vm == CPUVM_KVM || config.cpu_vm_dpmi == CPUVM_KVM)) + kvm_set_mmio(vmt->base_page << PAGE_SHIFT, vmt->pages << PAGE_SHIFT, + value != 0); + vga.inst_emu = value; +} + +/* + * DANG_BEGIN_FUNCTION vgaemu_adj_cfg + * + * description: + * Adjust VGAEmu according to VGA register changes. + * + * DANG_END_FUNCTION + * + */ + +void vgaemu_adj_cfg(unsigned what, unsigned msg) +{ + unsigned u, u0, u1; + static const char *txt1[] = { "byte", "odd/even (word)", "chain4 (dword)" }; + static const char *txt2[] = { "byte", "word", "dword" }; + + switch(what) { + case CFG_SEQ_ADDR_MODE: + u0 = vga.seq.addr_mode; + u = vga.seq.data[4] & 4 ? 0 : 1; + u = vga.seq.data[4] & 8 ? 2 : u; + vga.seq.addr_mode = u; + u1 = vga.seq.addr_mode == 0 ? 4 : 1; + if(u1 != vga.mem.planes) { + vga.mem.planes = u1; + vga.reconfig.mem = 1; + vga_msg("vgaemu_adj_cfg: mem reconfig (%u planes)\n", u1); + vgaemu_map_bank(); // update page protection + } + vgaemu_adjust_instremu(vga.mem.planes > 1 ? EMU_ALL_INST : 0); + if(msg || u != u0) vga_msg("vgaemu_adj_cfg: seq.addr_mode = %s\n", txt1[u]); + if (vga.mode_class == TEXT && vga.width < 2048) { + int horizontal_display_end = vga.crtc.data[0x1] + 1; + int horizontal_blanking_start = vga.crtc.data[0x2] + 1; + int multiplier = 9 - (vga.seq.data[1] & 1); + int width = _min(horizontal_display_end, horizontal_blanking_start) * + multiplier; + if ((vga.width != width) || (vga.char_width != multiplier)) { + vga.width = width; + vga.char_width = multiplier; + vga.text_width = horizontal_display_end; + vga.reconfig.display = 1; + vga.reconfig.re_init = 1; + } + } + break; + + case CFG_CRTC_ADDR_MODE: + if (vga.color_bits > 8) + return; + u0 = vga.crtc.addr_mode; + u1 = vga.scan_len; + u = vga.crtc.data[0x17] & 0x40 ? 0 : 1; + u = vga.crtc.data[0x14] & 0x40 ? 2 : u; + if (vga.scan_len > (255 << (u + 1))) + return; + if (vga.scan_len & ((1 << (u + 1)) - 1)) + return; + vga.crtc.addr_mode = u; + if (vga.config.standard) + vga.display_start = (vga.crtc.data[0x0d] + (vga.crtc.data[0x0c] << 8)) << + vga.crtc.addr_mode; + /* this shift should really be a rotation, depending on mode control bit 5 */ + vga.crtc.cursor_location = (vga.crtc.data[0x0f] + (vga.crtc.data[0x0e] << 8)) << + vga.crtc.addr_mode; + vga.scan_len = vga.crtc.data[0x13] << (vga.crtc.addr_mode + 1); + if (vga.scan_len == 0) vga.scan_len = 160; + if (u1 != vga.scan_len) vga.reconfig.mem = 1; + if(msg || u != u0) vga_msg("vgaemu_adj_cfg: crtc.addr_mode = %s, scan_len = %d\n", + txt2[u], vga.scan_len); + break; + + case CFG_CRTC_HEIGHT: + { + int vertical_total; + int vertical_retrace_start; + int vertical_retrace_end; + int vertical_display_end; + int vertical_multiplier; + int vertical_blanking_start; + int vertical_blanking_end; + int char_height; + int height; + + if (vga.height > 1024) + return; + + vertical_total = + vga.crtc.data[0x6] + + ((vga.crtc.data[0x7] & 0x1) << (8 - 0)) + + ((vga.crtc.data[0x7] & 0x20) << (9 - 5)); + vertical_retrace_start = + vga.crtc.data[0x10] + + ((vga.crtc.data[0x7] & 0x4) << (8 - 2)) + + ((vga.crtc.data[0x7] & 0x80) << (9 - 7)); + vertical_retrace_end = vga.crtc.data[0x11] & 0x0F; + vertical_display_end = + vga.crtc.data[0x12] + + ((vga.crtc.data[0x7] & 0x2) << (8 - 1)) + + ((vga.crtc.data[0x7] & 0x40) << (9 - 6)); + vertical_blanking_start = + vga.crtc.data[0x15] + + ((vga.crtc.data[0x7] & 0x8) << (8 - 3)) + + ((vga.crtc.data[0x9] & 0x20) << (9 - 5)); + vertical_blanking_end = + vga.crtc.data[0x16] & 0x7F; + char_height = (vga.crtc.data[0x9] & 0x1f) + 1; + vertical_multiplier = char_height << ((vga.crtc.data[0x9] & 0x80) >> 7); + /* see VGADOC: CGA is special for reg 9 */ + if(vga.mode_type == CGA) vertical_multiplier = char_height; + height = (_min(vertical_blanking_start, vertical_display_end) + 1) / + vertical_multiplier; + vga_msg("vgaemu_adj_cfg: vertical_total = %d\n", vertical_total); + vga_msg("vgaemu_adj_cfg: vertical_retrace_start = %d\n", vertical_retrace_start); + vga_msg("vgaemu_adj_cfg: vertical_retrace_end = %d\n", vertical_retrace_end); + vga_msg("vgaemu_adj_cfg: vertical_blanking_start = %d\n", vertical_blanking_start); + vga_msg("vgaemu_adj_cfg: vertical_blanking_end = %d\n", vertical_blanking_end); + vga_msg("vgaemu_adj_cfg: vertical_display_end = %d\n", vertical_display_end); + vga_msg("vgaemu_adj_cfg: vertical_multiplier = %d\n", vertical_multiplier); + vga_msg("vgaemu_adj_cfg: height = %d\n", height); + if (vga.line_compare != vga.crtc.line_compare / vertical_multiplier) { + vga.line_compare = vga.crtc.line_compare / vertical_multiplier; + dirty_all_video_pages(); + } + if (vga.mode_class == TEXT) { + height *= char_height; + } else { + char_height = height / 25; + if (char_height >= 16) + char_height = 16; + else if (char_height >= 14) + char_height = 14; + else + char_height = 8; + } + /* By Eric (eric@coli.uni-sb.de): */ + /* Required for 80x100 CGA "text graphics" with 8x2 font */ + if (vga.height != height || vga.char_height != char_height) { + vga.height = height; + vga.text_height = height / char_height; + vga.char_height = char_height; + vga_msg("vgaemu_adj_cfg: text_height=%d height=%d char_height=%d\n", + height, vertical_display_end+1, vga.char_height); + vga.reconfig.display = 1; + vga.reconfig.re_init = 1; + } + break; + } + case CFG_CRTC_WIDTH: + { + int horizontal_total; + int horizontal_display_end; + int horizontal_retrace_start; + int horizontal_retrace_end; + int horizontal_blanking_start; + int horizontal_blanking_end; + int multiplier; + int width; + + if (vga.width >= 2048) + return; + + /* everything below is in characters */ + horizontal_total = vga.crtc.data[0x0] +5; + horizontal_display_end = vga.crtc.data[0x1] + 1; + horizontal_blanking_start = vga.crtc.data[0x2] + 1; + horizontal_blanking_end = vga.crtc.data[0x3] & 0xF; + horizontal_retrace_start = vga.crtc.data[0x4]; + horizontal_retrace_end = vga.crtc.data[0x5] & 0xF; + if (vga.mode_class == TEXT) { + multiplier = 9 - (vga.seq.data[1] & 1); + } else { + multiplier = (vga.color_bits == 8 && vga.config.standard) ? 4 : 8; + } + if (vga.width % multiplier != 0) /* special user defined mode? */ + return; + width = _min(horizontal_display_end, horizontal_blanking_start) * + multiplier; + vga_msg("vgaemu_adj_cfg: horizontal_total = %d\n", horizontal_total); + vga_msg("vgaemu_adj_cfg: horizontal_retrace_start = %d\n", horizontal_retrace_start); + vga_msg("vgaemu_adj_cfg: horizontal_retrace_end = %d\n", horizontal_retrace_end); + vga_msg("vgaemu_adj_cfg: horizontal_blanking_start = %d\n", horizontal_blanking_start); + vga_msg("vgaemu_adj_cfg: horizontal_blanking_end = %d\n", horizontal_blanking_end); + vga_msg("vgaemu_adj_cfg: horizontal_display_end = %d\n", horizontal_display_end); + vga_msg("vgaemu_adj_cfg: multiplier = %d\n", multiplier); + vga_msg("vgaemu_adj_cfg: width = %d\n", width); + if ((vga.width != width) || (vga.char_width != multiplier)) { + vga.width = width; + vga.char_width = (multiplier >= 8) ? multiplier : 8; + vga.text_width = _min(horizontal_display_end, horizontal_blanking_start); + vga.reconfig.display = 1; + vga.reconfig.re_init = 1; + } + break; + } + case CFG_CRTC_LINE_COMPARE: + { + int vertical_multiplier; + + if (vga.height >= 1024) + return; + + vga.crtc.line_compare = + vga.crtc.data[0x18] + + ((vga.crtc.data[0x7] & 0x10) << (8 - 4)) + + ((vga.crtc.data[0x9] & 0x40) << (9 - 6)); + vertical_multiplier = ((vga.crtc.data[0x9] & 0x1F) +1) << + ((vga.crtc.data[0x9] & 0x80) >> 7); + vga_msg("vgaemu_adj_cfg: line_compare = %d\n", vga.crtc.line_compare); + if (vga.line_compare != vga.crtc.line_compare / vertical_multiplier) { + vga.line_compare = vga.crtc.line_compare / vertical_multiplier; + dirty_all_video_pages(); + } + break; + } + case CFG_MODE_CONTROL: + { + int oldclass, old_color_bits; + + if (vga.color_bits > 8) + return; + + oldclass = vga.mode_class; + vga.mode_class = (vga.gfx.data[6] & 1) ? GRAPH : TEXT; + vga.pixel_size = 4; + if (vga.mode_class == TEXT) { + vga.mode_type = vga.config.mono_port ? TEXT_MONO : TEXT; + } else { + unsigned char gdata = vga.gfx.data[5]; + unsigned char cdata = vga.crtc.data[0x17]; + unsigned char adata = vga.attr.data[0x12]; + + vga.pixel_size = 1; + if (gdata & 0x40) { + vga.mode_type = P8; + vga.pixel_size = 8; + } else if (gdata & 0x20) { + vga.mode_type = CGA; + vga.pixel_size = 2; + } else if (!(cdata & 2)) { + vga.mode_type = HERC; + } else if (adata == 5) { + vga.mode_type = PL2; + vga.pixel_size = 2; + } else if (adata == 1) { + vga.mode_type = (cdata & 1) ? PL1 : CGA; + } else { + vga.mode_type = PL4; + vga.pixel_size = 4; + } + } + old_color_bits = vga.color_bits; + vga.color_bits = vga.pixel_size; + vgaemu_map_bank(); // update page protection + vgaemu_adjust_instremu((vga.mode_type==PL4 || vga.mode_type==PL2) + ? EMU_ALL_INST : 0); + if (oldclass != vga.mode_class) { + vgaemu_adj_cfg(CFG_SEQ_ADDR_MODE, 0); + vgaemu_adj_cfg(CFG_CRTC_WIDTH, 0); + vgaemu_adj_cfg(CFG_CRTC_HEIGHT, 0); + } else if (old_color_bits != vga.color_bits) { + vgaemu_adj_cfg(CFG_CRTC_WIDTH, 0); + } + vga.mem.wrap = (vga.mode_type == CGA || vga.mode_class == TEXT ? + 32 : 64) * 1024; + vga.reconfig.re_init = 1; + break; + } + default: + vga_msg("vgaemu_adj_cfg: unknown item %u\n", what); + } +} + +static void vgaemu_config_scrub(void) +{ + if (config.vgaemu_memsize < 1024) { + error("VGA mem size must be at least 1024k\n"); + config.vgaemu_memsize = 1024; + } +} + +CONSTRUCTOR(static void init(void)) +{ + register_config_scrub(vgaemu_config_scrub); +} diff --git a/src/base/dev/vga/vgaemu_modelist.h b/src/base/dev/vga/vgaemu_modelist.h new file mode 100644 index 0000000..abc1944 --- /dev/null +++ b/src/base/dev/vga/vgaemu_modelist.h @@ -0,0 +1,161 @@ +/* + * *** This file is part of vgaemu.c *** + * + * + * List of all predefined video modes that VGAEmu supports. + * Modes defined via dosemu.conf will be appended to this list. + * + * special mode numbers: + * + * -1: no mode number available + * -2: (VESA only) number will be generated automatically + * + * Automatically generated modes are given mode numbers starting + * with VBE_FIRST_OEM_MODE. + * + */ + +#define VBE_FIRST_OEM_MODE 0x140 + +static vga_mode_info vga_mode_table[] = { + + /* + * The standard CGA/EGA/MCGA/VGA modes. + * Modifying the definitions for the standard modes + * will confuse the VGA Emulator - so don't do that! + */ + + /* generate screen width and height from characters x,y and font size x,y */ +#define VGA_MODE_WH(cx,cy,fx,fy) (cx)*(fx),(cy)*(fy),(cx),(cy),(fx),(fy) + + {0x00, -1, TEXT, TEXT, 4, VGA_MODE_WH( 40, 25, 9, 16)}, + {0x01, -1, TEXT, TEXT, 4, VGA_MODE_WH( 40, 25, 9, 16)}, + {0x02, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 25, 9, 16)}, + {0x03, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 25, 9, 16)}, + {0x03, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 25, 8, 16)}, + {0x03, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 25, 8, 14)}, + {0x03, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 25, 8, 8)}, + {0x03, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 21, 9, 16)}, + {0x03, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 28, 9, 16)}, + {0x03, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 43, 8, 14)}, + {0x03, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 43, 8, 8)}, + {0x03, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 50, 8, 8)}, + {0x03, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 60, 8, 8)}, + {0x04, -1, GRAPH, CGA, 2, VGA_MODE_WH( 40, 25, 8, 8)}, + {0x05, -1, GRAPH, CGA, 2, VGA_MODE_WH( 40, 25, 8, 8)}, + {0x06, -1, GRAPH, CGA, 1, VGA_MODE_WH( 80, 25, 8, 8)}, + {0x07, -1, TEXT,TEXT_MONO, 4, VGA_MODE_WH( 80, 25, 9, 16)}, + {0x0d, -1, GRAPH, PL4, 4, VGA_MODE_WH( 40, 25, 8, 8)}, + {0x0e, -1, GRAPH, PL4, 4, VGA_MODE_WH( 80, 25, 8, 8)}, + {0x0f, -1, GRAPH, PL2, 2, VGA_MODE_WH( 80, 25, 8, 14)}, + {0x10, -1, GRAPH, PL4, 4, VGA_MODE_WH( 80, 25, 8, 14)}, + {0x11, -1, GRAPH, PL1, 1, VGA_MODE_WH( 80, 30, 8, 16)}, + {0x12, -1, GRAPH, PL4, 4, VGA_MODE_WH( 80, 30, 8, 16)}, + {0x13, -1, GRAPH, P8, 8, VGA_MODE_WH( 40, 25, 8, 8)}, + + /* + * Trident 8900 SVGA modes. + * Maybe we are going to emulate a Trident 8900, so we already use the + * Trident mode numbers in advance. + * Note: we do not have 8x11 fonts. So adjust some modes to 8x14 -stsp + */ +#if 0 + {0x50, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 30, 8, 16)}, +#else + {0x50, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 30, 9, 16)}, +#endif +#if 0 + {0x51, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 43, 8, 11)}, +#else + {0x51, -1, TEXT, TEXT, 4, VGA_MODE_WH( 80, 43, 9, 16)}, +#endif +#if 0 + {0x52, 0x108, TEXT, TEXT, 4, VGA_MODE_WH( 80, 60, 8, 8)}, + #else + {0x52, 0x108, TEXT, TEXT, 4, VGA_MODE_WH( 80, 60, 9, 16)}, + #endif + #if 0 + {0x53, 0x109, TEXT, TEXT, 4, VGA_MODE_WH( 132, 25, 8, 14)}, +#else + {0x53, 0x109, TEXT, TEXT, 4, VGA_MODE_WH( 132, 25, 9, 16)}, +#endif + {0x54, -1, TEXT, TEXT, 4, VGA_MODE_WH( 132, 30, 8, 16)}, +#if 0 + {0x55, 0x10a, TEXT, TEXT, 4, VGA_MODE_WH( 132, 43, 8, 11)}, +#else + {0x55, 0x10a, TEXT, TEXT, 4, VGA_MODE_WH( 132, 43, 9, 16)}, +#endif +#if 0 + {0x56, 0x10c, TEXT, TEXT, 4, VGA_MODE_WH( 132, 60, 8, 8)}, +#else + {0x56, 0x10c, TEXT, TEXT, 4, VGA_MODE_WH( 132, 60, 9, 16)}, +#endif + {0x57, -1, TEXT, TEXT, 4, VGA_MODE_WH( 132, 25, 9, 14)}, + {0x58, -1, TEXT, TEXT, 4, VGA_MODE_WH( 132, 30, 9, 16)}, +#if 0 + {0x59, -1, TEXT, TEXT, 4, VGA_MODE_WH( 132, 43, 9, 11)}, +#else + {0x59, -1, TEXT, TEXT, 4, VGA_MODE_WH( 132, 43, 9, 14)}, +#endif + {0x5a, -1, TEXT, TEXT, 4, VGA_MODE_WH( 132, 60, 9, 8)}, + {0x5b, 0x102, GRAPH, PL4, 4, VGA_MODE_WH( 100, 75, 8, 8)}, + {0x5c, 0x100, GRAPH, P8, 8, VGA_MODE_WH( 80, 25, 8, 16)}, + {0x5d, 0x101, GRAPH, P8, 8, VGA_MODE_WH( 80, 30, 8, 16)}, + {0x5e, 0x103, GRAPH, P8, 8, VGA_MODE_WH( 100, 75, 8, 8)}, + {0x5f, 0x104, GRAPH, PL4, 4, VGA_MODE_WH( 128, 48, 8, 16)}, + /* {0x60, -1, GRAPH, CGA, 2, VGA_MODE_WH( 128, 48, 8, 16)}, Not supported! */ + {0x61, -1, GRAPH, PL4, 4, VGA_MODE_WH( 96, 64, 8, 16)}, + {0x62, 0x105, GRAPH, P8, 8, VGA_MODE_WH( 128, 48, 8, 16)}, + {0x63, 0x106, GRAPH, PL4, 4, VGA_MODE_WH( 160, 64, 8, 16)}, + {0x64, 0x107, GRAPH, P8, 8, VGA_MODE_WH( 160, 64, 8, 16)}, + {0x6a, -1, GRAPH, PL4, 4, VGA_MODE_WH( 100, 75, 8, 8)}, + {0x6b, -1, GRAPH, P24, 24, VGA_MODE_WH( 40, 25, 8, 8)}, + {0x6c, 0x112, GRAPH, P24, 24, VGA_MODE_WH( 80, 30, 8, 16)}, + {0x6d, 0x115, GRAPH, P24, 24, VGA_MODE_WH( 100, 75, 8, 8)}, + {0x70, -1, GRAPH, P15, 15, VGA_MODE_WH( 64, 30, 8, 16)}, + {0x71, -1, GRAPH, P16, 16, VGA_MODE_WH( 64, 30, 8, 16)}, + {0x74, 0x110, GRAPH, P15, 15, VGA_MODE_WH( 80, 30, 8, 16)}, + {0x75, 0x111, GRAPH, P16, 16, VGA_MODE_WH( 80, 30, 8, 16)}, + {0x76, 0x113, GRAPH, P15, 15, VGA_MODE_WH( 100, 75, 8, 8)}, + {0x77, 0x114, GRAPH, P16, 16, VGA_MODE_WH( 100, 75, 8, 8)}, + {0x78, 0x116, GRAPH, P15, 15, VGA_MODE_WH( 128, 48, 8, 16)}, + {0x79, 0x117, GRAPH, P16, 16, VGA_MODE_WH( 128, 48, 8, 16)}, + {0x7e, 0x10d, GRAPH, P15, 15, VGA_MODE_WH( 40, 25, 8, 8)}, + {0x7f, 0x10e, GRAPH, P16, 16, VGA_MODE_WH( 40, 25, 8, 8)}, + + /* + * VBE only modes. + */ + { -1, 0x118, GRAPH, P24, 24, VGA_MODE_WH( 128, 48, 8, 16)}, + { -1, 0x119, GRAPH, P15, 15, VGA_MODE_WH( 160, 64, 8, 16)}, + { -1, 0x11a, GRAPH, P16, 16, VGA_MODE_WH( 160, 64, 8, 16)}, + { -1, 0x11b, GRAPH, P24, 24, VGA_MODE_WH( 160, 64, 8, 16)}, + { -1, 0x120, GRAPH, P8, 8, VGA_MODE_WH( 200, 75, 8, 16)}, +}; + + +/* + * To add a simple mode definition (GRAPH, 64k bank segment at 0xa000, + * color depth 8, 15, 16, 24, 32), put the mode width and height into + * vgaemu_simple_mode_list[][]. Text sizes, character box sizes and + * mode numbers (VESA only) will be automatically generated. + * These modes are created after vga_mode_table[] has been parsed and before + * mode definitions in dosemu.conf are considered. + * If a mode resolution (width, height, colors) already exists, no + * new mode is created; instead the existing mode of the same resolution + * is activated (if it hasn't been already). + * + */ + +static int vgaemu_simple_mode_list[][2] = { + { 320, 200}, + { 320, 240}, + { 400, 300}, + { 480, 360}, + { 512, 384}, + { 560, 420}, + { 640, 480}, + { 800, 600}, + {1024, 768} +}; + diff --git a/src/base/dev/vga/vgafonts.c b/src/base/dev/vga/vgafonts.c new file mode 100644 index 0000000..0bec339 --- /dev/null +++ b/src/base/dev/vga/vgafonts.c @@ -0,0 +1,1242 @@ +/* + * Bitmap fonts for the VGA emulation; taken from + * ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip + * + * See COPYING.DOSEMU for the copyright notice. + */ +#include "vgaemu.h" +unsigned char vga_rom_08[256 * 8] = { + 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, + 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x18, 0x0c, 0x78, + 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x1c, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0x7e, 0xc3, 0x3c, 0x06, 0x3e, 0x66, 0x3f, 0x00, + 0xcc, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0xe0, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x30, 0x30, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x00, 0x00, 0x78, 0xc0, 0xc0, 0x78, 0x0c, 0x38, + 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, + 0xcc, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0xe0, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00, + 0xcc, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x7c, 0xc6, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00, + 0xe0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xc6, 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, + 0x30, 0x30, 0x00, 0x78, 0xcc, 0xfc, 0xcc, 0x00, + 0x1c, 0x00, 0xfc, 0x60, 0x78, 0x60, 0xfc, 0x00, + 0x00, 0x00, 0x7f, 0x0c, 0x7f, 0xcc, 0x7f, 0x00, + 0x3e, 0x6c, 0xcc, 0xfe, 0xcc, 0xcc, 0xce, 0x00, + 0x78, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0xcc, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0xe0, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xe0, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xcc, 0x00, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8, + 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, 0x00, + 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, + 0x18, 0x18, 0x7e, 0xc0, 0xc0, 0x7e, 0x18, 0x18, + 0x38, 0x6c, 0x64, 0xf0, 0x60, 0xe6, 0xfc, 0x00, + 0xcc, 0xcc, 0x78, 0xfc, 0x30, 0xfc, 0x30, 0x30, + 0xf8, 0xcc, 0xcc, 0xfa, 0xc6, 0xcf, 0xc6, 0xc7, + 0x0e, 0x1b, 0x18, 0x3c, 0x18, 0x18, 0xd8, 0x70, + 0x1c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x7e, 0x00, + 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x1c, 0x00, 0x78, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x1c, 0x00, 0xcc, 0xcc, 0xcc, 0x7e, 0x00, + 0x00, 0xf8, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0x00, + 0xfc, 0x00, 0xcc, 0xec, 0xfc, 0xdc, 0xcc, 0x00, + 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, + 0x30, 0x00, 0x30, 0x60, 0xc0, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0xc0, 0xc0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0x0c, 0x0c, 0x00, 0x00, + 0xc3, 0xc6, 0xcc, 0xde, 0x33, 0x66, 0xcc, 0x0f, + 0xc3, 0xc6, 0xcc, 0xdb, 0x37, 0x6f, 0xcf, 0x03, + 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x33, 0x66, 0xcc, 0x66, 0x33, 0x00, 0x00, + 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0xdb, 0xee, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, + 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0xc8, 0xdc, 0x76, 0x00, + 0x00, 0x78, 0xcc, 0xf8, 0xcc, 0xf8, 0xc0, 0xc0, + 0x00, 0xfc, 0xcc, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, + 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, + 0xfc, 0xcc, 0x60, 0x30, 0x60, 0xcc, 0xfc, 0x00, + 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0x70, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0xc0, + 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x00, + 0xfc, 0x30, 0x78, 0xcc, 0xcc, 0x78, 0x30, 0xfc, + 0x38, 0x6c, 0xc6, 0xfe, 0xc6, 0x6c, 0x38, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x6c, 0xee, 0x00, + 0x1c, 0x30, 0x18, 0x7c, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, 0x00, 0x00, + 0x06, 0x0c, 0x7e, 0xdb, 0xdb, 0x7e, 0x60, 0xc0, + 0x38, 0x60, 0xc0, 0xf8, 0xc0, 0x60, 0x38, 0x00, + 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x00, + 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00, + 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0xfc, 0x00, + 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xfc, 0x00, + 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xfc, 0x00, + 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0x70, + 0x30, 0x30, 0x00, 0xfc, 0x00, 0x30, 0x30, 0x00, + 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, + 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0f, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x3c, 0x1c, + 0x78, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x70, 0x18, 0x30, 0x60, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +unsigned char vga_rom_14[256 * 14] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, + 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, + 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, + 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, + 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, + 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, + 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, + 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x70, 0xf0, + 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, + 0x7f, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, + 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe, 0xf8, + 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x06, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x06, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, + 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x7c, + 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, + 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, + 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, + 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x28, 0x6c, 0xfe, 0x6c, 0x28, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, + 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, + 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, + 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x66, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, + 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, + 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, + 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, + 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, + 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, + 0x3c, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, + 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x60, + 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x0c, + 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, + 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x0c, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, + 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, + 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, + 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, + 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, + 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0xfc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, + 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x62, 0x66, + 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, + 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, + 0xc0, 0xde, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, + 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe6, 0x66, 0x6c, 0x6c, + 0x78, 0x6c, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xe6, + 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, + 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, + 0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, + 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0xe6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, + 0x38, 0x0c, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, + 0xd6, 0xd6, 0xfe, 0x7c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x38, + 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, + 0x8c, 0x18, 0x30, 0x60, 0xc2, 0xc6, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, + 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x3c, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, + 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60, + 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, + 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, + 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, + 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, + 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, + 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, + 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, + 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, + 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, 0xd6, 0xd6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, + 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0x7c, + 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, + 0xc6, 0x70, 0x1c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, + 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, + 0xc6, 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, + 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, + 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x66, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, + 0x70, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, + 0x6c, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc2, + 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, 0x00, + 0xcc, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, + 0x76, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, + 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, + 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0xcc, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x60, + 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, + 0x76, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, + 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, + 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcc, 0xcc, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, + 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x38, + 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x60, + 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x10, + 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, + 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, + 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x76, 0x36, 0x7e, 0xd8, 0xd8, + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c, + 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xce, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, + 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x60, + 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x78, 0xcc, + 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, 0x00, 0xc6, + 0xc6, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, + 0x38, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0x00, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60, + 0x60, 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, + 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, + 0x18, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xcc, 0xcc, + 0xf8, 0xc4, 0xcc, 0xde, 0xcc, 0xcc, 0xc6, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, + 0x7e, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, + 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x76, 0xdc, + 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, + 0xc6, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c, + 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, + 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, + 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, + 0x30, 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, + 0x00, 0xc0, 0xc0, 0xc6, 0xcc, 0xd8, 0x30, 0x66, + 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x3c, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, + 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x55, 0xaa, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, + 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, + 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, + 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, + 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, + 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, + 0xc0, 0xc0, 0x40, 0x00, 0x00, 0x00, 0xfe, 0xc6, + 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, + 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, + 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, + 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0xee, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, + 0x3e, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, + 0x60, 0x60, 0x7c, 0x60, 0x60, 0x30, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, + 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, + 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, + 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0xec, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, + 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* this font comes from vga.bdf */ +unsigned char vga_rom_16[256 * 16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xa5, + 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xdb, + 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, + 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, + 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, + 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, + 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, + 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, + 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, + 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, + 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x1e, 0x06, 0x0e, 0x1a, 0x78, 0xcc, + 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, + 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, + 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, + 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, + 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, + 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, + 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, + 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, + 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, + 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, + 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, + 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, + 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x6c, 0xfe, + 0x6c, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, + 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, + 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, + 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, + 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, + 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, + 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, + 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, + 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xd6, 0xd6, + 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, + 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, + 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, + 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, + 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, + 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, + 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, + 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, + 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, + 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, + 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, + 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, + 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, + 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, + 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, + 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, + 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, + 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, + 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, + 0xd6, 0xfe, 0xee, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x7c, 0x38, 0x38, + 0x7c, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x86, 0x0c, 0x18, 0x30, + 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, + 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, + 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, + 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, + 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, + 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, + 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, + 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, + 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, + 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, + 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, + 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, + 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, + 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, + 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, + 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, + 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, + 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, + 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x76, 0x36, + 0x7e, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, + 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60, 0x60, 0x60, + 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, + 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, + 0x7e, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xcc, 0xcc, 0xf8, 0xc4, 0xcc, 0xde, + 0xcc, 0xcc, 0xcc, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, + 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, + 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, + 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, + 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, + 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, + 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, + 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, + 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, + 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, + 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, + 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, + 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, + 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, + 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, + 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, + 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, + 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, + 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, + 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, + 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, + 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, + 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, + 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, + 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, + 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, + 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, + 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + + +/* + * Currently, we don't support those. + * + * Keep the size definitions in include/vgaemu.h consistent! + */ +unsigned char vga_rom_14_alt[1] = { 0 }; +unsigned char vga_rom_16_alt[1] = { 0 }; + diff --git a/src/base/emu-i386/Makefile b/src/base/emu-i386/Makefile new file mode 100644 index 0000000..3d70849 --- /dev/null +++ b/src/base/emu-i386/Makefile @@ -0,0 +1,14 @@ +top_builddir=../../.. +include $(top_builddir)/Makefile.conf + +CFILES = cpu.c do_vm86.c cputime.c coopth_vm86.c +ifeq ($(KVM),1) +CFILES += kvm.c +SFILES = kvmmon.S +endif + +include $(REALTOPDIR)/src/Makefile.common + +all: lib + +install: all diff --git a/src/base/emu-i386/coopth_vm86.c b/src/base/emu-i386/coopth_vm86.c new file mode 100644 index 0000000..5078328 --- /dev/null +++ b/src/base/emu-i386/coopth_vm86.c @@ -0,0 +1,294 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: coopth vm86 back-end. + * + * Author: Stas Sergeev + */ +#include +#include "emu.h" +#include "cpu.h" +#include "hlt.h" +#include "utilities.h" +#include "timers.h" +#include "dos2linux.h" +#include "coopth.h" +#include "coopth_be.h" + +struct co_vm86 { + struct vm86_regs *regs; + Bit16u hlt_off; + void (*post)(void); +}; + +#define INVALID_HLT 0xffff + +struct co_vm86_pth { + Bit16u hlt_off; + Bit16u ret_cs, ret_ip; + Bit16u owner; + uint64_t dbg; + unsigned prepped:1; +}; + +static struct co_vm86 coopth86[MAX_COOPTHREADS]; +static struct co_vm86_pth coopth86_pth[COOPTH_POOL_SIZE]; + +static __TLS int (*ctx_is_valid)(void); + +static int do_start_custom(int tid); + +static int is_active(int tid, int idx) +{ + struct vm86_regs *regs = coopth86[tid].regs; + + return (regs->cs == BIOS_HLT_BLK_SEG && + LO_WORD(regs->eip) == coopth86_pth[idx].hlt_off); +} + +static void do_callf(int tid, int idx) +{ + struct vm86_regs *regs = coopth86[tid].regs; + + if (ctx_is_valid) { + int ok = ctx_is_valid(); + if (!ok) + dosemu_error("coopth: unsafe context switch\n"); + } + coopth86_pth[idx].ret_cs = regs->cs; + coopth86_pth[idx].ret_ip = LO_WORD(regs->eip); + regs->cs = BIOS_HLT_BLK_SEG; + assert(coopth86[tid].hlt_off != INVALID_HLT); + LO_WORD(regs->eip) = coopth86[tid].hlt_off; +} + +static void do_retf(int tid, int idx) +{ + struct vm86_regs *regs = coopth86[tid].regs; + + regs->cs = coopth86_pth[idx].ret_cs; + LO_WORD(regs->eip) = coopth86_pth[idx].ret_ip; + coopth86_pth[idx].hlt_off = INVALID_HLT; +} + +static int to_sleep(int tid) +{ + struct vm86_regs *regs = coopth86[tid].regs; + + if (!(regs->eflags & VIF)) { + dosemu_error("sleep with interrupts disabled\n"); + return 0; + } + return 1; +} + +static void do_sleep(void) +{ + dosemu_sleep(); +} + +static void do_prep(int tid, int idx) +{ + coopth86_pth[idx].hlt_off = INVALID_HLT; + coopth86_pth[idx].prepped = 1; +} + +static uint64_t get_dbg_val(int tid, int idx) +{ + return coopth86_pth[idx].dbg; +} + +static const struct coopth_be_ops ops = { + .is_active = is_active, + .callf = do_callf, + .retf = do_retf, + .prep = do_prep, + .to_sleep = to_sleep, + .sleep = do_sleep, + .get_dbg_val = get_dbg_val, + .id = COOPTH_BE_VM86, +}; + +static void coopth_hlt(Bit16u offs, HLT_ARG(arg)) +{ + struct co_vm86 *thr = (struct co_vm86 *)arg + offs; + int tid = thr - coopth86; + + assert(tid >= 0 && tid < MAX_COOPTHREADS); + coopth_run_thread_internal(tid); +} + +static void coopth_auto_hlt(Bit16u offs, HLT_ARG(arg)) +{ + struct co_vm86 *thr = arg; + struct vm86_regs *regs = thr->regs; + int tid = thr - coopth86; + + assert(tid >= 0 && tid < MAX_COOPTHREADS); + switch (offs) { + case 0: + LO_WORD(regs->eip)++; // skip hlt + do_start_custom(tid); + break; + case 1: { + struct crun_ret ret = coopth_run_thread_internal(tid); + if (ret.term) { + thr->post(); + coopth_call_post_internal(tid); + do_prep(tid, ret.idx); + } + break; + } + } +} + +static int register_handler(const char *name, + emu_hlt_func fn, void *arg, int len) +{ + emu_hlt_t hlt_hdlr = HLT_INITIALIZER; + hlt_hdlr.name = name; + hlt_hdlr.len = len; + hlt_hdlr.func = fn; + hlt_hdlr.arg = arg; + return hlt_register_handler_vm86(hlt_hdlr); +} + +int coopth_create(const char *name, coopth_func_t func) +{ + int num; + struct co_vm86 *thr; + + num = coopth_create_internal(name, func, &ops); + if (num == -1) + return -1; + thr = &coopth86[num]; + thr->hlt_off = register_handler(name, coopth_hlt, thr, 1); + thr->regs = ®S; + return num; +} + +int coopth_create_multi(const char *name, int len, coopth_func_t func) +{ + int i, num; + struct co_vm86 *thr; + u_short hlt_off; + + num = coopth_create_multi_internal(name, len, func, &ops); + if (num == -1) + return -1; + hlt_off = register_handler(name, coopth_hlt, &coopth86[num], len); + for (i = 0; i < len; i++) { + thr = &coopth86[num + i]; + thr->hlt_off = hlt_off + i; + thr->regs = ®S; + } + return num; +} + +int coopth_create_vm86(const char *name, coopth_func_t func, + void (*post)(void), uint16_t *hlt_off) +{ + int num; + struct co_vm86 *thr; + Bit16u ret; + + num = coopth_create_internal(name, func, &ops); + if (num == -1) + return -1; + thr = &coopth86[num]; + ret = register_handler(name, coopth_auto_hlt, thr, 2); + thr->hlt_off = ret; // for some future unregister + thr->post = post; + thr->regs = ®S; + *hlt_off = ret; + return num; +} + +int coopth_start(int tid, void *arg) +{ + struct cstart_ret ret = coopth_start_internal(tid, arg, do_retf); + struct vm86_regs *regs = coopth86[tid].regs; + uint64_t dbg = ((uint64_t)regs->eax << 32) | regs->ebx; + + if (ret.idx == -1) + return -1; + if (!ret.detached) { + assert(coopth86[tid].hlt_off != INVALID_HLT); + coopth86_pth[ret.idx].hlt_off = coopth86[tid].hlt_off; + coopth86_pth[ret.idx].owner = sda_cur_psp(sda); + coopth86_pth[ret.idx].dbg = dbg; + do_callf(tid, ret.idx); + } + return 0; +} + +static int do_start_custom(int tid) +{ + int idx = coopth_start_custom_internal(tid, NULL); + struct vm86_regs *regs = coopth86[tid].regs; + uint64_t dbg = ((uint64_t)regs->eax << 32) | regs->ebx; + + if (idx == -1) + return -1; + assert(regs->cs == BIOS_HLT_BLK_SEG); + assert(coopth86_pth[idx].hlt_off == INVALID_HLT); + coopth86_pth[idx].hlt_off = LO_WORD(regs->eip); + coopth86_pth[idx].owner = sda_cur_psp(sda); + coopth86_pth[idx].dbg = dbg; + return 0; +} + +void coopth_join_vm86(int tid) +{ + return coopth_join_internal(tid, vm86_helper); +} + +int coopth_flush_vm86(void) +{ + return coopth_flush_internal(COOPTH_BE_VM86, vm86_helper); +} + +int coopth_wants_sleep_vm86(void) +{ + return coopth_wants_sleep_internal(COOPTH_BE_VM86); +} + +void coopth_set_ctx_checker_vm86(int (*checker)(void)) +{ + ctx_is_valid = checker; +} + +int coopth_get_thread_count_in_process_vm86(void) +{ + int i; + int cnt = 0; + for (i = 0; i < COOPTH_POOL_SIZE; i++) { + if (!coopth86_pth[i].prepped || coopth86_pth[i].hlt_off == INVALID_HLT) + continue; + if (coopth86_pth[i].owner == sda_cur_psp(sda)) + cnt++; + } + return cnt; +} + +void coopth_leave_vm86(void) +{ + struct co_vm86 *thr = &coopth86[coopth_get_tid()]; + coopth_leave_internal(); + assert(thr->post); + thr->post(); +} diff --git a/src/base/emu-i386/cpu.c b/src/base/emu-i386/cpu.c new file mode 100644 index 0000000..49a1270 --- /dev/null +++ b/src/base/emu-i386/cpu.c @@ -0,0 +1,482 @@ +/* + * DANG_BEGIN_MODULE + * + * REMARK + * CPU/V86 support for dosemu + * /REMARK + * + * DANG_END_MODULE + * + * much of this code originally written by Matthias Lautner + * taken over by: + * Robert Sanders, gt8134b@prism.gatech.edu + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dosemu_config.h" +#include "memory.h" +#include "emu.h" +#ifdef __linux__ +#include "sys_vm86.h" +#endif +#include "port.h" +#include "int.h" +#include "emudpmi.h" +#include "priv.h" +#include "instremu.h" +#include "kvm.h" + +#ifdef X86_EMULATOR +#include "simx86/syncpu.h" +#include "cpu-emu.h" +#define CRs TheCPU.cr +#define DRs TheCPU.dr +#define TRs TheCPU.tr +#else +/* Extra CPU registers. Note that GDTR,LDTR,IDTR are internal + * to the cpuemu and are defined in emu-ldt.c: + */ +static unsigned int CRs[5] = +{ + 0x00000013, /* valid bits: 0xe005003f */ + 0x00000000, /* invalid */ + 0x00000000, + 0x00000000, + 0x00000000 +}; + +/* + * DR0-3 = linear address of breakpoint 0-3 + * DR4=5 = reserved + * DR6 b0-b3 = BP active + * b13 = BD + * b14 = BS + * b15 = BT + * DR7 b0-b1 = G:L bp#0 + * b2-b3 = G:L bp#1 + * b4-b5 = G:L bp#2 + * b6-b7 = G:L bp#3 + * b8-b9 = GE:LE + * b13 = GD + * b16-19= LLRW bp#0 LL=00(1),01(2),11(4) + * b20-23= LLRW bp#1 RW=00(x),01(w),11(rw) + * b24-27= LLRW bp#2 + * b28-31= LLRW bp#3 + */ +static unsigned int DRs[8] = +{ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xffff1ff0, + 0x00000400, + 0xffff1ff0, + 0x00000400 +}; + +static unsigned int TRs[2] = +{ + 0x00000000, + 0x00000000 +}; +#endif + +/* fpu_state needs to be paragraph aligned for fxrstor/fxsave */ +emu_fpstate vm86_fpu_state __attribute__((aligned(16))); +fenv_t dosemu_fenv; +static void fpu_reset(void); + +/* + * DANG_BEGIN_FUNCTION cpu_trap_0f + * + * process opcodes 0F xx xx trapped by GP_fault + * returns 1 if handled, 0 otherwise + * Main difference with previous version: bits in our pseudo-control + * regs can now be written. This should make CPU detection pgms happy. + * + * DANG_END_FUNCTION + * + */ +int cpu_trap_0f (unsigned char *csp, cpuctx_t *scp) +{ + int increment_ip = 0; + uint16_t *reg16[8] = { &LWORD(eax), &LWORD(ecx), &LWORD(edx), &LWORD(ebx), + &LWORD(esp), &LWORD(ebp), &LWORD(esi), &LWORD(edi) }; + g_printf("CPU: TRAP op 0F %02x %02x\n",csp[1],csp[2]); + + if (csp[1] == 0x06) { + /* CLTS - ignore */ + increment_ip = 2; + } + else if (csp[1] == 0x31) { + /* ref: Van Gilluwe, "The Undocumented PC". The program + * 'cpurdtsc.exe' traps here */ + { + struct timeval tv; + unsigned long long t; + + /* try to do the best you can to return something + * meaningful, assume clk=100MHz */ + gettimeofday(&tv, NULL); + t = (unsigned long long)tv.tv_sec*100000000 + + (unsigned long long)tv.tv_usec*100; + REG(eax)=t & 0xffffffff; + REG(edx)=t >> 32; + } + increment_ip = 2; + } + else if ((((csp[1] & 0xfc)==0x20)||(csp[1]==0x24)||(csp[1]==0x26)) && + ((csp[2] & 0xc0) == 0xc0)) { + unsigned int *cdt; + unsigned int *srg; + int idx; + boolean rnotw; + + rnotw = ((csp[1]&2)==0); + idx = (csp[2]>>3)&7; + switch (csp[1]&7) { + case 0: case 2: cdt=CRs; + if (idx>4) return 0; + break; + case 1: case 3: cdt=DRs; break; + case 4: case 6: cdt=TRs; + if (idx<6) return 0; + idx -= 6; + break; + default: return 0; + } + + switch (csp[2]&7) { + case 0: srg = (scp ? &_eax:&_EAX); + break; + case 1: srg = (scp ? &_ecx:&_ECX); + break; + case 2: srg = (scp ? &_edx:&_EDX); + break; + case 3: srg = (scp ? &_ebx:&_EBX); + break; + case 6: srg = (scp ? &_esi:&_ESI); + break; + case 7: srg = (scp ? &_edi:&_EDI); + break; + default: /* ESP(4),EBP(5) */ + return 0; + } + if (rnotw) { + *srg = cdt[idx]; + g_printf("CPU: read =%08x\n",*srg); + } else { + g_printf("CPU: write=%08x\n",*srg); + if (cdt==CRs) { + /* special cases... they are too many, I hope this + * will suffice for all */ + if (idx==0) cdt[idx] = (*srg&0xe005002f)|0x10; + else if ((idx==1)||(idx==4)) return 0; + } + else cdt[idx] = *srg; + } + increment_ip = 3; + } else if (csp[1] == 0) { // SLDT, STR, ... + switch (csp[2] & 0xc0) { + case 0xc0: // register dest + *reg16[csp[2] & 7] = 0; + increment_ip = 3; + break; + default: + error("unsupported SLDT dest %x\n", csp[2]); + increment_ip = instr_len(csp, 0); + break; + } + } else if (csp[1] == 1) { // SGDT, SIDT, SMSW ... + switch (csp[2] & 0xc0) { + case 0xc0: // register dest + /* some meaningful value for smsw, 0 for rest */ + *reg16[csp[2] & 7] = ((csp[2] & 0x28) == 0x20) ? 0x31 : 0; + increment_ip = 3; + break; + default: + error("unsupported SMSW dest %x\n", csp[2]); + increment_ip = instr_len(csp, 0); + break; + } + } + if (increment_ip) { + if (scp) + _eip += increment_ip; + else + LWORD(eip) += increment_ip; + return 1; + } + /* all other trapped combinations: + * 0f 0a, 0c..0f, 27, 29, 2b..2f, c2..c6, d0..fe, etc. + */ + return 0; +} + +void cpu_reset(void) +{ +#ifdef X86_EMULATOR + reset_emu_cpu(); +#endif + /* ax,bx,cx,dx,si,di,bp,fs,gs can probably can be anything */ + REG(eax) = 0; + REG(ebx) = 0; + REG(ecx) = 0; + REG(edx) = 0; + REG(esi) = 0; + REG(edi) = 0; + REG(ebp) = 0; + REG(eip) = 0; + SREG(cs) = 0xffff; + REG(esp) = 0xfffe; + SREG(ss) = 0; /* This is the standard pc bios stack */ + SREG(es) = 0; /* standard pc es */ + SREG(ds) = 0x40; /* standard pc ds */ + SREG(fs) = 0; + SREG(gs) = 0; + REG(eflags) = 0; + fpu_reset(); +} + +#if 0 +static void fpu_init(void) +{ +#ifdef __x86_64__ + vm86_fpu_state.cwd = 0x037F; + vm86_fpu_state.swd = 0; + vm86_fpu_state.ftw = 0xFFFF; // bochs +#else + vm86_fpu_state.cw = 0x40; + vm86_fpu_state.sw = 0; + vm86_fpu_state.tag = 0xFFFF; // bochs +#endif +} +#endif + +static void fpu_reset(void) +{ + vm86_fpu_state.cwd = 0x0040; + vm86_fpu_state.swd = 0; +// vm86_fpu_state.ftw = 0x5555; //bochs + if (config.cpu_vm == CPUVM_KVM || config.cpu_vm_dpmi == CPUVM_KVM) + kvm_update_fpu(); +} + +static Bit8u fpu_io_read(ioport_t port, void *arg) +{ + return 0xff; +} + +static void fpu_io_write(ioport_t port, Bit8u val, void *arg) +{ + switch (port) { + case 0xf0: + /* We need to check if the FPU exception is pending, and set IGNNE + * if it is, before untriggering an IRQ. But we don't (and probably + * can't) emulate IGNNE properly. So the trick is to do fnclex in + * bios.S, then untrigger IRQ unconditionally. */ + pic_untrigger(13); /* done by default via int75 handler in bios.S */ + break; + case 0xf1: + fpu_reset(); + break; + } +} + +static int fpu_is_masked(void) +{ + uint8_t imr[2] = { [0] = port_inb(0x21), [1] = port_inb(0xa1) }; + uint16_t real_imr = (imr[1] << 8) | imr[0]; + return ((imr[0] & 4) || !!(real_imr & (1 << 13))); +} + +void raise_fpu_irq(void) +{ + if (fpu_is_masked() || !isset_IF_async()) { + error("FPU IRQ cannot be injected (%i %i), bye\n", + fpu_is_masked(), isset_IF()); + leavedos(2); + } + pic_request(13); +} + +/* + * DANG_BEGIN_FUNCTION cpu_setup + * + * description: + * Setup initial interrupts which can be revectored so that the kernel + * does not need to return to DOSEMU if such an interrupt occurs. + * + * DANG_END_FUNCTION + * + */ +void cpu_setup(void) +{ + emu_iodev_t io_dev; + int orig_vm, orig_vm_dpmi; + + io_dev.read_portb = fpu_io_read; + io_dev.write_portb = fpu_io_write; + io_dev.read_portw = NULL; + io_dev.write_portw = NULL; + io_dev.read_portd = NULL; + io_dev.write_portd = NULL; + io_dev.start_addr = 0xf0; + io_dev.end_addr = 0xff; + io_dev.handler_name = "Math Coprocessor"; + port_register_handler(io_dev, 0); + + savefpstate(vm86_fpu_state); +#ifdef FE_NOMASK_ENV + feenableexcept(FE_DIVBYZERO | FE_OVERFLOW); +#endif + fegetenv(&dosemu_fenv); + + orig_vm = config.cpu_vm; + orig_vm_dpmi = config.cpu_vm_dpmi; + if (config.cpu_vm == -1) + config.cpu_vm = +#if defined(__x86_64__) + CPUVM_KVM +#elif defined(__i386__) + CPUVM_VM86 +#else + CPUVM_EMU +#endif + ; + if (config.cpu_vm_dpmi == -1) + config.cpu_vm_dpmi = +#if defined(__x86_64__) || defined(__i386__) + CPUVM_KVM +#else + CPUVM_EMU +#endif + ; + + if ((config.cpu_vm == CPUVM_KVM || config.cpu_vm_dpmi == CPUVM_KVM) && + !init_kvm_cpu()) { + warn("KVM not available: %s\n", strerror(errno)); + /* if explicitly requested, don't continue */ + if (orig_vm == CPUVM_KVM || orig_vm_dpmi == CPUVM_KVM) { + leavedos(21); + return; + } + if (config.cpu_vm == CPUVM_KVM) + config.cpu_vm = CPUVM_EMU; + if (config.cpu_vm_dpmi == CPUVM_KVM) { +#ifdef X86_EMULATOR + config.cpu_vm_dpmi = CPUVM_EMU; +#else + config.cpu_vm_dpmi = CPUVM_NATIVE; +#endif + } + } + +#ifdef __i386__ + if (config.cpu_vm == CPUVM_VM86) { +#if 0 + if (vm86((void *)VM86_PLUS_INSTALL_CHECK) != -1 || + errno != EFAULT) +#else + if (vm86_plus(VM86_PLUS_INSTALL_CHECK, 0) != 0) +#endif + { + error("vm86 service not available in your kernel, %s\n", strerror(errno)); +#ifdef X86_EMULATOR + config.cpu_vm = CPUVM_EMU; +#else + exit(1); +#endif + } + } +#endif + +#ifdef X86_EMULATOR + if (config.cpu_vm == CPUVM_EMU || config.cpu_vm_dpmi == CPUVM_EMU) { + if (config.cpu_vm == CPUVM_EMU) + warn("using CPU emulation for vm86()\n"); + if (config.cpu_vm_dpmi == CPUVM_EMU) + warn("using CPU emulation for DPMI\n"); + } + init_emu_cpu(); +#endif +} + + +void fxsave_to_fsave(const struct emu_fpxstate *fxsave, struct emu_fsave *fptr) +{ + unsigned int tmp; + int i; + + fptr->cw = fxsave->cwd | 0xffff0000u; + fptr->sw = fxsave->swd | 0xffff0000u; + /* Expand the valid bits */ + tmp = fxsave->ftw; /* 00000000VVVVVVVV */ + tmp = (tmp | (tmp << 4)) & 0x0f0f; /* 0000VVVV0000VVVV */ + tmp = (tmp | (tmp << 2)) & 0x3333; /* 00VV00VV00VV00VV */ + tmp = (tmp | (tmp << 1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ + /* Transform each bit from 1 to 00 (valid) or 0 to 11 (empty). + The kernel will convert it back, so no need to worry about zero + and special states 01 and 10. */ + tmp = ~(tmp | (tmp << 1)); + fptr->tag = tmp | 0xffff0000u; + fptr->ipoff = fxsave->fip; + fptr->cssel = (uint16_t)fxsave->fcs | ((uint32_t)fxsave->fop << 16); + fptr->dataoff = fxsave->fdp; + fptr->datasel = fxsave->fds; + for (i = 0; i < 8; i++) + memcpy(&fptr->st[i], &fxsave->st[i], sizeof(fptr->st[i])); +} + +/* + * FPU tag word conversions. + */ +static unsigned short twd_i387_to_fxsr(unsigned short twd) +{ + unsigned int tmp; /* to avoid 16 bit prefixes in the code */ + + /* Transform each pair of bits into 01 (valid) or 00 (empty) */ + tmp = ~twd; + tmp = (tmp | (tmp>>1)) & 0x5555; /* 0V0V0V0V0V0V0V0V */ + /* and move the valid bits to the lower byte. */ + tmp = (tmp | (tmp >> 1)) & 0x3333; /* 00VV00VV00VV00VV */ + tmp = (tmp | (tmp >> 2)) & 0x0f0f; /* 0000VVVV0000VVVV */ + tmp = (tmp | (tmp >> 4)) & 0x00ff; /* 00000000VVVVVVVV */ + + return tmp; +} + +/* NOTE: this function does NOT memset the "unused" fxsave fields. + * We preserve the previous fxsave context. */ +void fsave_to_fxsave(const struct emu_fsave *fptr, struct emu_fpxstate *fxsave) + +{ + int i; + + fxsave->cwd = fptr->cw; + fxsave->swd = fptr->sw; + fxsave->ftw = twd_i387_to_fxsr(fptr->tag); + fxsave->fop = (uint16_t) ((uint32_t) fptr->cssel >> 16); + fxsave->fip = fptr->ipoff; + fxsave->fcs = (fptr->cssel & 0xffff); + fxsave->fdp = fptr->dataoff; + fxsave->fds = fptr->datasel; + + for (i = 0; i < 8; ++i) { + /* fxsave's regs are larger so memset first */ + memset(&fxsave->st[i], 0, sizeof(fxsave->st[0])); + memcpy(&fxsave->st[i], &fptr->st[i], sizeof(fptr->st[0])); + } +} diff --git a/src/base/emu-i386/cputime.c b/src/base/emu-i386/cputime.c new file mode 100644 index 0000000..78efe7b --- /dev/null +++ b/src/base/emu-i386/cputime.c @@ -0,0 +1,389 @@ +/* + * SIDOC_BEGIN_MODULE + * + * System time routines + * + * Functions used to get a monoton, 64-bit time value either from the + * kernel (with gettimeofday()) or from the CPU hi-res timer + * Maintainer: vignani@mail.tin.it (Alberto Vignani) + * + * SIDOC_END_MODULE + */ + +#include +#include +#include +#include +#include +#include +#include "emu.h" +#include "video.h" +#include "timers.h" +#include "pic.h" +#include "int.h" +#include "coopth.h" +#include "speaker.h" +#include "dosemu_config.h" +#include "sig.h" + +/* --------------------------------------------------------------------- */ +/* + * SIDOC_BEGIN_REMARK + * + * At the heart of the timing system in dosemu >= 0.67.11 is the availability + * of the system time as a 64-bit [type hitimer_t] monoton value. + * (a 64-bit timer on a 200MHz CPU increments by 2^48 a day). + * + * Dosemu needs this time under two resolutions: + * VERB + * - a MICROSECOND resolution for general timing purposes + * - a TICK(838ns) resolution for PIT + * /VERB + * On non-pentium machines, only the first one is available via the + * kernel call gettimeofday(). On the pentium and up, the situation is better + * since we have a cheap hi-res timer on-chip, and worse since this + * timer runs at a speed depending from the CPU clock (which we need + * to know/measure, and could be not 100% accurate esp. if the speed is + * a non-integer multiple of 33.3333). + * + * dosemu >= 0.67.11 can use both timing methods (call them 486 and pentium), + * and switch between them in a dynamic way when configuring. + * + * At the first level (local to the file cputime.c) there are the + * RAW timer functions, addressed by RAWcpuTIME(). These get the + * actual absolute CPU time in usecs. + * + * At the second level, GETcpuTIME() returns the relative, zero-based + * system time. This is where the 486/pentium switch happens. + * + * The third level is the actual timer interface for dosemu and is + * made of two functions: + * VERB + * - GETusTIME(s) gives the time in usecs + * - GETtickTIME(s) gives the time in ticks + * /VERB + * The 's' parameter can be used to control secondary time functions + * like 'time stretching' (see the READMEs). + * The function GETusSYSTIME() never activates this stretching, and + * is used only by the realtime thread-based 1-sec timer in rtc.c. + * + * All timing are RELATIVE to a base. The use of a based time allows us + * to play more freely with time, e.g. stop and restart it during debugging, + * stretch it, make it go at different speeds between real-time and CPU + * emulation, etc. The base has been chosen to be zero, because it will + * avoid overflows in calculations, produce more readable and more easily + * comparable debug log files, and also because only int0x1a and BIOS + * timer require knowledge of the actual time, PIT and PIC are not sensitive. + * + * SIDOC_END_REMARK + */ + +static hitimer_t (*RAWcpuTIME)(void); + +hitimer_u ZeroTimeBase = { 0 }; +static hitimer_t (*GETcpuTIME)(void); +static hitimer_t LastTimeRead = 0; +static hitimer_t StopTimeBase = 0; +int cpu_time_stop = 0; +static hitimer_t cached_time; +static pthread_mutex_t ctime_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t trigger_mtx = PTHREAD_MUTEX_INITIALIZER; +static int idle_tid; +static void idle_hlt_thr(void *arg); + +static hitimer_t do_gettime(void) +{ + struct timespec tv; +#ifdef CLOCK_MONOTONIC_COURSE + int err = clock_gettime(CLOCK_MONOTONIC_COARSE, &tv); +#else + int err = clock_gettime(CLOCK_MONOTONIC, &tv); +#endif + if (err) { + error("Cannot get time!\n"); + leavedos(49); + } + return (tv.tv_sec * 1000000ULL + tv.tv_nsec / 1000); +} + +/* + * RAWcpuTIME points to one of these functions, and returns the + * absolute CPU time + */ +static hitimer_t rawC4time(void) +{ + hitimer_t ctime; + + pthread_mutex_lock(&ctime_mtx); + ctime = cached_time; + pthread_mutex_unlock(&ctime_mtx); + if (!ctime) { + ctime = do_gettime(); + pthread_mutex_lock(&ctime_mtx); + cached_time = ctime; + pthread_mutex_unlock(&ctime_mtx); + } + return ctime; +} + +void uncache_time(void) +{ + pthread_mutex_lock(&ctime_mtx); + cached_time = 0; + pthread_mutex_unlock(&ctime_mtx); +} + +/* + * SIDOC_BEGIN_FUNCTION GETcpuTIME + * + * GETcpuTIME is a pointer to a function which returns the + * relative CPU time. Different methods of getting the time can + * then be implemented, currently there are two using + * gettimeofday() for 486 and TSC for pentium + * + * SIDOC_END_FUNCTION + */ +static hitimer_t getC4time(void) +{ + if (cpu_time_stop) return LastTimeRead; + return (rawC4time() - ZeroTimeBase.td); +} + +/* + * SIDOC_BEGIN_FUNCTION GETusTIME(sc) + * + * GETusTIME returns the DOS time with 1-usec resolution + * using GETcpuTIME to get the implementation-dependent CPU time. + * The 'sc' parameter is unused. + * + * SIDOC_END_FUNCTION + */ +hitimer_t GETusTIME(int sc) +{ + return GETcpuTIME(); +} + +/* + * SIDOC_BEGIN_FUNCTION GETtickTIME(sc) + * + * GETtickTIME returns the DOS time with 838ns resolution + * using GETcpuTIME to get the implementation-dependent CPU time. + * The 'sc' parameter is unused. + * + * SIDOC_END_FUNCTION + */ +hitimer_t GETtickTIME(int sc) +{ + return UStoTICK(GETcpuTIME()); +} + +/* + * SIDOC_BEGIN_FUNCTION GETusSYSTIME() + * + * GETusSYSTIME returns the real CPU time with 1-usec resolution + * + * SIDOC_END_FUNCTION + */ +hitimer_t GETusSYSTIME(void) +{ + return GETcpuTIME(); +} + + +void get_time_init(void) +{ + ZeroTimeBase.td = rawC4time(); + RAWcpuTIME = rawC4time; /* in usecs */ + GETcpuTIME = getC4time; /* in usecs */ + g_printf("TIMER: using clock_gettime(CLOCK_MONOTONIC)\n"); +} + +void cputime_late_init(void) +{ + idle_tid = coopth_create("hlt idle", idle_hlt_thr); +} + + +/* --------------------------------------------------------------------- */ + + +int stop_cputime (int quiet) +{ + if (cpu_time_stop) return 1; + if (!quiet) dbug_printf("STOP TIME\n"); + StopTimeBase = RAWcpuTIME(); + LastTimeRead = StopTimeBase - ZeroTimeBase.td; + cpu_time_stop = 1; + return 0; +} + + +int restart_cputime (int quiet) +{ + if (!cpu_time_stop) return 1; + if (!quiet) dbug_printf("RESTART TIME\n"); + cpu_time_stop = 0; + ZeroTimeBase.td += RAWcpuTIME() - StopTimeBase; + return 0; +} + +/* --------------------------------------------------------------------- */ +int dosemu_frozen = 0; +int dosemu_user_froze = 0; + +void freeze_dosemu_manual(void) +{ + dosemu_user_froze = 2; + freeze_dosemu(); +} + +void freeze_dosemu(void) +{ + if (dosemu_frozen) return; + + stop_cputime(0); + dosemu_frozen = 1; + if (dosemu_user_froze) dosemu_user_froze--; + dbug_printf("*** dosemu frozen\n"); + + speaker_pause(); + + if (Video && Video->change_config) + Video->change_config (CHG_TITLE, NULL); +} + +void unfreeze_dosemu(void) +{ + if (!dosemu_frozen) return; + + speaker_resume (); + + restart_cputime(0); + dosemu_frozen = 0; + dosemu_user_froze = 0; + dbug_printf("*** dosemu unfrozen\n"); + + if (Video && Video->change_config) + Video->change_config (CHG_TITLE, NULL); +} + +/* idle functions to let hogthreshold do its work .... */ +static int trigger1 = 0; +void reset_idle(int val) +{ + val *= config.hogthreshold; + pthread_mutex_lock(&trigger_mtx); + if (-val < trigger1) + trigger1 = -val; + pthread_mutex_unlock(&trigger_mtx); +} + +void alarm_idle(void) +{ + pthread_mutex_lock(&trigger_mtx); + trigger1++; + pthread_mutex_unlock(&trigger_mtx); +} + +void trigger_idle(void) +{ + pthread_mutex_lock(&trigger_mtx); + if (trigger1 >= 0) + trigger1++; + pthread_mutex_unlock(&trigger_mtx); +} + +void untrigger_idle(void) +{ + pthread_mutex_lock(&trigger_mtx); + if (trigger1 > 0) + trigger1--; + pthread_mutex_unlock(&trigger_mtx); +} + +void dosemu_sleep(void) +{ + sigset_t mask; + uncache_time(); + pthread_sigmask(SIG_SETMASK, NULL, &mask); + sigsuspend(&mask); +} + +/* "strong" idle callers will have threshold1 = 0 so only the + inner loop applies. Heuristic idlers (int16/ah=1, mouse) + need the two loops -- the outer loop can be reset using + reset_idle */ +static void _idle(int threshold1, int threshold, int threshold2, + const char *who, int enable_ints) +{ + static int trigger = 0; + int ret = 0; + int old_if = isset_IF(); + if (config.hogthreshold && CAN_SLEEP()) { + pthread_mutex_lock(&trigger_mtx); + if(trigger1 >= config.hogthreshold * threshold1) { + if (trigger++ >= (config.hogthreshold - 1) * threshold + threshold2) { + if (debug_level('g') > 5) + g_printf("sleep requested by %s\n", who); + pthread_mutex_unlock(&trigger_mtx); + if (enable_ints && !old_if) + set_IF(); + coopth_wait(); + if (enable_ints && !old_if) + clear_IF(); + ret = 1; + pthread_mutex_lock(&trigger_mtx); + trigger = 0; + if (debug_level('g') > 5) + g_printf("sleep ended\n"); + } + if (trigger1 > 0) + trigger1--; + } + pthread_mutex_unlock(&trigger_mtx); + } + + if (!ret && enable_ints && !old_if) + int_yield(); +} + +void idle(int threshold1, int threshold, int threshold2, const char *who) +{ + _idle(threshold1, threshold, threshold2, who, 0); +} + +void idle_enable2(int threshold1, int threshold, int threshold2, + const char *who) +{ + _idle(threshold1, threshold, threshold2, who, 1); +} + +void idle_enable(int threshold, int threshold2, const char *who) +{ + _idle(0, threshold, threshold2, who, 1); +} + +void int_yield(void) +{ + /* SeaBIOS does this: + * asm volatile("sti ; nop ; rep ; nop ; cli ; cld" : : :"memory"); + */ + set_IF(); + coopth_yield(); + clear_IF(); +} + +static void idle_hlt_thr(void *arg) +{ + if (!isset_IF()) { + error("cli/hlt detected, bye\n"); + leavedos(2); + return; + } + idle(0, 50, 0, "hlt idle"); +} + +void cpu_idle(void) +{ + coopth_start(idle_tid, NULL); +} diff --git a/src/base/emu-i386/do_vm86.c b/src/base/emu-i386/do_vm86.c new file mode 100644 index 0000000..6495560 --- /dev/null +++ b/src/base/emu-i386/do_vm86.c @@ -0,0 +1,771 @@ + +/* Define if we want graphics in X (of course we want :-) (root@zaphod) */ +/* WARNING: This may not work in BSD, because it was written for Linux! */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "emu.h" +#include "mhpdbg.h" +#ifdef __linux__ +#include "sys_vm86.h" +#endif +#include "bios.h" +#include "mouse.h" +#include "serial.h" +#include "xms.h" +#include "timers.h" +#include "cmos.h" +#include "memory.h" +#include "port.h" +#include "int.h" +#include "disks.h" +#include "ipx.h" /* TRB - add support for ipx */ +#include "bitops.h" +#include "coopth.h" +#include "utilities.h" +#ifdef X86_EMULATOR +#include "cpu-emu.h" +#endif +#include "kvm.h" + +#include "video.h" + +#include "pic.h" +#include "emudpmi.h" +#include "hlt.h" +#include "ipx.h" +#include "vgaemu.h" +#include "sig.h" + +static void pic_run(void); + +int vm86_fault(unsigned trapno, unsigned err, dosaddr_t cr2) +{ +#ifdef USE_MHPDBG + mhp_debug(DBG_INTx + (trapno << 8), 0, 1); +#endif + + if (dpmi_active() && dpmi_realmode_exception(trapno, err, cr2)) + return 0; + + switch (trapno) { + case 0x00: /* divide_error */ + case 0x01: /* debug */ + case 0x03: /* int3 */ + case 0x04: /* overflow */ + case 0x05: /* bounds */ + case 0x07: /* device_not_available */ + error_once("exception %#x occured\n", trapno); + if (!IS_REDIRECTED(trapno)) + goto sgleave; + do_int(trapno); + return 0; + + case 0x10: /* coprocessor error */ + raise_fpu_irq(); /* this is the 386 way of signalling this */ + return 0; + + case 0x11: /* alignment check */ + /* we are now safe; nevertheless, fall into the default + * case and exit dosemu, as an AC fault in vm86 is(?) a + * catastrophic failure. + */ + goto sgleave; + + case 0x06: /* invalid_op */ + { + unsigned char *csp; + error_once("SIGILL while in vm86(): %04x:%04x\n", SREG(cs), LWORD(eip)); + if (config.vga && SREG(cs) == config.vbios_seg) { + if (!config.vbios_post) + error("Fault in VBIOS code, try setting $_vbios_post=(1)\n"); + else + error("Fault in VBIOS code, try running xdosemu under X\n"); + goto sgleave; + } +#if 0 + show_regs(); +#endif /* 0 */ + csp = SEG_ADR((unsigned char *), cs, ip); + /* this one is for CPU detection programs + * actually we should check if int0x06 has been + * hooked by the pgm and redirected to it */ + if (!IS_IRET(0x06)) + { + do_int(trapno); + return 0; + } + /* Some db commands start with 2e (use cs segment) + and thus is accounted for here */ + if (csp[0] == 0x2e) { + csp++; + LWORD(eip)++; + goto sgleave; + } + if (csp[0] == 0xf0) { + dbug_printf("ERROR: LOCK prefix not permitted!\n"); + LWORD(eip)++; + return 0; + } + goto sgleave; + } + + default: +sgleave: + error("unexpected CPU exception 0x%02x err=0x%08x cr2=%08x while in vm86 (DOS)\n", + trapno, err, cr2); + { + int auxg = debug_level('g'); + FILE *aux = dbg_fd; + flush_log(); /* important! else we flush to stderr */ + dbg_fd = stderr; + set_debug_level('g',1); + show_regs(); + set_debug_level('g', auxg); + flush_log(); + dbg_fd = aux; + } + + show_regs(); + flush_log(); + leavedos_from_sig(4); + } + return 0; /* keeps GCC happy */ +} + +static int vm86_hlt_handle(void) +{ + dosaddr_t lina = SEGOFF2LINEAR(_CS, _IP); + int ret = HLT_RET_NORMAL; + + if ((lina >= BIOS_HLT_BLK) && (lina < BIOS_HLT_BLK+BIOS_HLT_BLK_SIZE)) { + Bit16u offs = lina - BIOS_HLT_BLK; + hlt_handle(vm86_hlt_state, offs, NULL); + } + else if (lina == XMSControl_ADD) { + xms_control(); + } + else if (lina == INT42HOOK_ADD) { + int42_hook(); + } + else if (lina == POSTHOOK_ADD) { + post_hook(); + } + else if (lina == DPMI_ADD + HLT_OFF(DPMI_dpmi_init)) { + /* The hlt instruction is 6 bytes in from DPMI_ADD */ + _IP += 1; /* skip halt to point to FAR RET */ + CARRY; + dpmi_init(); + } + else if ((lina >= DPMI_ADD) && (lina < DPMI_ADD + (DPMI_end - DPMI_OFF))) { + dpmi_realmode_hlt(lina); + } + else { + h_printf("HLT: unknown halt request CS:IP=%04x:%04x!\n", _CS, _IP); + _IP += 1; + ret = HLT_RET_NORMAL; + cpu_idle(); + } + return ret; +} + +/* */ +/* vm86_GP_fault @@@ 32768 MOVED_CODE_BEGIN @@@ 01/23/96, ./src/arch/linux/async/sigsegv.c --> src/emu-i386/do_vm86.c */ +/* + * DANG_BEGIN_FUNCTION vm86_GP_fault + * + * description: + * All from the kernel unhandled general protection faults from V86 mode + * are handled here. This are mainly port IO and the HLT instruction. + * + * DANG_END_FUNCTION + */ + +static int handle_GP_fault(void) +{ + unsigned char *csp, *lina; + int pref_seg; + int done,is_rep,prefix66,prefix67; + +#if 0 + u_short *ssp; + static int haltcount = 0; +#endif + +#define ASIZE_IS_32 (prefix67) +#define OSIZE_IS_32 (prefix66) +#define LWECX (ASIZE_IS_32 ? REG(ecx) : LWORD(ecx)) +#define setLWECX(x) {if (ASIZE_IS_32) REG(ecx)=(x); else LWORD(ecx) = (x);} +#define MAX_HALT_COUNT 3 + +#if 0 + csp = SEG_ADR((unsigned char *), cs, ip); + ssp = SEG_ADR((us *), ss, sp); + if ((*csp&0xfd)==0xec) { /* inb, outb */ + i_printf("VM86_GP_FAULT at %08lx, cod=%02x %02x*%02x %02x %02x %02x\n" + " stk=%04x %04x %04x %04x\n", + (long)csp, + csp[-2], csp[-1], csp[0], csp[1], csp[2], csp[3], + ssp[0], ssp[1], ssp[2], ssp[3]); + } +#endif + + csp = lina = SEG_ADR((unsigned char *), cs, ip); + + /* fprintf(stderr, "CSP in cpu is 0x%04x\n", *csp); */ + + + /* DANG_BEGIN_REMARK + * Here we handle all prefixes prior switching to the appropriate routines + * The exception CS:EIP will point to the first prefix that effects + * the faulting instruction, hence, 0x65 0x66 is same as 0x66 0x65. + * So we collect all prefixes and remember them. + * - Hans Lermen + * DANG_END_REMARK + */ + + #define __SEG_ADR_32(type, seg, reg) type(MK_FP32(seg, reg)) + #define SEG_ADR_32(type, seg, reg) type(LINEAR2UNIX(SEGOFF2LINEAR(seg, reg))) + done=0; + is_rep=0; + prefix66=prefix67=0; + pref_seg=-1; + + do { + switch (*(csp++)) { + case 0x66: /* operand prefix */ prefix66=1; break; + case 0x67: /* address prefix */ prefix67=1; break; + case 0x2e: /* CS */ pref_seg=SREG(cs); break; + case 0x3e: /* DS */ pref_seg=SREG(ds); break; + case 0x26: /* ES */ pref_seg=SREG(es); break; + case 0x36: /* SS */ pref_seg=SREG(ss); break; + case 0x65: /* GS */ pref_seg=SREG(gs); break; + case 0x64: /* FS */ pref_seg=SREG(fs); break; + case 0xf2: /* repnz */ + case 0xf3: /* rep */ is_rep=1; break; + default: done=1; + } + } while (!done); + csp--; + + switch (*csp) { + + case 0xf1: /* int 1 */ + LWORD(eip)++; /* emulated "undocumented" instruction */ + do_int(1); + break; + + case 0x6c: /* insb */ + LWORD(eip)++; + /* NOTE: ES can't be overwritten */ + /* WARNING: no test for (E)DI wrapping! */ + if (ASIZE_IS_32) /* a32 insb */ + REG(edi) += port_rep_inb(LWORD(edx), SEG_ADR_32((Bit8u *),SREG(es),REG(edi)), + LWORD(eflags)&DF, (is_rep?LWECX:1)); + else /* a16 insb */ + LWORD(edi) += port_rep_inb(LWORD(edx), SEG_ADR_32((Bit8u *),SREG(es),LWORD(edi)), + LWORD(eflags)&DF, (is_rep?LWECX:1)); + if (is_rep) setLWECX(0); + break; + + case 0x6d: /* (rep) insw / insd */ + LWORD(eip)++; + /* NOTE: ES can't be overwritten */ + /* WARNING: no test for (E)DI wrapping! */ + if (prefix66) { /* insd */ + if (ASIZE_IS_32) /* a32 insd */ + REG(edi) += port_rep_ind(LWORD(edx), SEG_ADR_32((Bit32u *),SREG(es),REG(edi)), + LWORD(eflags)&DF, (is_rep?LWECX:1)); + else /* a16 insd */ + LWORD(edi) += port_rep_ind(LWORD(edx), SEG_ADR_32((Bit32u *),SREG(es),LWORD(edi)), + LWORD(eflags)&DF, (is_rep?LWECX:1)); + } + else { /* insw */ + if (ASIZE_IS_32) /* a32 insw */ + REG(edi) += port_rep_inw(LWORD(edx), SEG_ADR_32((Bit16u *),SREG(es),REG(edi)), + LWORD(eflags)&DF, (is_rep?LWECX:1)); + else /* a16 insw */ + LWORD(edi) += port_rep_inw(LWORD(edx), SEG_ADR_32((Bit16u *),SREG(es),LWORD(edi)), + LWORD(eflags)&DF, (is_rep?LWECX:1)); + } + if (is_rep) setLWECX(0); + break; + + case 0x6e: /* (rep) outsb */ + LWORD(eip)++; + if (pref_seg < 0) pref_seg = SREG(ds); + /* WARNING: no test for (E)SI wrapping! */ + if (ASIZE_IS_32) /* a32 outsb */ + REG(esi) += port_rep_outb(LWORD(edx), __SEG_ADR_32((Bit8u *),pref_seg,REG(esi)), + LWORD(eflags)&DF, (is_rep?LWECX:1)); + else /* a16 outsb */ + LWORD(esi) += port_rep_outb(LWORD(edx), __SEG_ADR_32((Bit8u *),pref_seg,LWORD(esi)), + LWORD(eflags)&DF, (is_rep?LWECX:1)); + if (is_rep) setLWECX(0); + break; + + case 0x6f: /* (rep) outsw / outsd */ + LWORD(eip)++; + if (pref_seg < 0) pref_seg = SREG(ds); + /* WARNING: no test for (E)SI wrapping! */ + if (prefix66) { /* outsd */ + if (ASIZE_IS_32) /* a32 outsd */ + REG(esi) += port_rep_outd(LWORD(edx), __SEG_ADR_32((Bit32u *),pref_seg,REG(esi)), + LWORD(eflags)&DF, (is_rep?LWECX:1)); + else /* a16 outsd */ + LWORD(esi) += port_rep_outd(LWORD(edx), __SEG_ADR_32((Bit32u *),pref_seg,LWORD(esi)), + LWORD(eflags)&DF, (is_rep?LWECX:1)); + } + else { /* outsw */ + if (ASIZE_IS_32) /* a32 outsw */ + REG(esi) += port_rep_outw(LWORD(edx), __SEG_ADR_32((Bit16u *),pref_seg,REG(esi)), + LWORD(eflags)&DF, (is_rep?LWECX:1)); + else /* a16 outsw */ + LWORD(esi) += port_rep_outw(LWORD(edx), __SEG_ADR_32((Bit16u *),pref_seg,LWORD(esi)), + LWORD(eflags)&DF, (is_rep?LWECX:1)); + } + if (is_rep) setLWECX(0); + break; + + case 0xe5: /* inw xx, ind xx */ + LWORD(eip) += 2; + if (prefix66) REG(eax) = port_ind((int) csp[1]); + else LWORD(eax) = port_inw((int) csp[1]); + break; + + case 0xe4: /* inb xx */ + LWORD(eip) += 2; + LWORD(eax) &= ~0xff; + LWORD(eax) |= port_inb((int) csp[1]); + break; + + case 0xed: /* inw dx, ind dx */ + LWORD(eip)++; + if (prefix66) REG(eax) = port_ind(LWORD(edx)); + else LWORD(eax) = port_inw(LWORD(edx)); + break; + + case 0xec: /* inb dx */ + LWORD(eip)++; + LWORD(eax) &= ~0xff; + LWORD(eax) |= port_inb(LWORD(edx)); + break; + + case 0xe7: /* outw xx */ + LWORD(eip) += 2; + if (prefix66) port_outd((int)csp[1], REG(eax)); + else port_outw((int)csp[1], LWORD(eax)); + break; + + case 0xe6: /* outb xx */ + LWORD(eip) += 2; + port_outb((int) csp[1], LO(ax)); + break; + + case 0xef: /* outw dx, outd dx */ + LWORD(eip)++; + if (prefix66) port_outd(LWORD(edx), REG(eax)); + else port_outw(LWORD(edx), LWORD(eax)); + break; + + case 0xee: /* outb dx */ + LWORD(eip)++; + port_outb(LWORD(edx), LO(ax)); + break; + + case 0xf4: /* hlt...I use it for various things, + like trapping direct jumps into the XMS function */ + vm86_hlt_handle(); + break; + + case 0x0f: /* was: RDE hack, now handled in cpu.c */ + if (!cpu_trap_0f(csp, NULL)) + return 0; + break; + + case 0xf0: /* lock */ + default: + return 0; + } + + LWORD(eip) += (csp-lina); + return 1; +} + +static void vm86_GP_fault(void) +{ + unsigned char *lina; + if (handle_GP_fault()) { +#ifdef USE_MHPDBG + if (isset_TF() && !in_dpmi_pm()) + mhp_debug(DBG_TRAP + (1 << 8), 0, 0); +#endif + return; + } + lina = SEG_ADR((unsigned char *), cs, ip); +#ifdef USE_MHPDBG + mhp_debug(DBG_GPF, 0, 0); +#endif + set_debug_level('g', 1); + error_once("general protection at %p: %x\n", lina,*lina); + show_regs(); + show_ints(0, 0x33); + if (IS_REDIRECTED(0x0d)) { + do_int(0x0d); + return; + } + fatalerr = 4; + leavedos(fatalerr); /* shouldn't return */ +} +/* @@@ MOVE_END @@@ 32768 */ + +static int handle_GP_hlt(void) +{ + unsigned char *csp; + csp = SEG_ADR((unsigned char *), cs, ip); + if (*csp == 0xf4) + return vm86_hlt_handle(); + return HLT_RET_NONE; +} + +#ifdef __i386__ +static int true_vm86(union vm86_union *x) +{ + int ret; + uint32_t old_flags = REG(eflags); + + loadfpstate(vm86_fpu_state); +again: +#if 0 + ret = vm86(&x->vm86ps); +#else + /* need to use vm86_plus for now as otherwise dosdebug doesn't work */ + ret = vm86_plus(VM86_ENTER, &x->vm86compat); +#endif + /* optimize VM86_STI case that can return with ints disabled + * if VIP is set since kernel commit 5ed92a8ab (4.3-rc1) */ + if (VM86_TYPE(ret) == VM86_STI) { + if (!isset_IF()) + goto again; + } + /* kernel has a nasty habit of clearing VIP. + * TODO: check kernel version */ + REG(eflags) |= (old_flags & VIP); + + savefpstate(vm86_fpu_state); + /* there is no real need to save and restore the FPU state of the + emulator itself: savefpstate (fnsave) also resets the current FPU + state using fninit; fesetenv then restores trapping of division by + zero and overflow which is good enough for calling FPU-using + routines. + */ + fesetenv(&dosemu_fenv); + return ret; +} + +void true_vm86_fault(sigcontext_t *scp) +{ + if (_scp_trapno == 0x0e) { + /* we can get to instremu from here, so unblock SIGALRM & friends. + * It is needed to interrupt instremu when it runs for too long. */ + signal_unblock_async_sigs(); + if (vga_emu_fault(_scp_cr2, _scp_err, NULL) == True) + return; + } + vm86_fault(_scp_trapno, _scp_err, _scp_cr2); +} +#endif + +static int do_vm86(union vm86_union *x) +{ + if (config.cpu_vm == CPUVM_KVM) + return kvm_vm86(&x->vm86ps); +#ifdef X86_EMULATOR + if (config.cpu_vm == CPUVM_EMU) + return e_vm86(); +#endif +#ifdef __i386__ + return true_vm86(x); +#else + leavedos_main(2); + return 0; +#endif +} + +static void _do_vm86(void) +{ + int retval; +#ifdef USE_MHPDBG + int dret = 0; +#endif + + if (isset_IF() && isset_VIP()) { + error("both IF and VIP set\n"); + clear_VIP(); + } + in_vm86 = 1; + retval = do_vm86(&vm86u); + in_vm86 = 0; + + if ( +#ifdef X86_EMULATOR + (debug_level('e')>1)|| +#endif + (debug_level('g')>3)) { + dbug_printf ("RET_VM86, cs=%04x:%04x ss=%04x:%04x f=%08x ret=0x%x\n", + _CS, _EIP, _SS, _SP, _EFLAGS, retval); + if (debug_level('g')>8) + dbug_printf ("ax=%08x bx=%08x ss=%04x sp=%08x bp=%08x\n" + " cx=%08x dx=%08x ds=%04x cs=%04x ip=%08x\n" + " si=%08x di=%08x es=%04x flg=%08x\n", + _EAX, _EBX, _SS, _ESP, _EBP, _ECX, _EDX, _DS, _CS, _EIP, + _ESI, _EDI, _ES, _EFLAGS); + } + + switch (VM86_TYPE(retval)) { + case VM86_UNKNOWN: + vm86_GP_fault(); + if (in_dpmi_pm()) + return; +#ifdef USE_MHPDBG + /* instructions that cause GPF, could also cause single-step + * trap but didn't. Catch them here. */ + if (mhpdbg.active) + mhp_debug(DBG_PRE_VM86, 0, 0); +#endif + break; + case VM86_STI: + I_printf("Return from vm86() for STI\n"); +#ifdef USE_MHPDBG + /* VIP breaks us out for STI which could otherwise get a + * single-step trap. Catch it here. */ + if (mhpdbg.active) + mhp_debug(DBG_PRE_VM86, 0, 0); +#endif + break; + case VM86_INTx: +#ifdef USE_MHPDBG + if (mhpdbg.active) + dret = mhp_debug(DBG_INTx + (VM86_ARG(retval) << 8), 1, 0); + if (!dret) +#endif + do_int(VM86_ARG(retval)); + break; + case VM86_TRAP: +#ifdef USE_MHPDBG + if (mhpdbg.active) + dret = mhp_debug(DBG_TRAP + (VM86_ARG(retval) << 8), 0, 0); + if (!dret) +#endif + do_int(VM86_ARG(retval)); + break; + case VM86_PICRETURN: + I_printf("Return for FORCE_PIC\n"); + break; + case VM86_SIGNAL: + I_printf("Return for SIGNAL\n"); + break; + default: + error("unknown return value from vm86()=%x,%d-%x\n", VM86_TYPE(retval), VM86_TYPE(retval), VM86_ARG(retval)); + fatalerr = 4; + } +} + +/* + * DANG_BEGIN_FUNCTION run_vm86 + * + * description: + * Here is where DOSEMU runs VM86 mode with the vm86() call + * which also has the registers that it will be called with. It will stop + * vm86 mode for many reasons, like trying to execute an interrupt, doing + * port I/O to ports not opened for I/O, etc ... + * + * DANG_END_FUNCTION + */ +void run_vm86(void) +{ + int retval, cnt; + + if ( +#ifdef X86_EMULATOR + (debug_level('e')>1)|| +#endif + (debug_level('g')>3)) { + dbug_printf ("DO_VM86, cs=%04x:%04x ss=%04x:%04x f=%08x\n", + _CS, _EIP, _SS, _SP, _EFLAGS); + if (debug_level('g')>8) + dbug_printf ("ax=%08x bx=%08x ss=%04x sp=%08x bp=%08x\n" + " cx=%08x dx=%08x ds=%04x cs=%04x ip=%08x\n" + " si=%08x di=%08x es=%04x flg=%08x\n", + _EAX, _EBX, _SS, _ESP, _EBP, _ECX, _EDX, _DS, _CS, _EIP, + _ESI, _EDI, _ES, _EFLAGS); + } + + cnt = 0; + while ((retval = handle_GP_hlt())) { + cnt++; + if (debug_level('g')>3) { + g_printf("DO_VM86: premature fault handled, %i\n", cnt); + g_printf("RET_VM86, cs=%04x:%04x ss=%04x:%04x f=%08x\n", + _CS, _EIP, _SS, _SP, _EFLAGS); + } + if (in_dpmi_pm()) + return; +#ifdef USE_MHPDBG + if (mhpdbg.active) + mhp_debug(DBG_PRE_VM86, 0, 0); +#endif + /* if thread wants some sleep, we can't fuck it in a busy loop */ + if (coopth_wants_sleep_vm86()) { + pic_run(); // try to awake + if (in_dpmi_pm()) + return; + if (coopth_wants_sleep_vm86()) // not awaken? + return; + } + /* some subsystems doesn't want this optimization loop as well */ + if (retval == HLT_RET_SPECIAL) + return; + if (retval != HLT_RET_NORMAL) + break; + + if (debug_level('g') > 3) { + dbug_printf ("DO_VM86, cs=%04x:%04x ss=%04x:%04x f=%08x\n", + _CS, _EIP, _SS, _SP, _EFLAGS); + if (debug_level('g')>8) + dbug_printf ("ax=%08x bx=%08x ss=%04x sp=%08x bp=%08x\n" + " cx=%08x dx=%08x ds=%04x cs=%04x ip=%08x\n" + " si=%08x di=%08x es=%04x flg=%08x\n", + _EAX, _EBX, _SS, _ESP, _EBP, _ECX, _EDX, _DS, _CS, _EIP, + _ESI, _EDI, _ES, _EFLAGS); + } + } + pic_run(); /* trigger any hardware interrupts requested */ + if (in_dpmi_pm()) + return; + +#ifdef USE_MHPDBG + if (mhpdbg.active) + mhp_debug(DBG_PRE_VM86, 0, 0); +#endif + + _do_vm86(); +} + +/* same as run_vm86(), but avoids any looping in handling GPFs */ +void vm86_helper(void) +{ + assert(!in_dpmi_pm()); + clear_VIP(); + _do_vm86(); + handle_signals(); + coopth_run(); +} + +static void pic_run(void) +{ + int inum; + + if (!pic_pending()) { + clear_VIP(); + return; + } + + if (!isset_IF()) { +#if 0 + /* try to detect timer flood, and not set VIP if it is there. + * See https://github.com/stsp/dosemu2/issues/918 + */ + if (pic_sys_time < pic_dos_time + + TIMER0_FLOOD_THRESHOLD || + pic_pending_masked(1 << PIC_IRQ0)) + set_VIP(); + else + r_printf("PIC: timer flood work-around\n"); +#else + /* the above work-around regresses goblins2, see + * https://github.com/dosemu2/dosemu2/issues/1300 + */ + set_VIP(); +#endif + return; /* exit if ints are disabled */ + } + clear_VIP(); + + inum = pic_get_inum(); + if (dpmi_active()) + run_pm_int(inum); + else + real_run_int(inum); +} + +/* + * DANG_BEGIN_FUNCTION loopstep_run_vm86 + * + * description: + * Here we collect all stuff, that has to be executed within + * _one_ pass (step) of a loop containing run_vm86(). + * DANG_END_FUNCTION + */ +void loopstep_run_vm86(void) +{ + uncache_time(); + if (!dosemu_frozen && !signal_pending()) { + if (in_dpmi_pm()) + run_dpmi(); + else + run_vm86(); + } + if (dosemu_frozen) + dosemu_sleep(); + do_periodic_stuff(); + hardware_run(); +#ifdef USE_MHPDBG + if (mhpdbg_is_stopped()) + return; +#endif +} + +int do_call_back(Bit16u cs, Bit16u ip) +{ + fake_call_to(cs, ip); /* far jump to the vm86(DOS) routine */ + return coopth_sched(); +} + +int do_int_call_back(int intno) +{ + do_int(intno); + return coopth_sched(); +} + +int vm86_init(void) +{ + return 0; +} + +void *vm86_hlt_state; + +Bit16u hlt_register_handler_vm86(emu_hlt_t handler) +{ + return hlt_register_handler(vm86_hlt_state, handler); +} + +int hlt_unregister_handler_vm86(Bit16u start_addr) +{ + return hlt_unregister_handler(vm86_hlt_state, start_addr); +} diff --git a/src/base/emu-i386/kvm.c b/src/base/emu-i386/kvm.c new file mode 100644 index 0000000..5fba745 --- /dev/null +++ b/src/base/emu-i386/kvm.c @@ -0,0 +1,1424 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Emulate vm86() using KVM + * Started 2015, Bart Oldeman + * References: http://lwn.net/Articles/658511/ + * plus example at http://lwn.net/Articles/658512/ + */ + +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kvm.h" +#include "kvmmon_offsets.h" +#include "emu.h" +#include "emu-ldt.h" +#include "cpu-emu.h" +#include "vgaemu.h" +#include "dos2linux.h" +#include "mapping.h" +#include "sig.h" + +#ifndef X86_EFLAGS_FIXED +#define X86_EFLAGS_FIXED 2 +#endif +#include "emudpmi.h" + +#define SAFE_MASK (X86_EFLAGS_CF|X86_EFLAGS_PF| \ + X86_EFLAGS_AF|X86_EFLAGS_ZF|X86_EFLAGS_SF| \ + X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_OF| \ + X86_EFLAGS_RF| \ + X86_EFLAGS_NT|X86_EFLAGS_AC|X86_EFLAGS_ID) // 0x254dd5 +#define RETURN_MASK ((SAFE_MASK | 0x28 | X86_EFLAGS_FIXED) & \ + ~X86_EFLAGS_RF) // 0x244dff + +extern char _binary_kvmmon_o_bin_end[] asm("_binary_kvmmon_o_bin_end"); +extern char _binary_kvmmon_o_bin_start[] asm("_binary_kvmmon_o_bin_start"); + +/* V86/DPMI monitor structure to run code in V86 mode with VME enabled + or DPMI clients inside KVM + This contains: + 1. a TSS with + a. ss0:esp0 set to a stack at the top of the monitor structure + This stack contains a copy of the vm86_regs struct. + b. An interrupt redirect bitmap copied from info->int_revectored + c. I/O bitmap, for now set to trap all ints. Todo: sync with ioperm() + 2. A GDT with 5 entries + 0. 0 entry + 1. selector 8: flat CS + 2. selector 0x10: based SS (so the high bits of ESP are always 0, + which avoids issues with IRET). + 3. TSS + 4. LDT + 3. An IDT with 256 (0x100) entries: + a. 0x11 entries for CPU exceptions that can occur + b. 0xef entries for software interrupts + 4. The stack (from 1a) above + 5. Page directory and page tables + 6. The LDT, used by DPMI code; ldt_buffer in dpmi.c points here + 7. The code pointed to by the IDT entries, from kvmmon.S, on a new page + This just pushes the exception number, error code, and all registers + to the stack and executes the HLT instruction which is then trapped + by KVM. + */ + +#define TSS_IOPB_SIZE (65536 / 8) +#define GDT_ENTRIES 5 +#define GDT_SS (GDT_ENTRIES - 3) +#define GDT_TSS (GDT_ENTRIES - 2) +#define GDT_LDT (GDT_ENTRIES - 1) +#undef IDT_ENTRIES +#define IDT_ENTRIES 0x100 + +#define PG_PRESENT 1 +#define PG_RW 2 +#define PG_USER 4 +#define PG_DC 0x10 + +static struct monitor { + Task tss; /* 0000 */ + /* tss.esp0 0004 */ + /* tss.ss0 0008 */ + /* tss.IOmapbaseT (word) 0066 */ + struct revectored_struct int_revectored; /* 0068 */ + unsigned char io_bitmap[TSS_IOPB_SIZE+1];/* 0088 */ + /* TSS last byte (limit) 2088 */ + unsigned char padding0[0x2100-sizeof(Task) + -sizeof(struct revectored_struct) + -(TSS_IOPB_SIZE+1)]; + Descriptor gdt[GDT_ENTRIES]; /* 2100 */ + unsigned char padding1[0x2200-0x2100 + -GDT_ENTRIES*sizeof(Descriptor)]; /* 2118 */ + Gatedesc idt[IDT_ENTRIES]; /* 2200 */ + unsigned char padding2[0x3000-0x2200 + -IDT_ENTRIES*sizeof(Gatedesc) + -sizeof(unsigned int) + -sizeof(unsigned int)*2 + -sizeof(struct emu_fpxstate) + -sizeof(struct vm86_regs)]; /* 2308 */ + unsigned int cr2; /* Fault stack at 2DA0 */ + unsigned int cr3; /* cr3 in (no TLB flush if 0) */ + struct vm86_regs regs; + unsigned padding[1]; + struct emu_fpxstate fpstate; /* 2e00 */ + /* 3000: page directory, 4000: page table */ + unsigned int pde[PAGE_SIZE/sizeof(unsigned int)]; + unsigned int pte[(PAGE_SIZE*PAGE_SIZE)/sizeof(unsigned int) + /sizeof(unsigned int)]; + Descriptor ldt[LDT_ENTRIES]; /* 404000 */ + unsigned char code[256 * 32 + PAGE_SIZE]; /* 414000 */ + /* 414000 IDT exception 0 code start + 414010 IDT exception 1 code start + .... .... + 414ff0 IDT exception 0xff code start + 415000 IDT common code start + 415024 IDT common code end + */ + unsigned char kvm_tss[3*PAGE_SIZE]; + unsigned char kvm_identity_map[20*PAGE_SIZE]; +} *monitor; + +/* map the monitor high in physical/linear address space with some + room to spare (top - 16MB, monitor takes a little over 4MB) */ +#define MONITOR_DOSADDR 0xff000000 + +static struct kvm_cpuid2 *cpuid; +/* fpu vme de pse tsc msr mce cx8 */ +#define CPUID_FEATURES_EDX 0x1bf +static struct kvm_run *run; +static int kvmfd, vmfd, vcpufd; +static struct kvm_sregs sregs; + +#define MAXSLOT 400 +static struct kvm_userspace_memory_region maps[MAXSLOT]; + +static int init_kvm_vcpu(void); +static void kvm_set_readonly(dosaddr_t base, dosaddr_t size); + +#if !defined(DISABLE_SYSTEM_WA) || !defined(KVM_CAP_IMMEDIATE_EXIT) + +/* compat functions for older complex method of immediate exit + using a signal that is blocked outside KVM_RUN but not blocked inside it */ + +#include + +#ifndef KVM_CAP_IMMEDIATE_EXIT +#define KVM_CAP_IMMEDIATE_EXIT 136 +#define immediate_exit padding1[0] +#endif + +#define KVM_IMMEDIATE_EXIT_SIG (SIGRTMIN + 1) +#define KERNEL_SIGSET_T_SIZE 8 + +static void kvm_set_immediate_exit(int set) +{ + static int kvm_cap_immediate_exit = -1; + if (kvm_cap_immediate_exit == -1) { // setup + kvm_cap_immediate_exit = ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_IMMEDIATE_EXIT) > 0; + if (!kvm_cap_immediate_exit) { + struct sigaction sa; + sigset_t sigset; + /* KVM_SET_SIGNAL_MASK expects a kernel sigset_t which is + smaller than a glibc one */ + union { + struct kvm_signal_mask mask; + unsigned char buf[sizeof(struct kvm_signal_mask) + KERNEL_SIGSET_T_SIZE]; + } maskbuf; + + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_DFL; // never called + sigaction(KVM_IMMEDIATE_EXIT_SIG, &sa, NULL); + + /* block KVM_IMMEDIATE_EXIT_SIG in main thread */ + sigemptyset(&sigset); + sigaddset(&sigset, KVM_IMMEDIATE_EXIT_SIG); + pthread_sigmask(SIG_BLOCK, &sigset, NULL); + + /* don't block any signals inside the guest */ + sigemptyset(&sigset); + maskbuf.mask.len = KERNEL_SIGSET_T_SIZE; + assert(sizeof(sigset) >= KERNEL_SIGSET_T_SIZE); + memcpy(maskbuf.mask.sigset, &sigset, KERNEL_SIGSET_T_SIZE); + if (ioctl(vcpufd, KVM_SET_SIGNAL_MASK, &maskbuf.mask) == -1) { + perror("KVM: KVM_SET_SIGNAL_MASK"); + leavedos_main(99); + } + } + } + + if (kvm_cap_immediate_exit) { + assert(run->immediate_exit == !set); + run->immediate_exit = set; + return; + } + + if (set) + pthread_kill(pthread_self(), KVM_IMMEDIATE_EXIT_SIG); + else { + // need to flush the signal after KVM_RUN + sigset_t sigset; + assert(sigpending(&sigset) == 0 && + sigismember(&sigset, KVM_IMMEDIATE_EXIT_SIG)); + sigemptyset(&sigset); + sigaddset(&sigset, KVM_IMMEDIATE_EXIT_SIG); + sigwaitinfo(&sigset, NULL); + } +} + +#else // function without workaround if kernels older than 4.13 not supported + +static inline void kvm_set_immediate_exit(int set) +{ + assert(run->immediate_exit == !set); + run->immediate_exit = set; +} + +#endif + +static void set_idt_default(dosaddr_t mon, int i) +{ + unsigned int offs = mon + offsetof(struct monitor, code) + i * 32; + monitor->idt[i].offs_lo = offs & 0xffff; + monitor->idt[i].offs_hi = offs >> 16; + monitor->idt[i].seg = 0x8; // FLAT_CODE_SEL + monitor->idt[i].type = 0xe; + /* DPL is 0 so that software ints < 0x11 or 255 from DPMI clients will GPF. + Exceptions are int3 (BP) and into (OF): matching the Linux kernel + they must generate traps 3 and 4, and not GPF. + Exceptions > 0x10 cannot be triggered with the VM's settings of CR0/CR4 + Exception 0x10 is special as it is also a common software int; in real + DOS it can be turned off by resetting cr0.ne so it triggers IRQ13 + directly but this is impossible with KVM */ + monitor->idt[i].DPL = (i == 3 || i == 4 || i > 0x10) ? 3 : 0; +} + +void kvm_set_idt_default(int i) +{ + if (i < 0x11) + return; + set_idt_default(sregs.tr.base, i); +} + +static void set_idt(int i, uint16_t sel, uint32_t offs, int is_32, int tg) +{ + monitor->idt[i].offs_lo = offs & 0xffff; + monitor->idt[i].offs_hi = offs >> 16; + monitor->idt[i].seg = sel; + monitor->idt[i].type = is_32 ? 0xe : 0x6; + if (tg) + monitor->idt[i].type |= 1; + monitor->idt[i].DPL = 3; +} + +void kvm_set_idt(int i, uint16_t sel, uint32_t offs, int is_32, int tg) +{ + /* don't change IDT for exceptions and special entry that interrupts + the VM */ + if (i < 0x11) + return; + set_idt(i, sel, offs, is_32, tg); +} + +static void kvm_set_desc(Descriptor *desc, struct kvm_segment *seg) +{ + MKBASE(desc, seg->base); + MKLIMIT(desc, seg->limit >> (seg->g ? 12 : 0)); + desc->type = seg->type; + desc->present = seg->present; + desc->DPL = seg->dpl; + desc->DB = seg->db; + desc->S = seg->s; + desc->gran = seg->g; + desc->AVL = seg->avl; + desc->unused = 0; +} + +/* initialize KVM virtual machine monitor */ +static void init_kvm_monitor(void) +{ + int ret, i; + + if (!cpuid) + return; + + /* create monitor structure in memory */ + monitor = mmap_mapping_huge_page_aligned(MAPPING_SCRATCH|MAPPING_KVM, + sizeof(*monitor), PROT_READ | PROT_WRITE); + /* trap all I/O instructions with GPF */ + memset(monitor->io_bitmap, 0xff, TSS_IOPB_SIZE+1); + + if (!init_kvm_vcpu()) { + leavedos(99); + return; + } + + ret = ioctl(vcpufd, KVM_GET_SREGS, &sregs); + if (ret == -1) { + perror("KVM: KVM_GET_SREGS"); + leavedos(99); + return; + } + + sregs.tr.base = MONITOR_DOSADDR; + sregs.tr.limit = offsetof(struct monitor, io_bitmap) + TSS_IOPB_SIZE - 1; + sregs.tr.selector = 0x18; + sregs.tr.unusable = 0; + sregs.tr.type = 0xb; + sregs.tr.s = 0; + sregs.tr.dpl = 0; + sregs.tr.present = 1; + sregs.tr.avl = 0; + sregs.tr.l = 0; + sregs.tr.db = 0; + sregs.tr.g = 0; + + if (config.cpu_vm_dpmi == CPUVM_KVM) + ldt_buffer = (unsigned char *)monitor->ldt; + sregs.ldt.base = sregs.tr.base + offsetof(struct monitor, ldt); + sregs.ldt.limit = LDT_ENTRIES * LDT_ENTRY_SIZE - 1; + sregs.ldt.selector = 0x20; + sregs.ldt.unusable = 0; + sregs.ldt.type = 0x2; + sregs.ldt.s = 0; + sregs.ldt.dpl = 0; + sregs.ldt.present = 1; + sregs.ldt.avl = 0; + sregs.ldt.l = 0; + sregs.ldt.db = 0; + sregs.ldt.g = 0; + + monitor->tss.ss0 = 0x10; + monitor->tss.IOmapbase = offsetof(struct monitor, io_bitmap); + + // setup GDT + sregs.gdt.base = sregs.tr.base + offsetof(struct monitor, gdt); + sregs.gdt.limit = GDT_ENTRIES * sizeof(Descriptor) - 1; + for (i=1; igdt[i].limit_lo = 0xffff; + monitor->gdt[i].type = 0xa; + monitor->gdt[i].S = 1; + monitor->gdt[i].present = 1; + monitor->gdt[i].limit_hi = 0xf; + monitor->gdt[i].DB = 1; + monitor->gdt[i].gran = 1; + } + // based data selector (0x10), to avoid the ESP register corruption bug + monitor->gdt[GDT_SS].type = 2; + MKBASE(&monitor->gdt[GDT_SS], sregs.tr.base); + /* Set TSS and LDT segments. They are not used yet, as our guest + * does not do LTR or LLDT. + * Note: don't forget to clear TSS-busy bit before using that. */ + kvm_set_desc(&monitor->gdt[GDT_TSS], &sregs.tr); + kvm_set_desc(&monitor->gdt[GDT_LDT], &sregs.ldt); + + sregs.idt.base = sregs.tr.base + offsetof(struct monitor, idt); + sregs.idt.limit = IDT_ENTRIES * sizeof(Gatedesc)-1; + // setup IDT + for (i=0; iidt[i].present = 1; + } + assert(kvm_mon_end - kvm_mon_start <= sizeof(monitor->code)); + memcpy(monitor->code, _binary_kvmmon_o_bin_start, + _binary_kvmmon_o_bin_end - _binary_kvmmon_o_bin_start); + + /* setup paging */ + sregs.cr3 = sregs.tr.base + offsetof(struct monitor, pde); + /* base PDE; others derive from this one */ + monitor->pde[0] = (sregs.tr.base + offsetof(struct monitor, pte)) + | (PG_PRESENT | PG_RW | PG_USER); + /* exclude special regions for KVM-internal TSS and identity page */ + mmap_kvm(MAPPING_SCRATCH|MAPPING_KVM, MONITOR_DOSADDR, + offsetof(struct monitor, kvm_tss), + monitor, MONITOR_DOSADDR, PROT_READ | PROT_WRITE); + mprotect_kvm(MAPPING_KVM, sregs.tr.base + offsetof(struct monitor, code), + sizeof(monitor->code), PROT_READ | PROT_EXEC); + + sregs.cr0 |= X86_CR0_PE | X86_CR0_PG | X86_CR0_NE | X86_CR0_ET; + sregs.cr4 |= X86_CR4_VME; + if (config.umip) + sregs.cr4 |= X86_CR4_UMIP; + + /* setup registers to point to VM86 monitor */ + sregs.cs.base = 0; + sregs.cs.limit = 0xffffffff; + sregs.cs.selector = 0x8; + sregs.cs.db = 1; + sregs.cs.g = 1; + + sregs.ss.base = sregs.tr.base; + sregs.ss.limit = 0xffffffff; + sregs.ss.selector = 0x10; + sregs.ss.db = 1; + sregs.ss.g = 1; + + if (config.cpu_vm == CPUVM_KVM) + warn("Using V86 mode inside KVM\n"); +} + +/* Initialize KVM and memory mappings */ +static int init_kvm_vcpu(void) +{ + int ret, mmap_size; + + /* this call is only there to shut up the kernel saying + "KVM_SET_TSS_ADDR need to be called before entering vcpu" + this is only really needed if the vcpu is started in real mode and + the kernel needs to emulate that using V86 mode, as is necessary + on Nehalem and earlier Intel CPUs */ + ret = ioctl(vmfd, KVM_SET_TSS_ADDR, + MONITOR_DOSADDR + offsetof(struct monitor, kvm_tss)); + if (ret == -1) { + perror("KVM: KVM_SET_TSS_ADDR\n"); + return 0; + } + + uint64_t addr = MONITOR_DOSADDR + offsetof(struct monitor, kvm_identity_map); + ret = ioctl(vmfd, KVM_SET_IDENTITY_MAP_ADDR, &addr); + if (ret == -1) { + perror("KVM: KVM_SET_IDENTITY_MAP_ADDR\n"); + return 0; + } + + vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long)0); + if (vcpufd == -1) { + perror("KVM: KVM_CREATE_VCPU"); + return 0; + } + + assert(cpuid); + /* use host CPUID, adjust signature */ + for (unsigned int i = 0; i < cpuid->nent; i++) { + struct kvm_cpuid_entry2 *entry = &cpuid->entries[i]; + if (entry->function == KVM_CPUID_SIGNATURE) { + entry->eax = KVM_CPUID_FEATURES; + entry->ebx = 0x4b4d564b; // KVMK + entry->ecx = 0x564b4d56; // VMKV + entry->edx = 0x4d; // M + } + } + ret = ioctl(vcpufd, KVM_SET_CPUID2, cpuid); + if (ret == -1) { + perror("KVM: KVM_SET_CPUID2"); + return 0; + } + + mmap_size = ioctl(kvmfd, KVM_GET_VCPU_MMAP_SIZE, NULL); + if (mmap_size == -1) { + perror("KVM: KVM_GET_VCPU_MMAP_SIZE"); + return 0; + } + if (mmap_size < sizeof(*run)) { + error("KVM: KVM_GET_VCPU_MMAP_SIZE unexpectedly small\n"); + return 0; + } + run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0); + if (run == MAP_FAILED) { + perror("KVM: mmap vcpu"); + return 0; + } + run->exit_reason = KVM_EXIT_INTR; + return 1; +} + +int init_kvm_cpu(void) +{ + int ret; + int nent = 512; + + kvmfd = open("/dev/kvm", O_RDWR | O_CLOEXEC); + if (kvmfd == -1) { + error("KVM: error opening /dev/kvm: %s\n", strerror(errno)); + return 0; + } + +#if defined(KVM_CAP_SYNC_MMU) && defined(KVM_CAP_SET_IDENTITY_MAP_ADDR) && \ + defined(KVM_CAP_SET_TSS_ADDR) && defined(KVM_CAP_XSAVE) && \ + defined(KVM_CAP_IMMEDIATE_EXIT) + ret = ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_SYNC_MMU); + if (ret <= 0) { + error("KVM: SYNC_MMU unsupported %x\n", ret); + goto errcap; + } + ret = ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_SET_IDENTITY_MAP_ADDR); + if (ret <= 0) { + error("KVM: SET_IDENTITY_MAP_ADDR unsupported %x\n", ret); + goto errcap; + } + ret = ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR); + if (ret <= 0) { + error("KVM: SET_TSS_ADDR unsupported %x\n", ret); + goto errcap; + } + ret = ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_XSAVE); + if (ret <= 0) { + error("KVM: XSAVE unsupported %x\n", ret); + goto errcap; + } + ret = ioctl(kvmfd, KVM_CHECK_EXTENSION, KVM_CAP_IMMEDIATE_EXIT); +#ifndef KVM_IMMEDIATE_EXIT_SIG + if (ret <= 0) { + error("KVM: IMMEDIATE_EXIT unsupported %x\n", ret); + goto errcap; + } +#endif +#else + error("kernel is too old, KVM unsupported\n"); + goto errcap; +#endif + + vmfd = ioctl(kvmfd, KVM_CREATE_VM, (unsigned long)0); + if (vmfd == -1) { + warn("KVM: KVM_CREATE_VM: %s\n", strerror(errno)); + return 0; + } + + cpuid = malloc(sizeof(*cpuid) + nent * sizeof(cpuid->entries[0])); + memset(cpuid, 0, sizeof(*cpuid) + nent * sizeof(cpuid->entries[0])); // valgrind + cpuid->nent = nent; + ret = ioctl(kvmfd, KVM_GET_SUPPORTED_CPUID, cpuid); + if (ret == -1) { + perror("KVM: KVM_GET_SUPPORTED_CPUID"); + goto err; + } + if ((cpuid->entries[1].edx & CPUID_FEATURES_EDX) != CPUID_FEATURES_EDX) { + error("KVM: unsupported features, need %x got %x\n", + CPUID_FEATURES_EDX, cpuid->entries[1].edx); + goto err; + } + + init_kvm_monitor(); + return 1; + +err: + close(vmfd); + free(cpuid); + cpuid = NULL; +errcap: + close(kvmfd); + return 0; +} + +static struct kvm_userspace_memory_region * +kvm_get_memory_region(dosaddr_t dosaddr, dosaddr_t size) +{ + int slot; + struct kvm_userspace_memory_region *p = &maps[0]; + struct kvm_userspace_memory_region *ret = NULL; + + for (slot = 0; slot < MAXSLOT; slot++, p++) { + if (p->guest_phys_addr <= dosaddr && + dosaddr + size <= p->guest_phys_addr + p->memory_size) { + ret = p; + break; + } + } + /* if we are on full kvm then there should be no missing slots */ + if (config.cpu_vm == CPUVM_KVM && config.cpu_vm_dpmi == CPUVM_KVM) + assert(slot < MAXSLOT); + return ret; +} + +static void set_kvm_memory_region(struct kvm_userspace_memory_region *region) +{ + int ret; + Q_printf("KVM: map slot=%d flags=%d dosaddr=0x%08llx size=0x%08llx unixaddr=0x%llx\n", + region->slot, region->flags, region->guest_phys_addr, + region->memory_size, region->userspace_addr); + ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, region); + if (ret == -1) { + perror("KVM: KVM_SET_USER_MEMORY_REGION"); + leavedos_main(99); + } +} + +void set_kvm_memory_regions(void) +{ + int slot; + + if (!cpuid) + return; + + for (slot = 0; slot < MAXSLOT; slot++) { + struct kvm_userspace_memory_region *p = &maps[slot]; + if (p->memory_size != 0) + set_kvm_memory_region(p); + } +} + +static void mmap_kvm_no_overlap(unsigned targ, void *addr, size_t mapsize, int flags) +{ + struct kvm_userspace_memory_region *region; + int slot; + + if (config.cpu_vm_dpmi != CPUVM_KVM && addr != monitor) { + if (targ >= LOWMEM_SIZE + HMASIZE) + return; + if (targ + mapsize > LOWMEM_SIZE + HMASIZE) + mapsize = LOWMEM_SIZE + HMASIZE - targ; + if (mapsize == 0) + return; + } + + for (slot = 0; slot < MAXSLOT; slot++) + if (maps[slot].memory_size == 0) break; + + if (slot == MAXSLOT) { + error("KVM: insufficient number of memory slots %i\n", MAXSLOT); + leavedos_main(99); + } + + region = &maps[slot]; + region->slot = slot; + /* NOTE: KVM guest does not have the mem_base offset in LDT + * because we can do the same with EPT, keeping guest mem zero-based. */ + region->guest_phys_addr = targ; + region->userspace_addr = (uintptr_t)addr; + region->memory_size = mapsize; + region->flags = flags; + Q_printf("KVM: mapped guest %#x to host addr %p, size=%zx, LOG_DIRTY=%d\n", + targ, addr, mapsize, flags == KVM_MEM_LOG_DIRTY_PAGES ? 1 : 0); + /* NOTE: the actual EPT update is delayed to set_kvm_memory_regions */ +} + +static void do_munmap_kvm(dosaddr_t targ, size_t mapsize) +{ + /* unmaps KVM regions from targ to targ+mapsize, taking care of overlaps + NOTE: the actual EPT update is delayed to set_kvm_memory_regions */ + int slot; + for (slot = 0; slot < MAXSLOT; slot++) { + struct kvm_userspace_memory_region *region = &maps[slot]; + size_t sz = region->memory_size; + unsigned gpa = region->guest_phys_addr; + if (sz > 0 && targ + mapsize > gpa && targ < gpa + sz) { + /* overlap: first unmap this mapping */ + region->memory_size = 0; + /* may need to remap head or tail */ + if (gpa < targ) { + region->memory_size = targ - gpa; + } + if (gpa + sz > targ + mapsize) { + mmap_kvm_no_overlap(targ + mapsize, + (void *)((uintptr_t)region->userspace_addr + + targ + mapsize - gpa), + gpa + sz - (targ + mapsize), region->flags); + } + } + } +} + +void mmap_kvm(int cap, unsigned phys_addr, size_t mapsize, void *addr, dosaddr_t targ, int protect) +{ + size_t pagesize = sysconf(_SC_PAGESIZE); + unsigned int start = targ / pagesize; + unsigned int end = start + mapsize / pagesize; + unsigned int page; + + assert(cap & (MAPPING_INIT_LOWRAM|MAPPING_LOWMEM|MAPPING_KVM|MAPPING_VGAEMU)); + /* with KVM we need to manually remove/shrink existing mappings */ + do_munmap_kvm(phys_addr, mapsize); + mmap_kvm_no_overlap(phys_addr, addr, mapsize, 0); + /* monitor dirty pages on regular low ram for JIT */ + if ((cap & MAPPING_LOWMEM) && IS_EMU_JIT()) + kvm_set_dirty_log(phys_addr, mapsize); + for (page = start; page < end; page++, phys_addr += pagesize) { + int pde_entry = page >> 10; + if (monitor->pde[pde_entry] == 0) + monitor->pde[pde_entry] = monitor->pde[0] + pde_entry*pagesize; + monitor->pte[page] = phys_addr; + } + mprotect_kvm(cap, targ, mapsize, protect); +} + +void mprotect_kvm(int cap, dosaddr_t targ, size_t mapsize, int protect) +{ + size_t pagesize = sysconf(_SC_PAGESIZE); + unsigned int start = targ / pagesize; + unsigned int end = start + mapsize / pagesize; + unsigned int page; + struct kvm_userspace_memory_region *p; + + if (!(cap & (MAPPING_LOWMEM|MAPPING_EMS|MAPPING_HMA| + MAPPING_DPMI|MAPPING_VGAEMU|MAPPING_KVM|MAPPING_CPUEMU| + MAPPING_EXTMEM))) return; + if (memcheck_is_rom(targ)) { + kvm_set_readonly(targ, mapsize); + return; + } + p = kvm_get_memory_region(monitor->pte[start] & _PAGE_MASK, PAGE_SIZE); + if (!p) return; + + /* never apply read and write protections to regions with dirty logging or + phys MMIO and r/o */ + if (!(protect & PROT_WRITE) && + (p->flags & (KVM_MEM_LOG_DIRTY_PAGES|KVM_MEM_READONLY))) + return; + + if (monitor == NULL) return; + + Q_printf("KVM: protecting %x:%zx with prot %x\n", targ, mapsize, protect); + + for (page = start; page < end; page++) { + monitor->pte[page] &= _PAGE_MASK; + if (protect & PROT_WRITE) + monitor->pte[page] |= PG_PRESENT | PG_RW | PG_USER; + else if (protect & PROT_READ) + monitor->pte[page] |= PG_PRESENT | PG_USER; + if (cap & MAPPING_KVM) + monitor->pte[page] &= ~PG_USER; + } + monitor->cr3 = sregs.cr3; /* Force TLB flush */ +} + +static void kvm_set_readonly(dosaddr_t base, dosaddr_t size) +{ + struct kvm_userspace_memory_region *p = kvm_get_memory_region(base, size); + void *addr = (void *)((uintptr_t)(p->userspace_addr + + (base - p->guest_phys_addr))); + do_munmap_kvm(base, size); + mmap_kvm_no_overlap(base, addr, size, KVM_MEM_READONLY); +} + +void kvm_set_mmio(dosaddr_t base, dosaddr_t size, int on) +{ + struct kvm_userspace_memory_region *p = kvm_get_memory_region(base, size); + assert(p->flags & KVM_MEM_LOG_DIRTY_PAGES); + if (on == (p->flags == KVM_MEM_LOG_DIRTY_PAGES)) { + uint64_t region_size = p->memory_size; + p->flags = KVM_MEM_LOG_DIRTY_PAGES; + if (on) { + p->memory_size = 0; + p->flags |= KVM_MEM_READONLY; + } + set_kvm_memory_region(p); + p->memory_size = region_size; + } +} + +/* Enable dirty logging from base to base+size. + * This will not change the KVM-phys->host user space mapping itself but due + * to the way KVM works the memory slot typically needs to be split in 3 parts: + * 1. part without dirty log 2. part with dirty log 3. part without dirty log. + */ +void kvm_set_dirty_log(dosaddr_t base, dosaddr_t size) +{ + struct kvm_userspace_memory_region *p = kvm_get_memory_region(base, size); + void *addr = (void *)((uintptr_t)(p->userspace_addr + + (base - p->guest_phys_addr))); + do_munmap_kvm(base, size); + mmap_kvm_no_overlap(base, addr, size, KVM_MEM_LOG_DIRTY_PAGES); +} + +/* get dirty bitmap for memory region containing base. + * If base is not at the start of that region, the bitmap is shifted. + */ +void kvm_get_dirty_map(dosaddr_t base, unsigned char *bitmap) +{ + size_t bitmap_size; + struct kvm_dirty_log dirty_log = {0}; + struct kvm_userspace_memory_region *p = + kvm_get_memory_region(base, PAGE_SIZE); + + assert(p->flags & KVM_MEM_LOG_DIRTY_PAGES); + dirty_log.slot = p->slot; + dirty_log.dirty_bitmap = bitmap; + ioctl(vmfd, KVM_GET_DIRTY_LOG, &dirty_log); + bitmap_size = ((p->memory_size >> PAGE_SHIFT)+CHAR_BIT-1) / CHAR_BIT; + if (p->guest_phys_addr < base) { + int offset = ((base - p->guest_phys_addr) >> PAGE_SHIFT) / CHAR_BIT; + memmove(bitmap, bitmap + offset, bitmap_size - offset); + } +} + +/* This function works like handle_vm86_fault in the Linux kernel, + except: + * since we use VME we only need to handle + PUSHFD, POPFD, IRETD always + POPF, IRET only if it sets TF or IF with VIP set + STI only if VIP is set and VIF was not set + INT only if it is revectored + * The Linux kernel splits the CPU flags into on-CPU flags and + flags (VFLAGS) IOPL, NT, AC, and ID that are kept on the stack. + Here all those flags are merged into on-CPU flags, with the + exception of IOPL. IOPL is always set to 0 on the CPU, + and to 3 on the stack with PUSHF +*/ +static int kvm_handle_vm86_fault(struct vm86_regs *regs, unsigned int cpu_type) +{ + unsigned char opcode; + int data32 = 0, pref_done = 0; + unsigned int csp = regs->cs << 4; + unsigned int ssp = regs->ss << 4; + unsigned short ip = regs->eip & 0xffff; + unsigned short sp = regs->esp & 0xffff; + unsigned int orig_flags = regs->eflags; + int ret = -1; + + do { + switch (opcode = popb(csp, ip)) { + case 0x66: /* 32-bit data */ data32 = 1; break; + case 0x67: /* 32-bit address */ break; + case 0x2e: /* CS */ break; + case 0x3e: /* DS */ break; + case 0x26: /* ES */ break; + case 0x36: /* SS */ break; + case 0x65: /* GS */ break; + case 0x64: /* FS */ break; + case 0xf2: /* repnz */ break; + case 0xf3: /* rep */ break; + default: pref_done = 1; + } + } while (!pref_done); + + switch (opcode) { + + case 0x9c: { /* only pushfd faults with VME */ + unsigned int flags = regs->eflags & RETURN_MASK; + if (regs->eflags & X86_EFLAGS_VIF) + flags |= X86_EFLAGS_IF; + flags |= X86_EFLAGS_IOPL; + if (data32) + pushl(ssp, sp, flags); + else + pushw(ssp, sp, flags); + break; + } + + case 0xcd: { /* int xx */ + int intno = popb(csp, ip); + ret = VM86_INTx + (intno << 8); + break; + } + + case 0xcf: /* iret */ + if (data32) { + ip = popl(ssp, sp); + regs->cs = popl(ssp, sp); + } else { + ip = popw(ssp, sp); + regs->cs = popw(ssp, sp); + } + /* fall through into popf */ + case 0x9d: { /* popf */ + unsigned int newflags; + if (data32) { + newflags = popl(ssp, sp); + if (cpu_type >= CPU_286 && cpu_type <= CPU_486) { + newflags &= ~X86_EFLAGS_ID; + if (cpu_type < CPU_486) + newflags &= ~X86_EFLAGS_AC; + } + regs->eflags &= ~SAFE_MASK; + } else { + /* must have VIP or TF set in VME, otherwise does not trap */ + newflags = popw(ssp, sp); + regs->eflags &= ~(SAFE_MASK & 0xffff); + } + regs->eflags |= newflags & SAFE_MASK; + if (newflags & X86_EFLAGS_IF) { + regs->eflags |= X86_EFLAGS_VIF; + if (!(orig_flags & X86_EFLAGS_TF) && (orig_flags & X86_EFLAGS_VIP)) + ret = VM86_STI; + } else { + regs->eflags &= ~X86_EFLAGS_VIF; + } + break; + } + + case 0xfa: /* CLI (non-VME) */ + regs->eflags &= ~X86_EFLAGS_VIF; + break; + + case 0xfb: /* STI */ + /* must have VIP set in VME, otherwise does not trap */ + regs->eflags |= X86_EFLAGS_VIF; + ret = VM86_STI; + break; + + default: + return VM86_UNKNOWN; + } + + regs->esp = (regs->esp & 0xffff0000) | sp; + regs->eip = (regs->eip & 0xffff0000) | ip; + if (ret != -1) + return ret; + if (orig_flags & X86_EFLAGS_TF) + return VM86_TRAP + (1 << 8); + return ret; +} + +static void set_vm86_seg(struct kvm_segment *seg, unsigned selector) +{ + seg->selector = selector; + seg->base = selector << 4; + seg->limit = 0xffff; + seg->type = 3; + seg->present = 1; + seg->dpl = 3; + seg->db = 0; + seg->s = 1; + seg->g = 0; + seg->avl = 0; + seg->unusable = 0; +} + +static void set_ldt_seg(struct kvm_segment *seg, unsigned selector) +{ + Descriptor *desc = &monitor->ldt[selector >> 3]; + desc->type |= 1; /* force the "accessed" bit in LDT before access */ + seg->selector = selector; + seg->base = DT_BASE(desc); + seg->limit = DT_LIMIT(desc); + if (desc->gran) seg->limit = (seg->limit << 12) | 0xfff; + seg->type = desc->type; + seg->present = desc->present; + seg->dpl = desc->DPL; + seg->db = desc->DB; + seg->s = desc->S; + seg->g = desc->gran; + seg->avl = desc->AVL; + seg->unusable = !desc->present; +} + +void kvm_update_fpu(void) +{ + struct kvm_fpu fpu = {}; + int ret; + + memcpy(fpu.fpr, vm86_fpu_state.st, sizeof(vm86_fpu_state.st)); + fpu.fcw = vm86_fpu_state.cwd; + fpu.fsw = vm86_fpu_state.swd; + fpu.ftwx = vm86_fpu_state.ftw; + fpu.last_opcode = vm86_fpu_state.fop; + fpu.last_ip = vm86_fpu_state.fip; + fpu.last_dp = vm86_fpu_state.fdp; + memcpy(fpu.xmm, vm86_fpu_state.xmm, sizeof(vm86_fpu_state.xmm)); + fpu.mxcsr = vm86_fpu_state.mxcsr; + ret = ioctl(vcpufd, KVM_SET_FPU, &fpu); + if (ret == -1) { + perror("KVM: KVM_SET_FPU"); + leavedos_main(99); + } +} + +void kvm_get_fpu(void) +{ + struct kvm_fpu fpu; + int ret = ioctl(vcpufd, KVM_GET_FPU, &fpu); + if (ret == -1) { + perror("KVM: KVM_GET_FPU"); + leavedos_main(99); + } + memcpy(vm86_fpu_state.st, fpu.fpr, sizeof(vm86_fpu_state.st)); + vm86_fpu_state.cwd = fpu.fcw; + vm86_fpu_state.swd = fpu.fsw; + vm86_fpu_state.ftw = fpu.ftwx; + vm86_fpu_state.fop = fpu.last_opcode; + vm86_fpu_state.fip = fpu.last_ip; + vm86_fpu_state.fcs = 0; + vm86_fpu_state.fdp = fpu.last_dp; + vm86_fpu_state.fds = 0; + memcpy(vm86_fpu_state.xmm, fpu.xmm, sizeof(vm86_fpu_state.xmm)); + vm86_fpu_state.mxcsr = fpu.mxcsr; +} + +void kvm_enter(int pm) +{ + kvm_update_fpu(); +} + +void kvm_leave(int pm) +{ + kvm_get_fpu(); + + /* collect and invalidate all touched low dirty pages with JIT code */ + if (IS_EMU_JIT()) { + int slot; + struct kvm_userspace_memory_region *p = &maps[0]; + for (slot = 0; slot < MAXSLOT; slot++, p++) + if (p->memory_size && + p->guest_phys_addr + p->memory_size <= LOWMEM_SIZE+HMASIZE && + (p->flags & KVM_MEM_LOG_DIRTY_PAGES) && + memcheck_is_system_ram(p->guest_phys_addr)) { + unsigned char bitmap[(LOWMEM_SIZE+HMASIZE)/CHAR_BIT]; + int i; + kvm_get_dirty_map(p->guest_phys_addr, bitmap); + for (i = 0; i < p->memory_size >> PAGE_SHIFT; i++) + if (test_bit(i, bitmap)) + e_invalidate_page_full(p->guest_phys_addr + (i << PAGE_SHIFT)); + } + } +} + +static int kvm_post_run(struct vm86_regs *regs, struct kvm_regs *kregs) +{ + int ret = ioctl(vcpufd, KVM_GET_REGS, kregs); + if (ret == -1) { + perror("KVM: KVM_GET_REGS"); + leavedos_main(99); + } + ret = ioctl(vcpufd, KVM_GET_SREGS, &sregs); + if (ret == -1) { + perror("KVM: KVM_GET_SREGS"); + leavedos_main(99); + } + /* don't interrupt GDT code */ + if (!(kregs->rflags & X86_EFLAGS_VM) && !(sregs.cs.selector & 4)) { + g_printf("KVM: interrupt in GDT code, resuming\n"); + return 0; + } + if (!run->ready_for_interrupt_injection && + (kregs->rflags & (X86_EFLAGS_IF | X86_EFLAGS_VIF))) { + g_printf("KVM: not ready for injection on ring3\n"); + return 0; + } + + regs->eax = kregs->rax; + regs->ebx = kregs->rbx; + regs->ecx = kregs->rcx; + regs->edx = kregs->rdx; + regs->esi = kregs->rsi; + regs->edi = kregs->rdi; + regs->ebp = kregs->rbp; + regs->esp = kregs->rsp; + regs->eip = kregs->rip; + regs->eflags = kregs->rflags; + + regs->cs = sregs.cs.selector; + regs->ss = sregs.ss.selector; + if (kregs->rflags & X86_EFLAGS_VM) { + regs->ds = sregs.ds.selector; + regs->es = sregs.es.selector; + regs->fs = sregs.fs.selector; + regs->gs = sregs.gs.selector; + } else { + regs->__null_ds = sregs.ds.selector; + regs->__null_es = sregs.es.selector; + regs->__null_fs = sregs.fs.selector; + regs->__null_gs = sregs.gs.selector; + } + return 1; +} + +/* Inner loop for KVM, runs until HLT or signal */ +static unsigned int kvm_run(void) +{ + unsigned int exit_reason = 0; + struct kvm_regs kregs = {}; + static struct vm86_regs saved_regs; + struct vm86_regs *regs = &monitor->regs; + + if (run->exit_reason != KVM_EXIT_HLT && + memcmp(regs, &saved_regs, sizeof(*regs))) { + /* Only set registers if changes happened, usually + this means a hardware interrupt or sometimes + a callback, and also for the very first call to boot */ + int ret; + + kregs.rax = regs->eax; + kregs.rbx = regs->ebx; + kregs.rcx = regs->ecx; + kregs.rdx = regs->edx; + kregs.rsi = regs->esi; + kregs.rdi = regs->edi; + kregs.rbp = regs->ebp; + kregs.rsp = regs->esp; + kregs.rip = regs->eip; + kregs.rflags = regs->eflags; + ret = ioctl(vcpufd, KVM_SET_REGS, &kregs); + if (ret == -1) { + perror("KVM: KVM_SET_REGS"); + leavedos_main(99); + } + + if (regs->eflags & X86_EFLAGS_VM) { + set_vm86_seg(&sregs.cs, regs->cs); + set_vm86_seg(&sregs.ds, regs->ds); + set_vm86_seg(&sregs.es, regs->es); + set_vm86_seg(&sregs.fs, regs->fs); + set_vm86_seg(&sregs.gs, regs->gs); + set_vm86_seg(&sregs.ss, regs->ss); + } else { + set_ldt_seg(&sregs.cs, regs->cs); + set_ldt_seg(&sregs.ds, regs->__null_ds); + set_ldt_seg(&sregs.es, regs->__null_es); + set_ldt_seg(&sregs.fs, regs->__null_fs); + set_ldt_seg(&sregs.gs, regs->__null_gs); + set_ldt_seg(&sregs.ss, regs->ss); + } + ret = ioctl(vcpufd, KVM_SET_SREGS, &sregs); + if (ret == -1) { + perror("KVM: KVM_SET_SREGS"); + leavedos_main(99); + } + } + + while (!exit_reason) { + int ret = ioctl(vcpufd, KVM_RUN, NULL); + int errn = errno; + + /* KVM should only exit for four reasons: + 1. KVM_EXIT_HLT: at the hlt in kvmmon.S following an exception. + In this case the registers are pushed on and popped from the stack. + 2. KVM_EXIT_INTR: (with ret==-1) after a signal. In this case we + must restore and save registers using ioctls. + 3. KVM_EXIT_IRQ_WINDOW_OPEN: if it is not possible to inject interrupts + (or in our case properly interrupt using reason 2) + KVM is re-entered asking it to exit when interrupt injection is + possible, then it exits with this code. This only happens if a signal + occurs during execution of the monitor code in kvmmon.S. + 4. KVM_EXIT_MMIO: when attempting to write to ROM or r/w from/to MMIO + */ + if (ret != 0 && ret != -1) + error("KVM: strange return %i, errno=%i\n", ret, errn); + if (ret == -1 && errn == EINTR) { + if (!kvm_post_run(regs, &kregs)) + continue; + saved_regs = *regs; + exit_reason = KVM_EXIT_INTR; + break; + } else if (ret != 0) { + error("KVM: KVM_RUN failed: %s\n", strerror(errn)); + leavedos_main(99); + } + + switch (run->exit_reason) { + case KVM_EXIT_HLT: + exit_reason = KVM_EXIT_HLT; + break; + case KVM_EXIT_MMIO: + /* for ROM: simply ignore the write and continue */ + if (memcheck_is_rom(run->mmio.phys_addr)) + break; + + /* from the KVM api.txt: "the corresponding operations are complete + (and guest state is consistent) only after userspace has re-entered + the kernel with KVM_RUN. The kernel side will first finish + incomplete operations and then check for pending signals." */ + kvm_set_immediate_exit(1); + do { + dosaddr_t addr = (dosaddr_t)run->mmio.phys_addr; + unsigned char *data = run->mmio.data; + if (run->mmio.is_write) { + switch(run->mmio.len) { + case 1: write_byte(addr, data[0]); break; + case 2: write_word(addr, *(uint16_t*)data); break; + case 4: write_dword(addr, *(uint32_t*)data); break; + case 8: write_qword(addr, *(uint64_t*)data); break; + } + } else { + switch(run->mmio.len) { + case 1: data[0] = read_byte(addr); break; + case 2: *(uint16_t*)data = read_word(addr); break; + case 4: *(uint32_t*)data = read_dword(addr); break; + case 8: *(uint64_t*)data = read_qword(addr); break; + } + } + ret = ioctl(vcpufd, KVM_RUN, NULL); + /* read-modify-write instructions give two KVM_EXIT_MMIO + exits in a row before the signal exit */ + } while (ret == 0 && run->exit_reason == KVM_EXIT_MMIO); + assert(ret == -1 && errno == EINTR); + kvm_set_immediate_exit(0); + /* going to emulate some instructions */ + if (!kvm_post_run(regs, &kregs)) + break; + saved_regs = *regs; + exit_reason = KVM_EXIT_MMIO; + break; + case KVM_EXIT_IRQ_WINDOW_OPEN: + run->request_interrupt_window = !run->ready_for_interrupt_injection; + if (run->request_interrupt_window || !run->if_flag) break; + if (!kvm_post_run(regs, &kregs)) + break; + + saved_regs = *regs; + exit_reason = KVM_EXIT_IRQ_WINDOW_OPEN; + break; + case KVM_EXIT_FAIL_ENTRY: + error("KVM_EXIT_FAIL_ENTRY: hardware_entry_failure_reason = 0x%llx\n", + (unsigned long long)run->fail_entry.hardware_entry_failure_reason); + leavedos_main(99); + break; + case KVM_EXIT_INTERNAL_ERROR: + error("KVM_EXIT_INTERNAL_ERROR: suberror = 0x%x\n", run->internal.suberror); + leavedos_main(99); + break; + default: + error("KVM: exit_reason = 0x%x\n", run->exit_reason); + leavedos_main(99); + break; + } + } + return exit_reason; +} + +static void kvm_vme_tf_popf_fixup(struct vm86_regs *regs) +{ + if (regs->eip == 0 || !(regs->eflags & X86_EFLAGS_TF)) + return; + /* The problem was noticed on + * AMD FX(tm)-8350 Eight-Core Processor + * stepping: 0 microcode: 0x6000852 + * + * popfw fails to clear TF flag: + * https://github.com/dosemu2/dosemu2/issues/1350 + * We need to clear it by hands. + * We check for popf and assume it tried to clear TF. + * */ + if (READ_BYTE(SEGOFF2LINEAR(regs->cs, regs->eip - 1)) == 0x9d) { + if (!config.test_mode) { + error("KVM: applying TF fixup\n"); + regs->eflags &= ~X86_EFLAGS_TF; + } else { + error("KVM: not applying TF fixup (test mode)\n"); + } + } +} + +/* Emulate vm86() using KVM */ +int kvm_vm86(struct vm86_struct *info) +{ + struct vm86_regs *regs; + int vm86_ret; + unsigned int trapno, exit_reason; + + regs = &monitor->regs; + *regs = info->regs; +#if 0 + memcpy(&monitor->fpstate, &vm86_fpu_state, sizeof(vm86_fpu_state)); +#endif + monitor->int_revectored = info->int_revectored; + monitor->tss.esp0 = offsetof(struct monitor, regs) + sizeof(monitor->regs); + + regs->eflags &= (SAFE_MASK | X86_EFLAGS_VIF | X86_EFLAGS_VIP); + regs->eflags |= X86_EFLAGS_FIXED | X86_EFLAGS_VM | X86_EFLAGS_IF; + + do { + exit_reason = kvm_run(); + + vm86_ret = VM86_SIGNAL; + if (exit_reason != KVM_EXIT_HLT) break; + + /* high word(orig_eax) = exception number */ + /* low word(orig_eax) = error code */ + trapno = (regs->orig_eax >> 16) & 0xff; +#if 1 + if (trapno == 1 && (sregs.cr4 & X86_CR4_VME)) + kvm_vme_tf_popf_fixup(regs); +#endif + if (trapno == 1 || trapno == 3) + vm86_ret = VM86_TRAP | (trapno << 8); + else if (trapno == 0xd) + vm86_ret = kvm_handle_vm86_fault(regs, info->cpu_type); + } while (vm86_ret == -1); + + info->regs = *regs; + info->regs.eflags |= X86_EFLAGS_IOPL; +#if 0 + /* we do not update fpstate for performance reasons */ + memcpy(&vm86_fpu_state, &monitor->fpstate, sizeof(vm86_fpu_state)); +#endif + if (vm86_ret == VM86_SIGNAL && exit_reason == KVM_EXIT_HLT) { + unsigned trapno = (regs->orig_eax >> 16) & 0xff; + unsigned err = regs->orig_eax & 0xffff; + vm86_fault(trapno, err, monitor->cr2); + } else if (exit_reason == KVM_EXIT_MMIO) { + dosaddr_t addr = (dosaddr_t)run->mmio.phys_addr; + if (vga.inst_emu && vga_access(addr, addr)) + instr_emu_sim(NULL, 0, VGA_EMU_INST_EMU_COUNT); + } + return vm86_ret; +} + +/* Emulate do_dpmi_control() using KVM */ +int kvm_dpmi(cpuctx_t *scp) +{ + struct vm86_regs *regs; + int ret; + unsigned int exit_reason; + + monitor->tss.esp0 = offsetof(struct monitor, regs) + + offsetof(struct vm86_regs, es); +#if 0 + memcpy(&monitor->fpstate, &vm86_fpu_state, sizeof(vm86_fpu_state)); +#endif + regs = &monitor->regs; + do { + regs->eax = _eax; + regs->ebx = _ebx; + regs->ecx = _ecx; + regs->edx = _edx; + regs->esi = _esi; + regs->edi = _edi; + regs->ebp = _ebp; + regs->esp = _esp; + regs->eip = _eip; + + regs->cs = _cs; + regs->__null_ds = _ds; + regs->__null_es = _es; + regs->ss = _ss; + regs->__null_fs = _fs; + regs->__null_gs = _gs; + + regs->eflags = _eflags; + regs->eflags &= (SAFE_MASK | X86_EFLAGS_VIF | X86_EFLAGS_VIP | + X86_EFLAGS_IF); + regs->eflags |= X86_EFLAGS_FIXED; + + exit_reason = kvm_run(); + + _eax = regs->eax; + _ebx = regs->ebx; + _ecx = regs->ecx; + _edx = regs->edx; + _esi = regs->esi; + _edi = regs->edi; + _ebp = regs->ebp; + _esp = regs->esp; + _eip = regs->eip; + + _cs = regs->cs; + _ds = regs->__null_ds; + _es = regs->__null_es; + _ss = regs->ss; + _fs = regs->__null_fs; + _gs = regs->__null_gs; + + _eflags = regs->eflags; + +#if 0 + /* we do not update fpstate for performance reasons */ + memcpy(&vm86_fpu_state, &monitor->fpstate, sizeof(vm86_fpu_state)); +#endif + + ret = DPMI_RET_DOSEMU; /* mirroring sigio/sigalrm */ + if (exit_reason == KVM_EXIT_HLT) { + /* orig_eax >> 16 = exception number */ + /* orig_eax & 0xffff = error code */ + _cr2 = monitor->cr2; + _trapno = (regs->orig_eax >> 16) & 0xff; + _err = regs->orig_eax & 0xffff; + if (_trapno > 0x10) { + // convert software ints into the GPFs that the DPMI code expects + _err = (_trapno << 3) + 2; + _trapno = 0xd; + _eip -= 2; + } + + if (_trapno == 0x10) { +#if 0 + struct kvm_fpu fpu; + ioctl(vcpufd, KVM_GET_FPU, &fpu); +#ifdef __x86_64__ + memcpy(__fpstate->_st, fpu.fpr, sizeof __fpstate->_st); + __fpstate->cwd = fpu.fcw; + __fpstate->swd = fpu.fsw; + __fpstate->ftw = fpu.ftwx; + __fpstate->fop = fpu.last_opcode; + __fpstate->rip = fpu.last_ip; + __fpstate->rdp = fpu.last_dp; + memcpy(__fpstate->_xmm, fpu.xmm, sizeof __fpstate->_xmm); +#else + memcpy(__fpstate->_st, fpu.fpr, sizeof __fpstate->_st); + __fpstate->cw = fpu.fcw; + __fpstate->sw = fpu.fsw; + __fpstate->tag = fpu.ftwx; + __fpstate->ipoff = fpu.last_ip; + __fpstate->cssel = _cs; + __fpstate->dataoff = fpu.last_dp; + __fpstate->datasel = _ds; +#endif + print_exception_info(scp); +#endif + dbug_printf("coprocessor exception, calling IRQ13\n"); + raise_fpu_irq(); + ret = DPMI_RET_DOSEMU; + } else + ret = DPMI_RET_FAULT; + } else if (exit_reason == KVM_EXIT_MMIO) { + dosaddr_t addr = (dosaddr_t)run->mmio.phys_addr; + if (vga.inst_emu && vga_access(addr, addr)) + instr_emu_sim(scp, 1, VGA_EMU_INST_EMU_COUNT); + ret = DPMI_RET_CLIENT; + } + } while (!signal_pending() && ret == DPMI_RET_CLIENT); + return ret; +} + +void kvm_done(void) +{ + close(vcpufd); + close(vmfd); + close(kvmfd); + free(cpuid); +} + +#endif diff --git a/src/base/emu-i386/kvmmon.S b/src/base/emu-i386/kvmmon.S new file mode 100644 index 0000000..fd3e64b --- /dev/null +++ b/src/base/emu-i386/kvmmon.S @@ -0,0 +1,101 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * IDT handlers for the V86 monitor using KVM + * These just push the exception number and state, and then execute + * a common HLT which is trapped by KVM. + */ + + .section .text + .code32 + + .global _start +_start: // dummy + .globl kvm_mon_start +kvm_mon_start: + + i = 0 + .rept 0x100 + .if i == 8 || (i >= 0xa && i <= 0xe) + /* these exceptions already pushed an error code + (exception 0x11 (alignment) and 0x1e (security) cannot occur) */ + nop + nop + .else + /* push fake error code for consistent stack */ + pushl $0 + .endif + movw $i,2(%esp) + jmp kvm_mon_main + i = i + 1 + .fill kvm_mon_start+32*i-., 1, 0x90 + .endr + +kvm_mon_main: + push %gs + push %fs + push %es + push %ds + push %eax + push %ebp + push %edi + push %esi + push %edx + push %ecx + push %ebx + /* cr3: mark as 0 to not reload by default */ + pushl $0 + mov %cr2,%eax + push %eax +#if 0 + subl $0x200,%esp + fxsave (%esp) +#endif + .globl kvm_mon_hlt +kvm_mon_hlt: + hlt +#if 0 + fxrstor (%esp) + addl $0x200,%esp +#endif + pop %eax + pop %eax + or %eax,%eax + jz skip_cr3 + /* flush TLB by reloading cr3 if nonzero (set by mprotect_kvm()) */ + mov %eax,%cr3 +skip_cr3: + pop %ebx + pop %ecx + pop %edx + pop %esi + pop %edi + pop %ebp + pop %eax + pop %ds + pop %es + pop %fs + pop %gs + add $0x4,%esp + iret + + .globl kvm_mon_end +kvm_mon_end: + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/base/emu-i386/kvmmon_offsets.h b/src/base/emu-i386/kvmmon_offsets.h new file mode 100644 index 0000000..29bb2ba --- /dev/null +++ b/src/base/emu-i386/kvmmon_offsets.h @@ -0,0 +1,3 @@ +extern const unsigned kvm_mon_start; +extern const unsigned kvm_mon_hlt; +extern const unsigned kvm_mon_end; diff --git a/src/base/emu-i386/simx86/Makefile b/src/base/emu-i386/simx86/Makefile new file mode 100644 index 0000000..8db3da2 --- /dev/null +++ b/src/base/emu-i386/simx86/Makefile @@ -0,0 +1,33 @@ +# +# (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". +# +# for details see file COPYING in the DOSEMU distribution +# + +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf + + +EM86DIR=$(REALTOPDIR)/src/emu-i386/simx86 +EM86FLG=-Dlinux -DDOSEMU +ifeq ($(X86_JIT),1) +JITFILES = codegen-x86.c fp87-x86.c sigsegv.c cpatch.c trees.c +endif +CFILES = interp.c cpu-emu.c modrm-gen.c $(JITFILES) \ + codegen-sim.c fp87-sim.c modrm-sim.c protmode.c \ + memory.c tables.c +ALL_CPPFLAGS +=-I$(EM86DIR) $(EM86FLG) + +#ALL_CPPFLAGS +=-DNOJUMPS + +SFILES= +ALL=$(CFILES) $(SFILES) + +OBJS=$(CFILES:.c=.o) +DEPENDS=$(CFILES:.c=.d) + +include $(REALTOPDIR)/src/Makefile.common + +all: lib + +install: all diff --git a/src/base/emu-i386/simx86/codegen-arch.h b/src/base/emu-i386/simx86/codegen-arch.h new file mode 100644 index 0000000..0361faa --- /dev/null +++ b/src/base/emu-i386/simx86/codegen-arch.h @@ -0,0 +1,11 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +#include "codegen.h" +#include "codegen-sim.h" +#ifdef HOST_ARCH_X86 +#include "codegen-x86.h" +#endif diff --git a/src/base/emu-i386/simx86/codegen-sim.c b/src/base/emu-i386/simx86/codegen-sim.c new file mode 100644 index 0000000..b49ba55 --- /dev/null +++ b/src/base/emu-i386/simx86/codegen-sim.c @@ -0,0 +1,3019 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +/* + * BACK-END for the cpuemu interpreter. + * + * This module is a plain stupid interpreter for x86 code in C. + * There is a bit of 'lazy' flag handling but not much else. + * This is only a reference framework. No real-time stuff is possible + * with full interpretation (unless of course you have a 3GHz+ CPU) + * + * All instructions operate on a virtual CPU image in memory ("TheCPU"), + * and are completely self-contained. They read from TheCPU registers, + * operate, and store back to the same registers. There's an exception - + * FLAGS, which are not stored back until the end of a code block. + * + * There are two main functions here: + * AddrGen, which implements the AGU (Address Generation Unit). + * It calculates the address coming from ModRM and stores + * it into a well-defined register (edi in the x86 case) + * Gen, which does the ALU work and all the rest. There is no + * branch specific unit, as the branches are (in principle) + * all interpreted. + * Both functions use a variable parameter approach, just to make them + * hard to follow ;-) + * + */ + +#include +#include +#include +#include +#include "port.h" +#include "emu86.h" +#include "dos2linux.h" +#include "vgaemu.h" +#include "video.h" +#include "codegen.h" +#include "codegen-sim.h" + +#undef DEBUG_MORE + +void (*Gen)(int op, int mode, ...); +void (*AddrGen)(int op, int mode, ...); +unsigned int (*CloseAndExec)(unsigned int PC, int mode); +static unsigned int CloseAndExec_sim(unsigned int PC, int mode); + +int UseLinker = 0; + +static unsigned int P0 = (unsigned)-1; + +///////////////////////////////////////////////////////////////////////////// + +#define Offs_From_Arg() (signed char)(va_arg(ap,int)) + +/* WARNING - these are signed char offsets, NOT pointers! */ +char OVERR_DS=Ofs_XDS, OVERR_SS=Ofs_XSS; + +/* working registers of the host CPU */ +wkreg DR1; // "eax" +wkreg DR2; // "edx" +wkreg AR1; // "edi" +wkreg AR2; // "esi" +wkreg SR1; // "ebp" +wkreg TR1; // "ecx" +flgtmp RFL; + +///////////////////////////////////////////////////////////////////////////// + +/* empirical!! */ +//static int goodmemref(long m) +//{ +// if ((m>0) && (m<0x110000)) return 1; +// if ((m>0x40000000) && (m> 6; + if (RFL.mode & MBYTE) + return RFL.RES.b.bl==0; + if (RFL.mode & DATA16) + return RFL.RES.w.l==0; + return RFL.RES.d==0; +} + +static inline int is_sf_set(void) +{ + if (RFL.valid==V_INVALID) + return (CPUBYTE(Ofs_FLAGS)&0x80) >> 7; + if (RFL.mode & MBYTE) + return (RFL.RES.b.bl & 0x80) >> 7; + if (RFL.mode & DATA16) + return (RFL.RES.w.l & 0x8000) >> 15; + return (RFL.RES.d & 0x80000000) >> 31; +} + +static inline int is_of_set(void) +{ + if (RFL.valid==V_INVALID) + return (CPUWORD(Ofs_FLAGS)&0x800) >> 11; + if (RFL.mode & CLROVF) + return 0; + if (RFL.mode & SETOVF) + return 1; + return ((RFL.cout >> 31) ^ (RFL.cout >> 30)) & 1; +} + +#define SET_CF(c) CPUBYTE(Ofs_FLAGS)=((CPUBYTE(Ofs_FLAGS)&0xfe)|(c)) + +/* add/sub rule for carry using MSB: + * the carry-out expressions from Bochs 2.6 are used here. + * RFL.cout is a cheap-to-compute 32-bit word that encodes the following flags: + * CF is bit 31 + * OF is bit 31 xor bit 30 + * AF is bit 3 + * + * src1 src2 res cy(add) cy(sub) + * 0 0 0 0 0 + * 0 0 1 0 1 + * 0 1 0 1 1 + * 0 1 1 0 1 + * 1 0 0 1 0 + * 1 0 1 0 0 + * 1 1 0 1 0 + * 1 1 1 1 1 +*/ +static inline void FlagHandleAdd(unsigned src1, unsigned src2, unsigned res, + int wordsize) +{ + unsigned int cout = (src1 & src2) | ((src1 | src2) & ~res); + if (wordsize == 32) RFL.cout = cout; + if (wordsize == 16) RFL.cout = ((cout >> 14) << 30) | (cout & 8); + if (wordsize == 8) RFL.cout = ((cout >> 6) << 30) | (cout & 8); + int cy = cout >> (wordsize - 1); + SET_CF(cy & 1); +} + +static inline void FlagHandleSub(unsigned src1, unsigned src2, unsigned res, + int wordsize) +{ + unsigned int cout = (~src1 & src2) | ((~src1 ^ src2) & res); + if (wordsize == 32) RFL.cout = cout; + if (wordsize == 16) RFL.cout = ((cout >> 14) << 30) | (cout & 8); + if (wordsize == 8) RFL.cout = ((cout >> 6) << 30) | (cout & 8); + int cy = cout >> (wordsize - 1); + SET_CF(cy & 1); +} + +static inline void FlagHandleIncDec(unsigned low, unsigned high, int wordsize) +{ + unsigned int cout = low & ~high; + if (wordsize == 32) RFL.cout = cout; + if (wordsize == 16) RFL.cout = ((cout >> 14) << 30) | (cout & 8); + if (wordsize == 8) RFL.cout = ((cout >> 6) << 30) | (cout & 8); +} + +static inline int FlagSync_NZ (void) +{ + int zr,pl,nf; + if (RFL.valid==V_INVALID) return (CPUBYTE(Ofs_FLAGS)&0xc0); + if (RFL.mode & MBYTE) { + zr = (RFL.RES.b.bl==0) << 6; + pl = RFL.RES.b.bl & 0x80; + } + else if (RFL.mode & DATA16) { + zr = (RFL.RES.w.l==0) << 6; + pl = (RFL.RES.d>>8) & 0x80; + } + else { + zr = (RFL.RES.d==0) << 6; + pl = (RFL.RES.d>>24) & 0x80; + } + nf = zr | pl; + if (debug_level('e')>2) e_printf("Sync NZ flags = %02x\n", nf); + return nf; +} + +// Clear OF, leaving PF, AF, SF, ZF alone. +static void ClearOF(void) +{ + // Working on lazy flags + if(RFL.valid!=V_INVALID) + RFL.mode = (RFL.mode & ~(IGNOVF | SETOVF)) | CLROVF; + // Working on real flags + else + CPUWORD(Ofs_FLAGS) = CPUWORD(Ofs_FLAGS) & ~0x800; +} + +// Set OF, leaving PF, AF, SF, ZF alone. +static void SetOF(void) +{ + // Working on lazy flags + if(RFL.valid!=V_INVALID) + RFL.mode = (RFL.mode & ~(IGNOVF | CLROVF)) | SETOVF; + // Working on real flags + else + CPUWORD(Ofs_FLAGS) = CPUWORD(Ofs_FLAGS) | 0x800; +} + +#define SET_OF(ov) { if(ov) SetOF(); else ClearOF(); } + +static inline int FlagSync_O_ (void) +{ + int nf; + // OF + /* overflow rule using MSB: + * src1 src2 res OF ad/sub + * 0 0 0 0 0 + * 0 0 1 1 0 + * 0 1 0 0 0 + * 0 1 1 0 1 + * 1 0 0 0 1 + * 1 0 1 0 0 + * 1 1 0 1 0 + * 1 1 1 0 0 + */ + if (RFL.valid==V_INVALID) return (CPUWORD(Ofs_FLAGS)&0x800); + if (RFL.mode & CLROVF) + nf = 0; + else if (RFL.mode & SETOVF) + nf = 0x800; + else + // 80000000->0800 ^ 40000000->0800 + nf = ((RFL.cout >> 20) ^ (RFL.cout >> 19)) & 0x800; + if (debug_level('e')>1) e_printf("Sync O flag = %04x\n", nf); + return nf; +} + +void FlagSync_O (void) +{ + int nf; + if (RFL.mode & IGNOVF) return; + nf = FlagSync_O_(); + CPUWORD(Ofs_FLAGS) = (CPUWORD(Ofs_FLAGS) & 0xf7ff) | nf; +} + + +static unsigned char parity[256] = + { 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 0, 4, 4, 0, 4, 0, 0, 4, 4, 0, 0, 4, 0, 4, 4, 0, + 4, 0, 0, 4, 0, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 4 }; + + +static inline int FlagSync_AP_ (void) +{ + int af,pf,nf; + if (RFL.valid==V_INVALID) return (CPUBYTE(Ofs_FLAGS)&0x14); + // AF bit 4, S1, S2, RES: + // 0 0 0 -> NA 0 1 0 -> AC 1 0 0 -> AC 1 1 0 -> NA + // 0 0 1 -> AC 0 1 1 -> NA 1 0 1 -> NA 1 1 1 -> AC + if ((RFL.valid==V_SUB)||(RFL.valid==V_SBB)|| + (RFL.valid==V_ADC)||(RFL.valid==V_ADD)) + af = (RFL.cout & 0x8) << 1; + else + af = CPUBYTE(Ofs_FLAGS)&0x10; // Intel says undefined. + // PF + pf = parity[RFL.RES.b.bl]; + nf = af | pf; + if (debug_level('e')>2) e_printf("Sync AP flags = %02x\n", nf); + return nf; +} + +void FlagSync_AP (void) +{ + int nf = FlagSync_AP_(); + CPUBYTE(Ofs_FLAGS) = (CPUBYTE(Ofs_FLAGS) & 0xeb) | nf; +} + + +void FlagSync_All (void) +{ + int nf,mk; + if (RFL.valid==V_INVALID) return; + nf = FlagSync_AP_() | FlagSync_NZ(); + if (RFL.mode & IGNOVF) + mk = 0xff2b; + else { + nf |= FlagSync_O_(); + mk = 0xf72b; + } + if (debug_level('e')>1) e_printf("Sync ALL flags = %04x\n", nf); + CPUWORD(Ofs_FLAGS) = (CPUWORD(Ofs_FLAGS) & mk) | nf; + RFL.valid = V_INVALID; +} + + +///////////////////////////////////////////////////////////////////////////// + +void InitGen_sim(void) +{ + Gen = Gen_sim; + AddrGen = AddrGen_sim; + CloseAndExec = CloseAndExec_sim; + RFL.cout = RFL.RES.d = 0; + RFL.valid = V_INVALID; +} + +/* + * address generator unit + * careful - do not use eax, and NEVER change any flag! + */ +void AddrGen_sim(int op, int mode, ...) +{ + va_list ap; +#if PROFILE + hitimer_t t0 = 0; + if (debug_level('e')) t0 = GETTSC(); +#endif + + va_start(ap, mode); + switch(op) { + case A_DI_0: // base(32), imm + case A_DI_1: { // base(32), {imm}, reg, {shift} + long idsp=0; + signed char ofs; + ofs = va_arg(ap,int); + if (mode & MLEA) { // discard base reg + AR1.d = 0; // ofs = Ofs_RZERO; + } + else AR1.d = CPULONG(ofs); + + idsp = va_arg(ap,int); + if (op==A_DI_0) { + GTRACE3("A_DI_0",0xff,0xff,idsp); + TR1.d = idsp; + } + else if (mode & ADDR16) { + signed char o = Offs_From_Arg(); + GTRACE3("A_DI_1",o,ofs,idsp); + TR1.d = CPUWORD(o); + TR1.w.l += idsp; + } + else { + signed char o = Offs_From_Arg(); + GTRACE3("A_DI_1",o,ofs,idsp); + TR1.d = CPULONG(o) + idsp; + } + AR1.d += TR1.d; + } + break; + case A_DI_2: { // base(32), {imm}, reg, reg, {shift} + long idsp=0; + signed char ofs; + ofs = va_arg(ap,int); + if (mode & MLEA) { // discard base reg + AR1.d = 0; // ofs = Ofs_RZERO; + } + else AR1.d = CPULONG(ofs); + + idsp = va_arg(ap,int); + if (mode & ADDR16) { + signed char o1 = Offs_From_Arg(); + signed char o2 = Offs_From_Arg(); + GTRACE4("A_DI_2",o1,ofs,o2,idsp); + TR1.d = CPUWORD(o1) + CPUWORD(o2) + idsp; + AR1.d += TR1.w.l; + } + else { + signed char o1 = Offs_From_Arg(); + signed char o2 = Offs_From_Arg(); + unsigned char sh; + sh = (unsigned char)(va_arg(ap,int)); + GTRACE5("A_DI_2",o1,ofs,o2,idsp,sh); + TR1.d = CPULONG(o1) + + (CPULONG(o2) << (sh & 0x1f)) + idsp; + AR1.d += TR1.d; + } + } + break; + case A_DI_2D: { // modrm_sibd, 32-bit mode + long idsp; + unsigned char sh; + signed char o; + if (mode & MLEA) { + AR1.d = 0; + } + else { + AR1.d = CPULONG(OVERR_DS); + } + idsp = va_arg(ap,int); + o = Offs_From_Arg(); + sh = (unsigned char)(va_arg(ap,int)); + GTRACE4("A_DI_2D",o,0xff,idsp,sh); + TR1.d = (CPULONG(o) << (sh & 0x1f)) + idsp; + AR1.d += TR1.d; + } + break; + case A_SR_SH4: { // real mode make base addr from seg + signed char o = Offs_From_Arg(); + GTRACE1("A_SR_SH4",o); + SetSegReal(CPUWORD(o), o); + if (o == Ofs_SS) + CEmuStat |= CeS_MOVSS; + } + break; + } + va_end(ap); +#if PROFILE + if (debug_level('e')) GenTime += (GETTSC() - t0); +#endif +} + +void Gen_sim(int op, int mode, ...) +{ + va_list ap; + uint32_t S1, S2; +#if PROFILE + hitimer_t t0 = 0; + if (debug_level('e')) t0 = GETTSC(); +#endif + + P0 = (unsigned)-1; + va_start(ap, mode); + switch(op) { + case L_NOP: + GTRACE0("L_NOP"); + break; + // Special case: CR0&0x3f + case L_CR0: + GTRACE0("L_CR0"); + DR1.d = CPULONG(Ofs_CR0) & 0x3f; + break; + + case O_FOP: { + unsigned char exop = (unsigned char)va_arg(ap,int); + int reg = va_arg(ap,int); + GTRACE2("O_FPOP",exop,reg); + if (Fp87_op(exop, reg)) TheCPU.err = -96; + } + break; + + case L_REG: { + signed char o = Offs_From_Arg(); + if (mode&(MBYTE|MBYTX)) { + GTRACE1("L_REG_BYTE",o); + DR1.b.bl = CPUBYTE(o); + } + else { + GTRACE1("L_REG_WL",o); + if ((mode)&DATA16) DR1.w.l = CPUWORD(o); + else DR1.d = CPULONG(o); + } } + break; + case S_REG: { + signed char o = Offs_From_Arg(); + if (mode&MBYTE) { + GTRACE1("S_REG_BYTE",o); + CPUBYTE(o) = DR1.b.bl; + } + else { + GTRACE1("S_REG_WL",o); + if ((mode)&DATA16) CPUWORD(o) = DR1.w.l; + else CPULONG(o) = DR1.d; + } } + break; + case L_REG2REG: { + signed char o1 = Offs_From_Arg(); + signed char o2 = Offs_From_Arg(); + GTRACE2("REGtoREG",o1,o2); + if ((mode) & MBYTE) { + CPUBYTE(o2) = CPUBYTE(o1); + } else if ((mode) & DATA16) { + CPUWORD(o2) = CPUWORD(o1); + } else { + CPULONG(o2) = CPULONG(o1); + } } + break; + case S_DI_R: { + signed char o = Offs_From_Arg(); + GTRACE1("S_DI_R",o); + if (mode & DATA16) + CPUWORD(o) = AR1.w.l; + else + CPULONG(o) = AR1.d; + } + break; + case S_DI_IMM: { + int v = va_arg(ap,int); + dosaddr_t addr = AR1.d; + if (mode&MBYTE) { + GTRACE3("S_DI_IMM_B",0xff,0xff,v); + write_byte(addr, v); + } else { + GTRACE3("S_DI_IMM_WL",0xff,0xff,v); + if (mode&DATA16) write_word(addr, v); + else write_dword(addr, v); + } } + break; + + case L_IMM: { + signed char o = Offs_From_Arg(); + int v = va_arg(ap, int); + GTRACE3("L_IMM",o,0xff,v); + if (mode & MBYTE) { + CPUBYTE(o) = (signed char)v; + } + else if (mode & DATA16) { + CPUWORD(o) = (short)v; + } + else { + CPULONG(o) = v; + } } + break; + case L_IMM_R1: { + int v = va_arg(ap, int); + GTRACE3("L_IMM_R1",0xff,0xff,v); + if (mode & MBYTE) { + DR1.b.bl = (signed char)v; + } + else if (mode & DATA16) { + DR1.w.l = (short)v; + } + else { + DR1.d = v; + } } + break; + case L_MOVZS: { + signed char o; + int rcod = va_arg(ap,int)&1; // 0=z 1=s + o = Offs_From_Arg(); + GTRACE3("L_MOVZS",o,0xff,rcod); + if (mode & MBYTX) { + if (rcod) + DR1.d = DR1.bs.bl; + else + DR1.d = DR1.b.bl; + } + else { + if (rcod) + DR1.d = DR1.ws.l; + else + DR1.d = DR1.w.l; + } + if (mode & DATA16) { + CPUWORD(o) = DR1.w.l; + } + else { + CPULONG(o) = DR1.d; + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } + break; + + case L_LXS1: { + signed char o = Offs_From_Arg(); + GTRACE1("L_LXS1",o); + if (mode&DATA16) { + CPUWORD(o) = DR1.w.l = read_word(AR1.d); + AR1.d += 2; + } + else { + CPULONG(o) = DR1.d = read_dword(AR1.d); + AR1.d += 4; + } } + break; + case L_LXS2: { /* real mode segment base from segment value */ + signed char o = Offs_From_Arg(); + GTRACE1("L_LXS2",o); + DR1.d = read_word(AR1.d); + SetSegReal(DR1.w.l, o); + } + break; + case L_ZXAX: + GTRACE0("L_ZXAX"); + DR1.w.h = 0; + break; + + case L_DI_R1: { + dosaddr_t addr = AR1.d; + GTRACE0("L_DI"); + if (mode & (MBYTE|MBYTX)) { + DR1.b.bl = read_byte(addr); + } + else if (mode & DATA16) { + DR1.w.l = read_word(addr); + } + else { + DR1.d = read_dword(addr); + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } + break; + case S_DI: { + dosaddr_t addr = AR1.d; + GTRACE0("S_DI"); + if (mode&MBYTE) { + write_byte(addr, DR1.b.bl); + } + else if (mode & DATA16) { + write_word(addr, DR1.w.l); + } + else { + write_dword(addr, DR1.d); + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } + break; + + case O_ADD_R: { // OSZAPC + register wkreg v; + v.d = va_arg(ap, int); + RFL.mode = mode; + RFL.valid = V_ADD; + if (mode & IMMED) {GTRACE3("O_ADD_R",0xff,0xff,v.d);} + else {GTRACE3("O_ADD_R",v.bs.bl,0xff,v.d);} + if (mode & MBYTE) { + S1 = DR1.b.bl; + if (mode & IMMED) S2 = v.b.bl; + else S2 = CPUBYTE(v.bs.bl); + DR1.b.bl = RFL.RES.d = S1 + S2; + FlagHandleAdd(S1, S2, RFL.RES.d, 8); + } + else if (mode & DATA16) { + S1 = DR1.w.l; + if (mode & IMMED) S2 = v.w.l; + else S2 = CPUWORD(v.bs.bl); + DR1.w.l = RFL.RES.d = S1 + S2; + FlagHandleAdd(S1, S2, RFL.RES.d, 16); + } + else { + S1 = DR1.d; + if (mode & IMMED) S2 = v.d; + else S2 = CPULONG(v.bs.bl); + DR1.d = RFL.RES.d = S1 + S2; + FlagHandleAdd(S1, S2, RFL.RES.d, 32); + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } + break; + case O_OR_R: { // O=0 SZP C=0 + int v = va_arg(ap, int); + RFL.mode = mode | CLROVF; + RFL.valid = V_GEN; + if (mode & IMMED) {GTRACE3("O_OR_R",0xff,0xff,v);} + else {GTRACE3("O_OR_R",v,0xff,v);} + if (mode & MBYTE) { + if (!(mode & IMMED)) v = CPUBYTE(v); + RFL.RES.d = DR1.b.bl |= v; + } + else if (mode & DATA16) { + if (!(mode & IMMED)) v = CPUWORD(v); + RFL.RES.d = DR1.w.l |= v; + } + else { + if (!(mode & IMMED)) v = CPULONG(v); + RFL.RES.d = DR1.d |= v; + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + SET_CF(0); + } + break; + case O_AND_R: { // O=0 SZP C=0 + int v = va_arg(ap, int); + RFL.mode = mode | CLROVF; + RFL.valid = V_GEN; + if (mode & IMMED) {GTRACE3("O_AND_R",0xff,0xff,v);} + else {GTRACE3("O_AND_R",v,0xff,v);} + if (mode & MBYTE) { + if (!(mode & IMMED)) v = CPUBYTE(v); + RFL.RES.d = DR1.b.bl &= v; + } + else if (mode & DATA16) { + if (!(mode & IMMED)) v = CPUWORD(v); + RFL.RES.d = DR1.w.l &= v; + } + else { + if (!(mode & IMMED)) v = CPULONG(v); + RFL.RES.d = DR1.d &= v; + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + SET_CF(0); + } + break; + case O_XOR_R: { // O=0 SZP C=0 + int v = va_arg(ap, int); + RFL.mode = mode | CLROVF; + RFL.valid = V_GEN; + if (mode & IMMED) {GTRACE3("O_XOR_R",0xff,0xff,v);} + else {GTRACE3("O_XOR_R",v,0xff,v);} + if (mode & MBYTE) { + if (!(mode & IMMED)) v = CPUBYTE(v); + RFL.RES.d = DR1.b.bl ^= v; + } + else if (mode & DATA16) { + if (!(mode & IMMED)) v = CPUWORD(v); + RFL.RES.d = DR1.w.l ^= v; + } + else { + if (!(mode & IMMED)) v = CPULONG(v); + RFL.RES.d = DR1.d ^= v; + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + SET_CF(0); + } + break; + case O_SUB_R: { // OSZAPC + register wkreg v; + v.d = va_arg(ap, int); + RFL.mode = mode; + RFL.valid = V_SUB; + if (mode & IMMED) {GTRACE3("O_SUB_R",0xff,0xff,v.d);} + else {GTRACE3("O_SUB_R",v.bs.bl,0xff,v.d);} + if (mode & MBYTE) { + S1 = DR1.b.bl; + if (mode & IMMED) S2 = v.b.bl; + else S2 = CPUBYTE(v.bs.bl); + DR1.b.bl = RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 8); + } + else if (mode & DATA16) { + S1 = DR1.w.l; + if (mode & IMMED) S2 = v.w.l; + else S2 = CPUWORD(v.bs.bl); + DR1.w.l = RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 16); + } + else { + S1 = DR1.d; + if (mode & IMMED) S2 = v.d; + else S2 = CPULONG(v.bs.bl); + DR1.d = RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 32); + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } + break; + case O_CMP_R: { // OSZAPC + register wkreg v; + v.d = va_arg(ap, int); + RFL.mode = mode; + RFL.valid = V_SUB; + if (mode & IMMED) {GTRACE3("O_CMP_R",0xff,0xff,v.d);} + else {GTRACE3("O_CMP_R",v.bs.bl,0xff,v.d);} + if (mode & MBYTE) { + S1 = DR1.b.bl; + if (mode & IMMED) S2 = v.b.bl; + else S2 = CPUBYTE(v.bs.bl); + RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 8); + } + else if (mode & DATA16) { + S1 = DR1.w.l; + if (mode & IMMED) S2 = v.w.l; + else S2 = CPUWORD(v.bs.bl); + RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 16); + } + else { + S1 = DR1.d; + if (mode & IMMED) S2 = v.d; + else S2 = CPULONG(v.bs.bl); + RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 32); + } + } + break; + case O_ADC_R: { // OSZAPC + register wkreg v; + int cy; + v.d = va_arg(ap, int); + cy = CPUBYTE(Ofs_FLAGS) & 1; + RFL.mode = mode; + RFL.valid = (cy? V_ADC:V_ADD); + if (mode & IMMED) {GTRACE3("O_ADC_R",0xff,0xff,v.d);} + else {GTRACE3("O_ADC_R",v.bs.bl,0xff,v.d);} + if (mode & MBYTE) { + S1 = DR1.b.bl; + if (mode & IMMED) S2 = v.b.bl; + else S2 = CPUBYTE(v.bs.bl); + DR1.b.bl = RFL.RES.d = S1 + S2 + cy; + FlagHandleAdd(S1, S2, RFL.RES.d, 8); + } + else if (mode & DATA16) { + S1 = DR1.w.l; + if (mode & IMMED) S2 = v.w.l; + else S2 = CPUWORD(v.bs.bl); + DR1.w.l = RFL.RES.d = S1 + S2 + cy; + FlagHandleAdd(S1, S2, RFL.RES.d, 16); + } + else { + S1 = DR1.d; + if (mode & IMMED) S2 = v.d; + else S2 = CPULONG(v.bs.bl); + DR1.d = RFL.RES.d = S1 + S2 + cy; + FlagHandleAdd(S1, S2, RFL.RES.d, 32); + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } + break; + case O_SBB_R: { // OSZAPC + register wkreg v; + int cy; + v.d = va_arg(ap, int); + cy = CPUBYTE(Ofs_FLAGS) & 1; + RFL.mode = mode; + RFL.valid = V_SBB; + if (mode & IMMED) {GTRACE3("O_SBB_R",0xff,0xff,v.d);} + else {GTRACE3("O_SBB_R",v.bs.bl,0xff,v.d);} + if (mode & MBYTE) { + S1 = DR1.b.bl; + // movzbl v.b.bl->eax; add cy,eax; neg eax + if (mode & IMMED) S2 = v.b.bl; + else S2 = CPUBYTE(v.bs.bl); + DR1.b.bl = RFL.RES.d = S1 - S2 - cy; + FlagHandleSub(S1, S2, RFL.RES.d, 8); + } + else if (mode & DATA16) { + S1 = DR1.w.l; + if (mode & IMMED) S2 = v.w.l; + else S2 = CPUWORD(v.bs.bl); + DR1.w.l = RFL.RES.d = S1 - S2 - cy; + FlagHandleSub(S1, S2, RFL.RES.d, 16); + } + else { + S1 = DR1.d; + if (mode & IMMED) S2 = v.d; + else S2 = CPULONG(v.bs.bl); + DR1.d = RFL.RES.d = S1 - S2 - cy; + FlagHandleSub(S1, S2, RFL.RES.d, 32); + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } + break; + case O_CLEAR: { // == XOR r,r + signed char o = Offs_From_Arg(); + GTRACE1("O_CLEAR",o); + if (mode & MBYTE) { + CPUBYTE(o) = 0; + } + else if (mode & DATA16) { + CPUWORD(o) = 0; + } + else { + CPULONG(o) = 0; + } + CPUWORD(Ofs_FLAGS) = (CPUWORD(Ofs_FLAGS) & 0x7700) | 0x46; + RFL.valid = V_INVALID; + } + break; + case O_TEST: { // == OR r,r + signed char o = Offs_From_Arg(); + GTRACE1("O_TEST",o); + RFL.mode = mode | CLROVF; + RFL.valid = V_GEN; + if (mode & MBYTE) { + RFL.RES.d = CPUBYTE(o); + } + else if (mode & DATA16) { + RFL.RES.d = CPUWORD(o); + } + else { + RFL.RES.d = CPULONG(o); + } + SET_CF(0); + } + break; + case O_SBSELF: { + signed char o = Offs_From_Arg(); + GTRACE1("O_SBBSELF",o); + // if CY=0 -> reg=0, flag=xx46, OF=0 + // if CY=1 -> reg=-1, flag=xx97, OF=0 + if (CPUBYTE(Ofs_FLAGS)&1) { + RFL.RES.d = 0xffffffff; + CPUWORD(Ofs_FLAGS) = (CPUWORD(Ofs_FLAGS) & 0x7700) | 0x97; + } + else { + RFL.RES.d = 0; + CPUWORD(Ofs_FLAGS) = (CPUWORD(Ofs_FLAGS) & 0x7700) | 0x46; + } + if (mode & MBYTE) { + CPUBYTE(o) = RFL.RES.b.bl; + } + else if (mode & DATA16) { + CPUWORD(o) = RFL.RES.w.l; + } + else { + CPULONG(o) = RFL.RES.d; + } + RFL.valid = V_INVALID; + } + break; + case O_INC_R: { // OSZAP + signed char o = Offs_From_Arg(); + GTRACE1("O_INC_R",o); + RFL.mode = mode; + RFL.valid = V_ADD; + if (mode & MBYTE) { + S1 = CPUBYTE(o); + CPUBYTE(o) = RFL.RES.d = S1 + 1; + FlagHandleIncDec(S1, RFL.RES.d, 8); + } + else if (mode & DATA16) { + S1 = CPUWORD(o); + CPUWORD(o) = RFL.RES.d = S1 + 1; + FlagHandleIncDec(S1, RFL.RES.d, 16); + } + else { + S1 = CPULONG(o); + CPULONG(o) = RFL.RES.d = S1 + 1; + FlagHandleIncDec(S1, RFL.RES.d, 32); + } + } + break; + case O_DEC_R: { // OSZAP + signed char o = Offs_From_Arg(); + GTRACE1("O_DEC_R",o); + RFL.mode = mode; + RFL.valid = V_SUB; + if (mode & MBYTE) { + S1 = CPUBYTE(o); + CPUBYTE(o) = RFL.RES.d = S1 - 1; + FlagHandleIncDec(RFL.RES.d, S1, 8); + } + else if (mode & DATA16) { + S1 = CPUWORD(o); + CPUWORD(o) = RFL.RES.d = S1 - 1; + FlagHandleIncDec(RFL.RES.d, S1, 16); + } + else { + S1 = CPULONG(o); + CPULONG(o) = RFL.RES.d = S1 - 1; + FlagHandleIncDec(RFL.RES.d, S1, 32); + } + } + break; + case O_ADD_FR: { // OSZAPC + register wkreg v; + signed char o = Offs_From_Arg(); + v.d = 0; + if (mode & IMMED) v.d = va_arg(ap,int); + RFL.mode = mode; + RFL.valid = V_ADD; + if (mode & IMMED) {GTRACE3("O_ADD_FR",0xff,0xff,v.d);} + else {GTRACE3("O_ADD_FR",v.bs.bl,0xff,v.d);} + if (mode & MBYTE) { + S1 = CPUBYTE(o); + if (mode & IMMED) S2 = v.b.bl; + else S2 = DR1.b.bl; + CPUBYTE(o) = RFL.RES.d = S1 + S2; + FlagHandleAdd(S1, S2, RFL.RES.d, 8); + } + else if (mode & DATA16) { + S1 = CPUWORD(o); + if (mode & IMMED) S2 = v.w.l; + else S2 = DR1.w.l; + CPUWORD(o) = RFL.RES.d = S1 + S2; + FlagHandleAdd(S1, S2, RFL.RES.d, 16); + } + else { + S1 = CPULONG(o); + if (mode & IMMED) S2 = v.d; + else S2 = DR1.d; + CPULONG(o) = RFL.RES.d = S1 + S2; + FlagHandleAdd(S1, S2, RFL.RES.d, 32); + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } + break; + case O_OR_FR: { // O=0 SZP C=0 + register wkreg v; + signed char o = Offs_From_Arg(); + v.d = 0; + if (mode & IMMED) v.d = va_arg(ap,int); + RFL.mode = mode | CLROVF; + RFL.valid = V_GEN; + if (mode & IMMED) {GTRACE3("O_OR_FR",0xff,0xff,v.d);} + else {GTRACE3("O_OR_FR",v.d,0xff,v.d);} + if (mode & MBYTE) { + RFL.RES.d = CPUBYTE(o) |= (mode & IMMED ? v.b.bl : DR1.b.bl); + } + else if (mode & DATA16) { + RFL.RES.d = CPUWORD(o) |= (mode & IMMED ? v.w.l : DR1.w.l); + } + else { + RFL.RES.d = CPULONG(o) |= (mode & IMMED ? v.d : DR1.d); + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + SET_CF(0); + } + break; + case O_ADC_FR: { // OSZAPC + register wkreg v; + signed char o = Offs_From_Arg(); + int cy; + v.d = 0; + if (mode & IMMED) v.d = va_arg(ap,int); + cy = CPUBYTE(Ofs_FLAGS) & 1; + RFL.mode = mode; + RFL.valid = (cy? V_ADC:V_ADD); + if (mode & IMMED) {GTRACE3("O_ADC_FR",0xff,0xff,v.d);} + else {GTRACE3("O_ADC_FR",v.bs.bl,0xff,v.d);} + if (mode & MBYTE) { + S1 = CPUBYTE(o); + if (mode & IMMED) S2 = v.b.bl; + else S2 = DR1.b.bl; + CPUBYTE(o) = RFL.RES.d = S1 + S2 + cy; + FlagHandleAdd(S1, S2, RFL.RES.d, 8); + } + else if (mode & DATA16) { + S1 = CPUWORD(o); + if (mode & IMMED) S2 = v.w.l; + else S2 = DR1.w.l; + CPUWORD(o) = RFL.RES.d = S1 + S2 + cy; + FlagHandleAdd(S1, S2, RFL.RES.d, 16); + } + else { + S1 = CPULONG(o); + if (mode & IMMED) S2 = v.d; + else S2 = DR1.d; + CPULONG(o) = RFL.RES.d = S1 + S2 + cy; + FlagHandleAdd(S1, S2, RFL.RES.d, 32); + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } + break; + case O_SBB_FR: { // OSZAPC + register wkreg v; + signed char o = Offs_From_Arg(); + int cy; + v.d = 0; + if (mode & IMMED) v.d = va_arg(ap,int); + cy = CPUBYTE(Ofs_FLAGS) & 1; + RFL.mode = mode; + RFL.valid = V_SBB; + if (mode & IMMED) {GTRACE3("O_SBB_FR",0xff,0xff,v.d);} + else {GTRACE3("O_SBB_FR",v.bs.bl,0xff,v.d);} + if (mode & MBYTE) { + S1 = CPUBYTE(o); + // movzbl v.bs.bl->eax; add cy,eax; neg eax + if (mode & IMMED) S2 = v.b.bl; + else S2 = DR1.b.bl; + CPUBYTE(o) = RFL.RES.d = S1 - S2 - cy; + FlagHandleSub(S1, S2, RFL.RES.d, 8); + } + else if (mode & DATA16) { + S1 = CPUWORD(o); + if (mode & IMMED) S2 = v.w.l; + else S2 = DR1.w.l; + CPUWORD(o) = RFL.RES.d = S1 - S2 - cy; + FlagHandleSub(S1, S2, RFL.RES.d, 16); + } + else { + S1 = CPULONG(o); + if (mode & IMMED) S2 = v.d; + else S2 = DR1.d; + CPULONG(o) = RFL.RES.d = S1 - S2 - cy; + FlagHandleSub(S1, S2, RFL.RES.d, 32); + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } + break; + case O_AND_FR: { // O=0 SZP C=0 + register wkreg v; + signed char o = Offs_From_Arg(); + v.d = 0; + if (mode & IMMED) v.d = va_arg(ap,int); + RFL.mode = mode | CLROVF; + RFL.valid = V_GEN; + if (mode & IMMED) {GTRACE3("O_AND_FR",0xff,0xff,v.d);} + else {GTRACE3("O_AND_FR",v.d,0xff,v.d);} + if (mode & MBYTE) { + RFL.RES.d = CPUBYTE(o) &= (mode & IMMED ? v.b.bl : DR1.b.bl); + } + else if (mode & DATA16) { + RFL.RES.d = CPUWORD(o) &= (mode & IMMED ? v.w.l : DR1.w.l); + } + else { + RFL.RES.d = CPULONG(o) &= (mode & IMMED ? v.d : DR1.d); + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + SET_CF(0); + } + break; + case O_SUB_FR: { // OSZAPC + register wkreg v; + signed char o = Offs_From_Arg(); + v.d = 0; + if (mode & IMMED) v.d = va_arg(ap,int); + RFL.mode = mode; + RFL.valid = V_SUB; + if (mode & IMMED) {GTRACE3("O_SUB_FR",0xff,0xff,v.d);} + else {GTRACE3("O_SUB_FR",v.bs.bl,0xff,v.d);} + if (mode & MBYTE) { + S1 = CPUBYTE(o); + if (mode & IMMED) S2 = v.b.bl; + else S2 = DR1.b.bl; + CPUBYTE(o) = RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 8); + } + else if (mode & DATA16) { + S1 = CPUWORD(o); + if (mode & IMMED) S2 = v.w.l; + else S2 = DR1.w.l; + CPUWORD(o) = RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 16); + } + else { + S1 = CPULONG(o); + if (mode & IMMED) S2 = v.d; + else S2 = DR1.d; + CPULONG(o) = RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d,32); + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } + break; + case O_XOR_FR: { // O=0 SZP C=0 + register wkreg v; + signed char o = Offs_From_Arg(); + v.d = 0; + if (mode & IMMED) v.d = va_arg(ap,int); + RFL.mode = mode | CLROVF; + RFL.valid = V_GEN; + if (mode & IMMED) {GTRACE3("O_XOR_FR",0xff,0xff,v.d);} + else {GTRACE3("O_XOR_FR",v.d,0xff,v.d);} + if (mode & MBYTE) { + RFL.RES.d = CPUBYTE(o) ^= (mode & IMMED ? v.b.bl : DR1.b.bl); + } + else if (mode & DATA16) { + RFL.RES.d = CPUWORD(o) ^= (mode & IMMED ? v.w.l : DR1.w.l); + } + else { + RFL.RES.d = CPULONG(o) ^= (mode & IMMED ? v.d : DR1.d); + } + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + SET_CF(0); + } + break; + case O_CMP_FR: { // OSZAPC + register wkreg v; + signed char o = Offs_From_Arg(); + v.d = 0; + if (mode & IMMED) v.d = va_arg(ap,int); + RFL.mode = mode; + RFL.valid = V_SUB; + if (mode & IMMED) {GTRACE3("O_CMP_FR",0xff,0xff,v.d);} + else {GTRACE3("O_CMP_FR",v.bs.bl,0xff,v.d);} + if (mode & MBYTE) { + S1 = CPUBYTE(o); + if (mode & IMMED) S2 = v.b.bl; + else S2 = DR1.b.bl; + RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 8); + } + else if (mode & DATA16) { + S1 = CPUWORD(o); + if (mode & IMMED) S2 = v.w.l; + else S2 = DR1.w.l; + RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 16); + } + else { + S1 = CPULONG(o); + if (mode & IMMED) S2 = v.d; + else S2 = DR1.d; + RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 32); + } + } + break; + case O_NOT: // no flags + GTRACE0("O_NOT"); + if (mode & MBYTE) { + DR1.b.bl = ~DR1.b.bl; + } + else if (mode & DATA16) { + DR1.w.l = ~DR1.w.l; + } + else { + DR1.d = ~DR1.d; + } + break; + case O_NEG: // OSZAPC + GTRACE0("O_NEG"); + RFL.mode = mode; + RFL.valid = V_SUB; + if (mode & MBYTE) { + DR1.b.bl = RFL.RES.d = -(S2 = DR1.b.bl); + FlagHandleSub(0, S2, RFL.RES.d, 8); + } + else if (mode & DATA16) { + DR1.w.l = RFL.RES.d = -(S2 = DR1.w.l); + FlagHandleSub(0, S2, RFL.RES.d, 16); + } + else { + DR1.d = RFL.RES.d = -(S2 = DR1.d); + FlagHandleSub(0, S2, RFL.RES.d, 32); + } + break; + case O_INC: // OSZAP + GTRACE0("O_INC"); + RFL.mode = mode; + RFL.valid = V_ADD; + if (mode & MBYTE) { + S1 = DR1.bs.bl; + DR1.b.bl = RFL.RES.d = S1 + 1; + FlagHandleIncDec(S1, RFL.RES.d, 8); + } + else if (mode & DATA16) { + S1 = DR1.ws.l; + DR1.w.l = RFL.RES.d = S1 + 1; + FlagHandleIncDec(S1, RFL.RES.d, 16); + } + else { + S1 = DR1.d; + DR1.d = RFL.RES.d = S1 + 1; + FlagHandleIncDec(S1, RFL.RES.d, 32); + } + break; + case O_DEC: // OSZAP + GTRACE0("O_DEC"); + RFL.mode = mode; + RFL.valid = V_SUB; + if (mode & MBYTE) { + S1 = DR1.bs.bl; + DR1.b.bl = RFL.RES.d = S1 - 1; + FlagHandleIncDec(RFL.RES.d, S1, 8); + } + else if (mode & DATA16) { + S1 = DR1.ws.l; + DR1.w.l = RFL.RES.d = S1 - 1; + FlagHandleIncDec(RFL.RES.d, S1, 16); + } + else { + S1 = DR1.d; + DR1.d = RFL.RES.d = S1 - 1; + FlagHandleIncDec(RFL.RES.d, S1, 32); + } + break; + case O_CMPXCHG: { // OSZAPC + signed char o = Offs_From_Arg(); + RFL.mode = mode; + RFL.valid = V_SUB; + GTRACE1("O_CMPXCHG",o); + if (mode & MBYTE) { + S1 = CPUBYTE(Ofs_AL); + S2 = DR1.b.bl; + RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 8); + if (RFL.RES.d == 0) + DR1.b.bl = CPUBYTE(o); + else + CPUBYTE(Ofs_AL) = DR1.b.bl; + } + else if (mode & DATA16) { + S1 = CPUWORD(Ofs_AX); + S2 = DR1.w.l; + RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 16); + if (RFL.RES.d == 0) + DR1.w.l = CPUWORD(o); + else + CPUWORD(Ofs_AX) = DR1.w.l; + } + else { + S1 = CPULONG(Ofs_EAX); + S2 = DR1.d; + RFL.RES.d = S1 - S2; + FlagHandleSub(S1, S2, RFL.RES.d, 32); + if (RFL.RES.d == 0) + DR1.d = CPULONG(o); + else + CPULONG(Ofs_EAX) = DR1.d; + } + } + break; + case O_XCHG: { + signed char o = Offs_From_Arg(); + GTRACE1("O_XCHG",o); + if (mode & MBYTE) { + DR2.b.bl = DR1.b.bl; + DR1.b.bl = CPUBYTE(o); + CPUBYTE(o) = DR2.b.bl; + } + else if (mode & DATA16) { + DR2.w.l = DR1.w.l; + DR1.w.l = CPUWORD(o); + CPUWORD(o) = DR2.w.l; + } + else { + DR2.d = DR1.d; + DR1.d = CPULONG(o); + CPULONG(o) = DR2.d; + } } + break; + case O_XCHG_R: { + signed char o1 = Offs_From_Arg(); + signed char o2 = Offs_From_Arg(); + GTRACE2("O_XCHG_R",o1,o2); + if (mode & MBYTE) { + DR1.b.bl = CPUBYTE(o1); + CPUBYTE(o1) = CPUBYTE(o2); + CPUBYTE(o2) = DR1.b.bl; + } + else if (mode & DATA16) { + DR1.w.l = CPUWORD(o1); + CPUWORD(o1) = CPUWORD(o2); + CPUWORD(o2) = DR1.w.l; + } + else { + DR1.d = CPULONG(o1); + CPULONG(o1) = CPULONG(o2); + CPULONG(o2) = DR1.d; + } } + break; + + case O_MUL: { // OC + int of; + GTRACE0("O_MUL"); + RFL.mode = mode; + RFL.valid = V_GEN; + if (mode & MBYTE) { + DR1.w.l = + (unsigned int)CPUBYTE(Ofs_AL) * (unsigned int)DR1.b.bl; + CPUWORD(Ofs_AX) = DR1.w.l; + of = (DR1.b.bh != 0); + } + else if (mode&DATA16) { + DR1.d = + (unsigned int)CPUWORD(Ofs_AX) * (unsigned int)DR1.w.l; + CPUWORD(Ofs_AX) = DR1.w.l; + CPUWORD(Ofs_DX) = DR1.w.h; + of = (DR1.w.h != 0); + } + else { + u_int64_u v; + v.td = (u_int64_t)CPULONG(Ofs_EAX) * (u_int64_t)DR1.d; + CPULONG(Ofs_EAX) = v.t.tl; + CPULONG(Ofs_EDX) = v.t.th; + of = (v.t.th != 0); + } + if (of) { + SET_CF(1); + RFL.mode |= SETOVF; + } + else { + SET_CF(0); + RFL.mode |= CLROVF; + } } + break; + case O_IMUL: { // OC + int of; + RFL.mode = mode; + RFL.valid = V_GEN; + if (mode & MBYTE) { + if ((mode&(IMMED|DATA16))==(IMMED|DATA16)) { + int b = va_arg(ap, int); + signed char o = Offs_From_Arg(); + GTRACE3("O_IMUL",o,0xff,b); + DR1.ds = (int)DR1.ws.l * b; + CPUWORD(o) = DR1.w.l; + DR1.ds >>= 15; + of = ((DR1.ds!=0) && (DR1.ds!=-1)); + } + else if ((mode&(IMMED|DATA16))==IMMED) { + int b = va_arg(ap, int); + signed char o = Offs_From_Arg(); + int64_t v; + GTRACE3("O_IMUL",o,0xff,b); + v = (int64_t)DR1.ds * b; + CPULONG(o) = v & 0xffffffff; + v >>= 31; + of = ((v != 0) && (v != -1)); + } + else { + GTRACE0("O_IMUL"); + DR1.ds = + (int)(signed char)CPUBYTE(Ofs_AL) * (int)DR1.bs.bl; + CPUWORD(Ofs_AX) = DR1.w.l; + DR1.ds >>= 7; // AX sign extend of AL? + of = ((DR1.ds!=0) && (DR1.ds!=-1)); + } + } + else if (mode&DATA16) { + if (mode&IMMED) { + int b = va_arg(ap, int); + signed char o = Offs_From_Arg(); + GTRACE3("O_IMUL",o,0xff,b); + DR1.ds = (int)DR1.ws.l * b; + CPUWORD(o) = DR1.w.l; + } + else if (mode&MEMADR) { + signed char o = Offs_From_Arg(); + GTRACE1("O_IMUL",o); + DR1.ds = (int)(signed short)CPUWORD(o) * (int)DR1.ws.l; + CPUWORD(o) = DR1.w.l; + } + else { + GTRACE0("O_IMUL"); + DR1.ds = + (int)(signed short)CPUWORD(Ofs_AX) * (int)DR1.ws.l; + CPUWORD(Ofs_AX) = DR1.w.l; + CPUWORD(Ofs_DX) = DR1.w.h; + } + DR1.ds >>= 15; // DX:AX sign extend of AX? + of = ((DR1.ds!=0) && (DR1.ds!=-1)); + } + else { + int64_t v; + if (mode&IMMED) { + int b = va_arg(ap, int); + signed char o = Offs_From_Arg(); + GTRACE3("O_IMUL",o,0xff,b); + v = (int64_t)DR1.ds * b; + CPULONG(o) = v & 0xffffffff; + } + else if (mode&MEMADR) { + signed char o = Offs_From_Arg(); + int vd = CPULONG(o); + GTRACE1("O_IMUL",o); + v = (int64_t)DR1.ds * (int64_t)vd; + CPULONG(o) = v & 0xffffffff; + } + else { + int vd = CPULONG(Ofs_EAX); + GTRACE0("O_IMUL"); + v = (int64_t)vd * (int64_t)DR1.ds; + CPULONG(Ofs_EAX) = v & 0xffffffff; + CPULONG(Ofs_EDX) = (v >> 32) & 0xffffffff; + } + v >>= 31; // EDX:EAX sign extend of EAX? + of = ((v != 0) && (v != -1)); + } + if (of) { + SET_CF(1); + RFL.mode |= SETOVF; + } + else { + SET_CF(0); + RFL.mode |= CLROVF; + } } + break; + + case O_DIV: // no flags + GTRACE0("O_DIV"); + if (mode & MBYTE) { + RFL.RES.w.l = CPUWORD(Ofs_AX); + RFL.RES.w.h = 0; + S1 = DR1.b.bl; + if (S1==0) + TheCPU.err = EXCP00_DIVZ; + else { + unsigned v = RFL.RES.d / S1; + if (v > 0xff) + TheCPU.err = EXCP00_DIVZ; + else { + CPUBYTE(Ofs_AL) = v; + CPUBYTE(Ofs_AH) = RFL.RES.d % S1; + } + } + } + else { + if (mode&DATA16) { + RFL.RES.w.l = CPUWORD(Ofs_AX); + RFL.RES.w.h = CPUWORD(Ofs_DX); + S1 = DR1.w.l; + if (S1==0) + TheCPU.err = EXCP00_DIVZ; + else { + unsigned v = RFL.RES.d / S1; + if (v > 0xffff) + TheCPU.err = EXCP00_DIVZ; + else { + CPUWORD(Ofs_AX) = v; + CPUWORD(Ofs_DX) = RFL.RES.d % S1; + } + } + } + else { + u_int64_u v; + unsigned long rem; + v.t.tl = CPULONG(Ofs_EAX); + v.t.th = CPULONG(Ofs_EDX); + S1 = DR1.d; + if (S1==0) + TheCPU.err = EXCP00_DIVZ; + else { + rem = v.td % S1; + v.td /= S1; + if (v.t.th) + TheCPU.err = EXCP00_DIVZ; + else { + CPULONG(Ofs_EAX) = RFL.RES.d = v.t.tl; + CPULONG(Ofs_EDX) = rem; + } + } + } + } + break; + case O_IDIV: // no flags + GTRACE0("O_IDIV"); + if (mode & MBYTE) { + int32_t S = DR1.bs.bl; + RFL.RES.ds = (signed short)CPUWORD(Ofs_AX); + if (S==0) + TheCPU.err = EXCP00_DIVZ; + else { + int v = RFL.RES.ds / S; + if (v > 127 || v < -128) + TheCPU.err = EXCP00_DIVZ; + else { + CPUBYTE(Ofs_AL) = v; + CPUBYTE(Ofs_AH) = RFL.RES.ds % S; + } + } + } + else { + if (mode&DATA16) { + int32_t S = DR1.ws.l; + RFL.RES.w.l = CPUWORD(Ofs_AX); + RFL.RES.w.h = CPUWORD(Ofs_DX); + S = DR1.ws.l; + if (S==0) + TheCPU.err = EXCP00_DIVZ; + else { + int v = RFL.RES.ds / S; + if (v > 32767 || v < -32768) + TheCPU.err = EXCP00_DIVZ; + else { + CPUWORD(Ofs_AX) = v; + CPUWORD(Ofs_DX) = RFL.RES.ds % S; + } + } + } + else { + int64_t v; + long rem; + int32_t S = DR1.d; + v = CPULONG(Ofs_EAX) | + ((uint64_t)CPULONG(Ofs_EDX) << 32); + if (S==0) + TheCPU.err = EXCP00_DIVZ; + else { + rem = v % S; + v /= S; + if (v > 0x7fffffffLL || v < -0x80000000LL) + TheCPU.err = EXCP00_DIVZ; + else { + CPULONG(Ofs_EAX) = RFL.RES.d = v & 0xffffffff; + CPULONG(Ofs_EDX) = rem; + } + } + } + } + break; + case O_CBWD: + GTRACE0("O_CBWD"); + DR1.d = CPULONG(Ofs_EAX); + if (mode & MBYTE) { /* 0x98: CBW,CWDE */ + if (mode & DATA16) { // AL->AX + // cbw + CPUBYTE(Ofs_AH) = (DR1.b.bl&0x80? 0xff:0); + } + else { // AX->EAX + // cwde + CPUWORD(Ofs_AXH) = (DR1.b.bh&0x80? 0xffff:0); + } + } + else if (mode & DATA16) { /* 0x99: AX->DX:AX */ + // cwd + CPUWORD(Ofs_DX) = (DR1.b.bh&0x80? 0xffff:0); + } + else { /* 0x99: EAX->EDX:EAX */ + // cdq + CPULONG(Ofs_EDX) = (DR1.b.b3&0x80? 0xffffffff:0); + } + break; + case O_XLAT: + GTRACE0("XLAT"); + AR1.d = CPULONG(OVERR_DS); + TR1.d = CPULONG(Ofs_EBX) + CPUBYTE(Ofs_AL); + if (mode & ADDR16) { + TR1.d &= 0xFFFF; + } + AR1.d += TR1.d; + break; + + case O_ROL: { // O(if sh==1),C(if sh>0) + signed char o = Offs_From_Arg(); + unsigned int sh, rbef, raft, cy, ov; + GTRACE1("O_ROL",o); + if (mode & IMMED) sh = o; + else sh = CPUBYTE(Ofs_CL); + sh &= 31; + if(!sh) + break; // Carry unmodified, Overflow undefined. + + if (mode & MBYTE) { + sh &= 7; + rbef = DR1.b.bl; + raft = (rbef<>(8-sh)); + DR1.b.bl = raft; + cy = raft & 1; + ov = (rbef & 0x80) != (raft & 0x80); + } + else if (mode & DATA16) { + sh &= 15; + rbef = DR1.w.l; + raft = (rbef<>(16-sh)); + DR1.w.l = raft; + cy = raft & 1; + ov = (rbef & 0x8000) != (raft & 0x8000); + } + else { + // sh != 0, else we "break"ed above. + // sh < 32, so 32-sh is in range 1..31, which is OK. + rbef = DR1.d; + raft = (rbef<>(32-sh)); + DR1.d = raft; + cy = raft & 1; + ov = (rbef & 0x80000000U) != (raft & 0x80000000U); + } + if (debug_level('e')>1) dbug_printf("Sync C flag = %d\n", cy); + SET_CF(cy); + if (sh>1) + RFL.mode |= IGNOVF; + else + SET_OF(ov); + if (debug_level('e')>3) dbug_printf("(V) %08x\n",raft); + } + break; + case O_RCL: { // O(if sh==1),C(if sh>0) + signed char o = Offs_From_Arg(); + unsigned int sh, rbef, raft, cy, ov; + GTRACE1("O_RCL",o); + cy = CPUBYTE(Ofs_FLAGS) & 1; + if (mode & IMMED) sh = o; + else sh = CPUBYTE(Ofs_CL); + sh &= 31; + + if (mode & MBYTE) { + sh %= 9; + if (!sh) break; + rbef = DR1.b.bl; + raft = (rbef<>(9-sh)) | (cy<<(sh-1)); + DR1.b.bl = raft; + cy = (rbef>>(8-sh)) & 1; + ov = (rbef & 0x80) != (raft & 0x80); + } + else if (mode & DATA16) { + sh %= 17; + if (!sh) break; + rbef = DR1.w.l; + raft = (rbef<>(17-sh)) | (cy<<(sh-1)); + DR1.w.l = raft; + cy = (rbef>>(16-sh)) & 1; + ov = (rbef & 0x8000) != (raft & 0x8000); + } + else { + if (!sh) break; + rbef = DR1.d; + raft = (rbef<1) raft |= (rbef>>(33-sh)); + DR1.d = raft; + if(sh) + cy = (rbef>>(32-sh)) & 1; + ov = (rbef & 0x80000000U) != (raft & 0x80000000U); + } + if (debug_level('e')>1) dbug_printf("Sync C flag = %d\n", cy); + SET_CF(cy); + if (sh>1) + RFL.mode |= IGNOVF; + else + SET_OF(ov); + if (debug_level('e')>3) dbug_printf("(V) %08x\n",raft); + } + break; + case O_SHL: { // O(if sh==1),SZPC(if sh>0) + signed char o = Offs_From_Arg(); + unsigned int sh, rbef, raft=0, cy=0; + GTRACE3("O_SHL",0xff,0xff,o); + if (mode & IMMED) sh = o; + else sh = CPUBYTE(Ofs_CL); + sh &= 31; // happens on 286 and above, not on 8086 + if(!sh) + // All flags unchanged (at least on PIII) + break; + + RFL.mode = mode; + RFL.valid = V_GEN; + if (mode & MBYTE) + rbef = DR1.b.bl; + else if (mode & DATA16) + rbef = DR1.w.l; + else + rbef = DR1.d; + + // To simulate overflow flag. Keep in mind it is only defined + // for sh==1. In that case, SHL r/m,1 behaves like ADD r/m,r/m. + // So flag state gets set up like that + raft = (rbef << sh); + RFL.RES.d = raft; + + if (mode & MBYTE) { + cy = (raft & 0x100) != 0; + DR1.b.bl = raft; + RFL.cout = rbef << 24; + } + else if (mode & DATA16) { + cy = (raft & 0x10000) != 0; + DR1.w.l = raft; + RFL.cout = rbef << 16; + } + else { + cy = (rbef>>(32-sh)) & 1; + DR1.d = raft; + RFL.cout = rbef & 0xc0000000; + } + RFL.RES.d = raft; + if (debug_level('e')>1) dbug_printf("Sync C flag = %d\n", cy); + SET_CF(cy); + if (sh>1) RFL.mode |= IGNOVF; + if (debug_level('e')>3) dbug_printf("(V) %08x\n",raft); + } + break; + case O_ROR: { // O(if sh==1),C(if sh>0) + signed char o = Offs_From_Arg(); + unsigned int sh, rbef, raft, cy, ov; + GTRACE1("O_ROR",o); + if (mode & IMMED) sh = o; + else sh = CPUBYTE(Ofs_CL); + sh &= 31; + if(!sh) + break; // Carry unmodified, Overflow undefined. + + if (mode & MBYTE) { + sh &= 7; + rbef = DR1.b.bl; + raft = (rbef>>sh) | (rbef<<(8-sh)); + DR1.b.bl = raft; + cy = (raft & 0x80) != 0; + ov = (rbef & 0x80) != (raft & 0x80); + } + else if (mode & DATA16) { + sh &= 15; + rbef = DR1.w.l; + raft = (rbef>>sh) | (rbef<<(16-sh)); + DR1.w.l = raft; + cy = (raft & 0x8000) != 0; + ov = (rbef & 0x8000) != (raft & 0x8000); + } + else { + // sh != 0, else we "break"ed above. + // sh < 32, so 32-sh is in range 1..31, which is OK. + rbef = DR1.d; + raft = (rbef>>sh) | (rbef<<(32-sh)); + DR1.d = raft; + cy = (raft & 0x80000000U) != 0; + ov = (rbef & 0x80000000U) != (raft & 0x80000000U); + } + if (debug_level('e')>1) dbug_printf("Sync C flag = %d\n", cy); + SET_CF(cy); + if (sh>1) + RFL.mode |= IGNOVF; + else + SET_OF(ov); + if (debug_level('e')>3) dbug_printf("(V) %08x\n",raft); + } + break; + case O_RCR: { // O(if sh==1),C(if sh>0) + signed char o = Offs_From_Arg(); + unsigned int sh, rbef, raft, cy, ov; + GTRACE3("O_RCR",0xff,0xff,o); + cy = CPUBYTE(Ofs_FLAGS) & 1; + if (mode & IMMED) sh = o; + else sh = CPUBYTE(Ofs_CL); + sh &= 31; + if(!sh) + break; // Carry unmodified, Overflow undefined. + + if (mode & MBYTE) { + sh %= 9; + rbef = DR1.b.bl; + raft = (rbef>>sh) | (rbef<<(9-sh)) | (cy<<(8-sh)); + DR1.b.bl = raft; + if(sh) + cy = (rbef>>(sh-1)) & 1; + // else keep carry. + ov = (rbef & 0x80) != (raft & 0x80); + } + else if (mode & DATA16) { + sh %= 17; + rbef = DR1.w.l; + raft = (rbef>>sh) | (rbef<<(17-sh)) | (cy<<(16-sh)); + DR1.w.l = raft; + if(sh) + cy = (rbef>>(sh-1)) & 1; + ov = (rbef & 0x8000) != (raft & 0x8000); + } + else { + rbef = DR1.d; + raft = (rbef>>sh) | (cy<<(32-sh)); + if (sh>1) raft |= (rbef<<(33-sh)); + DR1.d = raft; + cy = (rbef>>(sh-1)) & 1; + ov = (rbef & 0x80000000U) != (raft & 0x80000000U); + } + if (debug_level('e')>1) dbug_printf("Sync C flag = %d\n", cy); + SET_CF(cy); + if (sh>1) + RFL.mode |= IGNOVF; + else + SET_OF(ov); + if (debug_level('e')>3) dbug_printf("(V) %08x\n",raft); + } + break; + case O_SHR: { // O(if sh==1),SZPC(if sh>0) + signed char o = Offs_From_Arg(); + unsigned int sh, rbef, raft, cy=0; + GTRACE3("O_SHR",0xff,0xff,o); + if (mode & IMMED) sh = o; + else sh = CPUBYTE(Ofs_CL); + sh &= 31; + if (!sh) + { + ClearOF(); + break; // shift count 0, flags unchanged, except OVFL + } + + RFL.mode = mode; + RFL.valid = V_GEN; + if (mode & MBYTE) + rbef = DR1.b.bl; + else if (mode & DATA16) + rbef = DR1.w.l; + else + rbef = DR1.d; + + cy = (rbef >> (sh-1)) & 1; + raft = rbef >> sh; + RFL.RES.d = raft; + + if (mode & MBYTE) { + DR1.b.bl = raft; + RFL.cout = raft << 24; + } + else if (mode & DATA16) { + DR1.w.l = raft; + RFL.cout = raft << 16; + } + else { + DR1.d = raft; + RFL.cout = raft & 0xc0000000; + } + if (debug_level('e')>1) dbug_printf("Sync C flag = %d\n", cy); + SET_CF(cy); + if (sh>1) RFL.mode |= CLROVF; + if (debug_level('e')>3) dbug_printf("(V) %08x\n",raft); + } + break; + case O_SAR: { // O(if sh==1),SZPC(if sh>0) + signed char o = Offs_From_Arg(); + unsigned int sh, cy=0; + signed int rbef, raft=0; + GTRACE3("O_SHR",0xff,0xff,o); + if (mode & IMMED) sh = o; + else sh = CPUBYTE(Ofs_CL); + sh &= 31; + if (!sh) + { + ClearOF(); + break; // shift count 0, flags unchanged, except OVFL + } + + RFL.mode = mode; + RFL.valid = V_GEN; + if (mode & MBYTE) + rbef = DR1.bs.bl; + else if (mode & DATA16) + rbef = DR1.ws.l; + else + rbef = DR1.ds; + + cy = (rbef >> (sh-1)) & 1; + raft = rbef >> sh; + RFL.RES.d = raft; + + if (mode & MBYTE) + DR1.bs.bl = raft; + else if (mode & DATA16) + DR1.ws.l = raft; + else + DR1.ds = raft; + + if (debug_level('e')>1) dbug_printf("Sync C flag = %d\n", cy); + SET_CF(cy); + RFL.cout = 0; /* clears overflow & auxiliary carry flag */ + if (debug_level('e')>3) dbug_printf("(V) %08x\n",raft); + } + break; + + case O_OPAX: { /* used by DAA..AAD */ + int n = va_arg(ap,int); + // get n bytes from parameter stack + unsigned char subop = Offs_From_Arg(); + GTRACE3("O_OPAX",0xff,0xff,n); + RFL.mode = mode; + /* sync AF *before* changing RFL.valid */ + if (subop != AAM && subop != AAD) + FlagSync_AP(); + RFL.valid = V_ADD; + RFL.cout = 0; /* clears overflow & auxiliary carry flag */ + DR1.d = CPULONG(Ofs_EAX); + switch (subop) { + case DAA: { + char cyaf = 0; + unsigned char altmp = DR1.b.bl; + if (((DR1.b.bl & 0x0f) > 9 ) || (IS_AF_SET)) { + DR1.b.bl += 6; + cyaf = ((CPUBYTE(Ofs_FLAGS)&1) || + (altmp > 0xf9)) | 8; + } + if ((altmp > 0x99) || (IS_CF_SET)) { + DR1.b.bl += 0x60; + cyaf |= 1; + } + SET_CF(cyaf & 1); + RFL.RES.d = DR1.bs.bl; /* for flags */ + RFL.cout = (RFL.cout & ~8) | (cyaf & 8); + } + break; + case DAS: { + char cyaf = 0; + unsigned char altmp = DR1.b.bl; + if (((altmp & 0x0f) > 9) || (IS_AF_SET)) { + DR1.b.bl -= 6; + cyaf = ((CPUBYTE(Ofs_FLAGS)&1) || + (altmp < 6)) | 8; + } + if ((altmp > 0x99) || (IS_CF_SET)) { + DR1.b.bl -= 0x60; + cyaf |= 1; + } + SET_CF(cyaf & 1); + RFL.RES.d = DR1.bs.bl; /* for flags */ + RFL.cout = (RFL.cout & ~8) | (cyaf & 8); + } + break; + case AAA: { + char cyaf; + char icarry = (DR1.b.bl > 0xf9); + if (((DR1.b.bl & 0x0f) > 9 ) || (IS_AF_SET)) { + DR1.b.bl = (DR1.b.bl + 6) & 0x0f; + DR1.b.bh = (DR1.b.bh + 1 + icarry); + cyaf = 9; + } else { + cyaf = 0; + DR1.b.bl &= 0x0f; + } + SET_CF(cyaf & 1); + RFL.cout = (RFL.cout & ~8) | (cyaf & 8); + } + break; + case AAS: { + char cyaf; + char icarry = (DR1.b.bl < 6); + if (((DR1.b.bl & 0x0f) > 9 ) || (IS_AF_SET)) { + DR1.b.bl = (DR1.b.bl - 6) & 0x0f; + DR1.b.bh = (DR1.b.bh - 1 - icarry); + cyaf = 9; + } else { + cyaf = 0; + DR1.b.bl &= 0x0f; + } + SET_CF(cyaf & 1); + RFL.cout = (RFL.cout & ~8) | (cyaf & 8); + } + break; + case AAM: { + signed char tmp = DR1.b.bl; + int base = Offs_From_Arg(); + DR1.b.bh = tmp / base; + RFL.RES.d = DR1.bs.bl = tmp % base; + /* clear AF (undefined: found experimentally) */ + S2 = RFL.RES.d; + } + break; + case AAD: { + int base = Offs_From_Arg(); + DR1.w.l = ((DR1.b.bh * base) + DR1.b.bl) & 0xff; + RFL.RES.d = DR1.bs.bl; /* for flags */ + /* clear AF (undefined: found experimentally) */ + S2 = RFL.RES.d; + } + break; + } + CPULONG(Ofs_EAX) = DR1.d; + } + break; + + case O_PUSH: { + unsigned long stackm = CPULONG(Ofs_STACKM); + GTRACE0("O_PUSH"); + if (mode & DATA16) { + AR2.d = CPULONG(Ofs_XSS); + SR1.d = CPULONG(Ofs_ESP) - 2; + SR1.d &= stackm; + write_word(AR2.d + SR1.d, DR1.w.l); + } + else { + AR2.d = CPULONG(Ofs_XSS); + SR1.d = CPULONG(Ofs_ESP) - 4; + SR1.d &= stackm; + write_dword(AR2.d + SR1.d, DR1.d); + } +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + SR1.d |= (CPULONG(Ofs_ESP) & ~stackm); +#endif + CPULONG(Ofs_ESP) = SR1.d; + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } break; + +/* PUSH derived (sub-)sequences: */ + case O_PUSH1: + GTRACE0("O_PUSH1"); + AR2.d = CPULONG(Ofs_XSS); + SR1.d = CPULONG(Ofs_ESP); + break; + + case O_PUSH2: { + signed char o = Offs_From_Arg(); + unsigned long stackm = CPULONG(Ofs_STACKM); + GTRACE1("O_PUSH2",o); + if (mode & DATA16) { + DR1.w.l = CPUWORD(o); + SR1.d -= 2; + SR1.d &= stackm; + write_word(AR2.d + SR1.d, DR1.w.l); + } + else { + DR1.d = CPULONG(o); + SR1.d -= 4; + SR1.d &= stackm; + write_dword(AR2.d + SR1.d, DR1.d); + } +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + SR1.d |= (CPULONG(Ofs_ESP) & ~stackm); +#endif + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } break; + + case O_PUSH3: + GTRACE0("O_PUSH3"); + CPULONG(Ofs_ESP) = SR1.d; + break; + + case O_PUSH2F: { + unsigned long stackm = CPULONG(Ofs_STACKM); + int ftmp; + GTRACE0("O_PUSHF"); + FlagSync_All(); +#if 0 // unused "extended PVI", if used should move to separate op + if (!V86MODE() && IOPL < 3 && (TheCPU.cr[4] & CR4_PVI)) + ftmp = (ftmp & ~(EFLAGS_IF|EFLAGS_VIF)) | ((ftmp & EFLAGS_VIF) ? EFLAGS_IF : 0); +#endif + ftmp = CPULONG(Ofs_EFLAGS) & (RETURN_MASK|EFLAGS_IF); + AR2.d = CPULONG(Ofs_XSS); + SR1.d = CPULONG(Ofs_ESP); + if (mode & DATA16) { + SR1.d = (SR1.d - 2) & stackm; + write_word(AR2.d + SR1.d, ftmp); + } + else { + SR1.d = (SR1.d - 4) & stackm; + write_dword(AR2.d + SR1.d, ftmp); + } +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + SR1.d |= (CPULONG(Ofs_ESP) & ~stackm); +#endif + CPULONG(Ofs_ESP) = SR1.d; + if (debug_level('e')>3) dbug_printf("(V) %08x\n",ftmp); + } break; + + case O_PUSHI: { + int v = va_arg(ap, int); + unsigned long stackm = CPULONG(Ofs_STACKM); + GTRACE3("O_PUSHI",0xff,0xff,v); + if (mode & DATA16) { + DR1.w.l = (short)v; + AR2.d = CPULONG(Ofs_XSS); + SR1.d = CPULONG(Ofs_ESP) - 2; + SR1.d &= stackm; + write_word(AR2.d + SR1.d, DR1.w.l); + } + else { + DR1.d = v; + AR2.d = CPULONG(Ofs_XSS); + SR1.d = CPULONG(Ofs_ESP) - 4; + SR1.d &= stackm; + write_dword(AR2.d + SR1.d, DR1.d); + } +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + SR1.d |= (CPULONG(Ofs_ESP) & ~stackm); +#endif + CPULONG(Ofs_ESP) = SR1.d; + } break; + + case O_POP: { + int imm16 = (mode&MRETISP) ? Offs_From_Arg() : 0; + long stackm = CPULONG(Ofs_STACKM); + GTRACE1("O_POP",imm16); + if (mode & DATA16) { + AR2.d = CPULONG(Ofs_XSS); + SR1.d = CPULONG(Ofs_ESP); + SR1.d &= stackm; + DR1.w.l = read_word(AR2.d + SR1.d); + SR1.d += 2 + imm16; +#ifdef STACK_WRAP_MP /* mask after incrementing */ + SR1.d &= stackm; +#endif + } + else { + AR2.d = CPULONG(Ofs_XSS); + SR1.d = CPULONG(Ofs_ESP); + SR1.d &= stackm; + DR1.d = read_dword(AR2.d + SR1.d); + SR1.d += 4 + imm16; +#ifdef STACK_WRAP_MP /* mask after incrementing */ + SR1.d &= stackm; +#endif + } +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + SR1.d |= (CPULONG(Ofs_ESP) & ~stackm); +#endif + CPULONG(Ofs_ESP) = SR1.d; + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } break; + +/* POP derived (sub-)sequences: */ + case O_POP1: + GTRACE0("O_POP1"); + AR2.d = CPULONG(Ofs_XSS); + SR1.d = CPULONG(Ofs_ESP); + break; + + case O_POP2: { + signed char o = Offs_From_Arg(); + long stackm = CPULONG(Ofs_STACKM); + GTRACE1("O_POP2",o); + if (mode & DATA16) { + SR1.d &= stackm; + DR1.w.l = read_word(AR2.d + SR1.d); + if (!(mode & MPOPRM)) + CPUWORD(o) = DR1.w.l; + SR1.d += 2; + } + else { + SR1.d &= stackm; + DR1.d = read_dword(AR2.d + SR1.d); + if (!(mode & MPOPRM)) + CPULONG(o) = DR1.d; + SR1.d += 4; + } +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + SR1.d |= (CPULONG(Ofs_ESP) & ~stackm); +#endif + if (debug_level('e')>3) dbug_printf("(V) %08x\n",DR1.d); + } break; + + case O_POP3: + GTRACE0("O_POP3"); + CPULONG(Ofs_ESP) = SR1.d; + break; + + case O_LEAVE: { + long stackm = CPULONG(Ofs_STACKM); + GTRACE0("O_LEAVE"); + if (mode & DATA16) { + SR1.d = CPUWORD(Ofs_BP); + AR2.d = CPULONG(Ofs_XSS); + SR1.d &= stackm; + DR1.w.l = read_word(AR2.d + SR1.d); + CPUWORD(Ofs_BP) = DR1.w.l; + SR1.d += 2; +#ifdef STACK_WRAP_MP /* mask after incrementing */ + SR1.d &= stackm; +#endif + } + else { + SR1.d = CPULONG(Ofs_EBP); + AR2.d = CPULONG(Ofs_XSS); + SR1.d &= stackm; + DR1.d = read_dword(AR2.d + SR1.d); + CPULONG(Ofs_EBP) = DR1.d; + SR1.d += 4; +#ifdef STACK_WRAP_MP /* mask after incrementing */ + SR1.d &= stackm; +#endif + } +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + SR1.d |= (CPULONG(Ofs_ESP) & ~stackm); +#endif + CPULONG(Ofs_ESP) = SR1.d; + } + break; + + case O_INT: { + unsigned char intno = va_arg(ap, int); + // Check bitmap, GPF if revectored + if (test_bit(intno, &TheCPU.int_revectored)) { + P0 = va_arg(ap, dosaddr_t); + TheCPU.err = EXCP0D_GPF; + } + else { + AR1.d = intno * 4; + } } + break; + + case O_MOVS_SetA: + GTRACE0("O_MOVS_SetA"); + if (mode&ADDR16) { + if (mode&MOVSSRC) { + AR2.d = CPULONG(OVERR_DS); + DR2.d = CPUWORD(Ofs_SI); /* for overflow calc */ + AR2.d += DR2.d; + } + if (mode&MOVSDST) { + AR1.d = CPULONG(Ofs_XES); + SR1.d = CPUWORD(Ofs_DI); /* for overflow calc */ + AR1.d += SR1.d; + + } + TR1.d = (mode&(MREP|MREPNE)? CPUWORD(Ofs_CX) : 1); + } + else { + if (mode&MOVSSRC) { + AR2.d = CPULONG(OVERR_DS) + CPULONG(Ofs_ESI); + } + if (mode&MOVSDST) { + AR1.d = CPULONG(Ofs_XES) + CPULONG(Ofs_EDI); + } + TR1.d = (mode&(MREP|MREPNE)? CPULONG(Ofs_ECX) : 1); + } + if (!(mode&(MREP|MREPNE|MOVSDST))) { + AR1.d = AR2.d; /* single lodsb uses L_DI_R1 */ + } + break; + + case O_MOVS_MovD: { + int df = (CPUWORD(Ofs_FLAGS) & EFLAGS_DF? -1:1); + dosaddr_t src, dest; + register unsigned int i; + i = TR1.d; + GTRACE4("O_MOVS_MovD",0xff,0xff,df,i); + if(i == 0) + break; + if(mode & ADDR16) { + unsigned int minofs, bytesbefore; + /* overflow check (DR2 is SI, SR1 is DI) */ + if (df == -1) { + minofs = min(SR1.d,DR2.d); + bytesbefore = (i-1)*OPSIZE(mode); + } else { + minofs = 0x10000 - max(SR1.d,DR2.d); + bytesbefore = i*OPSIZE(mode); + } + if(bytesbefore > minofs) { + unsigned int possible; + /* caught 16 bit address overflow: do it piecewise */ + + /* misaligned overflow generates trap. */ + if(minofs & (OPSIZE(mode)-1)) { + TheCPU.err=EXCP0D_GPF; + break; + } + + /* do maximal possible amount */ + possible = minofs / (OPSIZE(mode)); + if (df < 0) + possible++; + TR1.d = possible; + Gen_sim(O_MOVS_MovD,mode); + /* emulate overflow */ + if (df == -1) { + if(SR1.d == minofs) { + AR1.d -= df*0x10000; + SR1.d = 0xffff; + DR2.d -= minofs; + if (DR2.d > 0) + DR2.d -= 1; + else + DR2.d = 0xffff; + } + if(DR2.d == minofs) { + AR2.d -= df*0x10000; + DR2.d = 0xffff; + SR1.d -= minofs; + if (SR1.d > 0) + SR1.d -= 1; + else + SR1.d = 0xffff; + } + } else { + if(SR1.d == 0x10000 - minofs) { + AR1.d -= df*0x10000; + SR1.d = 0; + DR2.d += minofs; + DR2.d &= 0xffff; + } + if(DR2.d == 0x10000 - minofs) { + AR2.d -= df*0x10000; + DR2.d = 0; + SR1.d += minofs; + SR1.d &= 0xffff; + } + } + + /* do the rest */ + TR1.d = i - possible; + Gen_sim(O_MOVS_MovD,mode); + break; + } + } + dest = AR1.d; + src = AR2.d; + if (df<0) { + if (mode&MBYTE) { + while (i--) write_byte(dest--, read_byte(src--)); + } + else if (mode&DATA16) { + while (i--) { write_word(dest, read_word(src)); + dest -= 2; src -= 2; } + } + else { + while (i--) { write_dword(dest, read_dword(src)); + dest -= 4; src -= 4; } + } + } + else { + if (mode&MBYTE) { + while (i--) write_byte(dest++, read_byte(src++)); + } + else if (mode&DATA16) { + while (i--) { write_word(dest, read_word(src)); + dest += 2; src += 2; } + } + else { + while (i--) { write_dword(dest, read_dword(src)); + dest += 4; src += 4; } + } + } + TR1.d = 0; + AR1.d = dest; + AR2.d = src; + } + break; + case O_MOVS_LodD: { + int df = (CPUWORD(Ofs_FLAGS) & EFLAGS_DF? -1:1); + dosaddr_t addr; + register unsigned int i; + i = TR1.d; + GTRACE4("O_MOVS_LodD",0xff,0xff,df,i); + if (mode&(MREP|MREPNE)) { + dbug_printf("odd: REP LODS %d\n",i); + } + addr = AR2.d; + if (mode&MBYTE) { + while (i--) { DR1.b.bl = read_byte(addr); addr += df; } + } + else if (mode&DATA16) { + while (i--) { DR1.w.l = read_word(addr); addr += 2*df; } + } + else { + while (i--) { DR1.d = read_dword(addr); addr += 4*df; } + } + if (mode&(MREP|MREPNE)) TR1.d = 0; + AR2.d = addr; + // ! Warning DI,SI wrap in 16-bit mode + } + break; + case O_MOVS_StoD: { + int df = (CPUWORD(Ofs_FLAGS) & EFLAGS_DF? -1:1); + dosaddr_t addr; + register unsigned int i; + i = TR1.d; + GTRACE4("O_MOVS_StoD",0xff,0xff,df,i); + if((mode & ADDR16) && i) { + unsigned int minofs, bytesbefore; + /* overflow check (SR1 is DI) */ + if (df == -1) { + minofs = SR1.d; + bytesbefore = (i-1)*OPSIZE(mode); + } else { + minofs = 0x10000 - SR1.d; + bytesbefore = i*OPSIZE(mode); + } + if(bytesbefore > minofs) { + unsigned int possible; + /* caught 16 bit address overflow: do it piecewise */ + if(AR1.d & (OPSIZE(mode)-1)) + { + /* misaligned overflow generates trap. */ + TheCPU.err=EXCP0D_GPF; + break; + } + possible = minofs / (OPSIZE(mode)); + if (df < 0) + possible++; + TR1.d = possible; + Gen_sim(O_MOVS_StoD,mode); + SR1.d = (df == -1 ? 0xffff : 0); + AR1.d -= 0x10000*df; + TR1.d = i - possible; + Gen_sim(O_MOVS_StoD,mode); + break; + } + } + addr = AR1.d; + if (mode&MBYTE) { + while (i--) { write_byte(addr, DR1.b.bl); addr += df; } + } + else if (mode&DATA16) { + while (i--) { write_word(addr, DR1.w.l); addr += 2*df; } + } + else { + while (i--) { write_dword(addr, DR1.d); addr += 4*df; } + } + AR1.d = addr; + TR1.d = 0; + } + break; + case O_MOVS_ScaD: { // OSZAPC + int df = (CPUWORD(Ofs_FLAGS) & EFLAGS_DF? -1:1); + dosaddr_t addr; + register unsigned int i; + char k, z; + i = TR1.d; + GTRACE4("O_MOVS_ScaD",0xff,0xff,df,i); + if (i == 0) break; /* eCX = 0, no-op, no flags updated */ + RFL.mode = mode; + RFL.valid = V_SUB; + z = k = (mode&MREP? 1:0); + addr = AR1.d; + while (i && (z==k)) { + if (mode&MBYTE) { + RFL.RES.d = (S1=DR1.b.bl) - (S2=read_byte(addr)); + FlagHandleSub(S1, S2, RFL.RES.d, 8); + addr += df; + z = (RFL.RES.b.bl==0); + } + else if (mode&DATA16) { + RFL.RES.d = (S1=DR1.w.l) - (S2=read_word(addr)); + FlagHandleSub(S1, S2, RFL.RES.d, 16); + addr += 2*df; + z = (RFL.RES.w.l==0); + } + else { + RFL.RES.d = (S1=DR1.d) - (S2=read_dword(addr)); + FlagHandleSub(S1, S2, RFL.RES.d, 32); + addr += 4*df; + z = (RFL.RES.d==0); + } + i--; + } + AR1.d = addr; + TR1.d = i; + // ! Warning DI,SI wrap in 16-bit mode + } + break; + case O_MOVS_CmpD: { // OSZAPC + int df; + dosaddr_t addr1, addr2; + register unsigned int i; + char k, z; + i = TR1.d; + df = (CPUWORD(Ofs_FLAGS) & EFLAGS_DF? -1:1); + GTRACE4("O_MOVS_CmpD",0xff,0xff,df,i); + if (i == 0) break; /* eCX = 0, no-op, no flags updated */ + RFL.mode = mode; + RFL.valid = V_SUB; + addr1 = AR1.d; + if(!(mode & (MREP|MREPNE))) { + // assumes DR1=*AR2 + if (mode&MBYTE) { + DR2.b.bl = read_byte(addr1); + RFL.RES.d = (S1=DR1.b.bl) - (S2=DR2.b.bl); + } else if (mode&DATA16) { + DR2.w.l = read_word(addr1); + RFL.RES.d = (S1=DR1.w.l) - (S2=DR2.w.l); + } else { + DR2.d = read_dword(addr1); + RFL.RES.d = (S1=DR1.d) - (S2=DR2.d); + } + FlagHandleSub(S1, S2, RFL.RES.d, OPSIZE(mode)*8); + break; + } + z = k = (mode&MREP? 1:0); + addr2 = AR2.d; + while (i && (z==k)) { + if (mode&MBYTE) { + RFL.RES.d = (S1=read_byte(addr2)) - (S2=read_byte(addr1)); + FlagHandleSub(S1, S2, RFL.RES.d, 8); + addr1 += df; addr2 += df; + z = (RFL.RES.b.bl==0); + } + else if (mode&DATA16) { + RFL.RES.d = (S1=read_word(addr2)) - (S2=read_word(addr1)); + FlagHandleSub(S1, S2, RFL.RES.d, 16); + addr1 += 2*df; addr2 += 2*df; + z = (RFL.RES.w.l==0); + } + else { + RFL.RES.d = (S1=read_dword(addr2)) - (S2=read_dword(addr1)); + FlagHandleSub(S1, S2, RFL.RES.d, 32); + addr1 += 4*df; addr2 += 4*df; + z = (RFL.RES.d==0); + } + i--; + } + TR1.d = i; + AR1.d = addr1; + AR2.d = addr2; + // ! Warning DI,SI wrap in 16-bit mode + } + break; + + case O_MOVS_SavA: + GTRACE0("O_MOVS_SavA"); + if (!(mode&(MREP|MREPNE))) { + // %%edx set to DF's increment + DR2.d = (signed char)CPUBYTE(Ofs_DF_INCREMENTS+OPSIZEBIT(mode)); + if(mode & MOVSSRC) { + if (mode & ADDR16) + CPUWORD(Ofs_SI) += DR2.w.l; + else + CPULONG(Ofs_SI) += DR2.d; + } + if(mode & MOVSDST) { + if (mode & ADDR16) + CPUWORD(Ofs_DI) += DR2.w.l; + else + CPULONG(Ofs_DI) += DR2.d; + } + } + else if (mode&ADDR16) { + if (mode&(MREP|MREPNE)) { + CPUWORD(Ofs_CX) = TR1.w.l; + } + if (mode&MOVSSRC) { + AR2.d -= CPULONG(OVERR_DS); + CPUWORD(Ofs_SI) = AR2.w.l; + } + if (mode&MOVSDST) { + AR1.d -= CPULONG(Ofs_XES); + CPUWORD(Ofs_DI) = AR1.w.l; + } + } + else { + if (mode&(MREP|MREPNE)) { + CPULONG(Ofs_ECX) = TR1.d; + } + if (mode&MOVSSRC) { + AR2.d -= CPULONG(OVERR_DS); + CPULONG(Ofs_ESI) = AR2.d; + } + if (mode&MOVSDST) { + AR1.d -= CPULONG(Ofs_XES); + CPULONG(Ofs_EDI) = AR1.d; + } + } + break; + case O_SLAHF: { + int rcod = va_arg(ap,int)&1; // 0=LAHF 1=SAHF + if (rcod==0) { /* LAHF */ + GTRACE0("O_LAHF"); + FlagSync_All(); + CPUBYTE(Ofs_AH) = CPUBYTE(Ofs_FLAGS); + } + else { /* SAHF */ + GTRACE0("O_SAHF"); + CPUBYTE(Ofs_FLAGS) = (CPUBYTE(Ofs_AH)&0xd5)|0x02; + RFL.valid = V_INVALID; + } } + break; + case O_SETFL: { + unsigned char o1 = (unsigned char)va_arg(ap,int); + switch(o1) { // these are direct on x86 + case CMC: + GTRACE0("O_CMC"); + CPUBYTE(Ofs_FLAGS) ^= 1; + break; + case CLC: + GTRACE0("O_CLC"); + SET_CF(0); + break; + case STC: + GTRACE0("O_STC"); + SET_CF(1); + break; + case CLD: + GTRACE0("O_CLD"); + CPUWORD(Ofs_FLAGS) &= 0xfbff; + TheCPU.df_increments = 0x040201; + break; + case STD: + GTRACE0("O_STD"); + CPUWORD(Ofs_FLAGS) |= 0x400; + TheCPU.df_increments = 0xfcfeff; + break; + case CLI: + CPULONG(Ofs_EFLAGS) &= ~EFLAGS_IF; + break; + case INT: + GTRACE0("O_INT"); + CPUWORD(Ofs_FLAGS) &= ~(EFLAGS_IF|EFLAGS_TF); + break; + } } + break; + case O_BSWAP: { + unsigned char o1 = (unsigned char)va_arg(ap,int); + register long v; + GTRACE1("O_BSWAP",o1); + v = CPULONG(o1); + v = ((v & 0xff00ff00) >> 8) | ((v & 0x00ff00ff) << 8); + v = ((v & 0xffff0000) >> 16) | ((v & 0x0000ffff) << 16); + CPULONG(o1) = v; + } + break; + case O_SETCC: { + unsigned char o1 = (unsigned char)va_arg(ap,int); + GTRACE3("O_SETCC",0xff,0xff,o1); + FlagSync_All(); + switch(o1) { + case 0x00: DR1.b.bl = IS_OF_SET; break; + case 0x01: DR1.b.bl = !IS_OF_SET; break; + case 0x02: DR1.b.bl = IS_CF_SET; break; + case 0x03: DR1.b.bl = !IS_CF_SET; break; + case 0x04: DR1.b.bl = IS_ZF_SET; break; + case 0x05: DR1.b.bl = !IS_ZF_SET; break; + case 0x06: DR1.b.bl = IS_CF_SET || IS_ZF_SET; break; + case 0x07: DR1.b.bl = !IS_CF_SET && !IS_ZF_SET; break; + case 0x08: DR1.b.bl = IS_SF_SET; break; + case 0x09: DR1.b.bl = !IS_SF_SET; break; + case 0x0a: + e_printf("!!! SETp\n"); + DR1.b.bl = IS_PF_SET; break; + case 0x0b: + e_printf("!!! SETnp\n"); + DR1.b.bl = !IS_PF_SET; break; + case 0x0c: DR1.b.bl = IS_SF_SET ^ IS_OF_SET; break; + case 0x0d: DR1.b.bl = !(IS_SF_SET ^ IS_OF_SET); break; + case 0x0e: DR1.b.bl = (IS_SF_SET ^ IS_OF_SET) || IS_ZF_SET; break; + case 0x0f: DR1.b.bl = !(IS_SF_SET ^ IS_OF_SET) && !IS_ZF_SET; break; + } + } + break; + case O_BITOP: { + unsigned char o1 = (unsigned char)va_arg(ap,int); + signed char o2 = Offs_From_Arg(); + register int flg; + GTRACE3("O_BITOP",o2,0xff,o1); + if (o1 == 0x1c || o1 == 0x1d) { /* bsf/bsr */ + if (mode & DATA16) DR1.d = DR1.w.l; + DR1.d = o1 == 0x1c ? find_bit(DR1.d) : find_bit_r(DR1.d); + if (DR1.d == -1) { + flg = 0x40; + } else { + flg = 0; + if (mode & DATA16) + CPUWORD(o2) = DR1.d; + else + CPULONG(o2) = DR1.d; + } + // set ZF + FlagSync_All(); + CPUBYTE(Ofs_FLAGS)=(CPUBYTE(Ofs_FLAGS)&0xbf)|flg; + break; + } + if(o1 >= 0x20) + DR2.d = o2 & ((mode & DATA16) ? 0x0f : 0x1f); + else if (mode & DATA16) + { + DR2.d = CPUWORD(o2); + if (mode & RM_REG) + DR2.d &= 0x0f; + } else { + DR2.d = CPULONG(o2); + if (mode & RM_REG) + DR2.d &= 0x1f; + } + if ((mode & RM_REG) || o1 >= 0x20) { + if ((o1 & ~0x18) == 0x03 || (o1 & ~0x18) == 0x20) { + SET_CF((DR1.d >> DR2.d) & 1); + switch (o1 & 0x18) { + case 0x00: break; + case 0x08: DR1.d |= 1U << DR2.d; break; + case 0x10: DR1.d &= ~(1U << DR2.d); break; + case 0x18: DR1.d ^= 1U << DR2.d; break; + default: break; + } + } + } else { + /* add bit offset to effective address */ + AR1.d += (mode&DATA16) ? 2*(DR2.d>>4) : 4*(DR2.d>>5); + } + } break; + case O_SHFD: { + unsigned char l_r = (unsigned char)va_arg(ap,int)&8; + signed char o = Offs_From_Arg(); + unsigned char shc; + int cy; + if (mode & IMMED) { + shc = (unsigned char)va_arg(ap,int)&0x1f; + GTRACE4("O_SHFD",o,0xff,l_r,shc); + } + else { + shc = CPUBYTE(Ofs_CL)&0x1f; + GTRACE3("O_SHFD",o,0xff,l_r); + } + shc &= 31; + if (shc==0) break; + RFL.mode = mode; + RFL.valid = V_GEN; + if (mode & DATA16) { + if (l_r==0) { // left: <> (32-shc)); + cy = DR1.d & 1; + RFL.RES.d = DR1.w.l = DR1.w.h; + } + else { // right: >>mem|reg>> + DR1.w.h = CPUWORD(o); + /* undocumented: works like rotate internally */ + DR1.d = (DR1.d >> shc) | (DR1.d << (32-shc)); + cy = (DR1.d >> 31) & 1; + RFL.RES.d = DR1.w.l; + RFL.cout = RFL.RES.d << 16; + } + } + else { + u_int64_u v; + if (l_r==0) { // left: <> (64-shc)) & 1; + v.td <<= shc; + RFL.RES.d = DR1.d = v.t.th; + } + else { // right: >>mem|reg>> + v.t.th = CPULONG(o); + v.t.tl = DR1.d; + cy = (v.td >> (shc-1)) & 1; + v.td >>= shc; + RFL.RES.d = DR1.d = v.t.tl; + RFL.cout = RFL.RES.d; + } + } + SET_CF(cy); + if (shc>1) RFL.mode |= IGNOVF; + } break; + + case O_RDTSC: { // don't trust this one +#if 0 + hitimer_u t0, t1; + GTRACE0("O_RDTSC"); + t0.td = GETTSC(); + if (eTimeCorrect >= 0) { + t1.td = t0.td - TheCPU.EMUtime; + TheCPU.EMUtime = t1.td; + } + CPULONG(Ofs_EAX) = t0.t.tl; + CPULONG(Ofs_EDX) = t0.t.th; +#else + error("rdtsc not implemented\n"); +#endif + } + break; + + case O_INPDX: + GTRACE0("O_INPDX"); + DR2.d = CPULONG(Ofs_EDX); + if (mode&MBYTE) { + DR1.b.bl = port_inb(DR2.w.l); + CPUBYTE(Ofs_AL) = DR1.b.bl; + } + else if (mode & DATA16) { + DR1.w.l = port_inw(DR2.w.l); + CPUWORD(Ofs_AX) = DR1.w.l; + } + else { + DR1.d = port_ind(DR2.w.l); + CPULONG(Ofs_EAX) = DR1.d; + } + break; + case O_OUTPDX: + GTRACE0("O_OUTPDX"); + DR2.d = CPULONG(Ofs_EDX); + if (mode&MBYTE) { + DR1.b.bl = CPUBYTE(Ofs_AL); + port_outb(DR2.w.l,DR1.b.bl); + } + else if (mode & DATA16) { + DR1.w.l = CPUWORD(Ofs_AX); + port_outw(DR2.w.l,DR1.w.l); + } + else { + DR1.d = CPULONG(Ofs_EAX); + port_outd(DR2.w.l,DR1.d); + } + break; + + case JMP_INDIRECT: + P0 = LONG_CS + ((mode & DATA16) ? DR1.w.l : DR1.d); + if (debug_level('e')>2) + dbug_printf("** Jump taken to %08x\n",P0); + break; + + case JMP_LINK: { // opc, dspt, retaddr, link + int opc = va_arg(ap,int); + P0 = va_arg(ap,unsigned int); + unsigned int d_nt = va_arg(ap,unsigned int); + if (opc == CALLd || opc == CALLl) + PUSH(mode, d_nt); + if (debug_level('e')>2) { + if(opc == CALLd || opc == CALLl) + dbug_printf("CALL: ret=%08x\n",d_nt); + dbug_printf("** Jump taken to %08x\n",P0); + } } + break; + + case JF_LINK: + case JB_LINK: { // opc, PC, dspt, dspnt, link + int opc = va_arg(ap,int); + unsigned int PC = va_arg(ap,unsigned int); + unsigned int j_t = va_arg(ap,unsigned int); + unsigned int j_nt = va_arg(ap,unsigned int); + (void)PC; + switch(opc) { + case JO: P0 = is_of_set() ? j_t : j_nt; break; + case JNO: P0 = !is_of_set() ? j_t : j_nt; break; + case JB_JNAE: P0 = IS_CF_SET ? j_t : j_nt; break; + case JNB_JAE: P0 = !IS_CF_SET ? j_t : j_nt; break; + case JE_JZ: P0 = is_zf_set() ? j_t : j_nt; break; + case JNE_JNZ: P0 = !is_zf_set() ? j_t : j_nt; break; + case JBE_JNA: P0 = IS_CF_SET || is_zf_set() ? j_t : j_nt; break; + case JNBE_JA: P0 = !IS_CF_SET && !is_zf_set() ? j_t : j_nt; break; + case JS: P0 = is_sf_set() ? j_t : j_nt; break; + case JNS: P0 = !is_sf_set() ? j_t : j_nt; break; + case JP_JPE: + e_printf("!!! JPset\n"); + FlagSync_AP(); + P0 = IS_PF_SET ? j_t : j_nt; break; + case JNP_JPO: + e_printf("!!! JPclr\n"); + FlagSync_AP(); + P0 = !IS_PF_SET ? j_t : j_nt; break; + case JL_JNGE: + P0 = is_sf_set() ^ is_of_set() ? j_t : j_nt; break; + case JNL_JGE: + P0 = !(is_sf_set() ^ is_of_set()) ? j_t : j_nt; break; + case JLE_JNG: + P0 = (is_sf_set() ^ is_of_set()) || is_zf_set() ? j_t : j_nt; break; + case JNLE_JG: + P0 = !(is_sf_set() ^ is_of_set()) && !is_zf_set() ? j_t : j_nt; break; + case JCXZ: + P0 = ((mode&ADDR16? rCX : rECX) == 0) ? j_t : j_nt; break; + } + if (debug_level('e')>2 && P0 == j_t) dbug_printf("** Jump taken to %08x\n",j_t); + } + break; + case JLOOP_LINK: { // opc, dspt, dspnt, link + int opc = va_arg(ap,int); + unsigned int j_t = va_arg(ap,unsigned int); + unsigned int j_nt = va_arg(ap,unsigned int); + int cxv = (mode&ADDR16? --rCX : --rECX); + switch(opc) { + case LOOP: + P0 = cxv != 0 ? j_t : j_nt; break; + case LOOPZ_LOOPE: + P0 = cxv != 0 && is_zf_set() ? j_t : j_nt; break; + case LOOPNZ_LOOPNE: + P0 = cxv != 0 && !is_zf_set() ? j_t : j_nt; break; + } + if (debug_level('e')>2 && P0 == j_t) dbug_printf("** Jump taken to %08x\n",j_t); + } + break; + default: + leavedos_main(0x494c4c); + break; + + } + + va_end(ap); +#ifdef DEBUG_MORE + if (debug_level('e')>3) { +#else + if (debug_level('e')>6) { +#endif + dbug_printf("(R) DR1=%08x DR2=%08x AR1=%08x AR2=%08x\n", + DR1.d,DR2.d,AR1.d,AR2.d); + dbug_printf("(R) SR1=%08x TR1=%08x\n", + SR1.d,TR1.d); + dbug_printf("(R) RFL m=[%s] v=%d cout=%08x RES=%08x\n", + showmode(RFL.mode),RFL.valid,RFL.cout,RFL.RES.d); +// if (debug_level('e')==9) dbug_printf("\n%s",e_print_regs()); + } + + if (op == O_FOP) { + int exs = TheCPU.fpus & 0x7f; + if (debug_level('e')>3) { + e_printf(" %s\n", e_trace_fp()); + } + if (exs) { + e_printf("FPU: error status %02x\n",exs); + if ((exs & ~TheCPU.fpuc) & 0x3f) { + e_printf("FPU exception\n"); + TheCPU.err = EXCP10_COPR; + } + } + } + +#if PROFILE + if (debug_level('e')) GenTime += (GETTSC() - t0); +#endif +} + + +///////////////////////////////////////////////////////////////////////////// + + +static unsigned int CloseAndExec_sim(unsigned int PC, int mode) +{ + unsigned int ret; + if (debug_level('e')>1) { + if (TheCPU.sigalrm_pending>0) e_printf("** SIGALRM is pending\n"); + if (debug_level('e')>2) { + e_printf("==== Closing sequence at %08x\n", PC); + } +#if defined(SINGLESTEP)||defined(SINGLEBLOCK) + dbug_printf("\n%s",e_print_regs()); +#endif +#ifndef DEBUG_MORE + if (debug_level('e')>3) +#endif + { + dbug_printf("(R) DR1=%08x DR2=%08x AR1=%08x AR2=%08x\n", + DR1.d,DR2.d,AR1.d,AR2.d); + dbug_printf("(R) SR1=%08x TR1=%08x\n", + SR1.d,TR1.d); + dbug_printf("(R) RFL m=[%s] v=%d cout=%08x RES=%08x\n\n", + showmode(RFL.mode),RFL.valid,RFL.cout,RFL.RES.d); + } + } + + if (!(CEmuStat & CeS_INHI)) { + if (TheCPU.sigalrm_pending) { + CEmuStat|=CeS_SIGPEND; + TheCPU.sigalrm_pending = 0; + } + } + if (P0 == (unsigned)-1) + return PC; + ret = P0; + P0 = (unsigned)-1; + return ret; +} + + +///////////////////////////////////////////////////////////////////////////// + diff --git a/src/base/emu-i386/simx86/codegen-sim.h b/src/base/emu-i386/simx86/codegen-sim.h new file mode 100644 index 0000000..1b8677a --- /dev/null +++ b/src/base/emu-i386/simx86/codegen-sim.h @@ -0,0 +1,91 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originaly was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#ifndef _EMU86_CODEGEN_SIM_H +#define _EMU86_CODEGEN_SIM_H + +#include "vgaemu.h" + +///////////////////////////////////////////////////////////////////////////// + +typedef union { + unsigned int d; + signed int ds; + struct { unsigned short l,h; } w; + struct { signed short l,h; } ws; + struct { unsigned char bl,bh,b2,b3; } b; + struct { signed char bl,bh,b2,b3; } bs; +} wkreg; + +typedef struct { + int valid, mode, cout; + wkreg RES; +} flgtmp; + +#define V_INVALID 0 +#define V_GEN 1 // general case +#define V_SUB 2 +#define V_SBB 3 +#define V_ADC 4 +#define V_ADD 5 + +extern wkreg DR1; // "eax" +extern wkreg DR2; // "edx" +extern wkreg AR1; // "edi" +extern wkreg AR2; // "esi" +extern wkreg SR1; // "ebp" +extern wkreg TR1; // "ecx" +extern flgtmp RFL; + +#define GTRACE0(s) if (debug_level('e')>2) e_printf("(G) %-12s [%s]\n",(s),showmode(mode)) +#define GTRACE1(s,r) if (debug_level('e')>2) e_printf("(G) %-12s %s [%s]\n",(s),\ + showreg(r),showmode(mode)) +#define GTRACE2(s,r1,r2) if (debug_level('e')>2) e_printf("(G) %-12s %s %s [%s]\n",(s),\ + showreg(r1),showreg(r2),showmode(mode)) +#define GTRACE3(s,r1,r2,a) if (debug_level('e')>2) e_printf("(G) %-12s %s %s %08x [%s]\n",(s),\ + showreg(r1),showreg(r2),(int)(a),showmode(mode)) +#define GTRACE4(s,r1,r2,a,b) if (debug_level('e')>2) e_printf("(G) %-12s %s %s %08x %08x [%s]\n",\ + (s),showreg(r1),showreg(r2),(int)(a),(int)(b),showmode(mode)) +#define GTRACE5(s,r1,r2,a,b,c) if (debug_level('e')>2) e_printf("(G) %-12s %s %s %08x %08x %08x [%s]\n",\ + (s),showreg(r1),showreg(r2),(int)(a),(int)(b),(int)(c),showmode(mode)) +extern void FlagSync_AP (void); +extern void FlagSync_O (void); +extern void FlagSync_All (void); +extern void Gen_sim(int op, int mode, ...); +extern void AddrGen_sim(int op, int mode, ...); +extern void InitGen_sim(void); + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/src/base/emu-i386/simx86/codegen-x86.c b/src/base/emu-i386/simx86/codegen-x86.c new file mode 100644 index 0000000..7dff53c --- /dev/null +++ b/src/base/emu-i386/simx86/codegen-x86.c @@ -0,0 +1,3526 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +/* + * BACK-END for the cpuemu interpreter. + * + * It translates the intermediate ops (defined in codegen.h) to their + * final binary form and stores the generated code into a temporary + * buffer (CodeBuf). + * These intermediate ops are still being reworked and grow in an + * incremental way; I hope they will converge to some better defined + * set as soon as I'll start coding for some other processor. + * + * There should be other similar modules, one for each target. So you + * can have codegen-ppc.c or codegen-emulated.c or whatever else. + * + * This module generates x86 code. Hey, wait... x86 from x86? + * Actually the generated code runs always in 32-bit mode, so in a way + * the 16-bit V86 mode is "emulated". + * + * All instructions operate on a virtual CPU image in memory ("TheCPU"), + * and are completely self-contained. They read from TheCPU registers, + * operate, and store back to the same registers. There's an exception - + * FLAGS, which are not stored back until the end of a code block. + * In fact, you will note that there's NO flag handling here, because on + * x86 we use the real hardware to calculate them, and this speeds up + * things a lot compared to a full interpreter. Flags will be a nightmare + * for non-x86 host CPUs. + * + * This only applies to the condition code flags though, OF, SF, ZF, AF, + * PF and CF (0x8d5). All other flags are stored in EFLAGS=TheCPU.eflags, + * including DF. Normally the real DF is clear for compatibility with C + * code; it is only temporarily set during string instructions. + * + * There is NO optimization for the produced code. It is a very pipeline- + * unconscious code full of register dependencies and reloadings. + * Clearly we hope that the 1st level cache of the host CPU works as + * advertised ;-) + * + * There are two main functions here: + * AddrGen, which implements the AGU (Address Generation Unit). + * It calculates the address coming from ModRM and stores + * it into a well-defined register (edi in the x86 case) + * Gen, which does the ALU work and all the rest. There is no + * branch specific unit, as the branches are (in principle) + * all interpreted. + * Both functions use a variable parameter approach, just to make them + * hard to follow ;-) + * + */ + +/*************************************************************************** + * + * Registers on enter: + * ebx/rbx pointer to SynCPU (must not be changed) + * ebp/rbp memory base address (mem_base) + * [esp/rsp] cpu->eflags + * + * Registers used by the 32/64-bit machine: + * eax scratch, data + * ebx/rbx pointer to SynCPU (must not be changed) + * ecx scratch, address/count + * edx scratch, data + * esi scratch, address + * edi memory/register address + * ebp/rbp memory base address (mem_base) + * esp/rsp not modified + * flags modified + * + * Registers on exit: + * eax PC for the next instruction + * edx flags + * edi last memory address + * + ***************************************************************************/ +// + +#include +#include +#include +#include +#include "utilities.h" +#include "emu86.h" +#include "dlmalloc.h" +#include "mapping.h" +#ifdef HOST_ARCH_X86 +#include "codegen-x86.h" +#include "cpatch.h" + +static void Gen_x86(int op, int mode, ...); +static void AddrGen_x86(int op, int mode, ...); +static unsigned int CloseAndExec_x86(unsigned int PC, int mode); + +hitimer_u TimeStartExec; +static TNode *LastXNode = NULL; + +///////////////////////////////////////////////////////////////////////////// + +#define Offs_From_Arg() (char)(va_arg(ap,int)) + +/* This code is appended at the end of every instruction sequence. It + * passes back the IP of the next instruction after the sequence. + * (the one where we switch back to interpreted code). + * + * movl #return_PC,eax + * popl edx (flags) + * ret + */ +unsigned char TailCode[TAILSIZE+1] = + { 0xb8,0,0,0,0,0x5a,0xc3,0xf4 }; + +/* + * This function is only here for looking at the generated binary code + * with objdump. + */ +static void _test_(void) __attribute__((used)); + +static void _test_(void) +{ + __asm__ __volatile__ (" \ + nop \ + " : : : "memory" ); +} + +///////////////////////////////////////////////////////////////////////////// + +/* empirical!! */ +static int goodmemref(dosaddr_t m) +{ + if (m >= mMaxMem) + return 0; + if (m < 0x110000) return 1; + if (dpmi_is_valid_range(m, mMaxMem - m)) + return 1; + return 0; +} + +///////////////////////////////////////////////////////////////////////////// + + +void InitGen_x86(void) +{ + Gen = Gen_x86; + AddrGen = AddrGen_x86; + CloseAndExec = CloseAndExec_x86; + UseLinker = USE_LINKER; +} + + +///////////////////////////////////////////////////////////////////////////// + +/* NOTE: parameters IG->px must be the last argument in a Gn() macro + * because of the OR operator, which would cause trouble if the parameter + * is negative */ + +static unsigned char *CodeGen(unsigned char *CodePtr, unsigned char *BaseGenBuf, + IMeta *I, int j) +{ + /* evil hack, keeping state from MOVS_SavA to MOVS_SetA in + a static variable */ + static unsigned char * rep_retry_ptr = (unsigned char*)0xdeadbeef; + IGen *IG = &(I->gen[j]); + register unsigned char *Cp = CodePtr; + unsigned char * CpTemp; + int mode = IG->mode; + int rcod; +#if PROFILE + hitimer_t t0 = 0; + if (debug_level('e')) t0 = GETTSC(); +#endif + + switch(IG->op) { + case A_DI_0: // base(32), imm + // movl $imm,%%edi + G1(0xbf,Cp); G4(IG->p1,Cp); + if (!(mode & MLEA)) { + // addl offs(%%ebx),%%edi (seg reg offset) + G3M(0x03,0x7b,IG->p0,Cp); + } + break; + case A_DI_1: { // base(32), {imm}, reg + int idsp=IG->p1; + + if (mode & ADDR16) { + // movzwl offs(%%ebx),%%edi + G4M(0x0f,0xb7,0x7b,IG->p2,Cp); + if ((mode&IMMED) && (idsp!=0)) { + // addw $immed,%%di + G3(0xc78166,Cp); G2(idsp,Cp); + } + } + else { + // movl offs(%%ebx),%%edi + G3M(0x8b,0x7b,IG->p2,Cp); + if (idsp!=0) { + GenLeaEDI(idsp); + } + } + if (!(mode & MLEA)) { + // addl offs(%%ebx),%%edi (seg reg offset) + G3M(0x03,0x7b,IG->p0,Cp); + } } + break; + case A_DI_2: { // base(32), {imm}, reg, reg, {shift} + int idsp=IG->p1; + + if (mode & ADDR16) { + // movzwl offs(%%ebx),%%edi + G4M(0x0f,0xb7,0x7b,IG->p3,Cp); + // addw offs(%%ebx),%%di + G4M(0x66,0x03,0x7b,IG->p2,Cp); + if (idsp!=0) { + // addw $immed,%%di + G3(0xc78166,Cp); G2(idsp,Cp); + } + } + else { + unsigned char sh = IG->p4; + // movl offs(%%ebx),%%edi + G3M(0x8b,0x7b,IG->p3,Cp); + if (sh) { + // shll $1,%%edi + if (sh==1) { G2(0xe7d1,Cp); } + // shll $count,%%edi + else { G2(0xe7c1,Cp); G1(sh,Cp); } + } + // addl offs(%%ebx),%%edi + G3M(0x03,0x7b,IG->p2,Cp); + if (idsp!=0) { + GenLeaEDI(idsp); + } + } + if (!(mode & MLEA)) { + // addl offs(%%ebx),%%edi (seg reg offset) + G3M(0x03,0x7b,IG->p0,Cp); + } } + break; + case A_DI_2D: { // modrm_sibd, 32-bit mode + int idsp = IG->p0; + unsigned char sh = IG->p2; + // movl offs(%%ebx),%%edi + G3M(0x8b,0x7b,IG->p1,Cp); + // shll $count,%%ecx + if (sh) { + // shll $1,%%edi + if (sh==1) { G2(0xe7d1,Cp); } + // shll $count,%%edi + else { G2(0xe7c1,Cp); G1(sh,Cp); } + } + if (idsp!=0) { + GenLeaEDI(idsp); + } + if (!(mode & MLEA)) { + // addl offs(%%ebx),%%edi (seg reg offset) + G3M(0x03,0x7b,IG->ovds,Cp); + } } + break; + case A_SR_SH4: { // real mode make base addr from seg + // movzwl ofs(%%ebx),%%eax + G4M(0x0f,0xb7,0x43,IG->p0,Cp); + // shll $4,%%eax + G3M(0xc1,0xe0,0x04,Cp); + // movl %%eax,ofs(%%ebx) + G3M(0x89,0x43,IG->p1,Cp); + // addl $0xffff,%eax + G1(0x05,Cp); G4(0x0000ffff,Cp); + // movl %%eax,ofs(%%ebx) + G3M(0x89,0x43,IG->p1+4,Cp); + } + break; + case L_NOP: + G1(0x90,Cp); + break; + // Special case: CR0&0x3f + case L_CR0: + // movl Ofs_CR0(%%ebx),%%eax + G3M(0x8b,0x43,Ofs_CR0,Cp); + // andl $0x3f,%%eax + G3(0x3fe083,Cp); + break; + case O_FOP: { + unsigned char *p = Fp87_op_x86(CodePtr, IG->p0, IG->p1); + if (p == NULL) TheCPU.err = -96; + else Cp = p; + } + break; + + case L_REG: { + if (mode&(MBYTE|MBYTX)) { + // movb offs(%%ebx),%%al + G3M(0x8a,0x43,IG->p0,Cp); + } + else { + // mov{wl} offs(%%ebx),%%{e}ax + Gen66(mode,Cp); G3M(0x8b,0x43,IG->p0,Cp); + } } + break; + case S_REG: { + if (mode&MBYTE) { + // movb %%al,offs(%%ebx) + G3M(0x88,0x43,IG->p0,Cp); + } + else { + // mov{wl} %%{e}ax,offs(%%ebx) + Gen66(mode,Cp); G3M(0x89,0x43,IG->p0,Cp); + } } + break; + case L_REG2REG: { + if (mode&MBYTE) { + G3M(0x8a,0x43,IG->p0,Cp); // rsrc + G3M(0x88,0x43,IG->p1,Cp); // rdest + } else { + Gen66(mode,Cp); G3M(0x8b,0x43,IG->p0,Cp); + Gen66(mode,Cp); G3M(0x89,0x43,IG->p1,Cp); + } } + break; + case S_DI_R: { + // mov{wl} %%{e}di,offs(%%ebx) + Gen66(mode,Cp); G3M(0x89,0x7b,IG->p0,Cp); + } + break; + case S_DI_IMM: { + if (mode&MBYTE) { + // movb $xx,(%%edi) + G1(0xb0,Cp); G1(IG->p0,Cp); + STD_WRITE_B; + } else { + // mov{wl} $xx,(%%edi) + G1(0xb8,Cp); G4(IG->p0,Cp); + STD_WRITE_WL(mode); + } } + break; + + case L_IMM: { + if (mode&MBYTE) { + // movb $immed,offs(%%ebx) + G3M(0xc6,0x43,IG->p0,Cp); G1(IG->p1,Cp); + } + else { + // mov{wl} $immed,offs(%%ebx) + Gen66(mode,Cp); + G3M(0xc7,0x43,IG->p0,Cp); G2_4(mode,IG->p1,Cp); + } } + break; + case L_IMM_R1: { + if (mode&MBYTE) { + // movb $immed,%%al + G1(0xb0,Cp); G1(IG->p0,Cp); + } + else { + // mov{wl} $immed,%%{e}ax + Gen66(mode,Cp); G1(0xb8,Cp); G2_4(mode,IG->p0,Cp); + } } + break; + case L_MOVZS: + if (mode & MBYTX) { + if (!(mode & DATA16)) { + // mov{sz}bw %%al,%%eax + G3M(0x0f,(0xb6|IG->p0),0xc0,Cp); + } + else if (IG->p0) { + // movsbw %%al,%%ax = cbw + G2M(0x66,0x98,Cp); + } + else { + // movzbw %%al,%%ax = movb $0, %%ah + G2M(0xb4,0x00,Cp); + } + } + else { + if (mode & DATA16) { + // mov{sz}ww %%ax,%%ax + G4M(0x66,0x0f,(0xb7|IG->p0),0xc0,Cp); + } + else if (IG->p0) { + // movswl %%ax,%%eax = cwde + G1(0x98,Cp); + } + else { + // movzwl %%ax,%%eax + G3M(0x0f,0xb7,0xc0,Cp); + } + } + // mov{wl} %%{e}ax,offs(%%ebx) + Gen66(mode,Cp); G3M(0x89,0x43,IG->p1,Cp); + break; + + case L_LXS1: { + // mov{wl} (%%edi,%%ebp,1),%%{e}ax + Gen66(mode,Cp); G3M(0x8b,0x04,0x2f,Cp); + // mov{wl} %%{e}ax,offs(%%ebx) + Gen66(mode,Cp); G3M(0x89,0x43,IG->p0,Cp); + // leal {2|4}(%%edi),%%edi + G2M(0x8d,0x7f,Cp); G1((mode&DATA16? 2:4),Cp); + } + break; + case L_LXS2: { /* real mode segment base from segment value */ + // movzwl (%%edi,%%ebp,1),%%eax + G4M(0x0f,0xb7,0x04,0x2f,Cp); + // movw %%ax,ofs(%%ebx) + G4M(0x66,0x89,0x43,IG->p0,Cp); + // shll $4,%%eax + G3M(0xc1,0xe0,0x04,Cp); + // movl %%eax,ofs(%%ebx) + G3M(0x89,0x43,IG->p1,Cp); + // addl $0xffff,%eax + G1(0x05,Cp); G4(0x0000ffff,Cp); + // movl %%eax,ofs(%%ebx) + G3M(0x89,0x43,IG->p1+4,Cp); + } + break; + case L_ZXAX: + // movzwl %%ax,%%eax + G3(0xc0b70f,Cp); + break; + + case L_DI_R1: + if (mode&(MBYTE|MBYTX)) { + G3(0x2f048a,Cp); G1(0x90,Cp); + } + else if (mode&DATA16) { + G1(0x66,Cp); G3(0x2f048b,Cp); + } + else { + G3(0x2f048b,Cp); G1(0x90,Cp); + } + G2(0x9090,Cp); + break; + case S_DI: + if (mode&MBYTE) { + STD_WRITE_B; + } + else { + STD_WRITE_WL(mode); + } + break; + + case O_ADD_R: // acc = acc op reg + rcod = ADDbtrm; goto arith0; + case O_OR_R: + rcod = ORbtrm; goto arith0; + case O_ADC_R: + rcod = ADCbtrm; goto arith0; + case O_SBB_R: + rcod = SBBbtrm; goto arith0; + case O_AND_R: + rcod = ANDbtrm; goto arith0; + case O_SUB_R: + rcod = SUBbtrm; goto arith0; + case O_XOR_R: + rcod = XORbtrm; goto arith0; + case O_CMP_R: + rcod = CMPbtrm; goto arith0; + case O_INC_R: + rcod = GRP2brm; goto arith0; + case O_DEC_R: + rcod = 0x08fe; +arith0: { + G1(POPdx,Cp); // get flags from stack into %%edx + switch (IG->op) { + case O_ADC_R: // tests carry + case O_SBB_R: // tests carry + case O_INC_R: // preserves carry + case O_DEC_R: // preserves carry + // shr $1,%%edx to get carry flag from stack + G2M(0xd1,0xea,Cp); + } + if (mode & MBYTE) { + if (mode & IMMED) { + // OPb $immed,%%al + G1(rcod+2,Cp); G1(IG->p0,Cp); + } + else { + // OPb offs(%%ebx),%%al + G2(0x4300|rcod,Cp); G1(IG->p0,Cp); + } + } + else { + if (mode & IMMED) { + // OP{lw} $immed,%%{e}ax + Gen66(mode,Cp); + G1(rcod+3,Cp); G2_4(mode,IG->p0,Cp); + } + else { + // OP{wl} offs(%%ebx),%%{e}ax + Gen66(mode,Cp); + G2(0x4301|rcod,Cp); G1(IG->p0,Cp); + } + } + G1(PUSHF,Cp); // flags back on stack + } + break; + case O_CLEAR: + G3M(POPdx,0x31,0xc0,Cp); //ignore flags; xorl %%eax,%%eax + if (mode & MBYTE) { + // movb %%al,offs(%%ebx) + G3M(0x88,0x43,IG->p0,Cp); + } + else { + // mov{wl} %%{e}ax,offs(%%ebx) + Gen66(mode,Cp); G3M(0x89,0x43,IG->p0,Cp); + } + G1(PUSHF,Cp); // new flags on stack + break; + case O_TEST: + G1(POPdx,Cp); // ignore flags + if (mode & MBYTE) { + // testb $0xff,offs(%%ebx) + G4M(0xf6,0x43,IG->p0,0xffu,Cp); + } + else if (mode&DATA16) { + // testw $0xffff,offs(%%ebx) + G4M(0x66,0xf7,0x43,IG->p0,Cp); G2(0xffff,Cp); + } + else { + // test $0xffffffff,offs(%%ebx) + G3M(0xf7,0x43,IG->p0,Cp); G4(0xffffffff,Cp); + } + G1(PUSHF,Cp); // new flags on stack + break; + case O_SBSELF: + // if CY=0 -> reg=0, flag=xx46 + // if CY=1 -> reg=-1, flag=xx97 + // pop %%edx; shr $1,%%edx to get carry flag from stack + G3M(POPdx,0xd1,0xea,Cp); + // sbbl %%eax,%%eax + G2M(0x19,0xc0,Cp); + if (mode & MBYTE) { + // movb %%al,offs(%%ebx) + G3M(0x88,0x43,IG->p0,Cp); + } + else { + // mov{wl} %%{e}ax,offs(%%ebx) + Gen66(mode,Cp); G3M(0x89,0x43,IG->p0,Cp); + } + G1(PUSHF,Cp); // flags back on stack + break; + case O_ADD_FR: + rcod = ADDbfrm; /* 0x00 */ goto arith1; + case O_OR_FR: + rcod = ORbfrm; /* 0x08 */ goto arith1; + case O_ADC_FR: + rcod = ADCbfrm; /* 0x10 */ goto arith1; + case O_SBB_FR: + rcod = SBBbfrm; /* 0x18 */ goto arith1; + case O_AND_FR: + rcod = ANDbfrm; /* 0x20 */ goto arith1; + case O_SUB_FR: + rcod = SUBbfrm; /* 0x28 */ goto arith1; + case O_XOR_FR: + rcod = XORbfrm; /* 0x30 */ goto arith1; + case O_CMP_FR: + rcod = CMPbfrm; /* 0x38 */ +arith1: + G1(POPdx,Cp); // get flags from stack into %%edx + if (IG->op == O_ADC_FR || IG->op == O_SBB_FR) { + // shr $1,%%edx to get carry flag from stack + G2M(0xd1,0xea,Cp); + } + if (mode & MBYTE) { + if (mode & IMMED) { + // OPb $immed,offs(%%ebx) + G4M(0x80,0x43|rcod,IG->p0,IG->p1,Cp); + } + else { + // OPb %%al,offs(%%ebx) + G2(0x4300|rcod,Cp); G1(IG->p0,Cp); + } + } + else { + if (mode & IMMED) { + // OP{wl} $immed,offs(%%ebx) + Gen66(mode,Cp); + G3M(0x81,0x43|rcod,IG->p0,Cp); + G2_4(mode,IG->p1,Cp); + } + else { + // OP{wl} %%eax,offs(%%ebx) + Gen66(mode,Cp); + G2(0x4301|rcod,Cp); G1(IG->p0,Cp); + } + } + G1(PUSHF,Cp); // flags back on stack + break; + case O_NOT: + if (mode & MBYTE) { + // notb %%al + G2M(0xf6,0xd0,Cp); + } + else { + // NOT{wl} %%(e)ax + Gen66(mode,Cp); + G2M(0xf7,0xd0,Cp); + } + break; + case O_NEG: + G1(POPdx,Cp); // ignore flags from stack + if (mode & MBYTE) { + // negb %%al + G2M(0xf6,0xd8,Cp); + } + else { + // neg{wl} (%%edi) + Gen66(mode,Cp); + G2M(0xf7,0xd8,Cp); + } + G1(PUSHF,Cp); // new flags on stack + break; + case O_INC: + G1(POPdx,Cp); // get flags from stack into %%edx + // shr $1,%%edx to get preserved carry flag from stack + G2M(0xd1,0xea,Cp); + if (mode & MBYTE) { + // incb %%al + G2M(0xfe,0xc0,Cp); + } + else if (mode & DATA16) { + // inc %%ax +#ifdef __x86_64__ // 0x40 is a REX byte, not inc + G3M(0x66,0xff,0xc0,Cp); +#else + G2M(0x66,0x40,Cp); +#endif + } + else { + // inc %%eax +#ifdef __x86_64__ + G2M(0xff,0xc0,Cp); +#else + G1(0x40,Cp); +#endif + } + G1(PUSHF,Cp); // flags back on stack before writing + break; + case O_DEC: + G1(POPdx,Cp); // get flags from stack into %%edx + // shr $1,%%edx to get preserved carry flag from stack + G2M(0xd1,0xea,Cp); + if (mode & MBYTE) { + // decb %%al + G2M(0xfe,0xc8,Cp); + } + else if (mode & DATA16) { + // dec %%ax +#ifdef __x86_64__ // 0x48 is a REX byte, not dec + G3M(0x66,0xff,0xc8,Cp); +#else + G2M(0x66,0x48,Cp); +#endif + } + else { + // dec %%eax +#ifdef __x86_64__ + G2M(0xff,0xc8,Cp); +#else + G1(0x48,Cp); +#endif + } + G1(PUSHF,Cp); // flags back on stack + break; + case O_CMPXCHG: { + G1(POPdx,Cp); // ignore flags from stack + if (mode & MBYTE) { + // movb offs1(%%ebx),%%dl + G3M(0x8a,0x53,IG->p0,Cp); + // movb %%al,%%cl + G2M(0x88,0xc1,Cp); + // movb Ofs_AL(%%ebx),%%al + G3M(0x8a,0x43,Ofs_AL,Cp); + // cmpxchgb %%dl,%%cl + G3M(0x0f,0xb0,0xd1,Cp); + // movb %%al,Ofs_AL(%%ebx) + G3M(0x88,0x43,Ofs_AL,Cp); + // movb %%cl,%%al, + G2M(0x88,0xc8,Cp); + } + else { + // mov{wl} offs1(%%ebx),%%{e}dx + Gen66(mode,Cp); G3M(0x8b,0x53,IG->p0,Cp); + // mov{wl} %%{e}ax,%%{e}cx + Gen66(mode,Cp); G2M(0x89,0xc1,Cp); + // mov{wl} Ofs_EAX(%%ebx),%%{e}ax + Gen66(mode,Cp); G3M(0x8b,0x43,Ofs_EAX,Cp); + // cmpxchg{wl} %%{e}dx,%%{e}cx + Gen66(mode,Cp); G3M(0x0f,0xb1,0xd1,Cp); + // mov{wl} %%{e}ax,Ofs_EAX(%%ebx) + Gen66(mode,Cp); G3M(0x89,0x43,Ofs_EAX,Cp); + // mov{wl} %%{e}cx,%%{e}ax + Gen66(mode,Cp); G2M(0x89,0xc8,Cp); + } } + G1(PUSHF,Cp); // flags back on stack + break; + case O_XCHG: { + if (mode & MBYTE) { + // xchgb offs(%%ebx),%%al + G3M(0x86,0x43,IG->p0,Cp); + } + else { + // xchg{wl} offs(%%ebx),%%{e}ax + Gen66(mode,Cp); G3M(0x87,0x43,IG->p0,Cp); + } } + break; + case O_XCHG_R: { + // mov{wl} offs1(%%ebx),%%{e}ax + Gen66(mode,Cp); G3M(0x8b,0x43,IG->p0,Cp); + // xchg{wl} offs2(%%ebx),%%{e}ax + Gen66(mode,Cp); G3M(0x87,0x43,IG->p1,Cp); + // mov{wl} %%{e}ax,offs1(%%ebx) + Gen66(mode,Cp); G3M(0x89,0x43,IG->p0,Cp); + } + break; + case O_MUL: + G1(POPF,Cp); // get flags from stack + if (mode & MBYTE) { + // mulb Ofs_AL(%%ebx),%%al + G3M(0xf6,0x63,Ofs_AL,Cp); + // movw %%ax,Ofs_AX(%%ebx) + G4M(OPERoverride,0x89,0x43,Ofs_AX,Cp); + } + else if (mode&DATA16) { + // mulw Ofs_AX(%%ebx),%%ax + G4M(OPERoverride,0xf7,0x63,Ofs_AX,Cp); + // movw %%ax,Ofs_AX(%%ebx) + G4M(OPERoverride,0x89,0x43,Ofs_AX,Cp); + // movw %%dx,Ofs_DX(%%ebx) + G4M(OPERoverride,0x89,0x53,Ofs_DX,Cp); + } + else { + // mull Ofs_EAX(%%ebx),%%eax + G3M(0xf7,0x63,Ofs_EAX,Cp); + // movl %%eax,Ofs_EAX(%%ebx) + G3M(0x89,0x43,Ofs_EAX,Cp); + // movl %%edx,Ofs_EDX(%%ebx) + G3M(0x89,0x53,Ofs_EDX,Cp); + } + G1(PUSHF,Cp); // flags back on stack + break; + case O_IMUL: + G1(POPF,Cp); // get flags from stack + if (mode & MBYTE) { + if ((mode&(IMMED|DATA16))==(IMMED|DATA16)) { + // imul $immed,%%ax,%%ax + G3M(OPERoverride,0x6b,0xc0,Cp); G1(IG->p0,Cp); + // movw %%ax,offs(%%ebx) + G4M(OPERoverride,0x89,0x43,IG->p1,Cp); + } + else if ((mode&(IMMED|DATA16))==IMMED) { + // imul $immed,%%eax,%%eax + G2M(0x6b,0xc0,Cp); G1(IG->p0,Cp); + // movl %%eax,offs(%%ebx) + G3M(0x89,0x43,IG->p1,Cp); + } + else { + // imul Ofs_AL(%%ebx),%%al + G3M(0xf6,0x6b,Ofs_AL,Cp); + // movw %%ax,Ofs_AX(%%ebx) + G4M(OPERoverride,0x89,0x43,Ofs_AX,Cp); + } + } + else if (mode&DATA16) { + if (mode&IMMED) { + // imul $immed,%%ax,%%ax + G3M(OPERoverride,0x69,0xc0,Cp); G2(IG->p0,Cp); + // movw %%ax,offs(%%ebx) + G4M(OPERoverride,0x89,0x43,IG->p1,Cp); + } + else if (mode&MEMADR) { + // imul offs(%%ebx),%%ax + G4M(OPERoverride,0x0f,0xaf,0x43,Cp); + G1(IG->p0,Cp); + // movw %%ax,offs(%%ebx) + G4M(OPERoverride,0x89,0x43,IG->p0,Cp); + } + else { + // imul Ofs_AX(%%ebx),%%ax + G4M(OPERoverride,0xf7,0x6b,Ofs_AX,Cp); + // movw %%ax,Ofs_AX(%%ebx) + G4M(OPERoverride,0x89,0x43,Ofs_AX,Cp); + // movw %%dx,Ofs_DX(%%ebx) + G4M(OPERoverride,0x89,0x53,Ofs_DX,Cp); + } + } + else { + if (mode&IMMED) { + // imul $immed,%%eax,%%eax + G2M(0x69,0xc0,Cp); G4(IG->p0,Cp); + // movl %%eax,offs(%%ebx) + G3M(0x89,0x43,IG->p1,Cp); + } + else if (mode&MEMADR) { + // imul offs(%%ebx),%%eax + G4M(0x0f,0xaf,0x43,IG->p0,Cp); + // movl %%eax,offs(%%ebx) + G3M(0x89,0x43,IG->p0,Cp); + } + else { + // imul Ofs_EAX(%%ebx),%%eax + G3M(0xf7,0x6b,Ofs_EAX,Cp); + // movl %%eax,Ofs_EAX(%%ebx) + G3M(0x89,0x43,Ofs_EAX,Cp); + // movl %%edx,Ofs_EDX(%%ebx) + G3M(0x89,0x53,Ofs_EDX,Cp); + } + } + G1(PUSHF,Cp); // flags back on stack + break; + + case O_DIV: { + G1(POPF,Cp); // get flags from stack + G2(0xc189,Cp); // movl %%eax,%%ecx + if (mode & MBYTE) { + // movw Ofs_AX(%%ebx),%%ax + G4M(OPERoverride,0x8b,0x43,Ofs_AX,Cp); + /* exception trap: save current PC */ + // movl $eip,Ofs_CR2(%%ebx) + G2M(0xc7,0x43,Cp); G1(Ofs_CR2,Cp); G4(IG->p0,Cp); + // div %%cl,%%al + G2M(0xf6,0xf1,Cp); + // movw %%ax,Ofs_AX(%%ebx) + G4M(OPERoverride,0x89,0x43,Ofs_AX,Cp); + } + else if (mode&DATA16) { + // movw Ofs_AX(%%ebx),%%ax + G4M(OPERoverride,0x8b,0x43,Ofs_AX,Cp); + // movw Ofs_DX(%%ebx),%%dx + G4M(OPERoverride,0x8b,0x53,Ofs_DX,Cp); + /* exception trap: save current PC */ + // movl $eip,Ofs_CR2(%%ebx) + G2(0x43c7,Cp); G1(Ofs_CR2,Cp); G4(IG->p0,Cp); + // div %%cx,%%ax + G3M(OPERoverride,0xf7,0xf1,Cp); + // movw %%ax,Ofs_AX(%%ebx) + G4M(OPERoverride,0x89,0x43,Ofs_AX,Cp); + // movw %%dx,Ofs_DX(%%ebx) + G4M(OPERoverride,0x89,0x53,Ofs_DX,Cp); + } + else { + // movl Ofs_EAX(%%ebx),%%eax + G3M(0x8b,0x43,Ofs_EAX,Cp); + // movl Ofs_EDX(%%ebx),%%edx + G3M(0x8b,0x53,Ofs_EDX,Cp); + /* exception trap: save current PC */ + // movl $eip,Ofs_CR2(%%ebx) + G2(0x43c7,Cp); G1(Ofs_CR2,Cp); G4(IG->p0,Cp); + // div %%ecx,%%eax + G2M(0xf7,0xf1,Cp); + // movl %%eax,Ofs_EAX(%%ebx) + G3M(0x89,0x43,Ofs_EAX,Cp); + // movl %%edx,Ofs_EDX(%%ebx) + G3M(0x89,0x53,Ofs_EDX,Cp); + } + G1(PUSHF,Cp); // flags back on stack + } + break; + case O_IDIV: { + G1(POPF,Cp); // get flags from stack + G2(0xc189,Cp); // movw %%eax,%%ecx + if (mode & MBYTE) { + // movw Ofs_AX(%%ebx),%%ax + G4M(OPERoverride,0x8b,0x43,Ofs_AX,Cp); + /* exception trap: save current PC */ + // movl $eip,Ofs_CR2(%%ebx) + G2(0x43c7,Cp); G1(Ofs_CR2,Cp); G4(IG->p0,Cp); + // idiv %%cl,%%al + G2M(0xf6,0xf9,Cp); + // movw %%ax,Ofs_AX(%%ebx) + G4M(OPERoverride,0x89,0x43,Ofs_AX,Cp); + } + else if (mode&DATA16) { + // movw Ofs_AX(%%ebx),%%ax + G4M(OPERoverride,0x8b,0x43,Ofs_AX,Cp); + // movw Ofs_DX(%%ebx),%%dx + G4M(OPERoverride,0x8b,0x53,Ofs_DX,Cp); + /* exception trap: save current PC */ + // movl $eip,Ofs_CR2(%%ebx) + G2(0x43c7,Cp); G1(Ofs_CR2,Cp); G4(IG->p0,Cp); + // idiv %%cx,%%ax + G3M(OPERoverride,0xf7,0xf9,Cp); + // movw %%ax,Ofs_AX(%%ebx) + G4M(OPERoverride,0x89,0x43,Ofs_AX,Cp); + // movw %%dx,Ofs_DX(%%ebx) + G4M(OPERoverride,0x89,0x53,Ofs_DX,Cp); + } + else { + // movl Ofs_EAX(%%ebx),%%eax + G3M(0x8b,0x43,Ofs_EAX,Cp); + // movl Ofs_EDX(%%ebx),%%edx + G3M(0x8b,0x53,Ofs_EDX,Cp); + /* exception trap: save current PC */ + // movl $eip,Ofs_CR2(%%ebx) + G2(0x43c7,Cp); G1(Ofs_CR2,Cp); G4(IG->p0,Cp); + // idiv %%ecx,%%eax + G2M(0xf7,0xf9,Cp); + // movl %%eax,Ofs_EAX(%%ebx) + G3M(0x89,0x43,Ofs_EAX,Cp); + // movl %%edx,Ofs_EDX(%%ebx) + G3M(0x89,0x53,Ofs_EDX,Cp); + } + G1(PUSHF,Cp); // flags back on stack + } + break; + + case O_CBWD: + // movl Ofs_EAX(%%ebx),%%eax + G3M(0x8b,0x43,Ofs_EAX,Cp); + if (mode & MBYTE) { /* 0x98: CBW,CWDE */ + if (mode & DATA16) { // AL->AX + // cbw + G2(0x9866,Cp); + // movw %%ax,Ofs_AX(%%ebx) + G4M(0x66,0x89,0x43,Ofs_AX,Cp); + } + else { // AX->EAX + // cwde + // movl %%eax,Ofs_EAX(%%ebx) + G4M(0x98,0x89,0x43,Ofs_EAX,Cp); + } + } + else if (mode & DATA16) { /* 0x99: AX->DX:AX */ + // cwd + G2(0x9966,Cp); + // movw %%dx,Ofs_DX(%%ebx) + G4M(0x66,0x89,0x53,Ofs_DX,Cp); + } + else { /* 0x99: EAX->EDX:EAX */ + // cdq + // movl %%edx,Ofs_EDX(%%ebx) + G4M(0x99,0x89,0x53,Ofs_EDX,Cp); + } + break; + case O_XLAT: + // movl OVERR_DS(%%ebx),%%edi + G2(0x7b8b,Cp); G1(IG->ovds,Cp); + // movzbl Ofs_AL(%%ebx),%%ecx + G4M(0x0f,0xb6,0x4b,Ofs_AL,Cp); + // movl Ofs_EBX(%%ebx),%%eax + G3M(0x8b,0x43,Ofs_EBX,Cp); + // leal (%%ecx,%%eax,1),%%ecx + G3(0x010c8d,Cp); + if (mode & ADDR16) { + // movzwl %%cx,%%ecx + G3(0xC9B70F,Cp); + } + // leal (%%ecx,%%edi,1),%%edi + G3M(0x8d,0x3c,0x39,Cp); + break; + + case O_ROL: + rcod = 0x00; goto shrot0; + case O_ROR: + rcod = 0x08; goto shrot0; + case O_RCL: + rcod = 0x10; goto shrot0; + case O_RCR: + rcod = 0x18; goto shrot0; + case O_SHL: + rcod = 0x20; goto shrot0; + case O_SHR: + rcod = 0x28; goto shrot0; + case O_SAR: + rcod = 0x38; +shrot0: + G1(0x9d,Cp); // get flags from stack + if (mode & MBYTE) { + // op al,1: d0 c0+r + // op al,n: c0 c0+r n + // op al,cl: d2 c0+r + if (mode & IMMED) { + unsigned char sh = IG->p0; + G1(sh==1? 0xd0:0xc0,Cp); + G1(0xc0 | rcod,Cp); + if (sh!=1) G1(sh,Cp); + } + else { + // movb Ofs_CL(%%ebx),%%cl + G3M(0x8a,0x4b,Ofs_CL,Cp); + // OPb %%cl,%%al + G1(0xd2,Cp); G1(0xc0 | rcod,Cp); + } + } + else { + // op (e)ax,1: (66) d1 c0+r + // op (e)ax,n: (66) c1 c0+r n + // op (e)ax,cl: (66) d3 c0+r + if (mode & IMMED) { + unsigned char sh = IG->p0; + Gen66(mode,Cp); G1(sh==1? 0xd1:0xc1,Cp); + G1(0xc0 | rcod,Cp); + if (sh!=1) G1(sh,Cp); + } + else { + // movb Ofs_CL(%%ebx),%%cl + G3M(0x8a,0x4b,Ofs_CL,Cp); + // OP{wl} %%cl,(%%edi) + Gen66(mode,Cp); + G1(0xd3,Cp); G1(0xc0 | rcod,Cp); + } + } + G1(0x9c,Cp); // flags back on stack + break; + + case O_OPAX: { /* used by DAA,DAS,AAA,AAS,AAM,AAD */ + // movl Ofs_EAX(%%ebx),%%eax + G4M(0x9d,0x8b,0x43,Ofs_EAX,Cp); +#ifdef __x86_64__ /* have to emulate all of them... */ + switch(IG->p1) { + case DAA: + case DAS: { + int op = (IG->p1 == DAS ? 0x28 : 0); + const unsigned char pseq[] = { + // pushf; mov %al,%cl; add $0x66,%al + 0x9c,0x88,0xc1,0x04,0x66, + // pushf; pop %rax; pop %rdx + 0x9c,0x58,0x5a, + // or %dl,%al; and $0x11,%al // combine AF/CF + 0x08,0xd0,0x24,0x11, + // mov %al,%dl; rol $4,%al + 0x88,0xc2,0xc0,0xc0,0x04, + // imul $6,%eax // multiply 0 CF 0 0 0 AF by 6 + 0x6b,0xc0,0x06}; + + GNX(Cp, pseq, sizeof(pseq)); + // add/sub %al,%cl; pushf // Combine flags from + G3M(op,0xc1,0x9c,Cp); + // or %dl,(%rsp) // add/sub with AF/CF + G3M(0x08,0x14,0x24,Cp); + // movb %%cl,Ofs_EAX(%%ebx) + G3M(0x88,0x4b,Ofs_EAX,Cp); + break; + } + case AAA: + case AAS: { + int op = (IG->p1 == AAS ? 0x28 : 0); + const unsigned char pseq[] = { + // pushf; mov %eax,%ecx; and 0xf,%al + 0x9c,0x89,0xc1,0x24,0x0f, + // add $6,%al; pop %edx + 0x04,0x06,0x5a, + // or %dl,%al; and $0xee,%dl // ~(AF|CF) + 0x08,0xd0,0x80,0xe2,0xee, + // and $0x10,%al; xchg %eax,%ecx; jz 1f + 0x24,0x10,0x91,0x74,0x07}; + GNX(Cp, pseq, sizeof(pseq)); + + // add/sub $0x106, %ax + G4M(0x66,op+0x05,0x06,0x01,Cp); + // or $0x11,%dl; (AF|CF) 1: and $0xf,%al + G3M(0x80,0xca,0x11,Cp); G2M(0x24,0x0f,Cp); + // push %rdx; movl %%eax,Ofs_EAX(%%ebx) + G4M(0x52,0x89,0x43,Ofs_EAX,Cp); + break; + } + case AAM: + // mov $0,%ah; mov p2,%cl + G4M(0xb4,0x00,0xb1,IG->p2,Cp); + // div %cl; xchg %al,%ah + G4M(0xf6,0xf1,0x86,0xc4,Cp); + // orb %al,%al (for flags) + G2M(0x08,0xc0,Cp); + // movl %%eax,Ofs_EAX(%%ebx) + G4M(0x89,0x43,Ofs_EAX,0x9c,Cp); + break; + case AAD: + // mov %al,%cl; mov %ah,%al + G4M(0x88,0xc1,0x88,0xe0,Cp); + // mov p2,%ah; mul %ah + G4M(0xb4,IG->p2,0xf6,0xe4,Cp); + // add %cl,%al; mov $0,%ah + G4M(0x00,0xc8,0xb4,0x00,Cp); + // movl %%eax,Ofs_EAX(%%ebx) + G4M(0x89,0x43,Ofs_EAX,0x9c,Cp); + break; + default: + error("Unimplemented O_OPAX instruction\n"); + leavedos_main(99); + } +#else + // get n>0,n<3 bytes from parameter stack + G1(IG->p1,Cp); + if (IG->p0==2) { G1(IG->p2,Cp); } + // movl %%eax,Ofs_EAX(%%ebx) + G4M(0x89,0x43,Ofs_EAX,0x9c,Cp); +#endif + } + break; + + case O_PUSH: { + const unsigned char pseq16[] = { + // movl Ofs_XSS(%%ebx),%%esi + 0x8b,0x73,Ofs_XSS, + // movl Ofs_ESP(%%ebx),%%ecx + 0x8b,0x4b,Ofs_ESP, + // leal -2(%%ecx),%%ecx + 0x8d,0x49,0xfe, + // 16-bit stack seg w/underflow (RM) + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edx + 0x8d,0x14,0x0e, + // movw %%ax,(%%edx,%%ebp,1) + 0x66,0x89,0x04,0x2a, + // do 16-bit PM apps exist which use a 32-bit stack seg? +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + // movl %%ecx,Ofs_ESP(%%ebx) + 0x89,0x4b,Ofs_ESP + }; + const unsigned char pseq32[] = { + // movl Ofs_XSS(%%ebx),%%esi + 0x8b,0x73,Ofs_XSS, + // movl Ofs_ESP(%%ebx),%%ecx + 0x8b,0x4b,Ofs_ESP, + // leal -4(%%ecx),%%ecx + 0x8d,0x49,0xfc, + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edx + 0x8d,0x14,0x0e, + // movl %%eax,(%%edx,%%ebp,1) + 0x89,0x04,0x2a, +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + // movl %%ecx,Ofs_ESP(%%ebx) + 0x89,0x4b,Ofs_ESP + }; + const unsigned char *p; int sz; + if (mode&DATA16) p=pseq16,sz=sizeof(pseq16); + else p=pseq32,sz=sizeof(pseq32); + GNX(Cp, p, sz); + } break; + +/* PUSH derived (sub-)sequences: */ + case O_PUSH1: { + const unsigned char pseq[] = { + // movl Ofs_ESP(%%ebx),%%ecx + 0x8b,0x4b,Ofs_ESP, + // movl Ofs_XSS(%%ebx),%%esi + 0x8b,0x73,Ofs_XSS, + }; + GNX(Cp, pseq, sizeof(pseq)); + } break; + + case O_PUSH2: { /* register push only */ + const unsigned char pseq16[] = { + // movl offs(%%ebx),%%eax +/*00*/ 0x8b,0x43,0x00, + // leal -2(%%ecx),%%ecx + 0x8d,0x49,0xfe, + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edx + 0x8d,0x14,0x0e, + // movw %%ax,(%%edx,%%ebp,1) + 0x66,0x89,0x04,0x2a, +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + }; + const unsigned char pseq32[] = { + // movl offs(%%ebx),%%eax +/*00*/ 0x8b,0x43,0x00, + // leal -4(%%ecx),%%ecx + 0x8d,0x49,0xfc, + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edx + 0x8d,0x14,0x0e, + // movl %%eax,(%%edx,%%ebp,1) + 0x89,0x04,0x2a, +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + }; + const unsigned char *p; + unsigned char *q; + int sz; + if (mode&DATA16) p=pseq16,sz=sizeof(pseq16); + else p=pseq32,sz=sizeof(pseq32); + q=Cp; GNX(Cp, p, sz); + q[2] = IG->p0; + } break; + + case O_PUSH3: + // movl %%ecx,Ofs_ESP(%%ebx) + G3M(0x89,0x4b,Ofs_ESP,Cp); + break; + + case O_PUSH2F: { + const unsigned char pseqpre[] = { + // movl Ofs_XSS(%%ebx),%%esi + 0x8b,0x73,Ofs_XSS, + // movl Ofs_ESP(%%ebx),%%ecx + 0x8b,0x4b,Ofs_ESP, + // leal -4(%%ecx),%%ecx +/*08*/ 0x8d,0x49,0xfc, + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // movl (%%esp),%%edx (get flags on stack) + 0x8b,0x14,0x24, + // movl Ofs_FLAGS(%%ebx),%%eax + 0x8b,0x43,Ofs_EFLAGS, + // andw EFLAGS_CC,%%dx (0x8d5: OF/SF/ZF/AF/PF/CF) + 0x66,0x81,0xe2,0xd5,0x08, + // andw ~EFLAGS_CC,%%ax + 0x66,0x25,0x2a,0xf7, + // orw %%dx,%%ax + 0x66,0x09,0xd0, + }; +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + const unsigned char pseqpost[] = { + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, + }; +#endif + unsigned char *q=Cp; + GNX(Cp, pseqpre, sizeof(pseqpre)); + if (mode&DATA16) q[8] = 0xfe; /* use -2 in lea ins */ +#if 0 // unused "extended PVI", if used should move to separate op + if (!V86MODE() && IOPL < 3 && (TheCPU.cr[4] & CR4_PVI)) { + /* This solves the DOSX 'System test 8' error. + * The virtualized IF is pushed instead of the + * real one (which is always 1). This way, tests + * on the pushed value of the form + * cli + * pushf + * test 0x200,(esp) + * don't fail anymore. It is not clear, apart this + * special test case, whether pushing the virtual + * IF is actually useful; probably not. In any + * case, POPF ignores this IF on stack. + * Since PUSHF doesn't trap in PM, non-cpuemued + * dosemu will always fail this particular test. + */ + // rcr $10,%%eax (IF->cy) + G3M(0xc1,0xd8,0x0a,Cp); + // bt $19,(_EFLAGS-TheCPU)(%ebx) (test for VIF) + G3M(0x0f,0xba,0xa3,Cp); + /* relative ebx offset works on x86-64 too */ + G4((unsigned char *)&_EFLAGS-CPUOFFS(0),Cp); + // (19 from bt); rcl $10,%%eax + G4M(0x13,0xc1,0xd0,0x0a,Cp); + } +#endif + // leal (%%esi,%%ecx,1),%%edx + G3M(0x8d,0x14,0x0e,Cp); + if (mode&DATA16) { + // movw %%ax,(%%edx,%%ebp,1) + G4M(0x66,0x89,0x04,0x2a,Cp); + } else { + // andl RETURN_MASK|EFLAGS_IF,%%eax + G1(0x25,Cp); G4(RETURN_MASK|EFLAGS_IF,Cp); + // movl %%eax,(%%edx,%%ebp,1) + G3M(0x89,0x04,0x2a,Cp); + } +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + GNX(Cp, pseqpost, sizeof(pseqpost)); +#endif + // movl %%ecx,Ofs_ESP(%%ebx) + G3M(0x89,0x4b,Ofs_ESP,Cp); + } break; + + case O_PUSHI: { + const unsigned char pseq16[] = { + // movw $immed,%%ax +/*00*/ 0xb8,0,0,0,0, + // movl Ofs_XSS(%%ebx),%%esi + 0x8b,0x73,Ofs_XSS, + // movl Ofs_ESP(%%ebx),%%ecx + 0x8b,0x4b,Ofs_ESP, + // leal -2(%%ecx),%%ecx + 0x8d,0x49,0xfe, + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edx + 0x8d,0x14,0x0e, + // movw %%ax,(%%edx,%%ebp,1) + 0x66,0x89,0x04,0x2a, +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + // movl %%ecx,Ofs_ESP(%%ebx) + 0x89,0x4b,Ofs_ESP + }; + const unsigned char pseq32[] = { + // movl $immed,%%eax +/*00*/ 0xb8,0,0,0,0, + // movl Ofs_XSS(%%ebx),%%esi + 0x8b,0x73,Ofs_XSS, + // movl Ofs_ESP(%%ebx),%%ecx + 0x8b,0x4b,Ofs_ESP, + // leal -4(%%ecx),%%ecx + 0x8d,0x49,0xfc, + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edx + 0x8d,0x14,0x0e, + // movw %eax,(%%edx,%%ebp,1) + 0x89,0x04,0x2a, +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + // movl %%ecx,Ofs_ESP(%%ebx) + 0x89,0x4b,Ofs_ESP + }; + const unsigned char *p; + unsigned char *q; + int sz; + if (mode&DATA16) { + p = pseq16,sz=sizeof(pseq16); + } + else { + p = pseq32,sz=sizeof(pseq32); + } + q=Cp; GNX(Cp, p, sz); + *((int *)(q+1)) = IG->p0; + } break; + + case O_POP: { + const unsigned char pseq16[] = { + // movl Ofs_XSS(%%ebx),%%esi + 0x8b,0x73,Ofs_XSS, + // movl Ofs_ESP(%%ebx),%%ecx + 0x8b,0x4b,Ofs_ESP, + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edx + 0x8d,0x14,0x0e, + // movw (%%edx,%%ebp,1),%%ax, + 0x66,0x8b,0x04,0x2a, + // leal 2(%%ecx),%%ecx +/*10*/ 0x8d,0x89,0x02,0x00,0x00,0x00, +#ifdef STACK_WRAP_MP /* mask after incrementing */ + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, +#endif +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + // movl %%ecx,Ofs_ESP(%%ebx) + 0x89,0x4b,Ofs_ESP + }; + const unsigned char pseq32[] = { + // movl Ofs_XSS(%%ebx),%%esi + 0x8b,0x73,Ofs_XSS, + // movl Ofs_ESP(%%ebx),%%ecx + 0x8b,0x4b,Ofs_ESP, + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edx + 0x8d,0x14,0x0e, + // movl (%%edx,%%ebp,1),%%eax + 0x90,0x8b,0x04,0x2a, + // leal 4(%%ecx),%%ecx +/*10*/ 0x8d,0x89,0x04,0x00,0x00,0x00, +#ifdef STACK_WRAP_MP /* mask after incrementing */ + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, +#endif +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + // movl %%ecx,Ofs_ESP(%%ebx) + 0x89,0x4b,Ofs_ESP + }; + const unsigned char *p; int sz; + unsigned char *q; + if (mode&DATA16) p=pseq16,sz=sizeof(pseq16); + else p=pseq32,sz=sizeof(pseq32); + // for popping into memory the sequence is: + // first do address calculation, then pop, + // then store data, and last adjust stack + q = Cp; GNX(Cp, p, sz); + if (mode&MRETISP) + /* adjust stack after pop */ + *(int32_t *)(q+0x12) += IG->p0; + } break; + +/* POP derived (sub-)sequences: */ + case O_POP1: { + const unsigned char pseq[] = { + // movl Ofs_XSS(%%ebx),%%esi + 0x8b,0x73,Ofs_XSS, + // movl Ofs_ESP(%%ebx),%%ecx + 0x8b,0x4b,Ofs_ESP + }; + GNX(Cp, pseq, sizeof(pseq)); + } break; + + case O_POP2: { + const unsigned char pseq16[] = { + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edx + 0x8d,0x14,0x0e, + // movw (%%edx,%%ebp,1),%%ax + 0x66,0x8b,0x04,0x2a, + // movw %%ax,offs(%%ebx) +/*0a*/ 0x66,0x89,0x43,0x00, + // leal 2(%%ecx),%%ecx + 0x8d,0x49,0x02, +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + }; + const unsigned char pseq32[] = { + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edx + 0x8d,0x14,0x0e, + // movl (%%edx,%%ebp,1),%%eax + 0x90,0x8b,0x04,0x2a, + // movl %%eax,offs(%%ebx) +/*0a*/ 0x90,0x89,0x43,0x00, + // leal 4(%%ecx),%%ecx + 0x8d,0x49,0x04, +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + }; + const unsigned char *p; + unsigned char *q; + int sz; + if (mode&DATA16) p=pseq16,sz=sizeof(pseq16); + else p=pseq32,sz=sizeof(pseq32); + // for popping into memory the sequence is: + // first do address calculation, then pop, + // then store data, and last adjust stack + q=Cp; GNX(Cp, p, sz); + q[0x0d] = IG->p0; + if (mode&MPOPRM) { + // NOP the register write, save ecx into esi + // which is preserved in CPatches + *(uint32_t *)(q+0x0a) = 0x90909090; + // Use leal {2|4}(%%ecx),%%esi + q[0x0f] = 0x71; +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // use orl %%edx,%%esi + q[sz-1] = 0xd6; +#endif + } + } break; + + case O_POP3: + // movl %%e{si|cx},Ofs_ESP(%%ebx) + G3M(0x89,(mode&MPOPRM)?0x73:0x4b,Ofs_ESP,Cp); + break; + + case O_LEAVE: { + const unsigned char pseq16[] = { + // movzwl Ofs_BP(%%ebx),%%ecx + 0x0f,0xb7,0x4b,Ofs_EBP, + // movl Ofs_XSS(%%ebx),%%esi + 0x8b,0x73,Ofs_XSS, + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edx + 0x8d,0x14,0x0e, + // movw (%%edx,%%ebp,1),%%ax + 0x66,0x8b,0x04,0x2a, + // movw %%ax,Ofs_BP(%%ebx) + 0x66,0x89,0x43,Ofs_BP, + // leal 2(%%ecx),%%ecx + 0x8d,0x49,0x02, +#ifdef STACK_WRAP_MP /* mask after incrementing */ + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, +#endif +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + // movl %%ecx,Ofs_ESP(%%ebx) + 0x89,0x4b,Ofs_ESP + }; + const unsigned char pseq32[] = { + // movl Ofs_EBP(%%ebx),%%ecx + 0x8b,0x4b,Ofs_EBP, + // movl Ofs_XSS(%%ebx),%%esi + 0x8b,0x73,Ofs_XSS, + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edx + 0x8d,0x14,0x0e, + // movl (%%edx,%%ebp,1),%%eax + 0x8b,0x04,0x2a, + // movl %%eax,Ofs_EBP(%%ebx) + 0x89,0x43,Ofs_EBP, + // leal 4(%%ecx),%%ecx + 0x8d,0x49,0x04, +#ifdef STACK_WRAP_MP /* mask after incrementing */ + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, +#endif +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + // movl %%ecx,Ofs_ESP(%%ebx) + 0x89,0x4b,Ofs_ESP + }; + const unsigned char *p; int sz; + if (mode&DATA16) p=pseq16,sz=sizeof(pseq16); + else p=pseq32,sz=sizeof(pseq32); + GNX(Cp, p, sz); + } break; + + case O_INT: { + unsigned char intno = IG->p0; + int jpc = IG->p1; + // Check bitmap, GPF if revectored, else use intno*4 + // bt intno,Ofs_int_revectored(%ebx) + // Code offset of TheCPU.int_revectored.__map[intno>>5] + // as Ofs_int_revectored[intno>>3] aligned to 4. + // See http://x86.dapsen.com/html/file_module_x86_id_22.html + G3M(0x0f,0xba,0xa3,Cp); G4(Ofs_int_revectored+((intno>>3)&~3),Cp); + // remaining 5 intno bits within __map dword from bt + G1(intno&0x1f,Cp); + // jnc skip return + G2M(0x73,TAILSIZE+7,Cp); + // movb EXCP0D_GPF, Ofs_ERR(%%ebx) + G2M(0xc6,0x83,Cp); G4(Ofs_ERR,Cp); G1(EXCP0D_GPF,Cp); + // movl {exit_addr},%%eax; pop %%edx; ret + G1(0xb8,Cp); G4(jpc,Cp); G2M(0x5a,0xc3,Cp); + // address to call in edi + // movl $(inum*4), %edi + G1(0xbf,Cp); G4(intno*4, Cp); + break; + } + + case O_MOVS_SetA: { + /* use edi for loads unless MOVSDST or REP is set */ + unsigned char modrm = mode&(MREP|MREPNE|MOVSDST) ? 0x73 : 0x7b; + unsigned char modrm2 = mode&(MREP|MREPNE|MOVSDST) ? 0xee : 0xef; + if (mode&ADDR16) { + /* The CX load has to be before the address reloads */ + if (mode&(MREP|MREPNE)) { + // movzwl Ofs_CX(%%ebx),%%ecx + G4M(0x0f,0xb7,0x4b,Ofs_CX,Cp); + rep_retry_ptr = Cp; + } + if (mode&MOVSSRC) { + // movzwl Ofs_SI(%%ebx),%%e[sd]i + G4M(0x0f,0xb7,modrm,Ofs_SI,Cp); + if(mode & (MREPNE|MREP)) + { + /* EAX: iterations possible until address overflow + if DF is set. */ + // movl %%esi,%%eax + G2M(MOVwfrm,0xF0,Cp); + if(!(mode & MBYTE)) + { + if(mode & DATA16) + { + // shrl $1,%%eax + G2M(SHIFTw,0xE8,Cp); + } + else + { + // shrl $2,%%eax + G3M(SHIFTwi,0xE8,2,Cp); + } + } + // incl %%eax +#ifdef __x86_64__ // 0x40 is a REX byte, not inc + G2M(0xff,0xc0,Cp); +#else + G1(INCax,Cp); +#endif + } + // addl OVERR_DS(%%ebx),%%e[sd]i + G3M(0x03,modrm,IG->ovds,Cp); + if (mode&(MREP|MREPNE)) { + // addl %%[re]bp,%%[re][sd]i + Gen48(Cp); G2M(0x01,modrm2,Cp); + } + } + if (mode&MOVSDST) { + // movzwl Ofs_DI(%%ebx),%%edi + G4M(0x0f,0xb7,0x7b,Ofs_DI,Cp); + if(mode & (MREPNE|MREP)) + { + /* EDX: iterations possible until address overflow + if DF is set. */ + // movl %%edi,%%edx + G2M(MOVwfrm,0xFA,Cp); + if(!(mode & MBYTE)) + { + if(mode & DATA16) + { + // shrl $1,%%edx + G2M(SHIFTw,0xEA,Cp); + } + else + { + // shrl $2,%%edx + G3M(SHIFTwi,0xEA,2,Cp); + } + } + // incl %%edx +#ifdef __x86_64__ // 0x42 is a REX byte, not inc + G2M(0xff,0xc2,Cp); +#else + G1(INCdx,Cp); +#endif + } + // addl Ofs_XES(%%ebx),%%edi + G3M(0x03,0x7b,Ofs_XES,Cp); + if (mode&(MREP|MREPNE)) { + // addl %%[re]bp,%%[re]di + Gen48(Cp); G2M(0x01,0xef,Cp); + } + } + if (mode&(MREP|MREPNE)) { + /* Address overflow detection */ + // testl $4,Ofs_EFLAGS+1(%%ebx) + G4M(GRP1brm,0x43,Ofs_EFLAGS+1,0x4,Cp); + // jnz 0f (distance is 8 bytes per limit to adjust) + G2M(JNE_JNZ,(mode&(MOVSDST|MOVSSRC)) == (MOVSDST|MOVSSRC) ? + 0x10:0x08,Cp); + /* correct for cleared DF */ + if(mode&MOVSSRC) + { + // negl %%eax + G2M(GRP1wrm,0xD8,Cp); + // addl $(0x10000/opsize+1),%%eax + G2M(IMMEDwrm,0xC0,Cp); G4(0x10000/OPSIZE(mode)+1,Cp); + } + if(mode&MOVSDST) + { + // negl %%edx + G2M(GRP1wrm,0xDA,Cp); + // addl $(0x10000/opsize+1),%%edx + G2M(IMMEDwrm,0xC2,Cp); G4(0x10000/OPSIZE(mode)+1,Cp); + } + // 0: + /* consolidate limits to edx */ + switch(mode&(MOVSDST|MOVSSRC)) + { + case MOVSDST: + /* nothing to do, limit already in edx */ + break; + case MOVSSRC: + /* limit in eax, want it in edx */ + // xchg %%eax,%%edx + G1(XCHGdx,Cp); + break; + case MOVSSRC | MOVSDST: + /* smaller limit to edx */ + // cmp %%eax,%%edx + G2M(CMPwtrm,0xD0,Cp); + // jb 0f + G2M(JB_JNAE,0x01,Cp); + // xchg %%eax,%%edx + G1(XCHGdx,Cp); + break; + } + // cmp %%ecx,%%edx + G2M(CMPwfrm,0xCA,Cp); + // jbe 0f + G2M(JBE_JNA,0x02,Cp); + // mov %%ecx,%%edx + G2M(MOVwfrm,0xCA,Cp); + // 0: + // xchg %%ecx,%%edx + G2M(XCHGwrm,0xCA,Cp); + // sub %%ecx, %%edx + G2M(SUBwfrm,0xCA,Cp); + } + } + else { + if (mode&MOVSSRC) { + // movl OVERR_DS(%%ebx),%%e[sd]i + G3M(0x8b,modrm,IG->ovds,Cp); + // addl Ofs_ESI(%%ebx),%%e[sd]i + G3M(0x03,modrm,Ofs_ESI,Cp); + } + if (mode&MOVSDST) { + // movl Ofs_XES(%%ebx),%%edi + G3M(0x8b,0x7b,Ofs_XES,Cp); + // addl Ofs_EDI(%%ebx),%%edi + G3M(0x03,0x7b,Ofs_EDI,Cp); + } + if (mode&(MREP|MREPNE)) { + // movl Ofs_ECX(%%ebx),%%ecx + G3M(0x8b,0x4b,Ofs_ECX,Cp); + if (mode&MOVSSRC) { + // addl %%[re]bp,%%[re][sd]i + Gen48(Cp); G2M(0x01,modrm2,Cp); + } + if (mode&MOVSDST) { + // addl %%[re]bp,%%[re]di + Gen48(Cp); G2M(0x01,0xef,Cp); + } + } + } } + break; + + case O_MOVS_MovD: + GetDF(Cp); + G3M(NOP,NOP,REP,Cp); + if (mode&MBYTE) { G1(MOVSb,Cp); } + else { + Gen66(mode,Cp); + G1(MOVSw,Cp); + } + G1(CLD,Cp); + break; + case O_MOVS_LodD: + GetDF(Cp); + G3M(NOP,NOP,REP,Cp); + if (mode&MBYTE) { G1(LODSb,Cp); } + else { + Gen66(mode,Cp); + G1(LODSw,Cp); + } + G1(CLD,Cp); + break; + case O_MOVS_StoD: + GetDF(Cp); + G3M(NOP,NOP,REP,Cp); + if (mode&MBYTE) { G1(STOSb,Cp); } + else { + Gen66(mode,Cp); + G1(STOSw,Cp); + } + G1(CLD,Cp); + break; + case O_MOVS_ScaD: + CpTemp = NULL; + G2M(JCXZ,00,Cp); + // Pointer to the jecxz distance byte + CpTemp = Cp-1; + GetDF(Cp); + G2M(NOP,NOP,Cp); + G1((mode&MREP)?REP:REPNE,Cp); + if (mode&MBYTE) { G1(SCASb,Cp); } + else { + Gen66(mode,Cp); + G1(SCASw,Cp); + } + G3M(CLD,POPsi,PUSHF,Cp); // replace flags back on stack,esi=dummy + *CpTemp = (Cp-(CpTemp+1)); + break; + case O_MOVS_CmpD: + if(!(mode & (MREP|MREPNE))) { + // assumes eax=(%%esi) + // mov %%eax, %%edx + G2M(0x89,0xc2,Cp); + // mov (%%edi,%%ebp,1), %%{e}a[xl] + if (mode&MBYTE) { + G4M(0x8a,0x04,0x2f,0x90,Cp); + } + else if (mode&DATA16) { + G4M(0x66,0x8b,0x04,0x2f,Cp); + } + else { + G4M(0x8b,0x04,0x2f,0x90,Cp); + } + G2(0x9090,Cp); + // cmp %%eax, %%edx + if (mode&MBYTE) { + G2M(0x38,0xc2,Cp); + } + else { + Gen66(mode,Cp); + G2M(0x39,0xc2,Cp); + } + // replace flags back on stack,eax=dummy + G2M(POPax,PUSHF,Cp); + break; + } + CpTemp = NULL; + G2M(JCXZ,00,Cp); + // Pointer to the jecxz distance byte + CpTemp = Cp-1; + GetDF(Cp); + G2M(NOP,NOP,Cp); + G1((mode&MREP)?REP:REPNE,Cp); + if (mode&MBYTE) { G1(CMPSb,Cp); } + else { + Gen66(mode,Cp); + G1(CMPSw,Cp); + } + G3M(CLD,POPax,PUSHF,Cp); // replace flags back on stack,eax=dummy + *CpTemp = (Cp-(CpTemp+1)); + break; + + case O_MOVS_SavA: + if (!(mode&(MREP|MREPNE))) { + // %%edx set to DF's increment + // movsbl Ofs_DF_INCREMENTS+OPSIZEBIT(mode)(%%ebx),%%edx + G4M(0x0f,0xbe,0x53,Ofs_DF_INCREMENTS+OPSIZEBIT(mode),Cp); + if(mode & MOVSSRC) { + if (mode & ADDR16) + G1(0x66,Cp); + // add{wl} %{e}dx,Ofs_SI(%%ebx) + G3M(0x01,0x53,Ofs_SI,Cp); + } + if(mode & MOVSDST) { + if (mode & ADDR16) + G1(0x66,Cp); + // add{wl} %{e}dx,Ofs_DI(%%ebx) + G3M(0x01,0x53,Ofs_DI,Cp); + } + } + else if (mode&ADDR16) { + if(mode & MREPCOND) + { + /* it is important to *NOT* destroy the flags here, so + use lea instead of add. Flags are needed for termination + detection */ + // lea 0(%%edx,%%ecx),%%ecx ; add remaining to cx + G3M(0x8D,0x0C,0x11,Cp); + /* terminate immediately if rep was stopped by flags */ + // j[n]z 0f + G2M((mode&MREP)?JE_JZ:JNE_JNZ,0x02,Cp); + // xor %%edx,%%edx ; clear remaining + G2M(XORwtrm,0xD2,Cp); + // 0: + } + else if(mode & (MREP|MREPNE)) + { + /* use shorter add instruction for nonconditional reps */ + // add %%edx,%%ecx + G2M(ADDwtrm,0xCA,Cp); + } + if (mode&MOVSSRC) { + // esi = base1 + CPU_(e)SI +- n + // subl %%[re]bp,%%[re]si + Gen48(Cp); G2M(0x29,0xee,Cp); + // subl OVERR_DS(%%ebx),%%esi + G2(0x732b,Cp); G1(IG->ovds,Cp); + // movw %%si,Ofs_SI(%%ebx) + G4M(0x66,0x89,0x73,Ofs_SI,Cp); + } + if (mode&MOVSDST) { + // edi = base2 + CPU_(e)DI +- n + // subl %%[re]bp,%%[re]di + Gen48(Cp); G2M(0x29,0xef,Cp); + // subl Ofs_XES(%%ebx),%%edi + G3M(0x2b,0x7b,Ofs_XES,Cp); + // movw %%di,Ofs_DI(%%ebx) + G4M(0x66,0x89,0x7b,Ofs_DI,Cp); + } + // continue after SI/DI overflow; store ecx + if (mode&(MREP|MREPNE)) { + unsigned char * jmpbackbase; + // or %%edx,%%edx + G2M(ORwtrm,0xD2,Cp); + // jnz retry + jmpbackbase = Cp; + G2M(JNE_JNZ,(rep_retry_ptr-jmpbackbase-2)&0xFF,Cp); + // movw %%cx,Ofs_CX(%%ebx) + G4M(0x66,0x89,0x4b,Ofs_CX,Cp); + } + } + else { + if (mode&(MREP|MREPNE)) { + // movl %%ecx,Ofs_ECX(%%ebx) + G3M(0x89,0x4b,Ofs_ECX,Cp); + } + if (mode&MOVSSRC) { + // esi = base1 + CPU_(e)SI +- n + // subl %%[re]bp,%%[re]si + Gen48(Cp); G2M(0x29,0xee,Cp); + // subl OVERR_DS(%%ebx),%%esi + G2(0x732b,Cp); G1(IG->ovds,Cp); + // movl %%esi,Ofs_ESI(%%ebx) + G3M(0x89,0x73,Ofs_ESI,Cp); + } + if (mode&MOVSDST) { + // edi = base2 + CPU_(e)DI +- n + // subl %%[re]bp,%%[re]di + Gen48(Cp); G2M(0x29,0xef,Cp); + // subl Ofs_XES(%%ebx),%%edi + G3M(0x2b,0x7b,Ofs_XES,Cp); + // movl %%edi,Ofs_EDI(%%ebx) + G3M(0x89,0x7b,Ofs_EDI,Cp); + } + } + break; + + case O_SLAHF: + rcod = IG->p0; // 0=LAHF 1=SAHF + if (rcod==0) { /* LAHF */ + // movb 0(%%esp),%%al + G3M(0x8a,0x04,0x24,Cp); + // movb %%al,Ofs_AH(%%ebx) + G3M(0x88,0x43,Ofs_AH,Cp); + } + else { /* SAHF */ + // movb Ofs_AH(%%ebx),%%al + G3M(0x8a,0x43,Ofs_AH,Cp); + // movb %%al,0(%%esp) + G3M(0x88,0x04,0x24,Cp); + } + break; + case O_SETFL: { + unsigned char o1 = IG->p0; + switch(o1) { // these are direct on x86 + case CMC: // xorb $1,0(%%esp) + G4M(0x80,0x34,0x24,0x01,Cp); break; + case CLC: // andb $0xfe,(%%esp) + G4M(0x80,0x24,0x24,0xfeu,Cp); break; + case STC: // orb $1,0(%%esp) + G4M(0x80,0x0c,0x24,0x01,Cp); break; + case CLD: + // andb $0xfb,EFLAGS+1(%%ebx) + G4M(0x80,0x63,Ofs_EFLAGS+1,0xfbu,Cp); + // movl $0x040201,DF_INCREMENTS(%%ebx) + G3M(0xc7,0x43,Ofs_DF_INCREMENTS,Cp); + G4(0x040201,Cp); + break; + case STD: + // orb $4,EFLAGS+1(%%ebx) + G4M(0x80,0x4b,Ofs_EFLAGS+1,0x04,Cp); + // movl $0xfcfeff,DF_INCREMENTS(%%ebx) + G3M(0xc7,0x43,Ofs_DF_INCREMENTS,Cp); + G4(0xfcfeff,Cp); + break; + case CLI: + // andb $0xfd,EFLAGS+1(%%ebx) + G4M(0x80,0x63,Ofs_EFLAGS+1,~(uint8_t)(EFLAGS_IF>>8),Cp); + break; + case INT: + // clear IF & TF + // andb $0xfc,EFLAGS+1(%%ebx) + G4M(0x80,0x63,Ofs_EFLAGS+1, + ~(uint8_t)((EFLAGS_IF|EFLAGS_TF)>>8),Cp); + break; + } } + break; + case O_BSWAP: { + // movl offs(%%ebx),%%eax + G3M(0x8b,0x43,IG->p0,Cp); + // bswap %%eax + G2M(0x0f,0xc8,Cp); + // movl %%eax,offs(%%ebx) + G3M(0x89,0x43,IG->p0,Cp); + } + break; + case O_SETCC: { + unsigned char n = IG->p0; + PopPushF(Cp); // get flags from stack + // setcc %%al + G3M(0x0f,(0x90|(n&15)),0xc0,Cp); + } + break; + case O_BITOP: { + unsigned char n = IG->p0; + G1(0x9d,Cp); // get flags from stack + switch (n) { + case 0x03: /* BT */ + case 0x0b: /* BTS */ + case 0x13: /* BTR */ + case 0x1b: /* BTC */ + if (mode&DATA16) { + // movzwl offs(%%ebx),%%edx + G4M(0x0f,0xb7,0x53,IG->p1,Cp); + } + else { + // movl offs(%%ebx),%%edx + G3M(0x8b,0x53,IG->p1,Cp); + } + if (mode & RM_REG) { + // OP{wl} %%{e}dx,%%{e}ax + Gen66(mode,Cp); G3M(0x0f,(n+0xa0),0xd0,Cp); + } + else { + /* add bit offset to effective address */ + if (mode&DATA16) { + // shrl $4, %%edx + G3M(0xc1,0xea,0x04,Cp); + // leal (%%edi,%%edx,2), %%edi + G3M(0x8d,0x3c,0x57,Cp); + } else { + // shrl $5, %%edx + G3M(0xc1,0xea,0x05,Cp); + // leal (%%edi,%%edx,4), %%edi + G3M(0x8d,0x3c,0x97,Cp); + } + } + break; + case 0x1c: /* BSF */ + case 0x1d: /* BSR */ + // OP{wl} %%{e}ax,%%{e}dx + Gen66(mode,Cp); G3M(0x0f,(n+0xa0),0xd0,Cp); + // jz 1f + G2M(0x74,(mode&DATA16)?0x04:0x03,Cp); + // mov{wl} %%{e}dx,offs(%%ebx) 1: + Gen66(mode,Cp); G3M(0x89,0x53,IG->p1,Cp); + break; + case 0x20: /* BT imm8 */ + case 0x28: /* BTS imm8 */ + case 0x30: /* BTR imm8 */ + case 0x38: /* BTC imm8 */ + // OP{wl} $immed,%%{e}ax + Gen66(mode,Cp); G4M(0x0f,0xba,(n|0xc0),IG->p1,Cp); + break; + } + G1(0x9c,Cp); // flags back on stack + } break; + + case O_SHFD: { + unsigned char l_r = IG->p0; + G1(0x9d,Cp); // get flags from stack + // mov{wl} offs(%%ebx),%%{e}dx + Gen66(mode,Cp); G3M(0x8b,0x53,IG->p1,Cp); + if (mode & IMMED) { + unsigned char shc = IG->p2; + // sh{lr}d $immed,%%{e}dx,%%{e}ax + Gen66(mode,Cp); G4M(0x0f,(0xa4|l_r),0xd0,shc,Cp); + } + else { + // movl Ofs_ECX(%%ebx),%%ecx + G3M(0x8b,0x4b,Ofs_ECX,Cp); + // sh{lr}d %%cl,%%{e}dx,%%{e}ax + Gen66(mode,Cp); G3M(0x0f,(0xa5|l_r),0xd0,Cp); + } + G1(0x9c,Cp); // flags back on stack + } break; + + case O_RDTSC: { + // rdtsc + G2(0x310f,Cp); +#if 0 + if (eTimeCorrect >= 0) { + // movl %%eax,%%ecx + // movl %%edx,%%edi + G4(0xd789c189,Cp); + // subl TimeStartExec.t.tl(%%ebx),%%eax + // sbbl TimeStartExec.t.th(%%ebx),%%edx + G2(0x832b,Cp); + G4((unsigned char *)&TimeStartExec.t.tl-CPUOFFS(0),Cp); + G2(0x931b,Cp); + G4((unsigned char *)&TimeStartExec.t.th-CPUOFFS(0),Cp); + // addl TheCPU.EMUtime(%%ebx),%%eax + // adcl TheCPU.EMUtime+4(%%ebx),%%edx + G3M(0x03,0x43,Ofs_ETIME,Cp); + G3M(0x13,0x53,Ofs_ETIME+4,Cp); + // movl %%ecx,TimeStartExec.t.tl(%%ebx) + // movl %%edi,TimeStartExec.t.th(%%ebx) + G2(0x8b89,Cp); + G4((unsigned char *)&TimeStartExec.t.tl-CPUOFFS(0),Cp); + G2(0xbb89,Cp); + G4((unsigned char *)&TimeStartExec.t.th-CPUOFFS(0),Cp); + // movl %%eax,TheCPU.EMUtime(%%ebx) + // movl %%edx,TheCPU.EMUtime+4(%%ebx) + G3M(0x89,0x43,Ofs_ETIME,Cp); + G3M(0x89,0x53,Ofs_ETIME+4,Cp); + } +#endif + // movl %%eax,Ofs_EAX(%%ebx) + // movl %%edx,Ofs_EDX(%%ebx) + G3M(0x89,0x43,Ofs_EAX,Cp); + G3M(0x89,0x53,Ofs_EDX,Cp); + } + break; + + case O_INPDX: + // movl Ofs_EDX(%%ebx),%%edx + G3M(0x8b,0x53,Ofs_EDX,Cp); + if (mode&MBYTE) { + // inb (%%dx),%%al; movb %%al,Ofs_AL(%%ebx) + G4M(0xec,0x88,0x43,Ofs_AL,Cp); + } + else { + // in{wl} (%%dx),%%{e}ax + Gen66(mode,Cp); G1(0xed,Cp); + // mov{wl} %%{e}ax,Ofs_EAX(%%ebx) + Gen66(mode,Cp); G3M(0x89,0x43,Ofs_EAX,Cp); + } + break; + case O_OUTPDX: + // movl Ofs_EDX(%%ebx),%%edx + G3M(0x8b,0x53,Ofs_EDX,Cp); + if (mode&MBYTE) { + // movb Ofs_AL(%%ebx),%%al; outb %%al,(%%dx) + G4M(0x8a,0x43,Ofs_AL,0xee,Cp); + } + else { + // movl Ofs_EAX(%%ebx),%%eax + G3M(0x8b,0x43,Ofs_EAX,Cp); + // out{wl} %%{e}ax,(%%dx) + Gen66(mode,Cp); G1(0xef,Cp); + } + break; + + case JMP_INDIRECT: { // input: %%{e}ax = %%{e}ip + linkdesc *lt = IG->lt; + lt->t_type = JMP_INDIRECT; + if (mode&DATA16) + // movz{wl} %%ax,%%eax + G3M(0x0f,0xb7,0xc0,Cp); + // addl Ofs_XCS(%%ebx),%%eax + G3M(0x03,0x43,Ofs_XCS,Cp); + // pop %%edx; ret + G2M(0x5a,0xc3,Cp); + } + break; + + case JMP_LINK: { // opc, dspt, retaddr, link + const unsigned char pseq16[] = { + // movw $RA,%%ax +/*00*/ 0xb8,0,0,0,0, + // movl Ofs_XSS(%%ebx),%%esi + 0x8b,0x73,Ofs_XSS, + // movl Ofs_ESP(%%ebx),%%ecx + 0x8b,0x4b,Ofs_ESP, + // leal -2(%%ecx),%%ecx + 0x8d,0x49,0xfe, + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edi + 0x8d,0x14,0x0e, + // movw %%ax,(%%edx,%%ebp,1) + 0x66,0x89,0x04,0x2a, +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + // movl %%ecx,Ofs_ESP(%%ebx) + 0x89,0x4b,Ofs_ESP + }; + const unsigned char pseq32[] = { + // movl $RA,%%eax +/*00*/ 0xb8,0,0,0,0, + // movl Ofs_XSS(%%ebx),%%esi + 0x8b,0x73,Ofs_XSS, + // movl Ofs_ESP(%%ebx),%%ecx + 0x8b,0x4b,Ofs_ESP, + // leal -4(%%ecx),%%ecx + 0x8d,0x49,0xfc, + // andl StackMask(%%ebx),%%ecx + 0x23,0x4b,Ofs_STACKM, + // leal (%%esi,%%ecx,1),%%edi + 0x8d,0x14,0x0e, + // movl %%eax,(%%edx,%%ebp,1) + 0x89,0x04,0x2a, +#ifdef KEEP_ESP /* keep high 16-bits of ESP in small-stack mode */ + // movl StackMask(%%ebx),%%edx + 0x8b,0x53,Ofs_STACKM, + // notl %%edx + 0xf7,0xd2, + // andl Ofs_ESP(%%ebx),%%edx + 0x23,0x53,Ofs_ESP, + // orl %%edx,%%ecx + 0x09,0xd1, +#endif + // movl %%ecx,Ofs_ESP(%%ebx) + 0x89,0x4b,Ofs_ESP + }; + unsigned char opc = IG->p0; + int dspt = IG->p1; + int dspnt = IG->p2; + linkdesc *lt = IG->lt; + if (opc == CALLd || opc == CALLl) { + const unsigned char *p; + unsigned char *q; + int sz; + if (mode&DATA16) { + p=pseq16,sz=sizeof(pseq16); + } + else { + p=pseq32,sz=sizeof(pseq32); + } + q = Cp; GNX(Cp, p, sz); + *((int *)(q+1)) = dspnt; + if (debug_level('e')>1) e_printf("CALL: ret=%08x\n",dspnt); + } else if (mode & CKSIGN) { + // check signal on TAKEN branch + // for backjmp-after-jcc: + // movzwl Ofs_SIGAPEND(%%ebx),%%ecx + G4M(0x0f,0xb7,0x4b,Ofs_SIGAPEND,Cp); + // jecxz {continue}: exit if sigpend not 0 + G2M(0xe3,TAILSIZE,Cp); + // movl {exit_addr},%%eax; pop %%edx; ret + G1(0xb8,Cp); G4(dspt,Cp); G2(0xc35a,Cp); + } + // t: b8 [exit_pc] 5a c3 + G1(0xb8,Cp); + lt->t_type = JMP_LINK; + /* {n}t_link = offset from codebuf start to immed value */ + lt->t_link.rel = Cp-BaseGenBuf; + lt->nt_link.abs = 0; + G4(dspt,Cp); G2(0xc35a,Cp); + if (debug_level('e')>2) e_printf("JMP_Link %08x:%08x lk=%d:%08x:%p\n", + dspt,dspnt,lt->t_type,lt->t_link.rel,lt->nt_link.abs); + } + break; + + case JF_LINK: + case JB_LINK: { // opc, PC, dspt, dspnt, link + unsigned char opc = IG->p0; + int jpc = IG->p1; + int dspt = IG->p2; + int dspnt = IG->p3; + linkdesc *lt = IG->lt; + int sz; + // JCXZ: 8b 4b Ofs_ECX e3 07 or 0f b7 4b Ofs_ECX e3 07 + // JCC: 7x 07 + // nt: b8 [nt_pc] 5a c3 + // t: 0f b7 4f [sig] e3 07 + // b8 [sig_pc] 5a c3 + // b8 [t_pc] 5a c3 + sz = TAILSIZE + (mode & CKSIGN? 13:0); + if (opc==JCXZ) { + if (mode&ADDR16) { + // movzwl Ofs_ECX(%%ebx),%%ecx + G4M(0x0f,0xb7,0x4b,Ofs_ECX,Cp); + } + else { + // movl Ofs_ECX(%%ebx),%%ecx + G3M(0x8b,0x4b,Ofs_ECX,Cp); + } + G2M(0xe3,sz,Cp); // jecxz + } + else { + PopPushF(Cp); // get flags from stack + G2M(opc,sz,Cp); // normal condition (Jcc) + } + if (mode & CKSIGN) { + // check signal on NOT TAKEN branch + // for backjmp-after-jcc: + // movzwl Ofs_SIGAPEND(%%ebx),%%ecx + G4M(0x0f,0xb7,0x4b,Ofs_SIGAPEND,Cp); + // jecxz {continue}: exit if sigpend not 0 + G2M(0xe3,TAILSIZE,Cp); + // movl {exit_addr},%%eax; pop %%edx; ret + G1(0xb8,Cp); G4(jpc,Cp); G2(0xc35a,Cp); + } + lt->t_type = IG->op; + // not taken: continue with next instr + G1(0xb8,Cp); + /* {n}t_link = offset from codebuf start to immed value */ + lt->nt_link.rel = Cp-BaseGenBuf; + G4(dspnt,Cp); G2(0xc35a,Cp); + // taken + if (IG->op==JB_LINK) { + // check signal on TAKEN branch for back jumps + G4M(0x0f,0xb7,0x4b,Ofs_SIGAPEND,Cp); + G2M(0xe3,TAILSIZE,Cp); + G1(0xb8,Cp); G4(jpc,Cp); G2(0xc35a,Cp); + } + G1(0xb8,Cp); + lt->t_link.rel = Cp-BaseGenBuf; + G4(dspt,Cp); G2(0xc35a,Cp); + if (debug_level('e')>2) e_printf("J_Link %08x:%08x lk=%d:%08x:%08x\n", + dspt,dspnt,lt->t_type,lt->t_link.rel,lt->nt_link.rel); + } + break; + + case JLOOP_LINK: { // opc, PC, dspt, dspnt, link + unsigned char opc = IG->p0; + int dspt = IG->p1; + int dspnt = IG->p2; + linkdesc *lt = IG->lt; + // {66} dec Ofs_ECX(ebx) + // LOOP: jnz t + // LOOPZ: jz nt; test 0x40,dl; jnz t + // LOOPNZ: jz nt; test 0x40,dl; jz t + // nt: b8 [nt_pc] c3 + // t: 8b 0d [sig] jcxz t2 + // {66} inc Ofs_ECX(ebx) + // b8 [sig_pc] c3 + // t2: b8 [t_pc] c3 + if (mode&ADDR16) { + G4M(OPERoverride,0xff,0x4b,Ofs_ECX,Cp); + } + else { + G3M(0xff,0x4b,Ofs_ECX,Cp); + } + /* + * e2 LOOP taken = (e)cx nt=cxz + * e1 LOOPZ taken = (e)cx && ZF nt=cxz||!ZF + * e0 LOOPNZ taken = (e)cx && !ZF nt=cxz|| ZF + */ + if (opc==LOOPZ_LOOPE) { + G2M(0x74,0x06,Cp); // jz->nt + // test flags (on stack) + G4M(0xf6,0x04,0x24,0x40,Cp); + G2M(0x75,TAILSIZE,Cp); // jnz->t + } + else if (opc==LOOPNZ_LOOPNE) { + G2M(0x74,0x06,Cp); // jz->nt + // test flags (on stack) + G4M(0xf6,0x04,0x24,0x40,Cp); + G2M(0x74,TAILSIZE,Cp); // jz->t + } + else { + G2M(0x75,TAILSIZE,Cp); // jnz->t + } + lt->t_type = JLOOP_LINK; + // not taken: continue with next instr + G1(0xb8,Cp); + /* {n}t_link = offset from codebuf start to immed value */ + lt->nt_link.rel = Cp-BaseGenBuf; + G4(dspnt,Cp); G2(0xc35a,Cp); + // taken +#if 0 + /* CKSIGN is likely not needed for loops */ + if (mode & CKSIGN) { + // check signal on TAKEN branch + // for backjmp-after-jcc: + // movzwl Ofs_SIGAPEND(%%ebx),%%ecx + G4M(0x0f,0xb7,0x4b,Ofs_SIGAPEND,Cp); + // jecxz {continue}: exit if sigpend not 0 + G2M(0xe3,TAILSIZE,Cp); + // movl {exit_addr},%%eax; pop %%edx; ret + G1(0xb8,Cp); G4(dspt,Cp); G2(0xc35a,Cp); + } +#endif + G1(0xb8,Cp); + lt->t_link.rel = Cp-BaseGenBuf; + G4(dspt,Cp); G2(0xc35a,Cp); + if (debug_level('e')>2) e_printf("JLOOP_Link %08x:%08x lk=%d:%08x:%08x\n", + dspt,dspnt,lt->t_type,lt->t_link.rel,lt->nt_link.rel); + } + break; + + } +#if PROFILE + if (debug_level('e')) GenTime += (GETTSC() - t0); +#endif + return Cp; +} + + +/* + * address generator unit + * careful - do not use eax, and NEVER change any flag! + */ +static void AddrGen_x86(int op, int mode, ...) +{ + va_list ap; + IMeta *I; + IGen *IG; +#if PROFILE + hitimer_t t0 = 0; + if (debug_level('e')) t0 = GETTSC(); +#endif + + if (CurrIMeta<0) { + CurrIMeta=0; InstrMeta[0].ngen=0; InstrMeta[0].flags=0; + } + I = &InstrMeta[CurrIMeta]; + if (I->ngen >= NUMGENS) leavedos_main(0xbac1); + IG = &(I->gen[I->ngen]); + if (debug_level('e')>6) dbug_printf("AGEN: %3d %6x\n",op,mode); + + va_start(ap, mode); + IG->op = op; + IG->mode = mode; + IG->ovds = OVERR_DS; + + switch(op) { + case A_DI_0: // base(32), imm + case A_DI_1: { // base(32), {imm}, reg, {shift} + signed char ofs = (char)va_arg(ap,int); + signed char o; + IG->p0 = ofs; + IG->p1 = va_arg(ap,int); + if (op==A_DI_0) break; + o = Offs_From_Arg(); + IG->p2 = o; + } + break; + case A_DI_2: { // base(32), {imm}, reg, reg, {shift} + signed char o1,o2; + signed char ofs = (char)va_arg(ap,int); + unsigned char sh; + IG->p0 = ofs; + IG->p1 = va_arg(ap,int); + o1 = Offs_From_Arg(); + o2 = Offs_From_Arg(); + IG->p2 = o1; + IG->p3 = o2; + sh = (unsigned char)(va_arg(ap,int)); + IG->p4 = sh; + } + break; + case A_DI_2D: { // modrm_sibd, 32-bit mode + signed char o; + unsigned char sh; + IG->p0 = va_arg(ap,int); + o = Offs_From_Arg(); + IG->p1 = o; + sh = (unsigned char)(va_arg(ap,int)); + IG->p2 = sh; + } + break; + case A_SR_SH4: { // real mode make base addr from seg + signed char o1 = Offs_From_Arg(); + signed char o2 = Offs_From_Arg(); + IG->p0 = o1; + IG->p1 = o2; + if (o1 == Ofs_SS) + I->flags |= F_INHI; + } + break; + } + va_end(ap); + I->ngen++; +#if PROFILE + if (debug_level('e')) GenTime += (GETTSC() - t0); +#endif +} + + +static void Gen_x86(int op, int mode, ...) +{ + int rcod=0; + va_list ap; + IMeta *I; + IGen *IG; +#if PROFILE + hitimer_t t0 = 0; + if (debug_level('e')) t0 = GETTSC(); +#endif + + if (CurrIMeta<0) { + CurrIMeta=0; InstrMeta[0].ngen=0; InstrMeta[0].flags=0; + } + I = &InstrMeta[CurrIMeta]; + if (I->ngen >= NUMGENS) leavedos_main(0xbac2); + IG = &(I->gen[I->ngen]); + if (debug_level('e')>6) dbug_printf("CGEN: %3d %6x\n",op,mode); + + va_start(ap, mode); + IG->op = op; + IG->mode = mode; + IG->ovds = OVERR_DS; + + switch(op) { + case L_NOP: + case L_CR0: + case L_ZXAX: + case L_DI_R1: + case S_DI: + case O_NOT: + case O_NEG: + case O_INC: + case O_DEC: + case O_MUL: + case O_CBWD: + case O_XLAT: + case O_PUSH: + case O_PUSH1: + case O_PUSH2F: + case O_PUSH3: + case O_POP1: + case O_POP3: + case O_LEAVE: + case O_MOVS_SetA: + case O_MOVS_MovD: + case O_MOVS_LodD: + case O_MOVS_StoD: + case O_MOVS_ScaD: + case O_MOVS_CmpD: + case O_MOVS_SavA: + case O_RDTSC: + case O_INPDX: + case O_OUTPDX: + break; + + case L_REG: + case S_REG: + case S_DI_R: + case L_LXS1: + case O_XCHG: + case O_CLEAR: + case O_TEST: + case O_SBSELF: + case O_BSWAP: + case O_CMPXCHG: { + signed char o = Offs_From_Arg(); + IG->p0 = o; + } + break; + + case L_REG2REG: + case L_LXS2: /* real mode segment base from segment value */ + case O_XCHG_R: { + signed char o1 = Offs_From_Arg(); + signed char o2 = Offs_From_Arg(); + IG->p0 = o1; + IG->p1 = o2; + } + break; + + case S_DI_IMM: + case L_IMM_R1: + case O_ADD_R: // acc = acc op reg + case O_OR_R: + case O_ADC_R: + case O_SBB_R: + case O_AND_R: + case O_SUB_R: + case O_XOR_R: + case O_CMP_R: + case O_INC_R: + case O_DEC_R: + case O_DIV: + case O_IDIV: + case O_PUSHI: { + int v = va_arg(ap,int); + IG->p0 = v; + } + break; + + case O_ADD_FR: // reg = reg op acc/imm + case O_OR_FR: + case O_ADC_FR: + case O_SBB_FR: + case O_AND_FR: + case O_SUB_FR: + case O_XOR_FR: + case O_CMP_FR: { + signed char o = Offs_From_Arg(); + IG->p0 = o; + if (mode & IMMED) { + int v = va_arg(ap,int); + IG->p1 = v; + } } + break; + + case L_IMM: { + signed char o = Offs_From_Arg(); + int v = va_arg(ap,int); + IG->p0 = o; + IG->p1 = v; + } + break; + + case L_MOVZS: { + signed char o; + rcod = (va_arg(ap,int)&1)<<3; // 0=z 8=s + o = Offs_From_Arg(); + IG->p0 = rcod; + IG->p1 = o; + } + break; + + case O_IMUL: + if (mode&IMMED) { + int v = va_arg(ap,int); + signed char o = Offs_From_Arg(); + IG->p0 = v; + IG->p1 = o; + } + if (!(mode&MBYTE)) { + if (mode&MEMADR) { + signed char o = Offs_From_Arg(); + IG->p0 = o; + } + } + break; + + case O_ROL: + case O_ROR: + case O_RCL: + case O_RCR: + case O_SHL: + case O_SHR: + case O_SAR: + if (mode & IMMED) { + unsigned char sh = (unsigned char)va_arg(ap,int); + IG->p0 = sh; + } + break; + + case O_OPAX: { /* used by DAA..AAD */ + int n = va_arg(ap,int); + IG->p0 = n; + // get n>0,n<3 bytes from parameter stack + IG->p1 = va_arg(ap,int); + if (n==2) IG->p2 = va_arg(ap,int); + } + break; + + case O_POP: + if (mode & MRETISP) IG->p0 = Offs_From_Arg(); + break; + + case O_PUSH2: + case O_POP2: { + signed char o = Offs_From_Arg(); + IG->p0 = o; + } + break; + + case O_SLAHF: + rcod = va_arg(ap,int)&1; // 0=LAHF 1=SAHF + IG->p0 = rcod; + break; + + case O_SETFL: + case O_SETCC: { + unsigned char n = (unsigned char)va_arg(ap,int); + IG->p0 = n; + } + break; + + case O_FOP: + I->flags |= F_FPOP; + // fall through + case O_INT: { + unsigned char exop = (unsigned char)va_arg(ap,int); + IG->p0 = exop; + IG->p1 = va_arg(ap,int); // reg + } + break; + + case O_BITOP: { + unsigned char n = (unsigned char)va_arg(ap,int); + signed char o = Offs_From_Arg(); + IG->p0 = n; + IG->p1 = o; + } break; + + case O_SHFD: { + unsigned char l_r = (unsigned char)va_arg(ap,int)&8; + signed char o = Offs_From_Arg(); + IG->p0 = l_r; + IG->p1 = o; + if (mode & IMMED) { + unsigned char shc = (unsigned char)va_arg(ap,int)&0x1f; + IG->p2 = shc; + } + } break; + + case JMP_INDIRECT: + IG->lt = va_arg(ap,linkdesc *); // lt + break; + + case JMP_LINK: // opc, dspt, retaddr, link + case JLOOP_LINK: { + unsigned char opc = (unsigned char)va_arg(ap,int); + IG->p0 = opc; + IG->p1 = va_arg(ap,int); // dspt + IG->p2 = va_arg(ap,int); // dspnt + IG->lt = va_arg(ap,linkdesc *); // lt + } + break; + + case JF_LINK: + case JB_LINK: { // opc, PC, dspt, dspnt, link + unsigned char opc = (unsigned char)va_arg(ap,int); + IG->p0 = opc; + IG->p1 = va_arg(ap,int); // jpc + IG->p2 = va_arg(ap,int); // dspt + IG->p3 = va_arg(ap,int); // dspnt + IG->lt = va_arg(ap,linkdesc *); // lt + } + break; + + } + + va_end(ap); + I->ngen++; +#if PROFILE + if (debug_level('e')) GenTime += (GETTSC() - t0); +#endif +} + + +///////////////////////////////////////////////////////////////////////////// + + +static CodeBuf *ProduceCode(unsigned int PC, IMeta *I0) +{ + int i,j,nap,mall_req; + unsigned int adr_lo=0, adr_hi=0; + unsigned char *cp, *cp1, *BaseGenBuf, *CodePtr; + size_t GenBufSize; + CodeBuf *GenCodeBuf; + + if (debug_level('e')>1) { + e_printf("---------------------------------------------\n"); + e_printf("ProduceCode: CurrIMeta=%d\n",CurrIMeta); + } + if (CurrIMeta < 0) leavedos_main(0xbac3); + + /* reserve space for auto-ptr and info structures */ + nap = I0->ncount+1; + + /* allocate the actual code buffer here; size is a worst-case + * estimate based on measured bytes per opcode. + * + * Code buffer layout: + * 0000 (GenCodeBuf) pointed from {TNode}.mblock + * contains a back pointer to the TNode + * 0008/0004 self-pointer (address of this location) + * 0010/0008 Addr2Pc table (nap) pointed from {TNode}.pmeta + * nap+10/8 actual code produced (BaseGenBuf) + * plus tail code + * Only the code part is filled here. + * GenBufSize contain a first guess of the amount of space required + * + */ + GenBufSize = 0; + for (i=0; imeta[nap]; + I0->daddr = 0; + if (debug_level('e')>1) + e_printf("CodeBuf=%p siz %zd CodePtr=%p\n",GenCodeBuf,GenBufSize,CodePtr); + + for (i=0; inpc; + } + else { + if (I->npc < adr_lo) adr_lo = I->npc; + else if (I->npc > adr_hi) adr_hi = I->npc; + } + cp = cp1 = CodePtr; + I->daddr = cp - BaseGenBuf; + for (j=0; jngen; j++) { + CodePtr = CodeGen(CodePtr, BaseGenBuf, I, j); + if (CodePtr-cp1 > MAX_GEND_BYTES_PER_OP) { + dosemu_error("Generated code (%zd bytes) overflowed into buffer, please " + "increase MAX_GEND_BYTES_PER_OP=%d\n", + CodePtr-cp1, MAX_GEND_BYTES_PER_OP); + leavedos_main(0x535347); + } + if (debug_level('e')>1) { + IGen *IG = &(I->gen[j]); + int dg = CodePtr-cp1; + e_printf("PGEN(%02d,%02d) %3d %6x %2d %08x %08x %08x %08x %08x\n", + i,j,IG->op,IG->mode,dg, + IG->p0,IG->p1,IG->p2,IG->p3,IG->p4); + } + cp1 = CodePtr; + } + I->len = CodePtr - cp; + if (debug_level('e')>3) GCPrint(cp, BaseGenBuf, I->len); + } + if (debug_level('e')>1) + e_printf("Size=%td guess=%zd\n",(CodePtr-BaseGenBuf),GenBufSize); +/**/ if ((CodePtr-BaseGenBuf) > GenBufSize) leavedos_main(0x535347); + if (PC < adr_lo) adr_lo = PC; + else if (PC > adr_hi) adr_hi = PC; + I0->seqbase = adr_lo; + I0->seqlen = adr_hi - adr_lo; + + if (debug_level('e')>1) + e_printf("---------------------------------------------\n"); + + /* If the code doesn't terminate with a jump/loop instruction + * it still lacks the tail code; add it here */ + if (I0->clink.t_type==0) { + unsigned char *p = CodePtr; + /* copy tail instructions to the end of the code block */ + memcpy(p, TailCode, TAILSIZE); + p += TAILFIX; + I0->clink.t_link.abs = (unsigned int *)p; + *((unsigned int *)p) = PC; + CodePtr += TAILSIZE; + } + + /* show jump+tail code */ + if ((debug_level('e')>6) && (CurrIMeta>0)) { + IMeta *GL = &I0[CurrIMeta-1]; + unsigned char *pl = &BaseGenBuf[GL->daddr+GL->len]; + GCPrint(pl, BaseGenBuf, CodePtr - pl); + } + + I0->totlen = CodePtr - BaseGenBuf; + + /* shrink buffer to what is actually needed */ + mall_req = I0->totlen + offsetof(CodeBuf, meta) + sizeof(Addr2Pc) * nap; + GenCodeBuf = dlrealloc(GenCodeBuf, mall_req); + if (debug_level('e')>3) + e_printf("Seq len %#x:%#x\n",I0->seqlen,I0->totlen); + + return GenCodeBuf; +} + + +///////////////////////////////////////////////////////////////////////////// +/* + * The node linker. + * + * A code sequence can have one of two termination types: + * + * 1) straight end (no jump), or unconditional jump or call + * + * key: | + * | + * | + * mov $next_addr,eax + * ret + * + * 2) conditional jump or loop + * + * key: | + * | + * | + * jcond taken + * mov $not_taken_addr,eax + * ret + * taken: mov $taken_addr,eax + * ret + * + * In the first case, there's only one linking point; in the second, two. + * Linking means replacing the "mov addr,eax" instruction with a direct + * jump to the start point of the next code fragment. + * The parameters used are (t_ means taken, nt_ means not taken): + * t_ref,nt_ref pointers to next node + * t_link,nt_link addresses of the patch point + * t_target,nt_target jump targets, also used for unlinking + * Since a node can be referred from many others, we need to keep + * "back-references" in a list in order to unlink it. + */ + +static void _nodeflagbackrefs(TNode *LG, unsigned short flags) +{ + /* helper routine to flag all back references: + if the current node uses FP then all nodes that link to + it must be flagged as such, which is a recursive procedure + */ + backref *B; + + if ((LG->flags & flags) != flags) { + /* only go as far back as long as flags change */ + LG->flags |= flags; + for (B=LG->clink.bkr.next; B; B=B->next) + _nodeflagbackrefs(*B->ref, flags); + } +} + +static void NodeLinker(TNode *LG, TNode *G) +{ + unsigned int *lp; + linkdesc *T = &G->clink; + backref *B; +#if PROFILE + hitimer_t t0 = 0; +#endif + +#if !defined(SINGLESTEP) + if (!UseLinker) +#endif + return; + +#if PROFILE + if (debug_level('e')) t0 = GETTSC(); +#endif + if (debug_level('e')>8 && LG) e_printf("NodeLinker: %08x->%08x\n",LG->key,G->key); + + if (LG && (LG->alive>0)) { + int ra; + linkdesc *L = &LG->clink; + if (L->unlinked_jmp_targets) { // node ends with links + lp = L->t_link.abs; // check 'taken' branch + if (L->t_target==G->key && (L->unlinked_jmp_targets & TARGET_T)) { // points to current node? + if (L->t_ref!=0) { + dbug_printf("Linker: t_ref at %08x busy\n",LG->key); + leavedos_main(0x8102); + } + L->unlinked_jmp_targets &= ~TARGET_T; + // b8 [npc] -> e9/eb reladr + ra = G->addr - (unsigned char *)L->t_link.abs; + if ((ra > -127) && (ra < 128)) { + ra -= 1; ((char *)lp)[-1] = 0xeb; + } + else { + ra -= 4; ((char *)lp)[-1] = 0xe9; + } + *lp = ra; + L->t_ref = &G->mblock->bkptr; + B = calloc(1,sizeof(backref)); + // head insertion + B->next = T->bkr.next; + T->bkr.next = B; + B->ref = &LG->mblock->bkptr; + B->branch = 'T'; + T->nrefs++; + if (G==LG) { + G->flags |= F_SLFL; + if (debug_level('e')>1) { + e_printf("Linker: node (%p:%08x:%p) SELF link\n" + "\t\tjmp %08x, target=%08x, t_ref %d=%p->%p\n", + G,G->key,G->addr, + ra, L->t_target, T->nrefs, L->t_ref, *L->t_ref); + } + } + else if (debug_level('e')>1) { + e_printf("Linker: previous node (%p:%08x:%p)\n" + "\t\tlinked to (%p:%08x:%p)\n" + "\t\tjmp %08x, target=%08x, t_ref %d=%p->%p\n", + LG,LG->key,LG->addr, + G,G->key,G->addr, + ra, L->t_target, T->nrefs, L->t_ref, *L->t_ref); + } + _nodeflagbackrefs(LG, G->flags); + if (debug_level('e')>8) { backref *bk = T->bkr.next; +#ifdef DEBUG_LINKER + if (bk==NULL) { dbug_printf("bkr null\n"); leavedos_main(0x8108); } +#endif + while (bk) { dbug_printf("bkref=%c%p->%p\n",bk->branch, + bk->ref,*bk->ref); bk=bk->next; } + } + } + if (L->unlinked_jmp_targets & TARGET_NT) { // if it has a 'not taken' link + lp = L->nt_link.abs; // check 'not taken' branch + if (L->nt_target==G->key) { // points to current node? + if (L->nt_ref!=0) { + dbug_printf("Linker: nt_ref at %08x busy\n",LG->key); + leavedos_main(0x8103); + } + L->unlinked_jmp_targets &= ~TARGET_NT; + // b8 [npc] -> e9/eb reladr + ra = G->addr - (unsigned char *)L->nt_link.abs; + if ((ra > -127) && (ra < 128)) { + ra -= 1; ((char *)lp)[-1] = 0xeb; + } + else { + ra -= 4; ((char *)lp)[-1] = 0xe9; + } + *lp = ra; + L->nt_ref = &G->mblock->bkptr; + B = calloc(1,sizeof(backref)); + // head insertion + B->next = T->bkr.next; + T->bkr.next = B; + B->ref = &LG->mblock->bkptr; + B->branch = 'N'; + T->nrefs++; + if (G==LG) { + G->flags |= F_SLFL; + if (debug_level('e')>1) { + e_printf("Linker: node (%p:%08x:%p) SELF link\n" + "\t\tjmp %08x, target=%08x, nt_ref %d=%p->%p\n", + G,G->key,G->addr, + ra, L->nt_target, T->nrefs, L->nt_ref, *L->nt_ref); + } + } + else if (debug_level('e')>1) { + e_printf("Linker: previous node (%p:%08x:%p)\n" + "\t\tlinked to (%p:%08x:%p)\n" + "\t\tjmp %08x, target=%08x, nt_ref %d=%p->%p\n", + LG,LG->key,LG->addr, + G,G->key,G->addr, + ra, L->nt_target, T->nrefs, L->nt_ref, *L->nt_ref); + } + _nodeflagbackrefs(LG, G->flags); + if (debug_level('e')>8) { backref *bk = T->bkr.next; +#ifdef DEBUG_LINKER + if (bk==NULL) { dbug_printf("bkr null\n"); leavedos_main(0x8109); } +#endif + while (bk) { dbug_printf("bkref=%c%p->%p\n",bk->branch, + bk->ref,*bk->ref); bk=bk->next; } + } + } + } + } + } +#if PROFILE + if (debug_level('e')) LinkTime += (GETTSC() - t0); +#endif +} + + +void NodeUnlinker(TNode *G) +{ + unsigned int *lp; + linkdesc *T = &G->clink; + backref *B = T->bkr.next; +#if PROFILE + hitimer_t t0 = 0; +#endif + +#if !defined(SINGLESTEP) + if (!UseLinker) +#endif + return; +#if PROFILE + if (debug_level('e')) t0 = GETTSC(); +#endif + // unlink backward references (from other nodes to the current + // node) + if (debug_level('e')>8) + e_printf("Unlinker: bkr.next=%p\n",B); + while (B) { + backref *b2 = B; + if (B->branch=='T') { + TNode *H = *B->ref; + linkdesc *L = &H->clink; + if (debug_level('e')>2) e_printf("Unlinking T ref from node %p(%08x) to %08x\n", + H, L->t_target, G->key); + if (L->t_target != G->key) { + dbug_printf("Unlinker: BK ref error t=%08x k=%08x\n", + L->t_target, G->key); + leavedos_main(0x8110); + } + lp = L->t_link.abs; + ((char *)lp)[-1] = 0xb8; + *lp = L->t_target; + L->t_ref = NULL; L->unlinked_jmp_targets |= TARGET_T; + T->nrefs--; + } + else if (B->branch=='N') { + TNode *H = *B->ref; + linkdesc *L = &H->clink; + if (debug_level('e')>2) e_printf("Unlinking N ref from node %p(%08x) to %08x\n", + H, L->nt_target, G->key); + if (L->nt_target != G->key) { + dbug_printf("Unlinker: BK ref error u=%08x k=%08x\n", + L->nt_target, G->key); + leavedos_main(0x8110); + } + lp = L->nt_link.abs; + ((char *)lp)[-1] = 0xb8; + *lp = L->nt_target; + L->nt_ref = NULL; L->unlinked_jmp_targets |= TARGET_NT; + T->nrefs--; + } + else { + e_printf("Invalid unlink [%c] ref %p from node ?(?) to %08x\n", + B->branch, B->ref, G->key); + leavedos_main(0x8116); + } + B = B->next; + free(b2); + } + + if (T->nrefs) { + dbug_printf("Unlinker: nrefs error\n"); + leavedos_main(0x8115); + } + + // unlink forward references (from the current node to other + // nodes), which are backward refs for the other nodes + if (debug_level('e')>8) + e_printf("Unlinker: refs=T%p N%p\n",T->t_ref,T->nt_ref); + if (T->t_ref) { + TNode *Gt = *T->t_ref; + backref *Btq = &Gt->clink.bkr; + backref *Bt = Gt->clink.bkr.next; + if (debug_level('e')>2) e_printf("Unlink fwd T ref to node %p(%08x)\n",Gt, + Gt->key); + while (Bt) { + if (*Bt->ref==G) { + Btq->next = Bt->next; + Gt->clink.nrefs--; + free(Bt); + break; + } + Btq = Bt; + Bt = Bt->next; + } + if (Bt==NULL) { // not found... + dbug_printf("Unlinker: FW T ref error\n"); + leavedos_main(0x8111); + } + T->t_ref = NULL; + } + if (T->nt_ref) { + TNode *Gn = *T->nt_ref; + backref *Bnq = &Gn->clink.bkr; + backref *Bn = Gn->clink.bkr.next; + if (debug_level('e')>2) e_printf("Unlink fwd N ref to node %p(%08x)\n",Gn, + Gn->key); + while (Bn) { + if (*Bn->ref==G) { + Bnq->next = Bn->next; + Gn->clink.nrefs--; + free(Bn); + break; + } + Bnq = Bn; + Bn = Bn->next; + } + if (Bn==NULL) { // not found... + dbug_printf("Unlinker: FW N ref error\n"); + leavedos_main(0x8112); + } + T->nt_ref = NULL; + } + memset(T, 0, sizeof(linkdesc)); +#if PROFILE + if (debug_level('e')) LinkTime += (GETTSC() - t0); +#endif +} + + +///////////////////////////////////////////////////////////////////////////// +/* + * These are the functions which actually executes the generated code. + * + * There are two paths: + * 1) for CloseAndExec_x86 we are ending a code generation phase, and our code + * is still in the CodeBuf together with all its detailed info stored + * in InstrMeta. First we close the sequence adding the TailCode; + * it, and move it to the collecting tree and clear the temporary + * structures. Then, in Exec_x86 we execute the code. + * The PC parameter is the address in the source code of the next + * instruction following the end of the code block. It will be stored + * into the TailCode of the block. + * 2) We are executing a sequence found in the collecting tree. + * Exec_x86 is called directly. + * G is the node we found (possibly the start of a chain of linked + * code sequences). + * + * When the code is executed, it returns in eax the source address of the + * next instruction to find/parse. + * + */ + +static unsigned int CloseAndExec_x86(unsigned int PC, int mode) +{ + IMeta *I0; + TNode *G; + CodeBuf *GenCodeBuf; + + if (CurrIMeta <= 0) { +/**/ e_printf("(X) Nothing to exec at %08x\n",PC); + return PC; + } + + // we're creating a new node + I0 = &InstrMeta[0]; + + if (debug_level('e')>2) { + e_printf("==== Closing sequence at %08x\n", PC); + } + + GenCodeBuf = ProduceCode(PC, I0); + /* check for fatal error */ + if (TheCPU.err < 0) + return I0->npc; + + NodesParsed++; +#if PROFILE + if (debug_level('e')) TotalNodesParsed++; +#endif + G = Move2Tree(I0, GenCodeBuf); /* when is G==NULL? */ + /* InstrMeta will be zeroed at this point */ + /* mprotect the page here; a page fault will be triggered + * if some other code tries to write over the page including + * this node */ + e_markpage(G->seqbase, G->seqlen); + e_mprotect(G->seqbase, G->seqlen); + G->cs = LONG_CS; + G->mode = mode; + /* check links INSIDE current node */ + NodeLinker(G, G); + return Exec_x86(G); +} + +static unsigned int Exec_x86_pre(unsigned char *ecpu) +{ + unsigned long flg; + + /* get the protected mode flags. Note that RF and VM are cleared + * by pushfd (but not by ints and traps) */ + flg = getflags(); + + /* pass TF=0, IF=1, DF=0 */ + flg = (flg & ~(EFLAGS_CC|EFLAGS_IF|EFLAGS_DF|EFLAGS_TF)) | + (EFLAGS & EFLAGS_CC) | EFLAGS_IF; + +#ifndef __x86_64__ + if (config.cpuprefetcht0) +#endif + __asm__ __volatile__ ( +" prefetcht0 %0\n" + : : "m"(*ecpu) ); + + return flg; +} + +static void Exec_x86_post(unsigned long flg, unsigned int mem_ref) +{ + EFLAGS = (EFLAGS & ~EFLAGS_CC) | (flg & EFLAGS_CC); + TheCPU.mem_ref = mem_ref; +} + +/* stack frame for compiled code: + * esp+00 TheCPU flags + * 04/08 return address + * 18/20... locals of CloseAndExec + */ +#ifdef __x86_64__ +#define RE_REG(r) "%%r"#r +#define R_REG(r) "%r"#r +/* Generated code calls C functions which clobber r8-r11 */ +/* r12 is used to backup rsp */ +#define EXEC_CLOBBERS ,"r8","r9","r10","r11","r12" +#else +#define RE_REG(r) "%%e"#r +#define R_REG(r) "%e"#r +#define EXEC_CLOBBERS +#endif +asm(".text\n" + ".global do_seq_start\n" + "do_seq_start:\n" + "push "R_REG(dx)"\n" + "jmp *"R_REG(ax)"\n"); +ASMLINKAGE(void,do_seq_start,(void)); +static unsigned Exec_x86_asm(unsigned *mem_ref, unsigned long *flg, + unsigned char *ecpu, unsigned char *SeqStart) +{ + unsigned ePC; + InCompiledCode = 1; + asm volatile ( +#ifdef __x86_64__ + "movq %%rsp,%%r12\n" + "addq $-128,%%rsp\n" /* go below red zone */ + "andq $~15,%%rsp\n" /* 16-byte stack alignment */ + "push "RE_REG(bp)"\n" +#endif + "push "RE_REG(bp)"\n" + "mov %7, "RE_REG(bp)"\n" + "call *%6\n" /* call SeqStart */ + "pop "RE_REG(bp)"\n" +#ifdef __x86_64__ + "pop "RE_REG(bp)"\n" + "movq %%r12,%%rsp\n" +#endif + : "=d"(*flg),"=a"(ePC),"=D"(*mem_ref) + : "b"(ecpu),"d"(*flg),"a"(SeqStart),"R"(do_seq_start), + "m"(mem_base) + : "memory", "cc", "ecx", "esi" EXEC_CLOBBERS + ); + InCompiledCode = 0; + /* even though InCompiledCode is volatile, we also need a barrier */ + asm volatile ("":::"memory"); + return ePC; +} + +unsigned int Exec_x86(TNode *G) +{ + unsigned long flg; + unsigned char *ecpu; + unsigned int mem_ref; + unsigned int ePC; + unsigned short seqflg = G->flags; + unsigned char *SeqStart = G->addr; +#if PROFILE + hitimer_u TimeStartExec, TimeEndExec; +#endif + + ecpu = CPUOFFS(0); + if (debug_level('e')>1) { + if (TheCPU.sigalrm_pending>0) e_printf("** SIGALRM is pending\n"); + e_printf("==== Executing code at %p flg=%04x\n", + SeqStart,seqflg); + } +#ifdef ASM_DUMP + fprintf(aLog,"%p: exec\n",G->key); +#endif + if (seqflg & F_FPOP) { + if (TheCPU.fpstate) { + loadfpstate(*TheCPU.fpstate); + TheCPU.fpstate = NULL; + } + /* mask exceptions in generated code */ + unsigned short fpuc; + asm ("fstcw %0" : "=m"(TheCPU.fpuc)); + fpuc = TheCPU.fpuc | 0x3f; + asm ("fldcw %0" :: "m"(fpuc)); + } + + flg = Exec_x86_pre(ecpu); +#if PROFILE + __asm__ __volatile__ ( + "rdtsc\n" + : "=a"(TimeStartExec.t.tl),"=d"(TimeStartExec.t.th) + ); +#endif + ePC = Exec_x86_asm(&mem_ref, &flg, ecpu, SeqStart); +#if PROFILE + __asm__ __volatile__ ( + "rdtsc\n" + : "=a"(TimeEndExec.t.tl),"=d"(TimeEndExec.t.th) + ); +#endif + Exec_x86_post(flg, mem_ref); + + /* was there at least one FP op in the sequence? */ + if (seqflg & F_FPOP) { + int exs; + __asm__ __volatile__ ("fstsw %0" : "=m"(exs)); + exs &= 0x7f; + if (exs) { + e_printf("FPU: error status %02x\n",exs); + if ((exs & ~TheCPU.fpuc) & 0x3f) { + __asm__ __volatile__ ("fnclex\n" ::: "memory"); + e_printf("FPU exception\n"); + /* TheCPU.err = EXCP10_COPR; */ + } + } + } + + if (debug_level('e')) { +#if PROFILE + TimeEndExec.td -= TimeStartExec.td; + ExecTime += TimeEndExec.td; +#endif + if (debug_level('e')>1) { + e_printf("** End code, PC=%08x sig=%x\n",ePC, + TheCPU.sigalrm_pending); + if ((debug_level('e')>3) && (seqflg & F_FPOP)) { + e_printf(" %s\n", e_trace_fp()); + } + /* DANGEROUS - can crash dosemu! */ + if ((debug_level('e')>4) && goodmemref(mem_ref)) { + e_printf("*mem_ref [%#08x] = %08x\n",mem_ref, + READ_DWORD(mem_ref)); + } + } + } + /* signal_pending at this point is 1 if there was ANY signal, + * not just a SIGALRM + */ + if ((G->flags & F_INHI) && !(G->seqnum == 1 && (CEmuStat & CeS_INHI))) { + /* ignore signals and traps for movss/popss; if there is just + one compiled instruction it should be ignored unconditionally + if signals and traps were already ignored */ + CEmuStat |= CeS_INHI; + CEmuStat &= ~CeS_TRAP; + } else { + CEmuStat &= ~(CeS_INHI|CeS_MOVSS); + if (TheCPU.sigalrm_pending) { + CEmuStat|=CeS_SIGPEND; + TheCPU.sigalrm_pending = 0; + } + } + +#if defined(SINGLESTEP) + InvalidateNodeRange(G->key, 1, NULL); + avltr_delete(G->key); + if (debug_level('e')>1) e_printf("\n%s",e_print_regs()); +#else + /* + * After execution comes the linker stage. + * So the order is: + * 1) build code sequence in the IMeta buffer + * 2) move buffer to a newly allocated node in the tree + * 3) execute it, always returning back at the end + * 4) link it to other nodes + * Linking: a node is linked with the next + * one in execution order, provided that the end source address + * of the preceding node matches the start source address of the + * following (i.e. no interpreted instructions in between). + */ + if (G && G->alive>0) { + /* check links FROM LastXNode TO current node */ + if (LastXNode && LastXNode->alive > 0) + NodeLinker(LastXNode, G); + if (debug_level('e')>2 && G != LastXNode) + e_printf("New LastXNode=%08x\n",G->key); + LastXNode = G; + } +#endif + + return ePC; +} + +/* fast loop, only used if nothing special is going on; if anything + out of the ordinary happens, the above Exec_x86() is called */ +unsigned int Exec_x86_fast(TNode *G) +{ + unsigned char *ecpu = CPUOFFS(0); + unsigned long flg = Exec_x86_pre(ecpu); + unsigned int ePC, mem_ref; + unsigned mode = G->mode; + + do { + ePC = Exec_x86_asm(&mem_ref, &flg, ecpu, G->addr); + if (G->alive > 0) { + if (LastXNode->clink.unlinked_jmp_targets && + (LastXNode->clink.t_target == G->key || + LastXNode->clink.nt_target == G->key)) + NodeLinker(LastXNode, G); + LastXNode = G; + } + if (TheCPU.sigalrm_pending) { + CEmuStat|=CeS_SIGPEND; + break; + } + } while (!TheCPU.err && (G=FindTree(ePC)) && + GoodNode(G, mode) && !(G->flags & (F_FPOP|F_INHI))); + + Exec_x86_post(flg, mem_ref); + TheCPU.sigalrm_pending = 0; + return ePC; +} + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/src/base/emu-i386/simx86/codegen-x86.h b/src/base/emu-i386/simx86/codegen-x86.h new file mode 100644 index 0000000..11a07fc --- /dev/null +++ b/src/base/emu-i386/simx86/codegen-x86.h @@ -0,0 +1,143 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originaly was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#ifndef _EMU86_CODEGEN_X86_H +#define _EMU86_CODEGEN_X86_H + +#include "codegen.h" +#include "trees.h" +#include "vgaemu.h" + +#define TAILSIZE 7 +#define TAILFIX 1 + +///////////////////////////////////////////////////////////////////////////// + +extern unsigned int VgaAbsBankBase; +extern unsigned int Exec_x86(TNode *G); +extern unsigned int Exec_x86_fast(TNode *G); + +///////////////////////////////////////////////////////////////////////////// + +#define STD_WRITE_B G3M(0x88,0x04,0x2f,Cp); +#define STD_WRITE_WL(m) Gen66(m,Cp); G3M(0x89,0x04,0x2f,Cp) + +#define GenAddECX(o) if (((o) > -128) && ((o) < 128)) {\ + G2(0xc183,Cp); G1((o),Cp); } else {\ + G2(0xc181,Cp); G4((o),Cp); } + +#define GenLeaECX(o) if (((o) > -128) && ((o) < 128)) {\ + G2(0x498d,Cp); G1((o),Cp); } else {\ + G2(0x898d,Cp); G4((o),Cp); } + +#define GenLeaEDI(o) if (((o) > -128) && ((o) < 128)) {\ + G2(0x7f8d,Cp); G1((o),Cp); } else {\ + G2(0xbf8d,Cp); G4((o),Cp); } + +#define StackMaskEBP {G2(0x6b23,Cp); G1(Ofs_STACKM,Cp); } + +///////////////////////////////////////////////////////////////////////////// + +// get all flags and let them on the stack +#define PopPushF(Cp) if (((Cp)==BaseGenBuf)||((Cp)[-1]!=PUSHF)) \ + G2(0x9c9d,(Cp)) + +// cld; btl $0xa,EFLAGS(%ebx); jnc 1f; std; 1f: +#define GetDF(Cp) \ + G4M(CLD,TwoByteESC,0xba,0x63,Cp); \ + G1(Ofs_EFLAGS,Cp); \ + G4M(0x0a,JNB_JAE,0x01,STD,Cp); + +///////////////////////////////////////////////////////////////////////////// +// + +// 'no-jump' version, straight +#define Gen66(mode, Cp) \ + *(Cp)=OPERoverride; Cp+=BTA(BitDATA16, mode) + +// 64-bit operand size REX byte +#ifdef __x86_64__ +#define Gen48(Cp) G1(0x48,Cp) +#else +#define Gen48(Cp) +#endif + +// 'no-jump' version, tricky (depends on bit position) +#define G2_4(mode, val, Cp) \ + *((int *)(Cp))=(val); Cp+=BT24(BitDATA16, mode) + + +///////////////////////////////////////////////////////////////////////////// + +/* Code generation macros for x86 */ +#define GNX(d,s,l) {memcpy((d),(s),(l));(d)+=(l);} +#define G1(b,p) *(p)++=(unsigned char)(b) +#define G2(w,p) {unsigned short _w = (w); GNX(p,&_w,2); } +#define G2M(c,b,p) {unsigned short _wm = ((b)<<8)|(c);G2(_wm,p);} +#define G3(l,p) {unsigned int _l=(l); memcpy((p), &_l, 4);(p)+=3;} +#define G3M(c,b1,b2,p) {unsigned int _lm=((b2)<<16)|((b1)<<8)|(c);G3(_lm,p);} +#define G4(l,p) {unsigned int _l=(l); GNX(p,&_l,4);} +#define G4M(c,b1,b2,b3,p) {unsigned int _l=((unsigned)(b3)<<24)|((b2)<<16)|((b1)<<8)|(c);\ + GNX(p,&_l,4);} +#define G5(l,p) {unsigned long long _l=(l); GNX(p,&_l,5);} +#define G6(l,p) {unsigned long long _l=(l); GNX(p,&_l,6);} +#define G7(l,p) {unsigned long long _l=(l); memcpy((p), &_l, 8);(p)+=7;} +#define G8(l,p) {unsigned long long _l=(l); GNX(p,&_l,8);} + +///////////////////////////////////////////////////////////////////////////// +// +unsigned char *Fp87_op_x86(unsigned char *CodePtr, int exop, int reg); +void InitGen_x86(void); +void NodeUnlinker(TNode *G); + +extern unsigned char TailCode[]; + +static __inline__ int GoodNode(TNode *G, int mode) +{ + if (G->cs != LONG_CS) { + /* CS mismatch can confuse relative jump/call */ + e_printf("cs mismatch at %08x: old=%x new=%x\n", + G->key, G->cs, LONG_CS); + return 0; + } + if (G->mode != mode) { + /* mode mismatch can be 32/16 or MREALA */ + e_printf("mode mismatch at %08x: old=%x new=%x\n", + G->key, G->mode, mode); + return 0; + } + return 1; +} + +#endif diff --git a/src/base/emu-i386/simx86/codegen.h b/src/base/emu-i386/simx86/codegen.h new file mode 100644 index 0000000..3902aa6 --- /dev/null +++ b/src/base/emu-i386/simx86/codegen.h @@ -0,0 +1,307 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originaly was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#ifndef _EMU86_CODEGEN_H +#define _EMU86_CODEGEN_H + +#include +#include "syncpu.h" +#include "emudpmi.h" +#include "dos2linux.h" + +///////////////////////////////////////////////////////////////////////////// + +#define L_NOP 0 + +/* #define LEA_DI_R 1 */ +#define A_DI_0 2 +#define A_DI_1 3 +#define A_DI_2 4 +#define A_DI_2D 5 +#define A_SR_SH4 6 +#define O_FOP 7 + +#define L_REG 10 +#define S_REG 11 +#define L_REG2REG 12 +#define L_IMM 13 +#define L_IMM_R1 14 +#define S_DI_IMM 15 +#define S_DI_R 16 +#define L_MOVZS 17 +#define L_LXS1 18 +#define L_LXS2 19 +#define L_ZXAX 20 +#define L_CR0 21 +#define L_DI_R1 22 +#define S_DI 23 + +#define O_ADD_R 30 +#define O_OR_R 31 +#define O_ADC_R 32 +#define O_SBB_R 33 +#define O_AND_R 34 +#define O_SUB_R 35 +#define O_XOR_R 36 +#define O_CMP_R 37 +#define O_INC_R 38 +#define O_DEC_R 39 +#define O_SBB_FR 40 +#define O_SUB_FR 41 +#define O_CMP_FR 42 +#define O_CBWD 43 +#define O_XCHG 44 +#define O_XCHG_R 45 +#define O_INC 46 +#define O_DEC 47 +#define O_NOT 48 +#define O_NEG 49 +#define O_MUL 50 +#define O_IMUL 51 +#define O_DIV 52 +#define O_IDIV 53 +#define O_ROL 54 +#define O_ROR 55 +#define O_RCL 56 +#define O_RCR 57 +#define O_SHL 58 +#define O_SHR 59 +#define O_SAR 60 +#define O_OPAX 61 +#define O_XLAT 62 +#define O_CJMP 63 // unused +#define O_SLAHF 64 +#define O_SETFL 65 +#define O_BSWAP 66 +#define O_SETCC 67 +#define O_BITOP 68 +#define O_SHFD 69 +#define O_CLEAR 70 // xor r,r +#define O_TEST 71 // and r,r; or r,r +#define O_SBSELF 72 // sbb r,r +#define O_CMPXCHG 73 + +#define O_ADD_FR 75 +#define O_OR_FR 76 +#define O_ADC_FR 77 +#define O_AND_FR 78 +#define O_XOR_FR 79 + +#define O_PUSH 80 +#define O_PUSHI 81 +#define O_POP 82 +#define O_PUSH1 85 +#define O_PUSH2 86 +#define O_PUSH2F 87 +#define O_PUSH3 88 +#define O_POP1 89 +#define O_POP2 90 +#define O_POP3 91 +#define O_LEAVE 92 +#define O_INT 93 + +#define O_MOVS_SetA 100 +#define O_MOVS_MovD 101 +#define O_MOVS_SavA 102 +#define O_MOVS_LodD 103 +#define O_MOVS_StoD 104 +#define O_MOVS_ScaD 105 +#define O_MOVS_CmpD 106 +#define O_RDTSC 107 + +#define O_INPDX 108 +#define O_INPPC 109 +#define O_OUTPDX 110 +#define O_OUTPPC 111 + +#define JMP_INDIRECT 112 +#define JMP_LINK 113 +#define JB_LINK 114 +#define JF_LINK 115 +#define JLOOP_LINK 116 + +///////////////////////////////////////////////////////////////////////////// +// +#define ADDR16 0x00000001 +#define ADDR32 0x00000000 +#define BitADDR16 0 +#define DATA16 0x00000002 +#define DATA32 0x00000000 +#define BitDATA16 1 +#define MBYTE 0x00000004 +#define IMMED 0x00000008 +#define RM_REG 0x00000010 +#define NOFLDR 0x00000020 +#define SEGREG 0x00000040 +#define MLEA 0x00000080 +#define MREP 0x00000100 +#define MREPNE 0x00000200 +#define MEMADR 0x00000400 +#define MLOAD 0x00000800 +#define MSTORE 0x00001000 +#define MBYTX 0x00002000 +#define MOVSSRC 0x00004000 +#define MOVSDST 0x00008000 +#define MPOPRM 0x00010000 +#define MRETISP 0x00020000 +#define MREALA 0x00040000 + +#define CKSIGN 0x00100000 // check signal: for jumps +#define SKIPOP 0x00200000 +// for HOST_ARCH_SIM +#define CLROVF 0x00200000 +#define SETOVF 0x00400000 +#define IGNOVF 0x00800000 +// for HOST_ARCH_X86 +#define MREPCOND 0x01000000 // this is SCASx or CMPSx, REP can be terminated + // by flags + +// values for TNode.flags and IMeta.flags +#define F_FPOP 0x0001 +#define F_HITC 0x0002 +#define F_SLFL 0x0004 +#define F_INHI 0x0008 + +///////////////////////////////////////////////////////////////////////////// + +/* x386 */ +#define GetSWord(w) ((*w) = read_word(LONG_SS+sp)) +#define GetSLong(w) ((*w) = read_dword(LONG_SS+sp)) + +// returns 1(16 bit), 0(32 bit) +#define BTA(bpos, mode) (((mode) >> (bpos)) & 1) + +// returns 2(16 bit), 4(32 bit) +#define BT24(bpos, mode) (4 - (((mode) << (1-(bpos))) & 2)) + +static __inline__ int FastLog2(int v) +{ + return fls(v) - 1; +} + +///////////////////////////////////////////////////////////////////////////// + +static __inline__ void PUSH(int m, uint32_t w) +{ + unsigned int sp; + unsigned int addr; + + sp = (TheCPU.esp-BT24(BitDATA16, m)) & TheCPU.StackMask; + addr = LONG_SS + sp; + if (m&DATA16) { + e_invalidate(addr, 2); + WRITE_WORD(addr, w); + } else { + e_invalidate(addr, 4); + WRITE_DWORD(addr, w); + } +#ifdef KEEP_ESP + TheCPU.esp = (sp&TheCPU.StackMask) | (TheCPU.esp&~TheCPU.StackMask); +#else + TheCPU.esp = sp; +#endif +} + +///////////////////////////////////////////////////////////////////////////// + +static __inline__ void POP(int m, uint32_t *w) +{ + unsigned int sp = TheCPU.esp & TheCPU.StackMask; + if (m&DATA16) { + uint16_t w16; + GetSWord(&w16); + *w = (*w & 0xffff0000) | w16; sp+=2; + } + else { + GetSLong(w); sp+=4; + } + TheCPU.esp = (sp&TheCPU.StackMask) | (TheCPU.esp&~TheCPU.StackMask); +} + +static __inline__ void TOS_WORD(int m, uint16_t *w) // for segments +{ + unsigned int sp = TheCPU.esp & TheCPU.StackMask; + GetSWord(w); +} + +static __inline__ void NOS_WORD(int m, uint16_t *w) // for segments +{ + unsigned int sp = (TheCPU.esp+(m&DATA16? 2:4)) & TheCPU.StackMask; + GetSWord(w); +} + +static __inline__ void POP_ONLY(int m) +{ + unsigned int sp = TheCPU.esp + (m&DATA16? 2:4); + TheCPU.esp = (sp&TheCPU.StackMask) | (TheCPU.esp&~TheCPU.StackMask); +} + +void InitGen(void); +#ifdef X86_JIT +int NewIMeta(int npc, int *rc); +#else +static inline int NewIMeta(int npc, int *rc) {return 0;} +#endif +extern void (*Gen)(int op, int mode, ...); +extern void (*AddrGen)(int op, int mode, ...); +extern int (*Fp87_op)(int exop, int reg); +extern unsigned int (*CloseAndExec)(unsigned int PC, int mode); +void EndGen(void); +extern void fp87_set_rounding(void); +extern void fp87_save_except(void); +// +extern unsigned char InterOps[]; +extern char RmIsReg[]; +extern char OpIsPush[]; +extern char OpSize[]; +extern char OpSizeBit[]; + +#define OPSIZE(m) (OpSize[(m)&(DATA16|MBYTE)]) +#define OPSIZEBIT(m) (OpSizeBit[(m)&(DATA16|MBYTE)]) + +#ifdef X86_JIT +int Cpatch(sigcontext_t *scp); +int UnCpatch(unsigned char *eip); +void stub_rep(void) asm ("stub_rep__"); +void stub_stk_16(void) asm ("stub_stk_16__"); +void stub_stk_32(void) asm ("stub_stk_32__"); +void stub_wri_8 (void) asm ("stub_wri_8__" ); +void stub_wri_16(void) asm ("stub_wri_16__"); +void stub_wri_32(void) asm ("stub_wri_32__"); +void stub_read_8 (void) asm ("stub_read_8__" ); +void stub_read_16(void) asm ("stub_read_16__"); +void stub_read_32(void) asm ("stub_read_32__"); +#endif + +#endif diff --git a/src/base/emu-i386/simx86/cpatch.c b/src/base/emu-i386/simx86/cpatch.c new file mode 100644 index 0000000..bd67243 --- /dev/null +++ b/src/base/emu-i386/simx86/cpatch.c @@ -0,0 +1,573 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#include "emu86.h" +#include "trees.h" +#include "codegen-arch.h" +#include "cpatch.h" + +#ifdef HOST_ARCH_X86 + +static int in_cpatch; + +/* + * Return address of the stub function is passed into eip + */ +void m_munprotect(unsigned int addr, unsigned int len, unsigned char *eip) +{ + if (debug_level('e')>1) { + if (debug_level('e')>3) + e_printf("\tM_MUNPROT %08x:%p\n", addr,eip); + if (e_querymark(addr, len)) + e_printf("CODE %08x hit in DATA %p patch\n",addr,eip); + } + /* if only data in aliased low memory is hit, nothing to do */ + if (addr < LOWMEM_SIZE + HMASIZE) { + if (e_querymark(addr, len)) + // no need to invalidate the whole page here, + // as the page does not need to be unprotected + InvalidateNodeRange(addr,len,eip); + return; + } + /* Always unprotect and clear all code in the pages + * for DPMI data and code + * Maybe the stub was set up before that code was parsed. + * Clear that code */ +/* if (UnCpatch((void *)(eip-3))) leavedos_main(0); */ + len = PAGE_ALIGN(addr+len-1) - (addr & _PAGE_MASK); + addr &= _PAGE_MASK; + InvalidateNodeRange(addr,len,eip); +} + +#define repmovs(std,letter,cld) \ + asm volatile(#std" ; rep ; movs"#letter ";" #cld"\n\t" \ + : "=&c" (ecx), "=&D" (edi), "=&S" (esi) \ + : "0" (ecx), "1" (edi), "2" (esi) \ + : "memory") + +#define repstos(std,letter,cld) \ + asm volatile(#std" ; rep ; stos"#letter ";" #cld"\n\t" \ + : "=&c" (ecx), "=&D" (edi) \ + : "a" (eax), "0" (ecx), "1" (edi) \ + : "memory") + +struct rep_stack { + unsigned char *esi, *edi; + unsigned long ecx, eflags, edx, eax; +#ifdef __x86_64__ + unsigned long eax_pad; +#endif + unsigned char *eip; +} __attribute__((packed)); + + +void rep_movs_stos(struct rep_stack *stack) +{ + unsigned char *paddr = stack->edi; + unsigned int ecx = stack->ecx; + unsigned char *eip = stack->eip; + dosaddr_t addr; + unsigned int len = ecx; + unsigned char *edi; + unsigned char op; + unsigned int size; + + in_cpatch++; + assert(InCompiledCode); + InCompiledCode--; + addr = DOSADDR_REL(paddr); + if (*eip == 0xf3) /* skip rep */ + eip++; + op = eip[0]; + size = 1; + if (*eip == 0x66) { + size = 2; + op = eip[1]; + } + else if (*eip & 1) + size = 4; + len *= size; + m_munprotect(addr - ((EFLAGS & EFLAGS_DF) ? (len - size) : 0), + len, eip); + edi = LINEAR2UNIX(addr); + if ((op & 0xfe) == 0xa4) { /* movs */ + dosaddr_t source = DOSADDR_REL(stack->esi); + unsigned char *esi; + unsigned int v = vga_access(source, addr); + if (v) { + int df = ((EFLAGS & EFLAGS_DF) ? -size:size); + e_VgaMovs(&stack->edi, &stack->esi, ecx, df, v); + stack->ecx = 0; + goto done; + } + esi = LINEAR2UNIX(source); + if (ecx == len) { + if (EFLAGS & EFLAGS_DF) repmovs(std,b,cld); + else repmovs(,b,); + } + else if (ecx*2 == len) { + if (EFLAGS & EFLAGS_DF) repmovs(std,w,cld); + else repmovs(,w,); + } + else { + if (EFLAGS & EFLAGS_DF) repmovs(std,l,cld); + else repmovs(,l,); + } + if (EFLAGS & EFLAGS_DF) source -= len; + else source += len; + stack->esi = MEM_BASE32(source); + } + else if ((op & 0xfe) == 0xaa) { /* stos */ + unsigned int eax = stack->eax; + if (ecx == len) { + if (vga_write_access(addr)) { + if (EFLAGS & EFLAGS_DF) + vga_memset(addr - len + 1, eax, ecx); + else + vga_memset(addr, eax, ecx); + ecx = 0; + } + else if (EFLAGS & EFLAGS_DF) repstos(std,b,cld); + else repstos(,b,); + } + else if (ecx*2 == len) { + if (vga_write_access(addr)) { + if (EFLAGS & EFLAGS_DF) + vga_memsetw(addr - len + 2, eax, ecx); + else + vga_memsetw(addr, eax, ecx); + ecx = 0; + } + else if (EFLAGS & EFLAGS_DF) repstos(std,w,cld); + else repstos(,w,); + } + else { + if (vga_write_access(addr)) { + if (EFLAGS & EFLAGS_DF) + vga_memsetl(addr - len + 4, eax, ecx); + else + vga_memsetl(addr, eax, ecx); + ecx = 0; + } + else if (EFLAGS & EFLAGS_DF) repstos(std,l,cld); + else repstos(,l,); + } + } + else if ((op & 0xf6) == 0xa6) { /* cmps/scas */ + int repmod = (size == 1 ? MBYTE : size == 2 ? DATA16 : 0); + AR1.d = DOSADDR_REL(stack->edi); + TR1.d = stack->ecx; + repmod |= MOVSDST|MREPCOND|(eip[-1]==REPNE? MREPNE:MREP); + if ((op & 0xf6) == 0xa6) { /* cmps */ + repmod |= MOVSSRC; + AR2.d = DOSADDR_REL(stack->esi); + Gen_sim(O_MOVS_CmpD, repmod); + stack->esi = MEM_BASE32(AR2.d); + } + else { /* scas */ + DR1.d = stack->eax; + Gen_sim(O_MOVS_ScaD, repmod); + } + FlagSync_All(); + stack->edi = MEM_BASE32(AR1.d); + stack->ecx = TR1.d; + stack->eflags = (stack->eflags & ~EFLAGS_CC) | + (EFLAGS & EFLAGS_CC); + goto done; + } + if (EFLAGS & EFLAGS_DF) addr -= len; + else addr += len; + stack->edi = MEM_BASE32(addr); + stack->ecx = ecx; +done: + InCompiledCode++; + in_cpatch--; +} + +/* ======================================================================= */ + +void stk_16(dosaddr_t addr, Bit16u value) +{ + in_cpatch++; + assert(InCompiledCode); + InCompiledCode--; + e_invalidate(addr, 2); + WRITE_WORD(addr, value); + InCompiledCode++; + in_cpatch--; +} + +void stk_32(dosaddr_t addr, Bit32u value) +{ + in_cpatch++; + assert(InCompiledCode); + InCompiledCode--; + e_invalidate(addr, 4); + WRITE_DWORD(addr, value); + InCompiledCode++; + in_cpatch--; +} + +void wri_8(dosaddr_t addr, Bit8u value, unsigned char *eip) +{ + in_cpatch++; + assert(InCompiledCode); + InCompiledCode--; + m_munprotect(addr, 1, eip); + InCompiledCode++; + if (!emu_ldt_write(addr, value, 1)) { + if (vga_write_access(addr)) + vga_write(addr, value); + else + WRITE_BYTE(addr,value); + } + in_cpatch--; +} + +void wri_16(dosaddr_t addr, Bit16u value, unsigned char *eip) +{ + in_cpatch++; + assert(InCompiledCode); + InCompiledCode--; + m_munprotect(addr, 2, eip); + InCompiledCode++; + if (!emu_ldt_write(addr, value, 2)) { + if (vga_write_access(addr)) + vga_write_word(addr, value); + else + WRITE_WORD(addr,value); + } + in_cpatch--; +} + +void wri_32(dosaddr_t addr, Bit32u value, unsigned char *eip) +{ + in_cpatch++; + assert(InCompiledCode); + InCompiledCode--; + m_munprotect(addr, 4, eip); + InCompiledCode++; + if (!emu_ldt_write(addr, value, 4)) { + if (vga_write_access(addr)) + vga_write_dword(addr, value); + else + WRITE_DWORD(addr,value); + } + in_cpatch--; +} + +Bit8u read_8(dosaddr_t addr) +{ + return vga_read_access(addr) ? vga_read(addr) : READ_BYTE(addr); +} + +Bit16u read_16(dosaddr_t addr) +{ + return vga_read_access(addr) ? vga_read_word(addr) : READ_WORD(addr); +} + +Bit32u read_32(dosaddr_t addr) +{ + return vga_read_access(addr) ? vga_read_dword(addr) : READ_DWORD(addr); +} + +#ifdef __i386__ + +/* + * stack on entry: + * esp+00 return address + */ + +#define STUB_STK(cfunc) \ +" pushal\n \ + pushl %eax\n \ + pushl %edx\n \ + call "#cfunc"\n \ + addl $8,%esp\n \ + popal\n \ + ret\n" + + +#define STUB_WRI(cfunc) \ +" pushl (%esp)\n" /* return addr = patch point+3 */ \ +" pushl %eax\n" /* value to write */ \ +" pushl %edi\n" /* addr where to write */ \ +" call "#cfunc"\n" \ +" popl %edi\n" /* restore addr */ \ +" popl %eax\n" /* restore eax */ \ +" addl $4,%esp\n" /* remove parameters */ \ +" ret\n" + +#define STUB_READ(cfunc) \ +" pushl %edi\n" /* addr where to read */ \ +" call "#cfunc"\n" \ +" addl $4,%esp\n" /* remove parameters */ \ +" ret\n" + +asm ( +".text\n.globl stub_rep__\n" +"stub_rep__: jecxz 1f\n" /* zero move, nothing to do */ +" pushl %eax\n" /* save regs */ +" pushl %edx\n" /* edx used in 16bit overrun emulation, save too */ +" pushfl\n" /* push flags for DF */ +" pushl %ecx\n" /* push count */ +" pushl %edi\n" /* push dest address */ +" pushl %esi\n" /* push source address */ +" pushl %esp\n" /* push stack */ +" cld\n" +" call rep_movs_stos\n" +" addl $4,%esp\n" /* remove stack parameter */ +" popl %esi\n" /* obtain changed source address */ +" popl %edi\n" /* obtain changed dest address */ +" popl %ecx\n" /* obtain changed count */ +" popfl\n" /* real CPU flags back */ +" popl %edx\n" +" popl %eax\n" +"1: ret\n" +); + +/* ======================================================================= */ + +#else //__x86_64__ + +#define STUB_STK(cfunc) \ +" pushq %rax\n" /* save regs */ \ +" pushq %rcx\n" \ +" pushq %rdx\n" \ +" pushq %rdi\n" \ +" pushq %rsi\n" \ +" movl %edx,%edi\n" \ +" movl %eax,%esi\n" \ + /* pass base address in %rdi */ \ +" call "#cfunc"\n" \ +" popq %rsi\n" /* restore regs */ \ +" popq %rdi\n" \ +" popq %rdx\n" \ +" popq %rcx\n" \ +" popq %rax\n" \ +" ret\n" + + +#define STUB_WRI(cfunc) \ +" movq (%rsp),%rdx\n" /* return addr = patch point+3 */ \ +" pushq %rdi\n" /* save regs */ \ +" pushq %rsi\n" \ +" pushq %rax\n" \ +" movl %eax,%esi\n" /* value to write */ \ + /* pass addr where to write in %rdi */\ +" call "#cfunc"\n" \ +" popq %rax\n" /* restore regs */ \ +" popq %rsi\n" \ +" popq %rdi\n" \ +" ret\n" + +#define STUB_READ(cfunc) \ +" pushq %rdi\n" /* save regs */ \ +" call "#cfunc"\n" \ +" popq %rdi\n" /* restore regs */ \ +" ret\n" + +asm ( +".text\n.globl stub_rep__\n" +"stub_rep__: jrcxz 1f\n" /* zero move, nothing to do */ +" pushq %rax\n" /* save regs */ +" pushq %rax\n" /* save rax twice for 16-alignment */ +" pushq %rdx\n" +" pushfq\n" /* push flags for DF */ +" pushq %rcx\n" +" pushq %rdi\n" +" pushq %rsi\n" +" movq %rsp,%rdi\n" /* pass stack address in %rdi */ +" cld\n" +" call rep_movs_stos\n" +" popq %rsi\n" /* restore regs */ +" popq %rdi\n" +" popq %rcx\n" +" popfq\n" /* real CPU flags back */ +" popq %rdx\n" +" popq %rax\n" +" popq %rax\n" +"1: ret\n" +); + +#endif + +asm ( + ".text\n" +"stub_stk_16__:.globl stub_stk_16__\n"STUB_STK(stk_16) +"stub_stk_32__:.globl stub_stk_32__\n"STUB_STK(stk_32) +"stub_wri_8__: .globl stub_wri_8__\n "STUB_WRI(wri_8) +"stub_wri_16__:.globl stub_wri_16__\n"STUB_WRI(wri_16) +"stub_wri_32__:.globl stub_wri_32__\n"STUB_WRI(wri_32) +"stub_read_8__: .globl stub_read_8__\n "STUB_READ(read_8) +"stub_read_16__:.globl stub_read_16__\n"STUB_READ(read_16) +"stub_read_32__:.globl stub_read_32__\n"STUB_READ(read_32) +); + +/* call N(%ebx) */ +#define JSRPATCH(p,N) *((short *)(p))=0x53ff;p[2]=N; +#define JSRPATCHL(p,N) *((short *)(p))=0x93ff; *((int *)((p)+2))=N; + +/* + * enters here only from a fault + */ +int Cpatch(sigcontext_t *scp) +{ + unsigned char *p; + int w16; + unsigned int v; + unsigned char *eip = (unsigned char *)_scp_rip; + + if (in_cpatch) + return 0; + + p = eip; + if ((*p==0xf3 || *p==0xf2) && p[-1] == 0x90 && p[-2] == 0x90) { + // rep movs, rep stos, rep lods, rep scas, rep cmps + if (debug_level('e')>1) e_printf("### REP patch at %p\n",eip); + p-=2; + G2M(0xff,0x13,p); /* call (%ebx) */ + _scp_rip -= 2; /* make sure call (%ebx) is performed the first time */ + return 1; + } + + if (*p==0x66) w16=1,p++; else w16=0; + v = *((int *)p) & 0xffffff; + while (v==0x2a0489) { // stack: never fail + // mov %%{e}ax,(%%edx,%%ebp,1) + // we have a sequence: 66 89 04 2a + // or 89 04 2a + if (debug_level('e')>1) e_printf("### Stack patch at %p\n",p); + if (w16) { + p--; JSRPATCH(p,Ofs_stub_stk_16); p[3] = 0x90; p+=4; + } + else { + JSRPATCH(p,Ofs_stub_stk_32); p+=3; + } +#ifdef KEEP_ESP + p += 10; +#endif + /* check for optimized multiple register push */ + if (p[0]==0x89) return 1; //O_PUSH3 + p += 12; + if (p[0]==0xff) return 1; // already JSRPATCH'ed + if (*p==0x66) w16=1,p++; else w16=0; + v = *((int *)p) & 0xffffff; + /* extra check: should not fail */ + if (v!=0x2a0489) { + dbug_printf("CPUEMU: stack patch failure, fix source code! %x\n", v); + return 1; + } + } + if (v==0x2f0488) { // movb %%al,(%%edi,%%ebp,1) + // we have a sequence: 88 04 2f + if (debug_level('e')>1) e_printf("### Byte write patch at %p\n",eip); + JSRPATCH(p,Ofs_stub_wri_8); + return 1; + } + if (v==0x2f0489) { // mov %%{e}ax,(%%edi,%%ebp,1) + // we have a sequence: 89 04 2f + // or 66 89 04 2f + if (debug_level('e')>1) e_printf("### Word/Long write patch at %p\n",eip); + if (w16) { + p--; JSRPATCH(p,Ofs_stub_wri_16); p[3] = 0x90; + } + else { + JSRPATCH(p,Ofs_stub_wri_32); + } + return 1; + } + if (v==0x2f048a) { // movb (%%edi,%%ebp,1),%%al + // we have a sequence: 8a 04 2f 90 90 90 + if (debug_level('e')>1) e_printf("### Byte read patch at %p\n",eip); + JSRPATCHL(p,Ofs_stub_read_8); + return 1; + } + if (v==0x2f048b) { // mov (%%edi,%%ebp,1),%%{e}ax + // we have a sequence: 8b 04 2f 90 90 90 + // or 66 8b 04 2f 90 90 + if (debug_level('e')>1) e_printf("### Word/Long read patch at %p\n",eip); + if (w16) { + p--; JSRPATCHL(p,Ofs_stub_read_16); + } + else { + JSRPATCHL(p,Ofs_stub_read_32); + } + return 1; + } + if (debug_level('e')>1) e_printf("### Patch unimplemented: %08x\n",*((int *)p)); + return 0; +} + + +int UnCpatch(unsigned char *eip) +{ + register unsigned char *p; + p = eip; + + if (*eip != 0xff) return 1; + e_printf("UnCpatch at %p was %02x%02x%02x%02x%02x\n",eip, + eip[0],eip[1],eip[2],eip[3],eip[4]); + + if (p[1] == 0x13) { + p[0] = p[1] = 0x90; + } + else if (p[1] == 0x53) { + if ((unsigned char)p[2] == Ofs_stub_wri_8) { + *((short *)p) = 0x0488; p[2] = 0x2f; + } + else if ((unsigned char)p[2] == Ofs_stub_wri_16) { + *p++ = 0x66; *((short *)p) = 0x0488; p[3] = 0x2f; + } + else if ((unsigned char)p[2] == Ofs_stub_wri_32) { + *((short *)p) = 0x0489; p[2] = 0x2f; + } + else if ((unsigned char)p[2] == Ofs_stub_stk_16) { + *((int *)p) = 0x2a048966; + } + else if ((unsigned char)p[2] == Ofs_stub_stk_32) { + *((short *)p) = 0x0489; p[2] = 0x2a; + } + else return 1; + } + else return 1; + e_printf("UnCpatched at %p is %02x%02x%02x%02x%02x\n",eip, + eip[0],eip[1],eip[2],eip[3],eip[4]); + return 0; +} + +#endif //HOST_ARCH_X86 + +/* ======================================================================= */ + diff --git a/src/base/emu-i386/simx86/cpatch.h b/src/base/emu-i386/simx86/cpatch.h new file mode 100644 index 0000000..bbcf2bd --- /dev/null +++ b/src/base/emu-i386/simx86/cpatch.h @@ -0,0 +1,22 @@ +#ifdef __cplusplus +#define EXTERN extern "C" +#else +#define EXTERN extern +#endif +#ifdef __i386__ +#define ASMLINKAGE(x,y,z) EXTERN __attribute__((cdecl)) x y z asm(#y) +#else +#define ASMLINKAGE(x,y,z) EXTERN x y z asm(#y) +#endif + +struct rep_stack; + +ASMLINKAGE(void,rep_movs_stos,(struct rep_stack *stack)); +ASMLINKAGE(void,stk_16,(dosaddr_t addr, Bit16u value)); +ASMLINKAGE(void,stk_32,(dosaddr_t addr, Bit32u value)); +ASMLINKAGE(void,wri_8,(dosaddr_t addr, Bit8u value, unsigned char *eip)); +ASMLINKAGE(void,wri_16,(dosaddr_t addr, Bit16u value, unsigned char *eip)); +ASMLINKAGE(void,wri_32,(dosaddr_t addr, Bit32u value, unsigned char *eip)); +ASMLINKAGE(Bit8u,read_8,(dosaddr_t addr)); +ASMLINKAGE(Bit16u,read_16,(dosaddr_t addr)); +ASMLINKAGE(Bit32u,read_32,(dosaddr_t addr)); diff --git a/src/base/emu-i386/simx86/cpu-emu.c b/src/base/emu-i386/simx86/cpu-emu.c new file mode 100644 index 0000000..93e713f --- /dev/null +++ b/src/base/emu-i386/simx86/cpu-emu.c @@ -0,0 +1,1266 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#ifdef X86_EMULATOR +#include +#include /* for memset */ +#include +#include +#include "emu.h" +#include "timers.h" +#include "pic.h" +#include "mhpdbg.h" +#include "cpu-emu.h" +#include "emu86.h" +#include "codegen-arch.h" +#include "emudpmi.h" +#include "mapping.h" +#include "dis8086.h" +#include "sig.h" + +/* ======================================================================= */ + +#if PROFILE +hitimer_t AddTime, SearchTime, ExecTime, CleanupTime; // for debug +hitimer_t GenTime, LinkTime; +#endif + +static hitimer_t TotalTime; +static int iniflag = 0; + +/* This needs to be merged someday with 'mode' */ +volatile int CEmuStat = 0; + +int IsV86Emu = 1; +int IsDpmiEmu = 1; + +/* This keeps the delta time in CPU clocks between SIGALRMs and + * SIGPROFs, i.e. the time spent by the system outside of dosemu, + * which should be considered in time stretching */ +int e_sigpa_count; + +int in_vm86_emu = 0; +int in_dpmi_emu = 0; +jmp_buf jmp_env; + +union _SynCPU TheCPU_union; + +int Running = 0; +volatile int InCompiledCode = 0; + +unsigned int trans_addr, return_addr; // PC + +#ifdef DEBUG_TREE +FILE *tLog = NULL; +#endif +#ifdef ASM_DUMP +FILE *aLog = NULL; +#endif + +/* + * -------------------------------------------------------------- + * x86 sigcontext: + * 00 unsigned short gs, __gsh; + * 01 unsigned short fs, __fsh; + * 02 unsigned short es, __esh; + * 03 unsigned short ds, __dsh; + * 04 unsigned long edi; + * 05 unsigned long esi; + * 06 unsigned long ebp; + * 07 unsigned long esp; + * 08 unsigned long ebx; + * 09 unsigned long edx; + * 10 unsigned long ecx; + * 11 unsigned long eax; + * 12 unsigned long trapno; + * 13 unsigned long err; + * 14 unsigned long eip; + * 15 unsigned short cs, __csh; + * 16 unsigned long eflags; + * 17 unsigned long esp_at_signal; + * 18 unsigned short ss, __ssh; + * 19 fpregset_t fpstate; + * 20 unsigned long oldmask; + * 21 unsigned long cr2; + * + * x86 vm86plus regs: + * 00 long ebx; + * 01 long ecx; + * 02 long edx; + * 03 long esi; + * 04 long edi; + * 05 long ebp; + * 06 long eax; + * 07 long __null_ds; + * 08 long __null_es; + * 09 long __null_fs; + * 10 long __null_gs; + * 11 long orig_eax; + * 12 long eip; + * 13 unsigned short cs, __csh; + * 14 long eflags; + * 15 long esp; + * 16 unsigned short ss, __ssh; + * 17 unsigned short es, __esh; + * 18 unsigned short ds, __dsh; + * 19 unsigned short fs, __fsh; + * 20 unsigned short gs, __gsh; + * -------------------------------------------------------------- + */ +cpuctx_t e_scp; /* initialized to 0 */ + +/* ======================================================================= */ + +unsigned long eTSSMASK = 0; + +void e_priv_iopl(int pl) +{ + pl &= 3; + TheCPU.eflags = (TheCPU.eflags & ~EFLAGS_IOPL) | + (pl << 12); + e_printf("eIOPL: set IOPL to %d, flags=%#x\n",pl,TheCPU.eflags); +} + +void InvalidateSegs(void) +{ + CS_DTR.Attrib=0; + SS_DTR.Attrib=0; + DS_DTR.Attrib=0; + ES_DTR.Attrib=0; + FS_DTR.Attrib=0; + GS_DTR.Attrib=0; +} + +/* ======================================================================= */ + + +static char ehextab[] = "0123456789abcdef"; + +/* WARNING - do not convert spaces to tabs! */ +/* WARNING: the below code is written by some idiots, don't read! */ +#define ERB_LLEN 0x39 +#define ERB_LEFTM 5 +#define ERB_L1 0x00 +#define ERB_L2 (ERB_L1+ERB_LLEN) +#define ERB_L3 (ERB_L1+2*ERB_LLEN) +#define ERB_L4 (ERB_L1+3*ERB_LLEN) +#define ERB_L5 (ERB_L1+4*ERB_LLEN) +#define ERB_L6 (ERB_L5+0x23) +#define ERB_L7 (ERB_L6+0x37) +static char eregbuf[] = +/*00*/ "\teax=00000000 ebx=00000000 ecx=00000000 edx=00000000 \n" +/*39*/ "\tesi=00000000 edi=00000000 ebp=00000000 esp=00000000 \n" +/*72*/ "\t vf=00000000 cs=0000 ds=0000 es=0000 \n" +/*ab*/ "\t fs=0000 gs=0000 ss=0000 eip=00000000 \n" +/*e4*/ "\tops=00 00 00 00 00 00 00 00 00 00\n" + "\tstk=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000\n"; + +static inline void exprintl(unsigned long val,char *bf,unsigned int pos) +{ + char *p=bf+pos+7; + unsigned long v = val; +#ifdef __x86_64__ + v &= 0xffffffffffff; + if (v > 0xffffffff) p += 4; +#endif + while (v) { *p-- = ehextab[v&15]; v>>=4; } +} + +static inline void exprintw(unsigned short val,char *bf,unsigned int pos) +{ + char *p=bf+pos+3; + unsigned long v = val; + while (v) { *p-- = ehextab[v&15]; v>>=4; } +} + +static inline void exprintb(unsigned char val,char *bf,unsigned int pos) +{ + char *p=bf+pos+1; + unsigned long v = val; + while (v) { *p-- = ehextab[v&15]; v>>=4; } +} + +char *e_print_regs(void) +{ + static char buf[sizeof(eregbuf) + 300]; + char *p = buf; + char *q = eregbuf; + + while (*q) *p++ = *q++; + *p=0; + exprintl(rEAX,buf,(ERB_L1+ERB_LEFTM)); + exprintl(rEBX,buf,(ERB_L1+ERB_LEFTM)+13); + exprintl(rECX,buf,(ERB_L1+ERB_LEFTM)+26); + exprintl(rEDX,buf,(ERB_L1+ERB_LEFTM)+39); + exprintl(rESI,buf,(ERB_L2+ERB_LEFTM)); + exprintl(rEDI,buf,(ERB_L2+ERB_LEFTM)+13); + exprintl(rEBP,buf,(ERB_L2+ERB_LEFTM)+26); + exprintl(rESP,buf,(ERB_L2+ERB_LEFTM)+39); + exprintl(TheCPU.eflags,buf,(ERB_L3+ERB_LEFTM)); + exprintw(TheCPU.cs,buf,(ERB_L3+ERB_LEFTM)+13); + exprintw(TheCPU.ds,buf,(ERB_L3+ERB_LEFTM)+26); + exprintw(TheCPU.es,buf,(ERB_L3+ERB_LEFTM)+39); + exprintw(TheCPU.fs,buf,(ERB_L4+ERB_LEFTM)); + exprintw(TheCPU.gs,buf,(ERB_L4+ERB_LEFTM)+13); + exprintw(TheCPU.ss,buf,(ERB_L4+ERB_LEFTM)+26); + exprintl(TheCPU.eip,buf,(ERB_L4+ERB_LEFTM)+39); + { + int i; + dosaddr_t csp = LONG_CS+TheCPU.eip; + dosaddr_t st = LONG_SS+TheCPU.esp; + if (csp < 0x110000 || dpmi_is_valid_range(csp, 4096)) { + unsigned char *op = MEM_BASE32(csp); + for (i=(ERB_L5+ERB_LEFTM); i<(ERB_L6); i+=3) { + exprintb(*op++,buf,i); + } + } + if (st < 0x110000 || dpmi_is_valid_range(st, 4096)) { + unsigned short *stk = (unsigned short *)MEM_BASE32(st); + for (i=(ERB_L6+ERB_LEFTM); i<(ERB_L7-2); i+=5) { + exprintw(*stk++,buf,i); + } + } + } + return buf; +} + +#if MAX_SELECTORS != 8192 +#error MAX_SELECTORS needs to be 8192 +#endif + +#define GetSegmentBaseAddress(s) GetSegmentBase(s) +#define IsSegment32(s) dpmi_segment_is32(s) + +char *e_print_scp_regs(cpuctx_t *scp, int pmode) +{ + static char buf[300]; + unsigned short *stk; + int i, j; + + i = sprintf(buf, "RAX: %08x RBX: %08x RCX: %08x RDX: %08x" + " VFLAGS(h): %08x\n", + _rax, _rbx, _rcx, _rdx, _eflags); + i += sprintf(buf + i, "RSI: %08x RDI: %08x RBP: %08x RSP: %08x\n", + _rsi, _rdi, _rbp, _rsp); + i += sprintf(buf + i, "CS: %04x DS: %04x ES: %04x FS: %04x GS: %04x SS: %04x\n", + _cs, _ds, _es, _fs, _gs, _ss); + + if (pmode & 2) { +// buf[(ERB_L4+ERB_LEFTM)+47] = 0; + } + else { + if (pmode & 1) + stk = SEL_ADR(_ss, _esp); + else + stk = MK_FP32(_ss,_LWORD(esp)); + i += sprintf(buf + i, "Stack:"); + for (j = 0; j < 16; j++) + i += sprintf(buf + i, " %04hx", *stk++); + i += sprintf(buf + i, "\n"); + } + return buf; +} + + +char *e_emu_disasm(unsigned char *org, int is32, unsigned int refseg) +{ + static char buf[512]; + static char frmtbuf[256]; + int rc = 0; + int i; + char *p = buf, *p1; + dosaddr_t code; + dosaddr_t org2; + unsigned int segbase; +#ifdef USE_MHPDBG + unsigned int ref; +#endif + + if (in_dpmi_emu) + segbase = GetSegmentBase(refseg); + else + segbase = refseg * 16; + code = DOSADDR_REL(org); + org2 = code - segbase; +#ifdef USE_MHPDBG + rc = dis_8086(code, frmtbuf, is32, &ref, segbase); + p = buf + sprintf(buf,"%08x: ",code); +#endif + for (i=0; ics <= 0xffff) + csp2 = GetSegmentBase(seg); + org = csp2 + _eip; + } + if (org==lasta) { insrep=1; return buf; } /* skip 'rep xxx' steps */ + lasta = org; insrep = 0; + + rc = dis_8086(org, frmtbuf, pmode&&IsSegment32(seg), + &ref, (pmode? csp2 : refseg * 16)); + + pb = buf; + org2 = MEM_BASE32(org); + while ((*org2&0xfc)==0x64) org2++; /* skip most prefixes */ + if ((debug_level('t')>3)||(InterOps[*org2]&2)) + pb += sprintf(pb,"%s",e_print_scp_regs(scp,pmode)); + + p = pb + sprintf(pb," %08x: ",org); + for (i=0; i> (ifpr<<1)) & 3) { + case 0: case 1: +#endif + p += sprintf(p,"\tFp%d\t%16.8Lf%s\n\t", ifpr, *q, buf2); +#ifdef FPU_TAGS + break; + case 2: p += sprintf(p,"\tFp%d\tNaN/Inf%s\n\t", ifpr, buf2); + break; + case 3: p += sprintf(p,"\tFp%d\t****%s\n\t", ifpr, buf2); + break; + } +#endif + ifpr = (ifpr+1) & 7; + } + p += sprintf(p,"\tst=%d(%p) sw=%04x cw=%04x tag=%04x\n", + TheCPU.fpstt, FPRSTT, TheCPU.fpus, TheCPU.fpuc, TheCPU.fptag); + return buf; +} + +void GCPrint(unsigned char *cp, unsigned char *cbase, int len) +{ + int i; + while (len) { + dbug_printf(">>> %08tx:",cp-cbase); + for (i=0; (i<16) && len; i++,len--) dbug_printf(" %02x",*cp++); + dbug_printf("\n"); + } +} + + +char *showreg(signed char r) +{ + static const char *s4[] = { + "cUNP","xUNP","ZERO"," GS"," FS"," ES"," DS"," EDI", + " ESI"," EBP"," ESP"," EBX"," EDX"," ECX"," EAX","TPNO", + "SCPE"," EIP"," CS","EFLG"," SS"," CR2","FPCS","FPST", + "FNI0","FNI1","FNI2"," SIG"," ERR","SMSK","MODE"," CR0", + "FPRG","xFPR","TIML","TIMH"," GSl"," GSu"," GSo"," FSl", + " FSu"," FSo"," ESl"," ESu"," XES"," DSl"," DSu"," DSo", + " CSl"," CSu"," CSo"," SSl"," SSh"," SSo"," s16","xs16", + " s32","xs32"," w8"," xw8"," w16","xw16"," w32","xw32" + }; + static char m1[32]; + static int i = 0; + char *p; + unsigned int ix = r; + i = (i+8) & 0x18; p = m1+i; // for side effects in printf + if ((ix&0xff)==0xff) { *p=0; return p; } + ix = (ix>>2)&0x3f; + *((int *)p) = *((const int *)(s4[ix])); + if ((r&3)==0) p[4]=0; else { + p[4]='+'; p[5]=(r&3)+'0'; p[6]=0; + } + return p; +} + +char *showmode(unsigned int m) +{ + static char m0[28] = "ADBIRSGLrnMNXbsd....CoOi"; + static char m1[28]; + int i,j; + m &= 0xf0ffff; j = 24; + for (i=0; (i<24)&&(m!=0); i++) { if (m&1) m1[--j]=m0[i]; m>>=1; } + m1[24]=0; + return m1+j; +} + + +/* ======================================================================= */ +/* + * Register movements between the dosemu REGS and the emulator cpu + * A - Real and VM86 mode + */ + +/* + * Enter emulator in VM86 mode (sys_vm86) + */ +static void Reg2Cpu (int mode) +{ + /* + * Enter VM86 + */ + + /* From now on we'll work on the cpuemu eflags (BUT vm86s eflags can be + * changed asynchronously by signals) + * Note that IOPL=3 in the emulated flags so cpuemu works directly with + IF, not VIF */ + TheCPU.eflags = (vm86s.regs.eflags & SAFE_MASK) | IOPL_MASK; + if (isset_IF()) + TheCPU.eflags |= EFLAGS_IF; + TheCPU.eflags |= (VM | RF); // RF is cosmetic... + TheCPU.df_increments = (TheCPU.eflags&DF)?0xfcfeff:0x040201; + + if (debug_level('e')>1) e_printf("Reg2Cpu> vm86=%08x dpm=%08x emu=%08x\n", + REG(eflags),get_FLAGS(TheCPU.eflags),TheCPU.eflags); + TheCPU.eax = REG(eax); /* 2c -> 18 */ + TheCPU.ebx = REG(ebx); /* 20 -> 00 */ + TheCPU.ecx = REG(ecx); /* 28 -> 04 */ + TheCPU.edx = REG(edx); /* 24 -> 08 */ + TheCPU.esi = REG(esi); /* 14 -> 0c */ + TheCPU.edi = REG(edi); /* 10 -> 10 */ + TheCPU.ebp = REG(ebp); /* 18 -> 14 */ + TheCPU.esp = REG(esp); /* 1c -> 3c */ + TheCPU.err = 0; + TheCPU.eip = vm86s.regs.eip&0xffff; + + TheCPU.int_revectored = vm86s.int_revectored; + + SetSegReal(SREG(cs),Ofs_CS); + SetSegReal(SREG(ss),Ofs_SS); + SetSegReal(SREG(ds),Ofs_DS); + SetSegReal(SREG(es),Ofs_ES); + SetSegReal(SREG(fs),Ofs_FS); + SetSegReal(SREG(gs),Ofs_GS); + trans_addr = LONG_CS + TheCPU.eip; + + /* FPU state is loaded later on demand for JIT, not used for simulator */ + TheCPU.fpstate = &vm86_fpu_state; + if (debug_level('e')>1) { + if (debug_level('e')==9) e_printf("Reg2Cpu< vm86=%08x dpm=%08x emu=%08x\n%s\n", + REG(eflags),get_FLAGS(TheCPU.eflags),TheCPU.eflags, + e_print_regs()); + else e_printf("Reg2Cpu< vm86=%08x dpm=%08x emu=%08x\n", + REG(eflags),get_FLAGS(TheCPU.eflags),TheCPU.eflags); + } +} + +/* + * Exit emulator in VM86 mode and return to dosemu (return_to_32bit) + */ +void Cpu2Reg (void) +{ + if (debug_level('e')>1) e_printf("Cpu2Reg> vm86=%08x dpm=%08x emu=%08x\n", + REG(eflags),get_FLAGS(TheCPU.eflags),TheCPU.eflags); + REG(eax) = TheCPU.eax; + REG(ebx) = TheCPU.ebx; + REG(ecx) = TheCPU.ecx; + REG(edx) = TheCPU.edx; + REG(esi) = TheCPU.esi; + REG(edi) = TheCPU.edi; + REG(ebp) = TheCPU.ebp; + REG(esp) = TheCPU.esp; + SREG(ds) = TheCPU.ds; + SREG(es) = TheCPU.es; + SREG(ss) = TheCPU.ss; + SREG(fs) = TheCPU.fs; + SREG(gs) = TheCPU.gs; + SREG(cs) = TheCPU.cs; + REG(eip) = TheCPU.eip; + /* + * move flags from EFLAGS to eflags; resync vm86s eflags + * from the emulated ones. + * The cpuemu should not change VIP, the good one is always in vm86s. + */ + REG(eflags) = (REG(eflags) & VIP) | + (TheCPU.eflags & ~VIP); + if (TheCPU.eflags & EFLAGS_IF) + set_IF(); + else + clear_IF(); + REG(eflags) |= EFLAGS_IF; + + if (TheCPU.fpstate == NULL) { + if (!CONFIG_CPUSIM) + savefpstate(vm86_fpu_state); + else + fp87_save_except(); + fesetenv(&dosemu_fenv); + } + + if (debug_level('e')>1) e_printf("Cpu2Reg< vm86=%08x dpm=%08x emu=%08x\n", + REG(eflags),get_FLAGS(TheCPU.eflags),TheCPU.eflags); +} + + +/* ======================================================================= */ + +static void Scp2Cpu (cpuctx_t *scp) +{ + TheCPU.eax = _eax; + TheCPU.ebx = _ebx; + TheCPU.ecx = _ecx; + TheCPU.edx = _edx; + TheCPU.esi = _esi; + TheCPU.edi = _edi; + TheCPU.ebp = _ebp; + TheCPU.esp = _esp; + + TheCPU.eip = _eip; + TheCPU.eflags = _eflags | 2; + + TheCPU.cs = _cs; + TheCPU.fs = _fs; + TheCPU.gs = _gs; + + TheCPU.ds = _ds; + TheCPU.es = _es; + + TheCPU.scp_err = 0; + TheCPU.ss = _ss; + TheCPU.cr2 = _cr2; + TheCPU.df_increments = (TheCPU.eflags&DF)?0xfcfeff:0x040201; + + TheCPU.fpstate = &vm86_fpu_state; +} + +/* + * Build a sigcontext structure to enter fault handling from DPMI + */ +static void Cpu2Scp (cpuctx_t *scp, int trapno) +{ + if (debug_level('e')>1) e_printf("Cpu2Scp> scp=%08x dpm=%08x fl=%08x\n", + _eflags,get_FLAGS(TheCPU.eflags),TheCPU.eflags); + + /* setup stack context from cpu registers */ + _eax = TheCPU.eax; + _ebx = TheCPU.ebx; + _ecx = TheCPU.ecx; + _edx = TheCPU.edx; + _esi = TheCPU.esi; + _edi = TheCPU.edi; + _ebp = TheCPU.ebp; + _esp = TheCPU.esp; + _eip = TheCPU.eip; + + _cs = TheCPU.cs; + _fs = TheCPU.fs; + _gs = TheCPU.gs; + + _ds = TheCPU.ds; + _es = TheCPU.es; + + _err = TheCPU.scp_err; + _ss = TheCPU.ss; + _cr2 = TheCPU.cr2; + _trapno = trapno; + /* Error code format: + * b31-b16 = 0 (undef) + * b15-b0 = selector, where b2=LDT/GDT, b1=IDT, b0=EXT + * (b0-b1 are currently unimplemented here) + */ + if (!TheCPU.err) _err = 0; //??? + if (TheCPU.fpstate == NULL) { + if (!CONFIG_CPUSIM) + savefpstate(vm86_fpu_state); + else + fp87_save_except(); + /* there is no real need to save and restore the FPU state of the + emulator itself: savefpstate (fnsave) also resets the current FPU + state using fninit; fesetenv then restores trapping of division by + zero and overflow which is good enough for calling FPU-using + routines. + */ + fesetenv(&dosemu_fenv); + } + + /* push running flags - same as eflags, RF is cosmetic */ + _eflags = (TheCPU.eflags & (eTSSMASK|0xfd5)) | 0x10002; + if (debug_level('e')>1) e_printf("Cpu2Scp< scp=%08x vm86=%08x dpm=%08x fl=%08x\n", + _eflags,REG(eflags),get_FLAGS(TheCPU.eflags),TheCPU.eflags); +} + + +/* ======================================================================= */ +/* + * Register movements between the dosemu REGS and the emulator cpu + * B - DPMI and PM mode + */ + +/* + * Enter emulator in DPMI mode (context_switch) + */ +static int Scp2CpuD(cpuctx_t *scp) +{ + unsigned char big; int mode=0; + + Scp2Cpu(scp); + + mode |= ADDR16; + TheCPU.err = SetSegProt(mode&ADDR16,Ofs_CS,&big,TheCPU.cs); + if (TheCPU.err) goto erseg; + if (big) mode=0; else mode |= DATA16; + + TheCPU.err = SetSegProt(mode&ADDR16,Ofs_DS,&big,TheCPU.ds); + if (TheCPU.err) goto erseg; + TheCPU.err = SetSegProt(mode&ADDR16,Ofs_SS,&big,TheCPU.ss); + if (TheCPU.err) goto erseg; + TheCPU.StackMask = (big? 0xffffffff : 0x0000ffff); + TheCPU.err = SetSegProt(mode&ADDR16,Ofs_ES,&big,TheCPU.es); + if (TheCPU.err) goto erseg; + TheCPU.err = SetSegProt(mode&ADDR16,Ofs_FS,&big,TheCPU.fs); + if (TheCPU.err) goto erseg; + TheCPU.err = SetSegProt(mode&ADDR16,Ofs_GS,&big,TheCPU.gs); +erseg: + trans_addr = LONG_CS + _eip; + if (debug_level('e')>1) { + if (debug_level('e')==3) e_printf("Scp2CpuD%s: %08x -> %08x\n\tIP=%08x:%08x\n%s\n", + (TheCPU.err? " ERR":""), + _eflags, TheCPU.eflags, LONG_CS, _eip, + e_print_regs()); + else e_printf("Scp2CpuD%s: %08x -> %08x\n", + (TheCPU.err? " ERR":""), _eflags, TheCPU.eflags); + } + return mode; +} + + +/* ======================================================================= */ + + +void reset_emu_cpu(void) +{ + TheCPU.cr[0] = 0x13; /* valid bits: 0xe005003f */ + TheCPU.cr[4] = CR4_VME; + TheCPU.dr[4] = 0xffff1ff0; + TheCPU.dr[5] = 0x400; + TheCPU.dr[6] = 0xffff1ff0; + TheCPU.dr[7] = 0x400; + TheCPU.GDTR.Limit = TheCPU.IDTR.Limit = TheCPU.LDTR.Limit = TheCPU.TR.Limit = 0xffff; + TheCPU.cs_cache.BoundL = 0x400; + TheCPU.cs_cache.BoundH = 0x10ffff; + TheCPU.ss_cache.BoundL = 0x100; + TheCPU.ss_cache.BoundH = 0x10ffff; + TheCPU.ds_cache.BoundL = 0; + TheCPU.ds_cache.BoundH = 0x10ffff; + TheCPU.es_cache.BoundL = 0; + TheCPU.es_cache.BoundH = 0x10ffff; + TheCPU.fs_cache.BoundL = 0; + TheCPU.fs_cache.BoundH = 0x10ffff; + TheCPU.gs_cache.BoundL = 0; + TheCPU.gs_cache.BoundH = 0x10ffff; + + Reg2Cpu(ADDR16|DATA16); + TheCPU.StackMask = 0x0000ffff; +} + +void init_emu_cpu(void) +{ + if (Ofs_END > 128) { + error("CPUEMU: Ofs_END is too large, %i\n", Ofs_END); + config.exitearly = 1; + } + init_emu_npu(); + + switch (vm86s.cpu_type) { + case CPU_286: + eTSSMASK = 0; + break; + case CPU_386: + eTSSMASK = NT_MASK | IOPL_MASK; + break; + case CPU_486: + eTSSMASK = AC_MASK | NT_MASK | IOPL_MASK; + break; + default: + eTSSMASK = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK; + break; + } + e_printf("EMU86: tss mask=%08lx\n", eTSSMASK); +#ifdef HOST_ARCH_X86 + if (config.cpusim) + InitGen_sim(); + else { + InitGen_x86(); + InitTrees(); + } +#else + InitGen_sim(); +#endif + + IDT = NULL; + if (GDT==NULL) { + /* The GDT is not really used (yet?) but some instructions + like verr/verw refer to it, so just allocate a 0 GDT */ + GDT = calloc(65536,1); + } + /* use the cached LDT used by dpmi (w/o GDT) */ + if (LDT==NULL) { + LDT = (Descriptor *)dpmi_get_ldt_buffer(); + e_printf("LDT allocated at %p\n",LDT); + TheCPU.LDTR.Base = (long)LDT; + TheCPU.LDTR.Limit = 0xffff; + } +#ifdef HOST_ARCH_X86 + TheCPU.unprotect_stub = stub_rep; + TheCPU.stub_wri_8 = stub_wri_8; + TheCPU.stub_wri_16 = stub_wri_16; + TheCPU.stub_wri_32 = stub_wri_32; + TheCPU.stub_stk_16 = stub_stk_16; + TheCPU.stub_stk_32 = stub_stk_32; + TheCPU.stub_read_8 = stub_read_8; + TheCPU.stub_read_16 = stub_read_16; + TheCPU.stub_read_32 = stub_read_32; +#endif + + Running = 1; +} + +/* + * Under cpuemu, SIGALRM is redirected here. We need a source of + * asynchronous signals because without it any badly-behaved pgm + * can stop us forever. + */ +void e_gen_sigalrm(void) +{ + if (!in_dpmi_emu && !in_vm86_emu) + return; + + /* here we come from the kernel with cs==UCODESEL, as + * the passed context is that of dosemu, NOT that of the + * emulated CPU! */ + TheCPU.sigalrm_pending = 1; /* tested by loops */ +} + +void enter_cpu_emu(void) +{ + unsigned int realdelta = config.update / TIMER_DIVISOR; + + if (debug_level('e')) { + TotalTime = 0; +#if PROFILE + SearchTime = AddTime = ExecTime = CleanupTime = + GenTime = LinkTime = 0; +#endif + dbug_printf("EMU86: delta alrm=%d speed=%d\n", + realdelta,config.CPUSpeedInMhz); + } + e_sigpa_count = 0; + +#ifdef DEBUG_TREE + tLog = fopen(DEBUG_TREE_FILE,"w"); +#endif +#ifdef ASM_DUMP + aLog = fopen(ASM_DUMP_FILE,"w"); +#endif + dbug_printf("======================= ENTER CPU-EMU ===============\n"); + flush_log(); + iniflag = 1; +} + +static void print_statistics(void) +{ + dbug_printf("Total cpuemu time %16lld us (incl.trace)\n", + (long long)TotalTime/config.CPUSpeedInMhz); +#if PROFILE + dbug_printf("Total codgen time %16lld us\n", + (long long)GenTime/config.CPUSpeedInMhz); + dbug_printf("Total linker time %16lld us\n", + (long long)LinkTime/config.CPUSpeedInMhz); + dbug_printf("Total exec time %16lld us (incl.faults)\n", + (long long)ExecTime/config.CPUSpeedInMhz); + dbug_printf("Total insert time %16lld us\n", + (long long)AddTime/config.CPUSpeedInMhz); + dbug_printf("Total search time %16lld us\n", + (long long)SearchTime/config.CPUSpeedInMhz); + dbug_printf("Total clean time %16lld us\n", + (long long)CleanupTime/config.CPUSpeedInMhz); + dbug_printf("Max tree nodes %16d\n",MaxNodes); + dbug_printf("Max node size %16d\n",MaxNodeSize); + dbug_printf("Max tree depth %16d\n",MaxDepth); + dbug_printf("Nodes parsed %16d\n",TotalNodesParsed); + dbug_printf("Find misses %16d\n",NodesNotFound); + dbug_printf("Nodes executed %16d\n",TotalNodesExecd); + if (TotalNodesExecd) { + unsigned long long k; + k = ((long long)NodesFound * 100UL) / + (long long)TotalNodesExecd; + dbug_printf("Find hits %16d (%lld%%)\n",NodesFound,k); + k = ((long long)NodesFastFound * 100UL) / + (long long)TotalNodesExecd; + dbug_printf("Find last hits %16d (%lld%%)\n", + NodesFastFound,k); + } + dbug_printf("Page faults %16d\n",PageFaults); + dbug_printf("Signals received %16d\n",EmuSignals); + dbug_printf("Tree cleanups %16d\n",TreeCleanups); +#endif +} + +void leave_cpu_emu(void) +{ + if (IS_EMU() && iniflag) { + iniflag = 0; +#ifdef SKIP_EMU_VBIOS + if (IOFF(0x10)==CPUEMU_WATCHER_OFF) + IOFF(0x10)=INT10_WATCHER_OFF; +#endif +#ifdef HOST_ARCH_X86 + EndGen(); +#endif +#ifdef DEBUG_TREE + fclose(tLog); tLog = NULL; +#endif +#ifdef ASM_DUMP + fclose(aLog); aLog = NULL; +#endif + mprot_end(); + + free(GDT); + LDT = NULL; GDT = NULL; IDT = NULL; + dbug_printf("======================= LEAVE CPU-EMU ===============\n"); + if (debug_level('e')) print_statistics(); + } + flush_log(); +} + + +/* ======================================================================= + * kernel-level vm86 handling - this has been mostly moved into interp.c + * from /linux/arch/i386/kernel/vm86.c + * original code by Linus Torvalds and later enhancements by + * Lutz Molgedey and Hans Lermen. + */ +static int handle_vm86_fault(int *error_code) +{ + unsigned int csp, ip; + unsigned char op; + + csp = SEGOFF2LINEAR(_CS, 0); + ip = _IP; + op = popb(csp, ip); + if (debug_level('e')>1) e_printf("EMU86: vm86 fault %#x at %#x:%#x\n", + op, _CS, _IP); + + /* int xx */ + if (op==0xcd) { + int intno=popb(csp, ip); + _IP += 2; + if (debug_level('e')>1) + dbug_printf("EMU86: calling revectored int %#x\n", intno); + return VM86_INTx + (intno << 8); + } + else + return VM86_UNKNOWN; +} + +/* + * ======================================================================= + */ +static const char *retdescs[] = +{ + "VM86_SIGNAL","VM86_UNKNOWN","VM86_INTx","VM86_STI", + "VM86_PICRET","???","VM86_TRAP","???" +}; + +int e_vm86(void) +{ + int xval,retval,mode; +#ifdef SKIP_VM86_TRACE + int demusav; +#endif + int errcode; + + if (iniflag==0) enter_cpu_emu(); + TheCPU.sigalrm_pending = 0; + + e_sigpa_count = 0; + mode = ADDR16 | DATA16 | MREALA; + TheCPU.StackMask = 0x0000ffff; +#ifdef SKIP_VM86_TRACE + demusav=debug_level('e'); if (debug_level('e')) set_debug_level('e', 1); +#endif +// if (lastEMUsig && (debug_level('e')>1)) +// e_printf("EMU86: last sig at %lld, curr=%lld, next=%lld\n",lastEMUsig>>16, +// TheCPU.EMUtime>>16,sigEMUtime>>16); + + /* This emulates VM86_ENTER */ + /* ------ OUTER LOOP: exit for code >=0 and return to dosemu code */ + do { + Reg2Cpu(mode); + if (CONFIG_CPUSIM) { + RFL.valid = V_INVALID; + } + /* ---- INNER LOOP: exit with error or code>0 (vm86 fault) ---- */ + do { + /* enter VM86 mode */ + in_vm86_emu = 1; + if (debug_level('e')>1) + dbug_printf("INTERP: enter=%08x\n",trans_addr); + return_addr = Interp86(trans_addr, mode); + if (debug_level('e')>1) + dbug_printf("INTERP: exit=%08x err=%d\n",return_addr,TheCPU.err-1); + xval = TheCPU.err; + in_vm86_emu = 0; + /* 0 if ok, else exception code+1 or negative if dosemu err */ + if (xval < 0) { + error("EMU86: error %d\n", -xval); + in_vm86=0; + leavedos_main(1); + } + trans_addr = return_addr; + } + while (xval==0); + /* ---- INNER LOOP -- exit for exception ---------------------- */ + if (CONFIG_CPUSIM) + FlagSync_All(); + + Cpu2Reg(); + if (debug_level('e')>1) e_printf("---------------------\n\t EMU86: EXCP %#x\n", xval-1); + + retval = -1; + + if (xval==EXCP_SIGNAL) { /* coming here for async interruptions */ + if (CEmuStat & (CeS_SIGPEND|CeS_SIGACT)) + { CEmuStat &= ~(CeS_SIGPEND|CeS_SIGACT); retval=VM86_SIGNAL; } + } + else if (xval==EXCP_PICSIGNAL) { + retval = VM86_PICRETURN; + } + else if (xval==EXCP_STISIGNAL) { + retval = VM86_STI; + } + else if (xval==EXCP_GOBACK) { + retval = 0; + } + else { + switch (xval) { + case EXCP0D_GPF: { /* to kernel vm86 */ + retval=handle_vm86_fault(&errcode); /* kernel level */ +#ifdef SKIP_EMU_VBIOS + /* are we into the VBIOS? If so, exit and reenter e_vm86 */ + if ((SREG(cs)&0xf000)==config.vbios_seg) { + if (retval<0) retval=0; /* force exit even if handled */ + } +#endif + } + break; + case EXCP01_SSTP: + case EXCP03_INT3: { /* to kernel vm86 */ + retval = VM86_TRAP + ((xval-1) << 8); + break; + } + default: { + /* FAULT, handled via signal callback */ + vm86_fault(xval-1, TheCPU.scp_err, TheCPU.cr2); + retval = VM86_SIGNAL; + break; + } + } + } + } + while (retval < 0); + /* ------ OUTER LOOP -- exit to user level ---------------------- */ + + if (debug_level('e')>1) + e_printf("EMU86: retval=%s\n", retdescs[retval&7]); + +#ifdef SKIP_VM86_TRACE + set_debug_level('e',demusav); +#endif + return retval; +} + +/* ======================================================================= */ + + +int e_dpmi(cpuctx_t *scp) +{ + int xval,retval,mode; + + if (iniflag==0) enter_cpu_emu(); + TheCPU.sigalrm_pending = 0; + + e_sigpa_count = 0; + /* make clear we are in PM now */ + TheCPU.cr[0] |= 1; + + if (debug_level('e')>2) { + D_printf("EMU86: DPMI enter at %08x\n",DTgetSelBase(_cs)+_eip); + } +// if (lastEMUsig) +// e_printf("DPM86: last sig at %lld, curr=%lld, next=%lld\n",lastEMUsig>>16, +// TheCPU.EMUtime>>16,sigEMUtime>>16); + + /* ------ OUTER LOOP: exit for code >=0 and return to dosemu code */ + do { + TheCPU.err = 0; + mode = Scp2CpuD (scp); + if (CONFIG_CPUSIM) + RFL.valid = V_INVALID; + if (TheCPU.err) { + error("DPM86: segment error %d\n", TheCPU.err); + leavedos_main(0); + } + + /* ---- INNER LOOP: exit with error or code>0 (vm86 fault) ---- */ + do { + /* switch to DPMI process */ + in_dpmi_emu = 1; + e_printf("INTERP: enter=%08x mode=%04x\n",trans_addr,mode); + return_addr = Interp86(trans_addr, mode); + e_printf("INTERP: exit=%08x err=%d\n",return_addr,TheCPU.err-1); + xval = TheCPU.err; + in_dpmi_emu = 0; + /* 0 if ok, else exception code+1 or negative if dosemu err */ + if (xval < 0) { + error("DPM86: error %d\n", -xval); + error("@\n%s",e_print_regs()); + leavedos_main(0); + } + trans_addr = return_addr; + } + while (xval==0); + /* ---- INNER LOOP -- exit for exception ---------------------- */ + if (CONFIG_CPUSIM) + FlagSync_All(); + + if (debug_level('e')>1) e_printf("DPM86: EXCP %#x eflags=%08x\n", + xval-1, REG(eflags)); + + Cpu2Scp (scp, xval-1); + + retval = DPMI_RET_CLIENT; + + if ((xval==EXCP_SIGNAL) || (xval==EXCP_PICSIGNAL) || (xval==EXCP_STISIGNAL)) { + retval = DPMI_RET_DOSEMU; + } + else if (xval==EXCP_GOBACK) { + retval = DPMI_RET_DOSEMU; + } + else if (xval == EXCP0E_PAGE && vga_emu_fault(_cr2,_err,scp)==True) { + retval = DPMI_RET_CLIENT; + } else { + retval = DPMI_RET_FAULT; + } + } + while (retval == DPMI_RET_CLIENT); + /* ------ OUTER LOOP -- exit to user level ---------------------- */ + + return retval; +} + +/* set special SIM mode for VGAEMU faults */ +int instr_emu_sim(cpuctx_t *scp, int pmode, int cnt) +{ + instr_emu_sim_reset_count(cnt); +#ifdef HOST_ARCH_X86 + if (!config.cpusim) + InitGen_sim(); +#endif + CEmuStat |= CeS_INSTREMU; + if (pmode) + e_dpmi(scp); + else + e_vm86(); + CEmuStat &= ~CeS_INSTREMU; +#ifdef HOST_ARCH_X86 + /* back to regular JIT */ + if (!config.cpusim) + InitGen_x86(); +#endif + return True; +} + +/* ======================================================================= */ +/* file: src/cwsdpmi/exphdlr.c */ + +void e_dpmi_b0x(int op,cpuctx_t *scp) +{ + switch (op) { + case 0: { /* set */ + int enabled = TheCPU.dr[7]; + int i; + for (i=0; i<4; i++) { + if ((~enabled >> (i*2)) & 3) { + unsigned mask; + TheCPU.dr[i] = (_LWORD(ebx) << 16) | _LWORD(ecx); + TheCPU.dr[7] |= (3 << (i*2)); + mask = _HI(dx) & 3; if (mask==2) mask++; + mask |= ((_LO(dx)-1) << 2) & 0x0c; + TheCPU.dr[7] |= mask << (i*4 + 16); + _LWORD(ebx) = i; + _eflags &= ~CF; + break; + } + } + } + break; + case 1: { /* clear */ + int i = _LWORD(ebx) & 3; + TheCPU.dr[6] &= ~(1 << i); + TheCPU.dr[7] &= ~(3 << (i*2)); + TheCPU.dr[7] &= ~(15 << (i*4+16)); + _eflags &= ~CF; + break; + } + case 2: { /* get */ + int i = _LWORD(ebx) & 3; + _LWORD(eax) = (TheCPU.dr[6] >> i); + _eflags &= ~CF; + break; + } + case 3: { /* reset */ + int i = _LWORD(ebx) & 3; + TheCPU.dr[6] &= ~(1 << i); + _eflags &= ~CF; + break; + } + default: + _LWORD(eax) = 0x8016; + _eflags |= CF; + break; + } + if (TheCPU.dr[7] & 0xff) CEmuStat|=CeS_DRTRAP; else CEmuStat&=~CeS_DRTRAP; +/* + e_printf("DR0=%08lx DR1=%08lx DR2=%08lx DR3=%08lx\n",DRs[0],DRs[1],DRs[2],DRs[3]); + e_printf("DR6=%08lx DR7=%08lx\n",DRs[6],DRs[7]); +*/ +} + + +int e_debug_check(unsigned int PC) +{ + register unsigned long d7 = TheCPU.dr[7]; + + if (d7&0x03) { + if (d7&0x30000) return 0; /* only execute(00) bkp */ + if (PC==TheCPU.dr[0]) { + e_printf("DBRK: DR0 hit at %08x\n",PC); + TheCPU.dr[6] |= 1; + return 1; + } + } + if (d7&0x0c) { + if (d7&0x300000) return 0; + if (PC==TheCPU.dr[1]) { + e_printf("DBRK: DR1 hit at %08x\n",PC); + TheCPU.dr[6] |= 2; + return 1; + } + } + if (d7&0x30) { + if (d7&0x3000000) return 0; + if (PC==TheCPU.dr[2]) { + e_printf("DBRK: DR2 hit at %08x\n",PC); + TheCPU.dr[6] |= 4; + return 1; + } + } + if (d7&0xc0) { + if (d7&0x30000000) return 0; + if (PC==TheCPU.dr[3]) { + e_printf("DBRK: DR3 hit at %08x\n",PC); + TheCPU.dr[6] |= 8; + return 1; + } + } + return 0; +} + +int e_in_compiled_code(void) +{ + return InCompiledCode; +} + +/* ======================================================================= */ + +#endif /* X86_EMULATOR */ diff --git a/src/base/emu-i386/simx86/econfig.h b/src/base/emu-i386/simx86/econfig.h new file mode 100644 index 0000000..f2ad24c --- /dev/null +++ b/src/base/emu-i386/simx86/econfig.h @@ -0,0 +1,83 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originaly was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#ifndef _EMU86_CONFIG_H +#define _EMU86_CONFIG_H + +/***************************************************************************/ + +#undef SINGLESTEP +#undef SINGLEBLOCK +#define PROFILE 0 +#undef DBG_TIME +#undef SKIP_CPATCH + +#define FAKE_INS_TIME 20 + +#define MAXINODES 4096 +#define MAX_GEND_BYTES_PER_OP 76 +/* NUMGENS must be large enough in !SINGLESTEP mode */ +#define NUMGENS 128 +#undef ASM_DUMP +#define ASM_DUMP_FILE "/DOS/asmdump.log" + +#define AVL_MAX_HEIGHT 20 +#undef DEBUG_TREE +#define DEBUG_TREE_FILE "/DOS/treedump.log" + +#define USE_LINKER 1 // 0 or 1 +#undef DEBUG_LINKER +#undef SHOW_STAT + +#define FP_DISPHEX +#undef FPU_TAGS + +#undef DEBUG_VGA + +#define NODES_IN_POOL 100000 +#define NODELIFE(n) 200 +#define CLEAN_SPEED(n) (((n)<<2)+1) +#define AGENODE CreationIndex + +#undef TRAP_RETRACE + +/* If you undefine this, in 16-bit stack mode the high 16 bits of ESP + * will be zeroed after every push/pop operation. There's a small + * possibility of breaking some code, you can easily figure out how. + * For 32-bit stacks, keeping ESP is also a waste of time. */ +#define KEEP_ESP 1 + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/src/base/emu-i386/simx86/emu86.h b/src/base/emu-i386/simx86/emu86.h new file mode 100644 index 0000000..e41fbfc --- /dev/null +++ b/src/base/emu-i386/simx86/emu86.h @@ -0,0 +1,752 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originaly was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#ifndef _EMU86_EMU86_H +#define _EMU86_EMU86_H + +#include +#include +#include "econfig.h" +#include +#include "emu.h" +#include "timers.h" +#include "pic.h" +#include "cpu-emu.h" +#include "syncpu.h" + +#if PROFILE +extern hitimer_t AddTime, SearchTime, ExecTime, CleanupTime; +extern hitimer_t GenTime, LinkTime; +#endif + +#ifdef X86_JIT +#define CONFIG_CPUSIM (config.cpusim || (CEmuStat & CeS_INSTREMU)) +#else +#define CONFIG_CPUSIM 1 +#endif + +/* octal digits in a byte: hhmm.mlll */ +#define D_HO(b) (((b)>>6)&3) +#define D_MO(b) (((b)>>3)&7) +#define D_LO(b) ((b)&7) + +#define ADDbfrm 0x00 +#define ADDwfrm 0x01 +#define ADDbtrm 0x02 +#define ADDwtrm 0x03 +#define ADDbia 0x04 +#define ADDwia 0x05 +#define PUSHes 0x06 +#define POPes 0x07 +#define ORbfrm 0x08 +#define ORwfrm 0x09 +#define ORbtrm 0x0a +#define ORwtrm 0x0b +#define ORbi 0x0c +#define ORwi 0x0d +#define PUSHcs 0x0e +#define TwoByteESC 0x0f + +#define ADCbfrm 0x10 +#define ADCwfrm 0x11 +#define ADCbtrm 0x12 +#define ADCwtrm 0x13 +#define ADCbi 0x14 +#define ADCwi 0x15 +#define PUSHss 0x16 +#define POPss 0x17 +#define SBBbfrm 0x18 +#define SBBwfrm 0x19 +#define SBBbtrm 0x1a +#define SBBwtrm 0x1b +#define SBBbi 0x1c +#define SBBwi 0x1d +#define PUSHds 0x1e +#define POPds 0x1f + +#define ANDbfrm 0x20 +#define ANDwfrm 0x21 +#define ANDbtrm 0x22 +#define ANDwtrm 0x23 +#define ANDbi 0x24 +#define ANDwi 0x25 +#define SEGes 0x26 +#define DAA 0x27 +#define SUBbfrm 0x28 +#define SUBwfrm 0x29 +#define SUBbtrm 0x2a +#define SUBwtrm 0x2b +#define SUBbi 0x2c +#define SUBwi 0x2d +#define SEGcs 0x2e +#define DAS 0x2f + +#define XORbfrm 0x30 +#define XORwfrm 0x31 +#define XORbtrm 0x32 +#define XORwtrm 0x33 +#define XORbi 0x34 +#define XORwi 0x35 +#define SEGss 0x36 +#define AAA 0x37 +#define CMPbfrm 0x38 +#define CMPwfrm 0x39 +#define CMPbtrm 0x3a +#define CMPwtrm 0x3b +#define CMPbi 0x3c +#define CMPwi 0x3d +#define SEGds 0x3e +#define AAS 0x3f + +#define INCax 0x40 +#define INCcx 0x41 +#define INCdx 0x42 +#define INCbx 0x43 +#define INCsp 0x44 +#define INCbp 0x45 +#define INCsi 0x46 +#define INCdi 0x47 +#define DECax 0x48 +#define DECcx 0x49 +#define DECdx 0x4a +#define DECbx 0x4b +#define DECsp 0x4c +#define DECbp 0x4d +#define DECsi 0x4e +#define DECdi 0x4f + +#define PUSHax 0x50 +#define PUSHcx 0x51 +#define PUSHdx 0x52 +#define PUSHbx 0x53 +#define PUSHsp 0x54 +#define PUSHbp 0x55 +#define PUSHsi 0x56 +#define PUSHdi 0x57 +#define POPax 0x58 +#define POPcx 0x59 +#define POPdx 0x5a +#define POPbx 0x5b +#define POPsp 0x5c +#define POPbp 0x5d +#define POPsi 0x5e +#define POPdi 0x5f + +#define PUSHA 0x60 +#define POPA 0x61 +#define BOUND 0x62 +#define ARPL 0x63 +#define SEGfs 0x64 +#define SEGgs 0x65 +#define OPERoverride 0x66 +#define ADDRoverride 0x67 +#define PUSHwi 0x68 +#define IMULwrm 0x69 +#define PUSHbi 0x6a +#define IMULbrm 0x6b +#define INSb 0x6c +#define INSw 0x6d +#define OUTSb 0x6e +#define OUTSw 0x6f + +#define JO 0x70 +#define JNO 0x71 +#define JB_JNAE 0x72 +#define JNB_JAE 0x73 +#define JE_JZ 0x74 +#define JNE_JNZ 0x75 +#define JBE_JNA 0x76 +#define JNBE_JA 0x77 +#define JS 0x78 +#define JNS 0x79 +#define JP_JPE 0x7a +#define JNP_JPO 0x7b +#define JL_JNGE 0x7c +#define JNL_JGE 0x7d +#define JLE_JNG 0x7e +#define JNLE_JG 0x7f + +#define IMMEDbrm 0x80u +#define IMMEDwrm 0x81u +#define IMMEDbrm2 0x82u +#define IMMEDisrm 0x83u +#define TESTbrm 0x84u +#define TESTwrm 0x85u +#define XCHGbrm 0x86u +#define XCHGwrm 0x87u +#define MOVbfrm 0x88u +#define MOVwfrm 0x89u +#define MOVbtrm 0x8au +#define MOVwtrm 0x8bu +#define MOVsrtrm 0x8cu +#define LEA 0x8du +#define MOVsrfrm 0x8eu +#define POPrm 0x8fu + +#define NOP 0x90u +#define XCHGcx 0x91u +#define XCHGdx 0x92u +#define XCHGbx 0x93u +#define XCHGsp 0x94u +#define XCHGbp 0x95u +#define XCHGsi 0x96u +#define XCHGdi 0x97u +#define CBW 0x98u +#define CWD 0x99u +#define CALLl 0x9au +#define oWAIT 0x9bu +#define PUSHF 0x9cu +#define POPF 0x9du +#define SAHF 0x9eu +#define LAHF 0x9fu + +#define MOVmal 0xa0u +#define MOVmax 0xa1u +#define MOValm 0xa2u +#define MOVaxm 0xa3u +#define MOVSb 0xa4u +#define MOVSw 0xa5u +#define CMPSb 0xa6u +#define CMPSw 0xa7u +#define TESTbi 0xa8u +#define TESTwi 0xa9u +#define STOSb 0xaau +#define STOSw 0xabu +#define LODSb 0xacu +#define LODSw 0xadu +#define SCASb 0xaeu +#define SCASw 0xafu + +#define MOVial 0xb0u +#define MOVicl 0xb1u +#define MOVidl 0xb2u +#define MOVibl 0xb3u +#define MOViah 0xb4u +#define MOVich 0xb5u +#define MOVidh 0xb6u +#define MOVibh 0xb7u +#define MOViax 0xb8u +#define MOVicx 0xb9u +#define MOVidx 0xbau +#define MOVibx 0xbbu +#define MOVisp 0xbcu +#define MOVibp 0xbdu +#define MOVisi 0xbeu +#define MOVidi 0xbfu + +#define SHIFTbi 0xc0u +#define SHIFTwi 0xc1u +#define RETisp 0xc2u +#define RET 0xc3u +#define LES 0xc4u +#define LDS 0xc5u +#define MOVbirm 0xc6u +#define MOVwirm 0xc7u +#define ENTER 0xc8u +#define LEAVE 0xc9u +#define RETlisp 0xcau +#define RETl 0xcbu +#define INT3 0xccu +#define INT 0xcdu +#define INTO 0xceu +#define IRET 0xcfu + +#define SHIFTb 0xd0u +#define SHIFTw 0xd1u +#define SHIFTbv 0xd2u +#define SHIFTwv 0xd3u +#define AAM 0xd4u +#define AAD 0xd5u +#define RESERVED1 0xd6u +#define XLAT 0xd7u +#define ESC0 0xd8u +#define ESC1 0xd9u +#define ESC2 0xdau +#define ESC3 0xdbu +#define ESC4 0xdcu +#define ESC5 0xddu +#define ESC6 0xdeu +#define ESC7 0xdfu + +#define LOOPNZ_LOOPNE 0xe0u +#define LOOPZ_LOOPE 0xe1u +#define LOOP 0xe2u +#define JCXZ 0xe3u +#define INb 0xe4u +#define INw 0xe5u +#define OUTb 0xe6u +#define OUTw 0xe7u +#define CALLd 0xe8u +#define JMPd 0xe9u +#define JMPld 0xeau +#define JMPsid 0xebu +#define INvb 0xecu +#define INvw 0xedu +#define OUTvb 0xeeu +#define OUTvw 0xefu + +#define LOCK 0xf0u +#define REPNE 0xf2u +#define REP 0xf3u +#define HLT 0xf4u +#define CMC 0xf5u +#define GRP1brm 0xf6u +#define GRP1wrm 0xf7u +#define CLC 0xf8u +#define STC 0xf9u +#define CLI 0xfau +#define STI 0xfbu +#define CLD 0xfcu +#define STD 0xfdu +#define GRP2brm 0xfeu +#define GRP2wrm 0xffu + +// pseudo-opcodes for GRP2wrm +#define CALLi ((GRP2wrm<<8)|Ofs_DX) +#define CALLli ((GRP2wrm<<8)|Ofs_BX) +#define JMPi ((GRP2wrm<<8)|Ofs_SP) +#define JMPli ((GRP2wrm<<8)|Ofs_BP) + +// 2 byte opcodes with TwoByteESC (0x0f) prefix +#define JOimmdisp 0x80u +#define JNOimmdisp 0x81u +#define JBimmdisp 0x82u +#define JNBimmdisp 0x83u +#define JZimmdisp 0x84u +#define JNZimmdisp 0x85u +#define JBEimmdisp 0x86u +#define JNBEimmdisp 0x87u +#define JSimmdisp 0x88u +#define JNSimmdisp 0x89u +#define JPimmdisp 0x8au +#define JNPimmdisp 0x8bu +#define JLimmdisp 0x8cu +#define JNLimmdisp 0x8du +#define JLEimmdisp 0x8eu +#define JNLEimmdisp 0x8fu + +#define SETObrm 0x90u +#define SETNObrm 0x91u +#define SETBbrm 0x92u +#define SETNBbrm 0x93u +#define SETZbrm 0x94u +#define SETNZbrm 0x95u +#define SETBEbrm 0x96u +#define SETNBEbrm 0x97u +#define SETSbrm 0x98u +#define SETNSbrm 0x99u +#define SETPbrm 0x9au +#define SETNPbrm 0x9bu +#define SETLbrm 0x9cu +#define SETNLbrm 0x9du +#define SETLEbrm 0x9eu +#define SETNLEbrm 0x9fu + +// FP opcodes +#define FADDm32r_sti ((ESC0<<3) | (0x0 & 0x38) >> 3) +#define FMULm32r_sti ((ESC0<<3) | (0x8 & 0x38) >> 3) +#define FCOMm32r_sti ((ESC0<<3) | (0x10 /*or 0xd0*/ & 0x38) >> 3) +#define FCOMPm32r_sti ((ESC0<<3) | (0x18 /*or 0xd8*/ & 0x38) >> 3) +#define FSUBm32r_sti ((ESC0<<3) | (0x20 /*or 0xe0*/ & 0x38) >> 3) +#define FSUBRm32r_sti ((ESC0<<3) | (0x28 /*or 0xe8*/ & 0x38) >> 3) +#define FDIVm32r_sti ((ESC0<<3) | (0x30 /*or 0xf0*/ & 0x38) >> 3) +#define FDIVRm32r_sti ((ESC0<<3) | (0x38 /*or 0xf8*/ & 0x38) >> 3) + +#define FLDm32r_sti ((ESC1<<3) | (0x0 & 0x38) >> 3) +#define FXCH ((ESC1<<3) | (0xc8 & 0x38) >> 3) +#define FSTm32r_FNOP ((ESC1<<3) | (0x10 /*or 0xd0*/ & 0x38) >> 3) +#define FSTPm32r ((ESC1<<3) | (0x18 & 0x38) >> 3) +#define FD9SLASH4 ((ESC1<<3) | (0x20 /*or 0xe0-0xe5*/ & 0x38) >> 3) +#define FLDENV 0x20 +#define FCHS 0xe0 +#define FABS 0xe1 +#define FTST 0xe4 +#define FXAM 0xe5 +#define FLDCONST ((ESC1<<3) | (0xe8 /*or 0xe9-0xee*/ & 0x38) >> 3) +#define FLDCW 0x28 +#define FLD1 0xe8 +#define FLDL2T 0xe9 +#define FLDL2E 0xea +#define FLDPI 0xeb +#define FLDLG2 0xec +#define FLDLN2 0xed +#define FLDZ 0xee +#define FD9SLASH6 ((ESC1<<3) | (0x30 /*or 0xf0-0xf7*/ & 0x38) >> 3) +#define FSTENV 0x30 +#define F2XM1 0xf0 +#define FYL2X 0xf1 +#define FPTAN 0xf2 +#define FPATAN 0xf3 +#define FXTRACT 0xf4 +#define FPREM1 0xf5 +#define FDECSTP 0xf6 +#define FINCSTP 0xf7 +#define FD9SLASH7 ((ESC1<<3) | (0x38 /*or 0xf8-0xff*/ & 0x38) >> 3) +#define FSTCW 0x38 +#define FPREM 0xf8 +#define FYL2XP1 0xf9 +#define FSQRT 0xfa +#define FSINCOS 0xfb +#define FRNDINT 0xfc +#define FSCALE 0xfd +#define FSIN 0xfe +#define FCOS 0xff + +#define FADDm32i ((ESC2<<3) | (0x0 & 0x38) >> 3) +#define FMULm32i ((ESC2<<3) | (0x8 & 0x38) >> 3) +#define FICOMm32i ((ESC2<<3) | (0x10 & 0x38) >> 3) +#define FICOMPm32i ((ESC2<<3) | (0x18 & 0x38) >> 3) +#define FISUBm32i ((ESC2<<3) | (0x20 & 0x38) >> 3) +#define FISUBRm32i_FUCOMPPst1 ((ESC2<<3) | (0x28 /*or 0xe9*/ & 0x38) >> 3) +#define FIDIVm32i ((ESC2<<3) | (0x30 & 0x38) >> 3) +#define FIDIVRm32i ((ESC2<<3) | (0x38 & 0x38) >> 3) + +#define FILDm32i ((ESC3<<3) | (0x0 & 0x38) >> 3) +#define FISTm32i ((ESC3<<3) | (0x10 & 0x38) >> 3) +#define FISTPm32i ((ESC3<<3) | (0x18 & 0x38) >> 3) +#define FRSTORm94B_FINIT_FCLEX ((ESC3<<3) | (0x20 /*or 0xe3-2*/ & 0x38) >> 3) +#define FLDm80r ((ESC3<<3) | (0x28 & 0x38) >> 3) +#define FSTPm80r ((ESC3<<3) | (0x38 & 0x38) >> 3) + +#define FADDm64r_tosti ((ESC4<<3) | (0x0 & 0x38) >> 3) +#define FMULm64r_tosti ((ESC4<<3) | (0x8 & 0x38) >> 3) +#define FCOMm64r ((ESC4<<3) | (0x10 & 0x38) >> 3) +#define FCOMPm64r ((ESC4<<3) | (0x18 & 0x38) >> 3) +#define FSUBm64r_FSUBRfromsti ((ESC4<<3) | (0x20 /*or 0xe0*/ & 0x38) >> 3) +#define FSUBRm64r_FSUBfromsti ((ESC4<<3) | (0x28 /*or 0xe8*/ & 0x38) >> 3) +#define FDIVm64r_FDIVRtosti ((ESC4<<3) | (0x30 /*or 0xf0*/ & 0x38) >> 3) +#define FDIVRm64r_FDIVtosti ((ESC4<<3) | (0x38 /*or 0xf8*/ & 0x38) >> 3) + +#define FLDm64r_FFREE ((ESC5<<3) | (0x0 /*or 0xc0*/ & 0x38) >> 3) +#define FSTm64r_sti ((ESC5<<3) | (0x10 /*or 0xd0+i*/ & 0x38) >> 3) +#define FSTPm64r_sti ((ESC5<<3) | (0x18 /*or 0xd8+i*/ & 0x38) >> 3) +#define FUCOMsti ((ESC5<<3) | (0x20 /*or 0xe0+i*/ & 0x38) >> 3) +#define FUCOMPsti ((ESC5<<3) | (0x28 /*or 0xe8+i*/ & 0x38) >> 3) +#define FSAVEm94B ((ESC5<<3) | (0x30 & 0x38) >> 3) +#define FSTSWm16i ((ESC5<<3) | (0x38 & 0x38) >> 3) + +#define FADDm16i_tostipop ((ESC6<<3) | (0x0 & 0x38) >> 3) +#define FMULm16i_tostipop ((ESC6<<3) | (0x8 & 0x38) >> 3) +#define FICOMm16i ((ESC6<<3) | (0x10 & 0x38) >> 3) +#define FICOMPm16i_FCOMPPst1 ((ESC6<<3) | (0x18 /*or 0xd9*/ & 0x38) >> 3) +#define FISUBm16i_FSUBRPfromsti ((ESC6<<3) | (0x20 /*or 0xe0*/ & 0x38) >> 3) +#define FISUBRm16i_FSUBPfromsti ((ESC6<<3) | (0x28 /*or 0xe8*/ & 0x38) >> 3) +#define FIDIVm16i_FDIVRPtosti ((ESC6<<3) | (0x30 /*or 0xf0*/ & 0x38) >> 3) +#define FIDIVRm16i_FDIVPtosti ((ESC6<<3) | (0x38 /*or 0xf8*/ & 0x38) >> 3) + +#define FILDm16i ((ESC7<<3) | (0x0 & 0x38) >> 3) +#define FISTm16i ((ESC7<<3) | (0x10 & 0x38) >> 3) +#define FISTPm16i ((ESC7<<3) | (0x18 & 0x38) >> 3) +#define FBLDm80dec_FSTSWax ((ESC7<<3) | (0x20 /*or 0xe0*/ & 0x38) >> 3) +#define FILDm64i ((ESC7<<3) | (0x28 & 0x38) >> 3) +#define FBSTPm80dec ((ESC7<<3) | (0x30 & 0x38) >> 3) +#define FISTPm64i ((ESC7<<3) | (0x38 & 0x38) >> 3) + +/* + * control registers + */ +#define CR0_PE 0x00000001 +#define CR0_MP 0x00000002 +#define CR0_EM 0x00000004 +#define CR0_TS 0x00000008 +#define CR0_ET 0x00000010 +#define CR0_NE 0x00000020 +#define CR0_WP 0x00010000 +#define CR0_AM 0x00040000 +#define CR0_NW 0x20000000 +#define CR0_CD 0x40000000 +#define CR0_PG 0x80000000 +#define CR0_RESERVED 0x1ffaffc0 +#define CR0_NOT_IMP 0x00000000 + +#define CR3_PWT 0x00000008 +#define CR3_PCD 0x00000010 +#define CR3_PDB_SHIFT 12 +#define CR3_PDB_MASK 0xfffff000 +#define CR3_RESERVED 0x00000fe7 +#define CR3_NOT_IMP 0x00000018 + +#define CR4_VME 0x00000001 +#define CR4_PVI 0x00000002 +#define CR4_TSD 0x00000004 +#define CR4_DE 0x00000008 +#define CR4_PSE 0x00000010 +#define CR4_PAE 0x00000020 +#define CR4_MCE 0x00000040 +#define CR4_PGE 0x00000080 +#define CR4_PCE 0x00000100 +#define CR4_OSFXSR 0x00000200 +#define CR4_RESERVED 0xfffffc00 +#define CR4_NOT_IMP 0x0000012e + +#define EFLAGS_CF 0x00000001 +#define EFLAGS_SET 0x00000002 +#define EFLAGS_PF 0x00000004 +#define EFLAGS_AF 0x00000010 +#define EFLAGS_ZF 0x00000040 +#define EFLAGS_SF 0x00000080 +#define EFLAGS_TF 0x00000100 +#define EFLAGS_IF 0x00000200 +#define EFLAGS_DF 0x00000400 +#define EFLAGS_OF 0x00000800 +#define EFLAGS_IOPL 0x00003000 +#define EFLAGS_NT 0x00004000 +#define EFLAGS_RF 0x00010000 +#define EFLAGS_VM 0x00020000 +#define EFLAGS_AC 0x00040000 +#define EFLAGS_VIF 0x00080000 +#define EFLAGS_VIP 0x00100000 +#define EFLAGS_ID 0x00200000 + +#define EFLAGS_ALL (0x003f7fd5) +#define EFLAGS_NORFVM (EFLAGS_ALL & ~(EFLAGS_RF|EFLAGS_VM)) + +#define EFLAGS_REAL_32 (EFLAGS_ALL & ~(EFLAGS_VIP|EFLAGS_VIF|EFLAGS_VM)) +#define EFLAGS_V8086_32 (EFLAGS_ALL & ~(EFLAGS_VIP|EFLAGS_VIF|EFLAGS_VM|EFLAGS_IOPL)) +#define EFLAGS_ALL_16 LOWORD(EFLAGS_ALL) +#define EFLAGS_REAL_16 LOWORD(EFLAGS_REAL_32) +#define EFLAGS_V8086_16 LOWORD(EFLAGS_V8086_32) +#define EFLAGS_CC 0x000008d5 + +#define EFLAGS_IOPL_SHIFT 12 +#define EFLAGS_IOPL_MASK 0x3000 + +/* www.sandpile.org/80x86/cpuid.shtml */ +#define CPUID_FEATURE_FPU 0x00000001 +#define CPUID_FEATURE_VME 0x00000002 +#define CPUID_FEATURE_DBGE 0x00000004 +#define CPUID_FEATURE_PGSZE 0X00000008 +#define CPUID_FEATURE_TSC 0x00000010 +#define CPUID_FEATURE_MSR 0x00000020 +#define CPUID_FEATURE_PAE 0x00000040 +#define CPUID_FEATURE_MCK 0x00000080 +#define CPUID_FEATURE_CPMX 0x00000100 +#define CPUID_FEATURE_APIC 0x00000200 +#define CPUID_FEATURE_RSVD1 0x00000400 +#define CPUID_FEATURE_SEP 0x00000800 +#define CPUID_FEATURE_MTTR 0x00001000 +#define CPUID_FEATURE_PGE 0x00002000 +#define CPUID_FEATURE_MCA 0x00004000 +#define CPUID_FEATURE_CMOV 0x00008000 + +#define CPUID_FEATURE_PAT 0x00010000 +#define CPUID_FEATURE_36PG 0x00020000 +#define CPUID_FEATURE_RSVD2 0x00040000 /* pentium II, mendocino? */ +#define CPUID_FEATURE_RSVD3 0x00080000 + +#define CPUID_FEATURE_RSVD4 0x00100000 +#define CPUID_FEATURE_RSVD5 0x00200000 +#define CPUID_FEATURE_RSVD6 0x00400000 +#define CPUID_FEATURE_MMX 0x00800000 +#define CPUID_FEATURE_FXSAVE 0x01000000 +#define CPUID_FEATURE_PIII 0x02000000 /* pentium III, unknown */ +#define CPUID_FEATURE_RSVD 0xfc780400 + +#define MSR_TSC 0x00000010 +#define MSR_MTRR_CAP 0x000000fe +#define MSR_SYSENTER_CS 0x00000174 +#define MSR_SYSENTER_ESP 0x00000175 +#define MSR_SYSENTER_EIP 0x00000176 + +#define IS_CF_SET ((EFLAGS & EFLAGS_CF)!=0) +#define IS_AF_SET ((EFLAGS & EFLAGS_AF)!=0) +#define IS_ZF_SET ((EFLAGS & EFLAGS_ZF)!=0) +#define IS_SF_SET ((EFLAGS & EFLAGS_SF)!=0) +#define IS_OF_SET ((EFLAGS & EFLAGS_OF)!=0) +#define IS_PF_SET ((EFLAGS & EFLAGS_PF)!=0) + +/* + * ID VIP VIF AC VM RF 0 NT IOPL OF DF IF TF SF ZF 0 AF 0 PF 1 CF + * 1 1 0 1 1 1 0 1 0 1 0 1 + */ +#define SAFE_MASK (EFLAGS_OF|EFLAGS_DF|EFLAGS_TF|EFLAGS_SF| \ + EFLAGS_ZF|EFLAGS_AF|EFLAGS_PF|EFLAGS_CF| /* 0xDD5 */ \ + (eTSSMASK & ~IOPL_MASK)) +#define notSAFE_MASK (~SAFE_MASK&0x3fffff) +#define RETURN_MASK ((0xFFF&~EFLAGS_IF) | eTSSMASK) + +#define REALMODE() ((TheCPU.cr[0] & CR0_PE)==0) +#define V86MODE() ((TheCPU.eflags&EFLAGS_VM)!=0) +#define PROTMODE() (!REALMODE() && !V86MODE()) +#define REALADDR() (TheCPU.mode & MREALA) + +#if 1 +/* + * CPL is 0 in 'real' realmode, 3 (but can be less) in VM86 mode + * in PM it depends on the CS DPL + * + * if CPL<=IOPL the following insns DO NOT trap: + * IN, INS, OUT, OUTS, CLI, STI + * only POPF and IRET can change IOPL iff CPL==0 + * only POPF can change IF iff CPL<=IOPL + */ +//#define CPL (REALMODE()? 0:(V86MODE()? 3:(TheCPU.cs&3))) +#define CPL (V86MODE()? 3:(TheCPU.cs&3)) +#define IOPL ((EFLAGS & EFLAGS_IOPL_MASK) >> EFLAGS_IOPL_SHIFT) +#else /* simple */ +#define CPL 3 +#define IOPL 0 +#endif + +///////////////////////////////////////////////////////////////////////////// +/* + * INT 00 C - CPU-generated - DIVIDE ERROR + * INT 01 C - CPU-generated - SINGLE STEP + * INT 02 C - external hardware - NON-MASKABLE INTERRUPT + * INT 03 C - CPU-generated - BREAKPOINT + * INT 04 C - CPU-generated - INTO DETECTED OVERFLOW + * INT 05 C - CPU-generated (80186+) - BOUND RANGE EXCEEDED + * INT 06 C - CPU-generated (80286+) - INVALID OPCODE + * INT 07 C - CPU-generated (80286+) - PROCESSOR EXTENSION NOT AVAILABLE + * INT 08 C - CPU-generated (80286+) - DOUBLE EXCEPTION DETECTED + * INT 09 C - CPU-generated (80286,80386) - PROCESSOR EXTENSION PROTECTION ERROR + * INT 0A CP - CPU-generated (80286+) - INVALID TASK STATE SEGMENT + * INT 0B CP - CPU-generated (80286+) - SEGMENT NOT PRESENT + * INT 0C C - CPU-generated (80286+) - STACK FAULT + * INT 0D C - CPU-generated (80286+) - GENERAL PROTECTION VIOLATION + * INT 0E C - CPU-generated (80386+ native mode) - PAGE FAULT + * INT 10 C - CPU-generated (80286+) - COPROCESSOR ERROR + * INT 11 - CPU-generated (80486+) - ALIGNMENT CHECK + * INT 12 - CPU-generated (Pentium) - MACHINE CHECK EXCEPTION + */ +#define EXCP00_DIVZ 1 +#define EXCP01_SSTP 2 +#define EXCP02_NMI 3 +#define EXCP03_INT3 4 +#define EXCP04_INTO 5 +#define EXCP05_BOUND 6 +#define EXCP06_ILLOP 7 +#define EXCP07_PREX 8 +#define EXCP08_DBLE 9 +#define EXCP09_XERR 10 +#define EXCP0A_TSS 11 +#define EXCP0B_NOSEG 12 +#define EXCP0C_STACK 13 +#define EXCP0D_GPF 14 +#define EXCP0E_PAGE 15 +#define EXCP10_COPR 17 +#define EXCP11_ALGN 18 +#define EXCP12_MCHK 19 + +#define EXCP_GOBACK 64 +#define EXCP_SIGNAL 65 +#define EXCP_PICSIGNAL 66 +#define EXCP_STISIGNAL 67 +#define EXCP_MODESWITCH 68 + +///////////////////////////////////////////////////////////////////////////// + +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif +#ifndef max +#define max(a,b) ((a)>(b)?(a):(b)) +#endif +#ifndef PAGE_SHIFT +#define PAGE_SHIFT 12 +#endif +#ifndef PAGE_SIZE +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#endif +#define _PAGE_MASK (~(PAGE_SIZE-1)) + +///////////////////////////////////////////////////////////////////////////// +// +extern unsigned int return_addr; +extern jmp_buf jmp_env; +extern int in_vm86_emu, in_dpmi_emu; +extern unsigned long eTSSMASK; +extern int Running; /* into interpreter loop */ +extern unsigned int mMaxMem; +extern int UseLinker; +extern int PageFaults; + +extern volatile int CEmuStat; +extern volatile int InCompiledCode; +// +unsigned char *do_hwint(int mode, int intno); +unsigned int Interp86(unsigned int PC, int mode); +// +int _ModRM(unsigned char opc, unsigned int PC, int mode); +#define ModRM(o, p, m) ({ \ + int __l = _ModRM(o, p, m); \ + if (REG1 == -1) { \ + CODE_FLUSH(); \ + goto illegal_op; \ + } \ + if (CONFIG_CPUSIM && V86MODE() && !((m) & (ADDR16 | MLEA)) && TR1.d > 0xffff) { \ + CODE_FLUSH(); \ + goto not_permitted; \ + } \ + __l; \ +}) +int ModRMSim(unsigned int PC, int mode); +int ModGetReg1(unsigned int PC, int mode); +// +char *e_emu_disasm(unsigned char *org, int is32, unsigned int refseg); +char *e_print_regs(void); +char *e_print_scp_regs(cpuctx_t *scp, int pmode); +const char *e_trace_fp(void); +void GCPrint(unsigned char *cp, unsigned char *cbase, int len); +char *showreg(signed char r); +char *showmode(unsigned int m); +void Cpu2Reg (void); +int e_debug_check(unsigned int PC); +int e_mprotect(unsigned int addr, size_t len); +int e_querymprotrange(unsigned int addr, size_t len); +int e_markpage(unsigned int addr, size_t len); +int e_unmarkpage(unsigned int addr, size_t len); +int e_querymark(unsigned int addr, size_t len); +int e_querymark_all(unsigned int addr, size_t len); +void m_munprotect(unsigned int addr, unsigned int len, unsigned char *eip); +void mprot_init(void); +void mprot_end(void); +void InitGenCodeBuf(void); +void *AllocGenCodeBuf(size_t size); +void FreeGenCodeBuf(void *ptr); +// +void CollectStat(void); +// +///////////////////////////////////////////////////////////////////////////// +#ifdef HOST_ARCH_X86 +int e_handle_pagefault(dosaddr_t addr, unsigned err, sigcontext_t *scp); +int e_handle_fault(sigcontext_t *scp); +void init_emu_npu_x86(void); +#endif +void init_emu_npu(void); + +void e_VgaMovs(unsigned char **rdi, unsigned char **rsi, unsigned int rep, + int dp, unsigned int access); + +#endif // _EMU86_EMU86_H diff --git a/src/base/emu-i386/simx86/fp87-sim.c b/src/base/emu-i386/simx86/fp87-sim.c new file mode 100644 index 0000000..e6d2074 --- /dev/null +++ b/src/base/emu-i386/simx86/fp87-sim.c @@ -0,0 +1,1134 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#include +#include +#include +#include "dos2linux.h" +#include "emu86.h" +#include "codegen.h" +#include "codegen-sim.h" +#include "softfloat/softfloat.h" + +#define FPUS_IE (1 << 0) +#define FPUS_DE (1 << 1) +#define FPUS_ZE (1 << 2) +#define FPUS_OE (1 << 3) +#define FPUS_UE (1 << 4) +#define FPUS_PE (1 << 5) +#define FPUS_SF (1 << 6) +#define FPUS_ES (1 << 7) +#define FPUS_C0 (1 << 8) +#define FPUS_C1 (1 << 9) +#define FPUS_C2 (1 << 10) +#define FPUS_TOP ((1 << 11) | (1 << 12) | (1 << 13)) +#define FPUS_TOP_BIT 11 +#define FPUS_C3 (1 << 14) +#define FPUS_B (1 << 15) +#define FPUS_C (FPUS_C0 | FPUS_C1 | FPUS_C2 | FPUS_C3) + +// These are GNU extensions, so define them here if not already +#ifndef M_PIl +#define M_PIl 3.141592653589793238462643383279502884L +#endif +#ifndef M_LN2l +#define M_LN2l 0.693147180559945309417232121458176568L +#endif +#ifndef M_LN10l +#define M_LN10l 2.302585092994045684017991454684364208L +#endif +#ifndef M_LOG2El +#define M_LOG2El 1.442695040888963407359924681001892137L +#endif + +int (*Fp87_op)(int exop, int reg); +static int Fp87_op_sim(int exop, int reg); + +static long double WFR0, WFR1; + +#define S_next(r) (((r)+1)&7) +#define S_prev(r) (((r)-1)&7) +#define S_reg(r,n) (((r)+(n))&7) +#define ST0 (&TheCPU.fpregs[TheCPU.fpstt]) +#define ST1 (&TheCPU.fpregs[S_next(TheCPU.fpstt)]) +#define STn(n) (&TheCPU.fpregs[S_reg(TheCPU.fpstt,n)]) + +/* Note: 0 / special values for tag word are only done for FSTENV and FSAVE */ +#define FREETAG(n) TheCPU.fptag |= (3 << 2*S_reg(TheCPU.fpstt,n)) +#define UNFREETAG(n) TheCPU.fptag &= ~(3 << 2*S_reg(TheCPU.fpstt,n)) +#define SYNCFSP TheCPU.fpus=(TheCPU.fpus&~FPUS_TOP)|(TheCPU.fpstt<> 2*S_reg(TheCPU.fpstt,0)) & 3) == 3) + fps |= 0x4100; // empty + else if (!iscanonical(d)) + fps |= isnan(d) ? 0x0 : 0x4400; // pseudo normal/denormal + else switch(fpclassify(d)) { + case FP_NAN: + fps |= FPUS_C0; + break; + case FP_INFINITE: + fps |= FPUS_C0 | FPUS_C2; + break; + case FP_ZERO: + fps |= FPUS_C3; + break; + case FP_SUBNORMAL: + fps |= FPUS_C2 | FPUS_C3; + break; + case FP_NORMAL: + default: + fps |= FPUS_C2; + break; + } + if (signbit(d)) fps |= FPUS_C1; + TheCPU.fpus = (fps&~FPUS_TOP)|(TheCPU.fpstt<= 0; i--) { + uint8_t u = read_byte(p+i); + b = (b * 100) + (u >> 4) * 10 + (u & 0xf); + } + WFR0 = ((read_byte(p+9) & 0x80) ? -1 : 1) * b; + break; + } + case 0x2b: WFR0 = read_long_double(AR1.d); break; + case 0x2f: WFR0 = (int64_t)read_qword(AR1.d); break; + } + *ST0 = WFR0; + break; + +/*00*/ case 0x00: +/*02*/ case 0x02: +/*04*/ case 0x04: +/*06*/ case 0x06: +//* 00 D8 xx000nnn FADD mem32r +// 02 DA xx000nnn FIADD dw +// 04 DC xx000nnn FADD dr +// 06 DE xx000nnn FIADD w +/*08*/ case 0x08: +/*0a*/ case 0x0a: +/*0c*/ case 0x0c: +/*0e*/ case 0x0e: +//* 08 D8 xx001nnn FMUL mem32r +// 0A DA xx001nnn FIMUL dw +// 0C DC xx001nnn FMUL dr +// 0E DE xx001nnn FIMUL w +/*20*/ case 0x20: +/*22*/ case 0x22: +/*24*/ case 0x24: +/*26*/ case 0x26: +//* 20 D8 xx100nnn FSUB mem32r +// 22 DA xx100nnn FISUB dw +// 24 DC xx100nnn FSUB dr +// 26 DE xx100nnn FISUB w +/*28*/ case 0x28: +/*2a*/ case 0x2a: +/*2c*/ case 0x2c: +/*2e*/ case 0x2e: +//* 28 D8 xx101nnn FSUBR mem32r +// 2A DA xx101nnn FISUBR dw +// 2C DC xx101nnn FSUBR dr +// 2E DE xx101nnn FISUBR w +/*30*/ case 0x30: +/*32*/ case 0x32: +/*34*/ case 0x34: +/*36*/ case 0x36: +//* 30 D8 xx110nnn FDIV mem32r +// 32 DA xx110nnn FIDIV dw +// 34 DC xx110nnn FDIV dr +// 36 DE xx110nnn FIDIV w +/*38*/ case 0x38: +/*3a*/ case 0x3a: +/*3c*/ case 0x3c: +/*3e*/ case 0x3e: +//* 38 D8 xx111nnn FDIVR mem32r +// 3A DA xx111nnn FIDIVR dw +// 3C DC xx111nnn FDIVR dr +// 3E DE xx111nnn FIDIVR w + WFR0 = *ST0; + switch(exop) { // Fop (edi) + case 0x00: WFR0 += read_float(AR1.d); break; + case 0x02: WFR0 += (int32_t)read_dword(AR1.d); break; + case 0x04: WFR0 += read_double(AR1.d); break; + case 0x06: WFR0 += (int16_t)read_word(AR1.d); break; + case 0x08: WFR0 *= read_float(AR1.d); break; + case 0x0a: WFR0 *= (int32_t)read_dword(AR1.d); break; + case 0x0c: WFR0 *= read_double(AR1.d); break; + case 0x0e: WFR0 *= (int16_t)read_word(AR1.d); break; + case 0x20: WFR0 -= read_float(AR1.d); break; + case 0x22: WFR0 -= (int32_t)read_dword(AR1.d); break; + case 0x24: WFR0 -= read_double(AR1.d); break; + case 0x26: WFR0 -= (int16_t)read_word(AR1.d); break; + case 0x28: WFR0 = (long double)read_float(AR1.d) - WFR0; break; + case 0x2a: WFR0 = (long double)(int32_t)read_dword(AR1.d) - WFR0; break; + case 0x2c: WFR0 = (long double)read_double(AR1.d) - WFR0; break; + case 0x2e: WFR0 = (long double)(int16_t)read_word(AR1.d) - WFR0; break; + case 0x30: WFR0 /= read_float(AR1.d); break; + case 0x32: WFR0 /= (int32_t)read_dword(AR1.d); break; + case 0x34: WFR0 /= read_double(AR1.d); break; + case 0x36: WFR0 /= (int16_t)read_word(AR1.d); break; + case 0x38: WFR0 = (long double)read_float(AR1.d) / WFR0; break; + case 0x3a: WFR0 = (long double)(int32_t)read_dword(AR1.d) / WFR0; break; + case 0x3c: WFR0 = (long double)read_double(AR1.d) / WFR0; break; + case 0x3e: WFR0 = (long double)(int16_t)read_word(AR1.d) / WFR0; break; + } + *ST0 = WFR0; + break; + +/*10*/ case 0x10: +/*11*/ case 0x11: +/*12*/ case 0x12: +/*13*/ case 0x13: +/*14*/ case 0x14: +/*15*/ case 0x15: +/*16*/ case 0x16: +/*17*/ case 0x17: +/*18*/ case 0x18: +/*19*/ case 0x19: +/*1a*/ case 0x1a: +/*1b*/ case 0x1b: +/*1c*/ case 0x1c: +/*1d*/ case 0x1d: +/*1e*/ case 0x1e: +/*1f*/ case 0x1f: +//* 10 D8 xx010nnn FCOM mem32r +//* 11 D9 xx010nnn FST mem32r +// 12 DA xx010nnn FICOM dw +// 13 DB xx010nnn FIST dw +// 14 DC xx010nnn FCOM dr +// 15 DD xx010nnn FST dr +// 16 DE xx010nnn FICOM w +// 17 DF xx010nnn FIST w +//* 18 D8 xx011nnn FCOMP mem32r +//* 19 D9 xx011nnn FSTP mem32r +// 1A DA xx011nnn FICOMP dw +// 1B DB xx011nnn FISTP dw +// 1C DC xx011nnn FCOMP dr +// 1D DD xx011nnn FSTP dr +// 1E DE xx011nnn FICOMP w +// 1F DF xx011nnn FISTP w + WFR0 = *ST0; + switch(exop) { // Fop (edi), no pop + case 0x10: + case 0x18: WFR1 = (long double)read_float(AR1.d); goto fcom00; + case 0x12: + case 0x1a: WFR1 = (long double)(int32_t)read_dword(AR1.d); goto fcom00; + case 0x14: + case 0x1c: WFR1 = (long double)read_double(AR1.d); goto fcom00; + case 0x16: + case 0x1e: WFR1 = (long double)(int16_t)read_word(AR1.d); +fcom00: TheCPU.fpus &= ~(FPUS_C0 | FPUS_C2 | FPUS_C3); + if (WFR0 < WFR1) + TheCPU.fpus |= FPUS_C0; + else if (WFR0 == WFR1) + TheCPU.fpus |= FPUS_C3; + else if (WFR0 > WFR1); /* do nothing */ + else /* not comparable */ + TheCPU.fpus |= FPUS_C0 | FPUS_C2 | FPUS_C3; + break; + case 0x11: + case 0x19: write_float(AR1.d, WFR0); break; + case 0x15: + case 0x1d: write_double(AR1.d, WFR0); break; + case 0x13: + case 0x1b: + case 0x17: + case 0x1f: { + WFR0 = nearbyintl(WFR0); + if (exop & 4) { + if (isnan(WFR0) || isinf(WFR0) || + WFR0 < (long double)-0x8000 || + WFR0 > (long double)0x7fff) { + TheCPU.fpus |= FPUS_IE; + WFR0 = (long double)-0x8000; + } + else if (WFR0 != *ST0) /* flag inexact */ + TheCPU.fpus |= FPUS_PE; + write_word(AR1.d, (int16_t)WFR0); break; + } + if (isnan(WFR0) || isinf(WFR0) || + WFR0 < -(long double)0x80000000 || + WFR0 > (long double)0x7fffffff) { + TheCPU.fpus |= FPUS_IE; + WFR0 = -(long double)0x80000000; + } + else if (WFR0 != *ST0) /* flag inexact */ + TheCPU.fpus |= FPUS_PE; + write_dword(AR1.d, (int32_t)WFR0); break; } + } + if (exop&8) INCFSPP; + break; + +/*37*/ case 0x37: { +// 37 DF xx110nnn FBSTP + dosaddr_t p = AR1.d; + long long b; + int i; + WFR0 = *ST0; + WFR0 = nearbyintl(WFR0); + if (isnan(WFR0) || fabsl(WFR0) >= 1000000000000000000.0L) { + /* store packed BCD indefinite value */ + write_dword(p, 0); + write_word(p+4, 0); + write_dword(p+6, 0xffffc000u); + TheCPU.fpus |= FPUS_IE; + INCFSPP; + break; + } + else if (WFR0 != *ST0) + TheCPU.fpus |= FPUS_PE; + write_byte(p+9, signbit(WFR0) ? 0x80 : 0); + b = fabsl(WFR0); + for (i=0; i < 9; i++) { + uint8_t u = b % 10; + b /= 10; + write_byte(p+i, u | ((b % 10) << 4)); + b /= 10; + } + INCFSPP; + } + break; +/*3b*/ case 0x3b: +// 3B DB xx111nnn FSTP ext + WFR0 = *ST0; + write_long_double(AR1.d, WFR0); + INCFSPP; + break; +/*3f*/ case 0x3f: { +// 3F DF xx111nnn FISTP qw + WFR0 = *ST0; + WFR0 = nearbyintl(WFR0); + if (isnan(WFR0) || isinf(WFR0) || + WFR0 < (long double)(long long)0x8000000000000000ULL || + WFR0 > (long double)(long long)0x7fffffffffffffffULL) { + TheCPU.fpus |= FPUS_IE; + WFR0 = (long double)(long long)0x8000000000000000ULL; + } + else if (WFR0 != *ST0) + TheCPU.fpus |= FPUS_PE; + write_qword(AR1.d, (int64_t)WFR0); + INCFSPP; + } + break; +/*29*/ case 0x29: +//* 29 D9 xx101nnn FLDCW 2b + TheCPU.fpuc = read_word(AR1.d) | 0x40; + fp87_set_rounding(); + break; +/*39*/ case 0x39: +//* 39 D9 xx111nnn FSTCW 2b + write_word(AR1.d, TheCPU.fpuc); + break; + +/*67*/ case 0x67: if (reg!=0) goto fp_notok; +// 67.0 DF 11000000 FSTSW ax +/*3d*/ case 0x3d: +// 3D DD xx111nnn FSTSW 2b + // movw FPUS(ebx),ax + // andw 0xc7ff,ax + // movw FPSTT(ebx),cx + // andw 0x07,cx + // shll 11,ecx + // orl ecx,eax + SYNCFSP; + fp87_save_except(); + if (exop==0x3d) { + // movw ax,(edi) + write_word(AR1.d, TheCPU.fpus); + } + else { + CPUWORD(Ofs_AX) = TheCPU.fpus; + } + break; + +/*40*/ case 0x40: +/*48*/ case 0x48: +/*60*/ case 0x60: +/*68*/ case 0x68: +/*70*/ case 0x70: +/*78*/ case 0x78: +//* 40 D8 11000nnn FADD st,st(n) +//* 48 D8 11001nnn FMUL st,st(n) +//* 60 D8 11100nnn FSUB st,st(n) +//* 68 D8 11101nnn FSUBR st,st(n) +//* 70 D8 11110nnn FDIV st,st(n) +//* 78 D8 11111nnn FDIVR st,st(n) + WFR0 = *ST0; + WFR1 = *STn(reg); + switch (exop) { + case 0x40: WFR0 += WFR1; break; + case 0x48: WFR0 *= WFR1; break; + case 0x60: WFR0 -= WFR1; break; + case 0x68: WFR0 = WFR1 - WFR0; break; + case 0x70: WFR0 /= WFR1; break; + case 0x78: WFR0 = WFR1 / WFR0; break; + } + *ST0 = WFR0; + break; + +/*50*/ case 0x50: +/*58*/ case 0x58: +//* 50 D8 11010nnn FCOM st,st(n) +//* 58 D8 11011nnn FCOMP st,st(n) + WFR0 = *ST0; + WFR1 = *STn(reg); + TheCPU.fpus &= ~(FPUS_C0 | FPUS_C2 | FPUS_C3); + if (WFR0 < WFR1) + TheCPU.fpus |= FPUS_C0; + else if (WFR0 == WFR1) + TheCPU.fpus |= FPUS_C3; + else if (WFR0 > WFR1); /* do nothing */ + else /* not comparable */ + TheCPU.fpus |= FPUS_C0 | FPUS_C2 | FPUS_C3; + if (exop&8) INCFSPP; + break; + +/*6a*/ case 0x6a: if (reg!=1) goto fp_notok; +/*6d*/ case 0x6d: +/*65*/ case 0x65: +// 65 DD 11000nnn FUCOM st(n),st(0) +// 6D DD 11101nnn FUCOMP st(n) +// 6A.1 DA 11101001 FUCOMPP + WFR0 = *ST0; + WFR1 = *STn(reg); + TheCPU.fpus &= ~(FPUS_C0 | FPUS_C2 | FPUS_C3); + if (isnan(WFR0) || isnan(WFR1)) /* avoids FE_INVALID for QNaN */ + TheCPU.fpus |= FPUS_C0 | FPUS_C2 | FPUS_C3; + else if (WFR0 < WFR1) + TheCPU.fpus |= FPUS_C0; + else if (WFR0 == WFR1) + TheCPU.fpus |= FPUS_C3; + if (exop==0x6a) INCFSPP; + if (exop>=0x6a) INCFSPP; + break; + +// 73 DB 11000nnn FCOMI st(0),st(n) +// 77 DF 11000nnn FCOMIP st(0),st(n) +// 6B DB 11101nnn FUCOMI st(0),st(n) +// 6F DF 11101nnn FUCOMIP st(0),st(n) + +/*5e*/ case 0x5e: if (reg==1) { +// 5E.1 DE 11011001 FCOMPP + WFR0 = *ST0; + WFR1 = *ST1; + TheCPU.fpus &= ~(FPUS_C0 | FPUS_C2 | FPUS_C3); + if (WFR0 < WFR1) + TheCPU.fpus |= FPUS_C0; + else if (WFR0 == WFR1) + TheCPU.fpus |= FPUS_C3; + else if (WFR0 > WFR1); /* do nothing */ + else /* not comparable */ + TheCPU.fpus |= FPUS_C0 | FPUS_C2 | FPUS_C3; + INCFSPP; + INCFSPP; + } + else goto fp_notok; + break; + +/*44*/ case 0x44: +/*4c*/ case 0x4c: +/*46*/ case 0x46: +/*4e*/ case 0x4e: +// 44 DC 11000nnn FADD st(n),st +// 4C DC 11001nnn FMUL st(n),st +// 46 DE 11000nnn FADDP st(n),st +// 4E DE 11001nnn FMULP st(n),st + WFR0 = *ST0; + WFR1 = *STn(reg); + switch (exop) { + case 0x44: + case 0x46: WFR1 += WFR0; break; + case 0x4c: + case 0x4e: WFR1 *= WFR0; break; + } + *STn(reg) = WFR1; + if (exop&2) INCFSPP; + break; + +/*64*/ case 0x64: +/*6c*/ case 0x6c: +/*74*/ case 0x74: +/*7c*/ case 0x7c: +/*66*/ case 0x66: +/*6e*/ case 0x6e: +/*76*/ case 0x76: +/*7e*/ case 0x7e: +// 64 DC 11100nnn FSUBR st(n),st(0) +// 6C DC 11101nnn FSUB st(n),st(0) +// 74 DC 11110nnn FDIVR st(n),st(0) +// 7C DC 11111nnn FDIV st(n),st(0) +// 66 DE 11100nnn FSUBRP st(n),st(0) +// 6E DE 11101nnn FSUBP st(n),st(0) +// 76 DE 11110nnn FDIVRP st(n),st(0) +// 7E DE 11111nnn FDIVP st(n),st(0) + WFR0 = *ST0; + WFR1 = *STn(reg); + switch (exop) { + case 0x64: + case 0x66: WFR1 = WFR0 - WFR1; break; + case 0x6c: + case 0x6e: WFR1 -= WFR0; break; + case 0x74: + case 0x76: WFR1 = WFR0 / WFR1; break; + case 0x7c: + case 0x7e: WFR1 /= WFR0; break; + } + *STn(reg) = WFR1; + if (exop&2) INCFSPP; + break; + +/*41*/ case 0x41: +//* 41 D9 11000nnn FLD st(n) + WFR0 = *STn(reg); + DECFSPP; + *ST0 = WFR0; + break; + +/*45*/ case 0x45: +// 45 DD 11000nnn FFREE st(n) set tag(n) empty + FREETAG(reg); + break; +/*51*/ case 0x51: +/*59*/ case 0x59: + if (reg==0) { +//* 51.0 D9 11010000 FNOP + break; // nop + } + else goto fp_notok; + break; + +/*49*/ case 0x49: +//* 49 D9 11001nnn FXCH st,st(n) + { long double t; + memcpy(&t, ST0, sizeof t); + memcpy(ST0, STn(reg), sizeof t); + memcpy(STn(reg), &t, sizeof t); + } + break; + +/*55*/ case 0x55: +/*5d*/ case 0x5d: +// 55 DD 11010nnn FST st(n) +// 5D DD 11011nnn FSTP st(n) + *STn(reg) = *ST0; + UNFREETAG(reg); + if (exop==0x5d) INCFSPP; + break; + +/*61*/ case 0x61: +//* 61.0 D9 11100000 FCHS +//* 61.1 D9 11100001 FABS +//* 61.4 D9 11100100 FTST +//* 61.5 D9 11100101 FXAM + WFR0 = *ST0; + switch(reg) { + case 0: /* FCHS */ + TheCPU.fpus &= ~FPUS_C1; + WFR0 = -WFR0; break; + case 1: /* FABS */ + TheCPU.fpus &= ~FPUS_C1; + WFR0 = fabsl(WFR0); break; + case 4: /* FTST */ + TheCPU.fpus &= ~FPUS_C; + if (WFR0 < 0.0) TheCPU.fpus |= FPUS_C0; + else if (WFR0 == 0.0) TheCPU.fpus |= FPUS_C3; + else if (WFR0 > 0.0); /* do nothing; */ + else /* not comparable */ + TheCPU.fpus |= FPUS_C0 | FPUS_C2 | FPUS_C3; + break; + case 5: /* FXAM */ + fxam(WFR0); + break; + default: + goto fp_notok; + } + *ST0 = WFR0; + break; + +/*63*/ case 0x63: switch(reg) { +// 63.2* DB 11000010 FCLEX +// 63.3* DB 11000011 FINIT + case 2: /* FCLEX */ + TheCPU.fpus &= (FPUS_C | FPUS_TOP); + feclearexcept(FE_ALL_EXCEPT); + break; + case 3: /* FINIT */ + TheCPU.fpus = 0; + TheCPU.fpstt = 0; + TheCPU.fpuc = 0x37f; + TheCPU.fptag = 0xffff; + fp87_set_rounding(); + feclearexcept(FE_ALL_EXCEPT); + WFR0 = WFR1 = 0.0; + break; + default: /* FNENI,FNDISI: 8087 */ + /* FSETPM,FRSTPM: 80287 */ + goto fp_ok; // do nothing + } + break; + +/*69*/ case 0x69: { +//* 69.0 D9 11101000 FLD1 +//* 69.1 D9 11101001 FLDL2T +//* 69.2 D9 11101010 FLDL2E +//* 69.3 D9 11101011 FLDPI +//* 69.4 D9 11101100 FLDLG2 +//* 69.5 D9 11101101 FLDLN2 +//* 69.6 D9 11101110 FLDZ + DECFSPP; + switch (reg) { + case 0: WFR0 = 1.0; break; + case 1: WFR0 = M_LN10l/M_LN2l; break; + case 2: WFR0 = M_LOG2El; break; + case 3: WFR0 = M_PIl; break; + case 4: WFR0 = M_LN2l/M_LN10l; break; + case 5: WFR0 = M_LN2l; break; + case 6: WFR0 = 0.0; break; + default: goto fp_notok; + } + *ST0 = WFR0; + } + break; + +/*71*/ case 0x71: switch(reg) { +// 71.0 D9 11110000 F2XM1 st(0) +// 71.1 D9 11110001 FYL2X st(1)*l2(st(0))->st(1),pop +// 71.2 D9 11110010 FPTAN st(0),push 1 +// 71.3 D9 11110011 FPATAN st(1)/st(0)->st(1),pop +// 71.4 D9 11110100 FXTRACT exp->st(0),push signif +// 71.5 D9 11110101 FPREM1 st(0)/st(1)->st(0) +// 71.6 D9 11110110 FDECSTP +// 71.7 D9 11110111 FINCSTP + case 0: /* F2XM1 */ + WFR0 = *ST0; + WFR0 = expm1l(WFR0*M_LN2l); + *ST0 = WFR0; + break; + case 1: /* FYL2X */ + WFR0 = *ST0; + WFR1 = *ST1; + if (WFR0 < 0.0) { + WFR0 = -NAN; + TheCPU.fpus |= FPUS_IE; + } else if (WFR0 == 0.0) { + WFR0 = signbit(WFR1) ? INFINITY : -INFINITY; + TheCPU.fpus |= FPUS_ZE; + } else { + WFR0 = WFR1 * log2l(WFR0); + } + INCFSPP; + *ST0 = WFR0; + break; + case 3: /* FPATAN */ + WFR0 = *ST0; + WFR1 = *ST1; + WFR0 = atan2l(WFR1, WFR0); + INCFSPP; + *ST0 = WFR0; + break; + case 2: /* FPTAN */ + WFR0 = *ST0; + if (isfinite(WFR0) && fabsl(WFR0) >= 1ULL<<63) { + TheCPU.fpus |= FPUS_C2; + break; + } + WFR0 = tanl(WFR0); + *ST0 = WFR0; DECFSPP; + TheCPU.fpus &= ~FPUS_C2; + *ST0 = 1.0; + break; + case 4: /* FXTRACT */ + WFR0 = *ST0; + { + int exp; + WFR1 = frexpl(WFR0, &exp) * 2; + WFR0 = logbl(WFR0); + } + *ST0 = WFR0; DECFSPP; + *ST0 = WFR1; + break; + case 5: /* FPREM1 */ + WFR0 = *ST0; + WFR1 = *ST1; + TheCPU.fpus &= ~FPUS_C; + if (!isfinite(WFR0) || WFR0 == 0.0 || + !isfinite(WFR1) || WFR1 == 0.0) + WFR0 = remainderl(WFR0, WFR1); + else { + int d = ilogbl(WFR0) - ilogbl(WFR1); + if (d < 64) { + int iq; + /* result from remquol not exactly the + same as remainderl in some strange + edge cases */ + remquol(WFR0, WFR1, &iq); + iq = abs(iq); + WFR0 = remainderl(WFR0, WFR1); + TheCPU.fpus |= ((iq & 1) << (9-0)) | + ((iq & 2) << (14-1)) | + ((iq & 4) << (8-2)); + } else { + int n = (d & 0x1f) | 0x20; + WFR0 = fmodl(WFR0, ldexpl(WFR1, d - n)); + TheCPU.fpus |= FPUS_C2; + } + } + *ST0 = WFR0; + break; + case 6: /* FDECSTP */ + DECFSP; break; + case 7: /* FINCSTP */ + INCFSP; break; + } + break; + +/*79*/ case 0x79: switch(reg) { +// 79.0 D9 11111000 FPREM st(0)/st(1)->st(0) +// 79.1 D9 11111001 FYL2XP1 st(1)*lg(st(0))->st(1),pop +// 79.2 D9 11111010 FSQRT st(0) +// 79.3 D9 11111011 FSINCOS sin->st(0), push cos +// 79.4 D9 11111100 FRNDINT st(0) +// 79.5 D9 11111101 FSCALE st(0) by st(1) +// 79.6 D9 11111110 FSIN st(0) +// 79.7 D9 11111111 FCOS st(0) + case 0: /* FPREM */ + WFR0 = *ST0; + WFR1 = *ST1; + TheCPU.fpus &= ~FPUS_C; + if (!isfinite(WFR0) || WFR0 == 0.0 || + !isfinite(WFR1) || WFR1 == 0.0) + WFR0 = fmodl(WFR0, WFR1); + else { + int d = ilogbl(WFR0) - ilogbl(WFR1); + if (d < 64) { + unsigned iq; + int roundingmode = fegetround(); + fp87_save_except(); + fesetround(FE_TOWARDZERO); + iq = (uint64_t)fabsl(nearbyintl(WFR0 / WFR1)); + fesetround(roundingmode); + feclearexcept(FE_ALL_EXCEPT); + WFR0 = fmodl(WFR0, WFR1); + TheCPU.fpus |= ((iq & 1) << (9-0)) | + ((iq & 2) << (14-1)) | + ((iq & 4) << (8-2)); + } else { + int n = (d & 0x1f) | 0x20; + WFR0 = fmodl(WFR0, ldexpl(WFR1, d - n)); + TheCPU.fpus |= FPUS_C2; + } + } + *ST0 = WFR0; + break; + case 5: /* FSCALE */ + WFR0 = *ST0; + WFR1 = *ST1; + if (isnan(WFR1) || + (WFR0 == 0.0 && isinf(WFR1) == 1) || + (isinf(WFR0) && isinf(WFR1) == -1)) + WFR0 = WFR1 = NAN; + else if (isfinite(WFR0)) { + int inf = isinf(WFR1); + if (inf == 1) + WFR0 = WFR0 > 0 ? INFINITY : -INFINITY; + else if (inf == -1) + WFR0 = WFR0 > 0 ? 0.0 : -0.0; + } + if (WFR1 >= INT_MAX) WFR1 = INT_MAX; + if (WFR1 <= INT_MIN) WFR1 = INT_MIN; + WFR0 = ldexpl(WFR0, WFR1); + *ST0 = WFR0; + break; + case 1: /* FYL2XP1 */ + WFR0 = *ST0; + WFR1 = *ST1; + if (WFR0 > -1.0) + WFR0 = (WFR1 / M_LN2l) * log1pl(WFR0); + INCFSPP; + *ST0 = WFR0; + break; + case 2: /* FSQRT */ + WFR0 = *ST0; + if (signbit(WFR0) && WFR0 != -0.0) { + if (!isnan(WFR0)) TheCPU.fpus |= FPUS_IE; + WFR0 = -NAN; + } else { + WFR0 = sqrtl(WFR0); + } + *ST0 = WFR0; + break; + case 4: /* FRNDINT */ + WFR0 = *ST0; + WFR0 = rintl(WFR0); + *ST0 = WFR0; + break; + case 6: /* FSIN */ + WFR0 = *ST0; + if (isfinite(WFR0) && fabsl(WFR0) >= 1ULL<<63) { + TheCPU.fpus |= FPUS_C2; + break; + } + WFR0 = sinl(WFR0); + TheCPU.fpus &= ~FPUS_C2; + *ST0 = WFR0; + break; + case 7: /* FCOS */ + WFR0 = *ST0; + if (isfinite(WFR0) && fabsl(WFR0) >= 1ULL<<63) { + TheCPU.fpus |= FPUS_C2; + break; + } + WFR0 = cosl(WFR0); + TheCPU.fpus &= ~FPUS_C2; + *ST0 = WFR0; + break; + case 3: /* FSINCOS */ + WFR0 = *ST0; + if (isfinite(WFR0) && fabsl(WFR0) >= 1ULL<<63) { + TheCPU.fpus |= FPUS_C2; + break; + } + WFR1 = cosl(WFR0); + WFR0 = sinl(WFR0); + *ST0 = WFR0; DECFSPP; + TheCPU.fpus &= ~FPUS_C2; + *ST0 = WFR1; + break; + } + break; + +/*21*/ case 0x21: +/*25*/ case 0x25: { +//* 21 D9 xx100nnn FLDENV 14/28byte +// 25 DD xx100nnn FRSTOR 94/108byte + dosaddr_t p = TheCPU.mem_ref, q; + TheCPU.fpuc = read_word(p) | 0x40; + feclearexcept(FE_ALL_EXCEPT); + fp87_set_rounding(); + if (reg&DATA16) { + TheCPU.fpus = read_word(p+2); TheCPU.fptag = read_word(p+4); + q = p+14; + } + else { + TheCPU.fpus = read_word(p+4); TheCPU.fptag = read_word(p+8); + q = p+28; + } + TheCPU.fpstt = (TheCPU.fpus>>FPUS_TOP_BIT)&7; + if (exop==0x25) { + int i, k; + k = TheCPU.fpstt; + for (i=0; i<8; i++) { + TheCPU.fpregs[k] = read_long_double(q); + k = (k+1)&7; q += 10; + } + } + } + break; + +/* + * FSAVE: 4 modes - followed by FINIT + * + * A) 16-bit real (94 bytes) + * (00-01) Control Word + * (02-03) Status Word + * (04-05) Tag Word + * (06-07) IP 15..00 + * (08-09) IP 19..16,0,Opc 10..00 + * (0a-0b) OP 15..00 + * (0c-0d) OP 19..16,0... + * (0e-5d) FP registers + * + * B) 16-bit protected (94 bytes) + * (00-01) Control Word + * (02-03) Status Word + * (04-05) Tag Word + * (06-07) IP offset + * (08-09) IP selector + * (0a-0b) OP offset + * (0c-0d) OP selector + * (0e-5d) FP registers + * + * C) 32-bit real (108 bytes) + * (00-01) Control Word (02-03) reserved + * (04-05) Status Word (06-07) reserved + * (08-09) Tag Word (0a-0b) reserved + * (0c-0d) IP 15..00 (0e-0f) reserved + * (10-13) 0,0,0,0,IP 31..16,0,Opc 10..00 + * (14-15) OP 15..00 (16-17) reserved + * (18-1b) 0,0,0,0,OP 31..16,0... + * (1c-6b) FP registers + * + * D) 32-bit protected (108 bytes) + * (00-01) Control Word (02-03) reserved + * (04-05) Status Word (06-07) reserved + * (08-09) Tag Word (0a-0b) reserved + * (0c-0d) IP offset (0e-0f) reserved + * (10-13) 0,0,0,0,Opc 10..00,IP selector + * (14-15) OP offset (16-17) reserved + * (18-19) OP selector (1a-1b) reserved + * (1c-6b) FP registers + */ +/*31*/ case 0x31: +/*35*/ case 0x35: { + dosaddr_t q; + int i; + unsigned fptag, ntag; +//* 31 D9 xx110nnn FSTENV 14/28byte +// 35 DD xx110nnn FSAVE 94/108byte + fp87_save_except(); + TheCPU.fpus = (TheCPU.fpus & ~FPUS_TOP) | (TheCPU.fpstt<=0; --i) { + long double d = TheCPU.fpregs[i]; + ntag <<= 2; + if ((fptag & 0xc000) == 0xc000) ntag |= 3; + else if (isnan(d) || !isnormal(d) || isinf(d)) ntag |= 2; + else if (d == 0) ntag |= 1; + fptag <<= 2; + } + TheCPU.fptag = ntag; + dosaddr_t p = TheCPU.mem_ref; + if (reg&DATA16) { + write_word(p, TheCPU.fpuc); + write_word(p+2, TheCPU.fpus); + write_word(p+4, TheCPU.fptag); + /* IP,OP,opcode: n.i. */ + write_qword(p+6, 0); + q = p+14; + } + else { + dosaddr_t p = TheCPU.mem_ref; + write_dword(p, TheCPU.fpuc); + write_dword(p+4, TheCPU.fpus); + write_dword(p+8, TheCPU.fptag); + /* IP,OP,opcode: n.i. */ + write_qword(p+12, 0); + write_qword(p+20, 0); + q = p+28; + } + TheCPU.fpuc |= 0x3f; + if (exop==0x35) { + int i, k; + k = TheCPU.fpstt; + for (i=0; i<8; i++) { + write_long_double(q, TheCPU.fpregs[k]); + k = (k+1)&7; q += 10; + } + TheCPU.fpus = 0; + TheCPU.fpstt = 0; + TheCPU.fpuc = 0x37f; + TheCPU.fptag = 0xffff; + fp87_set_rounding(); + feclearexcept(FE_ALL_EXCEPT); + WFR0 = WFR1 = 0.0; + } + } + break; + +/*xx*/ default: +fp_notok: + return -1; + } +fp_ok: + return 0; +} + diff --git a/src/base/emu-i386/simx86/fp87-x86.c b/src/base/emu-i386/simx86/fp87-x86.c new file mode 100644 index 0000000..0043f76 --- /dev/null +++ b/src/base/emu-i386/simx86/fp87-x86.c @@ -0,0 +1,516 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#include +#include "emu86.h" + +#ifdef HOST_ARCH_X86 + +#include "codegen-x86.h" + +static int Fp87_op_x86_sim(int exop, int reg); + +/* + * Only mask bits 0-5 of the control word (fpuc), + * and memory and ax (for fstsw) references are emulated. + * All other FPU state is native. + */ + +struct float_env16 { + uint16_t fpuc; + uint16_t fpus; + uint16_t fptag; + uint16_t ignored[4]; + unsigned char fpregs[80]; +}; + +struct float_env32 { + uint16_t fpuc; + uint16_t dummy1; + uint16_t fpus; + uint16_t dummy2; + uint16_t fptag; + uint16_t dummy3; + uint32_t ignored[4]; + unsigned char fpregs[80]; +}; + +void init_emu_npu_x86 (void) +{ + Fp87_op = Fp87_op_x86_sim; + TheCPU.fpuc = 0x37f; +} + +unsigned char *Fp87_op_x86(unsigned char *CodePtr, int exop, int reg) +{ + register unsigned char *Cp = CodePtr; + +// 0B DB xx001nnn FISTTP dw // SSE3 capable CPUs +// 0D DD xx001nnn FISTTP qw // SSE3 capable CPUs +// 0F DF xx001nnn FISTTP w // SSE3 capable CPUs + +// 42 DA 11000nnn FCMOVB st(0),st(n) +// 43 DB 11000nnn FCMOVNB st(0),st(n) +// 4A DA 11001nnn FCMOVE st(0),st(n) +// 4B DB 11001nnn FCMOVNE st(0),st(n) +// 52 DA 11010nnn FCMOVBE st(0),st(n) +// 53 DB 11010nnn FCMOVNBE st(0),st(n) +// 5A DA 11011nnn FCMOVU st(0),st(n) +// 5B DB 11011nnn FCMOVNU st(0),st(n) + + e_printf("FPop %x.%d\n", exop, reg); + + switch(exop) { +/*01*/ case 0x01: +/*03*/ case 0x03: +/*05*/ case 0x05: +/*07*/ case 0x07: +//* 01 D9 xx000nnn FLD mem32r +// 03 DB xx000nnn FILD dw +// 05 DD xx000nnn FLD dr +// 07 DF xx000nnn FILD w +/*27*/ case 0x27: +// 27 DF xx100nnn FBLD +/*2b*/ case 0x2b: +/*2f*/ case 0x2f: +// 2B DB xx101nnn FLD ext +// 2F DF xx101nnn FILD qw +/*00*/ case 0x00: +/*02*/ case 0x02: +/*04*/ case 0x04: +/*06*/ case 0x06: +//* 00 D8 xx000nnn FADD mem32r +// 02 DA xx000nnn FIADD dw +// 04 DC xx000nnn FADD dr +// 06 DE xx000nnn FIADD w +/*08*/ case 0x08: +/*0a*/ case 0x0a: +/*0c*/ case 0x0c: +/*0e*/ case 0x0e: +//* 08 D8 xx001nnn FMUL mem32r +// 0A DA xx001nnn FIMUL dw +// 0C DC xx001nnn FMUL dr +// 0E DE xx001nnn FIMUL w +/*20*/ case 0x20: +/*22*/ case 0x22: +/*24*/ case 0x24: +/*26*/ case 0x26: +//* 20 D8 xx100nnn FSUB mem32r +// 22 DA xx100nnn FISUB dw +// 24 DC xx100nnn FSUB dr +// 26 DE xx100nnn FISUB w +/*28*/ case 0x28: +/*2a*/ case 0x2a: +/*2c*/ case 0x2c: +/*2e*/ case 0x2e: +//* 28 D8 xx101nnn FSUBR mem32r +// 2A DA xx101nnn FISUBR dw +// 2C DC xx101nnn FSUBR dr +// 2E DE xx101nnn FISUBR w +/*30*/ case 0x30: +/*32*/ case 0x32: +/*34*/ case 0x34: +/*36*/ case 0x36: +//* 30 D8 xx110nnn FDIV mem32r +// 32 DA xx110nnn FIDIV dw +// 34 DC xx110nnn FDIV dr +// 36 DE xx110nnn FIDIV w +/*38*/ case 0x38: +/*3a*/ case 0x3a: +/*3c*/ case 0x3c: +/*3e*/ case 0x3e: +//* 38 D8 xx111nnn FDIVR mem32r +// 3A DA xx111nnn FIDIVR dw +// 3C DC xx111nnn FDIVR dr +// 3E DE xx111nnn FIDIVR w +/*10*/ case 0x10: +/*11*/ case 0x11: +/*12*/ case 0x12: +/*13*/ case 0x13: +/*14*/ case 0x14: +/*15*/ case 0x15: +/*16*/ case 0x16: +/*17*/ case 0x17: +/*18*/ case 0x18: +/*19*/ case 0x19: +/*1a*/ case 0x1a: +/*1b*/ case 0x1b: +/*1c*/ case 0x1c: +/*1d*/ case 0x1d: +/*1e*/ case 0x1e: +/*1f*/ case 0x1f: +//* 10 D8 xx010nnn FCOM mem32r +//* 11 D9 xx010nnn FST mem32r +// 12 DA xx010nnn FICOM dw +// 13 DB xx010nnn FIST dw +// 14 DC xx010nnn FCOM dr +// 15 DD xx010nnn FST dr +// 16 DE xx010nnn FICOM w +// 17 DF xx010nnn FIST w +//* 18 D8 xx011nnn FCOMP mem32r +//* 19 D9 xx011nnn FSTP mem32r +// 1A DA xx011nnn FICOMP dw +// 1B DB xx011nnn FISTP dw +// 1C DC xx011nnn FCOMP dr +// 1D DD xx011nnn FSTP dr +// 1E DE xx011nnn FICOMP w +// 1F DF xx011nnn FISTP w + +/*37*/ case 0x37: +// 37 DF xx110nnn FBSTP +/*3b*/ case 0x3b: +/*3f*/ case 0x3f: +// 3B DB xx111nnn FSTP ext +// 3F DF xx111nnn FISTP qw +fp_mem: + G3M(0xd8+(exop&7),(exop&0x38)|4,0x2f,Cp); // Fop (edi,ebp,1) + break; + +/*29*/ case 0x29: +//* 29 D9 xx101nnn FLDCW 2b + // movw (edi,ebp,1),ax + G4(0x2f048b66,Cp); + // movl eax,ecx + G2(0xc189,Cp); + // orb 0x3f,al + G2(0x3f0c,Cp); + // movw ax,FPUC(ebx) + G3(0x438966,Cp); G1(Ofs_FPUC,Cp); + // fldcw FPUC(ebx) + G2(0x6bd9,Cp); G1(Ofs_FPUC,Cp); + // movw cx,FPUC(ebx) + G3(0x4b8966,Cp); G1(Ofs_FPUC,Cp); + break; +/*39*/ case 0x39: +//* 39 D9 xx111nnn FSTCW 2b + // movb FPUC(ebx),cl + G2(0x4b8a,Cp); G1(Ofs_FPUC,Cp); + // andb 0x3f,cl + G3(0x3fe180,Cp); + // fstcw FPUC(ebx) + G2(0x7bd9,Cp); G1(Ofs_FPUC,Cp); + // movw FPUC(ebx),ax + G3(0x438b66,Cp); G1(Ofs_FPUC,Cp); + // andb ~0x3f,al + G2(0xc024,Cp); + // orb cl,al + G2(0xc808,Cp); + // movw ax,FPUC(ebx) + G3(0x438966,Cp); G1(Ofs_FPUC,Cp); + // movw ax,(edi,ebp,1) + G4(0x2f048966,Cp); + break; + +/*3d*/ case 0x3d: goto fp_mem; +/*67*/ case 0x67: if (reg!=0) goto fp_notok; +// 67.0 DF 11000000 FSTSW ax +// 3D DD xx111nnn FSTSW 2b + G3M(0xdd,0x7b,Ofs_AX,Cp); // FSTSW Ofs_AX(%%ebx) + break; + +/*40*/ case 0x40: +/*48*/ case 0x48: +/*60*/ case 0x60: +/*68*/ case 0x68: +/*70*/ case 0x70: +/*78*/ case 0x78: +//* 40 D8 11000nnn FADD st,st(n) +//* 48 D8 11001nnn FMUL st,st(n) +//* 60 D8 11100nnn FSUB st,st(n) +//* 68 D8 11101nnn FSUBR st,st(n) +//* 70 D8 11110nnn FDIV st,st(n) +//* 78 D8 11111nnn FDIVR st,st(n) + +/*50*/ case 0x50: +/*58*/ case 0x58: +//* 50 D8 11010nnn FCOM st,st(n) +//* 58 D8 11011nnn FCOMP st,st(n) + goto fp_op; + +/*6a*/ case 0x6a: if (reg!=1) goto fp_notok; +/*6d*/ case 0x6d: +/*65*/ case 0x65: +// 65 DD 11000nnn FUCOM st(n),st(0) +// 6D DD 11101nnn FUCOMP st(n) +// 6A.1 DA 11101001 FUCOMPP + +// 73 DB 11000nnn FCOMI st(0),st(n) +// 77 DF 11000nnn FCOMIP st(0),st(n) +// 6B DB 11101nnn FUCOMI st(0),st(n) +// 6F DF 11101nnn FUCOMIP st(0),st(n) + goto fp_op; + +/*5e*/ case 0x5e: if (reg==1) { +// 5E.1 DE 11011001 FCOMPP + goto fp_op; + } + else goto fp_notok; + +/*44*/ case 0x44: +/*4c*/ case 0x4c: +/*46*/ case 0x46: +/*4e*/ case 0x4e: +// 44 DC 11000nnn FADD st(n),st +// 4C DC 11001nnn FMUL st(n),st +// 46 DE 11000nnn FADDP st(n),st +// 4E DE 11001nnn FMULP st(n),st + +/*64*/ case 0x64: +/*6c*/ case 0x6c: +/*74*/ case 0x74: +/*7c*/ case 0x7c: +/*66*/ case 0x66: +/*6e*/ case 0x6e: +/*76*/ case 0x76: +/*7e*/ case 0x7e: +// 64 DC 11100nnn FSUBR st(n),st(0) +// 6C DC 11101nnn FSUB st(n),st(0) +// 74 DC 11110nnn FDIVR st(n),st(0) +// 7C DC 11111nnn FDIV st(n),st(0) +// 66 DE 11100nnn FSUBRP st(n),st(0) +// 6E DE 11101nnn FSUBP st(n),st(0) +// 76 DE 11110nnn FDIVRP st(n),st(0) +// 7E DE 11111nnn FDIVP st(n),st(0) + +/*41*/ case 0x41: +//* 41 D9 11000nnn FLD st(n) + goto fp_op; + +/*45*/ case 0x45: goto fp_op; +/*51*/ case 0x51: +/*59*/ case 0x59: + if (reg==0) { +// 45 DD 11000nnn FFREE st(n) set tag(n) empty +//* 51.0 D9 11010000 FNOP + goto fp_op; + } + else goto fp_notok; + break; + +/*49*/ case 0x49: +//* 49 D9 11001nnn FXCH st,st(n) + +/*55*/ case 0x55: +/*5d*/ case 0x5d: +// 55 DD 11010nnn FST st(n) +// 5D DD 11011nnn FSTP st(n) + goto fp_op; + +/*61*/ case 0x61: +//* 61.0 D9 11100000 FCHS +//* 61.1 D9 11100001 FABS +//* 61.4 D9 11100100 FTST +//* 61.5 D9 11100101 FXAM + switch(reg) { + case 0: /* FCHS */ + case 1: /* FABS */ + case 4: /* FTST */ + case 5: /* FXAM */ + goto fp_op; + default: + goto fp_notok; + } + break; + +/*63*/ case 0x63: switch(reg) { +// 63.2* DB 11000010 FCLEX +// 63.3* DB 11000011 FINIT + case 2: /* FCLEX */ + goto fp_op; + case 3: /* FINIT */ + // movw 0x37f,FPUC(ebx) + G3M(0x66,0xc7,0x43,Cp); G1(Ofs_FPUC,Cp); G2(0x37f,Cp); + goto fp_op; + default: /* FNENI,FNDISI: 8087 */ + /* FSETPM,FRSTPM: 80287 */ + goto fp_ok; // do nothing + } + break; + +/*69*/ case 0x69: { +//* 69.0 D9 11101000 FLD1 +//* 69.1 D9 11101001 FLDL2T +//* 69.2 D9 11101010 FLDL2E +//* 69.3 D9 11101011 FLDPI +//* 69.4 D9 11101100 FLDLG2 +//* 69.5 D9 11101101 FLDLN2 +//* 69.6 D9 11101110 FLDZ + if (reg==7) goto fp_notok; + } + goto fp_op; + +/*71*/ case 0x71: +// 71.0 D9 11110000 FX2M1 st(0) +// 71.1 D9 11110001 FYL2X st(1)*l2(st(0))->st(1),pop +// 71.2 D9 11110010 FPTAN st(0),push 1 +// 71.3 D9 11110011 FPATAN st(1)/st(0)->st(1),pop +// 71.4 D9 11110100 FXTRACT exp->st(0),push signif +// 71.5 D9 11110101 FPREM1 st(0)/st(1)->st(0) +// 71.6 D9 11110110 FDECSTP +// 71.7 D9 11110111 FINCSTP + +/*79*/ case 0x79: +// 79.0 D9 11111000 FPREM st(0)/st(1)->st(0) +// 79.1 D9 11111001 FYL2XP1 st(1)*lg(st(0))->st(1),pop +// 79.2 D9 11111010 FSQRT st(0) +// 79.3 D9 11111011 FSINCOS sin->st(0), push cos +// 79.4 D9 11111100 FRNDINT st(0) +// 79.5 D9 11111101 FSCALE st(0) by st(1) +// 79.6 D9 11111110 FSIN st(0) +// 79.7 D9 11111111 FCOS st(0) + fp_op: + G2M(0xd8+(exop&7),0xc0|(exop&0x38)|reg,Cp); // Fop (st(reg)) + break; + +/*xx*/ default: +fp_notok: + return NULL; + } +fp_ok: + return Cp; +} + +static int Fp87_op_x86_sim(int exop, int reg) +{ + e_printf("FPop %x.%d\n", exop, reg); + + switch(exop) { +/*21*/ case 0x21: +/*25*/ case 0x25: { +//* 21 D9 xx100nnn FLDENV 14/28byte +// 25 DD xx100nnn FRSTOR 94/108byte + void *p = LINEAR2UNIX(TheCPU.mem_ref); + if (reg&DATA16) { + struct float_env16 q; + memcpy(&q, p, (exop == 0x21 ? 14 : 94)); + TheCPU.fpuc = q.fpuc; + /* mask exceptions in real FPU control word */ + q.fpuc |= 0x3f; + if (exop==0x21) + __asm__ __volatile__ ("data16 fldenv %0\n" :: "m"(q)); + else + __asm__ __volatile__ ("data16 frstor %0\n" :: "m"(q)); + } + else { + struct float_env32 q; + memcpy(&q, p, (exop == 0x21 ? 28 : 108)); + TheCPU.fpuc = q.fpuc; + /* mask exceptions in real FPU control word */ + q.fpuc |= 0x3f; + if (exop==0x21) + __asm__ __volatile__ ("fldenv %0\n" :: "m"(q)); + else + __asm__ __volatile__ ("frstor %0\n" :: "m"(q)); + } + } + break; + +/* + * FSAVE: 4 modes - followed by FINIT + * + * A) 16-bit real (94 bytes) + * (00-01) Control Word + * (02-03) Status Word + * (04-05) Tag Word + * (06-07) IP 15..00 + * (08-09) IP 19..16,0,Opc 10..00 + * (0a-0b) OP 15..00 + * (0c-0d) OP 19..16,0... + * (0e-5d) FP registers + * + * B) 16-bit protected (94 bytes) + * (00-01) Control Word + * (02-03) Status Word + * (04-05) Tag Word + * (06-07) IP offset + * (08-09) IP selector + * (0a-0b) OP offset + * (0c-0d) OP selector + * (0e-5d) FP registers + * + * C) 32-bit real (108 bytes) + * (00-01) Control Word (02-03) reserved + * (04-05) Status Word (06-07) reserved + * (08-09) Tag Word (0a-0b) reserved + * (0c-0d) IP 15..00 (0e-0f) reserved + * (10-13) 0,0,0,0,IP 31..16,0,Opc 10..00 + * (14-15) OP 15..00 (16-17) reserved + * (18-1b) 0,0,0,0,OP 31..16,0... + * (1c-6b) FP registers + * + * D) 32-bit protected (108 bytes) + * (00-01) Control Word (02-03) reserved + * (04-05) Status Word (06-07) reserved + * (08-09) Tag Word (0a-0b) reserved + * (0c-0d) IP offset (0e-0f) reserved + * (10-13) 0,0,0,0,Opc 10..00,IP selector + * (14-15) OP offset (16-17) reserved + * (18-19) OP selector (1a-1b) reserved + * (1c-6b) FP registers + */ +/*31*/ case 0x31: +/*35*/ case 0x35: { +//* 31 D9 xx110nnn FSTENV 14/28byte +// 35 DD xx110nnn FSAVE 94/108byte + if (exop==0x31) { + struct float_env16 *p = (struct float_env16 *)LINEAR2UNIX(TheCPU.mem_ref); + if (reg&DATA16) + __asm__ __volatile__ ("data16 fnstenv %0\n":"=m"(*p)); + else + __asm__ __volatile__ ("fnstenv %0\n" : "=m"(*p)); + p->fpuc = (p->fpuc & ~0x3f) | (TheCPU.fpuc & 0x3f); + } + else { + struct float_env32 *p = (struct float_env32 *)LINEAR2UNIX(TheCPU.mem_ref); + if (reg&DATA16) + __asm__ __volatile__ ("data16 fnsave %0\n" : "=m"(*p)); + else + __asm__ __volatile__ ("fnsave %0\n" : "=m"(*p)); + p->fpuc = (p->fpuc & ~0x3f) | (TheCPU.fpuc & 0x3f); + } + TheCPU.fpuc |= 0x3f; + if (exop==0x35) { + TheCPU.fpuc = 0x37f; + __asm__ __volatile__ ("fninit"); + } + } + break; + +/*xx*/ default: + return -1; + } + return 0; +} + +#endif diff --git a/src/base/emu-i386/simx86/host.h b/src/base/emu-i386/simx86/host.h new file mode 100644 index 0000000..0736fb1 --- /dev/null +++ b/src/base/emu-i386/simx86/host.h @@ -0,0 +1,220 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originaly was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#ifndef _EMU86_HOST_H +#define _EMU86_HOST_H + +#include "dos2linux.h" +#define read_byte(x) do_read_byte((x), emu_pagefault_handler) +#define read_word(x) do_read_word((x), emu_pagefault_handler) +#define read_dword(x) do_read_dword((x), emu_pagefault_handler) +#define read_qword(x) do_read_qword((x), emu_pagefault_handler) +#define write_byte(x,y) do_write_byte((x), (y), emu_pagefault_handler) +#define write_word(x,y) do_write_word((x), (y), emu_pagefault_handler) +#define write_dword(x,y) do_write_dword((x), (y), emu_pagefault_handler) +#define write_qword(x,y) do_write_qword((x), (y), emu_pagefault_handler) + +#if defined(ppc)||defined(__ppc)||defined(__ppc__) +/* NO PAGING! */ +/* + * $Id$ + */ +/* alas, egcs sounds like it has a bug in this code that doesn't use the + inline asm correctly, and can cause file corruption. */ +static __inline__ unsigned short ppc_pswap2(long addr) +{ + unsigned val; + __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : + "r" ((unsigned short *)addr), "m" (*(unsigned short *)addr)); + return val; +} + +static __inline__ void ppc_dswap2(long addr, unsigned short val) +{ + __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(unsigned short *)addr) : + "r" (val), "r" ((unsigned short *)addr)); +} + +static __inline__ unsigned long ppc_pswap4(long addr) +{ + unsigned val; + __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : + "r" ((unsigned long *)addr), "m" (*(unsigned long *)addr)); + return val; +} + +static __inline__ unsigned long long ppc_pswap8(long addr) +{ + union { unsigned long long lq; struct {unsigned long ll,lh;} lw; } val; + __asm__ __volatile__ (" \ + lwbrx %0,0,%2\n \ + addi %2,%2,4\n \ + lwbrx %1,0,%2" \ + : "=r" (val.lw.lh), "=r" (val.lw.ll) + : "r" ((unsigned long *)addr), "m" (*(unsigned long *)addr) ); + return val.lq; +} + +static __inline__ void ppc_dswap4(long addr, unsigned long val) +{ + __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(unsigned long *)addr) : + "r" (val), "r" ((unsigned long *)addr)); +} + +static __inline__ void ppc_dswap8(long addr, unsigned long long val) +{ + union { unsigned long long lq; struct {unsigned long lh,ll;} lw; } v; + v.lq = val; + __asm__ __volatile__ (" \ + stwbrx %1,0,%3\n \ + addi %3,%3,4\n \ + stwbrx %2,0,%3" \ + : "=m" (*(unsigned long *)addr) + : "r" (v.lw.ll), "r" (v.lw.lh), "r" ((unsigned long *)addr) ); +} + +#endif /* ppc */ + +///////////////////////////////////////////////////////////////////////////// + +#ifdef USE_BOUND +/* `Fetch` is for CODE reads, `Get`/`Put` is for DATA. + * WARNING - BOUND uses SIGNED limits!! */ +#define Fetch(a) ({ \ + register int p = (int)(a);\ + __asm__ ("boundl %0,%1" : : "r"(p),"m"(CS_DTR) : "memory" );\ + *((unsigned char *)p); }) +#define FetchW(a) ({ \ + register int p = (int)(a)+1;\ + __asm__ ("boundl %0,%1" : : "r"(p),"m"(CS_DTR) : "memory" );\ + *((unsigned short *)(a)); }) +#define FetchL(a) ({ \ + register int p = (int)(a)+3;\ + __asm__ ("boundl %0,%1" : : "r"(p),"m"(CS_DTR) : "memory" );\ + *((unsigned int *)(a)); }) + +#define DataFetchWL_U(m,a) ({ \ + register unsigned f = ((m)&DATA16? 1:3);\ + register int p = (int)(a)+f;\ + register int res;\ + __asm__ ("boundl %0,%1" : : "r"(p),"m"(CS_DTR) : "memory" );\ + __asm__ ("xorl %0,%0\n\ + shr $2,%1\n\ + jc 1f\n\ + .byte 0x66\n\ +1: movl (%2),%0"\ + : "=&r"(res) : "r"(f), "g"(a) : "memory" ); res; }) + +#define DataFetchWL_S(m,a) ({ \ + register unsigned f = ((m)&DATA16? 1:3);\ + register int p = (int)(a)+f;\ + __asm__ ("boundl %0,%1" : : "r"(p),"m"(CS_DTR) : "memory" );\ + (f&2? *((int *)(a)):*((short *)(a))); }) + +#define AddrFetchWL_U(m,a) ({ \ + register unsigned f = ((m)&ADDR16? 1:3);\ + register int p = (int)(a)+f;\ + register int res;\ + __asm__ ("boundl %0,%1" : : "r"(p),"m"(CS_DTR) : "memory" );\ + __asm__ ("xorl %0,%0\n\ + shr $2,%1\n\ + jc 1f\n\ + .byte 0x66\n\ +1: movl (%2),%0"\ + : "=&r"(res) : "r"(f), "g"(a) : "memory" ); res; }) + +#define AddrFetchWL_S(m,a) ({ \ + register unsigned f = ((m)&ADDR16? 1:3);\ + register int p = (int)(a)+f;\ + __asm__ ("boundl %0,%1" : : "r"(p),"m"(CS_DTR) : "memory" );\ + (f&2? *((int *)(a)):*((short *)(a))); }) +#else +#define Fetch(a) read_byte(a) +#define FetchW(a) read_word(a) +#define FetchL(a) read_dword(a) +#define DataFetchWL_U(m,a) ((m)&DATA16? FetchW(a):FetchL(a)) +#define DataFetchWL_S(m,a) ((m)&DATA16? (short)FetchW(a):(int)FetchL(a)) +#define AddrFetchWL_U(m,a) ((m)&ADDR16? FetchW(a):FetchL(a)) +#define AddrFetchWL_S(m,a) ((m)&ADDR16? (short)FetchW(a):(int)FetchL(a)) +#endif +#define GetDWord(a) read_word(a) +#define GetDLong(a) read_dword(a) +#define DataGetWL_U(m,a) ((m)&DATA16? GetDWord(a):GetDLong(a)) +#define DataGetWL_S(m,a) ((m)&DATA16? (short)GetDWord(a):(int)GetDLong(a)) + +#if 0 +#if defined(ppc)||defined(__ppc)||defined(__ppc__) +#define Fetch(a) *((unsigned char *)(a)) +#define FetchW(a) ppc_pswap2((int)(a)) +#define FetchL(a) ppc_pswap4((int)(a)) +#define DataFetchWL_U(m,a) ((m)&DATA16? FetchW(a):FetchL(a)) +#define DataFetchWL_S(m,a) ((m)&DATA16? (short)FetchW(a):(int)FetchL(a)) +#define AddrFetchWL_U(m,a) ((m)&ADDR16? FetchW(a):FetchL(a)) +#define AddrFetchWL_S(m,a) ((m)&ADDR16? (short)FetchW(a):(int)FetchL(a)) + +#define GetDWord(a) ppc_pswap2((int)(a)) +#define GetDLong(a) ppc_pswap4((int)(a)) +#define DataGetWL_U(m,a) ((m)&DATA16? GetDWord(a):GetDLong(a)) +#define DataGetWL_S(m,a) ((m)&DATA16? (short)GetDWord(a):(int)GetDLong(a)) +#endif + +/* general-purpose */ +//static inline unsigned short pswap2(long a) { +// register unsigned char *p = (unsigned char *)a; +// return p[0] | (p[1]<<8); +//} +// +//static inline unsigned short dswap2(unsigned short w) { +// register unsigned char *p = (unsigned char *)&w; +// return p[0] | (p[1]<<8); +//} +// +//static inline unsigned long pswap4(long a) { +// register unsigned char *p = (unsigned char *)a; +// return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); +//} +// +//static inline unsigned long dswap4(unsigned long l) { +// register unsigned char *p = (unsigned char *)&l; +// return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); +//} +//#define GetDWord(a) pswap2((long)a) +//#define GetDLong(a) pswap4((long)a) +//#define Fetch(p) *(p) +#endif + + +///////////////////////////////////////////////////////////////////////////// + +#endif diff --git a/src/base/emu-i386/simx86/interp.c b/src/base/emu-i386/simx86/interp.c new file mode 100644 index 0000000..6beff9d --- /dev/null +++ b/src/base/emu-i386/simx86/interp.c @@ -0,0 +1,3513 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#include +#include +#include "emu86.h" +#include "codegen-arch.h" +#include "port.h" +#include "emudpmi.h" +#include "mhpdbg.h" +#include "video.h" + +#if PROFILE +int EmuSignals = 0; +#endif + +/* countdown to exit after handling VGAEMU faults, reset by + planar VGA reads and writes */ +static int interp_inst_emu_count; + +static int ArOpsR[] = + { O_ADD_R, O_OR_R, O_ADC_R, O_SBB_R, O_AND_R, O_SUB_R, O_XOR_R, O_CMP_R }; +static int ArOpsFR[] = + { O_ADD_FR, O_OR_FR, O_ADC_FR, O_SBB_FR, O_AND_FR, O_SUB_FR, O_XOR_FR, O_CMP_FR}; +static char R1Tab_b[8] = + { Ofs_AL, Ofs_CL, Ofs_DL, Ofs_BL, Ofs_AH, Ofs_CH, Ofs_DH, Ofs_BH }; +static char R1Tab_l[14] = + { Ofs_EAX, Ofs_ECX, Ofs_EDX, Ofs_EBX, Ofs_ESP, Ofs_EBP, Ofs_ESI, Ofs_EDI, + Ofs_ES, Ofs_CS, Ofs_SS, Ofs_DS, Ofs_FS, Ofs_GS }; + +#define SEL_B_X(r) R1Tab_b[(r)] + +#define INC_WL_PCA(m,i) PC=(PC+(i)+BT24(BitADDR16, m)) +#define INC_WL_PC(m,i) PC=(PC+(i)+BT24(BitDATA16, m)) + +static __inline__ unsigned long GetCPU_WL(int m, signed char o) +{ + if (m&DATA16) return CPUWORD(o); else return CPULONG(o); +} + +static __inline__ void SetCPU_WL(int m, signed char o, unsigned long v) +{ + if (m&DATA16) CPUWORD(o)=v; else CPULONG(o)=v; +} + +static unsigned int DoCloseAndExec(unsigned int PC, int mode) +{ +#ifdef HOST_ARCH_X86 + if (!CONFIG_CPUSIM) { + unsigned P0 = InstrMeta[0].npc; + if (e_querymark(P0, PC - P0)) + InvalidateNodeRange(P0, PC - P0, NULL); + } +#endif + return CloseAndExec(PC, mode); +} + +/* + * close any pending instruction in the code cache and execute the + * current sequence. + * P0 is the start of current instruction, where the sequence stops + * if there are no jumps at the end. + * P2 is the address of the next instruction to execute. If different + * from P0, abort the current instruction and resume the parsing + * loop at P2. + */ +#ifdef HOST_ARCH_X86 +#define CODE_FLUSH2(m) { if (CONFIG_CPUSIM || CurrIMeta>0) {\ + unsigned int P2 = DoCloseAndExec(P0, m);\ + if (TheCPU.err) return P2;\ + PC = P0 = P2;\ + } NewNode=0; } +#else +#define CODE_FLUSH2(m) { \ + unsigned int P2 = CloseAndExec(P0, m);\ + if (TheCPU.err) return P2;\ + NewNode=0; } +#endif +#ifdef HOST_ARCH_X86 +#define CODE_FLUSH() { if (CONFIG_CPUSIM || CurrIMeta>0) {\ + unsigned int P2 = DoCloseAndExec(P0, _mode);\ + if (TheCPU.err || P0 != P2) return P2;\ + } NewNode=0; } +#else +#define CODE_FLUSH() CODE_FLUSH2(_mode) +#endif + +#define UNPREFIX(m) ((m)&~(DATA16|ADDR16))|(basemode&(DATA16|ADDR16)) + +///////////////////////////////////////////////////////////////////////////// + + +static int _MAKESEG(int mode, int *basemode, int ofs, unsigned short sv) +{ + SDTR tseg, *segc; + int e; + unsigned char big; + +// if ((ofs<0)||(ofs>=0x60)) return EXCP06_ILLOP; + + if (REALADDR()) { + return SetSegReal(sv,ofs); + } + + segc = (SDTR *)CPUOFFS(e_ofsseg[(ofs>>2)]); +// if (segc==NULL) return EXCP06_ILLOP; + + memcpy(&tseg,segc,sizeof(SDTR)); + e = SetSegProt(mode&ADDR16, ofs, &big, sv); + /* must NOT change segreg and LONG_xx if error! */ + if (e) { + memcpy(segc,&tseg,sizeof(SDTR)); + return e; + } + CPUWORD(ofs) = sv; + if (ofs==Ofs_CS) { + if (big) *basemode &= ~(ADDR16|DATA16); + else *basemode |= (ADDR16|DATA16); + if (debug_level('e')>1) e_printf("MAKESEG CS: big=%d basemode=%04x\n",big&1,*basemode); + } + if (ofs==Ofs_SS) { + TheCPU.StackMask = (big? 0xffffffff : 0x0000ffff); + if (debug_level('e')>1) e_printf("MAKESEG SS: big=%d basemode=%04x\n",big&1,*basemode); + } + return 0; +} + +#define MAKESEG(mode, ofs, sv) _MAKESEG(mode, &basemode, ofs, sv) + +///////////////////////////////////////////////////////////////////////////// +// +// jmp b8 j j j j 5a c3 +// link e9 l l l l -- -- +// jcc 7x 06 b8 a a a a 5a c3 b8 b b b b 5a c3 +// link 7x 06 e9 l l l l -- -- e9 l l l l -- -- +// + +static unsigned int _JumpGen(unsigned int P2, int mode, int opc, + int pskip, unsigned int *r_P0) +{ +#if !defined(SINGLESTEP) + unsigned int P1; +#endif + int dsp; + unsigned int d_t, d_nt, j_t, j_nt; + + /* pskip points to start of next instruction + * dsp is the displacement relative to this jump instruction, + * some cases: + * eb 00 dsp=2 jmp to next inst (dsp==pskip) + * eb ff dsp=1 illegal or tricky + * eb fe dsp=0 loop forever + */ + if ((opc>>8) == GRP2wrm || opc == INT) { // indirect jump + dsp = 0; + j_t = 0; + j_nt = 0; + d_t = 0; + d_nt = 0; + } + else if (opc == JMPld || opc == CALLl) { // far jmp/call + d_t = DataFetchWL_U(mode, P2+1); + j_t = SEGOFF2LINEAR(FetchW(P2 + pskip - 2), d_t); + dsp = j_t - P2; + } + else { + dsp = pskip; + if (pskip == 2) // short branch (byte) + dsp += (signed char)Fetch(P2+1); + else // long branch (word/long) + dsp += (int)DataFetchWL_S(mode, + P2+pskip-BT24(BitDATA16,mode)); + + /* displacement for taken branch */ + d_t = P2 - LONG_CS + dsp; + if (mode&DATA16) d_t &= 0xffff; + + /* jump address for taken branch */ + j_t = d_t + LONG_CS; + } + + /* displacement for not taken branch */ + d_nt = P2 - LONG_CS + pskip; + if (mode&DATA16) d_nt &= 0xffff; + + /* jump address for not taken branch, usually next instruction */ + j_nt = d_nt + LONG_CS; + *r_P0 = j_nt; + +#if !defined(SINGLESTEP) + P1 = P2 + pskip; +#endif + switch(opc) { + case JO ... JNLE_JG: + case JCXZ: + if (dsp < 0) mode |= CKSIGN; + /* is there a jump after the condition? if yes, simplify */ +#if !defined(SINGLESTEP) + if (!(EFLAGS & TF)) { + if (Fetch(P1)==JMPsid) { /* eb xx */ + int dsp2 = (signed char)Fetch(P1+1) + 2; + if (dsp2 < 0) mode |= CKSIGN; + d_nt = P1 - LONG_CS + dsp2; + if (mode&DATA16) d_nt &= 0xffff; + j_nt = d_nt + LONG_CS; + if (debug_level('e')>1) + e_printf("JMPs (%02x,%d) at %08x after Jcc: t=%08x nt=%08x\n", + Fetch(P1),dsp2,P1,j_t,j_nt); + } + else if (Fetch(P1)==JMPd) { /* e9 xxxx{xxxx} */ + int skp2 = BT24(BitDATA16,mode) + 1; + int dsp2 = skp2 + (int)DataFetchWL_S(mode, P1+1); + if (dsp2 < 0) mode |= CKSIGN; + d_nt = P1 - LONG_CS + dsp2; + if (mode&DATA16) d_nt &= 0xffff; + j_nt = d_nt + LONG_CS; + if (debug_level('e')>1) + e_printf("JMPl (%02x,%d) at %08x after Jcc: t=%08x nt=%08x\n", + Fetch(P1),dsp2,P1,j_t,j_nt); + } + } +#endif + /* backwards jump limited to 256 bytes */ + if ((dsp > -256) && (dsp < pskip)) { + if (dsp >= 0) { + // dsp>0 && dsp 0) + e_printf("### self jmp=%x dsp=%d pskip=%d\n",opc,dsp,pskip); + else // dsp==0 + /* strange but possible, very tight loop with an external + * condition changing a flag */ + e_printf("### dsp=0 jmp=%x pskip=%d\n",opc,pskip); + } + if (CONFIG_CPUSIM) + Gen(JB_LINK, mode, opc, P2, j_t, j_nt); +#ifdef HOST_ARCH_X86 + else + Gen(JB_LINK, mode, opc, P2, j_t, j_nt, &InstrMeta[0].clink); +#endif + } + else { + if (dsp == pskip) { + e_printf("### jmp %x 00\n",opc); +#if !defined(SINGLESTEP) + if (CONFIG_CPUSIM && !(EFLAGS & TF)) { + TheCPU.mode |= SKIPOP; + TheCPU.eip = d_nt; + return j_nt; + } +#endif + } + /* forward jump or backward jump >=256 bytes */ + if (CONFIG_CPUSIM) + Gen(JF_LINK, mode, opc, P2, j_t, j_nt); +#ifdef HOST_ARCH_X86 + else + Gen(JF_LINK, mode, opc, P2, j_t, j_nt, &InstrMeta[0].clink); +#endif + } + break; + case JMPld: { /* uncond jmp far */ + unsigned short jcs = FetchW(P2 + pskip - 2); + Gen(L_IMM, mode, Ofs_CS, jcs); + AddrGen(A_SR_SH4, mode, Ofs_CS, Ofs_XCS); + } + /* no break */ + case JMPsid: case JMPd: /* uncond jmp */ + if (dsp==0) { // eb fe + dbug_printf("!Forever loop!\n"); + leavedos_main(0xebfe); + } +#if !defined(SINGLESTEP) + if (CONFIG_CPUSIM && !(EFLAGS & TF) && opc != JMPld) { + if (debug_level('e')>1) dbug_printf("** JMP: ignored\n"); + TheCPU.mode |= SKIPOP; + TheCPU.eip = d_t; + return j_t; + } +#endif + if (dsp < 0) mode |= CKSIGN; + if (CONFIG_CPUSIM) + Gen(JMP_LINK, mode, opc, j_t, d_nt); +#ifdef HOST_ARCH_X86 + else + Gen(JMP_LINK, mode, opc, j_t, d_nt, &InstrMeta[0].clink); +#endif + break; + case CALLl: { /* call far */ + unsigned short jcs = FetchW(P2 + pskip - 2); + Gen(L_REG, mode, Ofs_CS); + Gen(O_PUSH, mode); + Gen(L_IMM, mode, Ofs_CS, jcs); + AddrGen(A_SR_SH4, mode, Ofs_CS, Ofs_XCS); + } + /* no break */ + case CALLd: /* call, unfortunately also uses JMP_LINK */ + if (CONFIG_CPUSIM) + Gen(JMP_LINK, mode, opc, j_t, d_nt); +#ifdef HOST_ARCH_X86 + else + Gen(JMP_LINK, mode, opc, j_t, d_nt, &InstrMeta[0].clink); +#endif + break; + case LOOP: case LOOPZ_LOOPE: case LOOPNZ_LOOPNE: + if (dsp == 0) { +#if !defined(SINGLESTEP) + if (CONFIG_CPUSIM && !(EFLAGS & TF)) { + // ndiags: shorten delay loops (e2 fe) + e_printf("### loop %x 0xfe\n",opc); + Gen(L_IMM, ((mode&ADDR16) ? (mode|DATA16) : mode), + Ofs_ECX, 0); + TheCPU.eip = d_nt; + return j_nt; + } +#endif + } + if (CONFIG_CPUSIM) + Gen(JLOOP_LINK, mode, opc, j_t, j_nt); +#ifdef HOST_ARCH_X86 + else + Gen(JLOOP_LINK, mode, opc, j_t, j_nt, &InstrMeta[0].clink); +#endif + break; + case RETl: case RETlisp: case JMPli: case CALLli: // far ret, indirect + case INT: + Gen(S_REG, mode, Ofs_CS); + AddrGen(A_SR_SH4, mode, Ofs_CS, Ofs_XCS); + Gen(L_REG, mode, Ofs_EIP); + /* fall through */ + case RET: case RETisp: case JMPi: case CALLi: // ret, indirect + if (CONFIG_CPUSIM) + Gen(JMP_INDIRECT, mode); +#ifdef HOST_ARCH_X86 + else + Gen(JMP_INDIRECT, mode, &InstrMeta[0].clink); +#endif + break; + default: dbug_printf("JumpGen: unknown condition\n"); + break; + } + + return (unsigned)-1; +} + +#define JumpGen(P2, mode, opc, pskip) ({ \ + unsigned int _P0, _P1; \ + int _rc; \ + _P1 = _JumpGen(P2, mode, opc, pskip, &_P0); \ + if (_P1 == (unsigned)-1) { \ + if (!CONFIG_CPUSIM) \ + NewIMeta(P0, &_rc); \ + _P1 = DoCloseAndExec(_P0, mode); \ + NewNode=0; \ + } \ + _P1; \ +}) + +///////////////////////////////////////////////////////////////////////////// + +#if !defined(SINGLESTEP)&&defined(HOST_ARCH_X86) +static unsigned int FindExecCode(unsigned int PC) +{ + int mode = TheCPU.mode; + TNode *G; + + /* for a sequence to be found, it must begin with + * an allowable opcode. Look into table. + * NOTE - this while can loop forever and stop + * any signal processing. Jumps are defined as + * a 'descheduling point' for checking signals. + */ + while (!(CEmuStat & (CeS_TRAP|CeS_DRTRAP|CeS_SIGPEND)) && + (G=FindTree(PC))) { + if (!GoodNode(G, mode)) { + InvalidateNodeRange(G->seqbase, G->seqlen, NULL); + return PC; + } + if (debug_level('e')>2) + e_printf("** Found compiled code at %08x\n",PC); + if (debug_level('e') && + /* check for codemarks inconsistency */ + e_querymark_all(G->seqbase, G->seqlen) == 0) { + int i, j; + error("no mark at %x (%i)\n", + G->seqbase, + e_querymark(G->seqbase, G->seqlen)); + j = -1; + for (i = 0; i < G->seqlen; i++) { + int mrk = e_querymark(G->seqbase + i, 1); + error("@%i ", mrk); + if (!mrk && j == -1) + j = i; + } + error("@\n"); + if (j != -1) + error("@corrupted at %x\n", G->seqbase + j); + } + /* ---- this is the MAIN EXECUTE point ---- */ + NodesExecd++; +#if PROFILE + TotalNodesExecd++; +#elif !defined(ASM_DUMP) + /* try fast inner loop if nothing special is going on */ + if (!(CEmuStat & (CeS_INHI|CeS_MOVSS)) && + !debug_level('e') && + GoodNode(G, mode) && !(G->flags & (F_FPOP|F_INHI))) + PC = Exec_x86_fast(G); + else +#endif + PC = Exec_x86(G); + if (G->seqlen == 0) { + error("CPU-EMU: Zero-len code node?\n"); + break; + } + if (TheCPU.err) return PC; + } + return PC; +} +#endif + +static void HandleEmuSignals(void) +{ +#if PROFILE + if (debug_level('e')) EmuSignals++; +#endif + if (CEmuStat & CeS_TRAP) { + /* force exit for single step trap */ + if (!TheCPU.err) + TheCPU.err = EXCP01_SSTP; + } + //else if (CEmuStat & CeS_DRTRAP) { + // if (e_debug_check(PC)) { + // TheCPU.err = EXCP01_SSTP; + // } + //} + else if (CEmuStat & CeS_SIGPEND) { + /* force exit after signal */ + CEmuStat = (CEmuStat & ~CeS_SIGPEND) | CeS_SIGACT; + TheCPU.err=EXCP_SIGNAL; + } + else if (CEmuStat & CeS_RPIC) { + /* force exit for PIC */ + CEmuStat &= ~CeS_RPIC; + TheCPU.err=EXCP_PICSIGNAL; + } + else if (CEmuStat & CeS_STI) { + /* force exit for IF set */ + CEmuStat &= ~CeS_STI; + TheCPU.err=EXCP_STISIGNAL; + } + /* clear optional exit conditions */ + CEmuStat &= ~CeS_TRAP; + if (TheCPU.err) + CEmuStat &= ~(CeS_SIGPEND | CeS_RPIC | CeS_STI); +} + +static unsigned int _Interp86(unsigned int PC, int mod0); +static unsigned int InterpOne(unsigned int PC, int *_basemode, int *__mode, + int *_NewNode); + +unsigned int Interp86(unsigned int PC, int mod0) +{ + unsigned int ret; + if (!Running) { + TheCPU.err = -1; + return 0; + } + ret = _Interp86(PC, mod0); + TheCPU.eip = ret - LONG_CS; + return ret; +} + +static unsigned int interp_pre(unsigned int PC, const int mode, int *_NewNode, + unsigned *_P0) +{ +#define NewNode (*_NewNode) +#define P0 (*_P0) + OVERR_DS = Ofs_XDS; + OVERR_SS = Ofs_XSS; + + if (!NewNode) { + if (CEmuStat & (CeS_TRAP|CeS_DRTRAP|CeS_SIGPEND|CeS_RPIC|CeS_STI)) { + HandleEmuSignals(); + if (TheCPU.err) return PC; + } + if (EFLAGS & TF) + CEmuStat |= CeS_TRAP; + } +#ifdef HOST_ARCH_X86 + if (!CONFIG_CPUSIM && e_querymark(PC, 1)) { + unsigned int P2 = PC; + if (NewNode) { + P0 = PC; + CODE_FLUSH2(mode); + } + assert(!NewNode); // don't exec with open node +#ifndef SINGLESTEP + if (!(EFLAGS & TF)) { + P2 = FindExecCode(PC); + if (TheCPU.err) return P2; + if (CEmuStat & (CeS_TRAP|CeS_DRTRAP|CeS_SIGPEND|CeS_RPIC|CeS_STI)) { + HandleEmuSignals(); + if (TheCPU.err) return P2; + if (EFLAGS & TF) + CEmuStat |= CeS_TRAP; + } + } +#endif + if (P2 == PC || e_querymark(P2, 1)) { + /* slow path */ + InvalidateNodeRange(P2, 1, NULL); + } + PC = P2; + } +#if 0 + /* this obviously can't happen with current code, but + * slows down execution under debug a lot */ + if (debug_level('e') && !CONFIG_CPUSIM && e_querymark(PC, 1)) + error("simx86: code nodes clashed at %x\n", PC); +#endif +#endif + P0 = PC; // P0 changes on instruction boundaries + if (!NewNode) { + NewNode = 1; + /* if NewNode was already 1, the registers are outdated */ + if (debug_level('e')==9) dbug_printf("\n%s",e_print_regs()); + } else if (CONFIG_CPUSIM && debug_level('e') == 9) + dbug_printf("\n%s",e_print_regs()); + if (debug_level('e')>2) { + char *ds; + unsigned short ocs = TheCPU.cs; + ds = e_emu_disasm(MEM_BASE32(P0),(~mode&3),ocs); + if (debug_level('e')>2) e_printf(" %s\n", ds); + } + return PC; +} + +static unsigned int interp_post(unsigned int PC, const int mode, + int *_NewNode, unsigned *_P0) +{ +#ifdef HOST_ARCH_X86 + if (NewNode) { + int rc=0; + if (!CONFIG_CPUSIM && !(TheCPU.mode&SKIPOP)) { + NewIMeta(P0, &rc); + if (rc < 0) { + if (debug_level('e')>2) + e_printf("============ Tab full:cannot close sequence\n"); + CODE_FLUSH2(mode); + NewIMeta(P0, &rc); + NewNode = 1; + } + } + } +#endif + +#ifdef SINGLEBLOCK + if (!CONFIG_CPUSIM && NewNode && CurrIMeta > 0) { + P0 = PC; + CODE_FLUSH2(mode); + } +#endif + + if (NewNode && (CEmuStat & CeS_TRAP)) { + P0 = PC; + CODE_FLUSH2(mode); + } + if (CEmuStat & (CeS_MOVSS|CeS_INSTREMU)) { + if (CEmuStat & CeS_MOVSS) { + /* following non-compiled (sim or protected mode) + mov ss / pop ss only */ + if (!(CEmuStat & CeS_INHI)) { + // directly following mov ss / pop ss + CEmuStat |= CeS_INHI; + CEmuStat &= ~CeS_TRAP; + } else { + // instruction after clear unconditionally + // even if it's another mov ss / pop ss + CEmuStat &= ~(CeS_INHI|CeS_MOVSS); + } + } + if ((CEmuStat & (CeS_INSTREMU|CeS_INHI)) == CeS_INSTREMU) { + if (debug_level('e')>1) + dbug_printf("CeS_INSTREMU, count=%d\n", + interp_inst_emu_count); + if (interp_inst_emu_count-- == 0) { + TheCPU.err = EXCP_GOBACK; + return PC; + } + } + } + return PC; +#undef P0 +#undef NewNode +} + +static unsigned int _Interp86(unsigned int PC, int basemode) +{ + volatile unsigned int P0 = PC; /* volatile because of setjmp */ + int mode; + int NewNode; + + if (PROTMODE() && setjmp(jmp_env)) { + /* long jump to here from simulated page fault */ + return P0; + } + + NewNode = 0; + TheCPU.err = 0; + CEmuStat &= ~CeS_TRAP; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" + while (Running) { + TheCPU.mode = mode = basemode; + PC = interp_pre(PC, mode, &NewNode, &P0); + if (TheCPU.err) + return PC; + PC = InterpOne(PC, &basemode, &mode, &NewNode); + if (TheCPU.err) + return PC; + PC = interp_post(PC, mode, &NewNode, &P0); + if (TheCPU.err) + return PC; + } +#pragma GCC diagnostic pop + return 0; +} + +static unsigned int InterpOne(unsigned int PC, int *_basemode, int *__mode, + int *_NewNode) +{ + unsigned int P0 = PC; + unsigned char opc; + unsigned int temp; + unsigned short ocs; + #define _mode (*__mode) + #define basemode (*_basemode) + #define NewNode (*_NewNode) + +override: + switch ((opc=Fetch(PC))) { +/*28*/ case SUBbfrm: +/*2a*/ case SUBbtrm: +/*30*/ case XORbfrm: +/*32*/ case XORbtrm: if (RmIsReg[Fetch(PC+1)]&2) { // same reg + Gen(O_CLEAR, _mode|MBYTE, R1Tab_b[Fetch(PC+1)&7]); + PC+=2; break; + } + if (opc & 2) goto intop3a; else goto intop28; +/*08*/ case ORbfrm: +/*0a*/ case ORbtrm: +/*20*/ case ANDbfrm: +/*22*/ case ANDbtrm: +/*84*/ case TESTbrm: + { int m = _mode | MBYTE; + if (RmIsReg[Fetch(PC+1)]&2) { // same reg + Gen(O_TEST, m, R1Tab_b[Fetch(PC+1)&7]); + PC+=2; break; + } + if (opc != TESTbrm) { + if (opc & 2) goto intop3a; else goto intop28; + } + PC += ModRM(opc, PC, m|MLOAD); // al=[rm] + Gen(O_AND_R, m, REG1); // op al,[ebx+reg] + } + break; +/*18*/ case SBBbfrm: +/*1a*/ case SBBbtrm: if (RmIsReg[Fetch(PC+1)]&2) { // same reg + Gen(O_SBSELF, _mode|MBYTE, R1Tab_b[Fetch(PC+1)&7]); + PC+=2; break; + } + if (opc & 2) goto intop3a; +/*00*/ case ADDbfrm: +/*10*/ case ADCbfrm: +/*38*/ case CMPbfrm: +intop28: { int m = _mode | MBYTE; + PC += ModRM(opc, PC, m); // DI=mem + if (TheCPU.mode & RM_REG) { + int op = ArOpsFR[D_MO(opc)]; + Gen(L_REG, m, REG1); // mov al,[ebx+reg] + Gen(op, m, REG3); // op [ebx+rmreg],al rmreg=rmreg op reg + } + else { + int op = ArOpsR[D_MO(opc)]; + Gen(L_DI_R1, m); // mov al,[edi] + Gen(op, m, REG1); // op al,[ebx+reg] + if (opc!=CMPbfrm) { + Gen(S_DI, m); // mov [edi],al mem=mem op reg + } + } } + break; +/*02*/ case ADDbtrm: +/*12*/ case ADCbtrm: +/*3a*/ case CMPbtrm: +intop3a: { int m = _mode | MBYTE; + int op = ArOpsFR[D_MO(opc)]; + PC += ModRM(opc, PC, m|MLOAD); // al=[rm] + Gen(op, m, REG1); // op [ebx+reg], al + } + break; +/*29*/ case SUBwfrm: +/*2b*/ case SUBwtrm: +/*31*/ case XORwfrm: +/*33*/ case XORwtrm: if (RmIsReg[Fetch(PC+1)]&2) { // same reg + Gen(O_CLEAR, _mode, R1Tab_l[Fetch(PC+1)&7]); + PC+=2; break; + } + if (opc & 2) goto intop3b; else goto intop29; +/*09*/ case ORwfrm: +/*0b*/ case ORwtrm: +/*21*/ case ANDwfrm: +/*23*/ case ANDwtrm: +/*85*/ case TESTwrm: if (RmIsReg[Fetch(PC+1)]&2) { // same reg + Gen(O_TEST, _mode, R1Tab_l[Fetch(PC+1)&7]); + PC+=2; break; + } + if (opc != TESTwrm) { + if (opc & 2) goto intop3b; else goto intop29; + } + PC += ModRM(opc, PC, _mode|MLOAD); // (e)ax=[rm] + Gen(O_AND_R, _mode, REG1); // op (e)ax,[ebx+reg] + break; +/*19*/ case SBBwfrm: +/*1b*/ case SBBwtrm: if (RmIsReg[Fetch(PC+1)]&2) { // same reg + Gen(O_SBSELF, _mode, R1Tab_l[Fetch(PC+1)&7]); + PC+=2; break; + } + if (opc & 2) goto intop3b; +/*01*/ case ADDwfrm: +/*11*/ case ADCwfrm: +/*39*/ case CMPwfrm: +intop29: PC += ModRM(opc, PC, _mode); // DI=mem + if (TheCPU.mode & RM_REG) { + int op = ArOpsFR[D_MO(opc)]; + Gen(L_REG, _mode, REG1); // mov (e)ax,[ebx+reg] + Gen(op, _mode, REG3); // op [ebx+rmreg],(e)ax rmreg=rmreg op reg + } + else { + int op = ArOpsR[D_MO(opc)]; + Gen(L_DI_R1, _mode); // mov (e)ax,[edi] + Gen(op, _mode, REG1); // op (e)ax,[ebx+reg] + if (opc!=CMPwfrm) { + Gen(S_DI, _mode); // mov [edi],(e)ax mem=mem op reg + } + } + break; +/*03*/ case ADDwtrm: +/*13*/ case ADCwtrm: +/*3b*/ case CMPwtrm: +intop3b: { int op = ArOpsFR[D_MO(opc)]; + PC += ModRM(opc, PC, _mode|MLOAD); // (e)ax=[rm] + Gen(op, _mode, REG1); // op [ebx+reg], (e)ax + } + break; +/*a8*/ case TESTbi: { + int m = _mode | MBYTE; + Gen(L_IMM_R1, m, Fetch(PC+1)); PC+=2; // mov al,#imm + Gen(O_AND_R, m, Ofs_AL); // op al,[ebx+reg] + } + break; +/*04*/ case ADDbia: +/*0c*/ case ORbi: +/*14*/ case ADCbi: +/*24*/ case ANDbi: +/*1c*/ case SBBbi: +/*2c*/ case SUBbi: +/*34*/ case XORbi: +/*3c*/ case CMPbi: { + int m = _mode | MBYTE; + int op = ArOpsFR[D_MO(opc)]; + // op [ebx+Ofs_EAX],#imm + Gen(op, m|IMMED, Ofs_EAX, Fetch(PC+1)); PC+=2; + } + break; +/*a9*/ case TESTwi: + Gen(L_IMM_R1, _mode|IMMED, DataFetchWL_U(_mode,PC+1)); + INC_WL_PC(_mode,1); + Gen(O_AND_R, _mode, Ofs_EAX); + break; +/*05*/ case ADDwia: +/*0d*/ case ORwi: +/*15*/ case ADCwi: +/*1d*/ case SBBwi: +/*25*/ case ANDwi: +/*2d*/ case SUBwi: +/*35*/ case XORwi: +/*3d*/ case CMPwi: { + int m = _mode; + int op = ArOpsFR[D_MO(opc)]; + // op [ebx+Ofs_EAX],#imm + Gen(op, m|IMMED, Ofs_EAX, DataFetchWL_U(_mode,PC+1)); + INC_WL_PC(_mode,1); + } + break; +/*69*/ case IMULwrm: + PC += ModRM(opc, PC, _mode|MLOAD); // mov (e)ax,[rm] + Gen(O_IMUL,_mode|IMMED,DataFetchWL_S(_mode,PC),REG1); + INC_WL_PC(_mode, 0); + break; +/*6b*/ case IMULbrm: + PC += ModRM(opc, PC, _mode|MLOAD); // mov (e)ax,[rm] + Gen(O_IMUL,_mode|MBYTE|IMMED,(signed char)Fetch(PC),REG1); + PC++; + break; + +/*9c*/ case PUSHF: { + if (V86MODE() && (IOPL<3)) { + if (CONFIG_CPUSIM) FlagSync_All(); +#ifdef HOST_ARCH_X86 + else CODE_FLUSH(); +#endif + /* virtual-8086 monitor */ + if (!(TheCPU.cr[4] & CR4_VME)) + goto not_permitted; /* GPF */ + temp = (EFLAGS|IOPL_MASK) & RETURN_MASK; + if (EFLAGS & VIF) temp |= EFLAGS_IF; + PUSH(_mode, temp); + if (debug_level('e')>1) + e_printf("Pushed flags %08x fl=%08x\n", + temp,EFLAGS); + } + else { + Gen(O_PUSH2F, _mode); + } + PC++; } + break; +/*9e*/ case SAHF: + Gen(O_SLAHF, _mode, 1); + PC++; break; +/*9f*/ case LAHF: + Gen(O_SLAHF, _mode, 0); + PC++; break; + +/*27*/ case DAA: + Gen(O_OPAX, _mode, 1, DAA); PC++; break; +/*2f*/ case DAS: + Gen(O_OPAX, _mode, 1, DAS); PC++; break; +/*37*/ case AAA: + Gen(O_OPAX, _mode, 1, AAA); PC++; break; +/*3f*/ case AAS: + Gen(O_OPAX, _mode, 1, AAS); PC++; break; +/*d4*/ case AAM: + Gen(O_OPAX, _mode, 2, AAM, Fetch(PC+1)); PC+=2; break; +/*d5*/ case AAD: + Gen(O_OPAX, _mode, 2, AAD, Fetch(PC+1)); PC+=2; break; + +/*d6*/ case 0xd6: /* Undocumented */ + CODE_FLUSH(); + e_printf("Undocumented op 0xd6\n"); + rAL = (EFLAGS & EFLAGS_CF? 0xff:0x00); + PC++; break; +/*62*/ case BOUND: { + signed int lo, hi, r; + CODE_FLUSH(); + if (Fetch(PC+1) >= 0xc0) { + goto not_permitted; + } + PC += ModRMSim(PC, _mode); + r = GetCPU_WL(_mode, REG1); + lo = DataGetWL_S(_mode,TheCPU.mem_ref); + TheCPU.mem_ref += BT24(BitDATA16, _mode); + hi = DataGetWL_S(_mode,TheCPU.mem_ref); + if(r < lo || r > hi) + { + e_printf("Bound interrupt 05\n"); + TheCPU.err=EXCP05_BOUND; + return P0; + } + break; + } +/*63*/ case ARPL: { + unsigned short dest, src; + CODE_FLUSH(); + PC += ModRMSim(PC, _mode); + if (TheCPU.mode & RM_REG) { + dest = CPUWORD(REG3); + } else { + dest = GetDWord(TheCPU.mem_ref); + } + src = GetCPU_WL(_mode, REG1); + if ((dest & 3) < (src & 3)) { + EFLAGS |= EFLAGS_ZF; + dest = (dest & ~3) | (src & 3); + if (TheCPU.mode & RM_REG) { + CPUWORD(REG3) = dest; + } else { + WRITE_WORD(TheCPU.mem_ref, dest); + } + } else { + EFLAGS &= ~EFLAGS_ZF; + } + if (CONFIG_CPUSIM) RFL.valid = V_INVALID; + break; + } +/*d7*/ case XLAT: + Gen(O_XLAT, _mode); + Gen(L_DI_R1, _mode|MBYTE); + Gen(S_REG, _mode|MBYTE, Ofs_AL); + PC++; break; +/*98*/ case CBW: + Gen(O_CBWD, _mode|MBYTE); PC++; break; +/*99*/ case CWD: + Gen(O_CBWD, _mode); PC++; break; + +/*07*/ case POPes: if (REALADDR()) { + Gen(O_POP, _mode); + Gen(S_REG, _mode, Ofs_ES); + AddrGen(A_SR_SH4, _mode, Ofs_ES, Ofs_XES); + } else { /* restartable */ + unsigned short sv = 0; + CODE_FLUSH(); + TOS_WORD(_mode, &sv); + TheCPU.err = MAKESEG(_mode, Ofs_ES, sv); + if (TheCPU.err) return P0; + POP_ONLY(_mode); + TheCPU.es = sv; + } + PC++; + break; +/*17*/ case POPss: if (REALADDR()) { + Gen(O_POP, _mode); + Gen(S_REG, _mode, Ofs_SS); + AddrGen(A_SR_SH4, _mode, Ofs_SS, Ofs_XSS); + } else { /* restartable */ + unsigned short sv = 0; + CODE_FLUSH(); + TOS_WORD(_mode, &sv); + TheCPU.err = MAKESEG(_mode, Ofs_SS, sv); + if (TheCPU.err) return P0; + POP_ONLY(_mode); + TheCPU.ss = sv; + CEmuStat |= CeS_MOVSS; + } + PC++; + break; +/*1f*/ case POPds: if (REALADDR()) { + Gen(O_POP, _mode); + Gen(S_REG, _mode, Ofs_DS); + AddrGen(A_SR_SH4, _mode, Ofs_DS, Ofs_XDS); + } else { /* restartable */ + unsigned short sv = 0; + CODE_FLUSH(); + TOS_WORD(_mode, &sv); + TheCPU.err = MAKESEG(_mode, Ofs_DS, sv); + if (TheCPU.err) return P0; + POP_ONLY(_mode); + TheCPU.ds = sv; + } + PC++; + break; + +/*26*/ case SEGes: + OVERR_DS = OVERR_SS = Ofs_XES; PC++; goto override; +/*2e*/ case SEGcs: /* CS is already checked */ + OVERR_DS = OVERR_SS = Ofs_XCS; PC++; goto override; +/*36*/ case SEGss: /* SS is already checked */ + OVERR_DS = OVERR_SS = Ofs_XSS; PC++; goto override; +/*3e*/ case SEGds: + OVERR_DS = OVERR_SS = Ofs_XDS; PC++; goto override; +/*64*/ case SEGfs: + OVERR_DS = OVERR_SS = Ofs_XFS; PC++; goto override; +/*65*/ case SEGgs: + OVERR_DS = OVERR_SS = Ofs_XGS; PC++; goto override; +/*66*/ case OPERoverride: /* 0x66: 32 bit operand, 16 bit addressing */ + _mode = (_mode & ~DATA16) | (~basemode & DATA16); + if (debug_level('e')>4) + e_printf("OPERoverride: new _mode %04x\n",_mode); + PC++; goto override; +/*67*/ case ADDRoverride: /* 0x67: 16 bit operand, 32 bit addressing */ + _mode = (_mode & ~ADDR16) | (~basemode & ADDR16); + if (debug_level('e')>4) + e_printf("ADDRoverride: new _mode %04x\n",_mode); + PC++; goto override; + +/*f0*/ case LOCK: { int i = 1; unsigned char op; + do { + op = Fetch(PC+i); + i++; + } while (op == SEGcs || op == SEGss || op == SEGds || + op == SEGes || op == SEGfs || op == SEGgs || + op == OPERoverride || op == ADDRoverride); + /* LOCK is allowed on BTS, BTR, BTC, XCHG, ADD...XOR (but not CMP), + INC, DEC, NOT, NEG, XADD -- just ignore LOCK for now... */ + if (op == 0x0f) { + op = Fetch(PC+i); /* BTS/BTR/BTC/XADD/ + CMPXCHG* */ + if (op == 0xab || op == 0xb3 || op == 0xbb || + op == 0xc0 || op == 0xc1 || + op == 0xb0 || op == 0xb1 || + (op == 0xc7 && D_MO(Fetch(PC+i+1)) == 1) || + op == 0xba /* GRP8 - Code Extension 22 */ + ) { + PC++; goto override; + } + } else if (op >= 0xf6 && op < 0xf8) { /*NOT/NEG*/ + op = Fetch(PC+i); + if ((op & 0x30) == 0x10) { + PC++; goto override; + } + } else if (op >= 0xfe) { /*INC/DEC*/ + op = Fetch(PC+i); + if ((op & 0x30) == 0x00) { + PC++; goto override; + } + } + else if ((op < 0x38 && (op & 0x8) < 6) || /*ADD..XOR*/ + (op >= 0x40 && op < 0x50) || /*INC/DEC*/ + (op >= 0x91 && op < 0x98) || + op == 0x86 || op == 0x87) { /*XCHG, not NOP*/ + PC++; goto override; + } + else if (op >= 0x80 && op <= 0x83) { /*ADD..XOR*/ + op = Fetch(PC+i); + if ((op & 0x38) < 0x38) { + PC++; goto override; + } + } + CODE_FLUSH(); + goto illegal_op; + } +/*40*/ case INCax: +/*41*/ case INCcx: +/*42*/ case INCdx: +/*43*/ case INCbx: +/*44*/ case INCsp: +/*45*/ case INCbp: +/*46*/ case INCsi: +/*47*/ case INCdi: + Gen(O_INC_R, _mode, R1Tab_l[D_LO(opc)]); PC++; break; +/*48*/ case DECax: +/*49*/ case DECcx: +/*4a*/ case DECdx: +/*4b*/ case DECbx: +/*4c*/ case DECsp: +/*4d*/ case DECbp: +/*4e*/ case DECsi: +/*4f*/ case DECdi: + Gen(O_DEC_R, _mode, R1Tab_l[D_LO(opc)]); PC++; break; + +/*06*/ case PUSHes: +/*0e*/ case PUSHcs: +/*16*/ case PUSHss: +/*1e*/ case PUSHds: +/*50*/ case PUSHax: +/*51*/ case PUSHcx: +/*52*/ case PUSHdx: +/*53*/ case PUSHbx: +/*54*/ case PUSHsp: +/*55*/ case PUSHbp: +/*56*/ case PUSHsi: +/*57*/ case PUSHdi: opc = OpIsPush[opc]; +#ifndef SINGLESTEP + if (!(EFLAGS & TF)) { + int m = _mode; // enter with prefix + int cnt = 2; + int is_66; + Gen(O_PUSH1, m); + /* optimized multiple register push */ + while (1) { + Gen(O_PUSH2, m, R1Tab_l[opc-1]); + PC++; + opc = OpIsPush[Fetch(PC)]; + is_66 = (Fetch(PC) == 0x66); + if (++cnt >= NUMGENS || (!opc && !is_66) || + e_querymark(PC, 1 + is_66)) + break; + m &= ~DATA16; + if (is_66) { // prefix 0x66 + m |= (~basemode & DATA16); + if ((opc=OpIsPush[Fetch(PC+1)])!=0) PC++; + else break; + } + else { + m |= (basemode & DATA16); + } + } + Gen(O_PUSH3, m); } else +#endif + { + Gen(L_REG, _mode, R1Tab_l[opc-1]); + Gen(O_PUSH, _mode); PC++; + } + break; +/*68*/ case PUSHwi: + Gen(O_PUSHI, _mode, DataFetchWL_U(_mode,(PC+1))); + INC_WL_PC(_mode,1); + break; +/*6a*/ case PUSHbi: + Gen(O_PUSHI, _mode, (signed char)Fetch(PC+1)); PC+=2; break; +/*60*/ case PUSHA: + /* push order: eax ecx edx ebx esp ebp esi edi */ + Gen(O_PUSH1, _mode); + Gen(O_PUSH2, _mode, Ofs_EAX); + Gen(O_PUSH2, _mode, Ofs_ECX); + Gen(O_PUSH2, _mode, Ofs_EDX); + Gen(O_PUSH2, _mode, Ofs_EBX); + Gen(O_PUSH2, _mode, Ofs_ESP); + Gen(O_PUSH2, _mode, Ofs_EBP); + Gen(O_PUSH2, _mode, Ofs_ESI); + Gen(O_PUSH2, _mode, Ofs_EDI); + Gen(O_PUSH3, _mode); PC++; break; +/*61*/ case POPA: + Gen(O_POP1, _mode); + Gen(O_POP2, _mode, Ofs_EDI); + Gen(O_POP2, _mode, Ofs_ESI); + Gen(O_POP2, _mode, Ofs_EBP); + Gen(O_POP2, _mode, Ofs_ESP); // overwritten in O_POP3 + Gen(O_POP2, _mode, Ofs_EBX); + Gen(O_POP2, _mode, Ofs_EDX); + Gen(O_POP2, _mode, Ofs_ECX); + Gen(O_POP2, _mode, Ofs_EAX); + Gen(O_POP3, _mode); PC++; break; + +/*58*/ case POPax: +/*59*/ case POPcx: +/*5a*/ case POPdx: +/*5b*/ case POPbx: +/*5c*/ case POPsp: +/*5d*/ case POPbp: +/*5e*/ case POPsi: +/*5f*/ case POPdi: +#ifndef SINGLESTEP + if (!(EFLAGS & TF)) { + int m = _mode; + int cnt = 2; + Gen(O_POP1, m); + do { + opc = Fetch(PC); + Gen(O_POP2, m, R1Tab_l[D_LO(opc)]); + m = UNPREFIX(m); + PC++; + /* for pop sp reload stack pointer */ + if (opc == POPsp) + Gen(O_POP1, m); + } while (++cnt < NUMGENS && (Fetch(PC)&0xf8)==0x58 && + !e_querymark(PC, 1)); + if (opc!=POPsp) Gen(O_POP3, m); + } else +#endif + { + Gen(O_POP, _mode); + Gen(S_REG, _mode, R1Tab_l[D_LO(opc)]); PC++; + } + break; +/*8f*/ case POPrm: + // now calculate address. This way when using %esp + // as index we use the value AFTER the pop + PC += ModRM(opc, PC, _mode|MPOPRM); + if (TheCPU.mode & RM_REG) { + // pop data into temporary storage and adjust esp + Gen(O_POP, _mode); + // store data + Gen(S_REG, _mode, REG3); + } else { + // read data into temporary storage + Gen(O_POP1, _mode); + Gen(O_POP2, _mode|MPOPRM, 0); + // store data + // S_DI may fault, in which case the instruction + // may need to be restarted with the original + // value of ESP! + Gen(S_DI, _mode); // mov [edi],{e}ax + Gen(O_POP3, _mode|MPOPRM); + } + break; + +/*70*/ case JO: +/*71*/ case JNO: +/*72*/ case JB_JNAE: +/*73*/ case JNB_JAE: +/*74*/ case JE_JZ: +/*75*/ case JNE_JNZ: +/*76*/ case JBE_JNA: +/*77*/ case JNBE_JA: +/*78*/ case JS: +/*79*/ case JNS: +/*7a*/ case JP_JPE: +/*7b*/ case JNP_JPO: +/*7c*/ case JL_JNGE: +/*7d*/ case JNL_JGE: +/*7e*/ case JLE_JNG: +/*7f*/ case JNLE_JG: +/*eb*/ case JMPsid: +/*e0*/ case LOOPNZ_LOOPNE: +/*e1*/ case LOOPZ_LOOPE: +/*e2*/ case LOOP: +/*e3*/ case JCXZ: { + PC = JumpGen(PC, _mode, opc, 2); + if (TheCPU.err) return PC; + } + break; + +/*82*/ case IMMEDbrm2: // add mem8,signed imm8: no AND,OR,XOR +/*80*/ case IMMEDbrm: { + int m = _mode | MBYTE; + int op = D_MO(Fetch(PC+1)); + PC += ModRM(opc, PC, m); + if (TheCPU.mode & RM_REG) { + op = ArOpsFR[op]; + // op [ebx+reg],#imm + Gen(op, m|IMMED, REG3, Fetch(PC)); + } + else { + op = ArOpsR[op]; + Gen(L_DI_R1, m); // mov al,[edi] + Gen(op, m|IMMED, Fetch(PC)); // op al,#imm + if (op!=O_CMP_R) + Gen(S_DI, m); // mov [edi],al mem=mem op #imm + } + PC++; } + break; +/*81*/ case IMMEDwrm: { + int op = D_MO(Fetch(PC+1)); + PC += ModRM(opc, PC, _mode); + if (TheCPU.mode & RM_REG) { + op = ArOpsFR[op]; + // op [ebx+reg],#imm + Gen(op, _mode|IMMED, REG3, DataFetchWL_U(_mode,PC)); + } + else { + op = ArOpsR[op]; + Gen(L_DI_R1, _mode); // mov ax,[edi] + Gen(op, _mode|IMMED, DataFetchWL_U(_mode,PC)); // op ax,#imm + if (op!=O_CMP_R) + Gen(S_DI, _mode);// mov [edi],ax mem=mem op #imm + } + INC_WL_PC(_mode,0); + } + break; +/*83*/ case IMMEDisrm: { + int op = D_MO(Fetch(PC+1)); + long v; + PC += ModRM(opc, PC, _mode); + v = (signed char)Fetch(PC); + if (TheCPU.mode & RM_REG) { + op = ArOpsFR[op]; + // op [ebx+reg],#imm + Gen(op, _mode|IMMED, REG3, v); + } + else { + op = ArOpsR[op]; + Gen(L_DI_R1, _mode); // mov ax,[edi] + Gen(op, _mode|IMMED, v); // op ax,#imm + if (op != O_CMP_R) + Gen(S_DI, _mode);// mov [edi],ax mem=mem op #imm + } + PC++; } + break; +/*86*/ case XCHGbrm: + if (RmIsReg[Fetch(PC+1)]&2) { + Gen(L_NOP, _mode); PC+=2; + } + else { + PC += ModRM(opc, PC, _mode|MBYTE|MLOAD);// al=[rm] + if (TheCPU.mode & RM_REG) { + Gen(O_XCHG, _mode|MBYTE, REG1); + Gen(S_REG, _mode|MBYTE, REG3); + } + else { + Gen(O_XCHG, _mode|MBYTE, REG1); + Gen(S_DI, _mode|MBYTE); + } + } + break; +/*87*/ case XCHGwrm: + if (RmIsReg[Fetch(PC+1)]&2) { + Gen(L_NOP, _mode); PC+=2; + } + else { + PC += ModRM(opc, PC, _mode|MLOAD); // (e)ax=[rm] + if (TheCPU.mode & RM_REG) { + Gen(O_XCHG, _mode, REG1); + Gen(S_REG, _mode, REG3); + } + else { + Gen(O_XCHG, _mode, REG1); + Gen(S_DI, _mode); + } + } + break; +/*88*/ case MOVbfrm: + if (ModGetReg1(PC, MBYTE)==3) { + Gen(L_REG2REG, MBYTE, REG1, REG3); PC+=2; + } else { + Gen(L_REG, _mode|MBYTE, REG1); + PC += ModRM(opc, PC, _mode|MBYTE|MSTORE); // [rm]=al + } + break; +/*89*/ case MOVwfrm: + if (ModGetReg1(PC, _mode)==3) { + Gen(L_REG2REG, _mode, REG1, REG3); PC+=2; + } else { + Gen(L_REG, _mode, REG1); + PC += ModRM(opc, PC, _mode|MSTORE); // [rm]=(e)ax + } + break; +/*8a*/ case MOVbtrm: + if (ModGetReg1(PC, MBYTE)==3) { + Gen(L_REG2REG, MBYTE, REG3, REG1); PC+=2; + } else { + PC += ModRM(opc, PC, _mode|MBYTE|MLOAD); // al=[rm] + Gen(S_REG, _mode|MBYTE, REG1); + } + break; +/*8b*/ case MOVwtrm: + if (ModGetReg1(PC, _mode)==3) { + Gen(L_REG2REG, _mode, REG3, REG1); PC+=2; + } else { + PC += ModRM(opc, PC, _mode|MLOAD); // (e)ax=[rm] + Gen(S_REG, _mode, REG1); + } + break; +/*8c*/ case MOVsrtrm: + PC += ModRM(opc, PC, _mode|SEGREG); + Gen(L_REG, _mode|DATA16, REG1); + //Gen(L_ZXAX, _mode); + if (TheCPU.mode & RM_REG) + Gen(S_REG, _mode|DATA16, REG3); + else + Gen(S_DI, _mode|DATA16); + break; +/*8d*/ case LEA: + if (Fetch(PC+1) >= 0xc0) { + CODE_FLUSH(); + goto not_permitted; + } + PC += ModRM(opc, PC, _mode|MLEA); + Gen(S_DI_R, _mode, REG1); + break; + +/*c4*/ case LES: + if (Fetch(PC+1) >= 0xc0) { + CODE_FLUSH(); + goto not_permitted; + } + if (REALADDR()) { + PC += ModRM(opc, PC, _mode); + Gen(L_LXS1, _mode, REG1); + Gen(L_LXS2, _mode, Ofs_ES, Ofs_XES); + } + else { + unsigned short sv = 0; + unsigned long rv; + CODE_FLUSH(); + PC += ModRMSim(PC, _mode); + rv = DataGetWL_U(_mode,TheCPU.mem_ref); + TheCPU.mem_ref += BT24(BitDATA16, _mode); + sv = GetDWord(TheCPU.mem_ref); + TheCPU.err = MAKESEG(_mode, Ofs_ES, sv); + if (TheCPU.err) return P0; + SetCPU_WL(_mode, REG1, rv); + TheCPU.es = sv; + } + break; +/*c5*/ case LDS: + if (Fetch(PC+1) >= 0xc0) { + CODE_FLUSH(); + goto not_permitted; + } + if (REALADDR()) { + PC += ModRM(opc, PC, _mode); + Gen(L_LXS1, _mode, REG1); + Gen(L_LXS2, _mode, Ofs_DS, Ofs_XDS); + } + else { + unsigned short sv = 0; + unsigned long rv; + CODE_FLUSH(); + PC += ModRMSim(PC, _mode); + rv = DataGetWL_U(_mode,TheCPU.mem_ref); + TheCPU.mem_ref += BT24(BitDATA16, _mode); + sv = GetDWord(TheCPU.mem_ref); + TheCPU.err = MAKESEG(_mode, Ofs_DS, sv); + if (TheCPU.err) return P0; + SetCPU_WL(_mode, REG1, rv); + TheCPU.ds = sv; + } + break; +/*8e*/ case MOVsrfrm: + if (REALADDR()) { + PC += ModRM(opc, PC, _mode|SEGREG|DATA16|MLOAD); + Gen(S_REG, _mode|DATA16, REG1); + AddrGen(A_SR_SH4, _mode, REG1, e_ofsseg[REG1>>2]); + } + else { + unsigned short sv = 0; + CODE_FLUSH(); + PC += ModRMSim(PC, _mode|SEGREG); + if (TheCPU.mode & RM_REG) { + sv = CPUWORD(REG3); + } else { + sv = GetDWord(TheCPU.mem_ref); + } + TheCPU.err = MAKESEG(_mode, REG1, sv); + if (TheCPU.err) return P0; + switch (REG1) { + case Ofs_DS: TheCPU.ds=sv; break; + case Ofs_SS: TheCPU.ss=sv; + CEmuStat |= CeS_MOVSS; + break; + case Ofs_ES: TheCPU.es=sv; break; + case Ofs_FS: TheCPU.fs=sv; break; + case Ofs_GS: TheCPU.gs=sv; break; + default: goto illegal_op; + } + } + break; + +/*9b*/ case oWAIT: +/*90*/ case NOP: //if (!IsCodeInBuf()) Gen(L_NOP, _mode); +#if 1 + Gen(L_NOP, _mode); +#else + TheCPU.mode|=SKIPOP; +#endif + PC++; + if (!(EFLAGS & TF)) + while (Fetch(PC)==NOP) PC++; + break; +/*91*/ case XCHGcx: +/*92*/ case XCHGdx: +/*93*/ case XCHGbx: +/*94*/ case XCHGsp: +/*95*/ case XCHGbp: +/*96*/ case XCHGsi: +/*97*/ case XCHGdi: + Gen(O_XCHG_R, _mode, Ofs_EAX, R1Tab_l[D_LO(opc)]); + PC++; break; + +/*a0*/ case MOVmal: + AddrGen(A_DI_0, _mode|IMMED, OVERR_DS, AddrFetchWL_U(_mode,PC+1)); + Gen(L_DI_R1, _mode|MBYTE); + Gen(S_REG, _mode|MBYTE, Ofs_AL); + INC_WL_PCA(_mode,1); + break; +/*a1*/ case MOVmax: + AddrGen(A_DI_0, _mode|IMMED, OVERR_DS, AddrFetchWL_U(_mode,PC+1)); + Gen(L_DI_R1, _mode); + Gen(S_REG, _mode, Ofs_EAX); + INC_WL_PCA(_mode,1); + break; +/*a2*/ case MOValm: + AddrGen(A_DI_0, _mode|IMMED, OVERR_DS, AddrFetchWL_U(_mode,PC+1)); + Gen(L_REG, _mode|MBYTE, Ofs_AL); + Gen(S_DI, _mode|MBYTE); + INC_WL_PCA(_mode,1); + break; +/*a3*/ case MOVaxm: + AddrGen(A_DI_0, _mode|IMMED, OVERR_DS, AddrFetchWL_U(_mode,PC+1)); + Gen(L_REG, _mode, Ofs_EAX); + Gen(S_DI, _mode); + INC_WL_PCA(_mode,1); + break; + +/*a4*/ case MOVSb: { int m = _mode|(MBYTE|MOVSSRC|MOVSDST); + Gen(O_MOVS_SetA, m&~MOVSDST); + Gen(L_DI_R1, m); + Gen(O_MOVS_SetA, m&~MOVSSRC); + Gen(S_DI, m); + Gen(O_MOVS_SavA, m); + PC++; + } break; +/*a5*/ case MOVSw: { int m = _mode|(MOVSSRC|MOVSDST); + Gen(O_MOVS_SetA, m&~MOVSDST); + Gen(L_DI_R1, m); + Gen(O_MOVS_SetA, m&~MOVSSRC); + Gen(S_DI, m); + PC++; + Gen(O_MOVS_SavA, m); +#ifndef SINGLESTEP + /* optimize common sequence MOVSw..MOVSw..MOVSb */ + if (!(EFLAGS & TF)) { + int cnt = 3; + m = UNPREFIX(m); + while (++cnt < NUMGENS && Fetch(PC) == MOVSw && + !e_querymark(PC, 1)) { + Gen(O_MOVS_SetA, m&~MOVSDST); + Gen(L_DI_R1, m); + Gen(O_MOVS_SetA, m&~MOVSSRC); + Gen(S_DI, m); + PC++; + Gen(O_MOVS_SavA, m); + } + if (Fetch(PC) == MOVSb && !e_querymark(PC, 1)) { + m |= MBYTE; + Gen(O_MOVS_SetA, m&~MOVSDST); + Gen(L_DI_R1, m); + Gen(O_MOVS_SetA, m&~MOVSSRC); + Gen(S_DI, m); + PC++; + Gen(O_MOVS_SavA, m); + } + } +#endif + } break; +/*a6*/ case CMPSb: { int m = _mode|(MBYTE|MOVSSRC|MOVSDST); + Gen(O_MOVS_SetA, m&~MOVSDST); + Gen(L_DI_R1, m); + Gen(O_MOVS_SetA, m&~MOVSSRC); + Gen(O_MOVS_CmpD, m); + Gen(O_MOVS_SavA, m); + PC++; } break; +/*a7*/ case CMPSw: { int m = _mode|(MOVSSRC|MOVSDST); + Gen(O_MOVS_SetA, m&~MOVSDST); + Gen(L_DI_R1, m); + Gen(O_MOVS_SetA, m&~MOVSSRC); + Gen(O_MOVS_CmpD, m); + Gen(O_MOVS_SavA, m); + PC++; } break; +/*aa*/ case STOSb: { int m = _mode|(MBYTE|MOVSDST); + Gen(O_MOVS_SetA, m); + Gen(L_REG, m, Ofs_AL); + Gen(S_DI, m); + Gen(O_MOVS_SavA, m); + PC++; } break; +/*ab*/ case STOSw: { int m = _mode|MOVSDST; + Gen(O_MOVS_SetA, m); + Gen(L_REG, m, Ofs_EAX); + Gen(S_DI, m); PC++; + Gen(O_MOVS_SavA, m); +#ifndef SINGLESTEP + if (!(EFLAGS & TF)) { + int cnt = 3; + m = UNPREFIX(m); + while (++cnt < NUMGENS && Fetch(PC) == STOSw && + !e_querymark(PC, 1)) { + Gen(O_MOVS_SetA, m); + Gen(S_DI, m); + Gen(O_MOVS_SavA, m); + PC++; + } + } +#endif + } break; +/*ac*/ case LODSb: { int m = _mode|(MBYTE|MOVSSRC); + Gen(O_MOVS_SetA, m); + Gen(L_DI_R1, m); + Gen(S_REG, m, Ofs_AL); PC++; +#ifndef SINGLESTEP + /* optimize common sequence LODSb-STOSb */ + if (!(EFLAGS & TF) && Fetch(PC) == STOSb && + !e_querymark(PC, 1)) { + Gen(O_MOVS_SetA, (m&ADDR16)|MOVSDST); + Gen(S_DI, m); + m |= MOVSDST; + PC++; + } +#endif + Gen(O_MOVS_SavA, m); + } break; +/*ad*/ case LODSw: { int m = _mode|MOVSSRC; + Gen(O_MOVS_SetA, m); + Gen(L_DI_R1, m); + Gen(S_REG, m, Ofs_EAX); PC++; +#ifndef SINGLESTEP + /* optimize common sequence LODSw-STOSw */ + if (!(EFLAGS & TF) && Fetch(PC) == STOSw && + !e_querymark(PC, 1)) { + Gen(O_MOVS_SetA, (m&ADDR16)|MOVSDST); + Gen(S_DI, m); + m |= MOVSDST; + PC++; + } +#endif + Gen(O_MOVS_SavA, m); + } break; +/*ae*/ case SCASb: { int m = _mode|(MBYTE|MOVSDST); + Gen(O_MOVS_SetA, m); + Gen(L_DI_R1, m); // mov al,[edi] + Gen(O_CMP_FR, m, Ofs_AL); // cmp [ebx+reg],al + Gen(O_MOVS_SavA, m); + PC++; } break; +/*af*/ case SCASw: { int m = _mode|MOVSDST; + Gen(O_MOVS_SetA, m); + Gen(L_DI_R1, m); // mov ax,[edi] + Gen(O_CMP_FR, m, Ofs_EAX); // cmp [ebx+reg],ax + Gen(O_MOVS_SavA, m); + PC++; } break; + +/*b0*/ case MOVial: +/*b1*/ case MOVicl: +/*b2*/ case MOVidl: +/*b3*/ case MOVibl: +/*b4*/ case MOViah: +/*b5*/ case MOVich: +/*b6*/ case MOVidh: +/*b7*/ case MOVibh: + Gen(L_IMM, _mode|MBYTE, SEL_B_X(D_LO(opc)), Fetch(PC+1)); + PC += 2; break; +/*b8*/ case MOViax: +/*b9*/ case MOVicx: +/*ba*/ case MOVidx: +/*bb*/ case MOVibx: +/*bc*/ case MOVisp: +/*bd*/ case MOVibp: +/*be*/ case MOVisi: +/*bf*/ case MOVidi: + Gen(L_IMM, _mode, R1Tab_l[D_LO(opc)], DataFetchWL_U(_mode,PC+1)); + INC_WL_PC(_mode, 1); break; + +/*d0*/ case SHIFTb: +/*d2*/ case SHIFTbv: +/*c0*/ case SHIFTbi: { + int m = _mode | MBYTE; + unsigned char count = 0; + PC += ModRM(opc, PC, m|MLOAD); + if (opc==SHIFTb) { m |= IMMED; count = 1; } + else if (opc==SHIFTbi) { + m |= IMMED; count = Fetch(PC)&0x1f; PC++; + } + switch (REG1) { + case Ofs_AL: /*0*/ // ROL + Gen(O_ROL, m, count); + break; + case Ofs_CL: /*1*/ // ROR + Gen(O_ROR, m, count); + break; + case Ofs_DL: /*2*/ // RCL + Gen(O_RCL, m, count); + break; + case Ofs_BL: /*3*/ // RCR + Gen(O_RCR, m, count); + break; + case Ofs_DH: /*6*/ // undoc + if (opc==SHIFTbv) { + CODE_FLUSH(); + goto illegal_op; + } + case Ofs_AH: /*4*/ // SHL,SAL + Gen(O_SHL, m, count); + break; + case Ofs_CH: /*5*/ // SHR + Gen(O_SHR, m, count); + break; + case Ofs_BH: /*7*/ // SAR + Gen(O_SAR, m, count); + break; + } + if (TheCPU.mode & RM_REG) + Gen(S_REG, m, REG3); + else + Gen(S_DI, m); + } + break; +/*d1*/ case SHIFTw: +/*d3*/ case SHIFTwv: +/*c1*/ case SHIFTwi: { + int m = _mode; + unsigned char count = 0; + PC += ModRM(opc, PC, m|MLOAD); + if (opc==SHIFTw) { m |= IMMED; count = 1; } + else if (opc==SHIFTwi) { + m |= IMMED; count = Fetch(PC)&0x1f; PC++; + } + switch (REG1) { + case Ofs_AX: /*0*/ // ROL + Gen(O_ROL, m, count); + break; + case Ofs_CX: /*1*/ // ROR + Gen(O_ROR, m, count); + break; + case Ofs_DX: /*2*/ // RCL + Gen(O_RCL, m, count); + break; + case Ofs_BX: /*3*/ // RCR + Gen(O_RCR, m, count); + break; + case Ofs_SI: /*6*/ // undoc + if ((opc==SHIFTw)||(opc==SHIFTwv)) { + CODE_FLUSH(); + goto illegal_op; + } + case Ofs_SP: /*4*/ // SHL,SAL + Gen(O_SHL, m, count); + break; + case Ofs_BP: /*5*/ // SHR + Gen(O_SHR, m, count); + break; + case Ofs_DI: /*7*/ // SAR + Gen(O_SAR, m, count); + break; + } + if (TheCPU.mode & RM_REG) + Gen(S_REG, m, REG3); + else + Gen(S_DI, m); + } + break; + +/*e9*/ case JMPd: +/*e8*/ case CALLd: + PC = JumpGen(PC, _mode, opc, 1 + BT24(BitDATA16,_mode)); + if (TheCPU.err) return PC; + break; + +/*9a*/ case CALLl: +/*ea*/ case JMPld: + if (REALADDR()) { + int len = 3 + BT24(BitDATA16,_mode); + dosaddr_t oip = PC + len - LONG_CS; + ocs = TheCPU.cs; + PC = JumpGen(PC, _mode, opc, len); + if (debug_level('e')>2) { + if (opc==CALLl) + e_printf("CALL_FAR: ret=%04x:%08x\n calling: %04x:%08x\n", + ocs,oip,TheCPU.cs,PC-LONG_CS); + else + e_printf("JMP_FAR: %04x:%08x\n",TheCPU.cs,PC-LONG_CS); + } + if (TheCPU.err) return PC; + } + else { + unsigned short jcs; + unsigned long oip,xcs,jip=0; + CODE_FLUSH(); + /* get new cs:ip */ + jip = DataFetchWL_U(_mode, PC+1); + INC_WL_PC(_mode,1); + jcs = FetchW(PC); + PC+=2; + /* check if new cs is valid, save old for error */ + ocs = TheCPU.cs; + xcs = LONG_CS; + TheCPU.err = MAKESEG(_mode, Ofs_CS, jcs); + if (TheCPU.err) { + TheCPU.cs = ocs; + TheCPU.cs_cache.BoundL = xcs; + // should not change + return P0; + } + if (opc==CALLl) { + /* ok, now push old cs:eip */ + oip = PC - xcs; + PUSH(_mode, ocs); + PUSH(_mode, oip); + if (debug_level('e')>2) + e_printf("CALL_FAR: ret=%04x:%08lx\n calling: %04x:%08lx\n", + ocs,oip,jcs,jip); + } + else { + if (debug_level('e')>2) + e_printf("JMP_FAR: %04x:%08lx\n",jcs,jip); + } + TheCPU.eip = jip; + PC = LONG_CS + jip; +#ifdef SKIP_EMU_VBIOS + if ((jcs&0xf000)==config.vbios_seg) { + /* return the new PC after the jump */ + TheCPU.err = EXCP_GOBACK; return PC; + } +#endif + } + break; + +/*c2*/ case RETisp: { + int dr = (signed short)FetchW(PC+1); + Gen(O_POP, _mode|MRETISP, dr); + PC = JumpGen(PC, _mode, opc, 3); + if (debug_level('e')>2) + e_printf("RET: ret=%08x inc_sp=%d\n",PC-LONG_CS,dr); + if (TheCPU.err) return PC; } + break; +/*c3*/ case RET: + Gen(O_POP, _mode); + PC = JumpGen(PC, _mode, opc, 1); + if (debug_level('e')>2) e_printf("RET: ret=%08x\n",PC-LONG_CS); + if (TheCPU.err) return PC; + break; +/*c6*/ case MOVbirm: + PC += ModRM(opc, PC, _mode|MBYTE); + if (TheCPU.mode & RM_REG) + Gen(L_IMM, _mode|MBYTE, REG3, Fetch(PC)); + else + Gen(S_DI_IMM, _mode|MBYTE, Fetch(PC)); + PC++; break; +/*c7*/ case MOVwirm: + PC += ModRM(opc, PC, _mode); + if (TheCPU.mode & RM_REG) + Gen(L_IMM, _mode, REG3, DataFetchWL_U(_mode,PC)); + else + Gen(S_DI_IMM, _mode, DataFetchWL_U(_mode,PC)); + INC_WL_PC(_mode,0); + break; +/*c8*/ case ENTER: { + unsigned int sp, bp, frm; + int level, ds; + level = Fetch(PC+3) & 0x1f; + if (level <= 1) { + int allocsize = FetchW(PC+1); + Gen(L_REG, _mode, Ofs_EBP); + Gen(O_PUSH, _mode); + if (level == 1) { + Gen(L_REG, _mode, Ofs_ESP); + Gen(O_PUSH, _mode); + Gen(S_REG, _mode, Ofs_EBP); + } + else { + Gen(L_REG2REG, _mode, Ofs_ESP, Ofs_EBP); + } + // subtract AllocSize from ESP via + // "lea -allocsize(%esp), %esp" + if (allocsize) { + AddrGen(A_DI_1, + _mode|MLEA|((_mode&DATA16)?ADDR16:0)|IMMED, + 0, -allocsize, Ofs_ESP); + Gen(S_DI_R, _mode, Ofs_ESP); + } + } + else { + CODE_FLUSH(); + ds = BT24(BitDATA16, _mode); + sp = LONG_SS + ((rESP - ds) & TheCPU.StackMask); + bp = LONG_SS + (rEBP & TheCPU.StackMask); + PUSH(_mode, rEBP); + frm = sp - LONG_SS; + sp -= ds*level; + while (--level) { + bp -= ds; + PUSH(_mode, (_mode&DATA16) ? + READ_WORD(bp) : READ_DWORD(bp)); + } + PUSH(_mode, frm); + if (_mode&DATA16) rBP = frm; else rEBP = frm; + sp -= FetchW(PC+1); + temp = sp - LONG_SS; + rESP = (temp&TheCPU.StackMask) | (rESP&~TheCPU.StackMask); + } + PC += 4; } + break; +/*c9*/ case LEAVE: + Gen(O_LEAVE, _mode); PC++; + break; +/*ca*/ case RETlisp: + if (REALADDR()) { + int dr = (signed short)FetchW(PC+1); + Gen(O_POP, _mode); + Gen(S_REG, _mode, Ofs_EIP); + Gen(O_POP, _mode|MRETISP, dr); + PC = JumpGen(PC, _mode, opc, 3); + if (debug_level('e')>2) + e_printf("RET_%d: ret=%08x\n",dr,TheCPU.eip); + if (TheCPU.err) return PC; + } + else { /* restartable */ + unsigned long dr; + uint16_t sv=0; + CODE_FLUSH(); + NOS_WORD(_mode, &sv); + dr = AddrFetchWL_U(_mode,PC+1); + TheCPU.err = MAKESEG(_mode, Ofs_CS, sv); + if (TheCPU.err) return P0; + TheCPU.eip=0; POP(_mode, &TheCPU.eip); + POP_ONLY(_mode); + if (debug_level('e')>2) + e_printf("RET_%ld: ret=%08x\n",dr,TheCPU.eip); + PC = LONG_CS + TheCPU.eip; + temp = rESP + dr; + rESP = (temp&TheCPU.StackMask) | (rESP&~TheCPU.StackMask); + } + break; +/*cc*/ case INT3: + CODE_FLUSH(); + e_printf("Interrupt 03\n"); + TheCPU.err=EXCP03_INT3; PC++; + return PC; +/*ce*/ case INTO: + CODE_FLUSH(); + if (CONFIG_CPUSIM) FlagSync_O(); + PC++; + if(EFLAGS & EFLAGS_OF) + { + e_printf("Overflow interrupt 04\n"); + TheCPU.err=EXCP04_INTO; + return PC; + } + break; +/*cd*/ case INT: { + int inum = Fetch(PC+1); +#ifdef ASM_DUMP + fprintf(aLog,"%08x:\t\tint %02x\n", P0, inum); +#endif + CEmuStat &= ~CeS_TRAP; // INT suppresses trap + if (V86MODE() && (TheCPU.cr[4] & CR4_VME) && IOPL == 3) { + Gen(O_INT, _mode, inum, P0); + if (TheCPU.err) return P0; + Gen(O_PUSH2F, _mode); + Gen(L_REG, _mode, Ofs_CS); + Gen(O_PUSH, _mode); + Gen(O_PUSHI, _mode, PC + 2 - LONG_CS); + Gen(O_SETFL, _mode, INT); + Gen(L_LXS1, _mode, Ofs_EIP); + Gen(L_DI_R1, _mode); + PC = JumpGen(PC, _mode, opc, 2); + if (debug_level('e')>1) + dbug_printf("EMU86: directly called int %#x ax=%#x at %#x:%#x\n", + inum, TheCPU.eax, TheCPU.cs, PC - LONG_CS); + break; + } + CODE_FLUSH(); + // V86: always #GP(0) if revectored or without VME + if (V86MODE() && !(TheCPU.cr[4] & CR4_VME) && IOPL<3) + goto not_permitted; + if (V86MODE() && (TheCPU.cr[4] & CR4_VME) && + !test_bit(inum, &vm86s.int_revectored)) { + uint32_t segoffs; + segoffs = read_dword(inum << 2); + if (CONFIG_CPUSIM) FlagSync_All(); + temp = (EFLAGS|IOPL_MASK) & (RETURN_MASK|EFLAGS_IF); + if (IOPL<3) { + temp &= ~EFLAGS_IF; + if (EFLAGS & VIF) + temp |= EFLAGS_IF; + } + PUSH(_mode, temp); + PUSH(_mode, TheCPU.cs); + PUSH(_mode, PC + 2 - LONG_CS); + TheCPU.err = MAKESEG(_mode, Ofs_CS, segoffs >> 16); + if (TheCPU.err) return P0; + TheCPU.eip = segoffs & 0xffff; + PC = LONG_CS + TheCPU.eip; + EFLAGS &= ~(TF|RF|AC|NT); + EFLAGS &= ~(IOPL==3 ? EFLAGS_IF : EFLAGS_VIF); + if (debug_level('e')>1) + dbug_printf("EMU86: directly calling int %#x ax=%#x at %#x:%#x\n", + inum, _AX, _CS, _IP); + break; + } + /* protected _mode INT or revectored V86 with IOPL=3 */ + if (PROTMODE()) switch(inum) { + case 0x03: + TheCPU.err=EXCP03_INT3; + PC += 2; + return PC; + case 0x04: + TheCPU.err=EXCP04_INTO; + PC += 2; + return PC; + } + TheCPU.scp_err = (inum << 3) | 2; + goto not_permitted; + break; + } + +/*cb*/ case RETl: + if (REALADDR()) { + Gen(O_POP, _mode); + Gen(S_REG, _mode, Ofs_EIP); + Gen(O_POP, _mode); + PC = JumpGen(PC, _mode, opc, 1); + if (debug_level('e')>1) + e_printf("RET_FAR: ret=%04x:%08x\n",TheCPU.cs,TheCPU.eip); + if (TheCPU.err) return PC; + break; /* un-fall */ + } + /* fall through */ +/*cf*/ case IRET: { /* restartable */ + uint16_t sv=0; + int m = _mode; + CODE_FLUSH(); + NOS_WORD(m, &sv); /* get segment */ + TheCPU.err = MAKESEG(m, Ofs_CS, sv); + if (TheCPU.err) return P0; + TheCPU.eip=0; POP(m, &TheCPU.eip); + POP_ONLY(m); + PC = LONG_CS + TheCPU.eip; + if (opc==RETl) { + if (debug_level('e')>1) + e_printf("RET_FAR: ret=%04x:%08x\n",sv,TheCPU.eip); + break; /* un-fall */ + } + if (debug_level('e')>1) { + e_printf("IRET: ret=%04x:%08x\n",sv,TheCPU.eip); + } + temp=0; POP(m, &temp); + if (CONFIG_CPUSIM) RFL.valid = V_INVALID; + if (REALMODE()) + FLAGS = temp; + else if (V86MODE()) { + goto stack_return_from_vm86; + } + else { + /* if (EFLAGS&EFLAGS_NT) goto task_return */ + /* if (temp&EFLAGS_VM) goto stack_return_to_vm86 */ + /* else stack_return */ + int amask = (CPL==0? 0:(EFLAGS_IOPL_MASK|VIF|VIP)) | + (CPL<=IOPL? 0:EFLAGS_IF) | 2; + if (_mode & DATA16) + FLAGS = (FLAGS&amask) | ((temp&0x7fd7)&~amask); + else /* should use eTSSMASK */ + EFLAGS = (EFLAGS&amask) | + ((temp&(eTSSMASK|0xfd7))&~amask); + TheCPU.df_increments = (EFLAGS&DF)?0xfcfeff:0x040201; + if (debug_level('e')>1) + e_printf("Popped flags %08x->{r=%08x v=%08x}\n",temp,EFLAGS,get_FLAGS(EFLAGS)); + } + } break; + +/*9d*/ case POPF: { + CODE_FLUSH(); + temp=0; POP(_mode, &temp); + if (CONFIG_CPUSIM) RFL.valid = V_INVALID; + if (V86MODE()) { + int is_tf; +stack_return_from_vm86: + if (debug_level('e')>1) + e_printf("Popped flags %08x fl=%08x\n", + temp,EFLAGS); + is_tf = !!(EFLAGS & TF); + if (IOPL==3) { /* Intel manual */ + /* keep reserved bits + IOPL,VIP,VIF,VM,RF */ + if (_mode & DATA16) + FLAGS &= ~(SAFE_MASK|EFLAGS_IF); + else + EFLAGS &= ~(SAFE_MASK|EFLAGS_IF); + EFLAGS |= temp & (SAFE_MASK|EFLAGS_IF); + } + else { + /* virtual-8086 monitor */ + if (!(TheCPU.cr[4] & CR4_VME)) + goto not_permitted; /* GPF */ + /* move mask from pop{e}flags to regs->eflags */ + if (_mode & DATA16) + FLAGS &= ~SAFE_MASK; + else + EFLAGS &= ~SAFE_MASK; + EFLAGS |= temp & SAFE_MASK; + if (temp & EFLAGS_IF) + EFLAGS |= EFLAGS_VIF; + } + if (temp & EFLAGS_IF) { + if (vm86s.regs.eflags & VIP) { + if (debug_level('e')>1) + e_printf("Return for STI fl=%08x\n", + EFLAGS); + TheCPU.err = (is_tf ? EXCP01_SSTP : EXCP_STISIGNAL); + return PC + (opc==POPF); + } + } + } + else { + int amask = (CPL==0? 0:EFLAGS_IOPL_MASK) | + (CPL<=IOPL? 0:EFLAGS_IF) | + (EFLAGS_VM|EFLAGS_RF) | 2; + if (_mode & DATA16) + FLAGS = (FLAGS&amask) | ((temp&0x7fd7)&~amask); + else + EFLAGS = (EFLAGS&amask) | + ((temp&(eTSSMASK|0xfd7))&~amask); + // unused "extended PVI" since real PVI does not + // affect POPF + if (IOPL<3 && (TheCPU.cr[4]&CR4_PVI)) { + if (temp & EFLAGS_IF) + EFLAGS |= EFLAGS_VIF; + else + EFLAGS &= ~EFLAGS_VIF; + } + if (debug_level('e')>1) + e_printf("Popped flags %08x->{r=%08x v=%08x}\n",temp,EFLAGS,_EFLAGS); + } + TheCPU.df_increments = (EFLAGS&DF)?0xfcfeff:0x040201; + if (opc==POPF) PC++; } + break; + +/*f2*/ case REPNE: +/*f3*/ case REP: /* also is REPE */ { + unsigned char repop; int repmod, realrepmod; + PC++; + repmod = _mode | (opc==REPNE? MREPNE:MREP); +repag0: + realrepmod = repmod; + if ((EFLAGS & TF) && + ((repmod & ADDR16) ? rCX : rECX) > 0) { + /* simulate rep below with TF set, because + we must trap for every string ins */ + repmod = repmod & ~(MREPNE|MREP); + } + repop = Fetch(PC); + switch (repop) { + case INSb: + case INSw: + case OUTSb: + case OUTSw: + CODE_FLUSH(); + goto not_permitted; + case LODSb: + repmod |= (MBYTE|MOVSSRC); + Gen(O_MOVS_SetA, repmod); + if (repmod & (MREPNE|MREP)) { + Gen(O_MOVS_LodD, repmod); + } + else { + Gen(L_DI_R1, repmod); + } + Gen(S_REG, repmod, Ofs_AL); + Gen(O_MOVS_SavA, repmod); + PC++; break; + case LODSw: + repmod |= MOVSSRC; + Gen(O_MOVS_SetA, repmod); + if (repmod & (MREPNE|MREP)) { + Gen(O_MOVS_LodD, repmod); + } + else { + Gen(L_DI_R1, repmod); + } + Gen(S_REG, repmod, Ofs_AX); + Gen(O_MOVS_SavA, repmod); + PC++; break; + case MOVSb: + repmod |= (MBYTE|MOVSSRC|MOVSDST); + if (repmod & (MREPNE|MREP)) { + Gen(O_MOVS_SetA, repmod); + Gen(O_MOVS_MovD, repmod); + } + else { + Gen(O_MOVS_SetA, repmod&~MOVSDST); + Gen(L_DI_R1, repmod); + Gen(O_MOVS_SetA, repmod&~MOVSSRC); + Gen(S_DI, repmod); + } + Gen(O_MOVS_SavA, repmod); + PC++; break; + case MOVSw: + repmod |= (MOVSSRC|MOVSDST); + if (repmod & (MREPNE|MREP)) { + Gen(O_MOVS_SetA, repmod); + Gen(O_MOVS_MovD, repmod); + } + else { + Gen(O_MOVS_SetA, repmod&~MOVSDST); + Gen(L_DI_R1, repmod); + Gen(O_MOVS_SetA, repmod&~MOVSSRC); + Gen(S_DI, repmod); + } + Gen(O_MOVS_SavA, repmod); + PC++; break; + case CMPSb: + repmod |= (MBYTE|MOVSSRC|MOVSDST|MREPCOND); + if (repmod & (MREPNE|MREP)) { + Gen(O_MOVS_SetA, repmod); + } + else { + Gen(O_MOVS_SetA, repmod&~MOVSDST); + Gen(L_DI_R1, repmod); + Gen(O_MOVS_SetA, repmod&~MOVSSRC); + } + Gen(O_MOVS_CmpD, repmod); + Gen(O_MOVS_SavA, repmod); + PC++; break; + case CMPSw: + repmod |= (MOVSSRC|MOVSDST|MREPCOND); + if (repmod & (MREPNE|MREP)) { + Gen(O_MOVS_SetA, repmod); + } + else { + Gen(O_MOVS_SetA, repmod&~MOVSDST); + Gen(L_DI_R1, repmod); + Gen(O_MOVS_SetA, repmod&~MOVSSRC); + } + Gen(O_MOVS_CmpD, repmod); + Gen(O_MOVS_SavA, repmod); + PC++; break; + case STOSb: + repmod |= (MBYTE|MOVSDST); + Gen(O_MOVS_SetA, repmod); + Gen(L_REG, repmod|MBYTE, Ofs_AL); + if (repmod & (MREPNE|MREP)) { + Gen(O_MOVS_StoD, repmod); + } + else { + Gen(S_DI, repmod); + } + Gen(O_MOVS_SavA, repmod); + PC++; break; + case STOSw: + repmod |= MOVSDST; + Gen(O_MOVS_SetA, repmod); + Gen(L_REG, repmod, Ofs_EAX); + if (repmod & (MREPNE|MREP)) { + Gen(O_MOVS_StoD, repmod); + } + else { + Gen(S_DI, repmod); + } + Gen(O_MOVS_SavA, repmod); + PC++; break; + case SCASb: + repmod |= (MBYTE|MOVSDST|MREPCOND); + Gen(O_MOVS_SetA, repmod); + Gen(L_REG, repmod|MBYTE, Ofs_AL); + if (repmod & (MREPNE|MREP)) { + Gen(O_MOVS_ScaD, repmod); + } + else { + Gen(L_DI_R1, repmod); + Gen(O_CMP_FR, repmod, Ofs_AL); + } + Gen(O_MOVS_SavA, repmod); + PC++; break; + case SCASw: + repmod |= MOVSDST|MREPCOND; + Gen(O_MOVS_SetA, repmod); + Gen(L_REG, repmod, Ofs_EAX); + if (repmod & (MREPNE|MREP)) { + Gen(O_MOVS_ScaD, repmod); + } + else { + Gen(L_DI_R1, repmod); + Gen(O_CMP_FR, repmod, Ofs_AL); + } + Gen(O_MOVS_SavA, repmod); + PC++; break; + case SEGes: + OVERR_DS = OVERR_SS = Ofs_XES; PC++; goto repag0; + case SEGcs: + OVERR_DS = OVERR_SS = Ofs_XCS; PC++; goto repag0; + case SEGss: + OVERR_DS = OVERR_SS = Ofs_XSS; PC++; goto repag0; + case SEGds: + OVERR_DS = OVERR_SS = Ofs_XDS; PC++; goto repag0; + case SEGfs: + OVERR_DS = OVERR_SS = Ofs_XFS; PC++; goto repag0; + case SEGgs: + OVERR_DS = OVERR_SS = Ofs_XGS; PC++; goto repag0; + case OPERoverride: + repmod = (repmod & ~DATA16) | (~basemode & DATA16); + if (debug_level('e')>4) + e_printf("OPERoverride: new _mode %04x\n",repmod); + PC++; goto repag0; + case ADDRoverride: + repmod = (repmod & ~ADDR16) | (~basemode & ADDR16); + if (debug_level('e')>4) + e_printf("ADDRoverride: new _mode %04x\n",repmod); + PC++; goto repag0; + } + if ((EFLAGS & TF) && !(repmod & (MREP|MREPNE))) { + /* with TF set, we simulate REP and maybe back + up IP */ +#ifdef HOST_ARCH_X86 + int rc = 0; + if (!CONFIG_CPUSIM) { + NewIMeta(P0, &rc); + CODE_FLUSH(); + /* don't cache intermediate nodes */ + InvalidateNodeRange(P0, PC - P0, NULL); + } +#endif + if (CONFIG_CPUSIM) FlagSync_All(); + if (repmod & ADDR16) { + rCX--; + if (rCX == 0) break; + } else { + rECX--; + if (rECX == 0) break; + } + if ((repop != CMPSb && repop != CMPSw && + repop != SCASb && repop != SCASw) || + ((realrepmod&MREP?1:0)==(EFLAGS&ZF?1:0))) { + PC = P0; + break; + } + } } + break; +/*f4*/ case HLT: + CODE_FLUSH(); + goto not_permitted; +/*f5*/ case CMC: PC++; +#if 0 + if ((CurrIMeta<0)&&(InterOps[Fetch(PC)]&1)) + EFLAGS ^= EFLAGS_CF; + else +#endif + Gen(O_SETFL, _mode, CMC); + break; +/*f6*/ case GRP1brm: { + PC += ModRM(opc, PC, _mode|MBYTE|MLOAD); // al=[rm] + switch(REG1) { + case Ofs_AL: /*0*/ /* TEST */ + case Ofs_CL: /*1*/ /* undocumented */ + Gen(O_AND_R, _mode|MBYTE|IMMED, Fetch(PC)); // op al,#imm + PC++; + break; + case Ofs_DL: /*2*/ /* NOT */ + Gen(O_NOT, _mode|MBYTE); // not al + if (TheCPU.mode & RM_REG) + Gen(S_REG, _mode|MBYTE, REG3); + else + Gen(S_DI, _mode|MBYTE); + break; + case Ofs_BL: /*3*/ /* NEG */ + Gen(O_NEG, _mode|MBYTE); // neg al + if (TheCPU.mode & RM_REG) + Gen(S_REG, _mode|MBYTE, REG3); + else + Gen(S_DI, _mode|MBYTE); + break; + case Ofs_AH: /*4*/ /* MUL AL */ + Gen(O_MUL, _mode|MBYTE); // al*[edi]->AX unsigned + break; + case Ofs_CH: /*5*/ /* IMUL AL */ + Gen(O_IMUL, _mode|MBYTE); // al*[edi]->AX signed + break; + case Ofs_DH: /*6*/ /* DIV AL */ + Gen(O_DIV, _mode|MBYTE, P0); // ah:al/[edi]->AH:AL unsigned + if (CONFIG_CPUSIM && TheCPU.err) return P0; + break; + case Ofs_BH: /*7*/ /* IDIV AL */ + Gen(O_IDIV, _mode|MBYTE, P0); // ah:al/[edi]->AH:AL signed + if (CONFIG_CPUSIM && TheCPU.err) return P0; + break; + } } + break; +/*f7*/ case GRP1wrm: { + PC += ModRM(opc, PC, _mode|MLOAD); // (e)ax=[rm] + switch(REG1) { + case Ofs_AX: /*0*/ /* TEST */ + case Ofs_CX: /*1*/ /* undocumented */ + Gen(O_AND_R, _mode|IMMED, DataFetchWL_U(_mode,PC)); // op al,#imm + INC_WL_PC(_mode,0); + break; + case Ofs_DX: /*2*/ /* NOT */ + Gen(O_NOT, _mode); // not (e)ax + if (TheCPU.mode & RM_REG) + Gen(S_REG, _mode, REG3); + else + Gen(S_DI, _mode); + break; + case Ofs_BX: /*3*/ /* NEG */ + Gen(O_NEG, _mode); // neg (e)ax + if (TheCPU.mode & RM_REG) + Gen(S_REG, _mode, REG3); + else + Gen(S_DI, _mode); + break; + case Ofs_SP: /*4*/ /* MUL AX */ + Gen(O_MUL, _mode); // (e)ax*[edi]->(E)DX:(E)AX unsigned + break; + case Ofs_BP: /*5*/ /* IMUL AX */ + Gen(O_IMUL, _mode); // (e)ax*[edi]->(E)DX:(E)AX signed + break; + case Ofs_SI: /*6*/ /* DIV AX+DX */ + Gen(O_DIV, _mode, P0); // (e)ax:(e)dx/[edi]->(E)AX:(E)DX unsigned + if (CONFIG_CPUSIM && TheCPU.err) return P0; + break; + case Ofs_DI: /*7*/ /* IDIV AX+DX */ + Gen(O_IDIV, _mode, P0); // (e)ax:(e)dx/[edi]->(E)AX:(E)DX signed + if (CONFIG_CPUSIM && TheCPU.err) return P0; + break; + } } + break; +/*f8*/ case CLC: PC++; +#if 0 + if ((CurrIMeta<0)&&(InterOps[Fetch(PC)]&1)) + EFLAGS &= ~EFLAGS_CF; + else +#endif + Gen(O_SETFL, _mode, CLC); + break; +/*f9*/ case STC: PC++; +#if 0 + if ((CurrIMeta<0)&&(InterOps[Fetch(PC)]&1)) + EFLAGS |= EFLAGS_CF; + else +#endif + Gen(O_SETFL, _mode, STC); + break; +/*fa*/ case CLI: + if (REALMODE() || (CPL <= IOPL) || (IOPL==3)) { + Gen(O_SETFL, _mode, CLI); + } + else { + CODE_FLUSH(); + /* virtual-8086 monitor */ + if (V86MODE()) { + if (debug_level('e')>2) e_printf("Virtual VM86 CLI\n"); + if (TheCPU.cr[4] & CR4_VME) + EFLAGS &= ~EFLAGS_VIF; + else + goto not_permitted; /* GPF */ + } + else if (TheCPU.cr[4] & CR4_PVI) { + if (debug_level('e')>2) e_printf("Virtual DPMI CLI\n"); + EFLAGS &= ~EFLAGS_VIF; + } + else + goto not_permitted; /* GPF */ + } + PC++; + break; +/*fb*/ case STI: + CODE_FLUSH(); + if (V86MODE()) { /* traps always (Intel man) */ + /* virtual-8086 monitor */ + if (debug_level('e')>2) e_printf("Virtual VM86 STI\n"); + if (IOPL==3) + EFLAGS |= EFLAGS_IF; + else if (TheCPU.cr[4]&CR4_VME) + EFLAGS |= EFLAGS_VIF; + else + goto not_permitted; /* GPF */ + if (vm86s.regs.eflags & VIP) { + if (debug_level('e')>1) + e_printf("Return for STI fl=%08x\n", + EFLAGS); + TheCPU.err=EXCP_STISIGNAL; + return PC+1; + } + } + else { + if (REALMODE() || (CPL <= IOPL) || (IOPL==3)) { + EFLAGS |= EFLAGS_IF; + } + else if (TheCPU.cr[4] & CR4_PVI) { + if (debug_level('e')>2) e_printf("Virtual DPMI STI\n"); + EFLAGS |= EFLAGS_VIF; + } + else + goto not_permitted; /* GPF */ + } + PC++; + break; +/*fc*/ case CLD: PC++; +#if 0 + if ((CurrIMeta<0)&&(InterOps[Fetch(PC)]&1)) { + EFLAGS &= ~EFLAGS_DF; + TheCPU.df_increments = 0x040201; + } + else +#endif + Gen(O_SETFL, _mode, CLD); + break; +/*fd*/ case STD: PC++; +#if 0 + if ((CurrIMeta<0)&&(InterOps[Fetch(PC)]&1)) { + EFLAGS |= EFLAGS_DF; + TheCPU.df_increments = 0xfcfeff; + } + else +#endif + Gen(O_SETFL, _mode, STD); + break; +/*fe*/ case GRP2brm: /* only INC and DEC are legal on bytes */ + ModGetReg1(PC, _mode); + switch(REG1) { + case Ofs_AL: /*0*/ + if (Fetch(PC+1) >= 0xc0) { + Gen(O_INC_R, _mode|MBYTE, + R1Tab_b[Fetch(PC+1) & 7]); + PC += 2; + break; + } + PC += ModRM(opc, PC, _mode|MBYTE|MLOAD);//al=[rm] + Gen(O_INC, _mode|MBYTE); + Gen(S_DI, _mode|MBYTE); + break; + case Ofs_CL: /*1*/ + if (Fetch(PC+1) >= 0xc8) { + Gen(O_DEC_R, _mode|MBYTE, + R1Tab_b[Fetch(PC+1) & 7]); + PC += 2; + break; + } + PC += ModRM(opc, PC, _mode|MBYTE|MLOAD);//al=[rm] + Gen(O_DEC, _mode|MBYTE); + Gen(S_DI, _mode|MBYTE); + break; + default: + CODE_FLUSH(); + goto illegal_op; + } + break; +/*ff*/ case GRP2wrm: + ModGetReg1(PC, _mode); + switch (REG1) { + case Ofs_AX: /*0*/ + /* it's not worth optimizing for registers + here (single byte DEC/INC exist), but do + it for consistency anyway. + */ + if (Fetch(PC+1) >= 0xc0) { + Gen(O_INC_R, _mode, + R1Tab_l[Fetch(PC+1) & 7]); + PC += 2; + break; + } + PC += ModRM(opc, PC, _mode|MLOAD); + Gen(O_INC, _mode); + Gen(S_DI, _mode); + break; + case Ofs_CX: /*1*/ + if (Fetch(PC+1) >= 0xc8) { + Gen(O_DEC_R, _mode, + R1Tab_l[Fetch(PC+1) & 7]); + PC += 2; + break; + } + PC += ModRM(opc, PC, _mode|MLOAD); + Gen(O_DEC, _mode); + Gen(S_DI, _mode); + break; + case Ofs_SP: /*4*/ // JMP near indirect + PC = JumpGen(PC, _mode, (opc<<8)|REG1, + ModRM(opc, PC, _mode|NOFLDR|MLOAD)); + if (TheCPU.err) return PC; + break; + case Ofs_DX: { /*2*/ // CALL near indirect + /* don't use MLOAD as O_PUSHI clobbers eax */ + int len = ModRM(opc, PC, _mode|NOFLDR); + dosaddr_t ret = PC + len - LONG_CS; + Gen(O_PUSHI, _mode, ret); + if (TheCPU.mode & RM_REG) + Gen(L_REG, _mode, REG3); + else + Gen(L_DI_R1, _mode); + PC = JumpGen(PC, _mode, (opc<<8)|REG1, len); + if (debug_level('e')>2) + e_printf("CALL indirect: ret=%08x\n\tcalling: %08x\n", + ret,PC-LONG_CS); + if (TheCPU.err) return PC; + } + break; + case Ofs_BX: /*3*/ // CALL long indirect restartable + case Ofs_BP: /*5*/ // JMP long indirect restartable + if (Fetch(PC+1) >= 0xc0) { + CODE_FLUSH(); + goto illegal_op; + } + if (REALADDR()) { + dosaddr_t oip = 0; + int len = ModRM(opc, PC, _mode|NOFLDR); + ocs = TheCPU.cs; + if (REG1==Ofs_BX) { + /* ok, now push old cs:eip */ + oip = PC + len - LONG_CS; + Gen(L_REG, _mode, Ofs_CS); + Gen(O_PUSH, _mode); + Gen(O_PUSHI, _mode, oip); + } + Gen(L_LXS1, _mode, Ofs_EIP); + Gen(L_DI_R1, _mode); + PC = JumpGen(PC, _mode, (opc<<8)|REG1, len); + if (debug_level('e')>2) { + unsigned short jcs = TheCPU.cs; + dosaddr_t jip = PC - LONG_CS; + if (REG1==Ofs_BX) + e_printf("CALL_FAR indirect: ret=%04x:%08x\n\tcalling: %04x:%08x\n", + ocs,oip,jcs,jip); + else + e_printf("JMP_FAR indirect: %04x:%08x\n",jcs,jip); + } +#ifdef SKIP_EMU_VBIOS + if ((jcs&0xf000)==config.vbios_seg) { + /* return the new PC after the jump */ + TheCPU.err = EXCP_GOBACK; + } +#endif + if (TheCPU.err) return PC; + } + else { + unsigned short jcs; + unsigned long oip,xcs,jip=0; + CODE_FLUSH(); + PC += ModRMSim(PC, _mode|NOFLDR); + TheCPU.eip = PC - LONG_CS; + /* get new cs:ip */ + jip = DataGetWL_U(_mode, TheCPU.mem_ref); + jcs = GetDWord(TheCPU.mem_ref+BT24(BitDATA16,_mode)); + /* check if new cs is valid, save old for error */ + ocs = TheCPU.cs; + xcs = LONG_CS; + TheCPU.err = MAKESEG(_mode, Ofs_CS, jcs); + if (TheCPU.err) { + TheCPU.cs = ocs; + TheCPU.cs_cache.BoundL = xcs; + // should not change + return P0; + } + if (REG1==Ofs_BX) { + /* ok, now push old cs:eip */ + oip = PC - xcs; + PUSH(_mode, ocs); + PUSH(_mode, oip); + if (debug_level('e')>2) + e_printf("CALL_FAR indirect: ret=%04x:%08lx\n\tcalling: %04x:%08lx\n", + ocs,oip,jcs,jip); + } + else { + if (debug_level('e')>2) + e_printf("JMP_FAR indirect: %04x:%08lx\n",jcs,jip); + } + TheCPU.eip = jip; + PC = LONG_CS + jip; + } + break; + case Ofs_SI: /*6*/ // PUSH + PC += ModRM(opc, PC, _mode|MLOAD); + Gen(O_PUSH, _mode); break; // push [rm] + break; + default: + CODE_FLUSH(); + goto illegal_op; + } + break; + +/*6c*/ case INSb: { + unsigned short a; + unsigned int rd; + CODE_FLUSH(); + a = rDX; + if (!test_ioperm(a)) goto not_permitted; + rd = (_mode&ADDR16? rDI:rEDI); + WRITE_BYTE(LONG_ES+rd, port_inb(a)); + if (EFLAGS & EFLAGS_DF) rd--; else rd++; + if (_mode&ADDR16) rDI=rd; else rEDI=rd; + PC++; } break; +/*ec*/ case INvb: { + unsigned short a; + int uc; + CODE_FLUSH(); + a = rDX; + if ((CEmuStat & CeS_INSTREMU) && + (uc=VGA_emulate_inb(a, NULL)) != -1) { + rAL = uc; + PC++; + break; + } +#ifdef TRAP_RETRACE + if (a==0x3da) { // video retrace bits + /* bit 0 = DE bit 3 = VR + * display=0 hor.retr.=1 ver.retr.=9 */ + int c1 = *((int *)(PC+1)); + int c2 = *((int *)(PC+5)); + int tp = test_ioperm(a) & 1; + dbug_printf("IN 3DA %08lx %08lx PP=%d\n",c1,c2,tp); + if (c1==0xfb7408a8) { + // 74 fb = wait for VR==1 + if (tp==0) set_ioperm(a,1,1); + while (((rAL=port_inb(a))&8)==0); + if (tp==0) set_ioperm(a,1,0); + PC+=5; break; + } + //else if (c1==0xfb7508a8) { + // 75 fb = wait for VR==0 + //} + //else if (c1==0xfbe008a8) { + // e0 fb = loop while VR==1 + //unsigned int rcx = _mode&DATA16? rCX:rECX; + //if (tp==0) set_ioperm(a,1,1); + //while ((((rAL=port_inb(a))&8)!=0) && rcx) + // rcx--; + //if (tp==0) set_ioperm(a,1,0); + //if (_mode&DATA16) rCX=rcx; else rECX=rcx; + //PC+=5; break; + //} + else if (c1==0xfbe108a8) { + // e1 fb = loop while VR==0 + unsigned int rcx = _mode&DATA16? rCX:rECX; + if (tp==0) set_ioperm(a,1,1); + while ((((rAL=port_inb(a))&8)==0) && rcx) + rcx--; + if (tp==0) set_ioperm(a,1,0); + if (_mode&DATA16) rCX=rcx; else rECX=rcx; + PC+=5; break; + } + else if (((c1&0xfffff6ff)==0xc0080024) && + ((c2&0xfffffffe)==0xf4eb0274)) { + // 74 02 eb f4 = wait for HR,VR==0 + // 75 02 eb f4 = wait for HR,VR==1 + register unsigned char amk = Fetch(PC+1); + if (amk==8) { + if (tp==0) set_ioperm(a,1,1); + if (PC[5]&1) + while (((rAL=port_inb(a))&amk)==0); + else + while (((rAL=port_inb(a))&amk)!=0); + if (tp==0) set_ioperm(a,1,0); + } + else + rAL = (PC[5]&1? amk:0); + PC+=9; break; + } + } +#endif + if (!test_ioperm(a)) goto not_permitted; +#ifdef CPUEMU_DIRECT_IO + Gen(O_INPDX, _mode|MBYTE); NewNode=1; +#else + rAL = port_inb(a); +#endif + } + PC++; break; +/*e4*/ case INb: { + unsigned short a; + CODE_FLUSH(); + /* there's no reason to compile this, as most of + * the ports under 0x100 are emulated by dosemu */ + a = Fetch(PC+1); + if (!test_ioperm(a)) goto not_permitted; + rAL = port_inb(a); + PC += 2; } break; +/*6d*/ case INSw: { + unsigned int rd; + int dp; + CODE_FLUSH(); + if (!test_ioperm(rDX)) goto not_permitted; + rd = (_mode&ADDR16? rDI:rEDI); + if (_mode&DATA16) { + WRITE_WORD(LONG_ES+rd, port_inw(rDX)); dp=2; + } + else { + WRITE_DWORD(LONG_ES+rd, port_ind(rDX)); dp=4; + } + if (EFLAGS & EFLAGS_DF) rd-=dp; else rd+=dp; + if (_mode&ADDR16) rDI=rd; else rEDI=rd; + PC++; } break; +/*ed*/ case INvw: { + CODE_FLUSH(); + if (!test_ioperm(rDX)) goto not_permitted; + if (_mode&DATA16) rAX = port_inw(rDX); + else rEAX = port_ind(rDX); + } PC++; break; +/*e5*/ case INw: { + unsigned short a; + CODE_FLUSH(); + a = Fetch(PC+1); + if (!test_ioperm(a)) goto not_permitted; + if (_mode&DATA16) rAX = port_inw(a); + else rEAX = port_ind(a); + PC += 2; } break; + +/*6e*/ case OUTSb: { + unsigned short a; + unsigned long rs; + CODE_FLUSH(); + a = rDX; + if (!test_ioperm(a)) goto not_permitted; + rs = (_mode&ADDR16? rSI:rESI); + do { + port_outb(a,Fetch(LONG_DS+rs)); + if (EFLAGS & EFLAGS_DF) rs--; else rs++; + PC++; + } while (Fetch(PC)==OUTSb); + if (_mode&ADDR16) rSI=rs; else rESI=rs; + } break; +/*ee*/ case OUTvb: { + unsigned short a; + CODE_FLUSH(); + a = rDX; + /* Note that we short circuit for vgaemu planar */ + if ((CEmuStat & CeS_INSTREMU) && + VGA_emulate_outb(a, rAL, NULL) != -1) { + PC++; + break; + } + if (!test_ioperm(a)) goto not_permitted; +#ifdef CPUEMU_DIRECT_IO + Gen(O_OUTPDX, _mode|MBYTE); NewNode=1; +#else + port_outb(a,rAL); +#endif + } + PC++; break; +/*e6*/ case OUTb: { + unsigned short a; + CODE_FLUSH(); + a = Fetch(PC+1); + /* there's no reason to compile this, as most of + * the ports under 0x100 are emulated by dosemu */ + if (!test_ioperm(a)) goto not_permitted; + port_outb(a,rAL); + PC += 2; } break; +/*6f*/ case OUTSw: + CODE_FLUSH(); + goto not_permitted; +/*ef*/ case OUTvw: { + unsigned short a; + CODE_FLUSH(); + a = rDX; + if ((CEmuStat & CeS_INSTREMU) && + VGA_emulate_outb(a, rAL, NULL) != -1 && + VGA_emulate_outb(a+1, rAH, NULL) != -1) { + PC++; + break; + } + if (!test_ioperm(a)) goto not_permitted; +#ifdef CPUEMU_DIRECT_IO + Gen(O_OUTPDX, _mode); NewNode=1; +#else + if (_mode&DATA16) port_outw(a,rAX); else port_outd(a,rEAX); +#endif + } + PC++; break; + +/*e7*/ case OUTw: { + unsigned short a; + CODE_FLUSH(); + a = Fetch(PC+1); + if (!test_ioperm(a)) goto not_permitted; + if (_mode&DATA16) port_outw(a,rAX); else port_outd(a,rEAX); + PC += 2; } break; + +/*d8*/ case ESC0: +/*d9*/ case ESC1: +/*da*/ case ESC2: +/*db*/ case ESC3: +/*dc*/ case ESC4: +/*dd*/ case ESC5: +/*de*/ case ESC6: +/*df*/ case ESC7: { + unsigned char b=Fetch(PC+1); + // extended opcode + // 1101.1ooo xxeeerrr -> eeeooo,rrr + // D8 -> 00,08,10,18...38 + // DF -> 07,0f,17,1f...3f + int exop = (b & 0x38) | (opc & 7); + int sim = 0; + if ((b&0xc0)==0xc0) { + exop |= 0x40; + PC += 2; + } + else { + if ((exop&0xeb)==0x21) { + CODE_FLUSH(); + PC += ModRMSim(PC, _mode|NOFLDR); + b = _mode; sim=1; + } + else { + PC += ModRM(opc, PC, _mode|NOFLDR); + } + } + b &= 7; + if (TheCPU.fpstate) { + /* For simulator, only need to mask all + exceptions, and set rounding properly; + for JIT, load emulated FPU state + into real FPU */ + if (CONFIG_CPUSIM) { + fesetenv(FE_DFL_ENV); + fp87_set_rounding(); + } else + loadfpstate(*TheCPU.fpstate); + TheCPU.fpstate = NULL; + } + if (sim) { + if (Fp87_op(exop,b)) { TheCPU.err = -96; return P0; } + } + else + Gen(O_FOP, _mode, exop, b); + } + break; + +/*0f*/ case TwoByteESC: { + unsigned char opc2 = Fetch(PC+1); + switch (opc2) { +// + case 0x00: { /* GRP6 - Extended Opcode 20 */ + unsigned char opm = D_MO(Fetch(PC+2)); + switch (opm) { + case 0: /* SLDT */ + CODE_FLUSH(); + if (REALMODE()) goto illegal_op; + PC += ModRMSim(PC+1, _mode) + 1; + error("SLDT not implemented\n"); + break; + case 1: /* STR */ + /* Store Task Register */ + CODE_FLUSH(); + if (REALMODE()) goto illegal_op; + PC += ModRMSim(PC+1, _mode) + 1; + error("STR not implemented\n"); + break; + case 2: /* LLDT */ + /* Load Local Descriptor Table Register */ + case 3: /* LTR */ + /* Load Task Register */ + CODE_FLUSH(); + goto not_permitted; + case 4: { /* VERR */ + unsigned short sv; int tmp; + CODE_FLUSH(); + if (REALMODE()) goto illegal_op; + PC += ModRMSim(PC+1, _mode) + 1; + if (TheCPU.mode & RM_REG) { + sv = CPUWORD(REG3); + } else { + sv = GetDWord(TheCPU.mem_ref); + } + tmp = hsw_verr(sv); + if (tmp < 0) goto illegal_op; + EFLAGS &= ~EFLAGS_ZF; + if (tmp) EFLAGS |= EFLAGS_ZF; + if (CONFIG_CPUSIM) RFL.valid = V_INVALID; + } + break; + case 5: { /* VERW */ + unsigned short sv; int tmp; + CODE_FLUSH(); + if (REALMODE()) goto illegal_op; + PC += ModRMSim(PC+1, _mode) + 1; + if (TheCPU.mode & RM_REG) { + sv = CPUWORD(REG3); + } else { + sv = GetDWord(TheCPU.mem_ref); + } + tmp = hsw_verw(sv); + if (tmp < 0) goto illegal_op; + EFLAGS &= ~EFLAGS_ZF; + if (tmp) EFLAGS |= EFLAGS_ZF; + if (CONFIG_CPUSIM) RFL.valid = V_INVALID; + } + break; + case 6: /* JMP indirect to IA64 code */ + case 7: /* Illegal */ + CODE_FLUSH(); + goto illegal_op; + } } + break; + case 0x01: { /* GRP7 - Extended Opcode 21 */ + unsigned char opm = D_MO(Fetch(PC+2)); + switch (opm) { + case 0: /* SGDT */ + /* Store Global Descriptor Table Register */ + PC++; PC += ModRM(opc, PC, _mode|DATA16|MSTORE); + error("SGDT not implemented\n"); + break; + case 1: /* SIDT */ + /* Store Interrupt Descriptor Table Register */ + PC++; PC += ModRM(opc, PC, _mode|DATA16|MSTORE); + error("SIDT not implemented\n"); + break; + case 2: /* LGDT */ /* PM privileged AND real _mode */ + /* Load Global Descriptor Table Register */ + case 3: /* LIDT */ /* PM privileged AND real _mode */ + /* Load Interrupt Descriptor Table Register */ + CODE_FLUSH(); + goto not_permitted; + case 4: /* SMSW, 80286 compatibility */ + /* Store Machine Status Word */ + Gen(L_CR0, _mode); + PC++; PC += ModRM(opc, PC, _mode|DATA16|MSTORE); + break; + case 5: /* Illegal */ + case 6: /* LMSW, 80286 compatibility, Privileged */ + /* Load Machine Status Word */ + case 7: /* Illegal */ + CODE_FLUSH(); + goto illegal_op; + } } + break; + + case 0x02: /* LAR */ /* Load Access Rights Byte */ + case 0x03: { /* LSL */ /* Load Segment Limit */ + unsigned short sv; int tmp; + CODE_FLUSH(); + if (REALMODE()) goto illegal_op; + PC += ModRMSim(PC+1, _mode) + 1; + if (TheCPU.mode & RM_REG) { + sv = CPUWORD(REG3); + } else { + sv = GetDWord(TheCPU.mem_ref); + } + if (!e_larlsl(_mode, sv)) { + EFLAGS &= ~EFLAGS_ZF; + if (CONFIG_CPUSIM) RFL.valid = V_INVALID; + } + else { + if (opc2==0x02) { /* LAR */ + tmp = GetSelectorFlags(sv); + if (_mode&DATA16) tmp &= 0xff; + tmp <<= 8; + if (tmp) SetFlagAccessed(sv); + } + else { /* LSL */ + tmp = GetSelectorByteLimit(sv); + } + EFLAGS |= EFLAGS_ZF; + if (CONFIG_CPUSIM) RFL.valid = V_INVALID; + SetCPU_WL(_mode, REG1, tmp); + } } + break; + + /* case 0x04: LOADALL */ + /* case 0x05: LOADALL(286) - SYSCALL(K6) */ + case 0x06: /* CLTS */ /* Privileged */ + /* Clear Task State Register */ + CODE_FLUSH(); + if (CPL != 0) goto not_permitted; + TheCPU.cr[0] &= ~8; + PC += 2; break; + /* case 0x07: LOADALL(386) - SYSRET(K6) etc. */ + case 0x08: /* INVD */ + /* INValiDate cache */ + case 0x09: /* WBINVD */ + /* Write-Back and INValiDate cache */ + PC += 2; break; + case 0x0a: + case 0x0b: + CODE_FLUSH(); + goto illegal_op; /* UD2 */ + /* case 0x0d: Code Extension 25(AMD-3D) */ + /* case 0x0e: FEMMS(K6-3D) */ + case 0x0f: /* AMD-3D */ + /* case 0x10-0x1f: various V20/MMX instr. */ + CODE_FLUSH(); + goto not_implemented; + case 0x20: /* MOVcdrd */ /* Privileged */ + case 0x22: /* MOVrdcd */ /* Privileged */ + case 0x21: /* MOVddrd */ /* Privileged */ + case 0x23: /* MOVrddd */ /* Privileged */ + case 0x24: /* MOVtdrd */ /* Privileged */ + case 0x26: { /* MOVrdtd */ /* Privileged */ + int *srg; int reg; unsigned char b,opd; + CODE_FLUSH(); + if (REALMODE()) goto not_permitted; + b = Fetch(PC+2); + if (D_HO(b)!=3) goto illegal_op; + reg = D_MO(b); b = D_LO(b); + if ((b==4)||(b==5)) goto illegal_op; + srg = (int *)CPUOFFS(R1Tab_l[b]); + opd = Fetch(PC+1)&2; + if (opc2&1) { + if (opd) TheCPU.dr[reg] = *srg; + else *srg = TheCPU.dr[reg]; + } + else if (opc2&4) { + reg-=6; if (reg<0) goto illegal_op; + if (opd) TheCPU.tr[reg] = *srg; + else *srg = TheCPU.tr[reg]; + } + else { + if ((reg==1)||(reg>4)) goto illegal_op; + if (opd) { /* write to CRs */ + if (reg==0) { + if ((TheCPU.cr[0] ^ *srg) & 1) { + dbug_printf("RM/PM switch not allowed\n"); + TheCPU.err = -94; return P0; + } + TheCPU.cr[0] = (*srg&0xe005002f)|0x10; + } + else TheCPU.cr[reg] = *srg; + } else { + *srg = TheCPU.cr[reg]; + } + } + } PC += 3; break; + case 0x30: /* WRMSR */ + CODE_FLUSH(); + goto not_implemented; + + case 0x31: /* RDTSC */ + Gen(O_RDTSC, _mode); + PC+=2; break; + case 0x32: /* RDMSR */ + CODE_FLUSH(); + goto not_implemented; + + /* case 0x33: RDPMC(P6) */ + /* case 0x34: SYSENTER(PII) */ + /* case 0x35: SYSEXIT(PII) */ + /* case 0x40-0x4f: CMOV(P6) */ + /* case 0x50-0x5f: various Cyrix/MMX */ + case 0x60 ... 0x6b: /* MMX */ + case 0x6e: case 0x6f: + case 0x74 ... 0x77: + /* case 0x78-0x7e: Cyrix */ + case 0x7e: case 0x7f: + CODE_FLUSH(); + goto not_implemented; +// + case JOimmdisp: /*80*/ + case JNOimmdisp: /*81*/ + case JBimmdisp: /*82*/ + case JNBimmdisp: /*83*/ + case JZimmdisp: /*84*/ + case JNZimmdisp: /*85*/ + case JBEimmdisp: /*86*/ + case JNBEimmdisp: /*87*/ + case JSimmdisp: /*88*/ + case JNSimmdisp: /*89*/ + case JPimmdisp: /*8a*/ + case JNPimmdisp: /*8b*/ + case JLimmdisp: /*8c*/ + case JNLimmdisp: /*8d*/ + case JLEimmdisp: /*8e*/ + case JNLEimmdisp: /*8f*/ + { + PC = JumpGen(PC, _mode, JO+(opc2-JOimmdisp), + 2 + BT24(BitDATA16,_mode)); + if (TheCPU.err) return PC; + } + break; +/// + case SETObrm: /*90*/ + case SETNObrm: /*91*/ + case SETBbrm: /*92*/ + case SETNBbrm: /*93*/ + case SETZbrm: /*94*/ + case SETNZbrm: /*95*/ + case SETBEbrm: /*96*/ + case SETNBEbrm: /*97*/ + case SETSbrm: /*98*/ + case SETNSbrm: /*99*/ + case SETPbrm: /*9a*/ + case SETNPbrm: /*9b*/ + case SETLbrm: /*9c*/ + case SETNLbrm: /*9d*/ + case SETLEbrm: /*9e*/ + case SETNLEbrm: /*9f*/ + Gen(O_SETCC, _mode, (opc2&0x0f)); + PC++; PC += ModRM(opc, PC, _mode|MBYTE|MSTORE); + break; +/// + case 0xa0: /* PUSHfs */ + Gen(L_REG, _mode, Ofs_FS); + Gen(O_PUSH, _mode); PC+=2; + break; + case 0xa1: /* POPfs */ + if (REALADDR()) { + Gen(O_POP, _mode); + Gen(S_REG, _mode, Ofs_FS); + AddrGen(A_SR_SH4, _mode, Ofs_FS, Ofs_XFS); + } else { /* restartable */ + unsigned short sv = 0; + CODE_FLUSH(); + TOS_WORD(_mode, &sv); + TheCPU.err = MAKESEG(_mode, Ofs_FS, sv); + if (TheCPU.err) return P0; + POP_ONLY(_mode); + TheCPU.fs = sv; + } + PC+=2; + break; +/// + case 0xa2: /* CPUID */ + CODE_FLUSH(); + if (rEAX==0) { + rEAX = 1; rEBX = 0x756e6547; + rECX = 0x6c65746e; rEDX = 0x49656e69; + } + else if (rEAX==1) { + rEAX = 0x052c; rEBX = rECX = 0; + rEDX = 0x1bf; + } + PC+=2; break; + + case 0xa3: /* BT */ + case 0xab: /* BTS */ + case 0xb3: /* BTR */ + case 0xbb: /* BTC */ + PC++; PC += ModRM(opc, PC, _mode); + if (TheCPU.mode & RM_REG) { + Gen(L_REG, _mode, REG3); + } + else { + /* add bit offset to effective address, + then load and store from there */ + Gen(O_BITOP, _mode, (opc2-0xa0), REG1); + Gen(L_DI_R1, _mode); + } + Gen(O_BITOP, _mode|RM_REG, (opc2-0xa0), REG1); + if (opc2 != 0xa3) { + if (TheCPU.mode & RM_REG) + Gen(S_REG, _mode, REG3); + else + Gen(S_DI, _mode); + } + break; + case 0xbc: /* BSF */ + case 0xbd: /* BSR */ + PC++; PC += ModRM(opc, PC, _mode|MLOAD); + _mode |= (TheCPU.mode & RM_REG); + Gen(O_BITOP, _mode, (opc2-0xa0), REG1); + break; + case 0xba: { /* GRP8 - Code Extension 22 */ + unsigned char opm = (Fetch(PC+2))&0x38; + switch (opm) { + case 0x00: /* Illegal */ + case 0x08: /* Illegal */ + case 0x10: /* Illegal */ + case 0x18: /* Illegal */ + CODE_FLUSH(); + goto illegal_op; + case 0x20: /* BT imm8 */ + case 0x28: /* BTS imm8 */ + case 0x30: /* BTR imm8 */ + case 0x38: /* BTC imm8 */ + PC++; PC += ModRM(opc, PC, _mode|MLOAD); + _mode |= (TheCPU.mode & RM_REG); + Gen(O_BITOP, _mode, opm, Fetch(PC)); + if (opm != 0x20) { + if (TheCPU.mode & RM_REG) { + Gen(S_REG, _mode, REG3); + } + else { + Gen(S_DI, _mode); + } + } + PC++; + break; + } } + break; + + case 0xa4: /* SHLDimm */ + /* Double Precision Shift Left by IMMED */ + case 0xa5: /* SHLDcl */ + /* Double Precision Shift Left by CL */ + case 0xac: /* SHRDimm */ + /* Double Precision Shift Left by IMMED */ + case 0xad: /* SHRDcl */ + /* Double Precision Shift Left by CL */ + PC++; PC += ModRM(opc, PC, _mode|MLOAD); + if (opc2&1) { + Gen(O_SHFD, _mode, (opc2&8), REG1); + } + else { + Gen(O_SHFD, _mode|IMMED, (opc2&8), REG1, Fetch(PC)); + PC++; + } + if (TheCPU.mode & RM_REG) + Gen(S_REG, _mode, REG3); + else + Gen(S_DI, _mode); + break; + + case 0xa6: /* CMPXCHGb (486 STEP A only) */ + case 0xa7: /* CMPXCHGw (486 STEP A only) */ + CODE_FLUSH(); + goto not_implemented; +/// + case 0xa8: /* PUSHgs */ + Gen(L_REG, _mode, Ofs_GS); + Gen(O_PUSH, _mode); PC+=2; + break; + case 0xa9: /* POPgs */ + if (REALADDR()) { + Gen(O_POP, _mode); + Gen(S_REG, _mode, Ofs_GS); + AddrGen(A_SR_SH4, _mode, Ofs_GS, Ofs_XGS); + } else { /* restartable */ + unsigned short sv = 0; + CODE_FLUSH(); + TOS_WORD(_mode, &sv); + TheCPU.err = MAKESEG(_mode, Ofs_GS, sv); + if (TheCPU.err) return P0; + POP_ONLY(_mode); + TheCPU.gs = sv; + } + PC+=2; + break; +/// + case 0xaa: + CODE_FLUSH(); + goto illegal_op; + /* case 0xae: Code Extension 24(MMX) */ + case 0xaf: /* IMULregrm */ + PC++; PC += ModRM(opc, PC, _mode|MLOAD); + Gen(O_IMUL, _mode|MEMADR, REG1); // reg*[edi]->reg signed + break; + case 0xb0: /* CMPXCHGb */ + PC++; PC += ModRM(opc, PC, _mode|MBYTE|MLOAD); + _mode |= (TheCPU.mode & RM_REG); + Gen(O_CMPXCHG, _mode | MBYTE, REG1); + if (TheCPU.mode & RM_REG) + Gen(S_REG, _mode | MBYTE, REG3); + else + Gen(S_DI, _mode | MBYTE); + break; + case 0xb1: /* CMPXCHGw */ + PC++; PC += ModRM(opc, PC, _mode|MLOAD); + _mode |= (TheCPU.mode & RM_REG); + Gen(O_CMPXCHG, _mode, REG1); + if (TheCPU.mode & RM_REG) + Gen(S_REG, _mode, REG3); + else + Gen(S_DI, _mode); + break; +/// + case 0xb2: /* LSS */ + if (Fetch(PC+2) >= 0xc0) { + CODE_FLUSH(); + goto not_permitted; + } + if (REALADDR()) { + PC++; PC += ModRM(opc, PC, _mode); + Gen(L_LXS1, _mode, REG1); + Gen(L_LXS2, _mode, Ofs_SS, Ofs_XSS); + } + else { + unsigned short sv = 0; + unsigned long rv; + CODE_FLUSH(); + PC++; PC += ModRMSim(PC, _mode); + rv = DataGetWL_U(_mode,TheCPU.mem_ref); + TheCPU.mem_ref += BT24(BitDATA16,_mode); + sv = GetDWord(TheCPU.mem_ref); + TheCPU.err = MAKESEG(_mode, Ofs_SS, sv); + if (TheCPU.err) return P0; + SetCPU_WL(_mode, REG1, rv); + TheCPU.ss = sv; + } + break; + case 0xb4: /* LFS */ + if (Fetch(PC+2) >= 0xc0) { + CODE_FLUSH(); + goto not_permitted; + } + if (REALADDR()) { + PC++; PC += ModRM(opc, PC, _mode); + Gen(L_LXS1, _mode, REG1); + Gen(L_LXS2, _mode, Ofs_FS, Ofs_XFS); + } + else { + unsigned short sv = 0; + unsigned long rv; + CODE_FLUSH(); + PC++; PC += ModRMSim(PC, _mode); + rv = DataGetWL_U(_mode,TheCPU.mem_ref); + TheCPU.mem_ref += BT24(BitDATA16,_mode); + sv = GetDWord(TheCPU.mem_ref); + TheCPU.err = MAKESEG(_mode, Ofs_FS, sv); + if (TheCPU.err) return P0; + SetCPU_WL(_mode, REG1, rv); + TheCPU.fs = sv; + } + break; + case 0xb5: /* LGS */ + if (Fetch(PC+2) >= 0xc0) { + CODE_FLUSH(); + goto not_permitted; + } + if (REALADDR()) { + PC++; PC += ModRM(opc, PC, _mode); + Gen(L_LXS1, _mode, REG1); + Gen(L_LXS2, _mode, Ofs_GS, Ofs_XGS); + } + else { + unsigned short sv = 0; + unsigned long rv; + CODE_FLUSH(); + PC++; PC += ModRMSim(PC, _mode); + rv = DataGetWL_U(_mode,TheCPU.mem_ref); + TheCPU.mem_ref += BT24(BitDATA16,_mode); + sv = GetDWord(TheCPU.mem_ref); + TheCPU.err = MAKESEG(_mode, Ofs_GS, sv); + if (TheCPU.err) return P0; + SetCPU_WL(_mode, REG1, rv); + TheCPU.gs = sv; + } + break; + case 0xb6: /* MOVZXb */ + PC++; PC += ModRM(opc, PC, _mode|MBYTX|MLOAD); + Gen(L_MOVZS, _mode|MBYTX, 0, REG1); + break; + case 0xb7: /* MOVZXw */ + PC++; PC += ModRM(opc, PC, _mode|DATA16|MLOAD); + Gen(L_MOVZS, _mode, 0, REG1); + break; + case 0xbe: /* MOVSXb */ + PC++; PC += ModRM(opc, PC, _mode|MBYTX|MLOAD); + Gen(L_MOVZS, _mode|MBYTX, 1, REG1); + break; + case 0xbf: /* MOVSXw */ + PC++; PC += ModRM(opc, PC, _mode|DATA16|MLOAD); + Gen(L_MOVZS, _mode, 1, REG1); + break; +/// + case 0xb8: /* JMP absolute to IA64 code */ + case 0xb9: + CODE_FLUSH(); + goto illegal_op; /* UD2 */ + case 0xc0: /* XADDb */ + PC++; PC += ModRM(opc, PC, _mode|MBYTE|MLOAD); + Gen(O_XCHG, _mode | MBYTE, REG1); + Gen(O_ADD_R, _mode | MBYTE, REG1); + if (TheCPU.mode & RM_REG) + Gen(S_REG, _mode|MBYTE, REG3); + else + Gen(S_DI, _mode|MBYTE); + break; + case 0xc1: /* XADDw */ + PC++; PC += ModRM(opc, PC, _mode|MLOAD); + Gen(O_XCHG, _mode, REG1); + Gen(O_ADD_R, _mode, REG1); + if (TheCPU.mode & RM_REG) + Gen(S_REG, _mode, REG3); + else + Gen(S_DI, _mode); + break; + + /* case 0xc2-0xc6: MMX */ + case 0xc7: { /* Code Extension 23 - 01=CMPXCHG8B mem */ + uint64_t edxeax, m; + unsigned char modrm; + CODE_FLUSH(); + modrm = Fetch(PC+2); + if (D_MO(modrm) != 1 || D_HO(modrm) == 3) + goto illegal_op; + PC++; PC += ModRMSim(PC, _mode); + edxeax = ((uint64_t)rEDX << 32) | rEAX; + m = read_qword(TheCPU.mem_ref); + if (edxeax == m) + { + EFLAGS |= EFLAGS_ZF; + m = ((uint64_t)rECX << 32) | rEBX; + } else { + EFLAGS &= ~EFLAGS_ZF; + rEDX = m >> 32; + rEAX = m & 0xffffffff; + } + write_qword(TheCPU.mem_ref, m); + if (CONFIG_CPUSIM) RFL.valid = V_INVALID; + break; + } + + case 0xc8: /* BSWAPeax */ + case 0xc9: /* BSWAPecx */ + case 0xca: /* BSWAPedx */ + case 0xcb: /* BSWAPebx */ + case 0xcc: /* BSWAPesp */ + case 0xcd: /* BSWAPebp */ + case 0xce: /* BSWAPesi */ + case 0xcf: /* BSWAPedi */ + if (!(_mode&DATA16)) { + Gen(O_BSWAP, 0, R1Tab_l[D_LO(opc2)]); + } /* else undefined */ + PC+=2; break; + case 0xd1 ... 0xd3: /* MMX */ + case 0xd5: case 0xd7: case 0xda: case 0xde: + case 0xdf: + case 0xe0 ... 0xe5: + case 0xf1 ... 0xf3: + case 0xf5 ... 0xf7: + case 0xfc ... 0xfe: + CODE_FLUSH(); + goto not_implemented; +/// + case 0xff: /* illegal, used by Windows */ + CODE_FLUSH(); + goto illegal_op; + default: + CODE_FLUSH(); + goto not_implemented; + } } + break; + +/*xx*/ default: + CODE_FLUSH(); + goto not_implemented; + } + if (TheCPU.err < 0) + return P0; + + /* check segment boundaries. TODO for prot _mode */ + if (REALADDR() && (PC - LONG_CS > 0xffff)) { + e_printf("PC out of bounds, %x\n", PC - LONG_CS); + CODE_FLUSH(); + goto not_permitted; + } + return PC; + +not_implemented: + dbug_printf("!!! Unimplemented %02x %02x %02x at %08x\n",opc, + Fetch(PC+1),Fetch(PC+2),PC); + TheCPU.err = -2; return P0; +not_permitted: + if (debug_level('e')>1) e_printf("!!! Not permitted %02x\n",opc); + TheCPU.err = EXCP0D_GPF; return P0; +//div_by_zero: +// dbug_printf("!!! Div by 0 %02x\n",opc); +// TheCPU.err = -6; return P0; +illegal_op: + dbug_printf("!!! Illegal op %02x %02x %02x\n",opc, + Fetch(PC+1),Fetch(PC+2)); + TheCPU.err = EXCP06_ILLOP; return P0; +} + +/* reset for VGA reads and writes */ +void instr_emu_sim_reset_count(int cnt) +{ + interp_inst_emu_count = cnt; +} diff --git a/src/base/emu-i386/simx86/memory.c b/src/base/emu-i386/simx86/memory.c new file mode 100644 index 0000000..1e9b985 --- /dev/null +++ b/src/base/emu-i386/simx86/memory.c @@ -0,0 +1,514 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "mapping.h" +#include "dosemu_debug.h" +#include "dlmalloc.h" +#include "emu86.h" +#include "trees.h" +#include "codegen.h" +#include "emudpmi.h" + +#define CGRAN 0 /* 2^n */ +#define CGRMASK (0xfffff>>CGRAN) + +#ifndef UINT64_WIDTH +#define UINT64_WIDTH 64 +#endif + +typedef struct _mpmap { + struct _mpmap *next; + int mega; + unsigned char pagemap[32]; /* (32*8)=256 pages *4096 = 1M */ + uint64_t subpage[(0x100000>>CGRAN)/UINT64_WIDTH]; /* 2^CGRAN-byte granularity, 1M/2^CGRAN bits */ +} tMpMap; + +static tMpMap *MpH = NULL; +unsigned int mMaxMem = 0; +int PageFaults = 0; +static tMpMap *LastMp = NULL; + +static int e_munprotect(unsigned int addr, size_t len); + +///////////////////////////////////////////////////////////////////////////// + +static inline tMpMap *FindM(unsigned int addr) +{ + register int a2l = addr >> (PAGE_SHIFT+8); + register tMpMap *M = LastMp; + + if (M && (M->mega==a2l)) return M; + M = MpH; + while (M) { + if (M->mega==a2l) { + LastMp = M; break; + } + M = M->next; + } + return M; +} + + +static int AddMpMap(unsigned int addr, unsigned int aend, int onoff) +{ + int bs=0, bp=0; + register int page; + tMpMap *M; + + do { + page = addr >> PAGE_SHIFT; + M = MpH; + while (M) { + if (M->mega==(page>>8)) break; + M = M->next; + } + if (M==NULL) { + M = (tMpMap *)calloc(1,sizeof(tMpMap)); + M->next = MpH; MpH = M; + M->mega = (page>>8); + } + if (bp < 32) { + bs |= (((unsigned)(onoff? test_and_set_bit(page&255, M->pagemap) : + test_and_clear_bit(page&255, M->pagemap)) & 1) << bp); + bp++; + } + if (debug_level('e')>1) { + if (addr > mMaxMem) mMaxMem = addr; + if (onoff) + dbug_printf("MPMAP: protect page=%08x was %x\n",addr,bs); + else + dbug_printf("MPMAP: unprotect page=%08x was %x\n",addr,bs); + } + addr += PAGE_SIZE; + } while (addr <= aend); + return bs; +} + + +int e_querymprot(dosaddr_t addr) +{ + register int a2 = addr >> PAGE_SHIFT; + tMpMap *M = FindM(addr); + + if (M==NULL) return 0; + return test_bit(a2&255, M->pagemap); +} + +int e_querymprotrange(unsigned int addr, size_t len) +{ + int a2l, a2h; + tMpMap *M = FindM(addr); + + a2l = addr >> PAGE_SHIFT; + a2h = (addr+len-1) >> PAGE_SHIFT; + + while (M && a2l <= a2h) { + if (test_bit(a2l&255, M->pagemap)) + return 1; + a2l++; + if ((a2l&255)==0) + M = M->next; + } + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// + + +int e_markpage(unsigned int addr, size_t len) +{ + unsigned int abeg, aend; + tMpMap *M = FindM(addr); + + if (M == NULL || len == 0) return 0; + + abeg = addr >> CGRAN; + aend = (addr+len-1) >> CGRAN; + + if (debug_level('e')>1) + dbug_printf("MARK from %08x to %08x for %08x\n", + abeg<subpage)); + set_bit(abeg&CGRMASK, M->subpage); + abeg++; + if ((abeg&CGRMASK) == 0) + M = M->next; + } + return 1; +} + +int e_unmarkpage(unsigned int addr, size_t len) +{ + unsigned int abeg, aend; + tMpMap *M = FindM(addr); + + if (M == NULL || len == 0) return 0; + + abeg = addr >> CGRAN; + aend = (addr+len-1) >> CGRAN; + + if (debug_level('e')>1) + dbug_printf("UNMARK from %08x to %08x for %08x\n", + abeg<subpage); + abeg++; + if ((abeg&CGRMASK) == 0) + M = M->next; + } + + /* check if unmarked pages have no more code, and if so, unprotect */ + abeg = addr & _PAGE_MASK; + aend = (addr + len) & _PAGE_MASK; + /* don't unprotect partial first page with code (if not also last) */ + if (aend != abeg && abeg != addr && e_querymark(abeg, PAGE_SIZE)) + abeg += PAGE_SIZE; + /* unprotect partial last page without code */ + if (aend != addr+len && !e_querymark(aend, PAGE_SIZE)) + aend += PAGE_SIZE; + + if (aend > abeg) + e_munprotect(abeg, aend - abeg); + + return 1; +} + +int e_querymark(unsigned int addr, size_t len) +{ + unsigned int abeg, aend, idx; + tMpMap *M = FindM(addr); + uint64_t mask; + + if (M == NULL) return 0; + + abeg = addr >> CGRAN; + aend = ((addr+len-1) >> CGRAN) + 1; + + if (debug_level('e')>2) + dbug_printf("QUERY MARK from %08x to %08x for %08x\n", + abeg<subpage)) + goto found; + return 0; + } + + idx = (abeg&CGRMASK) / UINT64_WIDTH; + // mask for first partial longword + mask = ~0ULL << (abeg & (UINT64_WIDTH-1)); + while (abeg < (aend & ~(UINT64_WIDTH-1))) { + if (M->subpage[idx] & mask) + goto found; + abeg = (abeg + UINT64_WIDTH) & ~(UINT64_WIDTH-1); + idx++; + mask = ~0ULL; + if (idx == sizeof(M->subpage)/sizeof(M->subpage[0])) { + M = M->next; + if (!M) + return 0; + idx = 0; + } + } + if (aend & (UINT64_WIDTH-1)) { + // mask for last partial longword + mask &= ~0ULL >> (UINT64_WIDTH - (aend & (UINT64_WIDTH-1))); + if (M->subpage[idx] & mask) + goto found; + } + return 0; +found: + if (debug_level('e')>1) { +// if (len > 1) abeg += ffsll(M->subpage[idx] & mask) - 1; + dbug_printf("QUERY MARK found code at " + "%08x to %08x for %08x\n", + abeg<> CGRAN; + aend = (addr+len-1) >> CGRAN; + + while (M && abeg <= aend) { + if (!test_bit(abeg&CGRMASK, M->subpage)) + return 0; + abeg++; + if ((abeg&CGRMASK) == 0) + M = M->next; + } + return 1; +} + +///////////////////////////////////////////////////////////////////////////// + + +int e_mprotect(unsigned int addr, size_t len) +{ + int e; + unsigned int abeg, aend, aend1; + unsigned int abeg1 = (unsigned)-1; + unsigned a; + int ret = 1; + + abeg = addr & _PAGE_MASK; + if (len==0) { + return 0; + } + else { + aend = (addr+len-1) & _PAGE_MASK; + } + /* only protect ranges that were not already protected by e_mprotect */ + for (a = abeg; a <= aend; a += PAGE_SIZE) { + int qp = e_querymprot(a); + if (!qp) { + if (abeg1 == (unsigned)-1) + abeg1 = a; + aend1 = a; + } + if ((a == aend || qp) && abeg1 != (unsigned)-1) { + e = mprotect_mapping(MAPPING_CPUEMU, abeg1, aend1-abeg1+PAGE_SIZE, + PROT_READ|PROT_EXEC); + if (e<0) { + e_printf("MPMAP: %s\n",strerror(errno)); + return -1; + } + ret = AddMpMap(abeg1, aend1+PAGE_SIZE-1, 1); + abeg1 = (unsigned)-1; + } + } + return ret; +} + +static int e_munprotect(unsigned int addr, size_t len) +{ + int e; + unsigned int abeg, aend, aend1; + unsigned int abeg1 = (unsigned)-1; + unsigned a; + int ret = 0; + + abeg = addr & _PAGE_MASK; + if (len==0) { + aend = abeg; + } + else { + aend = (addr+len-1) & _PAGE_MASK; + } + /* only unprotect ranges that were protected by e_mprotect */ + for (a = abeg; a <= aend; a += PAGE_SIZE) { + int qp = e_querymprot(a); + if (qp) { + if (abeg1 == (unsigned)-1) + abeg1 = a; + aend1 = a; + } + if ((a == aend || !qp) && abeg1 != (unsigned)-1) { + e = mprotect_mapping(MAPPING_CPUEMU, abeg1, aend1-abeg1+PAGE_SIZE, + PROT_READ|PROT_WRITE|PROT_EXEC); + if (e<0) { + e_printf("MPUNMAP: %s\n",strerror(errno)); + return -1; + } + ret = AddMpMap(abeg1, aend1+PAGE_SIZE-1, 0); + abeg1 = (unsigned)-1; + } + } + return ret; +} + +#ifdef HOST_ARCH_X86 +int e_handle_pagefault(dosaddr_t addr, unsigned err, sigcontext_t *scp) +{ + register int v; + unsigned char *p; + int in_dosemu; + + /* err: + * bit 0 = 1 page protect + * bit 1 = 1 writing + * bit 2 = 1 user mode + * bit 3 = 0 no reserved bit err + */ + if ((err & 0x06) != 0x06) + return 0; + /* additionally check if page is not mapped */ + if (addr >= LOWMEM_SIZE + HMASIZE && + /* can't fully trust err as page may be swapped out */ + !(err & 1) && !dpmi_read_access(addr)) + return 0; + if (!e_querymprot(addr)) + return 0; + + /* Got a fault in a write-protected memory page, that is, + * a page _containing_code_. 99% of the time we are + * hitting data or stack in the same page, NOT code. + * + * We check using e_querymprot whether we protected the + * page ourselves. Additionally an error code of 7 should + * have been given. + * + * _cr2 keeps the address where the code tries to write + * _rip keeps the address of the faulting instruction + * (in the code buffer or in the tree) + * + * The faults can also come from + * - native/KVM DPMI code if only vm86() is emulated + * - native/KVM vm86 code if only DPMI is emulated + * - DOSEMU itself (to be avoided) + * + * Possible instructions we'll find here are (see sigsegv.v): + * 8807 movb %%al,(%%edi) + * (66)8907 mov{wl} %%{e}ax,(%%edi) + * (f3)(66)a4,a5 movs + * (f3)(66)aa,ab stos + */ +#if PROFILE + if (debug_level('e')) PageFaults++; +#endif + in_dosemu = !(InCompiledCode || in_vm86 || DPMIValidSelector(_scp_cs)); + if (in_vm86) + p = SEG_ADR((unsigned char *), cs, ip); + else if (DPMIValidSelector(_scp_cs)) + p = (unsigned char *)MEM_BASE32(GetSegmentBase(_scp_cs) + _scp_rip); + else + p = (unsigned char *) _scp_rip; + if (debug_level('e')>1 || in_dosemu) { + v = *((int *)p); + __asm__("bswap %0" : "=r" (v) : "0" (v)); + e_printf("Faulting ops: %08x\n",v); + + if (!InCompiledCode) { + unsigned int cs = in_vm86 ? _CS : _scp_cs; + greg_t eip = in_vm86 ? _IP : _scp_rip; + e_printf("*\tFault out of %scode, cs:eip=%x:%llx," + " cr2=%x, fault_cnt=%d\n", + in_dosemu ? "DOSEMU " : "", + cs, (unsigned long long) eip, addr, + fault_cnt); + } + if (e_querymark(addr, 1)) { + e_printf("CODE node hit at %08x\n",addr); + } + else if (InCompiledCode) { + e_printf("DATA node hit at %08x\n",addr); + } + } + /* the page is not unprotected here, the code + * linked by Cpatch will do it */ + /* ACH: we can set up a data patch for code + * which has not yet been executed! */ +#ifndef SKIP_CPATCH + if (InCompiledCode && Cpatch(scp)) + return 1; +#endif + /* We HAVE to invalidate all the code in the page + * if the page is going to be unprotected */ + addr &= _PAGE_MASK; + return InvalidateNodeRange(addr, PAGE_SIZE, p); +} + +int e_handle_fault(sigcontext_t *scp) +{ + if (!InCompiledCode) + return 0; + /* page-faults are handled not here and only DE remains */ + if (_scp_trapno != 0) { + error("Fault %i in jit-compiled code\n", _scp_trapno); + return 0; + } + TheCPU.err = EXCP00_DIVZ + _scp_trapno; + _scp_eax = TheCPU.cr2; + _scp_edx = _scp_eflags; + TheCPU.cr2 = DOSADDR_REL(LINP(_scp_cr2)); + _scp_rip = *(long *)_scp_rsp; + _scp_rsp += sizeof(long); + return 1; +} +#endif + +///////////////////////////////////////////////////////////////////////////// + +void mprot_init(void) +{ + MpH = NULL; + AddMpMap(0,0,0); /* first mega in first entry */ + PageFaults = 0; +} + +void mprot_end(void) +{ + tMpMap *M = MpH; + int i; + unsigned char b; + + while (M) { + tMpMap *M2 = M; + for (i=0; i<32; i++) if ((b=M->pagemap[i])) { + unsigned int addr = (M->mega<<20) | (i<<15); + while (b) { + if (b & 1) { + if (debug_level('e')>1) + dbug_printf("MP_END %08x = RWX\n",addr); + mprotect_mapping(MAPPING_CPUEMU, addr, PAGE_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC); + } + addr += PAGE_SIZE; + b >>= 1; + } + } + M = M->next; + free(M2); + } + MpH = LastMp = NULL; +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/src/base/emu-i386/simx86/modrm-gen.c b/src/base/emu-i386/simx86/modrm-gen.c new file mode 100644 index 0000000..9a673a7 --- /dev/null +++ b/src/base/emu-i386/simx86/modrm-gen.c @@ -0,0 +1,179 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#include +#include "emu86.h" +#include "codegen.h" + +static unsigned char R1Tab_b[8] = + { Ofs_AL, Ofs_CL, Ofs_DL, Ofs_BL, Ofs_AH, Ofs_CH, Ofs_DH, Ofs_BH }; +static unsigned char R1Tab_w[8] = + { Ofs_AX, Ofs_CX, Ofs_DX, Ofs_BX, Ofs_SP, Ofs_BP, Ofs_SI, Ofs_DI }; +static unsigned char R1Tab_seg[8] = + { Ofs_ES, Ofs_CS, Ofs_SS, Ofs_DS, Ofs_FS, Ofs_GS, 0xff, 0xff }; +static unsigned char R1Tab_xseg[8] = + { Ofs_XES, Ofs_XCS, Ofs_XSS, Ofs_XDS, Ofs_XFS, Ofs_XGS, 0xff, 0xff }; +static unsigned char R1Tab_l[8] = + { Ofs_EAX, Ofs_ECX, Ofs_EDX, Ofs_EBX, Ofs_ESP, Ofs_EBP, Ofs_ESI, Ofs_EDI }; +static unsigned char R1Tab_wb[8] = + { Ofs_BX, Ofs_BX, Ofs_BP, Ofs_BP, Ofs_SI, Ofs_DI, Ofs_BP, Ofs_BX }; +static unsigned char R1Tab_wi[8] = + { Ofs_SI, Ofs_DI, Ofs_SI, Ofs_DI, Ofs_SP, Ofs_SP, Ofs_SP, Ofs_SP }; + +///////////////////////////////////////////////////////////////////////////// + +static void modrm_sib(unsigned char mod, unsigned char sib, unsigned int base) +{ + if (mod == 0 && D_LO(sib) == 5) { + int v = FetchL(base); + e_printf("ModRM sibd sib=%02x base=%08x v=%08x\n",sib,base,v); + } + else { + e_printf("ModRM sib sib=%02x\n",sib); + } +} + +///////////////////////////////////////////////////////////////////////////// + +int _ModRM(unsigned char opc, unsigned int PC, int mode) +{ + unsigned char mod,cab=Fetch(PC+1); + int l=2; + int dsp, base, index, shift; + + if (!(mode&NOFLDR)) { + mod = D_MO(cab); + if (mode & MBYTE) // not for movx + REG1 = R1Tab_b[mod]; + else if (mode & SEGREG) { + REG1 = R1Tab_seg[mod]; + SBASE = R1Tab_xseg[mod]; + } + else + REG1 = (mode&ADDR16? R1Tab_w[mod]:R1Tab_l[mod]); + } + mod = D_HO(cab); + if (mod == 3) { + TheCPU.mode |= RM_REG; + if (mode & (MBYTE|MBYTX)) + REG3 = R1Tab_b[cab&7]; + else if (mode & ADDR16) + REG3 = R1Tab_w[cab&7]; + else + REG3 = R1Tab_l[cab&7]; + if (mode & MLOAD) + Gen(L_REG, mode, REG3); // mov al,[ebx+reg] + else if (mode & MSTORE) + Gen(S_REG, mode, REG3); // mov [ebx+reg],al + return l; + } + dsp = shift = 0; + if (mode & ADDR16) { + base = R1Tab_wb[D_LO(cab)]; + index = R1Tab_wi[D_LO(cab)]; + if (mod == 2 || (mod == 0 && D_LO(cab) == 6)) { + dsp=(unsigned short)FetchW(PC+l); l+=2; + } + } + else { + base = R1Tab_l[D_LO(cab)]; + index = Ofs_ESP; + if (base == Ofs_ESP) { + unsigned char sib = Fetch(PC+l); l++; + base = R1Tab_l[D_LO(sib)]; + index = R1Tab_l[D_MO(sib)]; + shift = D_HO(sib); + if (debug_level('e')>5) + modrm_sib(mod, sib, PC+l); + } + if (mod == 2 || (mod == 0 && base == Ofs_EBP)) { + dsp=FetchL(PC+l); l+=4; + } + } + if (mod == 0 && l > 3) { + if (index == Ofs_ESP) + AddrGen(A_DI_0, mode|IMMED, OVERR_DS, dsp); + else + /* no SS: override on index */ + AddrGen(A_DI_2D, mode, dsp, index, shift); + } + else { + int overr = (base == Ofs_ESP || base == Ofs_EBP) ? + OVERR_SS : OVERR_DS; + if (mod==1) { + dsp=(signed char)Fetch(PC+l); l++; + } + /* for POPrm use the new ESP */ + if (base == Ofs_ESP && (mode & MPOPRM)) + dsp+=OPSIZE(mode); + if (index == Ofs_ESP) + AddrGen(A_DI_1, mode|IMMED, overr, dsp, base); + else + AddrGen(A_DI_2, mode|IMMED, overr, dsp, base, + index, shift); + } + if (mode & MLOAD) + Gen(L_DI_R1, mode); // mov al,[edi] + else if (mode & MSTORE) + Gen(S_DI, mode); // mov [edi],al + return l; +} + + +int ModGetReg1(unsigned int PC, int mode) +{ + unsigned char mod,cab=Fetch(PC+1); + + mod = D_MO(cab); + if (mode & MBYTE) // not for movx + REG1 = R1Tab_b[mod]; + else if (mode & SEGREG) { + REG1 = R1Tab_seg[mod]; + SBASE = R1Tab_xseg[mod]; + } + else + REG1 = (mode&ADDR16? R1Tab_w[mod]:R1Tab_l[mod]); + mod = D_HO(cab); + if (mod==3) { + TheCPU.mode |= RM_REG; + if (mode & MBYTE) + REG3 = R1Tab_b[cab&7]; + else if (mode & ADDR16) + REG3 = R1Tab_w[cab&7]; + else + REG3 = R1Tab_l[cab&7]; + } + return mod; +} + diff --git a/src/base/emu-i386/simx86/modrm-sim.c b/src/base/emu-i386/simx86/modrm-sim.c new file mode 100644 index 0000000..912dc16 --- /dev/null +++ b/src/base/emu-i386/simx86/modrm-sim.c @@ -0,0 +1,61 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#include "emu86.h" +#include "codegen.h" +#include "codegen-sim.h" + +/* + * Why this one? It is only a duplicate of modrm-gen but it interprets + * addresses instead of compiling them. After some headaches I decided + * that it was better to have a duplicate than to parse,compile,execute, + * find the node,delete it etc. + * + */ + +///////////////////////////////////////////////////////////////////////////// + +int ModRMSim(unsigned int PC, int mode) +{ + int l; + void (*AddrGen_save)(int op, int mode, ...); + + AddrGen_save = AddrGen; + AddrGen = AddrGen_sim; + l = _ModRM(0, PC, mode); + AddrGen = AddrGen_save; + TheCPU.mem_ref = AR1.d; + return l; +} + diff --git a/src/base/emu-i386/simx86/protmode.c b/src/base/emu-i386/simx86/protmode.c new file mode 100644 index 0000000..00ccd04 --- /dev/null +++ b/src/base/emu-i386/simx86/protmode.c @@ -0,0 +1,406 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + * linux/kernel/ldt.c + * Copyright (C) 1992 Krishna Balasubramanian and Linus Torvalds + * + ***************************************************************************/ + +/* ======================================================================= */ +/* + * Some protected-mode stuff, not much changed from the 1999 version. + * Interfaces with DPMI and its LDT format. + * Will be heavily reworked later. + */ + +#include +#include +#include +#include "emu.h" +#include "timers.h" +#include "pic.h" +#include "emu86.h" +#include "cpu-emu.h" +#include "codegen-arch.h" +#include "protmode.h" +#include "../dosext/dpmi/msdos/msdos_ldt.h" + +Descriptor *GDT = NULL; +Descriptor *LDT = NULL; +Gatedesc *IDT = NULL; + +static unsigned short sysxfer[] = { + DT_NO_XFER, DT_XFER_TSS16, DT_XFER_LDT, DT_XFER_TSS16, + DT_XFER_CG16, DT_XFER_TSKG, DT_XFER_IG16, DT_XFER_TRP16, + DT_NO_XFER, DT_XFER_TSS32, DT_NO_XFER, DT_XFER_TSS32, + DT_XFER_CG32, DT_NO_XFER, DT_XFER_IG32, DT_XFER_TRP32 +}; + +static char ofsnam[] = "??? ??? ??? GS: FS: ES: DS: ??? " + "??? ??? ??? ??? ??? ??? ??? ??? " + "??? ??? CS: ??? SS: ??? ??? ??? "; + +unsigned char e_ofsseg[] = { + 0, 0, 0, Ofs_XGS, Ofs_XFS, Ofs_XES, Ofs_XDS, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, Ofs_XCS, 0, Ofs_XSS, 0, 0, 0 }; + +#define MKOFSNAM(o,b) (memcpy((b), (ofsnam+(o)), 3), ((b)[3]=0), (b)) + +int SetSegReal(unsigned short sel, int ofs) +{ + static char buf[4]; + SDTR *sd; + + sd = (SDTR *)CPUOFFS(e_ofsseg[(ofs>>2)]); + + CPUWORD(ofs) = sel; + sd->BoundL = sel<<4; + sd->BoundH = sd->BoundL + 0xffff; + sd->Attrib = 2; + + if (debug_level('e')>1) + dbug_printf("SetSeg REAL %s%04x\n",MKOFSNAM(ofs,buf),sel); + return 0; +} + + +int SetSegProt(int a16, int ofs, unsigned char *big, unsigned long sel) +{ + static char buf[4]; + unsigned short wFlags, sys; + unsigned char lbig; + Descriptor *dt; + SDTR *sd; + + sd = (SDTR *)CPUOFFS(e_ofsseg[(ofs>>2)]); + + if ((sd->Oldsel==sel)&&((sd->Attrib&3)==1)) { + e_printf("SetSeg PROT %s%04lx cached\n",MKOFSNAM(ofs,buf),sel); + if (big) *big = (sd->Attrib&4? 0xff:0); + return 0; + } + sd->Oldsel = sel; + sd->Attrib = 0; + TheCPU.scp_err = sel & 0xfffc; + + if (sel < 4) { + if ((ofs==Ofs_CS)||(ofs==Ofs_SS)) return EXCP0D_GPF; + sd->BoundL = 0xc0000000; + return 0; /* DS..GS can be 0 for some while */ + } + + /* DT checks */ + if (sel & 4) { + dt = LDT; + if ((dt == NULL) || (dt[sel>>3].S==0) || + ((sel & 0xfff8) > TheCPU.LDTR.Limit)) { + e_printf("Invalid LDT selector %#lx\n", sel); + return EXCP0D_GPF; + } + } + else { + return EXCP0D_GPF; +#if 0 + dt = GDT; /* GDT is not yet there */ + if ((dt == NULL) || ((sel & 0xfff8) > TheCPU.GDTR.Limit)) + { + if (dt) e_printf("Invalid GDT selector %#lx\n", sel); + return EXCP0D_GPF; + } +#endif + } + /* should set CPL here if seg==CS ??? */ + wFlags = GetSelectorFlags(sel); + sys = (wFlags & DF_USER); + if (!(wFlags & DF_PRESENT)) { + e_printf("DT: selector %lx not present\n",sel); + if (ofs==Ofs_SS) return EXCP0C_STACK; + else return EXCP0B_NOSEG; + } + if (!sys) { /* must be GDT now */ + unsigned short sx = sysxfer[wFlags&15]; + if (debug_level('e')>3) + e_printf("GDT system segment %#lx type %d\n",sel,sx); + if (sx==DT_NO_XFER) return EXCP0D_GPF; + sd->BoundH = 0; /* try to trap if not checked */ + return 0; /* will check sys segment again later */ + } + lbig = (wFlags & DF_32)? 0xff : 0; + /* check data/code */ + if (ofs==Ofs_CS) { + /* data can't be executed... really? */ + if (!(wFlags & DF_CODE)) { + dbug_printf("Attempt to execute into data segment %lx\n",sel); + //return EXCP0D_GPF; + } + a16 = (lbig? 0:ADDR16); + } + else { + /* we CAN move a code sel into [DEFG]S provided that it + * can be read - but how can we trap writes? */ + /* Error summary (Intel): + * SS zero GP + * RPL != CPL GP + * DPL != CPL GP + * data not writable GP + * not present SS + * [DEFG]S not data or readable code GP + * data or nonconf code AND + * RPL>DPL AND CPL>DPL GP + * not present NP + */ + if ((wFlags & DF_CODE)&&(!(wFlags & DF_CREADABLE))) + return EXCP0D_GPF; + } + if (lbig && a16) { + if (debug_level('e')>3) + e_printf("Large segment %#lx in 16-bit mode\n",sel); + } + else if (!lbig && !a16) { + if (debug_level('e')>3) + e_printf("Small segment %#lx in 32-bit mode\n",sel); + } + SetFlagAccessed(sel); + sd->BoundL = GetPhysicalAddress(sel); + sd->BoundH = sd->BoundL + GetSelectorByteLimit(sel); + sd->Attrib = (lbig&4) | 1; + e_printf("SetSeg PROT %s%04lx\n",MKOFSNAM(ofs,buf),sel); + + if (big) *big = lbig; + if (debug_level('e')>2) { + e_printf("PMSEL %#04lx bounds=%08x:%08x flg=%04x big=%d\n", + sel, sd->BoundL, sd->BoundH, wFlags, lbig&1); + } + TheCPU.scp_err = 0; + return 0; +} + +#if 0 +/* not (yet) used */ +void ValidateAddr(unsigned char *addr, unsigned short sel) +{ + unsigned char *base; + unsigned short wFlags; + + if (!PROTMODE()) return; /* always ok */ + wFlags = GetSelectorFlags(sel); + if (wFlags & DF_PRESENT) { + base = GetSelectorAddress(sel); + if (addr >= base) { + base += GetSelectorByteLimit(sel); + if (addr <= base) return; + dbug_printf("addr %#x > limit for sel %#x\n",(int)addr,sel); + } + else + dbug_printf("addr %#x < base for sel %#x\n",(int)addr,sel); + } + else + dbug_printf("selector %#x not present\n",sel); + leavedos_main(0); +} +#endif + +/* flags with 0x409e mask: + * 0090 16 RO data + * 0092 16 RW data + * 0094 16 RO data + * 0096 16 RW data + * 0098 16 FO NC code + * 009a 16 RF NC code + * 009c 16 FO C code + * 009e 16 RF C code + * 4090 32 RO data + * 4092 32 RW data + * 4094 32 RO data + * 4096 32 RW data + * 4098 32 FO NC code + * 409a 32 RF NC code + * 409c 32 FO C code + * 409e 32 RF C code + * flags with 0x009f mask: + * x081 16 TSS + * x082 LDT + * x083 16 TSS + * x084 16 call gate + * x085 task gate + * x086 16 int gate + * x087 16 trap gate + * x089 32 TSS + * x08b 32 TSS + * x08c 32 call gate + * x08e 32 int gate + * x08f 32 trap gate + */ +unsigned short GetSelectorXfer(unsigned short w) +{ + unsigned short flags; + if (!PROTMODE()) return DT_RM_XFER; + flags=DTgetFlags(w); + if (!(flags&DF_PRESENT)) return DT_NO_XFER; + if (flags&DF_USER) { + if (flags&DF_CODE) return (flags&DF_32? DT_XFER_CODE32:DT_XFER_CODE16); + else return (flags&DF_32? DT_XFER_DATA32:DT_XFER_DATA16); + } + return sysxfer[flags&15]; +} + + +/* ======================================================================= */ + + +int hsw_verr(unsigned short sel) +{ + unsigned short wFlags; + if (V86MODE()) return -1; /* maybe error */ + /* test for present && CPL>=DPL && readable */ + wFlags = GetSelectorFlags(sel); + if (wFlags & DF_PRESENT) { + if (!(wFlags & DF_CODE) || (wFlags & DF_CREADABLE)) + return 1; + } + return 0; /* not ok, ZF->0 */ +} + +int hsw_verw(unsigned short sel) +{ + unsigned short wFlags; + if (V86MODE()) return -1; /* maybe error */ + /* test for present && CPL>=DPL && writeable */ + wFlags = GetSelectorFlags(sel); + if (wFlags & DF_PRESENT) { + if (!(wFlags & DF_CODE) || (wFlags & DF_DWRITEABLE)) + return 1; + } + return 0; /* not ok, ZF->0 */ +} + + +/* ======================================================================= */ + +int e_larlsl(int mode, unsigned short sel) +{ + Descriptor *dt; + DTR *dtr; + int pl; + + dt = (sel & 4? LDT : GDT); + dtr = (sel & 4? &(TheCPU.LDTR) : &(TheCPU.GDTR)); + /* check DT and limits */ + if (dt==NULL || dt == GDT || ((sel&0xfff8) > dtr->Limit)) { + return 0; + } + /* check system segments if in GDT */ + if (dt==GDT && (GDT[sel>>3].S==0)) { + /* "is a valid descriptor type" */ + int styp=GDT[sel>>3].type; + if (styp==0 || styp==6 || styp==7 || styp==8 || + styp==10 || styp==13 || styp==14 || styp==15) return 0; + } + /* "if the selector is visible at the current privilege level + * (modified by the selector's RPL)" */ + pl = CPL; if ((sel&3)>pl) pl=(sel&3); + if (pl > dt[sel>>3].DPL) { + return 0; + } + /* Intel manual says nothing about present bit */ + /* is all ok now? */ + return 1; +} + +/* ======================================================================= */ + +/* called from dpmi.c */ +unsigned short emu_do_LAR (unsigned short selector) +{ + unsigned short flg=0; + if (LDT) { + flg = DT_FLAGS(&LDT[selector>>3]) & 0xff; + if (flg) LDT[selector>>3].type |= 1; + } + return flg; +} + +/* called from dpmi.c */ +void emu_mhp_SetTypebyte (unsigned short selector, int typebyte) +{ + if (LDT) { +#if 0 /* clean version */ + Descriptor *lp = &LDT[selector>>3]; + lp->type = typebyte & 15; + lp->S = typebyte>>4; + lp->DPL = (typebyte>>5)&3; + lp->present = typebyte>>7; +#else /* quick & dirty & little-endian too */ + char *lp = (char *)(&LDT[selector>>3]); + lp[5] = (char)typebyte; +#endif + } +} + +/* ======================================================================= */ + +int emu_ldt_write(dosaddr_t addr, uint32_t op, int len) +{ + static cpuctx_t sc = {0}; + cpuctx_t *scp = ≻ + + if (!(msdos_ldt_access(addr))) + return 0; + + _cr2 = addr; + _ds = TheCPU.ds; + _es = TheCPU.es; + _fs = TheCPU.fs; + _gs = TheCPU.gs; + msdos_ldt_write(scp, op, len, _cr2); + if (_ds == 0) { TheCPU.ds = 0; SetSegProt(0,Ofs_DS,NULL,0); } + if (_es == 0) { TheCPU.es = 0; SetSegProt(0,Ofs_ES,NULL,0); } + if (_fs == 0) { TheCPU.fs = 0; SetSegProt(0,Ofs_FS,NULL,0); } + if (_gs == 0) { TheCPU.gs = 0; SetSegProt(0,Ofs_GS,NULL,0); } + return 1; +} + +void emu_pagefault_handler(dosaddr_t addr, int err, uint32_t op, int len) +{ + if (!in_dpmi_emu && !in_vm86_emu) { + default_sim_pagefault_handler(addr, err, op, len); + return; + } + if ((err & 2) && emu_ldt_write(addr, op, len)) + return; + /* trigger an exception in DPMI */ + TheCPU.err = EXCP0E_PAGE; + TheCPU.scp_err = err; + TheCPU.cr2 = addr; + longjmp(jmp_env, 1); +} diff --git a/src/base/emu-i386/simx86/protmode.h b/src/base/emu-i386/simx86/protmode.h new file mode 100644 index 0000000..e8b514c --- /dev/null +++ b/src/base/emu-i386/simx86/protmode.h @@ -0,0 +1,241 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originaly was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#ifndef _EMU86_PROTMODE_H +#define _EMU86_PROTMODE_H + +#include "emu-ldt.h" + +typedef struct { + unsigned int BoundL,BoundH; + unsigned short Oldsel, Attrib __attribute__ ((packed)); +} SDTR; + +typedef struct { + unsigned int Base; + unsigned int Limit; + unsigned int Attrib; +} DTR; + +/* + * segment selectors + */ +#define SELECTOR_GDT 0 +#define SELECTOR_LDT 1 +#define SELECTOR_RPL_SHIFT 0 +#define SELECTOR_RPL_MASK 0x03 +#define SELECTOR_TI_SHIFT 2 +#define SELECTOR_TI_MASK 0x4 +#define SELECTOR_INDEX_SHIFT 3 +#define SELECTOR_INDEX_MASK 0xfff8 + +#define SELECTOR_RPL(_sel) ((_sel) & SELECTOR_RPL_MASK) +#define SELECTOR_INDEX(_sel) ((_sel) >> SELECTOR_INDEX_SHIFT) + +#define NULL_SELECTOR(_sel) (!((_sel) & ~SELECTOR_RPL_MASK)) + +/* + * segment descriptors - little endian + * + * 00-15 limit 15-0 + * 16-39 base 23-0 + * 40-43 type + * 44 DT + * 45-46 DPL + * 47 P + * 48-51 limit 19-16 + * 52 vf + * 53 r + * 54 DB + * 55 G + * 56-63 base 31-24 + * + * limit mask 000h0000.0000llll + * base mask hh0000mm.llll0000 + * flags mask 00f0ff00.00000000 + * + */ +/* descriptor byte 6 */ +#define DF_PAGES 0x8000 +#define DF_32 0x4000 + +/* descriptor byte 5 */ +#define DF_PRESENT 0x80 +#define DF_DPL 0x60 +#define DF_USER 0x10 +#define DF_CODE 0x08 +#define DF_DATA 0x00 +#define DF_EXPANDDOWN 0x04 +#define DF_CREADABLE 0x02 +#define DF_DWRITEABLE 0x02 +#define DF_ACCESSED 0x01 + +#define DT_CODE(dp) ( ((dp)->S) && (((dp)->type & 0x8) == 0x8)) +#define DT_CONFORMING_CODE(dp) ( ((dp)->S) && (((dp)->type & 0xc) == 0xc)) +#define DT_NONCONFORMING_CODE(dp) ( ((dp)->S) && (((dp)->type & 0xc) == 0x8)) +#define DT_READABLE_CODE(dp) ( ((dp)->S) && (((dp)->type & 0xa) == 0xa)) +#define DT_DATA(dp) ( ((dp)->S) && (((dp)->type & 0x8) == 0x0)) +#define DT_WRITEABLE_DATA(dp) ( ((dp)->S) && (((dp)->type & 0xa) == 0x2)) +#define DT_EXPAND_DOWN(dp) ( ((dp)->S) && (((dp)->type & 0xc) == 0x4)) +#define DT_CALL_GATE(dp) (!((dp)->S) && (((dp)->type & 0x7) == 0x4)) +#define DT_LDT(dp) (!((dp)->S) && (((dp)->type & 0xf) == 0x2)) +#define DT_TASK_GATE(dp) (!((dp)->S) && (((dp)->type & 0xf) == 0x5)) +#define DT_TSS(dp) (!((dp)->S) && (((dp)->type & 0x5) == 0x1)) +#define DT_AVAIL_TSS(dp) (!((dp)->S) && (((dp)->type & 0x7) == 0x1)) + +#define DT_ACCESS 0x2 +#define DT_32BIT 0x8 +#define DT_TSS_BUSY 0x2 + +#define GT_TASK(gp) (((gp)->type & 0x1f) == 0x05) +#define GT_INTR(gp) (((gp)->type & 0x17) == 0x06) +#define GT_TRAP(gp) (((gp)->type & 0x17) == 0x07) +#define GT_32BIT(gp) ((gp)->type & 0x08) +#define GT_CS(dp) ((dp)->seg) +#define GT_OFFS(dp) (((dp)->offs_hi<<16) | ((dp)->offs_lo)) + +#define DATA_DESC 0x2 +#define CODE_DESC 0xa +#define TASK_DESC 0x9 /* TSS available */ +#define TASK_DESC_BUSY 0xb /* TSS busy */ + +#define DT_NO_XFER 0 /* error */ + +#define DT_XFER_DATA 0x10 +#define DT_XFER_DATA16 0x10 /* 16bit data segment */ +#define DT_XFER_DATA32 0x11 /* 32bit data segment */ + +#define DT_XFER_CODE 0x20 +#define DT_RM_XFER 0x20 /* real/VM86 mode */ +#define DT_XFER_CODE16 0x20 /* 16bit code segment */ +#define DT_XFER_CODE32 0x21 /* 32bit code segment */ + +#define DT_XFER_SYS 0x40 /* mask: bit 6 */ +#define DT_XFER_TSS16 0x41 /* n.i.*/ +#define DT_XFER_LDT 0x42 +#define DT_XFER_CG16 0x43 /* n.i.*/ +#define DT_XFER_TSKG 0x44 +#define DT_XFER_IG16 0x45 /* n.i.*/ +#define DT_XFER_TRP16 0x46 /* n.i.*/ +#define DT_XFER_TSS32 0x47 +#define DT_XFER_CG32 0x48 +#define DT_XFER_IG32 0x49 +#define DT_XFER_TRP32 0x4a + +/* Segment types */ + /* 0x00F2 */ +#define ST_DATA16 DF_PRESENT|DF_DPL|DF_USER|DF_DATA|DF_DWRITEABLE + /* 0x00FA */ +#define ST_CODE16 DF_PRESENT|DF_DPL|DF_USER|DF_CODE|DF_CREADABLE + /* 0x00F6 */ +#define ST_STACK16 DF_PRESENT|DF_DPL|DF_USER|DF_DATA|DF_EXPANDDOWN| \ + DF_DWRITEABLE + /* 0x80F2 */ +#define ST_DATA32 DF_32|DF_PRESENT|DF_DPL|DF_USER|DF_DATA|DF_DWRITEABLE + /* 0x80FA */ +#define ST_CODE32 DF_32|DF_PRESENT|DF_DPL|DF_USER|DF_CODE|DF_CREADABLE + /* 0x80F6 */ +#define ST_STACK32 DF_32|DF_PRESENT|DF_DPL|DF_USER|DF_DATA|DF_EXPANDDOWN| \ + DF_DWRITEABLE + +#define IS_GDT(sel) (((sel)&4)==0) +#define IS_LDT(sel) (((sel)&4)!=0) +#define LDTorGDT(sel) (IS_LDT(sel)? LDT:GDT) + +/* Segment limit -- 64K */ +#define SL_DEFAULT (LPARAM)0xFFFF + +/* Macros to access fields in LGDT tables */ + +#define DTgetSelBase(w) ({Descriptor *t=LDTorGDT(w); \ + (t[w>>3].present? DT_BASE(&t[(w)>>3]):0); }) +#define DTsetSelBase(w,d) ({Descriptor *t=LDTorGDT(w); MKBASE(&t[(w)>>3],d); }) +#define DTgetSelLimit(w) ({Descriptor *t=LDTorGDT(w); DT_LIMIT(&t[(w)>>3]); }) +#define DTsetSelLimit(w,d) ({Descriptor *t=LDTorGDT(w); MKLIMIT(&t[(w)>>3],d); }) +#define DTgetFlags(w) ({Descriptor *t=LDTorGDT(w); DT_FLAGS(&t[(w)>>3]); }) +#define GetFlagAccessed(w) ({Descriptor *t=LDTorGDT(w); t[(w)>>3].type&1; }) +#define SetFlagAccessed(w) ({Descriptor *t=LDTorGDT(w); t[(w)>>3].type|=1; }) + +#define GetSelectorAddress(w) (DTgetSelBase(w)) +#define GetPhysicalAddress(w) GetSelectorAddress(w) + +#define SetPhysicalAddress(w,l) {if (PROTMODE()) DTsetSelBase(w,(unsigned long)l);} + +#define GetSelectorLimit(w) (DTgetSelLimit(w)) + +#define GetSelectorByteLimit(w) ({Descriptor *t=LDTorGDT(w); \ + ((t[(w)>>3].gran?\ + (DT_LIMIT(&t[(w)>>3])<<12)|0xfff :\ + DT_LIMIT(&t[(w)>>3]))); }) +#define GetSelectorAddrMax(w) (GetSelectorAddress(w)+GetSelectorByteLimit(w)) + +#define SetSelectorLimit(w,d) {if (PROTMODE()) DTsetSelLimit(w,(unsigned long)d);} + +#define GetSelectorFlags(w) (DTgetFlags(w)) + +#define GetSelectorType(w) ({Descriptor *t=LDTorGDT(w); \ + (t[(w)>>3].type); }) + +#define CopySelector(t,w1,w2) memcpy((char *)&t[w1>>3], \ + (char *)&t[w2>>3], \ + sizeof(Descriptor)); + +/* Messages for DPMI_Notify() */ +#if 0 +/* unused? - Bart DN_MODIFY is also in */ +#define DN_ASSIGN 1 +#define DN_FREE 2 +#define DN_INIT 3 +#define DN_MODIFY 4 +#define DN_EXIT 5 +#endif + +#define SELECTOR_PADDRESS(sel) GetPhysicalAddress(sel) +// +extern unsigned char e_ofsseg[]; +// +int SetSegProt(int a16, int ofs, unsigned char *big, unsigned long sel); +int SetSegReal(unsigned short sel, int ofs); +int e_larlsl(int mode, unsigned short sel); +int hsw_verr(unsigned short sel); +int hsw_verw(unsigned short sel); +int emu_ldt_write(dosaddr_t addr, uint32_t op, int len); +void emu_pagefault_handler(dosaddr_t addr, int err, uint32_t op, int len); +// + +extern Descriptor *GDT; +extern Descriptor *LDT; +extern Gatedesc *IDT; + +#endif diff --git a/src/base/emu-i386/simx86/sigsegv.c b/src/base/emu-i386/simx86/sigsegv.c new file mode 100644 index 0000000..dada026 --- /dev/null +++ b/src/base/emu-i386/simx86/sigsegv.c @@ -0,0 +1,309 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#include +#include +#include +#include +#include "utilities.h" +#include "emu86.h" +#include "codegen-arch.h" +#include "trees.h" +#include "emudpmi.h" +#include "../dosext/dpmi/msdos/msdos_ldt.h" + +#include "video.h" +#include "bios.h" +#include "memory.h" +#include "priv.h" +#include "mapping.h" + +/* ======================================================================= */ + +void e_VgaMovs(unsigned char **rdi, unsigned char **rsi, unsigned int rep, + int dp, unsigned int access) +{ + dosaddr_t edi = DOSADDR_REL(*rdi); + dosaddr_t esi = DOSADDR_REL(*rsi); + +#ifdef DEBUG_VGA + e_printf("eVGAEmuFault: Movs ESI=%08x EDI=%08x ECX=%08x\n",esi,edi,rep); +#endif + switch (access) { + case 1: /* reading from VGA to mem */ + switch (abs(dp)) { + case 1: /* byte move */ + while (rep--) { + WRITE_BYTE(edi, vga_read(esi)); + esi+=dp,edi+=dp; + } + break; + case 2: /* word move */ + while (rep--) { + WRITE_WORD(edi, vga_read_word(esi)); + esi+=dp,edi+=dp; + } + break; + case 4: /* long move */ + while (rep--) { + WRITE_DWORD(edi, vga_read_dword(esi)); + esi+=dp,edi+=dp; + } + break; + } + break; + case 2: /* writing from mem to VGA */ + switch (abs(dp)) { + case 1: /* byte move */ + while (rep--) { + vga_write(edi,READ_BYTE(esi)); + esi+=dp,edi+=dp; + } + break; + case 2: /* word move */ + while (rep--) { + vga_write_word(edi,READ_WORD(esi)); + esi+=dp,edi+=dp; + } + break; + case 4: /* long move */ + while (rep--) { + vga_write_dword(edi,READ_DWORD(esi)); + esi+=dp,edi+=dp; + } + break; + } + break; + case 3: /* VGA to VGA */ + switch (abs(dp)) { + case 1: /* byte move */ + while (rep--) { + vga_write(edi,vga_read(esi)); + esi+=dp,edi+=dp; + } + break; + case 2: /* word move */ + while (rep--) { + vga_write_word(edi,vga_read_word(esi)); + esi+=dp,edi+=dp; + } + break; + case 4: /* long move */ + while (rep--) { + vga_write_dword(edi,vga_read_dword(esi)); + esi+=dp,edi+=dp; + } + break; + } + break; + } + *rsi = MEM_BASE32(esi); + *rdi = MEM_BASE32(edi); +} + +#if 1 +static int jitx86_instr_len(const unsigned char *rip) +{ + const unsigned char *p = rip; + int len; + + if (*p==0xf3) p++; /* rep */ + if (*p==0x66) p++; /* word */ + len = p - rip + 2; + + /* moves */ + if (*p >= 0x88 && *p <= 0x8b) { + int disp32_still_possible = 0; + switch (p[1] & 0xc0) { /* decode modifier */ + case 0: /* disp32 possible */ + switch (p[1] & 0x07) { /* decode rm */ + case 5: /* disp32 actually here */ + len += 4; + break; + case 4: /* disp32 is still possible in SIB byte */ + disp32_still_possible = 1; + break; + } + break; + case 0x40: /* have disp8 */ + len++; + break; + case 0x80: /* have disp32 */ + len += 4; + break; + } + if ((p[1] & 0xc0) < 0xc0 /* SIB byte possible */ + && (p[1] & 0x07) == 4) { /* SIB actually here */ + len++; + if (disp32_still_possible && (p[2] & 0x07) == 5) /* disp32 used by SIB */ + len += 4; + } + return len; + } + /* string moves */ + if (*p >= 0xa4 && *p <= 0xad) + return p - rip + 1; + return 0; +} + +static int e_vgaemu_fault(sigcontext_t *scp, unsigned page_fault) +{ + int i, j; + unsigned vga_page = 0, u=0; + + for (i = 0; i < VGAEMU_MAX_MAPPINGS; i++) { + j = page_fault - vga.mem.map[i].base_page; + if (j >= 0 && j < vga.mem.map[i].pages) { + vga_page = j + vga.mem.map[i].first_page; +#ifdef DEBUG_VGA + dbug_printf("eVGAEmuFault: found vga_page %x map %d\n",vga_page,i); +#endif + break; + } + } + + if (i == VGAEMU_MAX_MAPPINGS) { + if ((unsigned)((page_fault << 12) - vga.mem.graph_base) < + vga.mem.graph_size) { /* unmapped VGA area */ + u = jitx86_instr_len((unsigned char *)_scp_rip); + _scp_rip += u; + if (u==0) { + e_printf("eVGAEmuFault: unknown instruction, page at 0x%05x now writable\n", page_fault << 12); + vga_emu_protect_page(page_fault, 2); +/**/ leavedos_main(0x5640); + } + return 1; + } + else if (memcheck_is_rom(page_fault << PAGE_SHIFT)) { /* ROM area */ + u = jitx86_instr_len((unsigned char *)_scp_rip); + _scp_rip += u; + if (u==0 || (_scp_err&2)==0) { + e_printf("eVGAEmuFault: unknown instruction, converting ROM to RAM at 0x%05x\n", page_fault << 12); + vga_emu_protect_page(page_fault, 2); +/**/ leavedos_main(0x5641); + } + return 1; + } + else { + if (debug_level('e')>1) + dbug_printf("eVGAEmuFault: unhandled page fault (not in range)\n"); + return 0; + } + } + + if (vga_page < vga.mem.pages) { +/**/ e_printf("eVGAEmuFault: trying %08x\n",*((int *)_scp_rip)); + /* try CPatch, which should not fail */ + if (Cpatch(scp)) + return 1; + } + + error("eVGAEmuFault: unimplemented decode instr at %08"PRI_RG": %08x\n", + _scp_rip, *((int *)_scp_rip)); + leavedos_from_sig(0x5643); + return 0; +} +#endif + +/* ======================================================================= */ +/* + * DANG_BEGIN_FUNCTION dosemu_fault(int, sigcontext_t); + * + * All CPU exceptions (except 13=general_protection from V86 mode, + * which is directly scanned by the kernel) are handled here. + * + * DANG_END_FUNCTION + */ + +#define GetSegmentBaseAddress(s) GetSegmentBase(s) + +/* this function is called from dosemu_fault */ +static int e_emu_pagefault(sigcontext_t *scp, int pmode) +{ + if (InCompiledCode) { + dosaddr_t cr2 = DOSADDR_REL(LINP(_scp_cr2)); + if (e_vgaemu_fault(scp, cr2 >> 12) == 1) + return 1; + +#ifdef HOST_ARCH_X86 + if (e_handle_pagefault(cr2, _scp_err, scp)) + return 1; +#endif + /* use CPatch for LDT page faults, which should not fail */ + if (msdos_ldt_access(cr2) && Cpatch(scp)) + return 1; + TheCPU.scp_err = _scp_err; + /* save eip, eflags, and do a "ret" out of compiled code */ + TheCPU.err = EXCP0E_PAGE; + _scp_eax = FindPC((unsigned char *)_scp_rip); + e_printf("FindPC: found %x\n",_scp_eax); + _scp_edx = *(long *)_scp_rsp; // flags + _scp_rsp += sizeof(long); + TheCPU.cr2 = cr2; + _scp_rip = *(long *)_scp_rsp; + _scp_rsp += sizeof(long); + return 1; + } + return 0; +} + +int e_emu_fault(sigcontext_t *scp, int in_vm86) +{ + /* Possibilities: + * 1. Compiled code touches VGA prot + * 2. Compiled code touches cpuemu prot + * 3. Compiled code touches DPMI prot + * 4. reserved, was "fullsim code touches DPMI prot", but fullsim + * no longer faults since commit 70ca014459 + * 5. dosemu code touches cpuemu prot (bug) + * Compiled code means dpmi-jit, otherwise vm86 not here. + */ + if (_scp_trapno == 0x0e) { + /* cases 1, 2, 3 */ + if ((in_vm86 || EMU_DPMI()) && e_emu_pagefault(scp, !in_vm86)) + return 1; + /* case 5, any jit, bug */ + if (e_handle_pagefault(DOSADDR_REL(LINP(_scp_cr2)), _scp_err, scp)) { + dosemu_error("touched jit-protected page%s\n", + in_vm86 ? " in vm86-emu" : ""); + return 1; + } + } else if ((in_vm86 || EMU_DPMI()) && e_handle_fault(scp)) { + /* compiled code can cause fault (usually DE, Divide Exception) */ + return 1; + } + return 0; +} + +/* ======================================================================= */ diff --git a/src/base/emu-i386/simx86/softfloat/Makefile b/src/base/emu-i386/simx86/softfloat/Makefile new file mode 100644 index 0000000..439028a --- /dev/null +++ b/src/base/emu-i386/simx86/softfloat/Makefile @@ -0,0 +1,13 @@ +# +# (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". +# +# for details see file COPYING.DOSEMU in the DOSEMU distribution +# + +top_builddir=../../../../.. +include $(top_builddir)/Makefile.conf + + +CFILES = softfloat.c + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/emu-i386/simx86/softfloat/softfloat-macros.h b/src/base/emu-i386/simx86/softfloat/softfloat-macros.h new file mode 100644 index 0000000..e737775 --- /dev/null +++ b/src/base/emu-i386/simx86/softfloat/softfloat-macros.h @@ -0,0 +1,606 @@ +/* + * QEMU float support macros + * + * Derived from SoftFloat. + */ + +/*============================================================================ + +This C source fragment is part of the SoftFloat IEC/IEEE Floating-point +Arithmetic Package, Release 2b. + +Written by John R. Hauser. This work was made possible in part by the +International Computer Science Institute, located at Suite 600, 1947 Center +Street, Berkeley, California 94704. Funding was partially provided by the +National Science Foundation under grant MIP-9311980. The original version +of this code was written as part of a project to build a fixed-point vector +processor in collaboration with the University of California at Berkeley, +overseen by Profs. Nelson Morgan and John Wawrzynek. More information +is available through the Web page `http://www.cs.berkeley.edu/~jhauser/ +arithmetic/SoftFloat.html'. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has +been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES +RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS +AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES, +COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE +EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE +INSTITUTE (possibly via similar legal notice) AGAINST ALL LOSSES, COSTS, OR +OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE. + +Derivative works are acceptable, even for commercial purposes, so long as +(1) the source code for the derivative work includes prominent notice that +the work is derivative, and (2) the source code includes prominent notice with +these four paragraphs for those parts of this code that are retained. + +=============================================================================*/ + +/*---------------------------------------------------------------------------- +| This macro tests for minimum version of the GNU C compiler. +*----------------------------------------------------------------------------*/ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +#define SOFTFLOAT_GNUC_PREREQ(maj, min) ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#else +#define SOFTFLOAT_GNUC_PREREQ(maj, min) 0 +#endif + +/*---------------------------------------------------------------------------- +| Shifts `a' right by the number of bits given in `count'. If any nonzero +| bits are shifted off, they are ``jammed'' into the least significant bit of +| the result by setting the least significant bit to 1. The value of `count' +| can be arbitrarily large; in particular, if `count' is greater than 32, the +| result will be either 0 or 1, depending on whether `a' is zero or nonzero. +| The result is stored in the location pointed to by `zPtr'. +*----------------------------------------------------------------------------*/ + +SINLINE void shift32RightJamming(uint32_t a, int16 count, uint32_t *zPtr) { + uint32_t z; + + if (count == 0) { + z = a; + } else if (count < 32) { + z = (a >> count) | ((a << ((-count) & 31)) != 0); + } else { + z = (a != 0); + } + *zPtr = z; +} + +/*---------------------------------------------------------------------------- +| Shifts `a' right by the number of bits given in `count'. If any nonzero +| bits are shifted off, they are ``jammed'' into the least significant bit of +| the result by setting the least significant bit to 1. The value of `count' +| can be arbitrarily large; in particular, if `count' is greater than 64, the +| result will be either 0 or 1, depending on whether `a' is zero or nonzero. +| The result is stored in the location pointed to by `zPtr'. +*----------------------------------------------------------------------------*/ + +SINLINE void shift64RightJamming(uint64_t a, int16 count, uint64_t *zPtr) { + uint64_t z; + + if (count == 0) { + z = a; + } else if (count < 64) { + z = (a >> count) | ((a << ((-count) & 63)) != 0); + } else { + z = (a != 0); + } + *zPtr = z; +} + +/*---------------------------------------------------------------------------- +| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by 64 +| _plus_ the number of bits given in `count'. The shifted result is at most +| 64 nonzero bits; this is stored at the location pointed to by `z0Ptr'. The +| bits shifted off form a second 64-bit result as follows: The _last_ bit +| shifted off is the most-significant bit of the extra result, and the other +| 63 bits of the extra result are all zero if and only if _all_but_the_last_ +| bits shifted off were all zero. This extra result is stored in the location +| pointed to by `z1Ptr'. The value of `count' can be arbitrarily large. +| (This routine makes more sense if `a0' and `a1' are considered to form +| a fixed-point value with binary point between `a0' and `a1'. This fixed- +| point value is shifted right by the number of bits given in `count', and +| the integer part of the result is returned at the location pointed to by +| `z0Ptr'. The fractional part of the result may be slightly corrupted as +| described above, and is returned at the location pointed to by `z1Ptr'.) +*----------------------------------------------------------------------------*/ + +SINLINE void shift64ExtraRightJamming(uint64_t a0, uint64_t a1, int16 count, uint64_t *z0Ptr, uint64_t *z1Ptr) { + uint64_t z0, z1; + int8 negCount = (-count) & 63; + + if (count == 0) { + z1 = a1; + z0 = a0; + } else if (count < 64) { + z1 = (a0 << negCount) | (a1 != 0); + z0 = a0 >> count; + } else { + if (count == 64) { + z1 = a0 | (a1 != 0); + } else { + z1 = ((a0 | a1) != 0); + } + z0 = 0; + } + *z1Ptr = z1; + *z0Ptr = z0; +} + +/*---------------------------------------------------------------------------- +| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the +| number of bits given in `count'. Any bits shifted off are lost. The value +| of `count' can be arbitrarily large; in particular, if `count' is greater +| than 128, the result will be 0. The result is broken into two 64-bit pieces +| which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. +*----------------------------------------------------------------------------*/ + +SINLINE void shift128Right(uint64_t a0, uint64_t a1, int16 count, uint64_t *z0Ptr, uint64_t *z1Ptr) { + uint64_t z0, z1; + int8 negCount = (-count) & 63; + + if (count == 0) { + z1 = a1; + z0 = a0; + } else if (count < 64) { + z1 = (a0 << negCount) | (a1 >> count); + z0 = a0 >> count; + } else { + z1 = (count < 64) ? (a0 >> (count & 63)) : 0; + z0 = 0; + } + *z1Ptr = z1; + *z0Ptr = z0; +} + +/*---------------------------------------------------------------------------- +| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the +| number of bits given in `count'. If any nonzero bits are shifted off, they +| are ``jammed'' into the least significant bit of the result by setting the +| least significant bit to 1. The value of `count' can be arbitrarily large; +| in particular, if `count' is greater than 128, the result will be either +| 0 or 1, depending on whether the concatenation of `a0' and `a1' is zero or +| nonzero. The result is broken into two 64-bit pieces which are stored at +| the locations pointed to by `z0Ptr' and `z1Ptr'. +*----------------------------------------------------------------------------*/ + +SINLINE void shift128RightJamming(uint64_t a0, uint64_t a1, int16 count, uint64_t *z0Ptr, uint64_t *z1Ptr) { + uint64_t z0, z1; + int8 negCount = (-count) & 63; + + if (count == 0) { + z1 = a1; + z0 = a0; + } else if (count < 64) { + z1 = (a0 << negCount) | (a1 >> count) | ((a1 << negCount) != 0); + z0 = a0 >> count; + } else { + if (count == 64) { + z1 = a0 | (a1 != 0); + } else if (count < 128) { + z1 = (a0 >> (count & 63)) | (((a0 << negCount) | a1) != 0); + } else { + z1 = ((a0 | a1) != 0); + } + z0 = 0; + } + *z1Ptr = z1; + *z0Ptr = z0; +} + +/*---------------------------------------------------------------------------- +| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' right +| by 64 _plus_ the number of bits given in `count'. The shifted result is +| at most 128 nonzero bits; these are broken into two 64-bit pieces which are +| stored at the locations pointed to by `z0Ptr' and `z1Ptr'. The bits shifted +| off form a third 64-bit result as follows: The _last_ bit shifted off is +| the most-significant bit of the extra result, and the other 63 bits of the +| extra result are all zero if and only if _all_but_the_last_ bits shifted off +| were all zero. This extra result is stored in the location pointed to by +| `z2Ptr'. The value of `count' can be arbitrarily large. +| (This routine makes more sense if `a0', `a1', and `a2' are considered +| to form a fixed-point value with binary point between `a1' and `a2'. This +| fixed-point value is shifted right by the number of bits given in `count', +| and the integer part of the result is returned at the locations pointed to +| by `z0Ptr' and `z1Ptr'. The fractional part of the result may be slightly +| corrupted as described above, and is returned at the location pointed to by +| `z2Ptr'.) +*----------------------------------------------------------------------------*/ + +SINLINE void shift128ExtraRightJamming(uint64_t a0, uint64_t a1, uint64_t a2, int16 count, uint64_t *z0Ptr, + uint64_t *z1Ptr, uint64_t *z2Ptr) { + uint64_t z0, z1, z2; + int8 negCount = (-count) & 63; + + if (count == 0) { + z2 = a2; + z1 = a1; + z0 = a0; + } else { + if (count < 64) { + z2 = a1 << negCount; + z1 = (a0 << negCount) | (a1 >> count); + z0 = a0 >> count; + } else { + if (count == 64) { + z2 = a1; + z1 = a0; + } else { + a2 |= a1; + if (count < 128) { + z2 = a0 << negCount; + z1 = a0 >> (count & 63); + } else { + z2 = (count == 128) ? a0 : (a0 != 0); + z1 = 0; + } + } + z0 = 0; + } + z2 |= (a2 != 0); + } + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; +} + +/*---------------------------------------------------------------------------- +| Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the +| number of bits given in `count'. Any bits shifted off are lost. The value +| of `count' must be less than 64. The result is broken into two 64-bit +| pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. +*----------------------------------------------------------------------------*/ + +SINLINE void shortShift128Left(uint64_t a0, uint64_t a1, int16 count, uint64_t *z0Ptr, uint64_t *z1Ptr) { + + *z1Ptr = a1 << count; + *z0Ptr = (count == 0) ? a0 : (a0 << count) | (a1 >> ((-count) & 63)); +} + +/*---------------------------------------------------------------------------- +| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' left +| by the number of bits given in `count'. Any bits shifted off are lost. +| The value of `count' must be less than 64. The result is broken into three +| 64-bit pieces which are stored at the locations pointed to by `z0Ptr', +| `z1Ptr', and `z2Ptr'. +*----------------------------------------------------------------------------*/ + +SINLINE void shortShift192Left(uint64_t a0, uint64_t a1, uint64_t a2, int16 count, uint64_t *z0Ptr, uint64_t *z1Ptr, + uint64_t *z2Ptr) { + uint64_t z0, z1, z2; + int8 negCount; + + z2 = a2 << count; + z1 = a1 << count; + z0 = a0 << count; + if (0 < count) { + negCount = ((-count) & 63); + z1 |= a2 >> negCount; + z0 |= a1 >> negCount; + } + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; +} + +/*---------------------------------------------------------------------------- +| Adds the 128-bit value formed by concatenating `a0' and `a1' to the 128-bit +| value formed by concatenating `b0' and `b1'. Addition is modulo 2^128, so +| any carry out is lost. The result is broken into two 64-bit pieces which +| are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. +*----------------------------------------------------------------------------*/ + +SINLINE void add128(uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1, uint64_t *z0Ptr, uint64_t *z1Ptr) { + uint64_t z1; + + z1 = a1 + b1; + *z1Ptr = z1; + *z0Ptr = a0 + b0 + (z1 < a1); +} + +/*---------------------------------------------------------------------------- +| Adds the 192-bit value formed by concatenating `a0', `a1', and `a2' to the +| 192-bit value formed by concatenating `b0', `b1', and `b2'. Addition is +| modulo 2^192, so any carry out is lost. The result is broken into three +| 64-bit pieces which are stored at the locations pointed to by `z0Ptr', +| `z1Ptr', and `z2Ptr'. +*----------------------------------------------------------------------------*/ + +SINLINE void add192(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t b0, uint64_t b1, uint64_t b2, uint64_t *z0Ptr, + uint64_t *z1Ptr, uint64_t *z2Ptr) { + uint64_t z0, z1, z2; + int8 carry0, carry1; + + z2 = a2 + b2; + carry1 = (z2 < a2); + z1 = a1 + b1; + carry0 = (z1 < a1); + z0 = a0 + b0; + z1 += carry1; + z0 += (z1 < carry1); + z0 += carry0; + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; +} + +/*---------------------------------------------------------------------------- +| Subtracts the 128-bit value formed by concatenating `b0' and `b1' from the +| 128-bit value formed by concatenating `a0' and `a1'. Subtraction is modulo +| 2^128, so any borrow out (carry out) is lost. The result is broken into two +| 64-bit pieces which are stored at the locations pointed to by `z0Ptr' and +| `z1Ptr'. +*----------------------------------------------------------------------------*/ + +SINLINE void sub128(uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1, uint64_t *z0Ptr, uint64_t *z1Ptr) { + + *z1Ptr = a1 - b1; + *z0Ptr = a0 - b0 - (a1 < b1); +} + +/*---------------------------------------------------------------------------- +| Subtracts the 192-bit value formed by concatenating `b0', `b1', and `b2' +| from the 192-bit value formed by concatenating `a0', `a1', and `a2'. +| Subtraction is modulo 2^192, so any borrow out (carry out) is lost. The +| result is broken into three 64-bit pieces which are stored at the locations +| pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'. +*----------------------------------------------------------------------------*/ + +SINLINE void sub192(uint64_t a0, uint64_t a1, uint64_t a2, uint64_t b0, uint64_t b1, uint64_t b2, uint64_t *z0Ptr, + uint64_t *z1Ptr, uint64_t *z2Ptr) { + uint64_t z0, z1, z2; + int8 borrow0, borrow1; + + z2 = a2 - b2; + borrow1 = (a2 < b2); + z1 = a1 - b1; + borrow0 = (a1 < b1); + z0 = a0 - b0; + z0 -= (z1 < borrow1); + z1 -= borrow1; + z0 -= borrow0; + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; +} + +/*---------------------------------------------------------------------------- +| Multiplies `a' by `b' to obtain a 128-bit product. The product is broken +| into two 64-bit pieces which are stored at the locations pointed to by +| `z0Ptr' and `z1Ptr'. +*----------------------------------------------------------------------------*/ + +SINLINE void mul64To128(uint64_t a, uint64_t b, uint64_t *z0Ptr, uint64_t *z1Ptr) { + uint32_t aHigh, aLow, bHigh, bLow; + uint64_t z0, zMiddleA, zMiddleB, z1; + + aLow = a; + aHigh = a >> 32; + bLow = b; + bHigh = b >> 32; + z1 = ((uint64_t) aLow) * bLow; + zMiddleA = ((uint64_t) aLow) * bHigh; + zMiddleB = ((uint64_t) aHigh) * bLow; + z0 = ((uint64_t) aHigh) * bHigh; + zMiddleA += zMiddleB; + z0 += (((uint64_t)(zMiddleA < zMiddleB)) << 32) + (zMiddleA >> 32); + zMiddleA <<= 32; + z1 += zMiddleA; + z0 += (z1 < zMiddleA); + *z1Ptr = z1; + *z0Ptr = z0; +} + +/*---------------------------------------------------------------------------- +| Multiplies the 128-bit value formed by concatenating `a0' and `a1' by +| `b' to obtain a 192-bit product. The product is broken into three 64-bit +| pieces which are stored at the locations pointed to by `z0Ptr', `z1Ptr', and +| `z2Ptr'. +*----------------------------------------------------------------------------*/ + +SINLINE void mul128By64To192(uint64_t a0, uint64_t a1, uint64_t b, uint64_t *z0Ptr, uint64_t *z1Ptr, uint64_t *z2Ptr) { + uint64_t z0, z1, z2, more1; + + mul64To128(a1, b, &z1, &z2); + mul64To128(a0, b, &z0, &more1); + add128(z0, more1, 0, z1, &z0, &z1); + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; +} + +/*---------------------------------------------------------------------------- +| Multiplies the 128-bit value formed by concatenating `a0' and `a1' to the +| 128-bit value formed by concatenating `b0' and `b1' to obtain a 256-bit +| product. The product is broken into four 64-bit pieces which are stored at +| the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'. +*----------------------------------------------------------------------------*/ + +SINLINE void mul128To256(uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1, uint64_t *z0Ptr, uint64_t *z1Ptr, + uint64_t *z2Ptr, uint64_t *z3Ptr) { + uint64_t z0, z1, z2, z3; + uint64_t more1, more2; + + mul64To128(a1, b1, &z2, &z3); + mul64To128(a1, b0, &z1, &more2); + add128(z1, more2, 0, z2, &z1, &z2); + mul64To128(a0, b0, &z0, &more1); + add128(z0, more1, 0, z1, &z0, &z1); + mul64To128(a0, b1, &more1, &more2); + add128(more1, more2, 0, z2, &more1, &z2); + add128(z0, z1, 0, more1, &z0, &z1); + *z3Ptr = z3; + *z2Ptr = z2; + *z1Ptr = z1; + *z0Ptr = z0; +} + +/*---------------------------------------------------------------------------- +| Returns an approximation to the 64-bit integer quotient obtained by dividing +| `b' into the 128-bit value formed by concatenating `a0' and `a1'. The +| divisor `b' must be at least 2^63. If q is the exact quotient truncated +| toward zero, the approximation returned lies between q and q + 2 inclusive. +| If the exact quotient q is larger than 64 bits, the maximum positive 64-bit +| unsigned integer is returned. +*----------------------------------------------------------------------------*/ + +static uint64_t estimateDiv128To64(uint64_t a0, uint64_t a1, uint64_t b) { + uint64_t b0, b1; + uint64_t rem0, rem1, term0, term1; + uint64_t z; + + if (b <= a0) + return LIT64(0xFFFFFFFFFFFFFFFF); + b0 = b >> 32; + z = (b0 << 32 <= a0) ? LIT64(0xFFFFFFFF00000000) : (a0 / b0) << 32; + mul64To128(b, z, &term0, &term1); + sub128(a0, a1, term0, term1, &rem0, &rem1); + while (((int64_t) rem0) < 0) { + z -= LIT64(0x100000000); + b1 = b << 32; + add128(rem0, rem1, b0, b1, &rem0, &rem1); + } + rem0 = (rem0 << 32) | (rem1 >> 32); + z |= (b0 << 32 <= rem0) ? 0xFFFFFFFF : rem0 / b0; + return z; +} + +/*---------------------------------------------------------------------------- +| Returns an approximation to the square root of the 32-bit significand given +| by `a'. Considered as an integer, `a' must be at least 2^31. If bit 0 of +| `aExp' (the least significant bit) is 1, the integer returned approximates +| 2^31*sqrt(`a'/2^31), where `a' is considered an integer. If bit 0 of `aExp' +| is 0, the integer returned approximates 2^31*sqrt(`a'/2^30). In either +| case, the approximation returned lies strictly within +/-2 of the exact +| value. +*----------------------------------------------------------------------------*/ + +static uint32_t estimateSqrt32(int16 aExp, uint32_t a) { + static const uint16_t sqrtOddAdjustments[] = {0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0, + 0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67}; + static const uint16_t sqrtEvenAdjustments[] = {0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E, + 0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002}; + int8 index; + uint32_t z; + + index = (a >> 27) & 15; + if (aExp & 1) { + z = 0x4000 + (a >> 17) - sqrtOddAdjustments[(int) index]; + z = ((a / z) << 14) + (z << 15); + a >>= 1; + } else { + z = 0x8000 + (a >> 17) - sqrtEvenAdjustments[(int) index]; + z = a / z + z; + z = (0x20000 <= z) ? 0xFFFF8000 : (z << 15); + if (z <= a) + return (uint32_t)(((int32_t) a) >> 1); + } + return ((uint32_t)((((uint64_t) a) << 31) / z)) + (z >> 1); +} + +/*---------------------------------------------------------------------------- +| Returns the number of leading 0 bits before the most-significant 1 bit of +| `a'. If `a' is zero, 32 is returned. +*----------------------------------------------------------------------------*/ + +static int8 countLeadingZeros32(uint32_t a) { +#if SOFTFLOAT_GNUC_PREREQ(3, 4) + if (a) { + return __builtin_clz(a); + } else { + return 32; + } +#else + static const int8 countLeadingZerosHigh[] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int8 shiftCount; + + shiftCount = 0; + if (a < 0x10000) { + shiftCount += 16; + a <<= 16; + } + if (a < 0x1000000) { + shiftCount += 8; + a <<= 8; + } + shiftCount += countLeadingZerosHigh[a >> 24]; + return shiftCount; +#endif +} + +/*---------------------------------------------------------------------------- +| Returns the number of leading 0 bits before the most-significant 1 bit of +| `a'. If `a' is zero, 64 is returned. +*----------------------------------------------------------------------------*/ + +static int8 countLeadingZeros64(uint64_t a) { +#if SOFTFLOAT_GNUC_PREREQ(3, 4) + if (a) { + return __builtin_clzll(a); + } else { + return 64; + } +#else + int8 shiftCount; + + shiftCount = 0; + if (a < ((uint64_t) 1) << 32) { + shiftCount += 32; + } else { + a >>= 32; + } + shiftCount += countLeadingZeros32(a); + return shiftCount; +#endif +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' +| is equal to the 128-bit value formed by concatenating `b0' and `b1'. +| Otherwise, returns 0. +*----------------------------------------------------------------------------*/ + +SINLINE flag eq128(uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1) { + + return (a0 == b0) && (a1 == b1); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less +| than or equal to the 128-bit value formed by concatenating `b0' and `b1'. +| Otherwise, returns 0. +*----------------------------------------------------------------------------*/ + +SINLINE flag le128(uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1) { + + return (a0 < b0) || ((a0 == b0) && (a1 <= b1)); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less +| than the 128-bit value formed by concatenating `b0' and `b1'. Otherwise, +| returns 0. +*----------------------------------------------------------------------------*/ + +SINLINE flag lt128(uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1) { + + return (a0 < b0) || ((a0 == b0) && (a1 < b1)); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is +| not equal to the 128-bit value formed by concatenating `b0' and `b1'. +| Otherwise, returns 0. +*----------------------------------------------------------------------------*/ + +SINLINE flag ne128(uint64_t a0, uint64_t a1, uint64_t b0, uint64_t b1) { + + return (a0 != b0) || (a1 != b1); +} diff --git a/src/base/emu-i386/simx86/softfloat/softfloat-specialize.h b/src/base/emu-i386/simx86/softfloat/softfloat-specialize.h new file mode 100644 index 0000000..45ff01a --- /dev/null +++ b/src/base/emu-i386/simx86/softfloat/softfloat-specialize.h @@ -0,0 +1,993 @@ +/* + * QEMU float support + * + * Derived from SoftFloat. + */ + +/*============================================================================ + +This C source fragment is part of the SoftFloat IEC/IEEE Floating-point +Arithmetic Package, Release 2b. + +Written by John R. Hauser. This work was made possible in part by the +International Computer Science Institute, located at Suite 600, 1947 Center +Street, Berkeley, California 94704. Funding was partially provided by the +National Science Foundation under grant MIP-9311980. The original version +of this code was written as part of a project to build a fixed-point vector +processor in collaboration with the University of California at Berkeley, +overseen by Profs. Nelson Morgan and John Wawrzynek. More information +is available through the Web page `http://www.cs.berkeley.edu/~jhauser/ +arithmetic/SoftFloat.html'. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has +been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES +RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS +AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES, +COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE +EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE +INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR +OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE. + +Derivative works are acceptable, even for commercial purposes, so long as +(1) the source code for the derivative work includes prominent notice that +the work is derivative, and (2) the source code includes prominent notice with +these four paragraphs for those parts of this code that are retained. + +=============================================================================*/ + +#if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) +#define SNAN_BIT_IS_ONE 1 +#else +#define SNAN_BIT_IS_ONE 0 +#endif + +/*---------------------------------------------------------------------------- +| The pattern for a default generated half-precision NaN. +*----------------------------------------------------------------------------*/ +#if defined(TARGET_ARM) +const float16 float16_default_nan = const_float16(0x7E00); +#elif SNAN_BIT_IS_ONE +const float16 float16_default_nan = const_float16(0x7DFF); +#else +const float16 float16_default_nan = const_float16(0xFE00); +#endif + +/*---------------------------------------------------------------------------- +| The pattern for a default generated single-precision NaN. +*----------------------------------------------------------------------------*/ +#if defined(TARGET_SPARC) +const float32 float32_default_nan = const_float32(0x7FFFFFFF); +#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) +const float32 float32_default_nan = const_float32(0x7FC00000); +#elif SNAN_BIT_IS_ONE +const float32 float32_default_nan = const_float32(0x7FBFFFFF); +#else +const float32 float32_default_nan = const_float32(0xFFC00000); +#endif + +/*---------------------------------------------------------------------------- +| The pattern for a default generated double-precision NaN. +*----------------------------------------------------------------------------*/ +#if defined(TARGET_SPARC) +const float64 float64_default_nan = const_float64(LIT64(0x7FFFFFFFFFFFFFFF)); +#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) +const float64 float64_default_nan = const_float64(LIT64(0x7FF8000000000000)); +#elif SNAN_BIT_IS_ONE +const float64 float64_default_nan = const_float64(LIT64(0x7FF7FFFFFFFFFFFF)); +#else +const float64 float64_default_nan = const_float64(LIT64(0xFFF8000000000000)); +#endif + +/*---------------------------------------------------------------------------- +| The pattern for a default generated extended double-precision NaN. +*----------------------------------------------------------------------------*/ +#if SNAN_BIT_IS_ONE +#define floatx80_default_nan_high 0x7FFF +#define floatx80_default_nan_low LIT64(0xBFFFFFFFFFFFFFFF) +#else +#define floatx80_default_nan_high 0xFFFF +#define floatx80_default_nan_low LIT64(0xC000000000000000) +#endif + +const floatx80 floatx80_default_nan = make_floatx80_init(floatx80_default_nan_high, floatx80_default_nan_low); + +/*---------------------------------------------------------------------------- +| The pattern for a default generated quadruple-precision NaN. The `high' and +| `low' values hold the most- and least-significant bits, respectively. +*----------------------------------------------------------------------------*/ +#if SNAN_BIT_IS_ONE +#define float128_default_nan_high LIT64(0x7FFF7FFFFFFFFFFF) +#define float128_default_nan_low LIT64(0xFFFFFFFFFFFFFFFF) +#else +#define float128_default_nan_high LIT64(0xFFFF800000000000) +#define float128_default_nan_low LIT64(0x0000000000000000) +#endif + +const float128 float128_default_nan = make_float128_init(float128_default_nan_high, float128_default_nan_low); + +/*---------------------------------------------------------------------------- +| Raises the exceptions specified by `flags'. Floating-point traps can be +| defined here if desired. It is currently not possible for such a trap +| to substitute a result value. If traps are not implemented, this routine +| should be simply `float_exception_flags |= flags;'. +*----------------------------------------------------------------------------*/ + +void float_raise(uint8_t flags STATUS_PARAM) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | flags)); +} + +/*---------------------------------------------------------------------------- +| Internal canonical NaN format. +*----------------------------------------------------------------------------*/ +typedef struct { + flag sign; + uint64_t high, low; +} commonNaNT; + +/*---------------------------------------------------------------------------- +| Returns 1 if the half-precision floating-point value `a' is a quiet +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +int float16_is_quiet_nan(float16 a_) { + uint16_t a = float16_val(a_); +#if SNAN_BIT_IS_ONE + return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); +#else + return ((a & ~0x8000) >= 0x7c80); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the half-precision floating-point value `a' is a signaling +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +int float16_is_signaling_nan(float16 a_) { + uint16_t a = float16_val(a_); +#if SNAN_BIT_IS_ONE + return ((a & ~0x8000) >= 0x7c80); +#else + return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns a quiet NaN if the half-precision floating point value `a' is a +| signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ +float16 float16_maybe_silence_nan(float16 a_) { + if (float16_is_signaling_nan(a_)) { +#if SNAN_BIT_IS_ONE +#if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) + return float16_default_nan; +#else +#error Rules for silencing a signaling NaN are target-specific +#endif +#else + uint16_t a = float16_val(a_); + a |= (1 << 9); + return make_float16(a); +#endif + } + return a_; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the half-precision floating-point NaN +| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid +| exception is raised. +*----------------------------------------------------------------------------*/ + +static commonNaNT float16ToCommonNaN(float16 a STATUS_PARAM) { + commonNaNT z; + + if (float16_is_signaling_nan(a)) + float_raise(float_flag_invalid STATUS_VAR); + z.sign = float16_val(a) >> 15; + z.low = 0; + z.high = ((uint64_t) float16_val(a)) << 54; + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the canonical NaN `a' to the half- +| precision floating-point format. +*----------------------------------------------------------------------------*/ + +static float16 commonNaNToFloat16(commonNaNT a STATUS_PARAM) { + uint16_t mantissa = a.high >> 54; + + if (STATUS(default_nan_mode)) { + return float16_default_nan; + } + + if (mantissa) { + return make_float16(((((uint16_t) a.sign) << 15) | (0x1F << 10) | mantissa)); + } else { + return float16_default_nan; + } +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is a quiet +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +int float32_is_quiet_nan(float32 a_) { + uint32_t a = float32_val(a_); +#if SNAN_BIT_IS_ONE + return (((a >> 22) & 0x1FF) == 0x1FE) && (a & 0x003FFFFF); +#else + return (0xFF800000 <= (uint32_t)(a << 1)); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is a signaling +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +int float32_is_signaling_nan(float32 a_) { + uint32_t a = float32_val(a_); +#if SNAN_BIT_IS_ONE + return (0xFF800000 <= (uint32_t)(a << 1)); +#else + return (((a >> 22) & 0x1FF) == 0x1FE) && (a & 0x003FFFFF); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns a quiet NaN if the single-precision floating point value `a' is a +| signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +float32 float32_maybe_silence_nan(float32 a_) { + if (float32_is_signaling_nan(a_)) { +#if SNAN_BIT_IS_ONE +#if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) + return float32_default_nan; +#else +#error Rules for silencing a signaling NaN are target-specific +#endif +#else + uint32_t a = float32_val(a_); + a |= (1 << 22); + return make_float32(a); +#endif + } + return a_; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point NaN +| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid +| exception is raised. +*----------------------------------------------------------------------------*/ + +static commonNaNT float32ToCommonNaN(float32 a STATUS_PARAM) { + commonNaNT z; + + if (float32_is_signaling_nan(a)) + float_raise(float_flag_invalid STATUS_VAR); + z.sign = float32_val(a) >> 31; + z.low = 0; + z.high = ((uint64_t) float32_val(a)) << 41; + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the canonical NaN `a' to the single- +| precision floating-point format. +*----------------------------------------------------------------------------*/ + +static float32 commonNaNToFloat32(commonNaNT a STATUS_PARAM) { + uint32_t mantissa = a.high >> 41; + + if (STATUS(default_nan_mode)) { + return float32_default_nan; + } + + if (mantissa) + return make_float32((((uint32_t) a.sign) << 31) | 0x7F800000 | (a.high >> 41)); + else + return float32_default_nan; +} + +/*---------------------------------------------------------------------------- +| Select which NaN to propagate for a two-input operation. +| IEEE754 doesn't specify all the details of this, so the +| algorithm is target-specific. +| The routine is passed various bits of information about the +| two NaNs and should return 0 to select NaN a and 1 for NaN b. +| Note that signalling NaNs are always squashed to quiet NaNs +| by the caller, by calling floatXX_maybe_silence_nan() before +| returning them. +| +| aIsLargerSignificand is only valid if both a and b are NaNs +| of some kind, and is true if a has the larger significand, +| or if both a and b have the same significand but a is +| positive but b is negative. It is only needed for the x87 +| tie-break rule. +*----------------------------------------------------------------------------*/ + +#if defined(TARGET_ARM) +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag aIsLargerSignificand) { + /* ARM mandated NaN propagation rules: take the first of: + * 1. A if it is signaling + * 2. B if it is signaling + * 3. A (quiet) + * 4. B (quiet) + * A signaling NaN is always quietened before returning it. + */ + if (aIsSNaN) { + return 0; + } else if (bIsSNaN) { + return 1; + } else if (aIsQNaN) { + return 0; + } else { + return 1; + } +} +#elif defined(TARGET_MIPS) +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag aIsLargerSignificand) { + /* According to MIPS specifications, if one of the two operands is + * a sNaN, a new qNaN has to be generated. This is done in + * floatXX_maybe_silence_nan(). For qNaN inputs the specifications + * says: "When possible, this QNaN result is one of the operand QNaN + * values." In practice it seems that most implementations choose + * the first operand if both operands are qNaN. In short this gives + * the following rules: + * 1. A if it is signaling + * 2. B if it is signaling + * 3. A (quiet) + * 4. B (quiet) + * A signaling NaN is always silenced before returning it. + */ + if (aIsSNaN) { + return 0; + } else if (bIsSNaN) { + return 1; + } else if (aIsQNaN) { + return 0; + } else { + return 1; + } +} +#elif defined(TARGET_PPC) +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag aIsLargerSignificand) { + /* PowerPC propagation rules: + * 1. A if it sNaN or qNaN + * 2. B if it sNaN or qNaN + * A signaling NaN is always silenced before returning it. + */ + if (aIsSNaN || aIsQNaN) { + return 0; + } else { + return 1; + } +} +#else +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag aIsLargerSignificand) { + /* This implements x87 NaN propagation rules: + * SNaN + QNaN => return the QNaN + * two SNaNs => return the one with the larger significand, silenced + * two QNaNs => return the one with the larger significand + * SNaN and a non-NaN => return the SNaN, silenced + * QNaN and a non-NaN => return the QNaN + * + * If we get down to comparing significands and they are the same, + * return the NaN with the positive sign bit (if any). + */ + if (aIsSNaN) { + if (bIsSNaN) { + return aIsLargerSignificand ? 0 : 1; + } + return bIsQNaN ? 1 : 0; + } else if (aIsQNaN) { + if (bIsSNaN || !bIsQNaN) + return 0; + else { + return aIsLargerSignificand ? 0 : 1; + } + } else { + return 1; + } +} +#endif + +/*---------------------------------------------------------------------------- +| Select which NaN to propagate for a three-input operation. +| For the moment we assume that no CPU needs the 'larger significand' +| information. +| Return values : 0 : a; 1 : b; 2 : c; 3 : default-NaN +*----------------------------------------------------------------------------*/ +#if defined(TARGET_ARM) +static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag cIsQNaN, flag cIsSNaN, + flag infzero STATUS_PARAM) { + /* For ARM, the (inf,zero,qnan) case sets InvalidOp and returns + * the default NaN + */ + if (infzero && cIsQNaN) { + float_raise(float_flag_invalid STATUS_VAR); + return 3; + } + + /* This looks different from the ARM ARM pseudocode, because the ARM ARM + * puts the operands to a fused mac operation (a*b)+c in the order c,a,b. + */ + if (cIsSNaN) { + return 2; + } else if (aIsSNaN) { + return 0; + } else if (bIsSNaN) { + return 1; + } else if (cIsQNaN) { + return 2; + } else if (aIsQNaN) { + return 0; + } else { + return 1; + } +} +#elif defined(TARGET_PPC) +static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag cIsQNaN, flag cIsSNaN, + flag infzero STATUS_PARAM) { + /* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer + * to return an input NaN if we have one (ie c) rather than generating + * a default NaN + */ + if (infzero) { + float_raise(float_flag_invalid STATUS_VAR); + return 2; + } + + /* If fRA is a NaN return it; otherwise if fRB is a NaN return it; + * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB + */ + if (aIsSNaN || aIsQNaN) { + return 0; + } else if (cIsSNaN || cIsQNaN) { + return 2; + } else { + return 1; + } +} +#else +/* A default implementation: prefer a to b to c. + * This is unlikely to actually match any real implementation. + */ +static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag cIsQNaN, flag cIsSNaN, + flag infzero STATUS_PARAM) { + if (aIsSNaN || aIsQNaN) { + return 0; + } else if (bIsSNaN || bIsQNaN) { + return 1; + } else { + return 2; + } +} +#endif + +/*---------------------------------------------------------------------------- +| Takes two single-precision floating-point values `a' and `b', one of which +| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a +| signaling NaN, the invalid exception is raised. +*----------------------------------------------------------------------------*/ + +static float32 propagateFloat32NaN(float32 a, float32 b STATUS_PARAM) { + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; + flag aIsLargerSignificand; + uint32_t av, bv; + + aIsQuietNaN = float32_is_quiet_nan(a); + aIsSignalingNaN = float32_is_signaling_nan(a); + bIsQuietNaN = float32_is_quiet_nan(b); + bIsSignalingNaN = float32_is_signaling_nan(b); + av = float32_val(a); + bv = float32_val(b); + + if (aIsSignalingNaN | bIsSignalingNaN) + float_raise(float_flag_invalid STATUS_VAR); + + if (STATUS(default_nan_mode)) + return float32_default_nan; + + if ((uint32_t)(av << 1) < (uint32_t)(bv << 1)) { + aIsLargerSignificand = 0; + } else if ((uint32_t)(bv << 1) < (uint32_t)(av << 1)) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (av < bv) ? 1 : 0; + } + + if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, aIsLargerSignificand)) { + return float32_maybe_silence_nan(b); + } else { + return float32_maybe_silence_nan(a); + } +} + +/*---------------------------------------------------------------------------- +| Takes three single-precision floating-point values `a', `b' and `c', one of +| which is a NaN, and returns the appropriate NaN result. If any of `a', +| `b' or `c' is a signaling NaN, the invalid exception is raised. +| The input infzero indicates whether a*b was 0*inf or inf*0 (in which case +| obviously c is a NaN, and whether to propagate c or some other NaN is +| implementation defined). +*----------------------------------------------------------------------------*/ + +static float32 propagateFloat32MulAddNaN(float32 a, float32 b, float32 c, flag infzero STATUS_PARAM) { + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN; + int which; + + aIsQuietNaN = float32_is_quiet_nan(a); + aIsSignalingNaN = float32_is_signaling_nan(a); + bIsQuietNaN = float32_is_quiet_nan(b); + bIsSignalingNaN = float32_is_signaling_nan(b); + cIsQuietNaN = float32_is_quiet_nan(c); + cIsSignalingNaN = float32_is_signaling_nan(c); + + if (aIsSignalingNaN | bIsSignalingNaN | cIsSignalingNaN) { + float_raise(float_flag_invalid STATUS_VAR); + } + + which = pickNaNMulAdd(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN, + infzero STATUS_VAR); + + if (STATUS(default_nan_mode)) { + /* Note that this check is after pickNaNMulAdd so that function + * has an opportunity to set the Invalid flag. + */ + return float32_default_nan; + } + + switch (which) { + case 0: + return float32_maybe_silence_nan(a); + case 1: + return float32_maybe_silence_nan(b); + case 2: + return float32_maybe_silence_nan(c); + case 3: + default: + return float32_default_nan; + } +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is a quiet +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +int float64_is_quiet_nan(float64 a_) { + uint64_t a = float64_val(a_); +#if SNAN_BIT_IS_ONE + return (((a >> 51) & 0xFFF) == 0xFFE) && (a & LIT64(0x0007FFFFFFFFFFFF)); +#else + return (LIT64(0xFFF0000000000000) <= (uint64_t)(a << 1)); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is a signaling +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +int float64_is_signaling_nan(float64 a_) { + uint64_t a = float64_val(a_); +#if SNAN_BIT_IS_ONE + return (LIT64(0xFFF0000000000000) <= (uint64_t)(a << 1)); +#else + return (((a >> 51) & 0xFFF) == 0xFFE) && (a & LIT64(0x0007FFFFFFFFFFFF)); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns a quiet NaN if the double-precision floating point value `a' is a +| signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +float64 float64_maybe_silence_nan(float64 a_) { + if (float64_is_signaling_nan(a_)) { +#if SNAN_BIT_IS_ONE +#if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) + return float64_default_nan; +#else +#error Rules for silencing a signaling NaN are target-specific +#endif +#else + uint64_t a = float64_val(a_); + a |= LIT64(0x0008000000000000); + return make_float64(a); +#endif + } + return a_; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point NaN +| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid +| exception is raised. +*----------------------------------------------------------------------------*/ + +static commonNaNT float64ToCommonNaN(float64 a STATUS_PARAM) { + commonNaNT z; + + if (float64_is_signaling_nan(a)) + float_raise(float_flag_invalid STATUS_VAR); + z.sign = float64_val(a) >> 63; + z.low = 0; + z.high = float64_val(a) << 12; + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the canonical NaN `a' to the double- +| precision floating-point format. +*----------------------------------------------------------------------------*/ + +static float64 commonNaNToFloat64(commonNaNT a STATUS_PARAM) { + uint64_t mantissa = a.high >> 12; + + if (STATUS(default_nan_mode)) { + return float64_default_nan; + } + + if (mantissa) + return make_float64((((uint64_t) a.sign) << 63) | LIT64(0x7FF0000000000000) | (a.high >> 12)); + else + return float64_default_nan; +} + +/*---------------------------------------------------------------------------- +| Takes two double-precision floating-point values `a' and `b', one of which +| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a +| signaling NaN, the invalid exception is raised. +*----------------------------------------------------------------------------*/ + +static float64 propagateFloat64NaN(float64 a, float64 b STATUS_PARAM) { + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; + flag aIsLargerSignificand; + uint64_t av, bv; + + aIsQuietNaN = float64_is_quiet_nan(a); + aIsSignalingNaN = float64_is_signaling_nan(a); + bIsQuietNaN = float64_is_quiet_nan(b); + bIsSignalingNaN = float64_is_signaling_nan(b); + av = float64_val(a); + bv = float64_val(b); + + if (aIsSignalingNaN | bIsSignalingNaN) + float_raise(float_flag_invalid STATUS_VAR); + + if (STATUS(default_nan_mode)) + return float64_default_nan; + + if ((uint64_t)(av << 1) < (uint64_t)(bv << 1)) { + aIsLargerSignificand = 0; + } else if ((uint64_t)(bv << 1) < (uint64_t)(av << 1)) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (av < bv) ? 1 : 0; + } + + if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, aIsLargerSignificand)) { + return float64_maybe_silence_nan(b); + } else { + return float64_maybe_silence_nan(a); + } +} + +/*---------------------------------------------------------------------------- +| Takes three double-precision floating-point values `a', `b' and `c', one of +| which is a NaN, and returns the appropriate NaN result. If any of `a', +| `b' or `c' is a signaling NaN, the invalid exception is raised. +| The input infzero indicates whether a*b was 0*inf or inf*0 (in which case +| obviously c is a NaN, and whether to propagate c or some other NaN is +| implementation defined). +*----------------------------------------------------------------------------*/ + +static float64 propagateFloat64MulAddNaN(float64 a, float64 b, float64 c, flag infzero STATUS_PARAM) { + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN; + int which; + + aIsQuietNaN = float64_is_quiet_nan(a); + aIsSignalingNaN = float64_is_signaling_nan(a); + bIsQuietNaN = float64_is_quiet_nan(b); + bIsSignalingNaN = float64_is_signaling_nan(b); + cIsQuietNaN = float64_is_quiet_nan(c); + cIsSignalingNaN = float64_is_signaling_nan(c); + + if (aIsSignalingNaN | bIsSignalingNaN | cIsSignalingNaN) { + float_raise(float_flag_invalid STATUS_VAR); + } + + which = pickNaNMulAdd(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, cIsQuietNaN, cIsSignalingNaN, + infzero STATUS_VAR); + + if (STATUS(default_nan_mode)) { + /* Note that this check is after pickNaNMulAdd so that function + * has an opportunity to set the Invalid flag. + */ + return float64_default_nan; + } + + switch (which) { + case 0: + return float64_maybe_silence_nan(a); + case 1: + return float64_maybe_silence_nan(b); + case 2: + return float64_maybe_silence_nan(c); + case 3: + default: + return float64_default_nan; + } +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is a +| quiet NaN; otherwise returns 0. This slightly differs from the same +| function for other types as floatx80 has an explicit bit. +*----------------------------------------------------------------------------*/ + +int floatx80_is_quiet_nan(floatx80 a) { +#if SNAN_BIT_IS_ONE + uint64_t aLow; + + aLow = a.low & ~LIT64(0x4000000000000000); + return ((a.high & 0x7FFF) == 0x7FFF) && (uint64_t)(aLow << 1) && (a.low == aLow); +#else + return ((a.high & 0x7FFF) == 0x7FFF) && (LIT64(0x8000000000000000) <= ((uint64_t)(a.low << 1))); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is a +| signaling NaN; otherwise returns 0. This slightly differs from the same +| function for other types as floatx80 has an explicit bit. +*----------------------------------------------------------------------------*/ + +int floatx80_is_signaling_nan(floatx80 a) { +#if SNAN_BIT_IS_ONE + return ((a.high & 0x7FFF) == 0x7FFF) && (LIT64(0x8000000000000000) <= ((uint64_t)(a.low << 1))); +#else + uint64_t aLow; + + aLow = a.low & ~LIT64(0x4000000000000000); + return ((a.high & 0x7FFF) == 0x7FFF) && (uint64_t)(aLow << 1) && (a.low == aLow); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns a quiet NaN if the extended double-precision floating point value +| `a' is a signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_maybe_silence_nan(floatx80 a) { + if (floatx80_is_signaling_nan(a)) { +#if SNAN_BIT_IS_ONE +#if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; +#else +#error Rules for silencing a signaling NaN are target-specific +#endif +#else + a.low |= LIT64(0xC000000000000000); + return a; +#endif + } + return a; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point NaN `a' to the canonical NaN format. If `a' is a signaling NaN, the +| invalid exception is raised. +*----------------------------------------------------------------------------*/ + +static commonNaNT floatx80ToCommonNaN(floatx80 a STATUS_PARAM) { + commonNaNT z; + + if (floatx80_is_signaling_nan(a)) + float_raise(float_flag_invalid STATUS_VAR); + if (a.low >> 63) { + z.sign = a.high >> 15; + z.low = 0; + z.high = a.low << 1; + } else { + z.sign = floatx80_default_nan_high >> 15; + z.low = 0; + z.high = floatx80_default_nan_low << 1; + } + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the canonical NaN `a' to the extended +| double-precision floating-point format. +*----------------------------------------------------------------------------*/ + +static floatx80 commonNaNToFloatx80(commonNaNT a STATUS_PARAM) { + floatx80 z; + + if (STATUS(default_nan_mode)) { + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + + if (a.high >> 1) { + z.low = LIT64(0x8000000000000000) | a.high >> 1; + z.high = (((uint16_t) a.sign) << 15) | 0x7FFF; + } else { + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + } + + return z; +} + +/*---------------------------------------------------------------------------- +| Takes two extended double-precision floating-point values `a' and `b', one +| of which is a NaN, and returns the appropriate NaN result. If either `a' or +| `b' is a signaling NaN, the invalid exception is raised. +*----------------------------------------------------------------------------*/ + +static floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b STATUS_PARAM) { + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; + flag aIsLargerSignificand; + + aIsQuietNaN = floatx80_is_quiet_nan(a); + aIsSignalingNaN = floatx80_is_signaling_nan(a); + bIsQuietNaN = floatx80_is_quiet_nan(b); + bIsSignalingNaN = floatx80_is_signaling_nan(b); + + if (aIsSignalingNaN | bIsSignalingNaN) + float_raise(float_flag_invalid STATUS_VAR); + + if (STATUS(default_nan_mode)) { + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; + return a; + } + + if (a.low < b.low) { + aIsLargerSignificand = 0; + } else if (b.low < a.low) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (a.high < b.high) ? 1 : 0; + } + + if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, aIsLargerSignificand)) { + return floatx80_maybe_silence_nan(b); + } else { + return floatx80_maybe_silence_nan(a); + } +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is a quiet +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +int float128_is_quiet_nan(float128 a) { +#if SNAN_BIT_IS_ONE + return (((a.high >> 47) & 0xFFFF) == 0xFFFE) && (a.low || (a.high & LIT64(0x00007FFFFFFFFFFF))); +#else + return (LIT64(0xFFFE000000000000) <= (uint64_t)(a.high << 1)) && (a.low || (a.high & LIT64(0x0000FFFFFFFFFFFF))); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is a +| signaling NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +int float128_is_signaling_nan(float128 a) { +#if SNAN_BIT_IS_ONE + return (LIT64(0xFFFE000000000000) <= (uint64_t)(a.high << 1)) && (a.low || (a.high & LIT64(0x0000FFFFFFFFFFFF))); +#else + return (((a.high >> 47) & 0xFFFF) == 0xFFFE) && (a.low || (a.high & LIT64(0x00007FFFFFFFFFFF))); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns a quiet NaN if the quadruple-precision floating point value `a' is +| a signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +float128 float128_maybe_silence_nan(float128 a) { + if (float128_is_signaling_nan(a)) { +#if SNAN_BIT_IS_ONE +#if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) + a.low = float128_default_nan_low; + a.high = float128_default_nan_high; +#else +#error Rules for silencing a signaling NaN are target-specific +#endif +#else + a.high |= LIT64(0x0000800000000000); + return a; +#endif + } + return a; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point NaN +| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid +| exception is raised. +*----------------------------------------------------------------------------*/ + +static commonNaNT float128ToCommonNaN(float128 a STATUS_PARAM) { + commonNaNT z; + + if (float128_is_signaling_nan(a)) + float_raise(float_flag_invalid STATUS_VAR); + z.sign = a.high >> 63; + shortShift128Left(a.high, a.low, 16, &z.high, &z.low); + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the canonical NaN `a' to the quadruple- +| precision floating-point format. +*----------------------------------------------------------------------------*/ + +static float128 commonNaNToFloat128(commonNaNT a STATUS_PARAM) { + float128 z; + + if (STATUS(default_nan_mode)) { + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + + shift128Right(a.high, a.low, 16, &z.high, &z.low); + z.high |= (((uint64_t) a.sign) << 63) | LIT64(0x7FFF000000000000); + return z; +} + +/*---------------------------------------------------------------------------- +| Takes two quadruple-precision floating-point values `a' and `b', one of +| which is a NaN, and returns the appropriate NaN result. If either `a' or +| `b' is a signaling NaN, the invalid exception is raised. +*----------------------------------------------------------------------------*/ + +static float128 propagateFloat128NaN(float128 a, float128 b STATUS_PARAM) { + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; + flag aIsLargerSignificand; + + aIsQuietNaN = float128_is_quiet_nan(a); + aIsSignalingNaN = float128_is_signaling_nan(a); + bIsQuietNaN = float128_is_quiet_nan(b); + bIsSignalingNaN = float128_is_signaling_nan(b); + + if (aIsSignalingNaN | bIsSignalingNaN) + float_raise(float_flag_invalid STATUS_VAR); + + if (STATUS(default_nan_mode)) { + a.low = float128_default_nan_low; + a.high = float128_default_nan_high; + return a; + } + + if (lt128(a.high << 1, a.low, b.high << 1, b.low)) { + aIsLargerSignificand = 0; + } else if (lt128(b.high << 1, b.low, a.high << 1, a.low)) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (a.high < b.high) ? 1 : 0; + } + + if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, aIsLargerSignificand)) { + return float128_maybe_silence_nan(b); + } else { + return float128_maybe_silence_nan(a); + } +} diff --git a/src/base/emu-i386/simx86/softfloat/softfloat.c b/src/base/emu-i386/simx86/softfloat/softfloat.c new file mode 100644 index 0000000..ed50bc9 --- /dev/null +++ b/src/base/emu-i386/simx86/softfloat/softfloat.c @@ -0,0 +1,6384 @@ +/* + * QEMU float support + * + * Derived from SoftFloat. + */ + +/*============================================================================ + +This C source file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic +Package, Release 2b. + +Written by John R. Hauser. This work was made possible in part by the +International Computer Science Institute, located at Suite 600, 1947 Center +Street, Berkeley, California 94704. Funding was partially provided by the +National Science Foundation under grant MIP-9311980. The original version +of this code was written as part of a project to build a fixed-point vector +processor in collaboration with the University of California at Berkeley, +overseen by Profs. Nelson Morgan and John Wawrzynek. More information +is available through the Web page `http://www.cs.berkeley.edu/~jhauser/ +arithmetic/SoftFloat.html'. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has +been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES +RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS +AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES, +COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE +EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE +INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR +OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE. + +Derivative works are acceptable, even for commercial purposes, so long as +(1) the source code for the derivative work includes prominent notice that +the work is derivative, and (2) the source code includes prominent notice with +these four paragraphs for those parts of this code that are retained. + +=============================================================================*/ + +/* softfloat (and in particular the code in softfloat-specialize.h) is + * target-dependent and needs the TARGET_* macros. + */ +//#include + +#include "softfloat.h" + +/*---------------------------------------------------------------------------- +| Primitive arithmetic functions, including multi-word arithmetic, and +| division and square root approximations. (Can be specialized to target if +| desired.) +*----------------------------------------------------------------------------*/ +#include "softfloat-macros.h" + +/*---------------------------------------------------------------------------- +| Functions and definitions to determine: (1) whether tininess for underflow +| is detected before or after rounding by default, (2) what (if anything) +| happens when exceptions are raised, (3) how signaling NaNs are distinguished +| from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs +| are propagated from function inputs to output. These details are target- +| specific. +*----------------------------------------------------------------------------*/ +#include "softfloat-specialize.h" + +void set_float_rounding_mode(int val STATUS_PARAM) { + STATUS_W(float_rounding_mode, val); +} + +void set_float_exception_flags(int val STATUS_PARAM) { + STATUS_W(float_exception_flags, val); +} + +void set_floatx80_rounding_precision(int val STATUS_PARAM) { + STATUS_W(floatx80_rounding_precision, val); +} + +/*---------------------------------------------------------------------------- +| Returns the fraction bits of the half-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE uint32_t extractFloat16Frac(float16 a) { + return float16_val(a) & 0x3ff; +} + +/*---------------------------------------------------------------------------- +| Returns the exponent bits of the half-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE int16 extractFloat16Exp(float16 a) { + return (float16_val(a) >> 10) & 0x1f; +} + +/*---------------------------------------------------------------------------- +| Returns the sign bit of the single-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE flag extractFloat16Sign(float16 a) { + return float16_val(a) >> 15; +} + +/*---------------------------------------------------------------------------- +| Takes a 64-bit fixed-point value `absZ' with binary point between bits 6 +| and 7, and returns the properly rounded 32-bit integer corresponding to the +| input. If `zSign' is 1, the input is negated before being converted to an +| integer. Bit 63 of `absZ' must be zero. Ordinarily, the fixed-point input +| is simply rounded to an integer, with the inexact exception raised if the +| input cannot be represented exactly as an integer. However, if the fixed- +| point input is too large, the invalid exception is raised and the largest +| positive or negative integer is returned. +*----------------------------------------------------------------------------*/ + +static int32 roundAndPackInt32(flag zSign, uint64_t absZ STATUS_PARAM) { + int8 roundingMode; + flag roundNearestEven; + int8 roundIncrement, roundBits; + int32_t z; + + roundingMode = STATUS(float_rounding_mode); + roundNearestEven = (roundingMode == float_round_nearest_even); + roundIncrement = 0x40; + if (!roundNearestEven) { + if (roundingMode == float_round_to_zero) { + roundIncrement = 0; + } else { + roundIncrement = 0x7F; + if (zSign) { + if (roundingMode == float_round_up) + roundIncrement = 0; + } else { + if (roundingMode == float_round_down) + roundIncrement = 0; + } + } + } + roundBits = absZ & 0x7F; + absZ = (absZ + roundIncrement) >> 7; + absZ &= ~(((roundBits ^ 0x40) == 0) & roundNearestEven); + z = absZ; + if (zSign) + z = -z; + if ((absZ >> 32) || (z && ((z < 0) ^ zSign))) { + float_raise(float_flag_invalid STATUS_VAR); + return zSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; + } + if (roundBits) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + return z; +} + +/*---------------------------------------------------------------------------- +| Takes the 128-bit fixed-point value formed by concatenating `absZ0' and +| `absZ1', with binary point between bits 63 and 64 (between the input words), +| and returns the properly rounded 64-bit integer corresponding to the input. +| If `zSign' is 1, the input is negated before being converted to an integer. +| Ordinarily, the fixed-point input is simply rounded to an integer, with +| the inexact exception raised if the input cannot be represented exactly as +| an integer. However, if the fixed-point input is too large, the invalid +| exception is raised and the largest positive or negative integer is +| returned. +*----------------------------------------------------------------------------*/ + +static int64 roundAndPackInt64(flag zSign, uint64_t absZ0, uint64_t absZ1 STATUS_PARAM) { + int8 roundingMode; + flag roundNearestEven, increment; + int64_t z; + + roundingMode = STATUS(float_rounding_mode); + roundNearestEven = (roundingMode == float_round_nearest_even); + increment = ((int64_t) absZ1 < 0); + if (!roundNearestEven) { + if (roundingMode == float_round_to_zero) { + increment = 0; + } else { + if (zSign) { + increment = (roundingMode == float_round_down) && absZ1; + } else { + increment = (roundingMode == float_round_up) && absZ1; + } + } + } + if (increment) { + ++absZ0; + if (absZ0 == 0) + goto overflow; + absZ0 &= ~(((uint64_t)(absZ1 << 1) == 0) & roundNearestEven); + } + z = absZ0; + if (zSign) + z = -z; + if (z && ((z < 0) ^ zSign)) { + overflow: + float_raise(float_flag_invalid STATUS_VAR); + return zSign ? (int64_t) LIT64(0x8000000000000000) : LIT64(0x7FFFFFFFFFFFFFFF); + } + if (absZ1) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the fraction bits of the single-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE uint32_t extractFloat32Frac(float32 a) { + + return float32_val(a) & 0x007FFFFF; +} + +/*---------------------------------------------------------------------------- +| Returns the exponent bits of the single-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE int16 extractFloat32Exp(float32 a) { + + return (float32_val(a) >> 23) & 0xFF; +} + +/*---------------------------------------------------------------------------- +| Returns the sign bit of the single-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE flag extractFloat32Sign(float32 a) { + + return float32_val(a) >> 31; +} + +/*---------------------------------------------------------------------------- +| If `a' is denormal and we are in flush-to-zero mode then set the +| input-denormal exception and return zero. Otherwise just return the value. +*----------------------------------------------------------------------------*/ +static float32 float32_squash_input_denormal(float32 a STATUS_PARAM) { + if (STATUS(flush_inputs_to_zero)) { + if (extractFloat32Exp(a) == 0 && extractFloat32Frac(a) != 0) { + float_raise(float_flag_input_denormal STATUS_VAR); + return make_float32(float32_val(a) & 0x80000000); + } + } + return a; +} + +/*---------------------------------------------------------------------------- +| Normalizes the subnormal single-precision floating-point value represented +| by the denormalized significand `aSig'. The normalized exponent and +| significand are stored at the locations pointed to by `zExpPtr' and +| `zSigPtr', respectively. +*----------------------------------------------------------------------------*/ + +static void normalizeFloat32Subnormal(uint32_t aSig, int16 *zExpPtr, uint32_t *zSigPtr) { + int8 shiftCount; + + shiftCount = countLeadingZeros32(aSig) - 8; + *zSigPtr = aSig << shiftCount; + *zExpPtr = 1 - shiftCount; +} + +/*---------------------------------------------------------------------------- +| Packs the sign `zSign', exponent `zExp', and significand `zSig' into a +| single-precision floating-point value, returning the result. After being +| shifted into the proper positions, the three fields are simply added +| together to form the result. This means that any integer portion of `zSig' +| will be added into the exponent. Since a properly normalized significand +| will have an integer portion equal to 1, the `zExp' input should be 1 less +| than the desired result exponent whenever `zSig' is a complete, normalized +| significand. +*----------------------------------------------------------------------------*/ + +SINLINE float32 packFloat32(flag zSign, int16 zExp, uint32_t zSig) { + + return make_float32((((uint32_t) zSign) << 31) + (((uint32_t) zExp) << 23) + zSig); +} + +/*---------------------------------------------------------------------------- +| Takes an abstract floating-point value having sign `zSign', exponent `zExp', +| and significand `zSig', and returns the proper single-precision floating- +| point value corresponding to the abstract input. Ordinarily, the abstract +| value is simply rounded and packed into the single-precision format, with +| the inexact exception raised if the abstract input cannot be represented +| exactly. However, if the abstract value is too large, the overflow and +| inexact exceptions are raised and an infinity or maximal finite value is +| returned. If the abstract value is too small, the input value is rounded to +| a subnormal number, and the underflow and inexact exceptions are raised if +| the abstract input cannot be represented exactly as a subnormal single- +| precision floating-point number. +| The input significand `zSig' has its binary point between bits 30 +| and 29, which is 7 bits to the left of the usual location. This shifted +| significand must be normalized or smaller. If `zSig' is not normalized, +| `zExp' must be 0; in that case, the result returned is a subnormal number, +| and it must not require rounding. In the usual case that `zSig' is +| normalized, `zExp' must be 1 less than the ``true'' floating-point exponent. +| The handling of underflow and overflow follows the IEC/IEEE Standard for +| Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float32 roundAndPackFloat32(flag zSign, int16 zExp, uint32_t zSig STATUS_PARAM) { + int8 roundingMode; + flag roundNearestEven; + int8 roundIncrement, roundBits; + flag isTiny; + + roundingMode = STATUS(float_rounding_mode); + roundNearestEven = (roundingMode == float_round_nearest_even); + roundIncrement = 0x40; + if (!roundNearestEven) { + if (roundingMode == float_round_to_zero) { + roundIncrement = 0; + } else { + roundIncrement = 0x7F; + if (zSign) { + if (roundingMode == float_round_up) + roundIncrement = 0; + } else { + if (roundingMode == float_round_down) + roundIncrement = 0; + } + } + } + roundBits = zSig & 0x7F; + if (0xFD <= (uint16_t) zExp) { + if ((0xFD < zExp) || ((zExp == 0xFD) && ((int32_t)(zSig + roundIncrement) < 0))) { + float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + return packFloat32(zSign, 0xFF, -(roundIncrement == 0)); + } + if (zExp < 0) { + if (STATUS(flush_to_zero)) { + float_raise(float_flag_output_denormal STATUS_VAR); + return packFloat32(zSign, 0, 0); + } + isTiny = (STATUS(float_detect_tininess) == float_tininess_before_rounding) || (zExp < -1) || + (zSig + roundIncrement < 0x80000000); + shift32RightJamming(zSig, -zExp, &zSig); + zExp = 0; + roundBits = zSig & 0x7F; + if (isTiny && roundBits) + float_raise(float_flag_underflow STATUS_VAR); + } + } + if (roundBits) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + zSig = (zSig + roundIncrement) >> 7; + zSig &= ~(((roundBits ^ 0x40) == 0) & roundNearestEven); + if (zSig == 0) + zExp = 0; + return packFloat32(zSign, zExp, zSig); +} + +/*---------------------------------------------------------------------------- +| Takes an abstract floating-point value having sign `zSign', exponent `zExp', +| and significand `zSig', and returns the proper single-precision floating- +| point value corresponding to the abstract input. This routine is just like +| `roundAndPackFloat32' except that `zSig' does not have to be normalized. +| Bit 31 of `zSig' must be zero, and `zExp' must be 1 less than the ``true'' +| floating-point exponent. +*----------------------------------------------------------------------------*/ + +static float32 normalizeRoundAndPackFloat32(flag zSign, int16 zExp, uint32_t zSig STATUS_PARAM) { + int8 shiftCount; + + shiftCount = countLeadingZeros32(zSig) - 1; + return roundAndPackFloat32(zSign, zExp - shiftCount, zSig << shiftCount STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the fraction bits of the double-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE uint64_t extractFloat64Frac(float64 a) { + + return float64_val(a) & LIT64(0x000FFFFFFFFFFFFF); +} + +/*---------------------------------------------------------------------------- +| Returns the exponent bits of the double-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE int16 extractFloat64Exp(float64 a) { + + return (float64_val(a) >> 52) & 0x7FF; +} + +/*---------------------------------------------------------------------------- +| Returns the sign bit of the double-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE flag extractFloat64Sign(float64 a) { + + return float64_val(a) >> 63; +} + +/*---------------------------------------------------------------------------- +| If `a' is denormal and we are in flush-to-zero mode then set the +| input-denormal exception and return zero. Otherwise just return the value. +*----------------------------------------------------------------------------*/ +static float64 float64_squash_input_denormal(float64 a STATUS_PARAM) { + if (STATUS(flush_inputs_to_zero)) { + if (extractFloat64Exp(a) == 0 && extractFloat64Frac(a) != 0) { + float_raise(float_flag_input_denormal STATUS_VAR); + return make_float64(float64_val(a) & (1ULL << 63)); + } + } + return a; +} + +/*---------------------------------------------------------------------------- +| Normalizes the subnormal double-precision floating-point value represented +| by the denormalized significand `aSig'. The normalized exponent and +| significand are stored at the locations pointed to by `zExpPtr' and +| `zSigPtr', respectively. +*----------------------------------------------------------------------------*/ + +static void normalizeFloat64Subnormal(uint64_t aSig, int16 *zExpPtr, uint64_t *zSigPtr) { + int8 shiftCount; + + shiftCount = countLeadingZeros64(aSig) - 11; + *zSigPtr = aSig << shiftCount; + *zExpPtr = 1 - shiftCount; +} + +/*---------------------------------------------------------------------------- +| Packs the sign `zSign', exponent `zExp', and significand `zSig' into a +| double-precision floating-point value, returning the result. After being +| shifted into the proper positions, the three fields are simply added +| together to form the result. This means that any integer portion of `zSig' +| will be added into the exponent. Since a properly normalized significand +| will have an integer portion equal to 1, the `zExp' input should be 1 less +| than the desired result exponent whenever `zSig' is a complete, normalized +| significand. +*----------------------------------------------------------------------------*/ + +SINLINE float64 packFloat64(flag zSign, int16 zExp, uint64_t zSig) { + + return make_float64((((uint64_t) zSign) << 63) + (((uint64_t) zExp) << 52) + zSig); +} + +/*---------------------------------------------------------------------------- +| Takes an abstract floating-point value having sign `zSign', exponent `zExp', +| and significand `zSig', and returns the proper double-precision floating- +| point value corresponding to the abstract input. Ordinarily, the abstract +| value is simply rounded and packed into the double-precision format, with +| the inexact exception raised if the abstract input cannot be represented +| exactly. However, if the abstract value is too large, the overflow and +| inexact exceptions are raised and an infinity or maximal finite value is +| returned. If the abstract value is too small, the input value is rounded +| to a subnormal number, and the underflow and inexact exceptions are raised +| if the abstract input cannot be represented exactly as a subnormal double- +| precision floating-point number. +| The input significand `zSig' has its binary point between bits 62 +| and 61, which is 10 bits to the left of the usual location. This shifted +| significand must be normalized or smaller. If `zSig' is not normalized, +| `zExp' must be 0; in that case, the result returned is a subnormal number, +| and it must not require rounding. In the usual case that `zSig' is +| normalized, `zExp' must be 1 less than the ``true'' floating-point exponent. +| The handling of underflow and overflow follows the IEC/IEEE Standard for +| Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float64 roundAndPackFloat64(flag zSign, int16 zExp, uint64_t zSig STATUS_PARAM) { + int8 roundingMode; + flag roundNearestEven; + int16 roundIncrement, roundBits; + flag isTiny; + + roundingMode = STATUS(float_rounding_mode); + roundNearestEven = (roundingMode == float_round_nearest_even); + roundIncrement = 0x200; + if (!roundNearestEven) { + if (roundingMode == float_round_to_zero) { + roundIncrement = 0; + } else { + roundIncrement = 0x3FF; + if (zSign) { + if (roundingMode == float_round_up) + roundIncrement = 0; + } else { + if (roundingMode == float_round_down) + roundIncrement = 0; + } + } + } + roundBits = zSig & 0x3FF; + if (0x7FD <= (uint16_t) zExp) { + if ((0x7FD < zExp) || ((zExp == 0x7FD) && ((int64_t)(zSig + roundIncrement) < 0))) { + float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + return packFloat64(zSign, 0x7FF, -(roundIncrement == 0)); + } + if (zExp < 0) { + if (STATUS(flush_to_zero)) { + float_raise(float_flag_output_denormal STATUS_VAR); + return packFloat64(zSign, 0, 0); + } + isTiny = (STATUS(float_detect_tininess) == float_tininess_before_rounding) || (zExp < -1) || + (zSig + roundIncrement < LIT64(0x8000000000000000)); + shift64RightJamming(zSig, -zExp, &zSig); + zExp = 0; + roundBits = zSig & 0x3FF; + if (isTiny && roundBits) + float_raise(float_flag_underflow STATUS_VAR); + } + } + if (roundBits) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + zSig = (zSig + roundIncrement) >> 10; + zSig &= ~(((roundBits ^ 0x200) == 0) & roundNearestEven); + if (zSig == 0) + zExp = 0; + return packFloat64(zSign, zExp, zSig); +} + +/*---------------------------------------------------------------------------- +| Takes an abstract floating-point value having sign `zSign', exponent `zExp', +| and significand `zSig', and returns the proper double-precision floating- +| point value corresponding to the abstract input. This routine is just like +| `roundAndPackFloat64' except that `zSig' does not have to be normalized. +| Bit 63 of `zSig' must be zero, and `zExp' must be 1 less than the ``true'' +| floating-point exponent. +*----------------------------------------------------------------------------*/ + +static float64 normalizeRoundAndPackFloat64(flag zSign, int16 zExp, uint64_t zSig STATUS_PARAM) { + int8 shiftCount; + + shiftCount = countLeadingZeros64(zSig) - 1; + return roundAndPackFloat64(zSign, zExp - shiftCount, zSig << shiftCount STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the fraction bits of the extended double-precision floating-point +| value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE uint64_t extractFloatx80Frac(floatx80 a) { + + return a.low; +} + +/*---------------------------------------------------------------------------- +| Returns the exponent bits of the extended double-precision floating-point +| value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE int32 extractFloatx80Exp(floatx80 a) { + + return a.high & 0x7FFF; +} + +/*---------------------------------------------------------------------------- +| Returns the sign bit of the extended double-precision floating-point value +| `a'. +*----------------------------------------------------------------------------*/ + +SINLINE flag extractFloatx80Sign(floatx80 a) { + + return a.high >> 15; +} + +/*---------------------------------------------------------------------------- +| Normalizes the subnormal extended double-precision floating-point value +| represented by the denormalized significand `aSig'. The normalized exponent +| and significand are stored at the locations pointed to by `zExpPtr' and +| `zSigPtr', respectively. +*----------------------------------------------------------------------------*/ + +static void normalizeFloatx80Subnormal(uint64_t aSig, int32 *zExpPtr, uint64_t *zSigPtr) { + int8 shiftCount; + + shiftCount = countLeadingZeros64(aSig); + *zSigPtr = aSig << shiftCount; + *zExpPtr = 1 - shiftCount; +} + +/*---------------------------------------------------------------------------- +| Packs the sign `zSign', exponent `zExp', and significand `zSig' into an +| extended double-precision floating-point value, returning the result. +*----------------------------------------------------------------------------*/ + +SINLINE floatx80 packFloatx80(flag zSign, int32 zExp, uint64_t zSig) { + floatx80 z; + + z.low = zSig; + z.high = (((uint16_t) zSign) << 15) + zExp; + return z; +} + +/*---------------------------------------------------------------------------- +| Takes an abstract floating-point value having sign `zSign', exponent `zExp', +| and extended significand formed by the concatenation of `zSig0' and `zSig1', +| and returns the proper extended double-precision floating-point value +| corresponding to the abstract input. Ordinarily, the abstract value is +| rounded and packed into the extended double-precision format, with the +| inexact exception raised if the abstract input cannot be represented +| exactly. However, if the abstract value is too large, the overflow and +| inexact exceptions are raised and an infinity or maximal finite value is +| returned. If the abstract value is too small, the input value is rounded to +| a subnormal number, and the underflow and inexact exceptions are raised if +| the abstract input cannot be represented exactly as a subnormal extended +| double-precision floating-point number. +| If `roundingPrecision' is 32 or 64, the result is rounded to the same +| number of bits as single or double precision, respectively. Otherwise, the +| result is rounded to the full precision of the extended double-precision +| format. +| The input significand must be normalized or smaller. If the input +| significand is not normalized, `zExp' must be 0; in that case, the result +| returned is a subnormal number, and it must not require rounding. The +| handling of underflow and overflow follows the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static floatx80 roundAndPackFloatx80(int8 roundingPrecision, flag zSign, int32 zExp, uint64_t zSig0, + uint64_t zSig1 STATUS_PARAM) { + int8 roundingMode; + flag roundNearestEven, increment, isTiny; + int64 roundIncrement, roundMask, roundBits; + + roundingMode = STATUS(float_rounding_mode); + roundNearestEven = (roundingMode == float_round_nearest_even); + if (roundingPrecision == 80) + goto precision80; + if (roundingPrecision == 64) { + roundIncrement = LIT64(0x0000000000000400); + roundMask = LIT64(0x00000000000007FF); + } else if (roundingPrecision == 32) { + roundIncrement = LIT64(0x0000008000000000); + roundMask = LIT64(0x000000FFFFFFFFFF); + } else { + goto precision80; + } + zSig0 |= (zSig1 != 0); + if (!roundNearestEven) { + if (roundingMode == float_round_to_zero) { + roundIncrement = 0; + } else { + roundIncrement = roundMask; + if (zSign) { + if (roundingMode == float_round_up) + roundIncrement = 0; + } else { + if (roundingMode == float_round_down) + roundIncrement = 0; + } + } + } + roundBits = zSig0 & roundMask; + if (0x7FFD <= (uint32_t)(zExp - 1)) { + if ((0x7FFE < zExp) || ((zExp == 0x7FFE) && (zSig0 + roundIncrement < zSig0))) { + goto overflow; + } + if (zExp <= 0) { + if (STATUS(flush_to_zero)) { + float_raise(float_flag_output_denormal STATUS_VAR); + return packFloatx80(zSign, 0, 0); + } + isTiny = (STATUS(float_detect_tininess) == float_tininess_before_rounding) || (zExp < 0) || + (zSig0 <= zSig0 + roundIncrement); + shift64RightJamming(zSig0, 1 - zExp, &zSig0); + zExp = 0; + roundBits = zSig0 & roundMask; + if (isTiny && roundBits) + float_raise(float_flag_underflow STATUS_VAR); + if (roundBits) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + zSig0 += roundIncrement; + if ((int64_t) zSig0 < 0) + zExp = 1; + roundIncrement = roundMask + 1; + if (roundNearestEven && (roundBits << 1 == roundIncrement)) { + roundMask |= roundIncrement; + } + zSig0 &= ~roundMask; + return packFloatx80(zSign, zExp, zSig0); + } + } + if (roundBits) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + zSig0 += roundIncrement; + if (zSig0 < roundIncrement) { + ++zExp; + zSig0 = LIT64(0x8000000000000000); + } + roundIncrement = roundMask + 1; + if (roundNearestEven && (roundBits << 1 == roundIncrement)) { + roundMask |= roundIncrement; + } + zSig0 &= ~roundMask; + if (zSig0 == 0) + zExp = 0; + return packFloatx80(zSign, zExp, zSig0); +precision80: + increment = ((int64_t) zSig1 < 0); + if (!roundNearestEven) { + if (roundingMode == float_round_to_zero) { + increment = 0; + } else { + if (zSign) { + increment = (roundingMode == float_round_down) && zSig1; + } else { + increment = (roundingMode == float_round_up) && zSig1; + } + } + } + if (0x7FFD <= (uint32_t)(zExp - 1)) { + if ((0x7FFE < zExp) || ((zExp == 0x7FFE) && (zSig0 == LIT64(0xFFFFFFFFFFFFFFFF)) && increment)) { + roundMask = 0; + overflow: + float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + if ((roundingMode == float_round_to_zero) || (zSign && (roundingMode == float_round_up)) || + (!zSign && (roundingMode == float_round_down))) { + return packFloatx80(zSign, 0x7FFE, ~roundMask); + } + return packFloatx80(zSign, 0x7FFF, LIT64(0x8000000000000000)); + } + if (zExp <= 0) { + isTiny = (STATUS(float_detect_tininess) == float_tininess_before_rounding) || (zExp < 0) || !increment || + (zSig0 < LIT64(0xFFFFFFFFFFFFFFFF)); + shift64ExtraRightJamming(zSig0, zSig1, 1 - zExp, &zSig0, &zSig1); + zExp = 0; + if (isTiny && zSig1) + float_raise(float_flag_underflow STATUS_VAR); + if (zSig1) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + if (roundNearestEven) { + increment = ((int64_t) zSig1 < 0); + } else { + if (zSign) { + increment = (roundingMode == float_round_down) && zSig1; + } else { + increment = (roundingMode == float_round_up) && zSig1; + } + } + if (increment) { + ++zSig0; + zSig0 &= ~(((uint64_t)(zSig1 << 1) == 0) & roundNearestEven); + if ((int64_t) zSig0 < 0) + zExp = 1; + } + return packFloatx80(zSign, zExp, zSig0); + } + } + if (zSig1) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + if (increment) { + ++zSig0; + if (zSig0 == 0) { + ++zExp; + zSig0 = LIT64(0x8000000000000000); + } else { + zSig0 &= ~(((uint64_t)(zSig1 << 1) == 0) & roundNearestEven); + } + } else { + if (zSig0 == 0) + zExp = 0; + } + return packFloatx80(zSign, zExp, zSig0); +} + +/*---------------------------------------------------------------------------- +| Takes an abstract floating-point value having sign `zSign', exponent +| `zExp', and significand formed by the concatenation of `zSig0' and `zSig1', +| and returns the proper extended double-precision floating-point value +| corresponding to the abstract input. This routine is just like +| `roundAndPackFloatx80' except that the input significand does not have to be +| normalized. +*----------------------------------------------------------------------------*/ + +static floatx80 normalizeRoundAndPackFloatx80(int8 roundingPrecision, flag zSign, int32 zExp, uint64_t zSig0, + uint64_t zSig1 STATUS_PARAM) { + int8 shiftCount; + + if (zSig0 == 0) { + zSig0 = zSig1; + zSig1 = 0; + zExp -= 64; + } + shiftCount = countLeadingZeros64(zSig0); + shortShift128Left(zSig0, zSig1, shiftCount, &zSig0, &zSig1); + zExp -= shiftCount; + return roundAndPackFloatx80(roundingPrecision, zSign, zExp, zSig0, zSig1 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the least-significant 64 fraction bits of the quadruple-precision +| floating-point value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE uint64_t extractFloat128Frac1(float128 a) { + + return a.low; +} + +/*---------------------------------------------------------------------------- +| Returns the most-significant 48 fraction bits of the quadruple-precision +| floating-point value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE uint64_t extractFloat128Frac0(float128 a) { + + return a.high & LIT64(0x0000FFFFFFFFFFFF); +} + +/*---------------------------------------------------------------------------- +| Returns the exponent bits of the quadruple-precision floating-point value +| `a'. +*----------------------------------------------------------------------------*/ + +SINLINE int32 extractFloat128Exp(float128 a) { + + return (a.high >> 48) & 0x7FFF; +} + +/*---------------------------------------------------------------------------- +| Returns the sign bit of the quadruple-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +SINLINE flag extractFloat128Sign(float128 a) { + + return a.high >> 63; +} + +/*---------------------------------------------------------------------------- +| Normalizes the subnormal quadruple-precision floating-point value +| represented by the denormalized significand formed by the concatenation of +| `aSig0' and `aSig1'. The normalized exponent is stored at the location +| pointed to by `zExpPtr'. The most significant 49 bits of the normalized +| significand are stored at the location pointed to by `zSig0Ptr', and the +| least significant 64 bits of the normalized significand are stored at the +| location pointed to by `zSig1Ptr'. +*----------------------------------------------------------------------------*/ + +static void normalizeFloat128Subnormal(uint64_t aSig0, uint64_t aSig1, int32 *zExpPtr, uint64_t *zSig0Ptr, + uint64_t *zSig1Ptr) { + int8 shiftCount; + + if (aSig0 == 0) { + shiftCount = countLeadingZeros64(aSig1) - 15; + if (shiftCount < 0) { + *zSig0Ptr = aSig1 >> (-shiftCount); + *zSig1Ptr = aSig1 << (shiftCount & 63); + } else { + *zSig0Ptr = aSig1 << shiftCount; + *zSig1Ptr = 0; + } + *zExpPtr = -shiftCount - 63; + } else { + shiftCount = countLeadingZeros64(aSig0) - 15; + shortShift128Left(aSig0, aSig1, shiftCount, zSig0Ptr, zSig1Ptr); + *zExpPtr = 1 - shiftCount; + } +} + +/*---------------------------------------------------------------------------- +| Packs the sign `zSign', the exponent `zExp', and the significand formed +| by the concatenation of `zSig0' and `zSig1' into a quadruple-precision +| floating-point value, returning the result. After being shifted into the +| proper positions, the three fields `zSign', `zExp', and `zSig0' are simply +| added together to form the most significant 32 bits of the result. This +| means that any integer portion of `zSig0' will be added into the exponent. +| Since a properly normalized significand will have an integer portion equal +| to 1, the `zExp' input should be 1 less than the desired result exponent +| whenever `zSig0' and `zSig1' concatenated form a complete, normalized +| significand. +*----------------------------------------------------------------------------*/ + +SINLINE float128 packFloat128(flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1) { + float128 z; + + z.low = zSig1; + z.high = (((uint64_t) zSign) << 63) + (((uint64_t) zExp) << 48) + zSig0; + return z; +} + +/*---------------------------------------------------------------------------- +| Takes an abstract floating-point value having sign `zSign', exponent `zExp', +| and extended significand formed by the concatenation of `zSig0', `zSig1', +| and `zSig2', and returns the proper quadruple-precision floating-point value +| corresponding to the abstract input. Ordinarily, the abstract value is +| simply rounded and packed into the quadruple-precision format, with the +| inexact exception raised if the abstract input cannot be represented +| exactly. However, if the abstract value is too large, the overflow and +| inexact exceptions are raised and an infinity or maximal finite value is +| returned. If the abstract value is too small, the input value is rounded to +| a subnormal number, and the underflow and inexact exceptions are raised if +| the abstract input cannot be represented exactly as a subnormal quadruple- +| precision floating-point number. +| The input significand must be normalized or smaller. If the input +| significand is not normalized, `zExp' must be 0; in that case, the result +| returned is a subnormal number, and it must not require rounding. In the +| usual case that the input significand is normalized, `zExp' must be 1 less +| than the ``true'' floating-point exponent. The handling of underflow and +| overflow follows the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float128 roundAndPackFloat128(flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1, + uint64_t zSig2 STATUS_PARAM) { + int8 roundingMode; + flag roundNearestEven, increment, isTiny; + + roundingMode = STATUS(float_rounding_mode); + roundNearestEven = (roundingMode == float_round_nearest_even); + increment = ((int64_t) zSig2 < 0); + if (!roundNearestEven) { + if (roundingMode == float_round_to_zero) { + increment = 0; + } else { + if (zSign) { + increment = (roundingMode == float_round_down) && zSig2; + } else { + increment = (roundingMode == float_round_up) && zSig2; + } + } + } + if (0x7FFD <= (uint32_t) zExp) { + if ((0x7FFD < zExp) || + ((zExp == 0x7FFD) && eq128(LIT64(0x0001FFFFFFFFFFFF), LIT64(0xFFFFFFFFFFFFFFFF), zSig0, zSig1) && + increment)) { + float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + if ((roundingMode == float_round_to_zero) || (zSign && (roundingMode == float_round_up)) || + (!zSign && (roundingMode == float_round_down))) { + return packFloat128(zSign, 0x7FFE, LIT64(0x0000FFFFFFFFFFFF), LIT64(0xFFFFFFFFFFFFFFFF)); + } + return packFloat128(zSign, 0x7FFF, 0, 0); + } + if (zExp < 0) { + if (STATUS(flush_to_zero)) { + float_raise(float_flag_output_denormal STATUS_VAR); + return packFloat128(zSign, 0, 0, 0); + } + isTiny = (STATUS(float_detect_tininess) == float_tininess_before_rounding) || (zExp < -1) || !increment || + lt128(zSig0, zSig1, LIT64(0x0001FFFFFFFFFFFF), LIT64(0xFFFFFFFFFFFFFFFF)); + shift128ExtraRightJamming(zSig0, zSig1, zSig2, -zExp, &zSig0, &zSig1, &zSig2); + zExp = 0; + if (isTiny && zSig2) + float_raise(float_flag_underflow STATUS_VAR); + if (roundNearestEven) { + increment = ((int64_t) zSig2 < 0); + } else { + if (zSign) { + increment = (roundingMode == float_round_down) && zSig2; + } else { + increment = (roundingMode == float_round_up) && zSig2; + } + } + } + } + if (zSig2) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + if (increment) { + add128(zSig0, zSig1, 0, 1, &zSig0, &zSig1); + zSig1 &= ~((zSig2 + zSig2 == 0) & roundNearestEven); + } else { + if ((zSig0 | zSig1) == 0) + zExp = 0; + } + return packFloat128(zSign, zExp, zSig0, zSig1); +} + +/*---------------------------------------------------------------------------- +| Takes an abstract floating-point value having sign `zSign', exponent `zExp', +| and significand formed by the concatenation of `zSig0' and `zSig1', and +| returns the proper quadruple-precision floating-point value corresponding +| to the abstract input. This routine is just like `roundAndPackFloat128' +| except that the input significand has fewer bits and does not have to be +| normalized. In all cases, `zExp' must be 1 less than the ``true'' floating- +| point exponent. +*----------------------------------------------------------------------------*/ + +static float128 normalizeRoundAndPackFloat128(flag zSign, int32 zExp, uint64_t zSig0, uint64_t zSig1 STATUS_PARAM) { + int8 shiftCount; + uint64_t zSig2; + + if (zSig0 == 0) { + zSig0 = zSig1; + zSig1 = 0; + zExp -= 64; + } + shiftCount = countLeadingZeros64(zSig0) - 15; + if (0 <= shiftCount) { + zSig2 = 0; + shortShift128Left(zSig0, zSig1, shiftCount, &zSig0, &zSig1); + } else { + shift128ExtraRightJamming(zSig0, zSig1, 0, -shiftCount, &zSig0, &zSig1, &zSig2); + } + zExp -= shiftCount; + return roundAndPackFloat128(zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the 32-bit two's complement integer `a' +| to the single-precision floating-point format. The conversion is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 int32_to_float32(int32 a STATUS_PARAM) { + flag zSign; + + if (a == 0) + return float32_zero; + if (a == (int32_t) 0x80000000) + return packFloat32(1, 0x9E, 0); + zSign = (a < 0); + return normalizeRoundAndPackFloat32(zSign, 0x9C, zSign ? -a : a STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the 32-bit two's complement integer `a' +| to the double-precision floating-point format. The conversion is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 int32_to_float64(int32 a STATUS_PARAM) { + flag zSign; + uint32 absA; + int8 shiftCount; + uint64_t zSig; + + if (a == 0) + return float64_zero; + zSign = (a < 0); + absA = zSign ? -a : a; + shiftCount = countLeadingZeros32(absA) + 21; + zSig = absA; + return packFloat64(zSign, 0x432 - shiftCount, zSig << shiftCount); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the 32-bit two's complement integer `a' +| to the extended double-precision floating-point format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 int32_to_floatx80(int32 a STATUS_PARAM) { + flag zSign; + uint32 absA; + int8 shiftCount; + uint64_t zSig; + + if (a == 0) + return packFloatx80(0, 0, 0); + zSign = (a < 0); + absA = zSign ? -a : a; + shiftCount = countLeadingZeros32(absA) + 32; + zSig = absA; + return packFloatx80(zSign, 0x403E - shiftCount, zSig << shiftCount); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the 32-bit two's complement integer `a' to +| the quadruple-precision floating-point format. The conversion is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 int32_to_float128(int32 a STATUS_PARAM) { + flag zSign; + uint32 absA; + int8 shiftCount; + uint64_t zSig0; + + if (a == 0) + return packFloat128(0, 0, 0, 0); + zSign = (a < 0); + absA = zSign ? -a : a; + shiftCount = countLeadingZeros32(absA) + 17; + zSig0 = absA; + return packFloat128(zSign, 0x402E - shiftCount, zSig0 << shiftCount, 0); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the 64-bit two's complement integer `a' +| to the single-precision floating-point format. The conversion is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 int64_to_float32(int64 a STATUS_PARAM) { + flag zSign; + uint64 absA; + int8 shiftCount; + + if (a == 0) + return float32_zero; + zSign = (a < 0); + absA = zSign ? -a : a; + shiftCount = countLeadingZeros64(absA) - 40; + if (0 <= shiftCount) { + return packFloat32(zSign, 0x95 - shiftCount, absA << shiftCount); + } else { + shiftCount += 7; + if (shiftCount < 0) { + shift64RightJamming(absA, -shiftCount, &absA); + } else { + absA <<= shiftCount; + } + return roundAndPackFloat32(zSign, 0x9C - shiftCount, absA STATUS_VAR); + } +} + +float32 uint64_to_float32(uint64 a STATUS_PARAM) { + int8 shiftCount; + + if (a == 0) + return float32_zero; + shiftCount = countLeadingZeros64(a) - 40; + if (0 <= shiftCount) { + return packFloat32(1 > 0, 0x95 - shiftCount, a << shiftCount); + } else { + shiftCount += 7; + if (shiftCount < 0) { + shift64RightJamming(a, -shiftCount, &a); + } else { + a <<= shiftCount; + } + return roundAndPackFloat32(1 > 0, 0x9C - shiftCount, a STATUS_VAR); + } +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the 64-bit two's complement integer `a' +| to the double-precision floating-point format. The conversion is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 int64_to_float64(int64 a STATUS_PARAM) { + flag zSign; + + if (a == 0) + return float64_zero; + if (a == (int64_t) LIT64(0x8000000000000000)) { + return packFloat64(1, 0x43E, 0); + } + zSign = (a < 0); + return normalizeRoundAndPackFloat64(zSign, 0x43C, zSign ? -a : a STATUS_VAR); +} + +float64 uint64_to_float64(uint64 a STATUS_PARAM) { + if (a == 0) + return float64_zero; + return normalizeRoundAndPackFloat64(0, 0x43C, a STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the 64-bit two's complement integer `a' +| to the extended double-precision floating-point format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 int64_to_floatx80(int64 a STATUS_PARAM) { + flag zSign; + uint64 absA; + int8 shiftCount; + + if (a == 0) + return packFloatx80(0, 0, 0); + zSign = (a < 0); + absA = zSign ? -a : a; + shiftCount = countLeadingZeros64(absA); + return packFloatx80(zSign, 0x403E - shiftCount, absA << shiftCount); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the 64-bit two's complement integer `a' to +| the quadruple-precision floating-point format. The conversion is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 int64_to_float128(int64 a STATUS_PARAM) { + flag zSign; + uint64 absA; + int8 shiftCount; + int32 zExp; + uint64_t zSig0, zSig1; + + if (a == 0) + return packFloat128(0, 0, 0, 0); + zSign = (a < 0); + absA = zSign ? -a : a; + shiftCount = countLeadingZeros64(absA) + 49; + zExp = 0x406E - shiftCount; + if (64 <= shiftCount) { + zSig1 = 0; + zSig0 = absA; + shiftCount -= 64; + } else { + zSig1 = absA; + zSig0 = 0; + } + shortShift128Left(zSig0, zSig1, shiftCount, &zSig0, &zSig1); + return packFloat128(zSign, zExp, zSig0, zSig1); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the 32-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic---which means in particular that the conversion is rounded +| according to the current rounding mode. If `a' is a NaN, the largest +| positive integer is returned. Otherwise, if the conversion overflows, the +| largest integer with the same sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int32 float32_to_int32(float32 a STATUS_PARAM) { + flag aSign; + int16 aExp, shiftCount; + uint32_t aSig; + uint64_t aSig64; + + a = float32_squash_input_denormal(a STATUS_VAR); + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + if ((aExp == 0xFF) && aSig) + aSign = 0; + if (aExp) + aSig |= 0x00800000; + shiftCount = 0xAF - aExp; + aSig64 = aSig; + aSig64 <<= 32; + if (0 < shiftCount) + shift64RightJamming(aSig64, shiftCount, &aSig64); + return roundAndPackInt32(aSign, aSig64 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the 32-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. +| If `a' is a NaN, the largest positive integer is returned. Otherwise, if +| the conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int32 float32_to_int32_round_to_zero(float32 a STATUS_PARAM) { + flag aSign; + int16 aExp, shiftCount; + uint32_t aSig; + int32_t z; + a = float32_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + shiftCount = aExp - 0x9E; + if (0 <= shiftCount) { + if (float32_val(a) != 0xCF000000) { + float_raise(float_flag_invalid STATUS_VAR); + if (!aSign || ((aExp == 0xFF) && aSig)) + return 0x7FFFFFFF; + } + return (int32_t) 0x80000000; + } else if (aExp <= 0x7E) { + if (aExp | aSig) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + return 0; + } + aSig = (aSig | 0x00800000) << 8; + z = aSig >> (-shiftCount); + if ((uint32_t)(aSig << (shiftCount & 31))) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + if (aSign) + z = -z; + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the 16-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. +| If `a' is a NaN, the largest positive integer is returned. Otherwise, if +| the conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int16 float32_to_int16_round_to_zero(float32 a STATUS_PARAM) { + flag aSign; + int16 aExp, shiftCount; + uint32_t aSig; + int32 z; + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + shiftCount = aExp - 0x8E; + if (0 <= shiftCount) { + if (float32_val(a) != 0xC7000000) { + float_raise(float_flag_invalid STATUS_VAR); + if (!aSign || ((aExp == 0xFF) && aSig)) { + return 0x7FFF; + } + } + return (int32_t) 0xffff8000; + } else if (aExp <= 0x7E) { + if (aExp | aSig) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + return 0; + } + shiftCount -= 0x10; + aSig = (aSig | 0x00800000) << 8; + z = aSig >> (-shiftCount); + if ((uint32_t)(aSig << (shiftCount & 31))) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + if (aSign) { + z = -z; + } + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the 64-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic---which means in particular that the conversion is rounded +| according to the current rounding mode. If `a' is a NaN, the largest +| positive integer is returned. Otherwise, if the conversion overflows, the +| largest integer with the same sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int64 float32_to_int64(float32 a STATUS_PARAM) { + flag aSign; + int16 aExp, shiftCount; + uint32_t aSig; + uint64_t aSig64, aSigExtra; + a = float32_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + shiftCount = 0xBE - aExp; + if (shiftCount < 0) { + float_raise(float_flag_invalid STATUS_VAR); + if (!aSign || ((aExp == 0xFF) && aSig)) { + return LIT64(0x7FFFFFFFFFFFFFFF); + } + return (int64_t) LIT64(0x8000000000000000); + } + if (aExp) + aSig |= 0x00800000; + aSig64 = aSig; + aSig64 <<= 40; + shift64ExtraRightJamming(aSig64, 0, shiftCount, &aSig64, &aSigExtra); + return roundAndPackInt64(aSign, aSig64, aSigExtra STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the 64-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. If +| `a' is a NaN, the largest positive integer is returned. Otherwise, if the +| conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int64 float32_to_int64_round_to_zero(float32 a STATUS_PARAM) { + flag aSign; + int16 aExp, shiftCount; + uint32_t aSig; + uint64_t aSig64; + int64 z; + a = float32_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + shiftCount = aExp - 0xBE; + if (0 <= shiftCount) { + if (float32_val(a) != 0xDF000000) { + float_raise(float_flag_invalid STATUS_VAR); + if (!aSign || ((aExp == 0xFF) && aSig)) { + return LIT64(0x7FFFFFFFFFFFFFFF); + } + } + return (int64_t) LIT64(0x8000000000000000); + } else if (aExp <= 0x7E) { + if (aExp | aSig) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + return 0; + } + aSig64 = aSig | 0x00800000; + aSig64 <<= 40; + z = aSig64 >> (-shiftCount); + if ((uint64_t)(aSig64 << (shiftCount & 63))) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + if (aSign) + z = -z; + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the double-precision floating-point format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float32_to_float64(float32 a STATUS_PARAM) { + flag aSign; + int16 aExp; + uint32_t aSig; + a = float32_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + if (aExp == 0xFF) { + if (aSig) + return commonNaNToFloat64(float32ToCommonNaN(a STATUS_VAR) STATUS_VAR); + return packFloat64(aSign, 0x7FF, 0); + } + if (aExp == 0) { + if (aSig == 0) + return packFloat64(aSign, 0, 0); + normalizeFloat32Subnormal(aSig, &aExp, &aSig); + --aExp; + } + return packFloat64(aSign, aExp + 0x380, ((uint64_t) aSig) << 29); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the extended double-precision floating-point format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 float32_to_floatx80(float32 a STATUS_PARAM) { + flag aSign; + int16 aExp; + uint32_t aSig; + + a = float32_squash_input_denormal(a STATUS_VAR); + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + if (aExp == 0xFF) { + if (aSig) + return commonNaNToFloatx80(float32ToCommonNaN(a STATUS_VAR) STATUS_VAR); + return packFloatx80(aSign, 0x7FFF, LIT64(0x8000000000000000)); + } + if (aExp == 0) { + if (aSig == 0) + return packFloatx80(aSign, 0, 0); + normalizeFloat32Subnormal(aSig, &aExp, &aSig); + } + aSig |= 0x00800000; + return packFloatx80(aSign, aExp + 0x3F80, ((uint64_t) aSig) << 40); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value +| `a' to the double-precision floating-point format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float32_to_float128(float32 a STATUS_PARAM) { + flag aSign; + int16 aExp; + uint32_t aSig; + + a = float32_squash_input_denormal(a STATUS_VAR); + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + if (aExp == 0xFF) { + if (aSig) + return commonNaNToFloat128(float32ToCommonNaN(a STATUS_VAR) STATUS_VAR); + return packFloat128(aSign, 0x7FFF, 0, 0); + } + if (aExp == 0) { + if (aSig == 0) + return packFloat128(aSign, 0, 0, 0); + normalizeFloat32Subnormal(aSig, &aExp, &aSig); + --aExp; + } + return packFloat128(aSign, aExp + 0x3F80, ((uint64_t) aSig) << 25, 0); +} + +/*---------------------------------------------------------------------------- +| Rounds the single-precision floating-point value `a' to an integer, and +| returns the result as a single-precision floating-point value. The +| operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_round_to_int(float32 a STATUS_PARAM) { + flag aSign; + int16 aExp; + uint32_t lastBitMask, roundBitsMask; + int8 roundingMode; + uint32_t z; + a = float32_squash_input_denormal(a STATUS_VAR); + + aExp = extractFloat32Exp(a); + if (0x96 <= aExp) { + if ((aExp == 0xFF) && extractFloat32Frac(a)) { + return propagateFloat32NaN(a, a STATUS_VAR); + } + return a; + } + if (aExp <= 0x7E) { + if ((uint32_t)(float32_val(a) << 1) == 0) + return a; + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + aSign = extractFloat32Sign(a); + switch (STATUS(float_rounding_mode)) { + case float_round_nearest_even: + if ((aExp == 0x7E) && extractFloat32Frac(a)) { + return packFloat32(aSign, 0x7F, 0); + } + break; + case float_round_down: + return make_float32(aSign ? 0xBF800000 : 0); + case float_round_up: + return make_float32(aSign ? 0x80000000 : 0x3F800000); + } + return packFloat32(aSign, 0, 0); + } + lastBitMask = 1; + lastBitMask <<= 0x96 - aExp; + roundBitsMask = lastBitMask - 1; + z = float32_val(a); + roundingMode = STATUS(float_rounding_mode); + if (roundingMode == float_round_nearest_even) { + z += lastBitMask >> 1; + if ((z & roundBitsMask) == 0) + z &= ~lastBitMask; + } else if (roundingMode != float_round_to_zero) { + if (extractFloat32Sign(make_float32(z)) ^ (roundingMode == float_round_up)) { + z += roundBitsMask; + } + } + z &= ~roundBitsMask; + if (z != float32_val(a)) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + return make_float32(z); +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the absolute values of the single-precision +| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated +| before being returned. `zSign' is ignored if the result is a NaN. +| The addition is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float32 addFloat32Sigs(float32 a, float32 b, flag zSign STATUS_PARAM) { + int16 aExp, bExp, zExp; + uint32_t aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + bSig = extractFloat32Frac(b); + bExp = extractFloat32Exp(b); + expDiff = aExp - bExp; + aSig <<= 6; + bSig <<= 6; + if (0 < expDiff) { + if (aExp == 0xFF) { + if (aSig) + return propagateFloat32NaN(a, b STATUS_VAR); + return a; + } + if (bExp == 0) { + --expDiff; + } else { + bSig |= 0x20000000; + } + shift32RightJamming(bSig, expDiff, &bSig); + zExp = aExp; + } else if (expDiff < 0) { + if (bExp == 0xFF) { + if (bSig) + return propagateFloat32NaN(a, b STATUS_VAR); + return packFloat32(zSign, 0xFF, 0); + } + if (aExp == 0) { + ++expDiff; + } else { + aSig |= 0x20000000; + } + shift32RightJamming(aSig, -expDiff, &aSig); + zExp = bExp; + } else { + if (aExp == 0xFF) { + if (aSig | bSig) + return propagateFloat32NaN(a, b STATUS_VAR); + return a; + } + if (aExp == 0) { + if (STATUS(flush_to_zero)) { + if (aSig | bSig) { + float_raise(float_flag_output_denormal STATUS_VAR); + } + return packFloat32(zSign, 0, 0); + } + return packFloat32(zSign, 0, (aSig + bSig) >> 6); + } + zSig = 0x40000000 + aSig + bSig; + zExp = aExp; + goto roundAndPack; + } + aSig |= 0x20000000; + zSig = (aSig + bSig) << 1; + --zExp; + if ((int32_t) zSig < 0) { + zSig = aSig + bSig; + ++zExp; + } +roundAndPack: + return roundAndPackFloat32(zSign, zExp, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the absolute values of the single- +| precision floating-point values `a' and `b'. If `zSign' is 1, the +| difference is negated before being returned. `zSign' is ignored if the +| result is a NaN. The subtraction is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float32 subFloat32Sigs(float32 a, float32 b, flag zSign STATUS_PARAM) { + int16 aExp, bExp, zExp; + uint32_t aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + bSig = extractFloat32Frac(b); + bExp = extractFloat32Exp(b); + expDiff = aExp - bExp; + aSig <<= 7; + bSig <<= 7; + if (0 < expDiff) + goto aExpBigger; + if (expDiff < 0) + goto bExpBigger; + if (aExp == 0xFF) { + if (aSig | bSig) + return propagateFloat32NaN(a, b STATUS_VAR); + float_raise(float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + if (aExp == 0) { + aExp = 1; + bExp = 1; + } + if (bSig < aSig) + goto aBigger; + if (aSig < bSig) + goto bBigger; + return packFloat32(STATUS(float_rounding_mode) == float_round_down, 0, 0); +bExpBigger: + if (bExp == 0xFF) { + if (bSig) + return propagateFloat32NaN(a, b STATUS_VAR); + return packFloat32(zSign ^ 1, 0xFF, 0); + } + if (aExp == 0) { + ++expDiff; + } else { + aSig |= 0x40000000; + } + shift32RightJamming(aSig, -expDiff, &aSig); + bSig |= 0x40000000; +bBigger: + zSig = bSig - aSig; + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; +aExpBigger: + if (aExp == 0xFF) { + if (aSig) + return propagateFloat32NaN(a, b STATUS_VAR); + return a; + } + if (bExp == 0) { + --expDiff; + } else { + bSig |= 0x40000000; + } + shift32RightJamming(bSig, expDiff, &bSig); + aSig |= 0x40000000; +aBigger: + zSig = aSig - bSig; + zExp = aExp; +normalizeRoundAndPack: + --zExp; + return normalizeRoundAndPackFloat32(zSign, zExp, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the single-precision floating-point values `a' +| and `b'. The operation is performed according to the IEC/IEEE Standard for +| Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_add(float32 a, float32 b STATUS_PARAM) { + flag aSign, bSign; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + aSign = extractFloat32Sign(a); + bSign = extractFloat32Sign(b); + if (aSign == bSign) { + return addFloat32Sigs(a, b, aSign STATUS_VAR); + } else { + return subFloat32Sigs(a, b, aSign STATUS_VAR); + } +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the single-precision floating-point values +| `a' and `b'. The operation is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_sub(float32 a, float32 b STATUS_PARAM) { + flag aSign, bSign; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + aSign = extractFloat32Sign(a); + bSign = extractFloat32Sign(b); + if (aSign == bSign) { + return subFloat32Sigs(a, b, aSign STATUS_VAR); + } else { + return addFloat32Sigs(a, b, aSign STATUS_VAR); + } +} + +/*---------------------------------------------------------------------------- +| Returns the result of multiplying the single-precision floating-point values +| `a' and `b'. The operation is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_mul(float32 a, float32 b STATUS_PARAM) { + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + uint32_t aSig, bSig; + uint64_t zSig64; + uint32_t zSig; + + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + bSig = extractFloat32Frac(b); + bExp = extractFloat32Exp(b); + bSign = extractFloat32Sign(b); + zSign = aSign ^ bSign; + if (aExp == 0xFF) { + if (aSig || ((bExp == 0xFF) && bSig)) { + return propagateFloat32NaN(a, b STATUS_VAR); + } + if ((bExp | bSig) == 0) { + float_raise(float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + return packFloat32(zSign, 0xFF, 0); + } + if (bExp == 0xFF) { + if (bSig) + return propagateFloat32NaN(a, b STATUS_VAR); + if ((aExp | aSig) == 0) { + float_raise(float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + return packFloat32(zSign, 0xFF, 0); + } + if (aExp == 0) { + if (aSig == 0) + return packFloat32(zSign, 0, 0); + normalizeFloat32Subnormal(aSig, &aExp, &aSig); + } + if (bExp == 0) { + if (bSig == 0) + return packFloat32(zSign, 0, 0); + normalizeFloat32Subnormal(bSig, &bExp, &bSig); + } + zExp = aExp + bExp - 0x7F; + aSig = (aSig | 0x00800000) << 7; + bSig = (bSig | 0x00800000) << 8; + shift64RightJamming(((uint64_t) aSig) * bSig, 32, &zSig64); + zSig = zSig64; + if (0 <= (int32_t)(zSig << 1)) { + zSig <<= 1; + --zExp; + } + return roundAndPackFloat32(zSign, zExp, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of dividing the single-precision floating-point value `a' +| by the corresponding value `b'. The operation is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_div(float32 a, float32 b STATUS_PARAM) { + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + uint32_t aSig, bSig, zSig; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + bSig = extractFloat32Frac(b); + bExp = extractFloat32Exp(b); + bSign = extractFloat32Sign(b); + zSign = aSign ^ bSign; + if (aExp == 0xFF) { + if (aSig) + return propagateFloat32NaN(a, b STATUS_VAR); + if (bExp == 0xFF) { + if (bSig) + return propagateFloat32NaN(a, b STATUS_VAR); + float_raise(float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + return packFloat32(zSign, 0xFF, 0); + } + if (bExp == 0xFF) { + if (bSig) + return propagateFloat32NaN(a, b STATUS_VAR); + return packFloat32(zSign, 0, 0); + } + if (bExp == 0) { + if (bSig == 0) { + if ((aExp | aSig) == 0) { + float_raise(float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + float_raise(float_flag_divbyzero STATUS_VAR); + return packFloat32(zSign, 0xFF, 0); + } + normalizeFloat32Subnormal(bSig, &bExp, &bSig); + } + if (aExp == 0) { + if (aSig == 0) + return packFloat32(zSign, 0, 0); + normalizeFloat32Subnormal(aSig, &aExp, &aSig); + } + zExp = aExp - bExp + 0x7D; + aSig = (aSig | 0x00800000) << 7; + bSig = (bSig | 0x00800000) << 8; + if (bSig <= (aSig + aSig)) { + aSig >>= 1; + ++zExp; + } + zSig = (((uint64_t) aSig) << 32) / bSig; + if ((zSig & 0x3F) == 0) { + zSig |= ((uint64_t) bSig * zSig != ((uint64_t) aSig) << 32); + } + return roundAndPackFloat32(zSign, zExp, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the remainder of the single-precision floating-point value `a' +| with respect to the corresponding value `b'. The operation is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_rem(float32 a, float32 b STATUS_PARAM) { + flag aSign, zSign; + int16 aExp, bExp, expDiff; + uint32_t aSig, bSig; + uint32_t q; + uint64_t aSig64, bSig64, q64; + uint32_t alternateASig; + int32_t sigMean; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + bSig = extractFloat32Frac(b); + bExp = extractFloat32Exp(b); + if (aExp == 0xFF) { + if (aSig || ((bExp == 0xFF) && bSig)) { + return propagateFloat32NaN(a, b STATUS_VAR); + } + float_raise(float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + if (bExp == 0xFF) { + if (bSig) + return propagateFloat32NaN(a, b STATUS_VAR); + return a; + } + if (bExp == 0) { + if (bSig == 0) { + float_raise(float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + normalizeFloat32Subnormal(bSig, &bExp, &bSig); + } + if (aExp == 0) { + if (aSig == 0) + return a; + normalizeFloat32Subnormal(aSig, &aExp, &aSig); + } + expDiff = aExp - bExp; + aSig |= 0x00800000; + bSig |= 0x00800000; + if (expDiff < 32) { + aSig <<= 8; + bSig <<= 8; + if (expDiff < 0) { + if (expDiff < -1) + return a; + aSig >>= 1; + } + q = (bSig <= aSig); + if (q) + aSig -= bSig; + if (0 < expDiff) { + q = (((uint64_t) aSig) << 32) / bSig; + q >>= 32 - expDiff; + bSig >>= 2; + aSig = ((aSig >> 1) << (expDiff - 1)) - bSig * q; + } else { + aSig >>= 2; + bSig >>= 2; + } + } else { + if (bSig <= aSig) + aSig -= bSig; + aSig64 = ((uint64_t) aSig) << 40; + bSig64 = ((uint64_t) bSig) << 40; + expDiff -= 64; + while (0 < expDiff) { + q64 = estimateDiv128To64(aSig64, 0, bSig64); + q64 = (2 < q64) ? q64 - 2 : 0; + aSig64 = -((bSig * q64) << 38); + expDiff -= 62; + } + expDiff += 64; + q64 = estimateDiv128To64(aSig64, 0, bSig64); + q64 = (2 < q64) ? q64 - 2 : 0; + q = q64 >> (64 - expDiff); + bSig <<= 6; + aSig = ((aSig64 >> 33) << (expDiff - 1)) - bSig * q; + } + do { + alternateASig = aSig; + ++q; + aSig -= bSig; + } while (0 <= (int32_t) aSig); + sigMean = aSig + alternateASig; + if ((sigMean < 0) || ((sigMean == 0) && (q & 1))) { + aSig = alternateASig; + } + zSign = ((int32_t) aSig < 0); + if (zSign) + aSig = -aSig; + return normalizeRoundAndPackFloat32(aSign ^ zSign, bExp, aSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of multiplying the single-precision floating-point values +| `a' and `b' then adding 'c', with no intermediate rounding step after the +| multiplication. The operation is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic 754-2008. +| The flags argument allows the caller to select negation of the +| addend, the intermediate product, or the final result. (The difference +| between this and having the caller do a separate negation is that negating +| externally will flip the sign bit on NaNs.) +*----------------------------------------------------------------------------*/ + +float32 float32_muladd(float32 a, float32 b, float32 c, int flags STATUS_PARAM) { + flag aSign, bSign, cSign, zSign; + int aExp, bExp, cExp, pExp, zExp, expDiff; + uint32_t aSig, bSig, cSig; + flag pInf, pZero, pSign; + uint64_t pSig64, cSig64, zSig64; + uint32_t pSig; + int shiftcount; + flag signflip, infzero; + + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + c = float32_squash_input_denormal(c STATUS_VAR); + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + bSig = extractFloat32Frac(b); + bExp = extractFloat32Exp(b); + bSign = extractFloat32Sign(b); + cSig = extractFloat32Frac(c); + cExp = extractFloat32Exp(c); + cSign = extractFloat32Sign(c); + + infzero = ((aExp == 0 && aSig == 0 && bExp == 0xff && bSig == 0) || + (aExp == 0xff && aSig == 0 && bExp == 0 && bSig == 0)); + + /* It is implementation-defined whether the cases of (0,inf,qnan) + * and (inf,0,qnan) raise InvalidOperation or not (and what QNaN + * they return if they do), so we have to hand this information + * off to the target-specific pick-a-NaN routine. + */ + if (((aExp == 0xff) && aSig) || ((bExp == 0xff) && bSig) || ((cExp == 0xff) && cSig)) { + return propagateFloat32MulAddNaN(a, b, c, infzero STATUS_VAR); + } + + if (infzero) { + float_raise(float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + + if (flags & float_muladd_negate_c) { + cSign ^= 1; + } + + signflip = (flags & float_muladd_negate_result) ? 1 : 0; + + /* Work out the sign and type of the product */ + pSign = aSign ^ bSign; + if (flags & float_muladd_negate_product) { + pSign ^= 1; + } + pInf = (aExp == 0xff) || (bExp == 0xff); + pZero = ((aExp | aSig) == 0) || ((bExp | bSig) == 0); + + if (cExp == 0xff) { + if (pInf && (pSign ^ cSign)) { + /* addition of opposite-signed infinities => InvalidOperation */ + float_raise(float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + /* Otherwise generate an infinity of the same sign */ + return packFloat32(cSign ^ signflip, 0xff, 0); + } + + if (pInf) { + return packFloat32(pSign ^ signflip, 0xff, 0); + } + + if (pZero) { + if (cExp == 0) { + if (cSig == 0) { + /* Adding two exact zeroes */ + if (pSign == cSign) { + zSign = pSign; + } else if (STATUS(float_rounding_mode) == float_round_down) { + zSign = 1; + } else { + zSign = 0; + } + return packFloat32(zSign ^ signflip, 0, 0); + } + /* Exact zero plus a denorm */ + if (STATUS(flush_to_zero)) { + float_raise(float_flag_output_denormal STATUS_VAR); + return packFloat32(cSign ^ signflip, 0, 0); + } + } + /* Zero plus something non-zero : just return the something */ + return c ^ (signflip << 31); + } + + if (aExp == 0) { + normalizeFloat32Subnormal(aSig, &aExp, &aSig); + } + if (bExp == 0) { + normalizeFloat32Subnormal(bSig, &bExp, &bSig); + } + + /* Calculate the actual result a * b + c */ + + /* Multiply first; this is easy. */ + /* NB: we subtract 0x7e where float32_mul() subtracts 0x7f + * because we want the true exponent, not the "one-less-than" + * flavour that roundAndPackFloat32() takes. + */ + pExp = aExp + bExp - 0x7e; + aSig = (aSig | 0x00800000) << 7; + bSig = (bSig | 0x00800000) << 8; + pSig64 = (uint64_t) aSig * bSig; + if ((int64_t)(pSig64 << 1) >= 0) { + pSig64 <<= 1; + pExp--; + } + + zSign = pSign ^ signflip; + + /* Now pSig64 is the significand of the multiply, with the explicit bit in + * position 62. + */ + if (cExp == 0) { + if (!cSig) { + /* Throw out the special case of c being an exact zero now */ + shift64RightJamming(pSig64, 32, &pSig64); + pSig = pSig64; + return roundAndPackFloat32(zSign, pExp - 1, pSig STATUS_VAR); + } + normalizeFloat32Subnormal(cSig, &cExp, &cSig); + } + + cSig64 = (uint64_t) cSig << (62 - 23); + cSig64 |= LIT64(0x4000000000000000); + expDiff = pExp - cExp; + + if (pSign == cSign) { + /* Addition */ + if (expDiff > 0) { + /* scale c to match p */ + shift64RightJamming(cSig64, expDiff, &cSig64); + zExp = pExp; + } else if (expDiff < 0) { + /* scale p to match c */ + shift64RightJamming(pSig64, -expDiff, &pSig64); + zExp = cExp; + } else { + /* no scaling needed */ + zExp = cExp; + } + /* Add significands and make sure explicit bit ends up in posn 62 */ + zSig64 = pSig64 + cSig64; + if ((int64_t) zSig64 < 0) { + shift64RightJamming(zSig64, 1, &zSig64); + } else { + zExp--; + } + } else { + /* Subtraction */ + if (expDiff > 0) { + shift64RightJamming(cSig64, expDiff, &cSig64); + zSig64 = pSig64 - cSig64; + zExp = pExp; + } else if (expDiff < 0) { + shift64RightJamming(pSig64, -expDiff, &pSig64); + zSig64 = cSig64 - pSig64; + zExp = cExp; + zSign ^= 1; + } else { + zExp = pExp; + if (cSig64 < pSig64) { + zSig64 = pSig64 - cSig64; + } else if (pSig64 < cSig64) { + zSig64 = cSig64 - pSig64; + zSign ^= 1; + } else { + /* Exact zero */ + zSign = signflip; + if (STATUS(float_rounding_mode) == float_round_down) { + zSign ^= 1; + } + return packFloat32(zSign, 0, 0); + } + } + --zExp; + /* Normalize to put the explicit bit back into bit 62. */ + shiftcount = countLeadingZeros64(zSig64) - 1; + zSig64 <<= shiftcount; + zExp -= shiftcount; + } + shift64RightJamming(zSig64, 32, &zSig64); + return roundAndPackFloat32(zSign, zExp, zSig64 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the square root of the single-precision floating-point value `a'. +| The operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float32_sqrt(float32 a STATUS_PARAM) { + flag aSign; + int16 aExp, zExp; + uint32_t aSig, zSig; + uint64_t rem, term; + a = float32_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + if (aExp == 0xFF) { + if (aSig) + return propagateFloat32NaN(a, float32_zero STATUS_VAR); + if (!aSign) + return a; + float_raise(float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + if (aSign) { + if ((aExp | aSig) == 0) + return a; + float_raise(float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + if (aExp == 0) { + if (aSig == 0) + return float32_zero; + normalizeFloat32Subnormal(aSig, &aExp, &aSig); + } + zExp = ((aExp - 0x7F) >> 1) + 0x7E; + aSig = (aSig | 0x00800000) << 8; + zSig = estimateSqrt32(aExp, aSig) + 2; + if ((zSig & 0x7F) <= 5) { + if (zSig < 2) { + zSig = 0x7FFFFFFF; + goto roundAndPack; + } + aSig >>= aExp & 1; + term = ((uint64_t) zSig) * zSig; + rem = (((uint64_t) aSig) << 32) - term; + while ((int64_t) rem < 0) { + --zSig; + rem += (((uint64_t) zSig) << 1) | 1; + } + zSig |= (rem != 0); + } + shift32RightJamming(zSig, 1, &zSig); +roundAndPack: + return roundAndPackFloat32(0, zExp, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the binary exponential of the single-precision floating-point value +| `a'. The operation is performed according to the IEC/IEEE Standard for +| Binary Floating-Point Arithmetic. +| +| Uses the following identities: +| +| 1. ------------------------------------------------------------------------- +| x x*ln(2) +| 2 = e +| +| 2. ------------------------------------------------------------------------- +| 2 3 4 5 n +| x x x x x x x +| e = 1 + --- + --- + --- + --- + --- + ... + --- + ... +| 1! 2! 3! 4! 5! n! +*----------------------------------------------------------------------------*/ + +static const float64 float32_exp2_coefficients[15] = { + const_float64(0x3ff0000000000000ll), /* 1 */ + const_float64(0x3fe0000000000000ll), /* 2 */ + const_float64(0x3fc5555555555555ll), /* 3 */ + const_float64(0x3fa5555555555555ll), /* 4 */ + const_float64(0x3f81111111111111ll), /* 5 */ + const_float64(0x3f56c16c16c16c17ll), /* 6 */ + const_float64(0x3f2a01a01a01a01all), /* 7 */ + const_float64(0x3efa01a01a01a01all), /* 8 */ + const_float64(0x3ec71de3a556c734ll), /* 9 */ + const_float64(0x3e927e4fb7789f5cll), /* 10 */ + const_float64(0x3e5ae64567f544e4ll), /* 11 */ + const_float64(0x3e21eed8eff8d898ll), /* 12 */ + const_float64(0x3de6124613a86d09ll), /* 13 */ + const_float64(0x3da93974a8c07c9dll), /* 14 */ + const_float64(0x3d6ae7f3e733b81fll), /* 15 */ +}; + +float32 float32_exp2(float32 a STATUS_PARAM) { + flag aSign; + int16 aExp; + uint32_t aSig; + float64 r, x, xn; + int i; + a = float32_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + + if (aExp == 0xFF) { + if (aSig) + return propagateFloat32NaN(a, float32_zero STATUS_VAR); + return (aSign) ? float32_zero : a; + } + if (aExp == 0) { + if (aSig == 0) + return float32_one; + } + + float_raise(float_flag_inexact STATUS_VAR); + + /* ******************************* */ + /* using float64 for approximation */ + /* ******************************* */ + x = float32_to_float64(a STATUS_VAR); + x = float64_mul(x, float64_ln2 STATUS_VAR); + + xn = x; + r = float64_one; + for (i = 0; i < 15; i++) { + float64 f; + + f = float64_mul(xn, float32_exp2_coefficients[i] STATUS_VAR); + r = float64_add(r, f STATUS_VAR); + + xn = float64_mul(xn, x STATUS_VAR); + } + + return float64_to_float32(r, status); +} + +/*---------------------------------------------------------------------------- +| Returns the binary log of the single-precision floating-point value `a'. +| The operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ +float32 float32_log2(float32 a STATUS_PARAM) { + flag aSign, zSign; + int16 aExp; + uint32_t aSig, zSig, i; + + a = float32_squash_input_denormal(a STATUS_VAR); + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + + if (aExp == 0) { + if (aSig == 0) + return packFloat32(1, 0xFF, 0); + normalizeFloat32Subnormal(aSig, &aExp, &aSig); + } + if (aSign) { + float_raise(float_flag_invalid STATUS_VAR); + return float32_default_nan; + } + if (aExp == 0xFF) { + if (aSig) + return propagateFloat32NaN(a, float32_zero STATUS_VAR); + return a; + } + + aExp -= 0x7F; + aSig |= 0x00800000; + zSign = aExp < 0; + zSig = aExp << 23; + + for (i = 1 << 22; i > 0; i >>= 1) { + aSig = ((uint64_t) aSig * aSig) >> 23; + if (aSig & 0x01000000) { + aSig >>= 1; + zSig |= i; + } + } + + if (zSign) + zSig = -zSig; + + return normalizeRoundAndPackFloat32(zSign, 0x85, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is equal to +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. Otherwise, the comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float32_eq(float32 a, float32 b STATUS_PARAM) { + uint32_t av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat32Exp(a) == 0xFF) && extractFloat32Frac(a)) || + ((extractFloat32Exp(b) == 0xFF) && extractFloat32Frac(b))) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + av = float32_val(a); + bv = float32_val(b); + return (av == bv) || ((uint32_t)((av | bv) << 1) == 0); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is less than +| or equal to the corresponding value `b', and 0 otherwise. The invalid +| exception is raised if either operand is a NaN. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float32_le(float32 a, float32 b STATUS_PARAM) { + flag aSign, bSign; + uint32_t av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat32Exp(a) == 0xFF) && extractFloat32Frac(a)) || + ((extractFloat32Exp(b) == 0xFF) && extractFloat32Frac(b))) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloat32Sign(a); + bSign = extractFloat32Sign(b); + av = float32_val(a); + bv = float32_val(b); + if (aSign != bSign) + return aSign || ((uint32_t)((av | bv) << 1) == 0); + return (av == bv) || (aSign ^ (av < bv)); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is less than +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. The comparison is performed according +| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float32_lt(float32 a, float32 b STATUS_PARAM) { + flag aSign, bSign; + uint32_t av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat32Exp(a) == 0xFF) && extractFloat32Frac(a)) || + ((extractFloat32Exp(b) == 0xFF) && extractFloat32Frac(b))) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloat32Sign(a); + bSign = extractFloat32Sign(b); + av = float32_val(a); + bv = float32_val(b); + if (aSign != bSign) + return aSign && ((uint32_t)((av | bv) << 1) != 0); + return (av != bv) && (aSign ^ (av < bv)); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. The invalid exception is raised if either +| operand is a NaN. The comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float32_unordered(float32 a, float32 b STATUS_PARAM) { + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat32Exp(a) == 0xFF) && extractFloat32Frac(a)) || + ((extractFloat32Exp(b) == 0xFF) && extractFloat32Frac(b))) { + float_raise(float_flag_invalid STATUS_VAR); + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is equal to +| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception. The comparison is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float32_eq_quiet(float32 a, float32 b STATUS_PARAM) { + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat32Exp(a) == 0xFF) && extractFloat32Frac(a)) || + ((extractFloat32Exp(b) == 0xFF) && extractFloat32Frac(b))) { + if (float32_is_signaling_nan(a) || float32_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 0; + } + return (float32_val(a) == float32_val(b)) || ((uint32_t)((float32_val(a) | float32_val(b)) << 1) == 0); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is less than or +| equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not +| cause an exception. Otherwise, the comparison is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float32_le_quiet(float32 a, float32 b STATUS_PARAM) { + flag aSign, bSign; + uint32_t av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat32Exp(a) == 0xFF) && extractFloat32Frac(a)) || + ((extractFloat32Exp(b) == 0xFF) && extractFloat32Frac(b))) { + if (float32_is_signaling_nan(a) || float32_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloat32Sign(a); + bSign = extractFloat32Sign(b); + av = float32_val(a); + bv = float32_val(b); + if (aSign != bSign) + return aSign || ((uint32_t)((av | bv) << 1) == 0); + return (av == bv) || (aSign ^ (av < bv)); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point value `a' is less than +| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception. Otherwise, the comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float32_lt_quiet(float32 a, float32 b STATUS_PARAM) { + flag aSign, bSign; + uint32_t av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat32Exp(a) == 0xFF) && extractFloat32Frac(a)) || + ((extractFloat32Exp(b) == 0xFF) && extractFloat32Frac(b))) { + if (float32_is_signaling_nan(a) || float32_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloat32Sign(a); + bSign = extractFloat32Sign(b); + av = float32_val(a); + bv = float32_val(b); + if (aSign != bSign) + return aSign && ((uint32_t)((av | bv) << 1) != 0); + return (av != bv) && (aSign ^ (av < bv)); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the single-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. Quiet NaNs do not cause an exception. The +| comparison is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float32_unordered_quiet(float32 a, float32 b STATUS_PARAM) { + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat32Exp(a) == 0xFF) && extractFloat32Frac(a)) || + ((extractFloat32Exp(b) == 0xFF) && extractFloat32Frac(b))) { + if (float32_is_signaling_nan(a) || float32_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the 32-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic---which means in particular that the conversion is rounded +| according to the current rounding mode. If `a' is a NaN, the largest +| positive integer is returned. Otherwise, if the conversion overflows, the +| largest integer with the same sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int32 float64_to_int32(float64 a STATUS_PARAM) { + flag aSign; + int16 aExp, shiftCount; + uint64_t aSig; + a = float64_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + if ((aExp == 0x7FF) && aSig) + aSign = 0; + if (aExp) + aSig |= LIT64(0x0010000000000000); + shiftCount = 0x42C - aExp; + if (0 < shiftCount) + shift64RightJamming(aSig, shiftCount, &aSig); + return roundAndPackInt32(aSign, aSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the 32-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. +| If `a' is a NaN, the largest positive integer is returned. Otherwise, if +| the conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int32 float64_to_int32_round_to_zero(float64 a STATUS_PARAM) { + flag aSign; + int16 aExp, shiftCount; + uint64_t aSig, savedASig; + int32_t z; + a = float64_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + if (0x41E < aExp) { + if ((aExp == 0x7FF) && aSig) + aSign = 0; + goto invalid; + } else if (aExp < 0x3FF) { + if (aExp || aSig) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + return 0; + } + aSig |= LIT64(0x0010000000000000); + shiftCount = 0x433 - aExp; + savedASig = aSig; + aSig >>= shiftCount; + z = aSig; + if (aSign) + z = -z; + if ((z < 0) ^ aSign) { + invalid: + float_raise(float_flag_invalid STATUS_VAR); + return aSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; + } + if ((aSig << shiftCount) != savedASig) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the 16-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. +| If `a' is a NaN, the largest positive integer is returned. Otherwise, if +| the conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int16 float64_to_int16_round_to_zero(float64 a STATUS_PARAM) { + flag aSign; + int16 aExp, shiftCount; + uint64_t aSig, savedASig; + int32 z; + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + if (0x40E < aExp) { + if ((aExp == 0x7FF) && aSig) { + aSign = 0; + } + goto invalid; + } else if (aExp < 0x3FF) { + if (aExp || aSig) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + return 0; + } + aSig |= LIT64(0x0010000000000000); + shiftCount = 0x433 - aExp; + savedASig = aSig; + aSig >>= shiftCount; + z = aSig; + if (aSign) { + z = -z; + } + if (((int16_t) z < 0) ^ aSign) { + invalid: + float_raise(float_flag_invalid STATUS_VAR); + return aSign ? (int32_t) 0xffff8000 : 0x7FFF; + } + if ((aSig << shiftCount) != savedASig) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the 64-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic---which means in particular that the conversion is rounded +| according to the current rounding mode. If `a' is a NaN, the largest +| positive integer is returned. Otherwise, if the conversion overflows, the +| largest integer with the same sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int64 float64_to_int64(float64 a STATUS_PARAM) { + flag aSign; + int16 aExp, shiftCount; + uint64_t aSig, aSigExtra; + a = float64_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + if (aExp) + aSig |= LIT64(0x0010000000000000); + shiftCount = 0x433 - aExp; + if (shiftCount <= 0) { + if (0x43E < aExp) { + float_raise(float_flag_invalid STATUS_VAR); + if (!aSign || ((aExp == 0x7FF) && (aSig != LIT64(0x0010000000000000)))) { + return LIT64(0x7FFFFFFFFFFFFFFF); + } + return (int64_t) LIT64(0x8000000000000000); + } + aSigExtra = 0; + aSig <<= -shiftCount; + } else { + shift64ExtraRightJamming(aSig, 0, shiftCount, &aSig, &aSigExtra); + } + return roundAndPackInt64(aSign, aSig, aSigExtra STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the 64-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. +| If `a' is a NaN, the largest positive integer is returned. Otherwise, if +| the conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int64 float64_to_int64_round_to_zero(float64 a STATUS_PARAM) { + flag aSign; + int16 aExp, shiftCount; + uint64_t aSig; + int64 z; + a = float64_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + if (aExp) + aSig |= LIT64(0x0010000000000000); + shiftCount = aExp - 0x433; + if (0 <= shiftCount) { + if (0x43E <= aExp) { + if (float64_val(a) != LIT64(0xC3E0000000000000)) { + float_raise(float_flag_invalid STATUS_VAR); + if (!aSign || ((aExp == 0x7FF) && (aSig != LIT64(0x0010000000000000)))) { + return LIT64(0x7FFFFFFFFFFFFFFF); + } + } + return (int64_t) LIT64(0x8000000000000000); + } + z = aSig << shiftCount; + } else { + if (aExp < 0x3FE) { + if (aExp | aSig) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + return 0; + } + z = aSig >> (-shiftCount); + if ((uint64_t)(aSig << (shiftCount & 63))) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + } + if (aSign) + z = -z; + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the single-precision floating-point format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float64_to_float32(float64 a STATUS_PARAM) { + flag aSign; + int16 aExp; + uint64_t aSig; + uint32_t zSig; + a = float64_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + if (aExp == 0x7FF) { + if (aSig) + return commonNaNToFloat32(float64ToCommonNaN(a STATUS_VAR) STATUS_VAR); + return packFloat32(aSign, 0xFF, 0); + } + shift64RightJamming(aSig, 22, &aSig); + zSig = aSig; + if (aExp || zSig) { + zSig |= 0x40000000; + aExp -= 0x381; + } + return roundAndPackFloat32(aSign, aExp, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Packs the sign `zSign', exponent `zExp', and significand `zSig' into a +| half-precision floating-point value, returning the result. After being +| shifted into the proper positions, the three fields are simply added +| together to form the result. This means that any integer portion of `zSig' +| will be added into the exponent. Since a properly normalized significand +| will have an integer portion equal to 1, the `zExp' input should be 1 less +| than the desired result exponent whenever `zSig' is a complete, normalized +| significand. +*----------------------------------------------------------------------------*/ +static float16 packFloat16(flag zSign, int16 zExp, uint16_t zSig) { + return make_float16((((uint32_t) zSign) << 15) + (((uint32_t) zExp) << 10) + zSig); +} + +/* Half precision floats come in two formats: standard IEEE and "ARM" format. + The latter gains extra exponent range by omitting the NaN/Inf encodings. */ + +float32 float16_to_float32(float16 a, flag ieee STATUS_PARAM) { + flag aSign; + int16 aExp; + uint32_t aSig; + + aSign = extractFloat16Sign(a); + aExp = extractFloat16Exp(a); + aSig = extractFloat16Frac(a); + + if (aExp == 0x1f && ieee) { + if (aSig) { + return commonNaNToFloat32(float16ToCommonNaN(a STATUS_VAR) STATUS_VAR); + } + return packFloat32(aSign, 0xff, aSig << 13); + } + if (aExp == 0) { + int8 shiftCount; + + if (aSig == 0) { + return packFloat32(aSign, 0, 0); + } + + shiftCount = countLeadingZeros32(aSig) - 21; + aSig = aSig << shiftCount; + aExp = -shiftCount; + } + return packFloat32(aSign, aExp + 0x70, aSig << 13); +} + +float16 float32_to_float16(float32 a, flag ieee STATUS_PARAM) { + flag aSign; + int16 aExp; + uint32_t aSig; + uint32_t mask; + uint32_t increment; + int8 roundingMode; + a = float32_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + if (aExp == 0xFF) { + if (aSig) { + /* Input is a NaN */ + float16 r = commonNaNToFloat16(float32ToCommonNaN(a STATUS_VAR) STATUS_VAR); + if (!ieee) { + return packFloat16(aSign, 0, 0); + } + return r; + } + /* Infinity */ + if (!ieee) { + float_raise(float_flag_invalid STATUS_VAR); + return packFloat16(aSign, 0x1f, 0x3ff); + } + return packFloat16(aSign, 0x1f, 0); + } + if (aExp == 0 && aSig == 0) { + return packFloat16(aSign, 0, 0); + } + /* Decimal point between bits 22 and 23. */ + aSig |= 0x00800000; + aExp -= 0x7f; + if (aExp < -14) { + mask = 0x00ffffff; + if (aExp >= -24) { + mask >>= 25 + aExp; + } + } else { + mask = 0x00001fff; + } + if (aSig & mask) { + float_raise(float_flag_underflow STATUS_VAR); + roundingMode = STATUS(float_rounding_mode); + switch (roundingMode) { + case float_round_nearest_even: + increment = (mask + 1) >> 1; + if ((aSig & mask) == increment) { + increment = aSig & (increment << 1); + } + break; + case float_round_up: + increment = aSign ? 0 : mask; + break; + case float_round_down: + increment = aSign ? mask : 0; + break; + default: /* round_to_zero */ + increment = 0; + break; + } + aSig += increment; + if (aSig >= 0x01000000) { + aSig >>= 1; + aExp++; + } + } else if (aExp < -14 && STATUS(float_detect_tininess) == float_tininess_before_rounding) { + float_raise(float_flag_underflow STATUS_VAR); + } + + if (ieee) { + if (aExp > 15) { + float_raise(float_flag_overflow | float_flag_inexact STATUS_VAR); + return packFloat16(aSign, 0x1f, 0); + } + } else { + if (aExp > 16) { + float_raise(float_flag_invalid | float_flag_inexact STATUS_VAR); + return packFloat16(aSign, 0x1f, 0x3ff); + } + } + if (aExp < -24) { + return packFloat16(aSign, 0, 0); + } + if (aExp < -14) { + aSig >>= -14 - aExp; + aExp = -14; + } + return packFloat16(aSign, aExp + 14, aSig >> 13); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the extended double-precision floating-point format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 float64_to_floatx80(float64 a STATUS_PARAM) { + flag aSign; + int16 aExp; + uint64_t aSig; + + a = float64_squash_input_denormal(a STATUS_VAR); + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + if (aExp == 0x7FF) { + if (aSig) + return commonNaNToFloatx80(float64ToCommonNaN(a STATUS_VAR) STATUS_VAR); + return packFloatx80(aSign, 0x7FFF, LIT64(0x8000000000000000)); + } + if (aExp == 0) { + if (aSig == 0) + return packFloatx80(aSign, 0, 0); + normalizeFloat64Subnormal(aSig, &aExp, &aSig); + } + return packFloatx80(aSign, aExp + 0x3C00, (aSig | LIT64(0x0010000000000000)) << 11); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the double-precision floating-point value +| `a' to the quadruple-precision floating-point format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float64_to_float128(float64 a STATUS_PARAM) { + flag aSign; + int16 aExp; + uint64_t aSig, zSig0, zSig1; + + a = float64_squash_input_denormal(a STATUS_VAR); + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + if (aExp == 0x7FF) { + if (aSig) + return commonNaNToFloat128(float64ToCommonNaN(a STATUS_VAR) STATUS_VAR); + return packFloat128(aSign, 0x7FFF, 0, 0); + } + if (aExp == 0) { + if (aSig == 0) + return packFloat128(aSign, 0, 0, 0); + normalizeFloat64Subnormal(aSig, &aExp, &aSig); + --aExp; + } + shift128Right(aSig, 0, 4, &zSig0, &zSig1); + return packFloat128(aSign, aExp + 0x3C00, zSig0, zSig1); +} + +/*---------------------------------------------------------------------------- +| Rounds the double-precision floating-point value `a' to an integer, and +| returns the result as a double-precision floating-point value. The +| operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_round_to_int(float64 a STATUS_PARAM) { + flag aSign; + int16 aExp; + uint64_t lastBitMask, roundBitsMask; + int8 roundingMode; + uint64_t z; + a = float64_squash_input_denormal(a STATUS_VAR); + + aExp = extractFloat64Exp(a); + if (0x433 <= aExp) { + if ((aExp == 0x7FF) && extractFloat64Frac(a)) { + return propagateFloat64NaN(a, a STATUS_VAR); + } + return a; + } + if (aExp < 0x3FF) { + if ((uint64_t)(float64_val(a) << 1) == 0) + return a; + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + aSign = extractFloat64Sign(a); + switch (STATUS(float_rounding_mode)) { + case float_round_nearest_even: + if ((aExp == 0x3FE) && extractFloat64Frac(a)) { + return packFloat64(aSign, 0x3FF, 0); + } + break; + case float_round_down: + return make_float64(aSign ? LIT64(0xBFF0000000000000) : 0); + case float_round_up: + return make_float64(aSign ? LIT64(0x8000000000000000) : LIT64(0x3FF0000000000000)); + } + return packFloat64(aSign, 0, 0); + } + lastBitMask = 1; + lastBitMask <<= 0x433 - aExp; + roundBitsMask = lastBitMask - 1; + z = float64_val(a); + roundingMode = STATUS(float_rounding_mode); + if (roundingMode == float_round_nearest_even) { + z += lastBitMask >> 1; + if ((z & roundBitsMask) == 0) + z &= ~lastBitMask; + } else if (roundingMode != float_round_to_zero) { + if (extractFloat64Sign(make_float64(z)) ^ (roundingMode == float_round_up)) { + z += roundBitsMask; + } + } + z &= ~roundBitsMask; + if (z != float64_val(a)) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + return make_float64(z); +} + +float64 float64_trunc_to_int(float64 a STATUS_PARAM) { + int oldmode; + float64 res; + oldmode = STATUS(float_rounding_mode); + STATUS_W(float_rounding_mode, float_round_to_zero); + res = float64_round_to_int(a STATUS_VAR); + STATUS_W(float_rounding_mode, oldmode); + return res; +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the absolute values of the double-precision +| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated +| before being returned. `zSign' is ignored if the result is a NaN. +| The addition is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float64 addFloat64Sigs(float64 a, float64 b, flag zSign STATUS_PARAM) { + int16 aExp, bExp, zExp; + uint64_t aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + bSig = extractFloat64Frac(b); + bExp = extractFloat64Exp(b); + expDiff = aExp - bExp; + aSig <<= 9; + bSig <<= 9; + if (0 < expDiff) { + if (aExp == 0x7FF) { + if (aSig) + return propagateFloat64NaN(a, b STATUS_VAR); + return a; + } + if (bExp == 0) { + --expDiff; + } else { + bSig |= LIT64(0x2000000000000000); + } + shift64RightJamming(bSig, expDiff, &bSig); + zExp = aExp; + } else if (expDiff < 0) { + if (bExp == 0x7FF) { + if (bSig) + return propagateFloat64NaN(a, b STATUS_VAR); + return packFloat64(zSign, 0x7FF, 0); + } + if (aExp == 0) { + ++expDiff; + } else { + aSig |= LIT64(0x2000000000000000); + } + shift64RightJamming(aSig, -expDiff, &aSig); + zExp = bExp; + } else { + if (aExp == 0x7FF) { + if (aSig | bSig) + return propagateFloat64NaN(a, b STATUS_VAR); + return a; + } + if (aExp == 0) { + if (STATUS(flush_to_zero)) { + if (aSig | bSig) { + float_raise(float_flag_output_denormal STATUS_VAR); + } + return packFloat64(zSign, 0, 0); + } + return packFloat64(zSign, 0, (aSig + bSig) >> 9); + } + zSig = LIT64(0x4000000000000000) + aSig + bSig; + zExp = aExp; + goto roundAndPack; + } + aSig |= LIT64(0x2000000000000000); + zSig = (aSig + bSig) << 1; + --zExp; + if ((int64_t) zSig < 0) { + zSig = aSig + bSig; + ++zExp; + } +roundAndPack: + return roundAndPackFloat64(zSign, zExp, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the absolute values of the double- +| precision floating-point values `a' and `b'. If `zSign' is 1, the +| difference is negated before being returned. `zSign' is ignored if the +| result is a NaN. The subtraction is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float64 subFloat64Sigs(float64 a, float64 b, flag zSign STATUS_PARAM) { + int16 aExp, bExp, zExp; + uint64_t aSig, bSig, zSig; + int16 expDiff; + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + bSig = extractFloat64Frac(b); + bExp = extractFloat64Exp(b); + expDiff = aExp - bExp; + aSig <<= 10; + bSig <<= 10; + if (0 < expDiff) + goto aExpBigger; + if (expDiff < 0) + goto bExpBigger; + if (aExp == 0x7FF) { + if (aSig | bSig) + return propagateFloat64NaN(a, b STATUS_VAR); + float_raise(float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + if (aExp == 0) { + aExp = 1; + bExp = 1; + } + if (bSig < aSig) + goto aBigger; + if (aSig < bSig) + goto bBigger; + return packFloat64(STATUS(float_rounding_mode) == float_round_down, 0, 0); +bExpBigger: + if (bExp == 0x7FF) { + if (bSig) + return propagateFloat64NaN(a, b STATUS_VAR); + return packFloat64(zSign ^ 1, 0x7FF, 0); + } + if (aExp == 0) { + ++expDiff; + } else { + aSig |= LIT64(0x4000000000000000); + } + shift64RightJamming(aSig, -expDiff, &aSig); + bSig |= LIT64(0x4000000000000000); +bBigger: + zSig = bSig - aSig; + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; +aExpBigger: + if (aExp == 0x7FF) { + if (aSig) + return propagateFloat64NaN(a, b STATUS_VAR); + return a; + } + if (bExp == 0) { + --expDiff; + } else { + bSig |= LIT64(0x4000000000000000); + } + shift64RightJamming(bSig, expDiff, &bSig); + aSig |= LIT64(0x4000000000000000); +aBigger: + zSig = aSig - bSig; + zExp = aExp; +normalizeRoundAndPack: + --zExp; + return normalizeRoundAndPackFloat64(zSign, zExp, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the double-precision floating-point values `a' +| and `b'. The operation is performed according to the IEC/IEEE Standard for +| Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_add(float64 a, float64 b STATUS_PARAM) { + flag aSign, bSign; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + aSign = extractFloat64Sign(a); + bSign = extractFloat64Sign(b); + if (aSign == bSign) { + return addFloat64Sigs(a, b, aSign STATUS_VAR); + } else { + return subFloat64Sigs(a, b, aSign STATUS_VAR); + } +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the double-precision floating-point values +| `a' and `b'. The operation is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_sub(float64 a, float64 b STATUS_PARAM) { + flag aSign, bSign; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + aSign = extractFloat64Sign(a); + bSign = extractFloat64Sign(b); + if (aSign == bSign) { + return subFloat64Sigs(a, b, aSign STATUS_VAR); + } else { + return addFloat64Sigs(a, b, aSign STATUS_VAR); + } +} + +/*---------------------------------------------------------------------------- +| Returns the result of multiplying the double-precision floating-point values +| `a' and `b'. The operation is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_mul(float64 a, float64 b STATUS_PARAM) { + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + uint64_t aSig, bSig, zSig0, zSig1; + + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + bSig = extractFloat64Frac(b); + bExp = extractFloat64Exp(b); + bSign = extractFloat64Sign(b); + zSign = aSign ^ bSign; + if (aExp == 0x7FF) { + if (aSig || ((bExp == 0x7FF) && bSig)) { + return propagateFloat64NaN(a, b STATUS_VAR); + } + if ((bExp | bSig) == 0) { + float_raise(float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + return packFloat64(zSign, 0x7FF, 0); + } + if (bExp == 0x7FF) { + if (bSig) + return propagateFloat64NaN(a, b STATUS_VAR); + if ((aExp | aSig) == 0) { + float_raise(float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + return packFloat64(zSign, 0x7FF, 0); + } + if (aExp == 0) { + if (aSig == 0) + return packFloat64(zSign, 0, 0); + normalizeFloat64Subnormal(aSig, &aExp, &aSig); + } + if (bExp == 0) { + if (bSig == 0) + return packFloat64(zSign, 0, 0); + normalizeFloat64Subnormal(bSig, &bExp, &bSig); + } + zExp = aExp + bExp - 0x3FF; + aSig = (aSig | LIT64(0x0010000000000000)) << 10; + bSig = (bSig | LIT64(0x0010000000000000)) << 11; + mul64To128(aSig, bSig, &zSig0, &zSig1); + zSig0 |= (zSig1 != 0); + if (0 <= (int64_t)(zSig0 << 1)) { + zSig0 <<= 1; + --zExp; + } + return roundAndPackFloat64(zSign, zExp, zSig0 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of dividing the double-precision floating-point value `a' +| by the corresponding value `b'. The operation is performed according to +| the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_div(float64 a, float64 b STATUS_PARAM) { + flag aSign, bSign, zSign; + int16 aExp, bExp, zExp; + uint64_t aSig, bSig, zSig; + uint64_t rem0, rem1; + uint64_t term0, term1; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + bSig = extractFloat64Frac(b); + bExp = extractFloat64Exp(b); + bSign = extractFloat64Sign(b); + zSign = aSign ^ bSign; + if (aExp == 0x7FF) { + if (aSig) + return propagateFloat64NaN(a, b STATUS_VAR); + if (bExp == 0x7FF) { + if (bSig) + return propagateFloat64NaN(a, b STATUS_VAR); + float_raise(float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + return packFloat64(zSign, 0x7FF, 0); + } + if (bExp == 0x7FF) { + if (bSig) + return propagateFloat64NaN(a, b STATUS_VAR); + return packFloat64(zSign, 0, 0); + } + if (bExp == 0) { + if (bSig == 0) { + if ((aExp | aSig) == 0) { + float_raise(float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + float_raise(float_flag_divbyzero STATUS_VAR); + return packFloat64(zSign, 0x7FF, 0); + } + normalizeFloat64Subnormal(bSig, &bExp, &bSig); + } + if (aExp == 0) { + if (aSig == 0) + return packFloat64(zSign, 0, 0); + normalizeFloat64Subnormal(aSig, &aExp, &aSig); + } + zExp = aExp - bExp + 0x3FD; + aSig = (aSig | LIT64(0x0010000000000000)) << 10; + bSig = (bSig | LIT64(0x0010000000000000)) << 11; + if (bSig <= (aSig + aSig)) { + aSig >>= 1; + ++zExp; + } + zSig = estimateDiv128To64(aSig, 0, bSig); + if ((zSig & 0x1FF) <= 2) { + mul64To128(bSig, zSig, &term0, &term1); + sub128(aSig, 0, term0, term1, &rem0, &rem1); + while ((int64_t) rem0 < 0) { + --zSig; + add128(rem0, rem1, 0, bSig, &rem0, &rem1); + } + zSig |= (rem1 != 0); + } + return roundAndPackFloat64(zSign, zExp, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the remainder of the double-precision floating-point value `a' +| with respect to the corresponding value `b'. The operation is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_rem(float64 a, float64 b STATUS_PARAM) { + flag aSign, zSign; + int16 aExp, bExp, expDiff; + uint64_t aSig, bSig; + uint64_t q, alternateASig; + int64_t sigMean; + + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + bSig = extractFloat64Frac(b); + bExp = extractFloat64Exp(b); + if (aExp == 0x7FF) { + if (aSig || ((bExp == 0x7FF) && bSig)) { + return propagateFloat64NaN(a, b STATUS_VAR); + } + float_raise(float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + if (bExp == 0x7FF) { + if (bSig) + return propagateFloat64NaN(a, b STATUS_VAR); + return a; + } + if (bExp == 0) { + if (bSig == 0) { + float_raise(float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + normalizeFloat64Subnormal(bSig, &bExp, &bSig); + } + if (aExp == 0) { + if (aSig == 0) + return a; + normalizeFloat64Subnormal(aSig, &aExp, &aSig); + } + expDiff = aExp - bExp; + aSig = (aSig | LIT64(0x0010000000000000)) << 11; + bSig = (bSig | LIT64(0x0010000000000000)) << 11; + if (expDiff < 0) { + if (expDiff < -1) + return a; + aSig >>= 1; + } + q = (bSig <= aSig); + if (q) + aSig -= bSig; + expDiff -= 64; + while (0 < expDiff) { + q = estimateDiv128To64(aSig, 0, bSig); + q = (2 < q) ? q - 2 : 0; + aSig = -((bSig >> 2) * q); + expDiff -= 62; + } + expDiff += 64; + if (0 < expDiff) { + q = estimateDiv128To64(aSig, 0, bSig); + q = (2 < q) ? q - 2 : 0; + q >>= 64 - expDiff; + bSig >>= 2; + aSig = ((aSig >> 1) << (expDiff - 1)) - bSig * q; + } else { + aSig >>= 2; + bSig >>= 2; + } + do { + alternateASig = aSig; + ++q; + aSig -= bSig; + } while (0 <= (int64_t) aSig); + sigMean = aSig + alternateASig; + if ((sigMean < 0) || ((sigMean == 0) && (q & 1))) { + aSig = alternateASig; + } + zSign = ((int64_t) aSig < 0); + if (zSign) + aSig = -aSig; + return normalizeRoundAndPackFloat64(aSign ^ zSign, bExp, aSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of multiplying the double-precision floating-point values +| `a' and `b' then adding 'c', with no intermediate rounding step after the +| multiplication. The operation is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic 754-2008. +| The flags argument allows the caller to select negation of the +| addend, the intermediate product, or the final result. (The difference +| between this and having the caller do a separate negation is that negating +| externally will flip the sign bit on NaNs.) +*----------------------------------------------------------------------------*/ + +float64 float64_muladd(float64 a, float64 b, float64 c, int flags STATUS_PARAM) { + flag aSign, bSign, cSign, zSign; + int aExp, bExp, cExp, pExp, zExp, expDiff; + uint64_t aSig, bSig, cSig; + flag pInf, pZero, pSign; + uint64_t pSig0, pSig1, cSig0, cSig1, zSig0, zSig1; + int shiftcount; + flag signflip, infzero; + + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + c = float64_squash_input_denormal(c STATUS_VAR); + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + bSig = extractFloat64Frac(b); + bExp = extractFloat64Exp(b); + bSign = extractFloat64Sign(b); + cSig = extractFloat64Frac(c); + cExp = extractFloat64Exp(c); + cSign = extractFloat64Sign(c); + + infzero = ((aExp == 0 && aSig == 0 && bExp == 0x7ff && bSig == 0) || + (aExp == 0x7ff && aSig == 0 && bExp == 0 && bSig == 0)); + + /* It is implementation-defined whether the cases of (0,inf,qnan) + * and (inf,0,qnan) raise InvalidOperation or not (and what QNaN + * they return if they do), so we have to hand this information + * off to the target-specific pick-a-NaN routine. + */ + if (((aExp == 0x7ff) && aSig) || ((bExp == 0x7ff) && bSig) || ((cExp == 0x7ff) && cSig)) { + return propagateFloat64MulAddNaN(a, b, c, infzero STATUS_VAR); + } + + if (infzero) { + float_raise(float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + + if (flags & float_muladd_negate_c) { + cSign ^= 1; + } + + signflip = (flags & float_muladd_negate_result) ? 1 : 0; + + /* Work out the sign and type of the product */ + pSign = aSign ^ bSign; + if (flags & float_muladd_negate_product) { + pSign ^= 1; + } + pInf = (aExp == 0x7ff) || (bExp == 0x7ff); + pZero = ((aExp | aSig) == 0) || ((bExp | bSig) == 0); + + if (cExp == 0x7ff) { + if (pInf && (pSign ^ cSign)) { + /* addition of opposite-signed infinities => InvalidOperation */ + float_raise(float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + /* Otherwise generate an infinity of the same sign */ + return packFloat64(cSign ^ signflip, 0x7ff, 0); + } + + if (pInf) { + return packFloat64(pSign ^ signflip, 0x7ff, 0); + } + + if (pZero) { + if (cExp == 0) { + if (cSig == 0) { + /* Adding two exact zeroes */ + if (pSign == cSign) { + zSign = pSign; + } else if (STATUS(float_rounding_mode) == float_round_down) { + zSign = 1; + } else { + zSign = 0; + } + return packFloat64(zSign ^ signflip, 0, 0); + } + /* Exact zero plus a denorm */ + if (STATUS(flush_to_zero)) { + float_raise(float_flag_output_denormal STATUS_VAR); + return packFloat64(cSign ^ signflip, 0, 0); + } + } + /* Zero plus something non-zero : just return the something */ + return c ^ ((uint64_t) signflip << 63); + } + + if (aExp == 0) { + normalizeFloat64Subnormal(aSig, &aExp, &aSig); + } + if (bExp == 0) { + normalizeFloat64Subnormal(bSig, &bExp, &bSig); + } + + /* Calculate the actual result a * b + c */ + + /* Multiply first; this is easy. */ + /* NB: we subtract 0x3fe where float64_mul() subtracts 0x3ff + * because we want the true exponent, not the "one-less-than" + * flavour that roundAndPackFloat64() takes. + */ + pExp = aExp + bExp - 0x3fe; + aSig = (aSig | LIT64(0x0010000000000000)) << 10; + bSig = (bSig | LIT64(0x0010000000000000)) << 11; + mul64To128(aSig, bSig, &pSig0, &pSig1); + if ((int64_t)(pSig0 << 1) >= 0) { + shortShift128Left(pSig0, pSig1, 1, &pSig0, &pSig1); + pExp--; + } + + zSign = pSign ^ signflip; + + /* Now [pSig0:pSig1] is the significand of the multiply, with the explicit + * bit in position 126. + */ + if (cExp == 0) { + if (!cSig) { + /* Throw out the special case of c being an exact zero now */ + shift128RightJamming(pSig0, pSig1, 64, &pSig0, &pSig1); + return roundAndPackFloat64(zSign, pExp - 1, pSig1 STATUS_VAR); + } + normalizeFloat64Subnormal(cSig, &cExp, &cSig); + } + + /* Shift cSig and add the explicit bit so [cSig0:cSig1] is the + * significand of the addend, with the explicit bit in position 126. + */ + cSig0 = cSig << (126 - 64 - 52); + cSig1 = 0; + cSig0 |= LIT64(0x4000000000000000); + expDiff = pExp - cExp; + + if (pSign == cSign) { + /* Addition */ + if (expDiff > 0) { + /* scale c to match p */ + shift128RightJamming(cSig0, cSig1, expDiff, &cSig0, &cSig1); + zExp = pExp; + } else if (expDiff < 0) { + /* scale p to match c */ + shift128RightJamming(pSig0, pSig1, -expDiff, &pSig0, &pSig1); + zExp = cExp; + } else { + /* no scaling needed */ + zExp = cExp; + } + /* Add significands and make sure explicit bit ends up in posn 126 */ + add128(pSig0, pSig1, cSig0, cSig1, &zSig0, &zSig1); + if ((int64_t) zSig0 < 0) { + shift128RightJamming(zSig0, zSig1, 1, &zSig0, &zSig1); + } else { + zExp--; + } + shift128RightJamming(zSig0, zSig1, 64, &zSig0, &zSig1); + return roundAndPackFloat64(zSign, zExp, zSig1 STATUS_VAR); + } else { + /* Subtraction */ + if (expDiff > 0) { + shift128RightJamming(cSig0, cSig1, expDiff, &cSig0, &cSig1); + sub128(pSig0, pSig1, cSig0, cSig1, &zSig0, &zSig1); + zExp = pExp; + } else if (expDiff < 0) { + shift128RightJamming(pSig0, pSig1, -expDiff, &pSig0, &pSig1); + sub128(cSig0, cSig1, pSig0, pSig1, &zSig0, &zSig1); + zExp = cExp; + zSign ^= 1; + } else { + zExp = pExp; + if (lt128(cSig0, cSig1, pSig0, pSig1)) { + sub128(pSig0, pSig1, cSig0, cSig1, &zSig0, &zSig1); + } else if (lt128(pSig0, pSig1, cSig0, cSig1)) { + sub128(cSig0, cSig1, pSig0, pSig1, &zSig0, &zSig1); + zSign ^= 1; + } else { + /* Exact zero */ + zSign = signflip; + if (STATUS(float_rounding_mode) == float_round_down) { + zSign ^= 1; + } + return packFloat64(zSign, 0, 0); + } + } + --zExp; + /* Do the equivalent of normalizeRoundAndPackFloat64() but + * starting with the significand in a pair of uint64_t. + */ + if (zSig0) { + shiftcount = countLeadingZeros64(zSig0) - 1; + shortShift128Left(zSig0, zSig1, shiftcount, &zSig0, &zSig1); + if (zSig1) { + zSig0 |= 1; + } + zExp -= shiftcount; + } else { + shiftcount = countLeadingZeros64(zSig1) - 1; + zSig0 = zSig1 << shiftcount; + zExp -= (shiftcount + 64); + } + return roundAndPackFloat64(zSign, zExp, zSig0 STATUS_VAR); + } +} + +/*---------------------------------------------------------------------------- +| Returns the square root of the double-precision floating-point value `a'. +| The operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float64_sqrt(float64 a STATUS_PARAM) { + flag aSign; + int16 aExp, zExp; + uint64_t aSig, zSig, doubleZSig; + uint64_t rem0, rem1, term0, term1; + a = float64_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + if (aExp == 0x7FF) { + if (aSig) + return propagateFloat64NaN(a, a STATUS_VAR); + if (!aSign) + return a; + float_raise(float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + if (aSign) { + if ((aExp | aSig) == 0) + return a; + float_raise(float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + if (aExp == 0) { + if (aSig == 0) + return float64_zero; + normalizeFloat64Subnormal(aSig, &aExp, &aSig); + } + zExp = ((aExp - 0x3FF) >> 1) + 0x3FE; + aSig |= LIT64(0x0010000000000000); + zSig = estimateSqrt32(aExp, aSig >> 21); + aSig <<= 9 - (aExp & 1); + zSig = estimateDiv128To64(aSig, 0, zSig << 32) + (zSig << 30); + if ((zSig & 0x1FF) <= 5) { + doubleZSig = zSig << 1; + mul64To128(zSig, zSig, &term0, &term1); + sub128(aSig, 0, term0, term1, &rem0, &rem1); + while ((int64_t) rem0 < 0) { + --zSig; + doubleZSig -= 2; + add128(rem0, rem1, zSig >> 63, doubleZSig | 1, &rem0, &rem1); + } + zSig |= ((rem0 | rem1) != 0); + } + return roundAndPackFloat64(0, zExp, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the binary log of the double-precision floating-point value `a'. +| The operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ +float64 float64_log2(float64 a STATUS_PARAM) { + flag aSign, zSign; + int16 aExp; + uint64_t aSig, aSig0, aSig1, zSig, i; + a = float64_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + + if (aExp == 0) { + if (aSig == 0) + return packFloat64(1, 0x7FF, 0); + normalizeFloat64Subnormal(aSig, &aExp, &aSig); + } + if (aSign) { + float_raise(float_flag_invalid STATUS_VAR); + return float64_default_nan; + } + if (aExp == 0x7FF) { + if (aSig) + return propagateFloat64NaN(a, float64_zero STATUS_VAR); + return a; + } + + aExp -= 0x3FF; + aSig |= LIT64(0x0010000000000000); + zSign = aExp < 0; + zSig = (uint64_t) aExp << 52; + for (i = 1LL << 51; i > 0; i >>= 1) { + mul64To128(aSig, aSig, &aSig0, &aSig1); + aSig = (aSig0 << 12) | (aSig1 >> 52); + if (aSig & LIT64(0x0020000000000000)) { + aSig >>= 1; + zSig |= i; + } + } + + if (zSign) + zSig = -zSig; + return normalizeRoundAndPackFloat64(zSign, 0x408, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is equal to the +| corresponding value `b', and 0 otherwise. The invalid exception is raised +| if either operand is a NaN. Otherwise, the comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float64_eq(float64 a, float64 b STATUS_PARAM) { + uint64_t av, bv; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat64Exp(a) == 0x7FF) && extractFloat64Frac(a)) || + ((extractFloat64Exp(b) == 0x7FF) && extractFloat64Frac(b))) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + av = float64_val(a); + bv = float64_val(b); + return (av == bv) || ((uint64_t)((av | bv) << 1) == 0); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is less than or +| equal to the corresponding value `b', and 0 otherwise. The invalid +| exception is raised if either operand is a NaN. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float64_le(float64 a, float64 b STATUS_PARAM) { + flag aSign, bSign; + uint64_t av, bv; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat64Exp(a) == 0x7FF) && extractFloat64Frac(a)) || + ((extractFloat64Exp(b) == 0x7FF) && extractFloat64Frac(b))) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloat64Sign(a); + bSign = extractFloat64Sign(b); + av = float64_val(a); + bv = float64_val(b); + if (aSign != bSign) + return aSign || ((uint64_t)((av | bv) << 1) == 0); + return (av == bv) || (aSign ^ (av < bv)); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is less than +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. The comparison is performed according +| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float64_lt(float64 a, float64 b STATUS_PARAM) { + flag aSign, bSign; + uint64_t av, bv; + + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + if (((extractFloat64Exp(a) == 0x7FF) && extractFloat64Frac(a)) || + ((extractFloat64Exp(b) == 0x7FF) && extractFloat64Frac(b))) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloat64Sign(a); + bSign = extractFloat64Sign(b); + av = float64_val(a); + bv = float64_val(b); + if (aSign != bSign) + return aSign && ((uint64_t)((av | bv) << 1) != 0); + return (av != bv) && (aSign ^ (av < bv)); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. The invalid exception is raised if either +| operand is a NaN. The comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float64_unordered(float64 a, float64 b STATUS_PARAM) { + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat64Exp(a) == 0x7FF) && extractFloat64Frac(a)) || + ((extractFloat64Exp(b) == 0x7FF) && extractFloat64Frac(b))) { + float_raise(float_flag_invalid STATUS_VAR); + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is equal to the +| corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception.The comparison is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float64_eq_quiet(float64 a, float64 b STATUS_PARAM) { + uint64_t av, bv; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat64Exp(a) == 0x7FF) && extractFloat64Frac(a)) || + ((extractFloat64Exp(b) == 0x7FF) && extractFloat64Frac(b))) { + if (float64_is_signaling_nan(a) || float64_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 0; + } + av = float64_val(a); + bv = float64_val(b); + return (av == bv) || ((uint64_t)((av | bv) << 1) == 0); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is less than or +| equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not +| cause an exception. Otherwise, the comparison is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float64_le_quiet(float64 a, float64 b STATUS_PARAM) { + flag aSign, bSign; + uint64_t av, bv; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat64Exp(a) == 0x7FF) && extractFloat64Frac(a)) || + ((extractFloat64Exp(b) == 0x7FF) && extractFloat64Frac(b))) { + if (float64_is_signaling_nan(a) || float64_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloat64Sign(a); + bSign = extractFloat64Sign(b); + av = float64_val(a); + bv = float64_val(b); + if (aSign != bSign) + return aSign || ((uint64_t)((av | bv) << 1) == 0); + return (av == bv) || (aSign ^ (av < bv)); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point value `a' is less than +| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception. Otherwise, the comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float64_lt_quiet(float64 a, float64 b STATUS_PARAM) { + flag aSign, bSign; + uint64_t av, bv; + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat64Exp(a) == 0x7FF) && extractFloat64Frac(a)) || + ((extractFloat64Exp(b) == 0x7FF) && extractFloat64Frac(b))) { + if (float64_is_signaling_nan(a) || float64_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloat64Sign(a); + bSign = extractFloat64Sign(b); + av = float64_val(a); + bv = float64_val(b); + if (aSign != bSign) + return aSign && ((uint64_t)((av | bv) << 1) != 0); + return (av != bv) && (aSign ^ (av < bv)); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the double-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. Quiet NaNs do not cause an exception. The +| comparison is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float64_unordered_quiet(float64 a, float64 b STATUS_PARAM) { + a = float64_squash_input_denormal(a STATUS_VAR); + b = float64_squash_input_denormal(b STATUS_VAR); + + if (((extractFloat64Exp(a) == 0x7FF) && extractFloat64Frac(a)) || + ((extractFloat64Exp(b) == 0x7FF) && extractFloat64Frac(b))) { + if (float64_is_signaling_nan(a) || float64_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point value `a' to the 32-bit two's complement integer format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic---which means in particular that the conversion +| is rounded according to the current rounding mode. If `a' is a NaN, the +| largest positive integer is returned. Otherwise, if the conversion +| overflows, the largest integer with the same sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int32 floatx80_to_int32(floatx80 a STATUS_PARAM) { + flag aSign; + int32 aExp, shiftCount; + uint64_t aSig; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + if ((aExp == 0x7FFF) && (uint64_t)(aSig << 1)) + aSign = 0; + shiftCount = 0x4037 - aExp; + if (shiftCount <= 0) + shiftCount = 1; + shift64RightJamming(aSig, shiftCount, &aSig); + return roundAndPackInt32(aSign, aSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point value `a' to the 32-bit two's complement integer format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic, except that the conversion is always rounded +| toward zero. If `a' is a NaN, the largest positive integer is returned. +| Otherwise, if the conversion overflows, the largest integer with the same +| sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int32 floatx80_to_int32_round_to_zero(floatx80 a STATUS_PARAM) { + flag aSign; + int32 aExp, shiftCount; + uint64_t aSig, savedASig; + int32_t z; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + if (0x401E < aExp) { + if ((aExp == 0x7FFF) && (uint64_t)(aSig << 1)) + aSign = 0; + goto invalid; + } else if (aExp < 0x3FFF) { + if (aExp || aSig) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + return 0; + } + shiftCount = 0x403E - aExp; + savedASig = aSig; + aSig >>= shiftCount; + z = aSig; + if (aSign) + z = -z; + if ((z < 0) ^ aSign) { + invalid: + float_raise(float_flag_invalid STATUS_VAR); + return aSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; + } + if ((aSig << shiftCount) != savedASig) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point value `a' to the 64-bit two's complement integer format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic---which means in particular that the conversion +| is rounded according to the current rounding mode. If `a' is a NaN, +| the largest positive integer is returned. Otherwise, if the conversion +| overflows, the largest integer with the same sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int64 floatx80_to_int64(floatx80 a STATUS_PARAM) { + flag aSign; + int32 aExp, shiftCount; + uint64_t aSig, aSigExtra; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + shiftCount = 0x403E - aExp; + if (shiftCount <= 0) { + if (shiftCount) { + float_raise(float_flag_invalid STATUS_VAR); + if (!aSign || ((aExp == 0x7FFF) && (aSig != LIT64(0x8000000000000000)))) { + return LIT64(0x7FFFFFFFFFFFFFFF); + } + return (int64_t) LIT64(0x8000000000000000); + } + aSigExtra = 0; + } else { + shift64ExtraRightJamming(aSig, 0, shiftCount, &aSig, &aSigExtra); + } + return roundAndPackInt64(aSign, aSig, aSigExtra STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point value `a' to the 64-bit two's complement integer format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic, except that the conversion is always rounded +| toward zero. If `a' is a NaN, the largest positive integer is returned. +| Otherwise, if the conversion overflows, the largest integer with the same +| sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int64 floatx80_to_int64_round_to_zero(floatx80 a STATUS_PARAM) { + flag aSign; + int32 aExp, shiftCount; + uint64_t aSig; + int64 z; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + shiftCount = aExp - 0x403E; + if (0 <= shiftCount) { + aSig &= LIT64(0x7FFFFFFFFFFFFFFF); + if ((a.high != 0xC03E) || aSig) { + float_raise(float_flag_invalid STATUS_VAR); + if (!aSign || ((aExp == 0x7FFF) && aSig)) { + return LIT64(0x7FFFFFFFFFFFFFFF); + } + } + return (int64_t) LIT64(0x8000000000000000); + } else if (aExp < 0x3FFF) { + if (aExp | aSig) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + return 0; + } + z = aSig >> (-shiftCount); + if ((uint64_t)(aSig << (shiftCount & 63))) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + if (aSign) + z = -z; + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point value `a' to the single-precision floating-point format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 floatx80_to_float32(floatx80 a STATUS_PARAM) { + flag aSign; + int32 aExp; + uint64_t aSig; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + if (aExp == 0x7FFF) { + if ((uint64_t)(aSig << 1)) { + return commonNaNToFloat32(floatx80ToCommonNaN(a STATUS_VAR) STATUS_VAR); + } + return packFloat32(aSign, 0xFF, 0); + } + shift64RightJamming(aSig, 33, &aSig); + if (aExp || aSig) + aExp -= 0x3F81; + return roundAndPackFloat32(aSign, aExp, aSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point value `a' to the double-precision floating-point format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 floatx80_to_float64(floatx80 a STATUS_PARAM) { + flag aSign; + int32 aExp; + uint64_t aSig, zSig; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + if (aExp == 0x7FFF) { + if ((uint64_t)(aSig << 1)) { + return commonNaNToFloat64(floatx80ToCommonNaN(a STATUS_VAR) STATUS_VAR); + } + return packFloat64(aSign, 0x7FF, 0); + } + shift64RightJamming(aSig, 1, &zSig); + if (aExp || aSig) + aExp -= 0x3C01; + return roundAndPackFloat64(aSign, aExp, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the extended double-precision floating- +| point value `a' to the quadruple-precision floating-point format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 floatx80_to_float128(floatx80 a STATUS_PARAM) { + flag aSign; + int16 aExp; + uint64_t aSig, zSig0, zSig1; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + if ((aExp == 0x7FFF) && (uint64_t)(aSig << 1)) { + return commonNaNToFloat128(floatx80ToCommonNaN(a STATUS_VAR) STATUS_VAR); + } + shift128Right(aSig << 1, 0, 16, &zSig0, &zSig1); + return packFloat128(aSign, aExp, zSig0, zSig1); +} + +/*---------------------------------------------------------------------------- +| Rounds the extended double-precision floating-point value `a' to an integer, +| and returns the result as an extended quadruple-precision floating-point +| value. The operation is performed according to the IEC/IEEE Standard for +| Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_round_to_int(floatx80 a STATUS_PARAM) { + flag aSign; + int32 aExp; + uint64_t lastBitMask, roundBitsMask; + int8 roundingMode; + floatx80 z; + + aExp = extractFloatx80Exp(a); + if (0x403E <= aExp) { + if ((aExp == 0x7FFF) && (uint64_t)(extractFloatx80Frac(a) << 1)) { + return propagateFloatx80NaN(a, a STATUS_VAR); + } + return a; + } + if (aExp < 0x3FFF) { + if ((aExp == 0) && ((uint64_t)(extractFloatx80Frac(a) << 1) == 0)) { + return a; + } + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + aSign = extractFloatx80Sign(a); + switch (STATUS(float_rounding_mode)) { + case float_round_nearest_even: + if ((aExp == 0x3FFE) && (uint64_t)(extractFloatx80Frac(a) << 1)) { + return packFloatx80(aSign, 0x3FFF, LIT64(0x8000000000000000)); + } + break; + case float_round_down: + return aSign ? packFloatx80(1, 0x3FFF, LIT64(0x8000000000000000)) : packFloatx80(0, 0, 0); + case float_round_up: + return aSign ? packFloatx80(1, 0, 0) : packFloatx80(0, 0x3FFF, LIT64(0x8000000000000000)); + } + return packFloatx80(aSign, 0, 0); + } + lastBitMask = 1; + lastBitMask <<= 0x403E - aExp; + roundBitsMask = lastBitMask - 1; + z = a; + roundingMode = STATUS(float_rounding_mode); + if (roundingMode == float_round_nearest_even) { + z.low += lastBitMask >> 1; + if ((z.low & roundBitsMask) == 0) + z.low &= ~lastBitMask; + } else if (roundingMode != float_round_to_zero) { + if (extractFloatx80Sign(z) ^ (roundingMode == float_round_up)) { + z.low += roundBitsMask; + } + } + z.low &= ~roundBitsMask; + if (z.low == 0) { + ++z.high; + z.low = LIT64(0x8000000000000000); + } + if (z.low != a.low) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the absolute values of the extended double- +| precision floating-point values `a' and `b'. If `zSign' is 1, the sum is +| negated before being returned. `zSign' is ignored if the result is a NaN. +| The addition is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static floatx80 addFloatx80Sigs(floatx80 a, floatx80 b, flag zSign STATUS_PARAM) { + int32 aExp, bExp, zExp; + uint64_t aSig, bSig, zSig0, zSig1; + int32 expDiff; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + bSig = extractFloatx80Frac(b); + bExp = extractFloatx80Exp(b); + expDiff = aExp - bExp; + if (0 < expDiff) { + if (aExp == 0x7FFF) { + if ((uint64_t)(aSig << 1)) + return propagateFloatx80NaN(a, b STATUS_VAR); + return a; + } + if (bExp == 0) + --expDiff; + shift64ExtraRightJamming(bSig, 0, expDiff, &bSig, &zSig1); + zExp = aExp; + } else if (expDiff < 0) { + if (bExp == 0x7FFF) { + if ((uint64_t)(bSig << 1)) + return propagateFloatx80NaN(a, b STATUS_VAR); + return packFloatx80(zSign, 0x7FFF, LIT64(0x8000000000000000)); + } + if (aExp == 0) + ++expDiff; + shift64ExtraRightJamming(aSig, 0, -expDiff, &aSig, &zSig1); + zExp = bExp; + } else { + if (aExp == 0x7FFF) { + if ((uint64_t)((aSig | bSig) << 1)) { + return propagateFloatx80NaN(a, b STATUS_VAR); + } + return a; + } + zSig1 = 0; + zSig0 = aSig + bSig; + if (aExp == 0) { + normalizeFloatx80Subnormal(zSig0, &zExp, &zSig0); + goto roundAndPack; + } + zExp = aExp; + goto shiftRight1; + } + zSig0 = aSig + bSig; + if ((int64_t) zSig0 < 0) + goto roundAndPack; +shiftRight1: + shift64ExtraRightJamming(zSig0, zSig1, 1, &zSig0, &zSig1); + zSig0 |= LIT64(0x8000000000000000); + ++zExp; +roundAndPack: + return roundAndPackFloatx80(STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the absolute values of the extended +| double-precision floating-point values `a' and `b'. If `zSign' is 1, the +| difference is negated before being returned. `zSign' is ignored if the +| result is a NaN. The subtraction is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static floatx80 subFloatx80Sigs(floatx80 a, floatx80 b, flag zSign STATUS_PARAM) { + int32 aExp, bExp, zExp; + uint64_t aSig, bSig, zSig0, zSig1; + int32 expDiff; + floatx80 z; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + bSig = extractFloatx80Frac(b); + bExp = extractFloatx80Exp(b); + expDiff = aExp - bExp; + if (0 < expDiff) + goto aExpBigger; + if (expDiff < 0) + goto bExpBigger; + if (aExp == 0x7FFF) { + if ((uint64_t)((aSig | bSig) << 1)) { + return propagateFloatx80NaN(a, b STATUS_VAR); + } + float_raise(float_flag_invalid STATUS_VAR); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + if (aExp == 0) { + aExp = 1; + bExp = 1; + } + zSig1 = 0; + if (bSig < aSig) + goto aBigger; + if (aSig < bSig) + goto bBigger; + return packFloatx80(STATUS(float_rounding_mode) == float_round_down, 0, 0); +bExpBigger: + if (bExp == 0x7FFF) { + if ((uint64_t)(bSig << 1)) + return propagateFloatx80NaN(a, b STATUS_VAR); + return packFloatx80(zSign ^ 1, 0x7FFF, LIT64(0x8000000000000000)); + } + if (aExp == 0) + ++expDiff; + shift128RightJamming(aSig, 0, -expDiff, &aSig, &zSig1); +bBigger: + sub128(bSig, 0, aSig, zSig1, &zSig0, &zSig1); + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; +aExpBigger: + if (aExp == 0x7FFF) { + if ((uint64_t)(aSig << 1)) + return propagateFloatx80NaN(a, b STATUS_VAR); + return a; + } + if (bExp == 0) + --expDiff; + shift128RightJamming(bSig, 0, expDiff, &bSig, &zSig1); +aBigger: + sub128(aSig, 0, bSig, zSig1, &zSig0, &zSig1); + zExp = aExp; +normalizeRoundAndPack: + return normalizeRoundAndPackFloatx80(STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the extended double-precision floating-point +| values `a' and `b'. The operation is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_add(floatx80 a, floatx80 b STATUS_PARAM) { + flag aSign, bSign; + + aSign = extractFloatx80Sign(a); + bSign = extractFloatx80Sign(b); + if (aSign == bSign) { + return addFloatx80Sigs(a, b, aSign STATUS_VAR); + } else { + return subFloatx80Sigs(a, b, aSign STATUS_VAR); + } +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the extended double-precision floating- +| point values `a' and `b'. The operation is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_sub(floatx80 a, floatx80 b STATUS_PARAM) { + flag aSign, bSign; + + aSign = extractFloatx80Sign(a); + bSign = extractFloatx80Sign(b); + if (aSign == bSign) { + return subFloatx80Sigs(a, b, aSign STATUS_VAR); + } else { + return addFloatx80Sigs(a, b, aSign STATUS_VAR); + } +} + +/*---------------------------------------------------------------------------- +| Returns the result of multiplying the extended double-precision floating- +| point values `a' and `b'. The operation is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_mul(floatx80 a, floatx80 b STATUS_PARAM) { + flag aSign, bSign, zSign; + int32 aExp, bExp, zExp; + uint64_t aSig, bSig, zSig0, zSig1; + floatx80 z; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + bSig = extractFloatx80Frac(b); + bExp = extractFloatx80Exp(b); + bSign = extractFloatx80Sign(b); + zSign = aSign ^ bSign; + if (aExp == 0x7FFF) { + if ((uint64_t)(aSig << 1) || ((bExp == 0x7FFF) && (uint64_t)(bSig << 1))) { + return propagateFloatx80NaN(a, b STATUS_VAR); + } + if ((bExp | bSig) == 0) + goto invalid; + return packFloatx80(zSign, 0x7FFF, LIT64(0x8000000000000000)); + } + if (bExp == 0x7FFF) { + if ((uint64_t)(bSig << 1)) + return propagateFloatx80NaN(a, b STATUS_VAR); + if ((aExp | aSig) == 0) { + invalid: + float_raise(float_flag_invalid STATUS_VAR); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + return packFloatx80(zSign, 0x7FFF, LIT64(0x8000000000000000)); + } + if (aExp == 0) { + if (aSig == 0) + return packFloatx80(zSign, 0, 0); + normalizeFloatx80Subnormal(aSig, &aExp, &aSig); + } + if (bExp == 0) { + if (bSig == 0) + return packFloatx80(zSign, 0, 0); + normalizeFloatx80Subnormal(bSig, &bExp, &bSig); + } + zExp = aExp + bExp - 0x3FFE; + mul64To128(aSig, bSig, &zSig0, &zSig1); + if (0 < (int64_t) zSig0) { + shortShift128Left(zSig0, zSig1, 1, &zSig0, &zSig1); + --zExp; + } + return roundAndPackFloatx80(STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of dividing the extended double-precision floating-point +| value `a' by the corresponding value `b'. The operation is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_div(floatx80 a, floatx80 b STATUS_PARAM) { + flag aSign, bSign, zSign; + int32 aExp, bExp, zExp; + uint64_t aSig, bSig, zSig0, zSig1; + uint64_t rem0, rem1, rem2, term0, term1, term2; + floatx80 z; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + bSig = extractFloatx80Frac(b); + bExp = extractFloatx80Exp(b); + bSign = extractFloatx80Sign(b); + zSign = aSign ^ bSign; + if (aExp == 0x7FFF) { + if ((uint64_t)(aSig << 1)) + return propagateFloatx80NaN(a, b STATUS_VAR); + if (bExp == 0x7FFF) { + if ((uint64_t)(bSig << 1)) + return propagateFloatx80NaN(a, b STATUS_VAR); + goto invalid; + } + return packFloatx80(zSign, 0x7FFF, LIT64(0x8000000000000000)); + } + if (bExp == 0x7FFF) { + if ((uint64_t)(bSig << 1)) + return propagateFloatx80NaN(a, b STATUS_VAR); + return packFloatx80(zSign, 0, 0); + } + if (bExp == 0) { + if (bSig == 0) { + if ((aExp | aSig) == 0) { + invalid: + float_raise(float_flag_invalid STATUS_VAR); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + float_raise(float_flag_divbyzero STATUS_VAR); + return packFloatx80(zSign, 0x7FFF, LIT64(0x8000000000000000)); + } + normalizeFloatx80Subnormal(bSig, &bExp, &bSig); + } + if (aExp == 0) { + if (aSig == 0) + return packFloatx80(zSign, 0, 0); + normalizeFloatx80Subnormal(aSig, &aExp, &aSig); + } + zExp = aExp - bExp + 0x3FFE; + rem1 = 0; + if (bSig <= aSig) { + shift128Right(aSig, 0, 1, &aSig, &rem1); + ++zExp; + } + zSig0 = estimateDiv128To64(aSig, rem1, bSig); + mul64To128(bSig, zSig0, &term0, &term1); + sub128(aSig, rem1, term0, term1, &rem0, &rem1); + while ((int64_t) rem0 < 0) { + --zSig0; + add128(rem0, rem1, 0, bSig, &rem0, &rem1); + } + zSig1 = estimateDiv128To64(rem1, 0, bSig); + if ((uint64_t)(zSig1 << 1) <= 8) { + mul64To128(bSig, zSig1, &term1, &term2); + sub128(rem1, 0, term1, term2, &rem1, &rem2); + while ((int64_t) rem1 < 0) { + --zSig1; + add128(rem1, rem2, 0, bSig, &rem1, &rem2); + } + zSig1 |= ((rem1 | rem2) != 0); + } + return roundAndPackFloatx80(STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the remainder of the extended double-precision floating-point value +| `a' with respect to the corresponding value `b'. The operation is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_rem(floatx80 a, floatx80 b STATUS_PARAM) { + flag aSign, zSign; + int32 aExp, bExp, expDiff; + uint64_t aSig0, aSig1, bSig; + uint64_t q, term0, term1, alternateASig0, alternateASig1; + floatx80 z; + + aSig0 = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + bSig = extractFloatx80Frac(b); + bExp = extractFloatx80Exp(b); + if (aExp == 0x7FFF) { + if ((uint64_t)(aSig0 << 1) || ((bExp == 0x7FFF) && (uint64_t)(bSig << 1))) { + return propagateFloatx80NaN(a, b STATUS_VAR); + } + goto invalid; + } + if (bExp == 0x7FFF) { + if ((uint64_t)(bSig << 1)) + return propagateFloatx80NaN(a, b STATUS_VAR); + return a; + } + if (bExp == 0) { + if (bSig == 0) { + invalid: + float_raise(float_flag_invalid STATUS_VAR); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + normalizeFloatx80Subnormal(bSig, &bExp, &bSig); + } + if (aExp == 0) { + if ((uint64_t)(aSig0 << 1) == 0) + return a; + normalizeFloatx80Subnormal(aSig0, &aExp, &aSig0); + } + bSig |= LIT64(0x8000000000000000); + zSign = aSign; + expDiff = aExp - bExp; + aSig1 = 0; + if (expDiff < 0) { + if (expDiff < -1) + return a; + shift128Right(aSig0, 0, 1, &aSig0, &aSig1); + expDiff = 0; + } + q = (bSig <= aSig0); + if (q) + aSig0 -= bSig; + expDiff -= 64; + while (0 < expDiff) { + q = estimateDiv128To64(aSig0, aSig1, bSig); + q = (2 < q) ? q - 2 : 0; + mul64To128(bSig, q, &term0, &term1); + sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1); + shortShift128Left(aSig0, aSig1, 62, &aSig0, &aSig1); + expDiff -= 62; + } + expDiff += 64; + if (0 < expDiff) { + q = estimateDiv128To64(aSig0, aSig1, bSig); + q = (2 < q) ? q - 2 : 0; + q >>= 64 - expDiff; + mul64To128(bSig, q << (64 - expDiff), &term0, &term1); + sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1); + shortShift128Left(0, bSig, 64 - expDiff, &term0, &term1); + while (le128(term0, term1, aSig0, aSig1)) { + ++q; + sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1); + } + } else { + term1 = 0; + term0 = bSig; + } + sub128(term0, term1, aSig0, aSig1, &alternateASig0, &alternateASig1); + if (lt128(alternateASig0, alternateASig1, aSig0, aSig1) || + (eq128(alternateASig0, alternateASig1, aSig0, aSig1) && (q & 1))) { + aSig0 = alternateASig0; + aSig1 = alternateASig1; + zSign = !zSign; + } + return normalizeRoundAndPackFloatx80(80, zSign, bExp + expDiff, aSig0, aSig1 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the square root of the extended double-precision floating-point +| value `a'. The operation is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_sqrt(floatx80 a STATUS_PARAM) { + flag aSign; + int32 aExp, zExp; + uint64_t aSig0, aSig1, zSig0, zSig1, doubleZSig0; + uint64_t rem0, rem1, rem2, rem3, term0, term1, term2, term3; + floatx80 z; + + aSig0 = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + if (aExp == 0x7FFF) { + if ((uint64_t)(aSig0 << 1)) + return propagateFloatx80NaN(a, a STATUS_VAR); + if (!aSign) + return a; + goto invalid; + } + if (aSign) { + if ((aExp | aSig0) == 0) + return a; + invalid: + float_raise(float_flag_invalid STATUS_VAR); + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + if (aExp == 0) { + if (aSig0 == 0) + return packFloatx80(0, 0, 0); + normalizeFloatx80Subnormal(aSig0, &aExp, &aSig0); + } + zExp = ((aExp - 0x3FFF) >> 1) + 0x3FFF; + zSig0 = estimateSqrt32(aExp, aSig0 >> 32); + shift128Right(aSig0, 0, 2 + (aExp & 1), &aSig0, &aSig1); + zSig0 = estimateDiv128To64(aSig0, aSig1, zSig0 << 32) + (zSig0 << 30); + doubleZSig0 = zSig0 << 1; + mul64To128(zSig0, zSig0, &term0, &term1); + sub128(aSig0, aSig1, term0, term1, &rem0, &rem1); + while ((int64_t) rem0 < 0) { + --zSig0; + doubleZSig0 -= 2; + add128(rem0, rem1, zSig0 >> 63, doubleZSig0 | 1, &rem0, &rem1); + } + zSig1 = estimateDiv128To64(rem1, 0, doubleZSig0); + if ((zSig1 & LIT64(0x3FFFFFFFFFFFFFFF)) <= 5) { + if (zSig1 == 0) + zSig1 = 1; + mul64To128(doubleZSig0, zSig1, &term1, &term2); + sub128(rem1, 0, term1, term2, &rem1, &rem2); + mul64To128(zSig1, zSig1, &term2, &term3); + sub192(rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3); + while ((int64_t) rem1 < 0) { + --zSig1; + shortShift128Left(0, zSig1, 1, &term2, &term3); + term3 |= 1; + term2 |= doubleZSig0; + add192(rem1, rem2, rem3, 0, term2, term3, &rem1, &rem2, &rem3); + } + zSig1 |= ((rem1 | rem2 | rem3) != 0); + } + shortShift128Left(0, zSig1, 1, &zSig0, &zSig1); + zSig0 |= doubleZSig0; + return roundAndPackFloatx80(STATUS(floatx80_rounding_precision), 0, zExp, zSig0, zSig1 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is equal +| to the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. Otherwise, the comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int floatx80_eq(floatx80 a, floatx80 b STATUS_PARAM) { + + if (((extractFloatx80Exp(a) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(a) << 1)) || + ((extractFloatx80Exp(b) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(b) << 1))) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + return (a.low == b.low) && ((a.high == b.high) || ((a.low == 0) && ((uint16_t)((a.high | b.high) << 1) == 0))); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is +| less than or equal to the corresponding value `b', and 0 otherwise. The +| invalid exception is raised if either operand is a NaN. The comparison is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +int floatx80_le(floatx80 a, floatx80 b STATUS_PARAM) { + flag aSign, bSign; + + if (((extractFloatx80Exp(a) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(a) << 1)) || + ((extractFloatx80Exp(b) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(b) << 1))) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloatx80Sign(a); + bSign = extractFloatx80Sign(b); + if (aSign != bSign) { + return aSign || ((((uint16_t)((a.high | b.high) << 1)) | a.low | b.low) == 0); + } + return aSign ? le128(b.high, b.low, a.high, a.low) : le128(a.high, a.low, b.high, b.low); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is +| less than the corresponding value `b', and 0 otherwise. The invalid +| exception is raised if either operand is a NaN. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int floatx80_lt(floatx80 a, floatx80 b STATUS_PARAM) { + flag aSign, bSign; + + if (((extractFloatx80Exp(a) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(a) << 1)) || + ((extractFloatx80Exp(b) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(b) << 1))) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloatx80Sign(a); + bSign = extractFloatx80Sign(b); + if (aSign != bSign) { + return aSign && ((((uint16_t)((a.high | b.high) << 1)) | a.low | b.low) != 0); + } + return aSign ? lt128(b.high, b.low, a.high, a.low) : lt128(a.high, a.low, b.high, b.low); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point values `a' and `b' +| cannot be compared, and 0 otherwise. The invalid exception is raised if +| either operand is a NaN. The comparison is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ +int floatx80_unordered(floatx80 a, floatx80 b STATUS_PARAM) { + if (((extractFloatx80Exp(a) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(a) << 1)) || + ((extractFloatx80Exp(b) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(b) << 1))) { + float_raise(float_flag_invalid STATUS_VAR); + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is +| equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not +| cause an exception. The comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int floatx80_eq_quiet(floatx80 a, floatx80 b STATUS_PARAM) { + + if (((extractFloatx80Exp(a) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(a) << 1)) || + ((extractFloatx80Exp(b) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(b) << 1))) { + if (floatx80_is_signaling_nan(a) || floatx80_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 0; + } + return (a.low == b.low) && ((a.high == b.high) || ((a.low == 0) && ((uint16_t)((a.high | b.high) << 1) == 0))); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is less +| than or equal to the corresponding value `b', and 0 otherwise. Quiet NaNs +| do not cause an exception. Otherwise, the comparison is performed according +| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int floatx80_le_quiet(floatx80 a, floatx80 b STATUS_PARAM) { + flag aSign, bSign; + + if (((extractFloatx80Exp(a) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(a) << 1)) || + ((extractFloatx80Exp(b) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(b) << 1))) { + if (floatx80_is_signaling_nan(a) || floatx80_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloatx80Sign(a); + bSign = extractFloatx80Sign(b); + if (aSign != bSign) { + return aSign || ((((uint16_t)((a.high | b.high) << 1)) | a.low | b.low) == 0); + } + return aSign ? le128(b.high, b.low, a.high, a.low) : le128(a.high, a.low, b.high, b.low); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point value `a' is less +| than the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause +| an exception. Otherwise, the comparison is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int floatx80_lt_quiet(floatx80 a, floatx80 b STATUS_PARAM) { + flag aSign, bSign; + + if (((extractFloatx80Exp(a) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(a) << 1)) || + ((extractFloatx80Exp(b) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(b) << 1))) { + if (floatx80_is_signaling_nan(a) || floatx80_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloatx80Sign(a); + bSign = extractFloatx80Sign(b); + if (aSign != bSign) { + return aSign && ((((uint16_t)((a.high | b.high) << 1)) | a.low | b.low) != 0); + } + return aSign ? lt128(b.high, b.low, a.high, a.low) : lt128(a.high, a.low, b.high, b.low); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the extended double-precision floating-point values `a' and `b' +| cannot be compared, and 0 otherwise. Quiet NaNs do not cause an exception. +| The comparison is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ +int floatx80_unordered_quiet(floatx80 a, floatx80 b STATUS_PARAM) { + if (((extractFloatx80Exp(a) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(a) << 1)) || + ((extractFloatx80Exp(b) == 0x7FFF) && (uint64_t)(extractFloatx80Frac(b) << 1))) { + if (floatx80_is_signaling_nan(a) || floatx80_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point +| value `a' to the 32-bit two's complement integer format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic---which means in particular that the conversion is rounded +| according to the current rounding mode. If `a' is a NaN, the largest +| positive integer is returned. Otherwise, if the conversion overflows, the +| largest integer with the same sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int32 float128_to_int32(float128 a STATUS_PARAM) { + flag aSign; + int32 aExp, shiftCount; + uint64_t aSig0, aSig1; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + aSign = extractFloat128Sign(a); + if ((aExp == 0x7FFF) && (aSig0 | aSig1)) + aSign = 0; + if (aExp) + aSig0 |= LIT64(0x0001000000000000); + aSig0 |= (aSig1 != 0); + shiftCount = 0x4028 - aExp; + if (0 < shiftCount) + shift64RightJamming(aSig0, shiftCount, &aSig0); + return roundAndPackInt32(aSign, aSig0 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point +| value `a' to the 32-bit two's complement integer format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. If +| `a' is a NaN, the largest positive integer is returned. Otherwise, if the +| conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int32 float128_to_int32_round_to_zero(float128 a STATUS_PARAM) { + flag aSign; + int32 aExp, shiftCount; + uint64_t aSig0, aSig1, savedASig; + int32_t z; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + aSign = extractFloat128Sign(a); + aSig0 |= (aSig1 != 0); + if (0x401E < aExp) { + if ((aExp == 0x7FFF) && aSig0) + aSign = 0; + goto invalid; + } else if (aExp < 0x3FFF) { + if (aExp || aSig0) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + return 0; + } + aSig0 |= LIT64(0x0001000000000000); + shiftCount = 0x402F - aExp; + savedASig = aSig0; + aSig0 >>= shiftCount; + z = aSig0; + if (aSign) + z = -z; + if ((z < 0) ^ aSign) { + invalid: + float_raise(float_flag_invalid STATUS_VAR); + return aSign ? (int32_t) 0x80000000 : 0x7FFFFFFF; + } + if ((aSig0 << shiftCount) != savedASig) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point +| value `a' to the 64-bit two's complement integer format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic---which means in particular that the conversion is rounded +| according to the current rounding mode. If `a' is a NaN, the largest +| positive integer is returned. Otherwise, if the conversion overflows, the +| largest integer with the same sign as `a' is returned. +*----------------------------------------------------------------------------*/ + +int64 float128_to_int64(float128 a STATUS_PARAM) { + flag aSign; + int32 aExp, shiftCount; + uint64_t aSig0, aSig1; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + aSign = extractFloat128Sign(a); + if (aExp) + aSig0 |= LIT64(0x0001000000000000); + shiftCount = 0x402F - aExp; + if (shiftCount <= 0) { + if (0x403E < aExp) { + float_raise(float_flag_invalid STATUS_VAR); + if (!aSign || ((aExp == 0x7FFF) && (aSig1 || (aSig0 != LIT64(0x0001000000000000))))) { + return LIT64(0x7FFFFFFFFFFFFFFF); + } + return (int64_t) LIT64(0x8000000000000000); + } + shortShift128Left(aSig0, aSig1, -shiftCount, &aSig0, &aSig1); + } else { + shift64ExtraRightJamming(aSig0, aSig1, shiftCount, &aSig0, &aSig1); + } + return roundAndPackInt64(aSign, aSig0, aSig1 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point +| value `a' to the 64-bit two's complement integer format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. +| If `a' is a NaN, the largest positive integer is returned. Otherwise, if +| the conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int64 float128_to_int64_round_to_zero(float128 a STATUS_PARAM) { + flag aSign; + int32 aExp, shiftCount; + uint64_t aSig0, aSig1; + int64 z; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + aSign = extractFloat128Sign(a); + if (aExp) + aSig0 |= LIT64(0x0001000000000000); + shiftCount = aExp - 0x402F; + if (0 < shiftCount) { + if (0x403E <= aExp) { + aSig0 &= LIT64(0x0000FFFFFFFFFFFF); + if ((a.high == LIT64(0xC03E000000000000)) && (aSig1 < LIT64(0x0002000000000000))) { + if (aSig1) + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } else { + float_raise(float_flag_invalid STATUS_VAR); + if (!aSign || ((aExp == 0x7FFF) && (aSig0 | aSig1))) { + return LIT64(0x7FFFFFFFFFFFFFFF); + } + } + return (int64_t) LIT64(0x8000000000000000); + } + z = (aSig0 << shiftCount) | (aSig1 >> ((-shiftCount) & 63)); + if ((uint64_t)(aSig1 << shiftCount)) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + } else { + if (aExp < 0x3FFF) { + if (aExp | aSig0 | aSig1) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + return 0; + } + z = aSig0 >> (-shiftCount); + if (aSig1 || (shiftCount && (uint64_t)(aSig0 << (shiftCount & 63)))) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + } + if (aSign) + z = -z; + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point +| value `a' to the single-precision floating-point format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +float32 float128_to_float32(float128 a STATUS_PARAM) { + flag aSign; + int32 aExp; + uint64_t aSig0, aSig1; + uint32_t zSig; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + aSign = extractFloat128Sign(a); + if (aExp == 0x7FFF) { + if (aSig0 | aSig1) { + return commonNaNToFloat32(float128ToCommonNaN(a STATUS_VAR) STATUS_VAR); + } + return packFloat32(aSign, 0xFF, 0); + } + aSig0 |= (aSig1 != 0); + shift64RightJamming(aSig0, 18, &aSig0); + zSig = aSig0; + if (aExp || zSig) { + zSig |= 0x40000000; + aExp -= 0x3F81; + } + return roundAndPackFloat32(aSign, aExp, zSig STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point +| value `a' to the double-precision floating-point format. The conversion +| is performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic. +*----------------------------------------------------------------------------*/ + +float64 float128_to_float64(float128 a STATUS_PARAM) { + flag aSign; + int32 aExp; + uint64_t aSig0, aSig1; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + aSign = extractFloat128Sign(a); + if (aExp == 0x7FFF) { + if (aSig0 | aSig1) { + return commonNaNToFloat64(float128ToCommonNaN(a STATUS_VAR) STATUS_VAR); + } + return packFloat64(aSign, 0x7FF, 0); + } + shortShift128Left(aSig0, aSig1, 14, &aSig0, &aSig1); + aSig0 |= (aSig1 != 0); + if (aExp || aSig0) { + aSig0 |= LIT64(0x4000000000000000); + aExp -= 0x3C01; + } + return roundAndPackFloat64(aSign, aExp, aSig0 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the quadruple-precision floating-point +| value `a' to the extended double-precision floating-point format. The +| conversion is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +floatx80 float128_to_floatx80(float128 a STATUS_PARAM) { + flag aSign; + int32 aExp; + uint64_t aSig0, aSig1; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + aSign = extractFloat128Sign(a); + if (aExp == 0x7FFF) { + if (aSig0 | aSig1) { + return commonNaNToFloatx80(float128ToCommonNaN(a STATUS_VAR) STATUS_VAR); + } + return packFloatx80(aSign, 0x7FFF, LIT64(0x8000000000000000)); + } + if (aExp == 0) { + if ((aSig0 | aSig1) == 0) + return packFloatx80(aSign, 0, 0); + normalizeFloat128Subnormal(aSig0, aSig1, &aExp, &aSig0, &aSig1); + } else { + aSig0 |= LIT64(0x0001000000000000); + } + shortShift128Left(aSig0, aSig1, 15, &aSig0, &aSig1); + return roundAndPackFloatx80(80, aSign, aExp, aSig0, aSig1 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Rounds the quadruple-precision floating-point value `a' to an integer, and +| returns the result as a quadruple-precision floating-point value. The +| operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_round_to_int(float128 a STATUS_PARAM) { + flag aSign; + int32 aExp; + uint64_t lastBitMask, roundBitsMask; + int8 roundingMode; + float128 z; + + aExp = extractFloat128Exp(a); + if (0x402F <= aExp) { + if (0x406F <= aExp) { + if ((aExp == 0x7FFF) && (extractFloat128Frac0(a) | extractFloat128Frac1(a))) { + return propagateFloat128NaN(a, a STATUS_VAR); + } + return a; + } + lastBitMask = 1; + lastBitMask = (lastBitMask << (0x406E - aExp)) << 1; + roundBitsMask = lastBitMask - 1; + z = a; + roundingMode = STATUS(float_rounding_mode); + if (roundingMode == float_round_nearest_even) { + if (lastBitMask) { + add128(z.high, z.low, 0, lastBitMask >> 1, &z.high, &z.low); + if ((z.low & roundBitsMask) == 0) + z.low &= ~lastBitMask; + } else { + if ((int64_t) z.low < 0) { + ++z.high; + if ((uint64_t)(z.low << 1) == 0) + z.high &= ~1; + } + } + } else if (roundingMode != float_round_to_zero) { + if (extractFloat128Sign(z) ^ (roundingMode == float_round_up)) { + add128(z.high, z.low, 0, roundBitsMask, &z.high, &z.low); + } + } + z.low &= ~roundBitsMask; + } else { + if (aExp < 0x3FFF) { + if ((((uint64_t)(a.high << 1)) | a.low) == 0) + return a; + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + aSign = extractFloat128Sign(a); + switch (STATUS(float_rounding_mode)) { + case float_round_nearest_even: + if ((aExp == 0x3FFE) && (extractFloat128Frac0(a) | extractFloat128Frac1(a))) { + return packFloat128(aSign, 0x3FFF, 0, 0); + } + break; + case float_round_down: + return aSign ? packFloat128(1, 0x3FFF, 0, 0) : packFloat128(0, 0, 0, 0); + case float_round_up: + return aSign ? packFloat128(1, 0, 0, 0) : packFloat128(0, 0x3FFF, 0, 0); + } + return packFloat128(aSign, 0, 0, 0); + } + lastBitMask = 1; + lastBitMask <<= 0x402F - aExp; + roundBitsMask = lastBitMask - 1; + z.low = 0; + z.high = a.high; + roundingMode = STATUS(float_rounding_mode); + if (roundingMode == float_round_nearest_even) { + z.high += lastBitMask >> 1; + if (((z.high & roundBitsMask) | a.low) == 0) { + z.high &= ~lastBitMask; + } + } else if (roundingMode != float_round_to_zero) { + if (extractFloat128Sign(z) ^ (roundingMode == float_round_up)) { + z.high |= (a.low != 0); + z.high += roundBitsMask; + } + } + z.high &= ~roundBitsMask; + } + if ((z.low != a.low) || (z.high != a.high)) { + STATUS_W(float_exception_flags, (STATUS(float_exception_flags) | float_flag_inexact)); + } + return z; +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the absolute values of the quadruple-precision +| floating-point values `a' and `b'. If `zSign' is 1, the sum is negated +| before being returned. `zSign' is ignored if the result is a NaN. +| The addition is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float128 addFloat128Sigs(float128 a, float128 b, flag zSign STATUS_PARAM) { + int32 aExp, bExp, zExp; + uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; + int32 expDiff; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + bSig1 = extractFloat128Frac1(b); + bSig0 = extractFloat128Frac0(b); + bExp = extractFloat128Exp(b); + expDiff = aExp - bExp; + if (0 < expDiff) { + if (aExp == 0x7FFF) { + if (aSig0 | aSig1) + return propagateFloat128NaN(a, b STATUS_VAR); + return a; + } + if (bExp == 0) { + --expDiff; + } else { + bSig0 |= LIT64(0x0001000000000000); + } + shift128ExtraRightJamming(bSig0, bSig1, 0, expDiff, &bSig0, &bSig1, &zSig2); + zExp = aExp; + } else if (expDiff < 0) { + if (bExp == 0x7FFF) { + if (bSig0 | bSig1) + return propagateFloat128NaN(a, b STATUS_VAR); + return packFloat128(zSign, 0x7FFF, 0, 0); + } + if (aExp == 0) { + ++expDiff; + } else { + aSig0 |= LIT64(0x0001000000000000); + } + shift128ExtraRightJamming(aSig0, aSig1, 0, -expDiff, &aSig0, &aSig1, &zSig2); + zExp = bExp; + } else { + if (aExp == 0x7FFF) { + if (aSig0 | aSig1 | bSig0 | bSig1) { + return propagateFloat128NaN(a, b STATUS_VAR); + } + return a; + } + add128(aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1); + if (aExp == 0) { + if (STATUS(flush_to_zero)) { + if (zSig0 | zSig1) { + float_raise(float_flag_output_denormal STATUS_VAR); + } + return packFloat128(zSign, 0, 0, 0); + } + return packFloat128(zSign, 0, zSig0, zSig1); + } + zSig2 = 0; + zSig0 |= LIT64(0x0002000000000000); + zExp = aExp; + goto shiftRight1; + } + aSig0 |= LIT64(0x0001000000000000); + add128(aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1); + --zExp; + if (zSig0 < LIT64(0x0002000000000000)) + goto roundAndPack; + ++zExp; +shiftRight1: + shift128ExtraRightJamming(zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2); +roundAndPack: + return roundAndPackFloat128(zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the absolute values of the quadruple- +| precision floating-point values `a' and `b'. If `zSign' is 1, the +| difference is negated before being returned. `zSign' is ignored if the +| result is a NaN. The subtraction is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +static float128 subFloat128Sigs(float128 a, float128 b, flag zSign STATUS_PARAM) { + int32 aExp, bExp, zExp; + uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1; + int32 expDiff; + float128 z; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + bSig1 = extractFloat128Frac1(b); + bSig0 = extractFloat128Frac0(b); + bExp = extractFloat128Exp(b); + expDiff = aExp - bExp; + shortShift128Left(aSig0, aSig1, 14, &aSig0, &aSig1); + shortShift128Left(bSig0, bSig1, 14, &bSig0, &bSig1); + if (0 < expDiff) + goto aExpBigger; + if (expDiff < 0) + goto bExpBigger; + if (aExp == 0x7FFF) { + if (aSig0 | aSig1 | bSig0 | bSig1) { + return propagateFloat128NaN(a, b STATUS_VAR); + } + float_raise(float_flag_invalid STATUS_VAR); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + if (aExp == 0) { + aExp = 1; + bExp = 1; + } + if (bSig0 < aSig0) + goto aBigger; + if (aSig0 < bSig0) + goto bBigger; + if (bSig1 < aSig1) + goto aBigger; + if (aSig1 < bSig1) + goto bBigger; + return packFloat128(STATUS(float_rounding_mode) == float_round_down, 0, 0, 0); +bExpBigger: + if (bExp == 0x7FFF) { + if (bSig0 | bSig1) + return propagateFloat128NaN(a, b STATUS_VAR); + return packFloat128(zSign ^ 1, 0x7FFF, 0, 0); + } + if (aExp == 0) { + ++expDiff; + } else { + aSig0 |= LIT64(0x4000000000000000); + } + shift128RightJamming(aSig0, aSig1, -expDiff, &aSig0, &aSig1); + bSig0 |= LIT64(0x4000000000000000); +bBigger: + sub128(bSig0, bSig1, aSig0, aSig1, &zSig0, &zSig1); + zExp = bExp; + zSign ^= 1; + goto normalizeRoundAndPack; +aExpBigger: + if (aExp == 0x7FFF) { + if (aSig0 | aSig1) + return propagateFloat128NaN(a, b STATUS_VAR); + return a; + } + if (bExp == 0) { + --expDiff; + } else { + bSig0 |= LIT64(0x4000000000000000); + } + shift128RightJamming(bSig0, bSig1, expDiff, &bSig0, &bSig1); + aSig0 |= LIT64(0x4000000000000000); +aBigger: + sub128(aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1); + zExp = aExp; +normalizeRoundAndPack: + --zExp; + return normalizeRoundAndPackFloat128(zSign, zExp - 14, zSig0, zSig1 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of adding the quadruple-precision floating-point values +| `a' and `b'. The operation is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_add(float128 a, float128 b STATUS_PARAM) { + flag aSign, bSign; + + aSign = extractFloat128Sign(a); + bSign = extractFloat128Sign(b); + if (aSign == bSign) { + return addFloat128Sigs(a, b, aSign STATUS_VAR); + } else { + return subFloat128Sigs(a, b, aSign STATUS_VAR); + } +} + +/*---------------------------------------------------------------------------- +| Returns the result of subtracting the quadruple-precision floating-point +| values `a' and `b'. The operation is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_sub(float128 a, float128 b STATUS_PARAM) { + flag aSign, bSign; + + aSign = extractFloat128Sign(a); + bSign = extractFloat128Sign(b); + if (aSign == bSign) { + return subFloat128Sigs(a, b, aSign STATUS_VAR); + } else { + return addFloat128Sigs(a, b, aSign STATUS_VAR); + } +} + +/*---------------------------------------------------------------------------- +| Returns the result of multiplying the quadruple-precision floating-point +| values `a' and `b'. The operation is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_mul(float128 a, float128 b STATUS_PARAM) { + flag aSign, bSign, zSign; + int32 aExp, bExp, zExp; + uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3; + float128 z; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + aSign = extractFloat128Sign(a); + bSig1 = extractFloat128Frac1(b); + bSig0 = extractFloat128Frac0(b); + bExp = extractFloat128Exp(b); + bSign = extractFloat128Sign(b); + zSign = aSign ^ bSign; + if (aExp == 0x7FFF) { + if ((aSig0 | aSig1) || ((bExp == 0x7FFF) && (bSig0 | bSig1))) { + return propagateFloat128NaN(a, b STATUS_VAR); + } + if ((bExp | bSig0 | bSig1) == 0) + goto invalid; + return packFloat128(zSign, 0x7FFF, 0, 0); + } + if (bExp == 0x7FFF) { + if (bSig0 | bSig1) + return propagateFloat128NaN(a, b STATUS_VAR); + if ((aExp | aSig0 | aSig1) == 0) { + invalid: + float_raise(float_flag_invalid STATUS_VAR); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + return packFloat128(zSign, 0x7FFF, 0, 0); + } + if (aExp == 0) { + if ((aSig0 | aSig1) == 0) + return packFloat128(zSign, 0, 0, 0); + normalizeFloat128Subnormal(aSig0, aSig1, &aExp, &aSig0, &aSig1); + } + if (bExp == 0) { + if ((bSig0 | bSig1) == 0) + return packFloat128(zSign, 0, 0, 0); + normalizeFloat128Subnormal(bSig0, bSig1, &bExp, &bSig0, &bSig1); + } + zExp = aExp + bExp - 0x4000; + aSig0 |= LIT64(0x0001000000000000); + shortShift128Left(bSig0, bSig1, 16, &bSig0, &bSig1); + mul128To256(aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1, &zSig2, &zSig3); + add128(zSig0, zSig1, aSig0, aSig1, &zSig0, &zSig1); + zSig2 |= (zSig3 != 0); + if (LIT64(0x0002000000000000) <= zSig0) { + shift128ExtraRightJamming(zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2); + ++zExp; + } + return roundAndPackFloat128(zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the result of dividing the quadruple-precision floating-point value +| `a' by the corresponding value `b'. The operation is performed according to +| the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_div(float128 a, float128 b STATUS_PARAM) { + flag aSign, bSign, zSign; + int32 aExp, bExp, zExp; + uint64_t aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2; + uint64_t rem0, rem1, rem2, rem3, term0, term1, term2, term3; + float128 z; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + aSign = extractFloat128Sign(a); + bSig1 = extractFloat128Frac1(b); + bSig0 = extractFloat128Frac0(b); + bExp = extractFloat128Exp(b); + bSign = extractFloat128Sign(b); + zSign = aSign ^ bSign; + if (aExp == 0x7FFF) { + if (aSig0 | aSig1) + return propagateFloat128NaN(a, b STATUS_VAR); + if (bExp == 0x7FFF) { + if (bSig0 | bSig1) + return propagateFloat128NaN(a, b STATUS_VAR); + goto invalid; + } + return packFloat128(zSign, 0x7FFF, 0, 0); + } + if (bExp == 0x7FFF) { + if (bSig0 | bSig1) + return propagateFloat128NaN(a, b STATUS_VAR); + return packFloat128(zSign, 0, 0, 0); + } + if (bExp == 0) { + if ((bSig0 | bSig1) == 0) { + if ((aExp | aSig0 | aSig1) == 0) { + invalid: + float_raise(float_flag_invalid STATUS_VAR); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + float_raise(float_flag_divbyzero STATUS_VAR); + return packFloat128(zSign, 0x7FFF, 0, 0); + } + normalizeFloat128Subnormal(bSig0, bSig1, &bExp, &bSig0, &bSig1); + } + if (aExp == 0) { + if ((aSig0 | aSig1) == 0) + return packFloat128(zSign, 0, 0, 0); + normalizeFloat128Subnormal(aSig0, aSig1, &aExp, &aSig0, &aSig1); + } + zExp = aExp - bExp + 0x3FFD; + shortShift128Left(aSig0 | LIT64(0x0001000000000000), aSig1, 15, &aSig0, &aSig1); + shortShift128Left(bSig0 | LIT64(0x0001000000000000), bSig1, 15, &bSig0, &bSig1); + if (le128(bSig0, bSig1, aSig0, aSig1)) { + shift128Right(aSig0, aSig1, 1, &aSig0, &aSig1); + ++zExp; + } + zSig0 = estimateDiv128To64(aSig0, aSig1, bSig0); + mul128By64To192(bSig0, bSig1, zSig0, &term0, &term1, &term2); + sub192(aSig0, aSig1, 0, term0, term1, term2, &rem0, &rem1, &rem2); + while ((int64_t) rem0 < 0) { + --zSig0; + add192(rem0, rem1, rem2, 0, bSig0, bSig1, &rem0, &rem1, &rem2); + } + zSig1 = estimateDiv128To64(rem1, rem2, bSig0); + if ((zSig1 & 0x3FFF) <= 4) { + mul128By64To192(bSig0, bSig1, zSig1, &term1, &term2, &term3); + sub192(rem1, rem2, 0, term1, term2, term3, &rem1, &rem2, &rem3); + while ((int64_t) rem1 < 0) { + --zSig1; + add192(rem1, rem2, rem3, 0, bSig0, bSig1, &rem1, &rem2, &rem3); + } + zSig1 |= ((rem1 | rem2 | rem3) != 0); + } + shift128ExtraRightJamming(zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2); + return roundAndPackFloat128(zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the remainder of the quadruple-precision floating-point value `a' +| with respect to the corresponding value `b'. The operation is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_rem(float128 a, float128 b STATUS_PARAM) { + flag aSign, zSign; + int32 aExp, bExp, expDiff; + uint64_t aSig0, aSig1, bSig0, bSig1, q, term0, term1, term2; + uint64_t allZero, alternateASig0, alternateASig1, sigMean1; + int64_t sigMean0; + float128 z; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + aSign = extractFloat128Sign(a); + bSig1 = extractFloat128Frac1(b); + bSig0 = extractFloat128Frac0(b); + bExp = extractFloat128Exp(b); + if (aExp == 0x7FFF) { + if ((aSig0 | aSig1) || ((bExp == 0x7FFF) && (bSig0 | bSig1))) { + return propagateFloat128NaN(a, b STATUS_VAR); + } + goto invalid; + } + if (bExp == 0x7FFF) { + if (bSig0 | bSig1) + return propagateFloat128NaN(a, b STATUS_VAR); + return a; + } + if (bExp == 0) { + if ((bSig0 | bSig1) == 0) { + invalid: + float_raise(float_flag_invalid STATUS_VAR); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + normalizeFloat128Subnormal(bSig0, bSig1, &bExp, &bSig0, &bSig1); + } + if (aExp == 0) { + if ((aSig0 | aSig1) == 0) + return a; + normalizeFloat128Subnormal(aSig0, aSig1, &aExp, &aSig0, &aSig1); + } + expDiff = aExp - bExp; + if (expDiff < -1) + return a; + shortShift128Left(aSig0 | LIT64(0x0001000000000000), aSig1, 15 - (expDiff < 0), &aSig0, &aSig1); + shortShift128Left(bSig0 | LIT64(0x0001000000000000), bSig1, 15, &bSig0, &bSig1); + q = le128(bSig0, bSig1, aSig0, aSig1); + if (q) + sub128(aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1); + expDiff -= 64; + while (0 < expDiff) { + q = estimateDiv128To64(aSig0, aSig1, bSig0); + q = (4 < q) ? q - 4 : 0; + mul128By64To192(bSig0, bSig1, q, &term0, &term1, &term2); + shortShift192Left(term0, term1, term2, 61, &term1, &term2, &allZero); + shortShift128Left(aSig0, aSig1, 61, &aSig0, &allZero); + sub128(aSig0, 0, term1, term2, &aSig0, &aSig1); + expDiff -= 61; + } + if (-64 < expDiff) { + q = estimateDiv128To64(aSig0, aSig1, bSig0); + q = (4 < q) ? q - 4 : 0; + q >>= -expDiff; + shift128Right(bSig0, bSig1, 12, &bSig0, &bSig1); + expDiff += 52; + if (expDiff < 0) { + shift128Right(aSig0, aSig1, -expDiff, &aSig0, &aSig1); + } else { + shortShift128Left(aSig0, aSig1, expDiff, &aSig0, &aSig1); + } + mul128By64To192(bSig0, bSig1, q, &term0, &term1, &term2); + sub128(aSig0, aSig1, term1, term2, &aSig0, &aSig1); + } else { + shift128Right(aSig0, aSig1, 12, &aSig0, &aSig1); + shift128Right(bSig0, bSig1, 12, &bSig0, &bSig1); + } + do { + alternateASig0 = aSig0; + alternateASig1 = aSig1; + ++q; + sub128(aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1); + } while (0 <= (int64_t) aSig0); + add128(aSig0, aSig1, alternateASig0, alternateASig1, (uint64_t *) &sigMean0, &sigMean1); + if ((sigMean0 < 0) || (((sigMean0 | sigMean1) == 0) && (q & 1))) { + aSig0 = alternateASig0; + aSig1 = alternateASig1; + } + zSign = ((int64_t) aSig0 < 0); + if (zSign) + sub128(0, 0, aSig0, aSig1, &aSig0, &aSig1); + return normalizeRoundAndPackFloat128(aSign ^ zSign, bExp - 4, aSig0, aSig1 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns the square root of the quadruple-precision floating-point value `a'. +| The operation is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +float128 float128_sqrt(float128 a STATUS_PARAM) { + flag aSign; + int32 aExp, zExp; + uint64_t aSig0, aSig1, zSig0, zSig1, zSig2, doubleZSig0; + uint64_t rem0, rem1, rem2, rem3, term0, term1, term2, term3; + float128 z; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + aSign = extractFloat128Sign(a); + if (aExp == 0x7FFF) { + if (aSig0 | aSig1) + return propagateFloat128NaN(a, a STATUS_VAR); + if (!aSign) + return a; + goto invalid; + } + if (aSign) { + if ((aExp | aSig0 | aSig1) == 0) + return a; + invalid: + float_raise(float_flag_invalid STATUS_VAR); + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + if (aExp == 0) { + if ((aSig0 | aSig1) == 0) + return packFloat128(0, 0, 0, 0); + normalizeFloat128Subnormal(aSig0, aSig1, &aExp, &aSig0, &aSig1); + } + zExp = ((aExp - 0x3FFF) >> 1) + 0x3FFE; + aSig0 |= LIT64(0x0001000000000000); + zSig0 = estimateSqrt32(aExp, aSig0 >> 17); + shortShift128Left(aSig0, aSig1, 13 - (aExp & 1), &aSig0, &aSig1); + zSig0 = estimateDiv128To64(aSig0, aSig1, zSig0 << 32) + (zSig0 << 30); + doubleZSig0 = zSig0 << 1; + mul64To128(zSig0, zSig0, &term0, &term1); + sub128(aSig0, aSig1, term0, term1, &rem0, &rem1); + while ((int64_t) rem0 < 0) { + --zSig0; + doubleZSig0 -= 2; + add128(rem0, rem1, zSig0 >> 63, doubleZSig0 | 1, &rem0, &rem1); + } + zSig1 = estimateDiv128To64(rem1, 0, doubleZSig0); + if ((zSig1 & 0x1FFF) <= 5) { + if (zSig1 == 0) + zSig1 = 1; + mul64To128(doubleZSig0, zSig1, &term1, &term2); + sub128(rem1, 0, term1, term2, &rem1, &rem2); + mul64To128(zSig1, zSig1, &term2, &term3); + sub192(rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3); + while ((int64_t) rem1 < 0) { + --zSig1; + shortShift128Left(0, zSig1, 1, &term2, &term3); + term3 |= 1; + term2 |= doubleZSig0; + add192(rem1, rem2, rem3, 0, term2, term3, &rem1, &rem2, &rem3); + } + zSig1 |= ((rem1 | rem2 | rem3) != 0); + } + shift128ExtraRightJamming(zSig0, zSig1, 0, 14, &zSig0, &zSig1, &zSig2); + return roundAndPackFloat128(0, zExp, zSig0, zSig1, zSig2 STATUS_VAR); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is equal to +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. Otherwise, the comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float128_eq(float128 a, float128 b STATUS_PARAM) { + + if (((extractFloat128Exp(a) == 0x7FFF) && (extractFloat128Frac0(a) | extractFloat128Frac1(a))) || + ((extractFloat128Exp(b) == 0x7FFF) && (extractFloat128Frac0(b) | extractFloat128Frac1(b)))) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + return (a.low == b.low) && ((a.high == b.high) || ((a.low == 0) && ((uint64_t)((a.high | b.high) << 1) == 0))); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is less than +| or equal to the corresponding value `b', and 0 otherwise. The invalid +| exception is raised if either operand is a NaN. The comparison is performed +| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float128_le(float128 a, float128 b STATUS_PARAM) { + flag aSign, bSign; + + if (((extractFloat128Exp(a) == 0x7FFF) && (extractFloat128Frac0(a) | extractFloat128Frac1(a))) || + ((extractFloat128Exp(b) == 0x7FFF) && (extractFloat128Frac0(b) | extractFloat128Frac1(b)))) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloat128Sign(a); + bSign = extractFloat128Sign(b); + if (aSign != bSign) { + return aSign || ((((uint64_t)((a.high | b.high) << 1)) | a.low | b.low) == 0); + } + return aSign ? le128(b.high, b.low, a.high, a.low) : le128(a.high, a.low, b.high, b.low); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is less than +| the corresponding value `b', and 0 otherwise. The invalid exception is +| raised if either operand is a NaN. The comparison is performed according +| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float128_lt(float128 a, float128 b STATUS_PARAM) { + flag aSign, bSign; + + if (((extractFloat128Exp(a) == 0x7FFF) && (extractFloat128Frac0(a) | extractFloat128Frac1(a))) || + ((extractFloat128Exp(b) == 0x7FFF) && (extractFloat128Frac0(b) | extractFloat128Frac1(b)))) { + float_raise(float_flag_invalid STATUS_VAR); + return 0; + } + aSign = extractFloat128Sign(a); + bSign = extractFloat128Sign(b); + if (aSign != bSign) { + return aSign && ((((uint64_t)((a.high | b.high) << 1)) | a.low | b.low) != 0); + } + return aSign ? lt128(b.high, b.low, a.high, a.low) : lt128(a.high, a.low, b.high, b.low); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. The invalid exception is raised if either +| operand is a NaN. The comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float128_unordered(float128 a, float128 b STATUS_PARAM) { + if (((extractFloat128Exp(a) == 0x7FFF) && (extractFloat128Frac0(a) | extractFloat128Frac1(a))) || + ((extractFloat128Exp(b) == 0x7FFF) && (extractFloat128Frac0(b) | extractFloat128Frac1(b)))) { + float_raise(float_flag_invalid STATUS_VAR); + return 1; + } + return 0; +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is equal to +| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception. The comparison is performed according to the IEC/IEEE Standard +| for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float128_eq_quiet(float128 a, float128 b STATUS_PARAM) { + + if (((extractFloat128Exp(a) == 0x7FFF) && (extractFloat128Frac0(a) | extractFloat128Frac1(a))) || + ((extractFloat128Exp(b) == 0x7FFF) && (extractFloat128Frac0(b) | extractFloat128Frac1(b)))) { + if (float128_is_signaling_nan(a) || float128_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 0; + } + return (a.low == b.low) && ((a.high == b.high) || ((a.low == 0) && ((uint64_t)((a.high | b.high) << 1) == 0))); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is less than +| or equal to the corresponding value `b', and 0 otherwise. Quiet NaNs do not +| cause an exception. Otherwise, the comparison is performed according to the +| IEC/IEEE Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float128_le_quiet(float128 a, float128 b STATUS_PARAM) { + flag aSign, bSign; + + if (((extractFloat128Exp(a) == 0x7FFF) && (extractFloat128Frac0(a) | extractFloat128Frac1(a))) || + ((extractFloat128Exp(b) == 0x7FFF) && (extractFloat128Frac0(b) | extractFloat128Frac1(b)))) { + if (float128_is_signaling_nan(a) || float128_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloat128Sign(a); + bSign = extractFloat128Sign(b); + if (aSign != bSign) { + return aSign || ((((uint64_t)((a.high | b.high) << 1)) | a.low | b.low) == 0); + } + return aSign ? le128(b.high, b.low, a.high, a.low) : le128(a.high, a.low, b.high, b.low); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point value `a' is less than +| the corresponding value `b', and 0 otherwise. Quiet NaNs do not cause an +| exception. Otherwise, the comparison is performed according to the IEC/IEEE +| Standard for Binary Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float128_lt_quiet(float128 a, float128 b STATUS_PARAM) { + flag aSign, bSign; + + if (((extractFloat128Exp(a) == 0x7FFF) && (extractFloat128Frac0(a) | extractFloat128Frac1(a))) || + ((extractFloat128Exp(b) == 0x7FFF) && (extractFloat128Frac0(b) | extractFloat128Frac1(b)))) { + if (float128_is_signaling_nan(a) || float128_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 0; + } + aSign = extractFloat128Sign(a); + bSign = extractFloat128Sign(b); + if (aSign != bSign) { + return aSign && ((((uint64_t)((a.high | b.high) << 1)) | a.low | b.low) != 0); + } + return aSign ? lt128(b.high, b.low, a.high, a.low) : lt128(a.high, a.low, b.high, b.low); +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the quadruple-precision floating-point values `a' and `b' cannot +| be compared, and 0 otherwise. Quiet NaNs do not cause an exception. The +| comparison is performed according to the IEC/IEEE Standard for Binary +| Floating-Point Arithmetic. +*----------------------------------------------------------------------------*/ + +int float128_unordered_quiet(float128 a, float128 b STATUS_PARAM) { + if (((extractFloat128Exp(a) == 0x7FFF) && (extractFloat128Frac0(a) | extractFloat128Frac1(a))) || + ((extractFloat128Exp(b) == 0x7FFF) && (extractFloat128Frac0(b) | extractFloat128Frac1(b)))) { + if (float128_is_signaling_nan(a) || float128_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return 1; + } + return 0; +} + +/* misc functions */ +float32 uint32_to_float32(uint32 a STATUS_PARAM) { + return int64_to_float32(a STATUS_VAR); +} + +float64 uint32_to_float64(uint32 a STATUS_PARAM) { + return int64_to_float64(a STATUS_VAR); +} + +uint32 float32_to_uint32(float32 a STATUS_PARAM) { + int64_t v; + uint32 res; + + v = float32_to_int64(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise(float_flag_invalid STATUS_VAR); + } else if (v > 0xffffffff) { + res = 0xffffffff; + float_raise(float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + +uint32 float32_to_uint32_round_to_zero(float32 a STATUS_PARAM) { + int64_t v; + uint32 res; + + v = float32_to_int64_round_to_zero(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise(float_flag_invalid STATUS_VAR); + } else if (v > 0xffffffff) { + res = 0xffffffff; + float_raise(float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + +uint16 float32_to_uint16_round_to_zero(float32 a STATUS_PARAM) { + int64_t v; + uint16 res; + + v = float32_to_int64_round_to_zero(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise(float_flag_invalid STATUS_VAR); + } else if (v > 0xffff) { + res = 0xffff; + float_raise(float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + +uint32 float64_to_uint32(float64 a STATUS_PARAM) { + int64_t v; + uint32 res; + + v = float64_to_int64(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise(float_flag_invalid STATUS_VAR); + } else if (v > 0xffffffff) { + res = 0xffffffff; + float_raise(float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + +uint32 float64_to_uint32_round_to_zero(float64 a STATUS_PARAM) { + int64_t v; + uint32 res; + + v = float64_to_int64_round_to_zero(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise(float_flag_invalid STATUS_VAR); + } else if (v > 0xffffffff) { + res = 0xffffffff; + float_raise(float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + +uint16 float64_to_uint16_round_to_zero(float64 a STATUS_PARAM) { + int64_t v; + uint16 res; + + v = float64_to_int64_round_to_zero(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise(float_flag_invalid STATUS_VAR); + } else if (v > 0xffff) { + res = 0xffff; + float_raise(float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + +/* FIXME: This looks broken. */ +uint64_t float64_to_uint64(float64 a STATUS_PARAM) { + int64_t v; + + v = float64_val(int64_to_float64(INT64_MIN STATUS_VAR)); + v += float64_val(a); + v = float64_to_int64(make_float64(v) STATUS_VAR); + + return v - INT64_MIN; +} + +uint64_t float64_to_uint64_round_to_zero(float64 a STATUS_PARAM) { + int64_t v; + + v = float64_val(int64_to_float64(INT64_MIN STATUS_VAR)); + v += float64_val(a); + v = float64_to_int64_round_to_zero(make_float64(v) STATUS_VAR); + + return v - INT64_MIN; +} + +#define COMPARE(s, nan_exp) \ + SINLINE int float##s##_compare_internal(float##s a, float##s b, int is_quiet STATUS_PARAM) { \ + flag aSign, bSign; \ + uint##s##_t av, bv; \ + a = float##s##_squash_input_denormal(a STATUS_VAR); \ + b = float##s##_squash_input_denormal(b STATUS_VAR); \ + \ + if (((extractFloat##s##Exp(a) == nan_exp) && extractFloat##s##Frac(a)) || \ + ((extractFloat##s##Exp(b) == nan_exp) && extractFloat##s##Frac(b))) { \ + if (!is_quiet || float##s##_is_signaling_nan(a) || float##s##_is_signaling_nan(b)) { \ + float_raise(float_flag_invalid STATUS_VAR); \ + } \ + return float_relation_unordered; \ + } \ + aSign = extractFloat##s##Sign(a); \ + bSign = extractFloat##s##Sign(b); \ + av = float##s##_val(a); \ + bv = float##s##_val(b); \ + if (aSign != bSign) { \ + if ((uint##s##_t)((av | bv) << 1) == 0) { \ + /* zero case */ \ + return float_relation_equal; \ + } else { \ + return 1 - (2 * aSign); \ + } \ + } else { \ + if (av == bv) { \ + return float_relation_equal; \ + } else { \ + return 1 - 2 * (aSign ^ (av < bv)); \ + } \ + } \ + } \ + \ + int float##s##_compare(float##s a, float##s b STATUS_PARAM) { \ + return float##s##_compare_internal(a, b, 0 STATUS_VAR); \ + } \ + \ + int float##s##_compare_quiet(float##s a, float##s b STATUS_PARAM) { \ + return float##s##_compare_internal(a, b, 1 STATUS_VAR); \ + } + +COMPARE(32, 0xff) +COMPARE(64, 0x7ff) + +SINLINE int floatx80_compare_internal(floatx80 a, floatx80 b, int is_quiet STATUS_PARAM) { + flag aSign, bSign; + + if (((extractFloatx80Exp(a) == 0x7fff) && (extractFloatx80Frac(a) << 1)) || + ((extractFloatx80Exp(b) == 0x7fff) && (extractFloatx80Frac(b) << 1))) { + if (!is_quiet || floatx80_is_signaling_nan(a) || floatx80_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return float_relation_unordered; + } + aSign = extractFloatx80Sign(a); + bSign = extractFloatx80Sign(b); + if (aSign != bSign) { + + if ((((uint16_t)((a.high | b.high) << 1)) == 0) && ((a.low | b.low) == 0)) { + /* zero case */ + return float_relation_equal; + } else { + return 1 - (2 * aSign); + } + } else { + if (a.low == b.low && a.high == b.high) { + return float_relation_equal; + } else { + return 1 - 2 * (aSign ^ (lt128(a.high, a.low, b.high, b.low))); + } + } +} + +int floatx80_compare(floatx80 a, floatx80 b STATUS_PARAM) { + return floatx80_compare_internal(a, b, 0 STATUS_VAR); +} + +int floatx80_compare_quiet(floatx80 a, floatx80 b STATUS_PARAM) { + return floatx80_compare_internal(a, b, 1 STATUS_VAR); +} + +SINLINE int float128_compare_internal(float128 a, float128 b, int is_quiet STATUS_PARAM) { + flag aSign, bSign; + + if (((extractFloat128Exp(a) == 0x7fff) && (extractFloat128Frac0(a) | extractFloat128Frac1(a))) || + ((extractFloat128Exp(b) == 0x7fff) && (extractFloat128Frac0(b) | extractFloat128Frac1(b)))) { + if (!is_quiet || float128_is_signaling_nan(a) || float128_is_signaling_nan(b)) { + float_raise(float_flag_invalid STATUS_VAR); + } + return float_relation_unordered; + } + aSign = extractFloat128Sign(a); + bSign = extractFloat128Sign(b); + if (aSign != bSign) { + if ((((a.high | b.high) << 1) | a.low | b.low) == 0) { + /* zero case */ + return float_relation_equal; + } else { + return 1 - (2 * aSign); + } + } else { + if (a.low == b.low && a.high == b.high) { + return float_relation_equal; + } else { + return 1 - 2 * (aSign ^ (lt128(a.high, a.low, b.high, b.low))); + } + } +} + +int float128_compare(float128 a, float128 b STATUS_PARAM) { + return float128_compare_internal(a, b, 0 STATUS_VAR); +} + +int float128_compare_quiet(float128 a, float128 b STATUS_PARAM) { + return float128_compare_internal(a, b, 1 STATUS_VAR); +} + +/* min() and max() functions. These can't be implemented as + * 'compare and pick one input' because that would mishandle + * NaNs and +0 vs -0. + */ +#define MINMAX(s, nan_exp) \ + SINLINE float##s float##s##_minmax(float##s a, float##s b, int ismin STATUS_PARAM) { \ + flag aSign, bSign; \ + uint##s##_t av, bv; \ + a = float##s##_squash_input_denormal(a STATUS_VAR); \ + b = float##s##_squash_input_denormal(b STATUS_VAR); \ + if (float##s##_is_any_nan(a) || float##s##_is_any_nan(b)) { \ + return propagateFloat##s##NaN(a, b STATUS_VAR); \ + } \ + aSign = extractFloat##s##Sign(a); \ + bSign = extractFloat##s##Sign(b); \ + av = float##s##_val(a); \ + bv = float##s##_val(b); \ + if (aSign != bSign) { \ + if (ismin) { \ + return aSign ? a : b; \ + } else { \ + return aSign ? b : a; \ + } \ + } else { \ + if (ismin) { \ + return (aSign ^ (av < bv)) ? a : b; \ + } else { \ + return (aSign ^ (av < bv)) ? b : a; \ + } \ + } \ + } \ + \ + float##s float##s##_min(float##s a, float##s b STATUS_PARAM) { \ + return float##s##_minmax(a, b, 1 STATUS_VAR); \ + } \ + \ + float##s float##s##_max(float##s a, float##s b STATUS_PARAM) { \ + return float##s##_minmax(a, b, 0 STATUS_VAR); \ + } + +MINMAX(32, 0xff) +MINMAX(64, 0x7ff) + +/* Multiply A by 2 raised to the power N. */ +float32 float32_scalbn(float32 a, int n STATUS_PARAM) { + flag aSign; + int16_t aExp; + uint32_t aSig; + + a = float32_squash_input_denormal(a STATUS_VAR); + aSig = extractFloat32Frac(a); + aExp = extractFloat32Exp(a); + aSign = extractFloat32Sign(a); + + if (aExp == 0xFF) { + if (aSig) { + return propagateFloat32NaN(a, a STATUS_VAR); + } + return a; + } + if (aExp != 0) + aSig |= 0x00800000; + else if (aSig == 0) + return a; + + if (n > 0x200) { + n = 0x200; + } else if (n < -0x200) { + n = -0x200; + } + + aExp += n - 1; + aSig <<= 7; + return normalizeRoundAndPackFloat32(aSign, aExp, aSig STATUS_VAR); +} + +float64 float64_scalbn(float64 a, int n STATUS_PARAM) { + flag aSign; + int16_t aExp; + uint64_t aSig; + + a = float64_squash_input_denormal(a STATUS_VAR); + aSig = extractFloat64Frac(a); + aExp = extractFloat64Exp(a); + aSign = extractFloat64Sign(a); + + if (aExp == 0x7FF) { + if (aSig) { + return propagateFloat64NaN(a, a STATUS_VAR); + } + return a; + } + if (aExp != 0) + aSig |= LIT64(0x0010000000000000); + else if (aSig == 0) + return a; + + if (n > 0x1000) { + n = 0x1000; + } else if (n < -0x1000) { + n = -0x1000; + } + + aExp += n - 1; + aSig <<= 10; + return normalizeRoundAndPackFloat64(aSign, aExp, aSig STATUS_VAR); +} + +floatx80 floatx80_scalbn(floatx80 a, int n STATUS_PARAM) { + flag aSign; + int32_t aExp; + uint64_t aSig; + + aSig = extractFloatx80Frac(a); + aExp = extractFloatx80Exp(a); + aSign = extractFloatx80Sign(a); + + if (aExp == 0x7FFF) { + if (aSig << 1) { + return propagateFloatx80NaN(a, a STATUS_VAR); + } + return a; + } + + if (aExp == 0 && aSig == 0) + return a; + + if (n > 0x10000) { + n = 0x10000; + } else if (n < -0x10000) { + n = -0x10000; + } + + aExp += n; + return normalizeRoundAndPackFloatx80(STATUS(floatx80_rounding_precision), aSign, aExp, aSig, 0 STATUS_VAR); +} + +float128 float128_scalbn(float128 a, int n STATUS_PARAM) { + flag aSign; + int32_t aExp; + uint64_t aSig0, aSig1; + + aSig1 = extractFloat128Frac1(a); + aSig0 = extractFloat128Frac0(a); + aExp = extractFloat128Exp(a); + aSign = extractFloat128Sign(a); + if (aExp == 0x7FFF) { + if (aSig0 | aSig1) { + return propagateFloat128NaN(a, a STATUS_VAR); + } + return a; + } + if (aExp != 0) + aSig0 |= LIT64(0x0001000000000000); + else if (aSig0 == 0 && aSig1 == 0) + return a; + + if (n > 0x10000) { + n = 0x10000; + } else if (n < -0x10000) { + n = -0x10000; + } + + aExp += n - 1; + return normalizeRoundAndPackFloat128(aSign, aExp, aSig0, aSig1 STATUS_VAR); +} diff --git a/src/base/emu-i386/simx86/softfloat/softfloat.h b/src/base/emu-i386/simx86/softfloat/softfloat.h new file mode 100644 index 0000000..5f0f765 --- /dev/null +++ b/src/base/emu-i386/simx86/softfloat/softfloat.h @@ -0,0 +1,611 @@ +/* + * QEMU float support + * + * Derived from SoftFloat. + */ + +/*============================================================================ + +This C header file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic +Package, Release 2b. + +Written by John R. Hauser. This work was made possible in part by the +International Computer Science Institute, located at Suite 600, 1947 Center +Street, Berkeley, California 94704. Funding was partially provided by the +National Science Foundation under grant MIP-9311980. The original version +of this code was written as part of a project to build a fixed-point vector +processor in collaboration with the University of California at Berkeley, +overseen by Profs. Nelson Morgan and John Wawrzynek. More information +is available through the Web page `http://www.cs.berkeley.edu/~jhauser/ +arithmetic/SoftFloat.html'. + +THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has +been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES +RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS +AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES, +COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE +EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE +INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR +OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE. + +Derivative works are acceptable, even for commercial purposes, so long as +(1) the source code for the derivative work includes prominent notice that +the work is derivative, and (2) the source code includes prominent notice with +these four paragraphs for those parts of this code that are retained. + +=============================================================================*/ + +#ifndef SOFTFLOAT_H +#define SOFTFLOAT_H + +#if defined(CONFIG_SOLARIS) && defined(CONFIG_NEEDS_LIBSUNMATH) +#include +#endif + +//#include +#include + +/*---------------------------------------------------------------------------- +| Each of the following `typedef's defines the most convenient type that holds +| integers of at least as many bits as specified. For example, `uint8' should +| be the most convenient type that can hold unsigned integers of as many as +| 8 bits. The `flag' type must be able to hold either a 0 or 1. For most +| implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed +| to the same as `int'. +*----------------------------------------------------------------------------*/ +typedef uint8_t flag; + +#ifdef __APPLE__ +#define uint16 qemu_uint16 +#endif + +typedef uint8_t uint8; +typedef int8_t int8; +#ifndef _AIX +typedef int uint16; +typedef int int16; +#endif +typedef unsigned int uint32; +typedef signed int int32; +typedef uint64_t uint64; +typedef int64_t int64; + +#define LIT64(a) a##LL +#define SINLINE static inline + +#define STATUS_PARAM , float_status *status +#define STATUS_VAR , status + +#if defined(CONFIG_SYMBEX) && !defined(SYMBEX_LLVM_LIB) +uint8_t RR_cpu_float_status(void *p, unsigned size); +void WR_cpu_float_status(void *p, unsigned size, int v); + +#define STATUS(field) RR_cpu_float_status(&status->field, sizeof(status->field)) +#define STATUS_W(field, v) WR_cpu_float_status(&status->field, sizeof(status->field), v) +#else +#define STATUS(field) status->field +#define STATUS_W(field, v) status->field = v +#endif + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE floating-point ordering relations +*----------------------------------------------------------------------------*/ +enum { float_relation_less = -1, float_relation_equal = 0, float_relation_greater = 1, float_relation_unordered = 2 }; + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE floating-point types. +*----------------------------------------------------------------------------*/ +/* Use structures for soft-float types. This prevents accidentally mixing + them with native int/float types. A sufficiently clever compiler and + sane ABI should be able to see though these structs. However + x86/gcc 3.x seems to struggle a bit, so leave them disabled by default. */ +//#define USE_SOFTFLOAT_STRUCT_TYPES +#ifdef USE_SOFTFLOAT_STRUCT_TYPES +typedef struct { uint16_t v; } float16; +#define float16_val(x) (((float16)(x)).v) +#define make_float16(x) \ + __extension__({ \ + float16 f16_val = {x}; \ + f16_val; \ + }) +#define const_float16(x) \ + { x } +typedef struct { uint32_t v; } float32; +/* The cast ensures an error if the wrong type is passed. */ +#define float32_val(x) (((float32)(x)).v) +#define make_float32(x) \ + __extension__({ \ + float32 f32_val = {x}; \ + f32_val; \ + }) +#define const_float32(x) \ + { x } +typedef struct { uint64_t v; } float64; +#define float64_val(x) (((float64)(x)).v) +#define make_float64(x) \ + __extension__({ \ + float64 f64_val = {x}; \ + f64_val; \ + }) +#define const_float64(x) \ + { x } +#else +typedef uint16_t float16; +typedef uint32_t float32; +typedef uint64_t float64; +#define float16_val(x) (x) +#define float32_val(x) (x) +#define float64_val(x) (x) +#define make_float16(x) (x) +#define make_float32(x) (x) +#define make_float64(x) (x) +#define const_float16(x) (x) +#define const_float32(x) (x) +#define const_float64(x) (x) +#endif +typedef struct { + uint64_t low; + uint16_t high; +} floatx80; +#define make_floatx80(exp, mant) ((floatx80){mant, exp}) +#define make_floatx80_init(exp, mant) \ + { .low = mant, .high = exp } +typedef struct { +#ifdef HOST_WORDS_BIGENDIAN + uint64_t high, low; +#else + uint64_t low, high; +#endif +} float128; +#define make_float128(high_, low_) ((float128){.high = high_, .low = low_}) +#define make_float128_init(high_, low_) \ + { .high = high_, .low = low_ } + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE floating-point underflow tininess-detection mode. +*----------------------------------------------------------------------------*/ +enum { float_tininess_after_rounding = 0, float_tininess_before_rounding = 1 }; + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE floating-point rounding mode. +*----------------------------------------------------------------------------*/ +enum { float_round_nearest_even = 0, float_round_down = 1, float_round_up = 2, float_round_to_zero = 3 }; + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE floating-point exception flags. +*----------------------------------------------------------------------------*/ +enum { + float_flag_invalid = 1, + float_flag_divbyzero = 4, + float_flag_overflow = 8, + float_flag_underflow = 16, + float_flag_inexact = 32, + float_flag_input_denormal = 64, + float_flag_output_denormal = 128 +}; + +typedef struct float_status { + signed char float_detect_tininess; + signed char float_rounding_mode; + signed char float_exception_flags; + signed char floatx80_rounding_precision; + /* should denormalised results go to zero and set the inexact flag? */ + flag flush_to_zero; + /* should denormalised inputs go to zero and set the input_denormal flag? */ + flag flush_inputs_to_zero; + flag default_nan_mode; +} float_status; + +void set_float_rounding_mode(int val STATUS_PARAM); +void set_float_exception_flags(int val STATUS_PARAM); +SINLINE void set_float_detect_tininess(int val STATUS_PARAM) { + STATUS_W(float_detect_tininess, val); +} +SINLINE void set_flush_to_zero(flag val STATUS_PARAM) { + STATUS_W(flush_to_zero, val); +} +SINLINE void set_flush_inputs_to_zero(flag val STATUS_PARAM) { + STATUS_W(flush_inputs_to_zero, val); +} +SINLINE void set_default_nan_mode(flag val STATUS_PARAM) { + STATUS_W(default_nan_mode, val); +} +SINLINE int get_float_exception_flags(float_status *status) { + return STATUS(float_exception_flags); +} +void set_floatx80_rounding_precision(int val STATUS_PARAM); + +/*---------------------------------------------------------------------------- +| Routine to raise any or all of the software IEC/IEEE floating-point +| exception flags. +*----------------------------------------------------------------------------*/ +void float_raise(uint8_t flags STATUS_PARAM); + +/*---------------------------------------------------------------------------- +| Options to indicate which negations to perform in float*_muladd() +| Using these differs from negating an input or output before calling +| the muladd function in that this means that a NaN doesn't have its +| sign bit inverted before it is propagated. +*----------------------------------------------------------------------------*/ +enum { + float_muladd_negate_c = 1, + float_muladd_negate_product = 2, + float_muladd_negate_result = 3, +}; + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE integer-to-floating-point conversion routines. +*----------------------------------------------------------------------------*/ +float32 int32_to_float32(int32 STATUS_PARAM); +float64 int32_to_float64(int32 STATUS_PARAM); +float32 uint32_to_float32(uint32 STATUS_PARAM); +float64 uint32_to_float64(uint32 STATUS_PARAM); +floatx80 int32_to_floatx80(int32 STATUS_PARAM); +float128 int32_to_float128(int32 STATUS_PARAM); +float32 int64_to_float32(int64 STATUS_PARAM); +float32 uint64_to_float32(uint64 STATUS_PARAM); +float64 int64_to_float64(int64 STATUS_PARAM); +float64 uint64_to_float64(uint64 STATUS_PARAM); +floatx80 int64_to_floatx80(int64 STATUS_PARAM); +float128 int64_to_float128(int64 STATUS_PARAM); + +/*---------------------------------------------------------------------------- +| Software half-precision conversion routines. +*----------------------------------------------------------------------------*/ +float16 float32_to_float16(float32, flag STATUS_PARAM); +float32 float16_to_float32(float16, flag STATUS_PARAM); + +/*---------------------------------------------------------------------------- +| Software half-precision operations. +*----------------------------------------------------------------------------*/ +int float16_is_quiet_nan(float16); +int float16_is_signaling_nan(float16); +float16 float16_maybe_silence_nan(float16); + +/*---------------------------------------------------------------------------- +| The pattern for a default generated half-precision NaN. +*----------------------------------------------------------------------------*/ +extern const float16 float16_default_nan; + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE single-precision conversion routines. +*----------------------------------------------------------------------------*/ +int16 float32_to_int16_round_to_zero(float32 STATUS_PARAM); +uint16 float32_to_uint16_round_to_zero(float32 STATUS_PARAM); +int32 float32_to_int32(float32 STATUS_PARAM); +int32 float32_to_int32_round_to_zero(float32 STATUS_PARAM); +uint32 float32_to_uint32(float32 STATUS_PARAM); +uint32 float32_to_uint32_round_to_zero(float32 STATUS_PARAM); +int64 float32_to_int64(float32 STATUS_PARAM); +int64 float32_to_int64_round_to_zero(float32 STATUS_PARAM); +float64 float32_to_float64(float32 STATUS_PARAM); +floatx80 float32_to_floatx80(float32 STATUS_PARAM); +float128 float32_to_float128(float32 STATUS_PARAM); + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE single-precision operations. +*----------------------------------------------------------------------------*/ +float32 float32_round_to_int(float32 STATUS_PARAM); +float32 float32_add(float32, float32 STATUS_PARAM); +float32 float32_sub(float32, float32 STATUS_PARAM); +float32 float32_mul(float32, float32 STATUS_PARAM); +float32 float32_div(float32, float32 STATUS_PARAM); +float32 float32_rem(float32, float32 STATUS_PARAM); +float32 float32_muladd(float32, float32, float32, int STATUS_PARAM); +float32 float32_sqrt(float32 STATUS_PARAM); +float32 float32_exp2(float32 STATUS_PARAM); +float32 float32_log2(float32 STATUS_PARAM); +int float32_eq(float32, float32 STATUS_PARAM); +int float32_le(float32, float32 STATUS_PARAM); +int float32_lt(float32, float32 STATUS_PARAM); +int float32_unordered(float32, float32 STATUS_PARAM); +int float32_eq_quiet(float32, float32 STATUS_PARAM); +int float32_le_quiet(float32, float32 STATUS_PARAM); +int float32_lt_quiet(float32, float32 STATUS_PARAM); +int float32_unordered_quiet(float32, float32 STATUS_PARAM); +int float32_compare(float32, float32 STATUS_PARAM); +int float32_compare_quiet(float32, float32 STATUS_PARAM); +float32 float32_min(float32, float32 STATUS_PARAM); +float32 float32_max(float32, float32 STATUS_PARAM); +int float32_is_quiet_nan(float32); +int float32_is_signaling_nan(float32); +float32 float32_maybe_silence_nan(float32); +float32 float32_scalbn(float32, int STATUS_PARAM); + +SINLINE float32 float32_abs(float32 a) { + /* Note that abs does *not* handle NaN specially, nor does + * it flush denormal inputs to zero. + */ + return make_float32(float32_val(a) & 0x7fffffff); +} + +SINLINE float32 float32_chs(float32 a) { + /* Note that chs does *not* handle NaN specially, nor does + * it flush denormal inputs to zero. + */ + return make_float32(float32_val(a) ^ 0x80000000); +} + +SINLINE int float32_is_infinity(float32 a) { + return (float32_val(a) & 0x7fffffff) == 0x7f800000; +} + +SINLINE int float32_is_neg(float32 a) { + return float32_val(a) >> 31; +} + +SINLINE int float32_is_zero(float32 a) { + return (float32_val(a) & 0x7fffffff) == 0; +} + +SINLINE int float32_is_any_nan(float32 a) { + return ((float32_val(a) & ~(1 << 31)) > 0x7f800000UL); +} + +SINLINE int float32_is_zero_or_denormal(float32 a) { + return (float32_val(a) & 0x7f800000) == 0; +} + +SINLINE float32 float32_set_sign(float32 a, int sign) { + return make_float32((float32_val(a) & 0x7fffffff) | (sign << 31)); +} + +#define float32_zero make_float32(0) +#define float32_one make_float32(0x3f800000) +#define float32_ln2 make_float32(0x3f317218) +#define float32_pi make_float32(0x40490fdb) +#define float32_half make_float32(0x3f000000) +#define float32_infinity make_float32(0x7f800000) + +/*---------------------------------------------------------------------------- +| The pattern for a default generated single-precision NaN. +*----------------------------------------------------------------------------*/ +extern const float32 float32_default_nan; + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE double-precision conversion routines. +*----------------------------------------------------------------------------*/ +int16 float64_to_int16_round_to_zero(float64 STATUS_PARAM); +uint16 float64_to_uint16_round_to_zero(float64 STATUS_PARAM); +int32 float64_to_int32(float64 STATUS_PARAM); +int32 float64_to_int32_round_to_zero(float64 STATUS_PARAM); +uint32 float64_to_uint32(float64 STATUS_PARAM); +uint32 float64_to_uint32_round_to_zero(float64 STATUS_PARAM); +int64 float64_to_int64(float64 STATUS_PARAM); +int64 float64_to_int64_round_to_zero(float64 STATUS_PARAM); +uint64 float64_to_uint64(float64 a STATUS_PARAM); +uint64 float64_to_uint64_round_to_zero(float64 a STATUS_PARAM); +float32 float64_to_float32(float64 STATUS_PARAM); +floatx80 float64_to_floatx80(float64 STATUS_PARAM); +float128 float64_to_float128(float64 STATUS_PARAM); + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE double-precision operations. +*----------------------------------------------------------------------------*/ +float64 float64_round_to_int(float64 STATUS_PARAM); +float64 float64_trunc_to_int(float64 STATUS_PARAM); +float64 float64_add(float64, float64 STATUS_PARAM); +float64 float64_sub(float64, float64 STATUS_PARAM); +float64 float64_mul(float64, float64 STATUS_PARAM); +float64 float64_div(float64, float64 STATUS_PARAM); +float64 float64_rem(float64, float64 STATUS_PARAM); +float64 float64_muladd(float64, float64, float64, int STATUS_PARAM); +float64 float64_sqrt(float64 STATUS_PARAM); +float64 float64_log2(float64 STATUS_PARAM); +int float64_eq(float64, float64 STATUS_PARAM); +int float64_le(float64, float64 STATUS_PARAM); +int float64_lt(float64, float64 STATUS_PARAM); +int float64_unordered(float64, float64 STATUS_PARAM); +int float64_eq_quiet(float64, float64 STATUS_PARAM); +int float64_le_quiet(float64, float64 STATUS_PARAM); +int float64_lt_quiet(float64, float64 STATUS_PARAM); +int float64_unordered_quiet(float64, float64 STATUS_PARAM); +int float64_compare(float64, float64 STATUS_PARAM); +int float64_compare_quiet(float64, float64 STATUS_PARAM); +float64 float64_min(float64, float64 STATUS_PARAM); +float64 float64_max(float64, float64 STATUS_PARAM); +int float64_is_quiet_nan(float64 a); +int float64_is_signaling_nan(float64); +float64 float64_maybe_silence_nan(float64); +float64 float64_scalbn(float64, int STATUS_PARAM); + +SINLINE float64 float64_abs(float64 a) { + /* Note that abs does *not* handle NaN specially, nor does + * it flush denormal inputs to zero. + */ + return make_float64(float64_val(a) & 0x7fffffffffffffffLL); +} + +SINLINE float64 float64_chs(float64 a) { + /* Note that chs does *not* handle NaN specially, nor does + * it flush denormal inputs to zero. + */ + return make_float64(float64_val(a) ^ 0x8000000000000000LL); +} + +SINLINE int float64_is_infinity(float64 a) { + return (float64_val(a) & 0x7fffffffffffffffLL) == 0x7ff0000000000000LL; +} + +SINLINE int float64_is_neg(float64 a) { + return float64_val(a) >> 63; +} + +SINLINE int float64_is_zero(float64 a) { + return (float64_val(a) & 0x7fffffffffffffffLL) == 0; +} + +SINLINE int float64_is_any_nan(float64 a) { + return ((float64_val(a) & ~(1ULL << 63)) > 0x7ff0000000000000ULL); +} + +SINLINE int float64_is_zero_or_denormal(float64 a) { + return (float64_val(a) & 0x7ff0000000000000LL) == 0; +} + +SINLINE float64 float64_set_sign(float64 a, int sign) { + return make_float64((float64_val(a) & 0x7fffffffffffffffULL) | ((int64_t) sign << 63)); +} + +#define float64_zero make_float64(0) +#define float64_one make_float64(0x3ff0000000000000LL) +#define float64_ln2 make_float64(0x3fe62e42fefa39efLL) +#define float64_pi make_float64(0x400921fb54442d18LL) +#define float64_half make_float64(0x3fe0000000000000LL) +#define float64_infinity make_float64(0x7ff0000000000000LL) + +/*---------------------------------------------------------------------------- +| The pattern for a default generated double-precision NaN. +*----------------------------------------------------------------------------*/ +extern const float64 float64_default_nan; + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE extended double-precision conversion routines. +*----------------------------------------------------------------------------*/ +int32 floatx80_to_int32(floatx80 STATUS_PARAM); +int32 floatx80_to_int32_round_to_zero(floatx80 STATUS_PARAM); +int64 floatx80_to_int64(floatx80 STATUS_PARAM); +int64 floatx80_to_int64_round_to_zero(floatx80 STATUS_PARAM); +float32 floatx80_to_float32(floatx80 STATUS_PARAM); +float64 floatx80_to_float64(floatx80 STATUS_PARAM); +float128 floatx80_to_float128(floatx80 STATUS_PARAM); + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE extended double-precision operations. +*----------------------------------------------------------------------------*/ +floatx80 floatx80_round_to_int(floatx80 STATUS_PARAM); +floatx80 floatx80_add(floatx80, floatx80 STATUS_PARAM); +floatx80 floatx80_sub(floatx80, floatx80 STATUS_PARAM); +floatx80 floatx80_mul(floatx80, floatx80 STATUS_PARAM); +floatx80 floatx80_div(floatx80, floatx80 STATUS_PARAM); +floatx80 floatx80_rem(floatx80, floatx80 STATUS_PARAM); +floatx80 floatx80_sqrt(floatx80 STATUS_PARAM); +int floatx80_eq(floatx80, floatx80 STATUS_PARAM); +int floatx80_le(floatx80, floatx80 STATUS_PARAM); +int floatx80_lt(floatx80, floatx80 STATUS_PARAM); +int floatx80_unordered(floatx80, floatx80 STATUS_PARAM); +int floatx80_eq_quiet(floatx80, floatx80 STATUS_PARAM); +int floatx80_le_quiet(floatx80, floatx80 STATUS_PARAM); +int floatx80_lt_quiet(floatx80, floatx80 STATUS_PARAM); +int floatx80_unordered_quiet(floatx80, floatx80 STATUS_PARAM); +int floatx80_compare(floatx80, floatx80 STATUS_PARAM); +int floatx80_compare_quiet(floatx80, floatx80 STATUS_PARAM); +int floatx80_is_quiet_nan(floatx80); +int floatx80_is_signaling_nan(floatx80); +floatx80 floatx80_maybe_silence_nan(floatx80); +floatx80 floatx80_scalbn(floatx80, int STATUS_PARAM); + +SINLINE floatx80 floatx80_abs(floatx80 a) { + a.high &= 0x7fff; + return a; +} + +SINLINE floatx80 floatx80_chs(floatx80 a) { + a.high ^= 0x8000; + return a; +} + +SINLINE int floatx80_is_infinity(floatx80 a) { + return (a.high & 0x7fff) == 0x7fff && a.low == 0x8000000000000000LL; +} + +SINLINE int floatx80_is_neg(floatx80 a) { + return a.high >> 15; +} + +SINLINE int floatx80_is_zero(floatx80 a) { + return (a.high & 0x7fff) == 0 && a.low == 0; +} + +SINLINE int floatx80_is_zero_or_denormal(floatx80 a) { + return (a.high & 0x7fff) == 0; +} + +SINLINE int floatx80_is_any_nan(floatx80 a) { + return ((a.high & 0x7fff) == 0x7fff) && (a.low << 1); +} + +#define floatx80_zero make_floatx80(0x0000, 0x0000000000000000LL) +#define floatx80_one make_floatx80(0x3fff, 0x8000000000000000LL) +#define floatx80_ln2 make_floatx80(0x3ffe, 0xb17217f7d1cf79acLL) +#define floatx80_pi make_floatx80(0x4000, 0xc90fdaa22168c235LL) +#define floatx80_half make_floatx80(0x3ffe, 0x8000000000000000LL) +#define floatx80_infinity make_floatx80(0x7fff, 0x8000000000000000LL) + +/*---------------------------------------------------------------------------- +| The pattern for a default generated extended double-precision NaN. +*----------------------------------------------------------------------------*/ +extern const floatx80 floatx80_default_nan; + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE quadruple-precision conversion routines. +*----------------------------------------------------------------------------*/ +int32 float128_to_int32(float128 STATUS_PARAM); +int32 float128_to_int32_round_to_zero(float128 STATUS_PARAM); +int64 float128_to_int64(float128 STATUS_PARAM); +int64 float128_to_int64_round_to_zero(float128 STATUS_PARAM); +float32 float128_to_float32(float128 STATUS_PARAM); +float64 float128_to_float64(float128 STATUS_PARAM); +floatx80 float128_to_floatx80(float128 STATUS_PARAM); + +/*---------------------------------------------------------------------------- +| Software IEC/IEEE quadruple-precision operations. +*----------------------------------------------------------------------------*/ +float128 float128_round_to_int(float128 STATUS_PARAM); +float128 float128_add(float128, float128 STATUS_PARAM); +float128 float128_sub(float128, float128 STATUS_PARAM); +float128 float128_mul(float128, float128 STATUS_PARAM); +float128 float128_div(float128, float128 STATUS_PARAM); +float128 float128_rem(float128, float128 STATUS_PARAM); +float128 float128_sqrt(float128 STATUS_PARAM); +int float128_eq(float128, float128 STATUS_PARAM); +int float128_le(float128, float128 STATUS_PARAM); +int float128_lt(float128, float128 STATUS_PARAM); +int float128_unordered(float128, float128 STATUS_PARAM); +int float128_eq_quiet(float128, float128 STATUS_PARAM); +int float128_le_quiet(float128, float128 STATUS_PARAM); +int float128_lt_quiet(float128, float128 STATUS_PARAM); +int float128_unordered_quiet(float128, float128 STATUS_PARAM); +int float128_compare(float128, float128 STATUS_PARAM); +int float128_compare_quiet(float128, float128 STATUS_PARAM); +int float128_is_quiet_nan(float128); +int float128_is_signaling_nan(float128); +float128 float128_maybe_silence_nan(float128); +float128 float128_scalbn(float128, int STATUS_PARAM); + +SINLINE float128 float128_abs(float128 a) { + a.high &= 0x7fffffffffffffffLL; + return a; +} + +SINLINE float128 float128_chs(float128 a) { + a.high ^= 0x8000000000000000LL; + return a; +} + +SINLINE int float128_is_infinity(float128 a) { + return (a.high & 0x7fffffffffffffffLL) == 0x7fff000000000000LL && a.low == 0; +} + +SINLINE int float128_is_neg(float128 a) { + return a.high >> 63; +} + +SINLINE int float128_is_zero(float128 a) { + return (a.high & 0x7fffffffffffffffLL) == 0 && a.low == 0; +} + +SINLINE int float128_is_zero_or_denormal(float128 a) { + return (a.high & 0x7fff000000000000LL) == 0; +} + +SINLINE int float128_is_any_nan(float128 a) { + return ((a.high >> 48) & 0x7fff) == 0x7fff && ((a.low != 0) || ((a.high & 0xffffffffffffLL) != 0)); +} + +/*---------------------------------------------------------------------------- +| The pattern for a default generated quadruple-precision NaN. +*----------------------------------------------------------------------------*/ +extern const float128 float128_default_nan; + +#endif /* !SOFTFLOAT_H */ diff --git a/src/base/emu-i386/simx86/syncpu.h b/src/base/emu-i386/simx86/syncpu.h new file mode 100644 index 0000000..f9fdbce --- /dev/null +++ b/src/base/emu-i386/simx86/syncpu.h @@ -0,0 +1,304 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originaly was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#ifndef _EMU86_SYNCPU_H +#define _EMU86_SYNCPU_H + +/***************************************************************************/ + +#include "host.h" +#include "protmode.h" +#include +#include + +#if ULONG_MAX > 0xffffffffUL +#define PADDING32BIT(n) +#else +#define PADDING32BIT(n) unsigned int padding##n; +#endif + +typedef struct { +/* offsets are 8-bit signed */ +#define FIELD0 unprotect_stub /* field of SynCPU at offset 00 */ +/* ------------------------------------------------ */ +/*80*/ long double *fpregs; +/*84*/ PADDING32BIT(1) +/*88*/ unsigned long long reserve; +/*90*/ SDTR gs_cache; +/*9c*/ SDTR fs_cache; +/*a8*/ SDTR es_cache; +/*b4*/ SDTR ds_cache; +/*c0*/ SDTR cs_cache; +/*cc*/ SDTR ss_cache; +/* ------------------------------------------------ */ +/*d8*/ void (*stub_stk_16)(void); +/*dc*/ PADDING32BIT(2) +/*e0*/ void (*stub_stk_32)(void); +/*e4*/ PADDING32BIT(3) +/*e8*/ void (*stub_wri_8)(void); +/*ec*/ PADDING32BIT(4) +/*f0*/ void (*stub_wri_16)(void); +/*f4*/ PADDING32BIT(5) +/*f8*/ void (*stub_wri_32)(void); +/*fc*/ PADDING32BIT(6) +/* ------------------------------------------------ */ +/*00*/ void (*unprotect_stub)(void); /* must be at 0 for call (%ebx) */ +/*04*/ PADDING32BIT(7) +/*08*/ unsigned int rzero; +/*0c*/ unsigned short gs, __gsh; +/*10*/ unsigned short fs, __fsh; +/*14*/ unsigned short es, __esh; +/*18*/ unsigned short ds, __dsh; +/*1c*/ unsigned int edi; +/*20*/ unsigned int esi; +/*24*/ unsigned int ebp; +/*28*/ unsigned int esp; +/*2c*/ unsigned int ebx; +/*30*/ unsigned int edx; +/*34*/ unsigned int ecx; +/*38*/ unsigned int eax; +/*3c*/ unsigned int trapno; +/*40*/ unsigned int scp_err; +/*44*/ unsigned int eip; +/*48*/ unsigned short cs, __csh; +/*4c*/ unsigned int eflags; +/* ----end of i386 sigcontext 1:1 correspondence--- */ +/*50*/ unsigned short ss, __ssh; +/*54*/ unsigned int cr2; +/* ------------------------------------------------ */ +/*58*/ unsigned short fpuc, fpus; +/*5c*/ unsigned short fpstt, fptag; +/* ------------------------------------------------ */ +/*60*/ unsigned short sigalrm_pending, sigprof_pending; +/*64*/ unsigned int StackMask; +/*68*/ unsigned int reserve2; +/*6c*/ unsigned int df_increments; /* either 0x040201 or 0xfcfeff */ + /* begin of cr array */ +/*70*/ unsigned int cr[5]; /* only cr[0] is used in compiled code */ +/* ------------------------------------------------ */ +/*80*/ //unsigned int end_mark[0] = cr[4] + unsigned int tr[2]; + + int err; + unsigned int mode; + unsigned int sreg1; + unsigned int dreg1; + unsigned int xreg1; + +/* + * DR0-3 = linear address of breakpoint 0-3 + * DR4=5 = reserved + * DR6 b0-b3 = BP active + * b13 = BD + * b14 = BS + * b15 = BT + * DR7 b0-b1 = G:L bp#0 + * b2-b3 = G:L bp#1 + * b4-b5 = G:L bp#2 + * b6-b7 = G:L bp#3 + * b8-b9 = GE:LE + * b13 = GD + * b16-19= LLRW bp#0 LL=00(1),01(2),11(4) + * b20-23= LLRW bp#1 RW=00(x),01(w),11(rw) + * b24-27= LLRW bp#2 + * b28-31= LLRW bp#3 + */ + unsigned int dr[8]; + unsigned int mem_ref; +/* CPU register: base(32) limit(16) */ + DTR GDTR; +/* CPU register: base(32) limit(16) */ + DTR IDTR; +/* CPU register: sel(16) base(32) limit(16) attr(8) */ + unsigned short LDT_SEL; + DTR LDTR; +/* CPU register: sel(16) base(32) limit(16) attr(8) */ + unsigned short TR_SEL; + DTR TR; + + void (*stub_read_8)(void); + void (*stub_read_16)(void); + void (*stub_read_32)(void); + + /* should be moved to TSS once implemented */ + struct revectored_struct int_revectored; + + /* if not NULL, points to emulated FPU state + if NULL, emulator uses FPU instructions, so flags that + dosemu needs to restore its own FPU environment. */ + emu_fpregset_t fpstate; +} SynCPU; + +union _SynCPU { + SynCPU s; + unsigned char b[sizeof(SynCPU)]; + unsigned short w[sizeof(SynCPU)/2]; + unsigned int d[sizeof(SynCPU)/4]; +}; + +extern union _SynCPU TheCPU_union; +#define TheCPU TheCPU_union.s + +#define SCBASE offsetof(SynCPU,FIELD0) +#define Ofs_END (int)(offsetof(SynCPU,cr[4])-SCBASE) + +#define SC(o) ((signed char)(o)) +#define CPUOFFS(o) (((unsigned char *)&(TheCPU.FIELD0))+SC(o)) + +#define CPUBYTE(o) TheCPU_union.b[SCBASE+SC(o)] +#define CPUWORD(o) TheCPU_union.w[(SCBASE+SC(o))/2] +#define CPULONG(o) TheCPU_union.d[(SCBASE+SC(o))/4] + +#define rEAX TheCPU.eax +#define Ofs_EAX (unsigned char)(offsetof(SynCPU,eax)-SCBASE) +#define rECX TheCPU.ecx +#define Ofs_ECX (unsigned char)(offsetof(SynCPU,ecx)-SCBASE) +#define rEDX TheCPU.edx +#define Ofs_EDX (unsigned char)(offsetof(SynCPU,edx)-SCBASE) +#define rEBX TheCPU.ebx +#define Ofs_EBX (unsigned char)(offsetof(SynCPU,ebx)-SCBASE) +#define rESP TheCPU.esp +#define Ofs_ESP (unsigned char)(offsetof(SynCPU,esp)-SCBASE) +#define rEBP TheCPU.ebp +#define Ofs_EBP (unsigned char)(offsetof(SynCPU,ebp)-SCBASE) +#define rESI TheCPU.esi +#define Ofs_ESI (unsigned char)(offsetof(SynCPU,esi)-SCBASE) +#define rEDI TheCPU.edi +#define Ofs_EDI (unsigned char)(offsetof(SynCPU,edi)-SCBASE) +#define Ofs_EIP (unsigned char)(offsetof(SynCPU,eip)-SCBASE) + +#define Ofs_CS (unsigned char)(offsetof(SynCPU,cs)-SCBASE) +#define Ofs_DS (unsigned char)(offsetof(SynCPU,ds)-SCBASE) +#define Ofs_ES (unsigned char)(offsetof(SynCPU,es)-SCBASE) +#define Ofs_SS (unsigned char)(offsetof(SynCPU,ss)-SCBASE) +#define Ofs_FS (unsigned char)(offsetof(SynCPU,fs)-SCBASE) +#define Ofs_GS (unsigned char)(offsetof(SynCPU,gs)-SCBASE) +#define Ofs_EFLAGS (unsigned char)(offsetof(SynCPU,eflags)-SCBASE) +#define Ofs_CR0 (unsigned char)(offsetof(SynCPU,cr[0])-SCBASE) +#define Ofs_CR2 (unsigned char)(offsetof(SynCPU,cr2)-SCBASE) +#define Ofs_STACKM (unsigned char)(offsetof(SynCPU,StackMask)-SCBASE) +//#define Ofs_ETIME (unsigned char)(offsetof(SynCPU,EMUtime)-SCBASE) +#define Ofs_RZERO (unsigned char)(offsetof(SynCPU,rzero)-SCBASE) +#define Ofs_SIGAPEND (unsigned char)(offsetof(SynCPU,sigalrm_pending)-SCBASE) +#define Ofs_SIGFPEND (unsigned char)(offsetof(SynCPU,sigprof_pending)-SCBASE) +#define Ofs_DF_INCREMENTS (unsigned char)(offsetof(SynCPU,df_increments)-SCBASE) + +#define Ofs_FPR (unsigned char)(offsetof(SynCPU,fpregs)-SCBASE) +#define Ofs_FPSTT (unsigned char)(offsetof(SynCPU,fpstt)-SCBASE) +#define Ofs_FPUS (unsigned char)(offsetof(SynCPU,fpus)-SCBASE) +#define Ofs_FPUC (unsigned char)(offsetof(SynCPU,fpuc)-SCBASE) +#define Ofs_FPTAG (unsigned char)(offsetof(SynCPU,fptag)-SCBASE) + +// 'Base' is 1st field of xs_cache +#define Ofs_XDS (unsigned char)(offsetof(SynCPU,ds_cache)-SCBASE) +#define Ofs_XSS (unsigned char)(offsetof(SynCPU,ss_cache)-SCBASE) +#define Ofs_XES (unsigned char)(offsetof(SynCPU,es_cache)-SCBASE) +#define Ofs_XCS (unsigned char)(offsetof(SynCPU,cs_cache)-SCBASE) +#define Ofs_XFS (unsigned char)(offsetof(SynCPU,fs_cache)-SCBASE) +#define Ofs_XGS (unsigned char)(offsetof(SynCPU,gs_cache)-SCBASE) + +#define Ofs_stub_wri_8 (unsigned char)(offsetof(SynCPU,stub_wri_8)-SCBASE) +#define Ofs_stub_wri_16 (unsigned char)(offsetof(SynCPU,stub_wri_16)-SCBASE) +#define Ofs_stub_wri_32 (unsigned char)(offsetof(SynCPU,stub_wri_32)-SCBASE) +#define Ofs_stub_stk_16 (unsigned char)(offsetof(SynCPU,stub_stk_16)-SCBASE) +#define Ofs_stub_stk_32 (unsigned char)(offsetof(SynCPU,stub_stk_32)-SCBASE) +#define Ofs_stub_read_8 (unsigned int)(offsetof(SynCPU,stub_read_8)-SCBASE) +#define Ofs_stub_read_16 (unsigned int)(offsetof(SynCPU,stub_read_16)-SCBASE) +#define Ofs_stub_read_32 (unsigned int)(offsetof(SynCPU,stub_read_32)-SCBASE) +#define Ofs_ERR (unsigned int)(offsetof(SynCPU,err)-SCBASE) +#define Ofs_int_revectored (unsigned int)(offsetof(SynCPU,int_revectored)-SCBASE) + +#define rAX CPUWORD(Ofs_AX) +#define Ofs_AX (Ofs_EAX) +#define Ofs_AXH (Ofs_EAX+2) +#define rAL CPUBYTE(Ofs_AL) +#define Ofs_AL (Ofs_EAX) +#define rAH CPUBYTE(Ofs_AH) +#define Ofs_AH (Ofs_EAX+1) +#define rCX CPUWORD(Ofs_CX) +#define Ofs_CX (Ofs_ECX) +#define rCL CPUBYTE(Ofs_CL) +#define Ofs_CL (Ofs_ECX) +#define rCH CPUBYTE(Ofs_CH) +#define Ofs_CH (Ofs_ECX+1) +#define rDX CPUWORD(Ofs_DX) +#define Ofs_DX (Ofs_EDX) +#define rDL CPUBYTE(Ofs_DL) +#define Ofs_DL (Ofs_EDX) +#define rDH CPUBYTE(Ofs_DH) +#define Ofs_DH (Ofs_EDX+1) +#define rBX CPUWORD(Ofs_BX) +#define Ofs_BX (Ofs_EBX) +#define rBL CPUBYTE(Ofs_BL) +#define Ofs_BL (Ofs_EBX) +#define rBH CPUBYTE(Ofs_BH) +#define Ofs_BH (Ofs_EBX+1) +#define rSP CPUWORD(Ofs_SP) +#define Ofs_SP (Ofs_ESP) +#define rBP CPUWORD(Ofs_BP) +#define Ofs_BP (Ofs_EBP) +#define rSI CPUWORD(Ofs_SI) +#define Ofs_SI (Ofs_ESI) +#define rDI CPUWORD(Ofs_DI) +#define Ofs_DI (Ofs_EDI) +#define Ofs_FLAGS (Ofs_EFLAGS) +#define Ofs_FLAGSL (Ofs_EFLAGS) + +#define REG1 TheCPU.sreg1 +#define REG3 TheCPU.dreg1 +#define SBASE TheCPU.xreg1 +#define SIGAPEND TheCPU.sigalrm_pending +#define SIGFPEND TheCPU.sigprof_pending +#define MEMREF TheCPU.mem_ref +#define EFLAGS TheCPU.eflags +#define FLAGS CPUWORD(Ofs_EFLAGS) +#define FPX TheCPU.fpstt + +#define CS_DTR TheCPU.cs_cache +#define DS_DTR TheCPU.ds_cache +#define ES_DTR TheCPU.es_cache +#define SS_DTR TheCPU.ss_cache +#define FS_DTR TheCPU.fs_cache +#define GS_DTR TheCPU.gs_cache + +#define LONG_CS TheCPU.cs_cache.BoundL +#define LONG_DS TheCPU.ds_cache.BoundL +#define LONG_ES TheCPU.es_cache.BoundL +#define LONG_SS TheCPU.ss_cache.BoundL +#define LONG_FS TheCPU.fs_cache.BoundL +#define LONG_GS TheCPU.gs_cache.BoundL + +extern char OVERR_DS, OVERR_SS; + +#endif diff --git a/src/base/emu-i386/simx86/tables.c b/src/base/emu-i386/simx86/tables.c new file mode 100644 index 0000000..02d2df0 --- /dev/null +++ b/src/base/emu-i386/simx86/tables.c @@ -0,0 +1,407 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + ***************************************************************************/ + +#include "emu86.h" +#include "codegen.h" + +///////////////////////////////////////////////////////////////////////////// + +unsigned char byrev[256] = { + 0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0, + 0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0, + 0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8, + 0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8, + 0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4, + 0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4, + 0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec, + 0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc, + 0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2, + 0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2, + 0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea, + 0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa, + 0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6, + 0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6, + 0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee, + 0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe, + 0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1, + 0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1, + 0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9, + 0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9, + 0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5, + 0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5, + 0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed, + 0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd, + 0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3, + 0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3, + 0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb, + 0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb, + 0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7, + 0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7, + 0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef, + 0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff +}; + +///////////////////////////////////////////////////////////////////////////// + +/* table of opcode properties + * + * bit 7 1=jmp instr + * bit 6 1=special subtable + * bit 5 1=op modifies flags + * bit 4 1=op can write into memory (excl.stack) + * bit 3 1=has mod/r_m + * bit 2 1=prefix + * bit 1 1=force DPMI_TRACE registers display(?) + * bit 0 0=can be compiled 1=always interpreted + */ + +unsigned char InterOps[256] = +{ + 0x38, // ADDbfrm 0x00 + 0x38, // ADDwfrm 0x01 + 0x28, // ADDbtrm 0x02 + 0x28, // ADDwtrm 0x03 + 0x28, // ADDbia 0x04 + 0x28, // ADDwia 0x05 + 0x00, // PUSHes 0x06 + 0x02, // POPes 0x07 + 0x38, // ORbfrm 0x08 + 0x38, // ORwfrm 0x09 + 0x28, // ORbtrm 0x0a + 0x28, // ORwtrm 0x0b + 0x28, // ORbi 0x0c + 0x28, // ORwi 0x0d + 0x00, // PUSHcs 0x0e + 0x72, // TwoByteESC 0x0f + 0x38, // ADCbfrm 0x10 + 0x38, // ADCwfrm 0x11 + 0x28, // ADCbtrm 0x12 + 0x28, // ADCwtrm 0x13 + 0x28, // ADCbi 0x14 + 0x28, // ADCwi 0x15 + 0x00, // PUSHss 0x16 + 0x02, // POPss 0x17 + 0x38, // SBBbfrm 0x18 + 0x38, // SBBwfrm 0x19 + 0x28, // SBBbtrm 0x1a + 0x28, // SBBwtrm 0x1b + 0x28, // SBBbi 0x1c + 0x28, // SBBwi 0x1d + 0x00, // PUSHds 0x1e + 0x02, // POPds 0x1f + 0x38, // ANDbfrm 0x20 + 0x38, // ANDwfrm 0x21 + 0x28, // ANDbtrm 0x22 + 0x28, // ANDwtrm 0x23 + 0x28, // ANDbi 0x24 + 0x28, // ANDwi 0x25 + 0x04, // SEGes 0x26 + 0x20, // DAA 0x27 + 0x38, // SUBbfrm 0x28 + 0x38, // SUBwfrm 0x29 + 0x28, // SUBbtrm 0x2a + 0x28, // SUBwtrm 0x2b + 0x28, // SUBbi 0x2c + 0x28, // SUBwi 0x2d + 0x04, // SEGcs 0x2e + 0x20, // DAS 0x2f + 0x38, // XORbfrm 0x30 + 0x38, // XORwfrm 0x31 + 0x28, // XORbtrm 0x32 + 0x28, // XORwtrm 0x33 + 0x28, // XORbi 0x34 + 0x28, // XORwi 0x35 + 0x04, // SEGss 0x36 + 0x20, // AAA 0x37 + 0x28, // CMPbfrm 0x38 + 0x28, // CMPwfrm 0x39 + 0x28, // CMPbtrm 0x3a + 0x28, // CMPwtrm 0x3b + 0x28, // CMPbi 0x3c + 0x28, // CMPwi 0x3d + 0x04, // SEGds 0x3e + 0x20, // AAS 0x3f + 0x20, // INCax 0x40 + 0x20, // INCcx 0x41 + 0x20, // INCdx 0x42 + 0x20, // INCbx 0x43 + 0x20, // INCsp 0x44 + 0x20, // INCbp 0x45 + 0x20, // INCsi 0x46 + 0x20, // INCdi 0x47 + 0x20, // DECax 0x48 + 0x20, // DECcx 0x49 + 0x20, // DECdx 0x4a + 0x20, // DECbx 0x4b + 0x20, // DECsp 0x4c + 0x20, // DECbp 0x4d + 0x20, // DECsi 0x4e + 0x20, // DECdi 0x4f + 0x00, // PUSHax 0x50 + 0x00, // PUSHcx 0x51 + 0x00, // PUSHdx 0x52 + 0x00, // PUSHbx 0x53 + 0x00, // PUSHsp 0x54 + 0x00, // PUSHbp 0x55 + 0x00, // PUSHsi 0x56 + 0x00, // PUSHdi 0x57 + 0x00, // POPax 0x58 + 0x00, // POPcx 0x59 + 0x00, // POPdx 0x5a + 0x00, // POPbx 0x5b + 0x00, // POPsp 0x5c + 0x00, // POPbp 0x5d + 0x00, // POPsi 0x5e + 0x00, // POPdi 0x5f + 0x00, // PUSHA 0x60 + 0x00, // POPA 0x61 + 0x81, // BOUND 0x62 + 0x81, // ARPL 0x63 + 0x04, // SEGfs 0x64 + 0x04, // SEGgs 0x65 + 0x04, // OPERoverride 0x66 + 0x04, // ADDRoverride 0x67 + 0x00, // PUSHwi 0x68 + 0x28, // IMULwrm 0x69 + 0x00, // PUSHbi 0x6a + 0x28, // IMULbrm 0x6b + 0x11, // INSb 0x6c + 0x11, // INSw 0x6d + 0x01, // OUTSb 0x6e + 0x01, // OUTSw 0x6f + 0x82, // JO 0x70 + 0x82, // JNO 0x71 + 0x82, // JB_JNAE 0x72 + 0x82, // JNB_JAE 0x73 + 0x82, // JE_JZ 0x74 + 0x82, // JNE_JNZ 0x75 + 0x82, // JBE_JNA 0x76 + 0x82, // JNBE_JA 0x77 + 0x82, // JS 0x78 + 0x82, // JNS 0x79 + 0x82, // JP_JPE 0x7a + 0x82, // JNP_JPO 0x7b + 0x82, // JL_JNGE 0x7c + 0x82, // JNL_JGE 0x7d + 0x82, // JLE_JNG 0x7e + 0x82, // JNLE_JG 0x7f + 0x70, // IMMEDbrm 0x80 + 0x70, // IMMEDwrm 0x81 + 0x70, // IMMEDbrm2 0x82 + 0x70, // IMMEDisrm 0x83 + 0x28, // TESTbrm 0x84 + 0x28, // TESTwrm 0x85 + 0x18, // XCHGbrm 0x86 + 0x18, // XCHGwrm 0x87 + 0x18, // MOVbfrm 0x88 + 0x18, // MOVwfrm 0x89 + 0x08, // MOVbtrm 0x8a + 0x08, // MOVwtrm 0x8b + 0x08, // MOVsrtrm 0x8c + 0x08, // LEA 0x8d + 0x1a, // MOVsrfrm 0x8e + 0x18, // POPrm 0x8f + 0x00, // NOP 0x90 + 0x00, // XCHGcx 0x91 + 0x00, // XCHGdx 0x92 + 0x00, // XCHGbx 0x93 + 0x00, // XCHGsp 0x94 + 0x00, // XCHGbp 0x95 + 0x00, // XCHGsi 0x96 + 0x00, // XCHGdi 0x97 + 0x00, // CBW 0x98 + 0x00, // CWD 0x99 + 0x00, // CALLl 0x9a + 0x00, // WAIT 0x9b + 0x00, // PUSHF 0x9c + 0x21, // POPF 0x9d + 0x20, // SAHF 0x9e + 0x00, // LAHF 0x9f + 0x00, // MOVmal 0xa0 + 0x00, // MOVmax 0xa1 + 0x10, // MOValm 0xa2 + 0x10, // MOVaxm 0xa3 + 0x10, // MOVSb 0xa4 + 0x10, // MOVSw 0xa5 + 0x20, // CMPSb 0xa6 + 0x20, // CMPSw 0xa7 + 0x20, // TESTbi 0xa8 + 0x20, // TESTwi 0xa9 + 0x10, // STOSb 0xaa + 0x10, // STOSw 0xab + 0x00, // LODSb 0xac + 0x00, // LODSw 0xad + 0x20, // SCASb 0xae + 0x20, // SCASw 0xaf + 0x00, // MOVial 0xb0 + 0x00, // MOVicl 0xb1 + 0x00, // MOVidl 0xb2 + 0x00, // MOVibl 0xb3 + 0x00, // MOViah 0xb4 + 0x00, // MOVich 0xb5 + 0x00, // MOVidh 0xb6 + 0x00, // MOVibh 0xb7 + 0x00, // MOViax 0xb8 + 0x00, // MOVicx 0xb9 + 0x00, // MOVidx 0xba + 0x00, // MOVibx 0xbb + 0x00, // MOVisp 0xbc + 0x00, // MOVibp 0xbd + 0x00, // MOVisi 0xbe + 0x00, // MOVidi 0xbf + 0x38, // SHIFTbi 0xc0 + 0x38, // SHIFTwi 0xc1 + 0x00, // RETisp 0xc2 + 0x00, // RET 0xc3 + 0x02, // LES 0xc4 + 0x02, // LDS 0xc5 + 0x18, // MOVbirm 0xc6 + 0x18, // MOVwirm 0xc7 + 0x00, // ENTER 0xc8 + 0x00, // LEAVE 0xc9 + 0x00, // RETlisp 0xca + 0x00, // RETl 0xcb + 0x01, // INT3 0xcc + 0x00, // INT 0xcd + 0x01, // INTO 0xce + 0x21, // IRET 0xcf + 0x38, // SHIFTb 0xd0 + 0x38, // SHIFTw 0xd1 + 0x38, // SHIFTbv 0xd2 + 0x38, // SHIFTwv 0xd3 + 0x20, // AAM 0xd4 + 0x20, // AAD 0xd5 + 0x81, // RESERVED1 0xd6 + 0x00, // XLAT 0xd7 + 0x72, // ESC0 0xd8 + 0x72, // ESC1 0xd9 + 0x72, // ESC2 0xda + 0x72, // ESC3 0xdb + 0x72, // ESC4 0xdc + 0x72, // ESC5 0xdd + 0x72, // ESC6 0xde + 0x72, // ESC7 0xdf + 0x82, // LOOPNZ_LOOPNE 0xe0 + 0x82, // LOOPZ_LOOPE 0xe1 + 0x82, // LOOP 0xe2 + 0x02, // JCXZ 0xe3 + 0x01, // INb 0xe4 + 0x01, // INw 0xe5 + 0x01, // OUTb 0xe6 + 0x01, // OUTw 0xe7 + 0x80, // CALLd 0xe8 + 0x82, // JMPd 0xe9 + 0x80, // JMPld 0xea + 0x82, // JMPsid 0xeb +#ifdef CPUEMU_DIRECT_IO + 0x00, // INvb 0xec + 0x00, // INvw 0xed + 0x00, // OUTvb 0xee + 0x00, // OUTvw 0xef +#else + 0x01, // INvb 0xec + 0x01, // INvw 0xed + 0x01, // OUTvb 0xee + 0x01, // OUTvw 0xef +#endif + 0x85, // LOCK 0xf0 + 0x81, // BARTS_OP 0xf1 + 0x04, // REPNE 0xf2 + 0x04, // REP 0xf3 + 0x01, // HLT 0xf4 + 0x20, // CMC 0xf5 + 0x60, // GRP1brm 0xf6 + 0x60, // GRP1wrm 0xf7 + 0x20, // CLC 0xf8 + 0x20, // STC 0xf9 + 0x20, // CLI 0xfa + 0x21, // STI 0xfb + 0x20, // CLD 0xfc + 0x20, // STD 0xfd + 0x70, // GRP2brm 0xfe + 0x72, // GRP2wrm 0xff +}; + + +///////////////////////////////////////////////////////////////////////////// + + +char RmIsReg[256] = +{ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // bit 1 = same reg + 3,1,1,1,1,1,1,1,1,3,1,1,1,1,1,1, // 11000000 11001001 + 1,1,3,1,1,1,1,1,1,1,1,3,1,1,1,1, // 11010010 11011011 + 1,1,1,1,3,1,1,1,1,1,1,1,1,3,1,1, // 11100100 11101101 + 1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,3 // 11110110 11111111 +}; + +char OpIsPush[256] = +{ + 0,0,0,0,0,0, 9,0,0,0,0,0,0,0,10,0, // 06 0e + 0,0,0,0,0,0,11,0,0,0,0,0,0,0,12,0, // 16 1e + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,2,3,4,5,6,7,8,0,0,0,0,0,0,0,0, // 50..57 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +// OpSize[mode & (DATA16|MBYTE)] == size of operand in bytes +char OpSize[8] = {4,4,2,2,1,1,1,1}; + +// OpSize[mode & (DATA16|MBYTE)] == bit (2-log) for size of operand in bytes +char OpSizeBit[8] = {2,2,1,1,0,0,0,0}; + +///////////////////////////////////////////////////////////////////////////// + diff --git a/src/base/emu-i386/simx86/trees.c b/src/base/emu-i386/simx86/trees.c new file mode 100644 index 0000000..022aa18 --- /dev/null +++ b/src/base/emu-i386/simx86/trees.c @@ -0,0 +1,1567 @@ +/*************************************************************************** + * + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + * + * + * SIMX86 a Intel 80x86 cpu emulator + * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center + * a.vignani@crf.it + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Additional copyright notes: + * + * 1. The kernel-level vm86 handling was taken out of the Linux kernel + * (linux/arch/i386/kernel/vm86.c). This code originally was written by + * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. + * + * 2. The tree handling routines were adapted from libavl: + * libavl - manipulates AVL trees. + * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * The author may be contacted at on the + * Internet, or as Ben Pfaff, 12167 Airport Rd, DeWitt MI 48820, USA + * through more mundane means. + * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "emu86.h" +#include "dlmalloc.h" +#include "codegen-arch.h" + +IMeta *InstrMeta; +int CurrIMeta = -1; + +/* Tree structure to store collected code sequences */ +avltr_tree CollectTree; +avltr_traverser Traverser; +int ninodes = 0; + +int NodesCleaned = 0; +int NodesParsed = 0; +int NodesExecd = 0; +int CleanFreq = 8; +int CreationIndex = 0; + +#if PROFILE +int MaxDepth = 0; +int MaxNodes = 0; +int MaxNodeSize = 0; +int TotalNodesParsed = 0; +int TotalNodesExecd = 0; +int NodesFound = 0; +int NodesFastFound = 0; +int NodesNotFound = 0; +int TreeCleanups = 0; +#endif + +#ifdef HOST_ARCH_X86 + +#define FINDTREE_CACHE_HASH_MASK 0xfff +static TNode *findtree_cache[FINDTREE_CACHE_HASH_MASK+1]; + +TNode *TNodePool; +int NodeLimit = 10000; + +#define RANGE_IN_RANGE(al,ah,l,h) ({int _l2=(al);\ + int _h2=(ah); ((_h2 >= (l)) && (_l2 < (h))); }) +#define ADDR_IN_RANGE(a,l,h) ({typeof(a) _a2=(a); \ + ((_a2 >= (l)) && (_a2 < (h))); }) + +///////////////////////////////////////////////////////////////////////////// + +#define NEXTNODE(g) ({__typeof__(g) _g = (g)->link[1]; \ + if ((g)->rtag == PLUS) \ + while (_g->link[0]!=NULL) _g=_g->link[0]; \ + _g; }) + +static inline TNode *Tmalloc(void) +{ + TNode *G = TNodePool->link[0]; + TNode *G1 = G->link[0]; + if (G1==TNodePool) leavedos_main(0x4c4c); // return NULL; + TNodePool->link[0] = G1; G->link[0]=NULL; + memset(G, 0, sizeof(TNode)); // "bug covering" + return G; +} + +static inline void Tfree(TNode *G) +{ + G->key = G->alive = 0; + G->addr = NULL; + G->link[0] = TNodePool->link[0]; + TNodePool->link[0] = G; +} + +///////////////////////////////////////////////////////////////////////////// + +static inline void datacopy(TNode *nd, TNode *ns) +{ + char *s = (char *)&(ns->key); + char *d = (char *)&(nd->key); + int l = sizeof(TNode)-offsetof(TNode,key); + memcpy(d,s,l); +} + +static TNode *avltr_probe (const int key, int *found) +{ + avltr_tree *tree = &CollectTree; + TNode *t; + TNode *s, *p, *q, *r; + int k = 1; + + t = &tree->root; + s = p = t->link[0]; + + if (s == NULL) { + tree->count++; + ninodes = tree->count; + q = t->link[0] = Tmalloc(); + q->link[0] = NULL; + q->link[1] = t; + q->rtag = MINUS; + q->bal = 0; + return q; + } + + for (;;) { + int diff = (key - p->key); + + if (diff < 0) { + p->cache = 0; + q = p->link[0]; + if (q == NULL) { + q = Tmalloc(); + p->link[0] = q; + q->link[0] = NULL; + q->link[1] = p; + q->rtag = MINUS; + break; + } + } + else if (diff > 0) { + p->cache = 1; + q = p->link[1]; + if (p->rtag == MINUS) { + q = Tmalloc(); + q->link[1] = p->link[1]; + q->rtag = p->rtag; + p->link[1] = q; + p->rtag = PLUS; + q->link[0] = NULL; + break; + } + } + else { /* found */ + *found = 1; + return p; + } + + if (q->bal != 0) t = p, s = q; + p = q; + k++; +/**/ if (k>=AVL_MAX_HEIGHT) leavedos_main(0x777); +#if PROFILE + if (debug_level('e')) if (k>MaxDepth) MaxDepth=k; +#endif + } + + tree->count++; + ninodes = tree->count; +#if PROFILE + if (debug_level('e')) if (ninodes > MaxNodes) MaxNodes = ninodes; +#endif + q->bal = 0; + + r = p = s->link[(int) s->cache]; + while (p != q) { + p->bal = p->cache * 2 - 1; + p = p->link[(int) p->cache]; + } + + if (s->cache == 0) { + if (s->bal == 0) { + s->bal = -1; + return q; + } + else if (s->bal == +1) { + s->bal = 0; + return q; + } + + if (r->bal == -1) { + p = r; + if (r->rtag == MINUS) { + s->link[0] = NULL; + r->link[1] = s; + r->rtag = PLUS; + } + else { + s->link[0] = r->link[1]; + r->link[1] = s; + } + s->bal = r->bal = 0; + } + else { + p = r->link[1]; + r->link[1] = p->link[0]; + p->link[0] = r; + s->link[0] = p->link[1]; + p->link[1] = s; + if (p->bal == -1) s->bal = 1, r->bal = 0; + else if (p->bal == 0) s->bal = r->bal = 0; + else s->bal = 0, r->bal = -1; + p->bal = 0; + p->rtag = PLUS; + if (s->link[0] == s) s->link[0] = NULL; + if (r->link[1] == NULL) { + r->link[1] = p; + r->rtag = MINUS; + } + } + } + else { + if (s->bal == 0) { + s->bal = 1; + return q; + } + else if (s->bal == -1) { + s->bal = 0; + return q; + } + + if (r->bal == +1) { + p = r; + if (r->link[0] == NULL) { + s->rtag = MINUS; + r->link[0] = s; + } + else { + s->link[1] = r->link[0]; + s->rtag = PLUS; + r->link[0] = s; + } + s->bal = r->bal = 0; + } + else { + p = r->link[0]; + r->link[0] = p->link[1]; + p->link[1] = r; + s->link[1] = p->link[0]; + p->link[0] = s; + if (p->bal == +1) s->bal = -1, r->bal = 0; + else if (p->bal == 0) s->bal = r->bal = 0; + else s->bal = 0, r->bal = 1; + p->rtag = PLUS; + if (s->link[1] == NULL) { + s->link[1] = p; + s->rtag = MINUS; + } + if (r->link[0] == r) r->link[0] = NULL; + p->bal = 0; + } + } + + if (t != &tree->root && s == t->link[1]) t->link[1] = p; + else t->link[0] = p; + + return q; +} + + +void avltr_delete (const int key) +{ + avltr_tree *tree = &CollectTree; + TNode *pa[AVL_MAX_HEIGHT]; /* Stack P: Nodes. */ + unsigned char a[AVL_MAX_HEIGHT]; /* Stack P: Bits. */ + int k = 1; /* Stack P: Pointer. */ + TNode *p; + + a[0] = 0; + pa[0] = &tree->root; + p = tree->root.link[0]; + if (p == NULL) return; + + for (;;) { + int diff = (key - p->key); + + if (diff==0) break; + pa[k] = p; + if (diff < 0) { + if (p->link[0] == NULL) return; + p = p->link[0]; a[k] = 0; + } + else if (diff > 0) { + if (p->rtag != PLUS) return; + p = p->link[1]; a[k] = 1; + } + k++; +/**/ if (k>=AVL_MAX_HEIGHT) leavedos_main(0x777); + } +#if !defined(SINGLESTEP)&&!defined(SINGLEBLOCK) + if (debug_level('e')>2) + e_printf("Found node to delete at %p(%08x)\n",p,p->key); +#endif + tree->count--; + ninodes = tree->count; + + { + TNode *t = p; + TNode **q = &pa[k - 1]->link[(int) a[k - 1]]; + + if (t->rtag == MINUS) { + if (t->link[0] != NULL) { + TNode *const x = t->link[0]; + + *q = x; + (*q)->bal = 0; + if (x->rtag == MINUS) { + if (a[k - 1] == 1) x->link[1] = t->link[1]; + else x->link[1] = pa[k - 1]; + } + } + else { + *q = t->link[a[k - 1]]; + if (a[k - 1] == 0) pa[k - 1]->link[0] = NULL; + else pa[k - 1]->rtag = MINUS; + } + } + else { + TNode *r = t->link[1]; + if (r->link[0] == NULL) { + r->link[0] = t->link[0]; + r->bal = t->bal; + if (r->link[0] != NULL) { + TNode *s = r->link[0]; + while (s->rtag == PLUS) s = s->link[1]; + s->link[1] = r; + } + *q = r; + a[k] = 1; + pa[k++] = r; + } + else { + TNode *s = r->link[0]; + + a[k] = 1; + pa[k++] = t; + + a[k] = 0; + pa[k++] = r; + + while (s->link[0] != NULL) { + r = s; + s = r->link[0]; + a[k] = 0; + pa[k++] = r; + } + + if (t->mblock) dlfree(t->mblock); +/* e_printf("<03 node exchange %p->%p>\n",s,t); */ + datacopy(t, s); +/**/ if (t->addr==NULL) leavedos_main(0x8130); + /* keep the node reference to itself */ + t->mblock->bkptr = t; + s->addr = NULL; + s->mblock = NULL; + memset(&s->clink, 0, sizeof(linkdesc)); + s->key = 0; + + if (s->rtag == PLUS) r->link[0] = s->link[1]; + else r->link[0] = NULL; + p = s; + } + } + } + +/**/ if (Traverser.p==p) Traverser.init=0; +#if !defined(SINGLESTEP)&&!defined(SINGLEBLOCK) + if (debug_level('e')>2) e_printf("Remove node %p\n",p); +#endif +#ifdef DEBUG_LINKER + if (p->clink.nrefs) { + dbug_printf("Cannot delete - nrefs=%d\n",p->clink.nrefs); + leavedos_main(0x9140); + } + if (p->clink.bkr.next) { + dbug_printf("Cannot delete - bkr busy\n"); + leavedos_main(0x9141); + } + if (p->clink.t_ref || p->clink.nt_ref) { + dbug_printf("Cannot delete - ref busy\n"); + leavedos_main(0x9142); + } +#endif + if (p->mblock) dlfree(p->mblock); + Tfree(p); + + while (--k) { + TNode *const s = pa[k]; + + if (a[k] == 0) { + TNode *const r = s->link[1]; + + if (s->bal == -1) { + s->bal = 0; + continue; + } + else if (s->bal == 0) { + s->bal = +1; + break; + } + + if (s->rtag == MINUS || r->bal == 0) { + s->link[1] = r->link[0]; + r->link[0] = s; + r->bal = -1; + pa[k - 1]->link[(int) a[k - 1]] = r; + break; + } + else if (r->bal == +1) { + if (r->link[0] != NULL) { + s->rtag = PLUS; + s->link[1] = r->link[0]; + } + else + s->rtag = MINUS; + r->link[0] = s; + s->bal = r->bal = 0; + pa[k - 1]->link[a[k - 1]] = r; + } + else { + p = r->link[0]; + if (p->rtag == PLUS) r->link[0] = p->link[1]; + else r->link[0] = NULL; + p->link[1] = r; + p->rtag = PLUS; + if (p->link[0] == NULL) { + s->link[1] = p; + s->rtag = MINUS; + } + else { + s->link[1] = p->link[0]; + s->rtag = PLUS; + } + p->link[0] = s; + if (p->bal == +1) s->bal = -1, r->bal = 0; + else if (p->bal == 0) s->bal = r->bal = 0; + else s->bal = 0, r->bal = +1; + p->bal = 0; + pa[k - 1]->link[(int) a[k - 1]] = p; + if (a[k - 1] == 1) pa[k - 1]->rtag = PLUS; + } + } + else { + TNode *const r = s->link[0]; + + if (s->bal == +1) { + s->bal = 0; + continue; + } + else if (s->bal == 0) { + s->bal = -1; + break; + } + + if (s->link[0] == NULL || r->bal == 0) { + s->link[0] = r->link[1]; + r->link[1] = s; + r->bal = +1; + pa[k - 1]->link[(int) a[k - 1]] = r; + break; + } + else if (r->bal == -1) { + if (r->rtag == PLUS) s->link[0] = r->link[1]; + else s->link[0] = NULL; + r->link[1] = s; + r->rtag = PLUS; + s->bal = r->bal = 0; + pa[k - 1]->link[a[k - 1]] = r; + } + else { + p = r->link[1]; + if (p->link[0] != NULL) { + r->rtag = PLUS; + r->link[1] = p->link[0]; + } + else + r->rtag = MINUS; + p->link[0] = r; + if (p->rtag == MINUS) s->link[0] = NULL; + else s->link[0] = p->link[1]; + p->link[1] = s; + p->rtag = PLUS; + if (p->bal == -1) s->bal = +1, r->bal = 0; + else if (p->bal == 0) s->bal = r->bal = 0; + else s->bal = 0, r->bal = -1; + p->bal = 0; + if (a[k - 1] == 1) + pa[k - 1]->rtag = PLUS; + pa[k - 1]->link[(int) a[k - 1]] = p; + } + } + } +} + +#endif // HOST_ARCH_X86 + +///////////////////////////////////////////////////////////////////////////// + +static void avltr_init(void) +{ +#ifdef HOST_ARCH_X86 + if (!config.cpusim) { + int i; + TNode *G; + + CollectTree.root.link[0] = NULL; + CollectTree.root.link[1] = &CollectTree.root; + CollectTree.root.rtag = PLUS; + CollectTree.count = 0; + Traverser.init = 0; + Traverser.p = NULL; + + G = TNodePool; + for (i=0; i<(NODES_IN_POOL-1); i++) { + TNode *G1 = G; G++; + G1->link[0] = G; + } + G->link[0] = TNodePool; + + InstrMeta = malloc(sizeof(IMeta) * MAXINODES); + memset(InstrMeta, 0, sizeof(IMeta)); + } +#endif + g_printf("avltr_init\n"); + CurrIMeta = -1; + NodesCleaned = 0; + ninodes = 0; +} + + +#ifdef HOST_ARCH_X86 + +void avltr_destroy(void) +{ + avltr_tree *tree; +#if PROFILE + hitimer_t t0 = 0; +#endif + + tree = &CollectTree; + e_printf("--------------------------------------------------------------\n"); + e_printf("Destroy AVLtree with %d nodes\n",ninodes); + e_printf("--------------------------------------------------------------\n"); +#ifdef DEBUG_TREE + DumpTree (tLog); +#endif +#if PROFILE + if (debug_level('e')) t0 = GETTSC(); +#endif + + mprot_end(); + if (tree->root.link[0] != &tree->root) { + TNode *an[AVL_MAX_HEIGHT]; /* Stack A: nodes. */ + char ab[AVL_MAX_HEIGHT]; /* Stack A: bits. */ + int ap = 0; /* Stack A: height. */ + TNode *p = tree->root.link[0]; + + for (;;) { + while (p != NULL) { + ab[ap] = 0; + an[ap++] = p; + p = p->link[0]; + } + + for (;;) { + backref *B; + if (ap == 0) goto quit; + + p = an[--ap]; + if (ab[ap] == 0) { + ab[ap++] = 1; + if (p->rtag == MINUS) continue; + p = p->link[1]; + break; + } + B = p->clink.bkr.next; + while (B) { + backref *B2 = B; + B = B->next; + free(B2); + } + if (p->mblock) dlfree(p->mblock); + } + } + } +quit: + free(InstrMeta); +#if PROFILE + if (debug_level('e')) { + TreeCleanups++; + CleanupTime += (GETTSC() - t0); + } +#endif +} + + +///////////////////////////////////////////////////////////////////////////// + +/* + * Given addr with translated code (from e.g., a fault) find the + * corresponding original PC. This is slow but it's only used for + * DPMI exceptions. + */ +unsigned int FindPC(unsigned char *addr) +{ + TNode *G = &CollectTree.root; + unsigned char *ahE; + Addr2Pc *AP; + unsigned int i; + + for (;;) { + /* walk to next node */ + G = NEXTNODE(G); + if (G == &CollectTree.root) break; + if (!G->addr || !G->pmeta || G->alive<=0) continue; + ahE = G->addr + G->len; + if (!ADDR_IN_RANGE(addr,G->addr,ahE)) continue; + e_printf("### FindPC: Found node %p->%p..%p", addr,G->addr,ahE); + AP = G->pmeta; + for (i=0; iseqnum; i++) { + e_printf(" %08x:%p",(G->key+AP->dnpc),G->addr+AP->daddr); + if (addr < G->addr+AP->daddr) break; + AP++; + } + e_printf("\nFindPC: PC=%x\n", G->key+(AP-1)->dnpc); + return G->key+(AP-1)->dnpc; + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////// + +#ifdef DEBUG_LINKER + +static void CheckLinks(void) +{ + TNode *G = &CollectTree.root; + TNode *GL; + unsigned char *p; + linkdesc *L, *T; + backref *B; + int n, brt; + + for (;;) { + /* walk to next node */ + G = NEXTNODE(G); + if (G == &CollectTree.root) { + e_printf("DEBUG: node link check ok\n"); + return; + } + if (G->key<=0) { + error("Invalid key %08x\n",G->key); + goto nquit; + } + if (G->alive <= 0) { + e_printf("Node %p invalidated\n",G); + continue; + } + if (debug_level('e')>5) e_printf("Node %p at %08x selfr=%p\n",G,G->key, + G->mblock->bkptr); + if (G->mblock->bkptr != G) { + error("bad selfref\n"); goto nquit; + } + L = &G->clink; + if (L->t_type >= JMP_LINK) { + if (L->t_ref) { + GL = *L->t_ref; + if (debug_level('e')>5) + e_printf(" T: ref=%p link=%p\n", + GL,L->t_link.abs); + p = ((unsigned char *)L->t_link.abs) - 1; + if ((*p!=0xe9)&&(*p!=0xeb)) { + error("bad t_link jmp\n"); goto nquit; + } + if (debug_level('e')>5) + e_printf(" T: links to %p at %08x with jmp %08x\n",GL,GL->key, + *L->t_link.abs); + T = &GL->clink; + B = T->bkr.next; + if ((B==NULL) || (T->nrefs < 1)) { + error("bad backref B=%p n=%d\n",B,T->nrefs); + goto nquit; + } + n = 0; + brt = 0; + while (B) { + if (B->ref==&G->mblock->bkptr) { + n++; + brt += B->branch; + if (debug_level('e')>5) e_printf(" T: backref %d from %p\n",n,GL); + } + B = B->next; + } + if (n < 1 || n > 2 || (n == 2 && brt != 'N' + 'T')) { + error("0 or >1 backrefs1 (%i)\n", n); goto nquit; + } + } + else { + p = ((unsigned char *)L->t_link.abs) - 1; + if (*p!=0xb8) { + error("bad t_link jmp\n"); goto nquit; + } + } + if (L->nt_ref) { + GL = *L->nt_ref; + if (debug_level('e')>5) + e_printf(" N: ref=%p link=%p\n", + GL,L->nt_link.abs); + p = ((unsigned char *)L->nt_link.abs) - 1; + if ((*p!=0xe9)&&(*p!=0xeb)) { + error("bad nt_link jmp\n"); goto nquit; + } + if (debug_level('e')>5) + e_printf(" N: links to %p at %08x with jmp %08x\n",GL,GL->key, + *L->nt_link.abs); + T = &GL->clink; + B = T->bkr.next; + if ((B==NULL) || (T->nrefs < 1)) { + error("bad backref B=%p n=%d\n",B,T->nrefs); + goto nquit; + } + n = 0; + brt = 0; + while (B) { + if (B->ref==&G->mblock->bkptr) { + n++; + brt += B->branch; + if (debug_level('e')>5) e_printf(" N: backref %d from %p\n",n,GL); + } + B = B->next; + } + if (n < 1 || n > 2 || (n == 2 && brt != 'N' + 'T')) { + error("0 or >1 backrefs2 (%i)\n", n); goto nquit; + } + } + else if (L->nt_link.abs) { + p = ((unsigned char *)L->nt_link.abs) - 1; + if (*p!=0xb8) { + error("bad nt_link jmp\n"); goto nquit; + } + } + } + } +nquit: + leavedos_main(0x9143); +} + +#endif // DEBUG_LINKER + +#ifdef DEBUG_TREE + +void DumpTree (FILE *fd) +{ + TNode *G = &CollectTree.root; + linkdesc *L; + backref *B; + int nn; + + if (fd==NULL) return; + fprintf(fd,"\n== BOT ========= %6d nodes =============================\n",ninodes); + nn = 0; + + while (nn < 10000) { // sorry,only 4 digits available + /* walk to next node */ + G = NEXTNODE(G); + if (G == &CollectTree.root) { + fprintf(fd,"\n== EOT ====================================================\n"); + fflush(fd); + return; + } + fprintf(fd,"\n-----------------------------------------------------------\n"); + if (G->alive <= 0) { + fprintf(fd,"%04d Node %p invalidated\n",nn,G); + nn++; + continue; + } + fprintf(fd,"%04d Node %p at %08x..%08x mblock=%p flags=%#x\n", + nn,G,G->key,(G->seqbase+G->seqlen-1),G->mblock,G->flags); + fprintf(fd," AVL (%p:%p),%d,%d,%d,%d\n",G->link[0],G->link[1], + G->bal,G->cache,G->pad,G->rtag); + fprintf(fd," source: instr=%d, len=%#x\n",G->seqnum,G->seqlen); + fprintf(fd," translated: len=%#x\n",G->len); + L = &G->clink; + fprintf(fd," LINK type=%d refs=%d\n",L->t_type,L->nrefs); + if (L->t_type >= JMP_LINK) { + fprintf(fd," T ref=%p patch=%08x at %p\n",L->t_ref, + L->t_undo,L->t_link.abs); + if (L->t_type>JMP_LINK) { + fprintf(fd," N ref=%p patch=%08x at %p\n",L->nt_ref, + L->nt_undo,L->nt_link.abs); + } + } + if (L->nrefs) { + B = L->bkr.next; + while (B) { + fprintf(fd," bkref %c -> %p\n",B->branch,B->ref); + B = B->next; + } + } + if (G->addr && G->pmeta) { + int i, j, k; + unsigned char *p = G->addr; + Addr2Pc *AP = G->pmeta; + for (i=0; iseqnum; i++) { + fprintf(fd," %08x:%p",(G->key+AP->dnpc),G->addr+AP->daddr); + k = 0; + for (j=0; j<(AP[1].daddr-AP->daddr); j++) { + fprintf(fd," %02x",*p++); k++; + if (k>=16) { + fprintf(fd,"\n "); k=0; + } + } + fprintf(fd,"\n"); + AP++; + } + fprintf(fd," :%p",G->addr+AP->daddr); + k = 0; + for (j=AP->daddr; jlen; j++) { + fprintf(fd," %02x",*p++); k++; + if (k>=16) { + fprintf(fd,"\n "); k=0; + } + } + fprintf(fd,"\n"); + } + fflush(fd); + nn++; + } +} + +#endif // DEBUG_TREE + +static int TraverseAndClean(void) +{ + int cnt = 0; + TNode *G; +#if PROFILE + hitimer_t t0 = 0; + + if (debug_level('e')) t0 = GETTSC(); +#endif + + if (Traverser.init == 0) { + Traverser.p = G = &CollectTree.root; + Traverser.init = 1; + } + else + G = Traverser.p; + + /* walk to next node */ + G = NEXTNODE(G); + if (G == &CollectTree.root) { + G = NEXTNODE(G); + if (G == &CollectTree.root) + return 0; + } + + if ((G->addr != NULL) && (G->alive>0)) { + G->alive -= AGENODE; + if (G->alive <= 0) { + if (debug_level('e')>2) e_printf("TraverseAndClean: node at %08x decayed\n",G->key); + e_unmarkpage(G->seqbase, G->seqlen); + NodeUnlinker(G); + } + } + if ((G->addr == NULL) || (G->alive<=0)) { + if (debug_level('e')>2) e_printf("Delete node %08x\n",G->key); + avltr_delete(G->key); + cnt++; + } + else { + if (debug_level('e')>3) + e_printf("TraverseAndClean: node at %08x of %d life=%d\n", + G->key,ninodes,G->alive); + Traverser.p = G; + } +#if PROFILE + if (debug_level('e')) CleanupTime += (GETTSC() - t0); +#endif + return cnt; +} + +/* + * Add a node to the collector tree. + * The code is linearly stored in the CodeBuf and its associated structures + * are in the InstrMeta array. We allocate a buffer and copy the code, then + * we copy the sequence data from the head element of InstrMeta. In this + * process we lose all the correspondences between original code and compiled + * code addresses. At the end, we reset both CodeBuf and InstrMeta to prepare + * for a new sequence. + */ +TNode *Move2Tree(IMeta *I0, CodeBuf *GenCodeBuf) +{ + TNode *nG = NULL; +#if PROFILE + hitimer_t t0 = 0; + if (debug_level('e')) t0 = GETTSC(); +#endif + int key; + int len, found, nap; + IMeta *I; + int i, apl=0; + Addr2Pc *ap; + CodeBuf *mallmb; + void **cp; + + /* try to keep a limit to the number of nodes in the tree. 3000-4000 + * nodes are probably enough before performance starts to suffer */ + if (ninodes > NodeLimit) { + for (i=0; inpc; + + found = 0; + nG = avltr_probe(key, &found); +/**/ if (nG==NULL) leavedos_main(0x8201); + + if (found) { + if (debug_level('e')>2) { + e_printf("Equal keys: replace node %p at %08x\n", + nG,key); + } + /* ->REPLACE the code of the node found with the latest + compiled version */ + NodeUnlinker(nG); + if (nG->mblock) dlfree(nG->mblock); + } + else { +#if !defined(SINGLESTEP)&&!defined(SINGLEBLOCK) + if (debug_level('e')>2) { + e_printf("New TNode %d at=%p key=%08x\n", + ninodes,nG,key); + if (debug_level('e')>3) + e_printf("Header: len=%d n_ops=%d PC=%08x\n", + I0->totlen, I0->ncount, I0->npc); + } +#endif + nG->key = key; + } + + /* transfer info from first node of the Meta list to our new node */ + nG->seqbase = I0->seqbase; + nG->seqlen = I0->seqlen; + nG->seqnum = I0->ncount; +#if PROFILE + if (debug_level('e')) if (nG->len > MaxNodeSize) MaxNodeSize = nG->len; +#endif + nG->len = len = I0->totlen; + nG->flags = I0->flags; + nG->alive = NODELIFE(nG); + findtree_cache[key&FINDTREE_CACHE_HASH_MASK] = nG; + + /* allocate the extra memory used by the node. This includes the + * translated code plus the table of correspondences between source + * and translated addresses. + * The first longword of the memory block is special; it stores a + * back-pointer to the node. This because nodes can be moved in + * memory when rebalancing the AVL tree, while we need absolute and + * constant memory references. + * The second longword is equal to its own address. Guess why. + * After that come the offset table, then the code. + */ + nap = nG->seqnum+1; + mallmb = GenCodeBuf; + nG->mblock = GenCodeBuf; + nG->mblock->bkptr = nG; + cp = &nG->mblock->selfptr; + *cp = cp; + nG->pmeta = mallmb->meta; + if (nG->pmeta==NULL) leavedos_main(0x504d45); + nG->addr = (unsigned char *)&mallmb->meta[nap]; + + /* setup structures for inter-node linking */ + nG->clink.t_type = I0->clink.t_type; + nG->clink.unlinked_jmp_targets = 0; + if (I0->clink.t_type >= JMP_LINK) { + nG->clink.t_link.abs = (unsigned int *)(nG->addr + I0->clink.t_link.rel); + nG->clink.t_target = *nG->clink.t_link.abs; + nG->clink.unlinked_jmp_targets |= TARGET_T; + } + else + nG->clink.t_link.abs = I0->clink.t_link.abs; + if (I0->clink.t_type > JMP_LINK) { + nG->clink.nt_link.abs = (unsigned int *)(nG->addr + I0->clink.nt_link.rel); + nG->clink.nt_target = *nG->clink.nt_link.abs; + nG->clink.unlinked_jmp_targets |= TARGET_NT; + } + else + nG->clink.nt_link.abs = I0->clink.nt_link.abs; + if ((debug_level('e')>3) && nG->clink.t_type) + dbug_printf("Link %d: %p:%08x\n",nG->clink.t_type, + nG->clink.nt_link.abs, + (nG->clink.t_type>JMP_LINK? *nG->clink.nt_link.abs:0)); + + /* setup source/xlated instruction offsets */ + ap = nG->pmeta; + I = I0; + for (i=0; iseqnum; i++) { + ap->daddr = I->daddr; + apl = ap->daddr + I->len; + ap->dnpc = I->npc - I0->npc; + if (debug_level('e')>8) + e_printf("Pmeta %03d: %p(%04x):%08x(%04x)\n",i, + nG->addr+ap->daddr,ap->daddr,I->npc,ap->dnpc); + ap++, I++; + } + ap->daddr = apl; + if (debug_level('e')>8) e_printf("Pmeta %03d: (%04x)\n",i,apl); + +#ifdef DEBUG_LINKER + CheckLinks(); +#endif + CurrIMeta = -1; + memset(&InstrMeta[0],0,sizeof(IMeta)); +#if PROFILE + if (debug_level('e')) AddTime += (GETTSC() - t0); +#endif + return nG; +} + + +TNode *FindTree(int key) +{ + TNode *I; + static int tccount=0; +#if PROFILE + hitimer_t t0 = 0; +#endif + + if (TheCPU.sigprof_pending) { + CollectStat(); + TheCPU.sigprof_pending = 0; + } + + /* fast path: using cache indexed by low 12 bits of PC: + ~99.99% success rate */ + I = findtree_cache[key&FINDTREE_CACHE_HASH_MASK]; + if (I && (I->alive>0) && (I->key==key)) { + if (debug_level('e')) { + if (debug_level('e')>4) + e_printf("Found key %08x via cache\n", key); +#if PROFILE + NodesFastFound++; +#endif + } + I->alive = NODELIFE(I); + return I; + } + if (!e_querymark(key, 1)) + return NULL; + +#if PROFILE + if (debug_level('e')) t0 = GETTSC(); +#endif + I = CollectTree.root.link[0]; + if (I == NULL) return NULL; /* always NULL the first time! */ + + for (;;) { + int diff = (key - I->key); + + if (diff < 0) { + I = I->link[0]; + if (I == NULL) goto endsrch; + } + else if (diff > 0) { + if (I->rtag == MINUS) goto endsrch; + I = I->link[1]; + } + else break; + } + + if (I && I->addr && (I->alive>0)) { + if (debug_level('e')>3) e_printf("Found key %08x\n",key); + I->alive = NODELIFE(I); + findtree_cache[key&FINDTREE_CACHE_HASH_MASK] = I; +#if PROFILE + if (debug_level('e')) { + NodesFound++; + SearchTime += (GETTSC() - t0); + } +#endif + return I; + } + +endsrch: +#if PROFILE + if (debug_level('e')) SearchTime += (GETTSC() - t0); +#endif + if ((ninodes>500) && (((++tccount) >= CleanFreq) || NodesCleaned)) { + while (NodesCleaned > 0) { + (void)TraverseAndClean(); + if (NodesCleaned) NodesCleaned--; + } + tccount=0; + } + + if (debug_level('e')) { + if (debug_level('e')>4) e_printf("Not found key %08x\n",key); +#if PROFILE + NodesNotFound++; +#endif + } + return NULL; +} + + +///////////////////////////////////////////////////////////////////////////// +/* + * We come here: + * a) from a fault on a protected memory page. A page is protected + * when code has been found on it. In this case, len is zero. + * b) from a disk read, no matter if the pages were protected or not. + * When we read something from disk we must mark as dirty anything + * present on the memory we are going to overwrite. In this case, + * len is greater than 0. + * Too bad the smallest memory unit is a 4k page; DOS programs used to + * be quite small. It is not unusual to find code and data/stack on the + * same 4k page. + * + */ + +static void BreakNode(TNode *G, unsigned char *eip) +{ + Addr2Pc *A = G->pmeta; + int ebase; + unsigned char *p; + int i; + + if (eip==0) { + dbug_printf("Cannot break node %08x, eip=%p\n",G->key,eip); + leavedos_main(0x7691); + } + + ebase = eip - G->addr; + for (i=0; iseqnum; i++) { + if (A->daddr >= ebase) { // found following instr + p = G->addr + A->daddr; // translated IP of following instr + memcpy(p, TailCode, TAILSIZE); + *((int *)(p+TAILFIX)) = G->key + A->dnpc; + if (debug_level('e')>1) + e_printf("============ Force node closing at %08x(%p)\n", + (G->key+A->dnpc),p); + return; + } + A++; + } + e_printf("============ Node %08x break failed\n",G->key); +} + +static TNode *DoDelNode(TNode *G) +{ + if (Traverser.p == G) + Traverser.p = NEXTNODE(G); + avltr_delete(G->key); + return CollectTree.root.link[0]; +} + +int InvalidateNodeRange(int al, int len, unsigned char *eip) +{ + TNode *G; + int ah; + int cleaned = 0; +#if PROFILE + hitimer_t t0 = 0; + + if (debug_level('e')) t0 = GETTSC(); +#endif + ah = al + len; + if (debug_level('e')>1) dbug_printf("Invalidate area %08x..%08x\n",al,ah); + + G = CollectTree.root.link[0]; + if (G == NULL) goto quit; + /* find nearest (lesser than) node */ + for (;;) { + if (G == NULL) goto quit; + if (G->key > al) { + /* no need to check for dead node here as the left-most + * node will not be overlapped by anything from left */ + if (G->link[0]==NULL) break; + G = G->link[0]; + } + else if (G->key < al) { + TNode *G2; + G2 = G->link[1]; + if (G2 == &CollectTree.root || G2->key > al) { + if (G->alive <= 0) { + /* remove dead node as it may be overlapped by good one */ + G = DoDelNode(G); + continue; + } + break; + } else G = G2; + } + else { + if (G->alive <= 0) { + G = DoDelNode(G); + continue; + } + break; + } + } + if (debug_level('e')>1) e_printf("Invalidate from node %08x on\n",G->key); + + /* walk tree in ascending, hopefully sorted, address order */ + for (;;) { + if (G == &CollectTree.root || G->key >= ah) + break; + if (G->addr && (G->alive>0)) { + int ahG = G->seqbase + G->seqlen; + if (RANGE_IN_RANGE(G->seqbase,ahG,al,ah)) { + unsigned char *ahE = G->addr + G->len; + if (debug_level('e')>1) + dbug_printf("Invalidated node %p at %08x\n",G,G->key); + G->alive = 0; + e_unmarkpage(G->seqbase, G->seqlen); + NodeUnlinker(G); + cleaned++; + NodesCleaned++; + /* if the current eip is in *any* chunk of code that is deleted + (not just the one written to) + then we need to break the node immediately to go back to + the interpreter; otherwise the remaining chunk (that does + not officially exist anymore) that the SIGSEGV or patched + call returns to may write to the current unprotected page. + */ + if (eip && ADDR_IN_RANGE(eip,G->addr,ahE)) { + if (debug_level('e')>1) + e_printf("### Node self hit %p->%p..%p\n", + eip,G->addr,ahE); + BreakNode(G, eip); + } + } + } + G = NEXTNODE(G); + } +quit: + if (debug_level('e') && e_querymark(al, len)) + error("simx86: InvalidateNodeRange did not clear all code for %#08x, len=%x\n", + al, len); +#if PROFILE + if (debug_level('e')) CleanupTime += (GETTSC() - t0); +#endif + return cleaned; +} + + +#endif // HOST_ARCH_X86 + + +///////////////////////////////////////////////////////////////////////////// +static void do_invalidate(unsigned data, int cnt) +{ + cnt = PAGE_ALIGN(data+cnt-1) - (data & _PAGE_MASK); + data &= _PAGE_MASK; +#ifdef HOST_ARCH_X86 + /* e_querymprotrange prevents coming here for sim */ + assert (!config.cpusim); + InvalidateNodeRange(data, cnt, 0); +#endif +} + +void e_invalidate(unsigned data, int cnt) +{ + if (!IS_EMU_JIT()) + return; + /* nothing to invalidate if there are no page protections */ + if (!e_querymprotrange(data, cnt)) + return; + /* for low mappings only invalidate if code, not if data */ + if (data < LOWMEM_SIZE + HMASIZE) { +#ifdef HOST_ARCH_X86 + /* e_querymprotrange prevents coming here for sim */ + assert (!config.cpusim); + if (e_querymark(data, cnt)) { + // no need to invalidate the whole page here, + // as the page does not need to be unprotected + InvalidateNodeRange(data, cnt, 0); + return; + } +#endif + return; + } + do_invalidate(data, cnt); +} + +void e_invalidate_pa(unsigned pa, int cnt) +{ + dosaddr_t addr = physaddr_to_dosaddr(pa, cnt); + if (addr == (dosaddr_t)-1) + return; + e_invalidate(addr, cnt); +} + +void e_invalidate_full_pa(unsigned pa, int cnt) +{ + dosaddr_t addr = physaddr_to_dosaddr(pa, cnt); + if (addr == (dosaddr_t)-1) + return; + e_invalidate_full(addr, cnt); +} + +/* invalidate and unprotect even if we hit only data. + * Needed if we are about to destroy the page protection by other means. + * Otherwise use e_invalidate() */ +void e_invalidate_full(unsigned data, int cnt) +{ + if (!IS_EMU_JIT()) + return; + /* nothing to invalidate if there are no page protections */ + if (!e_querymprotrange(data, cnt)) + return; + do_invalidate(data, cnt); +} + +int e_invalidate_page_full(unsigned data) +{ + int cnt = PAGE_SIZE; + if (!IS_EMU_JIT()) + return 0; + data &= _PAGE_MASK; + /* nothing to invalidate if there are no page protections */ + if (!e_querymprotrange(data, cnt)) + return 0; + do_invalidate(data, cnt); + return 1; +} + +///////////////////////////////////////////////////////////////////////////// + + +#ifdef HOST_ARCH_X86 + +static void CleanIMeta(void) +{ +#if PROFILE + hitimer_t t0 = 0; + + if (debug_level('e')) t0 = GETTSC(); +#endif + memset(InstrMeta,0,sizeof(IMeta)); +#if PROFILE + if (debug_level('e')) CleanupTime += (GETTSC() - t0); +#endif +} + +///////////////////////////////////////////////////////////////////////////// + + +int NewIMeta(int npc, int *rc) +{ +#if PROFILE + hitimer_t t0 = 0; + + if (debug_level('e')) t0 = GETTSC(); +#endif + if (CurrIMeta >= 0) { + // add new opcode metadata + IMeta *I,*I0; + + if (CurrIMeta>=MAXINODES) { + *rc = -1; goto quit; + } + I = &InstrMeta[CurrIMeta]; + if (CurrIMeta==0) { // no open code sequences + if (debug_level('e')>2) e_printf("============ Opening sequence at %08x\n",npc); + I0 = I; + } + else { + I0 = &InstrMeta[0]; + } + + I0->ncount += 1; + I->npc = npc; + + if (CurrIMeta>0) { + /* F_INHI (pop ss/mov ss) only applies to the last + instruction in the sequence and not twice in + a row */ + if ((I->flags & F_INHI) && !(I0->flags & F_INHI)) + I0->flags |= F_INHI; + else + I0->flags &= ~F_INHI; + I0->flags |= I->flags & ~F_INHI; + } + if (debug_level('e')>4) { + e_printf("Metadata %03d PC=%08x flags=%x(%x) ng=%d\n", + CurrIMeta,I->npc,I->flags,I0->flags,I->ngen); + } +#if PROFILE + if (debug_level('e')) AddTime += (GETTSC() - t0); +#endif + CurrIMeta++; + *rc = 1; I++; + I->ngen = 0; + I->flags = 0; + return CurrIMeta; + } + *rc = 0; +quit: +#if PROFILE + if (debug_level('e')) AddTime += (GETTSC() - t0); +#endif + return -1; +} + +#endif // HOST_ARCH_X86 + +///////////////////////////////////////////////////////////////////////////// + +#ifdef SHOW_STAT +#define CST_SIZE 4096 +static struct { + long long a; + int b,c,m,d,s; +} xCST[CST_SIZE]; +#else +#define CST_SIZE 4 +static int xCST[CST_SIZE]; +#endif + +static int cstx = 0; +static int xCS1 = 0; + +void CollectStat (void) +{ + int i, m = 0; +#ifdef SHOW_STAT + int csm = config.CPUSpeedInMhz*1000; +// xCST[cstx].a = TheCPU.EMUtime; + xCST[cstx].s = TheCPU.sigprof_pending; + xCST[cstx].b = ninodes; + xCST[cstx].c = NodesParsed; + xCST[cstx].d = NodesExecd; + for (i=cstx-3; i<=cstx; i++) { + if (i<0) { if (!xCS1) i=0; else i=CST_SIZE+i; } + m += xCST[i].c; + } + m >>= 2; + xCST[cstx].m = CLEAN_SPEED(FastLog2(m)); + i = cstx; + if (debug_level('e')>1) + e_printf("SIGPROF %04d %8d %8d(%3d) %8d %d\n",i, + xCST[i].b,xCST[i].c,xCST[i].m,xCST[i].d,xCST[i].s); + cstx++; + if (cstx==CST_SIZE) { + if (!xCS1) xCS1=1; + for (i=0; i1) + e_printf("SIGPROF %d n=%8d p=%8d x=%8d ix=%3d cln=%2d\n", + TheCPU.sigprof_pending, + ninodes,NodesParsed,NodesExecd,CreationIndex, + CleanFreq); +#endif + NodesParsed = NodesExecd = 0; +} + + +///////////////////////////////////////////////////////////////////////////// + +void InitTrees(void) +{ + g_printf("InitTrees\n"); +#ifdef HOST_ARCH_X86 + if (!config.cpusim) + TNodePool = calloc(NODES_IN_POOL, sizeof(TNode)); +#endif + + avltr_init(); + +#ifdef HOST_ARCH_X86 + if (!config.cpusim && debug_level('e')>1) { + e_printf("Root tree node at %p\n",&CollectTree.root); + e_printf("TNode pool at %p\n",TNodePool); + } +#endif + NodesParsed = NodesExecd = 0; + CleanFreq = 8; + cstx = xCS1 = 0; + CreationIndex = 0; +#if PROFILE + if (debug_level('e')) { + MaxDepth = MaxNodes = MaxNodeSize = 0; + TotalNodesParsed = TotalNodesExecd = 0; + NodesFound = NodesFastFound = NodesNotFound = 0; + TreeCleanups = 0; + } +#endif +} + +void EndGen(void) +{ +#ifdef SHOW_STAT + int i; + int csm = config.CPUSpeedInMhz*1000; +#endif +#ifdef HOST_ARCH_X86 + if (!config.cpusim) + CleanIMeta(); +#endif + CurrIMeta = -1; +#ifdef HOST_ARCH_X86 + if (!config.cpusim) { + avltr_destroy(); + free(TNodePool); TNodePool=NULL; + } +#endif +#ifdef SHOW_STAT + for (i=0; i on the + * Internet, or as Ben Pfaff, 12167 Airport Rd, DeWitt MI 48820, USA + * through more mundane means. + * + ***************************************************************************/ + +#ifndef _EMU86_TREES_H +#define _EMU86_TREES_H + +///////////////////////////////////////////////////////////////////////////// +// +// Tree node key definition. +// + +struct avltr_node; + +typedef struct _bkref { + struct _bkref *next; + struct avltr_node **ref; + char branch; +} backref; + +#define TARGET_T 1 +#define TARGET_NT 2 + +typedef struct _lnkdesc { + unsigned char t_type; + unsigned short nrefs; + union { + unsigned int *abs; + unsigned int rel; + } t_link; + union { + unsigned int *abs; + unsigned int rel; + } nt_link; + unsigned int t_target, nt_target; + unsigned unlinked_jmp_targets; + struct avltr_node **t_ref, **nt_ref; + backref bkr; +} linkdesc; + +typedef struct _imgen { + unsigned int op, mode, ovds; + unsigned int p0,p1,p2,p3,p4; + linkdesc *lt; +} IGen; + +typedef struct _ianpc { + unsigned short daddr __attribute__ ((packed)); + signed short dnpc __attribute__ ((packed)); +} Addr2Pc; + +typedef struct _imeta { + int seqbase, npc; + unsigned short ncount, len, flags, seqlen, totlen, daddr; + linkdesc clink; + int ngen; + IGen gen[NUMGENS]; +} IMeta; + +typedef struct _codebufhdr { + struct avltr_node *bkptr; + void *selfptr; + Addr2Pc meta[0]; /* there are nap of these */ + /* behind these follows the code */ +} CodeBuf; + +extern IMeta *InstrMeta; +extern int CurrIMeta; +extern int NodesExecd; +extern int TotalNodesExecd; +extern int NodesParsed; +extern int TotalNodesParsed; +extern int MaxNodes; +extern int MaxNodeSize; +extern int MaxDepth; +extern int NodesNotFound; +extern int NodesFastFound; +extern int EmuSignals; +extern int NodesFound; +extern int TreeCleanups; + +typedef struct avltr_node +{ +/* ----- Structure for a node in a right-threaded AVL tree. ----- */ + struct avltr_node *link[2]; /* Subtrees or threads. */ + signed char bal; /* Balance factor. */ + char cache; /* Used during insertion. */ + char pad; /* Reserved for fully threaded trees. */ + signed char rtag; /* Right thread tag. */ +/* -------------------------------------------------------------- */ + int key; /* signed! and don't move it from here! */ +/* -------------------------------------------------------------- */ + int alive; + CodeBuf *mblock; + unsigned char *addr; + Addr2Pc *pmeta; + unsigned short len, flags, seqlen, seqnum __attribute__ ((packed)); + int seqbase; + linkdesc clink; + unsigned cs; + unsigned mode; +} TNode; + +/* Used for traversing a right-threaded AVL tree. */ +typedef struct avltr_traverser +{ + int init; /* Initialized? */ + struct avltr_node *p; /* Last node returned. */ +} avltr_traverser; + +/* Structure which holds information about a threaded AVL tree. */ +typedef struct avltr_tree +{ + struct avltr_node root; /* Tree root node. */ + int count; /* Number of nodes in the tree. */ +} avltr_tree; + +/* Tag types. */ +#define PLUS +1 +#define MINUS -1 + +#ifdef HOST_ARCH_X86 +extern avltr_tree CollectTree; + +void avltr_delete (const int key); +// +TNode *FindTree(int key); +TNode *Move2Tree(IMeta *I0, CodeBuf *GenCodeBuf); +// +#endif + +void InitTrees(void); + +#ifdef HOST_ARCH_X86 +unsigned int FindPC(unsigned char *addr); +int InvalidateNodeRange(int addr, int len, unsigned char *eip); +#endif + +#endif diff --git a/src/base/init/Makefile b/src/base/init/Makefile new file mode 100644 index 0000000..3ad9b95 --- /dev/null +++ b/src/base/init/Makefile @@ -0,0 +1,55 @@ +top_builddir=../../.. +include $(top_builddir)/Makefile.conf + +#DEFINES=-DYYDEBUG=1 -DYYERROR_VERBOSE -DYYPURE -DTESTING + +# CFLAGS=-O2 -Wall -Wmissing-prototypes \ +# -Wstrict-prototypes -ansi -pedantic -Wid-clash-31 -Wcast-qual +#-Wwrite-strings + +CFILES = config.c dosemu_c.c global_c.c memcheck.c init.c dev_list.c +LFILES = lexer.l +YFILES = parser.y +HFILES = parsglob.h + +# Insert all source- and header-files here. + +ALL = $(YFILES) $(LFILES) $(CFILES) $(HFILES) + +# All object-files are included here. + +OBJS=parser.o lex.yy.o $(CFILES:.c=.o) + +all: lib + +dosemu_c.c : $(top_srcdir)/etc/dosemu.conf + echo 'char dosemu_conf[] = \' > $@ + sed -e 's/^# $$_/$$_/g' \ + -e 's/\\/\\\\/g' -e "s/'/\\\'/g" -e 's/\"/\\\"/g' \ + -e 's/^/"/g' -e 's/$$/\\n\"\\/g' $< >> $@ + echo ';' >> $@ + +global_c.c : $(top_srcdir)/etc/global.conf + echo 'char global_conf[] = \' > $@ + sed -e 's/^#$$_/$$_/g' \ + -e 's/\\/\\\\/g' -e "s/'/\\\'/g" -e 's/\"/\\\"/g' \ + -e 's/^/"/g' -e 's/$$/\\n\"\\/g' $< >> $@ + echo ';' >> $@ + +# pattern rules are not parallel +%.c %.h: %.y + $(YACC) -v -do $@ $< + +.PRECIOUS: parser.c + +lex.yy.c : lexer.l parser.y parser.h + $(LEX) -i $< + +clean:: + rm -f parser.[ch] parser.output lex.yy.c dosemu_c.c global_c.c + +include $(REALTOPDIR)/src/Makefile.common +vpath %.y $(srcdir) +vpath %.l $(srcdir) + +ALL_CPPFLAGS += -I$(srcdir) diff --git a/src/base/init/config.c b/src/base/init/config.c new file mode 100644 index 0000000..13a5fe9 --- /dev/null +++ b/src/base/init/config.c @@ -0,0 +1,1690 @@ +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBBSD +#include +#endif +#include +#include +#include +#include + +#include "version.h" +#include "emu.h" +#include "timers.h" +#include "video.h" +#include "vc.h" +#include "mouse.h" +#include "serial.h" +#include "keyboard/keymaps.h" +#include "memory.h" +#include "bios.h" +#include "lpt.h" +#include "int.h" +#include "dosemu_config.h" +#include "init.h" +#include "disks.h" +#include "pktdrvr.h" +#include "speaker.h" +#include "sound/sound.h" +#include "keyboard/keyb_clients.h" +#include "translate/dosemu_charset.h" +#include "dos2linux.h" +#include "utilities.h" +#ifdef X86_EMULATOR +#include "cpu-emu.h" +#endif +#include "mhpdbg.h" +#include "mapping.h" + +/* + * Options used in config_init(). + * + * Please keep "getopt_string", secure_option_preparse(), config_init(), + * usage() and the manpage in sync! + * + * No more undocumented switches *please*. + * + * "--Fusers", "--Flibdir", "--Fimagedir" and "-n" not in "getopt_string" + * they are eaten by secure_option_preparse(). + */ + + +int config_check_only = 0; + +int dosemu_argc; +char **dosemu_argv; +char *dosemu_proc_self_exe = NULL; +int dosemu_proc_self_maps_fd = -1; + +static void usage(char *basename); + +const char *config_script_name = DEFAULT_CONFIG_SCRIPT; +const char *dosemu_loglevel_file_path = "/etc/" DOSEMU_LOGLEVEL; +char *dosemu_rundir_path; +char *dosemu_localdir_path; + +char *dosemu_lib_dir_path; +const char *dosemu_exec_dir_path = DOSEMUEXEC_DEFAULT; +char *dosemu_plugin_dir_path; +char *commands_path; +char *dosemu_image_dir_path; +char *dosemu_drive_c_path; +char keymaploadbase_default[] = PREFIX "/share/"; +char *keymap_load_base_path = keymaploadbase_default; +const char *keymap_dir_path = "keymap/"; +const char *owner_tty_locks = "uucp"; +const char *tty_locks_dir_path = "/var/lock"; +const char *tty_locks_name_path = "LCK.."; +const char *dosemu_midi_path = "~/" LOCALDIR_BASE_NAME "/run/" DOSEMU_MIDI; +const char *dosemu_midi_in_path = "~/" LOCALDIR_BASE_NAME "/run/" DOSEMU_MIDI_IN; +char *dosemu_map_file_name; +char *fddir_default; +char *comcom_dir; +char *fddir_boot; +char *xbat_dir; +static char *dosemu_uid; +struct config_info config; + +#define STRING_STORE_SIZE 10 +struct cfg_string_store { + struct string_store base; + char *strings[STRING_STORE_SIZE]; +} cfg_store = { { .num = STRING_STORE_SIZE } }; + +/* + * DANG_BEGIN_FUNCTION cpu_override + * + * description: + * Process user CPU override from the config file ('cpu xxx') or + * from the command line. Returns the selected CPU identifier or + * -1 on error. + * + * DANG_END_FUNCTION + * + */ +int cpu_override (int cpu) +{ + switch (cpu) { + case 2: + return CPU_286; + case 5: case 6: + return CPU_586; + /* fall through */ + case 4: + return CPU_486; + /* fall through */ + case 3: + return CPU_386; + } + return -1; +} + +static void dump_printf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +static void log_dump_printf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vlog_printf(-1, fmt, args); + va_end(args); +} + +void dump_config_status(void (*printfunc)(const char *, ...)) +{ + const char *s; + void (*print)(const char *, ...) = (printfunc ? printfunc : dump_printf); + + if (!printfunc) { + (*print)("\n-------------------------------------------------------------\n"); + (*print)("------dumping the runtime configuration _after_ parsing -----\n"); + } + else (*print)("dumping the current runtime configuration:\n"); + (*print)("Version: dosemu-" VERSTR " versioncode = 0x%08x\n\n", DOSEMU_VERSION_CODE); + (*print)("Running Kernel Version: linux-%d.%d.%d\n", kernel_version_code >> 16, (kernel_version_code >> 8) & 255, kernel_version_code & 255); + (*print)("cpu "); + switch (vm86s.cpu_type) { + case CPU_386: s = "386"; break; + case CPU_486: s = "486"; break; + case CPU_586: s = "586"; break; + default: s = "386"; break; + } + (*print)("%s\nrealcpu ", s); + switch (config.realcpu) { + case CPU_386: s = "386"; break; + case CPU_486: s = "486"; break; + case CPU_586: s = "586"; break; + default: s = "386"; break; + } + (*print)("%s\n", s); + (*print)("CPUclock %g MHz\ncpu_spd 0x%lx\ncpu_tick_spd 0x%lx\n", + (((double)LLF_US)/config.cpu_spd), config.cpu_spd, config.cpu_tick_spd); + + (*print)("pci %d\nmathco %d\nsmp %d\n", + config.pci, config.mathco, config.smp); + (*print)("cpuspeed %d\n", config.CPUSpeedInMhz); + + if (config_check_only) mapping_init(); + (*print)("mappingdriver %s\n", config.mappingdriver ? config.mappingdriver : "auto"); + if (config_check_only) mapping_close(); + (*print)("hdiskboot %d\n", + config.hdiskboot); + (*print)("mem_size %d\next_mem %d\n", + config.mem_size, config.ext_mem); + (*print)("ems_size 0x%x\nems_frame 0x%x\n", + config.ems_size, config.ems_frame); + (*print)("umb_a0 %i\numb_b0 %i\numb_f0 %i\ndos_up %i\n", + config.umb_a0, config.umb_b0, config.umb_f0, config.dos_up); + (*print)("dpmi 0x%x\ndpmi_base 0x%x\npm_dos_api %i\nignore_djgpp_null_derefs %i\n", + config.dpmi, config.dpmi_base, config.pm_dos_api, config.no_null_checks); + (*print)("mapped_bios %d\nvbios_file %s\n", + config.mapped_bios, (config.vbios_file ? config.vbios_file :"")); + (*print)("vbios_copy %d\nvbios_seg 0x%x\nvbios_size 0x%x\n", + config.vbios_copy, config.vbios_seg, config.vbios_size); + (*print)("console_keyb %d\nconsole_video %d\n", + config.console_keyb, config.console_video); + (*print)("kbd_tty %d\nexitearly %d\n", + config.kbd_tty, config.exitearly); + (*print)("fdisks %d\nhdisks %d\n", + config.fdisks, config.hdisks); + (*print)("term_esc_char 0x%x\nterm_color %d\n", + config.term_esc_char, config.term_color); + (*print)("xterm_title\n", config.xterm_title); + (*print)("X_display \"%s\"\nX_title \"%s\"\nX_icon_name \"%s\"\n", + (config.X_display ? config.X_display :""), config.X_title, config.X_icon_name); + (*print)("X_title_show_appname %d\n", + config.X_title_show_appname); + (*print)("X_blinkrate %d\nX_sharecmap %d\nX_mitshm %d\n", + config.X_blinkrate, config.X_sharecmap, config.X_mitshm); + (*print)("X_fixed_aspect %d\nX_aspect_43 %d\nX_lin_filt %d\n", + config.X_fixed_aspect, config.X_aspect_43, config.X_lin_filt); + (*print)("X_bilin_filt %d\nX_mode13fact %d\nX_winsize_x %d\n", + config.X_bilin_filt, config.X_mode13fact, config.X_winsize_x); + (*print)("X_winsize_y %d\nX_gamma %d\nX_fullscreen %d\nvgaemu_memsize 0x%x\n", + config.X_winsize_y, config.X_gamma, config.X_fullscreen, + config.vgaemu_memsize); + (*print)("SDL_hwrend %d\nSDL_fonts \"%s\"\n", + config.sdl_hwrend, config.sdl_fonts); + (*print)("SDL_clip_native %d\n", + config.sdl_clip_native); + (*print)("vesamode_list %p\nX_lfb %d\nX_pm_interface %d\n", + config.vesamode_list, config.X_lfb, config.X_pm_interface); + (*print)("X_font \"%s\"\n", config.X_font); + (*print)("vga_fonts %i\n", config.vga_fonts); + (*print)("X_mgrab_key \"%s\"\n", config.X_mgrab_key); + (*print)("X_background_pause %d\n", config.X_background_pause); + (*print)("X_noclose %d\n", config.X_noclose); + + switch (config.chipset) { + case PLAINVGA: s = "plainvga"; break; +#ifdef USE_SVGALIB + case SVGALIB: s = "svgalib"; break; +#endif + case VESA: s = "vesa"; break; + default: s = "unknown"; break; + } + (*print)("config.X %d\nhogthreshold %d\nchipset \"%s\"\n", + config.X, config.hogthreshold, s); + switch (config.cardtype) { + case CARD_VGA: s = "VGA"; break; + case CARD_MDA: s = "MGA"; break; + case CARD_CGA: s = "CGA"; break; + case CARD_EGA: s = "EGA"; break; + default: s = "unknown"; break; + } + (*print)("cardtype \"%s\"\npci_video %d\nfullrestore %d\n", + s, config.pci_video, config.fullrestore); + (*print)("gfxmemsize %d\nvga %d\n", + config.gfxmemsize, config.vga); + switch (config.speaker) { + case SPKR_OFF: s = "off"; break; + case SPKR_NATIVE: s = "native"; break; + case SPKR_EMULATED: s = "emulated"; break; + default: s = "wrong"; break; + } + (*print)("dualmon %d\nforce_vt_switch %d\nspeaker \"%s\"\n", + config.dualmon, config.force_vt_switch, s); + (*print)("update %d\nfreq %d\n", + config.update, config.freq); + (*print)("tty_lockdir \"%s\"\ntty_lockfile \"%s\"\nconfig.tty_lockbinary %d\n", + config.tty_lockdir, config.tty_lockfile, config.tty_lockbinary); + (*print)("num_ser %d\nnum_lpt %d\nfastfloppy %d\nfile_lock_limit %d\n", + config.num_ser, config.num_lpt, config.fastfloppy, config.file_lock_limit); + (*print)("emusys \"%s\"\n", + (config.emusys ? config.emusys : "")); + (*print)("vbios_post %d\ndetach %d\n", + config.vbios_post, config.detach); + (*print)("debugout \"%s\"\n", + (config.debugout ? config.debugout : "")); + { + char buf[256]; + GetDebugFlagsHelper(buf, 0); + (*print)("debug_flags \"%s\"\n", buf); + } + if (!printfunc && config.keytable) { + dump_keytable(stderr, config.keytable); + } else { + (*print) ("keytable not setup yet\n"); + } + (*print)("pre_stroke \"%s\"\n", (config.pre_stroke ? config.pre_stroke : "")); + (*print)("irqpassing= "); + if (config.sillyint) { + int i; + for (i=0; i <16; i++) { + if (config.sillyint & (1<= *argc) return 0; + for (i=option; i < *argc; i++) { + argv[i] = argv[i+1]; + } + *argc -= 1; + return option; +} + +/* + * Please keep "getopt_string", secure_option_preparse(), config_init(), + * usage() and the manpage in sync! + */ +static char * get_option(const char *key, int with_arg, int *argc, + char **argv) +{ + char *p; + char *basename; + int o = find_option(key, *argc, argv); + if (!o) return 0; + o = option_delete(o, argc, argv); + if (!with_arg) return NULL; + if (!with_arg || o >= *argc) return NULL; + if (argv[o][0] == '-') { + basename = strrchr(argv[0], '/'); /* parse the program name */ + basename = basename ? basename + 1 : argv[0]; + usage(basename); + exit(0); + } + p = strdup(argv[o]); + option_delete(o, argc, argv); + return p; +} + +static char *path_expand(char *path) +{ + char buf[PATH_MAX]; + char *p, *pp; + + buf[0] = '\0'; + for (pp = path, p = strchr(path, '%'); p; pp = p + 2, p = strchr(pp, '%')) { + int len = strlen(buf); + if (p > pp) + snprintf(buf + len, sizeof(buf) - len, "%.*s", (int)(p - pp), pp); + switch (p[1]) { + case 'I': + strlcat(buf, DOSEMUIMAGE_DEFAULT, sizeof(buf)); + break; + default: + error("Unknown substitution %%%c\n", p[1]); + return NULL; + } + } + strlcat(buf, pp, sizeof(buf)); + return expand_path(buf); +} + +void secure_option_preparse(int *argc, char **argv) +{ + char *opt; + int cnt; + + do { + cnt = 0; + opt = get_option("--Flibdir", 1, argc, argv); + if (opt && opt[0]) { + char *opt1 = path_expand(opt); + if (opt1) { + free(dosemu_lib_dir_path); + dosemu_lib_dir_path = opt1; + cnt++; + } else { + error("--Flibdir: %s does not exist\n", opt); + config.exitearly = 1; + } + free(opt); + } + + opt = get_option("--Fexecdir", 1, argc, argv); + if (opt && opt[0]) { + char *opt1 = path_expand(opt); + if (opt1) { + replace_string(CFG_STORE, dosemu_exec_dir_path, opt1); + dosemu_exec_dir_path = opt1; + cnt++; + } else { + error("--Fexecdir: %s does not exist\n", opt); + config.exitearly = 1; + } + free(opt); + } + + opt = get_option("--Fplugindir", 1, argc, argv); + if (opt && opt[0]) { + char *opt1 = path_expand(opt); + if (opt1) { + free(dosemu_plugin_dir_path); + dosemu_plugin_dir_path = opt1; + cnt++; + } else { + error("--Fplugindir: %s does not exist\n", opt); + config.exitearly = 1; + } + free(opt); + } + + opt = get_option("--Fcmddir", 1, argc, argv); + if (opt && opt[0]) { + char *opt1 = path_expand(opt); + if (opt1) { + free(commands_path); + commands_path = opt1; + cnt++; + } else { + error("--Fcmddir: %s does not exist\n", opt); + config.exitearly = 1; + } + free(opt); + } + + opt = get_option("--Fimagedir", 1, argc, argv); + if (opt && opt[0]) { + char *opt1 = path_expand(opt); + if (opt1) { + free(dosemu_image_dir_path); + dosemu_image_dir_path = opt1; + cnt++; + } else { + error("--Fimagedir: %s does not exist\n", opt); + config.exitearly = 1; + } + free(opt); + } + + opt = get_option("--Fdrive_c", 1, argc, argv); + if (opt && opt[0]) { + char *opt1 = path_expand(opt); + if (opt1) { + free(dosemu_drive_c_path); + dosemu_drive_c_path = opt1; + config.alt_drv_c = 1; + cnt++; + } else { + error("--Fdrive_c: %s does not exist\n", opt); + config.exitearly = 1; + } + free(opt); + } + + opt = get_option("--Flocal_dir", 1, argc, argv); + if (opt && opt[0]) { + char *opt1 = path_expand(opt); + if (opt1) { + free(dosemu_localdir_path); + dosemu_localdir_path = opt1; + cnt++; + } else { + error("--Flocal_dir: %s does not exist\n", opt); + config.exitearly = 1; + } + free(opt); + } + + } while (cnt); +} + +static void read_cpu_info(void) +{ + char *cpuflags, *cpu; + int k = 3; + int err; + + err = open_proc_scan("/proc/cpuinfo"); + if (err) + return; + cpu = get_proc_string_by_key("cpu family"); + if (cpu) { + k = atoi(cpu); + } else { /* old kernels < 2.1.74 */ + cpu = get_proc_string_by_key("cpu"); + /* 386, 486, etc */ + if (cpu) k = atoi(cpu) / 100; + } + if (k > 5) k = 5; + + cpuflags = get_proc_string_by_key("features"); + if (!cpuflags) + cpuflags = get_proc_string_by_key("flags"); + if (cpuflags && strstr(cpuflags, "umip")) + config.umip = 1; + else + warn("Your CPU doesn't support UMIP\n"); + + switch (k) { + case 5: + config.realcpu = CPU_586; +#ifdef X86_EMULATOR + if (cpuflags && (strstr(cpuflags, "mmxext") || + strstr(cpuflags, "sse"))) { + config.cpuprefetcht0 = 1; + } +#endif +#ifdef __i386__ + if (cpuflags && (strstr(cpuflags, "fxsr"))) { + config.cpufxsr = 1; + if (cpuflags && strstr(cpuflags, "sse")) + config.cpusse = 1; + } +#endif + if (cpuflags && strstr(cpuflags, "tsc")) { + /* bogospeed currently returns 0; should it deny + * pentium features, fall back into 486 case */ + if ((cpuflags = get_proc_string_by_key("cpu MHz"))) { + int di,df; + /* last known proc/cpuinfo format is xxx.xxxxxx, with 3 + * int and 6 decimal digits - but what if there are less + * or more digits??? */ + /* some broken kernel/cpu combinations apparently imply + * the existence of 0 Mhz CPUs */ + if (sscanf(cpuflags,"%d.%d",&di,&df)==2 && (di || df)) { + char cdd[8]; int i; + long long chz = 0; + char *p = cpuflags; + while (*p!='.') p++; + p++; + for (i=0; i<6; i++) cdd[i]=(*p && isdigit(*p)? *p++:'0'); + cdd[6]=0; sscanf(cdd,"%d",&df); + /* speed division factor to get 1us from CPU clocks - for + * details on fast division see timers.h */ + chz = (di * 1000000LL) + df; + + /* speed division factor to get 1us from CPU clock */ + config.cpu_spd = (LLF_US*1000000)/chz; + + /* speed division factor to get 838ns from CPU clock */ + config.cpu_tick_spd = (LLF_TICKS*1000000)/chz; + + warn ("Linux kernel %d.%d.%d; CPU speed is %lld Hz\n", + kernel_version_code >> 16, (kernel_version_code >> 8) & 255, + kernel_version_code & 255,chz); +/* fprintf (stderr,"CPU speed factors %ld,%ld\n", + config.cpu_spd, config.cpu_tick_spd); */ + config.CPUSpeedInMhz = di + (df>500000); +#ifdef X86_EMULATOR + warn ("CPU-EMU speed is %d MHz\n",config.CPUSpeedInMhz); +#endif + break; + } + else + cpuflags=NULL; + } + else + cpuflags=0; + } + /* fall through */ + case 4: config.realcpu = CPU_486; + /* fall through */ + case 3: + break; + default: + error("Unknown CPU type!\n"); + /* config.realcpu is set to CPU_386 at this point */ + } + if (config.mathco) { + const char *s = get_proc_string_by_key("fpu"); + config.mathco = s && (strcmp(s, "yes") == 0); + } + reset_proc_bufferptr(); + k = 0; + while (get_proc_string_by_key("processor")) { + k++; + advance_proc_bufferptr(); + } + if (k > 1) { + config.smp = 1; /* for checking overrides, later */ + } + close_proc_scan(); +} + +static void config_post_process(void) +{ +#ifdef X86_EMULATOR + char buf[256]; + size_t n; + char *di; + FILE *f = popen("uname -r", "r"); + n = fread(buf, 1, sizeof(buf) - 1, f); + buf[n >= 0 ? n : 0] = 0; + if (strstr(buf, "Microsoft") != NULL) { + c_printf("CONF: Running on Windows, SIM CPUEMU enabled\n"); + config.cpusim = 1; + config.cpu_vm = CPUVM_EMU; + config.cpu_vm_dpmi = CPUVM_EMU; + } + pclose(f); +#endif + config.realcpu = CPU_386; + if (vm86s.cpu_type > config.realcpu || config.mathco) + read_cpu_info(); + if (vm86s.cpu_type > config.realcpu) { + vm86s.cpu_type = config.realcpu; + fprintf(stderr, "CONF: emulated CPU forced down to real CPU: %d86\n",(int)vm86s.cpu_type); + } + if (config.cpu_vm_dpmi == CPUVM_NATIVE) + error("@Security warning: native DPMI mode is insecure, " + "adjust $_cpu_vm_dpmi\n"); + c_printf("CONF: V86 cpu vm set to %d\n", config.cpu_vm); + c_printf("CONF: DPMI cpu vm set to %d\n", config.cpu_vm_dpmi); + + /* console scrub */ + if (!Video && (di = getenv("DISPLAY")) && *di && !config.X && !config.term && + config.cardtype != CARD_NONE) { + config.console_video = 0; + config.emuretrace = 0; /* already emulated */ +#ifdef SDL_SUPPORT + config.sdl = 1; + config.sdl_sound = 1; +#else +#ifdef X_SUPPORT + config.X = 1; +#endif +#endif + } +#ifdef USE_CONSOLE_PLUGIN + if (on_console()) { + c_printf("CONF: running on console, vga=%i cv=%i\n", config.vga, + config.console_video); + if (!can_do_root_stuff && config.console_video) { + /* force use of Slang-terminal on console too */ + config.console_video = 0; + dbug_printf("no console on low feature (non-suid root) DOSEMU\n"); + } + if (config.console_keyb == -1) + config.console_keyb = KEYB_RAW; + if (config.speaker == SPKR_EMULATED) { + register_speaker((void *)(uintptr_t)console_fd, + console_speaker_on, console_speaker_off); + } + } else +#endif + { + c_printf("CONF: not running on console\n"); + if (config.console_keyb == -1) { + config.console_keyb = +#ifdef USE_SLANG + /* Slang will take over KEYB_OTHER */ + KEYB_OTHER; + load_plugin("term"); +#else + KEYB_TTY; +#endif + } + config.console_video = 0; +#if 0 + if (config.speaker == SPKR_NATIVE) { + config.speaker = SPKR_EMULATED; + } +#endif + } + if (!config.console_video) + config.vga = config.mapped_bios = 0; + if (config.vga) + config.mapped_bios = config.console_video = 1; + else + config.vbios_post = 0; + if ((config.vga || config.X) && config.mem_size > 640) { + error("$_dosmem = (%d) not allowed for X and VGA console graphics, " + "restricting to 640K\n", config.mem_size); + config.mem_size = 640; + } + if (config.umb_a0 == -1) { + config.umb_a0 = config.term; +#if 0 + if (config.umb_a0) { + warn("work around FreeDOS UMB bug\n"); + config.umb_a0++; + } +#endif + } + if (config.umb_b0 == -1) + config.umb_b0 = config.dumb_video; + if (config.umb_b8) + config.umb_b8 = config.dumb_video; + + /* page-align memory sizes */ + config.ext_mem &= ~3; + config.xms_size &= ~3; + config.ems_size &= ~3; + config.dpmi &= ~3; + + /* UID scrub */ + if (under_root_login) c_printf("CONF: running exclusively as ROOT:"); + else { + c_printf("CONF: mostly running as USER:"); + } + c_printf(" uid=%d (cached %d) gid=%d (cached %d)\n", + geteuid(), get_cur_euid(), getegid(), get_cur_egid()); + c_printf("CONF: priv operations %s\n", + can_do_root_stuff ? "available" : "unavailable"); + + /* Speaker scrub */ +#ifdef X86_EMULATOR + if (IS_EMU() && config.speaker==SPKR_NATIVE) { + c_printf("SPEAKER: can`t use native mode with cpu-emu\n"); + config.speaker=SPKR_EMULATED; + } +#endif + + if (config.pci && !can_do_root_stuff) { + c_printf("CONF: Warning: PCI requires root, disabled\n"); + config.pci = 0; + } + + if (!config.internal_cset) { +#ifdef LOCALE_PLUGIN + /* json plugin loads locale settings */ + load_plugin("json"); +#else + set_internal_charset("cp437"); +#endif + } + +#ifdef USE_IEEE1284 + if (config.opl2lpt_device) + load_plugin("lpt"); +#endif +} + +static config_scrub_t config_scrub_func[100]; + +/* + * DANG_BEGIN_FUNCTION register_config_scrub + * + * description: + * register a function Enforce consistency upon the `config` structure after + * all values have been set to remove silly option combinations + * + * DANG_END_FUNCTION + * + */ +int register_config_scrub(config_scrub_t new_config_scrub) +{ + int i; + int result = -1; + for(i = 0; i < sizeof(config_scrub_func)/sizeof(config_scrub_func[0]); i++) { + if (!config_scrub_func[i]) { + config_scrub_func[i] = new_config_scrub; + result = 0; + break; + } + } + if (result < 0) { + c_printf("register_config_scrub failed > %zu config_scrub functions\n", + sizeof(config_scrub_func)/sizeof(config_scrub_func[0])); + } + return result; +} + +/* + * DANG_BEGIN_FUNCTION unregister_config_scrub + * + * description: + * Complement of register_config_scrub + * This removes a scrub function. + * + * DANG_END_FUNCTION + * + */ +void unregister_config_scrub( config_scrub_t old_config_scrub) +{ + int i; + for(i = 0; i < sizeof(config_scrub_func)/sizeof(config_scrub_func[0]); i++) { + if (config_scrub_func[i] == old_config_scrub) { + config_scrub_func[i] = 0; + } + } +} + +/* + * DANG_BEGIN_FUNCTION config_scrub + * + * description: + * Enforce consistency upon the `config` structure after + * all values have been set to remove silly option combinations + * + * DANG_END_FUNCTION + * + */ +static void config_scrub(void) +{ + int i; + for(i = 0; i < sizeof(config_scrub_func)/sizeof(config_scrub_func[0]); i++) { + config_scrub_t func = config_scrub_func[i]; + if (func) { + func(); + } + } +} + +/* + * DANG_BEGIN_FUNCTION config_init + * + * description: + * This is called to parse the command-line arguments and config + * files. + * + * DANG_END_FUNCTION + * + */ +void +config_init(int argc, char **argv) +{ + int c=0; +#define I_MAX 5 + int i_incr[I_MAX] = {}; + int i_found = 0; + int i_cur; + int can_do_root_stuff_enabled = 0; + char *confname = NULL; + char *dosrcname = NULL; + int nodosrc = 0; + char *basename; + int err; + int was_exec = 0, was_T1 = 0; + const char * const getopt_string = + "23456ABC::c::D:d:E:e:f:H:hi:I:K:k::L:M:mNno:P:qSsT::t::VvwXx:Y" + "gp"/*NOPs kept for compat (not documented in usage())*/; + + basename = strrchr(argv[0], '/'); /* parse the program name */ + basename = basename ? basename + 1 : argv[0]; + + dosemu_argc = argc; + dosemu_argv = argv; + if (dosemu_proc_self_exe == NULL) + dosemu_proc_self_exe = dosemu_argv[0]; + + memcheck_type_init(); + our_envs_init(); + parse_debugflags("+cw", 1); +#ifdef USE_DJDEV64 + register_debug_class('J', NULL, "dj64"); +#endif + Video = NULL; + + /* options get parsed twice so show our own errors and only once */ + opterr = 0; + while ((c = getopt(argc, argv, getopt_string)) != EOF) { + switch (c) { + case 's': + if (can_do_root_stuff) + can_do_root_stuff_enabled = 1; + else + error("The -s switch requires root privileges\n"); + break; + case 'h': + usage(basename); + exit(0); + break; + case 'f': + dosrcname = path_expand(optarg); + if (!dosrcname) { + error("%s is missing\n", optarg); + leavedos(2); + } + break; + case 'I': + assert(i_found < I_MAX); + commandline_statements = strdup(optarg); + if (commandline_statements[0] == '\'') { + commandline_statements[0] = ' '; + while (commandline_statements[strlen(commandline_statements) - 1] != '\'') { + commandline_statements = realloc(commandline_statements, + strlen(commandline_statements) + 1 + + strlen(argv[optind]) + 1); + strcat(commandline_statements, " "); + strcat(commandline_statements, argv[optind]); + optind++; + i_incr[i_found]++; + } + commandline_statements[strlen(commandline_statements) - 1] = 0; + } + i_found++; + break; + case 'c': + config.console_video = 1; + config.vga = 0; // why? + if (optarg && optarg[0] == 'd') + config.detach = 1; + break; + case 'o': { + char *tmp = strdup(optarg); + char *p = strstr(tmp, ".%"); + + free(config.debugout); + config.debugout = NULL; + if (!p) { + config.debugout = tmp; + } else { + *p = '\0'; + switch (p[2]) { + case 'P': + asprintf(&config.debugout, "%s.%i", tmp, getpid()); + break; + default: + error("Unknown suffix %s\n", p + 1); + break; + } + free(tmp); + } + if (strcmp(optarg, "-") == 0) + dbg_fd = stderr; + break; + } + case 'n': + nodosrc = 1; + break; + case 'q': + config.quiet = 1; + break; + case 'v': + printf("dosemu2-" VERSTR "\n"); + printf("Revision: %i\n", REVISION); + exit(0); + } + } + + if (!can_do_root_stuff_enabled) + can_do_root_stuff = 0; + else if (under_root_login) + fprintf(stderr,"\nRunning as root in full feature mode\n"); + else + fprintf(stderr,"\nRunning privileged (%s) in full feature mode\n", + using_sudo ? "via sudo" : "suid-root"); + + stdio_init(); /* initialize stdio & open debug file */ + if (config_check_only) set_debug_level('c',1); + + move_dosemu_lib_dir(); + if (nodosrc && dosrcname) { + c_printf("CONF: using %s as primary config\n", dosrcname); + confname = dosrcname; + dosrcname = NULL; + } else { + confname = assemble_path(DOSEMU_CONF_DIR, DOSEMU_CONF); + if (access(confname, R_OK) == -1) { + free(confname); + confname = NULL; + } + } + parse_config(confname, dosrcname, nodosrc); + free(confname); + free(dosrcname); + + if (config.exitearly && !config_check_only) + exit(0); + + i_cur = 0; + GETOPT_RESET(); + while ((c = getopt(argc, argv, getopt_string)) != EOF) { + switch (c) { + case 'f': + case 'c': + case 'o': + case 'n': + case 'q': + case 's': + break; + case 'L': + dbug_printf("%s\n", optarg); + break; + case 'd': { + char *p = strdup(optarg); + char *d = strchr(p, ':'); + int ro = 0; + int cd = 0; + int grp = 0; + if (d) { + switch (d[1]) { + case 'R': + case 'r': + ro++; + break; + case 'C': + case 'c': + cd++; + break; + case 'G': + case 'g': + grp++; + break; + } + *d = '\0'; + } + err = add_extra_drive(p, ro, cd, grp); + free(p); + if (err) + config.exitearly = 1; + break; + } + case 'H': { +#ifdef USE_MHPDBG + dosdebug_flags = strtoul(optarg,0,0) & 255; +#else + error("debugger support not compiled in\n"); +#endif + break; + } + case 'i': + append_pre_strokes(optarg); + break; + case 'I': + assert(i_cur < i_found); + optind += i_incr[i_cur++]; + break; + case '2': case '3': case '4': case '5': case '6': + { + int cpu = cpu_override (c-'0'); + if (cpu > 0) { + fprintf(stderr,"CPU set to %d86\n",cpu); + vm86s.cpu_type = cpu; + } + else + fprintf(stderr,"error in CPU user override\n"); + } + break; + case 'g': /* obsolete "graphics" option */ + break; + case 'A': + config.hdiskboot = 0; + break; + case 'B': + config.hdiskboot = 1; + break; + case 'C': + config.hdiskboot = 2; + if (optarg && isdigit(optarg[0]) && optarg[0] > '0') { + config.hdiskboot += optarg[0] - '0'; + config.swap_bootdrv = 1; + } + break; + case 'k': + if (optarg) { + switch (optarg[0]) { + case 's': + config.console_keyb = KEYB_STDIO; + break; + case 't': + config.console_keyb = +#ifdef USE_SLANG + /* Slang will take over KEYB_OTHER */ + KEYB_OTHER; + load_plugin("term"); +#else + KEYB_TTY; +#endif + break; + case 'r': + config.console_keyb = KEYB_RAW; + break; + case 'o': + config.console_keyb = KEYB_OTHER; + break; + } + } else { + config.console_keyb = KEYB_RAW; + } + break; + case 't': + if (!optarg || optarg[0] != ':') { + /* terminal mode */ + config.X = config.console_video = 0; + config.term = 1; + } + if (optarg) { + char *opt_e; + if (strchr(optarg, 'c')) + config.clip_term = 1; + if (strchr(optarg, 'd')) + config.dumb_video = 1; + if ((opt_e = strchr(optarg, 'e'))) { + if (isdigit(opt_e[1])) + config.tty_stderr = opt_e[1] - '0'; + else + config.tty_stderr = 1; + } + } + break; + case 'X': + config.X = 1; + break; + case 'S': + config.sdl = 1; + config.sdl_sound = 1; + break; + case 'w': + config.X_fullscreen = !config.X_fullscreen; + break; + case 'D': + parse_debugflags(optarg, 1); + break; + case 'p': + /* unused */ + break; + case 'P': + if (terminal_fd == -1) { + open_terminal_pipe(optarg); + } else + error("terminal pipe already open\n"); + break; + case 'V': + g_printf("Configuring as VGA video card & mapped ROM\n"); + config.vga = 1; + if (config.mem_size > 640) + config.mem_size = 640; + break; + case 'Y': + config.dos_trace = 1; + break; + case 'N': + warn("DOS will not be started\n"); + config.exitearly = 1; + break; + + case 'x': + config.xms_size = atoi(optarg); + x_printf("enabling %dK XMS memory\n", config.xms_size); + break; + + case 'e': + config.ems_size = atoi(optarg); + g_printf("enabling %dK EMS memory\n", config.ems_size); + break; + + case 'm': + g_printf("turning internal MOUSE driver %s\n", + config.mouse.intdrv ? "off" : "on"); + config.mouse.intdrv = !config.mouse.intdrv; + break; + + case 'E': + g_printf("DOS command given on command line: %s\n", optarg); + config.dos_cmd = optarg; + was_exec++; + break; + case 'K': { + char *p; + g_printf("Unix path set to %s\n", optarg); + config.unix_path = strdup(optarg); + p = strchr(config.unix_path, ':'); + if (p) { + *p = '\0'; + config.dos_path = strdup(p + 1); + if (p == config.unix_path) { + free(config.unix_path); + config.unix_path = NULL; + } + } + if (config.unix_path && !exists_dir(config.unix_path) && + !exists_file(config.unix_path)) { + error("Path %s does not exist\n", config.unix_path); + config.exitearly = 1; + break; + } + break; + } + case 'T': + if (!optarg || strchr(optarg, '1')) + was_T1++; + if (!optarg || strchr(optarg, 'h')) + misc_e6_store_options("SHELL_LOADHIGH_DEFAULT=1"); + break; + + case '?': + default: + fprintf(stderr, "unrecognized option or missing argument: -%c\n\r", optopt); + usage(basename); + exit(1); + } + } + + /* make-style env vars passing */ + while (optind < argc) { + char *p, *p1; + if (strchr(argv[optind], '=') == NULL) { + char *fpath; + if (config.dos_cmd || config.unix_path) + break; + fpath = expand_path(argv[optind]); + if (!fpath) { + error("%s not found\n", argv[optind]); + break; + } + p = strrchr(fpath, '/'); + assert(p); // expand_path() should add some slashes + config.unix_path = strdup(fpath); + p1 = config.unix_path + (p - fpath); + *p1 = '\0'; + p++; + if (!*p) { + free(fpath); + break; + } + config.dos_cmd = strdup(p); + free(fpath); + optind++; + /* collect args */ + while (argv[optind]) { + g_printf("DOS command given on command line: %s\n", argv[optind]); + config.dos_cmd = realloc(config.dos_cmd, + strlen(config.dos_cmd) + strlen(argv[optind]) + 2); + strcat(config.dos_cmd, " "); + strcat(config.dos_cmd, argv[optind]); + optind++; + } + was_exec++; + break; + } + g_printf("ENV given on command line: %s\n", argv[optind]); + misc_e6_store_options(argv[optind]); + optind++; + } + if (argv[optind]) { + fprintf(stderr, "unrecognized argument: %s\n\r", argv[optind]); + exit(1); + } + + if (was_exec && !was_T1) + misc_e6_store_options("DOSEMU_EXIT=1"); + + config_post_process(); + config_scrub(); + if (config_check_only) { + dump_config_status(0); + usage(basename); + exit(1); + } else { + if (debug_level('c') >= 8) + dump_config_status(log_dump_printf); + } +} + +/* + * Please keep "getopt_string", secure_option_preparse(), config_init(), + * usage() and the manpage in sync! + */ +static void +usage(char *basename) +{ + fprintf(stderr, + "dosemu-" VERSTR "\n\n" + "USAGE:\n" + " %s [options] [-K linux path] [-E dos command]\n" + "\n" + " -2,3,4,5,6 choose 286, 386, 486 or 586 or 686 CPU\n" + " -A boot from first defined floppy disk (A)\n" + " -B boot from second defined floppy disk (B) (#)\n" + " -C boot from first defined hard disk (C)\n" + " -c use PC console video (!%%)\n" + " -X run in X Window (#)\n" + " -S run in SDL (#)\n" + , basename); + print_debug_usage(stderr); + fprintf(stderr, + " -E STRING pass DOS command on command line\n" + " -d DIR - mount DIR as a drive under DOS\n" + " -e SIZE enable SIZE K EMS RAM\n" + " -f use dosrcFile as user config-file\n" + " --Fusers bypass /etc/dosemu.users (^^)\n" + " --Flibdir change keymap and FreeDOS location\n" + " --Fimagedir bypass systemwide boot path\n" + " -n bypass the user configuration file (.dosemurc)\n" + " -L load and execute DEXE File\n" + " -I insert config statements (on commandline)\n" + " -i[bootdir] (re-)install a DOS from bootdir or interactively\n" + " -h display this help\n" + " -H wait for dosdebug terminal at startup and pass dflags\n" + " -k use PC console keyboard (!)\n" + " -K Specify unix path for program running with -E\n" + " -M set memory size to SIZE kilobytes (!)\n" + " -m toggle internal mouse driver\n" + " -N No boot of DOS\n" + " -o FILE put debug messages in file\n" + " -P copy debugging output to FILE\n" + " -p stop for prompting with a non-fatal configuration problem\n" + " -s enable direct hardware access (full feature) (!%%)\n" + " -T set flags for -E or -K options\n" + " -t use terminal (S-Lang) mode\n" + " -V use BIOS-VGA video modes (!#%%)\n" + " -v display version\n" + " -w toggle windowed/fullscreen mode in X\n" + " -x SIZE enable SIZE K XMS RAM\n" + " -Y interactive boot\n" + "\n" + " (!) BE CAREFUL! READ THE DOCS FIRST!\n" + " (%%) require DOSEMU be run as root (i.e. suid)\n" + " (^^) require DOSEMU not be run as root (i.e. not suid)\n" + " (#) options do not fully work yet\n" + "\n"); + fprintf(stderr, + " %s --help\n" + " %s --version print version of dosemu (and show this help)\n", + basename, basename); +} + + + /* charset */ +static struct char_set *get_charset(const char *name) +{ + struct char_set *charset; + + charset = lookup_charset(name); + if (!charset) { + error("Can't find charset %s", name); + } + return charset; + +} + +void set_external_charset(const char *charset_name) +{ + struct char_set *charset; + charset = get_charset(charset_name); + if (!charset) { + error("charset %s not available\n", charset_name); + return; + } + charset = get_terminal_charset(charset); + if (charset) { + if (!trconfig.output_charset) { + trconfig.output_charset = charset; + } + if (!trconfig.keyb_charset) { + trconfig.keyb_charset = charset; + } + } + + config.external_cset = strdup(charset_name); +} + +void set_internal_charset(const char *charset_name) +{ + struct char_set *charset_video, *charset_config; + charset_video = get_charset(charset_name); + if (!charset_video) { + error("charset %s not available\n", charset_name); + return; + } + if (!is_display_charset(charset_video)) { + error("%s not suitable as an internal charset", charset_name); + } + charset_config = get_terminal_charset(charset_video); + if (charset_video) { + trconfig.video_mem_charset = charset_video; + } +#if 0 + if (charset_config) { + trconfig.keyb_config_charset = charset_config; + } +#endif + if (charset_config) { + trconfig.dos_charset = charset_config; + } + + config.internal_cset = strdup(charset_name); +} + +void set_country_code(int cntry) +{ + config.country = cntry; +} diff --git a/src/base/init/dev_list.c b/src/base/init/dev_list.c new file mode 100644 index 0000000..64cdf28 --- /dev/null +++ b/src/base/init/dev_list.c @@ -0,0 +1,204 @@ +/* + * DANG_BEGIN_MODULE + * + * REMARK + * Description: + * Manages a list of the available I/O devices. It will automatically + * call their initialization and termination routines. + * The current I/O device list includes: + * VERB + * Fully emulated: pit, pic, cmos, serial + * Partially emulated: rtc, keyb, lpt + * Unemulated: dma, hdisk, floppy, pos + * /VERB + * /REMARK + * + * Maintainers: Scott Buchholz + * + * DANG_END_MODULE + * + */ + +#include +#include + +#include "emu.h" +#include "iodev.h" +#include "int.h" +#include "port.h" +#include "pic.h" +#include "chipset.h" +#include "serial.h" +#include "mouse.h" +#include "lpt.h" +#include "disks.h" +#include "dma.h" +#include "dosemu_debug.h" +#include "pktdrvr.h" +#include "ne2000.h" +#include "ipx.h" +#include "sound.h" +#include "joystick.h" +#include "emm.h" +#include "xms.h" +#include "emudpmi.h" +#include "virq.h" +#include "vint.h" +#include "vtmr.h" +#include "dos2linux.h" + +struct io_dev_struct { + const char *name; + void (* init_func)(void); + void (* reset_func)(void); + void (* term_func)(void); +}; +#define MAX_IO_DEVICES 30 + +#define MAX_DEVICES_OWNED 50 +struct owned_devices_struct { + const char *dev_names[MAX_DEVICES_OWNED]; + int devs_owned; +} owned_devices[MAX_IO_DEVICES]; + +static int current_device = -1; + +static struct io_dev_struct io_devices[MAX_IO_DEVICES] = { + { "cmos", cmos_init, cmos_reset, NULL }, + { "video", video_post_init, NULL, NULL }, + { "internal_mouse", dosemu_mouse_init, NULL, dosemu_mouse_close }, + { "serial", serial_init, serial_reset, serial_close }, + { "pic", pic_init, pic_reset, NULL }, + { "chipset", chipset_init, NULL, NULL }, + { "virq", virq_init, virq_reset, NULL }, + { "vint", vint_init, NULL, NULL }, + { "vtmr", vtmr_init, vtmr_reset, vtmr_done }, + { "pit", pit_init, pit_reset, pit_done }, + { "rtc", rtc_init, NULL, NULL }, + { "lpt", printer_init, NULL, NULL }, + { "dma", dma_init, dma_reset, NULL }, +#if 0 + { "floppy", floppy_init, floppy_reset, NULL }, + { "hdisk", hdisk_init, hdisk_reset, NULL }, +#endif + { "disks", disk_init, disk_reset, NULL }, + { "sound", sound_init, sound_reset, sound_done }, + { "mt32", mt32_init, mt32_reset, mt32_done }, + { "joystick", joy_init, joy_reset, joy_term }, +#ifdef IPX + { "ipx", ipx_init, NULL, ipx_close }, +#endif + { "packet driver", pkt_init, pkt_reset, pkt_term }, + { "ne2000", ne2000_init, ne2000_reset, ne2000_done }, + { "ems", ems_init, ems_reset, NULL }, + { "xms", xms_init, xms_reset, xms_done }, + { "dpmi", dpmi_setup, dpmi_reset, NULL }, + { "mfs", NULL, mfs_reset, mfs_done }, + { "cdrom", NULL, NULL, cdrom_done }, + { "dos2tty", dos2tty_init, NULL, dos2tty_done }, + { NULL, NULL, NULL, NULL } +}; + + +void iodev_init(void) /* called at startup */ +{ + int i; + + for (i = 0; i < MAX_IO_DEVICES; i++) + if (io_devices[i].init_func) { + current_device = i; + io_devices[i].init_func(); + } + + current_device = -1; +} + +void iodev_reset(void) /* called at reboot */ +{ + struct io_dev_struct *ptr; + + for (ptr = io_devices; ptr < &io_devices[MAX_IO_DEVICES]; ptr++) + if (ptr->reset_func) + ptr->reset_func(); +} + +void iodev_term(void) +{ + struct io_dev_struct *ptr; + + for (ptr = io_devices; ptr < &io_devices[MAX_IO_DEVICES]; ptr++) + if (ptr->term_func) + ptr->term_func(); +} + +void iodev_register(const char *name, + void (*init_func)(void), + void (*reset_func)(void), + void (*term_func)(void)) +{ + struct io_dev_struct *ptr; + for(ptr = io_devices; ptr < &io_devices[MAX_IO_DEVICES -1]; ptr++) { + if (ptr->name) { + if (strcmp(ptr->name, name) == 0) { + g_printf("IODEV: %s already registered\n", + name); + return; + } + continue; + } + ptr->name = name; + ptr->init_func = init_func; + ptr->reset_func = reset_func; + ptr->term_func = term_func; + return; + } + g_printf("IODEV: Could not find free slot for %s\n", + name); + return; +} + +void iodev_unregister(const char *name) +{ + struct io_dev_struct *ptr; + for(ptr = io_devices; ptr < &io_devices[MAX_IO_DEVICES -1]; ptr++) { + if (!ptr->name || (strcmp(ptr->name, name) != 0)) { + continue; + } + ptr->name = 0; + ptr->init_func = 0; + ptr->reset_func = 0; + ptr->term_func = 0; + } +} + +static int find_device_owner(const char *dev_name) +{ + int i, j; + for(i = 0; i < MAX_IO_DEVICES - 1; i++) { + for(j = 0; j < owned_devices[i].devs_owned; j++) + if (strcmp(dev_name, owned_devices[i].dev_names[j]) == 0) + return i; + } + return -1; +} + +void iodev_add_device(const char *dev_name) +{ + int dev_own; + if (current_device == -1) { + error("add_device() is called not during the init stage!\n"); + leavedos(10); + } + dev_own = find_device_owner(dev_name); + if (dev_own != -1) { + error("Device conflict: Attempt to use %s for %s and %s\n", + dev_name, io_devices[dev_own].name, io_devices[current_device].name); + config.exitearly = 1; + } + if (owned_devices[current_device].devs_owned >= MAX_DEVICES_OWNED) { + error("No free slot for device %s\n", dev_name); + config.exitearly = 1; + } + c_printf("registering %s for %s\n",dev_name,io_devices[current_device].name); + owned_devices[current_device].dev_names[owned_devices[current_device].devs_owned++] = dev_name; +} diff --git a/src/base/init/init.c b/src/base/init/init.c new file mode 100644 index 0000000..8eec5a3 --- /dev/null +++ b/src/base/init/init.c @@ -0,0 +1,514 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include +#endif + +#include "init.h" +#include "version.h" +#include "emu.h" +#include "memory.h" +#include "emudpmi.h" +#include "bios.h" +#include "int.h" +#include "timers.h" +#include "video.h" +#include "vc.h" +#include "mouse.h" +#include "port.h" +#include "joystick.h" +#include "pktdrvr.h" +#include "ipx.h" +#include "bitops.h" +#include "pic.h" +#include "dma.h" +#include "xms.h" +#include "lowmem.h" +#include "iodev.h" +#include "doshelpers.h" +#include "cpu-emu.h" +#include "kvm.h" +#include "mapping.h" +#include "smalloc.h" +#include "vgaemu.h" +#include "cpi.h" + +#define GFX_CHARS 0xffa6e + +smpool main_pool; + +#if 0 +static inline void dbug_dumpivec(void) +{ + int i; + + for (i = 0; i < 256; i++) { + int j; + + dbug_printf("%02x %08x", i, ((unsigned int *) 0)[i << 1]); + for (j = 0; j < 8; j++) + dbug_printf(" %02x", ((unsigned char *) (BIOSSEG * 16 + 16 * i))[j]); + dbug_printf("\n"); + } +} +#endif + +/* + * DANG_BEGIN_FUNCTION stdio_init + * + * description: + * Initialize stdio, open debugging output file if user specified one + * + * DANG_END_FUNCTION + */ +void stdio_init(void) +{ + if (config.debugout == NULL) { + char *home = getenv("HOME"); + if (home) { + const static char *debugout = "/.dosemu/boot.log"; + config.debugout = malloc(strlen(home) + strlen(debugout) + 1); + strcpy(config.debugout, home); + strcat(config.debugout, debugout); + } + } + if (config.debugout && config.debugout[0] != '-') { + dbg_fd = fopen(config.debugout, "we"); + if (!dbg_fd) + error("can't open \"%s\" for writing\n", config.debugout); + else + setlinebuf(dbg_fd); + } + + real_stderr = stderr; +#if defined(HAVE_ASSIGNABLE_STDERR) && defined(HAVE_FOPENCOOKIE) + if (dbg_fd) + stderr = fstream_tee(stderr, dbg_fd); +#endif +} + +/* + * DANG_BEGIN_FUNCTION timer_interrupt_init + * + * description: + * Tells the OS to send us periodic timer messages + * + * DANG_END_FUNCTION + */ +void timer_interrupt_init(void) +{ + struct itimerval itv; + int delta; + + delta = (config.update / TIMER_DIVISOR); + /* Check that the kernel actually supports such a frequency - we + * can't go faster than jiffies with setitimer() + */ + if (((delta/1000)+1) < (1000/sysconf(_SC_CLK_TCK))) { + c_printf("TIME: FREQ too fast, using defaults\n"); + config.update = 54925; config.freq = 18; + delta = 54925 / TIMER_DIVISOR; + } + + itv.it_interval.tv_sec = 0; + itv.it_interval.tv_usec = delta; + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = delta; + c_printf("TIME: using %d usec for updating ALRM timer\n", delta); + + setitimer(ITIMER_REAL, &itv, NULL); +} + +/* + * DANG_BEGIN_FUNCTION map_video_bios + * + * description: + * Map the video bios into main memory + * + * DANG_END_FUNCTION + */ +uint32_t int_bios_area[0x500/sizeof(uint32_t)]; +void map_video_bios(void) +{ + v_printf("Mapping VBIOS = %d\n",config.mapped_bios); + + if (config.mapped_bios) { + if (config.vbios_file) { + warn("WARN: loading VBIOS %s into mem at %#x (%#X bytes)\n", + config.vbios_file, VBIOS_START, VBIOS_SIZE); + load_file(config.vbios_file, 0, LINEAR2UNIX(VBIOS_START), VBIOS_SIZE); + } + else if (config.vbios_copy) { + warn("WARN: copying VBIOS from /dev/mem at %#x (%#X bytes)\n", + VBIOS_START, VBIOS_SIZE); + load_file("/dev/mem", VBIOS_START, LINEAR2UNIX(VBIOS_START), VBIOS_SIZE); + } + else { + warn("WARN: copying VBIOS file from /dev/mem\n"); + load_file("/dev/mem", VBIOS_START, LINEAR2UNIX(VBIOS_START), VBIOS_SIZE); + } + + /* copy graphics characters from system BIOS */ + load_file("/dev/mem", GFX_CHARS, vga_rom_08, 128 * 8); + + memcheck_addtype('V', "Video BIOS"); + memcheck_reserve('V', VBIOS_START, VBIOS_SIZE); + if (!config.vbios_post || config.chipset == VESA) + load_file("/dev/mem", 0, (unsigned char *)int_bios_area, sizeof(int_bios_area)); + } +} + +static void setup_fonts(void) +{ + uint8_t *f8, *f14, *f16; + int l8, l14, l16; + uint16_t cp; + char *path; + + if (!config.internal_cset || strncmp(config.internal_cset, "cp", 2) != 0) + return; + cp = atoi(config.internal_cset + 2); + if (!cp) + return; + c_printf("loading fonts for %s\n", config.internal_cset); + path = assemble_path(dosemu_lib_dir_path, "cpi"); + f8 = cpi_load_font(path, cp, 8, 8, &l8); + f14 = cpi_load_font(path, cp, 8, 14, &l14); + f16 = cpi_load_font(path, cp, 8, 16, &l16); + if (f8 && f14 && f16) { + assert(l8 == 256 * 8); + memcpy(vga_rom_08, f8, l8); + assert(l14 == 256 * 14); + memcpy(vga_rom_14, f14, l14); + assert(l16 == 256 * 16); + memcpy(vga_rom_16, f16, l16); + } else { + error("CPI not found for %s\n", config.internal_cset); + } + free(f8); + free(f14); + free(f16); + free(path); +} + +/* + * DANG_BEGIN_FUNCTION map_custom_bios + * + * description: + * Setup the dosemu amazing custom BIOS, quietly overwriting anything + * was copied there before. + * + * DANG_END_FUNCTION + */ +void map_custom_bios(void) +{ + unsigned int ptr; + + /* make sure nothing overlaps */ + assert(bios_data_start >= DOSEMU_LMHEAP_OFF + DOSEMU_LMHEAP_SIZE); + /* Copy the BIOS into DOS memory */ + ptr = SEGOFF2LINEAR(BIOSSEG, bios_data_start); + e_invalidate(ptr, DOSEMU_BIOS_SIZE()); + MEMCPY_2DOS(ptr, _binary_bios_o_bin_start, DOSEMU_BIOS_SIZE()); + setup_fonts(); + /* Initialise the ROM-BIOS graphic font (lower half only) */ + MEMCPY_2DOS(GFX_CHARS, vga_rom_08, 128 * 8); +} + +/* + * DANG_BEGIN_FUNCTION memory_init + * + * description: + * Set up all memory areas as would be present on a typical i86 during + * the boot phase. + * + * DANG_END_FUNCTION + */ +void memory_init(void) +{ + map_custom_bios(); /* map the DOSEMU bios */ + setup_interrupts(); /* setup interrupts */ + bios_setup_init(); + /* Initialize the lowmem heap that resides in a custom bios */ + lowmem_init(); +} + +/* + * DANG_BEGIN_FUNCTION device_init + * + * description: + * Calls all initialization routines for devices (keyboard, video, serial, + * disks, etc.) + * + * DANG_END_FUNCTION + */ +void device_init(void) +{ + video_config_init(); /* privileged part of video init */ + keyb_priv_init(); + mouse_priv_init(); +} + +/* + * DANG_BEGIN_FUNCTION mem_reserve + * + * description: + * reserves address space seen by DOS and DPMI apps + * There are two possibilities: + * 1) 0-based mapping: one map at 0 of 1088k, and the combined map + * below. + * This is only used for i386 + vm86() (not KVM/CPUEMU) + * 2) non-zero-based mapping: one combined mmap. + * Everything needs to be below 4G for native DPMI + * (intrinsically) and JIT CPUEMU (for now) + * + * DANG_END_FUNCTION + */ +static void *mem_reserve(uint32_t memsize) +{ + void *result; + int cap = MAPPING_INIT_LOWRAM | MAPPING_SCRATCH | MAPPING_DPMI; + int prot = PROT_NONE; + +#ifdef __i386__ + if (config.cpu_vm == CPUVM_VM86) { + result = mmap_mapping(MAPPING_NULL | MAPPING_SCRATCH, + NULL, LOWMEM_SIZE + HMASIZE, PROT_NONE); + if (result == MAP_FAILED) { + const char *msg = + "You can most likely avoid this problem by running\n" + "sysctl -w vm.mmap_min_addr=0\n" + "as root, or by changing the vm.mmap_min_addr setting in\n" + "/etc/sysctl.conf or a file in /etc/sysctl.d/ to 0.\n" + "If this doesn't help, disable selinux in /etc/selinux/config\n"; +#ifdef X86_EMULATOR + if (errno == EPERM || errno == EACCES) { + /* switch on vm86-only JIT CPU emulation with non-zero base */ + config.cpu_vm = CPUVM_EMU; + c_printf("CONF: JIT CPUEMU set to 3 for %d86\n", (int)vm86s.cpu_type); + error("Using CPU emulation because vm.mmap_min_addr > 0\n"); + error("@%s", msg); + } else +#endif + { + perror("LOWRAM mmap"); + error("Cannot map low DOS memory (the first 640k)\n"); + error("@%s", msg); + exit(EXIT_FAILURE); + } + } + } +#endif + + result = mmap_mapping_huge_page_aligned(cap, memsize, prot); + if (result == MAP_FAILED) { + perror ("LOWRAM mmap"); + exit(EXIT_FAILURE); + } + return result; +} + +static void low_mem_init_config_scrub(void) +{ + const uint32_t mem_1M = 1024 * 1024; + /* 16Mb limit is for being in reach of DMAc */ + const uint32_t mem_16M = mem_1M * 16; + uint32_t min_phys_rsv = LOWMEM_SIZE + EXTMEM_SIZE; + + if (EXTMEM_SIZE < HMASIZE) + config.ext_mem = 64; + if (config.xms_size) { + /* reserve 1Mb for XMS mappings */ + min_phys_rsv += mem_1M; + if (min_phys_rsv > mem_16M) { + error("$_ext_mem too large, please set to (%d) or lower, or set $_xms=(0)\n", + (EXTMEM_SIZE - (min_phys_rsv - mem_16M)) / 1024); + config.exitearly = 1; + return; + } + } + + min_phys_rsv = roundUpToNextPowerOfTwo(LOWMEM_SIZE + EXTMEM_SIZE + XMS_SIZE); + if (config.dpmi && min_phys_rsv > config.dpmi_base) { + error("$_dpmi_base is too small, please set to at least (0x%x)\n", min_phys_rsv); + config.exitearly = 1; + return; + } +} + +static void do_sm_error(int prio, const char *fmt, ...) +{ + char buf[16384]; + va_list al; + + va_start(al, fmt); + vsnprintf(buf, sizeof(buf), fmt, al); + va_end(al); + if (prio >= 3) + dosemu_error("%s", buf); + else if (prio == 2) + error("%s", buf); + else + dbug_printf("%s", buf); +} + +void map_memory_space(void) +{ + unsigned char *lowmem, *ptr, *ptr2; + int result; + uint32_t memsize; + int32_t phys_rsv, phys_low; + + smregister_default_error_notifier(do_sm_error); + open_mapping(MAPPING_INIT_LOWRAM); + g_printf ("DOS+HMA memory area being mapped in\n"); + lowmem = alloc_mapping_huge_page_aligned(MAPPING_INIT_LOWRAM, LOWMEM_SIZE + + EXTMEM_SIZE); + if (lowmem == MAP_FAILED) { + perror("LOWRAM alloc"); + leavedos(98); + } + + phys_low = roundUpToNextPowerOfTwo(LOWMEM_SIZE + EXTMEM_SIZE + XMS_SIZE); + memsize = phys_low; + if (config.dpmi) + /* LOWMEM_SIZE accounted twice for alignment */ + memsize += config.dpmi_base + HUGE_PAGE_ALIGN(dpmi_mem_size()); + mem_base = mem_reserve(memsize); + mem_base_mask = ~(uintptr_t)0; +#ifdef __x86_64__ + if (_MAP_32BIT) mem_base_mask = 0xffffffffu; +#endif + register_hardware_ram_virtual('L', 0, LOWMEM_SIZE + HMASIZE, 0); + result = alias_mapping(MAPPING_LOWMEM, 0, LOWMEM_SIZE + HMASIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, lowmem); + if (result == -1) { + perror ("LOWRAM mmap"); + exit(EXIT_FAILURE); + } + c_printf("Conventional memory mapped from %p to %p\n", lowmem, mem_base); + + if (config.xms_size) + memcheck_reserve('x', LOWMEM_SIZE + EXTMEM_SIZE, XMS_SIZE); + + sminit_comu(&main_pool, mem_base, memsize, mcommit, muncommit); + ptr = smalloc(&main_pool, LOWMEM_SIZE + HMASIZE); + assert(ptr == mem_base); + /* smalloc uses PROT_READ | PROT_WRITE, needs to add PROT_EXEC here */ + mprotect_mapping(MAPPING_LOWMEM, 0, LOWMEM_SIZE + HMASIZE, PROT_READ | PROT_WRITE | + PROT_EXEC); + /* we have an uncommitted hole up to phys_low */ + ptr += phys_low; + phys_rsv = phys_low - (LOWMEM_SIZE + HMASIZE); + /* create non-identity mapping up to phys_low */ + ptr2 = smalloc_topdown(&main_pool, config.dpmi ? phys_low : phys_rsv); + assert(ptr2); + if (config.dpmi) { + void *dptr = smalloc_fixed(&main_pool, MEM_BASE32(config.dpmi_base), + dpmi_mem_size()); + assert(dptr); + if (config.cpu_vm_dpmi == CPUVM_KVM) { + /* map dpmi+uncommitted space to kvm */ + int prot = PROT_READ | PROT_WRITE | PROT_EXEC; + mmap_kvm(MAPPING_INIT_LOWRAM, phys_low, ptr2 - ptr, ptr, phys_low, prot); + } + /* unused hole for alignment */ + ptr2 += LOWMEM_SIZE + HMASIZE; + } + + /* LOWMEM_SIZE + HMASIZE == base */ + memcheck_addtype('X', "EXT MEM"); + memcheck_reserve('X', LOWMEM_SIZE + HMASIZE, EXTMEM_SIZE - HMASIZE); + x_printf("Ext.Mem of size 0x%x at %#x\n", EXTMEM_SIZE - HMASIZE, + LOWMEM_SIZE + HMASIZE); + + /* establish ext_mem alias access for int15 */ + register_hardware_ram_virtual('X', LOWMEM_SIZE + HMASIZE, phys_rsv, + DOSADDR_REL(ptr2)); + if (config.dpmi) { + register_hardware_ram_virtual('U', DOSADDR_REL(ptr2), phys_rsv, + LOWMEM_SIZE + HMASIZE); + /* create ext_mem alias for dpmi */ + result = alias_mapping(MAPPING_EXTMEM, DOSADDR_REL(ptr2), + EXTMEM_SIZE - HMASIZE, + PROT_READ | PROT_WRITE, + lowmem + LOWMEM_SIZE + HMASIZE); + assert(result != -1); + } + + /* R/O protect 0xf0000-0xf4000 */ + if (!config.umb_f0) { + memcheck_addtype('R', "ROM at f000:0000 for $_umb_f0 = (off)"); + memcheck_reserve('R', 0xF0000, 0x4000); + } +} + +/* + * DANG_BEGIN_FUNCTION version_init + * + * description: + * Find version of OS running and set necessary global parms. + * + * DANG_END_FUNCTION + */ +void version_init(void) { + +#ifdef __linux__ + struct utsname unames; + char *s; + + uname(&unames); + kernel_version_code = strtol(unames.release, &s,0) << 16; + kernel_version_code += strtol(s+1, &s,0) << 8; + kernel_version_code += strtol(s+1, &s,0); + + if (kernel_version_code < KERNEL_VERSION(2, 6, 6)) { + error("You are running a kernel older than 2.6.6.\n" + "This may be very problematic for DOSEMU.\n" + "Please upgrade to a newer Linux kernel before reporting\n" + "problems.\n"); + } +#else + kernel_version_code = 0; +#endif +} + +void print_version(void) +{ + struct utsname unames; + + uname(&unames); + warn("dosemu2-%s is coming up on %s version %s %s %s\n", VERSTR, + unames.sysname, unames.release, unames.version, unames.machine); + warn("Compiled with " +#ifdef __clang__ + "clang version %d.%d.%d (gnuc %d.%d)", __clang_major__, __clang_minor__, + __clang_patchlevel__, __GNUC__, __GNUC_MINOR__); +#else + "gcc version %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); +#endif +#ifdef i386 + warn(" 32bit\n"); +#else + warn(" 64bit\n"); +#endif +#ifdef CFLAGS_STR +#define __S(...) #__VA_ARGS__ +#define _S(x) __S(x) + warn("CFLAGS: %s\n", _S(CFLAGS_STR)); +#endif +} + +CONSTRUCTOR(static void init(void)) +{ + register_config_scrub(low_mem_init_config_scrub); +} diff --git a/src/base/init/lexer.h b/src/base/init/lexer.h new file mode 100644 index 0000000..c49e4de --- /dev/null +++ b/src/base/init/lexer.h @@ -0,0 +1,26 @@ +/* + * We are intercepting the yylex() function calls from the parser + */ +#ifndef LEXER_H +#define LEXER_H +#define OUR_YY_DECL int yylex (void) +OUR_YY_DECL; + +extern void tell_lexer_if(int value); +extern void tell_lexer_loop(int cfile, int value); + +#ifndef LEXER +extern void yyrestart(FILE *input_file); +extern FILE* yyin; +#endif + +extern void yyerror(const char *, ...); +extern void yywarn(const char *, ...); +extern char *yy_vbuffer; +extern int include_stack_ptr; +extern char *include_fnames[]; +extern int include_lines[]; +extern int line_count; +extern int last_include; + +#endif diff --git a/src/base/init/lexer.l b/src/base/init/lexer.l new file mode 100644 index 0000000..0e78895 --- /dev/null +++ b/src/base/init/lexer.l @@ -0,0 +1,1210 @@ +%option noinput nounput +%{ + +#define YY_NO_UNPUT 1 +#define USE_LOOP_HANDLING 1 +#define LEXER 1 + +#if USE_LOOP_HANDLING + #define YY_NEVER_INTERACTIVE 1 /* need this to avoid access to yyin within + lexer code, even if yyin != NULL */ + /* however, flex 2.5.31 no longer respects this #define so we work + around it by opening /dev/null if a cachefile or a macrofile is + accessed. */ + #define LOOP_LIMIT 1000 /* this to avoid infinite loops, + if the user has errors in his + config file */ +#endif + +/* Flex defines these, but stdint.h is going to define them again */ +#undef INT8_MAX +#undef INT16_MAX +#undef INT32_MAX +#undef UINT8_MAX +#undef UINT16_MAX +#undef UINT32_MAX + +#include +#include +#undef ECHO +#include +#include +#include +#include +#include +#include +#include /* structure stat */ +#include /* prototype for stat() */ + +#include "emu.h" +#include "cpu.h" +#include "disks.h" +#include "lpt.h" +#include "video.h" +#include "mouse.h" +#include "serial.h" +#include "timers.h" +#include "keyboard/keymaps.h" + +#include "parser.h" +#include "dosemu_config.h" +#include "lexer.h" +int line_count; + +#define NESTING_DEPTH 32 /* as we handle loops as pseudo includes, + * this defines the depth of nested loops + * as well as that of includes + */ + +static int __active__=1, __next_ITEM_for_me__=0; +#define __active_max (2*NESTING_DEPTH) /* Note: 'while' pushes twice */ +static int __active_stack[__active_max+1]={1,0}; +static int __active_i=1; +int include_stack_ptr = 0; +int last_include = 0; + +static void push__active(void); +static void pop__active(void); +static void enter_includefile(char * fname); +static void enter_macrofile(char *variable); + +#if 0 /* this just to test the conditional code */ + #define TESTACTIVE (({if(__active__)fprintf(stderr,">>>%s<<<\n", yytext);}),__active__) +#else + #define TESTACTIVE __active__ +#endif + +#define RETURN if (TESTACTIVE) return +#define MAY_BE if (TESTACTIVE) + +#define MAY_BEFORME if (__next_ITEM_for_me__) { \ + if (get_config_variable(yytext)) { \ + __active__= (__next_ITEM_for_me__ > 0); \ + } \ + else __active__= (__next_ITEM_for_me__ < 0); \ + __next_ITEM_for_me__=0; \ +} \ +else if (TESTACTIVE) + +#define MAY_BEINCLUDE(other_stuff) if (TESTACTIVE) { \ + if (__next_ITEM_for_me__ == 2) { \ + __next_ITEM_for_me__=0; \ + yytext[strlen(yytext)-1] = '\0'; \ + enter_includefile(&yytext[1]); \ + } \ + else other_stuff \ +} + +char *yy_vbuffer=0; + +#if USE_LOOP_HANDLING + +#define CACHEFILES_MAX 32 +#define MACROFILE CACHEFILES_MAX +FILE *dev_null_files[MACROFILE+1]; + +static int cachefile_nr(FILE *f) +{ + int i; + for (i = 0; i < CACHEFILES_MAX; i++) + if (dev_null_files[i] == f) return i; + return CACHEFILES_MAX+1; +} + +#define YY_INPUT(buf,result,max_size) { \ + result = do_yy_input(buf, max_size); \ +} + +/* + * We are intercepting the yylex() function calls from the parser + */ +#undef YY_DECL +#define YY_DECL static int real_yylex ( YYSTYPE* yylval ) + + +/* + * we intercept lexer before executing each action + */ +#define YY_USER_ACTION if (__loop_handling__ > 0) \ + if (dump_input(yy_act)) break; + +static int __loop_handling__ = 0; +static int dump_input(int rulenum); +static void enter_cachefile(int cfile); +static int cachefile_read(char *buf, int size, int cfile); +static int cachefile_wrap(void); +static int macrofile_read(char *buf, int size); +static int macrofile_wrap(void); +static int do_yy_input(char *buf, int max_size); + +#else /* not USE_LOOP_HANDLING */ + + +#define YY_INPUT(buf,result,max_size) \ + if (!yyin) { \ + if (yy_vbuffer && yy_vbuffer[0]) { \ + buf[(max_size)-1]=0; \ + strncpy(buf,yy_vbuffer,max_size); \ + if (buf[(max_size)-1]) { \ + yy_vbuffer+=max_size; \ + result=max_size; \ + } \ + else { \ + result=strlen(buf); \ + yy_vbuffer=0; \ + } \ + } \ + else result=0; \ + } \ + else { \ + if ( YY_CURRENT_BUFFER->yy_is_interactive ) { \ + int c = getc( yyin ); \ + result = c == EOF ? 0 : 1; \ + buf[0] = (char) c; \ + } \ + else { \ + if ( ((result = fread( buf, 1, max_size, yyin )) == 0) && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + } \ + } + +#undef YY_DECL +#define YY_DECL int yylex YY_PROTO(( YYSTYPE* yylval )) + +#endif /* not USE_LOOP_HANDLING */ + + +%} + +DIGIT [0-9] +HEXDIGIT {DIGIT}|[a-fA-F] +LETTER [a-zA-Z] +ALPHNUM {LETTER}|{DIGIT} +IDENT _*{LETTER}({ALPHNUM}|_)* +STRQUOTELESS ({LETTER}|[/\.\~])({ALPHNUM}|[_\~@\-\+:\./])* + +%% + +%{ + /* NOTE: "include" _must_ be the first rule, we need to know + * the rule number, which _here_ is always '1' + */ +#define INCLUDE_RULE_NUM 1 +#define LOOP_RULE_NUM (INCLUDE_RULE_NUM+1) +#define DONE_RULE_NUM (LOOP_RULE_NUM+1) +%} +"include" MAY_BE __next_ITEM_for_me__=2; +"while"|"foreach" MAY_BE { + #if USE_LOOP_HANDLING + if (!__loop_handling__) { + __loop_handling__ =1; + YY_USER_ACTION + } + #endif + }; +"done" if (__loop_handling__ <0) pop__active(); +"while__yy__" { push__active(); RETURN(WHILESTATEMENT); } +"foreach__yy__" { push__active(); RETURN(FOREACHSTATEMENT); } + + /* special characters */ + +[{}()<>=,\-+\*;] RETURN(yytext[0]); + + /* conditionals */ + +"if" { push__active(); RETURN(IFSTATEMENT); } +"ifdef" { push__active(); if (__active__) __next_ITEM_for_me__=1; } +"ifndef" { push__active(); if (__active__) __next_ITEM_for_me__=-1; } +"else" { + if (__active__) __active__=0; + else if (__active_stack[__active_i-1] ) __active__=1; + } +"endif" pop__active(); + + /* config variable settings */ +"define" RETURN(DEFINE); +"undef" RETURN(UNDEF); +"checkuservar" RETURN(CHECKUSERVAR); + + /* boolean values */ +on RETURN(L_ON); +off RETURN(L_OFF); +auto RETURN(L_AUTO); +yes RETURN(L_YES); +no RETURN(L_NO); + + /* operators */ +"/" RETURN('/'); +"div" RETURN('/'); +"|" RETURN(OR_OP); +"^" RETURN(XOR_OP); +">>" RETURN(SHR_OP); +"<<" RETURN(SHL_OP); +"!" RETURN(NOT_OP); +"==" RETURN(EQ_OP); +">=" RETURN(GE_OP); +"<=" RETURN(LE_OP); +"!=" RETURN(NEQ_OP); +"&&" RETURN(L_AND_OP); +"&" RETURN(AND_OP); +"||" RETURN(L_OR_OP); +"~" RETURN(BIT_NOT_OP); +"eq" RETURN(STR_EQ_OP); +"ne" RETURN(STR_NEQ_OP); + + /* numbers */ +\\u{HEXDIGIT}{4} MAY_BE{yylval->i_value = strtoul(yytext+2, 0, 16); + return(UNICODE); } + + +{DIGIT}+ MAY_BE {yylval->i_value = strtoul(yytext, 0, 10); + return(INTEGER); } + +0x{HEXDIGIT}+ MAY_BE {yylval->i_value = strtoul(yytext, 0, 0); + return(INTEGER); } + +0b[01]+ MAY_BE {yylval->i_value = strtoul(yytext+2, 0, 2); + return(INTEGER); } + +{DIGIT}+\.{DIGIT}*([eE]\-?{DIGIT}+)? MAY_BE {yylval->r_value = atof(yytext); + return(REAL); } + + /* casts */ +"int" RETURN(INTCAST); +"real" RETURN(REALCAST); + + /* functions */ +"strlen" RETURN(STRLEN); +"strtol" RETURN(STRTOL); +"strncmp" RETURN(STRNCMP); +"strcat" RETURN(STRCAT); +"strpbrk" RETURN(STRPBRK); +"strsplit" RETURN(STRSPLIT); +"strdel" RETURN(STRDEL); +"strchr" RETURN(STRCHR); +"strrchr" RETURN(STRRCHR); +"strstr" RETURN(STRSTR); +"strspn" RETURN(STRSPN); +"strcspn" RETURN(STRCSPN); +"defined" RETURN(DEFINED); +"shell" RETURN(SHELL); + + /* just for test purposes */ +exprtest RETURN(EXPRTEST); + + /* keywords */ + +feature RETURN(FEATURE); +abort RETURN(ABORT); +warn RETURN(WARN); +error RETURN(ERROR); +x RETURN(L_X); +sdl RETURN(L_SDL); +fastfloppy RETURN(FASTFLOPPY); +timer RETURN(TIMER); +hogthreshold RETURN(HOGTHRESH); +speaker RETURN(SPEAKER); +ipxsupport RETURN(IPXSUPPORT); +ipx_network RETURN(IPXNETWORK); +pktdriver RETURN(PKTDRIVER); +ne2k RETURN(NE2K); +debug RETURN(DEBUG); +mouse RETURN(MOUSE); +serial RETURN(SERIAL); +keyboard RETURN(KEYBOARD); +keystroke RETURN(PRESTROKE); +terminal RETURN(TERMINAL); +video RETURN(VIDEO); +emuretrace RETURN(EMURETRACE); +mathco RETURN(MATHCO); +cpu RETURN(CPU); +cpuspeed RETURN(CPUSPEED); +bootdrive RETURN(BOOTDRIVE); +swap_bootdrive RETURN(SWAP_BOOTDRIVE); +xms RETURN(L_XMS); +umb_a0 RETURN(UMB_A0); +umb_b0 RETURN(UMB_B0); +umb_b8 RETURN(UMB_B8); +umb_f0 RETURN(UMB_F0); +hma RETURN(HMA); +dos_up RETURN(DOS_UP); +ems RETURN(L_EMS); +dpmi RETURN(L_DPMI); +dpmi_base RETURN(DPMI_BASE); +pm_dos_api RETURN(PM_DOS_API); +ignore_djgpp_null_derefs RETURN(NO_NULL_CHECKS); +dosmem RETURN(DOSMEM); +ext_mem RETURN(EXT_MEM); +ports RETURN(PORTS); +trace RETURN(TRACE); +clear RETURN(CLEAR); +trace_mmio RETURN(TRACE_MMIO); +sillyint RETURN(SILLYINT); +irqpassing RETURN(SILLYINT); +hardware_ram RETURN(HARDWARE_RAM); +disk RETURN(DISK); +printer RETURN(PRINTER); +emusys RETURN(EMUSYS); +file_lock_limit RETURN(FILE_LOCK_LIMIT); +lfn_support RETURN(LFN_SUPPORT); +force_int_revect RETURN(FINT_REVECT); +set_int_hooks RETURN(SET_INT_HOOKS); +trace_irets RETURN(TRACE_IRETS); +force_fs_redirect RETURN(FFS_REDIR); +ttylocks RETURN(TTYLOCKS); +sound_emu RETURN(L_SOUND); +oss_options RETURN(L_SND_OSS); +joystick_emu RETURN(L_JOYSTICK); +dosemumap RETURN(DOSEMUMAP); +logbufsize RETURN(LOGBUFSIZE); +logfilesize RETURN(LOGFILESIZE); +mappingdriver RETURN(MAPPINGDRIVER); + + /* sillyint values */ +use_sigio RETURN(USE_SIGIO); + + /* ems values */ +ems_size RETURN(EMS_SIZE); +ems_frame RETURN(EMS_FRAME); +ems_uma_pages RETURN(EMS_UMA_PAGES); +ems_conv_pages RETURN(EMS_CONV_PAGES); + + /* speaker values */ + +emulated RETURN(EMULATED); +native RETURN(NATIVE); + + /* cpuemu values */ + +cpu_vm RETURN(CPU_VM); +cpu_vm_dpmi RETURN(CPU_VM_DPMI); +kvm RETURN(KVM); +cpuemu RETURN(CPUEMU); +vm86 RETURN(VM86); + + /* disk keywords */ +hdimage RETURN(HDIMAGE); +image RETURN(HDIMAGE); +partition RETURN(L_PARTITION); +wholedisk RETURN(WHOLEDISK); +readonly RETURN(READONLY); +ro RETURN(READONLY); +threeinch RETURN(THREEINCH); +threeinch_2880 RETURN(THREEINCH_2880); +threeinch_720 RETURN(THREEINCH_720); +fiveinch RETURN(FIVEINCH); +fiveinch_360 RETURN(FIVEINCH_360); +boot RETURN(BOOT); +sectors RETURN(SECTORS); +cylinders RETURN(CYLINDERS); +tracks RETURN(TRACKS); +heads RETURN(HEADS); +offset RETURN(OFFSET); +floppy RETURN(L_FLOPPY); +cdrom RETURN(CDROM); +diskcyl4096 RETURN(DISKCYL4096); +hdtype1 RETURN(HDTYPE1); +hdtype2 RETURN(HDTYPE2); +hdtype9 RETURN(HDTYPE9); +default_drives RETURN(DEFAULT_DRIVES); +skip_drives RETURN(SKIP_DRIVES); + + /* keyboard */ +ctrl RETURN(CTRL_MAP); +shift_alt RETURN(SHIFT_ALT_MAP); +ctrl_alt RETURN(CTRL_ALT_MAP); +keytable RETURN(KEYTABLE); +layout RETURN(LAYOUT); +rawkeyboard RETURN(RAWKEYBOARD); +shift RETURN(SHIFT_MAP); +alt RETURN(ALT_MAP); +numpad RETURN(NUMPAD_MAP); +dump RETURN(DUMP); + /* dead keys for accents in keytable */ +dgrave RETURN(DGRAVE); +dacute RETURN(DACUTE); +dcircum RETURN(DCIRCUM); +dtilde RETURN(DTILDE); +dbreve RETURN(DBREVE); +daboved RETURN(DABOVED); +ddiares RETURN(DDIARES); +dabover RETURN(DABOVER); +ddacute RETURN(DDACUTE); +dcedilla RETURN(DCEDILLA); +diota RETURN(DIOTA); +dogonek RETURN(DOGONEK); +dcaron RETURN(DCARON); + + /* serial stuff */ + +base RETURN(BASE); +irq RETURN(IRQ); +baudrate RETURN(BAUDRATE); +device RETURN(DEVICE); +com RETURN(COM); +virtual RETURN(VIRTUAL); +vmodem RETURN(VMODEM); +pseudo RETURN(PSEUDO); +rtscts RETURN(RTSCTS); +low_latency RETURN(LOWLAT); +pccom RETURN(PCCOM); +exec RETURN(EXEC); +pts RETURN(PTS); +wrfile RETURN(WRFILE); +nullmodem RETURN(NULLMM); + + /* lock file stuff */ +directory RETURN(DIRECTORY); +namestub RETURN(NAMESTUB); +binary RETURN(BINARY); + + /* terminal stuff */ + +charset RETURN(CHARSET); +xterm_title RETURN(XTERM_TITLE); +color RETURN(COLOR); +escchar RETURN(ESCCHAR); +size RETURN(SIZE); + + /* mouse types */ + +microsoft RETURN(MICROSOFT); +ms3button RETURN(MS3BUTTON); +logitech RETURN(LOGITECH); +mmseries RETURN(MMSERIES); +mouseman RETURN(MOUSEMAN); +hitachi RETURN(HITACHI); +mousesystems RETURN(MOUSESYSTEMS); +busmouse RETURN(BUSMOUSE); +ps2 RETURN(PS2); +imps2 RETURN(IMPS2); +internaldriver RETURN(INTERNALDRIVER); +emulate3buttons RETURN(EMULATE3BUTTONS); +cleardtr RETURN(CLEARDTR); +mouse_ungrab_tweak RETURN(UNGRAB_TWEAK); + + /* video stuff - sorry for Matrox but MGA was already used */ + +vga RETURN(VGA); +ega RETURN(EGA); +cga RETURN(CGA); +mga RETURN(MGA); +mda RETURN(MGA); +none RETURN(NONE); +console RETURN(CONSOLE); +graphics RETURN(GRAPHICS); +chipset RETURN(CHIPSET); +memsize RETURN(MEMSIZE); +fullrestore RETURN(FULLREST); +partialrestore RETURN(PARTREST); +vgaemubios_file RETURN(VGAEMUBIOS_FILE); +vbios_file RETURN(VBIOS_FILE); +vbios_copy RETURN(VBIOS_COPY); +vbios_mmap RETURN(VBIOS_MMAP); +vbios_seg RETURN(VBIOS_SEG); +vbios_size RETURN(VBIOS_SIZE_TOK); +vbios_post RETURN(VBIOS_POST); +vga_fonts RETURN(VGA_FONTS); +dualmon RETURN(DUALMON); +forcevtswitch RETURN(FORCE_VT_SWITCH); +pci RETURN(PCI); +plainvga MAY_BE { yylval->i_value = PLAINVGA; return(CHIPSET_TYPE); } +svgalib MAY_BE { yylval->i_value = SVGALIB; return(CHIPSET_TYPE); } +vesa MAY_BE { yylval->i_value = VESA; return(CHIPSET_TYPE); } + + /* xwindows stuff */ + +display RETURN(L_DISPLAY); +title RETURN(L_TITLE); +title_show_appname RETURN(X_TITLE_SHOW_APPNAME); +icon_name RETURN(ICON_NAME); +blinkrate RETURN(X_BLINKRATE); +sharecmap RETURN(X_SHARECMAP); +mitshm RETURN(X_MITSHM); +font RETURN(X_FONT); +fixed_aspect RETURN(X_FIXED_ASPECT); +aspect_43 RETURN(X_ASPECT_43); +lin_filt RETURN(X_LIN_FILT); +bilin_filt RETURN(X_BILIN_FILT); +mode13fact RETURN(X_MODE13FACT); +winsize RETURN(X_WINSIZE); +gamma RETURN(X_GAMMA); +vgaemu_memsize RETURN(VGAEMU_MEMSIZE); +vesamode RETURN(VESAMODE); +lfb RETURN(X_LFB); +pm_interface RETURN(X_PM_INTERFACE); +mgrab_key RETURN(X_MGRAB_KEY); +background_pause RETURN(X_BACKGROUND_PAUSE); +fullscreen RETURN(X_FULLSCREEN); +noclose RETURN(X_NOCLOSE); +noresize RETURN(X_NORESIZE); + + /* SDL stuff */ +sdl_hwrend RETURN(SDL_HWREND); +sdl_fonts RETURN(SDL_FONTS); +sdl_wcontrols RETURN(SDL_WCONTROLS); +sdl_clip_native RETURN(SDL_CLIP_NATIVE); + + /* Sound stuff */ + +sb_base RETURN(SB_BASE); +sb_irq RETURN(SB_IRQ); +sb_dma RETURN(SB_DMA); +sb_hdma RETURN(SB_HDMA); +mpu_base RETURN(MPU_BASE); +mpu_base_mt32 RETURN(MPU_BASE_MT32); +midi_synth RETURN(MIDI_SYNTH); +mpu_irq RETURN(MPU_IRQ); +mpu_irq_mt32 RETURN(MPU_IRQ_MT32); +sound_driver RETURN(SOUND_DRIVER); +midi_driver RETURN(MIDI_DRIVER); +fluid_sfont RETURN(FLUID_SFONT); +fluid_volume RETURN(FLUID_VOLUME); +munt_roms RETURN(MUNT_ROMS); +opl2lpt_dev RETURN(OPL2LPT_DEV); +opl2lpt_type RETURN(OPL2LPT_TYPE); +snd_plugin_params RETURN(SND_PLUGIN_PARAMS); +pcm_hpf RETURN(PCM_HPF); +midi_file RETURN(MIDI_FILE); +wav_file RETURN(WAV_FILE); + + /* Joystick stuff */ + +joy_device RETURN(JOY_DEVICE); +joy_dos_min RETURN(JOY_DOS_MIN); +joy_dos_max RETURN(JOY_DOS_MAX); +joy_granularity RETURN(JOY_GRANULARITY); +joy_latency RETURN(JOY_LATENCY); + + /* packet driver */ +novell_hack RETURN(NOVELLHACK); +ethdev RETURN(ETHDEV); +tapdev RETURN(TAPDEV); +vdeswitch RETURN(VDESWITCH); +slirpargs RETURN(SLIRPARGS); +netsock RETURN(NETSOCK); +vnet RETURN(VNET); + /* debug flags */ + +io RETURN(IO); +port RETURN(PORT); +config RETURN(CONFIG); +read RETURN(READ); +write RETURN(WRITE); +keyb RETURN(KEYB); +warning RETURN(WARNING); +general RETURN(GENERAL); +hardware RETURN(HARDWARE); +ipc RETURN(L_IPC); +network RETURN(NETWORK); +sound RETURN(SOUND); +joystick RETURN(JOYSTICK); + + /* printer stuff */ + +lpt RETURN(LPT); +command RETURN(COMMAND); +timeout RETURN(TIMEOUT); +file RETURN(L_FILE); + + /* port/io stuff */ + +ormask RETURN(ORMASK); +andmask RETURN(ANDMASK); +rdonly RETURN(RDONLY); +wronly RETURN(WRONLY); +rdwr RETURN(RDWR); +range RETURN(RANGE); +fast RETURN(FAST); +slow RETURN(SLOW); + + /* ASPI driver */ +aspi RETURN(ASPI); +devicetype RETURN(DEVICETYPE); +target RETURN(TARGET); + + /* hacks */ +cli_timeout RETURN(CLI_TIMEOUT); +timemode RETURN(TIMEMODE); +timer_tweaks RETURN(TIMER_TWEAKS); + + /* charset stuff */ +external RETURN(EXTERNAL); +internal RETURN(INTERNAL); + + + /* perms */ +unix_exec RETURN(UEXEC); +lredir_paths RETURN(LPATHS); + +hostfs_drives RETURN(HDRIVES); + + /* strings */ + +\'[^\']*\' { + char *s; + for(s = strchr(yytext, '\n'); s != NULL; s = strchr(s+1,'\n')) + line_count++; + + MAY_BE { + yylval->s_value = strdup(yytext); + return(STRING); } + } + +{STRQUOTELESS} MAY_BEFORME { + yylval->s_value = strdup(yytext); + return(STRING); } +${IDENT} MAY_BE { + yylval->s_value = strdup(&yytext[1]); + return(VARIABLE); + } +$${IDENT} MAY_BE { enter_macrofile(&yytext[2]);} + + /* Note: we need the rule numbers of below actions + * The below first one is INCLUDEFILE_RULE_NUM + */ +\"[^\"]*\" { + char *s; + for(s = strchr(yytext, '\n'); s != NULL; s = strchr(s+1,'\n')) + line_count++; + + MAY_BEINCLUDE ( { + yylval->s_value = strdup(yytext); + return(STRING); } ) + } + + /* comments & whitespace */ + +[#][^\n]* ; /* comments to (and excluding) EOLN */ +[ \t]+ ; /* ignore all white space */ +\n line_count++; /* keep track of lines seen */ +. ;/* fprintf(stderr, "%s:%d discarding char '%c'\n", + include_fnames[include_stack_ptr],line_count, yytext[0]); */ + + +%% + /* the above '\n' rule (last rule) has rule-number 'YY_NUM_RULES-2' + * We have no other chance as to 'count' backward to get the + * rule number of ' quoted string, comments, e.t.c' + * BIG_FAT_NOTE: If you insert rules behind INCLUDEFILE_RULE_NUM, + * change INCLUDEFILE_RULE_NUM too !!! + */ +#define INCLUDEFILE_RULE_NUM (YY_NUM_RULES-2 - 3) +#define COMMENT_RULE_NUM (INCLUDEFILE_RULE_NUM +1) +#define WHITESPACE_RULE_NUM (COMMENT_RULE_NUM +1) +#define NEWLINE__RULE_NUM (WHITESPACE_RULE_NUM +1) + +#define MAX_INCLUDE_DEPTH NESTING_DEPTH +static YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH] ={0}; +char * include_fnames[MAX_INCLUDE_DEPTH] = {0}; +char * include_macbuf[MAX_INCLUDE_DEPTH] = {0}; +int include_lines[MAX_INCLUDE_DEPTH] = {0}; + +static void enter_includefile(char * fname) +{ + FILE * new_yyin; + char fname_[256]; + if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) { + yyerror("Includes nested too deeply" ); + return; + } + /* we want to have the include files in the same directory as + * the main config file, if we have no leading '/' + * and we want "keymap/xxx" mapped to e.g. "/keymap/xxx" + */ + if (fname[0] != '/') { + int i; + if (!strncmp(fname, keymap_dir_path, strlen(keymap_dir_path))) + strcpy(fname_, keymap_load_base_path); + else + strcpy(fname_, include_fnames[include_stack_ptr]); + i=strlen(fname_); + while (i && (fname_[i] != '/')) i--; + if (i) { + i++; + strcpy(fname_+i,fname); + fname=fname_; + } + } + new_yyin = fopen( fname, "re" ); + if ( ! new_yyin ) { + yyerror("cannot open includefile %s", fname); + return; + } + c_printf("CONF: opened include file %s\n", fname); + include_lines[include_stack_ptr] = line_count; + include_stack[include_stack_ptr] = YY_CURRENT_BUFFER; + include_stack_ptr++; + include_fnames[include_stack_ptr] = strdup(fname); + line_count = 1; + yyin = new_yyin; + include_stack[include_stack_ptr] = yy_create_buffer( yyin, YY_BUF_SIZE ); + yy_switch_to_buffer(include_stack[include_stack_ptr]); + last_include = 0; +} + +int yywrap(void) /* this gets called at EOF of a parsed file */ +{ + if (include_stack_ptr <= 0 ) return(1); +#if USE_LOOP_HANDLING +#if 1 + if (cachefile_nr(yyin) < CACHEFILES_MAX) return cachefile_wrap(); + if (yyin == dev_null_files[MACROFILE]) return macrofile_wrap(); +#else + if (macrobuffer) return macrofile_wrap(); + if (__loop_handling__ < 0) return cachefile_wrap(); +#endif +#endif + yy_switch_to_buffer(include_stack[include_stack_ptr-1] ); + fclose(include_stack[include_stack_ptr]->yy_input_file); + yy_delete_buffer(include_stack[include_stack_ptr]); + c_printf("CONF: closed include file %s\n", include_fnames[include_stack_ptr]); + free(include_fnames[include_stack_ptr]); + include_stack_ptr--; + line_count=include_lines[include_stack_ptr]; + last_include = 0; + return(0); +} + + +static void push__active(void) +{ + if (config_check_only>2) + fprintf(stderr, "%03d: PUSH %d->%d %d >%s<\n", line_count, __active_i, __active_i+1, __active__, yytext); + __active_stack[__active_i++]=__active__; + if (__active_i > __active_max) { + __active_i = __active_max; + yyerror("Lexer block stack overflow, unmatching if..else..endif"); + } +} + +static void pop__active(void) +{ + if (config_check_only>2) + fprintf(stderr, "%03d: POP %d<-%d %d >%s< %d\n", line_count, __active_i-1, __active_i, __active__, yytext, __loop_handling__); + if (__active_i <=0) { + yyerror("Lexer block stack underflow, unmatching if..else..endif"); + return; + } + __active__=__active_stack[--__active_i]; +} + +void tell_lexer_if(int value) +{ + __active__ = value !=0; +} + + +#if USE_LOOP_HANDLING + +/* --------------------------------------------------------- + * yylex() wrapper + * + * Here the record/replay stuff is handled. + * We need that in order to realize 'while' loops or such things + */ + + +#define SAVEBUFSIZE 0x4000 +static char *savebuf = 0; +static int savebufsize = 0; +static int savebufwptr =0; + + +#define CACHEFILESIZE (CACHEFILES_MAX * sizeof(struct cache_file)) +static struct cache_file *cachefile = 0; +static int cachefilesize = 0; +static int cachefilewptr = -1; +static int cachefilecurrent = -1; + +static void free_savebuffer(void); +static void free_cachefile_buffers(void); +static void close_cachefile_write(int stop); + + +OUR_YY_DECL +{ + int ret; + + ret = real_yylex(&yylval); + + if (__loop_handling__ >0) { + /* we respawn yylex in a loop, while filling the cache files */ + while (__loop_handling__ >0) { + ret = real_yylex(&yylval); + if (ret == YY_NULL) { + __loop_handling__ = 0; + close_cachefile_write(savebufwptr); + free_cachefile_buffers(); + free_savebuffer(); + yyerror("EOF while in loop, probably 'done' missing"); + return ret; + } + } + /* and again, to get the first token again */ + ret = real_yylex(&yylval); + } + return(ret); +} + +static void free_savebuffer(void) +{ + if (savebuf) free(savebuf); + savebuf = 0; + savebufsize = 0; + savebufwptr = 0; +} + +static void dumpout(const char *s, int len) +{ + if ((savebufwptr + len) > savebufsize) { + do { + savebufsize += SAVEBUFSIZE; + } while ((savebufwptr + len) > savebufsize); + if (!savebuf) + savebuf = malloc(savebufsize); + else savebuf = realloc(savebuf, savebufsize); + } + memcpy(savebuf+savebufwptr, s, len); + savebufwptr += len; +} + + +struct cache_file { + int start; /* point behind the loop begin statement */ + int stop; /* point behind the loop end statement */ + int rptr; + int parent; + YY_BUFFER_STATE yybuffer; + char *origfname; + int firstline; + int need_loop_back; + int looplimit; +}; + +static void free_cachefile_buffers(void) +{ + int i; + if (cachefile) { + for (i=0; i <= cachefilewptr; i++) + if (cachefile[i].origfname) free(cachefile[i].origfname); + free(cachefile); + cachefile = 0; + } + cachefilesize = 0; + cachefilecurrent = cachefilewptr = -1; +} + +static void create_cachefile(int start) +{ + struct cache_file *cf; + + cachefilewptr++; + if (((cachefilewptr+1) * sizeof(struct cache_file)) > cachefilesize) { + cachefilesize += CACHEFILESIZE; + if (!cachefile) + cachefile = malloc(cachefilesize); + else cachefile = realloc(cachefile, cachefilesize); + } + cf = cachefile + cachefilewptr; + cf->start = start; + cf->stop = start; + cf->rptr = start; + cf->firstline = line_count; + cf->origfname = 0; +#if 0 + /* NOTE: The 'include_stack_ptr > 0' sanity check may be needed + because of errors in do loops (to much 'done'), though this + would be for sure a syntax error in the config file, but we + got segfaults in the past and came not so far to report the + error. Hence, we have to check if the segfaults happen again. + Till then we leave the old code here in place. + --Hans, 980614 + */ + if (include_stack_ptr > 0 && include_fnames[include_stack_ptr]) +#else + if (include_fnames[include_stack_ptr]) +#endif + cf->origfname = strdup(include_fnames[include_stack_ptr]); + cf->parent = cachefilecurrent; + cachefilecurrent = cachefilewptr; +} + +static void close_cachefile_write(int stop) +{ + if (!cachefile || cachefilecurrent <0) + return; + cachefile[cachefilecurrent].stop = stop; + cachefilecurrent = cachefile[cachefilecurrent].parent; +} + + +static int cachefile_read(char *buf, int size, int cfile) +{ + struct cache_file *cf = cachefile+cfile; + + if (!cachefile || cachefilecurrent <0) return 0; + if (cf->rptr+size >= cf->stop) + size = cf->stop - cf->rptr; + if (size <= 0) return 0; + memcpy(buf, savebuf + cf->rptr, size); + cf->rptr += size; + return size; +} + + +static void enter_cachefile(int cfile) +{ + struct cache_file *cf; + + if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) { + yyerror("Loops nested too deeply" ); + return; + } + if (!cachefile) { + yyerror("mismatching loop begin/end \n"); + return; + } + cachefilecurrent = cfile; + if (config_check_only>1) + c_printf("CONF: opened cache file %d\n", cachefilecurrent); + include_lines[include_stack_ptr] = line_count; + include_stack[include_stack_ptr] = YY_CURRENT_BUFFER; + include_stack_ptr++; + cf = cachefile + cachefilecurrent; + cf->looplimit = LOOP_LIMIT; + cf->rptr = cf->start; + if (cf->origfname) + include_fnames[include_stack_ptr] = strdup(cf->origfname); + else include_fnames[include_stack_ptr] = 0; + line_count = cf->firstline; + dev_null_files[cachefilecurrent] = yyin = fopen("/dev/null", "re"); + include_stack[include_stack_ptr] = yy_create_buffer( yyin, cf->stop - cf->start +2); + yy_switch_to_buffer(include_stack[include_stack_ptr]); +} + + +static int cachefile_wrap(void) +{ + struct cache_file *cf; + int cfile; + + /* we come here from yywrap, when we got an EOF on the cache file */ + + if (include_stack_ptr <= 0 || !cachefile || cachefilecurrent <0) return 0; + cfile = cachefilecurrent; + cf = cachefile + cfile; + if (cf->need_loop_back) { + if (--cf->looplimit <0) { + yyerror("loop limit of %d loops exceeded\n", LOOP_LIMIT); + cf->need_loop_back = 0; + } + else { + cf->rptr = cf->start; + line_count = cf->firstline; + return(0); + } + } + cachefilecurrent = cf->parent; + yy_switch_to_buffer(include_stack[include_stack_ptr-1] ); + fclose(include_stack[include_stack_ptr]->yy_input_file); + dev_null_files[cfile] = NULL; + yy_delete_buffer(include_stack[include_stack_ptr]); + free(include_fnames[include_stack_ptr]); + if (config_check_only>1) + c_printf("CONF: closed cache file %d\n", cfile); + include_stack_ptr--; + line_count=include_lines[include_stack_ptr]; + if (cfile >= 0) { + /* when in inner loop, we have been executed from + * the copy, but the main cache file still is positioned + * directly behind the 'while__ ()' in the original. + * we have to skip this until 'done'. + * This can simply done by setting __active__=0, + * because the 'while__' has done an extra push__active() + * and the 'done' in the main cache file will do pop__active() + */ + __active__ = 0; + } + if (cachefilecurrent <0) { + /* end of loop handling */ + __loop_handling__ = 0; + free_cachefile_buffers(); + free_savebuffer(); + } + return(0); +} + +void tell_lexer_loop(int cfile, int value) +{ + __active__ = value !=0; + if (!cachefile) return; + if (__active__ && cachefilecurrent != cfile) { + /* we have to open a deeper nested cache file */ + enter_cachefile(cfile); + } + cachefile[cfile].need_loop_back = __active__; + return; +} + + +static int dump_input(int rulenum) +{ + static int lastchar = '\n'; + int savebufwptr_ = savebufwptr; + int skip_action = 1; + + switch (rulenum) { + case INCLUDE_RULE_NUM: { + dumpout("#",1); /* comment out the include statement*/ + skip_action = 0; + break; + } + } + + if (rulenum != COMMENT_RULE_NUM ) { + if (rulenum == WHITESPACE_RULE_NUM) { + if (lastchar != '\n') { + dumpout(" ",1); + lastchar = ' '; + } + } + else { + dumpout(yytext,yyleng); + lastchar = yytext[yyleng-1]; + } + } + else { + dumpout("#\n",2); + lastchar = '\n'; + line_count++; + } + switch (rulenum) { + case NEWLINE__RULE_NUM: { + line_count++; + break; + } + case INCLUDEFILE_RULE_NUM: { + if (__next_ITEM_for_me__ == 2) { + dumpout("\n",1); + lastchar = '\n'; + skip_action = 0; + } + break; + } + case LOOP_RULE_NUM: { + char buf[32]; + create_cachefile(savebufwptr_); + sprintf(buf, "__yy__ %d, ", cachefilecurrent); + dumpout(buf, strlen(buf)); + break; + } + case DONE_RULE_NUM: { + close_cachefile_write(savebufwptr); + if (cachefilecurrent <0) { + /* finished all caching, + * starting working phase */ + __loop_handling__ = -1; + enter_cachefile(0); + } + } + } + return skip_action; +} + +static void enter_macrofile(char *variable) +{ + char *macrobuffer; + if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) { + yyerror("macrocall ... nested too deeply" ); + return; + } + macrobuffer = checked_getenv(variable); + if (!macrobuffer) { + yywarn("macro '%s' not found \n", variable); + return; + } + if (config_check_only>1) + c_printf("CONF: opened macro file %s\n", variable); + include_lines[include_stack_ptr] = line_count; + include_stack[include_stack_ptr] = YY_CURRENT_BUFFER; + include_stack_ptr++; + include_macbuf[include_stack_ptr] = + include_fnames[include_stack_ptr] = strdup(macrobuffer); + dev_null_files[MACROFILE] = yyin = fopen("/dev/null", "re"); + include_stack[include_stack_ptr] = yy_create_buffer( yyin, strlen(macrobuffer) +2); + yy_switch_to_buffer(include_stack[include_stack_ptr]); +} + +static int macrofile_wrap(void) +{ + /* we come her from yywrap, when we got an EOF on the macro file */ + + if (include_stack_ptr <= 0) return 0; + yy_switch_to_buffer(include_stack[include_stack_ptr-1] ); + fclose(include_stack[include_stack_ptr]->yy_input_file); + dev_null_files[MACROFILE] = NULL; + yy_delete_buffer(include_stack[include_stack_ptr]); + free(include_macbuf[include_stack_ptr]); + if (config_check_only>1) + c_printf("CONF: closed macro file\n"); + include_stack_ptr--; + line_count=include_lines[include_stack_ptr]; + return(0); +} + + +static int macrofile_read(char *buf, int size) +{ + int len; + char *macroptr = include_fnames[include_stack_ptr]; + len = strlen(macroptr); + if (!len) return 0; + if (size > len) size = len; + memcpy(buf, macroptr, size); + include_fnames[include_stack_ptr] +=size; + return size; +} + +static int do_yy_input(char *buf, int max_size) +{ + int result; + if (!yyin) { + if (yy_vbuffer && yy_vbuffer[0]) { + buf[(max_size)-1]=0; + strncpy(buf,yy_vbuffer,max_size); + if (buf[(max_size)-1]) { + yy_vbuffer+=max_size; + result=max_size; + } + else { + result=strlen(buf); + yy_vbuffer=0; + } + } + else result=0; + } + else { + if ( YY_CURRENT_BUFFER->yy_is_interactive ) { + int c = getc( yyin ); + result = c == EOF ? 0 : 1; + buf[0] = (char) c; + } + else { + int cfile = cachefile_nr(yyin); + if (cfile < CACHEFILES_MAX) { + result = cachefile_read(buf, max_size, cfile); + } + else { + if (yyin == dev_null_files[MACROFILE]) { + result = macrofile_read(buf, max_size); + } + else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) && ferror( yyin ) ) + YY_FATAL_ERROR( "input in flex scanner failed" ); + } + } + } + return result; +} + +#endif /* USE_LOOP_HANDLING */ diff --git a/src/base/init/memcheck.c b/src/base/init/memcheck.c new file mode 100644 index 0000000..e743622 --- /dev/null +++ b/src/base/init/memcheck.c @@ -0,0 +1,310 @@ +#include +#include +#include + +#include "emu.h" +#include "utilities.h" +#include "memory.h" +#include "kvm.h" +#include "mapping.h" + +/* Notes: + * 1. leavedos() needs a real parameter + */ + +/* + * The point of these procedures is going to be to create a map of the + * available memory to facilitate: + * + * 1. check for memory conflicts (hardware page/ems page frame/etc.) + * 2. facilitate searching for frames (EMS frame, UMB blocks, etc.) + */ + +#define GRAN_SIZE 1024 /* Size of granularity in KB */ +#define MAX_PAGE (LOWMEM_SIZE/GRAN_SIZE) /* Number of 'pages' in memory */ + +static unsigned char mem_map[MAX_PAGE]; /* Map of memory contents */ +static const char *mem_names[256]; /* List of id. strings */ + +struct system_memory_map { + Bit32u base, hibase, length, hilength, type; +}; + +struct system_memory_map *system_memory_map; +size_t system_memory_map_size; + +static inline void round_addr(dosaddr_t *addr) +{ + *addr = (*addr + GRAN_SIZE - 1) / GRAN_SIZE; + *addr *= GRAN_SIZE; +} + +int memcheck_addtype(unsigned char map_char, const char *name) +{ + if (mem_names[map_char] != NULL) { + if (strcmp(mem_names[map_char], name) != 0) { + error("CONF: memcheck, conflicting map type '%c' defined for '%s' \ +& '%s'\n", map_char, mem_names[map_char], name); + config.exitearly = 1; + } + else + c_printf("CONF: memcheck, map type '%c' re-defined for '%s'\n", + map_char, name); + } + else { + mem_names[map_char] = name; + } + return(0); +} + +int memcheck_map_reserve(unsigned char map_char, dosaddr_t addr_start, + uint32_t size) +{ + int cntr; + dosaddr_t addr_end; + + c_printf("CONF: reserving %uKb at 0x%5.5X for '%c' (%s)\n", size/1024, + addr_start, map_char, mem_names[map_char]); + + round_addr(&addr_start); + addr_end = addr_start + size; + round_addr(&addr_end); + + for (cntr = addr_start / GRAN_SIZE; + cntr < addr_end / GRAN_SIZE && cntr < MAX_PAGE; cntr++) { + if (mem_map[cntr]) { + if (cntr != addr_start / GRAN_SIZE) { + error("CONF: memcheck - Fatal error. Memory conflict!\n"); + error(" Memory at 0x%4.4X:0x0000 is mapped to both:\n", + (cntr * GRAN_SIZE) / 16); + error(" '%s' & '%s'\n", mem_names[map_char], + mem_names[mem_map[cntr]]); + memcheck_dump(); + config.exitearly = 1; + return -2; + } + if (mem_map[cntr] == map_char) { + dosemu_error("The memory type '%s' has " + "been mapped twice to the same location (0x%X)\n", + mem_names[map_char], addr_start); + return -2; + } + return -1; + } else { + mem_map[cntr] = map_char; + } + } + + return 0; +} + +void memcheck_e820_reserve(dosaddr_t addr_start, uint32_t size, int reserved) +{ + struct system_memory_map *entry; + int len = system_memory_map_size / sizeof(*system_memory_map); + int i; + + system_memory_map_size += sizeof(*system_memory_map); + system_memory_map = realloc(system_memory_map, system_memory_map_size); + for (i = 0; i < len; i++) { + if (addr_start < system_memory_map[i].base) + break; + } + if (i < len) + memmove(&system_memory_map[i + 1], &system_memory_map[i], + sizeof(*system_memory_map) * (len - i)); + entry = system_memory_map + i; + entry->base = addr_start; + entry->hibase = 0; + entry->length = size; + entry->hilength = 0; + entry->type = reserved + 1; +} + +void memcheck_reserve(unsigned char map_char, dosaddr_t addr_start, + uint32_t size) +{ + int err; + + err = memcheck_map_reserve(map_char, addr_start, size); + if (err) { + if (err == -1) { + dosemu_error("CONF: memcheck - Fatal error. Memory conflict! %c\n", + map_char); + config.exitearly = 1; + } + return; + } + /* 'r' is dosemu heap. It is normally reserved, but fdpp can drill + * the UMB holes in it, so mark as available. */ + memcheck_e820_reserve(addr_start, size, + !(map_char == 'd' || map_char == 'U' || map_char == 'X' || + map_char == 'H' || map_char == 'r')); + + if (memcheck_is_rom(addr_start)) + mprotect_mapping(MAPPING_LOWMEM, addr_start, size, PROT_READ | PROT_EXEC); +} + +void memcheck_map_free(unsigned char map_char) +{ + int i; + + c_printf("CONF: freeing region for '%c' (%s)\n", + map_char, mem_names[map_char]); + + for (i = 0; i < MAX_PAGE; i++) { + if (mem_map[i] == map_char) + mem_map[i] = 0; + } +} + +void memcheck_type_init(void) +{ + static int once = 0; + if (once) return; + once = 1; + memcheck_addtype('d', "Base DOS memory (first 640K)"); + memcheck_addtype('r', "Dosemu reserved area"); + memcheck_addtype('b', "BIOS"); + memcheck_addtype('h', "Direct-mapped hardware page frame"); + memcheck_addtype('v', "Video memory"); +} + +void memcheck_init(void) +{ + memcheck_type_init(); + memcheck_reserve('d', 0x00000, config.mem_size*1024); /* dos memory */ + memcheck_reserve('r', 0xF0000 + DOSEMU_LMHEAP_OFF, DOSEMU_LMHEAP_SIZE); + assert(DOSEMU_LMHEAP_OFF + DOSEMU_LMHEAP_SIZE == bios_data_start); + /* dosemu bios */ + memcheck_reserve('b', 0xF0000 + bios_data_start, DOSEMU_BIOS_SIZE()); +} + +int memcheck_isfree(dosaddr_t addr_start, uint32_t size) +{ + int cntr; + dosaddr_t addr_end; + + round_addr(&addr_start); + addr_end = addr_start + size; + round_addr(&addr_end); + + for (cntr = addr_start / GRAN_SIZE; cntr < addr_end / GRAN_SIZE; cntr++) { + if (mem_map[cntr]) + return FALSE; + } + return TRUE; +} + +int memcheck_is_reserved(dosaddr_t addr_start, uint32_t size, + unsigned char map_char) +{ + int cntr; + dosaddr_t addr_end; + + round_addr(&addr_start); + addr_end = addr_start + size; + round_addr(&addr_end); + + for (cntr = addr_start / GRAN_SIZE; cntr < addr_end / GRAN_SIZE; cntr++) { + if (mem_map[cntr] != map_char) { + error("memcheck type mismatch at 0x%x: %c %c\n", + cntr * GRAN_SIZE, mem_map[cntr], map_char); + return FALSE; + } + } + return TRUE; +} + +int memcheck_is_rom(dosaddr_t addr) +{ + round_addr(&addr); + if (addr >= LOWMEM_SIZE) + return 0; + return strchr("R", mem_map[addr / GRAN_SIZE]) != NULL; +} + +int memcheck_is_hardware_ram(dosaddr_t addr) +{ + round_addr(&addr); + if (addr >= LOWMEM_SIZE) + return 0; + return strchr("evh", mem_map[addr / GRAN_SIZE]) != NULL; +} + +int memcheck_is_system_ram(dosaddr_t addr) +{ + round_addr(&addr); + return !memcheck_is_rom(addr) && + !memcheck_is_hardware_ram(addr); +} + +int memcheck_findhole(dosaddr_t *start_addr, uint32_t min_size, + uint32_t max_size) +{ + int cntr; + + round_addr(start_addr); + + for (cntr = *start_addr/GRAN_SIZE; cntr < MAX_PAGE; cntr++) { + int cntr2, end_page; + + /* any chance of finding anything? */ + if ((MAX_PAGE - cntr) * GRAN_SIZE < min_size) + return 0; + + /* if something's already there, no go */ + if (mem_map[cntr]) + continue; + + end_page = cntr + (max_size / GRAN_SIZE); + if (end_page > MAX_PAGE) + end_page = MAX_PAGE; + + for (cntr2 = cntr+1; cntr2 < end_page; cntr2++) { + if (mem_map[cntr2]) { + if ((cntr2 - cntr) * GRAN_SIZE >= min_size) { + *start_addr = cntr * GRAN_SIZE; + return ((cntr2 - cntr) * GRAN_SIZE); + } else { + /* hole isn't big enough, skip to the next one */ + cntr = cntr2; + break; + } + } + } + } + return 0; +} + +void memcheck_dump(void) +{ + int cntr; + c_printf("CONF: Memory map dump:\n"); + for (cntr = 0; cntr < MAX_PAGE; cntr++) { + if (cntr % 64 == 0) + c_printf("0x%5.5X: ", cntr * GRAN_SIZE); + c_printf("%c", (mem_map[cntr]) ? mem_map[cntr] : '.'); + if (cntr % 64 == 63) + c_printf("\n"); + } + c_printf("\nKey:\n"); + for (cntr = 0; cntr < 256; cntr++) { + if (mem_names[cntr]) + c_printf("%c: %s\n", cntr, mem_names[cntr]); + } + c_printf(".: (unused)\n"); + c_printf("CONF: End dump\n"); +} + +#if 0 +void *lowmemp(const unsigned char *ptr) +{ + dosaddr_t addr = DOSADDR_REL(ptr); +#ifdef __x86_64__ + if (addr > 0xffffffff) + return (void *)ptr; +#endif + return dosaddr_to_unixaddr(addr); +} +#endif diff --git a/src/base/init/parser.y b/src/base/init/parser.y new file mode 100644 index 0000000..f9980e2 --- /dev/null +++ b/src/base/init/parser.y @@ -0,0 +1,3137 @@ +/* parser.y + * + * Parser version 1 ... before 0.66.5 + * Parser version 2 at state of 0.66.5 97/05/30 + * Parser version 3 at state of 0.97.0.1 98/01/03 + * + * Note: starting with version 2, you may protect against version 3 via + * + * ifdef parser_version_3 + * # version 3 style parser + * else + * # old style parser + * endif + * + * Note2: starting with version 3 you _need_ atleast _one_ statement such as + * + * $XYZ = "something" + * + * to make the 'new version style check' happy, else dosemu will abort. + */ + +/* Merged parser stuff for keyboard plugin + */ + +/* Merged parser stuff for translate plugin + */ + +%{ + +#define YYDEBUG 0 + +#define PARSER_VERSION_STRING "parser_version_3" + +#include +#include +#include +#include +#include +#include +#include +#include /* structure stat */ +#include /* prototype for stat() */ +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif + +#include "dosemu_config.h" +#include "emu.h" +#ifdef X86_EMULATOR +#include "cpu-emu.h" +#endif +#include "disks.h" +#include "port.h" +#define allow_io port_allow_io +#include "mmio_tracing.h" +#include "lpt.h" +#include "video.h" +#include "vc.h" +#include "mouse.h" +#include "serial.h" +#include "timers.h" +#include "keyboard/keymaps.h" +#include "keyboard/keyb_server.h" +#include "translate/dosemu_charset.h" +#include "memory.h" +#include "mapping.h" +#include "utilities.h" +#include "aspi.h" +#include "int.h" +#include "pktdrvr.h" +#include "redirect.h" +#include "iodev.h" /* for TM_BIOS / TM_PIT / TM_LINUX */ + +#define USERVAR_PREF "dosemu_" + +static serial_t *sptr; +static serial_t nullser; +static mouse_t *mptr = &config.mouse; +static int c_ser = 0; + +static struct disk *dptr; +static struct disk nulldisk; +#define c_hdisks config.hdisks +#define c_fdisks config.fdisks +static int skipped_disks; + +static struct printer nullprt; +static struct printer *pptr = &nullprt; +static int c_printers = 0; + +static int ports_permission = IO_RDWR; +static unsigned int ports_ormask = 0; +static unsigned int ports_andmask = 0xFFFF; +static unsigned int portspeed = 0; + +static int errors = 0; +static int warnings = 0; + +static char *file_being_parsed; + + /* this to ensure we are parsing a new style */ +static int parser_version_3_style_used = 0; +#define CONFNAME_V3USED "version_3_style_used" + + /* local procedures */ + +static void start_ports(void); +static void start_mouse(void); +static void stop_mouse(void); +static void start_debug(void); +static void start_video(void); +static void stop_video(void); +static void set_vesamodes(int width, int height, int color_bits); +static int detect_vbios_seg(void); +static int detect_vbios_size(void); +static void stop_ttylocks(void); +static void start_serial(void); +static void stop_serial(void); +static void start_printer(void); +static void stop_printer(void); +static void start_keyboard(void); +static void keytable_start(char *layout); +static void keytable_stop(void); +static void keyb_layout(char *layout); +static void dump_keytables_to_file(char *name); +static void stop_terminal(void); +static void start_disk(void); +static void do_part(char *); +static void start_floppy(void); +static void stop_disk(int token); +static void start_vnet(char *); +static FILE* open_file(const char* filename); +static void close_file(FILE* file); +static void set_irq_value(int bits, int i1); +static void set_irq_range(int bits, int i1, int i2); +static int undefine_config_variable(const char *name); +static void check_user_var(char *name); +static char *run_shell(char *command); +static int for_each_handling(int loopid, char *varname, char *delim, char *list); +static void handle_features(int which, int value); +static void set_joy_device(char *devstring); +static int parse_timemode(const char *); +static void set_hdimage(struct disk *dptr, char *name); +static void set_drive_c(void); +static void set_default_drives(void); +static void set_dosemu_drive(void); +static void set_hostfs_drives(char *drivespec); + +#define TOF(x) ( x.type == TYPE_REAL ? x.value.r : x.value.i ) +#define V_VAL(x,y,z) \ +do { x.type = y.type; if (x.type == TYPE_REAL) x.value.r = (z); \ + else x.value.i = (z); } \ +while (0) + + /* variables in lexer.l */ + + + + + /* for unicode keyboard plugin */ +static void keyb_mod(int wich, t_keysym keynum, int unicode); +static void dump_keytable_part(FILE *f, t_keysym *map, int size); + +enum { + TYPE_NONE, + TYPE_INTEGER, + TYPE_BOOLEAN, + TYPE_REAL +} _type; + +%} + + + +%start lines + +%union { + int i_value; + char *s_value; + float r_value; + struct { + int type; + union { + int i; + float r; + } value; + } t_value; +}; + +%{ +#include "lexer.h" +%} + +%token INTEGER L_OFF L_ON L_AUTO L_YES L_NO CHIPSET_TYPE +%token REAL +%token STRING VARIABLE + + /* needed for expressions */ +%token EXPRTEST +%token INTCAST REALCAST +%left AND_OP OR_OP XOR_OP SHR_OP SHL_OP +%right NOT_OP /* logical NOT */ +%left EQ_OP GE_OP LE_OP '=' '<' '>' NEQ_OP +%left STR_EQ_OP STR_NEQ_OP +%left L_AND_OP L_OR_OP +%left '+' '-' +%left '*' '/' +%right UMINUS UPLUS BIT_NOT_OP + +%token STRLEN STRTOL STRNCMP STRCAT STRPBRK STRSPLIT STRCHR STRRCHR STRSTR +%token STRDEL STRSPN STRCSPN SHELL +%token DEFINED +%type expression int_expr bool_expr +%type real_expression real_expr +%type typed_expr +%type string_unquoted string_expr variable_content strarglist strarglist_item + + /* flow control */ +%token DEFINE UNDEF IFSTATEMENT WHILESTATEMENT FOREACHSTATEMENT + + /* variable handling */ +%token CHECKUSERVAR + + /* main options */ +%token FASTFLOPPY HOGTHRESH SPEAKER IPXSUPPORT IPXNETWORK NOVELLHACK +%token ETHDEV TAPDEV VDESWITCH SLIRPARGS NETSOCK VNET +%token DEBUG MOUSE SERIAL COM KEYBOARD TERMINAL VIDEO EMURETRACE TIMER +%token MATHCO CPU CPUSPEED BOOTDRIVE SWAP_BOOTDRIVE +%token L_XMS L_DPMI DPMI_BASE PM_DOS_API NO_NULL_CHECKS +%token PORTS DISK DOSMEM EXT_MEM +%token L_EMS UMB_A0 UMB_B0 UMB_B8 UMB_F0 HMA DOS_UP +%token EMS_SIZE EMS_FRAME EMS_UMA_PAGES EMS_CONV_PAGES +%token TTYLOCKS L_SOUND L_SND_OSS L_JOYSTICK FILE_LOCK_LIMIT +%token ABORT WARN ERROR +%token L_FLOPPY EMUSYS L_X L_SDL +%token DOSEMUMAP LOGBUFSIZE LOGFILESIZE MAPPINGDRIVER +%token LFN_SUPPORT FFS_REDIR SET_INT_HOOKS TRACE_IRETS FINT_REVECT + /* speaker */ +%token EMULATED NATIVE + /* cpuemu */ +%token CPUEMU CPU_VM CPU_VM_DPMI VM86 KVM + /* keyboard */ +%token RAWKEYBOARD +%token PRESTROKE +%token KEYTABLE SHIFT_MAP ALT_MAP NUMPAD_MAP DUMP LAYOUT +%token DGRAVE DACUTE DCIRCUM DTILDE DBREVE DABOVED DDIARES DABOVER DDACUTE DCEDILLA DIOTA DOGONEK DCARON + /* ipx */ +%token NETWORK PKTDRIVER NE2K + /* lock files */ +%token DIRECTORY NAMESTUB BINARY + /* serial */ +%token BASE IRQ DEVICE CHARSET BAUDRATE VIRTUAL VMODEM PSEUDO RTSCTS LOWLAT +%token PCCOM EXEC PTS WRFILE NULLMM + /* mouse */ +%token MICROSOFT MS3BUTTON LOGITECH MMSERIES MOUSEMAN HITACHI MOUSESYSTEMS BUSMOUSE PS2 IMPS2 +%token INTERNALDRIVER EMULATE3BUTTONS CLEARDTR UNGRAB_TWEAK + /* x-windows */ +%token L_DISPLAY L_TITLE X_TITLE_SHOW_APPNAME ICON_NAME X_BLINKRATE X_SHARECMAP X_MITSHM X_FONT +%token X_FIXED_ASPECT X_ASPECT_43 X_LIN_FILT X_BILIN_FILT X_MODE13FACT +%token X_WINSIZE X_NOCLOSE X_NORESIZE +%token X_GAMMA X_FULLSCREEN VGAEMU_MEMSIZE VESAMODE X_LFB X_PM_INTERFACE X_MGRAB_KEY X_BACKGROUND_PAUSE + /* sdl */ +%token SDL_HWREND SDL_FONTS SDL_WCONTROLS SDL_CLIP_NATIVE + /* video */ +%token VGA MGA CGA EGA NONE CONSOLE GRAPHICS CHIPSET FULLREST PARTREST +%token MEMSIZE VBIOS_SIZE_TOK VBIOS_SEG VGAEMUBIOS_FILE VBIOS_FILE +%token VBIOS_COPY VBIOS_MMAP DUALMON +%token VBIOS_POST VGA_FONTS + +%token FORCE_VT_SWITCH PCI + /* terminal */ +%token COLOR ESCCHAR XTERM_TITLE SIZE + /* debug */ +%token IO PORT CONFIG READ WRITE KEYB PRINTER WARNING GENERAL HARDWARE +%token L_IPC SOUND +%token TRACE CLEAR +%token TRACE_MMIO +%token UEXEC LPATHS HDRIVES + + /* printer */ +%token LPT COMMAND TIMEOUT L_FILE + /* disk */ +%token L_PARTITION WHOLEDISK +%token SECTORS CYLINDERS TRACKS HEADS OFFSET HDIMAGE HDTYPE1 HDTYPE2 HDTYPE9 DISKCYL4096 + /* floppy */ +%token THREEINCH THREEINCH_720 THREEINCH_2880 FIVEINCH FIVEINCH_360 READONLY BOOT +%token DEFAULT_DRIVES SKIP_DRIVES + /* ports/io */ +%token RDONLY WRONLY RDWR ORMASK ANDMASK RANGE FAST SLOW + /* Silly interrupts */ +%token SILLYINT USE_SIGIO + /* hardware ram mapping */ +%token HARDWARE_RAM + /* Sound Emulation */ +%token SB_BASE SB_IRQ SB_DMA SB_HDMA MPU_BASE MPU_BASE_MT32 +%token MPU_IRQ MPU_IRQ_MT32 MIDI_SYNTH +%token SOUND_DRIVER MIDI_DRIVER FLUID_SFONT FLUID_VOLUME +%token MUNT_ROMS OPL2LPT_DEV OPL2LPT_TYPE +%token SND_PLUGIN_PARAMS PCM_HPF MIDI_FILE WAV_FILE + /* CD-ROM */ +%token CDROM + /* ASPI driver */ +%token ASPI DEVICETYPE TARGET + /* features */ +%token FEATURE + /* joystick */ +%token JOYSTICK JOY_DEVICE JOY_DOS_MIN JOY_DOS_MAX JOY_GRANULARITY JOY_LATENCY + /* Hacks */ +%token CLI_TIMEOUT TIMEMODE TIMER_TWEAKS + + /* we know we have 1 shift/reduce conflict :-( + * and tell the parser to ignore that */ + /* %expect 1 */ + +%type int_bool irq_bool bool speaker floppy_bool +%type cpu_vm cpu_vm_dpmi + + /* special bison declaration */ +%token UNICODE +%token SHIFT_ALT_MAP CTRL_MAP CTRL_ALT_MAP + + + /* charset */ +%token INTERNAL EXTERNAL + +%% + +lines : + | lines line + | lines optdelim line + ; + +optdelim : ';' + | optdelim ';' + ; + +line: CHARSET '{' charset_flags '}' {} + /* charset flags */ + | HOGTHRESH expression { config.hogthreshold = $2; } + | DEFINE string_unquoted{ define_config_variable($2); free($2); } + | UNDEF string_unquoted { undefine_config_variable($2); free($2); } + | IFSTATEMENT '(' expression ')' { + /* NOTE: + * We _need_ absolutely to return to state stack 0 + * because we 'backward' modify the input stream for + * the parser (in lexer). Hence, _if_ the parser needs + * to read one token more than needed, we are lost, + * because we can't discard it. So please, don't + * play with the grammar without knowing what you do. + * The ')' _will_ return to state stack 0, but fiddling + * with brackets '()' in the underlaying 'expression' + * rules may distroy this. + * -- Hans 971231 + */ + tell_lexer_if($3); + } + /* Note: the below syntax of the while__yy__ statement + * is internal and _not_ visible out side. + * The visible syntax is: + * while ( expression ) + * + * done + */ + | WHILESTATEMENT INTEGER ',' '(' expression ')' { + tell_lexer_loop($2, $5); + } + | FOREACHSTATEMENT INTEGER ',' VARIABLE '(' string_expr ',' strarglist ')' { + tell_lexer_loop($2, for_each_handling($2,$4,$6,$8)); + free($4); free($6); free($8); + } + | SHELL '(' strarglist ')' { + char *s = run_shell($3); + if (s) free(s); + free($3); + } + | VARIABLE '=' strarglist { + if (!parser_version_3_style_used) { + parser_version_3_style_used = 1; + define_config_variable(CONFNAME_V3USED); + } + if ((strpbrk($1, "uhc") == $1) && ($1[1] == '_')) + yyerror("reserved variable %s can't be set\n", $1); + else + setenv($1, $3, 1); + free($1); free($3); + } + | CHECKUSERVAR check_user_var_list + | EXPRTEST typed_expr { + if ($2.type == TYPE_REAL) + c_printf("CONF TESTING: exprtest real %f\n", $2.value.r); + else + c_printf("CONF TESTING: exprtest int %d\n", $2.value.i); + } + /* abandoning 'single' abort due to shift/reduce conflicts + Just use ' abort "" ' + | ABORT { exit(99); } + */ + | ABORT strarglist + { if ($2[0]) fprintf(stderr,"CONF aborted with: %s\n", $2); + exit(99); + } + | ERROR strarglist { if ($2[0]) fprintf(stderr, "%s\n", $2); } + | WARN strarglist { warn("CONF: %s\n", $2); free($2); } + | EMUSYS string_expr + { + free(config.emusys); config.emusys = $2; + c_printf("CONF: config.emusys = '%s'\n", $2); + } + | EMUSYS '{' string_expr '}' + { + free(config.emusys); config.emusys = $3; + c_printf("CONF: config.emusys = '%s'\n", $3); + } + | DOSEMUMAP string_expr + { + free(dosemu_map_file_name); + dosemu_map_file_name = $2; + c_printf("CONF: dosemu.map path = '%s'\n", $2); + } + | MAPPINGDRIVER string_expr + { + free(config.mappingdriver); config.mappingdriver = $2; + c_printf("CONF: mapping driver = '%s'\n", $2); + } + | FILE_LOCK_LIMIT INTEGER + { + config.file_lock_limit = $2; + } + | LFN_SUPPORT bool + { + config.lfn = ($2!=0); + } + | FINT_REVECT bool + { + config.force_revect = ($2 == -2 ? 1 : $2); + } + | SET_INT_HOOKS bool + { + config.int_hooks = ($2 == -2 ? 1 : $2); + } + | TRACE_IRETS bool + { + config.trace_irets = ($2 == -2 ? 1 : $2); + } + | FFS_REDIR bool + { + config.force_redir = ($2!=0); + } + | FASTFLOPPY floppy_bool + { + config.fastfloppy = ($2!=0); + c_printf("CONF: fastfloppy = %d\n", config.fastfloppy); + } + | CPU expression + { + int cpu = cpu_override (($2%100)==86?($2/100)%10:0); + if (cpu > 0) { + c_printf("CONF: CPU set to %d86\n",cpu); + vm86s.cpu_type = cpu; + } + else + yyerror("error in CPU user override\n"); + } + | CPU_VM cpu_vm + { + config.cpu_vm = $2; + c_printf("CONF: CPU VM set to %d\n", config.cpu_vm); + } + | CPU_VM_DPMI cpu_vm_dpmi + { + config.cpu_vm_dpmi = $2; + c_printf("CONF: CPU VM set to %d for DPMI\n", + config.cpu_vm_dpmi); + } + | CPUEMU INTEGER + { +#ifdef X86_EMULATOR + config.cpusim = $2; + c_printf("CONF: CPUEMU set to %s\n", + config.cpusim ? "sim" : "jit"); +#endif + } + | CPUSPEED real_expression + { +#if 0 /* no longer used, but left in for dosemu.conf compatibility */ + if (config.realcpu >= CPU_586) { + config.cpu_spd = ((double)LLF_US)/$2; + config.cpu_tick_spd = ((double)LLF_TICKS)/$2; + c_printf("CONF: CPU speed = %g\n", ((double)$2)); + } +#endif + } + | CPUSPEED INTEGER INTEGER + { +#if 0 /* no longer used, but left in for dosemu.conf compatibility */ + if (config.realcpu >= CPU_586) { + config.cpu_spd = (LLF_US*$3)/$2; + config.cpu_tick_spd = (LLF_TICKS*$3)/$2; + c_printf("CONF: CPU speed = %d/%d\n", $2, $3); + } +#endif + } + | PCI bool + { + config.pci_video = ($2!=0); + config.pci = (abs($2)==2); + } + | BOOTDRIVE string_expr + { + if ($2[0] == 0) { + config.hdiskboot = -1; + } else if ($2[0] >= 'a') { + config.hdiskboot = $2[0] - 'a'; + } else { + error("wrong value for $_bootdrive\n"); + config.hdiskboot = -1; + } + free($2); + } + | SWAP_BOOTDRIVE bool + { + config.swap_bootdrv = ($2!=0); + } + | DEFAULT_DRIVES int_expr + { + c_printf("default_drives %i\n", $2); + switch ($2) { + case 0: + set_drive_c(); + break; + case 1: + set_dosemu_drive(); + set_default_drives(); + break; + case 2: + set_dosemu_drive(); + break; + default: + error("Path group %i not implemented\n", $2); + exit(1); + } + } + | SKIP_DRIVES int_expr + { + c_printf("skip %i drives from %i\n", $2, c_hdisks); + config.drives_mask |= ((1 << $2) - 1) << (c_hdisks + + skipped_disks + 2); + skipped_disks += $2; + } + | TIMER expression + { + config.freq = $2; + if ($2) { + config.update = 1000000 / $2; + } else { + config.update = 54925; + config.freq = 18; + } + c_printf("CONF: timer freq=%d, update=%d\n",config.freq,config.update); + } + | LOGBUFSIZE expression + { + char *b; + flush_log(); + b = malloc($2+1024); + if (!b) { + error("cannot get logbuffer\n"); + exit(1); + } + logptr = logbuf = b; + logbuf_size = $2; + } + | LOGFILESIZE expression + { + logfile_limit = $2; + } + | EMURETRACE bool + { + config.emuretrace = ($2!=0); + c_printf("CONF: emu_retrace %s\n", ($2) ? "on" : "off"); + } + | L_EMS '{' ems_flags '}' + | L_EMS int_bool + { + if ($2 >= 0) config.ems_size = $2; + if ($2 > 0) c_printf("CONF: %dk bytes EMS memory\n", $2); + } + | UMB_A0 bool + { + config.umb_a0 = $2; + if ($2 > 0) c_printf("CONF: umb at 0a0000: %s\n", ($2) ? "on" : "off"); + } + | UMB_B0 bool + { + config.umb_b0 = $2; + if ($2 > 0) c_printf("CONF: umb at 0b0000: %s\n", ($2) ? "on" : "off"); + } + | UMB_B8 bool + { + config.umb_b8 = $2; + if ($2 > 0) c_printf("CONF: umb at 0b8000: %s\n", ($2) ? "on" : "off"); + } + | UMB_F0 bool + { + config.umb_f0 = ($2!=0); + if ($2 > 0) c_printf("CONF: umb at 0f0000: %s\n", ($2) ? "on" : "off"); + } + | HMA bool + { + config.hma = ($2!=0); + if ($2 > 0) c_printf("CONF: HMA is: %s\n", ($2) ? "on" : "off"); + } + | DOS_UP bool + { + config.dos_up = $2; + if ($2 > 0) c_printf("CONF: dos_up: %s\n", ($2) ? "on" : "off"); + } + | L_DPMI int_bool + { + if ($2>=0) config.dpmi = $2; + c_printf("CONF: DPMI-Server %s (%#x)\n", ($2) ? "on" : "off", ($2)); + } + | DPMI_BASE int_bool + { + config.dpmi_base = $2; + c_printf("CONF: DPMI base addr = %#x\n", $2); + } + | PM_DOS_API bool + { + config.pm_dos_api = ($2!=0); + c_printf("CONF: PM DOS API Translator %s\n", ($2) ? "on" : "off"); + } + | NO_NULL_CHECKS bool + { + config.no_null_checks = ($2!=0); + c_printf("CONF: No DJGPP NULL deref checks: %s\n", ($2) ? "on" : "off"); + } + | DOSMEM int_bool { if ($2>=0) config.mem_size = $2; } + | EXT_MEM int_bool + { + if ($2>=0) config.ext_mem = $2; + if ($2 > 0) c_printf("CONF: %dk bytes int15 ext memory\n", $2); + } + | L_XMS int_bool + { + if ($2>=0) config.xms_size = $2; + if ($2 > 0) c_printf("CONF: %dk bytes XMS memory\n", $2); + } + | MATHCO bool { config.mathco = ($2!=0); } + | IPXSUPPORT bool + { + config.ipxsup = ($2!=0); + c_printf("CONF: IPX support %s\n", ($2) ? "on" : "off"); + } + | IPXNETWORK int_bool { config.ipx_net = $2; } + | PKTDRIVER bool + { + config.pktdrv = ($2!=0); + c_printf("CONF: Packet Driver %s.\n", + ($2) ? "enabled" : "disabled"); + } + | NE2K bool + { + config.ne2k = ($2!=0); + c_printf("CONF: NE2000 %s.\n", + ($2) ? "enabled" : "disabled"); + } + | ETHDEV string_expr { free(config.ethdev); config.ethdev = $2; } + | TAPDEV string_expr { free(config.tapdev); config.tapdev = $2; } + | VDESWITCH string_expr { free(config.vdeswitch); config.vdeswitch = $2; } + | SLIRPARGS string_expr { free(config.slirp_args); config.slirp_args = $2; } + | NETSOCK string_expr { free(config.netsock); config.netsock = $2; } + | NOVELLHACK bool { config.pktflags = ($2!=0); } + | VNET string_expr { start_vnet($2); free($2); } + | SPEAKER speaker + { + if ($2 == SPKR_NATIVE) { + if (can_do_root_stuff) { + c_printf("CONF: allowing speaker port access!\n"); + } else { + c_printf("CONF: native speaker not allowed: emulate\n"); + $2 = SPKR_EMULATED; + } + } + else + c_printf("CONF: not allowing speaker port access\n"); + config.speaker = $2; + } + | VIDEO + { start_video(); } + '{' video_flags '}' + { stop_video(); } + | VGA_FONTS bool + { config.vga_fonts = ($2!=0); } + | XTERM_TITLE string_expr { free(config.xterm_title); config.xterm_title = $2; } + | TERMINAL + '{' term_flags '}' + { stop_terminal(); } + | DEBUG strarglist { + parse_debugflags($2, 1); + free($2); + } + | DEBUG + { start_debug(); } + '{' debug_flags '}' + | MOUSE + { start_mouse(); } + '{' mouse_flags '}' + { stop_mouse(); } + | TTYLOCKS + '{' ttylocks_flags '}' + { stop_ttylocks(); } + | SERIAL + { start_serial(); } + '{' serial_flags '}' + { stop_serial(); } + | KEYBOARD + { start_keyboard(); } + '{' keyboard_flags '}' + | KEYTABLE string_expr + {keytable_start($2); free($2);} + '{' keyboard_mods '}' + {keytable_stop();} + | PRESTROKE string_expr + { + append_pre_strokes($2); + c_printf("CONF: appending pre-strokes '%s'\n", $2); + free($2); + } + | KEYTABLE DUMP string_expr { + dump_keytables_to_file($3); + free($3); + } + | PORTS + { start_ports(); } + '{' port_flags '}' + | TRACE PORTS '{' trace_port_flags '}' + | TRACE_MMIO + { config.mmio_tracing = 1; } + '{' trace_mmio_flags '}' + | DISK + { start_disk(); } + '{' disk_type disk_flags '}' + { stop_disk(DISK); } + | L_FLOPPY + { start_floppy(); } + '{' floppy_flags '}' + { stop_disk(L_FLOPPY); } + | CDROM '{' string_expr '}' + { + static int which = 0; + if (which >= 3) { + c_printf("CONF: too many cdrom drives defined\n"); + free($3); + } + else { + Path_cdrom[which] = $3; + c_printf("CONF: cdrom MSCD000%d on %s\n", which+1 ,$3); + which++; + } + } + | ASPI '{' string_expr DEVICETYPE string_expr TARGET expression '}' + { + char *s = aspi_add_device($3, $5, $7); + if (s) { + c_printf("CONF: aspi available for %s\n", s); + free(s); + } + else c_printf("CONF: aspi device %s not available\n", $3); + free($3); + free($5); + } + | PRINTER + { start_printer(); } + '{' printer_flags '}' + { stop_printer(); } + | L_X '{' x_flags '}' + | L_SDL '{' sdl_flags '}' + | SOUND bool { config.sound = ($2!=0); } + | L_SOUND '{' sound_flags '}' + | L_JOYSTICK bool { if (! $2) { config.joy_device[0] = config.joy_device[1] = NULL; } } + | L_JOYSTICK '{' joystick_flags '}' + | SILLYINT + { config.sillyint=0; } + '{' sillyint_flags '}' + | SILLYINT irq_bool + { if ($2) { + config.sillyint = 1 << $2; + c_printf("CONF: IRQ %d for irqpassing\n", $2); + } + } + | HARDWARE_RAM + '{' hardware_ram_flags '}' + | FEATURE '{' expression '=' expression '}' + { + handle_features($3, $5); + } + | CLI_TIMEOUT int_bool + { config.cli_timeout = $2; } + | TIMEMODE string_expr + { + config.timemode = parse_timemode($2); + c_printf("CONF: time mode = '%s'\n", $2); + free($2); + } + | TIMER_TWEAKS bool + { config.timer_tweaks = ($2 != 0); } + | UEXEC string_expr + { free(config.unix_exec); config.unix_exec = $2; } + | LPATHS string_expr + { free(config.lredir_paths); config.lredir_paths = $2; } + | HDRIVES string_expr + { set_hostfs_drives($2); free($2); } + | STRING + { yyerror("unrecognized command '%s'", $1); free($1); } + | error + ; + +expression: typed_expr { $$ = TOF($1); } + ; + +real_expression:typed_expr { $$ = TOF($1); } + ; + + /* run-time typed expressions */ + +typed_expr: int_expr {$$.type = TYPE_INTEGER; $$.value.i=$1;} + | bool_expr {$$.type = TYPE_BOOLEAN; $$.value.i=$1;} + | real_expr {$$.type = TYPE_REAL; $$.value.r=$1;} + | typed_expr '+' typed_expr + {V_VAL($$,$1,TOF($1) + TOF($3)); } + | typed_expr '-' typed_expr + {V_VAL($$,$1,TOF($1) - TOF($3)); } + | typed_expr '*' typed_expr + {V_VAL($$,$1,TOF($1) * TOF($3)); } + | typed_expr '/' typed_expr { + if (TOF($3)) V_VAL($$,$1,TOF($1) / TOF($3)); + else V_VAL($$,$1,TOF($3)); + } + | '-' typed_expr %prec UMINUS + {V_VAL($$,$2,-TOF($2)); } + | '+' typed_expr %prec UPLUS + {V_VAL($$,$2,TOF($2)); } + | typed_expr SHR_OP typed_expr + { unsigned int shift = (1 << (int)TOF($3)); + if (!shift) $$ = $1; + else V_VAL($$, $1, TOF($1) / shift);} + | typed_expr SHL_OP typed_expr + { unsigned int shift = (1 << (int)TOF($3)); + if (!shift) $$ = $1; + else V_VAL($$, $1, TOF($1) * shift);} + | variable_content { + char *s; + $$.type = TYPE_INTEGER; + $$.value.i = strtoul($1,&s,0); + switch (*s) { + case '.': case 'e': case 'E': + /* we assume a real number */ + $$.type = TYPE_REAL; + $$.value.r = strtod($1,0); + } + free($1); + } + | '(' typed_expr ')' {$$ = $2;} + ; + +int_expr: INTEGER + | typed_expr AND_OP typed_expr + {$$ = (int)TOF($1) & (int)TOF($3); } + | typed_expr OR_OP typed_expr + {$$ = (int)TOF($1) | (int)TOF($3); } + | typed_expr XOR_OP typed_expr + {$$ = (int)TOF($1) ^ (int)TOF($3); } + | BIT_NOT_OP typed_expr %prec BIT_NOT_OP + {$$ = (int)TOF($2) ^ (-1); } + | L_AUTO {$$ = -1; } + | INTCAST '(' typed_expr ')' {$$ = TOF($3);} + | STRTOL '(' string_expr ')' { + $$ = strtol($3,0,0); + free($3); + } + | STRLEN '(' string_expr ')' { + $$ = strlen($3); + free($3); + } + | STRNCMP '(' string_expr ',' string_expr ',' expression ')' { + $$ = strncmp($3,$5,$7); + free($3); free($5); + } + | STRPBRK '(' string_expr ',' string_expr ')' { + char *s = strpbrk($3,$5); + if (s) $$ = s - $3; + else $$ = -1; + free($3); free($5); + } + | STRCHR '(' string_expr ',' string_expr ')' { + char *s = strchr($3,$5[0]); + if (s) $$ = s - $3; + else $$ = -1; + free($3); free($5); + } + | STRRCHR '(' string_expr ',' string_expr ')' { + char *s = strrchr($3,$5[0]); + if (s) $$ = s - $3; + else $$ = -1; + free($3); free($5); + } + | STRSTR '(' string_expr ',' string_expr ')' { + char *s = strstr($3,$5); + if (s) $$ = s - $3; + else $$ = -1; + free($3); free($5); + } + | STRSPN '(' string_expr ',' string_expr ')' { + $$ = strspn($3,$5); + free($3); free($5); + } + | STRCSPN '(' string_expr ',' string_expr ')' { + $$ = strcspn($3,$5); + free($3); free($5); + } + | STRING { + if ( $1[0] == '\'' && $1[1] && $1[2] == '\'' && !$1[3] ) + $$ = $1[1]; + else yyerror("unrecognized expression '%s'", $1); + free($1); + } + ; + +bool_expr: typed_expr EQ_OP typed_expr + {$$ = TOF($1) == TOF($3); } + | typed_expr NEQ_OP typed_expr + {$$ = TOF($1) != TOF($3); } + | typed_expr GE_OP typed_expr + {$$ = TOF($1) >= TOF($3); } + | typed_expr LE_OP typed_expr + {$$ = TOF($1) <= TOF($3); } + | typed_expr '<' typed_expr + {$$ = TOF($1) < TOF($3); } + | typed_expr '>' typed_expr + {$$ = TOF($1) > TOF($3); } + | typed_expr L_AND_OP typed_expr + {$$ = TOF($1) && TOF($3); } + | typed_expr L_OR_OP typed_expr + {$$ = TOF($1) || TOF($3); } + | string_expr STR_EQ_OP string_expr + {$$ = strcmp($1,$3) == 0; free($1); free($3); } + | string_expr STR_NEQ_OP string_expr + {$$ = strcmp($1,$3) != 0; free($1); free($3); } + | NOT_OP typed_expr {$$ = (TOF($2) ? 0:1); } + | L_YES {$$ = -2; } + | L_NO {$$ = 0; } + | L_ON {$$ = -2; } + | L_OFF {$$ = 0; } + | DEFINED '(' string_unquoted ')' { + $$ = get_config_variable($3) !=0; + free($3); + } + ; + +real_expr: REAL + | REALCAST '(' typed_expr ')' {$$ = TOF($3);} + ; + +variable_content: + VARIABLE { + const char *s = $1; + if (get_config_variable(s)) + s = "1"; + else if (strncmp("c_",s,2) + && strncmp("u_",s,2) + && strncmp("h_",s,2) ) { + s = checked_getenv(s); + if (!s) s = ""; + } + else + s = "0"; + $$ = strdup(s); + free($1); + } + ; + +string_unquoted:STRING { + $$ = $1; + if ($$[0] == '\'' || $$[0] == '\"') { + size_t len = strlen($1) - 2; + memmove($$, $$+1, len); + $$[len] = '\0'; + } + } + ; + +string_expr: string_unquoted + | STRCAT '(' strarglist ')' {$$ = $3; } + | STRSPLIT '(' string_expr ',' expression ',' expression ')' { + int i = $5; + int len = $7; + int slen = strlen($3); + if ((i >=0) && (i < slen) && (len > 0)) { + if ((i+len) > slen) len = slen - i; + $3[i+len] = 0; + $$ = strdup($3 + i); + } + else + $$ = strdup(""); + free($3); + } + | STRDEL '(' string_expr ',' expression ',' expression ')' { + int i = $5; + int len = $7; + int slen = strlen($3); + char *s = strdup($3); + if ((i >=0) && (i < slen) && (len > 0)) { + if ((i+len) > slen) s[i] = 0; + else memmove(s+i, s+i+len, slen-i-len+1); + } + free($3); + $$ = s; + } + | SHELL '(' strarglist ')' { + $$ = run_shell($3); + free($3); + } + | variable_content {$$ = $1;} + ; + +strarglist: strarglist_item + | strarglist ',' strarglist_item { + char *s = malloc(strlen($1)+strlen($3)+1); + strcpy(s, $1); + strcat(s, $3); + $$ = s; + free($1); free($3); + } + ; + +strarglist_item: string_expr + | '(' typed_expr ')' { + int ret; + if ($2.type == TYPE_REAL) { + ret = asprintf(&$$, "%g", $2.value.r); + assert(ret != -1); + } else { + ret = asprintf(&$$, "%d", $2.value.i); + assert(ret != -1); + } + } + ; + +check_user_var_list: + VARIABLE { + check_user_var($1); + free($1); + } + | check_user_var_list ',' VARIABLE { + check_user_var($3); + free($3); + } + ; + + /* x-windows */ + +x_flags : x_flag + | x_flags x_flag + ; +x_flag : L_DISPLAY string_expr { free(config.X_display); config.X_display = $2; } + | L_TITLE string_expr { free(config.X_title); config.X_title = $2; } + | X_TITLE_SHOW_APPNAME bool { config.X_title_show_appname = ($2!=0); } + | ICON_NAME string_expr { free(config.X_icon_name); config.X_icon_name = $2; } + | X_BLINKRATE expression { config.X_blinkrate = $2; } + | X_SHARECMAP { config.X_sharecmap = 1; } + | X_MITSHM { config.X_mitshm = 1; } + | X_MITSHM bool { config.X_mitshm = ($2!=0); } + | X_FONT string_expr { free(config.X_font); config.X_font = $2; } + | X_FIXED_ASPECT bool { config.X_fixed_aspect = ($2!=0); } + | X_ASPECT_43 { config.X_aspect_43 = 1; } + | X_LIN_FILT { config.X_lin_filt = 1; } + | X_BILIN_FILT { config.X_bilin_filt = 1; } + | X_MODE13FACT expression { config.X_mode13fact = $2; } + | X_WINSIZE INTEGER INTEGER + { + config.X_winsize_x = $2; + config.X_winsize_y = $3; + } + | X_WINSIZE expression ',' expression + { + config.X_winsize_x = $2; + config.X_winsize_y = $4; + } + | X_GAMMA expression { config.X_gamma = $2; } + | X_FULLSCREEN bool { config.X_fullscreen = $2; } + | X_NOCLOSE bool { config.X_noclose = ($2!=0); } + | X_NORESIZE bool { config.X_noresize = ($2!=0); } + | VGAEMU_MEMSIZE expression { config.vgaemu_memsize = $2; } + | VESAMODE INTEGER INTEGER { set_vesamodes($2,$3,0);} + | VESAMODE INTEGER INTEGER INTEGER { set_vesamodes($2,$3,$4);} + | VESAMODE expression ',' expression { set_vesamodes($2,$4,0);} + | VESAMODE expression ',' expression ',' expression + { set_vesamodes($2,$4,$6);} + | X_LFB bool { config.X_lfb = ($2!=0); } + | X_PM_INTERFACE bool { config.X_pm_interface = ($2!=0); } + | X_MGRAB_KEY string_expr { free(config.X_mgrab_key); config.X_mgrab_key = $2; } + | X_BACKGROUND_PAUSE bool { config.X_background_pause = ($2!=0); } + ; + + /* sdl */ +sdl_flags : sdl_flag + | sdl_flags sdl_flag + ; +sdl_flag : SDL_HWREND expression { config.sdl_hwrend = ($2!=0); } + | SDL_FONTS string_expr { free(config.sdl_fonts); config.sdl_fonts = $2; } + | SDL_WCONTROLS expression { config.sdl_wcontrols = ($2!=0); } + | SDL_CLIP_NATIVE bool { config.sdl_clip_native = ($2!=0); } + ; + + /* sb emulation */ + +sound_flags : sound_flag + | sound_flags sound_flag + ; +sound_flag : SB_BASE expression { config.sb_base = $2; } + | SB_DMA expression { config.sb_dma = $2; } + | SB_HDMA expression { config.sb_hdma = $2; } + | SB_IRQ expression { config.sb_irq = $2; } + | MPU_BASE expression { config.mpu401_base = $2; } + | MPU_BASE_MT32 expression { config.mpu401_base_mt32 = $2; } + | MIDI_SYNTH string_expr { free(config.midi_synth); config.midi_synth = $2; } + | MPU_IRQ int_bool { config.mpu401_irq = $2; } + | MPU_IRQ_MT32 expression { config.mpu401_irq_mt32 = $2; } + | SOUND_DRIVER string_expr { free(config.sound_driver); config.sound_driver = $2; } + | MIDI_DRIVER string_expr { free(config.midi_driver); config.midi_driver = $2; } + | FLUID_SFONT string_expr { free(config.fluid_sfont); config.fluid_sfont = $2; } + | FLUID_VOLUME expression { config.fluid_volume = $2; } + | MUNT_ROMS string_expr + { + free(config.munt_roms_dir); + config.munt_roms_dir = concat_dir(dosemu_localdir_path, $2); + free($2); + } + | OPL2LPT_DEV string_expr + { + free(config.opl2lpt_device); + config.opl2lpt_device = $2; + } + | OPL2LPT_TYPE string_expr + { + if (strlen($2) == 4 && isdigit($2[3])) + config.opl2lpt_type = atoi($2 + 3) - 2; + else + yyerror("invalid value %s\n", $2); + free($2); + } + | SND_PLUGIN_PARAMS string_expr { free(config.snd_plugin_params); config.snd_plugin_params = $2; } + | PCM_HPF bool { config.pcm_hpf = ($2!=0); } + | MIDI_FILE string_expr { free(config.midi_file); config.midi_file = $2; } + | WAV_FILE string_expr { free(config.wav_file); config.wav_file = $2; } + ; + + /* joystick emulation */ + +joystick_flags : joystick_flag + | joystick_flags joystick_flag + ; +joystick_flag : JOY_DEVICE string_expr { set_joy_device($2); } + | JOY_DOS_MIN expression { config.joy_dos_min = $2; } + | JOY_DOS_MAX expression { config.joy_dos_max = $2; } + | JOY_GRANULARITY expression { config.joy_granularity = $2; } + | JOY_LATENCY expression { config.joy_latency = $2; } + ; + + /* video */ + +video_flags : video_flag + | video_flags video_flag + ; +video_flag : VGA { config.cardtype = CARD_VGA; } + | MGA { config.cardtype = CARD_MDA; } + | CGA { config.cardtype = CARD_CGA; } + | EGA { config.cardtype = CARD_EGA; } + | NONE { config.cardtype = CARD_NONE; } + | CHIPSET CHIPSET_TYPE + { + config.chipset = $2; + c_printf("CHIPSET: %d\n", $2); + } + | MEMSIZE expression { config.gfxmemsize = $2; } + | GRAPHICS + { config.vga = 1; + } + | GRAPHICS L_AUTO + { config.vga = -1; + } + | CONSOLE + { config.console_video = 1; + } + | CONSOLE L_AUTO + { config.console_video = -1; + } + | FULLREST { config.fullrestore = 1; } + | PARTREST { config.fullrestore = 0; } + | VBIOS_FILE string_expr { free(config.vbios_file); config.vbios_file = $2; + config.mapped_bios = 1; + config.vbios_copy = 0; } + | VGAEMUBIOS_FILE string_expr { free(config.vgaemubios_file); config.vgaemubios_file = $2; } + | VBIOS_COPY { free(config.vbios_file); + config.vbios_file = NULL; + config.mapped_bios = 1; + config.vbios_copy = 1; } + | VBIOS_MMAP { free(config.vbios_file); + config.vbios_file = NULL; + config.mapped_bios = 1; + config.vbios_copy = 1; } + | VBIOS_SEG expression + { + config.vbios_seg = $2; + c_printf("CONF: VGA-BIOS-Segment %x\n", $2); + if (($2 != 0xe000) && ($2 != 0xc000)) + { + config.vbios_seg = detect_vbios_seg(); + if (config.vbios_seg == -1) config.vbios_seg = 0xc000; + c_printf("CONF: VGA-BIOS-Segment set to 0x%x\n", config.vbios_seg); + } + } + | VBIOS_SIZE_TOK expression + { + config.vbios_size = $2; + c_printf("CONF: VGA-BIOS-Size %x\n", $2); + if (($2 != 0x8000) && ($2 != 0x10000)) + { + config.vbios_size = detect_vbios_size(); + if (config.vbios_size == -1) config.vbios_size = 0x10000; + c_printf("CONF: VGA-BIOS-Size set to 0x%x\n", config.vbios_size); + } + } + | VBIOS_POST { config.vbios_post = 1; } + | DUALMON { config.dualmon = 1; } + | FORCE_VT_SWITCH { config.force_vt_switch = 1; } + | PCI { config.pci_video = 1; } + | STRING + { yyerror("unrecognized video option '%s'", $1); + free($1); } + | error + ; + + /* terminal */ + +term_flags : term_flag + | term_flags term_flag + ; +term_flag : ESCCHAR expression { config.term_esc_char = $2; } + | COLOR bool { config.term_color = ($2!=0); } + | SIZE string_expr { free(config.term_size); config.term_size = $2; } + | STRING + { yyerror("unrecognized terminal option '%s'", $1); + free($1); } + | error + ; + +/* method_val : FAST { $$ = METHOD_FAST; } */ +/* | NCURSES { $$ = METHOD_NCURSES; } */ +/* ; */ + + /* debugging */ + +debug_flags : debug_flag + | debug_flags debug_flag + ; +debug_flag : VIDEO bool { set_debug_level('v', ($2!=0)); } + | L_OFF { set_debug_level('a', 0); } + | SERIAL bool { set_debug_level('s', ($2!=0)); } + | CONFIG bool { set_debug_level('c', ($2!=0)); } + | DISK bool { set_debug_level('d', ($2!=0)); } + | READ bool { set_debug_level('R', ($2!=0)); } + | WRITE bool { set_debug_level('W', ($2!=0)); } + | KEYB bool { set_debug_level('k', ($2!=0)); } + | KEYBOARD bool { set_debug_level('k', ($2!=0)); } + | PRINTER bool { set_debug_level('p', ($2!=0)); } + | IO bool { set_debug_level('i', ($2!=0)); } + | PORT bool { set_debug_level('i', ($2!=0)); } + | WARNING bool { set_debug_level('w', ($2!=0)); } + | GENERAL bool { set_debug_level('g', ($2!=0)); } + | L_XMS bool { set_debug_level('X', ($2!=0)); } + | L_DPMI bool { set_debug_level('M', ($2!=0)); } + | MOUSE bool { set_debug_level('m', ($2!=0)); } + | HARDWARE bool { set_debug_level('h', ($2!=0)); } + | L_IPC bool { set_debug_level('I', ($2!=0)); } + | L_EMS bool { set_debug_level('E', ($2!=0)); } + | NETWORK bool { set_debug_level('n', ($2!=0)); } + | L_X bool { set_debug_level('X', ($2!=0)); } + | L_SDL bool { set_debug_level('v', ($2!=0)); } + | SOUND bool { set_debug_level('S', ($2!=0)); } + | JOYSTICK bool { set_debug_level('j', ($2!=0)); } + | STRING + { yyerror("unrecognized debug flag '%s'", $1); free($1); } + | error + ; + + /* mouse */ + +mouse_flags : mouse_flag + | mouse_flags mouse_flag + ; +mouse_flag : DEVICE string_expr { free(mptr->dev); mptr->dev = $2; } + | INTERNALDRIVER { mptr->intdrv = TRUE; } + | EMULATE3BUTTONS { mptr->emulate3buttons = TRUE; } + | BAUDRATE expression { mptr->baudRate = $2; } + | CLEARDTR + { if (mptr->dev_type == MOUSE_MOUSESYSTEMS) + mptr->cleardtr = TRUE; + else + yyerror("option CLEARDTR is only valid for MicroSystems-mice"); + } + | MICROSOFT + { + mptr->dev_type = MOUSE_MICROSOFT; + mptr->flags = CS7 | CREAD | CLOCAL | HUPCL; + } + | MS3BUTTON + { + mptr->dev_type = MOUSE_MS3BUTTON; + mptr->flags = CS7 | CREAD | CLOCAL | HUPCL; + } + | MOUSESYSTEMS + { + mptr->dev_type = MOUSE_MOUSESYSTEMS; + mptr->flags = CS8 | CREAD | CLOCAL | HUPCL; +/* is cstopb needed? mptr->flags = CS8 | CSTOPB | CREAD | CLOCAL | HUPCL; */ + } + | MMSERIES + { + mptr->dev_type = MOUSE_MMSERIES; + mptr->flags = CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL; + } + | LOGITECH + { + mptr->dev_type = MOUSE_LOGITECH; + mptr->flags = CS8 | CSTOPB | CREAD | CLOCAL | HUPCL; + } + | PS2 + { + mptr->dev_type = MOUSE_PS2; + mptr->flags = 0; + } + | IMPS2 + { + mptr->dev_type = MOUSE_IMPS2; + mptr->flags = 0; + } + | MOUSEMAN + { + mptr->dev_type = MOUSE_MOUSEMAN; + mptr->flags = CS7 | CREAD | CLOCAL | HUPCL; + } + | HITACHI + { + mptr->dev_type = MOUSE_HITACHI; + mptr->flags = CS8 | CREAD | CLOCAL | HUPCL; + } + | BUSMOUSE + { + mptr->dev_type = MOUSE_BUSMOUSE; + mptr->flags = 0; + } + | UNGRAB_TWEAK bool + { + mptr->ignore_speed = ($2!=0); + } + | STRING + { yyerror("unrecognized mouse flag '%s'", $1); free($1); } + | error + ; + + /* keyboard */ + +keyboard_flags : keyboard_flag + | keyboard_flags keyboard_flag + ; +keyboard_flag : LAYOUT string_expr { keyb_layout($2); free($2); } + | RAWKEYBOARD bool { config.console_keyb = $2; } + | STRING + { yyerror("unrecognized keyboard flag '%s'", $1); + free($1);} + | error + ; + +keyboard_mods : keyboard_mod + | keyboard_mods keyboard_mod + ; + +keyboard_mod : CTRL_MAP expression '=' { keyb_mod('C', $2, 0); } keyboard_modvals + | SHIFT_ALT_MAP expression '=' { keyb_mod('a', $2, 0); } keyboard_modvals + | CTRL_ALT_MAP expression '=' { keyb_mod('c', $2, 0); } keyboard_modvals + | expression '=' { keyb_mod(' ', $1, 0); } keyboard_modvals + | SHIFT_MAP expression '=' { keyb_mod('S', $2, 0); } keyboard_modvals + | ALT_MAP expression '=' { keyb_mod('A', $2, 0); } keyboard_modvals + | NUMPAD_MAP expression '=' { keyb_mod('N', $2, 0); } keyboard_modvals + ; + +keyboard_modvals: keyboard_modval + | keyboard_modvals ',' keyboard_modval + ; + +keyboard_modval : UNICODE { keyb_mod(0, $1, 1); } + | DGRAVE { keyb_mod(0, DKY_DEAD_GRAVE, 1); } + | DACUTE { keyb_mod(0, DKY_DEAD_ACUTE, 1); } + | DCIRCUM { keyb_mod(0, DKY_DEAD_CIRCUMFLEX, 1); } + | DTILDE { keyb_mod(0, DKY_DEAD_TILDE, 1); } + | DBREVE { keyb_mod(0, DKY_DEAD_BREVE, 1); } + | DABOVED { keyb_mod(0, DKY_DEAD_ABOVEDOT, 1); } + | DDIARES { keyb_mod(0, DKY_DEAD_DIAERESIS, 1); } + | DABOVER { keyb_mod(0, DKY_DEAD_ABOVERING, 1); } + | DDACUTE { keyb_mod(0, DKY_DEAD_DOUBLEACUTE, 1); } + | DCEDILLA { keyb_mod(0, DKY_DEAD_CEDILLA, 1); } + | DIOTA { keyb_mod(0, DKY_VOID, 1); } /* no dead iotas exist */ + | DOGONEK { keyb_mod(0,DKY_DEAD_OGONEK, 1); } + | DCARON { keyb_mod(0, DKY_DEAD_CARON, 1); } + | INTEGER { keyb_mod(0, $1, 0); } + | '(' expression ')' { keyb_mod(0, $2, 0); } + | string_unquoted { + char *p = $1; + while (*p) keyb_mod(0, *p++, 0); + free($1); + } + ; + + /* lock files */ + +ttylocks_flags : ttylocks_flag + | ttylocks_flags ttylocks_flag + ; +ttylocks_flag : DIRECTORY string_expr { free(config.tty_lockdir); config.tty_lockdir = $2; } + | NAMESTUB string_expr { free(config.tty_lockfile); config.tty_lockfile = $2; } + | BINARY { config.tty_lockbinary = TRUE; } + | STRING + { yyerror("unrecognized ttylocks flag '%s'", $1); free($1); } + | error + ; + + /* serial ports */ + +serial_flags : serial_flag + | serial_flags serial_flag + ; +serial_flag : DEVICE string_expr { free(sptr->dev); sptr->dev = $2; } + | VIRTUAL { + if (isatty(0)) { + sptr->virt = TRUE; + sptr->pseudo = TRUE; + no_local_video = 1; + sptr->dev = strdup(ttyname(0)); + config.dumb_video = 1; + if (!config.tty_stderr) // can be 2 + config.tty_stderr = 1; + } else { + error("FD 0 is not a tty, can't " + "use a virtual com port\n"); + exit(1); + } + } + | PSEUDO { sptr->pseudo = TRUE; } + | VMODEM { sptr->vmodem = TRUE; } + | RTSCTS { sptr->system_rtscts = TRUE; } + | LOWLAT { sptr->low_latency = TRUE; } + | PCCOM { sptr->custom = SER_CUSTOM_PCCOM; } + | COM expression { sptr->real_comport = $2; } + | BASE expression { sptr->base_port = $2; } + | IRQ expression { sptr->irq = $2; } + | MOUSE { sptr->mouse = 1; + config.num_serial_mices++; } + | EXEC string_expr { free(sptr->exec); + sptr->exec = $2; } + | PTS string_expr { free(sptr->pts); + sptr->pts = $2; } + | WRFILE string_expr { free(sptr->wrfile); + sptr->wrfile = $2; } + | NULLMM string_expr { sptr->nullmm = atoi($2); free($2); } + | READONLY { sptr->ro = 1; } + | STRING + { yyerror("unrecognized serial flag '%s'", $1); free($1); } + | error + ; + + /* printer */ + +printer_flags : printer_flag + | printer_flags printer_flag + ; +printer_flag : LPT expression { c_printers = $2 - 1; } + | COMMAND string_expr { free(pptr->prtcmd); pptr->prtcmd = $2; } + | TIMEOUT expression { pptr->delay = $2; } + | L_FILE string_expr { free(pptr->dev); pptr->dev = $2; } + | BASE expression { pptr->base_port = $2; } + | STRING + { yyerror("unrecognized printer flag %s", $1); free($1); } + | error + ; + + /* disks */ + +floppy_flags : floppy_flag + | floppy_flags floppy_flag + ; +floppy_flag : READONLY { dptr->rdonly = 1; } + | THREEINCH { dptr->default_cmos = THREE_INCH_FLOPPY; } + | THREEINCH_2880 { dptr->default_cmos = THREE_INCH_2880KFLOP; } + | THREEINCH_720 { dptr->default_cmos = THREE_INCH_720KFLOP; } + | FIVEINCH { dptr->default_cmos = FIVE_INCH_FLOPPY; } + | FIVEINCH_360 { dptr->default_cmos = FIVE_INCH_360KFLOP; } + | BOOT { dptr->boot = 1; } + | L_FLOPPY string_expr + { + struct stat st; + + if (dptr->dev_name != NULL) + yyerror("Two names for a floppy-device given."); + if (stat($2, &st) < 0) { + yyerror("Could not stat floppy device."); + } + if (S_ISREG(st.st_mode)) { + dptr->type = IMAGE; + } else if (S_ISBLK(st.st_mode)) { + dptr->type = FLOPPY; + } else if (S_ISDIR(st.st_mode)) { + dptr->type = DIR_TYPE; + } else { + yyerror("Floppy device/file %s is wrong type", $2); + } + free(dptr->dev_name); + dptr->dev_name = $2; + dptr->floppy = 1; // tell IMAGE and DIR we are a floppy + } + | DEVICE string_expr + { + if (dptr->dev_name != NULL) + yyerror("Two names for a disk-image file or device given."); + free(dptr->dev_name); + dptr->dev_name = $2; + } + | DIRECTORY string_expr + { + if (dptr->dev_name != NULL) + yyerror("Two names for a directory given."); + dptr->type = DIR_TYPE; + free(dptr->dev_name); + dptr->dev_name = $2; + } + | STRING + { yyerror("unrecognized floppy disk flag '%s'\n", $1); free($1); } + | error + ; + +disk_flags : + | disk_flags disk_flag + ; +disk_flag : READONLY { dptr->rdonly = 1; } + | DISKCYL4096 { dptr->diskcyl4096 = 1; } + | HDTYPE1 { dptr->hdtype = 1; } + | HDTYPE2 { dptr->hdtype = 2; } + | HDTYPE9 { dptr->hdtype = 9; } + | SECTORS expression { dptr->sectors = $2; } + | CYLINDERS expression { dptr->tracks = $2; } + | TRACKS expression { dptr->tracks = $2; } + | HEADS expression { dptr->heads = $2; } + | OFFSET expression { dptr->header = $2; } + | L_PARTITION { dptr->part_image = 1; } + | STRING + { yyerror("unrecognized disk flag '%s'\n", $1); free($1); } + | error + ; + +disk_type : HDIMAGE string_expr + { + if (dptr->dev_name != NULL) + yyerror("Two names for a harddisk-image file given."); + set_hdimage(dptr, $2); + } + | WHOLEDISK STRING + { + if (dptr->dev_name != NULL) + yyerror("Two names for a harddisk given."); + dptr->type = HDISK; + dptr->dev_name = $2; + } + | L_PARTITION string_expr INTEGER + { + yywarn("{ partition \"%s\" %d } the" + " token '%d' is ignored and can be removed.", + $2,$3,$3); + do_part($2); + } + | L_PARTITION string_expr + { do_part($2); } + | DIRECTORY string_expr + { + if (dptr->dev_name != NULL) + yyerror("Two names for a directory given."); + dptr->type = DIR_TYPE; + dptr->dev_name = $2; + } + | STRING + { yyerror("unrecognized disk type '%s'\n", $1); free($1); } + | error + ; + + /* i/o ports */ + +port_flags : port_flag + | port_flags port_flag + ; +port_flag : INTEGER + { + c_printf("CONF: I/O port 0x%04x\n", (unsigned short)$1); + allow_io($1, 1, ports_permission, ports_ormask, + ports_andmask, portspeed); + if (portspeed) portspeed += ((portspeed>0) ? 1 : -1); + } + | '(' expression ')' + { + allow_io($2, 1, ports_permission, ports_ormask, + ports_andmask, portspeed); + if (portspeed) portspeed += ((portspeed>0) ? 1 : -1); + } + | RANGE INTEGER INTEGER + { + if (portspeed > 1) portspeed = 0; + c_printf("CONF: range of I/O ports 0x%04x-0x%04x\n", + (unsigned short)$2, (unsigned short)$3); + allow_io($2, $3 - $2 + 1, ports_permission, ports_ormask, + ports_andmask, portspeed); + portspeed=0; + } + | RANGE expression ',' expression + { + if (portspeed > 1) portspeed = 0; + c_printf("CONF: range of I/O ports 0x%04x-0x%04x\n", + (unsigned short)$2, (unsigned short)$4); + allow_io($2, $4 - $2 + 1, ports_permission, ports_ormask, + ports_andmask, portspeed); + portspeed=0; + } + | RDONLY { ports_permission = IO_READ; } + | WRONLY { ports_permission = IO_WRITE; } + | RDWR { ports_permission = IO_RDWR; } + | ORMASK expression { ports_ormask = $2; } + | ANDMASK expression { ports_andmask = $2; } + | FAST { portspeed = 1; } + | SLOW { portspeed = -1; } + | DEVICE string_expr { /* compatibility */ free($2); } + | STRING + { yyerror("unrecognized port command '%s'", $1); + free($1); } + | error + ; + +trace_port_flags : trace_port_flag + | trace_port_flags trace_port_flag + ; +trace_port_flag : INTEGER + { register_port_traceing($1, $1); } + | '(' expression ')' + { register_port_traceing($2, $2); } + | RANGE INTEGER INTEGER + { register_port_traceing($2, $3); } + | RANGE expression ',' expression + { register_port_traceing($2, $4); } + | CLEAR { clear_port_traceing(); } + | STRING + { yyerror("unrecognized port trace command '%s'", $1); + free($1); } + | error + ; + +/* MMIO tracing */ + +trace_mmio_flags : trace_mmio_flag + | trace_mmio_flags trace_mmio_flag + ; +trace_mmio_flag : INTEGER + { register_mmio_tracing($1, $1); + c_printf("CONF: MMIO tracing registered for 0x%x\n", $1); } + | '(' expression ')' + { register_mmio_tracing($2, $2); + c_printf("CONF: MMIO tracing registered for 0x%x\n", $2); } + | RANGE INTEGER INTEGER + { register_mmio_tracing($2, $3); + c_printf("CONF: MMIO tracing registered for 0x%x-0x%x\n", $2, $3); } + | RANGE expression ',' expression + { register_mmio_tracing($2, $4); + c_printf("CONF: MMIO tracing registered for 0x%x-0x%x\n", $2, $4); } + | STRING + { yyerror("unrecognized mmio trace command '%s'", $1); + free($1); } + | error + ; + + /* IRQ definition for Silly Interrupt Generator */ + +sillyint_flags : sillyint_flag + | sillyint_flags sillyint_flag + ; +sillyint_flag : INTEGER { set_irq_value(1, $1); } + | '(' expression ')' { set_irq_value(1, $2); } + | USE_SIGIO expression { set_irq_value(0x10001, $2); } + | RANGE INTEGER INTEGER { set_irq_range(1, $2, $3); } + | RANGE expression ',' expression { set_irq_range(1, $2, $4); } + | USE_SIGIO RANGE INTEGER INTEGER { set_irq_range(0x10001, $3, $4); } + | USE_SIGIO RANGE expression ',' expression { set_irq_range(0x10001, $3, $5); } + | STRING + { yyerror("unrecognized irqpassing command '%s'", $1); + free($1); } + | error + ; + + /* EMS definitions */ + +ems_flags : ems_flag + | ems_flags ems_flag + ; +ems_flag : INTEGER + { + config.ems_size = $1; + if ($1 > 0) c_printf("CONF: %dk bytes EMS memory\n", $1); + } + | '(' expression ')' + { + config.ems_size = $2; + if ($2 > 0) c_printf("CONF: %dk bytes EMS memory\n", $2); + } + | EMS_SIZE expression + { + config.ems_size = $2; + if ($2 > 0) c_printf("CONF: %dk bytes EMS memory\n", $2); + } + | EMS_FRAME expression + { +/* is there a technical reason why the EMS frame can't be at 0xC0000 or + 0xA0000 if there's space? */ +#if 0 + if ( (($2 & 0xfc00)>=0xc800) && (($2 & 0xfc00)<=0xe000) ) { + config.ems_frame = $2 & 0xfc00; + c_printf("CONF: EMS-frame = 0x%04x\n", config.ems_frame); + } + else yyerror("wrong EMS-frame: 0x%04x", $2); +#endif + config.ems_frame = $2 & 0xfc00; + c_printf("CONF: EMS-frame = 0x%04x\n", config.ems_frame); + } + | EMS_UMA_PAGES expression + { + config.ems_uma_pages = $2; + } + | EMS_CONV_PAGES expression + { + config.ems_cnv_pages = $2; + } + | STRING + { yyerror("unrecognized ems command '%s'", $1); + free($1); } + | error + ; + + /* memory areas to spare for hardware (adapter) ram */ + +hardware_ram_flags : hardware_ram_flag + | hardware_ram_flags hardware_ram_flag + ; +hardware_ram_flag : INTEGER + { + if (!register_hardware_ram('h', $1, PAGE_SIZE)) { + yyerror("wrong hardware ram address : 0x%08x", $1); + } + } + | '(' expression ')' + { + if (!register_hardware_ram('h', $2, PAGE_SIZE)) { + yyerror("wrong hardware ram address : 0x%08x", $2); + } + } + | RANGE INTEGER INTEGER + { + if (register_hardware_ram('h', $2, $3 - $2)) + c_printf("CONF: hardware ram pages at 0x%08x-%08x\n", $2, $3); + else + yyerror("wrong hardware ram address : 0x%08x", $2); + } + | RANGE expression ',' expression + { + if (register_hardware_ram('h', $2, $4 - $2)) + c_printf("CONF: hardware ram pages at 0x%08x-%08x\n", $2, $4); + else + yyerror("wrong hardware ram address : 0x%08x", $2); + } + | STRING + { yyerror("unrecognized hardware ram command '%s'", $1); + free($1); } + | error + ; + + /* booleans */ + +bool: expression + ; + +floppy_bool: expression + ; + +int_bool: expression + { + if ($1 == -2) { + yyerror("got 'on', expected 'off' or an integer"); + } + } + ; + +irq_bool: expression { + if ( $1 && (($1 < 2) || ($1 > 15)) ) { + yyerror("got '%d', expected 'off' or an integer 2..15", $1); + } + } + ; + + /* speaker values */ + +speaker : L_OFF { $$ = SPKR_OFF; } + | NATIVE { $$ = SPKR_NATIVE; } + | EMULATED { $$ = SPKR_EMULATED; } + | STRING { yyerror("got '%s', expected 'emulated' or 'native'", $1); + free($1); } + | error { yyerror("expected 'emulated' or 'native'"); } + ; + +cpu_vm : L_AUTO { $$ = -1; } + | VM86 { $$ = CPUVM_VM86; } + | KVM { $$ = CPUVM_KVM; } + | EMULATED { +#ifdef X86_EMULATOR + $$ = CPUVM_EMU; +#else + yyerror("CPU emulator not compiled in"); +#endif + } + | STRING { yyerror("got '%s' for cpu_vm", $1); + free($1); } + | error { yyerror("bad value for cpu_vm"); } + ; + +cpu_vm_dpmi : L_AUTO { $$ = -1; } + | NATIVE { $$ = CPUVM_NATIVE; } + | KVM { $$ = CPUVM_KVM; } + | EMULATED { $$ = CPUVM_EMU; } + | STRING { yyerror("got '%s' for cpu_vm_dpmi", $1); + free($1); } + | error { yyerror("bad value for cpu_vm_dpmi"); } + +charset_flags : charset_flag + | charset_flags charset_flag + ; + +charset_flag : INTERNAL STRING { set_internal_charset ($2); } + | EXTERNAL STRING { set_external_charset ($2); } + ; + +%% + + /* features */ + +static void handle_features(int which, int value) +{ + if ((which < 0) + || (which >= (sizeof(config.features) / sizeof(config.features[0])))) { + c_printf("CONF: wrong feature number %d\n", which); + return; + } + config.features[which] = value; + c_printf("CONF: feature %d set to %d\n", which, value); +} + + /* joystick */ + +static void set_joy_device(char *devstring) +{ + char *spacepos; + + free(config.joy_device[0]); + config.joy_device[0] = devstring; + config.joy_device[1] = NULL; + spacepos = strchr(devstring, ' '); + if (spacepos != NULL) { + *spacepos = '\0'; + config.joy_device[1] = spacepos + 1; + } +} + + /* mouse */ + +static void start_mouse(void) +{ + mptr = &config.mouse; + mptr->fd = -1; + mptr->com = -1; + mptr->com_num = -1; + mptr->has3buttons = 1; // drivers can disable this +} + +static void stop_mouse(void) +{ + char *p, *p1; + if (mptr->dev && (p = strstr(mptr->dev, "com")) && strlen(p) > 3) { + /* parse comX setting */ + if (!isdigit(p[3]) || isdigit(p[4])) { + yyerror("wrong $_mouse_dev setting"); + return; + } + mptr->com_num = atoi(p + 3); + /* see if something else is specified and remove comX */ + if (p > mptr->dev) { + p[-1] = 0; + } else if ((p1 = strchr(p, ','))) { + int l; + p1++; + l = strlen(p1); + memmove(mptr->dev, p1, l + 1); + } else { + free(mptr->dev); + mptr->dev = NULL; + } + c_printf("MOUSE: using COM%i\n", mptr->com); + } + + mptr->type = mptr->dev_type; + c_printf("MOUSE: %s, type %x using internaldriver: %s, emulate3buttons: %s baudrate: %d\n", + mptr->dev && mptr->dev[0] ? mptr->dev : "no device specified", + mptr->type, mptr->intdrv ? "yes" : "no", + mptr->emulate3buttons ? "yes" : "no", mptr->baudRate); +} + + /* debug */ + +static void start_ports(void) +{ + ports_permission = IO_RDWR; + ports_ormask = 0; + ports_andmask = 0xFFFF; +} + + /* debug */ + +static void start_debug(void) +{ + set_debug_level('a', 0); /* Default is no debugging output at all */ +} + + /* video */ + +static void start_video(void) +{ + free(config.vbios_file); + config.vbios_file = NULL; + config.vbios_copy = 0; + config.vbios_seg = 0xc000; + config.vbios_size = 0x10000; + config.console_video = 0; + config.cardtype = CARD_NONE; + config.chipset = PLAINVGA; + config.mapped_bios = 0; + config.vga = 0; + config.gfxmemsize = 256; + config.fullrestore = 0; + config.dualmon = 0; + config.force_vt_switch = 0; +} + +static void stop_video(void) +{ + if ((config.cardtype != CARD_VGA) || !config.console_video) { + config.vga = 0; + } +} + + /* vesa modes */ +static void set_vesamodes(int width, int height, int color_bits) +{ + vesamode_type *vmt = malloc(sizeof *vmt); + if(vmt != NULL) { + vmt->width = width; + vmt->height = height; + vmt->color_bits = color_bits; + vmt->next = config.vesamode_list; + config.vesamode_list = vmt; + } +} + + /* vbios detection */ +static int auto_vbios_seg = -1; +static int auto_vbios_size = -1; + +static void detect_vbios(void) +{ + unsigned char c[0x21]; + int foffset; + + if (auto_vbios_seg != -1) return; + if (!can_do_root_stuff || config.vbios_file) return; + for (foffset = 0xc0000; foffset < 0xf0000; foffset += 0x800) { + load_file("/dev/mem", foffset, c, sizeof(c)); + if (c[0]==0x55 && c[1]==0xaa + && c[0x1e]=='I' && c[0x1f]=='B' && c[0x20]=='M') { + auto_vbios_seg = foffset >> 4; + auto_vbios_size = PAGE_ALIGN(c[2] * 0x200); + break; + } + } +} + +static int detect_vbios_seg(void) +{ + detect_vbios(); + return auto_vbios_seg; +} + +static int detect_vbios_size(void) +{ + detect_vbios(); + return auto_vbios_size; +} + +static void stop_ttylocks(void) +{ + c_printf("SER: directory %s namestub %s binary %s\n", config.tty_lockdir, + config.tty_lockfile,(config.tty_lockbinary?"Yes":"No")); +} + + /* serial */ + +static void start_serial(void) +{ + if (c_ser >= MAX_SER) + sptr = &nullser; + else { + /* The defaults for interrupt, base_port, real_comport and dev are + ** automatically filled in inside the do_ser_init routine of serial.c + */ + sptr = &com_cfg[c_ser]; + sptr->dev = NULL; + sptr->irq = 0; + sptr->base_port = 0; + sptr->end_port = 0; + sptr->real_comport = 0; + sptr->mouse = 0; + sptr->virt = FALSE; + sptr->pseudo = FALSE; + sptr->system_rtscts = FALSE; + sptr->low_latency = FALSE; + sptr->dmx_port = 0; + sptr->custom = SER_CUSTOM_NONE; + } +} + + +static void stop_serial(void) +{ + if (c_ser >= MAX_SER) { + c_printf("SER: too many ports, ignoring %s\n", sptr->dev); + return; + } + c_printf("SER%d: %s", c_ser, sptr->dev ?: "none"); + if (sptr->base_port) + c_printf(" port %x", sptr->base_port); + if (sptr->irq) + c_printf(" irq %x", sptr->irq); + c_printf("\n"); + c_ser++; + config.num_ser = c_ser; +} + + /* keyboard */ + +static int keyboard_statement_already = 0; + +static void start_keyboard(void) +{ + if (!keyboard_statement_already) + config.layout_auto = 1; + config.console_keyb = 0; + keyboard_statement_already = 1; +} + +static void stop_terminal(void) +{ +} + + /* printer */ + +static void start_printer(void) +{ + pptr->prtcmd = NULL; + pptr->dev = NULL; + pptr->remaining = -1; + pptr->delay = 10; +} + +static void stop_printer(void) +{ + printer_config(c_printers, pptr); + c_printf("CONF(LPT%d) f: %s c: %s t: %d port: %x\n", + c_printers, pptr->dev, pptr->prtcmd, + pptr->delay, pptr->base_port); + c_printers++; + if (c_printers > config.num_lpt) + config.num_lpt = c_printers; +} + + /* disk */ + +static void start_floppy(void) +{ + if (c_fdisks >= MAX_FDISKS) + { + yyerror("There are too many floppy disks defined"); + dptr = &nulldisk; /* Dummy-Entry to avoid core-dumps */ + } + else + dptr = &disktab[c_fdisks]; + + dptr->sectors = 0; /* setup default values */ + dptr->heads = 0; + dptr->tracks = 0; + dptr->type = FLOPPY; + dptr->default_cmos = THREE_INCH_FLOPPY; + dptr->timeout = 0; + dptr->dev_name = NULL; /* default-values */ + dptr->rdonly = 0; + dptr->header = 0; +} + +static void dp_init(struct disk *dptr) +{ + dptr->type = NODISK; + dptr->sectors = -1; + dptr->heads = -1; + dptr->tracks = -1; + dptr->fdesc = -1; + dptr->hdtype = 0; + dptr->timeout = 0; + dptr->dev_name = NULL; /* default-values */ + dptr->rdonly = 0; + dptr->header = 0; +} + +static void start_disk(void) +{ + if (c_hdisks >= MAX_HDISKS) + { + yyerror("There are too many hard disks defined"); + dptr = &nulldisk; /* Dummy-Entry to avoid core-dumps */ + } + else + dptr = &hdisktab[c_hdisks]; + + dp_init(dptr); +} + +static void start_vnet(char *mode) +{ + if (strcmp(mode, "off") == 0) { + config.vnet = VNET_TYPE_NONE; + return; + } + if (strcmp(mode, "") == 0) { + config.vnet = VNET_TYPE_AUTO; + return; + } + if (strcmp(mode, "tap") == 0) { + config.vnet = VNET_TYPE_TAP; + return; + } + if (strcmp(mode, "vde") == 0) { + config.vnet = VNET_TYPE_VDE; + return; + } + if (strcmp(mode, "slirp") == 0) { + config.vnet = VNET_TYPE_SLIRP; + return; + } + if (strcmp(mode, "eth") == 0) + config.vnet = VNET_TYPE_ETH; + else { + error("Unknown vnet mode \"%s\"\n", mode); + config.exitearly = 1; + } +} + +static void do_part(char *dev) +{ + if (dptr->dev_name != NULL) + yyerror("Two names for a partition given."); + dptr->type = PARTITION; + free(dptr->dev_name); + dptr->dev_name = dev; +#ifdef __linux__ + dptr->part_info.number = atoi(dptr->dev_name+8); +#endif + if (dptr->part_info.number == 0) + yyerror("%s must be a PARTITION, can't find number suffix!\n", + dptr->dev_name); +} + +static void stop_disk(int token) +{ +#ifdef __linux__ + FILE *f; + struct mntent *mtab; + int mounted_rw; +#endif + + if (dptr == &nulldisk) /* is there any disk? */ + return; /* no, nothing to do */ + + if (!dptr->dev_name) { /* Is there a file/device-name? */ + if (token == L_FLOPPY) + error("floppy %c: no device/file-name given!\n", 'A'+c_fdisks); + else + error("drive %c: no device/file-name given!\n", 'C'+c_hdisks); + return; + } else { /* check the file/device for existance */ + struct stat st; + + if (stat(dptr->dev_name, &st) != 0) { /* Does this file exist? */ + yyerror("disk: device/file %s doesn't exist.", dptr->dev_name); + } + } + + if (dptr->type == NODISK) /* Is it one of image, floppy, harddisk ? */ + yyerror("disk: no device/file-name given!"); /* No, error */ + else + c_printf("CONF: disk type '%s'", disk_t_str(dptr->type)); + + if (dptr->type == PARTITION) { + c_printf(" partition# %d", dptr->part_info.number); +#ifdef __linux__ + mtab = NULL; + if ((f = setmntent(MOUNTED, "r")) != NULL) { + while ((mtab = getmntent(f))) + if (!strcmp(dptr->dev_name, mtab->mnt_fsname)) break; + endmntent(f); + } + if (mtab) { + mounted_rw = ( hasmntopt(mtab, MNTOPT_RW) != NULL ); + if (mounted_rw && !dptr->rdonly) + yyerror("\n\nYou specified '%s' for read-write Direct Partition Access," + "\nit is currently mounted read-write on '%s' !!!\n", + dptr->dev_name, mtab->mnt_dir); + else if (mounted_rw) + yywarn("You specified '%s' for read-only Direct Partition Access," + "\n it is currently mounted read-write on '%s'.\n", + dptr->dev_name, mtab->mnt_dir); + else if (!dptr->rdonly) + yywarn("You specified '%s' for read-write Direct Partition Access," + "\n it is currently mounted read-only on '%s'.\n", + dptr->dev_name, mtab->mnt_dir); + } +#endif + } + + if (dptr->type == IMAGE && dptr->part_image) + dptr->type = PARTITION; // image of a partition + + if (dptr->type == IMAGE) { + char buf[HEADER_SIZE]; + struct image_header *header = (struct image_header *)&buf; + int fd; + + dptr->header = 0; + + fd = open(dptr->dev_name, O_RDONLY); + if (fd != -1) { + if (read(fd, &buf, sizeof buf) == HEADER_SIZE) { + if (memcmp(header->sig, IMAGE_MAGIC, IMAGE_MAGIC_SIZE) == 0) + dptr->header = header->header_end; + c_printf(" header_size: %ld", (long)dptr->header); + } + close(fd); + } + } + + if (dptr->type == DIR_TYPE) + dptr->mfs_idx = mfs_define_drive(dptr->dev_name); + else + dptr->mfs_idx = 0; + + if (token == L_FLOPPY) { + c_printf(" floppy %c:\n", 'A'+c_fdisks); + disktab[c_fdisks].drive_num = c_fdisks; + c_fdisks++; + } + else { + c_printf(" drive %c:\n", 'C'+c_hdisks); + hdisktab[c_hdisks].drive_num = (c_hdisks | 0x80); + hdisktab[c_hdisks].log_offs = skipped_disks; + c_hdisks++; + } +} + + /* keyboard */ + +static void do_keyb_layout(const char *layout, int alt) +{ + struct keytable_entry *kt = keytable_list; + + while (kt->name) { + if (strcmp(kt->name, layout) == 0) { + if (alt) { + c_printf("CONF: Alternate keyboard-layout %s\n", kt->name); + config.altkeytable = kt; + } else { + c_printf("CONF: Keyboard-layout %s\n", kt->name); + config.keytable = kt; + } + config.layout_auto = 0; + return; + } + kt++; + } + yyerror("CONF: ERROR -- Keyboard has incorrect layout %s\n", layout); +} + +static void keyb_layout(char *layout) +{ + char *p = layout; + char *p1; + + if (strcmp(p, "auto") == 0) { + /* auto: do it later */ + config.keytable = NULL; + config.layout_auto = 1; + return; + } + while ((p1 = strsep(&p, ","))) + do_keyb_layout(p1, p1 != layout); +} + +static void keytable_start(char *layout) +{ + keyboard_statement_already = 1; + /* switch to builtin layout, then apply mods */ + keyb_layout(layout); +} + +static void keytable_stop(void) +{ +} + +static void dump_keytables_to_file(char *name) +{ + FILE * f; + struct keytable_entry *kt = keytable_list; + + f = fopen(name, "w"); + if (!f) { + error("cannot create keytable file %s\n", name); + exit(1); + } + + while (kt->name) { + dump_keytable(f, kt); + kt++; + } + fclose(f); + exit(0); +} + +static void set_irq_value(int bits, int i1) +{ + if ((i1>2) && (i1<=15)) { + config.sillyint |= (bits << i1); + c_printf("CONF: IRQ %d for irqpassing", i1); + if (bits & 0x10000) c_printf(" uses SIGIO\n"); + else c_printf("\n"); + } + else yyerror("wrong IRQ for irqpassing command: %d", i1); +} + +static void set_irq_range(int bits, int i1, int i2) { + int i; + if ( (i1<3) || (i1>15) || (i2<3) || (i2>15) || (i1 > i2 ) ) { + yyerror("wrong IRQ range for irqpassing command: %d .. %d", i1, i2); + } + else { + for (i=i1; i<=i2; i++) config.sillyint |= (bits << i); + c_printf("CONF: range of IRQs for irqpassing %d .. %d", i1, i2); + if (bits & 0x10000) c_printf(" uses SIGIO\n"); + else c_printf("\n"); + } +} + + + /* errors & warnings */ + +void yywarn(const char *string, ...) +{ + va_list vars; + error("@Warning: "); + va_start(vars, string); + vprint(string, vars); + va_end(vars); + error("@\n"); + warnings++; +} + +void yyerror(const char *string, ...) +{ + va_list vars; + va_start(vars, string); + if (include_stack_ptr != 0 && !last_include) { + int i; + error("@In file included from %s:%d\n", + include_fnames[0], include_lines[0]); + for(i = 1; i < include_stack_ptr; i++) { + error("@ from %s:%d\n", + include_fnames[i], include_lines[i]); + } + last_include = 1; + } + error("@Error in %s: (line %.3d) ", + include_fnames[include_stack_ptr], line_count); + vprint(string, vars); + error("@\n"); + va_end(vars); + errors++; +} + + +/* + * open_file - opens the configuration-file named *filename and returns + * a file-pointer. The error/warning-counters are reset to zero. + */ + +static FILE *open_file(const char *filename) +{ + errors = 0; /* Reset error counter */ + warnings = 0; /* Reset counter for warnings */ + + if (!filename) return 0; + return fopen(filename, "re"); /* Open config-file */ +} + +/* + * close_file - Close the configuration file and issue a message about + * errors/warnings that occured. If there were errors, the + * flag early-exit is that, so dosemu won't really. + */ + +static void close_file(FILE * file) +{ + if (file) fclose(file); /* Close the config-file */ + + if(errors) + error("@%d error(s) detected while parsing the configuration-file\n", + errors); + if(warnings) + error("@%d warning(s) detected while parsing the configuration-file\n", + warnings); + + if (errors != 0) /* Exit dosemu on errors */ + { + config.exitearly = TRUE; + } + errors = 0; + warnings = 0; +} + +static void set_hdimage(struct disk *dptr, char *name) +{ + char *l = strstr(name, ".lnk"); + + c_printf("Setting up hdimage %s\n", name); + if (l && strlen(l) == 4) { + const char *tmpl = "eval printf `cat %s`"; + char *cmd, path[1024], *rname; + FILE *f; + size_t ret; + + asprintf(&cmd, tmpl, name); + free(name); + f = popen(cmd, "r"); + free(cmd); + ret = fread(path, 1, sizeof(path), f); + pclose(f); + if (ret == 0) + return; + path[ret] = '\0'; + c_printf("Link resolved to %s\n", path); + rname = expand_path(path); + if (access(rname, R_OK) != 0) { + warn("hdimage: %s does not exist\n", rname); + free(rname); + return; + } + free(dptr->dev_name); + dptr->dev_name = rname; + dptr->type = DIR_TYPE; + c_printf("Set up as a directory\n"); + return; + } + dptr->type = IMAGE; + free(dptr->dev_name); + dptr->dev_name = name; + c_printf("Set up as an image\n"); +} + +static int add_drive(const char *name, int rdonly) +{ + struct disk *dptr = &hdisktab[c_hdisks]; + char *rname = expand_path(name); + if (access(rname, R_OK) != 0) { + free(rname); + return -1; + } + dp_init(dptr); + dptr->dev_name = rname; + dptr->type = DIR_TYPE; + dptr->rdonly = rdonly; + dptr->drive_num = (c_hdisks | 0x80); + dptr->log_offs = skipped_disks; + dptr->mfs_idx = mfs_define_drive(rname); + c_printf("Added drive %i (%x): %s\n", c_hdisks, dptr->drive_num, name); + c_hdisks++; + return 0; +} + +static void set_drive_c(void) +{ + int err; + + c_printf("Setting up drive C, %s\n", dosemu_drive_c_path); + if (!config.alt_drv_c && !exists_dir(dosemu_drive_c_path)) { + char *system_str; + c_printf("Creating default drive C\n"); + err = asprintf(&system_str, "mkdir -p %s/tmp", dosemu_drive_c_path); + assert(err != -1); + err = system(system_str); + free(system_str); + if (err) { + error("unable to create %s\n", dosemu_drive_c_path); + return; + } + } + if (config.alt_drv_c && c_hdisks) { + error("wrong mapping of Group 0 to %c\n", 'C' + c_hdisks); + dosemu_drive_c_path = strdup(DRIVE_C_DEFAULT); + config.alt_drv_c = 0; + } + config.drive_c_num = c_hdisks | 0x80; + err = add_drive(dosemu_drive_c_path, 0); + assert(!err); +} + +static void set_dosemu_drive(void) +{ + if (!commands_path) { + error("can't map utility drive, dosemu2 installation incomplete\n"); + leavedos(3); + return; + } + add_drive(commands_path, 1); +} + +static void set_default_drives(void) +{ +#define AD(p) do { \ + if (p) \ + add_drive(p, 1); \ +} while (0) + c_printf("Setting up default drives from %c\n", 'C' + c_hdisks); + if (config.try_freedos) { + AD(fddir_boot); + AD(fddir_default); + } else { + AD(comcom_dir); + AD(xbat_dir); + } +} + +static void set_hostfs_drives(char *drivespec) +{ + char *p; + int err; + + while ((p = strsep(&drivespec, " "))) { + int ro = 0; + int cd = 0; + int grp = 0; + char *d = strchr(p, ':'); + if (d) { + switch (d[1]) { + case 'r': + ro++; + break; + case 'c': + cd++; + break; + case 'g': + grp++; + break; + } + *d = '\0'; + } + err = add_extra_drive(p, ro, cd, grp); + if (err) + config.exitearly = 1; + } +} + +/* Parse TimeMode, Paul Crawford and Andrew Brooks 2004-08-01 */ +/* Accepts "bios", "pit", "linux", default is "bios". Use with TIMEMODE token */ +int parse_timemode(const char *timemodestr) +{ + if (timemodestr == NULL || timemodestr[0] == '\0') + return(TM_BIOS); /* default */ + if (strcmp(timemodestr, "linux")==0) + return(TM_LINUX); + if (strcmp(timemodestr, "pit")==0) + return(TM_PIT); + if (strcmp(timemodestr, "bios")==0) + return(TM_BIOS); + yyerror("Unrecognised time mode (not bios, pit or linux)"); + return(TM_BIOS); +} + +char *commandline_statements; + +static void do_parse(FILE *fp, const char *confname, const char *errtx) +{ + yyin = fp; + line_count = 1; + include_stack_ptr = 0; + c_printf("CONF: Parsing %s file.\n", confname); + file_being_parsed = strdup(confname); + include_fnames[include_stack_ptr] = file_being_parsed; + yyrestart(fp); + if (yyparse()) + yyerror(errtx, confname); + close_file(fp); + include_stack_ptr = 0; + include_fnames[include_stack_ptr] = 0; + free(file_being_parsed); +} + +int parse_config(const char *confname, const char *dosrcname, int nodosrc) +{ + char *dosrc = NULL; + FILE *fd; +#if YYDEBUG != 0 + yydebug = 1; +#endif + + define_config_variable(PARSER_VERSION_STRING); + define_config_variable("c_system"); + + yy_vbuffer = dosemu_conf; + do_parse(NULL, "built-in dosemu.conf", "error in built-in dosemu.conf"); + if (confname) { + yy_vbuffer = NULL; + fd = open_file(confname); + if (!fd) { + fprintf(stderr, "Cannot open base config file %s, Aborting DOSEMU.\n", confname); + exit(1); + } + do_parse(fd, confname, "error in configuration file %s"); + } + move_dosemu_local_dir(); + if (!nodosrc) { + char *newdosrc = NULL; + if (!dosrcname) { + dosrcname = getenv("_dosemurc"); + newdosrc = assemble_path(dosemu_localdir_path, dosrcname); + if (access(newdosrc, R_OK) == -1) + dosrcname = NULL; + else + dosrcname = newdosrc; + } + if (!dosrcname) { + dosrc = assemble_path(dosemu_localdir_path, DOSEMU_RC); + if (access(dosrc, R_OK) == -1) { + free(dosrc); + dosrc = get_path_in_HOME(DOSEMU_RC); + } else if (newdosrc) { + error("config file %s missing.\n\tUsing %s\n" + "\tExecute `mv %s %s`\n\tto silence this error.\n", + newdosrc, dosrc, dosrc, newdosrc); + } + if (access(dosrc, R_OK) == -1) { + free(dosrc); + dosrc = NULL; + } + } else { + dosrc = strdup(dosrcname); + } + if (newdosrc) { + free(newdosrc); + newdosrc = NULL; + } + } + if (dosrc) { + define_config_variable("c_user"); + yy_vbuffer = NULL; + fd = open_file(dosrc); + if (!fd) { + fprintf(stderr, "Cannot open base config file %s, Aborting DOSEMU.\n", + dosrc); + free(dosrc); + exit(1); + } + do_parse(fd, dosrc, "error in configuration file %s"); + free(dosrc); + } + yy_vbuffer = global_conf; + do_parse(NULL, "built-in global.conf", "error in built-in global.conf"); + + undefine_config_variable("c_system"); + + /* Now we parse any commandline statements from option '-I' + */ + + if (commandline_statements) { + #define XX_NAME "commandline" + + open_file(0); + define_config_variable("c_comline"); + c_printf("Parsing " XX_NAME " statements.\n"); + yyin=0; /* say: we have no input file */ + yy_vbuffer=commandline_statements; /* this is the input to scan */ + do_parse(0, XX_NAME, "error in user's %s statement"); + undefine_config_variable("c_comline"); + } + +#ifdef TESTING + error("TESTING: parser is terminating program\n"); + exit(0); +#endif + return 1; +} + +#define MAX_CONFIGVARIABLES 128 +char *config_variables[MAX_CONFIGVARIABLES+1] = {0}; +static int config_variables_count = 0; +static int config_variables_last = 0; + + +char *get_config_variable(const char *name) +{ + int i; + for (i=0; i< config_variables_count; i++) { + if (!strcmp(name, config_variables[i])) { + config_variables_last = i; + return config_variables[i]; + } + } + return 0; +} + +int define_config_variable(const char *name) +{ + if (!get_config_variable(name)) { + if (config_variables_count < MAX_CONFIGVARIABLES) { + config_variables[config_variables_count++] = strdup(name); + } + else { + c_printf("CONF: overflow on config variable list\n"); + return 0; + } + } + c_printf("CONF: config variable %s set\n", name); + return 1; +} + +static int undefine_config_variable(const char *name) +{ + if (get_config_variable(name)) { + int i; + if (!strcmp(name, CONFNAME_V3USED)) parser_version_3_style_used = 0; + free(config_variables[config_variables_last]); + for (i=config_variables_last; i<(config_variables_count-1); i++) { + config_variables[i] = config_variables[i+1]; + } + config_variables_count--; + c_printf("CONF: config variable %s unset\n", name); + return 1; + } + return 0; +} + +char *checked_getenv(const char *name) +{ + return getenv(name); +} + +static void check_user_var(char *name) +{ + char *name_; + char *s; + + name_ = malloc(strlen(name)+sizeof(USERVAR_PREF)); + strcpy(name_, USERVAR_PREF); + strcat(name_, name); + s = getenv(name_); + if (s) { + if (getenv(name)) + c_printf("CONF: variable %s replaced by user\n", name); + setenv(name, s, 1); + unsetenv(name_); + } + free(name_); +} + + +static char *run_shell(char *command) +{ + int pipefds[2]; + pid_t pid; + char excode[16] = "1"; + + setenv("DOSEMU_SHELL_RETURN", excode, 1); + if (pipe(pipefds)) return strdup(""); + pid = fork(); + if (pid == -1) return strdup(""); + if (!pid) { + /* child */ + int ret; + close(pipefds[0]); /* we won't read from the pipe */ + dup2(pipefds[1], 1); /* make the pipe child's stdout */ + priv_drop(); /* drop any priviledges */ + ret = system(command); + /* tell the parent: "we have finished", + * this way we need not to play games with select() + */ + if (ret == -1) ret = errno; + else ret >>=8; + write(pipefds[1],"\0\0\0",4); + close(pipefds[1]); + _exit(ret); + } + else { + /* parent */ + char *buf = 0; + int recsize = 128; + int bufsize = 0; + int ptr =0; + int ret; + int status; + + close(pipefds[1]); /* we won't write to the pipe */ + do { + bufsize = ptr + recsize; + if (!buf) buf = malloc(bufsize); + else buf = realloc(buf, bufsize); + ret = read(pipefds[0], buf+ptr, recsize -1); + if (ret > 0) { + ptr += ret; + } + } while (ret >0 && (ret < 4 || memcmp(buf+ptr-4,"\0\0\0",4))); + close(pipefds[0]); + waitpid(pid, &status, 0); + buf[ptr] = 0; + if (!buf[0]) { + free(buf); + buf = strdup(""); + } + sprintf(excode, "%d", WEXITSTATUS(status)); + setenv("DOSEMU_SHELL_RETURN", excode, 1); + return buf; + } +} + + +struct for_each_entry { + char *list; + char *ptr; + int skip_restart; +}; + +#define FOR_EACH_DEPTH 16 +static struct for_each_entry *for_each_list = 0; + +static int for_each_handling(int loopid, char *varname, char *delim, char *list) +{ + struct for_each_entry *fe; + char * _new; + char saved; + if (!for_each_list) { + int size = FOR_EACH_DEPTH * sizeof(struct for_each_entry); + for_each_list = malloc(size); + memset(for_each_list, 0, size); + } + if (loopid > FOR_EACH_DEPTH) { + yyerror("too deeply nested foreach\n"); + return 0; + } + fe = for_each_list + loopid; + if (!fe->list) { + /* starting the loop */ + fe->ptr = fe->list = strdup(list); + fe->skip_restart = 0; + if (loopid) { + /* in inner loops we need to skip the restart */ + fe->skip_restart = 1; + } + } + else { + if (fe->skip_restart) { + fe->skip_restart = 0; + return 1; + } + } + while (fe->ptr[0] && strchr(delim,fe->ptr[0])) fe->ptr++; + /* subsequent call */ + if (!fe->ptr[0]) { + /* loop end */ + free(fe->list); + fe->list = 0; + return (0); + } + _new = strpbrk(fe->ptr, delim); + if (!_new) _new = strchr(fe->ptr,0); + saved = *_new; + *_new = 0; + setenv(varname,fe->ptr,1); + if (saved) _new++; + fe->ptr = _new; + return (1); +} + +#ifdef TESTING_MAIN + +static void die(char *reason) +{ + error("par dead: %s\n", reason); + exit(0); +} + +int +main(int argc, char **argv) +{ + if (argc != 2) + die("no filename!"); + + if (!parse_config(argv[1], argv[2] /* will be NULL if not given */)) + die("parse failed!\n"); +} + +#endif + +/* for keyboard plugin */ + + + +static void keyb_mod(int wich, t_keysym keynum, int unicode) +{ + static t_keysym *table = 0; + static int count = 0; + + if (wich == ' ') { + switch (keynum & 0xFF00) { + case 0x000: wich = ' '; break; + case 0x100: wich = 'S'; break; + case 0x200: wich = 'A'; break; + case 0x300: wich = 'N'; break; + case 0x400: wich = 'C'; break; + case 0x500: wich = 'a'; break; + case 0x600: wich = 'c'; break; + default: + /* It's a bad value ditch it */ + wich ='X'; table = 0; count = 0; break; + } + keynum &= 0xFF; + } + + switch (wich) { + case ' ': table = config.keytable->key_map; + count=config.keytable->sizemap; + break; + case 'S': table = config.keytable->shift_map; + count=config.keytable->sizemap; + break; + case 'A': table = config.keytable->alt_map; + count=config.keytable->sizemap; + break; + case 'C': table = config.keytable->ctrl_map; + count=config.keytable->sizemap; + break; + case 'a': table = config.keytable->shift_alt_map; + count=config.keytable->sizemap; + break; + case 'c': table = config.keytable->ctrl_alt_map; + count=config.keytable->sizemap; + break; + case 'N': table = config.keytable->num_table; + count=config.keytable->sizepad; + break; + } + + + if (!table || (count == 0) || (wich != 0 && keynum >= count)) { + count = 0; + table = 0; + return; + } + if (wich != 0) { + table += keynum; + count -= keynum; + return; + } + if (!unicode) { + if (keynum == 0) { + keynum = DKY_VOID; + } else { + keynum = keynum + 0xEF00; + } + } + if (count > 0) { + *table++ = keynum; + count--; + } +} + + +static const char *get_key_name(t_keysym key) +{ + struct key_names { + t_keysym key; + const char *name; + }; + static struct key_names names[] = + { + { DKY_DEAD_GRAVE, "dgrave"}, + { DKY_DEAD_ACUTE, "dacute"}, + { DKY_DEAD_CIRCUMFLEX, "dcircum"}, + { DKY_DEAD_TILDE, "dtilde"}, + { DKY_DEAD_BREVE, "dbreve"}, + { DKY_DEAD_ABOVEDOT, "daboved"}, + { DKY_DEAD_DIAERESIS, "ddiares"}, + { DKY_DEAD_ABOVERING, "dabover"}, + { DKY_DEAD_DOUBLEACUTE, "ddacute"}, + { DKY_DEAD_CEDILLA, "dcedilla"}, + { DKY_DEAD_OGONEK, "dogonek"}, + { DKY_DEAD_CARON, "dcaron"}, + }; + int i; + for(i = 0; i < sizeof(names)/sizeof(names[0]); i++) { + if (names[i].key == key) { + return names[i].name; + } + } + return 0; +} + +static void dump_keytable_part(FILE *f, t_keysym *map, int size) +{ + int i, in_string=0; + t_keysym c; + const char *cc; + char comma=' ', buf[16]; + + /* Note: This code assumes every font is a superset of ascii */ + if (!map) { + return; + } + size--; + for (i=0; i<=size; i++) { + c = map[i]; + if (!(i & 15)) fprintf(f," "); + cc = get_key_name(c); + if (!cc && (c < 128) && isprint(c) && !strchr("\\\"\'`", c)) { + if (in_string) fputc(c,f); + else { + fprintf(f, "%c\"%c", comma, c); + in_string = 1; + } + } + else { + if (!cc) { + if (((c > 0) && (c < 128)) || ((c >= 0xEF00) && (c <= 0xEFFF))) { + sprintf(buf, "%d", c & 0xFF); + } else if (c == DKY_VOID) { + strcpy(buf, "0"); + } else { + sprintf(buf, "\\u%04x", c); + } + cc = buf; + } + if (!in_string) fprintf(f, "%c%s", comma, cc); + else { + fprintf(f, "\",%s", cc); + in_string = 0; + } + } + if ((i & 15) == 15) { + if (in_string) fputc('"', f); + if (i < size) fputc(',', f); + fputc('\n', f); + in_string = 0; + comma = ' '; + } + else comma = ','; + } + if (in_string) fputc('"', f); + fputc('\n', f); +} + + +void dump_keytable(FILE *f, struct keytable_entry *kt) +{ + fprintf(f, "keytable %s {\n", kt->name); + fprintf(f, " 0=\n"); + dump_keytable_part(f, kt->key_map, kt->sizemap); + fprintf(f, " shift 0=\n"); + dump_keytable_part(f, kt->shift_map, kt->sizemap); + fprintf(f, " alt 0=\n"); + dump_keytable_part(f, kt->alt_map, kt->sizemap); + fprintf(f, " numpad 0=\n"); + dump_keytable_part(f, kt->num_table, kt->sizepad-1); + fprintf(f, " ctrl 0=\n"); + dump_keytable_part(f, kt->ctrl_map, kt->sizemap); + fprintf(f, " shift-alt 0=\n"); + dump_keytable_part(f, kt->shift_alt_map, kt->sizemap); + fprintf(f, " ctrl-alt 0=\n"); + dump_keytable_part(f, kt->ctrl_alt_map, kt->sizemap); + fprintf(f, "}\n\n\n"); +} diff --git a/src/base/kbd_unicode/Makefile b/src/base/kbd_unicode/Makefile new file mode 100644 index 0000000..fd95486 --- /dev/null +++ b/src/base/kbd_unicode/Makefile @@ -0,0 +1,38 @@ +# +# (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". +# +# for details see file COPYING in the DOSEMU distribution +# + +top_builddir=../../.. +include $(top_builddir)/Makefile.conf + +# +# This is the Makefile for the keyboard-subdirectory of the DOS-emulator +# for Linux. +# + + +CFILES := serv_xlat.c serv_backend.c keymaps.c keyb_raw.c getfd.c \ + keyb_clients.c prestroke.c $(X_CFILES) keyb_none.c keyboard.c \ + dosemu_keys.c keynum.c +ALL_CPPFLAGS += -I$(top_srcdir)/src/include/keyboard + +DEPENDS= $(CFILES:.c=.d) +OBJS = $(CFILES:.c=.o) +HFILES = + +# Insert all source- and header-files here. + +ALL = $(CFILES) $(HFILES) + + +all: lib + +install: + +realclean:: + rm -f config.log config.status include/kbd_unicode_config.h + rm -rf autom4te*.cache + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/kbd_unicode/config/plugin_config.h b/src/base/kbd_unicode/config/plugin_config.h new file mode 100644 index 0000000..fa13c42 --- /dev/null +++ b/src/base/kbd_unicode/config/plugin_config.h @@ -0,0 +1 @@ +#define HAVE_UNICODE_KEYB 2 diff --git a/src/base/kbd_unicode/config/plugin_incdirs b/src/base/kbd_unicode/config/plugin_incdirs new file mode 100644 index 0000000..a1a869c --- /dev/null +++ b/src/base/kbd_unicode/config/plugin_incdirs @@ -0,0 +1 @@ +include diff --git a/src/base/kbd_unicode/config/plugin_lexer b/src/base/kbd_unicode/config/plugin_lexer new file mode 100644 index 0000000..bc53988 --- /dev/null +++ b/src/base/kbd_unicode/config/plugin_lexer @@ -0,0 +1,11 @@ + /* numbers */ +\\u{HEXDIGIT}{4} MAY_BE{yylval->i_value = strtoul(yytext+2, 0, 16); + return(UNICODE); } + + /* keyboard */ +ctrl RETURN(CTRL_MAP); +shift_alt RETURN(SHIFT_ALT_MAP); +ctrl_alt RETURN(CTRL_ALT_MAP); + +tr MAY_BE { yylval->i_value = KEYB_TR; + return(KEYB_LAYOUT); } diff --git a/src/base/kbd_unicode/config/plugin_parser b/src/base/kbd_unicode/config/plugin_parser new file mode 100644 index 0000000..6415c92 --- /dev/null +++ b/src/base/kbd_unicode/config/plugin_parser @@ -0,0 +1,217 @@ +/* Merged parser stuff for keyboard plugin + */ + +%{ + + /* for unicode keyboard plugin */ +static void keyb_mod(int wich, t_keysym keynum, int unicode); +static void dump_keytable_part(FILE *f, t_keysym *map, int size); + +%} + /* special bison declaration */ +%token UNICODE +%token SHIFT_ALT_MAP CTRL_MAP CTRL_ALT_MAP + +%% + + +keyboard_mod : CTRL_MAP expression '=' { keyb_mod('C', $2, 0); } keyboard_modvals + | SHIFT_ALT_MAP expression '=' { keyb_mod('a', $2, 0); } keyboard_modvals + | CTRL_ALT_MAP expression '=' { keyb_mod('c', $2, 0); } keyboard_modvals + ; + +keyboard_modval : UNICODE { keyb_mod(0, $1, 1); } + | DGRAVE { keyb_mod(0, DKY_DEAD_GRAVE, 1); } + | DACUTE { keyb_mod(0, DKY_DEAD_ACUTE, 1); } + | DCIRCUM { keyb_mod(0, DKY_DEAD_CIRCUMFLEX, 1); } + | DTILDE { keyb_mod(0, DKY_DEAD_TILDE, 1); } + | DBREVE { keyb_mod(0, DKY_DEAD_BREVE, 1); } + | DABOVED { keyb_mod(0, DKY_DEAD_ABOVEDOT, 1); } + | DDIARES { keyb_mod(0, DKY_DEAD_DIAERESIS, 1); } + | DABOVER { keyb_mod(0, DKY_DEAD_ABOVERING, 1); } + | DDACUTE { keyb_mod(0, DKY_DEAD_DOUBLEACUTE, 1); } + | DCEDILLA { keyb_mod(0, DKY_DEAD_CEDILLA, 1); } + | DIOTA { keyb_mod(0, DKY_VOID, 1); } /* no dead iotas exist */ + | DOGONEK { keyb_mod(0,DKY_DEAD_OGONEK, 1); } + | DCARON { keyb_mod(0, DKY_DEAD_CARON, 1); } + ; +%% + +/* for keyboard plugin */ + + + +static void keyb_mod(int wich, t_keysym keynum, int unicode) +{ + static t_keysym *table = 0; + static int count = 0; + + if (wich == ' ') { + switch (keynum & 0xFF00) { + case 0x000: wich = ' '; break; + case 0x100: wich = 'S'; break; + case 0x200: wich = 'A'; break; + case 0x300: wich = 'N'; break; + case 0x400: wich = 'C'; break; + case 0x500: wich = 'a'; break; + case 0x600: wich = 'c'; break; + default: + /* It's a bad value ditch it */ + wich ='X'; table = 0; count = 0; break; + } + keynum &= 0xFF; + } + + switch (wich) { + case ' ': table = config.keytable->key_map; + count=config.keytable->sizemap; + break; + case 'S': table = config.keytable->shift_map; + count=config.keytable->sizemap; + break; + case 'A': table = config.keytable->alt_map; + count=config.keytable->sizemap; + break; + case 'C': table = config.keytable->ctrl_map; + count=config.keytable->sizemap; + break; + case 'a': table = config.keytable->shift_alt_map; + count=config.keytable->sizemap; + break; + case 'c': table = config.keytable->ctrl_alt_map; + count=config.keytable->sizemap; + break; + case 'N': table = config.keytable->num_table; + count=config.keytable->sizepad; + break; + } + + + if (!table || (count == 0) || (wich != 0 && keynum >= count)) { + count = 0; + table = 0; + return; + } + if (wich != 0) { + table += keynum; + count -= keynum; + return; + } + if (!unicode) { + if (keynum == 0) { + keynum = DKY_VOID; + } else { + keynum = keynum + 0xEF00; + } + } + if (count > 0) { + *table++ = keynum; + count--; + } +} + + +static char *get_key_name(t_keysym key) +{ + struct key_names { + t_keysym key; + char *name; + }; + static struct key_names names[] = + { + { DKY_DEAD_GRAVE, "dgrave"}, + { DKY_DEAD_ACUTE, "dacute"}, + { DKY_DEAD_CIRCUMFLEX, "dcircum"}, + { DKY_DEAD_TILDE, "dtilde"}, + { DKY_DEAD_BREVE, "dbreve"}, + { DKY_DEAD_ABOVEDOT, "daboved"}, + { DKY_DEAD_DIAERESIS, "ddiares"}, + { DKY_DEAD_ABOVERING, "dabover"}, + { DKY_DEAD_DOUBLEACUTE, "ddacute"}, + { DKY_DEAD_CEDILLA, "dcedilla"}, + { DKY_DEAD_OGONEK, "dogonek"}, + { DKY_DEAD_CARON, "dcaron"}, + }; + int i; + for(i = 0; i < sizeof(names)/sizeof(names[0]); i++) { + if (names[i].key == key) { + return names[i].name; + } + } + return 0; +} + +static void dump_keytable_part(FILE *f, t_keysym *map, int size) +{ + int i, in_string=0; + t_keysym c; + char *cc, comma=' ', buf[16]; + + /* Note: This code assumes every font is a superset of ascii */ + if (!map) { + return; + } + size--; + for (i=0; i<=size; i++) { + c = map[i]; + if (!(i & 15)) fprintf(f," "); + cc = get_key_name(c); + if (!cc && (c < 128) && isprint(c) && !strchr("\\\"\'`", c)) { + if (in_string) fputc(c,f); + else { + fprintf(f, "%c\"%c", comma, c); + in_string = 1; + } + } + else { + if (!cc) { + if (((c > 0) && (c < 128)) || ((c >= 0xEF00) && (c <= 0xEFFF))) { + sprintf(buf, "%d", c & 0xFF); + } else if (c == DKY_VOID) { + strcpy(buf, "0"); + } else { + sprintf(buf, "\\u%04x", c); + } + cc = buf; + } + if (!in_string) fprintf(f, "%c%s", comma, cc); + else { + fprintf(f, "\",%s", cc); + in_string = 0; + } + } + if ((i & 15) == 15) { + if (in_string) fputc('"', f); + if (i < size) fputc(',', f); + fputc('\n', f); + in_string = 0; + comma = ' '; + } + else comma = ','; + } + if (in_string) fputc('"', f); + fputc('\n', f); +} + + +void dump_keytable(FILE *f, struct keytable_entry *kt) +{ + fprintf(f, "keytable %s {\n", kt->name); + fprintf(f, " 0=\n"); + dump_keytable_part(f, kt->key_map, kt->sizemap); + fprintf(f, " shift 0=\n"); + dump_keytable_part(f, kt->shift_map, kt->sizemap); + fprintf(f, " alt 0=\n"); + dump_keytable_part(f, kt->alt_map, kt->sizemap); + fprintf(f, " numpad 0=\n"); + dump_keytable_part(f, kt->num_table, kt->sizepad-1); + fprintf(f, " ctrl 0=\n"); + dump_keytable_part(f, kt->ctrl_map, kt->sizemap); + fprintf(f, " shift-alt 0=\n"); + dump_keytable_part(f, kt->shift_alt_map, kt->sizemap); + fprintf(f, " ctrl-alt 0=\n"); + dump_keytable_part(f, kt->ctrl_alt_map, kt->sizemap); + fprintf(f, "}\n\n\n"); +} + + diff --git a/src/base/kbd_unicode/dosemu_keys.c b/src/base/kbd_unicode/dosemu_keys.c new file mode 100644 index 0000000..37517a5 --- /dev/null +++ b/src/base/kbd_unicode/dosemu_keys.c @@ -0,0 +1,120 @@ +#include "emu.h" +#include "keyboard.h" +#include "keystate.h" +#include "keynum.h" +#include "bitops.h" +#include "vc.h" +#include "mouse.h" +#include "timers.h" +#include "keyb_clients.h" + +/* handle special dosemu keys like Ctrl-Alt-PgDn + * This should only be called with 'make' events! + * returns 1 if key was handled. + */ + +static int switch_to_console(int vc_num) +{ + if (config.console_keyb == KEYB_RAW || config.console_video) { + t_shiftstate shiftstate = get_modifiers_r(input_keyboard_state.shiftstate); + k_printf("KBD: switching to console #%d\n",vc_num); + shiftstate &= ~(MODIFIER_ALT|MODIFIER_CTRL); + set_shiftstate(shiftstate); + vt_activate(vc_num); + return TRUE; + } + return FALSE; +} + +Boolean handle_dosemu_keys(Boolean make, t_keysym key) +{ + Boolean result = TRUE; + switch(key) { + case DKY_DOSEMU_REBOOT: + if (make) { + k_printf("KBD: Ctrl-Alt-Del: rebooting dosemu\n"); + dos_ctrl_alt_del(); + } + break; + case DKY_DOSEMU_EXIT: + if (make) { + k_printf("KBD: Ctrl-Alt-PgDn: bye bye!\n"); + leavedos_once(0); + } + break; + + case DKY_DOSEMU_FREEZE: + if (make) { + if (!dosemu_frozen) { + freeze_dosemu_manual(); + } else { + unfreeze_dosemu(); + } + } + break; + + case DKY_DOSEMU_VT_1: + case DKY_DOSEMU_VT_2: + case DKY_DOSEMU_VT_3: + case DKY_DOSEMU_VT_4: + case DKY_DOSEMU_VT_5: + case DKY_DOSEMU_VT_6: + case DKY_DOSEMU_VT_7: + case DKY_DOSEMU_VT_8: + case DKY_DOSEMU_VT_9: + case DKY_DOSEMU_VT_10: + case DKY_DOSEMU_VT_11: + case DKY_DOSEMU_VT_12: + if (make) { + int vc_num; + vc_num = (key - DKY_DOSEMU_VT_1) +1; + result = switch_to_console(vc_num); + } + break; + + case DKY_MOUSE_UP: + case DKY_MOUSE_DOWN: + case DKY_MOUSE_LEFT: + case DKY_MOUSE_RIGHT: + case DKY_MOUSE_UP_AND_LEFT: + case DKY_MOUSE_UP_AND_RIGHT: + case DKY_MOUSE_DOWN_AND_LEFT: + case DKY_MOUSE_DOWN_AND_RIGHT: + case DKY_MOUSE_BUTTON_LEFT: + case DKY_MOUSE_BUTTON_MIDDLE: + case DKY_MOUSE_BUTTON_RIGHT: + mouse_keyboard(make, key); /* mouse emulation keys */ + break; + + case DKY_DOSEMU_HELP: + case DKY_DOSEMU_REDRAW: + case DKY_DOSEMU_SUSPEND: + case DKY_DOSEMU_RESET: + case DKY_DOSEMU_MONO: + case DKY_DOSEMU_PAN_UP: + case DKY_DOSEMU_PAN_DOWN: + case DKY_DOSEMU_PAN_LEFT: + case DKY_DOSEMU_PAN_RIGHT: + if (Keyboard->handle_keys) { + Keyboard->handle_keys(make, key); + } else + { + result = FALSE; + } + break; + +#if 0 + case DKY_MOUSE_GRAB: + if (Keyboard == &Keyboard_X) { + handle_X_keys(make, key); + } else { + result = FALSE; + } +#endif + + default: + result = FALSE; + break; + } + return result; +} diff --git a/src/base/kbd_unicode/getfd.c b/src/base/kbd_unicode/getfd.c new file mode 100644 index 0000000..d32df8d --- /dev/null +++ b/src/base/kbd_unicode/getfd.c @@ -0,0 +1,473 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +#include "getfd.h" + +#ifdef HAVE_SYS_KD_H +/* this code comes from kbd-1.08 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "priv.h" +#include "keyboard/keynum.h" +#include "translate/translate.h" + +/* + * getfd.c + * + * Get an fd for use with kbd/console ioctls. + * We try several things because opening /dev/console will fail + * if someone else used X (which does a chown on /dev/console). + */ + +static int cons_fd; + +static int +is_a_console(int fd) { + char arg; + + arg = 0; + return (ioctl(fd, KDGKBTYPE, &arg) == 0 + && ((arg == KB_101) || (arg == KB_84))); +} + +static int +open_a_console(const char *fnam) { + int fd; + PRIV_SAVE_AREA; + + enter_priv_on(); + fd = open(fnam, O_RDONLY); + if (fd < 0 && errno == EACCES) + fd = open(fnam, O_WRONLY); + leave_priv_setting(); + if (fd < 0) + return -1; + if (!is_a_console(fd)) { + close(fd); + return -1; + } + return fd; +} + +static int _getfd(void) { + int fd; + + fd = open_a_console("/dev/tty"); + if (fd >= 0) + return fd; + + fd = open_a_console("/dev/tty0"); + if (fd >= 0) + return fd; + + fd = open_a_console("/dev/vc/0"); + if (fd >= 0) + return fd; + + fd = open_a_console("/dev/console"); + if (fd >= 0) + return fd; + + for (fd = 0; fd < 3; fd++) + if (is_a_console(fd)) + return fd; + + return -1; +} + +int open_console(void) +{ + cons_fd = _getfd(); + return cons_fd; +} + +int getfd(void) +{ + return cons_fd; +} + +struct keycode_map { + unsigned char dosemu; + unsigned char kernel; +}; +static const struct keycode_map dosemu_to_kernel[] = +{ + {NUM_L_ALT, 0x38}, + {NUM_R_ALT, 100}, + {NUM_L_CTRL, 0x1d}, + {NUM_R_CTRL, 97}, + {NUM_L_SHIFT, 0x2a}, + {NUM_R_SHIFT, 0x36}, + {NUM_NUM, 0x45}, + {NUM_SCROLL, 0x46}, + {NUM_CAPS, 0x3a}, + {NUM_SPACE, 0x39}, + {NUM_BKSP, 0x0e}, + {NUM_RETURN, 0x1c}, + {NUM_TAB, 0x0f}, + {NUM_A, 0x1e}, + {NUM_B, 0x30}, + {NUM_C, 0x2e}, + {NUM_D, 0x20}, + {NUM_E, 0x12}, + {NUM_F, 0x21}, + {NUM_G, 0x22}, + {NUM_H, 0x23}, + {NUM_I, 0x17}, + {NUM_J, 0x24}, + {NUM_K, 0x25}, + {NUM_L, 0x26}, + {NUM_M, 0x32}, + {NUM_N, 0x31}, + {NUM_O, 0x18}, + {NUM_P, 0x19}, + {NUM_Q, 0x10}, + {NUM_R, 0x13}, + {NUM_S, 0x1f}, + {NUM_T, 0x14}, + {NUM_U, 0x16}, + {NUM_V, 0x2f}, + {NUM_W, 0x11}, + {NUM_X, 0x2d}, + {NUM_Y, 0x15}, + {NUM_Z, 0x2c}, + {NUM_1, 0x02}, + {NUM_2, 0x03}, + {NUM_3, 0x04}, + {NUM_4, 0x05}, + {NUM_5, 0x06}, + {NUM_6, 0x07}, + {NUM_7, 0x08}, + {NUM_8, 0x09}, + {NUM_9, 0x0a}, + {NUM_0, 0x0b}, + {NUM_DASH, 0x0c}, + {NUM_EQUALS, 0x0d}, + {NUM_LBRACK, 0x1a}, + {NUM_RBRACK, 0x1b}, + {NUM_SEMICOLON, 0x27}, + {NUM_APOSTROPHE, 0x28}, + {NUM_GRAVE, 0x29}, + {NUM_BACKSLASH, 0x2b}, + {NUM_COMMA, 0x33}, + {NUM_PERIOD, 0x34}, + {NUM_SLASH, 0x35}, + {NUM_LESSGREATER, 0x56}, + + {NUM_PAD_0, 0x52}, + {NUM_PAD_1, 0x4f}, + {NUM_PAD_2, 0x50}, + {NUM_PAD_3, 0x51}, + {NUM_PAD_4, 0x4b}, + {NUM_PAD_5, 0x4c}, + {NUM_PAD_6, 0x4d}, + {NUM_PAD_7, 0x47}, + {NUM_PAD_8, 0x48}, + {NUM_PAD_9, 0x49}, + {NUM_PAD_DECIMAL, 0x53}, + {NUM_PAD_SLASH, 98}, + {NUM_PAD_AST, 0x37}, + {NUM_PAD_MINUS, 0x4a}, + {NUM_PAD_PLUS, 0x4e}, + {NUM_PAD_ENTER, 96}, + + {NUM_ESC, 0x01}, + {NUM_F1, 0x3b}, + {NUM_F2, 0x3c}, + {NUM_F3, 0x3d}, + {NUM_F4, 0x3e}, + {NUM_F5, 0x3f}, + {NUM_F6, 0x40}, + {NUM_F7, 0x41}, + {NUM_F8, 0x42}, + {NUM_F9, 0x43}, + {NUM_F10, 0x44}, + {NUM_F11, 0x57}, + {NUM_F12, 0x58}, + + {NUM_INS, 110}, + {NUM_DEL, 111}, + {NUM_HOME, 102}, + {NUM_END, 107}, + {NUM_PGUP, 104}, + {NUM_PGDN, 109}, + {NUM_UP, 103}, + {NUM_DOWN, 108}, + {NUM_LEFT, 105}, + {NUM_RIGHT, 106}, + + {NUM_LWIN, 125}, + {NUM_RWIN, 126}, + {NUM_MENU, 127}, + + {NUM_PRTSCR_SYSRQ, 99}, + {NUM_PAUSE_BREAK, 119}, +}; + +/* + * Try to translate a keycode to a DOSEMU keycode... + */ + +static t_keysym dosemu_val(unsigned k) +{ + struct char_set *keyb_charset; + unsigned char buff[1]; + struct char_set_state keyb_state; + t_unicode ch; + + unsigned t = KTYP(k), v = KVAL(k); + t_keysym d; + + d = DKY_VOID; + if (t >= 14) + /* NR_TYPES is 14 in the kernel but keyboard.h doesn't + give it. Gives the Unicode value ^ 0xf000 */ + return k ^ 0xf000; + + switch(t) { + case KT_LATIN: + case KT_LETTER: /* is this correct for all KT_LETTERS? */ + keyb_charset = trconfig.keyb_charset; + init_charset_state(&keyb_state, keyb_charset); + buff[0] = v; + charset_to_unicode(&keyb_state, &ch, buff, 1); + cleanup_charset_state(&keyb_state); + d = ch; + break; + +#if 0 + case KT_FN: + switch(k) { + /* Function keys are hardcodes so ignored... */ + case K_F1: + case K_F2: + case K_F3: + case K_F4: + case K_F5: + case K_F6: + case K_F7: + case K_F8: + case K_F9: + case K_F10: + case K_F11: + case K_F12: + break; + case K_FIND: + /* home */ + case K_INSERT: + case K_REMOVE: + /* delete */ + case K_SELECT: + /* end */ + case K_PGUP: + case K_PGDN: + case K_MACRO: + /* menu */ + case K_HELP: + case K_DO: + /* execute */ + case K_PAUSE: + case K_UNDO: + break; + default: + break; + } + break; +#endif + + + case KT_SPEC: + switch(k) { + case K_HOLE: d = DKY_VOID; break; + case K_ENTER: d = DKY_RETURN; break; + default: d = DKY_VOID; /* K_CAPS, K_NUM, ... ??? */ + } + break; + + case KT_PAD: + switch(k) { + case K_P0: d = DKY_PAD_INS; break; + case K_P1: d = DKY_PAD_END; break; + case K_P2: d = DKY_PAD_DOWN; break; + case K_P3: d = DKY_PAD_PGDN; break; + case K_P4: d = DKY_PAD_LEFT; break; + case K_P5: d = DKY_PAD_CENTER; break; + case K_P6: d = DKY_PAD_RIGHT; break; + case K_P7: d = DKY_PAD_HOME; break; + case K_P8: d = DKY_PAD_UP; break; + case K_P9: d = DKY_PAD_PGUP; break; + case K_PPLUS: d = DKY_PAD_PLUS; break; + case K_PMINUS: d = DKY_PAD_MINUS; break; + case K_PSTAR: d = DKY_PAD_AST; break; + case K_PSLASH: d = DKY_PAD_SLASH; break; + case K_PENTER: d = DKY_PAD_ENTER; break; + case K_PCOMMA: d = DKY_PAD_SEPARATOR; break; + case K_PDOT: d = DKY_PAD_DECIMAL; break; + default: d = DKY_VOID; break; + } + break; + + case KT_SHIFT: + switch(k) { + case K_SHIFT: d = DKY_L_SHIFT; break; + case K_SHIFTL: d = DKY_L_SHIFT; break; + case K_SHIFTR: d = DKY_R_SHIFT; break; + case K_CTRL: d = DKY_L_CTRL; break; + case K_CTRLL: d = DKY_L_CTRL; break; + case K_CTRLR: d = DKY_R_CTRL; break; + case K_ALT: d = DKY_L_ALT; break; + case K_ALTGR: d = DKY_MODE_SWITCH; break; + } + break; + case KT_META: + d = DKY_VOID; + break; + + case KT_DEAD: + switch(k) { + case K_DGRAVE: d = DKY_DEAD_GRAVE; break; + case K_DACUTE: d = DKY_DEAD_ACUTE; break; + case K_DCIRCM: d = DKY_DEAD_CIRCUMFLEX; break; + case K_DTILDE: d = DKY_DEAD_TILDE; break; + case K_DDIERE: d = DKY_DEAD_DIAERESIS; break; + case K_DCEDIL: d = DKY_DEAD_CEDILLA; break; + } + break; + + case KT_LOCK: + switch(k) { + case K_ALTGRLOCK: d = DKY_ALTGR_LOCK; break; + } + break; + + default: + break; + } + + return d; +} + +int read_kbd_table(struct keytable_entry *kt, + struct keytable_entry *altkt) +{ + int fd, i, j = -1; + struct kbentry ke; + int altgr_present, altgr_lock_present; + + fd = getfd(); + if(fd < 0) { + return 1; + } + + altgr_present = 0; + altgr_lock_present = 0; + for(i = 0; i < sizeof(dosemu_to_kernel)/sizeof(dosemu_to_kernel[0]); i++) { + unsigned vp, vs, va, vsa, vc; + t_keysym kp, ks, ka, ksa, kc; + int kernel, dosemu; + kernel = dosemu_to_kernel[i].kernel; + dosemu = dosemu_to_kernel[i].dosemu; + ke.kb_index = kernel; + + ke.kb_table = 0; + if ((j = ioctl(fd, KDGKBENT, (unsigned long) &ke))) break; + vp = ke.kb_value; + + ke.kb_table = 1 << KG_SHIFT; + if ((j = ioctl(fd, KDGKBENT, (unsigned long) &ke))) break; + vs = ke.kb_value; + + ke.kb_table = 1 << KG_ALTGR; + if ((j = ioctl(fd, KDGKBENT, (unsigned long) &ke))) break; + va = ke.kb_value; + + ke.kb_table = (1 << KG_SHIFT) | (1 << KG_ALTGR); + if ((j = ioctl(fd, KDGKBENT, (unsigned long) &ke))) break; + vsa = ke.kb_value; + + ke.kb_table = 1 << KG_CTRL; + if ((j = ioctl(fd, KDGKBENT, (unsigned long) &ke))) break; + vc = ke.kb_value; + + kp = dosemu_val(vp); + ks = dosemu_val(vs); + ka = dosemu_val(va); + ksa = dosemu_val(vsa); + kc = dosemu_val(vc); + if ((kp == DKY_MODE_SWITCH) || (ks == DKY_MODE_SWITCH) || + (ka == DKY_MODE_SWITCH) || (kc == DKY_MODE_SWITCH)) { + k_printf("mode_switch\n"); + altgr_present = 1; + } + if ((kp == DKY_ALTGR_LOCK) || (ks == DKY_ALTGR_LOCK) || + (ka == DKY_ALTGR_LOCK) || (kc == DKY_ALTGR_LOCK)) { + k_printf("altgr lock\n"); + altgr_lock_present = 1; + continue; + } + if (ka == kp) { + ka = U_VOID; + } + /* Only allow control characters in the ctrl plane */ + if ((kc > 0x1f) && (kc != 0x7f)) { + kc = U_VOID; + } + /* As a special case filter [ctrl][tab] */ + if ((dosemu == NUM_TAB) && (kc == 0x09)) { + kc = U_VOID; + } + if (kp != U_VOID) kt->key_map[dosemu] = kp; + if (ks != U_VOID) kt->shift_map[dosemu] = ks; + if (ka != U_VOID) kt->alt_map[dosemu] = ka; + if (kc != U_VOID) kt->ctrl_map[dosemu] = kc; + if (ksa != U_VOID) kt->shift_alt_map[dosemu] = ksa; +#if 0 + printf("%02x: ", dosemu); + printf("p: %04x->%-6s ", vp, pretty_keysym(kp)); + printf("s: %04x->%-6s ", vs, pretty_keysym(ks)); + printf("a: %04x->%-6s ", va, pretty_keysym(ka)); + printf("c: %04x->%-6s ", vc, pretty_keysym(kc)); + printf("\n"); +#endif + } + if (altgr_lock_present) { + altkt->name = "alt auto"; + altkt->key_map = kt->alt_map; + altkt->alt_map = kt->key_map; + altkt->shift_map = kt->shift_alt_map; + altkt->shift_alt_map = kt->shift_map; + } + if (!altgr_present) { + for(i = 0; i < kt->sizemap; i++) { + kt->alt_map[i] = U_VOID; + } + } + + /* look for numpad ',' or '.' */ + ke.kb_index = 83; + ke.kb_table = 0; + if(!j && !(j = ioctl(fd, KDGKBENT, (unsigned long) &ke))) { + if(ke.kb_value == K_PDOT) kt->num_table[12] = '.'; + } + + if(fd > 2) close(fd); + + return j; +} + +#else +int open_console(void) { return -1; } +int getfd(void) { return -1; } +int read_kbd_table(struct keytable_entry *kt, + struct keytable_entry *altkt) { return -1; } +#endif diff --git a/src/base/kbd_unicode/getfd.h b/src/base/kbd_unicode/getfd.h new file mode 100644 index 0000000..fee663a --- /dev/null +++ b/src/base/kbd_unicode/getfd.h @@ -0,0 +1,12 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +#include "keyboard/keymaps.h" + +extern int open_console(void); +extern int getfd(void); +int read_kbd_table(struct keytable_entry *kt, + struct keytable_entry *altkt); diff --git a/src/base/kbd_unicode/keyb_clients.c b/src/base/kbd_unicode/keyb_clients.c new file mode 100644 index 0000000..7e3e5b9 --- /dev/null +++ b/src/base/kbd_unicode/keyb_clients.c @@ -0,0 +1,186 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* This file contains code on the keyboard client side which is common to all frontends. + * In particular, the client initialisation and the paste routine. + */ +#include +#include +#include "emu.h" +#include "sig.h" +#include "keyboard.h" +#include "keyb_clients.h" +#include "keymaps.h" +#include "video.h" +#include "translate/translate.h" + +static t_unicode *paste_buffer = NULL; +static int paste_len = 0, paste_idx = 0; +struct keyboard_client *Keyboard; +static struct keyboard_client *Kbd_root; + +static int paste_unicode_text(const t_unicode *text, int len) +{ + int i; + /* if previous paste in progress, ignore current request */ + /* XXX - maybe this should append ? */ + k_printf("KBD: paste_text called, len=%d\n",len); + if (paste_buffer!=NULL) { + k_printf("KBD: paste in progress, ignoring request\n"); + return 0; + } + paste_buffer = malloc(sizeof(t_unicode) * len); + memcpy(paste_buffer, text, sizeof(t_unicode) * len); + /* translate all 0xa's (\n) to ENTERs */ + for (i = 0; i < len; i++) + if (paste_buffer[i] == U_LINE_FEED) + paste_buffer[i] = U_CARRIAGE_RETURN; + paste_len = len; + paste_idx = 0; + return 1; +} + +/* paste a string of (almost) arbitrary length through the DOS keyboard, + * without danger of overrunning the keyboard queue/buffer. + * pasting in X causes either utf8, iso2022, or iso8859-1, all with + * unix ('\n') line end convention. + */ +int paste_text(const char *text, int len, const char *charset) +{ + struct char_set *paste_charset = lookup_charset(charset); + struct char_set_state state; + t_unicode *str; + int characters; + int result; + + init_charset_state(&state, paste_charset); + characters = character_count(&state, text, len); + if (characters == -1) { + k_printf("KBD: invalid paste\n"); + return 0; + } + str = malloc(sizeof(t_unicode) * (characters +1)); + + charset_to_unicode_string(&state, str, &text, len, characters + 1); + cleanup_charset_state(&state); + + result = paste_unicode_text(str, characters); + free(str); + + return result; +} + +static void paste_run(void) +{ + int count=0; + + if (paste_buffer) { /* paste in progress */ + k_printf("KBD: paste_run running\n"); + t_unicode keysym; + keysym = paste_buffer[paste_idx]; + + put_symbol(PRESS, keysym); + put_symbol(RELEASE, keysym); + + count++; + if (++paste_idx == paste_len) { /* paste finished */ + free(paste_buffer); + paste_buffer=NULL; + paste_len=paste_idx=0; + k_printf("KBD: paste finished\n"); + } + k_printf("KBD: paste_run() pasted %d chars\n",count); + } +} + +/* register keyboard at the back of the linked list */ +void register_keyboard_client(struct keyboard_client *keyboard) +{ + struct keyboard_client *k; + + keyboard->next = NULL; + if (Kbd_root == NULL) + Kbd_root = keyboard; + else { + for (k = Kbd_root; k->next; k = k->next); + k->next = keyboard; + } +} + +/* DANG_BEGIN_FUNCTION keyb_client_init + * + * Figures out which keyboard client to use and initialises it. + * + * First it calls the probe method to see if it should use the client, + * Then it call init to set that client up. + * + * If probe or init fails it tries another client. + * + * Eventually falling back to Keyboard_none a dummy client, which does nothing. + * + * DANG_END_FUNCTION + */ +int keyb_client_init(void) +{ + int ok; + + register_keyboard_client(&Keyboard_raw); + register_keyboard_client(&Keyboard_stdio); + register_keyboard_client(&Keyboard_none); + for (Keyboard = Kbd_root; Keyboard; Keyboard = Keyboard->next) { + k_printf("KBD: probing '%s' mode keyboard client\n", + Keyboard->name); + ok = Keyboard->probe(); + + if (ok) { + k_printf("KBD: initialising '%s' mode keyboard client\n", + Keyboard->name); + + ok = Keyboard->init?Keyboard->init():TRUE; + if (ok) { + k_printf("KBD: Keyboard init ok, '%s' mode\n", + Keyboard->name); + break; + } + else { + k_printf("KBD: Keyboard init ***failed***, '%s' mode\n", + Keyboard->name); + } + } + } + + sigalrm_register_handler(paste_run); + + return TRUE; +} + +void keyb_client_reset(void) +{ + if ((Keyboard!=NULL) && (Keyboard->reset!=NULL)) { + Keyboard->reset(); + } +} + +void keyb_client_close(void) +{ + if ((Keyboard!=NULL) && (Keyboard->close!=NULL)) { + Keyboard->close(); + } +} + +void keyb_client_set_leds(t_modifiers modifiers) +{ + /* FIXME static variable. . . */ + static t_modifiers prev_modifiers = -1; + + if (modifiers == prev_modifiers) + return; + prev_modifiers = modifiers; + + if (Keyboard->set_leds) { + Keyboard->set_leds(modifiers); + } +} diff --git a/src/base/kbd_unicode/keyb_none.c b/src/base/kbd_unicode/keyb_none.c new file mode 100644 index 0000000..f097e35 --- /dev/null +++ b/src/base/kbd_unicode/keyb_none.c @@ -0,0 +1,87 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * stdio keyboard client. + * + * Author: Stas Sergeev + */ + +#include +#include "ioselect.h" +#include "keyb_clients.h" + +static void stdio_kbd_run(int fd, void *arg); + +/* DANG_BEGIN_FUNCTION stdio_kbd_probe + * + * Succeed if we can run the dummy keyboard client, (we always can). + * + * DANG_END_FUNCTION + */ + +static int stdio_kbd_probe(void) +{ + if (no_local_video) + return 0; + return 1; +} + +static int stdio_kbd_init(void) +{ + add_to_io_select(STDIN_FILENO, stdio_kbd_run, NULL); + return 1; +} + +static void stdio_kbd_close(void) +{ + remove_from_io_select(STDIN_FILENO); +} + +static void stdio_kbd_run(int fd, void *arg) +{ + char buf[256]; + int rc; + rc = read(STDIN_FILENO, buf, sizeof(buf)); + ioselect_complete(fd); + if (rc > 0) + paste_text(buf, rc, "utf8"); +} + +struct keyboard_client Keyboard_stdio = +{ + "stdio", /* name */ + stdio_kbd_probe, /* probe */ + stdio_kbd_init, /* init */ + NULL, /* reset */ + stdio_kbd_close, /* close */ + NULL, /* set_leds */ +}; + + +static int none_kbd_probe(void) +{ + return 1; +} + +static int none_kbd_init(void) +{ + return 1; +} + +static void none_kbd_close(void) +{ +} + +struct keyboard_client Keyboard_none = +{ + "none", /* name */ + none_kbd_probe, /* probe */ + none_kbd_init, /* init */ + NULL, /* reset */ + none_kbd_close, /* close */ + NULL, /* set_leds */ +}; diff --git a/src/base/kbd_unicode/keyb_raw.c b/src/base/kbd_unicode/keyb_raw.c new file mode 100644 index 0000000..32bccac --- /dev/null +++ b/src/base/kbd_unicode/keyb_raw.c @@ -0,0 +1,267 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_KD_H +#include +#endif +#include + +#include "ioselect.h" +#include "keyboard.h" +#include "keyb_clients.h" +#include "translate/keysym_attributes.h" +#include "keystate.h" + +#define KBBUF_SIZE (KEYB_QUEUE_LENGTH / 2) + +/* LED FLAGS (from Linux keyboard code) */ +#define LED_SCRLOCK 0 +#define LED_NUMLOCK 1 +#define LED_CAPSLOCK 2 + +static struct termios save_termios; /* original terminal modes */ +#ifdef HAVE_SYS_KD_H +static int save_mode; /* original keyboard mode */ +#endif +static int kbd_fd = -1; + +static void set_kbd_leds(t_modifiers shiftstate) +{ +#ifdef HAVE_SYS_KD_H + unsigned int led_state = 0; + static t_modifiers prev_shiftstate = 0xffff; + + if (shiftstate==prev_shiftstate) return; + prev_shiftstate=shiftstate; + + if (shiftstate & MODIFIER_SCR) { + led_state |= (1 << LED_SCRLOCK); + } + if (shiftstate & MODIFIER_NUM) { + led_state |= (1 << LED_NUMLOCK); + } + if (shiftstate & MODIFIER_CAPS) { + led_state |= (1 << LED_CAPSLOCK); + } + k_printf("KBD(raw): kbd_set_leds() setting LED state\n"); + ioctl(kbd_fd, KDSETLED, led_state); +#endif +} + +static t_shiftstate get_kbd_flags(void) +{ + t_modifiers s = 0; +#ifdef HAVE_SYS_KD_H + int rc; + unsigned int led_state = 0; + + k_printf("KBD(raw): getting keyboard flags\n"); + + /* note: this reads the keyboard flags, not the LED state (which would + * be KDGETLED). + */ + rc = ioctl(kbd_fd, KDGKBLED, &led_state); + if (rc == -1) + return 0; + + if (led_state & (1 << LED_SCRLOCK)) s|=MODIFIER_SCR; + if (led_state & (1 << LED_NUMLOCK)) s|=MODIFIER_NUM; + if (led_state & (1 << LED_CAPSLOCK)) s|=MODIFIER_CAPS; +#endif + return s; +} + +static int use_move_key(t_keysym key) +{ + int result = FALSE; + /* If it's some kind of function key move it + * otherwise just make sure it gets pressed + */ + if (is_keysym_function(key) || + is_keysym_dosemu_key(key) || + is_keypad_keysym(key) || + (key == DKY_TAB) || + (key == DKY_RETURN) || + (key == DKY_BKSP) || (key == U_DELETE)) { + result = TRUE; + } + return result; +} + +static void do_raw_getkeys(int fd, void *arg) +{ + int i,count; + char buf[KBBUF_SIZE]; + + count = RPT_SYSCALL(read(kbd_fd, buf, KBBUF_SIZE - 1)); + ioselect_complete(fd); + k_printf("KBD(raw): do_raw_getkeys() found %d characters (Raw)\n", count); + if (count == -1) { + k_printf("KBD(raw): do_raw_getkeys(): keyboard read failed!\n"); + return; + } + buf[count] = 0; + if (config.console_keyb == KEYB_RAW) { + for (i = 0; i < count; i++) { + k_printf("KBD(raw): readcode: %02x \n", buf[i]); + put_rawkey(buf[i]); + } + } else { + const char *p = buf; + while (*p) { + int rc; + t_unicode _key[2]; + #define key _key[0] + struct char_set_state state; + init_charset_state(&state, trconfig.keyb_charset); + rc = charset_to_unicode_string(&state, &key, &p, strlen(p), 2); + cleanup_charset_state(&state); + if (rc != 1) + break; + if (use_move_key(key)) { + move_key(1, key); + move_key(0, key); + } else { + put_symbol(1, key); + put_symbol(0, key); + } + } + } +} + +#if 0 /* debug code */ +static void print_termios(struct termios term) +{ + k_printf("KBD(raw): TERMIOS Structure:\n"); + k_printf("KBD(raw): c_iflag=%lx\n", term.c_iflag); + k_printf("KBD(raw): c_oflag=%lx\n", term.c_oflag); + k_printf("KBD(raw): c_cflag=%lx\n", term.c_cflag); + k_printf("KBD(raw): c_lflag=%lx\n", term.c_lflag); + k_printf("KBD(raw): c_line =%x\n", (int)term.c_line); +} +#endif + +static int set_raw_mode(void) +{ + struct termios buf = save_termios; +#ifdef HAVE_SYS_KD_H + int err; + + if (config.console_keyb == KEYB_RAW) { + k_printf("KBD(raw): Setting keyboard to RAW mode\n"); + err = ioctl(kbd_fd, KDSKBMODE, K_RAW); + if (err) + return err; + } +#endif + cfmakeraw(&buf); + k_printf("KBD(raw): Setting TERMIOS Structure.\n"); + if (tcsetattr(kbd_fd, TCSAFLUSH, &buf) < 0) { + k_printf("KBD(raw): Setting TERMIOS structure failed.\n"); + return -1; + } + +#if 0 /* debug code */ + if (tcgetattr(kbd_fd, &buf) < 0) { + k_printf("KBD(raw): Termios ERROR\n"); + } + print_termios(buf); +#endif + return 0; +} + +/* + * DANG_BEGIN_FUNCTION raw_keyboard_init + * + * Initialize the keyboard for RAW mode. + * + * DANG_END_FUNCTION + */ +static int raw_keyboard_init(void) +{ + if (config.console_keyb > KEYB_TTY) + return FALSE; + + k_printf("KBD(raw): raw_keyboard_init()\n"); + + kbd_fd = STDIN_FILENO; +#ifdef HAVE_SYS_KD_H + if (config.console_keyb == KEYB_RAW) + ioctl(kbd_fd, KDGKBMODE, &save_mode); +#endif + if (tcgetattr(kbd_fd, &save_termios) < 0) { + error("KBD(raw): Couldn't tcgetattr(kbd_fd,...) !\n"); + memset(&save_termios, 0, sizeof(save_termios)); + return FALSE; + } + + if (set_raw_mode() == -1) + return FALSE; + + add_to_io_select(kbd_fd, do_raw_getkeys, NULL); + + return TRUE; +} + +/* + * DANG_BEGIN_FUNCTION raw_keyboard_reset + * + * Reset the keyboard shiftstate to match the keyboard LED's + * + * DANG_END_FUNCTION + */ +static void raw_keyboard_reset(void) +{ + /* initialise the server's shift state to the current keyboard state */ + set_shiftstate(get_kbd_flags()); + +} + +/* something like this oughta be defined in linux/kd.h but isn't... */ +/* reset LEDs to normal mode, reflecting keyboard state */ +#define LED_NORMAL 0x08 + +static void raw_keyboard_close(void) +{ + if (kbd_fd != -1) { + k_printf("KBD(raw): raw_keyboard_close: resetting keyboard to original mode\n"); + remove_from_io_select(kbd_fd); +#ifdef HAVE_SYS_KD_H + if (config.console_keyb == KEYB_RAW) { + ioctl(kbd_fd, KDSKBMODE, save_mode); + + k_printf("KBD(raw): resetting LEDs to normal mode\n"); + ioctl(kbd_fd, KDSETLED, LED_NORMAL); + } +#endif + k_printf("KBD(raw): Resetting TERMIOS structure.\n"); + if (tcsetattr(kbd_fd, TCSAFLUSH, &save_termios) < 0) { + k_printf("KBD(raw): Resetting keyboard termios failed.\n"); + } + kbd_fd = -1; + } +} + +static int raw_keyboard_probe(void) +{ + if (no_local_video) + return 0; + return isatty(STDIN_FILENO); +} + +struct keyboard_client Keyboard_raw = { + "raw", /* name */ + raw_keyboard_probe, /* probe */ + raw_keyboard_init, /* init */ + raw_keyboard_reset, /* reset */ + raw_keyboard_close, /* close */ + set_kbd_leds, /* set_leds */ +}; diff --git a/src/base/kbd_unicode/keyboard.c b/src/base/kbd_unicode/keyboard.c new file mode 100644 index 0000000..5bbeea0 --- /dev/null +++ b/src/base/kbd_unicode/keyboard.c @@ -0,0 +1,69 @@ +/* + * DANG_BEGIN_MODULE + * + * Description: Keyboard coordinator + * + * Maintainer: Eric Biederman + * + * REMARK + * This module coordinates the initialization of the keyboard. + * + * /REMARK + * DANG_END_MODULE + * + */ + +#include "emu.h" +#include "init.h" +#include "iodev.h" + +#include "getfd.h" +#include "keyboard.h" +#include "keyb_clients.h" +#include "keyb_server.h" + +static int initialized; + +void keyb_priv_init(void) +{ + /* this must be initialized before starting port-server */ + keyb_8042_init(); + open_console(); +} + +void keyb_init(void) +{ + if (!keyb_server_init()) { + error("can't init keyboard server\n"); + leavedos(19); + } + if (!keyb_client_init()) { + error("can't open keyboard client\n"); + leavedos(19); + } + initialized = 1; +} + +void keyb_reset(void) +{ + keyb_8042_reset(); + keyb_server_reset(); + keyb_client_reset(); +} + +void keyb_close(void) +{ + if (!initialized) + return; +#if 0 + keyb_8042_close(); +#endif + keyb_server_close(); + keyb_client_close(); + initialized = 0; +} + +CONSTRUCTOR(static void init(void)) +{ + iodev_register("keyb", keyb_init, keyb_reset, keyb_close); +} diff --git a/src/base/kbd_unicode/keymaps.c b/src/base/kbd_unicode/keymaps.c new file mode 100644 index 0000000..bdbe91b --- /dev/null +++ b/src/base/kbd_unicode/keymaps.c @@ -0,0 +1,2163 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "keymaps.h" +#include "keyb_clients.h" +#include "keynum.h" +#include "getfd.h" +#include "utilities.h" + +int (*XDetectLayout)(void); + +/* DANG_BEGIN_MODULE + * + * REMARK + * These are definitions, giving which key is related to which scancode in + * raw keyboard mode. Basically, the code of 'x' on a US keyboard may be that + * of a 'Y' on a German keyboard. This way, all types of keyboard can be + * represented under DOSEMU. Also, the right ALT-key is often a function + * key in it's own right. + * + * Note: + * These keymaps have all been converted into dosemu's own internal + * superset of unicode. + * Things particular to this superset are: + * U_VOID is used to represent the lack of a value: + * The range 0xE000 - 0xEFFF is the unicode private space, so dosemu + * makes use of it. + * 0xEF00 - 0xEFFF is used as a pass through to the locally configured + * character set. + * While the rest of the range is used for representing symbols that + * only appear on keyboards and not in alphabets. + * + * Note: unicode is also a superset of ascii, and iso8859-1 (i.e. latin1). + * + * /REMARK + * DANG_END_MODULE + * + * DANG_BEGIN_CHANGELOG + * + * 1999/06/16: Added support for automatic keyboard configuration. + * Latin-1 keyboards should work. Latin-2 probably not. + * -- sw (Steffen Winterfeldt ) + * + * DANG_END_CHANGELOG + * + */ + + +CONST t_keysym key_map_finnish[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '+', '\'', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '}', U_VOID, 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', '|', + '{', U_VOID, U_VOID, '\'', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_finnish[] = +{ + U_VOID, 27, '!', '"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', ']', '^', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', '\\', + '[', U_VOID, U_VOID, '*', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_finnish[] = +{ + U_VOID, U_VOID, U_VOID, '@', 0xefa3, '$', U_VOID, U_VOID, + '{', '[', ']', '}', '\\', U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, '~', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_finnish_latin1[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '+', 0xefb4, 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', 0xef86, 0xefa8, 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xef94, + 0xef84, 0xefa7, U_VOID, '\'', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_finnish_latin1[] = +{ + U_VOID, 27, '!', '"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', 0xef8f, '^', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xef99, + 0xef8e, 0xefab, U_VOID, '*', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_finnish_latin1[] = +{ + U_VOID, U_VOID, U_VOID, '@', 0xef9c, '$', U_VOID, U_VOID, + '{', '[', ']', '}', '\\', U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, '~', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_us[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', U_VOID, '\\', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', U_VOID, '*', + U_VOID, ' ', U_VOID, DKY_F1, DKY_F2, DKY_F3, DKY_F4, DKY_F5, + DKY_F6, DKY_F7, DKY_F8, DKY_F9, DKY_F10, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, DKY_F11, + DKY_F12, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_us[] = +{ + U_VOID, 27, '!', '@', '#', '$', '%', '^', + '&', '*', '(', ')', '_', '+', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '{', '}', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', + '"', '~', U_VOID, '|', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_us[] = +{ + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym ctrl_map_us[] = +{ + U_VOID, 0x1b, U_VOID, 0x00, U_VOID, U_VOID, U_VOID, 0x1e, + U_VOID, U_VOID, U_VOID, U_VOID, 0x1f, U_VOID, 0x7f, U_VOID, + 0x11, 0x17, 0x05, 0x12, 0x14, 0x19, 0x15, 0x09, + 0x0f, 0x10, 0x1b, 0x1d, 0x0a, U_VOID, 0x01, 0x13, + 0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, U_VOID, + U_VOID, U_VOID, U_VOID, 0x1c, 0x1a, 0x18, 0x03, 0x16, + 0x02, 0x0e, 0x0d, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, 0x20, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_uk[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', U_VOID, '#', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '\\', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_uk[] = +{ + U_VOID, 27, '!', '"', 0xef9c, '$', '%', '^', + '&', '*', '(', ')', '_', '+', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '{', '}', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', + '@', '~', '0', '~', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_uk[] = +{ + U_VOID, U_VOID, U_VOID, '@', U_VOID, U_EURO_SIGN, U_VOID, U_VOID, + '{', '[', ']', '}', '\\', U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, '~', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + DKY_DEAD_ACUTE, DKY_DEAD_GRAVE, U_VOID, DKY_DEAD_TILDE, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +/* Watch this keymap carefully. + * It produces a keymap that is virtually identical to that produces by keyb.exe + * in for both character sets cp437 & cp850. + * Further this keymap has been converted into unicode, the first real conversion. + * To get exactly the same translations as keyb.exe for two characters + * sets cp437 & cp850. The code keysym_approximations is relied upon to do the + * right thing. + * --EB 11 Feb 2000 + */ +CONST t_keysym key_map_de[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', 0x00df, 0x00b4, 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', 0x00fc, '+', 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0x00f6, + 0x00e4, '^', U_VOID, '#', 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_de[] = +{ + U_VOID, 27, '!', '"', 0x00a7, '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', 0x00dc, '*', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0x00d6, + 0x00c4, 0x00b0, U_VOID, '\'', 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_de[] = +{ + U_VOID, U_VOID, U_VOID, 0x00b2, 0x207f, U_VOID, U_VOID, U_VOID, + '{', '[', ']', '}', '\\', U_VOID, U_VOID, U_VOID, + '@', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, '~', U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, 0x00b5, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym ctrl_map_de[] = +{ + U_VOID, 0x1b, U_VOID, 0x00, U_VOID, U_VOID, U_VOID, 0x1e, + U_VOID, U_VOID, U_VOID, U_VOID, 0x1c, U_VOID, 0x7f, U_VOID, + 0x11, 0x17, 0x05, 0x12, 0x14, 0x1a, 0x15, 0x09, + 0x0f, 0x10, 0x1b, 0x1d, 0x0a, U_VOID, 0x01, 0x13, + 0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, 0x19, 0x18, 0x03, 0x16, + 0x02, 0x0e, 0x0d, U_VOID, U_VOID, 0x1f, U_VOID, U_VOID, + U_VOID, 0x20, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_de_latin1[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', 0xefe1, DKY_DEAD_ACUTE, 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', 0xef81, '+', 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xef94, + 0xef84, DKY_DEAD_CIRCUMFLEX, U_VOID, '#', 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_de_latin1[] = +{ + U_VOID, 27, '!', '"', 21, '$', '%', '&', + '/', '(', ')', '=', '?', DKY_DEAD_GRAVE, 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', 0xef9a, '*', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xef99, + 0xef8e, 0xeff8, U_VOID, '\'', 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_de_latin1[] = +{ + U_VOID, U_VOID, U_VOID, 0xeffd, 0xeffc, '$', U_VOID, U_VOID, + '{', '[', ']', '}', '\\', U_VOID, U_VOID, U_VOID, + '@', U_VOID, U_EURO_SIGN, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, '~', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, 0xefe6, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_fr[] = +{ + U_VOID, 27, '&', '{', '"', '\'', '(', '-', + '}', '_', '/', '@', ')', '=', 127, 9, + 'a', 'z', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '^', '$', 13, U_VOID, 'q', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', + '|', '`', U_VOID, '*', 'w', 'x', 'c', 'v', + 'b', 'n', ',', ';', ':', '!', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_fr[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', ']', '+', 127, 9, + 'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '<', '>', 13, U_VOID, 'Q', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', + '%', '~', U_VOID, '#', 'W', 'X', 'C', 'V', + 'B', 'N', '?', '.', '/', '\\', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_fr[] = +{ + U_VOID, U_VOID, U_VOID, '~', '#', '{', '[', '|', + '`', '\\', '^', '@', ']', '}', U_VOID, U_VOID, + '@', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, '~', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_fr_latin1[] = +{ + U_VOID, 27, '&', 0xef82, '"', '\'', '(', '-', + 0xef8a, '_', 0xef87, 0xef85, ')', '=', 127, 9, + 'a', 'z', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', DKY_DEAD_CIRCUMFLEX, '$', 13, U_VOID, 'q', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', + 0xef97, 0xeffd, U_VOID, '*', 'w', 'x', 'c', 'v', + 'b', 'n', ',', ';', ':', '!', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_fr_latin1[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', 0xeff8, '+', 127, 9, + 'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', DKY_DEAD_DIAERESIS, 0xef9c, 13, U_VOID, 'Q', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', + '%', '~', U_VOID, 0xefe6, 'W', 'X', 'C', 'V', + 'B', 'N', '?', '.', '/', 0xefa7, U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_fr_latin1[] = +{ + U_VOID, U_VOID, U_VOID, '~', '#', '{', '[', '|', + '`', '\\', '^', '@', ']', '}', U_VOID, U_VOID, + '@', U_VOID, U_EURO_SIGN, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, 0xefa4, 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_dk[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '+', '\'', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', 0xef86, U_VOID, 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xef91, + 0xef9b, U_VOID, U_VOID, '\'', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_dk[] = +{ + U_VOID, 27, '!', '"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', 0xef8f, '^', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xef92, + 0xef9d, U_VOID, U_VOID, '*', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_dk[] = +{ + U_VOID, U_VOID, U_VOID, '@', 0xefa3, '$', U_VOID, U_VOID, + '{', '[', ']', '}', U_VOID, '|', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, '~', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '\\', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_dk_latin1[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '+', 0xefb4, 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', 0xefe5, 0xefa8, 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xefe6, + 0xefa2, 0xefbd, U_VOID, '\'', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_dk_latin1[] = +{ + U_VOID, 27, '!', '"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', 0xefc5, '^', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xefc6, + 0xefa5, 0xefa7, U_VOID, '*', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_dk_latin1[] = +{ + U_VOID, U_VOID, U_VOID, '@', 0xefa3, '$', U_VOID, U_VOID, + '{', '[', ']', '}', U_VOID, '|', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, '~', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '\\', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_dvorak[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\\', '=', 127, 9, + '\'', ',', '.', 'p', 'y', 'f', 'g', 'c', + 'r', 'l', '/', ']', 13, U_VOID, 'a', 'o', + 'e', 'u', 'i', 'd', 'h', 't', 'n', 's', + '-', '`', U_VOID, '[', ';', 'q', 'j', 'k', + 'x', 'b', 'm', 'w', 'v', 'z', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_dvorak[] = +{ + U_VOID, 27, '!', '@', '#', '$', '%', '^', + '&', '*', '(', ')', '|', '+', 127, 9, + '"', '<', '>', 'P', 'Y', 'F', 'G', 'C', + 'R', 'L', '?', '}', 13, U_VOID, 'A', 'O', + 'E', 'U', 'I', 'D', 'H', 'T', 'N', 'S', + '_', '~', U_VOID, '{', ':', 'Q', 'J', 'K', + 'X', 'B', 'M', 'W', 'V', 'Z', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_dvorak[] = +{ + U_VOID, U_VOID, U_VOID, '@', U_VOID, '$', U_VOID, U_VOID, + '{', '[', ']', '}', '\\', U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, '~', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_sg[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\'', '^', 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', U_VOID, U_VOID, 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', U_VOID, + U_VOID, U_VOID, U_VOID, '$', 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_sg[] = +{ + U_VOID, 27, '+', '"', '*', U_VOID, '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', U_VOID, '!', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_sg[] = +{ + U_VOID, U_VOID, U_VOID, '@', '#', U_VOID, U_VOID, U_VOID, + '|', U_VOID, U_VOID, U_VOID, '\'', '~', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '[', ']', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + '{', U_VOID, U_VOID, '}', U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '\\', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_sg_latin1[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\'', '^', 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', 0xefc0, U_VOID, 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xef94, + 0xef84, 0xefa7, U_VOID, '$', 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_sg_latin1[] = +{ + U_VOID, 27, '+', '"', '*', 0xef80, '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', 0xef9a, '!', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xef99, + 0xef8e, 0xefb0, U_VOID, 0xefa3, 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_sg_latin1[] = +{ + U_VOID, U_VOID, 0xefb3, '@', '#', U_VOID, U_VOID, 0xefaa, + '|', 0xefa2, U_VOID, U_VOID, '\'', '~', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '[', ']', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, 0xefe9, + '{', U_VOID, U_VOID, '}', U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '\\', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_no[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '+', '\\', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '}', '~', 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', '|', + '{', '|', U_VOID, '\'', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_no[] = +{ + U_VOID, 27, '!', '"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', ']', '^', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', '\\', + '[', U_VOID, U_VOID, '*', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_no[] = +{ + U_VOID, U_VOID, U_VOID, '@', U_VOID, '$', U_VOID, U_VOID, + '{', '[', ']', '}', U_VOID, '\'', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, '~', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_no_latin1[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '+', '\\', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', 0xef86, DKY_DEAD_DIAERESIS, 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xef9b, + 0xef91, '|', U_VOID, '\'', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_no_latin1[] = +{ + U_VOID, 27, '!', '"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', DKY_DEAD_GRAVE, 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', 0xef8f, DKY_DEAD_CIRCUMFLEX, 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xef9d, + 0xef92, 0xeff5, U_VOID, '*', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_no_latin1[] = +{ + U_VOID, U_VOID, U_VOID, '@', 0xef9c, 0xefcf, U_VOID, U_VOID, + '{', '[', ']', '}', U_VOID, DKY_DEAD_ACUTE, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, DKY_DEAD_TILDE, 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_sf[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\'', '^', 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', U_VOID, U_VOID, 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', U_VOID, + U_VOID, U_VOID, U_VOID, '$', 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_sf[] = +{ + U_VOID, 27, '+', '"', '*', U_VOID, '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', U_VOID, '!', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_sf[] = +{ + U_VOID, U_VOID, U_VOID, '@', '#', U_VOID, U_VOID, U_VOID, + '|', U_VOID, U_VOID, U_VOID, '\'', '~', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '[', ']', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + '{', U_VOID, U_VOID, '}', U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '\\', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_sf_latin1[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\'', '^', 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', 0xefe8, 0xefa8, 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xefe9, + 0xefe0, 0xefa7, U_VOID, '$', 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_sf_latin1[] = +{ + U_VOID, 27, '+', '"', '*', 0xefe7, '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', 0xeffc, '!', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xeff6, + 0xefe4, 0xefb0, U_VOID, 0xefa3, 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_sf_latin1[] = +{ + U_VOID, U_VOID, U_VOID, '@', '#', U_VOID, U_VOID, 0xefac, + '|', 0xefa2, U_VOID, U_VOID, 0xefb4, '~', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '[', ']', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + '{', U_VOID, U_VOID, '}', U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '\\', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_es[] = { + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\'', U_VOID, 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '`', '+', 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', U_VOID, + '\'', '`', U_VOID, U_VOID, 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_es[] = { + U_VOID, 27, '!', '"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', U_VOID, 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '^', '*', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', U_VOID, + U_VOID, '~', U_VOID, U_VOID, 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_es[] = { + U_VOID, U_VOID, '|', '@', '#', '$', U_VOID, U_VOID, + '{', '[', ']', '}', '\\', '~', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '[', ']', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + '{', '\\', U_VOID, '}', U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '~', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_es_latin1[] = { + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\'', 0xefad, 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', DKY_DEAD_GRAVE, '+', 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xefa4, + DKY_DEAD_ACUTE, 0xefa7, U_VOID, 0xef87, 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_es_latin1[] = { + U_VOID, 27, '!', '"', 0xeffa, '$', '%', '&', + '/', '(', ')', '=', '?', 0xefa8, 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', DKY_DEAD_CIRCUMFLEX, '*', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xefa5, + DKY_DEAD_DIAERESIS, 0xefa6, U_VOID, 0xef80, 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_es_latin1[] = { + U_VOID, U_VOID, '|', '@', '#', '$', U_VOID, 0xefac, + '{', '[', ']', '}', '\\', '~', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '[', ']', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + '{', '\\', U_VOID, '}', U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '~', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +/* keyboard driver for BELGIAN KEYBOARD */ + +CONST t_keysym key_map_be[] = +{ + U_VOID, 27, '&', 0xef82, '"', '\'', '(', 21, + 0xef8a, '!', 0xef80, 0xef85, ')', '-', 127, 9, + 'a', 'z', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', DKY_DEAD_CIRCUMFLEX, '$', 13, U_VOID, 'q', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', + 0xef97, 0xeffd, U_VOID, 0xefe6, 'w', 'x', 'c', 'v', + 'b', 'n', ',', ';', ':', '=', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_be[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', 0xeff8, '_', 127, 9, + 'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', DKY_DEAD_DIAERESIS, '*', 13, U_VOID, 'Q', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', + '%', 0xeffc, U_VOID, 0xef9c, 'W', 'X', 'C', 'V', + 'B', 'N', '?', '.', '/', '+', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_be[] = +{ + U_VOID, U_VOID, '|', '@', '#', U_VOID, U_VOID, '^', + U_VOID, U_VOID, '{', '}', DKY_DEAD_ABOVERING, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '[', ']', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + DKY_DEAD_ACUTE, DKY_DEAD_GRAVE, U_VOID, DKY_DEAD_GRAVE, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, DKY_DEAD_CEDILLA, U_VOID, U_VOID, DKY_DEAD_TILDE, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '\\', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_po[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '`', 0xefae, 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '+', DKY_DEAD_ACUTE, 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xef87, + 0xefa7, '\\', U_VOID, DKY_DEAD_TILDE, 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_po[] = +{ + U_VOID, 27, '!', '"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', 0xefaf, 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '*', DKY_DEAD_GRAVE, 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xef80, + 0xefa6, '|', '0', DKY_DEAD_CIRCUMFLEX, 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_po[] = +{ + U_VOID, U_VOID, U_VOID, '@', 0xef9c, 21, U_VOID, U_VOID, + '{', '[', ']', '}', U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, DKY_DEAD_DIAERESIS, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_it[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\'', 0xef8d, 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', 0xef8a, '+', 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xef95, + 0xef85, '\\', U_VOID, 0xef97, 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_it[] = +{ + U_VOID, 27, '!', '"', 0xef9c, '$', '%', '&', + '/', '(', ')', '=', '?', '^', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', 0xef82, '*', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xef87, + 0xeff8, '|', '0', 21, 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_it[] = +{ + U_VOID, U_VOID, U_VOID, '@', U_VOID, '$', U_VOID, U_VOID, + '{', '[', ']', '}', '{', '}', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '[', ']', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '@', + '#', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_sw[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '+', '\'', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', 0xef86, '~', 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xef94, + 0xef84, 21, U_VOID, '\'', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_sw[] = +{ + 0xefab, U_VOID, '!', '"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '`', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', 0xef8f, '^', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xef99, + 0xef8e, 0xefab, U_VOID, '*', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_sw[] = +{ + U_VOID, U_VOID, U_VOID, '@', 0xef9c, '$', U_VOID, U_VOID, + '{', '[', ']', '}', '\\', U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, '~', 13, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_hu[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', 0xef94, 0xef81, 0xefa2, 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', 0xef8b, 0xefa3, 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xef82, + 0xefa0, '0', U_VOID, 0xeffb, 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, 0xefa1, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_hu[] = +{ + U_VOID, 27, '\'', '"', '+', '!', '%', '/', + '=', '(', ')', 0xef99, 0xef9a, 224, 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', 0xef8a, 0xefe9, 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xef90, + 0xefb5, 21, '0', 0xefeb, 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', '?', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, 0xef92, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_hu[] = +{ + U_VOID, U_VOID, '~', DKY_DEAD_CARON, DKY_DEAD_CIRCUMFLEX, DKY_DEAD_BREVE, 0xeff8, DKY_DEAD_OGONEK, + DKY_DEAD_GRAVE, DKY_DEAD_ABOVEDOT, DKY_DEAD_ACUTE, DKY_DEAD_DOUBLEACUTE, DKY_DEAD_DIAERESIS, DKY_DEAD_CEDILLA, U_VOID, U_VOID, + '\\', '|', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, 0xef92, + U_VOID, U_VOID, 0xeff6, 0xef9e, 13, U_VOID, U_VOID, 0xefd0, + 0xefd1, '[', ']', U_VOID, 0xefa1, 0xef88, 0xef9d, '$', + 0xefe1, U_VOID, U_VOID, 0xefcf, '>', '#', '&', '@', + '{', '}', U_VOID, ';', U_VOID, '*', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_hu_cwi[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', 0xef94, 0xef81, 0xefa2, 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', 0xef93, 0xefa3, 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xef82, + 0xefa0, '0', U_VOID, 0xef96, 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, 0xefa1, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_hu_cwi[] = +{ + U_VOID, 27, '\'', '"', '+', '!', '%', '/', + '=', '(', ')', 0xef99, 0xef9a, 0xef95, 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', 0xefa7, 0xef97, 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xef90, + 0xef8f, 21, '0', 0xef98, 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', '?', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, 0xef8d, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_hu_cwi[] = +{ + U_VOID, U_VOID, '~', DKY_DEAD_CARON, DKY_DEAD_CIRCUMFLEX, DKY_DEAD_BREVE, 0xeff8, DKY_DEAD_OGONEK, + DKY_DEAD_GRAVE, DKY_DEAD_ABOVEDOT, DKY_DEAD_ACUTE, DKY_DEAD_DOUBLEACUTE, DKY_DEAD_DIAERESIS, DKY_DEAD_CEDILLA, U_VOID, U_VOID, + '\\', '|', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, 0xef8d, + U_VOID, U_VOID, 0xeff6, U_VOID, 13, U_VOID, U_VOID, U_VOID, + U_VOID, '[', ']', U_VOID, 0xefa1, U_VOID, U_VOID, '$', + 0xefe1, U_VOID, U_VOID, U_VOID, '>', '#', '&', '@', + '{', '}', U_VOID, ';', U_VOID, '*', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_hu_latin2[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', 0xeff6, 0xeffc, 0xeff3, 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', 0xeff5, 0xeffa, 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xefe9, + 0xefe1, '0', U_VOID, 0xeffb, 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, 0xefed, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_hu_latin2[] = +{ + U_VOID, 27, '\'', '"', '+', '!', '%', '/', + '=', '(', ')', 0xefd6, 0xefdc, 0xefd3, 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', 0xefd5, 0xefda, 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xefc9, + 0xefc1, 21, '0', 0xefdb, 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', '?', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, 0xefcd, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_hu_latin2[] = +{ + U_VOID, U_VOID, '~', DKY_DEAD_CARON, DKY_DEAD_CIRCUMFLEX, DKY_DEAD_BREVE, 0xeff8, DKY_DEAD_OGONEK, + DKY_DEAD_GRAVE, DKY_DEAD_ABOVEDOT, DKY_DEAD_ACUTE, DKY_DEAD_DOUBLEACUTE, DKY_DEAD_DIAERESIS, DKY_DEAD_CEDILLA, U_VOID, U_VOID, + '\\', '|', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, 0xefcd, + U_VOID, U_VOID, 0xeff7, 0xefd7, 13, U_VOID, U_VOID, 0xeff0, + 0xefd0, '[', ']', U_VOID, 0xefed, 0xefb3, 0xefa3, '$', + 0xefdf, U_VOID, U_VOID, 0xefa4, '>', '#', '&', '@', + '{', '}', U_VOID, ';', U_VOID, '*', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_jp106[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '^', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '@', '[', 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + ':', '`', U_VOID, ']', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '0', U_VOID, + U_VOID, '\\', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, '\\', U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '\\', U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_jp106[] = +{ + U_VOID, 27, '!', '"', '#', '$', '%', '&', + '\'', '(', ')', '~', '=', '~', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '`', '{', DKY_DEAD_GRAVE, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', '+', + '*', '~', '0', '}', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '0', U_VOID, + U_VOID, '_', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, '_', U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_jp106[] = +{ + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + + +/* Polish keyboard */ +CONST t_keysym key_map_pl[] = +{ + U_VOID,27,'1','2','3','4','5','6', + '7','8','9','0','-','=',127,9, + 'q','w','e','r','t','y','u','i', + 'o','p','[',']',13,U_VOID,'a','s', + 'd','f','g','h','j','k','l',';', + 39,96,U_VOID,92,'z','x','c','v', + 'b','n','m',',','.','/',U_VOID,'*', + U_VOID,32,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,'-',U_VOID,U_VOID,U_VOID,'+',U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID}; + +CONST t_keysym shift_map_pl[] = +{ + U_VOID,27,'!','@','#','$','%','^', + '&','*','(',')','_','+',127,9, + 'Q','W','E','R','T','Y','U','I', + 'O','P','{','}',13,U_VOID,'A','S', + 'D','F','G','H','J','K','L',':', + 34,'~',U_VOID,'|','Z','X','C','V', + 'B','N','M','<','>','?',U_VOID,'*', + U_VOID,32,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,'-',U_VOID,U_VOID,U_VOID,'+',U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID}; + +CONST t_keysym alt_map_pl[] = +{ + U_VOID,U_VOID,U_VOID,'@',U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,0x0119,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + 0x00f3,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,0x0105,0x015b, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,0x0142,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,0x017c,0x017a,0x0107,U_VOID, + U_VOID,0x0144,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID}; + +CONST t_keysym shift_alt_map_pl[] = +{ + U_VOID,U_VOID,U_VOID,'@',U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,0x0118,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + 0x00d3,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,0x0104,0x015a, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,0x0141,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,0x017b,0x0179,0x0106,U_VOID, + U_VOID,0x0143,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID}; + + +CONST t_keysym key_map_hr_cp852[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\'', '+', 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', 0xefe7, 0xefd0, 13, U_VOID, 'a', 's', /*231=s, 208=d*/ + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xef9f, /*159=c tvrdo*/ + 0xef86, U_VOID, U_VOID, 0xefa7, 'y', 'x', 'c', 'v', /*134=c meko,167=z*/ + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_hr_cp852[] = +{ + U_VOID, 27, '!', '"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '*', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', 0xefe6, 0xefd1, 13, U_VOID, 'A', 'S', /*230=S, 209=D*/ + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xefac, /*172=C tvrdo*/ + 0xef8f, U_VOID, U_VOID, 0xefa6, 'Y', 'X', 'C', 'V', /*143=C meko,166=Z*/ + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_hr_cp852[] = +{ + U_VOID, U_VOID, '~', 0xefb7, '^', 0xeff4, 0xeff8, 0xeff2, + '`', 0xeffa, 0xefef, 0xeff1, 0xeff9, 0xeff7, U_VOID, U_VOID, + '\\', '|', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, 0xeff6, 0xef9e, 13, U_VOID, U_VOID, U_VOID, + U_VOID, '[', ']', U_VOID, U_VOID, 0xef92, 0xef91, '\\', + 0xefe1, U_VOID, U_VOID, 0xefcf, U_VOID, U_VOID, U_VOID, '@', + '{', '}', 0xeff5, U_VOID, '|', '/', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_hr_latin2[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '\'', '+', 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', 0xefb9, 0xeff0, 13, U_VOID, 'a', 's', /*185=s, 240=d*/ + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xefe8, /*232=c tvrdo*/ + 0xefe6, U_VOID, U_VOID, 0xefbe, 'y', 'x', 'c', 'v', /*230=c meko,190=z*/ + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_hr_latin2[] = +{ + U_VOID, 27, '!', '"', '#', '$', '%', '&', + '/', '(', ')', '=', '?', '*', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', 0xefa9, 0xefd0, 13, U_VOID, 'A', 'S', /*169=S, 208=D*/ + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xefc8, /*200=C tvrdo*/ + 0xefc6, U_VOID, U_VOID, 0xefae, 'Y', 'X', 'C', 'V', /*198=C meko,174=Z*/ + 'B', 'N', 'M', ';', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '>', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_hr_latin2[] = +{ + U_VOID, U_VOID, '~', 0xefb7, '^', 0xefa2, 0xefb0, 0xefb2, + '`', 0xefff, 0xefb4, 0xefbd, 0xefa8, 0xefb8, U_VOID, U_VOID, + '\\', '|', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, 0xeff7, 0xefd7, 13, U_VOID, U_VOID, U_VOID, + U_VOID, '[', ']', U_VOID, U_VOID, 0xefb3, 0xefa3, '\\', + 0xefdf, U_VOID, U_VOID, 0xefa4, U_VOID, U_VOID, U_VOID, '@', + '{', '}', 0xefa7, U_VOID, '|', '/', U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '|', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + + +/* Czech keyboard QWERTY */ +CONST t_keysym key_map_cz_qwerty[] = +{ + U_VOID, 27, '+', 0xefd8, 0xefe7, 0xef9f, 0xeffd, 0xefa7, + 0xefec, 0xefa0, 0xefa1, 0xef82, '=', DKY_DEAD_ACUTE, 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', 0xefa3, ')', 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xef85, + 0xeff5, ';', U_VOID, DKY_DEAD_DIAERESIS, 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '&', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_cz_qwerty[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '%', DKY_DEAD_CARON, 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '/', '(', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', '"', + '!', 0xeff8, U_VOID, '\'', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '?', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '*', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_cz_qwerty[] = +{ + U_VOID, U_VOID, '~', DKY_DEAD_CARON, DKY_DEAD_CIRCUMFLEX, DKY_DEAD_BREVE, 0xeff8, DKY_DEAD_OGONEK, + DKY_DEAD_GRAVE, DKY_DEAD_ABOVEDOT, DKY_DEAD_ACUTE, DKY_DEAD_DOUBLEACUTE, DKY_DEAD_DIAERESIS, DKY_DEAD_CEDILLA, U_VOID, U_VOID, + '\\', '|', 0xefa9, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + 0xefa2, U_VOID, 0xeff6, 0xef9e, U_VOID, U_VOID, 0xefa5, 0xefd0, + 0xefd1, '[', ']', U_VOID, U_VOID, 0xef88, 0xef9d, '$', + 0xefe1, U_VOID, U_VOID, 0xefcf, '>', '#', 0xef86, '@', + '{', '}', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +/* Czech keyboard QWERTZ */ +CONST t_keysym key_map_cz_qwertz[] = +{ + U_VOID, 27, '+', 0xefd8, 0xefe7, 0xef9f, 0xeffd, 0xefa7, + 0xefec, 0xefa0, 0xefa1, 0xef82, '=', DKY_DEAD_ACUTE, 127, 9, + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', + 'o', 'p', 0xefa3, ')', 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xef85, + 0xeff5, ';', U_VOID, DKY_DEAD_DIAERESIS, 'y', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '-', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '&', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_cz_qwertz[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '%', DKY_DEAD_CARON, 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', + 'O', 'P', '/', '(', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', '"', + '!', 0xeff8, U_VOID, '\'', 'Y', 'X', 'C', 'V', + 'B', 'N', 'M', '?', ':', '_', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '*', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_cz_qwertz[] = +{ + U_VOID, U_VOID, '~', DKY_DEAD_CARON, DKY_DEAD_CIRCUMFLEX, DKY_DEAD_BREVE, 0xeff8, DKY_DEAD_OGONEK, + DKY_DEAD_GRAVE, DKY_DEAD_ABOVEDOT, DKY_DEAD_ACUTE, DKY_DEAD_DOUBLEACUTE, DKY_DEAD_DIAERESIS, DKY_DEAD_CEDILLA, U_VOID, U_VOID, + '\\', '|', 0xefa9, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + 0xefa2, U_VOID, 0xeff6, 0xef9e, U_VOID, U_VOID, 0xefa5, 0xefd0, + 0xefd1, '[', ']', U_VOID, U_VOID, 0xef88, 0xef9d, '$', + 0xefe1, U_VOID, U_VOID, 0xefcf, '>', '#', 0xef86, '@', + '{', '}', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, '<', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_ru[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', 127, 9, + 0x439,0x446,0x443,0x43A,0x435,0x43D,0x433,0x448,0x449,0x437,0x445,0x44A, + 13, U_VOID, + 0x444,0x44B,0x432,0x430,0x43F,0x440,0x43E,0x43B,0x434,0x436,0x44D,0x451, + U_VOID, '\\', + 0x44F,0x447,0x441,0x43C,0x438,0x442,0x44C,0x431,0x44E, + '/', U_VOID, '*', + U_VOID, 32, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_ru[] = +{ + U_VOID, 27, '!', '@', '#', '$', '%', '^', + '&', '*', '(', ')', '_', '+', 127, 9, + 0x419,0x426,0x423,0x41A,0x415,0x41D,0x413,0x428,0x429,0x417,0x425,0x42A, + 13, U_VOID, + 0x424,0x42B,0x412,0x410,0x41F,0x420,0x41E,0x41B,0x414,0x416,0x42D,0x401, + U_VOID, '\\', + 0x42F,0x427,0x421,0x41C,0x418,0x422,0x42C,0x411,0x42E, + '/', U_VOID, '*', + U_VOID, 32, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_ru[] = +{ + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym ctrl_map_ru[] = +{ + U_VOID, 0x1b, U_VOID, 0x00, U_VOID, U_VOID, U_VOID, 0x1e, + U_VOID, U_VOID, U_VOID, U_VOID, 0x1f, U_VOID, 0x7f, U_VOID, + 0x11, 0x17, 0x05, 0x12, 0x14, 0x19, 0x15, 0x09, + 0x0f, 0x10, 0x1b, 0x1d, 0x0a, U_VOID, 0x01, 0x13, + 0x04, 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, U_VOID, + U_VOID, U_VOID, U_VOID, 0x1c, 0x1a, 0x18, 0x03, 0x16, + 0x02, 0x0e, 0x0d, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, 0x20, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym key_map_tr[] = { + U_VOID,27,'1','2','3','4','5','6','7','8','9','0','*','-',127,9, + 'q','w','e','r','t','y','u',0x131,'o','p',0x11f,0xfc,13,U_VOID, + 'a','s','d','f','g','h','j','k','l',0x15f,'i',0xe9,U_VOID,44, + 'z','x','c','v','b','n','m',0xf6,0xe7,'.',U_VOID, + '*',U_VOID,' ',U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,'-',U_VOID,U_VOID,U_VOID,'+',U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,'<',U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID +}; +CONST t_keysym shift_map_tr[] = +{ + U_VOID,27,'!','\'','^','+','%','&','/','(',')','=','?','_',127,9, + 'Q','W','E','R','T','Y','U','I','O','P',0x11e,0xdc,13,U_VOID, + 'A','S','D','F','G','H','J','K','L',0x15e,0x130,34,U_VOID,';', + 'Z','X','C','V','B','N','M',0xd6,0xc7,':',U_VOID, + '*',U_VOID,' ',U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,'-',U_VOID,U_VOID,U_VOID,'+',U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,'>', + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID +}; +CONST t_keysym alt_map_tr[] = +{ + U_VOID,U_VOID,U_VOID,U_VOID,'#','$',U_VOID,U_VOID,'{','[',']','}','\\', + U_VOID,U_VOID,U_VOID, + '@',U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + '~',U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,'`',U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,'|',U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID,U_VOID, + U_VOID,U_VOID,U_VOID +}; + +/* this prefereable is overloaded via '-I keytable keyb-user' + * and is preset with an US keyboard layout + */ + +CONST t_keysym key_map_user[] = +{ + U_VOID, 27, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', 127, 9, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', 13, U_VOID, 'a', 's', + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', U_VOID, '\\', 'z', 'x', 'c', 'v', + 'b', 'n', 'm', ',', '.', '/', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym shift_map_user[] = +{ + U_VOID, 27, '!', '@', '#', '$', '%', '^', + '&', '*', '(', ')', '_', '+', 127, 9, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '{', '}', 13, U_VOID, 'A', 'S', + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', + '"', '~', '0', '|', 'Z', 'X', 'C', 'V', + 'B', 'N', 'M', '<', '>', '?', U_VOID, '*', + U_VOID, ' ', U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, '-', U_VOID, U_VOID, U_VOID, '+', U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + +CONST t_keysym alt_map_user[] = +{ + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, U_VOID, + U_VOID +}; + + +CONST t_keysym num_table_dot[] = { '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', '.', '\0' }; +CONST t_keysym num_table_comma[] = { '7', '8', '9', '-', '4', '5', '6', '+', '1', '2', '3', '0', ',', '\0' }; + +#define CT(X) (sizeof(X)/sizeof(X[0])) +struct keytable_entry keytable_list[] = { + /* US must be first in the table, no other rules about order */ + {"us", KEYB_US, 0, CT(key_map_us), CT(num_table_comma), + key_map_us, shift_map_us, alt_map_us, + num_table_dot, ctrl_map_us}, + {"finnish", KEYB_FINNISH, 0, CT(key_map_finnish), CT(num_table_comma), + key_map_finnish, shift_map_finnish, alt_map_finnish, + num_table_comma,}, + {"finnish-latin1", KEYB_FINNISH_LATIN1, 0, CT(key_map_finnish_latin1), CT(num_table_comma), + key_map_finnish_latin1, shift_map_finnish_latin1, alt_map_finnish_latin1, + num_table_comma,}, + {"uk", KEYB_UK, 0, CT(key_map_uk), CT(num_table_comma), + key_map_uk, shift_map_uk, alt_map_uk, + num_table_dot,}, + {"de", KEYB_DE, 0, CT(key_map_de), CT(num_table_comma), + key_map_de, shift_map_de, alt_map_de, + num_table_comma, ctrl_map_de}, + {"de-latin1", KEYB_DE_LATIN1, 0, CT(key_map_de_latin1), CT(num_table_comma), + key_map_de_latin1, shift_map_de_latin1, alt_map_de_latin1, + num_table_comma,}, + {"fr", KEYB_FR, 0, CT(key_map_fr), CT(num_table_comma), + key_map_fr, shift_map_fr, alt_map_fr, + num_table_dot,}, + {"fr-latin1", KEYB_FR_LATIN1, 0, CT(key_map_fr_latin1), CT(num_table_comma), + key_map_fr_latin1, shift_map_fr_latin1, alt_map_fr_latin1, + num_table_dot,}, + {"dk", KEYB_DK, 0, CT(key_map_dk), CT(num_table_comma), + key_map_dk, shift_map_dk, alt_map_dk, + num_table_comma,}, + {"dk-latin1", KEYB_DK_LATIN1, 0, CT(key_map_dk_latin1), CT(num_table_comma), + key_map_dk_latin1, shift_map_dk_latin1, alt_map_dk_latin1, + num_table_comma,}, + {"dvorak", KEYB_DVORAK, 0, CT(key_map_dvorak), CT(num_table_comma), + key_map_dvorak, shift_map_dvorak, alt_map_dvorak, + num_table_comma,}, + {"sg", KEYB_SG, 0, CT(key_map_sg), CT(num_table_comma), + key_map_sg, shift_map_sg, alt_map_sg, + num_table_comma,}, + {"sg-latin1", KEYB_SG_LATIN1, 0, CT(key_map_sg_latin1), CT(num_table_comma), + key_map_sg_latin1, shift_map_sg_latin1, alt_map_sg_latin1, + num_table_comma,}, + {"keyb-no", KEYB_NO, 0, CT(key_map_no), CT(num_table_comma), + key_map_no, shift_map_no, alt_map_no, + num_table_comma,}, + {"no-latin1", KEYB_NO_LATIN1, 0, CT(key_map_no_latin1), CT(num_table_comma), + key_map_no_latin1, shift_map_no_latin1, alt_map_no_latin1, + num_table_comma,}, + {"sf", KEYB_SF, 0, CT(key_map_sf), CT(num_table_comma), + key_map_sf, shift_map_sf, alt_map_sf, + num_table_comma,}, + {"sf-latin1", KEYB_SF_LATIN1, 0, CT(key_map_sf_latin1), CT(num_table_comma), + key_map_sf_latin1, shift_map_sf_latin1, alt_map_sf_latin1, + num_table_comma,}, + {"es", KEYB_ES, 0, CT(key_map_es), CT(num_table_comma), + key_map_es, shift_map_es, alt_map_es, + num_table_comma,}, + {"es-latin1", KEYB_ES_LATIN1, 0, CT(key_map_es_latin1), CT(num_table_comma), + key_map_es_latin1, shift_map_es_latin1, alt_map_es_latin1, + num_table_comma,}, + {"be", KEYB_BE, 0, CT(key_map_be), CT(num_table_comma), + key_map_be, shift_map_be, alt_map_be, + num_table_dot,}, + {"po", KEYB_PO, 0, CT(key_map_po), CT(num_table_comma), + key_map_po, shift_map_po, alt_map_po, + num_table_dot,}, + {"it", KEYB_IT, 0, CT(key_map_it), CT(num_table_comma), + key_map_it, shift_map_it, alt_map_it, + num_table_dot,}, + {"sw", KEYB_SW, 0, CT(key_map_sw), CT(num_table_comma), + key_map_sw, shift_map_sw, alt_map_sw, + num_table_dot,}, + {"hu", KEYB_HU, 0, CT(key_map_hu), CT(num_table_comma), + key_map_hu, shift_map_hu, alt_map_hu, + num_table_comma,}, + {"hu-cwi", KEYB_HU_CWI, 0, CT(key_map_hu_cwi), CT(num_table_comma), + key_map_hu_cwi, shift_map_hu_cwi, alt_map_hu_cwi, + num_table_comma,}, + {"hu-latin2", KEYB_HU_LATIN2, 0, CT(key_map_hu_latin2), CT(num_table_comma), + key_map_hu_latin2, shift_map_hu_latin2, alt_map_hu_latin2, + num_table_comma,}, + {"jp106", KEYB_JP106, 0, CT(key_map_jp106), CT(num_table_comma), + key_map_jp106, shift_map_jp106, alt_map_jp106, + num_table_dot,}, + {"pl", KEYB_PL, 0, CT(key_map_pl), CT(num_table_comma), + key_map_pl, shift_map_pl, alt_map_pl, + num_table_comma, 0, shift_alt_map_pl,}, + {"hr-cp852", KEYB_HR_CP852, 0, CT(key_map_hr_cp852), CT(num_table_comma), + key_map_hr_cp852, shift_map_hr_cp852, alt_map_hr_cp852, + num_table_comma,}, + {"hr-latin2", KEYB_HR_LATIN2, 0, CT(key_map_hr_latin2), CT(num_table_comma), + key_map_hr_latin2, shift_map_hr_latin2, alt_map_hr_latin2, + num_table_comma,}, + {"cz-qwerty", KEYB_CZ_QWERTY, 0, CT(key_map_cz_qwerty), CT(num_table_comma), + key_map_cz_qwerty, shift_map_cz_qwerty, alt_map_cz_qwerty, + num_table_dot,}, + {"cz-qwertz", KEYB_CZ_QWERTZ, 0, CT(key_map_cz_qwertz), CT(num_table_comma), + key_map_cz_qwertz, shift_map_cz_qwertz, alt_map_cz_qwertz, + num_table_comma,}, + {"ru", KEYB_RU, 0, CT(key_map_ru), CT(num_table_dot), + key_map_ru, shift_map_ru, alt_map_ru, + num_table_dot, ctrl_map_ru}, + {"tr", KEYB_TR, 0, CT(key_map_tr), CT(num_table_dot), + key_map_tr, shift_map_tr, alt_map_tr, + num_table_dot,}, + {"keyb-user", KEYB_USER, 0, CT(key_map_user), CT(num_table_dot), + key_map_user, shift_map_user, alt_map_user, + num_table_dot,}, + {0}, + {0}, + {0} +}; + +#if 0 +static char* pretty_keysym(t_keysym d) +{ + static char b[100]; + char *s = b; + + if(d < 0x20 || d == 0x7f || d >= 0x80) { + sprintf(b, "0x%02x", d); + } + else if (d >= 0x80) { + sprintf(b, "0x%04x", d); + } + else { + sprintf(b, "'%c' ", (char) d); + } + + if(d == '\'') s = "'\\''"; + if(d == DKY_VOID) s = " -- "; + + return s; +} +#endif + +/* + * Read the console keyboard description and try to build + * a DOSEMU compatible map from it. + * + * NOTE: If you use X you might get the *wrong* mapping + * (e.g. on remote machines)... :-) + * + * we now compare this map and all the keymaps above + * with the X keymap (if X is available) and chose the + * best one. + */ +void setup_default_keytable(void) +{ + static const char *dt_name = "auto"; + static t_keysym + plain_map[NUM_KEY_NUMS], + shift_map[NUM_KEY_NUMS], + alt_map[NUM_KEY_NUMS], + num_map[14], + ctrl_map[NUM_KEY_NUMS], + shift_alt_map[NUM_KEY_NUMS], + ctrl_alt_map[NUM_KEY_NUMS]; + struct keytable_entry *kt, *altkt; + int i, idx; +#if defined(XKMAPS_SUPPORT) && defined(USE_DL_PLUGINS) + void *handle; +#endif + + idx = sizeof keytable_list / sizeof *keytable_list - 3; + + k_printf("KBD: setup_default_keytable: setting up table %d\n", idx); + + config.keytable = kt = keytable_list + idx; + config.altkeytable = altkt = kt + 1; + + kt->name = dt_name; + kt->keyboard = KEYB_AUTO; + kt->flags = 0; + kt->sizemap = CT(plain_map); + kt->sizepad = CT(num_map); + kt->key_map = plain_map; + kt->shift_map = shift_map; + kt->alt_map = alt_map; + kt->num_table = num_map; + kt->ctrl_map = ctrl_map; + kt->shift_alt_map = shift_alt_map; + kt->ctrl_alt_map = ctrl_alt_map; + + memcpy(altkt, kt, sizeof(*kt)); + altkt->name = NULL; + altkt->keyboard = KEYB_AUTO; + altkt->flags = 0; + + /* Initialize everything to unknown */ + for(i = 0; i < NUM_KEY_NUMS; i++) { + plain_map[i] = U_VOID; + shift_map[i] = U_VOID; + alt_map[i] = U_VOID; + ctrl_map[i] = U_VOID; + shift_alt_map[i] = U_VOID; + ctrl_alt_map[i] = U_VOID; + } + /* Copy in the us keymap for a default */ + memcpy(plain_map, key_map_us, sizeof(key_map_us)); + memcpy(shift_map, shift_map_us, sizeof(shift_map_us)); + memcpy(alt_map, alt_map_us, sizeof(alt_map_us)); + memcpy(num_map, num_table_dot, sizeof(num_table_dot)); + memcpy(ctrl_map, ctrl_map_us, sizeof(ctrl_map_us)); + + /* Now copy parameters for the linux kernel keymap */ + if(read_kbd_table(kt, altkt)) { + k_printf("setup_default_keytable: failed to read kernel console keymap\n"); + kt->name = NULL; + } + + idx = 1; +#ifdef XKMAPS_SUPPORT +#ifdef USE_DL_PLUGINS + handle = load_plugin("XKmaps"); + if (handle && XDetectLayout) + idx = XDetectLayout(); +#else + idx = X11_DetectLayout(); +#endif +#endif + if (idx && kt->name == NULL) { + error("Unable to open console or check with X to evaluate the keyboard " + "map.\nPlease specify your keyboard map explicitly via the " + "$_layout option. Defaulting to US.\n"); + config.keytable = keytable_list; /* US must be first */ + } +} diff --git a/src/base/kbd_unicode/keynum.c b/src/base/kbd_unicode/keynum.c new file mode 100644 index 0000000..ee90f36 --- /dev/null +++ b/src/base/kbd_unicode/keynum.c @@ -0,0 +1,165 @@ +#include "keynum.h" + +t_keynum validate_keynum(t_keynum key) +{ + switch(key) { + /* shift keys */ + case NUM_L_ALT: + case NUM_R_ALT: + case NUM_L_CTRL: + case NUM_R_CTRL: + case NUM_L_SHIFT: + case NUM_R_SHIFT: + case NUM_NUM: + case NUM_SCROLL: + case NUM_CAPS: + + /* main block */ + + case NUM_SPACE: + case NUM_BKSP: + case NUM_RETURN: + case NUM_TAB: + case NUM_A: + case NUM_B: + case NUM_C: + case NUM_D: + case NUM_E: + case NUM_F: + case NUM_G: + case NUM_H: + case NUM_I: + case NUM_J: + case NUM_K: + case NUM_L: + case NUM_M: + case NUM_N: + case NUM_O: + case NUM_P: + case NUM_Q: + case NUM_R: + case NUM_S: + case NUM_T: + case NUM_U: + case NUM_V: + case NUM_W: + case NUM_X: + case NUM_Y: + case NUM_Z: + case NUM_1: + case NUM_2: + case NUM_3: + case NUM_4: + case NUM_5: + case NUM_6: + case NUM_7: + case NUM_8: + case NUM_9: + case NUM_0: + case NUM_DASH: + case NUM_EQUALS: + case NUM_LBRACK: + case NUM_RBRACK: + case NUM_SEMICOLON: + case NUM_APOSTROPHE: /* ' */ + case NUM_GRAVE: /* ` */ + case NUM_BACKSLASH: + case NUM_COMMA: + case NUM_PERIOD: + case NUM_SLASH: + case NUM_LESSGREATER: /* not on US keyboards */ + + /* keypad */ + + case NUM_PAD_0: + case NUM_PAD_1: + case NUM_PAD_2: + case NUM_PAD_3: + case NUM_PAD_4: + case NUM_PAD_5: + case NUM_PAD_6: + case NUM_PAD_7: + case NUM_PAD_8: + case NUM_PAD_9: + case NUM_PAD_DECIMAL: + case NUM_PAD_SLASH: + case NUM_PAD_AST: + case NUM_PAD_MINUS: + case NUM_PAD_PLUS: + case NUM_PAD_ENTER: + + + /* function keys */ + + case NUM_ESC: + case NUM_F1: + case NUM_F2: + case NUM_F3: + case NUM_F4: + case NUM_F5: + case NUM_F6: + case NUM_F7: + case NUM_F8: + case NUM_F9: + case NUM_F10: + case NUM_F11: + case NUM_F12: + case NUM_F13: + case NUM_F14: + case NUM_F15: + case NUM_F16: + case NUM_F17: + case NUM_F18: + case NUM_F19: + case NUM_F20: + case NUM_F21: + case NUM_F22: + case NUM_F23: + case NUM_F24: + + /* cursor block */ + + case NUM_INS: + case NUM_DEL: + case NUM_HOME: + case NUM_END: + case NUM_PGUP: + case NUM_PGDN: + case NUM_UP: + case NUM_DOWN: + case NUM_LEFT: + case NUM_RIGHT: + + /* New keys on a Microsoft Windows keyboard */ + case NUM_LWIN: + case NUM_RWIN: + case NUM_MENU: + + /* Multimedia/misc keys */ + case NUM_MUTE: + case NUM_VOLUMEDOWN: + case NUM_VOLUMEUP: + case NUM_AC_BOOKMARKS: + + /* Dual scancode keys */ + case NUM_PRTSCR_SYSRQ: + case NUM_PAUSE_BREAK: + + /* These are logical rather than physical keys: + * The scancodes for the keys exist but the keys really don't + */ + case NUM_FAKE_L_SHIFT: + case NUM_FAKE_R_SHIFT: + case NUM_PRTSCR: + case NUM_SYSRQ: + case NUM_PAUSE: + case NUM_BREAK: + + break; + default: + key = NUM_VOID; + break; + } + return key; +} + diff --git a/src/base/kbd_unicode/prestroke.c b/src/base/kbd_unicode/prestroke.c new file mode 100644 index 0000000..c16b00f --- /dev/null +++ b/src/base/kbd_unicode/prestroke.c @@ -0,0 +1,214 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* This is file prestroke.c + * + * (C) 1997 under GPL, Hans Lermen + * (put under DOSEMU-policy 1998, -Hans) + */ + +#include +#include +#include "emu.h" +#include "keyboard.h" +#include "translate/translate.h" +#include "video.h" /* for charset defines */ + +static int default_stroke_pause = -1; +static int stroke_pause; +static int stroke_started; + + +#define GETNUMBER(s) ({ \ + int ret; t_unicode *end; \ + ret = unicode_to_long(s, &end, 0); \ + s = end; \ + if (*s) s++; \ + ret; \ +}) + +static t_unicode *type_one_key(t_unicode *in) +{ + int esc, keynum; + t_unicode ch; + t_unicode keysym; + static char ctrl[]="JMGHLIK"; + + stroke_pause = default_stroke_pause; + if (!in || !in[0]) return 0; + + ch=*(in++); + switch(ch) { + case '\\': { + ch=(*in++); + if (!ch) return 0; + esc = -1; + switch(ch) { + case 'v': esc++; + case 't': esc++; + case 'f': esc++; + case 'b': esc++; + case 'a': esc++; + case 'r': esc++; + case 'n': esc++; + case '^': { /* example: \a == \^G == G + \r == \^M == M == */ + if (esc >= 0) + ch = ctrl[esc]; + else + ch = *(in++); + keysym = ch; + if (keysym != DKY_VOID) { + if (keysym == DKY_M) { + put_symbol(PRESS, DKY_RETURN); + put_symbol(RELEASE, DKY_RETURN); + } else { + put_modified_symbol(PRESS, MODIFIER_CTRL, keysym); + put_modified_symbol(RELEASE, MODIFIER_CTRL, keysym); + } + } + return in; + } + case 'A': { /* example: \Az == z */ + ch = *(in++); + keysym = ch; + if (keysym != DKY_VOID) { + put_modified_symbol(PRESS, MODIFIER_ALT, keysym); + put_modified_symbol(RELEASE, MODIFIER_ALT, keysym); + } + return in; + } + case 'F': { /* example: \F12; == key F12 + \F1; == key F1 */ + keynum = GETNUMBER(in); + if ((keynum > 0) && (keynum < 12)) { + keysym = DKY_F1 + keynum -1; + put_symbol(PRESS, keysym); /* key pressed */ + put_symbol(RELEASE, keysym); /* key released */ + } + return in; + } + case 'p': { /* example: \p100; == pause one second */ + keynum = GETNUMBER(in); + if ((keynum > 0) && (keynum < 10000)) { + stroke_pause = keynum; + } + return in; + } + case 'P': { /* example: \P15; == set rate to 100/15 cps */ + keynum = GETNUMBER(in); + if (keynum > 0) { + keynum--; + if (keynum > 100) keynum = 100; + default_stroke_pause = keynum; + } + return in; + } + case 'M': { /* 'M' == as in Move, + ('P' would be nicer (Pu for PageUp) but is already used...) + example: \Mh == \Mu == + \Me == \Md == + \M8 == + \M4 == \M6 == + \M2 == + */ + ch=*(in++); + switch(ch) { + case 'i': keynum = DKY_INS; break; /* DKY_INS */ + case 'h': keynum = DKY_HOME; break; /* DKY_HOME */ + case 'u': keynum = DKY_PGUP; break; /* DKY_PGUP */ + case 'c': keynum = DKY_DEL; break; /* DKY_DEL */ + case 'e': keynum = DKY_END; break; /* DKY_END */ + case 'd': keynum = DKY_PGDN; break; /* DKY_PGDN */ + case '8': keynum = DKY_UP; break; /* DKY_UP */ + case '4': keynum = DKY_LEFT; break; /* DKY_LEFT */ + case '6': keynum = DKY_RIGHT; break; /* DKY_RIGHT */ + case '2': + default: keynum = DKY_DOWN; break; /* DKY_DOWN */ + } + move_key(PRESS, keynum); + move_key(RELEASE, keynum); + return in; + } + case '_': /* space */ + move_key(PRESS, DKY_SPACE); + move_key(RELEASE, DKY_SPACE); + return in; + default: + ; + /* fall through */ + } + } + default: { + keysym = ch; + if (keysym != DKY_VOID) { + put_symbol(PRESS, keysym); /* key pressed */ + put_symbol(RELEASE, keysym); /* key release */ + } + return in; + } + } + return in; +} + +static t_unicode *pre_stroke = 0, *pre_stroke_mem = 0; +int type_in_pre_strokes(void) +{ + struct char_set *keyb_charset = trconfig.keyb_charset; + + if (!stroke_started) + return stroke_pause; + if (config.pre_stroke && !pre_stroke) { + size_t characters, src_len; + const char *ptr; + struct char_set_state state; + + init_charset_state(&state, keyb_charset); + src_len = strlen(config.pre_stroke) +1; + + ptr = config.pre_stroke; + characters = character_count(&state, ptr, src_len); + + pre_stroke_mem = pre_stroke = + malloc(sizeof(t_unicode) * (characters +1)); + charset_to_unicode_string(&state, pre_stroke, &ptr, src_len, + characters + 1); + + cleanup_charset_state(&state); + } + if (pre_stroke) { + pre_stroke = type_one_key(pre_stroke); + if (!pre_stroke) { + free(config.pre_stroke); + free(pre_stroke_mem); + config.pre_stroke = 0; + } + } + return stroke_pause; +} + +void append_pre_strokes(char *s) +{ + if (config.pre_stroke) { + int l1,l2; + char *n; + + l1 = strlen(config.pre_stroke); + l2 = strlen(s); + n = realloc(config.pre_stroke, l1+l2+1); + if (!n) return; + memcpy(n+l1, s, l2+1); + config.pre_stroke = n; + } + else { + config.pre_stroke = strdup(s); + } +} + +void start_pre_strokes(void) +{ + stroke_started++; +} diff --git a/src/base/kbd_unicode/serv_backend.c b/src/base/kbd_unicode/serv_backend.c new file mode 100644 index 0000000..b1de586 --- /dev/null +++ b/src/base/kbd_unicode/serv_backend.c @@ -0,0 +1,218 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * DANG_BEGIN_MODULE + * + * Description: Keyboard backend - interface to the DOS side + * + * Maintainer: Eric Biederman + * + * REMARK + * This module handles interfacing to the DOS side both on int9/port60h level, + * or on the bios buffer level. + * Keycodes are buffered in a queue, which, however, has limited depth, so it + * shouldn't be used for pasting. + * + * More information about this module is in doc/README.newkbd + * + * /REMARK + * DANG_END_MODULE + * + */ + +#include +#include + +#include "emu.h" +#include "types.h" +#include "keyboard.h" +#include "keyb_server.h" +#include "keyb_clients.h" +#include "bios.h" +#include "pic.h" +#include "cpu.h" +#include "timers.h" +#include "keystate.h" + +/********************** (BIOS) mode backend ***************/ + +/* + * Interface to DOS (BIOS keyboard buffer/shiftstate flags) + */ + +void clear_bios_keybuf(void) +{ + WRITE_WORD(BIOS_KEYBOARD_BUFFER_START,0x001e); + WRITE_WORD(BIOS_KEYBOARD_BUFFER_END, 0x003e); + WRITE_WORD(BIOS_KEYBOARD_BUFFER_HEAD, 0x001e); + WRITE_WORD(BIOS_KEYBOARD_BUFFER_TAIL, 0x001e); + MEMSET_DOS(BIOS_KEYBOARD_BUFFER,0,32); + + WRITE_BYTE(BIOS_KEYBOARD_TOKEN,0); /* buffer for Alt-XXX (not used by emulator) */ + put_shift_state(dos_keyboard_state.shiftstate); + keyb_client_set_leds(get_modifiers_r(dos_keyboard_state.shiftstate)); +} + +#if 0 +static inline Boolean bios_keybuf_full(void) +{ + int start,end,head,tail; + + start = READ_WORD(BIOS_KEYBOARD_BUFFER_START); + end = READ_WORD(BIOS_KEYBOARD_BUFFER_END); + head = READ_WORD(BIOS_KEYBOARD_BUFFER_HEAD); + tail = READ_WORD(BIOS_KEYBOARD_BUFFER_TAIL); + + tail+=2; + if (tail==end) tail=start; + return (tail==head); +} + +static inline void put_bios_keybuf(Bit16u scancode) +{ + int start,end,head,tail; + + k_printf("KBD: put_bios_keybuf(%04x)\n",(unsigned int)scancode); + + start = READ_WORD(BIOS_KEYBOARD_BUFFER_START); + end = READ_WORD(BIOS_KEYBOARD_BUFFER_END); + head = READ_WORD(BIOS_KEYBOARD_BUFFER_HEAD); + tail = READ_WORD(BIOS_KEYBOARD_BUFFER_TAIL); + + WRITE_WORD(0x400+tail,scancode); + tail+=2; + if (tail==end) tail=start; + if (tail==head) { + k_printf("KBD: BIOS keyboard buffer overflow\n"); + return; + } + + WRITE_WORD(BIOS_KEYBOARD_BUFFER_TAIL,tail); +} +#endif + +/* + * update the seg 0x40 keyboard flags from dosemu's internal 'shiftstate' + * variable. + * This is called either from kbd_process() or the get_bios_key() helper. + * It is never called if a dos application takes complete + * control of int9. + */ + +void put_shift_state(t_shiftstate shift) +{ + Bit8u flags1, flags2, flags3, leds; + + flags1=leds=0; + /* preserve pause bit */ + flags2 = READ_BYTE(BIOS_KEYBOARD_FLAGS2) & PAUSE_MASK; + flags3 = READ_BYTE(BIOS_KEYBOARD_FLAGS3) & ~0x0c; + + if (shift & INS_LOCK) flags1 |= 0x80; + if (shift & CAPS_LOCK) { flags1 |= 0x40; leds |= 0x04; } + if (shift & NUM_LOCK) { flags1 |= 0x20; leds |= 0x02; } + if (shift & SCR_LOCK) { flags1 |= 0x10; leds |= 0x01; } + if (shift & ANY_ALT) flags1 |= 0x08; + if (shift & ANY_CTRL) flags1 |= 0x04; + if (shift & L_SHIFT) flags1 |= 0x02; + if (shift & R_SHIFT) flags1 |= 0x01; + + if (shift & INS_PRESSED) flags2 |= 0x80; + if (shift & CAPS_PRESSED) flags2 |= 0x40; + if (shift & NUM_PRESSED) flags2 |= 0x20; + if (shift & SCR_PRESSED) flags2 |= 0x10; + if (shift & SYSRQ_PRESSED) flags2 |= 0x04; + if (shift & L_ALT) flags2 |= 0x02; + if (shift & L_CTRL) flags2 |= 0x01; + + flags3 |= 0x10; /* set MF101/102 keyboard flag */ + if (shift & R_ALT) flags3 |= 0x08; + if (shift & R_CTRL) flags3 |= 0x04; + + WRITE_BYTE(BIOS_KEYBOARD_FLAGS1,flags1); + WRITE_BYTE(BIOS_KEYBOARD_FLAGS2,flags2); + WRITE_BYTE(BIOS_KEYBOARD_FLAGS3,flags3); + WRITE_BYTE(BIOS_KEYBOARD_LEDS,leds); +} + +static t_shiftstate get_shift_state(void) +{ + Bit8u flags1, flags2, flags3; + t_shiftstate shift = 0; + + flags1 = READ_BYTE(BIOS_KEYBOARD_FLAGS1); + flags2 = READ_BYTE(BIOS_KEYBOARD_FLAGS2); + flags3 = READ_BYTE(BIOS_KEYBOARD_FLAGS3); + + if (flags1 & 0x80) shift |= INS_LOCK; + if (flags1 & 0x40) shift |= CAPS_LOCK; + if (flags1 & 0x20) shift |= NUM_LOCK; + if (flags1 & 0x10) shift |= SCR_LOCK; + if (flags1 & 0x02) shift |= L_SHIFT; + if (flags1 & 0x01) shift |= R_SHIFT; + + if (flags2 & 0x80) shift |= INS_PRESSED; + if (flags2 & 0x40) shift |= CAPS_PRESSED; + if (flags2 & 0x20) shift |= NUM_PRESSED; + if (flags2 & 0x10) shift |= SCR_PRESSED; + if (flags2 & 0x04) shift |= SYSRQ_PRESSED; + if (flags2 & 0x02) shift |= L_ALT; + if (flags2 & 0x01) shift |= L_CTRL; + + if (flags3 & 0x08) shift |= R_ALT; + if (flags3 & 0x04) shift |= R_CTRL; + + return shift; +} + + +Bit16u get_bios_key(t_rawkeycode raw) +{ + Boolean make; + t_keynum key; + Bit16u bios_key = 0; + Bit8u flags3 = READ_BYTE(BIOS_KEYBOARD_FLAGS3); + + if (flags3 & 1) { + if (raw == 0x1d || raw == 0x9d) + dos_keyboard_state.raw_state.rawprefix = 0xe1; + else if (raw & 0x80) + dos_keyboard_state.raw_state.rawprefix = 0xe19d; + else + dos_keyboard_state.raw_state.rawprefix = 0xe11d; + } else if (flags3 & 2) + dos_keyboard_state.raw_state.rawprefix = 0xe0; + else + dos_keyboard_state.raw_state.rawprefix = 0; + + key = compute_keynum(&make, raw, &dos_keyboard_state.raw_state); + key = compute_functional_keynum(make, key, &dos_keyboard_state.keys_pressed); + if (key != NUM_VOID) { + t_shiftstate shiftstate = get_shift_state(); + dos_keyboard_state.shiftstate = shiftstate; + bios_key = translate_key(make, key, &dos_keyboard_state); + if (shiftstate != dos_keyboard_state.shiftstate) { + put_shift_state(dos_keyboard_state.shiftstate); + keyb_client_set_leds(get_modifiers_r(dos_keyboard_state.shiftstate)); + } + } + + flags3 = READ_BYTE(BIOS_KEYBOARD_FLAGS3) & ~3; + switch (dos_keyboard_state.raw_state.rawprefix) { + case 0xe0: + flags3 |= 2; + break; + case 0xe1: + case 0xe11d: + case 0xe19d: + flags3 |= 1; + break; + } + WRITE_BYTE(BIOS_KEYBOARD_FLAGS3, flags3); + + return bios_key; +} diff --git a/src/base/kbd_unicode/serv_xlat.c b/src/base/kbd_unicode/serv_xlat.c new file mode 100644 index 0000000..68253da --- /dev/null +++ b/src/base/kbd_unicode/serv_xlat.c @@ -0,0 +1,2390 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * DANG_BEGIN_MODULE + * + * Description: Keyboard translation + * + * Maintainer: Eric Biederman + * + * REMARK + * This module contains the the translation part of the keyboard 'server', + * which translates key events into the form in which they can be sent to DOS. + * + * The frontends will call one of the following functions to send + * keyboard events to DOS: + * + * VERB + * put_rawkey(t_rawkeycode code); + * move_key(Boolean make, t_keysym key) + * put_symbol(Boolean make, t_keysym sym) + * + * set_shiftstate(t_shiftstate s); + * /VERB + * + * Interface to serv_backend.c is through write_queue(raw). + * + * More information about this module is in doc/README.newkbd + * + * /REMARK + * DANG_END_MODULE + * + */ + + /* Note: + * While it is very bad to hard code keys, dos has already + * hard coded the shift keys, the number pad keys, and + * virtually everything but the letter keys. Dosemu cannot + * remove this hard coding without confusing dos programs. + * The point of dosemu's configurability is primarily to + * allow non-us keyboards to work as they would in dos. Not + * to make a nice place to customize your keyboard. + * + * The keys currently hard coded are: + * NUM_TAB + * NUM_RETURN + * NUM_BKSP + * NUM_ESC + * NUM_PAD_ENTER + * NUM_PAD_SLASH + * NUM_SPACE + * NUM_LBRACK -- fixme + * NUM_BACKSLASH -- fixme + * NUM_RBRACK -- fixme + * NUM_6 -- fixme? + * NUM_DASH -- fixme + * NUM_FAKE_L_SHIFT + * NUM_FAKE_R_SHIFT + * NUM_L_SHIFT + * NUM_R_SHIFT + * NUM_L_CTRL + * NUM_R_CTRL + * NUM_L_ALT + * NUM_R_ALT + * NUM_CAPS + * NUM_NUM + * NUM_SCROLL + * NUM_PTRSCR_SYSRQ + * NUM_PAUSE_BREAK + * NUM_PAUSE + * NUM_BREAK + * NUM_PRTSCR + * NUM_SYSRQ + * NUM_PAD_7 NUM_PAD_8 NUM_PAD_9 + * NUM_PAD_4 NUM_PAD_5 NUM_PAD_6 + * NUM_PAD_1 NUM_PAD_2 NUM_PAD_3 + * NUM_PAD_0 + * NUM_PAD_MINUS -- fixme? + * NUM_PAD_PLUS -- fixme? + * NUM_PAD_DECIMAL -- fixme? + * NUM_INS NUM_DEL + * NUM_HOME NUM_END + * NUM_PGUP NUM_PGDN + * NUM_UP NUM_DOWN + * NUM_LEFT NUM_RIGHT + * NUM_TAB --fixme? + * NUM_F1 + * NUM_F2 + * NUM_F3 + * NUM_F4 + * NUM_F5 + * NUM_F6 + * NUM_F7 + * NUM_F8 + * NUM_F9 + * NUM_F10 + * NUM_F11 + * NUM_F12 + * + */ + +#define __KBD_SERVER + +#include +#include + +#include "emu.h" +#include "bitops.h" + +#include "keymaps.h" +#include "keyboard.h" +#include "keyb_server.h" + +#include "keynum.h" +#include "keystate.h" +#include "translate/keysym_attributes.h" +#include "translate/translate.h" + +#include "video.h" /* for charset defines */ + +#define CHARSET_DEBUG 0 +#define TRANSLATE_DEBUG 0 +#define SHIFTSTATE_DEBUG 0 + +#if NUM_KEYSYMS != 0x10000 +#error NUM_KEYSYMS needs to be 0x10000 +#endif + +/* FIXME FIXME! + * this does not work yet: intended only for pasting, but implemented + * also for normal keypresses. So disable until this fixed. */ +#define PASTE_RECOMPUTE 0 + +static void sync_shift_state(t_modifiers desired, struct keyboard_state *state); +static t_shiftstate translate_shiftstate(t_shiftstate cur_shiftstate, + struct translate_rule *rule, t_keynum key, t_shiftstate *mask); + +/* + * various tables + */ + +/* + * BIOS scancode tables + * + * The key num is used as the index + * For 'unshifted', no translation is done (bios scancode=hardware scancode), + * for SHIFT the translation is done 'by hand' in make_bios_code(). + */ + + +static const Bit8u bios_ctrl_scancodes[NUM_KEY_NUMS] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00-07 */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x94, /* 08-0F */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x00, 0x1e, 0x1f, /* 18-1F */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 28-2F */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x96, /* 30-37 */ + 0x38, 0x39, 0x3a, 0x5e, 0x5f, 0x60, 0x61, 0x62, /* 38-3F */ + 0x63, 0x64, 0x65, 0x66, 0x67, 0x45, 0x44, 0x77, /* 40-47 */ + 0x8d, 0x84, 0x8e, 0x73, 0x8f, 0x74, 0x90, 0x75, /* 48-4F */ + 0x91, 0x76, 0x92, 0x93, 0x54, 0x55, 0x56, 0x89, /* 50-57 */ + 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 58-5F */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 60-67 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 68-6F */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 70-77 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78-7F */ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 80-87 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 88-8F */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 90-97 */ + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, /* 98-9F */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* A0-A7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* A8-AF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, /* B0-B7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* B8-BF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, /* C0-C7 */ + 0x8d, 0x84, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, /* C8-CF */ + 0x91, 0x76, 0x92, 0x93, 0x00, 0x00, 0x00, 0x00, /* D0-D7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* D8-DF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* E0-E7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* E8-EF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* F0-F7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, /* F8-FF */ +}; + +static const Bit8u bios_alt_scancodes[NUM_KEY_NUMS] = +{ + 0x00, 0x01, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, /* 00-07 */ + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x0e, 0xa5, /* 08-0F */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x00, 0x1e, 0x1f, /* 18-1F */ + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ + 0x28, 0x29, 0x00, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 28-2F */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x00, 0x37, /* 30-37 */ + 0x00, 0x39, 0x00, 0x68, 0x69, 0x6a, 0x6b, 0x6c, /* 38-3F */ + 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x00, 0x00, 0x97, /* 40-47 */ + 0x98, 0x99, 0x4a, 0x9b, 0x00, 0x9d, 0x4e, 0x9f, /* 48-4F */ + 0xa0, 0xa1, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x8b, /* 50-57 */ + 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 58-5F */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 60-67 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 68-6F */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 70-77 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78-7F */ + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 80-87 */ + 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, /* 88-8F */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 90-97 */ + 0x00, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00, /* 98-9F */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* A0-A7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* A8-AF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, /* B0-B7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* B8-BF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, /* C0-C7 */ + 0x98, 0x99, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9f, /* C8-CF */ + 0xa0, 0xa1, 0xa2, 0xa3, 0x00, 0x00, 0x00, 0x00, /* D0-D7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* D8-DF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* E0-E7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* E8-EF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* F0-F7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* F8-FF */ +}; + +static const Bit8u bios_alt_mapped_scancodes[] = +{ + 0x1E, 0x30, 0x2E, 0x20, 0x12, 0x21, 0x22, 0x23, /* A-H */ + 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18, 0x19, /* I-P */ + 0x10, 0x13, 0x1F, 0x14, 0x16, 0x2F, 0x11, 0x2D, /* Q-W */ + 0x15, 0x2C /* Y-Z */ +}; + +/****************************************************************************************** + * State initialization + ******************************************************************************************/ + +/* + * Scancode to Character translation + * ============================================================================= + */ + +static void init_misc_all_maps(t_keysym *rule) +{ + rule[NUM_L_ALT] = DKY_L_ALT; + rule[NUM_R_ALT] = DKY_R_ALT; + rule[NUM_L_CTRL] = DKY_L_CTRL; + rule[NUM_R_CTRL] = DKY_R_CTRL; + rule[NUM_L_SHIFT] = DKY_L_SHIFT; + rule[NUM_R_SHIFT] = DKY_R_SHIFT; + rule[NUM_NUM] = DKY_NUM; + rule[NUM_CAPS] = DKY_CAPS; +} + +static void init_misc_plain_map(t_keysym *rule) +{ + rule[NUM_TAB] = DKY_TAB; + rule[NUM_RETURN] = DKY_RETURN; + rule[NUM_BKSP] = DKY_BKSP; + rule[NUM_ESC] = DKY_ESC; + rule[NUM_PAD_ENTER] = DKY_PAD_ENTER; + rule[NUM_PAD_SLASH] = DKY_PAD_SLASH; + rule[NUM_SCROLL] = DKY_SCROLL; + + rule[NUM_PAD_0] = DKY_PAD_INS; + rule[NUM_PAD_1] = DKY_PAD_END; + rule[NUM_PAD_2] = DKY_PAD_DOWN; + rule[NUM_PAD_3] = DKY_PAD_PGDN; + rule[NUM_PAD_4] = DKY_PAD_LEFT; + rule[NUM_PAD_5] = DKY_PAD_CENTER; + rule[NUM_PAD_6] = DKY_PAD_RIGHT; + rule[NUM_PAD_7] = DKY_PAD_HOME; + rule[NUM_PAD_8] = DKY_PAD_UP; + rule[NUM_PAD_9] = DKY_PAD_PGUP; + rule[NUM_PAD_DECIMAL] = DKY_PAD_DEL; + rule[NUM_PAD_SLASH] = DKY_PAD_SLASH; + rule[NUM_PAD_AST] = DKY_PAD_AST; + rule[NUM_PAD_MINUS] = DKY_PAD_MINUS; + rule[NUM_PAD_PLUS] = DKY_PAD_PLUS; + rule[NUM_PAD_ENTER] = DKY_PAD_ENTER; + + rule[NUM_F1] = DKY_F1; + rule[NUM_F2] = DKY_F2; + rule[NUM_F3] = DKY_F3; + rule[NUM_F4] = DKY_F4; + rule[NUM_F5] = DKY_F5; + rule[NUM_F6] = DKY_F6; + rule[NUM_F7] = DKY_F7; + rule[NUM_F8] = DKY_F8; + rule[NUM_F9] = DKY_F9; + rule[NUM_F10] = DKY_F10; + rule[NUM_F11] = DKY_F11; + rule[NUM_F12] = DKY_F12; + + rule[NUM_INS] = DKY_INS; + rule[NUM_DEL] = DKY_DEL; + rule[NUM_HOME] = DKY_HOME; + rule[NUM_END] = DKY_END; + rule[NUM_PGUP] = DKY_PGUP; + rule[NUM_PGDN] = DKY_PGDN; + rule[NUM_UP] = DKY_UP; + rule[NUM_DOWN] = DKY_DOWN; + rule[NUM_LEFT] = DKY_LEFT; + rule[NUM_RIGHT] = DKY_RIGHT; + + rule[NUM_PRTSCR_SYSRQ] = DKY_PRTSCR; + rule[NUM_PAUSE_BREAK] = DKY_PAUSE; +} + +static void init_misc_shifted_map(t_keysym *rule) +{ + rule[NUM_TAB] = DKY_LEFT_TAB; + rule[NUM_RETURN] = DKY_RETURN; + rule[NUM_BKSP] = DKY_BKSP; + rule[NUM_ESC] = DKY_ESC; + rule[NUM_PAD_ENTER] = DKY_PAD_ENTER; + rule[NUM_PAD_SLASH] = DKY_PAD_SLASH; +} + +static void init_misc_keypad_map(t_keysym *rule) +{ + int i; + /* replace generic characters with their keysym variety */ + for(i = 0x47; i <= 0x53; i++ ) { + t_keysym keysym = rule[i]; + switch(keysym) { + case U_DIGIT_ZERO: keysym = DKY_PAD_0; break; + case U_DIGIT_ONE: keysym = DKY_PAD_1; break; + case U_DIGIT_TWO: keysym = DKY_PAD_2; break; + case U_DIGIT_THREE: keysym = DKY_PAD_3; break; + case U_DIGIT_FOUR: keysym = DKY_PAD_4; break; + case U_DIGIT_FIVE: keysym = DKY_PAD_5; break; + case U_DIGIT_SIX: keysym = DKY_PAD_6; break; + case U_DIGIT_SEVEN: keysym = DKY_PAD_7; break; + case U_DIGIT_EIGHT: keysym = DKY_PAD_8; break; + case U_DIGIT_NINE: keysym = DKY_PAD_9; break; + case U_PERIOD: keysym = DKY_PAD_DECIMAL; break; + case U_COMMA: keysym = DKY_PAD_SEPARATOR; break; + case U_SLASH: keysym = DKY_PAD_SLASH; break; + case U_PLUS_SIGN: keysym = DKY_PAD_PLUS; break; + case U_ASTERISK: keysym = DKY_PAD_AST; break; + case U_HYPHEN_MINUS: keysym = DKY_PAD_MINUS; break; + } + rule[i] = keysym; + } +} +static void init_heuristics_alt_map(t_keysym *rule, t_keysym *plain_rule) +{ + int i, j; + + /* Heuristic compute the alt code based on the + * values on the other keycaps. + */ + for(i = 1; i < 27; i++) { + for(j = 0; j < NUM_KEY_NUMS; j++) { + /* lower case letters */ + if (plain_rule[j] == (i + 0x60)) { + rule[j] = i + DKY_ALT_A -1; + } + /* upper case letters */ + if (plain_rule[j] == (i + 0x40)) { + rule[j] = i + DKY_ALT_A -1; + } + } + } +} +static void init_misc_alt_map(t_keysym *rule) +{ + rule[NUM_SPACE] = DKY_SPACE; + rule[NUM_PRTSCR_SYSRQ] = DKY_SYSRQ; +} +static void init_misc_altgr_map(t_keysym *rule) +{ + rule[NUM_SPACE] = DKY_SPACE; +} +static void init_misc_shift_altgr_map(t_keysym *rule) +{ + rule[NUM_SPACE] = DKY_SPACE; +} +static void init_misc_ctrl_alt_map(t_keysym *rule) +{ + rule[NUM_PAD_AST] = DKY_DOSEMU_REBOOT; + rule[NUM_8] = DKY_DOSEMU_REBOOT; + /* C-A-D is disabled */ +// rule[NUM_DEL] = DKY_DOSEMU_REBOOT; + + rule[NUM_PGDN] = DKY_DOSEMU_EXIT; + rule[NUM_PAD_3] = DKY_DOSEMU_EXIT; + rule[NUM_P] = DKY_DOSEMU_FREEZE; + + rule[NUM_F1] = DKY_DOSEMU_VT_1; + rule[NUM_F2] = DKY_DOSEMU_VT_2; + rule[NUM_F3] = DKY_DOSEMU_VT_3; + rule[NUM_F4] = DKY_DOSEMU_VT_4; + rule[NUM_F5] = DKY_DOSEMU_VT_5; + rule[NUM_F6] = DKY_DOSEMU_VT_6; + rule[NUM_F7] = DKY_DOSEMU_VT_7; + rule[NUM_F8] = DKY_DOSEMU_VT_8; + rule[NUM_F9] = DKY_DOSEMU_VT_9; + rule[NUM_F10] = DKY_DOSEMU_VT_10; + rule[NUM_F11] = DKY_DOSEMU_VT_11; + rule[NUM_F12] = DKY_DOSEMU_VT_12; + + rule[NUM_PAD_8] = DKY_MOUSE_UP; + rule[NUM_PAD_2] = DKY_MOUSE_DOWN; + rule[NUM_PAD_4] = DKY_MOUSE_LEFT; + rule[NUM_PAD_6] = DKY_MOUSE_RIGHT; + rule[NUM_PAD_7] = DKY_MOUSE_UP_AND_LEFT; + rule[NUM_PAD_9] = DKY_MOUSE_UP_AND_RIGHT; + rule[NUM_PAD_1] = DKY_MOUSE_DOWN_AND_LEFT; +#if 0 + rule[NUM_PAD_3] = DKY_MOUSE_DOWN_AND_RIGHT; +#endif +} + +static void init_heuristics_ctrl_map(t_keysym *rule, t_keysym *plain_rule) +{ + int i, j; + + /* Heuristic compute the ctrl code based on the + * values on the other keycaps. + */ + for(i = 1; i < 27; i++) { + for(j = 0; j < NUM_KEY_NUMS; j++) { + /* lower case letters */ + if (plain_rule[j] == (i + 0x60)) { + rule[j] = i; + break; + } + /* upper case letters */ + if (plain_rule[j] == (i + 0x40)) { + rule[j] = i; + break; + } + } + } + /* funky heuristics based on keycaps don't do as well for the + * these as just hardcoding the positions to those used on the + * us keyboard. + */ + rule[NUM_2] = 0x0; + rule[NUM_LBRACK] = 0x1b; + rule[NUM_BACKSLASH] = 0x1c; + rule[NUM_RBRACK] = 0x1d; + rule[NUM_6] = 0x1e; + rule[NUM_DASH] = 0x1f; +} + +static void init_misc_ctrl_map(t_keysym *rule) +{ + rule[NUM_RETURN] = 0x0a; + rule[NUM_PAD_ENTER] = 0x0a; + rule[NUM_SPACE] = 0x20; + rule[NUM_ESC] = 0x1b; + rule[NUM_BKSP] = 0x7f; + rule[NUM_PAUSE_BREAK] = DKY_BREAK; +} + +static void init_translate_rule(t_keysym *rule, + int map_size, t_keysym *key_map, + int key_bias) +{ + t_unicode ch; + int i; + struct char_set *keyb_charset = trconfig.keyb_config_charset; + + for( i = 0; i < map_size; i++) { + ch = key_map[i]; + if (ch == DKY_VOID) + continue; + /* 0xef00 - 0xefff is a pass through range to the current + * character set. + */ + if ((ch >= 0xef00) && (ch <= 0xefff)) { + unsigned char buff[1]; + struct char_set_state keyb_state; + init_charset_state(&keyb_state, keyb_charset); + buff[0] = ch & 0xFF; + charset_to_unicode(&keyb_state, &ch, buff, 1); + cleanup_charset_state(&keyb_state); + if (ch == DKY_VOID) + continue; + } + rule[i + key_bias] = ch; + } +} + +#if TRANSLATE_DEBUG +static void dump_translate_rules(struct scancode_translate_rules *rules) +{ + int i, j; +#define LOOP(type) \ + do { \ + k_printf(#type ":\n"); \ + for(i = 0; i < NUM_KEY_NUMS; i++) { \ + t_keysym keysym = type[i]; \ + t_keynum keynum = validate_keynum(i); \ + if (keysym == DKY_VOID || keynum == NUM_VOID) \ + continue; \ + k_printf("keynum->keysym: %02x->%04x\n", \ + keynum, keysym); \ + } \ + } while(0) + + for(j = 0; j < NUM_RULES; j++) { + LOOP(rules->trans_rules.rule_arr[j].rule_map); + } +} +#endif + +/* FIXME: I need external input for ctrl mappings! + * I have a clever hack that gets it right most of the time, + * but it's not quite right. Also some ctrl keys are hardwired to + * their position on a US keyboard. + */ + +static void +init_scancode_translation_rules(struct scancode_translate_rules *maps, + struct keytable_entry *key_table) +{ + struct scancode_translate_rules *rules=NULL; + int i, j; + + for(i = 0; i < MAPS_MAX; i++) { + if(maps[i].keyboard == -1) { + rules = &maps[i]; + break; + } + } + if(rules == NULL) { + error("init: maximum keymaps limit exceeded\n"); + return; + } + + rules->keyboard = key_table->keyboard; + + /* Initialize everything to a known value */ + rules->trans_rules.rule_structs.plain.modifiers = 0; + rules->trans_rules.rule_structs.shift.modifiers = MODIFIER_SHIFT; + rules->trans_rules.rule_structs.ctrl.modifiers = MODIFIER_CTRL; + rules->trans_rules.rule_structs.alt.modifiers = MODIFIER_ALT; + rules->trans_rules.rule_structs.altgr.modifiers = MODIFIER_ALTGR; + rules->trans_rules.rule_structs.shift_altgr.modifiers = MODIFIER_SHIFT | MODIFIER_ALTGR; + rules->trans_rules.rule_structs.ctrl_alt.modifiers = MODIFIER_CTRL | MODIFIER_ALT; + + for(j = 0; j < NUM_RULES; j++) { + for(i = 0; i < NUM_KEY_NUMS; i++) + rules->trans_rules.rule_arr[j].rule_map[i] = DKY_VOID; + } + + /* plain keys */ + init_translate_rule(rules->trans_rules.rule_structs.plain.rule_map, + key_table->sizemap, key_table->key_map, 0); + init_misc_plain_map(rules->trans_rules.rule_structs.plain.rule_map); + + /* shifted keys */ + init_translate_rule(rules->trans_rules.rule_structs.shift.rule_map, + key_table->sizemap, key_table->shift_map, 0); + init_misc_shifted_map(rules->trans_rules.rule_structs.shift.rule_map); + + /* ctrl keys */ + if (key_table->ctrl_map) { + init_translate_rule(rules->trans_rules.rule_structs.ctrl.rule_map, + key_table->sizemap, key_table->ctrl_map, 0); + } else { + /* FIXME!!! (unreliable heuristic used) */ + init_heuristics_ctrl_map(rules->trans_rules.rule_structs.ctrl.rule_map, + rules->trans_rules.rule_structs.plain.rule_map); + } + init_misc_ctrl_map(rules->trans_rules.rule_structs.ctrl.rule_map); + + /* alt keys */ + init_heuristics_alt_map(rules->trans_rules.rule_structs.alt.rule_map, + rules->trans_rules.rule_structs.plain.rule_map); /* FIXME (heuristic used) */ + init_misc_alt_map(rules->trans_rules.rule_structs.alt.rule_map); + + /* altgr keys */ + if (key_table->alt_map) { + init_translate_rule(rules->trans_rules.rule_structs.altgr.rule_map, + key_table->sizemap, key_table->alt_map, 0); + } + init_misc_altgr_map(rules->trans_rules.rule_structs.altgr.rule_map); + + /* shift alt keys */ + if (key_table->shift_alt_map) { + init_translate_rule(rules->trans_rules.rule_structs.shift_altgr.rule_map, + key_table->sizemap, key_table->shift_alt_map, 0); + } + init_misc_shift_altgr_map(rules->trans_rules.rule_structs.shift_altgr.rule_map); + + /* ctrl alt keys */ + if (key_table->ctrl_alt_map) { + init_translate_rule(rules->trans_rules.rule_structs.ctrl_alt.rule_map, + key_table->sizemap, key_table->ctrl_alt_map, 0); + } + init_misc_ctrl_alt_map(rules->trans_rules.rule_structs.ctrl_alt.rule_map); + + /* shift num_pad key is the same as num_lock num_pad key */ + /* keypad */ + init_translate_rule(rules->trans_rules.rule_structs.shift.rule_map, + key_table->sizepad, key_table->num_table, 0x47); + init_misc_keypad_map(rules->trans_rules.rule_structs.shift.rule_map); + + for(i = 0; i < NUM_RULES; i++) + init_misc_all_maps(rules->trans_rules.rule_arr[i].rule_map); +#if TRANSLATE_DEBUG + dump_translate_rules(rules); +#endif +} + +/* + * Character to Scancode sequence translation + * ============================================================================= + */ + +static void init_charset_keymap(struct character_translate_rules *charset, + struct scancode_translate_rules *rules, int mapnum) +{ + t_keynum key; + int i, j, k; + t_keysym ch; + t_modifiers shiftstate; + t_shiftstate mask; + struct translate_rule *rule, *rule1; + + for(j = 0; j < NUM_RULES; j++) { + rule = &rules->trans_rules.rule_arr[j]; + for (i = 0; i < NUM_KEY_NUMS; i++) { + ch = rule->rule_map[i]; + shiftstate = rule->modifiers; + if (ch == DKY_VOID) { + continue; + } + key = validate_keynum(i); + if (key == NUM_VOID) { + continue; + } + if (charset->keys[ch].key != NUM_VOID) { + continue; + } + charset->keys[ch].key = key; + charset->keys[ch].shiftstate = shiftstate; + charset->keys[ch].map = mapnum; + charset->keys[ch].shiftstate_mask = ~0; + for(k = 0; k < NUM_RULES; k++) { + rule1 = &rules->trans_rules.rule_arr[k]; + if (rule1->rule_map[i] != ch) { + charset->keys[ch].shiftstate_mask &= ~rule1->modifiers; + } + if (~get_modifiers_r(translate_shiftstate( + ~0, &rules->trans_rules.rule_structs.plain, key, &mask)) & + ~charset->keys[ch].shiftstate_mask) { + charset->keys[ch].shiftstate_mask &= ~get_modifiers_r(mask); + } + } + } + } +} + +static void init_one_deadkey(void *p, t_keysym dead_sym, t_keysym in, t_keysym out) +{ + struct character_translate_rules *charset = p; + + if (charset->keys[dead_sym].key == NUM_VOID) { + /* If we can't type the dead key we can't use it + * to type anything else either. + */ + return; + } + if ((charset->keys[out].key != NUM_VOID) || + (charset->keys[in].key == NUM_VOID) + /* Only one dead key can be active at a time + * but it may be needed to press one dead key + * to get another (a truly silly case) + */ + ) { + return; + } + /* Note: The shiftstate is the same going for the + * deadsym and for the result symbol + */ + charset->keys[out].key = + charset->keys[in].key; + charset->keys[out].shiftstate = + charset->keys[in].shiftstate; + charset->keys[out].deadsym = dead_sym; + charset->keys[out].map = charset->keys[dead_sym].map; +} + +static void init_charset_deadmap(struct character_translate_rules *charset) +{ + traverse_dead_key_list(charset, init_one_deadkey); +} + +static void check_video_mem_charset(struct character_translate_rules *charset) +{ + int i; + struct char_set *vmem_charset = trconfig.video_mem_charset; + + /* any mapping from < 0x20 to Unicode >= 0x20 ? */ + for (i = 0x20; i < NUM_KEYSYMS; i++) { + unsigned char buff[1]; + size_t result; + struct char_set_state vmem_state; + init_charset_state(&vmem_state, vmem_charset); + result = unicode_to_charset(&vmem_state, i, buff, 1); + if (result == 1 && buff[0] < 0x20 && + charset->keys[buff[0]].key != NUM_VOID) { + charset->keys[i] = charset->keys[buff[0]]; + } + cleanup_charset_state(&vmem_state); + } +} + +static void init_one_approximation(void *p, t_unicode symbol, t_unicode approximation) +{ + struct character_translate_rules *charset = p; + + if ((symbol >= NUM_KEYSYMS) || (approximation >= NUM_KEYSYMS)) + return; + + if ((charset->keys[symbol].key == NUM_VOID) && + (charset->keys[approximation].key != NUM_VOID) && + (charset->keys[approximation].character == charset->keys[symbol].character)) { + /* Copy the code from the approximate symbol to the symbol */ + charset->keys[symbol] = charset->keys[approximation]; + } +} +static void init_charset_approximations(struct character_translate_rules *charset) +{ + traverse_approximation_list(charset, init_one_approximation); +} + +#if CHARSET_DEBUG +static void dump_charset(struct character_translate_rules *charset) +{ + int i; + for(i = 0; i < NUM_KEYSYMS; i++) { + if (charset->keys[i].key == NUM_VOID) + continue; + k_printf("sym: %04x key: %02x shiftstate: %04x shiftstate_mask: %04x deadsym: %04x character: %02x -> '%c'\n", + i, + charset->keys[i].key, + charset->keys[i].shiftstate, + charset->keys[i].shiftstate_mask, + charset->keys[i].deadsym, + charset->keys[i].character, + charset->keys[i].character); + } +} +#endif + +static void init_charset_keys(struct character_translate_rules *charset, + struct scancode_translate_rules maps[]) +{ + int i; + struct char_set *keyb_charset = trconfig.keyb_config_charset; + + /* first initialize the charset_keys to nothing */ + for (i = 0; i < NUM_KEYSYMS; i++) { + unsigned char buff[1]; + struct char_set_state keyb_state; + init_charset_state(&keyb_state, keyb_charset); + /* FIXME: handle smaller tables ?*/ + unicode_to_charset(&keyb_state, i, buff, 1); + charset->keys[i].key = NUM_VOID; + charset->keys[i].shiftstate = 0; + charset->keys[i].deadsym = DKY_VOID; + charset->keys[i].character = buff[0]; + cleanup_charset_state(&keyb_state); + } + + /* unmapped characters have an ascii key of 0 */ + charset->keys[DKY_VOID].character = 0; + + for(i=0;irules = rules; +/* for now, we just assume that NumLock=off, Caps=off, Scroll=off. If the raw frontend + * is running, it'll tell us the actual shiftstate later, otherwise we'll have to + * live with this. + */ + state->shiftstate = 0; + state->alt_num_buffer = 0; + state->accent = DKY_VOID; + state->raw_state.rawprefix = 0; +} + +static void init_rules(struct keyboard_rules *rules) +{ + int i; + + rules->activemap = 0; + for(i = 0; i < MAPS_MAX; i++) { + rules->maps[i].keyboard = -1; + } +} + +/* + * State management + * ============================================================================= + */ +static struct keyboard_rules keyboard_rules; +struct keyboard_state input_keyboard_state; +struct keyboard_state dos_keyboard_state; + +static void keyb_init_state(void) +{ + init_rules(&keyboard_rules); + init_scancode_translation_rules(keyboard_rules.maps, config.keytable); + if(config.altkeytable) { + init_scancode_translation_rules(keyboard_rules.maps, config.altkeytable); + } + init_charset_keys(&keyboard_rules.charset, keyboard_rules.maps); + init_active_keyboard_state(&input_keyboard_state, &keyboard_rules); + init_active_keyboard_state(&dos_keyboard_state, &keyboard_rules); +} +static void keyb_reset_state(void) +{ +} +/****************************************************************************************** + * Queue front end (keycode translation etc.) + ******************************************************************************************/ + +/* + * translate a keyboard event into a 16-bit BIOS scancode word (LO=ascii char, HI="scancode"). + * Always trust the lower level routines to generat the correct ascii + * code itself, unless the ascii is a flag to mark the key as an extended key. + */ + +static Bit16u make_bios_code_r(Boolean make, t_keynum key, + unsigned ascii, t_keysym keysym, + struct keyboard_state *state) +{ + t_shiftstate shiftstate = state->shiftstate; + Bit8u bios_scan = 0; + Bit16u special = 0; + + /* Note: + * While it is very bad to hard code keys, dos has already + * hard coded the shift keys, the number pad keys, and + * virtually everything but the letter keys. Dosemu cannot + * remove this hard coding without confusing dos programs. + * The point of dosemu's configurability is primarily to + * allow non-us keyboards to work as they would in dos. Not + * to make a nice place to customize your keyboard. + */ + + if (make) { + /* filter out hidden keys (shift keys & new keys) */ + switch(key) { + default: + /*k_printf("KBD: make_bios_code() hidden key\n");*/ + return 0; + break; + /* main block */ + case NUM_SPACE: case NUM_BKSP: case NUM_RETURN: case NUM_TAB: + case NUM_A: case NUM_B: case NUM_C: case NUM_D: case NUM_E: + case NUM_F: case NUM_G: case NUM_H: case NUM_I: case NUM_J: + case NUM_K: case NUM_L: case NUM_M: case NUM_N: case NUM_O: + case NUM_P: case NUM_Q: case NUM_R: case NUM_S: case NUM_T: + case NUM_U: case NUM_V: case NUM_W: case NUM_X: case NUM_Y: + case NUM_Z: case NUM_1: case NUM_2: case NUM_3: case NUM_4: + case NUM_5: case NUM_6: case NUM_7: case NUM_8: case NUM_9: + case NUM_0: case NUM_DASH: case NUM_EQUALS: case NUM_LBRACK: + case NUM_RBRACK: case NUM_SEMICOLON: case NUM_APOSTROPHE: + case NUM_GRAVE: case NUM_BACKSLASH: case NUM_COMMA: + case NUM_PERIOD: case NUM_SLASH: case NUM_LESSGREATER: + + /* keypad */ + case NUM_PAD_0: case NUM_PAD_1: case NUM_PAD_2: case NUM_PAD_3: + case NUM_PAD_4: case NUM_PAD_5: case NUM_PAD_6: case NUM_PAD_7: + case NUM_PAD_8: case NUM_PAD_9: case NUM_PAD_DECIMAL: + case NUM_PAD_SLASH: case NUM_PAD_AST: case NUM_PAD_MINUS: + case NUM_PAD_PLUS: case NUM_PAD_ENTER: + + /* function keys */ + case NUM_ESC: case NUM_F1: case NUM_F2: case NUM_F3: case NUM_F4: + case NUM_F5: case NUM_F6: case NUM_F7: case NUM_F8: case NUM_F9: + case NUM_F10: case NUM_F11: case NUM_F12: + + /* cursor block */ + case NUM_INS: case NUM_DEL: case NUM_HOME: case NUM_END: + case NUM_PGUP: case NUM_PGDN: case NUM_UP: case NUM_DOWN: + case NUM_LEFT: case NUM_RIGHT: + + /* special */ + case NUM_PAUSE: case NUM_BREAK: case NUM_PRTSCR: case NUM_SYSRQ: + break; + } + /* check for special handling */ + switch(key) { + case NUM_PAUSE: special=SP_PAUSE; break; + case NUM_BREAK: special=SP_BREAK; break; + case NUM_PRTSCR: special=SP_PRTSCR; break; + case NUM_SYSRQ: special=SP_SYSRQ_MAKE; break; + } + + /* convert to BIOS INT16 scancode, depending on shift state + */ + if (shiftstate & ANY_ALT) { + switch(key) { + /* alt-keypad */ + case NUM_PAD_7: case NUM_PAD_8: case NUM_PAD_9: + case NUM_PAD_4: case NUM_PAD_5: case NUM_PAD_6: + case NUM_PAD_1: case NUM_PAD_2: case NUM_PAD_3: + case NUM_PAD_0: + { + int val = 0; + switch(key) { + case NUM_PAD_9: val = 9; break; + case NUM_PAD_8: val = 8; break; + case NUM_PAD_7: val = 7; break; + case NUM_PAD_6: val = 6; break; + case NUM_PAD_5: val = 5; break; + case NUM_PAD_4: val = 4; break; + case NUM_PAD_3: val = 3; break; + case NUM_PAD_2: val = 2; break; + case NUM_PAD_1: val = 1; break; + case NUM_PAD_0: val = 0; break; + } + state->alt_num_buffer = + (state->alt_num_buffer*10 + val) & 0xff; + k_printf("KBD: alt-keypad key=%08d ascii=%c buffer=%d\n", + key, ascii, state->alt_num_buffer); + ascii=0; + bios_scan=0; + break; + } + + default: + if ((keysym >= DKY_ALT_A)&&(keysym <= DKY_ALT_Z)) { + bios_scan=bios_alt_mapped_scancodes[keysym - DKY_ALT_A]; + } else { + bios_scan=bios_alt_scancodes[key]; + } + if (ascii != 0) { + /* This gets the scancodes right for AltGr keys */ + bios_scan = key & 0x7f; + } + } + } + else if (shiftstate & ANY_CTRL) { + bios_scan = bios_ctrl_scancodes[key]; + + if (key == NUM_PRTSCR) { + special = 0; + } + /* Mark the extended keys */ + switch (key) { + /* The cursor block */ + case NUM_INS: case NUM_DEL: + case NUM_HOME: case NUM_END: + case NUM_PGUP: case NUM_PGDN: + case NUM_UP: case NUM_DOWN: + case NUM_LEFT: case NUM_RIGHT: + ascii = 0xe0; break; + } + if ((bios_scan == key) && (keysym == DKY_VOID)) { + ascii = 0; + bios_scan = 0; + } + } + else if (shiftstate & ANY_SHIFT) { + switch(key) { + case NUM_TAB: + bios_scan=0x0f; break; + case NUM_F1: + bios_scan=0x54; break; + case NUM_F2: + bios_scan=0x55; break; + case NUM_F3: + bios_scan=0x56; break; + case NUM_F4: + bios_scan=0x57; break; + case NUM_F5: + bios_scan=0x58; break; + case NUM_F6: + bios_scan=0x59; break; + case NUM_F7: + bios_scan=0x5a; break; + case NUM_F8: + bios_scan=0x5b; break; + case NUM_F9: + bios_scan=0x5c; break; + case NUM_F10: + bios_scan=0x5d; break; + case NUM_F11: + bios_scan=0x87; break; + case NUM_F12: + bios_scan=0x88; break; + case NUM_PAD_ENTER: + bios_scan=0xe0; break; + case NUM_PRTSCR: + bios_scan=0; break; + case NUM_PAD_SLASH: + bios_scan=0xe0; break; + default: + bios_scan=key&0x7f; break; + } + /* mark the extended keys */ + switch(key) { + /* cursor block keys */ + case NUM_HOME: case NUM_UP: case NUM_PGUP: + case NUM_LEFT: case NUM_RIGHT: + case NUM_END: case NUM_DOWN: case NUM_PGDN: + case NUM_INS: case NUM_DEL: + ascii = 0xe0; + } + } + else { /* unshifted */ + switch(key) { + case NUM_F11: + bios_scan=0x85; break; + case NUM_F12: + bios_scan=0x86; break; + case NUM_PAD_ENTER: + bios_scan=0xe0; break; + case NUM_PRTSCR: + bios_scan=0; break; + case NUM_PAD_SLASH: + bios_scan=0xe0; break; + default: + bios_scan=key&0x7f; break; + } + /* mark the extended keys */ + switch(key) { + /* cursor block keys */ + case NUM_HOME: case NUM_UP: case NUM_PGUP: + case NUM_LEFT: case NUM_RIGHT: + case NUM_END: case NUM_DOWN: case NUM_PGDN: + case NUM_INS: case NUM_DEL: + ascii = 0xe0; + } + } + } + else { /* !make */ + if (key == NUM_SYSRQ) { + special = SP_SYSRQ_BREAK; + } + else if (state->alt_num_buffer && + (key == NUM_L_ALT || key == NUM_R_ALT)) { + bios_scan = 0; + ascii = state->alt_num_buffer; + state->alt_num_buffer = 0; + } + else { + ascii = 0; + } + } +#if 0 + k_printf("KBD: bios_scan=%02x ascii=%02x special=%04x\n", + bios_scan,ascii,special); +#endif + if (special != 0) { + return special; + } + else { + return (bios_scan << 8)| ascii; + } +} + +static void do_shift_keys_r(Boolean make, t_keysym keysym, t_shiftstate *shiftstate_ret) +{ + t_shiftstate mask=0, togglemask=0, shiftstate = *shiftstate_ret; + + /* Note: + * While it is very bad to hard code keys, dos has already + * hard coded the shift keys. Dosemu cannot remove this + * hard coding without confusing dos programs. The point of + * dosemu's configurability is primarily to allow non-us + * keyboards to work as they would in dos. Not to make a + * nice place to customize your keyboard. + */ + switch(keysym) { + case DKY_L_ALT: mask=L_ALT; break; + case DKY_R_ALT: mask=R_ALT; break; + case DKY_L_CTRL: mask=L_CTRL; break; + case DKY_R_CTRL: mask=R_CTRL; break; + case DKY_L_SHIFT: mask=L_SHIFT; break; + case DKY_R_SHIFT: mask=R_SHIFT; break; + case DKY_CAPS: mask=CAPS_PRESSED; togglemask=CAPS_LOCK; break; + case DKY_NUM: mask=NUM_PRESSED; togglemask=NUM_LOCK; break; + case DKY_SCROLL: mask=SCR_PRESSED; togglemask=SCR_LOCK; break; + /* FIXME INS_PRESSED */ + case DKY_INS: mask=INS_PRESSED; togglemask=INS_LOCK; break; + case DKY_PAD_INS: mask=INS_PRESSED; togglemask=INS_LOCK; break; + case DKY_SYSRQ: mask=SYSRQ_PRESSED; break; + default: break; + } + if (make) { + /* don't toggle on repeat events */ + if (!(shiftstate &mask)) { + shiftstate ^= togglemask; + } + shiftstate |= mask; + } + else { + shiftstate &= ~mask; + } + + if (mask) + k_printf("KBD: do_shift_keys(%s): key=%02x new shiftstate=%04x\n", + (shiftstate_ret == &input_keyboard_state.shiftstate)? "input": + (shiftstate_ret == &dos_keyboard_state.shiftstate)? "dos": + "unknown", + keysym, shiftstate); + +#if 0 + if (make && config.shiftclearcaps && + (keysym==DKY_L_SHIFT || keysym==DKY_R_SHIFT)) + { + shiftstate &= ~CAPS_LOCK; + } +#endif + + *shiftstate_ret = shiftstate; + return; +} + +static Boolean is_keypad_key(t_keynum key) +{ + Boolean result = FALSE; + /* Note: + * While it is very bad to hard code keys, dos has already + * hard coded the number pad keys. Dosemu cannot remove this + * hard coding without confusing dos programs. The point of + * dosemu's configurabiltiy is primairy to allow non-us + * keyboards to work as they would in dos. Not to make a nice + * place to customize your keyboard. + */ + + switch(key) { + /* keypad keys */ + case NUM_PAD_7: case NUM_PAD_8: case NUM_PAD_9: case NUM_PAD_MINUS: + case NUM_PAD_4: case NUM_PAD_5: case NUM_PAD_6: case NUM_PAD_PLUS: + case NUM_PAD_1: case NUM_PAD_2: case NUM_PAD_3: + case NUM_PAD_0: case NUM_PAD_DECIMAL: + result = TRUE; + } + return result; +} + +static t_shiftstate translate_shiftstate(t_shiftstate cur_shiftstate, + struct translate_rule *rule, t_keynum key, t_shiftstate *mask) +{ + t_shiftstate shiftstate = cur_shiftstate; + + if (mask) + *mask = 0; + + if (is_keypad_key(key)) { + /* just modifying my local copy of shiftstate... */ + if ((!!(shiftstate & NUM_LOCK)) ^ (!!(shiftstate & ANY_SHIFT))) { + shiftstate |= ANY_SHIFT; + } else { + shiftstate &= ~ANY_SHIFT; + } + if (mask && ((shiftstate & ANY_SHIFT) != (cur_shiftstate & ANY_SHIFT))) + *mask |= NUM_LOCK; + } + if (is_keysym_letter(rule->rule_map[key])) { + /* Use the shift mappings to handle CAPS_LOCK */ + /* just modifying my local copy of shiftstate... */ + if ((!!(shiftstate & CAPS_LOCK)) ^ (!!(shiftstate & ANY_SHIFT))) { + shiftstate |= ANY_SHIFT; + } else { + shiftstate &= ~ANY_SHIFT; + } + if (mask && ((shiftstate & ANY_SHIFT) != (cur_shiftstate & ANY_SHIFT))) + *mask |= CAPS_LOCK; + } +#if SHIFTSTATE_DEBUG + k_printf("KBD: translate(old_shiftstate=%04x, shiftstate=%04x, key=%04x)\n", + cur_shiftstate, shiftstate, key); +#endif + return shiftstate; +} + +static t_keysym *get_rule_ptr(t_keynum key, struct keyboard_state *state) +{ + t_keysym *ch; + t_shiftstate shiftstate = translate_shiftstate(state->shiftstate, + &state->rules->maps[state->rules->activemap].trans_rules.rule_structs.plain, key, NULL); + + if ((shiftstate & ANY_ALT) && (shiftstate & ANY_CTRL)) { + ch = &state->rules->maps[state->rules->activemap].trans_rules.rule_structs.ctrl_alt.rule_map[key]; + } + else if ((shiftstate & R_ALT) && (shiftstate & ANY_SHIFT)) { + ch = &state->rules->maps[state->rules->activemap].trans_rules.rule_structs.shift_altgr.rule_map[key]; + } + else if (shiftstate & R_ALT) { + ch = &state->rules->maps[state->rules->activemap].trans_rules.rule_structs.altgr.rule_map[key]; + } + else if (shiftstate & ANY_ALT) { + ch = &state->rules->maps[state->rules->activemap].trans_rules.rule_structs.alt.rule_map[key]; + } + else if (shiftstate & ANY_CTRL) { + ch = &state->rules->maps[state->rules->activemap].trans_rules.rule_structs.ctrl.rule_map[key]; + } + else if (shiftstate & ANY_SHIFT) { + ch = &state->rules->maps[state->rules->activemap].trans_rules.rule_structs.shift.rule_map[key]; + } + else /* unshifted */ { + ch = &state->rules->maps[state->rules->activemap].trans_rules.rule_structs.plain.rule_map[key]; + } + + return ch; +} + +/* translate a keynum to its ASCII equivalent. + * (*is_accent) returns TRUE if the if char was produced with an accent key. + */ + +static t_keysym translate_r(Boolean make, t_keynum key, Boolean *is_accent, + struct keyboard_state *state) +{ + t_keysym ch = *get_rule_ptr(key, state); + + *is_accent=FALSE; + + if (make && (state->accent != DKY_VOID)) { + t_keysym new_ch = keysym_dead_key_translation(state->accent, ch); + if ((new_ch != ch) && (state->rules->charset.keys[ch].character)) { + /* allow accents for the characters that are + * in the current character set. + */ + ch = new_ch; + *is_accent = TRUE; + } + if (state->rules->charset.keys[ch].character) + state->accent = DKY_VOID; + } + if (make && is_keysym_dead(ch)) { + state->accent = ch; + k_printf("KBD: got accent %04x\n", ch); + } + k_printf("KBD: translated key = %04x\n", ch); + return ch; +} + + +/*********************************************************************************************** + * The key press work horse + ***********************************************************************************************/ + +static inline void send_scan_code_byte(Boolean make, unsigned char c) +{ + if (c) { + if (!make) { + c |= 0x80; + } + write_queue(&keyb_queue, c); + } +} + +static void send_scan_code_string(Boolean make, keystring scan_code_string) +{ + unsigned char c1, c2, c3, c4; + + c1 = (scan_code_string & 0xFF000000) >> 24; + c2 = (scan_code_string & 0x00FF0000) >> 16; + c3 = (scan_code_string & 0x0000FF00) >> 8; + c4 = (scan_code_string & 0x000000FF); + send_scan_code_byte(make, c1); + send_scan_code_byte(make, c2); + send_scan_code_byte(make, c3); + send_scan_code_byte(make, c4); +} + +static inline void assign_bit(Boolean value, int nr, void *addr) +{ + if (value) { + set_bit(nr, addr); + } else { + clear_bit(nr, addr); + } +} + +t_keynum compute_functional_keynum(Boolean make, t_keynum keynum, + struct key_pressed_state *keystate) +{ + unsigned char *keys = keystate->keys; + Boolean old_make; + /* first handle the virtual keys */ + switch(keynum) { + case NUM_PRTSCR_SYSRQ: + /* If the key is already pressed find out what key it + really is */ + if (test_bit(NUM_PRTSCR_SYSRQ, keys)) { + if (test_bit(NUM_PRTSCR, keys)) { + keynum = NUM_PRTSCR; + } else if (test_bit(NUM_SYSRQ, keys)) { + keynum = NUM_SYSRQ; + } else { + /* should never happen */ + keynum = NUM_VOID; + } + } else if (make) { + if (!test_bit(NUM_L_ALT, keys) && + !test_bit(NUM_R_ALT, keys)) { + keynum = NUM_PRTSCR; + } else { + keynum = NUM_SYSRQ; + } + } else { + /* ignore extra breaks */ + keynum = NUM_VOID; + } + break; + case NUM_PAUSE_BREAK: + /* If the key is already pressed find out what key it + really is */ + if (test_bit(NUM_PAUSE_BREAK, keys)) { + if (test_bit(NUM_PAUSE, keys)) { + keynum = NUM_PAUSE; + } else if (test_bit(NUM_BREAK, keys)) { + keynum = NUM_BREAK; + } else { + /* should never happen */ + keynum = NUM_VOID; + } + } else if (make) { + if (!test_bit(NUM_L_CTRL, keys) && + !test_bit(NUM_R_CTRL, keys)) { + keynum = NUM_PAUSE; + } else { + keynum = NUM_BREAK; + } + } else { + /* ignore extra breaks */ + keynum = NUM_VOID; + } + break; + /* pseudo keys aren't real kill them */ + case NUM_FAKE_L_SHIFT: + case NUM_FAKE_R_SHIFT: + case NUM_PRTSCR: + case NUM_SYSRQ: + case NUM_PAUSE: + case NUM_BREAK: + keynum = NUM_VOID; + break; + default: + break; + } + /* Then suppress duplicate key releases */ + + old_make = test_bit(keynum, keys); + if (old_make == make) { + if (!make) { + /* don't send key releases when the key isn't pressed! */ + keynum = NUM_VOID; + } else { + /* auto repeats are o.k. */ + } + } + return keynum; +} + +static void update_keypressed_state(Boolean make, t_keynum keynum, + struct key_pressed_state *keys) +{ + assign_bit(make, keynum, keys); + switch(keynum) { + case NUM_PAUSE: + case NUM_BREAK: + assign_bit(make, NUM_PAUSE_BREAK, keys); + break; + case NUM_PRTSCR: + case NUM_SYSRQ: + assign_bit(make, NUM_PRTSCR_SYSRQ, keys); + break; + } +} + +/* additionally strings */ +#define SCN_FAKE_L_SHIFT 0xe02a +#define SCN_FAKE_R_SHIFT 0xe036 +#define SCN_PAUSE 0xe11d45 +#define SCN_BREAK 0xe046 +#define SCN_PRTSCR 0xe037 +#define SCN_SYSRQ 0x54 +#define SCN_INS 0xe052 +#define SCN_DEL 0xe053 +#define SCN_HOME 0xe047 +#define SCN_END 0xe04f +#define SCN_PGUP 0xe049 +#define SCN_PGDN 0xe051 +#define SCN_UP 0xe048 +#define SCN_DOWN 0xe050 +#define SCN_LEFT 0xe04b +#define SCN_RIGHT 0xe04d +#define SCN_PAD_SLASH 0xe035 +#define SCN_L_SHIFT 0x002a +#define SCN_R_SHIFT 0x0036 +#define SCN_PAUSE_MAKE 0xe11d45 +#define SCN_PAUSE_BREAK 0xe19dc5 + +static keystring compute_scancode_string(Boolean make, t_keynum keynum) +{ + keystring scan_code_string; + + scan_code_string = keynum & 0x7f; + if (keynum & 0x80) { + scan_code_string |= 0xe000; + } + /* Fixup for virtual keys */ + switch(keynum) { + case NUM_PAUSE: + scan_code_string = SCN_PAUSE; + break; + case NUM_BREAK: + scan_code_string = SCN_BREAK; + break; + case NUM_PRTSCR: + scan_code_string = SCN_PRTSCR; + break; + case NUM_SYSRQ: + scan_code_string = SCN_SYSRQ; + break; + } + return scan_code_string; +} + +/* + * All key presses eventually go through put_keynum_r before they are + * put into the queue and fed to dos. This routine carefully keeps + * track of the state before we queue the keys so we can accurately + * predict the key translation dos, or our bios eventually makes. + * + * Also multiple key releases are filtered out, and work is put in to + * add the appropriate dummy scancodes, so we can be indistinguishable + * from a real keyboard. + * + * We also filter out keys here for dosemu's own personal use. + * + * Predicting the key translation is important for put_character, and + * cut and paste that rests on top of put_character. + * + * Not saving the translation is important for some rare but legal + * applications of int15. + * + * Also doing it this way ensures we generate the correct scancodes on non-us + * keyboards for the characters pressed. An important advantage. + */ + +static void put_keynum_r(Boolean make, t_keynum input_keynum, struct keyboard_state *state) +{ + t_keynum keynum; + Boolean old_make; + keystring scan_code_string; + struct key_pressed_state *keys = &state->keys_pressed; + + keynum = compute_functional_keynum(make, input_keynum, keys); + if (keynum == NUM_VOID) { + k_printf("put_keynum_r(%s) called with invalid keynum %02x:%02x\n", + (make?"make":"break"), input_keynum, keynum); + return; + } + old_make = test_bit(keynum, keys); + + k_printf("put_keynum_r: old_make:%d make:%d key:%02x\n", + !!old_make, !!make, keynum); + + /* update various bits of keyboard state */ + if (translate_key(make, keynum, state) == (Bit16u)-1) + return; + + scan_code_string = compute_scancode_string(make, keynum); + +#if PASTE_RECOMPUTE + /* A real pc keyboard for backwards compatibility inserts + * fake presses and releases of the left and right shift keys + */ + + /* I check here for keys that need SCN_FAKE_L_SHIFT or + * SCN_FAKE_R_SHIFT in front of them. + */ + switch(scan_code_string) { + case SCN_INS: case SCN_HOME: case SCN_PGUP: + case SCN_DEL: case SCN_END: case SCN_PGDN: + case SCN_UP: + case SCN_DOWN: case SCN_LEFT: case SCN_RIGHT: + case SCN_PAD_SLASH: + if (make) { + /* Real PC keyboards use the state of the NumLock light, for + * the numlock state. Since I depend on being able to + * pretranslate the keyboard for cut and paste, I'll just use + * my expected numlock state. + * At any rate this is just a heuristic, and not guaranteed to + * be correct so any reasonable approximation should be fine. + */ + Boolean num_state = !!(state->shiftstate & NUM_LOCK); + Boolean fake_lshift = !!test_bit(NUM_FAKE_L_SHIFT, keys); + Boolean fake_rshift = !!test_bit(NUM_FAKE_R_SHIFT, keys); + if (scan_code_string != SCN_PAD_SLASH) { + if (num_state && !fake_lshift && !fake_rshift) { + send_scan_code_string(PRESS, SCN_FAKE_L_SHIFT); + set_bit(NUM_FAKE_L_SHIFT, keys); + } + fake_lshift = fake_lshift && !num_state; + fake_rshift = fake_rshift && !num_state; + } + if (fake_lshift) { + send_scan_code_string(RELEASE, SCN_FAKE_L_SHIFT); + clear_bit(NUM_FAKE_L_SHIFT, keys); + } + if (fake_rshift) { + send_scan_code_string(RELEASE, SCN_FAKE_R_SHIFT); + clear_bit(NUM_FAKE_R_SHIFT, keys); + } + } + send_scan_code_string(make, scan_code_string); + if (!make) { + Boolean lshift = test_bit(NUM_L_SHIFT, keys); + Boolean rshift = test_bit(NUM_R_SHIFT, keys); + if (!!test_bit(NUM_FAKE_L_SHIFT, keys) != !!lshift) { + send_scan_code_string(lshift, SCN_FAKE_L_SHIFT); + assign_bit(lshift, NUM_FAKE_L_SHIFT, keys); + } + if (!!test_bit(NUM_FAKE_R_SHIFT, keys) != !!rshift) { + send_scan_code_string(rshift, SCN_FAKE_R_SHIFT); + assign_bit(rshift, NUM_FAKE_R_SHIFT, keys); + } + } + break; + case SCN_PRTSCR: + if (make) { + Boolean fake_lshift = test_bit(NUM_FAKE_L_SHIFT, keys); + Boolean fake_rshift = test_bit(NUM_FAKE_R_SHIFT, keys); + if (!test_bit(NUM_L_CTRL, keys) && + !test_bit(NUM_R_CTRL, keys) && + !fake_lshift && !fake_rshift) { + send_scan_code_string(PRESS, SCN_FAKE_L_SHIFT); + set_bit(NUM_FAKE_L_SHIFT, keys); + } + } + send_scan_code_string(make, scan_code_string); + if (!make) { + Boolean lshift = test_bit(NUM_L_SHIFT, keys); + Boolean rshift = test_bit(NUM_R_SHIFT, keys); + if (!!test_bit(NUM_FAKE_L_SHIFT, keys) != !!lshift) { + send_scan_code_string(lshift, SCN_FAKE_L_SHIFT); + assign_bit(lshift, NUM_FAKE_L_SHIFT, keys); + } + if (!!test_bit(NUM_FAKE_R_SHIFT, keys) != !!rshift) { + send_scan_code_string(rshift, SCN_FAKE_R_SHIFT); + assign_bit(rshift, NUM_FAKE_R_SHIFT, keys); + } + } + break; + case SCN_L_SHIFT: + { + Boolean rshift = test_bit(NUM_R_SHIFT, keys); + if (!!test_bit(NUM_FAKE_R_SHIFT, keys) != !!rshift) { + send_scan_code_string(rshift, SCN_FAKE_R_SHIFT); + assign_bit(rshift, NUM_FAKE_R_SHIFT, keys); + } + /* Clear fake shift actions if applicable */ + if ((!!old_make != !!make) && + (!!test_bit(NUM_FAKE_L_SHIFT, keys) == !!make)) { + send_scan_code_string(!make, SCN_FAKE_L_SHIFT); + } + assign_bit(make, NUM_FAKE_L_SHIFT, keys); + send_scan_code_string(make, scan_code_string); + break; + } + case SCN_R_SHIFT: + { + Boolean lshift = test_bit(NUM_L_SHIFT, keys); + if (!!test_bit(NUM_FAKE_L_SHIFT, keys) != !!lshift) { + send_scan_code_string(lshift, SCN_FAKE_L_SHIFT); + assign_bit(lshift, NUM_FAKE_L_SHIFT, keys); + } + /* Clear fake shift actions if applicable */ + if ((!!old_make != !!make) && + (!!test_bit(NUM_FAKE_R_SHIFT, keys) == !!make)) { + send_scan_code_string(!make, SCN_FAKE_R_SHIFT); + } + assign_bit(make, NUM_FAKE_R_SHIFT, keys); + send_scan_code_string(make, scan_code_string); + break; + } + default: + { + Boolean lshift = test_bit(NUM_L_SHIFT, keys); + Boolean rshift = test_bit(NUM_R_SHIFT, keys); + if (!!test_bit(NUM_FAKE_L_SHIFT, keys) != !!lshift) { + send_scan_code_string(lshift, SCN_FAKE_L_SHIFT); + assign_bit(lshift, NUM_FAKE_L_SHIFT, keys); + } + if (!!test_bit(NUM_FAKE_R_SHIFT, keys) != !!rshift) { + send_scan_code_string(rshift, SCN_FAKE_R_SHIFT); + assign_bit(rshift, NUM_FAKE_R_SHIFT, keys); + } + send_scan_code_string(make, scan_code_string); + break; + } + } +#else + send_scan_code_string(make, scan_code_string); +#endif + /* although backend_run() is called periodically anyway, for faster response + * it's a good idea to call it straight away. + */ + backend_run(); +} + +static void put_keynum_grp(Boolean make, t_keynum key, t_unicode sym, + struct keyboard_state *state) +{ + if (sym != DKY_VOID) { + /* switch active keymap if needed */ + state->rules->activemap = state->rules->charset.keys[sym].map; + } + put_keynum_r(make, key, state); +} + +static void put_keynum(Boolean make, t_keynum key, t_unicode sym, + struct keyboard_state *state) +{ + if (sym != DKY_VOID) { + t_keysym *ch; + /* switch active keymap if needed */ + state->rules->activemap = state->rules->charset.keys[sym].map; + ch = get_rule_ptr(key, state); + if (!config.layout_auto || is_keysym_dead(*ch)) + error("$_layout inconsistency\n"); + else if (*ch != sym && state->accent == DKY_VOID) { + k_printf("replace char %x with %x\n", *ch, sym); + *ch = sym; + } + } + put_keynum_r(make, key, state); +} + + /*********************************************************************************************** + * Intermediate level interface functions + ***********************************************************************************************/ + +/* + * DANG_BEGIN_FUNCTION compute_keynum + * + * The task of compute_keynum() is to 'collect' keyboard bytes (e.g. + * 0xe0 prefixes) until it thinks it has assembled an entire keyboard + * event. The entire keyboard event is then returned, otherwise + * NUM_VOID is returned. + * + * DANG_END_FUNCTION + */ + +t_keynum compute_keynum(Boolean *make_ret, + t_rawkeycode code, struct raw_key_state *state) +{ + t_scancode scan; + t_keynum key; + Boolean make = FALSE; + *make_ret = FALSE; + + k_printf("KBD: compute_keynum(%x, %x, %s) called\n", + (int)code, state->rawprefix, + (state ==&input_keyboard_state.raw_state)? "input": + (state ==&dos_keyboard_state.raw_state)? "dos": + "unknown"); + + if (code==0xe0 || code==0xe1) { + state->rawprefix=code; + return NUM_VOID; + } + + if (state->rawprefix==0xe1) { /* PAUSE key: expext 2 scancode bytes */ + state->rawprefix = 0xe100 | code; + return NUM_VOID; + } + + scan = (state->rawprefix<<8) | code; + state->rawprefix=0; + + if ((scan&0xff0000) == 0xe10000) + k_printf("KBD: E1 scancode 0x%06x\n",(int)scan); + + /* for BIOS, ignore the fake shift scancodes 0xe02a, 0xe0aa 0xe036, 0xe0b6 + sent by the keyboard for fn/numeric keys */ + + if (scan==0xe02a || scan==0xe0aa || scan==0xe036 || scan==0xe0b6) { + key = NUM_VOID; + } + else { + if (scan==SCN_PAUSE_MAKE) { + key = NUM_PAUSE_BREAK; + make = TRUE; + } + else if (scan==SCN_PAUSE_BREAK) { + key = NUM_PAUSE_BREAK; + make = FALSE; + } + else if ((scan & (~0x80)) == SCN_PRTSCR) { + key = NUM_PRTSCR_SYSRQ; + make = ((scan & 0x80) == 0); + } else { + key = scan & 0x7f; + if ((scan & 0xff00) != 0) { + key |= 0x80; + } + make = ((scan & 0x80) == 0); + } + } + if (!validate_keynum(key)) { + key = NUM_VOID; + } + *make_ret = make; + return key; +} +/* + * DANG_BEGIN_FUNCTION translate_key + * translate_key takes a keysym event and calculates the appropriate + * bios translation. + * + * As a side effect translate_key updates the appropriate pieces of state + * to reflect the current keyboard state. + * + * Calling translate_key twice on the same data is likely to be hazardous. + * + * DANG_END_FUNCTION + */ + +Bit16u translate_key(Boolean make, t_keynum key, + struct keyboard_state *state) +{ + Boolean is_accent; + Bit16u bios_key; + Bit8u ascii; + t_keysym keysym; + + k_printf("translate_key: make=%d, key=%04x, %s\n", + make, key, + (state == &input_keyboard_state)? "input": + (state == &dos_keyboard_state)? "dos": + "unknown"); + + /* Each of the following functions: + * update_keypressed_state, + * do_shift_keys_r, translate_r & make_bios_code_r can potentially + * change the keyboard state. + */ + + update_keypressed_state(make, key, &state->keys_pressed); + + is_accent = FALSE; + keysym = translate_r(make, key, &is_accent, state); + do_shift_keys_r(make, keysym, &state->shiftstate); + if (handle_dosemu_keys(make, keysym)) { + return -1; + } + ascii = state->rules->charset.keys[keysym].character; + + /* translate to a BIOS (soft) scancode. For accented characters, the BIOS + * code is 0, i.e. bios_key == ((0 << 8)|ascii) == ascii + * just like ALT-numpad sequences. + */ + bios_key = state->accent != DKY_VOID ? 0 : + is_accent ? ascii : make_bios_code_r(make, key, ascii, keysym, state); + + if (bios_key == 0x23e0 || bios_key == 0x18e0) /* Cyrillic_er work around */ + bios_key &= 0x00FF; /* ^^^^^^^^^^^^^^^^^^ Oacute */ +#if 0 + k_printf("translate_key: keysym=%04x bios_key=%04x\n", + keysym, bios_key); +#endif + + return bios_key; +} + + + +/* typing a specific character */ + + +/* get the logical modifiers from the keyboard state */ +t_modifiers get_modifiers_r(t_shiftstate shiftstate) +{ + t_modifiers modifiers = 0; + if (shiftstate & ANY_SHIFT) modifiers |= MODIFIER_SHIFT; + if (shiftstate & ANY_CTRL) modifiers |= MODIFIER_CTRL; + if (shiftstate & ANY_ALT) modifiers |= MODIFIER_ALT; + /* Note: when R_ALT is active both MODIFIER_ALT & MODIFIER_ALTGR are + * active + */ + if (shiftstate & R_ALT) modifiers |= MODIFIER_ALTGR; + if (shiftstate & CAPS_LOCK) modifiers |= MODIFIER_CAPS; + if (shiftstate & NUM_LOCK) modifiers |= MODIFIER_NUM; + if (shiftstate & SCR_LOCK) modifiers |= MODIFIER_SCR; + if (shiftstate & INS_LOCK) modifiers |= MODIFIER_INS; + return modifiers; +} + +/* + * sync_shift_state takes the current shiftstate, and the shiftstate + * we want to set and generates the appropriate key strokes to cause + * the new shiftstate to come into effect. + */ + +static void sync_shift_state(t_modifiers desired, struct keyboard_state *state) +{ + t_modifiers current = get_modifiers_r(state->shiftstate); + struct key_pressed_state *keys = &state->keys_pressed; + + /* altgr implies alt */ + if (desired & MODIFIER_ALTGR) desired |= MODIFIER_ALT; + + /* To keep some bits constant copy them from state->shiftstate into + * desired. + */ + if (current == desired) { + return; + } + k_printf("KBD: sync_shift_state(%s): current=%04x desired=%04x\n", + ((state == &input_keyboard_state)? "input": + (state == &dos_keyboard_state)? "dos": + "unknown"), + current, desired); + + if (!!(current & MODIFIER_INS) != !!(desired & MODIFIER_INS)) { + t_keynum keynum1 = state->rules->charset.keys[DKY_INS].key; + t_keynum keynum2 = state->rules->charset.keys[DKY_PAD_INS].key; + + /* by preference toggle something that is already held down */ + if (test_bit(keynum1, state)) { + put_keynum_r(RELEASE, keynum1, state); + put_keynum_r(PRESS, keynum1, state); + } else if (!(current & MODIFIER_NUM) && test_bit(keynum2, state)) { + put_keynum_r(RELEASE, keynum2, state); + put_keynum_r(PRESS, keynum2, state); + } else { + put_keynum_r(PRESS, keynum1, state); + put_keynum_r(RELEASE, keynum1, state); + } + } + if (!!(current & MODIFIER_CAPS) != !!(desired & MODIFIER_CAPS)) { + t_keynum keynum = state->rules->charset.keys[DKY_CAPS].key; + if (test_bit(keynum, keys)) { + put_keynum_r(RELEASE, keynum, state); + put_keynum_r(PRESS, keynum, state); + } else { + put_keynum_r(PRESS, keynum, state); + put_keynum_r(RELEASE, keynum, state); + } + } + if (!!(current & MODIFIER_NUM) != !!(desired & MODIFIER_NUM)) { + t_keynum keynum = state->rules->charset.keys[DKY_NUM].key; + if (test_bit(keynum, keys)) { + put_keynum_r(RELEASE, keynum, state); + put_keynum_r(PRESS, keynum, state); + } else { + put_keynum_r(PRESS, keynum, state); + put_keynum_r(RELEASE, keynum, state); + } + } + if (!!(current & MODIFIER_SCR) != !!(desired & MODIFIER_SCR)) { + t_keynum keynum = state->rules->charset.keys[DKY_SCROLL].key; + if (test_bit(keynum, keys)) { + put_keynum_r(RELEASE, keynum, state); + put_keynum_r(PRESS, keynum, state); + } else { + put_keynum_r(PRESS, keynum, state); + put_keynum_r(RELEASE, keynum, state); + } + } + if (!!(current & MODIFIER_SHIFT) != !!(desired & MODIFIER_SHIFT)) { + t_keynum lkeynum = state->rules->charset.keys[DKY_L_SHIFT].key; + t_keynum rkeynum = state->rules->charset.keys[DKY_R_SHIFT].key; + if (current & MODIFIER_SHIFT) { + if (state->shiftstate & L_SHIFT) { + put_keynum_r(RELEASE, lkeynum, state); + } + if (state->shiftstate & R_SHIFT) { + put_keynum_r(RELEASE, rkeynum, state); + } + } else { + put_keynum_r(PRESS, lkeynum, state); + } + } + if (!!(current & MODIFIER_CTRL) != !!(desired & MODIFIER_CTRL)) { + t_keynum lkeynum = state->rules->charset.keys[DKY_L_CTRL].key; + t_keynum rkeynum = state->rules->charset.keys[DKY_R_CTRL].key; + if (current & MODIFIER_CTRL) { + if (state->shiftstate & L_CTRL) { + put_keynum_r(RELEASE, lkeynum, state); + } + if (state->shiftstate & R_CTRL) { + put_keynum_r(RELEASE, rkeynum, state); + } + } else { + put_keynum_r(PRESS, lkeynum, state); + } + } + if (!!(current & MODIFIER_ALTGR) != !!(desired & MODIFIER_ALTGR)) { + t_keynum keynum = state->rules->charset.keys[DKY_R_ALT].key; + if (current & MODIFIER_ALTGR) { + put_keynum_r(RELEASE, keynum, state); + } else { + put_keynum_r(PRESS, keynum, state); + } + } + /* reread because of interactions */ + current = get_modifiers_r(state->shiftstate); + if (!!(current & MODIFIER_ALT) != !!(desired & MODIFIER_ALT)) { + t_keynum lkeynum = state->rules->charset.keys[DKY_L_ALT].key; + t_keynum rkeynum = state->rules->charset.keys[DKY_R_ALT].key; + if (current & MODIFIER_ALT) { + if (state->shiftstate & L_ALT) { + put_keynum_r(RELEASE, lkeynum, state); + } + if (state->shiftstate & R_ALT) { + put_keynum_r(RELEASE, rkeynum, state); + } + } else { + put_keynum_r(PRESS, lkeynum, state); + } + } + k_printf("KBD: sync_shift_state(%s) done: current=%04x desired=%04x\n", + ((state == &input_keyboard_state)? "input": + (state == &dos_keyboard_state)? "dos": + "unknown"), + get_modifiers_r(state->shiftstate), desired); +} + +/* + * type_alt_num() is for those occasions when we desired to type a + * specific letter, as in cut and paste, but we don't have a normal + * method to type that letter. So we generate the keystrokes needed + * to use the alt num buffer to type the desired character. + */ +static void type_alt_num(unsigned char ascii, struct keyboard_state *state) +{ + int old_alt_num_buffer; + int amount_to_add, one,two,three; + t_shiftstate alt_shiftstate; + + static const t_keynum num_key[] = + { NUM_PAD_0, NUM_PAD_1, NUM_PAD_2, NUM_PAD_3, NUM_PAD_4, + NUM_PAD_5, NUM_PAD_6, NUM_PAD_7, NUM_PAD_8, NUM_PAD_9 }; + + k_printf("KBD: type_alt_num(%04x, '%c') called\n", + ascii, ascii?ascii:' '); + /* insert these keys as alt number combinations, + * after appropriately setting the shift state. + */ + alt_shiftstate = state->shiftstate & ANY_ALT; + if (alt_shiftstate & ANY_ALT) { + old_alt_num_buffer = state->alt_num_buffer; + } else { + old_alt_num_buffer = 0; + put_keynum_r(PRESS, NUM_L_ALT, state); + } + amount_to_add = (ascii - old_alt_num_buffer) & 0xff; + three = amount_to_add %10; amount_to_add /= 10; + two = amount_to_add %10; amount_to_add /= 10; + one = amount_to_add %10; + + put_keynum_r(PRESS, num_key[one], state); + put_keynum_r(RELEASE, num_key[one], state); + put_keynum_r(PRESS, num_key[two], state); + put_keynum_r(RELEASE, num_key[two], state); + put_keynum_r(PRESS, num_key[three], state); + put_keynum_r(RELEASE, num_key[three], state); + + if (!(alt_shiftstate & ANY_ALT)) { + put_keynum_r(RELEASE, NUM_L_ALT, state); + return; /* LEAVE */ + } + /* release the alt keys to force the ascii code to appear */ + if (alt_shiftstate &L_ALT) { + put_keynum_r(RELEASE, NUM_L_ALT, state); + } + if (alt_shiftstate &R_ALT) { + put_keynum_r(RELEASE, NUM_R_ALT, state); + } + /* press the alt keys again */ + if (alt_shiftstate &L_ALT) { + put_keynum_r(PRESS, NUM_L_ALT, state); + } + if (alt_shiftstate &R_ALT) { + put_keynum_r(PRESS, NUM_R_ALT, state); + } + + /* press keys to restore the old alt_num_buffer */ + amount_to_add = old_alt_num_buffer; + three = amount_to_add %10; amount_to_add /= 10; + two = amount_to_add %10; amount_to_add /= 10; + one = amount_to_add %10; + + put_keynum_r(PRESS, num_key[one], state); + put_keynum_r(RELEASE, num_key[one], state); + put_keynum_r(PRESS, num_key[two], state); + put_keynum_r(RELEASE, num_key[two], state); + put_keynum_r(PRESS, num_key[three], state); + put_keynum_r(RELEASE, num_key[three], state); + +} + +/* + * put_character_symbol() uses the precalculated reverse key maps to + * press a specific character, and release it. If there is no + * appropriate entry in the reverse keymap, it calls type_alt_num() to + * type it that way instead. + * + * It also handles a slightly ugly case. Sometimes it is simply known that we + * want some symbol with extra modifiers added. Examples are Ctrl-A or Alt-A. + * Quite a few of these don't have their own special symbols, and it wouldn't + * help even if they did, because we generally don't have enough information to + * map those hypothetical symbols to keys. + * + * I make an attempt to handle that ugly case here, but for symbols that you + * require dead keys to press, or symbols that can only be pressed with an + * alt# combination I ignore the request for extra modifiers. Perhaps later. + * + * Note: this routine does not allow for the modification of the shiftstate! + */ + +static void put_character_symbol( + Boolean make, t_modifiers modifiers, t_keysym ch, + struct keyboard_state *state) +{ + t_modifiers old_shiftstate, new_shiftstate; + struct press_state *key; + if (ch == DKY_VOID) { + return; + } + old_shiftstate = get_modifiers_r(state->shiftstate); + key = &state->rules->charset.keys[ch]; + /* switch active keymap if needed */ + state->rules->activemap = key->map; + new_shiftstate = key->shiftstate | (old_shiftstate & key->shiftstate_mask); + if (key->deadsym == DKY_VOID) { + new_shiftstate |= modifiers; + } + if (key->key != NUM_VOID) { + if (make) { + /* Note: dead keys always have the same shiftstate */ + sync_shift_state(new_shiftstate, state); + put_character_symbol(PRESS, 0, key->deadsym, state); + put_character_symbol(RELEASE, 0, key->deadsym, state); + put_keynum_r(PRESS, key->key, state); + } + else { + put_keynum_r(RELEASE, key->key, state); + } + } else { + if (make) { + type_alt_num(state->rules->charset.keys[ch].character, state); + } + } + sync_shift_state(old_shiftstate, state); +} + +/*********************************************************************************************** + * High-level interface functions + ***********************************************************************************************/ + +/* + * DANG_BEGIN_FUNCTION put_rawkey + * + * This function sends a raw keycode byte, e.g. read directly from the hardware, + * to DOS. It is both queued for the port60h emulation and processed for the + * BIOS keyboard buffer, using the national translation tables etc. + * + * For DOS applications using int16h we will therefore not have to load + * KEYB.EXE, others (e.g. games) need their own drivers anyway. + * + * This function is used if we are at the console and config.rawkeyboard=on. + * + * DANG_END_FUNCTION + */ + +/* + * the main task of putrawkey() is to 'collect' keyboard bytes (e.g. + * 0xe0 prefixes) until it thinks it has assembled an entire keyboard + * event. The combined keycode is then both translated and stored + * in the 'raw' field of the queue. + */ + +void put_rawkey(t_rawkeycode code) +{ + Boolean make; + t_keynum key; + key = compute_keynum(&make, code, &input_keyboard_state.raw_state); + if (key != NUM_VOID) { + put_keynum_r(make, key, &input_keyboard_state); + } +} + +/* + * DANG_BEGIN_FUNCTION move_keynum + * + * This does all the work of sending a key event to DOS. + * Either pressing a key releasing one. The key to move is + * the key specified by keynum. + * + * keynum - the keynum from keynum.h indicating a physical key + * make - TRUE for key press, FALSE for release + * + * Applications using int16h will always see the appropriate ASCII code + * for the given keyboard key and the current keyboard state. All the + * character translation is done for you to keep from reporting + * inconsistent key events. + * + * An emulated hardware scancode is also sent to port60h. + * + * Note that you have to send both MAKE (press) and BREAK (release) events. + * If no BREAK codes are available (e.g. terminal mode), send them + * immediately after the MAKE codes. + * + * DANG_END_FUNCTION + */ + +int move_keynum(Boolean make, t_keynum keynum, t_unicode sym) +{ + k_printf("move_keynum: keynum=%04x\n", keynum); + /* move_keynum only works for valid keynum... */ + assert(keynum != NUM_VOID); + put_keynum(make, keynum, sym, &input_keyboard_state); + return 0; +} + +int move_keynum_grp(Boolean make, t_keynum keynum, int grp) +{ + struct keyboard_state *state = &input_keyboard_state; + k_printf("move_keynum_grp: keynum=%04x\n", keynum); + /* move_keynum only works for valid keynum... */ + assert(keynum != NUM_VOID); + state->rules->activemap = grp; + if (state->rules->activemap >= MAPS_MAX || + state->rules->maps[state->rules->activemap]. + keyboard == -1) { + k_printf("no such map %i\n", state->rules->activemap); + state->rules->activemap = 0; + } + put_keynum_r(make, keynum, state); + return 0; +} + +int move_keynum_grpsym(Boolean make, t_keynum keynum, t_unicode sym) +{ + k_printf("move_keynum_grpsym: keynum=%04x\n", keynum); + /* move_keynum only works for valid keynum... */ + assert(keynum != NUM_VOID); + assert(sym != DKY_VOID); + put_keynum_grp(make, keynum, sym, &input_keyboard_state); + return 0; +} + +/* + * DANG_BEGIN_FUNCTION keysym_to_keynum + * + * Allows peeking into the keytables. + * This returns the keynum a given keysym sits on. + * Unless modifiers is NULL, the modifiers needed to obtain the keysym + * is written to the target of modifiers. + * + * DANG_END_FUNCTION + */ + +t_keynum keysym_to_keynum(t_keysym key, t_modifiers * modifiers) +{ + struct press_state *sym_info; + t_keynum keynum = NUM_VOID; + t_modifiers mods = MODIFIER_VOID; + + if (key != DKY_VOID) { + sym_info = &input_keyboard_state.rules->charset.keys[key]; + keynum = sym_info->key; + mods = sym_info->shiftstate; + } + if (modifiers) + *modifiers = mods; + + return keynum; +} + +/* + * DANG_BEGIN_FUNCTION move_key + * + * This does all the work of sending a key event to DOS. + * Either pressing a key releasing one. The key to move is + * the key that is labeled with the specified keysym. + * + * key - the keysym, one of the DKY_ constants from new-kbd.h + * make - TRUE for key press, FALSE for release + * + * Applications using int16h will always see the appropriate ASCII code + * for the given keyboard key and the current keyboard state. All the + * character translation is done for you to keep from reporting + * inconsistent key events. + * + * An emulated hardware scancode is also sent to port60h. + * + * Note that you have to send both MAKE (press) and BREAK (release) events. + * If no BREAK codes are available (e.g. terminal mode), send them + * immediately after the MAKE codes. + * + * DANG_END_FUNCTION + */ + +int move_key(Boolean make, t_keysym key) +{ + int result = 0; + struct press_state *sym_info; + t_keynum keynum; + t_keysym deadsym; + sym_info = &input_keyboard_state.rules->charset.keys[key]; + keynum = sym_info->key; + deadsym = sym_info->deadsym; + k_printf("move_key: key=%04x keynum=%04x\n", + key, keynum); + /* move_key only works for key caps, and a symbol only reachable + * by pressing a dead key first doesn't qualify as a key cap. + */ + if ((keynum != NUM_VOID) && (deadsym == DKY_VOID)) { + put_keynum_r(make, keynum, &input_keyboard_state); + } else { + result = -1; + } + return result; +} + +/* + * DANG_BEGIN_FUNCTION put_symbol + * + * This does all the work of sending a key event to DOS. + * sym -- The unicode value of the symbol you want to send + * + * Applications using int16h will always see the symbol passed + * here, if it is representable in the current dos character set. The + * appropriate scancodes are generated automatically to keep the + * keyboard code consistent. + * + * An emulated hardware scancode is also sent to port60h. + * + * Note that you have to send both MAKE (press) and BREAK (release) events. + * If no BREAK codes are available (e.g. terminal mode), send them + * immediately after the MAKE codes. + * + * DANG_END_FUNCTION + */ + +void put_symbol(Boolean make, t_keysym sym) +{ + k_printf("put_symbol: keysym=%04x\n", + sym); + put_character_symbol(make, 0, sym, &input_keyboard_state); +} + + +/* + * DANG_BEGIN_FUNCTION put_modified_symbol + * + * This does all the work of sending a key event to DOS. + * sym -- The unicode value of the symbol you want to send + * modifiers -- modifiers like alt etc you what to change your symbol with. + * + * This function is a concession to the reality, in which key events + * are a composed of active modifiers, and a key label. + * + * This function behaves as put_symbol does, except before pressing + * the key it adds the specified modifiers to the modifiers it would + * normally use. + * + * For cases where the symbol can only be created by an alt# combination + * or by pressing a dead key (Basically any case where more than one + * key is required, after setting the shiftstate) it gives up and just + * sends the symbol. + * + * Note that you have to send both MAKE (press) and BREAK (release) events. + * If no BREAK codes are available (e.g. terminal mode), send them + * immediately after the MAKE codes. + * + * DANG_END_FUNCTION + */ + +void put_modified_symbol(Boolean make, t_modifiers modifiers, t_keysym sym) +{ + k_printf("put_symbol: modifiers=%04x keysym=%04x\n", + modifiers, sym); + put_character_symbol(make, modifiers, sym, &input_keyboard_state); +} + +/* + * DANG_BEGIN_FUNCTION get_shiftstate + * + * This simply reads the keyboard server's shift state. + * + * This is intended to be used in conjunction with set_shiftstate + * to sync up a shiftstate with a source of key events. + * + * With the addition of this function the keyboard interface is clean enough + * so if needed a completely different translation engine can be dropped in + * to support a totally different environment (windows or whatever). + * + * DANG_END_FUNCTION + */ + +t_modifiers get_shiftstate(void) +{ + return get_modifiers_r(input_keyboard_state.shiftstate); +} + +/* + * DANG_BEGIN_FUNCTION set_shiftstate + * + * This simply sets the keyboard server's shift state. + * + * If there are shiftstate bits you want to keep fixed simply grab them with + * get_shiftstate, before calling this function. + * + * This changes the keyboard flags by generating the appropriate + * shift key make/break codes that normally come along with such + * changes. So this function should be safe in any context. + * + * Note also that you can't simply write to the shiftstate variable + * instead of using this function. + * + * DANG_END_FUNCTION + */ + +void set_shiftstate(t_modifiers s) +{ + sync_shift_state(s, &input_keyboard_state); +} + + +int keyb_server_reset(void) +{ + k_printf("KBD: keyb_server_reset()\n"); + + keyb_reset_state(); + backend_reset(); + + return TRUE; +} + +int keyb_server_init(void) +{ + k_printf("KBD: keyb_server_init()\n"); + if (!config.keytable) + setup_default_keytable(); + keyb_init_state(); + return 1; +} + +void keyb_server_close(void) +{ + k_printf("KBD: keyb_server_close()\n"); +} + +void keyb_server_run(void) +{ + backend_run(); +} + diff --git a/src/base/lib/libpcl/Makefile b/src/base/lib/libpcl/Makefile new file mode 100644 index 0000000..30e58f8 --- /dev/null +++ b/src/base/lib/libpcl/Makefile @@ -0,0 +1,15 @@ +# +# (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". +# +# for details see file COPYING.DOSEMU in the DOSEMU distribution +# + +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf +ALL_CPPFLAGS += -I$(REALTOPDIR)/src/base/lib/mcontext + +CFILES=pcl.c pcl_ctx.c + +include $(REALTOPDIR)/src/Makefile.common + +all: lib diff --git a/src/base/lib/libpcl/pcl.c b/src/base/lib/libpcl/pcl.c new file mode 100644 index 0000000..fa816f2 --- /dev/null +++ b/src/base/lib/libpcl/pcl.c @@ -0,0 +1,236 @@ +/* This code is taken from libpcl library. + * Rip-off done by stsp for dosemu2 project. + * Original copyrights below. */ + +/* + * PCL by Davide Libenzi (Portable Coroutine Library) + * Copyright (C) 2003..2010 Davide Libenzi + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Davide Libenzi + * + */ + +#define WANT_ASAN 1 +#ifdef __clang__ +#define HAS_FEATURE(x) __has_feature(x) +#else +#define HAS_FEATURE(x) 0 +#endif +#if (defined(__SANITIZE_ADDRESS__) || HAS_FEATURE(address_sanitizer)) && WANT_ASAN +#define USE_ASAN 1 +#else +#define USE_ASAN 0 +#endif +#if USE_ASAN +#include +#endif +#include +#include +#include "pcl.h" +#include "pcl_private.h" +#include "pcl_ctx.h" + +static cothread_ctx *co_get_thread_ctx(coroutine *co); + +static void co_switch_context(co_base *octx, co_base *nctx) +{ +#if USE_ASAN + void *fake_stack_save = NULL; + __sanitizer_start_switch_fiber(octx->exited ? NULL : &fake_stack_save, + nctx->stack, nctx->stack_size); + nctx->tmp = fake_stack_save; +#endif + if (octx->ctx.ops->swap_context(&octx->ctx, nctx->ctx.cc) < 0) { + fprintf(stderr, "[PCL] Context switch failed\n"); + exit(1); + } +#if USE_ASAN + __sanitizer_finish_switch_fiber(fake_stack_save, NULL, NULL); +#endif +} + + +static void co_runner(void *arg) +{ + coroutine *co = arg; + cothread_ctx *tctx; + +#if USE_ASAN + __sanitizer_finish_switch_fiber(co->tmp, NULL, NULL); +#endif + tctx = co_get_thread_ctx(co); + co->restarget = co->caller; + co->func(co->data); + co_exit(tctx); + abort(); +} + +static coroutine *do_co_create(void (*func)(void *), void *data, void *stack, + int size, int corosize) +{ + int alloc = 0; + coroutine *co; + + if ((size &= ~(sizeof(long) - 1)) < CO_MIN_SIZE) + return NULL; + if (stack == NULL) { + size = CO_STK_COROSIZE(size + corosize); + stack = malloc(size); + if (stack == NULL) + return NULL; + alloc = size; + } + co = stack; + co->stack = (char *) stack + CO_STK_COROSIZE(corosize); + co->stack_size = size; + co->alloc = alloc; + co->func = func; + co->data = data; + co->exited = 0; + + return co; +} + +coroutine_t co_create(cohandle_t handle, void (*func)(void *), void *data, + void *stack, int size) +{ + coroutine *co; + cothread_ctx *tctx = (cothread_ctx *)handle; + + co = do_co_create(func, data, stack, size, tctx->ctx_sizeof); + if (!co) + return NULL; + co->ctx = tctx->co_main.ctx; + co->ctx.cc = co->stk; + co->ctx_main = tctx; + if (co->ctx.ops->create_context(&co->ctx, co_runner, co, co->stack, + size - CO_STK_COROSIZE(tctx->ctx_sizeof)) < 0) { + if (co->alloc) + free(co); + return NULL; + } + + return (coroutine_t) co; +} + +void co_delete(coroutine_t coro) +{ + coroutine *co = (coroutine *) coro; + cothread_ctx *tctx = co_get_thread_ctx(co); + + if ((co_base *)co == tctx->co_curr) { + fprintf(stderr, "[PCL] Cannot delete itself: curr=%p\n", + tctx->co_curr); + exit(1); + } + if (co->alloc) + free(co); +} + +void co_call(coroutine_t coro) +{ + coroutine *co = (coroutine *) coro; + cothread_ctx *tctx = co_get_thread_ctx(co); + co_base *oldco = tctx->co_curr; + + co->caller = tctx->co_curr; + tctx->co_curr = co; + + co_switch_context(oldco, co); + + if (co->exited) + co_delete(co); +} + +void co_resume(cohandle_t handle) +{ + cothread_ctx *tctx = (cothread_ctx *)handle; + + co_call(tctx->co_curr->restarget); + tctx->co_curr->restarget = tctx->co_curr->caller; +} + +void co_exit(cohandle_t handle) +{ + cothread_ctx *tctx = (cothread_ctx *)handle; + co_base *newco = tctx->co_curr->restarget, *co = tctx->co_curr; + + co->exited = 1; + tctx->co_curr = newco; + + co_switch_context(co, newco); +} + +coroutine_t co_current(cohandle_t handle) +{ + cothread_ctx *tctx = (cothread_ctx *)handle; + + return (coroutine_t) tctx->co_curr; +} + +void *co_get_data(coroutine_t coro) +{ + coroutine *co = (coroutine *) coro; + + return co->data; +} + +void *co_set_data(coroutine_t coro, void *data) +{ + coroutine *co = (coroutine *) coro; + void *odata; + + odata = co->data; + co->data = data; + + return odata; +} + +/* + * Simple case, the single thread one ... + */ + +static void do_co_init(cothread_ctx *tctx) +{ + tctx->co_main.ctx.cc = tctx->stk0; + tctx->co_main.ctx_main = tctx; + tctx->co_main.exited = 0; + tctx->co_curr = &tctx->co_main; +} + +cohandle_t co_thread_init(enum CoBackend b) +{ + int sz = ctx_sizeof(b); + cothread_ctx *tctx = malloc(sizeof(cothread_ctx) + CO_STK_ALIGN(sz)); + + do_co_init(tctx); + ctx_init(b, &tctx->co_main.ctx.ops); + tctx->ctx_sizeof = sz; + return tctx; +} + +void co_thread_cleanup(cohandle_t handle) +{ + cothread_ctx *tctx = (cothread_ctx *)handle; + + free(tctx); +} + +static cothread_ctx *co_get_thread_ctx(coroutine *co) +{ + return co->ctx_main; +} diff --git a/src/base/lib/libpcl/pcl.h b/src/base/lib/libpcl/pcl.h new file mode 100644 index 0000000..1b468c2 --- /dev/null +++ b/src/base/lib/libpcl/pcl.h @@ -0,0 +1,68 @@ +/* + * PCL by Davide Libenzi (Portable Coroutine Library) + * Copyright (C) 2003..2010 Davide Libenzi + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Davide Libenzi + * + */ + +#if !defined(PCL_H) +#define PCL_H + +#ifdef __cplusplus +#define PCLXC extern "C" +#else +#define PCLXC +#endif + +typedef void *coroutine_t; +typedef void *cohandle_t; + +#define _WANT_UCONTEXT 0 +#if _WANT_UCONTEXT || !defined(MCONTEXT) +#define WANT_UCONTEXT 1 +#else +#define WANT_UCONTEXT 0 +#endif + +enum CoBackend { +#ifdef MCONTEXT + PCL_C_MC, +#else +/* our libmcontext doesn't yet support aarch64 */ +#define PCL_C_MC PCL_C_UC +#endif +#if WANT_UCONTEXT + PCL_C_UC, +#endif + PCL_C_MAX }; + +PCLXC cohandle_t co_thread_init(enum CoBackend b); +PCLXC void co_thread_cleanup(cohandle_t handle); + +PCLXC coroutine_t co_create(cohandle_t handle, void (*func)(void *), + void *data, void *stack, int size); +PCLXC void co_delete(coroutine_t coro); +PCLXC void co_call(coroutine_t coro); +PCLXC void co_resume(cohandle_t handle); +PCLXC void co_exit(cohandle_t handle); +PCLXC coroutine_t co_current(cohandle_t handle); +PCLXC void *co_get_data(coroutine_t coro); +PCLXC void *co_set_data(coroutine_t coro, void *data); + +#endif + diff --git a/src/base/lib/libpcl/pcl_ctx.c b/src/base/lib/libpcl/pcl_ctx.c new file mode 100644 index 0000000..c0c0da2 --- /dev/null +++ b/src/base/lib/libpcl/pcl_ctx.c @@ -0,0 +1,144 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: context handling backends for libpcl + * + * Author: Stas Sergeev + */ + +#include +#include +#ifdef MCONTEXT +#include "mcontext.h" +#endif +#include "pcl.h" +#include "pcl_private.h" +#include "pcl_ctx.h" + +#if WANT_UCONTEXT +static int ctx_get_context(struct s_co_ctx *ctx) +{ + return getcontext((ucontext_t *)ctx->cc); +} + +static int ctx_set_context(struct s_co_ctx *ctx) +{ + return setcontext((ucontext_t *)ctx->cc); +} + +static int ctx_swap_context(struct s_co_ctx *ctx1, void *ctx2) +{ + return swapcontext((ucontext_t *)ctx1->cc, ctx2); +} + +static int ctx_create_context(co_ctx_t *ctx, void (*func)(void*), void *arg, + char *stkbase, long stksiz) +{ + ucontext_t *cc = (ucontext_t *)ctx->cc; + + if (getcontext(cc)) + return -1; + cc->uc_link = NULL; + cc->uc_stack.ss_sp = stkbase; + cc->uc_stack.ss_size = stksiz - sizeof(long); + cc->uc_stack.ss_flags = 0; + makecontext(cc, (void*)func, 1, arg); + + return 0; +} + +static struct pcl_ctx_ops ctx_ops = { + .create_context = ctx_create_context, + .get_context = ctx_get_context, + .set_context = ctx_set_context, + .swap_context = ctx_swap_context, +}; +#endif + +#ifdef MCONTEXT +static int mctx_get_context(struct s_co_ctx *ctx) +{ + return getmcontext((m_ucontext_t *)ctx->cc); +} + +static int mctx_set_context(struct s_co_ctx *ctx) +{ + return setmcontext((m_ucontext_t *)ctx->cc); +} + +static int mctx_swap_context(struct s_co_ctx *ctx1, void *ctx2) +{ + return swapmcontext((m_ucontext_t *)ctx1->cc, ctx2); +} + +static int mctx_create_context(co_ctx_t *ctx, void (*func)(void*), void *arg, + char *stkbase, long stksiz) +{ + m_ucontext_t *cc = (m_ucontext_t *)ctx->cc; + + if (getmcontext(cc)) + return -1; + cc->uc_link = NULL; + cc->uc_stack.ss_sp = stkbase; + cc->uc_stack.ss_size = stksiz - sizeof(long); + cc->uc_stack.ss_flags = 0; + makemcontext(cc, func, arg); + + return 0; +} + +static struct pcl_ctx_ops mctx_ops = { + .create_context = mctx_create_context, + .get_context = mctx_get_context, + .set_context = mctx_set_context, + .swap_context = mctx_swap_context, +}; +#endif + +static struct pcl_ctx_ops *ops_arr[] = { +#ifdef MCONTEXT + /*[PCL_C_MC] = */&mctx_ops, +#endif +#if WANT_UCONTEXT + /*[PCL_C_UC] = */&ctx_ops, +#endif +}; + +int ctx_init(enum CoBackend b, struct pcl_ctx_ops **ops) +{ + if (b >= PCL_C_MAX) + return -1; + assert(ops_arr[b]); + *ops = ops_arr[b]; + return 0; +} + +int ctx_sizeof(enum CoBackend b) +{ + switch (b) { +#ifdef MCONTEXT + case PCL_C_MC: + return sizeof(m_ucontext_t); +#endif +#if WANT_UCONTEXT + case PCL_C_UC: + return sizeof(ucontext_t); +#endif + default: + return -1; + } +} diff --git a/src/base/lib/libpcl/pcl_ctx.h b/src/base/lib/libpcl/pcl_ctx.h new file mode 100644 index 0000000..0494762 --- /dev/null +++ b/src/base/lib/libpcl/pcl_ctx.h @@ -0,0 +1,23 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef PCL_CTX_H +#define PCL_CTX_H + +int ctx_init(enum CoBackend b, struct pcl_ctx_ops **ops); +int ctx_sizeof(enum CoBackend b); + +#endif diff --git a/src/base/lib/libpcl/pcl_private.h b/src/base/lib/libpcl/pcl_private.h new file mode 100644 index 0000000..21f0ba1 --- /dev/null +++ b/src/base/lib/libpcl/pcl_private.h @@ -0,0 +1,86 @@ +/* This code is taken from libpcl library. + * Rip-off done by stsp for dosemu2 project. + * Original copyrights below. */ + +/* + * PCL by Davide Libenzi (Portable Coroutine Library) + * Copyright (C) 2003..2010 Davide Libenzi + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Davide Libenzi + * + */ + +#if !defined(PCL_PRIVATE_H) +#define PCL_PRIVATE_H + +#include +#include + +/* + * The following value must be power of two (N^2). + */ +#define _CO_STK_ALIGN 256 +#define CO_STK_ALIGN(x) (((x) + _CO_STK_ALIGN - 1) & ~(_CO_STK_ALIGN - 1)) +#define CO_STK_COROSIZE(x) CO_STK_ALIGN((x) + sizeof(coroutine)) +#define CO_MIN_SIZE (4 * 1024) + +struct s_co_ctx; +struct pcl_ctx_ops { + int (*create_context)(struct s_co_ctx *ctx, void (*func)(void*), + void *arg, char *stkbase, long stksiz); + int (*get_context)(struct s_co_ctx *ctx); + int (*set_context)(struct s_co_ctx *ctx); + int (*swap_context)(struct s_co_ctx *ctx1, void *ctx2); +}; + +typedef struct s_co_ctx { + void *cc; + struct pcl_ctx_ops *ops; +} co_ctx_t; + +typedef struct s_co_base { + co_ctx_t ctx; + struct s_co_base *caller; + struct s_co_base *restarget; + struct s_cothread_ctx *ctx_main; + void *tmp; + int stack_size; + char *stack; + unsigned int exited:1; +} co_base; + +typedef +#ifdef __cplusplus +struct s_coroutine : public co_base { +#else +struct s_coroutine { + co_base; +#endif + int alloc; + void (*func)(void *); + void *data; + char stk[0]; +} coroutine; + +typedef struct s_cothread_ctx { + co_base co_main; + co_base *co_curr; + int ctx_sizeof; + char stk0[0]; +} cothread_ctx; + +#endif diff --git a/src/base/lib/mapping/Makefile b/src/base/lib/mapping/Makefile new file mode 100644 index 0000000..6bb2f7d --- /dev/null +++ b/src/base/lib/mapping/Makefile @@ -0,0 +1,30 @@ +# +# (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". +# +# for details see file COPYING in the DOSEMU distribution +# + +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf + +# This is the Makefile for the mapping-subdirectory of DOSEMU. +# Please send comments and bug-reports to Hans Lermen + + +#The C files, include files and dependancies here. +CFILES = mapping.c mapfile.c mapashm.c +DEPENDS = $(CFILES:.c=.d) +HFILES = + + +# Insert all source- and header-files here. +ALL = $(CFILES) $(HFILES) + +# All object-files are included here. +OBJS = $(CFILES:.c=.o) + +all: lib + +install: + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/lib/mapping/mapashm.c b/src/base/lib/mapping/mapashm.c new file mode 100644 index 0000000..aeb361b --- /dev/null +++ b/src/base/lib/mapping/mapashm.c @@ -0,0 +1,141 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: memory mapping library, anon-SHM backend. + * + * Authors: Stas Sergeev, Bart Oldeman. + */ + +#if defined(__linux__) || defined(__APPLE__) + +#include +#include + +#ifdef __APPLE__ +#include +#include +#endif + +#include "dosemu_debug.h" +#include "mapping.h" + +/* ------------------------------------------------------------ */ + +static void *alias_mapping_ashm(int cap, void *target, size_t mapsize, int protect, void *source) +{ + int flags; +#ifdef __APPLE__ + mach_vm_address_t targetaddr; + vm_prot_t cur, max; + + if (target != (void *)-1) { + flags = VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE; + targetaddr = (vm_address_t)target; + } else { + flags = VM_FLAGS_ANYWHERE; + targetaddr = (vm_address_t)NULL; + } + if (mach_vm_remap(mach_task_self(), &targetaddr, mapsize, 0, flags, + mach_task_self(), (mach_vm_address_t)source, FALSE, &cur, &max, VM_INHERIT_NONE) + != KERN_SUCCESS) + return MAP_FAILED; + + target = (void *)targetaddr; +#else + flags = MREMAP_MAYMOVE; + if (target != (void *)-1) + flags |= MREMAP_FIXED; + else + target = NULL; + target = mremap(source, 0, mapsize, flags, target); + if (target == MAP_FAILED) return MAP_FAILED; +#endif + mprotect(target, mapsize, protect); + return target; +} + +static int open_mapping_ashm(int cap) +{ + if (cap) Q_printf("MAPPING: open, cap=%s\n", + decode_mapping_cap(cap)); + return 1; +} + +static void close_mapping_ashm(int cap) +{ + Q_printf("MAPPING: close, cap=%s\n", decode_mapping_cap(cap)); +} + +static void *alloc_mapping_ashm(int cap, size_t mapsize, void *target) +{ + int fixed = 0; + + Q__printf("MAPPING: alloc, cap=%s, mapsize=%zx\n", cap, mapsize); + if (target != (void *)-1) + fixed = MAP_FIXED; + else + target = NULL; + return mmap(target, mapsize, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS | fixed, -1, 0); +} + +static void free_mapping_ashm(int cap, void *addr, size_t mapsize) +/* NOTE: addr needs to be the same as what was supplied by alloc_mapping_ashm */ +{ + Q__printf("MAPPING: free, cap=%s, addr=%p, mapsize=%zx\n", + cap, addr, mapsize); + munmap(addr, mapsize); +} + +static void *resize_mapping_ashm(int cap, void *addr, size_t oldsize, size_t newsize) +{ + void *ret = addr; + Q__printf("MAPPING: resize, cap=%s, addr=%p, oldsize=%zx, newsize=%zx\n", + cap, addr, oldsize, newsize); + + if (newsize < oldsize) { +#ifdef HAVE_MREMAP + ret = mremap(addr, oldsize, newsize, 0); + assert(ret == addr); +#else + /* ensure page-aligned */ + assert(!(oldsize & (PAGE_SIZE - 1))); + assert(!(newsize & (PAGE_SIZE - 1))); + if (munmap((unsigned char *)addr + newsize, oldsize - newsize) == -1) + ret = MAP_FAILED; +#endif + } else if (newsize > oldsize) { + ret = alloc_mapping_ashm(cap, newsize, NULL); + if (ret != MAP_FAILED) { + memcpy(ret, addr, oldsize); + free_mapping_ashm(cap, addr, oldsize); + } + } + return ret; +} + +struct mappingdrivers mappingdriver_ashm = { + "mapashm", + "anonymous non-expandable shared memory mapping", + open_mapping_ashm, + close_mapping_ashm, + alloc_mapping_ashm, + free_mapping_ashm, + resize_mapping_ashm, + alias_mapping_ashm +}; +#endif diff --git a/src/base/lib/mapping/mapfile.c b/src/base/lib/mapping/mapfile.c new file mode 100644 index 0000000..8180726 --- /dev/null +++ b/src/base/lib/mapping/mapfile.c @@ -0,0 +1,345 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: memory mapping library, posix SHM and file backends. + * + * Authors: Stas Sergeev, Bart Oldeman. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dosemu_debug.h" +#include "mapping.h" + +/* ------------------------------------------------------------ */ + +/* There are 255 EMS handles, 65 XMS handles, + 2 for lowmem + vgaemu + So 512 is definitely sufficient */ +#define MAX_FILE_MAPPINGS 512 +static struct file_mapping { + unsigned char *addr; /* pointer to allocated shared memory */ + size_t size; + size_t fsize; + int fd; + int prot; +} file_mappings[MAX_FILE_MAPPINGS]; + +typedef int (*open_cb_t)(void); +static open_cb_t open_cb; + +static struct file_mapping *find_file_mapping(unsigned char *target) +{ + int i; + struct file_mapping *p = file_mappings; + + for (i = 0; i < MAX_FILE_MAPPINGS; i++, p++) + if (p->size && target >= p->addr && target < &p->addr[p->size]) + break; + assert(i < MAX_FILE_MAPPINGS); + return p; +} + +/* Do not create mapping nodes for aliases, so can't alias the alias. + * This is the simplest design. */ +static void *alias_mapping_file(int cap, void *target, size_t mapsize, int protect, void *source) +{ + int fixed = 0; + struct file_mapping *p = find_file_mapping(source); + off_t offs = (unsigned char *)source - p->addr; + void *addr; + + if (offs + mapsize > p->size) { + Q_printf("MAPPING: alias_map to address outside of temp file\n"); + errno = EINVAL; + return MAP_FAILED; + } + if (target != (void *)-1) + fixed = MAP_FIXED; + else + target = NULL; + /* /dev/shm may be mounted noexec, and then mounting PROT_EXEC fails. + However mprotect may work around this (maybe not in future kernels) + alloc_mappings can just be rw though. + */ + addr = mmap(target, mapsize, protect, MAP_SHARED | fixed, p->fd, offs); + if (addr == MAP_FAILED) { + addr = mmap(target, mapsize, protect & ~PROT_EXEC, MAP_SHARED | fixed, + p->fd, offs); + if (addr != MAP_FAILED) { + int ret = mprotect(addr, mapsize, protect); + if (ret == -1) { + perror("mprotect()"); + error("shared memory mprotect failed, exiting\n"); + exit(2); + } + } else + perror("mmap()"); + } +#if 1 + Q_printf("MAPPING: alias_map, fileoffs %llx to %p size %zx, result %p\n", + (long long)offs, target, mapsize, addr); +#endif + return addr; +} + +static int do_open_file(void) +{ + char tmp[] = "/tmp/dosemu2_mapfile_XXXXXX"; + int fd = mkstemp(tmp); + if (fd == -1) { + perror("mkstemp()"); + return -1; + } + unlink(tmp); + return fd; +} + +static int open_mapping_file(int cap) +{ + if (cap == MAPPING_PROBE) { + open_cb_t o = do_open_file; + int fd = o(); + if (fd == -1) + return 0; + close(fd); + open_cb = o; + } + return 1; +} + +#ifdef HAVE_SHM_OPEN +static int do_open_pshm(void) +{ + char *name; + int ret, fd; + + ret = asprintf(&name, "/dosemu_%d", getpid()); + assert(ret != -1); + /* FD_CLOEXEC is set by default */ + fd = shm_open(name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd == -1) { + free(name); + return -1; + } + shm_unlink(name); + free(name); + return fd; +} + +static int open_mapping_pshm(int cap) +{ + if (cap == MAPPING_PROBE) { + open_cb_t o = do_open_pshm; + int fd = o(); + if (fd == -1) + return 0; + close(fd); + open_cb = o; + } + return 1; +} +#endif + +#ifdef HAVE_MEMFD_CREATE +static int do_open_mshm(void) +{ + char *name; + int ret, fd; + + ret = asprintf(&name, "dosemu_%d", getpid()); + assert(ret != -1); + + fd = memfd_create(name, MFD_CLOEXEC); + free(name); + return fd; +} + +static int open_mapping_mshm(int cap) +{ + if (cap == MAPPING_PROBE) { + open_cb_t o = do_open_mshm; + int fd = o(); + if (fd == -1) + return 0; + close(fd); + open_cb = o; + } + return 1; +} +#endif + +static void do_free_mapping(struct file_mapping *p) +{ + munmap(p->addr, p->size); + close(p->fd); + p->size = 0; +} + +static void close_mapping_file(int cap) +{ + Q_printf("MAPPING: close, cap=%s\n", decode_mapping_cap(cap)); + if (cap == MAPPING_ALL) { + int i; + struct file_mapping *p; + for (i = 0, p = file_mappings; i < MAX_FILE_MAPPINGS; i++, p++) { + if (p->size) + do_free_mapping(p); + } + } +} + +static void *alloc_mapping_file(int cap, size_t mapsize, void *target) +{ + int i, fixed = 0, prot = PROT_READ | PROT_WRITE; + struct file_mapping *p; + int fd, rc; + + Q__printf("MAPPING: alloc, cap=%s, mapsize=%zx\n", cap, mapsize); + for (i = 0, p = file_mappings; i < MAX_FILE_MAPPINGS; i++, p++) + if (p->size == 0) + break; + assert(i < MAX_FILE_MAPPINGS); + fd = open_cb(); + if (fd < 0) + return MAP_FAILED; + rc = ftruncate(fd, mapsize); + assert(rc != -1); + + if (target != (void *)-1) + fixed = MAP_FIXED; + else + target = NULL; + target = mmap(target, mapsize, prot, MAP_SHARED | fixed, fd, 0); + if (target == MAP_FAILED) + return MAP_FAILED; +#if HAVE_DECL_MADV_POPULATE_WRITE + { + int err = madvise(target, mapsize, MADV_POPULATE_WRITE); + if (err) + perror("madvise()"); + } +#endif + p->addr = target; + p->size = mapsize; + p->fsize = mapsize; + p->fd = fd; + p->prot = prot; + return target; +} + +static void free_mapping_file(int cap, void *addr, size_t mapsize) +/* NOTE: addr needs to be the same as what was supplied by alloc_mapping_file */ +{ + struct file_mapping *p; + + Q__printf("MAPPING: free, cap=%s, addr=%p, mapsize=%zx\n", + cap, addr, mapsize); + p = find_file_mapping(addr); + do_free_mapping(p); +} + +/* + * NOTE: DPMI relies on realloc_mapping() _not_ changing the address ('addr'), + * when shrinking the memory region. + */ +/* We resize only from the beginning of the mapping. + * This is the simplest design. */ +static void *resize_mapping_file(int cap, void *addr, size_t oldsize, size_t newsize) +{ + Q__printf("MAPPING: realloc, cap=%s, addr=%p, oldsize=%zx, newsize=%zx\n", + cap, addr, oldsize, newsize); + if (cap & (MAPPING_EMS | MAPPING_DPMI)) { + struct file_mapping *p = find_file_mapping(addr); + int size = p->size; + + if (!size || size != oldsize || addr != p->addr) return (void *)-1; + if (size == newsize) return addr; + if (newsize < size) { + p->size = newsize; +#ifdef HAVE_MREMAP + p->addr = mremap(addr, oldsize, newsize, 0); + assert(p->addr == addr); +#else + /* ensure page-aligned */ + assert(!(oldsize & (PAGE_SIZE - 1))); + assert(!(newsize & (PAGE_SIZE - 1))); + munmap(addr + newsize, oldsize - newsize); +#endif + } else { + if (newsize > p->fsize) { + int rc = ftruncate(p->fd, newsize); + assert(rc != -1); + p->fsize = newsize; + } + p->size = newsize; +#if HAVE_DECL_MREMAP_MAYMOVE + p->addr = mremap(addr, oldsize, newsize, MREMAP_MAYMOVE); +#else + p->addr = mmap(NULL, newsize, p->prot, MAP_SHARED, p->fd, 0); + munmap(addr, oldsize); +#endif + } + return p->addr; + } + return (void *)-1; +} + +#ifdef HAVE_SHM_OPEN +struct mappingdrivers mappingdriver_shm = { + "mapshm", + "Posix SHM mapping", + open_mapping_pshm, + close_mapping_file, + alloc_mapping_file, + free_mapping_file, + resize_mapping_file, + alias_mapping_file +}; +#endif + +#ifdef HAVE_MEMFD_CREATE +struct mappingdrivers mappingdriver_mshm = { + "mapmshm", + "memfd mapping", + open_mapping_mshm, + close_mapping_file, + alloc_mapping_file, + free_mapping_file, + resize_mapping_file, + alias_mapping_file +}; +#endif + +struct mappingdrivers mappingdriver_file = { + "mapfile", + "temp file mapping", + open_mapping_file, + close_mapping_file, + alloc_mapping_file, + free_mapping_file, + resize_mapping_file, + alias_mapping_file +}; diff --git a/src/base/lib/mapping/mapping.c b/src/base/lib/mapping/mapping.c new file mode 100644 index 0000000..de0ba97 --- /dev/null +++ b/src/base/lib/mapping/mapping.c @@ -0,0 +1,1039 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * Purpose: memory mapping library. + * + * Authors: Stas Sergeev, Bart Oldeman. + * Initially started by Hans Lermen, old copyrights below: + */ +/* file mapping.c + * + * generic mapping driver interface + * (C) Copyright 2000, Hans Lermen, lermen@fgan.de + * + * NOTE: We handle _all_ memory mappings within the mapping drivers, + * mmap-type as well as IPC shm, except for X-mitshm (in X.c), + * which is a special case and doesn't involve DOS space atall. + * + * If you ever need to do mapping within DOSEMU (except X-mitshm), + * _always_ use the interface supplied by the mapping drivers! + * ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + */ + +#include "emu.h" +#include "xms.h" +#include "utilities.h" +#include "dos2linux.h" +#include "kvm.h" +#include "mapping.h" +#include "smalloc.h" +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif + +#ifdef __i386__ +enum { MEM_BASE, KVM_BASE, VM86_BASE, MAX_BASES }; +#else +enum { MEM_BASE, KVM_BASE, MAX_BASES }; +#endif + +struct mem_map_struct { + off_t src; + void *bkp_base; + void *base[MAX_BASES]; + dosaddr_t dst; + int len; +}; + +#ifdef __linux__ +#define MAX_KMEM_MAPPINGS 4096 +static int kmem_mappings = 0; +static struct mem_map_struct kmem_map[MAX_KMEM_MAPPINGS]; +#endif + +static int init_done = 0; +unsigned char *mem_base; +uintptr_t mem_base_mask; +static struct { + unsigned char *base; + size_t size; +} mem_bases[MAX_BASES]; +uint8_t *lowmem_base; + +static struct mappingdrivers *mappingdrv[] = { +#ifdef HAVE_MEMFD_CREATE + &mappingdriver_mshm, /* first try memfd mmap */ +#endif +#ifdef __APPLE__ + &mappingdriver_ashm, /* on Mac first try private anon-mmap + mach_vm_remap */ +#endif +#ifdef HAVE_SHM_OPEN + &mappingdriver_shm, /* then shm_open which is usually broken */ +#endif +#ifdef __linux__ + &mappingdriver_ashm, /* then anon-shared-mmap */ +#endif + &mappingdriver_file, /* and then a temp file */ +}; + +static struct mappingdrivers *mappingdriver; + +#define ALIAS_SIZE (LOWMEM_SIZE + HMASIZE) +static unsigned char *lowmem_aliasmap[ALIAS_SIZE/PAGE_SIZE]; +struct hardware_ram; +static dosaddr_t do_get_hardware_ram(unsigned addr, uint32_t size, + struct hardware_ram **r_hw); +static unsigned do_find_hardware_ram(dosaddr_t va, uint32_t size, + struct hardware_ram **r_hw); +static void hwram_update_aliasmap(struct hardware_ram *hw, unsigned addr, + int size, unsigned char *src); + +static void update_aliasmap(dosaddr_t dosaddr, size_t mapsize, + unsigned char *unixaddr) +{ + unsigned addr2; + struct hardware_ram *hw; + + if (dosaddr >= mem_bases[MEM_BASE].size) + return; + addr2 = do_find_hardware_ram(dosaddr, mapsize, &hw); + if (addr2 == (unsigned)-1) + return; + hwram_update_aliasmap(hw, addr2, mapsize, unixaddr); + invalidate_unprotected_page_cache(dosaddr, mapsize); +} + +void *dosaddr_to_unixaddr(dosaddr_t addr) +{ + if (addr < ALIAS_SIZE) + return lowmem_aliasmap[addr >> PAGE_SHIFT] + (addr & (PAGE_SIZE - 1)); + return MEM_BASE32(addr); +} + +void *physaddr_to_unixaddr(unsigned int addr) +{ + void *hwr = get_hardware_uaddr(addr); + if (hwr != MAP_FAILED) + return hwr; + if (addr < ALIAS_SIZE) + return dosaddr_to_unixaddr(addr); + return MAP_FAILED; +} + +dosaddr_t physaddr_to_dosaddr(unsigned int addr, int len) +{ + dosaddr_t ret = get_hardware_ram(addr, len); + if (ret != (dosaddr_t)-1) + return ret; + if (addr + len <= ALIAS_SIZE) + return addr; + return -1; +} + +#ifdef __linux__ +static int map_find_idx(struct mem_map_struct *map, int max, off_t addr) +{ + int i; + for (i = 0; i < max; i++) { + if (map[i].src == addr) + return i; + } + return -1; +} + +static int map_find(struct mem_map_struct *map, int max, + dosaddr_t addr, int size, int mapped) +{ + int i; + dosaddr_t dst, dst1; + dosaddr_t min = -1; + int idx = -1; + dosaddr_t max_addr = addr + size; + for (i = 0; i < max; i++) { + if (!map[i].dst || !map[i].len || (map[i].dst != -1) != mapped) + continue; + dst = map[i].dst; + dst1 = dst + map[i].len; + if (dst >= addr && dst < max_addr) { + if (min == (dosaddr_t)-1 || dst < min) { + min = dst; + idx = i; + } + } + if (dst1 > addr && dst < max_addr) { + if (min == (dosaddr_t)-1 || dst1 < min) { + min = addr; + idx = i; + } + } + } + return idx; +} +#endif + +static unsigned char *MEM_BASE32x(dosaddr_t a, int base) +{ + if (mem_bases[base].base == MAP_FAILED || a >= mem_bases[base].size) + return MAP_FAILED; + return &mem_bases[base].base[a]; +} + +#ifdef __linux__ +// counts number of kmem mappings, only used for assert +static int kmem_mapped(dosaddr_t addr, int mapsize) +{ + int i, cnt = 0; + + while ((i = map_find(kmem_map, kmem_mappings, addr, mapsize, 1)) != -1) { + cnt++; + } + return cnt; +} + +static void kmem_map_single(int cap, int idx, dosaddr_t targ) +{ + assert(targ != (dosaddr_t)-1); + if (cap & MAPPING_LOWMEM) { + int i; + for(i = 0; i < MAX_BASES; i++) { + void *dst = MEM_BASE32x(targ, i); + if (dst == MAP_FAILED) + continue; + mremap(kmem_map[idx].base[i], kmem_map[idx].len, kmem_map[idx].len, + MREMAP_MAYMOVE | MREMAP_FIXED, dst); + } + } else { + mremap(kmem_map[idx].base[MEM_BASE], kmem_map[idx].len, kmem_map[idx].len, + MREMAP_MAYMOVE | MREMAP_FIXED, MEM_BASE32(targ)); + } + kmem_map[idx].dst = targ; + update_aliasmap(targ, kmem_map[idx].len, kmem_map[idx].bkp_base); +} +#endif + +static int is_kvm_map(int cap) +{ + if (config.cpu_vm != CPUVM_KVM && config.cpu_vm_dpmi != CPUVM_KVM) + return 0; + if (config.cpu_vm_dpmi == CPUVM_KVM) + return 1; + if (cap & MAPPING_INIT_LOWRAM) + return 1; + /* v86 kvm, dpmi native */ + return (!(cap & MAPPING_DPMI)); +} + +void *alias_mapping_ux(int cap, size_t mapsize, int protect, void *source) +{ + void *target = (void *)-1; + return mappingdriver->alias(cap, target, mapsize, protect, source); +} + +int alias_mapping(int cap, dosaddr_t targ, size_t mapsize, int protect, void *source) +{ + int i; + + assert(targ != (dosaddr_t)-1); + Q__printf("MAPPING: alias, cap=%s, targ=%#x, size=%zx, protect=%x, source=%p\n", + cap, targ, mapsize, protect, source); +#ifdef __linux__ + assert(kmem_mapped(targ, mapsize) == 0); +#endif + + for (i = 0; i < MAX_BASES; i++) { + void *target, *addr; + int prot; + target = MEM_BASE32x(targ, i); + if (target == MAP_FAILED) + continue; + /* protections on KVM_BASE go via page tables in the VM, not mprotect */ + prot = i == KVM_BASE ? (PROT_READ|PROT_WRITE|PROT_EXEC) : protect; + addr = mappingdriver->alias(cap, target, mapsize, prot, source); + if (addr == MAP_FAILED) + return -1; + Q__printf("MAPPING: %s alias created at %p\n", cap, addr); + } + update_aliasmap(targ, mapsize, source); + if (is_kvm_map(cap)) + mprotect_kvm(cap, targ, mapsize, protect); + + return 0; +} + +#ifdef __linux__ +static void *mmap_mapping_kmem(int cap, dosaddr_t targ, size_t mapsize, + off_t source) +{ + int i; + void *target; + + Q__printf("MAPPING: map kmem, cap=%s, target=%x, size=%zx, source=%#jx\n", + cap, targ, mapsize, (intmax_t)source); + + i = map_find_idx(kmem_map, kmem_mappings, source); + if (i == -1) { + error("KMEM mapping for %#jx was not allocated!\n", (intmax_t)source); + return MAP_FAILED; + } + if (kmem_map[i].len != mapsize) { + error("KMEM mapping for %#jx allocated for size %#x, but %#zx requested\n", + (intmax_t)source, kmem_map[i].len, mapsize); + return MAP_FAILED; + } + + if (targ == (dosaddr_t)-1) { + target = smalloc_aligned_topdown(&main_pool, NULL, PAGE_SIZE, mapsize); + if (!target) { + error("OOM for mmap_mapping_kmem, %s\n", strerror(errno)); + return MAP_FAILED; + } + targ = DOSADDR_REL(target); + } else { + target = MEM_BASE32(targ); + } + kmem_map_single(cap, i, targ); + + return target; +} +#endif + +static int mapping_is_hole(void *start, size_t size) +{ + uintptr_t beg = (uintptr_t)start; + uintptr_t end = beg + size; + return (mapping_find_hole(beg, end, size) == start); +} + +// KVM api documentation recommends that the lower 21 bits of guest_phys_addr and +// userspace_addr be identical, so align to a huge page boundary (2MB). +// This also helps debugging, since subtracting hex numbers is much easier that way +// for a human being. +void *mmap_mapping_huge_page_aligned(int cap, size_t mapsize, int protect) +{ + size_t edge; + int flags = (cap & MAPPING_INIT_LOWRAM) ? _MAP_32BIT : 0; + unsigned char *addr = mmap(NULL, mapsize + HUGE_PAGE_SIZE, protect, + MAP_PRIVATE | flags | MAP_ANONYMOUS, -1, 0); + if (addr == MAP_FAILED) + return addr; + + /* align up to next 2MB */ + edge = (unsigned char *)HUGE_PAGE_ALIGN((uintptr_t)addr + PAGE_SIZE) - addr; + + /* trim front */ + if (edge > PAGE_SIZE) + munmap(addr, edge - PAGE_SIZE); + + /* create guard page to trap bad things */ + mprotect(&addr[edge - PAGE_SIZE], PAGE_SIZE, PROT_NONE); + + addr += edge; + + /* trim back */ + edge = HUGE_PAGE_SIZE - edge; + if (edge > 0) + munmap(&addr[mapsize], edge); + + if (cap & MAPPING_INIT_LOWRAM) { + mem_bases[MEM_BASE].base = addr; + mem_bases[MEM_BASE].size = mapsize; + if (is_kvm_map(cap)) { + cap = MAPPING_LOWMEM; + mapsize = LOWMEM_SIZE + HMASIZE; + protect = PROT_READ|PROT_WRITE|PROT_EXEC; + void *kvm_base = mmap_mapping_huge_page_aligned(cap, mapsize, protect); + if (kvm_base == MAP_FAILED) + return kvm_base; + mem_bases[KVM_BASE].base = kvm_base; + mem_bases[KVM_BASE].size = mapsize; + } +#ifdef __i386__ + if (config.cpu_vm == CPUVM_VM86) { + mem_bases[VM86_BASE].base = 0; + mem_bases[VM86_BASE].size = ALIAS_SIZE; + } +#endif + } + + return addr; +} + +void *mmap_mapping(int cap, void *target, size_t mapsize, int protect) +{ + void *addr; + int flags; + + assert(target != (void *)-1); + + /* not removing MAP_FIXED when mapping to 0 */ + if ((cap & MAPPING_NULL) && !mapping_is_hole(target, mapsize)) + return MAP_FAILED; + + flags = (cap & MAPPING_NOOVERLAP) ? +#ifdef MAP_FIXED_NOREPLACE + MAP_FIXED_NOREPLACE +#else + 0 +#endif + : MAP_FIXED; + + addr = mmap(target, mapsize, protect, + MAP_PRIVATE | flags | MAP_ANONYMOUS, -1, 0); + + if (addr != MAP_FAILED && addr != target) { +#ifdef MAP_FIXED_NOREPLACE +#ifdef __linux__ + /* under valgrind this flag doesn't work */ + if (kernel_version_code >= KERNEL_VERSION(4, 17, 0)) + error_once0("MAP_FIXED_NOREPLACE doesn't work\n"); +#endif +#endif + munmap(addr, mapsize); + return MAP_FAILED; + } + return addr; +} + +/* Restore mapping previously broken by direct mmap() call. */ +int restore_mapping(int cap, dosaddr_t targ, size_t mapsize) +{ + void *addr; + void *target; + assert((cap & MAPPING_DPMI) && (targ != (dosaddr_t)-1)); + target = MEM_BASE32(targ); + addr = mmap_mapping(cap, target, mapsize, PROT_READ | PROT_WRITE); + if (is_kvm_map(cap)) + mprotect_kvm(cap, targ, mapsize, PROT_READ | PROT_WRITE); + return (addr == target ? 0 : -1); +} + +int mprotect_mapping(int cap, dosaddr_t targ, size_t mapsize, int protect) +{ + int i, ret = -1; + + Q__printf("MAPPING: mprotect, cap=%s, targ=%x, size=%zx, protect=%x\n", + cap, targ, mapsize, protect); + invalidate_unprotected_page_cache(targ, mapsize); + if (is_kvm_map(cap)) + mprotect_kvm(cap, targ, mapsize, protect); + if (!(cap & MAPPING_LOWMEM)) { + ret = mprotect(MEM_BASE32(targ), mapsize, protect); + if (ret) + error("mprotect() failed: %s\n", strerror(errno)); + return ret; + } + for (i = 0; i < MAX_BASES; i++) { + void *addr = MEM_BASE32x(targ, i); + /* protections on KVM_BASE go via page tables in the VM, not mprotect */ + if (addr == MAP_FAILED || i == KVM_BASE) + continue; + assert(i == MEM_BASE || targ + mapsize <= ALIAS_SIZE); + Q__printf("MAPPING: mprotect, cap=%s, addr=%p, size=%zx, protect=%x\n", + cap, addr, mapsize, protect); + ret = mprotect(addr, mapsize, protect); + if (ret) { + error("mprotect() failed: %s\n", strerror(errno)); + return ret; + } + } + return ret; +} + +/* + * This gets called on DOSEMU startup to determine the kind of mapping + * and setup the appropriate function pointers + */ +void mapping_init(void) +{ + int i; + int numdrivers = sizeof(mappingdrv) / sizeof(mappingdrv[0]); + + assert(!init_done); + init_done++; + + if (config.mappingdriver && strcmp(config.mappingdriver, "auto")) { + /* first try the mapping driver the user wants */ + for (i=0; i < numdrivers; i++) { + if (!strcmp(mappingdrv[i]->key, config.mappingdriver)) + break; + } + if (i >= numdrivers) { + error("Wrong mapping driver specified: %s\n", config.mappingdriver); + leavedos(2); + return; + } + if (!mappingdrv[i]->open(MAPPING_PROBE)) { + error("Failed to initialize mapping %s\n", config.mappingdriver); + leavedos(2); + return; + } + mappingdriver = mappingdrv[i]; + } else { + for (i = 0; i < numdrivers; i++) { + if (mappingdrv[i] && (*mappingdrv[i]->open)(MAPPING_PROBE)) { + mappingdriver = mappingdrv[i]; + Q_printf("MAPPING: using the %s driver\n", mappingdriver->name); + break; + } + } + if (i >= numdrivers) { + error("MAPPING: cannot allocate an appropriate mapping driver\n"); + leavedos(2); + return; + } + } + + for (i = 0; i < MAX_BASES; i++) { + mem_bases[i].base = MAP_FAILED; + mem_bases[i].size = 0; + } +} + +/* this gets called on DOSEMU termination cleanup all mapping stuff */ +void mapping_close(void) +{ + if (init_done && mappingdriver->close) close_mapping(MAPPING_ALL); +} + +static char dbuf[256]; +char *decode_mapping_cap(int cap) +{ + char *p = dbuf; + p[0] = 0; + if (!cap) return dbuf; + if ((cap & MAPPING_ALL) == MAPPING_ALL) { + p += sprintf(p, " ALL"); + } + else { + if (cap & MAPPING_OTHER) p += sprintf(p, " OTHER"); + if (cap & MAPPING_EMS) p += sprintf(p, " EMS"); + if (cap & MAPPING_DPMI) p += sprintf(p, " DPMI"); + if (cap & MAPPING_VGAEMU) p += sprintf(p, " VGAEMU"); + if (cap & MAPPING_VIDEO) p += sprintf(p, " VIDEO"); + if (cap & MAPPING_VC) p += sprintf(p, " VC"); + if (cap & MAPPING_HGC) p += sprintf(p, " HGC"); + if (cap & MAPPING_HMA) p += sprintf(p, " HMA"); + if (cap & MAPPING_SHARED) p += sprintf(p, " SHARED"); + if (cap & MAPPING_INIT_HWRAM) p += sprintf(p, " INIT_HWRAM"); + if (cap & MAPPING_INIT_LOWRAM) p += sprintf(p, " INIT_LOWRAM"); + if (cap & MAPPING_EXTMEM) p += sprintf(p, " EXTMEM"); + if (cap & MAPPING_KVM) p += sprintf(p, " KVM"); + } + if (cap & MAPPING_KMEM) p += sprintf(p, " KMEM"); + if (cap & MAPPING_LOWMEM) p += sprintf(p, " LOWMEM"); + if (cap & MAPPING_SCRATCH) p += sprintf(p, " SCRATCH"); + if (cap & MAPPING_SINGLE) p += sprintf(p, " SINGLE"); + if (cap & MAPPING_NULL) p += sprintf(p, " NULL"); + return dbuf; +} + +int open_mapping(int cap) +{ + return mappingdriver->open(cap); +} + +void close_mapping(int cap) +{ + if (mappingdriver->close) mappingdriver->close(cap); +} + +#ifdef __linux__ +static void *alloc_mapping_kmem(int cap, size_t mapsize, off_t source) +{ + void *addr, *addr2; + + Q_printf("MAPPING: alloc kmem, source=%#jx size=%#zx\n", + (intmax_t)source, mapsize); + if (source == -1) { + error("KMEM mapping without source\n"); + leavedos(64); + } + if (map_find_idx(kmem_map, kmem_mappings, source) != -1) { + error("KMEM mapping for %#jx allocated twice!\n", (intmax_t)source); + return MAP_FAILED; + } + open_kmem(); + if (cap & MAPPING_LOWMEM) { + int i; + for (i = 0; i < MAX_BASES; i++) { + addr = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, + mem_fd, source); + if (addr == MAP_FAILED) { + close_kmem(); + return MAP_FAILED; + } + kmem_map[kmem_mappings].base[i] = addr; + } + } else { + addr = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, + mem_fd, source); + if (addr == MAP_FAILED) { + close_kmem(); + return MAP_FAILED; + } + kmem_map[kmem_mappings].base[MEM_BASE] = addr; + } + addr2 = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, + mem_fd, source); + close_kmem(); + if (addr2 == MAP_FAILED) + return MAP_FAILED; + + kmem_map[kmem_mappings].src = source; + kmem_map[kmem_mappings].bkp_base = addr2; + kmem_map[kmem_mappings].dst = -1; + kmem_map[kmem_mappings].len = mapsize; + kmem_mappings++; + Q_printf("MAPPING: region allocated at %p\n", addr2); + return addr2; +} +#endif + +static void *do_alloc_mapping(int cap, size_t mapsize, void *addr) +{ + addr = mappingdriver->alloc(cap, mapsize, addr); + if (addr == MAP_FAILED) { + error("failed to alloc %zx\n", mapsize); + leavedos(2); + return NULL; + } + mprotect(addr, mapsize, PROT_READ | PROT_WRITE); + + if (cap & MAPPING_INIT_LOWRAM) { + Q__printf("MAPPING: LOWRAM_INIT, cap=%s, base=%p\n", cap, addr); + lowmem_base = addr; + } + Q__printf("MAPPING: %s allocated at %p\n", cap, addr); + return addr; +} + +void *alloc_mapping(int cap, size_t mapsize) +{ + Q__printf("MAPPING: alloc, cap=%s size=%#zx\n", cap, mapsize); + return do_alloc_mapping(cap, mapsize, (void *)-1); +} + +void *alloc_mapping_huge_page_aligned(int cap, size_t mapsize) +{ + void *addr; + Q__printf("MAPPING: alloc_huge_page_aligned, cap=%s size=%#zx\n", cap, mapsize); + addr = mmap_mapping_huge_page_aligned(cap, mapsize, PROT_NONE); + return addr == MAP_FAILED ? MAP_FAILED : do_alloc_mapping(cap, mapsize, addr); +} + +void free_mapping(int cap, void *addr, size_t mapsize) +{ + if (cap & MAPPING_KMEM) { + return; + } + mprotect(addr, mapsize, PROT_READ | PROT_WRITE); + mappingdriver->free(cap, addr, mapsize); +} + +void *realloc_mapping(int cap, void *addr, size_t oldsize, size_t newsize) +{ + if (!addr) { + if (oldsize) // no-no, realloc of the lowmem is not good too + dosemu_error("realloc_mapping() called with addr=NULL, oldsize=%#zx\n", oldsize); + Q_printf("MAPPING: realloc from NULL changed to malloc\n"); + return alloc_mapping(cap, newsize); + } + if (!oldsize) + dosemu_error("realloc_mapping() addr=%p, oldsize=0\n", addr); + + return mappingdriver->resize(cap, addr, oldsize, newsize); +} + +static void populate_aliasmap(unsigned char **map, unsigned char *addr, + int size) +{ + int i; + + for (i = 0; i < size >> PAGE_SHIFT; i++) + map[i] = addr ? addr + (i << PAGE_SHIFT) : NULL; +} + +static unsigned char **alloc_aliasmap(unsigned char *addr, int size) +{ + unsigned char **ret = malloc((size >> PAGE_SHIFT) * sizeof(*ret)); + populate_aliasmap(ret, addr, size); + return ret; +} + +struct hardware_ram { + size_t base; + dosaddr_t default_vbase; + dosaddr_t vbase; + size_t size; + int type; + unsigned char **aliasmap; + struct hardware_ram *next; +}; + +static struct hardware_ram *hardware_ram; + +static int do_map_hwram(struct hardware_ram *hw) +{ +#ifdef __linux__ + unsigned char *p; + int cap = MAPPING_KMEM; + if (hw->default_vbase != (dosaddr_t)-1) + cap |= MAPPING_LOWMEM; + else if (!config.dpmi) + return 0; + p = mmap_mapping_kmem(cap, hw->default_vbase, hw->size, hw->base); + if (p == MAP_FAILED) { + error("mmap error in map_hardware_ram %s\n", strerror (errno)); + return -1; + } + hw->vbase = DOSADDR_REL(p); + g_printf("mapped hardware ram at 0x%08zx .. 0x%08zx at %#x\n", + hw->base, hw->base+hw->size-1, hw->vbase); + return 0; +#else + return -1; +#endif +} + +/* + * DANG_BEGIN_FUNCTION init_hardware_ram + * + * description: + * Initialize the hardware direct-mapped pages + * + * DANG_END_FUNCTION + */ +void init_hardware_ram(void) +{ + struct hardware_ram *hw; + + for (hw = hardware_ram; hw != NULL; hw = hw->next) { +#ifdef __linux__ + unsigned char *uaddr; + int cap = MAPPING_KMEM; +#endif + if (hw->vbase != (dosaddr_t)-1) /* virtual hardware ram mapped later */ + continue; +#ifdef __linux__ + if (hw->default_vbase != (dosaddr_t)-1) + cap |= MAPPING_LOWMEM; + uaddr = alloc_mapping_kmem(cap, hw->size, hw->base); + if (uaddr == MAP_FAILED) { + error("failed to map KMEM at %lx, size %zx: %s\n", hw->base, hw->size, + strerror(errno)); + continue; + } + populate_aliasmap(hw->aliasmap, uaddr, hw->size); +#endif + if (do_map_hwram(hw) == -1) + return; + } +} + +static int do_register_hwram(int type, unsigned base, unsigned size, + void *uaddr, dosaddr_t va) +{ + struct hardware_ram *hw; + + c_printf("Registering HWRAM, type=%c base=%#x size=%#x\n", type, base, size); + hw = malloc(sizeof(*hw)); + hw->base = base; + if (base < LOWMEM_SIZE) + hw->default_vbase = hw->base; + else + hw->default_vbase = -1; + hw->vbase = va; + hw->size = size; + hw->type = type; + if (base + size > ALIAS_SIZE) + hw->aliasmap = alloc_aliasmap(uaddr, size); + else { + /* for kmem, uaddr determined later. for !kmem alias on lowmem_base */ + assert(!uaddr); + hw->aliasmap = &lowmem_aliasmap[base >> PAGE_SHIFT]; + } + hw->next = hardware_ram; + hardware_ram = hw; + if (!uaddr && (base >= LOWMEM_SIZE || type == 'h')) + memcheck_reserve(type, base, size); + return 1; +} + +int register_hardware_ram(int type, unsigned base, unsigned int size) +{ + if (!can_do_root_stuff) { + dosemu_error("can't use hardware ram in low feature (non-suid root) DOSEMU\n"); + return 0; + } + return do_register_hwram(type, base, size, NULL, -1); +} + +void register_hardware_ram_virtual2(int type, unsigned base, unsigned int size, + void *uaddr, dosaddr_t va) +{ + void *uaddr2 = base < ALIAS_SIZE ? NULL : MEM_BASE32(va); + do_register_hwram(type, base, size, uaddr2, va); + if (config.cpu_vm_dpmi == CPUVM_KVM || + (config.cpu_vm == CPUVM_KVM && base + size <= LOWMEM_SIZE + HMASIZE)) { + int prot = PROT_READ | PROT_WRITE | PROT_EXEC; + int cap = MAPPING_INIT_LOWRAM; + if (type == 'L') + cap |= MAPPING_LOWMEM; + mmap_kvm(cap, base, size, uaddr, va, prot); + } +} + +void register_hardware_ram_virtual(int type, unsigned base, unsigned int size, + dosaddr_t va) +{ + void *uaddr = base < mem_bases[KVM_BASE].size ? + MEM_BASE32x(base, KVM_BASE) : MEM_BASE32(va); + register_hardware_ram_virtual2(type, base, size, uaddr, va); +} + +int unregister_hardware_ram_virtual(dosaddr_t base) +{ + struct hardware_ram *hw, *phw; + + for (phw = NULL, hw = hardware_ram; hw != NULL; phw = hw, hw = hw->next) { + if (hw->base == base) { + if (phw) + phw->next = hw->next; + else + hardware_ram = hw->next; + if (hw->base + hw->size > ALIAS_SIZE) + free(hw->aliasmap); + free(hw); + return 0; + } + } + return -1; +} + +/* given physical address addr, gives the corresponding vbase or -1 */ +static dosaddr_t do_get_hardware_ram(unsigned addr, uint32_t size, + struct hardware_ram **r_hw) +{ + struct hardware_ram *hw; + + for (hw = hardware_ram; hw != NULL; hw = hw->next) { + if (hw->vbase != -1 && + hw->base <= addr && addr + size <= hw->base + hw->size) { + if (r_hw) + *r_hw = hw; + return hw->vbase + addr - hw->base; + } + } + return -1; +} + +static unsigned do_find_hardware_ram(dosaddr_t va, uint32_t size, + struct hardware_ram **r_hw) +{ + struct hardware_ram *hw; + + for (hw = hardware_ram; hw != NULL; hw = hw->next) { + if (hw->vbase == -1) + continue; + if (hw->vbase <= va && va + size <= hw->vbase + hw->size) { + if (r_hw) + *r_hw = hw; + return hw->base + va - hw->vbase; + } + } + return -1; +} + +dosaddr_t get_hardware_ram(unsigned addr, uint32_t size) +{ + return do_get_hardware_ram(addr, size, NULL); +} + +static void hwram_update_aliasmap(struct hardware_ram *hw, unsigned addr, + int size, unsigned char *src) +{ + int off = addr - hw->base; + assert(!(off & (PAGE_SIZE - 1))); // page-aligned + assert(!(size & (PAGE_SIZE - 1))); // page-aligned + // lowmem needs permanent aliasing + assert(!(src == NULL && (hw->base + hw->size <= ALIAS_SIZE))); + populate_aliasmap(&hw->aliasmap[off >> PAGE_SHIFT], src, size); +} + +void *get_hardware_uaddr(unsigned addr) +{ + struct hardware_ram *hw; + + for (hw = hardware_ram; hw != NULL; hw = hw->next) { + if (hw->vbase != -1 && + hw->base <= addr && addr < hw->base + hw->size) { + int off = addr - hw->base; + return hw->aliasmap[off >> PAGE_SHIFT] + (off & (PAGE_SIZE - 1)); + } + } + return MAP_FAILED; +} + +void list_hardware_ram(void (*print)(const char *, ...)) +{ + struct hardware_ram *hw; + + (*print)("hardware_ram: %s\n", hardware_ram ? "" : "no"); + if (!hardware_ram) return; + (*print)("hardware_pages:\n"); + for (hw = hardware_ram; hw != NULL; hw = hw->next) + (*print)("%08x-%08x\n", hw->base, hw->base + hw->size - 1); +} + +#ifdef __linux__ +/* why count ??? */ +/* Because you do not want to open it more than once! */ +static u_char kmem_open_count = 0; + +void +open_kmem (void) +{ + PRIV_SAVE_AREA + /* as I understad it, /dev/kmem is the kernel's view of memory, + * and /dev/mem is the identity-mapped (i.e. physical addressed) + * memory. Currently under Linux, both are the same. + */ + + kmem_open_count++; + + if (mem_fd != -1) + return; + enter_priv_on(); + mem_fd = open("/dev/mem", O_RDWR | O_CLOEXEC); + leave_priv_setting(); + if (mem_fd < 0) + { + error("can't open /dev/mem: errno=%d, %s \n", + errno, strerror (errno)); + leavedos (0); + return; + } + g_printf ("Kmem opened successfully\n"); +} + +void +close_kmem (void) +{ + + if (kmem_open_count) + { + kmem_open_count--; + if (kmem_open_count) + return; + close (mem_fd); + mem_fd = -1; + v_printf ("Kmem closed successfully\n"); + } +} +#endif + +void *mapping_find_hole(unsigned long start, unsigned long stop, + unsigned long size) +{ + FILE *fp; + unsigned long beg, end, pend; + int fd, ret; + + /* find out whether the address request is available */ + if ((fd = dup(dosemu_proc_self_maps_fd)) == -1) { + error("dup() failed\n"); + return MAP_FAILED; + } + if ((fp = fdopen(fd, "r")) == NULL) { + error("can't open /proc/self/maps\n"); + return MAP_FAILED; + } + fseek(fp, 0, SEEK_SET); + pend = start; + while ((ret = fscanf(fp, "%lx-%lx%*[^\n]", &beg, &end)) == 2) { + if (beg <= start) { + if (end > pend) + pend = end; + continue; + } + if (beg - pend >= size) + break; + if (end + size > stop) { + fclose(fp); + return MAP_FAILED; + } + pend = end; + } + fclose(fp); + if (ret != 2) + return MAP_FAILED; + return (void *)pend; +} + +int mcommit(void *ptr, size_t size) +{ + int err; + dosaddr_t targ = DOSADDR_REL(ptr); + int cap = MAPPING_INIT_LOWRAM; + err = mprotect_mapping(cap, targ, size, PROT_READ | PROT_WRITE); + if (err == -1) + return 0; +#if HAVE_DECL_MADV_POPULATE_WRITE + err = madvise(ptr, size, MADV_POPULATE_WRITE); + if (err) + perror("madvise()"); +#endif + return 1; +} + +int muncommit(void *ptr, size_t size) +{ + dosaddr_t targ = DOSADDR_REL(ptr); + int cap = MAPPING_INIT_LOWRAM; + if (mprotect_mapping(cap, targ, size, PROT_NONE) == -1) + return 0; + return 1; +} + +int alias_mapping_pa(int cap, unsigned addr, size_t mapsize, int protect, + void *source) +{ + void *addr2; + struct hardware_ram *hw; + dosaddr_t va = do_get_hardware_ram(addr, mapsize, &hw); + if (va == (dosaddr_t)-1) + return 0; + assert(addr >= LOWMEM_SIZE + HMASIZE); + addr2 = mappingdriver->alias(cap, MEM_BASE32(va), mapsize, protect, source); + if (addr2 == MAP_FAILED) + return 0; + assert(addr2 == MEM_BASE32(va)); + hwram_update_aliasmap(hw, addr, mapsize, source); + invalidate_unprotected_page_cache(va, mapsize); + if (is_kvm_map(cap)) + mprotect_kvm(cap, va, mapsize, protect); + return 1; +} + +int unalias_mapping_pa(int cap, unsigned addr, size_t mapsize) +{ + struct hardware_ram *hw; + dosaddr_t va = do_get_hardware_ram(addr, mapsize, &hw); + if (va == (dosaddr_t)-1) + return 0; + assert(addr >= LOWMEM_SIZE + HMASIZE); + restore_mapping(cap, va, mapsize); + hwram_update_aliasmap(hw, addr, mapsize, NULL); + invalidate_unprotected_page_cache(va, mapsize); + return 1; +} diff --git a/src/base/lib/mcontext/386-ucontext.h b/src/base/lib/mcontext/386-ucontext.h new file mode 100644 index 0000000..9ec6e65 --- /dev/null +++ b/src/base/lib/mcontext/386-ucontext.h @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 1999 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/i386/include/ucontext.h,v 1.4 1999/10/11 20:33:09 luoqi Exp $ + */ + +struct m_mcontext { + /* + * The first 20 fields must match the definition of + * sigcontext. So that we can support sigcontext + * and ucontext_t at the same time. + */ + int mc_onstack; /* XXX - sigcontext compat. */ + int mc_gs; + int mc_fs; + int mc_es; + int mc_ds; + int mc_edi; + int mc_esi; + int mc_ebp; + int mc_isp; + int mc_ebx; + int mc_edx; + int mc_ecx; + int mc_eax; + int mc_trapno; + int mc_err; + int mc_eip; + int mc_cs; + int mc_eflags; + int mc_esp; /* machine state */ + int mc_ss; + + int mc_fpregs[28]; /* env87 + fpacc87 + u_long */ + int __spare__[17]; +}; diff --git a/src/base/lib/mcontext/COPYRIGHT b/src/base/lib/mcontext/COPYRIGHT new file mode 100644 index 0000000..45fcbc0 --- /dev/null +++ b/src/base/lib/mcontext/COPYRIGHT @@ -0,0 +1,43 @@ + +This software was developed as part of a project at MIT. + +Copyright (c) 2005-2007 Russ Cox, + Massachusetts Institute of Technology + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +=== + +Contains parts of an earlier library that has: + +/* + * The authors of this software are Rob Pike, Sape Mullender, and Russ Cox + * Copyright (c) 2003 by Lucent Technologies. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + diff --git a/src/base/lib/mcontext/Makefile b/src/base/lib/mcontext/Makefile new file mode 100644 index 0000000..6abcd7b --- /dev/null +++ b/src/base/lib/mcontext/Makefile @@ -0,0 +1,9 @@ +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf + +CFILES = context.c +SSFILES = asm.SS + +all: lib + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/lib/mcontext/aarch64-ucontext.h b/src/base/lib/mcontext/aarch64-ucontext.h new file mode 100644 index 0000000..6c30910 --- /dev/null +++ b/src/base/lib/mcontext/aarch64-ucontext.h @@ -0,0 +1,37 @@ +/* Copyright (C) 1998-2022 Free Software Foundation, Inc. + + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* System V/AArch64 ABI compliant context switching support. */ + +/* Context to describe whole processor state. This only describes + the core registers; coprocessor registers get saved elsewhere + (e.g. in uc_regspace, or somewhere unspecified on the stack + during non-RT signal handlers). */ +struct m_mcontext + { + unsigned long long int fault_address; + unsigned long long int regs[31]; + unsigned long long int sp; + unsigned long long int pc; + unsigned long long int pstate; + /* This field contains extension records for additional processor + state such as the FP/SIMD state. It has to match the definition + of the corresponding field in the sigcontext struct, see the + arch/arm64/include/uapi/asm/sigcontext.h linux header for details. */ + unsigned char __reserved[4096] __attribute__ ((__aligned__ (16))); + }; diff --git a/src/base/lib/mcontext/amd64-ucontext.h b/src/base/lib/mcontext/amd64-ucontext.h new file mode 100644 index 0000000..203c89c --- /dev/null +++ b/src/base/lib/mcontext/amd64-ucontext.h @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 1999 Marcel Moolenaar + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/i386/include/ucontext.h,v 1.4 1999/10/11 20:33:09 luoqi Exp $ + */ + +struct m_mcontext { + /* + * The first 20 fields must match the definition of + * sigcontext. So that we can support sigcontext + * and ucontext_t at the same time. + */ + long mc_onstack; /* XXX - sigcontext compat. */ + long mc_rdi; /* machine state (struct trapframe) */ + long mc_rsi; + long mc_rdx; + long mc_rcx; + long mc_r8; + long mc_r9; + long mc_rax; + long mc_rbx; + long mc_rbp; + long mc_r10; + long mc_r11; + long mc_r12; + long mc_r13; + long mc_r14; + long mc_r15; + long mc_trapno; + long mc_addr; + long mc_flags; + long mc_err; + long mc_rip; + long mc_cs; + long mc_rflags; + long mc_rsp; + long mc_ss; + + long mc_len; /* sizeof(mcontext_t) */ +#define _MC_FPFMT_NODEV 0x10000 /* device not present or configured */ +#define _MC_FPFMT_XMM 0x10002 + long mc_fpformat; +#define _MC_FPOWNED_NONE 0x20000 /* FP state not used */ +#define _MC_FPOWNED_FPU 0x20001 /* FP state came from FPU */ +#define _MC_FPOWNED_PCB 0x20002 /* FP state came from PCB */ + long mc_ownedfp; + /* + * See for the internals of mc_fpstate[]. + */ + long mc_fpstate[64]; + long mc_spare[8]; +}; diff --git a/src/base/lib/mcontext/arm-ucontext.h b/src/base/lib/mcontext/arm-ucontext.h new file mode 100644 index 0000000..c801cc9 --- /dev/null +++ b/src/base/lib/mcontext/arm-ucontext.h @@ -0,0 +1,4 @@ +struct m_mcontext { + int gregs[16]; +}; + diff --git a/src/base/lib/mcontext/asm.SS b/src/base/lib/mcontext/asm.SS new file mode 100644 index 0000000..fefe877 --- /dev/null +++ b/src/base/lib/mcontext/asm.SS @@ -0,0 +1,464 @@ +/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */ + +#if defined(__FreeBSD__) && defined(__i386__) && __FreeBSD__ < 5 +#define NEEDX86CONTEXT 1 +#define SET setmcontext +#define GET getmcontext +#else +#if defined(__FreeBSD__) && defined(__x86_64__) +#define NEEDAMD64CONTEXT 1 +#define SET _setmcontext +#define GET _getmcontext +#endif +#endif + +#if defined(__OpenBSD__) && defined(__i386__) +#define NEEDX86CONTEXT 1 +#define SET setmcontext +#define GET getmcontext +#endif + +#if defined(__APPLE__) +#if defined(__i386__) +#define NEEDX86CONTEXT 1 +#define SET _setmcontext +#define GET _getmcontext +#elif defined(__x86_64__) +#define NEEDAMD64CONTEXT 1 +#define SET __setmcontext +#define GET __getmcontext +#elif defined(__arm__) +#define NEEDARMCONTEXT 1 +#define SET _setmcontext +#define GET _getmcontext +#else +#define NEEDPOWERCONTEXT 1 +#define SET __setmcontext +#define GET __getmcontext +#endif +#endif + +#if defined(__linux__) +#if defined(__i386__) +#define NEEDX86CONTEXT 1 +#define SET _setmcontext +#define GET _getmcontext +#elif defined(__x86_64__) +#define NEEDAMD64CONTEXT 1 +#define SET _setmcontext +#define GET _getmcontext +#elif defined(__aarch64__) +#define NEEDAARCH64CONTEXT 1 +#define SET _setmcontext +#define GET _getmcontext +#elif defined(__arm__) +#define NEEDARMCONTEXT 1 +#define SET _setmcontext +#define GET _getmcontext +#elif defined(__mips__) +#define NEEDMIPSCONTEXT 1 +#define SET setmcontext +#define GET getmcontext +#else +#define NEEDPOWERCONTEXT 1 +#define SET __setmcontext +#define GET __getmcontext +#endif +#endif + +#ifdef NEEDX86CONTEXT +.globl SET +SET: + movl 4(%esp), %eax + + mov 8(%eax), %fs + mov 12(%eax), %es + mov 16(%eax), %ds + mov 76(%eax), %ss + movl 20(%eax), %edi + movl 24(%eax), %esi + movl 28(%eax), %ebp + movl 36(%eax), %ebx + movl 40(%eax), %edx + movl 44(%eax), %ecx + + movl 72(%eax), %esp + pushl 60(%eax) /* new %eip */ + movl 48(%eax), %eax + ret + +.globl GET +GET: + movl 4(%esp), %eax + + mov %fs, 8(%eax) + mov %es, 12(%eax) + mov %ds, 16(%eax) + mov %ss, 76(%eax) + movl %edi, 20(%eax) + movl %esi, 24(%eax) + movl %ebp, 28(%eax) + movl %ebx, 36(%eax) + movl %edx, 40(%eax) + movl %ecx, 44(%eax) + + movl $1, 48(%eax) /* %eax */ + movl (%esp), %ecx /* %eip */ + movl %ecx, 60(%eax) + leal 4(%esp), %ecx /* %esp */ + movl %ecx, 72(%eax) + + movl 44(%eax), %ecx /* restore %ecx */ + movl $0, %eax + ret +#endif + +#ifdef NEEDAMD64CONTEXT +.globl SET +SET: + movq 16(%rdi), %rsi + movq 24(%rdi), %rdx + movq 32(%rdi), %rcx + movq 40(%rdi), %r8 + movq 48(%rdi), %r9 + movq 56(%rdi), %rax + movq 64(%rdi), %rbx + movq 72(%rdi), %rbp + movq 80(%rdi), %r10 + movq 88(%rdi), %r11 + movq 96(%rdi), %r12 + movq 104(%rdi), %r13 + movq 112(%rdi), %r14 + movq 120(%rdi), %r15 + movq 184(%rdi), %rsp + pushq 160(%rdi) /* new %eip */ + movq 8(%rdi), %rdi + ret + +.globl GET +GET: + movq %rdi, 8(%rdi) + movq %rsi, 16(%rdi) + movq %rdx, 24(%rdi) + movq %rcx, 32(%rdi) + movq %r8, 40(%rdi) + movq %r9, 48(%rdi) + movq $1, 56(%rdi) /* %rax */ + movq %rbx, 64(%rdi) + movq %rbp, 72(%rdi) + movq %r10, 80(%rdi) + movq %r11, 88(%rdi) + movq %r12, 96(%rdi) + movq %r13, 104(%rdi) + movq %r14, 112(%rdi) + movq %r15, 120(%rdi) + + movq (%rsp), %rcx /* %rip */ + movq %rcx, 160(%rdi) + leaq 8(%rsp), %rcx /* %rsp */ + movq %rcx, 184(%rdi) + + movq 32(%rdi), %rcx /* restore %rcx */ + movq $0, %rax + ret +#endif + +#ifdef NEEDPOWERCONTEXT +/* get FPR and VR use flags with sc 0x7FF3 */ +/* get vsave with mfspr reg, 256 */ + +.text +.align 2 + +.globl GET +GET: /* xxx: instruction scheduling */ + mflr r0 + mfcr r5 + mfctr r6 + mfxer r7 + stw r0, 0*4(r3) + stw r5, 1*4(r3) + stw r6, 2*4(r3) + stw r7, 3*4(r3) + + stw r1, 4*4(r3) + stw r2, 5*4(r3) + li r5, 1 /* return value for setmcontext */ + stw r5, 6*4(r3) + + stw r13, (0+7)*4(r3) /* callee-save GPRs */ + stw r14, (1+7)*4(r3) /* xxx: block move */ + stw r15, (2+7)*4(r3) + stw r16, (3+7)*4(r3) + stw r17, (4+7)*4(r3) + stw r18, (5+7)*4(r3) + stw r19, (6+7)*4(r3) + stw r20, (7+7)*4(r3) + stw r21, (8+7)*4(r3) + stw r22, (9+7)*4(r3) + stw r23, (10+7)*4(r3) + stw r24, (11+7)*4(r3) + stw r25, (12+7)*4(r3) + stw r26, (13+7)*4(r3) + stw r27, (14+7)*4(r3) + stw r28, (15+7)*4(r3) + stw r29, (16+7)*4(r3) + stw r30, (17+7)*4(r3) + stw r31, (18+7)*4(r3) + + li r3, 0 /* return */ + blr + +.globl SET +SET: + lwz r13, (0+7)*4(r3) /* callee-save GPRs */ + lwz r14, (1+7)*4(r3) /* xxx: block move */ + lwz r15, (2+7)*4(r3) + lwz r16, (3+7)*4(r3) + lwz r17, (4+7)*4(r3) + lwz r18, (5+7)*4(r3) + lwz r19, (6+7)*4(r3) + lwz r20, (7+7)*4(r3) + lwz r21, (8+7)*4(r3) + lwz r22, (9+7)*4(r3) + lwz r23, (10+7)*4(r3) + lwz r24, (11+7)*4(r3) + lwz r25, (12+7)*4(r3) + lwz r26, (13+7)*4(r3) + lwz r27, (14+7)*4(r3) + lwz r28, (15+7)*4(r3) + lwz r29, (16+7)*4(r3) + lwz r30, (17+7)*4(r3) + lwz r31, (18+7)*4(r3) + + lwz r1, 4*4(r3) + lwz r2, 5*4(r3) + + lwz r0, 0*4(r3) + mtlr r0 + lwz r0, 1*4(r3) + mtcr r0 /* mtcrf 0xFF, r0 */ + lwz r0, 2*4(r3) + mtctr r0 + lwz r0, 3*4(r3) + mtxer r0 + + lwz r3, 6*4(r3) + blr +#endif + +#ifdef NEEDARMCONTEXT +.globl GET +GET: + str r1, [r0,#4] + str r2, [r0,#8] + str r3, [r0,#12] + str r4, [r0,#16] + str r5, [r0,#20] + str r6, [r0,#24] + str r7, [r0,#28] + str r8, [r0,#32] + str r9, [r0,#36] + str r10, [r0,#40] + str r11, [r0,#44] + str r12, [r0,#48] + str r13, [r0,#52] + str r14, [r0,#56] + /* store 1 as r0-to-restore */ + mov r1, #1 + str r1, [r0] + /* return 0 */ + mov r0, #0 + mov pc, lr + +.globl SET +SET: + ldr r1, [r0,#4] + ldr r2, [r0,#8] + ldr r3, [r0,#12] + ldr r4, [r0,#16] + ldr r5, [r0,#20] + ldr r6, [r0,#24] + ldr r7, [r0,#28] + ldr r8, [r0,#32] + ldr r9, [r0,#36] + ldr r10, [r0,#40] + ldr r11, [r0,#44] + ldr r12, [r0,#48] + ldr r13, [r0,#52] + ldr r14, [r0,#56] + ldr r0, [r0] + mov pc, lr +#endif + +#ifdef NEEDAARCH64CONTEXT + +/* Size of an X regiser in bytes. */ +#define SZREG 8 + +/* Size of a V register in bytes. */ +#define SZVREG 16 + +#define oX0 8 +#define oSP 8*32 +#define oPC 8*33 +#define oPSTATE 8*34 + +/* Save current context. + + Copyright (C) 2009-2022 Free Software Foundation, Inc. + + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ +.globl GET +GET: + /* The saved context will return to the getcontext() call point + with a return value of 1 -stsp (glibc uses 0) */ + mov x2, 1 + str x2, [x0, oX0 + 0 * SZREG] + + /* x0-x18 seems to be caller-save, glibc doesn't save them... + * though it does actually save x18, strange. -stsp */ + stp x18, x19, [x0, oX0 + 18 * SZREG] + stp x20, x21, [x0, oX0 + 20 * SZREG] + stp x22, x23, [x0, oX0 + 22 * SZREG] + stp x24, x25, [x0, oX0 + 24 * SZREG] + stp x26, x27, [x0, oX0 + 26 * SZREG] + stp x28, x29, [x0, oX0 + 28 * SZREG] + str x30, [x0, oX0 + 30 * SZREG] + + /* Place LR into the saved PC, this will ensure that when + switching to this saved context with setcontext() control + will pass back to the caller of getcontext(), we have + already arrange to return the appropriate return value in x0 + above. */ + str x30, [x0, oPC] + + /* Save the current SP */ + mov x2, sp + str x2, [x0, oSP] + + /* Initialize the pstate. */ + str xzr, [x0, oPSTATE] + + /* Return 0 for success */ + mov x0, 0 + ret + +/* Set current context. + + Copyright (C) 2009-2022 Free Software Foundation, Inc. + + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ +.globl SET +SET: + ldp x18, x19, [x0, oX0 + 18 * SZREG] + ldp x20, x21, [x0, oX0 + 20 * SZREG] + ldp x22, x23, [x0, oX0 + 22 * SZREG] + ldp x24, x25, [x0, oX0 + 24 * SZREG] + ldp x26, x27, [x0, oX0 + 26 * SZREG] + ldp x28, x29, [x0, oX0 + 28 * SZREG] + ldr x30, [x0, oX0 + 30 * SZREG] + ldr x2, [x0, oSP] + mov sp, x2 + + ldr x16, [x0, oPC] + /* Restore arg registers. */ + ldp x2, x3, [x0, oX0 + 2 * SZREG] + ldp x4, x5, [x0, oX0 + 4 * SZREG] + ldp x6, x7, [x0, oX0 + 6 * SZREG] + ldp x0, x1, [x0, oX0 + 0 * SZREG] + /* Jump to the new pc value. */ + br x16 +#endif + +#ifdef NEEDMIPSCONTEXT +.globl GET +GET: + sw $4, 24($4) + sw $5, 28($4) + sw $6, 32($4) + sw $7, 36($4) + + sw $16, 72($4) + sw $17, 76($4) + sw $18, 80($4) + sw $19, 84($4) + sw $20, 88($4) + sw $21, 92($4) + sw $22, 96($4) + sw $23, 100($4) + + sw $28, 120($4) /* gp */ + sw $29, 124($4) /* sp */ + sw $30, 128($4) /* fp */ + sw $31, 132($4) /* ra */ + + xor $2, $2, $2 + j $31 + nop + +.globl SET +SET: + lw $16, 72($4) + lw $17, 76($4) + lw $18, 80($4) + lw $19, 84($4) + lw $20, 88($4) + lw $21, 92($4) + lw $22, 96($4) + lw $23, 100($4) + + lw $28, 120($4) /* gp */ + lw $29, 124($4) /* sp */ + lw $30, 128($4) /* fp */ + + /* + * If we set $31 directly and j $31, + * we would loose the outer return address. + * Use a temporary register, then. + */ + lw $8, 132($4) /* ra */ + + /* bug: not setting the pc causes a bus error */ + lw $25, 132($4) /* pc */ + + lw $5, 28($4) + lw $6, 32($4) + lw $7, 36($4) + lw $4, 24($4) + + j $8 + nop +#endif + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/base/lib/mcontext/context.c b/src/base/lib/mcontext/context.c new file mode 100644 index 0000000..ebc718c --- /dev/null +++ b/src/base/lib/mcontext/context.c @@ -0,0 +1,54 @@ +/* This code is taken from libtask library. + * Rip-off done by stsp for dosemu2 project. + * Original copyrights below. */ + +/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */ + +#include +#include +#include +#include +#include "mcontext.h" + +void makemcontext(m_ucontext_t *ucp, void (*func)(void*), void *arg) +{ + uintptr_t *sp; + + sp = (uintptr_t *)((unsigned char *)ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size); + sp = (void*)((uintptr_t)sp - (uintptr_t)sp%16); /* 16-align for OS X */ + +#ifdef __aarch64__ + ucp->uc_mcontext.regs[19] = (uintptr_t) ucp->uc_link; + ucp->uc_mcontext.sp = (uintptr_t) sp; + ucp->uc_mcontext.pc = (uintptr_t) func; + ucp->uc_mcontext.regs[0] = (uint64_t)arg; +#else +#ifdef __arm__ + ucp->uc_mcontext.gregs[13] = (uintptr_t)sp; + ucp->uc_mcontext.gregs[14] = (uintptr_t)func; + ucp->uc_mcontext.gregs[0] = (uintptr_t)arg; +#else +#ifdef __i386__ + sp -= 3; // alignment + *--sp = (uintptr_t)arg; +#else + ucp->uc_mcontext.mc_rdi = (uintptr_t)arg; +#endif + *--sp = 0; /* return address */ +#ifdef __i386__ + ucp->uc_mcontext.mc_eip = (uintptr_t)func; + ucp->uc_mcontext.mc_esp = (uintptr_t)sp; +#else + ucp->uc_mcontext.mc_rip = (uintptr_t)func; + ucp->uc_mcontext.mc_rsp = (uintptr_t)sp; +#endif +#endif +#endif +} + +int swapmcontext(m_ucontext_t *oucp, const m_ucontext_t *ucp) +{ + if(getmcontext(oucp) == 0) + setmcontext(ucp); + return 0; +} diff --git a/src/base/lib/mcontext/mcontext.h b/src/base/lib/mcontext/mcontext.h new file mode 100644 index 0000000..718b99f --- /dev/null +++ b/src/base/lib/mcontext/mcontext.h @@ -0,0 +1,72 @@ +/* This code is taken from libtask library. + * Rip-off done by stsp for dosemu2 project. + * Original copyrights below. */ + +/* Copyright (c) 2005-2006 Russ Cox, MIT; see COPYRIGHT */ + +#ifndef MCONTEXT_H +#define MCONTEXT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct m_mcontext m_mcontext_t; +typedef struct m_ucontext m_ucontext_t; + +# if defined(__i386__) +# include "386-ucontext.h" +# elif defined(__x86_64__) +# include "amd64-ucontext.h" +# elif defined(__arm__) +# include "arm-ucontext.h" +# elif defined(__aarch64__) +# include "aarch64-ucontext.h" +# elif defined (__power__) +# include "power-ucontext.h" +# else +# error Unsupported arch +# endif + +struct m_ucontext { + /* + * Keep the order of the first two fields. Also, + * keep them the first two fields in the structure. + * This way we can have a union with struct + * sigcontext and ucontext_t. This allows us to + * support them both at the same time. + * note: the union is not defined, though. + */ + sigset_t uc_sigmask; + m_mcontext_t uc_mcontext; + + struct __ucontext *uc_link; + stack_t uc_stack; + int __spare__[8]; +}; + +extern int _getmcontext(m_mcontext_t*); +extern int _setmcontext(const m_mcontext_t*); +static inline int setmcontext(const struct m_ucontext *u) +{ + return _setmcontext(&u->uc_mcontext); +} +/* getmcontext() MUST be inlined, or it will grab the context + * of its own stack frame */ +static inline __attribute__((always_inline)) +int getmcontext(struct m_ucontext *u) +{ + /* called so frequently that memset() is expensive */ +// memset(&u->uc_mcontext, 0, sizeof u->uc_mcontext); + return _getmcontext(&u->uc_mcontext); +} +extern int swapmcontext(m_ucontext_t*, const m_ucontext_t*); +extern void makemcontext(m_ucontext_t*, void(*)(void*), void*); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/src/base/lib/mcontext/mips-ucontext.h b/src/base/lib/mcontext/mips-ucontext.h new file mode 100644 index 0000000..4d4429e --- /dev/null +++ b/src/base/lib/mcontext/mips-ucontext.h @@ -0,0 +1,77 @@ +typedef struct mcontext mcontext_t; +typedef struct ucontext ucontext_t; + +extern int swapcontext(ucontext_t*, const ucontext_t*); +extern void makecontext(ucontext_t*, void(*)(), int, ...); + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ucontext.h 8.1 (Berkeley) 6/10/93 + * JNPR: ucontext.h,v 1.2 2007/08/09 11:23:32 katta + * $FreeBSD: src/sys/mips/include/ucontext.h,v 1.2 2010/01/10 19:50:24 imp Exp $ + */ + +struct mcontext { + /* + * These fields must match the corresponding fields in struct + * sigcontext which follow 'sc_mask'. That way we can support + * struct sigcontext and ucontext_t at the same time. + */ + int mc_onstack; /* sigstack state to restore */ + int mc_pc; /* pc at time of signal */ + int mc_regs[32]; /* processor regs 0 to 31 */ + int sr; /* status register */ + int mullo, mulhi; /* mullo and mulhi registers... */ + int mc_fpused; /* fp has been used */ + int mc_fpregs[33]; /* fp regs 0 to 31 and csr */ + int mc_fpc_eir; /* fp exception instruction reg */ + void *mc_tls; /* pointer to TLS area */ + int __spare__[8]; /* XXX reserved */ +}; + +struct ucontext { + /* + * Keep the order of the first two fields. Also, + * keep them the first two fields in the structure. + * This way we can have a union with struct + * sigcontext and ucontext_t. This allows us to + * support them both at the same time. + * note: the union is not defined, though. + */ + sigset_t uc_sigmask; + mcontext_t uc_mcontext; + + struct __ucontext *uc_link; + stack_t uc_stack; + int uc_flags; + int __spare__[4]; +}; diff --git a/src/base/lib/mcontext/power-ucontext.h b/src/base/lib/mcontext/power-ucontext.h new file mode 100644 index 0000000..691f0f5 --- /dev/null +++ b/src/base/lib/mcontext/power-ucontext.h @@ -0,0 +1,37 @@ +#define setcontext(u) _setmcontext(&(u)->mc) +#define getcontext(u) _getmcontext(&(u)->mc) +typedef struct mcontext mcontext_t; +typedef struct ucontext ucontext_t; +struct mcontext +{ + ulong pc; /* lr */ + ulong cr; /* mfcr */ + ulong ctr; /* mfcr */ + ulong xer; /* mfcr */ + ulong sp; /* callee saved: r1 */ + ulong toc; /* callee saved: r2 */ + ulong r3; /* first arg to function, return register: r3 */ + ulong gpr[19]; /* callee saved: r13-r31 */ +/* +// XXX: currently do not save vector registers or floating-point state +// ulong pad; +// uvlong fpr[18]; / * callee saved: f14-f31 * / +// ulong vr[4*12]; / * callee saved: v20-v31, 256-bits each * / +*/ +}; + +struct ucontext +{ + struct { + void *ss_sp; + uint ss_size; + } uc_stack; + sigset_t uc_sigmask; + mcontext_t mc; +}; + +void makecontext(ucontext_t*, void(*)(void), int, ...); +int swapcontext(ucontext_t*, const ucontext_t*); +int _getmcontext(mcontext_t*); +void _setmcontext(const mcontext_t*); + diff --git a/src/base/lib/misc/Makefile b/src/base/lib/misc/Makefile new file mode 100644 index 0000000..e1a40a0 --- /dev/null +++ b/src/base/lib/misc/Makefile @@ -0,0 +1,11 @@ + +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf + +CFILES = smalloc.c pgalloc.c dlmalloc.c ringbuf.c spscq.c cpi.c dis8086.c \ + shlock.c sequencr.c + +include $(REALTOPDIR)/src/Makefile.common + +clean:: + rm -f *.out diff --git a/src/base/lib/misc/cpi.c b/src/base/lib/misc/cpi.c new file mode 100644 index 0000000..2b9e441 --- /dev/null +++ b/src/base/lib/misc/cpi.c @@ -0,0 +1,202 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: CPI font loader + * + * Author: Stas Sergeev + * + * based on codepage.c from kbd package. + * Original copyrights below: + * Author: Ahmed M. Naas (ahmed@oea.xs4all.nl) + * Many changes: aeb@cwi.nl [changed until it would handle all + * *.cpi files people have sent me; I have no documentation, + * so all this is experimental] + * Remains to do: DRDOS fonts. + * + * Copyright: Public domain. + */ + +//#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpi.h" + +#define PACKED __attribute__((packed)) + +struct FontFileHeader { + unsigned char id0; + char id[7]; + unsigned char res[8]; + unsigned short pnum; /* number of pointers */ + unsigned char ptyp; /* type of pointers */ + uint32_t fih_offset; /* FontInfoHeader offset */ + unsigned short num_codepages; +} PACKED; + +struct CPEntryHeader { + unsigned short size; + uint32_t off_nexthdr; + unsigned short device_type; /* screen=1; printer=2 */ + unsigned char device_name[8]; + unsigned short codepage; + unsigned char res[6]; + uint32_t off_font; +} PACKED; + +struct CPInfoHeader { + unsigned short reserved; + unsigned short num_fonts; + unsigned short size; +} PACKED; + +struct ScreenFontHeader { + unsigned char height; + unsigned char width; + unsigned short reserved; + unsigned short num_chars; +} PACKED; + +static uint8_t *find_font(uint8_t *data, uint16_t cp, + uint8_t w, uint8_t h, off_t size, int *r_size) +{ + struct FontFileHeader hdr; + struct CPEntryHeader cph; + struct CPInfoHeader cpi; + struct ScreenFontHeader sf; + uint8_t *p = data; + off_t remain = size; + + if (remain < sizeof(hdr)) + return NULL; + memcpy(&hdr, p, sizeof(hdr)); + p += sizeof(hdr); + remain -= sizeof(hdr); + if (strncmp(hdr.id, "FONT", 4) != 0) + return NULL; + while (hdr.num_codepages--) { + if (remain < sizeof(cph)) + return NULL; + memcpy(&cph, p, sizeof(cph)); + p += sizeof(cph); + remain -= sizeof(cph); + if (cph.device_type != 1) + return NULL; +#define NEXT_HDR() \ + p = data + cph.off_nexthdr; \ + remain = size - cph.off_nexthdr + + if (cph.codepage != cp) { + NEXT_HDR(); + continue; + } + p = data + cph.off_font; + remain = size - cph.off_font; + if (remain < sizeof(cpi)) + return NULL; + memcpy(&cpi, p, sizeof(cpi)); + p += sizeof(cpi); + remain -= sizeof(cpi); + while (cpi.num_fonts--) { + int flen; + if (remain < sizeof(sf)) + return NULL; + memcpy(&sf, p, sizeof(sf)); + p += sizeof(sf); + remain -= sizeof(sf); + flen = sf.height * sf.num_chars; + if (sf.width == w && sf.height == h) { + *r_size = flen; + return p; + } + p += flen; + remain -= flen; + } + NEXT_HDR(); + } + return NULL; +} + +uint8_t *cpi_load_font(const char *path, uint16_t cp, + uint8_t w, uint8_t h, int *r_size) +{ + uint8_t *ret = NULL; + glob_t p; + char *wild; + int fd; + int rc; + struct stat st; + uint8_t *file_data = NULL; + uint8_t *font_data; + int i; + + asprintf(&wild, "%s/*.cpi", path); + glob(wild, 0, NULL, &p); + free(wild); + for (i = 0; i < p.gl_pathc; i++) { + fd = open(p.gl_pathv[i], O_RDONLY); + if (fd == -1) + goto err1; + rc = fstat(fd, &st); + if (rc == -1) + goto err2; + file_data = malloc(st.st_size); + rc = read(fd, file_data, st.st_size); + if (rc != st.st_size) + goto err3; + close(fd); + font_data = find_font(file_data, cp, w, h, st.st_size, r_size); + if (font_data) { + ret = malloc(*r_size); + memcpy(ret, font_data, *r_size); + } + free(file_data); + if (ret) + break; + } + globfree(&p); + return ret; + +err3: + free(file_data); +err2: + close(fd); +err1: + globfree(&p); + return NULL; +} + +#if 0 +int main(int argc, char *argv[]) +{ + uint8_t *p; + int len; + if (argc != 4) + return 1; + p = cpi_load_font(argv[1], atoi(argv[2]), 8, atoi(argv[3]), &len); + if (p) { + printf("font found, len %i\n", len); + free(p); + } + return 0; +} +#endif diff --git a/src/base/lib/misc/dis8086.c b/src/base/lib/misc/dis8086.c new file mode 100644 index 0000000..23398d3 --- /dev/null +++ b/src/base/lib/misc/dis8086.c @@ -0,0 +1,1151 @@ +/* this code is adapted from 2asm via MAME and DOSBox + * changed jcc in 32-bit mode (addr_to_hex) to give normal addresses without : + * store referenced address in refoff for dosdebug. + * removed callback opcode + */ + +/* + Ripped out some stuff from the mame releae to only make it for 386's + Changed some variables to use the standard DOSBox data types + Added my callback opcode + +*/ + +/* + * 2asm: Convert binary files to 80*86 assembler. Version 1.00 + * Adapted by Andrea Mazzoleni for use with MAME + * HJB 990321: + * Changed output of hex values from 0xxxxh to $xxxx format + * Removed "ptr" from "byte ptr", "word ptr" and "dword ptr" +*/ + +/* 2asm comments + +License: + + 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. + + 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. + +Comments: + + The code was originally snaffled from the GNU C++ debugger, as ported + to DOS by DJ Delorie and Kent Williams (williams@herky.cs.uiowa.edu). + Extensively modified by Robin Hilliard in Jan and May 1992. + + This source compiles under Turbo C v2.01. The disassembler is entirely + table driven so it's fairly easy to change to suit your own tastes. + + The instruction table has been modified to correspond with that in + `Programmer's Technical Reference: The Processor and Coprocessor', + Robert L. Hummel, Ziff-Davis Press, 1992. Missing (read "undocumented") + instructions were added and many mistakes and omissions corrected. + + +Health warning: + + When writing and degbugging this code, I didn't have (and still don't have) + a 32-bit disassembler to compare this guy's output with. It's therefore + quite likely that bugs will appear when disassembling instructions which use + the 386 and 486's native 32 bit mode. It seems to work fine in 16 bit mode. + +Any comments/updates/bug reports to: + + Robin Hilliard, Lough Guitane, Killarney, Co. Kerry, Ireland. + Tel: [+353] 64-54014 + Internet: softloft@iruccvax.ucc.ie + Compu$erve: 100042, 1237 + + If you feel like registering, and possibly get notices of updates and + other items of software, then send me a post card of your home town. + + Thanks and enjoy! + +*/ +#include +#include +#include +#include +#include +#include "cpu.h" +#include "dis8086.h" +#define INLINE static inline +#undef REG +static int disasunix; +static inline unsigned char mem_readb(unsigned int x) +{ + if (disasunix) + return UNIX_READ_BYTE((uintptr_t)x); + else + return READ_BYTE(x); +} + +typedef Bit8u UINT8; +typedef Bit16u UINT16; +typedef Bit32u UINT32; +typedef unsigned int PhysPt; + +typedef Bit8s INT8; +typedef Bit16s INT16; +typedef Bit32s INT32; + + +/* Little endian uint read */ +#define le_uint8(ptr) (*(UINT8*)ptr) + +INLINE UINT16 le_uint16(const void* ptr) { + const UINT8* ptr8 = (const UINT8*)ptr; + return (UINT16)ptr8[0] | (UINT16)ptr8[1] << 8; +} +INLINE UINT32 le_uint32(const void* ptr) { + const UINT8* ptr8 = (const UINT8*)ptr; + return (UINT32)ptr8[0] | (UINT32)ptr8[1] << 8 | (UINT32)ptr8[2] << 16 | (UINT32)ptr8[3] << 24; +} + +/* Little endian int read */ +#define le_int8(ptr) ((INT8)le_uint8(ptr)) +#define le_int16(ptr) ((INT16)le_uint16(ptr)) +#define le_int32(ptr) ((INT32)le_uint32(ptr)) + +#define fp_segment(dw) ((dw >> 16) & 0xFFFFU) +#define fp_offset(dw) (dw & 0xFFFFU) +#define fp_addr(seg, off) ( (seg<<4)+off ) + +static UINT8 must_do_size; /* used with size of operand */ +static int wordop; /* dealing with word or byte operand */ + +static int instruction_offset; +//static UINT16 instruction_segment; + +static char* ubufs; /* start of buffer */ +static char* ubufp; /* last position of buffer */ +static int invalid_opcode = 0; +static int first_space = 1; + +static int prefix; /* segment override prefix byte */ +static int modrmv; /* flag for getting modrm byte */ +static int sibv; /* flag for getting sib byte */ +static int opsize; /* just like it says ... */ +static int addrsize; +static int addr32bit=0; + +static int refoff; + +/* some defines for extracting instruction bit fields from bytes */ + +#define MOD(a) (((a)>>6)&7) +#define REG(a) (((a)>>3)&7) +#define RM(a) ((a)&7) +#define SCALE(a) (((a)>>6)&7) +#define INDEX(a) (((a)>>3)&7) +#define BASE(a) ((a)&7) + +/* Percent tokens in strings: + First char after '%': + A - direct address + C - reg of r/m picks control register + D - reg of r/m picks debug register + E - r/m picks operand + F - flags register + G - reg of r/m picks general register + I - immediate data + J - relative IP offset ++ K - call/jmp distance + M - r/m picks memory + O - no r/m, offset only + R - mod of r/m picks register only + S - reg of r/m picks segment register + T - reg of r/m picks test register + X - DS:ESI + Y - ES:EDI + 2 - prefix of two-byte opcode ++ e - put in 'e' if use32 (second char is part of reg name) ++ put in 'w' for use16 or 'd' for use32 (second char is 'w') ++ j - put in 'e' in jcxz if prefix==0x66 + f - floating point (second char is esc value) + g - do r/m group 'n', n==0..7 + p - prefix + s - size override (second char is a,o) ++ d - put d if double arg, nothing otherwise (pushfd, popfd &c) ++ w - put w if word, d if double arg, nothing otherwise (lodsw/lodsd) ++ P - simple prefix + + Second char after '%': + a - two words in memory (BOUND) + b - byte + c - byte or word + d - dword ++ f - far call/jmp ++ n - near call/jmp + p - 32 or 48 bit pointer ++ q - byte/word thingy + s - six byte pseudo-descriptor + v - word or dword + w - word ++ x - sign extended byte + F - use floating regs in mod/rm + 1-8 - group number, esc value, etc +*/ + +/* watch out for aad && aam with odd operands */ + +static const char *(*opmap1)[256]; + +static const char *op386map1[256] = { +/* 0 */ + "add %Eb,%Gb", "add %Ev,%Gv", "add %Gb,%Eb", "add %Gv,%Ev", + "add al,%Ib", "add %eax,%Iv", "push es", "pop es", + "or %Eb,%Gb", "or %Ev,%Gv", "or %Gb,%Eb", "or %Gv,%Ev", + "or al,%Ib", "or %eax,%Iv", "push cs", "%2 ", +/* 1 */ + "adc %Eb,%Gb", "adc %Ev,%Gv", "adc %Gb,%Eb", "adc %Gv,%Ev", + "adc al,%Ib", "adc %eax,%Iv", "push ss", "pop ss", + "sbb %Eb,%Gb", "sbb %Ev,%Gv", "sbb %Gb,%Eb", "sbb %Gv,%Ev", + "sbb al,%Ib", "sbb %eax,%Iv", "push ds", "pop ds", +/* 2 */ + "and %Eb,%Gb", "and %Ev,%Gv", "and %Gb,%Eb", "and %Gv,%Ev", + "and al,%Ib", "and %eax,%Iv", "%pe", "daa", + "sub %Eb,%Gb", "sub %Ev,%Gv", "sub %Gb,%Eb", "sub %Gv,%Ev", + "sub al,%Ib", "sub %eax,%Iv", "%pc", "das", +/* 3 */ + "xor %Eb,%Gb", "xor %Ev,%Gv", "xor %Gb,%Eb", "xor %Gv,%Ev", + "xor al,%Ib", "xor %eax,%Iv", "%ps", "aaa", + "cmp %Eb,%Gb", "cmp %Ev,%Gv", "cmp %Gb,%Eb", "cmp %Gv,%Ev", + "cmp al,%Ib", "cmp %eax,%Iv", "%pd", "aas", +/* 4 */ + "inc %eax", "inc %ecx", "inc %edx", "inc %ebx", + "inc %esp", "inc %ebp", "inc %esi", "inc %edi", + "dec %eax", "dec %ecx", "dec %edx", "dec %ebx", + "dec %esp", "dec %ebp", "dec %esi", "dec %edi", +/* 5 */ + "push %eax", "push %ecx", "push %edx", "push %ebx", + "push %esp", "push %ebp", "push %esi", "push %edi", + "pop %eax", "pop %ecx", "pop %edx", "pop %ebx", + "pop %esp", "pop %ebp", "pop %esi", "pop %edi", +/* 6 */ + "pusha%d ", "popa%d ", "bound %Gv,%Ma", "arpl %Ew,%Rw", + "%pf", "%pg", "%so", "%sa", + "push %Iv", "imul %Gv,%Ev,%Iv","push %Ix", "imul %Gv,%Ev,%Ib", + "insb", "ins%ew", "outsb", "outs%ew", +/* 7 */ + "jo %Jb", "jno %Jb", "jc %Jb", "jnc %Jb", + "je %Jb", "jne %Jb", "jbe %Jb", "ja %Jb", + "js %Jb", "jns %Jb", "jpe %Jb", "jpo %Jb", + "jl %Jb", "jge %Jb", "jle %Jb", "jg %Jb", +/* 8 */ + "%g0 %Eb,%Ib", "%g0 %Ev,%Iv", "%g0 %Eb,%Ib", "%g0 %Ev,%Ix", + "test %Eb,%Gb", "test %Ev,%Gv", "xchg %Eb,%Gb", "xchg %Ev,%Gv", + "mov %Eb,%Gb", "mov %Ev,%Gv", "mov %Gb,%Eb", "mov %Gv,%Ev", + "mov %Ew,%Sw", "lea %Gv,%M ", "mov %Sw,%Ew", "pop %Ev", +/* 9 */ + "nop", "xchg %ecx,%eax", "xchg %edx,%eax", "xchg %ebx,%eax", + "xchg %esp,%eax", "xchg %ebp,%eax", "xchg %esi,%eax", "xchg %edi,%eax", + "cbw", "cwd", "call %Ap", "fwait", + "pushf%d ", "popf%d ", "sahf", "lahf", +/* a */ + "mov al,%Oc", "mov %eax,%Ov", "mov %Oc,al", "mov %Ov,%eax", + "%P movsb", "%P movs%w", "%P cmpsb", "%P cmps%w ", + "test al,%Ib", "test %eax,%Iv", "%P stosb", "%P stos%w ", + "%P lodsb", "%P lods%w ", "%P scasb", "%P scas%w ", +/* b */ + "mov al,%Ib", "mov cl,%Ib", "mov dl,%Ib", "mov bl,%Ib", + "mov ah,%Ib", "mov ch,%Ib", "mov dh,%Ib", "mov bh,%Ib", + "mov %eax,%Iv", "mov %ecx,%Iv", "mov %edx,%Iv", "mov %ebx,%Iv", + "mov %esp,%Iv", "mov %ebp,%Iv", "mov %esi,%Iv", "mov %edi,%Iv", +/* c */ + "%g1 %Eb,%Ib", "%g1 %Ev,%Ib", "ret %Iw", "ret", + "les %Gv,%Mp", "lds %Gv,%Mp", "mov %Eb,%Ib", "mov %Ev,%Iv", + "enter %Iw,%Ib", "leave", "retf %Iw", "retf", + "int 03", "int %Ib", "into", "iret", +/* d */ + "%g1 %Eb,1", "%g1 %Ev,1", "%g1 %Eb,cl", "%g1 %Ev,cl", + "aam ; %Ib", "aad ; %Ib", "setalc", "xlat", +#if 0 + "esc 0,%Ib", "esc 1,%Ib", "esc 2,%Ib", "esc 3,%Ib", + "esc 4,%Ib", "esc 5,%Ib", "esc 6,%Ib", "esc 7,%Ib", +#else + "%f0", "%f1", "%f2", "%f3", + "%f4", "%f5", "%f6", "%f7", +#endif +/* e */ + "loopne %Jb", "loope %Jb", "loop %Jb", "j%j cxz %Jb", + "in al,%Ib", "in %eax,%Ib", "out %Ib,al", "out %Ib,%eax", + "call %Jv", "jmp %Jv", "jmp %Ap", "jmp %Ks%Jb", + "in al,dx", "in %eax,dx", "out dx,al", "out dx,%eax", +/* f */ + "lock %p ", "icebp", "repne %p ", "repe %p ", + "hlt", "cmc", "%g2", "%g2", + "clc", "stc", "cli", "sti", + "cld", "std", "%g3", "%g4" +}; + +static const char *second[] = { +/* 0 */ + "%g5", "%g6", "lar %Gv,%Ew", "lsl %Gv,%Ew", + 0, "[loadall]", "clts", "[loadall]", + "invd", "wbinvd", 0, "UD2", + 0, 0, 0, 0, +/* 1 */ + "mov %Eb,%Gb", "mov %Ev,%Gv", "mov %Gb,%Eb", "mov %Gv,%Ev", + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, +/* 2 */ + "mov %Rd,%Cd", "mov %Rd,%Dd", "mov %Cd,%Rd", "mov %Dd,%Rd", + "mov %Rd,%Td", 0, "mov %Td,%Rd", 0, + 0, 0, 0, 0, + 0, 0, 0, 0, +/* 3 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +/* 4 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +/* 5 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +/* 6 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +/* 7 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +/* 8 */ + "jo %Jv", "jno %Jv", "jb %Jv", "jnb %Jv", + "jz %Jv", "jnz %Jv", "jbe %Jv", "ja %Jv", + "js %Jv", "jns %Jv", "jp %Jv", "jnp %Jv", + "jl %Jv", "jge %Jv", "jle %Jv", "jg %Jv", +/* 9 */ + "seto %Eb", "setno %Eb", "setc %Eb", "setnc %Eb", + "setz %Eb", "setnz %Eb", "setbe %Eb", "setnbe %Eb", + "sets %Eb", "setns %Eb", "setp %Eb", "setnp %Eb", + "setl %Eb", "setge %Eb", "setle %Eb", "setg %Eb", +/* a */ + "push fs", "pop fs", "cpuid", "bt %Ev,%Gv", + "shld %Ev,%Gv,%Ib", "shld %Ev,%Gv,cl", 0, 0, + "push gs", "pop gs", 0, "bts %Ev,%Gv", + "shrd %Ev,%Gv,%Ib", "shrd %Ev,%Gv,cl", 0, "imul %Gv,%Ev", +/* b */ + "cmpxchg %Eb,%Gb", "cmpxchg %Ev,%Gv", "lss %Mp", "btr %Ev,%Gv", + "lfs %Mp", "lgs %Mp", "movzx %Gv,%Eb", "movzx %Gv,%Ew", + 0, 0, "%g7 %Ev,%Ib", "btc %Ev,%Gv", + "bsf %Gv,%Ev", "bsr %Gv,%Ev", "movsx %Gv,%Eb", "movsx %Gv,%Ew", +/* c */ + "xadd %Eb,%Gb", "xadd %Ev,%Gv", 0, 0, + 0, 0, 0, 0, + "bswap eax", "bswap ecx", "bswap edx", "bswap ebx", + "bswap esp", "bswap ebp", "bswap esi", "bswap edi", +/* d */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +/* e */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +/* f */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const char *groups[][8] = { /* group 0 is group 3 for %Ev set */ +/* 0 */ + { "add", "or", "adc", "sbb", + "and", "sub", "xor", "cmp" }, +/* 1 */ + { "rol", "ror", "rcl", "rcr", + "shl", "shr", "shl", "sar" }, +/* 2 */ /* v v*/ + { "test %Eq,%Iq", "test %Eq,%Iq", "not %Ec", "neg %Ec", + "mul %Ec", "imul %Ec", "div %Ec", "idiv %Ec" }, +/* 3 */ + { "inc %Eb", "dec %Eb", 0, 0, + 0, 0, 0, 0, }, +/* 4 */ + { "inc %Ev", "dec %Ev", "call %Kn%Ev", "call %Kf%Ep", + "jmp %Kn%Ev", "jmp %Kf%Ep", "push %Ev", 0 }, +/* 5 */ + { "sldt %Ew", "str %Ew", "lldt %Ew", "ltr %Ew", + "verr %Ew", "verw %Ew", 0, 0 }, +/* 6 */ + { "sgdt %Ms", "sidt %Ms", "lgdt %Ms", "lidt %Ms", + "smsw %Ew", 0, "lmsw %Ew", "invlpg" }, +/* 7 */ + { 0, 0, 0, 0, + "bt", "bts", "btr", "btc" } +}; + +/* zero here means invalid. If first entry starts with '*', use st(i) */ +/* no assumed %EFs here. Indexed by RM(modrm()) */ +static const char *f0[] = { 0, 0, 0, 0, 0, 0, 0, 0}; +static const char *fop_8[] = { "*fld st,%GF" }; +static const char *fop_9[] = { "*fxch st,%GF" }; +static const char *fop_10[] = { "fnop", 0, 0, 0, 0, 0, 0, 0 }; +static const char *fop_11[] = { "*fst st,%GF" }; +static const char *fop_12[] = { "fchs", "fabs", 0, 0, "ftst", "fxam", 0, 0 }; +static const char *fop_13[] = { "fld1", "fldl2t", "fldl2e", "fldpi", + "fldlg2", "fldln2", "fldz", 0 }; +static const char *fop_14[] = { "f2xm1", "fyl2x", "fptan", "fpatan", + "fxtract", "fprem1", "fdecstp", "fincstp" }; +static const char *fop_15[] = { "fprem", "fyl2xp1", "fsqrt", "fsincos", + "frndint", "fscale", "fsin", "fcos" }; +static const char *fop_21[] = { 0, "fucompp", 0, 0, 0, 0, 0, 0 }; +static const char *fop_28[] = { "[fneni]", "[fndis]", "fclex", "finit", "[fnsetpm]", "[frstpm]", 0, 0 }; +static const char *fop_32[] = { "*fadd %GF,st" }; +static const char *fop_33[] = { "*fmul %GF,st" }; +static const char *fop_34[] = { "*fcom %GF,st" }; +static const char *fop_35[] = { "*fcomp %GF,st" }; +static const char *fop_36[] = { "*fsubr %GF,st" }; +static const char *fop_37[] = { "*fsub %GF,st" }; +static const char *fop_38[] = { "*fdivr %GF,st" }; +static const char *fop_39[] = { "*fdiv %GF,st" }; +static const char *fop_40[] = { "*ffree %GF" }; +static const char *fop_41[] = { "*fxch %GF" }; +static const char *fop_42[] = { "*fst %GF" }; +static const char *fop_43[] = { "*fstp %GF" }; +static const char *fop_44[] = { "*fucom %GF" }; +static const char *fop_45[] = { "*fucomp %GF" }; +static const char *fop_48[] = { "*faddp %GF,st" }; +static const char *fop_49[] = { "*fmulp %GF,st" }; +static const char *fop_50[] = { "*fcomp %GF,st" }; +static const char *fop_51[] = { 0, "fcompp", 0, 0, 0, 0, 0, 0 }; +static const char *fop_52[] = { "*fsubrp %GF,st" }; +static const char *fop_53[] = { "*fsubp %GF,st" }; +static const char *fop_54[] = { "*fdivrp %GF,st" }; +static const char *fop_55[] = { "*fdivp %GF,st" }; +static const char *fop_60[] = { "fstsw ax", 0, 0, 0, 0, 0, 0, 0 }; + +static const char **fspecial[] = { /* 0=use st(i), 1=undefined 0 in fop_* means undefined */ + 0, 0, 0, 0, 0, 0, 0, 0, + fop_8, fop_9, fop_10, fop_11, fop_12, fop_13, fop_14, fop_15, + f0, f0, f0, f0, f0, fop_21, f0, f0, + f0, f0, f0, f0, fop_28, f0, f0, f0, + fop_32, fop_33, fop_34, fop_35, fop_36, fop_37, fop_38, fop_39, + fop_40, fop_41, fop_42, fop_43, fop_44, fop_45, f0, f0, + fop_48, fop_49, fop_50, fop_51, fop_52, fop_53, fop_54, fop_55, + f0, f0, f0, f0, fop_60, f0, f0, f0, +}; + +static const char *floatops[] = { /* assumed " %EF" at end of each. mod != 3 only */ +/*00*/ "fadd", "fmul", "fcom", "fcomp", + "fsub", "fsubr", "fdiv", "fdivr", +/*08*/ "fld", 0, "fst", "fstp", + "fldenv", "fldcw", "fstenv", "fstcw", +/*16*/ "fiadd", "fimul", "ficomw", "ficompw", + "fisub", "fisubr", "fidiv", "fidivr", +/*24*/ "fild", 0, "fist", "fistp", + "frstor", "fldt", 0, "fstpt", +/*32*/ "faddq", "fmulq", "fcomq", "fcompq", + "fsubq", "fsubrq", "fdivq", "fdivrq", +/*40*/ "fldq", 0, "fstq", "fstpq", + 0, 0, "fsave", "fstsw", +/*48*/ "fiaddw", "fimulw", "ficomw", "ficompw", + "fisubw", "fisubrw", "fidivw", "fidivr", +/*56*/ "fildw", 0, "fistw", "fistpw", + "fbldt", "fildq", "fbstpt", "fistpq" +}; + +static char *addr_to_hex(UINT32 addr) { + static char buffer[11]; + + if (opsize == 32) + sprintf(buffer, "%08X", addr); + else { + addr = fp_offset(addr); + sprintf(buffer, "%04X", addr); + } + refoff = addr; + return buffer; +} + +static PhysPt getbyte_mac; +static PhysPt startPtr; + +static UINT8 getbyte(void) { + return mem_readb(getbyte_mac++); +} + +/* + only one modrm or sib byte per instruction, tho' they need to be + returned a few times... +*/ + +static int modrm(void) +{ + if (modrmv == -1) + modrmv = getbyte(); + return modrmv; +} + +static int sib(void) +{ + if (sibv == -1) + sibv = getbyte(); + return sibv; +} + +/*------------------------------------------------------------------------*/ + +static void uprintf(const char *s, ...) +{ + va_list arg_ptr; + + va_start(arg_ptr, s); + vsprintf(ubufp, s, arg_ptr); + while (*ubufp) + ubufp++; + va_end(arg_ptr); +} + +static void uputchar(char c) +{ + *ubufp++ = c; + *ubufp = 0; +} + +/*------------------------------------------------------------------------*/ + +static int bytes(char c) +{ + switch (c) { + case 'b': + return 1; + case 'w': + return 2; + case 'd': + return 4; + case 'v': + if (opsize == 32) + return 4; + else + return 2; + } + return 0; +} + +/*------------------------------------------------------------------------*/ +static void outhex(char subtype, int extend, int optional, int defsize, int sign) +{ + int n=0, s=0, i; + INT32 delta = 0; + unsigned char buff[6]; + char *name; + char signchar; + + switch (subtype) { + case 'q': + if (wordop) { + if (opsize==16) { + n = 2; + } else { + n = 4; + } + } else { + n = 1; + } + break; + + case 'a': + break; + case 'x': + if (opsize == 8) { + extend = 1; + } else if (opsize == 16) { + extend = 2; + } else if (opsize == 32) { + extend = 4; + } + n = 1; + break; + case 'b': + n = 1; + break; + case 'w': + n = 2; + break; + case 'd': + n = 4; + break; + case 's': + n = 6; + break; + case 'c': + case 'v': + if (defsize == 32) + n = 4; + else + n = 2; + break; + case 'p': + if (defsize == 32) + n = 6; + else + n = 4; + s = 1; + break; + } + for (i=0; i n) { + if (subtype!='x') { + if (delta<0) { + delta = -delta; + signchar = '-'; + } else + signchar = '+'; + if (delta || !optional) + uprintf("%c%0*lX", (char)signchar, (int)(extend), (long)delta); + } else { + if (extend == 2) { + delta = (UINT16)delta; + uprintf("%0.*lX", (int)(2*extend), (long)delta ); + } else if (extend == 4) { + delta = (UINT32)delta; + uprintf("%0.*lX", (int)(2*extend), (UINT32)delta ); + } + } + return; + } + if ((n == 4) && !sign) { + name = addr_to_hex(delta); + uprintf("%s", name); + return; + } + switch (n) { + case 1: + if (sign && (char)delta<0) { + delta = -delta; + signchar = '-'; + } else + signchar = '+'; + if (sign) + uprintf("%c%02lX", (char)signchar, delta & 0xFFL); + else + uprintf("%02lX", delta & 0xFFL); + break; + + case 2: + if (sign && delta<0) { + signchar = '-'; + delta = -delta; + } else + signchar = '+'; + if (sign) + uprintf("%c%04lX", (char)signchar, delta & 0xFFFFL); + else { + if (subtype != 'w') refoff += delta & 0xFFFFL; + uprintf("%04lX", delta & 0xFFFFL); + } + break; + + case 4: + if (sign && delta<0) { + delta = -delta; + signchar = '-'; + } else + signchar = '+'; + if (sign) + uprintf("%c%08lX", (char)signchar, delta & 0xFFFFFFFFL); + else { + refoff += delta & 0xFFFFFFFFL; + uprintf("%08lX", delta & 0xFFFFFFFFL); + } + break; + } +} + + +/*------------------------------------------------------------------------*/ + +static void reg_name(int regnum, char size) +{ + if (size == 'F') { /* floating point register? */ + uprintf("st(%d)", regnum); + return; + } + if ((((size == 'c') || (size == 'v')) && (opsize == 32)) || (size == 'd')) + uputchar('e'); + if ((size=='q' || size == 'b' || size=='c') && !wordop) { + uputchar("acdbacdb"[regnum]); + uputchar("llllhhhh"[regnum]); + } else { + uputchar("acdbsbsd"[regnum]); + uputchar("xxxxppii"[regnum]); + } +} + + +/*------------------------------------------------------------------------*/ + +static void ua_str(const char *str); + +static void do_sib(int m) +{ + int s, i, b; + + s = SCALE(sib()); + i = INDEX(sib()); + b = BASE(sib()); + switch (b) { /* pick base */ + case 0: ua_str("%p:[eax"); break; + case 1: ua_str("%p:[ecx"); break; + case 2: ua_str("%p:[edx"); break; + case 3: ua_str("%p:[ebx"); break; + case 4: ua_str("%p:[esp"); break; + case 5: + if (m == 0) { + ua_str("%p:["); + outhex('d', 4, 0, addrsize, 0); + } else { + ua_str("%p:[ebp"); + } + break; + case 6: ua_str("%p:[esi"); break; + case 7: ua_str("%p:[edi"); break; + } + switch (i) { /* and index */ + case 0: uprintf("+eax"); break; + case 1: uprintf("+ecx"); break; + case 2: uprintf("+edx"); break; + case 3: uprintf("+ebx"); break; + case 4: break; + case 5: uprintf("+ebp"); break; + case 6: uprintf("+esi"); break; + case 7: uprintf("+edi"); break; + } + if (i != 4) { + switch (s) { /* and scale */ + case 0: /*uprintf("");*/ break; + case 1: uprintf("*2"); break; + case 2: uprintf("*4"); break; + case 3: uprintf("*8"); break; + } + } +} + + + +/*------------------------------------------------------------------------*/ +static void do_modrm(char subtype) +{ + int mod = MOD(modrm()); + int rm = RM(modrm()); + int extend = (addrsize == 32) ? 4 : 2; + + if (mod == 3) { /* specifies two registers */ + reg_name(rm, subtype); + return; + } + if (must_do_size) { + if (wordop) { + if (opsize==32) { /* then must specify size */ + ua_str("dword "); + } else { + ua_str("word "); + } + } else { + ua_str("byte "); + } + } + if ((mod == 0) && (rm == 5) && (addrsize == 32)) {/* mem operand with 32 bit ofs */ + ua_str("%p:["); + outhex('d', extend, 0, addrsize, 0); + uputchar(']'); + return; + } + if ((mod == 0) && (rm == 6) && (addrsize == 16)) { /* 16 bit dsplcmnt */ + ua_str("%p:["); + outhex('w', extend, 0, addrsize, 0); + uputchar(']'); + return; + } + if ((addrsize != 32) || (rm != 4)) + ua_str("%p:["); + if (addrsize == 16) { + switch (rm) { + case 0: uprintf("bx+si"); break; + case 1: uprintf("bx+di"); break; + case 2: uprintf("bp+si"); break; + case 3: uprintf("bp+di"); break; + case 4: uprintf("si"); break; + case 5: uprintf("di"); break; + case 6: uprintf("bp"); break; + case 7: uprintf("bx"); break; + } + } else { + switch (rm) { + case 0: uprintf("eax"); break; + case 1: uprintf("ecx"); break; + case 2: uprintf("edx"); break; + case 3: uprintf("ebx"); break; + case 4: do_sib(mod); break; + case 5: uprintf("ebp"); break; + case 6: uprintf("esi"); break; + case 7: uprintf("edi"); break; + } + } + switch (mod) { + case 1: + outhex('b', extend, 1, addrsize, 0); + break; + case 2: + outhex('v', extend, 1, addrsize, 1); + break; + } + uputchar(']'); +} + + + +/*------------------------------------------------------------------------*/ +static void floating_point(int e1) +{ + int esc = e1*8 + REG(modrm()); + + if ((MOD(modrm()) == 3)&&fspecial[esc]) { + if (fspecial[esc][0]) { + if (fspecial[esc][0][0] == '*') { + ua_str(fspecial[esc][0]+1); + } else { + ua_str(fspecial[esc][RM(modrm())]); + } + } else { + ua_str(floatops[esc]); + ua_str(" %EF"); + } + } else { + ua_str(floatops[esc]); + ua_str(" %EF"); + } +} + + +/*------------------------------------------------------------------------*/ +/* Main table driver */ + +#define INSTRUCTION_SIZE (getbyte_mac - startPtr) + +static void percent(char type, char subtype) +{ + INT32 vofs = 0; + char *name=NULL; + int extend = (addrsize == 32) ? 4 : 2; + UINT8 c; + + switch (type) { + case 'A': /* direct address */ + outhex(subtype, extend, 0, addrsize, 0); + break; + + case 'C': /* reg(r/m) picks control reg */ + uprintf("CR%d", REG(modrm())); + must_do_size = 0; + break; + + case 'D': /* reg(r/m) picks debug reg */ + uprintf("DR%d", REG(modrm())); + must_do_size = 0; + break; + + case 'E': /* r/m picks operand */ + do_modrm(subtype); + break; + + case 'G': /* reg(r/m) picks register */ + if (subtype == 'F') /* 80*87 operand? */ + reg_name(RM(modrm()), subtype); + else + reg_name(REG(modrm()), subtype); + must_do_size = 0; + break; + + case 'I': /* immed data */ + outhex(subtype, 0, 0, opsize, 0); + break; + + case 'J': /* relative IP offset */ + switch (bytes(subtype)) { /* sizeof offset value */ + case 1: + vofs = (INT8)getbyte(); + name = addr_to_hex(vofs+instruction_offset+INSTRUCTION_SIZE); + break; + case 2: + vofs = getbyte(); + vofs += getbyte()<<8; + vofs = (INT16)vofs; + name = addr_to_hex(vofs+instruction_offset+INSTRUCTION_SIZE); + break; + /* i386 */ + case 4: + vofs = (UINT32)getbyte(); /* yuk! */ + vofs |= (UINT32)getbyte() << 8; + vofs |= (UINT32)getbyte() << 16; + vofs |= (UINT32)getbyte() << 24; + name = addr_to_hex(vofs+instruction_offset+INSTRUCTION_SIZE); + break; + } + if (vofs<0) + uprintf("%s ($-%x)", name, -vofs); + else + uprintf("%s ($+%x)", name, vofs); + break; + + case 'K': + switch (subtype) { + case 'f': + ua_str("far "); + break; + case 'n': + ua_str("near "); + break; + case 's': + ua_str("short "); + break; + } + break; + + case 'M': /* r/m picks memory */ + do_modrm(subtype); + break; + + case 'O': /* offset only */ + ua_str("%p:["); + outhex(subtype, extend, 0, addrsize, 0); + uputchar(']'); + break; + + case 'P': /* prefix byte (rh) */ + ua_str("%p:"); + break; + + case 'R': /* mod(r/m) picks register */ + reg_name(RM(modrm()), subtype); /* rh */ + must_do_size = 0; + break; + + case 'S': /* reg(r/m) picks segment reg */ + uputchar("ecsdfg"[REG(modrm())]); + uputchar('s'); + must_do_size = 0; + break; + + case 'T': /* reg(r/m) picks T reg */ + uprintf("tr%d", REG(modrm())); + must_do_size = 0; + break; + + case 'X': /* ds:si type operator */ + uprintf("ds:["); + if (addrsize == 32) + uputchar('e'); + uprintf("si]"); + break; + + case 'Y': /* es:di type operator */ + uprintf("es:["); + if (addrsize == 32) + uputchar('e'); + uprintf("di]"); + break; + + case '2': /* old [pop cs]! now indexes */ + ua_str(second[getbyte()]); /* instructions in 386/486 */ + break; + + case 'g': /* modrm group `subtype' (0--7) */ + ua_str(groups[subtype-'0'][REG(modrm())]); + break; + + case 'd': /* sizeof operand==dword? */ + if (opsize == 32) + uputchar('d'); + uputchar(subtype); + break; + + case 'w': /* insert explicit size specifier */ + if (opsize == 32) + uputchar('d'); + else + uputchar('w'); + uputchar(subtype); + break; + + case 'e': /* extended reg name */ + if (opsize == 32) { + if (subtype == 'w') + uputchar('d'); + else { + uputchar('e'); + uputchar(subtype); + } + } else + uputchar(subtype); + break; + + case 'f': /* '87 opcode */ + floating_point(subtype-'0'); + break; + + case 'j': + if (addrsize==32 || opsize==32) /* both of them?! */ + uputchar('e'); + break; + + case 'p': /* prefix byte */ + switch (subtype) { + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 's': + prefix = subtype; + c = getbyte(); + wordop = c & 1; + ua_str((*opmap1)[c]); + break; + case ':': + if (prefix) + uprintf("%cs:", prefix); + break; + case ' ': + c = getbyte(); + wordop = c & 1; + ua_str((*opmap1)[c]); + break; + } + break; + + case 's': /* size override */ + switch (subtype) { + case 'a': + addrsize = 48 - addrsize; + c = getbyte(); + wordop = c & 1; + ua_str((*opmap1)[c]); +/* ua_str(opmap1[getbyte()]); */ + break; + case 'o': + opsize = 48 - opsize; + c = getbyte(); + wordop = c & 1; + ua_str((*opmap1)[c]); +/* ua_str(opmap1[getbyte()]); */ + break; + } + break; + } +} + + +static void ua_str(const char *str) +{ + char c; + + if (str == 0) { + invalid_opcode = 1; + uprintf("?"); + return; + } + + if (strpbrk(str, "CDFGRST")) /* specifiers for registers=>no size 2b specified */ + must_do_size = 0; + + while ((c = *str++) != 0) { + if (c == ' ' && first_space) + { + first_space = 0; + do + { + uputchar(' '); + } while ( (int)(ubufp - ubufs) < 5 ); + } + else + if (c == '%') { + c = *str++; + if (!c) + break; + percent(c, *str); + if (!*str) + break; + str++; + } else { + uputchar(c); + } + } +} + + +static Bitu DasmI386(char* buffer, PhysPt pc, Bitu cur_ip, bool bit32) +{ + Bitu c; + + + instruction_offset = cur_ip; + /* input buffer */ + startPtr = pc; + getbyte_mac = pc; + + /* output buffer */ + ubufs = buffer; + ubufp = buffer; + first_space = 1; + + addr32bit=1; + + prefix = 0; + modrmv = sibv = -1; /* set modrm and sib flags */ + if (bit32) opsize = addrsize = 32; + else opsize = addrsize = 16; + c = getbyte(); + wordop = c & 1; + must_do_size = 1; + invalid_opcode = 0; + opmap1=&op386map1; + ua_str(op386map1[c]); + + if (invalid_opcode) { + /* restart output buffer */ + ubufp = buffer; + /* invalid instruction, use db xx */ + uprintf("db %02X", (unsigned)c); + return 1; + } + + return getbyte_mac-pc; +} + +int dis_8086(unsigned int code, + char *outbuf, + int def_size, + unsigned int * refof, + unsigned int refsegbase) +{ + int rc; + refoff = 0; + disasunix = def_size & 4; + rc = DasmI386(outbuf, code, code - refsegbase, def_size & ~4); + *refof = refsegbase + refoff; + return rc; +} diff --git a/src/base/lib/misc/dlmalloc.c b/src/base/lib/misc/dlmalloc.c new file mode 100644 index 0000000..133a3f9 --- /dev/null +++ b/src/base/lib/misc/dlmalloc.c @@ -0,0 +1,4131 @@ +/* + This is a version (aka dlmalloc) of malloc/free/realloc written by + Doug Lea and released to the public domain, as explained at + http://creativecommons.org/licenses/publicdomain. Send questions, + comments, complaints, performance data, etc to dl@cs.oswego.edu + + The only change for DOSEMU is to use PROT_EXEC, for use in cpuemu, + with DL_PREFIX set, and HAVE_MORECORE 0; a #if for MORECORE_CONTIGUOUS + was added to avoid a GCC warning -- Bart + +* Version 2.8.3 Thu Sep 22 11:16:15 2005 Doug Lea (dl at gee) + + Note: There may be an updated version of this malloc obtainable at + ftp://gee.cs.oswego.edu/pub/misc/malloc.c + Check before installing! + +* Quickstart + + This library is all in one file to simplify the most common usage: + ftp it, compile it (-O3), and link it into another program. All of + the compile-time options default to reasonable values for use on + most platforms. You might later want to step through various + compile-time and dynamic tuning options. + + For convenience, an include file for code using this malloc is at: + ftp://gee.cs.oswego.edu/pub/misc/malloc-2.8.3.h + You don't really need this .h file unless you call functions not + defined in your system include files. The .h file contains only the + excerpts from this file needed for using this malloc on ANSI C/C++ + systems, so long as you haven't changed compile-time options about + naming and tuning parameters. If you do, then you can create your + own malloc.h that does include all settings by cutting at the point + indicated below. Note that you may already by default be using a C + library containing a malloc that is based on some version of this + malloc (for example in linux). You might still want to use the one + in this file to customize settings or to avoid overheads associated + with library versions. + +* Vital statistics: + + Supported pointer/size_t representation: 4 or 8 bytes + size_t MUST be an unsigned type of the same width as + pointers. (If you are using an ancient system that declares + size_t as a signed type, or need it to be a different width + than pointers, you can use a previous release of this malloc + (e.g. 2.7.2) supporting these.) + + Alignment: 8 bytes (default) + This suffices for nearly all current machines and C compilers. + However, you can define MALLOC_ALIGNMENT to be wider than this + if necessary (up to 128bytes), at the expense of using more space. + + Minimum overhead per allocated chunk: 4 or 8 bytes (if 4byte sizes) + 8 or 16 bytes (if 8byte sizes) + Each malloced chunk has a hidden word of overhead holding size + and status information, and additional cross-check word + if FOOTERS is defined. + + Minimum allocated size: 4-byte ptrs: 16 bytes (including overhead) + 8-byte ptrs: 32 bytes (including overhead) + + Even a request for zero bytes (i.e., malloc(0)) returns a + pointer to something of the minimum allocatable size. + The maximum overhead wastage (i.e., number of extra bytes + allocated than were requested in malloc) is less than or equal + to the minimum size, except for requests >= mmap_threshold that + are serviced via mmap(), where the worst case wastage is about + 32 bytes plus the remainder from a system page (the minimal + mmap unit); typically 4096 or 8192 bytes. + + Security: static-safe; optionally more or less + The "security" of malloc refers to the ability of malicious + code to accentuate the effects of errors (for example, freeing + space that is not currently malloc'ed or overwriting past the + ends of chunks) in code that calls malloc. This malloc + guarantees not to modify any memory locations below the base of + heap, i.e., static variables, even in the presence of usage + errors. The routines additionally detect most improper frees + and reallocs. All this holds as long as the static bookkeeping + for malloc itself is not corrupted by some other means. This + is only one aspect of security -- these checks do not, and + cannot, detect all possible programming errors. + + If FOOTERS is defined nonzero, then each allocated chunk + carries an additional check word to verify that it was malloced + from its space. These check words are the same within each + execution of a program using malloc, but differ across + executions, so externally crafted fake chunks cannot be + freed. This improves security by rejecting frees/reallocs that + could corrupt heap memory, in addition to the checks preventing + writes to statics that are always on. This may further improve + security at the expense of time and space overhead. (Note that + FOOTERS may also be worth using with MSPACES.) + + By default detected errors cause the program to abort (calling + "abort()"). You can override this to instead proceed past + errors by defining PROCEED_ON_ERROR. In this case, a bad free + has no effect, and a malloc that encounters a bad address + caused by user overwrites will ignore the bad address by + dropping pointers and indices to all known memory. This may + be appropriate for programs that should continue if at all + possible in the face of programming errors, although they may + run out of memory because dropped memory is never reclaimed. + + If you don't like either of these options, you can define + CORRUPTION_ERROR_ACTION and USAGE_ERROR_ACTION to do anything + else. And if if you are sure that your program using malloc has + no errors or vulnerabilities, you can define INSECURE to 1, + which might (or might not) provide a small performance improvement. + + Thread-safety: NOT thread-safe unless USE_LOCKS defined + When USE_LOCKS is defined, each public call to malloc, free, + etc is surrounded with either a pthread mutex or a win32 + spinlock (depending on WIN32). This is not especially fast, and + can be a major bottleneck. It is designed only to provide + minimal protection in concurrent environments, and to provide a + basis for extensions. If you are using malloc in a concurrent + program, consider instead using ptmalloc, which is derived from + a version of this malloc. (See http://www.malloc.de). + + System requirements: Any combination of MORECORE and/or MMAP/MUNMAP + This malloc can use unix sbrk or any emulation (invoked using + the CALL_MORECORE macro) and/or mmap/munmap or any emulation + (invoked using CALL_MMAP/CALL_MUNMAP) to get and release system + memory. On most unix systems, it tends to work best if both + MORECORE and MMAP are enabled. On Win32, it uses emulations + based on VirtualAlloc. It also uses common C library functions + like memset. + + Compliance: I believe it is compliant with the Single Unix Specification + (See http://www.unix.org). Also SVID/XPG, ANSI C, and probably + others as well. + +* Overview of algorithms + + This is not the fastest, most space-conserving, most portable, or + most tunable malloc ever written. However it is among the fastest + while also being among the most space-conserving, portable and + tunable. Consistent balance across these factors results in a good + general-purpose allocator for malloc-intensive programs. + + In most ways, this malloc is a best-fit allocator. Generally, it + chooses the best-fitting existing chunk for a request, with ties + broken in approximately least-recently-used order. (This strategy + normally maintains low fragmentation.) However, for requests less + than 256bytes, it deviates from best-fit when there is not an + exactly fitting available chunk by preferring to use space adjacent + to that used for the previous small request, as well as by breaking + ties in approximately most-recently-used order. (These enhance + locality of series of small allocations.) And for very large requests + (>= 256Kb by default), it relies on system memory mapping + facilities, if supported. (This helps avoid carrying around and + possibly fragmenting memory used only for large chunks.) + + All operations (except malloc_stats and mallinfo) have execution + times that are bounded by a constant factor of the number of bits in + a size_t, not counting any clearing in calloc or copying in realloc, + or actions surrounding MORECORE and MMAP that have times + proportional to the number of non-contiguous regions returned by + system allocation routines, which is often just 1. + + The implementation is not very modular and seriously overuses + macros. Perhaps someday all C compilers will do as good a job + inlining modular code as can now be done by brute-force expansion, + but now, enough of them seem not to. + + Some compilers issue a lot of warnings about code that is + dead/unreachable only on some platforms, and also about intentional + uses of negation on unsigned types. All known cases of each can be + ignored. + + For a longer but out of date high-level description, see + http://gee.cs.oswego.edu/dl/html/malloc.html + +* MSPACES + If MSPACES is defined, then in addition to malloc, free, etc., + this file also defines mspace_malloc, mspace_free, etc. These + are versions of malloc routines that take an "mspace" argument + obtained using create_mspace, to control all internal bookkeeping. + If ONLY_MSPACES is defined, only these versions are compiled. + So if you would like to use this allocator for only some allocations, + and your system malloc for others, you can compile with + ONLY_MSPACES and then do something like... + static mspace mymspace = create_mspace(0,0); // for example + #define mymalloc(bytes) mspace_malloc(mymspace, bytes) + + (Note: If you only need one instance of an mspace, you can instead + use "USE_DL_PREFIX" to relabel the global malloc.) + + You can similarly create thread-local allocators by storing + mspaces as thread-locals. For example: + static __thread mspace tlms = 0; + void* tlmalloc(size_t bytes) { + if (tlms == 0) tlms = create_mspace(0, 0); + return mspace_malloc(tlms, bytes); + } + void tlfree(void* mem) { mspace_free(tlms, mem); } + + Unless FOOTERS is defined, each mspace is completely independent. + You cannot allocate from one and free to another (although + conformance is only weakly checked, so usage errors are not always + caught). If FOOTERS is defined, then each chunk carries around a tag + indicating its originating mspace, and frees are directed to their + originating spaces. +*/ + +#include "dlmalloc.h" + +/*------------------------------ internal #includes ---------------------- */ + +#ifdef WIN32 +#pragma warning( disable : 4146 ) /* no "unsigned" warnings */ +#endif /* WIN32 */ + +#include /* for printing in malloc_stats */ + +#ifndef LACKS_ERRNO_H +#include /* for MALLOC_FAILURE_ACTION */ +#endif /* LACKS_ERRNO_H */ +#if FOOTERS +#include /* for magic initialization */ +#endif /* FOOTERS */ +#ifndef LACKS_STDLIB_H +#include /* for abort() */ +#endif /* LACKS_STDLIB_H */ +#ifdef DEBUG +#if ABORT_ON_ASSERT_FAILURE +#define assert(x) if(!(x)) ABORT +#else /* ABORT_ON_ASSERT_FAILURE */ +#include +#endif /* ABORT_ON_ASSERT_FAILURE */ +#else /* DEBUG */ +#define assert(x) +#endif /* DEBUG */ +#ifndef LACKS_STRING_H +#include /* for memset etc */ +#endif /* LACKS_STRING_H */ +#if USE_BUILTIN_FFS +#ifndef LACKS_STRINGS_H +#include /* for ffs */ +#endif /* LACKS_STRINGS_H */ +#endif /* USE_BUILTIN_FFS */ +#if HAVE_MMAP +#ifndef LACKS_SYS_MMAN_H +#include /* for mmap */ +#endif /* LACKS_SYS_MMAN_H */ +#ifndef LACKS_FCNTL_H +#include +#endif /* LACKS_FCNTL_H */ +#endif /* HAVE_MMAP */ +#if HAVE_MORECORE +#ifndef LACKS_UNISTD_H +#include /* for sbrk */ +#else /* LACKS_UNISTD_H */ +#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__) +extern void* sbrk(ptrdiff_t); +#endif /* FreeBSD etc */ +#endif /* LACKS_UNISTD_H */ +#endif /* HAVE_MMAP */ + +#ifndef WIN32 +#ifndef malloc_getpagesize +# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ +# ifndef _SC_PAGE_SIZE +# define _SC_PAGE_SIZE _SC_PAGESIZE +# endif +# endif +# ifdef _SC_PAGE_SIZE +# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# else +# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) + extern size_t getpagesize(); +# define malloc_getpagesize getpagesize() +# else +# ifdef WIN32 /* use supplied emulation of getpagesize */ +# define malloc_getpagesize getpagesize() +# else +# ifndef LACKS_SYS_PARAM_H +# include +# endif +# ifdef EXEC_PAGESIZE +# define malloc_getpagesize EXEC_PAGESIZE +# else +# ifdef NBPG +# ifndef CLSIZE +# define malloc_getpagesize NBPG +# else +# define malloc_getpagesize (NBPG * CLSIZE) +# endif +# else +# ifdef NBPC +# define malloc_getpagesize NBPC +# else +# ifdef PAGESIZE +# define malloc_getpagesize PAGESIZE +# else /* just guess */ +# define malloc_getpagesize ((size_t)4096U) +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#endif +#endif + +/* ------------------- size_t and alignment properties -------------------- */ + +/* The byte and bit size of a size_t */ +#define SIZE_T_SIZE (sizeof(size_t)) +#define SIZE_T_BITSIZE (sizeof(size_t) << 3) + +/* Some constants coerced to size_t */ +/* Annoying but necessary to avoid errors on some platforms */ +#define SIZE_T_ZERO ((size_t)0) +#define SIZE_T_ONE ((size_t)1) +#define SIZE_T_TWO ((size_t)2) +#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) +#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) +#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) +#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) + +/* The bit mask value corresponding to MALLOC_ALIGNMENT */ +#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) + +/* True if address a has acceptable alignment */ +#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) + +/* the number of bytes to offset an address to align it */ +#define align_offset(A)\ + ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ + ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) + +/* -------------------------- MMAP preliminaries ------------------------- */ + +/* + If HAVE_MORECORE or HAVE_MMAP are false, we just define calls and + checks to fail so compiler optimizer can delete code rather than + using so many "#if"s. +*/ + + +/* MORECORE and MMAP must return MFAIL on failure */ +#define MFAIL ((void*)(MAX_SIZE_T)) +#define CMFAIL ((char*)(MFAIL)) /* defined for convenience */ + +#if !HAVE_MMAP +#define IS_MMAPPED_BIT (SIZE_T_ZERO) +#define USE_MMAP_BIT (SIZE_T_ZERO) +#define CALL_MMAP(s) MFAIL +#define CALL_MUNMAP(a, s) (-1) +#define DIRECT_MMAP(s) MFAIL + +#else /* HAVE_MMAP */ +#define IS_MMAPPED_BIT (SIZE_T_ONE) +#define USE_MMAP_BIT (SIZE_T_ONE) + +#ifndef WIN32 +#define CALL_MUNMAP(a, s) munmap((a), (s)) +#define MMAP_PROT (PROT_READ|PROT_WRITE|PROT_EXEC) +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif /* MAP_ANON */ +#ifdef MAP_ANONYMOUS +#define MMAP_FLAGS (MAP_PRIVATE|MAP_ANONYMOUS) +#define CALL_MMAP(s) mmap(0, (s), MMAP_PROT, MMAP_FLAGS, -1, 0) +#else /* MAP_ANONYMOUS */ +/* + Nearly all versions of mmap support MAP_ANONYMOUS, so the following + is unlikely to be needed, but is supplied just in case. +*/ +#define MMAP_FLAGS (MAP_PRIVATE) +static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */ +#define CALL_MMAP(s) ((dev_zero_fd < 0) ? \ + (dev_zero_fd = open("/dev/zero", O_RDWR), \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) : \ + mmap(0, (s), MMAP_PROT, MMAP_FLAGS, dev_zero_fd, 0)) +#endif /* MAP_ANONYMOUS */ + +#define DIRECT_MMAP(s) CALL_MMAP(s) +#else /* WIN32 */ + +/* Win32 MMAP via VirtualAlloc */ +static void* win32mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; +} + +/* For direct MMAP, use MEM_TOP_DOWN to minimize interference */ +static void* win32direct_mmap(size_t size) { + void* ptr = VirtualAlloc(0, size, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, + PAGE_READWRITE); + return (ptr != 0)? ptr: MFAIL; +} + +/* This function supports releasing coalesced segments */ +static int win32munmap(void* ptr, size_t size) { + MEMORY_BASIC_INFORMATION minfo; + char* cptr = ptr; + while (size) { + if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0) + return -1; + if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr || + minfo.State != MEM_COMMIT || minfo.RegionSize > size) + return -1; + if (VirtualFree(cptr, 0, MEM_RELEASE) == 0) + return -1; + cptr += minfo.RegionSize; + size -= minfo.RegionSize; + } + return 0; +} + +#define CALL_MMAP(s) win32mmap(s) +#define CALL_MUNMAP(a, s) win32munmap((a), (s)) +#define DIRECT_MMAP(s) win32direct_mmap(s) +#endif /* WIN32 */ +#endif /* HAVE_MMAP */ + +#if HAVE_MMAP && HAVE_MREMAP +#define CALL_MREMAP(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv)) +#else /* HAVE_MMAP && HAVE_MREMAP */ +#define CALL_MREMAP(addr, osz, nsz, mv) MFAIL +#endif /* HAVE_MMAP && HAVE_MREMAP */ + +#if HAVE_MORECORE +#define CALL_MORECORE(S) MORECORE(S) +#else /* HAVE_MORECORE */ +#define CALL_MORECORE(S) MFAIL +#endif /* HAVE_MORECORE */ + +/* mstate bit set if contiguous morecore disabled or failed */ +#define USE_NONCONTIGUOUS_BIT (4U) + +/* segment bit set in create_mspace_with_base */ +#define EXTERN_BIT (8U) + + +/* --------------------------- Lock preliminaries ------------------------ */ + +#if USE_LOCKS + +/* + When locks are defined, there are up to two global locks: + + * If HAVE_MORECORE, morecore_mutex protects sequences of calls to + MORECORE. In many cases sys_alloc requires two calls, that should + not be interleaved with calls by other threads. This does not + protect against direct calls to MORECORE by other threads not + using this lock, so there is still code to cope the best we can on + interference. + + * magic_init_mutex ensures that mparams.magic and other + unique mparams values are initialized only once. +*/ + +#ifndef WIN32 +/* By default use posix locks */ +#include +#define MLOCK_T pthread_mutex_t +#define INITIAL_LOCK(l) pthread_mutex_init(l, NULL) +#define ACQUIRE_LOCK(l) pthread_mutex_lock(l) +#define RELEASE_LOCK(l) pthread_mutex_unlock(l) + +#if HAVE_MORECORE +static MLOCK_T morecore_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif /* HAVE_MORECORE */ + +static MLOCK_T magic_init_mutex = PTHREAD_MUTEX_INITIALIZER; + +#else /* WIN32 */ +/* + Because lock-protected regions have bounded times, and there + are no recursive lock calls, we can use simple spinlocks. +*/ + +#define MLOCK_T long +static int win32_acquire_lock (MLOCK_T *sl) { + for (;;) { +#ifdef InterlockedCompareExchangePointer + if (!InterlockedCompareExchange(sl, 1, 0)) + return 0; +#else /* Use older void* version */ + if (!InterlockedCompareExchange((void**)sl, (void*)1, (void*)0)) + return 0; +#endif /* InterlockedCompareExchangePointer */ + Sleep (0); + } +} + +static void win32_release_lock (MLOCK_T *sl) { + InterlockedExchange (sl, 0); +} + +#define INITIAL_LOCK(l) *(l)=0 +#define ACQUIRE_LOCK(l) win32_acquire_lock(l) +#define RELEASE_LOCK(l) win32_release_lock(l) +#if HAVE_MORECORE +static MLOCK_T morecore_mutex; +#endif /* HAVE_MORECORE */ +static MLOCK_T magic_init_mutex; +#endif /* WIN32 */ + +#define USE_LOCK_BIT (2U) +#else /* USE_LOCKS */ +#define USE_LOCK_BIT (0U) +#define INITIAL_LOCK(l) +#endif /* USE_LOCKS */ + +#if USE_LOCKS && HAVE_MORECORE +#define ACQUIRE_MORECORE_LOCK() ACQUIRE_LOCK(&morecore_mutex); +#define RELEASE_MORECORE_LOCK() RELEASE_LOCK(&morecore_mutex); +#else /* USE_LOCKS && HAVE_MORECORE */ +#define ACQUIRE_MORECORE_LOCK() +#define RELEASE_MORECORE_LOCK() +#endif /* USE_LOCKS && HAVE_MORECORE */ + +#if USE_LOCKS +#define ACQUIRE_MAGIC_INIT_LOCK() ACQUIRE_LOCK(&magic_init_mutex); +#define RELEASE_MAGIC_INIT_LOCK() RELEASE_LOCK(&magic_init_mutex); +#else /* USE_LOCKS */ +#define ACQUIRE_MAGIC_INIT_LOCK() +#define RELEASE_MAGIC_INIT_LOCK() +#endif /* USE_LOCKS */ + + +/* ----------------------- Chunk representations ------------------------ */ + +/* + (The following includes lightly edited explanations by Colin Plumb.) + + The malloc_chunk declaration below is misleading (but accurate and + necessary). It declares a "view" into memory allowing access to + necessary fields at known offsets from a given base. + + Chunks of memory are maintained using a `boundary tag' method as + originally described by Knuth. (See the paper by Paul Wilson + ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such + techniques.) Sizes of free chunks are stored both in the front of + each chunk and at the end. This makes consolidating fragmented + chunks into bigger chunks fast. The head fields also hold bits + representing whether chunks are free or in use. + + Here are some pictures to make it clearer. They are "exploded" to + show that the state of a chunk can be thought of as extending from + the high 31 bits of the head field of its header through the + prev_foot and PINUSE_BIT bit of the following chunk header. + + A chunk that's in use looks like: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk (if P = 1) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 1| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +- -+ + | | + +- -+ + | : + +- size - sizeof(size_t) available payload bytes -+ + : | + chunk-> +- -+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| + | Size of next chunk (may or may not be in use) | +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + And if it's free, it looks like this: + + chunk-> +- -+ + | User payload (must be in use, or we would have merged!) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 0| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Prev pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | : + +- size - sizeof(struct chunk) unused bytes -+ + : | + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| + | Size of next chunk (must be in use, or we would have merged)| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | : + +- User payload -+ + : | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |0| + +-+ + Note that since we always merge adjacent free chunks, the chunks + adjacent to a free chunk must be in use. + + Given a pointer to a chunk (which can be derived trivially from the + payload pointer) we can, in O(1) time, find out whether the adjacent + chunks are free, and if so, unlink them from the lists that they + are on and merge them with the current chunk. + + Chunks always begin on even word boundaries, so the mem portion + (which is returned to the user) is also on an even word boundary, and + thus at least double-word aligned. + + The P (PINUSE_BIT) bit, stored in the unused low-order bit of the + chunk size (which is always a multiple of two words), is an in-use + bit for the *previous* chunk. If that bit is *clear*, then the + word before the current chunk size contains the previous chunk + size, and can be used to find the front of the previous chunk. + The very first chunk allocated always has this bit set, preventing + access to non-existent (or non-owned) memory. If pinuse is set for + any given chunk, then you CANNOT determine the size of the + previous chunk, and might even get a memory addressing fault when + trying to do so. + + The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of + the chunk size redundantly records whether the current chunk is + inuse. This redundancy enables usage checks within free and realloc, + and reduces indirection when freeing and consolidating chunks. + + Each freshly allocated chunk must have both cinuse and pinuse set. + That is, each allocated chunk borders either a previously allocated + and still in-use chunk, or the base of its memory arena. This is + ensured by making all allocations from the the `lowest' part of any + found chunk. Further, no free chunk physically borders another one, + so each free chunk is known to be preceded and followed by either + inuse chunks or the ends of memory. + + Note that the `foot' of the current chunk is actually represented + as the prev_foot of the NEXT chunk. This makes it easier to + deal with alignments etc but can be very confusing when trying + to extend or adapt this code. + + The exceptions to all this are + + 1. The special chunk `top' is the top-most available chunk (i.e., + the one bordering the end of available memory). It is treated + specially. Top is never included in any bin, is used only if + no other chunk is available, and is released back to the + system if it is very large (see M_TRIM_THRESHOLD). In effect, + the top chunk is treated as larger (and thus less well + fitting) than any other available chunk. The top chunk + doesn't update its trailing size field since there is no next + contiguous chunk that would have to index off it. However, + space is still allocated for it (TOP_FOOT_SIZE) to enable + separation or merging when space is extended. + + 3. Chunks allocated via mmap, which have the lowest-order bit + (IS_MMAPPED_BIT) set in their prev_foot fields, and do not set + PINUSE_BIT in their head fields. Because they are allocated + one-by-one, each must carry its own prev_foot field, which is + also used to hold the offset this chunk has within its mmapped + region, which is needed to preserve alignment. Each mmapped + chunk is trailed by the first two fields of a fake next-chunk + for sake of usage checks. + +*/ + +struct malloc_chunk { + size_t prev_foot; /* Size of previous chunk (if free). */ + size_t head; /* Size and inuse bits. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +}; + +typedef struct malloc_chunk mchunk; +typedef struct malloc_chunk* mchunkptr; +typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ +typedef unsigned int bindex_t; /* Described below */ +typedef unsigned int binmap_t; /* Described below */ +typedef unsigned int flag_t; /* The type of various bit flag sets */ + +/* ------------------- Chunks sizes and alignments ----------------------- */ + +#define MCHUNK_SIZE (sizeof(mchunk)) + +#if FOOTERS +#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +#else /* FOOTERS */ +#define CHUNK_OVERHEAD (SIZE_T_SIZE) +#endif /* FOOTERS */ + +/* MMapped chunks need a second word of overhead ... */ +#define MMAP_CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +/* ... and additional padding for fake next-chunk at foot */ +#define MMAP_FOOT_PAD (FOUR_SIZE_T_SIZES) + +/* The smallest size we can malloc is an aligned minimal chunk */ +#define MIN_CHUNK_SIZE\ + ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* conversion from malloc headers to user pointers, and back */ +#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) +/* chunk associated with aligned address A */ +#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) + +/* Bounds on request (not chunk) sizes. */ +#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) +#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) + +/* pad request bytes into a usable size */ +#define pad_request(req) \ + (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* pad request, checking for minimum (but not maximum) */ +#define request2size(req) \ + (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) + + +/* ------------------ Operations on head and foot fields ----------------- */ + +/* + The head field of a chunk is or'ed with PINUSE_BIT when previous + adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in + use. If the chunk was obtained with mmap, the prev_foot field has + IS_MMAPPED_BIT set, otherwise holding the offset of the base of the + mmapped region to the base of the chunk. +*/ + +#define PINUSE_BIT (SIZE_T_ONE) +#define CINUSE_BIT (SIZE_T_TWO) +#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) + +/* Head value for fenceposts */ +#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) + +/* extraction of fields from head words */ +#define cinuse(p) ((p)->head & CINUSE_BIT) +#define pinuse(p) ((p)->head & PINUSE_BIT) +#define chunksize(p) ((p)->head & ~(INUSE_BITS)) + +#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) +#define clear_cinuse(p) ((p)->head &= ~CINUSE_BIT) + +/* Treat space at ptr +/- offset as a chunk */ +#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) +#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) + +/* Ptr to next or previous physical malloc_chunk. */ +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~INUSE_BITS))) +#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) + +/* extract next chunk's pinuse bit */ +#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) + +/* Get/set size at footer */ +#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) + +/* Set size, pinuse bit, and foot */ +#define set_size_and_pinuse_of_free_chunk(p, s)\ + ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) + +/* Set size, pinuse bit, foot, and clear next pinuse */ +#define set_free_with_pinuse(p, s, n)\ + (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) + +#define is_mmapped(p)\ + (!((p)->head & PINUSE_BIT) && ((p)->prev_foot & IS_MMAPPED_BIT)) + +/* Get the internal overhead associated with chunk p */ +#define overhead_for(p)\ + (is_mmapped(p)? MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD) + +/* Return true if malloced space is not necessarily cleared */ +#if MMAP_CLEARS +#define calloc_must_clear(p) (!is_mmapped(p)) +#else /* MMAP_CLEARS */ +#define calloc_must_clear(p) (1) +#endif /* MMAP_CLEARS */ + +/* ---------------------- Overlaid data structures ----------------------- */ + +/* + When chunks are not in use, they are treated as nodes of either + lists or trees. + + "Small" chunks are stored in circular doubly-linked lists, and look + like this: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Larger chunks are kept in a form of bitwise digital trees (aka + tries) keyed on chunksizes. Because malloc_tree_chunks are only for + free chunks greater than 256 bytes, their size doesn't impose any + constraints on user chunk sizes. Each node looks like: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to left child (child[0]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to right child (child[1]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to parent | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | bin index of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Each tree holding treenodes is a tree of unique chunk sizes. Chunks + of the same size are arranged in a circularly-linked list, with only + the oldest chunk (the next to be used, in our FIFO ordering) + actually in the tree. (Tree members are distinguished by a non-null + parent pointer.) If a chunk with the same size an an existing node + is inserted, it is linked off the existing node using pointers that + work in the same way as fd/bk pointers of small chunks. + + Each tree contains a power of 2 sized range of chunk sizes (the + smallest is 0x100 <= x < 0x180), which is is divided in half at each + tree level, with the chunks in the smaller half of the range (0x100 + <= x < 0x140 for the top nose) in the left subtree and the larger + half (0x140 <= x < 0x180) in the right subtree. This is, of course, + done by inspecting individual bits. + + Using these rules, each node's left subtree contains all smaller + sizes than its right subtree. However, the node at the root of each + subtree has no particular ordering relationship to either. (The + dividing line between the subtree sizes is based on trie relation.) + If we remove the last chunk of a given size from the interior of the + tree, we need to replace it with a leaf node. The tree ordering + rules permit a node to be replaced by any leaf below it. + + The smallest chunk in a tree (a common operation in a best-fit + allocator) can be found by walking a path to the leftmost leaf in + the tree. Unlike a usual binary tree, where we follow left child + pointers until we reach a null, here we follow the right child + pointer any time the left one is null, until we reach a leaf with + both child pointers null. The smallest chunk in the tree will be + somewhere along that path. + + The worst case number of steps to add, find, or remove a node is + bounded by the number of bits differentiating chunks within + bins. Under current bin calculations, this ranges from 6 up to 21 + (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case + is of course much better. +*/ + +struct malloc_tree_chunk { + /* The first four fields must be compatible with malloc_chunk */ + size_t prev_foot; + size_t head; + struct malloc_tree_chunk* fd; + struct malloc_tree_chunk* bk; + + struct malloc_tree_chunk* child[2]; + struct malloc_tree_chunk* parent; + bindex_t index; +}; + +typedef struct malloc_tree_chunk tchunk; +typedef struct malloc_tree_chunk* tchunkptr; +typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ + +/* A little helper macro for trees */ +#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) + +/* ----------------------------- Segments -------------------------------- */ + +/* + Each malloc space may include non-contiguous segments, held in a + list headed by an embedded malloc_segment record representing the + top-most space. Segments also include flags holding properties of + the space. Large chunks that are directly allocated by mmap are not + included in this list. They are instead independently created and + destroyed without otherwise keeping track of them. + + Segment management mainly comes into play for spaces allocated by + MMAP. Any call to MMAP might or might not return memory that is + adjacent to an existing segment. MORECORE normally contiguously + extends the current space, so this space is almost always adjacent, + which is simpler and faster to deal with. (This is why MORECORE is + used preferentially to MMAP when both are available -- see + sys_alloc.) When allocating using MMAP, we don't use any of the + hinting mechanisms (inconsistently) supported in various + implementations of unix mmap, or distinguish reserving from + committing memory. Instead, we just ask for space, and exploit + contiguity when we get it. It is probably possible to do + better than this on some systems, but no general scheme seems + to be significantly better. + + Management entails a simpler variant of the consolidation scheme + used for chunks to reduce fragmentation -- new adjacent memory is + normally prepended or appended to an existing segment. However, + there are limitations compared to chunk consolidation that mostly + reflect the fact that segment processing is relatively infrequent + (occurring only when getting memory from system) and that we + don't expect to have huge numbers of segments: + + * Segments are not indexed, so traversal requires linear scans. (It + would be possible to index these, but is not worth the extra + overhead and complexity for most programs on most platforms.) + * New segments are only appended to old ones when holding top-most + memory; if they cannot be prepended to others, they are held in + different segments. + + Except for the top-most segment of an mstate, each segment record + is kept at the tail of its segment. Segments are added by pushing + segment records onto the list headed by &mstate.seg for the + containing mstate. + + Segment flags control allocation/merge/deallocation policies: + * If EXTERN_BIT set, then we did not allocate this segment, + and so should not try to deallocate or merge with others. + (This currently holds only for the initial segment passed + into create_mspace_with_base.) + * If IS_MMAPPED_BIT set, the segment may be merged with + other surrounding mmapped segments and trimmed/de-allocated + using munmap. + * If neither bit is set, then the segment was obtained using + MORECORE so can be merged with surrounding MORECORE'd segments + and deallocated/trimmed using MORECORE with negative arguments. +*/ + +struct malloc_segment { + char* base; /* base address */ + size_t size; /* allocated size */ + struct malloc_segment* next; /* ptr to next segment */ + flag_t sflags; /* mmap and extern flag */ +}; + +#define is_mmapped_segment(S) ((S)->sflags & IS_MMAPPED_BIT) +#define is_extern_segment(S) ((S)->sflags & EXTERN_BIT) + +typedef struct malloc_segment msegment; +typedef struct malloc_segment* msegmentptr; + +/* ---------------------------- malloc_state ----------------------------- */ + +/* + A malloc_state holds all of the bookkeeping for a space. + The main fields are: + + Top + The topmost chunk of the currently active segment. Its size is + cached in topsize. The actual size of topmost space is + topsize+TOP_FOOT_SIZE, which includes space reserved for adding + fenceposts and segment records if necessary when getting more + space from the system. The size at which to autotrim top is + cached from mparams in trim_check, except that it is disabled if + an autotrim fails. + + Designated victim (dv) + This is the preferred chunk for servicing small requests that + don't have exact fits. It is normally the chunk split off most + recently to service another small request. Its size is cached in + dvsize. The link fields of this chunk are not maintained since it + is not kept in a bin. + + SmallBins + An array of bin headers for free chunks. These bins hold chunks + with sizes less than MIN_LARGE_SIZE bytes. Each bin contains + chunks of all the same size, spaced 8 bytes apart. To simplify + use in double-linked lists, each bin header acts as a malloc_chunk + pointing to the real first node, if it exists (else pointing to + itself). This avoids special-casing for headers. But to avoid + waste, we allocate only the fd/bk pointers of bins, and then use + repositioning tricks to treat these as the fields of a chunk. + + TreeBins + Treebins are pointers to the roots of trees holding a range of + sizes. There are 2 equally spaced treebins for each power of two + from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything + larger. + + Bin maps + There is one bit map for small bins ("smallmap") and one for + treebins ("treemap). Each bin sets its bit when non-empty, and + clears the bit when empty. Bit operations are then used to avoid + bin-by-bin searching -- nearly all "search" is done without ever + looking at bins that won't be selected. The bit maps + conservatively use 32 bits per map word, even if on 64bit system. + For a good description of some of the bit-based techniques used + here, see Henry S. Warren Jr's book "Hacker's Delight" (and + supplement at http://hackersdelight.org/). Many of these are + intended to reduce the branchiness of paths through malloc etc, as + well as to reduce the number of memory locations read or written. + + Segments + A list of segments headed by an embedded malloc_segment record + representing the initial space. + + Address check support + The least_addr field is the least address ever obtained from + MORECORE or MMAP. Attempted frees and reallocs of any address less + than this are trapped (unless INSECURE is defined). + + Magic tag + A cross-check field that should always hold same value as mparams.magic. + + Flags + Bits recording whether to use MMAP, locks, or contiguous MORECORE + + Statistics + Each space keeps track of current and maximum system memory + obtained via MORECORE or MMAP. + + Locking + If USE_LOCKS is defined, the "mutex" lock is acquired and released + around every public call using this mspace. +*/ + +/* Bin types, widths and sizes */ +#define NSMALLBINS (32U) +#define NTREEBINS (32U) +#define SMALLBIN_SHIFT (3U) +#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) +#define TREEBIN_SHIFT (8U) +#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) +#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) +#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) + +struct malloc_state { + binmap_t smallmap; + binmap_t treemap; + size_t dvsize; + size_t topsize; + char* least_addr; + mchunkptr dv; + mchunkptr top; + size_t trim_check; + size_t magic; + mchunkptr smallbins[(NSMALLBINS+1)*2]; + tbinptr treebins[NTREEBINS]; + size_t footprint; + size_t max_footprint; + flag_t mflags; +#if USE_LOCKS + MLOCK_T mutex; /* locate lock among fields that rarely change */ +#endif /* USE_LOCKS */ + msegment seg; +}; + +typedef struct malloc_state* mstate; + +/* ------------- Global malloc_state and malloc_params ------------------- */ + +/* + malloc_params holds global properties, including those that can be + dynamically set using mallopt. There is a single instance, mparams, + initialized in init_mparams. +*/ + +struct malloc_params { + size_t magic; + size_t page_size; + size_t granularity; + size_t mmap_threshold; + size_t trim_threshold; + flag_t default_mflags; +}; + +static struct malloc_params mparams; + +/* The global malloc_state used for all non-"mspace" calls */ +static struct malloc_state _gm_; +#define gm (&_gm_) +#define is_global(M) ((M) == &_gm_) +#define is_initialized(M) ((M)->top != 0) + +/* -------------------------- system alloc setup ------------------------- */ + +/* Operations on mflags */ + +#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) +#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) +#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) + +#define use_mmap(M) ((M)->mflags & USE_MMAP_BIT) +#define enable_mmap(M) ((M)->mflags |= USE_MMAP_BIT) +#define disable_mmap(M) ((M)->mflags &= ~USE_MMAP_BIT) + +#define use_noncontiguous(M) ((M)->mflags & USE_NONCONTIGUOUS_BIT) +#define disable_contiguous(M) ((M)->mflags |= USE_NONCONTIGUOUS_BIT) + +#define set_lock(M,L)\ + ((M)->mflags = (L)?\ + ((M)->mflags | USE_LOCK_BIT) :\ + ((M)->mflags & ~USE_LOCK_BIT)) + +/* page-align a size */ +#define page_align(S)\ + (((S) + (mparams.page_size)) & ~(mparams.page_size - SIZE_T_ONE)) + +/* granularity-align a size */ +#define granularity_align(S)\ + (((S) + (mparams.granularity)) & ~(mparams.granularity - SIZE_T_ONE)) + +#define is_page_aligned(S)\ + (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) +#define is_granularity_aligned(S)\ + (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) + +/* True if segment S holds address A */ +#define segment_holds(S, A)\ + ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) + +/* Return segment holding given address */ +static msegmentptr segment_holding(mstate m, char* addr) { + msegmentptr sp = &m->seg; + for (;;) { + if (addr >= sp->base && addr < sp->base + sp->size) + return sp; + if ((sp = sp->next) == 0) + return 0; + } +} + +/* Return true if segment contains a segment link */ +static int has_segment_link(mstate m, msegmentptr ss) { + msegmentptr sp = &m->seg; + for (;;) { + if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) + return 1; + if ((sp = sp->next) == 0) + return 0; + } +} + +#ifndef MORECORE_CANNOT_TRIM +#define should_trim(M,s) ((s) > (M)->trim_check) +#else /* MORECORE_CANNOT_TRIM */ +#define should_trim(M,s) (0) +#endif /* MORECORE_CANNOT_TRIM */ + +/* + TOP_FOOT_SIZE is padding at the end of a segment, including space + that may be needed to place segment records and fenceposts when new + noncontiguous segments are added. +*/ +#define TOP_FOOT_SIZE\ + (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) + + +/* ------------------------------- Hooks -------------------------------- */ + +/* + PREACTION should be defined to return 0 on success, and nonzero on + failure. If you are not using locking, you can redefine these to do + anything you like. +*/ + +#if USE_LOCKS + +/* Ensure locks are initialized */ +#define GLOBALLY_INITIALIZE() (mparams.page_size == 0 && init_mparams()) + +#define PREACTION(M) ((GLOBALLY_INITIALIZE() || use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) +#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } +#else /* USE_LOCKS */ + +#ifndef PREACTION +#define PREACTION(M) (0) +#endif /* PREACTION */ + +#ifndef POSTACTION +#define POSTACTION(M) +#endif /* POSTACTION */ + +#endif /* USE_LOCKS */ + +/* + CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. + USAGE_ERROR_ACTION is triggered on detected bad frees and + reallocs. The argument p is an address that might have triggered the + fault. It is ignored by the two predefined actions, but might be + useful in custom actions that try to help diagnose errors. +*/ + +#if PROCEED_ON_ERROR + +/* A count of the number of corruption errors causing resets */ +int malloc_corruption_error_count; + +/* default corruption action */ +static void reset_on_error(mstate m); + +#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) +#define USAGE_ERROR_ACTION(m, p) + +#else /* PROCEED_ON_ERROR */ + +#ifndef CORRUPTION_ERROR_ACTION +#define CORRUPTION_ERROR_ACTION(m) ABORT +#endif /* CORRUPTION_ERROR_ACTION */ + +#ifndef USAGE_ERROR_ACTION +#define USAGE_ERROR_ACTION(m,p) ABORT +#endif /* USAGE_ERROR_ACTION */ + +#endif /* PROCEED_ON_ERROR */ + +/* -------------------------- Debugging setup ---------------------------- */ + +#ifndef DEBUG + +#define check_free_chunk(M,P) +#define check_inuse_chunk(M,P) +#define check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) +#define check_malloc_state(M) +#define check_top_chunk(M,P) + +#else /* DEBUG */ +#define check_free_chunk(M,P) do_check_free_chunk(M,P) +#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) +#define check_top_chunk(M,P) do_check_top_chunk(M,P) +#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) +#define check_mmapped_chunk(M,P) do_check_mmapped_chunk(M,P) +#define check_malloc_state(M) do_check_malloc_state(M) + +static void do_check_any_chunk(mstate m, mchunkptr p); +static void do_check_top_chunk(mstate m, mchunkptr p); +static void do_check_mmapped_chunk(mstate m, mchunkptr p); +static void do_check_inuse_chunk(mstate m, mchunkptr p); +static void do_check_free_chunk(mstate m, mchunkptr p); +static void do_check_malloced_chunk(mstate m, void* mem, size_t s); +static void do_check_tree(mstate m, tchunkptr t); +static void do_check_treebin(mstate m, bindex_t i); +static void do_check_smallbin(mstate m, bindex_t i); +static void do_check_malloc_state(mstate m); +static int bin_find(mstate m, mchunkptr x); +static size_t traverse_and_check(mstate m); +#endif /* DEBUG */ + +/* ---------------------------- Indexing Bins ---------------------------- */ + +#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) +#define small_index(s) ((s) >> SMALLBIN_SHIFT) +#define small_index2size(i) ((i) << SMALLBIN_SHIFT) +#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) + +/* addressing by index. See above about smallbin repositioning */ +#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) +#define treebin_at(M,i) (&((M)->treebins[i])) + +/* assign tree index for size S to variable I */ +#if defined(__GNUC__) && defined(i386) +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K;\ + __asm__("bsrl %1,%0\n\t" : "=r" (K) : "rm" (X));\ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} +#else /* GNUC */ +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int Y = (unsigned int)X;\ + unsigned int N = ((Y - 0x100) >> 16) & 8;\ + unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ + N += K;\ + N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ + K = 14 - N + ((Y <<= K) >> 15);\ + I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ + }\ +} +#endif /* GNUC */ + +/* Bit representing maximum resolved size in a treebin at i */ +#define bit_for_tree_index(i) \ + (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) + +/* Shift placing maximum resolved bit in a treebin at i as sign bit */ +#define leftshift_for_tree_index(i) \ + ((i == NTREEBINS-1)? 0 : \ + ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) + +/* The size of the smallest chunk held in bin with index i */ +#define minsize_for_tree_index(i) \ + ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ + (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) + + +/* ------------------------ Operations on bin maps ----------------------- */ + +/* bit corresponding to given index */ +#define idx2bit(i) ((binmap_t)(1) << (i)) + +/* Mark/Clear bits with given index */ +#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) +#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) +#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) + +#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) +#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) +#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) + +/* index corresponding to given bit */ + +#if defined(__GNUC__) && defined(i386) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + __asm__("bsfl %1,%0\n\t" : "=r" (J) : "rm" (X));\ + I = (bindex_t)J;\ +} + +#else /* GNUC */ +#if USE_BUILTIN_FFS +#define compute_bit2idx(X, I) I = ffs(X)-1 + +#else /* USE_BUILTIN_FFS */ +#define compute_bit2idx(X, I)\ +{\ + unsigned int Y = X - 1;\ + unsigned int K = Y >> (16-4) & 16;\ + unsigned int N = K; Y >>= K;\ + N += K = Y >> (8-3) & 8; Y >>= K;\ + N += K = Y >> (4-2) & 4; Y >>= K;\ + N += K = Y >> (2-1) & 2; Y >>= K;\ + N += K = Y >> (1-0) & 1; Y >>= K;\ + I = (bindex_t)(N + Y);\ +} +#endif /* USE_BUILTIN_FFS */ +#endif /* GNUC */ + +/* isolate the least set bit of a bitmap */ +#define least_bit(x) ((x) & -(x)) + +/* mask with all bits to left of least bit of x on */ +#define left_bits(x) ((x<<1) | -(x<<1)) + +/* mask with all bits to left of or equal to least bit of x on */ +#define same_or_left_bits(x) ((x) | -(x)) + + +/* ----------------------- Runtime Check Support ------------------------- */ + +/* + For security, the main invariant is that malloc/free/etc never + writes to a static address other than malloc_state, unless static + malloc_state itself has been corrupted, which cannot occur via + malloc (because of these checks). In essence this means that we + believe all pointers, sizes, maps etc held in malloc_state, but + check all of those linked or offsetted from other embedded data + structures. These checks are interspersed with main code in a way + that tends to minimize their run-time cost. + + When FOOTERS is defined, in addition to range checking, we also + verify footer fields of inuse chunks, which can be used guarantee + that the mstate controlling malloc/free is intact. This is a + streamlined version of the approach described by William Robertson + et al in "Run-time Detection of Heap-based Overflows" LISA'03 + http://www.usenix.org/events/lisa03/tech/robertson.html The footer + of an inuse chunk holds the xor of its mstate and a random seed, + that is checked upon calls to free() and realloc(). This is + (probablistically) unguessable from outside the program, but can be + computed by any code successfully malloc'ing any chunk, so does not + itself provide protection against code that has already broken + security through some other means. Unlike Robertson et al, we + always dynamically check addresses of all offset chunks (previous, + next, etc). This turns out to be cheaper than relying on hashes. +*/ + +#if !INSECURE +/* Check if address a is at least as high as any from MORECORE or MMAP */ +#define ok_address(M, a) ((char*)(a) >= (M)->least_addr) +/* Check if address of next chunk n is higher than base chunk p */ +#define ok_next(p, n) ((char*)(p) < (char*)(n)) +/* Check if p has its cinuse bit on */ +#define ok_cinuse(p) cinuse(p) +/* Check if p has its pinuse bit on */ +#define ok_pinuse(p) pinuse(p) + +#else /* !INSECURE */ +#define ok_address(M, a) (1) +#define ok_next(b, n) (1) +#define ok_cinuse(p) (1) +#define ok_pinuse(p) (1) +#endif /* !INSECURE */ + +#if (FOOTERS && !INSECURE) +/* Check if (alleged) mstate m has expected magic field */ +#define ok_magic(M) ((M)->magic == mparams.magic) +#else /* (FOOTERS && !INSECURE) */ +#define ok_magic(M) (1) +#endif /* (FOOTERS && !INSECURE) */ + + +/* In gcc, use __builtin_expect to minimize impact of checks */ +#if !INSECURE +#if defined(__GNUC__) && __GNUC__ >= 3 +#define RTCHECK(e) __builtin_expect(e, 1) +#else /* GNUC */ +#define RTCHECK(e) (e) +#endif /* GNUC */ +#else /* !INSECURE */ +#define RTCHECK(e) (1) +#endif /* !INSECURE */ + +/* macros to set up inuse chunks with or without footers */ + +#if !FOOTERS + +#define mark_inuse_foot(M,p,s) + +/* Set cinuse bit and pinuse bit of next chunk */ +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set size, cinuse and pinuse bit of this chunk */ +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) + +#else /* FOOTERS */ + +/* Set foot of inuse chunk to be xor of mstate and seed */ +#define mark_inuse_foot(M,p,s)\ + (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) + +#define get_mstate_for(p)\ + ((mstate)(((mchunkptr)((char*)(p) +\ + (chunksize(p))))->prev_foot ^ mparams.magic)) + +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ + mark_inuse_foot(M,p,s)) + +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ + mark_inuse_foot(M,p,s)) + +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + mark_inuse_foot(M, p, s)) + +#endif /* !FOOTERS */ + +/* ---------------------------- setting mparams -------------------------- */ + +/* Initialize mparams */ +static int init_mparams(void) { + if (mparams.page_size == 0) { + size_t s; + + mparams.mmap_threshold = DEFAULT_MMAP_THRESHOLD; + mparams.trim_threshold = DEFAULT_TRIM_THRESHOLD; +#if MORECORE_CONTIGUOUS + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT; +#else /* MORECORE_CONTIGUOUS */ + mparams.default_mflags = USE_LOCK_BIT|USE_MMAP_BIT|USE_NONCONTIGUOUS_BIT; +#endif /* MORECORE_CONTIGUOUS */ + +#if (FOOTERS && !INSECURE) + { +#if USE_DEV_RANDOM + int fd; + unsigned char buf[sizeof(size_t)]; + /* Try to use /dev/urandom, else fall back on using time */ + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && + read(fd, buf, sizeof(buf)) == sizeof(buf)) { + s = *((size_t *) buf); + close(fd); + } + else +#endif /* USE_DEV_RANDOM */ + s = (size_t)(time(0) ^ (size_t)0x55555555U); + + s |= (size_t)8U; /* ensure nonzero */ + s &= ~(size_t)7U; /* improve chances of fault for bad values */ + + } +#else /* (FOOTERS && !INSECURE) */ + s = (size_t)0x58585858U; +#endif /* (FOOTERS && !INSECURE) */ + ACQUIRE_MAGIC_INIT_LOCK(); + if (mparams.magic == 0) { + mparams.magic = s; + /* Set up lock for main malloc area */ + INITIAL_LOCK(&gm->mutex); + gm->mflags = mparams.default_mflags; + } + RELEASE_MAGIC_INIT_LOCK(); + +#ifndef WIN32 + mparams.page_size = malloc_getpagesize; + mparams.granularity = ((DEFAULT_GRANULARITY != 0)? + DEFAULT_GRANULARITY : mparams.page_size); +#else /* WIN32 */ + { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + mparams.page_size = system_info.dwPageSize; + mparams.granularity = system_info.dwAllocationGranularity; + } +#endif /* WIN32 */ + + /* Sanity-check configuration: + size_t must be unsigned and as wide as pointer type. + ints must be at least 4 bytes. + alignment must be at least 8. + Alignment, min chunk size, and page size must all be powers of 2. + */ + if ((sizeof(size_t) != sizeof(char*)) || + (MAX_SIZE_T < MIN_CHUNK_SIZE) || + (sizeof(int) < 4) || + (MALLOC_ALIGNMENT < (size_t)8U) || + ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || + ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || + ((mparams.granularity & (mparams.granularity-SIZE_T_ONE)) != 0) || + ((mparams.page_size & (mparams.page_size-SIZE_T_ONE)) != 0)) + ABORT; + } + return 0; +} + +/* support for mallopt */ +static int change_mparam(int param_number, int value) { + size_t val = (size_t)value; + init_mparams(); + switch(param_number) { + case M_TRIM_THRESHOLD: + mparams.trim_threshold = val; + return 1; + case M_GRANULARITY: + if (val >= mparams.page_size && ((val & (val-1)) == 0)) { + mparams.granularity = val; + return 1; + } + else + return 0; + case M_MMAP_THRESHOLD: + mparams.mmap_threshold = val; + return 1; + default: + return 0; + } +} + +#ifdef DEBUG +/* ------------------------- Debugging Support --------------------------- */ + +/* Check properties of any chunk, whether free, inuse, mmapped etc */ +static void do_check_any_chunk(mstate m, mchunkptr p) { + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); +} + +/* Check properties of top chunk */ +static void do_check_top_chunk(mstate m, mchunkptr p) { + msegmentptr sp = segment_holding(m, (char*)p); + size_t sz = chunksize(p); + assert(sp != 0); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(sz == m->topsize); + assert(sz > 0); + assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); + assert(pinuse(p)); + assert(!next_pinuse(p)); +} + +/* Check properties of (inuse) mmapped chunks */ +static void do_check_mmapped_chunk(mstate m, mchunkptr p) { + size_t sz = chunksize(p); + size_t len = (sz + (p->prev_foot & ~IS_MMAPPED_BIT) + MMAP_FOOT_PAD); + assert(is_mmapped(p)); + assert(use_mmap(m)); + assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(ok_address(m, p)); + assert(!is_small(sz)); + assert((len & (mparams.page_size-SIZE_T_ONE)) == 0); + assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD); + assert(chunk_plus_offset(p, sz+SIZE_T_SIZE)->head == 0); +} + +/* Check properties of inuse chunks */ +static void do_check_inuse_chunk(mstate m, mchunkptr p) { + do_check_any_chunk(m, p); + assert(cinuse(p)); + assert(next_pinuse(p)); + /* If not pinuse and not mmapped, previous chunk has OK offset */ + assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p); + if (is_mmapped(p)) + do_check_mmapped_chunk(m, p); +} + +/* Check properties of free chunks */ +static void do_check_free_chunk(mstate m, mchunkptr p) { + size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT); + mchunkptr next = chunk_plus_offset(p, sz); + do_check_any_chunk(m, p); + assert(!cinuse(p)); + assert(!next_pinuse(p)); + assert (!is_mmapped(p)); + if (p != m->dv && p != m->top) { + if (sz >= MIN_CHUNK_SIZE) { + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(is_aligned(chunk2mem(p))); + assert(next->prev_foot == sz); + assert(pinuse(p)); + assert (next == m->top || cinuse(next)); + assert(p->fd->bk == p); + assert(p->bk->fd == p); + } + else /* markers are always of size SIZE_T_SIZE */ + assert(sz == SIZE_T_SIZE); + } +} + +/* Check properties of malloced chunks at the point they are malloced */ +static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT); + do_check_inuse_chunk(m, p); + assert((sz & CHUNK_ALIGN_MASK) == 0); + assert(sz >= MIN_CHUNK_SIZE); + assert(sz >= s); + /* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */ + assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE)); + } +} + +/* Check a tree and its subtrees. */ +static void do_check_tree(mstate m, tchunkptr t) { + tchunkptr head = 0; + tchunkptr u = t; + bindex_t tindex = t->index; + size_t tsize = chunksize(t); + bindex_t idx; + compute_tree_index(tsize, idx); + assert(tindex == idx); + assert(tsize >= MIN_LARGE_SIZE); + assert(tsize >= minsize_for_tree_index(idx)); + assert((idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); + + do { /* traverse through chain of same-sized nodes */ + do_check_any_chunk(m, ((mchunkptr)u)); + assert(u->index == tindex); + assert(chunksize(u) == tsize); + assert(!cinuse(u)); + assert(!next_pinuse(u)); + assert(u->fd->bk == u); + assert(u->bk->fd == u); + if (u->parent == 0) { + assert(u->child[0] == 0); + assert(u->child[1] == 0); + } + else { + assert(head == 0); /* only one node on chain has parent */ + head = u; + assert(u->parent != u); + assert (u->parent->child[0] == u || + u->parent->child[1] == u || + *((tbinptr*)(u->parent)) == u); + if (u->child[0] != 0) { + assert(u->child[0]->parent == u); + assert(u->child[0] != u); + do_check_tree(m, u->child[0]); + } + if (u->child[1] != 0) { + assert(u->child[1]->parent == u); + assert(u->child[1] != u); + do_check_tree(m, u->child[1]); + } + if (u->child[0] != 0 && u->child[1] != 0) { + assert(chunksize(u->child[0]) < chunksize(u->child[1])); + } + } + u = u->fd; + } while (u != t); + assert(head != 0); +} + +/* Check all the chunks in a treebin. */ +static void do_check_treebin(mstate m, bindex_t i) { + tbinptr* tb = treebin_at(m, i); + tchunkptr t = *tb; + int empty = (m->treemap & (1U << i)) == 0; + if (t == 0) + assert(empty); + if (!empty) + do_check_tree(m, t); +} + +/* Check all the chunks in a smallbin. */ +static void do_check_smallbin(mstate m, bindex_t i) { + sbinptr b = smallbin_at(m, i); + mchunkptr p = b->bk; + unsigned int empty = (m->smallmap & (1U << i)) == 0; + if (p == b) + assert(empty); + if (!empty) { + for (; p != b; p = p->bk) { + size_t size = chunksize(p); + mchunkptr q; + /* each chunk claims to be free */ + do_check_free_chunk(m, p); + /* chunk belongs in bin */ + assert(small_index(size) == i); + assert(p->bk == b || chunksize(p->bk) == chunksize(p)); + /* chunk is followed by an inuse chunk */ + q = next_chunk(p); + if (q->head != FENCEPOST_HEAD) + do_check_inuse_chunk(m, q); + } + } +} + +/* Find x in a bin. Used in other check functions. */ +static int bin_find(mstate m, mchunkptr x) { + size_t size = chunksize(x); + if (is_small(size)) { + bindex_t sidx = small_index(size); + sbinptr b = smallbin_at(m, sidx); + if (smallmap_is_marked(m, sidx)) { + mchunkptr p = b; + do { + if (p == x) + return 1; + } while ((p = p->fd) != b); + } + } + else { + bindex_t tidx; + compute_tree_index(size, tidx); + if (treemap_is_marked(m, tidx)) { + tchunkptr t = *treebin_at(m, tidx); + size_t sizebits = size << leftshift_for_tree_index(tidx); + while (t != 0 && chunksize(t) != size) { + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + sizebits <<= 1; + } + if (t != 0) { + tchunkptr u = t; + do { + if (u == (tchunkptr)x) + return 1; + } while ((u = u->fd) != t); + } + } + } + return 0; +} + +/* Traverse each chunk and check it; return total */ +static size_t traverse_and_check(mstate m) { + size_t sum = 0; + if (is_initialized(m)) { + msegmentptr s = &m->seg; + sum += m->topsize + TOP_FOOT_SIZE; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + mchunkptr lastq = 0; + assert(pinuse(q)); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + sum += chunksize(q); + if (cinuse(q)) { + assert(!bin_find(m, q)); + do_check_inuse_chunk(m, q); + } + else { + assert(q == m->dv || bin_find(m, q)); + assert(lastq == 0 || cinuse(lastq)); /* Not 2 consecutive free */ + do_check_free_chunk(m, q); + } + lastq = q; + q = next_chunk(q); + } + s = s->next; + } + } + return sum; +} + +/* Check all properties of malloc_state. */ +static void do_check_malloc_state(mstate m) { + bindex_t i; + size_t total; + /* check bins */ + for (i = 0; i < NSMALLBINS; ++i) + do_check_smallbin(m, i); + for (i = 0; i < NTREEBINS; ++i) + do_check_treebin(m, i); + + if (m->dvsize != 0) { /* check dv chunk */ + do_check_any_chunk(m, m->dv); + assert(m->dvsize == chunksize(m->dv)); + assert(m->dvsize >= MIN_CHUNK_SIZE); + assert(bin_find(m, m->dv) == 0); + } + + if (m->top != 0) { /* check top chunk */ + do_check_top_chunk(m, m->top); + assert(m->topsize == chunksize(m->top)); + assert(m->topsize > 0); + assert(bin_find(m, m->top) == 0); + } + + total = traverse_and_check(m); + assert(total <= m->footprint); + assert(m->footprint <= m->max_footprint); +} +#endif /* DEBUG */ + +/* ----------------------------- statistics ------------------------------ */ + +#if !NO_MALLINFO +static struct _mallinfo internal_mallinfo(mstate m) { + struct _mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + if (!PREACTION(m)) { + check_malloc_state(m); + if (is_initialized(m)) { + size_t nfree = SIZE_T_ONE; /* top always free */ + size_t mfree = m->topsize + TOP_FOOT_SIZE; + size_t sum = mfree; + msegmentptr s = &m->seg; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + size_t sz = chunksize(q); + sum += sz; + if (!cinuse(q)) { + mfree += sz; + ++nfree; + } + q = next_chunk(q); + } + s = s->next; + } + + nm.arena = sum; + nm.ordblks = nfree; + nm.hblkhd = m->footprint - sum; + nm.usmblks = m->max_footprint; + nm.uordblks = m->footprint - mfree; + nm.fordblks = mfree; + nm.keepcost = m->topsize; + } + + POSTACTION(m); + } + return nm; +} +#endif /* !NO_MALLINFO */ + +static void internal_malloc_stats(mstate m) { + if (!PREACTION(m)) { + size_t maxfp = 0; + size_t fp = 0; + size_t used = 0; + check_malloc_state(m); + if (is_initialized(m)) { + msegmentptr s = &m->seg; + maxfp = m->max_footprint; + fp = m->footprint; + used = fp - (m->topsize + TOP_FOOT_SIZE); + + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + if (!cinuse(q)) + used -= chunksize(q); + q = next_chunk(q); + } + s = s->next; + } + } + + fprintf(stderr, "max system bytes = %10lu\n", (unsigned long)(maxfp)); + fprintf(stderr, "system bytes = %10lu\n", (unsigned long)(fp)); + fprintf(stderr, "in use bytes = %10lu\n", (unsigned long)(used)); + + POSTACTION(m); + } +} + +/* ----------------------- Operations on smallbins ----------------------- */ + +/* + Various forms of linking and unlinking are defined as macros. Even + the ones for trees, which are very long but have very short typical + paths. This is ugly but reduces reliance on inlining support of + compilers. +*/ + +/* Link a free chunk into a smallbin */ +#define insert_small_chunk(M, P, S) {\ + bindex_t I = small_index(S);\ + mchunkptr B = smallbin_at(M, I);\ + mchunkptr F = B;\ + assert(S >= MIN_CHUNK_SIZE);\ + if (!smallmap_is_marked(M, I))\ + mark_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, B->fd)))\ + F = B->fd;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + B->fd = P;\ + F->bk = P;\ + P->fd = F;\ + P->bk = B;\ +} + +/* Unlink a chunk from a smallbin */ +#define unlink_small_chunk(M, P, S) {\ + mchunkptr F = P->fd;\ + mchunkptr B = P->bk;\ + bindex_t I = small_index(S);\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (F == B)\ + clear_smallmap(M, I);\ + else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\ + (B == smallbin_at(M,I) || ok_address(M, B)))) {\ + F->bk = B;\ + B->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} + +/* Unlink the first chunk from a smallbin */ +#define unlink_first_small_chunk(M, B, P, I) {\ + mchunkptr F = P->fd;\ + assert(P != B);\ + assert(P != F);\ + assert(chunksize(P) == small_index2size(I));\ + if (B == F)\ + clear_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, F))) {\ + B->fd = F;\ + F->bk = B;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} + +/* Replace dv node, binning the old one */ +/* Used only when dvsize known to be small */ +#define replace_dv(M, P, S) {\ + size_t DVS = M->dvsize;\ + if (DVS != 0) {\ + mchunkptr DV = M->dv;\ + assert(is_small(DVS));\ + insert_small_chunk(M, DV, DVS);\ + }\ + M->dvsize = S;\ + M->dv = P;\ +} + +/* ------------------------- Operations on trees ------------------------- */ + +/* Insert chunk into tree */ +#define insert_large_chunk(M, X, S) {\ + tbinptr* H;\ + bindex_t I;\ + compute_tree_index(S, I);\ + H = treebin_at(M, I);\ + X->index = I;\ + X->child[0] = X->child[1] = 0;\ + if (!treemap_is_marked(M, I)) {\ + mark_treemap(M, I);\ + *H = X;\ + X->parent = (tchunkptr)H;\ + X->fd = X->bk = X;\ + }\ + else {\ + tchunkptr T = *H;\ + size_t K = S << leftshift_for_tree_index(I);\ + for (;;) {\ + if (chunksize(T) != S) {\ + tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ + K <<= 1;\ + if (*C != 0)\ + T = *C;\ + else if (RTCHECK(ok_address(M, C))) {\ + *C = X;\ + X->parent = T;\ + X->fd = X->bk = X;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + else {\ + tchunkptr F = T->fd;\ + if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ + T->fd = F->bk = X;\ + X->fd = F;\ + X->bk = T;\ + X->parent = 0;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + }\ + }\ +} + +/* + Unlink steps: + + 1. If x is a chained node, unlink it from its same-sized fd/bk links + and choose its bk node as its replacement. + 2. If x was the last node of its size, but not a leaf node, it must + be replaced with a leaf node (not merely one with an open left or + right), to make sure that lefts and rights of descendents + correspond properly to bit masks. We use the rightmost descendent + of x. We could use any other leaf, but this is easy to locate and + tends to counteract removal of leftmosts elsewhere, and so keeps + paths shorter than minimally guaranteed. This doesn't loop much + because on average a node in a tree is near the bottom. + 3. If x is the base of a chain (i.e., has parent links) relink + x's parent and children to x's replacement (or null if none). +*/ + +#define unlink_large_chunk(M, X) {\ + tchunkptr XP = X->parent;\ + tchunkptr R;\ + if (X->bk != X) {\ + tchunkptr F = X->fd;\ + R = X->bk;\ + if (RTCHECK(ok_address(M, F))) {\ + F->bk = R;\ + R->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else {\ + tchunkptr* RP;\ + if (((R = *(RP = &(X->child[1]))) != 0) ||\ + ((R = *(RP = &(X->child[0]))) != 0)) {\ + tchunkptr* CP;\ + while ((*(CP = &(R->child[1])) != 0) ||\ + (*(CP = &(R->child[0])) != 0)) {\ + R = *(RP = CP);\ + }\ + if (RTCHECK(ok_address(M, RP)))\ + *RP = 0;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + }\ + if (XP != 0) {\ + tbinptr* H = treebin_at(M, X->index);\ + if (X == *H) {\ + if ((*H = R) == 0) \ + clear_treemap(M, X->index);\ + }\ + else if (RTCHECK(ok_address(M, XP))) {\ + if (XP->child[0] == X) \ + XP->child[0] = R;\ + else \ + XP->child[1] = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + if (R != 0) {\ + if (RTCHECK(ok_address(M, R))) {\ + tchunkptr C0, C1;\ + R->parent = XP;\ + if ((C0 = X->child[0]) != 0) {\ + if (RTCHECK(ok_address(M, C0))) {\ + R->child[0] = C0;\ + C0->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + if ((C1 = X->child[1]) != 0) {\ + if (RTCHECK(ok_address(M, C1))) {\ + R->child[1] = C1;\ + C1->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ +} + +/* Relays to large vs small bin operations */ + +#define insert_chunk(M, P, S)\ + if (is_small(S)) insert_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } + +#define unlink_chunk(M, P, S)\ + if (is_small(S)) unlink_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } + + +/* Relays to internal calls to malloc/free from realloc, memalign etc */ + +#if ONLY_MSPACES +#define internal_malloc(m, b) mspace_malloc(m, b) +#define internal_free(m, mem) mspace_free(m,mem); +#else /* ONLY_MSPACES */ +#if MSPACES +#define internal_malloc(m, b)\ + (m == gm)? dlmalloc(b) : mspace_malloc(m, b) +#define internal_free(m, mem)\ + if (m == gm) dlfree(mem); else mspace_free(m,mem); +#else /* MSPACES */ +#define internal_malloc(m, b) dlmalloc(b) +#define internal_free(m, mem) dlfree(mem) +#endif /* MSPACES */ +#endif /* ONLY_MSPACES */ + +/* ----------------------- Direct-mmapping chunks ----------------------- */ + +/* + Directly mmapped chunks are set up with an offset to the start of + the mmapped region stored in the prev_foot field of the chunk. This + allows reconstruction of the required argument to MUNMAP when freed, + and also allows adjustment of the returned chunk to meet alignment + requirements (especially in memalign). There is also enough space + allocated to hold a fake next chunk of size SIZE_T_SIZE to maintain + the PINUSE bit so frees can be checked. +*/ + +/* Malloc using mmap */ +static void* mmap_alloc(mstate m, size_t nb) { + size_t mmsize = granularity_align(nb + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + if (mmsize > nb) { /* Check for wrap around 0 */ + char* mm = (char*)(DIRECT_MMAP(mmsize)); + if (mm != CMFAIL) { + size_t offset = align_offset(chunk2mem(mm)); + size_t psize = mmsize - offset - MMAP_FOOT_PAD; + mchunkptr p = (mchunkptr)(mm + offset); + p->prev_foot = offset | IS_MMAPPED_BIT; + (p)->head = (psize|CINUSE_BIT); + mark_inuse_foot(m, p, psize); + chunk_plus_offset(p, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(p, psize+SIZE_T_SIZE)->head = 0; + + if (mm < m->least_addr) + m->least_addr = mm; + if ((m->footprint += mmsize) > m->max_footprint) + m->max_footprint = m->footprint; + assert(is_aligned(chunk2mem(p))); + check_mmapped_chunk(m, p); + return chunk2mem(p); + } + } + return 0; +} + +/* Realloc using mmap */ +static mchunkptr mmap_resize(mstate m, mchunkptr oldp, size_t nb) { + size_t oldsize = chunksize(oldp); + if (is_small(nb)) /* Can't shrink mmap regions below small size */ + return 0; + /* Keep old chunk if big enough but not too big */ + if (oldsize >= nb + SIZE_T_SIZE && + (oldsize - nb) <= (mparams.granularity << 1)) + return oldp; + else { + size_t offset = oldp->prev_foot & ~IS_MMAPPED_BIT; + size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; + size_t newmmsize = granularity_align(nb + SIX_SIZE_T_SIZES + + CHUNK_ALIGN_MASK); + char* cp = (char*)CALL_MREMAP((char*)oldp - offset, + oldmmsize, newmmsize, 1); + if (cp != CMFAIL) { + mchunkptr newp = (mchunkptr)(cp + offset); + size_t psize = newmmsize - offset - MMAP_FOOT_PAD; + newp->head = (psize|CINUSE_BIT); + mark_inuse_foot(m, newp, psize); + chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; + chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; + + if (cp < m->least_addr) + m->least_addr = cp; + if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) + m->max_footprint = m->footprint; + check_mmapped_chunk(m, newp); + return newp; + } + } + return 0; +} + +/* -------------------------- mspace management -------------------------- */ + +/* Initialize top chunk and its size */ +static void init_top(mstate m, mchunkptr p, size_t psize) { + /* Ensure alignment */ + size_t offset = align_offset(chunk2mem(p)); + p = (mchunkptr)((char*)p + offset); + psize -= offset; + + m->top = p; + m->topsize = psize; + p->head = psize | PINUSE_BIT; + /* set size of fake trailing chunk holding overhead space only once */ + chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; + m->trim_check = mparams.trim_threshold; /* reset on each update */ +} + +/* Initialize bins for a new mstate that is otherwise zeroed out */ +static void init_bins(mstate m) { + /* Establish circular links for smallbins */ + bindex_t i; + for (i = 0; i < NSMALLBINS; ++i) { + sbinptr bin = smallbin_at(m,i); + bin->fd = bin->bk = bin; + } +} + +#if PROCEED_ON_ERROR + +/* default corruption action */ +static void reset_on_error(mstate m) { + int i; + ++malloc_corruption_error_count; + /* Reinitialize fields to forget about all memory */ + m->smallbins = m->treebins = 0; + m->dvsize = m->topsize = 0; + m->seg.base = 0; + m->seg.size = 0; + m->seg.next = 0; + m->top = m->dv = 0; + for (i = 0; i < NTREEBINS; ++i) + *treebin_at(m, i) = 0; + init_bins(m); +} +#endif /* PROCEED_ON_ERROR */ + +/* Allocate chunk and prepend remainder with chunk in successor base. */ +static void* prepend_alloc(mstate m, char* newbase, char* oldbase, + size_t nb) { + mchunkptr p = align_as_chunk(newbase); + mchunkptr oldfirst = align_as_chunk(oldbase); + size_t psize = (char*)oldfirst - (char*)p; + mchunkptr q = chunk_plus_offset(p, nb); + size_t qsize = psize - nb; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + + assert((char*)oldfirst > (char*)q); + assert(pinuse(oldfirst)); + assert(qsize >= MIN_CHUNK_SIZE); + + /* consolidate remainder with first chunk of old base */ + if (oldfirst == m->top) { + size_t tsize = m->topsize += qsize; + m->top = q; + q->head = tsize | PINUSE_BIT; + check_top_chunk(m, q); + } + else if (oldfirst == m->dv) { + size_t dsize = m->dvsize += qsize; + m->dv = q; + set_size_and_pinuse_of_free_chunk(q, dsize); + } + else { + if (!cinuse(oldfirst)) { + size_t nsize = chunksize(oldfirst); + unlink_chunk(m, oldfirst, nsize); + oldfirst = chunk_plus_offset(oldfirst, nsize); + qsize += nsize; + } + set_free_with_pinuse(q, qsize, oldfirst); + insert_chunk(m, q, qsize); + check_free_chunk(m, q); + } + + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); +} + + +/* Add a segment to hold a new noncontiguous region */ +static void add_segment(mstate m, char* tbase, size_t tsize, flag_t mmapped) { + /* Determine locations and sizes of segment, fenceposts, old top */ + char* old_top = (char*)m->top; + msegmentptr oldsp = segment_holding(m, old_top); + char* old_end = oldsp->base + oldsp->size; + size_t ssize = pad_request(sizeof(struct malloc_segment)); + char* rawsp = old_end - (ssize + FOUR_SIZE_T_SIZES + CHUNK_ALIGN_MASK); + size_t offset = align_offset(chunk2mem(rawsp)); + char* asp = rawsp + offset; + char* csp = (asp < (old_top + MIN_CHUNK_SIZE))? old_top : asp; + mchunkptr sp = (mchunkptr)csp; + msegmentptr ss = (msegmentptr)(chunk2mem(sp)); + mchunkptr tnext = chunk_plus_offset(sp, ssize); + mchunkptr p = tnext; +// int nfences = 0; + + /* reset top to new space */ + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + + /* Set up segment record */ + assert(is_aligned(ss)); + set_size_and_pinuse_of_inuse_chunk(m, sp, ssize); + *ss = m->seg; /* Push current record */ + m->seg.base = tbase; + m->seg.size = tsize; + m->seg.sflags = mmapped; + m->seg.next = ss; + + /* Insert trailing fenceposts */ + for (;;) { + mchunkptr nextp = chunk_plus_offset(p, SIZE_T_SIZE); + p->head = FENCEPOST_HEAD; +// ++nfences; + if ((char*)(&(nextp->head)) < old_end) + p = nextp; + else + break; + } +// assert(nfences >= 2); + + /* Insert the rest of old top into a bin as an ordinary free chunk */ + if (csp != old_top) { + mchunkptr q = (mchunkptr)old_top; + size_t psize = csp - old_top; + mchunkptr tn = chunk_plus_offset(q, psize); + set_free_with_pinuse(q, psize, tn); + insert_chunk(m, q, psize); + } + + check_top_chunk(m, m->top); +} + +/* -------------------------- System allocation -------------------------- */ + +/* Get memory from system using MORECORE or MMAP */ +static void* sys_alloc(mstate m, size_t nb) { + char* tbase = CMFAIL; + size_t tsize = 0; + flag_t mmap_flag = 0; + + init_mparams(); + + /* Directly map large chunks */ + if (use_mmap(m) && nb >= mparams.mmap_threshold) { + void* mem = mmap_alloc(m, nb); + if (mem != 0) + return mem; + } + + /* + Try getting memory in any of three ways (in most-preferred to + least-preferred order): + 1. A call to MORECORE that can normally contiguously extend memory. + (disabled if not MORECORE_CONTIGUOUS or not HAVE_MORECORE or + or main space is mmapped or a previous contiguous call failed) + 2. A call to MMAP new space (disabled if not HAVE_MMAP). + Note that under the default settings, if MORECORE is unable to + fulfill a request, and HAVE_MMAP is true, then mmap is + used as a noncontiguous system allocator. This is a useful backup + strategy for systems with holes in address spaces -- in this case + sbrk cannot contiguously expand the heap, but mmap may be able to + find space. + 3. A call to MORECORE that cannot usually contiguously extend memory. + (disabled if not HAVE_MORECORE) + */ + +#if MORECORE_CONTIGUOUS + if (MORECORE_CONTIGUOUS && !use_noncontiguous(m)) { + char* br = CMFAIL; + msegmentptr ss = (m->top == 0)? 0 : segment_holding(m, (char*)m->top); + size_t asize = 0; + ACQUIRE_MORECORE_LOCK(); + + if (ss == 0) { /* First time through or recovery */ + char* base = (char*)CALL_MORECORE(0); + if (base != CMFAIL) { + asize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE); + /* Adjust to end on a page boundary */ + if (!is_page_aligned(base)) + asize += (page_align((size_t)base) - (size_t)base); + /* Can't call MORECORE if size is negative when treated as signed */ + if (asize < HALF_MAX_SIZE_T && + (br = (char*)(CALL_MORECORE(asize))) == base) { + tbase = base; + tsize = asize; + } + } + } + else { + /* Subtract out existing available top space from MORECORE request. */ + asize = granularity_align(nb - m->topsize + TOP_FOOT_SIZE + SIZE_T_ONE); + /* Use mem here only if it did continuously extend old space */ + if (asize < HALF_MAX_SIZE_T && + (br = (char*)(CALL_MORECORE(asize))) == ss->base+ss->size) { + tbase = br; + tsize = asize; + } + } + + if (tbase == CMFAIL) { /* Cope with partial failure */ + if (br != CMFAIL) { /* Try to use/extend the space we did get */ + if (asize < HALF_MAX_SIZE_T && + asize < nb + TOP_FOOT_SIZE + SIZE_T_ONE) { + size_t esize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE - asize); + if (esize < HALF_MAX_SIZE_T) { + char* end = (char*)CALL_MORECORE(esize); + if (end != CMFAIL) + asize += esize; + else { /* Can't use; try to release */ + CALL_MORECORE(-asize); + br = CMFAIL; + } + } + } + } + if (br != CMFAIL) { /* Use the space we did get */ + tbase = br; + tsize = asize; + } + else + disable_contiguous(m); /* Don't try contiguous path in the future */ + } + + RELEASE_MORECORE_LOCK(); + } +#endif + + if (HAVE_MMAP && tbase == CMFAIL) { /* Try MMAP */ + size_t req = nb + TOP_FOOT_SIZE + SIZE_T_ONE; + size_t rsize = granularity_align(req); + if (rsize > nb) { /* Fail if wraps around zero */ + char* mp = (char*)(CALL_MMAP(rsize)); + if (mp != CMFAIL) { + tbase = mp; + tsize = rsize; + mmap_flag = IS_MMAPPED_BIT; + } + } + } + + if (HAVE_MORECORE && tbase == CMFAIL) { /* Try noncontiguous MORECORE */ + size_t asize = granularity_align(nb + TOP_FOOT_SIZE + SIZE_T_ONE); + if (asize < HALF_MAX_SIZE_T) { + char* br = CMFAIL; + char* end = CMFAIL; + ACQUIRE_MORECORE_LOCK(); + br = (char*)(CALL_MORECORE(asize)); + end = (char*)(CALL_MORECORE(0)); + RELEASE_MORECORE_LOCK(); + if (br != CMFAIL && end != CMFAIL && br < end) { + size_t ssize = end - br; + if (ssize > nb + TOP_FOOT_SIZE) { + tbase = br; + tsize = ssize; + } + } + } + } + + if (tbase != CMFAIL) { + + if ((m->footprint += tsize) > m->max_footprint) + m->max_footprint = m->footprint; + + if (!is_initialized(m)) { /* first-time initialization */ + m->seg.base = m->least_addr = tbase; + m->seg.size = tsize; + m->seg.sflags = mmap_flag; + m->magic = mparams.magic; + init_bins(m); + if (is_global(m)) + init_top(m, (mchunkptr)tbase, tsize - TOP_FOOT_SIZE); + else { + /* Offset top by embedded malloc_state */ + mchunkptr mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) -TOP_FOOT_SIZE); + } + } + + else { + /* Try to merge with an existing segment */ + msegmentptr sp = &m->seg; + while (sp != 0 && tbase != sp->base + sp->size) + sp = sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & IS_MMAPPED_BIT) == mmap_flag && + segment_holds(sp, m->top)) { /* append */ + sp->size += tsize; + init_top(m, m->top, m->topsize + tsize); + } + else { + if (tbase < m->least_addr) + m->least_addr = tbase; + sp = &m->seg; + while (sp != 0 && sp->base != tbase + tsize) + sp = sp->next; + if (sp != 0 && + !is_extern_segment(sp) && + (sp->sflags & IS_MMAPPED_BIT) == mmap_flag) { + char* oldbase = sp->base; + sp->base = tbase; + sp->size += tsize; + return prepend_alloc(m, tbase, oldbase, nb); + } + else + add_segment(m, tbase, tsize, mmap_flag); + } + } + + if (nb < m->topsize) { /* Allocate from new or extended top space */ + size_t rsize = m->topsize -= nb; + mchunkptr p = m->top; + mchunkptr r = m->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + check_top_chunk(m, m->top); + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); + } + } + + MALLOC_FAILURE_ACTION; + return 0; +} + +/* ----------------------- system deallocation -------------------------- */ + +/* Unmap and unlink any mmapped segments that don't contain used chunks */ +static size_t release_unused_segments(mstate m) { + size_t released = 0; + msegmentptr pred = &m->seg; + msegmentptr sp = pred->next; + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + msegmentptr next = sp->next; + if (is_mmapped_segment(sp) && !is_extern_segment(sp)) { + mchunkptr p = align_as_chunk(base); + size_t psize = chunksize(p); + /* Can unmap if first chunk holds entire segment and not pinned */ + if (!cinuse(p) && (char*)p + psize >= base + size - TOP_FOOT_SIZE) { + tchunkptr tp = (tchunkptr)p; + assert(segment_holds(sp, (char*)sp)); + if (p == m->dv) { + m->dv = 0; + m->dvsize = 0; + } + else { + unlink_large_chunk(m, tp); + } + if (CALL_MUNMAP(base, size) == 0) { + released += size; + m->footprint -= size; + /* unlink obsoleted record */ + sp = pred; + sp->next = next; + } + else { /* back out if cannot unmap */ + insert_large_chunk(m, tp, psize); + } + } + } + pred = sp; + sp = next; + } + return released; +} + +static int sys_trim(mstate m, size_t pad) { + size_t released = 0; + if (pad < MAX_REQUEST && is_initialized(m)) { + pad += TOP_FOOT_SIZE; /* ensure enough room for segment overhead */ + + if (m->topsize > pad) { + /* Shrink top space in granularity-size units, keeping at least one */ + size_t unit = mparams.granularity; + size_t extra = ((m->topsize - pad + (unit - SIZE_T_ONE)) / unit - + SIZE_T_ONE) * unit; + msegmentptr sp = segment_holding(m, (char*)m->top); + + if (!is_extern_segment(sp)) { + if (is_mmapped_segment(sp)) { + if (HAVE_MMAP && + sp->size >= extra && + !has_segment_link(m, sp)) { /* can't shrink if pinned */ + size_t newsize = sp->size - extra; + /* Prefer mremap, fall back to munmap */ + if ((CALL_MREMAP(sp->base, sp->size, newsize, 0) != MFAIL) || + (CALL_MUNMAP(sp->base + newsize, extra) == 0)) { + released = extra; + } + } + } + else if (HAVE_MORECORE) { + if (extra >= HALF_MAX_SIZE_T) /* Avoid wrapping negative */ + extra = (HALF_MAX_SIZE_T) + SIZE_T_ONE - unit; + ACQUIRE_MORECORE_LOCK(); + { + /* Make sure end of memory is where we last set it. */ + char* old_br = (char*)(CALL_MORECORE(0)); + if (old_br == sp->base + sp->size) { + char* rel_br = (char*)(CALL_MORECORE(-extra)); + char* new_br = (char*)(CALL_MORECORE(0)); + if (rel_br != CMFAIL && new_br < old_br) + released = old_br - new_br; + } + } + RELEASE_MORECORE_LOCK(); + } + } + + if (released != 0) { + sp->size -= released; + m->footprint -= released; + init_top(m, m->top, m->topsize - released); + check_top_chunk(m, m->top); + } + } + + /* Unmap any unused mmapped segments */ + if (HAVE_MMAP) + released += release_unused_segments(m); + + /* On failure, disable autotrim to avoid repeated failed future calls */ + if (released == 0) + m->trim_check = MAX_SIZE_T; + } + + return (released != 0)? 1 : 0; +} + +/* ---------------------------- malloc support --------------------------- */ + +/* allocate a large request from the best fitting chunk in a treebin */ +static void* tmalloc_large(mstate m, size_t nb) { + tchunkptr v = 0; + size_t rsize = -nb; /* Unsigned negation */ + tchunkptr t; + bindex_t idx; + compute_tree_index(nb, idx); + + if ((t = *treebin_at(m, idx)) != 0) { + /* Traverse tree for this bin looking for node with size == nb */ + size_t sizebits = nb << leftshift_for_tree_index(idx); + tchunkptr rst = 0; /* The deepest untaken right subtree */ + for (;;) { + tchunkptr rt; + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + v = t; + if ((rsize = trem) == 0) + break; + } + rt = t->child[1]; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + if (rt != 0 && rt != t) + rst = rt; + if (t == 0) { + t = rst; /* set t to least subtree holding sizes > nb */ + break; + } + sizebits <<= 1; + } + } + + if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ + binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; + if (leftbits != 0) { + bindex_t i; + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + t = *treebin_at(m, i); + } + } + + while (t != 0) { /* find smallest of tree or subtree */ + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + t = leftmost_child(t); + } + + /* If dv is a better fit, return 0 so malloc will use it */ + if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { + if (RTCHECK(ok_address(m, v))) { /* split */ + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(m, r, rsize); + } + return chunk2mem(v); + } + } + CORRUPTION_ERROR_ACTION(m); + } + return 0; +} + +/* allocate a small request from the best fitting chunk in a treebin */ +static void* tmalloc_small(mstate m, size_t nb) { + tchunkptr t, v; + size_t rsize; + bindex_t i; + binmap_t leastbit = least_bit(m->treemap); + compute_bit2idx(leastbit, i); + + v = t = *treebin_at(m, i); + rsize = chunksize(t) - nb; + + while ((t = leftmost_child(t)) != 0) { + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + } + + if (RTCHECK(ok_address(m, v))) { + mchunkptr r = chunk_plus_offset(v, nb); + assert(chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(m, r, rsize); + } + return chunk2mem(v); + } + } + + CORRUPTION_ERROR_ACTION(m); + return 0; +} + +/* --------------------------- realloc support --------------------------- */ + +static void* internal_realloc(mstate m, void* oldmem, size_t bytes) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + return 0; + } + if (!PREACTION(m)) { + mchunkptr oldp = mem2chunk(oldmem); + size_t oldsize = chunksize(oldp); + mchunkptr next = chunk_plus_offset(oldp, oldsize); + mchunkptr newp = 0; + void* extra = 0; + + /* Try to either shrink or extend into top. Else malloc-copy-free */ + + if (RTCHECK(ok_address(m, oldp) && ok_cinuse(oldp) && + ok_next(oldp, next) && ok_pinuse(next))) { + size_t nb = request2size(bytes); + if (is_mmapped(oldp)) + newp = mmap_resize(m, oldp, nb); + else if (oldsize >= nb) { /* already big enough */ + size_t rsize = oldsize - nb; + newp = oldp; + if (rsize >= MIN_CHUNK_SIZE) { + mchunkptr remainder = chunk_plus_offset(newp, nb); + set_inuse(m, newp, nb); + set_inuse(m, remainder, rsize); + extra = chunk2mem(remainder); + } + } + else if (next == m->top && oldsize + m->topsize > nb) { + /* Expand into top */ + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; + mchunkptr newtop = chunk_plus_offset(oldp, nb); + set_inuse(m, oldp, nb); + newtop->head = newtopsize |PINUSE_BIT; + m->top = newtop; + m->topsize = newtopsize; + newp = oldp; + } + } + else { + USAGE_ERROR_ACTION(m, oldmem); + POSTACTION(m); + return 0; + } + + POSTACTION(m); + + if (newp != 0) { + if (extra != 0) { + internal_free(m, extra); + } + check_inuse_chunk(m, newp); + return chunk2mem(newp); + } + else { + void* newmem = internal_malloc(m, bytes); + if (newmem != 0) { + size_t oc = oldsize - overhead_for(oldp); + memcpy(newmem, oldmem, (oc < bytes)? oc : bytes); + internal_free(m, oldmem); + } + return newmem; + } + } + return 0; +} + +/* --------------------------- memalign support -------------------------- */ + +static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { + if (alignment <= MALLOC_ALIGNMENT) /* Can just use malloc */ + return internal_malloc(m, bytes); + if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ + alignment = MIN_CHUNK_SIZE; + if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ + size_t a = MALLOC_ALIGNMENT << 1; + while (a < alignment) a <<= 1; + alignment = a; + } + + if (bytes >= MAX_REQUEST - alignment) { + if (m != 0) { /* Test isn't needed but avoids compiler warning */ + MALLOC_FAILURE_ACTION; + } + } + else { + size_t nb = request2size(bytes); + size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; + char* mem = (char*)internal_malloc(m, req); + if (mem != 0) { + void* leader = 0; + void* trailer = 0; + mchunkptr p = mem2chunk(mem); + + if (PREACTION(m)) return 0; + if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */ + /* + Find an aligned spot inside chunk. Since we need to give + back leading space in a chunk of at least MIN_CHUNK_SIZE, if + the first calculation places us at a spot with less than + MIN_CHUNK_SIZE leader, we can move to the next aligned spot. + We've allocated enough total room so that this is always + possible. + */ + char* br = (char*)mem2chunk((size_t)(((size_t)(mem + + alignment - + SIZE_T_ONE)) & + -alignment)); + char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? + br : br+alignment; + mchunkptr newp = (mchunkptr)pos; + size_t leadsize = pos - (char*)(p); + size_t newsize = chunksize(p) - leadsize; + + if (is_mmapped(p)) { /* For mmapped chunks, just adjust offset */ + newp->prev_foot = p->prev_foot + leadsize; + newp->head = (newsize|CINUSE_BIT); + } + else { /* Otherwise, give back leader, use the rest */ + set_inuse(m, newp, newsize); + set_inuse(m, p, leadsize); + leader = chunk2mem(p); + } + p = newp; + } + + /* Give back spare room at the end */ + if (!is_mmapped(p)) { + size_t size = chunksize(p); + if (size > nb + MIN_CHUNK_SIZE) { + size_t remainder_size = size - nb; + mchunkptr remainder = chunk_plus_offset(p, nb); + set_inuse(m, p, nb); + set_inuse(m, remainder, remainder_size); + trailer = chunk2mem(remainder); + } + } + + assert (chunksize(p) >= nb); + assert((((size_t)(chunk2mem(p))) % alignment) == 0); + check_inuse_chunk(m, p); + POSTACTION(m); + if (leader != 0) { + internal_free(m, leader); + } + if (trailer != 0) { + internal_free(m, trailer); + } + return chunk2mem(p); + } + } + return 0; +} + +/* ------------------------ comalloc/coalloc support --------------------- */ + +static void** ialloc(mstate m, + size_t n_elements, + size_t* sizes, + int opts, + void* chunks[]) { + /* + This provides common support for independent_X routines, handling + all of the combinations that can result. + + The opts arg has: + bit 0 set if all elements are same size (using sizes[0]) + bit 1 set if elements should be zeroed + */ + + size_t element_size; /* chunksize of each element, if all same */ + size_t contents_size; /* total size of elements */ + size_t array_size; /* request size of pointer array */ + void* mem; /* malloced aggregate space */ + mchunkptr p; /* corresponding chunk */ + size_t remainder_size; /* remaining bytes while splitting */ + void** marray; /* either "chunks" or malloced ptr array */ + mchunkptr array_chunk; /* chunk for malloced ptr array */ + flag_t was_enabled; /* to disable mmap */ + size_t size; + size_t i; + + /* compute array length, if needed */ + if (chunks != 0) { + if (n_elements == 0) + return chunks; /* nothing to do */ + marray = chunks; + array_size = 0; + } + else { + /* if empty req, must still return chunk representing empty array */ + if (n_elements == 0) + return (void**)internal_malloc(m, 0); + marray = 0; + array_size = request2size(n_elements * (sizeof(void*))); + } + + /* compute total element size */ + if (opts & 0x1) { /* all-same-size */ + element_size = request2size(*sizes); + contents_size = n_elements * element_size; + } + else { /* add up all the sizes */ + element_size = 0; + contents_size = 0; + for (i = 0; i != n_elements; ++i) + contents_size += request2size(sizes[i]); + } + + size = contents_size + array_size; + + /* + Allocate the aggregate chunk. First disable direct-mmapping so + malloc won't use it, since we would not be able to later + free/realloc space internal to a segregated mmap region. + */ + was_enabled = use_mmap(m); + disable_mmap(m); + mem = internal_malloc(m, size - CHUNK_OVERHEAD); + if (was_enabled) + enable_mmap(m); + if (mem == 0) + return 0; + + if (PREACTION(m)) return 0; + p = mem2chunk(mem); + remainder_size = chunksize(p); + + assert(!is_mmapped(p)); + + if (opts & 0x2) { /* optionally clear the elements */ + memset((size_t*)mem, 0, remainder_size - SIZE_T_SIZE - array_size); + } + + /* If not provided, allocate the pointer array as final part of chunk */ + if (marray == 0) { + size_t array_chunk_size; + array_chunk = chunk_plus_offset(p, contents_size); + array_chunk_size = remainder_size - contents_size; + marray = (void**) (chunk2mem(array_chunk)); + set_size_and_pinuse_of_inuse_chunk(m, array_chunk, array_chunk_size); + remainder_size = contents_size; + } + + /* split out elements */ + for (i = 0; ; ++i) { + marray[i] = chunk2mem(p); + if (i != n_elements-1) { + if (element_size != 0) + size = element_size; + else + size = request2size(sizes[i]); + remainder_size -= size; + set_size_and_pinuse_of_inuse_chunk(m, p, size); + p = chunk_plus_offset(p, size); + } + else { /* the final element absorbs any overallocation slop */ + set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); + break; + } + } + +#ifdef DEBUG + if (marray != chunks) { + /* final element must have exactly exhausted chunk */ + if (element_size != 0) { + assert(remainder_size == element_size); + } + else { + assert(remainder_size == request2size(sizes[i])); + } + check_inuse_chunk(m, mem2chunk(marray)); + } + for (i = 0; i != n_elements; ++i) + check_inuse_chunk(m, mem2chunk(marray[i])); + +#endif /* DEBUG */ + + POSTACTION(m); + return marray; +} + + +/* -------------------------- public routines ---------------------------- */ + +#if !ONLY_MSPACES + +void* dlmalloc(size_t bytes) { + /* + Basic algorithm: + If a small request (< 256 bytes minus per-chunk overhead): + 1. If one exists, use a remainderless chunk in associated smallbin. + (Remainderless means that there are too few excess bytes to + represent as a chunk.) + 2. If it is big enough, use the dv chunk, which is normally the + chunk adjacent to the one used for the most recent small request. + 3. If one exists, split the smallest available chunk in a bin, + saving remainder in dv. + 4. If it is big enough, use the top chunk. + 5. If available, get memory from system and use it + Otherwise, for a large request: + 1. Find the smallest available binned chunk that fits, and use it + if it is better fitting than dv chunk, splitting if necessary. + 2. If better fitting than any binned chunk, use the dv chunk. + 3. If it is big enough, use the top chunk. + 4. If request size >= mmap threshold, try to directly mmap this chunk. + 5. If available, get memory from system and use it + + The ugly goto's here ensure that postaction occurs along all paths. + */ + + if (!PREACTION(gm)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = gm->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(gm, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(gm, b, p, idx); + set_inuse_and_pinuse(gm, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb > gm->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(gm, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(gm, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(gm, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(gm, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (gm->treemap != 0 && (mem = tmalloc_small(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (gm->treemap != 0 && (mem = tmalloc_large(gm, nb)) != 0) { + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + } + + if (nb <= gm->dvsize) { + size_t rsize = gm->dvsize - nb; + mchunkptr p = gm->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = gm->dv = chunk_plus_offset(p, nb); + gm->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + } + else { /* exhaust dv */ + size_t dvs = gm->dvsize; + gm->dvsize = 0; + gm->dv = 0; + set_inuse_and_pinuse(gm, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + else if (nb < gm->topsize) { /* Split top */ + size_t rsize = gm->topsize -= nb; + mchunkptr p = gm->top; + mchunkptr r = gm->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(gm, p, nb); + mem = chunk2mem(p); + check_top_chunk(gm, gm->top); + check_malloced_chunk(gm, mem, nb); + goto postaction; + } + + mem = sys_alloc(gm, nb); + + postaction: + POSTACTION(gm); + return mem; + } + + return 0; +} + +void dlfree(void* mem) { + /* + Consolidate freed chunks with preceding or succeeding bordering + free chunks, if they exist, and then place in a bin. Intermixed + with special cases for top, dv, mmapped chunks, and usage errors. + */ + + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } +#else /* FOOTERS */ +#define fm gm +#endif /* FOOTERS */ + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if ((prevsize & IS_MMAPPED_BIT) != 0) { + prevsize &= ~IS_MMAPPED_BIT; + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + insert_chunk(fm, p, psize); + check_free_chunk(fm, p); + goto postaction; + } + } + erroraction: + USAGE_ERROR_ACTION(fm, p); + postaction: + POSTACTION(fm); + } + } +#if !FOOTERS +#undef fm +#endif /* FOOTERS */ +} + +void* dlcalloc(size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = dlmalloc(req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + memset(mem, 0, req); + return mem; +} + +void* dlrealloc(void* oldmem, size_t bytes) { + if (oldmem == 0) + return dlmalloc(bytes); +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { + dlfree(oldmem); + return 0; + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { +#if ! FOOTERS + mstate m = gm; +#else /* FOOTERS */ + mstate m = get_mstate_for(mem2chunk(oldmem)); + if (!ok_magic(m)) { + USAGE_ERROR_ACTION(m, oldmem); + return 0; + } +#endif /* FOOTERS */ + return internal_realloc(m, oldmem, bytes); + } +} + +void* dlmemalign(size_t alignment, size_t bytes) { + return internal_memalign(gm, alignment, bytes); +} + +void** dlindependent_calloc(size_t n_elements, size_t elem_size, + void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + return ialloc(gm, n_elements, &sz, 3, chunks); +} + +void** dlindependent_comalloc(size_t n_elements, size_t sizes[], + void* chunks[]) { + return ialloc(gm, n_elements, sizes, 0, chunks); +} + +void* dlvalloc(size_t bytes) { + size_t pagesz; + init_mparams(); + pagesz = mparams.page_size; + return dlmemalign(pagesz, bytes); +} + +void* dlpvalloc(size_t bytes) { + size_t pagesz; + init_mparams(); + pagesz = mparams.page_size; + return dlmemalign(pagesz, (bytes + pagesz - SIZE_T_ONE) & ~(pagesz - SIZE_T_ONE)); +} + +int dlmalloc_trim(size_t pad) { + int result = 0; + if (!PREACTION(gm)) { + result = sys_trim(gm, pad); + POSTACTION(gm); + } + return result; +} + +size_t dlmalloc_footprint(void) { + return gm->footprint; +} + +size_t dlmalloc_max_footprint(void) { + return gm->max_footprint; +} + +#if !NO_MALLINFO +struct _mallinfo dlmallinfo(void) { + return internal_mallinfo(gm); +} +#endif /* NO_MALLINFO */ + +void dlmalloc_stats(void) { + internal_malloc_stats(gm); +} + +size_t dlmalloc_usable_size(void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + if (cinuse(p)) + return chunksize(p) - overhead_for(p); + } + return 0; +} + +int dlmallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +#endif /* !ONLY_MSPACES */ + +/* ----------------------------- user mspaces ---------------------------- */ + +#if MSPACES + +static mstate init_user_mstate(char* tbase, size_t tsize) { + size_t msize = pad_request(sizeof(struct malloc_state)); + mchunkptr mn; + mchunkptr msp = align_as_chunk(tbase); + mstate m = (mstate)(chunk2mem(msp)); + memset(m, 0, msize); + INITIAL_LOCK(&m->mutex); + msp->head = (msize|PINUSE_BIT|CINUSE_BIT); + m->seg.base = m->least_addr = tbase; + m->seg.size = m->footprint = m->max_footprint = tsize; + m->magic = mparams.magic; + m->mflags = mparams.default_mflags; + disable_contiguous(m); + init_bins(m); + mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); + check_top_chunk(m, m->top); + return m; +} + +mspace create_mspace(size_t capacity, int locked) { + mstate m = 0; + size_t msize = pad_request(sizeof(struct malloc_state)); + init_mparams(); /* Ensure pagesize etc initialized */ + + if (capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + size_t rs = ((capacity == 0)? mparams.granularity : + (capacity + TOP_FOOT_SIZE + msize)); + size_t tsize = granularity_align(rs); + char* tbase = (char*)(CALL_MMAP(tsize)); + if (tbase != CMFAIL) { + m = init_user_mstate(tbase, tsize); + m->seg.sflags = IS_MMAPPED_BIT; + set_lock(m, locked); + } + } + return (mspace)m; +} + +mspace create_mspace_with_base(void* base, size_t capacity, int locked) { + mstate m = 0; + size_t msize = pad_request(sizeof(struct malloc_state)); + init_mparams(); /* Ensure pagesize etc initialized */ + + if (capacity > msize + TOP_FOOT_SIZE && + capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + m = init_user_mstate((char*)base, capacity); + m->seg.sflags = EXTERN_BIT; + set_lock(m, locked); + } + return (mspace)m; +} + +size_t destroy_mspace(mspace msp) { + size_t freed = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + msegmentptr sp = &ms->seg; + while (sp != 0) { + char* base = sp->base; + size_t size = sp->size; + flag_t flag = sp->sflags; + sp = sp->next; + if ((flag & IS_MMAPPED_BIT) && !(flag & EXTERN_BIT) && + CALL_MUNMAP(base, size) == 0) + freed += size; + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return freed; +} + +/* + mspace versions of routines are near-clones of the global + versions. This is not so nice but better than the alternatives. +*/ + + +void* mspace_malloc(mspace msp, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (!PREACTION(ms)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = ms->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(ms, idx); + p = b->fd; + assert(chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(ms, b, p, idx); + set_inuse_and_pinuse(ms, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb > ms->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(ms, i); + p = b->fd; + assert(chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(ms, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(ms, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(ms, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + + if (nb <= ms->dvsize) { + size_t rsize = ms->dvsize - nb; + mchunkptr p = ms->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = ms->dv = chunk_plus_offset(p, nb); + ms->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + } + else { /* exhaust dv */ + size_t dvs = ms->dvsize; + ms->dvsize = 0; + ms->dv = 0; + set_inuse_and_pinuse(ms, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb < ms->topsize) { /* Split top */ + size_t rsize = ms->topsize -= nb; + mchunkptr p = ms->top; + mchunkptr r = ms->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + mem = chunk2mem(p); + check_top_chunk(ms, ms->top); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + mem = sys_alloc(ms, nb); + + postaction: + POSTACTION(ms); + return mem; + } + + return 0; +} + +void mspace_free(mspace msp, void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); +#else /* FOOTERS */ + mstate fm = (mstate)msp; +#endif /* FOOTERS */ + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + if ((prevsize & IS_MMAPPED_BIT) != 0) { + prevsize &= ~IS_MMAPPED_BIT; + psize += prevsize + MMAP_FOOT_PAD; + if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) + fm->footprint -= psize; + goto postaction; + } + else { + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + if (should_trim(fm, tsize)) + sys_trim(fm, 0); + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + insert_chunk(fm, p, psize); + check_free_chunk(fm, p); + goto postaction; + } + } + erroraction: + USAGE_ERROR_ACTION(fm, p); + postaction: + POSTACTION(fm); + } + } +} + +void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = internal_malloc(ms, req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + memset(mem, 0, req); + return mem; +} + +void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) { + if (oldmem == 0) + return mspace_malloc(msp, bytes); +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { + mspace_free(msp, oldmem); + return 0; + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { +#if FOOTERS + mchunkptr p = mem2chunk(oldmem); + mstate ms = get_mstate_for(p); +#else /* FOOTERS */ + mstate ms = (mstate)msp; +#endif /* FOOTERS */ + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return internal_realloc(ms, oldmem, bytes); + } +} + +void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return internal_memalign(ms, alignment, bytes); +} + +void** mspace_independent_calloc(mspace msp, size_t n_elements, + size_t elem_size, void* chunks[]) { + size_t sz = elem_size; /* serves as 1-element array */ + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, &sz, 3, chunks); +} + +void** mspace_independent_comalloc(mspace msp, size_t n_elements, + size_t sizes[], void* chunks[]) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return ialloc(ms, n_elements, sizes, 0, chunks); +} + +int mspace_trim(mspace msp, size_t pad) { + int result = 0; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + if (!PREACTION(ms)) { + result = sys_trim(ms, pad); + POSTACTION(ms); + } + } + else { + USAGE_ERROR_ACTION(ms,ms); + } + return result; +} + +void mspace_malloc_stats(mspace msp) { + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + internal_malloc_stats(ms); + } + else { + USAGE_ERROR_ACTION(ms,ms); + } +} + +size_t mspace_footprint(mspace msp) { + size_t result; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->footprint; + } + USAGE_ERROR_ACTION(ms,ms); + return result; +} + + +size_t mspace_max_footprint(mspace msp) { + size_t result; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->max_footprint; + } + USAGE_ERROR_ACTION(ms,ms); + return result; +} + + +#if !NO_MALLINFO +struct _mallinfo mspace_mallinfo(mspace msp) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + } + return internal_mallinfo(ms); +} +#endif /* NO_MALLINFO */ + +int mspace_mallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + +#endif /* MSPACES */ + +/* -------------------- Alternative MORECORE functions ------------------- */ + +/* + Guidelines for creating a custom version of MORECORE: + + * For best performance, MORECORE should allocate in multiples of pagesize. + * MORECORE may allocate more memory than requested. (Or even less, + but this will usually result in a malloc failure.) + * MORECORE must not allocate memory when given argument zero, but + instead return one past the end address of memory from previous + nonzero call. + * For best performance, consecutive calls to MORECORE with positive + arguments should return increasing addresses, indicating that + space has been contiguously extended. + * Even though consecutive calls to MORECORE need not return contiguous + addresses, it must be OK for malloc'ed chunks to span multiple + regions in those cases where they do happen to be contiguous. + * MORECORE need not handle negative arguments -- it may instead + just return MFAIL when given negative arguments. + Negative arguments are always multiples of pagesize. MORECORE + must not misinterpret negative args as large positive unsigned + args. You can suppress all such calls from even occurring by defining + MORECORE_CANNOT_TRIM, + + As an example alternative MORECORE, here is a custom allocator + kindly contributed for pre-OSX macOS. It uses virtually but not + necessarily physically contiguous non-paged memory (locked in, + present and won't get swapped out). You can use it by uncommenting + this section, adding some #includes, and setting up the appropriate + defines above: + + #define MORECORE osMoreCore + + There is also a shutdown routine that should somehow be called for + cleanup upon program exit. + + #define MAX_POOL_ENTRIES 100 + #define MINIMUM_MORECORE_SIZE (64 * 1024U) + static int next_os_pool; + void *our_os_pools[MAX_POOL_ENTRIES]; + + void *osMoreCore(int size) + { + void *ptr = 0; + static void *sbrk_top = 0; + + if (size > 0) + { + if (size < MINIMUM_MORECORE_SIZE) + size = MINIMUM_MORECORE_SIZE; + if (CurrentExecutionLevel() == kTaskLevel) + ptr = PoolAllocateResident(size + RM_PAGE_SIZE, 0); + if (ptr == 0) + { + return (void *) MFAIL; + } + // save ptrs so they can be freed during cleanup + our_os_pools[next_os_pool] = ptr; + next_os_pool++; + ptr = (void *) ((((size_t) ptr) + RM_PAGE_MASK) & ~RM_PAGE_MASK); + sbrk_top = (char *) ptr + size; + return ptr; + } + else if (size < 0) + { + // we don't currently support shrink behavior + return (void *) MFAIL; + } + else + { + return sbrk_top; + } + } + + // cleanup any allocated memory pools + // called as last thing before shutting down driver + + void osCleanupMem(void) + { + void **ptr; + + for (ptr = our_os_pools; ptr < &our_os_pools[MAX_POOL_ENTRIES]; ptr++) + if (*ptr) + { + PoolDeallocate(*ptr); + *ptr = 0; + } + } + +*/ + + +/* ----------------------------------------------------------------------- +History: + V2.8.3 Thu Sep 22 11:16:32 2005 Doug Lea (dl at gee) + * Add max_footprint functions + * Ensure all appropriate literals are size_t + * Fix conditional compilation problem for some #define settings + * Avoid concatenating segments with the one provided + in create_mspace_with_base + * Rename some variables to avoid compiler shadowing warnings + * Use explicit lock initialization. + * Better handling of sbrk interference. + * Simplify and fix segment insertion, trimming and mspace_destroy + * Reinstate REALLOC_ZERO_BYTES_FREES option from 2.7.x + * Thanks especially to Dennis Flanagan for help on these. + + V2.8.2 Sun Jun 12 16:01:10 2005 Doug Lea (dl at gee) + * Fix memalign brace error. + + V2.8.1 Wed Jun 8 16:11:46 2005 Doug Lea (dl at gee) + * Fix improper #endif nesting in C++ + * Add explicit casts needed for C++ + + V2.8.0 Mon May 30 14:09:02 2005 Doug Lea (dl at gee) + * Use trees for large bins + * Support mspaces + * Use segments to unify sbrk-based and mmap-based system allocation, + removing need for emulation on most platforms without sbrk. + * Default safety checks + * Optional footer checks. Thanks to William Robertson for the idea. + * Internal code refactoring + * Incorporate suggestions and platform-specific changes. + Thanks to Dennis Flanagan, Colin Plumb, Niall Douglas, + Aaron Bachmann, Emery Berger, and others. + * Speed up non-fastbin processing enough to remove fastbins. + * Remove useless cfree() to avoid conflicts with other apps. + * Remove internal memcpy, memset. Compilers handle builtins better. + * Remove some options that no one ever used and rename others. + + V2.7.2 Sat Aug 17 09:07:30 2002 Doug Lea (dl at gee) + * Fix malloc_state bitmap array misdeclaration + + V2.7.1 Thu Jul 25 10:58:03 2002 Doug Lea (dl at gee) + * Allow tuning of FIRST_SORTED_BIN_SIZE + * Use PTR_UINT as type for all ptr->int casts. Thanks to John Belmonte. + * Better detection and support for non-contiguousness of MORECORE. + Thanks to Andreas Mueller, Conal Walsh, and Wolfram Gloger + * Bypass most of malloc if no frees. Thanks To Emery Berger. + * Fix freeing of old top non-contiguous chunk im sysmalloc. + * Raised default trim and map thresholds to 256K. + * Fix mmap-related #defines. Thanks to Lubos Lunak. + * Fix copy macros; added LACKS_FCNTL_H. Thanks to Neal Walfield. + * Branch-free bin calculation + * Default trim and mmap thresholds now 256K. + + V2.7.0 Sun Mar 11 14:14:06 2001 Doug Lea (dl at gee) + * Introduce independent_comalloc and independent_calloc. + Thanks to Michael Pachos for motivation and help. + * Make optional .h file available + * Allow > 2GB requests on 32bit systems. + * new WIN32 sbrk, mmap, munmap, lock code from . + Thanks also to Andreas Mueller , + and Anonymous. + * Allow override of MALLOC_ALIGNMENT (Thanks to Ruud Waij for + helping test this.) + * memalign: check alignment arg + * realloc: don't try to shift chunks backwards, since this + leads to more fragmentation in some programs and doesn't + seem to help in any others. + * Collect all cases in malloc requiring system memory into sysmalloc + * Use mmap as backup to sbrk + * Place all internal state in malloc_state + * Introduce fastbins (although similar to 2.5.1) + * Many minor tunings and cosmetic improvements + * Introduce USE_PUBLIC_MALLOC_WRAPPERS, USE_MALLOC_LOCK + * Introduce MALLOC_FAILURE_ACTION, MORECORE_CONTIGUOUS + Thanks to Tony E. Bennett and others. + * Include errno.h to support default failure action. + + V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) + * return null for negative arguments + * Added Several WIN32 cleanups from Martin C. Fong + * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' + (e.g. WIN32 platforms) + * Cleanup header file inclusion for WIN32 platforms + * Cleanup code to avoid Microsoft Visual C++ compiler complaints + * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing + memory allocation routines + * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) + * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to + usage of 'assert' in non-WIN32 code + * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to + avoid infinite loop + * Always call 'fREe()' rather than 'free()' + + V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) + * Fixed ordering problem with boundary-stamping + + V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) + * Added pvalloc, as recommended by H.J. Liu + * Added 64bit pointer support mainly from Wolfram Gloger + * Added anonymously donated WIN32 sbrk emulation + * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen + * malloc_extend_top: fix mask error that caused wastage after + foreign sbrks + * Add linux mremap support code from HJ Liu + + V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) + * Integrated most documentation with the code. + * Add support for mmap, with help from + Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Use last_remainder in more cases. + * Pack bins using idea from colin@nyx10.cs.du.edu + * Use ordered bins instead of best-fit threshold + * Eliminate block-local decls to simplify tracing and debugging. + * Support another case of realloc via move into top + * Fix error occurring when initial sbrk_base not word-aligned. + * Rely on page size for units instead of SBRK_UNIT to + avoid surprises about sbrk alignment conventions. + * Add mallinfo, mallopt. Thanks to Raymond Nijssen + (raymond@es.ele.tue.nl) for the suggestion. + * Add `pad' argument to malloc_trim and top_pad mallopt parameter. + * More precautions for cases where other routines call sbrk, + courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Added macros etc., allowing use in linux libc from + H.J. Lu (hjl@gnu.ai.mit.edu) + * Inverted this history list + + V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) + * Re-tuned and fixed to behave more nicely with V2.6.0 changes. + * Removed all preallocation code since under current scheme + the work required to undo bad preallocations exceeds + the work saved in good cases for most test programs. + * No longer use return list or unconsolidated bins since + no scheme using them consistently outperforms those that don't + given above changes. + * Use best fit for very large chunks to prevent some worst-cases. + * Added some support for debugging + + V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) + * Removed footers when chunks are in use. Thanks to + Paul Wilson (wilson@cs.texas.edu) for the suggestion. + + V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) + * Added malloc_trim, with help from Wolfram Gloger + (wmglo@Dent.MED.Uni-Muenchen.DE). + + V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) + + V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) + * realloc: try to expand in both directions + * malloc: swap order of clean-bin strategy; + * realloc: only conditionally expand backwards + * Try not to scavenge used bins + * Use bin counts as a guide to preallocation + * Occasionally bin return list chunks in first scan + * Add a few optimizations from colin@nyx10.cs.du.edu + + V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) + * faster bin computation & slightly different binning + * merged all consolidations to one part of malloc proper + (eliminating old malloc_find_space & malloc_clean_bin) + * Scan 2 returns chunks (not just 1) + * Propagate failure in realloc if malloc returns 0 + * Add stuff to allow compilation on non-ANSI compilers + from kpv@research.att.com + + V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) + * removed potential for odd address access in prev_chunk + * removed dependency on getpagesize.h + * misc cosmetics and a bit more internal documentation + * anticosmetics: mangled names in macros to evade debugger strangeness + * tested on sparc, hp-700, dec-mips, rs6000 + with gcc & native cc (hp, dec only) allowing + Detlefs & Zorn comparison study (in SIGPLAN Notices.) + + Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) + * Based loosely on libg++-1.2X malloc. (It retains some of the overall + structure of old version, but most details differ.) + +*/ diff --git a/src/base/lib/misc/pgalloc.c b/src/base/lib/misc/pgalloc.c new file mode 100644 index 0000000..9124e4b --- /dev/null +++ b/src/base/lib/misc/pgalloc.c @@ -0,0 +1,154 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: ultra-light page allocator + * + * Author: stsp + * + */ +#include +#include +#include "pgalloc.h" + +void *pgainit(unsigned npages) +{ + int *ret = calloc(npages + 1, sizeof(int)); + ret[0] = npages + 1; // put array len in first element + return ret; +} + +void pgadone(void *pool) +{ + free(pool); +} + +void pgareset(void *pool) +{ + int *p = pool; + int i; + + for (i = 1; i < p[0]; i++) + p[i] = 0; +} + +static int find_free(int *p, unsigned npages) +{ + int i; + + if (npages > p[0]) + return -1; + for (i = 1; i <= p[0] - npages; i++) { + if (p[i] == 0) { + int j; + if (npages == 1) + return i; + for (j = i + 1; j < p[0] && p[j] == 0; j++) { + if (j - i + 1 == npages) + return i; + } + i = j; + } + } + return -1; +} + +#define ID(x) (-(x) - 1) + +int pgaalloc(void *pool, unsigned npages, unsigned id) +{ + int *p = pool; + int idx = find_free(p, npages); + if (idx > 0) { + int i; + p[idx] = ID(id); + for (i = idx + 1; i < idx + npages; i++) + p[i] = i - idx; + } + return idx - 1; +} + +int pgaresize(void *pool, unsigned page, unsigned oldpages, unsigned newpages) +{ + int i; + int *p = pool; + page++; // skip len + assert(page + oldpages < p[0]); + assert(page + newpages < p[0]); + assert(p[page] < 0); + + if (newpages <= oldpages) { /* shrink */ + for (i = newpages; i < oldpages; i++) + p[page+i] = 0; + return page; + } + + /* check if we can expand */ + for (i = oldpages; i < newpages; i++) + if (p[page+i]) + return -1; + + /* allocate the expansion */ + for (i = oldpages; i < newpages; i++) + p[page+i] = i; + return page; +} + +void pgafree(void *pool, unsigned page) +{ + int *p = pool; + page++; // skip len + assert(page < p[0]); + assert(p[page] < 0); + do + p[page++] = 0; + while (page < p[0] && p[page] > 0); +} + +int pgaavail_largest(void *pool) +{ + int *p = pool; + int i, max = 0; + + for (i = 1; i < p[0]; i++) { + if (p[i] == 0) { + int j; + for (j = i + 1; j < p[0] && p[j] == 0; j++); + if (j - i > max) + max = j - i; + i = j; + } + } + return max; +} + +struct pgrm pgarmap(void *pool, unsigned page) +{ + struct pgrm ret = { -1, -1 }; + int *p = pool; + page++; // skip len + assert(page < p[0]); + if (p[page] == 0) + return ret; + ret.pgoff = 0; + if (p[page] > 0) { + ret.pgoff = p[page]; + page -= p[page]; + assert(page > 0 && p[page] < 0); + } + ret.id = ID(p[page]); + return ret; +} diff --git a/src/base/lib/misc/ringbuf.c b/src/base/lib/misc/ringbuf.c new file mode 100644 index 0000000..9df05c8 --- /dev/null +++ b/src/base/lib/misc/ringbuf.c @@ -0,0 +1,302 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: ring buffer API. + * + * Author: Stas Sergeev + */ + +#include +#include +#include +#include +#include +#include "utilities.h" +#include "ringbuf.h" + +void rng_init(struct rng_s *rng, size_t objnum, size_t objsize) +{ + void *buf = calloc(objnum, objsize); + assert(buf); + rng_init_pool(rng, objnum, objsize, buf); + rng->need_free = 1; +} + +void rng_init_pool(struct rng_s *rng, size_t objnum, size_t objsize, void *buf) +{ + rng->buffer = buf; + rng->need_free = 0; + rng->objnum = objnum; + rng->objsize = objsize; + rng->tail = rng->objcnt = 0; + rng->allow_ovw = 1; +} + +void rng_allow_ovw(struct rng_s *rng, int on) +{ + rng->allow_ovw = on; +} + +int rng_destroy(struct rng_s *rng) +{ + int ret = rng->objcnt; + if (rng->need_free) + free(rng->buffer); + rng->buffer = NULL; + rng->objcnt = 0; + return ret; +} + +int rng_get(struct rng_s *rng, void *buf) +{ + if (!rng->objcnt) + return 0; + if (buf) + memcpy(buf, rng->buffer + rng->tail, rng->objsize); + rng->tail += rng->objsize; + rng->tail %= rng->objsize * rng->objnum; + rng->objcnt--; + return 1; +} + +int rng_peek(struct rng_s *rng, unsigned int idx, void *buf) +{ + int obj_pos; + if (rng->objcnt <= idx) + return 0; + obj_pos = (rng->tail + idx * rng->objsize) % (rng->objnum * rng->objsize); + assert(buf); + memcpy(buf, rng->buffer + obj_pos, rng->objsize); + return 1; +} + +int rng_put(struct rng_s *rng, const void *obj) +{ + unsigned int head_pos, ret = 1; + head_pos = (rng->tail + rng->objcnt * rng->objsize) % + (rng->objnum * rng->objsize); + assert(head_pos <= (rng->objnum - 1) * rng->objsize); + assert(rng->objcnt <= rng->objnum); + if (rng->objcnt == rng->objnum && !rng->allow_ovw) + return 0; + memcpy(rng->buffer + head_pos, obj, rng->objsize); + rng->objcnt++; + if (rng->objcnt > rng->objnum) { + rng_get(rng, NULL); + ret = 0; + } + assert(rng->objcnt <= rng->objnum); + return ret; +} + +int rng_put_const(struct rng_s *rng, int value) +{ + assert(rng->objsize <= sizeof(value)); + return rng_put(rng, &value); +} + +int rng_push(struct rng_s *rng, const void *obj) +{ + unsigned int ret = 1; + if (rng->tail < rng->objsize) { + assert(!rng->tail); + rng->tail = rng->objsize * (rng->objnum - 1); + } else { + rng->tail -= rng->objsize; + } + memcpy(rng->buffer + rng->tail, obj, rng->objsize); + rng->objcnt++; + if (rng->objcnt > rng->objnum) { + rng->objcnt--; + ret = 0; + } + assert(rng->objcnt <= rng->objnum); + return ret; +} + +int rng_push_const(struct rng_s *rng, int value) +{ + assert(rng->objsize <= sizeof(value)); + return rng_push(rng, &value); +} + +int rng_poke(struct rng_s *rng, unsigned int idx, void *buf) +{ + int obj_pos; + if (rng->objcnt <= idx) + return 0; + obj_pos = (rng->tail + idx * rng->objsize) % (rng->objnum * rng->objsize); + memcpy(rng->buffer + obj_pos, buf, rng->objsize); + return 1; +} + +int rng_add(struct rng_s *rng, int num, const void *buf) +{ + int i, ret = 0; + for (i = 0; i < num; i++) + ret += rng_put(rng, (const unsigned char *)buf + i * rng->objsize); + return ret; +} + +int rng_remove(struct rng_s *rng, int num, void *buf) +{ + int i, ret = 0; + for (i = 0; (i < num) && rng->objcnt; i++) + ret += rng_get(rng, buf ? (unsigned char *)buf + i * rng->objsize : NULL); + return ret; +} + +int rng_count(struct rng_s *rng) +{ + if (!rng->buffer) + return -1; + return rng->objcnt; +} + +ssize_t rng_get_free_space(struct rng_s *rng) +{ + if (!rng->buffer) + return -1; + return (rng->objnum - rng->objcnt) * rng->objsize; +} + +void rng_clear(struct rng_s *rng) +{ + rng->objcnt = 0; +} + + +/* sequential buffer API */ + +#define SQALIGN(x) (ALIGN(x, sizeof(struct seqitem))) +#define SQ_PSUB(x, y) ((x)->bytes - (y)->bytes) +#define SQ_PINC(x, y) ((x)->bytes + (y)) +#define SQ_PDEC(x, y) ((x)->bytes - (y)) +#define SQ_BEG(s) ((s)->beg->bytes) +#define SQ_END(s) (SQ_BEG(s) + (s)->len) +#define SQ_TAIL(s) ((s)->tail->bytes) +#define SQ_PREV(s) ((s)->prev->bytes) + +int seqbuf_init(struct seqbuf *seq, void *buffer, size_t len) +{ + void *beg = (void *)SQALIGN((uintptr_t)buffer); + seq->beg = beg; + seq->tail = beg; + seq->len = len - (SQ_BEG(seq) - (uint8_t *)buffer); + seq->prev = NULL; + return 0; +} + +static union seqiu *sqcalc_next(struct seqbuf *seq, union seqiu *pit) +{ + size_t opos, pos; + assert(pit); + opos = SQ_PSUB(pit, seq->beg); + pos = opos + sizeof(struct seqitem) + pit->it.len + pit->it.waste; + assert(pos <= seq->len && (pos == SQALIGN(pos) || pos == seq->len)); + return (union seqiu *)(SQ_BEG(seq) + pos); +} + +static union seqiu *sqcalc_next_wrp(struct seqbuf *seq, union seqiu *pit) +{ + union seqiu *itp = sqcalc_next(seq, pit); + if (itp->bytes >= SQ_END(seq)) + itp = seq->beg; + return itp; +} + +static size_t sqcalc_avail(struct seqbuf *seq) +{ + size_t avail1, avail2; + union seqiu *cur; + if (!seq->prev) + return seq->len; + cur = sqcalc_next(seq, seq->prev); + /* cur == tail means full because empty is !prev */ + if (cur <= seq->tail) + return SQ_PSUB(seq->tail, cur); + avail1 = seq->len - SQ_PSUB(cur, seq->beg); + avail2 = SQ_PSUB(seq->tail, seq->beg); + return _max(avail1, avail2); +} + +int seqbuf_write(struct seqbuf *seq, const void *buffer, size_t len) +{ + union seqiu *itp; + size_t avail = sqcalc_avail(seq); + size_t req_len = SQALIGN(len + sizeof(struct seqitem)); + if (avail < req_len || !len) + return 0; + itp = (seq->prev ? sqcalc_next(seq, seq->prev) : seq->beg); + if (SQ_PINC(itp, req_len) > SQ_END(seq)) { + assert(seq->prev); + seq->prev->it.waste += SQ_END(seq) - itp->bytes; + itp = seq->beg; + } + assert(SQ_PINC(itp, req_len) <= SQ_END(seq)); + itp->it.waste = req_len - len; + itp->it.len = len; + memcpy(itp->it.data, buffer, len); + seq->prev = itp; + return len; +} + +static void sqadvance_tail(struct seqbuf *seq) +{ + if (seq->tail == seq->prev) { + seq->prev = NULL; + seq->tail = seq->beg; + } else { + seq->tail = sqcalc_next_wrp(seq, seq->tail); + } +} + +int seqbuf_read(struct seqbuf *seq, void *buffer, size_t len) +{ + union seqiu *itp; + size_t rlen; + if (!seq->prev) + return 0; + itp = seq->tail; + if (len < itp->it.len) + return -itp->it.len; + memcpy(buffer, itp->it.data, itp->it.len); + rlen = itp->it.len; + sqadvance_tail(seq); + return rlen; +} + +void *seqbuf_get(struct seqbuf *seq, size_t *len) +{ + if (!seq->prev) + return NULL; + *len = seq->tail->it.len; + return seq->tail->it.data; +} + +void seqbuf_put(struct seqbuf *seq) +{ + assert(seq->prev); + sqadvance_tail(seq); +} + +size_t seqbuf_get_read_len(struct seqbuf *seq) +{ + if (!seq->prev) + return 0; + return seq->tail->it.len; +} diff --git a/src/base/lib/misc/sequencr.c b/src/base/lib/misc/sequencr.c new file mode 100644 index 0000000..98154e9 --- /dev/null +++ b/src/base/lib/misc/sequencr.c @@ -0,0 +1,168 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * simple sequencer library. + * + * Author: @stsp + * + */ +#include +#include +#include +#include "dosemu_debug.h" +#include "sequencr.h" + +struct seq_tag_s { + int tag; + int data; +}; + +#define MAX_TAGS 5 +struct seq_item_s { + struct seq_item_s *next; + unsigned long long tstamp; + struct seq_tag_s tags[MAX_TAGS]; + int ntags; +}; + +struct seq_s { + struct seq_item_s *head; + struct seq_item_s *tail; + pthread_mutex_t seq_mtx; +}; + +void *sequencer_init(void) +{ + struct seq_s *s = malloc(sizeof(*s)); + assert(s); + s->head = s->tail = NULL; + pthread_mutex_init(&s->seq_mtx, NULL); + return s; +} + +static struct seq_item_s *do_get(struct seq_s *s) +{ + struct seq_item_s *ret = s->head; + if (!ret) + return NULL; + s->head = ret->next; + if (!s->head) + s->tail = NULL; + return ret; +} + +static void do_free(struct seq_item_s *i) +{ + free(i); +} + +static void do_clear(struct seq_s *s) +{ + while (s->head) { + struct seq_item_s *i = do_get(s); + do_free(i); + } +} + +void sequencer_done(void *handle) +{ + struct seq_s *s = handle; + do_clear(s); + pthread_mutex_destroy(&s->seq_mtx); + free(s); +} + +void sequencer_clear(void *handle) +{ + struct seq_s *s = handle; + pthread_mutex_lock(&s->seq_mtx); + do_clear(s); + pthread_mutex_unlock(&s->seq_mtx); +} + +struct seq_item_s *sequencer_add(void *handle, unsigned long long tstamp) +{ + struct seq_s *s = handle; + struct seq_item_s *i; + + i = malloc(sizeof(*i)); + assert(i); + i->next = NULL; + i->tstamp = tstamp; + i->ntags = 0; + + pthread_mutex_lock(&s->seq_mtx); + if (s->tail) { + if (tstamp < s->tail->tstamp) { + error("time goes backwards? %lli < %lli\n", tstamp, s->tail->tstamp); + i->tstamp = s->tail->tstamp; + } + s->tail->next = i; + } else { + assert(!s->head); + s->head = i; + } + s->tail = i; + pthread_mutex_unlock(&s->seq_mtx); + return i; +} + +void sequencer_add_tag(struct seq_item_s *i, int tag, int data) +{ + assert(i->ntags < MAX_TAGS); + i->tags[i->ntags].tag = tag; + i->tags[i->ntags].data = data; + i->ntags++; +} + +void *sequencer_get(void *handle) +{ + struct seq_s *s = handle; + struct seq_item_s *i; + + pthread_mutex_lock(&s->seq_mtx); + i = do_get(s); + pthread_mutex_unlock(&s->seq_mtx); + return i; +} + +unsigned long long sequencer_get_next(void *handle) +{ + struct seq_s *s = handle; + unsigned long long ret = 0; + + pthread_mutex_lock(&s->seq_mtx); + if (s->head) + ret = s->head->tstamp; + pthread_mutex_unlock(&s->seq_mtx); + return ret; +} + +void sequencer_free(struct seq_item_s *item) +{ + do_free(item); +} + +int sequencer_find(struct seq_item_s *item, int tag) +{ + int i; + for (i = 0; i < item->ntags; i++) { + if (item->tags[i].tag == tag) + return item->tags[i].data; + } + return -1; +} diff --git a/src/base/lib/misc/shlock.c b/src/base/lib/misc/shlock.c new file mode 100644 index 0000000..3b0c727 --- /dev/null +++ b/src/base/lib/misc/shlock.c @@ -0,0 +1,250 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * shlock - shared UUCP-style lockfile library. + * Provides inter-process read/write locks and refcounting. + * Refcounting is provided by the means of an unlock operation + * returning an indication of "no more locks on that resource". + * + * This implementation provides the lock-free operations, i.e. it + * doesn't use any locking internally. Which makes it a bit complex, + * as we lack the atomic open-and-lock operation, unlink-from-fd + * (funlink()/frmdir()) functions and a few other things to liverage + * the simple lock-free implementation. + * Also it strives to provide the race-free operations with the + * predictable behavior in all cases, but the callers are advised + * to use the synchronization to avoid locking and unlocking the + * same resources simultaneously from different processes. + * + * Author: @stsp + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "shlock.h" + +#define LOCK_DIR "/tmp" +#define LOCK_PFX "LCK.." + +struct shlck { + char *tspec; + char *fspec; + char *dir; + int excl; + int fd; + int tmp_fd; +}; + +static void do_gc(const char *fspec) +{ + char *pat; + glob_t gl; + int rc; + + asprintf(&pat, "%s/" LOCK_PFX "*", fspec); + rc = glob(pat, GLOB_ERR | GLOB_NOSORT | GLOB_NOESCAPE, NULL, &gl); + if (rc == 0) { + int i; + for (i = 0; i < gl.gl_pathc; i++) { + int fd = open(gl.gl_pathv[i], O_RDONLY); + if (fd == -1) + continue; + /* tmpfiles are not participating in locking, so we can use + * LOCK_NB safely */ + rc = flock(fd, LOCK_SH | LOCK_NB); + if (rc == 0) + unlink(gl.gl_pathv[i]); + close(fd); + } + globfree(&gl); + } + free(pat); +} + +static char *fixupspec(char *fspec, const char *dir1, const char *dir2) +{ + char *ret; + int len = strlen(dir1); + assert(fspec[len] == '/'); + asprintf(&ret, "%s%s", dir2, fspec + len); + return ret; +} + +void *shlock_open(const char *dir, const char *name, int excl, int block) +{ + struct shlck *ret; + char *fspec, *dspec, *tspec, *ttspec, *dtspec; + int fd, tmp_fd, rc; + int flg = block ? 0 : LOCK_NB; + + rc = asprintf(&dspec, LOCK_DIR "/%s", dir); + assert(rc != -1); + rc = asprintf(&fspec, "%s/%s", dspec, name); + assert(rc != -1); + rc = asprintf(&dtspec, LOCK_DIR "/%s.XXXXXX", name); + assert(rc != -1); + /* create tmp dir */ + mkdtemp(dtspec); + rc = asprintf(&ttspec, "%s/" LOCK_PFX "%i_XXXXXX", dtspec, getpid()); + assert(rc != -1); + + /* create tmp file in tmp dir */ + tmp_fd = mkstemp(ttspec); + if (tmp_fd == -1) { + perror("mkstemp()"); + rmdir(dtspec); + goto err_free_d; + } + tspec = fixupspec(ttspec, dtspec, fspec); + /* lock it before exposing */ + rc = flock(tmp_fd, LOCK_EX); + if (rc == -1) { + perror("flock(tmp)"); + rmdir(dtspec); + goto err_clotmp; + } + + rc = -1; + while (rc == -1) { + rc = mkdir(dspec, 0775); + if (rc == -1 && errno != EEXIST) { + perror("mkdir()"); + unlink(ttspec); + rmdir(dtspec); + goto err_clotmp; + } + /* Try to rename tmp dir. The idea is that fspec dir is never + * created empty, because we rename to it from non-empty one. */ + rc = rename(dtspec, fspec); + if (rc == -1) { + if (errno == ENOENT) + continue; // race, someone removed parent dir + /* couldn't rename the whole dir (fspec exists and not empty), + * so try to move tmp file alone */ + rc = rename(ttspec, tspec); + if (rc == -1) { + if (errno == ENOENT) + continue; // race, someone removed parent dir + perror("rename()"); + unlink(ttspec); + rmdir(dtspec); + goto err_rmddir; + } + rc = rmdir(dtspec); + if (rc == -1) { + perror("rmdir()"); + goto err_rmt; + } + } + } + + free(ttspec); + ttspec = NULL; // exclude from error cleanup + free(dtspec); + dtspec = NULL; // exclude from error cleanup + /* At this point our fspec directory is stable and can't disappear. + * Try collecting stalled tmpfiles. */ + do_gc(fspec); + + fd = open(fspec, O_RDONLY | O_DIRECTORY); + if (fd == -1) { + perror("open(dir)"); + goto err_rmt; + } + if (excl) { + rc = flock(fd, LOCK_EX | flg); + if (rc == -1) + goto err_close; + } else { + rc = flock(fd, LOCK_SH | flg); + if (rc == -1) { + perror("flock()"); + goto err_close; + } + } + + ret = malloc(sizeof(*ret)); + ret->tspec = tspec; + ret->fspec = fspec; + ret->dir = dspec; + ret->excl = excl; + ret->fd = fd; + ret->tmp_fd = tmp_fd; + return ret; + +err_close: + close(fd); +err_rmt: + unlink(tspec); + rmdir(fspec); +err_rmddir: + rmdir(dspec); +err_clotmp: + close(tmp_fd); + free(tspec); +err_free_d: + free(ttspec); + free(dtspec); + free(fspec); + free(dspec); + return NULL; +} + +int shlock_close(void *handle) +{ + struct shlck *s = handle; + int rc, ret = 0; + + rc = unlink(s->tspec); + if (rc == -1) + perror("unlink()"); + rc = close(s->tmp_fd); + if (rc == -1) + perror("close(tmp)"); + /* At that point someone can remove and re-create our dir. + * The creation loop is designed the way the created dir is never + * empty, so it can't be removed here in that case. + * Note that whoever removes our dir, returns the removal indication + * to the caller. So if the dir doesn't exist currently (no matter + * was it just removed, or removed then re-created then removed again, + * and so on), then we do not return the removal indication, which + * is fine, as that means 1 indication per 1 removal. */ + rc = rmdir(s->fspec); // fails if still not empty + if (rc == 0) + ret++; + /* close() drops OFD lock */ + rc = close(s->fd); + if (rc == -1) + perror("close()"); + rc = rmdir(s->dir); // fails if still not empty + if (rc == 0) + ret++; + + free(s->tspec); + free(s->fspec); + free(s->dir); + free(s); + return ret; +} diff --git a/src/base/lib/misc/smalloc.c b/src/base/lib/misc/smalloc.c new file mode 100644 index 0000000..895902e --- /dev/null +++ b/src/base/lib/misc/smalloc.c @@ -0,0 +1,719 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * smalloc - small memory allocator for dosemu. + * + * Author: Stas Sergeev + */ + +#include +#include +#include +#include +#include +#include +#include "smalloc.h" + +#define POOL_USED(p) (p->mn.used || p->mn.next) +#ifndef _min +#define _min(x, y) ((x) < (y) ? (x) : (y)) +#endif +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif +#define _PAGE_MASK (~(PAGE_SIZE-1)) +#ifndef PAGE_ALIGN +#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&_PAGE_MASK) +#endif + +static void smerror_dummy(int prio, const char *fmt, ...) FORMAT(printf, 2, 3); + +static void (*smerr)(int prio, const char *fmt, ...) + FORMAT(printf, 2, 3) = smerror_dummy; + +static void smerror_dummy(int prio, const char *fmt, ...) +{ +} + +#define smerror(mp, ...) mp->smerr(3, __VA_ARGS__) + +static int do_dump(struct mempool *mp, char *buf, int len) +{ + int pos = 0; + struct memnode *mn; + +#define DO_PRN(...) do { \ + if (pos >= len) \ + return -1; \ + pos += snprintf(buf + pos, len - pos, __VA_ARGS__); \ +} while (0) + DO_PRN("Total size: %zi\n", mp->size); + DO_PRN("Available space: %zi (%zi used)\n", mp->avail, + mp->size - mp->avail); + DO_PRN("Largest free area: %zi\n", smget_largest_free_area(mp)); + DO_PRN("Memory pool dump:\n"); + for (mn = &mp->mn; mn; mn = mn->next) + DO_PRN("\tarea: %zi bytes, %s\n", + mn->size, mn->used ? "used" : "free"); + return 0; +} + +void smdump(struct mempool *mp) +{ + char buf[16384]; + int err = do_dump(mp, buf, sizeof(buf)); + if (!err) + mp->smerr(0, "%s", buf); + else + mp->smerr(3, "dump buffer overflow\n"); +} + +static FORMAT(printf, 3, 4) +void do_smerror(int prio, struct mempool *mp, const char *fmt, ...) +{ + char buf[16384]; + int err; + size_t pos; + va_list al; + + assert(prio != -1); + va_start(al, fmt); + pos = vsnprintf(buf, sizeof(buf), fmt, al); + va_end(al); + err = -1; + if (pos < sizeof(buf)) + err = do_dump(mp, buf + pos, sizeof(buf) - pos); + if (!err) + mp->smerr(0, "%s", buf); + else + mp->smerr(3, "dump buffer overflow\n"); +} + +static int get_oom_pr(struct mempool *mp, size_t size) +{ + if (size <= smget_largest_free_area(mp)) + return -1; + if (size > mp->size) + return 2; + if (size > mp->avail) + return 1; + return 0; +} + +static void sm_uncommit(struct mempool *mp, void *addr, size_t size) +{ + /* align address up and align down size */ + uintptr_t a = (uintptr_t)addr; + uintptr_t aa = PAGE_ALIGN(a); + size_t aligned_size = (size - (aa - a)) & _PAGE_MASK; + void *aligned_addr = (void *)aa; + mp->avail += size; + assert(mp->avail <= mp->size); + if (!mp->uncommit) + return; + mp->uncommit(aligned_addr, aligned_size); +} + +static int __sm_commit(struct mempool *mp, void *addr, size_t size, + void *e_addr, size_t e_size) +{ + if (!mp->commit) + return 1; + if (!mp->commit(addr, size)) { + smerror(mp, "SMALLOC: failed to commit %p %zi\n", addr, size); + if (e_size) + sm_uncommit(mp, e_addr, e_size); + return 0; + } + return 1; +} + +static int sm_commit(struct mempool *mp, void *addr, size_t size, + void *e_addr, size_t e_size) +{ + /* align address down and align up size */ + uintptr_t a = (uintptr_t)addr; + uintptr_t aa = a & _PAGE_MASK; + size_t aligned_size = PAGE_ALIGN(size + (a - aa)); + void *aligned_addr = (void *)aa; + int ok = __sm_commit(mp, aligned_addr, aligned_size, e_addr, e_size); + if (ok) { + assert(mp->avail >= size); + mp->avail -= size; + } + return ok; +} + +static int sm_commit_simple(struct mempool *mp, void *addr, size_t size) +{ + return sm_commit(mp, addr, size, NULL, 0); +} + +static void mntruncate(struct memnode *pmn, size_t size) +{ + int delta = pmn->size - size; + + if (delta == 0) + return; + /* delta can be < 0 */ + if (pmn->next && !pmn->next->used) { + struct memnode *nmn = pmn->next; + + assert(size > 0 && nmn->size + delta >= 0); + + nmn->size += delta; + nmn->mem_area -= delta; + pmn->size -= delta; + if (nmn->size == 0) { + pmn->next = nmn->next; + free(nmn); + assert(!pmn->next || pmn->next->used); + } + } else { + struct memnode *new_mn; + + assert(size < pmn->size); + + new_mn = (struct memnode *)malloc(sizeof(struct memnode)); + new_mn->next = pmn->next; + new_mn->size = delta; + new_mn->used = 0; + new_mn->mem_area = pmn->mem_area + size; + + pmn->next = new_mn; + pmn->size = size; + } +} + +static struct memnode *find_mn(struct mempool *mp, unsigned char *ptr, + struct memnode **prev) +{ + struct memnode *pmn, *mn; + if (!POOL_USED(mp)) { + smerror(mp, "SMALLOC: unused pool passed\n"); + return NULL; + } + for (pmn = NULL, mn = &mp->mn; mn; pmn = mn, mn = mn->next) { + if (mn->mem_area > ptr) + return NULL; + if (mn->mem_area == ptr) { + if (prev) + *prev = pmn; + return mn; + } + } + return NULL; +} + +static struct memnode *find_mn_at(struct mempool *mp, unsigned char *ptr) +{ + struct memnode *mn; + for (mn = &mp->mn; mn; mn = mn->next) { + if (mn->mem_area > ptr) + return NULL; + if (mn->mem_area + mn->size > ptr) + return mn; + } + return NULL; +} + +static struct memnode *smfind_free_area(struct mempool *mp, size_t size) +{ + struct memnode *mn; + for (mn = &mp->mn; mn; mn = mn->next) { + if (!mn->used && mn->size >= size) + return mn; + } + return NULL; +} + +static struct memnode *smfind_free_area_topdown(struct mempool *mp, + unsigned char *top, size_t size) +{ + struct memnode *mn; + struct memnode *mn1 = NULL; + for (mn = &mp->mn; mn; mn = mn->next) { + if (top && mn->mem_area + size > top) + break; + if (!mn->used && mn->size >= size) + mn1 = mn; + } + return mn1; +} + +static struct memnode *sm_alloc_fixed(struct mempool *mp, void *ptr, + size_t size) +{ + struct memnode *mn; + ptrdiff_t delta; + if (!size || !ptr) { + smerror(mp, "SMALLOC: zero-sized allocation attempted\n"); + return NULL; + } + if (!(mn = find_mn_at(mp, (unsigned char *)ptr))) { + smerror(mp, "SMALLOC: invalid address %p on alloc_fixed\n", ptr); + return NULL; + } + if (mn->used) { + do_smerror(0, mp, "SMALLOC: address %p already allocated\n", ptr); + return NULL; + } + delta = (uint8_t *)ptr - mn->mem_area; + assert(delta >= 0); + if (size + delta > mn->size) { + int pr = get_oom_pr(mp, size); + if (pr < 0) + pr = 0; + do_smerror(pr, mp, "SMALLOC: no space %zi at address %p\n", size, ptr); + return NULL; + } + if (delta) { + mntruncate(mn, delta); + mn = mn->next; + assert(!mn->used && mn->size >= size); + } + if (!sm_commit_simple(mp, mn->mem_area, size)) + return NULL; + mn->used = 1; + mntruncate(mn, size); + assert(mn->size == size); + memset(mn->mem_area, 0, size); + return mn; +} + +static struct memnode *sm_alloc_aligned(struct mempool *mp, size_t align, + size_t size) +{ + struct memnode *mn; + int delta; + uintptr_t iptr; + if (!size) { + smerror(mp, "SMALLOC: zero-sized allocation attempted\n"); + return NULL; + } + /* power of 2 align */ + assert(__builtin_popcount(align) == 1); + align--; + if (!(mn = smfind_free_area(mp, size + align))) { + do_smerror(get_oom_pr(mp, size), mp, + "SMALLOC: Out Of Memory on alloc, requested=%zu\n", size); + return NULL; + } + /* insert small node to align the start */ + iptr = (uintptr_t)mn->mem_area; + delta = ((iptr | align) - iptr + 1) & align; + if (delta) { + mntruncate(mn, delta); + mn = mn->next; + assert(!mn->used && mn->size >= size); + } + if (!sm_commit_simple(mp, mn->mem_area, size)) + return NULL; + mn->used = 1; + mntruncate(mn, size); + assert(mn->size == size); + memset(mn->mem_area, 0, size); + return mn; +} + +static struct memnode *sm_alloc_mn(struct mempool *mp, size_t size) +{ + return sm_alloc_aligned(mp, 1, size); +} + +static struct memnode *sm_alloc_aligned_topdown(struct mempool *mp, + unsigned char *top, size_t align, size_t size) +{ + struct memnode *mn; + int delta; + uintptr_t iptr; + uintptr_t min_top; + uintptr_t iend; + if (!size) { + smerror(mp, "SMALLOC: zero-sized allocation attempted\n"); + return NULL; + } + /* power of 2 align */ + assert(__builtin_popcount(align) == 1); + align--; + if (!(mn = smfind_free_area_topdown(mp, top, size + align))) { + do_smerror(get_oom_pr(mp, size), mp, + "SMALLOC: Out Of Memory on alloc, requested=%zu\n", size); + return NULL; + } + /* use top part of the found area */ + min_top = (uintptr_t)mn->mem_area + mn->size; + if (top) + min_top = _min(min_top, (uintptr_t)top); + iptr = (min_top - size) & ~align; + iend = iptr + size; + delta = (uintptr_t)mn->mem_area + mn->size - iend; + if (delta) + mntruncate(mn, mn->size - delta); + assert(iptr >= (uintptr_t)mn->mem_area); + delta = iptr - (uintptr_t)mn->mem_area; + if (delta) { + mntruncate(mn, delta); + mn = mn->next; + assert(!mn->used && mn->size >= size); + } + if (!sm_commit_simple(mp, mn->mem_area, size)) + return NULL; + mn->used = 1; + mntruncate(mn, size); + assert(mn->size == size); + memset(mn->mem_area, 0, size); + return mn; +} + +static struct memnode *sm_alloc_topdown(struct mempool *mp, size_t size) +{ + return sm_alloc_aligned_topdown(mp, NULL, 1, size); +} + +void *smalloc(struct mempool *mp, size_t size) +{ + struct memnode *mn = sm_alloc_mn(mp, size); + if (!mn) + return NULL; + return mn->mem_area; +} + +void *smalloc_fixed(struct mempool *mp, void *ptr, size_t size) +{ + struct memnode *mn = sm_alloc_fixed(mp, ptr, size); + if (!mn) + return NULL; + assert(mn->mem_area == ptr); + return mn->mem_area; +} + +void *smalloc_aligned(struct mempool *mp, size_t align, size_t size) +{ + struct memnode *mn = sm_alloc_aligned(mp, align, size); + if (!mn) + return NULL; + assert(((uintptr_t)mn->mem_area & (align - 1)) == 0); + return mn->mem_area; +} + +void *smalloc_topdown(struct mempool *mp, size_t size) +{ + struct memnode *mn = sm_alloc_topdown(mp, size); + if (!mn) + return NULL; + return mn->mem_area; +} + +void *smalloc_aligned_topdown(struct mempool *mp, unsigned char *top, + size_t align, size_t size) +{ + struct memnode *mn = sm_alloc_aligned_topdown(mp, top, align, size); + if (!mn) + return NULL; + assert(((uintptr_t)mn->mem_area & (align - 1)) == 0); + return mn->mem_area; +} + +int smfree(struct mempool *mp, void *ptr) +{ + struct memnode *mn, *pmn; + if (!ptr) + return -1; + if (!(mn = find_mn(mp, (unsigned char *)ptr, &pmn))) { + smerror(mp, "SMALLOC: bad pointer passed to smfree()\n"); + return -1; + } + if (!mn->used) { + smerror(mp, "SMALLOC: attempt to free the not allocated region (double-free)\n"); + return -1; + } + assert(mn->size > 0); + sm_uncommit(mp, mn->mem_area, mn->size); + mn->used = 0; + if (mn->next && !mn->next->used) { + /* merge with next */ + assert(mn->next->mem_area >= mn->mem_area); + mntruncate(mn, mn->size + mn->next->size); + } + if (pmn && !pmn->used) { + /* merge with prev */ + assert(pmn->mem_area <= mn->mem_area); + mntruncate(pmn, pmn->size + mn->size); + mn = pmn; + } + return 0; +} + +/* part of smrealloc() that covers the cases where the + * extra memnode needs to be allocated for realloc */ +static struct memnode *sm_realloc_alloc_mn(struct mempool *mp, + struct memnode *pmn, struct memnode *mn, + struct memnode *nmn, size_t size) +{ + struct memnode *new_mn; + if (pmn && !pmn->used && pmn->size + mn->size + + (nmn->used ? 0 : nmn->size) >= size) { + /* move to prev memnode */ + size_t psize = _min(size, pmn->size); + if (!sm_commit_simple(mp, pmn->mem_area, psize)) + return NULL; + if (size > pmn->size + mn->size) { + if (!sm_commit(mp, nmn->mem_area, size - pmn->size - mn->size, + pmn->mem_area, psize)) + return NULL; + } + pmn->used = 1; + memmove(pmn->mem_area, mn->mem_area, mn->size); + memset(pmn->mem_area + mn->size, 0, size - mn->size); + mn->used = 0; + if (size < pmn->size + mn->size) { + size_t overl = size > pmn->size ? size - pmn->size : 0; + sm_uncommit(mp, mn->mem_area + overl, mn->size - overl); + } + if (!nmn->used) // merge with next + mntruncate(mn, mn->size + nmn->size); + mntruncate(pmn, size); + new_mn = pmn; + } else { + /* relocate */ + new_mn = sm_alloc_mn(mp, size); + if (!new_mn) { + do_smerror(get_oom_pr(mp, size), mp, + "SMALLOC: Out Of Memory on realloc, requested=%zu\n", size); + return NULL; + } + memcpy(new_mn->mem_area, mn->mem_area, mn->size); + smfree(mp, mn->mem_area); + } + return new_mn; +} + +void *smrealloc(struct mempool *mp, void *ptr, size_t size) +{ + struct memnode *mn, *pmn; + if (!ptr) + return smalloc(mp, size); + if (!(mn = find_mn(mp, (unsigned char *)ptr, &pmn))) { + smerror(mp, "SMALLOC: bad pointer passed to smrealloc()\n"); + return NULL; + } + if (!mn->used) { + smerror(mp, "SMALLOC: attempt to realloc the not allocated region\n"); + return NULL; + } + if (size == 0) { + smfree(mp, ptr); + return NULL; + } + if (size == mn->size) + return ptr; + if (size < mn->size) { + /* shrink */ + sm_uncommit(mp, mn->mem_area + size, mn->size - size); + mntruncate(mn, size); + } else { + /* grow */ + struct memnode *nmn = mn->next; + if (nmn && !nmn->used && mn->size + nmn->size >= size) { + /* expand by shrinking next memnode */ + if (!sm_commit_simple(mp, nmn->mem_area, size - mn->size)) + return NULL; + memset(nmn->mem_area, 0, size - mn->size); + mntruncate(mn, size); + } else { + /* need to allocate new memnode */ + mn = sm_realloc_alloc_mn(mp, pmn, mn, nmn, size); + if (!mn) + return NULL; + } + } + assert(mn->size == size); + return mn->mem_area; +} + +void *smrealloc_aligned(struct mempool *mp, void *ptr, int align, size_t size) +{ + struct memnode *mn, *pmn; + assert(__builtin_popcount(align) == 1); + if (!ptr) + return smalloc_aligned(mp, align, size); + if (!(mn = find_mn(mp, (unsigned char *)ptr, &pmn))) { + smerror(mp, "SMALLOC: bad pointer passed to smrealloc()\n"); + return NULL; + } + if (!mn->used) { + smerror(mp, "SMALLOC: attempt to realloc the not allocated region\n"); + return NULL; + } + if (size == 0) { + smfree(mp, ptr); + return NULL; + } + if (size == mn->size) + return ptr; + if ((uintptr_t)mn->mem_area & (align - 1)) { + smerror(mp, "SMALLOC: unaligned pointer passed to smrealloc_aligned()\n"); + return NULL; + } + if (size < mn->size) { + /* shrink */ + sm_uncommit(mp, mn->mem_area + size, mn->size - size); + mntruncate(mn, size); + } else { + /* grow */ + struct memnode *nmn = mn->next; + if (nmn && !nmn->used && mn->size + nmn->size >= size) { + /* expand by shrinking next memnode */ + if (!sm_commit_simple(mp, nmn->mem_area, size - mn->size)) + return NULL; + memset(nmn->mem_area, 0, size - mn->size); + mntruncate(mn, size); + } else { + /* lazy impl */ + struct memnode *new_mn = sm_alloc_aligned(mp, align, size); + if (!new_mn) + return NULL; + memcpy(new_mn->mem_area, mn->mem_area, mn->size); + smfree(mp, mn->mem_area); + } + } + assert(mn->size == size); + return mn->mem_area; +} + +int sminit(struct mempool *mp, void *start, size_t size) +{ + mp->size = size; + mp->mn.size = size; + mp->mn.used = 0; + mp->mn.next = NULL; + mp->mn.mem_area = (unsigned char *)start; + mp->avail = size; + mp->commit = NULL; + mp->uncommit = NULL; + mp->smerr = smerr; + return 0; +} + +static int do_sminit_com(struct mempool *mp, void *start, size_t size, + int (*commit)(void *area, size_t size), + int (*uncommit)(void *area, size_t size), int do_uncommit) +{ + sminit(mp, start, size); + mp->commit = commit; + mp->uncommit = uncommit; + if (uncommit && do_uncommit) + uncommit(start, size); + return 0; +} + +int sminit_com(struct mempool *mp, void *start, size_t size, + int (*commit)(void *area, size_t size), + int (*uncommit)(void *area, size_t size)) +{ + return do_sminit_com(mp, start, size, commit, uncommit, 1); +} + +int sminit_comu(struct mempool *mp, void *start, size_t size, + int (*commit)(void *area, size_t size), + int (*uncommit)(void *area, size_t size)) +{ + return do_sminit_com(mp, start, size, commit, uncommit, 0); +} + +void smfree_all(struct mempool *mp) +{ + struct memnode *mn; + while (POOL_USED(mp)) { + mn = &mp->mn; + if (!mn->used) + mn = mn->next; + assert(mn && mn->used); + smfree(mp, mn->mem_area); + } + assert(!mp->mn.next); +} + +int smdestroy(struct mempool *mp) +{ + unsigned avail = mp->avail; + + smfree_all(mp); + assert(mp->mn.size >= avail); + /* return leaked size */ + return mp->mn.size - avail; +} + +size_t smget_free_space(struct mempool *mp) +{ + return mp->avail; +} + +size_t smget_free_space_upto(struct mempool *mp, unsigned char *top) +{ + struct memnode *mn; + int cnt = 0; + for (mn = &mp->mn; mn; mn = mn->next) { + if (mn->mem_area + mn->size > top) { + if (!mn->used && mn->mem_area < top) + cnt += top - mn->mem_area; + break; + } + if (!mn->used) + cnt += mn->size; + } + return cnt; +} + +size_t smget_largest_free_area(struct mempool *mp) +{ + struct memnode *mn; + size_t size = 0; + for (mn = &mp->mn; mn; mn = mn->next) { + if (!mn->used && mn->size > size) + size = mn->size; + } + return size; +} + +int smget_area_size(struct mempool *mp, void *ptr) +{ + struct memnode *mn; + if (!(mn = find_mn(mp, (unsigned char *)ptr, NULL))) { + smerror(mp, "SMALLOC: bad pointer passed to smget_area_size()\n"); + return -1; + } + return mn->size; +} + +void *smget_base_addr(struct mempool *mp) +{ + return mp->mn.mem_area; +} + +void smregister_error_notifier(struct mempool *mp, + void (*func)(int prio, const char *fmt, ...) FORMAT(printf, 2, 3)) +{ + mp->smerr = func; +} + +void smregister_default_error_notifier( + void (*func)(int prio, const char *fmt, ...) FORMAT(printf, 2, 3)) +{ + smerr = func; +} diff --git a/src/base/lib/misc/spscq.c b/src/base/lib/misc/spscq.c new file mode 100644 index 0000000..89d1caa --- /dev/null +++ b/src/base/lib/misc/spscq.c @@ -0,0 +1,111 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: single-producer single-consumer queue with locking + * + * Author: stsp + * + */ +#include +#include +#include +#include +#include "utilities.h" +#include "spscq.h" + +struct spscq { + unsigned size; + unsigned rd_pos; + unsigned fillup; + pthread_cond_t wr_cnd; + pthread_mutex_t wr_mtx; + unsigned char data[0]; +}; + +void *spscq_init(unsigned size) +{ + struct spscq *q = malloc(sizeof(*q) + size); + q->size = size; + q->rd_pos = q->fillup = 0; + pthread_cond_init(&q->wr_cnd, NULL); + pthread_mutex_init(&q->wr_mtx, NULL); + return q; +} + +void spscq_done(void *arg) +{ + free(arg); +} + +void *spscq_write_area(void *arg, unsigned *r_len) +{ + struct spscq *q = arg; + unsigned wr_pos, top; + pthread_mutex_lock(&q->wr_mtx); + while (q->fillup == q->size) + cond_wait(&q->wr_cnd, &q->wr_mtx); + wr_pos = q->rd_pos + q->fillup; + if (wr_pos >= q->size) { + wr_pos -= q->size; + top = q->rd_pos; + } else { + top = q->size; + } + pthread_mutex_unlock(&q->wr_mtx); + assert(top > wr_pos); + *r_len = top - wr_pos; + return (q->data + wr_pos); +} + +void spscq_commit_write(void *arg, unsigned len) +{ + struct spscq *q = arg; + pthread_mutex_lock(&q->wr_mtx); + q->fillup += len; + pthread_mutex_unlock(&q->wr_mtx); +} + +int spscq_read(void *arg, void *buf, unsigned len) +{ + struct spscq *q = arg; + unsigned done = 0; + pthread_mutex_lock(&q->wr_mtx); + if (q->fillup) { + unsigned ret; + ret = _min(q->fillup, q->size - q->rd_pos); + ret = _min(ret, len); + memcpy(buf, q->data + q->rd_pos, ret); + q->rd_pos += ret; + if (q->rd_pos == q->size) + q->rd_pos = 0; + q->fillup -= ret; + len -= ret; + done += ret; + if (q->fillup && len) { // can read more + ret = _min(q->fillup, len); + memcpy(buf + done, q->data + q->rd_pos, ret); + q->rd_pos += ret; + q->fillup -= ret; + len -= ret; + done += ret; + } + } + pthread_mutex_unlock(&q->wr_mtx); + if (done) + pthread_cond_signal(&q->wr_cnd); + return done; +} diff --git a/src/base/lib/timer/Makefile b/src/base/lib/timer/Makefile new file mode 100644 index 0000000..ca0a971 --- /dev/null +++ b/src/base/lib/timer/Makefile @@ -0,0 +1,10 @@ +top_builddir = ../../../.. +include $(top_builddir)/Makefile.conf + +ifeq ($(USE_EVTIMER_FD),1) +CFILES = evtimer_fd.c +else +CFILES = evtimer.c +endif + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/lib/timer/evtimer.c b/src/base/lib/timer/evtimer.c new file mode 100644 index 0000000..e67cda2 --- /dev/null +++ b/src/base/lib/timer/evtimer.c @@ -0,0 +1,186 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: event timer API for freebsd. + * + * Author: stsp + * + * Note: SIGEV_THREAD is completely broken. + * timerfd-based impl should be used whereever possible. + * This implementation is provided only as a fall-back for freebsd. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBBSD +#include +#endif +#include "evtimer.h" + +struct evtimer { + timer_t tmr; + void (*callback)(int ticks, void *); + void *arg; + clockid_t clk_id; + struct timespec start; + pthread_mutex_t start_mtx; + int blocked; + pthread_mutex_t block_mtx; + pthread_cond_t block_cnd; + int ticks; + int in_cbk; +}; + +static void evhandler(union sigval sv) +{ + int bl; + struct evtimer *t = sv.sival_ptr; + + pthread_mutex_lock(&t->block_mtx); + bl = t->blocked; + t->ticks += timer_getoverrun(t->tmr) + 1; + if (!bl) + t->in_cbk++; + pthread_mutex_unlock(&t->block_mtx); + if (!bl) { + t->callback(t->ticks, t->arg); + pthread_mutex_lock(&t->block_mtx); + t->in_cbk--; + t->ticks = 0; + pthread_mutex_unlock(&t->block_mtx); + pthread_cond_signal(&t->block_cnd); + } +} + +void *evtimer_create(void (*cbk)(int ticks, void *), void *arg) +{ + struct evtimer *t; + clockid_t id = CLOCK_MONOTONIC; + struct sigevent sev = { .sigev_notify = SIGEV_THREAD, + .sigev_notify_function = evhandler }; + timer_t tmr; + int rc; + + t = malloc(sizeof(*t)); + assert(t); + sev.sigev_value.sival_ptr = t; + rc = timer_create(id, &sev, &tmr); + assert(rc != -1); + t->tmr = tmr; + t->callback = cbk; + t->arg = arg; + t->clk_id = id; + t->blocked = 0; + t->ticks = 0; + t->in_cbk = 0; + pthread_mutex_init(&t->start_mtx, NULL); + pthread_mutex_init(&t->block_mtx, NULL); + pthread_cond_init(&t->block_cnd, NULL); + return t; +} + +void evtimer_delete(void *tmr) +{ + struct evtimer *t = tmr; + + timer_delete(t->tmr); + pthread_mutex_destroy(&t->start_mtx); + pthread_mutex_destroy(&t->block_mtx); + pthread_cond_destroy(&t->block_cnd); + free(t); +} + +void evtimer_set_rel(void *tmr, uint64_t ns, int periodic) +{ + struct evtimer *t = tmr; + struct itimerspec i = {}; + struct timespec rel, abs, start; + + rel.tv_sec = ns / NANOSECONDS_PER_SECOND; + rel.tv_nsec = ns % NANOSECONDS_PER_SECOND; + if (periodic) + i.it_interval = rel; + clock_gettime(t->clk_id, &start); + timespecadd(&start, &rel, &abs); + i.it_value = abs; + timer_settime(t->tmr, TIMER_ABSTIME, &i, NULL); + pthread_mutex_lock(&t->start_mtx); + t->start = start; + pthread_mutex_unlock(&t->start_mtx); +} + +uint64_t evtimer_gettime(void *tmr) +{ + struct evtimer *t = tmr; + struct timespec rel, abs; + + clock_gettime(t->clk_id, &abs); + pthread_mutex_lock(&t->start_mtx); + timespecsub(&abs, &t->start, &rel); + pthread_mutex_unlock(&t->start_mtx); + return (rel.tv_sec * NANOSECONDS_PER_SECOND + rel.tv_nsec); +} + +void evtimer_stop(void *tmr) +{ + struct evtimer *t = tmr; + struct itimerspec i = {}; + struct timespec start; + + timer_settime(t->tmr, 0, &i, NULL); + clock_gettime(t->clk_id, &start); + pthread_mutex_lock(&t->start_mtx); + t->start = start; + pthread_mutex_unlock(&t->start_mtx); +} + +void evtimer_block(void *tmr) +{ + struct evtimer *t = tmr; + + pthread_mutex_lock(&t->block_mtx); + t->blocked++; + while (t->in_cbk) + pthread_cond_wait(&t->block_cnd, &t->block_mtx); + pthread_mutex_unlock(&t->block_mtx); +} + +void evtimer_unblock(void *tmr) +{ + struct evtimer *t = tmr; + int ticks; + + /* if ticks accumulated, quickly deliver them first */ + pthread_mutex_lock(&t->block_mtx); + ticks = t->ticks; + pthread_mutex_unlock(&t->block_mtx); + if (ticks) + t->callback(ticks, t->arg); + /* then unblock */ + pthread_mutex_lock(&t->block_mtx); + t->ticks -= ticks; + t->blocked--; + pthread_mutex_unlock(&t->block_mtx); +} diff --git a/src/base/lib/timer/evtimer_fd.c b/src/base/lib/timer/evtimer_fd.c new file mode 100644 index 0000000..d706f72 --- /dev/null +++ b/src/base/lib/timer/evtimer_fd.c @@ -0,0 +1,286 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: event timer API on top of timerfd. + * + * Author: @stsp + * + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_TIMERFD_CREATE +#include +#else +#include +#include +#endif +#include +#include +#ifdef HAVE_BTHREAD_H +#include +#endif +#include +#ifdef HAVE_LIBBSD +#include +#else +#ifndef timespecadd +#define timespecadd(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ + if ((vsp)->tv_nsec >= 1000000000L) { \ + (vsp)->tv_sec++; \ + (vsp)->tv_nsec -= 1000000000L; \ + } \ + } while (0) +#endif +#endif +#include "utilities.h" +#include "evtimer.h" + +struct evtimer { + int fd; + void (*callback)(int ticks, void *); + void *arg; + clockid_t clk_id; + struct timespec start; + pthread_mutex_t start_mtx; + int blocked; + pthread_mutex_t block_mtx; + pthread_cond_t block_cnd; + pthread_cond_t unblock_cnd; + int in_cbk; + pthread_t thr; +}; + +static void do_callback(struct evtimer *t) +{ + uint64_t ticks; +#ifdef HAVE_TIMERFD_CREATE +again: + int rc = read(t->fd, &ticks, sizeof(ticks)); + if (rc == -1) { + if (errno == EAGAIN) // other thread modified timer + goto again; + perror("read()"); + return; + } +#else + struct kevent event; + int rc = kevent(t->fd, NULL, 0, &event, 1, NULL); + if (rc == -1) + perror("kevent()"); + if (event.flags & EV_ERROR) + error("bad kevent return, %i %s\n", rc, strerror(event.data)); + ticks = event.data; +#endif + + t->callback(ticks, t->arg); +} + +static void *evthread(void *arg) +{ + struct evtimer *t = arg; + + while (1) { + struct pollfd pf = { .fd = t->fd, .events = POLLIN }; + int rc = poll(&pf, 1, -1); + switch (rc) { + case -1: + perror("poll()"); + /* no break */ + case 0: + return NULL; + default: + if (!(pf.revents & POLLIN)) { + error("bad poll return, %i %x\n", rc, pf.revents); + continue; + } + break; + } + + pthread_mutex_lock(&t->block_mtx); + while (t->blocked) + cond_wait(&t->unblock_cnd, &t->block_mtx); + t->in_cbk++; + pthread_mutex_unlock(&t->block_mtx); + + do_callback(t); + + pthread_mutex_lock(&t->block_mtx); + t->in_cbk--; + pthread_mutex_unlock(&t->block_mtx); + pthread_cond_signal(&t->block_cnd); + } + + return NULL; +} + +void *evtimer_create(void (*cbk)(int ticks, void *), void *arg) +{ + struct evtimer *t; + clockid_t id = CLOCK_MONOTONIC; +#ifdef HAVE_TIMERFD_CREATE + int fd = timerfd_create(id, TFD_NONBLOCK | TFD_CLOEXEC); +#else + int fd = kqueue(); + int rc = fcntl(fd, F_GETFD); + if (rc != -1) + rc = fcntl(fd, F_SETFD, rc | FD_CLOEXEC); + assert(rc != -1); +#endif + + assert(fd != -1); + t = malloc(sizeof(*t)); + assert(t); + t->fd = fd; + t->callback = cbk; + t->arg = arg; + t->clk_id = id; + t->blocked = 0; + t->in_cbk = 0; + pthread_mutex_init(&t->start_mtx, NULL); + pthread_mutex_init(&t->block_mtx, NULL); + pthread_cond_init(&t->block_cnd, NULL); + pthread_cond_init(&t->unblock_cnd, NULL); + pthread_create(&t->thr, NULL, evthread, t); +#if defined(HAVE_PTHREAD_SETNAME_NP) && defined(__GLIBC__) + pthread_setname_np(t->thr, "dosemu: evtmr"); +#endif + return t; +} + +void evtimer_delete(void *tmr) +{ + struct evtimer *t = tmr; +#ifdef HAVE_TIMERFD_CREATE + struct itimerspec i = {}; + + timerfd_settime(t->fd, 0, &i, NULL); +#else + struct kevent change; + + EV_SET(&change, 1, EVFILT_TIMER, EV_DELETE, 0, 0, 0); + kevent(t->fd, &change, 1, NULL, 0, NULL); +#endif + pthread_mutex_lock(&t->block_mtx); + t->blocked++; + while (t->in_cbk) + cond_wait(&t->block_cnd, &t->block_mtx); + pthread_mutex_unlock(&t->block_mtx); + pthread_cancel(t->thr); + pthread_join(t->thr, NULL); + + close(t->fd); + pthread_mutex_destroy(&t->start_mtx); + pthread_mutex_destroy(&t->block_mtx); + pthread_cond_destroy(&t->block_cnd); + pthread_cond_destroy(&t->unblock_cnd); + free(t); +} + +void evtimer_set_rel(void *tmr, uint64_t ns, int periodic) +{ + struct evtimer *t = tmr; + struct timespec start; +#ifdef HAVE_TIMERFD_CREATE + struct itimerspec i = {}; + struct timespec rel, abs; + + rel.tv_sec = ns / NANOSECONDS_PER_SECOND; + rel.tv_nsec = ns % NANOSECONDS_PER_SECOND; + if (periodic) + i.it_interval = rel; +#endif + clock_gettime(t->clk_id, &start); +#ifdef HAVE_TIMERFD_CREATE + timespecadd(&start, &rel, &abs); + i.it_value = abs; + timerfd_settime(t->fd, TFD_TIMER_ABSTIME, &i, NULL); +#else + struct kevent change; /* event we want to monitor */ + if (periodic) + EV_SET(&change, 1, EVFILT_TIMER, EV_ADD | EV_ENABLE, NOTE_NSECONDS, ns, 0); + else + EV_SET(&change, 1, EVFILT_TIMER, EV_ONESHOT | EV_ADD | EV_ENABLE, NOTE_NSECONDS, ns, 0); + kevent(t->fd, &change, 1, NULL, 0, NULL); +#endif + pthread_mutex_lock(&t->start_mtx); + t->start = start; + pthread_mutex_unlock(&t->start_mtx); +} + +uint64_t evtimer_gettime(void *tmr) +{ + struct evtimer *t = tmr; + uint64_t rel; + struct timespec abs; + + clock_gettime(t->clk_id, &abs); + pthread_mutex_lock(&t->start_mtx); + rel = abs.tv_sec * NANOSECONDS_PER_SECOND + abs.tv_nsec - + (t->start.tv_sec * NANOSECONDS_PER_SECOND + t->start.tv_nsec); + pthread_mutex_unlock(&t->start_mtx); + return rel; +} + +void evtimer_stop(void *tmr) +{ + struct evtimer *t = tmr; + struct timespec start; +#ifdef HAVE_TIMERFD_CREATE + struct itimerspec i = {}; + + timerfd_settime(t->fd, 0, &i, NULL); +#else + struct kevent change; + + EV_SET(&change, 1, EVFILT_TIMER, EV_DELETE, 0, 0, 0); + kevent(t->fd, &change, 1, NULL, 0, NULL); +#endif + clock_gettime(t->clk_id, &start); + pthread_mutex_lock(&t->start_mtx); + t->start = start; + pthread_mutex_unlock(&t->start_mtx); +} + +void evtimer_block(void *tmr) +{ + struct evtimer *t = tmr; + + pthread_mutex_lock(&t->block_mtx); + t->blocked++; + while (t->in_cbk) + cond_wait(&t->block_cnd, &t->block_mtx); + pthread_mutex_unlock(&t->block_mtx); +} + +void evtimer_unblock(void *tmr) +{ + struct evtimer *t = tmr; + + pthread_mutex_lock(&t->block_mtx); + t->blocked--; + pthread_mutex_unlock(&t->block_mtx); + pthread_cond_signal(&t->unblock_cnd); +} diff --git a/src/base/lib/translate/Makefile b/src/base/lib/translate/Makefile new file mode 100644 index 0000000..2ea1d5c --- /dev/null +++ b/src/base/lib/translate/Makefile @@ -0,0 +1,48 @@ +# +# (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". +# +# for details see file COPYING in the DOSEMU distribution +# + +top_builddir=../../../.. +include $(top_builddir)/Makefile.conf + +# +# This is the Makefile for the keyboard-subdirectory of the DOS-emulator +# for Linux. +# + +# make_attributes.c is a little utility to convert the attributes +# as supplied by (the unicode organization) to a format usable +# by dosemu. + +CFILES = $(CHARSET_CFILES) translate.c \ + keysym_attributes.c keysym_dead_map.c \ + keysym_approximations.c \ + unicode_utils.c dosemu_charset.c translate_config.c + +DEPENDS= $(CFILES:.c=.d) +OBJS = $(CFILES:.c=.o) +HFILES = + +# Insert all source- and header-files here. + +ALL = $(CFILES) $(HFILES) + +all: lib + +clean:: + -rm -f keysym_attributes.c make_attributes make_attributes.o crunch_UnicodeData + +include $(REALTOPDIR)/src/Makefile.common +vpath UnicodeCrunchedAttributes $(srcdir) + +crunch_UnicodeData: crunch_UnicodeData.c + $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(INCDIR) -o $@ $< + +make_attributes: make_attributes.c + $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(INCDIR) -o $@ $< + +keysym_attributes.c: UnicodeCrunchedAttributes make_attributes + ./make_attributes < $< > $@ + diff --git a/src/base/lib/translate/UnicodeCrunchedAttributes b/src/base/lib/translate/UnicodeCrunchedAttributes new file mode 100644 index 0000000..a618772 --- /dev/null +++ b/src/base/lib/translate/UnicodeCrunchedAttributes @@ -0,0 +1,1508 @@ +# This file has been automatically generated by ./crunch_UnicodeData +# Do not modify +Cc 0000-001F +Zs 0020 +Po 0021-0023 +Sc 0024 +Po 0025-0027 +Ps 0028 +Pe 0029 +Po 002A +Sm 002B +Po 002C +Pd 002D +Po 002E-002F +Nd 0030-0039 +Po 003A-003B +Sm 003C-003E +Po 003F-0040 +Lu 0041-005A +Ps 005B +Po 005C +Pe 005D +Sk 005E +Pc 005F +Sk 0060 +Ll 0061-007A +Ps 007B +Sm 007C +Pe 007D +Sm 007E +Cc 007F-009F +Zs 00A0 +Po 00A1 +Sc 00A2-00A5 +So 00A6-00A7 +Sk 00A8 +So 00A9 +Ll 00AA +Pi 00AB +Sm 00AC +Pd 00AD +So 00AE +Sk 00AF +So 00B0 +Sm 00B1 +No 00B2-00B3 +Sk 00B4 +Ll 00B5 +So 00B6 +Po 00B7 +Sk 00B8 +No 00B9 +Ll 00BA +Pf 00BB +No 00BC-00BE +Po 00BF +Lu 00C0-00D6 +Sm 00D7 +Lu 00D8-00DE +Ll 00DF-00F6 +Sm 00F7 +Ll 00F8-00FF +Lu 0100 +Ll 0101 +Lu 0102 +Ll 0103 +Lu 0104 +Ll 0105 +Lu 0106 +Ll 0107 +Lu 0108 +Ll 0109 +Lu 010A +Ll 010B +Lu 010C +Ll 010D +Lu 010E +Ll 010F +Lu 0110 +Ll 0111 +Lu 0112 +Ll 0113 +Lu 0114 +Ll 0115 +Lu 0116 +Ll 0117 +Lu 0118 +Ll 0119 +Lu 011A +Ll 011B +Lu 011C +Ll 011D +Lu 011E +Ll 011F +Lu 0120 +Ll 0121 +Lu 0122 +Ll 0123 +Lu 0124 +Ll 0125 +Lu 0126 +Ll 0127 +Lu 0128 +Ll 0129 +Lu 012A +Ll 012B +Lu 012C +Ll 012D +Lu 012E +Ll 012F +Lu 0130 +Ll 0131 +Lu 0132 +Ll 0133 +Lu 0134 +Ll 0135 +Lu 0136 +Ll 0137-0138 +Lu 0139 +Ll 013A +Lu 013B +Ll 013C +Lu 013D +Ll 013E +Lu 013F +Ll 0140 +Lu 0141 +Ll 0142 +Lu 0143 +Ll 0144 +Lu 0145 +Ll 0146 +Lu 0147 +Ll 0148-0149 +Lu 014A +Ll 014B +Lu 014C +Ll 014D +Lu 014E +Ll 014F +Lu 0150 +Ll 0151 +Lu 0152 +Ll 0153 +Lu 0154 +Ll 0155 +Lu 0156 +Ll 0157 +Lu 0158 +Ll 0159 +Lu 015A +Ll 015B +Lu 015C +Ll 015D +Lu 015E +Ll 015F +Lu 0160 +Ll 0161 +Lu 0162 +Ll 0163 +Lu 0164 +Ll 0165 +Lu 0166 +Ll 0167 +Lu 0168 +Ll 0169 +Lu 016A +Ll 016B +Lu 016C +Ll 016D +Lu 016E +Ll 016F +Lu 0170 +Ll 0171 +Lu 0172 +Ll 0173 +Lu 0174 +Ll 0175 +Lu 0176 +Ll 0177 +Lu 0178-0179 +Ll 017A +Lu 017B +Ll 017C +Lu 017D +Ll 017E-0180 +Lu 0181-0182 +Ll 0183 +Lu 0184 +Ll 0185 +Lu 0186-0187 +Ll 0188 +Lu 0189-018B +Ll 018C-018D +Lu 018E-0191 +Ll 0192 +Lu 0193-0194 +Ll 0195 +Lu 0196-0198 +Ll 0199-019B +Lu 019C-019D +Ll 019E +Lu 019F-01A0 +Ll 01A1 +Lu 01A2 +Ll 01A3 +Lu 01A4 +Ll 01A5 +Lu 01A6-01A7 +Ll 01A8 +Lu 01A9 +Ll 01AA-01AB +Lu 01AC +Ll 01AD +Lu 01AE-01AF +Ll 01B0 +Lu 01B1-01B3 +Ll 01B4 +Lu 01B5 +Ll 01B6 +Lu 01B7-01B8 +Ll 01B9-01BA +Lo 01BB +Lu 01BC +Ll 01BD-01BF +Lo 01C0-01C3 +Lu 01C4 +Lt 01C5 +Ll 01C6 +Lu 01C7 +Lt 01C8 +Ll 01C9 +Lu 01CA +Lt 01CB +Ll 01CC +Lu 01CD +Ll 01CE +Lu 01CF +Ll 01D0 +Lu 01D1 +Ll 01D2 +Lu 01D3 +Ll 01D4 +Lu 01D5 +Ll 01D6 +Lu 01D7 +Ll 01D8 +Lu 01D9 +Ll 01DA +Lu 01DB +Ll 01DC-01DD +Lu 01DE +Ll 01DF +Lu 01E0 +Ll 01E1 +Lu 01E2 +Ll 01E3 +Lu 01E4 +Ll 01E5 +Lu 01E6 +Ll 01E7 +Lu 01E8 +Ll 01E9 +Lu 01EA +Ll 01EB +Lu 01EC +Ll 01ED +Lu 01EE +Ll 01EF-01F0 +Lu 01F1 +Lt 01F2 +Ll 01F3 +Lu 01F4 +Ll 01F5 +Lu 01F6-01F8 +Ll 01F9 +Lu 01FA +Ll 01FB +Lu 01FC +Ll 01FD +Lu 01FE +Ll 01FF +Lu 0200 +Ll 0201 +Lu 0202 +Ll 0203 +Lu 0204 +Ll 0205 +Lu 0206 +Ll 0207 +Lu 0208 +Ll 0209 +Lu 020A +Ll 020B +Lu 020C +Ll 020D +Lu 020E +Ll 020F +Lu 0210 +Ll 0211 +Lu 0212 +Ll 0213 +Lu 0214 +Ll 0215 +Lu 0216 +Ll 0217 +Lu 0218 +Ll 0219 +Lu 021A +Ll 021B +Lu 021C +Ll 021D +Lu 021E +Ll 021F +Lu 0222 +Ll 0223 +Lu 0224 +Ll 0225 +Lu 0226 +Ll 0227 +Lu 0228 +Ll 0229 +Lu 022A +Ll 022B +Lu 022C +Ll 022D +Lu 022E +Ll 022F +Lu 0230 +Ll 0231 +Lu 0232 +Ll 0233 +Ll 0250-02AD +Lm 02B0-02B8 +Sk 02B9-02BA +Lm 02BB-02C1 +Sk 02C2-02CF +Lm 02D0-02D1 +Sk 02D2-02DF +Lm 02E0-02E4 +Sk 02E5-02ED +Lm 02EE +Mn 0300-034E +Mn 0360-0362 +Sk 0374-0375 +Lm 037A +Po 037E +Sk 0384-0385 +Lu 0386 +Po 0387 +Lu 0388-038A +Lu 038C +Lu 038E-038F +Ll 0390 +Lu 0391-03A1 +Lu 03A3-03AB +Ll 03AC-03CE +Ll 03D0-03D1 +Lu 03D2-03D4 +Ll 03D5-03D7 +Lu 03DA +Ll 03DB +Lu 03DC +Ll 03DD +Lu 03DE +Ll 03DF +Lu 03E0 +Ll 03E1 +Lu 03E2 +Ll 03E3 +Lu 03E4 +Ll 03E5 +Lu 03E6 +Ll 03E7 +Lu 03E8 +Ll 03E9 +Lu 03EA +Ll 03EB +Lu 03EC +Ll 03ED +Lu 03EE +Ll 03EF-03F3 +Lu 0400-042F +Ll 0430-045F +Lu 0460 +Ll 0461 +Lu 0462 +Ll 0463 +Lu 0464 +Ll 0465 +Lu 0466 +Ll 0467 +Lu 0468 +Ll 0469 +Lu 046A +Ll 046B +Lu 046C +Ll 046D +Lu 046E +Ll 046F +Lu 0470 +Ll 0471 +Lu 0472 +Ll 0473 +Lu 0474 +Ll 0475 +Lu 0476 +Ll 0477 +Lu 0478 +Ll 0479 +Lu 047A +Ll 047B +Lu 047C +Ll 047D +Lu 047E +Ll 047F +Lu 0480 +Ll 0481 +So 0482 +Mn 0483-0486 +Me 0488-0489 +Lu 048C +Ll 048D +Lu 048E +Ll 048F +Lu 0490 +Ll 0491 +Lu 0492 +Ll 0493 +Lu 0494 +Ll 0495 +Lu 0496 +Ll 0497 +Lu 0498 +Ll 0499 +Lu 049A +Ll 049B +Lu 049C +Ll 049D +Lu 049E +Ll 049F +Lu 04A0 +Ll 04A1 +Lu 04A2 +Ll 04A3 +Lu 04A4 +Ll 04A5 +Lu 04A6 +Ll 04A7 +Lu 04A8 +Ll 04A9 +Lu 04AA +Ll 04AB +Lu 04AC +Ll 04AD +Lu 04AE +Ll 04AF +Lu 04B0 +Ll 04B1 +Lu 04B2 +Ll 04B3 +Lu 04B4 +Ll 04B5 +Lu 04B6 +Ll 04B7 +Lu 04B8 +Ll 04B9 +Lu 04BA +Ll 04BB +Lu 04BC +Ll 04BD +Lu 04BE +Ll 04BF +Lu 04C0-04C1 +Ll 04C2 +Lu 04C3 +Ll 04C4 +Lu 04C7 +Ll 04C8 +Lu 04CB +Ll 04CC +Lu 04D0 +Ll 04D1 +Lu 04D2 +Ll 04D3 +Lu 04D4 +Ll 04D5 +Lu 04D6 +Ll 04D7 +Lu 04D8 +Ll 04D9 +Lu 04DA +Ll 04DB +Lu 04DC +Ll 04DD +Lu 04DE +Ll 04DF +Lu 04E0 +Ll 04E1 +Lu 04E2 +Ll 04E3 +Lu 04E4 +Ll 04E5 +Lu 04E6 +Ll 04E7 +Lu 04E8 +Ll 04E9 +Lu 04EA +Ll 04EB +Lu 04EC +Ll 04ED +Lu 04EE +Ll 04EF +Lu 04F0 +Ll 04F1 +Lu 04F2 +Ll 04F3 +Lu 04F4 +Ll 04F5 +Lu 04F8 +Ll 04F9 +Lu 0531-0556 +Lm 0559 +Po 055A-055F +Ll 0561-0587 +Po 0589 +Pd 058A +Mn 0591-05A1 +Mn 05A3-05B9 +Mn 05BB-05BD +Po 05BE +Mn 05BF +Po 05C0 +Mn 05C1-05C2 +Po 05C3 +Mn 05C4 +Lo 05D0-05EA +Lo 05F0-05F2 +Po 05F3-05F4 +Po 060C +Po 061B +Po 061F +Lo 0621-063A +Lm 0640 +Lo 0641-064A +Mn 064B-0655 +Nd 0660-0669 +Po 066A-066D +Mn 0670 +Lo 0671-06D3 +Po 06D4 +Lo 06D5 +Mn 06D6-06DC +Me 06DD-06DE +Mn 06DF-06E4 +Lm 06E5-06E6 +Mn 06E7-06E8 +So 06E9 +Mn 06EA-06ED +Nd 06F0-06F9 +Lo 06FA-06FC +So 06FD-06FE +Po 0700-070D +Cf 070F +Lo 0710 +Mn 0711 +Lo 0712-072C +Mn 0730-074A +Lo 0780-07A5 +Mn 07A6-07B0 +Mn 0901-0902 +Mc 0903 +Lo 0905-0939 +Mn 093C +Lo 093D +Mc 093E-0940 +Mn 0941-0948 +Mc 0949-094C +Mn 094D +Lo 0950 +Mn 0951-0954 +Lo 0958-0961 +Mn 0962-0963 +Po 0964-0965 +Nd 0966-096F +Po 0970 +Mn 0981 +Mc 0982-0983 +Lo 0985-098C +Lo 098F-0990 +Lo 0993-09A8 +Lo 09AA-09B0 +Lo 09B2 +Lo 09B6-09B9 +Mn 09BC +Mc 09BE-09C0 +Mn 09C1-09C4 +Mc 09C7-09C8 +Mc 09CB-09CC +Mn 09CD +Mc 09D7 +Lo 09DC-09DD +Lo 09DF-09E1 +Mn 09E2-09E3 +Nd 09E6-09EF +Lo 09F0-09F1 +Sc 09F2-09F3 +No 09F4-09F9 +So 09FA +Mn 0A02 +Lo 0A05-0A0A +Lo 0A0F-0A10 +Lo 0A13-0A28 +Lo 0A2A-0A30 +Lo 0A32-0A33 +Lo 0A35-0A36 +Lo 0A38-0A39 +Mn 0A3C +Mc 0A3E-0A40 +Mn 0A41-0A42 +Mn 0A47-0A48 +Mn 0A4B-0A4D +Lo 0A59-0A5C +Lo 0A5E +Nd 0A66-0A6F +Mn 0A70-0A71 +Lo 0A72-0A74 +Mn 0A81-0A82 +Mc 0A83 +Lo 0A85-0A8B +Lo 0A8D +Lo 0A8F-0A91 +Lo 0A93-0AA8 +Lo 0AAA-0AB0 +Lo 0AB2-0AB3 +Lo 0AB5-0AB9 +Mn 0ABC +Lo 0ABD +Mc 0ABE-0AC0 +Mn 0AC1-0AC5 +Mn 0AC7-0AC8 +Mc 0AC9 +Mc 0ACB-0ACC +Mn 0ACD +Lo 0AD0 +Lo 0AE0 +Nd 0AE6-0AEF +Mn 0B01 +Mc 0B02-0B03 +Lo 0B05-0B0C +Lo 0B0F-0B10 +Lo 0B13-0B28 +Lo 0B2A-0B30 +Lo 0B32-0B33 +Lo 0B36-0B39 +Mn 0B3C +Lo 0B3D +Mc 0B3E +Mn 0B3F +Mc 0B40 +Mn 0B41-0B43 +Mc 0B47-0B48 +Mc 0B4B-0B4C +Mn 0B4D +Mn 0B56 +Mc 0B57 +Lo 0B5C-0B5D +Lo 0B5F-0B61 +Nd 0B66-0B6F +So 0B70 +Mn 0B82 +Mc 0B83 +Lo 0B85-0B8A +Lo 0B8E-0B90 +Lo 0B92-0B95 +Lo 0B99-0B9A +Lo 0B9C +Lo 0B9E-0B9F +Lo 0BA3-0BA4 +Lo 0BA8-0BAA +Lo 0BAE-0BB5 +Lo 0BB7-0BB9 +Mc 0BBE-0BBF +Mn 0BC0 +Mc 0BC1-0BC2 +Mc 0BC6-0BC8 +Mc 0BCA-0BCC +Mn 0BCD +Mc 0BD7 +Nd 0BE7-0BEF +No 0BF0-0BF2 +Mc 0C01-0C03 +Lo 0C05-0C0C +Lo 0C0E-0C10 +Lo 0C12-0C28 +Lo 0C2A-0C33 +Lo 0C35-0C39 +Mn 0C3E-0C40 +Mc 0C41-0C44 +Mn 0C46-0C48 +Mn 0C4A-0C4D +Mn 0C55-0C56 +Lo 0C60-0C61 +Nd 0C66-0C6F +Mc 0C82-0C83 +Lo 0C85-0C8C +Lo 0C8E-0C90 +Lo 0C92-0CA8 +Lo 0CAA-0CB3 +Lo 0CB5-0CB9 +Mc 0CBE +Mn 0CBF +Mc 0CC0-0CC4 +Mn 0CC6 +Mc 0CC7-0CC8 +Mc 0CCA-0CCB +Mn 0CCC-0CCD +Mc 0CD5-0CD6 +Lo 0CDE +Lo 0CE0-0CE1 +Nd 0CE6-0CEF +Mc 0D02-0D03 +Lo 0D05-0D0C +Lo 0D0E-0D10 +Lo 0D12-0D28 +Lo 0D2A-0D39 +Mc 0D3E-0D40 +Mn 0D41-0D43 +Mc 0D46-0D48 +Mc 0D4A-0D4C +Mn 0D4D +Mc 0D57 +Lo 0D60-0D61 +Nd 0D66-0D6F +Mc 0D82-0D83 +Lo 0D85-0D96 +Lo 0D9A-0DB1 +Lo 0DB3-0DBB +Lo 0DBD +Lo 0DC0-0DC6 +Mn 0DCA +Mc 0DCF-0DD1 +Mn 0DD2-0DD4 +Mn 0DD6 +Mc 0DD8-0DDF +Mc 0DF2-0DF3 +Po 0DF4 +Lo 0E01-0E30 +Mn 0E31 +Lo 0E32-0E33 +Mn 0E34-0E3A +Sc 0E3F +Lo 0E40-0E45 +Lm 0E46 +Mn 0E47-0E4E +Po 0E4F +Nd 0E50-0E59 +Po 0E5A-0E5B +Lo 0E81-0E82 +Lo 0E84 +Lo 0E87-0E88 +Lo 0E8A +Lo 0E8D +Lo 0E94-0E97 +Lo 0E99-0E9F +Lo 0EA1-0EA3 +Lo 0EA5 +Lo 0EA7 +Lo 0EAA-0EAB +Lo 0EAD-0EB0 +Mn 0EB1 +Lo 0EB2-0EB3 +Mn 0EB4-0EB9 +Mn 0EBB-0EBC +Lo 0EBD +Lo 0EC0-0EC4 +Lm 0EC6 +Mn 0EC8-0ECD +Nd 0ED0-0ED9 +Lo 0EDC-0EDD +Lo 0F00 +So 0F01-0F03 +Po 0F04-0F12 +So 0F13-0F17 +Mn 0F18-0F19 +So 0F1A-0F1F +Nd 0F20-0F29 +No 0F2A-0F33 +So 0F34 +Mn 0F35 +So 0F36 +Mn 0F37 +So 0F38 +Mn 0F39 +Ps 0F3A +Pe 0F3B +Ps 0F3C +Pe 0F3D +Mc 0F3E-0F3F +Lo 0F40-0F47 +Lo 0F49-0F6A +Mn 0F71-0F7E +Mc 0F7F +Mn 0F80-0F84 +Po 0F85 +Mn 0F86-0F87 +Lo 0F88-0F8B +Mn 0F90-0F97 +Mn 0F99-0FBC +So 0FBE-0FC5 +Mn 0FC6 +So 0FC7-0FCC +So 0FCF +Lo 1000-1021 +Lo 1023-1027 +Lo 1029-102A +Mc 102C +Mn 102D-1030 +Mc 1031 +Mn 1032 +Mn 1036-1037 +Mc 1038 +Mn 1039 +Nd 1040-1049 +Po 104A-104F +Lo 1050-1055 +Mc 1056-1057 +Mn 1058-1059 +Lu 10A0-10C5 +Lo 10D0-10F6 +Po 10FB +Lo 1100-1159 +Lo 115F-11A2 +Lo 11A8-11F9 +Lo 1200-1206 +Lo 1208-1246 +Lo 1248 +Lo 124A-124D +Lo 1250-1256 +Lo 1258 +Lo 125A-125D +Lo 1260-1286 +Lo 1288 +Lo 128A-128D +Lo 1290-12AE +Lo 12B0 +Lo 12B2-12B5 +Lo 12B8-12BE +Lo 12C0 +Lo 12C2-12C5 +Lo 12C8-12CE +Lo 12D0-12D6 +Lo 12D8-12EE +Lo 12F0-130E +Lo 1310 +Lo 1312-1315 +Lo 1318-131E +Lo 1320-1346 +Lo 1348-135A +Po 1361-1368 +Nd 1369-1371 +No 1372-137C +Lo 13A0-13F4 +Lo 1401-166C +Po 166D-166E +Lo 166F-1676 +Zs 1680 +Lo 1681-169A +Ps 169B +Pe 169C +Lo 16A0-16EA +Po 16EB-16ED +No 16EE-16F0 +Lo 1780-17B3 +Mc 17B4-17B6 +Mn 17B7-17BD +Mc 17BE-17C5 +Mn 17C6 +Mc 17C7-17C8 +Mn 17C9-17D3 +Po 17D4-17DA +Sc 17DB +Po 17DC +Nd 17E0-17E9 +Po 1800-1805 +Pd 1806 +Po 1807-180A +Cf 180B-180E +Nd 1810-1819 +Lo 1820-1842 +Lm 1843 +Lo 1844-1877 +Lo 1880-18A8 +Mn 18A9 +Lu 1E00 +Ll 1E01 +Lu 1E02 +Ll 1E03 +Lu 1E04 +Ll 1E05 +Lu 1E06 +Ll 1E07 +Lu 1E08 +Ll 1E09 +Lu 1E0A +Ll 1E0B +Lu 1E0C +Ll 1E0D +Lu 1E0E +Ll 1E0F +Lu 1E10 +Ll 1E11 +Lu 1E12 +Ll 1E13 +Lu 1E14 +Ll 1E15 +Lu 1E16 +Ll 1E17 +Lu 1E18 +Ll 1E19 +Lu 1E1A +Ll 1E1B +Lu 1E1C +Ll 1E1D +Lu 1E1E +Ll 1E1F +Lu 1E20 +Ll 1E21 +Lu 1E22 +Ll 1E23 +Lu 1E24 +Ll 1E25 +Lu 1E26 +Ll 1E27 +Lu 1E28 +Ll 1E29 +Lu 1E2A +Ll 1E2B +Lu 1E2C +Ll 1E2D +Lu 1E2E +Ll 1E2F +Lu 1E30 +Ll 1E31 +Lu 1E32 +Ll 1E33 +Lu 1E34 +Ll 1E35 +Lu 1E36 +Ll 1E37 +Lu 1E38 +Ll 1E39 +Lu 1E3A +Ll 1E3B +Lu 1E3C +Ll 1E3D +Lu 1E3E +Ll 1E3F +Lu 1E40 +Ll 1E41 +Lu 1E42 +Ll 1E43 +Lu 1E44 +Ll 1E45 +Lu 1E46 +Ll 1E47 +Lu 1E48 +Ll 1E49 +Lu 1E4A +Ll 1E4B +Lu 1E4C +Ll 1E4D +Lu 1E4E +Ll 1E4F +Lu 1E50 +Ll 1E51 +Lu 1E52 +Ll 1E53 +Lu 1E54 +Ll 1E55 +Lu 1E56 +Ll 1E57 +Lu 1E58 +Ll 1E59 +Lu 1E5A +Ll 1E5B +Lu 1E5C +Ll 1E5D +Lu 1E5E +Ll 1E5F +Lu 1E60 +Ll 1E61 +Lu 1E62 +Ll 1E63 +Lu 1E64 +Ll 1E65 +Lu 1E66 +Ll 1E67 +Lu 1E68 +Ll 1E69 +Lu 1E6A +Ll 1E6B +Lu 1E6C +Ll 1E6D +Lu 1E6E +Ll 1E6F +Lu 1E70 +Ll 1E71 +Lu 1E72 +Ll 1E73 +Lu 1E74 +Ll 1E75 +Lu 1E76 +Ll 1E77 +Lu 1E78 +Ll 1E79 +Lu 1E7A +Ll 1E7B +Lu 1E7C +Ll 1E7D +Lu 1E7E +Ll 1E7F +Lu 1E80 +Ll 1E81 +Lu 1E82 +Ll 1E83 +Lu 1E84 +Ll 1E85 +Lu 1E86 +Ll 1E87 +Lu 1E88 +Ll 1E89 +Lu 1E8A +Ll 1E8B +Lu 1E8C +Ll 1E8D +Lu 1E8E +Ll 1E8F +Lu 1E90 +Ll 1E91 +Lu 1E92 +Ll 1E93 +Lu 1E94 +Ll 1E95-1E9B +Lu 1EA0 +Ll 1EA1 +Lu 1EA2 +Ll 1EA3 +Lu 1EA4 +Ll 1EA5 +Lu 1EA6 +Ll 1EA7 +Lu 1EA8 +Ll 1EA9 +Lu 1EAA +Ll 1EAB +Lu 1EAC +Ll 1EAD +Lu 1EAE +Ll 1EAF +Lu 1EB0 +Ll 1EB1 +Lu 1EB2 +Ll 1EB3 +Lu 1EB4 +Ll 1EB5 +Lu 1EB6 +Ll 1EB7 +Lu 1EB8 +Ll 1EB9 +Lu 1EBA +Ll 1EBB +Lu 1EBC +Ll 1EBD +Lu 1EBE +Ll 1EBF +Lu 1EC0 +Ll 1EC1 +Lu 1EC2 +Ll 1EC3 +Lu 1EC4 +Ll 1EC5 +Lu 1EC6 +Ll 1EC7 +Lu 1EC8 +Ll 1EC9 +Lu 1ECA +Ll 1ECB +Lu 1ECC +Ll 1ECD +Lu 1ECE +Ll 1ECF +Lu 1ED0 +Ll 1ED1 +Lu 1ED2 +Ll 1ED3 +Lu 1ED4 +Ll 1ED5 +Lu 1ED6 +Ll 1ED7 +Lu 1ED8 +Ll 1ED9 +Lu 1EDA +Ll 1EDB +Lu 1EDC +Ll 1EDD +Lu 1EDE +Ll 1EDF +Lu 1EE0 +Ll 1EE1 +Lu 1EE2 +Ll 1EE3 +Lu 1EE4 +Ll 1EE5 +Lu 1EE6 +Ll 1EE7 +Lu 1EE8 +Ll 1EE9 +Lu 1EEA +Ll 1EEB +Lu 1EEC +Ll 1EED +Lu 1EEE +Ll 1EEF +Lu 1EF0 +Ll 1EF1 +Lu 1EF2 +Ll 1EF3 +Lu 1EF4 +Ll 1EF5 +Lu 1EF6 +Ll 1EF7 +Lu 1EF8 +Ll 1EF9 +Ll 1F00-1F07 +Lu 1F08-1F0F +Ll 1F10-1F15 +Lu 1F18-1F1D +Ll 1F20-1F27 +Lu 1F28-1F2F +Ll 1F30-1F37 +Lu 1F38-1F3F +Ll 1F40-1F45 +Lu 1F48-1F4D +Ll 1F50-1F57 +Lu 1F59 +Lu 1F5B +Lu 1F5D +Lu 1F5F +Ll 1F60-1F67 +Lu 1F68-1F6F +Ll 1F70-1F7D +Ll 1F80-1F87 +Lt 1F88-1F8F +Ll 1F90-1F97 +Lt 1F98-1F9F +Ll 1FA0-1FA7 +Lt 1FA8-1FAF +Ll 1FB0-1FB4 +Ll 1FB6-1FB7 +Lu 1FB8-1FBB +Lt 1FBC +Sk 1FBD +Ll 1FBE +Sk 1FBF-1FC1 +Ll 1FC2-1FC4 +Ll 1FC6-1FC7 +Lu 1FC8-1FCB +Lt 1FCC +Sk 1FCD-1FCF +Ll 1FD0-1FD3 +Ll 1FD6-1FD7 +Lu 1FD8-1FDB +Sk 1FDD-1FDF +Ll 1FE0-1FE7 +Lu 1FE8-1FEC +Sk 1FED-1FEF +Ll 1FF2-1FF4 +Ll 1FF6-1FF7 +Lu 1FF8-1FFB +Lt 1FFC +Sk 1FFD-1FFE +Zs 2000-200B +Cf 200C-200F +Pd 2010-2015 +Po 2016-2017 +Pi 2018 +Pf 2019 +Ps 201A +Pi 201B-201C +Pf 201D +Ps 201E +Pi 201F +Po 2020-2027 +Zl 2028 +Zp 2029 +Cf 202A-202E +Zs 202F +Po 2030-2038 +Pi 2039 +Pf 203A +Po 203B-203E +Pc 203F-2040 +Po 2041-2043 +Sm 2044 +Ps 2045 +Pe 2046 +Po 2048-204D +Cf 206A-206F +No 2070 +No 2074-2079 +Sm 207A-207C +Ps 207D +Pe 207E +Ll 207F +No 2080-2089 +Sm 208A-208C +Ps 208D +Pe 208E +Sc 20A0-20AF +Mn 20D0-20DC +Me 20DD-20E0 +Mn 20E1 +Me 20E2-20E3 +So 2100-2101 +Lu 2102 +So 2103-2106 +Lu 2107 +So 2108-2109 +Ll 210A +Lu 210B-210D +Ll 210E-210F +Lu 2110-2112 +Ll 2113 +So 2114 +Lu 2115 +So 2116-2118 +Lu 2119-211D +So 211E-2123 +Lu 2124 +So 2125 +Lu 2126 +So 2127 +Lu 2128 +So 2129 +Lu 212A-212D +So 212E +Ll 212F +Lu 2130-2131 +So 2132 +Lu 2133 +Ll 2134 +Lo 2135-2138 +Ll 2139 +So 213A +No 2153-215F +Nl 2160-2183 +Sm 2190-2194 +So 2195-2199 +Sm 219A-219B +So 219C-219F +Sm 21A0 +So 21A1-21A2 +Sm 21A3 +So 21A4-21A5 +Sm 21A6 +So 21A7-21AD +Sm 21AE +So 21AF-21CD +Sm 21CE-21CF +So 21D0-21D1 +Sm 21D2 +So 21D3 +Sm 21D4 +So 21D5-21F3 +Sm 2200-22F1 +So 2300-2307 +Sm 2308-230B +So 230C-231F +Sm 2320-2321 +So 2322-2328 +Ps 2329 +Pe 232A +So 232B-237B +So 237D-239A +So 2400-2426 +So 2440-244A +No 2460-249B +So 249C-24E9 +No 24EA +So 2500-2595 +So 25A0-25B6 +Sm 25B7 +So 25B8-25C0 +Sm 25C1 +So 25C2-25F7 +So 2600-2613 +So 2619-266E +Sm 266F +So 2670-2671 +So 2701-2704 +So 2706-2709 +So 270C-2727 +So 2729-274B +So 274D +So 274F-2752 +So 2756 +So 2758-275E +So 2761-2767 +No 2776-2793 +So 2794 +So 2798-27AF +So 27B1-27BE +So 2800-28FF +So 2E80-2E99 +So 2E9B-2EF3 +So 2F00-2FD5 +So 2FF0-2FFB +Zs 3000 +Po 3001-3003 +So 3004 +Lm 3005 +Lo 3006 +Nl 3007 +Ps 3008 +Pe 3009 +Ps 300A +Pe 300B +Ps 300C +Pe 300D +Ps 300E +Pe 300F +Ps 3010 +Pe 3011 +So 3012-3013 +Ps 3014 +Pe 3015 +Ps 3016 +Pe 3017 +Ps 3018 +Pe 3019 +Ps 301A +Pe 301B +Pd 301C +Ps 301D +Pe 301E-301F +So 3020 +Nl 3021-3029 +Mn 302A-302F +Pd 3030 +Lm 3031-3035 +So 3036-3037 +Nl 3038-303A +So 303E-303F +Lo 3041-3094 +Mn 3099-309A +Sk 309B-309C +Lm 309D-309E +Lo 30A1-30FA +Pc 30FB +Lm 30FC-30FE +Lo 3105-312C +Lo 3131-318E +So 3190-3191 +No 3192-3195 +So 3196-319F +Lo 31A0-31B7 +So 3200-321C +No 3220-3229 +So 322A-3243 +So 3260-327B +So 327F +No 3280-3289 +So 328A-32B0 +So 32C0-32CB +So 32D0-32FE +So 3300-3376 +So 337B-33DD +So 33E0-33FE +Lo 3400 +Lo 4DB5 +Lo 4E00 +Lo 9FA5 +Lo A000-A48C +So A490-A4A1 +So A4A4-A4B3 +So A4B5-A4C0 +So A4C2-A4C4 +So A4C6 +Lo AC00 +Lo D7A3 +Cs D800 +Cs DB7F-DB80 +Cs DBFF-DC00 +Cs DFFF +Co E000 +Co F8FF +Lo F900-FA2D +Ll FB00-FB06 +Ll FB13-FB17 +Lo FB1D +Mn FB1E +Lo FB1F-FB28 +Sm FB29 +Lo FB2A-FB36 +Lo FB38-FB3C +Lo FB3E +Lo FB40-FB41 +Lo FB43-FB44 +Lo FB46-FBB1 +Lo FBD3-FD3D +Ps FD3E +Pe FD3F +Lo FD50-FD8F +Lo FD92-FDC7 +Lo FDF0-FDFB +Mn FE20-FE23 +Po FE30 +Pd FE31-FE32 +Pc FE33-FE34 +Ps FE35 +Pe FE36 +Ps FE37 +Pe FE38 +Ps FE39 +Pe FE3A +Ps FE3B +Pe FE3C +Ps FE3D +Pe FE3E +Ps FE3F +Pe FE40 +Ps FE41 +Pe FE42 +Ps FE43 +Pe FE44 +Po FE49-FE4C +Pc FE4D-FE4F +Po FE50-FE52 +Po FE54-FE57 +Pd FE58 +Ps FE59 +Pe FE5A +Ps FE5B +Pe FE5C +Ps FE5D +Pe FE5E +Po FE5F-FE61 +Sm FE62 +Pd FE63 +Sm FE64-FE66 +Po FE68 +Sc FE69 +Po FE6A-FE6B +Lo FE70-FE72 +Lo FE74 +Lo FE76-FEFC +Cf FEFF +Po FF01-FF03 +Sc FF04 +Po FF05-FF07 +Ps FF08 +Pe FF09 +Po FF0A +Sm FF0B +Po FF0C +Pd FF0D +Po FF0E-FF0F +Nd FF10-FF19 +Po FF1A-FF1B +Sm FF1C-FF1E +Po FF1F-FF20 +Lu FF21-FF3A +Ps FF3B +Po FF3C +Pe FF3D +Sk FF3E +Pc FF3F +Sk FF40 +Ll FF41-FF5A +Ps FF5B +Sm FF5C +Pe FF5D +Sm FF5E +Po FF61 +Ps FF62 +Pe FF63 +Po FF64 +Pc FF65 +Lo FF66-FF6F +Lm FF70 +Lo FF71-FF9D +Lm FF9E-FF9F +Lo FFA0-FFBE +Lo FFC2-FFC7 +Lo FFCA-FFCF +Lo FFD2-FFD7 +Lo FFDA-FFDC +Sc FFE0-FFE1 +Sm FFE2 +Sk FFE3 +So FFE4 +Sc FFE5-FFE6 +So FFE8 +Sm FFE9-FFEC +So FFED-FFEE +Cf FFF9-FFFB +So FFFC-FFFD diff --git a/src/base/lib/translate/crunch_UnicodeData.c b/src/base/lib/translate/crunch_UnicodeData.c new file mode 100644 index 0000000..16318ba --- /dev/null +++ b/src/base/lib/translate/crunch_UnicodeData.c @@ -0,0 +1,180 @@ +#include +#include +#include + +#define MAX_SYMBOLS 65536 +#define MAX_LETTERS 256 +#define MAX_PROPERTY_LENGTH 2 + +typedef unsigned char t_property[MAX_PROPERTY_LENGTH+1]; +static t_property properties[MAX_SYMBOLS]; + +#define MAX_SYMBOL_LENGTH 4 + +static void init_properties(void) +{ + int i; + for(i = 0; i < MAX_SYMBOLS; i++) { + properties[i][0] = '\0'; + } +} + +static void set_property(unsigned symbol, t_property *property) +{ + if (symbol < MAX_SYMBOLS) { + strcpy(properties[symbol], *property); +#if 0 + fprintf(stderr, "%04x %s %s\n", + symbol, + properties[symbol], + *property); +#endif + } +} + +static void set_property_range( + unsigned symbol_start, unsigned symbol_end, t_property *property) +{ + unsigned i; + for(i = symbol_start; (i <= symbol_end) && (i < MAX_SYMBOLS); i++) { + strcpy(properties[symbol_start], *property); + } +} + + +static void process_UnicodeData_input(FILE *input) +{ + +#define STATE_RESETTING 0 +#define STATE_GETTING_SYMBOL 1 +#define STATE_SKIPPING_NAME 2 +#define STATE_GETTING_LETTER 3 +#define STATE_GETTING_EOL 4 + + int state = STATE_RESETTING; + char symbol_digit[MAX_SYMBOL_LENGTH+1]; + int symbol_digits; + unsigned symbol; + t_property property; + int property_length; + int c; + int line = 1; + +#define next_char() \ +c = getc(input); if (c < 0) break + + c = 0; + while (c >= 0) { + switch (state) { + case STATE_RESETTING: + symbol = 0; + symbol_digits = 0; + symbol_digit[0] = '\0'; + state = STATE_GETTING_SYMBOL; + property[0] = '\0'; + property_length = 0; + /* fall through */ + case STATE_GETTING_SYMBOL: + next_char(); + if ((symbol_digits <= MAX_SYMBOL_LENGTH) && + (isxdigit(c))) { + symbol_digit[symbol_digits++] = c; + } + else if (c == ';') { + symbol_digit[symbol_digits] = '\0'; + symbol = strtoul(symbol_digit, 0, 16); + state = STATE_SKIPPING_NAME; + } else { + fprintf(stderr, "Bad symbol number line %d\n", line); + state = STATE_GETTING_EOL; + } + break; + case STATE_SKIPPING_NAME: + next_char(); + if (c == ';') { + state = STATE_GETTING_LETTER; + } + break; + case STATE_GETTING_LETTER: + next_char(); + if ((property_length < MAX_PROPERTY_LENGTH) && + isalpha(c)) { + property[property_length++] = c; +#if 0 + fprintf(stderr, "%c\n", c); +#endif + } + else if (c == ';') { + property[property_length] = '\0'; + set_property(symbol, &property); + state = STATE_GETTING_EOL; + } + else { + fprintf(stderr, "Can't find letter on line %d\n", line); + state = STATE_GETTING_EOL; + } + break; + case STATE_GETTING_EOL: + next_char(); + if (c == '\n') { + line++; + state = STATE_RESETTING; + } + break; + } + } +#undef STATE_RESETTING +#undef STATE_GETTING_SYMBOL +#undef STATE_SKIPPING_NAME +#undef STATE_GETTING_LETTER +#undef STATE_GETTING_EOL +} + +static void create_crunched_output(char *name, FILE *output) +{ + int i; + t_property *last_property = 0; + fprintf(output, + "# This file has been automatically generated by %s\n" + "# Do not modify\n", + name ); + for(i = 0; i < MAX_SYMBOLS; i++) { + if (last_property) { + if (strcmp(*last_property, properties[i]) != 0) { + if (last_property != &properties[i-1]) { + fprintf(output, "-%04X", i-1); + } + fprintf(output, "\n"); + last_property = 0; + } + } + if (!last_property && properties[i][0] != '\0') { + last_property = &properties[i]; + fprintf(output, "%s %04X", *last_property, i); + } + } + if (last_property) { + if (last_property != &properties[i-1]) { + fprintf(output, "-%04X", i-1); + } + fprintf(output, "\n"); + } +} + +static void init(void) +{ + init_properties(); +} + +static int crunch_UnicodeData(char *name, FILE *in, FILE *out) +{ + init(); + process_UnicodeData_input(in); + create_crunched_output(name, out); + return 0; +} + +int main(int argc, char **argv) +{ + return crunch_UnicodeData(argv[0], stdin, stdout); +} diff --git a/src/base/lib/translate/dosemu_charset.c b/src/base/lib/translate/dosemu_charset.c new file mode 100644 index 0000000..06638f4 --- /dev/null +++ b/src/base/lib/translate/dosemu_charset.c @@ -0,0 +1,261 @@ +#include +#include +#include +#include "translate/unicode_symbols.h" +#include "translate/translate.h" +#include "translate/dosemu_charset.h" + +/* + * Terminal Charset operations + * ============================ + */ +/* The terminal charset ops + * A terminal character set is defined as one where + * characters 0x00 - 0x1f & 0x7f are identity mapped with ascii. + * + * Unicode has this property (except for character size), allowing + * for an implementation with no lookup tables. + * + * All other characters are forwarded to the original character set. + */ +static size_t terminal_to_unicode(struct char_set_state *state, + struct char_set *set, int offset, + t_unicode *symbol_out, const unsigned char *str, size_t in_len) +{ + size_t result; + + if ((str[0] <= 0x20) || (str[0] == 0x7f)) { + *symbol_out = str[0]; + result = 1; + } + else { + struct char_set *piece; + piece = set->c0; + result = piece->ops->charset_to_unicode( + state, piece, offset, + symbol_out, str, in_len); + } + return result; +} + +static size_t unicode_to_terminal(struct char_set_state *state, + struct char_set *set, int offset, + t_unicode symbol, unsigned char *str, size_t out_len) +{ + size_t result; + + if ((symbol <= 0x20) || (symbol == 0x7f)) { + str[0] = symbol; + result = 1; + } + else { + struct char_set *piece; + piece = set->c0; + result = piece->ops->unicode_to_charset( + state, piece, offset, + symbol, str, out_len); + if ((result == 1) && ((str[0] <= 0x20) || (str[0] == 0x7f))) { + /* Ditch reserved characters */ + errno = EILSEQ; + result = -1; + } + } + return result; +} + +static int init_terminal_charset_state(struct char_set_state *state) +{ + struct char_set *set; + set = state->chars; + return set->c0->ops->init(state); +} + +static void cleanup_terminal_charset_state(struct char_set_state *state) +{ + struct char_set *set; + set = state->chars; + return set->c0->ops->cleanup(state); +} +static void copy_terminal_charset_state( + struct char_set_state *dst, const struct char_set_state *src) +{ + struct char_set *set; + set = src->chars; + set->c0->ops->copy(dst, src); +} + +struct foreach_terminal_state { + void *callback_data; + foreach_callback_t callback; +}; +static void foreach_terminal_callback(void *callback_data, + t_unicode symbol, const unsigned char *bytes, size_t byte_len) +{ + struct foreach_terminal_state *state = callback_data; + if ((byte_len == 1) && ((bytes[0] <= 0x20) || (bytes[0] == 0x7f))) { + /* suppress translations for the control characters.. */ + return; + } + state->callback(state->callback_data, symbol, bytes, byte_len); +} + +static void foreach_terminal(struct char_set *set, int offset, + void *callback_data, foreach_callback_t callback) +{ + static const unsigned char _delete[] = {0x7f}; + struct foreach_terminal_state state; + int i; + + state.callback_data = callback_data; + state.callback = callback; + for(i = 0; i <= 0x20; i++) { + unsigned char buff[1]; + buff[0] = i; + state.callback(state.callback_data, i, buff, 1); + } + state.callback(state.callback_data, U_DELETE, _delete, 1); + set->c0->ops->foreach(set, offset, &state, foreach_terminal_callback); +} + +struct char_set_operations terminal_charset_ops = { + .unicode_to_charset= &unicode_to_terminal, + .charset_to_unicode= &terminal_to_unicode, + .init= &init_terminal_charset_state, + .cleanup= &cleanup_terminal_charset_state, + .copy= ©_terminal_charset_state, + .foreach= &foreach_terminal, +}; + +/* + * Terminal Charset management + * =========================== + */ + +int is_terminal_charset(struct char_set *set) +{ + /* Verify ^A - ^Z are handled correctly */ + unsigned char buff[3]; + t_unicode symbol; + struct char_set_state test; + int is_terminal; + int i; + + is_terminal = 1; + for(i = 0; is_terminal && (i <= 0x20); i++) { + /* First the character we want translated */ + buff[0] = i; + /* Then extra characters that can't be a part + * of any sane escape code... + */ + buff[1] = 0; + buff[2] = 0; + init_charset_state(&test, set); + if ((charset_to_unicode(&test, &symbol, buff, sizeof(buff)) != 1) || + (symbol != i)) { + is_terminal = 0; + } + cleanup_charset_state(&test); + } + buff[0] = 0x7f; + buff[1] = 0; + buff[2] = 0; + init_charset_state(&test, set); + if ((charset_to_unicode(&test, &symbol, buff, sizeof(buff)) != 1) || + (symbol != U_DELETE)) { + is_terminal = 0; + } + cleanup_charset_state(&test); + + return is_terminal; +} + +struct char_set *get_terminal_charset(struct char_set *set) +{ + struct char_set *result; + if (!set) { + return 0; + } + if (is_terminal_charset(set)) { + result = set; + } + else { + static const char *prefix = "terminal_"; + char *name; + name = malloc(strlen(prefix) + strlen(set->names[0]) + 1); + sprintf(name, "%s%s", prefix, set->names[0]); + result = lookup_charset(name); + if (result) { + free(name); + } + else { + result = malloc(sizeof(*result)); + memset(result, '\0', sizeof(*result)); + result->names[0] = name; + result->names[1] = 0; + result->c0 = set; + result->ops = &terminal_charset_ops; + register_charset(result); + } + } + return result; +} + +/* + * Display Charset operations + * ============================ + */ + +struct is_display_charset_state { + int stateful; + int out_of_range; + int control; + unsigned char byte[256]; +}; + +static void is_display_charset_callback(void *callback_data, + t_unicode symbol, const unsigned char *bytes, size_t byte_len) +{ + struct is_display_charset_state *state = callback_data; + if (symbol < 0x1F || (symbol >= 0x80 && symbol <= 0x9F)) { + state->control = 1; + } + if (byte_len != 1) { + state->out_of_range = 1; + } + else { + /* If a character is repeated + * take that as evidence of a + * stateful character set... + */ + if (state->byte[bytes[0]]) { + state->stateful = 1; + } + state->byte[bytes[0]] = 1; + } +} + +int is_display_charset(struct char_set *set) +{ + struct is_display_charset_state state; + int is_display; + int i; + + /* A display character set (i.e. one that reads from video memory) + * should have the property of having 256 characters, no state, + * and no control characters... + */ + + memset(&state, '\0', sizeof(state)); + state.stateful = state.out_of_range = state.control = 0; + + foreach_character_mapping(set, &state, is_display_charset_callback); + + is_display = 1; + for(i = 0; i < 256; i++) { + is_display &= !!state.byte[i]; + } + is_display &= !state.stateful; + is_display &= !state.out_of_range; + is_display &= !state.control; + return is_display; +} diff --git a/src/base/lib/translate/keysym_approximations.c b/src/base/lib/translate/keysym_approximations.c new file mode 100644 index 0000000..ab7dc87 --- /dev/null +++ b/src/base/lib/translate/keysym_approximations.c @@ -0,0 +1,359 @@ +#include "translate/unicode_symbols.h" +#include "keyboard/keyboard.h" +#include "translate/translate.h" +#include "plugin_config.h" + +struct unicode_close_match { + t_unicode symbol; /* A unicode symbol for which we have an approximation */ + t_unicode approximation; /* A symbol that closely resembles symbol */ +}; + +/* Note: order is important in this list, + * For a given symbol the approximations that is closes should + * come first. + */ +static const struct unicode_close_match approximations[] = +{ + /* Approximation for unmapped characters */ + { U_REPLACEMENT_CHARACTER, U_QUESTION_MARK }, + + /* Approximations on dosemus special keysyms + * in the private unicode area. + */ + { DKY_PAD_0, U_DIGIT_ZERO }, + { DKY_PAD_1, U_DIGIT_ONE }, + { DKY_PAD_2, U_DIGIT_TWO }, + { DKY_PAD_3, U_DIGIT_THREE }, + { DKY_PAD_4, U_DIGIT_FOUR }, + { DKY_PAD_5, U_DIGIT_FIVE }, + { DKY_PAD_6, U_DIGIT_SIX }, + { DKY_PAD_7, U_DIGIT_SEVEN }, + { DKY_PAD_8, U_DIGIT_EIGHT }, + { DKY_PAD_9, U_DIGIT_NINE }, + { DKY_PAD_DECIMAL, U_PERIOD }, +#ifdef HAVE_UNICODE_KEYB + { DKY_PAD_SEPARATOR, U_COMMA }, +#endif + { DKY_PAD_SLASH, U_SLASH }, + { DKY_PAD_AST, U_ASTERISK }, + { DKY_PAD_MINUS, U_HYPHEN_MINUS }, + { DKY_PAD_PLUS, U_PLUS_SIGN }, + { DKY_PAD_ENTER, U_CARRIAGE_RETURN }, + + /* Be careful with these DKY_XXXX -> U_NULL mappings. + * These are crucial for the keyboard code to work. + * These mappings prevent unmapped function keys from + * having the same mapping as unmapped characters, + * which are o.k. to display as a strange character. + * + */ + { DKY_PAD_HOME, U_NULL }, + { DKY_PAD_UP, U_NULL }, + { DKY_PAD_PGUP, U_NULL }, + + { DKY_PAD_LEFT, U_NULL }, + { DKY_PAD_CENTER, U_NULL }, + { DKY_PAD_RIGHT, U_NULL }, + { DKY_PAD_END, U_NULL }, + { DKY_PAD_DOWN, U_NULL }, + { DKY_PAD_PGDN, U_NULL }, + { DKY_PAD_INS, U_NULL }, + { DKY_PAD_DEL, U_NULL }, + + + { DKY_ESC, U_NULL }, + { DKY_F1, U_NULL }, + { DKY_F2, U_NULL }, + { DKY_F3, U_NULL }, + { DKY_F4, U_NULL }, + { DKY_F5, U_NULL }, + { DKY_F6, U_NULL }, + { DKY_F7, U_NULL }, + { DKY_F8, U_NULL }, + { DKY_F9, U_NULL }, + { DKY_F10, U_NULL }, + { DKY_F11, U_NULL }, + { DKY_F12, U_NULL }, + + + { DKY_INS, U_NULL }, + { DKY_DEL, U_NULL }, + { DKY_HOME, U_NULL }, + { DKY_END, U_NULL }, + { DKY_PGUP, U_NULL }, + { DKY_PGDN, U_NULL }, + { DKY_UP, U_NULL }, + { DKY_DOWN, U_NULL }, + { DKY_LEFT, U_NULL }, + { DKY_RIGHT, U_NULL }, + + { DKY_L_ALT, U_NULL }, + { DKY_R_ALT, U_NULL }, + { DKY_L_CTRL, U_NULL }, + { DKY_R_CTRL, U_NULL }, + { DKY_L_SHIFT, U_NULL }, + { DKY_R_SHIFT, U_NULL }, + { DKY_NUM, U_NULL }, + { DKY_SCROLL, U_NULL }, + { DKY_CAPS, U_NULL }, + + { DKY_PRTSCR, U_NULL }, + { DKY_PAUSE, U_NULL }, + { DKY_SYSRQ, U_NULL }, + { DKY_BREAK, U_NULL }, + + { DKY_PAD_SEPARATOR, U_NULL }, + + { DKY_ALT_A, U_NULL }, + { DKY_ALT_B, U_NULL }, + { DKY_ALT_C, U_NULL }, + { DKY_ALT_D, U_NULL }, + { DKY_ALT_E, U_NULL }, + { DKY_ALT_F, U_NULL }, + { DKY_ALT_G, U_NULL }, + { DKY_ALT_H, U_NULL }, + { DKY_ALT_I, U_NULL }, + { DKY_ALT_J, U_NULL }, + { DKY_ALT_K, U_NULL }, + { DKY_ALT_L, U_NULL }, + { DKY_ALT_M, U_NULL }, + { DKY_ALT_N, U_NULL }, + { DKY_ALT_O, U_NULL }, + { DKY_ALT_P, U_NULL }, + { DKY_ALT_Q, U_NULL }, + { DKY_ALT_R, U_NULL }, + { DKY_ALT_S, U_NULL }, + { DKY_ALT_T, U_NULL }, + { DKY_ALT_U, U_NULL }, + { DKY_ALT_V, U_NULL }, + { DKY_ALT_W, U_NULL }, + { DKY_ALT_X, U_NULL }, + { DKY_ALT_Y, U_NULL }, + { DKY_ALT_Z, U_NULL }, + + { DKY_LEFT_TAB, U_NULL }, + + /* General approximations */ + { U_HOUSE, U_CIRCUMFLEX_ACCENT }, + + { U_WHITE_SMILING_FACE, U_LATIN_SMALL_LETTER_O_WITH_DIAERESIS }, + { U_BLACK_SMILING_FACE, U_LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS }, + { U_BLACK_HEART_SUIT, U_LATIN_SMALL_LETTER_H }, + { U_BLACK_DIAMOND_SUIT, U_LATIN_SMALL_LETTER_D }, + { U_BLACK_CLUB_SUIT, U_LATIN_SMALL_LETTER_C }, + { U_BLACK_SPADE_SUIT, U_LATIN_SMALL_LETTER_S }, + + { U_BULLET, U_BULLET_OPERATOR }, + { U_BULLET, U_DEGREE_SIGN }, + { U_BULLET, U_MIDDLE_DOT }, + { U_BULLET, U_PLUS_SIGN }, + { U_INVERSE_BULLET, U_DARK_SHADE }, + { U_INVERSE_BULLET, U_NUMBER_SIGN }, + { U_WHITE_CIRCLE, U_LATIN_SMALL_LETTER_O }, + { U_INVERSE_WHITE_CIRCLE, U_MEDIUM_SHADE }, + { U_INVERSE_WHITE_CIRCLE, U_DIGIT_ZERO }, + { U_MALE_SIGN, U_LATIN_SMALL_LETTER_O_WITH_ACUTE }, + { U_FEMALE_SIGN, U_YEN_SIGN }, + { U_FEMALE_SIGN, U_PLUS_SIGN }, + { U_EIGHTH_NOTE, U_SQUARE_ROOT }, + { U_EIGHTH_NOTE, U_SEMICOLON }, + { U_BEAMED_EIGHTH_NOTES, U_INTERSECTION }, + { U_BEAMED_EIGHTH_NOTES, U_LATIN_CAPITAL_LETTER_M }, + { U_WHITE_SUN_WITH_RAYS, U_ASTERISK }, + + { U_BLACK_RIGHT_POINTING_POINTER, U_GREATER_THAN_SIGN}, + { U_BLACK_LEFT_POINTING_POINTER, U_LESS_THAN_SIGN}, + { U_BLACK_RIGHT_POINTING_SMALL_TRIANGLE, U_GREATER_THAN_SIGN}, + { U_BLACK_LEFT_POINTING_SMALL_TRIANGLE, U_LESS_THAN_SIGN}, + { U_UP_DOWN_ARROW, U_INVERTED_EXCLAMATION_MARK}, + { U_UP_DOWN_ARROW, U_VERTICAL_LINE}, + { U_DOUBLE_EXCLAMATION_MARK, U_BOX_DRAWINGS_DOUBLE_VERTICAL}, + { U_DOUBLE_EXCLAMATION_MARK, U_LATIN_CAPITAL_LETTER_H}, + { U_PILCROW_SIGN, U_GREEK_SMALL_LETTER_PI}, + { U_PILCROW_SIGN, U_LATIN_CAPITAL_LETTER_P}, + { U_SECTION_SIGN, U_POUND_SIGN}, + { U_SECTION_SIGN, U_LATIN_SMALL_LETTER_F_WITH_HOOK}, + { U_BLACK_RECTANGLE, U_BLACK_SQUARE}, + { U_BLACK_RECTANGLE, U_PLUS_SIGN}, + { U_UP_DOWN_ARROW_WITH_BASE, U_INVERTED_QUESTION_MARK}, + { U_UP_DOWN_ARROW_WITH_BASE, U_VERTICAL_LINE}, + { U_UPWARDS_ARROW, U_TOP_HALF_INTEGRAL}, + { U_UPWARDS_ARROW, U_CIRCUMFLEX_ACCENT}, + { U_DOWNWARDS_ARROW, U_BOTTOM_HALF_INTEGRAL}, + { U_DOWNWARDS_ARROW, U_LATIN_SMALL_LETTER_V}, + { U_RIGHTWARDS_ARROW, U_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK}, + { U_RIGHTWARDS_ARROW, U_GREATER_THAN_SIGN}, + { U_LEFTWARDS_ARROW, U_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK}, + { U_LEFTWARDS_ARROW, U_LESS_THAN_SIGN}, + { U_RIGHT_ANGLE, U_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT}, + { U_RIGHT_ANGLE, U_HYPHEN_MINUS}, + { U_LEFT_RIGHT_ARROW, U_BOX_DRAWINGS_DOUBLE_HORIZONTAL}, + { U_LEFT_RIGHT_ARROW, U_HYPHEN_MINUS}, + { U_BLACK_UP_POINTING_TRIANGLE, U_CIRCUMFLEX_ACCENT}, + { U_BLACK_DOWN_POINTING_TRIANGLE, U_LATIN_SMALL_LETTER_V}, + + { U_PESETA_SIGN, U_LATIN_CAPITAL_LETTER_P}, + { U_LATIN_SMALL_LETTER_F_WITH_HOOK, U_LATIN_SMALL_LETTER_F}, + + { U_REVERSED_NOT_SIGN, U_HYPHEN_MINUS }, + + { U_LIGHT_SHADE, U_COLON }, + { U_MEDIUM_SHADE, U_PERCENT_SIGN}, + { U_DARK_SHADE, U_AMPERSAND}, + { U_BOX_DRAWINGS_LIGHT_VERTICAL, U_VERTICAL_LINE}, + { U_BOX_DRAWINGS_LIGHT_VERTICAL_AND_LEFT, U_LEFT_CURLY_BRACKET}, + { U_BOX_DRAWINGS_VERTICAL_SINGLE_AND_LEFT_DOUBLE, U_LEFT_CURLY_BRACKET}, + { U_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_LEFT_SINGLE, U_LEFT_CURLY_BRACKET}, + { U_BOX_DRAWINGS_DOWN_DOUBLE_AND_LEFT_SINGLE, U_PERIOD}, + { U_BOX_DRAWINGS_DOWN_SINGLE_AND_LEFT_DOUBLE, U_PERIOD}, + { U_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_LEFT, U_LEFT_CURLY_BRACKET}, + { U_BOX_DRAWINGS_DOUBLE_VERTICAL, U_LATIN_CAPITAL_LETTER_I}, + { U_BOX_DRAWINGS_DOUBLE_DOWN_AND_LEFT, U_PERIOD}, + { U_BOX_DRAWINGS_DOUBLE_UP_AND_LEFT, U_APOSTROPHE}, + { U_BOX_DRAWINGS_UP_DOUBLE_AND_LEFT_SINGLE, U_APOSTROPHE}, + { U_BOX_DRAWINGS_UP_SINGLE_AND_LEFT_DOUBLE, U_APOSTROPHE}, + { U_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT, U_NOT_SIGN}, + { U_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT, U_PERIOD}, + + { U_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT, U_GRAVE_ACCENT}, + { U_BOX_DRAWINGS_LIGHT_UP_AND_HORIZONTAL, U_GRAVE_ACCENT}, + { U_BOX_DRAWINGS_LIGHT_DOWN_AND_HORIZONTAL, U_PLUS_SIGN}, + { U_BOX_DRAWINGS_LIGHT_VERTICAL_AND_RIGHT, U_RIGHT_CURLY_BRACKET}, + { U_BOX_DRAWINGS_LIGHT_HORIZONTAL, U_HYPHEN_MINUS}, + { U_BOX_DRAWINGS_LIGHT_VERTICAL_AND_HORIZONTAL, U_PLUS_SIGN}, + { U_BOX_DRAWINGS_VERTICAL_SINGLE_AND_RIGHT_DOUBLE, U_RIGHT_CURLY_BRACKET}, + { U_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_RIGHT_SINGLE, U_RIGHT_CURLY_BRACKET}, + { U_BOX_DRAWINGS_DOUBLE_UP_AND_RIGHT, U_GRAVE_ACCENT}, + { U_BOX_DRAWINGS_DOUBLE_DOWN_AND_RIGHT, U_PERIOD}, + { U_BOX_DRAWINGS_DOUBLE_UP_AND_HORIZONTAL, U_ASTERISK}, + { U_BOX_DRAWINGS_DOUBLE_DOWN_AND_HORIZONTAL, U_ASTERISK}, + { U_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_RIGHT, U_RIGHT_CURLY_BRACKET}, + { U_BOX_DRAWINGS_DOUBLE_HORIZONTAL, U_EQUALS_SIGN}, + { U_BOX_DRAWINGS_DOUBLE_VERTICAL_AND_HORIZONTAL, U_ASTERISK}, + { U_BOX_DRAWINGS_UP_SINGLE_AND_HORIZONTAL_DOUBLE, U_ASTERISK}, + + { U_BOX_DRAWINGS_UP_DOUBLE_AND_HORIZONTAL_SINGLE, U_PLUS_SIGN}, + { U_BOX_DRAWINGS_DOWN_SINGLE_AND_HORIZONTAL_DOUBLE, U_ASTERISK}, + { U_BOX_DRAWINGS_DOWN_DOUBLE_AND_HORIZONTAL_SINGLE, U_PLUS_SIGN}, + { U_BOX_DRAWINGS_UP_DOUBLE_AND_RIGHT_SINGLE, U_GRAVE_ACCENT}, + { U_BOX_DRAWINGS_UP_SINGLE_AND_RIGHT_DOUBLE, U_GRAVE_ACCENT}, + { U_BOX_DRAWINGS_DOWN_SINGLE_AND_RIGHT_DOUBLE, U_PERIOD}, + { U_BOX_DRAWINGS_DOWN_DOUBLE_AND_RIGHT_SINGLE, U_PERIOD}, + { U_BOX_DRAWINGS_VERTICAL_DOUBLE_AND_HORIZONTAL_SINGLE, U_ASTERISK}, + { U_BOX_DRAWINGS_VERTICAL_SINGLE_AND_HORIZONTAL_DOUBLE, U_ASTERISK}, + { U_BOX_DRAWINGS_LIGHT_UP_AND_LEFT, U_APOSTROPHE}, + { U_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT, U_PERIOD}, + { U_FULL_BLOCK, U_NUMBER_SIGN}, + { U_LOWER_HALF_BLOCK, U_LOW_LINE}, + { U_LEFT_HALF_BLOCK, U_RIGHT_SQUARE_BRACKET}, + { U_RIGHT_HALF_BLOCK, U_LEFT_SQUARE_BRACKET}, + { U_UPPER_HALF_BLOCK, U_TILDE}, + + { U_GREEK_SMALL_LETTER_ALPHA, U_LATIN_SMALL_LETTER_A}, + + { U_GREEK_CAPITAL_LETTER_GAMMA, U_NOT_SIGN}, + { U_GREEK_SMALL_LETTER_PI, U_PILCROW_SIGN}, + { U_GREEK_CAPITAL_LETTER_SIGMA, U_LEFT_CURLY_BRACKET}, + { U_GREEK_SMALL_LETTER_SIGMA, U_LATIN_SMALL_LETTER_O_WITH_ACUTE}, + + { U_GREEK_SMALL_LETTER_TAU, U_LATIN_SMALL_LETTER_T}, + { U_GREEK_CAPITAL_LETTER_PHI, U_LATIN_CAPITAL_LETTER_O_WITH_STROKE}, + { U_GREEK_CAPITAL_LETTER_THETA, U_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX}, + { U_GREEK_CAPITAL_LETTER_OMEGA, U_LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS}, + { U_GREEK_SMALL_LETTER_DELTA, U_LATIN_SMALL_LETTER_O_WITH_ACUTE}, + { U_INFINITY, U_LATIN_SMALL_LETTER_O}, + { U_GREEK_SMALL_LETTER_PHI, U_LATIN_SMALL_LETTER_O_WITH_STROKE}, + { U_GREEK_SMALL_LETTER_EPSILON, U_LATIN_CAPITAL_LETTER_E}, + { U_INTERSECTION, U_LATIN_SMALL_LETTER_N}, + + { U_IDENTICAL_TO, U_EQUALS_SIGN}, + + { U_GREATER_THAN_OR_EQUAL_TO, U_GREATER_THAN_SIGN}, + { U_LESS_THAN_OR_EQUAL_TO, U_LESS_THAN_SIGN}, + { U_TOP_HALF_INTEGRAL, U_LEFT_PARENTHESIS}, + { U_BOTTOM_HALF_INTEGRAL, U_RIGHT_PARENTHESIS}, + + { U_ALMOST_EQUAL_TO, U_EQUALS_SIGN}, + + { U_BULLET_OPERATOR, U_MIDDLE_DOT}, + + { U_SQUARE_ROOT, U_PERCENT_SIGN}, + { U_SUPERSCRIPT_LATIN_SMALL_LETTER_N, U_SUPERSCRIPT_THREE}, + { U_SUPERSCRIPT_LATIN_SMALL_LETTER_N, U_GRAVE_ACCENT}, + + { U_BLACK_SQUARE, U_EQUALS_SIGN }, + + { U_LATIN_SMALL_LETTER_DOTLESS_I, U_PERIOD}, + { U_DOUBLE_LOW_LINE, U_GREATER_THAN_SIGN }, + + { U_NOT_SIGN, U_HYPHEN_MINUS}, + { U_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK, U_LESS_THAN_SIGN }, + { U_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK, U_GREATER_THAN_SIGN }, + + /* reverse map */ + { U_BROKEN_BAR, U_VERTICAL_LINE }, + { U_DIAERESIS, U_QUOTATION_MARK }, + { U_SOFT_HYPHEN, U_HYPHEN_MINUS }, + { U_SUPERSCRIPT_THREE, U_SUPERSCRIPT_LATIN_SMALL_LETTER_N }, + { U_ACUTE_ACCENT, U_APOSTROPHE }, + { U_MIDDLE_DOT, U_BULLET_OPERATOR }, + { U_CEDILLA, U_COMMA }, + { U_LATIN_CAPITAL_LETTER_O_WITH_STROKE, U_GREEK_SMALL_LETTER_PHI }, + { U_LATIN_SMALL_LETTER_O_WITH_STROKE, U_GREEK_SMALL_LETTER_PHI }, +/* + { U_CURRENCY_SIGN, x }, + { U_COPYRIGHT_SIGN, x }, + { U_REGISTERED_SIGN, x }, + { U_MACRON, x }, + { U_SUPERSCRIPT_ONE, x }, + { U_VULGAR_FRACTION_THREE_QUARTERS, x }, +*/ + { U_LATIN_CAPITAL_LETTER_A_WITH_GRAVE, U_LATIN_CAPITAL_LETTER_A }, + { U_LATIN_CAPITAL_LETTER_A_WITH_ACUTE, U_LATIN_CAPITAL_LETTER_A }, + { U_LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX, U_LATIN_CAPITAL_LETTER_A }, + { U_LATIN_CAPITAL_LETTER_A_WITH_TILDE, U_LATIN_CAPITAL_LETTER_A }, + { U_LATIN_CAPITAL_LETTER_E_WITH_GRAVE, U_LATIN_CAPITAL_LETTER_E }, + { U_LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX, U_LATIN_CAPITAL_LETTER_E }, + { U_LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS, U_LATIN_CAPITAL_LETTER_E }, + { U_LATIN_CAPITAL_LETTER_I_WITH_GRAVE, U_LATIN_CAPITAL_LETTER_I }, + { U_LATIN_CAPITAL_LETTER_I_WITH_ACUTE, U_LATIN_CAPITAL_LETTER_I }, + { U_LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX, U_LATIN_CAPITAL_LETTER_I }, + { U_LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS, U_LATIN_CAPITAL_LETTER_I }, + { U_LATIN_CAPITAL_LETTER_ETH, U_LATIN_SMALL_LETTER_D }, + { U_LATIN_CAPITAL_LETTER_O_WITH_GRAVE, U_LATIN_CAPITAL_LETTER_O }, + { U_LATIN_CAPITAL_LETTER_O_WITH_ACUTE, U_LATIN_CAPITAL_LETTER_O }, + { U_LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX, U_LATIN_CAPITAL_LETTER_O }, + { U_LATIN_CAPITAL_LETTER_O_WITH_TILDE, U_LATIN_CAPITAL_LETTER_O }, + { U_MULTIPLICATION_SIGN, U_LATIN_SMALL_LETTER_X }, + { U_LATIN_CAPITAL_LETTER_U_WITH_GRAVE, U_LATIN_CAPITAL_LETTER_U }, + { U_LATIN_CAPITAL_LETTER_U_WITH_ACUTE, U_LATIN_CAPITAL_LETTER_U }, + { U_LATIN_CAPITAL_LETTER_THORN, U_LATIN_CAPITAL_LETTER_P }, + { U_LATIN_SMALL_LETTER_SHARP_S, U_LATIN_SMALL_LETTER_S }, + { U_LATIN_SMALL_LETTER_A_WITH_TILDE, U_LATIN_SMALL_LETTER_A }, + { U_LATIN_SMALL_LETTER_ETH, U_LATIN_SMALL_LETTER_D }, + { U_LATIN_SMALL_LETTER_O_WITH_TILDE, U_LATIN_SMALL_LETTER_O }, + { U_LATIN_SMALL_LETTER_Y_WITH_ACUTE, U_LATIN_SMALL_LETTER_Y }, + { U_LATIN_SMALL_LETTER_THORN, U_LATIN_SMALL_LETTER_P } +}; +static const int approximation_count = sizeof(approximations)/sizeof(approximations[0]); + +void traverse_approximations(t_unicode symbol, void *p, approximations_callback_t callback) +{ + int i; + for(i = 0; i < approximation_count; i++) { + if (approximations[i].symbol == symbol) { + callback(p, approximations[i].approximation); + } + } + return; +} + +void traverse_approximation_list(void *p, approximation_list_callback_t callback) +{ + int i; + for(i = 0; i < approximation_count; i++) { + callback(p, + approximations[i].symbol, + approximations[i].approximation); + } +} diff --git a/src/base/lib/translate/keysym_dead_map.c b/src/base/lib/translate/keysym_dead_map.c new file mode 100644 index 0000000..9a58a9b --- /dev/null +++ b/src/base/lib/translate/keysym_dead_map.c @@ -0,0 +1,1237 @@ +#include "translate/keysym_attributes.h" +/* This information was obtained by taking the unicode list of decompositions + * ordering it by `dead key' and trimming off what seemed inapropriate. + * This information may be excisve or incomplete. + * The only test it has undergone is comparing it against the old dead key maps + * in keymaps.c to ensure it is a complete super set, of the old information. + * + * The mapping of a standalone diaersis has changed from double quotes + * to a standalone diaerisis. + */ + +struct dead_map { + t_keysym dead_key; + t_keysym in; + t_keysym out; +}; + +static const struct dead_map keysym_dead_map[] = +{ +/* 0302 caret/circumflex 0302 */ +{ 0x0302, 0x0302, 0x005E }, /* just for keyboard */ +{ 0x0302, 0x0020, 0x005E }, +{ 0x0302, 0x0041, 0x00C2 }, +{ 0x0302, 0x0045, 0x00CA }, +{ 0x0302, 0x0049, 0x00CE }, +{ 0x0302, 0x004F, 0x00D4 }, +{ 0x0302, 0x0055, 0x00DB }, +{ 0x0302, 0x0061, 0x00E2 }, +{ 0x0302, 0x0065, 0x00EA }, +{ 0x0302, 0x0069, 0x00EE }, +{ 0x0302, 0x006F, 0x00F4 }, +{ 0x0302, 0x0075, 0x00FB }, +{ 0x0302, 0x0043, 0x0108 }, +{ 0x0302, 0x0063, 0x0109 }, +{ 0x0302, 0x0047, 0x011C }, +{ 0x0302, 0x0067, 0x011D }, +{ 0x0302, 0x0048, 0x0124 }, +{ 0x0302, 0x0068, 0x0125 }, +{ 0x0302, 0x004A, 0x0134 }, +{ 0x0302, 0x006A, 0x0135 }, +{ 0x0302, 0x0053, 0x015C }, +{ 0x0302, 0x0073, 0x015D }, +{ 0x0302, 0x0057, 0x0174 }, +{ 0x0302, 0x0077, 0x0175 }, +{ 0x0302, 0x0059, 0x0176 }, +{ 0x0302, 0x0079, 0x0177 }, +{ 0x0302, 0x005A, 0x1E90 }, +{ 0x0302, 0x007A, 0x1E91 }, + +/* 0300 Grave Accent */ +{ 0x0300, 0x0300, 0x0060 }, /* just for keyboard */ +{ 0x0300, 0x0020, 0x0060 }, +{ 0x0300, 0x0041, 0x00C0 }, +{ 0x0300, 0x0045, 0x00C8 }, +{ 0x0300, 0x0049, 0x00CC }, +{ 0x0300, 0x004F, 0x00D2 }, +{ 0x0300, 0x0055, 0x00D9 }, +{ 0x0300, 0x0061, 0x00E0 }, +{ 0x0300, 0x0065, 0x00E8 }, +{ 0x0300, 0x0069, 0x00EC }, +{ 0x0300, 0x006F, 0x00F2 }, +{ 0x0300, 0x0075, 0x00F9 }, +{ 0x0300, 0x00DC, 0x01DB }, +{ 0x0300, 0x00FC, 0x01DC }, +{ 0x0300, 0x0112, 0x1E14 }, +{ 0x0300, 0x0113, 0x1E15 }, +{ 0x0300, 0x014C, 0x1E50 }, +{ 0x0300, 0x014D, 0x1E51 }, +{ 0x0300, 0x0057, 0x1E80 }, +{ 0x0300, 0x0077, 0x1E81 }, +{ 0x0300, 0x00C2, 0x1EA6 }, +{ 0x0300, 0x00E2, 0x1EA7 }, +{ 0x0300, 0x0102, 0x1EB0 }, +{ 0x0300, 0x0103, 0x1EB1 }, +{ 0x0300, 0x00CA, 0x1EC0 }, +{ 0x0300, 0x00EA, 0x1EC1 }, +{ 0x0300, 0x00D4, 0x1ED2 }, +{ 0x0300, 0x00F4, 0x1ED3 }, +{ 0x0300, 0x01A0, 0x1EDC }, +{ 0x0300, 0x01A1, 0x1EDD }, +{ 0x0300, 0x01AF, 0x1EEA }, +{ 0x0300, 0x01B0, 0x1EEB }, +{ 0x0300, 0x0059, 0x1EF2 }, +{ 0x0300, 0x0079, 0x1EF3 }, +{ 0x0300, 0x1F00, 0x1F02 }, +{ 0x0300, 0x1F01, 0x1F03 }, +{ 0x0300, 0x1F08, 0x1F0A }, +{ 0x0300, 0x1F09, 0x1F0B }, +{ 0x0300, 0x1F10, 0x1F12 }, +{ 0x0300, 0x1F11, 0x1F13 }, +{ 0x0300, 0x1F18, 0x1F1A }, +{ 0x0300, 0x1F19, 0x1F1B }, +{ 0x0300, 0x1F20, 0x1F22 }, +{ 0x0300, 0x1F21, 0x1F23 }, +{ 0x0300, 0x1F28, 0x1F2A }, +{ 0x0300, 0x1F29, 0x1F2B }, +{ 0x0300, 0x1F30, 0x1F32 }, +{ 0x0300, 0x1F31, 0x1F33 }, +{ 0x0300, 0x1F38, 0x1F3A }, +{ 0x0300, 0x1F39, 0x1F3B }, +{ 0x0300, 0x1F40, 0x1F42 }, +{ 0x0300, 0x1F41, 0x1F43 }, +{ 0x0300, 0x1F48, 0x1F4A }, +{ 0x0300, 0x1F49, 0x1F4B }, +{ 0x0300, 0x1F50, 0x1F52 }, +{ 0x0300, 0x1F51, 0x1F53 }, +{ 0x0300, 0x1F59, 0x1F5B }, +{ 0x0300, 0x1F60, 0x1F62 }, +{ 0x0300, 0x1F61, 0x1F63 }, +{ 0x0300, 0x1F68, 0x1F6A }, +{ 0x0300, 0x1F69, 0x1F6B }, +{ 0x0300, 0x03B1, 0x1F70 }, +{ 0x0300, 0x03B5, 0x1F72 }, +{ 0x0300, 0x03B7, 0x1F74 }, +{ 0x0300, 0x03B9, 0x1F76 }, +{ 0x0300, 0x03BF, 0x1F78 }, +{ 0x0300, 0x03C5, 0x1F7A }, +{ 0x0300, 0x03C9, 0x1F7C }, +{ 0x0300, 0x0391, 0x1FBA }, +{ 0x0300, 0x0395, 0x1FC8 }, +{ 0x0300, 0x0397, 0x1FCA }, +{ 0x0300, 0x1FBF, 0x1FCD }, +{ 0x0300, 0x03CA, 0x1FD2 }, +{ 0x0300, 0x0399, 0x1FDA }, +{ 0x0300, 0x1FFE, 0x1FDD }, +{ 0x0300, 0x03CB, 0x1FE2 }, +{ 0x0300, 0x03A5, 0x1FEA }, +{ 0x0300, 0x00A8, 0x1FED }, +{ 0x0300, 0x039F, 0x1FF8 }, +{ 0x0300, 0x03A9, 0x1FFA }, + +/* 0308 Diaeresis */ +{ 0x0308, 0x0308, 0x00A8 }, /* just for keyboard */ +{ 0x0308, 0x0020, 0x00A8 }, +{ 0x0308, 0x0041, 0x00C4 }, +{ 0x0308, 0x0045, 0x00CB }, +{ 0x0308, 0x0049, 0x00CF }, +{ 0x0308, 0x004F, 0x00D6 }, +{ 0x0308, 0x0055, 0x00DC }, +{ 0x0308, 0x0061, 0x00E4 }, +{ 0x0308, 0x0065, 0x00EB }, +{ 0x0308, 0x0069, 0x00EF }, +{ 0x0308, 0x006F, 0x00F6 }, +{ 0x0308, 0x0075, 0x00FC }, +{ 0x0308, 0x0079, 0x00FF }, +{ 0x0308, 0x0059, 0x0178 }, +{ 0x0308, 0x0399, 0x03AA }, +{ 0x0308, 0x03A5, 0x03AB }, +{ 0x0308, 0x03B9, 0x03CA }, +{ 0x0308, 0x03C5, 0x03CB }, +{ 0x0308, 0x03D2, 0x03D4 }, +{ 0x0308, 0x0415, 0x0401 }, +{ 0x0308, 0x0406, 0x0407 }, +{ 0x0308, 0x0435, 0x0451 }, +{ 0x0308, 0x0456, 0x0457 }, +{ 0x0308, 0x0410, 0x04D2 }, +{ 0x0308, 0x0430, 0x04D3 }, +{ 0x0308, 0x018F, 0x04DA }, +{ 0x0308, 0x0259, 0x04DB }, +{ 0x0308, 0x0416, 0x04DC }, +{ 0x0308, 0x0436, 0x04DD }, +{ 0x0308, 0x0417, 0x04DE }, +{ 0x0308, 0x0437, 0x04DF }, +{ 0x0308, 0x0418, 0x04E4 }, +{ 0x0308, 0x0438, 0x04E5 }, +{ 0x0308, 0x041E, 0x04E6 }, +{ 0x0308, 0x043E, 0x04E7 }, +{ 0x0308, 0x019F, 0x04EA }, +{ 0x0308, 0x0275, 0x04EB }, +{ 0x0308, 0x0423, 0x04F0 }, +{ 0x0308, 0x0443, 0x04F1 }, +{ 0x0308, 0x0427, 0x04F4 }, +{ 0x0308, 0x0447, 0x04F5 }, +{ 0x0308, 0x042B, 0x04F8 }, +{ 0x0308, 0x044B, 0x04F9 }, +{ 0x0308, 0x0048, 0x1E26 }, +{ 0x0308, 0x0068, 0x1E27 }, +{ 0x0308, 0x00D5, 0x1E4E }, +{ 0x0308, 0x00F5, 0x1E4F }, +{ 0x0308, 0x016A, 0x1E7A }, +{ 0x0308, 0x016B, 0x1E7B }, +{ 0x0308, 0x0057, 0x1E84 }, +{ 0x0308, 0x0077, 0x1E85 }, +{ 0x0308, 0x0058, 0x1E8C }, +{ 0x0308, 0x0078, 0x1E8D }, +{ 0x0308, 0x0074, 0x1E97 }, + +/* 0304 Macron */ +/* I can't handle these... ??? + * 01E0;0041 0307 0304 + * 01E1;0061 0307 0304 + */ +{ 0x0304, 0x0304, 0x00AF }, /* just for keyboard */ +{ 0x0304, 0x0020, 0x00AF }, +{ 0x0304, 0x0041, 0x0100 }, +{ 0x0304, 0x0061, 0x0101 }, +{ 0x0304, 0x0045, 0x0112 }, +{ 0x0304, 0x0065, 0x0113 }, +{ 0x0304, 0x0049, 0x012A }, +{ 0x0304, 0x0069, 0x012B }, +{ 0x0304, 0x004F, 0x014C }, +{ 0x0304, 0x006F, 0x014D }, +{ 0x0304, 0x0055, 0x016A }, +{ 0x0304, 0x0075, 0x016B }, +{ 0x0304, 0x00DC, 0x01D5 }, +{ 0x0304, 0x00FC, 0x01D6 }, +{ 0x0304, 0x00C4, 0x01DE }, +{ 0x0304, 0x00E4, 0x01DF }, +{ 0x0304, 0x00C6, 0x01E2 }, +{ 0x0304, 0x00E6, 0x01E3 }, +{ 0x0304, 0x01EA, 0x01EC }, +{ 0x0304, 0x01EB, 0x01ED }, +{ 0x0304, 0x0418, 0x04E2 }, +{ 0x0304, 0x0438, 0x04E3 }, +{ 0x0304, 0x0423, 0x04EE }, +{ 0x0304, 0x0443, 0x04EF }, +{ 0x0304, 0x0047, 0x1E20 }, +{ 0x0304, 0x0067, 0x1E21 }, +{ 0x0304, 0x1E36, 0x1E38 }, +{ 0x0304, 0x1E37, 0x1E39 }, +{ 0x0304, 0x1E5A, 0x1E5C }, +{ 0x0304, 0x1E5B, 0x1E5D }, +{ 0x0304, 0x03B1, 0x1FB1 }, +{ 0x0304, 0x0391, 0x1FB9 }, +{ 0x0304, 0x03B9, 0x1FD1 }, +{ 0x0304, 0x0399, 0x1FD9 }, +{ 0x0304, 0x03C5, 0x1FE1 }, +{ 0x0304, 0x03A5, 0x1FE9 }, + +/* 0301 Acute Accent */ +{ 0x0301, 0x0301, 0x00B4 }, /* just for keyboard */ +{ 0x0301, 0x0020, 0x00B4 }, +{ 0x0301, 0x0041, 0x00C1 }, +{ 0x0301, 0x0045, 0x00C9 }, +{ 0x0301, 0x0049, 0x00CD }, +{ 0x0301, 0x004F, 0x00D3 }, +{ 0x0301, 0x0055, 0x00DA }, +{ 0x0301, 0x0059, 0x00DD }, +{ 0x0301, 0x0061, 0x00E1 }, +{ 0x0301, 0x0065, 0x00E9 }, +{ 0x0301, 0x0069, 0x00ED }, +{ 0x0301, 0x006F, 0x00F3 }, +{ 0x0301, 0x0075, 0x00FA }, +{ 0x0301, 0x0079, 0x00FD }, +{ 0x0301, 0x0043, 0x0106 }, +{ 0x0301, 0x0063, 0x0107 }, +{ 0x0301, 0x004C, 0x0139 }, +{ 0x0301, 0x006C, 0x013A }, +{ 0x0301, 0x004E, 0x0143 }, +{ 0x0301, 0x006E, 0x0144 }, +{ 0x0301, 0x0052, 0x0154 }, +{ 0x0301, 0x0072, 0x0155 }, +{ 0x0301, 0x0053, 0x015A }, +{ 0x0301, 0x0073, 0x015B }, +{ 0x0301, 0x005A, 0x0179 }, +{ 0x0301, 0x007A, 0x017A }, +{ 0x0301, 0x00DC, 0x01D7 }, +{ 0x0301, 0x00FC, 0x01D8 }, +{ 0x0301, 0x0047, 0x01F4 }, +{ 0x0301, 0x0067, 0x01F5 }, +{ 0x0301, 0x00C5, 0x01FA }, +{ 0x0301, 0x00E5, 0x01FB }, +{ 0x0301, 0x00C6, 0x01FC }, +{ 0x0301, 0x00E6, 0x01FD }, +{ 0x0301, 0x00D8, 0x01FE }, +{ 0x0301, 0x00F8, 0x01FF }, +{ 0x0301, 0x0413, 0x0403 }, +{ 0x0301, 0x041A, 0x040C }, +{ 0x0301, 0x0433, 0x0453 }, +{ 0x0301, 0x043A, 0x045C }, +{ 0x0301, 0x00C7, 0x1E08 }, +{ 0x0301, 0x00E7, 0x1E09 }, +{ 0x0301, 0x0112, 0x1E16 }, +{ 0x0301, 0x0113, 0x1E17 }, +{ 0x0301, 0x00CF, 0x1E2E }, +{ 0x0301, 0x00EF, 0x1E2F }, +{ 0x0301, 0x004B, 0x1E30 }, +{ 0x0301, 0x006B, 0x1E31 }, +{ 0x0301, 0x004D, 0x1E3E }, +{ 0x0301, 0x006D, 0x1E3F }, +{ 0x0301, 0x00D5, 0x1E4C }, +{ 0x0301, 0x00F5, 0x1E4D }, +{ 0x0301, 0x014C, 0x1E52 }, +{ 0x0301, 0x014D, 0x1E53 }, +{ 0x0301, 0x0050, 0x1E54 }, +{ 0x0301, 0x0070, 0x1E55 }, +{ 0x0301, 0x0168, 0x1E78 }, +{ 0x0301, 0x0169, 0x1E79 }, +{ 0x0301, 0x0057, 0x1E82 }, +{ 0x0301, 0x0077, 0x1E83 }, +{ 0x0301, 0x00C2, 0x1EA4 }, +{ 0x0301, 0x00E2, 0x1EA5 }, +{ 0x0301, 0x0102, 0x1EAE }, +{ 0x0301, 0x0103, 0x1EAF }, +{ 0x0301, 0x00CA, 0x1EBE }, +{ 0x0301, 0x00EA, 0x1EBF }, +{ 0x0301, 0x00D4, 0x1ED0 }, +{ 0x0301, 0x00F4, 0x1ED1 }, +{ 0x0301, 0x01A0, 0x1EDA }, +{ 0x0301, 0x01A1, 0x1EDB }, +{ 0x0301, 0x01AF, 0x1EE8 }, +{ 0x0301, 0x01B0, 0x1EE9 }, +{ 0x0301, 0x1F00, 0x1F04 }, +{ 0x0301, 0x1F01, 0x1F05 }, +{ 0x0301, 0x1F08, 0x1F0C }, +{ 0x0301, 0x1F09, 0x1F0D }, +{ 0x0301, 0x1F10, 0x1F14 }, +{ 0x0301, 0x1F11, 0x1F15 }, +{ 0x0301, 0x1F18, 0x1F1C }, +{ 0x0301, 0x1F19, 0x1F1D }, +{ 0x0301, 0x1F20, 0x1F24 }, +{ 0x0301, 0x1F21, 0x1F25 }, +{ 0x0301, 0x1F28, 0x1F2C }, +{ 0x0301, 0x1F29, 0x1F2D }, +{ 0x0301, 0x1F30, 0x1F34 }, +{ 0x0301, 0x1F31, 0x1F35 }, +{ 0x0301, 0x1F38, 0x1F3C }, +{ 0x0301, 0x1F39, 0x1F3D }, +{ 0x0301, 0x1F40, 0x1F44 }, +{ 0x0301, 0x1F41, 0x1F45 }, +{ 0x0301, 0x1F48, 0x1F4C }, +{ 0x0301, 0x1F49, 0x1F4D }, +{ 0x0301, 0x1F50, 0x1F54 }, +{ 0x0301, 0x1F51, 0x1F55 }, +{ 0x0301, 0x1F59, 0x1F5D }, +{ 0x0301, 0x1F60, 0x1F64 }, +{ 0x0301, 0x1F61, 0x1F65 }, +{ 0x0301, 0x1F68, 0x1F6C }, +{ 0x0301, 0x1F69, 0x1F6D }, +{ 0x0301, 0x03B1, 0x1F71 }, +{ 0x0301, 0x03B5, 0x1F73 }, +{ 0x0301, 0x03B7, 0x1F75 }, +{ 0x0301, 0x03B9, 0x1F77 }, +{ 0x0301, 0x03BF, 0x1F79 }, +{ 0x0301, 0x03C5, 0x1F7B }, +{ 0x0301, 0x03C9, 0x1F7D }, +{ 0x0301, 0x0391, 0x1FBB }, +{ 0x0301, 0x0395, 0x1FC9 }, +{ 0x0301, 0x0397, 0x1FCB }, +{ 0x0301, 0x1FBF, 0x1FCE }, +{ 0x0301, 0x03CA, 0x1FD3 }, +{ 0x0301, 0x0399, 0x1FDB }, +{ 0x0301, 0x1FFE, 0x1FDE }, +{ 0x0301, 0x03CB, 0x1FE3 }, +{ 0x0301, 0x03A5, 0x1FEB }, +{ 0x0301, 0x00A8, 0x1FEE }, +{ 0x0301, 0x039F, 0x1FF9 }, +{ 0x0301, 0x03A9, 0x1FFB }, + +/* 0327 Cedilla */ +{ 0x0327, 0x0327, 0x00B8 }, /* just for keyboard */ +{ 0x0327, 0x0020, 0x00B8 }, +{ 0x0327, 0x0043, 0x00C7 }, +{ 0x0327, 0x0063, 0x00E7 }, +{ 0x0327, 0x0047, 0x0122 }, +{ 0x0327, 0x0067, 0x0123 }, +{ 0x0327, 0x004B, 0x0136 }, +{ 0x0327, 0x006B, 0x0137 }, +{ 0x0327, 0x004C, 0x013B }, +{ 0x0327, 0x006C, 0x013C }, +{ 0x0327, 0x004E, 0x0145 }, +{ 0x0327, 0x006E, 0x0146 }, +{ 0x0327, 0x0052, 0x0156 }, +{ 0x0327, 0x0072, 0x0157 }, +{ 0x0327, 0x0053, 0x015E }, +{ 0x0327, 0x0073, 0x015F }, +{ 0x0327, 0x0054, 0x0162 }, +{ 0x0327, 0x0074, 0x0163 }, +{ 0x0327, 0x0044, 0x1E10 }, +{ 0x0327, 0x0064, 0x1E11 }, +{ 0x0327, 0x0114, 0x1E1C }, +{ 0x0327, 0x0115, 0x1E1D }, +{ 0x0327, 0x0048, 0x1E28 }, +{ 0x0327, 0x0068, 0x1E29 }, + +/* 0306 Breve */ +{ 0x0306, 0x0306, 0x02D8 }, /* just for keyboard */ +{ 0x0306, 0x0020, 0x02D8 }, +{ 0x0306, 0x0041, 0x0102 }, +{ 0x0306, 0x0061, 0x0103 }, +{ 0x0306, 0x0045, 0x0114 }, +{ 0x0306, 0x0065, 0x0115 }, +{ 0x0306, 0x0047, 0x011E }, +{ 0x0306, 0x0067, 0x011F }, +{ 0x0306, 0x0049, 0x012C }, +{ 0x0306, 0x0069, 0x012D }, +{ 0x0306, 0x004F, 0x014E }, +{ 0x0306, 0x006F, 0x014F }, +{ 0x0306, 0x0055, 0x016C }, +{ 0x0306, 0x0075, 0x016D }, +{ 0x0306, 0x0423, 0x040E }, +{ 0x0306, 0x0418, 0x0419 }, +{ 0x0306, 0x0438, 0x0439 }, +{ 0x0306, 0x0443, 0x045E }, +{ 0x0306, 0x0416, 0x04C1 }, +{ 0x0306, 0x0436, 0x04C2 }, +{ 0x0306, 0x0410, 0x04D0 }, +{ 0x0306, 0x0430, 0x04D1 }, +{ 0x0306, 0x0415, 0x04D6 }, +{ 0x0306, 0x0435, 0x04D7 }, +{ 0x0306, 0x03B1, 0x1FB0 }, +{ 0x0306, 0x0391, 0x1FB8 }, +{ 0x0306, 0x03B9, 0x1FD0 }, +{ 0x0306, 0x0399, 0x1FD8 }, +{ 0x0306, 0x03C5, 0x1FE0 }, +{ 0x0306, 0x03A5, 0x1FE8 }, + +/* 0307 Above Dot */ +{ 0x0307, 0x0307, 0x02D9 }, /* just for keyboard */ +{ 0x0307, 0x0020, 0x02D9 }, +{ 0x0307, 0x0043, 0x010A }, +{ 0x0307, 0x0063, 0x010B }, +{ 0x0307, 0x0045, 0x0116 }, +{ 0x0307, 0x0065, 0x0117 }, +{ 0x0307, 0x0047, 0x0120 }, +{ 0x0307, 0x0067, 0x0121 }, +{ 0x0307, 0x0049, 0x0130 }, +{ 0x0307, 0x005A, 0x017B }, +{ 0x0307, 0x007A, 0x017C }, +{ 0x0307, 0x0042, 0x1E02 }, +{ 0x0307, 0x0062, 0x1E03 }, +{ 0x0307, 0x0044, 0x1E0A }, +{ 0x0307, 0x0064, 0x1E0B }, +{ 0x0307, 0x0046, 0x1E1E }, +{ 0x0307, 0x0066, 0x1E1F }, +{ 0x0307, 0x0048, 0x1E22 }, +{ 0x0307, 0x0068, 0x1E23 }, +{ 0x0307, 0x004D, 0x1E40 }, +{ 0x0307, 0x006D, 0x1E41 }, +{ 0x0307, 0x004E, 0x1E44 }, +{ 0x0307, 0x006E, 0x1E45 }, +{ 0x0307, 0x0050, 0x1E56 }, +{ 0x0307, 0x0070, 0x1E57 }, +{ 0x0307, 0x0052, 0x1E58 }, +{ 0x0307, 0x0072, 0x1E59 }, +{ 0x0307, 0x0053, 0x1E60 }, +{ 0x0307, 0x0073, 0x1E61 }, +{ 0x0307, 0x015A, 0x1E64 }, +{ 0x0307, 0x015B, 0x1E65 }, +{ 0x0307, 0x0160, 0x1E66 }, +{ 0x0307, 0x0161, 0x1E67 }, +{ 0x0307, 0x1E62, 0x1E68 }, +{ 0x0307, 0x1E63, 0x1E69 }, +{ 0x0307, 0x0054, 0x1E6A }, +{ 0x0307, 0x0074, 0x1E6B }, +{ 0x0307, 0x0057, 0x1E86 }, +{ 0x0307, 0x0077, 0x1E87 }, +{ 0x0307, 0x0058, 0x1E8A }, +{ 0x0307, 0x0078, 0x1E8B }, +{ 0x0307, 0x0059, 0x1E8E }, +{ 0x0307, 0x0079, 0x1E8F }, +{ 0x0307, 0x017F, 0x1E9B }, + +/* 030A Above Ring */ +{ 0x030A, 0x030A, 0x02DA }, /* just for keyboard */ +{ 0x030A, 0x0020, 0x02DA }, +{ 0x030A, 0x0041, 0x00C5 }, +{ 0x030A, 0x0061, 0x00E5 }, +{ 0x030A, 0x0055, 0x016E }, +{ 0x030A, 0x0075, 0x016F }, +{ 0x030A, 0x0077, 0x1E98 }, +{ 0x030A, 0x0079, 0x1E99 }, + +/* 0328 OGONEK */ +{ 0x0328, 0x0328, 0x02DB }, /* just for keyboard */ +{ 0x0328, 0x0020, 0x02DB }, +{ 0x0328, 0x0041, 0x0104 }, +{ 0x0328, 0x0061, 0x0105 }, +{ 0x0328, 0x0045, 0x0118 }, +{ 0x0328, 0x0065, 0x0119 }, +{ 0x0328, 0x0049, 0x012E }, +{ 0x0328, 0x0069, 0x012F }, +{ 0x0328, 0x0055, 0x0172 }, +{ 0x0328, 0x0075, 0x0173 }, +{ 0x0328, 0x004F, 0x01EA }, +{ 0x0328, 0x006F, 0x01EB }, + + +/* 0303 Tilde */ +{ 0x0303, 0x0020, 0x02DC }, +{ 0x0303, 0x0041, 0x00C3 }, +{ 0x0303, 0x0061, 0x00E3 }, +{ 0x0303, 0x004E, 0x00D1 }, +{ 0x0303, 0x006E, 0x00F1 }, +{ 0x0303, 0x004F, 0x00D5 }, +{ 0x0303, 0x006F, 0x00F5 }, +{ 0x0303, 0x0049, 0x0128 }, +{ 0x0303, 0x0069, 0x0129 }, +{ 0x0303, 0x0055, 0x0168 }, +{ 0x0303, 0x0075, 0x0169 }, +{ 0x0303, 0x0056, 0x1E7C }, +{ 0x0303, 0x0076, 0x1E7D }, +{ 0x0303, 0x00C2, 0x1EAA }, +{ 0x0303, 0x00E2, 0x1EAB }, +{ 0x0303, 0x0102, 0x1EB4 }, +{ 0x0303, 0x0103, 0x1EB5 }, +{ 0x0303, 0x0045, 0x1EBC }, +{ 0x0303, 0x0065, 0x1EBD }, +{ 0x0303, 0x00CA, 0x1EC4 }, +{ 0x0303, 0x00EA, 0x1EC5 }, +{ 0x0303, 0x00D4, 0x1ED6 }, +{ 0x0303, 0x00F4, 0x1ED7 }, +{ 0x0303, 0x01A0, 0x1EE0 }, +{ 0x0303, 0x01A1, 0x1EE1 }, +{ 0x0303, 0x01AF, 0x1EEE }, +{ 0x0303, 0x01B0, 0x1EEF }, +{ 0x0303, 0x0059, 0x1EF8 }, +{ 0x0303, 0x0079, 0x1EF9 }, + + +/* 030C Caron */ +{ 0x030C, 0x030C, 0x02C7 }, /* Only for keyboard */ +{ 0x030C, 0x0020, 0x02C7 }, /* This isn't mentioned? */ +{ 0x030C, 0x0043, 0x010C }, +{ 0x030C, 0x0063, 0x010D }, +{ 0x030C, 0x0044, 0x010E }, +{ 0x030C, 0x0064, 0x010F }, +{ 0x030C, 0x0045, 0x011A }, +{ 0x030C, 0x0065, 0x011B }, +{ 0x030C, 0x004C, 0x013D }, +{ 0x030C, 0x006C, 0x013E }, +{ 0x030C, 0x004E, 0x0147 }, +{ 0x030C, 0x006E, 0x0148 }, +{ 0x030C, 0x0052, 0x0158 }, +{ 0x030C, 0x0072, 0x0159 }, +{ 0x030C, 0x0053, 0x0160 }, +{ 0x030C, 0x0073, 0x0161 }, +{ 0x030C, 0x0054, 0x0164 }, +{ 0x030C, 0x0074, 0x0165 }, +{ 0x030C, 0x005A, 0x017D }, +{ 0x030C, 0x007A, 0x017E }, +{ 0x030C, 0x0041, 0x01CD }, +{ 0x030C, 0x0061, 0x01CE }, +{ 0x030C, 0x0049, 0x01CF }, +{ 0x030C, 0x0069, 0x01D0 }, +{ 0x030C, 0x004F, 0x01D1 }, +{ 0x030C, 0x006F, 0x01D2 }, +{ 0x030C, 0x0055, 0x01D3 }, +{ 0x030C, 0x0075, 0x01D4 }, +{ 0x030C, 0x00DC, 0x01D9 }, +{ 0x030C, 0x00FC, 0x01DA }, +{ 0x030C, 0x0047, 0x01E6 }, +{ 0x030C, 0x0067, 0x01E7 }, +{ 0x030C, 0x004B, 0x01E8 }, +{ 0x030C, 0x006B, 0x01E9 }, +{ 0x030C, 0x01B7, 0x01EE }, +{ 0x030C, 0x0292, 0x01EF }, +{ 0x030C, 0x006A, 0x01F0 }, + + +/* 030B Double Acute */ +{ 0x030B, 0x030B, 0x02DD }, /* only for keyboard */ +{ 0x030B, 0x0020, 0x02DD }, +{ 0x030B, 0x004F, 0x0150 }, +{ 0x030B, 0x006F, 0x0151 }, +{ 0x030B, 0x0055, 0x0170 }, +{ 0x030B, 0x0075, 0x0171 }, +{ 0x030B, 0x0423, 0x04F2 }, +{ 0x030B, 0x0443, 0x04F3 }, + + +/* 030D Vertical Line Above */ +{ 0x030D, 0x030D, 0x0384 }, /* only for keyboard */ +{ 0x030D, 0x0020, 0x0384 }, +{ 0x030D, 0x00A8, 0x0385 }, +{ 0x030D, 0x0391, 0x0386 }, +{ 0x030D, 0x0395, 0x0388 }, +{ 0x030D, 0x0397, 0x0389 }, +{ 0x030D, 0x0399, 0x038A }, +{ 0x030D, 0x039F, 0x038C }, +{ 0x030D, 0x03A5, 0x038E }, +{ 0x030D, 0x03A9, 0x038F }, +{ 0x030D, 0x03B1, 0x03AC }, +{ 0x030D, 0x03B5, 0x03AD }, +{ 0x030D, 0x03B7, 0x03AE }, +{ 0x030D, 0x03B9, 0x03AF }, +{ 0x030D, 0x03BF, 0x03CC }, +{ 0x030D, 0x03C5, 0x03CD }, +{ 0x030D, 0x03C9, 0x03CE }, +{ 0x030D, 0x03D2, 0x03D3 }, + + +/* 0345 Greek YPOGEGRAMMENI */ +{ 0x0345, 0x0345, 0x037A }, /* only for keyboard */ +{ 0x0345, 0x0020, 0x037A }, +{ 0x0345, 0x1F00, 0x1F80 }, +{ 0x0345, 0x1F01, 0x1F81 }, +{ 0x0345, 0x1F02, 0x1F82 }, +{ 0x0345, 0x1F03, 0x1F83 }, +{ 0x0345, 0x1F04, 0x1F84 }, +{ 0x0345, 0x1F05, 0x1F85 }, +{ 0x0345, 0x1F06, 0x1F86 }, +{ 0x0345, 0x1F07, 0x1F87 }, +{ 0x0345, 0x1F08, 0x1F88 }, +{ 0x0345, 0x1F09, 0x1F89 }, +{ 0x0345, 0x1F0A, 0x1F8A }, +{ 0x0345, 0x1F0B, 0x1F8B }, +{ 0x0345, 0x1F0C, 0x1F8C }, +{ 0x0345, 0x1F0D, 0x1F8D }, +{ 0x0345, 0x1F0E, 0x1F8E }, +{ 0x0345, 0x1F0F, 0x1F8F }, +{ 0x0345, 0x1F20, 0x1F90 }, +{ 0x0345, 0x1F21, 0x1F91 }, +{ 0x0345, 0x1F22, 0x1F92 }, +{ 0x0345, 0x1F23, 0x1F93 }, +{ 0x0345, 0x1F24, 0x1F94 }, +{ 0x0345, 0x1F25, 0x1F95 }, +{ 0x0345, 0x1F26, 0x1F96 }, +{ 0x0345, 0x1F27, 0x1F97 }, +{ 0x0345, 0x1F28, 0x1F98 }, +{ 0x0345, 0x1F29, 0x1F99 }, +{ 0x0345, 0x1F2A, 0x1F9A }, +{ 0x0345, 0x1F2B, 0x1F9B }, +{ 0x0345, 0x1F2C, 0x1F9C }, +{ 0x0345, 0x1F2D, 0x1F9D }, +{ 0x0345, 0x1F2E, 0x1F9E }, +{ 0x0345, 0x1F2F, 0x1F9F }, +{ 0x0345, 0x1F60, 0x1FA0 }, +{ 0x0345, 0x1F61, 0x1FA1 }, +{ 0x0345, 0x1F62, 0x1FA2 }, +{ 0x0345, 0x1F63, 0x1FA3 }, +{ 0x0345, 0x1F64, 0x1FA4 }, +{ 0x0345, 0x1F65, 0x1FA5 }, +{ 0x0345, 0x1F66, 0x1FA6 }, +{ 0x0345, 0x1F67, 0x1FA7 }, +{ 0x0345, 0x1F68, 0x1FA8 }, +{ 0x0345, 0x1F69, 0x1FA9 }, +{ 0x0345, 0x1F6A, 0x1FAA }, +{ 0x0345, 0x1F6B, 0x1FAB }, +{ 0x0345, 0x1F6C, 0x1FAC }, +{ 0x0345, 0x1F6D, 0x1FAD }, +{ 0x0345, 0x1F6E, 0x1FAE }, +{ 0x0345, 0x1F6F, 0x1FAF }, +{ 0x0345, 0x1F70, 0x1FB2 }, +{ 0x0345, 0x03B1, 0x1FB3 }, +{ 0x0345, 0x1F71, 0x1FB4 }, +{ 0x0345, 0x1FB6, 0x1FB7 }, +{ 0x0345, 0x0391, 0x1FBC }, +{ 0x0345, 0x1F74, 0x1FC2 }, +{ 0x0345, 0x03B7, 0x1FC3 }, +{ 0x0345, 0x1F75, 0x1FC4 }, +{ 0x0345, 0x1FC6, 0x1FC7 }, +{ 0x0345, 0x0397, 0x1FCC }, +{ 0x0345, 0x1F7C, 0x1FF2 }, +{ 0x0345, 0x03C9, 0x1FF3 }, +{ 0x0345, 0x1F79, 0x1FF4 }, +{ 0x0345, 0x1FF6, 0x1FF7 }, +{ 0x0345, 0x03A9, 0x1FFC }, + +/* 0313 Comma Above/PSILI */ +{ 0x0313, 0x0313, 0x1FBF }, /* only for keyboard */ +{ 0x0313, 0x0020, 0x1FBF }, +{ 0x0313, 0x03B1, 0x1F00 }, +{ 0x0313, 0x0391, 0x1F08 }, +{ 0x0313, 0x03B5, 0x1F10 }, +{ 0x0313, 0x0395, 0x1F18 }, +{ 0x0313, 0x03B7, 0x1F20 }, +{ 0x0313, 0x0397, 0x1F28 }, +{ 0x0313, 0x03B9, 0x1F30 }, +{ 0x0313, 0x0399, 0x1F38 }, +{ 0x0313, 0x03BF, 0x1F40 }, +{ 0x0313, 0x039F, 0x1F48 }, +{ 0x0313, 0x03C5, 0x1F50 }, +{ 0x0313, 0x03C9, 0x1F60 }, +{ 0x0313, 0x03A9, 0x1F68 }, +{ 0x0313, 0x03C1, 0x1FE4 }, + +/* 0342 Greek PERISPOMENI */ +{ 0x0342, 0x0342, 0x1FC0 }, /* only for keyboard */ +{ 0x0342, 0x0020, 0x1FC0 }, +{ 0x0342, 0x1F00, 0x1F06 }, +{ 0x0342, 0x1F01, 0x1F07 }, +{ 0x0342, 0x1F08, 0x1F0E }, +{ 0x0342, 0x1F09, 0x1F0F }, +{ 0x0342, 0x1F20, 0x1F26 }, +{ 0x0342, 0x1F21, 0x1F27 }, +{ 0x0342, 0x1F28, 0x1F2E }, +{ 0x0342, 0x1F29, 0x1F2F }, +{ 0x0342, 0x1F30, 0x1F36 }, +{ 0x0342, 0x1F31, 0x1F37 }, +{ 0x0342, 0x1F38, 0x1F3E }, +{ 0x0342, 0x1F39, 0x1F3F }, +{ 0x0342, 0x1F50, 0x1F56 }, +{ 0x0342, 0x1F51, 0x1F57 }, +{ 0x0342, 0x1F59, 0x1F5F }, +{ 0x0342, 0x1F60, 0x1F66 }, +{ 0x0342, 0x1F61, 0x1F67 }, +{ 0x0342, 0x1F68, 0x1F6E }, +{ 0x0342, 0x1F69, 0x1F6F }, +{ 0x0342, 0x03B1, 0x1FB6 }, +{ 0x0342, 0x00A8, 0x1FC1 }, +{ 0x0342, 0x03B7, 0x1FC6 }, +{ 0x0342, 0x1FBF, 0x1FCF }, +{ 0x0342, 0x03B9, 0x1FD6 }, +{ 0x0342, 0x03CA, 0x1FD7 }, +{ 0x0342, 0x1FFE, 0x1FDF }, +{ 0x0342, 0x03C9, 0x1FF6 }, + + +/* 0314 Reversed Comma Above /Greek DASIA */ +{ 0x0314, 0x0314, 0x1FFE }, /* only for keyboard */ +{ 0x0314, 0x0020, 0x1FFE }, +{ 0x0314, 0x03B1, 0x1F01 }, +{ 0x0314, 0x0391, 0x1F09 }, +{ 0x0314, 0x03B5, 0x1F11 }, +{ 0x0314, 0x0395, 0x1F19 }, +{ 0x0314, 0x03B7, 0x1F21 }, +{ 0x0314, 0x0397, 0x1F29 }, +{ 0x0314, 0x03B9, 0x1F31 }, +{ 0x0314, 0x0399, 0x1F39 }, +{ 0x0314, 0x03BF, 0x1F41 }, +{ 0x0314, 0x039F, 0x1F49 }, +{ 0x0314, 0x03C5, 0x1F51 }, +{ 0x0314, 0x03A5, 0x1F59 }, +{ 0x0314, 0x03C9, 0x1F61 }, +{ 0x0314, 0x03A9, 0x1F69 }, +{ 0x0314, 0x03C1, 0x1FE5 }, +{ 0x0314, 0x03A1, 0x1FEC }, + +/* 3099 KATAKANA-HIRAGANA VOICED SOUND MARK */ +{ 0x3099, 0x3099, 0x309B }, /* only for keyboard */ +{ 0x3099, 0x0020, 0x309B }, +{ 0x3099, 0x304B, 0x304C }, +{ 0x3099, 0x304D, 0x304E }, +{ 0x3099, 0x304F, 0x3050 }, +{ 0x3099, 0x3051, 0x3052 }, +{ 0x3099, 0x3053, 0x3054 }, +{ 0x3099, 0x3055, 0x3056 }, +{ 0x3099, 0x3057, 0x3058 }, +{ 0x3099, 0x3059, 0x305A }, +{ 0x3099, 0x305B, 0x305C }, +{ 0x3099, 0x305D, 0x305E }, +{ 0x3099, 0x305F, 0x3060 }, +{ 0x3099, 0x3061, 0x3062 }, +{ 0x3099, 0x3064, 0x3065 }, +{ 0x3099, 0x3066, 0x3067 }, +{ 0x3099, 0x3068, 0x3069 }, +{ 0x3099, 0x306F, 0x3070 }, +{ 0x3099, 0x3072, 0x3073 }, +{ 0x3099, 0x3075, 0x3076 }, +{ 0x3099, 0x3078, 0x3079 }, +{ 0x3099, 0x307B, 0x307C }, +{ 0x3099, 0x3046, 0x3094 }, +{ 0x3099, 0x309D, 0x309E }, +{ 0x3099, 0x30AB, 0x30AC }, +{ 0x3099, 0x30AD, 0x30AE }, +{ 0x3099, 0x30AF, 0x30B0 }, +{ 0x3099, 0x30B1, 0x30B2 }, +{ 0x3099, 0x30B3, 0x30B4 }, +{ 0x3099, 0x30B5, 0x30B6 }, +{ 0x3099, 0x30B7, 0x30B8 }, +{ 0x3099, 0x30B9, 0x30BA }, +{ 0x3099, 0x30BB, 0x30BC }, +{ 0x3099, 0x30BD, 0x30BE }, +{ 0x3099, 0x30BF, 0x30C0 }, +{ 0x3099, 0x30C1, 0x30C2 }, +{ 0x3099, 0x30C4, 0x30C5 }, +{ 0x3099, 0x30C6, 0x30C7 }, +{ 0x3099, 0x30C8, 0x30C9 }, +{ 0x3099, 0x30CF, 0x30D0 }, +{ 0x3099, 0x30D2, 0x30D3 }, +{ 0x3099, 0x30D5, 0x30D6 }, +{ 0x3099, 0x30D8, 0x30D9 }, +{ 0x3099, 0x30DB, 0x30DC }, +{ 0x3099, 0x30A6, 0x30F4 }, +{ 0x3099, 0x30EF, 0x30F7 }, +{ 0x3099, 0x30F0, 0x30F8 }, +{ 0x3099, 0x30F1, 0x30F9 }, +{ 0x3099, 0x30F2, 0x30FA }, +{ 0x3099, 0x30FD, 0x30FE }, + +/* 309A KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ +{ 0x309A, 0x309A, 0x309C }, /* only for keyboard */ +{ 0x309A, 0x0020, 0x309C }, +{ 0x309A, 0x306F, 0x3071 }, +{ 0x309A, 0x3072, 0x3074 }, +{ 0x309A, 0x3075, 0x3077 }, +{ 0x309A, 0x3078, 0x307A }, +{ 0x309A, 0x307B, 0x307D }, +{ 0x309A, 0x30CF, 0x30D1 }, +{ 0x309A, 0x30D2, 0x30D4 }, +{ 0x309A, 0x30D5, 0x30D7 }, +{ 0x309A, 0x30D8, 0x30DA }, +{ 0x309A, 0x30DB, 0x30DD }, + +/* I'm not at all sure about these... */ +/* 0305 Overline */ +{ 0x0305, 0x0020, 0x203E }, + +/* 0343 Greek KORONIS */ +{ 0x0343, 0x0020, 0x1FBD }, + +/* 0332 Low Line/underscore */ +{ 0x0332, 0x0020, 0x005F }, + +/* 0333 Double LOW LINE */ +{ 0x0333, 0x0020, 0x2017 }, + +/* 031B HORN */ +/* standalone ??? */ +{ 0x031B, 0x004F, 0x01A0 }, +{ 0x031B, 0x006F, 0x01A1 }, +{ 0x031B, 0x0055, 0x01AF }, +{ 0x031B, 0x0075, 0x01B0 }, + + +/* 030F Double Grave Accent */ +/* standalone ??? */ +{ 0x030F, 0x0041, 0x0200 }, +{ 0x030F, 0x0061, 0x0201 }, +{ 0x030F, 0x0045, 0x0204 }, +{ 0x030F, 0x0065, 0x0205 }, +{ 0x030F, 0x0049, 0x0208 }, +{ 0x030F, 0x0069, 0x0209 }, +{ 0x030F, 0x004F, 0x020C }, +{ 0x030F, 0x006F, 0x020D }, +{ 0x030F, 0x0052, 0x0210 }, +{ 0x030F, 0x0072, 0x0211 }, +{ 0x030F, 0x0055, 0x0214 }, +{ 0x030F, 0x0075, 0x0215 }, +{ 0x030F, 0x0474, 0x0476 }, +{ 0x030F, 0x0475, 0x0477 }, + +/* 0311 Inverted BREVE */ +/* standalone ??? */ +{ 0x0311, 0x0041, 0x0202 }, +{ 0x0311, 0x0061, 0x0203 }, +{ 0x0311, 0x0045, 0x0206 }, +{ 0x0311, 0x0065, 0x0207 }, +{ 0x0311, 0x0049, 0x020A }, +{ 0x0311, 0x0069, 0x020B }, +{ 0x0311, 0x004F, 0x020E }, +{ 0x0311, 0x006F, 0x020F }, +{ 0x0311, 0x0052, 0x0212 }, +{ 0x0311, 0x0072, 0x0213 }, +{ 0x0311, 0x0055, 0x0216 }, +{ 0x0311, 0x0075, 0x0217 }, + +/* 0344 Greek DIALYTIKA TONOS */ +/* 385 standalone ??? */ +{ 0x0344, 0x03B9, 0x0390 }, +{ 0x0344, 0x03C5, 0x03B0 }, + +/* 093C DEVANAGARI SIGN NUKTA */ +/* standalone ??? */ +{ 0x093C, 0x0928, 0x0929 }, +{ 0x093C, 0x0930, 0x0931 }, +{ 0x093C, 0x0933, 0x0934 }, +{ 0x093C, 0x0915, 0x0958 }, +{ 0x093C, 0x0916, 0x0959 }, +{ 0x093C, 0x0917, 0x095A }, +{ 0x093C, 0x091C, 0x095B }, +{ 0x093C, 0x0921, 0x095C }, +{ 0x093C, 0x0922, 0x095D }, +{ 0x093C, 0x092B, 0x095E }, +{ 0x093C, 0x092F, 0x095F }, + +/* 09BC BENGALI SIGN NUKTA */ +/* standalone ??? */ +{ 0x09BC, 0x09AC, 0x09B0 }, +{ 0x09BC, 0x09A1, 0x09DC }, +{ 0x09BC, 0x09A2, 0x09DD }, +{ 0x09BC, 0x09AF, 0x09DF }, + +/* 09BE BENGALI VOWEL SIGN */ +/* standalone ??? */ +{ 0x09BE, 0x09C7, 0x09CB }, + +/* 09B7 BENGALI AU LENGTH MARK */ +/* standalone ??? */ +{ 0x09D7, 0x09C7, 0x09CC }, + +/* 0A3C GURMUKHI SIGN NUKTA */ +/* standalone ??? */ +{ 0x0A3C, 0x0A16, 0x0A59 }, +{ 0x0A3C, 0x0A17, 0x0A5A }, +{ 0x0A3C, 0x0A1C, 0x0A5B }, +{ 0x0A3C, 0x0A21, 0x0A5C }, +{ 0x0A3C, 0x0A2B, 0x0A5E }, + +/* 0B56 ORIYA AI LENGTH MARK */ +/* standalone ??? */ +{ 0x0B56, 0x0B47, 0x0B48 }, + +/* 0B3E ORIYA VOWEL SIGN */ +/* standalone ??? */ +{ 0x0B3E, 0x0B47, 0x0B4B }, + +/* 0325 RING BELOW */ +/* standalone ??? */ +{ 0x0325, 0x0041, 0x1E00 }, +{ 0x0325, 0x0061, 0x1E01 }, + +/* 0323 DOT BELOW */ +/* standalone ??? */ +{ 0x0323, 0x0042, 0x1E04 }, +{ 0x0323, 0x0062, 0x1E05 }, +{ 0x0323, 0x0044, 0x1E0C }, +{ 0x0323, 0x0064, 0x1E0D }, +{ 0x0323, 0x0048, 0x1E24 }, +{ 0x0323, 0x0068, 0x1E25 }, +{ 0x0323, 0x004B, 0x1E32 }, +{ 0x0323, 0x006B, 0x1E33 }, +{ 0x0323, 0x004C, 0x1E36 }, +{ 0x0323, 0x006C, 0x1E37 }, +{ 0x0323, 0x004D, 0x1E42 }, +{ 0x0323, 0x006D, 0x1E43 }, +{ 0x0323, 0x004E, 0x1E46 }, +{ 0x0323, 0x006E, 0x1E47 }, +{ 0x0323, 0x0052, 0x1E5A }, +{ 0x0323, 0x0072, 0x1E5B }, +{ 0x0323, 0x0053, 0x1E62 }, +{ 0x0323, 0x0073, 0x1E63 }, +{ 0x0323, 0x0054, 0x1E6C }, +{ 0x0323, 0x0074, 0x1E6D }, +{ 0x0323, 0x0056, 0x1E7E }, +{ 0x0323, 0x0076, 0x1E7F }, +{ 0x0323, 0x0057, 0x1E88 }, +{ 0x0323, 0x0077, 0x1E89 }, +{ 0x0323, 0x005A, 0x1E92 }, +{ 0x0323, 0x007A, 0x1E93 }, +{ 0x0323, 0x0041, 0x1EA0 }, +{ 0x0323, 0x0061, 0x1EA1 }, +{ 0x0323, 0x00C2, 0x1EAC }, +{ 0x0323, 0x00E2, 0x1EAD }, +{ 0x0323, 0x0102, 0x1EB6 }, +{ 0x0323, 0x0103, 0x1EB7 }, +{ 0x0323, 0x0045, 0x1EB8 }, +{ 0x0323, 0x0065, 0x1EB9 }, +{ 0x0323, 0x00CA, 0x1EC6 }, +{ 0x0323, 0x00EA, 0x1EC7 }, +{ 0x0323, 0x0049, 0x1ECA }, +{ 0x0323, 0x0069, 0x1ECB }, +{ 0x0323, 0x004F, 0x1ECC }, +{ 0x0323, 0x006F, 0x1ECD }, +{ 0x0323, 0x00D4, 0x1ED8 }, +{ 0x0323, 0x00F4, 0x1ED9 }, +{ 0x0323, 0x01A0, 0x1EE2 }, +{ 0x0323, 0x01A1, 0x1EE3 }, +{ 0x0323, 0x0055, 0x1EE4 }, +{ 0x0323, 0x0075, 0x1EE5 }, +{ 0x0323, 0x0059, 0x1EF4 }, +{ 0x0323, 0x0079, 0x1EF5 }, + +/* 0331 MACRON BELOW */ +/* standalone ??? */ +{ 0x0331, 0x0042, 0x1E06 }, +{ 0x0331, 0x0062, 0x1E07 }, +{ 0x0331, 0x0044, 0x1E0E }, +{ 0x0331, 0x0064, 0x1E0F }, +{ 0x0331, 0x004B, 0x1E34 }, +{ 0x0331, 0x006B, 0x1E35 }, +{ 0x0331, 0x004C, 0x1E3A }, +{ 0x0331, 0x006C, 0x1E3B }, +{ 0x0331, 0x004E, 0x1E48 }, +{ 0x0331, 0x006E, 0x1E49 }, +{ 0x0331, 0x0052, 0x1E5E }, +{ 0x0331, 0x0072, 0x1E5F }, +{ 0x0331, 0x0054, 0x1E6E }, +{ 0x0331, 0x0074, 0x1E6F }, +{ 0x0331, 0x005A, 0x1E94 }, +{ 0x0331, 0x007A, 0x1E95 }, +{ 0x0331, 0x0068, 0x1E96 }, + +/* 032D CICUMFLEX ACCENT BELOW */ +/* standalone ??? */ +{ 0x032D, 0x0044, 0x1E12 }, +{ 0x032D, 0x0064, 0x1E13 }, +{ 0x032D, 0x0045, 0x1E18 }, +{ 0x032D, 0x0065, 0x1E19 }, +{ 0x032D, 0x004C, 0x1E3C }, +{ 0x032D, 0x006C, 0x1E3D }, +{ 0x032D, 0x004E, 0x1E4A }, +{ 0x032D, 0x006E, 0x1E4B }, +{ 0x032D, 0x0054, 0x1E70 }, +{ 0x032D, 0x0074, 0x1E71 }, +{ 0x032D, 0x0055, 0x1E76 }, +{ 0x032D, 0x0075, 0x1E77 }, + +/* 0330 TILDE BELOW */ +/* standalone ??? */ +{ 0x0330, 0x0045, 0x1E1A }, +{ 0x0330, 0x0065, 0x1E1B }, +{ 0x0330, 0x0049, 0x1E2C }, +{ 0x0330, 0x0069, 0x1E2D }, +{ 0x0330, 0x0055, 0x1E74 }, +{ 0x0330, 0x0075, 0x1E75 }, + +/* 032E BREVE BELOW */ +/* standalone ??? */ +{ 0x032E, 0x0048, 0x1E2A }, +{ 0x032E, 0x0068, 0x1E2B }, + +/* 0324 DIAERESIS BELOW */ +/* standalone ??? */ +{ 0x0324, 0x0055, 0x1E72 }, +{ 0x0324, 0x0075, 0x1E73 }, + +/* 0309 HOOK ABOVE */ +/* standalone ??? */ +{ 0x0309, 0x0041, 0x1EA2 }, +{ 0x0309, 0x0061, 0x1EA3 }, +{ 0x0309, 0x00C2, 0x1EA8 }, +{ 0x0309, 0x00E2, 0x1EA9 }, +{ 0x0309, 0x0102, 0x1EB2 }, +{ 0x0309, 0x0103, 0x1EB3 }, +{ 0x0309, 0x0045, 0x1EBA }, +{ 0x0309, 0x0065, 0x1EBB }, +{ 0x0309, 0x00CA, 0x1EC2 }, +{ 0x0309, 0x00EA, 0x1EC3 }, +{ 0x0309, 0x0049, 0x1EC8 }, +{ 0x0309, 0x0069, 0x1EC9 }, +{ 0x0309, 0x004F, 0x1ECE }, +{ 0x0309, 0x006F, 0x1ECF }, +{ 0x0309, 0x00D4, 0x1ED4 }, +{ 0x0309, 0x00F4, 0x1ED5 }, +{ 0x0309, 0x01A0, 0x1EDE }, +{ 0x0309, 0x01A1, 0x1EDF }, +{ 0x0309, 0x0055, 0x1EE6 }, +{ 0x0309, 0x0075, 0x1EE7 }, +{ 0x0309, 0x01AF, 0x1EEC }, +{ 0x0309, 0x01B0, 0x1EED }, +{ 0x0309, 0x0059, 0x1EF6 }, +{ 0x0309, 0x0079, 0x1EF7 }, + +/* 0338 LONG SOLIDUS OVERLAY */ +/* standalone ??? */ +{ 0x0338, 0x2203, 0x2204 }, +{ 0x0338, 0x2208, 0x2209 }, +{ 0x0338, 0x220B, 0x220C }, +{ 0x0338, 0x2223, 0x2224 }, +{ 0x0338, 0x2225, 0x2226 }, +{ 0x0338, 0x007E, 0x2241 }, +{ 0x0338, 0x2243, 0x2244 }, +{ 0x0338, 0x2245, 0x2247 }, +{ 0x0338, 0x2248, 0x2249 }, +{ 0x0338, 0x003D, 0x2260 }, +{ 0x0338, 0x2261, 0x2262 }, +{ 0x0338, 0x224D, 0x226D }, +{ 0x0338, 0x003C, 0x226E }, +{ 0x0338, 0x003E, 0x226F }, +{ 0x0338, 0x2264, 0x2270 }, +{ 0x0338, 0x2265, 0x2271 }, +{ 0x0338, 0x2272, 0x2274 }, +{ 0x0338, 0x2273, 0x2275 }, +{ 0x0338, 0x2276, 0x2278 }, +{ 0x0338, 0x2277, 0x2279 }, +{ 0x0338, 0x227A, 0x2280 }, +{ 0x0338, 0x227B, 0x2281 }, +{ 0x0338, 0x2282, 0x2284 }, +{ 0x0338, 0x2283, 0x2285 }, +{ 0x0338, 0x2286, 0x2288 }, +{ 0x0338, 0x2287, 0x2289 }, +{ 0x0338, 0x22A2, 0x22AC }, +{ 0x0338, 0x22A8, 0x22AD }, +{ 0x0338, 0x22A9, 0x22AE }, +{ 0x0338, 0x22AB, 0x22AF }, +{ 0x0338, 0x227C, 0x22E0 }, +{ 0x0338, 0x227D, 0x22E1 }, +{ 0x0338, 0x2291, 0x22E2 }, +{ 0x0338, 0x2292, 0x22E3 }, +{ 0x0338, 0x22B2, 0x22EA }, +{ 0x0338, 0x22B3, 0x22EB }, +{ 0x0338, 0x22B4, 0x22EC }, +{ 0x0338, 0x22B5, 0x22ED }, + +/* 0B57 ORIYA AU LENGTH */ +/* standalone ??? */ +{ 0x0B57, 0x0B47, 0x0B4C }, + +/* 0B3C ORIYA SIGN NUKTA */ +/* standalone ??? */ +{ 0x0B3C, 0x0B21, 0x0B5C }, +{ 0x0B3C, 0x0B22, 0x0B5D }, +{ 0x0B3C, 0x0B2F, 0x0B5F }, + +/* 0BD7 TAMIL AU LENGTH */ +/* standalone ??? */ +{ 0x0BD7, 0x0B92, 0x0B94 }, +{ 0x0BD7, 0x0BC6, 0x0BCC }, + +/* 0BBE TAMIL VOWEL SIGN */ +/* standalone ??? */ +{ 0x0BBE, 0x0BC6, 0x0BCA }, +{ 0x0BBE, 0x0BC7, 0x0BCB }, + +/* 0C56 TELUGU AI LENGTH MARK */ +/* standalone ??? */ +{ 0x0C56, 0x0C46, 0x0C48 }, + +/* 0CD5 KANNADA LENGTH MARK */ +/* I can't handle this case... + * 0CCB;0CC6 0CC2 0CD5 + */ +/* standalone ??? */ +{ 0x0CD5, 0x0CBF, 0x0CC0 }, +{ 0x0CD5, 0x0CC6, 0x0CC7 }, + +/* 0CD6 KANNADA AI LENGTH MARK */ +/* standalone ??? */ +{ 0x0CD6, 0x0CC6, 0x0CC8 }, + +/* 0CC2 KANNADA VOWL SIGN UU */ +/* standalone ??? */ +{ 0x0CC2, 0x0CC6, 0x0CCA }, + +/* 0D3E MALAYALAM VOWEL SIGN AA */ +/* standalone ??? */ +{ 0x0D3E, 0x0D46, 0x0D4A }, +{ 0x0D3E, 0x0D47, 0x0D4B }, + +/* 0D57 MALAYALAM AU LENGTH MARK */ +/* standalone ?? */ +{ 0x0D57, 0x0D46, 0x0D4C }, + +/* 0E4D THAI CHARACTER NIKHAHIT */ +/* standalone ?? */ +{ 0x0E32, 0x0E4D, 0x0E33 }, + +/* 0ECD LAO NIGGAHITA */ +/* standalone ?? */ +{ 0x0EB2, 0x0ECD, 0x0EB3 }, + +/* 0FB7 TIBETAN SUBJOINED LETTER HA */ +/* standalone ?? */ +{ 0x0FB7, 0x0F42, 0x0F43 }, +{ 0x0FB7, 0x0F4C, 0x0F4D }, +{ 0x0FB7, 0x0F51, 0x0F52 }, +{ 0x0FB7, 0x0F56, 0x0F57 }, +{ 0x0FB7, 0x0F5B, 0x0F5C }, +{ 0x0FB7, 0x0F92, 0x0F93 }, +{ 0x0FB7, 0x0F9C, 0x0F9D }, +{ 0x0FB7, 0x0FA1, 0x0FA2 }, +{ 0x0FB7, 0x0FA6, 0x0FA7 }, +{ 0x0FB7, 0x0FAB, 0x0FAC }, + +/* 0F71 TIBETAN VOWEL SIGN AA */ +/* standalone ?? */ +{ 0x0F71, 0x0F72, 0x0F73 }, +{ 0x0F71, 0x0F74, 0x0F75 }, +{ 0x0F71, 0x0F76, 0x0F77 }, +{ 0x0F71, 0x0F78, 0x0F79 }, +{ 0x0F71, 0x0F80, 0x0F81 }, + +/* TIBETAN VOWEL SIGN REVERSED I */ +/* standalone */ +{ 0x0F80, 0x0FB2, 0x0F76 }, +{ 0x0F80, 0x0FB3, 0x0F78 }, + + +/* 05B7 HEBREW POINT PATAH */ +/* standalone ?? */ +{ 0x05B7, 0x05F2, 0xFB1F }, +{ 0x05B7, 0x05D0, 0xFB2E }, + +/* 05BC HEBREW POINT DAGESH OR MAPIQ */ +/* standalone ?? */ +{ 0x05BC, 0x05D0, 0xFB30 }, +{ 0x05BC, 0x05D1, 0xFB31 }, +{ 0x05BC, 0x05D2, 0xFB32 }, +{ 0x05BC, 0x05D3, 0xFB33 }, +{ 0x05BC, 0x05D4, 0xFB34 }, +{ 0x05BC, 0x05D5, 0xFB35 }, +{ 0x05BC, 0x05D6, 0xFB36 }, +{ 0x05BC, 0x05D8, 0xFB38 }, +{ 0x05BC, 0x05D9, 0xFB39 }, +{ 0x05BC, 0x05DA, 0xFB3A }, +{ 0x05BC, 0x05DB, 0xFB3B }, +{ 0x05BC, 0x05DC, 0xFB3C }, +{ 0x05BC, 0x05DE, 0xFB3E }, +{ 0x05BC, 0x05E0, 0xFB40 }, +{ 0x05BC, 0x05E1, 0xFB41 }, +{ 0x05BC, 0x05E3, 0xFB43 }, +{ 0x05BC, 0x05E4, 0xFB44 }, +{ 0x05BC, 0x05E6, 0xFB46 }, +{ 0x05BC, 0x05E7, 0xFB47 }, +{ 0x05BC, 0x05E8, 0xFB48 }, +{ 0x05BC, 0x05E9, 0xFB49 }, +{ 0x05BC, 0x05EA, 0xFB4A }, + +/* 05BF HEBREW POINT RAFE */ +/* standalone ?? */ +{ 0x05BF, 0x05D1, 0xFB4C }, +{ 0x05BF, 0x05DB, 0xFB4D }, +{ 0x05BF, 0x05E4, 0xFB4E }, + +/* 0FB5 TIBETAN SUBJOINED LETTER SSA */ +/* standalone ?? */ +{ 0x0FB5, 0x0F40, 0x0F69 }, +{ 0x0FB5, 0x0F90, 0x0FB9 }, + +/* 05C1 HEBREW POINT SHIN DOT */ +/* I can't handle this... + * FB2C;05E9 05BC 05C1 + */ +/* standalone ?? */ +{ 0x05C1, 0x05E9, 0xFB2A }, + + +/* 05C2 HEBREW POINT SIN DOT */ +/* I can't handle this... + * FB2D;05E9 05BC 05C2 + */ +/* standalone ?? */ +{ 0x05C2, 0x05E9, 0xFB2B }, + +/* 05B9 HEBREW POINT HOLAM */ +/* standalone ?? */ +{ 0x05B9, 0x05D5, 0xFB4B }, + +/* 05B8 HEBREW POINT QAMATAS */ +/* standalone ?? */ +{ 0x05B8, 0x05D0, 0xFB2F }, + +}; +static const int keysym_dead_map_count = sizeof(keysym_dead_map)/sizeof(keysym_dead_map[0]); + +t_keysym keysym_dead_key_translation(t_keysym dead_key, t_keysym in) +{ + int i; + for(i = 0; i < keysym_dead_map_count; i++) { + if ((keysym_dead_map[i].dead_key == dead_key) && + (keysym_dead_map[i].in == in)) { + return keysym_dead_map[i].out; + } + } + return in; +} + +void traverse_dead_key_list(void *p, dead_keys_callback_t callback) +{ + int i; + for(i = 0; i < keysym_dead_map_count; i++) { + callback(p, keysym_dead_map[i].dead_key, + keysym_dead_map[i].in, keysym_dead_map[i].out); + } +} diff --git a/src/base/lib/translate/make_attributes.c b/src/base/lib/translate/make_attributes.c new file mode 100644 index 0000000..7ab1e32 --- /dev/null +++ b/src/base/lib/translate/make_attributes.c @@ -0,0 +1,367 @@ +#include +#include +#include + +#include "translate/unicode_symbols.h" +#include "translate/keysym_attributes.h" + +#undef NUM_KEYSYMS +#define NUM_KEYSYMS 0x10000 + +struct print_state +{ + FILE *out; + int line; + int chars; +}; +static void start_line(struct print_state *state, int max) +{ + if (state->chars > max) { + fprintf(state->out, "\n"); + state->line++; + state->chars = 0; + } +} +static void print_header(struct print_state *state) +{ + start_line(state, 0); + fprintf(state->out, +"#include \"translate/keysym_attributes.h\"\n" +"\n" +"/* automatically generated do not touch */\n " +"unsigned char keysym_attributes[] = \n" +"{\n" + ); + state->line += 4; + state->chars = 0; +} + +static void print_trailer(struct print_state *state) +{ + start_line(state, 0); + fprintf(state->out, +"};\n" + ); + +} + +static void print_entry(struct print_state *state, int letter, int attrib) +{ + int amount; +#if 0 + fprintf(state->out, "/* 0x%04X */ 0x%02X,\n", letter, attrib); + state->line++; + state->chars = 0; +#else + start_line(state, 75); + amount = fprintf(state->out, "%d,", attrib); + if (amount > 0) { + state->chars += amount; + } +#endif +} + +static void dump_attributes(FILE *out, unsigned char *attributes) +{ + int i; + struct print_state state[1]; + state->out = out; + state->line = 1; + state->chars = 0; + print_header(state); + for(i = 0; i < NUM_KEYSYMS; i++) { + print_entry(state, i, attributes[i]); + } + print_trailer(state); +} + +struct entry { + unsigned long range_start; + unsigned long range_end; + char type[3]; +}; + +static unsigned char compute_attribute(char *type) +{ + unsigned char attrib = KEYSYM_UNKNOWN; + + if (type[0] == 'M') { + attrib = KEYSYM_COMBINING; + } + if (type[0] == 'N') { + attrib = KEYSYM_NUMBER; + } + if (type[0] == 'Z') { + attrib = KEYSYM_SPACE; + } + if (type[0] == 'C') { + attrib = KEYSYM_CONTROL; + } + if (type[0] == 'L') { + attrib = KEYSYM_LETTER; + } + if (type[0] == 'P') { + attrib = KEYSYM_PUNCTUATION; + } + if (type[0] == 'S') { + attrib = KEYSYM_SYMBOL; + } + return attrib; +} + +static void store_attributes(struct entry *entry, unsigned char *attributes) +{ + int i; + unsigned char attrib = compute_attribute(entry->type); + for(i = entry->range_start; (i < NUM_KEYSYMS) && (i <= entry->range_end); i++) { + attributes[i] = attrib; + } +} + +static int compute_missing_attribute(int letter) +{ + int attrib = KEYSYM_UNKNOWN; + switch(letter) { + /* keypad */ + case DKY_PAD_0: + case DKY_PAD_1: + case DKY_PAD_2: + case DKY_PAD_3: + case DKY_PAD_4: + case DKY_PAD_5: + case DKY_PAD_6: + case DKY_PAD_7: + case DKY_PAD_8: + case DKY_PAD_9: + attrib = KEYSYM_NUMBER; + break; + + case DKY_PAD_DECIMAL: + case DKY_PAD_SLASH: + case DKY_PAD_AST: + case DKY_PAD_MINUS: + case DKY_PAD_PLUS: + case DKY_PAD_ENTER: + case DKY_PAD_HOME: + case DKY_PAD_UP: + case DKY_PAD_PGUP: + case DKY_PAD_LEFT: + case DKY_PAD_CENTER: + case DKY_PAD_RIGHT: + case DKY_PAD_END: + case DKY_PAD_DOWN: + case DKY_PAD_PGDN: + case DKY_PAD_INS: + case DKY_PAD_DEL: + attrib = KEYSYM_EXTERNAL_FUNCTION; + break; + + /* function_keys */ + case DKY_F1: + case DKY_F2: + case DKY_F3: + case DKY_F4: + case DKY_F5: + case DKY_F6: + case DKY_F7: + case DKY_F8: + case DKY_F9: + case DKY_F10: + case DKY_F11: + case DKY_F12: + attrib = KEYSYM_EXTERNAL_FUNCTION; + break; + + /* cursor block */ + case DKY_INS: + case DKY_DEL: + case DKY_HOME: + case DKY_END: + case DKY_PGUP: + case DKY_PGDN: + case DKY_UP: + case DKY_DOWN: + case DKY_LEFT: + case DKY_RIGHT: + attrib = KEYSYM_EXTERNAL_FUNCTION; + break; + + /* shift keys */ + case DKY_L_ALT: + case DKY_R_ALT: + case DKY_L_CTRL: + case DKY_R_CTRL: + case DKY_L_SHIFT: + case DKY_R_SHIFT: + case DKY_NUM: + case DKY_SCROLL: + case DKY_CAPS: + attrib = KEYSYM_EXTERNAL_FUNCTION; + break; + + /* special keys */ + case DKY_PRTSCR: + case DKY_PAUSE: + case DKY_SYSRQ: + case DKY_BREAK: + attrib = KEYSYM_EXTERNAL_FUNCTION; + break; + + /* more */ + case DKY_PAD_SEPARATOR: + attrib = KEYSYM_EXTERNAL_FUNCTION; + break; + + /* alt */ + case DKY_ALT_A: + case DKY_ALT_B: + case DKY_ALT_C: + case DKY_ALT_D: + case DKY_ALT_E: + case DKY_ALT_F: + case DKY_ALT_G: + case DKY_ALT_H: + case DKY_ALT_I: + case DKY_ALT_J: + case DKY_ALT_K: + case DKY_ALT_L: + case DKY_ALT_M: + case DKY_ALT_N: + case DKY_ALT_O: + case DKY_ALT_P: + case DKY_ALT_Q: + case DKY_ALT_R: + case DKY_ALT_S: + case DKY_ALT_T: + case DKY_ALT_U: + case DKY_ALT_V: + case DKY_ALT_W: + case DKY_ALT_X: + case DKY_ALT_Y: + case DKY_ALT_Z: + attrib = KEYSYM_EXTERNAL_FUNCTION; + break; + + /* more */ + case DKY_LEFT_TAB: + attrib = KEYSYM_EXTERNAL_FUNCTION; + break; + + /* dead keys */ + case DKY_DEAD_ALT: + case DKY_DEAD_CTRL: + case DKY_DEAD_SHIFT: + case DKY_DEAD_ALTGR: + case DKY_DEAD_DKY_PAD: + attrib = KEYSYM_DEAD; + break; + + /* dosemu special functions */ + case DKY_DOSEMU_HELP: + case DKY_DOSEMU_REDRAW: + case DKY_DOSEMU_SUSPEND: + case DKY_DOSEMU_RESET: + case DKY_DOSEMU_MONO: + case DKY_DOSEMU_PAN_UP: + case DKY_DOSEMU_PAN_DOWN: + case DKY_DOSEMU_PAN_LEFT: + case DKY_DOSEMU_PAN_RIGHT: + case DKY_DOSEMU_REBOOT: + case DKY_DOSEMU_EXIT: + case DKY_DOSEMU_VT_1: + case DKY_DOSEMU_VT_2: + case DKY_DOSEMU_VT_3: + case DKY_DOSEMU_VT_4: + case DKY_DOSEMU_VT_5: + case DKY_DOSEMU_VT_6: + case DKY_DOSEMU_VT_7: + case DKY_DOSEMU_VT_8: + case DKY_DOSEMU_VT_9: + case DKY_DOSEMU_VT_10: + case DKY_DOSEMU_VT_11: + case DKY_DOSEMU_VT_12: + case DKY_DOSEMU_VT_NEXT: + case DKY_DOSEMU_VT_PREV: + case DKY_MOUSE_UP: + case DKY_MOUSE_DOWN: + case DKY_MOUSE_LEFT: + case DKY_MOUSE_RIGHT: + case DKY_MOUSE_UP_AND_LEFT: + case DKY_MOUSE_UP_AND_RIGHT: + case DKY_MOUSE_DOWN_AND_LEFT: + case DKY_MOUSE_DOWN_AND_RIGHT: + case DKY_MOUSE_BUTTON_LEFT: + case DKY_MOUSE_BUTTON_MIDDLE: + case DKY_MOUSE_BUTTON_RIGHT: + case DKY_MOUSE_GRAB: + case DKY_DOSEMU_X86EMU_DEBUG: + attrib = KEYSYM_INTERNAL_FUNCTION; + break; + } + return attrib; +} + +static int read_next_entry(FILE *in, struct entry *entry) +{ + char number_str1[5]; + char number_str2[5]; + char type_str[3]; + char data[2+1+4+1+4+1+100]; + char *result; + do { + result = fgets(data, sizeof(data), in); + if (result == 0) { + return -1; + } + } while(data[0] == '#'); + memcpy(type_str, data+0, 2); + type_str[2] = '\0'; + memcpy(number_str1, data+3, 4); + number_str1[4] = '\0'; + if (data[7] == '-') { + memcpy(number_str2, data+8, 4); + number_str2[4] = '\0'; + } else { + strcpy(number_str2, number_str1); + } + entry->range_start = strtoul(number_str1, NULL, 16); + entry->range_end = strtoul(number_str2, NULL, 16); + strcpy(entry->type, type_str); + + return 0; +} + +static void init_keysyms(unsigned char *attributes) +{ + int i; + for(i = 0; i < NUM_KEYSYMS; i++) { + attributes[i] = compute_missing_attribute(i); + } +} + +static void read_file(FILE *in, unsigned char *attributes) +{ + struct entry entry[1]; + while(read_next_entry(in, entry) == 0) { + store_attributes(entry, attributes); + } +} + +static void process_file(FILE *in, FILE *out) +{ + static unsigned char attributes[NUM_KEYSYMS]; + init_keysyms(attributes); + read_file(in, attributes); + dump_attributes(out, attributes); +} + +int main(int argc, char**argv) +{ + if (argc != 1) { + fprintf(stderr, "usage %s < infilename > outfile \n", + argv[0]); + exit(1); + } + process_file(stdin, stdout); + return 0; +} diff --git a/src/base/lib/translate/translate.c b/src/base/lib/translate/translate.c new file mode 100644 index 0000000..4ebbba4 --- /dev/null +++ b/src/base/lib/translate/translate.c @@ -0,0 +1,924 @@ +#include +#include +#include +#include +#include +#include "init.h" +#include "emu.h" +#include "translate/translate.h" +#include "translate/unicode_symbols.h" +#include "dosemu_debug.h" + +/* + * Default/Dummy Charset operations + * ========================= + */ + +static size_t charset_to_unicode_default( + struct char_set_state *state, + struct char_set *set, int offset, + t_unicode *symbol_out, const unsigned char *str, size_t in_len) +{ + if (in_len == 0) { + errno = EINVAL; + } + else { + errno = EILSEQ; + } + return -1; +} + +static size_t unicode_to_charset_default( + struct char_set_state *state, + struct char_set *set, int offset, + t_unicode symbol, unsigned char *outbuf, size_t out_len) +{ + errno = EILSEQ; + return -1; +} + +static int init_charset_state_dummy(struct char_set_state *state) +{ + return 0; +} + +static void cleanup_charset_state_dummy(struct char_set_state *state) +{ + return; +} + +static void copy_charset_default( + struct char_set_state *dst, const struct char_set_state *src) +{ + memcpy(&dst->u, &src->u, sizeof(src->u)); +} + +static void foreach_charset_default(struct char_set *set, + int offset, + void *callback_data, foreach_callback_t callback) +{ + /* I don't know anything so do this with brute force! */ + t_unicode i; + unsigned char buff[100]; + + /* Walk through all of the unicode characters and report + * if how they are mapped + */ + for(i = 0; i < 0xFFFF; i++) { + size_t len; + struct char_set_state state; + init_charset_state(&state, set); + len = unicode_to_charset(&state, i, buff, sizeof(buff)); + if (len > 0) { + callback(callback_data, i, buff, len); + } + cleanup_charset_state(&state); + } +} + +struct char_set_operations dummy_charset_ops = { + .unicode_to_charset= &unicode_to_charset_default, + .charset_to_unicode= &charset_to_unicode_default, + .init= &init_charset_state_dummy, + .cleanup= &cleanup_charset_state_dummy, + .copy= ©_charset_default, + .foreach= &foreach_charset_default, +}; +/* + * Default Primitive Charset operations + * ====================================== + */ +static size_t unicode_to_charset_primitive( + struct char_set_state *state, + struct char_set *piece, int offset, + t_unicode symbol, unsigned char *outbuf, size_t out_len) +{ + int i; + unsigned char buff[3]; + size_t buff_len; + + buff_len = 0; + + for(i = 0; i < piece->chars_count; i++) { + if (piece->chars[i] == symbol) { + break; + } + } + if (i < piece->chars_count) { + buff_len = piece->bytes_per_char; + if (buff_len == 1) { + buff[0] = i + offset; + } + else if (buff_len == 2) { + buff[0] = (i/piece->logical_chars_count) + offset; + buff[1] = (i%piece->logical_chars_count) + offset; + } + } + if (buff_len > 0) { + if (buff_len <= out_len) { + memcpy(outbuf, buff, buff_len); + return buff_len; + } + else { + errno = -E2BIG; + return -1; + } + } + else { + errno = EILSEQ; + return -1; + } +} + +static size_t charset_to_unicode_primitive( + struct char_set_state *state, + struct char_set *piece, int offset, + t_unicode *symbol_out, + const unsigned char *str, size_t in_len) +{ + int index; + int max; + + max = offset + piece->logical_chars_count -1; + index = piece->chars_count; + + if (in_len < piece->bytes_per_char) { + errno = EINVAL; + return -1; + } + if (piece->bytes_per_char == 1) { + unsigned char ch = *str; + if ((ch >= offset) && (ch <= max)) { + index = ch - offset; + } +#if 0 + u_printf("primitive: range:%02x-%02x %02x->%d\n", + offset, max, ch, index); +#endif + } + else if (piece->bytes_per_char == 2) { + if ((str[0] >= offset) && (str[0] <= max) && + (str[1] >= offset) && (str[1] <= max)) { + index = ((str[0] - offset) * piece->logical_chars_count) + + (str[1] - offset); + } +#if 0 + u_printf("primitive: range:%02x-%02x %02x:%02x->%d\n", + offset, max, str[0], str[1], index); +#endif + } + if (index < piece->chars_count) { + *symbol_out = piece->chars[index]; + return piece->bytes_per_char; + } + else { + errno = EILSEQ; + return -1; + } +} + +static void foreach_primitive(struct char_set *piece, + int offset, + void *callback_data, foreach_callback_t callback) +{ + unsigned char buff[2]; + int i; + for(i = 0; i < piece->chars_count; i++) { + if (piece->bytes_per_char == 1) { + buff[0] = i + offset; + } + else if (piece->bytes_per_char == 2) { + buff[0] = (i/piece->logical_chars_count) + offset; + buff[1] = (i%piece->logical_chars_count) + offset; + } + callback(callback_data, + piece->chars[i], buff, piece->bytes_per_char); + } +} + + +struct char_set_operations primitive_charset_ops = +{ + .unicode_to_charset= &unicode_to_charset_primitive, + .charset_to_unicode= &charset_to_unicode_primitive, + .init= &init_charset_state_dummy, + .cleanup= &cleanup_charset_state_dummy, + .copy= ©_charset_default, + .foreach= &foreach_primitive, +}; + +/* + * Default Compound Charset operations + * ===================================== + */ + +static size_t attempt_piece(struct char_set_state *state, + struct char_set *piece, int offset, + t_unicode symbol, + unsigned char *outbuf, size_t out_len) +{ + if (piece == NULL) { + return -1; + } + if (piece->logical_chars_count == 94) { + offset++; + } + return piece->ops->unicode_to_charset(state, piece, offset, + symbol, outbuf, out_len); +} + +static size_t unicode_to_compound_charset( + struct char_set_state *ch_state, + struct char_set *set, int offset, + t_unicode symbol, + unsigned char *outbuf, size_t out_len) +{ + size_t result; + + result = -1; + errno = EILSEQ; + + if (set->g0->logical_chars_count == 94) { + if (symbol == U_SPACE) { + outbuf[0] = 0x20; + result = 1; + } + else if (symbol == U_DELETE) { + outbuf[0] = 0x7f; + result = 1; + } + } + if ((result == -1) && (errno == EILSEQ)) { + result = attempt_piece(ch_state, set->g0, 0x20, symbol, outbuf, out_len); + } + if ((result == -1) && (errno == EILSEQ)) { + result = attempt_piece(ch_state, set->g1, 0xA0, symbol, outbuf, out_len); + } + if ((result == -1) && (errno == EILSEQ)) { + result = attempt_piece(ch_state, set->c0, 0x00, symbol, outbuf, out_len); + } + if ((result == -1) && (errno == EILSEQ)) { + result = attempt_piece(ch_state, set->c1, 0x80, symbol, outbuf, out_len); + } + return result; +} + + +static size_t compound_charset_to_unicode( + struct char_set_state *state, + struct char_set *set, int offset, + t_unicode *symbol_out, + const unsigned char *str, size_t in_len) +{ + struct char_set *piece; + if (set->g0->logical_chars_count == 94) { + if (str[0] == 0x20) { + *symbol_out = U_SPACE; + return 1; + } + else if (str[0] == 0x7f) { + *symbol_out = U_DELETE; + return 1; + } + } + if ((str[0] & 0x7f) <= 0x1F) { + offset = str[0] & 0x80; + piece = (offset < 0x80)?set->c0:set->c1; + } + else { + offset = (str[0] & 0x80) + 0x20; + piece = (offset < 0x80)?set->g0:set->g1; + if (piece && piece->logical_chars_count == 94) { + offset++; + } + } + if (piece == NULL) { + return -1; + } + return piece->ops->charset_to_unicode( + state, piece, offset, symbol_out, str, in_len); +} + + +static void foreach_piece( + struct char_set *piece, int offset, + void *callback_data, foreach_callback_t callback) +{ + if (piece->logical_chars_count == 94) { + offset++; + } + piece->ops->foreach(piece, offset, callback_data, callback); +} + +static void foreach_compound_charset(struct char_set *set, + int offset, + void *callback_data, foreach_callback_t callback) +{ + foreach_piece(set->c0, 0x00, callback_data, callback); + foreach_piece(set->g0, 0x20, callback_data, callback); + foreach_piece(set->c1, 0x80, callback_data, callback); + foreach_piece(set->g1, 0xA0, callback_data, callback); +} + + +struct char_set_operations compound_charset_ops = { + .unicode_to_charset= &unicode_to_compound_charset, + .charset_to_unicode= &compound_charset_to_unicode, + .init= &init_charset_state_dummy, + .cleanup= &cleanup_charset_state_dummy, + .copy= ©_charset_default, + .foreach= &foreach_compound_charset, +}; + +/* + * General Charset operations + * ============================ + */ + +static void initialize_charset_piece(struct char_set *piece) +{ + if (!piece) { + return; + } + if (!piece->next) { + register_charset(piece); + } +} + +static void initialize_charset_ops(struct char_set *set) +{ + if (!set->ops) { + if (set->c0 || set->c1 || set->g0 || set->g1) { + set->ops = &compound_charset_ops; + } + else if (set->chars) { + set->ops = &primitive_charset_ops; + } + else { + set->ops = &dummy_charset_ops; + } + } + if (!set->ops->unicode_to_charset) { + set->ops->unicode_to_charset = dummy_charset_ops.unicode_to_charset; + } + if (!set->ops->charset_to_unicode) { + set->ops->charset_to_unicode = dummy_charset_ops.charset_to_unicode; + } + if (!set->ops->init) { + set->ops->init = dummy_charset_ops.init; + } + if (!set->ops->cleanup) { + set->ops->cleanup = dummy_charset_ops.cleanup; + } + if (!set->ops->copy) { + set->ops->copy = dummy_charset_ops.copy; + } + if (!set->ops->foreach) { + set->ops->foreach = dummy_charset_ops.foreach; + } +} + +static void initialize_charset(struct char_set *set) +{ + static const char null_string[] = ""; +#if 0 + fprintf(stderr, "++initializing char_set(%p): %s\n", + set, set->names[0]); +#endif + if (!set->chars) { + set->chars_count = 0; + } + if (!set->final_chars) { + set->final_chars = null_string; + } + initialize_charset_ops(set); + initialize_charset_piece(set->c0); + initialize_charset_piece(set->c1); + initialize_charset_piece(set->g0); + initialize_charset_piece(set->g1); +#if 0 + fprintf(stderr, "--finished char_set(%p): %s\n", + set, set->names[0]); +#endif +} + +static struct char_set *char_set_list_head = 0; + +struct char_set *lookup_charset(const char *name) +{ + struct char_set *list = char_set_list_head; +#if 0 + fprintf(stderr, "Lookup_charset:%s\n", name); +#endif + for(;list; list = list->next) { + const char **ptr; + for(ptr = list->names; *ptr; ptr++) { + if (strcmp(*ptr, name) == 0) { +#if 0 + fprintf(stderr, "found charset:%s\n", name); +#endif + return list; + } + } + } +#if 0 + fprintf(stderr, "Couldn't find charset:%s\n", name); +#endif + return NULL; +} + +struct char_set *lookup_charset_piece( + const char *final_chars, int len, int chars_count, int bytes_per_char) +{ + struct char_set *list = char_set_list_head; + for(; list; list = list->next) { + if ((strlen(list->final_chars) == len) && + (memcmp(list->final_chars, final_chars, len) == 0) && + (list->logical_chars_count == chars_count) && + (list->bytes_per_char == bytes_per_char)) { + return list; + } + } + return 0; +} + +void traverse_charsets(void *callback_data, charsets_callback_t callback) +{ + struct char_set *list = char_set_list_head; + + for(; list; list = list->next) { + callback(callback_data, list); + } +} + +int register_charset(struct char_set *set) +{ + struct char_set *list = char_set_list_head; + int result = 0; +#if 0 + fprintf(stderr, "+registering char_set(%p): %s\n", + set, set->names[0]); +#endif + if (set->next == 0) { + if (list == 0) { + char_set_list_head = set; + } else { + while (list->next) { + list = list->next; + } + if (list != set) { + list->next = set; + } + } + initialize_charset(set); + } + else { + result = -1; + } +#if 0 + fprintf(stderr, "-finished char_set(%p): %s -->%d\n", + set, set->names[0], result); +#endif + return result; +} + +int unregister_charset(struct char_set *set) +{ + struct char_set *list = char_set_list_head; + if (list == 0) { + return -1; + } + if (list == set) { + char_set_list_head = list->next; + } else { + while ((list->next != set) && (list->next != 0)) { + list = list->next; + } + if (list->next == set) { + list->next = set->next; + set->next = 0; + } else { + return -1; + } + } + return 0; +} + +struct unicode_to_charset_state { + jmp_buf jmp_env; + t_unicode symbol; + struct char_set_state *ostate; + struct char_set *chars; + unsigned char *outbuf; + size_t out_bytes_left; + size_t result; +}; + +static void unicode_to_charset_callback(void *p, t_unicode approximation) +{ + struct unicode_to_charset_state *state = p; + + if (debug_level('u') > 1) + u_printf("U: symbol %04x approximation: %04x\n", + state->symbol, approximation); + state->result = state->chars->ops->unicode_to_charset( + state->ostate, state->chars, 0, approximation, + state->outbuf, state->out_bytes_left); + + /* done searching? */ + if ((state->result != -1) || (errno != EILSEQ)) { + longjmp(state->jmp_env, 1); + } +} + +size_t unicode_to_charset(struct char_set_state *ostate, t_unicode symbol, + unsigned char *outbuf, size_t out_bytes_left) +{ + struct unicode_to_charset_state state; + + if (out_bytes_left == 0) { + errno = E2BIG; + return -1; + } + if (!ostate) { + errno = EBADF; + return -1; + } + state.chars = ostate->chars; + if (!state.chars) { + errno = EBADF; + return -1; + } + + state.result = state.chars->ops->unicode_to_charset( + ostate, state.chars, 0, symbol, outbuf, out_bytes_left); + + if ((state.result == -1) && (errno == EILSEQ)) { + state.symbol = symbol; + state.ostate = ostate; + state.outbuf = outbuf; + state.out_bytes_left = out_bytes_left; + if (setjmp(state.jmp_env) == 0) { + traverse_approximations(state.symbol, &state, + unicode_to_charset_callback); + } + } + if ((state.result == -1) && (errno == EILSEQ)) { + state.symbol = U_REPLACEMENT_CHARACTER; + state.ostate = ostate; + state.outbuf = outbuf; + state.out_bytes_left = out_bytes_left; + if (setjmp(state.jmp_env) == 0) { + traverse_approximations(state.symbol, &state, + unicode_to_charset_callback); + } + } + + if (debug_level('u') > 1) { + int i; + u_printf("U: unicode->charset charset:%s symbol:%04x -> char:", + state.chars->names[0], symbol); + for(i = 0; (state.result != -1) && (i < state.result); i++) { + u_printf("%s%02x", ((i != 0)?",":""), outbuf[i]); + } + u_printf("...%zu", state.result); + if (state.result == -1) { + u_printf(":%d(%s)", errno, strerror(errno)); + } + u_printf("\n"); + } + + return state.result; +} + +size_t charset_to_unicode(struct char_set_state *state, + t_unicode *symbol, + const unsigned char *inbuf, size_t in_bytes_left) +{ + struct char_set *chars; + size_t result; + + if (!state) { + errno = EBADF; + return -1; + } + chars = state->chars; + if (!chars || !inbuf) { + errno = EBADF; + return -1; + } + if (in_bytes_left <= 0) { + errno = EINVAL; + return -1; + } + result = chars->ops->charset_to_unicode( + state, chars, 0, symbol, inbuf, in_bytes_left); + if (debug_level('u') > 1) { + int i; + u_printf("U: charset->unicode charset:%s ", chars->names[0]); + for(i = 0; (result != -1) && (i < result); i++) { + u_printf("%s%02x", ((i != 0)?",":""), inbuf[i]); + } + u_printf(" -> symbol:%04x...%zu", + *symbol, result); + if (result == -1) { + u_printf(":%d(%s)", errno, strerror(errno)); + } + u_printf("\n"); + } + return result; +} + +int init_charset_state(struct char_set_state *state, struct char_set *chars) +{ + assert(chars && state); + memset(state, '\0', sizeof(*state)); + state->chars = chars; + return chars->ops->init(state); +} + +void copy_charset_state(struct char_set_state *dst, + const struct char_set_state *src) +{ + if (!src || !dst) { + return; + } + dst->chars = src->chars; + src->chars->ops->copy(dst, src); + return; +} + +void cleanup_charset_state(struct char_set_state *state) +{ + if (!state) { + return; + } + if (state->chars->ops && state->chars->ops->cleanup) { + state->chars->ops->cleanup(state); + } +} + +void foreach_character_mapping(struct char_set *set, + void *callback_data, foreach_callback_t callback) +{ + set->ops->foreach(set, 0, callback_data, callback); +} + +char *unicode_string_to_charset(const wchar_t *u, const char *charset) +{ + struct char_set_state paste_state; + struct char_set *paste_charset; + char *s, *p; + size_t len = 0, result; + + while (u[len]) + len++; + paste_charset = lookup_charset(charset); + len *= MB_LEN_MAX; + len++; + p = s = malloc(len); + init_charset_state(&paste_state, paste_charset); + + while (*u) { + result = unicode_to_charset(&paste_state, *u++, (unsigned char *)p, len); + if (result == -1) { + warn("unicode to string unfinished\n"); + break; + } + p += result; + len -= result; + } + *p = '\0'; + cleanup_charset_state(&paste_state); + return s; +} + +CONSTRUCTOR(static void init(void)) +{ + register_debug_class('u', 0, "Unicode translation"); +} + +#define TRANSLATE_DEBUG 0 + +#if TRANSLATE_DEBUG +#include "video.h" +#include +/* koi8-r -> cp866 */ +const u_char koi8_to_dos[] = { + 0xa0, 0xa1, 0xa2, 0xf1, 0xa4, 0xa5, 0xa6, 0xa7, /* A0-A7 */ + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* A8-AF */ + 0xb0, 0xb1, 0xb2, 0xf0, 0xb4, 0xb5, 0xb6, 0xb7, /* B0-B7 */ + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* B8-BF */ + 0xee, 0xa0, 0xa1, 0xe6, 0xa4, 0xa5, 0xe4, 0xa3, /* C0-C7 */ + 0xe5, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, /* C8-CF */ + 0xaf, 0xef, 0xe0, 0xe1, 0xe2, 0xe3, 0xa6, 0xa2, /* D0-D7 */ + 0xec, 0xeb, 0xa7, 0xe8, 0xed, 0xe9, 0xe7, 0xea, /* D8-DF */ + 0x9e, 0x80, 0x81, 0x96, 0x84, 0x85, 0x94, 0x83, /* E0-E7 */ + 0x95, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, /* E8-EF */ + 0x8f, 0x9f, 0x90, 0x91, 0x92, 0x93, 0x86, 0x82, /* F0-F7 */ + 0x9c, 0x9b, 0x87, 0x98, 0x9d, 0x99, 0x97, 0x9a /* F8-FF */ +}; +/* cp866 -> koi8-r */ +static const u_char dos_to_koi8[] = { + 0xe1, 0xe2, 0xf7, 0xe7, 0xe4, 0xe5, 0xf6, 0xfa, /* 80-87 */ + 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, /* 88-8F */ + 0xf2, 0xf3, 0xf4, 0xf5, 0xe6, 0xe8, 0xe3, 0xfe, /* 90-97 */ + 0xfb, 0xfd, 0xff, 0xf9, 0xf8, 0xfc, 0xe0, 0xf1, /* 98-9F */ + 0xc1, 0xc2, 0xd7, 0xc7, 0xc4, 0xc5, 0xd6, 0xda, /* A0-A7 */ + 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, /* A8-AF */ + 0x90, 0x91, 0x92, 0x81, 0x87, 0xb2, 0xb4, 0xa7, /* B0-B7 */ + 0xa6, 0xb5, 0xa1, 0xa8, 0xae, 0xad, 0xac, 0x83, /* B8-BF */ + 0x84, 0x89, 0x88, 0x86, 0x80, 0x8a, 0xaf, 0xb0, /* C0-C7 */ + 0xab, 0xa5, 0xbb, 0xb8, 0xb1, 0xa0, 0xbe, 0xb9, /* C8-CF */ + 0xba, 0xb6, 0xb7, 0xaa, 0xa9, 0xa2, 0xa4, 0xbd, /* D0-D7 */ + 0xbc, 0x85, 0x82, 0x8d, 0x8c, 0x8e, 0x8f, 0x8b, /* D8-DF */ + 0xd2, 0xd3, 0xd4, 0xd5, 0xc6, 0xc8, 0xc3, 0xde, /* E0-E7 */ + 0xdb, 0xdd, 0xdf, 0xd9, 0xd8, 0xdc, 0xc0, 0xd1, /* E8-EF */ + 0xb3, 0xa3, 0x99, 0x98, 0x93, 0x9b, 0x9f, 0x97, /* F0-F7 */ + 0x9c, 0x95, 0x9e, 0x96, 0xbf, 0x9d, 0x94, 0x9a /* F8-FF */ +}; +/* iso-8859-1 -> cp437 */ +const unsigned char latin_to_dos[] = { + 0, 0xad, 0x9b, 0x9c, 0, 0x9d, 0x7c, 0x15, /* A0-A7 */ + 0x22, 0, 0xa6, 0xae, 0xaa, 0x2d, 0, 0, /* A8-AF */ + 0xf8, 0xf1, 0xfd, 0xfc, 0x27, 0xe6, 0x14, 0xf9, /* B0-B7 */ + 0x2c, 0, 0xa7, 0xaf, 0xac, 0xab, 0, 0xa8, /* B8-BF */ + 0, 0, 0, 0, 0x8e, 0x8f, 0x92, 0x80, /* C0-C7 */ + 0, 0x90, 0, 0, 0, 0, 0, 0, /* C8-CF */ + 0, 0xa5, 0, 0, 0, 0, 0x99, 0, /* D0-D7 */ + 0xed, 0, 0, 0, 0x9a, 0, 0, 0xe1, /* D8-DF */ + 0x85, 0xa0, 0x83, 0, 0x84, 0x86, 0x91, 0x87, /* E0-E7 */ + 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, /* E8-EF */ + 0, 0xa4, 0xa2, 0x95, 0x93, 0, 0x94, 0xf6, /* F0-F7 */ + 0xed, 0x97, 0xa3, 0x96, 0x81, 0, 0, 0x98 /* F8-FF */ +}; + +/* cp437 -> iso8859-1 */ +const unsigned char dos_to_latin[] = { + 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, /* 80-87 */ + 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5, /* 88-8F */ + 0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, /* 90-97 */ + 0xff, 0xd6, 0xdc, 0xa2, 0xa3, 0xa5, 0x00, 0x00, /* 98-9F */ + 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xaa, 0xba, /* A0-A7 */ + 0xbf, 0x00, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb, /* A8-AF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* B0-B7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* B8-BF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* C0-C7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* C8-CF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* D0-D7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* D8-DF */ + 0x00, 0xdf, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x00, /* E0-E7 */ + 0x00, 0x00, 0x00, 0xf0, 0x00, 0xd8, 0x00, 0x00, /* E8-EF */ + 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0xf7, 0x00, /* F0-F7 */ + 0xb0, 0xb7, 0x00, 0x00, 0xb3, 0xb2, 0x00, 0x00 /* F8-FF */ +}; + +/* iso-8859-1 -> cp850 */ +const unsigned char latin1_to_dos[] = { + 0xff, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, /* A0-A7 */ + 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, /* A8-AF */ + 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, /* B0-B7 */ + 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, /* B8-BF */ + 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* C0-C7 */ + 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* C8-CF */ + 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, /* D0-D7 */ + 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, /* D8-DF */ + 0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87, /* E0-E7 */ + 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, /* E8-EF */ + 0xd0, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6, /* F0-F7 */ + 0xed, 0x97, 0xa3, 0x96, 0x81, 0xec, 0xe7, 0x98 /* F8-FF */ +}; + +/* cp850 -> iso-8859-1 */ +const unsigned char dos_to_latin1[] = { + 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, /* 80-87 */ + 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5, /* 88-8F */ + 0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, /* 90-97 */ + 0xff, 0xd6, 0xdc, 0xa2, 0xa3, 0xd8, 0xd7, 0x00, /* 98-9F */ + 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xaa, 0xba, /* A0-A7 */ + 0xbf, 0xae, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb, /* A8-AF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xc2, 0xc0, /* B0-B7 */ + 0xa9, 0x00, 0x00, 0x00, 0x00, 0xa2, 0xa5, 0xac, /* B8-BF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xc3, /* C0-C7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, /* C8-CF */ + 0xf0, 0xd0, 0xca, 0xcb, 0xc8, 0x00, 0xcd, 0xce, /* D0-D7 */ + 0xcf, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xcc, 0x00, /* D8-DF */ + 0xd3, 0xdf, 0xd4, 0xd2, 0xf5, 0xe5, 0xb5, 0xdc, /* E0-E7 */ + 0xfc, 0xda, 0xdb, 0xd9, 0xfd, 0xdd, 0xad, 0xb4, /* E8-EF */ + 0xad, 0xb1, 0x00, 0xbe, 0xb6, 0xa7, 0xf7, 0xb8, /* F0-F7 */ + 0xb0, 0xa8, 0xb7, 0xb9, 0xb3, 0xb2, 0x00, 0xa0 /* F8-FF */ +}; + +/* iso-8859-2 -> cp852 */ +const unsigned char latin2_to_dos[] = { + 0, 0xa4, 0xf4, 0x9d, 0xcf, 0x95, 0x97, 0xf5, /* A0-A7 */ + 0xf9, 0xe6, 0xb8, 0x9b, 0x8d, 0xf0, 0xa6, 0xbd, /* A8-AF */ + 0xf8, 0xa5, 0xf7, 0x88, 0xef, 0x96, 0x98, 0xf3, /* B0-B7 */ + 0xf2, 0xe7, 0xad, 0x9c, 0xab, 0xf1, 0xa7, 0xbe, /* B8-BF */ + 0xe8, 0xb5, 0xb6, 0xc6, 0x8e, 0x91, 0x8f, 0x80, /* C0-C7 */ + 0xac, 0x90, 0xa8, 0xd3, 0xb7, 0xd6, 0xd7, 0xd2, /* C8-CF */ + 0xd1, 0xe3, 0xd5, 0xe0, 0xe2, 0x8a, 0x99, 0x9e, /* D0-D7 */ + 0xfc, 0xde, 0xe9, 0xeb, 0x9a, 0xed, 0xdd, 0xe1, /* D8-DF */ + 0xea, 0xa0, 0x83, 0xc7, 0x84, 0x92, 0x86, 0x87, /* E0-E7 */ + 0x9f, 0x82, 0xa9, 0x89, 0xd8, 0xa1, 0x8c, 0xd4, /* E8-EF */ + 0xd0, 0xe4, 0xe5, 0xa2, 0x93, 0x8b, 0x94, 0xf6, /* F0-F7 */ + 0xfd, 0x85, 0xa3, 0xfb, 0x81, 0xec, 0xee, 0xfa /* F8-FF */ +}; + +/* cp852 -> iso-8859-2 */ +const unsigned char dos_to_latin2[] = { + 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xf9, 0xe6, 0xe7, /* 80-87 */ + 0xb3, 0xeb, 0xf5, 0xd5, 0xee, 0xac, 0xc4, 0xc6, /* 88-8F */ + 0xc9, 0xc5, 0xe5, 0xf4, 0xf6, 0xa5, 0xb5, 0xa6, /* 90-97 */ + 0xb6, 0xd6, 0xdc, 0xab, 0xbb, 0xa3, 0xd7, 0xe8, /* 98-9F */ + 0xe1, 0xed, 0xf3, 0xfa, 0xa1, 0xb1, 0xae, 0xbe, /* A0-A7 */ + 0xca, 0xea, 0x00, 0xbc, 0xc8, 0xba, 0xab, 0xbb, /* A8-AF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xc2, 0xcc, /* B0-B7 */ + 0xaa, 0x00, 0x00, 0x00, 0x00, 0xaf, 0xbf, 0x00, /* B8-BF */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe3, /* C0-C7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, /* C8-CF */ + 0xf0, 0xd0, 0xcf, 0xcb, 0xef, 0xd2, 0xcd, 0xce, /* D0-D7 */ + 0xec, 0x00, 0x00, 0x00, 0x00, 0xde, 0xd9, 0x00, /* D8-DF */ + 0xd3, 0xdf, 0xd4, 0xd1, 0xf1, 0xf2, 0xa9, 0xb9, /* E0-E7 */ + 0xc0, 0xda, 0xe0, 0xdb, 0xfd, 0xdd, 0xfe, 0xb4, /* E8-EF */ + 0xad, 0xbd, 0xb2, 0xb7, 0xa2, 0xa7, 0xf7, 0xb8, /* F0-F7 */ + 0xb0, 0xa8, 0xff, 0xfb, 0xd8, 0xf8, 0x00, 0xa0 /* F8-FF */ +}; +int lookup_character(int index, const unsigned char *table) +{ + int result = -1; + if (index >= 0x80) { + result = table[index - 0x80]; + } else if (index == 124) { + result = 0xa6; + } else if (index == 21) { + result = 0xa7; + } else if (index == 20) { + result = 0xb6; + } else { + result = index; + } + return result; +} + + +int reverse_lookup_character(int index, const unsigned char *table) +{ + int result = -1; + if (index >= 0xa0) { + result = table[index - 0xa0]; + } else { + result = index; + } + return result; +} + +void translate_test(void) +{ + unsigned char test_charset_new[256], test_charset_old[256]; + unsigned char reverse_charset_new[256], reverse_charset_old[256]; + const unsigned char *old_charset, *old_reverse_charset; + struct char_set *paste_charset, *video_charset; + int i; + memset(test_charset_new, 0, 256); + memset(test_charset_old, 0, 256); + memset(reverse_charset_new, 0, 256); + memset(reverse_charset_old, 0, 256); + paste_charset = lookup_charset("iso8859_1_terminal"); + video_charset = lookup_charset("cp437_display"); + old_charset = dos_to_latin; + old_reverse_charset = latin_to_dos; + /* Build the translate tables */ + for(i = 0; i < 256; i++) { + t_unicode uni; + uni = charset_to_unicode(video_charset, i); + test_charset_new[i] = unicode_to_charset(paste_charset, uni); + test_charset_old[i] = lookup_character(i, old_charset); + v_printf("f:%d->%04x:%02x\n", + i, uni, test_charset_new[i]); + } + for(i = 0; i < 256; i++) { + t_unicode uni; + uni = charset_to_unicode(paste_charset, i); + reverse_charset_new[i] = unicode_to_charset(video_charset, uni); + reverse_charset_old[i] = reverse_lookup_character(i, old_reverse_charset); + v_printf("r:%d->%04x:%02x\n", + i, uni, reverse_charset_new[i]); + } + /* Now spot differences */ + for(i = 0; i < 256; i++) { + if (test_charset_new[i] != test_charset_old[i]) { + v_printf("f: at: %d was: %02x is: %02x\n", + i, test_charset_old[i], test_charset_new[i]); + } + } + for(i = 0; i < 256; i++) { + if (reverse_charset_new[i] != reverse_charset_old[i]) { + v_printf("r: at: %d was: %02x is: %02x\n", + i, reverse_charset_old[i], reverse_charset_new[i]); + } + } + +} + +#endif /* TRANSLATE_DEBUG */ diff --git a/src/base/lib/translate/translate_config.c b/src/base/lib/translate/translate_config.c new file mode 100644 index 0000000..b15af42 --- /dev/null +++ b/src/base/lib/translate/translate_config.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include "dosemu_debug.h" +#include "translate/translate.h" +#include "translate/dosemu_charset.h" +#include "dosemu_config.h" +#include "init.h" +#include "emu.h" +#include "video.h" +#include "utilities.h" + +struct translate_config_t trconfig; /* Initialized to nulls */ + +static void config_translate_scrub(void) +{ + /* set the character sets used base upon config.term_charset */ + if (!trconfig.keyb_charset) + trconfig.keyb_charset = lookup_charset("default"); + if (!trconfig.video_mem_charset) + trconfig.video_mem_charset = lookup_charset("cp437"); +#if 0 + if (!trconfig.keyb_config_charset) + trconfig.keyb_config_charset = + get_terminal_charset(lookup_charset("cp437")); +#endif + if (!trconfig.output_charset) + trconfig.output_charset = lookup_charset("default"); + if (!trconfig.dos_charset) + trconfig.dos_charset = get_terminal_charset(lookup_charset("cp437")); + v_printf("video_mem_charset=%s\n", + trconfig.video_mem_charset?(trconfig.video_mem_charset->names)[0]:""); + k_printf("keyb_config_charset=%s\n", + trconfig.keyb_charset?trconfig.keyb_config_charset->names[0]:""); + v_printf("output_charset=%s\n", + trconfig.output_charset?trconfig.output_charset->names[0]:""); + k_printf("keyb_charset=%s\n", + trconfig.keyb_charset?trconfig.keyb_charset->names[0]:""); + d_printf("dos_charset=%s\n", + trconfig.dos_charset?trconfig.dos_charset->names[0]:""); +} + +CONSTRUCTOR(static void init(void)) +{ + register_config_scrub(config_translate_scrub); +} diff --git a/src/base/lib/translate/unicode_utils.c b/src/base/lib/translate/unicode_utils.c new file mode 100644 index 0000000..95dac24 --- /dev/null +++ b/src/base/lib/translate/unicode_utils.c @@ -0,0 +1,184 @@ +#include + +/* return the number of unicode characters in string S */ +size_t unicode_len(t_unicode *str) +{ + size_t count = 0; + while(*str++) { + count++; + } + return count; +} + +/* number of characters in a possibly multibyte string */ +size_t character_count(const struct char_set_state *in_state, const char *str, + size_t max_str_len) +{ + struct char_set_state state; + size_t characters, consumed; + t_unicode temp; + + characters = 0; + copy_charset_state(&state, in_state); + do { + consumed = charset_to_unicode(&state, &temp, (const unsigned char *)str, max_str_len); + if (consumed == (size_t) -1) { + /* An error occurred abort */ + if (characters == 0) { + characters = (size_t) -1; + } + break; + } + if (consumed) { + max_str_len -= consumed; + str += consumed; + characters++; + } + } while(max_str_len && (consumed > 0)); + cleanup_charset_state(&state); + return characters; +} + +/* convert a possibly multibyte string to unicode */ +size_t charset_to_unicode_string(struct char_set_state *state, + t_unicode *dst, + const char **src, size_t src_len, size_t dst_len) +{ + size_t characters, consumed; + characters = 0; + + if (dst_len < 2) // require at least 2 for char and \0 + return -1; + do { + consumed = charset_to_unicode(state, dst, (const unsigned char *)*src, src_len); + if (consumed == (size_t) -1) { + /* An error occurred abort */ + if (characters == 0) { + characters = (size_t) -1; + } + break; + } + if (consumed) { + src_len -= consumed; + dst_len--; + *src += consumed; + characters++; + dst++; + } + } while(src_len && dst_len > 1 && (consumed > 0)); + if (characters != (size_t) -1) { + /* Null terminate the unicode string. */ + *dst = 0; + } + return characters; +} + +size_t unicode_to_charset_string(struct char_set_state *state, + char *dst, + const t_unicode **src, size_t src_len, size_t dst_len) +{ + size_t characters, produced; + characters = 0; + + if (dst_len < 2) // require at least 2 for char and \0 + return -1; + do { + produced = unicode_to_charset(state, **src, + (unsigned char *)dst, dst_len); + if (produced == (size_t) -1) { + /* An error occurred abort */ + if (characters == 0) { + characters = (size_t) -1; + } + break; + } + if (produced) { + src_len--; + dst_len -= produced; + (*src)++; + characters += produced; + dst += produced; + } + } while(src_len && dst_len > 1 && (produced > 0)); + if (characters != (size_t) -1) { + /* Null terminate the unicode string. */ + *dst = 0; + } + return characters; +} + +static unsigned char_value(wint_t ch) +{ + unsigned value = 37; + if ((ch >= '0') || (ch <= '9')) { + value = ch - '0'; + } + else if ((ch >= 'A') || (ch <= 'Z')) { + value = ch - 'A'; + } + else if ((ch >= 'a') || (ch <= 'z')) { + value = ch - 'a'; + } + return value; +} + + +/* convert a unicode string value into a number: see strtol */ +extern long int unicode_to_long (t_unicode *ptr, + t_unicode **endptr, int base) +{ + long int result; + int sign; + int value; + + sign = 1; + result = 0; + + if (base && ((base < 2) || (base > 36))) { + if (endptr) { + *endptr = ptr; + } + return 0; + } + /* skip leading spaces */ + while((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\n')) { + ptr++; + } + /* handle a leading sign */ + if (ptr[0] == '-') { + sign = -1; + ptr++; + } + if (ptr[0] == '+') { + sign = 1; + ptr++; + } + /* Figure out the base */ + if (base == 0) { + if ((ptr[0] == '0') && (ptr[1] == 'x')) { + base = 16; + } + else if (ptr[0] == '0') { + base = 8; + } + else { + base = 10; + } + } + /* skip 0x prefix */ + if ((base == 16) && (ptr[0] == '0') && (ptr[1] == 'x')) { + ptr += 2; + } + while((value = char_value(*ptr)) < base) { + result *= base; + result += value; + ptr++; + } + if (sign == -1) { + result = -result; + } + if (endptr) { + *endptr = ptr; + } + return result; +} diff --git a/src/base/misc/Makefile b/src/base/misc/Makefile new file mode 100644 index 0000000..6c2feb8 --- /dev/null +++ b/src/base/misc/Makefile @@ -0,0 +1,11 @@ + +top_builddir=../../.. +include $(top_builddir)/Makefile.conf + +CFILES = hma.c ioctl.c disks.c utilities.c dos2linux.c fatfs.c mmio_tracing.c \ + clipboard.c + +include $(REALTOPDIR)/src/Makefile.common + +clean:: + rm -f *.out diff --git a/src/base/misc/clipboard.c b/src/base/misc/clipboard.c new file mode 100644 index 0000000..11f4d62 --- /dev/null +++ b/src/base/misc/clipboard.c @@ -0,0 +1,175 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: clipboard support + * + * Authors: @andrewbird @stsp + */ +#include +#ifdef HAVE_LIBBSD +#include +#endif +#include "dosemu_debug.h" +#include "types.h" +#include "translate/translate.h" +#include "clipboard.h" + +struct clipboard_system *Clipboard; + +static char *clipboard_make_str_utf8(int type, const char *p, int size) +{ + char *q; + + if (type == CF_TEXT) { + q = strndup(p, size); + } else { // CF_OEMTEXT + struct char_set_state state; + int characters; + t_unicode *str; + + init_charset_state(&state, trconfig.dos_charset); + + characters = character_count(&state, p, size); + if (characters == -1) { + v_printf("SDL_clipboard: Write invalid char count\n"); + return NULL; + } + + str = malloc(sizeof(t_unicode) * (characters + 1)); + charset_to_unicode_string(&state, str, &p, size, characters + 1); + cleanup_charset_state(&state); + q = unicode_string_to_charset((wchar_t *)str, "utf8"); + free(str); + } + return q; +} + +static char *clipboard_make_str_dos(int type, const char *p, int size) +{ + char *q; + + if (type == CF_TEXT) { + q = strndup(p, size); + } else { // CF_OEMTEXT + struct char_set_state state; + struct char_set *utf8; + int characters; + t_unicode *str; + + utf8 = lookup_charset("utf8"); + init_charset_state(&state, utf8); + + characters = character_count(&state, p, size); + if (characters == -1) { + v_printf("SDL_clipboard: _clipboard_grab_data() invalid char count\n"); + return NULL; + } + + str = malloc(sizeof(t_unicode) * (characters + 1)); + charset_to_unicode_string(&state, str, &p, size, characters + 1); + cleanup_charset_state(&state); + q = unicode_string_to_charset((wchar_t *)str, trconfig.dos_charset->names[0]); + free(str); + } + return q; +} + +int register_clipboard_system(struct clipboard_system *cs) +{ + Clipboard = cs; + return 1; +} + +char *clip_str; + +static void do_clear(void) +{ + free(clip_str); + clip_str = NULL; +} + +void add_clip_str(char *q) +{ + if (clip_str) { + clip_str = realloc(clip_str, strlen(clip_str) + strlen(q) + 1); + strcat(clip_str, q); + free(q); + } else { + clip_str = q; + } +} + +int cnn_clear(void) +{ + do_clear(); + return TRUE; +} + +int cnn_write(int type, const char *p, int size) +{ + char *q; + + if (type != CF_TEXT && type != CF_OEMTEXT) { + v_printf("SDL_clipboard: Write failed, type (0x%02x) unsupported\n", type); + return FALSE; + } + q = clipboard_make_str_utf8(type, p, size); + if (!q) + return FALSE; + add_clip_str(q); + return TRUE; +} + +int cnn_getsize(int type) +{ + char *q; + int ret; + + if (type == CF_TEXT) + v_printf("SDL_clipboard: GetSize of type CF_TEXT\n"); + else if (type == CF_OEMTEXT) + v_printf("SDL_clipboard: GetSize of type CF_OEMTEXT\n"); + else { + v_printf("SDL_clipboard: GetSize failed (type 0x%02x unsupported\n", type); + return 0; + } + + if (!clip_str) { + v_printf("SDL_clipboard: GetSize failed (grabbed data is NULL\n"); + return 0; + } + q = clipboard_make_str_dos(type, clip_str, strlen(clip_str)); + if (!q) + return 0; + ret = strlen(clip_str) + 1; + free(q); + return ret; +} + +int cnn_getdata(int type, char *p, int size) +{ + char *q; + + if (!clip_str) + return FALSE; + q = clipboard_make_str_dos(type, clip_str, strlen(clip_str)); + if (!q) + return FALSE; + strlcpy(p, q, size); + free(q); + return TRUE; +} diff --git a/src/base/misc/disks.c b/src/base/misc/disks.c new file mode 100644 index 0000000..4c8f80e --- /dev/null +++ b/src/base/misc/disks.c @@ -0,0 +1,2182 @@ +/* dos emulator, Matthias Lautner + * Extensions by Robert Sanders, 1992-93 + * + * floppy disks, dos partitions or their images (files) (maximum 8 heads) + */ + +#include "emu.h" +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ + #include + #include + #include +#endif +#include +#include +#include + +#include "int.h" +#include "port.h" +#include "bios.h" +#include "disks.h" +#include "timers.h" +#include "doshelpers.h" +#include "priv.h" +#include "int.h" +#include "fatfs.h" +#include "utilities.h" +#include "dos2linux.h" +#include "redirect.h" +#include "cpu-emu.h" + +static uint8_t mbr_boot_code[] = { + /* + * Present the usual start to the code in an MBR + + jmp short @1 + nop + */ + 0xeb, + 0x01, + 0x90, + /* + @1: + mov ax,0fffeh + int 0e6h + */ + 0xb8, + DOS_HELPER_MBR, + 0xff, + 0xcd, + DOS_HELPER_INT, + /* + * This is an instruction that we never execute and is present only to + * convince Norton Disk Doctor that we are a valid mbr + + int 0x13 + */ + 0xcd, + 0x13, +}; +static_assert(sizeof(mbr_boot_code) < PART_INFO_START, + "mbr_boot_code size is incorrect"); + +static int disks_initiated = 0; +struct disk disktab[MAX_FDISKS]; +struct disk hdisktab[MAX_HDISKS]; + +#define FDISKS config.fdisks +#define HDISKS config.hdisks + +#if 1 +# define FLUSHDISK(dp) flush_disk(dp) +#else +# define FLUSHDISK(dp) if (dp && dp->removable && !config.fastfloppy) \ + ioctl(dp->fdesc, FDFLUSH, 0) +#endif + +static void flush_disk(struct disk *dp) +{ + if (dp && dp->removable && dp->fdesc >= 0) { + if (dp->type == IMAGE || (dp->type == FLOPPY && !config.fastfloppy)) { + close(dp->fdesc); + dp->fdesc = -1; + } else { + fsync(dp->fdesc); + } + } +} + +struct disk_fptr { + void (*autosense) (struct disk *); + void (*setup) (struct disk *); +}; + +static void image_auto(struct disk *); + +static void hdisk_auto(struct disk *); +static void hdisk_setup(struct disk *); + +static void floppy_auto(struct disk *); +static void floppy_setup(struct disk *); + +static void partition_auto(struct disk *); + +static void dir_auto(struct disk *); +static void dir_setup(struct disk *); + +static void MBR_setup(struct disk *); +static void VBR_setup(struct disk *); + +static struct disk_fptr disk_fptrs[NUM_DTYPES] = +{ + {image_auto, MBR_setup}, + {hdisk_auto, hdisk_setup}, + {floppy_auto, floppy_setup}, + {partition_auto, VBR_setup}, + {dir_auto, dir_setup} +}; + +const char *disk_t_str(disk_t t) { + static char tmp[32]; + + switch (t) { + case NODISK: + return "None"; + case IMAGE: + return "Image"; + case HDISK: + return "Hard Disk"; + case FLOPPY: + return "Floppy"; + case PARTITION: + return "Partition"; + case DIR_TYPE: + return "Directory"; + default: + sprintf(tmp, "Unknown Type %d", t); + return tmp; + } +} + +const char *floppy_t_str(floppy_t t) { + static char tmp[32]; + + switch (t) { + case FIVE_INCH_360KFLOP: + return "5 1/4 inch 360Kb"; + case FIVE_INCH_FLOPPY: + return "5 1/4 inch 1.2Mb"; + case THREE_INCH_720KFLOP: + return "3 1/2 inch 720Kb"; + case THREE_INCH_FLOPPY: + return "3 1/2 inch 1.44Mb"; + case THREE_INCH_2880KFLOP: + return "3 1/2 inch 2.88Mb"; + default: + sprintf(tmp, "Unknown Type %d", t); + return tmp; + } +} + +static void dump_disk_blks(unsigned tb, int count, int ssiz) +{ + static char buf[80], cbuf[20]; + int a,i,j; + char *p; + unsigned char c; + unsigned int q; + + q=tb; a=0; + while (count--) { + for (i=0; i < (ssiz>>4); i++) { + p=buf; + p+=sprintf(p,"%04x: ",a); + for (j=0; j<16; j++) { + c=READ_BYTE(q); + p+=sprintf(p,"%02x ",c); + cbuf[j] = (isprint(c)? c:'.'); + q++; + } + cbuf[16]=0; a+=16; + d_printf("%s %s\n",buf,cbuf); + } + d_printf("\n"); + } +} + +int read_mbr(const struct disk *dp, unsigned buffer) +{ + /* copy the MBR... */ + e_invalidate(buffer, sizeof(dp->part_info.mbr)); + memcpy_2dos(buffer, &dp->part_info.mbr, sizeof(dp->part_info.mbr)); + return sizeof(dp->part_info.mbr); +} + +/* read_sectors + * + * okay, here's the purpose of this: to handle reads orthogonally across + * all disk types. The tricky one is PARTITION, which looks like this: + * + * |||..........,,,###################################### + * + * | adds up to 1 sector (512 bytes) of Master Boot Record (MBR) + * THIS IS KEPT IN MEMORY, and is the file + * /partition.. + * + * . is any number of sectors before the desired partition start address. + * THESE SECTORS ARE NOT EMULATED, BUT MERELY SKIPPED + * + * , is some number of sectors "before" the partition. These sectors + * occur after the partition start address. + * THESE SECTORS ARE NOT EMULATED, BUT MERELY SKIPPED + * I might not need to skip these, but it seems to work... + * + * read_sectors should be able to handle a read which crosses any number + * of boundaries by memcpy()ing, skipping, and read()ing the appropriate + * combination. + */ + +static off_t calc_pos(const struct disk *dp, int64_t sector) +{ + off_t pos; + + if (dp->type == PARTITION || dp->type == DIR_TYPE) + sector -= dp->start; + pos = sector * SECTOR_SIZE; + if (pos >= 0) + pos += dp->header; + return pos; +} + +int +read_sectors(const struct disk *dp, unsigned buffer, uint64_t sector, + long count) +{ + off_t pos; + long already = 0; + long tmpread; + + if ( (sector + count - 1) >= dp->num_secs) { + d_printf("Sector not found, read_sectors!\n"); + show_regs(); + return -DERR_NOTFOUND; + } + + pos = calc_pos(dp, sector); + d_printf("DISK: %s: Trying to read %ld sectors at LBA %"PRIu64"", + dp->dev_name,count,sector); + d_printf("%+"PRIi64" at pos %"PRIi64"\n", dp->header, pos); + + /* reads beginning before that actual disk/file */ + if (pos < 0 && count > 0) { + int readsize = count * SECTOR_SIZE; + int mbroff = pos + dp->start * SECTOR_SIZE; + int mbrread = 0; + + if (!(dp->type == PARTITION || dp->type == DIR_TYPE)) { + error("negative offset on non-partition disk type\n"); + return -DERR_NOTFOUND; + } + + readsize = readsize > -pos ? -pos : readsize; + + /* + * this will end up pretending to read the requested data, but will + * only read as much as can be read from the fake MBR in memory + * and zero the rest + */ + + /* copy the MBR... */ + if(mbroff < sizeof(dp->part_info.mbr)) { + mbrread = sizeof(dp->part_info.mbr) - mbroff; + mbrread = mbrread > readsize ? readsize : mbrread; + e_invalidate(buffer, mbrread); + memcpy_2dos(buffer, (const uint8_t *)&dp->part_info.mbr + mbroff, mbrread); + d_printf("read 0x%lx bytes from MBR, ofs = 0x%lx (0x%lx bytes left)\n", + (unsigned long) mbrread, (unsigned long) mbroff, (unsigned long) (readsize - mbrread) + ); + } + + /* ... and zero the rest */ + if(readsize > mbrread) { + MEMSET_DOS(buffer + mbrread, 0, readsize - mbrread); + d_printf("emulated reading 0x%lx bytes, ofs = 0x%lx\n", + (unsigned long) (readsize - mbrread), (unsigned long) (mbroff + mbrread) + ); + } + + if(readsize == count * SECTOR_SIZE) { + d_printf(" got entire read done from memory. off:%d, count:%d\n", mbroff, readsize); + return readsize; + } + + buffer += readsize; + pos += readsize + dp->header; + already += readsize; + } + + if(dp->type == DIR_TYPE && dp->fatfs) { + /* this should not never happen */ + if(pos % SECTOR_SIZE || already % SECTOR_SIZE) { + error("illegal read offset for %s\n", dp->dev_name); + return -DERR_NOTFOUND; + } + tmpread = fatfs_read(dp->fatfs, buffer, pos / SECTOR_SIZE, count - already / SECTOR_SIZE); + if(tmpread == -1) return -DERR_NOTFOUND; + if(tmpread == -2) return -DERR_ECCERR; + tmpread *= SECTOR_SIZE; + } + else { + if(pos != lseek(dp->fdesc, pos, SEEK_SET)) { + error("Sector not found in read_sector, error = %s!\n", strerror(errno)); + return -DERR_NOTFOUND; + } + tmpread = dos_read(dp->fdesc, buffer, count * SECTOR_SIZE - already); + } + + if(tmpread != -1) { + if (debug_level('d') > 8) dump_disk_blks(buffer, count - already / SECTOR_SIZE, SECTOR_SIZE); + return tmpread + already; + } + else { +/**/ dbug_printf("disks.c: read failed\n"); + return -DERR_ECCERR; + } +} + +int +write_sectors(struct disk *dp, unsigned buffer, uint64_t sector, + long count) +{ + off_t pos; + long tmpwrite, already = 0; + + if ( (sector + count - 1) >= dp->num_secs) { + error("Sector not found, write_sectors!\n"); + show_regs(); + return -DERR_NOTFOUND; + } + + if (dp->rdonly) { + d_printf("ERROR: write to readonly disk %s\n", dp->dev_name); +#if 0 + LO(ax) = 0; /* no sectors written */ + CARRY; /* error */ + HI(ax) = 0xcc; /* write fault */ +#endif + return -DERR_WRITEFLT; + } + + pos = calc_pos(dp, sector); + d_printf("DISK: %s: Trying to write %ld sectors at LBA %"PRIu64"", + dp->dev_name,count,sector); + d_printf(" at pos %"PRIi64"\n", pos); + + /* + * writes outside the partition (before the actual disk/file) are ignored + */ + if (pos < 0 && count > 0) { + long writesize = count * SECTOR_SIZE; + + if(!(dp->type == PARTITION || dp->type == DIR_TYPE)) { + error("negative offset on non-partition disk type\n"); + return -DERR_NOTFOUND; + } + + writesize = writesize > -pos ? -pos : writesize; + + d_printf("emulated writing 0x%lx bytes, ofs = 0x%lx (0x%lx left)\n", + writesize, (long) (pos - dp->header), count * SECTOR_SIZE - writesize + ); + + if(writesize == count * SECTOR_SIZE) return writesize; + + buffer += writesize; + pos += writesize + dp->header; + already += writesize; + } + + if(dp->type == DIR_TYPE && dp->fatfs) { + /* this should not never happen */ + if(pos % SECTOR_SIZE || already % SECTOR_SIZE) { + error("illegal write offset for %s\n", dp->dev_name); + return -DERR_NOTFOUND; + } + tmpwrite = fatfs_write(dp->fatfs, buffer, pos / SECTOR_SIZE, count - already / SECTOR_SIZE); + if(tmpwrite == -1) return -DERR_NOTFOUND; + if(tmpwrite == -1) return -DERR_WRITEFLT; + tmpwrite *= SECTOR_SIZE; + } + else { + if(pos != lseek(dp->fdesc, pos, SEEK_SET)) { + error("Sector not found in write_sector!\n"); + return -DERR_NOTFOUND; + } + tmpwrite = dos_write(dp->fdesc, buffer, count * SECTOR_SIZE - already); + } + + /* this should make floppies a little safer...I would as soon use the + * O_SYNC flag, but we don't have it. fsync() should be in the kernel + * soon, I think, thanks to Stephen Tweedie. + * Also look into detecting floppy change so we can close/reopen + * it. perhaps the FDFLUSH ioctl()? + */ + + FLUSHDISK(dp); + + return tmpwrite + already; +} + +static int set_floppy_chs_by_type(floppy_t t, struct disk *dp) { + switch (t) { + case THREE_INCH_2880KFLOP: + dp->heads = 2; + dp->tracks = 80; + dp->sectors = 5760/80/2; + break; + case THREE_INCH_FLOPPY: + dp->heads = 2; + dp->tracks = 80; + dp->sectors = 2880/80/2; + break; + case THREE_INCH_720KFLOP: + dp->heads = 2; + dp->tracks = 80; + dp->sectors = 1440/80/2; + break; + case FIVE_INCH_FLOPPY: + dp->heads = 2; + dp->tracks = 80; + dp->sectors = 2400/80/2; + break; + case FIVE_INCH_360KFLOP: + dp->heads = 2; + dp->tracks = 40; + dp->sectors = 720/40/2; + break; + default: + return 0; + } + return 1; +} + +static int set_floppy_chs_by_size(off_t s, struct disk *dp) { + switch (s) { + case 2949120: // 2.88M 3 1/2 inches + dp->tracks = 80; + dp->heads = 2; + dp->sectors = 36; + break; + case 1474560: // 1.44M 3 1/2 inches + dp->tracks = 80; + dp->heads = 2; + dp->sectors = 18; + break; + case 737280: // 720K 3 1/2 inches + dp->tracks = 80; + dp->heads = 2; + dp->sectors = 9; + break; + case 1228800: // 1.2M 5 1/4 inches + dp->tracks = 80; + dp->heads = 2; + dp->sectors = 15; + break; + case 368640: // 360K 5 1/4 inches + dp->tracks = 40; + dp->heads = 2; + dp->sectors = 9; + break; + case 184320: // 180K 5 1/4 inches + dp->tracks = 40; + dp->heads = 1; + dp->sectors = 9; + break; + case 163840: // 160K 5 1/4 inches + dp->tracks = 40; + dp->heads = 1; + dp->sectors = 8; + break; + default: + return 0; + } + return 1; +} + +#ifdef DISK_DEBUG + +static void print_bpb(struct on_disk_bpb *bpb) +{ + d_printf(" bpb\n"); + d_printf(" bytes_per_sector %u\n", bpb->bytes_per_sector); + d_printf(" sectors_per_cluster %u\n", bpb->sectors_per_cluster); + d_printf(" reserved_sectors %u\n", bpb->reserved_sectors); + d_printf(" num_fats %u\n", bpb->num_fats); + d_printf(" num_root_entries %u\n", bpb->num_root_entries); + d_printf(" num_sectors_small %u\n", bpb->num_sectors_small); + d_printf(" media_type %x\n", bpb->media_type); + d_printf(" sectors_per_fat %u\n", bpb->sectors_per_fat); + d_printf(" sectors_per_track %u\n", bpb->sectors_per_track); + d_printf(" num_heads %u\n", bpb->num_heads); + // assume v331+ + if (bpb->num_sectors_small == 0) { + d_printf(" v331_400_hidden_sectors %u\n", bpb->v331_400_hidden_sectors); + d_printf(" v331_400_num_sectors_large %u\n", bpb->v331_400_num_sectors_large); + } + // assume v340+ + if (bpb->v340_400_signature == BPB_SIG_V340 || bpb->v340_400_signature == BPB_SIG_V400) { + d_printf(" v340_400_drive_number %x\n", bpb->v340_400_drive_number); + d_printf(" v340_400_flags %x\n", bpb->v340_400_flags); + d_printf(" v340_400_signature %x\n", bpb->v340_400_signature); + d_printf(" v340_400_serial_number %x\n", bpb->v340_400_serial_number); + } + + if (bpb->v340_400_signature == BPB_SIG_V400) { + d_printf(" v400_vol_label '%.11s'\n", bpb->v400_vol_label); + d_printf(" v400_fat_type '%.8s'\n", bpb->v400_fat_type); + } +} + +static void print_disk_structure(struct disk *dp) +{ + d_printf(" disk structure\n"); + d_printf(" rdonly=%d\n", dp->rdonly); + d_printf(" boot=%d\n", dp->boot); + d_printf(" sectors=%d(0x%x)\n", dp->sectors, dp->sectors); + d_printf(" heads=%d(0x%x)\n", dp->heads, dp->heads); + d_printf(" tracks=%d(0x%x)\n", dp->tracks, dp->tracks); + d_printf(" start=%lu(0x%lx)\n", dp->start, dp->start); + d_printf(" num_secs=%"PRIu64"(0x%"PRIx64")\n", dp->num_secs, dp->num_secs); + d_printf(" drive_num=0x%02x\n", dp->drive_num); + d_printf(" header=%lld\n", (long long int)dp->header); +} + +#else +#define print_bpb(x) +#define print_disk_structure(x) +#endif + +static void print_partition_entry(struct on_disk_partition *p) +{ + d_printf(" partition entry\n"); + d_printf(" beg head %u, sec %u, cyl %u / end head %u, sec %u, cyl %u\n", + p->start_head, p->start_sector, + PTBL_HL_GET(p, start_track), + p->end_head, p->end_sector, + PTBL_HL_GET(p, end_track)); + d_printf(" pre_secs %u, num_secs %u(0x%x)\n", + p->num_sect_preceding, + p->num_sectors, p->num_sectors); + d_printf(" type 0x%02x, bootflag 0x%02x\n", p->OS_type, p->bootflag); +} + +static void hdisk_auto(struct disk *dp) +{ +#ifdef __linux__ + struct hd_geometry geo; + unsigned long size32; + uint64_t size64; + unsigned char tmp_mbr[SECTOR_SIZE]; + + /* No point in trying HDIO_GETGEO_BIG, as that is already deprecated again by now */ + if (ioctl(dp->fdesc, HDIO_GETGEO, &geo) < 0) { + error("can't get GEO of %s: %s\n", dp->dev_name, + strerror(errno)); + leavedos(21); + } + else { + /* ignore cylinders from HDIO_GETGEO: they are truncated! */ + dp->sectors = geo.sectors; + dp->heads = geo.heads; + dp->start = geo.start; + } + /* BLKGETSIZE64 returns the number of bytes into a uint64_t */ + if (ioctl(dp->fdesc, BLKGETSIZE64, &size64) == 0) { + unsigned int sector_size; + if (ioctl(dp->fdesc, BLKSSZGET, §or_size) != 0) { + error("Hmm... BLKSSZGET failed (not fatal): %s\n", strerror(errno)); + sector_size = SECTOR_SIZE; + } + if (size64 & (sector_size - 1) ) { + error("hdisk size is not sector-aligned (%"PRIu64" bytes), truncated!\n", + size64 & (sector_size - 1) ); + } + /* size64 += sector_size - 1; */ /* round up */ + /* round down! */ + size64 /= sector_size; /* convert to sectors */ + dp->num_secs = size64; + } + else + { + // BLKGETSIZE is always there + /* BLKGETSIZE returns the number of *sectors* into a uint32_t */ + if (ioctl(dp->fdesc, BLKGETSIZE, &size32) == 0) + dp->num_secs = size32; + else { + perror("Error getting capacity using BLKGETSIZE and BLKGETSIZE64. This is fatal :-(\n"); + leavedos(1); + } + } + + if (dp->type == HDISK && read(dp->fdesc, tmp_mbr, SECTOR_SIZE) == SECTOR_SIZE + && tmp_mbr[SECTOR_SIZE - 2] == 0x55 && tmp_mbr[SECTOR_SIZE - 1] == 0xaa) { + /* see also fdisk.c in util-linux: HDIO_GETGEO is not reliable; we should + use the values in the partion table if possible */ + int pnum; + unsigned int h = 0, s = 0, end_head, end_sec; + struct on_disk_partition *part = (struct on_disk_partition *)(tmp_mbr + 0x1be); + for (pnum = 1; pnum <= 4; pnum++) { + unsigned end_cyl; + /* sys id should be nonzero */ + if (part->OS_type == 0) continue; + end_head = part->end_head; + end_sec = part->end_sector; + end_cyl = PTBL_HL_GET(part, end_track); + if (h == 0) { + unsigned endseclba; + h = end_head + 1; + s = end_sec; + /* check if CHS matches LBA */ + endseclba = part->num_sect_preceding + part->num_sectors; + if (end_cyl < 1023 && end_cyl*h*s + end_head*s + end_sec != endseclba) { + /* if mismatch, try with s=63 (e.g., a 4GB USB key with 978/128/63) */ + unsigned h2 = endseclba / (end_cyl*63); + if (end_cyl*h2*63 + end_head*63 + end_sec == endseclba) { + h = h2; + s = 63; + } + } + } else if ((end_head != h || end_sec != s) && s != 63) { + h = 0; + break; + } + } + if (h > 1 && s > 0) { + dp->sectors = s; + dp->heads = h; + } + } + + dp->tracks = dp->num_secs / (dp->heads * dp->sectors); + d_printf("HDISK auto disk %s; h=%d, s=%d, t=%d, start=%ld\n", + dp->dev_name, dp->heads, dp->sectors, dp->tracks, dp->start); +#endif +} + +static void hdisk_setup(struct disk *dp) +{ + d_printf("HDISK setup\n"); +} + +static void floppy_auto(struct disk *dp) +{ + d_printf("FLOPPY auto\n"); +} + +static void floppy_setup(struct disk *dp) +{ + d_printf("FLOPPY setup\n"); +} + +static void dir_auto(struct disk *dp) +{ + if (dp->floppy) { + if (!set_floppy_chs_by_type(dp->default_cmos, dp)) + d_printf("DIR: Invalid floppy disk type (%d)\n", dp->default_cmos); + else + d_printf("DIR: Selected floppy disk type (%s)\n", + floppy_t_str(dp->default_cmos)); + dp->start = 0; + dp->rdonly = 1; // should be for HDD too, but... + } else { + /* + * We emulate an entire disk with 1 partition starting at t/h/s 0/1/1. + * You are free to change the geometry (e.g. to change the partition size). + */ + switch (dp->hdtype) { + case 0: + dp->tracks = 255; + dp->heads = 255; + dp->sectors = 63; + break; + case 1: + dp->tracks = 306; + dp->heads = 4; + dp->sectors = 17; + d_printf("DIR: Forcing IBM disk type 1\n"); + break; + case 2: + dp->tracks = 615; + dp->heads = 4; + dp->sectors = 17; + d_printf("DIR: Forcing IBM disk type 2\n"); + break; + case 9: + dp->tracks = 900; + dp->heads = 15; + dp->sectors = 17; + d_printf("DIR: Forcing IBM disk type 9\n"); + break; + default: + error("DIR: Invalid disk type (%d)\n", dp->hdtype); + config.exitearly = 1; + break; + } + dp->start = dp->sectors; + } + + dp->num_secs = (unsigned long long)dp->tracks * dp->heads * dp->sectors; + + dp->header = 0; + + d_printf( + "DIR auto disk %s; h=%d, s=%d, t=%d, start=%ld\n", + dp->dev_name, dp->heads, dp->sectors, dp->tracks, dp->start + ); +} + +static struct on_disk_partition build_pi(struct disk *dp) +{ + struct on_disk_partition p; + int num_sectors = dp->tracks * dp->heads * dp->sectors - dp->start; + + p.bootflag = PART_BOOT; + p.start_head = 1; + p.start_sector = 1; + PTBL_HL_SET(&p, start_track, 0); + if (num_sectors <= 4078*8) + p.OS_type = 0x01; + else if (num_sectors < (1 << 16)) + p.OS_type = 0x04; + else + p.OS_type = 0x06; + p.end_head = dp->heads - 1; + p.end_sector = dp->sectors; + PTBL_HL_SET(&p, end_track, dp->tracks - 1); + p.num_sect_preceding = dp->start; + p.num_sectors = num_sectors; + return p; +} + +static void dir_setup(struct disk *dp) +{ + int i = strlen(dp->dev_name); + while (--i >= 0) + if (dp->dev_name[i] == '/') + dp->dev_name[i] = 0; + else + break; + + if (!dp->floppy) { + dp->part_info.number = 1; + memcpy(&dp->part_info.mbr.code, &mbr_boot_code, sizeof(mbr_boot_code)); + dp->part_info.mbr.partition[0] = build_pi(dp); + dp->part_info.mbr.signature = MBR_SIG; + + d_printf("DIR setup disk %s:\n", dp->dev_name); + print_partition_entry(&dp->part_info.mbr.partition[0]); + print_disk_structure(dp); + } + + dp->fatfs = NULL; +} + +static void image_auto(struct disk *dp) +{ + uint32_t magic; + uint64_t filesize; + struct stat st; + union { + char buf[512]; + struct image_header header; + struct on_disk_mbr mbr; + struct on_disk_vbr vbr; + } sect0; + static_assert(sizeof(sect0) == 512, "bad sect0 size"); + + d_printf("IMAGE auto-sensing\n"); + + if (dp->fdesc == -1) { + warn("WARNING: image filedesc not open\n"); + dp->fdesc = open(dp->dev_name, (dp->rdonly ? O_RDONLY : O_RDWR) | O_CLOEXEC); + if (dp->fdesc == -1) { + /* We should check whether errno is EROFS, but if not the next open will + fail again and the following lseek will throw us out of dos. So we win + a very tiny amount of time in case it works. Also, if for some reason + this does work (should be impossible), we can at least try to + continue. (again how sick can you get :-) ) + */ + dp->fdesc = open(dp->dev_name, O_RDONLY | O_CLOEXEC); + dp->rdonly = 1; + } + } + + if (dp->fdesc == -1) { + warn("WARNING: image filedesc still not open\n"); + leavedos(19); + return; + } + + if (dp->floppy) { + + if (fstat(dp->fdesc, &st) < 0) { + d_printf("IMAGE auto couldn't stat disk file %s\n", dp->dev_name); + leavedos(19); + return; + } + if (!(set_floppy_chs_by_size(st.st_size, dp) || + set_floppy_chs_by_type(dp->default_cmos, dp)) ){ + d_printf("IMAGE auto set floppy geometry %s\n", dp->dev_name); + leavedos(19); + return; + } + dp->start = 0; + dp->num_secs = (unsigned long long)dp->tracks * dp->heads * dp->sectors; + + d_printf("IMAGE auto floppy %s; t=%d, h=%d, s=%d\n", + dp->dev_name, dp->tracks, dp->heads, dp->sectors); + return; + } + + // Hard disk image + + lseek(dp->fdesc, 0, SEEK_SET); + if (RPT_SYSCALL(read(dp->fdesc, §0.buf, sizeof(sect0))) != sizeof(sect0)) { + error("could not read sector 0 in image_init\n"); + leavedos(19); + } + + memcpy(&magic, sect0.header.sig, 4); + if (strncmp(sect0.header.sig, IMAGE_MAGIC, IMAGE_MAGIC_SIZE) == 0 || + (magic == DEXE_MAGIC) ) { + d_printf(" Dosemu header found, image will contain whole disk\n"); + dp->heads = sect0.header.heads; + dp->sectors = sect0.header.sectors; + dp->tracks = sect0.header.cylinders; + dp->header = sect0.header.header_end; + dp->num_secs = (unsigned long long)dp->tracks * dp->heads * dp->sectors; + + } else if (sect0.vbr.signature == VBR_SIG && + sect0.vbr.u.bpb.media_type == 0xf8 && sect0.vbr.u.bpb.num_fats == 2) { /* VBR */ + d_printf(" VBR found, image is a filesystem\n"); + + if (sect0.vbr.u.bpb.num_sectors_small == 0 && ( + sect0.vbr.u.bpb.v340_400_signature == BPB_SIG_V340 || + sect0.vbr.u.bpb.v340_400_signature == BPB_SIG_V400)) + dp->num_secs = sect0.vbr.u.bpb.v331_400_num_sectors_large; + else if (sect0.vbr.u.bpb7.num_sectors_small == 0 && ( + sect0.vbr.u.bpb7.signature == BPB_SIG_V7_SHORT || + sect0.vbr.u.bpb7.signature == BPB_SIG_V7_LONG)) + dp->num_secs = sect0.vbr.u.bpb7.num_sectors_large; + else + dp->num_secs = sect0.vbr.u.bpb.num_sectors_small; + dp->heads = sect0.vbr.u.bpb.num_heads; + dp->sectors = sect0.vbr.u.bpb.sectors_per_track; + + // Must also set these for build_pi input in setup phase + dp->start = dp->sectors; // one cylinder for mbr + alignment + dp->num_secs += dp->start; + dp->tracks = dp->num_secs / dp->sectors / dp->heads; + dp->header = 0; + + dp->type = PARTITION; // VBR_setup() will subsequently be run + dp->part_image = 1; + + } else if (sect0.mbr.signature == MBR_SIG) { /* MBR */ + d_printf(" MBR found, image contains partitions\n"); + + filesize = lseek(dp->fdesc, 0, SEEK_END); + if (filesize & (SECTOR_SIZE - 1) ) { + error("hdimage size is not sector-aligned (%"PRIu64" bytes), truncated!\n", + filesize & (SECTOR_SIZE - 1) ); + } + dp->num_secs = (filesize /* + SECTOR_SIZE - 1 */ ) / SECTOR_SIZE; + dp->heads = 255; + dp->sectors = 63; + if (dp->num_secs % (dp->heads * dp->sectors) ) { + d_printf("hdimage size is not cylinder-aligned (%"PRIu64" sectors), truncated!\n", + dp->num_secs % (dp->heads * dp->sectors) ); + } + dp->tracks = (dp->num_secs /* + (dp->heads * dp->sectors - 1) */ ) + / (dp->heads * dp->sectors); + /* round down number of sectors and number of tracks */ + dp->header = 0; + + } else { + error("IMAGE %s header lacks magic string - cannot autosense!\n", + dp->dev_name); + leavedos(20); + } + + d_printf("IMAGE auto disk %s; num_secs=%"PRIu64", t=%d, h=%d, s=%d, off=%ld\n", + dp->dev_name, dp->num_secs, + dp->tracks, dp->heads, dp->sectors, + (long) dp->header); +} + +static void MBR_setup(struct disk *dp) +{ + ssize_t rd; + int ret, i; + + if (dp->floppy) { + return; + } + + /* Disk / Image already has MBR */ + dp->part_info.number = 1; + ret = lseek(dp->fdesc, dp->header, SEEK_SET); + if (ret == -1) { + error("MBR_setup: Can't seek '%s'\n", dp->dev_name); + leavedos(35); + } + rd = read(dp->fdesc, &dp->part_info.mbr, sizeof(dp->part_info.mbr)); + if (rd != sizeof(dp->part_info.mbr)) { + error("MBR_setup: Can't read MBR from '%s'\n", dp->dev_name); + leavedos(35); + } + + d_printf("MBR_setup: %s:\n", dp->dev_name); + + for (i = 0; i < 4; i++) { + if (dp->part_info.mbr.partition[i].OS_type) + print_partition_entry(&dp->part_info.mbr.partition[i]); + } + + print_disk_structure(dp); +} + +static void partition_auto(struct disk *dp) +{ + struct on_disk_vbr vbr; + + d_printf("PARTITION auto\n"); + + if (!dp->part_image) { +#ifdef __linux__ + unsigned long length; + if (ioctl(dp->fdesc, BLKGETSIZE, &length)) { + error("calling ioctl BLKGETSIZE for PARTITION %s\n", dp->dev_name); + leavedos(22); + } + dp->num_secs = length; +#else + error("no support for block device PARTITION on non linux platforms\n"); + leavedos(22); +#endif + } else { + struct stat sb; + if (fstat(dp->fdesc, &sb)) { + error("calling ioctl FSTAT for PARTITION(image) %s\n", dp->dev_name); + leavedos(22); + } + dp->num_secs = sb.st_size / SECTOR_SIZE; + } + + lseek(dp->fdesc, 0, SEEK_SET); + if (RPT_SYSCALL(read(dp->fdesc, &vbr, sizeof(vbr))) != sizeof(vbr)) { + error("could not read first sector PARTITION %s\n", dp->dev_name); + leavedos(22); + } + + if (vbr.signature == VBR_SIG && + vbr.u.bpb.media_type == 0xf8 && vbr.u.bpb.num_fats == 2) { + + d_printf("VBR found, we have a filesystem on PARTITION %s\n", dp->dev_name); + + if (vbr.u.bpb.num_sectors_small == 0 && ( + vbr.u.bpb.v340_400_signature == BPB_SIG_V340 || + vbr.u.bpb.v340_400_signature == BPB_SIG_V400)) + dp->num_secs = vbr.u.bpb.v331_400_num_sectors_large; + else if (vbr.u.bpb7.num_sectors_small == 0 && ( + vbr.u.bpb7.signature == BPB_SIG_V7_SHORT || + vbr.u.bpb7.signature == BPB_SIG_V7_LONG)) + dp->num_secs = vbr.u.bpb7.num_sectors_large; + else + dp->num_secs = vbr.u.bpb.num_sectors_small; + dp->heads = vbr.u.bpb.num_heads; + dp->sectors = vbr.u.bpb.sectors_per_track; + + } else { + dp->heads = 254; + dp->sectors = 255; + } + + // Must also set these for build_pi input in setup phase + dp->start = dp->sectors; // one cylinder for mbr + alignment + dp->num_secs += dp->start; + dp->tracks = dp->num_secs / dp->sectors / dp->heads; + dp->header = 0; +} + +static void VBR_setup(struct disk *dp) +{ + struct on_disk_vbr vbr; + uint8_t typ = 0; + + d_printf("VBR setup for %s\n", dp->dev_name); + + if (dp->floppy) { + return; + } + + lseek(dp->fdesc, 0, SEEK_SET); + if (RPT_SYSCALL(read(dp->fdesc, &vbr, sizeof(vbr))) != sizeof(vbr)) { + d_printf(" BPB could not be read\n"); + } else { + if (vbr.u.bpb7.num_sectors_small == 0 && ( + vbr.u.bpb7.signature == BPB_SIG_V7_SHORT || + vbr.u.bpb7.signature == BPB_SIG_V7_LONG)) + typ = 0x0b; + print_bpb(&vbr.u.bpb); + } + + dp->part_info.number = 1; + memcpy(&dp->part_info.mbr.code, &mbr_boot_code, sizeof(mbr_boot_code)); + dp->part_info.mbr.partition[0] = build_pi(dp); + if (typ) + dp->part_info.mbr.partition[0].OS_type = typ; + dp->part_info.mbr.signature = MBR_SIG; + + print_partition_entry(&dp->part_info.mbr.partition[0]); + print_disk_structure(dp); +} + +void +disk_close(void) +{ + struct disk *dp; + + if (!disks_initiated) return; /* just to be safe */ + for (dp = disktab; dp < &disktab[FDISKS]; dp++) { + if (dp->removable && dp->fdesc >= 0) { + d_printf("DISK: Closing disk %s\n",dp->dev_name); + (void) close(dp->fdesc); + dp->fdesc = -1; + } + } +} + +static void disk_sync(void) +{ + struct disk *dp; + + if (!disks_initiated) return; /* just to be safe */ + for (dp = disktab; dp < &disktab[FDISKS]; dp++) { + if (dp->removable && dp->fdesc >= 0) { + d_printf("DISK: Syncing disk %s\n",dp->dev_name); + (void) fsync(dp->fdesc); + } + } +} + + +void +disk_open(struct disk *dp) +{ + if (dp == NULL || dp->fdesc >= 0) + return; + + dp->fdesc = SILENT_DOS_SYSCALL(open(dp->type == DIR_TYPE ? + "/dev/null" : dp->dev_name, (dp->rdonly ? O_RDONLY : + O_RDWR) | O_CLOEXEC)); + if (dp->type == IMAGE || dp->type == DIR_TYPE) + return; + +#ifdef __linux__ + /* FIXME: + * Why the hell was the below handling restricted to non-removeable disks? + * This made opening writeprotected floppies impossible :-( + * -- Hans, 990112 + */ + if ( /*!dp->removable &&*/ (dp->fdesc < 0)) { + if (errno == EROFS || errno == ENODEV) { + dp->fdesc = DOS_SYSCALL(open(dp->dev_name, O_RDONLY | O_CLOEXEC)); + if (dp->fdesc < 0) { + error("ERROR: (disk) can't open %s for read nor write: %s\n", dp->dev_name, strerror(errno)); + /* In case we DO get more clever, we want to share that code */ + } else { + dp->rdonly = 1; + d_printf("(disk) can't open %s for read/write. Readonly used.\n", dp->dev_name); + } + } else { + d_printf("ERROR: (disk) can't open %s: %s\n", dp->dev_name, strerror(errno)); + } + } + +{ + struct floppy_struct fl; + +#if 1 + /* NOTE: Starting with linux 1.3.100 the floppy driver has changed + * so that it no longer returns from the following ioctl without + * getting interrupted by SIGALARM (-EINTR). Also a retry does not help, + * because this one gets interrupt again (and again). + * To overcome this problem we temporary switch off the timer + * during the ioctl. (well, not what we really like) + * ( 19 May 1996, Hans Lermen ) */ + int res=0; + + sigalarm_onoff(0); + res = ioctl(dp->fdesc, FDGETPRM, &fl); + sigalarm_onoff(1); + + if (res == -1) { +#else + if (ioctl(dp->fdesc, FDGETPRM, &fl) == -1) { +#endif + if ((dp->fdesc == -1) || (errno == ENODEV)) { /* no disk available */ + dp->sectors = 0; + dp->heads = 0; + dp->tracks = 0; + dp->num_secs = 0; + return; + } + error("can't get floppy parameter of %s (%s)\n", dp->dev_name, strerror(errno)); + fatalerr = 5; + return; + } + d_printf("FLOPPY %s h=%d, s=%d, t=%d\n", dp->dev_name, fl.head, fl.sect, fl.track); + dp->sectors = fl.sect; + dp->heads = fl.head; + dp->tracks = fl.track; + dp->num_secs = (unsigned long long)dp->tracks * dp->heads * dp->sectors; + DOS_SYSCALL(ioctl(dp->fdesc, FDMSGOFF, 0)); +} +#endif +} + +void +disk_close_all(void) +{ + struct disk *dp; + int i; + + if (!disks_initiated) + return; /* prevent idiocy */ + + for (dp = disktab; dp < &disktab[FDISKS]; dp++) { + if (dp->fdesc >= 0) { + d_printf("Floppy disk Closing %x\n", dp->fdesc); + (void) close(dp->fdesc); + dp->fdesc = -1; + } + } + FOR_EACH_HDISK(i, { + if(hdisktab[i].type == DIR_TYPE) fatfs_done(&hdisktab[i]); + if (hdisktab[i].fdesc >= 0) { + d_printf("Hard disk Closing %x\n", hdisktab[i].fdesc); + (void) close(hdisktab[i].fdesc); + hdisktab[i].fdesc = -1; + } + }); + disks_initiated = 0; +} + +static Bit8u floppy_DOR = 0xc; + +static Bit8u floppy_io_read(ioport_t port, void *arg) +{ + if (port == 0x3f2) + return floppy_DOR; + return 0xff; +} + +static void floppy_io_write(ioport_t port, Bit8u value, void *arg) +{ + if (port == 0x3f2) { + floppy_DOR = value; + if ((value & 0x30) == 0) + disk_close(); + } +} + +/* + * DANG_BEGIN_FUNCTION disk_init + * + * description: + * Test by opening all floppies/hardrives configured. + * + * DANG_END_FUNCTION + */ +void +disk_init(void) +{ + struct disk *dp; + int i; + + disks_initiated = 1; /* disk_init has been called */ + + if (FDISKS) { + emu_iodev_t io_device; + + io_device.read_portb = floppy_io_read; + io_device.write_portb = floppy_io_write; + io_device.read_portw = NULL; + io_device.write_portw = NULL; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + io_device.handler_name = "Floppy Drive"; + io_device.start_addr = 0x03F0; + io_device.end_addr = 0x03F7; + port_register_handler(io_device, 0); + } + + for (i = 0; i < MAX_FDISKS; i++) { + dp = &disktab[i]; + dp->fdesc = -1; + dp->floppy = 1; + dp->removable = 1; + dp->serial = 0xF10031A0 + dp->drive_num; // sernum must be unique! + } + + for (i = 0; i < MAX_HDISKS; i++) { + dp = &hdisktab[i]; + dp->fdesc = -1; + dp->floppy = 0; + dp->serial = 0x4ADD1B0A + dp->drive_num; // sernum must be unique! + } +} + +static void disk_reset2(void) +{ + struct stat stbuf; + struct disk *dp; + int i; + + /* + * Open floppy disks + */ + for (i = 0; i < FDISKS; i++) { + dp = &disktab[i]; + + if (stat(dp->dev_name, &stbuf) < 0) { + error("can't stat %s\n", dp->dev_name); + config.exitearly = 1; + } + + if (S_ISREG(stbuf.st_mode)) { + d_printf("dev %s is an image\n", dp->dev_name); + dp->type = IMAGE; + } else if (S_ISBLK(stbuf.st_mode)) { + d_printf("dev %s: %#x\n", dp->dev_name, (unsigned) stbuf.st_rdev); + dp->type = FLOPPY; + if (dp->fdesc != -1) + close(dp->fdesc); + dp->fdesc = -1; +#ifdef __linux__ + if ((stbuf.st_rdev & 0xff00) == 0x200) { + d_printf("DISK %s removable\n", dp->dev_name); + } +#endif + } else if (S_ISDIR(stbuf.st_mode)) { + d_printf("dev %s is a directory\n", dp->dev_name); + dp->type = DIR_TYPE; + dp->removable = 0; + } else { + error("dev %s is wrong type\n", dp->dev_name); + config.exitearly = 1; + } + + disk_fptrs[dp->type].autosense(dp); + disk_fptrs[dp->type].setup(dp); + } + + /* + * Open hard disks + */ + FOR_EACH_HDISK(i, { + dp = &hdisktab[i]; + if (dp->fdesc != -1) + close(dp->fdesc); + dp->fdesc = open(dp->type == DIR_TYPE ? "/dev/null" : dp->dev_name, + (dp->rdonly ? O_RDONLY : O_RDWR) | O_CLOEXEC); + if (dp->fdesc < 0) { + if (errno == EROFS || errno == EACCES) { + dp->fdesc = open(dp->dev_name, O_RDONLY | O_CLOEXEC); + if (dp->fdesc < 0) { + error("can't open %s for read nor write: %s\n", dp->dev_name, strerror(errno)); + config.exitearly = 1; + } else { + dp->rdonly = 1; + d_printf("(disk) can't open %s for read/write. Readonly did work though\n", dp->dev_name); + } + } else { + error("can't open %s: #%d - %s\n", dp->dev_name, errno, strerror(errno)); + config.exitearly = 1; + } + } + dp->removable = 0; + + /* HACK: if unspecified geometry (-1) then try to get it from kernel. + May only work on WD compatible disks (MFM/RLL/ESDI/IDE). */ + if (dp->sectors == -1) + disk_fptrs[dp->type].autosense(dp); + + /* do all the necessary dirtiness to get this disk working + * (mostly for the partition type) + */ + disk_fptrs[dp->type].setup(dp); + }); +} + +void disk_reset(void) +{ + struct disk *dp; + int i; + + disk_reset2(); + + subst_file_ext(NULL); + for (dp = disktab; dp < &disktab[FDISKS]; dp++) { + if(dp->type == DIR_TYPE) { + if (dp->fatfs) fatfs_done(dp); + fatfs_init(dp); + } + } + FOR_EACH_HDISK(i, { + if(hdisktab[i].type == DIR_TYPE) { + if (hdisktab[i].fatfs) fatfs_done(&hdisktab[i]); + fatfs_init(&hdisktab[i]); + } + }); +} + +static void hdisk_reset(int num) +{ + int i; + + disk_reset2(); + + subst_file_ext(NULL); + FOR_EACH_HDISK(i, { + if(hdisktab[i].type == DIR_TYPE) { + if (hdisktab[i].fatfs) + fatfs_done(&hdisktab[i]); + } + }); + if (HDISKS > num) + HDISKS = num; + FOR_EACH_HDISK(i, { + if (HDISK_NUM(i) >= num + 2) { + hdisktab[i].drive_num = 0; + continue; + } + if(hdisktab[i].type == DIR_TYPE) + fatfs_init(&hdisktab[i]); + }); +} + +int disk_is_bootable(const struct disk *dp) +{ + switch (dp->type) { + case DIR_TYPE: + return fatfs_is_bootable(dp->fatfs); + case IMAGE: + if (dp->floppy) + return 1; + return dp->part_info.mbr.partition[dp->part_info.number - 1].bootflag == PART_BOOT; + default: // fsck on other types + return 1; + } +} + +int disk_root_contains(const struct disk *dp, int file_idx) +{ + if (dp->type != DIR_TYPE) + return 0; + return fatfs_root_contains(dp->fatfs, file_idx); +} + +int disk_validate_boot_part(struct disk *dp) +{ + int hdtype; + + if (dp->type != DIR_TYPE || dp->floppy) + return 1; + + hdtype = fatfs_get_part_type(dp->fatfs); + if (hdtype == -1) + return 0; + if (!hdtype) + return 1; + + if (dp->hdtype == 0) { /* Unspecified disk type */ + d_printf("DISK: Automatically selecting IBM disk type %i\n", hdtype); + dp->hdtype = hdtype; + dp->sectors = -1; + } + + /* some old DOSes only boot if there are no more than 2 drives */ + d_printf("DISK: Clamping number of hdisks to 2\n"); + hdisk_reset(2); + + return fatfs_is_bootable(dp->fatfs); +} + +static int checkdp(struct disk *disk) +{ + if (disk == NULL) { + d_printf("DISK: null dp\n"); + return 1; + } + else if (disk->fdesc == -1) { + d_printf("DISK: closed disk\n"); + return 1; + } + else + return 0; +} + +int int13(void) +{ + unsigned int disk, head, sect, track, number; + uint64_t number_sectors; + int res; + off_t pos; + unsigned buffer; + struct disk *dp; + int checkdp_val; + unsigned status_addr; + + disk = LO(dx); + if (!(disk & 0x80)) { + status_addr = BIOS_DISK_STATUS; + if (disk >= FDISKS) { + d_printf("INT13: no such fdisk %x\n", disk); + dp = NULL; + } else + dp = &disktab[disk]; + switch (HI(ax)) { + /* NOTE: we use this counter for closing. Also older games seem to rely + * on it. We count it down in INT08 (bios.S) --SW, --Hans, --Bart + */ + case 0: case 2: case 3: case 5: case 10: case 11: case 0x42: case 0x43: + WRITE_BYTE(BIOS_MOTOR_TIMEOUT, 37); /* set timeout to 2 seconds */ + break; + } + } else { + status_addr = BIOS_HDISK_STATUS; + dp = hdisk_find(disk); + if (!dp) + d_printf("INT13: no such hdisk %x\n", disk); + } + + d_printf("INT13: ax=%04x cx=%04x dx=%04x\n", LWORD(eax), LWORD(ecx), LWORD(edx)); + + /* this is a bad hack to ensure that the cached blocks aren't. + * Linux only checks disk change on open() + */ + + switch (HI(ax)) { + case 0: /* init */ + d_printf("DISK %02x init\n", disk); + HI(ax) = DERR_NOERR; + NOCARRY; + break; + + case 1: /* read error code into AH */ + if ((HI(ax) = READ_BYTE(status_addr)) == 0) + NOCARRY; + else + CARRY; + d_printf("DISK error code\n"); + return 1; + + case 2: /* read */ + FLUSHDISK(dp); + disk_open(dp); + checkdp_val = checkdp(dp); + if (!checkdp_val && dp->diskcyl4096 && dp->heads <= 64) + head = HI(dx) & 0x3f; + else + head = HI(dx); + sect = (REG(ecx) & 0x3f) - 1; + /* Note that the unsigned int sect will underflow if cl & 3Fh is 0. + Further on this is detected as a too-large value for the sector. */ + track = (HI(cx)) | + ((REG(ecx) & 0xc0) << 2); + if (!checkdp_val && dp->diskcyl4096 && dp->heads <= 64 && (HI(dx) & 0xc0)) + track |= (HI(dx) & 0xc0) << 4; + buffer = SEGOFF2LINEAR(SREG(es), LWORD(ebx)); + number = LO(ax); + d_printf("DISK %02x read [h:%d,s:%d,t:%d](%d)->%#x (%04x:%04x)\n", + disk, head, sect, track, number, buffer, SREG(es), LWORD(ebx)); + + if (number > I13_MAX_ACCESS) { + error("Too large read, ah=0x02!\n"); + error("DISK %02x read [h:%d,s:%d,t:%d](%d)->%#x (%04x:%04x)\n", + disk, head, sect, track, number, buffer, SREG(es), LWORD(ebx)); + HI(ax) = DERR_BOUNDARY; + CARRY; + break; + } + + if (checkdp_val || head >= dp->heads || + sect >= dp->sectors || track >= dp->tracks) { + d_printf("Sector not found, ah=0x02!\n"); + d_printf("DISK %02x read [h:%d,s:%d,t:%d](%d)->%#x (%04x:%04x)\n", + disk, head, sect, track, number, buffer, SREG(es), LWORD(ebx)); + if (dp) { + d_printf("DISK dev %s GEOM %d heads %d sects %d trk\n", + dp->dev_name, dp->heads, dp->sectors, dp->tracks); + } else { + d_printf("DISK %02x undefined.\n", disk); + } + show_regs(); + HI(ax) = DERR_NOTFOUND; + REG(eflags) |= CF; + break; + } + + res = read_sectors(dp, buffer, + DISK_OFFSET(dp, head, sect, track) / SECTOR_SIZE, + number); + + if (res < 0) { + HI(ax) = -res; + CARRY; + break; + } + else if (res & 511) { /* must read multiple of 512 bytes */ + error("sector_corrupt 1, return = %d!\n", res); + HI(ax) = DERR_BADSEC; /* sector corrupt */ + CARRY; + break; + } + + LWORD(eax) = res >> 9; + REG(eflags) &= ~CF; + R_printf("DISK read @%d/%d/%d (%d) -> %#x OK.\n", + head, track, sect, res >> 9, buffer); + break; + + case 3: /* write */ + FLUSHDISK(dp); + disk_open(dp); + checkdp_val = checkdp(dp); + if (!checkdp_val && dp->diskcyl4096 && dp->heads <= 64) + head = HI(dx) & 0x3f; + else + head = HI(dx); + sect = (REG(ecx) & 0x3f) - 1; + /* Note that the unsigned int sect will underflow if cl & 3Fh is 0. + Further on this is detected as a too-large value for the sector. */ + track = (HI(cx)) | + ((REG(ecx) & 0xc0) << 2); + if (!checkdp_val && dp->diskcyl4096 && dp->heads <= 64 && (HI(dx) & 0xc0)) + track |= (HI(dx) & 0xc0) << 4; + buffer = SEGOFF2LINEAR(SREG(es), LWORD(ebx)); + number = LO(ax); + W_printf("DISK %02x write [h:%d,s:%d,t:%d](%d)->%#x (%04x:%04x)\n", + disk, head, sect, track, number, buffer, SREG(es), LWORD(ebx)); + + if (number > I13_MAX_ACCESS) { + error("Too large write, ah=0x03!\n"); + error("DISK %02x write [h:%d,s:%d,t:%d](%d)->%#x (%04x:%04x)\n", + disk, head, sect, track, number, buffer, SREG(es), LWORD(ebx)); + HI(ax) = DERR_BOUNDARY; + CARRY; + break; + } + + if (checkdp_val || head >= dp->heads || + sect >= dp->sectors || track >= dp->tracks) { + error("Sector not found, ah=0x03!\n"); + error("DISK %02x write [h:%d,s:%d,t:%d](%d)->%#x (%04x:%04x)\n", + disk, head, sect, track, number, buffer, SREG(es), LWORD(ebx)); + if (dp) { + error("DISK dev %s GEOM %d heads %d sects %d trk\n", + dp->dev_name, dp->heads, dp->sectors, dp->tracks); + } else { + error("DISK %02x undefined.\n", disk); + } + HI(ax) = DERR_NOTFOUND; + REG(eflags) |= CF; + break; + } + + if (dp->rdonly) { + W_printf("DISK %02x write protected!\n", disk); + if (dp->floppy) + HI(ax) = DERR_WP; + else + HI(ax) = DERR_WRITEFLT; + REG(eflags) |= CF; + break; + } + + res = write_sectors(dp, buffer, + DISK_OFFSET(dp, head, sect, track) / SECTOR_SIZE, + number); + if (res < 0) { + W_printf("DISK write error: %d\n", -res); + HI(ax) = -res; + CARRY; + break; + } + else if (res & 511) { /* must write multiple of 512 bytes */ + error("Write sector corrupt 2 (wrong size)!\n"); + HI(ax) = DERR_BADSEC; + CARRY; + break; + } + + LWORD(eax) = res >> 9; + REG(eflags) &= ~CF; + W_printf("DISK write @%d/%d/%d (%d) OK.\n", + head, track, sect, res >> 9); + break; + + case 4: /* test */ + FLUSHDISK(dp); + disk_open(dp); + head = HI(dx); + sect = (REG(ecx) & 0x3f) - 1; + track = (HI(cx)) | + ((REG(ecx) & 0xc0) << 2); + number = LO(ax); + d_printf("DISK %02x test [h:%d,s:%d,t:%d](%d)\n", + disk, head, sect, track, number); + + if (checkdp(dp) || head >= dp->heads || + sect >= dp->sectors || track >= dp->tracks) { + HI(ax) = DERR_NOTFOUND; + REG(eflags) |= CF; + error("test: sector not found 5\n"); + if (dp) + dbug_printf("hds: %d, sec: %d, tks: %d\n", + dp->heads, dp->sectors, dp->tracks); + break; + } + pos = calc_pos(dp, DISK_OFFSET(dp, head, sect, track) / SECTOR_SIZE); + if (pos < 0) { + /* XXX: we ignore write to this area, so verify does not work */ + REG(eflags) &= ~CF; + break; + } + + if (pos != lseek(dp->fdesc, pos, 0)) { + HI(ax) = DERR_NOTFOUND; + REG(eflags) |= CF; + error("test: sector not found 6\n"); + break; + } +#if 0 + res = lseek(dp->fdesc, number << 9, 0); + if (res & 0x1ff) { /* must read multiple of 512 bytes and res != -1 */ + HI(ax) = DERR_BADSEC; + REG(eflags) |= CF; + error("test: sector corrupt 3\n"); + break; + } + LWORD(eax) = res >> 9; +#endif + REG(eflags) &= ~CF; + break; + + case 8: /* get disk drive parameters */ + d_printf("disk get parameters %#x\n", disk); + + if (dp != NULL) { + if (disk < 0x80) { /* get CMOS type for floppies*/ + switch (dp->sectors) { + case 9: + if (dp->tracks == 80) + LO(bx) = THREE_INCH_720KFLOP; + else + LO(bx) = FIVE_INCH_360KFLOP; + break; + case 15: + LO(bx) = FIVE_INCH_FLOPPY; + break; + case 18: + LO(bx) = THREE_INCH_FLOPPY; + break; + case 36: + LO(bx) = THREE_INCH_2880KFLOP; + break; + case 0: + LO(bx) = dp->default_cmos; + if (dp->default_cmos == FIVE_INCH_360KFLOP) + dp->tracks = 40; + else + dp->tracks = 80; + dp->heads = 2; + switch (dp->default_cmos) { + case FIVE_INCH_360KFLOP: + case THREE_INCH_720KFLOP: + dp->sectors = 9; + break; + case FIVE_INCH_FLOPPY: + dp->sectors = 15; + break; + case THREE_INCH_FLOPPY: + dp->sectors = 18; + break; + case THREE_INCH_2880KFLOP: + dp->sectors = 36; + break; + default: + dp->sectors = 18; + } + dp->num_secs = + (unsigned long long)dp->tracks * dp->heads * dp->sectors; + d_printf("auto type defaulted to CMOS %d, sectors: %d\n", LO(bx), + dp->sectors); + break; + default: + LO(bx) = THREE_INCH_FLOPPY; + d_printf("type det. failed. num_tracks is: %d\n", dp->tracks); + break; + } + + /* return the Diskette Parameter Table */ + SREG(es) = ISEG(0x1e); // address at 0000:0078 i.e. int1e vector + LWORD(edi) = IOFF(0x1e); + d_printf("Diskette Parameter Table at %04X:%04X\n", + SREG(es), LWORD(edi)); + } + + /* these numbers are "zero based" */ + HI(dx) = dp->heads - 1; + track = dp->tracks - 1; + if (track > 0x3FF) + { + /* if tracks do not fit, clamp to the maximum representable */ + track = 0x3FF; + } + HI(cx) = track & 0xff; + + LO(dx) = (disk < 0x80) ? FDISKS : HDISKS; + LO(cx) = (dp->sectors & 0x3f) | ((track & 0x300) >> 2); + LO(ax) = 0; + HI(ax) = DERR_NOERR; + REG(eflags) &= ~CF; /* no error */ + } + else { + LWORD(edx) = 0; /* no hard disks */ + LWORD(ecx) = 0; + LO(bx) = 0; + HI(ax) = DERR_BADCMD; + REG(eflags) |= CF; /* error */ + } + break; + + /* beginning of Alan's additions */ + case 0x9: /* initialise drive from bpb */ + CARRY; + HI(ax) = DERR_BADCMD; + break; + + case 0x0A: /* We don't have access to ECC info */ + case 0x0B: + CARRY; + HI(ax) = DERR_BADCMD; /* unsupported opn. */ + break; + + case 0x0C: /* explicit seek heads. - bit hard */ + CARRY; + HI(ax) = DERR_BADCMD; + break; + + case 0x0D: /* Drive reset (hd only) */ + NOCARRY; + HI(ax) = DERR_NOERR; + break; + + case 0x0E: /* XT only funcs */ + case 0x0F: + CARRY; + HI(ax) = DERR_NOERR; + break; + + case 0x10: /* Test drive is ok */ + case 0x11: /* Recalibrate */ + disk = LO(dx); + if (disk < 0x80 || disk >= 0x80 + HDISKS) { + /* Controller didn't respond */ + HI(ax) = DERR_CONTROLLER; + CARRY; + break; + } + else { + HI(ax) = DERR_CONTROLLER; + NOCARRY; + } + break; + + case 0x12: /* XT diagnostics */ + case 0x13: + REG(eax) &= 0xFF; + CARRY; + break; + + case 0x14: /* AT diagnostics. Unix keeps the drive happy + so report ok if it valid */ + REG(eax) &= 0xFF; + NOCARRY; + break; + /* end of Alan's additions */ + + case 0x15: /* Get type */ + d_printf("disk gettype %#x\n", disk); + if (dp != NULL && disk >= 0x80) { + if (dp->floppy) { + HI(ax) = 1; /* floppy disk, change detect (1=no, 2=yes) */ + d_printf("disk gettype: floppy\n"); + LWORD(edx) = 0; + LWORD(ecx) = 0; + } + else { + d_printf("disk gettype: hard disk\n"); + HI(ax) = 3; /* fixed disk */ + number = dp->tracks; + /* With 0x3FF as the maximum cylinder number (zero-based), + the maximum *amount* of cylinders is one more, ie 0x400. */ + if (number > 0x400) number = 0x400; + number_sectors = (uint64_t)number * dp->sectors * dp->heads; + if (number_sectors > 0xFFFFFFFF) + number_sectors = 0xFFFFFFFF; + LWORD(ecx) = (number_sectors >> 16) & 0xFFFF; + LWORD(edx) = number_sectors & 0xFFFF; + } + REG(eflags) &= ~CF; /* no error */ + } + else { + if (dp != NULL) { + d_printf("gettype on floppy %d\n", disk); + HI(ax) = 1; /* floppy, no change detect=1 */ + NOCARRY; + } + else { + d_printf("gettype: no disk %d\n", disk); + HI(ax) = 0; /* disk not there */ + REG(eflags) |= CF; /* error */ + } + } + break; + + case 0x16: + /* get disk change status - hard - by claiming + our disks don't have a changed line we are kind of ok */ + warn("int13: CHECK DISKCHANGE LINE\n"); + disk = LO(dx); + if (disk >= FDISKS || disktab[disk].removable) { + d_printf("int13: DISK CHANGED\n"); + CARRY; + /* REG(eax)&=0xFF; + REG(eax)|=0x200; */ + HI(ax) = 1; /* change occurred */ + } + else { + NOCARRY; + HI(ax) = 00; /* clear AH */ + d_printf("int13: NO CHANGE\n"); + } + break; + + case 0x17: + /* set disk type: should do all the ioctls etc + but I'm not feeling that brave yet */ + /* al=type dl=drive */ + CARRY; + break; + /* end of Alan's 2nd mods */ + + case 0x18: /* Set media type for format */ + track = HI(cx) + ((LO(cx) & 0xc0) << 2); + sect = LO(cx) & 0x3f; + d_printf("disk: set media type %x failed, %d sectors, %d tracks\n", disk, sect, track); + HI(ax) = DERR_BADCMD; /* function not available */ + CARRY; + break; + + case 0x20: /* ??? */ + d_printf("weird int13, ax=0x%04x\n", LWORD(eax)); + break; + case 0x28: /* DRDOS 6.0 call ??? */ + d_printf("int 13h, ax=%04x...DRDOS call\n", LWORD(eax)); + break; + + case 0x41: /* IBM/MS Extensions, install check */ + LWORD(ebx) = IMEXT_MAGIC; + HI(ax) = IMEXT_VER_MAJOR; + LWORD(ecx) = IMEXT_API_SUPPORT_BITS; + NOCARRY; + break; + + case 0x42: { /* IBM/MS Extensions, read */ + struct ibm_ms_diskaddr_pkt *diskaddr; + + FLUSHDISK(dp); + disk_open(dp); + diskaddr = SEG_ADR((struct ibm_ms_diskaddr_pkt *), ds, si); + + if (diskaddr->len < sizeof(*diskaddr) /* 0x10 */ ) { + error("Too small disk packet, ah=0x42!\n"); + error("DISK %02x ext read\n", disk); + HI(ax) = DERR_BADCMD; /* Award Medallion BIOS v6.0 uses this code */ + CARRY; + break; + } + + checkdp_val = checkdp(dp); + buffer = SEGOFF2LINEAR(diskaddr->buf_seg, diskaddr->buf_ofs); + number = diskaddr->blocks; + WRITE_P(diskaddr->blocks, 0); + d_printf("DISK %02x ext read [LBA %"PRIu64"](%d)->%#x (%04x:%04x)\n", + disk, diskaddr->block, number, + buffer, diskaddr->buf_seg, diskaddr->buf_ofs); + + if (checkdp_val) { + d_printf("Sector not found, AH=0x42!\n"); + d_printf("DISK %02x ext read [LBA %"PRIu64"](%d)->%#x (%04x:%04x)\n", + disk, diskaddr->block, number, + buffer, diskaddr->buf_seg, diskaddr->buf_ofs); + if (dp) { + d_printf("DISK dev %s GEOM %d heads %d sects %d trk\n", + dp->dev_name, dp->heads, dp->sectors, dp->tracks); + } else { + d_printf("DISK %02x undefined.\n", disk); + } + show_regs(); + HI(ax) = DERR_NOTFOUND; + REG(eflags) |= CF; + break; + } + + res = read_sectors(dp, buffer, diskaddr->block, number); + + if (res < 0) { + HI(ax) = -res; + CARRY; + break; + } + else if (res & 511) { /* must read multiple of 512 bytes */ + error("sector_corrupt 1, return = %d!\n", res); + HI(ax) = DERR_BADSEC; /* sector corrupt */ + CARRY; + break; + } + + WRITE_P(diskaddr->blocks, res >> 9); + HI(ax) = 0; + REG(eflags) &= ~CF; + R_printf("DISK ext read LBA %"PRIu64" (%d) -> %#x OK.\n", + diskaddr->block, res >> 9, buffer); + break; + } + + case 0x43: { /* IBM/MS Extensions, write */ + struct ibm_ms_diskaddr_pkt *diskaddr; + + FLUSHDISK(dp); + disk_open(dp); + diskaddr = SEG_ADR((struct ibm_ms_diskaddr_pkt *), ds, si); + + if (diskaddr->len < sizeof(*diskaddr) /* 0x10 */ ) { + error("Too small disk packet, ah=0x43!\n"); + error("DISK %02x ext write\n", disk); + HI(ax) = DERR_BADCMD; /* Award Medallion BIOS v6.0 uses this code */ + CARRY; + break; + } + + checkdp_val = checkdp(dp); + buffer = SEGOFF2LINEAR(diskaddr->buf_seg, diskaddr->buf_ofs); + number = diskaddr->blocks; + WRITE_P(diskaddr->blocks, 0); + d_printf("DISK %02x ext write [LBA %"PRIu64"](%d)->%#x (%04x:%04x)\n", + disk, diskaddr->block, number, + buffer, diskaddr->buf_seg, diskaddr->buf_ofs); + + if (checkdp_val) { + error("Sector not found, AH=0x43!\n"); + error("DISK %02x ext write [LBA %"PRIu64"](%d)->%#x (%04x:%04x)\n", + disk, diskaddr->block, number, + buffer, diskaddr->buf_seg, diskaddr->buf_ofs); + if (dp) { + error("DISK dev %s GEOM %d heads %d sects %d trk\n", + dp->dev_name, dp->heads, dp->sectors, dp->tracks); + } else { + error("DISK %02x undefined.\n", disk); + } + HI(ax) = DERR_NOTFOUND; + REG(eflags) |= CF; + break; + } + + if (dp->rdonly) { + d_printf("DISK %02x is write protected!\n", disk); + if (dp->floppy) + HI(ax) = DERR_WP; + else + HI(ax) = DERR_WRITEFLT; + REG(eflags) |= CF; + break; + } + + res = write_sectors(dp, buffer, diskaddr->block, number); + if (res < 0) { + HI(ax) = -res; + CARRY; + break; + } + else if (res & 511) { /* must read multiple of 512 bytes */ + error("sector_corrupt 1, return = %d!\n", res); + HI(ax) = DERR_BADSEC; /* sector corrupt */ + CARRY; + break; + } + + WRITE_P(diskaddr->blocks, res >> 9); + HI(ax) = 0; + REG(eflags) &= ~CF; + R_printf("DISK ext write LBA %"PRIu64" (%d) -> %#x OK.\n", + diskaddr->block, res >> 9, buffer); + break; + } + + case 0x44: /* IBM/MS Extensions, verify */ + /* Always succeeds. Should perhaps check validity of sector address. */ + NOCARRY; + HI(ax) = 0; + break; + + case 0x47: /* IBM/MS Extensions, extended seek */ + NOCARRY; + HI(ax) = 0; + break; + + case 0x48: { /* IBM/MS Extensions, get drive parameters */ + struct ibm_ms_drive_params *params; + + FLUSHDISK(dp); + disk_open(dp); + params = SEG_ADR((struct ibm_ms_drive_params *), ds, si); + + if (checkdp(dp)) { + error("Invalid drive, AH=0x48!\n"); + if (dp) { + d_printf("DISK dev %s GEOM %d heads %d sects %d trk\n", + dp->dev_name, dp->heads, dp->sectors, dp->tracks); + } else { + d_printf("DISK %02x undefined.\n", disk); + } + show_regs(); + HI(ax) = DERR_NOTFOUND; + REG(eflags) |= CF; + break; + } + + WRITE_P(params->flags, IMEXT_INFOFLAG_CHSVALID | IMEXT_INFOFLAG_NODMAERR); + if (dp->floppy) + WRITE_P(params->flags, params->flags | IMEXT_INFOFLAG_REMOVABLE); + WRITE_P(params->tracks, dp->tracks); + WRITE_P(params->heads, dp->heads); + WRITE_P(params->sectors, dp->sectors); + WRITE_P(params->total_sectors_lo, dp->num_secs & 0xffffffff); + WRITE_P(params->total_sectors_hi, dp->num_secs >> 32); + WRITE_P(params->bytes_per_sector, SECTOR_SIZE); + if (params->len >= 0x1e) + WRITE_P(params->edd_cfg_ofs, params->edd_cfg_seg = 0xffff); + NOCARRY; + HI(ax) = 0; + break; + } + + case 0x5: /* format */ + NOCARRY; /* successful */ + HI(ax) = DERR_NOERR; + break; + case 0xdc: + d_printf("int 13h, ax=%04x...weird windows disk interrupt\n", + LWORD(eax)); + break; + case 0xf9: /* SWBIOS installation check */ + CARRY; + break; + case 0xfe: /* SWBIOS get extended cyl count */ + if (dp) LWORD(edx) = dp->tracks % 1024; + NOCARRY; + break; + default: + d_printf("disk error, unknown command: int13, ax=0x%04x\n", + LWORD(eax)); + show_regs(); + CARRY; + HI(ax) = DERR_BADCMD; + break; + } + + WRITE_BYTE(status_addr, HI(ax)); + return 1; +} + +/* flush disks every config.fastfloppy ticks */ +void +floppy_tick(void) +{ + static int ticks = 0; + + /* some progs (InstallShield/win31) monitor these locations */ + WRITE_BYTE(BIOS_MOTOR_TIMEOUT, READ_BYTE(BIOS_MOTOR_TIMEOUT) - 1); + WRITE_BYTE(BIOS_FDC_RESULT_BUFFER, READ_BYTE(BIOS_FDC_RESULT_BUFFER) + 1); + + if (config.fastfloppy && ++ticks >= config.fastfloppy) { + disk_sync(); + if (debug_level('d') > 2) + d_printf("FLOPPY: flushing after %d ticks\n", ticks); + ticks = 0; + } +} + +fatfs_t *get_fat_fs_by_serial(unsigned long serial, int *r_idx, int *r_ro) +{ + int i; + + for (i = 0; i < FDISKS; i++) { + struct disk *dp = &disktab[i]; + if(dp->type == DIR_TYPE && dp->fatfs && dp->serial == serial) { + *r_idx = dp->mfs_idx; + *r_ro = dp->rdonly; + return dp->fatfs; + } + } + FOR_EACH_HDISK(i, { + struct disk *dp = &hdisktab[i]; + if(dp->type == DIR_TYPE && dp->fatfs && dp->serial == serial) { + *r_idx = dp->mfs_idx; + *r_ro = dp->rdonly; + return dp->fatfs; + } + }); + return NULL; +} + +fatfs_t *get_fat_fs_by_drive(unsigned char drv_num) +{ + struct disk *dp = NULL; + int num = drv_num & 0x7f; + + if (drv_num & 0x80) { + dp = hdisk_find(drv_num); + } else { + if (num < FDISKS) + dp = &disktab[num]; + } + if (!dp) + return NULL; + if (dp->type == DIR_TYPE) + return dp->fatfs; + return NULL; +} + +struct disk *hdisk_find(uint8_t num) +{ + int i; + FOR_EACH_HDISK(i, + if (hdisktab[i].drive_num == num) + return &hdisktab[i]; + ); + return NULL; +} + +struct disk *hdisk_find_by_path(const char *path) +{ + int i; + FOR_EACH_HDISK(i, + if (hdisktab[i].dev_name && strcmp(hdisktab[i].dev_name, path) == 0) + return &hdisktab[i]; + ); + return NULL; +} diff --git a/src/base/misc/dos2linux.c b/src/base/misc/dos2linux.c new file mode 100644 index 0000000..9394d9f --- /dev/null +++ b/src/base/misc/dos2linux.c @@ -0,0 +1,1455 @@ +/* + * + * DANG_BEGIN_MODULE + * REMARK + * + * This file contains a simple system for passing information through to + * DOSEMU from the Linux side. It does not allow dynamic message passing, + * but it intended to provide useful information for the DOS user. + * + * As such, the current set of implemented commands are : + * GET_USER_ENVVAR and GET_COMMAND + * + * These are made available to the DOSEMU by using the DOS_HELPER interrupt + * (Currently 0xE6) and writing a string into a location passed to this + * interrupt handler using the registers. In the case of GET_USER_ENVVAR the + * string also contains the name of the environment variable to interrogate. + * (The string is overwritten with the reply). + * + * /REMARK + * DANG_END_MODULE + * + * DANG_BEGIN_CHANGELOG + * + * $Log$ + * Revision 1.16 2006/01/28 09:45:34 bartoldeman + * Move disclaimer and install prompts from dos_post_boot to banner code, + * using BIOS calls. + * + * Revision 1.15 2005/12/31 06:23:11 bartoldeman + * Mostly from Clarence: unix.com patches in dosemu-exec-friendly.diff, + * so that "dosemu keen1.exe" works again. + * But do not use heuristics that split options from commands, by making + * quotes of filenames that contain spaces compulsory. + * + * Revision 1.14 2005/12/18 07:58:44 bartoldeman + * Put some basic checks into dos_read/dos_write, and memmove_dos2dos to + * avoid touching the protected video memory. + * Fixes #1379806 (gw stopped to work under X). + * + * Revision 1.13 2005/11/29 18:24:31 stsp + * + * call_msdos() must switch to real mode before calling DOS, if needed. + * + * Revision 1.12 2005/11/29 10:25:04 bartoldeman + * Adjust video.c init so that -dumb does not pop up an X window. + * Make -dumb quiet until the command is executed if a command is given. So + * dosemu -dumb dir + * gives a directory listing and nothing else. + * + * Revision 1.11 2005/11/21 18:12:51 stsp + * + * - Map the DOS<-->unix STDOUT/STDERR properly for unix.com. + * - Improved the DOS console reading for unix.com + * + * Revision 1.10 2005/11/19 00:54:40 stsp + * + * Make unix.com "keyboard-aware" (FR #1360156). It works like a tty + * in a -icanon mode. + * system() replaced with execlp() to make it possible to emulate ^C + * with SIGINT. + * Also reworked the output-grabbing again - it was still skipping an + * output sometimes. + * + * Revision 1.9 2005/11/18 21:47:56 stsp + * + * Make unix.com to use DOS/stdout instead of the direct video mem access, + * so that the file redirection to work. + * + * Revision 1.8 2005/11/02 04:52:55 stsp + * + * Fix run_unix_command (#1345102) + * + * Revision 1.7 2005/09/30 22:15:59 stsp + * + * Avoid using LOWMEM for the non-const addresses. + * This fixes iplay (again), as it read()s directly to the EMS frame. + * Also, moved dos_read/write to dos2linux.c to make them globally + * available. And removed the __builtin_constant_p() check in LINEAR2UNIX - + * gcc might still be able to optimize the const addresses, but falling + * back for dosaddr_to_unixaddr() will happen less frequently. + * + * Revision 1.6 2005/09/05 09:47:56 bartoldeman + * Moved much of X_change_config to dos2linux.c. Much of it will be shared + * with SDL. + * + * Revision 1.5 2005/06/20 17:12:11 stsp + * + * Remove the ancient (unused) ioctl queueing code. + * + * Revision 1.4 2005/05/20 00:26:09 bartoldeman + * It's 2005 this year. + * + * Revision 1.3 2005/03/21 17:24:09 stsp + * + * Fixed (and re-enabled) the terminate-after-execute feature (bug #1152829). + * Also some minor adjustments/leftovers. + * + * Revision 1.2 2004/01/16 20:50:27 bartoldeman + * Happy new year! + * + * Revision 1.1.1.1 2003/06/23 00:02:07 bartoldeman + * Initial import (dosemu-1.1.5.2). + * + * + * DANG_END_CHANGELOG + * + */ + + + +#include +#include +#include +#ifdef HAVE_LIBBSD +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __GLIBC__ +#include +#endif +#include + +#include "emu.h" +#include "cpu-emu.h" +#include "int.h" +#include "emudpmi.h" +#include "timers.h" +#include "video.h" +#include "lowmem.h" +#include "coopth.h" +#include "utilities.h" +#include "dos2linux.h" +#include "vgaemu.h" +#include "disks.h" +#include "mapping.h" +#include "redirect.h" +#include "translate/translate.h" +#include "../../dosext/mfs/lfn.h" +#include "../../dosext/mfs/mfs.h" +#include "mmio_tracing.h" +#include "spscq.h" + +#define com_stderr 2 + +#ifndef max +#define max(a,b) ((a)>(b)? (a):(b)) +#endif + +#define GET_USER_ENVVAR 0x52 +#define EXEC_USER_COMMAND 0x50 + +static char *misc_dos_options; +int com_errno; + +char *misc_e6_options(void) +{ + return misc_dos_options; +} + +void misc_e6_store_options(const char *str) +{ + size_t olen = 0; + size_t slen = strlen(str); + /* any later arguments are collected as DOS options */ + if (misc_dos_options) { + olen = strlen(misc_dos_options); + misc_dos_options = realloc(misc_dos_options, olen + slen + 2); + misc_dos_options[olen] = ' '; + strcpy(misc_dos_options + olen + 1, str); + } else { + misc_dos_options = malloc(slen + 1); + strcpy(misc_dos_options, str); + } + g_printf ("Storing Options : %s\n", misc_dos_options); +} + + +static int pty_fd; +static int pty_done; +static int cbrk; +static sem_t rd_sem; +static pthread_t reader; +static void *queue; + +static void *rd_thread(void *arg) +{ + while (1) { + sem_wait(&rd_sem); + while (1) { + void *ptr; + unsigned len; + ssize_t rd; + + ptr = spscq_write_area(queue, &len); + rd = read(pty_fd, ptr, len); + if (rd <= 0) + break; + spscq_commit_write(queue, rd); + } + __atomic_store_n(&pty_done, 1, __ATOMIC_RELEASE); + } + return NULL; +} + +static void pty_thr(void) +{ +#define MAX_LEN (1024+1) + char buf[MAX_LEN]; + int rd, wr; + struct char_set_state kstate; + struct char_set_state dstate; + + init_charset_state(&kstate, trconfig.keyb_charset); + init_charset_state(&dstate, trconfig.dos_charset); + sem_post(&rd_sem); + while (1) { + rd = spscq_read(queue, buf, sizeof(buf) - 1); + if (rd > 0) { + int rc; + const char *p = buf; + buf[rd] = 0; + while (*p) { + t_unicode uni[MAX_LEN]; + const t_unicode *u = uni; + char buf2[MAX_LEN * MB_LEN_MAX]; + rc = charset_to_unicode_string(&kstate, uni, &p, strlen(p), + MAX_LEN); + if (rc <= 0) + break; + rc = unicode_to_charset_string(&dstate, buf2, &u, rc, + sizeof(buf2)); + if (rc <= 0) + break; + com_doswritecon(buf2, rc); + } + } + if (__atomic_load_n(&pty_done, __ATOMIC_ACQUIRE)) + break; + + wr = com_dosreadcon(buf, sizeof(buf) - 1); + if (wr > 0) + write(pty_fd, buf, wr); + + if (!rd && !wr) + coopth_wait(); + } + cleanup_charset_state(&kstate); + cleanup_charset_state(&dstate); +} + +void dos2tty_init(void) +{ + pty_fd = posix_openpt(O_RDWR); + if (pty_fd == -1) + { + error("openpt failed %s\n", strerror(errno)); + return; + } + unlockpt(pty_fd); + sem_init(&rd_sem, 0, 0); + queue = spscq_init(1024 * 64); // 64K queue + pthread_create(&reader, NULL, rd_thread, queue); +#if defined(HAVE_PTHREAD_SETNAME_NP) && defined(__GLIBC__) + pthread_setname_np(reader, "dosemu: ttyrd"); +#endif +} + +void dos2tty_done(void) +{ + pthread_cancel(reader); + pthread_join(reader, NULL); + spscq_done(queue); + close(pty_fd); + sem_destroy(&rd_sem); +} + +static void dos2tty_start(void) +{ + char a; + int rd; + cbrk = com_setcbreak(0); + /* flush pending input first */ + do { + rd = com_dosreadcon(&a, 1); + } while (rd > 0); + pty_done = 0; + /* must run with interrupts enabled to read keypresses */ + assert(!isset_IF()); + set_IF(); + pty_thr(); +} + +static void dos2tty_stop(void) +{ + clear_IF(); + com_setcbreak(cbrk); +} + +static int do_wait_cmd(pid_t pid) +{ + int status, retval; + + dos2tty_start(); + while ((retval = waitpid(pid, &status, WNOHANG)) == 0) + coopth_wait(); + if (retval == -1) + error("waitpid: %s\n", strerror(errno)); + dos2tty_stop(); + /* print child exitcode. not perfect */ + g_printf("run_unix_command() (parent): child exit code: %i\n", + WEXITSTATUS(status)); + return WEXITSTATUS(status); +} + +int run_unix_command(int argc, const char **argv, int bg) +{ + const char *path; + char *p; + pid_t pid; + + path = findprog(argv[0], getenv("PATH")); + if (!path) { + com_printf("unix: %s not found\n", argv[0]); + return -1; + } + /* check if path allowed */ + p = config.unix_exec ? strstr(config.unix_exec, path) : NULL; + if (p) { + /* make sure the found string is entire word */ + int l = strlen(path); + if ((p > config.unix_exec && p[-1] != ' ') || + (p[l] != '\0' && p[l] != ' ')) + p = NULL; + } + if (!p) { + com_printf("unix: execution of %s is not allowed.\n" + "Add %s to $_unix_exec list.\n", + argv[0], path); + error("execution of %s is not allowed.\n" + "Add %s to $_unix_exec list.\n", + argv[0], path); + return -1; + } + + g_printf("UNIX: run %s, %i args\n", path, argc); + pid = run_external_command(path, argc, argv, !bg, -1, pty_fd); + if (bg) { + sigchld_enable_cleanup(pid); + return 0; + } + return do_wait_cmd(pid); +} + +/* no PATH searching, no arguments allowed, no stdin, no inherited fds */ +int run_unix_secure(const char *prg) +{ + char *path; + const char *argv[2]; + pid_t pid; + + path = assemble_path(dosemu_exec_dir_path, prg); + if (!exists_file(path)) { + com_printf("unix: %s not found\n", path); + free(path); + return -1; + } + argv[0] = prg; + argv[1] = NULL; /* no args allowed */ + g_printf("UNIX: run_secure %s '%s'\n", path, prg); + pid = run_external_command(path, 1, argv, 0, STDERR_FILENO + 1, pty_fd); + free(path); + return do_wait_cmd(pid); +} + +/* + * This function provides parts of the interface to reconfigure parts + * of X/SDL and the VGA emulation during a DOSEMU session. + * It is used by the xmode.exe program that comes with DOSEMU. + */ +int change_config(unsigned item, void *buf, int grab_active, + int kbd_grab_active, int clip_mode) +{ + static char title_emuname [TITLE_EMUNAME_MAXLEN] = {0}; + static char title_appname [TITLE_APPNAME_MAXLEN] = {0}; + int err = 0; + + g_printf("change_config: item = %d, buffer = %p\n", item, buf); + + switch(item) { + + case CHG_TITLE: + { + /* high-level write (shows name of emulator + running app) */ + char title [TITLE_EMUNAME_MAXLEN + TITLE_APPNAME_MAXLEN + 35] = {0}; + wchar_t wtitle [sizeof(title)]; + char *unixptr = NULL; + char *s; + + /* app - DOS in a BOX */ + /* name of running application (if any) */ + if (config.X_title_show_appname && strlen (title_appname)) + strcpy (title, title_appname); + + /* append name of emulator */ + if (strlen (title_emuname)) { + if (strlen (title)) strcat (title, " - "); + strcat (title, title_emuname); + } else if (strlen (config.X_title)) { + if (strlen (title)) strcat (title, " - "); + unixptr = title + strlen (title); + /* foreign string, cannot trust its length to be <= TITLE_EMUNAME_MAXLEN */ + snprintf (unixptr, TITLE_EMUNAME_MAXLEN, "%s ", config.X_title); + } + + if (dosemu_frozen) { + if (strlen (title)) strcat (title, " "); + + if (dosemu_user_froze) + strcat (title, "[paused - Ctrl+Alt+P] "); + else + strcat (title, "[background pause] "); + } + + if (grab_active || kbd_grab_active || clip_mode) { + strcat(title, "["); + if (kbd_grab_active) { + strcat(title, "keyboard"); + if (grab_active || clip_mode) + strcat(title, "+"); + } + if (grab_active) { + strcat(title, "mouse"); + if (clip_mode) + strcat(title, "+"); + } + if (clip_mode) + strcat(title, "clip"); + strcat(title, " grab] "); + } + + /* now actually change the title of the Window */ + if (unixptr == NULL) + unixptr = strchr(title, '\0'); + for (s = title; s < unixptr; s++) + wtitle[s - title] = dos_to_unicode_table[(unsigned char)*s]; + wtitle[unixptr - title] = 0; + if (*unixptr) { + if (mbstowcs(&wtitle[unixptr - title], unixptr, TITLE_EMUNAME_MAXLEN) + == -1) + wtitle[unixptr - title] = 0; + wtitle[unixptr - title + TITLE_EMUNAME_MAXLEN] = 0; + } + Video->change_config (CHG_TITLE, wtitle); + } + break; + + case CHG_TITLE_EMUNAME: + g_printf ("change_config: emu_name = %s\n", (char *) buf); + snprintf (title_emuname, TITLE_EMUNAME_MAXLEN, "%s", ( char *) buf); + Video->change_config (CHG_TITLE, NULL); + break; + + case CHG_TITLE_APPNAME: + g_printf ("change_config: app_name = %s\n", (char *) buf); + snprintf (title_appname, TITLE_APPNAME_MAXLEN, "%s", (char *) buf); + Video->change_config (CHG_TITLE, NULL); + break; + + case CHG_TITLE_SHOW_APPNAME: + g_printf("change_config: show_appname %i\n", *((int *) buf)); + config.X_title_show_appname = *((int *) buf); + Video->change_config (CHG_TITLE, NULL); + break; + + case CHG_WINSIZE: + config.X_winsize_x = *((int *) buf); + config.X_winsize_y = ((int *) buf)[1]; + g_printf("change_config: set initial graphics window size to %d x %d\n", config.X_winsize_x, config.X_winsize_y); + break; + + case CHG_BACKGROUND_PAUSE: + g_printf("change_config: background_pause %i\n", *((int *) buf)); + config.X_background_pause = *((int *) buf); + break; + + case GET_TITLE_APPNAME: + snprintf (buf, TITLE_APPNAME_MAXLEN, "%s", title_appname); + break; + + default: + err = 100; + } + + return err; +} + +/* set of 4096 addresses rounded down to page boundaries where + bits 12..23 equal the index: if the page is in this set we quickly + know that regular reads and writes are valid. + Initialize with invalid entries */ +static dosaddr_t unprotected_page_cache[PAGE_SIZE] = {0xffffffff}; +/* software TLB to translate unprotected pages to UNIX addresses */ +static unsigned char *unprotected_page_unixaddr_tlb[PAGE_SIZE] = {}; + +void invalidate_unprotected_page_cache(dosaddr_t addr, int len) +{ + unsigned int page; + for (page = addr >> PAGE_SHIFT; + page <= (addr + len - 1) >> PAGE_SHIFT; page++) + unprotected_page_cache[page & (PAGE_SIZE-1)] = 0xffffffff; +} + +/* returns the host address if it's definitely unprotected, + otherwise NULL */ +static inline void *unprotected_dosaddr_to_unixaddr(dosaddr_t addr, int len) +{ + /* hash = low 12 bits of page number, add len-1 to fail on boundaries. + This gives no clashes if all addresses are under 16MB and >99.99% hit + rate when addresses over 16MB are present while still using a small + cache table. + See http://www.emulators.com/docs/nx08_stlb.htm for background on + this technique. + */ + int hash = (addr >> PAGE_SHIFT) & (PAGE_SIZE-1); + if (unprotected_page_cache[hash] == ((addr + len - 1) & _PAGE_MASK)) + return &unprotected_page_unixaddr_tlb[hash][addr & (PAGE_SIZE-1)]; + else + return NULL; +} + +/* marks the page as unprotected in the cache, and add the host address to + the TLB */ +static inline void set_unprotected_page(dosaddr_t addr, void *uaddr) +{ + int hash = (addr >> PAGE_SHIFT) & (PAGE_SIZE-1); + unprotected_page_cache[hash] = addr & _PAGE_MASK; + unprotected_page_unixaddr_tlb[hash] = (void *)((uintptr_t)uaddr & _PAGE_MASK); +} + +void default_sim_pagefault_handler(dosaddr_t addr, int err, uint32_t op, int len) +{ + if (err & 2) + dosemu_error("Invalid write to addr %#x, ptr %p, len %d\n", + addr, MEM_BASE32(addr), len); + else + dosemu_error("Invalid read from addr %#x, ptr %p\n", + addr, MEM_BASE32(addr)); + leavedos_main(1); +} + +static void check_read_pagefault(dosaddr_t addr, void *uaddr, + sim_pagefault_handler_t handler) +{ + if (addr >= LOWMEM_SIZE + HMASIZE) { + if (!dpmi_read_access(addr)) + /* uncommitted page is never "present" */ + handler(addr, 4, 0, 0); + /* only add writable pages to the cache! */ + if (!dpmi_write_access(addr)) + return; + } + if (!e_querymprot(addr) && !memcheck_is_rom(addr)) + set_unprotected_page(addr, uaddr); +} + +uint8_t do_read_byte(dosaddr_t addr, sim_pagefault_handler_t handler) +{ + void *uaddr = unprotected_dosaddr_to_unixaddr(addr, 1); + if (!uaddr) { + /* use vga_write_access instead of vga_read_access here to avoid adding + read-only addresses to the cache */ + if (vga_write_access(addr)) + return vga_read(addr); + if (config.mmio_tracing && mmio_check(addr)) + return mmio_trace_byte(addr, READ_BYTE(addr), MMIO_READ); + uaddr = dosaddr_to_unixaddr(addr); + check_read_pagefault(addr, uaddr, handler); + } + return UNIX_READ_BYTE(uaddr); +} + +uint16_t do_read_word(dosaddr_t addr, sim_pagefault_handler_t handler) +{ + void *uaddr = unprotected_dosaddr_to_unixaddr(addr, 2); + if (!uaddr) { + if (((addr+1) & (PAGE_SIZE-1)) == 0) + /* split if spanning a page boundary */ + return do_read_byte(addr, handler) | + ((uint16_t)do_read_byte(addr+1, handler) << 8); + if (vga_write_access(addr)) + return vga_read_word(addr); + if (config.mmio_tracing && mmio_check(addr)) + return mmio_trace_word(addr, READ_WORD(addr), MMIO_READ); + uaddr = dosaddr_to_unixaddr(addr); + check_read_pagefault(addr, uaddr, handler); + } + return UNIX_READ_WORD(uaddr); +} + +uint32_t do_read_dword(dosaddr_t addr, sim_pagefault_handler_t handler) +{ + void *uaddr = unprotected_dosaddr_to_unixaddr(addr, 4); + if (!uaddr) { + if (((addr+3) & (PAGE_SIZE-1)) < 3) + return do_read_word(addr, handler) | + ((uint32_t)do_read_word(addr+2, handler) << 16); + if (vga_write_access(addr)) + return vga_read_dword(addr); + if (config.mmio_tracing && mmio_check(addr)) + return mmio_trace_dword(addr, READ_DWORD(addr), MMIO_READ); + uaddr = dosaddr_to_unixaddr(addr); + check_read_pagefault(addr, uaddr, handler); + } + return UNIX_READ_DWORD(uaddr); +} + +uint64_t do_read_qword(dosaddr_t addr, sim_pagefault_handler_t handler) +{ + return do_read_dword(addr, handler) | + ((uint64_t)do_read_dword(addr+4, handler) << 32); +} + +static int check_write_pagefault(dosaddr_t addr, void *uaddr, uint32_t op, int len, + sim_pagefault_handler_t handler) +{ + if (addr >= LOWMEM_SIZE + HMASIZE && !dpmi_write_access(addr)) { + handler(addr, 6 + dpmi_read_access(addr), op, len); + return 1; + } + if (!e_querymprot(addr) && !memcheck_is_rom(addr)) + set_unprotected_page(addr, uaddr); + return 0; +} + +void do_write_byte(dosaddr_t addr, uint8_t byte, sim_pagefault_handler_t handler) +{ + void *uaddr = unprotected_dosaddr_to_unixaddr(addr, 1); + if (!uaddr) { + if (vga_write_access(addr)) { + + vga_write(addr, byte); + return; + } + if (config.mmio_tracing && mmio_check(addr)) + mmio_trace_byte(addr, byte, MMIO_WRITE); + e_invalidate(addr, 1); + uaddr = dosaddr_to_unixaddr(addr); + if (check_write_pagefault(addr, uaddr, byte, 1, handler)) + return; + } + UNIX_WRITE_BYTE(uaddr, byte); +} + +void do_write_word(dosaddr_t addr, uint16_t word, sim_pagefault_handler_t handler) +{ + void *uaddr = unprotected_dosaddr_to_unixaddr(addr, 2); + if (!uaddr) { + if (((addr+1) & (PAGE_SIZE-1)) == 0) { + do_write_byte(addr, word & 0xff, handler); + do_write_byte(addr+1, word >> 8, handler); + return; + } + if (vga_write_access(addr)) { + vga_write_word(addr, word); + return; + } + if (config.mmio_tracing && mmio_check(addr)) + mmio_trace_word(addr, word, MMIO_WRITE); + e_invalidate(addr, 2); + uaddr = dosaddr_to_unixaddr(addr); + if (check_write_pagefault(addr, uaddr, word, 2, handler)) + return; + } + UNIX_WRITE_WORD(uaddr, word); +} + +void do_write_dword(dosaddr_t addr, uint32_t dword, sim_pagefault_handler_t handler) +{ + void *uaddr = unprotected_dosaddr_to_unixaddr(addr, 4); + if (!uaddr) { + if (((addr+3) & (PAGE_SIZE-1)) < 3) { + do_write_word(addr, dword & 0xffff, handler); + do_write_word(addr+2, dword >> 16, handler); + return; + } + if (vga_write_access(addr)) { + vga_write_dword(addr, dword); + return; + } + if (config.mmio_tracing && mmio_check(addr)) + mmio_trace_dword(addr, dword, MMIO_WRITE); + e_invalidate(addr, 4); + uaddr = dosaddr_to_unixaddr(addr); + if (check_write_pagefault(addr, uaddr, dword, 4, handler)) + return; + } + UNIX_WRITE_DWORD(uaddr, dword); +} + +void do_write_qword(dosaddr_t addr, uint64_t qword, sim_pagefault_handler_t handler) +{ + do_write_dword(addr, qword & 0xffffffff, handler); + do_write_dword(addr+4, qword >> 32, handler); +} + +uint8_t read_byte(dosaddr_t addr) +{ + return do_read_byte(addr, default_sim_pagefault_handler); +} + +uint16_t read_word(dosaddr_t addr) +{ + return do_read_word(addr, default_sim_pagefault_handler); +} + +uint32_t read_dword(dosaddr_t addr) +{ + return do_read_dword(addr, default_sim_pagefault_handler); +} + +uint64_t read_qword(dosaddr_t addr) +{ + return do_read_qword(addr, default_sim_pagefault_handler); +} + +void write_byte(dosaddr_t addr, uint8_t byte) +{ + do_write_byte(addr, byte, default_sim_pagefault_handler); +} + +void write_word(dosaddr_t addr, uint16_t word) +{ + do_write_word(addr, word, default_sim_pagefault_handler); +} + +void write_dword(dosaddr_t addr, uint32_t dword) +{ + do_write_dword(addr, dword, default_sim_pagefault_handler); +} + +void write_qword(dosaddr_t addr, uint64_t qword) +{ + do_write_qword(addr, qword, default_sim_pagefault_handler); +} + +void memcpy_2unix(void *dest, dosaddr_t src, size_t n) +{ + if (vga.inst_emu && src >= 0xa0000 && src < 0xc0000) + memcpy_from_vga(dest, src, n); + else while (n) { + /* EMS can produce the non-contig mapping. We need to iterate it + * page-by-page or use a separate alias window... */ + dosaddr_t bound = (src & _PAGE_MASK) + PAGE_SIZE; + size_t to_copy = _min(n, bound - src); + MEMCPY_2UNIX(dest, src, to_copy); + src += to_copy; + dest += to_copy; + n -= to_copy; + } +} + +void memcpy_2dos(dosaddr_t dest, const void *src, size_t n) +{ + if (vga.inst_emu && dest >= 0xa0000 && dest < 0xc0000) + memcpy_to_vga(dest, src, n); + else { + e_invalidate(dest, n); + while (n) { + dosaddr_t bound = (dest & _PAGE_MASK) + PAGE_SIZE; + size_t to_copy = _min(n, bound - dest); + MEMCPY_2DOS(dest, src, to_copy); + src += to_copy; + dest += to_copy; + n -= to_copy; + } + } +} + +void memset_dos(dosaddr_t dest, char ch, size_t n) +{ + if (vga.inst_emu && dest >= 0xa0000 && dest < 0xc0000) + vga_memset(dest, ch, n); + else { + e_invalidate(dest, n); + while (n) { + dosaddr_t bound = (dest & _PAGE_MASK) + PAGE_SIZE; + size_t to_copy = _min(n, bound - dest); + MEMSET_DOS(dest, ch, to_copy); + dest += to_copy; + n -= to_copy; + } + } +} + +void memmove_dos2dos(dosaddr_t dest, dosaddr_t src, size_t n) +{ + /* XXX GW (Game Wizard Pro) does this. + TODO: worry about overlaps; could be a little cleaner + using the memcheck.c mechanism */ + if (vga.inst_emu && src >= 0xa0000 && src < 0xc0000) + memcpy_dos_from_vga(dest, src, n); + else if (vga.inst_emu && dest >= 0xa0000 && dest < 0xc0000) + memcpy_dos_to_vga(dest, src, n); + else { + e_invalidate(dest, n); + while (n) { + dosaddr_t bound1 = (src & _PAGE_MASK) + PAGE_SIZE; + dosaddr_t bound2 = (dest & _PAGE_MASK) + PAGE_SIZE; + size_t to_copy1 = _min(bound1 - src, bound2 - dest); + size_t to_copy = _min(n, to_copy1); + MEMMOVE_DOS2DOS(dest, src, to_copy); + src += to_copy; + dest += to_copy; + n -= to_copy; + } + } +} + +void memcpy_dos2dos(unsigned dest, unsigned src, size_t n) +{ + /* Jazz Jackrabbit does DOS read to VGA via protmode selector */ + if (vga.inst_emu && src >= 0xa0000 && src < 0xc0000) + memcpy_dos_from_vga(dest, src, n); + else if (vga.inst_emu && dest >= 0xa0000 && dest < 0xc0000) + memcpy_dos_to_vga(dest, src, n); + else { + e_invalidate(dest, n); + while (n) { + dosaddr_t bound1 = (src & _PAGE_MASK) + PAGE_SIZE; + dosaddr_t bound2 = (dest & _PAGE_MASK) + PAGE_SIZE; + size_t to_copy1 = _min(bound1 - src, bound2 - dest); + size_t to_copy = _min(n, to_copy1); + MEMCPY_DOS2DOS(dest, src, to_copy); + src += to_copy; + dest += to_copy; + n -= to_copy; + } + } +} + +int unix_read(int fd, void *data, int cnt) +{ + return RPT_SYSCALL(read(fd, data, cnt)); +} + +int dos_read(int fd, unsigned data, int cnt) +{ + int ret; + /* GW also reads or writes directly from a file to protected video memory. */ + if (vga.inst_emu && data >= 0xa0000 && data < 0xc0000) { + char buf[cnt]; + ret = unix_read(fd, buf, cnt); + if (ret >= 0) + memcpy_to_vga(data, buf, ret); + } + else + ret = unix_read(fd, LINEAR2UNIX(data), cnt); + if (ret > 0) + e_invalidate(data, ret); + return (ret); +} + +int unix_write(int fd, const void *data, int cnt) +{ + return RPT_SYSCALL(write(fd, data, cnt)); +} + +int dos_write(int fd, unsigned data, int cnt) +{ + int ret; + const unsigned char *d; + unsigned char *buf; + + if (!cnt) + return 0; + buf = alloca(cnt); + if (vga.inst_emu && data >= 0xa0000 && data < 0xc0000) { + memcpy_from_vga(buf, data, cnt); + d = buf; + } else { + d = LINEAR2UNIX(data); + } + ret = unix_write(fd, d, cnt); + g_printf("Wrote %10.10s\n", d); + return (ret); +} + +#define BUF_SIZE 1024 +int com_vsnprintf(char *str, size_t msize, const char *format, va_list ap) +{ + char *s = str; + int i, size; + char scratch[BUF_SIZE]; + + assert(msize <= BUF_SIZE); + size = vsnprintf(scratch, msize, format, ap); + for (i=0; i < size; i++) { + if (s - str >= msize - 1) + break; + if (scratch[i] == '\n') { + *s++ = '\r'; + if (s - str >= msize - 1) + break; + } + *s++ = scratch[i]; + } + if (msize) + *s = 0; + return s - str; +} + +int com_vsprintf(char *str, const char *format, va_list ap) +{ + return com_vsnprintf(str, BUF_SIZE, format, ap); +} + +int com_sprintf(char *str, const char *format, ...) +{ + va_list ap; + int ret; + va_start(ap, format); + + ret = com_vsprintf(str, format, ap); + va_end(ap); + return ret; +} + +int com_vfprintf(int dosfilefd, const char *format, va_list ap) +{ + int size; + char scratch2[BUF_SIZE]; + + size = com_vsprintf(scratch2, format, ap); + if (!size) return 0; + return com_doswrite(dosfilefd, scratch2, size); +} + +int com_vprintf(const char *format, va_list ap) +{ + int size; + char scratch2[BUF_SIZE]; + + size = com_vsprintf(scratch2, format, ap); + if (!size) return 0; + return com_dosprint(scratch2); +} + +int com_fprintf(int dosfilefd, const char *format, ...) +{ + va_list ap; + int ret; + va_start(ap, format); + + ret = com_vfprintf(dosfilefd, format, ap); + va_end(ap); + return ret; +} + +int com_printf(const char *format, ...) +{ + va_list ap; + int ret; + va_start(ap, format); + + ret = com_vprintf(format, ap); + va_end(ap); + return ret; +} + +int com_puts(const char *s) +{ + return com_printf("%s", s); +} + +char *skip_white_and_delim(char *s, int delim) +{ + while (*s && isspace(*s)) s++; + if (*s == delim) s++; + while (*s && isspace(*s)) s++; + return s; +} + +void call_msdos(void) +{ + do_int_call_back(0x21); +} + +int com_doswrite(int dosfilefd, const char *buf32, u_short size) +{ + char *s; + u_short int23_seg, int23_off; + int ret = -1; + + if (!size) return 0; + com_errno = 8; + s = lowmem_alloc(size); + if (!s) return -1; + memcpy(s, buf32, size); + pre_msdos(); + LWORD(ecx) = size; + LWORD(ebx) = dosfilefd; + SREG(ds) = DOSEMU_LMHEAP_SEG; + LWORD(edx) = DOSEMU_LMHEAP_OFFS_OF(s); + LWORD(eax) = 0x4000; /* write handle */ + /* write() can be interrupted with ^C. Therefore we set int0x23 here + * so that even in this case it will return to the proper place. */ + int23_seg = ISEG(0x23); + int23_off = IOFF(0x23); + SETIVEC(0x23, CBACK_SEG, CBACK_OFF); + call_msdos(); /* call MSDOS */ + SETIVEC(0x23, int23_seg, int23_off); /* restore 0x23 ASAP */ + lowmem_free(s); + if (LWORD(eflags) & CF) + com_errno = LWORD(eax); + else + ret = LWORD(eax); + post_msdos(); + return ret; +} + +int com_dosread(int dosfilefd, char *buf32, u_short size) +{ + char *s; + u_short int23_seg, int23_off; + int ret = -1; + + if (!size) return 0; + com_errno = 8; + s = lowmem_alloc(size); + if (!s) return -1; + pre_msdos(); + LWORD(ecx) = size; + LWORD(ebx) = dosfilefd; + SREG(ds) = DOSEMU_LMHEAP_SEG; + LWORD(edx) = DOSEMU_LMHEAP_OFFS_OF(s); + LWORD(eax) = 0x3f00; + /* read() can be interrupted with ^C, esp. when it reads from a + * console. Therefore we set int0x23 here so that even in this + * case it will return to the proper place. */ + int23_seg = ISEG(0x23); + int23_off = IOFF(0x23); + SETIVEC(0x23, CBACK_SEG, CBACK_OFF); + call_msdos(); /* call MSDOS */ + SETIVEC(0x23, int23_seg, int23_off); /* restore 0x23 ASAP */ + if (LWORD(eflags) & CF) { + com_errno = LWORD(eax); + } else { + memcpy(buf32, s, _min(size, LWORD(eax))); + ret = LWORD(eax); + } + post_msdos(); + lowmem_free(s); + return ret; +} + +int com_dosreadcon(char *buf32, u_short size) +{ + u_short rd; + + if (!size) + return 0; + pre_msdos(); + for (rd = 0; rd < size; rd++) { + LWORD(eax) = 0x600; + LO(dx) = 0xff; + call_msdos(); + if (LWORD(eflags) & ZF) + break; + buf32[rd] = LO(ax); + } + post_msdos(); + return rd; +} + +int com_doswritecon(const char *buf32, u_short size) +{ + return com_doswrite(STDOUT_FILENO, buf32, size); +} + +int com_dosprint(const char *buf32) +{ + return com_doswritecon(buf32, strlen(buf32)); +} + +int com_dosopen(const char *name, int flags) +{ + int ret = -1; + int len = strlen(name) + 1; + char *s = lowmem_alloc(len); + strcpy(s, name); + pre_msdos(); + HI(ax) = 0x3d; + switch (flags & O_ACCMODE) { + case O_RDONLY: + default: + LO(ax) = 0; + break; + case O_WRONLY: + LO(ax) = 1; + break; + case O_RDWR: + LO(ax) = 2; + break; + } + if (flags & O_CLOEXEC) + LO(ax) |= 1 << 7; + SREG(ds) = DOSEMU_LMHEAP_SEG; + LWORD(edx) = DOSEMU_LMHEAP_OFFS_OF(s); + LWORD(ecx) = 0; + call_msdos(); + if (LWORD(eflags) & CF) + com_errno = LWORD(eax); + else + ret = LWORD(eax); + post_msdos(); + lowmem_free(s); + return ret; +} + +int com_dosclose(int fd) +{ + int ret = -1; + pre_msdos(); + HI(ax) = 0x3e; + LWORD(ebx) = fd; + call_msdos(); + if (LWORD(eflags) & CF) + com_errno = LWORD(eax); + else + ret = 0; + post_msdos(); + return ret; +} + +int com_bioscheckkey(void) +{ + int ret; + pre_msdos(); + HI(ax) = 1; + do_int_call_back(0x16); + ret = !(LWORD(eflags) & ZF); + post_msdos(); + return ret; +} + +int com_biosgetch(void) +{ + int ret; + do { + ret = com_bioscheckkey(); + } while (!ret); + pre_msdos(); + HI(ax) = 0; + do_int_call_back(0x16); + ret = LO(ax); + post_msdos(); + return ret; +} + +int com_biosread(char *buf32, u_short size) +{ + u_short rd = 0; + int ch; + + if (!size) return 0; + while (rd < size) { + ch = com_biosgetch(); + if (ch == '\b') { + if (rd > 0) { + p_dos_str("\b \b"); + rd--; + } + continue; + } + if (ch != '\r') + buf32[rd++] = ch; + else + buf32[rd++] = '\n'; + p_dos_str("%c", buf32[rd - 1]); + if (ch == '\r' || ch == '\3') + break; + } + return rd; +} + +int com_setcbreak(int on) +{ + int old_b; + pre_msdos(); + LWORD(eax) = 0x3300; + call_msdos(); + old_b = LO(dx); + LO(ax) = 1; + LO(dx) = on; + call_msdos(); + post_msdos(); + return old_b; +} + +/* Output a character to the screen. */ +void char_out(unsigned char ch, int page) +{ + struct vm86_regs saved_regs = REGS; + HI(ax) = 0xe; + LO(ax) = ch; + HI(bx) = page; + LO(bx) = 7; + do_int_call_back(0x10); + REGS = saved_regs; +} + +unsigned char sdb_drive_letter(sdb_t sdb) +{ + return (*(u_char *)&sdb[sdb_drive_letter_off]); +} +char *sdb_template_name(sdb_t sdb) +{ + return ((char *)&sdb[sdb_template_name_off]); +} +char *sdb_template_ext(sdb_t sdb) +{ + return ((char *)&sdb[sdb_template_ext_off]); +} +unsigned char sdb_attribute(sdb_t sdb) +{ + return (*(u_char *)&sdb[sdb_attribute_off]); +} +unsigned short sdb_dir_entry(sdb_t sdb) +{ + return (*(u_short *)&sdb[sdb_dir_entry_off]); +} +unsigned short sdb_p_cluster(sdb_t sdb) +{ + return (*(u_short *)&sdb[sdb_p_cluster_off]); +} +char *sdb_file_name(sdb_t sdb) +{ + return ((char *)&sdb[sdb_file_name_off]); +} +char *sdb_file_ext(sdb_t sdb) +{ + return ((char *)&sdb[sdb_file_ext_off]); +} +unsigned char sdb_file_attr(sdb_t sdb) +{ + return (*(u_char *)&sdb[sdb_file_attr_off]); +} +unsigned short sdb_file_time(sdb_t sdb) +{ + return (*(u_short *)&sdb[sdb_file_time_off]); +} +unsigned short sdb_file_date(sdb_t sdb) +{ + return (*(u_short *)&sdb[sdb_file_date_off]); +} +unsigned short sdb_file_st_cluster(sdb_t sdb) +{ + return (*(u_short *)&sdb[sdb_file_st_cluster_off]); +} +Bit32u sdb_file_size(sdb_t sdb) +{ + return (*(u_int *)&sdb[sdb_file_size_off]); +} + +unsigned short sft_handle_cnt(sft_t sft) +{ + return (*(u_short *)&sft[sft_handle_cnt_off]); +} +unsigned short sft_open_mode(sft_t sft) +{ + return (*(u_short *)&sft[sft_open_mode_off]); +} +unsigned char sft_attribute_byte(sft_t sft) +{ + return (*(u_char *)&sft[sft_attribute_byte_off]); +} +unsigned short sft_device_info(sft_t sft) +{ + return (*(u_short *)&sft[sft_device_info_off]); +} +Bit32u sft_dev_drive_ptr(sft_t sft) +{ + return (*(u_int *)&sft[sft_dev_drive_ptr_off]); +} +unsigned short sft_start_cluster(sft_t sft) +{ + return (*(u_short *)&sft[sft_start_cluster_off]); +} +unsigned short sft_time(sft_t sft) +{ + return (*(u_short *)&sft[sft_time_off]); +} +unsigned short sft_date(sft_t sft) +{ + return (*(u_short *)&sft[sft_date_off]); +} +Bit32u sft_size(sft_t sft) +{ + return (*(u_int *)&sft[sft_size_off]); +} +Bit32u sft_position(sft_t sft) +{ + return (*(u_int *)&sft[sft_position_off]); +} +unsigned short sft_rel_cluster(sft_t sft) +{ + return (*(u_short *)&sft[sft_rel_cluster_off]); +} +unsigned short sft_abs_cluster(sft_t sft) +{ + return (*(u_short *)&sft[sft_abs_cluster_off]); +} +unsigned short sft_directory_sector(sft_t sft) +{ + return (*(u_short *)&sft[sft_directory_sector_off]); +} +unsigned char sft_directory_entry(sft_t sft) +{ + return (*(u_char *)&sft[sft_directory_entry_off]); +} +char *sft_name(sft_t sft) +{ + return ( (char *)&sft[sft_name_off]); +} +char *sft_ext(sft_t sft) +{ + return ( (char *)&sft[sft_ext_off]); +} +unsigned short sft_fd(sft_t sft) +{ + return (*(u_short *)&sft[sft_fd_off]); +} + +dosaddr_t sda_current_dta(sda_t sda) +{ + if (!sda) + return 0; + return (FARADDR((far_t *)&sda[sda_current_dta_off])); +} +unsigned short sda_error_code(sda_t sda) +{ + if (!sda) + return 0; + return (*(u_short *)&sda[4]); +} +unsigned short sda_cur_psp(sda_t sda) +{ + if (!sda) + return 0; + return (*(u_short *)&sda[sda_cur_psp_off]); +} +unsigned char sda_cur_drive(sda_t sda) +{ + if (!sda) + return 0; + return (*(u_char *)&sda[sda_cur_drive_off]); +} +char *sda_filename1(sda_t sda) +{ + if (!sda) + return NULL; + return ((char *)&sda[sda_filename1_off]); +} +char *sda_filename2(sda_t sda) +{ + if (!sda) + return NULL; + return ((char *)&sda[sda_filename2_off]); +} +sdb_t sda_sdb(sda_t sda) +{ + if (!sda) + return NULL; + return ((sdb_t )&sda[sda_sdb_off]); +} +cds_t sda_cds(sda_t sda) +{ + if (!sda) + return NULL; + return ((cds_t)(FARPTR((far_t *)&sda[sda_cds_off]))); +} +unsigned char sda_search_attribute(sda_t sda) +{ + if (!sda) + return 0; + return (*(u_char *)&sda[sda_search_attribute_off]); +} +unsigned char sda_open_mode(sda_t sda) +{ + if (!sda) + return 0; + return (*(u_char *)&sda[sda_open_mode_off]); +} +sdb_t sda_rename_source(sda_t sda) +{ + if (!sda) + return NULL; + return ((sdb_t )&sda[sda_rename_source_off]); +} +char *sda_user_stack(sda_t sda) +{ + if (!sda) + return NULL; + return ((char *)(FARPTR((far_t *)&sda[sda_user_stack_off]))); +} + +/* + * Data for extended open/create operations, DOS 4 or greater: + */ +unsigned short sda_ext_act(sda_t sda) +{ + return (*(u_short *)&sda[sda_ext_act_off]); +} +unsigned short sda_ext_attr(sda_t sda) +{ + return (*(u_short *)&sda[sda_ext_attr_off]); +} +unsigned short sda_ext_mode(sda_t sda) +{ + return (*(u_short *)&sda[sda_ext_mode_off]); +} + +unsigned short psp_parent_psp(psp_t psp) +{ + return (*(u_short *)&psp[0x16]); +} +char *psp_handles(psp_t psp) +{ + return ((char *)(FARPTR((far_t *)&psp[0x34]))); +} + +far_t lol_dpbfarptr(lol_t lol) +{ + return (rFAR_FARt(READ_DWORD((lol)+lol_dpbfarptr_off))); +} +far_t lol_cdsfarptr(lol_t lol) +{ + return (rFAR_FARt(READ_DWORD((lol)+lol_cdsfarptr_off))); +} +char lol_last_drive(lol_t lol) +{ + return (READ_BYTE((lol)+lol_last_drive_off)); +} +// lol_nuldev(lol_t lol) ((lol)+lol_nuldev_off) +// lol_njoined(lol_t lol) ((lol)+lol_njoined_off) diff --git a/src/base/misc/fatfs.c b/src/base/misc/fatfs.c new file mode 100644 index 0000000..150856d --- /dev/null +++ b/src/base/misc/fatfs.c @@ -0,0 +1,1995 @@ +/* + * All modifications in this file to the original code are + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * DANG_BEGIN_MODULE + * + * REMARK + * FAT filesystem emulation (read-only) for DOSEMU. + * /REMARK + * DANG_END_MODULE + * + * Copyright (c) 1997 Steffen Winterfeldt + * + * email: Steffen Winterfeldt + * + * FAT12 support by Stas Sergeev + * + */ + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* + * Debug level. + * 0 - normal / 1 - useful / 2 - too much + */ +#define DEBUG_FATFS 2 + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#define fatfs_msg(x...) d_printf("fatfs: " x) + +#if DEBUG_FATFS >= 1 +#define fatfs_deb(x...) d_printf("fatfs: " x) +#else +#define fatfs_deb(x...) +#endif + +#if DEBUG_FATFS >= 2 +#define fatfs_deb2(x...) d_printf("fatfs: " x) +#else +#define fatfs_deb2(x...) +#endif + +#define FATFS_IMPL + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include "emu.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* RxDOS.3 lsv uses types */ +#ifdef HAVE_LIBBSD +#include +#endif + +#include "int.h" +#include "disks.h" +#include "doshelpers.h" +#include "cpu-emu.h" +#include "dos2linux.h" +#include "utilities.h" +#include "fatfs.h" +#include "fatfs_priv.h" + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +static int read_sec(fatfs_t *, unsigned, unsigned char *buf); +static int read_boot(fatfs_t *, unsigned char *buf); +static int read_fat(fatfs_t *, unsigned, unsigned char *buf); +static int read_root(fatfs_t *, unsigned, unsigned char *buf); +static int read_data(fatfs_t *, unsigned, unsigned char *buf); +static void make_label(fatfs_t *); +static unsigned new_obj(fatfs_t *); +static void scan_dir(fatfs_t *, unsigned); +static char *full_name(fatfs_t *, unsigned, const char *); +static void add_object(fatfs_t *, unsigned, char *); +static unsigned dos_time(time_t *); +static unsigned make_dos_entry(fatfs_t *, const obj_t *, unsigned char **); +static unsigned find_obj(fatfs_t *, unsigned); +static void assign_clusters(fatfs_t *, unsigned, unsigned); +static int read_cluster(fatfs_t *, unsigned, unsigned, unsigned char *buf); +static int read_file(fatfs_t *, unsigned, unsigned, unsigned, + unsigned char *buf); +static int read_dir(fatfs_t *, unsigned, unsigned, unsigned, + unsigned char *buf); +static unsigned next_cluster(fatfs_t *, unsigned); +static void build_boot_blk(fatfs_t *m, unsigned char *b); + +static uint64_t sys_type; +static int sys_done; +static const char *real_config_sys = "CONFIG.SYS"; +static char config_sys[16]; +#define MAX_HOOKS 5 +static void (*sys_hook[MAX_HOOKS])(struct sys_dsc *sfiles, fatfs_t *); +static int sys_hooks_used; + +void fatfs_set_sys_hook(void (*hook)(struct sys_dsc *, fatfs_t *)) +{ + assert(sys_hooks_used < MAX_HOOKS); + sys_hook[sys_hooks_used++] = hook; +} + +#define IX(i, j) ((1 << i##_IDX) | (1 << j##_IDX)) +static const uint64_t MS_D = IX(IO, MSD); +static const uint64_t DR_D = IX(DRB, DRD); +static const uint64_t PC_D = IX(IBMB, IBMD); +static const uint64_t EDR_D = IX(EDRB, EDRD); +static const uint64_t RXO_D = IX(RXOB, RXOD); +static const uint64_t RXM_D = IX(RXMB, RXMD); +static const uint64_t RXN_D = (1 << RXND_IDX); +static const uint64_t MOS_D = IX(MOSB, MOSD); +static const uint64_t FDO_D = (1 << IPL_IDX); +static const uint64_t FD_D = (1 << KER_IDX); +#ifdef USE_FDPP +static const uint64_t FDP_D = (1 << FDP_IDX); +#endif + +static const uint64_t OLDPCD_D = (PC_D | (1 << 25)); +static const uint64_t NEWPCD_D = (PC_D | (1 << 26)); +static const uint64_t OLDDRD_D = DR_D; +/* Most DR-DOS versions have the same filenames as PC-DOS for compatibility + * reasons but have larger file sizes which defeats the PC-DOS old/new logic, + * so we need a special case */ +static const uint64_t MIDDRD_D = (PC_D | (1 << 27)); +static const uint64_t ENHDRD_D = EDR_D; + +static const uint64_t OLDMSD_D = (MS_D | (1 << 28)); +static const uint64_t NECMSD_D = (MS_D | (1 << 29)); +static const uint64_t MIDMSD_D = (MS_D | (1 << 30)); +static const uint64_t NEWMSD_D = (MS_D | (1ULL << 31)); +static const uint64_t OLDMOS_D = (MOS_D | (1ULL << 32)); + +FATFS_EXPORTS + +static const struct sys_dsc i_sfiles[] = { + [IO_IDX] = { "IO.SYS", 1, }, + [MSD_IDX] = { "MSDOS.SYS", 1, FLG_ALLOW_EMPTY }, + [DRB_IDX] = { "DRBIOS.SYS", 1, }, + [DRD_IDX] = { "DRBDOS.SYS", 1, }, + [IBMB_IDX] = { "IBMBIO.COM", 1, }, + [IBMD_IDX] = { "IBMDOS.COM", 1, }, + [EDRB_IDX] = { "DRBIO.SYS", 1, }, + [EDRD_IDX] = { "DRDOS.SYS", 1, }, + [RXOB_IDX] = { "RXDOSBIO.SYS", 1, }, + [RXOD_IDX] = { "RXDOS.SYS", 1, }, + [RXMB_IDX] = { "RXBIO.SYS", 1, }, + [RXMD_IDX] = { "RXDOS.SYS", 1, }, + [RXND_IDX] = { "RXDOS.COM", 1, }, + [MOSB_IDX] = { "$$MOS.SYS", 1, }, + [MOSD_IDX] = { "$$SHELL.SYS", 1, }, + [IPL_IDX] = { "IPL.SYS", 1, }, + [KER_IDX] = { "KERNEL.SYS", 1, }, + [CMD_IDX] = { "COMMAND.COM", 0, }, + [RXCMD_IDX]= { "RXDOSCMD.EXE", 0, }, + [CONF_IDX] = { config_sys, 0, }, + [CONF2_IDX]= { "FDCONFIG.SYS", 0, }, + [CONF3_IDX]= { "DCONFIG.SYS", 0, }, + [CONF4_IDX]= { "FDPPCONF.SYS", 0, }, + [AUT_IDX] = { "AUTOEXEC.BAT", 0, }, + [AUT2_IDX] = { "FDPPAUTO.BAT", 0, }, + [DEMU_IDX] = { "DOSEMU", 0, FLG_ISDIR }, +}; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void fatfs_init(struct disk *dp) +{ + int i; + fatfs_t *f; + int num_sectors = dp->tracks * dp->heads * dp->sectors - dp->start; + + if(dp->fatfs) fatfs_done(dp); + fatfs_msg("init: %s\n", dp->dev_name); + + if(SECTOR_SIZE != 0x200) { + fatfs_msg("init failed: unsupported sector size\n"); + return; + } + + if(!(dp->fatfs = calloc(1, sizeof *f))) { + fatfs_msg("init failed: no memory left\n"); + return; + } + f = dp->fatfs; + + f->ffn = malloc(MAX_DIR_NAME_LEN + MAX_FILE_NAME_LEN + 1); + if(!f->ffn) { + fatfs_msg("init failed: no memory left\n"); + return; + } + f->ffn_obj = 1; /* this object doesn't exist */ + + f->dir = dp->dev_name; + if (dp->floppy) { + switch (dp->default_cmos) { + case THREE_INCH_2880KFLOP: + f->media_id = 0xf0; + f->cluster_secs = 2; + f->root_secs = 15; + f->fat_secs = 9; + break; + case THREE_INCH_FLOPPY: + f->media_id = 0xf0; + f->cluster_secs = 1; + f->root_secs = 14; + f->fat_secs = 9; + break; + case FIVE_INCH_FLOPPY: + f->media_id = 0xf9; + f->cluster_secs = 1; + f->root_secs = 14; + f->fat_secs = 7; + break; + case THREE_INCH_720KFLOP: + f->media_id = 0xf9; + f->cluster_secs = 2; + f->root_secs = 7; + f->fat_secs = 3; + break; + case FIVE_INCH_360KFLOP: + f->media_id = 0xfd; + f->cluster_secs = 2; + f->root_secs = 7; + f->fat_secs = 2; + break; + } + f->fat_type = FAT_TYPE_FAT12; + f->total_secs = dp->tracks * dp->heads * dp->sectors; + } else if (num_sectors <= 4078*8) { + f->media_id = 0xf8; + f->cluster_secs = 8; + f->fat_type = FAT_TYPE_FAT12; + f->total_secs = num_sectors; + f->root_secs = 32; + f->fat_secs = ((f->total_secs / f->cluster_secs + 2) * 3 + 0x3ff) >> 10; + + fatfs_msg("Using FAT12, sectors count=%i\n", f->total_secs); + + } else { + unsigned u; + f->media_id = 0xf8; + f->fat_type = FAT_TYPE_FAT16; + f->total_secs = num_sectors; + f->root_secs = 32; + for (u = 4; u <= 512; u <<= 1) { + if (u * 0xfff0u > f->total_secs) + break; + } + f->cluster_secs = u; + f->fat_secs = ((f->total_secs / f->cluster_secs + 2) * 2 + 0x1ff) >> 9; + + fatfs_msg("Using FAT16, sectors count=%i & cluster_size=%d\n", + f->total_secs, f->cluster_secs); + + } + f->serial = dp->serial; + f->secs_track = dp->sectors; + f->bytes_per_sect = SECTOR_SIZE; + f->heads = dp->heads; + f->reserved_secs = 1; + f->hidden_secs = dp->start; + f->fats = 2; + f->root_entries = f->root_secs << 4; + f->last_cluster = (f->total_secs - f->reserved_secs - f->fats * f->fat_secs + - f->root_secs) / f->cluster_secs + 1; + f->drive_num = dp->drive_num; + + f->obj = NULL; + f->objs = f->alloc_objs = 0; + + f->fd = -1; + f->fd_obj = 0; + + new_obj(f); /* going to be our root dir object */ + if(f->obj == NULL) { + fatfs_msg("init failed: no memory left\n"); + return; + } + + f->got_all_objs = 0; + f->first_free_cluster = 2; + + make_label(f); + + fatfs_deb2("init: volume label set to \"%s\"\n", f->label); + memcpy(f->sfiles, i_sfiles, sizeof(f->sfiles)); + strcpy(config_sys, real_config_sys); + if (config.emusys) { + char *p = strrchr(config_sys, '.'); + if (p && p - config_sys <= 8) + strlcpy(p + 1, config.emusys, 4); + strupperDOS(config_sys); + } + for (i = 0; i < sys_hooks_used; i++) + sys_hook[i](f->sfiles, f); + f->ok = 1; + /* entry 0 not freed, not doing strdup() here */ + f->obj[0].name = f->dir; + f->obj[0].full_name = f->dir; + f->obj[0].is.dir = 1; + scan_dir(f, 0); /* set # of root entries accordingly ??? */ +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +void fatfs_done(struct disk *dp) +{ + unsigned u; + fatfs_t *f; + + fatfs_msg("done: %s\n", dp->dev_name); + + if(!(f = dp->fatfs)) return; + + for(u = 1 ; u < f->objs; u++) { + if(f->obj[u].name) + free(f->obj[u].name); + if(f->obj[u].full_name) + free(f->obj[u].full_name); + } + + if(f->ffn) free(f->ffn); + if(f->boot_sec) free(f->boot_sec); + if(f->obj) free(f->obj); + + free(dp->fatfs); dp->fatfs = NULL; +} + + +/* + * Returns # of read sectors, -1 = sector not found, -2 = read error. + */ +int fatfs_read(fatfs_t *f, unsigned buf, unsigned pos, int len) +{ + int i, l = len; + unsigned char b[0x200]; + + fatfs_deb("read: dir %s, sec %u, len %d\n", f->dir, pos, l); + + if(!f->ok) return -1; + + while(l) { + if((i = read_sec(f, pos, b))) return i; + MEMCPY_2DOS(buf, b, 0x200); + e_invalidate(buf, 0x200); + buf += 0x200; pos++; l--; + } + + return len; +} + + +/* + * Returns # of written sectors, -1 = sector not found, -2 = read error. + */ +int fatfs_write(fatfs_t *f, unsigned buf, unsigned pos, int len) +{ + error("fatfs write ignored: dir %s, sec %u, len %d\n", f->dir, pos, len); + + if(!f->ok) return -1; + + return len; +} + +int fatfs_is_bootable(const fatfs_t *f) +{ + return (f->sys_type != 0); +} + +int fatfs_root_contains(const fatfs_t *f, int file_idx) +{ + if (file_idx >= MAX_SYS_IDX) + return 0; + return f->sys_found[file_idx]; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +int read_sec(fatfs_t *f, unsigned pos, unsigned char *buf) +{ + unsigned u0, u1; + + if(pos == 0) return read_boot(f, buf); + + u0 = f->reserved_secs; + u1 = u0 + f->fat_secs * f->fats; + if(pos >= u0 && pos < u1) { + return read_fat(f, (pos - u0) % f->fat_secs, buf); + } + + u0 = u1; + u1 = u0 + f->root_secs; + if(pos >= u0 && pos < u1) { + return read_root(f, pos - u0, buf); + } + + u0 = u1; + u1 = f->total_secs; + if(pos >= u0 && pos < u1) { + return read_data(f, pos - u0, buf); + } + + return -1; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +int read_fat(fatfs_t *f, unsigned pos, unsigned char *buf) +{ + unsigned epfs, u, u0, u1 = 0, i = 0, nbit = 0, lnb = 0, boffs, bioffs, wb; + + fatfs_deb("dir %s, reading fat sector %d\n", f->dir, pos); + + memset(buf, 0, 0x200); + + if (f->fat_type == FAT_TYPE_FAT12) { + epfs = f->bytes_per_sect * 2 / 3; // 341 + boffs = ((f->bytes_per_sect * 2) % 3) * 4; // 4 + } else { + epfs = f->bytes_per_sect / 2; + boffs = 0; + } + u0 = pos * epfs + ((pos * boffs) / 12); + bioffs = (pos * boffs) % 12; + if(f->got_all_objs && u0 >= f->first_free_cluster) + return 0; + + for(u = 0;; u++) { + u1 = next_cluster(f, u + u0); + fatfs_deb2("cluster %u follows %u\n", u1, u + u0); + if (f->fat_type == FAT_TYPE_FAT12) { + u1 &= 0xfff; + lnb = 12; + } else { + lnb = 16; + } + if (bioffs) { + fatfs_deb2("... offset %u bits\n", bioffs); + u1 >>= bioffs; + lnb -= bioffs; + bioffs = 0; + } + buf[i] |= (u1 << nbit) & 0xff; + wb = _min(8 - nbit, lnb); + u1 >>= wb; + lnb -= wb; + nbit += wb; + i += nbit >> 3; + nbit &= 7; + if (i >= SECTOR_SIZE) break; + if (!lnb) continue; + buf[i] |= u1; + nbit += lnb; + i += nbit >> 3; + nbit &= 7; + if (i >= SECTOR_SIZE) break; + } + + return 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +int read_root(fatfs_t *f, unsigned pos, unsigned char *buf) +{ + fatfs_deb("dir %s, reading root dir sector %d\n", f->dir, pos); + + return read_cluster(f, 0, pos, buf); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +int read_data(fatfs_t *f, unsigned pos, unsigned char *buf) +{ + fatfs_deb("dir %s, reading data sector %d\n", f->dir, pos); + + return read_cluster(f, pos / f->cluster_secs + 2, pos % f->cluster_secs, buf); +} + +static int get_bpb_version(struct on_disk_bpb *bpb) +{ + + if (bpb->v340_400_signature == BPB_SIG_V400) + return 400; + + if (bpb->v340_400_signature == BPB_SIG_V340) + return 340; + + if (bpb->num_sectors_small == 0) { // FAT16B + if (bpb->v300_320_hidden_sectors == bpb->v331_400_hidden_sectors) { + // We know the data following v300_320_hidden_sectors must be greater + // than zero if it's to represent num_sectors_large and so if uint16 + // and uint32 representations of hidden_sectors are the same value we + // must have a uint32 field, so the BPB is v3.31 + return 331; + } else { + // Since v3.00 doesn't support FAT16B it must be v3.20 + return 320; + } + } + + // FAT12 or FAT16 + // We know the data following v300_320_hidden_sectors must be zero to + // represent num_sectors_large on a v320 or v331 BPB, but equally it could + // just be data in a v300 BPB, so we can't distinguish between BPB + // versions 3.00, 3.20, and 3.31. + return 300; +} + +static void set_bpb_common(fatfs_t *f, struct on_disk_bpb *bpb) +{ + bpb->bytes_per_sector = f->bytes_per_sect; + bpb->sectors_per_cluster = f->cluster_secs; + bpb->reserved_sectors = f->reserved_secs; + bpb->num_fats = f->fats; + bpb->num_root_entries = f->root_entries; + bpb->num_sectors_small = (f->total_secs < 65536L) ? f->total_secs : 0; + bpb->media_type = f->media_id; + bpb->sectors_per_fat = f->fat_secs; + bpb->sectors_per_track = f->secs_track; + bpb->num_heads = f->heads; +} + +static void update_geometry(fatfs_t *f, unsigned char *b) +{ + struct on_disk_bpb *bpb = (struct on_disk_bpb *) &b[0x0b]; + int version = get_bpb_version(bpb); + + /* set the part of geometry that is supported by all DOS versions */ + set_bpb_common(f, bpb); + + /* set the geometry with BPB version dependent fields */ + if (bpb->num_sectors_small > 0) { // FAT12 or FAT16 + if (version == 300 || version == 320) { + // Since early versions of BPB on small disks can be indistinguishable, + // we can't be as specific as we would like. However, we don't need to + // as we need only to write the first uint16 i.e v300_320_hidden_sectors + // and the following will be zeros as before. + bpb->v300_320_hidden_sectors = f->hidden_secs; + } else { + bpb->v331_400_hidden_sectors = f->hidden_secs; + bpb->v331_400_num_sectors_large = 0; + } + } else { // FAT16B + if (version == 320) { + // It's unclear how large sectors are represented in a uint16_t + bpb->v300_320_hidden_sectors = f->hidden_secs; + // bpb->v320_num_sectors_large = ???? + } else { + bpb->v331_400_hidden_sectors = f->hidden_secs; + bpb->v331_400_num_sectors_large = f->total_secs; + } + } + + /* set the drive number */ + if (version >= 340) + bpb->v340_400_drive_number = f->drive_num; + else if (version == 331 && f->sys_type != OLDDRD_D) { + // DR_DOS 3.4x has string data in 0x1fd position and doesn't need + // drive number set + b[0x1fd] = f->drive_num; + } + + if (version >= 400) + memcpy(bpb->v400_fat_type, f->fat_type == FAT_TYPE_FAT12 ? "FAT12 " : "FAT16 ", 8); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +int read_boot(fatfs_t *f, unsigned char *b) +{ + fatfs_deb("dir %s, reading boot sector\n", f->dir); + + if(!f->boot_sec) { + return -1; + } + + memcpy(b, f->boot_sec, 0x200); + return 0; +} + + /* + * We use the directory name as volume label. If it's too long, we take + * out path elements from the beginning until less than 12 bytes are left. + * If this doesn't work (last element is > 11 bytes), then we have no label. + * A leading slash is always removed. + */ +void make_label(fatfs_t *f) +{ + int i, j; + char *s = f->dir, sdos[strlen(s) + 1]; + + memset(f->label, ' ', 11); + f->label[11] = 0; + + if(*s == '/') s++; + name_ufs_to_dos(sdos, s); + s = sdos; + i = strlen(s); + if(i == 0) return; + + if(i > 11) { + for(j = 0; j < i; j++) { + if(s[j] == '/' && i - j <= 12) break; + } + if(j != i) { i -= j + 1; s += j + 1; } + else { + char *p = strrchr(s, '/'); + if (!p) + p = s; + s = p + 1; + i = 11; + } + } + + + if(s[i - 1] == '/') i--; + + if(*s == ' ') s++, i--; + + if(i <= 11 && i > 0) { + memcpy(f->label, s, i); + while ((s = strchr(f->label, '.'))) + *s = ' '; + while ((s = strchr(f->label, '/'))) + *s = ' '; + while ((s = strchr(f->label, '-'))) + *s = ' '; + strupperDOS(f->label); + } +} + + +/* + * Returns index of a new object. Zero if something failed. + */ +unsigned new_obj(fatfs_t *f) +{ + void *p; + unsigned new_objs = 2; + + if(f->objs >= f->alloc_objs) { + p = realloc(f->obj, (f->alloc_objs + new_objs) * sizeof *f->obj); + if(p == NULL) { + fatfs_msg("new_obj: out of memory (%u objs)\n", f->alloc_objs); + return 0; + } + f->obj = p; + memset(f->obj + f->alloc_objs, 0, new_objs * sizeof *f->obj); + f->alloc_objs += new_objs; + } + + fatfs_deb2("new_obj: created obj %d (%d left)\n", f->objs, f->alloc_objs - f->objs - 1); + + return f->objs++; +} + +static const char *system_type(uint64_t t) { + switch(t) { + case 0: + return "Non-system partition"; + case MS_D: + return "Unknown MS-DOS"; + case PC_D: + return "Unknown PC-DOS"; + case OLDDRD_D: + return "Old DR-DOS (< 5.0)"; + case MIDDRD_D: + return "Original DR-DOS (>= v5.0 && <= v8.0) || OpenDOS (<= 7.01.06)"; + case ENHDRD_D: + return "Enhanced DR-DOS (>= 7.01.07)"; + case NEWPCD_D: + return "New PC-DOS (>= v4.0)"; + case OLDPCD_D: + return "Old PC-DOS (< v4.0)"; + case NEWMSD_D: + return "New MS-DOS (>= v7.0)"; + case MIDMSD_D: + return "Newer MS-DOS (>= v4.0 && < v7.0)"; + case OLDMSD_D: + return "Old MS-DOS (< v4.0)"; + case NECMSD_D: + return "NEC MS-DOS (3.30)"; + case FDO_D: + return "Old FreeDOS"; + case FD_D: + return "FreeDOS"; +#ifdef USE_FDPP + case FDP_D: + return "FDPP"; +#endif + case RXO_D: + return "RxDOS (< v7.20)"; + case RXM_D: + return "RxDOS (v7.20)"; + case RXN_D: + return "RxDOS (>= v7.23)"; + case MOS_D: + return "PC-MOS/386"; + case OLDMOS_D: + return "PC-MOS 5.01"; + } + + return "Unknown System Type"; +} + +static int fs_prio[MAX_SYS_IDX]; + +static fatfs_t *cur_d; + +static int get_s_idx(const char *name, fatfs_t *f) +{ + int i; + for (i = 0; i < MAX_SYS_IDX; i++) { + if (!f->sfiles[i].name) + continue; + if (strequalDOS(name, f->sfiles[i].name)) + return i; + } + return -1; +} + +static int sys_file_idx(const char *name, fatfs_t *f) +{ + int idx, err; + struct stat sb; + const struct sys_dsc *fp; + const char *path; + + idx = get_s_idx(name, f); + if (idx == -1) + return -1; + fp = &f->sfiles[idx]; + if (!fp->is_sys) + return -1; + path = full_name(f, 0, name); + if (!path) + return -1; + err = stat(path, &sb); + if (err) + return -1; + if (!(S_ISREG(sb.st_mode) || (S_ISDIR(sb.st_mode) && + (fp->flags & FLG_ISDIR)))) + return -1; + if (S_ISREG(sb.st_mode) && !(fp->flags & FLG_ALLOW_EMPTY) && + sb.st_size == 0) + return -1; + return idx; +} + +static int d_filter(const struct dirent *d) +{ + const char *name = d->d_name; + int idx; + + if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) + return 0; + idx = sys_file_idx(name, cur_d); + if (idx != -1) + sys_type |= 1 << idx; + idx = get_s_idx(name, cur_d); + if (idx != -1) + cur_d->sys_found[idx] = 1; + return 1; +} + +static void init_sfiles(void) +{ + int i, sfs = 1; + int sysf_located = 0; + memset(fs_prio, 0, sizeof(fs_prio)); + if((sys_type & MS_D) == MS_D) { + sys_type = MS_D; /* MS-DOS */ + fs_prio[IO_IDX] = sfs++; + fs_prio[MSD_IDX] = sfs++; + sysf_located = 1; + } + if((sys_type & DR_D) == DR_D) { + sys_type = DR_D; /* DR-DOS */ + fs_prio[DRB_IDX] = sfs++; + fs_prio[DRD_IDX] = sfs++; + sysf_located = 1; + } + if((sys_type & EDR_D) == EDR_D) { + sys_type = EDR_D; /* Enhanced DR-DOS (7.01.07+) */ + fs_prio[EDRB_IDX] = sfs++; + fs_prio[EDRD_IDX] = sfs++; + sysf_located = 1; + } + if((sys_type & PC_D) == PC_D) { + sys_type = PC_D; /* PC-DOS */ + fs_prio[IBMB_IDX] = sfs++; + fs_prio[IBMD_IDX] = sfs++; + sysf_located = 1; + } + if((sys_type & RXO_D) == RXO_D) { + sys_type = RXO_D; /* RxDOS (Old naming) */ + fs_prio[RXOB_IDX] = sfs++; + fs_prio[RXOD_IDX] = sfs++; + sysf_located = 1; + } + if((sys_type & RXM_D) == RXM_D) { + sys_type = RXM_D; /* RxDOS (New naming) */ + fs_prio[RXMB_IDX] = sfs++; + fs_prio[RXMD_IDX] = sfs++; + sysf_located = 1; + } + if((sys_type & RXN_D) == RXN_D) { + sys_type = RXN_D; /* RxDOS 7.23+, single-file loading */ + fs_prio[RXND_IDX] = sfs++; + sysf_located = 1; + } + if((sys_type & MOS_D) == MOS_D) { + sys_type = MOS_D; /* PC-MOS/386 */ + fs_prio[MOSB_IDX] = sfs++; + fs_prio[MOSD_IDX] = sfs++; + sysf_located = 1; + } + if((sys_type & FDO_D) == FDO_D) { + sys_type = FDO_D; /* FreeDOS, orig. Patv kernel */ + fs_prio[IPL_IDX] = sfs++; + sysf_located = 1; + } + if((sys_type & FD_D) == FD_D) { + sys_type = FD_D; /* FreeDOS, FD maintained kernel */ + fs_prio[KER_IDX] = sfs++; + sysf_located = 1; + } +#ifdef USE_FDPP + if((sys_type & FDP_D) == FDP_D) { + sys_type = FDP_D; /* FDPP kernel */ + fs_prio[FDP_IDX] = sfs++; + sysf_located = 1; + } +#endif + for (i = 0; i < MAX_SYS_IDX; i++) { + if (!cur_d->sfiles[i].name) + continue; + if (cur_d->sfiles[i].is_sys || !cur_d->sys_found[i]) + continue; + fs_prio[i] = sfs++; + } + cur_d->sys_objs = sfs - 1; + if (!sysf_located) + sys_type = 0; + sys_done = 1; +} + +static int d_compar(const struct dirent **d1, const struct dirent **d2) +{ + const char *name1 = (*d1)->d_name; + const char *name2 = (*d2)->d_name; + int idx1 = get_s_idx(name1, cur_d); + int idx2 = get_s_idx(name2, cur_d); + int prio1, prio2; + if (idx1 == -1 && idx2 == -1) + return alphasort(d1, d2); + if (idx1 == -1) + return 1; + if (idx2 == -1) + return -1; + if (!sys_done) + init_sfiles(); + prio1 = fs_prio[idx1]; + prio2 = fs_prio[idx2]; + if (prio1 && (!prio2 || prio1 < prio2)) + return -1; + if (prio2 && (!prio1 || prio2 < prio1)) + return 1; + return alphasort(d1, d2); +} + +static void set_vol_and_len(fatfs_t *f, unsigned oi) +{ + obj_t *o = f->obj + oi; + struct stat sb; + unsigned u; + if(oi == 0) { + if(*f->label != ' ') { + if((u = new_obj(f))) { /* volume label */ + o = f->obj + oi; + f->obj[u].name = strdup(f->label); + f->obj[u].parent = oi; + f->obj[u].is.label = 1; + f->obj[u].is.not_real = 1; + if(!f->obj[oi].first_child) f->obj[oi].first_child = u; + f->obj[u].dos_dir_size = 0x20; + o->size += 0x20; + if(!stat(f->dir, &sb)) { + f->obj[u].time = dos_time(&sb.st_mtime); + } + fatfs_deb2("added label \"%s\"\n", f->label); + } + } + } + + o = f->obj + oi; + u = f->cluster_secs << 9; + o->len = (o->size + u - 1) / u; +} + +/* + * Reads the directory entries and assigns the object ids. + */ +void scan_dir(fatfs_t *f, unsigned oi) +{ + obj_t *o = f->obj + oi; + char *s, *name; + unsigned u; + int i; + struct dirent **dlist; + int num; + int read_bb; + + // just checking... + if(!o->is.dir || o->size || !o->name || o->is.scanned) { + fatfs_msg("scan_dir: oops #1\n"); + return; + } + + fatfs_deb2("scan_dir: reading \"%s\"\n", o->name); + + o->is.scanned = 1; + + name = full_name(f, oi, ""); + if(!name) { + fatfs_msg("file name too complex: object %u\n", oi); + return; + } + if (!oi) { + sys_type = sys_done = 0; + memset(f->sys_found, 0, sizeof(f->sys_found)); + f->sys_objs = 0; + } + name = strdup(name); + cur_d = f; + num = scandir(name, &dlist, d_filter, d_compar); + free(name); + if (num < 0) { + fatfs_msg("fatfs: scandir failed\n"); + return; + } + if (!sys_done) + init_sfiles(); + + if(oi) { + for(i = 0; i < 2; i++) { + if((u = new_obj(f))) { /* ".", ".." */ + o = f->obj + oi; + f->obj[u].is.dir = 1; + if(i) + f->obj[u].is.parent_dir = 1; + else + f->obj[u].is.this_dir = 1; + f->obj[u].is.not_real = 1; + f->obj[u].parent = oi; + f->obj[u].dos_dir_size = 0x20; + if(!f->obj[oi].first_child) f->obj[oi].first_child = u; + o->size += 0x20; + } + } + } else { + char *buf, *buf_ptr; + int fd, size; + struct stat sb; + + if (sys_type == MS_D) { + s = full_name(f, oi, dlist[0]->d_name); /* io.sys */ + if (s && stat(s, &sb) == 0) { + if((fd = open(s, O_RDONLY)) != -1) { + buf = malloc(sb.st_size + 1); + assert(sb.st_size < PTRDIFF_MAX); // fixes gcc warning + size = read(fd, buf, sb.st_size); + if (size > 0) { + if(buf[0] == 'M' && buf[1] == 'Z') { /* MS-DOS >= 7 */ + sys_type = NEWMSD_D; + } else { /* see if it has a version string */ + buf[size] = '\0'; + for (buf_ptr=buf;buf_ptr < buf + size; buf_ptr++) { + if(strncmp(buf_ptr, "NEC IO.SYS for MS-DOS", 21)==0) { + sys_type = NECMSD_D; + break; + } + if(strncmp(buf_ptr, "Version ", 8) == 0) { + char *vno = buf_ptr+8; + if(*vno >= '1' && *vno <= '3') { + sys_type = OLDMSD_D; + break; + } else if(*vno >= '4'&& *vno <= '6') { + sys_type = MIDMSD_D; + break; + } else { + char sc[21]; + strncpy(sc, buf_ptr, sizeof(sc)); + sc[sizeof(sc)-1] = '\0'; + fatfs_msg("fatfs: unknown version string \"%s\"\n", sc); + } + } + } + } + } + free(buf); + close(fd); + } + if ((sys_type == MS_D) && (sb.st_size <= 26*1024)) { + sys_type = OLDMSD_D; /* unknown but small enough to be < v4 */ + } + } + if (sys_type == MS_D) + sys_type = MIDMSD_D; /* default to v4.x -> v6.x */ + } + + if (sys_type == PC_D) { + /* see if it is PC-DOS or Original DR-DOS */ + s = full_name(f, oi, dlist[0]->d_name); + if (s && stat(s, &sb) == 0) { + if((fd = open(s, O_RDONLY)) != -1) { + buf = malloc(sb.st_size + 1); + assert(sb.st_size < PTRDIFF_MAX); // fixes gcc warning + size = read(fd, buf, sb.st_size); + if (size > 0) { + buf[size] = 0; + buf_ptr = buf; + while (!strstr(buf_ptr, "IBM DOS") && + !strstr(buf_ptr, "PC-DOS") && + !strstr(buf_ptr, "DR-DOS") && + !strstr(buf_ptr, "DR-OpenDOS") && + !strstr(buf_ptr, "Caldera") && + !strstr(buf_ptr, "DIGITAL RESEARCH") && + !strstr(buf_ptr, "Novell") && buf_ptr < buf + size) { + buf_ptr += strlen(buf_ptr) + 1; + } + if (buf_ptr < buf + size) { + if (strstr(buf_ptr, "IBM DOS")) + sys_type = NEWPCD_D; + else if (strstr(buf_ptr, "DR-DOS") || + strstr(buf_ptr, "DR-OpenDOS") || + strstr(buf_ptr, "Caldera") || + strstr(buf_ptr, "Novell") || + strstr(buf_ptr, "DIGITAL RESEARCH")) + sys_type = MIDDRD_D; + else + sys_type = OLDPCD_D; + } + } + free(buf); + close(fd); + } + if ((sys_type == PC_D) && (sb.st_size <= 26*1024)) { + sys_type = OLDPCD_D; /* unknown but small enough to be < v4 */ + } + } + if (sys_type == PC_D) + sys_type = NEWPCD_D; /* default to v4.x -> v7.x */ + } + + if (sys_type == MOS_D) { + /* see if it is old MOS */ + s = full_name(f, oi, dlist[0]->d_name); + if (s && stat(s, &sb) == 0 && sb.st_size == 128880) { + if((fd = open(s, O_RDONLY)) != -1) { + uint32_t buf; + lseek(fd, 0x175, SEEK_SET); + read(fd, &buf, sizeof(buf)); + if (buf == 0x20200105) /* 5.01 */ + sys_type = OLDMOS_D; + close(fd); + } + } + } + + if (!sys_type) { + fatfs_msg("system files not found!\n"); + } else { + f->sys_type = sys_type; + fatfs_msg("system type is \"%s\" (0x%"PRIx64")\n", + system_type(f->sys_type), f->sys_type); + } + + /* load boot block from "boot.blk" file or generate Dosemu's own */ + f->boot_sec = malloc(0x200); + s = full_name(f, oi, "boot.blk"); + read_bb = 0; + if (s && (fd = open(s, O_RDONLY)) != -1) { + if ( + fstat(fd, &sb) == 0 && S_ISREG(sb.st_mode) && sb.st_size == 0x200 && + read(fd, f->boot_sec, 0x200) == 0x200) { + read_bb = 1; + fatfs_msg("fatfs: boot block taken from boot.blk\n"); + update_geometry(f, f->boot_sec); + } + close(fd); + } + if (!read_bb) { + fatfs_msg("fatfs: boot block generated\n"); + build_boot_blk(f, f->boot_sec); + } + } + + for (i = 0; i < num; i++) { + struct dirent *dent = dlist[i]; + add_object(f, oi, dent->d_name); + free(dent); + } + free(dlist); + + set_vol_and_len(f, oi); + if (!oi && f->sys_objs) + assign_clusters(f, 0, f->sys_objs); +} + +int fatfs_get_part_type(const fatfs_t *f) +{ + switch (f->sys_type) { + case 0: + return -1; + case OLDMSD_D: + case NECMSD_D: + case OLDPCD_D: + return 2; + } + return 0; +} + +/* + * Return fully qualified filename. + */ +char *full_name(fatfs_t *f, unsigned oi, const char *name) +{ + char *s = f->ffn; + int i = MAX_DIR_NAME_LEN, j; + unsigned save_oi; + + if(!s || !name || oi >= f->objs) return NULL; + + j = strlen(name); + if(j > MAX_FILE_NAME_LEN) return NULL; +#if 0 + do { + s[i + j] = tolowerDOS(name[j]); + } while (--j >= 0); +#else + strcpy(s + i, name); +#endif + /* directory name cached ? */ + if(oi == f->ffn_obj) { + fatfs_deb2("full_name: %u = \"%s\" (cached)\n", oi, f->ffn_ptr); + return f->ffn_ptr; + } + + save_oi = oi; + f->ffn_obj = 1; + f->ffn_ptr = NULL; + + do { + if(!(name = f->obj[oi].name)) return NULL; + j = strlen(name); + if(j + 1 > i) return NULL; + s[--i] = '/'; + memcpy(s + (i -= j), name, j); + if(!oi) break; + oi = f->obj[oi].parent; + } while(1); + + fatfs_deb2("full_name: %d = \"%s\"\n", save_oi, s + i); + + f->ffn_obj = save_oi; + return f->ffn_ptr = s + i; +} + + +static void _add_object(fatfs_t *f, unsigned parent, char *s, const char *name) +{ + struct stat sb; + obj_t tmp_o = {{0}, 0}; + unsigned u; + + fatfs_deb("trying to add \"%s\":\n", s); + if(stat(s, &sb)) { + fatfs_deb("file not found\n"); + return; + } + + if(!(S_ISDIR(sb.st_mode) || S_ISREG(sb.st_mode))) { + fatfs_deb("entry ignored\n"); + return; + } + + if(S_ISREG(sb.st_mode)) { + tmp_o.size = sb.st_size; + u = f->cluster_secs << 9; + tmp_o.len = (tmp_o.size + u - 1) / u; + if(tmp_o.size == 0) tmp_o.is.not_real = 1; + } + else { + tmp_o.is.dir = 1; + } + + if(!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) tmp_o.is.ro = 1; + tmp_o.parent = parent; + + tmp_o.time = dos_time(&sb.st_mtime); + + tmp_o.full_name = strdup(s); + tmp_o.name = strdup(name); + if(!(u = make_dos_entry(f, &tmp_o, NULL))) { + fatfs_deb("fatfs: make_dos_entry(%s) failed\n", name); + goto err; + } + tmp_o.dos_dir_size = u; + if (parent == 0 && f->obj[parent].size >= f->root_secs << 9) { + static int warned; + fatfs_deb("fatfs: root directory overflow on %s, %i\n", + f->obj[parent].name, f->obj[parent].size); + if (!warned) { + error("fatfs: root directory overflow on %s, %i\n", + f->obj[parent].name, f->obj[parent].size); + warned++; + } + goto err; + } + + if(!(u = new_obj(f))) + goto err; + + if(f->obj[parent].is.dir) { + f->obj[parent].size += tmp_o.dos_dir_size; + if(!f->obj[parent].first_child) f->obj[parent].first_child = u; + } + + f->obj[u] = tmp_o; + + fatfs_deb("added as a %s\n", tmp_o.is.dir ? "directory" : "file"); + return; + +err: + free(tmp_o.name); + free(tmp_o.full_name); +} + +void add_object(fatfs_t *f, unsigned parent, char *nm) +{ + char *s, *name = nm; + + if(!(strcmp(name, ".") && strcmp(name, ".."))) return; + + if (nm[0] == '/') { + s = nm; + name = strrchr(nm, '/') + 1; + } else { + if(!(s = full_name(f, parent, name))) { + fatfs_msg("file name too complex: parent %u, name \"%s\"\n", parent, name); + return; + } + } + if (strcasecmp(name, real_config_sys) == 0 && + strcasecmp(name, config_sys) != 0) { + fatfs_deb("fatfs: skip %s because of emusys\n", name); + return; + } + if (strcasecmp(name, config_sys) == 0 && + strcasecmp(name, real_config_sys) != 0) { + _add_object(f, parent, s, real_config_sys); + fatfs_deb("fatfs: subst %s -> %s\n", name, real_config_sys); + } + + return _add_object(f, parent, s, name); +} + +unsigned dos_time(time_t *tt) +{ + struct tm *t = localtime(tt); + + return (t->tm_sec >> 1) + (t->tm_min << 5) + (t->tm_hour << 11) + + (t->tm_mday << 16) + ((t->tm_mon + 1) << 21) + + ((unsigned)(t->tm_year < 80 ? 0 : t->tm_year - 80) << 25); +} + + +unsigned make_dos_entry(fatfs_t *f, const obj_t *o, unsigned char **e) +{ + static unsigned char dos_ent[0x20]; + const char *s; + char sdos[PATH_MAX + 1]; + unsigned u, start; + int i, j; + + if(e) *e = dos_ent; + memset(dos_ent, 0, sizeof dos_ent); + + s = o->name; + if(o->is.this_dir) { + s = "."; + o = f->obj + o->parent; + } + + start = o->start; + + if(o->is.parent_dir) { + s = ".."; + u = o->parent; + if(f->obj[u].parent) { + u = f->obj[u].parent; + start = f->obj[u].start; + } + else { + start = 0; + } + o = f->obj + u; + } + + name_ufs_to_dos(sdos, s); + s = sdos; + if (!s[0]) + return 0; + + if(o->is.ro) dos_ent[0x0b] += 0x01; + if(o->is.hidden) dos_ent[0x0b] += 0x02; + if(o->is.sys) dos_ent[0x0b] += 0x04; + if(o->is.label) dos_ent[0x0b] += 0x08; + if(o->is.dir) dos_ent[0x0b] += 0x10; + + dos_ent[0x16] = o->time; + dos_ent[0x17] = o->time >> 8; + dos_ent[0x18] = o->time >> 16; + dos_ent[0x19] = o->time >> 24; + dos_ent[0x1a] = start; + dos_ent[0x1b] = start >> 8; + if(!o->is.dir) { + dos_ent[0x1c] = o->size; + dos_ent[0x1d] = o->size >> 8; + dos_ent[0x1e] = o->size >> 16; + dos_ent[0x1f] = o->size >> 24; + } + + if(o->is.label) { + memcpy(dos_ent, o->name, 11); + return 0x20; + } + + memset(dos_ent, ' ', 11); + + if(!strcmp(s, ".")) { *dos_ent = '.'; return 0x20; } + if(!strcmp(s, "..")) { *dos_ent = dos_ent[1] = '.'; return 0x20; } + + for(i = 0, j = 0; i < 8 && s[j] && s[j] != '.'; j++) { + if (s[j] == ' ') + continue; + dos_ent[i++] = toupperDOS(s[j]); + } + + if(!s[j]) + return 0x20; + if (s[j] != '.' || strlen(s + j) > 4) { + /* poor man's lfn mangling */ + char *dot; + if (i > 6) + i = 6; + memcpy(dos_ent + i, "~1", 2); + dot = strchr(s, '.'); + if (!dot) + return 0x20; + j = dot - s; + } + + for(j++, i = 0; s[j] && i < 3; j++) { + if (s[j] == ' ' || s[j] == '.') + continue; + dos_ent[8 + i++] = toupperDOS(s[j]); + } + + return 0x20; +} + + +unsigned find_obj(fatfs_t *f, unsigned clu) +{ + unsigned u; + + if(clu >= f->first_free_cluster) return 0; + + for(u = 0; u < f->objs; u++) { + if( + !f->obj[u].is.not_real && + clu >= f->obj[u].start && + clu < f->obj[u].start + f->obj[u].len + ) break; + } + + if(u == f->objs) return 0; + + return u; +} + + +void assign_clusters(fatfs_t *f, unsigned max_clu, unsigned max_obj) +{ + unsigned u, k; + + fatfs_deb2("assigning up to cluster %u, obj %u\n", max_clu, max_obj); + + if(max_clu == 0 && max_obj == 0) return; + + for(u = 1; u < f->objs; u++) { + if(f->got_all_objs) break; + if(f->first_free_cluster > max_clu && u > max_obj) break; + if(f->obj[u].is.not_real) continue; + if(f->obj[u].start) continue; + if(f->obj[u].is.dir && !f->obj[u].is.scanned) scan_dir(f, u); + f->obj[u].start = f->first_free_cluster; + f->first_free_cluster += f->obj[u].len; + if(f->first_free_cluster > f->last_cluster) { + f->obj[u].start = 0; + f->obj[u].is.not_real = 1; + f->first_free_cluster -= f->obj[u].len; + f->got_all_objs = 1; + fatfs_msg("assign_clusters: file system full\n"); + error("fatfs: file system full, %s\n", f->obj[0].name); + /* remove dangling objects */ + for(k = u; k < f->objs; k++) { + if(f->obj[k].name) + free(f->obj[k].name); + if(f->obj[k].full_name) + free(f->obj[k].full_name); + } + f->objs = u; + } + fatfs_deb("assign_clusters: obj %u, start %u, len %u (%s)\n", + u, f->obj[u].start, f->obj[u].len, f->obj[u].name); + } + + if(u == f->objs) { + fatfs_deb("assign_clusters: got everything\n"); + f->got_all_objs = 1; + } +} + + +/* + * Read sector pos of cluster clu. Reads only *1* sector! + */ +int read_cluster(fatfs_t *f, unsigned clu, unsigned pos, unsigned char *buf) +{ + unsigned u = 0; + + fatfs_deb2("reading cluster %u (+%u)\n", clu, pos); + + if(!f->got_all_objs && clu >= f->first_free_cluster) assign_clusters(f, clu, 0); + + if(clu && !(u = find_obj(f, clu))) { + fatfs_deb2("cluster %u is unused\n", clu); + memset(buf, 0, 0x200); + return 0; + } + + fatfs_deb2("cluster %u is in object %u\n", clu, u); + + return f->obj[u].is.dir ? read_dir(f, u, clu, pos, buf) : + read_file(f, u, clu, pos, buf); +} + + +int read_file(fatfs_t *f, unsigned oi, unsigned clu, unsigned pos, + unsigned char *buf) +{ + obj_t *o = f->obj + oi; + char *s; + off_t ofs; + + if(f->fd_obj && oi != f->fd_obj) { + close(f->fd); + f->fd = -1; + f->fd_obj = 0; + } + + fatfs_deb2("read_file: obj %u, cluster %u, sec %u%s\n", oi, clu, pos, f->fd_obj ? " (fd cached)" : ""); + + if(clu && o->start == 0) return -1; + if(clu < o->start) return -1; + clu -= o->start; + if(clu >= o->len) return -1; + pos = (pos + clu * f->cluster_secs) << 9; + if(pos + 0x200 > o->size) { + memset(buf, 0, 0x200); + } + if(pos >= o->size) return 0; + + s = o->full_name; + fatfs_deb2("going to read 0x200 bytes from file \"%s\", ofs 0x%x \n", s, pos); + + if(f->fd_obj == 0) { + if((f->fd = open(s, O_RDONLY | O_CLOEXEC)) == -1) { + fatfs_deb("fatfs: open %s failed\n", s); + return -1; + } + f->fd_obj = oi; + } + + if((ofs = lseek(f->fd, pos, SEEK_SET)) == -1) return -1; + + if(ofs != pos) return -1; + + if(read(f->fd, buf, 0x200) == -1) return -2; + + return 0; +} + + +/* + * This function may fail if an directory entry is longer than + * 512 bytes. This is currently, however, impossible as all entries + * are exactly 32 bytes long. + */ +int read_dir(fatfs_t *f, unsigned oi, unsigned clu, unsigned pos, + unsigned char *buf) +{ + obj_t *o = f->obj + oi; + unsigned i, j, k, l; + unsigned char *s; + + fatfs_deb2("read_dir: obj %u, cluster %u, sec %u\n", oi, clu, pos); + + if(clu && o->start == 0) return -1; + if(clu < o->start) return -1; + clu -= o->start; + if(clu && clu >= o->len) return -1; + pos = (pos + clu * f->cluster_secs) << 9; + memset(buf, 0, 0x200); + + if(pos >= o->size) return 0; + if(o->first_child == 0) return 0; + + for(i = o->first_child, j = 0; i < f->objs && f->obj[i].parent == oi; i++) { + if(j + f->obj[i].dos_dir_size >= pos) break; + j += f->obj[i].dos_dir_size; + } + + /* should never happen... */ + if(i == f->objs || f->obj[i].parent != oi) return -1; + + if(f->obj[i].start == 0 && !f->obj[i].is.not_real) { + assign_clusters(f, 0, i); + o = f->obj + oi; + } + + + k = 0; + if(pos != j) { + l = make_dos_entry(f, f->obj + i, &s); + if(l + j >= pos) { + k = l + j - pos; + if(k > 0x200) k = 0x200; + memcpy(buf, s, k); + } + i++; + } + + while(i < f->objs && f->obj[i].parent == oi && k < 0x200) { + if(f->obj[i].start == 0 && !f->obj[i].is.not_real) { + assign_clusters(f, 0, i); + o = f->obj + oi; + } + l = make_dos_entry(f, f->obj + i, &s); + if(l + k > 0x200) l = 0x200 - k; + if(l) memcpy(buf + k, s, l); + k += l; + i++; + } + + return 0; +} + + +unsigned next_cluster(fatfs_t *f, unsigned clu) +{ + static unsigned last_start = 0, last_end = 0; + unsigned u = 0; + + if(clu < 2) { + u = 0xffff; + if(clu == 0) *(unsigned char *) &u = f->media_id; + return u; + } + + if(!(clu >= last_start && clu < last_end)) { + if(!f->got_all_objs && clu >= f->first_free_cluster) assign_clusters(f, clu, 0); + if(!(u = find_obj(f, clu))) return 0; + last_start = f->obj[u].start; + last_end = last_start + f->obj[u].len; + if(clu >= last_end) return 0; + } + + if(clu == last_end - 1) return 0xffff; + + return clu + 1; +} + +/* + * This will be called by dos_helper (base/async/int.c) + * when the bootsector is executed. + * We load the systemfile directly and then jump to the entry point. + * A normal bootsector at 0x7c00 won't work, because the file will + * overwrite 0x7c00. Instead of fiddling with moving the bootsector + * 'out of danger' we just do the stuff here in 32-bit space. + */ +void mimic_boot_blk(void) +{ + int fd, size, idx; + unsigned r_o, d_o, sp; + int load_offs; + uint16_t seg; + uint16_t ofs; + + fatfs_t *f = get_fat_fs_by_drive(LO(dx)); + + if (!f || (idx = sys_file_idx(f->obj[1].name, f)) == -1) { + error("BOOT-helper requested, but no systemfile available\n"); + leavedos(99); + return; + } + + if ((fd = open(f->obj[1].full_name, O_RDONLY)) == -1) { + error("cannot open DOS system file %s\n", f->obj[1].full_name); + leavedos(99); + } + + /* 1st root directory sector */ + r_o = f->fats * f->fat_secs + f->reserved_secs + f->hidden_secs; + + /* 1st data sector (= start of 1st system file) */ + d_o = r_o + f->root_secs; + + /* defaults */ + seg = 0x0070; + ofs = 0x0000; + load_offs = 0; + SREG(cs) = seg; + LWORD(eip) = ofs; + + size = f->obj[1].size; + + switch(f->sys_type) { + case NEWMSD_D: /* for IO.SYS, MS-DOS version >= 7 */ + seg = 0x0070; + ofs = 0x0200; + load_offs = -ofs; // different to cs:ip + size = 4 * SECTOR_SIZE; + + LWORD(ebp) = sp = 0x7c00; + pushw(0, sp, (d_o >> 16)); // DX passed on the stack + pushw(0, sp, (d_o & 0xffff)); // AX passed on the stack + LWORD(esp) = sp; + + LWORD(edi) = 0x0002; + SREG(cs) = seg; + LWORD(eip) = ofs; + break; + + case NEWPCD_D: /* MS-DOS 4.0 -> 6.22 / PC-DOS 4.0 -> 7.0 */ + case MIDMSD_D: + // load root directory + read_root(f, 0, LINEAR2UNIX(SEGOFF2LINEAR(0x50, 0x0))); // (0000:0500) + + size = 4 * SECTOR_SIZE; + + LWORD(eax) = d_o >> 16; + LWORD(ebx) = d_o & 0xffff; + LWORD(ecx) = f->media_id << 8; /* ch */ + LWORD(edx) = f->drive_num; + break; + + case OLDPCD_D: /* old MS-DOS & PC-DOS < v4.0 */ + case OLDMSD_D: + // load root directory + read_root(f, 0, LINEAR2UNIX(SEGOFF2LINEAR(0x50, 0x0))); // (0000:0500) + + /* + * MS-DOS 3.10 needs the offset of MSDOS.SYS / 16 passed in AX and other + * versions don't seem to mind. See the issue discussion at + * https://github.com/stsp/dosemu2/issues/278 + */ + LWORD(eax) = ((f->obj[2].start - 2) * f->cluster_secs * SECTOR_SIZE) / 16; + LWORD(ebx) = d_o & 0xffff; + LWORD(ecx) = f->media_id << 8; /* ch */ + LWORD(edx) = f->drive_num; + break; + + case NECMSD_D: + // load root directory + read_root(f, 0, LINEAR2UNIX(SEGOFF2LINEAR(0x50, 0x0))); // (0000:0500) + + LWORD(ebx) = (d_o - f->hidden_secs) & 0xffff; /* Nec special calling */ + LWORD(ecx) = f->media_id << 8; /* ch */ + LWORD(edx) = f->drive_num; + break; + + case OLDDRD_D: /* DR-DOS */ + LWORD(ebx) = f->drive_num; + break; + + case MIDDRD_D: /* DR-DOS with IBM compatibility naming */ + LWORD(edx) = f->drive_num; + LWORD(ebp) = LWORD(esp) = 0x7c00; + break; + + case ENHDRD_D: /* Enhanced DR-DOS 7.01.07+ */ + /* setup done by real boot sector + r + AX=0000 BX=0080 CX=0004 DX=0080 SI=2012 DI=0000 SP=7ba0 BP=7c00 + DS=1fe0 ES=0870 FS=0000 GS=0000 FL=000a3246 + CS:IP=0070:0000 SS:SP=1fe0:7ba0 + + d ds:bp + 1fe0:7c00 EB 3C 90 44 52 44 4F 53 37 2E 30 00 02 08 01 00 k<.DRDOS7.0..... + 1fe0:7c10 02 00 02 37 51 F8 08 00 11 00 04 00 11 00 00 00 ...7Qx.......... + 1fe0:7c20 00 00 00 00 80 00 29 24 23 E7 1B 4E 4F 20 4E 41 ......)$#g.NO NA + 1fe0:7c30 4D 45 20 20 20 20 46 41 54 31 32 20 20 20 FA FC ME FAT12 z| + 1fe0:7c40 31 C0 8E D8 BD 00 7C B8 E0 1F 8E C0 89 EE 89 EF 1@.X=.|8`..@.n.o + 1fe0:7c50 B9 00 01 F3 A5 EA 5E 7C E0 1F 00 00 70 00 8E D8 9..s%j^|`...p..X + 1fe0:7c60 8E D0 8D 66 A0 FB 90 90 90 C7 46 C0 10 00 C7 46 .P.f {...GF@..GF + 1fe0:7c70 C2 01 00 8C 5E C6 C7 46 C4 A0 63 8B 76 1C 8B 7E B...^FGFD c.v..~ + */ + LWORD(edx) = f->drive_num; + + SREG(ds) = SREG(ss) = 0x1fe0; + LWORD(ebp) = 0x7c00; + LWORD(esp) = 0x7ba0; // as per dump above, but not used by 7.01.07 + + /* load boot sector */ + read_boot(f, LINEAR2UNIX(SEGOFF2LINEAR(0x1fe0, 0x7c00))); + break; + + case FDO_D: /* FreeDOS, orig. Patv kernel */ + seg = 0x2000; + ofs = 0x0000; + LWORD(edx) = f->drive_num; + SREG(cs) = seg; + LWORD(eip) = ofs; + break; + + case FD_D: /* FreeDOS, FD maintained kernel */ + int_try_disable_revect(); // assume emufs.sys loaded + seg = 0x0060; + ofs = 0x0000; + LWORD(ebx) = f->drive_num; + SREG(ds) = seg; + SREG(es) = seg; + SREG(ss) = 0x1FE0; + LWORD(esp) = 0x7c00; /* temp stack */ + LWORD(ebp) = 0x7C00; + SREG(cs) = seg; + LWORD(eip) = ofs; + + /* load boot sector to stack */ + read_boot(f, LINEAR2UNIX(SEGOFF2LINEAR(_SS, _SP))); + break; + + case RXO_D: + case RXM_D: + // load root directory + read_root(f, 0, LINEAR2UNIX(SEGOFF2LINEAR(0x50, 0x0))); // (0000:0500) + + LWORD(ecx) = f->media_id << 8; /* ch */ + LWORD(edx) = f->drive_num; + + SREG(ds) = ISEG(0x1e); + LWORD(esi) = IOFF(0x1e); + size = 3 * SECTOR_SIZE; + break; + + case RXN_D: { + char *string_pointer; + const uint32_t loadtop = SEGOFF2LINEAR(0x1FE0, 0x7C00 - 8192); + /* 1FE0h:7C00h -> BPB, -8192: stack reservation */ + struct { + uint32_t FirstCluster; /* (all) first cluster of load file */ + uint32_t FATSector; /* (FAT32,FAT16) loaded sector-in-FAT, or -1 */ + uint16_t FATSeg; /* (all) FAT buffer (FAT12: full FAT), or 0 */ + uint16_t LoadSeg; /* (all) => behind last loaded paragraph */ + uint32_t DataStart; /* (all) data area start sector-in-partition */ + } __attribute__((packed)) *lsv; + + seg = 0x0200; + ofs = 0x0400; /* execute at 200h:400h */ + load_offs = -ofs; /* load to 200h:0 */ + LWORD(ebx) = f->drive_num; + LWORD(edx) = f->drive_num; + SREG(ss) = 0x1FE0; + LWORD(ebp) = 0x7C00; /* -> BPB, -> behind lsv */ + LWORD(esp) = 0x7C00 - sizeof(*lsv); /* -> lsv */ + SREG(cs) = seg; + LWORD(eip) = ofs; + + read_boot(f, LINEAR2UNIX(SEGOFF2LINEAR(0x1FE0, 0x7C00))); /* load BPB */ + /* RxDOS.3 load protocol note: + The top 20 KiB below LMA top, EBDA, and/or RPL space is assumed + to be available to the iniload stage for its own stack and buffers. + As we use the FreeDOS-derived fixed BPB address of 1FE0h:7C00h, + we assume that these 20 KiB are left available. + If we did "auto-BPB" allocation like lDebug's BOOT command, + we would have to insure to reserve the top 20 KiB. + */ + + if ( ((seg << 4) + size) > loadtop ) { + /* (seg << 4) + size -> after end of load */ + size = loadtop - (seg << 4); /* limit loaded size to max */ + } + if (size < 0x600) { + error("too small DOS system file %s\n", f->obj[1].full_name); + leavedos(99); + } + + string_pointer = (char *) + LINEAR2UNIX(SEGOFF2LINEAR(0x1FE0, 0x7C00)) + + sizeof(struct on_disk_bpb) + 0x000B; /* -> after (FAT16) BPB */ + strcpy(string_pointer, "RXDOS COM"); /* give it padded filename */ + + lsv = LINEAR2UNIX(SEGOFF2LINEAR(0x1FE0, 0x7C00 - sizeof(*lsv))); + lsv->FirstCluster = 2; /* first cluster of file */ + lsv->FATSector = -1; /* (FAT32,FAT16) none */ + lsv->FATSeg = 0; /* (FAT12) none */ + lsv->LoadSeg = seg + ((size + 15) >> 4); /* => behind last loaded */ + lsv->DataStart = d_o - f->hidden_secs; + + break; + } + + case MOS_D: /* PC-MOS/386 */ + case OLDMOS_D: /* PC-MOS 5.01 */ + seg = 0x0080; + ofs = 0x0000; + SREG(ds) = 0x2790; + SREG(es) = 0x2000; + SREG(cs) = seg; + LWORD(eip) = ofs; + + /* load boot sector to stack */ + read_boot(f, LINEAR2UNIX(SEGOFF2LINEAR(0x2790, 0x0))); + + // load root directory + read_root(f, 0, LINEAR2UNIX(SEGOFF2LINEAR(0x27b0, 0x0))); + + SREG(ss) = 0x2790; + LWORD(esp) = 0x0700; + break; + + default: + if (f->sfiles[idx].pre_boot && f->sfiles[idx].pre_boot(f->boot_sec) == 0) { + /* load boot sector to stack */ + read_boot(f, LINEAR2UNIX(SEGOFF2LINEAR(_SS, _SP))); + } else { + error("%s boot failed\n", system_type(f->sys_type)); + leavedos(99); + return; + } + break; + } + config.boot_dos = f->sys_type; + c_printf("config.boot_dos set to %x\n", config.boot_dos); + c_printf("config.int_hooks set to %i\n", config.int_hooks); + c_printf("config.force_revect set to %i\n", config.force_revect); + + // load bootfile i.e IO.SYS, IBMBIO.COM, etc + if (!(f->sfiles[idx].flags & FLG_NOREAD)) + dos_read(fd, SEGOFF2LINEAR(_CS, _IP) + load_offs, size); + close(fd); +} + +/* + * Build our own minimal boot block (if no "boot.blk" file was found). + */ +void build_boot_blk(fatfs_t *f, unsigned char *b) +{ + struct on_disk_bpb *bpb = (struct on_disk_bpb *) &b[0x0b]; + + memset(b, 0, 0x200); + b[0x00] = 0xeb; /* jmp 0x7c40 */ + b[0x01] = 0x3e; + b[0x02] = 0x90; + + memcpy(b + 0x03, "IBM 3.3", 8); + + set_bpb_common(f, bpb); + bpb->v331_400_hidden_sectors = f->hidden_secs; + bpb->v331_400_num_sectors_large = bpb->num_sectors_small ? 0 : f->total_secs; + bpb->v340_400_drive_number = f->drive_num; + bpb->v340_400_flags = 0; + bpb->v340_400_signature = BPB_SIG_V400; + bpb->v340_400_serial_number = f->serial; + memcpy(bpb->v400_vol_label, f->label, 11); + memcpy(bpb->v400_fat_type, + f->fat_type == FAT_TYPE_FAT12 ? "FAT12 " : "FAT16 ", 8); + + if ((f->sys_type & MOS_D) == MOS_D) + b[0x3e] = f->drive_num; + if (f->sys_type == OLDMOS_D) { + /* MOS has a bug: if no floppies installed, first HDD goes to A, + * but the boot HDD is always looked up starting from C. So we + * pretend to be a floppy to bypass the buggy code. */ + if (!config.fdisks) + b[0x3e] &= ~0x80; + } + + /* boot loading is done by DOSEMU-HELPER with mimic_boot_blk() function */ + b[0x40] = 0xb8; /* mov ax,0fdh */ + b[0x41] = DOS_HELPER_BOOTSECT; + b[0x42] = 0; + b[0x43] = 0xcd; /* int 0e6h */ + b[0x44] = DOS_HELPER_INT; + + /* This is an instruction that we never execute and is present only to + * convince Norton Disk Doctor that we are a valid boot program */ + b[0x45] = 0xcd; /* int 0x13 */ + b[0x46] = 0x13; + + /* + * IO.SYS from MS-DOS 7+ is currently the only DOS we know of that reuses + * the messages in the boot block, so create a compatible message table here. + */ + if (f->sys_type == NEWMSD_D) { + /* + There are four possible messages that can be defined. The standard + MS-DOS 7.0 boot block only seems to define three. They are: + MSG_1 = "Invalid system disk" + MSG_2 = "Disk I/O error" + MSG_3 points to MSG_1 string data + MSG_4 = "Replace the disk, and then press any key" + + A dump of the salient part of the boot block shows +000180 03 18 01 27 0d 0a 49 6e 76 61 6c 69 64 20 73 79 >...'..Invalid sy< +000190 73 74 65 6d 20 64 69 73 6b ff 0d 0a 44 69 73 6b >stem disk...Disk< +0001a0 20 49 2f 4f 20 65 72 72 6f 72 ff 0d 0a 52 65 70 > I/O error...Rep< +0001b0 6c 61 63 65 20 74 68 65 20 64 69 73 6b 2c 20 61 >lace the disk, a< +0001c0 6e 64 20 74 68 65 6e 20 70 72 65 73 73 20 61 6e >nd then press an< +0001d0 79 20 6b 65 79 0d 0a 00 >y key... < + + Based on the text in lDebug's msg.asm which describes the format well: + - The first four bytes give displacements to the various messages. + - Each message string will be terminated with 0xff, except the last + which will be terminated in the usual manner by 0x00. + - No displacement shall be 0x00 + - The maximum allowed displacement is 0x7f. + - MS-DOS 7.10 from MSW 98 SE seems to have at least 167h (359) bytes + allocated to its buffer for these. + + For Dosemu's purposes it is sufficient to have a single string and have + all of the messages reference it. For this to work it is necessary to pad + the start of the string with 0xff to ensure we don't end up with a MSG_4 + illegal displacement of 0x00. + */ + unsigned t_o = 0x48; + int eos = (0x7ded-0x7c00); // 0x7ded is the maximum limit of string + + snprintf((char *)b + t_o, eos - t_o, "\x04\x03\x02\x01\xff" + "\r\n" + "Sorry, could not load an operating system from\r\n" + "%s\r\n" + "Please check the IO.SYS file for corruption or replace with FreeDOS\r\n" + "\r\n" + "Press any key to retry...\r\n" + , f->dir); + + /* The value here is based on the final location in memory, which is not + * the same as a real MS-DOS boot sector would specify. Here's what lDebug + * has to say on the matter: + ; this pointer points to the MS-DOS 7 message table. + ; note that in actual MS-DOS 7 boot sectors, this value is + ; eg 17Fh, which is incorrectly used with the boot sector's + ; SS to load the table into the initial loader. + */ + b[0x1ee] = (0x7c00 + t_o); /* address of error msg table */ + b[0x1ef] = (0x7c00 + t_o) >> 8; + } + + /* add the boot block signature */ + b[0x1fe] = 0x55; + b[0x1ff] = 0xaa; +} + +const char *fatfs_get_host_dir(const fatfs_t *f) +{ + return f->dir; +} + +struct sys_dsc *fatfs_get_sfiles(fatfs_t *f) +{ + return f->sfiles; +} diff --git a/src/base/misc/fatfs.h b/src/base/misc/fatfs.h new file mode 100644 index 0000000..f72017a --- /dev/null +++ b/src/base/misc/fatfs.h @@ -0,0 +1,66 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +#ifndef FATFS_H +#define FATFS_H + +typedef struct fatfs_s fatfs_t; + +int fatfs_read(fatfs_t *f, unsigned buf, unsigned pos, int len); +int fatfs_write(fatfs_t *f, unsigned buf, unsigned pos, int len); +int fatfs_is_bootable(const fatfs_t *f); +int fatfs_root_contains(const fatfs_t *f, int file_idx); +int fatfs_get_part_type(const fatfs_t *f); +const char *fatfs_get_host_dir(const fatfs_t *f); +struct sys_dsc *fatfs_get_sfiles(fatfs_t *f); + +struct sys_dsc { + const char *name; + int is_sys; + int flags; + int (*pre_boot)(unsigned char *boot_sec); +}; + +#define FLG_ALLOW_EMPTY 1 +#define FLG_COMCOM32 2 +#define FLG_ISDIR 4 +#define FLG_NOREAD 8 +#define FLG_COMCOM64 0x10 + +void fatfs_set_sys_hook(void (*hook)(struct sys_dsc *, fatfs_t *)); + +enum { IO_IDX, MSD_IDX, DRB_IDX, DRD_IDX, + IBMB_IDX, IBMD_IDX, EDRB_IDX, EDRD_IDX, + RXOB_IDX, RXOD_IDX, RXMB_IDX, RXMD_IDX, RXND_IDX, + MOSB_IDX, MOSD_IDX, + IPL_IDX, KER_IDX, + CMD_IDX, RXCMD_IDX, + CONF_IDX, CONF2_IDX, CONF3_IDX, CONF4_IDX, + AUT_IDX, AUT2_IDX, + DEMU_IDX, MAX_SYS_IDX +}; + +#define _FATFS_EXPORTS \ + XPRT(MS_D) \ + XPRT(PC_D) \ + XPRT(FD_D) +#ifdef USE_FDPP +#define FATFS_EXPORTS _FATFS_EXPORTS \ + XPRT(FDP_D) +#else +#define FATFS_EXPORTS _FATFS_EXPORTS +#endif + +#define F_EXTRN(n) extern const int FATFS_##n +#ifndef FATFS_IMPL +#define XPRT(n) F_EXTRN(n); +FATFS_EXPORTS +#else +#define XPRT(n) F_EXTRN(n); \ + const int FATFS_##n = n; +#endif + +#endif diff --git a/src/base/misc/fatfs_priv.h b/src/base/misc/fatfs_priv.h new file mode 100644 index 0000000..043661e --- /dev/null +++ b/src/base/misc/fatfs_priv.h @@ -0,0 +1,74 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +#ifndef FATFS_PRIV_H +#define FATFS_PRIV_H + +#define MAX_DIR_NAME_LEN 256 /* max size of fully qualified path */ +#define MAX_FILE_NAME_LEN 256 /* max size of a file name */ + +typedef struct { + struct { + unsigned dir :1; + unsigned scanned :1; + unsigned ro :1; + unsigned hidden :1; + unsigned sys :1; + unsigned label :1; /* is volume label */ + unsigned not_real :1; /* entry doesn't need a start cluster */ + unsigned this_dir :1; /* is "." entry */ + unsigned parent_dir :1; /* is ".." entry */ + } is; + unsigned start, len; /* start cluster, length in clusters */ + unsigned parent; /* index of parent object */ + unsigned first_child; /* index of first dir entry */ + unsigned size; /* file length in bytes */ + unsigned time; /* date/time in dos format */ + char *name; + char *full_name; + unsigned dos_dir_size; /* size of the dos directory entry */ +} obj_t; + +enum { FAT_TYPE_NONE, FAT_TYPE_FAT12, FAT_TYPE_FAT16, FAT_TYPE_FAT32 }; + +struct fatfs_s { + char *dir; /* base directory name */ + unsigned ok; /* successfully initialized */ + + unsigned secs_track, heads, reserved_secs, hidden_secs, total_secs; + unsigned bytes_per_sect; + unsigned serial; + char label[12]; + unsigned char fat_type; + unsigned char media_id; + unsigned fat_secs; + unsigned fats; + unsigned root_secs; + unsigned root_entries; + unsigned cluster_secs; + unsigned char drive_num; + uint64_t sys_type; /* see fatfs::scan_dir() */ + + unsigned got_all_objs; + unsigned last_cluster; + unsigned first_free_cluster; + unsigned objs, alloc_objs; + unsigned sys_objs; + obj_t *obj; + + char *ffn, *ffn_ptr; /* buffer for file names */ + unsigned ffn_obj; + + unsigned char *boot_sec; + + int fd; + unsigned fd_obj; + + int sys_found[MAX_SYS_IDX]; + struct sys_dsc sfiles[MAX_SYS_IDX]; +}; + +#endif diff --git a/src/base/misc/hma.c b/src/base/misc/hma.c new file mode 100644 index 0000000..48d282b --- /dev/null +++ b/src/base/misc/hma.c @@ -0,0 +1,86 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * Robert Sanders, started 3/1/93 + * + * Hans Lermen, moved 'mapping' to generic mapping drivers, 2000/02/02 + * + */ +#include +#include +#include +#include "memory.h" +#include "emu.h" +#include "hma.h" +#include "mapping.h" +#include "bios.h" +#include "utilities.h" +#include "dos2linux.h" +#include "cpu-emu.h" + +#define HMAAREA 0x100000 + +int a20; + +static void HMA_MAP(int HMA) +{ + int ret; + /* destroy simx86 memory protections first */ + e_invalidate_full(HMAAREA, HMASIZE); + /* Note: MAPPING_HMA is magic, don't be confused by src==dst==HMAAREA here */ + off_t src = HMA ? HMAAREA : 0; + x_printf("Entering HMA_MAP with HMA=%d\n", HMA); + ret = alias_mapping(MAPPING_HMA, HMAAREA, HMASIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, LOWMEM(src)); + if (ret == -1) { + x_printf("HMA: Mapping HMA to HMAAREA %#x unsuccessful: %s\n", + HMAAREA, strerror(errno)); + leavedos(47); + } + x_printf("HMA: %smapped\n", HMA ? "" : "un"); +} + +void set_a20(int enableHMA) +{ + if (!config.hma) + return; + if (a20 == enableHMA) { + g_printf("WARNING: redundant %s of A20!\n", enableHMA ? "enabling" : + "disabling"); + return; + } + + /* to turn the A20 on, one must unmap the "wrapped" low page, and + * map in the real HMA memory. to turn it off, one must unmap the HMA + * and make FFFF:xxxx addresses wrap to the low page. + */ + HMA_MAP(enableHMA); + + a20 = enableHMA; +} + +void HMA_init(void) +{ + /* initially, no HMA */ + int ret = alias_mapping(MAPPING_HMA, HMAAREA, HMASIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, LOWMEM(0)); + if (ret == -1) { + error("HMA: Mapping HMA to HMAAREA %#x unsuccessful: %s\n", + HMAAREA, strerror(errno)); + config.exitearly = 1; + return; + } + a20 = 0; + memcheck_addtype('H', "HMA"); + memcheck_reserve('H', LOWMEM_SIZE, HMASIZE); +} + + +void +hma_exit(void) +{ +} diff --git a/src/base/misc/ioctl.c b/src/base/misc/ioctl.c new file mode 100644 index 0000000..39c0b00 --- /dev/null +++ b/src/base/misc/ioctl.c @@ -0,0 +1,327 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "memory.h" + +#include "emu.h" +#ifdef __linux__ +#include "sys_vm86.h" +#endif +#include "bios.h" +#include "video.h" +#include "timers.h" +#include "cmos.h" +#include "mouse.h" +#include "xms.h" +#include "ipx.h" /* TRB - add support for ipx */ +#include "serial.h" +#include "int.h" +#include "bitops.h" +#include "pic.h" +#include "emudpmi.h" +#include "ioselect.h" + +#ifdef USE_MHPDBG + #include "mhpdbg.h" +#endif + + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +struct io_callback_s { + void (*func)(int, void *); + void *arg; + const char *name; + int fd; + unsigned flags; +}; +#define MAX_FD FD_SETSIZE +static struct io_callback_s io_callback_func[MAX_FD]; +static struct io_callback_s io_callback_stash[MAX_FD]; +static fd_set fds_sigio; +static fd_set fds_masked; + +#if defined(SIG) +static inline int process_interrupt(SillyG_t *sg) +{ + int irq, ret=0; + + if ((irq = sg->irq) != 0) { + h_printf("INTERRUPT: 0x%02x\n", irq); + pic_request(irq); + } + return ret; +} + +void irq_select(void) +{ + if (SillyG) { + int irq_bits = vm86_plus(VM86_GET_IRQ_BITS, 0); + if (irq_bits) { + SillyG_t *sg=SillyG; + while (sg->fd) { + if (irq_bits & (1 << sg->irq)) { + if (process_interrupt(sg)) { + vm86_plus(VM86_GET_AND_RESET_IRQ,sg->irq); + h_printf("SIG: We have an interrupt\n"); + } + } + sg++; + } + } + } +} +#else +void irq_select(void) +{ +} +#endif + +/* */ +/* io_select @@@ 24576 MOVED_CODE_BEGIN @@@ 01/23/96, ./src/base/misc/dosio.c --> src/base/misc/ioctl.c */ + +static int max_fd; +static int syncpipe[2]; +static pthread_t io_thr; +static pthread_mutex_t fun_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t blk_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t fds_mtx = PTHREAD_MUTEX_INITIALIZER; + +static void ioselect_demux(void *arg) +{ + struct io_callback_s *p = arg; + struct io_callback_s f; + + pthread_mutex_lock(&fun_mtx); + f = *p; + pthread_mutex_unlock(&fun_mtx); + /* check if not removed from other thread */ + if (f.func) { + g_printf("GEN: fd %i has data for %s\n", f.fd, f.name); + f.func(f.fd, f.arg); + reset_idle(0); + } +} + +static void io_select(void) +{ + int selrtn, i; + fd_set fds; + int nfds; + + pthread_mutex_lock(&fds_mtx); + fds = fds_sigio; + nfds = max_fd + 1; + pthread_mutex_unlock(&fds_mtx); + + pthread_mutex_lock(&blk_mtx); + for (i = 0; i < nfds; i++) { + if (FD_ISSET(i, &fds_masked)) + FD_CLR(i, &fds); + } + pthread_mutex_unlock(&blk_mtx); + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + selrtn = RPT_SYSCALL(select(nfds, &fds, NULL, NULL, NULL)); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + + switch (selrtn) { + case 0: /* none ready, nothing to do :-) */ + return; + + case -1: /* error (not EINTR) */ + error("bad io_select: %s\n", strerror(errno)); + break; + + default: /* has at least 1 descriptor ready */ + pthread_mutex_lock(&blk_mtx); + for (i = 0; i < nfds; i++) { + if (FD_ISSET(i, &fds_masked)) + continue; + if (FD_ISSET(i, &fds)) { + if (io_callback_func[i].flags & IOFLG_IMMED) { + if (io_callback_func[i].flags & IOFLG_MASKED) + FD_SET(i, &fds_masked); + io_callback_func[i].func(i, io_callback_func[i].arg); + } else { + FD_SET(i, &fds_masked); + add_thread_callback(ioselect_demux, &io_callback_func[i], "ioselect"); + } + } + } + pthread_mutex_unlock(&blk_mtx); + break; + } +} + +/* + * DANG_BEGIN_FUNCTION add_to_io_select + * + * arguments: + * fd - File handle to add to select statement + * want_sigio - want SIGIO (1) if it's available, or not (0). + * + * description: + * Add file handle to one of 2 select FDS_SET's depending on + * whether the kernel can handle SIGIO. + * + * DANG_END_FUNCTION + */ +void +add_to_io_select_new(int new_fd, void (*func)(int, void *), void *arg, + unsigned flags, const char *name) +{ + struct io_callback_s *f = &io_callback_func[new_fd]; + + if (new_fd >= MAX_FD) { + error("Too many IO fds used.\n"); + leavedos(76); + } + + io_callback_stash[new_fd] = *f; + + g_printf("GEN: fd=%d gets SIGIO for %s\n", new_fd, name); + pthread_mutex_lock(&fun_mtx); + f->func = func; + f->arg = arg; + f->name = name; + f->fd = new_fd; + f->flags = flags; + pthread_mutex_unlock(&fun_mtx); + + pthread_mutex_lock(&fds_mtx); + if (new_fd > max_fd) + max_fd = new_fd; + FD_SET(new_fd, &fds_sigio); + pthread_mutex_unlock(&fds_mtx); + + if (!io_callback_stash[new_fd].func) + write(syncpipe[1], "+", 1); +} + +/* + * DANG_BEGIN_FUNCTION remove_from_io_select + * + * arguments: + * fd - File handle to remove from select statement. + * used_sigio - used SIGIO (1) if it's available, or not (0). + * + * description: + * Remove a file handle from one of 2 select FDS_SET's depending + * on whether the kernel can handle SIGIO. + * + * DANG_END_FUNCTION + */ +void remove_from_io_select(int fd) +{ + if (fd < 0 || !io_callback_func[fd].func) { + g_printf("GEN: removing bogus fd %d (ignoring)\n", fd); + return; + } + + pthread_mutex_lock(&fun_mtx); + io_callback_func[fd] = io_callback_stash[fd]; + pthread_mutex_unlock(&fun_mtx); + io_callback_stash[fd].func = NULL; + + if (!io_callback_func[fd].func) { + pthread_mutex_lock(&fds_mtx); + FD_CLR(fd, &fds_sigio); + pthread_mutex_unlock(&fds_mtx); + write(syncpipe[1], "-", 1); + g_printf("GEN: fd=%d removed from select SIGIO\n", fd); + } +} + +static void do_unmask(int fd) +{ + pthread_mutex_lock(&blk_mtx); + FD_CLR(fd, &fds_masked); + pthread_mutex_unlock(&blk_mtx); + write(syncpipe[1], "=", 1); +} + +void ioselect_complete(int fd) +{ + do_unmask(fd); +} + +void ioselect_block(int fd) +{ + assert(io_callback_func[fd].flags & IOFLG_IMMED); + pthread_mutex_lock(&blk_mtx); + FD_SET(fd, &fds_masked); + pthread_mutex_unlock(&blk_mtx); +} + +void ioselect_unblock(int fd) +{ + assert(io_callback_func[fd].flags & IOFLG_IMMED); + do_unmask(fd); +} + +static void *ioselect_thread(void *arg) +{ + while (1) { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + io_select(); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_testcancel(); + } + return NULL; +} + +static void do_syncpipe(int fd, void *arg) +{ + char buf[4096]; + read(fd, buf, sizeof(buf)); +} + +void ioselect_init(void) +{ + struct io_callback_s *f; + struct sched_param parm = { .sched_priority = 1 }; + + FD_ZERO(&fds_sigio); + FD_ZERO(&fds_masked); + pipe(syncpipe); + assert(syncpipe[0] < MAX_FD); + f = &io_callback_func[syncpipe[0]]; + f->func = do_syncpipe; + f->arg = NULL; + f->name = "syncpipe"; + f->fd = syncpipe[0]; + f->flags = IOFLG_IMMED; + max_fd = syncpipe[0]; + FD_SET(syncpipe[0], &fds_sigio); + pthread_create(&io_thr, NULL, ioselect_thread, NULL); + pthread_setschedparam(io_thr, SCHED_FIFO, &parm); +#if defined(HAVE_PTHREAD_SETNAME_NP) && defined(__GLIBC__) + pthread_setname_np(io_thr, "dosemu: io"); +#endif +} + +void ioselect_done(void) +{ + pthread_cancel(io_thr); + pthread_join(io_thr, NULL); + close(syncpipe[1]); +} diff --git a/src/base/misc/mmio_tracing.c b/src/base/misc/mmio_tracing.c new file mode 100644 index 0000000..21c2ccf --- /dev/null +++ b/src/base/misc/mmio_tracing.c @@ -0,0 +1,124 @@ +#include "mmio_tracing.h" + +struct mmio_address_range { + dosaddr_t start, stop; +}; + +struct mmio_tracing_config { + struct mmio_address_range address_ranges[MMIO_TRACING_MAX_REGIONS]; + unsigned valid_ranges; + dosaddr_t min_addr, max_addr; +}; + +static struct mmio_tracing_config mmio_tracing_config; + + +static void mmio_tracing_scrub(void) +{ + if (!EMU_FULL()) + error("MMIO: tracing is only only working for fully simulated cpu. " + "Config must be set to '$_cpu_vm=\"emulated\"', '$_cpu_vm_dpmi=\"emulated\"' and '$_cpu_emu=\"fullsim\"'\n"); +} + +void register_mmio_tracing(dosaddr_t startaddr, dosaddr_t stopaddr) +{ + if (stopaddr < startaddr) { + error("MMIO: address order wrong."); + return; + } + + if (mmio_tracing_config.valid_ranges < MMIO_TRACING_MAX_REGIONS - 1) { + if (mmio_tracing_config.valid_ranges == 0) { + mmio_tracing_config.min_addr = startaddr; + mmio_tracing_config.max_addr = stopaddr; + register_config_scrub(mmio_tracing_scrub); + } else { + if (startaddr < mmio_tracing_config.min_addr) + mmio_tracing_config.min_addr = startaddr; + if (stopaddr > mmio_tracing_config.max_addr) + mmio_tracing_config.max_addr = stopaddr; + } + mmio_tracing_config.address_ranges[mmio_tracing_config.valid_ranges]. + start = startaddr; + mmio_tracing_config.address_ranges[mmio_tracing_config.valid_ranges]. + stop = stopaddr; + mmio_tracing_config.valid_ranges++; + } else + error + ("MMIO: Too many address regions to trace. Increase MMIO_TRACING_MAX_REGIONS to allow some more..."); +} + +bool mmio_check(dosaddr_t addr) +{ + /* to not slow down too much for any other memory access (not in tracing region, + MMIO is usually in some distance to RAM) */ + if ((addr >= mmio_tracing_config.min_addr) + && (addr <= mmio_tracing_config.max_addr)) { + for (unsigned k = 0; k < mmio_tracing_config.valid_ranges; k++) { + if ((addr >= mmio_tracing_config.address_ranges[k].start) && + (addr <= mmio_tracing_config.address_ranges[k].stop)) + return true; + } + } + return false; +} + +uint8_t mmio_trace_byte(dosaddr_t addr, uint8_t value, uint8_t type) +{ + switch (type) { + case MMIO_READ: + F_printf("MMIO: Reading byte at %X: %02X\n", addr, value); + break; + case MMIO_WRITE: + F_printf("MMIO: Writing byte at %X: %02X\n", addr, value); + break; + default: + F_printf("MMIO: Failed. Wrong arguments."); + } + return value; +} + +uint16_t mmio_trace_word(dosaddr_t addr, uint16_t value, uint8_t type) +{ + switch (type) { + case MMIO_READ: + F_printf("MMIO: Reading word at %X: %04X\n", addr, value); + break; + case MMIO_WRITE: + F_printf("MMIO: Writing word at %X: %04X\n", addr, value); + break; + default: + F_printf("MMIO: Failed. Wrong arguments."); + } + return value; +} + +uint32_t mmio_trace_dword(dosaddr_t addr, uint32_t value, uint8_t type) +{ + switch (type) { + case MMIO_READ: + F_printf("MMIO: Reading dword at %X: %08X\n", addr, value); + break; + case MMIO_WRITE: + F_printf("MMIO: Writing dword at %X: %08X\n", addr, value); + break; + default: + F_printf("MMIO: Failed. Wrong arguments."); + } + return value; +} + +uint64_t mmio_trace_qword(dosaddr_t addr, uint64_t value, uint8_t type) +{ + switch (type) { + case MMIO_READ: + F_printf("MMIO: Reading qword at %X: %016"PRIX64"\n", addr, value); + break; + case MMIO_WRITE: + F_printf("MMIO: Writing qword at %X: %016"PRIX64"\n", addr, value); + break; + default: + F_printf("MMIO: Failed. Wrong arguments."); + } + return value; +} diff --git a/src/base/misc/utilities.c b/src/base/misc/utilities.c new file mode 100644 index 0000000..911af65 --- /dev/null +++ b/src/base/misc/utilities.c @@ -0,0 +1,1279 @@ +/* + * Various sundry utilities for dos emulator. + * + */ +#include "emu.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBBSD +#include +#endif + +#include "bios.h" +#include "timers.h" +#include "pic.h" +#include "emudpmi.h" +#include "debug.h" +#include "utilities.h" +#include "dos2linux.h" +#include "dosemu_config.h" +#include "mhpdbg.h" + +/* + * NOTE: SHOW_TIME _only_ should be enabled for + * internal debugging use, but _never_ for productions releases! + * (this would break the port traceing stuff -D+T, which expects + * a machine interpretable and compressed format) + * + * --Hans 990213 + */ +#define SHOW_TIME 0 /* 0 or 1 */ +#ifdef X86_EMULATOR +#include "cpu-emu.h" +#endif + +#ifndef INITIAL_LOGBUFSIZE +#define INITIAL_LOGBUFSIZE 0 +#endif +#ifndef INITIAL_LOGBUFLIMIT +#define INITIAL_LOGFILELIMIT (1024*1024*1024ULL) +#endif + +static pthread_mutex_t log_mtx = PTHREAD_MUTEX_INITIALIZER; + +#ifdef CIRCULAR_LOGBUFFER +#define NUM_CIRC_LINES 32768 +#define SIZ_CIRC_LINES 384 +#define MAX_LINE_SIZE (SIZ_CIRC_LINES-16) +char *logbuf; +static char *loglines[NUM_CIRC_LINES]; +static int loglineidx = 0; +char *logptr; +#else +#define MAX_LINE_SIZE 1000 +static char logbuf_[INITIAL_LOGBUFSIZE+1025]; +char *logptr=logbuf_; +char *logbuf=logbuf_; +#endif +int logbuf_size = INITIAL_LOGBUFSIZE; +int logfile_limit = INITIAL_LOGFILELIMIT; +int log_written = 0; + +static char hxtab[16]="0123456789abcdef"; + +#if 0 +static inline char *prhex8 (char *p, unsigned long v) +{ + int i; + for (i=7; i>=0; --i) { p[i]=hxtab[v&15]; v>>=4; } + p[8]=' '; + return p+9; +} +#endif +#if SHOW_TIME +static char *timestamp (char *p) +{ + unsigned long t; + int i; + +#ifdef DBG_TIME + t = GETusTIME(0); +#else + t = pic_sys_time/1193; +#endif + /* [12345678]s - SYS time */ + { + p[0] = '['; + for (i=8; i>0; --i) + { if (t) { p[i]=(t%10)+'0'; t/=10; } else p[i]='0'; } + p[9]=']'; p[10]=' '; + } + return p+11; +} +#else +#define timestamp(p) (p) +#endif + +int is_printable(const char *s) +{ + int i; + int l = strlen(s); + + for (i = 0; i < l; i++) { + if (!isprint(s[i])) + return 0; + } + return 1; +} + +char *strprintable(char *s) +{ + static char buf[8][128]; + static int bufi = 0; + char *t, c; + + bufi = (bufi + 1) & 7; + t = buf[bufi]; + while (*s) { + c = *s++; + if ((unsigned)c < ' ') { + *t++ = '^'; + *t++ = c | 0x40; + } + else if ((unsigned)c > 126) { + *t++ = 'X'; + *t++ = hxtab[(c >> 4) & 15]; + *t++ = hxtab[c & 15]; + } + else *t++ = c; + } + *t++ = 0; + return buf[bufi]; +} + +char *chrprintable(char c) +{ + char buf[2]; + buf[0] = c; + buf[1] = 0; + return strprintable(buf); +} + +int vlog_printf(int flg, const char *fmt, va_list args) +{ + int i; + static int is_cr = 1; + +#ifdef USE_MHPDBG + if (dosdebug_flags & DBGF_INTERCEPT_LOG) { + va_list args2; + va_copy(args2, args); + /* we give dosdebug a chance to interrupt on given logoutput */ + i = vmhp_log_intercept(flg, fmt, args2); + va_end(args2); + if ((dosdebug_flags & DBGF_DISABLE_LOG_TO_FILE) || !dbg_fd) return i; + } +#endif + + if (!flg || !dbg_fd || +#ifdef USE_MHPDBG + (shut_debug && (flg<10) && !mhpdbg.active) +#else + (shut_debug && (flg<10)) +#endif + ) return 0; + +#ifdef CIRCULAR_LOGBUFFER + logptr = loglines[loglineidx++]; +#endif + { + char *q; + + q = (is_cr? timestamp(logptr) : logptr); + i = vsnprintf(q, MAX_LINE_SIZE, fmt, args); + if (i >= MAX_LINE_SIZE) { /* truncated for buffer overflow */ + i = MAX_LINE_SIZE-2; + q[i++]='\n'; q[i]=0; is_cr=1; + } + else if (i > 0) is_cr = (q[i-1]=='\n'); + i += (q-logptr); + } + +#ifdef CIRCULAR_LOGBUFFER + loglineidx %= NUM_CIRC_LINES; + *(loglines[loglineidx]) = 0; + + if (flg == -1) { + char *p; + int i, k; + k = loglineidx; + for (i=0; i logbuf_size) || (flg == -1)) { + int fsz = logptr-logbuf; + /* writing a big buffer can produce timer bursts, which under DPMI + * can cause stack overflows! + */ + if (terminal_pipe) { + write(terminal_fd, logptr, fsz); + } + if (write(fileno(dbg_fd), logbuf, fsz) < 0) { + if (errno==ENOSPC) leavedos(0x4c4c); + } + logptr = logbuf; +#if 1 + if (logfile_limit) { + log_written += fsz; + if (log_written > logfile_limit) { + fflush(dbg_fd); +#if 1 + ftruncate(fileno(dbg_fd),0); + fseek(dbg_fd, 0, SEEK_SET); + log_written = 0; +#else + fclose(dbg_fd); + shut_debug = 1; + dbg_fd = 0; /* avoid recursion in leavedos() */ + leavedos(0); +#endif + } + } +#endif + } +#endif + return i; +} + +static int in_log_printf=0; + +int log_printf(int flg, const char *fmt, ...) +{ +#ifdef CIRCULAR_LOGBUFFER + static int first = 1; +#endif + va_list args; + int ret; + +#ifdef CIRCULAR_LOGBUFFER + if (first) { + int i; + logbuf = calloc((NUM_CIRC_LINES+4), SIZ_CIRC_LINES); + for (i=0; i%s\n", _new); + return _new; +} + +char *concat_dir(const char *s1, const char *s2) +{ + if (s2[0] == '/') + return strdup(s2); + return _concat_dir(s1, s2); +} + +char *assemble_path(const char *dir, const char *file) +{ + char *s; + wordexp_t p; + int err; + + err = wordexp(dir, &p, WRDE_NOCMD); + assert(!err); + assert(p.we_wordc == 1); + asprintf(&s, "%s/%s", p.we_wordv[0], file); + wordfree(&p); + return s; +} + +char *expand_path(const char *dir) +{ + char *s; + wordexp_t p; + int err; + + err = wordexp(dir, &p, WRDE_NOCMD); + if (err) + return NULL; + if (p.we_wordc != 1) { + wordfree(&p); + return NULL; + } + s = realpath(p.we_wordv[0], NULL); + wordfree(&p); + return s; +} + +char *mkdir_under(const char *basedir, const char *dir) +{ + char *s; + + if (dir) + s = assemble_path(basedir, dir); + else + s = strdup(basedir); + if (!exists_dir(s)) { + if (mkdir(s, S_IRWXU)) { + fprintf(stderr, "can't create dir %s: %s\n", s, + strerror(errno)); + free(s); + s = NULL; + } + } + return s; +} + +char *get_path_in_HOME(const char *path) +{ + char *home = getenv("HOME"); + + if (!home) { + fprintf(stderr, "odd environment, you don't have $HOME, giving up\n"); + leavedos(0x45); + } + if (!path) { + return strdup(home); + } + return assemble_path(home, path); +} + +char *get_dosemu_local_home(void) +{ + return mkdir_under(get_path_in_HOME(".dosemu"), 0); +} + +char *prefix(const char *suffix) +{ + char *p1, *ret, *s, *p; + + if (dosemu_proc_self_exe[0] != '/') { + error("cannot evaluate prefix from relative path %s\n", + dosemu_proc_self_exe); + return assemble_path(PREFIX, suffix); + } + s = strdup(dosemu_proc_self_exe); + p = dirname(s); + assert(p); + p1 = strrchr(p, '/'); + if (p1 && strcmp(p1 + 1, "bin") == 0) { + *p1 = '\0'; + ret = assemble_path(p, suffix); + } else { + error("unable to evaluate prefix from %s\n", dosemu_proc_self_exe); + ret = assemble_path(PREFIX, suffix); + } + free(s); + return ret; +} + +int argparse(char *s, char *argvx[], int maxarg) +{ + int mode = 0; + int argcx = 0; + char delim = 0; + + maxarg --; + for ( ; *s; s++) { + if (!mode) { + if (*s > ' ') { + mode = 1; + argvx[argcx++] = s; + switch (*s) { + case '"': + case '\'': + delim = *s; + mode = 2; + } + if (argcx >= maxarg) + break; + } + } else if (mode == 1) { + if (*s <= ' ') { + mode = 0; + *s = 0x00; + } + } else { + if (*s == delim) mode = 1; + } + } + argvx[argcx] = NULL; + return(argcx); +} + +void subst_file_ext(char *ptr) +{ +#define ext_fix(s) { char *r=(s); \ + while (*r) { *r=toupperDOS(*r); r++; } } + static int subst_sys=2; + + if (ptr == NULL) { + /* reset */ + subst_sys = 2; + return; + } + + /* skip leading drive name and \ */ + if (ptr[1]==':' && ptr[2]=='\\') ptr+=3; + else if (ptr[0]=='\\') ptr++; + + if (subst_sys && config.emusys) { + char config_name[6+1+3+1]; +#if 0 /* + * NOTE: as the method used in fatfs.c can't handle multiple + * files to be faked, we can't do it here, because this would + * confuse more than doing anything valuable --Hans 2001/03/16 + */ + + /* skip the D for DCONFIG.SYS in DR-DOS */ + if (toupperDOS(ptr[0]) == 'D') ptr++; +#endif + ext_fix(config.emusys); + snprintf(config_name, sizeof(config_name), "CONFIG.%-.3s", config.emusys); + if (subst_sys == 1 && !strequalDOS(ptr, config_name) && + !strequalDOS(ptr, "CONFIG.SYS")) { + subst_sys = 0; + } else if (strequalDOS(ptr, "CONFIG.SYS")) { + strcpy(ptr, config_name); + d_printf("DISK: Substituted %s for CONFIG.SYS\n", ptr); + subst_sys = 1; + } + } +} + +void sigalarm_onoff(int on) +{ + static struct itimerval itv_old; +#ifdef X86_EMULATOR + static struct itimerval itv_oldp; +#endif + static struct itimerval itv; + static volatile int is_off = 0; + if (on) { + if (is_off--) { + setitimer(ITIMER_REAL, &itv_old, NULL); +#ifdef X86_EMULATOR + setitimer(ITIMER_VIRTUAL, &itv_oldp, NULL); +#endif + } + } + else if (!is_off++) { + itv.it_interval.tv_sec = itv.it_interval.tv_usec = 0; + itv.it_value = itv.it_interval; + setitimer(ITIMER_REAL, &itv, &itv_old); +#ifdef X86_EMULATOR + setitimer(ITIMER_VIRTUAL, &itv, &itv_oldp); +#endif + } +} + +/* dynamic readlink, adapted from "info libc" */ +char *readlink_malloc (const char *filename) +{ + int size = 50; + int nchars; + char *buffer; + + do { + size *= 2; + nchars = 0; + buffer = malloc(size); + if (buffer != NULL) { + nchars = readlink(filename, buffer, size); + if (nchars < 0) { + free(buffer); + buffer = NULL; + } + } + } while (nchars >= size); + if (buffer != NULL) + buffer[nchars] = '\0'; + return buffer; +} + +void dosemu_error(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + verror(fmt, args); + va_end(args); + gdb_debug(); +} + +#ifdef USE_DL_PLUGINS +static void *do_dlopen(const char *filename, int flags) +{ + void *handle = dlopen(filename, flags | RTLD_NOLOAD); + if (handle) + return handle; + handle = dlopen(filename, flags); + if (handle) + return handle; + error("%s: %s\n", filename, dlerror()); + return NULL; +} + +void *load_plugin(const char *plugin_name) +{ + char *fullname; + char *p; + void *handle; + int ret; + static int warned; + + if (!warned && dosemu_proc_self_exe && + (p = strrchr(dosemu_proc_self_exe, '/'))) { + asprintf(&fullname, "%.*s/libplugin_%s.so", + (int)(p - dosemu_proc_self_exe), + dosemu_proc_self_exe, plugin_name); + if (access(fullname, R_OK) == 0 && + strncmp(fullname, dosemu_plugin_dir_path, + strlen(dosemu_plugin_dir_path)) != 0) { + error("running from build dir must be done via script\n"); + warned++; + } + free(fullname); + + } + ret = asprintf(&fullname, "%s/libplugin_%s.so", + dosemu_plugin_dir_path, plugin_name); + assert(ret != -1); + handle = do_dlopen(fullname, RTLD_LOCAL | RTLD_NOW); + free(fullname); + return handle; +} + +void close_plugin(void *handle) +{ + dlclose(handle); +} +#else +void *load_plugin(const char *plugin_name) +{ + return NULL; +} +void close_plugin(void *handle) +{ +} +#endif + +/* http://media.unpythonic.net/emergent-files/01108826729/popen2.c */ +int popen2_custom(const char *cmdline, struct popen2 *childinfo) +{ + pid_t p; + int pipe_stdin[2], pipe_stdout[2]; + sigset_t oset; + + if(pipe(pipe_stdin)) return -1; + if(pipe(pipe_stdout)) return -1; + +// printf("pipe_stdin[0] = %d, pipe_stdin[1] = %d\n", pipe_stdin[0], pipe_stdin[1]); +// printf("pipe_stdout[0] = %d, pipe_stdout[1] = %d\n", pipe_stdout[0], pipe_stdout[1]); + + signal_block_async_nosig(&oset); + p = fork(); + assert(p >= 0); + if(p == 0) { /* child */ + setsid(); // escape ctty + close(pipe_stdin[1]); + dup2(pipe_stdin[0], 0); + close(pipe_stdin[0]); + close(pipe_stdout[0]); + dup2(pipe_stdout[1], 1); + dup2(pipe_stdout[1], 2); + close(pipe_stdout[1]); + + /* close signals, then unblock */ + /* SIGIOs should not disturb us because they only go + * to the F_SETOWN pid. OTOH disarming SIGIOs will cause this: + * https://github.com/stsp/dosemu2/issues/455 + * ioselect_done(); - not calling. + * Instead we mark all SIGIO fds with FD_CLOEXEC. + */ + signal_done(); + sigprocmask(SIG_SETMASK, &oset, NULL); + + execl("/bin/sh", "sh", "-c", cmdline, NULL); + perror("execl"); + _exit(99); + } + sigprocmask(SIG_SETMASK, &oset, NULL); + close(pipe_stdin[0]); + close(pipe_stdout[1]); + if (fcntl(pipe_stdin[1], F_SETFD, FD_CLOEXEC) == -1) + error("fcntl failed to set FD_CLOEXEC '%s'\n", strerror(errno)); + if (fcntl(pipe_stdout[0], F_SETFD, FD_CLOEXEC) == -1) + error("fcntl failed to set FD_CLOEXEC '%s'\n", strerror(errno)); + childinfo->child_pid = p; + childinfo->to_child = pipe_stdin[1]; + childinfo->from_child = pipe_stdout[0]; + return 0; +} + +int popen2(const char *cmdline, struct popen2 *childinfo) +{ + int ret = popen2_custom(cmdline, childinfo); + if (ret) + return ret; + ret = sigchld_enable_cleanup(childinfo->child_pid); + if (ret) { + error("failed to popen %s\n", cmdline); + pclose2(childinfo); + kill(childinfo->child_pid, SIGKILL); + } + return ret; +} + +int pclose2(struct popen2 *childinfo) +{ + int err; + if (!childinfo->child_pid) + return -1; + err = close(childinfo->from_child); + err |= close(childinfo->to_child); + /* kill process too? */ + childinfo->child_pid = 0; + return err; +} + +/* + * Copyright (c) 1998, 2015 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#if 0 +/* disable this and use libbsd */ +/* + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return(src - osrc - 1); /* count does not include NUL */ +} +#endif + +/* Copyright (c) 1997 Todd C. Miller */ +/* modified by stsp */ +const char *findprog(const char *prog, const char *pathc) +{ + static char filename[PATH_MAX]; + char *p; + char *path; + char dot[] = "."; + int proglen, plen; + struct stat sbuf; + char *pathcpy; + + /* Special case if prog contains '/' */ + if (strchr(prog, '/')) { + if ((stat(prog, &sbuf) == 0) && S_ISREG(sbuf.st_mode) && + access(prog, X_OK) == 0) { + return prog; + } else { +// warnx("%s: Command not found.", prog); + return NULL; + } + } + + if (!pathc) + return NULL; + path = strdup(pathc); + assert(path); + pathcpy = path; + + proglen = strlen(prog); + while ((p = strsep(&pathcpy, ":")) != NULL) { + if (*p == '\0') + p = dot; + + plen = strlen(p); + while (p[plen-1] == '/') + p[--plen] = '\0'; /* strip trailing '/' */ + + if (plen + 1 + proglen >= sizeof(filename)) { +// warnx("%s/%s: %s", p, prog, strerror(ENAMETOOLONG)); + free(path); + return NULL; + } + + snprintf(filename, sizeof(filename), "%s/%s", p, prog); + if ((stat(filename, &sbuf) == 0) && S_ISREG(sbuf.st_mode) && + access(filename, X_OK) == 0) { + free(path); + return filename; + } + } + (void)free(path); + return NULL; +} + +char *strupper(char *src) +{ + char *s = src; + for (; *src; src++) + *src = toupper(*src); + return s; +} + +char *strlower(char *src) +{ + char *s = src; + for (; *src; src++) + *src = tolower(*src); + return s; +} + +int replace_string(struct string_store *store, const char *old, char *str) +{ + int i; + int empty = -1; + + for (i = 0; i < store->num; i++) { + if (old == store->strings[i]) { + free(store->strings[i]); + store->strings[i] = str; + return 1; + } + if (!store->strings[i] && empty == -1) + empty = i; + } + assert(empty != -1); + store->strings[empty] = str; + return 0; +} + +#ifdef HAVE_FOPENCOOKIE +struct tee_struct { + FILE *stream[2]; +}; + +static ssize_t tee_write(void *cookie, const char *buf, size_t size) +{ + struct tee_struct *c = cookie; + fwrite(buf, 1, size, c->stream[0]); + return fwrite(buf, 1, size, c->stream[1]); +} + +static int tee_close(void *cookie) +{ + int ret; + struct tee_struct *c = cookie; + fclose(c->stream[0]); + ret = fclose(c->stream[1]); + free(c); + return ret; +} + +static cookie_io_functions_t tee_ops = { + .write = tee_write, + .close = tee_close, +}; + +FILE *fstream_tee(FILE *orig, FILE *copy) +{ + FILE *f; + struct tee_struct *c = malloc(sizeof(struct tee_struct)); + assert(c); + c->stream[0] = copy; + c->stream[1] = orig; + f = fopencookie(c, "w", tee_ops); + assert(f); + setbuf(f, NULL); + return f; +} +#endif + +static int pts_open(int pty_fd) +{ + int err, pts_fd; + + setsid(); // will have ctty + /* open pts _after_ setsid, or it won't became a ctty */ + err = grantpt(pty_fd); + if (err) { + error("grantpt failed: %s\n", strerror(errno)); + return err; + } + pts_fd = open(ptsname(pty_fd), O_RDWR | O_CLOEXEC); + if (pts_fd == -1) { + error("pts open failed: %s\n", strerror(errno)); + return -1; + } + return pts_fd; +} + +static int pshared_sem_init(pshared_sem_t *sem, unsigned int value) +{ + char sem_name[] = "/dosemu2_psem_%PXXXXXX"; + pshared_sem_t s; + int ret; + + tempname(sem_name, 6); + s = sem_open(sem_name, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, value); + if (!s) + { + /* Stalled sem. Name collision is practically impossible + * because we unlink the sem immediately after creation, + * and also we can't collide with another running dosemu2 + * instance because we use PID in the sem name. */ + error("sem_open %s failed %s\n", sem_name, strerror(errno)); + sem_unlink(sem_name); + s = sem_open(sem_name, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, value); + } + if (!s) + { + error("sem_open failed %s\n", strerror(errno)); + return -1; + } + ret = sem_unlink(sem_name); + if (!ret) + *sem = s; + return ret; +} + +static int pshared_sem_destroy(pshared_sem_t *sem) +{ + int ret = sem_close(*sem); + *sem = NULL; + return ret; +} + +pid_t run_external_command(const char *path, int argc, const char **argv, + int use_stdin, int close_from, int pty_fd) +{ + pid_t pid; + int wt, retval; + sigset_t set, oset; + int pts_fd; + pshared_sem_t pty_sem; + + retval = pshared_sem_init(&pty_sem, 0); + assert(!retval); + signal_block_async_nosig(&oset); + sigprocmask(SIG_SETMASK, NULL, &set); + /* fork child */ + switch ((pid = fork())) { + case -1: /* failed */ + sigprocmask(SIG_SETMASK, &oset, NULL); + g_printf("run_unix_command(): fork() failed\n"); + return -1; + case 0: /* child */ + priv_drop(); + pts_fd = pts_open(pty_fd); + /* Reading master side before slave opened, results in EOF. + * Notify user that reads are now safe. */ + pshared_sem_post(pty_sem); + pshared_sem_destroy(&pty_sem); + if (pts_fd == -1) { + error("run_unix_command(): open pts failed %s\n", strerror(errno)); + _exit(EXIT_FAILURE); + } + close(0); + close(1); + close(2); + if (use_stdin) + dup(pts_fd); + else + open("/dev/null", O_RDONLY); + dup(pts_fd); + dup(pts_fd); + close(pts_fd); + close(pty_fd); + if (close_from != -1) +#ifdef HAVE_CLOSEFROM + closefrom(close_from); +#else + for (; close_from < sysconf(_SC_OPEN_MAX); close_from++) + close(close_from); +#endif + /* close signals, then unblock */ + signal_done(); + /* flush pending signals */ + do { +#ifdef HAVE_SIGTIMEDWAIT + struct timespec to = { 0, 0 }; + wt = sigtimedwait(&set, NULL, &to); +#else + int i; + sigset_t pending; + sigpending(&pending); + wt = -1; + for (i = 1; i < SIGMAX; i++) + if (sigismember(&pending, i) && sigismember(&set, i)) { + sigwait(&set, NULL); + wt = 0; + } +#endif + } while (wt != -1); + sigprocmask(SIG_SETMASK, &oset, NULL); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wincompatible-pointer-types" + retval = execve(path, argv, dosemu_envp); /* execute command */ +#pragma GCC diagnostic pop + error("exec failed: %s\n", strerror(errno)); + _exit(retval); + break; + } + sigprocmask(SIG_SETMASK, &oset, NULL); + /* wait until its safe to read from pty_fd */ + pshared_sem_wait(pty_sem); + pshared_sem_destroy(&pty_sem); + return pid; +} + +/* ripped out of glibc. same as mktemp() but doesn't check the file */ +/* + congruential generator that starts with Var's value + mixed in with a clock's low-order bits if available. */ +typedef uint_fast64_t random_value; +#define RANDOM_VALUE_MAX UINT_FAST64_MAX +#define BASE_62_DIGITS 10 /* 62**10 < UINT_FAST64_MAX */ +#define BASE_62_POWER (62LL * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62) + +static random_value +random_bits (random_value var) +{ + struct timespec tv; + clock_gettime (CLOCK_MONOTONIC, &tv); + var ^= tv.tv_nsec; + return 2862933555777941757 * var + 3037000493; +} + +/* These are the characters used in temporary file names. */ +static const char letters[] = +"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + +int tempname(char *tmpl, size_t x_suffix_len) +{ + int suffixlen = 0; + size_t len; + char *XXXXXX, *p; + + /* A random variable. The initial value is used only the for fallback path + on 'random_bits' on 'getrandom' failure. Its initial value tries to use + some entropy from the ASLR and ignore possible bits from the stack + alignment. */ + random_value v = ((uintptr_t) &v) / (sizeof(long)); + + /* How many random base-62 digits can currently be extracted from V. */ + int vdigits = 0; + + /* Least unfair value for V. If V is less than this, V can generate + BASE_62_DIGITS digits fairly. Otherwise it might be biased. */ + random_value const unfair_min + = RANDOM_VALUE_MAX - RANDOM_VALUE_MAX % BASE_62_POWER; + + len = strlen (tmpl); + if (len < x_suffix_len + suffixlen + || strspn (&tmpl[len - x_suffix_len - suffixlen], "X") < x_suffix_len) + { + return -1; + } + + if ((p = strstr(tmpl, "%PX"))) { + /* reserve at least 1 X */ + int plen = snprintf(p, x_suffix_len + 2, "%02i", getpid()); + if (plen >= x_suffix_len + 2) + return -1; + assert(p[plen] == '\0'); // snprintf's trailing 0 + p[plen] = 'X'; + assert(plen >= 2); + x_suffix_len -= plen - 2; + } + + /* This is where the Xs start. */ + XXXXXX = &tmpl[len - x_suffix_len - suffixlen]; + + { + for (size_t i = 0; i < x_suffix_len; i++) + { + if (vdigits == 0) + { + do + { + v = random_bits (v); + } + while (unfair_min <= v); + + vdigits = BASE_62_DIGITS; + } + + XXXXXX[i] = letters[v % 62]; + v /= 62; + vdigits--; + } + } + + return 0; +} + +int mktmp_in(char *dir_tmpl, const char *fname, mode_t mode) +{ + int fd; + char *p; + char *d = mkdtemp(dir_tmpl); + if (!d) + return -1; + p = assemble_path(d, fname); + fd = open(p, O_CREAT | O_RDWR | O_EXCL, mode); + free(p); + return fd; +} diff --git a/src/base/mouse/Makefile b/src/base/mouse/Makefile new file mode 100644 index 0000000..cd6d8ec --- /dev/null +++ b/src/base/mouse/Makefile @@ -0,0 +1,27 @@ +top_builddir=../../.. +include $(top_builddir)/Makefile.conf + +# +# This is the Makefile for the mouse-subdirectory of the DOS-emulator +# for Linux. +# $Id$ + + +# The following variables may go into the Makfile in the parent-directory + +ALL_CPPFLAGS+=$(DEFINES) + +# Insert all source- and header-files here. + + +CFILES = mouse.c mouseint.c mousevid.c gcursor.c mousedrv.c +DEPENDS=$(CFILES:.c=.d) +HFILES = mousevid.h +ALL = $(CFILES) $(HFILES) +OBJS = $(CFILES:.c=.o) + +all: lib + +install: + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/mouse/gcursor.c b/src/base/mouse/gcursor.c new file mode 100644 index 0000000..a90a103 --- /dev/null +++ b/src/base/mouse/gcursor.c @@ -0,0 +1,516 @@ +/* DOSEMU mouse graphics cursor code */ +/* written by David Etherton, etherton@netcom.com. */ + +#include +#include +#include + +#include "vc.h" +#include "port.h" +#include "vgaemu.h" +#include "mousevid.h" +#include "mouse.h" +#include "gcursor.h" + +/* store pre-shifted versions of our cursor and screen masks for speed. */ +/* this allows us to treat our cursor draws as (masked) byte transfers, + removing the expensive shifting out of the speed-sensitive loops */ +/* (the mouse cursor only needs to be clipped to the screen extents, which + are always on byte boundaries) */ +/* A 16x16 cursor at... + 8 ppb is 3 bytes/line * 16 lines * 8 shifts, or 384 bytes + 4 ppb is 5 bytes/line * 16 lines * 4 shifts, or 320 bytes + 1 ppb is 16 bytes/line * 16 lines * 1 shift, or 256 bytes */ +static unsigned char screenmasks[384]; +static unsigned char cursormasks[384]; +static struct mousevideoinfo mouse_current_video; +static int get_current_graphics_video_mode(void); + +static void +realize_mask(unsigned char *dest, unsigned short *src, int org, int vga_val) +{ + static int pixelsPerByte[] = { 0, 8, 4, 8, 1 }; + static int bytesPerLine[] = { 0, 3, 5, 3, 16 }; + int ppb = pixelsPerByte[org]; + int pass, x, y; + int fill1s = (vga_val == 255); /* are we doing screen mask? */ +#if 0 + unsigned char *orig_dest = dest; +#endif + + memset(dest,0,384); + + for (pass=0; pass < ppb; pass++) { + for (y=0; y<16; y++) { + unsigned mask; + unsigned srcy = src[y]; + static unsigned char lut8[] = + { 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01 }; + static unsigned char lut4[] = { 0xC0,0x30,0x0C,0x03 }; + + if (ppb == 8) { + for (x=0, mask=0x8000; x<16; x++,mask>>=1) + if (mask & srcy) + dest[(x + pass) >> 3] |= + lut8[(x + pass) & 7]; + if (fill1s) { + dest[0] |= 0xFF << (8 - pass); + dest[2] |= 0xFF >> pass; + } + } + else if (ppb == 4) { + for (x=0, mask=0x8000; x<16; x++,mask>>=1) + if (mask & srcy) + dest[(x + pass) >> 2] |= + lut4[(x + pass) & 3]; + if (fill1s) { + dest[0] |= 0xFF << (8 - (pass + pass)); + dest[4] |= 0xFF >> (pass + pass); + } + } + else { + for (x=0, mask=0x8000; x<16; x++,mask>>=1) + if (mask & srcy) + dest[x] = vga_val; + } + dest += bytesPerLine[org]; + } + } +#if 0 +{ + int x,y; + m_printf("INPUT MASK:\n"); + for (y=0; y<16; y++) + m_printf("%04x\n",src[y]); + m_printf("org = %d COMPILED MASK DUMP:\n",org); + for (y=0; y<16; y++) { + for (x=0; x> 1) * 80) + offset; +} + +/* if we're on scanline y, return the offset to the start of scanline (y+1) */ +static inline +int cga_nextscan(int y) +{ + if (y & 1) + return - 0x2000 + 80; + else + return 0x2000; +} + + +static void +cga2_bitblt(int x,int y,int width,int height,int toscr,int bpl, + unsigned char *bs) +{ + int byteWidth = ((x + width - 1) >> 3) - (x >> 3) + 1; + unsigned scr = GRBASE + cga_scanline(y) + (x >> 3); + + if (toscr) + while (height--) { + memcpy_to_vga(scr,bs,byteWidth); + bs += byteWidth; + scr += cga_nextscan(y++); + } + else + while (height--) { + memcpy_from_vga(bs,scr,byteWidth); + bs += byteWidth; + scr += cga_nextscan(y++); + } +} + + +static void +cga4_bitblt(int x,int y,int width,int height,int toscr,int bpl, + unsigned char *bs) +{ + int byteWidth = ((x + width - 1) >> 2) - (x >> 2) + 1; + unsigned scr = GRBASE + cga_scanline(y) + (x >> 2); + + if (toscr) + while (height--) { + memcpy_to_vga(scr,bs,byteWidth); + bs += byteWidth; + scr += cga_nextscan(y++); + } + else + while (height--) { + memcpy_from_vga(bs,scr,byteWidth); + bs += byteWidth; + scr += cga_nextscan(y++); + } +} + + +static void +ega16_bitblt(int x,int y,int width,int height,int toscr,int bpl, + unsigned char *bs) +{ + int byteWidth = ((x + width - 1) >> 3) - (x >> 3) + 1; + int plane; + unsigned scr = GRBASE + (y * bpl) + (x >> 3); + + SAVE_EGA_STATE + + for (plane=0; plane<4; plane++) { + int h = height; + unsigned s = scr; + + if (toscr) { + /* only enable writes to a single plane */ + write_ega_reg(SEQ_I,0x2,1<> 3) + (yofs * 3) + (x & 7) * 48; + int bwidth = ((x + width - 1) >> 3) - (x >> 3); + unsigned scr = GRBASE + cga_scanline(y) + (x >> 3); + + while (height--) { + unsigned s = scr; + int i = index; + switch (bwidth) { + case 2: do_pixel + case 1: do_pixel + case 0: do_pixel + } + scr += cga_nextscan(y++); + index += 3; + } +} + + +static void +cga4_cursor(int x,int y,int width,int height,int xofs,int yofs,int bpl) +{ + int index = (xofs >> 2) + (yofs * 5) + (x & 3) * 80; + int bwidth = ((x + width - 1) >> 2) - (x >> 2); + unsigned scr = GRBASE + cga_scanline(y) + (x >> 2); + + + while (height--) { + unsigned s = scr; + int i = index; + switch (bwidth) { + case 4: do_pixel + case 3: do_pixel + case 2: do_pixel + case 1: do_pixel + case 0: do_pixel + } + scr += cga_nextscan(y++); + index += 5; + } +} + + +static void +ega16_cursor(int x,int y,int width,int height,int xofs,int yofs,int bpl) +{ + int index = (xofs >> 3) + (yofs * 3) + (x & 7) * 48; + int bwidth = ((x + width - 1) >> 3) - (x >> 3); + unsigned scr = GRBASE + (y * bpl) + (x >> 3); + int plane; + + SAVE_EGA_STATE + + /* repeat once for each plane. requires more vram accesses but + fewer port writes. these days that's usually a win. */ + for (plane = 0; plane < 4; plane++) { + int h = height; + int ii = index; + unsigned ss = scr; + + /* enable reads from this plane */ + write_ega_reg(GRA_I,0x4,plane); + + /* enable writes to this plane */ + write_ega_reg(SEQ_I,0x2,1<drawn && get_current_graphics_video_mode()) { + mouse_blitters[mouse_current_video.organization]( + erase->x,erase->y, + erase->width,erase->height, + 1,mouse_current_video.bytesperline, + erase->backingstore.graphics); + erase->drawn = FALSE; + } +} + + +void +draw_graphics_cursor(int x,int y,int hotx,int hoty,int width,int height, + mouse_erase_t *erase) +{ + int xofs, yofs; + + if (!get_current_graphics_video_mode()) + return; + + /* adjust for hot spot */ + erase->x = x - hotx; + /* left clip */ + if (erase->x < 0) { + width += erase->x; + xofs = -erase->x; + erase->x = 0; + } + else { /* right clip? (never need both) */ + xofs = 0; + if (erase->x + width > mouse_current_video.width) + width = mouse_current_video.width - erase->x; + } + + /* adjust for hot spot */ + erase->y = y - hoty; + /* top clip */ + if (erase->y < 0) { + height += erase->y; + yofs = -erase->y; + erase->y = 0; + } + else { /* bottom clip? (never need both) */ + yofs = 0; + if (erase->y + height > mouse_current_video.height) + height = mouse_current_video.height - erase->y; + } + + /* remember final (clipped) width and height */ + erase->width = width; + erase->height = height; + + if (width > 0 && height > 0) { + /* remember contents beneath cursor. */ + mouse_blitters[mouse_current_video.organization]( + erase->x,erase->y, + erase->width,erase->height, + 0,mouse_current_video.bytesperline, + erase->backingstore.graphics); + + /* draw new cursor */ + mouse_cursors[mouse_current_video.organization]( + erase->x,erase->y, + erase->width,erase->height, + xofs,yofs, + mouse_current_video.bytesperline); + + /* remember that we need to erase it. */ + erase->drawn = TRUE; + } +} diff --git a/src/base/mouse/mouse.c b/src/base/mouse/mouse.c new file mode 100644 index 0000000..b85a909 --- /dev/null +++ b/src/base/mouse/mouse.c @@ -0,0 +1,2422 @@ +/* mouse.c for the DOS emulator + * Robert Sanders, gt8134b@prism.gatech.edu + * + */ + +#include "init.h" +#include "emu.h" +#include "bios.h" +#include "int.h" +#include "memory.h" +#include "video.h" /* video base address */ +#include "serial.h" +#include "timers.h" +#include "virq.h" +#include "coopth.h" +#include "utilities.h" +#include "doshelpers.h" +#include "lowmem.h" +#include "mouse.h" + +/* This is included for video mode support. Please DO NOT remove ! + * mousevid.h will become part of VGA emulator package */ +#include "mousevid.h" +#include "gcursor.h" +#include "vgaemu.h" + +#define SETHIGH(x, v) HI_BYTE_d(x) = (v) +#define SETLO_WORD(x, v) LO_WORD(x) = (v) +#define SETLO_BYTE(x, v) LO_BYTE_d(x) = (v) +#define SETWORD(x, v) SETLO_WORD(x, v) + +#define MOUSE_RX mouse_roundx(get_mx()) +#define MOUSE_RY mouse_roundy(get_my()) +#define MOUSE_MINX 0 +#define MOUSE_MINY 0 +#define INIT_SPEED_X 8 +#define INIT_SPEED_Y 16 + +static int mouse_tid; + +static void reset_scale(void); + +static int mickeyx(void) +{ + return (mouse.unscm_x / (mouse.px_range * 8)); +} + +static int mickeyy(void) +{ + return (mouse.unscm_y / (mouse.py_range * 8)); +} + +static int _get_mx(void) +{ + return mouse.unsc_x / mouse.px_range; +} + +static int get_mx(void) +{ + static int warned; + int ret = _get_mx(); + if (mouse.virtual_maxx && ret > mouse.virtual_maxx && !warned) { + dosemu_error("MOUSE: X out of range: %i > %i, %i\n", + ret, mouse.virtual_maxx, mouse.px_range); + warned++; + } + return ret; +} + +static int _get_my(void) +{ + return mouse.unsc_y / mouse.py_range; +} + +static int get_my(void) +{ + static int warned; + int ret = _get_my(); + if (mouse.virtual_maxy && ret > mouse.virtual_maxy && !warned) { + dosemu_error("MOUSE: Y out of range: %i > %i, %i\n", + ret, mouse.virtual_maxy, mouse.py_range); + warned++; + } + return ret; +} + +static void set_px_ranges(int x_range, int y_range) +{ + /* need to update unscaled coords */ + mouse.unsc_x = mouse.unsc_x * x_range / mouse.px_range; + mouse.unsc_y = mouse.unsc_y * y_range / mouse.py_range; + mouse.unscm_x = mouse.unscm_x * x_range / mouse.px_range; + mouse.unscm_y = mouse.unscm_y * y_range / mouse.py_range; + + m_printf("MOUSE: setting px ranges (%i, %i) -> (%i, %i)\n", + mouse.px_range, mouse.py_range, x_range, y_range); + mouse.px_range = x_range; + mouse.py_range = y_range; +} + +static +void mouse_cursor(int), mouse_pos(void), mouse_setpos(void), + mouse_setxminmax(void), mouse_setyminmax(void), mouse_set_tcur(void), + mouse_set_gcur(void), mouse_setsub(void), mouse_bpressinfo(void), mouse_brelinfo(void), + mouse_mickeys(void), mouse_version(void), mouse_enable_internaldriver(void), + mouse_disable_internaldriver(void), + mouse_software_reset(void), + mouse_getgeninfo(void), mouse_exclusionarea(void), mouse_setcurspeed(void), + mouse_storestate(void), mouse_restorestate(void), + mouse_getmaxcoord(void), mouse_getstorereq(void), mouse_setsensitivity(void), + mouse_detsensitivity(void), mouse_detstatbuf(void), mouse_excevhand(void), + mouse_largecursor(void), mouse_doublespeed(void), mouse_alternate(void), + mouse_detalternate(void), mouse_hardintrate(void), mouse_disppage(void), + mouse_detpage(void), mouse_getmaxminvirt(void); +static void scale_coords3(int x, int y, int x_range, int y_range, + int speed_x, int speed_y, long long *s_x, long long *s_y); +static void scale_coords(int x, int y, int x_range, int y_range, + long long *s_x, long long *s_y); +static void scale_coords_spd_unsc(int x, int y, long long *s_x, long long *s_y); +static void scale_coords_spd_unsc_mk(int x, int y, long long *s_x, + long long *s_y); +static void do_move_abs(int x, int y, int x_range, int y_range, int vis); + +/* mouse movement functions */ +static void mouse_reset(void); +static void mouse_do_cur(int callback), mouse_update_cursor(void); +static void mouse_reset_to_current_video_mode(void); + +static void int33_mouse_move_buttons(int lbutton, int mbutton, int rbutton, void *udata); +static void int33_mouse_move_button(int num, int press, void *udata); +static void int33_mouse_move_wheel(int dy, void *udata); +static void int33_mouse_move_relative(int dx, int dy, int x_range, int y_range, void *udata); +static void int33_mouse_move_mickeys(int dx, int dy, void *udata); +static void int33_mouse_move_absolute(int x, int y, int x_range, int y_range, + int vis, void *udata); +static void int33_mouse_drag_to_corner(int x_range, int y_range, void *udata); +static void int33_mouse_enable_native_cursor(int flag, void *udata); +static void call_int15_mouse_event_handler(void); + +/* graphics cursor */ +void graph_cursor(void), text_cursor(void); +void graph_plane(int); + +/* called when mouse changes */ +static void mouse_delta(int); + +/* Internal mouse helper functions */ +static int mouse_clip_coords(void); +static void mouse_hide_on_exclusion(void); + +static int mouse_events = 0; +struct dragged_hack { + int cnt, skipped; + int x, y, x_range, y_range; +} dragged; +static mouse_erase_t mouse_erase; +static struct mousevideoinfo mouse_current_video; + +#define mice (&config.mouse) +struct mouse_struct mouse; + +static inline int mouse_roundx(int x) +{ + return x & ~((1 << mouse.xshift)-1); +} + +static inline int mouse_roundy(int y) +{ + return y & ~((1 << mouse.yshift)-1); +} + +static unsigned short default_graphcursormask[HEIGHT] = { + 0x0000, /*0000000000000000*/ + 0x4000, /*0100000000000000*/ + 0x6000, /*0110000000000000*/ + 0x7000, /*0111000000000000*/ + 0x7800, /*0111100000000000*/ + 0x7c00, /*0111110000000000*/ + 0x7e00, /*0111111000000000*/ + 0x7f00, /*0111111100000000*/ + 0x7f80, /*0111111110000000*/ + 0x7f00, /*0111111100000000*/ + 0x7c00, /*0111110000000000*/ + 0x4600, /*0100011000000000*/ + 0x0600, /*0000011000000000*/ + 0x0300, /*0000001100000000*/ + 0x0300, /*0000001100000000*/ + 0x0000 /*0000000000000000*/ +}; + +static unsigned short default_graphscreenmask[HEIGHT] = { + 0x3fff, /*0011111111111111*/ + 0x1fff, /*0001111111111111*/ + 0x0fff, /*0000111111111111*/ + 0x07ff, /*0000011111111111*/ + 0x03ff, /*0000001111111111*/ + 0x01ff, /*0000000111111111*/ + 0x00ff, /*0000000011111111*/ + 0x007f, /*0000000001111111*/ + 0x003f, /*0000000000111111*/ + 0x007f, /*0000000001111111*/ + 0x01ff, /*0000000111111111*/ + 0x10ff, /*0001000011111111*/ + 0xb0ff, /*1011000011111111*/ + 0xf87f, /*1111100001111111*/ + 0xf87f, /*1111100001111111*/ + 0xfcff /*1111110011111111*/ +}; + +void +mouse_helper(struct vm86_regs *regs) +{ + if (!mice->intdrv) { + m_printf("MOUSE No Internaldriver set, exiting mouse_helper()\n"); + SETWORD(regs->eax, 0xffff); + return; + } + + SETWORD(regs->eax, 0); /* Set successful completion */ + + switch (LO_BYTE_d(regs->ebx)) { + case 0: /* Reset iret for mouse */ + m_printf("MOUSE move iret !\n"); + mouse_enable_internaldriver(); + break; + case 1: /* Select Microsoft Mode */ + m_printf("MOUSE Microsoft Mouse (two buttons) selected.\n"); + mouse.threebuttons = FALSE; + break; + case 2: /* Select PC Mouse (3 button) */ + /* enabling this if you only have a two button mouse and didn't + set emulate3buttons is silly. however, if your mouse really + does have three buttons, you want to be able to use them. */ + m_printf("MOUSE PC Mouse (three buttons) selected.\n"); + mouse.threebuttons = TRUE; + break; + case 3: /* Tell me what mode we are in ? */ + if (!mouse.threebuttons) + SETHIGH(regs->ebx, 0x10); /* We are currently in Microsoft Mode */ + else + SETHIGH(regs->ebx, 0x20); /* We are currently in PC Mouse Mode */ + SETLO_BYTE(regs->ecx, mice->init_speed_x); + SETHIGH(regs->ecx, mice->init_speed_y); + SETLO_BYTE(regs->edx, (mice->ignorevesa & 1) | + ((mice->ignore_speed << 1) & 2)); + break; + case 4: /* Set vertical speed */ + if (LO_BYTE_d(regs->ecx) < 1) { + m_printf("MOUSE Vertical speed out of range. ERROR!\n"); + SETWORD(regs->eax, 1); + } else + mice->init_speed_y = LO_BYTE_d(regs->ecx); + break; + case 5: /* Set horizontal speed */ + if (LO_BYTE_d(regs->ecx) < 1) { + m_printf("MOUSE Horizontal speed out of range. ERROR!\n"); + SETWORD(regs->eax, 1); + } else + mice->init_speed_x = LO_BYTE_d(regs->ecx); + break; + case 6: /* Ignore vesa modes */ + mice->ignorevesa = LO_BYTE_d(regs->ecx); + break; + case 7: /* get minimum internal resolution */ + SETWORD(regs->ecx, mouse.min_max_x); + SETWORD(regs->edx, mouse.min_max_y); + break; + case 8: /* set minimum internal resolution */ + mouse.min_max_x = LO_WORD(regs->ecx); + mouse.min_max_y = LO_WORD(regs->edx); + reset_scale(); + break; + case 9: /* set cursor visibility */ + mouse_client_show_cursor(LO_BYTE_d(regs->ecx)); + break; + case 0xa: /* lock cursor visibility */ + mouse.visibility_locked = LO_BYTE_d(regs->ecx); + break; + case 0xb: /* ungrabbed mode tweak */ + mice->ignore_speed = LO_BYTE_d(regs->ecx); + break; + case DOS_SUBHELPER_MOUSE_START_VIDEO_MODE_SET: + m_printf("MOUSE Start video mode set\n"); + /* make sure cursor gets turned off */ + mouse_cursor(-1); + break; + case DOS_SUBHELPER_MOUSE_END_VIDEO_MODE_SET: + m_printf("MOUSE End video mode set\n"); + { + /* redetermine the video mode: + the stack contains: mode, saved ax, saved bx */ + unsigned int ssp = SEGOFF2LINEAR(regs->ss, 0); + unsigned int sp = (regs->esp + 2 + 6) & 0xffff; + unsigned ax = popw(ssp, sp); + int mode = popw(ssp, sp); + + if (!mice->ignorevesa && mode >= 0x100 && + (mode & 0xff00) != 0x1100 && ax == 0x4f) { + /* no chargen function; check if vesa mode set successful */ + vidmouse_set_video_mode(mode); + } else if ((mode & 0xff00) != 0x1100) { + vidmouse_set_video_mode(-1); + } + mouse_reset_to_current_video_mode(); + } + /* replace cursor if necessary */ + mouse_cursor(1); + /* reset hide count on mode switches to fix this: + * https://github.com/stsp/dosemu2/issues/314 + */ + if (mouse.cursor_on < -1) { + m_printf("MOUSE: normalizing hide count, %i\n", mouse.cursor_on); + mouse.cursor_on = -1; + } + break; + case 0xf2: + m_printf("MOUSE int74 helper\n"); + if (mouse.ps2.state && (mouse.ps2.cs || mouse.ps2.ip)) { + call_int15_mouse_event_handler(); + mouse_events = 0; + } + pic_untrigger(12); + break; + case 0xff: + m_printf("MOUSE Checking InternalDriver presence !!\n"); + break; + default: + m_printf("MOUSE Unknown mouse_helper function\n"); + SETWORD(regs->eax, 1); /* Set unsuccessful completion */ + } +} + +void mouse_ps2bios(void) +{ + m_printf("PS2MOUSE: Call ax=0x%04x, bx=0x%04x\n", LWORD(eax), LWORD(ebx)); + if (!mice->intdrv) { + REG(eax) = 0x0500; /* No ps2 mouse device handler */ + CARRY; + return; + } + + switch (REG(eax) &= 0x00FF) { + case 0x0000: + mouse.ps2.state = HI(bx); + HI(ax) = 0; + NOCARRY; + break; + case 0x0001: + mouse.ps2.state = 0; + HI(ax) = 0; + LWORD(ebx) = 0xAAAA; /* we have a ps2 mouse */ + NOCARRY; + break; + case 0x0002: + HI(ax) = 0; + NOCARRY; + break; + case 0x0003: + if (HI(bx) > 3) { + CARRY; + HI(ax) = 1; + } else { + NOCARRY; + HI(ax) = 0; + } + break; + case 0x0004: + HI(bx) = 0xAA; + HI(ax) = 0; + NOCARRY; + break; + case 0x0005: /* Initialize ps2 mouse */ + HI(ax) = 0; + mouse.ps2.state = 0; + mouse.ps2.pkg = HI(bx); + NOCARRY; + break; + case 0x0006: + switch (HI(bx)) { + case 0x00: + LO(bx) = (mouse.rbutton ? 1 : 0); + LO(bx) |= (mouse.lbutton ? 4 : 0); + LO(bx) |= 0; /* scaling 1:1 */ + LO(bx) |= 0x20; /* device enabled */ + LO(bx) |= 0; /* stream mode */ + LO(cx) = 0; /* resolution, one count */ + LO(dx) = 0; /* sample rate */ + HI(ax) = 0; + NOCARRY; + break; + case 0x01: + HI(ax) = 0; + NOCARRY; + break; + case 0x02: + HI(ax) = 1; + CARRY; + break; + } + break; + case 0x0007: + m_printf("PS2MOUSE: set device handler %04x:%04x\n", SREG(es), LWORD(ebx)); + mouse.ps2.cs = SREG(es); + mouse.ps2.ip = LWORD(ebx); + HI(ax) = 0; + NOCARRY; + break; + default: + HI(ax) = 1; + g_printf("PS2MOUSE: Unknown call ax=0x%04x\n", LWORD(eax)); + CARRY; + } +} + +int +mouse_int(void) +{ + if (!mice->intdrv) + return 0; + m_printf("MOUSE: int 33h, ax=%x bx=%x\n", LWORD(eax), LWORD(ebx)); + + switch (LWORD(eax)) { + case 0x00: /* Mouse Reset/Get Mouse Installed Flag */ + mouse_reset(); + break; + + case 0x01: /* Show Mouse Cursor */ + mouse_cursor(1); + break; + + case 0x02: /* Hide Mouse Cursor */ + mouse_cursor(-1); + break; + + case 0x03: /* Get Mouse Position and Button Status */ + mouse_pos(); + break; + + case 0x04: /* Set Cursor Position */ + mouse_setpos(); + break; + + case 0x05: /* Get mouse button press info */ + mouse_bpressinfo(); + break; + + case 0x06: /* Get mouse button release info */ + mouse_brelinfo(); + break; + + case 0x07: /* Set Mouse Horizontal Min/Max Position */ + mouse_setxminmax(); + break; + + case 0x08: /* Set Mouse Vertical Min/Max Position */ + mouse_setyminmax(); + break; + + case 0x09: /* Set mouse gfx cursor */ + mouse_set_gcur(); + break; + + case 0x0A: /* Set mouse text cursor */ + mouse_set_tcur(); + break; + + case 0x0B: /* Read mouse motion counters */ + mouse_mickeys(); + break; + + case 0x0C: /* Set mouse subroutine */ + mouse_setsub(); + break; + + case 0x0D: /* Enable Light Pen */ + break; + + case 0x0E: /* Disable Light Pen */ + break; + + case 0x0F: /* Set cursor speed */ + mouse_setcurspeed(); + break; + + case 0x10: /* Define mouse exclusion zone */ + mouse_exclusionarea(); + break; + + case 0x11: + LWORD(eax) = 0x574D; + LWORD(ebx) = 0; + LWORD(ecx) = 1; + break; + + case 0x12: /* Set Large Graphics Cursor Block */ + mouse_largecursor(); + break; + + case 0x13: /* Set Maximum for mouse speed doubling */ + mouse_doublespeed(); + break; + + case 0x14: /* Exchange Event Handler */ + mouse_excevhand(); + break; + + case 0x15: /* Get size of buffer needed to hold state */ + mouse_detstatbuf(); + break; + + case 0x16: /* Save mouse driver state */ + mouse_storestate(); + break; + + case 0x17: /* Restore mouse driver state */ + mouse_restorestate(); + break; + + case 0x18: /* Install alternate event handler */ + mouse_alternate(); + break; + + case 0x19: /* Determine address of alternate */ + mouse_detalternate(); + break; + + case 0x1A: /* Set mouse sensitivity */ + mouse_setsensitivity(); + break; + + case 0x1B: /* Determine mouse sensitivity */ + mouse_detsensitivity(); + break; + + case 0x1C: /* Set mouse hardware interrupt rate */ + mouse_hardintrate(); + break; + + case 0x1D: /* Set display page */ + mouse_disppage(); + break; + + case 0x1E: /* Determine display page */ + mouse_detpage(); + break; + + case 0x1F: /* Disable Mouse Driver */ + mouse_disable_internaldriver(); + break; + + case 0x20: /* Enable Mouse Driver */ + mouse_enable_internaldriver(); + break; + + case 0x21: + mouse_software_reset(); /* Perform Software reset on mouse */ + break; + + case 0x22: /* Set language for messages */ + m_printf("MOUSE: Set LANGUAGE, ignoring.\n"); + break; /* Ignore as we are US version only */ + + case 0x23: /* Get language for messages */ + m_printf("MOUSE: Get LANGUAGE\n"); + LWORD(ebx) = 0x0000; /* Return US/ENGLISH */ + break; + + case 0x24: /* Get driver version/mouse type/IRQ */ + mouse_version(); + break; + + case 0x25: /* Get general driver information */ + mouse_getgeninfo(); + break; + + case 0x26: /* Return Maximal Co-ordinates */ + mouse_getmaxcoord(); + break; + +#if 0 + case 0x27: /* Get masks and mickey counts */ + mouse_getmasksmicks(); /* TO DO ! when > 7.01 */ + break; + + case 0x28: /* Set video mode */ + mouse_setvidmode(); /* TO DO ! now ! */ + break; + + case 0x29: /* Count video modes */ + mouse_countvids(); /* TO DO ! now ! */ + break; + + case 0x2A: /* Get cursor hotspot */ + mouse_gethotspot(); /* TO DO ! when > 7.02 */ + break; + + case 0x2B: /* Set acceleration curves */ + mouse_setacccurv(); /* TO DO ! now */ + break; + + case 0x2C: /* Read acceleration curves */ + mouse_getacccurv(); /* TO DO ! now */ + break; + + case 0x2D: /* Set/Get active acceleration curves */ + mouse_setgetactcurv(); /* TO DO ! now */ + break; + + case 0x2E: /* Set acceleration profile names */ + mouse_setaccprofile(); /* TO DO ! when > 8.01 */ + break; + + case 0x2F: /* Hardware reset */ + mouse_hardreset(); /* TO DO ! when > 7.02 */ + break; + + case 0x30: /* Set/Get ballpoint information */ + mouse_getsetballpoint(); /* TO DO ! when > 7.04 */ + break; +#endif + + case 0x31: /* Get minimum/maximum virtual coords */ + mouse_getmaxminvirt(); /* TO DO ! when > 7.05 */ + break; + +#if 0 + case 0x32: /* Get active advanced functions */ + mouse_getadvfunc(); /* TO DO ! when > 7.05 */ + break; + + case 0x33: /* Get switch settings */ + mouse_getswitchset(); /* TO DO ! when > 7.05 */ + break; + + case 0x34: /* Get MOUSE.INI ! Location */ + mouse_getinilocation(); /* TO DO ! when > 8.00 */ + break; + + case 0x35: /* LCD Large Screen support */ + mouse_lcd(); /* TO DO ! when > 8.10 */ + break; +#endif + +/* + * Functions after 0x35 are not Microsoft calls, Implemented for PC Mouse + * and Mouse Systems 3 button emulation + */ + + case 0x42: + mouse_getstorereq(); /* Get storage requirements */ + break; + + default: + m_printf("MOUSE: function 0x%04x not implemented\n", LWORD(eax)); + break; + } + + if (mouse.cursor_on == 0) + mouse_update_cursor(); + + return 1; +} + +void +mouse_detpage(void) +{ + LWORD(ebx) = mouse.display_page; +} + +void +mouse_disppage(void) +{ + /* turn off (ie erase) the mouse cursor before changing the + display page so that it doesn't erase the wrong page */ + mouse_cursor(-1); + mouse.display_page = LWORD(ebx); + mouse_cursor(1); +} + +void +mouse_hardintrate(void) +{ + /* Only used by the Inport Mouse */ +} + +void +mouse_detalternate(void) +{ + error("MOUSE: RETURN ALTERNATE MOUSE USER HANDLER unimplemented.\n"); + LWORD(ecx) = 0; /* Error ! */ +} + +void +mouse_alternate(void) +{ + error("MOUSE: SET ALTERNATE MOUSE USER HANDLER unimplemented.\n"); + LWORD(eax) = 0xffff; /* Failed to install alternate */ +} + +void +mouse_doublespeed(void) +{ + mouse.threshold = LWORD(edx); /* Double speed at mickeys per second */ +} + +void +mouse_largecursor(void) +{ + LWORD(eax) = 0; /* Failed to set large cursor */ +} + +static void +mouse_exclusionarea(void) +{ + mouse.exc_ux = LWORD(ecx); + mouse.exc_uy = LWORD(edx); + mouse.exc_lx = LWORD(esi); + mouse.exc_ly = LWORD(edi); +} + +static void +mouse_getstorereq(void) +{ + if (!mouse.threebuttons) + LWORD(eax) = 0x42; /* Configures mouse for Microsoft Mode */ + else { + LWORD(eax) = 0xffff; /* Configures mouse for PC Mouse Mode */ + LWORD(ebx) = sizeof(mouse); /* applies to Genius Mouse too */ + } +} + +static void +mouse_getmaxcoord(void) +{ + LWORD(ebx) = (mice->intdrv ? 1 : 0); + LWORD(ecx) = mouse.maxx; + LWORD(edx) = mouse.maxy; + m_printf("MOUSE: COORDINATES: x: %d, y: %d\n",mouse.maxx,mouse.maxy); +} + +static void +mouse_getmaxminvirt(void) +{ + LWORD(eax) = mouse.virtual_minx; + LWORD(ebx) = mouse.virtual_miny; + LWORD(ecx) = mouse.virtual_maxx; + LWORD(edx) = mouse.virtual_maxy; + m_printf("MOUSE: VIRTUAL COORDINATES: x: %d-%d, y: %d-%d\n", + mouse.virtual_minx, mouse.virtual_maxx, + mouse.virtual_miny, mouse.virtual_maxy); +} + +static void +mouse_getgeninfo(void) +{ + /* Set AX to 0 */ + LWORD(eax) = 0x0000; + + /* Bits 0-7 are count of currently-active Mouse Display Drivers (MDD) */ + LWORD(eax) |= 0x0000; /* For now none ! */ + + /* Bits 8-11 are the interrupt rate */ + LWORD(eax) |= 0x0000; /* Don't allow interrupts */ + + /* Bits 12-13 are Mouse cursor information */ + if (mouse.gfx_cursor) + LWORD(eax) |= 0x2000; /* Set graphic cursor */ + else + LWORD(eax) |= 0x0000; /* Set software text cursor */ + + /* Bit 14 sets driver is newer integrated type */ + LWORD(eax) |= 0x0000; /* Not the newer integrated type */ + + /* Bit 15, 0=COM file, 1=SYS file */ + LWORD(eax) |= 0x8000; /* Set SYS file */ +} + +static void +mouse_software_reset(void) +{ + m_printf("MOUSE: software reset on mouse\n"); + + /* Disable cursor, and de-install current event handler */ + mouse.cs = 0; + mouse.ip = 0; + mouse.ps2.cs = 0; + mouse.ps2.ip = 0; + mouse_enable_internaldriver(); + if (mouse.cursor_on < -1) { + m_printf("MOUSE: normalizing hide count, %i\n", mouse.cursor_on); + mouse.cursor_on = -1; + } + + /* Return 0xffff on success, 0x21 on failure */ + LWORD(eax) = 0xffff; + + /* Return Number of mouse buttons */ + if (mouse.threebuttons) + LWORD(ebx) = 3; + else + LWORD(ebx) = 2; +} + +static void +mouse_detsensitivity(void) +{ + LWORD(ebx) = mouse.sens_x; /* horizontal speed */ + LWORD(ecx) = mouse.sens_y; /* vertical speed */ + LWORD(edx) = mouse.threshold; /* double speed threshold */ +} + +static void +mouse_setsensitivity(void) +{ + mouse.sens_x = LWORD(ebx); + if (mouse.sens_x == 0) + mouse.sens_x++; + mouse.sens_y = LWORD(ecx); + if (mouse.sens_y == 0) + mouse.sens_y++; + mouse.threshold = LWORD(edx); + m_printf("MOUSE: set sensitivity %i:%i %i\n", LWORD(ebx), LWORD(ecx), + LWORD(edx)); +} + +static void +mouse_restorestate(void) +{ + int current_state; + current_state = mouse.cursor_on; + /* turn cursor off before restore */ + if (current_state >= 0) { + mouse.cursor_on = 0; + mouse_cursor(-1); + } + + memcpy((void *)&mouse, MK_FP32(SREG(es),LWORD(edx)), sizeof(mouse)); + + /* regenerate mouse graphics cursor from masks; they take less + space than the "compiled" version. */ + define_graphics_cursor(mouse.graphscreenmask, mouse.graphcursormask); + + /* we turned off the mouse cursor prior to saving, so turn it + back on again at the restore. */ + if (mouse.cursor_on >= 0) { + mouse.cursor_on = -1; + mouse_cursor(1); + } + + m_printf("MOUSE: Restore mouse state\n"); +} + + +static void +mouse_storestate(void) +{ + int current_state; + current_state = mouse.cursor_on; + /* always turn off mouse cursor prior to saving, that way we don't + have to remember the erase information */ + if (current_state >= 0) { + mouse.cursor_on = 0; + mouse_cursor(-1); + } + mouse.cursor_on = current_state; + + memcpy(MK_FP32(SREG(es),LWORD(edx)), (void *)&mouse, sizeof(mouse)); + + /* now turn it back on */ + if (mouse.cursor_on >= 0) { + mouse.cursor_on = -1; + mouse_cursor(1); + } + mouse.cursor_on = current_state; + m_printf("MOUSE: Save mouse state\n"); +} + + +static void +mouse_detstatbuf(void) +{ + LWORD(ebx) = sizeof(mouse); + m_printf("MOUSE: get size of state buffer 0x%04x\n", LWORD(ebx)); +} + +static void +mouse_excevhand(void) +{ + unsigned short tmp1, tmp2, tmp3; + + m_printf("MOUSE: exchange subroutines\n"); + tmp1 = mouse.mask; + tmp2 = mouse.cs; + tmp3 = mouse.ip; + mouse_setsub(); + LWORD(ecx) = tmp1; + SREG(es) = tmp2; + LWORD(edx) = tmp3; +} + +void +mouse_setcurspeed(void) +{ + long long oldx, oldy, newx, newy; + + if (mouse.cursor_on < 0) { + /* when speed changes, in ungrabbed mode we need to update deltas + * so that the (invisible) cursor not to move */ + scale_coords(mouse.px_abs, mouse.py_abs, mouse.px_range, mouse.py_range, + &oldx, &oldy); + scale_coords3(mouse.px_abs, mouse.py_abs, mouse.px_range, mouse.py_range, + LWORD(ecx), LWORD(edx), &newx, &newy); + mouse.x_delta -= newx - oldx; + mouse.y_delta -= newy - oldy; + } + + m_printf("MOUSE: set cursor speed: x=%i, y=%i\n", LWORD(ecx), LWORD(edx)); + if (LWORD(ecx) > 0) + mouse.speed_x = LWORD(ecx); + if (LWORD(edx) > 0) + mouse.speed_y = LWORD(edx); +} + +static int get_unsc_x(int dx) +{ + return dx * mouse.px_range; +} + +static int get_unsc_y(int dy) +{ + return dy * mouse.py_range; +} + +static void setxy(int x, int y) +{ + mouse.unsc_x = get_unsc_x(x); + mouse.unsc_y = get_unsc_y(y); +} + +/* change pos from software preserving coord reminders */ +static void setxy_sw(int x, int y) +{ + int rem_x = mouse.unsc_x % get_unsc_x(1 << mouse.xshift); + int rem_y = mouse.unsc_y % get_unsc_y(1 << mouse.yshift); + + mouse.unsc_x = get_unsc_x(mouse_roundx(x)) + rem_x; + mouse.unsc_y = get_unsc_y(mouse_roundy(y)) + rem_y; +} + +static int get_unsc_mk_x(int dx) +{ + return dx * mouse.px_range; +} + +static int get_unsc_mk_y(int dy) +{ + return dy * mouse.py_range; +} + +static void setmxy(int x, int y) +{ + mouse.unscm_x = get_unsc_mk_x(x); + mouse.unscm_y = get_unsc_mk_y(y); +} + +static void add_abs_coords(long long udx, long long udy) +{ + mouse.unsc_x += udx; + mouse.unsc_y += udy; + + mouse_clip_coords(); +} + +static void add_mickey_coords(long long udx, long long udy) +{ + mouse.unscm_x += udx; + mouse.unscm_y += udy; +} + +static void get_scale_range(int *mx_range, int *my_range) +{ + if (config.mouse.ignore_speed) { + *mx_range = _max(mouse.maxx, mouse.virtual_maxx) - MOUSE_MINX + 1; + *my_range = _max(mouse.maxy, mouse.virtual_maxy) - MOUSE_MINY + 1; + } else { + *mx_range = mouse.maxx - MOUSE_MINX + 1; + *my_range = mouse.maxy - MOUSE_MINY + 1; + } +} + +static void add_mk(int dx, int dy) +{ + int mx_range, my_range; + long long udx, udy; + + /* pixel range set to 1 */ + udx = dx * 8; + udy = dy * 8; + get_scale_range(&mx_range, &my_range); + + add_mickey_coords(udx, udy); + add_abs_coords(udx * mx_range / (mouse.speed_x * mouse.min_max_x), + udy * my_range / (mouse.speed_y * mouse.min_max_y)); +} + +static void add_px(int dx, int dy) +{ + long long mdx, mdy, udx, udy; + + scale_coords_spd_unsc(dx, dy, &mdx, &mdy); + scale_coords_spd_unsc_mk(dx, dy, &udx, &udy); + add_mickey_coords(udx, udy); + add_abs_coords(mdx, mdy); +} + +static void reset_scale(void) +{ + /* To get quicken to run correctly I no longer ignore maxx and maxy + * values as stored in the BIOS variables for text mode. + * Does this violate a spec? + * -- EB 25 May 1998 + */ + +/* DANG_BEGIN_REMARK + * Whoever wrote the dos mouse driver spec was brain dead... + * For some video modes the mouse driver appears to randomly + * pick a shift factor, possibly to keep at least a 640x200 resolution. + * + * The general programming documentation doesn't make this clear. + * And says that in text modes it is safe to divide the resolution by + * 8 to get the coordinates in characters. + * + * The only safe way to handle the mouse driver is to call function + * 0x26 Get max x & max y coordinates and scale whatever the driver + * returns yourself. + * + * To handle programs written by programmers who weren't so cautious a + * doctrine of least surprise has been implemented. + * + * As much as possible do the same as a standard dos mouse driver in the + * original vga modes 0,1,2,3,4,5,6,7,13,14,15,16,17,18,19. + * + * For other text modes allow the divide by 8 technique to work. + * For other graphics modes return x & y in screen coordinates. + * Except when those modes are either 40x?? or 320x??? + * and then handle the x resolution as in 40x25 and 320x200 modes. + * + * 320x200 modes are slightly controversial as I have indications that + * not all mouse drivers do the same thing. So I have taken the + * simplest, and most common route, which is also long standing dosemu + * practice of always shifting the xaxis by 1. When I researched this + * I could find no examples that did otherwise. + * + * + * -- Eric Biederman 19 August 1998 + * + * DANG_END_REMARK + */ + + if (mouse_current_video.textgraph == 'T') { + mouse.xshift = 3; + mouse.yshift = 3; + if (mouse_current_video.width == 40) { + mouse.xshift = 4; + } + mouse.gfx_cursor = FALSE; + } else { + mouse.gfx_cursor = TRUE; + mouse.xshift = 0; + mouse.yshift = 0; + while ((mouse_current_video.width << mouse.xshift) < mouse.min_max_x) { + mouse.xshift++; + } + while ((mouse_current_video.height << mouse.yshift) < mouse.min_max_y) { + mouse.yshift++; + } + define_graphics_cursor(default_graphscreenmask,default_graphcursormask); + } + + /* Set the maximum sizes */ + mouse.maxx = mouse_current_video.width << mouse.xshift; + mouse.maxy = mouse_current_video.height << mouse.yshift; + + /* Change mouse.maxx & mouse.maxy from ranges to maximums */ + mouse.maxx = mouse_roundx(mouse.maxx - 1); + mouse.maxy = mouse_roundy(mouse.maxy - 1); + mouse.maxx += (1 << mouse.xshift) -1; + mouse.maxy += (1 << mouse.yshift) -1; +} + +static void +mouse_reset_to_current_video_mode(void) +{ + int err; + /* This looks like a generally safer place to reset scaling factors + * then in mouse_reset, as it gets called more often. + * -- Eric Biederman 29 May 2000 + */ + mouse.speed_x = INIT_SPEED_X; + mouse.speed_y = INIT_SPEED_Y; + mouse.sens_x = 100; + mouse.sens_y = 100; + mouse.threshold = 200; + mouse.px_abs = 0; + mouse.py_abs = 0; + + /* + * Here we make sure text modes are resolved properly, according to the + * standard vga/ega/cga/mda specs for int10. If we don't know that we are + * in text mode, then we return pixel resolution and assume graphic mode. + * stsp: use mode 6 as a fall-back. + */ + err = get_current_video_mode(&mouse_current_video); + if (err) { + m_printf("MOUSE: fall-back to mode 6\n"); + vidmouse_set_video_mode(-1); + vidmouse_get_video_mode(6, &mouse_current_video); + } + + if (!mouse.win31_mode) + reset_scale(); + + /* force update first time after reset */ + mouse.oldrx = -1; + + /* Setup up the virtual coordinates */ + mouse.virtual_minx = MOUSE_MINX; + mouse.virtual_maxx = mouse.maxx; + mouse.virtual_miny = MOUSE_MINY; + mouse.virtual_maxy = mouse.maxy; + + /* counters may become out-of-range, adjust */ + mouse_clip_coords(); + + m_printf("maxx=%i, maxy=%i speed_x=%i speed_y=%i type=%d\n", + mouse.maxx, mouse.maxy, mouse.speed_x, mouse.speed_y, + mice->type); +} + +static void int33_mouse_enable_native_cursor(int flag, void *udata) +{ + mice->native_cursor = flag; + mouse_do_cur(1); +} + +static void scale_coords_spd(int x, int y, int x_range, int y_range, + int mx_range, int my_range, int speed_x, int speed_y, + long long *s_x, long long *s_y) +{ + *s_x = ((long long)x * mx_range * mice->init_speed_x * 100) / + (x_range * speed_x * mouse.sens_x) + MOUSE_MINX; + *s_y = ((long long)y * my_range * mice->init_speed_y * 100) / + (y_range * speed_y * mouse.sens_y) + MOUSE_MINY; +} + +static void scale_coords_spd_unsc_mk(int x, int y, long long *s_x, + long long *s_y) +{ + scale_coords_spd(x, y, 1, 1, mouse.min_max_x, mouse.min_max_y, + 1, 1, s_x, s_y); +} + +static void scale_coords_spd_unsc(int x, int y, long long *s_x, long long *s_y) +{ + int mx_range, my_range; + + get_scale_range(&mx_range, &my_range); + scale_coords_spd(x, y, 1, 1, mx_range, my_range, + mouse.speed_x, mouse.speed_y, s_x, s_y); +} + +static void scale_coords_basic(int x, int y, int x_range, int y_range, + int mx_range, int my_range, long long *s_x, long long *s_y) +{ + *s_x = ((long long)x * mx_range) / x_range + MOUSE_MINX; + *s_y = ((long long)y * my_range) / y_range + MOUSE_MINY; +} + +static void scale_coords2(int x, int y, int x_range, int y_range, + int mx_range, int my_range, int speed_x, int speed_y, + long long *s_x, long long *s_y) +{ + /* if cursor is not visible we need to take into + * account the user's speed. If it is visible, then + * in ungrabbed mode we have to ignore user's speed. */ + if (!config.mouse.ignore_speed && mouse.cursor_on < 0) + scale_coords_spd(x, y, x_range, y_range, + mx_range, my_range, speed_x, speed_y, + s_x, s_y); + else + scale_coords_basic(x, y, x_range, y_range, + mx_range, my_range, s_x, s_y); +} + +static void scale_coords3(int x, int y, int x_range, int y_range, + int speed_x, int speed_y, long long *s_x, long long *s_y) +{ + int mx_range, my_range; + + get_scale_range(&mx_range, &my_range); + scale_coords2(x, y, x_range, y_range, mx_range, my_range, + _min(speed_x, mice->init_speed_x), + _min(speed_y, mice->init_speed_y), s_x, s_y); +} + +static void scale_coords(int x, int y, int x_range, int y_range, + long long *s_x, long long *s_y) +{ + int mx_range, my_range; + + get_scale_range(&mx_range, &my_range); + scale_coords2(x, y, x_range, y_range, mx_range, my_range, + /* simcity hack: only handle cursor speed-ups but ignore + * slow-downs. */ + _min(mouse.speed_x, mice->init_speed_x), + _min(mouse.speed_y, mice->init_speed_y), s_x, s_y); +} + +static void mouse_reset(void) +{ + m_printf("MOUSE: reset mouse/installed!\n"); + + mouse.ps2.state = 0; + mouse.ps2.cs = 0; + mouse.ps2.ip = 0; + + mouse_enable_internaldriver(); + + /* Return 0xffff on mouse installed, 0x0000 - no mouse driver installed */ + /* Return number of mouse buttons */ + LWORD(eax) = 0xffff; + if (mouse.threebuttons) + LWORD(ebx)=3; + else + LWORD(ebx)=2; + mouse_reset_to_current_video_mode(); + + /* turn cursor off if reset requested by software and it was on. */ + if (mouse.cursor_on >= 0) { + mouse.cursor_on = 0; + mouse_cursor(-1); + } + + mouse.cursor_on = -1; + mouse.lbutton = mouse.mbutton = mouse.rbutton = 0; + mouse.lpcount = mouse.mpcount = mouse.rpcount = 0; + mouse.lrcount = mouse.mrcount = mouse.rrcount = 0; + mouse.wmcount = 0; + + mouse.exc_lx = mouse.exc_ux = -1; + mouse.exc_ly = mouse.exc_uy = -1; + + mouse.ip = mouse.cs = 0; + mouse.mask = 0; /* no interrupts */ + + mouse.threshold = 64; + + mouse.lpx = mouse.lpy = mouse.lrx = mouse.lry = 0; + mouse.rpx = mouse.rpy = mouse.rrx = mouse.rry = 0; + + mouse.unscm_x = mouse.unscm_y = 0; + mouse.unsc_x = mouse.unsc_y = 0; + mouse.x_delta = mouse.y_delta = 0; + mouse.need_resync = 0; + dragged.cnt = 0; + + mouse.textscreenmask = 0xffff; + mouse.textcursormask = 0x7f00; + memcpy((void *)mouse.graphscreenmask,default_graphscreenmask,32); + memcpy((void *)mouse.graphcursormask,default_graphcursormask,32); + mouse.hotx = mouse.hoty = -1; + + mouse.win31_mode = 0; + + mouse_do_cur(1); +} + +void +mouse_cursor(int flag) /* 1=show, -1=hide */ +{ + int need_resync = 0; + /* Delete exclusion zone, if show cursor applied */ + if (flag == 1) { + mouse.exc_lx = mouse.exc_ux = -1; + mouse.exc_ly = mouse.exc_uy = -1; + if (mouse.x_delta || mouse.y_delta) { + mouse.x_delta = mouse.y_delta = 0; + need_resync = 1; + } + } + + /* already on, don't do anything */ + if (flag == 1 && mouse.cursor_on == 0) + return; + + /* adjust hide count (always negative or zero) */ + mouse.cursor_on += flag; + + /* update the cursor if we just turned it off or on */ + if ((flag == -1 && mouse.cursor_on == -1) || + (flag == 1 && mouse.cursor_on == 0)){ + mouse_do_cur(1); + if (flag == 1 && need_resync && !dragged.cnt) + do_move_abs(mouse.px_abs, mouse.py_abs, mouse.px_range, mouse.py_range, + mouse.cursor_on >= 0); + /* first time enabling should work immediately */ + if (flag == 1 && !mouse.visibility_changed) + mouse_client_show_cursor(mouse.visibility_locked ?: mouse.cursor_on >= 0); + else + mouse.visibility_changed++; + } + + m_printf("MOUSE: %s mouse cursor %d\n", flag > 0 ? "show" : "hide", mouse.cursor_on); +} + +void +mouse_pos(void) +{ + m_printf("MOUSE: get mouse position x:%d, y:%d, b(l%d m%d r%d)\n", get_mx(), + get_my(), mouse.lbutton, mouse.mbutton, mouse.rbutton); + LWORD(ecx) = MOUSE_RX; + LWORD(edx) = MOUSE_RY; + LO(bx) = (mouse.rbutton ? 2 : 0) | (mouse.lbutton ? 1 : 0); + if (mouse.threebuttons) + LO(bx) |= (mouse.mbutton ? 4 : 0); + HI(bx) = mouse.wmcount; + mouse.wmcount = 0; +} + +/* Set mouse position */ +void +mouse_setpos(void) +{ + setxy_sw(LWORD(ecx), LWORD(edx)); + mouse_clip_coords(); + mouse_hide_on_exclusion(); + if (mouse.cursor_on >= 0) { + mouse.x_delta = mouse.y_delta = 0; + mouse_do_cur(1); + } else { + long long abs_x, abs_y; + scale_coords(mouse.px_abs, mouse.py_abs, mouse.px_range, mouse.py_range, + &abs_x, &abs_y); + mouse.x_delta = get_mx() - abs_x; + mouse.y_delta = get_my() - abs_y; + } + m_printf("MOUSE: set cursor pos x:%d, y:%d delta x:%d, y:%d\n", + get_mx(), get_my(), mouse.x_delta, mouse.y_delta); +} + +void +mouse_bpressinfo(void) +{ + m_printf("MOUSE: Button press info\n"); + + if (LWORD(ebx) > 2) + LWORD(ebx) = 0; + + switch(LWORD(ebx)) { + + case 0xffff: /* wheel movement */ + LWORD(ebx) = mouse.wmcount; + mouse.wmcount = 0; + LWORD(ecx) = mouse.wmx; + LWORD(edx) = mouse.wmy; + break; + + case 0: /* left button */ + LWORD(ebx) = mouse.lpcount; + mouse.lpcount = 0; + LWORD(ecx) = mouse.lpx; + LWORD(edx) = mouse.lpy; + break; + + case 1: /* right button */ + LWORD(ebx) = mouse.rpcount; + mouse.rpcount = 0; + LWORD(ecx) = mouse.rpx; + LWORD(edx) = mouse.rpy; + break; + + case 2: /* middle button */ + LWORD(ebx) = mouse.mpcount; + mouse.mpcount = 0; + LWORD(ecx) = mouse.mpx; + LWORD(edx) = mouse.mpy; + break; + } + + LO(ax) = (mouse.rbutton ? 2 : 0) | (mouse.lbutton ? 1 : 0); + if (mouse.threebuttons) + LO(ax) |= (mouse.mbutton ? 4 : 0); + HI(ax) = mouse.wmcount; +} + +void +mouse_brelinfo(void) +{ + m_printf("MOUSE: Button release info\n"); + + if (LWORD(ebx) > 2) + LWORD(ebx) = 0; + + switch(LWORD(ebx)) { + + case 0xffff: /* wheel movement */ + LWORD(ebx) = mouse.wmcount; + mouse.wmcount = 0; + LWORD(ecx) = mouse.wmx; + LWORD(edx) = mouse.wmy; + break; + + case 0: /* left button */ + LWORD(ebx) = mouse.lrcount; + mouse.lrcount = 0; + LWORD(ecx) = mouse.lrx; + LWORD(edx) = mouse.lry; + break; + + case 1: /* right button */ + LWORD(ebx) = mouse.rrcount; + mouse.rrcount = 0; + LWORD(ecx) = mouse.rrx; + LWORD(edx) = mouse.rry; + break; + + case 2: /* middle button */ + LWORD(ebx) = mouse.mrcount; + mouse.mrcount = 0; + LWORD(ecx) = mouse.mrx; + LWORD(edx) = mouse.mry; + break; + } + + LO(ax) = (mouse.rbutton ? 2 : 0) | (mouse.lbutton ? 1 : 0); + if (mouse.threebuttons) + LO(ax) |= (mouse.mbutton ? 4 : 0); + HI(ax) = mouse.wmcount; +} + +void +mouse_setxminmax(void) +{ + m_printf("MOUSE: set horz. min: %d, max: %d\n", LWORD(ecx), LWORD(edx)); + + /* if the min > max, they are swapped */ + mouse.virtual_minx = (LWORD(ecx) > LWORD(edx)) ? LWORD(edx) : LWORD(ecx); + mouse.virtual_maxx = (LWORD(ecx) > LWORD(edx)) ? LWORD(ecx) : LWORD(edx); + mouse.virtual_minx = mouse_roundx(mouse.virtual_minx); + mouse.virtual_maxx = mouse_roundx(mouse.virtual_maxx); + mouse.virtual_maxx += (1 << mouse.xshift) -1; + + mouse_clip_coords(); +} + +void +mouse_setyminmax(void) +{ + m_printf("MOUSE: set vert. min: %d, max: %d\n", LWORD(ecx), LWORD(edx)); + + /* if the min > max, they are swapped */ + mouse.virtual_miny = (LWORD(ecx) > LWORD(edx)) ? LWORD(edx) : LWORD(ecx); + mouse.virtual_maxy = (LWORD(ecx) > LWORD(edx)) ? LWORD(ecx) : LWORD(edx); + mouse.virtual_miny = mouse_roundy(mouse.virtual_miny); + mouse.virtual_maxy = mouse_roundy(mouse.virtual_maxy); + mouse.virtual_maxy += (1 << mouse.yshift) -1; + + mouse_clip_coords(); +} + +void +mouse_set_gcur(void) +{ + unsigned short *ptr = MK_FP32(SREG(es),LWORD(edx)); + + m_printf("MOUSE: set gfx cursor...hspot: %d, vspot: %d, masks: %04x:%04x\n", + LWORD(ebx), LWORD(ecx), SREG(es), LWORD(edx)); + + /* remember hotspot location */ + mouse.hotx = LWORD(ebx); + mouse.hoty = LWORD(ecx); + + /* remember mask definition */ + memcpy((void *)mouse.graphscreenmask,ptr,32); + memcpy((void *)mouse.graphcursormask,ptr+16,32); + + /* compile it so that it can actually be drawn. */ + define_graphics_cursor(mouse.graphscreenmask, mouse.graphcursormask); +} + +void +mouse_set_tcur(void) +{ + m_printf("MOUSE: set text cursor...type: %d, start: 0x%04x, end: 0x%04x\n", + LWORD(ebx), LWORD(ecx), LWORD(edx)); + + if (LWORD(ebx)==0) { /* Software cursor */ + mouse.textscreenmask = LWORD(ecx); + mouse.textcursormask = LWORD(edx); + mouse_do_cur(1); + } else { /* Hardware cursor */ + /* CX - should be starting line of hardware cursor + * DX - should be ending line of hardware cursor + * FIXME ! -- then again, who ever actually uses the hw cursor? */ + /* (the only advantage of the hw cursor is that you don't have to + erase it before hitting vram */ + mouse.textscreenmask = 0x7fff; + mouse.textcursormask = 0xff00; + mouse_do_cur(1); + } +} + +void +mouse_setsub(void) +{ + mouse.cs = SREG(es); + mouse.ip = LWORD(edx); + mouse.mask = LWORD(ecx); + + m_printf("MOUSE: user defined sub %04x:%04x, mask 0x%02x\n", + SREG(es), LWORD(edx), LWORD(ecx)); +} + +void +mouse_mickeys(void) +{ + int mkx = mickeyx(); + int mky = mickeyy(); + m_printf("MOUSE: read mickeys %d %d\n", mkx, mky); + LWORD(ecx) = mkx; + LWORD(edx) = mky; + + /* counters get reset after read */ + if (mkx) + mouse.unscm_x -= get_unsc_mk_x(mkx) * 8; + if (mky) + mouse.unscm_y -= get_unsc_mk_y(mky) * 8; + + if (dragged.cnt) { + dragged.cnt = 0; + if (dragged.skipped) { + dragged.skipped = 0; + do_move_abs(dragged.x, dragged.y, dragged.x_range, dragged.y_range, + mouse.cursor_on >= 0); + } + } +} + +void +mouse_version(void) +{ + LWORD(ebx) = MOUSE_VERSION; + if (mice->com == -1) { + HI(cx) = 4; /* ps2 mouse */ + LO(cx) = 0; + } else { + HI(cx) = 2; /* serial mouse */ + LO(cx) = com_cfg[mice->com].irq; + } + m_printf("MOUSE: get version %04x, 0x%04x\n", LWORD(ebx), LWORD(ecx)); +} + +void +mouse_disable_internaldriver(void) +{ + LWORD(eax) = 0x001F; + SREG(es) = mouse.cs; + LWORD(ebx) = mouse.ip; + + mouse.enabled = FALSE; + + mouse_client_show_cursor(1); + m_printf("MOUSE: Disable InternalDriver\n"); +} + +void +mouse_enable_internaldriver(void) +{ + mouse.enabled = TRUE; + mouse_client_show_cursor(mouse.cursor_on >= 0); + m_printf("MOUSE: Enable InternalDriver\n"); +} + +/* Keyboard mouse control :) */ +void mouse_keyboard(bool make, t_keysym key) +{ + static struct keyboard_mouse_state { + int l, r, u, d; + int ul, ur, dl, dr; + int lbutton, mbutton, rbutton; + } state; + int dx, dy; + switch (key) { + case DKY_MOUSE_DOWN: + state.d = !!make; + break; + case DKY_MOUSE_LEFT: + state.l = !!make; + break; + case DKY_MOUSE_RIGHT: + state.r = !!make; + break; + case DKY_MOUSE_UP: + state.u = !!make; + break; + case DKY_MOUSE_UP_AND_LEFT: + state.ul = make; + break; + case DKY_MOUSE_UP_AND_RIGHT: + state.ur = make; + break; + case DKY_MOUSE_DOWN_AND_LEFT: + state.dl = make; + break; + case DKY_MOUSE_DOWN_AND_RIGHT: + state.dr = make; + break; + case DKY_MOUSE_BUTTON_LEFT: + state.lbutton = make; + break; + case DKY_MOUSE_BUTTON_RIGHT: + state.rbutton = make; + break; + case DKY_MOUSE_BUTTON_MIDDLE: + state.mbutton = make; + break; + default: + m_printf("MOUSE: keyboard_mouse(), key 0x%02x unknown!\n", key); + break; + } + dx = dy = 0; + if (state.d) { + dy += 1; + } + if (state.dl) { + dy += 1; + dx -= 1; + } + if (state.dr) { + dy += 1; + dx += 1; + } + if (state.u) { + dy -= 1; + } + if (state.ul) { + dy -= 1; + dx -= 1; + } + if (state.ur) { + dy -= 1; + dx += 1; + } + if (state.r) { + dx += 1; + } + if (state.l) { + dx -= 1; + } + mouse_move_mickeys(dx, dy, MOUSE_VKBD); + mouse_move_buttons(state.lbutton, state.mbutton, state.rbutton, + MOUSE_VKBD); +} + +static int mouse_clip_coords2(int x, int y, int *r_x, int *r_y) +{ + int clipped = 0; + + *r_x = x; + *r_y = y; + /* put the mouse coordinate in bounds */ + if (x < mouse.virtual_minx) { + *r_x = mouse.virtual_minx; + clipped |= 1; + } + if (y < mouse.virtual_miny) { + *r_y = mouse.virtual_miny; + clipped |= 2; + } + if (x > mouse.virtual_maxx) { + *r_x = mouse.virtual_maxx; + clipped |= 1; + } + if (y > mouse.virtual_maxy) { + *r_y = mouse.virtual_maxy; + clipped |= 2; + } + return clipped; +} + +static int mouse_clip_coords(void) +{ + int newx, newy, ret; + + ret = mouse_clip_coords2(_get_mx(), _get_my(), &newx, &newy); + if (ret & 1) + mouse.unsc_x = get_unsc_x(newx); + if (ret & 2) + mouse.unsc_y = get_unsc_y(newy); + return ret; +} + +static void mouse_hide_on_exclusion(void) +{ + /* Check exclusion zone, then turn off cursor if necessary */ + if (mouse.exc_ux != -1) { + short xl, xr, yt, yb; + + /* find the cursor box's edges coords (in mouse coords not screen) */ + if (mouse.gfx_cursor) { /* graphics cursor */ + xl = get_mx() - (mouse.hotx << mouse.xshift); + xr = get_mx() + ((HEIGHT-1 - mouse.hotx) << mouse.xshift); + yt = get_my() - (mouse.hoty << mouse.yshift); + yb = get_my() + ((HEIGHT-1 - mouse.hoty) << mouse.yshift); + } + else { /* text cursor */ + xl = MOUSE_RX; + xr = MOUSE_RX + (1 << mouse.xshift) - 1; + yt = MOUSE_RY; + yb = MOUSE_RY + (1 << mouse.yshift) - 1; + + } + /* check zone boundary */ + if ((xr >= mouse.exc_ux) && + (xl <= mouse.exc_lx) && + (yb >= mouse.exc_uy) && + (yt <= mouse.exc_ly)) + mouse_cursor(-1); + } +} + +static void mouse_move(int abs) +{ + /* delayed visibility change */ + if (mouse.visibility_changed) { + mouse.visibility_changed = 0; + mouse_client_show_cursor(mouse.visibility_locked ?: mouse.cursor_on >= 0); + } + + mouse_hide_on_exclusion(); + mouse_update_cursor(); + + m_printf("MOUSE: move: x=%d,y=%d\n", get_mx(), get_my()); + + mouse_delta(DELTA_CURSOR | (abs ? DELTA_ABSOLUTE : 0)); +} + +static void mouse_lb(void) +{ + m_printf("MOUSE: left button %s\n", mouse.lbutton ? "pressed" : "released"); + if (!mouse.lbutton) { + mouse.lrcount++; + mouse.lrx = MOUSE_RX; + mouse.lry = MOUSE_RY; + mouse_delta(DELTA_LEFTBUP); + } + else { + mouse.lpcount++; + mouse.lpx = MOUSE_RX; + mouse.lpy = MOUSE_RY; + mouse_delta(DELTA_LEFTBDOWN); + } +} + +static void mouse_mb(void) +{ + m_printf("MOUSE: middle button %s\n", mouse.mbutton ? "pressed" : "released"); + if (!mouse.mbutton) { + mouse.mrcount++; + mouse.mrx = MOUSE_RX; + mouse.mry = MOUSE_RY; + mouse_delta(DELTA_MIDDLEBUP); + } + else { + mouse.mpcount++; + mouse.mpx = MOUSE_RX; + mouse.mpy = MOUSE_RY; + mouse_delta(DELTA_MIDDLEBDOWN); + } +} + +static void mouse_rb(void) +{ + m_printf("MOUSE: right button %s\n", mouse.rbutton ? "pressed" : "released"); + if (!mouse.rbutton) { + mouse.rrcount++; + mouse.rrx = MOUSE_RX; + mouse.rry = MOUSE_RY; + mouse_delta(DELTA_RIGHTBUP); + } + else { + mouse.rpcount++; + mouse.rpx = MOUSE_RX; + mouse.rpy = MOUSE_RY; + mouse_delta(DELTA_RIGHTBDOWN); + } +} + +static void int33_mouse_move_buttons(int lbutton, int mbutton, int rbutton, void *udata) +{ + if (dragged.skipped) { + dragged.skipped = 0; + do_move_abs(dragged.x, dragged.y, dragged.x_range, + dragged.y_range, mouse.cursor_on >= 0); + } + + /* Provide 3 button emulation on 2 button mice, + But only when PC Mouse Mode is set, otherwise + Microsoft Mode = 2 buttons. + Middle press if left/right pressed together. + Middle release done for us by the above routines. + This bit also resets the left/right button, because + we can't have all three buttons pressed at once. + Well, we could, but not under emulation on 2 button mice. + Alan Hourihane */ + if (mice->emulate3buttons && mouse.threebuttons) { + if (lbutton && rbutton) { + mbutton = 1; /* Set middle button */ + } + } + /* + * update the event mask + */ + if (lbutton != mouse.lbutton) { + mouse.lbutton = lbutton; + mouse_lb(); + } + if (mouse.threebuttons && mbutton != mouse.mbutton) { + mouse.mbutton = mbutton; + mouse_mb(); + } + if (rbutton != mouse.rbutton) { + mouse.rbutton = rbutton; + mouse_rb(); + } +} + +static void int33_mouse_move_button(int num, int press, void *udata) +{ + if (dragged.skipped) { + dragged.skipped = 0; + do_move_abs(dragged.x, dragged.y, dragged.x_range, + dragged.y_range, mouse.cursor_on >= 0); + } + + switch (num) { + case 0: + if (press != mouse.lbutton) { + mouse.lbutton = press; + mouse_lb(); + } + break; + case 1: + if (mouse.threebuttons && press != mouse.mbutton) { + mouse.mbutton = press; + mouse_mb(); + } + break; + case 2: + if (press != mouse.rbutton) { + mouse.rbutton = press; + mouse_rb(); + } + break; + } +} + +static void int33_mouse_move_wheel(int dy, void *udata) +{ + m_printf("MOUSE: wheel movement %i\n", dy); + mouse.wmcount += dy; + mouse.wmx = MOUSE_RX; + mouse.wmy = MOUSE_RY; + mouse_delta(DELTA_WHEEL); +} + +static void int33_mouse_move_relative(int dx, int dy, int x_range, int y_range, + void *udata) +{ + if (mouse.px_range != x_range || mouse.py_range != y_range) + set_px_ranges(x_range, y_range); + add_px(dx, dy); + mouse.x_delta = mouse.y_delta = 0; + mouse.need_resync = 1; + + m_printf("mouse_move_relative(%d, %d) -> %d %d \n", + dx, dy, get_mx(), get_my()); + + /* + * update the event mask + */ + if (dx || dy) + mouse_move(0); +} + +static void int33_mouse_move_mickeys(int dx, int dy, void *udata) +{ + if (mouse.px_range != 1 || mouse.py_range != 1) + set_px_ranges(1, 1); + add_mk(dx, dy); + + m_printf("mouse_move_mickeys(%d, %d) -> %d %d \n", + dx, dy, get_mx(), get_my()); + + /* + * update the event mask + */ + if (dx || dy) + mouse_move(0); +} + +static int move_abs_mickeys(int dx, int dy, int x_range, int y_range) +{ + + int ret = 0, oldmx = mickeyx(), oldmy = mickeyy(); + long long mdx, mdy; + scale_coords_spd_unsc_mk(dx, dy, &mdx, &mdy); + + if (mdx || mdy) { + add_mickey_coords(mdx, mdy); + ret = 1; + } + m_printf("mouse_move_absolute dx:%d dy:%d mickeys: %d %d -> %d %d\n", + dx, dy, oldmx, oldmy, mickeyx(), mickeyy()); + + return ret; +} + +static int move_abs_coords(int x, int y, int x_range, int y_range, int vis) +{ + long long new_x, new_y; + int clipped, c_x, c_y, oldx = get_mx(), oldy = get_my(); + + scale_coords(x, y, x_range, y_range, &new_x, &new_y); + /* for visible cursor always recalc deltas */ + if (vis) + mouse.x_delta = mouse.y_delta = 0; + clipped = mouse_clip_coords2(new_x + mouse.x_delta, + new_y + mouse.y_delta, &c_x, &c_y); + /* we don't allow DOS prog to grab mouse pointer by locking it + * inside a clipping region. So just update deltas. If cursor + * is visible, we always try to keep them at 0. */ + if (clipped) { + mouse.x_delta = c_x - new_x; + mouse.y_delta = c_y - new_y; + } + setxy(c_x, c_y); + + m_printf("mouse_move_absolute(%d, %d, %d, %d) %d %d -> %d %d \n", + x, y, x_range, y_range, oldx, oldy, get_mx(), get_my()); + + return 1; +} + +static void do_move_abs(int x, int y, int x_range, int y_range, int vis) +{ + int moved = 0; + + if (mouse.px_range != x_range || mouse.py_range != y_range) + set_px_ranges(x_range, y_range); + + moved |= move_abs_mickeys(x - mouse.px_abs, y - mouse.py_abs, + x_range, y_range); + moved |= move_abs_coords(x, y, x_range, y_range, vis); + + mouse.px_abs = x; + mouse.py_abs = y; + + /* + * update the event mask + */ + if (moved) + mouse_move(1); +} + +static void int33_mouse_move_absolute(int x, int y, int x_range, int y_range, + int vis, void *udata) +{ + /* give an app some time to chew dragging */ + if (dragged.cnt > 1) { + m_printf("MOUSE: deferring abs move\n"); + dragged.skipped++; + dragged.x = x; + dragged.y = y; + dragged.x_range = x_range; + dragged.y_range = y_range; + return; + } + if (mouse.need_resync) { + /* this is mainly for Carmageddon. It can't tolerate the + * mickeys getting out of sync with coords. So we recalculate + * the position when switching from rel to abs mode. */ + int mx_range, my_range; + mouse.need_resync = 0; + /* for invisible cursor update deltas and return */ + if (mouse.cursor_on < 0) { + long long new_x, new_y; + scale_coords(x, y, x_range, y_range, &new_x, &new_y); + mouse.x_delta = get_mx() - new_x; + mouse.y_delta = get_my() - new_y; + mouse.px_abs = x; + mouse.py_abs = y; + return; + } + get_scale_range(&mx_range, &my_range); + mouse.px_abs = mouse.unsc_x * mouse.speed_x / + (mice->init_speed_x * mx_range); + mouse.py_abs = mouse.unsc_y * mouse.speed_y / + (mice->init_speed_y * my_range); + m_printf("MOUSE: synced coords, x:%i y:%i\n", + mouse.px_abs, mouse.py_abs); + } + do_move_abs(x, y, x_range, y_range, vis); +} + +/* this is for buggy apps that use the mickey tracking and have + * the unreachable areas in ungrabbed mode, because of the resolution + * mismatch (they organize their own resolution via mickey tracking). + * Transport Tycoon is an example. + * The idea is that having an unreachable area only at the bottom is + * better than randomly in different places, like at the top. + */ +static void int33_mouse_drag_to_corner(int x_range, int y_range, void *udata) +{ + m_printf("MOUSE: drag to corner, %i\n", dragged.cnt); + if (dragged.cnt > 1) + return; + int33_mouse_move_relative(-3 * x_range, -3 * y_range, x_range, y_range, + udata); + dragged.cnt = 5; + dragged.skipped = 0; + mouse.px_abs = 0; + mouse.py_abs = 0; + mouse.x_delta = mouse.y_delta = 0; + mouse.need_resync = 0; +} + +/* + * add the event to the current event mask + */ +void +mouse_delta(int event) +{ + mouse_events |= event; + reset_idle(0); +} + +/* + * call PS/2 (int15) event handler + */ +static void call_int15_mouse_event_handler(void) +{ + int status; + unsigned int ssp, sp; + int dx, dy, cnt = 0; + + rm_stack_enter(); + ssp = SEGOFF2LINEAR(SREG(ss), 0); + sp = LWORD(esp); + + do { + cnt++; + dx = mickeyx(); + /* PS/2 wants the y direction reversed */ + dy = -mickeyy(); + + status = (mouse.rbutton ? 2 : 0) | (mouse.lbutton ? 1 : 0) | 8; + if (mouse.threebuttons) + status |= (mouse.mbutton ? 4 : 0); + /* overflow; many DOS drivers do a simple cbw on the value... */ + if (dx < -128 || dx > 127) + dx = dx < -128 ? -128 : 127; + if (dy < -128 || dy > 127) + dy = dy < -128 ? -128 : 127; + mouse.unscm_x -= get_unsc_mk_x(dx * 8); + mouse.unscm_y += get_unsc_mk_y(dy * 8); + /* we'll call the handler again */ + if (dx < 0) + status |= 0x10; + if (dy < 0) + status |= 0x20; + + m_printf("PS2MOUSE data: dx=%hhx, dy=%hhx, status=%hx\n", (Bit8u)dx, (Bit8u)dy, (unsigned short)status); + pushw(ssp, sp, status); + pushw(ssp, sp, dx & 0xff); + pushw(ssp, sp, dy & 0xff); + pushw(ssp, sp, 0); + LWORD(esp) -= 8; + + /* jump to mouse cs:ip */ + m_printf("PS2MOUSE: .........jumping to %04x:%04x %i times\n", + mouse.ps2.cs, mouse.ps2.ip, cnt); + do_call_back(mouse.ps2.cs, mouse.ps2.ip); + LWORD(esp) += 8; + } while (mickeyx() || mickeyy()); + + rm_stack_leave(); +} + +static void call_int33_mouse_event_handler(void) +{ + rm_stack_enter(); + LWORD(eax) = mouse_events; + LWORD(ecx) = MOUSE_RX; + LWORD(edx) = MOUSE_RY; + LWORD(esi) = mickeyx(); + LWORD(edi) = mickeyy(); + LO(bx) = (mouse.rbutton ? 2 : 0) | (mouse.lbutton ? 1 : 0); + if (mouse.threebuttons) + LO(bx) |= (mouse.mbutton ? 4 : 0); + HI(bx) = mouse.wmcount; + mouse.wmcount = 0; + + /* jump to mouse cs:ip */ + m_printf("MOUSE: event %d, x %d(%d), y %d(%d), mx %d, my %d, b %x\n", + mouse_events, MOUSE_RX, get_mx(), MOUSE_RY, get_my(), mickeyx(), + mickeyy(), LWORD(ebx)); + m_printf("MOUSE: .........jumping to %04x:%04x\n", mouse.cs, mouse.ip); + SREG(ds) = mouse.cs; /* put DS in user routine */ + do_call_back(mouse.cs, mouse.ip); + rm_stack_leave(); + + /* When mouse is dragged to the corner with mouse_drag_to_corner(), + * the mickey counters are going crazy. This is not a problem when + * an app _reads_ the counters - they are then reset. However, if it + * doesn't read them but instead monitors them via callback (Carmageddon), + * then we need to sync them with coords eventually, or, otherwise, drop + * the dragging hack entirely. Since the dragging hack is pretty + * useful and the idea of monitoring mickeys is outright silly (and + * almost no progs do that), the dragging hack stays, and the hack + * below compensates its effect on mickeys. - stsp */ + if (dragged.cnt) { + /* syncing mickey counters with coords is silly, I know, but + * Carmageddon needs this after dragging mouse to the corner. */ + setmxy(get_mx() * mouse.speed_x, get_my() * mouse.speed_y); + dragged.cnt = 0; + m_printf("MOUSE: mickey synced with coords, x:%i->%i y:%i->%i\n", + get_mx(), mickeyx(), get_my(), mickeyy()); + } +} + +/* this function is called from int74 via inte6 */ +static void call_mouse_event_handler(void *arg) +{ + int handled = 0; + + if ((mouse.mask & mouse_events) && (mouse.cs || mouse.ip)) { + call_int33_mouse_event_handler(); + mouse_events = 0; + handled = 1; + } else { + m_printf("MOUSE: Skipping event handler, " + "mask=0x%x, ev=0x%x, cs=0x%x, ip=0x%x\n", + mouse.mask, mouse_events, mouse.cs, mouse.ip); + } + + if (handled && dragged.skipped) { + dragged.skipped = 0; + do_move_abs(dragged.x, dragged.y, dragged.x_range, dragged.y_range, + mouse.cursor_on >= 0); + } +} + +/* unconditional mouse cursor update */ +static void mouse_do_cur(int callback) +{ + if (mouse.gfx_cursor) { + graph_cursor(); + } + else { + text_cursor(); + } + + if (mice->native_cursor || !callback) + return; +#if 0 + /* this callback is used to e.g. warp the X cursor if int33/ax=4 + requested it to be moved */ + mouse_client_set_cursor(mouse.cursor_on == 0?1: 0, + get_mx() - mouse.x_delta, + get_my() - mouse.y_delta, + mouse.maxx - MOUSE_MINX +1, mouse.maxy - MOUSE_MINY +1); +#endif +} + +/* conditionally update the mouse cursor only if it's changed position. */ +static void +mouse_update_cursor(void) +{ + /* sigh, too many programs seem to expect the mouse cursor + to magically redraw itself, so we'll bend to their will... */ + if (MOUSE_RX != mouse.oldrx || MOUSE_RY != mouse.oldry) { + mouse_do_cur(0); + mouse.oldrx = MOUSE_RX; + mouse.oldry = MOUSE_RY; + } +} + +void +text_cursor(void) +{ + unsigned int p; + int offset; + int cx, cy; + int co = READ_WORD(BIOS_SCREEN_COLUMNS); + p = screen_adr(mouse.display_page); + offset = mouse_erase.x*2; + cx = MOUSE_RX >> mouse.xshift; + cy = MOUSE_RY >> mouse.yshift; + + if (mouse_erase.drawn) { + /* only erase the mouse cursor if it's the same thing we + drew; some applications seem to reset the mouse + *after* clearing the screen and we end up leaving + glitches behind. */ + if (vga_read_word(p + offset) == mouse_erase.backingstore.text[1]) + vga_write_word(p + offset, mouse_erase.backingstore.text[0]); + mouse_erase.drawn = FALSE; + } + + if (mouse.cursor_on != 0 || !mice->native_cursor) + return; + + /* remember where we drew this. */ + mouse_erase.x = cx + cy * co; + offset = mouse_erase.x*2; + mouse_erase.backingstore.text[0] = vga_read_word(p + offset); + + /* draw it. */ + mouse_erase.backingstore.text[1] = + (vga_read_word(p + offset) & mouse.textscreenmask) ^ mouse.textcursormask; + vga_write_word(p + offset, mouse_erase.backingstore.text[1]); + + mouse_erase.drawn = TRUE; +} + +void +graph_cursor(void) +{ + erase_graphics_cursor(&mouse_erase); + + /* draw_graphics_cursor wants screen coordinates, we have coordinates + based on width of 640; hotspot is always in screen coordinates. */ + if (mouse.cursor_on == 0 && mice->native_cursor) + draw_graphics_cursor(MOUSE_RX >> mouse.xshift, MOUSE_RY >> mouse.yshift, + mouse.hotx,mouse.hoty,16,16,&mouse_erase); +} + + +static void mouse_curtick(void) +{ + if (!mice->intdrv) + return; + +// if (debug_level('m') >= 9) +// m_printf("MOUSE: curtick x:%d y:%d\n", MOUSE_RX, MOUSE_RY); + + /* HACK: we need some time for an app to sense the dragging event */ + if (dragged.cnt > 1) { + dragged.cnt--; + } else if (dragged.skipped) { + dragged.skipped = 0; + do_move_abs(dragged.x, dragged.y, dragged.x_range, dragged.y_range, + mouse.cursor_on >= 0); + } + if (mouse.cursor_on != 0) + return; + + /* we used to do an unconditional update here, but that causes a + distracting flicker in the mouse cursor. */ + mouse_update_cursor(); +} + +static enum VirqSwRet do_mouse_irq(void *arg) +{ + int ret = VIRQ_SWRET_DONE; + + if (mouse.ps2.state && (mouse.ps2.cs || mouse.ps2.ip)) + return ret; + if (mouse_events) { + coopth_start(mouse_tid, NULL); + ret = VIRQ_SWRET_BH; + } + return ret; +} + +static enum VirqHwRet do_mouse_fifo(void *arg) +{ + int cnt = mousedrv_process_fifo("int33 mouse"); + if (cnt == -1) { + error("mouse fifo empty\n"); + return VIRQ_HWRET_DONE; + } + if (mouse_events) + pic_request(12); // for ps2 + return (cnt ? VIRQ_HWRET_CONT : VIRQ_HWRET_DONE); +} + +/* + * DANG_BEGIN_FUNCTION mouse_init + * + * description: + * Initialize internal mouse. + * + * DANG_END_FUNCTION + */ +static int int33_mouse_init(void) +{ + if (!mice->intdrv) + return 0; + + /* set minimum internal resolution + * 640x200 is a long standing dosemu default + * although I accidentally broke it broke it by flipping my X & Y coordinates. + * -- Eric Biederman 29 May 2000 + */ + mouse.min_max_x = 640; + mouse.min_max_y = 200; + + mouse.px_range = mouse.py_range = 1; + mouse.px_abs = mouse.py_abs = 0; + + /* we'll admit we have three buttons if the user asked us to emulate + one or else we think the mouse actually has a third button. */ + if (mice->emulate3buttons || mice->has3buttons) + mouse.threebuttons = TRUE; + else + mouse.threebuttons = FALSE; + + mouse.enabled = FALSE; + + mice->native_cursor = 1; + mouse.cursor_on = -1; + mice->init_speed_x = INIT_SPEED_X; + mice->init_speed_y = INIT_SPEED_Y; + mouse.speed_x = INIT_SPEED_X; + mouse.speed_y = INIT_SPEED_Y; + mouse.sens_x = 100; + mouse.sens_y = 100; + mouse.threshold = 200; + mouse.exc_lx = mouse.exc_ux = -1; + mouse.exc_ly = mouse.exc_uy = -1; + + virq_register(VIRQ_MOUSE, do_mouse_fifo, do_mouse_irq, NULL); + mouse_tid = coopth_create("mouse", call_mouse_event_handler); + sigalrm_register_handler(mouse_curtick); + + m_printf("MOUSE: INIT complete\n"); + return 1; +} + +static int int33_mouse_accepts(int from, void *udata) +{ + if (!mice->intdrv) + return 0; + /* if commouse.c is active, we only accept events from it, + * and nothing else. Those events ignore .accepts member. */ + if (mice->com != -1) + return 0; + /* for 2 mice see if source is ours */ + if (mice->type != mice->dev_type) + return (from == mice->type); + return 1; +} + +static void fifo_notify(void *udata) +{ + virq_raise(VIRQ_MOUSE); +} + +void mouse_late_init(void) +{ + if (!mice->intdrv) return; + + mouse.enabled = FALSE; + mouse.cursor_on = -1; + mouse_client_show_cursor(0); +} + +void mouse_set_win31_mode(void) +{ + if (!mice->intdrv) { + CARRY; + return; + } + + mouse.maxx = 65535; + mouse.maxy = 65535; + mouse.win31_mode = 1; + m_printf("MOUSE: Enabled win31 mode\n"); + + LWORD(eax) = 0; +} + +void +dosemu_mouse_close(void) +{ + mouse_client_close(); +} + +/* TO DO LIST: (in no particular order) + - play with acceleration profiles more +*/ + +struct mouse_drv int33_mouse = { + .init = int33_mouse_init, + .accepts = int33_mouse_accepts, + .move_button = int33_mouse_move_button, + .move_buttons = int33_mouse_move_buttons, + .move_wheel = int33_mouse_move_wheel, + .move_relative = int33_mouse_move_relative, + .move_mickeys = int33_mouse_move_mickeys, + .move_absolute = int33_mouse_move_absolute, + .drag_to_corner = int33_mouse_drag_to_corner, + .enable_native_cursor = int33_mouse_enable_native_cursor, + .name = "int33 mouse", +}; + +CONSTRUCTOR(static void int33_mouse_register(void)) +{ + register_mouse_driver_buffered(&int33_mouse, fifo_notify); +} diff --git a/src/base/mouse/mousedrv.c b/src/base/mouse/mousedrv.c new file mode 100644 index 0000000..5989ccc --- /dev/null +++ b/src/base/mouse/mousedrv.c @@ -0,0 +1,368 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * swappable mouse drivers + * + * Author: Stas Sergeev + */ +#include +#include +#include +#include +#include "emu.h" +#include "serial.h" +#include "utilities.h" +#include "ringbuf.h" +#include "mouse.h" + + +struct bdrv_s { + struct mouse_drv *drv; + void *udata; + struct rng_s buf; + pthread_mutex_t buf_mtx; + void (*callback)(void *); + void **arg_p; +}; + +struct mouse_drv_wrp { + struct mouse_drv *drv; + struct mouse_drv_wrp *next; + void *udata; + int initialized; + struct bdrv_s bdrv; +}; +static struct mouse_drv_wrp *mdrv; + +enum MsApiIds { + MID_move_button, + MID_move_buttons, + MID_move_wheel, + MID_move_relative, + MID_move_mickeys, + MID_move_absolute, + MID_drag_to_corner, + MID_enable_native_cursor, +}; + +#define B_MAX_ARGS 10 +struct mbuf_s { + enum MsApiIds id; + int args[B_MAX_ARGS]; +}; + +struct mouse_client_wrp { + struct mouse_client *clnt; + int initialized; +}; +#define MAX_MOUSE_CLIENTS 20 +static struct mouse_client_wrp Mouse[MAX_MOUSE_CLIENTS]; +static int mclnt_num; + +/* register mouse at the back of the linked list */ +void register_mouse_client(struct mouse_client *mouse) +{ + assert(mclnt_num < MAX_MOUSE_CLIENTS); + Mouse[mclnt_num++].clnt = mouse; +} + +#define mouse_client_f(t, f, p, c) \ +t mouse_client_##f(p) \ +{ \ + int i; \ + for (i = 0; i < mclnt_num; i++) { \ + if (!Mouse[i].initialized || !Mouse[i].clnt->f) \ + continue; \ + Mouse[i].clnt->f c; \ + } \ +} +mouse_client_f(void, close, void, ()) +mouse_client_f(void, post_init, void, ()) +mouse_client_f(void, show_cursor, int yes, (yes)) +mouse_client_f(void, reset, void, ()) + +static struct mouse_drv_wrp *do_create_driver(struct mouse_drv *mouse) +{ + struct mouse_drv_wrp *ms; + + ms = malloc(sizeof(*ms)); + ms->drv = mouse; + ms->bdrv.drv = NULL; + ms->udata = NULL; + ms->initialized = 0; + ms->next = NULL; + return ms; +} + +void register_mouse_driver(struct mouse_drv *mouse) +{ + struct mouse_drv_wrp *m, *ms; + + ms = do_create_driver(mouse); + if (mdrv == NULL) { + mdrv = ms; + } else { + for (m = mdrv; m->next; m = m->next); + m->next = ms; + } +} + +void mousedrv_set_udata(const char *name, void *udata) +{ + struct mouse_drv_wrp *m; + for (m = mdrv; m; m = m->next) { + if (strcmp(name, m->drv->name) == 0) { + m->udata = udata; + break; + } + } +} + +static void fifo_mdrv_add(struct bdrv_s *r, struct mbuf_s *b) +{ + int rc, cnt; + + pthread_mutex_lock(&r->buf_mtx); + rc = rng_put(&r->buf, b); + cnt = rng_count(&r->buf); + pthread_mutex_unlock(&r->buf_mtx); + if (!rc) + error("mouse queue overflow\n"); + if (cnt == 1) + r->callback(*r->arg_p); +} + +#define MOUSE_BO(n, DEF, ...) \ +static void fifo_mdrv_##n DEF \ +{ \ + struct mbuf_s b = { MID_##n, {__VA_ARGS__} }; \ + fifo_mdrv_add(udata, &b); \ +} + +MOUSE_BO(move_button, (int num, int press, void *udata), + num, press) +MOUSE_BO(move_buttons, (int lbutton, int mbutton, int rbutton, void *udata), + lbutton, mbutton, rbutton) +MOUSE_BO(move_wheel, (int dy, void *udata), dy) +MOUSE_BO(move_relative, (int dx, int dy, int x_range, int y_range, + void *udata), + dx, dy, x_range, y_range) +MOUSE_BO(move_mickeys, (int dx, int dy, void *udata), dx, dy) +MOUSE_BO(move_absolute, (int x, int y, int x_range, int y_range, int vis, + void *udata), + x, y, x_range, y_range, vis) +MOUSE_BO(drag_to_corner, (int x_range, int y_range, void *udata), + x_range, y_range) +MOUSE_BO(enable_native_cursor, (int flag, void *udata), flag) + +struct mouse_drv fifo_mdrv = { + .move_button = fifo_mdrv_move_button, + .move_buttons = fifo_mdrv_move_buttons, + .move_wheel = fifo_mdrv_move_wheel, + .move_relative = fifo_mdrv_move_relative, + .move_mickeys = fifo_mdrv_move_mickeys, + .move_absolute = fifo_mdrv_move_absolute, + .drag_to_corner = fifo_mdrv_drag_to_corner, + .enable_native_cursor = fifo_mdrv_enable_native_cursor, + .name = "fifo helper", +}; + +static void fifo_mdrv_init(struct mouse_drv_wrp *m, void (*cb)(void *)) +{ +#define M_FIFO_LEN 1024 + m->bdrv.drv = &fifo_mdrv; + rng_init(&m->bdrv.buf, M_FIFO_LEN, sizeof(struct mbuf_s)); + pthread_mutex_init(&m->bdrv.buf_mtx, NULL); + m->bdrv.udata = &m->bdrv; + m->bdrv.callback = cb; + m->bdrv.arg_p = &m->udata; +} + +static int do_process_fifo(struct mouse_drv_wrp *m) +{ + int rc, ret; + struct mbuf_s b; + struct mouse_drv *d = m->drv; + + assert(m->bdrv.drv); + pthread_mutex_lock(&m->bdrv.buf_mtx); + rc = rng_get(&m->bdrv.buf, &b); + ret = rng_count(&m->bdrv.buf); + pthread_mutex_unlock(&m->bdrv.buf_mtx); + if (!rc) + return -1; + switch (b.id) { +#define MP(n, ...) \ + case MID_##n: \ + d->n(__VA_ARGS__, m->udata); \ + break + + MP(move_button, b.args[0], b.args[1]); + MP(move_buttons, b.args[0], b.args[1], b.args[2]); + MP(move_wheel, b.args[0]); + MP(move_relative, b.args[0], b.args[1], b.args[2], b.args[3]); + MP(move_mickeys, b.args[0], b.args[1]); + MP(move_absolute, b.args[0], b.args[1], b.args[2], b.args[3], b.args[4]); + MP(drag_to_corner, b.args[0], b.args[1]); + MP(enable_native_cursor, b.args[0]); + } + return ret; +} + +int mousedrv_process_fifo(const char *name) +{ + struct mouse_drv_wrp *m; + for (m = mdrv; m; m = m->next) { + if (strcmp(name, m->drv->name) == 0) + return do_process_fifo(m); + } + return 0; +} + +void register_mouse_driver_buffered(struct mouse_drv *mouse, + void (*cb)(void *)) +{ + struct mouse_drv_wrp *m, *ms; + + ms = do_create_driver(mouse); + fifo_mdrv_init(ms, cb); + if (mdrv == NULL) { + mdrv = ms; + } else { + for (m = mdrv; m->next; m = m->next); + m->next = ms; + } +} + +static int none_init(void) +{ + return TRUE; +} + +static struct mouse_client Mouse_none = { + "No Mouse", /* name */ + none_init, /* init */ + NULL, /* close */ + NULL +}; + +static void mouse_client_init(void) +{ + int i; + +#ifdef USE_GPM + if (config.term) + load_plugin("gpm"); +#endif + register_mouse_client(&Mouse_raw); + register_mouse_client(&Mouse_none); + for (i = 0; i < mclnt_num; i++) { + m_printf("MOUSE: initialising '%s' mode mouse client\n", + Mouse[i].clnt->name); + + Mouse[i].initialized = Mouse[i].clnt->init ? Mouse[i].clnt->init() : TRUE; + if (Mouse[i].initialized) { + m_printf("MOUSE: Mouse init ok, '%s' mode\n", Mouse[i].clnt->name); + } + else { + m_printf("MOUSE: Mouse init ***failed***, '%s' mode\n", + Mouse[i].clnt->name); + } + } +} + +void dosemu_mouse_init(void) +{ + struct mouse_drv_wrp *m; + for (m = mdrv; m; m = m->next) { + if (!m->drv->init || m->drv->init()) + m->initialized = 1; + } + + mouse_client_init(); +} + +#define MOUSE_DO(n, DEF, ...) \ +void mouse_##n DEF \ +{ \ + struct mouse_drv_wrp *m; \ + for (m = mdrv; m; m = m->next) { \ + struct mouse_drv *d, *b; \ + void *ud; \ + if (!m->initialized) \ + continue; \ + d = m->drv; \ + b = m->bdrv.drv ?: d; \ + ud = m->bdrv.drv ? m->bdrv.udata : m->udata; \ + if (d->n && d->accepts(from, m->udata)) \ + b->n(__VA_ARGS__, ud); \ + } \ +} +MOUSE_DO(move_button, (int num, int press, int from), num, press) +MOUSE_DO(move_buttons, (int lbutton, int mbutton, int rbutton, int from), + lbutton, mbutton, rbutton) +MOUSE_DO(move_wheel, (int dy, int from), dy) +MOUSE_DO(move_relative, (int dx, int dy, int x_range, int y_range, int from), + dx, dy, x_range, y_range) +MOUSE_DO(move_mickeys, (int dx, int dy, int from), dx, dy) +MOUSE_DO(move_absolute, (int x, int y, int x_range, int y_range, int vis, + int from), + x, y, x_range, y_range, vis) +MOUSE_DO(drag_to_corner, (int x_range, int y_range, int from), + x_range, y_range) +MOUSE_DO(enable_native_cursor, (int flag, int from), flag) + +int mousedrv_accepts(const char *id, int from) +{ + struct mouse_drv_wrp *m; + for (m = mdrv; m; m = m->next) { + struct mouse_drv *d; + if (!m->initialized) + continue; + d = m->drv; + if (strcmp(d->name, id) != 0) + continue; + if (d->accepts) + return d->accepts(from, m->udata); + } + return 0; +} + +#define DID(...) (__VA_ARGS__, const char *id) +#define MOUSE_ID_DO(n, DEF, ...) \ +void mouse_##n##_id DID DEF \ +{ \ + struct mouse_drv_wrp *m; \ + for (m = mdrv; m; m = m->next) { \ + struct mouse_drv *d; \ + if (!m->initialized) \ + continue; \ + d = m->drv; \ + if (strcmp(d->name, id) != 0) \ + continue; \ + /* not checking for .accepts */ \ + if (d->n) \ + d->n(__VA_ARGS__, m->udata); \ + } \ +} +MOUSE_ID_DO(move_button, (int num, int press), + num, press) +MOUSE_ID_DO(move_buttons, (int lbutton, int mbutton, int rbutton), + lbutton, mbutton, rbutton) +MOUSE_ID_DO(move_wheel, (int dy), dy) +MOUSE_ID_DO(move_mickeys, (int dx, int dy), dx, dy) +MOUSE_ID_DO(enable_native_cursor, (int flag), flag) diff --git a/src/base/mouse/mouseint.c b/src/base/mouse/mouseint.c new file mode 100644 index 0000000..1425c98 --- /dev/null +++ b/src/base/mouse/mouseint.c @@ -0,0 +1,582 @@ +/* Modified for DOSEMU's internal mouse driver support + * by Alan Hourihane (alanh@fairlite.demon.co.uk) + * (29/4/94) + */ + +/* + * + * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. + * Copyright 1993 by David Dawes + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the names of Thomas Roell and David Dawes not be + * used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. Thomas Roell + * and David Dawes makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * THOMAS ROELL AND DAVID DAWES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THOMAS ROELL OR DAVID DAWES BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "emu.h" +#include "mouse.h" +#include "serial.h" +#include "iodev.h" +#include "ioselect.h" + +static void DOSEMUSetMouseSpeed(int old, int _new, unsigned cflag); + +static int mouse_frozen = 0; + +/* + * DOSEMUSetupMouse -- + * Sets up the mouse parameters + */ + +static void DOSEMUSetupMouse(void) +{ + mouse_t *mice = &config.mouse; + m_printf("MOUSE: DOSEMUSetupMouse called\n"); + mice->oldset = malloc(sizeof(*mice->oldset)); + tcgetattr(mice->fd, mice->oldset); + if (mice->dev_type == MOUSE_MOUSEMAN) + { + DOSEMUSetMouseSpeed(1200, 1200, mice->flags); + (void)RPT_SYSCALL(write(mice->fd, "*X", 2)); + DOSEMUSetMouseSpeed(1200, mice->baudRate, mice->flags); + } + else if (mice->dev_type != MOUSE_BUSMOUSE && mice->dev_type != MOUSE_PS2 && + mice->dev_type != MOUSE_IMPS2) + { + m_printf("MOUSE: setting speed to %d baud\n",mice->baudRate); +#if 0 /* this causes my dosemu to hang [rz] */ + DOSEMUSetMouseSpeed(9600, mice->baudRate, mice->flags); + DOSEMUSetMouseSpeed(4800, mice->baudRate, mice->flags); + DOSEMUSetMouseSpeed(2400, mice->baudRate, mice->flags); +#endif + DOSEMUSetMouseSpeed(1200, mice->baudRate, mice->flags); + if (mice->dev_type == MOUSE_LOGITECH) + { + m_printf("MOUSEINT: Switching to MM-SERIES protocol...\n"); + + /* Switch the mouse into MM series mode; actually, chances + are, if you have an older logitech mouse (like I do), you + were already *in* MM series mode. */ + (void)RPT_SYSCALL(write(mice->fd, "S", 1)); + + /* Need to use flags for MM series, not Logitech (bugfix) */ + DOSEMUSetMouseSpeed(mice->baudRate, mice->baudRate, + CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL); + } + + if (mice->dev_type == MOUSE_HITACHI) + { + char speedcmd; + + (void)RPT_SYSCALL(write(mice->fd, "z8", 2)); /* Set Parity = "NONE" */ + usleep(50000); + (void)RPT_SYSCALL(write(mice->fd, "zb", 2)); /* Set Format = "Binary" */ + usleep(50000); + (void)RPT_SYSCALL(write(mice->fd, "@", 1)); /* Set Report Mode = "Stream" */ + usleep(50000); + (void)RPT_SYSCALL(write(mice->fd, "R", 1)); /* Set Output Rate = "45 rps" */ + usleep(50000); + (void)RPT_SYSCALL(write(mice->fd, "I\x20", 2)); /* Set Incrememtal Mode "20" */ + usleep(50000); + (void)RPT_SYSCALL(write(mice->fd, "E", 1)); /* Set Data Type = "Relative */ + usleep(50000); + + /* These sample rates translate to 'lines per inch' on the Hitachi + tablet */ + if (mice->sampleRate <= 40) speedcmd = 'g'; + else if (mice->sampleRate <= 100) speedcmd = 'd'; + else if (mice->sampleRate <= 200) speedcmd = 'e'; + else if (mice->sampleRate <= 500) speedcmd = 'h'; + else if (mice->sampleRate <= 1000) speedcmd = 'j'; + else speedcmd = 'd'; + (void)RPT_SYSCALL(write(mice->fd, &speedcmd, 1)); + usleep(50000); + + (void)RPT_SYSCALL(write(mice->fd, "\021", 1)); /* Resume DATA output */ + } + else + { + m_printf("MOUSE: set sample rate to %d\n",mice->sampleRate); + if (mice->sampleRate <= 0) { (void)RPT_SYSCALL(write(mice->fd, "O", 1));} + else if (mice->sampleRate <= 15) { (void)RPT_SYSCALL(write(mice->fd, "J", 1));} + else if (mice->sampleRate <= 27) { (void)RPT_SYSCALL(write(mice->fd, "K", 1));} + else if (mice->sampleRate <= 42) { (void)RPT_SYSCALL(write(mice->fd, "L", 1));} + else if (mice->sampleRate <= 60) { (void)RPT_SYSCALL(write(mice->fd, "R", 1));} + else if (mice->sampleRate <= 85) { (void)RPT_SYSCALL(write(mice->fd, "M", 1));} + else if (mice->sampleRate <= 125) { (void)RPT_SYSCALL(write(mice->fd, "Q", 1));} + else { (void)RPT_SYSCALL(write(mice->fd, "N", 1));} + } + } + + if (mice->dev_type == MOUSE_IMPS2) + { + static unsigned char s1[] = { 243, 200, 243, 100, 243, 80, }; + static unsigned char s2[] = { 246, 230, 244, 243, 100, 232, 3, }; + write (mice->fd, s1, sizeof (s1)); + usleep (30000); + write (mice->fd, s2, sizeof (s2)); + usleep (30000); + tcflush (mice->fd, TCIFLUSH); + } + +#ifdef CLEARDTR_SUPPORT + if (mice->dev_type == MOUSE_MOUSESYSTEMS && (mice->cleardtr)) + { + int val = TIOCM_DTR; + m_printf("MOUSE: clearing DTR\n"); + ioctl(mice->fd, TIOCMBIC, &val); + } +#if 0 /* Jochen 05.05.94 */ +/* Not used for my 3 button mouse */ + if (mice->dev_type == MOUSE_MOUSESYSTEMS && (mice->flags & MF_CLEAR_RTS)) + { + int val = TIOCM_RTS; + m_printf("MOUSE: clearing RTS\n"); + ioctl(mice->fd, TIOCMBIC, &val); + } +#endif +#endif +} + +int DOSEMUMouseProtocol(unsigned char *rBuf, int nBytes, int type, + const char *id) +{ + int i, buttons=0, dx=0, dy=0; + mouse_t *mice = &config.mouse; + static int pBufP = 0; + static unsigned char pBuf[8]; + + static unsigned char proto[11][5] = { + /* hd_mask hd_id dp_mask dp_id nobytes */ + { 0x40, 0x40, 0x40, 0x00, 3 }, /* MicroSoft */ + { 0x40, 0x40, 0x40, 0x00, 3 }, /* MicroSoft-3Bext */ + { 0xf8, 0x80, 0x00, 0x00, 5 }, /* MouseSystems */ + { 0xe0, 0x80, 0x80, 0x00, 3 }, /* MMSeries */ + { 0xe0, 0x80, 0x80, 0x00, 3 }, /* Logitech */ + { 0xf8, 0x80, 0x00, 0x00, 5 }, /* BusMouse */ + { 0x40, 0x40, 0x40, 0x00, 3 }, /* MouseMan + [CHRIS-211092] */ + { 0xc0, 0x00, 0x00, 0x00, 3 }, /* PS/2 mouse */ + { 0xe0, 0x80, 0x80, 0x00, 3 }, /* MM_HitTablet */ + { 0x00, 0x00, 0x00, 0x00, 0 }, /* X-mouse (unused entry) */ + { 0xc0, 0x00, 0x00, 0x00, 4 }, /* IMPS/2 mouse */ + }; + + for ( i=0; i < nBytes; i++) { + /* + * check, if we have a usable data byte + */ + if (pBufP != 0 && type != MOUSE_PS2 && + type != MOUSE_IMPS2 && + ((rBuf[i] & proto[type][2]) != proto[type][3] + || rBuf[i] == 0x80)) { + m_printf("MOUSEINT: Skipping package, pBufP = %d\n",pBufP); + pBufP = 0; /* skip package */ + } + + /* + * check, if the current byte is the start of a new package + * if not, skip it + */ + if (pBufP == 0 && + (rBuf[i] & proto[type][0]) != proto[type][1]) { + m_printf("MOUSEINT: Skipping byte %02x\n",rBuf[i]); + continue; + } + + pBuf[pBufP++] = rBuf[i]; + if (pBufP != proto[type][4]) continue; + m_printf("MOUSEINT: package %02x %02x %02x\n",pBuf[0],pBuf[1],pBuf[2]); + /* + * assembly full package + */ + switch(type) { + + case MOUSE_MICROSOFT: /* The ms protocol, unextended. */ + buttons = ((pBuf[0] & 0x20) >> 3) | ((pBuf[0] & 0x10) >> 4); + dx = (char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F)); + dy = (char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F)); + break; + + case MOUSE_MS3BUTTON: /* Microsoft */ + { + /* The original micro$oft protocol, with a middle-button extension */ + /* + * some devices report a change of middle-button state by + * repeating the current button state (patch by Mark Lord) + */ + static unsigned char prev=0; + + if (pBuf[0]==0x40 && !(prev|pBuf[1]|pBuf[2])) + buttons = 2; /* third button on MS compatible mouse */ + else + buttons= ((pBuf[0] & 0x20) >> 3) | ((pBuf[0] & 0x10) >> 4); + prev = buttons; + dx = (char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F)); + dy = (char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F)); + } + break; + + case MOUSE_MOUSEMAN: /* MouseMan / TrackMan [CHRIS-211092] */ + /* + * the damned MouseMan has 3/4 bytes packets. The extra byte + * is only there if the middle button is active. + * I get the extra byte as a packet with magic numbers in it. + * and then switch to 4-byte mode (A.Rubini in gpm-1.10) + * Don't complain if you use this code with hi-res timers - AV + */ + /* chordMiddle is always 0! see config.c */ + if (mice->chordMiddle) + buttons = ((pBuf[0] & 0x30) == 0x30) ? 2 : + (((pBuf[0] & 0x20) >> 3) | ((pBuf[0] & 0x10) >> 4)); + else { + buttons = ((pBuf[0] & 0x20) >> 3) | ((pBuf[0] & 0x10) >> 4); + m_printf("MOUSEINT: buttons=%02x\n",buttons); + + /* + * if the middle button was pressed or released + * we get a fourth byte. Check if it is in the buffer + */ + if (i+1 < nBytes) { + if ((rBuf[i+1] & ~0x23)==0) { + buttons |= (rBuf[++i] & 0x20) >> 4; + m_printf("MOUSEINT: middle button from buffer: %02x\n", + buttons); + } + } else { + /* + * no data there, try to read it + * select seems not to work here ( it returns -1 with + * errno==EINTR ), so we poll for a few clock ticks + * I really don't like this. So, if anybody has a better + * idea... + */ + clock_t c; + int n; + /* + * Wait from 10 to 20ms for the data to arrive + * (8.3ms for a byte at 1200 baud) + */ + c = clock() + 3; + n=0; + do { + n = RPT_SYSCALL(read(mice->fd, rBuf+i, 1)); + m_printf("MOUSEINT: Inside read waiting for input!\n"); + } while (n < 1 && clock() < c); + if (n==1) { + /* + * There is data! + * Check if it is the status of the middle button, + * if not, decrement i so that the byte is used + * during the next loop + */ + if ((rBuf[i] & ~0x23)==0) { + buttons |= (rBuf[i] & 0x20) >> 4; + m_printf("MOUSEINT: middle button read: %02x\n", + buttons); + } else { + i--; + } + } + } + } /* chordMiddle */ + dx = (char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F)); + dy = (char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F)); + break; + + case MOUSE_MOUSESYSTEMS: /* Mouse Systems Corp */ + buttons = (~pBuf[0]) & 0x07; + dx = (char)(pBuf[1]) + (char)(pBuf[3]); + dy = - ((char)(pBuf[2]) + (char)(pBuf[4])); + break; + + case MOUSE_HITACHI: /* MM_HitTablet */ + buttons = pBuf[0] & 0x07; + if (buttons != 0) + buttons = 1 << (buttons - 1); + dx = (pBuf[0] & 0x10) ? pBuf[1] : -pBuf[1]; + dy = (pBuf[0] & 0x08) ? -pBuf[2] : pBuf[2]; + break; + + case MOUSE_MMSERIES: /* MM Series */ + case MOUSE_LOGITECH: /* Logitech Mice */ + buttons = pBuf[0] & 0x07; + dx = (pBuf[0] & 0x10) ? pBuf[1] : -pBuf[1]; + dy = (pBuf[0] & 0x08) ? -pBuf[2] : pBuf[2]; + break; + + case MOUSE_BUSMOUSE: /* BusMouse */ + buttons = (~pBuf[0]) & 0x07; + dx = (char)pBuf[1]; + dy = - (char)pBuf[2]; + break; + + case MOUSE_PS2: /* PS/2 mouse */ + case MOUSE_IMPS2: /* IMPS/2 mouse */ + buttons = (pBuf[0] & 0x04) >> 1 | /* Middle */ + (pBuf[0] & 0x02) >> 1 | /* Right */ + (pBuf[0] & 0x01) << 2; /* Left */ + dx = (pBuf[0] & 0x10) ? pBuf[1]-256 : pBuf[1]; + dy = (pBuf[0] & 0x20) ? -(pBuf[2]-256) : -pBuf[2]; + break; + } + + if (id) { + mouse_move_buttons_id(!!(buttons & 0x04), !!(buttons & 0x02), + !!(buttons & 0x01), id); + if (dx || dy) + mouse_move_mickeys_id(dx, dy, id); + } else { + mouse_move_buttons(!!(buttons & 0x04), !!(buttons & 0x02), + !!(buttons & 0x01), type); + if (dx || dy) + mouse_move_mickeys(dx, dy, type); + } + pBufP = 0; + return (i + 1); + } /* assembly full package */ + return nBytes; +} + +static void DOSEMUSetMouseSpeed(int old, int _new, unsigned cflag) +{ + struct termios tty; + const char *c; + mouse_t *mice = &config.mouse; + + m_printf("MOUSE: set speed %d -> %d\n",old,_new); + + if (tcgetattr(mice->fd, &tty) < 0) + { + m_printf("MOUSE: Unable to get status of mouse. Mouse may not function properly.\n"); + } + + tty.c_iflag = IGNBRK | IGNPAR; + tty.c_oflag = 0; + tty.c_lflag = 0; + tty.c_cflag = (tcflag_t)cflag; + tty.c_cc[VTIME] = 0; + tty.c_cc[VMIN] = 1; + + switch (old) + { + case 9600: + cfsetispeed(&tty, B9600); + cfsetospeed(&tty, B9600); + break; + case 4800: + cfsetispeed(&tty, B4800); + cfsetospeed(&tty, B4800); + break; + case 2400: + cfsetispeed(&tty, B2400); + cfsetospeed(&tty, B2400); + break; + case 1200: + default: + cfsetispeed(&tty, B1200); + cfsetospeed(&tty, B1200); + } + + m_printf("MOUSE: calling tcsetattr\n"); + if (tcsetattr(mice->fd, TCSADRAIN, &tty) < 0) + { + m_printf("MOUSE: Unable to set mouse attributes. Mouse may not function properly.\n"); + } + + switch (_new) + { + case 9600: + c = "*q"; + cfsetispeed(&tty, B9600); + cfsetospeed(&tty, B9600); + break; + case 4800: + c = "*p"; + cfsetispeed(&tty, B4800); + cfsetospeed(&tty, B4800); + break; + case 2400: + c = "*o"; + cfsetispeed(&tty, B2400); + cfsetospeed(&tty, B2400); + break; + case 1200: + default: + c = "*n"; + cfsetispeed(&tty, B1200); + cfsetospeed(&tty, B1200); + } + + m_printf("MOUSE: writing speed\n"); + if (write(mice->fd, c, 2) != 2) + { + m_printf("MOUSE: Unable to write to mouse fd. Mouse may not function properly.\n"); + } + + m_printf("MOUSE: calling tcsetattr\n"); + if (tcsetattr(mice->fd, TCSADRAIN, &tty) < 0) + { + m_printf("MOUSE: Unable to set mouse attributes. Mouse may not function properly.\n"); + } +} + +static void raw_mouse_getevent(int fd, void *arg) +{ + #define MOUSE_BUFFER 8 + unsigned char rBuf[MOUSE_BUFFER]; + mouse_t *mice = &config.mouse; + const char *id = NULL; + int nBytes; + + nBytes = RPT_SYSCALL(read(mice->fd, rBuf, MOUSE_BUFFER)); + ioselect_complete(fd); + if (nBytes <= 0) + return; + m_printf("MOUSE: Read %d bytes.\n", nBytes); + if (config.num_serial_mices) + id = "serial mouse"; + DOSEMUMouseProtocol(rBuf, nBytes, mice->dev_type, id); +} + +static void parent_close_mouse (void) +{ + mouse_t *mice = &config.mouse; + if (mice->fd > 0) { + remove_from_io_select(mice->fd); + DOS_SYSCALL(close (mice->fd)); + } +} + +void mouse_priv_init(void) +{ + PRIV_SAVE_AREA + mouse_t *mice = &config.mouse; + struct stat buf; + int ret, mode = O_RDWR | O_NONBLOCK; + + mice->fd = -1; + if (mice->type != mice->dev_type && !config.num_serial_mices) + return; + + /* in non-graphics mode don't steal the device from gpm */ + if (!mice->dev || !strlen(mice->dev) || (!config.vga && + !config.num_serial_mices)) + return; + + ret = stat(mice->dev, &buf); + if (ret == -1) { + error("Cannot stat internal mouse device '%s'\n", strerror(errno)); + return; + } + if (S_ISFIFO(buf.st_mode) || mice->dev_type == MOUSE_BUSMOUSE || + mice->dev_type == MOUSE_PS2) { + /* no write permission is necessary for FIFO's (eg., gpm) */ + mode = O_RDONLY | O_NONBLOCK; + } + + enter_priv_on(); + mice->fd = DOS_SYSCALL(open(mice->dev, mode)); + leave_priv_setting(); + if (mice->fd == -1) + error("Cannot open internal mouse device %s\n", mice->dev); +} + +void freeze_mouse(void) +{ + mouse_t *mice = &config.mouse; + if (mouse_frozen || mice->fd == -1) + return; + remove_from_io_select(mice->fd); + mouse_frozen = 1; +} + +void unfreeze_mouse(void) +{ + mouse_t *mice = &config.mouse; + if (!mouse_frozen || mice->fd == -1) + return; + add_to_io_select(mice->fd, raw_mouse_getevent, NULL); + mouse_frozen = 0; +} + +static int raw_mouse_init(void) +{ + mouse_t *mice = &config.mouse; + struct stat buf; + int ret; + + m_printf("Opening internal mouse: %s\n", mice->dev); + if (mice->fd == -1) + return FALSE; + add_to_io_select(mice->fd, raw_mouse_getevent, NULL); + + ret = fstat(mice->fd, &buf); + if (ret == 0 && !S_ISFIFO(buf.st_mode) && + mice->dev_type != MOUSE_BUSMOUSE && mice->dev_type != MOUSE_PS2) + DOSEMUSetupMouse(); + /* this is only to try to get the initial internal driver two/three + button mode state correct; user can override it later. */ + if (mice->dev_type == MOUSE_MICROSOFT) + mice->has3buttons = FALSE; + else + mice->has3buttons = TRUE; + iodev_add_device(mice->dev); + return TRUE; +} + +static void raw_mouse_close(void) +{ + int result; + mouse_t *mice = &config.mouse; + + if (mice->fd == -1) + return; + + if (mice->oldset) { + m_printf("mouse_close: calling tcsetattr\n"); + result=tcsetattr(mice->fd, TCSANOW, mice->oldset); + if (result==0) + m_printf("mouse_close: tcsetattr ok\n"); + else + m_printf("mouse_close: tcsetattr failed: %s\n",strerror(errno)); + } + m_printf("mouse_close: closing mouse device, fd=%d\n",mice->fd); + parent_close_mouse(); + m_printf("mouse_close: ok\n"); +} + +struct mouse_client Mouse_raw = { + "raw", /* name */ + raw_mouse_init, /* init */ + raw_mouse_close, /* close */ + NULL +}; + diff --git a/src/base/mouse/mousevid.c b/src/base/mouse/mousevid.c new file mode 100644 index 0000000..f7f20ec --- /dev/null +++ b/src/base/mouse/mousevid.c @@ -0,0 +1,129 @@ +/* + * + * DANG_BEGIN_CHANGELOG + * + * 1998/02/22: Video mode information is now derived directly from VGAEmu, if + * X is active. + * + * -- sw (Steffen Winterfeldt ) + * + * DANG_END_CHANGELOG + * + */ + +#include "mousevid.h" +#include "vgaemu.h" + +#define MDA_OFFS 0x10000 +#define CGA_OFFS 0x18000 +#define EGA_OFFS 0x00000 + +static struct mousevideoinfo videomodes[] = { + { 0,'T',40,25,160,ORG_TEXT,CGA_OFFS }, + { 1,'T',40,25,160,ORG_TEXT,CGA_OFFS }, + { 2,'T',80,25,160,ORG_TEXT,CGA_OFFS }, + { 3,'T',80,25,160,ORG_TEXT,CGA_OFFS }, + { 4,'G',320,200,40,ORG_CGA4,CGA_OFFS }, + { 5,'G',320,200,40,ORG_CGA4,CGA_OFFS }, + { 6,'G',640,200,40,ORG_CGA2,CGA_OFFS }, + { 7,'T',80,25,160,ORG_TEXT,MDA_OFFS }, + { 8 }, /* don't think any PCjr's will run linux */ + { 9 }, /* don't think any PCjr's will run linux */ + { 0xA }, /* don't think any PCjr's will run linux */ + { 0xB }, /* used during font setup */ + { 0xC }, /* used during font setup */ + { 0xD,'G',320,200,40,ORG_EGA16,EGA_OFFS }, + { 0xE,'G',640,200,80,ORG_EGA16,EGA_OFFS }, + { 0xF,'G',640,350,80,ORG_EGA16,EGA_OFFS }, /* actually mono */ + { 0x10,'G',640,350,80,ORG_EGA16,EGA_OFFS }, + { 0x11,'G',640,480,80,ORG_EGA16,EGA_OFFS }, /* actually mono */ + { 0x12,'G',640,480,80,ORG_EGA16,EGA_OFFS }, + { 0x13,'G',320,200,320,ORG_VGA,EGA_OFFS }, +}; + +static int vesamode = -1; + +void vidmouse_set_video_mode(int mode) +{ + vesamode = mode; +} + +int vidmouse_get_video_mode(int mode, struct mousevideoinfo *r_vmo) +{ + *r_vmo = videomodes[mode]; + return 0; +} + +int get_current_video_mode(struct mousevideoinfo *r_vmo) +{ + vga_mode_info *vmi = NULL; + /* we catch int10; every vesa mode set calls the helper (via cx) */ + int i = READ_BYTE(BIOS_VIDEO_MODE); + int ret; + + if(i > 0x13 || vesamode != -1) { + /* vesa mode?: + use the VGAEMU mode table; it may not be 100% correct for + the console but it's right for most common modes */ + if (vesamode == -1) + vesamode = i; + m_printf("MOUSE: looking for vesamode %x\n", vesamode); + vmi = vga_emu_find_mode(vesamode, NULL); + if (vmi == NULL) { + m_printf("MOUSE: Unknown video mode 0x%04x, no mouse cursor.\n", i); + return i; + } + r_vmo->mode = vmi->VGA_mode; + r_vmo->textgraph = vmi->mode_class == TEXT ? 'T' : 'G'; + if(vmi->mode_class == TEXT) { + r_vmo->width = vmi->text_width; + r_vmo->height = vmi->text_height; + r_vmo->bytesperline = vmi->text_width * 2; + } + else { + r_vmo->width = vmi->width; + r_vmo->height = vmi->height; + /* dword aligned */ + r_vmo->bytesperline = (vmi->width + 3) & ~3; + if(vmi->color_bits > 8) + r_vmo->bytesperline *= ((vmi->color_bits + 7) & ~7) >> 3; + } + switch(vmi->type) { + case TEXT: + case TEXT_MONO: r_vmo->organization = ORG_TEXT; break; + case CGA: r_vmo->organization = ORG_CGA4; break; + case PL1: + case PL2: + case PL4: r_vmo->organization = ORG_EGA16; break; + case P8: r_vmo->organization = ORG_VGA; break; + default: r_vmo->organization = ORG_UNKNOWN; + } + r_vmo->offset = ((vmi->buffer_start - 0xa000) << 4); + ret = 0; + } + else + { + /* invalid video mode */ + if(i < 0 || i > 0x13 || !videomodes[i].textgraph) { + m_printf("MOUSE: Unknown video mode 0x%02x, no mouse cursor.\n", i); + return i; + } + ret = vidmouse_get_video_mode(i, r_vmo); + if (r_vmo->textgraph == 'T') { /* read the size from the bios data area */ + r_vmo->width = READ_WORD(BIOS_SCREEN_COLUMNS); + r_vmo->height = READ_BYTE(BIOS_ROWS_ON_SCREEN_MINUS_1) +1; + r_vmo->bytesperline = r_vmo->width *2; + } + r_vmo->offset += READ_WORD(BIOS_VIDEO_MEMORY_ADDRESS); + } + + m_printf( + "MOUSE: video mode 0x%02x found (%c%dx%d at 0x%04x).\n", + r_vmo->mode, r_vmo->textgraph, + r_vmo->width, r_vmo->height, + r_vmo->offset + 0xa0000 + ); + + /* valid video mode */ + return ret; +} diff --git a/src/base/mouse/mousevid.h b/src/base/mouse/mousevid.h new file mode 100644 index 0000000..0c0e6b6 --- /dev/null +++ b/src/base/mouse/mousevid.h @@ -0,0 +1,42 @@ +/* + * BIOS video modes + * This file provides the basis to implement a VGA/EGA/CGA/MDA BIOS emulator. + * Contains currently supported video modes. + */ + +/* + * This file is currently used by the mouse driver to obtain + * certain video mode parameters. + */ + +#ifndef MOUSEVID_H +#define MOUSEVID_H + +#include "emu.h" +#include "video.h" +#include "bios.h" + +/* video memory organization types */ +enum { + ORG_TEXT, + ORG_CGA2, + ORG_CGA4, + ORG_EGA16, + ORG_VGA, + ORG_UNKNOWN +}; + +struct mousevideoinfo { + int mode; /* mode number (currently redundant) */ + char textgraph; /* 'G' for graphics mode, 'T' for text mode */ + int width, height; /* extents */ + int bytesperline; /* bytes per line */ + int organization; /* ram organization method, see above */ + int offset; /* offset from 0xA0000 of vram for this mode */ +}; + +int get_current_video_mode(struct mousevideoinfo *r_vmo); +void vidmouse_set_video_mode(int mode); +int vidmouse_get_video_mode(int mode, struct mousevideoinfo *r_vmo); + +#endif diff --git a/src/base/serial/Makefile b/src/base/serial/Makefile new file mode 100644 index 0000000..a21c3f3 --- /dev/null +++ b/src/base/serial/Makefile @@ -0,0 +1,24 @@ +top_builddir=../../.. +include $(top_builddir)/Makefile.conf + +# This is the Makefile for the serial-subdirectory of DOSEMU. +# Please send comments and bug-reports to marky@magmacom.com +# Please read the code in this directory for more information. + +#The C files, include files and dependancies here. +CFILES = ser_init.c ser_irq.c ser_ports.c int14.c fossil.c tty_io.c \ + sermouse.c commouse.c comredir.c nullmm.c +DEPENDS=$(CFILES:.c=.d) +HFILES = ser_defs.h + +# Insert all source- and header-files here. +ALL = $(CFILES) $(HFILES) README.serial + +# All object-files are included here. +OBJS = $(CFILES:.c=.o) + +all: lib + +install: + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/serial/commouse.c b/src/base/serial/commouse.c new file mode 100644 index 0000000..7c84943 --- /dev/null +++ b/src/base/serial/commouse.c @@ -0,0 +1,188 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * mouse.com backend + * + * FIXME: only supporting MS protocol (M, M3) for now + * + * Author: Stas Sergeev + */ +#include +#include "emu.h" +#include "init.h" +#include "hlt.h" +#include "int.h" +#include "port.h" +#include "coopth.h" +#include "ser_defs.h" + +#define _com_num config.mouse.com +#define COM_INTR COM_INTERRUPT(_com_num) +static u_short irq_hlt; +static void com_irq(Bit16u idx, HLT_ARG(arg)); + +static int com_mouse_init(void) +{ + emu_hlt_t hlt_hdlr = HLT_INITIALIZER; + int num; + if (config.mouse.com_num == -1 || !config.mouse.intdrv) + return 0; + num = get_com_idx(config.mouse.com_num); + if (num == -1) + return 0; + + _com_num = num; + hlt_hdlr.name = "commouse isr"; + hlt_hdlr.func = com_irq; + irq_hlt = hlt_register_handler_vm86(hlt_hdlr); + + return 1; +} + +static void com_irq(Bit16u idx, HLT_ARG(arg)) +{ + uint8_t iir, val; + s_printf("COMMOUSE: got irq\n"); + iir = read_IIR(_com_num); + switch (iir & UART_IIR_CND_MASK) { + case UART_IIR_NO_INT: + break; + case UART_IIR_RDI: + while (read_LSR(_com_num) & UART_LSR_DR) { + val = read_char(_com_num); + /* + * talk to int33 explicitly as we don't want to talk + * to for example sermouse.c + */ + DOSEMUMouseProtocol(&val, 1, MOUSE_MS3BUTTON, "int33 mouse"); + } + break; + default: + s_printf("COMMOUSE: unexpected interrupt cond %#x\n", iir); + break; + } + + do_eoi_iret(); +} + +static int get_char(int num) +{ + LWORD(edx) = com_cfg[num].real_comport - 1; + HI(ax) = 2; + LO(ax) = 0; + do_int_call_back(0x14); + if (HI(ax) & 0x80) + return -1; + return LO(ax); +} + +static Bit8u get_lsr(int num) +{ + LWORD(edx) = com_cfg[num].real_comport - 1; + HI(ax) = 3; + LO(ax) = 0; + do_int_call_back(0x14); + return HI(ax); +} + +static int com_mouse_reset(void) +{ + #define MAX_RD 20 + int ch, i; + uint8_t imr, imr1; + char buf[3]; + struct vm86_regs saved_regs; + + if (_com_num == -1) + return -1; + write_IER(_com_num, 0); + + saved_regs = REGS; + LWORD(edx) = com_cfg[_com_num].real_comport - 1; + HI(ax) = 0; // init port + LO(ax) = 0x80 | UART_LCR_WLEN8; // 1200, 8N1 + do_int_call_back(0x14); + + write_MCR(_com_num, UART_MCR_DTR); + for (i = 0; i < MAX_RD; i++) { + set_IF(); + coopth_wait(); + clear_IF(); + if (!(get_lsr(_com_num) & UART_LSR_DR)) + break; + get_char(_com_num); // read out everything + } + if (i == MAX_RD) { + s_printf("COMMOUSE: error, reading junk\n"); + goto out_err; + } + write_MCR(_com_num, UART_MCR_DTR | UART_MCR_RTS); + set_IF(); + coopth_wait(); + clear_IF(); + if (get_lsr(_com_num) & UART_LSR_FE) + get_char(_com_num); + for (i = 0; i < 2; i++) { + ch = get_char(_com_num); + if (ch == -1) + break; + buf[i] = ch; + } + buf[i] = '\0'; + REGS = saved_regs; + if (buf[0] != 'M') { + s_printf("COMMOUSE: unsupported ID %s\n", buf); + return -1; + } + + SETIVEC(COM_INTR, BIOS_HLT_BLK_SEG, irq_hlt); + write_MCR(_com_num, com[_com_num].MCR | UART_MCR_OUT2); + imr = imr1 = port_inb(0x21); + imr &= ~(1 << com_cfg[_com_num].irq); + if (imr != imr1) + port_outb(0x21, imr); + write_IER(_com_num, UART_IER_RDI); + return 0; + +out_err: + REGS = saved_regs; + return -1; +} + +static void com_mouse_post_init(void) +{ + if (_com_num == -1) + return; + mouse_enable_native_cursor_id(1, "int33 mouse"); + com[_com_num].ivec.segment = ISEG(COM_INTR); + com[_com_num].ivec.offset = IOFF(COM_INTR); + com_mouse_reset(); +} + +static struct mouse_client com_mouse = { + "com mouse", /* name */ + com_mouse_init, /* init */ + NULL, /* close */ + NULL, + com_mouse_post_init, + com_mouse_reset, +}; + +CONSTRUCTOR(static void com_mouse_register(void)) +{ + register_mouse_client(&com_mouse); +} diff --git a/src/base/serial/comredir.c b/src/base/serial/comredir.c new file mode 100644 index 0000000..19190a2 --- /dev/null +++ b/src/base/serial/comredir.c @@ -0,0 +1,253 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: com port redirect to console + * + * Author: stsp + */ +#include +#include +#include +#include + +#include "emu.h" +#include "hlt.h" +#include "int.h" +#include "coopth.h" +#include "port.h" +#include "keyboard/keyb_server.h" +#include "dos2linux.h" +#include "serial.h" +#include "ser_defs.h" +#include "comredir.h" + +static int com_num; +static int com_num_wr; +static far_t old_ivec; +static u_short irq_hlt; +static int redir_tid; +static void comredir_thr(void *arg); +static far_t old_int15; +static u_short int15_hlt; +static int int15_tid; +static void int15_thr(void *arg); +static void int15_irq(Bit16u idx, HLT_ARG(arg)); +static far_t old_int10; +static void int10_irq(Bit16u idx, HLT_ARG(arg)); +static u_short int10_hlt; +static unsigned tflags; +// suppress output +#define TFLG_SUPPR 1 +// append NL to CR on output +#define TFLG_OANL 2 +// prepend CR to NL on output +#define TFLG_OPCR 4 +// append NL to CR on input +#define TFLG_IANL 8 +// prepend CR to NL on input +#define TFLG_IPCR 0x10 + +void comredir_init(void) +{ + emu_hlt_t hlt_hdlr = HLT_INITIALIZER; + hlt_hdlr.name = "comint15 isr"; + hlt_hdlr.func = int15_irq; + hlt_hdlr.len = 1; + int15_hlt = hlt_register_handler_vm86(hlt_hdlr); + hlt_hdlr.name = "comint10 isr"; + hlt_hdlr.func = int10_irq; + hlt_hdlr.len = 1; + int10_hlt = hlt_register_handler_vm86(hlt_hdlr); + + redir_tid = coopth_create_vm86("comredir thr", comredir_thr, do_eoi_iret, + &irq_hlt); + int15_tid = coopth_create("comint15 thr", int15_thr); +} + +void comredir_setup(int num, int num_wr, unsigned flags) +{ + int i = -1, j = -1; + if (num > 0 && num <= 4) { + struct vm86_regs saved_regs = REGS; + int intr; + unsigned char imr, imr1; + + if (com_num) { + com_printf("comredir is already active\n"); + return; + } + i = get_com_idx(num); + if (i == -1) { + com_printf("comredir: com port %i not configured\n", num); + return; + } + j = get_com_idx(num_wr); + if (j == -1) { + com_printf("comredir: com port %i not configured\n", num_wr); + return; + } + intr = COM_INTERRUPT(i); + old_ivec.segment = ISEG(intr); + old_ivec.offset = IOFF(intr); + SETIVEC(intr, BIOS_HLT_BLK_SEG, irq_hlt); + + _AX = 0b11100011; // 8N1 + _DX = i; + do_int_call_back(0x14); + if (num_wr != num) { + _AX = 0b11100011; // 8N1 + _DX = num_wr - 1; + do_int_call_back(0x14); + } + REGS = saved_regs; + write_MCR(i, com[i].MCR | UART_MCR_OUT2); + write_IER(i, UART_IER_RDI); + + imr = imr1 = port_inb(0x21); + imr &= ~(1 << com_cfg[i].irq); + if (imr != imr1) + port_outb(0x21, imr); + + old_int15.segment = ISEG(0x15); + old_int15.offset = IOFF(0x15); + if (num_wr) + SETIVEC(0x15, BIOS_HLT_BLK_SEG, int15_hlt); + + old_int10.segment = ISEG(0x10); + old_int10.offset = IOFF(0x10); + if (flags & TFLG_SUPPR) + SETIVEC(0x10, BIOS_HLT_BLK_SEG, int10_hlt); + } else { + if (!com_num) + return; + int intr = COM_INTERRUPT(com_num - 1); + write_IER(com_num - 1, 0); + SETIVEC(intr, old_ivec.segment, old_ivec.offset); + SETIVEC(0x15, old_int15.segment, old_int15.offset); + SETIVEC(0x10, old_int10.segment, old_int10.offset); + } + com_num = i + 1; + com_num_wr = j + 1; + tflags = flags; +} + +void comredir_reset(void) +{ + com_num = 0; +} + +static void do_int10(void) +{ + unsigned int ssp, sp; + + ssp = SEGOFF2LINEAR(SREG(ss), 0); + sp = LWORD(esp); + pushw(ssp, sp, LWORD(eflags)); + LWORD(esp) -= 2; + do_call_back(old_int10.segment, old_int10.offset); +} + +static void do_char_out(char out) +{ + _AH = 0x0e; + _AL = out; + _BX = 0; + do_int10(); +} + +static void comredir_thr(void *arg) +{ + int i = com_num - 1; + uint8_t iir; + + s_printf("comredir: got irq\n"); + iir = read_IIR(i); + switch (iir & UART_IIR_CND_MASK) { + case UART_IIR_NO_INT: + break; + case UART_IIR_RDI: { + struct vm86_regs saved_regs = REGS; + while (read_LSR(i) & UART_LSR_DR) { + unsigned char c = read_char(i); + if ((tflags & TFLG_IPCR) && c == '\n') + do_char_out('\r'); + do_char_out(c); + if ((tflags & TFLG_IANL) && c == '\r') + do_char_out('\n'); + if (c == 0x1a) // ^Z, exit + comredir_setup(0, 0, 0); + } + REGS = saved_regs; + break; + } + default: + s_printf("comredir: unexpected interrupt cond %#x\n", iir); + break; + } +} + +static void int15_thr(void *arg) +{ + unsigned int ssp, sp; + + ssp = SEGOFF2LINEAR(SREG(ss), 0); + sp = LWORD(esp); + pushw(ssp, sp, LWORD(eflags)); + LWORD(esp) -= 2; + do_call_back(old_int15.segment, old_int15.offset); + if (!isset_CF()) + return; + clear_CF(); + if (!(_AL & 0x80)) { + unsigned char c = get_bios_key(_AL); + if (!c) { + set_CF(); + return; + } + if ((tflags & TFLG_OPCR) && c == '\n') + write_char(com_num_wr - 1, '\r'); + write_char(com_num_wr - 1, c); + if ((tflags & TFLG_OANL) && c == '\r') + write_char(com_num_wr - 1, '\n'); + if (c == 0x1a) { // ^Z, exit + do_char_out(c); + comredir_setup(0, 0, 0); + } + } else { + set_CF(); + } +} + +static void int15_irq(Bit16u idx, HLT_ARG(arg)) +{ + if (_AH != 0x4f) { + jmp_to(old_int15.segment, old_int15.offset); + return; + } + fake_iret(); + coopth_start(int15_tid, NULL); +} + +static void int10_irq(Bit16u idx, HLT_ARG(arg)) +{ + /* suppress console output */ + if (_AH == 9 || _AH == 0xe || _AH == 0x13 || _AH == 2) { + fake_iret(); + return; + } + jmp_to(old_int10.segment, old_int10.offset); +} diff --git a/src/base/serial/comredir.h b/src/base/serial/comredir.h new file mode 100644 index 0000000..96e7c76 --- /dev/null +++ b/src/base/serial/comredir.h @@ -0,0 +1,8 @@ +#ifndef COMREDIR_H +#define COMREDIR_H + +void comredir_init(void); +void comredir_setup(int num, int num_wr, unsigned flags); +void comredir_reset(void); + +#endif diff --git a/src/base/serial/fossil.c b/src/base/serial/fossil.c new file mode 100644 index 0000000..4916f45 --- /dev/null +++ b/src/base/serial/fossil.c @@ -0,0 +1,608 @@ +/* DANG_BEGIN_MODULE + * + * REMARK + * fossil.c: FOSSIL serial driver emulator for dosemu. + * + * Copyright (C) 1995 by Pasi Eronen. + * Portions Copyright (C) 1995 by Mark Rejhon + * + * The code in this module 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 2 of + * the License, or (at your option) any later version. + * + * /REMARK + * DANG_END_MODULE + */ + +#include +#include +#include +#include + +#include "emu.h" +#include "hlt.h" +#include "int.h" +#include "port.h" +#include "coopth.h" +#include "utilities.h" +#include "serial.h" +#include "ser_defs.h" +#include "comredir.h" +#include "doshelpers.h" + +/* Get the LSR/MSR status bits in FOSSIL format. Since we don't care about + * the delta/interrupt bits anyway, reading the com[] structure is + * faster than calling do_serial_in. + */ +#define FOSSIL_GET_STATUS(num) \ + (((com[(num)].LSR & 0x63) << 8) | (com[(num)].MSR & 0x80) | 0x08) + +/* Some FOSSIL constants. */ +#define FOSSIL_SEG BIOSSEG +#define FOSSIL_MAGIC 0x1954 +#define FOSSIL_REVISION 5 +#define FOSSIL_MAX_FUNCTION 0x1f + +#define FOSSIL_RX_BUFFER_SIZE RX_BUFFER_SIZE +#define FOSSIL_RX_BUF_BYTES(num) RX_BUF_BYTES(num) +#define FOSSIL_TX_BUFFER_SIZE 64 +#define FOSSIL_TX_BUF_BYTES(num) _min(TX_BUF_BYTES(num), FOSSIL_TX_BUFFER_SIZE) + +/* This flag indicates that the DOS part of the emulation, FOSSIL.COM, + * is loaded. This module does nothing as long as this flag is false, + * so other (DOS-based) FOSSIL drivers may be used. + */ +boolean fossil_initialised = FALSE; + +static u_short irq_hlt; +static void fossil_irq(Bit16u idx, HLT_ARG(arg)); + +void fossil_init(void) +{ + emu_hlt_t hlt_hdlr = HLT_INITIALIZER; + hlt_hdlr.name = "fossil isr"; + hlt_hdlr.func = fossil_irq; + hlt_hdlr.len = 2; + irq_hlt = hlt_register_handler_vm86(hlt_hdlr); +} + +static void fossil_setup(void) +{ + int i; + + fossil_initialised = TRUE; + + // In + WRITE_DWORD(SEGOFF2LINEAR(FOSSIL_SEG, FOSSIL_oldisr), MK_FP16(SREG(es), LWORD(ebx))); + + // Initialise signature etc + WRITE_WORD(SEGOFF2LINEAR(FOSSIL_SEG, FOSSIL_magic), FOSSIL_MAGIC); + WRITE_BYTE(SEGOFF2LINEAR(FOSSIL_SEG, FOSSIL_maxfun), FOSSIL_MAX_FUNCTION); + + // Out + SREG(ds) = FOSSIL_SEG; + LWORD(edx) = FOSSIL_isr; + + for (i = 0; i < config.num_ser; i++) + com[i].fossil_active = FALSE; +} + +static void fossil_irq(Bit16u idx, HLT_ARG(arg)) +{ + int i; + uint8_t iir, lsr; + int inum = idx + 3; + + s_printf("FOSSIL: got irq %i\n", inum); + for (i = 0; i < config.num_ser; i++) { + if (com[i].opened <= 0 || com_cfg[i].irq != inum) + continue; + iir = read_IIR(i); + switch (iir & UART_IIR_CND_MASK) { + case UART_IIR_NO_INT: + break; + case UART_IIR_RDI: + write_IER(i, 0); + lsr = read_LSR(i); + if (lsr & UART_LSR_DR) { + if (com[i].fossil_blkrd_tid != COOPTH_TID_INVALID) { + coopth_wake_up(com[i].fossil_blkrd_tid); + com[i].fossil_blkrd_tid = COOPTH_TID_INVALID; + } else { + error("FOSSIL: tid not set!\n"); + } + } + break; + default: + s_printf("FOSSIL: unexpected interrupt cond %#x\n", iir); + break; + } + } + + do_eoi_iret(); +} + +/**************************************************************************/ +/* FOSSIL INTERRUPT 0x14 */ +/**************************************************************************/ + +/* This function handles the FOSSIL calls. It's called by int14.c + * if the com[num].fossil_active flag is true, and always for + * function 0x04 (initialize FOSSIL driver). + */ +void fossil_int14(int num) +{ + uint8_t req = HI(ax); + + switch (HI(ax)) { + /* Initialize serial port. */ + case 0x00: + { + int lcr; + int divisors[] = { DIV_19200, DIV_38400, DIV_300, DIV_600, DIV_1200, + DIV_2400, DIV_4800, DIV_9600 }; + + s_printf("SER%d: FOSSIL 0x00: Initialize port %d, AL=0x%02x\n", + num, LO(dx), LO(ax)); + + /* Read the LCR register and set character size, parity and stopbits */ + lcr = read_LCR(num); + lcr = (lcr & ~UART_LCR_PARA) | (LO(ax) & UART_LCR_PARA); + + /* Raise DTR and RTS */ + write_MCR(num, com[num].MCR | UART_MCR_DTR | UART_MCR_RTS); + + /* Set DLAB bit, set Baudrate Divisor Latch values, and clear DLAB. */ + write_LCR(num, lcr | UART_LCR_DLAB); + write_DLL(num, divisors[LO(ax) >> 5] & 0xff); + write_DLM(num, divisors[LO(ax) >> 5] >> 8); + write_LCR(num, lcr & ~UART_LCR_DLAB); + + LWORD(eax) = FOSSIL_GET_STATUS(num); + s_printf("SER%d: FOSSIL 0x00: Return with AL=0x%02x AH=0x%02x\n", + num, LO(ax), HI(ax)); + break; + } + + /* Read character (should be with wait) */ + case 0x02: + if (com[num].fossil_blkrd_tid != COOPTH_TID_INVALID) { + /* the previous read was probably interrupted by ^C... */ + s_printf("SER%d: FOSSIL 0x02: Read with wait interrupted??\n", num); + coopth_ensure_sleeping(com[num].fossil_blkrd_tid); + /* to avoid resource leakage, we just kill it the unsafe way */ + coopth_cancel(com[num].fossil_blkrd_tid); + coopth_unsafe_detach(com[num].fossil_blkrd_tid, __FILE__); + com[num].fossil_blkrd_tid = COOPTH_TID_INVALID; + write_IER(num, 0); + } + + while (!(com[num].LSR & UART_LSR_DR)) { /* Was a character received? */ + #if SER_DEBUG_FOSSIL_RW + s_printf("SER%d: FOSSIL 0x02: Read char with wait\n", num); + #endif + write_IER(num, UART_IER_RDI); + set_IF(); + com[num].fossil_blkrd_tid = coopth_get_tid(); + coopth_sleep(); + assert(com[num].fossil_blkrd_tid == COOPTH_TID_INVALID); + clear_IF(); + } + LO(ax) = read_char(num); + HI(ax) = 0; + #if SER_DEBUG_FOSSIL_RW + s_printf("SER%d: FOSSIL 0x02: Read char 0x%02x\n", num, LO(ax)); + #endif + break; + + /* Initialize FOSSIL driver. */ + case 0x04: + case 0x1c: { + uint8_t imr, imr1; + fossil_info_t *fi; + int ioff = com_cfg[num].irq - 3; + int inum = COM_INTERRUPT(num); + + assert(sizeof(*fi) == 19); + + /* Do nothing if TSR isn't installed or already initialized. */ + if (!fossil_initialised || com[num].fossil_active) + return; + if (ioff > 1) { + error("COM%i irq misconfigured, %i\n", num, com_cfg[num].irq); + return; + } + com[num].fossil_active = TRUE; + LWORD(eax) = FOSSIL_MAGIC; + HI(bx) = FOSSIL_REVISION; + LO(bx) = FOSSIL_MAX_FUNCTION; + /* Raise DTR */ + write_MCR(num, com[num].MCR | UART_MCR_DTR); + /* Enable FIFOs. I'm not sure if the changing of FIFO size really + * affects anything, but it seems to work :-). + */ + write_FCR(num, UART_FCR_ENABLE_FIFO|UART_FCR_TRIGGER_14); + /* init IRQs, set disabled */ + write_MCR(num, com[num].MCR | UART_MCR_OUT2); + write_IER(num, 0); + /* interrupts are shared, don't set twice */ + if (ISEG(inum) != BIOS_HLT_BLK_SEG || IOFF(inum) != irq_hlt + ioff) { + com[num].ivec.segment = ISEG(inum); + com[num].ivec.offset = IOFF(inum); + SETIVEC(inum, BIOS_HLT_BLK_SEG, irq_hlt + ioff); + imr = imr1 = port_inb(0x21); + imr &= ~(1 << com_cfg[num].irq); + if (imr != imr1) + port_outb(0x21, imr); + } + com[num].fossil_blkrd_tid = COOPTH_TID_INVALID; + + /* Initialize FOSSIL driver info. This is used by the function 0x1b + * (Get driver info). + */ + + fi = &com[num].fossil_info; + fi->size = sizeof(*fi); + fi->frev = FOSSIL_REVISION; + fi->irev = 0; // Driver revision (not used) + fi->id_offset = FOSSIL_idstring; + fi->id_segment = FOSSIL_SEG; + fi->rx_bufsize = FOSSIL_RX_BUFFER_SIZE; + fi->tx_bufsize = FOSSIL_TX_BUFFER_SIZE; + fi->scrn_width = 80; + fi->scrn_height = 25; + fi->bps = 0; // Bps rate (not used) + + s_printf("SER%d: FOSSIL 0x%02x: Emulation activated\n", num, req); + break; + } + + /* FOSSIL shutdown. */ + case 0x05: + case 0x1d: { + uint8_t imr; + if (!com[num].fossil_active) + break; + imr = port_inb(0x21); + imr |= (1 << com_cfg[num].irq); + port_outb(0x21, imr); + SETIVEC(COM_INTERRUPT(num), com[num].ivec.segment, com[num].ivec.offset); + com[num].fossil_active = FALSE; + /* Note: the FIFO values aren't restored. Hopefully nobody notices... */ + s_printf("SER%d: FOSSIL 0x%02x: Emulation deactivated\n", num, req); + break; + } + + /* Lower/raise DTR. */ + case 0x06: + write_MCR(num, (com[num].MCR & ~UART_MCR_DTR) | (LO(ax) ? UART_MCR_DTR : 0)); + s_printf("SER%d: FOSSIL 0x06: DTR set to %d\n", num, LO(ax)); + break; + + /* Purge output buffer */ + case 0x09: + uart_clear_fifo(num, UART_FCR_CLEAR_XMIT); + s_printf("SER%d: FOSSIL 0x09: Purge output buffer\n", num); + break; + + /* Purge input buffer */ + case 0x0a: + uart_clear_fifo(num, UART_FCR_CLEAR_RCVR); + s_printf("SER%d: FOSSIL 0x0a: Purge input buffer\n", num); + break; + + /* Write character (without wait) */ + case 0x0b: + /* hope fifo doesn't overflow */ + if (FIFO_ENABLED(num) || (com[num].LSR & UART_LSR_THRE)) { + write_char(num, LO(ax)); + LWORD(eax) = 1; + #if SER_DEBUG_FOSSIL_RW + s_printf("SER%d: FOSSIL 0x0b: Write char 0x%02x, return AX=%d\n", num, LO(ax), 1); + #endif + } else { + #if SER_DEBUG_FOSSIL_RW + s_printf("SER%d: FOSSIL 0x0b: Write char 0x%02x, return AX=0\n", num, LO(ax)); + #endif + LWORD(eax) = 0; + } + break; + + /* Block read */ + case 0x18: + { + unsigned char *p = SEG_ADR((unsigned char *), es, di); + int n = 0, len = LWORD(ecx); + while (n < len) { +#if 0 + /* do we need wait here? */ + if (!(com[num].LSR & UART_LSR_DR)) { + set_IF(); + coopth_wait(); + clear_IF(); + } +#endif + if (com[num].LSR & UART_LSR_DR) /* Was a character received? */ + p[n++] = read_char(num); /* Read character */ + else + break; + } + LWORD(eax) = n; + #if SER_DEBUG_FOSSIL_RW + s_printf("SER%d: FOSSIL 0x18: Block read, %d/%d bytes\n", num, n, len); + #endif + break; + } + + /* Block write */ + case 0x19: + { + unsigned char *p = SEG_ADR((unsigned char *), es, di); + int n, len = LWORD(ecx); + for (n = 0; n < len; n++) { + if (!FIFO_ENABLED(num) && !(com[num].LSR & UART_LSR_THRE)) + break; + write_char(num, p[n]); + } + LWORD(eax) = n; + #if SER_DEBUG_FOSSIL_RW + s_printf("SER%d: FOSSIL 0x19: Block write, %d/%d bytes\n", num, n, len); + #endif + break; + } + + /* Get FOSSIL driver info. */ + case 0x1b: { + unsigned char *p = SEG_ADR((unsigned char *), es, di); + int bufsize; + fossil_info_t fossil_info, *fi; + + if (!fossil_initialised) { + LWORD(eax) = 0; // Perhaps the only way to indicate no valid data + return; + } + + bufsize = (LWORD(ecx) < sizeof(*fi)) ? LWORD(ecx) : sizeof(*fi); + + if (LO(dx) == 0xff) { + fi = &fossil_info; + + fi->size = sizeof(*fi); + fi->frev = FOSSIL_REVISION; + fi->irev = 0; // Driver revision (not used) + fi->id_offset = FOSSIL_idstring; + fi->id_segment = FOSSIL_SEG; + fi->rx_bufsize = FOSSIL_RX_BUFFER_SIZE; + fi->rx_remaining = 0; // Not valid in non port specific context + fi->tx_bufsize = FOSSIL_TX_BUFFER_SIZE; + fi->tx_remaining = 0; // Not valid in non port specific context + fi->scrn_width = 80; + fi->scrn_height = 25; + fi->bps = 0; // Bps rate (not used) + +#if SER_DEBUG_FOSSIL_STATUS + s_printf("SER: FOSSIL 0x1b: Driver info, ibuf=%d, obuf=%d, " + "idaddr=%04x:%04x, AX=%d\n", + FOSSIL_RX_BUFFER_SIZE, FOSSIL_TX_BUFFER_SIZE, fi->id_segment, + fi->id_offset, bufsize); +#endif + } else { + fi = &com[num].fossil_info; + + /* Fill in some values that aren't constant. */ + fi->rx_remaining = FOSSIL_RX_BUFFER_SIZE - FOSSIL_RX_BUF_BYTES(num); + fi->tx_remaining = FOSSIL_TX_BUFFER_SIZE - FOSSIL_TX_BUF_BYTES(num); + +#if SER_DEBUG_FOSSIL_STATUS + s_printf("SER%d: FOSSIL 0x1b: Driver info, i=%d/%d, o=%d/%d, AX=%d\n", + num, fi->rx_remaining, FOSSIL_RX_BUFFER_SIZE, fi->tx_remaining, + FOSSIL_TX_BUFFER_SIZE, bufsize); +#endif + } + + /* Copy data to user area. */ + memcpy(p, fi, bufsize); + LWORD(eax) = bufsize; + break; + } + + /* Function 1Eh - Extended line control initialization. + * + * This function is intended to exactly emulate the PS/2's BIOS INT + * 14 services, function 4. + */ + + case 0x1e: { + int lcr; + uint8_t mbits; + int divisors[] = {DIV_110, DIV_150, DIV_300, DIV_600, DIV_1200, + DIV_2400, DIV_4800, DIV_9600, DIV_19200}; + uint8_t pbits[] = {0b00000000, 0b00001000, 0b00011000}; + + s_printf("SER%d: FOSSIL 0x1e: Extended initialize port %d\n", num, LO(dx)); + + mbits = 0; + + // break AL + if (LO(ax) == 1) + s_printf("SER%d: FOSSIL 0x1e: Unhandled Ctrl break set request\n", num); + + // parity BH + if (HI(bx) < 3) // None, Odd, Even parity + mbits |= pbits[HI(bx)]; + else + s_printf("SER%d: FOSSIL 0x1e: Unhandled Mark or Space parity request\n", + num); + + // stop BL + if (LO(bx) == 1) + mbits |= (1 << 2); + + // data CH + if (HI(cx) < 4) + mbits |= HI(cx); + + /* Read the LCR register and set character size, parity and stopbits */ + lcr = read_LCR(num); + lcr = (lcr & ~UART_LCR_PARA) | (mbits & UART_LCR_PARA); + + /* Raise DTR and RTS */ + write_MCR(num, com[num].MCR | UART_MCR_DTR | UART_MCR_RTS); + + // speed CL + if (LO(cx) < 9) { + /* Set DLAB bit, in order to set the baudrate */ + write_LCR(num, lcr | 0x80); + + /* Write the Baudrate Divisor Latch values */ + write_DLL(num, divisors[LO(cx)] & 0xFF); /* LSB */ + write_DLM(num, divisors[LO(cx)] >> 8); /* MSB */ + + /* Lower DLAB bit */ + write_LCR(num, lcr & ~0x80); + } else { + s_printf("SER%d: FOSSIL 0x1e: Out of range speed request %d\n", + num, LO(cx)); + } + + LWORD(eax) = FOSSIL_GET_STATUS(num); + s_printf("SER%d: FOSSIL 0x1e: Return with AL=0x%02x AH=0x%02x\n", num, + LO(ax), HI(ax)); + break; + } + + /* Function 1Fh - Extended comm port control + * + * This function is intended to exactly emulate the PS/2's BIOS INT + * 14 services, function 5. + */ + + case 0x1f: { + uint8_t mcr; + + if (LO(ax) == 1) { + mcr = LO(bx) & UART_MCR_VALID; // Mask off the reserved bits + mcr |= (1 << 3); // X00 will not allow communications + // interrupts to be disabled, so do likewise. + write_MCR(num, mcr); + s_printf("SER%d: FOSSIL 0x1f: Write MCR (0x%02x)\n", num, mcr); + } else { + LO(bx) = read_MCR(num); + s_printf("SER%d: FOSSIL 0x1f: Read MCR (0x%02x)\n", num, LO(bx)); + } + + LWORD(eax) = FOSSIL_GET_STATUS(num); + s_printf("SER%d: FOSSIL 0x1f: Return with AL=0x%02x AH=0x%02x\n", num, + LO(ax), HI(ax)); + break; + } + + /* Unimplemented functions. Some of these could be implemented quite easily, + * but most programs (at least the ones I use) don't use use them. + */ + + /* Get timer tick information. */ + case 0x07: + /* Flush output buffer. */ + case 0x08: + /* Non-destructive read (peek). */ + case 0x0c: + /* Keyboard read (without wait). */ + case 0x0d: + /* Keyboard read (with wait). */ + case 0x0e: + /* Set flow control. */ + case 0x0f: + /* Enable/disable Ctrl-C/Ctrl-K checking and transmitter. */ + case 0x10: + /* Set cursor location. */ + case 0x11: + /* Get cursor location. */ + case 0x12: + /* Write character to screen (with ANSI support). */ + case 0x13: + /* Enable/disable DCD watchdog. */ + case 0x14: + /* Write character to screen (using BIOS). */ + case 0x15: + /* Add/delete function from timer tick chain. */ + case 0x16: + /* Reboot system. */ + case 0x17: + /* Break begin/end. */ + case 0x1a: + /* Install external application function. */ + case 0x7e: + /* Remove external application function. */ + case 0x7f: + s_printf("SER%d: FOSSIL 0x%02x: Function not implemented!\n", num, HI(ax)); + break; + + /* This runs if nothing handles the FOSSIL function. */ + default: + s_printf("SER%d: FOSSIL 0x%02x: Unknown function!\n", num, HI(ax)); + } +} + + +/**************************************************************************/ +/* SERIAL HELPER FUNCTION */ +/**************************************************************************/ + +/* The DOS part of FOSSIL emulator, FOSSIL.COM, uses this call to activate + * the dosemu part of the emulation. + */ +void serial_helper(void) +{ + uint16_t old_seg, old_off; + + switch (HI(ax)) { + /* installation check. */ + case DOS_SUBHELPER_SERIAL_FOSSIL_CHECK: + LWORD(eax) = fossil_initialised; + s_printf("SER: FOSSIL: installation check, AX=%d\n", fossil_initialised); + break; + + /* TSR install. */ + case DOS_SUBHELPER_SERIAL_TSR_INSTALL: + s_printf("SER: FOSSIL: TSR install (no longer valid)\n"); + LWORD(ebx) = DOS_ERROR_SERIAL_TSR_INVALID; + CARRY; + break; + + case DOS_SUBHELPER_SERIAL_FOSSIL_INIT: + if (fossil_initialised) { + LWORD(ebx) = DOS_ERROR_SERIAL_ALREADY_INSTALLED; + CARRY; + break; + } + + if (config.num_ser == 0) { + LWORD(ebx) = DOS_ERROR_SERIAL_CONFIG_DISABLED; + CARRY; + break; + } + + if (LO(cx) != DOS_VERSION_SERIAL_FOSSIL) { + LWORD(ebx) = DOS_ERROR_SERIAL_FOSSIL_VERSION; + CARRY; + break; + } + + old_seg = SREG(es); + old_off = LWORD(ebx); + fossil_setup(); + NOCARRY; + s_printf("SER: FOSSIL: installation, ES:BX=%04x:%04x => DS:DX=%04x:%04x\n", + old_seg, old_off, SREG(ds), LWORD(edx)); + break; + + case DOS_SUBHELPER_SERIAL_COMREDIR_INIT: + comredir_setup(_BL, _CL, _CH); + break; + + default: + s_printf("SER: FOSSIL helper 0x%02x: Unknown function!\n", HI(ax)); + } +} diff --git a/src/base/serial/int14.c b/src/base/serial/int14.c new file mode 100644 index 0000000..22cd58b --- /dev/null +++ b/src/base/serial/int14.c @@ -0,0 +1,239 @@ +/* DANG_BEGIN_MODULE + * + * REMARK + * int14.c: Serial BIOS services for DOSEMU. + * Please read the README.serial file in this directory for more info! + * + * Copyright (C) 1995 by Mark Rejhon + * + * The code in this module 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 2 of + * the License, or (at your option) any later version. + * + * This module is maintained by Mark Rejhon at these Email addresses: + * marky@magmacom.com + * ag115@freenet.carleton.ca + * + * /REMARK + * DANG_END_MODULE + */ + +/* DANG_BEGIN_NEWIDEA + * If any of you coders are ambitious, try thinking of the following: + * - Converting this into inline assembler and use direct port access + * DANG_END_NEWIDEA + */ + +/* Standard includes, just to be on the safe side. */ +#include +#include + +/* Other includes that may be needed for this serial code */ +#include "emu.h" +#include "serial.h" +#include "ser_defs.h" +#include "timers.h" +#include "coopth.h" + +/* The following function sets the speed of the serial port. + * num is the index, and speed is a baudrate divisor value. + */ +static void set_speed(int num, int speed) +{ + write_DLL(num, speed & 0xFF); /* Write baudrate divisor LSB */ + write_DLM(num, (speed >> 8) & 0xFF); /* Write baudrate divisor MSB */ +} + + +/**************************************************************************/ +/* BIOS INTERRUPT 0x14 */ +/**************************************************************************/ + +/* DANG_BEGIN_FUNCTION int14 + * The following function executes a BIOS interrupt 0x14 function. + * This code by Mark Rejhon replaced some very buggy, old int14 interface + * a while back. These routines are not flawless since it does not wait + * for a character during receive, and this may confuse some programs. + * DANG_END_FUNCTION + */ +int int14(void) +{ + int num; + int temp; + + /* Translate the requested COM port number in the DL register, into + * the necessary arbitrary port number system used throughout this module. + */ + num = get_com_idx(LO(dx)+1); + if (num == -1) + return 1; /* Exit if not on supported port */ + + switch (HI(ax)) { + case 0: /* Initialize serial port. */ + if (com[num].fossil_active) { + fossil_int14(num); + break; + } + + if (com_cfg[num].mouse && ((LO(ax) & 0xE0) != 0x80)) { + s_printf("SER%d: INT14 0x0: Deny init of the mouse port\n", num); + LWORD(eax) = 0xff00; + break; + } + + s_printf("SER%d: INT14 0x0: Initialize port %d, AL=0x%x\n", + num, LO(dx), LO(ax)); + + /* Read the LCR register */ + temp = read_LCR(num); + + /* The following sets character size, parity, and stopbits */ + temp = (temp & ~UART_LCR_PARA) | (LO(ax) & UART_LCR_PARA); + + /* Set DLAB bit, in order to set the baudrate */ + write_LCR(num, temp | 0x80); + + /* Write the Baudrate Divisor Latch values */ + switch (LO(ax) & 0xE0) { + case 0x00: set_speed(num, DIV_110); break; + case 0x20: set_speed(num, DIV_150); break; + case 0x40: set_speed(num, DIV_300); break; + case 0x60: set_speed(num, DIV_600); break; + case 0x80: set_speed(num, DIV_1200); break; + case 0xa0: set_speed(num, DIV_2400); break; + case 0xc0: set_speed(num, DIV_4800); break; + case 0xe0: set_speed(num, DIV_9600); break; + } + + /* Lower DLAB bit */ + write_LCR(num, temp & ~0x80); + + /* Note that SeaBIOS does not touch MCR... why do we? */ + temp = read_MCR(num) & UART_MCR_VALID; + temp &= ~UART_MCR_LOOP; + /* Raise DTR and RTS on the Modem Control Register */ + temp |= UART_MCR_DTR | UART_MCR_RTS; + write_MCR(num, temp); + + HI(ax) = read_LSR(num); /* Read Line Status (LSR) into AH */ + LO(ax) = read_MSR(num); /* Read Modem Status (MSR) into AL */ + + s_printf("SER%d: INT14 0x0: Return with AL=0x%x AH=0x%x\n", + num, LO(ax), HI(ax)); + break; + + /* Write character with wait */ + case 1: { + const int timeout = 10; + const int scale = 0x10000; + int i = 1; + hitimer_t end_time = GETtickTIME(0) + timeout * scale; + s_printf("SER%d: INT14 0x1: Write char 0x%x\n",num,LO(ax)); +#if 1 + while (GETtickTIME(0) < end_time) { + if (read_LSR(num) & UART_LSR_THRE) + break; + s_printf("SER%d: INT14 0x1: Wait for xmit, %i\n", num, i); + i++; + set_IF(); + coopth_wait(); + clear_IF(); + } +#endif + if (FIFO_ENABLED(num) || (read_LSR(num) & UART_LSR_THRE)) { + write_char(num, LO(ax)); /* Transmit character */ + HI(ax) = read_LSR(num) & ~0x80; /* Character was sent */ + } else { + s_printf("SER%d: INT14 TX overrun\n",num); + HI(ax) = read_LSR(num) | 0x80; /* return error */ + } + break; + } + + /* Read character function */ + case 2: { + const int timeout = 10; + const int scale = 0x10000; + int i = 1; + hitimer_t end_time = GETtickTIME(0) + timeout * scale; + if (com[num].fossil_active) { + fossil_int14(num); + break; + } + + s_printf("SER%d: INT14 0x2: Read char\n", num); +#if 1 + while (GETtickTIME(0) < end_time) { + if (read_LSR(num) & UART_LSR_DR) + break; + s_printf("SER%d: INT14 0x2: Wait for recv, %i\n", num, i); + i++; + set_IF(); + coopth_wait(); + clear_IF(); + } +#endif + if ((read_LSR(num) & UART_LSR_DR)) { + int dsr = (read_MSR(num) & UART_MSR_DSR); + LO(ax) = read_char(num); /* Read character */ + HI(ax) = read_LSR(num); /* Character was received */ + if (dsr) { + HI(ax) &= ~0x80; + s_printf("SER%d: INT14 0x2: Read char 0x%x\n",num,LO(ax)); + } else { + HI(ax) |= 0x80; + s_printf("SER%d: INT14 0x2: Read but no DSR.\n",num); + } + } + else { + HI(ax) = read_LSR(num) | 0x80; /* Timeout */ + s_printf("SER%d: INT14 0x2: Read char timeout.\n",num); + } + break; + } + + /* Port Status request function. */ + case 3: + HI(ax) = read_LSR(num); /* Read Line Status (LSR) into AH */ + LO(ax) = read_MSR(num); /* Read Modem Status (MSR) into AL */ + s_printf("SER%d: INT14 0x3: Port Status, AH=0x%x AL=0x%x\n", + num,HI(ax),LO(ax)); + break; + + /* FOSSIL initialize. */ + case 4: + fossil_int14(num); + break; + + /* This runs if nothing handles the int14 function */ + default: + if (com[num].fossil_active) { + fossil_int14(num); + break; + } + + s_printf("SER%d: INT14 0x%x: Unsupported interrupt on port %d\n", + num,HI(ax),LO(dx)); + break; + } + return 1; +} + +void serial_mem_setup(void) +{ + int num; + /* Write serial port information into BIOS data area 0040:0000 + * This is for DOS and many programs to recognize ports automatically + */ + for (num = 0; num < config.num_ser; num++) { + if ((com_cfg[num].real_comport >= 1) && (com_cfg[num].real_comport <= 4)) { + WRITE_WORD(BIOS_BASE_ADDRESS_COM1 + (com_cfg[num].real_comport-1)*2, com_cfg[num].base_port); + + /* Debugging to determine whether memory location was written properly */ + s_printf("SER%d: BIOS memory location %p has value of %#x\n", num, + ((u_short *) (BIOS_BASE_ADDRESS_COM1) + (com_cfg[num].real_comport-1)) + ,READ_WORD(BIOS_BASE_ADDRESS_COM1 + 2*(com_cfg[num].real_comport-1))); + } + } +} diff --git a/src/base/serial/nullmm.c b/src/base/serial/nullmm.c new file mode 100644 index 0000000..e0c8ffb --- /dev/null +++ b/src/base/serial/nullmm.c @@ -0,0 +1,137 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Null-modem connection + * + * Author: stsp + */ +#include +#include "emu.h" +#include "init.h" +#include "utilities.h" +#include "ser_defs.h" +#include "nullmm.h" + +static int add_buf(com_t *c, const char *buf, int len) +{ + if (RX_BUF_BYTES(c->num) + len > RX_BUFFER_SIZE) { + if(s3_printf) s_printf("SER%d: Too many bytes (%i) in buffer\n", c->num, + RX_BUF_BYTES(c->num)); + return 0; + } + + /* Slide the buffer contents to the bottom */ + rx_buffer_slide(c->num); + + memcpy(&c->rx_buf[c->rx_buf_end], buf, len); + if (debug_level('s') >= 9) { + int i; + for (i = 0; i < len; i++) + s_printf("SER%d: Got mouse data byte: %#x\n", c->num, + c->rx_buf[c->rx_buf_end + i]); + } + c->rx_buf_end += len; + receive_engine(c->num, len); + return len; +} + +static void nullmm_rx_buffer_dump(com_t *c) +{ +} + +static void nullmm_tx_buffer_dump(com_t *c) +{ +} + +static int nullmm_get_tx_queued(com_t *c) +{ + return 0; +} + +static void nullmm_termios(com_t *c) +{ +} + +static int nullmm_brkctl(com_t *c, int flag) +{ + return 0; +} + +static ssize_t nullmm_write(com_t *c, char *buf, size_t len) +{ + int idx = get_com_idx(c->cfg->nullmm); + if (idx == -1) + return -1; + return add_buf(&com[idx], buf, len); +} + +static int nullmm_dtr(com_t *c, int flag) +{ + return 0; +} + +static int nullmm_rts(com_t *c, int flag) +{ + return 0; +} + +static int nullmm_open(com_t *c) +{ + return 1; +} + +static int nullmm_close(com_t *c) +{ + return 0; +} + +static int nullmm_uart_fill(com_t *c) +{ + return 0; +} + +static int nullmm_get_msr(com_t *c) +{ + com_t *c2; + int idx = get_com_idx(c->cfg->nullmm); + if (idx == -1) + return -1; + c2 = &com[idx]; + unsigned char msr = UART_MSR_DCD; + if (c2->MCR & UART_MCR_DTR) + msr |= UART_MSR_DSR; + if (c2->MCR & UART_MCR_RTS) + msr |= UART_MSR_CTS; + return msr; +} + + +struct serial_drv nullmm_drv = { + nullmm_rx_buffer_dump, + nullmm_tx_buffer_dump, + nullmm_get_tx_queued, + nullmm_termios, + nullmm_brkctl, + nullmm_write, + nullmm_dtr, + nullmm_rts, + nullmm_open, + nullmm_close, + nullmm_uart_fill, + nullmm_get_msr, + "serial_mouse_tty" +}; diff --git a/src/base/serial/nullmm.h b/src/base/serial/nullmm.h new file mode 100644 index 0000000..1d25139 --- /dev/null +++ b/src/base/serial/nullmm.h @@ -0,0 +1,6 @@ +#ifndef NULLMM_H +#define NULLMM_H + +extern struct serial_drv nullmm_drv; + +#endif diff --git a/src/base/serial/ser_defs.h b/src/base/serial/ser_defs.h new file mode 100644 index 0000000..dcefd4d --- /dev/null +++ b/src/base/serial/ser_defs.h @@ -0,0 +1,469 @@ +/* DANG_BEGIN_MODULE + * + * REMARK + * ser_defs.h: Include file for all files in the 'serial' subdirectory. + * Please send bug reports and bugfixes to marky@magmacom.com + * Please read the files in this 'serial' subdirectory for more info. + * + * /REMARK + * This module is maintained by Stas Sergeev + * + * COPYRIGHTS + * ~~~~~~~~~~ + * UART defs derived from Theodore Ts'o work: /linux/include/linux/serial.h + * Other code Copyright (C) 1995 by Mark Rejhon + * + * The code in this module 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 2 of + * the License, or (at your option) any later version. + * + * DANG_END_MODULE + */ +#ifndef SER_DEFS_H +#define SER_DEFS_H + +#include "serial.h" + +/* DANG_BEGIN_REMARK + * Extensions to serial debugging. + * + * SER_DEBUG_MAIN (0 or 1) + * - extra debug output on the most critical information. + * + * SER_DEBUG_HEAVY (0 or 1) + * - super-heavy extra debug output, including all ports reads and writes, + * and every character received and transmitted! + * + * SER_DEBUG_INTERRUPT (0 or 1) + * - additional debug output related to serial interrupt code, + * including flagging serial interrupts, or PIC-driven code. + * + * SER_DEBUG_FOSSIL_RW (0 or 1) + * - heavy FOSSIL debug output, including all reads and writes. + * + * SER_DEBUG_FOSSIL_STATUS (0 or 1) + * - super-heavy FOSSIL debug output, including all status checks. + * + * You must recompile dosemu everytime one of these constants are modified. + * Just type 'make' in the dosemu dir and it will recompile the changes only. + * DANG_END_REMARK + */ +#define SER_DEBUG_MAIN 1 /* 0 or 1 */ +#define SER_DEBUG_HEAVY 1 /* 0 or 1 */ +#define SER_DEBUG_INTERRUPT 1 /* 0 or 1 */ +#define SER_DEBUG_FOSSIL_RW 1 /* 0 or 1 */ +#define SER_DEBUG_FOSSIL_STATUS 0 /* 0 or 1 */ + +/* + * DANG_BEGIN_REMARK + * + * IMPORTANT INFO about com[] variable array structure used in serial.c + * + * Most of the serial variables are stored in the com[] array. + * The com[] array is a structure in itself. Take a look at the + * 'serial_t' struct declaration in the serial.h module for more info + * about this. Only the most commonly referenced global variables + * are listed here: + * + * config.num_ser Number of serial ports active. + * com[x].base_port The base port address of emulated serial port. + * com[x].real_comport The COM port number. + * com[x].mouse Flag mouse (to enable extended features) + * com[x].fd File descriptor for port device + * com[x].dev[] Filename of port port device + * com[x].dev_locked Flag whether device has been locked + * + * The arbritary example variable 'x' in com[x] can have a minimum value + * of 0 and a maximum value of (config.numser - 1). There can be no gaps + * for the value 'x', even though gaps between actual COM ports are permitted. + * It is strongly noted that the 'x' does not equal the COM port number. + * This example code illustrates the fact, and how the com[] array works: + * + * for (i = 0; i < config.numser; i++) + * s_printf("COM port number %d has a base address of %x", + * com[i].real_comport, com[i].base_port); + * + * DANG_END_REMARK + */ + +/* Defines to control existence of the debug outputting commands. + * The C compiler is smart enough to not generate extra code when certain + * debugging output commands are not defined to anything in particular. + */ +#if SER_DEBUG_MAIN +#define s1_printf 1 +#else +#define s1_printf 0 +#endif + +#if SER_DEBUG_HEAVY +#define s2_printf 1 +#else +#define s2_printf 0 +#endif + +#if SER_DEBUG_INTERRUPT +#define s3_printf 1 +#else +#define s3_printf 0 +#endif + +/************************************************************ + * The following UART definitions are derived from * + * linux/include/linux/serial.h, a work by Theodore Ts'o. * + * These definitions should NOT be changed! * + ************************************************************/ + +/* These are the UART port assignments, expressed as offsets from the base + * register. These assignments should hold for any serial port based on + * a 8250, 16450, or 16550(A). + */ +#define UART_RX 0 /* In: Receive buffer (DLAB=0) */ +#define UART_TX 0 /* Out: Transmit buffer (DLAB=0) */ +#define UART_DLL 0 /* Out: Devisor Latch Low (DLAB=1) */ +#define UART_DLM 1 /* Out: Devisor Latch High (DLAB=1) */ +#define UART_IER 1 /* Out: Interrupt Enable Register */ +#define UART_IIR 2 /* In: Interrupt ID Register */ +#define UART_FCR 2 /* Out: FIFO Control Register */ +#define UART_LCR 3 /* Out: Line Control Register */ +#define UART_MCR 4 /* Out: Modem Control Register */ +#define UART_LSR 5 /* In: Line Status Register */ +#define UART_MSR 6 /* In: Modem Status Register */ +#define UART_SCR 7 /* I/O: Scratch Register */ + +/* These are the definitions for the FIFO Control Register + */ +#define UART_FCR_ENABLE_FIFO 0x01 /* Enable the FIFO */ +#define UART_FCR_CLEAR_RCVR 0x02 /* Clear the RCVR FIFO */ +#define UART_FCR_CLEAR_XMIT 0x04 /* Clear the XMIT FIFO */ +#define UART_FCR_DMA_SELECT 0x08 /* For DMA applications */ +#define UART_FCR_TRIGGER_MASK 0xC0 /* Mask for the FIFO trigger range */ +#define UART_FCR_TRIGGER_1 0x00 /* Mask for trigger set at 1 */ +#define UART_FCR_TRIGGER_4 0x40 /* Mask for trigger set at 4 */ +#define UART_FCR_TRIGGER_8 0x80 /* Mask for trigger set at 8 */ +#define UART_FCR_TRIGGER_14 0xC0 /* Mask for trigger set at 14 */ + +#define UART_FCR_CLEAR_CMD (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT) +#define UART_FCR_SETUP_CMD (UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8) + +/* These are the definitions for the Line Control Register + * + * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting + * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits. + * UART_LCR_SPAR is rarely used, but if this is set, it means to + * stick the parity bit high or low, depending on UART_LCR_PARITY. + */ +#define UART_LCR_DLAB 0x80 /* Devisor latch access bit */ +#define UART_LCR_SBC 0x40 /* Set break control */ +#define UART_LCR_SPAR 0x20 /* Stick parity bit continously high or low */ +#define UART_LCR_EPAR 0x10 /* Even paraity select */ +#define UART_LCR_PARITY 0x08 /* Parity Enable */ +#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */ +#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ +#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ +#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ +#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ +#define UART_LCR_PARA 0x1f /* Parity, Stop bits and Wordlength */ + +/* These are the definitions for the Line Status Register + */ +#define UART_LSR_TEMT 0x40 /* Transmitter empty */ +#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ +#define UART_LSR_BI 0x10 /* Break interrupt indicator */ +#define UART_LSR_FE 0x08 /* Frame error indicator */ +#define UART_LSR_PE 0x04 /* Parity error indicator */ +#define UART_LSR_OE 0x02 /* Overrun error indicator */ +#define UART_LSR_DR 0x01 /* Receiver data ready */ +#define UART_LSR_ERR 0x1e /* All the error indicators */ + +/* These are the definitions for the Interrupt Indentification Register + */ +#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ +#define UART_IIR_MSI 0x00 /* Modem status interrupt */ +#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ +#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ +#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ +#define UART_IIR_CND_MASK 7 +#if 0 +#define UART_IIR_CTI 0x0c /* Character timeout indication */ +#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ +#define UART_IIR_FIFO_ENABLE_1 0x40 +#define UART_IIR_FIFO_ENABLE_2 0x80 +#define UART_IIR_FIFO (UART_IIR_FIFO_ENABLE_1|UART_IIR_FIFO_ENABLE_2) +#else +#define IIR_FIFO_ENABLE 3 +#endif + +/* These are the definitions for the Interrupt Enable Register + */ +#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ +#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ +#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ +#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ +#define UART_IER_VALID 0x0f + +/* These are the definitions for the Modem Control Register + */ +#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ +#define UART_MCR_OUT2 0x08 /* Out2 complement */ +#define UART_MCR_OUT1 0x04 /* Out1 complement */ +#define UART_MCR_RTS 0x02 /* RTS complement */ +#define UART_MCR_DTR 0x01 /* DTR complement */ +#define UART_MCR_VALID 0x1F /* The valid registers of the MCR */ + +/* These are the definitions for the Modem Status Register + */ +#define UART_MSR_DCD 0x80 /* Data Carrier Detect */ +#define UART_MSR_RI 0x40 /* Ring Indicator */ +#define UART_MSR_DSR 0x20 /* Data Set Ready */ +#define UART_MSR_CTS 0x10 /* Clear to Send */ +#define UART_MSR_DDCD 0x08 /* Delta DCD */ +#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */ +#define UART_MSR_DDSR 0x02 /* Delta DSR */ +#define UART_MSR_DCTS 0x01 /* Delta CTS */ +#define UART_MSR_DELTA 0x0F /* Any of the delta bits! */ +#define UART_MSR_STAT 0xF0 /* Any of the non-delta bits! */ + +/* These are baudrate divisors. BaudRate = 1843200 / (DIVISOR * 16) + */ +#define DIV_50 0x900 +#define DIV_110 0x417 +#define DIV_150 0x300 +#define DIV_300 0x180 +#define DIV_600 0x0C0 +#define DIV_1200 0x060 +#define DIV_1800 0x040 +#define DIV_2000 0x03A +#define DIV_2400 0x030 +#define DIV_3600 0x020 +#define DIV_4800 0x018 +#define DIV_7200 0x010 +#define DIV_9600 0x00C +#define DIV_19200 0x006 +#define DIV_38400 0x003 +#define DIV_57600 0x002 +#define DIV_115200 0x001 + +/* Interrupts pending flag bits for com[num].int_type + * These bits must match the UART_IER_xxx bits, above + */ +#define RX_INTR UART_IER_RDI /* 1 */ +#define TX_INTR UART_IER_THRI /* 2 */ +#define LS_INTR UART_IER_RLSI /* 4 */ +#define MS_INTR UART_IER_MSI /* 8 */ + + +/********************************************************** + * The following are definitions that can be fine-tuned * + **********************************************************/ + +/* The following are positive constants that adjust the soonness of the next + * receive or transmit interrupt in FIFO mode. These are a little + * bit sensitive, and may dissappear when better timer code arrives. + */ +#define TIMEOUT_RX 3 + +/* Maximum number of 115200ths of a seconds worth of chars to buffer + * If more characters than this is in the buffer, then wait till it + * drops below this value, to trigger the next transmit interrupt + * Right now this is set to 1/10th sec worth of buffered xmit bytes. + */ +#define TX_BUF_THRESHOLD 11520L + +/* Frequency of read() on the serial device. This is in format of + * 115200ths between every read(). Right now this is set to 1/60th + * of a second between reads, (1920/115200) if no data was waiting. + * This is to reduce system load caused by read() on the serial device. + */ +#define RX_READ_FREQ 1920L + +/* These are sizes for the internal recieve and transmit buffers. + * They must be at least 16 bytes because these double as FIFO's, + * The 16-byte limitation is emulated, though, for compatibility + * purposes. (Although this may be configurable eventually) + * + * DANG_FIXTHIS Why does a RX_BUFFER_SIZE of 256 cause slower performance than a size of 128? + */ +#define RX_BUFFER_SIZE 128 + +/* how many bytes left in output queue when signalling interrupt to DOS */ +#define TX_QUEUE_THRESHOLD 14 +#define TX_BUF_BYTES(num) (com[num].tx_cnt > TX_QUEUE_THRESHOLD ? \ + com[num].tx_cnt - TX_QUEUE_THRESHOLD : 0) + + +/* Minimum frequency for modem status checks, in 115200ths seconds + * between checks of the modem status. Right now this is set to + * 1/30th of a second (3840/115200) + */ +#define MS_MIN_FREQ 3840L + +struct serial_drv; + +struct iir { + u_char mask; + union { + struct { + u_char cti:1; + u_char rsv:1; + u_char fifo_64b:1; + u_char fifo_enable:2; + } flg; + u_char flags:5; + }; +}; + +typedef struct { + uint16_t size; // size of structure in bytes + uint8_t frev; // FOSSIL spec driver conforms to + uint8_t irev; // revision level of this specific driver + uint16_t id_offset; // pointer to ASCIZ identification string + uint16_t id_segment; + uint16_t rx_bufsize; + uint16_t rx_remaining; // number of bytes left in buffer + uint16_t tx_bufsize; + uint16_t tx_remaining; // number of bytes left in buffer + uint8_t scrn_width; + uint8_t scrn_height; + uint8_t bps; +} __attribute__((packed)) fossil_info_t; + +typedef struct { + /* MAIN VARIABLES */ + serial_t *cfg; + int num; + int fd; /* File descriptor of device */ + int opened; + int wr_fd; + boolean is_file; + boolean is_closed; + boolean dev_locked; /* Flag to indicate that device is locked */ + boolean fossil_active; /* Flag: FOSSIL emulation active */ + fossil_info_t fossil_info; /* FOSSIL driver info structure */ + pid_t pty_pid; + struct vec_t ivec; + /* MODEM STATUS */ +// long int ms_freq; /* Frequency of Modem Status (MS) check */ + long int ms_timer; /* Countdown to forced MS check */ + /* RECEIVE */ + long int rx_timer; /* Countdown to next read() system call */ + u_char rx_timeout; /* Recieve Interrupt timeout counter */ + u_char rx_fifo_trigger; /* Receive Fifo trigger value */ + int rx_fifo_size; /* Size of receive FIFO to emulate */ + /* MISCELLANEOUS */ + u_char int_condition; /* Interrupt Condition flags - TX/RX/MS/LS */ + + /* The following are serial port registers */ + int dll, dlm; /* Baudrate divisor LSB and MSB */ + u_char IER; /* Interrupt Enable Register */ + struct iir IIR; /* Interrupt Identification Register */ + u_char LCR; /* Line Control Register */ + u_char FCReg; /* Fifo Control Register (.FCR is a name conflict) */ + u_char MCR; /* Modem Control Register */ + u_char LSR; /* Line Status Register */ + u_char MSR; /* Modem Status Register */ + u_char SCR; /* Scratch Pad Register */ + + /* The following are the transmit and receive buffer variables + * They are bigger than the 16 bytes of a real FIFO to improve + * performance, but the 16-byte limitation of the receive FIFO + * is still emulated using a counter, to improve compatibility. + */ + u_char rx_buf[RX_BUFFER_SIZE]; /* Receive Buffer */ + u_char rx_buf_start; /* Receive Buffer queue start */ + u_char rx_buf_end; /* Receive Buffer queue end */ + + int tx_cnt; + int fossil_blkrd_tid; + + struct termios oldset; /* Original termios settings */ + struct termios newset; /* Current termios settings */ + + struct serial_drv *drv; +} com_t; + +extern com_t com[MAX_SER]; + +extern boolean fossil_initialised; + +#define RX_BUF_BYTES(num) (com[num].rx_buf_end - com[num].rx_buf_start) +//#define RX_FIFO_BYTES(num) min(RX_BUF_BYTES(num), com[num].rx_fifo_size) +#define INT_REQUEST(num) (com[num].int_condition & com[num].IER) +#define INT_ENAB(num) (com[num].MCR & UART_MCR_OUT2) +#define TX_TRIGGER(num) (!(com[num].LSR & UART_LSR_THRE)) +#define FIFO_ENABLED(num) (com[num].IIR.flg.fifo_enable == IIR_FIFO_ENABLE) +#define DLAB(num) (com[num].LCR & UART_LCR_DLAB) + +/******************************************************************* + * Function declarations in order to resolve function references * + *******************************************************************/ + +int convert_bit(int, int, int); +void serial_int_engine(int, int); +void serial_timer_update(void); +void uart_clear_fifo(int, int); +void fossil_int14(int); +void ser_termios(int num); +void modstat_engine(int num); +int msr_compute_delta_bits(int oldmsr, int newmsr); +int ser_open(int num); +void receive_engine(int num, int size); +void transmit_engine(int num); +void rx_buffer_slide(int num); +void tx_buffer_slide(int num); +int serial_get_tx_queued(int num); +void serial_update(int num); + +void rx_buffer_dump(int num); +void tx_buffer_dump(int num); +int serial_get_tx_queued(int num); +void ser_termios(int num); +int serial_brkctl(int num, int brkflg); +ssize_t serial_write(int num, char *buf, size_t len); +int serial_dtr(int num, int flag); +int serial_rts(int num, int flag); +int ser_open(int num); +int ser_close(int num); +int uart_fill(int num); +int serial_get_msr(int num); + +void fossil_init(void); + +struct serial_drv { + void (*rx_buffer_dump)(com_t *c); + void (*tx_buffer_dump)(com_t *c); + int (*serial_get_tx_queued)(com_t *c); + void (*ser_termios)(com_t *c); + int (*serial_brkctl)(com_t *c, int brkflg); + ssize_t (*serial_write)(com_t *c, char *buf, size_t len); + int (*serial_dtr)(com_t *c, int flag); + int (*serial_rts)(com_t *c, int flag); + int (*ser_open)(com_t *c); + int (*ser_close)(com_t *c); + int (*uart_fill)(com_t *c); + int (*serial_get_msr)(com_t *c); + const char *name; +}; + +#define COM_INTERRUPT(num) (com_cfg[num].irq + 8) + +#define read_reg(num, offset) do_serial_in((num), com_cfg[(num)].base_port + (offset)) +#define read_char(num) read_reg((num), UART_RX) +#define read_LCR(num) read_reg((num), UART_LCR) +#define read_MCR(num) read_reg((num), UART_MCR) +#define read_LSR(num) read_reg((num), UART_LSR) +#define read_MSR(num) read_reg((num), UART_MSR) +#define read_IIR(num) read_reg((num), UART_IIR) +#define write_reg(num, offset, byte) do_serial_out((num), com_cfg[(num)].base_port + (offset), (byte)) +#define write_char(num, byte) write_reg((num), UART_TX, (byte)) +#define write_DLL(num, byte) write_reg((num), UART_DLL, (byte)) +#define write_DLM(num, byte) write_reg((num), UART_DLM, (byte)) +#define write_FCR(num, byte) write_reg((num), UART_FCR, (byte)) +#define write_LCR(num, byte) write_reg((num), UART_LCR, (byte)) +#define write_MCR(num, byte) write_reg((num), UART_MCR, (byte)) +#define write_IER(num, byte) write_reg((num), UART_IER, (byte)) + +int get_com_idx(int num); + +#endif /* SER_DEFS_H */ diff --git a/src/base/serial/ser_init.c b/src/base/serial/ser_init.c new file mode 100644 index 0000000..6c40d75 --- /dev/null +++ b/src/base/serial/ser_init.c @@ -0,0 +1,457 @@ +/* DANG_BEGIN_MODULE + * + * REMARK + * ser_init.c: Serial ports initialization for DOSEMU + * Please read the README.serial file in this directory for more info! + * + * Lock file stuff was derived from Taylor UUCP with these copyrights: + * Copyright (C) 1991, 1992 Ian Lance Taylor + * Uri Blumenthal (C) 1994 + * Paul Cadach, (C) 1994 + * + * Rest of serial code Copyright (C) 1995 by Mark Rejhon + * + * The code in this module 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 2 of + * the License, or (at your option) any later version. + * + * This module is maintained by Mark Rejhon at these Email addresses: + * marky@magmacom.com + * ag115@freenet.carleton.ca + * /REMARK + * + * maintainer: + * Mark Rejhon + * + * DANG_END_MODULE + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "emu.h" +#include "sig.h" +#include "port.h" +#include "bios.h" +#include "pic.h" +#include "serial.h" +#include "ser_defs.h" +#include "tty_io.h" +#include "sermouse.h" +#include "utilities.h" /* due to getpwnam */ +#include "iodev.h" +#include "comredir.h" +#include "nullmm.h" + +int no_local_video = 0; +serial_t com_cfg[MAX_SER]; +com_t com[MAX_SER]; +struct ser_dmx { + ioport_t port; + Bit8u def_val; + int use_cnt; + char name[16]; +}; +#define DMX_MAX 4 +static struct ser_dmx dmxs[DMX_MAX]; +static int num_dmxs; + +static void add_dmx(ioport_t port, int val) +{ + int i; + Bit8u dval = val - 1; + for (i = 0; i < num_dmxs; i++) { + if (dmxs[i].port == port) { + if (dmxs[i].def_val != dval) { + error("SER: inconsistent config for demux on port %#x\n", port); + return; + } + dmxs[i].use_cnt++; + return; + } + } + num_dmxs++; + assert(num_dmxs <= DMX_MAX); + dmxs[i].port = port; + dmxs[i].def_val = dval; + dmxs[i].use_cnt = 1; + sprintf(dmxs[i].name, "ser_dmx_%hhi", (uint8_t)i); +} + +static Bit8u dmx_readb(ioport_t port, void *arg) +{ + int num, i; + Bit8u val; + for (i = 0; i < num_dmxs; i++) { + if (dmxs[i].port == port) + break; + } + assert(i < num_dmxs); + val = dmxs[i].def_val; + for (num = 0; num < config.num_ser; num++) { + if (com_cfg[num].dmx_port == port && + (com[num].int_condition & com_cfg[num].dmx_mask)) { + if (com_cfg[num].dmx_val) + val |= 1 << com_cfg[num].dmx_shift; + else + val &= ~(1 << com_cfg[num].dmx_shift); + } + } + s_printf("SER: read demux at port %#x=%#x\n", dmxs[i].port, val); + return val; +} + +static void dmx_writeb(ioport_t port, Bit8u value, void *arg) +{ + s_printf("SER: write to readonly port %#x, val=%#x\n", port, value); +} + +static int init_dmxs(void) +{ + emu_iodev_t io_device; + int i; + + for (i = 0; i < num_dmxs; i++) { + io_device.read_portb = dmx_readb; + io_device.write_portb = dmx_writeb; + io_device.read_portw = NULL; + io_device.write_portw = NULL; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + io_device.start_addr = dmxs[i].port; + io_device.end_addr = dmxs[i].port; + io_device.handler_name = dmxs[i].name; + port_register_handler(io_device, 0); + s_printf("SER: added demux at port %#x\n", dmxs[i].port); + } + return i; +} + +static void ser_reset_dev(int num) +{ + com[num].dll = 0x30; /* Baudrate divisor LSB: 2400bps */ + com[num].dlm = 0; /* Baudrate divisor MSB: 2400bps */ + com[num].IER = 0; /* Interrupt Enable Register */ + com[num].IIR.mask = 0; /* Interrupt I.D. Register */ + com[num].LCR = UART_LCR_WLEN8; /* Line Control Register: 5N1 */ + com[num].FCReg = 0; /* FIFO Control Register */ + com[num].rx_fifo_trigger = 1; /* Receive FIFO trigger level */ + com[num].MCR = 0; /* Modem Control Register */ + com[num].LSR = UART_LSR_TEMT | UART_LSR_THRE; /* Txmit Hold Reg Empty */ + com[num].MSR = 0; /* Modem Status Register */ + com[num].SCR = 0; /* Scratch Register */ + com[num].int_condition = TX_INTR; /* FLAG: Pending xmit intr */ + com[num].IIR.flags = 0; /* FLAG: FIFO disabled */ + com[num].ms_timer = 0; /* Modem Status check timer */ + com[num].rx_timer = 0; /* Receive read() polling timer */ + com[num].rx_timeout = 0; /* FLAG: No Receive timeout */ + com[num].rx_fifo_size = 16; /* Size of receive FIFO to emulate */ + com[num].tx_cnt = 0; + uart_clear_fifo(num,UART_FCR_CLEAR_CMD); /* Initialize FIFOs */ +} + +static void ser_setup_custom(int num) +{ + int i, cnt; + switch (com_cfg[num].custom) { + case SER_CUSTOM_NONE: + break; + case SER_CUSTOM_PCCOM: + s_printf("SER%d: setting up PCCOM config\n", num); + cnt = 0; + for (i = 0; i < num; i++) { + if (com_cfg[i].custom == SER_CUSTOM_PCCOM) + cnt++; + } + com_cfg[num].dmx_port = (cnt < 4 ? 0x2bf : 0x1bf); + com_cfg[num].dmx_mask = RX_INTR; + com_cfg[num].dmx_shift = cnt; + com_cfg[num].dmx_val = 0; + + if (!com_cfg[num].base_port) + com_cfg[num].base_port = (cnt < 4 ? 0x2a0 : 0x1a0) + cnt * 8; + com_cfg[num].end_port = com_cfg[num].base_port + 6; + if (!com_cfg[num].irq) + com_cfg[num].irq = (cnt < 4 ? 5 : 7); + break; + } +} + +static Bit8u com_readb(ioport_t port, void *arg) +{ + int tmp; + for (tmp = 0; tmp < config.num_ser; tmp++) { + if (((u_short)(port & ~7)) == com_cfg[tmp].base_port) { + return(do_serial_in(tmp, (int)port)); + } + } + return 0; +} + +static void com_writeb(ioport_t port, Bit8u value, void *arg) +{ + int tmp; + for (tmp = 0; tmp < config.num_ser; tmp++) { + if (((u_short)(port & ~7)) == com_cfg[tmp].base_port) { + do_serial_out(tmp, (int)port, (int)value); + } + } +} + +/* The following function is the main initialization routine that + * initializes the UART for ONE serial port. This includes setting up + * the environment, define default variables, the emulated UART's init + * stat, and open/initialize the serial line. [num = port] + */ +static void do_ser_init(int num) +{ + emu_iodev_t io_device; + + /* The following section sets up default com port, interrupt, base + ** port address, and device path if they are undefined. The defaults are: + ** + ** COM1: irq = 4 base_port = 0x3F8 device = /dev/ttyS0 + ** COM2: irq = 3 base_port = 0x2F8 device = /dev/ttyS1 + ** COM3: irq = 4 base_port = 0x3E8 device = /dev/ttyS2 + ** COM4: irq = 3 base_port = 0x2E8 device = /dev/ttyS3 + */ + + static struct { + int irq; + ioport_t base_port; + const char *dev; + const char *handler_name; + } default_com[MAX_SER] = { + { 4, 0x3F8, "/dev/ttyS0", "COM1" }, + { 3, 0x2F8, "/dev/ttyS1", "COM2" }, + { 4, 0x3E8, "/dev/ttyS2", "COM3" }, + { 3, 0x2E8, "/dev/ttyS3", "COM4" }, + + { 3, 0x4220, "/dev/ttyS4", "COM5" }, + { 3, 0x4228, "/dev/ttyS5", "COM6" }, + { 3, 0x5220, "/dev/ttyS6", "COM7" }, + { 3, 0x5228, "/dev/ttyS7", "COM8" }, + + { 4, 0x6220, "/dev/ttyS8", "COM9" }, + { 4, 0x6228, "/dev/ttyS9", "COM10" }, + { 4, 0x7220, "/dev/ttyS10", "COM11" }, + { 4, 0x7228, "/dev/ttyS11", "COM12" }, + + { 4, 0x8220, "/dev/ttyS12", "COM13" }, + { 4, 0x8228, "/dev/ttyS13", "COM14" }, + { 4, 0x9220, "/dev/ttyS14", "COM15" }, + { 4, 0x9228, "/dev/ttyS15", "COM16" }, + }; + + ser_setup_custom(num); + + if (com_cfg[num].real_comport == 0) { /* Is comport number undef? */ + error("SER%d: No COMx port number given\n", num); + config.exitearly = 1; + return; + } + + if (com_cfg[num].irq == 0) { /* Is interrupt undefined? */ + /* Define it depending on using standard irqs */ + com_cfg[num].irq = default_com[com_cfg[num].real_comport-1].irq; + } + + if (com_cfg[num].base_port == 0) { /* Is base port undefined? */ + /* Define it depending on using standard addrs */ + com_cfg[num].base_port = default_com[com_cfg[num].real_comport-1].base_port; + } + if (com_cfg[num].end_port == 0) + com_cfg[num].end_port = com_cfg[num].base_port + 7; + +#ifdef USE_MODEMU + if (com_cfg[num].vmodem) + com_cfg[num].dev = modemu_init(num); +#endif + /* Is the device file undef? */ + if ((!com_cfg[num].dev || !com_cfg[num].dev[0]) && !com_cfg[num].mouse) { + /* Define it using std devs */ + com_cfg[num].dev = strdup(default_com[com_cfg[num].real_comport-1].dev); + } + if (com_cfg[num].dev && com_cfg[num].dev[0]) + iodev_add_device(com_cfg[num].dev); + + /* FOSSIL emulation is inactive at startup. */ + com[num].fossil_active = FALSE; + + /*** The following is where the real initialization begins ***/ + + /* Tell the port manager that we exist and that we're alive */ + io_device.read_portb = com_readb; + io_device.write_portb = com_writeb; + io_device.read_portw = NULL; + io_device.write_portw = NULL; + io_device.read_portd = NULL; + io_device.write_portd = NULL; + io_device.start_addr = com_cfg[num].base_port; + io_device.end_addr = com_cfg[num].end_port; + io_device.handler_name = default_com[num].handler_name; + port_register_handler(io_device, 0); + + /* Information about serial port added to debug file */ + s_printf("SER%d: COM%d, intlevel=%d, base=0x%x, device=%s\n", + num, com_cfg[num].real_comport, com_cfg[num].irq, + com_cfg[num].base_port, com_cfg[num].dev); +#if 0 + /* first call to serial timer update func to initialize the timer */ + /* value, before the com[num] structure is initialized */ + serial_timer_update(); +#endif + /* Set file descriptor as unused, then attempt to open serial port */ + com[num].fd = -1; + + if (com_cfg[num].dmx_port) + add_dmx(com_cfg[num].dmx_port, com_cfg[num].dmx_val); +} + +void serial_reset(void) +{ + int num; + for (num = 0; num < config.num_ser; num++) + ser_reset_dev(num); + + fossil_initialised = FALSE; + comredir_reset(); +} + +/* DANG_BEGIN_FUNCTION serial_run + * + * This is the main housekeeping function, which should be called about + * 20 to 100 times per second. The more frequent, the better, up to + * a certain point. However, it should be self-compensating if it + * executes 10 times or even 1000 times per second. Serial performance + * increases with frequency of execution of serial_run. + * + * Serial mouse performance becomes more smooth if the time between + * calls to serial_run are smaller. + * + * DANG_END_FUNCTION + */ +static void serial_run(void) +{ + int i; +#if 0 + /* Update the internal serial timers */ + serial_timer_update(); +#endif + /* Do the necessary interrupt checksing in a logically efficient manner. + * All the engines have built-in code to prevent loading the + * system if they are called 100x's per second. + */ + for (i = 0; i < config.num_ser; i++) { + if (com[i].opened <= 0) + continue; + serial_update(i); + } +} + +/* DANG_BEGIN_FUNCTION serial_init + * + * This is the master serial initialization function that is called + * upon startup of DOSEMU to initialize ALL the emulated UARTs for + * all configured serial ports. The UART is initialized via the + * initialize_uart function, which opens the serial ports and defines + * variables for the specific UART. + * + * If the port is a mouse, the port is only initialized when i + * + * DANG_END_FUNCTION + */ +void serial_init(void) +{ + int i; + warn("SERIAL $Id$\n"); + s_printf("SER: Running serial_init, %d serial ports\n", config.num_ser); + + /* Do UART init here - Need to set up registers and init the lines. */ + for (i = 0; i < config.num_ser; i++) { + com[i].num = i; + com[i].cfg = &com_cfg[i]; + com[i].fd = -1; + com[i].wr_fd = -1; + com[i].opened = 0; + com[i].dev_locked = FALSE; + com[i].drv = com_cfg[i].mouse ? &serm_drv : + (com_cfg[i].nullmm ? &nullmm_drv : &tty_drv); + + /* Serial port init is skipped if the port is used for a mouse, and + * dosemu is running in Xwindows, or not at the console. This is due + * to the fact the mouse is in use by Xwindows (internal driver is used) + * Direct access to the mouse by dosemu is useful mainly at the console. + */ + do_ser_init(i); + } + + init_dmxs(); + fossil_init(); + comredir_init(); + sigalrm_register_handler(serial_run); +} + +/* Like serial_init, this is the master function that is called externally, + * but at the end, when the user quits DOSEMU. It deinitializes all the + * configured serial ports. + */ +void serial_close(void) +{ + int i; + s_printf("SER: Running serial_close\n"); + for (i = 0; i < config.num_ser; i++) { + if (com[i].opened <= 0) + continue; +#ifdef USE_MODEMU + if (com_cfg[i].vmodem) + modemu_done(i); +#endif + ser_close(i); + } +} + +int get_com_idx(int num) +{ + int i; + for (i = 0; i < config.num_ser; i++) { + if (com[i].cfg->real_comport == num) + return i; + } + return -1; +} + +#define SER_FN0(rt, f) \ +rt f(int num) \ +{ \ + return com[num].drv->f(&com[num]); \ +} +#define SER_FN1(rt, f, p, c) \ +rt f(int num, p c) \ +{ \ + return com[num].drv->f(&com[num], c); \ +} +#define SER_FN2(rt, f, p1, c1, p2, c2) \ +rt f(int num, p1 c1, p2 c2) \ +{ \ + return com[num].drv->f(&com[num], c1, c2); \ +} + +SER_FN0(void, rx_buffer_dump) +SER_FN0(void, tx_buffer_dump) +SER_FN0(int, serial_get_tx_queued) +SER_FN0(void, ser_termios) +SER_FN1(int, serial_brkctl, int, brkflg) +SER_FN2(ssize_t, serial_write, char*, buf, size_t, len) +SER_FN1(int, serial_dtr, int, flag) +SER_FN1(int, serial_rts, int, flag) +SER_FN0(int, ser_open) +SER_FN0(int, ser_close) +SER_FN0(int, uart_fill) +SER_FN0(int, serial_get_msr) diff --git a/src/base/serial/ser_irq.c b/src/base/serial/ser_irq.c new file mode 100644 index 0000000..c8fd436 --- /dev/null +++ b/src/base/serial/ser_irq.c @@ -0,0 +1,311 @@ +/* DANG_BEGIN_MODULE + * + * REMARK + * ser_irq.c: Serial interrupt services for DOSEMU + * Please read the README.serial file in this directory for more info! + * + * Copyright (C) 1995 by Mark Rejhon + * + * The code in this module 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 2 of + * the License, or (at your option) any later version. + * + * /REMARK + * This module is maintained by Stas Sergeev + * + * DANG_END_MODULE + */ + +#include +#include +#include + +#include "emu.h" +#include "timers.h" +#include "pic.h" +#include "serial.h" +#include "ser_defs.h" +#include "tty_io.h" + +/**************************************************************************/ +/* The SERIAL ENGINES */ +/**************************************************************************/ + +/* This function updates the timer counters for several serial events + * which includes: Transmission so that data gets written to the serial + * device at a constant rate, without buffer overflows. Receive, so that + * the read() system call is not done more often than needed. Modem + * Status check, so that it is not done more often than needed. + * + * In short, the serial timer ensures that the serial emulation runs + * more smoothly, and does not put a heavy load on the system. + */ +/* NOTE: with the async notifications we don't need that any more. + * Disabled -- stsp + */ +#if 0 +void serial_timer_update(void) +{ + static hitimer_t oldtp = 0; /* Timer value from last call */ + hitimer_t tp; /* Current timer value */ + unsigned long elapsed; /* No of 115200ths seconds elapsed */ + int i; + + /* Get system time. PLEASE DON'T CHANGE THIS LINE, unless you can + * _guarantee_ that the substitute/stored timer value _is_ up to date + * at _this_ instant! (i.e: vm86s exit time did not not work well) + */ + tp = GETusTIME(0); + if (oldtp==0) oldtp=tp; + /* compute the number of 115200ths of seconds since last timer update */ + elapsed = T64DIV((tp-oldtp),8.680555556); + + /* Save the old timer values for next time */ + oldtp = tp; + + /* Update all the timers */ + for (i = 0; i < config.num_ser; i++) { + /* Countdown before next character will be transmitted */ + if (com[i].tx_timer > elapsed) + com[i].tx_timer -= elapsed; + else + com[i].tx_timer = 0; + + /* Countdown to next Modem Status check */ + if (com[i].ms_timer > elapsed) + com[i].ms_timer -= elapsed; + else + com[i].ms_timer = 0; + + /* Countdown before next read() of the serial port */ + if (com[i].rx_timer > elapsed) + com[i].rx_timer -= elapsed; + else + com[i].rx_timer = 0; + } +} +#endif + +/* This function does housekeeping for serial receive operations. Duties + * of this function include keeping the UART FIFO/RBR register filled, + * and checking if it's time to generate a hardware interrupt (RDI). + * [num = port] + */ +void receive_engine(int num, int size) /* Internal 16550 Receive emulation */ +{ + if (com[num].MCR & UART_MCR_LOOP) return; /* Return if loopback */ + + if (size && RX_BUF_BYTES(num) == size && FIFO_ENABLED(num)) /* if fifo was empty */ + com[num].rx_timeout = TIMEOUT_RX; /* set timeout counter */ + + if (RX_BUF_BYTES(num)) { + com[num].LSR |= UART_LSR_DR; /* Set recv data ready bit */ + /* Has it gone above the receive FIFO trigger level? */ + if (!FIFO_ENABLED(num) || RX_BUF_BYTES(num) >= com[num].rx_fifo_trigger) { + if(s3_printf) s_printf("SER%d: Func uart_fill requesting RX_INTR\n",num); + com[num].rx_timeout = 0; + serial_int_engine(num, RX_INTR); /* Update interrupt status */ + } + } + + if (FIFO_ENABLED(num) && RX_BUF_BYTES(num) && com[num].rx_timeout) { /* Is it in FIFO mode? */ + com[num].rx_timeout--; /* Decrement counter */ + if (!com[num].rx_timeout) { /* Has timeout counted down? */ + com[num].IIR.flg.cti = 1; + if(s3_printf) s_printf("SER%d: Receive timeout (%i bytes), requesting RX_INTR\n", + num, RX_BUF_BYTES(num)); + serial_int_engine(num, RX_INTR); /* Update interrupt status */ + } + } +} + +static void update_tx_cnt(int num) +{ + /* find out how many bytes are queued by tty - may be slow */ + int queued = serial_get_tx_queued(num); + if (queued < 0) + queued = 0; + if (queued > com[num].tx_cnt) + s_printf("SER%d: ERROR: queued=%i tx_cnt=%i\n", num, queued, com[num].tx_cnt); + com[num].tx_cnt = queued; +} + +/* This function does housekeeping for serial transmit operations. + * Duties of this function include attempting to transmit the data out + * of the XMIT FIFO/THR register, and checking if it's time to generate + * a hardware interrupt (THRI). [num = port] + */ +void transmit_engine(int num) /* Internal 16550 Transmission emulation */ +{ + if (!TX_TRIGGER(num)) { + if (!(com[num].LSR & UART_LSR_TEMT)) { +#if 1 +/* the below code doesn't work right because of this slowdown patch: + * http://lkml.indiana.edu/hypermail/linux/kernel/1210.1/01456.html + * Disable it for now. + * + * ... and re-enable thanks to this patch: + * https://lkml.org/lkml/2013/5/5/49 + */ + if (com[num].tx_cnt) + update_tx_cnt(num); + if (!com[num].tx_cnt) +#endif + com[num].LSR |= UART_LSR_TEMT; + } + return; + } + + /* If Linux is handling flow control, then check the CTS state. + * If the CTS state is low, then don't start new transmit interrupt! + */ + if (com_cfg[num].system_rtscts) { + int cts = (serial_get_msr(num) & UART_MSR_CTS); + if (!cts) + return; /* Return if CTS is low */ + } + + if (com[num].tx_cnt > TX_QUEUE_THRESHOLD) + update_tx_cnt(num); + if (debug_level('s') > 5) + s_printf("SER%d: queued=%i\n", num, com[num].tx_cnt); + if (com[num].tx_cnt > TX_QUEUE_THRESHOLD) + return; + + com[num].LSR |= UART_LSR_THRE; + if (!com[num].tx_cnt) + com[num].LSR |= UART_LSR_TEMT; + if(s3_printf) s_printf("SER%d: Func transmit_engine requesting TX_INTR\n",num); + serial_int_engine(num, TX_INTR); /* Update interrupt status */ +} + + +/* This function does housekeeping for modem status operations. + * Duties of this function include polling the serial line for its status + * and updating the MSR, and checking if it's time to generate a hardware + * interrupt (MSI). [num = port] + */ +void modstat_engine(int num) /* Internal Modem Status processing */ +{ + int newmsr, delta; + + /* Return if in loopback mode */ + if (com[num].MCR & UART_MCR_LOOP) return; + +#if 0 + /* Return if it is not time to do a modem status check */ + if (com[num].ms_timer > 0) return; + com[num].ms_timer += MS_MIN_FREQ; +#endif + + if(com_cfg[num].pseudo) { + if (com[num].is_closed) + newmsr = 0; + else + newmsr = UART_MSR_CTS | UART_MSR_DSR | UART_MSR_DCD; + } else { + newmsr = serial_get_msr(num); + } + delta = msr_compute_delta_bits(com[num].MSR, newmsr); + + com[num].MSR = (com[num].MSR & UART_MSR_DELTA) | newmsr | delta; + + if (delta) { + if(s2_printf) s_printf("SER%d: Modem Status Change: MSR -> 0x%x\n",num,newmsr); + if(s3_printf) s_printf("SER%d: Func modstat_engine requesting MS_INTR\n",num); + serial_int_engine(num, MS_INTR); /* Update interrupt status */ + } +} + +/* There is no need for a Line Status Engine function right now since there + * is not much justification for one, since all the error bits that causes + * interrupt, are handled by Linux and are difficult for this serial code + * to detect. When easy non-blocking break signal handling is introduced, + * there will be justification for a simple Line Status housekeeping + * function whose purpose is to detect an error condition (mainly a + * break signal sent from the remote) and generate a hardware interrupt + * on its occurrence (RLSI). + * + * DANG_BEGIN_REMARK + * Linux code hackers: How do I detect a break signal without having + * to rely on Linux signals? Can I peek a 'break state bit'? + * Also, how do I 'turn on' and 'turn off' the break state, via + * an ioctl() or tcsetattr(), rather than using POSIX tcsendbrk()? + * DANG_END_REMARK + */ +#if 0 +void linestat_engine(int num) /* Internal Line Status processing */ +{ + s3_printf("SER%d: Func linestat_engine setting LS_INTR\n",num); + serial_int_engine(num, LS_INTR); /* Update interrupt status */ +} +#endif + +/* DANG_BEGIN_FUNCTION serial_int_engine + * + * This function is the serial interrupts scheduler. Its purpose is to + * update interrupt status and/or invoke a requested serial interrupt. + * If interrupts are not enabled, the Interrupt Identification Register + * is still updated and the function returns. + * + * Since it is not possible to run the interrupt on the spot, it triggers + * the interrupt via the pic_request() function (which is in pic.c) + * and sets a flag that an interrupt is going to be occur soon. + * + * [num = port, int_requested = the requested serial interrupt] + * + * DANG_END_FUNCTION + */ +void serial_int_engine(int num, int int_requested) +{ + /* Safety code to avoid receive and transmit while DLAB is set high */ + if (DLAB(num)) int_requested &= ~(RX_INTR | TX_INTR); + + if (TX_TRIGGER(num)) { + transmit_engine(num); + } + + if (!int_requested && !com[num].int_condition) + return; + com[num].int_condition |= int_requested; + + /* At this point, we don't much care which function is requested; that + * is taken care of in the serial_int_engine. However, if interrupts are + * disabled, then the Interrupt Identification Register must be set. + */ + + /* See if a requested interrupt is enabled */ + if (INT_ENAB(num) && INT_REQUEST(num)) { + if(s3_printf) s_printf("SER%d: Func pic_request intlevel=%d, int_requested=%d\n", + num, com_cfg[num].irq, int_requested); + pic_request(com_cfg[num].irq); /* Flag PIC for interrupt */ + } + else + if(s3_printf) s_printf("SER%d: Interrupt %d (%d) cannot be requested: enable=%d IER=0x%x\n", + num, com_cfg[num].irq, com[num].int_condition, INT_ENAB(num), com[num].IER); +} + + +/**************************************************************************/ +/* The MAIN ENGINE LOOP */ +/**************************************************************************/ + +void serial_update(int num) +{ + int size = 0; + +#ifdef USE_MODEMU + if (com_cfg[num].vmodem) + modemu_update(num); +#endif + /* optimization: don't read() when enough data buffered */ + if (RX_BUF_BYTES(num) < com[num].rx_fifo_trigger) + size = uart_fill(num); + if (size > 0) + receive_engine(num, size); /* Receive operations */ + else if (RX_BUF_BYTES(num)) + receive_engine(num, 0); /* Handle timeouts */ + transmit_engine(num); /* Transmit operations */ + modstat_engine(num); /* Modem Status operations */ +} diff --git a/src/base/serial/ser_ports.c b/src/base/serial/ser_ports.c new file mode 100644 index 0000000..6bb81f2 --- /dev/null +++ b/src/base/serial/ser_ports.c @@ -0,0 +1,760 @@ +/* DANG_BEGIN_MODULE + * + * REMARK + * ser_ports.c: Serial ports for DOSEMU: Software emulated 16550 UART! + * Please read the README.serial file in this directory for more info! + * + * Copyright (C) 1995 by Mark Rejhon + * + * The code in this module 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 2 of + * the License, or (at your option) any later version. + * + * This module is maintained by Stas Sergeev + * + * /REMARK + * DANG_END_MODULE + */ + +/**************************** DECLARATIONS *******************************/ + +#include +#include +#include + +#include "emu.h" +#include "ser_defs.h" +#include "tty_io.h" + + +/*************************************************************************/ +/* MISCELLANEOUS serial support functions */ +/*************************************************************************/ + +/* This is a function used for translating a bit to a different bit, + * depending on a bit inside an integer 'testval'. It will return 'outbit' + * if the bit 'inbit' is set in 'testval'. + */ +inline int convert_bit(int testval, int inbit, int outbit) +{ + if (testval & inbit) + return outbit; + else + return 0; +} + + +/* This function slides the contents of the receive buffer to the + * bottom of the buffer. A sliding buffer is used instead of + * a circular buffer because this way, a single read() can easily + * put data straight into our internal receive buffer! + */ +void rx_buffer_slide(int num) +{ + if (com[num].rx_buf_start == 0) + return; + /* Move existing chars in receive buffer to the start of buffer */ + memmove(com[num].rx_buf, com[num].rx_buf + com[num].rx_buf_start, + RX_BUF_BYTES(num)); + + /* Update start and end pointers in buffer */ + com[num].rx_buf_end -= com[num].rx_buf_start; + com[num].rx_buf_start = 0; +} + +static void clear_int_cond(int num, u_char val) +{ + com[num].int_condition &= ~val; + /* reset IIR too, to recalculate later */ + com[num].IIR.mask = 0; + if (!INT_REQUEST(num)) + pic_untrigger(com_cfg[num].irq); +} + +static void recalc_IIR(int num) +{ + int tmp; +#if 0 + /* disabled being too expensive... */ + serial_update(num); +#endif + tmp = INT_REQUEST(num); + if (!tmp) + com[num].IIR.mask = 0; + else if (tmp & LS_INTR) + com[num].IIR.mask = LS_INTR; + else if (tmp & RX_INTR) + com[num].IIR.mask = RX_INTR; + else if (tmp & TX_INTR) + com[num].IIR.mask = TX_INTR; + else if (tmp & MS_INTR) + com[num].IIR.mask = MS_INTR; +} + +static u_char get_IIR_val(int num) +{ + u_char val = com[num].IIR.flags << 3; + switch (com[num].IIR.mask) { + case 0: + val |= UART_IIR_NO_INT; + break; + case LS_INTR: + val |= UART_IIR_RLSI; + break; + case RX_INTR: + val |= UART_IIR_RDI; + break; + case TX_INTR: + val |= UART_IIR_THRI; + break; + case MS_INTR: + val |= UART_IIR_MSI; + break; + } + return val; +} + +/* This function clears the specified XMIT and/or RCVR FIFO's. This is + * done on initialization, and when changing from 16450 to 16550 mode, and + * vice versa. [num = port, fifo = flags to indicate which fifos to clear] + */ +void uart_clear_fifo(int num, int fifo) +{ + /* DANG_FIXTHIS Should clearing UART cause THRE int if it's enabled? */ + + if(s1_printf) s_printf("SER%d: Clear FIFO.\n",num); + + /* Clear the receive FIFO */ + if (fifo & UART_FCR_CLEAR_RCVR) { + /* Preserve THR empty state, clear error bits and recv data ready bit */ + com[num].LSR &= ~(UART_LSR_ERR | UART_LSR_DR); + com[num].rx_buf_start = 0; /* Beginning of rec FIFO queue */ + com[num].rx_buf_end = 0; /* End of rec FIFO queue */ + com[num].IIR.flg.cti = 0; /* clear timeout */ + com[num].rx_timeout = 0; /* Receive intr already occurred */ + clear_int_cond(num, LS_INTR | RX_INTR); /* Clear LS/RX conds */ + rx_buffer_dump(num); /* Clear receive buffer */ + } + + /* Clear the transmit FIFO */ + if (fifo & UART_FCR_CLEAR_XMIT) { + /* Preserve recv data ready bit and error bits, and set THR empty */ + com[num].LSR |= UART_LSR_TEMT | UART_LSR_THRE; + clear_int_cond(num, TX_INTR); /* Clear TX int condition */ + tx_buffer_dump(num); /* Clear transmit buffer */ + } +} + + +/*************************************************************************/ +/* RECEIVE handling functions */ +/*************************************************************************/ + +/* This function returns a received character. + * The character comes from the RBR register (or FIFO), which would have + * been previously filled by the uart_fill routine (which does the + * actual reads from the serial line) or loopback code. This function + * usually runs through the do_serial_in routine. [num = port] + */ +static int get_rx(int num) +{ + int val; + com[num].rx_timeout = 0; /* Reset timeout counter */ + com[num].IIR.flg.cti = 0; + + /* if no data, try to get some */ + if (!RX_BUF_BYTES(num)) { + int size = uart_fill(num); + if (size > 0) + receive_engine(num, size); + } + /* if still no data, go out */ + if (!RX_BUF_BYTES(num)) { + if (com[num].LSR & UART_LSR_DR) { + error("COM%i: DR set but buffer empty\n", num); + com[num].LSR &= ~UART_LSR_DR; + } + return 0; + } + + /* Get byte from internal receive queue */ + val = com[num].rx_buf[com[num].rx_buf_start++]; + /* Clear data waiting status and interrupt condition flag */ + clear_int_cond(num, RX_INTR); + /* and see if more to read */ + receive_engine(num, 0); + + if (!RX_BUF_BYTES(num)) + com[num].LSR &= ~UART_LSR_DR; + + return val; /* Return received byte */ +} + + +/*************************************************************************/ +/* MODEM STATUS handling functions */ +/*************************************************************************/ + + + + +/* The following computes the MSR delta bits. It is done by computing + * the difference between two MSR values (oldmsr and newmsr) by xor'ing + * bits 4-7 of them. The exception is the RI (ring) bit which is a + * trailing edge bit (the RI trailing edge bit is set on only when RI + * goes from on to off). [oldmsr = old value, newmsr = new value] + */ +int msr_compute_delta_bits(int oldmsr, int newmsr) +{ + int delta; + + /* Compute difference bits, and restrict RI bit to trailing-edge */ + delta = (oldmsr ^ newmsr) & ~(newmsr & UART_MSR_RI); + + /* Shift bits to lowest 4 bits and mask other bits except delta bits */ + delta = (delta >> 4) & UART_MSR_DELTA; + + return delta; +} + + +/* This function returns the value in the MSR (Modem Status Register) + * and does the expected UART operation whenever the MSR is read. + * [num = port] + */ +static int +get_msr(int num) +{ + int val; + + modstat_engine(num); /* Get the fresh MSR status */ + val = com[num].MSR; /* Save old MSR value */ + com[num].MSR &= UART_MSR_STAT; /* Clear delta bits */ + clear_int_cond(num, MS_INTR); /* MSI condition satisfied */ + + return val; /* Return MSR value */ +} + + +/*************************************************************************/ +/* LINE STATUS handling functions */ +/*************************************************************************/ + +/* This function returns the value in the LSR (Line Status Register) + * and does the expected UART operation whenever the LSR is read. + * [num = port] + */ +static int +get_lsr(int num) +{ + int val; + + val = com[num].LSR; /* Save old LSR value */ + clear_int_cond(num, LS_INTR); /* RLSI int condition satisfied */ + com[num].LSR &= ~UART_LSR_ERR; /* Clear error bits */ + + return (val); /* Return LSR value */ +} + + +/*************************************************************************/ +/* TRANSMIT handling functions */ +/*************************************************************************/ + +/* This function transmits a character. This function is called mainly + * through do_serial_out, when the Transmit Register is written to. + * The end result is that the character is put into the THR or the + * transmit FIFO. (or receive fifo if in Loopback test mode) + * [num = port, val = character to transmit] + */ +static void put_tx(int num, char val) +{ + int rtrn; +#if 0 + /* Update the transmit timer */ + com[num].tx_timer += com[num].tx_char_time; +#endif + clear_int_cond(num, TX_INTR); /* TX interrupt condition satisfied */ + + /* Loop-back writes. Parity is currently not calculated. No + * UART diagnostics programs including COMTEST.EXE, that I tried, + * complained about parity problems during loopback tests anyway! + * Even some real UART clones don't calculate parity during loopback. + */ + if (com[num].MCR & UART_MCR_LOOP) { + com[num].rx_timeout = 0; /* Reset timeout counter */ + switch (com[num].LCR & UART_LCR_WLEN8) { /* Word size adjustment */ + case UART_LCR_WLEN7: val &= 0x7f; break; + case UART_LCR_WLEN6: val &= 0x3f; break; + case UART_LCR_WLEN5: val &= 0x1f; break; + } + if (FIFO_ENABLED(num)) { /* Is it in FIFO mode? */ + /* Is the FIFO full? */ + if (RX_BUF_BYTES(num) >= com[num].rx_fifo_size) { + if(s3_printf) s_printf("SER%d: Func put_tx loopback overrun requesting LS_INTR\n",num); + com[num].LSR |= UART_LSR_OE; /* Indicate overrun error */ + serial_int_engine(num, LS_INTR); /* Update interrupt status */ + } + else { /* FIFO not full */ + /* Put char into recv FIFO */ + com[num].rx_buf[com[num].rx_buf_end] = val; + com[num].rx_buf_end++; + + /* If the buffer touches the top, slide chars to bottom of buffer */ + if ((com[num].rx_buf_end - 1) >= RX_BUFFER_SIZE) rx_buffer_slide(num); + /* Is it the past the receive FIFO trigger level? */ + if (RX_BUF_BYTES(num) >= com[num].rx_fifo_trigger) { + com[num].rx_timeout = 0; + if(s3_printf) s_printf("SER%d: Func put_tx loopback requesting RX_INTR\n",num); + serial_int_engine(num, RX_INTR); /* Update interrupt status */ + } + } + com[num].LSR |= UART_LSR_DR; /* Flag Data Ready bit */ + } + else { /* FIFOs not enabled */ + com[num].rx_buf[com[num].rx_buf_end] = val; /* Overwrite old byte */ + com[num].rx_buf_end++; + if (com[num].LSR & UART_LSR_DR) { /* Was data waiting? */ + com[num].LSR |= UART_LSR_OE; /* Indicate overrun error */ + if(s3_printf) s_printf("SER%d: Func put_tx loopback overrun requesting LS_INTR\n",num); + serial_int_engine(num, LS_INTR); /* Update interrupt status */ + } + else { + com[num].LSR |= UART_LSR_DR; /* Flag Data Ready bit */ + com[num].rx_timeout = 0; + if(s3_printf) s_printf("SER%d: Func put_tx loopback requesting RX_INTR\n",num); + serial_int_engine(num, RX_INTR); /* Update interrupt status */ + } + } + return; + } + /* Else, not in loopback mode */ + + if (!FIFO_ENABLED(num) && !(com[num].LSR & UART_LSR_THRE)) { + s_printf("SER%d: ERROR: TX overrun\n", num); + /* no indication bit for this??? */ + return; + } + + rtrn = serial_write(num, &val, 1); + if (rtrn != 1) { /* Did transmit fail? */ + s_printf("SER%d: write failed! %s\n", num, strerror(errno)); /* Set overflow flag */ + } else { + com[num].LSR &= ~(UART_LSR_THRE | UART_LSR_TEMT); /* THR full */ + com[num].tx_cnt++; + } + + transmit_engine(num); +} + + +/*************************************************************************/ +/* Miscallenous UART REGISTER handling functions */ +/*************************************************************************/ + +/* This function handles writes to the FCR (FIFO Control Register). + * [num = port, val = new value to write to FCR] + */ +static void +put_fcr(int num, int val) +{ + val &= 0xcf; /* bits 4,5 are reserved. */ + /* Bits 1-6 are only programmed when bit 0 is set (FIFO enabled) */ + if (val & UART_FCR_ENABLE_FIFO) { + /* fifos are reset when we change from 16450 to 16550 mode.*/ + if (!(com[num].FCReg & UART_FCR_ENABLE_FIFO)) + uart_clear_fifo(num, UART_FCR_CLEAR_CMD); + else if (val & UART_FCR_CLEAR_CMD) + /* Commands to reset either of the two fifos. The clear-FIFO bits + * are disposed right after the FIFO's are cleared, and are not saved. + */ + uart_clear_fifo(num, val & UART_FCR_CLEAR_CMD); + + /* Various flags to indicate that fifos are enabled. */ + com[num].FCReg = (val & ~UART_FCR_TRIGGER_14) | UART_FCR_ENABLE_FIFO; + com[num].IIR.flg.fifo_enable = IIR_FIFO_ENABLE; + + /* Don't need to emulate RXRDY,TXRDY pins, used for bus-mastering. */ + if (val & UART_FCR_DMA_SELECT) { + if(s1_printf) s_printf("SER%d: FCR: Attempt to change RXRDY & TXRDY pin modes\n",num); + } + + /* Assign special variable for trigger value for easy access. */ + switch (val & UART_FCR_TRIGGER_14) { + case UART_FCR_TRIGGER_1: com[num].rx_fifo_trigger = 1; break; + case UART_FCR_TRIGGER_4: com[num].rx_fifo_trigger = 4; break; + case UART_FCR_TRIGGER_8: com[num].rx_fifo_trigger = 8; break; + case UART_FCR_TRIGGER_14: com[num].rx_fifo_trigger = 14; break; + } + if(s2_printf) s_printf("SER%d: FCR: Trigger Level is %d\n",num,com[num].rx_fifo_trigger); + } + else { /* FIFO are disabled */ + /* If uart was set in 16550 mode, this will reset it back to 16450 mode. + * If it already was in 16450 mode, there is no harm done. + */ + com[num].IIR.flg.fifo_enable = 0; /* Disabled FIFO flag */ + com[num].FCReg &= (~UART_FCR_ENABLE_FIFO); /* Flag FIFO as disabled */ + uart_clear_fifo(num, UART_FCR_CLEAR_CMD); /* Clear FIFO */ + } +} + + +/* This function handles writes to the LCR (Line Control Register). + * [num = port, val = new value to write to LCR] + */ +static void +put_lcr(int num, int val) +{ + int changed = com[num].LCR ^ val; /* bitmask of changed bits */ + + com[num].LCR = val; /* Set new LCR value */ + + if (val & UART_LCR_DLAB) { /* Is Baudrate Divisor Latch set? */ + s_printf("SER%d: LCR = 0x%x, DLAB high.\n", num, val); + } + else { + s_printf("SER%d: LCR = 0x%x, DLAB low.\n", num, val); + } + + if (changed & UART_LCR_SBC) + serial_brkctl(num, !!(val & UART_LCR_SBC)); + + /* obviously the writes to LCR (except BREAK state) would + * invalidate the rx fifo on a real com port. Pseudo ports do not care. */ + if ((changed & ~UART_LCR_SBC) && !DLAB(num)) { + if (!com[num].cfg->pseudo) + uart_clear_fifo(num, UART_FCR_CLEAR_CMD); + ser_termios(num); /* Sets new line settings */ + } +} + + +/* This function handles writes to the MCR (Modem Control Register). + * [num = port, val = new value to write to MCR] + */ +static void +put_mcr(int num, int val) +{ + int newmsr, delta; + int changed; + changed = com[num].MCR ^ val; /* Bitmask of changed bits */ + com[num].MCR = val & UART_MCR_VALID; /* Set valid bits for MCR */ + + if (val & UART_MCR_LOOP) { /* Is Loopback Mode set? */ + /* If loopback just enabled, clear FIFO and turn off DTR & RTS on line */ + if (changed & UART_MCR_LOOP) { /* Was loopback just set? */ + if (FIFO_ENABLED(num)) + uart_clear_fifo(num,UART_FCR_CLEAR_CMD); /* Clear FIFO's */ + serial_rts(num, 0); + serial_dtr(num, 0); + } + + /* During a UART Loopback test these bits are, Write(Out) => Read(In) + * MCR Bit 1 RTS => MSR Bit 4 CTS, MCR Bit 2 OUT1 => MSR Bit 6 RI + * MCR Bit 0 DTR => MSR Bit 5 DSR, MCR Bit 3 OUT2 => MSR Bit 7 DCD + */ + newmsr = convert_bit(val, UART_MCR_RTS, UART_MSR_CTS); + newmsr |= convert_bit(val, UART_MCR_DTR, UART_MSR_DSR); + newmsr |= convert_bit(val, UART_MCR_OUT1, UART_MSR_RI); + newmsr |= convert_bit(val, UART_MCR_OUT2, UART_MSR_DCD); + + /* Compute delta bits of MSR */ + delta = msr_compute_delta_bits(com[num].MSR, newmsr); + + /* Update the MSR and its delta bits, not erasing old delta bits!! */ + com[num].MSR = (com[num].MSR & UART_MSR_DELTA) | delta | newmsr; + + /* Set the MSI interrupt flag if loopback changed the modem status */ + if(s3_printf) s_printf("SER%d: Func put_mcr loopback requesting MS_INTR\n",num); + if (delta) serial_int_engine(num, MS_INTR); /* Update interrupt status */ + + } + else { /* It's not in Loopback Mode */ + /* Was loopback mode just turned off now? Then reset FIFO */ + if (changed & UART_MCR_LOOP) { + if (FIFO_ENABLED(num)) + uart_clear_fifo(num,UART_FCR_CLEAR_CMD); + } + + /* Force RTS & DTR reinitialization if the loopback state has changed */ + if (UART_MCR_LOOP) changed |= UART_MCR_RTS | UART_MCR_DTR; + + /* Update DTR setting on serial device only if DTR state changed */ + if (changed & UART_MCR_DTR) { + if(s1_printf) s_printf("SER%d: MCR: DTR -> %d\n",num,(val & UART_MCR_DTR)); + serial_dtr(num, !!(val & UART_MCR_DTR)); + } + + /* Update RTS setting on serial device only if RTS state changed */ + if ((changed & UART_MCR_RTS) && !com_cfg[num].system_rtscts) { + if(s1_printf) s_printf("SER%d: MCR: RTS -> %d\n",num,(val & UART_MCR_RTS)); + serial_rts(num, !!(val & UART_MCR_RTS)); + } + + /* Set interrupt enable flag according to OUT2 bit in MCR */ + if (INT_ENAB(num)) { + if(s3_printf) s_printf("SER%d: Update interrupt status after MCR update\n",num); + serial_int_engine(num, 0); + } else { + pic_untrigger(com_cfg[num].irq); + } + } +} + + +/* This function handles writes to the LSR (Line Status Register). + * [num = port, val = new value to write to LSR] + */ +static void +put_lsr(int num, int val) +{ + int int_type = 0; + + com[num].LSR = val & 0x1F; /* Bits 6,7,8 are ignored */ + + if (val & UART_LSR_ERR) /* Any error bits set? */ + int_type |= LS_INTR; /* Flag RLSI interrupt */ + else + clear_int_cond(num, LS_INTR); /* Unflag RLSI condition */ + + if (val & UART_LSR_DR) /* Is data ready bit set? */ + int_type |= RX_INTR; /* Flag RDI interrupt */ + else + clear_int_cond(num, RX_INTR); /* Unflag RDI condition */ + + if (val & UART_LSR_THRE) { + com[num].LSR |= UART_LSR_TEMT | UART_LSR_THRE; /* Set THRE state */ + int_type |= TX_INTR; /* Flag TX interrupt */ + } + else { + com[num].LSR &= ~(UART_LSR_THRE | UART_LSR_TEMT); /* Clear THRE state */ + clear_int_cond(num, TX_INTR); /* Unflag THRI condition */ + } + + if (int_type) { + /* Update interrupt status */ + if(s3_printf) s_printf("SER%d: Func put_lsr caused int_type = %d\n",num,int_type); + serial_int_engine(num, int_type); + } + /* need to sync back DR */ + receive_engine(num, 0); +} + + +/* This function handles writes to the MSR (Modem Status Register). + * [num = port, val = new value to write to MSR] + */ +static inline void +put_msr(int num, int val) +{ + /* Update MSR register */ + com[num].MSR = (com[num].MSR & UART_MSR_STAT) | (val & UART_MSR_DELTA); + + /* Update interrupt status */ + if (com[num].MSR & UART_MSR_DELTA) { + if(s3_printf) s_printf("SER%d: Func put_msr requesting MS_INTR\n",num); + serial_int_engine(num, MS_INTR); + } +} + + +/*************************************************************************/ +/* PORT I/O to UART registers: INPUT AND OUTPUT */ +/*************************************************************************/ + +/* DANG_BEGIN_FUNCTION do_serial_in + * The following function returns a value from an I/O port. The port + * is an I/O address such as 0x3F8 (the base port address of COM1). + * There are 8 I/O addresses for each serial port which ranges from + * the base port (ie 0x3F8) to the base port plus seven (ie 0x3FF). + * [num = abritary port number for serial line, address = I/O port address] + * DANG_END_FUNCTION + */ +int +do_serial_in(int num, ioport_t address) +{ + int val; + + /* delayed open happens here */ + if (!com[num].opened) + com[num].opened = ser_open(num); + if (com[num].opened <= 0) + return 0; + + switch (address - com_cfg[num].base_port) { + case UART_RX: /* Read from Received Byte Register */ +/*case UART_DLL:*/ /* or Read from Baudrate Divisor Latch LSB */ + if (DLAB(num)) { /* Is DLAB set? */ + val = com[num].dll; /* Then read Divisor Latch LSB */ + if(s1_printf) s_printf("SER%d: Read Divisor LSB = 0x%x\n",num,val); + } + else { + val = get_rx(num); /* Else, read Received Byte Register */ + if(s2_printf) s_printf("SER%d: Receive 0x%x\n",num,val); + } + break; + + case UART_IER: /* Read from Interrupt Enable Register */ +/*case UART_DLM:*/ /* or Read from Baudrate Divisor Latch MSB */ + if (DLAB(num)) { /* Is DLAB set? */ + val = com[num].dlm; /* Then read Divisor Latch MSB */ + if(s1_printf) s_printf("SER%d: Read Divisor MSB = 0x%x\n",num,val); + } + else { + val = com[num].IER; /* Else, read Interrupt Enable Register */ + if(s1_printf) s_printf("SER%d: Read IER = 0x%x\n",num,val); + } + break; + + case UART_IIR: /* Read from Interrupt Identification Register */ + if (!com[num].IIR.mask) + recalc_IIR(num); + val = get_IIR_val(num); + if (val & UART_IIR_THRI) { + if(s2_printf) s_printf("SER%d: Read IIR = 0x%x (THRI now cleared)\n",num,val); + clear_int_cond(num, TX_INTR); /* Unflag TX int condition */ + } + else { + if(s2_printf) s_printf("SER%d: Read IIR = 0x%x\n",num,val); + } + break; + + case UART_LCR: /* Read from Line Control Register */ + val = com[num].LCR; + if(s2_printf) s_printf("SER%d: Read LCR = 0x%x\n",num,val); + break; + + case UART_MCR: /* Read from Modem Control Register */ + val = com[num].MCR; + if(s2_printf) s_printf("SER%d: Read MCR = 0x%x\n",num,val); + break; + + case UART_LSR: /* Read from Line Status Register */ + val = get_lsr(num); + if(s2_printf) s_printf("SER%d: Read LSR = 0x%x\n",num,val); + break; + + case UART_MSR: /* Read from Modem Status Register */ + val = get_msr(num); + if(s2_printf) s_printf("SER%d: Read MSR = 0x%x\n",num,val); + break; + + case UART_SCR: /* Read from Scratch Register */ + val = com[num].SCR; + if(s2_printf) s_printf("SER%d: Read SCR = 0x%x\n",num,val); + break; + + default: /* The following code should never execute. */ + s_printf("ERROR: Port read 0x%x out of bounds for serial port %d\n", + address,num); + val = 0; + break; + } + + return val; +} + + +/* DANG_BEGIN_FUNCTION do_serial_out + * The following function writes a value to an I/O port. The port + * is an I/O address such as 0x3F8 (the base port address of COM1). + * [num = abritary port number for serial line, address = I/O port address, + * val = value to write to I/O port address] + * DANG_END_FUNCTION + */ +int +do_serial_out(int num, ioport_t address, int val) +{ + + /* delayed open happens here */ + if (!com[num].opened) + com[num].opened = ser_open(num); + if (com[num].opened <= 0) + return 0; + + switch (address - com_cfg[num].base_port) { + case UART_TX: /* Write to Transmit Holding Register */ +/*case UART_DLL:*/ /* or write to Baudrate Divisor Latch LSB */ + if (DLAB(num)) { /* If DLAB set, */ + com[num].dll = val; /* then write to Divisor Latch LSB */ + if(s2_printf) s_printf("SER%d: Divisor LSB = 0x%02x\n", num, val); + } + else { + if (s2_printf) { + if (com[num].MCR & UART_MCR_LOOP) + s_printf("SER%d: Transmit 0x%x Loopback\n",num,val); + else + s_printf("SER%d: Transmit 0x%x\n",num,val); + } + put_tx(num, val); /* else, Transmit character (write to THR) */ + } + break; + + case UART_IER: /* Write to Interrupt Enable Register */ +/*case UART_DLM:*/ /* or write to Baudrate Divisor Latch MSB */ + if (DLAB(num)) { /* If DLAB set, */ + com[num].dlm = val; /* then write to Divisor Latch MSB */ + if(s2_printf) s_printf("SER%d: Divisor MSB = 0x%x\n", num, val); + } + else { /* Else, write to Interrupt Enable Register */ + int tflg = 0; + if ( !(com[num].IER & UART_IER_THRI) && (val & UART_IER_THRI) ) { + /* Flag to allow THRI if enable THRE went from state 0 -> 1 */ + tflg = 1; + com[num].int_condition |= TX_INTR; // is this needed? + } + com[num].IER = (val & UART_IER_VALID); /* Write to IER */ + if (!INT_REQUEST(num)) + pic_untrigger(com_cfg[num].irq); + if(s1_printf) s_printf("SER%d: Write IER = 0x%x\n", num, val); + if (tflg) + serial_int_engine(num, 0); + } + break; + + case UART_FCR: /* Write to FIFO Control Register */ + if(s1_printf) s_printf("SER%d: FCR = 0x%x -> 0x%x\n", num, + com[num].FCReg, val); + put_fcr(num, val); + break; + + case UART_LCR: /* Write to Line Control Register */ + /* The debug message is in the ser_termios routine via put_lcr */ + put_lcr(num, val); + break; + + case UART_MCR: /* Write to Modem Control Register */ + if(s1_printf) s_printf("SER%d: MCR = 0x%x -> 0x%x\n", num, + com[num].MCR, val); + put_mcr(num, val); + break; + + case UART_LSR: /* Write to Line Status Register */ + if(s1_printf) s_printf("SER%d: LSR = 0x%x -> 0x%x\n", num, + com[num].LSR, val); + put_lsr(num, val); /* writeable only to lower 6 bits */ + break; + + case UART_MSR: /* Write to Modem Status Register */ + if(s1_printf) s_printf("SER%d: MSR = 0x%x -> 0x%x\n", num, + com[num].MSR, val); + put_msr(num, val); /* writeable only to lower 4 bits */ + break; + + case UART_SCR: /* Write to Scratch Register */ + if(s1_printf) s_printf("SER%d: SCR = 0x%x -> 0x%x\n", num, + com[num].SCR, val); + com[num].SCR = val; + break; + + default: /* The following should never execute */ + s_printf("ERROR: Port write 0x%x out of bounds for serial port %d\n", + address,num); + break; + } + + serial_int_engine(num, 0); /* Update interrupt status */ + + return 0; +} diff --git a/src/base/serial/sermouse.c b/src/base/serial/sermouse.c new file mode 100644 index 0000000..ad014a1 --- /dev/null +++ b/src/base/serial/sermouse.c @@ -0,0 +1,326 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Serial mouse + * + * Author: Stas Sergeev, with some help from gpm code + */ +#include +#include "emu.h" +#include "init.h" +#include "utilities.h" +#include "ser_defs.h" +#include "sermouse.h" + +struct serm_state { + int enabled; + int nrst; + int opened; + int div; + char but; + int x, y; + int lb, mb, rb; +}; +static struct serm_state serm; + +/*========================================================================*/ +/* + * When repeating, it is important not to try to repeat more bits of dx and + * dy than the protocol can handle. Otherwise, you may end up repeating the + * low bits of a large value, which causes erratic motion. + */ +/*========================================================================*/ +static int limit_delta(int delta, int min, int max) +{ + return delta > max ? max : + delta < min ? min : delta; +} + +static int ser_mouse_accepts(int from, void *udata) +{ + com_t *c = udata; + if (!serm.opened) + return 0; + if (!c) { + dosemu_error("sermouse NULL udata\n"); + return 0; + } + /* if commouse is used, we need to accept any events */ + return (c->cfg->mouse && (config.mouse.dev_type == from || + config.mouse.com != -1)); +} + +static int add_buf(com_t *c, const char *buf, int len) +{ + if (!serm.enabled || !serm.opened || serm.div != DIV_1200) + return 0; + if (RX_BUF_BYTES(c->num) + len > RX_BUFFER_SIZE) { + if(s3_printf) s_printf("SER%d: Too many bytes (%i) in buffer\n", c->num, + RX_BUF_BYTES(c->num)); + return 0; + } + + /* Slide the buffer contents to the bottom */ + rx_buffer_slide(c->num); + + memcpy(&c->rx_buf[c->rx_buf_end], buf, len); + if (debug_level('s') >= 9) { + int i; + for (i = 0; i < len; i++) + s_printf("SER%d: Got mouse data byte: %#x\n", c->num, + c->rx_buf[c->rx_buf_end + i]); + } + c->rx_buf_end += len; + receive_engine(c->num, len); + return len; +} + +static void ser_mouse_move_button(int num, int press, void *udata) +{ + com_t *c = udata; + char buf[3] = {0x40, 0, 0}; + + s_printf("SERM: button %i %i\n", num, press); + switch (num) { + case 0: + if (press == serm.lb) + return; + serm.lb = press; + if (press) + serm.but |= 0x20; + else + serm.but &= ~0x20; + break; + case 1: + if (press == serm.mb) + return; + serm.mb = press; + /* change in mbutton is signalled by sending the prev state */ + break; + case 2: + if (press == serm.rb) + return; + serm.rb = press; + if (press) + serm.but |= 0x10; + else + serm.but &= ~0x10; + break; + } + buf[0] |= serm.but; + + add_buf(c, buf, sizeof(buf)); +} + +static void ser_mouse_move_buttons(int lbutton, int mbutton, int rbutton, + void *udata) +{ + com_t *c = udata; + char buf[3] = {0x40, 0, 0}; + + if (serm.lb == lbutton && serm.mb == mbutton && serm.rb == rbutton) + return; + serm.lb = lbutton; + serm.mb = mbutton; + serm.rb = rbutton; + s_printf("SERM: buttons %i %i %i\n", lbutton, mbutton, rbutton); + serm.but = 0; + if (lbutton) + serm.but |= 0x20; + if (rbutton) + serm.but |= 0x10; + buf[0] |= serm.but; + /* change in mbutton is signalled by sending the prev state */ + + add_buf(c, buf, sizeof(buf)); +} + +static void ser_mouse_move_wheel(int dy, void *udata) +{ + s_printf("serial mouse wheel move\n"); +} + +static void ser_mouse_move_mickeys(int dx, int dy, void *udata) +{ + com_t *c = udata; + char buf[3] = {0x40, 0, 0}; + + if (!dx && !dy) + return; + s_printf("SERM: movement %i %i\n", dx, dy); + buf[0] |= serm.but; + dx = limit_delta(dx, -128, 127); + buf[1] = dx & ~0xC0; + buf[0] |= (dx & 0xC0) >> 6; + + dy = limit_delta(dy, -128, 127); + buf[2] = dy & ~0xC0; + buf[0] |= (dy & 0xC0) >> 4; + + add_buf(c, buf, sizeof(buf)); +} + +static void ser_mouse_move_relative(int dx, int dy, int x_range, int y_range, + void *udata) +{ + if (!dx && !dy) + return; + /* oops, ignore ranges and use hardcoded ratio for now */ + ser_mouse_move_mickeys(dx, dy * 2, udata); +} + +static void ser_mouse_move_absolute(int x, int y, int x_range, int y_range, + int vis, void *udata) +{ + int dx = x - serm.x; + int dy = y - serm.y; + + if (!dx && !dy) + return; + ser_mouse_move_relative(dx, dy, x_range, y_range, udata); + serm.x = x; + serm.y = y; +} + +static void ser_mouse_drag_to_corner(int x_range, int y_range, void *udata) +{ + int i; + for (i = 0; i < 10; i++) + ser_mouse_move_mickeys(-100, -100, udata); + serm.x = serm.y = 0; +} + +struct mouse_drv ser_mouse = { + .accepts = ser_mouse_accepts, + .move_button = ser_mouse_move_button, + .move_buttons = ser_mouse_move_buttons, + .move_wheel = ser_mouse_move_wheel, + .move_relative = ser_mouse_move_relative, + .move_mickeys = ser_mouse_move_mickeys, + .move_absolute = ser_mouse_move_absolute, + .drag_to_corner = ser_mouse_drag_to_corner, + .name = "serial mouse", +}; + +CONSTRUCTOR(static void serial_mouse_register(void)) +{ + register_mouse_driver(&ser_mouse); +} + + +static void serm_rx_buffer_dump(com_t *c) +{ +} + +static void serm_tx_buffer_dump(com_t *c) +{ +} + +static int serm_get_tx_queued(com_t *c) +{ + return 0; +} + +static void serm_termios(com_t *c) +{ + serm.div = ((c->dlm << 8) | c->dll); + s_printf("SERM: set div to %i\n", serm.div); +} + +static int serm_brkctl(com_t *c, int flag) +{ + return 0; +} + +static ssize_t serm_write(com_t *c, char *buf, size_t len) +{ + return 0; +} + +static int serm_dtr(com_t *c, int flag) +{ + serm.enabled = flag; + modstat_engine(c->num); // update DSR + return 0; +} + +static int serm_rts(com_t *c, int flag) +{ + if (flag && !serm.nrst) { + /* ctmouse wrongly expects "M" here. It doesn't work with "M3" */ + const char *id = "M3"; + /* Many mouse drivers require this, they detect for Framing Errors + * coming from the mouse, during initialization, usually right after + * the LCR register is set, so this is why this line of code is here + */ + c->LSR |= UART_LSR_FE; /* Set framing error */ + if(s3_printf) s_printf("SERM: framing error\n"); + rx_buffer_slide(c->num); + if (c->rx_buf_end >= c->rx_fifo_size) { + error("SERM: fifo overflow\n"); + return 0; + } + c->rx_buf[c->rx_buf_end++] = 0; + serial_int_engine(c->num, LS_INTR); /* Update interrupt status */ + add_buf(c, id, strlen(id)); + } + serm.nrst = flag; + modstat_engine(c->num); // update DSR + return 0; +} + +static int serm_open(com_t *c) +{ + s_printf("SERM: open for port %i\n", c->num); + mousedrv_set_udata(ser_mouse.name, c); + serm.opened = 1; + serm.but = 0; + return 1; +} + +static int serm_close(com_t *c) +{ + serm.opened = 0; + return 0; +} + +static int serm_uart_fill(com_t *c) +{ + return 0; +} + +static int serm_get_msr(com_t *c) +{ + return ((serm.enabled && serm.nrst) ? UART_MSR_DSR : 0); +} + + +struct serial_drv serm_drv = { + serm_rx_buffer_dump, + serm_tx_buffer_dump, + serm_get_tx_queued, + serm_termios, + serm_brkctl, + serm_write, + serm_dtr, + serm_rts, + serm_open, + serm_close, + serm_uart_fill, + serm_get_msr, + "serial_mouse_tty" +}; diff --git a/src/base/serial/sermouse.h b/src/base/serial/sermouse.h new file mode 100644 index 0000000..889d5c2 --- /dev/null +++ b/src/base/serial/sermouse.h @@ -0,0 +1,6 @@ +#ifndef SERMOUSE_H +#define SERMOUSE_H + +extern struct serial_drv serm_drv; + +#endif diff --git a/src/base/serial/tty_io.c b/src/base/serial/tty_io.c new file mode 100644 index 0000000..7a1d175 --- /dev/null +++ b/src/base/serial/tty_io.c @@ -0,0 +1,794 @@ +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "emu.h" +#include "utilities.h" +#include "dosemu_config.h" +#include "ioselect.h" +#include "ser_defs.h" +#include "tty_io.h" + +/* This function flushes the internal unix receive buffer [num = port] */ +static void tty_rx_buffer_dump(com_t *c) +{ + tcflush(c->fd, TCIFLUSH); +} + +/* This function flushes the internal unix transmit buffer [num = port] */ +static void tty_tx_buffer_dump(com_t *c) +{ + tcflush(c->fd, TCOFLUSH); +} + +static int tty_get_tx_queued(com_t *c) +{ + int ret, queued; + ret = ioctl(c->fd, TIOCOUTQ, &queued); + if (ret < 0) + return ret; + return queued; +} + +/* This function updates the line settings of a serial line (parity, + * stop bits, word size, and baudrate) to conform to the value + * stored in the Line Control Register (com[].LCR) and the Baudrate + * Divisor Latch Registers (com[].dlm and com[].dll) [num = port] + */ +static void do_termios(com_t *c) +{ + speed_t baud; + long int rounddiv; + + if (c->is_file) + return; + + /* The following is the same as (com[num].dlm * 256) + com[num].dll */ + #define DIVISOR ((c->dlm << 8) | c->dll) + + s_printf("SER%d: LCR = 0x%x, ",c->num,c->LCR); + + /* Set the word size */ + c->newset.c_cflag &= ~CSIZE; + switch (c->LCR & UART_LCR_WLEN8) { + case UART_LCR_WLEN5: + c->newset.c_cflag |= CS5; + s_printf("5"); + break; + case UART_LCR_WLEN6: + c->newset.c_cflag |= CS6; + s_printf("6"); + break; + case UART_LCR_WLEN7: + c->newset.c_cflag |= CS7; + s_printf("7"); + break; + case UART_LCR_WLEN8: + c->newset.c_cflag |= CS8; + s_printf("8"); + break; + } + + /* Set the parity. Rarely-used MARK and SPACE parities not supported yet */ + if (c->LCR & UART_LCR_PARITY) { + c->newset.c_cflag |= PARENB; + if (c->LCR & UART_LCR_EPAR) { + c->newset.c_cflag &= ~PARODD; + s_printf("E"); + } + else { + c->newset.c_cflag |= PARODD; + s_printf("O"); + } + } + else { + c->newset.c_cflag &= ~PARENB; + s_printf("N"); + } + + /* Set the stop bits: UART_LCR_STOP set means 2 stop bits, 1 otherwise */ + if (c->LCR & UART_LCR_STOP) { + /* This is actually 1.5 stop bit when word size is 5 bits */ + c->newset.c_cflag |= CSTOPB; + s_printf("2, "); + } + else { + c->newset.c_cflag &= ~CSTOPB; + s_printf("1, "); + } + + /* Linux 1.1.65 and above supports 115200 and 57600 directly, while + * Linux 1.1.64 and below do not support them. For these kernels, make + * B115200 and B57600 equal to B38400. These defines also may be + * important if DOSEMU is ported to other nix-like operating systems. + */ + #ifndef B115200 + #define B115200 B38400 + #endif + #ifndef B57600 + #define B57600 B38400 + #endif + + /* The following sets the baudrate. These nested IF statements rounds + * upwards to the next higher baudrate. (ie, rounds downwards to the next + * valid divisor value) The formula is: bps = 1843200 / (divisor * 16) + * + * Note: 38400, 57600 and 115200 won't work properly if setserial has + * the 'spd_hi' or the 'spd_vhi' setting turned on (they're obsolete!) + */ + if ((DIVISOR < DIV_57600) && DIVISOR) { /* 115200 bps */ + s_printf("bps = 115200, "); + rounddiv = DIV_115200; + baud = B115200; + } else if (DIVISOR < DIV_38400) { /* 57600 bps */ + s_printf("bps = 57600, "); + rounddiv = DIV_57600; + baud = B57600; + } else if (DIVISOR < DIV_19200) { /* 38400 bps */ + s_printf("bps = 38400, "); + rounddiv = DIV_38400; + baud = B38400; + } else if (DIVISOR < DIV_9600) { /* 19200 bps */ + s_printf("bps = 19200, "); + rounddiv = DIV_19200; + baud = B19200; + } else if (DIVISOR < DIV_4800) { /* 9600 bps */ + s_printf("bps = 9600, "); + rounddiv = DIV_9600; + baud = B9600; + } else if (DIVISOR < DIV_2400) { /* 4800 bps */ + s_printf("bps = 4800, "); + rounddiv = DIV_4800; + baud = B4800; + } else if (DIVISOR < DIV_1800) { /* 2400 bps */ + s_printf("bps = 2400, "); + rounddiv = DIV_2400; + baud = B2400; + } else if (DIVISOR < DIV_1200) { /* 1800 bps */ + s_printf("bps = 1800, "); + rounddiv = DIV_1800; + baud = B1800; + } else if (DIVISOR < DIV_600) { /* 1200 bps */ + s_printf("bps = 1200, "); + rounddiv = DIV_1200; + baud = B1200; + } else if (DIVISOR < DIV_300) { /* 600 bps */ + s_printf("bps = 600, "); + rounddiv = DIV_600; + baud = B600; + } else if (DIVISOR < DIV_150) { /* 300 bps */ + s_printf("bps = 300, "); + rounddiv = DIV_300; + baud = B300; + } else if (DIVISOR < DIV_110) { /* 150 bps */ + s_printf("bps = 150, "); + rounddiv = DIV_150; + baud = B150; + } else if (DIVISOR < DIV_50) { /* 110 bps */ + s_printf("bps = 110, "); + rounddiv = DIV_110; + baud = B110; + } else { /* 50 bps */ + s_printf("bps = 50, "); + rounddiv = DIV_50; + baud = B50; + } + s_printf("divisor 0x%x -> 0x%lx\n", DIVISOR, rounddiv); + + /* The following does the actual system calls to set the line parameters */ + cfsetispeed(&c->newset, baud); + cfsetospeed(&c->newset, baud); + if (debug_level('s') > 7) { + s_printf("SER%d: iflag=%x oflag=%x cflag=%x lflag=%x\n", c->num, + c->newset.c_iflag, c->newset.c_oflag, + c->newset.c_cflag, c->newset.c_lflag); + } +} + +static void tty_termios(com_t *c) +{ + do_termios(c); + tcsetattr(c->fd, TCSANOW, &c->newset); +} + +static int tty_brkctl(com_t *c, int brkflg) +{ + int ret; + /* there is change of break state */ + if (brkflg) { + s_printf("SER%d: Setting BREAK state.\n", c->num); + tcdrain(c->fd); + ret = ioctl(c->fd, TIOCSBRK); + } else { + s_printf("SER%d: Clearing BREAK state.\n", c->num); + ret = ioctl(c->fd, TIOCCBRK); + } + return ret; +} + +static ssize_t tty_write(com_t *c, char *buf, size_t len) +{ + int fd; + if (c->cfg->ro && c->wr_fd == -1) + return len; + fd = (c->wr_fd == -1 ? c->fd : c->wr_fd); + return RPT_SYSCALL(write(fd, buf, len)); /* Attempt char xmit */ +} + +static int tty_dtr(com_t *c, int flag) +{ + int ret, control; + control = TIOCM_DTR; + if (flag) + ret = ioctl(c->fd, TIOCMBIS, &control); + else + ret = ioctl(c->fd, TIOCMBIC, &control); + return ret; +} + +static int tty_rts(com_t *c, int flag) +{ + int ret, control; + control = TIOCM_RTS; + if (flag) + ret = ioctl(c->fd, TIOCMBIS, &control); + else + ret = ioctl(c->fd, TIOCMBIC, &control); + return ret; +} + +/* Determines if the tty is already locked. Stolen from uri-dip-3.3.7k + * Nice work Uri Blumenthal & Ian Lance Taylor! + * [nam = complete path to lock file, return = nonzero if locked] + */ +static int tty_already_locked(char *nam) +{ + int i = 0, pid = 0; + FILE *fd = (FILE *)0; + + /* Does the lock file on our device exist? */ + if ((fd = fopen(nam, "re")) == (FILE *)0) + return 0; /* No, return perm to continue */ + + /* Yes, the lock is there. Now let's make sure at least */ + /* there's no active process that owns that lock. */ + if(config.tty_lockbinary) + i = read(fileno(fd), &pid, sizeof(pid)) == sizeof(pid); + else + i = fscanf(fd, "%d", &pid); + + (void) fclose(fd); + + if (i != 1) /* Lock file format's wrong! Kill't */ + return 0; + + /* We got the pid, check if the process's alive */ + if (kill(pid, 0) == 0) /* it found process */ + return 1; /* Yup, it's running... */ + + /* Dead, we can proceed locking this device... */ + return 0; +} + +/* Locks or unlocks a terminal line Stolen from uri-dip-3.3.7k + * Nice work Uri Blumenthal & Ian Lance Taylor! + * [path = device name, + * mode: 1 = lock, 2 = reaquire lock, anythingelse = unlock, + * return = zero if success, greater than zero for failure] + */ +static int tty_lock(const char *path, int mode) +{ + char saved_path[strlen(config.tty_lockdir) + 1 + + strlen(config.tty_lockfile) + + strlen(path) + 1]; + struct passwd *pw; + pid_t ime; + const char *slash; + + if (path == NULL) return(0); /* standard input */ + slash = strrchr(path, '/'); + if (slash == NULL) + slash = path; + else + slash++; + + sprintf(saved_path, "%s/%s%s", config.tty_lockdir, config.tty_lockfile, + slash); + + if (mode == 1) { /* lock */ + { + FILE *fd; + if (tty_already_locked(saved_path) == 1) { + error("attempt to use already locked tty %s\n", saved_path); + return (-1); + } + unlink(saved_path); /* kill stale lockfiles, if any */ + fd = fopen(saved_path, "we"); + if (fd == (FILE *)0) { + error("tty: lock: (%s): %s\n", saved_path, strerror(errno)); + return(-1); + } + + ime = getpid(); + if(config.tty_lockbinary) + write (fileno(fd), &ime, sizeof(ime)); + else + fprintf(fd, "%10d\n", (int)ime); + + (void)fclose(fd); + } + + /* Make sure UUCP owns the lockfile. Required by some packages. */ + if ((pw = getpwnam(owner_tty_locks)) == NULL) { + error("tty: lock: UUCP user %s unknown!\n", owner_tty_locks); + return(0); /* keep the lock anyway */ + } + + (void) chown(saved_path, pw->pw_uid, pw->pw_gid); + (void) chmod(saved_path, 0644); + } + else if (mode == 2) { /* re-acquire a lock after a fork() */ + FILE *fd; + + fd = fopen(saved_path,"we"); + if (fd == (FILE *)0) { + error("tty_lock: reacquire (%s): %s\n", + saved_path, strerror(errno)); + return(-1); + } + ime = getpid(); + + if(config.tty_lockbinary) + write (fileno(fd), &ime, sizeof(ime)); + else + fprintf(fd, "%10d\n", (int)ime); + + (void) fclose(fd); + (void) chmod(saved_path, 0440); + return(0); + } + else { /* unlock */ + FILE *fd; + int retval; + + fd = fopen(saved_path,"we"); + if (fd == (FILE *)0) { + error("tty_lock: can't reopen %s to delete: %s\n", + saved_path, strerror(errno)); + return (-1); + } + + retval = unlink(saved_path); + if (retval < 0) { + error("tty: unlock: (%s): %s\n", saved_path, + strerror(errno)); + fclose(fd); + return -1; + } + } + return(0); +} + +static void ser_set_params(com_t *c) +{ + int data = 0; + + /* Return if not a tty */ + if (tcgetattr(c->fd, &c->newset) == -1) { + if(s1_printf) s_printf("SER%d: Line Control: NOT A TTY (%s).\n",c->num,strerror(errno)); + return; + } + c->newset.c_cflag = CS8 | CLOCAL | CREAD; + c->newset.c_iflag = IGNBRK | IGNPAR; + c->newset.c_oflag = 0; + c->newset.c_lflag = 0; + if (c->cfg->virt) { + c->newset.c_lflag |= ISIG; + c->newset.c_cc[VERASE] = 8; // set to BS, its DEL by default + } + +#ifdef __linux__ + c->newset.c_line = 0; +#endif + c->newset.c_cc[VMIN] = 1; + c->newset.c_cc[VTIME] = 0; + if (c->cfg->system_rtscts) + c->newset.c_cflag |= CRTSCTS; + + if(s2_printf) s_printf("SER%d: do_ser_init: running ser_termios\n", c->num); + do_termios(c); /* Set line settings now */ + tcsetattr(c->fd, TCSANOW, &c->newset); + + /* Pull down DTR and RTS. This is the most natural for most comm */ + /* devices including mice so that DTR rises during mouse init. */ + if (!c->cfg->pseudo) { + data = TIOCM_DTR | TIOCM_RTS; + if (ioctl(c->fd, TIOCMBIC, &data) && errno == EINVAL) { + s_printf("SER%d: TIOCMBIC unsupported, setting pseudo flag\n", c->num); + c->cfg->pseudo = 1; + } + } +} + +/* This function checks for newly received data and fills the UART + * FIFO (16550 mode) or receive register (16450 mode). + * + * Note: The receive buffer is now a sliding buffer instead of + * a queue. This has been found to be more efficient here. + * + * [num = port] + */ +static int tty_uart_fill(com_t *c) +{ + int size = 0; + + if (c->fd < 0) + return 0; + + /* Return if in loopback mode */ + if (c->MCR & UART_MCR_LOOP) + return 0; + + /* Is it time to do another read() of the serial device yet? + * The rx_timer is used to prevent system load caused by empty read()'s + * It also skip the following code block if the receive buffer + * contains enough data for a full FIFO (at least 16 bytes). + * The receive buffer is a sliding buffer. + */ + if (RX_BUF_BYTES(c->num) >= RX_BUFFER_SIZE) { + if(s3_printf) s_printf("SER%d: Too many bytes (%i) in buffer\n", c->num, + RX_BUF_BYTES(c->num)); + return 0; + } + + /* Slide the buffer contents to the bottom */ + rx_buffer_slide(c->num); + + /* Do a block read of data. + * Guaranteed minimum requested read size of (RX_BUFFER_SIZE - 16)! + */ + size = RPT_SYSCALL(read(c->fd, + &c->rx_buf[c->rx_buf_end], + RX_BUFFER_SIZE - c->rx_buf_end)); + ioselect_complete(c->fd); + if (size < 0) + return 0; + if (size == 0) { + c->is_closed = TRUE; + if(s3_printf) s_printf("SER%d: Got 0 bytes, setting is_closed\n", c->num); + return 0; + } + if(s3_printf) s_printf("SER%d: Got %i bytes, %i in buffer\n", c->num, + size, RX_BUF_BYTES(c->num)); + if (debug_level('s') >= 9) { + int i; + for (i = 0; i < size; i++) + s_printf("SER%d: Got data byte: %#x\n", c->num, + c->rx_buf[c->rx_buf_end + i]); + } + c->rx_buf_end += size; + return size; +} + +static void async_serial_run(int fd, void *arg) +{ + com_t *c = arg; + int size; + s_printf("SER%d: Async notification received\n", c->num); + size = tty_uart_fill(c); + if (size > 0) + receive_engine(c->num, size); +} + +static int ser_open_existing(com_t *c) +{ + struct stat st; + int err, io_sel = 0, oflags = O_NONBLOCK; + + err = stat(c->cfg->dev, &st); + if (err) { + error("SERIAL: stat(%s) failed: %s\n", c->cfg->dev, + strerror(errno)); + c->fd = -2; + return -1; + } + if (S_ISFIFO(st.st_mode)) { + s_printf("SER%i: %s is fifo, setting pseudo flag\n", c->num, + c->cfg->dev); + c->is_file = TRUE; + c->cfg->pseudo = TRUE; + /* force read-only to avoid SIGPIPE */ + c->cfg->ro = TRUE; + oflags |= O_RDONLY; + io_sel = 1; + } else { + if (S_ISREG(st.st_mode)) { + s_printf("SER%i: %s is file, setting pseudo flag\n", c->num, + c->cfg->dev); + c->is_file = TRUE; + c->cfg->pseudo = TRUE; + oflags |= O_RDONLY; + if (!c->cfg->ro && !c->cfg->wrfile) + c->wr_fd = RPT_SYSCALL(open(c->cfg->dev, O_WRONLY | O_APPEND)); + } else { + oflags |= O_RDWR; + io_sel = 1; + } + } + + c->fd = RPT_SYSCALL(open(c->cfg->dev, oflags)); + if (c->fd < 0) { + error("SERIAL: Unable to open device %s: %s\n", + c->cfg->dev, strerror(errno)); + return -1; + } + + if (!c->is_file && !isatty(c->fd)) { + s_printf("SERIAL: Serial port device %s is not a tty\n", + c->cfg->dev); + c->is_file = TRUE; + c->cfg->pseudo = TRUE; + } + + if (!c->is_file) { + RPT_SYSCALL(tcgetattr(c->fd, &c->oldset)); + RPT_SYSCALL(tcgetattr(c->fd, &c->newset)); +#if 0 + if (c->cfg->low_latency) { + struct serial_struct ser_info; + int err = ioctl(c->fd, TIOCGSERIAL, &ser_info); + if (err) { + error("SER%d: failure getting serial port settings, %s\n", + c->num, strerror(errno)); + } else { + ser_info.flags |= ASYNC_LOW_LATENCY; + err = ioctl(c->fd, TIOCSSERIAL, &ser_info); + if (err) + error("SER%d: failure setting low_latency flag, %s\n", + c->num, strerror(errno)); + else + s_printf("SER%d: low_latency flag set\n", c->num); + } + } +#endif + ser_set_params(c); + } + if (io_sel) + add_to_io_select(c->fd, async_serial_run, (void *)c); + return 0; +} + +static int pty_init(com_t *c) +{ + int pty_fd = posix_openpt(O_RDWR); + if (pty_fd == -1) + { + error("openpt failed %s\n", strerror(errno)); + return -1; + } + unlockpt(pty_fd); + fcntl(pty_fd, F_SETFL, O_NONBLOCK); + return pty_fd; +} + +static int tty_close(com_t *c); + +static void pty_exit(void *arg) +{ + com_t *c = arg; + error("pty process terminated\n"); + tty_close(c); +} + +static int pty_open(com_t *c, const char *cmd) +{ + struct termios t; + const char *argv[] = { "sh", "-c", cmd, NULL }; + const int argc = 4; + int pty_fd; + + pty_fd = pty_init(c); + pid_t pid = run_external_command("/bin/sh", argc, argv, 1, -1, pty_fd); + if (pid == -1) + return -1; + sigchld_register_handler(pid, pty_exit, c); + c->pty_pid = pid; + cfmakeraw(&t); + tcsetattr(pty_fd, TCSANOW, &t); + return pty_fd; +} + +static int pty_close(com_t *c, int fd) +{ + sigchld_enable_handler(c->pty_pid, 0); + return close(fd); +} + +/* This function opens ONE serial port for DOSEMU. Normally called only + * by do_ser_init below. [num = port, return = file descriptor] + */ +static int tty_open(com_t *c) +{ + int err; + + if (c->cfg->exec) { + c->fd = pty_open(c, c->cfg->exec); + if (c->fd == -1) + return -1; + c->cfg->pseudo = TRUE; + add_to_io_select(c->fd, async_serial_run, (void *)c); + return c->fd; + } + if (c->cfg->pts) { + c->fd = pty_init(c); + if (c->fd == -1) + return -1; + grantpt(c->fd); + err = symlink(ptsname(c->fd), c->cfg->pts); + if (err) { + error("symlink(%s, %s): %s", ptsname(c->fd), c->cfg->pts, + strerror(errno)); + close(c->fd); + c->fd = -2; + return -1; + } + c->cfg->pseudo = TRUE; + add_to_io_select(c->fd, async_serial_run, (void *)c); + return c->fd; + } + if (c->fd != -1) + return -1; + s_printf("SER%d: Running ser_open, %s, fd=%d\n", c->num, + c->cfg->dev, c->fd); + + if (c->fd != -1) + return (c->fd); + + if (c->cfg->virt) + { + /* don't try to remove any lock: they don't make sense for ttyname(0) */ + s_printf("Opening Virtual Port\n"); + c->dev_locked = FALSE; + } else if (config.tty_lockdir[0]) { + if (tty_lock(c->cfg->dev, 1) >= 0) { /* Lock port */ + /* We know that we have access to the serial port */ + /* If the port is used for a mouse, then don't lock, because + * the use of the mouse serial port can be switched between processes, + * such as on Linux virtual consoles. + */ + c->dev_locked = TRUE; + } else { + /* The port is in use by another process! Don't touch the port! */ + c->dev_locked = FALSE; + c->fd = -2; + return(-1); + } + } else { + s_printf("Warning: Port locking disabled in the config.\n"); + c->dev_locked = FALSE; + } + + err = access(c->cfg->dev, F_OK); + if (!err) { + err = ser_open_existing(c); + if (err) + goto fail_unlock; + } else { + c->fd = open(c->cfg->dev, O_WRONLY | O_CREAT | O_EXCL, 0640); + if (c->fd == -1) { + error("SER%i: unable to open or create %s\n", c->num, c->cfg->dev); + goto fail_unlock; + } + } + if (c->cfg->wrfile) { + c->wr_fd = open(c->cfg->wrfile, O_WRONLY | O_CREAT | O_TRUNC, 0640); + if (c->wr_fd == -1) { + error("SER%i: unable to open or create for write %s\n", c->num, c->cfg->dev); + goto fail_unlock; + } + } + + modstat_engine(c->num); + return c->fd; + +fail_unlock: + if (c->dev_locked && tty_lock(c->cfg->dev, 0) >= 0) /* Unlock port */ + c->dev_locked = FALSE; + + c->fd = -2; // disable permanently + return -1; +} + +/* This function closes ONE serial port for DOSEMU. Normally called + * only by do_ser_init below. [num = port, return = file error code] + */ +static int tty_close(com_t *c) +{ + int ret; + if (c->fd < 0) + return -1; + if (c->wr_fd != -1) { + close(c->wr_fd); + c->wr_fd = -1; + } + s_printf("SER%d: Running ser_close\n", c->num); + remove_from_io_select(c->fd); + if (c->cfg->exec) { + ret = pty_close(c, c->fd); + c->fd = -1; + return ret; + } + if (c->cfg->pts) { + unlink(c->cfg->pts); + close(c->fd); + c->fd = -1; + return 0; + } + + /* save current dosemu settings of the file and restore the old settings + * before closing the file down. + */ + if (!c->is_file) { + RPT_SYSCALL(tcgetattr(c->fd, &c->newset)); + RPT_SYSCALL(tcsetattr(c->fd, TCSANOW, &c->oldset)); + } + ret = RPT_SYSCALL(close(c->fd)); + c->fd = -1; + + /* Clear the lockfile from DOSEMU */ + if (c->dev_locked) { + if (tty_lock(c->cfg->dev, 0) >= 0) + c->dev_locked = FALSE; + } + return ret; +} + +static int tty_get_msr(com_t *c) +{ + int control, err; + err = ioctl(c->fd, TIOCMGET, &control); + if (err) + return 0; + return (((control & TIOCM_CTS) ? UART_MSR_CTS : 0) | + ((control & TIOCM_DSR) ? UART_MSR_DSR : 0) | + ((control & TIOCM_RNG) ? UART_MSR_RI : 0) | + ((control & TIOCM_CAR) ? UART_MSR_DCD : 0)); +} + + +struct serial_drv tty_drv = { + tty_rx_buffer_dump, + tty_tx_buffer_dump, + tty_get_tx_queued, + tty_termios, + tty_brkctl, + tty_write, + tty_dtr, + tty_rts, + tty_open, + tty_close, + tty_uart_fill, + tty_get_msr, + "tty_io" +}; diff --git a/src/base/serial/tty_io.h b/src/base/serial/tty_io.h new file mode 100644 index 0000000..b734911 --- /dev/null +++ b/src/base/serial/tty_io.h @@ -0,0 +1,6 @@ +#ifndef TTY_IO_H +#define TTY_IO_H + +extern struct serial_drv tty_drv; + +#endif diff --git a/src/base/sound/Makefile b/src/base/sound/Makefile new file mode 100644 index 0000000..7dcd764 --- /dev/null +++ b/src/base/sound/Makefile @@ -0,0 +1,17 @@ +# +# (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". +# +# for details see file COPYING.DOSEMU in the DOSEMU distribution +# + +top_builddir=../../.. +include $(top_builddir)/Makefile.conf + + +CFILES = midi.c sndpcm.c + +all: lib + +install: + +include $(REALTOPDIR)/src/Makefile.common diff --git a/src/base/sound/midi.c b/src/base/sound/midi.c new file mode 100644 index 0000000..2e43394 --- /dev/null +++ b/src/base/sound/midi.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2006 Stas Sergeev + * + * The below copyright strings have to be distributed unchanged together + * with this file. This prefix can not be modified or separated. + */ + +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include "emu.h" +#include "ringbuf.h" +#include "timers.h" +#include "utilities.h" +#include "sound/sound.h" +#include "sound.h" +#include "sound/midi.h" + +#define MAX_OUT_PLUGINS 15 +/* support only 1 input plugin for now to avoid the concurrent writes */ +#define MAX_IN_PLUGINS 1 +static struct pcm_holder out[ST_MAX][MAX_OUT_PLUGINS]; +static struct pcm_holder in[MAX_IN_PLUGINS]; +#define OUT_PLUGIN(i, j) ((const struct midi_out_plugin *)out[i][j].plugin) +#define IN_PLUGIN(i) ((const struct midi_in_plugin *)in[i].plugin) +static int out_registered[ST_MAX], in_registered; +static int out_enabled[ST_MAX]; +static struct rng_s midi_in; +#define MAX_DL_HANDLES 10 +static void *dl_handles[MAX_DL_HANDLES]; +static int num_dl_handles; +static enum SynthType synth_type; + +void midi_write(unsigned char val, enum SynthType type) +{ + int i; + enum SynthType stype = (type == ST_ANY ? synth_type : type); + /* if no plugin of requested type, then try to use anything */ + if (!out_enabled[stype] && out_enabled[synth_type]) + stype = synth_type; + for (i = 0; i < out_registered[stype]; i++) + if (out[stype][i].opened) + OUT_PLUGIN(stype, i)->write(val); + for (i = 0; i < out_registered[ST_ANY]; i++) + if (out[ST_ANY][i].opened) + OUT_PLUGIN(ST_ANY, i)->write(val); +// idle(0, 0, 0, "midi"); +} + +void midi_init(void) +{ + int i, j; +#ifdef USE_DL_PLUGINS +#define LOAD_PLUGIN(x) \ + dl_handles[num_dl_handles] = load_plugin(x); \ + if (dl_handles[num_dl_handles]) \ + num_dl_handles++ +#ifdef USE_FLUIDSYNTH + LOAD_PLUGIN("fluidsynth"); +#endif +#ifdef USE_MUNT + LOAD_PLUGIN("munt"); +#endif +#ifdef USE_ALSA + LOAD_PLUGIN("alsa"); +#endif +#endif + rng_init(&midi_in, 64, 1); + for (i = 0; i < ST_MAX; i++) { + pcm_init_plugins(out[i], out_registered[i]); + for (j = 0; j < out_registered[i]; j++) { + if (out[i][j].opened) + out_enabled[i]++; + } + } + pcm_init_plugins(in, in_registered); + + if (out_enabled[ST_GM]) + synth_type = ST_GM; + else if (out_enabled[ST_MT32]) + synth_type = ST_MT32; + if (!midi_set_synth_type_from_string(config.midi_synth)) + S_printf("MIDI: unsupported synth mode %s\n", config.midi_synth); +} + +void midi_done(void) +{ + int i; + midi_stop(); + for (i = 0; i < ST_MAX; i++) + pcm_deinit_plugins(out[i], out_registered[i]); + pcm_deinit_plugins(in, in_registered); + rng_destroy(&midi_in); + for (i = 0; i < num_dl_handles; i++) +#if 0 + close_plugin(dl_handles[i]); +#else + (void)dl_handles[i]; +#endif +} + +void midi_stop(void) +{ + int i, j; + for (i = 0; i < ST_MAX; i++) { + for (j = 0; j < out_registered[i]; j++) + if (OUT_PLUGIN(i, j)->stop && out[i][j].opened) + OUT_PLUGIN(i, j)->stop(out[i][j].arg); + } + for (i = 0; i < in_registered; i++) + if (IN_PLUGIN(i)->stop && in[i].opened) + IN_PLUGIN(i)->stop(in[i].arg); +} + +void midi_timer(void) +{ + int i, j; + for (i = 0; i < ST_MAX; i++) { + for (j = 0; j < out_registered[i]; j++) + if (OUT_PLUGIN(i, j)->run && out[i][j].opened) + OUT_PLUGIN(i, j)->run(); + } +} + +void midi_put_data(unsigned char *buf, size_t size) +{ + rng_add(&midi_in, size, buf); + + run_sb(); +} + +int midi_get_data_byte(unsigned char *buf) +{ + if (!rng_count(&midi_in)) + return 0; + return rng_get(&midi_in, buf); +} + +int midi_register_output_plugin(const struct midi_out_plugin *plugin) +{ + int index, st = plugin->stype; + if (out_registered[st] >= MAX_OUT_PLUGINS) { + error("Cannot register midi plugin %s\n", plugin->name); + return 0; + } + index = out_registered[st]++; + out[st][index].plugin = plugin; + out[st][index].opened = 0; + return 1; +} + +int midi_register_input_plugin(const struct midi_in_plugin *plugin) +{ + int index; + if (in_registered >= MAX_IN_PLUGINS) { + error("Cannot register midi plugin %s\n", plugin->name); + return 0; + } + index = in_registered++; + in[index].plugin = plugin; + in[index].opened = 0; + return 1; +} + +int midi_set_synth_type(enum SynthType st) +{ + if (st == ST_ANY || st >= ST_MAX || !out_enabled[st]) + return 0; + synth_type = st; + return 1; +} + +enum SynthType midi_get_synth_type(void) +{ + return synth_type; +} + +int midi_set_synth_type_from_string(const char *stype) +{ + if (strcmp(stype, "gm") == 0) + return midi_set_synth_type(ST_GM); + if (strcmp(stype, "mt32") == 0) + return midi_set_synth_type(ST_MT32); + midi_set_synth_type(ST_GM); + return 0; +} diff --git a/src/base/sound/sndpcm.c b/src/base/sound/sndpcm.c new file mode 100644 index 0000000..20bff71 --- /dev/null +++ b/src/base/sound/sndpcm.c @@ -0,0 +1,1458 @@ +/* + * Copyright (C) 2006 Stas Sergeev + * + * The below copyright strings have to be distributed unchanged together + * with this file. This prefix can not be modified or separated. + */ + +/* + * 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 2 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + * Purpose: Sound midlayer. Resampling, converting and all the other hard work. + * + * Author: Stas Sergeev. + * + * TODO: Add the PDM processing for PC-Speaker. + */ + +#include +#include +#include +#include +#include +#include +#include "emu.h" +#include "utilities.h" +#include "ringbuf.h" +#include "timers.h" +#include "sound/sound.h" + + +#define pcm_printf(...) do { \ + if (debug_level('S') >= 9) S_printf(__VA_ARGS__); \ +} while (0) +#define SND_BUFFER_SIZE 100000 /* enough to hold 1.1s of 44100/stereo */ +#define BUFFER_DELAY 40000.0 + +#define MIN_BUFFER_DELAY (BUFFER_DELAY) +#define READ_AREA_SIZE (BUFFER_DELAY * 9) // that large? +#define WRITE_AREA_SIZE MIN_BUFFER_DELAY +#define READ_AREA_START WRITE_AREA_SIZE +#define NORM_BUFFER_DELAY (READ_AREA_START + BUFFER_DELAY / 2) +#define INIT_BUFFER_DELAY (READ_AREA_START + BUFFER_DELAY * 1) +#define MAX_BUFFER_DELAY (READ_AREA_START + READ_AREA_SIZE) +#define MIN_GUARD_SIZE 1024 +#define MIN_READ_GUARD_PERIOD (1000000 * MIN_GUARD_SIZE / (2 * 44100)) +#define WR_BUFFER_LW (BUFFER_DELAY / 2) +#define MIN_READ_DELAY (MIN_BUFFER_DELAY + MIN_READ_GUARD_PERIOD) +#define WRITE_INIT_POS (WRITE_AREA_SIZE / 2) + +/* Layout of our buffer is as follows: + * + * | | + * WRITE_INIT_POS ->+ write area +<---------------------- <-WR_BUFFER_LW (GC) + * | | \ + * READ_AREA_START->+--------------+->MIN_BUFFER_DELAY | + * | | | + * + read area +->INIT_BUFFER_DELAY | + * | | | + * +--------------+->MAX_BUFFER_DELAY | + * | | | + * | GC area +----------------------> + * \_____________/ + */ + +#if 1 +/* this used to fix clicks in duke3d. */ +#define MAX_STREAM_STRETCH 200000.0 +#else +#define MAX_STREAM_STRETCH 0.0 +#endif + +enum { + SNDBUF_STATE_INACTIVE, + SNDBUF_STATE_PLAYING, + SNDBUF_STATE_FLUSHING, + SNDBUF_STATE_STALLED, +}; + +struct sample { + int format; + double tstamp; + unsigned char data[2]; +}; + +struct stream { + int channels; + struct rng_s buffer; + /* buf_cnt is a flat counter, never decrements. We have to use + * something really "long" for it, because "int" can overflow in + * about 6.7 hours of playing stereo sound at rate 44100. + * Surprisingly @runderwoo have actually hit such overflow when + * buf_cnt was "int". Lets use "long long". */ + long long buf_cnt; + int state; + int flags; + unsigned int stretch:1; + unsigned int prepared:1; + void *vol_arg; + double start_time; + double stop_time; + double stretch_per; + double stretch_tot; + /* for the raw channels heuristic */ + double raw_speed_adj; + double last_adj_time; + double adj_time_delay; + double last_fillup; + /* --- */ + const char *name; +}; + +#define MAX_STREAMS 10 +#define MAX_PLAYERS 10 +#define MAX_RECORDERS 10 +#define MAX_EFPS 5 +#define MAX_EFP_LINKS 5 + +struct efp_link { + int handle; + struct pcm_holder *efp; +}; + +struct pcm_player_wr { + double time; + long long last_cnt[MAX_STREAMS]; + int last_idx[MAX_STREAMS]; + double last_tstamp[MAX_STREAMS]; + struct efp_link efpl[MAX_EFP_LINKS]; + int num_efp_links; +}; + + +#define HPF_CTL 10 + +struct efp_wr { + enum EfpType type; +}; + +#define PLAYER(p) ((const struct pcm_player *)p->plugin) +#define PL_PRIV(p) ((struct pcm_player_wr *)p->priv) +#define PL_LNAME(p) (p->longname ?: p->name) +#define RECORDER(p) ((const struct pcm_recorder *)p->plugin) +#define EFPR(p) ((const struct pcm_efp *)p->plugin) +#define EF_PRIV(p) ((struct efp_wr *)p->priv) + +struct pcm_struct { + struct stream stream[MAX_STREAMS]; + int num_streams; + double (*get_volume)(int id, int chan_dst, int chan_src, void *); + int (*is_connected)(int id, void *arg); + int (*checkid2)(void *id2, void *arg); + pthread_mutex_t strm_mtx; + pthread_mutex_t time_mtx; + struct pcm_holder players[MAX_PLAYERS]; + int num_players; + int playing; + struct pcm_holder recorders[MAX_RECORDERS]; + int num_recorders; + struct pcm_holder efps[MAX_EFPS]; + int num_efps; + double time; +}; +static struct pcm_struct pcm; + +#define MAX_DL_HANDLES 10 +static void *dl_handles[MAX_DL_HANDLES]; +static int num_dl_handles; + +static double get_vol_dummy(int id, int chan_dst, int chan_src, void *arg) +{ + return (chan_src == chan_dst ? 1.0 : 0.0); +} + +static int is_connected_dummy(int id, void *arg) +{ + return 1; +} + +static int checkid2_dummy(void *id2, void *arg) +{ + return 1; +} + +#if defined(USE_LIBAO) || defined(SDL_SUPPORT) +static int pcm_get_cfg(const char *name) +{ + int i; + for (i = 0; i < pcm.num_players; i++) { + struct pcm_holder *p = &pcm.players[i]; + if (!strcmp(p->plugin->name, name)) + return (p->plugin->get_cfg ? p->plugin->get_cfg(p->arg) : 0); + } + return -1; +} +#endif + +int pcm_init(void) +{ +#ifdef USE_DL_PLUGINS + int ca = -1, cs = -1; +#endif + pcm_printf("PCM: init\n"); + pthread_mutex_init(&pcm.strm_mtx, NULL); + pthread_mutex_init(&pcm.time_mtx, NULL); + +#ifdef USE_DL_PLUGINS +#define LOAD_PLUGIN_C(x, c) do { \ + dl_handles[num_dl_handles] = load_plugin(x); \ + if (dl_handles[num_dl_handles]) { \ + num_dl_handles++; \ + c \ + } } while(0) +#define LOAD_PLUGIN(x) LOAD_PLUGIN_C(x,) +#ifdef USE_LIBAO + LOAD_PLUGIN_C("libao", { ca = pcm_get_cfg("ao"); } ); +#endif +#ifdef SDL_SUPPORT + if (ca == -1 || config.sdl || strstr(config.sound_driver, "sdl")) + LOAD_PLUGIN_C("sdl", { cs = pcm_get_cfg("sdl"); } ); +#endif + if (!ca && !cs) { // auto, config.sdl==1 + assert(config.sdl); + config.sdl_sound = 1; + } + else if (ca == PCM_CF_ENABLED) + config.libao_sound = 1; + else if (cs == PCM_CF_ENABLED) + config.sdl_sound = 1; + else if (cs != ca) { + if (!ca) + config.libao_sound = 1; + else + config.sdl_sound = 1; + } + +#ifdef USE_ALSA + LOAD_PLUGIN("alsa"); +#endif +#ifdef LADSPA_SUPPORT + LOAD_PLUGIN("ladspa"); +#endif +#endif + assert(num_dl_handles <= MAX_DL_HANDLES); + + pcm.get_volume = get_vol_dummy; + pcm.is_connected = is_connected_dummy; + pcm.checkid2 = checkid2_dummy; + + /* init efps before players because players init code refers to efps */ + if (!pcm_init_plugins(pcm.efps, pcm.num_efps)) + pcm_printf("no PCM effect processors initialized\n"); + if (!pcm_init_plugins(pcm.players, pcm.num_players)) + pcm_printf("ERROR: no PCM output plugins initialized\n"); + if (!pcm_init_plugins(pcm.recorders, pcm.num_recorders)) + pcm_printf("ERROR: no PCM input plugins initialized\n"); + return 1; +} + +static void pcm_clear_stream(int strm_idx) +{ + pcm.stream[strm_idx].buf_cnt += rng_count(&pcm.stream[strm_idx].buffer); + rng_clear(&pcm.stream[strm_idx].buffer); +} + +static void pcm_reset_stream(int strm_idx) +{ + pcm_clear_stream(strm_idx); + pcm.stream[strm_idx].state = SNDBUF_STATE_INACTIVE; + pcm.stream[strm_idx].stretch = 0; + pcm.stream[strm_idx].stretch_per = 0; + pcm.stream[strm_idx].stretch_tot = 0; + pcm.stream[strm_idx].prepared = 0; +} + +int pcm_allocate_stream(int channels, const char *name, void *vol_arg) +{ + int index; + if (pcm.num_streams >= MAX_STREAMS) { + error("PCM: stream pool exhausted, max=%i\n", MAX_STREAMS); + return -1; + } + pthread_mutex_lock(&pcm.strm_mtx); + index = pcm.num_streams++; + rng_init(&pcm.stream[index].buffer, SND_BUFFER_SIZE, + sizeof(struct sample)); + /* to keep timestamps contiguous, we disable overwrites */ + rng_allow_ovw(&pcm.stream[index].buffer, 0); + pcm.stream[index].channels = channels; + pcm.stream[index].name = name; + pcm.stream[index].buf_cnt = 0; + pcm.stream[index].vol_arg = vol_arg; + pcm_reset_stream(index); + pthread_mutex_unlock(&pcm.strm_mtx); + pcm_printf("PCM: Stream %i allocated for \"%s\"\n", index, name); + return index; +} + +void pcm_set_flag(int strm_idx, int flag) +{ + if (pcm.stream[strm_idx].flags & flag) + return; + pcm_printf("PCM: setting flag %x for stream %i (%s)\n", + flag, strm_idx, pcm.stream[strm_idx].name); + pcm.stream[strm_idx].flags |= flag; + if (pcm.stream[strm_idx].flags & PCM_FLAG_RAW) + pcm.stream[strm_idx].raw_speed_adj = 1.0; +} + +void pcm_clear_flag(int strm_idx, int flag) +{ + if (!(pcm.stream[strm_idx].flags & flag)) + return; + pcm_printf("PCM: clearing flag %x for stream %i (%s)\n", + flag, strm_idx, pcm.stream[strm_idx].name); + pcm.stream[strm_idx].flags &= ~flag; +} + +int pcm_format_size(int format) +{ + switch (format) { + case PCM_FORMAT_U8: + case PCM_FORMAT_S8: + return 1; + case PCM_FORMAT_U16_LE: + case PCM_FORMAT_S16_LE: + return 2; + default: + error("PCM: format %i is not supported\n", format); + return 0; + } +} + +static int pcm_get_format8(int is_signed) +{ + return is_signed ? PCM_FORMAT_S8 : PCM_FORMAT_U8; +} + +static int pcm_get_format16(int is_signed) +{ + return is_signed ? PCM_FORMAT_S16_LE : PCM_FORMAT_U16_LE; +} + +int pcm_get_format(int is_16, int is_signed) +{ + return is_16 ? pcm_get_format16(is_signed) : + pcm_get_format8(is_signed); +} + +static int cutoff(int val, int min, int max) +{ + if (val < min) + return min; + if (val > max) + return max; + return val; +} + +int pcm_samp_cutoff(int val, int format) +{ + switch (format) { + case PCM_FORMAT_U8: + return cutoff(val, 0, UCHAR_MAX); + case PCM_FORMAT_S8: + return cutoff(val, SCHAR_MIN, SCHAR_MAX); + case PCM_FORMAT_U16_LE: + return cutoff(val, 0, USHRT_MAX); + case PCM_FORMAT_S16_LE: + return cutoff(val, SHRT_MIN, SHRT_MAX); + default: + error("PCM: format %i is not supported\n", format); + return 0; + } +} + +#define UC2SS(v) ((*(unsigned char *)(v) - 128) * 256) +#define SC2SS(v) (*(signed char *)(v) * 256) +#define US2SS(v) (*(unsigned short *)(v) - 32768) +#define SS2UC(v) ((unsigned char)(((v) + 32768) / 256)) +#define SS2SC(v) ((signed char)((v) / 256)) +#define SS2US(v) ((unsigned short)((v) + 32768)) + +static short sample_to_S16(void *data, int format) +{ + switch (format) { + case PCM_FORMAT_U8: + return UC2SS(data); + case PCM_FORMAT_S8: + return SC2SS(data); + case PCM_FORMAT_U16_LE: + return US2SS(data); + case PCM_FORMAT_S16_LE: + return *(short *) data; + default: + error("PCM: format %i is not supported\n", format); + return 0; + } +} + +static void S16_to_sample(short sample, sndbuf_t *buf, int format) +{ + switch (format) { + case PCM_FORMAT_U8: + *buf = SS2UC(sample); + break; + case PCM_FORMAT_S8: + *buf = SS2SC(sample); + break; + case PCM_FORMAT_U16_LE: + *buf = SS2US(sample); + break; + case PCM_FORMAT_S16_LE: + *buf = sample; + break; + default: + error("PCM: format1 %i is not supported\n", format); + } +} + +static int count_active_streams(int id) +{ + int i, ret = 0; + for (i = 0; i < pcm.num_streams; i++) { + if (id != PCM_ID_ANY && !pcm.is_connected(id, pcm.stream[i].vol_arg)) + continue; + if (pcm.stream[i].state == SNDBUF_STATE_INACTIVE) + continue; + ret++; + } + return ret; +} + +double pcm_frame_period_us(int rate) +{ + return (1000000.0 / rate); +} + +double pcm_frag_period(int size, struct player_params *params) +{ + int samp_sz, nsamps; + samp_sz = pcm_format_size(params->format); + nsamps = size / samp_sz; + return nsamps * pcm_frame_period_us(params->rate) / params->channels; +} + +int pcm_frag_size(double period, struct player_params *params) +{ + int nframes = period / pcm_frame_period_us(params->rate); + int nsamps = nframes * params->channels; + return nsamps * pcm_format_size(params->format); +} + +static int peek_last_sample(int strm_idx, struct sample *samp) +{ + int idx = rng_count(&pcm.stream[strm_idx].buffer); + if (!idx) + return 0; + return rng_peek(&pcm.stream[strm_idx].buffer, idx - 1, samp); +} + +void pcm_prepare_stream(int strm_idx) +{ + long long now = GETusTIME(0); + struct stream *s; + + s = &pcm.stream[strm_idx]; + switch (s->state) { + + case SNDBUF_STATE_PLAYING: + error("PCM: prepare playing stream %s\n", s->name); + return; + + case SNDBUF_STATE_STALLED: + error("PCM: prepare stalled stream %s\n", s->name); + /* should never happen, but if we are here we reset stretches */ + pthread_mutex_lock(&pcm.strm_mtx); + pcm_reset_stream(strm_idx); + pthread_mutex_unlock(&pcm.strm_mtx); + break; + + case SNDBUF_STATE_FLUSHING: + /* very careful: because of poor syncing the stupid things happen. + * Like, for instance, samples written in the future... */ + if (now < s->stop_time) { + pcm_printf("PCM: ERROR: sample in the future, %f now=%llu, %s\n", + s->stop_time, now, s->name); + now = s->stop_time; + } + break; + } + + s->start_time = now; + s->prepared = 1; +} + +static void pcm_stream_stretch(int strm_idx) +{ + long long now = GETusTIME(0); + struct stream *s = &pcm.stream[strm_idx]; + assert(s->state != SNDBUF_STATE_PLAYING && + s->state != SNDBUF_STATE_INACTIVE); + s->start_time = now; + s->stretch = 1; +} + +static double calc_buffer_fillup(int strm_idx, double time) +{ + struct stream *s = &pcm.stream[strm_idx]; + if (s->state != SNDBUF_STATE_PLAYING && s->state != SNDBUF_STATE_FLUSHING) + return 0; + return s->stop_time > time ? s->stop_time - time : 0; +} + +static void start_player(struct pcm_holder *p) +{ + int i; + for (i = 0; i < PL_PRIV(p)->num_efp_links; i++) { + struct efp_link *l = &PL_PRIV(p)->efpl[i]; + EFPR(l->efp)->start(l->handle); + } + PLAYER(p)->start(p->arg); +} + +static void stop_player(struct pcm_holder *p) +{ + int i; + PLAYER(p)->stop(p->arg); + for (i = 0; i < PL_PRIV(p)->num_efp_links; i++) { + struct efp_link *l = &PL_PRIV(p)->efpl[i]; + EFPR(l->efp)->stop(l->handle); + } +} + +static void pcm_start_output(int id) +{ + int i; + long long now = GETusTIME(0); + for (i = 0; i < pcm.num_players; i++) { + struct pcm_holder *p = &pcm.players[i]; + if (!(PLAYER(p)->id & id)) + continue; + if (p->opened) { + pcm_reset_player(i); + pthread_mutex_unlock(&pcm.strm_mtx); + start_player(p); + pthread_mutex_lock(&pcm.strm_mtx); + } + } + pcm.time = now - MAX_BUFFER_DELAY; + pcm.playing |= id; + pcm_printf("PCM: output started, %i\n", pcm.playing); +} + +static void pcm_stop_output(int id) +{ + int i; + for (i = 0; i < pcm.num_players; i++) { + struct pcm_holder *p = &pcm.players[i]; + if (id != PCM_ID_ANY && !(PLAYER(p)->id & id)) + continue; + if (p->opened) { + pthread_mutex_unlock(&pcm.strm_mtx); + stop_player(p); + pthread_mutex_lock(&pcm.strm_mtx); + } + } + pcm.playing &= ~id; + pcm_printf("PCM: output stopped, %i\n", pcm.playing); +} + +static void handle_raw_adj(int strm_idx, double fillup, double time) +{ +#define ADJ_PERIOD 2000000 + double raw_delay = INIT_BUFFER_DELAY; + double delta = (fillup - raw_delay) / (raw_delay * 100); + double time_delta = time - pcm.stream[strm_idx].last_adj_time; + + if (pcm.stream[strm_idx].adj_time_delay - time_delta < 0) { + if (fillup == 0) { + delta *= 10; + if (pcm.stream[strm_idx].last_fillup == 0) + delta *= 10; + } + /* of course this heuristic doesnt work, but we have to try... */ + if ((fillup > raw_delay * 2 && + fillup >= pcm.stream[strm_idx].last_fillup) || + (fillup < raw_delay / 1.5 && + fillup <= pcm.stream[strm_idx].last_fillup)) { + pcm.stream[strm_idx].raw_speed_adj -= delta; + if (pcm.stream[strm_idx].raw_speed_adj > 5) + pcm.stream[strm_idx].raw_speed_adj = 5; + if (pcm.stream[strm_idx].raw_speed_adj < 0.2) + pcm.stream[strm_idx].raw_speed_adj = 0.2; +// error("speed %f d %f f %f\n", pcm.stream[strm_idx].raw_speed_adj, +// delta, fillup); + } + pcm.stream[strm_idx].last_fillup = fillup; + pcm.stream[strm_idx].last_adj_time = time; + if (pcm.stream[strm_idx].adj_time_delay < ADJ_PERIOD) + pcm.stream[strm_idx].adj_time_delay += ADJ_PERIOD / 4; + } +} + +static void pcm_handle_get(int strm_idx, double time) +{ + double stop_time = time - READ_AREA_START; + double fillup = calc_buffer_fillup(strm_idx, stop_time); + if (debug_level('S') >= 9) + pcm_printf("PCM: Buffer %i fillup=%f\n", strm_idx, fillup); + switch (pcm.stream[strm_idx].state) { + + case SNDBUF_STATE_INACTIVE: + error("PCM: getting data from inactive buffer (strm=%i)\n", + strm_idx); + break; + + case SNDBUF_STATE_PLAYING: + if (pcm.stream[strm_idx].flags & PCM_FLAG_RAW) + handle_raw_adj(strm_idx, fillup, stop_time); + if (rng_count(&pcm.stream[strm_idx].buffer) < + pcm.stream[strm_idx].channels * 2 && fillup == 0) { + pcm_printf("PCM: ERROR: buffer on stream %i exhausted (%s)\n", + strm_idx, pcm.stream[strm_idx].name); + /* ditch the last sample here, if it is the only remaining */ + pcm_clear_stream(strm_idx); + } + if (fillup == 0) { + if (!(pcm.stream[strm_idx].flags & PCM_FLAG_RAW)) + pcm_printf("PCM: ERROR: buffer on stream %i stalled (%s)\n", + strm_idx, pcm.stream[strm_idx].name); + pcm.stream[strm_idx].state = SNDBUF_STATE_STALLED; + } + if (pcm.stream[strm_idx].state == SNDBUF_STATE_PLAYING && + !(pcm.stream[strm_idx].flags & PCM_FLAG_POST) && + fillup < WR_BUFFER_LW) { + pcm_printf("PCM: buffer fillup %f is too low, %s %i %f\n", + fillup, pcm.stream[strm_idx].name, + rng_count(&pcm.stream[strm_idx].buffer), stop_time); + } + break; + + case SNDBUF_STATE_FLUSHING: + if (rng_count(&pcm.stream[strm_idx].buffer) < + pcm.stream[strm_idx].channels * 2 && fillup == 0) { + pcm_reset_stream(strm_idx); + pcm_printf("PCM: stream %s stopped\n", pcm.stream[strm_idx].name); + } else if (fillup == 0 && !pcm.stream[strm_idx].stretch) { + pcm_stream_stretch(strm_idx); + pcm_printf("PCM: stream %s passed wr margin\n", + pcm.stream[strm_idx].name); + } + break; + + case SNDBUF_STATE_STALLED: + if (pcm.stream[strm_idx].flags & PCM_FLAG_RAW) { + if (fillup == 0 && pcm.stream[strm_idx].last_fillup == 0 && + stop_time - pcm.stream[strm_idx].last_adj_time > ADJ_PERIOD) { + pcm_reset_stream(strm_idx); + pcm.stream[strm_idx].raw_speed_adj = 1; + } else { + handle_raw_adj(strm_idx, fillup, stop_time); + } + } + break; + } +} + +static void pcm_handle_write(int strm_idx, double time) +{ + if (pcm.playing && time < pcm.time + READ_AREA_SIZE) { + if (pcm.stream[strm_idx].state == SNDBUF_STATE_PLAYING) + error("PCM: timing screwed up\n"); + pcm_printf("PCM: timing screwed up, s=%s cur=%f pl=%f delta=%f\n", + pcm.stream[strm_idx].name, time, pcm.time, + pcm.time - time); + } + + switch (pcm.stream[strm_idx].state) { + case SNDBUF_STATE_STALLED: + pcm.stream[strm_idx].stretch_tot += pcm.stream[strm_idx].stretch_per; + pcm_printf("PCM: restarting stalled stream %s, str=%f strt=%f\n", + pcm.stream[strm_idx].name, pcm.stream[strm_idx].stretch_per, + pcm.stream[strm_idx].stretch_tot); + if (pcm.stream[strm_idx].stretch_per > MAX_STREAM_STRETCH) { + /* assume stream was down. Happens with build engine + * games like quake1, that rely on a cli-timeout feature, + * and as such, have a very large initial lag but then + * go to normal. */ + pcm_printf("PCM: ERROR: resetting total stretch time\n"); + pcm.stream[strm_idx].stretch_tot = 0; + } + pcm.stream[strm_idx].stretch_per = 0; + if (pcm.stream[strm_idx].stretch_tot > MAX_STREAM_STRETCH) { + pcm_printf("PCM: ERROR: limiting stretch time to %f\n", + MAX_STREAM_STRETCH); + pcm.stream[strm_idx].stretch_tot = MAX_STREAM_STRETCH; + } + break; + case SNDBUF_STATE_FLUSHING: + if (pcm.stream[strm_idx].stretch) + pcm_printf("PCM: restarting stream %s\n", pcm.stream[strm_idx].name); + else + pcm_printf("PCM: resuming stream %s\n", pcm.stream[strm_idx].name); + break; + } + + if (pcm.stream[strm_idx].state != SNDBUF_STATE_PLAYING) { + pcm.stream[strm_idx].state = SNDBUF_STATE_PLAYING; + pcm.stream[strm_idx].stretch = 0; + pcm.stream[strm_idx].prepared = 0; + } +} + +static void pcm_handle_flush(int strm_idx) +{ + switch (pcm.stream[strm_idx].state) { + + case SNDBUF_STATE_INACTIVE: + if (!(pcm.stream[strm_idx].flags & PCM_FLAG_RAW)) { + error("PCM: attempt to flush inactive buffer (%s)\n", + pcm.stream[strm_idx].name); + } + break; + + case SNDBUF_STATE_PLAYING: + pcm.stream[strm_idx].state = SNDBUF_STATE_FLUSHING; + break; + + case SNDBUF_STATE_STALLED: + pthread_mutex_lock(&pcm.strm_mtx); + pcm_reset_stream(strm_idx); + pthread_mutex_unlock(&pcm.strm_mtx); + break; + } +} + +int pcm_flush(int strm_idx) +{ + pcm_printf("PCM: flushing stream %i (%s)\n", strm_idx, + pcm.stream[strm_idx].name); + pcm_handle_flush(strm_idx); + return 1; +} + +static double get_stream_time(int strm_idx) +{ + long long now = GETusTIME(0); + double time = pcm.stream[strm_idx].start_time; + double delta = now - time; + switch (pcm.stream[strm_idx].state) { + + case SNDBUF_STATE_INACTIVE: + if (pcm.stream[strm_idx].prepared) { +user_tstamp: + if (delta > WRITE_AREA_SIZE) { + error("PCM: too large delta on stream %s\n", + pcm.stream[strm_idx].name); + pcm.stream[strm_idx].start_time = time = now - WRITE_INIT_POS; + } + return time; + } + pcm_printf("PCM: stream not prepared: %s\n", pcm.stream[strm_idx].name); + return now - WRITE_INIT_POS; + + case SNDBUF_STATE_STALLED: + pcm.stream[strm_idx].stretch_per = now - WRITE_AREA_SIZE - + pcm.stream[strm_idx].stop_time; + return now - WRITE_AREA_SIZE; + + case SNDBUF_STATE_FLUSHING: + if (pcm.stream[strm_idx].stretch) + return now - fmod(delta, MIN_BUFFER_DELAY); + if (pcm.stream[strm_idx].prepared && + !(pcm.stream[strm_idx].flags & PCM_FLAG_SLTS)) + goto user_tstamp; + /* in SLoppy TimeStamp mode ignore user's timestamp */ + /* no break */ + case SNDBUF_STATE_PLAYING: + return pcm.stream[strm_idx].stop_time; + + } + return 0; +} + +double pcm_get_stream_time(int strm_idx) +{ + double ret; + /* we allow user to write samples to the future to prevent + * subsequent underflows */ + pthread_mutex_lock(&pcm.time_mtx); + ret = get_stream_time(strm_idx) - pcm.stream[strm_idx].stretch_tot; + pthread_mutex_unlock(&pcm.time_mtx); + return ret; +} + +static double pcm_calc_tstamp(int strm_idx) +{ + double tstamp = get_stream_time(strm_idx); + if ((pcm.stream[strm_idx].flags & PCM_FLAG_RAW) && + pcm.stream[strm_idx].state == SNDBUF_STATE_STALLED) { + long long now = GETusTIME(0); + if (tstamp < now - WRITE_INIT_POS) + tstamp = now - WRITE_INIT_POS; + } + return tstamp; +} + +void pcm_write_interleaved(sndbuf_t ptr[][SNDBUF_CHANS], int frames, + int rate, int format, int nchans, int strm_idx) +{ + int i, j; + struct sample samp; + double frame_per; + struct stream *strm; + + strm = &pcm.stream[strm_idx]; + assert(nchans <= strm->channels); + if (strm->flags & PCM_FLAG_RAW) + rate /= strm->raw_speed_adj; + + samp.format = format; + samp.tstamp = 0; + frame_per = pcm_frame_period_us(rate); + pthread_mutex_lock(&pcm.strm_mtx); + for (i = 0; i < frames; i++) { + int l; + struct sample s2; +retry: + samp.tstamp = pcm_calc_tstamp(strm_idx); + l = peek_last_sample(strm_idx, &s2); + assert(!(l && samp.tstamp < s2.tstamp)); + for (j = 0; j < strm->channels; j++) { + int ch = j % nchans; + memcpy(samp.data, &ptr[i][ch], pcm_format_size(format)); + l = rng_put(&strm->buffer, &samp); + if (!l) { + if (!(strm->flags & PCM_FLAG_RAW)) { + error("Sound buffer %i overflowed (%s)\n", strm_idx, + strm->name); + pcm_reset_stream(strm_idx); + goto retry; + } else { + pcm_printf("Sound buffer %i overflowed (%s)\n", strm_idx, + strm->name); + strm->adj_time_delay = 0; + goto cont; + } + } + } + pcm_handle_write(strm_idx, samp.tstamp); + strm->stop_time = samp.tstamp + frame_per; + } + +cont: + for (i = 0; i < PCM_ID_MAX; i++) { + int id = 1 << i; + if (!pcm.is_connected(id, strm->vol_arg)) + continue; + if (!(id & pcm.playing)) + pcm_start_output(id); + } + pthread_mutex_unlock(&pcm.strm_mtx); +} + +static void pcm_remove_samples(double time) +{ + #define GUARD_SAMPS 1 + int i; + struct sample s; + for (i = 0; i < pcm.num_streams; i++) { + if (pcm.stream[i].state == SNDBUF_STATE_INACTIVE) + continue; + while (rng_count(&pcm.stream[i].buffer) >= pcm.stream[i].channels * + (GUARD_SAMPS + 1)) { + /* we leave GUARD_SAMPS samples below the timestamp untouched */ + rng_peek(&pcm.stream[i].buffer, pcm.stream[i].channels * + GUARD_SAMPS, &s); + if (s.tstamp > time) + break; + pcm.stream[i].buf_cnt += pcm.stream[i].channels; + rng_remove(&pcm.stream[i].buffer, pcm.stream[i].channels, NULL); + } + } +} + +static sndbuf_t pcm_interpolate(struct sample s1, struct sample s2, + double time) +{ + sndbuf_t v1 = sample_to_S16(s1.data, s1.format); + sndbuf_t v2 = sample_to_S16(s2.data, s2.format); + if (s2.tstamp <= s1.tstamp) + return v1; + /* simple linear interpolation for now */ + return (v1 + (time - s1.tstamp) * (v2 - v1) / (s2.tstamp - s1.tstamp)); +} + +static void pcm_get_samples(double time, + sndbuf_t samp[MAX_STREAMS][SNDBUF_CHANS], int *idxs, + int out_channels, int id) +{ + int i, j; + int started; + int have_prev; + struct sample s[SNDBUF_CHANS], prev_s[SNDBUF_CHANS]; + + for (i = 0; i < pcm.num_streams; i++) { + for (j = 0; j < SNDBUF_CHANS; j++) + samp[i][j] = 0; + if (pcm.stream[i].state == SNDBUF_STATE_INACTIVE || + !pcm.is_connected(id, pcm.stream[i].vol_arg)) + continue; + + have_prev = 0; + started = 0; + if (idxs[i] >= pcm.stream[i].channels) { + idxs[i] -= pcm.stream[i].channels; + have_prev = 1; + } + while (rng_count(&pcm.stream[i].buffer) - idxs[i] >= + pcm.stream[i].channels) { + for (j = 0; j < pcm.stream[i].channels; j++) + rng_peek(&pcm.stream[i].buffer, idxs[i] + j, &s[j]); + if (out_channels == 2 && pcm.stream[i].channels == 1) + s[1] = s[0]; + if (s[0].tstamp > time) { + if (!started) { + /* assert on idxs in sync with time */ + assert(!have_prev); + break; + } + for (j = 0; j < out_channels; j++) + samp[i][j] = pcm_interpolate(prev_s[j], s[j], time); + break; + } + memcpy(prev_s, s, sizeof(struct sample) * out_channels); + idxs[i] += pcm.stream[i].channels; + started = 1; + } + } +} + +static void pcm_mix_samples(sndbuf_t in[][SNDBUF_CHANS], + sndbuf_t out[SNDBUF_CHANS], int channels, int format, + double volume[][SNDBUF_CHANS][SNDBUF_CHANS]) +{ + int i, j, k; + int value[SNDBUF_CHANS] = { 0 }; + + for (j = 0; j < SNDBUF_CHANS; j++) { + for (i = 0; i < pcm.num_streams; i++) { + if (pcm.stream[i].state == SNDBUF_STATE_INACTIVE) + continue; + for (k = 0; k < SNDBUF_CHANS; k++) { + if (volume[i][j][k] == 0) + continue; + value[j] += in[i][k] * volume[i][j][k]; + } + } + } + for (i = channels; i < SNDBUF_CHANS; i++) + value[0] += value[i]; + for (i = 0; i < channels; i++) { + S16_to_sample(pcm_samp_cutoff(value[i], PCM_FORMAT_S16_LE), + &out[i], format); + } +} + +static void calc_idxs(struct pcm_player_wr *pl, int idxs[MAX_STREAMS]) +{ + int i; + for (i = 0; i < pcm.num_streams; i++) { + if (pcm.stream[i].state == SNDBUF_STATE_INACTIVE) + continue; + assert(pcm.stream[i].buf_cnt >= pl->last_cnt[i]); + if (pl->last_idx[i] > pcm.stream[i].buf_cnt - pl->last_cnt[i]) { + struct sample s; + idxs[i] = pl->last_idx[i] - (pcm.stream[i].buf_cnt - + pl->last_cnt[i]); + assert(idxs[i] <= rng_count(&pcm.stream[i].buffer)); + rng_peek(&pcm.stream[i].buffer, idxs[i] - 1, &s); + assert(pl->last_tstamp[i] == s.tstamp); + } else { + idxs[i] = 0; + } + } +} + +static void save_idxs(struct pcm_player_wr *pl, int idxs[MAX_STREAMS]) +{ + int i; + for (i = 0; i < pcm.num_streams; i++) { + if (pcm.stream[i].state == SNDBUF_STATE_INACTIVE) + continue; + assert(idxs[i] <= rng_count(&pcm.stream[i].buffer)); + if (idxs[i] > 0) { + struct sample s; + rng_peek(&pcm.stream[i].buffer, idxs[i] - 1, &s); + pl->last_tstamp[i] = s.tstamp; + } + pl->last_cnt[i] = pcm.stream[i].buf_cnt; + pl->last_idx[i] = idxs[i]; + } +} + +static void get_volumes(int id, double volume[][SNDBUF_CHANS][SNDBUF_CHANS]) +{ + int i, j, k; + for (i = 0; i < pcm.num_streams; i++) { + struct stream *strm = &pcm.stream[i]; + if (strm->state == SNDBUF_STATE_INACTIVE) + continue; + for (j = 0; j < SNDBUF_CHANS; j++) + for (k = 0; k < SNDBUF_CHANS; k++) + volume[i][j][k] = pcm.get_volume(id, j, k, strm->vol_arg); + } +} + +int pcm_data_get_interleaved(sndbuf_t buf[][SNDBUF_CHANS], int nframes, + struct player_params *params) +{ + int idxs[MAX_STREAMS], out_idx, handle, i; + long long now; + double start_time, stop_time, frame_period, frag_period, time; + sndbuf_t samp[MAX_STREAMS][SNDBUF_CHANS]; + double volume[MAX_STREAMS][SNDBUF_CHANS][SNDBUF_CHANS]; + struct pcm_holder *p; + + now = GETusTIME(0); + handle = params->handle; + p = &pcm.players[handle]; + start_time = PL_PRIV(p)->time; + frag_period = nframes * pcm_frame_period_us(params->rate); + stop_time = start_time + frag_period; + if (start_time < now - MAX_BUFFER_DELAY) { + error("PCM: \"%s\" too large delay, start=%f min=%f d=%f\n", + p->plugin->name, start_time, + now - MAX_BUFFER_DELAY, now - MAX_BUFFER_DELAY - start_time); + start_time = now - INIT_BUFFER_DELAY; + stop_time = start_time + frag_period; + } + if (start_time > now - MIN_READ_DELAY) { + pcm_printf("PCM: \"%s\" too small start delay, stop=%f max=%f d=%f\n", + p->plugin->name, stop_time, + now - MIN_BUFFER_DELAY, stop_time - + (now - MIN_BUFFER_DELAY)); + return 0; + } + if (stop_time > now - MIN_BUFFER_DELAY) { + size_t new_nf; + pcm_printf("PCM: \"%s\" too small stop delay, stop=%f max=%f d=%f\n", + p->plugin->name, stop_time, + now - MIN_BUFFER_DELAY, stop_time - + (now - MIN_BUFFER_DELAY)); + stop_time = now - MIN_BUFFER_DELAY; + frag_period = stop_time - start_time; + new_nf = frag_period / pcm_frame_period_us(params->rate); + assert(new_nf <= nframes); + nframes = new_nf; + } + pcm_printf("PCM: going to process %i samps for %s (st=%f stp=%f d=%f)\n", + nframes, p->plugin->name, start_time, + stop_time, now - start_time); + + pthread_mutex_lock(&pcm.strm_mtx); + if (!p->opened) { + pcm_printf("PCM: player %s already closed\n", + p->plugin->name); + pthread_mutex_unlock(&pcm.strm_mtx); + return 0; + } + frame_period = pcm_frame_period_us(params->rate); + time = start_time; + calc_idxs(PL_PRIV(p), idxs); + get_volumes(PLAYER(p)->id, volume); + for (out_idx = 0; out_idx < nframes; out_idx++) { + pcm_get_samples(time, samp, idxs, params->channels, PLAYER(p)->id); + pcm_mix_samples(samp, buf[out_idx], params->channels, params->format, + volume); + time += frame_period; + } + if (fabs(time - stop_time) > frame_period) + error("PCM: time=%f stop_time=%f p=%f\n", + time, stop_time, frame_period); + PL_PRIV(p)->time = stop_time; + save_idxs(PL_PRIV(p), idxs); + pthread_mutex_unlock(&pcm.strm_mtx); + + for (i = 0; i < PL_PRIV(p)->num_efp_links; i++) { + struct efp_link *l = &PL_PRIV(p)->efpl[i]; + EFPR(l->efp)->process(l->handle, buf, nframes, + params->channels, params->format, params->rate); + } + + if (out_idx != nframes) + error("PCM: requested=%i prepared=%i\n", nframes, out_idx); + + return out_idx; +} + +size_t pcm_data_get(void *data, size_t size, + struct player_params *params) +{ + int i, j; + sndbuf_t buf[size][SNDBUF_CHANS]; + int ss = pcm_format_size(params->format); + int fsz = params->channels * ss; + int nframes = size / fsz; + + nframes = pcm_data_get_interleaved(buf, nframes, params); + for (i = 0; i < nframes; i++) { + for (j = 0; j < params->channels; j++) + memcpy(data + (i * fsz + j * ss), &buf[i][j], ss); + } + return nframes * fsz; +} + +static void pcm_advance_time(double time) +{ + int i; + double start_time = time - MAX_BUFFER_DELAY; + pthread_mutex_lock(&pcm.strm_mtx); + pcm.time = start_time; + /* remove processed samples from input buffers (last sample stays) */ + pcm_remove_samples(start_time); + for (i = 0; i < pcm.num_streams; i++) { + if (pcm.stream[i].state == SNDBUF_STATE_INACTIVE) + continue; + if (debug_level('S') >= 9) + pcm_printf("PCM: stream %i fillup2: %i\n", i, + rng_count(&pcm.stream[i].buffer)); + pcm_handle_get(i, time); + } + + for (i = 0; i < PCM_ID_MAX; i++) { + if (!count_active_streams(1 << i) && (pcm.playing & (1 << i))) + pcm_stop_output(1 << i); + } + pthread_mutex_unlock(&pcm.strm_mtx); +} + +int pcm_register_player(const struct pcm_player *player, void *arg) +{ + struct pcm_holder *p; + pcm_printf("PCM: registering player: %s\n", PL_LNAME(player)); + if (pcm.num_players >= MAX_PLAYERS) { + error("PCM: attempt to register more than %i player\n", MAX_PLAYERS); + return 0; + } + p = &pcm.players[pcm.num_players]; + p->plugin = player; + p->arg = arg; + p->priv = malloc(sizeof(struct pcm_player_wr)); + memset(p->priv, 0, sizeof(struct pcm_player_wr)); + return pcm.num_players++; +} + +int pcm_register_recorder(const struct pcm_recorder *recorder, void *arg) +{ + struct pcm_holder *p; + pcm_printf("PCM: registering recorder: %s\n", PL_LNAME(recorder)); + if (pcm.num_recorders >= MAX_RECORDERS) { + error("PCM: attempt to register more than %i recorder\n", MAX_RECORDERS); + return 0; + } + p = &pcm.recorders[pcm.num_recorders]; + p->plugin = recorder; + p->arg = arg; + return pcm.num_recorders++; +} + +int pcm_register_efp(const struct pcm_efp *efp, enum EfpType type, void *arg) +{ + struct pcm_holder *p; + pcm_printf("PCM: registering efp: %s\n", PL_LNAME(efp)); + if (pcm.num_efps >= MAX_EFPS) { + error("PCM: attempt to register more than %i efps\n", MAX_EFPS); + return 0; + } + p = &pcm.efps[pcm.num_efps]; + p->plugin = efp; + p->arg = arg; + p->priv = malloc(sizeof(struct efp_wr)); + memset(p->priv, 0, sizeof(struct efp_wr)); + EF_PRIV(p)->type = type; + return pcm.num_efps++; +} + +void pcm_reset_player(int handle) +{ + long long now = GETusTIME(0); + struct pcm_holder *p = &pcm.players[handle]; + struct pcm_player_wr *pl = PL_PRIV(p); + pl->time = now - INIT_BUFFER_DELAY; + memset(pl->last_idx, 0, sizeof(pl->last_idx)); + memset(pl->last_cnt, 0, sizeof(pl->last_cnt)); +} + +void pcm_timer(void) +{ + int i; + long long now = GETusTIME(0); + for (i = 0; i < pcm.num_players; i++) { + struct pcm_holder *p = &pcm.players[i]; + struct pcm_player_wr *pl = PL_PRIV(p); + if (p->opened && PLAYER(p)->timer) { + double delta = now - NORM_BUFFER_DELAY - pl->time; + PLAYER(p)->timer(delta, p->arg); + } + } + pthread_mutex_lock(&pcm.time_mtx); + pcm_advance_time(now); + pthread_mutex_unlock(&pcm.time_mtx); +} + +void pcm_done(void) +{ + int i; + for (i = 0; i < pcm.num_streams; i++) { + if (pcm.stream[i].state == SNDBUF_STATE_PLAYING || + pcm.stream[i].state == SNDBUF_STATE_STALLED) + pcm_flush(i); + } + pthread_mutex_lock(&pcm.strm_mtx); + if (pcm.playing) + pcm_stop_output(PCM_ID_ANY); + pthread_mutex_unlock(&pcm.strm_mtx); + + pcm_deinit_plugins(pcm.recorders, pcm.num_recorders); + pcm_deinit_plugins(pcm.players, pcm.num_players); + pcm_deinit_plugins(pcm.efps, pcm.num_efps); + + for (i = 0; i < pcm.num_streams; i++) + rng_destroy(&pcm.stream[i].buffer); + pthread_mutex_destroy(&pcm.strm_mtx); + pthread_mutex_destroy(&pcm.time_mtx); + + for (i = 0; i < num_dl_handles; i++) +#if 0 + close_plugin(dl_handles[i]); +#else + (void)dl_handles[i]; +#endif + for (i = 0; i < pcm.num_players; i++) + free(pcm.players[i].priv); + for (i = 0; i < pcm.num_recorders; i++) + free(pcm.recorders[i].priv); + for (i = 0; i < pcm.num_efps; i++) + free(pcm.efps[i].priv); +} + +int pcm_init_plugins(struct pcm_holder *plu, int num) +{ +#define SAFE_OPEN(p) (p->plugin->open ? p->plugin->open(p->arg) : 1) + int i, sel, max_w, max_i, cnt; + cnt = 0; + sel = 0; + /* first deal with enabled plugins */ + for (i = 0; i < num; i++) { + struct pcm_holder *p = &plu[i]; + p->cfg_flags = (p->plugin->get_cfg ? p->plugin->get_cfg(p->arg) : 0); + if (p->cfg_flags & PCM_CF_ENABLED) { + p->opened = SAFE_OPEN(p); + pcm_printf("PCM: Initializing selected plugin: %s: %s\n", + PL_LNAME(p->plugin), p->opened ? "OK" : "Failed"); + if (p->opened) { + cnt++; + if (!(p->plugin->flags & PCM_F_PASSTHRU)) + sel++; + } else { + p->failed = 1; + } + } + } + /* then deal with pass-thru plugins */ + for (i = 0; i < num; i++) { + struct pcm_holder *p = &plu[i]; + if (p->opened || p->failed || + (p->plugin->flags & PCM_F_EXPLICIT) || + (p->cfg_flags & PCM_CF_DISABLED) || + !(p->plugin->flags & PCM_F_PASSTHRU)) + continue; + p->opened = SAFE_OPEN(p); + pcm_printf("PCM: Initializing pass-through plugin: %s: %s\n", + PL_LNAME(p->plugin), p->opened ? "OK" : "Failed"); + if (!p->opened) + p->failed = 1; + else + cnt++; + } + /* lastly deal with weight */ + if (!sel) do { + max_w = -1; + max_i = -1; + for (i = 0; i < num; i++) { + struct pcm_holder *p = &plu[i]; + if (p->opened || p->failed || + (p->plugin->flags & PCM_F_EXPLICIT) || + (p->cfg_flags & PCM_CF_DISABLED) || + (p->plugin->flags & PCM_F_PASSTHRU)) + continue; + if (p->plugin->weight > max_w) { + if (max_i != -1) + pcm_printf("PCM: Bypassing plugin: %s: (%i < %i)\n", + PL_LNAME(plu[max_i].plugin), max_w, + p->plugin->weight); + max_w = p->plugin->weight; + max_i = i; + } + } + if (max_i != -1) { + struct pcm_holder *p = &plu[max_i]; + p->opened = SAFE_OPEN(p); + pcm_printf("PCM: Initializing plugin: %s (w=%i): %s\n", + PL_LNAME(p->plugin), max_w, p->opened ? "OK" : "Failed"); + if (!p->opened) + p->failed = 1; + else + cnt++; + } + } while (max_i != -1 && !plu[max_i].opened); + return cnt; +} + +void pcm_deinit_plugins(struct pcm_holder *plu, int num) +{ + int i; + for (i = 0; i < num; i++) { + struct pcm_holder *p = &plu[i]; + if (p->opened) { + if (p->plugin->close) + p->plugin->close(p->arg); + p->opened = 0; + } + } +} + +int pcm_start_input(void *arg) +{ + int i, ret = 0; + for (i = 0; i < pcm.num_recorders; i++) { + struct pcm_holder *p = &pcm.recorders[i]; + if (p->opened && RECORDER(p)->start && + pcm.checkid2(RECORDER(p)->id2, arg)) { + RECORDER(p)->start(p->arg); + ret++; + } + } + pcm_printf("PCM: input started, %i\n", ret); + return ret; +} + +void pcm_stop_input(void *arg) +{ + int i; + for (i = 0; i < pcm.num_recorders; i++) { + struct pcm_holder *p = &pcm.recorders[i]; + if (p->opened && RECORDER(p)->stop && + pcm.checkid2(RECORDER(p)->id2, arg)) + RECORDER(p)->stop(p->arg); + } + pcm_printf("PCM: input stopped\n"); +} + +void pcm_set_volume_cb(double (*get_vol)(int, int, int, void *)) +{ + pcm.get_volume = get_vol; +} + +void pcm_set_connected_cb(int (*is_connected)(int, void *)) +{ + pcm.is_connected = is_connected; +} + +void pcm_set_checkid2_cb(int (*checkid2)(void *, void *)) +{ + pcm.checkid2 = checkid2; +} + +int pcm_setup_efp(int handle, enum EfpType type, int param1, int param2, + float param3) +{ + struct pcm_holder *p; + int i; + + p = &pcm.players[handle]; + for (i = 0; i < pcm.num_efps; i++) { + struct pcm_holder *e = &pcm.efps[i]; + if (e->opened && EF_PRIV(e)->type == type) { + struct efp_link *l = + &PL_PRIV(p)->efpl[PL_PRIV(p)->num_efp_links++]; + assert(PL_PRIV(p)->num_efp_links <= MAX_EFP_LINKS); + l->handle = EFPR(e)->setup(param1, param2, param3, e->arg); + l->efp = e; + pcm_printf("PCM: connected efp \"%s\" to player \"%s\"\n", + e->plugin->name, p->plugin->name); + return 1; + } + } + return 0; +} + +int pcm_setup_hpf(struct player_params *params) +{ + return pcm_setup_efp(params->handle, EFP_HPF, params->rate, + params->channels, HPF_CTL); +} + +int pcm_parse_cfg(const char *string, const char *name) +{ + char *p; + int l; + char *on = pcm_parse_params(config.snd_plugin_params, name, "enabled"); + int off = (on && on[0] == '0'); + free(on); + if (off) { + pcm_printf("PCM: %s driver disabled in the config\n", name); + return PCM_CF_DISABLED; + } + l = strlen(name); + p = strstr(string, name); + if (p && (p == string || p[-1] == ',') && (p[l] == 0 || p[l] == ',')) { + pcm_printf("PCM: Enabling %s driver\n", name); + return PCM_CF_ENABLED; + } + return 0; +} + +char *pcm_parse_params(const char *string, const char *name, const char *param) +{ + char *p, *buf; + int l; + l = asprintf(&buf, "%s:%s=", name, param); + assert(l > 0); + p = strstr(string, buf); + free(buf); + if (p && (p == string || p[-1] == ' ')) { + char *val = strdup(p + l); + char *c = strchr(val, ' '); + if (c) + *c = 0; + pcm_printf("PCM: Param \"%s\" for driver \"%s\": %s\n", param, name, val); + return val; + } + return NULL; +} diff --git a/src/base/speaker/Makefile b/src/base/speaker/Makefile new file mode 100644 index 0000000..de0dcef --- /dev/null +++ b/src/base/speaker/Makefile @@ -0,0 +1,18 @@ +top_builddir=../../.. +include $(top_builddir)/Makefile.conf + +# Makefile for speaker code. + +CFILES=speaker.c $(X_CFILES) console_speaker.c + +SFILES= +ALL=$(CFILES) $(SFILES) + +OBJS=$(CFILES:.c=.o) +DEPENDS=$(CFILES:.c=.d) + +include $(REALTOPDIR)/src/Makefile.common + +all: lib + +install: all diff --git a/src/base/speaker/console_speaker.c b/src/base/speaker/console_speaker.c new file mode 100644 index 0000000..486ae95 --- /dev/null +++ b/src/base/speaker/console_speaker.c @@ -0,0 +1,26 @@ +#include "speaker.h" +/* + * Console Speaker Emulation + * ============================================================================= + */ + +#include +#ifdef __linux__ +#include "Sys/kd.h" +#endif + + +void console_speaker_on(void *gp, unsigned ms, unsigned short period) +{ +#ifdef __linux__ + ioctl((int)(uintptr_t)gp, KDMKTONE, + (unsigned) ((ms & 0xffff) << 16) | (period & 0xffff)); +#endif +} + +void console_speaker_off(void *gp) +{ +#ifdef __linux__ + ioctl((int)(uintptr_t)gp, KDMKTONE, 0); +#endif +} diff --git a/src/base/speaker/speaker.c b/src/base/speaker/speaker.c new file mode 100644 index 0000000..a684f49 --- /dev/null +++ b/src/base/speaker/speaker.c @@ -0,0 +1,186 @@ +/* + * Speaker emulation code, file speaker.c + * + * (C) 1997 under GPL or LGPL, Eric Biederman + * + * DANG_BEGIN_MODULE + * + * REMARK + * + * The pc-speaker emulator for Dosemu. + * + * This file contains functions to make a pc speaker beep for dosemu. + * + * Actuall emulation is done in src/base/dev/misc/timers.c in do_sound. + * + * Currently emulation is only done when the new keyboard is enabled but with a + * little extra work it should be possible to enable it for the old keyboard + * code if necessary. + * + * For parts of dosemu that want to beep the pc-speaker (say the video bios) + * #include "speaker.h" + * Use 'speaker_on(ms, period)' + * to turn the pc-speaker on for 'ms' milliseconds with period 'period'. + * The function returns immediately. + * + * Use 'speaker_off()' + * To turn the pc-speaker definentily off. This is mostly useful when exiting + * the program to ensure you aren't killy someones ears :) + * + * 'speaker_on' always overrides whatever previous speaker sound was previously + * given. No mixing happens. + * + * For code that wants to implement speaker emulation. The recommended method + * is to add a file in src/base/speaker with the necessary code. Declare it's + * methods in speaker.h (or somewhere accessible to your code). And call + * + * 'register_speaker(gp, on, off)' + * when your speaker code is ready to function. + * + * gp may be any void pointer value. + * + * gp is passed as the first argument to the functions arguments 'on' + * and 'off' when global functions 'speaker_on' and 'speaker_off' are called. + * This allows important state information to be passed to the functions. And + * reduces reliance on global variables. + * + * The functions 'on' and 'off' besides the extra parameter are called just as + * the global functions 'speaker_on' and 'speaker_off' are called respectively. + * + * Before the registered function is no longer valid call + * 'register_speaker(NULL, NULL, NULL)' this will reset the speaker code + * to it's default speaker functions, which will always work. + * + * --EB 20 Sept 1997 + * + * /REMARK + * maintainer: + * Eric W. Biederman + * DANG_END_MODULE + * + * Changes: Hans 970926 (at time of patch inclusion) + * - Reduced type/prototyping usage to DOSEMU common one ;-) + * + */ + +#include "emu.h" +#include "speaker.h" +#include "port.h" +#include "iodev.h" +#include "timers.h" + +/* + * Speaker info structure. + * ============================================================================ + */ +struct speaker_info { + void *gp; /* a general pointer it can hold anything, + * it is passed to both speaker_on & speaker_off + */ + speaker_on_t on; + speaker_off_t off; +}; + +/* flag to avoid turning the speaker off if it is already off */ +static int speaker_is_on; + +/* + * Generic speaker emulation + * ============================================================================= + */ +#include /* for putchar */ +#include + +static void dumb_speaker_on(void * gp, unsigned ms, unsigned short period) +{ + FILE *out = (config.tty_stderr ? stderr : stdout); + putc('\007', out); + if (!config.tty_stderr) + fflush(stdout); +} +static void dumb_speaker_off(void *gp) +{ + /* we can't :( */ +} + +static struct speaker_info dumb_speaker = +{ NULL, dumb_speaker_on, dumb_speaker_off }; + +/* + * Speaker Emulation Control + * ============================================================================= + */ + + +static struct speaker_info speaker = +{ NULL, dumb_speaker_on, dumb_speaker_off}; + +void register_speaker(void *gp, + speaker_on_t speaker_on, + speaker_off_t speaker_off) +{ + if (speaker_on && speaker_off) { + speaker.gp = gp; + speaker.on = speaker_on; + speaker.off = speaker_off; + } else { + speaker = dumb_speaker; + } +} + +/* this does the EMULATED mode speaker emulation */ +void speaker_on(unsigned ms, unsigned short period) +{ + if (config.speaker == SPKR_OFF) + return; + i_printf("SPEAKER: on, period=%d\n", period); + speaker_is_on = 1; + if (!speaker.on) { + speaker = dumb_speaker; + } + speaker.on(speaker.gp, ms, period); +} + +void speaker_off(void) +{ + if (!speaker_is_on) + return; + i_printf("SPEAKER: sound OFF!\n"); + if (!speaker.off) { + speaker = dumb_speaker; + } + speaker.off(speaker.gp); + speaker_is_on = 0; +} + +static int saved_port_val; +void speaker_pause (void) +{ + switch (config.speaker) + { + case SPKR_NATIVE: + saved_port_val = port_inb (0x61); + std_port_outb (0x61, saved_port_val & 0xFC); /* clear timer & speaker bits */ + break; + case SPKR_EMULATED: + speaker_off (); + break; + case SPKR_OFF: + break; + } +} + +void speaker_resume (void) +{ + switch (config.speaker) + { + case SPKR_NATIVE: + std_port_outb (0x61, saved_port_val); /* restore timer & speaker bits */ + break; + case SPKR_EMULATED: +// do_sound(pit[2].write_latch & 0xffff); + break; + case SPKR_OFF: + break; + } +} diff --git a/src/base/video/Makefile b/src/base/video/Makefile new file mode 100644 index 0000000..68b5d78 --- /dev/null +++ b/src/base/video/Makefile @@ -0,0 +1,12 @@ +top_builddir=../../.. +include $(top_builddir)/Makefile.conf + +# +# This is the Makefile for the video-subdirectory of the DOS-emulator +# for Linux. + +CFILES = text.c render.c video.c instremu.c remap.c + +all: lib + +include $(SRCPATH)/Makefile.common diff --git a/src/base/video/instremu.c b/src/base/video/instremu.c new file mode 100644 index 0000000..f086be5 --- /dev/null +++ b/src/base/video/instremu.c @@ -0,0 +1,923 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * DANG_BEGIN_MODULE + * + * REMARK + * Emulates most machine code instructions for VGAEmu. + * Some ideas were taken from Bochs (see www.bochs.com). + * + * The emulator stays in emulation until either we have a pending + * signal, or if COUNT instructions do not access the VGA memory, or + * the instruction is not known. In future this may be merged with + * cpuemu. + * + * Note that this emulation short-circuits "IN" and "OUT" port reads and + * and writes, but only if these refer to the standard vga ports, + * that is, if VGA_emulate_outb() and VGA_emulate_inb() can handle + * them. + * + * The emulator has some sort of a RISC structure: for each read or write + * it is checked whether it refers to video memory, and if that is the case + * we call Logical_VGA_read() or Logical_VGA_write(), respectively. + * These functions are in vgaemu.c. + * + * /REMARK + * DANG_END_MODULE + * + * DANG_BEGIN_CHANGELOG + * + * 2000/05/18: Moved instr_sim and friends from vgaemu.c (Bart) + * + * Updated to remove MASTERCOUNT option and get out of the emulator + * if we have a signal pending. + * --EB 27 May 2000 + * + * 2000/06/01: Over the last weeks: added many 32-bit instructions, + * improved speed and cleaned up the source. (Bart) + * + * DANG_END_CHANGELOG + * + */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include "emu.h" +#include "emudpmi.h" +#include "cpu.h" +#include "dos2linux.h" +#include "instremu.h" + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * some configurable options + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Debug level for the Graphics Controller. + * 0 - normal / 1 - useful / 2 - too much + */ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#define DEBUG_INSTR 0 /* (<= 2) instruction emulation */ + +#define R_LO(a) LO_BYTE_d(a) +#define R_HI(a) HI_BYTE_d(a) +#define R_WORD(a) LO_WORD(a) +#define R_DWORD(a) (*((unsigned *) &(a))) +#define AL (R_LO(x86->eax)) +#define AH (R_HI(x86->eax)) +#define AX (R_WORD(x86->eax)) +#define BL (R_LO(x86->ebx)) +#define BH (R_HI(x86->ebx)) +#define BX (R_WORD(x86->ebx)) +#define CL (R_LO(x86->ecx)) +#define CH (R_HI(x86->ecx)) +#define CX (R_WORD(x86->ecx)) +#define DL (R_LO(x86->edx)) +#define DH (R_HI(x86->edx)) +#define DX (R_WORD(x86->edx)) +#define SI (R_WORD(x86->esi)) +#define DI (R_WORD(x86->edi)) +#define SP (R_WORD(x86->esp)) +#define BP (R_WORD(x86->ebp)) +#define EFLAGS (R_DWORD(x86->eflags)) +#define FLAGS (R_WORD(EFLAGS)) + +#define instr_msg(x...) v_printf("instremu: " x) + +#define instr_deb(x...) v_printf("instremu: " x) + +#if DEBUG_INSTR >= 2 +#define instr_deb2(x...) v_printf("instremu: " x) +#else +#define instr_deb2(x...) +#endif + +enum {REPNZ = 0, REPZ = 1, REP_NONE = 2}; + +struct rm { + unsigned char *r; + dosaddr_t m; +}; + +typedef struct x86_regs { + unsigned eax, ecx, edx, ebx, esp, ebp, esi, edi; + /* this sequence is important because this is the cpu's order and thus + gives us an optimization */ + unsigned eip; + unsigned eflags; + unsigned es, cs, ss, ds, fs, gs; + unsigned cs_base, ds_base, es_base, ss_base, fs_base, gs_base; + unsigned seg_base, seg_ss_base; + unsigned _32bit:1; /* 16/32 bit code */ + unsigned address_size; /* in bytes so either 4 or 2 */ + unsigned operand_size; + unsigned prefixes, rep; + unsigned (*instr_read)(struct rm rm); + void (*instr_write)(struct rm rm, unsigned u); + struct rm (*modrm)(unsigned char *cp, struct x86_regs *x86, int *inst_len); +} x86_regs; + +#if DEBUG_INSTR >= 1 +static char *seg_txt[7] = { "", "es: ", "cs: ", "ss: ", "ds: ", "fs: ", "gs: " }; +static char *rep_txt[3] = { "", "repnz ", "repz " }; +static char *lock_txt[2] = { "", "lock " }; +#endif + +static unsigned wordmask[5] = {0,0xff,0xffff,0xffffff,0xffffffff}; + +static unsigned char it[0x100] = { + 7, 7, 7, 7, 2, 3, 1, 1, 7, 7, 7, 7, 2, 3, 1, 0, + 7, 7, 7, 7, 2, 3, 1, 1, 7, 7, 7, 7, 2, 3, 1, 1, + 7, 7, 7, 7, 2, 3, 0, 1, 7, 7, 7, 7, 2, 3, 0, 1, + 7, 7, 7, 7, 2, 3, 0, 1, 7, 7, 7, 7, 2, 3, 0, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 7, 7, 0, 0, 0, 0, 3, 9, 2, 8, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + + 8, 9, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 1, 1, 1, + 4, 4, 4, 4, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + + 8, 8, 3, 1, 7, 7, 8, 9, 5, 1, 3, 1, 1, 2, 1, 1, + 7, 7, 7, 7, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 6, 2, 1, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 7, 7, 1, 1, 1, 1, 1, 1, 7, 7 +}; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +static unsigned seg, lock, rep; +#define vga_base vga.mem.bank_base +#define vga_end (vga_base + vga.mem.bank_len) + +static unsigned arg_len(unsigned char *, int); + +static unsigned instr_read_word(struct rm rm); +static unsigned instr_read_dword(struct rm rm); +static void instr_write_word(struct rm rm, unsigned u); +static void instr_write_dword(struct rm rm, unsigned u); +static dosaddr_t sib(unsigned char *cp, x86_regs *x86, int *inst_len); +static struct rm modrm32(unsigned char *cp, x86_regs *x86, int *inst_len); +static struct rm modrm16(unsigned char *cp, x86_regs *x86, int *inst_len); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * DANG_BEGIN_FUNCTION instr_len + * + * Returns the length of an instruction; 0 if it could not + * be determined. + * + * DANG_END_FUNCTION + * + */ + +int instr_len(unsigned char *p, int is_32) +{ + unsigned u, osp, asp; + unsigned char *p0 = p; +#if DEBUG_INSTR >= 1 + unsigned char *p1 = p; +#endif + + seg = lock = rep = 0; + osp = asp = is_32; + + for(u = 1; u && p - p0 < 17;) switch(*p++) { /* get prefixes */ + case 0x26: /* es: */ + seg = 1; break; + case 0x2e: /* cs: */ + seg = 2; break; + case 0x36: /* ss: */ + seg = 3; break; + case 0x3e: /* ds: */ + seg = 4; break; + case 0x64: /* fs: */ + seg = 5; break; + case 0x65: /* gs: */ + seg = 6; break; + case 0x66: /* operand size */ + osp ^= 1; break; + case 0x67: /* address size */ + asp ^= 1; break; + case 0xf0: /* lock */ + lock = 1; break; + case 0xf2: /* repnz */ + rep = 2; break; + case 0xf3: /* rep(z) */ + rep = 1; break; + default: /* no prefix */ + u = 0; + } + p--; + +#if DEBUG_INSTR >= 1 + p1 = p; +#endif + + if(p - p0 >= 16) return 0; + + if(*p == 0x0f) { + p++; + switch (*p) { + case 0x80 ... 0x8f: + p += osp ? 5 : 3; + return p - p0; + case 0xa4: + p++; + p += (u = arg_len(p, asp)); + if(!u) p = p0; + return p + 1 - p0; + case 0xba: + p += 4; + return p - p0; + case 0xa5: + case 0xb6: + case 0xb7: + case 0xbe: + case 0xbf: + case 0: + case 1: + case 0x20: + case 0x22: + p++; + p += (u = arg_len(p, asp)); + if(!u) p = p0; + return p - p0; + default: + /* not yet */ + error("unsupported instr_len %x %x\n", p[0], p[1]); + return 0; + } + } + + switch(it[*p]) { + case 1: /* op-code */ + p += 1; break; + + case 2: /* op-code + byte */ + p += 2; break; + + case 3: /* op-code + word/dword */ + p += osp ? 5 : 3; break; + + case 4: /* op-code + [word/dword] */ + p += asp ? 5 : 3; break; + + case 5: /* op-code + word/dword + byte */ + p += osp ? 6 : 4; break; + + case 6: /* op-code + [word/dword] + word */ + p += asp ? 7 : 5; break; + + case 7: /* op-code + mod + ... */ + p++; + p += (u = arg_len(p, asp)); + if(!u) p = p0; + break; + + case 8: /* op-code + mod + ... + byte */ + p++; + p += (u = arg_len(p, asp)) + 1; + if(!u) p = p0; + break; + + case 9: /* op-code + mod + ... + word/dword */ + p++; + p += (u = arg_len(p, asp)) + (osp ? 4 : 2); + if(!u) p = p0; + break; + + default: + p = p0; + } + +#if DEBUG_INSTR >= 1 + if(p >= p0) { + instr_deb("instr_len: instr = "); + v_printf("%s%s%s%s%s", + osp ? "osp " : "", asp ? "asp " : "", + lock_txt[lock], rep_txt[rep], seg_txt[seg] + ); + if(p > p1) for(u = 0; u < p - p1; u++) { + v_printf("%02x ", p1[u]); + } + v_printf("\n"); + } +#endif + + return p - p0; +} + + +static unsigned arg_len(unsigned char *p, int asp) +{ + unsigned u = 0, m, s = 0; + + m = *p & 0xc7; + if(asp) { + if(m == 5) { + u = 5; + } + else { + if((m >> 6) < 3 && (m & 7) == 4) s = 1; + switch(m >> 6) { + case 1: + u = 2; break; + case 2: + u = 5; break; + default: + u = 1; + } + u += s; + } + } + else { + if(m == 6) + u = 3; + else + switch(m >> 6) { + case 1: + u = 2; break; + case 2: + u = 3; break; + default: + u = 1; + } + } + + instr_deb2("arg_len: %02x %02x %02x %02x: %u bytes\n", p[0], p[1], p[2], p[3], u); + + return u; +} + +/* + * Some functions to make using the vga emulation easier. + * + * + */ + +unsigned instr_read_word(struct rm rm) +{ + unsigned u; + + if (rm.r) { + memcpy(&u, rm.r, 2); + return u; + } + + /* + * segment wrap-arounds within a data word are not allowed since + * at least i286, so no problems here + */ + u = read_word(rm.m); + +#if DEBUG_INSTR >= 2 + instr_deb2("Read word 0x%x", u); + if (addr<0x8000000) v_printf(" from address %x\n", addr); else v_printf("\n"); +#endif + return u; +} + +unsigned instr_read_dword(struct rm rm) +{ + unsigned u; + + if (rm.r) { + memcpy(&u, rm.r, 4); + return u; + } + + /* + * segment wrap-arounds within a data word are not allowed since + * at least i286, so no problems here + */ + u = read_dword(rm.m); + +#if DEBUG_INSTR >= 2 + instr_deb2("Read word 0x%x", u); + if (addr<0x8000000) v_printf(" from address %x\n", addr); else v_printf("\n"); +#endif + return u; +} + +void instr_write_word(struct rm rm, unsigned u) +{ + if (rm.r) { + memcpy(rm.r, &u, 2); + return; + } + + /* + * segment wrap-arounds within a data word are not allowed since + * at least i286, so no problems here. + * we assume application do not try to mix here + */ + + write_word(rm.m, u); + +#if DEBUG_INSTR >= 2 + instr_deb2("Write word 0x%x", u); + if (dst<0x8000000) v_printf(" at address %x\n", dst); else v_printf("\n"); +#endif +} + +void instr_write_dword(struct rm rm, unsigned u) +{ + if (rm.r) { + memcpy(rm.r, &u, 4); + return; + } + + /* + * segment wrap-arounds within a data word are not allowed since + * at least i286, so no problems here. + * we assume application do not try to mix here + */ + + write_dword(rm.m, u); + +#if DEBUG_INSTR >= 2 + instr_deb2("Write word 0x%x", u); + if (dst<0x8000000) v_printf(" at address %x\n", dst); else v_printf("\n"); +#endif +} + +static inline void pop(unsigned *val, x86_regs *x86) +{ + struct rm mem = {}; + + mem.m = x86->ss_base + (x86->esp & wordmask[(x86->_32bit+1)*2]); + if (x86->_32bit) + x86->esp += x86->operand_size; + else + LO_WORD(x86->esp) += x86->operand_size; + *val = (x86->operand_size == 4 ? instr_read_dword(mem) : instr_read_word(mem)); +} + +/* helper functions/macros reg8/reg/sreg/sib/modrm16/32 for instr_sim + for address and register decoding */ + +#define reg8(reg, x86) (((unsigned char *)(x86))+((reg)&0x3)*4+(((reg)>>2)&1)) +#define reg(reg, x86) ((&(x86)->eax)+((reg)&0x7)) +#define sreg(reg, x86) ((&((x86)->es))+((reg)&0x7)) +#define sreg_idx(reg) (es_INDEX+((reg)&0x7)) + +dosaddr_t sib(unsigned char *cp, x86_regs *x86, int *inst_len) +{ + unsigned addr = 0; + + switch(cp[1] & 0xc0) { /* decode modifier */ + case 0x40: + addr = (int)(signed char)cp[3]; + break; + case 0x80: + addr = R_DWORD(cp[3]); + break; + } + + if ((cp[2] & 0x38) != 0x20) /* index cannot be esp */ + addr += *reg(cp[2]>>3, x86) << (cp[2] >> 6); + + switch(cp[2] & 0x07) { /* decode address */ + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x06: + case 0x07: + return (addr + *reg(cp[2], x86) + x86->seg_base); + case 0x04: /* esp */ + return (addr + x86->esp + x86->seg_ss_base); + case 0x05: + if (cp[1] >= 0x40) + return (addr + x86->ebp + x86->seg_ss_base); + else { + *inst_len += 4; + return (addr + R_DWORD(cp[3]) + x86->seg_base); + } + } + return 0; /* keep gcc happy */ +} + +struct rm modrm16(unsigned char *cp, x86_regs *x86, int *inst_len) +{ + unsigned addr = 0; + struct rm rm = {}; + *inst_len = 0; + + switch(cp[1] & 0xc0) { /* decode modifier */ + case 0x40: + addr = (short)(signed char)cp[2]; + *inst_len = 1; + break; + case 0x80: + addr = R_WORD(cp[2]); + *inst_len = 2; + break; + case 0xc0: + if (cp[0]&1) /*(d)word*/ + rm.r = (unsigned char *)reg(cp[1], x86); + else + rm.r = reg8(cp[1], x86); + return rm; + } + + + switch(cp[1] & 0x07) { /* decode address */ + case 0x00: + rm.m = (((addr + x86->ebx + x86->esi) & 0xffff) + x86->seg_base); + break; + case 0x01: + rm.m = (((addr + x86->ebx + x86->edi) & 0xffff) + x86->seg_base); + break; + case 0x02: + rm.m = (((addr + x86->ebp + x86->esi) & 0xffff) + x86->seg_ss_base); + break; + case 0x03: + rm.m = (((addr + x86->ebp + x86->edi) & 0xffff) + x86->seg_ss_base); + break; + case 0x04: + rm.m = (((addr + x86->esi) & 0xffff) + x86->seg_base); + break; + case 0x05: + rm.m = (((addr + x86->edi) & 0xffff) + x86->seg_base); + break; + case 0x06: + if (cp[1] >= 0x40) + rm.m = (((addr + x86->ebp) & 0xffff) + x86->seg_ss_base); + else { + *inst_len += 2; + rm.m = (R_WORD(cp[2]) + x86->seg_base); + } + break; + case 0x07: + rm.m = (((addr + x86->ebx) & 0xffff) + x86->seg_base); + break; + } + return rm; +} + +struct rm modrm32(unsigned char *cp, x86_regs *x86, int *inst_len) +{ + unsigned addr = 0; + struct rm rm = {}; + *inst_len = 0; + + switch(cp[1] & 0xc0) { /* decode modifier */ + case 0x40: + addr = (int)(signed char)cp[2]; + *inst_len = 1; + break; + case 0x80: + addr = R_DWORD(cp[2]); + *inst_len = 4; + break; + case 0xc0: + if (cp[0]&1) /*(d)word*/ + rm.r = ((unsigned char *)reg(cp[1], x86)); + else + rm.r = reg8(cp[1], x86); + return rm; + } + + switch(cp[1] & 0x07) { /* decode address */ + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x06: + case 0x07: + rm.m = (addr + *reg(cp[1], x86) + x86->seg_base); + break; + case 0x04: /* sib byte follows */ + *inst_len += 1; + rm.m = sib(cp, x86, inst_len); + break; + case 0x05: + if (cp[1] >= 0x40) + rm.m = (addr + x86->ebp + x86->seg_ss_base); + else { + *inst_len += 4; + rm.m = (R_DWORD(cp[2]) + x86->seg_base); + } + break; + } + return rm; +} + +static int handle_prefixes(x86_regs *x86) +{ + unsigned eip = x86->eip; + int prefix = 0; + + for (;; eip++) { + switch(*(unsigned char *)MEM_BASE32(x86->cs_base + eip)) { + /* handle (some) prefixes */ + case 0x26: + prefix++; + x86->seg_base = x86->seg_ss_base = x86->es_base; + break; + case 0x2e: + prefix++; + x86->seg_base = x86->seg_ss_base = x86->cs_base; + break; + case 0x36: + prefix++; + x86->seg_base = x86->seg_ss_base = x86->ss_base; + break; + case 0x3e: + prefix++; + x86->seg_base = x86->seg_ss_base = x86->ds_base; + break; + case 0x64: + prefix++; + x86->seg_base = x86->seg_ss_base = x86->fs_base; + break; + case 0x65: + prefix++; + x86->seg_base = x86->seg_ss_base = x86->gs_base; + break; + case 0x66: + prefix++; + x86->operand_size = 6 - x86->operand_size; + if (x86->operand_size == 4) { + x86->instr_read = instr_read_dword; + x86->instr_write = instr_write_dword; + } else { + x86->instr_read = instr_read_word; + x86->instr_write = instr_write_word; + } + break; + case 0x67: + prefix++; + x86->address_size = 6 - x86->address_size; + x86->modrm = (x86->address_size == 4 ? modrm32 : modrm16); + break; + case 0xf2: + prefix++; + x86->rep = REPNZ; + break; + case 0xf3: + prefix++; + x86->rep = REPZ; + break; + default: + return prefix; + } + } + return prefix; +} + +static void prepare_x86(x86_regs *x86) +{ + x86->seg_base = x86->ds_base; + x86->seg_ss_base = x86->ss_base; + x86->address_size = x86->operand_size = (x86->_32bit + 1) * 2; + + x86->modrm = (x86->address_size == 4 ? modrm32 : modrm16); + x86->rep = REP_NONE; + + if (x86->operand_size == 4) { + x86->instr_read = instr_read_dword; + x86->instr_write = instr_write_dword; + } else { + x86->instr_read = instr_read_word; + x86->instr_write = instr_write_word; + } +} + +#define M(a) (struct rm){.m = (a)} + +static void scp_to_x86_regs(x86_regs *x86, cpuctx_t *scp, int pmode) +{ + if(pmode) { + x86->eax = _eax; + x86->ebx = _ebx; + x86->ecx = _ecx; + x86->edx = _edx; + x86->esi = _esi; + x86->edi = _edi; + x86->ebp = _ebp; + x86->esp = _esp; + x86->eip = _eip; + x86->eflags = _eflags; + x86->cs = _cs; + x86->ds = _ds; + x86->es = _es; + x86->ss = _ss; + x86->fs = _fs; + x86->gs = _gs; + x86->cs_base = GetSegmentBase(_cs); + x86->ds_base = GetSegmentBase(_ds); + x86->es_base = GetSegmentBase(_es); + x86->ss_base = GetSegmentBase(_ss); + x86->fs_base = GetSegmentBase(_fs); + x86->gs_base = GetSegmentBase(_gs); + x86->_32bit = _cs && dpmi_segment_is32(_cs) ? 1 : 0; + } + else { + x86->eax = REG(eax); + x86->ebx = REG(ebx); + x86->ecx = REG(ecx); + x86->edx = REG(edx); + x86->esi = REG(esi); + x86->edi = REG(edi); + x86->ebp = REG(ebp); + x86->esp = REG(esp); + x86->eip = REG(eip); + x86->eflags = REG(eflags); + x86->cs = SREG(cs); + x86->ds = SREG(ds); + x86->es = SREG(es); + x86->ss = SREG(ss); + x86->fs = SREG(fs); + x86->gs = SREG(gs); + x86->cs_base = SEGOFF2LINEAR(x86->cs, 0); + x86->ds_base = SEGOFF2LINEAR(x86->ds, 0); + x86->es_base = SEGOFF2LINEAR(x86->es, 0); + x86->ss_base = SEGOFF2LINEAR(x86->ss, 0); + x86->fs_base = SEGOFF2LINEAR(x86->fs, 0); + x86->gs_base = SEGOFF2LINEAR(x86->gs, 0); + x86->_32bit = 0; + } + prepare_x86(x86); +} + +static void x86_regs_to_scp(x86_regs *x86, cpuctx_t *scp, int pmode) +{ + if(pmode) { + _cs = x86->cs; + _ds = x86->ds; + _es = x86->es; + _fs = x86->fs; + _gs = x86->gs; + _ss = x86->ss; + _eax = x86->eax; + _ebx = x86->ebx; + _ecx = x86->ecx; + _edx = x86->edx; + _esi = x86->esi; + _edi = x86->edi; + _ebp = x86->ebp; + _esp = x86->esp; + _eip = x86->eip; + _eflags = x86->eflags; + } + else { + REG(eax) = x86->eax; + REG(ebx) = x86->ebx; + REG(ecx) = x86->ecx; + REG(edx) = x86->edx; + REG(esi) = x86->esi; + REG(edi) = x86->edi; + REG(ebp) = x86->ebp; + REG(esp) = x86->esp; + REG(eip) = x86->eip; + REG(eflags) = x86->eflags; + } +} + +int decode_modify_segreg_insn(cpuctx_t *scp, int pmode, + unsigned int *new_val) +{ + struct rm mem = {}; + unsigned cs; + int inst_len, ret = -1; + x86_regs x86; + + scp_to_x86_regs(&x86, scp, pmode); + + cs = x86.cs_base; + x86.prefixes = handle_prefixes(&x86); + x86.eip += x86.prefixes; + + switch(*(unsigned char *)MEM_BASE32(cs + x86.eip)) { + case 0x8e: /* mov segreg,r/m16 */ + ret = sreg_idx(*(unsigned char *)MEM_BASE32(cs + x86.eip + 1) >> 3); + mem = x86.modrm(MEM_BASE32(cs + x86.eip), &x86, &inst_len); + if ((*(unsigned char *)MEM_BASE32(cs + x86.eip + 1) & 0xc0) == 0xc0) /* compensate for mov r,segreg */ + memcpy(new_val, reg(*(unsigned char *)MEM_BASE32(cs + x86.eip + 1), &x86), 2); + else + *new_val = instr_read_word(mem); + x86.eip += inst_len + 2; + break; + + case 0xca: /*retf imm 16*/ + case 0xcb: /*retf*/ + case 0xcf: /*iret*/ + { + unsigned tmp_eip; + pop(&tmp_eip, &x86); + pop(new_val, &x86); + ret = cs_INDEX; + switch (*(unsigned char *)MEM_BASE32(cs + x86.eip)) { + case 0xca: /*retf imm 16*/ + x86.esp += ((unsigned short *) (MEM_BASE32(cs + x86.eip + 1)))[0]; + break; + case 0xcf: /*iret*/ + { + unsigned flags; + pop(&flags, &x86); + x86.eflags = flags; + break; + } + } + x86.eip = tmp_eip; + } + break; + + case 0xea: /* jmp seg:off16/off32 */ + { + unsigned tmp_eip; + tmp_eip = x86.instr_read(M(x86.cs_base + x86.eip + 1)); + *new_val = instr_read_word(M(x86.cs_base + x86.eip + 1 + x86.operand_size)); + ret = cs_INDEX; + x86.eip = tmp_eip; + } + break; + + case 0xc4: /* les */ + mem = x86.modrm(MEM_BASE32(cs + x86.eip), &x86, &inst_len); + *new_val = instr_read_word(M(mem.m+x86.operand_size)); + if (x86.operand_size == 2) + R_WORD(*reg(*(unsigned char *)MEM_BASE32(cs + x86.eip + 1) >> 3, &x86)) = instr_read_word(mem); + else + *reg(*(unsigned char *)MEM_BASE32(cs + x86.eip + 1) >> 3, &x86) = instr_read_dword(mem); + ret = es_INDEX; + x86.eip += inst_len + 2; + break; + + case 0xc5: /* lds */ + mem = x86.modrm(MEM_BASE32(cs + x86.eip), &x86, &inst_len); + *new_val = instr_read_word(M(mem.m+x86.operand_size)); + if (x86.operand_size == 2) + R_WORD(*reg(*(unsigned char *)MEM_BASE32(cs + x86.eip + 1) >> 3, &x86)) = instr_read_word(mem); + else + *reg(*(unsigned char *)MEM_BASE32(cs + x86.eip + 1) >> 3, &x86) = instr_read_dword(mem); + ret = ds_INDEX; + x86.eip += inst_len + 2; + break; + + case 0x07: /* pop es */ + case 0x17: /* pop ss */ + case 0x1f: /* pop ds */ + ret = sreg_idx(*(unsigned char *)MEM_BASE32(cs + x86.eip) >> 3); + pop(new_val, &x86); + x86.eip++; + break; + + case 0x0f: + x86.eip++; + switch (*(unsigned char *)MEM_BASE32(cs + x86.eip)) { + case 0xa1: /* pop fs */ + case 0xa9: /* pop gs */ + pop(new_val, &x86); + ret = sreg_idx(*(unsigned char *)MEM_BASE32(cs + x86.eip) >> 3); + x86.eip++; + break; + + case 0xb2: /* lss */ + mem = x86.modrm(MEM_BASE32(cs + x86.eip), &x86, &inst_len); + *new_val = instr_read_word(M(mem.m+x86.operand_size)); + if (x86.operand_size == 2) + R_WORD(*reg(*(unsigned char *)MEM_BASE32(cs + x86.eip + 1) >> 3, &x86)) = instr_read_word(mem); + else + *reg(*(unsigned char *)MEM_BASE32(cs + x86.eip + 1) >> 3, &x86) = instr_read_dword(mem); + ret = ss_INDEX; + x86.eip += inst_len + 2; + break; + + case 0xb4: /* lfs */ + mem = x86.modrm(MEM_BASE32(cs + x86.eip), &x86, &inst_len); + *new_val = instr_read_word(M(mem.m+x86.operand_size)); + if (x86.operand_size == 2) + R_WORD(*reg(*(unsigned char *)MEM_BASE32(cs + x86.eip + 1) >> 3, &x86)) = instr_read_word(mem); + else + *reg(*(unsigned char *)MEM_BASE32(cs + x86.eip + 1) >> 3, &x86) = instr_read_dword(mem); + ret = fs_INDEX; + x86.eip += inst_len + 2; + break; + + case 0xb5: /* lgs */ + mem = x86.modrm(MEM_BASE32(cs + x86.eip), &x86, &inst_len); + *new_val = instr_read_word(M(mem.m+x86.operand_size)); + if (x86.operand_size == 2) + R_WORD(*reg(*(unsigned char *)MEM_BASE32(cs + x86.eip + 1) >> 3, &x86)) = instr_read_word(mem); + else + *reg(*(unsigned char *)MEM_BASE32(cs + x86.eip + 1) >> 3, &x86) = instr_read_dword(mem); + ret = gs_INDEX; + x86.eip += inst_len + 2; + break; + } + break; + } + + x86_regs_to_scp(&x86, scp, pmode); + return ret; +} diff --git a/src/base/video/remap.c b/src/base/video/remap.c new file mode 100644 index 0000000..b1d6011 --- /dev/null +++ b/src/base/video/remap.c @@ -0,0 +1,3553 @@ +/* + * DANG_BEGIN_MODULE + * + * REMARK + * Transform a 2D image (rescale and color space conversion). + * + * Here are functions to adapt the VGA graphics to various + * X displays. + * + * /REMARK + * DANG_END_MODULE + * + * Copyright (c) 1997 Steffen Winterfeldt + * + * + * DANG_BEGIN_CHANGELOG + * + * 1997/07/08: Gamma correction now uses only integer operations. + * -- sw (Steffen Winterfeldt ) + * + * 1998/11/01: Added so far unsupported video modes. We now + * support all but the Hercules mode. + * -- sw + * + * 1999/01/05: Added support for Hercules mode. + * -- sw + * + * DANG_END_CHANGELOG + * + */ + +#include "emu.h" +#include +#include +#include +#include +#include +#include /* mprotect() */ + +#include "vgaemu.h" +#include "mapping.h" +#include "init.h" +#include "render.h" +#include "render_priv.h" +#include "remap_priv.h" + +#define LUT_OFS_33 256 * 3 +#define LUT_OFS_67 256 * 4 +#define LUT_OFS_11 256 * 5 +#define LUT_OFS_22 256 * 6 +#define LUT_OFS_45 256 * 7 + +#ifdef REMAP_TEST +RemapFuncDesc *remap_test(void); +#endif +RemapFuncDesc *remap_gen(void); + +RemapFuncDesc *(*remap_list_funcs[])(void) = { + remap_gen, +#if 0 +#if defined(__i386__) && !defined(__clang__) + remap_opt, +#endif +#endif +#ifdef REMAP_TEST + remap_test, +#endif + NULL +}; + +static RemapFuncDesc *remap_list = NULL; + +static int base_init = 0; +static FILE *rdm = NULL; + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +static void do_base_init(void); + +static void do_nothing(void) {}; +static void do_nothing_remap(struct RemapObjectStruct *a) {}; +static int do_nearly_nothing(RemapObject *a, unsigned b, unsigned c, unsigned d, unsigned e, unsigned f) { return 0; }; +static RectArea do_nearly_something_rect(RemapObject *ro, int x0, int y0, int width, int height) { RectArea ra = {0, 0, 0, 0}; return ra; }; +static RectArea do_nearly_something_mem(RemapObject *ro, int offset, int len) { RectArea ra = {0, 0, 0, 0}; return ra; }; + +static unsigned u_pow(unsigned, unsigned); +static unsigned gamma_fix(unsigned, unsigned); +static int true_col_palette_update(RemapObject *, unsigned, unsigned, unsigned, unsigned, unsigned); +static int pseudo_col_palette_update(RemapObject *, unsigned, unsigned, unsigned, unsigned, unsigned); + +static void rgb_color_reduce(const ColorSpaceDesc *, int, int, int, RGBColor *); +static unsigned rgb_color_reduced_2int(const ColorSpaceDesc *, RGBColor); +static unsigned rgb_color_2int(const ColorSpaceDesc *csd, int rbits, int gbits, + int bbits, RGBColor c); +static void rgb_lin_filt(RGBColor, RGBColor *, RGBColor *); +static void rgb_bilin_filt(RGBColor, RGBColor *, RGBColor *, RGBColor *); + +static void src_resize_update(RemapObject *, int, int, int); +static void dst_resize_update(RemapObject *, int, int, int); +static void resize_update(RemapObject *); + +int bre_s(int, int, int); +int bre_s2(int, int, int); +int bre_acc(int, int, int); +int bre_d_0(int, int, int); +void bre_update(RemapObject *); +void bre_lin_filt_update(RemapObject *); +void bre_bilin_filt_update(RemapObject *); + +static RemapFuncDesc *find_remap_func(unsigned, int, int, RemapFuncDesc *); +static RemapFuncDesc *find_best_remap_func(unsigned, int, int, RemapFuncDesc *); +static void install_remap_funcs(RemapObject *, int); + +static RectArea remap_mem_1(RemapObject *, int, int); +static RectArea remap_rect_1(RemapObject *, int, int, int, int); +static RectArea remap_rect_dst_1(RemapObject *, int, int, int, int); +static RectArea remap_mem_2(RemapObject *, int, int); + +/* + * set file handle for debug messages + */ +void set_remap_debug_msg(FILE *_rdm) { rdm = _rdm; } + + +/* + * some basic intializations + */ +static void do_base_init(void) +{ + RemapFuncDesc *rfd0, *rfd; + int i = 0; + + /* setup remap_list to hold a chained list of remap function descriptions */ + + remap_list = NULL; + + while(remap_list_funcs[i] != NULL) { + rfd0 = rfd = remap_list_funcs[i++](); + if(rfd != NULL) { + while(rfd->next != NULL) rfd = rfd->next; + rfd->next = remap_list; + remap_list = rfd0; + } + } +} + + +/* + * initialize a remap object + */ +static RemapObject *_remap_init(int src_mode, int dst_mode, int features, + const ColorSpaceDesc *color_space, int gamma) +{ + RemapObject *ro = malloc(sizeof(*ro)); + int color_lut_size = 256; + unsigned u, u0, u1; + + ro->features = features; + ro->palette_update = do_nearly_nothing; + ro->src_resize = src_resize_update; + ro->dst_resize = dst_resize_update; + ro->remap_rect = do_nearly_something_rect; + ro->remap_rect_dst = do_nearly_something_rect; + ro->remap_mem = do_nearly_something_mem; + ro->state = 0; + ro->src_mode = src_mode; + ro->dst_mode = dst_mode; + ro->src_tmp_line = NULL; + ro->src_width = ro->src_height = ro->src_scan_len = + ro->dst_width = ro->dst_height = ro->dst_scan_len = + ro->src_x0 = ro->src_y0 = ro->src_x1 = ro->src_y1 = + ro->dst_x0 = ro->dst_y0 = ro->dst_x1 = ro->dst_y1 = + ro->src_offset = ro->dst_offset = 0; + ro->src_start = ro->dst_start = 0; + ro->bre_x = ro->bre_y = NULL; + ro->true_color_lut = NULL; + ro->color_lut_size = 0; + ro->bit_lut = NULL; + ro->gamma_lut = malloc(256 * (sizeof *ro->gamma_lut)); + for(u = 0; u < 256; u++) + ro->gamma_lut[u] = gamma_fix(u, gamma); + ro->gamma = gamma; + + ro->remap_func = ro->remap_func_init = NULL; + ro->remap_func_flags = 0; + ro->remap_func_name = "no_func"; +#if 0 + ro->co = malloc(sizeof(*ro->co)); + if(ro->co == NULL) { + ro->state |= ROS_MALLOC_FAIL; + } + else { + *ro->co = code_init(); + } +#endif + ro->remap_line = NULL; + ro->func_all = ro->func_1 = ro->func_2 = NULL; + + if(!base_init) { + do_base_init(); + base_init = 1; + } + + ro->dst_color_space = color_space; + ro->dst_image = NULL; + + ro->bre_x = calloc(1, sizeof(*ro->bre_x)); + if(ro->bre_x == NULL) ro->state |= ROS_MALLOC_FAIL; + ro->bre_y = calloc(1, sizeof(*ro->bre_y)); + if(ro->bre_y == NULL) ro->state |= ROS_MALLOC_FAIL; + + install_remap_funcs(ro, features); + + if( + (ro->func_all && (ro->func_all->flags & RFF_LIN_FILT)) || + (ro->func_1 && (ro->func_1->flags & RFF_LIN_FILT)) || + (ro->func_2 && (ro->func_2->flags & RFF_LIN_FILT)) + ) { + color_lut_size = 256 * 3; + } + + if( + (ro->func_all && (ro->func_all->flags & RFF_BILIN_FILT)) || + (ro->func_1 && (ro->func_1->flags & RFF_BILIN_FILT)) || + (ro->func_2 && (ro->func_2->flags & RFF_BILIN_FILT)) + ) { + color_lut_size = 256 * 6; + } + + if( + ( + ro->src_mode & ( + MODE_PSEUDO_8 | MODE_VGA_X | + MODE_VGA_1 | MODE_VGA_2 | MODE_VGA_4 | + MODE_CGA_1 | MODE_CGA_2 | MODE_HERC + ) + ) == ro->src_mode + ) { + if((ro->dst_mode & MODE_PSEUDO_8) == ro->dst_mode) { + ro->palette_update = do_nearly_nothing; + } + if((ro->dst_mode & MODE_TRUE_8) == ro->dst_mode) { + ro->true_color_lut = calloc(color_lut_size, sizeof(*ro->true_color_lut)); + if(ro->true_color_lut == NULL) ro->state |= ROS_MALLOC_FAIL; + ro->color_lut_size = color_lut_size; + ro->palette_update = pseudo_col_palette_update; + } + else if((ro->dst_mode & MODE_TRUE_COL) == ro->dst_mode) { + /* **** really too much for now... **** */ + color_lut_size *= 3; + ro->true_color_lut = calloc(color_lut_size, sizeof(*ro->true_color_lut)); + if(ro->true_color_lut == NULL) ro->state |= ROS_MALLOC_FAIL; + ro->color_lut_size = color_lut_size; + ro->palette_update = true_col_palette_update; + } + ro->bit_lut = calloc(8*4*256, 1); + if(ro->bit_lut == NULL) { + ro->state |= ROS_MALLOC_FAIL; + } + else { + for(u = 0; u < 0x100; u++) { + u0 = u1 = 0; + if((u & 0x80)) u0 |= 1 << 0; + if((u & 0x40)) u0 |= 1 << 8; + if((u & 0x20)) u0 |= 1 << 16; + if((u & 0x10)) u0 |= 1 << 24; + if((u & 0x08)) u1 |= 1 << 0; + if((u & 0x04)) u1 |= 1 << 8; + if((u & 0x02)) u1 |= 1 << 16; + if((u & 0x01)) u1 |= 1 << 24; + ro->bit_lut[2 * u ] = u0; + ro->bit_lut[2 * u + 1 ] = u1; + ro->bit_lut[2 * u + 0x200] = u0 << 1; + ro->bit_lut[2 * u + 1 + 0x200] = u1 << 1; + ro->bit_lut[2 * u + 0x400] = u0 << 2; + ro->bit_lut[2 * u + 1 + 0x400] = u1 << 2; + ro->bit_lut[2 * u + 0x600] = u0 << 3; + ro->bit_lut[2 * u + 1 + 0x600] = u1 << 3; + } + } + } + + if( + (ro->src_mode & + ( + MODE_TRUE_COL | MODE_PSEUDO_8 | MODE_VGA_X | + MODE_VGA_1 | MODE_VGA_2 | MODE_VGA_4 + ) + ) && + (ro->dst_mode & (MODE_TRUE_COL | MODE_PSEUDO_8)) == ro->dst_mode + ) { + ro->remap_mem = remap_mem_1; + ro->remap_rect = remap_rect_1; + ro->remap_rect_dst = remap_rect_dst_1; + } + + if((ro->src_mode & (MODE_CGA_1 | MODE_CGA_2 | MODE_HERC)) && + (ro->dst_mode & (MODE_TRUE_COL | MODE_PSEUDO_8)) == ro->dst_mode + ) { + ro->remap_mem = remap_mem_2; + } + + return ro; +} + + +/* + * destroy a remap object + */ +#define FreeIt(_p_) if(_p_ != NULL) { free(_p_); _p_ = NULL; } +static void _remap_done(RemapObject *ro) +{ + FreeIt(ro->gamma_lut) + FreeIt(ro->bre_x) + FreeIt(ro->bre_y) + FreeIt(ro->true_color_lut) + FreeIt(ro->bit_lut); + FreeIt(ro->src_tmp_line); +#if 0 + if(ro->co != NULL) { + code_done(ro->co); + free(ro->co); + ro->co = NULL; + } +#endif + free(ro); +} +#undef FreeIt + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* + * Calculate a^b (a >= 0, b >= 0). + * a, b are fixed point numbers with 16 bit decimals. + * + * The result is accurate enough for our purpose (relative + * error typically 10^-4, worst cases still better than 10^-2). + */ + +unsigned u_pow(unsigned a, unsigned b) +{ + unsigned long long l, l2, r; + unsigned b0; + int i, j; + + if(a == 0) return b ? 0 : 1 << 16; + + b0 = b >> 16; + r = 1 << 16; /* 1.0 */ + + for(l = a, i = 0; i < 16 && b0; i++) { + if(b0 & 1) r = r * l >> 16; + l = l * l >> 16; + b0 >>= 1; + } + + for(l = a, i = 0; i < 16 && (b & ((1 << 16) - 1)); i++) { + for(l2 = l, j = 0; j < 10; j++) { + l2 = (l2 >> 1) + (l << 15) / l2; + } /* l2 = sqrt(l) now */ + l = l2; + if(b & (1 << 15)) r = r * l >> 16; + b <<= 1; + } + + return r; +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +unsigned gamma_fix(unsigned color, unsigned gamma) +{ + return gamma ? u_pow(color << 8, (100 << 16) / gamma) >> 8 : color; +} + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +void gamma_correct(RemapObject *ro, RGBColor *c, unsigned *bits) +{ + int i; + + if(*bits <= 1) return; + if(ro->gamma_lut == NULL) return; + i = *bits - 8; + if(i > 0) { + c->r >>= i; c->g >>= i; c->b >>= i; + } + if(i < 0) { + i = -i; + c->r <<= i; c->g <<= i; c->b <<= i; + } + c->r &= 255; c->g &= 255; c->b &= 255; + c->r = ro->gamma_lut[c->r]; + c->g = ro->gamma_lut[c->g]; + c->b = ro->gamma_lut[c->b]; + *bits = 8; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +static int true_col_palette_update(RemapObject *ro, unsigned i, unsigned bits, + unsigned r, unsigned g, unsigned b) +{ + RGBColor c = {r, g, b}, c1, c2, c3; + unsigned u, u0, uo; + + if(i >= 256) return 0; + + gamma_correct(ro, &c, &bits); + + u0 = u = rgb_color_2int(ro->dst_color_space, bits, bits, bits, c); + + if(ro->dst_color_space->bits == 8) u |= u << 8; + if(ro->dst_color_space->bits <= 16) u |= u << 16; + +#if 0 + fprintf(rdm, "true_col_palette_update: pal[%u] = (0x%x, 0x%x, 0x%x) = 0x%08x\n", i, r, g, b, u); +#endif + + uo = ro->true_color_lut[i]; + ro->true_color_lut[i] = u; + + if( + (ro->func_all && (ro->func_all->flags & RFF_LIN_FILT)) || + (ro->func_1 && (ro->func_1->flags & RFF_LIN_FILT)) || + (ro->func_2 && (ro->func_2->flags & RFF_LIN_FILT)) || + (ro->func_all && (ro->func_all->flags & RFF_BILIN_FILT)) || + (ro->func_1 && (ro->func_1->flags & RFF_BILIN_FILT)) || + (ro->func_2 && (ro->func_2->flags & RFF_BILIN_FILT)) + ) { + rgb_color_reduce(ro->dst_color_space, bits, bits, bits, &c); + rgb_lin_filt(c, &c1, &c2); + ro->true_color_lut[i + LUT_OFS_33] = rgb_color_reduced_2int(ro->dst_color_space, c1); + ro->true_color_lut[i + LUT_OFS_67] = rgb_color_reduced_2int(ro->dst_color_space, c2); + } + + if( + (ro->func_all && (ro->func_all->flags & RFF_BILIN_FILT)) || + (ro->func_1 && (ro->func_1->flags & RFF_BILIN_FILT)) || + (ro->func_2 && (ro->func_2->flags & RFF_BILIN_FILT)) + ) { + rgb_bilin_filt(c, &c1, &c2, &c3); + ro->true_color_lut[i + LUT_OFS_11] = rgb_color_reduced_2int(ro->dst_color_space, c1); + ro->true_color_lut[i + LUT_OFS_22] = rgb_color_reduced_2int(ro->dst_color_space, c2); + ro->true_color_lut[i + LUT_OFS_45] = rgb_color_reduced_2int(ro->dst_color_space, c3); + } + + if(ro->dst_color_space->bits > 8 && ro->dst_color_space->bits <= 16) { + ro->true_color_lut[i + 256] = u0; + ro->true_color_lut[i + 256 * 2] = u0 << 16; + } + + return uo == u ? 0 : 1; +} + +static int pseudo_col_palette_update(RemapObject *ro, unsigned i, unsigned bits, + unsigned r, unsigned g, unsigned b) +{ + RGBColor c = {r, g, b}; + unsigned u, uo; + + gamma_correct(ro, &c, &bits); + + u = rgb_color_2int(ro->dst_color_space, bits, bits, bits, c); + +#if 0 + fprintf(rdm, "pseudo_col_palette_update: pal[%u] = (0x%x, 0x%x, 0x%x) = 0x%02x:0x%02x\n", i, r, g, b, u0, u1); +#endif + + if(i < 256) { + uo = ro->true_color_lut[i]; + ro->true_color_lut[i] = u; + return u == uo ? 0 : 1; + } + + return 0; +} + +static unsigned dit_col(int s_c, int d_c, int col, int dit, int lim) +{ + int k, l, k0, k1, kr; + + k0 = ((d_c - 1) * col + d_c - 2) / (s_c - 1); + k1 = k0 + 1; + if(k1 >= d_c) k1 = k0; + k = ((s_c - 1) * k0) / (d_c - 1); + l = ((s_c - 1) * k1) / (d_c - 1); + if(k != l) { + kr = ((col - k) * dit + ((l - k) >> 1) ) / (l - k); + } + else { + kr = 0; + } + + return kr <= lim ? k0 : k1; /* or < ? */ +} + +void rgb_color_reduce(const ColorSpaceDesc *csd, int rbits, int gbits, + int bbits, RGBColor *c) +{ + c->r &= (1 << rbits) - 1; + c->g &= (1 << gbits) - 1; + c->b &= (1 << bbits) - 1; + + if(csd->r_mask || csd->g_mask || csd->b_mask) { + c->r = csd->r_bits >= rbits ? c->r << (csd->r_bits - rbits) : c->r >> (rbits - csd->r_bits); + c->g = csd->g_bits >= gbits ? c->g << (csd->g_bits - gbits) : c->g >> (gbits - csd->g_bits); + c->b = csd->b_bits >= bbits ? c->b << (csd->b_bits - bbits) : c->b >> (bbits - csd->b_bits); + } +} + +unsigned rgb_color_reduced_2int(const ColorSpaceDesc *csd, RGBColor c) +{ + c.r <<= csd->r_shift; + c.g <<= csd->g_shift; + c.b <<= csd->b_shift; + return c.r | c.g | c.b; +} + +static unsigned rgb_color_2int(const ColorSpaceDesc *csd, int rbits, int gbits, + int bbits, RGBColor c) +{ + unsigned r, g, b; + unsigned i0, i1, i2, i3; + + if(csd->r_mask || csd->g_mask || csd->b_mask) { + rgb_color_reduce(csd, rbits, gbits, bbits, &c); + return rgb_color_reduced_2int(csd, c); + } + + c.r &= (1 << rbits) - 1; + c.g &= (1 << gbits) - 1; + c.b &= (1 << bbits) - 1; + +#ifdef REMAP_REAL_DITHER + if(csd->r_bits && csd->g_bits && csd->b_bits) { + r = dit_col(1 << rbits, csd->r_bits, c.r, 5, 1); + g = dit_col(1 << gbits, csd->g_bits, c.g, 5, 1); + b = dit_col(1 << bbits, csd->b_bits, c.b, 5, 1); + i0 = r * csd->r_shift + g * csd->g_shift + b * csd->b_shift; + + r = dit_col(1 << rbits, csd->r_bits, c.r, 5, 3); + g = dit_col(1 << gbits, csd->g_bits, c.g, 5, 3); + b = dit_col(1 << bbits, csd->b_bits, c.b, 5, 3); + i1 = r * csd->r_shift + g * csd->g_shift + b * csd->b_shift; + + r = dit_col(1 << rbits, csd->r_bits, c.r, 5, 4); + g = dit_col(1 << gbits, csd->g_bits, c.g, 5, 4); + b = dit_col(1 << bbits, csd->b_bits, c.b, 5, 4); + i2 = r * csd->r_shift + g * csd->g_shift + b * csd->b_shift; + + r = dit_col(1 << rbits, csd->r_bits, c.r, 5, 2); + g = dit_col(1 << gbits, csd->g_bits, c.g, 5, 2); + b = dit_col(1 << bbits, csd->b_bits, c.b, 5, 2); + i3 = r * csd->r_shift + g * csd->g_shift + b * csd->b_shift; + + if(csd->pixel_lut != NULL) { + i0 = csd->pixel_lut[i0]; + i1 = csd->pixel_lut[i1]; + i2 = csd->pixel_lut[i2]; + i3 = csd->pixel_lut[i3]; + } + return i0 + (i1 << 8) + (i2 << 16) + (i3 << 24); + } +#else + /* + * the following calculation is taken directly from X.c + */ + if(csd->r_bits && csd->g_bits && csd->b_bits && bits) { + r = (c.r * csd->r_bits) >> rbits; + g = (c.g * csd->g_bits) >> gbits; + b = (c.b * csd->b_bits) >> bbits; + + i0 = r * csd->r_shift + g * csd->g_shift + b * csd->b_shift; + + r = (((c.r + (c.r - ((r << rbits) / csd->r_bits))) * csd->r_bits) >> rbits); + g = (((c.g + (c.g - ((g << gbits) / csd->g_bits))) * csd->g_bits) >> gbits); + b = (((c.b + (c.b - ((b << bbits) / csd->b_bits))) * csd->b_bits) >> bbits); + + if(r >= csd->r_bits) r = csd->r_bits - 1; + if(g >= csd->g_bits) g = csd->g_bits - 1; + if(b >= csd->b_bits) b = csd->b_bits - 1; + + i1 = r * csd->r_shift + g * csd->g_shift + b * csd->b_shift; + + if(csd->pixel_lut != NULL) { + i0 = csd->pixel_lut[i0]; + i1 = csd->pixel_lut[i1]; + } + + return i0 + (i1 << 8) + (i1 << 16) + (i0 << 24); + } +#endif + + return 0; +} + +static unsigned bgr_2int(const ColorSpaceDesc *csd, int rbits, int gbits, + int bbits, unsigned bgr) +{ + RGBColor c = { bgr >> (bbits + gbits), bgr >> bbits, bgr }; + + return rgb_color_2int(csd, rbits, gbits, bbits, c); +} + +#if 0 +static RGBColor int_2rgb_color(const ColorSpaceDesc *csd, unsigned bits, unsigned u) +{ + RGBColor c = {0, 0, 0}; + unsigned nr = u & csd->r_mask, ng = u & csd->g_mask, nb = u & csd->b_mask; + + if(csd->r_mask || csd->g_mask || csd->b_mask) { + nr >>= csd->r_shift; + ng >>= csd->g_shift; + nb >>= csd->b_shift; + + c.r = csd->r_bits >= bits ? nr >> (csd->r_bits - bits) : nr << (bits - csd->r_bits); + c.g = csd->g_bits >= bits ? ng >> (csd->g_bits - bits) : ng << (bits - csd->g_bits); + c.b = csd->b_bits >= bits ? nb >> (csd->b_bits - bits) : nb << (bits - csd->b_bits); + + return c; + } + + return c; +} +#endif + +void rgb_lin_filt(RGBColor c, RGBColor *c1, RGBColor *c2) +{ + unsigned u; + + u = (c.r + 1) / 3; c1->r = u; c2->r = c.r - u; + u = (c.g + 1) / 3; c1->g = u; c2->g = c.g - u; + u = (c.b + 1) / 3; c1->b = u; c2->b = c.b - u; +} + +void rgb_bilin_filt(RGBColor c, RGBColor *c1, RGBColor *c2, RGBColor *c3) +{ + unsigned u; + +#if 0 + u = c.r; c1->r = (u + 1) / 9; c2->r = (u * 2 + 1) / 9; c3->r = (u * 4 + 1) / 9; + if(c1->r + 2 * c2->r + c3->r < u) c3->r++; + if(c1->r + 2 * c2->r + c3->r < u) c2->r++; + u = c.g; c1->g = (u + 1) / 9; c2->g = (u * 2 + 1) / 9; c3->g = (u * 4 + 1) / 9; + if(c1->g + 2 * c2->g + c3->g < u) c3->g++; + if(c1->g + 2 * c2->g + c3->g < u) c2->g++; + u = c.b; c1->b = (u + 1) / 9; c2->b = (u * 2 + 1) / 9; c3->b = (u * 4 + 1) / 9; + if(c1->b + 2 * c2->b + c3->b < u) c3->b++; + if(c1->b + 2 * c2->b + c3->b < u) c2->b++; +#endif + + u = c.r; c1->r = (u + 4) / 9; c2->r = (u * 2 + 4) / 9; c3->r = u - c1->r - 2 * c2->r; + u = c.g; c1->g = (u + 4) / 9; c2->g = (u * 2 + 4) / 9; c3->g = u - c1->g - 2 * c2->g; + u = c.b; c1->b = (u + 4) / 9; c2->b = (u * 2 + 4) / 9; c3->b = u - c1->b - 2 * c2->b; + + /* magic !!! */ + if(c3->r == 114) c3->r--; + if(c3->g == 114) c3->g--; + if(c3->b == 114) c3->b--; + +} + + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +static void src_resize_update(RemapObject *ro, int width, int height, int scan_len) +{ + if ( ro->src_width == width && + ro->src_height == height && + ro->src_scan_len == scan_len) + return; + ro->src_width = width; + ro->src_height = height; + ro->src_scan_len = scan_len; + ro->src_tmp_line = realloc(ro->src_tmp_line, width); + // check return value? + resize_update(ro); +} + +static void dst_resize_update(RemapObject *ro, int width, int height, int scan_len) +{ + if ( ro->dst_width == width && + ro->dst_height == height && + ro->dst_scan_len == scan_len) + return; + ro->dst_width = width; + ro->dst_height = height; + ro->dst_scan_len = scan_len; + resize_update(ro); +} + +static void resize_update(RemapObject *ro) +{ + ro->state &= ~(ROS_REMAP_FUNC_OK | ROS_REMAP_IGNORE); + + if(!(ro->state & (ROS_SCALE_ALL | ROS_SCALE_1 | ROS_SCALE_2))) { + return; + } + + if( + ro->src_width == 0 || ro->src_height == 0 || + ro->dst_width == 0 || ro->dst_height == 0 || + ro->src_scan_len == 0 || ro->dst_scan_len == 0 + ) { + ro->state |= ROS_REMAP_FUNC_OK | ROS_REMAP_IGNORE; + return; + } + + ro->remap_line = do_nothing; + + if( + ro->src_width == ro->dst_width && + ro->src_height == ro->dst_height && + (ro->state & ROS_SCALE_1) + ) { + ro->remap_func = ro->func_1->func; + ro->remap_func_flags = ro->func_1->flags; + ro->remap_func_name = ro->func_1->func_name; + ro->remap_func_init = ro->func_1->func_init; + } + else if( + (ro->src_width << 1) == ro->dst_width && + (ro->src_height << 1) == ro->dst_height && + (ro->state & ROS_SCALE_2) + ) { + ro->remap_func = ro->func_2->func; + ro->remap_func_flags = ro->func_2->flags; + ro->remap_func_name = ro->func_2->func_name; + ro->remap_func_init = ro->func_2->func_init; + } + else if(ro->state & ROS_SCALE_ALL) { + ro->remap_func = ro->func_all->func; + ro->remap_func_flags = ro->func_all->flags; + ro->remap_func_name = ro->func_all->func_name; + ro->remap_func_init = ro->func_all->func_init; + } + else { + ro->remap_func = do_nothing_remap; + ro->remap_func_flags = 0; + ro->remap_func_name = "do_nothing"; + ro->remap_func_init = NULL; + } + + if(ro->remap_func_flags & RFF_BILIN_FILT) + bre_bilin_filt_update(ro); + else if(ro->remap_func_flags & RFF_LIN_FILT) + bre_lin_filt_update(ro); + else + bre_update(ro); + + if(ro->remap_func_init != NULL) { + ro->remap_func_init(ro); + } + + if(ro->remap_func != NULL && ro->remap_func != do_nothing_remap) { ro->state |= ROS_REMAP_FUNC_OK; } + +#ifdef REMAP_RESIZE_DEBUG + fprintf(rdm, "resize_update: using %s for remap %dx%d --> %dx%d\n", + ro->remap_func_name, ro->src_width, ro->src_height, ro->dst_width, ro->dst_height + ); +#endif + +} + + +int bre_s(int d, int s_len, int d_len) +{ + return (s_len / 2 + s_len * d) / d_len; +} + +int bre_s2(int d, int s_len, int d_len) +{ + return (s_len * d) / d_len; +} + +int bre_acc(int d, int s_len, int d_len) +{ + return (s_len / 2 + s_len * d) % d_len; +} + +int bre_d_0(int s, int s_len, int d_len) +{ + return ((s_len + 1) / 2 - 1 + s * d_len) / s_len; +} + +void bre_update(RemapObject *ro) +{ + int i, l, *ii, y; + + if(ro->bre_x != NULL) free(ro->bre_x); + if(ro->bre_y != NULL) free(ro->bre_y); + + if(!(l = ro->dst_width)) l++; + ro->bre_x = ii = malloc(l * sizeof(*ro->bre_x)); + + if(ro->bre_x == NULL) { + ro->state |= ROS_MALLOC_FAIL; + } + else { + for(i = 0; i < l; i++) ii[i] = bre_s(i + 1, ro->src_width, l); + + if(ro->src_mode == MODE_VGA_X) { + for(i = 0; i < l; i++) ii[i] = (ii[i] >> 2) + ((ii[i] & 3) << 16); + } + + for(i = l - 1; i > 0; i--) ii[i] -= ii[i - 1]; + } + + if(!(l = ro->dst_height)) l++; + ro->bre_y = ii = malloc(l * sizeof(*ro->bre_y)); + + if(ro->bre_y == NULL) { + ro->state |= ROS_MALLOC_FAIL; + } + else { + for(i = 0; i < l; i++) { + if(ro->src_mode == MODE_CGA_1 || ro->src_mode == MODE_CGA_2) { + y = bre_s(i, ro->src_height, l); + ii[i] = (y >> 1) * ro->src_scan_len + ((y & 1) ? 0x2000 : 0); + } + else if(ro->src_mode == MODE_HERC) { + y = bre_s(i, ro->src_height, l); + ii[i] = (y >> 2) * ro->src_scan_len + (y & 3) * 0x2000; + } + else { + ii[i] = bre_s(i, ro->src_height, l) * ro->src_scan_len; + } + } + } + +} + +void bre_lin_filt_update(RemapObject *ro) +{ + int i, k, l, *ii; + + if(ro->bre_x != NULL) free(ro->bre_x); + if(ro->bre_y != NULL) free(ro->bre_y); + + if(!(l = ro->dst_width)) l++; + ro->bre_x = ii = malloc(2 * l * sizeof(*ro->bre_x)); + + if(ro->bre_x == NULL) { + ro->state |= ROS_MALLOC_FAIL; + } + else { + k = ro->src_width; + if(k == 0) k = 1; + k = 3 * (k - 1); + for(i = 0; i < l; i++) { ii[i] = bre_s2(i + 1, k, l - 1); } + for(ii[l] = 0, i = 1; i < l; i++) { ii[l + i] = ii[i - 1] % 3; } + + for(i = 0; i < l; i++) { ii[i] /= 3; } + ii[l - 1] = ro->src_width - 1; /* just for fun... (value is never needed) */ + for(i = l - 1; i > 0; i--) { ii[i] -= ii[i - 1]; } + ii[0] = 0; + if(ii[2 * l - 1] != 0) fprintf(stderr, "**** oho: %d remains\n", ii[2 * l - 1]); + } + + if(!(l = ro->dst_height)) l++; + ro->bre_y = ii = malloc(l * sizeof(*ro->bre_y)); + + if(ro->bre_y == NULL) { + ro->state |= ROS_MALLOC_FAIL; + } + else { + for(i = 0; i < l; i++) { + ii[i] = bre_s(i, ro->src_height, l) * ro->src_scan_len; + } + } +} + +void bre_bilin_filt_update(RemapObject *ro) +{ + int i, k, l, *ii; + + if(ro->bre_x != NULL) free(ro->bre_x); + if(ro->bre_y != NULL) free(ro->bre_y); + + if(!(l = ro->dst_width)) l++; + ro->bre_x = ii = malloc(2 * l * sizeof(*ro->bre_x)); + + if(ro->bre_x == NULL) { + ro->state |= ROS_MALLOC_FAIL; + } + else { + k = ro->src_width; + if(k == 0) k = 1; + k = 3 * (k - 1); + for(i = 0; i < l; i++) { ii[i] = bre_s2(i + 1, k, l - 1); } + for(ii[l] = 0, i = 1; i < l; i++) { ii[l + i] = ii[i - 1] % 3; } + + for(i = 0; i < l; i++) { ii[i] /= 3; } + ii[l - 1] = ro->src_width - 1; /* just for fun... (value is never needed) */ + for(i = l - 1; i > 0; i--) { ii[i] -= ii[i - 1]; } + ii[0] = 0; + if(ii[2 * l - 1] != 0) fprintf(stderr, "**** oho: %d remains\n", ii[2 * l - 1]); + } + + if(!(l = ro->dst_height)) l++; + ro->bre_y = ii = malloc(2 * l * sizeof(*ro->bre_y)); + + if(ro->bre_y == NULL) { + ro->state |= ROS_MALLOC_FAIL; + } + else { + k = ro->src_height; + if(k == 0) k = 1; + k = 3 * (k - 1); + + for(i = 0; i < l; i++) { ii[i] = bre_s2(i, k, l - 1); } + for(i = 0; i < l; i++) { ii[l + i] = ii[i] % 3; } + + for(i = 0; i < l; i++) { ii[i] /= 3; } + if(ii[2 * l - 1] != 0) fprintf(stderr, "**** oho: %d remains\n", ii[2 * l - 1]); + if(ii[l - 1] >= ro->src_height) fprintf(stderr, "**** oho: %d lines is out of bounds\n", ii[l - 1]); + for(i = 0; i < l; i++) { ii[i] *= ro->src_scan_len; } + } +} + +#ifdef REMAP_AREA_DEBUG +#define REMAP_AREA_DEBUG_FUNC(_ro_) remap_area_debug_func(_ro_) + +static void remap_area_debug_func(RemapObject *ro) +{ + fprintf(rdm, "[%s]\n", ro->remap_func_name); + fprintf(rdm, " src_offset = %d, src_scan_len = %d, src_area = %d x %d, %d x %d\n", + ro->src_offset, ro->src_scan_len, ro->src_x0, ro->src_y0, ro->src_x1, ro->src_y1 + ); + fprintf(rdm, " dst_offset = %d, dst_scan_len = %d, dst_area = %d x %d, %d x %d\n", + ro->dst_offset, ro->dst_scan_len, ro->dst_x0, ro->dst_y0, ro->dst_x1, ro->dst_y1 + ); +} + +#else +#define REMAP_AREA_DEBUG_FUNC(_ro_) +#endif + +static RectArea remap_mem_1(RemapObject *ro, int offset, int len) +{ + RectArea ra = {0, 0, 0, 0}; + int i1, i2, j1, j2; + int pixel_size = 1; + + if(ro->state & ROS_REMAP_IGNORE) return ra; + if(ro->remap_func == NULL) return ra; + +#ifdef REMAP_AREA_DEBUG + fprintf(rdm, "remap_mem: ofs = %d, len = %d\n", offset, len); + fprintf(rdm, + " src: base = 0x%x, width = %d, height = %d, scan_len = %d\n", + (unsigned) ro->src_image, ro->src_width, ro->src_height, ro->src_scan_len + ); + fprintf(rdm, + " dst: base = 0x%x, width = %d, height = %d, scan_len = %d\n", + (unsigned) ro->dst_image, ro->dst_width, ro->dst_height, ro->dst_scan_len + ); +#endif + + if(offset < 0) len += offset, offset = 0; + + switch(ro->src_mode) { + case MODE_TRUE_15: + case MODE_TRUE_16: pixel_size = 2; break; + case MODE_TRUE_24: pixel_size = 3; break; + case MODE_TRUE_32: pixel_size = 4; break; + case MODE_TRUE_8: + case MODE_PSEUDO_8: + default: pixel_size = 1; + } + if (len < pixel_size) return ra; + + i1 = offset / ro->src_scan_len; + i2 = (offset % ro->src_scan_len) / pixel_size; + j1 = (offset + len) / ro->src_scan_len; + j2 = ((offset + len) % ro->src_scan_len) / pixel_size; + + /* make sure it's all visible */ + if(i2 >= ro->src_width) i1++, i2 = 0, offset = i1 * ro->src_scan_len; + if(i1 >= ro->src_height || i1 > j1) return ra; + if(j2 >= ro->src_width) j1++, j2 = 0; + if(j1 >= ro->src_height) j1 = ro->src_height, j2 = 0; + + ra.width = ro->dst_width; + + if (ro->remap_func_flags & (RFF_REMAP_RECT | RFF_REMAP_LINES)) { + ro->src_offset = i1 * ro->src_scan_len; + ro->src_x0 = ro->dst_x0 = 0; + ro->src_x1 = ro->src_width; + ro->dst_x1 = ro->dst_width; + ro->src_y0 = i1; + ro->src_y1 = j1; + if(j2) ro->src_y1++; + ro->dst_y0 = bre_d_0(ro->src_y0, ro->src_height, ro->dst_height); + ro->dst_y1 = bre_d_0(ro->src_y1, ro->src_height, ro->dst_height); + ro->dst_offset = ro->dst_y0 * ro->dst_scan_len; + ra.y = ro->dst_y0; + ra.height = ro->dst_y1 - ro->dst_y0; + REMAP_AREA_DEBUG_FUNC(ro); + if(ro->dst_y0 != ro->dst_y1) { + ro->remap_func(ro); + } + } + else { + ro->src_offset = ro->dst_offset = 0; + ro->src_x0 = ro->dst_x0 = ro->src_y0 = ro->dst_y0 = 0; + ro->src_x1 = ro->src_width; + ro->dst_x1 = ro->dst_width; + ro->src_y1 = ro->src_height; + ro->dst_y1 = ro->dst_height; + ra.height = ro->dst_height; + REMAP_AREA_DEBUG_FUNC(ro); + ro->remap_func(ro); + } + + return ra; +} + + +static RectArea remap_rect_1(RemapObject *ro, int x0, int y0, int width, int height) +{ + RectArea ra = {0, 0, 0, 0}; + int x1, y1; + int pixel_size = 1; + + if(ro->state & ROS_REMAP_IGNORE) return ra; + if(ro->remap_func == NULL) return ra; + + if(x0 < 0) width -= x0, x0 = 0; + if(y0 < 0) height -= y0, y0 = 0; + + if(x0 >= ro->src_width || y0 >= ro->src_height) return ra; + if(width <= 0 || height <= 0) return ra; + + x1 = x0 + width; + y1 = y0 + height; + + if(x1 > ro->src_width) x1 = ro->src_width, width = x1 - x0; + if(y1 > ro->src_height) y1 = ro->src_height, height = y1 - y0; + + ra.x = bre_d_0(x0, ro->src_width, ro->dst_width); + ra.y = bre_d_0(y0, ro->src_height, ro->dst_height); + ra.width = bre_d_0(x1, ro->src_width, ro->dst_width) - ra.x; + ra.height = bre_d_0(y1, ro->src_height, ro->dst_height) - ra.y; + + if(!(ra.width && ra.height)) return ra; + + switch(ro->dst_mode) { + case MODE_TRUE_15: + case MODE_TRUE_16: pixel_size = 2; break; + case MODE_TRUE_24: pixel_size = 3; break; + case MODE_TRUE_32: pixel_size = 4; break; + case MODE_TRUE_8: + case MODE_PSEUDO_8: + default: pixel_size = 1; + } + + if(ro->remap_func_flags & RFF_REMAP_RECT) { + ro->src_x0 = x0; + ro->src_x1 = x1; + ro->src_y0 = y0; + ro->src_y1 = y1; + ro->src_offset = ro->src_y0 * ro->src_scan_len + ro->src_x0; + ro->dst_x0 = ra.x; + ro->dst_x1 = ra.x + ra.width; + ro->dst_y0 = ra.y; + ro->dst_y1 = ra.y + ra.height; + ro->dst_offset = ro->dst_y0 * ro->dst_scan_len + ro->dst_x0 * pixel_size; + REMAP_AREA_DEBUG_FUNC(ro); + ro->remap_func(ro); + } + else if(ro->remap_func_flags & RFF_REMAP_LINES) { + ro->src_x0 = 0; + ro->src_x1 = ro->src_width; + ro->src_y0 = y0; + ro->src_y1 = y1; + ro->src_offset = ro->src_y0 * ro->src_scan_len; + ro->dst_x0 = 0; + ro->dst_x1 = ro->dst_width; + ro->dst_y0 = ra.y; + ro->dst_y1 = ra.y + ra.height; + ro->dst_offset = ro->dst_y0 * ro->dst_scan_len; + REMAP_AREA_DEBUG_FUNC(ro); + ro->remap_func(ro); + } + else { + ro->src_offset = ro->dst_offset = 0; + ro->src_x0 = ro->dst_x0 = ro->src_y0 = ro->dst_y0 = 0; + ro->src_x1 = ro->src_width; + ro->dst_x1 = ro->dst_width; + ro->src_y1 = ro->src_height; + ro->dst_y1 = ro->dst_height; + ra.x = ra.y = 0; + ra.width = ro->dst_width; + ra.height = ro->dst_height; + REMAP_AREA_DEBUG_FUNC(ro); + ro->remap_func(ro); + } + + return ra; +} + +static RectArea remap_rect_dst_1(RemapObject *ro, int x0, int y0, int width, int height) +{ + RectArea ra = {0, 0, 0, 0}; + int x1, y1; + int pixel_size = 1; + + if(ro->state & ROS_REMAP_IGNORE) return ra; + if(ro->remap_func == NULL) return ra; + + if(x0 < 0) width -= x0, x0 = 0; + if(y0 < 0) height -= y0, y0 = 0; + + if(x0 >= ro->dst_width || y0 >= ro->dst_height) return ra; + if(width <= 0 || height <= 0) return ra; + + x1 = x0 + width; + y1 = y0 + height; + + if(x1 > ro->dst_width) x1 = ro->dst_width, width = x1 - x0; + if(y1 > ro->dst_height) y1 = ro->dst_height, height = y1 - y0; + + ra.x = x0; + ra.y = y0; + ra.width = width; + ra.height = height; + + if(!(ra.width && ra.height)) return ra; + + switch(ro->dst_mode) { + case MODE_TRUE_15: + case MODE_TRUE_16: pixel_size = 2; break; + case MODE_TRUE_24: pixel_size = 3; break; + case MODE_TRUE_32: pixel_size = 4; break; + case MODE_TRUE_8: + case MODE_PSEUDO_8: + default: pixel_size = 1; + } + + if(ro->remap_func_flags & RFF_REMAP_RECT) { + ro->src_x0 = bre_s(x0, ro->src_width, ro->dst_width); + ro->src_x1 = bre_s(x1, ro->src_width, ro->dst_width); + ro->src_y0 = bre_s(y0, ro->src_height, ro->dst_height); + ro->src_y1 = bre_s(y1, ro->src_height, ro->dst_height); + ro->src_offset = ro->src_y0 * ro->src_scan_len + ro->src_x0; + ro->dst_x0 = x0; + ro->dst_x1 = x1; + ro->dst_y0 = y0; + ro->dst_y1 = y1; + ro->dst_offset = ro->dst_y0 * ro->dst_scan_len + ro->dst_x0 * pixel_size; + REMAP_AREA_DEBUG_FUNC(ro); + ro->remap_func(ro); + } + else if(ro->remap_func_flags & RFF_REMAP_LINES) { + ro->src_x0 = 0; + ro->src_x1 = ro->src_width; + ro->src_y0 = bre_s(y0, ro->src_height, ro->dst_height); + ro->src_y1 = bre_s(y1, ro->src_height, ro->dst_height); + ro->src_offset = ro->src_y0 * ro->src_scan_len; + ro->dst_x0 = 0; + ro->dst_x1 = ro->dst_width; + ro->dst_y0 = y0; + ro->dst_y1 = y1; + ro->dst_offset = ro->dst_y0 * ro->dst_scan_len; + REMAP_AREA_DEBUG_FUNC(ro); + ro->remap_func(ro); + } + else { + ro->src_offset = ro->dst_offset = 0; + ro->src_x0 = ro->dst_x0 = ro->src_y0 = ro->dst_y0 = 0; + ro->src_x1 = ro->src_width; + ro->dst_x1 = ro->dst_width; + ro->src_y1 = ro->src_height; + ro->dst_y1 = ro->dst_height; + ra.x = ra.y = 0; + ra.width = ro->dst_width; + ra.height = ro->dst_height; + REMAP_AREA_DEBUG_FUNC(ro); + ro->remap_func(ro); + } + + return ra; +} + + +/* + * for CGA/Hercules-like modes + */ +static RectArea remap_mem_2(RemapObject *ro, int offset, int len) +{ + RectArea ra = {0, 0, 0, 0}; + int i1, i2, j1, j2; + + if(ro->state & ROS_REMAP_IGNORE) return ra; + if(ro->remap_func == NULL) return ra; + +#ifdef REMAP_AREA_DEBUG + fprintf(rdm, "remap_mem: ofs = %d, len = %d\n", offset, len); + fprintf(rdm, + " src: base = 0x%x, width = %d, height = %d, scan_len = %d\n", + (unsigned) ro->src_image, ro->src_width, ro->src_height, ro->src_scan_len + ); + fprintf(rdm, + " dst: base = 0x%x, width = %d, height = %d, scan_len = %d\n", + (unsigned) ro->dst_image, ro->dst_width, ro->dst_height, ro->dst_scan_len + ); +#endif + + if(offset < 0) len += offset, offset = 0; + if(len <= 0) return ra; + + i1 = offset / ro->src_scan_len; + i2 = offset % ro->src_scan_len; + j1 = (offset + len) / ro->src_scan_len; + j2 = (offset + len) % ro->src_scan_len; + + if(ro->src_mode == MODE_HERC) { + i1 <<= 2; + j1 <<= 2; j1 += 3; + } + else { /* CGA */ + i1 <<= 1; + j1 <<= 1; j1++; + } + + /* make sure it's all visible */ + if(i2 >= ro->src_width) i1++, i2 = 0, offset = i1 * ro->src_scan_len; + if(i1 >= ro->src_height || i1 > j1) return ra; + if(j2 >= ro->src_width) j1++, j2 = 0; + if(j1 >= ro->src_height) j1 = ro->src_height, j2 = 0; + + ra.width = ro->dst_width; + + if( + (ro->remap_func_flags & RFF_REMAP_RECT) || + (ro->remap_func_flags & RFF_REMAP_LINES) + ) { + if(ro->src_mode == MODE_HERC) { + ro->src_offset = (i1 >> 2) * ro->src_scan_len + (i1 & 3) * 0x2000; + } + else { /* CGA */ + ro->src_offset = (i1 >> 1) * ro->src_scan_len + (i1 & 1 ? 0x2000 : 0); + } + ro->src_x0 = ro->dst_x0 = 0; + ro->src_x1 = ro->src_width; ro->dst_x1 = ro->dst_width; + ro->src_y0 = i1; + ro->src_y1 = j1; + if(j2) ro->src_y1++; + ro->dst_y0 = bre_d_0(ro->src_y0, ro->src_height, ro->dst_height); + ro->dst_y1 = bre_d_0(ro->src_y1, ro->src_height, ro->dst_height); + ro->dst_offset = ro->dst_y0 * ro->dst_scan_len; + ra.y = ro->dst_y0; + ra.height = ro->dst_y1 - ro->dst_y0; + REMAP_AREA_DEBUG_FUNC(ro); + if(ro->dst_y0 != ro->dst_y1) { + ro->remap_func(ro); + } + } + else { + ro->src_offset = ro->dst_offset = 0; + ro->src_x0 = ro->dst_x0 = ro->src_y0 = ro->dst_y0 = 0; + ro->src_x1 = ro->src_width; + ro->dst_x1 = ro->dst_width; + ro->src_y1 = ro->src_height; + ro->dst_y1 = ro->dst_height; + ra.height = ro->dst_height; + REMAP_AREA_DEBUG_FUNC(ro); + ro->remap_func(ro); + } + + return ra; +} + + +/* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * + * some functions for managing the list of remap functions + * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + */ + +/* + * Search for the first suitable remap function. + */ +static RemapFuncDesc *find_remap_func(unsigned flags, int src_mode, int dst_mode, RemapFuncDesc *rfd) +{ + while(rfd != NULL) { + if((rfd->src_mode & src_mode) && + (rfd->dst_mode & dst_mode) && + (rfd->flags & (flags | RFF_BILIN_FILT | RFF_LIN_FILT)) == flags + ) break; + rfd = rfd->next; + } + return rfd; +} + + +/* + * Searches for the best remap function available. + * Prefers optimized ones over generic implementations and + * functions that remap rectangular areas over those that + * can remap only complete lines. + */ +static RemapFuncDesc *find_best_remap_func(unsigned flags, int src_mode, int dst_mode, RemapFuncDesc *rfd) +{ + RemapFuncDesc *rfd1 = NULL; + #define REMAB_COMBS 4 + unsigned f_list[6 * REMAB_COMBS]; + int features = 6; + int i; + + flags &= (RFF_LIN_FILT | RFF_BILIN_FILT | RFF_SCALE_ALL | RFF_SCALE_1 | RFF_SCALE_2); + + f_list[0] = flags | RFF_OPT_PENTIUM | RFF_REMAP_RECT; + f_list[1] = flags | RFF_OPT_PENTIUM | RFF_REMAP_LINES; + f_list[2] = flags | RFF_REMAP_RECT; + f_list[3] = flags | RFF_REMAP_LINES; + f_list[4] = flags | RFF_OPT_PENTIUM; + f_list[5] = flags; + + for(i = 0; i < 6; i++) { + f_list[features + i] = (f_list[i] & ~RFF_LIN_FILT) | RFF_BILIN_FILT; + f_list[features * 2 + i] = (f_list[i] & ~RFF_BILIN_FILT) | RFF_LIN_FILT; + f_list[features * 3 + i] = f_list[i] & ~(RFF_LIN_FILT | RFF_BILIN_FILT); + } + + features *= REMAB_COMBS; + + for(i = 0; i < features; i++) { + if((rfd1 = find_remap_func(f_list[i], src_mode, dst_mode, rfd)) != NULL) break; + } + + return rfd1; +} + + +/* + * Looks up all remap functions that are (possibly) needed + * for a particular mode. Does _not_ set ro->remap_func! + */ +static void install_remap_funcs(RemapObject *ro, int remap_features) +{ + if (remap_features & RFF_BILIN_FILT) + remap_features &= ~RFF_LIN_FILT; + ro->func_all = find_best_remap_func(remap_features | RFF_SCALE_ALL, ro->src_mode, ro->dst_mode, remap_list); + ro->func_1 = find_best_remap_func(remap_features | RFF_SCALE_1 , ro->src_mode, ro->dst_mode, remap_list); + ro->func_2 = find_best_remap_func(remap_features | RFF_SCALE_2 , ro->src_mode, ro->dst_mode, remap_list); + + if (ro->func_all) + ro->state |= ROS_SCALE_ALL; + /* accept partial scalers only if filtering matches or no full scaler */ + if (ro->func_1 && ((((ro->func_1->flags ^ remap_features) & + (RFF_BILIN_FILT | RFF_LIN_FILT)) == 0) + || !ro->func_all)) + ro->state |= ROS_SCALE_1; + if (ro->func_2 && ((((ro->func_2->flags ^ remap_features) & + (RFF_BILIN_FILT | RFF_LIN_FILT)) == 0) + || !ro->func_all)) + ro->state |= ROS_SCALE_2; + if (!ro->state) + error("remap function not found for mode %i\n", ro->src_mode); + + ro->supported_src_modes = find_supported_modes(ro->dst_mode); +} + +#if 0 +static int _find_supported_modes(unsigned dst_mode) +{ + int modes = 0; + RemapFuncDesc *rfd; + if(!base_init) { + do_base_init(); + base_init = 1; + } + rfd = remap_list; + while(rfd != NULL) { + if(rfd->dst_mode & dst_mode) modes |= rfd->src_mode; + rfd = rfd->next; + } + return modes; +} +#endif + +/* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * + * some not so optimized remap functions + * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + */ + +void gen_1to8_all(RemapObject *); +void gen_1to8p_all(RemapObject *); +void gen_1to16_all(RemapObject *); +void gen_1to24_all(RemapObject *); +void gen_1to32_all(RemapObject *); + +void gen_2to8_all(RemapObject *); +void gen_2to8p_all(RemapObject *); +void gen_2to16_all(RemapObject *); +void gen_2to24_all(RemapObject *); +void gen_2to32_all(RemapObject *); + +void gen_c2to8_all(RemapObject *); +void gen_c2to8p_all(RemapObject *); +void gen_c2to16_all(RemapObject *); +void gen_c2to24_all(RemapObject *); +void gen_c2to32_all(RemapObject *); + +void gen_4to8_all(RemapObject *); +void gen_4to8p_all(RemapObject *); +void gen_4to16_all(RemapObject *); +void gen_4to24_all(RemapObject *); +void gen_4to32_all(RemapObject *); + +void gen_8to8_all(RemapObject *); +void gen_8to8_1(RemapObject *); +void gen_8to8p_all(RemapObject *); +void gen_8to8p_1(RemapObject *); +void gen_8to16_all(RemapObject *); +void gen_8to16_lin(RemapObject *); +void gen_8to16_bilin(RemapObject *); +void gen_8to24_all(RemapObject *); +void gen_8to32_all(RemapObject *); +void gen_8to32_1(RemapObject *); +void gen_8to32_lin(RemapObject *); +void gen_8to32_bilin(RemapObject *); + +//void gen_15to16_all(RemapObject *); +//void gen_15to24_all(RemapObject *); +void gen_15to32_all(RemapObject *); +void gen_15to32_1(RemapObject *); + +//void gen_16to16_all(RemapObject *); +void gen_16to16_1(RemapObject *); +//void gen_16to24_all(RemapObject *); +void gen_16to32_all(RemapObject *); +void gen_16to32_1(RemapObject *); + +//void gen_24to24_all(RemapObject *); +void gen_24to24_1(RemapObject *); +void gen_24to32_all(RemapObject *); +void gen_24to32_1(RemapObject *); + +void gen_32to32_all(RemapObject *); +void gen_32to32_1(RemapObject *); + + +static RemapFuncDesc remap_gen_list[] = { + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_1 | MODE_CGA_1 | MODE_HERC, + MODE_TRUE_8, + gen_1to8_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_1 | MODE_CGA_1 | MODE_HERC, + MODE_PSEUDO_8, + gen_1to8p_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_1 | MODE_CGA_1 | MODE_HERC, + MODE_TRUE_15 | MODE_TRUE_16, + gen_1to16_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_1 | MODE_CGA_1 | MODE_HERC, + MODE_TRUE_24, + gen_1to24_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_1 | MODE_CGA_1 | MODE_HERC, + MODE_TRUE_32, + gen_1to32_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_2, + MODE_TRUE_8, + gen_2to8_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_2, + MODE_PSEUDO_8, + gen_2to8p_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_2, + MODE_TRUE_15 | MODE_TRUE_16, + gen_2to16_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_2, + MODE_TRUE_24, + gen_2to24_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_2, + MODE_TRUE_32, + gen_2to32_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_CGA_2, + MODE_TRUE_8, + gen_c2to8_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_CGA_2, + MODE_PSEUDO_8, + gen_c2to8p_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_CGA_2, + MODE_TRUE_15 | MODE_TRUE_16, + gen_c2to16_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_CGA_2, + MODE_TRUE_24, + gen_c2to24_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_CGA_2, + MODE_TRUE_32, + gen_c2to32_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_4, + MODE_TRUE_8, + gen_4to8_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_4, + MODE_PSEUDO_8, + gen_4to8p_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_4, + MODE_TRUE_15 | MODE_TRUE_16, + gen_4to16_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_4, + MODE_TRUE_24, + gen_4to24_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_4, + MODE_TRUE_32, + gen_4to32_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_X | MODE_PSEUDO_8, + MODE_TRUE_8, + gen_8to8_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_1 | RFF_REMAP_RECT, + MODE_PSEUDO_8, + MODE_TRUE_8, + gen_8to8_1, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_X | MODE_PSEUDO_8, + MODE_PSEUDO_8, + gen_8to8p_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_1 | RFF_REMAP_LINES, + MODE_PSEUDO_8, + MODE_PSEUDO_8, + gen_8to8p_1, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_X | MODE_PSEUDO_8, + MODE_TRUE_15 | MODE_TRUE_16, + gen_8to16_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES | RFF_LIN_FILT, + MODE_PSEUDO_8, + MODE_TRUE_15 | MODE_TRUE_16, + gen_8to16_lin, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES | RFF_BILIN_FILT, + MODE_PSEUDO_8, + MODE_TRUE_15 | MODE_TRUE_16, + gen_8to16_bilin, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_X | MODE_PSEUDO_8, + MODE_TRUE_24, + gen_8to24_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_VGA_X | MODE_PSEUDO_8, + MODE_TRUE_32, + gen_8to32_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_1 | RFF_REMAP_RECT, + MODE_PSEUDO_8, + MODE_TRUE_32, + gen_8to32_1, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES | RFF_LIN_FILT, + MODE_PSEUDO_8, + MODE_TRUE_32, + gen_8to32_lin, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES | RFF_BILIN_FILT, + MODE_PSEUDO_8, + MODE_TRUE_32, + gen_8to32_bilin, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_TRUE_15, + MODE_TRUE_32, + gen_15to32_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_1 | RFF_REMAP_LINES, + MODE_TRUE_15, + MODE_TRUE_32, + gen_15to32_1, + NULL + ), + + REMAP_DESC( + RFF_SCALE_1 | RFF_REMAP_LINES, + MODE_TRUE_15, + MODE_TRUE_15, + gen_16to16_1, + NULL + ), + + REMAP_DESC( + RFF_SCALE_1 | RFF_REMAP_LINES, + MODE_TRUE_16, + MODE_TRUE_16, + gen_16to16_1, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_TRUE_16, + MODE_TRUE_32, + gen_16to32_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_1 | RFF_REMAP_LINES, + MODE_TRUE_16, + MODE_TRUE_32, + gen_16to32_1, + NULL + ), + + REMAP_DESC( + RFF_SCALE_1 | RFF_REMAP_LINES, + MODE_TRUE_24, + MODE_TRUE_24, + gen_24to24_1, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_TRUE_24, + MODE_TRUE_32, + gen_24to32_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_1 | RFF_REMAP_LINES, + MODE_TRUE_24, + MODE_TRUE_32, + gen_24to32_1, + NULL + ), + + REMAP_DESC( + RFF_SCALE_ALL | RFF_REMAP_LINES, + MODE_TRUE_32, + MODE_TRUE_32, + gen_32to32_all, + NULL + ), + + REMAP_DESC( + RFF_SCALE_1 | RFF_REMAP_LINES, + MODE_TRUE_32, + MODE_TRUE_32, + gen_32to32_1, + NULL + ), + +}; + +/* + * returns chained list of modes + */ +RemapFuncDesc *remap_gen(void) +{ + int i; + + for(i = 0; i < sizeof(remap_gen_list) / sizeof(*remap_gen_list) - 1; i++) { + remap_gen_list[i].next = remap_gen_list + i + 1; + } + + return remap_gen_list; +} + + +/* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * + * now the implementation + * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + */ + +/* + * 1 bit pseudo color --> 8 bit true color (shared color map) + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_1to8_all(RemapObject *ro) +{ + int k; + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + const unsigned char *src, *src0; + unsigned char *dst; + unsigned char *lut = (unsigned char *)ro->true_color_lut; + unsigned char c0; + int i; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + k = (d_y & 1) << 1; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + i = s_x >> 3; + c0 = src[i]; + i = (s_x & 7) ^ 7; + c0 >>= i; + c0 &= 1; + dst[d_x++] = lut[4 * c0 + (k ^= 1)]; + s_x += *(bre_x++); + } + } +} + +/* + * 1 bit pseudo color --> 8 bit pseudo color (private color map) + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_1to8p_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + unsigned char c0; + int i; + + const unsigned char *src, *src0; + unsigned char *dst; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + i = s_x >> 3; + c0 = src[i]; + i = (s_x & 7) ^ 7; + c0 >>= i; + c0 &= 1; + dst[d_x++] = c0; + s_x += *(bre_x++); + } + } +} + +/* + * 1 bit pseudo color --> 15/16 bit true color + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_1to16_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 1; + int *bre_x; + int *bre_y = ro->bre_y; + unsigned char c0; + + const unsigned char *src, *src0; + unsigned short *dst; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned short *)(ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + c0 = (src[s_x >> 3] >> ((s_x & 7) ^ 7)) & 1; + dst[d_x++] = ro->true_color_lut[c0]; + s_x += *(bre_x++); + } + } +} + +/* + * 1 bit pseudo color --> 24 bit true color + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_1to24_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + unsigned char c0; + + const unsigned char *src, *src0; + unsigned char *dst; + unsigned color; + + src0 = ro->src_image + ro->src_start; + dst = (ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width * 3; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + c0 = (src[s_x >> 3] >> ((s_x & 7) ^ 7)) & 1; + color = ro->true_color_lut[c0]; + dst[d_x++] = color & 0xFF; + dst[d_x++] = (color >> 8) & 0xFF; + dst[d_x++] = (color >> 16) & 0xFF; + s_x += *(bre_x++); + } + } +} + +/* + * 1 bit pseudo color --> 32 bit true color + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_1to32_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 2; + int *bre_x; + int *bre_y = ro->bre_y; + unsigned char c0; + + const unsigned char *src, *src0; + unsigned *dst; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned *)(ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + c0 = (src[s_x >> 3] >> ((s_x & 7) ^ 7)) & 1; + dst[d_x++] = ro->true_color_lut[c0]; + s_x += *(bre_x++); + } + } +} + +/* + * 2 bit VGA pseudo color --> 8 bit true color (shared color map) + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_2to8_all(RemapObject *ro) +{ + int k; + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + const unsigned char *src, *src0; + unsigned char *dst; + unsigned char *lut = (unsigned char *)ro->true_color_lut; + unsigned char c0, c1; + int i; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + k = (d_y & 1) << 1; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + i = s_x >> 3; + c0 = src[i]; + c1 = src[i + 0x20000]; + i = (s_x & 7) ^ 7; + c0 >>= i; + c1 >>= i; + c0 &= 1; + c1 &= 1; + c0 |= c1 << 1; + dst[d_x++] = lut[4 * c0 + (k ^= 1)]; + s_x += *(bre_x++); + } + } +} + +/* + * 2 bit VGA pseudo color --> 8 bit pseudo color (private color map) + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_2to8p_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + unsigned char c0, c1; + int i; + + const unsigned char *src, *src0; + unsigned char *dst; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + i = s_x >> 3; + c0 = src[i]; + c1 = src[i + 0x20000]; + i = (s_x & 7) ^ 7; + c0 >>= i; + c1 >>= i; + c0 &= 1; + c1 &= 1; + c0 |= c1 << 1; + dst[d_x++] = c0; + s_x += *(bre_x++); + } + } +} + +/* + * 2 bit VGA pseudo color --> 15/16 bit true color + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_2to16_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 1; + int *bre_x; + int *bre_y = ro->bre_y; + unsigned char c0, c1; + int i; + + const unsigned char *src, *src0; + unsigned short *dst; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned short *)(ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + i = s_x >> 3; + c0 = src[i]; + c1 = src[i + 0x20000]; + i = (s_x & 7) ^ 7; + c0 >>= i; + c1 >>= i; + c0 &= 1; + c1 &= 1; + c0 |= (c1 << 1); + dst[d_x++] = ro->true_color_lut[c0]; + s_x += *(bre_x++); + } + } +} + +/* + * 2 bit VGA pseudo color --> 24 bit true color + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_2to24_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + unsigned char c0, c1; + int i; + + const unsigned char *src, *src0; + unsigned char *dst; + unsigned color; + + src0 = ro->src_image + ro->src_start; + dst = (ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width * 3; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + i = s_x >> 3; + c0 = src[i]; + c1 = src[i + 0x20000]; + i = (s_x & 7) ^ 7; + c0 >>= i; + c1 >>= i; + c0 &= 1; + c1 &= 1; + c0 |= c1 << 1; + color = ro->true_color_lut[c0]; + dst[d_x++] = color & 0xFF; + dst[d_x++] = (color >> 8) & 0xFF; + dst[d_x++] = (color >> 16) & 0xFF; + s_x += *(bre_x++); + } + } +} + +/* + * 2 bit VGA pseudo color --> 32 bit true color + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_2to32_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 2; + int *bre_x; + int *bre_y = ro->bre_y; + unsigned char c0, c1; + int i; + + const unsigned char *src, *src0; + unsigned *dst; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned *)(ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + i = s_x >> 3; + c0 = src[i]; + c1 = src[i + 0x20000]; + i = (s_x & 7) ^ 7; + c0 >>= i; + c1 >>= i; + c0 &= 1; + c1 &= 1; + c0 |= c1 << 1; + dst[d_x++] = ro->true_color_lut[c0]; + s_x += *(bre_x++); + } + } +} + +/* + * 2 bit CGA pseudo color --> 8 bit true color (shared color map) + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_c2to8_all(RemapObject *ro) +{ + int k; + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + const unsigned char *src, *src0; + unsigned char *dst; + unsigned char *lut = (unsigned char *)ro->true_color_lut; + unsigned char c0; + int i; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + k = (d_y & 1) << 1; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + i = s_x >> 2; + c0 = src[i]; + i = 2 * ((s_x & 3) ^ 3); + c0 >>= i; + c0 &= 3; + dst[d_x++] = lut[4 * c0 + (k ^= 1)]; + s_x += *(bre_x++); + } + } +} + +/* + * 2 bit CGA pseudo color --> 8 bit pseudo color (private color map) + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_c2to8p_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + unsigned char c0; + int i; + + const unsigned char *src, *src0; + unsigned char *dst; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + i = s_x >> 2; + c0 = src[i]; + i = 2 * ((s_x & 3) ^ 3); + c0 >>= i; + c0 &= 3; + dst[d_x++] = c0; + s_x += *(bre_x++); + } + } +} + +/* + * 2 bit CGA pseudo color --> 15/16 bit true color + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_c2to16_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 1; + int *bre_x; + int *bre_y = ro->bre_y; + unsigned char c0; + + const unsigned char *src, *src0; + unsigned short *dst; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned short *)(ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + c0 = (src[s_x >> 2] >> (2 * ((s_x & 3) ^ 3))) & 3; + dst[d_x++] = ro->true_color_lut[c0]; + s_x += *(bre_x++); + } + } +} + +/* + * 2 bit CGA pseudo color --> 24 bit true color + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_c2to24_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + unsigned char c0; + + const unsigned char *src, *src0; + unsigned char *dst; + unsigned color; + + src0 = ro->src_image + ro->src_start; + dst = (ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width * 3; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + c0 = (src[s_x >> 2] >> (2 * ((s_x & 3) ^ 3))) & 3; + color = ro->true_color_lut[c0]; + dst[d_x++] = color & 0xFF; + dst[d_x++] = (color >> 8) & 0xFF; + dst[d_x++] = (color >> 16) & 0xFF; + s_x += *(bre_x++); + } + } +} + +/* + * 2 bit CGA pseudo color --> 32 bit true color + * supports arbitrary scaling + * + * -- very basic and slow -- + */ +void gen_c2to32_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 2; + int *bre_x; + int *bre_y = ro->bre_y; + unsigned char c0; + + const unsigned char *src, *src0; + unsigned *dst; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned *)(ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + c0 = (src[s_x >> 2] >> (2 * ((s_x & 3) ^ 3))) & 3; + dst[d_x++] = ro->true_color_lut[c0]; + s_x += *(bre_x++); + } + } +} + +/* + * 4 bit pseudo color --> 8 bit true color (shared color map) + * supports arbitrary scaling + * + */ +void gen_4to8_all(RemapObject *ro) +{ + int k; + int d_x_len, s_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0, *src_last; + unsigned char *clut = (unsigned char*) ro->true_color_lut, *dst, *src1; + unsigned *dst1, *lut; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + s_x_len = ro->src_width >> 3; + src1 = ro->src_tmp_line; + dst1 = (unsigned *) src1; + lut = ro->bit_lut; + src_last = NULL; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + k = (d_y & 1) << 1; + if(src != src_last) { + src_last = src; + for(s_x = d_x = 0; s_x < s_x_len; s_x++, d_x += 2) { + dst1[d_x ] = lut[2 * src[s_x ] ] | + lut[2 * src[s_x + 0x10000] + 0x200] | + lut[2 * src[s_x + 0x20000] + 0x400] | + lut[2 * src[s_x + 0x30000] + 0x600]; + dst1[d_x + 1] = lut[2 * src[s_x ] + 1 ] | + lut[2 * src[s_x + 0x10000] + 1 + 0x200] | + lut[2 * src[s_x + 0x20000] + 1 + 0x400] | + lut[2 * src[s_x + 0x30000] + 1 + 0x600]; + } + } + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + dst[d_x++] = clut[4 * src1[s_x] + (k ^= 1)]; + s_x += *(bre_x++); + } + } +} + +/* + * 4 bit pseudo color --> 8 bit pseudo color (private color map) + * supports arbitrary scaling + * + */ +void gen_4to8p_all(RemapObject *ro) +{ + int d_x_len, s_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0, *src_last; + unsigned char *dst, *src1; + unsigned *dst1, *lut; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + s_x_len = ro->src_width >> 3; + src1 = ro->src_tmp_line; + dst1 = (unsigned *) src1; + lut = ro->bit_lut; + src_last = NULL; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + if(src != src_last) { + src_last = src; + for(s_x = d_x = 0; s_x < s_x_len; s_x++, d_x += 2) { + dst1[d_x ] = lut[2 * src[s_x ] ] | + lut[2 * src[s_x + 0x10000] + 0x200] | + lut[2 * src[s_x + 0x20000] + 0x400] | + lut[2 * src[s_x + 0x30000] + 0x600]; + dst1[d_x + 1] = lut[2 * src[s_x ] + 1 ] | + lut[2 * src[s_x + 0x10000] + 1 + 0x200] | + lut[2 * src[s_x + 0x20000] + 1 + 0x400] | + lut[2 * src[s_x + 0x30000] + 1 + 0x600]; + } + } + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + dst[d_x++] = src1[s_x]; + s_x += *(bre_x++); + } + } +} + +/* + * 4 bit pseudo color --> 15/16 bit true color + * supports arbitrary scaling + * + */ +void gen_4to16_all(RemapObject *ro) +{ + int d_x_len, s_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 1; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0, *src_last; + unsigned char *src1; + unsigned short *dst; + unsigned *dst1, *lut; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned short *) (ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + s_x_len = ro->src_width >> 3; + src1 = ro->src_tmp_line; + dst1 = (unsigned *) src1; + lut = ro->bit_lut; + src_last = NULL; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + if(src != src_last) { + src_last = src; + for(s_x = d_x = 0; s_x < s_x_len; s_x++, d_x += 2) { + dst1[d_x ] = lut[2 * src[s_x ] ] | + lut[2 * src[s_x + 0x10000] + 0x200] | + lut[2 * src[s_x + 0x20000] + 0x400] | + lut[2 * src[s_x + 0x30000] + 0x600]; + dst1[d_x + 1] = lut[2 * src[s_x ] + 1 ] | + lut[2 * src[s_x + 0x10000] + 1 + 0x200] | + lut[2 * src[s_x + 0x20000] + 1 + 0x400] | + lut[2 * src[s_x + 0x30000] + 1 + 0x600]; + } + } + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + dst[d_x++] = ro->true_color_lut[src1[s_x]]; + s_x += *(bre_x++); + } + } +} + +/* + * 4 bit pseudo color --> 24 bit true color + * supports arbitrary scaling + * + */ +void gen_4to24_all(RemapObject *ro) +{ + int d_x_len, s_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + + unsigned *dst1, *lut; + const unsigned char *src, *src0, *src_last; + unsigned char *dst, *src1; + unsigned color; + + src0 = ro->src_image + ro->src_start; + dst = (ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width *3; + s_x_len = ro->src_width >> 3; + src1 = ro->src_tmp_line; + dst1 = (unsigned *) src1; + lut = ro->bit_lut; + src_last = NULL; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + if(src != src_last) { + src_last = src; + for(s_x = d_x = 0; s_x < s_x_len; s_x++, d_x += 2) { + dst1[d_x ] = lut[2 * src[s_x ] ] | + lut[2 * src[s_x + 0x10000] + 0x200] | + lut[2 * src[s_x + 0x20000] + 0x400] | + lut[2 * src[s_x + 0x30000] + 0x600]; + dst1[d_x + 1] = lut[2 * src[s_x ] + 1 ] | + lut[2 * src[s_x + 0x10000] + 1 + 0x200] | + lut[2 * src[s_x + 0x20000] + 1 + 0x400] | + lut[2 * src[s_x + 0x30000] + 1 + 0x600]; + } + } + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + color = ro->true_color_lut[src1[s_x]]; + dst[d_x++] = color & 0xFF; + dst[d_x++] = (color >> 8) & 0xFF; + dst[d_x++] = (color >> 16) & 0xFF; + s_x += *(bre_x++); + } + } +} + +/* + * 4 bit pseudo color --> 32 bit true color + * supports arbitrary scaling + * + */ +void gen_4to32_all(RemapObject *ro) +{ + int d_x_len, s_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 2; + int *bre_x; + int *bre_y = ro->bre_y; + + unsigned *dst1, *lut; + const unsigned char *src, *src0, *src_last; + unsigned char *src1; + unsigned *dst; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned *) (ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + s_x_len = ro->src_width >> 3; + src1 = ro->src_tmp_line; + dst1 = (unsigned *) src1; + lut = ro->bit_lut; + src_last = NULL; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + if(src != src_last) { + src_last = src; + for(s_x = d_x = 0; s_x < s_x_len; s_x++, d_x += 2) { + dst1[d_x ] = lut[2 * src[s_x ] ] | + lut[2 * src[s_x + 0x10000] + 0x200] | + lut[2 * src[s_x + 0x20000] + 0x400] | + lut[2 * src[s_x + 0x30000] + 0x600]; + dst1[d_x + 1] = lut[2 * src[s_x ] + 1 ] | + lut[2 * src[s_x + 0x10000] + 1 + 0x200] | + lut[2 * src[s_x + 0x20000] + 1 + 0x400] | + lut[2 * src[s_x + 0x30000] + 1 + 0x600]; + } + } + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + dst[d_x++] = ro->true_color_lut[src1[s_x]]; + s_x += *(bre_x++); + } + } +} + +/* + * 8 bit pseudo color --> 8 bit true color (shared color map) + * supports arbitrary scaling + */ +void gen_8to8_all(RemapObject *ro) +{ + int k; + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + const unsigned char *src, *src0; + unsigned char *dst; + unsigned char *lut = (unsigned char *)ro->true_color_lut; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + + for (d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + k = (d_y & 1) << 1; + for (s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len;) { + dst[d_x++] = lut[4 * src[s_x] + (k ^= 1)]; + s_x += *(bre_x++); + } + } +} + +/* + * 8 bit pseudo color --> 8 bit true color (shared color map) + */ +void gen_8to8_1(RemapObject *ro) +{ + int i, j, l, k; + const unsigned char *src; + unsigned char *dst; + unsigned char *lut = (unsigned char *)ro->true_color_lut; + + src = ro->src_image + ro->src_start + ro->src_offset; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + + l = ro->src_x1 - ro->src_x0; + + for (j = ro->src_y0; j < ro->src_y1; j++) { + k = (j & 1) << 1; + for (i = 0; i < l; i++) { + dst[i] = lut[4 * src[i] + (k ^= 1)]; + } + dst += ro->dst_scan_len; + src += ro->src_scan_len; + } +} + +/* + * 8 bit pseudo color --> 8 bit pseudo color (private color map) + * supports arbitrary scaling + */ +void gen_8to8p_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0; + unsigned char *dst; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + dst[d_x++] = src[s_x]; + s_x += *(bre_x++); + } + } +} + +/* + * 8 bit pseudo color --> 8 bit pseudo color (private color map) + */ +void gen_8to8p_1(RemapObject *ro) +{ + int i; + const unsigned char *src; + unsigned char *dst; + + src = ro->src_image + ro->src_start + ro->src_offset; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + + for(i = ro->src_y0; i < ro->src_y1; i++) { + memcpy(dst, src, ro->src_width); + src += ro->src_scan_len; + dst += ro->dst_scan_len; + } +} + +/* + * 8 bit pseudo color --> 15/16 bit true color + * supports arbitrary scaling + */ +void gen_8to16_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 1; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0; + unsigned short *dst; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned short *) (ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + dst[d_x++] = ro->true_color_lut[src[s_x]]; + s_x += *(bre_x++); + } + } +} + +/* + * 8 bit pseudo color --> 15/16 bit true color + * supports arbitrary scaling + */ +void gen_8to16_lin(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 1; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0; + unsigned short *dst; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned short *) (ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + switch(*(bre_x + d_x_len)) { + case 0: + dst[d_x++] = ro->true_color_lut[src[s_x]]; + break; + case 1: + dst[d_x++] = ro->true_color_lut[src[s_x] + LUT_OFS_67] + ro->true_color_lut[src[s_x + 1] + LUT_OFS_33]; + break; + case 2: + dst[d_x++] = ro->true_color_lut[src[s_x] + LUT_OFS_33] + ro->true_color_lut[src[s_x + 1] + LUT_OFS_67]; + break; + default: + fprintf(stderr, "***** oops\n"); + } + s_x += *(bre_x++); + } + } +} + +/* + * 8 bit pseudo color --> 15/16 bit true color + * supports arbitrary scaling + */ +void gen_8to16_bilin(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 1; + int s_scan_len = ro->src_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0; + unsigned short *dst; + unsigned *lut = ro->true_color_lut; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned short *) (ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + + switch(*(bre_y + d_y - 1 + ro->dst_height)) { + case 0: + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + switch(*(bre_x + d_x_len)) { + case 0: + dst[d_x++] = lut[src[s_x]]; + break; + case 1: + dst[d_x++] = lut[src[s_x] + LUT_OFS_67] + lut[src[s_x + 1] + LUT_OFS_33]; + break; + case 2: + dst[d_x++] = lut[src[s_x] + LUT_OFS_33] + lut[src[s_x + 1] + LUT_OFS_67]; + break; + default: + fprintf(stderr, "***** oops\n"); + } + s_x += *(bre_x++); + } + break; + + case 1: + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + switch(*(bre_x + d_x_len)) { + case 0: + dst[d_x++] = lut[src[s_x ] + LUT_OFS_67] + + lut[src[s_x + s_scan_len] + LUT_OFS_33]; + break; + case 1: + dst[d_x++] = lut[src[s_x ] + LUT_OFS_45] + lut[src[s_x + 1 ] + LUT_OFS_22] + + lut[src[s_x + s_scan_len] + LUT_OFS_22] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_11]; + break; + case 2: + dst[d_x++] = lut[src[s_x ] + LUT_OFS_22] + lut[src[s_x + 1 ] + LUT_OFS_45] + + lut[src[s_x + s_scan_len] + LUT_OFS_11] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_22]; + break; + default: + fprintf(stderr, "***** oops\n"); + } + s_x += *(bre_x++); + } + break; + + case 2: + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + switch(*(bre_x + d_x_len)) { + case 0: + dst[d_x++] = lut[src[s_x ] + LUT_OFS_33] + + lut[src[s_x + s_scan_len] + LUT_OFS_67]; + break; + case 1: + dst[d_x++] = lut[src[s_x ] + LUT_OFS_22] + lut[src[s_x + 1 ] + LUT_OFS_11] + + lut[src[s_x + s_scan_len] + LUT_OFS_45] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_22]; + break; + case 2: + dst[d_x++] = lut[src[s_x ] + LUT_OFS_11] + lut[src[s_x + 1 ] + LUT_OFS_22] + + lut[src[s_x + s_scan_len] + LUT_OFS_22] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_45]; + break; + default: + fprintf(stderr, "***** oops\n"); + } + s_x += *(bre_x++); + } + break; + + default: + fprintf(stderr, "###### oops\n"); + } + + } +} + +/* + * 8 bit pseudo color --> 24 bit true color + * supports arbitrary scaling + */ +void gen_8to24_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0; + unsigned char *dst; + unsigned long long color; + + src0 = ro->src_image + ro->src_start; + dst = (ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width *3; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + color = ro->true_color_lut[src[s_x]]; + dst[d_x++] = color & 0xFF; + dst[d_x++] = (color >> 8) & 0xFF; + dst[d_x++] = (color >> 16) & 0xFF; + s_x += *(bre_x++); + } + } +} + +/* + * 8 bit pseudo color --> 32 bit true color + * supports arbitrary scaling + */ +void gen_8to32_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 2; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0; + unsigned *dst; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned *) (ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + dst[d_x++] = ro->true_color_lut[src[s_x]]; + s_x += *(bre_x++); + } + } +} + +/* + * 8 bit pseudo color --> 32 bit true color + */ +void gen_8to32_1(RemapObject *ro) +{ + int i, j, l; + const unsigned char *src; + unsigned *dst; + + src = ro->src_image + ro->src_start + ro->src_offset; + dst = (unsigned *) (ro->dst_image + ro->dst_start + ro->dst_offset); + l = (ro->src_x1 - ro->src_x0); + + for(j = ro->src_y0; j < ro->src_y1; j++) { + for(i = 0; i < l; i++) { + dst[i] = ro->true_color_lut[src[i]]; + } + dst += ro->dst_scan_len >> 2; + src += ro->src_scan_len; + } +} + +/* + * 8 bit pseudo color --> 32 bit true color + * supports arbitrary scaling + */ +void gen_8to32_lin(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 2; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0; + unsigned *dst; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned *) (ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + switch(*(bre_x + d_x_len)) { + case 0: + dst[d_x++] = ro->true_color_lut[src[s_x]]; + break; + case 1: + dst[d_x++] = ro->true_color_lut[src[s_x] + LUT_OFS_67] + ro->true_color_lut[src[s_x + 1] + LUT_OFS_33]; + break; + case 2: + dst[d_x++] = ro->true_color_lut[src[s_x] + LUT_OFS_33] + ro->true_color_lut[src[s_x + 1] + LUT_OFS_67]; + break; + default: + fprintf(stderr, "***** oops\n"); + } + s_x += *(bre_x++); + } + } +} + +/* + * 8 bit pseudo color --> 32 bit true color + * supports arbitrary scaling + */ +void gen_8to32_bilin(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len >> 2; + int s_scan_len = ro->src_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0; + unsigned *dst; + unsigned *lut = ro->true_color_lut; + + src0 = ro->src_image + ro->src_start; + dst = (unsigned *) (ro->dst_image + ro->dst_start + ro->dst_offset); + d_x_len = ro->dst_width; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + + switch(*(bre_y + d_y - 1 + ro->dst_height)) { + case 0: + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + switch(*(bre_x + d_x_len)) { + case 0: + dst[d_x++] = lut[src[s_x]]; + break; + case 1: + dst[d_x++] = lut[src[s_x] + LUT_OFS_67] + lut[src[s_x + 1] + LUT_OFS_33]; + break; + case 2: + dst[d_x++] = lut[src[s_x] + LUT_OFS_33] + lut[src[s_x + 1] + LUT_OFS_67]; + break; + default: + fprintf(stderr, "***** oops\n"); + } + s_x += *(bre_x++); + } + break; + + case 1: + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + switch(*(bre_x + d_x_len)) { + case 0: + dst[d_x++] = lut[src[s_x ] + LUT_OFS_67] + + lut[src[s_x + s_scan_len] + LUT_OFS_33]; + break; + case 1: + dst[d_x++] = lut[src[s_x ] + LUT_OFS_45] + lut[src[s_x + 1 ] + LUT_OFS_22] + + lut[src[s_x + s_scan_len] + LUT_OFS_22] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_11]; + break; + case 2: + dst[d_x++] = lut[src[s_x ] + LUT_OFS_22] + lut[src[s_x + 1 ] + LUT_OFS_45] + + lut[src[s_x + s_scan_len] + LUT_OFS_11] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_22]; + break; + default: + fprintf(stderr, "***** oops\n"); + } + s_x += *(bre_x++); + } + break; + + case 2: + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + switch(*(bre_x + d_x_len)) { + case 0: + dst[d_x++] = lut[src[s_x ] + LUT_OFS_33] + + lut[src[s_x + s_scan_len] + LUT_OFS_67]; + break; + case 1: + dst[d_x++] = lut[src[s_x ] + LUT_OFS_22] + lut[src[s_x + 1 ] + LUT_OFS_11] + + lut[src[s_x + s_scan_len] + LUT_OFS_45] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_22]; + break; + case 2: + dst[d_x++] = lut[src[s_x ] + LUT_OFS_11] + lut[src[s_x + 1 ] + LUT_OFS_22] + + lut[src[s_x + s_scan_len] + LUT_OFS_22] + lut[src[s_x + s_scan_len + 1] + LUT_OFS_45]; + break; + default: + fprintf(stderr, "***** oops\n"); + } + s_x += *(bre_x++); + } + break; + + default: + fprintf(stderr, "###### oops\n"); + } + + } +} + +/* + * 15 bit true color --> 32 bit true color + * supports arbitrary scaling + */ +void gen_15to32_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0; + unsigned char *dst; + const unsigned short *src_2; + unsigned *dst_4; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + src_2 = (const unsigned short *) src; + dst_4 = (unsigned *) dst; + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + dst_4[d_x++] = bgr_2int(ro->dst_color_space, 5, 5, 5, src_2[s_x]); + s_x += *(bre_x++); + } + } +} + +/* + * 15 bit true color --> 32 bit true color + * Source format is BGR (see vesa.c:vbe_mode_info() ) + */ +void gen_15to32_1(RemapObject *ro) +{ + int i, j; + const unsigned char *src; + unsigned char *dst; + const unsigned short *src_2; + unsigned *dst_4; + + src = ro->src_image + ro->src_start + ro->src_offset; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + + for(i = ro->src_y0; i < ro->src_y1; i++) { + src_2 = (const unsigned short *)src; + dst_4 = (unsigned *) dst; + + for(j = 0; j < ro->dst_width; j++) { + // get 5-bit color values + // (green channel is cut between two byte values) + // [0] gggbbbbb + // [1] 0rrrrrgg + *dst_4++ = bgr_2int(ro->dst_color_space, 5, 5, 5, *src_2++); + } + + src += ro->src_scan_len; + dst += ro->dst_scan_len; + } +} + +/* + * 16 bit true color --> 16 bit true color + * 15 bit true color --> 15 bit true color + * Source format is BGR (see vesa.c:vbe_mode_info() ) + * *** ignores color space description *** + */ +void gen_16to16_1(RemapObject *ro) +{ + int i; + const unsigned char *src; + unsigned char *dst; + + src = ro->src_image + ro->src_start + ro->src_offset; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + + for(i = ro->src_y0; i < ro->src_y1; i++) { + memcpy(dst, src, ro->src_width << 1); + src += ro->src_scan_len; + dst += ro->dst_scan_len; + } +} + +/* + * 16 bit true color --> 32 bit true color + * supports arbitrary scaling + */ +void gen_16to32_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0; + unsigned char *dst; + const unsigned short *src_2; + unsigned *dst_4; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + src_2 = (const unsigned short *) src; + dst_4 = (unsigned *) dst; + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + dst_4[d_x++] = bgr_2int(ro->dst_color_space, 5, 6, 5, src_2[s_x]); + s_x += *(bre_x++); + } + } +} + +/* + * 16 bit true color --> 32 bit true color + * Source format is BGR (see vesa.c:vbe_mode_info() ) + */ +void gen_16to32_1(RemapObject *ro) +{ + int i, j; + const unsigned char *src; + unsigned char *dst; + const unsigned short *src_2; + unsigned *dst_4; + + src = ro->src_image + ro->src_start + ro->src_offset; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + + for(i = ro->src_y0; i < ro->src_y1; i++) { + src_2 = (const unsigned short *)src; + dst_4 = (unsigned *) dst; + + for(j = 0; j < ro->dst_width; j++) { + // get 5-bit/6-bit color values + // (green channel is cut between two byte values) + // [0] gggbbbbb + // [1] rrrrrggg + *dst_4++ = bgr_2int(ro->dst_color_space, 5, 6, 5, *src_2++); + } + + src += ro->src_scan_len; + dst += ro->dst_scan_len; + } +} + +/* + * 24 bit true color --> 24 bit true color + * Source format is BGR (see vesa.c:vbe_mode_info() ) + * *** ignores color space description *** + */ +void gen_24to24_1(RemapObject *ro) +{ + int i; + const unsigned char *src; + unsigned char *dst; + + src = ro->src_image + ro->src_start + ro->src_offset; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + + for(i = ro->src_y0; i < ro->src_y1; i++) { + memcpy(dst, src, ro->src_width * 3); + src += ro->src_scan_len; + dst += ro->dst_scan_len; + } +} + +/* + * 24 bit true color --> 32 bit true color + * supports arbitrary scaling + */ +void gen_24to32_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0; + unsigned char *dst; + unsigned *dst_4; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + dst_4 = (unsigned *) dst; + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + RGBColor c = { src[s_x * 3 + 2], src[s_x * 3 + 1], src[s_x * 3] }; + dst_4[d_x++] = rgb_color_2int(ro->dst_color_space, 8, 8, 8, c); + s_x += *(bre_x++); + } + } +} + +/* + * 24 bit true color --> 32 bit true color + * Source format is BGR (see vesa.c:vbe_mode_info() ) + */ +void gen_24to32_1(RemapObject *ro) +{ + int i, j; + const unsigned char *src, *src_1; + unsigned char *dst; + unsigned *dst_4; + + src = ro->src_image + ro->src_start + ro->src_offset; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + + for(i = ro->src_y0; i < ro->src_y1; i++) { + src_1 = src; + dst_4 = (unsigned *) dst; + + for(j = 0; j < ro->src_width; j++) { + RGBColor c = { src_1[2], src_1[1], src_1[0] }; + + *dst_4++ = rgb_color_2int(ro->dst_color_space, 8, 8, 8, c); + src_1 += 3; + } + + src += ro->src_scan_len; + dst += ro->dst_scan_len; + } +} + +/* + * 32 bit true color --> 32 bit true color + * supports arbitrary scaling + */ +void gen_32to32_all(RemapObject *ro) +{ + int d_x_len; + int s_x, d_x, d_y; + int d_scan_len = ro->dst_scan_len; + int *bre_x; + int *bre_y = ro->bre_y; + + const unsigned char *src, *src0; + unsigned char *dst; + unsigned *dst_4; + + src0 = ro->src_image + ro->src_start; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + d_x_len = ro->dst_width; + + for(d_y = ro->dst_y0; d_y < ro->dst_y1; dst += d_scan_len) { + src = src0 + bre_y[d_y++]; + dst_4 = (unsigned *) dst; + for(s_x = d_x = 0, bre_x = ro->bre_x; d_x < d_x_len; ) { + RGBColor c = { src[s_x * 4 + 2], src[s_x * 4 + 1], src[s_x * 4] }; + dst_4[d_x++] = rgb_color_2int(ro->dst_color_space, 8, 8, 8, c); + s_x += *(bre_x++); + } + } +} + +/* + * 32 bit true color --> 32 bit true color + * Source format is BGR (see vesa.c:vbe_mode_info() ) + * *** ignores color space description *** + */ +void gen_32to32_1(RemapObject *ro) +{ + int i; + const unsigned char *src; + unsigned char *dst; + + src = ro->src_image + ro->src_start + ro->src_offset; + dst = ro->dst_image + ro->dst_start + ro->dst_offset; + + for(i = ro->src_y0; i < ro->src_y1; i++) { + memcpy(dst, src, ro->src_width << 2); + src += ro->src_scan_len; + dst += ro->dst_scan_len; + } +} + +#define RO(p) (*(RemapObject **)p) + +static RemapObject *re_create_obj(RemapObject *old, int new_mode) +{ + RemapObject *dst = _remap_init(new_mode, old->dst_mode, + old->features, old->dst_color_space, old->gamma); + if (old->color_lut_size && dst->color_lut_size == old->color_lut_size) + memcpy(dst->true_color_lut, old->true_color_lut, dst->color_lut_size * + sizeof(*dst->true_color_lut)); + else + dirty_all_vga_colors(); + _remap_done(old); + return dst; +} + +static int _remap_palette_update(void *ros, unsigned i, + unsigned bits, unsigned r, unsigned g, unsigned b) +{ + RemapObject *ro = RO(ros); + return ro->palette_update(ro, i, bits, r, g, b); +} + +static RectArea _remap_remap_rect(void *ros, const struct bitmap_desc src_img, + int src_mode, + int x0, int y0, int width, int height, + struct bitmap_desc dst_img) +{ + RemapObject *ro = RO(ros); + if (src_mode != ro->src_mode) + RO(ros) = ro = re_create_obj(ro, src_mode); + ro->src_image = src_img.img; + ro->src_start = 0; + ro->dst_image = dst_img.img; + ro->src_resize(ro, src_img.width, src_img.height, src_img.scan_len); + ro->dst_resize(ro, dst_img.width, dst_img.height, dst_img.scan_len); + return ro->remap_rect(ro, x0, y0, width, height); +} + +static RectArea _remap_remap_rect_dst(void *ros, + const struct bitmap_desc src_img, + int src_mode, + int x0, int y0, int width, int height, struct bitmap_desc dst_img) +{ + RemapObject *ro = RO(ros); + if (src_mode != ro->src_mode) + RO(ros) = ro = re_create_obj(ro, src_mode); + ro->src_image = src_img.img; + ro->src_start = 0; + ro->dst_image = dst_img.img; + ro->src_resize(ro, src_img.width, src_img.height, src_img.scan_len); + ro->dst_resize(ro, dst_img.width, dst_img.height, dst_img.scan_len); + return ro->remap_rect_dst(ro, x0, y0, width, height); +} + +static RectArea _remap_remap_mem(void *ros, + const struct bitmap_desc src_img, + int src_mode, + int src_start, int offset, int len, struct bitmap_desc dst_img) +{ + RemapObject *ro = RO(ros); + if (src_mode != ro->src_mode) + RO(ros) = ro = re_create_obj(ro, src_mode); + ro->src_image = src_img.img; + ro->src_start = src_start; + ro->dst_image = dst_img.img; + ro->src_resize(ro, src_img.width, src_img.height, src_img.scan_len); + ro->dst_resize(ro, dst_img.width, dst_img.height, dst_img.scan_len); + return ro->remap_mem(ro, offset, len); +} + +static int _remap_get_cap(void *ros) +{ + RemapObject *ro = RO(ros); + return ro->state; +} + +static void *_remap_remap_init(int dst_mode, int features, + const ColorSpaceDesc *color_space, int gamma) +{ + RemapObject **p, *o; + p = malloc(sizeof(*p)); + o = malloc(sizeof(*o)); + /* create dummy remap. init properly later, when src mode is known */ + memset(o, 0, sizeof(*o)); + o->dst_mode = dst_mode; + o->features = features; + o->dst_color_space = color_space; + o->palette_update = do_nearly_nothing; + o->gamma = gamma; + *p = o; + return p; +} + +static void _remap_remap_done(void *ros) +{ + RemapObject *ro = RO(ros); + _remap_done(ro); + free(ros); +} + +static struct remap_calls rmcalls = { + _remap_remap_init, + _remap_remap_done, + _remap_palette_update, + _remap_remap_rect, + _remap_remap_rect_dst, + _remap_remap_mem, + _remap_get_cap, + "dosemu gfx remapper" +}; + +CONSTRUCTOR(static void initialize(void)) +{ + register_remapper(&rmcalls, REMAP_DOSEMU); +} + +/* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * + * some test remap functions + * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + */ + +#ifdef REMAP_TEST + +void test_8to32_(RemapObject *); + +static RemapFuncDesc remap_test_list[] = { + +}; + +/* + * returns chained list of modes + */ +RemapFuncDesc *remap_test(void) +{ + int i; + + for(i = 0; i < sizeof(remap_test_list) / sizeof(*remap_test_list) - 1; i++) { + remap_test_list[i].next = remap_test_list + i + 1; + } + + return remap_test_list; +} + + +#endif diff --git a/src/base/video/remap_priv.h b/src/base/video/remap_priv.h new file mode 100644 index 0000000..989e902 --- /dev/null +++ b/src/base/video/remap_priv.h @@ -0,0 +1,158 @@ +/* + * Header file for remap.c & remap_asm.S. + * + * Copyright (c) 1997 Steffen Winterfeldt + * + */ + +/* + * print listing of generated code + */ +#ifndef REMAP_PRIV_H +#define REMAP_PRIV_H + +#undef REMAP_CODE_DEBUG + +#undef REMAP_RESIZE_DEBUG +#undef REMAP_AREA_DEBUG +#undef REMAP_TEST /* Do not define! -- sw */ + +/* + * define to use a 'real' 2x2 dither when using a shared color map + * (this does not affect the remap speed) + */ +#define REMAP_REAL_DITHER + +#ifndef __ASSEMBLER__ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * + * the C part; see below for the asm definitions + * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + */ + +#define MODE_TRUE_COL (MODE_TRUE_8 | MODE_TRUE_15 | MODE_TRUE_16 | MODE_TRUE_24 | MODE_TRUE_32) + +#define REMAP_DESC(FL, SRC, DST, F, INI) {FL, SRC, DST, F, #F, INI, NULL} + +typedef struct { + unsigned r, g, b; +} RGBColor; + +struct RemapObjectStruct; + +typedef struct RemapFuncDescStruct { + unsigned flags; + unsigned src_mode; + unsigned dst_mode; + void (*func)(struct RemapObjectStruct *); + const char *func_name; + void (*func_init)(struct RemapObjectStruct *); + struct RemapFuncDescStruct *next; +} RemapFuncDesc; + +typedef struct RemapObjectStruct { + int (*palette_update)(struct RemapObjectStruct *, unsigned, unsigned, unsigned, unsigned, unsigned); + void (*src_resize)(struct RemapObjectStruct *, int, int, int); + void (*dst_resize)(struct RemapObjectStruct *, int, int, int); + RectArea (*remap_rect)(struct RemapObjectStruct *, int, int, int, int); + RectArea (*remap_rect_dst)(struct RemapObjectStruct *, int, int, int, int); + RectArea (*remap_mem)(struct RemapObjectStruct *, int, int); + int state; + int src_mode, dst_mode; + int features; + const ColorSpaceDesc *dst_color_space; + unsigned gamma; /* 4 byte !! */ + unsigned char *gamma_lut; + const unsigned char *src_image; + unsigned char *dst_image; + unsigned char *src_tmp_line; + unsigned src_width, src_height, src_scan_len; + unsigned dst_width, dst_height, dst_scan_len; + int src_x0, src_y0, src_x1, src_y1; + int dst_x0, dst_y0, dst_x1, dst_y1; + int src_offset, dst_offset; + int src_start, dst_start; + int *bre_x, *bre_y; + unsigned *true_color_lut; + int color_lut_size; + unsigned *bit_lut; + int supported_src_modes; + void (*remap_func)(struct RemapObjectStruct *); + unsigned remap_func_flags; + const char *remap_func_name; + void (*remap_func_init)(struct RemapObjectStruct *); + void (*remap_line)(void); + RemapFuncDesc *func_all; + RemapFuncDesc *func_1; + RemapFuncDesc *func_2; +} RemapObject; + +/* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * function prototypes + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + */ + +void set_remap_debug_msg(FILE *); +void gamma_correct(RemapObject *, RGBColor *, unsigned *); + +/* remap_pent.c */ +RemapFuncDesc *remap_opt(void); + +#else /* __ASSEMBLER__ */ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + .macro RO_Struct _str_ + .equ \_str_, RO_ElemCount + .equ RO_ElemCount, RO_ElemCount+4 + .endm + .equ RO_ElemCount, 0 + + RO_Struct ro_palette_update + RO_Struct ro_src_resize + RO_Struct ro_dst_resize + RO_Struct ro_remap_rect + RO_Struct ro_remap_mem + RO_Struct ro_state + RO_Struct ro_src_mode + RO_Struct ro_src_mode + RO_Struct ro_dst_color_space + RO_Struct ro_gamma + RO_Struct ro_gamma_lut + RO_Struct ro_src_image + RO_Struct ro_dst_image + RO_Struct ro_src_tmp_line + RO_Struct ro_src_width + RO_Struct ro_src_height + RO_Struct ro_src_scan_len + RO_Struct ro_dst_width + RO_Struct ro_dst_height + RO_Struct ro_dst_scan_len + RO_Struct ro_src_x0 + RO_Struct ro_src_y0 + RO_Struct ro_src_x1 + RO_Struct ro_src_y1 + RO_Struct ro_dst_x0 + RO_Struct ro_dst_y0 + RO_Struct ro_dst_x1 + RO_Struct ro_dst_y1 + RO_Struct ro_src_offset + RO_Struct ro_dst_offset + RO_Struct ro_bre_x + RO_Struct ro_bre_y + RO_Struct ro_true_color_lut + RO_Struct ro_bit_lut + RO_Struct ro_supported_src_modes + RO_Struct ro_remap_func + RO_Struct ro_remap_func_flags + RO_Struct remap_func_name + RO_Struct remap_func_init + RO_Struct ro_co + RO_Struct ro_remap_line + RO_Struct ro_func_all + RO_Struct ro_func_1 + RO_Struct ro_func_2 + +#endif /* __ASSEMBLER__ */ + +#endif diff --git a/src/base/video/render.c b/src/base/video/render.c new file mode 100644 index 0000000..41a95d9 --- /dev/null +++ b/src/base/video/render.c @@ -0,0 +1,912 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +#include +#include +#include +#include +#include +#include "emu.h" +#include "utilities.h" +#include "vgaemu.h" +#include "vgatext.h" +#include "render.h" +#include "video.h" +#include "render_priv.h" + +#define RENDER_THREADED 1 +#define TEXT_THREADED 1 + +struct rmcalls_wrp { + struct remap_calls *calls; + int prio; +}; +#define MAX_REMAPS 5 +static struct rmcalls_wrp rmcalls[MAX_REMAPS]; +static int num_remaps; +static int is_updating; +static pthread_mutex_t upd_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_rwlock_t text_mtx = PTHREAD_RWLOCK_INITIALIZER; +#if RENDER_THREADED +static pthread_t render_thr; +#endif +static pthread_mutex_t render_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_rwlock_t mode_mtx = PTHREAD_RWLOCK_INITIALIZER; +static sem_t render_sem; +static void do_rend_gfx(void); +static void do_rend_text(void); +static int remap_mode(void); +static void bitmap_refresh_pal(void *opaque, DAC_entry *col, int index); + +struct rs_wrp { + struct render_system *render; + int locked; +}; +#define MAX_RENDERS 5 +struct render_wrp { + struct rs_wrp wrp[MAX_RENDERS]; + int num_renders; + int render_locked; + int render_text; + int text_locked; + struct remap_object *gfx_remap; + struct remap_object *text_remap; + struct bitmap_desc dst_image[MAX_RENDERS]; +}; +static struct render_wrp Render; +static int initialized; +static int cur_mode_class; + +__attribute__((warn_unused_result)) +static int render_lock(void) +{ + int i, j; + for (i = 0; i < Render.num_renders; i++) { + Render.dst_image[i] = Render.wrp[i].render->lock(); + if (Render.wrp[i].render->flags & RENDF_DISABLED) { + Render.wrp[i].render->unlock(); + continue; + } + if (!Render.dst_image[i].width) { + error("render %s failed to lock\n", Render.wrp[i].render->name); + /* undo locks in case of a failure */ + for (j = 0; j < i; j++) + Render.wrp[j].render->unlock(); + return -1; + } + Render.wrp[i].locked++; + } + Render.render_locked++; + return 0; +} + +static void render_unlock(void) +{ + int i; + for (i = 0; i < Render.num_renders; i++) { + if (!Render.wrp[i].locked) + continue; + Render.wrp[i].locked--; + Render.wrp[i].render->unlock(); + } + Render.render_locked--; +} + +static void check_locked(void) +{ + if (!Render.render_locked) + dosemu_error("render not locked!\n"); +} + +static void render_text_begin(void) +{ + pthread_rwlock_rdlock(&text_mtx); + text_lock(); + Render.render_text++; +} + +static void render_text_end(void) +{ + text_unlock(); + Render.render_text--; + assert(!Render.text_locked); + pthread_rwlock_unlock(&text_mtx); +} + +static void render_text_lock(void *opaque) +{ + int err; + if (Render.render_text || Render.text_locked) { + dosemu_error("render not in text mode!\n"); + leavedos(95); + return; + } + err = render_lock(); + if (err) + return; + Render.text_locked++; +} + +static void render_text_unlock(void *opaque) +{ + Render.text_locked--; + render_unlock(); +} + +static void render_rect_add(int rend_idx, RectArea rect) +{ + Render.wrp[rend_idx].render->refresh_rect(rect.x, rect.y, rect.width, rect.height); +} + +/* + * Draw a text string for bitmap fonts. + * The attribute is the VGA color/mono text attribute. + */ +static void bitmap_draw_string(void *opaque, int x, int y, + const char *text, int len, Bit8u attr) +{ + struct remap_object **obj = opaque; + struct bitmap_desc src_image; + src_image = convert_bitmap_string(x, y, text, len, attr); + if (!src_image.img) + return; + remap_remap_rect(*obj, src_image, MODE_PSEUDO_8, + vga.char_width * x, vga.char_height * y, + vga.char_width * len, vga.char_height); +} + +static void bitmap_draw_text_line(void *opaque, int x, int y, float ul, + int len, Bit8u attr) +{ + struct remap_object **obj = opaque; + struct bitmap_desc src_image; + src_image = draw_bitmap_line(x, y, ul, len, attr); + remap_remap_rect(*obj, src_image, MODE_PSEUDO_8, + vga.char_width * x, vga.char_height * y, + vga.char_width * len, vga.char_height); +} + +static void bitmap_draw_text_cursor(void *opaque, int x, int y, + Bit8u attr, int start, int end, Boolean focus) +{ + struct remap_object **obj = opaque; + struct bitmap_desc src_image; + src_image = draw_bitmap_cursor(x, y, attr, start, end, focus); + remap_remap_rect(*obj, src_image, MODE_PSEUDO_8, + vga.char_width * x, vga.char_height * y, + vga.char_width, vga.char_height); +} + +static struct text_system Text_bitmap = +{ + bitmap_draw_string, + bitmap_draw_text_line, + bitmap_draw_text_cursor, + bitmap_refresh_pal, + render_text_lock, + render_text_unlock, + &Render.text_remap, + "text_bitmap", + TEXTF_BMAP_FONT, +}; + +int register_render_system(struct render_system *render_system) +{ + assert(Render.num_renders < MAX_RENDERS); + Render.wrp[Render.num_renders++].render = render_system; + return 1; +} + +/* + * Initialize the interface between the VGA emulator and X. + * Check if X's color depth is supported. + */ +int remapper_init(int have_true_color, int have_shmap, int features, + ColorSpaceDesc *csd) +{ + int remap_src_modes, ximage_mode; + +// set_remap_debug_msg(stderr); + + if(have_true_color) { + switch(csd->bits) { + case 1: ximage_mode = MODE_TRUE_1_MSB; break; + case 15: ximage_mode = MODE_TRUE_15; break; + case 16: ximage_mode = MODE_TRUE_16; break; + case 24: ximage_mode = MODE_TRUE_24; break; + case 32: ximage_mode = MODE_TRUE_32; break; + default: ximage_mode = MODE_UNSUP; + } + } + else { + switch(csd->bits) { + case 8: ximage_mode = have_shmap ? MODE_TRUE_8 : MODE_PSEUDO_8; break; + default: ximage_mode = MODE_UNSUP; + } + } + + remap_src_modes = find_supported_modes(ximage_mode); + Render.gfx_remap = remap_init(ximage_mode, features, csd); + /* linear 1 byte per pixel */ + Render.text_remap = remap_init(ximage_mode, features, csd); + register_text_system(&Text_bitmap); + init_text_mapper(ximage_mode, features, csd); + + return vga_emu_init(remap_src_modes, csd); +} + +#if RENDER_THREADED +static void *render_thread(void *arg) +{ + while (1) { + sem_wait(&render_sem); + pthread_mutex_lock(&upd_mtx); + is_updating = 1; + pthread_mutex_unlock(&upd_mtx); + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + do_rend_gfx(); +#if TEXT_THREADED + do_rend_text(); +#endif + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_mutex_lock(&upd_mtx); + is_updating = 0; + pthread_mutex_unlock(&upd_mtx); + } + return NULL; +} +#endif + +int render_init(void) +{ + int err = 0; +#if RENDER_THREADED + err = sem_init(&render_sem, 0, 0); + assert(!err); + err = pthread_create(&render_thr, NULL, render_thread, NULL); +#if defined(HAVE_PTHREAD_SETNAME_NP) && defined(__GLIBC__) + pthread_setname_np(render_thr, "dosemu: render"); +#endif + assert(!err); +#endif + initialized++; + return err; +} + +/* + * Free resources associated with remap_obj. + */ +void render_done(void) +{ + if (!initialized) + return; + initialized--; +#if RENDER_THREADED + pthread_cancel(render_thr); + pthread_join(render_thr, NULL); + sem_destroy(&render_sem); +#endif +} + +void remapper_done(void) +{ + done_text_mapper(); + if (Render.text_remap) + remap_done(Render.text_remap); + if (Render.gfx_remap) + remap_done(Render.gfx_remap); +} + +/* + * Update the displayed image to match the current DAC entries. + * Will redraw the *entire* screen if at least one color has changed. + */ +static void bitmap_refresh_pal(void *opaque, DAC_entry *col, int index) +{ + struct remap_object **ro = opaque; + remap_palette_update(*ro, index, vga.dac.bits, col->r, col->g, col->b); +} + +static void refresh_truecolor(DAC_entry *col, int index, void *udata) +{ + struct remap_object *ro = udata; + remap_palette_update(ro, index, vga.dac.bits, col->r, col->g, col->b); +} + +/* returns True if the screen needs to be redrawn */ +static Boolean refresh_palette(void *opaque) +{ + struct remap_object **obj = opaque; + return changed_vga_colors(refresh_truecolor, *obj); +} + +/* + * Update the active colormap to reflect the current DAC entries. + */ +static void refresh_graphics_palette(void) +{ + if (refresh_palette(&Render.gfx_remap)) + dirty_all_video_pages(); +} + +static int font_is_changed(void) +{ + return memcmp(vga.backup_font, vga.mem.base + 0x20000, 256 * 32); +} + +static int suitable_mode_class(void) +{ + /* Add more checks here. + * Need to treat any weird text mode as gfx. */ + if (vga.char_width < 8 || vga.char_width > 9 || vga.char_height < 8) + return GRAPH; + /* check if font wasn't changed */ + if (font_is_changed()) + return GRAPH; + return TEXT; +} + +struct vid_mode_params get_mode_parameters(void) +{ + struct vid_mode_params ret; + int x_res, y_res, w_x_res, w_y_res; + + w_x_res = x_res = vga.width; + w_y_res = y_res = vga.height; + + /* scale really small modes to ~320x240: + TODO: look at vga registers */ + if(w_x_res <= 160 && w_x_res > 0) + w_x_res = (320 / w_x_res) * w_x_res; + if(w_y_res <= 120 && w_y_res > 0) + w_y_res = (240 / w_y_res) * w_y_res; + /* 320x200-style modes */ + if(w_x_res <= 360 && w_y_res <= 240) { + w_x_res *= config.X_mode13fact; + w_y_res *= config.X_mode13fact; + } else if(w_y_res <= 240) { + /* 640x200-style modes */ + w_y_res *= 2; + } else if(w_x_res <= 360) { + /* 320x480-style modes */ + w_x_res *= 2; + } + + if(config.X_winsize_x > 0 && config.X_winsize_y > 0) { + w_x_res = config.X_winsize_x; + w_y_res = config.X_winsize_y; + } + + if(config.X_aspect_43) { + w_y_res = (w_x_res * 3) >> 2; + } +#if 0 + cap = remap_get_cap(Render.gfx_remap); + if(!(cap & (ROS_SCALE_ALL | ROS_SCALE_1 | ROS_SCALE_2))) { + error("setmode: video mode 0x%02x not supported on this screen\n", vga.mode); + /* why do we need a blank screen? */ + /* Because many games use 16bpp only for the trailer, so not quitting + * here may actually help. */ +#if 0 + leavedos(24); +#endif + } + + if(!(cap & ROS_SCALE_ALL)) { + if((cap & ROS_SCALE_2) && !(cap & ROS_SCALE_1)) { + w_x_res = x_res << 1; + w_y_res = y_res << 1; + } + else { + w_x_res = x_res; + w_y_res = y_res; + } + } +#endif + + ret.w_x_res = w_x_res; + ret.w_y_res = w_y_res; + ret.x_res = x_res; + ret.y_res = y_res; + if (vga.mode_class == GRAPH) + ret.mode_class = GRAPH; + else + ret.mode_class = suitable_mode_class(); + ret.text_width = vga.text_width; + ret.text_height = vga.text_height; + return ret; +} + +/* + * Modify the current graphics mode. + * Currently used to turn on/off chain4 addressing, change + * the VGA screen size, change the DAC size. + */ +static void modify_mode(void) +{ + if(vga.reconfig.mem) { + dirty_all_video_pages(); + vga.reconfig.mem = 0; + } + + if(vga.reconfig.dac) { + dirty_all_vga_colors(); + vga.reconfig.dac = 0; + v_printf("modify_mode: DAC bits = %d\n", vga.dac.bits); + } +} + + +static void update_graphics_loop(unsigned display_start, + unsigned display_end, int src_offset, + int update_offset, vga_emu_update_type *veut) +{ + int i = -1; + + while ((i = vga_emu_update(veut, display_start + src_offset + update_offset, + display_end, i)) != -1) { + remap_remap_mem(Render.gfx_remap, BMP(vga.mem.base + display_start, + vga.width, vga.height, vga.scan_len), + remap_mode(), + src_offset, update_offset + + veut->update_start - display_start, + veut->update_len); + } +} + +static void update_graphics_screen(void) +{ + vga_emu_update_type veut; + unsigned display_end, wrap; + + refresh_graphics_palette(); + + display_end = vga.display_start + vga.scan_len * vga.height; + if (vga.line_compare < vga.height) { + unsigned wrap2 = vga.display_start + vga.scan_len * vga.line_compare; + wrap = _min(vga.mem.wrap, wrap2); + } else { + wrap = _min(vga.mem.wrap, display_end); + } + + update_graphics_loop(vga.display_start, wrap, 0, 0, &veut); + + if (display_end > wrap) { + int len = wrap - vga.display_start; + int rem = len % vga.scan_len; + int align = 0; + /* This is for programs such as Commander Keen 4 that set the + display_start close to the end of the video memory, and + we need to wrap at 0xb0000 + */ + /* XXX: we align to next line here because the remappers can + * align to the beginning of the first line, accessing below + * video memory start. Aligning to next line will give the + * graphics artifacts, but its better than the crash. + * The real fix will require having all RFF_REMAP_RECT remappers + * and an API to pass src and dst offsets separately. + * Basically we need a completely new remapper code to handle + * this properly. + */ + if (rem) + align = vga.scan_len - rem; + update_graphics_loop(0, display_end - wrap, -len, len + align, &veut); + } +} + +int render_is_updating(void) +{ + int upd; + pthread_mutex_lock(&upd_mtx); + upd = is_updating; + pthread_mutex_unlock(&upd_mtx); + return upd; +} + +static void do_rend_gfx(void) +{ + pthread_rwlock_rdlock(&mode_mtx); + vga_emu_update_lock(); + if(vga.reconfig.mem || vga.reconfig.dac) + modify_mode(); + switch (vga.mode_class) { + case TEXT: + break; + case GRAPH: + if (vgaemu_is_dirty()) { + int err = render_lock(); + if (err) + break; + update_graphics_screen(); + render_unlock(); + } + break; + default: + v_printf("VGA not yet initialized\n"); + break; + } + vga_emu_update_unlock(); + pthread_rwlock_unlock(&mode_mtx); +} + +static void do_rend_text(void) +{ + pthread_rwlock_rdlock(&mode_mtx); + vga_emu_update_lock(); + if(vga.reconfig.mem || vga.reconfig.dac) + modify_mode(); + switch (vga.mode_class) { + case TEXT: + blink_cursor(); + if (text_is_dirty()) { + render_text_begin(); + update_text_screen(); + render_text_end(); + } + break; + case GRAPH: + break; + default: + v_printf("VGA not yet initialized\n"); + break; + } + vga_emu_update_unlock(); + pthread_rwlock_unlock(&mode_mtx); +} + +void render_mode_lock(void) +{ + pthread_rwlock_rdlock(&mode_mtx); +} + +void render_mode_lock_w(void) +{ + pthread_rwlock_wrlock(&mode_mtx); +} + +void render_mode_unlock(void) +{ + pthread_rwlock_unlock(&mode_mtx); +} + +int render_update_vidmode(void) +{ + int ret = 0; + if (Video->setmode) { + struct vid_mode_params vmp; + pthread_rwlock_wrlock(&mode_mtx); + vmp = get_mode_parameters(); + ret = Video->setmode(vmp); + pthread_rwlock_unlock(&mode_mtx); + if (ret) + cur_mode_class = vmp.mode_class; + } + return ret; +} + +/* + * DANG_BEGIN_FUNCTION update_screen + * + * description: + * Update the part of the screen which has changed, in text mode + * and in graphics mode. Usually called from the SIGALRM handler. + * + * X_update_screen returns 0 if nothing was updated, 1 if the whole + * screen was updated, and 2 for a partial update. + * + * It is called in arch/linux/async/signal.c::SIGALRM_call() as part + * of a struct video_system (see top of X.c) every 50 ms or + * every 10 ms if 2 was returned, depending somewhat on various config + * options as e.g. config.X_updatefreq and VIDEO_CHECK_DIRTY. + * At least it is supposed to do that. + * + * DANG_END_FUNCTION + * + * Text and graphics updates are separate functions now; the code was + * too messy. -- sw + */ +int update_screen(void) +{ + int upd = render_is_updating(); + + /* update vidmode before doing any rendering. */ + if(vga.reconfig.display || (cur_mode_class == TEXT && font_is_changed()) || + (vga.mode_class == TEXT && cur_mode_class == GRAPH && + !font_is_changed())) { + if (upd) + return 1; + v_printf( + "modify_mode: geometry changed to %d x% d, scan_len = %d bytes\n", + vga.width, vga.height, vga.scan_len + ); + vga_emu_update_lock(); + render_update_vidmode(); + dirty_all_video_pages(); + vga.reconfig.display = 0; + vga_emu_update_unlock(); + } + +#if !RENDER_THREADED + do_rend_gfx(); + do_rend_text(); +#else +#if !TEXT_THREADED + do_rend_text(); +#endif +#endif + if (!upd) { + if (Video->update_screen) + Video->update_screen(); + } + + if(vga.config.video_off) { + v_printf("update_screen: nothing done (video_off = 0x%x)\n", vga.config.video_off); + return 1; + } + if (upd) + return 1; + + sem_post(&render_sem); + return 1; +} + +void redraw_text_screen(void) +{ + pthread_rwlock_wrlock(&text_mtx); + dirty_text_screen(); + dirty_all_vga_colors(); + pthread_rwlock_unlock(&text_mtx); +} + +void render_gain_focus(void) +{ + text_gain_focus(); +} + +void render_lose_focus(void) +{ + text_lose_focus(); +} + +static int remap_mode(void) +{ + int mode_type; + switch(vga.mode_type) { + case CGA: + mode_type = vga.pixel_size == 2 ? MODE_CGA_2 : MODE_CGA_1; break; + case HERC: + mode_type = MODE_HERC; break; + case PL1: + mode_type = MODE_VGA_1; break; + case PL2: + mode_type = MODE_VGA_2; break; + case PL4: + mode_type = MODE_VGA_4; break; + case P8: + mode_type = vga.seq.addr_mode == 2 ? MODE_PSEUDO_8 : MODE_VGA_X; break; + case P15: + mode_type = MODE_TRUE_15; break; + case P16: + mode_type = MODE_TRUE_16; break; + case P24: + mode_type = MODE_TRUE_24; break; + case P32: + mode_type = MODE_TRUE_32; break; + default: + mode_type = 0; + } + return mode_type; +} + +void render_blit(int x, int y, int width, int height) +{ + int err = render_lock(); + if (err) + return; + if (vga.mode_class == TEXT) { + struct bitmap_desc src_image; + src_image = get_text_canvas(); + remap_remap_rect_dst(Render.text_remap, src_image, MODE_PSEUDO_8, + x, y, width, height); + } else { + /* unfortunately this does not handle mem wrap, so keen4 will + * have artifacts. Don't use this blit too much... SDL plugin + * doesn't use it but an X plugin does. Wrap should really be + * handled by remapper. */ + remap_remap_rect_dst(Render.gfx_remap, BMP(vga.mem.base + vga.display_start, + vga.width, vga.height, vga.scan_len), remap_mode(), + x, y, width, height); + } + render_unlock(); +} + +int register_remapper(struct remap_calls *calls, int prio) +{ + struct rmcalls_wrp *rm; + assert(num_remaps < MAX_REMAPS); + rm = &rmcalls[num_remaps++]; + rm->calls = calls; + rm->prio = prio; + return 0; +} + +static int find_rmcalls(int sidx) +{ + int i, idx = -1, max1 = -1, max = -1; + if (sidx >= 0) + max = rmcalls[sidx].prio; + for (i = 0; i < num_remaps; i++) { + int p = rmcalls[i].prio; + if (p > max1 && (p < max || max == -1)) { + max1 = p; + idx = i; + } + } + return idx; +} + +struct remap_object *remap_init(int dst_mode, int features, + const ColorSpaceDesc *color_space) +{ + void *rm = NULL; + struct remap_object *ro; + struct remap_calls *calls = NULL; + int i = -1; + do { + i = find_rmcalls(i); + if (i == -1) + break; + calls = rmcalls[i].calls; + assert(calls); + rm = calls->init(dst_mode, features, color_space, config.X_gamma); + if (!rm) + v_printf("remapper %i \"%s\" failed for mode %x\n", + i, rmcalls[i].calls->name, dst_mode); + } while (!rm); + if (!rm) { + error("gfx remapper failure\n"); + leavedos(3); + return NULL; + } + ro = malloc(sizeof(*ro)); + ro->calls = calls; + ro->priv = rm; + return ro; +} + +void remap_done(struct remap_object *ro) +{ + ro->calls->done(ro->priv); + free(ro); +} + +#define REMAP_CALL0(r, x) \ +r remap_##x(struct remap_object *ro) \ +{ \ + r ret; \ + CHECK_##x(); \ + pthread_mutex_lock(&render_mtx); \ + ret = ro->calls->x(ro->priv); \ + pthread_mutex_unlock(&render_mtx); \ + return ret; \ +} +#define REMAP_CALL1(r, x, t, a) \ +r remap_##x(struct remap_object *ro, t a) \ +{ \ + r ret; \ + CHECK_##x(); \ + pthread_mutex_lock(&render_mtx); \ + ret = ro->calls->x(ro->priv, a); \ + pthread_mutex_unlock(&render_mtx); \ + return ret; \ +} +#define REMAP_CALL3(r, x, t1, a1, t2, a2, t3, a3) \ +r remap_##x(struct remap_object *ro, t1 a1, t2 a2, t3 a3) \ +{ \ + r ret; \ + CHECK_##x(); \ + pthread_mutex_lock(&render_mtx); \ + ret = ro->calls->x(ro->priv, a1, a2, a3); \ + pthread_mutex_unlock(&render_mtx); \ + return ret; \ +} +#define REMAP_CALL5(r, x, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5) \ +r remap_##x(struct remap_object *ro, t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) \ +{ \ + r ret; \ + CHECK_##x(); \ + pthread_mutex_lock(&render_mtx); \ + ret = ro->calls->x(ro->priv, a1, a2, a3, a4, a5); \ + pthread_mutex_unlock(&render_mtx); \ + return ret; \ +} +#define REMAP_CALL5_WR(_x, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5) \ +void remap_##_x(struct remap_object *ro, t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) \ +{ \ + RectArea r; \ + int i; \ + CHECK_##_x(); \ + pthread_mutex_lock(&render_mtx); \ + for (i = 0; i < Render.num_renders; i++) { \ + if (!Render.wrp[i].locked) \ + continue; \ + r = ro->calls->_x(ro->priv, a1, a2, a3, a4, a5, Render.dst_image[i]); \ + if (r.width) \ + render_rect_add(i, r); \ + } \ + pthread_mutex_unlock(&render_mtx); \ +} +#define REMAP_CALL6_WR(_x, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5, t6, a6) \ +void remap_##_x(struct remap_object *ro, t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6) \ +{ \ + RectArea r; \ + int i; \ + CHECK_##_x(); \ + pthread_mutex_lock(&render_mtx); \ + for (i = 0; i < Render.num_renders; i++) { \ + if (!Render.wrp[i].locked) \ + continue; \ + r = ro->calls->_x(ro->priv, a1, a2, a3, a4, a5, a6, Render.dst_image[i]); \ + if (r.width) \ + render_rect_add(i, r); \ + } \ + pthread_mutex_unlock(&render_mtx); \ +} + +void render_enable(struct render_system *render) +{ + pthread_mutex_lock(&render_mtx); + render->flags &= ~RENDF_DISABLED; + pthread_mutex_unlock(&render_mtx); +} + +void render_disable(struct render_system *render) +{ + pthread_mutex_lock(&render_mtx); + render->flags |= RENDF_DISABLED; + pthread_mutex_unlock(&render_mtx); +} + +#define CHECK_get_cap() +#define CHECK_remap_mem() check_locked() +#define CHECK_remap_rect() check_locked() +#define CHECK_remap_rect_dst() check_locked() +#define CHECK_palette_update() +#define CHECK_adjust_gamma() + +REMAP_CALL5(int, palette_update, unsigned, i, + unsigned, bits, unsigned, r, unsigned, g, unsigned, b) +REMAP_CALL6_WR(remap_rect, const struct bitmap_desc, src_img, + int, src_mode, + int, x0, int, y0, int, width, int, height +) +REMAP_CALL6_WR(remap_rect_dst, const struct bitmap_desc, src_img, + int, src_mode, + int, x0, int, y0, int, width, int, height +) +REMAP_CALL5_WR(remap_mem, const struct bitmap_desc, src_img, + int, src_mode, + int, src_start, + int, offset, int, len +) +REMAP_CALL0(int, get_cap) + +void color_space_complete(ColorSpaceDesc *csd) +{ + unsigned ui; + + if((ui = csd->r_mask)) for(csd->r_shift = 0; !(ui & 1); ui >>= 1, csd->r_shift++); + if(ui) for(csd->r_bits = 0; ui; ui >>= 1, csd->r_bits++); + if((ui = csd->g_mask)) for(csd->g_shift = 0; !(ui & 1); ui >>= 1, csd->g_shift++); + if(ui) for(csd->g_bits = 0; ui; ui >>= 1, csd->g_bits++); + if((ui = csd->b_mask)) for(csd->b_shift = 0; !(ui & 1); ui >>= 1, csd->b_shift++); + if(ui) for(csd->b_bits = 0; ui; ui >>= 1, csd->b_bits++); +} + +int find_supported_modes(unsigned dst_mode) +{ + return ~0; +} diff --git a/src/base/video/render_priv.h b/src/base/video/render_priv.h new file mode 100644 index 0000000..6b1e173 --- /dev/null +++ b/src/base/video/render_priv.h @@ -0,0 +1,33 @@ +#ifndef RENDER_PRIV_H +#define RENDER_PRIV_H + +int find_supported_modes(unsigned dst_mode); + +struct remap_object { + struct remap_calls *calls; + void *priv; +}; + +struct remap_object *remap_init(int dst_mode, int features, + const ColorSpaceDesc *color_space); +void remap_done(struct remap_object *ro); +int remap_palette_update(struct remap_object *ro, unsigned i, + unsigned bits, unsigned r, unsigned g, unsigned b); +void remap_remap_rect(struct remap_object *ro, + const struct bitmap_desc src_img, + int src_mode, + int x0, int y0, int width, int height +); +void remap_remap_rect_dst(struct remap_object *ro, + const struct bitmap_desc src_img, + int src_mode, + int x0, int y0, int width, int height +); +void remap_remap_mem(struct remap_object *ro, + const struct bitmap_desc src_img, + int src_mode, + int src_start, int offset, int len +); +int remap_get_cap(struct remap_object *ro); + +#endif diff --git a/src/base/video/text.c b/src/base/video/text.c new file mode 100644 index 0000000..edc9d59 --- /dev/null +++ b/src/base/video/text.c @@ -0,0 +1,1071 @@ +/* + * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". + * + * for details see file COPYING in the DOSEMU distribution + */ + +/* + * Debug level for the X interface. + * 0 - normal / 1 - useful / 2 - too much + */ +#define DEBUG_X 0 + +/* "fine tuning" option for X_update_screen */ +#define MAX_UNCHANGED 3 + +#define x_msg(x...) X_printf("X: " x) + +#if DEBUG_X >= 1 +#define x_deb(x...) X_printf("X: " x) +#else +#define x_deb(x...) +#endif + +#if DEBUG_X >= 2 +#define x_deb2(x...) X_printf("X: " x) +#else +#define x_deb2(x...) +#endif + +#include +#include +#include +#include +#include + +#include "emu.h" +#include "utilities.h" +#include "bios.h" +#include "video.h" +#include "memory.h" +#include "vgaemu.h" +#include "vgatext.h" +#include "render_priv.h" +#include "translate/translate.h" + +#define MAX_TEXTS 5 +static struct text_system *Text[MAX_TEXTS]; +static int num_texts; + +Boolean have_focus; +static unsigned prev_cursor_location = -1; +static uint16_t prev_cursor_shape = NO_CURSOR; +static int blink_state = 1; +static int blink_count = 8; +static int need_redraw_cursor; +static unsigned char *text_canvas; +static uint16_t prev_screen[MAX_COLUMNS * MAX_LINES]; /* pointer to currently displayed screen */ +static u_char prev_font[256 * 32]; + +#if CONFIG_SELECTION +static int sel_start_row = -1, sel_end_row = + -1, sel_start_col, sel_end_col, sel_col, sel_row; +static unsigned short *sel_start = NULL, *sel_end = NULL; +static t_unicode *sel_text = NULL; +static Boolean doing_selection, visible_selection, rect_selection; +#endif + + +/* macros for accessing video memory. w is an uint16_t* + to a character cell, attr is a byte. +*/ + +#define CHAR(w) (*(Bit8u *)(w)) +#define ATTR(w) (*(((Bit8u *)(w))+1)) + +#if CONFIG_SELECTION +#define SEL_ACTIVE(x, y) sel_active(x, y) + +static int sel_active(int x, int y) +{ + if (!visible_selection) + return 0; + if (y < sel_start_row || y > sel_end_row) + return 0; + if (x < sel_start_col && (y == sel_start_row || rect_selection)) + return 0; + if (x > sel_end_col && (y == sel_end_row || rect_selection)) + return 0; + return 1; +} + +static Bit8u sel_attr(Bit8u a) +{ + /* adapted from Linux vgacon code */ + if (vga.mode_type == TEXT_MONO) + a ^= ((a & 0x07) == 0x01) ? 0x70 : 0x77; + else + a = (a & 0x88) | ((a & 0x70) >> 4) | ((a & 0x07) << 4); + return a; +} + +#define XATTR(w, x, y) (SEL_ACTIVE(x, y) ? sel_attr(ATTR(w)) : ATTR(w)) +#else +#define XATTR(w, x, y) (ATTR(w)) +#endif + +#define XREAD_WORD(w, x, y) ((XATTR(w, x, y)<<8)|CHAR(w)) + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int register_text_system(struct text_system *text_system) +{ + assert(num_texts < MAX_TEXTS); + Text[num_texts++] = text_system; + return 1; +} + +void text_lock(void) +{ + int i; + + for (i = 0; i < num_texts; i++) { + if (Text[i]->flags & TEXTF_DISABLED) + continue; + if (Text[i]->lock) + Text[i]->lock(Text[i]->opaque); + } +} + +void text_unlock(void) +{ + int i; + + for (i = 0; i < num_texts; i++) { + if (Text[i]->flags & TEXTF_DISABLED) + continue; + if (Text[i]->unlock) + Text[i]->unlock(Text[i]->opaque); + } +} + +/* + * Draw a text string. + * The attribute is the VGA color/mono text attribute. + */ +static void draw_string(int x, int y, unsigned char *text, int len, + Bit8u attr) +{ + int i; + + x_deb2("X_draw_string: %d chars at (%d, %d), attr = 0x%02x\n", + len, x, y, (unsigned) attr); + for (i = 0; i < num_texts; i++) { + char charbuff[MAX_COLUMNS], *p; + + if (Text[i]->flags & TEXTF_DISABLED) + continue; + memcpy(charbuff, text, len); + if (!(Text[i]->flags & TEXTF_BMAP_FONT)) { + while ((p = memchr(charbuff, '\0', len))) + *p = ' '; + } + Text[i]->Draw_string(Text[i]->opaque, x, y, charbuff, len, attr); + if (vga.mode_type == TEXT_MONO && vga.char_height + && (attr == 0x01 || attr == 0x09 || attr == 0x89)) { + int ul = vga.crtc.data[0x14] & 0x1f; + if (ul > vga.char_height - 1) + ul = vga.char_height - 1; + Text[i]->Draw_line(Text[i]->opaque, x, y, ul / (float)vga.char_height, + len, attr); + } + } +} + +/* + * convert offset in video mem to y*scan_len+x*2 + */ +static unsigned memoffs_to_location(unsigned memoffs) +{ + // FIXME: handle vga.mem.wrap as in render.c:update_graphics_screen + if (vga.line_compare < vga.text_height && + memoffs < (vga.text_height - vga.line_compare) * vga.scan_len) + return memoffs + vga.line_compare * vga.scan_len; + return memoffs - vga.display_start; +} + +/* + * convert y*scan_len+x*2-style location to offset in video mem + */ +static unsigned location_to_memoffs(unsigned location) +{ + // FIXME: handle vga.mem.wrap as in render.c:update_graphics_screen + if (vga.line_compare < vga.text_height && + location >= vga.line_compare * vga.scan_len) + return location - vga.line_compare * vga.scan_len; + return location + vga.display_start; +} + +/* + * check if the cursor location is within bounds and it's text mode + */ +static int check_cursor_location(unsigned cursor_location, int *x, int *y) +{ + /* no hardware cursor emulation in graphics modes (erik@sjoerd) + or if vga.scan_len==0 (before vgaemu is fully initialized) */ + if (vga.mode_class == GRAPH || vga.scan_len == 0) + return 0; + + *x = (cursor_location % vga.scan_len) / 2; + *y = cursor_location / vga.scan_len; + + /* don't draw it if it's out of bounds */ + return (*y >= 0 && *y < vga.text_height && *x >= 0 + && *x < vga.text_width); +} + +/* + * Restore a character cell (used to remove the cursor). + * Do nothing in graphics modes. + */ +static void restore_cell(unsigned cursor_location) +{ + Bit16u *sp, *oldsp; + u_char c; + int x, y; + + if (!check_cursor_location(cursor_location, &x, &y)) + return; + + sp = (Bit16u *) (vga.mem.base + location_to_memoffs(cursor_location)); + oldsp = prev_screen + cursor_location / 2; + c = CHAR(sp); + + *oldsp = XREAD_WORD(sp, x, y); + draw_string(x, y, &c, 1, XATTR(sp, x, y)); +} + +/* + * Draw the cursor (nothing in graphics modes, normal if we have focus, + * rectangle otherwise). + */ +static void draw_cursor(void) +{ + int x, y, i, cs, ce; + + if (check_cursor_location + (memoffs_to_location(vga.crtc.cursor_location), &x, &y) + && (blink_state || !have_focus)) { + Bit16u *cursor = (Bit16u *) (vga.mem.base + vga.crtc.cursor_location); + for (i = 0; i < num_texts; i++) { + if (Text[i]->flags & TEXTF_DISABLED) + continue; + cs = CURSOR_START(vga.crtc.cursor_shape) & 0x1f; + ce = CURSOR_END(vga.crtc.cursor_shape) & 0x1f; + if (cs > ce) + ce = (CURSOR_END(vga.crtc.cursor_shape) + vga.char_height) & 0x1f; + if (cs > ce) + return; + Text[i]->Draw_cursor(Text[i]->opaque, x, y, XATTR(cursor, x, y), + cs, ce, have_focus); + } + } +} + +/* + * Move cursor to a new position (and erase the old cursor). + * Do nothing in graphics modes. + */ +static void redraw_cursor(void) +{ + if (prev_cursor_shape != NO_CURSOR) + restore_cell(prev_cursor_location); + + if (vga.crtc.cursor_shape.w != NO_CURSOR) + draw_cursor(); + + prev_cursor_location = memoffs_to_location(vga.crtc.cursor_location); + prev_cursor_shape = vga.crtc.cursor_shape.w; +} + +struct bitmap_desc draw_bitmap_cursor(int x, int y, Bit8u attr, int start, + int end, Boolean focus) +{ + int i, j; + int fg = ATTR_FG(attr); + int len = vga.scan_len / 2 * vga.char_width; + unsigned char *deb; + + deb = text_canvas + vga.char_width * x + len * (vga.char_height * y); + if (focus) { + deb += len * start; + for (i = 0; i < end - start + 1; i++) { + for (j = 0; j < vga.char_width; j++) { + *deb++ = fg; + } + deb += len - vga.char_width; + } + } else { + /* Draw only a rectangle */ + for (j = 0; j < vga.char_width; j++) + *deb++ = fg; + deb += len - vga.char_width; + for (i = 0; i < vga.char_height - 2; i++) { + *deb++ = fg; + deb += vga.char_width - 2; + *deb++ = fg; + deb += len - vga.char_width; + } + for (j = 0; j < vga.char_width; j++) + *deb++ = fg; + } + return BMP(text_canvas, vga.width, vga.height, vga.width); +} + +/* + * Draw a horizontal line (for text modes) + * The attribute is the VGA color/mono text attribute. + */ +struct bitmap_desc draw_bitmap_line(int x, int y, float ul, int linelen, + Bit8u attr) +{ + int fg = ATTR_FG(attr); + int len = vga.scan_len / 2 * vga.char_width; + unsigned char *deb; + + x = vga.char_width * x; + y = vga.char_height * y + ul * vga.char_height; + linelen *= vga.char_width; + + deb = text_canvas + len * y + x; + memset(deb, fg, linelen); + return BMP(text_canvas, vga.width, vga.height, vga.width); +} + +#if 0 +void reset_redraw_text_screen(void) +{ + unsigned compare; + prev_cursor_shape = NO_CURSOR; + + /* Comment Eric: If prev_screen is too small, we must update */ + /* everything continuously anyway, sigh... */ + /* so we better cheat and clip co / li / ..., danger >:->. */ + if (vga.scan_len * vga.text_height > 65535) { + if (vga.scan_len > MAX_COLUMNS * 2) + vga.scan_len = MAX_COLUMNS * 2; + if (vga.text_width > MAX_COLUMNS) + vga.text_width = MAX_COLUMNS; + if (vga.text_height > MAX_LINES) + vga.text_height = MAX_LINES; + } + compare = _min(vga.text_height, vga.line_compare) * vga.scan_len; + memcpy(prev_screen, vga.mem.base + location_to_memoffs(0), compare); + memcpy(&prev_screen[compare / 2], vga.mem.base, + vga.scan_len * vga.text_height - compare); +} +#endif + +static void refresh_text_pal(DAC_entry * col, int index, void *udata) +{ + int i; + + for (i = 0; i < num_texts; i++) { + if (Text[i]->flags & TEXTF_DISABLED) + continue; + if (Text[i]->SetPalette) + Text[i]->SetPalette(Text[i]->opaque, col, index); + } +} + +/* + * Reallocate color cells if a text color has changed. If no + * free color cell is left, choose an approximate color. + * + * Note: Redraws the *entire* screen if at least one color has changed. + */ +static int refresh_text_palette(void) +{ + if (vga.pixel_size > 4) { + X_printf + ("X: refresh_text_palette: invalid color size - no updates made\n"); + return -1; + } + + return changed_vga_colors(refresh_text_pal, NULL); +} + +struct bitmap_desc get_text_canvas(void) +{ + return BMP(text_canvas, vga.width, vga.height, vga.width); +} + +/* + * Redraw the entire screen (in text modes). Used only for expose events. + * It's graphics mode counterpart is a simple put_ximage() call + * (cf. X_handle_events). Since we now use backing store in text modes, + * this function will likely never be called (depending on X's configuration). + * + * Note: It redraws the *entire* screen. + */ +static void text_redraw_text_screen(void) +{ + Bit16u *sp, *oldsp; + u_char charbuff[MAX_COLUMNS], *bp; + int x, y, start_x; + Bit8u attr; + + if (vga.mode_class == GRAPH) { + x_msg("X_redraw_text_screen: Text refresh in graphics video mode?\n"); + return; + } + x_msg("X_redraw_text_screen: all\n"); + + vga.reconfig.mem = 0; + refresh_text_palette(); + + if (vga.text_width > MAX_COLUMNS) { + x_msg("X_redraw_text_screen: unable to handle %d columns\n", + vga.text_width); + return; + } + + sp = (Bit16u *) (vga.mem.base + location_to_memoffs(0)); + oldsp = prev_screen; + + x_deb("X_redraw_text_screen: mode 0x%x (%d x %d), screen_adr = %p\n", + vga.mode, co, li, sp); + + for (y = 0; y < vga.text_height; y++) { + x = 0; + sp = (Bit16u *) (vga.mem.base + location_to_memoffs(y * vga.scan_len)); + do { /* scan in a string of chars of the same attribute */ + bp = charbuff; + start_x = x; + attr = XATTR(sp, x, y); + + do { /* conversion of string to X */ + *oldsp++ = XREAD_WORD(sp, x, y); + *bp++ = CHAR(sp); + sp++; + x++; + } while (XATTR(sp, x, y) == attr && x < vga.text_width); + *bp = '\0'; + draw_string(start_x, y, charbuff, x - start_x, attr); + } while (x < vga.text_width); + oldsp += vga.scan_len / 2 - vga.text_width; + } + + memcpy(prev_font, vga.mem.base + 0x20000, 256 * 32); +} + +void dirty_text_screen(void) +{ + memset(prev_screen, 0xff, MAX_COLUMNS * MAX_LINES * sizeof(uint16_t)); +} + +static int text_font_changed(void) +{ + return memcmp(prev_font, vga.mem.base + 0x20000, 256 * 32); +} + +int text_is_dirty(void) +{ + unsigned char *sp; + int ret; + unsigned int compare; + if (blink_count == 0 || need_redraw_cursor || + memoffs_to_location(vga.crtc.cursor_location) != + prev_cursor_location) + return 1; + + if (text_font_changed()) + return 1; + + sp = vga.mem.base + location_to_memoffs(0); + if (vga.text_height <= vga.line_compare) + return memcmp(prev_screen, sp, + vga.text_width * vga.text_height * sizeof(uint16_t)); + + compare = vga.line_compare * vga.scan_len; + ret = memcmp(prev_screen, sp, compare); + if (ret == 0) + ret = memcmp(&prev_screen[compare / sizeof(uint16_t)], vga.mem.base, + vga.scan_len * vga.text_height - compare); + return ret; +} + +/* + * Redraw the cursor if it's necessary. + * Do nothing in graphics modes. + */ +static void update_cursor(void) +{ + if (need_redraw_cursor) { + need_redraw_cursor = FALSE; + redraw_cursor(); + return; + } + if (vga.crtc.cursor_shape.w == NO_CURSOR) + return; + if (memoffs_to_location(vga.crtc.cursor_location) != + prev_cursor_location) { + restore_cell(prev_cursor_location); + prev_cursor_location = memoffs_to_location(vga.crtc.cursor_location); + prev_cursor_shape = vga.crtc.cursor_shape.w; + redraw_cursor(); + return; + } + if (blink_count) + return; + + blink_count = config.X_blinkrate; + if (blink_count) + blink_state ^= 1; + + if (blink_state) + draw_cursor(); + else + restore_cell(memoffs_to_location(vga.crtc.cursor_location)); +} + +/* + * Blink the cursor. Called from SIGALRM handler. + * Do nothing in graphics modes. + */ +void blink_cursor(void) +{ + if (!have_focus) + return; + if (blink_count) { + blink_count--; + return; + } +} + +void init_text_mapper(int image_mode, int features, ColorSpaceDesc * csd) +{ + /* think 9x32 is maximum */ + text_canvas = malloc(MAX_COLUMNS * 9 * MAX_LINES * 32); + if (text_canvas == NULL) + error("X: cannot allocate text mode canvas for font simulation\n"); + need_redraw_cursor = TRUE; + memset(text_canvas, 0, MAX_COLUMNS * 9 * MAX_LINES * 32); +} + +void done_text_mapper(void) +{ + free(text_canvas); +} + +struct bitmap_desc convert_bitmap_string(int x, int y, const char *text, + int len, Bit8u attr) +{ + unsigned src, height, xx, yy, cc, srcp, srcp2, bits; + unsigned long fgX; + unsigned long bgX; + static int last_redrawn_line = -1; + struct bitmap_desc ra = { }; + + if (y >= vga.text_height) + return ra; /* clip */ + if (x >= vga.text_width) + return ra; /* clip */ + if (x + len > vga.text_width) + len = vga.text_width - x; /* clip */ + + /* fgX = text_colors[ATTR_FG(attr)]; *//* if no remapper used */ + /* bgX = text_colors[ATTR_BG(attr)]; *//* if no remapper used */ + fgX = ATTR_FG(attr); + bgX = ATTR_BG(attr); + + /* we could set fgX = bgX: vga.attr.data[0x10] & 0x08 enables */ + /* blinking to be triggered by (attr & 0x80) - but the third */ + /* condition would need to be periodically and having to redo */ + /* all blinking chars again each time that they blink sucks. */ + /* so we ALWAYS interpret blinking as bright background, which */ + /* is what also happens when not vga.attr.data[0x10] & 0x08... */ + /* An IDEA would be to have palette animation and use special */ + /* colors for the bright-or-blinking background, although the */ + /* official blink would be the foreground, not the background. */ + + height = vga.char_height; /* not font_height - should start to */ + /* remove font_height completely. It */ + /* holds the X font's size... */ + src = vga.seq.fontofs[(attr & 8) >> 3]; + + if (y != last_redrawn_line) /* have some less output */ + X_printf("X_draw_string(x=%d y=%d len=%d attr=%d %dx%d @ 0x%04x)\n", + x, y, len, attr, vga.char_width, height, src); + last_redrawn_line = y; + + if (((y + 1) * height) > vga.height) { + v_printf("Tried to print below scanline %d (row %d)\n", vga.height, y); + return ra; + } + if (((x + len) * vga.char_width) > vga.width) { + v_printf("Tried to print past right margin\n"); + v_printf("x=%d len=%d vga.char_width=%d width=%d\n", + x, len, vga.char_width, vga.width); + len = vga.width / vga.char_width - x; + } + + /* would use vgaemu_xy2ofs, but not usable for US, NOW! */ + srcp = vga.width * y * height; + srcp += x * vga.char_width; + + /* vgaemu -> vgaemu_put_char would edit the vga.mem.base[...] */ + /* but as vga memory is used as text buffer at this moment... */ + for (yy = 0; yy < height; yy++) { + srcp2 = srcp; + for (cc = 0; cc < len; cc++) { + bits = vga.mem.base[0x20000 + src + (32 * (unsigned char) text[cc])]; + for (xx = 0; xx < 8; xx++) { + text_canvas[srcp2++] + = (bits & 0x80) ? fgX : bgX; + bits <<= 1; + } + if (vga.char_width >= 9) { /* copy 8th->9th for line gfx */ + /* (only if enabled by bit... */ + if ((vga.attr.data[0x10] & 0x04) && ((text[cc] & 0xc0) == 0xc0)) { + text_canvas[srcp2] = text_canvas[srcp2 - 1]; + srcp2++; + } else { /* ...or fill with background */ + text_canvas[srcp2++] = bgX; + } + srcp2 += (vga.char_width - 9); + } /* (pixel-x has reached on next char now) */ + } + srcp += vga.width; /* next line */ + src++; /* globally shift to the next font row!!! */ + } + + return BMP(text_canvas, vga.width, vga.height, vga.width); +} + +/* + * Update the text screen. + */ +void update_text_screen(void) +{ + Bit16u *sp, *oldsp; + u_char charbuff[MAX_COLUMNS], *bp; + int x, y; /* X and Y position of character being updated */ + int start_x, len, unchanged, co, cursor_row; + unsigned start_off; + Bit8u attr; + + static int yloop = -1; + int numscan = 0; /* Number of lines scanned. */ + + if (!num_texts) // not yet inited + return; + + if (vga.reconfig.mem) { + text_redraw_text_screen(); + vga.reconfig.mem = 0; + return; + } else { + int refr = refresh_text_palette(); + if (refr) + dirty_text_screen(); + } + update_cursor(); + + /* The highest priority is given to the current screen row for the + * first iteration of the loop, for maximum typing response. + * If y is out of bounds, then give it an invalid value so that it + * can be given a different value during the loop. + */ + y = cursor_row = + memoffs_to_location(vga.crtc.cursor_location) / vga.scan_len; + if ((y < 0) || (y >= vga.text_height)) + y = -1; + +/* X_printf("X: X_update_screen, co=%d, li=%d, yloop=%d, y=%d, lines=%d\n", + co,li,yloop,y,lines);*/ + + /* The following loop scans lines on the screen until the maximum number + * of lines have been updated, or the entire screen has been scanned. + */ + co = vga.scan_len / 2; + while (numscan < vga.text_height) { + /* The following sets the row to be scanned and updated, if it is not + * the first iteration of the loop, or y has an invalid value from + * loop pre-initialization. + */ + if ((numscan > 0) || (y < 0)) { + yloop = (yloop + 1) % vga.text_height; + if (yloop == cursor_row) + yloop = (yloop + 1) % vga.text_height; + y = yloop; + } + numscan++; + + sp = (Bit16u *) (vga.mem.base + location_to_memoffs(y * vga.scan_len)); + oldsp = prev_screen + y * co; + + x = 0; + do { + /* find a non-matching character position */ + start_x = x; + while (XREAD_WORD(sp, x, y) == *oldsp) { + sp++; + oldsp++; + x++; + if (x == vga.text_width) + goto line_done; + } +/* now scan in a string of changed chars of the same attribute. + To keep the number of X calls (and thus the overhead) low, + we tolerate a few unchanged characters (up to MAX_UNCHANGED in + a row) in the 'changed' string. +*/ + bp = charbuff; + start_off = memoffs_to_location((u_char *) sp - vga.mem.base); + start_x = x; +#if CONFIG_SELECTION + /* don't show selection if the DOS app changed it */ + if (SEL_ACTIVE(x, y) && *sp != *oldsp) + visible_selection = FALSE; +#endif + attr = XATTR(sp, x, y); + unchanged = 0; /* counter for unchanged chars */ + + while (1) { + *bp++ = CHAR(sp); + *oldsp++ = XREAD_WORD(sp, x, y); + sp++; + x++; + + if ((XATTR(sp, x, y) != attr) || (x == vga.text_width)) + break; + if (XREAD_WORD(sp, x, y) == *oldsp) { + if (unchanged > MAX_UNCHANGED) + break; + unchanged++; + } else { + unchanged = 0; +#if CONFIG_SELECTION + /* don't show selection if the DOS app changed it */ + if (SEL_ACTIVE(x, y) && *sp != *oldsp) + visible_selection = FALSE; +#endif + } + } + len = x - start_x - unchanged; + + /* ok, we've got the string now send it to the X server */ + + draw_string(start_x, y, charbuff, len, attr); + + if ((prev_cursor_location >= start_off) && + (prev_cursor_location < start_off + len * 2)) { + prev_cursor_shape = NO_CURSOR; +/* old cursor was overwritten */ + } + } + while (x < vga.text_width); + line_done: +/* update the cursor. We do this here to avoid the cursor 'running behind' + when using a fast key-repeat. +*/ + if (y == cursor_row) { + if (memoffs_to_location(vga.crtc.cursor_location) != + prev_cursor_location + || vga.crtc.cursor_shape.w != prev_cursor_shape) + redraw_cursor(); + } + } + + memcpy(prev_font, vga.mem.base + 0x20000, 256 * 32); +} + +void text_lose_focus(void) +{ + if (!have_focus) + return; + have_focus = FALSE; + blink_state = TRUE; + blink_count = config.X_blinkrate; + need_redraw_cursor = TRUE; +} + +void text_gain_focus(void) +{ + if (have_focus) + return; + have_focus = TRUE; + need_redraw_cursor = TRUE; +} + +#if CONFIG_SELECTION + +/* + * Calculate sel_start and sel_end pointers from sel_start | end_col | row. + */ +static void calculate_selection(void) +{ + unsigned start, end; + + start = location_to_memoffs(sel_start_row * vga.scan_len + + sel_start_col * 2); + end = location_to_memoffs(sel_end_row * vga.scan_len + + sel_end_col * 2); + sel_start = (Bit16u *) (vga.mem.base + start); + sel_end = (Bit16u *) (vga.mem.base + end); +} + + +/* + * Clear visible part of selection (but not the selection text buffer). + */ +static void clear_visible_selection(void) +{ + visible_selection = FALSE; +} + + +/* + * Free the selection text buffer. + */ +void clear_selection_data(void) +{ + X_printf("X: Selection data cleared\n"); + if (sel_text != NULL) { + free(sel_text); + sel_text = NULL; + } + doing_selection = FALSE; + clear_visible_selection(); +} + + +/* + * Check if we should clear selection. + * Clear if cursor is in or before selected area. +*/ +void clear_if_in_selection(void) +{ + unsigned cursor_row, cursor_col; + + if (!visible_selection) + return; + + cursor_row = + memoffs_to_location(vga.crtc.cursor_location) / vga.scan_len; + cursor_col = + (memoffs_to_location(vga.crtc.cursor_location) % vga.scan_len) / 2; + X_printf("X:clear check selection , cursor at %d %d\n", cursor_col, + cursor_row); + if (((sel_start_row <= cursor_row) && (cursor_row <= sel_end_row) + && (sel_start_col <= cursor_col) && (cursor_col <= sel_end_col)) || + /* cursor either inside selectio */ + ((cursor_row <= sel_start_row) && (cursor_col <= sel_start_col))) + /* or infront of selection */ + { + X_printf("X:selection clear, key-event!\n"); + clear_visible_selection(); + } +} + + +/* + * Start the selection process (mouse button 1 pressed). + */ +void start_selection(int col, int row, int rect) +{ + sel_start_col = sel_end_col = sel_col = col; + sel_start_row = sel_end_row = sel_row = row; + rect_selection = rect; + doing_selection = visible_selection = TRUE; + X_printf("X:start selection , start %d %d, end %d %d\n", + sel_start_col, sel_start_row, sel_end_col, sel_end_row); +} + + +/* + * Extend the selection (mouse motion). + */ +void extend_selection(int col, int row) +{ + int dstart = 0; + if (!doing_selection) + return; + if (row < sel_row) { + sel_start_row = row; + sel_end_row = sel_row; + } else { + sel_end_row = row; + sel_start_row = sel_row; + } + if (rect_selection) { + if (col < sel_col) + dstart++; + } else { + if (row < sel_row || (row == sel_row && col < sel_col)) + dstart++; + } + if (dstart) { + sel_start_col = col; + sel_end_col = sel_col; + } else { + sel_end_col = col; + sel_start_col = sel_col; + } + X_printf("X:extend selection , start %d %d, end %d %d\n", + sel_start_col, sel_start_row, sel_end_col, sel_end_row); + calculate_selection(); // make selection visible +} + + +/* + * Start extending the selection (mouse button 3 pressed). + */ +void start_extend_selection(int col, int row) +{ + /* Try to extend selection, visibility is handled by extend_selection */ + if (!visible_selection) + return; + doing_selection = TRUE; + extend_selection(col, row); +} + +static void save_selection(int col1, int row1, int col2, int row2) +{ + int row, col, co; + u_char *sel_text_dos, *sel_text_ptr; + t_unicode *sel_text_unicode, *prev_sel_text_unicode; + size_t sel_space, sel_text_bytes; + u_char *p; + Bit16u *screen_adr; + + struct char_set_state video_state; /* must not have any... */ + + struct char_set *video_charset = trconfig.video_mem_charset; + + init_charset_state(&video_state, video_charset); + + co = vga.scan_len / 2; + screen_adr = (Bit16u *) vga.mem.base; + p = sel_text_dos = malloc(vga.text_width); + sel_space = (row2 - row1 + 1) * (co + 1) * MB_LEN_MAX + 1; + sel_text_unicode = sel_text = malloc(sel_space * sizeof(t_unicode)); + + /* Copy the text data. */ + for (row = row1; row <= row2; row++) { + prev_sel_text_unicode = sel_text_unicode; + p = sel_text_ptr = sel_text_dos; + sel_text_bytes = 0; + for (col = 0; col < vga.text_width; col++) { + if (!SEL_ACTIVE(col, row)) + continue; + *p++ = + CHAR(screen_adr + + location_to_memoffs(2 * (row * co + col)) / 2); + sel_text_bytes++; + } + while (sel_text_bytes) { + t_unicode symbol; + size_t result; + /* If we hit any run with what we have */ + result = charset_to_unicode(&video_state, &symbol, + sel_text_ptr, sel_text_bytes); + if (result == -1) { + warn("save_selection unfinished\n"); + break; + } + sel_text_bytes -= result; + sel_text_ptr += result; + *sel_text_unicode++ = symbol; + } + /* Remove end-of-line spaces and add a newline. */ + if (col == vga.text_width) { + sel_text_unicode--; + while ((*sel_text_unicode == ' ') && + (sel_text_unicode > prev_sel_text_unicode)) { + sel_text_unicode--; + sel_space++; + } + sel_text_unicode++; + if (!sel_space) { + error("BUG: pasting OOM\n"); + leavedos(91); + } + if (row < row2) { + *sel_text_unicode++ = '\n'; + sel_space--; + } + } + } + free(sel_text_dos); + if (!sel_space) { + error("BUG: pasting OOM2\n"); + leavedos(91); + } + *sel_text_unicode = '\0'; + sel_space--; + + cleanup_charset_state(&video_state); +} + +/* + * Copy the selected text to sel_text, and inform the X server about it. + */ +static void save_selection_data(void) +{ + int col1, row1, col2, row2; + unsigned start_loc, end_loc; + + if ((sel_end - sel_start) < 0) { + visible_selection = FALSE; + return; + } + start_loc = memoffs_to_location((Bit8u *) sel_start - vga.mem.base); + end_loc = memoffs_to_location((Bit8u *) sel_end - vga.mem.base); + row1 = start_loc / vga.scan_len; + row2 = end_loc / vga.scan_len; + col1 = (start_loc % vga.scan_len) / 2; + col2 = (end_loc % vga.scan_len) / 2; + + /* Allocate space for text. */ + if (sel_text != NULL) + free(sel_text); + + save_selection(col1, row1, col2, row2); + + v_printf("VGAEMU: Selection, %d,%d->%d,%d\n", col1, row1, col2, row2); +} + + +/* + * End of selection (button released). + */ +t_unicode *end_selection(void) +{ + if (!doing_selection) + return NULL; + doing_selection = FALSE; + calculate_selection(); + save_selection_data(); + return sel_text; +} + +/* + * Convert X coordinate to column, with bounds checking. + */ +int x_to_col(int x, int w_x_res) +{ + int col = x * vga.text_width / w_x_res; + if (col < 0) + col = 0; + else if (col >= vga.text_width) + col = vga.text_width - 1; + return (col); +} + + +/* + * Convert Y coordinate to row, with bounds checking. + */ +int y_to_row(int y, int w_y_res) +{ + int row = y * vga.text_height / w_y_res; + if (row < 0) + row = 0; + else if (row >= vga.text_height) + row = vga.text_height - 1; + return (row); +} + +#endif /* CONFIG_SELECTION */ diff --git a/src/base/video/video.c b/src/base/video/video.c new file mode 100644 index 0000000..37ad024 --- /dev/null +++ b/src/base/video/video.c @@ -0,0 +1,566 @@ + +/* currently this file contains only global video vars and + initialization/cleanup code. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "emu.h" +#include "int.h" +#include "bios.h" +#include "port.h" +#include "memory.h" +#include "render.h" +#include "vgaemu.h" +#include "vc.h" +#include "mapping.h" +#include "utilities.h" +#include "pci.h" +#include "keyboard/keyb_clients.h" +#include "video.h" + +struct video_system *Video; +#define MAX_VID_CLIENTS 16 +static struct video_system *vid_clients[MAX_VID_CLIENTS]; +static int num_vid_clients; +int video_initialized; + +/* I put Video_none here because I don't want to make a special file for such + * a simple task -- Goga + */ +static int i_empty_void (void) {return 0;} +static void v_empty_void (void) {} + +static int video_none_init(void) +{ + vga_emu_setmode(video_mode, CO, LI); + return 0; +} + +static struct video_system Video_none = { + i_empty_void, /* priv_init */ + video_none_init, /* init */ + NULL, /* late_init */ + NULL, /* early_close */ + v_empty_void, /* close */ + NULL, /* setmode */ + NULL, /* update_screen */ + NULL, /* change_config */ + NULL, /* handle_events */ + "none" +}; + +static int no_real_terminal(void) +{ + char *term = getenv("TERM"); + if ( term == NULL + || !strcmp(term,"dumb") /* most cron's have this */ + || !strcmp(term,"none") /* ... some have this */ + || !strcmp(term,"dosemu-none") /* ... when called recursively */ + ) { + /* + * We assume we are running without a terminal (e.g. in a cronjob). + * Hence, we setup the TERM variable to "dosemu-none", + * set a suitable TERMCAP entry ... and ignore the rest + * the TERMCAP is needed because we still use SLang for the keyboard. + */ + setenv("TERM", "dosemu-none", 1); + setenv("TERMCAP", + "dosemu-none|for running DOSEMU without real terminal:" + ":am::co#80:it#8:li#25:" + ":ce=\\E[K:cl=\\E[H\\E[2J:cm=\\E[%i%d;%dH:do=\\E[B:ho=\\E[H:" + ":le=\\E[D:nd=\\E[C:ta=^I:up=\\E[A:", + 1 + ); + return 1; + } + return 0; +} + +/* According to Reinhard Karcher console graphics may actually work with KMS! */ +/* check whether KMS is used. + * in that case graphics=(auto)/console=(auto) will not activate console + * graphics. + * code adapted from libdrm (drmCheckModesettingSupported) + */ +static int using_kms(void) +{ +#ifdef __linux__ + char pci_dev_dir[1024]; + int bus, dev, func; + DIR *sysdir; + struct dirent *dent; + int found = 0; + pciRec *pcirec; + + if (!on_console()) return 0; // not using KMS under X + if (!pcibios_init()) return 0; + pcirec = pcibios_find_primary_vga(); + if (!pcirec) return 0; + + bus = pcirec->bdf >> 8; + dev = (pcirec->bdf & 0xff) >> 3; + func = pcirec->bdf & 0x7; + + sprintf(pci_dev_dir, "/sys/bus/pci/devices/0000:%02x:%02x.%d/drm", + bus, dev, func); + + sysdir = opendir(pci_dev_dir); + if (sysdir) { + dent = readdir(sysdir); + while (dent) { + if (!strncmp(dent->d_name, "controlD", 8)) { + found = 1; + break; + } + dent = readdir(sysdir); + } + closedir(sysdir); + if (found) + return 1; + } + + sprintf(pci_dev_dir, "/sys/bus/pci/devices/0000:%02x:%02x.%d/", + bus, dev, func); + + sysdir = opendir(pci_dev_dir); + if (!sysdir) + return 0; + + dent = readdir(sysdir); + while (dent) { + if (!strncmp(dent->d_name, "drm:controlD", 12)) { + found = 1; + break; + } + dent = readdir(sysdir); + } + + closedir(sysdir); + if (found) + return 1; + +#endif + return 0; +} + +void register_video_client(struct video_system *vid) +{ + assert(num_vid_clients < MAX_VID_CLIENTS); + vid_clients[num_vid_clients++] = vid; + v_printf("VID: registered video client %s\n", vid->name); +} + +struct video_system *video_get(const char *name) +{ + int i; + for (i = 0; i < num_vid_clients; i++) { + if (strcmp(vid_clients[i]->name, name) == 0) + return vid_clients[i]; + } + return NULL; +} + +static void init_video_none(void) +{ + c_printf("VID: Video set to Video_none\n"); + config.cardtype = CARD_NONE; + config.console_video = config.mapped_bios = config.vga = 0; + Video=&Video_none; + config.term = 1; + config.dumb_video = 1; + setbuf(stdout, NULL); +} + +/* + * DANG_BEGIN_FUNCTION video_init + * + * description: + * Set pointer to correct structure of functions to initialize, close, + * etc... video routines. + * + * DANG_END_FUNCTION + */ +static int video_init(void) +{ + if (!config.term && config.console_video != 1 && + config.cardtype != CARD_NONE && using_kms()) + { + config.vga = config.console_video = config.mapped_bios = config.pci_video = 0; +#ifdef SDL_SUPPORT + warn("KMS detected: using SDL mode.\n"); + load_plugin("sdl"); + config.sdl = 1; + Video = video_get("sdl"); + if (Video) { + config.X = 1; // for compatibility, to be removed + config.X_fullscreen = 1; + config.X_fixed_aspect = 0; + config.console_keyb = KEYB_OTHER; + goto done; + } else { + error("failed to load sdl plugin\n"); + } +#else +#ifdef USE_SLANG + warn("KMS detected: using terminal mode.\n"); + config.term = 1; +#else + error("KMS detected but neither SDL nor slang are built.\n"); + init_video_none(); + goto done; +#endif +#endif + } + +#ifdef USE_CONSOLE_PLUGIN + if (config.console_video || config.console_keyb == KEYB_RAW) + load_plugin("console"); +#endif + /* figure out which video front end we are to use */ + if ((config.term && no_real_terminal()) || config.dumb_video || config.cardtype == CARD_NONE) { + init_video_none(); + } + else if (config.sdl) { + load_plugin("sdl"); + Video = video_get("sdl"); + if (Video) { + config.X = 1; // for compatibility, to be removed + } else { + error("failed to load sdl plugin\n"); + } + } else if (config.X) { + load_plugin("X"); + Video = video_get("X"); + if (Video) { + config.X = 1; + } else { + error("failed to load X plugin\n"); + } + } + else if (config.vga) { + c_printf("VID: Video set to Video_graphics\n"); + Video = video_get("graphics"); + } + else if (config.console_video) { + if (config.cardtype == CARD_MDA) + { + c_printf("VID: Video set to Video_hgc\n"); + Video = video_get("hgc"); + } + else + { + c_printf("VID: Video set to Video_console\n"); + Video = video_get("console"); + } + } + +done: + if (Video && Video->priv_init) { + int err = Video->priv_init(); /* call the specific init routine */ + if (err) { + warn("VID: priv initialization failed for %s\n", Video->name); + Video = NULL; + } + } + + if (config.term) { + config.X = 0; + config.sdl = 0; +#ifndef USE_SLANG + if (config.dumb_video) { + config.term = 0; + } else { + error("terminal support not compiled in\n"); + leavedos(2); + } +#endif + } + + return 0; +} + +void video_early_close(void) +{ + v_printf("VID: video_early_close() called\n"); + if (Video && Video->early_close) { + Video->early_close(); + v_printf("VID: video_close()->Video->early_close() called\n"); + } + /* Note: update_screen() may be called up to video_close(). + * Hope that plugins that implement early_close(), do NOT implement + * update_screen() at the same time. */ +} + +void video_close(void) +{ + v_printf("VID: video_close() called\n"); + render_done(); + if (Video && Video->close) { + Video->close(); + v_printf("VID: video_close()->Video->close() called\n"); + } +} + +/* load bytes of file starting at offset + * into memory at + */ +int +load_file(const char *name, int foffset, unsigned char *mstart, int msize) +{ + int fd; + + if (strcmp(name, "/dev/mem") == 0) { + v_printf("kmem used for loadfile\n"); + open_kmem(); + fd = mem_fd; + } + else + fd = open(name, O_RDONLY); + + if (fd == -1) { + v_printf("VID: load_file() fd invalid\n"); + return -1; + } + + (void)DOS_SYSCALL(lseek(fd, foffset, SEEK_SET)); + (void)RPT_SYSCALL(read(fd, mstart, msize)); + + if (strcmp(name, "/dev/mem") == 0) + close_kmem(); + else + close(fd); + return 0; +} + +static void do_reserve_vmem(dosaddr_t base, int len) +{ + if (config.vga) + register_hardware_ram('v', base, len); + memcheck_reserve('v', base, len); +} + +static void reserve_video_memory(void) +{ + if (config.umb_b0 && !config.dualmon) { + if (!config.umb_a0) + do_reserve_vmem(GRAPH_BASE, GRAPH_SIZE); + if (!config.umb_b8) + do_reserve_vmem(VGA_PHYS_TEXT_BASE, VGA_TEXT_SIZE); + } else if (config.umb_b8) { + if (!config.umb_a0) + do_reserve_vmem(GRAPH_BASE, GRAPH_SIZE); + if (!config.umb_b0) + do_reserve_vmem(MDA_PHYS_TEXT_BASE, VGA_TEXT_SIZE); + } else { + if (!config.umb_a0) + do_reserve_vmem(VMEM_BASE, VMEM_SIZE); + else + do_reserve_vmem(VMEM_BASE + 0x10000, VMEM_SIZE - 0x10000); + } +} + +void +gettermcap(int i, int *co, int *li) +{ + struct winsize ws; /* buffer for TIOCSWINSZ */ + + *li = *co = 0; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0) { + *li = ws.ws_row; + *co = ws.ws_col; + } + + if (*co > MAX_COLUMNS || *li > MAX_LINES) { + error("Screen size is too large: %dx%d, max is %dx%d, ", + *co, *li, MAX_COLUMNS, MAX_LINES); + if (*li > MAX_LINES) + *li = MAX_LINES; + if (*co > MAX_COLUMNS) + *co = MAX_COLUMNS; + error("@Using %dx%d\n", *co, *li); + } + + if (*li == 0 || *co == 0) { + error("unknown window sizes li=%d co=%d, setting to 80x25\n", *li, *co); + *li = LI; + *co = CO; + } + else + v_printf("VID: Setting windows size to li=%d, co=%d\n", *li, *co); +} + +void video_config_init(void) +{ + video_init(); + reserve_video_memory(); +} + +static void init_video_term(void) +{ +#ifdef USE_SLANG + config.X = 0; + config.console_keyb = KEYB_OTHER; + load_plugin("term"); + Video = video_get("term"); + if (!Video) { + init_video_none(); + } else { + config.term = 1; + c_printf("VID: Video set to Video_term\n"); + } +#else + init_video_none(); +#endif +} + +void video_post_init(void) +{ + int err = 0; + + switch (config.cardtype) { + case CARD_MDA: + { + bios_configuration |= (MDA_CONF_SCREEN_MODE); + video_combo = MDA_VIDEO_COMBO; + break; + } + case CARD_CGA: + { + bios_configuration |= (CGA_CONF_SCREEN_MODE); + video_combo = CGA_VIDEO_COMBO; + break; + } + case CARD_EGA: + { + bios_configuration |= (EGA_CONF_SCREEN_MODE); + video_combo = EGA_VIDEO_COMBO; + break; + } + case CARD_VGA: + { + bios_configuration |= (VGA_CONF_SCREEN_MODE); + video_combo = VGA_VIDEO_COMBO; + break; + } + default: /* or Terminal, is this correct ? */ + { + bios_configuration |= (CGA_CONF_SCREEN_MODE); + video_combo = CGA_VIDEO_COMBO; + break; + } + } + + /* init vgaemu & render before initing video subsystem */ + if (!config.vga) { + vga_emu_pre_init(); + if (!config.dumb_video) + render_init(); + } + + if (Video && Video->init) { + c_printf("VID: initializing video %s\n", Video->name); + err = Video->init(); + if (err) + warn("VID: initialization failed for %s\n", Video->name); + } + if (!Video || err) { + if (config.sdl) { + /* silly fall-back from SDL to X or slang. + * Can work because X/slang do not have priv_init */ + config.sdl = 0; + if (using_kms()) { + init_video_term(); + if (Video) { + err = Video->init(); + if (err) { + error("Unable to initialize SDL and terminal video\n"); + leavedos(3); + } + } + } +#ifdef X_SUPPORT + else { + load_plugin("X"); + Video = video_get("X"); + if (Video) { + err = Video->init(); + if (err) { + error("Unable to initialize X and SDL video\n"); + leavedos(3); + } + config.X = 1; + c_printf("VID: Video set to Video_X\n"); + } + } +#endif + } else { + init_video_term(); + if (Video) { + err = Video->init(); + if (err) + Video = NULL; + } + } + } + if (!Video) { + error("Unable to initialize video subsystem\n"); + leavedos(3); + /* leavedos does not exit immediately. */ + return; + } +} + +void video_late_init(void) +{ + if (Video && Video->late_init) + Video->late_init(); +} + +int on_console(void) +{ +#ifdef __linux__ + struct stat chkbuf; + int major, minor; + + if (console_fd == -2) + return 0; + + console_fd = -2; + + if (fstat(STDIN_FILENO, &chkbuf) != 0) + return 0; + + major = chkbuf.st_rdev >> 8; + minor = chkbuf.st_rdev & 0xff; + + c_printf("major = %d minor = %d\n", + major, minor); + /* console major num is 4, minor 64 is the first serial line */ + if (S_ISCHR(chkbuf.st_mode) && (major == 4) && (minor < 64)) { + console_fd = STDIN_FILENO; + return 1; + } +#endif + return 0; +} + +void +vt_activate(int num) +{ + struct video_system *v = video_get("console"); + if (v && v->vt_activate) + v->vt_activate(num); + else + error("VID: Console plugin unavailable\n"); +} diff --git a/src/bindist/autoemu.bat b/src/bindist/autoemu.bat new file mode 100644 index 0000000..b997eea --- /dev/null +++ b/src/bindist/autoemu.bat @@ -0,0 +1,15 @@ +@echo off +rem generic autoexec.bat for DOSEMU + any DOS +rem must be edited in most circumstances +path c:\dos;d:\dosemu +set TEMP=c:\tmp +if not exist %TEMP%\nul mkdir %TEMP% +emusound -e +prompt $P$G +rem uncomment to load another bitmap font +rem mode con codepage prepare=((850) c:\dos\ega.cpi) +rem mode con codepage select 850 +rem chcp 850 +system -s DOSEMU_VERSION +echo "Welcome to dosemu2 %DOSEMU_VERSION%!" +call exechlp.bat -ep diff --git a/src/bindist/bat/dosrc.d/2emuargs.bat b/src/bindist/bat/dosrc.d/2emuargs.bat new file mode 100644 index 0000000..1b9b74b --- /dev/null +++ b/src/bindist/bat/dosrc.d/2emuargs.bat @@ -0,0 +1,2 @@ +@echo off +exechlp.bat -ep diff --git a/src/bindist/bat/dosrc.d/3uhook.bat b/src/bindist/bat/dosrc.d/3uhook.bat new file mode 100644 index 0000000..159220d --- /dev/null +++ b/src/bindist/bat/dosrc.d/3uhook.bat @@ -0,0 +1,2 @@ +@echo off +if exist %USERDRV%:\userhook.bat call %USERDRV%:\userhook.bat diff --git a/src/bindist/bat/dosrc.d/4banner.bat b/src/bindist/bat/dosrc.d/4banner.bat new file mode 100644 index 0000000..6cb7e11 --- /dev/null +++ b/src/bindist/bat/dosrc.d/4banner.bat @@ -0,0 +1,4 @@ +@echo off +echo Welcome to dosemu2! +system -s DOSEMU_VERSION +echo Build %DOSEMU_VERSION% diff --git a/src/bindist/bat/dosrc.d/6blaster.bat b/src/bindist/bat/dosrc.d/6blaster.bat new file mode 100644 index 0000000..6439d9d --- /dev/null +++ b/src/bindist/bat/dosrc.d/6blaster.bat @@ -0,0 +1,2 @@ +@echo off +emusound -e diff --git a/src/bindist/bat/dosrc.d/8font.bat b/src/bindist/bat/dosrc.d/8font.bat new file mode 100644 index 0000000..49135d5 --- /dev/null +++ b/src/bindist/bat/dosrc.d/8font.bat @@ -0,0 +1,2 @@ +@echo off +xmode -custom-font on diff --git a/src/bindist/bat/exechlp.bat b/src/bindist/bat/exechlp.bat new file mode 100644 index 0000000..43e8268 --- /dev/null +++ b/src/bindist/bat/exechlp.bat @@ -0,0 +1,14 @@ +@echo off +system %1 %2 %3 +if not "%DOSEMU_SYS_DRV%" == "" %DOSEMU_SYS_DRV%: +if ERRORLEVEL 1 exitemu 1 +if not "%DOSEMU_SYS_DIR%" == "" cd %DOSEMU_SYS_DIR% +if ERRORLEVEL 1 exitemu 1 +if "%DOSEMU_SYS_CMD%" == "exit" goto cont +if "%DOSEMU_SYS_CMD%" == "" goto done +call %DOSEMU_SYS_CMD% +set SHELL_LOADHIGH_DEFAULT= +:cont +if "%DOSEMU_EXIT%" == "1" exitemu %ERRORLEVEL% +C: +:done diff --git a/src/bindist/bat/swapdrv.bat b/src/bindist/bat/swapdrv.bat new file mode 100644 index 0000000..33d115e --- /dev/null +++ b/src/bindist/bat/swapdrv.bat @@ -0,0 +1,2 @@ +emudrv -fS %1 %2 +set COMSPEC=%2\COMMAND.COM diff --git a/src/bindist/c/config.sys b/src/bindist/c/config.sys new file mode 100644 index 0000000..220ad85 --- /dev/null +++ b/src/bindist/c/config.sys @@ -0,0 +1,18 @@ +rem generic config.sys for DOSEMU + any DOS +rem must be copied to C:\ +rem SWITCHES=/F +DOS=UMB,HIGH +lastdrive=Z +files=40 +stacks=0,0 +buffers=10 +device=c:\dos\himem.sys +device=d:\dosemu\emufs.sys +device=d:\dosemu\umb.sys +devicehigh=d:\dosemu\ems.sys +devicehigh=d:\dosemu\cdrom.sys +install=d:\dosemu\emufs.com +rem uncomment to load another bitmap font (see also autoemu.bat) +rem devicehigh=c:\dos\display.sys con=(ega,,1) +rem for MS-DOS: +shell=c:\command.com /e:1024 /p /k d:\autoemu.bat diff --git a/src/bindist/c/dautoemu.bat b/src/bindist/c/dautoemu.bat new file mode 100644 index 0000000..c828dcd --- /dev/null +++ b/src/bindist/c/dautoemu.bat @@ -0,0 +1,4 @@ +@echo off +rem dr-dos boot proxy +rem must be copied to C:\ +call d:\dautoemu.bat diff --git a/src/bindist/c/dconfig.sys b/src/bindist/c/dconfig.sys new file mode 100644 index 0000000..757c87d --- /dev/null +++ b/src/bindist/c/dconfig.sys @@ -0,0 +1,3 @@ +rem dr-dos boot proxy +rem must be copied to C:\ +chain=d:\dconfig.sys diff --git a/src/bindist/c/fdconfig.sys b/src/bindist/c/fdconfig.sys new file mode 100644 index 0000000..d13afe8 --- /dev/null +++ b/src/bindist/c/fdconfig.sys @@ -0,0 +1,15 @@ +rem config.sys for DOSEMU + FreeDOS +rem must be copied to C:\ +rem SWITCHES=/F +DOS=UMB,HIGH +dosdata=umb +lastdrive=Z +files=40 +stacks=0 +buffers=10 +device=d:\dosemu\emufs.sys +device=d:\dosemu\umb.sys +devicehigh=d:\dosemu\ems.sys +devicehigh=d:\dosemu\cdrom.sys +install=d:\dosemu\emufs.com +shellhigh=command.com /e:1024 /k d:\fdautoem.bat diff --git a/src/bindist/c/odautoem.bat b/src/bindist/c/odautoem.bat new file mode 100644 index 0000000..ad45285 --- /dev/null +++ b/src/bindist/c/odautoem.bat @@ -0,0 +1,7 @@ +@ECHO Off +PATH C:\OPENDOS;D:\DOSEMU;C:\ +VERIFY OFF +PROMPT $P$G +SET OPENDOSCFG=C:\OPENDOS +CALL C:\EMUBIN\UXMACROS.BAT +CALL D:\DAUTOEMU.BAT diff --git a/src/bindist/c/odconfig.sys b/src/bindist/c/odconfig.sys new file mode 100644 index 0000000..252b435 --- /dev/null +++ b/src/bindist/c/odconfig.sys @@ -0,0 +1,17 @@ +rem config.sys for DOSEMU + DR-OpenDOS (not "DR or OpenDOS", but DR-OpenDOS) +rem must be copied to C:\ +DEVICE=C:\OPENDOS\SETVER.EXE +SHELL=C:\COMMAND.COM C:\ /E:512 /P:odautoem.bat +BREAK=OFF +BUFFERS=15 +FILES=20 +FCBS=4,4 +LASTDRIVE=Z +HISTORY=ON,512,ON +COUNTRY=1,,C:\OPENDOS\COUNTRY.SYS +device=d:\dosemu\emufs.sys +device=d:\dosemu\umb.sys +rem DOS=UMB works only after umb.sys +DOS=HIGH,UMB +devicehigh=d:\dosemu\ems.sys +devicehigh=d:\dosemu\cdrom.sys diff --git a/src/bindist/dautoemu.bat b/src/bindist/dautoemu.bat new file mode 100644 index 0000000..ee53f21 --- /dev/null +++ b/src/bindist/dautoemu.bat @@ -0,0 +1,12 @@ +@echo off +rem autoexec.bat for DOSEMU + DR/Novell DOS +rem dont set path to d:\dosemu as Novell command.com has bug +set TEMP=c:\tmp +if not exist %TEMP%\nul mkdir %TEMP% +rem emufs not needed on DR-DOS +emufs +emusound -e +prompt $P$G +system -s DOSEMU_VERSION +echo "Welcome to dosemu2 %DOSEMU_VERSION%!" +call d:\dosemu\exechlp.bat -ep diff --git a/src/bindist/dconfig.sys b/src/bindist/dconfig.sys new file mode 100644 index 0000000..643a140 --- /dev/null +++ b/src/bindist/dconfig.sys @@ -0,0 +1,17 @@ +rem config.sys for DOSEMU + DR/Novell/Open DOS (but not DR-OpenDOS!) +rem SWITCHES=/F +lastdrive=Z +files=40 +stacks=0,0 +buffers=10 +rem below is for DR-DOS +rem device=d:\dosemu\emufs.sys /ALL +rem below is for Novell-DOS but works also on DR +device=d:\dosemu\emufs.sys +device=d:\dosemu\umb.sys +rem Reportedly DOS=UMB should be after umb.sys +DOS=UMB,HIGH +devicehigh=d:\dosemu\ems.sys +devicehigh=d:\dosemu\cdrom.sys +shell=command.com /e:1024 /p:dautoemu.bat +HISTORY=ON,512,ON diff --git a/src/bindist/fdautoem.bat b/src/bindist/fdautoem.bat new file mode 100644 index 0000000..30fe101 --- /dev/null +++ b/src/bindist/fdautoem.bat @@ -0,0 +1,25 @@ +@echo off +rem autoexec.bat for DOSEMU + FreeDOS +path d:\dosemu +rem this is needed when booting from dosemu-freedos-bin +if exist f:\gnu\nul path %PATH%;f:\bin;f:\gnu +if exist c:\bin\nul path %PATH%;c:\bin +set HELPPATH=f:\help +if exist c:\help\nul set HELPPATH=c:\help +if exist c:\tmp\nul goto noswapdrv +if exist e:\tmp\nul call swapdrv.bat c: e: +:noswapdrv +set TEMP=c:\tmp +if not exist %TEMP%\nul mkdir %TEMP% +emusound -e +emumouse c 1 +prompt $P$G +rem uncomment to load another bitmap font +rem lh display con=(vga,437,2) +rem mode con codepage prepare=((850) f:\cpi\ega.cpx) +rem mode con codepage select 850 +rem chcp 850 +echo Welcome to dosemu2! +system -s DOSEMU_VERSION +echo Build %DOSEMU_VERSION% +call exechlp.bat -ep diff --git a/src/bindist/fdppauto.bat b/src/bindist/fdppauto.bat new file mode 100644 index 0000000..0423453 --- /dev/null +++ b/src/bindist/fdppauto.bat @@ -0,0 +1,13 @@ +@echo off +rem autoexec.bat for DOSEMU2 + FDPP +path %DOSEMUDRV%:\dosemu +if not "%SHELLDRV%" == "" path %PATH%;%SHELLDRV%:\ +if not "%FREEDOSDRV%" == "" path %PATH%;%FREEDOSDRV%:\bin;%FREEDOSDRV%:\gnu +if not "%USERDRV%" == "" set TEMP=%USERDRV%:\tmp +rem first run external plugins. -E commands may depend on them. +if "%XBATDRV%" == "" goto noxbat +path %PATH%;%XBATDRV%:\ +for %%b in (%XBATDRV%:\dosrc.d\*.bat) do call %%b +:noxbat +rem run dosemu2 plugins, including vars and -E commands. +for %%b in (%DOSEMUDRV%:\dosemu\dosrc.d\*.bat) do call %%b diff --git a/src/bindist/fdppconf.sys b/src/bindist/fdppconf.sys new file mode 100644 index 0000000..5418185 --- /dev/null +++ b/src/bindist/fdppconf.sys @@ -0,0 +1,20 @@ +rem config.sys for DOSEMU2 + FDPP +SWITCHES=#0 +COUNTRY=#1 +; early hook for external himem etc +CHAIN=@globhook.sys +DOS=UMB +dosdata=umb +fileshigh=128 +lastdrive=Z +int0divz=off +device=dosemu\umb.sys +; hook for EMM driver +CHAIN=@emmhook.sys +devicehigh=dosemu\ems.sys +rem emufs.sys /all replaces emufs.com +devicehigh=dosemu\emufs.sys /all +devicehigh=dosemu\cdrom.sys +set SHELL_ALLOW_EXIT=1 +shellhigh=#2 /M2 +CHAIN=@%0\userhook.sys diff --git a/src/doc/DANG/DANG.sgml b/src/doc/DANG/DANG.sgml new file mode 100644 index 0000000..af44398 --- /dev/null +++ b/src/doc/DANG/DANG.sgml @@ -0,0 +1,5269 @@ + + +
+ + + + The DOSEMU Alterer Novices Guide + + Alistair MacDonald, <alistair@slitesys.demon.co.uk> + + version dosemu-1.4.0 + + + + +This Document is the DOSEMU Alterer Novices Guide. It is known as the DANG. + + + + + + + +Introduction + + +This document is the preliminary draft of a manual to help +people understand the inner workings of dosemu. It is the goal of +this document to create new dosemu hackers. This concept was inspired +by the linux kernel hackers guide. + + + +This Guide was concieved and originally written by "Corey Sweeney" +<corey@interaccess.com>. It has been completely revised. It is now +generated automatically directly from the source code. Special thanks to +"James B. MacLean" <macleajb@ednet.ns.ca> for supplying the original +information. (It was mostly ripped out of a mail message.) "Jochen Hein" +has made many useful comments & suggestions. + + + +At the end if this document is a section detailing how this guide is put +together. This may help you when trying to locate the relevant pieces of +code. If you add new code, it would be useful if the relevant markers +are added where appropriate. + + + +This file is a collective effort. If you don't like one of the +explanations, or want to add anything, please send me something! + + + + + +The Main group of Modules + + +These files are used to start DOSEMU as well as hold globally called +functions and global vars. + + + +Functions in dos.c + + +These are the functions defined in dos.c. + + + +dosemu + + +Arguments are: + + + + + + + argc - Count of argumnents. + + + + + + argc - Actual arguments. + + + + + +Function created by entry point into libdosemu. Called to +jump into the emulate function of DOSEMU. + + + + + + + +Functions in emu.c + + +These are the functions defined in emu.c. + + + +jmp_emulate + + +call the emulate function by way of the dll headers. Always make sure +that this line is the first of emu.c and link emu.o as the first object +file to the lib + + + + + +emulate + + +Arguments are: + + + + + + + argc - Argument count. + + + + + + argv - Arguments. + + + + + +Emulate gets called from dos.c. It initializes DOSEMU to +prepare it for running in vm86 mode. This involves catching signals, +preparing memory, calling all the initialization functions for the I/O +subsystems (video/serial/etc...), getting the boot sector instructions +and calling vm86(). + + + + + + + +Remarks in emu.c + + +DOSEMU must not work within the 1 meg DOS limit, so +start of code is loaded at a higher address, at some time this could +conflict with other shared libs. If DOSEMU is compiled statically +(without shared libs), and org instruction is used to provide the jump +above 1 meg. + + + + + +Remarks in include/emu.h + + +The `vm86_struct` is used to pass all the necessary status/registers to +DOSEMU when running in vm86 mode. + + + +----- + + + + DOSEMU keeps system wide configuration status in a structure +called config. + + + +----- + + + + The var `fatalerr` can be given a true value at any time to have DOSEMU +exit on the next return from vm86 mode. + + + +----- + + + + The var 'running_DosC' is set by the DosC kernel and is used to handle +some things differently, e.g. the redirector. +It interfaces via INTe6,0xDC (DOS_HELPER_DOSC), but only if running_DosC +is !=0. At the very startup DosC issues a INTe6,0xdcDC to set running_DosC +with the contents of BX (which is the internal DosC version). + + + + + + + +The Init group of Modules + + +These files are used for initialization and runtime configuration of DOSEMU + + + +Functions in base/init/init.c + + +These are the functions defined in base/init/init.c. + + + +stdio_init + + +Initialize stdio, open debugging output file if user specified one + + + + + +time_setting_init + + +Beats me + + + + + +timer_interrupt_init + + +Tells the OS to send us periodic timer messages + + + + + +map_video_bios + + +Map the video bios into main memory + + + + + +map_custom_bios + + +Setup the dosemu amazing custom BIOS, quietly overwriting anything +was copied there before. Do not overwrite graphic fonts! + + + + + +memory_init + + +Set up all memory areas as would be present on a typical i86 during +the boot phase. + + + + + +device_init + + +Calls all initialization routines for devices (keyboard, video, serial, +disks, etc.) + + + + + +low_mem_init + + +Initializes the lower 1Meg via mmap & sets up the HMA region + + + + + +version_init + + +Find version of OS running and set necessary global parms. + + + + + + + +Functions in base/init/config.c + + +These are the functions defined in base/init/config.c. + + + +cpu_override + + +Process user CPU override from the config file ('cpu xxx') or +from the command line. Returns the selected CPU identifier or +-1 on error. + + + + + +register_config_scrub + + +register a function Enforce consistency upon the `config` structure after +all values have been set to remove silly option combinations + + + + + +unregister_config_scrub + + +Complement of register_config_scrub +This removes a scrub function. + + + + + +config_scrub + + +Enforce consistency upon the `config` structure after +all values have been set to remove silly option combinations + + + + + +config_init + + +This is called to parse the command-line arguments and config +files. + + + + + + + +Remarks in base/init/config.c + + +For simpler support of X, DOSEMU can be started +by a symbolic link called `xdos` which DOSEMU will use to switch +into X-mode. + + + + + + + +The DPMI group of Modules + + +DPMI is Lutz's Baby. It's a really important part of the Emulator as far +as we are concerned, since it will allow us to run so many more programs +and, most importantly, bcc. This is the one thing that the WINE developers +want that we haven't been able to give them. + + + +If you think you can help .... "Away you Go!" (Sorry to those non-UK folks ... +Thats a reference to a UK kids sports programme from my youth ... anyway ... +enough of this banter. You'll be wanting to know that this is all about +DPMI ...) + + + +Functions in dosext/dpmi/dpmi.c + + +These are the functions defined in dosext/dpmi/dpmi.c. + + + +dpmi_control + + +This function is similar to the vm86() syscall in the kernel and +switches to dpmi code. + + + + + +run_pm_int + + +This routine is used for running protected mode hardware +interrupts. +run_pm_int() switches to the locked protected mode stack +and calls the handler. If no handler is installed the +real mode interrupt routine is called. + + + + + +run_pm_dos_int + + +This routine is used for reflecting the software +interrupts 0x1c, 0x23 and 0x24 to protected mode. + + + + + +do_default_cpu_exception + + +This is the default CPU exception handler. +Exceptions 0, 1, 2, 3, 4, 5 and 7 are reflected +to real mode. All other exceptions are terminating the client +(and may be dosemu too :-)). + + + + + +do_cpu_exception + + +This routine switches to the locked protected mode stack, +disables interrupts and calls the DPMI client exception handler. +If no handler is installed the default handler is called. + + + + + +dpmi_fault + + +This is the brain of DPMI. All CPU exceptions are first +reflected (from the signal handlers) to this code. + + + +Exception from nonprivileged instructions INT XX, STI, CLI, HLT +and from WINDOWS 3.1 are handled here. + + + +All here unhandled exceptions are reflected to do_cpu_exception() + + + +Note for cpu-emu: exceptions generated from the emulator are handled +here. 'Real' system exceptions (e.g. from an emulator fault) are +redirected to emu_dpmi_fault() in fullemu mode + + + + + + + +Remarks in dosext/dpmi/dpmi.c + + +We are caching ldt here for speed reasons and for Windows 3.1. +I would love to have an readonly ldt-alias (located in the first +16MByte for use with 16-Bit descriptors (WIN-LDT)). This is on my +wish list for the kernel hackers (Linus mainly) :-))))))). + + + +----- + + + + DPMI is designed such that the stack change needs a task switch. +We are doing it via an SIGSEGV - instead of one task switch we have +now four :-(. +Arrgh this is the point where I should start to include DPMI stuff +in the kernel, but then we could include the rest of dosemu too. +Would Linus love this? I don't :-((((. +Anyway I would love to see first a working DPMI port, maybe we +will later (with version 0.9 or similar :-)) start with it to +get a really fast dos emulator............... + + + +NOTE: Using DIRECT_DPMI_CONTEXT_SWITCH we avoid these 4 taskswitches +actually doing 0. We don't need a 'physical' taskswitch +(not having different TSS for us and DPMI), we only need a complete +register (context) replacement. For back-switching, however, we need +the sigcontext technique, so we build a proper sigcontext structure +even for 'hand made taskswitch'. (Hans Lermen, June 1996) + + + +-- the whole emu_stack_frame could be eliminated except for eip/rip +and esp/rsp. For the most part GCC can worry about clobbered registers +(Bart Oldeman, October 2006) + + + +dpmi_control is called only from dpmi_run when in_dpmi_dos_int==0 + + + +----- + + + + Hopefully the below LAR can serve as a replacement for the KERNEL_LDT, +which we are abandoning now. Especially the 'accessed-bit' will get +updated in the ldt-cache with the code below. +Most DPMI-clients fortunately _are_ using LAR also to get this info, +however, some do not. Some of those which do _not_, at least use the +DPMI-GetDescriptor function, so this may solve the problem. +(Hans Lermen, July 1996) + + + +----- + + + + Here we handle all prefixes prior switching to the appropriate routines +The exception CS:EIP will point to the first prefix that effects the +the faulting instruction, hence, 0x65 0x66 is same as 0x66 0x65. +So we collect all prefixes and remember them. +- Hans Lermen + + + + + +Items for Fixing in dosext/dpmi/dpmi.c + + +We shouldn't return to dosemu code if IF=0, but it helps - WHY? */ + + + + + + + +The Video group of Modules + + +All of the Video handling code is in the "video" subdirectory. + + + +There is one file for each video card or chipset and the master file. To +Add a new card, it needs a set of save & restore routines putting in a file +here. + + + +Functions in env/video/video.c + + +These are the functions defined in env/video/video.c. + + + +video_init + + +Set pointer to correct structure of functions to initialize, close, +etc... video routines. + + + + + + + +Remarks in env/video/video.c + + +Here the sleeping lion will be awoken and eat much of CPU time !!! + + + +The result of setting VM86_SCREEN_BITMAP (at state of Linux 1.1.56): +Each vm86 call will set 32 pages of video mem RD-only +(there may be 1000000 per second) +Write access to RD-only page results in page-fault (mm/memory.c), +which will set a bit in current->screen_bitmap and calls do_wp_page() +which does __get_free_page(GFP_KERNEL) but frees it immediatly, +because copy-on-write is not neccessary and sets RD/WR for the page. +(this could happen 32000000 per second, if the CPU were fast enough) +It would be better to get the DIRTY-bit directly from the page table, +isn't it? A special syscall in emumodule could do this. + + + +----- + + + + reserve_video_memory() + + + +This procedure is trying to eke out all the UMB blocks possible to +maximize your memory under DOSEMU. If you know about dual monitor +setups, you can contribute by putting in the correct graphics page +address values. + + + + + +Functions in plugin/X/X.c + + +These are the functions defined in plugin/X/X.c. + + + +X_init + + +Initialize everything X-related. + + + + + +X_close + + +Destroy the window, unload font, pixmap and colormap. + + + + + +X_shm_init + + +Check availability of the MIT-SHM shared memory extension. + + + + + +X_shm_init + + +Turn off usage of the MIT-SHM shared memory extension. + + + + + +X_set_mouse_cursor + + +called by mouse.c to hide/display the mouse and set it's position. +This is currently the only callback from mouse.c to X. + + + + + +X_handle_events + + +Handle pending X events (called from SIGALRM handler). + + + + + +graphics_cmap_init + + +Allocate a colormap for graphics modes and initialize it. +Do mostly nothing on true color displays. +Otherwise, do: +- if colormaps have less than 256 entries (notably 16 or 2 colors), +don't use a private colormap +- if a shared map is requested and there are less than 36 colors (3/4/3) +available, use a private colormap + + + +Note: Text modes always use the screen's default colormap. + + + + + +X_set_videomode + + +This is the interface function called by the video subsystem +to set a video mode. + + + +NOTE: The actual mode is taken from the global variable "video_mode". + + + +Set the video mode. +If mode_class is -1, this will only reinitialize the current mode. +The other arguments are ignored in this case. + + + + + +set_mouse_position + + +Place the mouse on the right position. + + + + + + + +Remarks in plugin/X/X.c + + +DO NOT REMOVE THIS TEST!!! +It is magic, without it EMS fails on my machine under X. +Perhaps someday when we don't use a buggy /proc/self/mem.. +-- EB 18 May 1998 +A slightly further look says it's not the test so much as +suppressing noop resize events... +-- EB 1 June 1998 + + + + + +Functions in env/video/vgaemu.c + + +These are the functions defined in env/video/vgaemu.c. + + + +VGA_emulate_outb + + +Emulates writes to VGA ports. +This is a hardware emulation function. + + + +Arguments are: + + + + + + + port - The port being written to. + + + + + + value - The value written, + + + + + + + + + + +VGA_emulate_inb + + +Emulates reads from VGA ports. +This is a hardware emulation function. + + + +Arguments are: + + + + + + + port - The port being read from. + + + + + + + + + + +vga_emu_fault + + +vga_emu_fault() is used to catch video access, and handle it. +This function is called from arch/linux/async/sigsegv.c::dosemu_fault1(). +Now it catches only changes in a 4K page, but maybe it is useful to +catch each video access. The problem when you do that is, you have to +simulate each instruction which could write to the video memory. +It is easy to get the place where the exception happens (scp->cr2), +but what are those changes? +An other problem is, it could eat a lot of time, but it does now also. + + + +MODIFICATION: VGA mode 12h under X is supported in exactly the +way that was suggested above. Not every instruction needs to be +simulated in order to make this feature useful, just the ones used to +access video RAM by key applications (Borland BGI, Protel, etc.). + + + +MODIFICATION: all VGA modes now work and almost all instructions are +simulated. + + + +Arguments are: + + + + + + + scp - A pointer to a struct sigcontext holding some relevant data. + + + + + + + + + + +vga_emu_init + + +vga_emu_init() must be called before using the VGAEmu functions. +It is only called from env/video/X.c::X_init() at the moment. +This function basically initializes the global variable `vga' and +allocates the VGA memory. + + + +It does in particular *not* map any memory into the range +0xa0000 - 0xc0000, this is done as part of a VGA mode switch. + + + +There should be an accompanying vga_emu_done(). + + + +Arguments are: + + + + + + + vedt - Pointer to struct describing the type of display we are actually + + + + + + attached to. + + + + + + + + + + +vga_emu_update + + +vga_emu_update() scans the VGA memory for dirty (= written to since last +update) pages and returns the changed area in *veut. See the definition +of vga_emu_update_type in env/video/vgaemu_inside.h for details. + + + +You will need to call this function repeatedly until it returns 0 to +grab all changes. You can specify an upper limit for the size of the +area that will be returned using `veut->max_max_len' and `veut->max_len'. +See the example in env/video/X.c how this works. + + + +If the return value of vga_emu_update() is >= 0, it is the number of changed +pages, -1 means there are still changed pages but the maximum update chunk size +(`veut->max_max_len') was exceeded. + + + +This function does in its current form not work for Hercules modes; it +does, however work for text modes, although this feature is currently +not used. + + + +Arguments are: + + + + + + + veut - A pointer to a vga_emu_update_type object holding all relevant info. + + + + + + + + + + +vgaemu_switch_plane + + +vgaemu_switch_plane() maps the specified plane. + + + +This function returns True on success and False on error, usually +indicating an invalid bank number. + + + +Arguments are: + + + + + + + plane (0..3) - The plane to map. + + + + + + + + + + +vga_emu_switch_bank + + +vga_emu_switch_bank() is used to emulate video-bankswitching. + + + +This function returns True on success and False on error, usually +indicating an invalid bank number. + + + +Arguments are: + + + + + + + bank - The bank to switch to. + + + + + + + + + + +vga_emu_find_mode + + +Searches a video mode with the requested mode number. + + + +The search starts with the mode *after* the mode `vmi' points to. +If `vmi' == NULL, starts at the beginning of the internal mode table. +`mode' may be a standard VGA mode number (0 ... 0x7f) or a +VESA mode number (>= 0x100). The mode number may have its don't-clear-bit +(bit 7 or bit 15) or its use-lfb-bit (bit 14) set. +The special mode number -1 will match any mode and may be used to +scan through the whole table. + + + +Returns NULL if no mode was found and a pointer into the mode table +otherwise. The returned pointer is a suitable argument for subsequent +calls to this function. + + + +You should (and can) access the mode table only through this function. + + + +Arguments are: + + + + + + + mode - video mode. + + + + + + vmi - pointer into internal mode list + + + + + + + + + + +vga_emu_setmode + + +Set a video mode. + + + +Switches to `mode' with text sizes `width' and `height' or (if no such +mode was found) at least `width' and `height'. + + + +Arguments are: + + + + + + + mode - The new video mode. + + + + + + width - Number of text columns. + + + + + + height - Number of text rows. + + + + + + + + + + +vga_emu_set_textsize + + +Sets the text mode resolution. Typically called after +a font change. + + + +Arguments are: + + + + + + + width - Number of text columns. + + + + + + height - Number of text rows. + + + + + + + + + + +dirty_all_video_pages + + +Marks the whole VGA memory as modified. + + + + + +dirty_all_vga_colors + + +Marks all colors as changed. + + + + + +changed_vga_colors + + +Checks DAC and Attribute Controller to find all colors with +changed RGB-values. +Returns number of changed colors. +Note: the list _must_ be large enough, that is, have at least +min(256, (1 << vga.pixel_size)) entries! + + + +Arguments are: + + + + + + + de - list of DAC entries to store changed colors in + + + + + + + + + + +vgaemu_adj_cfg + + +Adjust VGAEmu according to VGA register changes. + + + + + + + +Functions in env/video/vesa.c + + +These are the functions defined in env/video/vesa.c. + + + +vbe_init + + +Initializes the VGA/VBE BIOS and the VBE support. + + + +Arguments are: + + + + + + + vedt - Pointer to struct describing the type of display we are actually + + + + + + attached to. + + + + + + + + + + +do_vesa_int + + +This is the VESA interrupt handler. + + + +It is called from base/bios/int10.c::int10(). The VESA interrupt is called +with 0x4f in AH and the function number (0x00 ... 0x10) in AL. + + + + + + + +Functions in env/video/attremu.c + + +These are the functions defined in env/video/attremu.c. + + + +Attr_init + + +Initializes the attribute controller. +This is an interface function. + + + + + +Attr_get_entry + + +Directly reads the Attribute Controller's registers. +This is an interface function. + + + + + +Attr_set_entry + + +Directly sets the Attribute Controller's registers. +This is an interface function. + + + + + +Attr_read_value + + +Emulates reads from the attribute controller. +This is a hardware emulation function. + + + + + +Attr_write_value + + +Emulates writes to attribute controller combined index and data +register. Read VGADOC for details. +This is a hardware emulation function. + + + + + +Attr_get_index + + +Returns the current index of the attribute controller. +This is a hardware emulation function, though in fact this function +is undefined in a real attribute controller. +Well, it is exactly what my VGA board (S3) does. -- sw +This is a hardware emulation function. + + + + + + + +Functions in env/video/dacemu.c + + +These are the functions defined in env/video/dacemu.c. + + + +DAC_init + + +Initializes the DAC. +It depends on a correct value in vga.pixel_size. This function should be +called during VGA mode initialization. +This is an interface function. + + + + + +DAC_set_width + + +Sets the DAC width. Typical values are 6 or 8 bits. +In theory, we support other values as well (untested). +This is an interface function. + + + + + +DAC_get_entry + + +Returns a complete DAC entry (r, g, b). +Don't forget to set DAC_entry.index first! +This is an interface function. + + + + + +DAC_set_entry + + +Sets a complete DAC entry (r,g,b). +This is an interface function. + + + + + +DAC_rgb2gray + + +Converts a DAC register's RGB values to gray scale. +This is an interface function. + + + + + +DAC_set_read_index + + +Specifies which palette entry is read. +This is a hardware emulation function. + + + + + +DAC_set_write_index + + +Specifies which palette entry is written. +This is a hardware emulation function. + + + + + +DAC_read_value + + +Read a value from the DAC. Each read will cycle through the registers for +red, green and blue. After a ``blue read'' the read index will be +incremented. Read VGADOC4 if you want to know more about the DAC. +This is a hardware emulation function. + + + + + +DAC_write_value + + +Write a value to the DAC. Each write will cycle through the registers for +red, green and blue. After a ``blue write'' the write index will be +incremented. +This is a hardware emulation function. + + + + + +DAC_get_pel_mask + + +Returns the current PEL mask. Note that changed_vga_colors() already +applies the PEL mask; so applications should not worry too much about it. +This is a hardware emulation function. + + + + + +DAC_set_pel_mask + + +Sets the PEL mask and marks all DAC entries as dirty. +This is a hardware emulation function. + + + + + +DAC_get_state + + +Returns the current state of the DAC. +This is a hardware emulation function. + + + + + + + +Functions in env/video/crtcemu.c + + +These are the functions defined in env/video/crtcemu.c. + + + +CRTC_init + + +Initializes the CRT Controller. +This is an interface function. + + + + + + + +Functions in env/video/dualmon.c + + +These are the functions defined in env/video/dualmon.c. + + + +MDA_init + + +Initializes the monochrome card. First detects which monochrome +card is used, because the Hercules RamFont and the Hercules InColor +need one more register to be initialized. If there is no monochrome +card at all, we just think there is one and poke an peek in the void. +After the detection the card is initialized. + + + +returns: +nothing + + + +Arguments are: + + + + + + + none + + + + + + + + + + + + +Remarks in env/video/dualmon.c + + +After MDA_init() the VGA is configured, something in video.c +or console.c "reprograms" the monochrome card again in such a way +that I always have to run hgc.com before I can use any program that +uses the monochrome card. I've spent a day trying to find it, but I +can't figure out. Something is writing to one of the following ports: +0x3b4, 0x3b5, 0x3b8, 0x3b9, 0x3ba, 0x3bb, 0x3bf. +The problem occurs at (at least) the following 2 systems: + + + +- AMD 386DX40, Trident 9000/512Kb ISA, Hercules Graphics Card Plus +- Intel 486DX2/66, Cirrus Logic 5426/1Mb VLB, Hercules clone + + + +The problem doesn't occur when I start dosemu from a telnet connection +or from a VT100 terminal. (Erik Mouw, jakmouw@et.tudelft.nl) + + + + + +Functions in env/video/vgaemu.c + + +These are the functions defined in env/video/vgaemu.c. + + + +VGA_emulate_outb + + +Emulates writes to VGA ports. +This is a hardware emulation function. + + + +Arguments are: + + + + + + + port - The port being written to. + + + + + + value - The value written, + + + + + + + + + + +VGA_emulate_inb + + +Emulates reads from VGA ports. +This is a hardware emulation function. + + + +Arguments are: + + + + + + + port - The port being read from. + + + + + + + + + + +vga_emu_fault + + +vga_emu_fault() is used to catch video access, and handle it. +This function is called from arch/linux/async/sigsegv.c::dosemu_fault1(). +Now it catches only changes in a 4K page, but maybe it is useful to +catch each video access. The problem when you do that is, you have to +simulate each instruction which could write to the video memory. +It is easy to get the place where the exception happens (scp->cr2), +but what are those changes? +An other problem is, it could eat a lot of time, but it does now also. + + + +MODIFICATION: VGA mode 12h under X is supported in exactly the +way that was suggested above. Not every instruction needs to be +simulated in order to make this feature useful, just the ones used to +access video RAM by key applications (Borland BGI, Protel, etc.). + + + +MODIFICATION: all VGA modes now work and almost all instructions are +simulated. + + + +Arguments are: + + + + + + + scp - A pointer to a struct sigcontext holding some relevant data. + + + + + + + + + + +vga_emu_init + + +vga_emu_init() must be called before using the VGAEmu functions. +It is only called from env/video/X.c::X_init() at the moment. +This function basically initializes the global variable `vga' and +allocates the VGA memory. + + + +It does in particular *not* map any memory into the range +0xa0000 - 0xc0000, this is done as part of a VGA mode switch. + + + +There should be an accompanying vga_emu_done(). + + + +Arguments are: + + + + + + + vedt - Pointer to struct describing the type of display we are actually + + + + + + attached to. + + + + + + + + + + +vga_emu_update + + +vga_emu_update() scans the VGA memory for dirty (= written to since last +update) pages and returns the changed area in *veut. See the definition +of vga_emu_update_type in env/video/vgaemu_inside.h for details. + + + +You will need to call this function repeatedly until it returns 0 to +grab all changes. You can specify an upper limit for the size of the +area that will be returned using `veut->max_max_len' and `veut->max_len'. +See the example in env/video/X.c how this works. + + + +If the return value of vga_emu_update() is >= 0, it is the number of changed +pages, -1 means there are still changed pages but the maximum update chunk size +(`veut->max_max_len') was exceeded. + + + +This function does in its current form not work for Hercules modes; it +does, however work for text modes, although this feature is currently +not used. + + + +Arguments are: + + + + + + + veut - A pointer to a vga_emu_update_type object holding all relevant info. + + + + + + + + + + +vgaemu_switch_plane + + +vgaemu_switch_plane() maps the specified plane. + + + +This function returns True on success and False on error, usually +indicating an invalid bank number. + + + +Arguments are: + + + + + + + plane (0..3) - The plane to map. + + + + + + + + + + +vga_emu_switch_bank + + +vga_emu_switch_bank() is used to emulate video-bankswitching. + + + +This function returns True on success and False on error, usually +indicating an invalid bank number. + + + +Arguments are: + + + + + + + bank - The bank to switch to. + + + + + + + + + + +vga_emu_find_mode + + +Searches a video mode with the requested mode number. + + + +The search starts with the mode *after* the mode `vmi' points to. +If `vmi' == NULL, starts at the beginning of the internal mode table. +`mode' may be a standard VGA mode number (0 ... 0x7f) or a +VESA mode number (>= 0x100). The mode number may have its don't-clear-bit +(bit 7 or bit 15) or its use-lfb-bit (bit 14) set. +The special mode number -1 will match any mode and may be used to +scan through the whole table. + + + +Returns NULL if no mode was found and a pointer into the mode table +otherwise. The returned pointer is a suitable argument for subsequent +calls to this function. + + + +You should (and can) access the mode table only through this function. + + + +Arguments are: + + + + + + + mode - video mode. + + + + + + vmi - pointer into internal mode list + + + + + + + + + + +vga_emu_setmode + + +Set a video mode. + + + +Switches to `mode' with text sizes `width' and `height' or (if no such +mode was found) at least `width' and `height'. + + + +Arguments are: + + + + + + + mode - The new video mode. + + + + + + width - Number of text columns. + + + + + + height - Number of text rows. + + + + + + + + + + +vga_emu_set_textsize + + +Sets the text mode resolution. Typically called after +a font change. + + + +Arguments are: + + + + + + + width - Number of text columns. + + + + + + height - Number of text rows. + + + + + + + + + + +dirty_all_video_pages + + +Marks the whole VGA memory as modified. + + + + + +dirty_all_vga_colors + + +Marks all colors as changed. + + + + + +changed_vga_colors + + +Checks DAC and Attribute Controller to find all colors with +changed RGB-values. +Returns number of changed colors. +Note: the list _must_ be large enough, that is, have at least +min(256, (1 << vga.pixel_size)) entries! + + + +Arguments are: + + + + + + + de - list of DAC entries to store changed colors in + + + + + + + + + + +vgaemu_adj_cfg + + +Adjust VGAEmu according to VGA register changes. + + + + + + + +Functions in env/video/instremu.c + + +These are the functions defined in env/video/instremu.c. + + + +instr_len + + +Returns the length of an instruction; 0 if it could not +be determined. + + + + + +instr_sim + + +instr_sim is used to simulate instructions that access the +VGA video memory in planar modes when using X as the video output +device. + + + +It is necessary to do this in order to simulate the effects +of the hardware VGA controller in X mode. + + + +If the return value is 0, it means the instruction was not one +that for which a simulation is provided. The return value is 1 for success, +but the function exits because we need to go back to the DOSEMU's main loop +or count runs out. + + + +Arguments are: + + + + + + + x86: the structure holding everything about the cpu-state we need. + + + + + + + + + + +instr_emu + + +instr_emu is the main interface to instr_sim. It puts the processor +state in the x86 structure. + + + +Arguments are: + + + + + + + scp - A pointer to a struct sigcontext holding some relevant data. + + + + + + pmode - flags protected mode + + + + + + cnt - number of instructions to be simulated + + + + + + + + + + + + + + +The New_Keyboard group of Modules + + +Most of the New Keyboard handling code is in the "plugin/kbd_unicode" subdirectory. + + + +Functions in plugin/kbd_unicode/serv_xlat.c + + +These are the functions defined in plugin/kbd_unicode/serv_xlat.c. + + + +compute_keynum + + +The task of compute_keynum() is to 'collect' keyboard bytes (e.g. +0xe0 prefixes) until it thinks it has assembled an entire keyboard +event. The entire keyboard event is then returned, otherwise +NUM_VOID is returned. + + + + + +translate_key + + +translate_key takes a keysym event and calculates the appropriate +bios translation. + + + +As a side effect translate_key updates the apropriate pieces of state +to reflect the current keyboard state. + + + +Calling translate_key twice on the same data is likely to be hazardous. + + + + + +put_rawkey + + +This function sends a raw keycode byte, e.g. read directly from the hardware, +to DOS. It is both queued for the port60h emulation and processed for the +BIOS keyboard buffer, using the national translation tables etc. + + + +For DOS applications using int16h we will therefore not have to load +KEYB.EXE, others (e.g. games) need their own drivers anyway. + + + +This function is used if we are at the console and config.rawkeyboard=on. + + + + + +move_keynum + + +This does all the work of sending a key event to DOS. +Either pressing a key releasing one. The key to move is +the key specified by keynum. + + + +keynum - the keynum from keynum.h indicating a physical key +make - TRUE for key press, FALSE for release + + + +Applications using int16h will always see the appropriate ASCII code +for the given keyboard key and the current keyboard state. All the +chracter translation is done for you to keep from reporting +inconsistent key events. + + + +An emulated hardware scancode is also sent to port60h. + + + +Note that you have to send both MAKE (press) and BREAK (release) events. +If no BREAK codes are available (e.g. terminal mode), send them +immediately after the MAKE codes. + + + + + +keysym_to_keynum + + +Allows peeking into the keytables. +This returns the keynum a given keysym sits on. + + + + + +move_key + + +This does all the work of sending a key event to DOS. +Either pressing a key releasing one. The key to move is +the key that is labeled with the specified keysym. + + + +key - the keysym, one of the DKY_ constants from new-kbd.h +make - TRUE for key press, FALSE for release + + + +Applications using int16h will always see the appropriate ASCII code +for the given keyboard key and the current keyboard state. All the +chracter translation is done for you to keep from reporting +inconsistent key events. + + + +An emulated hardware scancode is also sent to port60h. + + + +Note that you have to send both MAKE (press) and BREAK (release) events. +If no BREAK codes are available (e.g. terminal mode), send them +immediately after the MAKE codes. + + + + + +put_symbol + + +This does all the work of sending a key event to DOS. +sym -- The unicode value of the symbol you want to send + + + +Applications using int16h will always see the symbol passed +here, if it is representable in the current dos character set. The +appropriate scancodes are generated automatically to keep the +keyboard code consistent. + + + +An emulated hardware scancode is also sent to port60h. + + + +Note that you have to send both MAKE (press) and BREAK (release) events. +If no BREAK codes are available (e.g. terminal mode), send them +immediately after the MAKE codes. + + + + + +put_modified_symbol + + +This does all the work of sending a key event to DOS. +sym -- The unicode value of the symbol you want to send +modifiers -- modifiers like alt etc you what to change your symbol with. + + + +This function is a concession to the reality, in which key events +are a composed of active modifiers, and a key label. + + + +This function behaves as put_symbol does, except before pressing +the key it adds the specified modifiers to the modifiers it would +normally use. + + + +For cases where the symbol can only be created by an alt# combination +or by pressing a dead key (Basically any case where more than one +key is requried, after setting the shiftstate) it gives up and just +sends the symbol. + + + +Note that you have to send both MAKE (press) and BREAK (release) events. +If no BREAK codes are available (e.g. terminal mode), send them +immediately after the MAKE codes. + + + + + +get_shiftstate + + +This simply reads the keyboard server's shift state. + + + +This is intended to be used in conjunction with set_shiftstate +to sync up a shiftstate with a source of key events. + + + +With the addition of this function the keyboard inteface is clean enough +so if needed a completly different translation engine can be dropped in +to support a totally different environment (windows or whatever). + + + + + +set_shiftstate + + +This simply sets the keyboard server's shift state. + + + +If there are shiftstate bits you want to keep fixed simply grab them with +get_shiftstate, before calling this function. + + + +This changes the keyboard flags by generating the appropriate +shift key make/break codes that normally come along with such +changes. So this function should be safe in any context. + + + +Note also that you can't simply write to the shiftstate variable +instead of using this function. + + + + + + + +Functions in plugin/kbd_unicode/keyb_clients.c + + +These are the functions defined in plugin/kbd_unicode/keyb_clients.c. + + + +keyb_client_init + + +Figures out which keyboard client to use and initialises it. + + + +First it calls the probe method to see if it should use the client, +Then it call init to set that client up. + + + +If probe or init fails it trys another client. + + + +Eventually falling back to Keyboard_none a dummy client, which does nothing. + + + + + + + +Functions in plugin/kbd_unicode/keyb_none.c + + +These are the functions defined in plugin/kbd_unicode/keyb_none.c. + + + +none_probe + + +Succeed if we can run the dummy keyboard client, (we always can). +but first try the other fall-back (slang keyboard) + + + + + + + +Functions in plugin/kbd_unicode/keyb_raw.c + + +These are the functions defined in plugin/kbd_unicode/keyb_raw.c. + + + +raw_keyboard_init + + +Initialize the keyboard for RAW mode. + + + + + +raw_keyboard_reset + + +Reset the keyboard shiftstate to match the keyboard LED's + + + + + + + +Functions in plugin/term/keyb_slang.c + + +These are the functions defined in plugin/term/keyb_slang.c. + + + +setup_pc_scancode_mode + + +Initialize the keyboard in pc scancode mode. +This functionality is ideal but rarely supported on a terminal. + + + + + +exit_pc_scancode_mode + + +Set the terminal back to a keyboard mode other +programs can understand. + + + + + +do_pc_scancode_getkeys + + +Set the terminal back to a keyboard mode other +programs can understand. + + + + + +slang_keyb_init() + + +Code is called at start up to set up the terminal line for non-raw mode. + + + + + +slang_keyb_probe() + + +Code is called at start up to see if we can use the slang keyboard. + + + + + + + + + +The Misc group of Modules + + +These are the remaining important files, that do not really fit into another +group. These should not be dismissed as unimportant - rather, they are often +amongst the most important. + + + +Functions in base/async/int.c + + +These are the functions defined in base/async/int.c. + + + +int1a + + +int 0x1A call + + + +This has (among other things) the calls that DOS makes to get/set its sense +of time. On booting, DOS gets the RTC time and date with AH=2 and AH=4, +after that it should use AH=0 calls to read the 'tick' counter from BIOS +memory. Each time this crosses midnight, a flag is set that DOS uses to +increment its date. + + + +Here we can now change the 'view' of time so the calls either return BIOS +tick (most DOS like), read the PIT counter (avoids INT-8 changes) or gets +LINUX time (most accurate for long term NTP-adjusted time keeping). + + + + + +ms_dos + + +int0x21 call + + + +we trap this for two functions: simulating the EMMXXXX0 device and +fudging the CONFIG.XXX and AUTOEXEC.XXX bootup files. + + + +note that the emulation herein may cause problems with programs +that like to take control of certain int 21h functions, or that +change functions that the true int 21h functions use. An example +of the latter is ANSI.SYS, which changes int 10h, and int 21h +uses int 10h. for the moment, ANSI.SYS won't work anyway, so it's +no problem. + + + + + +run_caller_func(i, revect) + + +This function runs the specified caller function in response to an +int instruction. Where i is the interrupt function to execute. + + + +revect specifies whether we call a non-revectored leaf interrupt function +or a "watcher" that sits in between: +the leaf interrupt function is called if cs:ip is at f000:i*10 or if +(the int vector points there and the int is labelled non-revectored) +otherwise the non-leaf interrupt function is called, which may chain +through to the real interrupt function (if it returns 0) + + + +This function runs the instruction with the following model _CS:_IP is the +address to start executing at after the caller function terminates, and +_EFLAGS are the flags to use after termination. For the simple case of an +int instruction this is easy. _CS:_IP = retCS:retIP and _FLAGS = retFLAGS +as well equally the current values (retIP = curIP +2 technically). + + + +However if the function is called (from dos) by simulating an int instruction +(something that is common with chained interrupt vectors) +_CS:_IP = BIOS_SEG:HLT_OFF(i) and _FLAGS = curFLAGS +while retCS, retIP, and retFlags are on the stack. These I pop and place in +the appropriate registers. + + + +This functions actions certainly correct for functions executing an int/iret +discipline. And almost certianly correct for functions executing an +int/retf#2 discipline (with flag changes), as an iret is always possilbe. +However functions like dos int 0x25 and 0x26 executing with a int/retf will +not be handled correctlty by this function and if you need to handle them +inside dosemu use a halt handler instead. + + + +Finally there is a possible trouble spot lurking in this code. Interrupts +are only implicitly disabled when it calls the caller function, so if for +some reason the main loop would be entered before the caller function returns +wrong code may execute if the retFLAGS have interrupts enabled! + + + +This is only a real handicap for sequences of dosemu code execute for long +periods of time as we try to improve timer response and prevent signal queue +overflows! -- EB 10 March 1997 + + + +Grumble do to code that executes before interrupts, and the +semantics of default_interupt, I can't implement this function as I +would like. In the tricky case of being called from dos by +simulating an int instruction, I must leave retCS, retIP, on the +stack. But I can safely read retFlags so I do. +I pop retCS, and retIP just before returning to dos, as well as +dropping the stack slot that held retFlags. + + + +This improves consistency of interrupt handling, but not quite as +much as if I could handle it the way I would like. +-- EB 30 Nov 1997 + + + +Trying to get it right now -- BO 25 Jan 2003 + + + +This function returns 1 if it's completely finished (no need to run +real_run_int()), otherwise 0. + + + + + +DO_INT + + +DO_INT is used to deal with interrupts returned to DOSEMU by the +kernel. + + + + + +setup_interrupts + + +SETUP_INTERRUPTS is used to initialize the interrupt_function +array which directs handling of interrupts in protected mode and +also initializes the base vector for interrupts in real mode. + + + + + +int_vector_setup + + +Setup initial interrupts which can be revectored so that the kernel +does not need to return to DOSEMU if such an interrupt occurs. + + + + + + + +Remarks in base/async/int.c + + +Many video BIOSes use hi interrupt vector locations as +scratchpad area - this is because they come before DOS and feel +safe to do it. But we are initializing vectors before video, so +this only causes trouble. I assume no video BIOS will ever: +- change vectors < 0xe0 (0:380-0:3ff area) +- change anything in the vector area _after_ installation - AV + + + + + +Functions in arch/linux/async/sigsegv.c + + +These are the functions defined in arch/linux/async/sigsegv.c. + + + +dosemu_fault(int, struct sigcontext); + + +All CPU exceptions (except 13=general_protection from V86 mode, +which is directly scanned by the kernel) are handled here. + + + + + +print_exception_info + + +Prints information about an exception: exception number, error code, +address, reason, etc. + + + + + + + +Functions in arch/linux/async/signal.c + + +These are the functions defined in arch/linux/async/signal.c. + + + +NEWSETQSIG + + +Arguments are: + + + + + + + sig - the signal to have a handler installed to. + + + + + + fun - the signal handler function to install + + + + + +All signals that wish to be handled properly in context with the +execution of vm86() mode, and signals that wish to use non-reentrant +functions should add themselves to the ADDSET_SIGNALS_THAT_QUEUE define +and use SETQSIG(). To that end they will also need to be set up in an +order such as SIGIO. + + + + + +SIG_init + + +The IRQ numbers to monitor are taken from config.sillyint, each bit +corresponding to one IRQ. The higher 16 bit are defining the use of +SIGIO + + + + + +signal_init + + +Initialize the signals to have NONE being blocked. +Currently this is NOT of much use to DOSEMU. + + + + + +handle_signals + + +Due to signals happening at any time, the actual work to be done +because a signal occurs is done here in a serial fashion. + + + +The concept, should this eventualy work, is that a signal should only +flag that it has occurred and let DOSEMU deal with it in an orderly +fashion as it executes the rest of it's code. + + + + + +SIGNAL_save + + +Arguments are: + + + + + + + context - signal context to save. + + + + + + signal_call - signal handling routine to be called. + + + + + +Save into an array structure queue the signal context of the current +signal as well as the function to call for dealing with this signal. +This is a queue because any signal may occur multiple times before +DOSEMU deals with it down the road. + + + + + +SIGIO_call + + +Whenever I/O occurs on devices allowing SIGIO to occur, DOSEMU +will be flagged to run this call which inturn checks which +fd(s) was set and execute the proper routine to get the I/O +from that device. + + + + + + + +Remarks in arch/linux/async/signal.c + + +We assume system call restarting... under linux 0.99pl8 and earlier, +this was the default. SA_RESTART was defined in 0.99pl8 to explicitly +request restarting (and thus does nothing). However, if this ever +changes, I want to be safe + + + +----- + + + + Check for keyboard coming from client +For now, first byte is interrupt requests from Client + + + + + +Functions in base/misc/disks.c + + +These are the functions defined in base/misc/disks.c. + + + +disk_init + + +Test by opening all floppies/hardrives configured. + + + + + + + +Functions in base/dev/misc/timers.c + + +These are the functions defined in base/dev/misc/timers.c. + + + +initialize_timers + + +ensure the 0x40 port timer is initially set correctly + + + + + +timer_tick + + +Every time we get a TIMER signal from Linux, this procedure is called. +It checks to see if we should queue a timer interrupt based on the +current values. + + + + + +do_sound + + +do_sound handles the _emulated_ mode pc-speaker emulation. + + + +As far as I can determine all cases of the pc-speaker are now +emulated. But I am not sure where Rainer Zimmerman got his +(pit[2].mode == 2) || (pit[2].mode == 3) test in the original +implementation, it doesn't seem to cause problems though. + + + +The implementation of speaker_on & speaker_off can be found in +src/base/speaker.c + + + +Major Changes from version written by Rainter Zimmerman. + + + +o Added support for programs that control the directly through bit 1 +of port61. + + + +o Added a generic interface to allow multiple speaker backends. + + + +o Implemented X speaker code so the emulated speaker now works in X. + + + +--EB 21 September 1997 + + + + + +timer_int_engine + + +This is experimental TIMER-IRQ CHAIN code! +This is a function to determine whether it is time to invoke a +new timer irq 0 event. Normally it is 18 times a second, but +many video games set it to 100 times per second or more. Since +the kernel cannot keep an accurate timer interrupt, the job of this +routine is to perform a chained timer irq 0 right after the previous +timer irq 0. This routine should, ideally, be called right after +the end of a timer irq, if possible. + + + +This would speed up high frequency timer interrupts if this code +can be converted into an assembly macro equivalent! + + + +PLEASE NOTE + + + +This code has been replaced by interrupt scheduling code in pic. +The result is that we simply call pic_sched and run the dos interrupt. +If the new code causes no problems, I'll revise this section permanently. + + + + + + + +Functions in base/misc/dos2linux.c + + +These are the functions defined in base/misc/dos2linux.c. + + + +run_unix_command + + +Runs a command and prints the (stdout and stderr) output on the dosemu +screen. + + + +Return values mean: + + + + +Arguments are: + + + + + + + buffer - string with command to execute + + + + + + + + + + + + +Functions in base/misc/ioctl.c + + +These are the functions defined in base/misc/ioctl.c. + + + +io_select_init + + +Initialize fd_sets to NULL for both SIGIO and NON-SIGIO. + + + + + +add_to_io_select + + +Arguments are: + + + + + + + fd - File handle to add to select statment + + + + + + want_sigio - want SIGIO (1) if it's available, or not (0). + + + + + +Add file handle to one of 2 select FDS_SET's depending on +whether the kernel can handle SIGIO. + + + + + +remove_from_io_select + + +Arguments are: + + + + + + + fd - File handle to remove from select statment. + + + + + + used_sigio - used SIGIO (1) if it's available, or not (0). + + + + + +Remove a file handle from one of 2 select FDS_SET's depending +on whether the kernel can handle SIGIO. + + + + + + + +Functions in base/dev/misc/lpt.c + + +These are the functions defined in base/dev/misc/lpt.c. + + + +printer_init + + +Initialize printer control structures + + + + + + + +Functions in base/dev/misc/pci.c + + +These are the functions defined in base/dev/misc/pci.c. + + + +pci_read_header + + +Use standard 32-bit (type 1) access method to read PCI +configuration space data + + + + + +pci_setup + + +Register standard PCI ports 0xcf8-0xcff + + + + + + + +Functions in base/dev/misc/joystick.c + + +These are the functions defined in base/dev/misc/joystick.c. + + + +joy_latency_over + + +Tells DOSEMU whether or not it is time to update its internal status +of the joystick (for nonblocking reads only). + + + +DOS programs read/poll from the joystick port hundreds of thousands of +times per second so the idea is that we really don't need to read from +Linux for every such query (increasing performance by about 40%) because: + + + +1. humans are incapable of changing the status of the joystick +(moving, pressing buttons) more than about 10 times per second + + + +2. no one will not notice a delay in DOS registering the joystick status +(if it is in the order of a few milliseconds) + + + +Of course, this means that you should not set joy_latency in dosemu.conf +to more than 1000/(#times I can press a button/move joy per second * 2), +unless you want DOSEMU to miss quick axis/button changes and want to +wait a ridiculous amount of time before DOSEMU registers any changes at +all. + + + + + +joy_emu_button_set + + +Update the button status for each joystick. + + + +We must perform "button mapping" if only the first joystick is enabled +i.e. we are required to map the "excessive" buttons (>2) from the first +joystick onto the second: + + + +a) 3rd button of 1st joy --> 1st button of 2nd joy + + + +b) 4th button of 1st joy --> 2nd button of 2nd joy + + + + + +joy_emu_axis_set + + +Update the axis status for each joystick. + + + +We must perform "axis mapping" if only the first joystick is enabled +i.e. we are required to map the "excessive" axes (>2) from the first +joystick onto the second: + + + +a) 3rd axis of 1st joy --> 2st axis of 2nd joy + + + +b) 4th axis of 1st joy --> 1st axis of 2nd joy +(yes, these are reversed deliberately because it's what happens in DOS) + + + + + +joy_emu_axis_conv + + +Convert a Linux joystick axis reading to a DOS one by making use of +the differences in the allowable range of axis values. + + + +NOTE: I don't know whether or not Linux returns exponential values +for the joystick but (apparently) DOS programs expect the values to +be exponential and so if this is to be fixed, it should probably be +done in this function. + + + + + +joy_linux_process_event + + +Update global joystick status variables given a Linux joystick event. + + + + + +joy_linux_read_events + + +Process the event queue for _both_ linux joysticks using nonblocking +reads with the new joystick API (joy_driver_new). + + + +This should be done before (well, actually, not before _every_ +single read -- see joy_latency_over()) the joystick status is returned +to DOS as all Linux joystick events are queued until they are processed +and we want to return a reasonably current state of the joystick +-- not what it was a long time ago. _Both_ joysticks are processed +here because of axis/button mapping affecting the status of both emulated +joysticks (what DOS sees). + + + + + +joy_linux_read_status + + +Read both the current position and current button status of the joystick +from Linux (joy_driver_old). + + + + + +joy_linux_read_buttons_(family) + + +Eventually called from DOS to get the button status of the joysticks. +The threaded version will simply get the status from global variables. +The unthreaded versions will perform non-blocking reads. + + + + + +joy_linux_read_axis_(family) + + +Eventually called from DOS to get the axis status of the joysticks. +The threaded version will simply get the values from global variables. +The unthreaded versions will perform non-blocking reads. + + + +Arguments are: + + + + + + + invalid_val - value to return to signify a non-existent axis + + + + + + update - whether DOSEMU should update its internal axis values from Linux + + + + + + (for each read of the joystick position, set this flag _only_ + + + + + + on the first of the 4 calls to this function unless you want + + + + + + the axis positions to be from different points in time :) + + + + + + + + + + +joy_bios_read + + +This is the int15 function 0x84 handler (i.e. BIOS joystick emulation), +called from src/base/async/int.c. + + + +The real BIOS actually reads its values straight from the joystick port +(0x201) but we don't bother because we can do it faster :) + + + +Because of this, it returns the joystick axis values with the same +range as port 0x201 BUT the range for a real BIOS varies between +computers as it is dependant on how it reads from the port +(hopefully this won't cause any problems). + + + + + +joy_port_inb + + +This function emulates reads from the joystick port (0x201) -- this is +the most common way of detecting and reading the joystick. + + + +The real implementation of this port sets a bit for each axis for a +certain period of time, corresponding to analog measurements of the +position of each axis (so "if you count the analog values in software, +a faster machine yields different values from a slow machine [unless +you use a timer]" - DOS 6: A Developer's Guide). + + + +In contrast, this implementation sets the bits high for a certain number +of port reads, corresponding to the position of each axis (independent +of time). This means that, for most programs, the axis range will be +that specified in dosemu.conf (which is rather convenient) and avoids +the issue of super-fast computers causing DOS program axis counters to +overflow (e.g. in a real system, if the program used an 8-bit variable +for storing the position of an axis and the system was fast enough to +read from the port more than 127 or 255 times, there would be trouble). + + + + + + + +Remarks in base/dev/misc/joystick.c + + +We make a runtime decision based on the detected joystick API version +and #ifdef USE_PTHREADS, on the way in which we obtain the joystick +status from Linux (a "driver"): + + + +1. joy_driver_nojoy: simply tells DOS programs that you have no +joystick(s) + + + +2. joy_driver_old: uses old, non-blocking joystick API (<1.0.0); +limited to 2 axes; supported because DOSEMU +supports old kernels + + + +3. joy_driver_new: uses new, non-blocking joystick API (>=1.0.0); +a (little) slower than joy_driver_new_threaded + + + +4. joy_driver_new_threaded: uses new, BLOCKING joystick API (>=1.0.0); +efficient but requires pthreads (which +is known to make DOSEMU unstable!) + + + +The same driver is used for both joysticks. + + + +----- + + + + if the 2nd joystick is enabled, we ignore any button >= 2 regardless +of which joystick it is (if it's the 1st, the 2nd joystick would +overwrite its buttons; if it's the 2nd, it would be out of range) + + + +----- + + + + if the 2nd joystick is enabled, we ignore any axis >= 2 regardless +of which joystick it is (if it's the 1st, the 2nd joystick would +overwrite its axes; if it's the 2nd, it would be out of range) + + + +----- + + + + Apparently, the Carry Flag is cleared if int15 function 0x84 exists +and it is set if it doesn't exist. + + + +But what does this mean? Does the existence of such a BIOS function +depend on the existence of a Game Card/SoundBlaster, or does it just +mean that there is such an implemented BIOS function, regardless of +whether or not you have a joystick? + + + +I have never seen a real BIOS set the Carry Flag on this call, even +on a computer without a joystick -- so to mimick what happens in the +real world, I just clear the Carry Flag regardless of whether the user +has a joystick or not. This could be incorrect behaviour so it may +have to be changed in the future. + + + +----- + + + + Here we set bits based on joystick axis counters. +The code here is particularly tricky and if you try to change it, you +will probably break it :) + + + +----- + + + + Here we read the button status from Linux (programs can read the +button status from the port, _without_ making a dummy write to the +port first so the Linux read must be done _here_) and return it. + + + + + +Items for Fixing in base/dev/misc/joystick.c + + +does this code work for ports other than 0x201? + + + +----- + + + + joy_reset() is called immediately after joy_init(), which is rather inconvenient (anyone heard of a port_unregister_handler()?) so we don't bother resetting at all but in the future this could cause problems + + + +----- + + + + perhaps we should not have been called to start with? + + + + + +Remarks in include/doshelpers.h + + +The Helper Interrupt uses the following groups: + + + +0x00 - Check for DOSEMU +0x01-0x11 - Initialisation functions & Debugging +0x12 - Set hogthreshold (aka garrot?) +0x20 - MFS functions +0x21-0x22 - EMS functions +0x28 - Garrot Functions for use with the mouse +0x29 - Serial functions +0x30 - (removed functionality) +0x33 - Mouse Functions +0x40 - CD-ROM functions +0x50-0x5f - DOSEMU/Linux communications +50 -- run unix command in ES:DX +51,52? +53 -- do system(ES:DX) +54 -- get CPU speed +55 -- get terminal type +0x60-0x6f - reserved for plug-ins +0x7a - IPX functions +0x8x -- utility functions +0x80 -- getcwd(ES:DX, size AX) +0x81 -- chdir(ES:DX) +0xdc - helper for DosC kernel +0xfe - called from our MBR, emulate MBR-code. +0xff - Terminate DOSEMU + + + +There are (as yet) no guidelines on choosing areas for new functions. + + + + + + + +The CPU_Intel group of Modules + + +These files all relate to Intel-x86 specific code. + + + +Functions in emu-i386/cpu.c + + +These are the functions defined in emu-i386/cpu.c. + + + +cpu_trap_0f + + +process opcodes 0F xx xx trapped by GP_fault +returns 1 if handled, 0 otherwise +Main difference with previous version: bits in our pseudo-control +regs can now be written. This should make CPU detection pgms happy. + + + + + +cpu_setup + + +Setup initial interrupts which can be revectored so that the kernel +does not need to return to DOSEMU if such an interrupt occurs. + + + + + + + +Functions in emu-i386/ports.c + + +These are the functions defined in emu-i386/ports.c. + + + +port_inb(ioport_t port) + + +Handles/simulates an inb() port IO read + + + + + +port_outb(ioport_t port, Bit8u byte) + + +Handles/simulates an outb() port IO write + + + + + +port_inw(ioport_t port) + + +Handles/simulates an inw() port IO read. Usually this invokes +port_inb() twice, but it may be necessary to do full word i/o for +some video boards. + + + + + +port_outw(ioport_t port, Bit16u word) + + +Handles/simulates an outw() port IO write + + + + + +port_ind(ioport_t port) + + +Handles/simulates an ind()/outd() port IO read/write. + + + + + +special_port_inb,special_port_outb + + +I don't know what to do of this stuff... it was added incrementally to +port.c and has mainly to do with video code. This is not the right +place for it... +Anyway, this implements some HGC stuff for X and the emuretrace +port access for 0x3c0/0x3da + + + + + +port_init() + + +Resets all the port port_handler information. +This must be called before parsing the config file - +This must NOT be called again when warm booting! +Can't use debug logging, it is called too early. + + + + + +extra_port_init() + + +Catch all the special cases previously defined in ports.c +mainly video stuff that should be moved away from here +This must be called at the end of initialization phase + + + +NOTE: the order in which these inits are done could be significant! +I tried to keep it the same it was in ports.c but this code surely +can still have bugs + + + + + +port_register_handler + + +Assigns a handle in the port table to a range of ports with or +without a device, and registers the ports + + + + + +set_ioperm + + +wrapper for the ioperm() syscall, returns -1 if not successful. + + + + + + + +Remarks in emu-i386/ports.c + + +The following port_{in|out}{bwd} functions are the main entry points to +the port code. They look into the port_handle_table and call the +appropriate code, usually the std_port_ functions, but each device is +free to register its own functions which in turn will call std_port or +directly access I/O (like video code does), or emulate it - AV + + + +----- + + + + optimized versions for rep - basically we avoid changing privileges +and iopl on and off lots of times. We are safe letting iopl=3 here +since we don't exit from this code until finished. +This code is shared between VM86 and DPMI. + + + +----- + + + + This is the core of the new emuretrace algorithm: +If a read of port 0x3da is performed we just set it +as pending and set ioperm OFF for port 0x3c0 +When a write to port 0x3c0 is then trapped, we perform +any pending read to 0x3da and reset the ioperm for +0x3c0 in the default ON state. +This way we avoid extra port accesses when the program +is only looking for the sync bits, and we don't miss +the case where the read to 0x3da is used to reset the +index/data flipflop for port 0x3c0. Futher accesses to +port 0x3c0 are handled at full speed. + + + +----- + + + + find out whether the port address request is available; +this way, try to deny uncoordinated access + + + +If it is not listed in /proc/ioports, register them +(we need some syscall to do so bo 960609)... +(we have a module to do so AV 970813) +if it is registered, we need the name of a device to open +if we can't open it, we disallow access to that port + + + +----- + + + + We need to check if our required port range is in use +by some device. So we look into proc/ioports to check +the addresses. Fine, but at this point we must supply +a device name ourselves, and we can't check from here +if it's the right one. The device is then open and left +open until dosemu ends; for the rest, in the original +code the device wasn't used, just locked, and only then +port access was granted. + + + + + +Items for Fixing in emu-i386/ports.c + + +This stuff should be moved to video code!! + + + +----- + + + + we should free the name but we are going to exit anyway + + + + + +Functions in emu-i386/do_vm86.c + + +These are the functions defined in emu-i386/do_vm86.c. + + + +vm86_GP_fault + + +All from the kernel unhandled general protection faults from V86 mode +are handled here. This are mainly port IO and the HLT instruction. + + + + + +run_vm86 + + +Here is where DOSEMU runs VM86 mode with the vm86() call +which also has the registers that it will be called with. It will stop +vm86 mode for many reasons, like trying to execute an interrupt, doing +port I/O to ports not opened for I/O, etc ... + + + + + +loopstep_run_vm86 + + +Here we collect all stuff, that has to be executed within +_one_ pass (step) of a loop containing run_vm86(). + + + + + + + +Remarks in emu-i386/do_vm86.c + + +Here we handle all prefixes prior switching to the appropriate routines +The exception CS:EIP will point to the first prefix that effects +the faulting instruction, hence, 0x65 0x66 is same as 0x66 0x65. +So we collect all prefixes and remember them. +- Hans Lermen + + + + + +Functions in emu-i386/cputime.c + + +These are the functions defined in emu-i386/cputime.c. + + + +GETcpuTIME + + +GETcpuTIME is a pointer to a function which returns the +relative CPU time. Different methods of getting the time can +then be implemented, currently there are two using +gettimeofday() for 486 and TSC for pentium + + + + + +GETusTIME(sc) + + +GETusTIME returns the DOS time with 1-usec resolution +using GETcpuTIME to get the implementation-dependent CPU time. +The 'sc' parameter is unused. + + + + + +GETtickTIME(sc) + + +GETtickTIME returns the DOS time with 838ns resolution +using GETcpuTIME to get the implementation-dependent CPU time. +The 'sc' parameter is unused. + + + + + +GETusSYSTIME() + + +GETusSYSTIME returns the real CPU time with 1-usec resolution + + + + + + + +Remarks in emu-i386/cputime.c + + +At the heart of the timing system in dosemu >= 0.67.11 is the availability +of the system time as a 64-bit [type hitimer_t] monoton value. +(a 64-bit timer on a 200MHz CPU increments by 2^48 a day). + + + +Dosemu needs this time under two resolutions: + + + + + + - a MICROSECOND resolution for general timing purposes + - a TICK(838ns) resolution for PIT + + + + + + On non-pentium machines, only the first one is available via the +kernel call gettimeofday(). On the pentium and up, the situation is better +since we have a cheap hi-res timer on-chip, and worse since this +timer runs at a speed depending from the CPU clock (which we need +to know/measure, and could be not 100% accurate esp. if the speed is +a non-integer multiple of 33.3333). + + + +dosemu >= 0.67.11 can use both timing methods (call them 486 and pentium), +and switch between them in a dynamic way when configuring. + + + +At the first level (local to the file cputime.c) there are the +RAW timer functions, addressed by RAWcpuTIME(). These get the +actual absolute CPU time in usecs. + + + +At the second level, GETcpuTIME() returns the relative, zero-based +system time. This is where the 486/pentium switch happens. + + + +The third level is the actual timer interface for dosemu and is +made of two functions: + + + + + + - GETusTIME(s) gives the time in usecs + - GETtickTIME(s) gives the time in ticks + + + + + + The 's' parameter can be used to control secondary time functions +like 'time stretching' (see the READMEs). +The function GETusSYSTIME() never activates this stretching, and +is used only by the realtime thread-based 1-sec timer in rtc.c. + + + +All timing are RELATIVE to a base. The use of a based time allows us +to play more freely with time, e.g. stop and restart it during debugging, +stretch it, make it go at different speeds between real-time and CPU +emulation, etc. The base has been chosen to be zero, because it will +avoid overflows in calculations, produce more readable and more easily +comparable debug log files, and also because only int0x1a and BIOS +timer require knowledge of the actual time, PIT and PIC are not sensitive. + + + + + +Functions in emu-i386/simx86/sigsegv.c + + +These are the functions defined in emu-i386/simx86/sigsegv.c. + + + +dosemu_fault(int, struct sigcontext); + + +All CPU exceptions (except 13=general_protection from V86 mode, +which is directly scanned by the kernel) are handled here. + + + + + + + + + +The Serial group of Modules + + +This is the code that works our serial emulation. This needs to be very fast +if we are to convince DOS that we have a very fast serial port. + + + +Remarks in base/serial/ser_defs.h + + +Extensions to serial debugging. + + + +SER_DEBUG_MAIN (0 or 1) +- extra debug output on the most critical information. + + + +SER_DEBUG_HEAVY (0 or 1) +- super-heavy extra debug output, including all ports reads and writes, +and every character received and transmitted! + + + +SER_DEBUG_INTERRUPT (0 or 1) +- additional debug output related to serial interrupt code, +including flagging serial interrupts, or PIC-driven code. + + + +SER_DEBUG_FOSSIL_RW (0 or 1) +- heavy FOSSIL debug output, including all reads and writes. + + + +SER_DEBUG_FOSSIL_STATUS (0 or 1) +- super-heavy FOSSIL debug output, including all status checks. + + + +You must recompile dosemu everytime one of these constants are modified. +Just type 'make' in the dosemu dir and it will recompile the changes only. + + + +----- + + + + IMPORTANT INFO about com[] variable array structure used in serial.c + + + +Most of the serial variables are stored in the com[] array. +The com[] array is a structure in itself. Take a look at the +'serial_t' struct declaration in the serial.h module for more info +about this. Only the most commonly referenced global variables +are listed here: + + + +config.num_ser Number of serial ports active. +com[x].base_port The base port address of emulated serial port. +com[x].real_comport The COM port number. +com[x].interrupt The PIC interrupt level (based on IRQ number) +com[x].mouse Flag mouse (to enable extended features) +com[x].fd File descriptor for port device +com[x].dev[] Filename of port port device +com[x].dev_locked Flag whether device has been locked + + + +The arbritary example variable 'x' in com[x] can have a minimum value +of 0 and a maximum value of (config.numser - 1). There can be no gaps +for the value 'x', even though gaps between actual COM ports are permitted. +It is strongly noted that the 'x' does not equal the COM port number. +This example code illustrates the fact, and how the com[] array works: + + + +for (i = 0; i < config.numser; i++) +s_printf("COM port number %d has a base address of %x", +com[i].real_comport, com[i].base_port); + + + + + +Functions in base/serial/ser_init.c + + +These are the functions defined in base/serial/ser_init.c. + + + +serial_init + + +This is the master serial initialization function that is called +upon startup of DOSEMU to initialize ALL the emulated UARTs for +all configured serial ports. The UART is initialized via the +initialize_uart function, which opens the serial ports and defines +variables for the specific UART. + + + +If the port is a mouse, the port is only initialized when i + + + + + + + +Items for Fixing in base/serial/ser_init.c + + +This needs more work before it is implemented into /etc/dosemu.conf as an 'rtscts' option. + + + + + +Functions in base/serial/ser_ports.c + + +These are the functions defined in base/serial/ser_ports.c. + + + +do_serial_in + + +The following function returns a value from an I/O port. The port +is an I/O address such as 0x3F8 (the base port address of COM1). +There are 8 I/O addresses for each serial port which ranges from +the base port (ie 0x3F8) to the base port plus seven (ie 0x3FF). +[num = abritary port number for serial line, address = I/O port address] + + + + + +do_serial_out + + +The following function writes a value to an I/O port. The port +is an I/O address such as 0x3F8 (the base port address of COM1). +[num = abritary port number for serial line, address = I/O port address, +val = value to write to I/O port address] + + + + + + + +Items for Fixing in base/serial/ser_ports.c + + +Should clearing UART cause THRE int if it's enabled? */ + + + +----- + + + + Fix the calculation assumption + + + +----- + + + + Is this safe to put this here? */ + + + +----- + + + + Is this safe to put this here? */ + + + + + +Functions in base/serial/ser_irq.c + + +These are the functions defined in base/serial/ser_irq.c. + + + +serial_int_engine + + +This function is the serial interrupts scheduler. Its purpose is to +update interrupt status and/or invoke a requested serial interrupt. +If interrupts are not enabled, the Interrupt Identification Register +is still updated and the function returns. See pic_serial_run() below +it is executed right at the instant the interrupt is actually invoked. + + + +Since it is not possible to run the interrupt on the spot, it triggers +the interrupt via the pic_request() function (which is in pic.c) +and sets a flag that an interrupt is going to be occur soon. + + + +Please read pic_serial_run() for more information about interrupts. +[num = port, int_requested = the requested serial interrupt] + + + + + +pic_serial_run + + +This function is called by the priority iunterrupt controller when a +serial interrupt occurs. It executes the highest priority serial +interrupt for that port. (Priority order is: RLSI, RDI, THRI, MSI) + + + +Because it is theoretically possible for things to change between the +interrupt trigger and the actual interrupt, some checks must be +repeated. + + + + + +serial_run + + +This is the main housekeeping function, which should be called about +20 to 100 times per second. The more frequent, the better, up to +a certain point. However, it should be self-compensating if it +executes 10 times or even 1000 times per second. Serial performance +increases with frequency of execution of serial_run. + + + +Serial mouse performance becomes more smooth if the time between +calls to serial_run are smaller. + + + + + + + +Remarks in base/serial/ser_irq.c + + +Linux code hackers: How do I detect a break signal without having +to rely on Linux signals? Can I peek a 'break state bit'? +Also, how do I 'turn on' and 'turn off' the break state, via +an ioctl() or tcsetattr(), rather than using POSIX tcsendbrk()? + + + + + +Items for Fixing in base/serial/ser_irq.c + + +how do we cancel a PIC interrupt, when we have come this far? + + + + + +Functions in base/serial/int14.c + + +These are the functions defined in base/serial/int14.c. + + + +int14 + + +The following function executes a BIOS interrupt 0x14 function. +This code by Mark Rejhon replaced some very buggy, old int14 interface +a while back. These routines are not flawless since it does not wait +for a character during receive, and this may confuse some programs. + + + + + + + +New Ideas for base/serial/int14.c + + +If any of you coders are ambitious, try thinking of the following: +- Converting this into inline assembler and use direct port access + + + + + +Items for Fixing in base/serial/fossil.c + + +This really should be write-with-wait. */ + + + + + +Items for Fixing in include/serial.h + + +Why does a RX_BUFFER_SIZE of 256 cause slower performance than a size of 128? + + + + + + + +The Mouse group of Modules + + +All of the Mouse handling code is in the "mouse" subdirectory. + + + +There are only 2 main files, mouse.c and mouseint.c. + + + +Functions in base/mouse/mouse.c + + +These are the functions defined in base/mouse/mouse.c. + + + +mouse_init + + +Initialize internal mouse. + + + + + + + +Remarks in base/mouse/mouse.c + + +I have not properly tested this INT74 - JES 96/10/20 +I have removed it. INT74 is irq 12. Which I suppose is the proper +irq for a ps2 mouse. It appears initial support was planned to +support irq 12 and at Mouse_ROUTINE_OFF is a routine that +acknowledges an irq. That routine is probably what should be +acknowledging irq12, and what int 0x74 should point to. +I have disabled int0x74 support for now. --EB 29 Oct 1997 +Got it working --BO 4 Nov 2004 + + + +----- + + + + Whoever wrote the dos mouse driver spec was brain dead... +For some video modes the mouse driver appears to randomly +pick a shift factor, possibly to keep at least a 640x200 resolution. + + + +The general programming documentation doesn't make this clear. +And says that in text modes it is safe to divide the resolution by +8 to get the coordinates in characters. + + + +The only safe way to handle the mouse driver is to call function +0x26 Get max x & max y coordinates and scale whatever the driver +returns yourself. + + + +To handle programs written by programmers who weren't so cautious a +doctrine of least suprise has been implemented. + + + +As much as possible do the same as a standard dos mouse driver in the +original vga modes 0,1,2,3,4,5,6,7,13,14,15,16,17,18,19. + + + +For other text modes allow the divide by 8 technique to work. +For other graphics modes return x & y in screen coordinates. +Except when those modes are either 40x?? or 320x??? +and then handle the x resolution as in 40x25 and 320x200 modes. + + + +320x200 modes are slightly controversial as I have indications that +not all mouse drivers do the same thing. So I have taken the +simplest, and most common route, which is also long standing dosemu +practice of always shifting the xaxis by 1. When I researched this +I could find no examples that did otherwise. + + + +-- Eric Biederman 19 August 1998 + + + +This code has now been updated so it defaults as above but allows +work arounds if necessary. Because tweaking dosemu is easier +than fixing applications without source. + + + +-- Eric Biederman 29 May 2000 + + + + + + + +The Bios group of Modules + + +All of the Bios code is in the "bios" subdirectory. + + + +DOSEMU requires certain code to be coded in assembler and also code to +be located in the F000 segment. This is where all such code should be +put. + + + +Functions in base/bios/hlt.c + + +These are the functions defined in base/bios/hlt.c. + + + +hlt_init(void) + + +Resets all the HLT handlers + + + + + +hlt_handle() + + +Handles a HLT instruction reached inside the dos emulator. + + + + + + + + + +The PIC group of Modules + + +All of the PIC handling code is in the "PIC" subdirectory. + + + +Functions in base/dev/pic/pic.c + + +These are the functions defined in base/dev/pic/pic.c. + + + +pic_print + + +This is the pic debug message printer. It writes out some basic +information, followed by an informative message. The basic information +consists of: +interrupt nesting counter change flag (+, -, or blank) +interrupt nesting count (pic_icount) +interrupt level change flag (+, -, or blank) +current interrupt level +interrupt in-service register +interrupt mask register +interrupt request register +message part one +decimal data value +message part two + + + +If the message part 2 pointer is a null pointer, then only message +part one (without the data value) is printed. + + + +The change flags are there to facilitate grepping for changes in +pic_ilevel and pic_icount + + + +To avoid line wrap, the first seven values are printed without labels. +Instead, a header line is printed every 15 messages. + + + + + +write_pic0,write_pic1 + + +write_pic_0() and write_pic1() implement dos writes to the pic ports. +They are called by the code that emulates inb and outb instructions. +Each function implements both ports for the pic: pic0 is on ports +0x20 and 0x21; pic1 is on ports 0xa0 and 0xa1. These functions take + + + +Arguments are: + + + + + + +read_pic0,read_pic1 + + +read_pic0 and read_pic1 return the values for the interrupt mask register +(port 1), or either the in service register or interrupt request register, +as determined by the last OCW3 command (port 0). These functions take +a single parameter, which is a port number (0 or 1). They are called by +code that emulates the inb instruction. + + + + + +pic_seti + + +pic_seti is used to initialize an interrupt for dosemu. It requires +four parameters. The first parameter is the interrupt level, which +may select the NMI, any of the IRQs, or any of the 16 extra levels +(16 - 31). The second parameter is the dosemu function to be called +when the interrupt is activated. This function should call do_irq() +if the DOS interrupt is really to be activated. If there is no special +dosemu code to call, the second parameter can specify do_irq(), but +see that description for some special considerations. +The third parameter is a number of an interrupt to activate if there is +no default interrupt for this ilevel. +The fourth parameter is the dosemu function to be called from do_irq(). +Required by some internal dosemu drivers that needs some additional code +before calling an actual handler. This function MUST provide a EOI at +the end of a callback. + + + + + +run_irqs + + +run_irqs, which is initiated via the macro pic_run, is the "brains" of +the pic. It is called from the vm86() loop, checks for the highest +priority interrupt requested, and executes it. This function is +written in assembly language in order to take advantage of atomic +(indivisible) instructions, so that it should be safe for a two +process model, even in a multiple CPU machine. A c language +version was started, but it became impossible, even with in-line +assembly macros, because such macros can only return a single result. +If I find a way to do it in c, I will, but don't hold your breath. + + + +I found a way to write it in C --EB 15 Jan 97 + + + + + +do_irq + + +do_irq() calls the correct do_int(). +It then executes a vm86 loop until an outb( end-of-interrupt) is found. +For priority levels 0 and >15 (not real IRQs), vm86 executes once, then +returns, since no outb20 will come. +Returns: 0 = complete, 1 = interrupt not run because it directly +calls our "bios" See run_timer_tick() in timer.c for an example +To assure notification when the irq completes, we push flags, ip, and cs +here and fake cs:ip to PIC_[SEG,OFF], where there is a hlt. This makes +the irq generate a sigsegv, which calls pic_iret when it completes. +pic_iret then pops the real cs:ip from the stack. +This routine is RE-ENTRANT - it calls run_irqs, +which may call an interrupt routine, +which may call do_irq(). Be Careful! !!!!!!!!!!!!!!!!!! +No single interrupt is ever re-entered. + + + +Callers: +base/misc/ioctl.c +base/keyboard/serv_8042.c +base/keyboard/keyboard-server.c +base/serial/ser_irq.c +dosext/sound/sound.c +dosext/net/net/pktnew.c + + + + + +pic_resched + + +pic_resched decrements a count of interrupts on the stack +(set by do_irq()). If the count is then less or equal to some pre-defined +value (normally 1, pic_icount_od), pic_resched moves all queued interrupts +to the interrupt request register. + + + +Normally it is called from pic_iret(), but it can also be called +directly if dosemu was fooled by the program and failed to catch iret. + + + + + +pic_request + + +pic_request triggers an interrupt. There is presently no way to +"un-trigger" an interrupt. The interrupt will be initiated the +next time pic_run is called, unless masked or superceded by a +higher priority interrupt. pic_request takes one argument, an +interrupt level, which specifies the interrupt to be triggered. +If that interrupt is already active, the request will be queued +until all active interrupts have been completed. The queue is +only one request deep for each interrupt, so it is the responsibility +of the interrupt code to retrigger itself if more interrupts are +needed. + + + + + +pic_iret + + +pic_iret is used to sense that all active interrupts are really complete, +so that interrupts queued by pic_request can be triggered. +Interrupts end when they issue an outb 0x20 to the pic, however it is +not yet safe at that time to retrigger interrupts, since the stack has +not been restored to its initial state by an iret. pic_iret is called +whenever interrupts have been enabled by a popf, sti, or iret. It +determines if an iret was the cause by comparing stack contents with +cs and ip. If so, it calls pic_resched() and does the actual iret by +pop'ing ip and cs from stack. +It is possible for pic_iret to be fooled by dos code; for this reason +active interrupts are checked, any queued interrupts that are also +active will remain queued. +Also, some programs fake an iret, so that it is possible for pic_iret +to fail. +See pic_watch for the watchdog timer that catches and fixes this event. + + + + + +pic_watch + + +pic_watch is a watchdog timer for pending interrupts. If pic_iret +somehow fails to activate a pending interrupt request for 2 consecutive +timer ticks, pic_watch will activate them anyway. pic_watch is called +ONLY by timer_tick, the interval timer signal handler, so the two functions +will probably be merged. + + + + + +pic_pending + + +This function returns a non-zero value if the designated interrupt has +been requested and is not masked. In these circumstances, it is important +for a hardware emulation to return a status which does *not* reflect the +event(s) which caused the request, until the interrupt actually gets +processed. This, in turn, hides the interrupt latency of pic from the dos +software. + + + +The single parameter ilevel is the interrupt level (see pic.h) of the +interrupt of interest. + + + +If the requested interrupt level is currently active, the returned status +will depend upon whether the interrupt code has re-requested itself. If +no re-request has occurred, a value of false (zero) will be returned. + + + + + +pic_activate + + +pic_activate requests any interrupts whose scheduled time has arrived. +anything after pic_dos_time and before pic_sys_time is activated. +pic_dos_time is advanced to the earliest time scheduled. + + + + + +pic_sched + + +pic_sched schedules an interrupt for activation after a designated +time interval. The time measurement is in unis of 1193047/second, +the same rate as the pit counters. This is convenient for timer +emulation, but can also be used for pacing other functions, such as +serial emulation, incoming keystrokes, or video updates. Some sample +intervals: + + + +rate/sec: 5 7.5 11 13.45 15 30 60 +interval: 238608 159072 108459 88702 79536 39768 19884 + + + +rate/sec: 120 180 200 240 360 480 720 +interval: 9942 6628 5965 4971 3314 2485 1657 + + + +rate/sec: 960 1440 1920 2880 3840 5760 11520 +interval: 1243 829 621 414 311 207 103 + + + +pic_sched expects two parameters: an interrupt level and an interval. +To assure proper repeat scheduling, pic_sched should be called from +within the interrupt handler for the same interrupt. The maximum +interval is 15 minutes (0x3fffffff). + + + + + + + +Remarks in base/dev/pic/pic.c + + +pic_push,pic_pop + + + +Pic maintains two stacks of the current interrupt level. an internal one +is maintained by run_irqs, and is valid whenever the emulator code for +an interrupt is active. These functions maintain an external stack, +which is valid from the time the dos interrupt code is called until +the code has issued all necessary EOIs. Because pic will not necessarily +get control immediately after an EOI, another EOI (for another interrupt) +could occur. This external stack is kept strictly synchronized with +the actions of the dos code to avoid any problems. pic_push and pic_pop +maintain the external stack. + + + + + + + +The Sound group of Modules + + +The sound code provides emulation of the SB. The actual emulation provided +depends upon the support available from the kernel sound driver. Because +this is very OS dependant the driver code itself is kept in architecture +specifc files under src/arch/osname/dosext/sound/. Communication is via +a set of interface functions and the device independant structures. + + + +Functions in dosext/sound/sound.c + + +These are the functions defined in dosext/sound/sound.c. + + + +sb_io_read + + +Arguments are: + + + + + + + port - The I/O port being read from. + + + + + +This handles all of the reads for the SB emulation. The value read is +returned. The value of 0xFF indicates an invalid read. (assumes the ports +float high when not pulled low by the hardware.) + + + + + +adlib_io_read + + +Arguments are: + + + + + + + port - The I/O port being read from. + + + + + +This handles all of the reads for the adlib (FM) emulation. The value read +is returned. The value of 0xFF indicates an invalid read. (assumes the ports +float high when not pulled low by the hardware.) +The FM emulation is not written yet. The current plan is to use the midi +emulation where available as this is the most common use for the FM sound. + + + + + +mpu401_io_read + + +Arguments are: + + + + + + + port - The I/O port being read from. + + + + + +The MPU-401 functionality is primarily provided by 'midid' - a standalone +program. This makes most of the MPU-401 code simply a pass-through driver. + + + + + +sb_io_write + + +Arguments are: + + + + + + + port - The I/O port being written to. + + + + + + value - The value being output. + + + + + +This handles the writes for the SB emulation. Very little of the processing +is performed in this function as it basically consists of a very large +switch() statement. The processing here is limited to trivial (1 line) items +and distinguishing between the different actions and responses that the +different revisions of the SB series give. + + + + + +sb_dsp_write + + +Arguments are: + + + + + + + value - The value being written to the DSP. + + + + + +The SB DSP is a complete I/O system in itself controlled via a number of +data bytes. The number of bytes depends upon the function. The function +to be executed is determined by the first byte. +If there is no existing command then the command is stored. This then used +in the switch to identify the action to be taken. When the command has +supplied all of its arguments, or failed, then the command storage is +cleared. Each DSP function is responsible for clearing this itself. +Again, this function relies on other functions to do the real work, and +apart from storing details of the command and parameters is basically a +large switch statement. + + + + + + + +Remarks in dosext/sound/sound.c + + +Write silence could probably be implemented by setting up a +"DMA" transfer from /dev/null - AM + + + + + +Items for Fixing in dosext/sound/sound.c + + +The file header needs tidying up a _LOT_ ! */ + + + +----- + + + + Adlib status reads are unimplemented */ + + + +----- + + + + Advanced adlib reads are unimplemented */ + + + +----- + + + + CMS Writes are unimplemented. + + + +----- + + + + DSP Status is unimplemented + + + +----- + + + + Adlib Waveform tests are unimplemented */ + + + +----- + + + + Advanced Adlib register writes are unimplemented */ + + + +----- + + + + Advanced Adlib data writes are unimplemented */ + + + +----- + + + + SB Midi is Unimplemented + + + +----- + + + + Sine Generation is unimplemented + + + +----- + + + + AUX Status is Unimplemented + + + + + +Remarks in base/dev/dma/dma.c + + +**** WARNING **** +This Code _HAS_ changed. + + + +----- + + + + The Emulated DMA channels are provided by using files and writes. +This means that they are easy to track. +It might cause problems when attempting to interface to the REAL DMA +controller. (Necessary to talk to hardware which uses DMA.) + + + +Note that DMA controller 2 uses word granular addressing and controller 1 +uses byte granular address ... this simplifies the code ! + + + + + +Items for Fixing in base/dev/dma/dma.c + + +: Cascade Mode Reads are not supported + + + +----- + + + + : The Verify Mode is not supported + + + +----- + + + + : The Invalid Mode is not supported (!) + + + + + + + +The FileAccess group of Modules + + +This hold all kind of accessing files on a Unix filesysten from DOS. + + + +Remarks in dosext/mfs/mfs.c + + +The msdos_dir_ent structure has much more than 28 bytes. +Is this significant? + + + +----- + + + + Added compares to device so that newer versions of Foxpro which test directories +using xx\yy\device perform closer to whats DOS does. + + + + + +Items for Fixing in dosext/mfs/mfs.c + + +We probably should use llseek here for file > 2 GBytes + + + +----- + + + + returned size of struct dir_ent seems wrong at 28 bytes. */ + + + + + + + +And Finally ... + + +The Following items are used to delimit the text used to create this file. +Whilst it is not necessary to know this, they are included because they may +be useful for searching, as they are (at least at the moment) reasonably +unique. + + + +DANG_BEGIN_MODULE / DANG_END_MODULE +This will bracket a description of the file (normally at the +start). Within this you may have the keyword 'Maintainer:' followed +by a list (one line each) of maintainers for this packet. These +will be turned into URLs. + + + +DANG_BEGIN_FUNCTION / DANG_END_FUNCTION +This brackets a description of functions (good this, isn't it!) +Not every function needs to be described in this way - just the +major ones. Within this you may have the keywords: `arguments:', +`return:' and `description:', which will sort out the information +following it to build proper lists. + + + +DANG_BEGIN_STRUCT / DANG_END_STRUCT +This brackets a description of structures and data definitions +Not every structure needs to be described in this way - just the +major ones. Within this you may have the keywords: `elements:', +and `description:', which will sort out the information +following it to build proper lists. Also, you may bracket the +structur definition of real C-code, given you have one element +per line. In this case comments (/*...*/) behind the element will +be inserted properly into the formatted list while the C-code +itself is still compilable. + + + +DANG_BEGIN_REMARK / DANG_END_REMARK +This brackets descriptions of obscure items, like data structures +and architecture. + + + +DANG_FIXTHIS +This is a one line item, indicating a an area requiring a fix, or +redesign. + + + +DANG_BEGIN_NEWIDEA / DANG_END_NEWIDEA +New Ideas Start Here! As Ideas are proposed, that get added with +their description, so that future generations can laugh at or +code the ideas ..... These bracket the idea description. + + + +DANG_BEGIN_CHANGELOG / DANG_END_CHANGELOG +Changelogs - very useful for bug fixing, and avvailable for use +with DPR (or that's the theory) + + + +In addition there are some keywords, that are recognized within a bracket. + + + +VERB ... /VERB +This formats the enclosed text verbatim. This is valid within +*_MODULE, *_REMARK, *_STRUCT, *_FUNCTION + + + +REMARK ... /REMARK +This is only valid within *_MODULE and also can contain VERB +brackets. Its useful to when you want to have a global modul +description + + + +PROTO ... /PROTO +This is only valid within *_FUNCTION and +takes a C-function prototype as `verbatim' until either +a `{' or a /PROTO is seen. After this all input is `skipped' +until the next PROTO or a /SKIP. + + + +SKIP ... /SKIP +This is only valid within *_FUNCTION and skips formatting until +either PROTO or /SKIP is seen. + + + + +
diff --git a/src/doc/DANG/DANG_CONFIG b/src/doc/DANG/DANG_CONFIG new file mode 100644 index 0000000..b498964 --- /dev/null +++ b/src/doc/DANG/DANG_CONFIG @@ -0,0 +1,108 @@ +# +# DANG configuration Data - For v0.3 +# + +TITLE: The DOSEMU Alterer Novices Guide +AUTHOR: Alistair MacDonald, <alistair@slitesys.demon.co.uk> +ABSTRACT: This Document is the DOSEMU Alterer Novices Guide. It is known as the DANG. + +# BASEDIR sets a path fragment which is prepended to _all_ paths. +BASEDIR: ../../ + +# GROUPs are formed from : +# ... +# These may be split over multiple lines, and use and whitespace characters +# as separators (assuming the Perl lives up to it's claims ! 8-) ) +GROUP: Main doc/DANG/summary_Main dos.c emu.c include/emu.h +GROUP: Init doc/DANG/summary_Init base/init/init.c + base/init/config.c + base/init/dev_list.c +#GROUP: Clients doc/DANG/summary_Clients clients/ncurses.c +GROUP: DPMI doc/DANG/summary_DPMI dosext/dpmi/dpmi.c + dosext/dpmi/msdos.h +GROUP: Video doc/DANG/summary_Video env/video/vc.c + env/video/video.c plugin/X/X.c env/video/vgaemu.c + env/video/vesa.c + env/video/vesabios.S + env/video/vesabios_pm.S + env/video/attremu.c env/video/dacemu.c + env/video/seqemu.c env/video/crtcemu.c + env/video/remap.c env/video/remap_asm.S + env/video/console.c + env/video/dualmon.c env/video/et4000.c env/video/hgc.c + base/bios/int10.c env/video/s3.c env/video/terminal.c + env/video/trident.c env/video/vga.c + env/video/vgaemu.c include/vgaemu.h + env/video/instremu.c +GROUP: New_Keyboard doc/DANG/summary_New_Keyboard + plugin/kbd_unicode/keyboard.c + plugin/kbd_unicode/keymaps.c + plugin/kbd_unicode/serv_xlat.c + plugin/kbd_unicode/serv_backend.c + plugin/kbd_unicode/serv_8042.c + plugin/kbd_unicode/keyb_clients.c + plugin/kbd_unicode/keyb_none.c + plugin/kbd_unicode/keyb_raw.c + plugin/term/keyb_slang.c + plugin/term/term_core.c +GROUP: Misc doc/DANG/summary_Misc dosext/misc/emm.c + dosext/misc/xms.c + base/async/int.c include/int.h + arch/linux/async/sigsegv.c + arch/linux/async/signal.c + include/ports.h base/misc/dosio.c base/misc/disks.c + dev/misc/lpt.c + base/dev/misc/timers.c + base/speaker/speaker.c + base/misc/shared.c + base/misc/dos2linux.c + base/misc/ioctl.c + base/dev/misc/cmos.c + base/dev/misc/lpt.c + base/dev/misc/pci.c + base/dev/misc/joystick.c + include/doshelpers.h + dosext/drivers/aspi.c +GROUP: CPU_Intel doc/DANG/summary_CPU_Intel + emu-i386/cpu.c + emu-i386/ports.c + emu-i386/do_vm86.c + emu-i386/fake_cpu.c + emu-i386/n_ports.c + emu-i386/cputime.c + emu-i386/simx86/sigsegv.c +GROUP: Serial doc/DANG/summary_Serial + base/serial/ser_defs.h base/serial/ser_init.c + base/serial/ser_ports.c base/serial/ser_irq.c + base/serial/int14.c base/serial/fossil.c + include/serial.h +#base/serial/README.serial +GROUP: Mouse doc/DANG/summary_Mouse base/mouse/mouse.c +GROUP: Bios doc/DANG/summary_BIOS base/bios/bios.S + base/bios/hlt.c +GROUP: PIC doc/DANG/summary_PIC base/dev/pic/pic.c include/pic.h +GROUP: Sound doc/DANG/summary_Sound dosext/sound/sound.c + arch/linux/dosext/sound/linux_sound.c + base/dev/dma/dma.c +GROUP: FileAccess doc/DANG/summary_FileAccess + dosext/mfs/mfs.c +#GROUP: Net doc/DANG/summary_Net dosext/net/net/ipx.c +#GROUP: Shared doc/DANG/summary_SHARED dosemu/shared.c +GROUP: Debugger doc/DANG/summary_Debugger + arch/linux/debugger/dosdebug.c + include/mhpdbg.h + arch/linux/debugger/mhpdbg.c + arch/linux/debugger/mhpdbgc.c + +# INTRODUCTION is the location of Introduction. There may be +# more than one of these. +INTRODUCTION: doc/DANG/DANG_intro + +# FINALLY is the location of a section to place at the end. There may be +# more than one of these. +FINALLY: doc/DANG/DANG_outro + +# Local Variables: +# ff-paths-for-file: ("../../") +# End: + diff --git a/src/doc/DANG/DANG_c.pl b/src/doc/DANG/DANG_c.pl new file mode 100755 index 0000000..049ac80 --- /dev/null +++ b/src/doc/DANG/DANG_c.pl @@ -0,0 +1,1254 @@ +#!/usr/bin/perl +# + +if ($0 =~ /DANG_c\.pl/) {$dang_mode = 1;} +else {$dang_mode = 0;} + +# if (dang_mode) { +# +# DANG Compiler +# ------------- +# +# Converts the comments in the DOSEMU source code into items suitable for +# inclusion in the DANG. +# +# The Markers are : +# +# DANG_BEGIN_MODULE / DANG_END_MODULE +# DANG_BEGIN_CHANGELOG / DANG_END_CHANGELOG +# DANG_BEGIN_REMARK / DANG_END_REMARK +# DANG_BEGIN_FUNCTION / DANG_END_FUNCTION +# DANG_BEGIN_NEWIDEA / DANG_END_NEWIDEA +# DANG_FIXTHIS +# +# +# Version 0.1: +# 11/ 6/94 AM Coding commences ... finally ! +# Version 0.2: +# 12/ 6/94 AM James suggests SGML - so here goes (it's easier than texinfo) +# Version 0.3: +# ??/??/?? AM Added the configuration file +# Version 0.4: +# 7/ 9/94 AM Made major modifications to rationalise the FUNCTION system. +# Modified the options. Added Indexing. Fixed single line +# item stripping. +# Version 0.5: +# 3/ 2/96 AM Made this thing PERL5 happy, and added a couple of minor +# features. Added -i. This is likely to be the last version. +# Hopefully this will be succeeded by a more generic system. +# Version 0.5a: +# 18/ 3/97 AM Changed the method of finding the version number. +# +# Version 0.6: +# 18/ 6/00 AM Finally produces valid linuxdoc-96 output +# +# +# +# } +# +# if (!dang_mode) { +# +# NOTE: This is retrieved from the work of Alistair MacDonald +# and (though adapted to general purpose) remains +# (C) Alistair MacDonald +# +# The original software is named DANG (Dosemu Alterer Novice Guide) and +# as such is part of the DOSEMU distribution. +# +# SIDOC (Source Imbedded DOCumentation) Compiler +# --------------------------------------------- +# +# Converts the comments in the source code into items suitable for +# inclusion in the SIDOC. +# +# The Markers are : +# +# SIDOC_BEGIN_MODULE / SIDOC_END_MODULE +# SIDOC_BEGIN_CHANGELOG / SIDOC_END_CHANGELOG +# SIDOC_BEGIN_REMARK / SIDOC_END_REMARK +# SIDOC_BEGIN_FUNCTION / SIDOC_END_FUNCTION +# SIDOC_BEGIN_NEWIDEA / SIDOC_END_NEWIDEA +# SIDOC_FIXTHIS +# +# } +# +# +# Command line options +# -------------------- +# -v - verbose. +# -c fname - use configuration file. +# -i - 'Irritate' by commenting on missing items. +# fname ... - verify the files. +# + +undef $opt_c; + +require "getopts.pl"; + +&Getopts('ivc:'); # Look for the flags ... + +{ + local (*INPUT); + + undef @groups; + undef @intro; + undef @finally; + + $main_title = ""; + $main_author = ""; + $main_abstract = ""; + + if ($opt_c !~ /.+/) { + die "$0: The option -c takes one compulsory argument - a configuration filename.\n"; + } elsif (defined $opt_c) { + $INPUT = "<". $opt_c; + open (INPUT) || die "Can't open $INPUT as a configuration file."; + + while () { + &build_config ($_); + } + &build_config (undef); + close INPUT; + + if ($dang_mode) {&get_dosemu_version;} + else {&get_version;} + + if ($dang_mode) {$OUTPUT = ">>DANG.sgml";} + else {$OUTPUT = ">>SIDOC.sgml";} + open OUTPUT; + + print OUTPUT <<"EndOfPreamble"; + + + + +
+ + + $main_title + $main_author + version $version + +$main_abstract + + + + + +Introduction + +

+EndOfPreamble + + + if (defined @intro) { + $INPUT = "<". $base_dir. shift @intro; + open (INPUT) || die "Can't open $INPUT as a introduction file."; + + while () { + $temp = &protect_specials($_); + print OUTPUT $temp; + } + print OUTPUT "\n"; + + close INPUT; + + foreach (@intro) { + $INPUT = "<". $base_dir. $_; + open (INPUT) || die "Can't open $INPUT as an introduction file."; + + print OUTPUT "-----\n\n"; + while () { + $temp = &protect_specials($_); + print OUTPUT $temp; + } + print OUTPUT "\n"; + + close $INPUT; + } + + print OUTPUT "\n\n"; + + undef @intro; + } elsif ($opt_i) { + print OUTPUT "

There appears to be no Introduction at this time.

\n\n"; + } + + print OUTPUT "

\n\n"; + + foreach $group (@groups) { + ($name, $summary, @elements) = split(/[ \t\n]+/, $group); + + if ((!defined $name) || (!defined $summary) || (!defined @elements)) + { + die "Missing Data in the group $group\n"; + } + + print OUTPUT "The $name group of Modules\n

\n"; + + $INPUT = "<". $base_dir. $summary; + open (INPUT) || die "Can't open $INPUT as a summary file."; + + while () { + $temp = &protect_specials($_); + print OUTPUT $temp; + } + print OUTPUT "

\n\n"; + + close INPUT; + + foreach $fname (@elements) + { + &scan_file ($fname); + } + + print OUTPUT "
\n\n"; + } + + if (defined @finally) { + + print OUTPUT "And Finally ...\n\n

"; + + $INPUT = "<". $base_dir. shift @finally; + open (INPUT) || die "Can't open $INPUT as a termination file."; + + while () { + $temp = &protect_specials($_); + print OUTPUT $temp; + } + print OUTPUT "

\n"; + + close INPUT; + + foreach (@finally) { + $INPUT = "<". $base_dir. $_; + open (INPUT) || die "Can't open $INPUT as an termination file."; + + print OUTPUT "

-----

\n\n

"; + while () { + $temp = &protect_specials($_); + print OUTPUT $temp; + } + print OUTPUT "

\n"; + + close $INPUT; + } + + print OUTPUT "\n\n"; + + undef @finally; + } + print OUTPUT "
\n\n"; + + + print OUTPUT "\n
\n"; + print OUTPUT "\n
\n"; + } else { # No configuration file ... + $OUTPUT = ">/dev/null"; + foreach $fname (@ARGV) + { + &scan_file ($fname); + } + + } + + close OUTPUT; +} + +# Sets $version to be the Version of DOSEMU used to generate the source. +# This is grabbed from dosconfig.h + +sub get_dosemu_version { + local (*INPUT); + local ($dat); + $INPUT = $base_dir. "../VERSION"; + open INPUT; + $dat = ; + chop $dat; + $version = "dosemu-$dat"; + close INPUT; +} + +# Sets $version to be the Version of the source paket. +# This is grabbed from main Makefile + +sub get_version { + local (*INPUT); + local ($pl, $ver, $sublv); + + $INPUT = $base_dir. "Makefile"; + open INPUT; + + while () { + if (/PATCHLEVEL="(.*)"/) { + $pl = $1; + next; + } + if (/^VERSION="(.*)"/) { + $ver = $1; + } + if (/^SUBLEVEL="(.*)"/) { + $sublv = $1; + } + } + + $version = "v$ver.$sublv.$pl"; + close INPUT; +} + +# Yup - Its another one of these pattern matchers .... 8-( +# + +sub build_config { + local ($input) = $_; + + # Right - Config files: + # '#' signify comments - any extra character will be deleted. + # GROUP: ... + # This may be spread over multiple lines ... Groups are related files + # ie the mouse files, the video files. They are related in DANG/SIDOC too ... + # SUMMARY: + # The master summary file. + # BASEDIR: directory + # Directory to run _all_ paths from ... (no direct paths .. 8-( ) + # + # note: Comments may appear anywhere on the line, but the keywords must + # be the first non-whitespace characters ... + + if (!defined $input) { + if ($group) { + push (@groups, $data); + undef $data; + $group --; + } + + return; + } + + s/\#.*$//; # Strip out comments ... + + chop; + + if (/^\s*TITLE:\s*/) { + $main_title = "$main_title" . $'; + return; + } + + if (/^\s*AUTHOR:\s*/) { + $main_author = "$main_author" . $'; + return; + } + + if (/^\s*ABSTRACT:\s*/) { + $main_abstract = "$main_abstract" . $'; + return; + } + + if (/^\s*GROUP:\s*/) { + if ($group) { + push (@groups, $data); + } else { + $group ++; + } + $data = $'; + return; + } + + if (/^\s*INTRODUCTION:\s*/) { + if ($group) { + push (@groups, $data); + undef $data; + $group --; + } + push (@intro, $'); + return; + } + + if (/^\s*FINALLY:\s*/) { + if ($group) { + push (@groups, $data); + undef $data; + $group --; + } + push (@finally, $'); + return; + } + + if (/^\s*BASEDIR:\s*/) { + if ($group) { + push (@groups, $data); + undef $data; + $group --; + } + $base_dir = $'; + return; + } + + if ($group) { + $data .= $_; + } +} + + + +# This converts any special characters in the output into the relevant +# protected items .... + +sub protect_specials { + local ($text) = @_; + + $text =~ s/&/&/g; + $text =~ s/\<\\/&etago;/g; + $text =~ s//>/g; + $text =~ s/\$/$/g; + $text =~ s/\#/#/g; + $text =~ s/%/%/g; +# $text =~ s/\'{1}/''/g; +# $text =~ s/\`{1}/``/g; +# $text =~ s/"/&dquo;/g; # " (For emacs !) + $text =~ s/\[/[/g; + $text =~ s/\]/]/g; + + return $text; +} + + +# This back-converts any special characters in the output into the relevant +# protected items .... + +sub un_protect_specials { + local ($text) = @_; + + $text =~ s/]/\]/g; + + $text =~ s/[/\[/g; + $text =~ s/&dquot;/"/g; # " (For emacs !) + $text =~ s/%/%/g; + $text =~ s/#/\#/g; + $text =~ s/$/\$/g; + $text =~ s/>/>/g; + $text =~ s/</) + { + $line ++; + + if (/(SIDOC|DANG)_BEGIN_MODULE/) { + # Module Start Found ... + if ($module != 0) { + print "Already Found a Module start at line $start_line. Ignoring.\n"; + } else { + $module ++; + if ($scanning != 0) { + &abort_scan ($filename) + } else { + if ($opt_v) { + print "MODULE Start found at line $line.\n"; + } + + $scanning ++; + $start_line = $line; + $dat = $'; + } + } + next; + } + + if (/(SIDOC|DANG)_END_MODULE/) { + # Module End Found. + if ($module != 1) { + print "No Module Start found (END at line $line). Ignoring.\n"; + } else { + if ($opt_v) { + print "MODULE end found at line $line.\n"; + } + + $module --; + $dat .= $`; + push (@modules, &strip_comments ($dat)); + undef $dat; + $scanning --; + } + next; + } + + if (/(SIDOC|DANG)_BEGIN_CHANGELOG/) { + # Changelog Start Found ... + if ($changes != 0) { + print "Already Found a Changelog start at line $start_line. Ignoring.\n"; + } else { + $changes ++; + if ($scanning != 0) { + &abort_scan ($filename) + } else { + if ($opt_v) { + print "CHANGELOG Start found at line $line.\n"; + } + + $scanning ++; + $start_line = $line; + $dat = $'; + } + } + next; + } + + if (/(SIDOC|DANG)_END_CHANGELOG/) { + # Changelog End Found. + if ($changes != 1) { + print "No Changelog Start found (END at line $line). Ignoring.\n"; + } else { + if ($opt_v) { + print "CHANGELOG end found at line $line.\n"; + } # + + $changes --; + $dat .= $`; + push (@changes, &strip_comments ($dat)); + undef $dat; + $scanning --; + } + next; + } + + if (/(SIDOC|DANG)_BEGIN_FUNCTION/) { + # Function Start Found ... + if ($functions != 0) { + print "Already Found a Function start at line $start_line. Ignoring.\n"; + } else { + $functions ++; + if ($scanning != 0) { + &abort_scan ($filename) + } else { + if ($opt_v) { + print "FUNCTION Start found at line $line.\n"; + } + + $scanning ++; + $start_line = $line; + $dat = $'; + } + } + next; + } + + if (/(SIDOC|DANG)_END_FUNCTION/) { + # Function End Found. + if ($functions != 1) { + print "No Function Start found (END at line $line). Ignoring.\n"; + } else { + if ($opt_v) { + print "FUNCTION end found at line $line.\n"; + } + + $functions --; + $dat .= $`; + push (@functions, &strip_comments ($dat)); + undef $dat; + $scanning --; + } + next; + } + + if (/(SIDOC|DANG)_BEGIN_STRUCT/) { + # Structure Start Found ... + if ($structures != 0) { + print "Already Found a Structure start at line $start_line. Ignoring.\n"; + } else { + $structures ++; + if ($scanning != 0) { + &abort_scan ($filename) + } else { + if ($opt_v) { + print "FUNCTION Start found at line $line.\n"; + } + + $scanning ++; + $start_line = $line; + $dat = $'; + } + } + next; + } + + if (/(SIDOC|DANG)_END_STRUCT/) { + # Function End Found. + if ($structures != 1) { + print "No Function Start found (END at line $line). Ignoring.\n"; + } else { + if ($opt_v) { + print "FUNCTION end found at line $line.\n"; + } + + $structures --; +#this also prefixes '/*' if present :-( $dat .= $`; + push (@structures, &strip_comments ($dat)); + undef $dat; + $scanning --; + } + next; + } + + if (/(SIDOC|DANG)_BEGIN_REMARK/) { + # Remark Start Found ... + if ($remarks != 0) { + print "Already Found a Remark start at line $start_line. Ignoring.\n"; + } else { + $remarks ++; + if ($scanning != 0) { + &abort_scan ($filename) + } else { + if ($opt_v) { + print "REMARK Start found at line $line.\n"; + } + + $scanning ++; + $start_line = $line; + $dat = $'; + } + } + next; + } + + if (/(SIDOC|DANG)_END_REMARK/) { + # Remark End Found. + if ($remarks != 1) { + print "No Remark Start found (END at line $line). Ignoring.\n"; + } else { + if ($opt_v) { + print "REMARK end found at line $line.\n"; + } + + $remarks --; + $dat .= $`; + push (@remarks, &strip_comments ($dat)); + undef $dat; + $scanning --; + } + next; + } + + if (/(SIDOC|DANG)_BEGIN_NEWIDEA/) { + # NEWIDEA Start Found ... + if ($newideas != 0) { + print "Already Found a NewIdea start at line $start_line. Ignoring.\n"; + } else { + $newideas ++; + if ($scanning != 0) { + &abort_scan ($filename) + } else { + if ($opt_v) { + print "NEWIDEA Start found at line $line.\n"; + } + + $scanning ++; + $start_line = $line; + $dat = $'; + } + } + next; + } + + if (/(SIDOC|DANG)_END_NEWIDEA/) { + # NEWIDEA End Found. + if ($newideas != 1) { + print "No NewIdeas Start found (END at line $line). Ignoring.\n"; + } else { + if ($opt_v) { + print "NEWIDEA end found at line $line.\n"; + } + + $newideas --; + $dat .= $`; + push (@newideas, &strip_comments ($dat)); + undef $dat; + $scanning --; + } + next; + } + + if (/(SIDOC|DANG)_FIXTHIS/) { + # Fixthis Found ... + if ($scanning != 0) { + die "Alreading Scanning an Item - Missing end (started at line $start_line.) Aborting\n"; + } else { + if ($opt_v) { + print "FIXTHIS found at line $line.\n"; + } + + $start_line = $line; + push (@fixthis, &strip_comments ($')); + undef $dat; + } + next; + } + + $dat .= $_; + } + close INPUT; + + if ($opt_v) { + print "Finished scanning\n"; + print "Interpreting the file\n"; + } + + &handle_modules ($filename); + &handle_changes ($filename); + &handle_functions ($filename); + &handle_structures ($filename); + &handle_remarks ($filename); + &handle_fixthis ($filename); + &handle_newideas ($filename); + + if ($opt_v) { + print "Finished with file $filename\n"; + } + +} + +sub abort_scan { + local ($filename) = @_; + + if ($module == 1) { + die ("At line $line in $filename I found a new marker.\nI was already scanning a MODULE section which started at line $start_line.\n*** ABORTING ***\n"); + } + if ($functions == 1) { + die ("At line $line in $filename I found a new marker.\nI was already scanning a FUNCTION section which started at line $start_line.\n*** ABORTING ***\n"); + } + if ($structures == 1) { + die ("At line $line in $filename I found a new marker.\nI was already scanning a FUNCTION section which started at line $start_line.\n*** ABORTING ***\n"); + } + if ($remarks == 1) { + die ("At line $line in $filename I found a new marker.\nI was already scanning a REMARKS section which started at line $start_line.\n*** ABORTING ***\n"); + } + if ($changes == 1) { + die ("At line $line in $filename I found a new marker.\nI was already scanning a CHANGELOG section which started at line $start_line.\n*** ABORTING ***\n"); + } + if ($newideas == 1) { + die ("At line $line in $filename I found a new marker.\nI was already scanning a NEWIDEAS section which started at line $start_line.\n*** ABORTING ***\n"); + } + + die ("At line $line in $filename I found a new marker.\nI was already scanning something which started at line $start_line.\n(I would tell you what it was, but apparently I've fogotten what it was!)\n*** ABORTING ***\n"); + +} + +sub strip_comments { + local ($text) = @_; + +# / */ \s* \n \s* /* / \n /g + $text =~ s#\*/\s*\n\s*/\*#\n#mg; + $text =~ s#^\s*//##mg; + + $text =~ s#^\s*\*/\s*\n##mg; + $text =~ s#^\s*/\*\s*\n##mg; + $text =~ s#^\s*/\*\s*##mg; +#print "AAAAAAA $text BBBBBBB\n"; + + $text =~ s#^\s*\*##mg; + + $text =~ s#^\s*# #m; + $text =~ s#^\n# \n#m; +# $text =~ s#\s*\*//?$# #m; + + return &protect_specials ($text); +} + +sub handle_subremarks { + local ($dat,$all) = @_; + local (@data); + local ($inverb, $inremark); + + @data = split (/\n/, $dat); + $inverb = 0; + $inremark = 0; + foreach $data (@data) { + if ($data =~ /^\s*VERB\s*/) { + print OUTPUT "

\n"; + $inverb =1; + next; + } + if ($inverb) { + if ($data =~ /^\s*\/VERB\s*/) { + print OUTPUT "

\n

"; + $inverb =0; + } + else { + $data = un_protect_specials($data); + print OUTPUT "$data\n"; + } + next; + } + if ($all) { + print OUTPUT "$data\n"; + next; + } + if ($data =~ /^\s*REMARK\s*/) { + $inremark =1; + next; + } + if ($inremark) { + if ($data =~ /^\s*\/REMARK\s*/) { + $inremark =0; + } + else { + print OUTPUT "$data\n"; + } + next; + } + } +} + +sub handle_modules { + local ($filename) = @_; + local ($mod, $count); + + $count = 0; + + if (defined @modules && $#modules > 0) { + print OUTPUT "$filename Information\n

\n"; + + foreach $mod (@modules) { + &handle_subremarks($mod,0); + if ($mod =~ /maintainer:/io) { + if ($opt_v) { + print "Found maintainer details ...\n"; + } + if ($count > 0) { + print OUTPUT "

-----

\n\n

", $`, "\n"; + } + print OUTPUT "

Maintainers:

\n

\n"; + $maints = $'; + while ($maints =~ /\s*(.*)\s*<(.*)>/g) { + $addr = $2; + $who = $1; + if ($opt_v) { + print "Maintainer: $who <$addr>\n"; + } + print OUTPUT "$who &nl;\n"; + } + print OUTPUT "

\n

"; + } else { + if ($count > 0) { + print OUTPUT "

-----

\n\n

", $mod, "

\n

"; + } + } + $count ++; + } + + undef @modules; + + print OUTPUT "

\n\n"; + + } elsif ($opt_i) { + print OUTPUT "$filename Information\n

\n"; + print OUTPUT "There appears to be no MODULE information for this file.\n\n"; + print OUTPUT "

\n
\n"; + } + +} + + +# This does nothing - We are ignoring the Changes. + +sub handle_changes { + local ($filename) = @_; + + undef @changes; +} + + +sub handle_functions { + local ($filename) = @_; + local (@nodes); + local ($this, $count, @data, $data); + local ($inargslist, @args); + local ($inverb,$inproto,$skip); + + if (defined @functions) { + print OUTPUT "Functions in $filename\n\n"; + + foreach (@functions) { +# /\s*(\w*)/; + /\s*(.*)\n/; + push (nodes, $1); + } + + print OUTPUT "

These are the functions defined in $filename.

\n\n"; + + while ($this = shift @nodes ) { + @data = split (/\n/, shift (@functions)); + shift @data; # Ignore the first line - the function name + + if (join ('', @data) =~ /^\s*$/) { + next; + } + + print OUTPUT "$this\n\n\n

\n"; + $inargslist = 0; + $inretlist = 0; + undef @args; + + $inverb = 0; + $inproto = 0; + $skip = 0; + foreach $data (@data) { + if ($data =~ /^\s*(\/*)SKIP\s*/) { + if ($1 eq "/") {$skip = 0;} + else {$skip = 1;} + next; + } + if ($skip) { + if ($data =~ /^\s*\/*VERB\s*|^\s*\/*PROTO\s*/) { + $skip = 0; + } + else {next;} + } + if ($data =~ /^\s*VERB\s*/) { + print OUTPUT "

\n"; + $inverb =1; + next; + } + if ($inverb) { + if ($data =~ /^\s*\/VERB\s*/) { + print OUTPUT "

\n"; + $inverb =0; + } + else { + $data = un_protect_specials($data); + print OUTPUT "$data\n"; + } + next; + } + if ($data =~ /^\s*PROTO\s*/) { + if ($inproto) {print OUTPUT "\n";} + print OUTPUT "

\n"; + $inproto =1; + next; + } + if ($inproto) { + if ($data =~ /^\s*\{|^\s*\/PROTO\s*/) { + print OUTPUT "

\n"; + $inproto =0; + $skip = 1; + } + else { + $data = un_protect_specials($data); + print OUTPUT "$data\n"; + } + next; + } + if ($data =~ /^\s*\/PROTO\s*/) { + $inproto =0; + next; + } + if ($data =~ /^\s*$/) { # Blank line .... + if ($inargslist || $inretlist) {next;} # .... skip + } + if ($data =~ /return:/io) { # Want to treat data as return + print OUTPUT "

Return values mean:&nl;\n"; + $inretlist = 1; + $inargslist = 0; + next; + } + + if ($data =~ /arguments:/io) { # Want to treat data as args + print OUTPUT "

Arguments are:&nl;\n"; + $inargslist = 1; + $inretlist = 0; + next; + } + + if ($data =~ /description:/io) { # Descriptive (default) + $inargslist = 0; + $inretlist = 0; + next; + } + + if ($inargslist || $inretlist) { # storing list items + push (args, $data); + next; + } + + if (defined @args) { # no longer storing - time to output + print OUTPUT "\n"; + foreach $_ (@args) { + print OUTPUT " ", $_, "\n"; + } + print OUTPUT "\n"; + undef @args; + } + print OUTPUT $data, "\n"; + + } + + if (defined @args) { # no longer storing - time to output + print OUTPUT "\n"; + foreach $_ (@args) { + print OUTPUT " ", $_, "\n"; + } + print OUTPUT "\n"; + } + + print OUTPUT "

\n
\n"; + } + + undef @functions; + + print OUTPUT "
\n\n"; + + } elsif ($opt_i) { + print OUTPUT "

We appear to have no information on the functions in $filename.

\n\n"; + } + +} + +sub handle_structures { + local ($filename) = @_; + local (@nodes); + local ($this, $count, @data, $data); + local ($inargslist, @args, $arg, $insubarg); + local ($inverb) = 0; + + if (defined @structures) { + print OUTPUT "Data Definitions in $filename\n\n

\n"; + + foreach (@structures) { + /\s*(.*)\n/; + push (nodes, $1); + } + + print OUTPUT "These are the structures and/or data defined in $filename.\n\n"; + + + while ($this = shift @nodes ) { + print OUTPUT "$this\n\n\n

\n"; + @data = split (/\n/, shift (@structures)); + shift @data; # Ignore the first line - the header line + $inargslist = 0; + $inretlist = 0; + undef @args; + + $inverb = 0; + foreach $data (@data) { + if ($data =~ /^\s*VERB\s*/) { + print OUTPUT "

\n

\n"; + $inverb =1; + next; + } + if ($inverb) { + if ($data =~ /^\s*\/VERB\s*/) { + print OUTPUT "

\n

"; + $inverb =0; + } + else { + $data = un_protect_specials($data); + print OUTPUT "$data\n"; + } + next; + } + if ($data =~ /^\s*$/) { # Blank line .... + if ($inargslist) {next;} # .... skip + } + + if ($data =~ /elements:/io) { # Want to treat data as args + print OUTPUT "

Elements are:&nl;\n"; + $inargslist = 1; + next; + } + + if ($data =~ /description:/io) { # Descriptive (default) + $inargslist = 0; + next; + } + + if ($inargslist) { # storing list items + push (args, $data); + next; + } + + if (defined @args) { # no longer storing - time to output + print OUTPUT "\n"; + $insubarg =0; + foreach $arg (@args) { + if ($insubarg) { + if ($arg =~ /\*\//) { + print OUTPUT "$`\n"; + $insubarg = 0; + next; + } + print OUTPUT $arg, "\n"; + next; + } + if ($arg =~ /\/\*/) { + print OUTPUT " ", $`, "\n\n"; + $arg = $'; + if ($arg =~ /\*\//) {$arg = $`;} + else {$insubarg = 1;} + print OUTPUT $arg, "\n"; + next; + } + print OUTPUT " ", $arg, "\n"; + } + print OUTPUT "\n"; + undef @args; + } + print OUTPUT $data, "\n"; + + } + + if (defined @args) { # no longer storing - time to output + print OUTPUT "\n"; + $insubarg = 0; + foreach $arg (@args) { + if ($insubarg) { + if ($arg =~ /\*\//) { + print OUTPUT "$`\n"; + $insubarg = 0; + next; + } + print OUTPUT $arg, "\n"; + next; + } + if ($arg =~ /\/\*/) { + print OUTPUT " ", $`, "\n"; + $arg = $'; + if ($arg =~ /\*\//) {$arg = $`;} + else {$insubarg = 1;} + print OUTPUT $arg, "\n"; + next; + } + print OUTPUT " ", $arg, "\n"; + } + print OUTPUT "\n"; + } + + print OUTPUT "\n\n"; + } + + undef @structures; + + print OUTPUT "

\n
\n\n"; + } elsif ($opt_i) { + print OUTPUT "We appear to have no information on the structures in $filename.\n\n"; + } + +} + +sub handle_newideas { + local ($filename) = @_; + + if (defined @newideas) { + print OUTPUT "New Ideas for $filename\n\n

\n"; + + print OUTPUT (shift @newideas), "\n"; + + foreach (@newideas) { + print OUTPUT "

-----

\n\n

", $_, "\n"; + } + + undef @newideas; + + print OUTPUT "

\n\n"; + + } elsif ($opt_i) { + print OUTPUT "Apparently, there are no new ideas for $filename.\n\n"; + } + +} + +sub handle_remarks { + local ($filename) = @_; + + if (defined @remarks) { + print OUTPUT "Remarks in $filename\n\n

\n"; + + &handle_subremarks(shift @remarks,1); +# print OUTPUT (shift @remarks), "\n"; + + foreach (@remarks) { +# print OUTPUT "-----\n\n", $_, "\n"; + print OUTPUT "

-----

\n\n

"; + &handle_subremarks($_,1); + } + + undef @remarks; + + print OUTPUT "

\n
\n\n"; + } elsif ($opt_i) { + print OUTPUT "Apparently, no-one has anything interesting to say about $filename.\n\n"; + } +} + +sub handle_fixthis { + local ($filename) = @_; + + if (defined @fixthis) { + print OUTPUT "Items for Fixing in $filename\n\n

\n"; + + print OUTPUT (shift @fixthis), "\n"; + + foreach (@fixthis) { + print OUTPUT "

-----

\n\n

", $_, "\n"; + } + + undef @fixthis; + + print OUTPUT "

\n
\n\n"; + } elsif ($opt_i) { + print OUTPUT "Apparently, nothing needs fixing in $filename.\n\n"; + } + +} + + + +# Local Variables: +# mode:perl +# End: + + diff --git a/src/doc/DANG/DANG_intro b/src/doc/DANG/DANG_intro new file mode 100644 index 0000000..df87122 --- /dev/null +++ b/src/doc/DANG/DANG_intro @@ -0,0 +1,19 @@ +This document is the preliminary draft of a manual to help +people understand the inner workings of dosemu. It is the goal of +this document to create new dosemu hackers. This concept was inspired +by the linux kernel hackers guide. + +This Guide was concieved and originally written by "Corey Sweeney" +. It has been completely revised. It is now +generated automatically directly from the source code. Special thanks to +"James B. MacLean" for supplying the original +information. (It was mostly ripped out of a mail message.) "Jochen Hein" +has made many useful comments & suggestions. + +At the end if this document is a section detailing how this guide is put +together. This may help you when trying to locate the relevant pieces of +code. If you add new code, it would be useful if the relevant markers +are added where appropriate. + +This file is a collective effort. If you don't like one of the +explanations, or want to add anything, please send me something! diff --git a/src/doc/DANG/DANG_outro b/src/doc/DANG/DANG_outro new file mode 100644 index 0000000..1e62913 --- /dev/null +++ b/src/doc/DANG/DANG_outro @@ -0,0 +1,66 @@ +The Following items are used to delimit the text used to create this file. +Whilst it is not necessary to know this, they are included because they may +be useful for searching, as they are (at least at the moment) reasonably +unique. + +DANG_BEGIN_MODULE / DANG_END_MODULE + This will bracket a description of the file (normally at the + start). Within this you may have the keyword 'Maintainer:' followed + by a list (one line each) of maintainers for this packet. These + will be turned into URLs. + +DANG_BEGIN_FUNCTION / DANG_END_FUNCTION + This brackets a description of functions (good this, isn't it!) + Not every function needs to be described in this way - just the + major ones. Within this you may have the keywords: `arguments:', + `return:' and `description:', which will sort out the information + following it to build proper lists. + +DANG_BEGIN_STRUCT / DANG_END_STRUCT + This brackets a description of structures and data definitions + Not every structure needs to be described in this way - just the + major ones. Within this you may have the keywords: `elements:', + and `description:', which will sort out the information + following it to build proper lists. Also, you may bracket the + structur definition of real C-code, given you have one element + per line. In this case comments (/*...*/) behind the element will + be inserted properly into the formatted list while the C-code + itself is still compilable. + +DANG_BEGIN_REMARK / DANG_END_REMARK + This brackets descriptions of obscure items, like data structures + and architecture. + +DANG_FIXTHIS + This is a one line item, indicating a an area requiring a fix, or + redesign. + +DANG_BEGIN_NEWIDEA / DANG_END_NEWIDEA + New Ideas Start Here! As Ideas are proposed, that get added with + their description, so that future generations can laugh at or + code the ideas ..... These bracket the idea description. + +DANG_BEGIN_CHANGELOG / DANG_END_CHANGELOG + Changelogs - very useful for bug fixing, and avvailable for use + with DPR (or that's the theory) + +In addition there are some keywords, that are recognized within a bracket. + +VERB ... /VERB + This formats the enclosed text verbatim. This is valid within + *_MODULE, *_REMARK, *_STRUCT, *_FUNCTION + +REMARK ... /REMARK + This is only valid within *_MODULE and also can contain VERB + brackets. Its useful to when you want to have a global modul + description + +PROTO ... /PROTO + This is only valid within *_FUNCTION and + takes a C-function prototype as `verbatim' until either + a `{' or a /PROTO is seen. After this all input is `skipped' + until the next PROTO or a /SKIP. + +SKIP ... /SKIP + This is only valid within *_FUNCTION and skips formatting until + either PROTO or /SKIP is seen. diff --git a/src/doc/DANG/Makefile b/src/doc/DANG/Makefile new file mode 100644 index 0000000..ebe2c2e --- /dev/null +++ b/src/doc/DANG/Makefile @@ -0,0 +1,22 @@ +top_builddir=../../.. +SUBDIR = doc/DANG +include $(top_builddir)/Makefile.conf + +DOCSOURCES := $(notdir $(wildcard $(srcdir)/*.sgml)) +TXT = $(DOCSOURCES:.sgml=.txt) +HTML = $(DOCSOURCES:.sgml=.html) + +all: $(HTML) +txt: $(TXT) + +%.txt: %.sgml + $(srcdir)/../tools/doSgmlTools.pl -t -v $^ $@ + +%.html: %.sgml + $(srcdir)/../tools/doSgmlTools.pl -h -v $^ $@ + +clean: + rm -f *.txt DANG*.html *~ *.tex *.log *.aux *.toc *.dvi *.ps + +install: $(HTML) + cp $(HTML) $(top_builddir)/doc diff --git a/src/doc/DANG/make_DANG b/src/doc/DANG/make_DANG new file mode 100755 index 0000000..0b004a6 --- /dev/null +++ b/src/doc/DANG/make_DANG @@ -0,0 +1,10 @@ +#/bin/sh +rm -f DANG.sgml +./DANG_c.pl -v -c DANG_CONFIG +# *** WARNING *** +# This doesn't work with the stock sgmltools ! +# ld2dbx is a custom backend. +sgmltools -b ld2db DANG.sgml +../tools/fixupSGML.pl < DANG.db-sgml > DANG.sgml +rm DANG.db-sgml +perl -pi -e 's###;' DANG.sgml diff --git a/src/doc/DANG/summary_BIOS b/src/doc/DANG/summary_BIOS new file mode 100644 index 0000000..1c2ed4b --- /dev/null +++ b/src/doc/DANG/summary_BIOS @@ -0,0 +1,5 @@ + All of the Bios code is in the "bios" subdirectory. + + DOSEMU requires certain code to be coded in assembler and also code to + be located in the F000 segment. This is where all such code should be + put. diff --git a/src/doc/DANG/summary_CPU_Intel b/src/doc/DANG/summary_CPU_Intel new file mode 100644 index 0000000..f8b50d1 --- /dev/null +++ b/src/doc/DANG/summary_CPU_Intel @@ -0,0 +1 @@ +These files all relate to Intel-x86 specific code. diff --git a/src/doc/DANG/summary_Clients b/src/doc/DANG/summary_Clients new file mode 100644 index 0000000..8cfa62d --- /dev/null +++ b/src/doc/DANG/summary_Clients @@ -0,0 +1,4 @@ +One of the long term aims of the DOSEMU development team is to provide +a client-server based system. This will allow people to add different +display systems in more simply. Those in current development are detailed +in this section. diff --git a/src/doc/DANG/summary_DPMI b/src/doc/DANG/summary_DPMI new file mode 100644 index 0000000..e7efb69 --- /dev/null +++ b/src/doc/DANG/summary_DPMI @@ -0,0 +1,9 @@ +DPMI is Lutz's Baby. It's a really important part of the Emulator as far +as we are concerned, since it will allow us to run so many more programs +and, most importantly, bcc. This is the one thing that the WINE developers +want that we haven't been able to give them. + +If you think you can help .... "Away you Go!" (Sorry to those non-UK folks ... +Thats a reference to a UK kids sports programme from my youth ... anyway ... +enough of this banter. You'll be wanting to know that this is all about +DPMI ...) diff --git a/src/doc/DANG/summary_Debugger b/src/doc/DANG/summary_Debugger new file mode 100644 index 0000000..e69de29 diff --git a/src/doc/DANG/summary_FileAccess b/src/doc/DANG/summary_FileAccess new file mode 100644 index 0000000..6bff9dc --- /dev/null +++ b/src/doc/DANG/summary_FileAccess @@ -0,0 +1 @@ +This hold all kind of accessing files on a Unix filesysten from DOS. diff --git a/src/doc/DANG/summary_Init b/src/doc/DANG/summary_Init new file mode 100644 index 0000000..ec2e913 --- /dev/null +++ b/src/doc/DANG/summary_Init @@ -0,0 +1 @@ +These files are used for initialization and runtime configuration of DOSEMU diff --git a/src/doc/DANG/summary_Main b/src/doc/DANG/summary_Main new file mode 100644 index 0000000..7e1a16b --- /dev/null +++ b/src/doc/DANG/summary_Main @@ -0,0 +1,2 @@ +These files are used to start DOSEMU as well as hold globally called +functions and global vars. diff --git a/src/doc/DANG/summary_Misc b/src/doc/DANG/summary_Misc new file mode 100644 index 0000000..58f9030 --- /dev/null +++ b/src/doc/DANG/summary_Misc @@ -0,0 +1,3 @@ +These are the remaining important files, that do not really fit into another +group. These should not be dismissed as unimportant - rather, they are often +amongst the most important. diff --git a/src/doc/DANG/summary_Mouse b/src/doc/DANG/summary_Mouse new file mode 100644 index 0000000..045ae9f --- /dev/null +++ b/src/doc/DANG/summary_Mouse @@ -0,0 +1,3 @@ + All of the Mouse handling code is in the "mouse" subdirectory. + + There are only 2 main files, mouse.c and mouseint.c. diff --git a/src/doc/DANG/summary_Net b/src/doc/DANG/summary_Net new file mode 100644 index 0000000..bcbd6a3 --- /dev/null +++ b/src/doc/DANG/summary_Net @@ -0,0 +1 @@ +This hold files, that relate to networking over DOS. diff --git a/src/doc/DANG/summary_New_Keyboard b/src/doc/DANG/summary_New_Keyboard new file mode 100644 index 0000000..9c71654 --- /dev/null +++ b/src/doc/DANG/summary_New_Keyboard @@ -0,0 +1,2 @@ +Most of the New Keyboard handling code is in the "plugin/kbd_unicode" subdirectory. + diff --git a/src/doc/DANG/summary_Old_Keyboard b/src/doc/DANG/summary_Old_Keyboard new file mode 100644 index 0000000..46cacdb --- /dev/null +++ b/src/doc/DANG/summary_Old_Keyboard @@ -0,0 +1,3 @@ +All of the Old Keyboard handling code is in the "plugin/keyboard" subdirectory. + +Latest addition is SLANG. diff --git a/src/doc/DANG/summary_PIC b/src/doc/DANG/summary_PIC new file mode 100644 index 0000000..7fdeede --- /dev/null +++ b/src/doc/DANG/summary_PIC @@ -0,0 +1 @@ + All of the PIC handling code is in the "PIC" subdirectory. diff --git a/src/doc/DANG/summary_SHARED b/src/doc/DANG/summary_SHARED new file mode 100644 index 0000000..7dfdcd5 --- /dev/null +++ b/src/doc/DANG/summary_SHARED @@ -0,0 +1,886 @@ + +
+ +DANG +The DOSEMU Alterer Novices Guide +<author>Alistair MacDonald, <tt/alistair@slitesys.demon.co.uk/ +<date>For DOSEMU v0.63.1 +<abstract> +This Document is the DOSEMU Alterer Novices Guide. It is known as the DANG. +</abstract> + +<toc> + +<sect>Introduction + +<p> +This document is the preliminary draft of a manual to help +people understand the inner workings of dosemu. It is the goal of +this document to create new dosemu hackers. This concept was inspired +by the linux kernel hackers guide. + +This Guide was concieved and originally written by &dquot;Corey Sweeney&dquot; +<corey@>. It has been completely revised. It is now +generated automatically directly from the source code. Special thanks to +&dquot;James B. MacLean&dquot; <macleajb@ednet.ns.ca> for supplying the original +information. (It was mostly ripped out of a mail message.) &dquot;Jochen Hein&dquot; +has made many useful comments & suggestions. + +At the end if this document is a section detailing how this guide is put +together. This may help you when trying to locate the relevant pieces of +code. If you add new code, it would be useful if the relevant markers +are added where appropriate. + +This file is a collective effort. If you don't like one of the +explanations, or want to add anything, please send me something! + + + + +<sect>The Main group of Modules + +<p> +These files are used to start DOSEMU as well as hold globally called +functions and global vars. + + +<sect1>dos.c Information + +<p> +<sect2>Functions in dos.c + +<p> +These are the functions defined in dos.c. + +<sect3>dosemu + + +<p> +<p>Arguments are:&nl; +<itemize> +<ITEM> argc - Count of argumnents. +<ITEM> argc - Actual arguments. +</itemize> + Function created by entry point into libdosemu. Called to + jump into the emulate function of DOSEMU. + + +<sect1>emu.c Information + +<p> +<sect2>Functions in emu.c + +<p> +These are the functions defined in emu.c. + +<sect3>jmp_emulate + + +<p> + call the emulate function by way of the dll headers. Always make sure + that this line is the first of emu.c and link emu.o as the first object + file to the lib + + +<sect3>SIG_int + + +<p> + Requires the sig/sillyint.o driver loaded (using NEW modules package), + or a kernel patch (implementing sig/int.c driver). + The IRQ numbers to monitor are taken from config.sillyint, each bit + corresponding to one IRQ. The higher 16 bit are defining the use of + SIGIO + + +<sect3>emulate + + +<p> +<p>Arguments are:&nl; +<itemize> +<ITEM> argc - Argument count. +<ITEM> argv - Arguments. +</itemize> + Emulate gets called from dos.c. It initializes DOSEMU to + prepare it for running in vm86 mode. This involves catching signals, + preparing memory, calling all the initialization functions for the I/O + subsystems (video/serial/etc...), getting the boot sector instructions + and calling vm86(). + + +<sect2>Remarks in emu.c + +<p> + DOSEMU must not work within the 1 meg DOS limit, so + start of code is loaded at a higher address, at some time this could + conflict with other shared libs. If DOSEMU is compiled statically + (without shared libs), and org instruction is used to provide the jump + above 1 meg. + +----- + + At this time we have to use + SIGALRM in addition to SIGIO I don't (yet) know why + the SIGIO signal gets lost sometimes (once per + minute or longer). But if it happens, we can + retrigger this way over SIGALRM. Normally SIGIO + happens before SIGALARM, so nothing hurts. (Hans) + +<sect1>include/emu.h Information + +<p> +<sect2>Functions in include/emu.h + +<p> +These are the functions defined in include/emu.h. + +<sect3>NEWSETQSIG + + +<p> +<p>Arguments are:&nl; +<itemize> +<ITEM> sig - the signal to have a handler installed to. +<ITEM> fun - the signal handler function to install +</itemize> + All signals that wish to be handled properly in context with the + execution of vm86() mode, and signals that wish to use non-reentrant + functions should add themselves to the SIGNALS_THAT_QUEUE define and + use SETQSIG(). To that end they will also need to be set up in an + order such as SIGIO. + + +<sect2>Remarks in include/emu.h + +<p> + The `vm86_struct` is used to pass all the necessary status/registers to + DOSEMU when running in vm86 mode. + +----- + + We assume system call restarting... under linux 0.99pl8 and earlier, + this was the default. SA_RESTART was defined in 0.99pl8 to explicitly + request restarting (and thus does nothing). However, if this ever + changes, I want to be safe + +----- + + DOSEMU keeps system wide configuration status in a structure + called config. + +----- + + The var `fatalerr` can be given a true value at any time to have DOSEMU + exit on the next return from vm86 mode. + +<sect>The DPMI group of Modules + +<p> +DPMI is Lutz's Baby. It's a really important part of the Emulator as far +as we are concerned, since it will allow us to run so many more programs +and, most importantly, bcc. This is the one thing that the WINE developers +want that we haven't been able to give them. + +If you think you can help .... &dquot;Away you Go!&dquot; (Sorry to those non-UK folks ... +Thats a reference to a UK kids sports programme from my youth ... anyway ... +enough of this banter. You'll be wanting to know that this is all about +DPMI ...) + + +<sect1>dosext/dpmi/dpmi.c Information + +<p> +<sect2>Functions in dosext/dpmi/dpmi.c + +<p> +These are the functions defined in dosext/dpmi/dpmi.c. + +<sect3>dpmi_control + + +<p> + This function is similar to the vm86() syscall in the kernel and + switches to dpmi code. + + +<sect3>run_pm_int + + +<p> + This routine is used for running protected mode hardware + interrupts and software interrupts 0x1c, 0x23 and 0x24. + run_pm_int() switches to the locked protected mode stack + and calls the handler. If no handler is installed the + real mode interrupt routine is called. + + +<sect3>do_default_cpu_exception + + +<p> + This is the default CPU exception handler. + Exceptions 0, 1, 2, 3, 4, 5 and 7 are reflected + to real mode. All other exceptions are terminating the client + (and may be dosemu too :-)). + + +<sect3>do_cpu_exception + + +<p> + This routine switches to the locked protected mode stack, + disables interrupts and calls the DPMI client exception handler. + If no handler is installed the default handler is called. + + +<sect3>dpmi_fault + + +<p> + This is the brain of DPMI. All CPU exceptions are first + reflected (from the signal handlers) to this code. + Exception from nonpriveleged instructions INT XX, STI, CLI, HLT + and from WINDOWS 3.1 are handled here. + All here unhandled exceptions are reflected to do_cpu_exception() + + +<sect2>Remarks in dosext/dpmi/dpmi.c + +<p> + We are caching ldt here for speed reasons and for Windows 3.1. + I would love to have an readonly ldt-alias (located in the first + 16MByte for use with 16-Bit descriptors (WIN-LDT)). This is on my + wish list for the kernel hackers (Linus mainly) :-))))))). + + +----- + + DPMI is designed such that the stack change needs a task switch. + We are doing it via an SIGSEGV - instead of one task switch we have + now four :-(. + Arrgh this is the point where I should start to include DPMI stuff + in the kernel, but then we could include the rest of dosemu too. + Would Linus love this? I don't :-((((. + Anyway I would love to see first a working DPMI port, maybe we + will later (with version 0.9 or similar :-)) start with it to + get a really fast dos emulator............... + + +----- + + Handling of the virtuell interruptflag is still not correct and there + are many open questions since DPMI specifications are unclear in this + point. + An example: If IF=1 in protected mode and real mode code is called + which is disabling interrupts via cli and returning to protected + mode, is IF then still one or zero? + I guess I have to think a lot about this and to write a small dpmi + client running under a commercial dpmi server :-). + +<sect2>Items for Fixing in dosext/dpmi/dpmi.c + +<p> + We shouldn't return to dosemu code if IF=0, but it helps - WHY? + +----- + + we should not change registers for hardware interrupts + +<sect2>New Ideas for dosext/dpmi/dpmi.c + +<p> + Simulate Local Descriptor Table for MS-Windows 3.1 + must be read only, so if krnl386.exe/krnl286.exe + try to write to this table, we will bomb into sigsegv() + and and emulate direct ldt access + +<sect>The Video group of Modules + +<p> +All of the Video handling code is in the &dquot;video&dquot; subdirectory. + +There is one file for each video card or chipset and the master file. To +Add a new card, it needs a set of save & restore routines putting in a file +here. + + +<sect1>env/video/vc.c Information + +<p> +<sect1>env/video/video.c Information + +<p> +<sect2>Functions in env/video/video.c + +<p> +These are the functions defined in env/video/video.c. + +<sect3>video_init + + +<p> + Set pointer to correct structure of functions to initialize, close, + etc... video routines. + + +<sect2>Remarks in env/video/video.c + +<p> + Here the sleeping lion will be awoken and eat much of CPU time !!! + + The result of setting VM86_SCREEN_BITMAP (at state of Linux 1.1.56): + Each vm86 call will set 32 pages of video mem RD-only + (there may be 1000000 per second) + Write access to RD-only page results in page-fault (mm/memory.c), + which will set a bit in current->screen_bitmap and calls do_wp_page() + which does __get_free_page(GFP_KERNEL) but frees it immediatly, + because copy-on-write is not neccessary and sets RD/WR for the page. + (this could happen 32000000 per second, if the CPU were fast enough) + It would be better to get the DIRTY-bit directly from the page table, + isn't it? A special syscall in emumodule could do this. + +----- + + reserve_video_memory() + + This procedure is trying to eke out all the UMB blocks possible to + maximize your memory under DOSEMU. If you know about dual monitor + setups, you can contribute by putting in the correct graphics page + address values. + +<sect1>env/video/X.c Information + +<p> +<sect2>Functions in env/video/X.c + +<p> +These are the functions defined in env/video/X.c. + +<sect3>get_vga256_colors + + +<p> + Allocates a colormap for 256 color modes and initializes it. + + +<sect3>X_close + + +<p> + Destroys the window, unloads font, pixmap and colormap. + + +<sect3>X_setmode + + +<p> + Resizes the window, also the graphical sizes/video modes. + remember the dos videomodi + + +<sect3>X_change_mouse_cursor + + +<p> + This function seems to be called each screen_update :( + It is called in base/mouse/mouse.c:mouse_cursor(int) a lot for show and + hide. + + +<sect3>X_redraw_screen + + +<p> + Redraws the entire screen, also in graphics mode + Used for expose events etc. + returns: + nothing +<p>Arguments are:&nl; +<itemize> +<ITEM> none +</itemize> + + +<sect3>X_update_screen + + +<p> + Updates, also in graphics mode + Graphics in X has to be smarter and improved + returns: + 0 - nothing updated + 2 - partly updated + 1 - whole update +<p>Arguments are:&nl; +<itemize> +<ITEM> none +</itemize> + + +<sect3>set_mouse_position + + +<p> + places the mouse on the right position + Not tested in X with graphics + returns: + nothing +<p>Arguments are:&nl; +<itemize> +<ITEM> x,y - coordinates +</itemize> + + +<sect1>env/video/console.c Information + +<p> +<sect1>env/video/dualmon.c Information + +<p> +<sect2>Functions in env/video/dualmon.c + +<p> +These are the functions defined in env/video/dualmon.c. + +<sect3>MDA_init + + +<p> + Initializes the monochrome card. First detects which monochrome + card is used, because the Hercules RamFont and the Hercules InColor + need one more register to be initialized. If there is no monochrome + card at all, we just think there is one and poke an peek in the void. + After the detection the card is initialized. + returns: + nothing +<p>Arguments are:&nl; +<itemize> +<ITEM> none +</itemize> + + +<sect2>Remarks in env/video/dualmon.c + +<p> + After MDA_init() the VGA is configured, something in video.c + or console.c &dquot;reprograms&dquot; the monochrome card again in such a way + that I always have to run hgc.com before I can use any program that + uses the monochrome card. I've spent a day trying to find it, but I + can't figure out. Something is writing to one of the following ports: + 0x3b4, 0x3b5, 0x3b8, 0x3b9, 0x3ba, 0x3bb, 0x3bf. + The problem occurs at (at least) the following 2 systems: + + - AMD 386DX40, Trident 9000/512Kb ISA, Hercules Graphics Card Plus + - Intel 486DX2/66, Cirrus Logic 5426/1Mb VLB, Hercules clone + + The problem doesn't occur when I start dosemu from a telnet connection + or from a VT100 terminal. (Erik Mouw, jakmouw@et.tudelft.nl) + + +<sect1>env/video/et4000.c Information + +<p> +<sect1>env/video/hgc.c Information + +<p> +<sect1>base/bios/int10.c Information + +<p> +<sect1>env/video/s3.c Information + +<p> +<sect1>env/video/terminal.c Information + +<p> +<sect1>env/video/trident.c Information + +<p> +<sect1>env/video/vga.c Information + +<p> +<sect>The Keyboard group of Modules + +<p> +All of the Keyboard handling code is in the &dquot;keyboard&dquot; subdirectory. + +Latest addition is SLANG. + + +<sect1>base/keyboard/Xkeyb.c Information + +<p> +<sect1>base/keyboard/keymaps.c Information + +<p> +<sect2>Remarks in base/keyboard/keymaps.c + +<p> + The DEAD codes must refer to keys that don't exist on any language + keyboard. I hope nobody has a smily face key :-) + dead_key_table is a list of the dead keys supported. They must + be placed on the correct key in the keymaps above. See key_map_es_latin1. + + +----- + + dos850_dead_map consists of the triple, {deadkey, letter, result}. + It should be correct for all the code page 850 users (Western Europe). + If you uses a different code page, please create a map! + Jon Tombs jon@gtex02.us.es + + +<sect1>base/keyboard/slang-termio.c Information + +<p> +<sect>The Misc group of Modules + +<p> +These are the remaining important files, that do not really fit into another +group. These should not be dismissed as unimportant - rather, they are often +amongst the most important. + + +<sect1>dosext/misc/emm.c Information + +<p> +<sect1>dosext/misc/xms.c Information + +<p> +<sect1>arch/linux/async/sigsegv.c Information + +<p> +<sect2>Functions in arch/linux/async/sigsegv.c + +<p> +These are the functions defined in arch/linux/async/sigsegv.c. + +<sect3>dosemu_fault + + +<p> + All CPU exceptions (except 13=general_protection from V86 mode, + which is directly scaned by the kernel) are handled here. + + +<sect1>include/int.h Information + +<p> +<sect1>include/ports.h Information + +<p> +<sect1>base/misc/dosio.c Information + +<p> +<sect1>base/misc/disks.c Information + +<p> +<sect2>Functions in base/misc/disks.c + +<p> +These are the functions defined in base/misc/disks.c. + +<sect3>disk_init + + +<p> + Test by opening all floppies/hardrives configured. + + +<sect1>emi-i386/cpu.c Information + +<p> +<sect1>dev/misc/lpt.c Information + +<p> +<sect>The Serial group of Modules + +<p> +This is the code that works our serial emulation. This needs to be very fast +if we are to convince DOS that we have a very fast serial port. + +<sect1>base/serial/ser_defs.h Information + +<p> +<sect2>Remarks in base/serial/ser_defs.h + +<p> + Extensions to serial debugging. + + SER_DEBUG_MAIN (0 or 1) + - extra debug output on the most critical information. + + SER_DEBUG_HEAVY (0 or 1) + - super-heavy extra debug output, including all ports reads and writes, + and every character received and transmitted! + + SER_DEBUG_INTERRUPT (0 or 1) + - additional debug output related to serial interrupt code, + including flagging serial interrupts, or PIC-driven code. + + SER_DEBUG_FOSSIL_RW (0 or 1) + - heavy FOSSIL debug output, including all reads and writes. + + SER_DEBUG_FOSSIL_STATUS (0 or 1) + - super-heavy FOSSIL debug output, including all status checks. + + You must recompile dosemu everytime one of these constants are modified. + Just type 'make' in the dosemu dir and it will recompile the changes only. + +----- + + IMPORTANT INFO about com[] variable array structure used in serial.c + + Most of the serial variables are stored in the com[] array. + The com[] array is a structure in itself. Take a look at the + 'serial_t' struct declaration in the serial.h module for more info + about this. Only the most commonly referenced global variables + are listed here: + + config.num_ser Number of serial ports active. + com[x].base_port The base port address of emulated serial port. + com[x].real_comport The COM port number. + com[x].interrupt The PIC interrupt level (based on IRQ number) + com[x].mouse Flag mouse (to enable extended features) + com[x].fd File descriptor for port device + com[x].dev[] Filename of port port device + com[x].dev_locked Flag whether device has been locked + + The arbritary example variable 'x' in com[x] can have a minimum value + of 0 and a maximum value of (config.numser - 1). There can be no gaps + for the value 'x', even though gaps between actual COM ports are permitted. + It is strongly noted that the 'x' does not equal the COM port number. + This example code illustrates the fact, and how the com[] array works: + + for (i = 0; i < config.numser; i++) + s_printf(&dquot;COM port number %d has a base address of %x&dquot;, + com[i].real_comport, com[i].base_port); + + +<sect1>base/serial/ser_init.c Information + +<p> +<sect2>Maintainers +<p> +Mark Rejhon <htmlurl url="mailto:marky@ottawa.com" name="<marky@ottawa.com>">&nl; +<sect2>Functions in base/serial/ser_init.c + +<p> +These are the functions defined in base/serial/ser_init.c. + +<sect3>serial_init + + +<p> + This is the master serial initialization function that is called + upon startup of DOSEMU to initialize ALL the emulated UARTs for + all configured serial ports. The UART is initialized via the + initialize_uart function, which opens the serial ports and defines + variables for the specific UART. + If the port is a mouse, the port is only initialized when i + + +<sect2>Items for Fixing in base/serial/ser_init.c + +<p> + This needs more work before it is implemented into /etc/dosemu.conf as an 'rtscts' option. + +<sect1>base/serial/ser_ports.c Information + +<p> +<sect2>Functions in base/serial/ser_ports.c + +<p> +These are the functions defined in base/serial/ser_ports.c. + +<sect3>do_serial_in + + +<p> + The following function returns a value from an I/O port. The port + is an I/O address such as 0x3F8 (the base port address of COM1). + There are 8 I/O addresses for each serial port which ranges from + the base port (ie 0x3F8) to the base port plus seven (ie 0x3FF). + [num = abritary port number for serial line, address = I/O port address] + + +<sect3>do_serial_out + + +<p> + The following function writes a value to an I/O port. The port + is an I/O address such as 0x3F8 (the base port address of COM1). + [num = abritary port number for serial line, address = I/O port address, + val = value to write to I/O port address] + + +<sect2>Items for Fixing in base/serial/ser_ports.c + +<p> + Should clearing UART cause THRE int if it's enabled? + +----- + + Fix the calculation assumption + +----- + + Is this safe to put this here? + +----- + + Is this safe to put this here? + +<sect1>base/serial/ser_irq.c Information + +<p> +<sect2>Functions in base/serial/ser_irq.c + +<p> +These are the functions defined in base/serial/ser_irq.c. + +<sect3>serial_int_engine + + +<p> + This function is the serial interrupts scheduler. Its purpose is to + update interrupt status and/or invoke a requested serial interrupt. + If interrupts are not enabled, the Interrupt Identification Register + is still updated and the function returns. See pic_serial_run() below + it is executed right at the instant the interrupt is actually invoked. + Since it is not possible to run the interrupt on the spot, it triggers + the interrupt via the pic_request() function (which is in pic.c) + and sets a flag that an interrupt is going to be occur soon. + Please read pic_serial_run() for more information about interrupts. + [num = port, int_requested = the requested serial interrupt] + + +<sect3>pic_serial_run + + +<p> + This function is called by the priority iunterrupt controller when a + serial interrupt occurs. It executes the highest priority serial + interrupt for that port. (Priority order is: RLSI, RDI, THRI, MSI) + Because it is theoretically possible for things to change between the + interrupt trigger and the actual interrupt, some checks must be + repeated. + + +<sect3>serial_run + + +<p> + This is the main housekeeping function, which should be called about + 20 to 100 times per second. The more frequent, the better, up to + a certain point. However, it should be self-compensating if it + executes 10 times or even 1000 times per second. Serial performance + increases with frequency of execution of serial_run. + Serial mouse performance becomes more smooth if the time between + calls to serial_run are smaller. + + +<sect2>Remarks in base/serial/ser_irq.c + +<p> + Linux code hackers: How do I detect a break signal without having + to rely on Linux signals? Can I peek a 'break state bit'? + Also, how do I 'turn on' and 'turn off' the break state, via + an ioctl() or tcsetattr(), rather than using POSIX tcsendbrk()? + +<sect2>Items for Fixing in base/serial/ser_irq.c + +<p> + how do we cancel a PIC interrupt, when we have come this far? + +----- + + Perhaps this can be modified to limit max chain length? + +<sect>The Mouse group of Modules + +<p> + All of the Mouse handling code is in the &dquot;mouse&dquot; subdirectory. + + There are only 2 main files, mouse.c and mouseint.c. + + + +<sect1>base/mouse/mouse.c Information + +<p> +<sect2>Functions in base/mouse/mouse.c + +<p> +These are the functions defined in base/mouse/mouse.c. + +<sect3>mouse_init + + +<p> + Initialize internal mouse. + + +<sect>The Bios group of Modules + +<p> + All of the Bios code is in the &dquot;bios&dquot; subdirectory. + + DOSEMU requires certain code to be coded in assembler and also code to + be located in the F000 segment. This is where all such code should be + put. + + +<sect1>base/bios/bios.S Information + +<p> +<sect>The PIC group of Modules + +<p> + All of the PIC handling code is in the &dquot;PIC&dquot; subdirectory. + + +<sect1>dev/pic/pic.c Information + +<p> +<sect1>devpic/pic.h Information + +<p> +<sect>And Finally ... + +<p> +The Following items are used to delimit the text used to create this file. +Whilst it is not necessary to know this, they are included because they may +be useful for searching, as they are (at least at the moment) reasonably +unique. + +DANG_BEGIN_MODULE / DANG_END_MODULE + This will bracket a description of the file (normally at the + start). + +DANG_BEGIN_FUNCTION / DANG_END_FUNCTION + This brackets a description of functions (good this, isn't it!) + Not every function needs to be described in this way - just the + major ones. + +DANG_BEGIN_REMARK / DANG_END_REMARK + This brackets descriptions of obscure items, like data structures + and architecture. + +DANG_FIXTHIS + This is a one line item, indicating a an area requiring a fix, or + redesign. + +DANG_BEGIN_NEWIDEA / DANG_END_NEWIDEA + New Ideas Start Here! As Ideas are proposed, that get added with + their description, so that future generations can laugh at or + code the ideas ..... These bracket the idea description. + +DANG_BEGIN_CHANGELOG / DANG_END_CHANGELOG + Changelogs - very useful for bug fixing, and avvailable for use + with DPR (or that's the theory) + + + + +</article> diff --git a/src/doc/DANG/summary_Serial b/src/doc/DANG/summary_Serial new file mode 100644 index 0000000..4cf91ee --- /dev/null +++ b/src/doc/DANG/summary_Serial @@ -0,0 +1,2 @@ +This is the code that works our serial emulation. This needs to be very fast +if we are to convince DOS that we have a very fast serial port. diff --git a/src/doc/DANG/summary_Sound b/src/doc/DANG/summary_Sound new file mode 100644 index 0000000..131b112 --- /dev/null +++ b/src/doc/DANG/summary_Sound @@ -0,0 +1,6 @@ +The sound code provides emulation of the SB. The actual emulation provided +depends upon the support available from the kernel sound driver. Because +this is very OS dependant the driver code itself is kept in architecture +specifc files under src/arch/osname/dosext/sound/. Communication is via +a set of interface functions and the device independant structures. + diff --git a/src/doc/DANG/summary_Video b/src/doc/DANG/summary_Video new file mode 100644 index 0000000..db495d1 --- /dev/null +++ b/src/doc/DANG/summary_Video @@ -0,0 +1,5 @@ +All of the Video handling code is in the "video" subdirectory. + +There is one file for each video card or chipset and the master file. To +Add a new card, it needs a set of save & restore routines putting in a file +here. diff --git a/src/doc/HOWTO/EMUfailure.sgml b/src/doc/HOWTO/EMUfailure.sgml new file mode 100644 index 0000000..f16b1b6 --- /dev/null +++ b/src/doc/HOWTO/EMUfailure.sgml @@ -0,0 +1,408 @@ +<!DOCTYPE article PUBLIC "-//Davenport//DTD DocBook V3.0//EN"> + +<!-- This is the dosemu EMUproblem SGML source --> + +<article> + +<artheader> + +<title>Known dosemu problems + + + + +This file lists programs and groups of programs not running or running +only partially under dosemu. The most up-to-date version of this file +may be found on: +http://www.dosemu.org/. +Please report about possible additions to +linux-msdos@vger.kernel.org +or the SourceForge BTS at +http://www.dosemu.org/. +Perhaps your program can be made going +with the help of others. Have a look at the dosemu-howto how to do so. + + + + + + + +Fundamental problems + + +Programs that don't work under the MSDOS Emulator and probably won't +ever work, because of fundamental problems. Some of these fundamental +problems result in these programs not being runnable on +Win3.x/Win95/WinNT and under OS/2 DOS box either. These programs +are characterized by using any of these features: + + + +Virtual Control Program Interface (VCPI) + + +VCPI allows programs to run in ring 0. This is kernel mode in Linux +and not sensible. + + + +Example: sim2181.exe from Analog Devices DSP Kit + + + + + +Programs using older versions of the Pharlap Extender + + +Older versions of the Pharlap Extender (run286) need ring-0 access +under DOSEMU to install their own DPMI server. The use of proprietary +undocumented extensions to the DPMI protocol makes DOSEMU's DPMI server +unsuitable for this extender. + + + +Example: Autocad Version 12c1 For DOS + + + +Example: the game BioForge by Origin Systems. + + + + + +Programs using the JEMM memory manager + + +The JEMM memory manager provides proprietary extensions to the EMS +protocol. These are not supported by DOSEMU. + + + +Example: Wing Commander Privateer by Origin Systems + + + + + +Does my failing program belong to these groups? + + +Check with "strings <program.exe> | less" if the program +contains some of these keywords: vcpi, RUN286. + + + + + + +Fundamental problem with the Linux kernel + + +The Programmable Interval Timer (PIT) can be programmed to produce +interrupts with frequencies up to almost 2MHz. Linux sets this to +only 100Hz (2.6 kernels can set it to 1KHz) and doesn't allow the +software to change that. This limits the minimal interval between subsequent +SIGALRM notifications for software that uses the setitimer(2) syscall. +To emulate the PIT frequencies that are higher than the frequency Linux +sets the PIT to, dosemu uses "interrupt bursts": on every SIGALRM +reception dosemu triggers the timer interrupt as many times as necessary +to compensate the gap since the previous SIGALRM reception. This allows +to keep a precise timing but causes problems for some programs. When +the timer interrupt handler is invoked more than once without letting +the main thread to execute, some programs can lock up. The game "Cosmo" is +one of those. + + +Another problem is that due to the aforementioned low timer frequency +dosemu is not able to properly emulate the timings for different +emulated hardware. That also causes problems for some programs. +Scream Tracker 3, for example, can lock up at startup because the +interrupt from an emulated SB card can be triggered earlier than it +should be in a real system. + + +Possibly a workaround may be found in future DOSEMU versions. + + +Linux kernels prior to 3.16 may have various problems on x86-64. +Make sure to not use 3.14 and 3.15 as they + +lack 16bit segments support + + + +3.16 adds support for espfix64 feature. + + + + + +Fundamental problems with the CPU + + +There are several defects in Intel's x86 CPUs that are causing +problems for some software. Below is a description of the defects +that are known to cause problems for software running under dosemu. + + + + +Problem with the virtualization of the IF flag + + + + +Intel's manual + +says: + + +" A procedure may use the POPF instruction to change the setting of the IF + flag only if the CPL is less than or equal to the current IOPL. An attempt + by a less privileged procedure to change the IF flag does not result in + an exception; the IF flag simply remains unchanged. " + + +The fact that the exception is not being generated, prevents dosemu from +catching and properly simulating the POPF instruction executed in protected +mode. That, in particular, means that the following code, executed in +protected mode (not in v86 mode) under dosemu, will end up with interrupts +disabled (IF cleared): + + + + +sti +pushf +cli +popf + +[ the interrupts are still disabled here ] + + +This bug can only affect DPMI programs, as using DPMI is the only way +to execute protected mode code under dosemu. +Known programs that are affected are the games from ID software, namely +Doom2 and Duke Nukem 3D, but only when configured with sound. +An optional work-around was added to dosemu, which just re-enables the +interrupts if they were disabled for too long in protected mode. +Additionally the address of the instruction that disabled the interrupts, +is added to a black-list and this instruction is ignored for subsequent +passes so that it can't disable the interrupts any more. +This is potentially unsafe, but if the timeout is long enough, no harm +was observed so far. +The timeout is configured via the $_cli_timeout option, which is measured +in a 10ms timer ticks. Setting that option to 0 disables the workaround +completely, making Doom2 unplayable with sound enabled. + + + + +ESP register corruption + + +Intel's x86 CPUs have a defect + +described here +, +chapter "Specification Clarifications" +section 4: "Use Of ESP In 16-Bit Code With 32-Bit Interrupt Handlers", +which reads as follows: + + +"ISSUE: When a 32-bit IRET is used to return to another privilege level, +and the old level uses a 4G stack (D/B bit in the segment register = 1), +while the new level uses a 64k stack (D/B bit = 0), then only the +lower word of ESP is updated. The upper word remains unchanged. This is +fine for pure 16-bit code, as well as pure 32-bit code. However, when +32-bit interrupt handlers are present, 16-bit code should avoid any +dependence on the upper word of ESP. No changes are necessary in existing +16-bit code, since the only way to access ESP in USE16 segments is +through the 32-bit address size prefix." + + +Unfortunately, the above quote from Intel is silent about the 32-bit +programs that use 16-bit stack segment - this is where the problem pops us. +The corruption happens when the Linux kernel returns control to the dosemu +process, while a 32-bit DPMI client that uses a 16-bit data segment for +the stack is active. This is not the usual case, but unfortunately some +32-bit DPMI clients are actually using a 16-bit segment for the stack, +and even the dos4gw extender behaves that way sometimes. + + +Programs that are known to be affected by this issue are: + + + +Need For Speed 1 (demo version at least, when configured with sound) + + + + +Syndicate Wars (when used with dos4gw 0.8) + + + + +Open Cubic Player + + + + + + +These programs are crashing shortly after startup, but this problem +is difficult to detect reliably, so there may be many more programs +that experience a random crashes due to this CPU bug. + + + +The reliable work-around was developed and added into linux-2.6.12 +for 32-bit systems, and into linux-3.16 for 64-bit systems. + + +Note: linux kernels prior to 3.16 may have various problems on x86-64. + + + + + + + + + +Known bugs + + +Things YOU may help changing + + + +List of currently known bugs in dosemu2 + + + + + +Some documentation is known to be well out of date. + + + + + +Some database programs (Clipper, FoxPro) have problems with +locking in certain configurations. smbfs doesn't support +locking. $_full_file_locks=(on) may or may not help. + + + + + +Mortal Kombat 1 and 2 are not producing any sound for unknown reasons. + + + + + +X-COM Apocalypse (DEMO version) locks up at startup if configured with sound. + + + + + + + + + + +Programs exhibiting graphical problems in xdosemu + +The following programs work perfectly on the Linux console +(suid/sudo/root) with graphics enabled but exhibit minor or +major glitches in xdosemu. + + + + +Games with graphical problems + + +The following games exhibit glitches or don't work at all in +xdosemu. Please let us know when any problems are solved or +even better, help us solving! + + + + +Commander Keen 1 wobbles like jelly and the window shakes +every time it scrolls. + + + + +Pinball Dreams 2 takes a long time to start. Once it's past +the startup screen it runs fine though. + + + + + + + +Differences in behaviour between Dosemu and Dosemu2 + +The following differences may be apparent if you have previously been +using Dosemu. + + + +Filesystems + + + + +MFS + + +Network device DOS filesystem that provides read write access to the host filesystem. + + + + +Dosemu2 does not redirect a drive to arbitrary host paths on the command +line of the emufs.sys driver when it is loaded in config.sys. Use the lredir2 +command to accomplish this in autoexec.bat instead. + + + + +Dosemu2 does not support LFN on the SUBST / JOIN drives. + + + + +Dosemu2 now provides enhanced FAT32 disk functions only as a fallback +to the native DOS in the event that the DOS does not implement them itself. +A consequence of being a fallback is that if the DOS has a bug in its +implementation it is not overriden by Dosemu2. Examples of this are +MS-DOS 7.0 and PC-DOS 7.10. + + + + + + + +
diff --git a/src/doc/HOWTO/Makefile b/src/doc/HOWTO/Makefile new file mode 100644 index 0000000..9682156 --- /dev/null +++ b/src/doc/HOWTO/Makefile @@ -0,0 +1,23 @@ +top_builddir=../../.. +SUBDIR = doc/HOWTO +include $(top_builddir)/Makefile.conf + +DOCSOURCES := EMUfailure.sgml tweaks.sgml +TXT = $(DOCSOURCES:.sgml=.txt) +HTML = $(DOCSOURCES:.sgml=.html) + +all: $(HTML) +txt: $(TXT) + +%.txt: %.sgml + $(srcdir)/../tools/doSgmlTools.pl -t $^ $@ + +%.html: %.sgml + $(srcdir)/../tools/doSgmlTools.pl -h $^ $@ + + +clean: + rm -f *.txt *.html *~ *.tex *.log *.aux *.toc *.dvi *.ps + +install: $(HTML) + cp $(HTML) $(top_builddir)/doc diff --git a/src/doc/HOWTO/dosemu-HOWTO.sgml b/src/doc/HOWTO/dosemu-HOWTO.sgml new file mode 100644 index 0000000..230b7f6 --- /dev/null +++ b/src/doc/HOWTO/dosemu-HOWTO.sgml @@ -0,0 +1,1927 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +The dosemu HOWTO + + + +Mike Deisher. + Updated by Uwe Bonnes for dosemu-0.64.4, 15 March 1997. + Updated by David Hodges, davidhodges@altavista.iname.com + + + +For dosemu-1.2, 27 September 2003 + + + + +This is the `Frequently Asked Questions' (FAQ) / HOWTO document for +dosemu. The most up-to-date version of the dosemu-HOWTO may be found +at http://www.dosemu.org. + + + + + + + +The preliminaries + + +What is dosemu, anyway? + + +To quote the manual, "dosemu" is a user-level program which uses +certain special features of the Linux kernel and the 80386 processor +to run MS-DOS/FreeDOS/DR-DOS in what we in the biz call a `DOS box.' +The DOS box, a combination of hardware and software trickery, has these +capabilities: + + + + + + + the ability to virtualize all input/output and processor control +instructions + + + + + + the ability to support the word size and addressing modes of the +iAPX86 processor family's "real mode," while still running within +the full protected mode environment + + + + + + the ability to trap all DOS and BIOS system calls and emulate such +calls as are necessary for proper operation and good performance + + + + + + the ability to simulate a hardware environment over which DOS +programs are accustomed to having control. + + + + + + the ability to provide DOS services through native Linux +services; for example, dosemu can provide a virtual hard disk +drive which is actually a Linux directory hierarchy. + + + + + + + + +What operating systems does dosemu work under? + + +Dosemu is primarily written for Linux. At one time it also worked on NetBSD and +possibly FreeBSD, however the graphics emulation was unlikely to work +on NetBSD, which means that all DOS programs that use graphics mode +(most DOS programs) did not work under NetBSD. + + + + + +What processors does dosemu work on? + + +Dosemu only works on Intel 80x86 processors, e.g. 80386, 80486, +Pentium etc. + + + + + +What if I have an Alpha/Sparc/Motorola/other non-Intel CPU? + + +Dosemu only works on Intel 80x86 processors but there are alternatives: +Bochs (http://bochs.sourceforge.net) +is an open source PC emulator that +runs on most Unixes (as well as MS-Windows). +DOSBox (http://dosbox.sourceforge.net) +also emulates the CPU but specializes in DOS games. +QEMU ( +http://fabrice.bellard.free.fr/qemu/index.org.html) +can launch Linux processes compiled for one CPU on another CPU. +Wine and DOSEMU are the main targets for QEMU. +There are other +(non-free) alternatives - see the comp.emulators.misc FAQ, available via +usenet or http://www.faqs.org/faqs/emulators-faq/ + + + + + +Do I need MS-DOS to use dosemu? + + +No. You need some version of DOS but not necessarily MS-DOS. +The supplied FreeDOS will do for almost all DOS programs. One notable +exception is Windows 3.x. See also . + + + + + +Can I run Microsoft Windows programs under dosemu? + + +Yes, but it would be better to use the Windows emulator +Wine (http://www.winehq.com). If you insist on running Windows +under dosemu, see section 8 - dosemu and MS-Windows 3.1. + + + + + +I want to run something that won't run under DOSemu or Wine + + +See , or try VMWARE at +http://www.vmware.com +or Plex86 at http://www.plex86.org + + + + + +Names and numbers + + + +(xx/yy/zz) + + + + +means day zz in month yy in year xx. + + + + +winemu + + + +winemu mean WinOS/2 running in dosemu. + + + + + + + + + + +What version of Dosemu should I use? + + +Dosemu uses the same numbering scheme as the kernel used. Uneven second +numbers are for possible unstable developer releases, even second numbers +are for releases considered stable. At the time of writing, 1.4.0 +is the latest stable release, while 1.3.5 is the latest +developer's release. So if you just want to use dosemu, get the latest +stable release. + + + + + +What's the newest version of dosemu and where can I get it? + + +The newest stable version of dosemu as of 2007/05/05 is +dosemu-1.4.0 and can be obtained at + +http://www.dosemu.org/. + + + + +We just decided to leave BETA stage, however: there may be serious bugs +and very little documentation for new features. The development version +is particularly likely to have bugs. Please use it only if you +like to do active development. Preferably fix bugs in the development +version instead of reporting them. + + + + + +Where can I ask questions? + + +If you have problems regarding installing and running dosemu after +reading the documentation, first try to help yourself: Your question +has probably been asked and perhaps answered before. Try some search +engine on the internet to retrieve that information. E.g. you can ask + +http://groups.google.com + +to find all newsnet articles containing the keywords of your +question. Helping yourself will probably be faster than asking a well +known question. It also frees up the time of developers from answering +trivial questions and so helps the further development of dosemu. + + + + + +Where can I report bugs and ask questions? + + +If you want to ask questions and report bugs regarding dosemu, you +should consider subscribing to the linux-msdos mailing list. +To subscribe, send mail to Majordomo@vger.kernel.org +with the following command in the body of your email message: + + + +subscribe linux-msdos your_username@your.email.address + + + +If you ever want to remove yourself from the mailing list, you can +send mail to Majordomo@vger.kernel.org +with the following command in the body of your email message: + + + +unsubscribe linux-msdos your_username@your.email.address + + + +(95/8/11). When you are subscribed to linux-msdos, you can send your +report as mail to linux-msdos@vger.kernel.org. There is a gate that +send mails to linux-msdos@vger.kernel.org as postings to the +newsgroup named linux.dev.msdos. If your News provider +doesn't carry that group, ask her(him) to add that group. +Before you ask a question, you should carefully read all of the +documentation, including this HOWTO and check one of the mailing list +archives listed at + +http://www.dosemu.org/mailinglist.html + to see whether your +question has already been asked and answered. + + + +Alternatively, use the SourceForge Bug Tracking System for DOSEMU at + +http://sourceforge.net/tracker/?atid=457447&group_id=49784&func=browse +. The advantage of this system is that your bug is less likely +forgotten. + + + + +What documentation is available for dosemu? + + +Dosemu comes with documentation. The main documentation files +README.txt and README-tech.txt cover virtually all aspects of dosemu +and may be more up-to-date than this HOWTO. + + + +The "dosemu Novice's Altering Guide" or DANG is a road map to the +inner workings of dosemu. It is designed for the adventurous, those +who wish to modify the source code themselves. The DANG is maintained +by Alistair MacDonald (alistair@slitesys.demon.co.uk) and is +found in the doc directory of the dosemu source tree. + + + +The EMU failure list (EMUfailure.txt) is a partial list of programs known +not to work under dosemu. + + + +And then, of course, there is the dosemu FAQ/HOWTO. But you already +know about that, don't you. It is also posted once in a while to the +mailing list and found in the doc-directory. The most recent version +can be found at http://www.dosemu.org. + + + + + +I have a program that fails, not listed in EMUfailure.txt + + +First check, if the failure of your program is not caused by some +of the fundamental incapabilities of dosemu, listed in +EMUfailure.txt. If +you think you have something new, please report to +linux-msdos@vger.kernel.org. +Perhaps it can be made going with the help others. Give detailed +information about your setup, tell the version of kernel, dosemu etc +and name the observed errors. You can use xdosemu to cut and paste the +error message into your report. But keep your report in a readable +form. We know the content of the commented out options. So only send the +active lines from your dosemu.conf and +~/.dosemu/.dosemurc. +Try running dosemu with some or all +debug output turned on and scan through your debug output +and at first only send those parts you think are relevant. Few people +are willing to decode some long attachment to a mail, to do debugging +for others. But keep your logs at hand, in case others ask detailed +questions. + + + + + +How do I submit changes or additions to the HOWTO? + + +The preferred method is to edit the file +dosemu-HOWTO-xx.x.sgml to incorporate the changes, create a +diff file by typing something like: + + + +diff -uw original-file new-file + + + +and send it to linux-msdos@vger.kernel.org. +If you do not know SGML, that's ok. Changes or new information in any +form will be accepted. Creating the diff file just makes it easier on +the HOWTO maintainer. :-) + + + + + +Message from Greg... + + +Unless otherwise stated, Linux HOWTO documents are copyrighted by +their respective authors. Linux HOWTO documents may be reproduced and +distributed in whole or in part, in any medium physical or electronic, +as long as this copyright notice is retained on all copies. Commercial +redistribution is allowed and encouraged; however, the author would +like to be notified of any such distributions. + + + +All translations, derivative works, or aggregate works incorporating +any Linux HOWTO documents must be covered under this copyright notice. +That is, you may not produce a derivative work from a HOWTO and impose +additional restrictions on its distribution. Exceptions to these rules +may be granted under certain conditions; please contact the Linux HOWTO +coordinator at the address given below. + + + +In short, we wish to promote dissemination of this information through as +many channels as possible. However, we do wish to retain copyright on the +HOWTO documents, and would like to be notified of any plans to redistribute +the HOWTOs. + + + +If you have questions, please contact Greg Hankins, the Linux HOWTO +coordinator, at gregh@cc.gatech.edu(95/8/11). + + + + + + + +Compiling and installing dosemu + + +Where are the installation instructions? + + +The installation instructions are in the file INSTALL, +included in the distribution. + + + + + +Top problems while compiling and installing dosemu. + + + + + +Forgetting to read the README and +INSTALL. + + + + + +Try to compile some old version of dosemu. + + + + + +Use dosemu with a kernel that does not have IPC compiled in. + + + + + +Compile with gcc older than 2.95.2, egcs older than 2.91.66 (1.1.2) +or glibc older than 2.1.3. + + + + + +Forgetting to use a dosemu-freedos-bin tarball when no other DOS +is available on the system. + + + + + +Run DOSEMU with partition access while they are already mounted. + + + + + + + + +How do I make a.out binaries? + + + Starting with version 0.64.4 there is no a.out support any more. +If you absolutely need it, you must use version 0.64.3.1. +The configure script then should take care for this, if your setup is a +standard setup. + + + + + +How do I compile dosemu on a machine with low memory? + + +Marty Leisner leisner@sdsp.mc.xerox.com reported (95/4/8) that: + + + +If you have problems with running out of swap space you may want to +add CFLAGS+=-fno-inline after CFLAGS is defined in +src/dosext/dpmi/Makefile. Be careful before you do this and check for +the existence of swap space. I found Linux crashes at times when it +has no swap space. + + + + + +Compilation fails with some strange error regarding "slang" + + +You probably have plugin_slang off +in your compiletime-settings file. You need to change it to +plugin_slang on and recompile. + + + + + +What configurable options are available? + + +The compiletime-settings.help file describes the +options you can change +at compile time. README.txt describes the options you can +change at run time. + + + + + +Do I need to run dosemu as root? + + +No. Although most of dosemu drops root privilege after initialization, it is still +safer to not run dosemu as root. Most normal DOS applications don't need dosemu +to run as root, especially if you run dosemu under X. +Thus you should not allow users to run a suid root copy of dosemu, +wherever possible, but only a non-suid copy. You can configure this on +a per-user basis using the dosemu.users +file, or setup a configuration using sudo +and use dosemu -s (see INSTALL). + + + +Programs that require direct access to hardware ports (e.g. if the +program needs to talk to some unusual hardware that is not supported +by Linux and dosemu) require dosemu to run as root but printers, +serial ports, mice, video cards and sound are emulated by dosemu so +you won't usually need direct hardware access and won't need to +run as root to use these. +Some programs are just too sneaky for +dosemu's hardware emulation to work properly though ... + + + +You will need to edit dosemu.users (see +dosemu.users.example) for any suid-root/sudo +access beyond console graphics or run dosemu as root for hardware +access. + + + + + +How do I patch dosemu? + + +If you do patch dosemu from one version to another, do +"make pristine;./configure; make". +If you don't make pristine, at least the version +of the new executable will be wrong, if the whole thing compiles at +all (97/2/9). + + + + + +What versions of DOS are known to run with dosemu? + + +All versions of DOS should work with dosemu, with the following caveats: + + + +DOS 4.01 had problems by itself, so it won't work reliably with dosemu +either. + + + +With MsDos-7 aka Win95 you must not start the graphic shell +at bootup. If you make the hdimage bootable with the so called "Rescue Disk" +you are offered to make during the Windows installation, you get the right +settings. If you use your normal Win95 installation to transfer the system +files, have a look at the msdos.sys written on the hdimage and change the +settings under the section [Options] to have entries like: + + + +[Options] +Logo=0 +BootGUI=0 + + + + + + + +Hard disk setup + + +How do I use my hard disk with dosemu? + + +First, mount your dos hard disk partition as a Linux subdirectory. +For example, you could create a directory in Linux such as /dos (mkdir +-m 755 /dos) and add a line like: + + + +/dev/hda1 /dos msdos umask=022 + + + +to your /etc/fstab. (In this example, the partition is mounted +read-only. You may want to mount it read/write by replacing "022" +with "000" and using the -m 777 option with mkdir). Now mount +/dos. + + + +Now you can run (for instance) +lredir f: linux\fs/dos +at the DOS prompt to map drive F to /dos. +Re-redirecting C using lredir c: linux\fs/dos works too +but then you need to copy all your dosemu utilities (in +c:\dosemu) to a place where they can still be +found. + +If you want to boot via /dos, read on. + + + +The README.txt says: + + + + you just can have a Linux directory containing all what you + want to have under your DOS C:. Copy your IO.SYS, MSDOS.SYS or what + ever to that directory (e.g. $HOME/.dosemu/bootdir), put +$_hdimage = "bootdir" + into your $HOME/.dosemu/.dosemurc, and up it goes. +Alternatively you can specify an absolute path such as +"/dos" or "/home/username/dosemu/freedos". + DOSEMU makes a lredir'ed + drive out of it and can boot from it. You can edit config.sys and + autoexec.bat within this directory before you start dosemu. Further more, you may have a more sohisticated setup. Given you want to + run the same DOS drive as you normal have when booting into native + DOS, then you just mount you DOS partition under Linux (say to + /dos) + and put links to its subdirectories into the boot dir. This way you + can decide which files/directories have to be visible under DOSEMU and + which have to be different. Here's a small and incomplete example + bootdir setup: + + + +config.sys +autoexec.bat +command.com -> /dos/command.com +io.sys -> /dos/io.sys +msdos.sys -> /dos/msdos.sys +dos -> /dos/dos +bc -> /dos/bc +windows -> /dos/windows + + + + + +How can I access the hdimage from Linux? + + +Use mtools. With a line in /etc/mtools.conf like: + + + +drive n: file="/path/to/hdimage" MTOOLS_SKIP_CHECK=1 \ +MTOOLS_LOWER_CASE=1 MTOOLS_NO_VFAT=1 partition=1 offset=128 + + + +you can use the mtools on the hdimage, like "mdir n:". "mcopy +n:/config.emu /tmp" copies the config.emu file from the hdimage to +/tmp/config.emu. You can edit it there and copy it back. Use a drive +letter you find sensible. "N:" is only an example. + + + + + +Can I use my stacked/double-spaced/super-stored disk? + + +At this time, compressed drives cannot be accessed via the +redirector (lredir or emufs) on a standard kernel. There is a patch +for the kernel to mount compressed files under the name "dmsdosfs". +Find it on ibiblio.org and its mirrors + +http://ibiblio.org:/pub/Linux/system/filesystems/dosfs/. + +A good idea is also to look in http://ibiblio.org:/pub/Linux/Incoming +for a newer version. +The "wholedisk" option in older versions of dosemu is no longer +allowed in recent versions, however, a line like + + + +$_hdimage = "/dev/hda1" + + + +may work, if you have read and write access to this device and +at the risk that you could lose all data in that partition +on a dosemu crash. Also it is extremely dangerous to let +two dosemu sessions access the partition at the same time. + + + +If your dos partition is already mounted with write access and you try +to run dosemu with partition access, dosemu will print a +warning message and abort. This prevents DOS and Linux from making +independent writes to your disk and trashing the data on your dos +partition (95/8/11). + + + +Holger Schemel q99492@pbhrzx.uni-paderborn.de reported +(94/2/10) that: + + + +Works even fine under dosemu with MS-DOS 6.0. If you have problems, then +you have to edit the file `DBLSPACE.INI' manually and change the disk +letter to the letter your drive gets under dosemu. + + + + + +Creating your own hdimage file. + + +There is an extra util program called mkfatimage16 which +allows for creating a hdimage file headers. The full information is in the +manpage (man/mkfatimage16.1) included in your distribution. + + + +To create a hard disk image file with a geometry corresponding to that of a +real hard disk of 32 megabytes run: + + + +mkfatimage16 -k 32768 > hdimage + + + +This is probably too large for most needs; if you need this much +space, consider using the disk redirector. + + + +Usually it is a good idea to format the drive after it. + + + + + + + +Parallel ports, serial ports and mice + + +Port access worked with older version, but doesn't work now! + + +Read README.txt and the port-section +in dosemu.conf. Also, you must now you the -s +dosemu command line switch. + + + + + +Port access was faster with older versions! + + +To have a chance to log port access, by default every port access +produces an exception out of vm86-mode. This takes some time. +If you don't want to log port access, use the keyword fast in the +appropriate port statement. + + + + + +Where are the (microsoft compatible) mouse drivers? + + +Tom Kimball tk@pssparc2.oc.com reported (93/11/24) that: + + + +Several people said to use a different mouse driver and suggested +some. I found a couple that seem to work fine. + + + +oak.oakland.edu:/pub/msdos/mouse/mouse701.zip (mscmouse) +oak.oakland.edu:/pub/msdos/mouse/gmous102.zip (gmouse) + + + +Normally you can use dosemu's internaldriver, so you don't need any +additional mousedriver in dosemu outside winemu (97/2/10). + + + + + +Why doesn't the mouse driver work? + + +Mark Rejhon mdrejhon@magi.com reported (95/4/7) that: + + + +If you start the mouse driver and it just hangs (it might actually +take 30-60s), but if you are waiting longer than a minute for the +mouse driver to start, try specifying the COM port that the mouse +is on, at the mouse driver command line. + + + + + +Why does dosemu clobber COM4? + + +Rob Janssen rob@pe1chl.ampr.org reported (94/3/24) that: + + + +According to jmorriso@bogomips.ee.ubc.ca, "dosemu still +clobbers COM4 (0x2e8, IRQ 5). 0x2e8 isn't in ports{} in config. I +have to run setserial /dev/ttyS3 irq 5 on it after dosemu exits." + + + +This is caused by your VGA BIOS. I have found that by enabling the IO +port trace and seeing where it was clobbered. + + + +Disable some ports in config and it will +work fine. When you then have problems with the video, try to enable +more selective ranges of IO addresses (e.g., 40-43). + + + + + +How do I use dosemu over the serial ports? + + + + +How can I switch between dosemu and a shell over the serial line? + + +John Taylor taylor@pollux.cs.uga.edu reported (94/5/25) +that: + + + +I am running Linux 1.1.13 and want to point out a great feature that +should be protected and not taken out (IMHO). With the 52 version, I +can run the program, "screen." From screen, i can invoke dosemu -D-a. +What is really great (IMHO) is the screen commands (the CTRL-A cmds) +still work. This means I can do a CTRL-A C and add another unix shell, +and switch between the two (DOS / UNIX). This allows me to use dosemu +over the serial line really well, because switching is made easy. + + + +You may also consider using dosemu -dumb. In this +case the escape sequences from the DOS applications go directly to the +terminal. + + + + + +How can I get the parallel ports to work? + + +The dosemu.conf has lines at the end to redirect printers to either +lpr or a file. If you want direct access to the bare metal, comment +out these emulation lines, and add the line: + + + +$_ports = "device /dev/lp0 fast range 0x3bc 0x3bf" # lpt0 + + + +for the "monitor card" printer port (corresponds to /dev/lp0), or: + + + +$_ports = "device /dev/lp1 fast range 0x378 0x37f" # lpt1 + + + +or: + + + +$_ports = "device /dev/lp1 fast range 0x278 0x27f" # lpt2 + + + +for LPT1 (/dev/lp1) or LPT2 (/dev/lp2) respectively(97/2/9). + + + +Hans Lermen lermen@dosemu.org writes: + + + +But NOTE: these lines should not be _added_ simply, the string should +be concatenated such as: + + + +$_ports = "...." +$_ports = $_ports, " device /dev/lp0 fast range 0x3bc 0x3bf" += +This blank is important + + + +Note: newer kernels don't have hardcoded dependencies between +/dev/lp* and ioports. Check +/proc/ioports for lines such as +0378-037a : parport0. + + + + + + + +Multiple users and Non-interactive sessions + + +Can I use dosemu on a multi-user system? + + +Yes, you can configure dosemu on a per-user basis. See README.txt for +details. + + + + + +How can I run dos commands non-interactively? + + + +You can do any of the following: + + + + + + +use the keystroke configuration option and the -input command line option +as described in the manual page to specify keystrokes on the command +line that will act exactly as if you had typed them within dosemu. + + + + + +Use the dosemu [-E] dos-command and +unix -e as your +last line in autoexec.bat. +The specified dos command is executed. If '-E' is not given, then +DOSEMU exits after running the command. + + + + + + +Here are some additional alternatives: + + + +Daniel T. Schwager danny@dragon.s.bawue.de reported +(94/7/2) that: + + + +You can use different dosemu.conf files (and different hd-boot-images +with different autoexec.bat's) and call dosemu like: + + + +$ dosemu -f my_quicken_q_exe_dosemu.conf + + + +--------------------- + + + +Dietmar Braun braun@math20.mathematik.uni-bielefeld.de +reported (94/7/4) that: + + + +This is no problem at all when you use the redirector of dosemu. It +is possible to redirect a drive letter to a linux path given by an +environment variable. + + + +So I have a shell script named "DOS" which does something like: + + + +mkdir /tmp/dos.$$ +DOSTMP=/tmp/dos.$$; export DOSTMP + + + +and then a little trick to get "echo $* > +$DOSTMP/startup.bat" really working (actually a small C Program +which turns `/' in `\' and terminates lines correctly for +messy dos with cr/lf pairs and adds ^Z at the end of the +file), creates startup files, links and so on in this directory, and +then starts dosemu. Within "autoexec.bat" drive c: is +redirected from hdimage to this tmp-directory, which has links for +$HOME and $PWD. + + + +So if I want to see my filenames shortened to 8.3 I can type "DOS +dir" and I get my current directory listing. So I have full DOS +multi user (I don't have any DOS partition and redirecting to Linux +preserves user permissions) and multi tasking. (dosemu sessions +are completely independent). I did this once to be able to use a dos +driver for my printer. My printcap df is actually a DOS program. So +you can even make DOS executables act as lpr filters. + + + + + + + +dosemu and Netware + + +How do I get Netware access from dosemu? + + +As always, access through the Linux filesystem is preferred. Mount +your Netware drives with Caldera's Netware utilities or Volker +Lendecke's free ncpfs utility +ftp://ftp.gwdg.de:/pub/linux/misc/ncpfs. +If you need real IPX access, +e.g. to run Novell's "syscon", read doc/NOVELL-HOWTO.txt. +You probably can not currently access Netware from FreeDOS. + + + + + + + +dosemu and X-windows(97/2/9). + + +How do I obtain fullscreen mode in xdosemu? + +Toggle between windowed and fullscreen mode using +Ctrl-Alt-F. + + + + +Can I run dosemu in console mode while running X? + + +Ronald Schalk R.Schalk@uci.kun.nl reported (94/1/17) +that: + + + +Yes, no problem. Just remember to use ctrl-alt-<Fn> to go to +a Virtual Console (VC), and you can run any Linux application (dosemu +is a linux-application). I've got almost always WP5.1 in a dos +session. + + + +[Note: Use ctrl-alt-F7 to switch back to X from dosemu, if X +runs on VC7.] + + + + + +Is it possible to run dosemu in a window in X-windows? + + +If you have X installed and you have successfully compiled dosemu +and run it successfully outside X-windows, you should be able to +run xdosemu or dosemu -X right away to bring up a dosemu +window. If this does not work, make sure: + + + + + +Dosemu has X support compiled in. This is default, however +if you you have compiled dosemu with "x off" in the +compiletime-settings file you don't have X support. So changing +"x off" to "x on" in the compiletime-settings file, followed by +"make pristine; make; make install" should build +you a dosemu-executable with X support, if you have the +X-libraries installed in /usr/X11R6. + + + + +You might need to fix backspace and delete; but nowadays this is +rarely necessary. In that case, set up your X key-mappings. In an xterm, type +xmodmap -e "keycode 22 = 0xff08", and then xmodmap -e "keycode 107 = 0xffff". + + + + +Configure the X-related configuration options in +~/.dosemu/.dosemurc. + + + + + +Alternatively, you can run dosemu inside a color xterm, +which is not recommended because many color xterms have buggy +support for the complex text display capabilities of dosemu. +This does not require X_SUPPORT to be compiled into dosemu. +However, if you really want to do this, do the following steps: + + + + + +If necessary set up your X key-mappings as explained above. + + + + +Configure the terminal-related (not X-related) settings in +~/.dosemu/.dosemurc. + + + + + +Marty Leisner leisner@sdsp.mc.xerox.com reported (95/3/31) +that: + + + +I have xrdb log the following resources + + + +dosxterm*Font: vga +dosxterm*geometry: 80x25 +dosxterm*saveLines: 25 + + + +or I alias dosxterm to +term -fn vga -title dosxterm -geometry 80x25 -sl 25. + + + +If you use the xrdb method, all you have to do is run +xterm -name dosxterm. + + + + + +Xdosemu does not work on a remote X-display! + + +At present, dosemu is set up to use the MIT shared memory +extensions. This extension only works on a local display. If you want +to run xdosemu on a remote display, you might need to set +$_X_mitshm=(off) in your +dosemu.conf +or ~/.dosemu/.dosemurc. + + + + + +Xdosemu does not find the VGA font + + +Check that the vga fonts you installed are listed in the font.dir of the +directory you installed the fonts in: + + + +hertz:~> grep misc /usr/X11R6/lib/X11/XF86Config +FontPath "/usr/X11R6/lib/X11/fonts/misc/" +hertz:~> grep vga /usr/X11R6/lib/X11/fonts/misc/fonts.dir +vga.pcf vga +vga11x19.pcf vga11x19 +hertz:~> ls /usr/X11R6/lib/X11/fonts/misc/vga* +/usr/X11R6/lib/X11/fonts/misc/vga.pcf +/usr/X11R6/lib/X11/fonts/misc/vga11x19.bdf +/usr/X11R6/lib/X11/fonts/misc/vga11x19.pcf + + + + +If you installed some X-fonts, like you did when you installed dosemu with +X-Support for the first time, "mkfontdir" and then "xset fp rehash" needs +to be run. The dosemu install should take care for "mkfontdir" and tells you +about "xset fp rehash". Tell us if it doesn't work for you. +(97/2/13) + + + + + +The vga font is very small on my high resolution display + + +Set $_X_font="vga11x19". + + + + + +Dosemu compilation fails with some strange error regarding X! + + +As stated above, dosemu uses the MIT shared memory extensions by +default. Under XFree86 they are only available with Version 3.1.2 and +above. If you have an older version, consider to upgrade, or configure +dosemu to not use this extension by setting mitshm +off in compiletime-settings (97/2/9). + + + + + +Does ansi emulation work properly? + + +Marty Leisner leisner@sdsp.mc.xerox.com reported (95/3/31) +that: + + + +Yes. I use nnansi.com under X windows. I find 25, 43 and 50 +line mode work properly, however 50 line mode is difficult to use on a +1024x768 screen (unless smaller fonts are used or you use a bigger +screen. 43 line mode will resize the xterm window to use 43 lines. + + + +DEVICE=c:\bin\nansi.sys in FreeDOS' +config.sys works too, except in "dumb" mode. + + + + + + + +dosemu and MS-Windows 3.x + + +Is it possible to run MS-Windows 3.x under dosemu? + + +Yes, versions up to and including Windows for Workgroups 3.11 are +currently working, but only under DOSEMU+MSDOS (not FreeDOS). + + + + + +Can I run 32bit stuff in Windows? + + +Sorry, no you can't. +Win32s is not supported yet, Win9x is neither. + + + + + +Can I run Windows 3.x in xdosemu + + +Yes, this is similar to using it in DOSEMU on the console; the only +extra thing to consider here is that the emulated graphics card +in xdosemu is a Trident, which is most likely different from +your real graphics card. The mouse also needs some special +attention. + + + +Hints: + + + + + +For faster graphics (256 colors instead of 16 is faster in xdosemu), +get the Trident SVGA drivers for Windows. The files are tvgaw31a.zip +and/or tvgaw31b.zip. They are available at garbo.uwasa.fi in +/windows/drivers (any mirrors?). + + + + +Unpack the Trident drivers. + + + + +In Windows setup, install the Trident "800x600 256 color for 512K +boards" driver. + + + + +Start xdosemu. + + + + +In Dosemu, start windows. + + + + + + + +Can I install windows from within dosemu? + + +Yes, up to and including Windows For Workgroups 3.11. + + + +Notes for the mouse under win3.x-in-xdosemu: + + + + +In order to let the mouse properly work you need the following in your +win.ini file: + + + +[windows] +MouseThreshold1=0 +MouseThreshold2=0 +MouseSpeed=0 + + + + + +The mouse cursor gets not painted by X, but by windows itself, so it depends +on the refresh rate how often it gets updated, though the mouse coordinates +movement itself will not get delayed. +In fact you have 2 cursors, but the X-cursor is given an `invisible' +cursor shape while within the DOS-Box. + + + + + +Because the coordinates passed to windows are interpreted relatively, we +need to calibrate the cursor. This is done automatically whenever you +enter the DOS-Box window: The cursor gets forced to 0,0 and then back +to its right coordinates. Hence, if you want to re-calibrate the cursor, +just move the cursor outside and then inside the DOS-Box again. +(97/2/10) + + + + + + + + + +Video and sound + + +Can I run 32-bit video games under dosemu? + + +Mark Rejhon mdrejhon@magi.com reported (95/4/8) that: + + + +You can run many 32-bit video games in dosemu. +If the game is compatible in an OS/2 DOS box, there are chances +that it will work in dosemu. +(Example 32-bit games include Descent, Dark Forces, Mortal Kombat 2, +Rise of The Triad, which have all successfully been tested in recent +dosemu releases). + + + +Before you attempt to run a video game on the console, +you must have the keyboard configured in raw keyboard mode, +and enabled VGA graphics modes, in +dosemu.conf or run dosemu -s. +Moreover you must run DOSEMU as root, execute it via sudo or make +dosemu.bin suid root. + + + +Alternatively try running the game in xdosemu; press +Ctrl-Alt-F to toggle fullscreen mode. Root +permissions are unneeded and in almost +all cases no configuration is necessary, but the speed may be +slower than on the console, and a few games still exhibit glitches. +Use speed 0 or +some high value instead of 0 at the DOS prompt to temporarily set +your HogThreshold to run DOSEMU +at full tilt. After exiting the game use speed 1 +or exit DOSEMU to give the CPU some rest. + + + +Note that game timers can be a little bit slow, due to +Linux multitasking and lack of high-frequency timer support. So the +games may run from anywhere from 5 to 100 percent speed. Typically, +the speed is approximately 50 percent in recent dosemu releases and is +expected to improve eventually. + + + +Who knows, it might even work. If you can't get it to work, check +EMUfailure.txt if the program is listed there, or falls in a category of +programs that at present don't or probably never work with dosemu. If +you think, it should be listed in EMUfailure.txt, report to +linux-msdos@vger.kernel.org. + + + + + +Exiting from dosemu on the console gives me a screen full of garbage. + + + (95/4/8) +The problem is that the font information for the VGA text screen is +not being saved. Get a copy of the svgalib package. The current +source is in: + + + +ibiblio.org:/pub/Linux/libs/graphics/svgalib-1.4.3.tar.gz + + + +It may also be available as a pre-compiled package in your favorite +Linux distribution (e.g., Slackware, etc.). Use savetextmode to +save the current text mode and font to a file in /tmp before +running dosemu. Then run textmode upon exiting dosemu to restore +it. + + + +Addition from lermen@elserv.ffm.fgan.de (97/2/11): + + + +Have a look also at README-tech.txt, where it +is explained how dosdebug can aid you recovering. + + + +If you use a graphical framebuffer console (but not vesafb) then +/usr/sbin/fbset "1400x1050-60" +or whatever your normal resolution and frequency are may help. + + + + + +Why doesn't my soundcard software work with dosemu? + + +Dosemu includes a Sound Blaster 16 emulator called SBemu. See +sound-usage.txt for information on setting this up. It may not +work for everything. Programs that are known to work are Impulse +Tracker and most games. One that is known not to work well is +FastTracker II. + + + +If you have trouble getting sound to work at all, make sure you +have a working OSS driver and have configured everything properly +as described in sound-usage.txt. If most of your sound software +works but some programs don't detect sound, make sure your BLASTER +environment variable is set properly. Finally, make sure you are +aware of the difference between digital sound, FM, and MIDI; it +may be, for example, that you have digital sound working but MIDI +isn't set up properly. + + + +If you believe DOSEMU really is having trouble with a specific +program, and it isn't a configuration error on your part, then +please, submit a bug report! Have a look at sound-usage.txt for +information on getting debugging output. + + + + + + + +Problems and fixes + + +Security issues + + +A full featured Dosemu needs to be suid root, e.g to access ports. +Most dos programs don't need this however so when running under X you +usually do not need to run dosemu as root. +Dosemu runs as suid "root" only at the initialization stage, and drops +this right thereafter, except in a forked "port server" (where necessary). +But with DPMI, the Dos client program can access the whole user +space, hence also can modify the dosemu code itself. +This cannot lead to privilege elevation but may crash DOSEMU. +There are several other important security considerations discussed in the +Security section of README.txt. + + + + + +dosemu says "ERROR: general protection" and terminates when I run some program + + +This will happen if your program uses DPMI and you do not have DPMI +enabled. Try changing the line: + + + +$_dpmi = (off) + + + +in dosemu.conf to: + + + +$_dpmi = (nnnn) + + + +where nnnn is the number of kilobytes of memory you wish to give to +the DOS program (e.g. Doom requires about 4000 kilobytes). + + + +Another likely cause is that your program uses VCPI (see +EMUfailure.txt +for more details) or some other means of switching to protected mode +(other than DPMI), in which case it will never work under dosemu. + + + +If none of these is the case, see . + + + + + +Dosemu dies when booting. I have Win95 installed. + + +Dosemu relies that the Dos-Version on the hdimage and the Drive you map +to contain command.com are the same. If not, dosemu will crash sooner or +later. With the dual boot option Win95 offers when pressing the +F4, F5 and +F8 keys with the "Starting Win95" text, versions on the Win95 drive may +swap. Take special care for command.com. Let your shell variable in +config.emu point to the correct static version of command.com, e.g.: +shell=c:\win95\command.com c:\ /P /E:1024 (97/02/28) + + + + + +Dosemu hangs! How can I kill it? + + +Switch to another console and type dosdebug, then type kill (it may +take a while but will work eventually). + + + + + +Dosemu crashed and now I can't type anything. + + +Daniel Barlow jo95004@sable.ox.ac.uk reported (95/4/8) that: + + + +If you have no terminal or network access that you can use to log in, +you may have to press the reset button. If you can still get a usable +shell somehow, run kbd_mode -a to switch the keyboard out of +raw mode, and/or stty sane on the console so that you can see +what you're typing. + + + +A useful thing to do is to use a script to run dosemu, and run +kbd_mode -a automatically right after dosemu. When dosemu +crashes, the script usually will resume running, and execute +kbd_mode -a. + + + +Alternatively, if the magic SysRq key is enabled in the Linux kernel, +you can use Alt-SysRq-r to switch to xlate mode, and +use Alt-Fn to switch to any other console (including +X, if your screen display is thoroughly broken). + + + + + +I've enabled EMS memory in dosemu.conf but it does not help. + + +Rob Janssen rob@pe1chl.ampr.org reported (94/7/11): +Don't forget to load the provided ems.sys from +config.sys. + + + + + +How do I get rid of all those annoying "disk change" messages? + + +(94/8/11) +Grab and install klogd. Try: + + + +ibiblio.org:/pub/Linux/system/Daemons/sysklogd1.2.tgz + + + + + +Why won't dosemu run a second time after exiting in console mode? + + + Aldy Hernandez aldy@sauron.cc.andrews.edu reported (94/7/8): +You should disable your video and/or BIOS caching. + + + + + +Why will dosemu run in a term but not in the console? + + +JyiJiin Luo jjluo@casbah.acns.nwu.edu reported +(94/4/19): +I experienced exactly the same problem before. I figured out all the +video shadow in my AMI BIOS must be disabled. Now dosemu runs fine on +my system. + + + + + +How can I speed up dosemu? + + +In some cases it is useful to play with the value of the +HogThreshold variable in your dosemu.conf file. + + + +Daniel Barlow jo95004@sable.ox.ac.uk reported +(95/4/8): +HogThreshold should now be set to approximately half of the BogoMips +value that the system reports on boot. + + + + + +How do I see debugging output? + + +Daniel Barlow jo95004@sable.ox.ac.uk reported (95/4/8): +As of dosemu 0.60, debugging output is redirected to a file specified +on the command line. Use dosemu -D+a -o /tmp/debug to log all +debug output to /tmp/debug. There should no longer be any +need to redirect stderr. + + + + + +Why are my keystrokes echoed ttwwiiccee?? + + +Nick Holloway alfie@dcs.warwick.ac.uk reported (94/2/22) +that: + + + +After running dos after playing with some stty settings, I was getting +doubled key presses. I can now reveal what the reason is! + + + +It only happens when dos is run on the console with `istrip' set. This is +(I think) because the raw scancodes are mutilated by the `istrip', +so that key release events look like key press events. + + + +So, the input processing needs to be turned off when using the scan +codes on a console (it wouldn't be a good idea to do it for tty lines). + + + + + +Dosemu scrambles my screen? + + + For those graphics cards not fully supported in dosemu, with allowed +console graphics a dosemu crash may leave your console in a scrambles and +nearly unusable way. To prepare for that situation, Spudgun +spudgun@earthlight.co.nz posted the following solution. First save your +registers when running on the console +~> cat /usr/bin/savetextmode +~> restoretextmode -w /etc/textregs +~> restorefont -w /etc/fontdata +Then, when a crash happened, run following script: +restoretextmode -r /etc/textregs +restorefont -r /etc/fontdata +restorepalette + + + +If it doesn't fix it nothing will! +I also found having an X server running sometimes put my Vid card's +registers into a strange state where this script made things worse +I think since changing X servers and/or running savetextmode on a vt while X +was running helped. (97/04/08) + + + + + +MS FoxPro 2.6 won't run + + +FoxPro 2.6 doesn't run on network drives. Alexey Naidyonov +growler@growler.tsu.tula.ru states on that problem: +And I guess your FoxPro files are on lredir'ed disk, yeah? The matter is that +FoxPro doesn't run on such disk, but when I said $_hdimage="/dev/hda? ..." in +dosemu.conf, it runs. + + + + + + + +Contributing to the dosemu project + + +Who is responsible for dosemu? + + +Dosemu is built upon the work of Matthias Lautner and Robert Sanders. +Bart Oldeman bart@dosemu.org is responsible for +organizing the latest releases of dosemu. + + + + +History of dosemu +Version Date Person +------------------------------------------------- +0.1 September 3, 1992 Matthias Lautner +0.2 September 13, 1992 Matthias Lautner +0.3 ??? Matthias Lautner +0.4 November 26, 1992 Matthias Lautner +0.47 January 27, 1993 Robert Sanders +0.47.7 February 5, 1993 Robert Sanders +0.48 February 16, 1993 Robert Sanders +0.48pl1 February 18, 1993 Robert Sanders +0.49 May 20, 1993 Robert Sanders +0.49pl2 November 18, 1993 James MacLean +0.49pl3 November 30, 1993 James MacLean +0.49pl3.3 December 3, 1993 James MacLean +0.50 March 4, 1994 James MacLean +0.50pl1 March 18, 1994 James MacLean +0.52 June 16, 1994 James MacLean +0.60 April 9, 1995 James MacLean +0.64.4 February 9,1997 Hans Lermen +0.66.3 April 20, 1997 Hans Lermen +0.98.1 December 9, 1998 Hans Lermen +0.98.6 March 21, 1999 Hans Lermen +1.0.0 March 6, 2000 Hans Lermen +1.2.0 January 18, 2004 Bart Oldeman +1.4.0 May 5, 2007 Bart Oldeman + + + + + +I want to help. Who should I contact? + + +Please join the linux-msdos and dosemu-devel lists; see + +http://www.dosemu.org/mailinglist.html + for more information. + + + + + +
diff --git a/src/doc/HOWTO/tweaks.sgml b/src/doc/HOWTO/tweaks.sgml new file mode 100644 index 0000000..9fca270 --- /dev/null +++ b/src/doc/HOWTO/tweaks.sgml @@ -0,0 +1,411 @@ + + + + +
+ + + +Known tweaks needed to run programs under dosemu + + + + +This file lists programs that needs specific tweaks in order to +run them under dosemu2. + + + + + + + +Millenium + + + +Millenium game fails to detect Sound Blaster, unless the SB IRQ is 5 + + +Solution: set $_sb_irq=(5). Fortunately this is a default setting, so +in most cases you won't care. + + + + +Millenium game crashes when entering space combat + + +Solution: to the file 2200gx.exe apply the following patch: + +000030B2: AA 47 + + + + + + +Need For Speed Special Edition + + +Wrong colors in video clips. + + +Solution: to file nfs.exe apply the following patch: + +000607E6: 00 08 + + +see here for details + + + + + +Hangs or glitches in video clips. + + +Solution: to file nfs.exe apply the following patch: + +000B8E5A: 78 EB +000B8E72: 78 EB + + + + + +Installer crashes or unstable. + + +Solution: to file infsd.exe apply the following patch: + +0002873E: 7C EB + + +see here for details + + + + + + +Gobliiins + + +Goblins halts at startup with "Divide Error" message + + +Solution: apply the following patch to gobega.exe: + +00004A19: F7 90 +00004A1A: F3 90 + + + + + + +Prehistorik 2 + + +Game freezes or crashes shortly after start. + + +Solution: set + +$_umb_f0 = (off) + +in your dosemu.conf or .dosemurc. + +see here for details + + + + + +Sound is crackling. + + +Solution: set + +$_dos_up = (off) + +in your dosemu.conf or .dosemurc. +The only "explanation" we can provide, is a conspiracy theory that +prehistoric is trying to slander third-party DOSes that put LoL/SDA +to UMB. + + + + + +Carmageddon + + +Timer is too fast during race + + + +see here for details + + + +Solution: for hi-res mode, apply the following patch to carma.exe: + +00083549: 89 83 +0008354A: C5 E8 +0008354B: 29 04 +0008354C: F5 90 +0008354D: 89 90 +0008354E: 2D A3 + + + +For low-res mode, apply the following patch to carma.exe: + +00083549: 89 83 +0008354A: C5 E8 +0008354B: 29 02 +0008354C: F5 90 +0008354D: 89 90 +0008354E: 2D A3 + + + +Unfortunately the timer is still a bit unstable, and runs as slower +as faster you drive your car. +To completely disable the timer, apply the following patch to carma.exe: + +00083549: 89 83 +0008354A: C5 E8 +0008354B: 29 00 +0008354C: F5 90 +0008354D: 89 90 +0008354E: 2D A3 + + + + + + +Simon 2 + + +Game freezes at start. + + +Solution: apply the following patch to runflat.exe: + +000130FF: 75 90 +00013100: F7 90 +00013110: 75 90 +00013111: F6 90 +00013112: D1 90 +00013113: E9 90 + + + + + + +Prince of Persia 2 + + +Game slows down unbearably after some playing. + + +Solution: update to v1.1 of the game or apply the following +patch to prince.exe: + +00016549: CF CB + + + + + + +Cosmo game by Apogee Software + + +Cosmo's Cosmic Adventure doesn't start + + +Solution: Unpack the cosmo1.exe which is packed with LZEXE. +Then apply the following patch: + +00011F8F: 75 90 +00011F90: F9 90 + + + + + + +Pinball Fantasies + + +Long start-up delay (like half a minute) + + +No solution yet. + + + + +Black screen and hang after choosing the table. + + +Solution: A few driver files needs to be patched. +Or just patch the one that you selected in a sound setup. + + +Apply the following patch to sb16.sdr: + +00002007: F7 90 +00002008: F3 90 + + + +Apply the following patch to sbpro.sdr: + +000020A4: F7 90 +000020A5: F3 90 + + + +Apply the following patch to sb20.sdr: + +0000206B: F7 90 +0000206C: F3 90 + + + +Apply the following patch to sblaster.sdr: + +00001F25: F7 90 +00001F26: F3 90 + + + + +Game doesn't see the Fx keys that should start the game. + + +No solution yet, the game is unplayable. + + + + + +WordPerfect 6.2 + + +Hang trying to play the MIDI file embedded in document. + + +Apply the following patch to vmp.com: + +00002C4B: 72 90 +00002C4C: 0D 90 +00002C5B: 01 00 + + +see here for details + + + + + + +LEXICON 1.2 (mod 8.98) + + +dosemu crashes after a few minutes of work. + + +Looking at lexicon code, the crash seems intentional. +Probably some kind of a copy protection. +Apply the following patch to lexicon.exe: + +0000ED9C: 74 EB +00011DAE: 74 EB + + + + + + +Test Drive 2 + + +Game doesn't start, just returns to command prompt. + + +Test Drive 2 only works from drives up to F:. In most dosemu2 setups, +these drive letters are occupied. Solution is to copy the game to C:. + + + + + +Tetris Classic, Super Tetris + + +Game is too slow. + + +Solution: set + +$_timer_tweaks = (on) + + + + + + +Street Fighter 2 + + +Game crashes on intro screen. + + +Solution: set + +$_timer_tweaks = (on) + + + + + + +Game Wizard (GW) + + +Hangs on start. + + +Solution: set + +$_dos_up = (off) + + + + + + +Seven Cities of Gold + + +Game hangs on start-up + + +Apply the following patch to 7cities.exe: + +000049DE: 72 90 +000049DF: D8 90 + + + + + +
diff --git a/src/doc/Makefile b/src/doc/Makefile new file mode 100644 index 0000000..da53c30 --- /dev/null +++ b/src/doc/Makefile @@ -0,0 +1,7 @@ + +SUBDIRS = HOWTO README + +all clean install: + @for i in $(SUBDIRS); \ + do $(MAKE) -C $$i $@; \ + done diff --git a/src/doc/README/CDROM b/src/doc/README/CDROM new file mode 100644 index 0000000..8789a62 --- /dev/null +++ b/src/doc/README/CDROM @@ -0,0 +1,197 @@ + +Using CDROMS + + +The built-in driver + + +This documents the cdrom extension rucker@astro.uni-bonn.de has +written for Dosemu. + + + +An easy way to access files on a CDROM is to mount it in Linux and use +Lredir to refer to it. However, any low-level access, often used by +games is impossible that way, unless you use the C option. +For that you need to load some drivers in DOS. CDROM image files (ISOs) +can be used in a similar fashion. + + + +The driver consists of a server on the Linux side +(src/dosext/drivers/cdrom.c, accessed via int 0xe6 handle 0x40) and a +device driver (src/commands/cdrom.S) on the DOS side. + + + +Please send any suggestions and bug reports to <rucker@astro.uni-bonn.de> + + + +To install it: + + + + + + Create a (symbolic) link /dev/cdrom to the device file of your drive +or use the cdrom statement in dosemu.conf to define it. + + + + + + Make sure that you have read/write access to the device file of your +drive, otherwise you won't be able to use the cdrom under Dosemu +directly because of security reasons. + + + + + Load cdrom.sys within your config.sys file with e.g.: + + + + + + devicehigh=d:\dosemu\cdrom.sys + + + + + + +Mount the CD-ROM in Linux (some distributions do this automatically), and +use + + + + lredir e: linux\fs/media/cdrom c + + + +to access the CD-ROM as drive E:. The "C" option specifies that the +low-level access provided via cdrom.sys is used. Or ... +start Microsoft cdrom extension as follows: + + + + + + mscdex /d:mscd0001 /l:driveletter + +or + + shsucdex /d:mscd0001 /l:driveletter + + + + + + + + + + +To change the cd while Dosemu is running, use the DOS program 'eject.com'. +If is not possible to change the disk, when the drive has been opened by +another process (e.g. mounted), then you need to unmount it first! + + + +Lredir has the advantage of supporting long file names, and not using +any DOS memory, whereas MS/SHSUCDX are more low-level and perhaps more +compatible. You would need to use a DOS TSR driver such as DOSLFN to +use long file names with SHSUCDX. + + + +Remarks by zimmerma@rz.fht-esslingen.de: + + + +This driver has been successfully tested with Linux' SCSI CDROMS by the +author, with the Mitsumi driver mcd.c and with the Aztech/Orchid/Okano/Wearnes- +CDROM driver aztcd.c by me. With the latter CDROM-drives changing the CD-ROM +is not recognized correctly by the drive under all circumstances and is +therefore disabled. So eject.com will not work. +For other CD-ROM drives you may enable this feature by setting the variable +'eject_allowed = 1' in file dosemu/drivers/cdrom.c (you'll find it near the +top of the file). With the mcd.c and aztcd.c Linux drivers this may cause your +system to hang for some 30 seconds (or even indefinitely), so don't change the +default value 'eject_allowed = 0'. + + + +Support for up to 4 drives: + + + +If you have more then one cdrom, you can use the cdrom statement +in dosemu.conf like this: + + + $_cdrom = "/dev/cdrom /dev/cdrom2 image.iso" + + +and have multiple instancies of the DOS driver: + + + device=cdrom.sys + device=cdrom.sys 2 + device=cdrom.sys 3 + + + + + +The one and only parameter to the device driver is a digit between 1 and 4, +(if its missing then 1 is assumed) +for the DOS devices MSCD0001, MSCD0002 ... MSCD0004 respectively. You then +also need to use lredir or tell MSCDEX about these drivers such as + + + lredir e: linux\fs/media/cdrom c + lredir f: linux\fs/media/cdrom2 c 2 + lredir g: linux\fs/media/cdrom3 c 3 + +where you need to loop-mount the image file, or + + mscdex /d:mscd0001 /d:mscd0002 /l:driveletter + + + +In this case the /l: argument defines the driveletter of the first /d:, +the others will get assigned successive driveletters. + + + +History: + + + +Release with dosemu.0.60.0 +Karsten Rucker (rucker@astro.uni-bonn.de) +April 1995 + + + +Additional remarks for mcd.c and aztcd.c +Werner Zimmermann (zimmerma@rz.fht-esslingen.de) +May 30, 1995 + + + +Release with dosemu-0.99.5 +Manuel Villegas Marin (manolo@espanet.com) +Support for up to 4 drives +December 4, 1998 + + + + + + diff --git a/src/doc/README/DANG-tech b/src/doc/README/DANG-tech new file mode 100644 index 0000000..87c72c8 --- /dev/null +++ b/src/doc/README/DANG-tech @@ -0,0 +1,337 @@ + +The DANG system + + +Description + + +The DANG compiler is a perl script which produces a linuxdoc-sgml document +from special comments embedded in the DOSEMU source code. This document is +intended to give an overview of the DOSEMU code for the benefit of hackers. + + + + + +Changes from last compiler release + + + + + + + + Recognizes 'maintainer:' information. + + + + + + '-i' (irritating) flag. + + + + + + Corrections to sgml special character protection. + + + + + + + + + + +Using DANG in your code + + +THE FOLLOWING MAY NOT SOUND VERY NICE, BUT IS INTENDED TO KEEP DANG AS IT +WAS DESIGNED TO BE - A GUIDE FOR NOVICES. + + + +DANG is not intended to be a vehicle for copyrights, gratuitous plugs, or +anything similar. It is intended to guide hackers through the maze of DOSEMU +source code. The comments should be short and informative. I will mercilessly +hack out comments which do not conform to this. If this damages anything, or +annoys anyone, so be it. + + + +I spent hours correcting the DOSEMU 0.63.1 source code because some +developers didn't follow the rules. They are very simple, and I'll remind you +of the below. (I am here to co-ordinate this, not do the work. The comments +are the responsibility of all of us.) + + + + + +DANG Markers + + +Some initial comments: + + + + + + All the text is passed through a text formatting system. This means +that text may not be formatted how you want it to be. If you insist on +having something formatted in a particular way, then it probably +shouldn't be in DANG. Try a README instead. Embedding sgml tags in +the comment will probably not work. + + + + + + Copyrights and long detailed discussions should not be in DANG. If +there is a good reason for including a copyright notice, then it should +be included ONCE, prefereably in the group summary file (see +'./src/doc/DANG/DANG_CONFIG' for the locations) + + + + + + If I say something must be done in a particular way, then it +MUST. There is no point in doing it differently because the script +will not work correctly (and changing the script won't help anyone +else.) In most cases the reason for a particular style is to help users +who are actually reading the SOURCE file. + + + + + + + + + DANG_BEGIN_MODULE / DANG_END_MODULE + + +These should enclose a piece of summary text at the start of a file. It should +explain the purpose of the file. Anything on the same line as DANG_BEGIN_MODULE +is ignored. A future version may use the rest of this line to provide a +user selected name for the module. There may be any number of lines of text. +To include information on the maintainer(s) of a module, put 'maintainer:' on +a blank line. The following lines should contain the maintainers details in +form: + + + + + +name ..... <user@address> + + + + + +There should only be one maintainer on a line. There can be no other lines +of description before DANG_END_MODULE. + + + +Example: + + +/* + * DANG_BEGIN_MODULE + * + * This is the goobledygook module. It provides BAR services. Currently there + * are no facilities for mixing a 'FOO' cocktail. The stubs are in 'mix_foo()'. + * + * Maintainer: + * Alistair MacDonald <alistair@slitesys.demon.co.uk> + * Foo Bar <foobar@inter.net.junk> + * + * DANG_END_MODULE + */ + + + + + + + + DANG_BEGIN_FUNCTION / DANG_END_FUNCTION + + +This is used to describe functions. Ideally this should only be complicated +or important functions as this avoids cluttering DANG with lots of +descriptions for trivial functions. Any text on the same line as +'DANG_BEGIN_FUNCTION' is taken as the name of the function. + + + +There are two optional sub-markers: 'description:' & 'arguments:' These can +be included at any point, and the default marker is a description. The +difference is that arguments are converted into a bulleted list. For this +reason, there should be only one argument (& it's description) per line. +I suggest that arguments should be in the form 'name - description of name'. + + + +Example: + + +/* + * DANG_BEGIN_FUNCTION mix_foo + * + * This function provides the foo mixing for the module. The 'foo' cocktail + * is used to wet the throat of busy hackers. + * + * arguments: + * soft_drink - the type of soft drink to use. + * ice_cream - the flavour of ice-cream required + * + * description: + * A number of hackers would prefer something a little stronger. + * + * DANG_END_FUNCTION + */ + + + + + + + + DANG_BEGIN_REMARK / DANG_END_REMARK + + +This is used to provide in-context comments within the code. They can be used +to describe some particularly interesting or complex code. It should be +borne in mind that this will be out of context in DANG, and that DANG is +intended for Novice DOSEMU hackers too ... + + + +Example: + + +/* + * DANG_BEGIN_REMARK + * + * We select the method of preparation of the cocktail, according to the type + * of cocktail being prepared. To do this we divide the cocktails up into : + * VERY_ALCHOHOLIC, MILDLY_ALCHOHOLIC & ALCOHOL_FREE + * + * DANG_END_REMARK + */ + + + + + + + + DANG_BEGIN_NEWIDEA / DANG_END_NEWIDEA + + +This is used to make a note of a new idea which we would like to have +implemented, or an alternative way of coding something. This can be used +as a scratch pad until the idea is in a state where someone can actually +begin coding. + + + +Example: + + +/* + * DANG_BEGIN_NEWIDEA + * + * Rather than hard coding the names of the mixer functions we could try + * using an array constructed at compile time - Alistair + * + * How to we get the list of functions ? - Foo + * + * DANG_END_NEWIDEA + */ + + + + + + + + DANG_FIXTHIS + + +This is for single line comments on an area which needs fixing. This should +say where the fix is required. This may become a multi-line comment in the +future. + + + +Example: + + +/* DANG_FIXTHIS The mix_foo() function should be written to replace the stub */ + + + + + + + + DANG_BEGIN_CHANGELOG / DANG_END_CHANGELOG + + +This is not currently used. It should be placed around the CHANGES section in +the code. It was intended to be used along with the DPR. + + + +* No Example * + + + + + + + + Usage + + +The current version of the configuration file resides in './src/doc/DANG'. The program +needs to be told where to find this using '-c <file>'. On my system I run it +in the './src/doc/DANG' directory using './DANG_c.pl -c DANG_CONFIG', but +its really up to you. + + + +The other options are '-v' for verbose mode and '-i'. +-v isn't really useful, but it does tell you what it is doing at all times. +-i is 'irritating mode' and makes comments in the DANG file about missing +items, such as no MODULE information, no FUNCTION information, etc. + + + +If the program is executed with a list of files as parameters then the files +will be processed, but no sgml output produced. This can be used for basic +debugging, as it warns you when there are missing BEGIN/END markers, etc. + + + + + + Future + + +I have vaguelly started writing the successor of this program. This will be a +more general program with a more flexible configuration system. Don't hold +your breath for an imminent release though. The new version will make it +easier to change the markers & document structure, as well as providing +feedback on errors. + + + + + + diff --git a/src/doc/README/HOGTHRESHOLD b/src/doc/README/HOGTHRESHOLD new file mode 100644 index 0000000..5b75419 --- /dev/null +++ b/src/doc/README/HOGTHRESHOLD @@ -0,0 +1,150 @@ + +Setting HogThreshold + + +Greetings DOSEMU fans, + + + +Hogthreshold is a value that you may modify in your DOSEMU.CONF file. It +is a measure of the "niceness" of Dosemu. That is to say, it attempts to +return to Linux while DOS is 'idling' so that DOSEMU does not hog all the +CPU cycles while waiting at the DOS prompt. + + + +Determining the optimal Hogthreshold value involves a little bit of magic +(but not so much really.) One way is to try different values and look at +the 'top' reading in another console. Setting the value too low may +mildly slow Dosemu performance. Setting the value too high will keep the +idling code from working. + + + +That said, a good basic value to try is "half of your Bogo-Mips value". +(The Bogo-Mips value is displayed when the kernel is booting, it's an +imaginary value somewhat related to CPU performance.) + + + +Setting the value to 0 will disable idling entirely. The default value is +10. + + + +This files is some kind of FAQ on how to use the 'HogThreshold' value in the +dosemu config file. + + + +In case you have more questions feel free to ask me ( +<andi@andiunx.m.isar.de>). + + + +Those of you who simply want to have DOSEMU running at highest possible speed +simply leave the value to zero, but if you are concerned about +DOSEMU eating too much CPU time it's worth playing with the HogThreshold value. + + + +Why do I need to set the HogThreshold value, why can't DOSEMU +just stop if it is waiting for a keystroke ? + + +The reason is the way how DOS and a lot of applications have implemented +`waiting for a keystroke'. + + + +It's most often done by something similar to the following code fragment : + + + + + +wait_for_key: + ; do something + mov ah,1 + int 0x16 ; check key status + jz wait_for_key ; jump if no key + ; found a key + mov ah,0 + int 0x16 ; get key + + + + + +This means that the application is busy waiting for the keystroke. + + + +What is a good value for HogThreshold to start with ? + + +On a 40 MHZ 486 start with a value of 10. +Increase this value if you to have your DOS application run faster, +decrease it if you think too much CPU time is used. + + + +It does not work on my machine. + + +You need to have at least dosemu0.53pl40 in order to have the +anti-hog code in effect. + + + +Why not simply use a very low value of "HogThreshold" ? +Do I really have to try an individual value of HogThreshold ? + + +This would slow down your DOS application. But why not, DOS is slow +anyway :-). + + + +How do I found out about CPU usage of DOSEMU ? + + +Simply use `top'. It displays cpu and memory usage. + + + + + + + +P.S. If you want to change the HogThreshold value during execution, +simply call + + + mov al,12h + mov bx,the_new_value + int e6h + + +This is what speed.com does. If you are interested, please take a look +at speed.c. + + + +Notes: If your application is unkind enough to do waits using an int16h +fcn 1h loop without calling the keyboard idle interrupt (int 28h), this +code is not going to help much. If someone runs into a program like this, +let me ( +<scottb@eecs.nwu.edu> +) know and I'll rewrite something into the +int16 bios. + + + + diff --git a/src/doc/README/Makefile b/src/doc/README/Makefile new file mode 100644 index 0000000..257522f --- /dev/null +++ b/src/doc/README/Makefile @@ -0,0 +1,44 @@ +top_builddir=../../.. +SUBDIR = doc/README +include $(top_builddir)/Makefile.conf + +DOCSOURCES := README.sgml +TXT = $(DOCSOURCES:.sgml=.txt) +DVI = $(DOCSOURCES:.sgml=.dvi) +HTML = $(DOCSOURCES:.sgml=.html) + +SECTIONS = config SECURITY sound lredir runasuser CDROM \ + X Windows mouse batch commands keymap dosnet Winnet + +TECHSECTIONS = config-tech port-io vif newnewkbd newkbd \ + HOGTHRESHOLD priv timing pentium DANG-tech mkfatimage16 doc sound-tech \ + dma pic dosdebug serial recover net cpuemu mfsnls + +all: $(HTML) + +txt: $(TXT) + +html: $(HTML) + +%.txt: %.sgml +# sgml2txt 2>/dev/null $< + $(srcdir)/../tools/doSgmlTools.pl -t $^ $@ + +#$(HTMLS): +# $(srcdir)../tools/doSgmlTools.pl -s $< + +%.html: %.sgml + $(srcdir)/../tools/doSgmlTools.pl -h $^ $@ + +README.sgml: $(srcdir)/header $(patsubst %,$(srcdir)/%,$(SECTIONS)) $(srcdir)/footer + cat $^ > $@ + +README-tech.sgml: $(srcdir)/header-tech $(patsubst %,$(srcdir)/%,$(TECHSECTIONS)) $(srcdir)/footer + cat $^ > $@ + +clean: + rm -f *.txt *.html *~ *.tex *.log *.aux *.toc *.dvi *.ps \ + README.sgml README-tech.sgml + +install: $(HTML) + cp $(HTML) $(top_builddir)/doc diff --git a/src/doc/README/Priv-usage b/src/doc/README/Priv-usage new file mode 100644 index 0000000..283b01d --- /dev/null +++ b/src/doc/README/Priv-usage @@ -0,0 +1,65 @@ +Catalog of usages of privileges. + +mhpdbg.c in mhp_close() for: +unlink(pipename_in) +unlink(pipename_out) + +reason: Because the pipe are typically created in a directory that has + restricted writership. +fix: Remove the privelege and change the directory. + +mhpdbg.c in mhp_init() for: +mkfifo(pipename_in ...) +mkfifo(pipename_out ...) +open(pipename_in ...) +open(pipename_out ..) +unlink pipename_in ...) +unlink pipename_out ...) + +Reason: Fix: see above. + +mhpdbgc.c in mhp_dump_to_file() for: +enter_priv_off +open(argv[3], O_WRONLY | O_CREAT | O_TRUNC, 00775); + +Reason: Fix: Being careful no fix needed. + +int.c in DOS_HELPER_0x53 for: +enter_priv_off() +system(...) +Reason: Fix: Being careful no fix needed. + +lpt.c in printer_open() for: +enter_priv_off() +lpt[prnum].file = fopen(lpt[prpnum].dev, "a") +lpt[prnum].file = fopen(lpt[prnum].dev, "a"); +Reason: Fix: Being careful no fix needed. + +config.c in open_terminal_pipe() for: +enter_priv_off() +terminal_fd = DOS_SYSCALL(open(path, O_RDWR)); +Reason: Fix: Being careful no fix needed. + +config.c in config_init() for: +enter_priv_off() +f=open(optarg, "r") +dbg_fd = fopen(config.debugout, "w"); +Reason: Fix: Being careful no fix needed. + +init.c in tmpdir_init() for: +enter_priv_off() +mkdir(tmpdir, S_IREAD | S_IWRITE | S_IEXEC) + +Reason: Be careful in creating a temporary directory for the mfs code. +Fix: None needed + +lexer.l in enter_include_file() for: +priv_on if ("c_system") +new_yyin = fopen( fname, "r" ); + +Reason: We should use all our priveleges to access the global config file. + But only user privelges to access the others. +Fix: Don't allow access to global config files when we don't have privelges. + +parser.y + diff --git a/src/doc/README/SECURITY b/src/doc/README/SECURITY new file mode 100644 index 0000000..a56c527 --- /dev/null +++ b/src/doc/README/SECURITY @@ -0,0 +1,72 @@ + +Security + + +This part of the document by Hans Lermen, +<lermen@fgan.de> +on Apr 6, 1997. + + + +These are the hints we give you, when running dosemu on a machine that is +(even temporary) connected to the internet or other machines, or that +otherwise allows 'foreign' people login to your machine. + + + + + + + + +Don't set the "s" bit, as DOSEMU can run in +lowfeature mode without the "s" bit set. If you want fullfeatures +for some of your users, it is recommended to use "sudo". Alternatively +you can just use the keyword `nosuidroot' in +/etc/dosemu.users to forbid some (or all) users execution of +a suid root running dosemu (they may use a non-suid root copy of +the binary though). +DOSEMU now drops its root privileges before booting; however +there may still be security problems in the initialization code, +and by making DOSEMU suid-root you can give users direct access to +resources they don't normally have access too, such as selected I/O +ports, hardware IRQs and hardware RAM. +For any direct hardware access you must always use the "-s" switch. + + + +If DOSEMU is invoked via "sudo" then it will automatically switch to +the user who invoked "sudo". An example /etc/sudoers entry is this: + +joeuser hostname=(root) NOPASSWD: /usr/local/bin/dosemu.bin + +If you use PASSWD instead of NOPASSWD then users need to type their +own passwords when sudo asks for it. The "dosemu" script can be +invoked using the "-s" option to automatically use sudo. + + + + + + Use proper file permissions to restrict access to a +suid root DOSEMU binary in addition to /etc/dosemu.users `nosuidroot'. +( double security is better ). + + + + + + NEVER let foreign users execute dosemu under root login !!! +(Starting with dosemu-0.66.1.4 this isn't necessary any more, +all functionality should also be available when running as user) + + + + + + + + diff --git a/src/doc/README/Windows b/src/doc/README/Windows new file mode 100644 index 0000000..4bd9017 --- /dev/null +++ b/src/doc/README/Windows @@ -0,0 +1,82 @@ + +Running Windows under DOSEMU + + +DOSEMU can run any 16bit Windows up to 3.1, and has limited +support for Windows 3.11. You should be able to install and run +these versions of Windows without any extra work. There are still +a few caveats however, and if you have some problems, read on. + + + +Mouse in Windows under DOSEMU + +In Windows, the mouse cursor is not always in sync with the native X +mouse cursor. This problem can be easily avoided by enabling mouse grab. +There also exist an + +alternative mouse driver + +specially written to work in ungrabbed mode under many emulators or +on a real machine. + + + + +Windows 3.x in SVGA modes + + +If you want to run Windows in SVGA mode, you can either use the +Trident drivers, or the patched SVGA drivers of Windows 3.11. + + + +The Trident drivers for Windows are in the files tvgaw31a.zip +and/or tvgaw31b.zip. Search www.winsite.com to get them. +Unpack the archive. In Windows setup, install the +Trident "800x600 256 color for 512K boards" driver. + + + +Windows 3.11 comes with SVGA drivers. You can also download +them: search www.winsite.com for svga.exe and install the drivers. +Then go to + +http://www.japheth.de/dwnload1.html + +and get the SVGAPatch tool. This tool causes the above drivers to +work with most of the video cards in a real DOS environment, and +makes them DOSEMU-compatible too. + + + + +VxD support + +By the time of writing this, DOSEMU does not have support +for Windows ring-0 device drivers (.vxd, .386). Fortunately, most of +Windows 3.1 drivers are ring-3 ones (.drv), so you can easily +install the Sound Blaster drivers, for instance. This is not the +case with Windows 3.11. Its network drivers are ring-0 ones, so +the native Winsock does not work. In order to get networking +operational, you need to get the Trumpet Winsock package. Refer +to chapter "Using Windows and Winsock" for more info on this. + + + + +DOS shell in Windows + +There is probably little use of a DOS shell under Windows under +DOSEMU... But for those who need it, here are some basic hints. +The DOS shell is supported by DOSEMU only if Windows is running in +Standard mode. The Enhanced mode DOS shell is currently unsupported. +Note however, that unlike in a real DOS environment, under DOSEMU +the DOS shell of the Windows in Standard mode allows you to run +protected mode applications (in the real DOS environment only the DOS shell +of the Enhanced mode allows this). + + + + + diff --git a/src/doc/README/Winnet b/src/doc/README/Winnet new file mode 100644 index 0000000..54809fb --- /dev/null +++ b/src/doc/README/Winnet @@ -0,0 +1,267 @@ + +Using Windows and Winsock + + +This is the Windows Net Howto by Frisoni Gloriano +<gfrisoni@hi-net.it> +on 15 may 1997 + + + +This document tries to describe how to run the Windows trumpet winsock over +the dosemu built-in packet driver, and then run all TCP/IP winsock-based +application (netscape, eudora, mirc, free agent .....) in windows environment. + + + +This is a very long step-by-step list of operation, but you can make +little scripts to do all very quickly ;-) + + + +In this example, I use the dosnet based packet driver. It is very powerful +because you can run a "Virtual net" between your dos-windows session and the +linux, and run tcp/application application without a real (hardware) net. + + + +LIST OF REQUIRED SOFTWARE + + + + + + + + The WINPKT.COM virtual packet driver, version 11.2 +I have found this little tsr in the Crynwr packet driver distribution +file PKTD11.ZIP + + + + + + The Trumpet Winsock 2.0 revision B winsock driver for windows. + + + + + + + + + + +STEP BY STEP OPERATION (LINUX SIDE) + + + + + + + Enable "dosnet" based dosemu packet driver: + + + + + + cd ./src/dosext/net/net + select_packet (Ask single or multi -> m) + + + + + + + Make the dosnet linux module: + + + + + + cd ./src/dosext/net/v-net + make + + + + + + + Make the new dosemu, with right packet driver support built-in: + + + + + + make + make install + + + + + + + Now you must load the dosnet module: + + + + + + insmod ./src/dosext/net/v-net/dosnet.o + + + + + + + Some linux-side network setup (activate device, routing). This stuff depends +from your environment, so I write an example setup. + + + +Here you configure a network interface dsn0 (the dosnet interface) with +the ip address 144.16.112.1 and add a route to this interface. + + + +This is a good example to make a "virtul network" from your dos/windows +environment and the linux environment. + + + + + + ifconfig dsn0 144.16.112.1 broadcast 144.16.112.255 netmask 255.255.255.0 + route add -net 144.16.112.0 dsn0 + + + + + + + + + + + + +STEP BY STEP OPERATION (DOS SIDE) + + +I suppose you know how to run windows in dosemu. You can read the +document if you need more information. Windows is not very stable, but works. + + + + + + + + + start dosemu. + + + + + + copy the winpkt.com driver and the trumpet winsock driver in some +dos directory. + + + + + start the winpkt TSR. (dosemu assign the 0x60 interrupt vector to the +built-in packet driver) + + + + + + winpkt 0x60 + + + + + + + edit the trumpet winsock setup file trumpwsk.ini. Here is an example of +how to setup this file: +(I think you can use less parameters, if you have the time to play with +this file. You can also setup this stuff from the winsock setup dialog-box). + + + + + + [Trumpet Winsock] + netmask=255.255.255.0 <-- class C netmask. + gateway=144.16.112.1 <-- address in the default gateway. + dns=www.xxx.yyy.zzz <-- You must use right value for the dns. + domain=hi-net.it + ip=144.16.112.10 <-- Windows address in the dosnet. + vector=60 <-- packet driver interrupt vector. + mtu=1500 + rwin=4096 + mss=1460 + rtomax=60 + ip-buffers=32 + slip-enabled=0 <--- disable slip + slip-port=2 + slip-baudrate=57600 + slip-handshake=1 + slip-compressed=0 + dial-option=1 + online-check=0 + inactivity-timeout=5 + slip-timeout=0 + slip-redial=0 + dial-parity=0 + font=Courier,9 + registration-name="" + registration-password="" + use-socks=0 + socks-host=0.0.0.0 + socks-port=1080 + socks-id= + socks-local1=0.0.0.0 0.0.0.0 + socks-local2=0.0.0.0 0.0.0.0 + socks-local3=0.0.0.0 0.0.0.0 + socks-local4=0.0.0.0 0.0.0.0 + ppp-enabled=0 <-------- disable ppp + ppp-usepap=0 + ppp-username="" + ppp-password="" + win-posn=42 220 867 686 -1 -1 -4 -4 1 + trace-options=16392 + + [default vars] + + + + + + + + Now you can run windows, startup trumpet winsock and ..... +enjoy with your windoze tcp/ip :-) + + + + + + + + +Gloriano Frisoni. +<gfrisoni@hi-net.it> + + + + + diff --git a/src/doc/README/X b/src/doc/README/X new file mode 100644 index 0000000..d4d4421 --- /dev/null +++ b/src/doc/README/X @@ -0,0 +1,433 @@ + +Using X + + +This chapter provides some hints and tips for using DOSEMU in X. + + + +Basic information + + +If you start dosemu in X it brings up its own window, +in which you can +also execute graphical programs such as games. To force text-only execution +of DOSEMU in an xterm or other terminal (konsole, gnome-terminal, and so on), +you need to run dosemu -t. + + + +Use dosemu -w to start DOSEMU in fullscreen mode. +When running DOSEMU, +you can flip between fullscreen and windowed mode by pressing +<Ctrl><Alt><F>. +The graphics window is resizeable. + + + +Some DOS applications want precise mouse control that is only possible +if the mouse cursor is trapped in the DOSEMU window. To enable this you +need to grab the mouse by pressing <Ctrl><Alt><Home>. +Similarly, you can grab the keyboard by pressing +<Ctrl><Alt><K>, or +<Ctrl><Alt><Shift><K> if your +window manager already grabs <Ctrl><Alt><K>. +After you grab the keyboard +all key combinations (including <Alt><Tab> and so on) +are passed to DOSEMU. +In fullscreen mode the mouse and keyboard are both automatically grabbed. + + + +Use <Ctrl><Alt><Pause> to pause and unpause +the DOSEMU session, which is useful if you want it to sit silently +in the background when it is eating too much CPU time. +Press <Ctrl><Alt><PgDn> or click the close button +of the window to exit DOSEMU. + + + +DOSEMU uses bitmapped internal fonts by default, so it can accurately +simulate a real VGA card text mode. It is also possible to use X fonts. +The advantages of these is that they may be easier on the eyes, and +are faster, in particular if you use DOSEMU remotely. Any native DOS font +setting utilities do not work, however. To set an X font use the +provided xmode.com utility, using + +xmode -font vga + +at the DOS prompt or + +$_X_font = "vga" + +in dosemu.conf. The provided fonts are vga, vga8x19, vga11x19, vga10x24, +vga12x30, vga-cp866, and vga10x20-cp866. + + + +If the mouse is not grabbed, then you can copy and paste text if the DOSEMU +window is in text mode. This uses the standard X mechanism: select by +dragging the left mouse button, and paste by pressing the middle mouse +button. + + + + + More detailed information, hints and tips + + +What you might take care of: + + + + + + + + +If backspace and delete do not work, you can try + 'xmodmap -e "keycode 22 = 0xff08"' to get use of your backspace key, and + + + + + + 'xmodmap -e "keycode 107 = 0xffff"' to get use of your delete key. + + + + + + Make sure DOSEMU has X support compiled in. The configure script + complains loudly if it does not find X development libraries. + + + + + + There are some X-related configuration options for dosemu.conf. +See the file itself for details. + + + + + + Keyboard support of course depends on your X keyboard mappings (xmodmap). +If certain keys don't work (like Pause, Backspace,...), it *might* be +because you haven't defined them in your xmodmap, or defined to something +other than DOSEMU expects. + + + + + using the provided icon (dosemu.xpm): + + + + + + + + + you need the xpm (pixmaps) package. If you're not sure, look for +a file like /usr/X11R6/lib/libXpm.so.* + + + + + + you also need a window manager which supports pixmaps. Fvwm is fine, +but I can't tell you about others. Twm probaby won't do. + + + + + + copy dosemu.xpm to where you usually keep your pixmap (not bitmap!) +files (perhaps /usr/share/pixmaps) + + + + +tell your window manager to use it. For fvwm, add the following +line to your fvwmrc file: + + + + + + + Icon "xdosemu" dosemu.xpm + + + + + +This assumes you have defined a PixmapPath. Otherwise, specify the +entire pathname. + + + + + + note that if you set a different icon name than "xdosemu" in your +dosemu.conf, you will also have to change the fvwmrc entry. + + + + + + restart the window manager. There's usually an option in the +root menu to do so. + + + + + + + + +Now you should see the icon whenever you iconify xdosemu. + + + +Note that the xdosemu program itself does not include the icon - that's +why you have to tell the window manager. I chose to do it this way +to avoid xdosemu requiring the Xpm library. + + + + + + If anything else does not work as expected, don't panic :-) +Remember the thing is still under construction. +However, if you think it's a real bug, please tell me. + + + + + + + + + +The VGA Emulator + + +In X, a VGA card is emulated. The same happens if you use the SDL library +using dosemu -S. This emulation (vgaemu) enables +X to run graphics modes. + + + +Some features: + + + + + Video memory. 1 Mb is allocated. It is mapped with mmap() in the VGA +memory region of DOSEMU (0xa00000-0xbfffff) to support bank switching. +This is very i386-Linux specific, don't be surprised if it doesn't work +under NetBSD or another Linux flavour (Alpha/Sparc/MIPS/etc). + + + + + + The DAC (Digital to Analog Converter). The DAC is completely emulated, +except for the pelmask. This is not difficult to implement, but it is +terribly slow because a change in the pelmask requires a complete redraw +of the screen. Fortunately, the pelmask changes aren't used often so +nobody will notice ;-) + + + + + + The attribute controller is emulated. + + + + + + The emulator emulates a basic Trident TVGA8900C graphics card. + All standard VGA modes are emulated, most VGA hardware features (mode-X, + 320x240 and so on), some Trident extensions, and on + top of that many high-resolution VESA 2.0 modes, that were not present + in the original Trident card. + Some (very few) programs, such as Fast Tracker, play intimately with the + VGA hardware and may not work. As vgaemu improves these problems should + disappear. + + + + + + Nearly full VESA 2.0 support. + + + + + + A VESA compatible video BIOS is mapped at 0xc00000. It is small because + it only contains some glue code and the BIOS fonts (8x8, 8x14, 8x16). + + + + + + support for hi- and true-color modes (using Trident SVGA mode numbers +and bank switching) + + + + + + Support for mode-X type graphics modes (non-chain4 modes as used by e.g. DOOM) + + + + + + gamma correction for graphics modes + + + + + + video memory size is configurable via dosemu.conf + + + + + + initial graphics window size is configurable + + + + + +The current hi- (16bpp) and true (24/32bpp) color support does +not allow resizing of the graphics window and gamma correction +is ignored. + + + + + +As the typical graphics mode with 320x200x8 will be used often +with large scalings and modern graphics boards are pretty fast, +Steffen Winterfeldt added something to eat up your CPU time: you can turn on +the bilinear interpolation. It greatly improves the display +quality (but is rather slow as I haven't had time yet to implement +an optimized version - it's plain C for now). +If the bilinear filter is too slow, you might instead try the linear +filter which interpolates only horizontally. + + + +Note that (bi)linear filtering is not available on all +VGA/X display combinations. The standard drawing routines +are used instead in such cases. + + + +If a VGA mode is not supported on your current X display, the graphics +screen will just remain black. Note that this does not mean +that DOSEMU has crashed. + + + +The only unsupported VBE function is VGA state save/restore. But this +functionality is rarely used and its lack should not cause +too much problems. + + + +VBE allows you to use the horizontal and vertical scrolling +function even in text modes. This feature is not implemented. + + + +If you think it causes problems, the linear frame buffer (LFB) +can be turned of via dosemu.conf as well as the protected mode +interface. Note, however, that LFB modes are faster than +banked modes, even in DOSEMU. + + + +The default VBE mode list defines a lot of medium resolution +modes suitable for games (like Duke3D). You can still +create your own modes via dosemu.conf. Note that you +cannot define the same mode twice; the second (and all +subsequent) definitions will be ignored. + + + +Modes that are defined but cannot be supported due +to lack of video memory or because they cannot be +displayed on your X display, are marked as unsupported +in the VBE mode list (but are still in it). + + + +The current interface between VGAEmu and X will try to update +all invalid video pages at a time. This may, particularly +in hi-res VBE/SVGA modes, considerably disturb DOSEMU's signal +handling. That cannot be helped for the moment, but will +be addressed soon (by running an extra update thread). + + + +If you really think that this is the cause of your problem, you might +try to play with veut.max_max_len in env/video/render.c. +This variable limits the amount of video memory that is updated +during one timer interrupt. This way you can dramatically +reduce the load of screen updates, but at the same rate reduce your +display quality. + + + +Gamma correction works in both 4 and 8 bit modes. It must be specified +as a float value, e.g. $_X_gamma=(1.0). Higher values +give brighter graphics, lower make them darker. Reasonable +values are within a range of 0.5 ... 2.0. + + + +You can specify the video memory size that the VGA emulator +should use in dosemu.conf. The value will be rounded up +to the nearest 256 kbyte boundary. You should stick to typical +values like 1024, 2048 or 4096 as not to confuse DOS applications. +Note that whatever value you give, 4 bit modes are only +supported up to a size of 800x600. + + + +You can influence the initial size of the graphics window in various +ways. Normally it will have the same size (in pixel) as the VGA graphics +mode, except for mode 0x13 (320x200, 256 colors), which will be scaled by +the value of mode13fact (defaults to 2). +Alternatively, you can directly specify a window size in dosemu.conf via +$_X_winsize. You can still resize the window later. + + + +The config option $_X_fixed_aspect allows you to fix the aspect ratio +of the graphics window while resizing it. Alternatively, $_X_aspect_43 +ties the aspect ratio to a value of 4:3. The idea behind this is that, whatever +the actual resolution of a graphics mode is in DOS, it is displayed on +a 4:3 monitor. This way you won't run into problems with modes such +as 640x200 (or even 320x200) which would look somewhat distorted otherwise. + + + +For planar modes (for instance, most 16 colour modes, but also certain +256-colour modes: mode-X), vgaemu has to +switch to partial cpu emulation. This can be slow, but expect it to +improve over time. + + + + + diff --git a/src/doc/README/batch b/src/doc/README/batch new file mode 100644 index 0000000..06fa0d5 --- /dev/null +++ b/src/doc/README/batch @@ -0,0 +1,209 @@ + +Running a DOS application directly from Unix shell + + +This part of the document was written by Hans +<lermen@fgan.de>. + + + +This chapter deals with starting DOS commands directly from Linux. +You can use this information to set up icons or menu items in X. +Using the keystroke, input and output redirection facilities you can +use DOS commands in shell scripts, cron jobs, web services, and so on. + + + +Using <command>unix -e</command> in autoexec.bat + + +The default autoexec.bat file has a statement unix -e at the end. This +command executes the DOS program or command that was specified on the +dosemu command line. + + + +For example: + +dosemu "/home/clarence/games/commander keen/keen1.exe" + +will automatically: + + +Lredir "/" if the specified program is not available from an +already-redirected drive, + + +"cd" to the correct directory, + + +execute the program automagically, + + +and quit DOSEMU when finished, all without any typing in DOS. + + +Using "-E" at the command line causes DOSEMU to continue after the +DOS program finishes. + + + +You can also specify a DOS command, such as "dir". A combination with +the -dumb command-line option is useful if you want to retrieve the +output of the DOS command, such as + +dosemu -dumb dir > listing + +In this case (using -dumb, but not -E) all the startup messages that +DOSEMU and DOS generate are suppressed and you only get the output of "dir". +The output file contains DOS end-of-line markers (CRLF). + + + + + + +Using the keystroke facility. + + +Make use of the -input command-line option, such as + + +dosemu -input 'dir > C:\\garbage\rexitemu\r' + + +The '...' will be 'typed in' by DOSEMU exactly as if you had them +typed at the keyboard. The advantage of this technique is, that all +DOS applications will accept them, even interactive ones. A '\' is +interpreted as in C and leads in ESC-codes. Here is a list of the +current implemented ones: + + + + + +\r Carriage return == <ENTER> +\n LF +\t tab +\b backspace +\f formfeed +\a bell +\v vertical tab + + +\^x <Ctrl>x, where X is one of the usual C,M,L,[ ... + (e.g.: \^[ == <Ctrl>[ == ESC ) + +\Ax <Alt>x, hence \Ad means <Alt>d + +\Fn; Function key Fn. Note that the trailing ';' is needed. + (e.g.: \F10; == F10 ) + +\Pn; Set the virtual typematic rate, thats the speed for + autotyping in. It is given in unix timer ticks to wait + between two strokes. A value of 7 for example leads to + a rate of 100/7=14 cps. + +\pn; Before typing the next stroke wait n unix ticks. + This is useful, when the DOS-application flushes the + keyboard buffer on startup. Your strokes would be discarded, + if you don't wait. + + + + + + + + +Using an input file + + + + + + + Make a file "FILE" containing all keystrokes you need to boot DOSEMU +and to start your dos-application, ... and don't forget to have CR +(literal ^M) for 'ENTER'. FILE may look like this (as on my machine): + + + + + +2^Mdir > C:\garbage^Mexitemu^M + +which could choose point 2 of the boot menu, execute 'dir' with output +to 'garbage', and terminate DOSEMU, where the ^M stands for CR. + + + + +and execute DOSEMU like this: + + +dosemu -dumb < FILE + + + + + + + + + + +In bash you can also use + +echo -e 'dir \gt; c:\\garbage\rexitemu\r' | dosemu -dumb + +or, when your dos-app does only normal printout (text), then you may +even do this + +echo -e 'dir\rexitemu\r' | dosemu -dumb > FILE.out + + + + + +FILE.out then contains the output from the DOS application, but (unlike the +unix -e technique, merged with all startup messages. + + + +You may elaborate this technique by writing a script, which gets the +dos-command to execute from the commandline and generate 'FILE' for you. + + + + + +Running DOSEMU within a cron job + + +When you try to use one of the above to start DOSEMU out of a crontab, +then you have to asure, that the process has a proper environment set up +( especially the TERM and/or TERMCAP variable ). + + + +Normally cron would setup TERM=dumb, this is fine because DOSEMU recognizes +it and internally sets it's own TERMCAP entry and TERM to `dosemu-none'. +You may also configure your video to + + + dosemu ... -dumb + + +or have a TERM=none to force the same setting. +In all other crontab run cases you may get nasty error messages either +from DOSEMU or from Slang. + + + + + + diff --git a/src/doc/README/commands b/src/doc/README/commands new file mode 100644 index 0000000..8956a55 --- /dev/null +++ b/src/doc/README/commands @@ -0,0 +1,281 @@ + +Commands & Utilities + + +These are some utitlies to assist you in using Dosemu. + + + +Programs + + + + + +uchdir.com + + + change the Unix directory for Dosemu (use chdir(2)) + + + +dosdbg.com + + + change the debug setting from within Dosemu + + + + + + dosdbg -- show current state + + + + + + dosdbg <string> + + + + + + dosdbg help -- show usage information + + + + + + + + +eject.com + + + eject CD-ROM from drive + + + +emumouse.com + + + fine tune internal mousedriver of Dosemu + + + + + + emumouse h -- display help screen + + + + + + + + +exitemu.com + + + terminate Dosemu + + + +ugetcwd.com + + + get the Unix directory for Dosemu (use getcwd(2)) + + + +isemu.com + + + detects Dosemu version and returns greater 0 if running under +Dosemu + + + +lancheck.exe + + + ??? + + + +lredir.com + + + redirect Linux directory to Dosemu + + + + + + lredir -- show current redirections + + + + + + lredir D: LINUX\FS\tmp -- redirects /tmp to drive D: + + + + + + lredir help -- display help screen + + + + + + + + +speed.com + + + set cpu usage (HogThreshold) from inside Dosemu + + + +system.com + + + interface to system(2)... + + + +unix.com + + + execute Unix commands from Dosemu + + + + + + unix -- display help screen + + + + + + unix ls -al -- list current Linux directory + + + + + + caution! try "unix" and read the help screen + + + + + + + + +cmdline.exe + + + Read /proc/self/cmdline and put strings such as "var=xxx" +found on the commandline into the DOS enviroment. +Example having this as the dosemu commandine: + + + dosemu.bin "autoexec=echo This is a test..." + + +then doing + + + + C:\cmdline < D:\proc\self\cmdline + %autoexec% + + +would display "This is a test..." on the DOS-Terminal + + + +vgaoff.com + + + disable vga option + + + +vgaon.com + + + enable vga option + + + +xmode.exe + + + set special X parameter when running as xdos + + + + + + + + + +Drivers + + +These are useful drivers for Dosemu + + + + + + +cdrom.sys + + + allow direct access to CD-ROM drives from Dosemu + + + +ems.sys + + + enable EMM in Dosemu + + + +emufs.sys + + + redirect Unix directory to Dosemu + + + +aspi.sys + + + ASPI conform SCSI driver + + + +fossil.com + + + FOSSIL serial driver (TSR) + + + + + + + + + + diff --git a/src/doc/README/config b/src/doc/README/config new file mode 100644 index 0000000..b5648ac --- /dev/null +++ b/src/doc/README/config @@ -0,0 +1,1452 @@ + +Runtime Configuration Options + + +This section of the document by Hans, +<lermen@fgan.de>. Last +updated on May 4, 2007, by Bart Oldeman. + + + +Most of DOSEMU configuration is done during runtime and per default it +can use the system wide configuration file dosemu.conf (which is often +situated in /etc or /etc/dosemu) optionally followed by the users +~/.dosemurc and additional configurations statements on the commandline +(-I option). If /etc/dosemu.users exists then dosemu.conf is searched for +in /etc and otherwise in /etc/dosemu (or an alternative sysconfdir +compiletime-setting). + + + +The default dosemu.conf and ~/.dosemurc have all settings commented +out for documentation purposes; the commented out values are the +ones that DOSEMU uses by default. Note that a non-suid type of installation +does not need the dosemu.users and dosemu.conf files, and the main +per-user configuration file is $HOME/.dosemurc. +However, for security reasons, a suid-root installation will not +run without dosemu.users, and in that case certain dosemu.conf settings +are ignored by ~/.dosemurc. + + + +In fact dosemu.conf and ~/.dosemurc (which have identical syntax) +are included by the systemwide configuration script global.conf which, +by default, is built into the DOSEMU binary. As a normal user +you won't ever think on editing this, only dosemu.conf and your personal +~/.dosemurc. The syntax of global.conf is described in detail in +README-tech.txt, so this is skipped here. However, the option -I string too +uses the same syntax as global.conf, hence, if you are doing some special +stuff (after you got familar with DOSEMU) you may need to have a look there. + + + +The first file expected (and interpreted) before any other configuration +(such as global.conf, dosemu.conf and ~/.dosemurc) is /etc/dosemu.users +or /etc/dosemu/dosemu.users. As mentioned above, this file is entirely +optional for non-suid-root (default) installations. +Within this file the general permissions are set: + + + + + + + + + which users are allowed to use DOSEMU. + + + + + + which users are allowed to use DOSEMU suid root. + + + + + + which users are allowed to have a private lib dir. + + + + + + what kind of access class the user belongs to. + + + + + + what special configuration stuff the users needs + + + + + + + + +and further more: + + + + + + whether the lib dir (DOSEMU_LIB_DIR) resides elsewhere. + + + + + + setting the loglevel. + + + + + + + + +Except for lines starting with `xxx=' (explanation below), +each line in dosemu.user corresponds to exactly one valid user count, +the special user `all' means any user not mentioned earlier. Format: + + + [ <login> | all ] [ confvar [ confvar [ ... ] ] ] + + + + + +The below example is from etc/dosemu.users.secure, which you may copy +to /etc/dosemu.users. + + + root c_all # root is allowed to do all weird things + nobody nosuidroot guest # variable 'guest' is checked in global.conf + # to allow only DEXE execution + guest nosuidroot guest # login guest treated as `nobody' + myfriend c_all unrestricted private_setup + myboss nosuidroot restricted private_setup + all nosuidroot restricted # all other users have normal user restrictions + + +Note that the above `restricted' is checked in global.conf and will +disable all secure relevant feature. Setting `guest' will force +setting `restricted' too. + + + +The use of `nosuidroot' will force a suid root dosemu binary to exit, +the user may however use a non-suid root copy of the binary. +For more information on this look at README-tech, +chapter 11.1 `Privileges and Running as User' + + + +Giving the keyword `private_setup' to a user means he/she can have a private +DOSEMU lib under $HOME/.dosemu/lib. If this directory is existing, DOSEMU +will expect all normally under DOSEMU_LIB_DIR within that directory. +As this would be a security risk, it only will be allowed, if the used DOSEMU +binary is non-suid-root. If you really trust a user you may additionally give +the keyword `unrestricted', which will allow this user to execute a suid-root +binary even on a private lib directory (though, be aware). + + + +In addition, dosemu.users can be used to define some global settings, which +must be known before any other file is accessed, such as: + + + default_lib_dir= /opt/dosemu # replaces DOSEMU_LIB_DIR + log_level= 2 # highest log level + + + + + +With `default_lib_dir=' you may move DOSEMU_LIB_DIR elsewhere, this mostly +is interesting for distributors, who want it elsewhere but won't patch the +DOSEMU source just for this purpose. But note, the dosemu supplied scripts +and helpers may need some adaption too in order to fit your new directory. + + + +The `log_level=' can be 0 (never log) or 1 (log only errors) or 2 (log all) +and controls the ammount written to the systems log facility (notice). +This keyword replaces the former /etc/dosemu.loglevel file, which now is +obsolete. + + + +Nevertheless, for a first try of DOSEMU you may prefer etc/dosemu.users.easy, +which just contains + + + root c_all + all c_all + + +to allow everybody all weird things. For more details on security issues +have a look at chapter 3. + + + +After the file dosemu.users, the file dosemu.conf (via global.conf, which +may be built-in) is interpreted, +and only during global.conf parsing the access to all configuration options is +allowed. dosemu.conf normally lives in the same directory as dosemu.users, +for instance /etc/dosemu or /etc (that is, sysconfdir in compiletime-settings). + +Your personal ~/.dosemurc is included directly after dosemu.conf, +but has less access rights (in fact the lowest level), all variables you +define within ~/.dosemurc transparently are prefixed with `dosemu_' such +that the normal namespace cannot be polluted (and a hacker cannot overwrite +security relevant enviroment variables). Within global.conf only those +~/.dosemurc created variables, that are needed are taken over and may +overwrite those defined in dosemu.conf. + + + +The dosemu.conf (global.conf) may check for the configuration variables, +that are set in dosemu.users and optionaly include further configuration +files. But once dosemu.conf (global.conf) has finished interpretation, +the access to secure relevant configurations is (class-wise) restricted while +the following interpretation of (old) .dosrc and -I statements. + + + +For more details on security settings/issues look at README-tech.txt, for +now (using DOSEMU the first time) you should need only the below description +of dosemu.conf (~/.dosemurc) + + + +Format of dosemu.conf and ~/.dosemurc + + +All settings are variables, and have the form of + + + + + + $_xxx = (n) + + +or + + + $_zzz = "s" + + + + + +where `n' is a numerical or boolean value and `s' is a string. +Note that the brackets are important, else the parser will not decide +for a number expression. For numbers you may have complete expressions +( such as (2*1024) ) and strings may be concatenated such as + + + + + + $_zzz = "This is a string containing '", '"', "' (quotes)" + + + + + +Hence a comma separated list of strings is concatenated. +All these settings are also environment variables. You can override them +by prefixing with dosemu_, e.g. + +dosemu__X_title="DOS was in the BOX" dosemu + +temporarily changes the X window title. + + + +Disks, boot directories and floppies + + +The parameter settings are tailored to fit the recommended +usage of disk and floppy access. There are other methods too, but for these +you have to look at README-tech.txt (and you may need to modify global.conf). +We strongly recommend that you use the proposed techique. Here the normal +setup: + + + + + +# List of hdimages or boot directories under +# ~/.dosemu, the system config directory (/etc/dosemu by default), or +# syshdimagedir (/var/lib/dosemu by default) assigned in this order +# such as "hdimage_c directory_d hdimage_e" +# Absolute pathnames are also allowed. + $_hdimage = "drives/*" + $_floppy_a ="threeinch" # or "fiveinch" or empty, if not existing + $_floppy_b = "" # dito for B: + $_cdrom = "/dev/cdrom" # list of CDROM devices + + + + + +A hdimage is +a file containing a virtual image of a DOS-FAT filesystem. Once you have +booted it, you (or autoexec.bat) can use `lredir' to access any directory +in your Linux tree as DOS drive (a -t msdos mounted too). +Look at chapter 5 (Using Lredir) for more details. +If you want to create your own hdimage use "mkfatimage16" (see the manual +page). To make it bootable you can make it, say, drive F:, and use +"SYS F:" at the DOS prompt. +The DOSEMU-HOWTO explains how to manipulate it using mtools. + + + +You can also specify a Linux directory containing all what you want to have +under your DOS C:. Copy your IO.SYS, MSDOS.SYS or equivalent to that +directory (e.g. DOSEMU_LIB_DIR/bootdir), set + + + $_hdimage = "bootdir" + + +and up it goes. Alternatively you can specify +an absolute path such as "/dos" or "/home/username/dosemu/freedos". +DOSEMU makes a lredir'ed drive out of it and can boot from it. +You can edit the config.sys and the autoexec.bat within this directory +before you start dosemu. +Further more, you may have a more sohisticated setup. Given you want to run +the same DOS drive as you normal have when booting into native DOS, +then you just mount you DOS partition under Linux (say to /dos) and +put links to its subdirectories into the boot dir. This way you can decide +which files/directories have to be visible under DOSEMU and which have to be +different. Here a small and not complete example bootdir setup: + + + config.sys + autoexec.bat + command.com -> /dos/command.com + io.sys -> /dos/io.sys + msdos.sys -> /dos/msdos.sys + dos -> /dos/dos + bc -> /dos/bc + windows -> /dos/windows + + + + + +As a further enhancement of your drives setup you may even use the following +strategy: given you have the following directory structure in one the +directories where the $_hdimage setting applies (this is done by default +in ~/.dosemu and in /etc/dosemu) + + + drives/c + drives/d + + +where c and d are symbolic +links to appropriate DOS useable directories, then the following single +statement + + + $_hdimage = "drives/*" + + +will assign all these directories to drive C: and D: respectively. +Note, however, that the order in which the directories under drives/* +are assigned comes from the order given by /bin/ls. Hence the folling + + + drives/x + drives/a + + +will assign C: to drives/a and D: to drives/x, keep that in mind. + + + +In some rare cases you may have problems accessing Lredir'ed drives +(especially when your DOS application refuses to run on a 'network drive'), +For this to overcome you may need to use so-called `partition access', +use a floppy, or a special-purpose hdimage. The odd +with partition access is, that you never should have +those partition +mounted in the Linux file system at the same time as you use it in DOSEMU +(which is quite uncomfortable and dangerous on a multitasking OS such as +Linux ). Though DOSEMU checks for mounted partitions, there may be races +that are not caught. In addition, when your DOSEMU crashes, it may leave +some FAT sectors unflushed to the disk, hence destroying the partition. +Anyway, if you think you need it, you must have r/w access to the partition +in /dev, and you have to `assign' real DOS partitions as follows: + + + + + + $_hdimage = "hdimage.first /dev/hda1 /dev/sdc4:ro" + + + + + +The above will have `hdimage.first' as booted drive C:, /dev/hda1 as D: +(read/write) and /dev/sdc4 as E: (readonly). You may have any kind of order +within `$_hdimage', hence + + + + + + $_hdimage = "/dev/hda1 hdimage.first /dev/sdc4:ro" + + + + + +would have /dev/hda1 as booted drive C:. Note that the access to the +/dev/* devices must be exclusive (no other process should use it) +except for `:ro'. + + + + + +Controlling amount of debug output + + +DOSEMU will help you find problems, when you enable its debug messages. +These will go into the file, that you defined via the `-o file' or `-O' +commandline option (the latter prints to stderr). If you do not specify +any -O or -o switch, then the log output will be written to ~/.dosemu/boot.log. +You can preset the kind of debug output via + + + + + + $_debug = "-a" + + +where the string contains all you normally may pass to the `-D' commandline +option (look at the man page for details). + + + + + +Basic emulation settings + + +Whether a numeric processor should be shown to the DOS space + + + $_mathco = (on) + + + + + +Which type of CPU should be emulated (NOTE: this is not the one you +are running on, but your setting may not exeed the capabilities of +the running CPU). Valid values are: 80[345]86 + + + $_cpu = (80386) + + + + + +To let DOSEMU use the Pentium cycle counter (if availabe) to do better timing +use the below + + + + + + $_rdtsc = (off) # or on + + +Note that the RDTSC can be unreliable on SMP systems, and in combination with +power management (APM/ACPI). + + + +For the above `rdtsc' feature DOSEMU needs to know the exact CPU clock, +it normally calibrates it itself, but is you encounter a wrong mesurement +you may overide it such as + + + $_cpuspeed = (166.666) # 0 = let DOSEMU calibrate + + + + + +If you have a PCI board you may allow DOSEMU to access the PCI +configuration space by defining the below + + + $_pci = (auto) # or auto, or off + + + + + +NOTE: `$_pci' can not be overwritten by ~/.dosemurc. +The "on" setting can be very dangerous because it gives DOSEMU complete +write access; you need to edit dosemu.users to enable it. +In console graphics mode, some video card BIOSes need some PCI +configuration space access, +which is enabled by the default (auto) setting. This setting is far more +restricted and less dangerous. + + + +Starting with dosemu-1.0 there is a flexible way to handle the mapping +strategy used by DOSEMU, which is needed by video emulation, EMS, +DPMI and XMS support and other things to map a given page of memory to the +required virtual DOS address space. + + + +Normally DOSEMU will detect the proper mapping driver for the kernel you are +using, however, in some cases you may want to define it explicitely to +overcome eventual problems. For this you can specify + + + $_mapping= "mapfile" + + +to force the use of the driver, which uses a temporary file. + + + +If you are using a kernel above 2.3.40, you may use + + + $_mapping= "mapshm" + +which uses a POSIX shared memory object (the default) or + + $_mapping= "mapashm" + +which uses anonymous shared memory (in case the above gives problems). + + + +Note, that in case of `mapfile' and `mapshm' the size of the file or the +segment depend on how much memory you configured for XMS, EMS and DPMI +(see below). +You should take care yourself that you have enough diskspace +for 'mapfile'. For 'mapshm' the tmpfs mount option 'size=nbytes' controls +the amount of space; by default it is half of the (real machine) memory. + + + +Defining the memory layout, which DOS should see: + + + $_xms = (8192) # in Kbyte + $_ems = (2048) # in Kbyte + $_ems_frame = (0xe400) + $_dpmi = (0x5000) # in Kbyte + $_dosmem = (640) # in Kbyte, < 640 + + +Note that (other as in native DOS) each piece of mem is separate, hence +DOS perhaps will show other values for 'extended' memory. To use EMS +memory you must load the supplied ems.sys device driver. For XMS memory +you must either use a DOS XMS driver such as himem.sys, himem.exe, +fdxms.sys, or fdxxms.sys, or the internal XMS driver via ems.sys. + + + +If you want mixed operation on the filesystem, from which you +boot DOSEMU (native and via DOSEMU), it may be necessary to have two +separate sets of config.sys and system.ini. DOSEMU can +fake a different file extension, so DOS will get other files when +running under DOSEMU. Faking autoexec.bat cannot happen in a reliable +fashion, so if you would like to use an autoexec.bat replacement then +just use the SHELL command in config.XXX, like this: + + +SHELL=COMMAND.COM /P /K AUTOEMU.BAT + + + + + + $_emusys = "" # empty or 3 char., config.sys -> config.XXX + $_emuini = "" # empty or 3 char., system.ini -> system.XXX + + + + + +As you would realize at the first glance: DOS will not have the +the CPU for its own. But how much it gets from Linux, depends on the setting +of `hogthreshold'. +The HogThreshold value determines how nice Dosemu will be about +giving other Linux processes a chance to run. + + + + + + $_hogthreshold = (1) # 0 == all CPU power to DOSEMU + # 1 == max power for Linux + # >1 the higher, the faster DOSEMU will be + + + + + + + +Code page and character set + + +To select the character set and code page for use with DOSEMU you have + + + $_external_char_set = "XXX" + +where XXX is one of + +"cp437", "cp737", "cp773", "cp775", "cp850", "cp852", "cp857", "cp860", +"cp861", "cp862", "cp863", "cp864", "cp865", "cp866", "cp869", "cp874", +"cp1125", "cp1251" +"iso8859-1", "iso8859-2", "iso8859-3", "iso8859-4", "iso8859-5", "iso8859-6", +"iso8859-7", "iso8859-8", "iso8859_9", "iso8859-14", "iso8859-15", "koi8-r" +"koi8-u", "koi8-ru", "utf8" + + + + +The external character set is used to: + + + compute the unicode values of characters coming in from the terminal + + + compute the character set index of unicode characters output to + a terminal display screen. + + +The default is to use "", which denotes the current locale, and is usually +the right setting. + + + +If you set a DOS external character set, then it is to the user +to load a proper DOS font (cp437.f16, cp850.f16 or cp852.f16 on the +console). + + + + + $_internal_char_set = "XXX" + +where XXX is one of: + +"cp437", "cp737", "cp773", "cp775", "cp850", "cp852", "cp857", "cp860", +"cp861", "cp862", "cp863", "cp864", "cp865", "cp866", "cp869", "cp874" +"cp895", "cp1125", "cp1251", "bg-mik" + + + + +The internal character set is used to: + + + compute the unicode value of characters of video memory, + when using DOSEMU in a terminal or using X with a unicode + font. + + + compute the character set index of unicode characters + returned by bios keyboard translation services. + + + compute the unicode values of characters in file names. + + + + + + + +Terminals + + +This section applies whenever you run DOSEMU remotely, in an xterm or +on the Linux console without graphics. +Color terminal support is now built into DOSEMU. Skip this section for +now to use terminal defaults, until you get DOSEMU to work. + + + $_term_color = (on) # terminal with color support + $_term_updfreq = (4) # time between refreshs (units: 20 == 1 second) + $_escchar = (30) # 30 == Ctrl-^, special-sequence prefix + + +`term_updfreq' is a number indicating the frequency of terminal updates of +the screen. The smaller the number, the more frequent. A value of 20 gives +a frequency of about one per second, which is very slow. +`escchar' is a number (ascii code below 32) that specifies the control +character used as a prefix character for sending alt, shift, ctrl, and +function keycodes. The default value is 30 which is Ctrl-^. So, for +example, + + + F1 is 'Ctrl-^1', Alt-F7 is 'Ctrl-^s Ctrl-^7'. + For online help, press 'Ctrl-^h' or 'Ctrl-^?'. + + + + + + + +Keyboard settings + + +When running DOSEMU from console (also remote from console) or X you +may need to define a proper keyboard layout. It is possible to let +DOSEMU do this work automatically for you (see auto below), however, +this may fail and you'll end up defining it explicitely. This is done either +by choosing one on the internal keytables or by loading an external +keytable from DOSEMU_LIB_DIR/keymap/* (which you may modify according +to your needs). Both sets have identical names (though you may add +any new one to DOSEMU_LIB_DIR/keymap/*): + + + be finnish hu-latin2 sg-latin1 + de finnish-latin1 it sw + de-latin1 fr keyb-no uk + dk fr-latin1 no-latin1 us + dk-latin1 hr-cp852 po + dvorak hr-latin2 sf + es hu sf-latin1 + es-latin1 hu-cwi sg jp106 + cz-qwerty cz-qwertz + + +You define an internal keytable such as + + + $_layout = "name" + + +where `name' is one of the above. To load a keytable you just prefix +the string with "load" such as + + + $_layout = "load de-latin1" + + + + + +Note, however, that you have to set + + + $_X_keycode = (on) + + +to use this feature under X, because by default the keytable is +forced to be neutral (us). Normally you will have the correct settings +of your keyboard given by the X-server. + + + +The most comfortable method, however, is to first let DOSEMU set the +keyboard layout itself. This involves 2 parts and can be done by setting + + + $_X_keycode = (auto) + + +which checks for existence of the X keyboard extension and if yes, +it sets $_X_keycode to 'on', that means the DOSEMU keytables +are active. The second part (which is independent from $_X_keycode) +can be set by + + + $_layout = "auto" + + +DOSEMU then queries the keyboard layout from the kernel or X (which +only does work on the console or X, but not in remote text terminals) +and generates a new DOSEMU +keytable out of the kernel information. This currently seems +only to work for latin-1 layouts, the latin-2 type of accents +seem not to exist so far in the kernel (linux/keyboard.h). +The resulting table can be monitor with DOSEMU 'keytable dump' +feature (see README-tech.txt) for details). + + + +When being on console you might wish to use raw keyboard, especially +together with some games, that don't use the BIOS/DOS to get their +keystrokes. + + + $_rawkeyboard = (1) + + +However, be carefull, when the application locks, you may not be able +to switch your console and recover from this. For details on recovering +look at README-tech.txt (Recovering the console after a crash). + + + + + +X Support settings + + +If DOSEMU is running in its own X-window (not xterm), you may need to +tailor it to your needs. Here a summary of the settings and a brief +description what they mean. A more detailed description of values +one can be found at chapter 2.2.14 (X Support settings) of README-tech.txt + + + + + +$_X_updfreq = (5) # time between refreshs (units: 20 == 1 second) +$_X_title = "DOS in a BOX" # Title in the top bar of the window +$_X_icon_name = "xdos" # Text for icon, when minimized +$_X_keycode = (off) # on == translate keybord via dosemu keytables +$_X_blinkrate = (8) # blink rate for the cursor +$_X_font = "" # basename from /usr/X11R6/lib/X11/fonts/misc/* + # (without extension) e.g. "vga" +$_X_mitshm = (on) # Use shared memory extensions +$_X_sharecmap = (off) # share the colormap with other applications +$_X_fixed_aspect = (on) # Set fixed aspect for resize the graphics window +$_X_aspect_43 = (on) # Always use an aspect ratio of 4:3 for graphics +$_X_lin_filt = (off) # Use linear filtering for >15 bpp interpol. +$_X_bilin_filt = (off) # Use bi-linear filtering for >15 bpp interpol. +$_X_mode13fact = (2) # initial factor for video mode 0x13 (320x200) +$_X_winsize = "" # "x,y" of initial windows size +$_X_gamma = (1.0) # gamma correction +$_X_vgaemu_memsize = (1024) # size (in Kbytes) of the frame buffer + # for emulated vga +$_X_lfb = (on) # use linear frame buffer in VESA modes +$_X_pm_interface = (on) # use protected mode interface for VESA modes +$_X_mgrab_key = "" # KeySym name to activate mouse grab, empty == off +$_X_vesamode = "" # "xres,yres ... xres,yres" + # List of vesamodes to add. The list has to contain + # SPACE separated "xres,yres" pairs + + + + + + + +Builtin ASPI SCSI Driver + + +The builtin ASPI driver (a SCSI interface protocol defined by Adaptec) can be +used to run DOS based SCSI drivers that use this standard (most SCSI devices +ship with such a DOS driver). This enables you to run hardware on Linux, that +normally isn't supported otherwise, such as CD writers, Scanners e.t.c. +The driver was successfully tested with Dat-streamers, EXABYTE tapedrives, +JAZ drives (from iomega) and CD writers. To make it work under DOSEMU +you need + + + + + + to configure $_aspi to define which +of the /dev/sgX devices you want to show up in DOSEMU. + + + + + + to load the DOSEMU aspi.sys stub driver within config.sys +(e.g. DEVICE=ASPI.SYS) before any ASPI using driver. + + + + + + + + +The $_aspi variable takes strings listing all generic SCSI +devices, that you want give to DOSEMU. NOTE: You should make sure, +that they are not used by Linux elsewhere, else you would come into +much trouble. To help you not doing the wrong thing, DOSEMU can +check the devicetype of the SCSI device such as + + +$_aspi = "sg2:WORM" + + +in which case you define /dev/sg2 being a CD writer device. If you omit +the type, + + + +$_aspi = "sg2 sg3 sg4" + + +DOSEMU will refuse any device that is a disk drive (imagine, what would happen +if you try to map a CD writer to the disk which contains a mounted Linux FS?). +If you want to map a disk drive to DOSEMU's ASPI driver, you need to +tell it explicitely + + +$_aspi = "sg1:Direct-Access" + + +or + + +$_aspi = "sg1:0" + + +and as you can see, `Direct-Access' is the devicetype reported by + + +$ cat /proc/scsi/scsi + + +which will list all SCSI devices in the order they are assigned to +the /dev/sgX devices (the first being /dev/sg0). You may also use the +DOSEMU supplied tool `scsicheck' (in src/tools/peripher), which helps +a lot to get the configuration right: + + +$ scsicheck +sg0 scsi0 ch0 ID0 Lun0 ansi2 Direct-Access(0) IBM DCAS-34330 S61A + $_aspi = "sg0:Direct-Access:0" (or "0/0/0/0:Direct-Access:0") +sg1 scsi0 ch0 ID5 Lun0 ansi2 Direct-Access(0) IOMEGA ZIP 100 D.08 + $_aspi = "sg1:Direct-Access:5" (or "0/0/5/0:Direct-Access:5") +sg2 scsi0 ch0 ID6 Lun0 ansi2 CD-ROM(5) TOSHIBA CD-ROM XM-5701TA 0167 + $_aspi = "sg2:CD-ROM:6" (or "0/0/6/0:CD-ROM:6") <== multiple IDs +sg3 scsi1 ch0 ID4 Lun0 ansi2 Sequential-Access(1) HP C1533A 9503 + $_aspi = "sg3:Sequential-Access:4" (or "1/0/4/0:Sequential-Access:4") +sg4 scsi1 ch0 ID6 Lun0 ansi1 WORM(4) IMS CDD522/10 1.07 + $_aspi = "sg4:WORM:6" (or "1/0/6/0:WORM:6") <== multiple IDs + + + + + +In the above example there are two scsi hostadapters (scsi0 and scsi1) and +DOSEMU will not show more than one hostadapter to DOS (mapping them +all into one), hence you would get problems accessing sg2 and sg4. For this +you may remap a different targetID such as + + +$_aspi = "sg2:CD-ROM:5 sg4:WORM" + + +and all would be fine. From the DOS side the CD-ROM appears as target 5 +and the WORM (CD writer) as target 6. +Also from the above scsicheck output, you can see, that you can opt +to use a `host/channel/ID/LUN' construct in place of `sgX' such as + + +$_aspi = "0/0/6/0:CD-ROM:5 1/0/6/0:WORM" + + +which is exactly the same as the above example, exept it will assign +the right device, even if for some reasons you have changed the order +in which sgX device are assigned by the kernel. Those changes happen, if +you turned off power of one device `between' or if you play with dynamic +allocation of scsi devices via the /proc/scsi interface such as + + +echo "scsi remove-single-device 0 0 5 0" >/proc/scsi/scsi + + +to delete a device and + + +echo "scsi add-single-device 0 0 5 0" >/proc/scsi/scsi + + +to add a device. HOWEVER, we strongly discourage +you to use these kernel feature for temporaryly switching off +power of connected devices or even unplugging them: normal SCSI busses +are not hotpluggable. Damage may happen and uncontroled voltage +bursts during power off/on may lock your system !!! + + + +Coming so far, one big problem remains: the (hard coded) buffersize for +the sg devices in the Linux kernel (default 32k) may be to small for DOS +applications and, if your distributor yet didn't it, you may need to +recompile your kernel with a bigger buffer. The buffer size is defined +in linux/include/scsi/sg.h and to be on the secure side you may define + + +#define SG_BIG_BUFF (128*1024-512) /* 128 Kb buffer size */ + + +though, CD writers are reported to work with 64Kb and the `Iomega guest' +driver happily works with the default size of 32k. + + + + + +COM ports and mice + + +We have simplified the configuration for mice and serial ports and +check for depencies between them. If all strings in the below example +are empty, then no mouse and/or COM port is available. Note. that you +need no mouse.com driver installed in your DOS environment, DOSEMU +has the mousedriver builtin. The mouse settings below only apply to +DOSEMU in the Linux console; in X and terminals the mouse is detected +automatically. The below example is such a setup + + + + + + $_com1 = "" # e.g. "/dev/mouse" or "/dev/ttyS0" + $_com2 = "/dev/modem" # e.g. "/dev/modem" or "/dev/ttyS1" + + $_mouse = "microsoft" # one of: microsoft, mousesystems, logitech, + # mmseries, mouseman, hitachi, busmouse, ps2 + $_mouse_dev = "/dev/mouse" # one of: com1, com2 or /dev/mouse + $_mouse_flags = "" # list of none or one or more of: + # "emulate3buttons cleardtr" + $_mouse_baud = (0) # baudrate, 0 == don't set + + + + + +The above example lets you have your modem on COM2, COM1 is spare (as +you may have your mouse under native DOS there and don't want to change +the configuration of your modem software between boots of native DOS and Linux) + + + +However, you may use your favorite DOS mouse driver and directly let it +drive COM1 by changing the below variables (rest of variables unchanged) + + + + + + $_com1 = "/dev/mouse" + $_mouse_dev = "com1" + + + + + +And finaly, when you have a PS/2 or USB mouse on your machine you use +the built-in mouse driver (not your mouse.com) to get it work: +( again leaving the rest of variables unchanged) + + + + + + $_mouse = "ps2" + $_mouse_dev = "/dev/mouse" + + + + + +When using a PS/2 or USB mouse or when having more than 2 serial ports you may +of course assign _any_ free serialdevice to COM1, COM2. The order doesn't +matter: + + + + + + $_com1 = "/dev/ttyS2" + $_com2 = "/dev/ttyS0" + + + + + + + +Printers + + +Printer is emulated by piping printer data to your normal Linux printer. +The belows tells DOSEMU which printers to use. The `timeout' tells DOSEMU +how long to wait after the last output to LPTx before considering the +print job as `done' and to close it. + + + + + +# Print commands to use for LPT1, LPT2 and LPT3. +# Default: "lpr -l, lpr -l -P lpt2, lpr -l P lpt3" +# Which means: use the default print queue for LPT1, "lpt2" queue for LPT2, +# "lpt3" queue for LPT3. "-l" means raw printing mode (no preprocessing). + +$_lpt1 = "lpr -l" +$_lpt2 = "lpr -l -P lpt2" +$_lpt3 = "lpr -l -P lpt3" + +$_printer_timeout = (20)# idle time in seconds before spooling out + + + + + + + +Sound + + +The following settings will tell DOSEMU to emulate an SB16 sound +card using your Linux sound drivers. For more information see +sound-usage.txt. + + + + + +$_sound = (on) # sound support on/off +$_sb_base = (0x220) # base IO-address (HEX) +$_sb_irq = (5) # IRQ +$_sb_dma = (1) # Low 8-bit DMA channel +$_sb_hdma = (5) # High 16-bit DMA channel +$_sb_dsp = "/dev/dsp" # Path to the sound device +$_sb_mixer = "" # path to the mixer control +$_mpu_base = (0x330) # base address for the MPU-401 chip (HEX) + + + + + + + +Joystick + + +Here are the settings for Joystick emulation. + + + + + +$_joy_device = "/dev/js0 /dev/js1" + # 1st and 2nd joystick device + # e.g. "/dev/js0" or "/dev/js0 /dev/js1" + # (or "" if you don't want joystick support) + # +$_joy_dos_min = (1) # range for joystick axis readings, must be > 0 +$_joy_dos_max = (150) # avoid setting this to > 250 +$_joy_granularity = (1) # the higher, the less sensitive - + # useful if you have a wobbly joystick + # +$_joy_latency = (1) # delay between nonblocking linux joystick reads + # increases performance if >0 and processor>=Pentium + # recommended: 1-50ms or 0 if unsure + + + + + + + +Networking under DOSEMU + + +Turn the following option `on' if you require IPX/SPX emulation, +there is no need to load IPX.COM within the DOS session. +( the option does not emulate LSL.COM, IPXODI.COM, etc. ) +And NOTE: You must have IPX protocol configured into the kernel. + + + + + + $_ipxsupport = (on) + + + + + +Enable Novell 8137->raw 802.3 translation hack in new packet driver. + + + + + + $_novell_hack = (on) + + + + + +If you make use of TUN/TAP driver, then you can select it via + + + + + + $_vnet = "tap" + + + + + +But if you just need 'single' packet driver support that talks to, for +instance, your ethernet card eth0 then you need to set + + + + + + $_netdev = "eth0" + + + + + +Note that dosnet and eth0 require raw packet access, and hence +(suid)-root. If $_vnet = "dosnet", then $_netdev will default to "dsn0". +If you would like to use persistent TUN/TAP devices then you +need to specifify the TAP device in $_netdev. +For more on this look at chapter 15 (Networking using DOSEMU). + + + + + +Settings for enabling direct hardware access + +The following settings (together with the direct console video settings +below make it possible for DOSEMU to access your real (non-emulated) +computer hardware directly. Because Linux does not permit this for +ordinary users, DOSEMU needs to be run as root, via sudo or suid-root +to be able to use these settings. They can NOT be overwritten by the +user configuration file ~/.dosemurc. +You must also run dosemu with the "-s" switch. This activates sudo +when appropriate; without it, even root will not get direct hardware +access. + + + +Here you tell DOSEMU what to do when DOS wants let play the speaker: + + + $_speaker = "" # or "native" or "emulated" + + + + + +And with the below may gain control over real ports on you machine. +But: + + + +WARNING: GIVING ACCESS TO PORTS IS BOTH A SECURITY CONCERN AND +SOME PORTS ARE DANGEROUS TO USE. PLEASE SKIP THIS SECTION, AND +DON'T FIDDLE WITH THIS SECTION UNLESS YOU KNOW WHAT YOU'RE DOING. + + + + + + $_ports = "" # list of portnumbers such as "0x1ce 0x1cf 0x238" + # or "0x1ce range 0x280,0x29f 310" + # or "range 0x1a0,(0x1a0+15)" + + + + + +If you have hardware, that is not supported under Linux but you have +a DOS driver for, it may be necessary to enable IRQ passing to DOS. +Note that IRQ passing does not work on x86-64. + + + $_irqpassing = "" # list of IRQ number (2-15) to pass to DOS such as + # "3 8 10" + + + + + + + +Video settings ( console only ) + + +!!WARNING!!: IF YOU ENABLE GRAPHICS ON AN INCOMPATIBLE ADAPTOR, +YOU COULD GET A BLANK SCREEN OR MESSY SCREEN EVEN AFTER EXITING DOSEMU. +Read doc/README-tech.txt (Recovering the console after a crash). + + + +Start with only text video using the following setup + + + + + + $_video = "vga" # one of: plainvga, vga, ega, mda, mga, cga + $_console = (0) # use 'console' video + $_graphics = (0) # use the cards BIOS to set graphics + $_vbios_seg = (0xc000) # set the address of your VBIOS (e.g. 0xe000) + $_vbios_size = (0x10000)# set the size of your BIOS (e.g. 0x8000) + $_vmemsize = (0) # amount of video RAM to save/restore + $_chipset = "" + $_dualmon = (0) # if you have one vga _plus_ one hgc (2 monitors) + + + + + +After you get it `somehow' working and you have one of the DOSEMU supported +graphic cards you may switch to graphics mode changing the below + + + + + + $_graphics = (1) # use the cards BIOS to set graphics + + + + + +If you have a 100% compatible standard VGA card that may work, +however, you get better results, if your card has one of the DOSEMU supported +video chips and you tell DOSEMU to use it such as + + + + + + $_chipset = "s3" # one of: plainvga, trident, et4000, diamond, s3, + # avance, cirrus, matrox, wdvga, paradise, ati, sis, + # svgalib, vesa + + + + + +Note, `s3' is only an example, you must set the correct video chip +else it most like will crash your screen. + + + +The 'svgalib' setting uses svgalib 1.9.21 or greater for determining the correct +video chip. + + + +The 'vmemsize' setting's default of 0 causes DOSEMU to try to autodetect +the amount of video memory on the graphics card. This amount is saved +and restored when you switch away from and back to the virtual console DOSEMU +runs in using the Ctrl-Alt-Fn key combination. Since saving video +memory can be a very slow operation you may want to restrict 'vmemsize' +to a value that was more common when DOS was still mainstream. For instance +1024 covers it if you want to be able to save and restore modes up to +1024x768x256. + + + +NOTE: `video setting' can not be overwritten by ~/.dosemurc. + + + + + +Time settings + +By Paul Crawford. This is a short guide to the time modes supported in +DOSEMU; you can get +a more detailed description of how and why they operate at + +http://www.sat.dundee.ac.uk/~psc/dosemu_time_advanced.html +. There are three modes currently supported +using the $_timemode variable: + + + $_timemode = "bios" + $_timemode = "pit" + $_timemode = "linux" + + +Most users will only ever have a need for either BIOS or LINUX mode, +and the decision comes down to the two basic characteristics: + + + +In BIOS mode, the DOSEMU session begins with the current (local) +date & time, and once running it behaves fully like an emulated PC. +You can set the date & time using the normal DOS methods as you would expect. +However, time keeping accuracy in the long term under DOS was never very good, +and under DOSEMU it is typically poorer. + + + + + +In LINUX mode, the emulated PC always reports the current host time, +and so you no longer have the option to set the DOS time (the date can be +set, which is odd, as this is kept in DOS and not in the emulator). In +this mode you get the long term accuracy of the host, so if you are +running NTP or similar, you always get accurate time. + + + + + +The PIT mode is an odd compromise, it provides BIOS-like time +(settable, decoupled from host time), but with slightly higher +accuracy. In summary, if you need accurate time, choose LINUX mode, +otherwise if you need time setting capabilities or close 'real' PC +emulation, choose BIOS mode. + + + +A reasonable question for some applications is +"how do I get accurate UTC time?", of course, the more general question is +about running the emulated DOS session in a different timezone and/or with +differing "daylight saving" options to the host computer. +This is quite simple in fact, just start DOSEMU with the environment +variable TZ set to the required zone, and don't forget to have the +corresponding command in the DOS autoexec.bat file (otherwise +Microsoft products all assume -8 hours PST!). The DOSEMU emulation +of file system access is also based on the local timezone it is running in, +so you will get consistent file time stamps compared to the emulated time. +So in the UTC case, you would start DOSEMU in a shell with TZ=GMT0, and +have a line in autoexec.bat with "set TZ=GMT0" before any of your programs +are run. + + + + + + + diff --git a/src/doc/README/config-tech b/src/doc/README/config-tech new file mode 100644 index 0000000..6077736 --- /dev/null +++ b/src/doc/README/config-tech @@ -0,0 +1,3556 @@ + +Runtime Configuration Options + + +This section of the document by Hans, +<lermen@fgan.de>. Last +updated on Mar 20, 1998. + + + +Most of DOSEMU configuration is done during runtime and per default it +expects the system wide configuration file /etc/dosemu.conf optionally +followed by the user's ~/.dosemu/.dosemurc and additional configurations statements +on the commandline (-I option). The builtin configuration file of a DEXE +file is passed using the -I technique, hence the rules of -I apply. + + + +In fact /etc/dosemu.conf and ~/.dosemu/.dosemurc (which have identical syntax) +are included by the systemwide configuration script +/var/lib/dosemu/global.conf, but as a normal user you won't ever think on +editing this, only dosemu.conf and your personal ~/.dosemu/.dosemurc. + + + +In DOSEMU prior to 0.97.5 the private configuration file was called ~/.dosrc +(not to be confused with the new ~/.dosemu/.dosemurc). This will work as expected +formerly, but is subject to be nolonger supported in the near future. +This (old) ~/.dosrc is processed after global.conf and follows (same +as -I) the syntax of global.conf. + + + +The first file expected (and interpreted) before any other configuration +(such as global.conf, dosemu.conf and ~/.dosemu/.dosemurc) is /etc/dosemu.users. +If /etc/dosemu.users doesn't exist, DOSEMU check for /etc/dosemu/dosemu.users, +this makes people happy, which prefer to have to configuration stuff in +a separate directory under /etc. +Within dosemu.users the general permissions are set: + + + + + + + + + which users are allowed to have a private lib dir. + + + + + + what kind of access class the user belongs to. + + + + + + what special configuration stuff the users needs + + + + + + + + +and further more: + + + + + + whether the lib dir (/var/lib/dosemu) resides elsewhere. + + + + + + setting the loglevel. + + + + + + + + +This is done via setting configuration variables. + + + +After /etc/dosemu.users /etc/dosemu.conf (via global.conf) is interpreted, +and only during global.conf parsing access to all configuration options is +allowed. Your personal ~/.dosemu/.dosemurc is included directly after dosemu.conf, +but has less access rights (in fact the lowest level), all variables you +define within ~/.dosemu/.dosemurc transparently are prefixed with `dosemu_' such +that the normal namespace cannot be polluted (and a hacker cannot overwrite +security relevant enviroment variables). Within global.conf only those +~/.dosemu/.dosemurc created variables, that are needed are taken over and may +overwrite those defined in /etc/dosemu.conf. + + + +The dosemu.conf (global.conf) may check for the configuration variables, +that are set in /etc/dosemu.users and optionaly include further configuration +files. But once /etc/dosemu.conf (global.conf) has finished interpretation, +the access to secure relevant configurations is (class-wise) restricted while +the following interpretation of (old) .dosrc and -I statements. + + + +For an example of a general configuration look +at ./etc/global.conf. The later behaves insecure, when /etc/dosemu.users +is a copy of ./etc/dosemu.users.easy and behave 'secure', when +/etc/dosemu.users is a copy of ./etc/dosemu.users.secure. + + + +Format of /etc/dosemu.users + + +Except for lines starting with x=' (explanation below), +each line corresponds to exactly _one_ valid user count: + + + + + + loginname [ c_strict ] [ classes ...] [ c_dexeonly ] [ other ] + + + + + +where the elements are: + + + + + + +loginname + + + valid login name (root also is one) or 'all'. The later means +any user not mentioned in previous lines. + + + +classes + + + One or more of the following: + + + +c_all + + + no restriction + + + +c_normal + + + normal restrictions, all but the following classes: +c_var, c_vport, c_irq, c_hardram, c_pci, c_net. + + + +c_var + + + allow (un)setting of configuration- and +environment variables + + + +c_vport + + + allow setting of 'emuretrace' + + + +c_port + + + allow `port' setting + + + +c_irq + + + allow `irqpassing' statement + + + +c_hardram + + + allow 'hardware_ram' settings + + + +c_pci + + + allow 'PCI' settings + + + +c_net + + + allow direct net and dosnet settings (TUN/TAP is always allowed). + + + + + + +other + + + Here you may define any configuration variable, that you +want to test +in global.conf (or (old) .dosrc, -I), see `ifdef', `ifndef' +When this variable is intended to be unset in lower privilege +configuration files (.dosrc, -I), then the variable name +has to be prefixed with `u_'. + + + +private_setup + + + Keyword, this makes dosemu to accept a private DOSEMU lib +under $HOME/.dosemu/lib. If this directory is existing, DOSEMU +will expect all normally under /var/lib/dosemu within that +directory,including `global.conf'. As this would be a security +risc, it only will be allowed, if the used DOSEMU binary is +non-suid-root. If you realy trust a user you may additionally +give the keyword `unrestricted', which will allow this user to +execute a suid-root binary even on a private lib directory +(though, be aware). + + + +unrestricted + + + Keyword, used to allow `private_setup' on suid-root +binaries too. USE WITH CARE ! + + + + + + + +A line with '#' at colum 1 is treated as comment line. When only the login +name is given (no further parameters, old format) the following setting is +assumed: + + + + + + if 'root' c_all + else c_normal + + + + + +In addition, dosemu.users can be used to define some global settings, which +must be known before any other file is accessed, such as: + + + default_lib_dir= /opt/dosemu # replaces /var/lib/dosemu + config_script= /etc/dosemu/global.conf # uses this file instead of the + # built-in global.conf + log_level= 2 # highest log level + + + + + +With `default_lib_dir=' you may move /var/lib/dosemu elsewere, this mostly +is interesting for distributors, who want it elsewhere but won't patch the +DOSEMU source just for this purpose. But note, the dosemu supplied scripts +and helpers may need some adaption too in order to fit your new directory. + + + +By default, a global.conf is used that is linked into the main DOSEMU +executable. Using `config_script=` you can specify a standalone filename +for use as 'global.conf'. The default setting is 'builtin'. + + + +The `log_level=' can be 0 (never log) or 1 (log only errors) or 2 (log all) +and controls the ammount written to the systems log facility (notice). +This keyword replaces the former /etc/dosemu.loglevel file, which now is +obsolete. + + + + + +Format of global.conf ( (old) .dosrc, -I option) + + +The configuration files are not line oriented, instead are consisting of +`statements' (optionally separated by `;'), whitespaces are removed +and all behind a '#' up to the end of the line is treated as comment. +( Note that older DOSEMUs also allowed `!' and `;' as comment character, +but those are no longer supported ). + + + +Enviroment variables and configuration variables + + +They existed already in very early versions of DOSEMU (until now), +but now evironment variables are much more useful in dosemu.conf / global.conf +as before, because you now can set them, test them in the new 'if' statement +and compute them in expressions. +The problem with the enviroment variables is, however, +that the user may set and fake them before calling DOSEMU, hence this +is a security problem. To avoid this, we have the above mentioned +configuration variables, which are of complete different name space +and are not visible outside of DOSEMU's configuration parser. On the other +hand it may be useful to export settings from global.conf to the +running DOS environment, which can be achieved by the 'unix.exe -e' +DOS programm. + + + +To summarize: + + + +configuration variables + + + have their own namespace only within the +configuration parser. They usual are prefixed by c_, u_ +and h_ and cannot be made visible outside. They do not contain +any value and are only tested for existence. + + + +environment variables + + + are inherited from the calling process, can +be set within global.conf (dosemu.conf) and passed to DOSEMU running +DOS-applications. Within *.conf they always are prefixed +by '$' (Hence TERM becomes $TERM, even on the left side of an +assigment). However, setting them is controled by the configuration +variable 'c_var' (see above) and may be disallowed within the user +supplied (old) .dosrc and alike. + + + + + + + +At startup DOSEMU generates the following environment variables, which +may be used to let the configuration adapt better: + + + + + + +KERNEL_VERSION_CODE + + + holds the numerical coded version of the running +linux kernel (same format as within linux/version.h) + + + +DOSEMU_VERSION_CODE + + + holds the numerical coded version of the running +DOSEMU version (format: MSB ... LSB == major, minor, patchlevel, +sublevel) + + + +DOSEMU_EUID + + + effective uid + + + +DOSEMU_UID + + + uid. You may protect security relevant parts of the +configuration such as: + + +if ( ! $DOSEMU_EUID && ($DOSEMU_EUID != $DOSEMU_UID) ) + warn "running suid root" +endif + + + + + + +DOSEMU_USER + + + The user name, that got matched in /etc/dosemu.users. +This needs not to be the _real_ user name, it may be `all' or +`unknown'. + + + +DOSEMU_REAL_USER + + + The user name out of getpwuid(uid). + + + +DOSEMU_SHELL_RETURN + + + The exitcode (0-255) from the recently executed +shell() command. + + + +DOSEMU_OPTIONS + + + A string of all commandline options used (one +character per option). You may remove a character from this string, +such that the normal override of dosemu.conf settings will not take +place for that option. However, parsing the command line options +happens in two stages, one _before_ parsing dosemu.conf and one +_after_. The options 'FfhIdLoO23456' have +already gotten processed before dosemu.conf, so they can be disabled. + + + + + + + + + +Conditional statements + + +You may control execution of configuration statements via the following +conditional statement: + + + + + + ifdef <configuration variable> + + +or + + + ifndef <configuration variable> + ... + else + ... + endif + + + + + + where variable is a configuration variable (not an +environment variable). Additionally there is a `normal' if statement, +a while statement and a foreach statement such as + + + if ( expression ) + endif + while ( expression ) + done + foreach loop_variable (delim, list) + done + + +but these behaves a bit different and are described later. + + + +The 'else' clause may be ommitted and 'ifndef' is the opposite to 'ifdef'. +The <variable> can't be tested for its contents, only if it is set or not. +Clauses also may contain further if*def..endif clause up to a depth of 15. +All stuff in /etc/dosemu.users behind the 'loginname' in fact are +configuration variables +that are set. Hence, what you set there, can be tested here in the config +file. Further more you may set/unset configuration variables +in the config files itself: + + + + + + define <configuration variable> + undef <configuration variable> + + + + + +However, use of define/undef is restricted to scope of global.conf, +as long as you don't 'define c_var' _within_ global.conf. +If you are under scope of a 'user config file' (e.g. outside +global.conf) you have to prefix the configuration variable +name with 'u_', else it +will not be allowed to be set/unset (hence 'c_' type variables can't be +unset out of scope of global.conf). + + + +There are some configuration variables +(besides the ones described above for dosemu.users) +implicitely predefined by DOSEMU itself: + + + + + + +c_system + + + set while being in global.conf + + + +c_user + + + set while parsing a user configuration file + + + +c_dosrc + + + set while parsing (old) .dosrc + + + +c_comline + + + set while parsing -I option statements + + + +c_dexerun + + + set if a DEXE will be executed + + + +h_<ownhost> + + + defined on startup as 'h_<host>.<domain>' of the host +DOSEMU is running on. If <domain> can't be resolved, +the pure hostname is taken. This makes sense only if a file +system containing DOSEMU is mounted on diskless machines and you +want restrict access. Note however, h_<ownhost> is set using +gethostname/getdomainname. Hence, if the user on the +diskless machine has root access, this is _not_ secure, +because he could fake a valid address. + + + + + + + +Also, you may define any 'u_' type variable at start of DOSEMU via the new +option -u such as + + + + + +# dosemu -u myspecialfun + + + + + +this will then define the config variable 'u_myspecialfun' _before_ parsing +any configuration file. You then may check this in your (old) ./dosrc or +global.conf to do the needed special configuration. + + + +Now, what's this with the if statement and while statement? +All those conditionals via ifdef and indef are completly +handled before the remaining input is passed to the parser. Hence you +even may use them within a configuration statement such as + + + + + +terminal { charset + ifdef u_likeibm + ibm + else + latin + endif + updatefreq 4 color on } + + + + + +This is not the case with the (above mentioned) if statement, this one +is of course processed within the parser itself and can only take place within +the proper syntax context such as + + + + + + + if ( defined( u_likeibm ) ) + $mycharset = "ibm" + else + $mycharset = "latin" + endif + terminal { charset $mycharset updatefreq 4 color on } + + + + + +but it has the advantage, that you can put any (even complex) +expression (see chapter `expressions') between the brackets. +If the expression's numerical value is 0, then false is assumed, else true. + + + +Same rules apply to the while statement, the loop will be executed +as long as `expression' is not 0. The loop end is given by the keyword +done such as in + + + + + + $counter = (3) + while ($counter > 0) + # what ever should loop + $counter = ($counter -1) + done + + # or some kind of list processing + # ... but look below, `foreach' does it better + $list = "aa bbb ccc" + while (strlen($list)) + $item = strdel($list, strchr($list," "), 999) + $list = strsplit($list, (strlen($item)+1),9999); + warn "doing something with ", $item + done + + + + + + +The foreach statement behaves a bit like the /bin/sh `for i in', but +you can specify a list of delimiters. + + + + + + $list = "anton, berta; caesar dora" + $delim = " ,;" + foreach $xx ($delim, $list) + warn "My name is ", $xx + done + + $list = "a b c : 1 2 3" + $delim = ":" + foreach $xx ($delim, $list) + if ($delim eq ":") + $delim = " "; + else + warn "processing number ", $xx + endif + done + + +The later example jumps to the colon (`:') in one step and after that +process the numbers step by step. + + + +For all loops and `if' statement the allowed depth is 32 (totally). + + + + + +Include files + + +If you for some reason want to bundle some major settings in a separate file +you can include it via + + + + + + include "somefile" + + + + + +If 'somefile' doesn't have a leading '/', it is assumed to be relative to /etc. +Also includeing may be nested up to a max depth of 10 files. +Note however, that the privilege is inherited from the main file from which +is included, hence all what is included by global.conf has its +privilege. + + + +However, there are restrictions for `while' loops: You can't have +include statements within loops without beeing prepared for unexpected +behave. In fact you may try, but due to the technique used, include files +within loops are loaded completely prior loop execution. +Hence, if you do conditional including this won't work. + + + +A further restriction is, that the name of the include file must not be +a variable. However, you can work around this with a macro (see next +chapter) as shows the following example: + + + + + + $file = $HOME, "/.my_dosrc_include" + shell("test -f ", $file) + if ( ! $DOSEMU_SHELL_RETURN) + # we can include + $INC = ' include "', $file, '"'; + $$INC + endif + + + + + + + +Macro substitution + + +There is a very rudimentary macro substitution available, which +does allow recursion but no arguments: Whenever you write + + + + + + $MYMACRO = "warn 'this is executed as macro'" ; + $$MYMACRO + + +it will expand to + + + warn 'this is executed as macro' + + + + + +Note, that the `;' is required for parser to recognize the end of the +statement and to set the variable before it tries to read the next +token (which will let the lexer process `$$MYMACRO'). Macro substitution +is completely done on the input stream before the parser gets the data. + + + +For what is it worth then? Now, this enables you to insert text into the +input stream, that otherwise would be expected to be constant. Or simple, +it allows you to be lazy to write the same things more then once. + + + $loop = ' + while ($xxx) + warn "loop in macro ",$xxx + $xxx = ($xxx -1) + done + '; + $xxx = (2); $$loop; $xxx = (3); $$loop; + + $_X_keycode = (off) + $_X_lin_filt = (on) + ... + if ($_X_keycode) $_X_keycode = "keycode" else $_X_keycode = "" endif + if ($_X_lin_filt) $_X_lin_filt = "lin_filt" else $_X_lin_filt = "" endif + X { icon_name "xdos" $$_X_keycode $$_X_lin_filt } + + +You see, that in cases the variables are `false', the (parameterless) +`keycode' and/or `lin_filt' keywords would not appear in the `X{}' statement. + + + + + +Expressions + + +Expression within the configuration files follow the usual numerical rules +and may be as complex as you wish. At some places, the parser only can +`understand' expressions, when you enclose them in brackets, but mostly +you just can type + + + 123 + 456 + 2 * 1.2 + + + + + +Though, if you want be sure, you better type them as + + + ( 123 + 456 + 2 * 1.2 ) + + + + + +You may place expressions whenever a numerical value is expected and there +is no ambiguity in the syntax. Such an ambiguity is given, when a statement +needs more then one successive number such as + + + + + + ... winsize x y ... + ... vesamode width heigh ... + ... range from to ... + + + + + +If you want to place expression herein, you need the new syntax for those +statements / terms which have a coma (instead of a blank) as delimiter: + + + + + + ... winsize x , y ... + ... vesamode width , heigh ... + ... range from , to ... + + + + + +The old syntax is left for compatibility and is only parsed correcty, if +pure numbers (integers) are used. + + + +Valid constant numbers (not only in expressions) are + + + 123 decimal, integer + 0x1a hexadecimal, integer + 0b10101 bitstream, integer + 1.2 float number, real + 0.5e3 exponential form, real + off boolean false, integer + on boolean true, integer + no boolean false, integer + yes boolean true, integer + + + + + +The following operator are recognized: + + + + - * as usual + / div division, the '/' _must_ be surrounded by whitespaces, else + it conflicts with pathnames on quoteless strings + | bitwise or + ^ bitwise exclusive or + ~ bitwise not + & bitwise and + >> shift right + << shift left + < less then + <= less or equal + > greater then + >= greater or equal + || boolean or + && boolean and + ! boolean not + == numerical equivalence + eq string equivalence + != numerical, not equal + ne string not equal + + + + + +The type of the expression result may be real (float) or integer, depending +on which type is on the `left side'. Conversion is mostly done automaticaly, +but you may force it using the (below mentioned) int() and real() functions +such as: + + + $is_real = ( 3.1415 * 100 ) + $is_integer = ( int( 3.1415 * 100) ) + $is_integer = ( 100 * 3.1415 ) + $is_real = ( real($is_integer) ) + + + + + +The above also shows, how environment variables can be set: if you want +to place `expressions' (which are always numbers) onto a variable, you +have to surround them with brackets, else the parser won't be able to detect +them. In principal, all $xxx settings are string-settings and numbers will +be converted correctly before. In fact the `$xxx =' statement accepts a +complete coma separated list, which it will concatenate, examples: + + + + + + $termnum = (1) + $MYTERM = "/dev/ttyp", $termnum # results in "/dev/ttyp1" + $VER = (($DOSEMU_VERSION_CODE >> 24) & 255) + $MINOR = (($DOSEMU_VERSION_CODE >> 16) & 255) + $running_dosemu = "dosemu-", $VER, ".", $MINOR + + + + + +Several builtin functions, which can be used in expressions, are available: + + + +int(real) + + + converts a float expression to integer + + + +real(integer) + + + converts a integer expression to float + + + +strlen(string) + + + returns the length of `string' + + + +strtol(string) + + + returns the integer value of `string' + + + +strncmp(str1,str2,expression) + + + compares strings, see `man strncmp' + + + +strpbrk(str1,str2) + + + like `man strpbrk', but returns an index +or -1 instead of a char pointer. + + + +strchr(str1,str2) + + + like `man strchr', but returns an index +or -1 instead of a char pointer. + + + +strrchr(str1,str2) + + + like `man strrchr', but returns an index +or -1 instead of a char pointer. + + + +strstr(str1,str2) + + + like `man strstr', but returns an index +or -1 instead of a char pointer. + + + +strspn(str1,str2) + + + as `man strspn' + + + +strcspn(str1,str2) + + + as `man strcspn' + + + +defined(varname) + + + returns true, if the configuration variable +`varname' exists. + + + + + + + + + +String expressions + + +For manipulation of strings there are the following builtin functions, +which all return a new string. These very well may be placed as argument +to a numerical function's string argument, but within an `expression' they +may be only used together with the `eq' or `ne' operator. + + + + + + +strcat(str1, ,strn) + + + concatenates any number of strings, the result +is a string again. + + + +strsplit(string, expr1, expr2) + + + cuts off parts of `string', starting +at index `expr1' with length of `expr2'. +If either `expr1' is < 0 or `expr2' is < 1, an empty +string is returned. + + + +strdel(string, expr1, expr2) + + + deletes parts of `string', starting +at index `expr1' with length of `expr2'. +If either `expr1' or `expr2' is < 0, nothing +is deleted (the original strings is returned.) + + + +shell(string) + + + executes the command in `string' and returns +its stdout result in a string. The exit code of +executed command is put into $DOSEMU_SHELL_RETURN +(value between 0 and 255). You may also call +shell() as a standalone statement (hence +discarding the returned string), if you only need +$DOSEMU_SHELL_RETURN (or not even that). +However, to avoid +security implications all privilegdes are dropped +and executions is under control of c_shell +configuration variable. The default is, that it +can only be executed from within global.conf. + + + + + + + +With these tools you should be able to make your global.conf adapt +to any special case, such as different terminal types, different hdimages +for different users and/or different host, adapt to different (future) +dosemu and/or kernel versions. Here some small examples: + + + + + + $whoami = shell("who am i") + if ( strchr($whoami, "(" ) < 0 ) + # beeing on console + else + if (strstr($whoami, "(:") < 0) + # beeing remote logged in + endif + if ($TERM eq "xterm") + # beeing on xterm + else + if (strstr("linux console", $TERM) < 0) + # remote side must be some type of real terminal + else + # remote side is a Linux console + endif + endif + endif + + if ($DISPLAY ne "") + # we can rely on reaching an Xserver + if (strsplit($DISPLAY, 0, 1) ne ":") + # the X server is remote + endif + endif + + if ($DOSEMU_REAL_USER eq "alistair") + # ha, this one is allowed to do odds sound tricks :-) + endif + + # disable setting direct VGA console graphics mode per commandline option -V + $DOSEMU_OPTIONS = strdel($DOSEMU_OPTIONS, strchr($DOSEMU_OPTIONS,"V"),1); + + + + + + + + +`Dry' testing your configuration + + +It may be useful to verify, that your *.conf does what you +want before starting a real running DOSEMU. For this purpose there is a +new commandline option (-h), which just runs the parser, print some +useful output, dumps the main configuration structure and then exits. +The option has an argument (0,1,2), which sets the amount of parser debug +output: 0 = no parser debug, 1 = print loop debugs, 2 = same as 1 plus +if_else_endif-stack debug. This feature can be used such as + + + + + + $ dosemu.bin -h0 -O 2>&1 | less + + + + + +The output of `-h2' looks like + + + PUSH 1->2 1 >foreach__yy__< + PUSH 2->3 1 >if< + POP 2<-3 0 >endif< -1 + POP 1<-2 1 >done< -1 + PUSH 1->2 1 >foreach__yy__< + PUSH 2->3 1 >if< + POP 2<-3 1 >endif< -1 + POP 1<-2 1 >done< -1 + | | | +-------`cached' read status (0 = not cached) + | | +-----------------`if' or `loop' test result (0 = false = skipped) + | +-------------------inner level + +----------------------outer level (1 = no depth) + + + + + +There are also some configuration statements, which aren't of any use +except for help debugging your *.conf such as + + + + + + exprtest ($DOSEMU_VERSION_CODE) # will just print the result + warn "content of DOSEMU_VERSION_CODE: ", $DOSEMU_VERSION_CODE + + + + + + + +Debug statement + + +This section is of interest mainly to programmers. This is useful if +you are having problems with DOSEMU and you want to enclose debug info +when you make bug reports to a member of the DOSEMU development team. +Simply set desired flags to "on" or "off", then redirect stderr of +DOSEMU to a file using "dosemu.bin -o debug" to record the debug information +if desired. Skip this section if you're only starting to set up. + + + + + + debug { config off disk off warning off hardware off + port off read off general off IPC off + video off write off xms off ems off + serial off keyb off dpmi off + printer off mouse off sound off + } + + + + + +Alternatively you may use the same format as -D commandline option +(but without the -D in front), look at the dosemu.bin.1 man page. + + + + + + debug "+a-v" # all messages but video + debug "+4r" # default + maximum of PIC debug + + + + + +or simply (to turn off all debugging) + + + + + + debug { off } + + + + + + + +Miscellaneous + + +The HogThreshold value determines how nice Dosemu will be about +giving other Linux processes a chance to run. Setting the HogThreshold +value to approximately half of you BogoMips value will slightly +degrade Dosemu performance, but significantly increase overall +system idle time. A zero value runs Dosemu at full tilt. + + + + + + HogThreshold 0 + + + + + +Want startup DOSEMU banner messages? Of course :-) + + + + + + dosbanner on + + + + + +For "mathco", set this to "on" to enable the coprocessor during DOSEMU. +This really only has an effect on kernels prior to 1.0.3. + + + + + + mathco on + + + + + +For "cpu", set this to the CPU you want recognized during DOSEMU. + + + + + + cpu 80386 + + + + + +If you have DOSEMU configured to use the 386-emulator, you can enable the +emulator via + + + + + + cpu emulated + + + + + +You may ask why we need to emulate a 386 on an 386 ;-) Well, for normal +purpose its not needed (and will be slower anyway), but the emulator offers +a better way to debug DOS applications, especially DPMI code. +Also, we hope that some day we may be able to run DOSEMU on other machines +than 386. At the time of writing this, the cpu emulators is very alpha and +you should not use it except you are willing to help us. So please, don't +even try to report 'bugs' except you have a patch for the bug too. + + + +If you have a pentium, DOSEMU can make use of the pentium cycle counter +to do better timing. DOSEMU detects the pentium and will use the RDTSC +instruction for get time per default. To disable this feature use + + + + + + rdtsc off + + + + + +Also, to use the pentium cycle counter correctly DOSEMU needs to know +the CPU-clock which your chip is running. This per default is calibrated +automatically, however, that may be not exact enough. In this case you +have to set it yourself via + + + + + + cpuspeed 180 + + + + + +or, for values like 166.6666 you may give two numbers such as + + + + + + + cpuspeed 500 3 + + + + + +which will be calculated as 'cpuspeed=500/3' + + + +If you have a PCI board you may allow DOSEMU to access the PCI configuration +space by defining the below + + + + + + pci on | off + + + + + +PCI is assumed to be present on CPUs better then a pentium, otherwise +the default is 'pci off' + + + +For "bootA"/"bootC", set this to the bootup drive you want to use. +It is strongly recommended you start with "bootA" to get DOSEMU +going, and during configuration of DOSEMU to recognize hard disks. + + + + + + bootA + + + + + +During compile there will be a symbol map generated, this usually +then is ./bin/dosemu.map. You may wnt to save it to an other places +and let 'dosdebug' know where to find it: + + + + + + dosemumap /var/lib/dosemu/dosemu.map + + + + + +Normally all debug logging is done _imediately_ (unbuffered). However, +when dumping big amounts of logdata, the dynamic behave of DOSEMU may +change, hence hiding the real problem (or causing a new one) +Using the below switches buffering on and sets the buffer size. + + + + + + logbufsize 0x20000 + + + + + +In addition, you may want to limit the file size of the log file, especially +when you expect huge amounts of data (such as with -D+e). This can be done +via + + + logfilesize 0x200000 + + + + + +which (in this case) will limit the size to 2Mbytes. The default setting is +a file size of 10Mbytes. + + + +When you want to abort DOSEMU from within a configuration file (because +you detected something weird) then do + + + + + + abort + + +or + + + abort "message text" + + + + + +When you want just to warn the user use the following (the message will +get printed to the log file via the debug-flag '+c') + + + + + + warn "message text" + + + + + +When you want to use the CDrom driver and the Linux device is other +then /dev/cdrom you may define + + + + + + cdrom { /dev/xxx } + + + + + +However, you need to include the DOSEMU supplied cdrom.sys into your config.sys +such as + + + device=cdrom.sys + + + + + +If you have more then one cdrom, you have to use the cdrom statement +multiple times such as + + + cdrom { /dev/cdrom } + cdrom { /dev/cdrom2 } + + +and have multiple instancies of the DOS driver too: + + + device=cdrom.sys + device=cdrom.sys 2 + + + + + +In any case you will need MSCDEX.EXE in your autoexe.bat refer to the +DOS devices MSCD0001, MSCD0002 respectively. + + + + + +Code page and character set + + +To select the character set and code page for use with DOSEMU we now +(against earlier versions of DOSEMU) have a separate statement: + + + charset XXX + + +where XXX is one of + + + +ibm + + +With the new (default) unicode keyboard plugin, +the text is processed using cp437->cp437 for the display, +so the font used must be cp437 (eq cp437.f16 on the console). +This is no longer a passthrough to the local character set, +it never really was, but it acted like it. When reading characters +they are assumed to be in iso-8859-1 from the terminal. + + +With the old keyboard code, the text is taken whithout translation, +it is to the user to load a proper DOS font +(cp437.f16, cp850.f16 or cp852.f16 on the console). + + + +latin + + + the text is processed using cp437->iso-8859-1 translation, +so the font used must be iso-8859-1 (eg iso01.f16 on console); +which is the default for unix in western languages countries. + + + +latin1 + + + like latin, but using cp850->iso-8859-1 translation (the +difference between cp437 and cp850 is that cp437 uses some chars for +drawing boxes while cp850 uses them for accentuated letters) + + + +latin2 + + + like latin1 but uses cp852->iso-8859-2 translation, so +translates the default DOS charset of eastern european countries to the +default unix charset for those countries. + + + + +The default one is ``latin'' and if the string is empty, then an automatic +attempt is made: ``ibm'' for remote console and ``latin'' for anything else. +Depending on the charset setting the (below described) keyboard layouts +and/or the terminal behave may vary. You need to know the correct code page +your DOS is configured for in order to get the correct results. +For most western european countries 'latin' should be the correct setting. + + + +With the advent of handling all characters internally to dosemu +things have gotten more flexible, and more interesting. The following +form of the charset option has been added. + + charset { external "iso8859_1" internal "cp437" } + + + + +For external character set the allowable character sets are: + +"cp437", "cp737", "cp773", "cp775", "cp850", "cp852", "cp857", "cp860", +"cp861", "cp862", "cp863", "cp864", "cp865", "cp866", "cp869", "cp874", +"iso8859-1", "iso8859-2", "iso8859-3", "iso8859-4", "iso8859-5", +"iso8859-6", "iso8859-7", "iso8859-8", "iso8859-9", "iso8859-14", +"iso8859-15" + + + + +For the internal character set the allowable character sets are: + +"cp437", "cp737", "cp773", "cp775", "cp850", "cp852", "cp857", "cp860", +"cp861", "cp862", "cp863", "cp864", "cp865", "cp866", "cp869", "cp874" + + + + +The external character set is used to: + + + compute the unicode values of characters coming in from the terminal + + + compute the character set index of unicode characters output to a + terminal display screen. + + + compute the unicode values of characters pasted into dosemu. + + + + + +The internal character set is used to: + + + compute the unicode value of characters of video memory + + + compute the character set index of unicode characters + returned by bios keyboard translation services. + + + + + + + + +Keyboard settings + + +For defining the keyboard layouts you are using there is the "keyboard" +statement such as + + + + + + keyboard { layout us .... } + + +or + + + keyboard { layout us {alt 66=230} ... } + + + + + +The later modifies the US keyboard layout such that it will allow you to +type a character 230 (micro) with right ALT-M. + + + +The same can be done via a "keytable" statement such as + + + + + + keytable keyb-user {alt 66=230} + + + + + +The "keyb-user" is preset with an US layout and is intended to be used +as "buffer" for user defined keyboard tables. + + + +The format of the "{}" modification list (either in the "keyboard" or the +"keytable" statement) is as follows: + + + + + + [submap] key_number = value [,value [,...]] + + + + + +Given we call the above a "definition" then it may be repeated +(blank separated) such as + + + + + + definition [definition [...]] + + + + + +"key_number" is the physical number, that the keybord send to the machine +when you hit the key, its the index into the keytable. "value" may be +any integer between 0...255, a string, or one of the following keywords +for "dead keys" (NOTE: deadkeys have a value below 32, so be careful). + + + + + + dgrave (dead grave) + dacute (dead acute) + dcircum (dead circumflex) + dtilde (dead tilde) + dbreve (dead breve) + daboved (dead above dot) + ddiares (dead diaresis) + dabover (dead above ring) + ddacute (dead double acute) + dcedilla (dead cedilla) + dogonek (dead ogonek) + dcaron (dead caron) + + + + + +With the addition of internal unicode support "value" may be +any integer between 0..65535 (specifying a unicode value), a hexadecimal +integer in the form \uFFFF all 4 hexadecimal digits are mandatory, +a string interpreted in the internal character set. + + + +The implementation of dead keys has changed, and values of the +unicode combining characters are used for dead key character caps. +In particular: + +0x0300 U_COMBINING_GRAVE_ACCENT dgrave +0x0301 U_COMBINING_ACUTE_ACCENT dacute +0x0302 U_COMBINING_CIRCUMFLEX_ACCENT dcircum +0x0303 U_COMBINING_TILDE dtilde +0x0306 U_COMBINING_BREVE dbreve +0x0307 U_COMBINING_DOT_ABOVE daboved +0x0308 U_COMBINING_DIAERESIS ddiares +0x030A U_COMBINING_RING_ABOVE dabover +0x030B U_COMBINING_DOUBLE_ACUTE_ACCENT ddacute +0x0327 U_COMBINING_CEDILLA dcedilla +0x0328 U_COMBINING_OGONEK dogonek +0x030C U_COMBINING_CARON dcaron + + + + +In unicode there is a private range of characters 0xE000 - 0xEFFF that +an application can use as it wishes. In dosemu the exact usage +of this range is defined in src/include/keyboard.h. Current +this range is broke up as follows: + +0xEF00 - 0xEFFF is defined as a pass through to whatever + character set is being displayed. +0xE100 - 0xE1FF are used for special keycaps +0xE200 - 0xE2FF are used for any extra dead keys we may need to make up +0xE300 - 0xE3FF are used for special dosemu function keys + + + + +After a "key_number =" there may be any number of comma separated values, +which will go into the table starting with "key_number", hence all below +examples are equivalent + + + + + + { 2="1" 3="2" } + { 2="1","2" } + { 2="12" } + { 2=0x31,50 } + + + + + +"submap" tells in about something about the shift state for which the +definition is to use or wether we mean the numpad: + + + + + + shift 16="Q" (means key 16 + SHIFT pressed) + alt 16="@" (means key 16 + right ALT pressed (so called AltGr)) + numpad 12="," (means numpad index 12 (depricated)) + ctrl 16=\u0011 (means key 16 + ctrl pressed) + shift-alt 16=\u02A0 (means key 16 + AltGR pressed and shift pressed) + ctrl-alt 209=\uE30A (means key 209 (PGDN) + ctrl pressed and alt pressed) + + + + + +You may even replace the whole table with a comma/blank separated list of +values. In order to make it easy for you, you may use dosemu to create +such a list. The following statement will dump all current key tables out +into a file "kbdtables", which is directly suitable for inclusion into +global.conf (hence it follows the syntax): + + + + + + keytable dump "kbdtables" + + + + + +However, don't put this not into your global.conf, because dosemu will +exit after generating the tablefile. Instead use the -I commandline option +such as + + + + + + $ dosemu.bin -I 'keytable dump "kbdtables"' + + + + + +After installation of dosemu ("make install") you'll find the current dosemu +keytables in /var/lib/dosemu/keymap (and in ./etc/keymap, where they are +copied from). These tables are generated via "keytable dump" and split into +discrete files, such that you may modify them to fit your needs. +You may load them such as + + + + + + $ dosemu.bin -I 'include "keymap/de-latin1"' + + + + + +(when an include file is starting with "keymap/" it is taken out of +/var/lib/dosemu). When there was a keytable previously defined (e.g. +in global.conf), they new one will be take anyway and a warning will +be printed on stderr. + + + +Anyway, you are not forced to modify or load a keytable, and +with the "layout" keyword from the "keyboard" statement, you can specify +your country's keyboard layout. +The following builtin layouts are implemented: + + + + + + finnish us dvorak sf + finnish-latin1 uk sg sf-latin1 + de dk sg-latin1 es + de-latin1 dk-latin1 fr es-latin1 + be keyb-no fr-latin1 po + it no-latin1 sw jp106 + hu hu-cwi hu-latin2 keyb-user + po hr-cp852 hr-latin2 + + + + + +The keyb-user is selected by default if the "layout" keyword is omitted, +and this in fact is identical to us-layout, as long as it got not +overloaded by the "keytable" statement (see above). + + + +The keyword "keybint" allows more accurate of keyboard interrupts, +It is a bit unstable, but makes keyboard work better when set to "on". + + + +The keyword "rawkeyboard" allows for accurate keyboard emulation for +DOS programs, and is only activated when DOSEMU starts up at the +console. It only becomes a problem when DOSEMU prematurely exits +with a "Segmentation Fault" fatal error, because the keyboard would +have not been reset properly. In that case, you would have to run +kbd_mode -a remotely, or use the RESET button. In reality, +this should never happen. But if it does, please do report to the +dosemu development team, of the problem and detailed circumstances, +we're trying our best! If you don't need near complete keyboard +emulation (needed by major software package), set it to "off" + + + +recommended: + + + + + + keyboard { layout us keybint on rawkeyboard off } + + +or + + + keyboard { layout de-latin1 keybint on rawkeyboard on } + + + + + +If you want DOSEMU feed with keystrokes, that are typed in +automagically, then you may define them such as + + + + + + keystroke "cd c:\\mysource\r" + + + + + +You may have any number of 'keystroke' statements, they all will be +concatenated. + + + +This feature however doesn't make much sense _here_ in the +configuration file, instead together with the commandine option -I +you can start dosemu and execute any arbitrary dos command such as + + + + + + # dosemu.bin -D-a -I 'keystroke "c:\rcd \\windows\rwinemu\r"' + + + + + +For more details please look at ./doc/README.batch + + + +Ah, but _one_ sensible useage _here_ is to 'pre-strike' that damned F8 +that is needed for DOS-7.0, when you don't want to edit the msdos.sys: + + + + + + keystroke "\F8;" + + + + + + + +Serial stuff + + +You can specify up to 4 simultaneous serial ports here. +If more than one ports have the same IRQ, only one of those ports +can be used at the same time. Also, you can specify the com port, +base address, irq, and device path! The defaults are: + + + + + + + + + COM1 default is base 0x03F8, irq 4, and device /dev/cua0 + + + + + + COM2 default is base 0x02F8, irq 3, and device /dev/cua1 + + + + + + COM3 default is base 0x03E8, irq 4, and device /dev/cua2 + + + + + + COM4 default is base 0x02E8, irq 3, and device /dev/cua3 + + + + + + + + +If the "com" keyword is omitted, the next unused COM port is assigned. +Also, remember, these are only how you want the ports to be emulated +in DOSEMU. That means what is COM3 on IRQ 5 in real DOS, can become +COM1 on IRQ 4 in DOSEMU! + + + +NOTE: You must have /var/lock for LCK-file generation ! +You may change this path and the lockfile name via the +below 'ttylocks' statement. + + + +Also, as an example of defaults, these two lines are functionally equal: + + + + + + serial { com 1 mouse } + serial { com 1 mouse base 0x03F8 irq 4 device /dev/cua0 } + + + + + +If you want to use a serial mouse with DOSEMU, the "mouse" keyword +should be specified in only one of the serial lines. (For PS/2 +mice, it is not necessary, and device path is in mouse line instead) + + + +Use/modify any of the following if you want to support a modem: +(or any other serial device.) + + + + + + serial { com 1 device /dev/modem } + serial { com 2 device /dev/modem } + serial { com 3 device /dev/modem } + serial { com 4 device /dev/modem } + serial { com 3 base 0x03E8 irq 5 device /dev/cua2 } + + + + + +If you are going to load a msdos mouse driver for mouse support +use/modify one of the following + + + + + + serial { mouse com 1 device /dev/mouse } + serial { mouse com 2 device /dev/mouse } + + + + + +What type is your mouse? Use one of the following. +Use the 'internaldriver' option to try Dosemu internaldriver. +Use the 'emulate3buttons' for 3button mice. + + + + + + mouse { microsoft } + mouse { logitech } + mouse { mmseries } + mouse { mouseman } + mouse { hitachi } + mouse { mousesystems } + mouse { busmouse } + mouse { ps2 device /dev/mouse internaldriver emulate3buttons } + mouse { mousesystems device /dev/mouse internaldriver cleardtr } + + + + + +For tty locking capabilities: + + + +The serial lines are locked by dosemu via usual lock file technique, +which is compatible with most other unix apps (such as mgetty, dip, +e.t.c). However, you carefully need to check _where_ those other apps +expect the lock files. The most common used place is /var/lock. +The dosemu default one is /var/lock. The below defines /var/lock + + + + + + ttylocks { directory /var/lock } + + + + + +Note: you are responsible for ensuring that the directory exists ! +If you want to define the lock prefix stub also, use this one + + + + + + ttylocks { directory /var/lock namestub LCK.. } + + + + + +If the lockfile should contain the PID in binary form (instead of ASCII}, +you may use the following + + + + + + ttylocks { directory /var/lock namestub LCK.. binary } + + + + + + + +Networking Support + + +Turn the following option 'on' if you require IPX/SPX emulation. +Therefore, there is no need to load IPX.COM within the DOS session. +The following option does not emulate LSL.COM, IPXODI.COM, etc. +NOTE: YOU MUST HAVE IPX PROTOCOL ENABLED IN KERNEL !! + + + + + + ipxsupport off + + + + + +Enable Novell 8137->raw 802.3 translation hack in new packet driver. + + + + + + pktdriver novell_hack + + + + + + + +Terminals + + +This section applies whenever you run DOSEMU remotely or in an xterm. +Color terminal support is now built into DOSEMU. Skip this section for +now to use terminal defaults, until you get DOSEMU to work. + + + +There are a number of keywords for the terminal { } configuration line. + + + + + + +color + + +Enable or disable color terminal support. One of ``on'' (default) or +``off''. + + + +updatefreq + + +A number indicating the frequency of terminal updates of the screen. +The smaller the number, the more frequent. A value of 20 gives a +frequency of about one per second, which is very slow. However, more +CPU time is given to DOS applications when updates are less frequent. +A value of 4 (default) is recommended in most cases, but if you have a +fast system or link, you can decrease this to 0. + + + +escchar + + +A number (ascii code below 32) that specifies the control character +used as a prefix character for sending alt, shift, ctrl, and function +keycodes. The default value is 30 which is Ctrl-^. So, for example, + + + F1 is 'Ctrl-^1', Alt-F7 is 'Ctrl-^s Ctrl-^7'. + For online help, press 'Ctrl-^h' or 'Ctrl-^?'. + + + + + + + + + +Use the following to enable the IBM character set. + + + + + + terminal { charset ibm color on } + + + + + +Use this for color xterms or rxvt's with no IBM font, with only 8 colors. + + + + + + terminal { charset latin color on } + + + + + +Use this for color xterms or rxvt's with IBM font, with only 8 colors. + + + + + + terminal { charset ibm color on } + + + + + +More detailed line for user configuration: + + + + + + terminal { charset latin updatefreq 4 color on } + + + + + + + +X Support settings + + +If DOSEMU is running in its own X-window (not xterm), you may need +to tailor it to your needs. Valid keywords for the X { } config line: + + + + + + +updatefreq + + +A number indicating the frequency of X updates of the screen. +The smaller the number, the more frequent. A value of 20 gives a +frequency of about one per second, which is very slow. However, more +CPU time is given to DOS applications when updates are less frequent. +The default is 8. + + + +display + + +The X server to use. If this is not specified, dosemu will use +the DISPLAY environment variable. (This is the normal case) The default +is ":0". + + + +title + + +What you want dosemu to display in the title bar of its window. +The default is "dosemu". + + + +icon_name + + +Used when the dosemu window is iconified. The default is "dosemu". + + + +keycode + + +Used to give Xdos access to keycode part of XFree86. The default is off. + + + +NOTE: + + + + + + You should _not_ use this when using X remotely +(the remote site may have other raw keyboard settings). + + + + + + If you use "keycode", you also must define an +appropriate keyboard layout (see above). + + + + + + If you do not use "keycode" then under X a neutral keyboard +layout is forced ( keyboard {layout us} ) regardless of +what you have set above. + + + + + +Anyway, a cleaner way than using "keycode" is to let the X-server +fiddle with keyboard translation and customize it via .xmodmaps. + + + +blinkrate + + +A number which sets the blink rate for the cursor. The default is 8. + + + +font + + +Used to pick a font other than vga (default). Must be monospaced. + + + +mitshm + + +Use shared memory extensions. The default is ``on''. + + + +sharecmap + + +Used to share the colormap with other applications in graphics mode. +If not set, a private colormap is used. The default is off. + + + +fixed_aspect + + +Set fixed aspect for resize the graphics window. One of ``on'' (default) +or ``off''. + + + +aspect_43 + + +Always use an aspect ratio of 4:3 for graphics. The default is +``floating''. + + + +lin_filt + + +Use linear filtering for >15 bpp interpolation. The default is off. + + + +bilin_filt + + +Use bi-linear filtering for >15 bpp interpolation. The default is off. + + + +mode13fact + + +A number which sets the initial size factor for video mode 0x13 (320x200). +The default is 2. + + + +winsize + + +A pair of numbers which set the initial width and height of the window to a fixed +value. The default is to float. + + + +gamma + + +Set value for gamma correction, a value of 100 means gamma 1.0. The default +is 100. + + + +vgaemu_memsize + + +Set the size (in Kbytes) of the frame buffer for emulated vga under X. The +default is 1024. + + + +lfb + + +Enable or disable the linear frame buffer in VESA modes. The default is ``on''. + + + +pm_interface + + +Enable or disable the protected mode interface for VESA modes. The default is ``on''. + + + +mgrab_key + + +String, name of KeySym name to activate mouse grab. Default is `empty' +(no mouse grab). A possible value could be "Home". + + + +vesamode + + +Define a VESA mode. Two variants are supported: vesamode width height +and vesamode width height color_bits. The first adds the specified +resolution in all supported color depths (currently 8, 15, 16, 24 and 32 +bit). + + + + + + + +Recommended X statement: + + + + + + X { updatefreq 8 title "DOS in a BOX" icon_name "xdos" } + + + + + + + +Video settings ( console only ) + + +!!WARNING!!: A LOT OF THIS VIDEO CODE IS ALPHA! IF YOU ENABLE GRAPHICS +ON AN INCOMPATIBLE ADAPTOR, YOU COULD GET A BLANK SCREEN OR MESSY SCREEN +EVEN AFTER EXITING DOSEMU. JUST REBOOT (BLINDLY) AND THEN MODIFY CONFIG. + + + +Start with only text video using the following line, to get started. +then when DOSEMU is running, you can set up a better video configuration. + + + + + + video { vga } Use this line, if you are using VGA + video { cga console } Use this line, if you are using CGA + video { ega console } Use this line, if you are using EGA + video { mda console } Use this line, if you are using MDA + + + + + +Notes for Graphics: + + + + + + If your VGA-Bios resides at E000-EFFF, turn off video BIOS shadow +for this address range and add the statement vbios_seg 0xe000 +to the correct vios-statement, see the example below + + + + + + If your VBios size is only 32K you set it with vbios_size 0x8000, +you then gain some space for UMB or hardware ram locations. + + + + + + Set "allowvideoportaccess on" earlier in this configuration file +if DOSEMU won't boot properly, such as hanging with a blank screen, +beeping, leaving Linux video in a bad state, or the video card +bootup message seems to stick. + + + + + Video BIOS shadowing (in your CMOS setup) at C000-CFFF must be disabled. + + + +CAUTION: TURN OFF VIDEO BIOS SHADOWING BEFORE ENABLING GRAPHICS! +This is not always necessary, but a word to the wise +shall be sufficient. + + + + + If you have a dual-monitor configuration (e.g. MDA as second display), +you then may run CAD programs on 2 displays or let play your debugger +on the MDA while debugging a graphics program on the VGA (e.g TD -do ). +You also may switch to the MDA display by using the DOS command +mode mono (mode co80 returns to your normal display). +This feature can be enabled by the switch "dualmon" like this: + + + video { vga console graphics dualmon } + + +and can be used on a xterm and the console, but of course not, if you +have the MDA as your primary display. +You also must set USE_DUALMON 1 in include/video.h. +NOTE: Make sure no more then one process is using this feature ! +( you will get funny garbage on your MDA display. ) +Also, you must NOT have the dualmon-patches for kernel applied +( having the MDA as Linux console ) + + + + + If you want to run dosemu in situations when human doesn't sit at console +(for instance to run it by cron) and want console option be enabled +you should use option forcevtswitch. + + + { vga console forcevtswitch } + + +Without the option dosemu waits for becoming virtual terminal +on which dosemu is run active (i.e. user must press Alt-F?). +With this option dosemu perform the switch itself. +Be careful with this option because with it user sat at console +may face with unexpected switch. + + + + + + + + +It may be necessary to set this to "on" if DOSEMU can't boot up properly +on your system when it's set "off" and when graphics are enabled. +Note: May interfere with serial ports when using certain video boards. + + + + + + allowvideoportaccess on + + + + + +Any 100% compatible standard VGA card MAY work with this: + + + + + + video { vga console graphics } + + + + + +If your VGA-BIOS is at segment E000, this may work for you: + + + + + + video { vga console graphics vbios_seg 0xe000 } + + + + + +Trident SVGA with 1 megabyte on board + + + + + + video { vga console graphics chipset trident memsize 1024 } + + + + + +Diamond SVGA (not S3 chip) + + + + + + video { vga console graphics chipset diamond } + + + + + +Cirrus SVGA + + + + + + video { vga console graphics chipset cirrus } + + + + + +ET4000 SVGA card with 1 megabyte on board: + + + + + + video { vga console graphics chipset et4000 memsize 1024 } + + +or + + + video { vga console graphics chipset et4000 memsize 1024 vbios_size 0x8000 } + + + + + +S3-based SVGA video card with 1 megabyte on board: + + + + + + video { vga console graphics chipset s3 memsize 1024 } + + + + + +Avance Logic (ALI) 230x SVGA + + + + + + video { vga console graphics chipset avance } + + + + + +For ATI graphic mode + + + + + + ports { 0x1ce 0x1cf 0x238 0x23b 0x23c 0x23f 0x9ae8 0x9ae9 0x9aee 0x9aef } + + + + + +Matrox millenium: + + + + + + video { vga console graphics chipset matrox } + + + + + +VGA-cards with a WD(Paradise) chip: + + + + + + video { vga console graphics chipset wdvga } + + + + + + + +Memory settings + + +These are memory parameters, stated in number of kilobytes. +If you get lots of disk swapping while DOSEMU runs, you should +reduce these values. + + + +umb_max is a new parameter which tells DOSEMU to be more aggressive +about finding upper memory blocks. The default is 'off'. + + + +To be more aggressive about finding XMS UMB blocks use this: + + + + + + umb_max on + + + + + +To be more secure use 'secure on'. If "on", then it disables DPMI access +to dosemu code and also disables execution of dosemu supplied 'system' +commands, which may execute arbitrary Linux-commands otherwise. +The background is, that DPMI clients are allowed to create selectors +that span the whole user space, hence may hack into the dosemu code, +and (when dosemu runs root or is suid root) can be a security hole. +"secure on" closes this hole, though this would very likely also disable +some dos4gw games :(. +Therfore NOTE: You may not be able to run some DPMI programs, hence, +before reporting such a program as 'not running', first try to set 'secure off'. + + + + + + secure on # "on" or "off" + + + + + +The below enables/disables DPMI and sets the size of DPMI memory. + + + + + + dpmi 4086 # DPMI size in K, or "off" + + + + + +The best solution (security wise), however, is to run dosemu non-suid root +under X (which is possible since dosemu-0.97.10). Under X most of the +things we would need to run under root privilidges aren't needed, as the +X server supports them. And giving DPMI access to non-suid root dosemu +is not at all critical. You may forbid some users to use an eventually +available suid root binary by setting `nosuidroot' in /etc/dosemu.users. + + + +XMS is enabled by the following statement + + + + + + xms 1024 # XMS size in K, or "off" + + + + + +For ems, you now can set the frame to any 16K between 0xc800..0xe000 + + + + + + ems 1024 # EMS size in K, or "off" + + +or + + + ems { ems_size 1024 ems_frame 0xe000 } + + +or + + + ems { ems_size 2048 ems_frame 0xd000 } + + + + + +If you have adapters, which have memory mapped IO, you may map those regions +with hardware_ram { .. }. You can only map in entities of 4k, you give the +address, not the segment. +The entry below maps 0xc8000..0xc8fff and 0xcc000..0xcffff: + + + + + + hardware_ram { 0xc8000 range 0xcc000 0xcffff } + + + + + +With the entry below you define the maximum conventional RAM to show apps: + + + + + + dosmem 640 + + + + + + + +IRQ passing + + +The irqpassing statement accepts IRQ values between 3..15, +if using the { .. } syntax each value or range can be prefixed +by the keyword use_sigio to monitor the IRQ via SIGIO. +If this is missing the IRQ is monitored by SIGALRM. + + + +Use/modify one of the below statements + + + + + + irqpassing off # this disables IRQ monitoring + irqpassing 15 + irqpassing { 15 } + irqpassing { use_sigio 15 } + irqpassing { 10 use_sigio range 3 5 } + + + + + + + +Port Access + + +WARNING: GIVING ACCESS TO PORTS IS BOTH A SECURITY CONCERN AND +SOME PORTS ARE DANGEROUS TO USE. PLEASE SKIP THIS SECTION, AND +DON'T FIDDLE WITH THIS SECTION UNLESS YOU KNOW WHAT YOU'RE DOING. + + + +These keywords are allowable on a "ports" line. + + + + + + +range addr1 addr2 + + +This allows access to this range of ports + + + +ormask value + + +The default is 0 + + + +andmask value + + +The default is 0xffff + + + +rdonly|wronly|rdwr + + +This specifies what kind of access to allow to the ports. The default +is "rdwr" + + + +fast + + +Put port(s) in the ioperm bitmap (only valid for ports below 0x400) +An access doesn't trap and isn't logged, but as vm86() isn't interrupted, +it's much faster. The default is not fast. + + + +device name + + +If the ports are registered, open this device to block access. The open() +must be successfull or access to the ports will be denied. If you know +what you are doing, use /dev/null to fake a device to block + + + + + + + + + + ports { 0x388 0x389 } # for SimEarth + ports { 0x21e 0x22e 0x23e 0x24e 0x25e 0x26e 0x27e 0x28e 0x29e } # for jill + + + + + + + +Speaker + + +These keywords are allowable on the "speaker" line: + + + + + + +native + + + Enable DOSEMU direct access to the speaker ports. + + + +emulated + + + Enable simple beeps at the terminal. + + + +off + + + Disable speaker emulation. + + + + + + + +Recommended: + + + + + + speaker off + + + + + + + +Hard disks + + +WARNING: DAMAGE MIGHT RESULT TO YOUR HARD DISK (LINUX AND/OR DOS) +IF YOU FIDDLE WITH THIS SECTION WITHOUT KNOWING WHAT YOU'RE DOING! + + + +The best way to get started with DOSEMU is to use use the bootdisk method +(see doc/README.txt, chapter "Disks, boot directories and floppies"). +Fiddling with hdimages and real harddisk is obsolete now. + + + +As a last resort, if you want DOSEMU to be able to access a DOS partition, the +safer type of access is "partition" access, because "wholedisk" +access gives DOSEMU write access to a whole physical disk, +including any vulnerable Linux partitions on that drive! + + + +IMPORTANT + + + +You must not have LILO installed on the partition for dosemu to boot off. +As of 04/26/94, doublespace and stacker 3.1 will work with wholedisk +or partition only access. Stacker 4.0 has been reported to work with +wholedisk access. + + + +Please read the documentation in the "doc" subdirectory for info +on how to set up access to real hard disk. + + + +These are meanings of the keywords: + + + + + + +image + + + specifies a hard disk image file. + + + +partition + + + specifies partition access, with device and partition number. + + + +wholedisk + + + specifies full access to entire hard drive. + + + +readonly + + + for read only access. A good idea to set up with. + + + +diskcyl4096 + + + INT13 support for more then 1024 cylinders, bits 6/7 of +DH (head) used to build a 12 bit cylinder number. + + + +bootfile + + + to specify an image of a boot sector to boot from. + + + + + + + +Use/modify one (or more) of the folling statements: + + + + + + disk { image "/var/lib/dosemu/hdimage" } # use diskimage file. + disk { partition "/dev/hda1" readonly } # 1st partition on 1st IDE. + disk { partition "/dev/hda1" bootfile "/var/lib/bootsect.dos" } + # 1st partition on 1st IDE + # booting from the specified + # file. + disk { partition "/dev/hda6" readonly } # 6th logical partition. + disk { partition "/dev/sdb1" readonly } # 1st partition on 2nd SCSI. + disk { wholedisk "/dev/hda" } # Entire disk drive unit + + + + + +Recommended: + + + + + + disk { image "/var/lib/dosemu/hdimage" } + + + + + + + +DOSEMU boot + + +Use the following option to boot from the specified file, and then +once booted, have bootoff execute in autoexec.bat. Thanks Ted :-). +Notice it follows a typical floppy spec. To create this file use: + + + + + +dd if=/dev/fd0 of=/var/lib/dosemu/bdisk bs=16k + + + + + + + + bootdisk { heads 2 sectors 18 tracks 80 threeinch file /var/lib/dosemu/bdisk } + + + + + +Specify extensions for the CONFIG and AUTOEXEC files. If the below +are uncommented, the extensions become CONFIG.EMU and AUTOEXEC.EMU. +NOTE: this feature may affect file naming even after boot time. +If you use MSDOS 6+, you may want to use a CONFIG.SYS menu instead. + + + + + + EmuSys EMU + EmuBat EMU + + + + + + + +Floppy disks + + +This part is fairly easy. Make sure that the first (/dev/fd0) and +second (/dev/fd1) floppy drives are of the correct size, "threeinch" +and/or "fiveinch". A floppy disk image can be used instead, however. + + + +FOR SAFETY, UNMOUNT ALL FLOPPY DRIVES FROM YOUR FILESYSTEM BEFORE +STARTING UP DOSEMU! DAMAGE TO THE FLOPPY MAY RESULT OTHERWISE! + + + +Use/modify one of the below: + + + + + + floppy { device /dev/fd0 threeinch } + floppy { device /dev/fd1 fiveinch } + floppy { heads 2 sectors 18 tracks 80 + threeinch file /var/lib/dosemu/diskimage } + + + + + +If floppy disk speed is very important, uncomment the following +line. However, this makes the floppy drive a bit unstable. This +is best used if the floppies are write-protected. +Use an integer value to set the time between floppy updates. + + + + + + FastFloppy 8 + + + + + + + +Printers + + +Printer is emulated by piping printer data to a file or via a unix +command such as "lpr". Don't bother fiddling with this configuration +until you've got DOSEMU up and running already. + + + +NOTE: Printers are assigned to LPT1:, LPT2:, and LPT3: on a one for +one basis with each line below. The first printer line is assigned +to LPT1:, second to LPT2:, and third to LPT3:. If you do not specify +a base port, the emulator will setup the bios to report 0x378, 0x278, +and 0x3bc for LPT1:, LPT2:, and LPT3: respectively. + + + +To use standard unix lpr command for printing use this line: + + + + + + printer { options "%s" command "lpr" timeout 20 } + + + + + +And for any special options like using pr to format files, +add it to the options parameter: + + + + + + printer { options "-p %s" command "lpr" timeout 10 } pr format it + + + + + +To just have your printer output end up in a file, use the following line: + + + + + + printer { file "lpt3" } + + + + + +If you have a DOS application that is looking to access the printer +port directly, and uses the bios LPT: setting to find out the port to use, +you can modify the base port the bios will report with the following: + + + + + + printer { options "%s" command "lpr" base 0x3bc } + + + + + +Be sure to also add a port line to allow the application access to +the port: + + + + + + ports { device /dev/lp0 0x3bc 0x3bd 0x3be } + + + + + +NOTE: applications that require this will not interfere with applications +that continue to use the standard bios calls. These applications will +continue to send the output piped to the file or unix command. + + + + + +Sound + + +The sound driver is capable of emulating Sound Blaster cards up to +and including the SB16. It works for most programs, but there are +a few that cause problems. + + + + + + +sb_base + + + base address of the SB (HEX) + + + +sb_irq + + + IRQ for the SB + + + +sb_dma + + + Low 8-bit DMA channel for the SB + + + +sb_hdma + + + High 16-bit DMA channel for the SB + + + +sb_dsp + + + Path to the sound device + + + +sb_mixer + + + path to the mixer control + + + +mpu_base + + + base address for the MPU-401 chip (HEX) (Not Implemented) + + + + + + + +Use this to disable sound support even if it is configured in + + + + + + sound_emu off + + + + + +Linux defaults + + + + + + sound_emu { sb_base 0x220 sb_irq 5 sb_dma 1 sb_hdma 5 sb_dsp /dev/dsp + sb_mixer /dev/mixer mpu_base 0x330 } + + + + + +NetBSD defaults + + + + + + sound_emu { sb_base 0x220 sb_irq 5 sb_dma 1 sb_hdma 5 sb_dsp /dev/sound + sb_mixer /dev/mixer mpu_base 0x330 } + + + + + + + + + + diff --git a/src/doc/README/configuration.sgml b/src/doc/README/configuration.sgml new file mode 100644 index 0000000..b8cf0a0 --- /dev/null +++ b/src/doc/README/configuration.sgml @@ -0,0 +1,53 @@ + + +
+ + + +DOSEMU Configuration + + +The DOSEMU team +Edited by Alistair MacDonald + + + + +<alistair@slitesys.demon.co.uk> + + + + + + +For DOSEMU v1.0 pl0.0 + + + + +DOSEMU Configuration + + +Running './setup-dosemu' +from this directory will allow you to configure both the compilation and +the run-time aspects of DOSEMU. When changing the run-time settings of +DOSEMU you can set both the system wide settings and your personal settings. +Even better, the program will automatically tell you your current settings +or suggest defaults where possible. + + + +The program needs Tcl/Tk, take care to have it available on your system. + + + +Comments, and bug reports to me, Alistair MacDonald +<alistair@slitesys.demon.co.uk> + + + + +
diff --git a/src/doc/README/cpuemu b/src/doc/README/cpuemu new file mode 100644 index 0000000..6ad667a --- /dev/null +++ b/src/doc/README/cpuemu @@ -0,0 +1,175 @@ + +Software X386 emulation + + +This section written in a hurry by Alberto Vignani +<vignani@mbox.vol.it> +, Oct 20, 1997 + + + +The CPU emulator + + +The CPU emulator has been derived from +<the Twin Willows libraries>. +Only the relevant parts of the library, namely the /intp32 subdirectory and +the needed include files, have been extracted from the Twin sources into +the src/twin directory. The Twin reference version is 3.1.1. +In the Twin code, changes needed for the dosemu interface have been marked with + + + #ifdef DOSEMU + + + + + +Here is a summary of the changes I made in the Twin libraries: + + + + + + I added vm86 mode, and related exception handling. + + + + + + I made a first attempt to entry-point symmetry; the final goal is +to have an 'invoke_code32' in interp_32_32.c, which can reach the +16-bit code using 0x66,0x67 prefixes, the same way the 16-bit code +is currently doing the other way. The variables 'code32' and 'data32' +are used for prefix control. + + + + + + some optimizations to memory access and multiplication code for +little-endian machines and GNU compiler. + + + + + + dosemu-style debug output; this is the biggest part of the patch + + + + + + bugfixes. These are NOT marked with #ifdef DOSEMU! + + + + + + + + +The second part of the cpuemu patch is the interface to dosemu, which is +controlled by the X86_EMULATOR macro. This macro was probably part of a +very old attempt to interface dosemu with Bochs, I deleted the old code +and replaced it with the Twin interface. + + + +The X86_EMULATOR macro enables the compilation of the two files (cpu-emu.c +and emu-utils.c) in the src/emu-i386/intp32 directory, which contain the +vm86 emulator call (taken from the kernel sources) and some utility/debug +functions. These files are kept separate from the Twin directory but need +it to compile. + + + +For controlling the emulator behaviour, the file include/cpu-emu.h provides +three macros: + + + +DONT_START_EMU + + + if undefined, the emulator starts immediately; +otherwise, a call to int 0xe6 al=0x90 is required to switch from +the standard vm86 to it. To switch in and out from the emulator, +the small utilities 'ecpuon.com' and 'ecpuoff.com' are provided. + + + +TRACE_HIGH + + + controls the memory areas you want to include into the +debug trace. The default value excludes the video BIOS and the HMA, +but feel free to change it following your needs. + + + +VT_EMU_ONLY + + + if defined, use of the emulator forces VT console mode, by +ignoring the 'console' and 'graphics' statements in the video +config line. + + + + + + + +To enable the CPU emulator add + + + cpuemu on + + +to compiletime-settings, or pass + + + --enable-cpuemu + + +to configure. + + + +To use the emulator, put + + + cpu emulated + + +into /etc/dosemu.conf. Or start dosemu with -I 'cpu emulated'. + + + +The 'e' flag was added to the debug control string, it has currently a +value range from 1 to 4 and controls the level of detail the emulator +writes into the dosemu debug log. WARNING - logs greater than 100Mbytes +are the rule with cpu-emu!!!. As a safety measure, 'e' is not automatically +added to the debug flags when you use 'a'; the 'e' parameter must be +explicitly added. In addition, there is a new configuration parameter for +/etc/dosemu.conf: + + + logfilesize value + + +This will limit the file size of the logfile. Once the limit is reached, +it truncates the file to zero and continues writing to it. + + + + + + diff --git a/src/doc/README/dma b/src/doc/README/dma new file mode 100644 index 0000000..9a57d75 --- /dev/null +++ b/src/doc/README/dma @@ -0,0 +1,183 @@ + +DMA Code + + +Current DOSEMU DMA code + + +Unfortunately I haven't documented this yet. However, the current code has been +completely rewritten from this. + + + + + +Original DOSEMU DMA code + + + + + DOSEMU DMA code + Copyright (C) 1995 Joel N. Weber II + + This dma code 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 2 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. + + + + + +This is intended to be a reasonably complete implementation of dma. However, +the following has been omitted: + + + + + + There is no command register. Half the bits control hardware details, +two bits are related to memory to memory transfer, and two bits have +misc. functions that might be useful execpt that they are global, so +probably only the bios messes with them. And dosemu's psuedo-bios +certainly doesn't need them. + + + + + + 16 bit DMA isn't supported, because I'm not quite sure how it's supposed +to work. DMA channels 4-7 are treated the same way as 0-3 execpt that +they have different address. Also, my currnet goal is a sb pro emulator, +and the sb pro doesn't use 16 bit DMA. So it's really a combination of +lack of information and lack of need. + + + + + + Verify is not supported. It doesn't actually move any data, so how +can it possibly be simulated? + + + + + + The mode selection that determines whether to transfer one byte at a +time or several is ignored because we want to feed the data to the kernel +efficiently and let it control the speed. + + + + + + Cascade mode really isn't supported; however all the channels are +available so I don't consider it nessisary. + + + + + + Autoinitialization may be broken. From the docs I have, the current and +reload registers might be written at the same time??? I can't tell. + + + + + + The docs I have refer to a "temporary register". I can't figure out +what it is, so guess what: It ain't implemented. (It's only a read +register). + + + + + + + + +The following is known to be broken. It should be fixed. Any volunteers? :-) + + + + + + + + + dma_ff1 is not used yet + + + + + + Address decrement is not supported. As far as I know, there's no +simple, effecient flag to pass to the kernel. + + + + + + I should have used much more bitwise logic and conditional +expressions. + + + + + + + + +This is my second real C program, and I had a lot of experience in Pascal +before that. + + + +Adding DMA devices to DOSEMU + + +Read include/dma.h. In the dma_ch[] struct, you'll find some fields that +don't exist on the real DMA controller itself. Those are for you to fill in. +I trust that they are self-explainatory. + + + +One trick that you should know: if you know you're writing to a device which +will fill up and you want the transfer to occur in the background, open the +file with O_NONBLOCK. + + + + + +References + + +PC Game Programers Encyclopedia +ftp://teeri.oulu.fi/pub/msdos/programming/gpe/ + + + +The Intel Microprocessors: 8086/8088, 80186, 80286, 80386, 80486, +Barry B. Brey,ISBN 0-02-314250-2,1994,Macmillan + + + +(The only reason I use this book so extensively is it's the only one like it +that I can find in the Hawaii State Library System :-) + + + + + + + + diff --git a/src/doc/README/doc b/src/doc/README/doc new file mode 100644 index 0000000..a2cbe07 --- /dev/null +++ b/src/doc/README/doc @@ -0,0 +1,394 @@ + + + +Documenting DOSEMU + + +Effective from dosemu-1.0.1 the documentation format for DOSEMU is DocBook. Currently this is DocBook 3.0, but formatted in lower case for XML compatibility. This will probably be migrated to DocBook 4, but the changes are unlikely to affect most authors. + + + +Every programmer can handle the few basic DocBook commands +that are need to make some really good documents! There are many similarities with HTML, which is hardly surprising as they are both SGML narkup languages. The source to this document may make useful reading (This is the file ./src/doc/README/doc) + + + +Sections + + +There are 5 section levels you can use. They are all automatically +numbered. Your choices are: + + + + + + +<sect1> + + + A top level section. This is indexed. + + + +<sect2> + + + A 2nd level section. This is indexed. + + + +<sect3> + + + A 3rd level section. This is indexed. + + + +<sect4> + + + A 4th level section. This is not indexed. + + + +<sect5> + + + A 5th level section. This is not indexed. + + + + + + + + + + + + +You cannot skip section levels on the way up or down (ie you must go +<sect>,<sect1>,<sect2> and not <sect>,<sect2>). +You must also close each section when you finish it. This includes the enclosing sections. + + + + + +The title of the section must be enclosed within <title>...</title> tags. + + + + + +Each paragraph of text must be enclosed within <para>...</para>. + + + + + + + + + + +Emphasising text + + +There is only one way to do this. + + + + + + +<em>...</em> + + + Emphasises text like this. + + + + + + + + +Lists + + +Here we have 3 useful types: + + + + + + +itemizedList + + + A standard bulletted list + + + +orderedList + + + A "numbered" list + + + +variableList + + + A description list (like this) + + + + + + + +For ``itemizedList'' and ``orderedList'' the items are marked with +<listitem>. eg: + + +<itemizedList> +<listitem><para> item 1 </para></listitem> +<listitem><para> item 2 </para></listitem> +</itemize> + + + + + +For the ``variableList'' the items have two additional markers. Each entry in the list is enclosed in <varlistentry>...</varlistentry>. Each ``term'' is marked using <term>...</term> and then the content is marked with listitem, as above. eg: + + +<variableList> +<varlistentry><term>item 1</term><listitem><para> is here</para></listitem></varlistentry> +<varlistentry><term>item 2</term><listitem><para> is here!</para></listitem></varlistentry> +</variableList> + + + + + + + +Quoting stuff + + +If you want to quote a small amount use <literal>...</literal>. eg: + + + +This is <literal>./src/doc/README/doc</literal> + + + +To quote a large section, such as part of a file or an example use +<screen>. eg: + + +<screen> +... +</screen> + + + + + +You still need to ``quote'' any <, > or & characters although most other +characters should be OK. + + + + + +Special Characters + + +Obviously some characters are going to need to be quoted. In general these are +the same ones as HTML. The most common ones are: + + + + + +< + + +&lt; + + + + +> + + +&gt; + + + + +& + + +&amp; + + + + + + + + + + +Cross-References & URLs + + +One of the extra feature that this lets us do is include URLs and +cross-references. + + + +Cross-References + + +These have 2 parts: a label, and a reference. + + + +The label is <... id="...">. ie an id on the element you want to refer to. + + + +The reference is <xref linkend="..." >. The linkend should refer to an id somewhere else in the same document (not necessarily the +same file as the README's consist of multiple files). The section naem will be inserted automatically at site of the <xref>. + + + + + +URLs + + +This is <ulink url="...">...</ulink> +. The url will be active only for HTML. The text will be used at all times. eg: + + + + + +See the <ulink url="http://www.dosemu.org/">DOSEMU website</ulink>. + + +Which will appear as See the DOSEMU website. + + + + + +Email addresses + + +Whilst it is possible to do insert email addresses as URLs there is a better way. Simply enclose the address in <email>...</email> +. eg: + + + + + +<email>alistair@slitesys.demon.co.uk</email> + + + + + + + +References + + +Here are a few other places to find information about writing using DocBook. + + + + + +http://www.docbook.org/tdg/ + +``DocBook: The Definitive Guide'' is a complete reference to DocBook. It is an O'Reilly book, which is also readable online. + + + + +http://metalab.unc.edu/godoy/using-docbook/using-docbook.html + +This is a basic guide to DocBook. + + + + +http://www.linuxdoc.org/HOWTO/HOWTO-HOWTO/index.html + +Part of the Linux Documentation Project (LDP) this is a guide to writing HOWTO's specifically, but covers DocBook in general as this is now the preferred MarkUp for the LDP. + + + + + + + + + diff --git a/src/doc/README/dosdebug b/src/doc/README/dosdebug new file mode 100644 index 0000000..b01cf23 --- /dev/null +++ b/src/doc/README/dosdebug @@ -0,0 +1,653 @@ + +DOSEMU debugger v0.6 + + +This section written on 98/02/08. +Send comments to Max Parke +<mhp@light.lightlink.com> and to Hans Lermen +<lermen@fgan.de> + + + +Introduction + + +This is release v0.6 of the DOSEMU debugger, with the +following features: + + + + + + interactive + + + + + + DPMI-support + + + + + + display/disassembly/modify of registers and memory (DOS and DPMI) + + + + + display/disassembly memory (dosemu code and data) + + + + + + read-only access to DOSEMU kernel via memory dump and disassembly + + + + + + uses /usr/src/dosemu/dosemu.map for above (can be changed via +runtime configuration) + + + + + + + + + + breakpoints (int3-style, breakpoint on INT xx and DPMI-INT xx) + + + + + + DPMI-INT breakpoints can have an AX value for matching. +(e.g. 'bpintd 31 0203' will stop _before_ DPMI function 0x203) + + + + + + + + + + + breakpoints via monitoring DOSEMU's logoutput using regular +expressions + + + + + + on-the-fly changing amount of logoutput (-D debugflags) + + + + + + (temporary) redirect logoutput to debugger terminal. + + + + + + single stepping (traceing). + + + + + + dump parts of DOS mem to file. + + + + + + symbolic debugging via microsoft linker .MAP file support + + + + + + access is via the 'dosdebug' client from another virtual console. +So, you have a "debug window" and the DOS window/keyboard, etc. are +undisturbed. VM86 execution can be started, stopped, etc. + + + + + + If dosemu 'hangs' you can use the 'kill' command from dosbugger to +recover. + + + + + + code base is on dosemu-0.97.2.1 + + + + + + + + + + +Usage + + +To run, start up DOSEMU. Then switch to another virtual console +(or remote login or use another xterm) and do: + + + dosdebug + + +If there are more then one dosemu process running, you will need +to pass the pid to dosdebug, e.g: + + + + + + dosdebug 2134 + + + + + +NOTE: You must be the owner of the running dosemu to 'debug-login'. + + + +You should get connected and a banner message. +If you type 'q', only the terminal client will terminate, +if you type 'kill', both dosemu and the terminal client will be +terminated. + + + +It may be desirable to debug the DOS or its drivers itself during startup, +to realize that you need to synchronize DOSEMU and your debugger terminal. +This can be done using the -H1 command line option of DOSEMU: + + + $ dosemu -H1 + + +DOSEMU will then lock before jumping into the loaded bootsector waiting +for dosdebug to connect. Once connected you are in `stopped' state +and can set breakpoints or singlestep through the bootstrap code. + + + + + +Commands + + +See mhpdbgc.c for code and cmd table. + + + +(all numeric args in hex) + + + +? + + + Print a help page + + + +q + + + Quit the debug session + + + +kill + + + Kill the dosemu process +(this may take a while, so be patient) +See also + + + +console n + + + Switch to console n + + + +r + + + list regs + + + +r reg val + + + change contents of 'reg' to 'val' +(e.g: r AX 1234) + + + +e ADDR valuelist + + + modify memory (0-1Mb) +`ADDR' maybe just a `-' (minus), then last (incremented) address from +a previous `e' or `ed' command is used (this allowes consecutive writes). + + + +`valuelist' is a blank separated list of + + + +hexnumber + + + such as 0F or C800 + + + +char + + + enclosed in single quotes such as 'A' or 'b' + + + +register + + + any valid register symbol, in this case the current value +(and size) of that registe is take (e.g AX is 2 bytes, +EAX is 4 bytes) + + + +string + + + enclosed in double quotes such "this is a string" + + + + +The default size of each value is one byte (except registers), this +size can be overridden by suffixing a `W' (word, 2 bytes) or `L' (long, 4 +bytes) such as C800w or F0008123L + + + +ed ADDR valuelist + + + same as above `e' command, except that the +numbers are expected as decimals per default. To write a hexvalue with +`ed' you may prefix it with `0x' as in C or write an octal value +prefixing a `0'. + + + +d ADDR SIZE + + + dump memory (no limit) + + + +u ADDR SIZE + + + unassemble memory (no limit) + + + +g + + + go (if stopped) + + + +stop + + + stop (if running) + + + +mode 0|1|+d|-d + + + set mode (0=SEG16, 1=LIN32) for u and d commands ++d enables DPMI mode (default on startup), +-d disables DPMI mode. + + + +t + + + single step (may jump over IRET or POPF) + + + +tc + + + single step, loop forever until key pressed + + + +tf + + + single step, force over IRET and POPF +NOTE: the scope of 't' 'tf' or a 'come back for break' +is either 'in DPMI' or realmode, depending on +wether a DPMI-client is active (in_dpmi). + + + +r32 + + + dump regs in 32 bit format + + + +bp addr + + + set int3 style breakpoint +NOTE: the scope is defined wether a DPMI-client is active +(in_dpmi). The resulting 'come back' will force +the mode that was when you defined the breakpoint. + + + +bc breakp.No. + + + Clear a breakpoint. + + + +bpint xx + + + set breakpoint on INT xx + + + +bcint xx + + + clr breakpoint on INT xx + + + +bpintd xx [ax] + + + set breakpoint on DPMI INT xx optionaly matching ax. + + + +bcintd xx [ax] + + + clear breakpoint on DPMI INT xx. + + + +bpload + + + set one shot breakpoint at entry point +of the next loaded DOS-program. + + + +bl + + + list active breakpoints + + + +bplog regex + + + set a breakpoint on logoutput using regex. With this +the normal DOSEMU log output (enabled via the -D commandline option +or the dosdebug `log' command) is monitored via the regular +expression `regex' (look at GNU regex manual) and when a match is found +emulation is set into `stopped' mode. There may be 8 log breakpoint +active simultaneously. Without the `regex' given `bplog' such prints +the current active breakpoints. + + + +bpclog number + + + clears a log break point. + + + +log [flags] + + + get/set debug-log flags (e.g 'log +M-k') + + + +log on|off + + + redirect dbug-log output to the dosdebug terminal + + + +ldt sel [lines] + + + dump ldt starting at selector 'sel' for 'lines' +'sel' may be a symbolic register name. + + + +(rmapfile) + + + (internal command to read /usr/src/dosemu/dosemu.map +at startup time) + + + +rusermap org fn + + + read microsoft linker format .MAP file "fn" +code origin = "org". +for example if your code is at 1234:0, org would +be 12340. + + + + + + + +Addresses may be specified as: + + + + + + a linear address. Allows 'd' and 'u' commands to look at both +DOSEMU kernel and DOS box memory (0-1Mb). + + + + + + a seg:off address (0-1Mb) +seg as well as off can be a symbolic registers name (e.g cs:eip) +'seg' under DPMI is resolved via LDT, if so a numeric 'seg' value +is prefixed by # (e.g. #00af:0000. +You may force a seg to treaten as LDT selector by prefixing the '#'. +Accordingly to the default address mode 'off' under DPMI is 16 or +32 bit. +When in DPMI mode, and you want to address/display realmode +stuff, then you must switch off DPMI mode ('mode -d') + + + + + + a symbolic address. usermap is searched first, then dosemu map. +( not for DPMI programms ) + + + + + + an asterisk(*): CS:IP (cs:eip) + + + + + + a dollar sign($): SS:SP (ss:esp) + + + + + + + + + + +Performance + + +If you have dosemu compiled with the debugger support, but the +debugger is not active and/or the process is not stopped, you +will not see any great performance penalty. + + + + + +Wish List + + +Main wish is to add support for hardware debug registers (if someone +would point me in the direction, what syscalls to use, etc.) +Then you could breakpoint on memory reads/writes, etc! + + + + + +BUGS + + +There must be some. + + + +Known bugs + + + + + + + + Though you may set breakpoints and do singlestep in Windows31, +this is a 'one shot': It will bomb after you type 'g' again. +( I suspect this is a timer problem, we really should freeze +the timer and all hardware/mouse IRQs while the program is in 'stop'). +Debugging and singlestepping through DJGPP code doesn't have any +problems. + + + + + + INT3 type breakpoints in DPMI code are very tricky, because you +never know when the client has remapped/freed the piece of code +that is patched with 0xCC ( the one byte INT3 instruction ). +Use that with caution !! + + + + + + Single stepping doesn't work correctly on call's. May be the +trap-flag is lost. +However, when in DPMI the problems are minor. + + + + + + popf sometime clears the trap-flag, so single stepping +results in a 'go' command. +'tf' works around, but we should do it better. + + + + + + When stopped for a long period, the BIOS-timer will be updated to +fast and may result in stack overflow. We need to also stop the timer +for dosemu. + + + + + + When not stopped, setting break points doesn't work properly. +So, as a work around: Setting breakpoints while not in stop is disabled. + + + + + + + + + + + + + diff --git a/src/doc/README/dosnet b/src/doc/README/dosnet new file mode 100644 index 0000000..1ba33cc --- /dev/null +++ b/src/doc/README/dosnet @@ -0,0 +1,250 @@ + +Networking using DOSEMU + + +Direct NIC access + +The easiest (and not recommended) way to set up the networking +in DOSEMU is to use the direct NIC access. It means that DOSEMU +will exclusively use one of your network interfaces, say eth1. +No other processes will be able to use that interface. If they +try to, the data exchange will became unreliable. So you have +to make sure that this network interface is not used by anything +including the kernel itself, before starting DOSEMU. +The settings for this method are as follows: + +$_pktdriver = (on) +$_ethdev = "eth1" +$_vnet = "eth" + +Note that this method requires root privileges. + + +As you can see, this simple method has many shortcomings. If +you don't have the network card dedicated specially for dosemu, +consider using more advanced method called "Virtual Networking". + + + + +Virtual networking + +Virtual networking is a mechanism that allows to overcome all the +limitations of the direct NIC access method, but it requires more +work to set up everything properly. +A special virtual network devices can be created using TUN/TAP interface. +This will enable multiple dosemu sessions and the linux kernel to be on +a separate virtual network. Each dosemu will have its own network device +and ethernet address. + + + +First make sure that your Linux kernel comes with support for TUN/TAP; +for details check Documentation/networking/tuntap.txt +in the Linux kernel source. The user who runs DOSEMU should have +read/write access to /dev/net/tun. Then either: + + + + +Set + +$_pktdriver=(on) +$_vnet = "tap" +$_tapdev = "" + + + +Start DOSEMU as usual and configure the network device while DOSEMU is +running (using ifconfig manually as root, a script, or usernetctl if +your distribution supplies that), e.g. + +ifconfig dosemu_tap0 up 192.168.74.1 + + + +Configure the DOS TCP/IP network clients to have another IP address in the +subnet you just configured. This address should be unique, i.e. no other +dosemu, or the kernel, should have this address. For the example addresses +given above, 192.168.74.2-192.168.74.254 would be good. +Your network should now be up and running and you can, for example, +use a DOS SSH client to ssh to your own machine, but it will +be down as soon as you exit DOSEMU. + + + + + +Or set + +$_pktdriver=(on) +$_vnet = "tap" +$_tapdev = "tap0" + + + +Obtain tunctl from the user mode linux project. Then set up a persistent +TAP device using tunctl (use the -u owner option if you do that as root). +Configure the network using ifconfig as above, but now before starting +DOSEMU. Now start DOSEMU as often as you like and you can use the network +in the same way as you did above. + + +Note, however, that multiple DOSEMU sessions that run at the same time +need to use multiple tapxx devices. $_tapdev can be changed without +editing dosemu.conf or ~/.dosemu/.dosemurc (if you leave it commented out there) +by using dosemu -I "netdev tap1". + + + + + + +With the above you did set up a purely virtual internal network between +the DOSEMU and the real Linux box. This is why in the +above example 192.168.74.1 should *not* be a real IP address of the +Linux box, and the 192.168.74 network should not exist as a real +network. To enable DOS programs to talk to the outside world you have +to set up Ethernet bridging or IP routing. + + + +Bridging + +Bridging, using brctl (look for the bridge-utils package if you don't +have it), is somewhat easier to accomplish than the IP routing. +You set up a bridge, for example named "br0" and connect eth0 and +tap0 to it. Suppose the Linux box has IP 192.168.1.10 on eth0, where +192.168.1.x can be a real LAN, and the uid of the user who is +going to use DOSEMU is 500, then you can do (as root): + +brctl addbr br0 +ifconfig eth0 0.0.0.0 promisc up +brctl addif br0 eth0 +ifconfig br0 192.168.1.10 netmask 255.255.255.0 up +tunctl -u 500 +ifconfig tap0 0.0.0.0 promisc up +brctl addif br0 tap0 + +Now the DOSEMU's IP can be set to (for example) 192.168.1.11. +It will appear in the same network your linux machine is. + + + + +IP Routing + +If you like to use IP routing, note that unlike with bridging, +each DOSEMU box will reside in a separate IP subnet, which consists +only of 2 nodes: DOSEMU itself and the corresponding TAP device on +Linux side. +You have to choose an IP address for that subnet. If your LAN has the +address 192.168.1.0 and the netmask is 255.255.255.0, the dosemu subnet can +have the address 192.168.74.0 and tap0 can have the address 192.168.74.1: + +ifconfig tap0 192.168.74.1 netmask 255.255.255.0 up + +Choose a valid IP address from that subnet for DOSEMU box. It can be +192.168.74.2. Configure your DOS client to use that IP. Configure your +DOS client to use a gateway, which is the TAP device with IP 192.168.74.1. +Then you have to add the proper entry to the routing table on your Linux +box: + +route add -net 192.168.74.0 netmask 255.255.255.0 dev tap0 + +The resulting entry in the routing table will look like this: + +Destination Gateway Genmask Flags Metric Ref Use Iface +192.168.74.0 * 255.255.255.0 U 0 0 0 tap0 + + +Then, unless the Linux box on which DOSEMU is running is a default +gateway for the rest of you LAN, you will have to also add an entry +to the routing table on each node of your LAN: + +route add -net 192.168.74.0 netmask 255.255.255.0 gw 192.168.1.10 + +(192.168.1.10 is the IP of the box DOSEMU is running on). +Also you have to check whether IP forwarding is enabled, and if not - +enable it: + +echo 1 > /proc/sys/net/ipv4/ip_forward + +Now DOSEMU will be able to access any node of your LAN and vice versa. +Sometimes the forwarding is blocked by the firewall rules. In that +case try the following command (as root): + +iptables -t filter -I FORWARD 1 -i tap0 -j ACCEPT + + + + +Yet one more thing have to be done if you want dosemu to be able to +access Internet. Unlike in your LAN, you are not supposed to change +the routing tables on an every Internet host, so how to make them +to direct the IP packets back to dosemu's virtual network? To +accomplish this, you only have to enable the IP Masquerading on +the network interface that looks into Internet. If your machine +serves as a gateway in a LAN, then the masquerading is most likely +already enabled, and no further work is required. Otherwise you +must run the following command +(assuming the eth0 interface serves the Internet connection): + +iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE + +Now you'll find your dosemu session being able to access Internet. +If you want these changes to be permanent, you can use iptables-save +script to save the changes to your iptables configuration file. + + + + + +VDE networking backend + +If you just need to have a working internet access from within your +DOSEMU environment, then you might want to consider using VDE networking. +slirpvde is a user mode networking utility providing a NAT-like +service, DHCP server, as well as a virtualized network in the 10.0.2.0/24 +range, with a default gateway and a DNS server. +Note that dosemu uses VDE backend as a fallback when other methods are +unavailable. It also tries to start all the vde utilities, so no +configuration efforts are usually needed. + + +Use the following command to get VDE sources: + +svn checkout svn://svn.code.sf.net/p/vde/svn/trunk vde-svn + +You may also need to apply the following patches: + +patch1 + + +patch2 + + +patch3 + + +patch4 + + + +VDE-specific config options: + +$_pktdriver=(on) +$_vnet = "vde" +$_vdeswitch = "/tmp/switch1 +$_slirpargs = "--dhcp" + + + +Your wattcp.cfg file will look as simple as this: + +my_ip=dhcp + + + + + diff --git a/src/doc/README/footer b/src/doc/README/footer new file mode 100644 index 0000000..fb4b468 --- /dev/null +++ b/src/doc/README/footer @@ -0,0 +1 @@ + diff --git a/src/doc/README/header b/src/doc/README/header new file mode 100644 index 0000000..530c7d4 --- /dev/null +++ b/src/doc/README/header @@ -0,0 +1,294 @@ + + + +
+ + + +DOSEMU + + + +The DOSEMU team + + + +Alistair MacDonald + + +alistair@slitesys.demon.co.uk + + + + + + +For DOSEMU v1.4 pl0.0 + + + + +This document is the amalgamation of a series of README files which were +created to deal with the lack of DOSEMU documentation. + + + + + + + +Introduction + + +You can start DOSEMU using + + + $ dosemu + + +If you have never used DOSEMU before, and FreeDOS is present, then +DOSEMU will boot, and present you with a welcome screen and a C:\> +command prompt. + + + +If for some reason it does not start, or DOSEMU crashes somewhere, +look at ~/.dosemu/boot.log for details. + + + +Remember, that you can't use <Ctrl>-C +within DOS to exit from DOS. +For this you need to execute exitemu +or, when using the 'DOS in a BOX' <Ctrl><Alt><PgDn> +. + + + +Your DOS drives are set up as follows: + + A: floppy drive (if it exists) + C: points to the Linux directory ~/.dosemu/drive_c. It contains the + files config.sys, autoexec.bat and a directory for temporary files. + It is available for general DOS use. + D: points to your Linux home directory + E: points to your CD-ROM drive, if it is mounted at /media/cdrom + Z: points to the read-only DOSEMU and FreeDOS commands directory + It actually points to ~/mydos/dosemu/drive_z; it appears read-only + inside DOSEMU. + + + + +You can use the LREDIR DOSEMU command to adjust +these settings, or edit +/etc/dosemu/dosemu.conf, ~/.dosemu/.dosemurc, c:\config.sys, or c:\autoexec.bat, +or change the symbolic links in ~/.dosemu/drives. + + + +Enter HELP for more information on DOS and DOSEMU commands. +Note that FreeDOS COMMAND.COM DIR command shows long +file names if you type DIR/LFN. + + + +Other useful keys are: + +<Ctrl><Alt><F> toggle full-screen mode in X +<Ctrl><Alt><K> grab the keyboard in X +<Ctrl><Alt><Home> grab the mouse in X +<Ctrl><Alt><Del> reboot +<Ctrl><^> use special keys on terminals (dosemu -t) + + + + +DOSEMU modes of operation + + +There exist various ways of starting DOSEMU, depending on the environment +and certain command line options. By default, in X, it will start using +a special 'DOS in a Box' which provides a usual PC setup, using a 80x25 +text mode. It also supports graphics. The box can be rescaled by dragging +the window borders using the mouse. + + + +However, in certain situation you may want to use a different mode. + + + +Terminal mode + + +Terminal mode is automatically entered if you do not have X available, for +instance when logging in remotely from a Windows system or at the Linux +console. You can force it using: + + + $ dosemu -t + + +In this mode the display of graphics is impossible, but you can use +full-screen DOS text mode applications. It is advisable to give the +terminal window a size of 80 by 25 characters, or use "stty cols 80 rows 25" +on the Linux console, before starting it because many DOS applications +are confused about other sizes. + + + +You can use the $_internal_char_set option in ~/.dosemu/.dosemurc or +dosemu.conf to change the code page that DOSEMU thinks that DOS is using. + + + +Many terminals do not support various function key combinations. On the +Linux console you can work around that by using the raw keyboard mode +(-k flag, or $_rawkeyboard). xterm's support many +key combinations. In +other cases you'll have to work around it using the special +Ctrl-^ shortcut +(Ctrl-6 on US keyboards). Press Ctrl-^ h +for help. + + + + + +Dumb mode + + +For DOS applications that only read from standard input and write to +standard output, without any full-screen usage, you can use dumb +mode. To use this you must invoke DOSEMU like + + + $ dosemu -dumb + + +this has the advantage that (A) the output of the DOS application stacks +up in your scroll buffer and (B) you can redirect it to a file such as + + + $ dosemu -dumb dir > listing + + +Note that editing is often restricted to BACKSPACE'ing. + + + + +SDL mode + + +You can start dosemu with the "-S" option to use the SDL library. In +X it will just look like a regular DOS in a Box but with a different +shaped text mode mouse cursor. You can also use this mode on frame buffer +consoles. + + + + + +Console graphics mode + + +Console graphics mode is the hardest to setup and may potentially lock +up your system, but if it works it gives you direct VGA hardware access +which may be quicker and more accurate than the emulation used in X. + + + +You need root rights to use it. To enable it, it is recommended to use +"sudo": + + + +install sudo if you haven't already done so + +use visudo as root to add entries such as + + joeuser hostname=(root) PASSWD: /usr/local/bin/dosemu.bin + +to your /etc/sudoers file, where "joeuser" is the user who is +allowed to run privileged DOSEMU and "hostname" is the name of +your current host (use "ALL" for any host). + +if you change PASSWD to NOPASSWD then joeuser does not need to type +the user's password (not root's password) when invoking DOSEMU +(a little less secure, if somebody hacks into joeuser's account). + +now invoke DOSEMU using dosemu -s + + + + + + + +Running a DOS program directly from Linux. + + +You can use something like + + dosemu "/home/clarence/games/commander keen/keen1.exe" + +which will automatically cause the DOS in DOSEMU to + + "cd" to the correct directory, + + execute the program automagically, + + and quit DOSEMU when finished. + + + + + +Using a different DOS + +It is possible to use a different DOS than the supplied FreeDOS in +DOSEMU. A straightforward way is to just copy the relevant system +files (io.sys, msdos.sys, etc.) to ~/.dosemu/drive_c, and then the +next time you run dosemu it will automatically use them. You may +need to edit config.sys and autoexec.bat though, if the DOS complains. + + + +Another way is to boot directly from a Linux mounted FAT partition, +with Windows 9x or any DOS installed. You can change the C: drive +to point to that by using dosemu -i. + + + +In that case the DOSEMU support commands are available on drive D: instead +of drive Z:. You might want to use different config.sys and autoexec.bat +files with your DOS. For example, you can try to copy D:\config.emu +and D:\autoemu.bat to C:\, adjust them, and use the $_emusys option +in ~/.dosemu/.dosemurc or dosemu.conf. + + + +Manual adjustment of the C: drive is also possible, by changing the +~/.dosemu/drives/c symbolic link or by specifying it explicitly +using the $_hdimage run-time option. + + + + + +About this document + +The rest of this document goes into more detail about all the different +settings and possibilities. +This documentation is derived from a number of smaller documents. This makes it +easier for individuals to maintain the documentation relevant to their area of +expertise. Previous attempts at documenting DOSEMU failed because the +documentation on a large project like this quickly becomes too much for one +person to handle. + + + + + diff --git a/src/doc/README/header-tech b/src/doc/README/header-tech new file mode 100644 index 0000000..1b49616 --- /dev/null +++ b/src/doc/README/header-tech @@ -0,0 +1,54 @@ + + +
+ + + +DOSEMU Technical Guide + + + +The DOSEMU team + + + +Alistair MacDonald + + +alistair@slitesys.demon.co.uk + + + + + + +For DOSEMU v1.2 pl0.0 + + + + +This document is the amalgamation of a series of technical README files which were created to deal with the lack of DOSEMU documentation. + + + + + + + +Introduction + + +This documentation is derived from a number of smaller documents. This makes it +easier for individuals to maintain the documentation relevant to their area of +expertise. Previous attempts at documenting DOSEMU failed because the +documentation on a large project like this quickly becomes too much for one +person to handle. + + + +These are the technical READMEs. Many of these have traditionally been +scattered around the source directories. + + + + diff --git a/src/doc/README/keymap b/src/doc/README/keymap new file mode 100644 index 0000000..0458d2d --- /dev/null +++ b/src/doc/README/keymap @@ -0,0 +1,98 @@ + +Keymaps + + +This keymap is for using dosemu over telnet, and +having *all* your keys work. This keymap is not complete. But +hopefully with everyones help it will be someday :) + + + +There are a couple of things that are intentionally +broken with this keymap, most noteably F11 and F12. This is because +they are not working though slang correctly at the current instant. +I have them mapped to "greyplus" and "greyminus". Also the scroll +lock is mapped to shift-f3. This is because the scroll lock dosn't +work right at all. Please feel free to send keymap patches in that +fix anything but these. + + + +If you want to patch dosemu to fix either of those problems, i'd be +glad to accept those :) + + + +to figure out how to edit this, read the keystroke-howto. + + + +as of 3/30/95, control/shift/alternate +home/insert/delete/end/pageup/pagedown should work. + + + +Major issues will be: + + + +Do we move "alt-<fkey>" to "control-<fkey>" to switch virtual +consoles? + + + +who is going to fix the linux keyboard device to be able to handle +multiple keymaps at the same time? + + + +-------------------------------------------------------- + + + +to use it: + + + +as root type + + + loadkeys dosemu.new.keymap + + + + + +(then run dosemu via telnet, or something in slang mode) + + + +when your done, find your old keymap, and load it back, cause +control-home won't work in emacs anymore (or any other special key in +any applicaion that uses xlate) + + + +if you find a key missing, please add it and send me the patch. (test +it first! :) + + + +if you find a key missing, and don't feel like coding it, don't tell +me! I already know that there are a lot of keys missing. + + + +corey sweeney +<corey@interaccess.com > + + + +Sytron Computers + + + + diff --git a/src/doc/README/lredir b/src/doc/README/lredir new file mode 100644 index 0000000..2f2cd75 --- /dev/null +++ b/src/doc/README/lredir @@ -0,0 +1,184 @@ + +Using Lredir + + +This section of the document by Hans, +<lermen@fgan.de>. Last +updated on October, 23 2002. + + + +What is it? Well, its simply a small DOS program that tells the +MFS (Mach File System) code what 'network' drives to redirect. +With this you can 'mount' any Linux directory as a virtual drive +into DOS. In addition to this, Linux as well as multiple dosemu sessions +may simultaneously access the same drives, what you can't when using +partition access. + + + +how do you use it? + + +Mount your dos hard disk partition as a Linux subdirectory. +For example, you could create a directory in Linux such as /dos (mkdir +-m 755 /dos) and add a line like + + + + + + /dev/hda1 /dos msdos umask=022 + + + + + +to your /etc/fstab. (In this example, the hard disk is mounted read- +only. You may want to mount it read/write by replacing "022" with +"000" and using the -m 777 option with mkdir). Now mount /dos. Now +you can add a line like + + + + + + lredir d: linux\fs/dos + + + + + +to the AUTOEXEC.BAT file in your hdimage (but see the comments +below). On a multi-user system you may want to use + + + + + + lredir d: linux\fs\${home} + + + + + +where "home" is the name of an environmental variable that contains +the location of the dos directory (/dos in this example) + + + +You may even redirect to a NFS mounted volume on a remote machine with +a /etc/fstab entry like this + + + otherhost: /dos nfs nolock + + +Note that the nolock> option might be +needed for 2.2.x kernels, because +apparently the locks do not propagate fast enough and DOSEMU's (MFS code) +share emulation will fail (seeing a lock on its own files). + + + +In addition, you may want to have your native DOS partion as C: under dosemu. +To reach this aim you also can use Lredir to turn off the 'virtual' +hdimage and switch on the real drive C: such as this: + + + +Assuming you have a c:\dosemu directory on both drives (the virtual +and the real one) and have mounted your DOS partition as /dosc, +you then should have the following files on the virtual drive: + + + +autoexec.bat: + + + + + + lredir z: linux\fs\dosc + copy c:\dosemu\auto2.bat z:\dosemu\auto2.bat + lredir del z: + c:\dosemu\auto2.bat + + + + + +dosemu\auto2.bat: + + + + + + lredir c: linux\fs\dosc + rem further autoexec stuff + + + + + +To make the reason clear why the batch file (not necessaryly autoexec.bat) +must be identical: + + + +Command.com, which interpretes the batchfile keeps a position pointer +(byte offset) to find the next line within this file. It opens/closes the +batchfile for every new batchline it reads from it. +If the batchfile in which the 'lredir c: ...' happens is on c:, then +command.com suddenly reads the next line from the batchfile of that +newly 'redired' drive. ... you see what is meant? + + + + + +Other alternatives using Lredir + + +To have a redirected drive available at time of config.sys you may +either use emufs.sys such as + + + + + + device=c:\emufs.sys /dosc + + + + + +or make use of the install instruction of config.sys (but not both) such as + + + + + + install=c:\lredir.exe c: linux\fs\dosc + + + + + +The later has the advantage, that you are on your native C: from the +beginning, but, as with autoexec.bat, both config.sys must be identical. + + + + +For information on using 'lredired' drives as a 'user' (ie having the right +permissions), please look at the section on Running dosemu as a normal user. + + + + + + diff --git a/src/doc/README/mfsnls b/src/doc/README/mfsnls new file mode 100644 index 0000000..d0071a0 --- /dev/null +++ b/src/doc/README/mfsnls @@ -0,0 +1,249 @@ + +MFS and National Language Support + + +This section written by Oleg V. Zhirov +<O.V.Zhirov@inp.nsk.su> +, Aug 3, 1998 + + + +MFS and National Language Support + + +Main problem is that *nix and DOS uses codesets, which can differ. So, +in Russia the most popular codeset for *nix is koi8-r, while DOS +standard used so called `alternative' codeset cp866. + + + +While DOSEMU access DOS partitions directly, through original DOS (V)FAT +drivers, it doesn't matter, that linux locales are set to koi8-r, and +all works correctly. However, in this way you cannot start more than one +copy of DOSEMU at the same time. + + + +A more elegant solution (which in fact heavily supported by recent +development +of DOSEMU) is to mount all DOS partitions into linux filesystem and access +them through the `internal network' system MFS (Mach File System). It allows +use the security of real *nix (linux) filesystem, and one can have +simultaneously so many DOSEMU sessions, as he wants ;-). + + + +However, new problems occur: + + + + + + + + + Mount DOS partitions into Linux. Currently, linux kernel +linux-2.0.34 support mounting both MSDOS and VFAT partitions. No problem is, +if your DOS partition has true MSDOS format. More ambigious case is if your +have VFAT partition. You can mount it as VFAT and get access to 'long names', +which looks as very attractive option. But in this case you *have lost* +any access to _short_filenames_ - DOS aliases of long filenames. To access +short_filenames you need mount VFAT partition as MSDOS (!) (I am not +even sure, that this is completely safe (!)). + + + + + + IMPORTANT: mounting DOS partition, you optionally make filenames +convertion from DOS codeset to Linux one, if you want to see filenames +correctly (in Russia cp866 -> koi8-r). Otherwise I need to support both +codesets in my Linux. (Currently I do no conversion and have on my console +both codesets). + + + + + + Bug in MFS: transfer directory/file names from Linux to DOS via MFS +includes also some name conversion from *nix standard to DOS one. In +original (current in dosemu-0.97.10) release of MFS a lot of corresponding +locale-dependent (!!!) char/string operations on _DOS_names_ were performed +with _Linux_ locale setting (in my case koi8-r). As a result, one get a +garbage instead of original filenames. + + + + + + If you mount VFAT partition in LINUX as VFAT, some of filenames are +long. I fact, curent dosemu MFS system can `mangle' them, converting into +short aliases. There is NO vay to reconstruct the true DOS alias from +long_name_, since in DOS created SFNAME~index index value depends +exceptionaly on the _history_ of file creation. As a result, `mangled' +names differs from true DOS shortnames. + + + + + + + + + + +Patching of MFS + + +Presented patch of MFS cures problem (3) only. Summary of modification: + + + + + all locale-dependent code is removed from mfs.c and mangle.c +and is located in file util.c only. Here I add a set of routins: + + + islowerDOS(...)(+), tolowerDOS(...)(+), + isupperDOS(...)(+), toupperDOS()(+), + isalphaDOS(...)(+), isalnumDOS(...)(+), + is_valid_DOS_char(...)(+), chrcmpDOS(...)(+), + + strlowerDOS(...), strhaslowerDOS(...), + strupperDOS(...), strhasupperDOS(...), + chrcmpDOS(...)(+), strncmpDOS(...), strcmpDOS(...), + strncasecmpDOS(...), strcasecmpDOS(...). + + +Upper part (marked by (+)) has all the information on the codesets, +while lower part is derived from the upper one. Currently codepage 866 +implementation is completely sufficient for Russia (e.g for Ukrainian +some definitions should be added). As for Western Europe latin codepages, +some sort of support existed before is conserved. In any case, further +addition of other codepages is quite easy. + + + + + + file mangle.c was revised, and some dead code was excluded. To my +opinion, this file need to be more critically revized: some stuff, +(e.g. verification of 8.3 format) is duplicated in mfs.c, and other +stuf looks too overcomplicated (am I right?). I have a suspection, +that most of codes overlaps in functionality with those of mfs.c. +Unfortunately, I have no time to examine the file mfs.c more carefully. + + + + + + At last, I replace all locale-dependent operation in mangle.c and +mfs.c by their DOS partners: func() -> funcDOS(), defined in util.c. + + + + + + + + +To patch MFS put patch.mfs into directory dosemu-0.97.10/src/dosext/mfs/ +and issue a command + + +patch -p1 <patch.mfs + + +and then compile dosemu as usual. + + + +To mount dos VFAT partition in Linux filesystem I do like as + + + + + +mount -t vfat -o noexec,umask=022,gid=107,codepage=866,iocharset=cp866 /dev/hda1 /dos_C + + + + + +(or corresponding line in /etc/fstab). In this vay I have dos names in the +`alternative' codepage 866. To operate them in Linux I turn console in +`altenative' charset mode (see, e.g. CYRILLIC-HOWTO). + + + +NOTE: VFAT module in linux-2.0.34 seems to be buggy: Creating via Linux +filename with lowercase russian letters, you obtain file not accessible +by DOS (or even Win95) - probably, people, who makes VFAT support have +forgotten creating shortname DOS alias from long names or short names +turn to upper case chars with ASCII codes > 127 ? (To my first look, +they use tolower() and toupper() functions which works for ascii<=127). +Fortunately, DOS creates filenames properly, even with cyrillic letters, +and creating files with russian names is completely safe. + + + +In DOSEMU I load DOS (DOS-7) from hdimage. +To access dos partition in DOSEMU I have in config.sys two lines + + + + + + install=c:\subst.exe L: C:\ + install=c:\lredir.exe C: linux\fs/dos_C + + + + + +Obvious disadvantage of my approach: long filenames have short aliases +slightly different than in direct partition access. In practice, this +doesn't result in problem, since DOS stuff usually does not exploit long +filenames, and files with long filenames are irrelevant. + + + + + + TODO: + + + + + + + + In Linux kernel: Cure in VFAT module upper/lowercase bugs for +ascii>127. + + + + + + In Linux kernel: VFAT module should provide access to short DOS aliases +of long names, even if VFAT partition is mounted in longname support mode (with -t vfat). + + + + + + In dosemu MFS: +MFS should use short DOS aliases, actually existing for VFAT longnames. + + + + + + + + + + diff --git a/src/doc/README/mkfatimage16 b/src/doc/README/mkfatimage16 new file mode 100644 index 0000000..69ed81a --- /dev/null +++ b/src/doc/README/mkfatimage16 @@ -0,0 +1,28 @@ + +mkfatimage16 -- Make a FAT hdimage pre-loaded with files + + +This section from Pasi Eronen +<pe@iki.fi> (1995-08-28) + + + +To put it brief, mkfatimage16 creates an hdimage file for dosemu that's +pre-loaded with the files specified on the command line. Its purpose is to +remove the need to include an hdimage.dist file in the dosemu distribution +(and it saves about 100K in the .tar.gz size on the way). + + + +mkfatimage16 takes an additional switch [-t number_of_tracks] and automatically +switches to the 16-bit FAT format if the specified size is > 16MB. + + + +As always, comments, suggestions, etc. are welcome. + + + diff --git a/src/doc/README/mouse b/src/doc/README/mouse new file mode 100644 index 0000000..72e375a --- /dev/null +++ b/src/doc/README/mouse @@ -0,0 +1,192 @@ + +The DOSEMU mouse + +This section written by Eric Biederman eric@dosemu.org + + + +Setting up the emulated mouse in DOSEMU + + +For most dos applications you should be able to use the +internal mouse with very little setup, and very little trouble. + +Under X, or in terminal mode, you don't need to do anything, unless you want +to use the middle button then you need to add to autoexec.bat: + + +emumouse 3 + + +On the console, in text mode, without root, the GPM library can be used, +and no extra setup is necessary. +Otherwise, especially with console graphics (sudo/suid/root, the -s switch, +and $_graphics=(1)), it takes just a tad bit more work: + + +in dosemu.conf: + + +$_mouse = "mousesystems" +$_mouse_dev = "/dev/gpmdata" + + +And in autoexec.bat: + +emumouse 3 + + +This sets you up to use the gpm mouse repeater if you don't +use the repeater, you need to set $_mouse and $_mouse_dev to +different values. + +The GPM repeater might be configured to use a different protocol +than the default. If you are having problems, check the 'repeat_type' setting +in your gpm.conf. These are the mappings from the GPM repeat_type to the +DOSEMU $_mouse for common settings: + + +GPM setting DOSEMU setting +------------------------------- +msc (default) mousesystems +ms3 microsoft +raw select type of your real mouse + + + + + + +Problems + +In X there are 2 ways applications can get into trouble. + + +The most common way is if they don't trust the mouse driver +to keep track of where the mouse is on the screen, and insist +on doing it themselves. win31 & geoworks are too applications +in this category. They read mouse movement from the mouse +driver in turns of mickeys i.e. they read the raw movement data. + + +To support this mouse driver then tracks where you were and where you +move to, in terms of x,y screen coordinates. Then works the standard +formulas backwards that calculate screen coordinates from mickeys to +generate mickeys from screen coordinates. And it feeds these mickeys +to the application. As long as the application and dosemu agree on +the same formulas for converting mickeys to screen coordinates all is +good. + + +The only real problem with this is sometimes X mouse and the +application mouse get out of sync. Especially if you take your +mouse cursor out of the dosemu window, and bring it back in again. + + +To compensate for getting out of sync what we do is whenever we +reenter the Xdos window is send a massive stream of mickeys heading +for the upper left corner of the screen. This should be enough to +kick us any an good mouse drivers screen limits. Then once we know +where we are we simulate movement for 0,0. + + +In practice this isn't quite perfect but it does work reasonably well. + + +The tricky part then is to get the application and dosemu to agree on +the algorithm. The algorithm we aim at is one mickey one pixel. +Dosemu does this by default under X but you can force it with. + + +emumouse x 8 y 8 a + + +To do this in various various applications generally falls under +the category of disable all mouse accelration. + + +for win31 you need + + + MouseThreshold1=0 + MouseThreshold2=0 + MouseSpeed=0 + + +in the '[windows]' section of win.ini + + +The fool proof solution is to take total control of the mouse in X. +This is controlled by the $_X_mgrab_key in /etc/dosemu.conf +$_X_mgrab_key contains an X keysym of a key that when pressed +with both Ctrl & Alt held down will turn on the mouse grab, which +restricts the X mouse to the dosemu window, and gives dosemu complete +control over it. Ctrl-Alt-$_X_mgrab_key will then release the +mouse grab returning things to normal. + + +I like: $_X_mgrab_key="Scroll_Lock" (Ctrl-Alt-Scroll_Lock) +but $_X_mgrab_key="a" is a good conservative choice. (Ctrl-Alt-A) +You can use xev to see what keysyms a key generates. + + +Currently the way the X mouse code and the mouse grab are +structured the internal mouse driver cannot display the mouse +when the mouse grab is active. In particular without the grab +active to display the mouse cursor we just let X draw the mouse for +us, (as normal). When the mouse grab is active we restrict the mouse +to our current window, and continually reset it to the center of the +current screeen (allowing us to get relative amounts of movement). +A side effect of this is that the the position of the X cursor and the +dos cursor _not_ the same. So we need a different strategy to display +the dos cursor. + + +The other way an application can get into trouble in X, and also +on the console for that matter is if it is partially broken. In +particular the mouse driver is allowed to return coordinates that +have little to no connection with the actual screen resolution. So an +application mouse ask the mouse driver it's maximums and then scale +the coordinates it gets into screen positions. The broken +applications don't ask what the maximum & minimum resolutions are +and just assume that they know what is going on. + +To keep this problem from being too severe in mouse.c we have +attempted to match the default resolutions used by other mouse +drivers. However since this is up to the choice of an individual +mouse driver there is doubtless code out there developed with +different resolutions in mind. + +If you get stuck with such a broken application we have developed a +work around, that is partially effective. The idea being that if the +application draws it's own mouse pointer it really doesn't matter +where the dos mouse driver thinks the mouse is things should work. +So with emumouse it is possible to set a minimum resolution to return +to an application. By setting this minimum resolution to as big or +bigger than the application expect to see it should work. The side +effect of setting a minimum resolution bigger than the application +expects to see in X is that there will be some edges to the of +the screen where the application draws the cursor at the edge of the +window, and yet you need to continue scrolling a ways before the cursor +comes out there. In general this will affect the right and bottom +edges of the screen. + +To read the current minimum use: + +emumouse i + +The default is 640x200 + +To set the minimum resolution use: + +emumouse Mx 640 My 200 + +If you application doesn't draw it's own mouse cursor a skew of this +kind can be nasty. And there is no real good fix. You can set the +mininum down as well as up and so it may be possible to match what +the app expects as an internal resolution. However there is only +so much we can do to get a borken application to run and that +appears to be the limit. + + + diff --git a/src/doc/README/net b/src/doc/README/net new file mode 100644 index 0000000..ceacbd7 --- /dev/null +++ b/src/doc/README/net @@ -0,0 +1,111 @@ + +Net code + + + + + + + Added support for multiple type handling. +So it does type demultiplexing within dosemu. + + + +So we are able to run telbin and pdipx/netx simultaneously ;-) + + + + + + Changed the code for F_DRIVER_INFO. If the handle is '0', it need not +be valid handle (when input to this function). At least CUTCP simply +set this value to zero when it requests for info for the first +time. The current code wrongly treated it as valid handle, and +returned the class of first registered type. + + + + + Some things still to be done ... + + + + + + Novell Hack is currently disabled. + + + + + + Compare class along with type when a new packet arrives. + + + + + + Lots of clean ups required. (I have put in lots of pd_printf's.) +Need to be removed even if debug is not used because function +call within packet handler is very costly ... + + + + + + change all 'dsn0' / 'dosnet' to vnet (i.e. virtual net.) or so. + + + + + +and + + + + + + Use of SIGIO ... + + + + + + Special protocol stack to do demultiplexing of types?? + + + + + + Use of devices instead of the current mechanism of implementing +v-net? + + + + + + + + + + + + + +NOTE: THERE ARE STILL BUGS. The dosemu session just hangs. -- +Usually when in CUTCP. (And specially when I open two sessions.) +What is strange is, it seems to work fine for some time even when two +sessions are opened. +I strongly feel it is due to new PIC code because it is used +heavily by the network code ... (especially in interactive telnet +sessions.) + + + +Vinod +<vinod@cse.iitb.ernet.in> + + + diff --git a/src/doc/README/new b/src/doc/README/new new file mode 100644 index 0000000..b90533a --- /dev/null +++ b/src/doc/README/new @@ -0,0 +1,112 @@ + +Net code + + + + + + + Added support for multiple type handling. +So it does type demultiplexing within dosemu. + + + +So we are able to run telbin and pdipx/netx simultaneously ;-) + + + + + + Changed the code for F_DRIVER_INFO. If the handle is '0', it need not +be valid handle (when input to this function). At least CUTCP simply +set this value to zero when it requests for info for the first +time. The current code wrongly treated it as valid handle, and +returned the class of first registered type. + + + + + Some things still to be done ... + + + + + + Novell Hack is currently disabled. + + + + + + Compare class along with type when a new packet arrives. + + + + + + Lots of clean ups required. (I have put in lots of pd_printf's.) +Need to be removed even if debug is not used because function +call within packet handler is very costly ... + + + + + + change all 'dsn0' / 'dosnet' to vnet (i.e. virtual net.) or so. + + + + + +and + + + + + + Use of SIGIO ... + + + + + + Special protocol stack to do demultiplexing of types?? + + + + + + Use of devices instead of the current mechanism of implementing +v-net? + + + + + + + + + + + + + +NOTE: THERE ARE STILL BUGS. The dosemu session just hangs. -- +Usually when in CUTCP. (And specially when I open two sessions.) +What is strange is, it seems to work fine for some time even when two +sessions are opened. +I strongly feel it is due to new PIC code because it is used +heavily by the network code ... (especially in interactive telnet +sessions.) + + + +Vinod +<vinod@cse.iitb.ernet.in> + + + + diff --git a/src/doc/README/newkbd b/src/doc/README/newkbd new file mode 100644 index 0000000..bbe04aa --- /dev/null +++ b/src/doc/README/newkbd @@ -0,0 +1,782 @@ + +Old Keyboard Code + + +This file describes the old keyboard code which was written in late '95 for +scottb's dosemu-0.61, and adapted to the mainstream 0.63 in mid-'96. + + + +It was last updated by R.Zimmermann +<zimmerm@mathematik.uni-marburg.de> +on 18 Sep 96 and updated by Hans +<lermen@fgan.de> +on 17 Jan 97. ( correction notes marked *HH -- Hans ) + + + +Whats New + + +What's new in the new keyboard code? A lot. + + + +To the user: + + + + + + Most of the keyboard-related bugs should have gone away. Hope I didn't +introduce too many new ones (-: +Keyboard emulation should be more accurate now; some keys are supported +that weren't before, e.g. Pause. + + + + + + The X { keycode } option is now obsolete. This was basically a bad hack +to make things work, and was incompatible to X servers other than XFree86. + + + + + + + + +To the dosemu hacker: + + + + + While the old code already claimed to be "client-server" (and was, to +some extent), the new code introduces a clean, well-defined interface +between the `server', which is the interface to DOS (int9, bios etc.), +and the `clients', which are the interfaces to the user frontends supported +by dosemu. Currently, clients are `raw', `slang' (i.e. terminal), and `X'. + + + + +Clients send keystrokes to the server through the interface mentioned +above (which is defined in "keyboard.h"), the most important functions being +`putkey()' and `putrawkey()'. + + + + + + The keyboard server was rewritten from scratch, the clients were heavily +modified. + + + + + + There is now general and efficient support for pasting large text objects. +Simply call paste_text(). + + + + + + The keyboard-related code is now largely confined to base/keyboard, +rather than scattered around in various files. + + + + + + + + +There is a compile-time option NEW_KBD_CODE (on by default) to activate the +new keyboard code. The old stuff is still in there, but I haven't recently checked +whether it still works, or even compiles. Once the new code is reasonably well tested +I'll remove it. +( *HH: the old code is made workeable and remains ON per default, it will +stay maintained for a while, so we can easily check where the bugs come +from ) + + + + + +Status + + +Almost everything seems to work well now. + + + +The keyboard server should now quite accurately emulate all key combinations +described the `MAKE CODES' & `SCAN CODES' tables of HelpPC 2.1, which I +used as a reference. + + + +See below for a list of known bugs. + + + +What I need now is YOUR beta-testing... please go ahead and try if all your +application's wierd key combinations work, and let me know if they don't. + + + + + +Keyboard server interface + + +This is all you should need to know if you just want to send keystrokes +to DOS. + + + +Use the functions + + + + + + + + + putrawkey(t_rawkeycode code); + + + + + + putkey(Boolean make, t_keysym key) + + + + + + putkey_shift(Boolean make, t_keysym key, t_shiftstate shiftstate) + + + + + + + + +You may also read (but not write!) the variable 'shiftstate' if necessary. + + + +ehm... see the DANG comments in base/newkbd-server.c for more information... + + + +NOTE: the server's queue is limited to a relatively small number of keyboard +events (currently 15). IMO, it is not a good idea to let the queue be +arbitrarily long, as this would render the behaviour more incontrollable +if the user typed a lot of mad things while a dos program wasn't polling the +keyboard. + + + +For pasting, there is special support in base/keyboard/keyb_clients.c which +runs on top of the server. + + + + + +Keyboard server structure + + +[NOTE: you won't need to read this unless you actually want to modify +the keyboard server code. In that case, however, you MUST read it!] + + + +[Note: I'll have to update this. The queue backend works somewhat different +now.] + + + +The central data structure of the keyboard server is the dosemu keyboard +queue (to be distinguished from the bios keyboard buffer, which is run +by int09 and int16). + + + +The keyboard server code can be largely divided into the `queue frontend' +(serv_xlat.c, serv_maps.c), which does keycode translation, and the +`queue backend' (serv_backend.c, serv_8042.c), which does the interfacing +to DOS. The two sides communicate only through the queue. + + + +Each queue entry holds a data structure corresponding to (mostly) +one keypress or release event. [The exception are the braindead +0xe02a / 0xe0aa shift key emulation codes the keyboard processor +`decorates' some kinds of keyboard events with, which for convenience +are treated as seperate events.] + + + +Each queue entry holds a up to 4 bytes of raw keycodes for the +port 60h emulation, along with a 2-byte translated int16h keycode +and the shift state after this event was processed. +Note that the bios_key field can be empty (=0), e.g. for shift keys, +while the raw field should always contain something. + + + +queue handling functions + + + + + + + +static inline Boolean queue_empty(void); + + + + + +static inline void clear_queue(void); + + + + + +static inline void write_queue(Bit16u bios_key,t_shiftstate shift,Bit32u raw); + + + + + +static void read_queue(Bit16u *bios_key, t_shiftstate *shift, t_rawkeycode *raw); + + + + + + + + +Accordingly, the keyboard code is largely divided into two parts, + + + + + + the 'front end' of the queue, responsible for translating keyboard +events into the 'queue entry' format. + + + + + + the 'back end' of the queue, which reads the queue and sends keycodes +to DOS + + + + + + + + + + +The Front End + + + + + + putrawkey() -------->----+ + \ \ | + \ v | + \ translate() | + \ | | + \ v \ (t_rawkeycode[4]) /---QUEUE----\ + /->---\---|-----------*------------------------> [ raw ] + / \ \ (t_keysym+char) [ ] + putkey() ->-\--*--------------> make_bios_code() --> [ bios_key ] + \ \ [ ] + \ v /--------> [ shiftstate ] + \---> do_shift_keys() / \------------/ + | / + v (t_shiftstate) / + [shiftstate]---------------/ + + +---------> data flow (&calls, sometimes) +,,,,,,,,,> calls + + + + + + +Functions in serv_xlat.c + + + + + + + +static Boolean do_shift_keys(Boolean make, t_keysym key); + + + + + +static Bit16u make_bios_code(Boolean make, t_keysym key, uchar ascii); + + + + + +static uchar translate(t_keysym key); + + + + + +static Boolean handle_dosemu_keys(t_keysym key); + + + + + +void putrawkey(t_rawkeycode code); + + + + + +void putkey(Boolean make, t_keysym key, uchar ascii); + + + + + +void putkey_shift(Boolean make, t_keysym key, uchar ascii, t_shiftstate s); + + + + + + + + +Any keyboard client or other part of dosemu wishing to send keyboard +events to DOS will do so by calling one of the functions putrawkey, +putkey, and putkey_shift. + + + +putrawkey + + +is called with a single raw scancode byte. Scancodes from subsequent +calls are assembled into complete keyboard events, translated and +placed into the queue. + + + + + +putkey & others + + +,,,to be documented. + + + + + + + + + +The Back End + + +Queue Back End + + + + + + EMULATOR SIDE | x86 SIDE + | + ....[through PIC].|.................... + : | : v +QUEUE .....> out_b_8042() --> [ port 60h ] ----:---> other_int9_handler +| : | \ `....... (:) (|) +| : | \ v (v) (|) ++->int_chk_q()-> bios_buffer----> [ get_bios_key ]-----> default_int9_handler + ^ \ : | | (|) + : \----> shiftstate_buffer : | v (v) + : | .....: | bios keyb buffer + : v v | + : copy_shift_state() ----+-------------> bios shiftstate + : | + : | + : | + backend_run() | + +Abbreviations: +int_chk_q() = int_check_queue() +out_b_8042() = output_byte_8042() + + + + + + + +Functions in newkbd-server.c + + + + + + + +void do_irq1(); + + + + + +void clear_keybuf(); + + + + + +static inline Boolean keybuf_full(void); + + + + + +static inline void put_keybuf(Bit16u scancode); + + + + + +void copy_shift_state(t_shiftstate shift); + + + + + +static void kbd_process(void); + + + + + + + + +Transfer of the keyboard events from the dosemu queue to DOS is done as +follows: + + + +As soon as a key is stored into the empty queue, kbd_process() triggers +IRQ1 through the PIC emulation, which some time later will call do_irq1(). + + + +do_irq1() will prepare for the interrupt execution by reading from +the queue and storing the values in the variables raw_buffer, +shiftstate_buffer, and bios_buffer, and then call run_irq() to +run the actual DOS interrupt handler. + + + +There are two cases: + + + + + the default int09 handler in the dosemu bios (base/bios_emu.S) +will call the helper function get_bios_key(), which returns +the translated bios keycode from bios_buffer and copies the +shiftstate from shiftstate_buffer. The raw keycodes are not used. +get_bios_key() may also return 0 if no translated keycode is +ready. + + + +The int9 handler will also call the `keyboard hook' int15h, ax=????. + + + + + if a dos application or TSR has redirected the keyboard interrupt, +its handler might read from port 60h to get raw scancodes. +Port 60h is of course virtualized, and the read returns the value +from raw_buffer. + + + +Note that a mix between the two cases is also possible, e.g. a +TSR's int9 handler first reads port 60h to check if a particular +key was pressed, then gives over to the default int9 handler. +Even these cases should be (and are, I think) handled properly. + + + +Note also that in any case, int9 is called once for each raw scancode +byte. Eg.,suppose the user pressed the PgDn key, whose raw +scancode is E0 51: + + + + + + + + first call to int9: + read port 60h = 0xe0 + read port 60h = 0xe0 (**) + call get_bios_key() = 0 + iret +do_irq1() reschedules IRQ1 because further scancodes are in the queue + + + + + second call to int9 + read port 60h = 0x51 + call get_bios_key() = 0x5100 (bios scancode of PgDn) + iret + + + + + +(** multiple port 60h reads during the same interrupt yield the +same result.) + + + + + + + + +This is not a complete documentation. If you actually want to hack the +keyboard server, you can't avoid reading the code, I'm afraid ;-) + + + + + + + + + +Known bugs & incompatibilites + + + + + + + + behaviour wrt. cli/sti is inaccurate, because the PIC code currently +doesn't allow un-requesting if IRQ's. + + + + + + emulation of special 8042 and keyboard commands is incomplete and +probably still somewhat faulty. + + + + + + the 'internal' keyboard flags in seg 0x40, like E0 prefix received etc. +are never set. This shouldn't hurt, for all but the most braindead +TSRs. + + + + + + CAPS LOCK uppercase translation may be incorrect for some (non-german) +national characters. + + + + + + typematic codes in X and non-raw modes are Make+Break, not just Make. +This shouldn't hurt, though. + + + + + + in X mode, shift+Gray cursor keys deliver numbers if NumLock is off. +This is an X problem, and AFIK nothing can be done about it. + + + + + + in X, something may be wrong with F11+F12 handling (and again, possibly +necessarily wrong). + + + + + + the Pause key works in terms of raw scancodes, however it's function +is not implemented (i.e. it doesn't actually halt DOS execution.) + + + + + + in terminal (i.e. slang) mode, several things might be wrong or at least +improveable. + + + + + + there is no difference between the int16h functions 0,1 and the extended +functions 0x10,0x11 - i.e. 0,1 don't filter out extended keycodes. + + + + + + keyb.exe still doesn't work (hangs) - most probably due to the above. + + + + + + + + + + +Changes from 0.61.10 + + + + + + + + adapted to 0.63.55 + + + + + + adapted to 0.63.33 + + + + + + renamed various files + + + + + + various minor cleanups + + + + + + removed putkey_shift, added set_shiftstate + + + + + + in RAW mode, read current shiftstate at startup + + + + + + created base/keyboard/keyb_client.c for general client initialisation and +paste support. + + + + + + + + + + +TODO + + + + + + + +find what's wrong with TC++ 1.0 + + + + + +implement pause key + + + + + +adapt x2dos (implement interface to send keystrokes from outside dosemu) + + + + + +once everything is proved to work, remove the old keyboard code + + + + + + + + + + + diff --git a/src/doc/README/newnewkbd b/src/doc/README/newnewkbd new file mode 100644 index 0000000..87630cb --- /dev/null +++ b/src/doc/README/newnewkbd @@ -0,0 +1,211 @@ + +New Keyboard Code + + +This file describes the keyboard code which was written in 1999 + + + +It was last updated by Eric Biederman +<ebiederm@xmission.com> +on 22 April 2000 + + + +Whats New + + +What's new in the new keyboard code? + + + +Virtually all of the interface code gets keystrokes +has been rewritten. While the actual emulation of the hardware +has been fairly static. + + + +To the user: + + + + The terminal interface has been internationalized. + + + Keymaps can now be written in unicode making them + character set independant. + + + On non-us keyboard layouts the scan codes should always be + correct now. + + + The X { keycode } option is now fully supported and portable, + to any X server that implements the X keyboard extension. + + + + + + + +To the dosemu hacker: + + + + + While the old code already claimed to be "client-server" (and was, to +some extent), the new code introduces a clean, well-defined interface +between the `server', which is the interface to DOS (int9, bios etc.), +and the `clients', which are the interfaces to the user frontends supported +by dosemu. Currently, clients are `raw', `slang' (i.e. terminal), and `X'. + + + + +Clients send keystrokes to the server through the interface mentioned +above (which is defined in "keyboard.h"), the most important functions being +`putkey()' and `putrawkey()'. + + + + + + The keyboard server was rewritten from scratch, the clients were heavily +modified. + + + + + + There is now general and efficient support for pasting large text objects. +Simply call paste_text(). + + + + + + The keyboard-related code is now largely confined to base/keyboard, +rather than scattered around in various files. + + + + + + + + +There is a compile-time option NEW_KBD_CODE (on by default) to activate the +new keyboard code. Once the new code is reasonably well tested I'll remove it. + + + +Just like the old keyboard code, we still have the rawkeyboard=on/off modes. +The keybint=on/off modes have gone away. + + + + + +Status + + +Almost everything seems to work well now. + + + +The keyboard server should now quite accurately emulate all key combinations +described the `MAKE CODES' & `SCAN CODES' tables of HelpPC 2.1, which I +used as a reference. + + + +See below for a list of known bugs. + + + +What I need now is YOUR beta-testing... please go ahead and try if all your +application's wierd key combinations work, and let me know if they don't. + + + + + +Known bugs & incompatibilites + + + + + + + + behaviour wrt. cli/sti is inaccurate, because the PIC code currently +doesn't allow un-requesting if IRQ's. + + + + + + emulation of special 8042 and keyboard commands is incomplete and +probably still somewhat faulty. + + + + + + the 'internal' keyboard flags in seg 0x40, like E0 prefix received etc. +are never set. This shouldn't hurt, for all but the most braindead +TSRs. + + + + + the Pause key works in terms of raw scancodes, however it's function +is not implemented (i.e. it doesn't actually halt DOS execution.) + + + + If the interrupt is not acknowledged and the keyboard port is + read we don't eventually give up like a real keyboard and deliver + the next byte in the keyboard buffer. + + + + + + + + + +TODO + + + + + + Implement better multinational cut/paste in X + + + Implement timeouts on the length of type a byte is available in + the keyboard data port. + + + implement pause key + + + once everything is proved to work, remove the old keyboard code + + + implement utf8 and possibly iso2022 terminal support + + + + + + + + + diff --git a/src/doc/README/pentium b/src/doc/README/pentium new file mode 100644 index 0000000..1a73f3a --- /dev/null +++ b/src/doc/README/pentium @@ -0,0 +1,151 @@ + +Pentium-specific issues in dosemu + + +This section written by Alberto Vignani +<vignani@mbox.vol.it> +, Aug 10, 1997 + + + +The pentium cycle counter + + +On 586 and higher CPUs the 'rdtsc' instruction allows access to an internal +64-bit TimeStamp Counter (TSC) which increments at the CPU clock rate. +Access to this register is controlled by bit 2 in the config register cr4; +hopefully under Linux this bit is always off, thus allowing access to the +counter also from user programs at CPL 3. + + + +The TSC is part of a more general group of performance evaluation +registers, but no other feature of these registers is of any use for dosemu. +Too bad the TSC can't raise an interrupt! + + + +Advantages of the TSC: it is extremely cheap to access +(11 clock cycles, no system call). + + + +Drawbacks of using the TSC: you must know your CPU speed to get the absolute +time value, and the result is machine-class specific, i.e. you can't run a +binary compiled for pentium on a 486 or lower CPU (this is not the case for +dosemu, as it can dynamically switch back to 486-style code). + + + + + +How to compile for pentium + + +There are no special options required to compile for pentium, the CPU +selection is done at runtime by parsing /proc/cpuinfo. You can't override +the CPU selection of the real CPU, only the emulated one. + + + + + +Runtime calibration + + +At the very start of dosemu the bogospeed() function in base/init/config.c +is called. This function first looks for the CPUID instruction (missing on +386s and early 486s), then looks for the CPUSPEED environment variable, +and at the end tries to determine the speed itself. + + + +The environment variable CPUSPEED takes an integer value, which is the speed +of your processor, e.g.: + + + + + + export CPUSPEED=200 + + + + + +The last method used is the autocalibration, which compares the values of +gettimeofday() and TSC over an interval of several hundred milliseconds, and +is quite accurate AFAIK. + + + +You can further override the speed determination by using the statement + + + + + + cpuspeed div mul + + +in your configuration file. The integer ratio (mul/div) allows to specify +almost any possible speed (e.g. 133.33... will become '400 3'). You can even +slow down dosemu for debugging purposes (only if using TSC, however). + + + +The speed value is internally converted into two pairs of integers of the +form {multiplier,divider}, to avoid float calculations. The first pair is +used for the 1-usec clock, the second one for the tick(838ns) clock. + + + + + +Timer precision + + +I found no info about this issue. It is reasonable to assume that if your +CPU is specified to run at 100MHz, it should run at that exact speed (within +the tolerances of quartz crystals, which are normally around 10ppm). But I +suspect that the exact speed depends on your motherboard. +If you are worried, there are many small test programs you can run under +plain DOS to get the exact speed. +Anyway, this should be not a very important point, since all the file +timings are done by calling the library/kernel time routines, and do not +depend on the TSC. + + + + + +Additional points + + +The experimental 'time stretching' algorithm is only enabled when using the +pentium (with or without TSC). I found that it is a bit 'heavy' for 486 +machines and disallowed it. + + + +If your dosemu was compiled with pentium features, you can switch +back to the 'standard' (gettimeofday()) timing at runtime by adding the +statement + + + + + + rdtsc off + + +in your configuration file. + + + + + + diff --git a/src/doc/README/pic b/src/doc/README/pic new file mode 100644 index 0000000..ac75ab5 --- /dev/null +++ b/src/doc/README/pic @@ -0,0 +1,433 @@ + +DOSEMU Programmable Interrupt Controller + + +This emulation, in files picu.c and picu.h, emulates all of the useful +features of the two 8259 programmable interrupt controllers. Support +includes these i/o commands: + + + + + + +ICW1 bits 0 and 1 + + + number of ICWs to expect + + + +ICW2 bits 3 - 7 + + + base address of IRQs + + + +ICW3 no bits + + + accepted but ignored + + + +ICW4 no bits + + + accepted but ignored + + + +OCW1 all bits + + + sets interrupt mask + + + +OCW2 bits 7,5-0 + + + EOI commands only + + + +OCW3 bits 0,1,5,6 + + + select read register, +select special mask mode + + + + + + + +Reads of both PICs ports are supported completely. + + + + Other features + + + + + + + + + Support for 16 additional lower priority interrupts. Interrupts +are run in a fully nested fashion. All interrupts call dosemu functions, +which may then determine if the vm mode interrupt should be executed. The +vm mode interrupt is executed via a function call. + + + + + + Limited support for the non-maskable interrupt. This is handled the +same way as the extra low priority interrupts, but has the highest +priority of all. + + + + + + Masking is handled correctly: masking bit 2 of PIC0 also masks all +IRQs on PIC1. + + + + + + An additional mask register is added for use by dosemu itself. This +register initially has all interrupts masked, and checks that a dosemu +function has been registered for the interrupt before unmasking it. + + + + + + Dos IRQ handlers are deemed complete when they notify the PIC via +OCW2 writes. The additional lower priority interrupts are deemed +complete as soon as they are successfully started. + + + + + + Interrupts are triggered when the PIC emulator, called in the vm86 +loop, detects a bit set in the interrupt request register. This +register is a global variable, so that any dosemu code can easily trigger +an interrupt. + + + + + + + + + + + Caveats + + +OCW2 support is not exactly correct for IRQs 8 - 15. The correct +sequence is that an OCW2 to PIC0 enables IRQs 0, 1, 3, 4, 5, 6, and 7; +and an OCW2 to PIC1 enables IRQs 8-15 (IRQ2 is really IRQ9). This +emulation simply enables everything after two OCW2s, regardless of +which PIC they are sent to. + + + + +OCW2s reset the currently executing interrupt, not the highest +priority one, although the two should always be the same, unless some +dos programmer tries special mask mode. This emulation works correctly +in special mask mode: all types of EOIs (specific and non-specific) are +treated as specific EOIs to the currently executing interrupt. The +interrupt specification in a specific EOI is ignored. + + + +Modes not supported: + + + + + + Auto-EOI (see below) + + + + + + 8080-8085 + + + + + + Polling + + + + + + Rotating Priority + + + + + + + + +None of these modes is useable in a PC, anyway. The 16 additional +interrupts are run in Auto-EOI mode, since any dos code for them +shouldn't include OCW2s. The level 0 (NMI) interrupt is also handled +this way. + + + + + +Notes on theory of operation: + + +The documentation refers to levels. These are priority levels, +the lowest (0) having highest priority. Priority zero is reserved +for use by NMI. The levels correspond with IRQ numbers as follows: + + + + + + IRQ0=1 IRQ1=2 IRQ8=3 IRQ9=4 IRQ10=5 IRQ11=6 IRQ12=7 IRQ13=8 + IRQ14=9 IRQ15=10 IRQ3=11 IRQ4=12 IRQ5=13 IRQ6=14 IRQ7=15 + + + + + +There is no IRQ2; it's really IRQ9. + + + + +There are 16 more levels (16 - 31) available for use by dosemu functions. + + + + +If all levels were activated, from 31 down to 1 at the right rate, the +two functions run_irqs() and do_irq() could recurse up to 15 times. No +other functions would recurse; however a top level interrupt handler +that served more than one level could get re-entered, but only while the +first level started was in a call to do_irq(). If all levels were +activated at once, there would be no recursion. There would also be no +recursion if the dos irq code didn't enable interrupts early, which is +quite common. + + + +Functions supported from DOSEMU side + + +Functions that Interface with DOS: + + + + + +unsigned char read_picu0(port), +unsigned char read_picu1(port) + + +should be called by the i/o handler whenever a read of the PIC i/o ports +is requested. The "port" parameter is either a 0 or a 1; for PIC0 these +correspond to i/o addresses 0x20 and 0x21. For PIC1 they correspond with +i/o addresses 0xa0 and 0xa1. The returned value is the byte that was +read. + + + +void write_picu0(port,unsigned char value), +void write_picu1(port,unsigned char value) + + +should be called by the i/o handler whenever a write to a PIC port is +requested. Port mapping is the same as for read_picu, above. The value +to be written is passed in parameter "value". + + + +int do_irq() + + +is the function that actually executes a dos interrupt. do_irq() looks +up the correct interrupt, and if it points to the dosemu "bios", does +nothing and returns a 1. It is assumed that the calling function will +test this value and run the dosemu emulation code directly if needed. +If the interrupt is revectored to dos or application code, run_int() is +called, followed by a while loop executing the standard run_vm86() and +other calls. The in-service register is checked by the while statement, +and when the necessary OCW2s have been received, the loop exits and +a 0 is returned to the caller. Note that the 16 interrupts that are not +part of the standard AT PIC system will not be writing OCW2s; for these +interrupts the vm86 loop is executed once, and control is returned before +the corresponding dos code has completed. If run_int() is called, the +flags, cs, and ip are pushed onto the stack, and cs:ip is pointed to +PIC_SEG:PIC_OFF in the bios, where there is a hlt instruction. This is +handled by pic_iret. + + + +Since the while loop above checks for interrupts, this function can be +re-entered. In the worst case, a total of about 1k of process stack space +could be needed. + + + +pic_sti(), +pic_cli() + + +These are really macros that should be called when an sti or cli +instruction is encountered. Alternately, these could be called as part of +run_irqs() before anything else is done, based on the condition of the +vm86 v_iflag. Note that pic_cli() sets the pic_iflag to a non-zero value, +and pic_sti() sets pic_iflag to zero. + + + +pic_iret() + + +should be called whenever it is reasonably certain that an iret has +occurred. This call need not be done at exactly the right time, its +only function is to activate any pending interrupts. A pending interrupt +is one which was self-scheduled. For example, if the IRQ4 code calls +pic_request(PIC_IRQ4), the request won't have any effect until after +pic_iret() is called. It is held off until this call in an effort +to avoid filling up the stack. If pic_iret() is called too soon, +it may aggravate stack overflow problems. If it is called too late, +a self-scheduled interrupt won't occur quite as soon. To guarantee +that pic_iret will be called, do_irq saves the real return address on the +stack, then pushes the address of a hlt, which generates a SIGSEGV. +Because the SIGSEGV comes before the hlt, pic_iret can be called by the +signal handler, as well as from the vm86 loop. In either case, pic_iret +looks for this situation, and pops the real flags, cs, and ip. + + + + + + + + + + + +Other Functions + + + + + + +void run_irqs() + + +causes the PIC code to run all requested interrupts, in priority order. +The execution of this function is described below. + + + +First we save the old interrupt level. Then *or* the interrupt mask +and the in-service register, and use the complement of that to mask off +any interrupt requests that are inhibited. Then we enter a loop, looking +for any bits still set. When one is found, it's interrupt level is +compared with the old level, to make sure it is a higher priority. If +special mask mode is set, the old level is biased so that this test can't +fail. Once we know we want to run this interrupt, the request bit is +atomicly cleared and double checked (in case something else came along and +cleared it somehow). Finally, if the bit was actually cleared by this +code, the appropriate bit is set in the in-service register. The second +pic register (for PIC1) is also set if needed. Then the user code +registered for this interrupt is called. Finally, when the user code +returns, the in-service register(s) are reset, the old interrupt level is +restored, and control is returned to the caller. The assembly language +version of this funcction takes up to about 16 486 clocks if no request +bits are set, plus 100-150 clocks for each bit that is set in the request +register. + + + +void picu_seti(unsigned int level,void (*func), unsigned int ivec) + + +sets the interrupt vector and emulator function to be called then the +bit of the interrupt request register corresponding to level is set. +The interrupt vector is ignored if level<16, since those vectors are +under the control of dos. If the function is specified as NULL, the +mask bit for that level is set. + + + +void picu_maski(int level) + + +sets the emulator mask bit for that level, inhibiting its execution. + + + +void picu_unmask(int ilevel) + + +checks if an emulator function is assigned to the given level. If so, +the mask bit for the level is reset. + + + +void pic_watch() + + +runs periodically and checks for 'stuck' interrupt requests. These +can happen if an irq causes a stack switch and enables interrupts +before returning. If an interrupt is stuck this way for 2 successive +calls to pic_watch, that interrupt is activated. + + + + + + + + + + + +A (very) little technical information for the curious + + +There are two big differences when using pic. First, interrupts are not +queued beyond a depth of 1 for each interrupt. It is up to the interrupt +code to detect that further interrupts are required and reschedule itself. +Second, interrupt handlers are designated at dosemu initialization time. +Triggering them involves merely specifying the appropriate irq. + + + +Since timer interrupts are always spaced apart, the lack of queueing has no +effect on them. The keyboard interrupts are re-scheduled if there is +anything in the scan_queue. + + + + + + diff --git a/src/doc/README/port-io b/src/doc/README/port-io new file mode 100644 index 0000000..313f660 --- /dev/null +++ b/src/doc/README/port-io @@ -0,0 +1,234 @@ + +Accessing ports with dosemu + + +This section written by Alberto Vignani +<vignani@mbox.vol.it> +, Aug 10, 1997 and updated by Bart Oldeman, June 2003. + + + +General + + +For port I/O access the type ioport_t has been defined; it should +be an unsigned short, but the previous unsigned int is retained for +compatibility. Also for compatibility, the order of parameters has been +kept as-is; new code should use the port_real_xxx(port,value) function. +The new port code is selected at configuration time by the parameter + + + --enable-new-port + + +which will define the macro NEW_PORT_CODE. +Files: portss.c is now no more used and has been replaced by n_ports.c; +all functions used by 'old' port code have been moved to ports.c. Note +that the code in portss.c (retrieved from Scott Buchholz's pre-0.61) +was previously disabled (not used), because it had problems with older +versions of dosemu. + + + +The rep;in,out instructions will been optimized so to call iopl() +only once. + + + + + +Port I/O access + + +Every process under Linux has a map of ports it is allowed to access. Too +bad this map covers only the first 1024 (0x400) ports. For all the ports +whose access permission is off, and all ports over 0x400, an exception is +generated and trapped by dosemu. + + + +When the I/O permission (ioperm) bit for a port is ON, the time it takes to +access the port is much lower than a microsecond (30 cycles on a P5-150); when +the port is accessed from dosemu through the exception mechanism, access +times are in the range of tenths of us (3000 cycles on the P5-150) instead. +It is easy to show that 99% of this time is spent in the kernel trap and +syscalls, and the influence of the port selection method (table or switch) +is actually minimal. + + + +There is nothing we can do for ports over 0x400, only hope that these slow +access times are not critical for the hardware (generally they are not) and +use the largest possible word width (i.e. do not break 16- and 32-bit +accesses). + + + +The 'old' port code used a switch...case mechanism for accessing ports, +while now the table access (previously in the unused file portss.c) has been +chosen, as it is much more clear, easy to maintain and not slower than the +"giant switch" method (at least on pentium and up). + + + +There are two main tables in ports.c: + + + + + + the port table, a 64k char array indexed by port number and +storing the number of the handle to be used for that port. 0 means no handle +defined, valid range is 1-253, 0xfe and 0xff are reserved. + + + + + + the handle table, an array of structures describing the properties +for a port group and its associated functions: + static struct _port_handler { + unsigned char(*read_portb) (ioport_t port_addr); + void (*write_portb) (ioport_t port_addr, unsigned char byte); + unsigned short(*read_portw) (ioport_t port_addr); + void (*write_portw) (ioport_t port_addr, unsigned short word); + char *handler_name; + int irq, fd; + } port_handler[EMU_MAX_IO_DEVICES]; + + + + + + + + + +It works this way: when an I/O instruction is trapped (in do_vm86.c and +dpmi.c) the standard entry points port_in[bwd],port_out[bwd] are +called. They log the port access if specified and then perform a double +indexed jump into the port table to the function responsible for the +actual port access/emulation. + + + +Ports must be registered before they can be used. This is the +purpose of the port_register_handler function, which is called from +inside the various device initializations in dosemu itself, and of +port_allow_io, which is used for user-specified ports instead. +Some ports, esp. those of the video adapter, are registered an trapped this +way only under X, while in console mode they are permanently enabled (ioperm +ON). The ioperm bit is also set to ON for the user-specified ports below +0x400 defined as fast. +Of course, when a port has the ioperm bit ON, it is not trapped, and thus +cannot be traced from inside dosemu. + + + +There are other things to consider: + + + + + + system integrity + + + + + + system security + + + + + + + + +System Integrity + + +To block two programs from accessing a port without knowing what the other +program does, this is the strategy dosemu takes: + + + + + + If the port is not listed in /proc/ioports, no other program should +access the port. Dosemu will register these ports. This will also block a +second dosemu-process from accessing these ports. Unfortunately there is +no kernel call yet for registering a port range system-wide; see +later for current hacks. + + + + + If the port is listed, there's probably a device that could use these +ports. So we require the system administrator to give the name of the +corresponding device. Dosemu tries to open this device and hopes this will +block other from accessing. The parallel ports (0x378-0x37f) and /dev/lp1 +act in this way. + + + +To allow access to a port registered in /proc/ioports, it is necessary +that the open on the device given by the system administrator +succeeds. An open on /dev/null will always succeed, but use it at your own risk. + + + + + + + + + + +System Security + + +If the strategy administrator did list ports in /etc/dosemu.conf and allows a +user listed in /etc/dosemu.users to use dosemu, (s)he must know what (s)he is +doing. Port access is inherently dangerous, as the system can easily be +screwed up if something goes wrong: just think of the blank screen you get +when dosemu crashes without restoring screen registers... +As an extra precaution you have to use the "-s" dosemu command line +switch so you won't access the hardware directly or use console graphics +by accident. + + + + + +The port server + +Starting with version 1.1.4.5 the exception mechanism uses a port server. +If any slow ports from $_ports, any ports above 0x3ff (depending on the +Linux kernel, including some video cards and $_pci), or the native speaker +are selected (timer 2 of port +0x43 cannot be fast), DOSEMU will fork. The main DOSEMU will then drop +its root privileges and communicates +via pipes with the (forked) privileged port server. The server then checks +if it is allowed to access the port and acts appropriately. This way it is +impossible for a DPMI program to manipulate any forbidden ports (separate +address spaces). Fortunately the overhead of pipes and process switching +seems to be negligible compared to the time it takes to trap the port access. + + + +If the speaker is emulated and all ports are "fast", or if DOSEMU is +non-suid-root and run by a normal user, then the above forking is unnecessary +and does not occur. + + + + + + + + diff --git a/src/doc/README/priv b/src/doc/README/priv new file mode 100644 index 0000000..8eccc45 --- /dev/null +++ b/src/doc/README/priv @@ -0,0 +1,295 @@ + +Privileges and Running as User + + +This section written by Hans Lermen +<lermen@fgan.de> +, Apr 6, 1997. +And updated by Eric Biederman +<ebiederm+eric@npwt.net> +30 Nov 1997. + + + +What we were suffering from + + +Well, I got sick with the complaints about 'have problems running as user' +and did much effort to 'learn' about what we were doing with priv-settings. +To make it short: It was a real mess, there was the whole dosemu history +of different strategies to reach the same aim. Well, this didn't make +it clearer. I appreciate all the efforts that were done by a lot of people +in the team to make that damn stuff working, and atleast finding all those +places where we need to handle privs was a great work and is yet worth. +... but sorry, how it was handled didn't work. + + + +The main odds happened because sometimes functions, that were called +within a priv_on/off()...priv_default() brackets were calling +priv_on/off()...priv_default() themselves. And with introduction of this +priv_default() macro, which handled the 'default' case (run as user or +run as root) the confusion was complete. Conclusion: recursive settings +were not handled, and you always had to manually keep track of wether +privs were on or off. ... terrible and not maintainable. + + + + +Therefore I decided to do it 'the right way (tm)' and overworked it +completely. The only thing that now remains to do, is to check for more +places where we have to temporary allow/disallow root priviledges. I also +decided to be a good boy and make 'RUN_AS_ROOT' a compile time option +and 'run as user' the default one. + + + +(promise: I'll also will run dosemu as user before releasing, +so that I can see, if something goes wrong with it ;-) + + + +Enter Eric: + + + +What we have been suffering from lately is that threads were added to +dosemu, and the stacks Hans added when he did it 'the right way (tm)' +were all of a sudden large static variables that could not be kept +consistent. That and hans added caching of the curent uids for +efficiency in dosemu, again more static variables. + + + +When I went through the first time and added all of the strange +unmaintainable things Hans complains of above, and found where +priveleges where actually needed I hand't thought it was as bad as Hans +perceived it, so I had taken the lazy way out. That and my main concern +was to make the privelege setting consistent enough to not give +mysterous erros in dosemu. But I had comtemplated doing it 'the right +way (tm)' and my scheme for doing it was a bit different from the way +Hans did it. + + + +With Hans the stack was explicit but hidden behind the scenes. With my +latest incarnation the stack is even more explicit. The elements of the +privelege stack are now local variables in subroutines. And these local +variables need to be declared explicitly in a given subroutine. This +method isn't quite a fool proof as Han's method, but a fool could mess up +Hans's method up as well. And any competent person should be able to +handle a local variable, safely. + + + +For the case of the static cached uid I have simply placed them in the +thread control block. The real challenge in integrating the with the +thread code is the thread code was using root priveleges and changing +it's priveleges with the priv code during it's initialization. For the +time being I have disabled all of the thread code's mucking with root +priveleges, and placed it's initialization before the privelege code's +initialization. We can see later if I can make Han's thread code work +`the right way (tm)' as he did for my privelege code. + + + +ReEnter Hans: ;-) + + + +In order to have more checking wether we (not an anonymous fool) are +forgetting something on the road, I modified Erics method such that +the local variable is declared by a macro and preset with a magic, +that is checked in priv.c. The below explanations reflect this change. + + + +ReReEnter Hans (2000/02/28): ;-) + + + +The treads code has definitively gone (no one made serious use of it), +so the above thread related arguments too. + + + + + +The new 'priv stuff' + + +This works as follows + + + + + + + + All settings have to be done in 'pairs' such as + + + enter_priv_on(); /* need to have root access for 'do_something' */ + do_something(); + leave_priv_setting(); + + +or + + + enter_priv_off(); /* need pure user access for 'do_something' */ + do_something(); + leave_priv_setting(); + + + + + + + + On enter_priv_XXX() the current state will be saved (pushed) on a +local variable on the stack and later restored from that on +leave_priv_setting(). This variable is has to be defined at +entry of each function (or block), that uses a enter/leave_priv bracket. +To avoid errors it has to be defined via the macro PRIV_SAVE_AREA. +The 'stack depth' is just _one_ and is checked to not overflow. +The enter/leave_priv_* in fact are macros, that pass a pointer to +the local privs save area to the appropriate 'real_' functions. +( this way, we can't forget to include priv.h and PRIV_SAVE_AREA ) +Hence, you never again have to worry about previous priv settings, +and whenever you feel you need to switch off or on privs, you can do it +without coming into trouble. + + + + + + We now have the system calls (getuid, setreuid, etc.) only in +src/base/misc/priv.c. We cash the setting and don't do unnecessary +systemcalls. Hence NEVER call 'getuid', 'setreuid' etc. yourself, +instead use the above supplied functions. The only places where I +broke this 'holy law' myself was when printing the log, showing both +values (the real and the cached one). + + + + + + In case of dosemu was started out of a root login, we skip +all priv-settings. There is a new variable 'under_root_login' +which is only set when dosemu is started from a root login. + + + + + + On all places were iopl() is called outside a enter_priv...leave_priv +bracket, the new priv_iopl() function is to be used in order to +force privileges. + + + + + + + + +This is much cleaner and 'automagically' also solves the problem +with the old 'priv_off/default' sheme. Because, 'running as user' just +needs to do _one_ priv_off at start of dosemu, if we don't do it we are +'running as root' ;-) + + + +A last remark: Though it may be desirable to have a non suid root dosemu, +this is not possible. Even if we get GGI in the official kernel, we can't +solve the problems with port access (which we need for a lot of DOS apps) +... and ioperm() / iopl() need root privileges. +This leads to the fact that 'i_am_root' will be always '1', makeing +it a macro gives GCC a changes optimize away the checks for it. +We will leave 'i_am_root' in, maybe there is some day in the future +that gives us a kernel allowing a ports stuff without root privilege, +... never say never ;-) + + + +Enter Eric: + + + +The current goal is to have a non suid-root dosemu atleast in X, +lacking some features. But the reason I disabled it when I first introduced +the infamous in this file priv_on/priv_default mechanism is that it hasn't +been tested yet, still stands. What remains to do is an audit of what +code _needs_ root permissions, what code could benefit with a sgid dosemu, +and what code just uses root permissions because when we are suid root some +operations can only been done as root. + + + +When the audit is done (which has started with my most recent patch (I +now know what code to look at)). It should be possible to disable +options that are only possible when we are suid root, at dosemu +configuration time. Then will be the task of finding ways to do things +without root permissions. + + + +Enter Hans (960626): + + + +Last act of this story: DOSEMU now can run non-suid root and +though it looses a lot of features because of not beeing able to run on +console in this mode or not able to do any ports stuff e.t.c., its quite +useable under X (I was even able to run win31, hence DPMI is secure now +with the s-bit off). + + + +The names of some variables where changed, such that it conforms more +to what they now do: i_am_root became can_do_root_stuff, and +under_root_login was splitted into the static skip_priv_setting, +while the global valiable under_root_login keeps its meaning. + + + +skip_priv_setting is set for both: running as root and running as +user non-suid root. can_do_root_stuff is global and used in the same +places were i_am_root was used before. + + + +On start of DOSEMU, we now call parse_dosemu_users() directly +after priv_init(), and this is top of main(). This way we catch any +hacker attack quite early and should be much securer as ever before. + + + +Additionally, parse_dosemu_users now checks for the keyword `nosuidroot' +in /etc/dosemu.users and if this is found for a user, this user is not +allowed to execute DOSEMU on a suid root binary (though he can use +a non-suid root copy of it) and dosemu will exit in this case. +Dropping all priviledges on a suid root binary and continue (as if +the -s bit wasn't set) is not possible, because /proc/self/mem has +the ownership of the suid binary (hence root) and dosemu would crash +later, when trying to open it. Do a chown on /proc/self/mem doesn't +work either. + + + +The last action to make the non-suid root stuff working, was moving +the files from /var/run/* to some user accessable location. This +now is ~/.dosemu/* and dosdebug was adapted to support that. + + + + + + diff --git a/src/doc/README/recover b/src/doc/README/recover new file mode 100644 index 0000000..6dcce61 --- /dev/null +++ b/src/doc/README/recover @@ -0,0 +1,180 @@ + +Recovering the console after a crash + + +In general the easiest way to recover is to press Alt-SysRq-R (for this +you must have the "magic SysRq" key enabled in the Linux kernel, check +/proc/sys/kernel/sysrq !), and then switch to X using Alt-F7. +Switching back to a console using Ctrl-Alt-Fx +generally gives you a working text mode. If not, you could try +'vbetool', or read on. + + + +The below is a mail from Kevin Buhr +<buhr@stat.wisc.edu> +, that was posted +on linux-msdos some time ago. Because it describes a way to recover +from a totally locked console, the technique described below was partially +intergrated in dosdebug. So, the below 'switchcon.c' is now part of +dosdebug and you may use it via: + + + + + + dosdebug + console n + + + + + +where n is the console you want to switch to. + + + +But keep in mind, that dosdebug tries to kill your dosemu process the +safest way it can, so first use dosdebug's kill command: + + + + + + dosdebug + kill + + + + + +In the worst case you will get the following output on your remote terminal: + + + + + + ...oh dear, have to do kill SIGKILL + dosemu process (pid 1234) is killed + If you want to switch to an other console, + then enter a number between 1..8, else just type enter: + 2 <========= this is what you enter + dosdebug terminated + NOTE: If you had a totally locked console, + you may have to blindly type in 'kbd -a; texmode + on the console you switched to. + + + + + +The mail message + + + + +Date: Fri, 21 Apr 95 14:16 CDT +To: tegla@katalin.csoma.elte.hu +Cc: linux-msdos@vger.rutgers.edu +In-Reply-To: <Pine.LNX.3.91.950421163705.1348B-100000@katalin.csoma.elte.hu> (message from Nagy Peter on Fri, 21 Apr 1995 16:51:27 +0200 (MET DST)) +Subject: Restoring text mode (was Re: talk) +From: buhr@stat.wisc.edu (Kevin Buhr) +Sender: owner-linux-msdos@vger.rutgers.edu +Precedence: bulk +Status: RO +X-Status: + +| But when dosemu dies in graphics mode ( this happens every 30 minutes +| or so), it leaves the screen in graphics mode. You can do anything +| blindly (even start dosemu again) but the console screen is always left +| in graphics mode. + +I know what you mean... this is a real pain in the ass. + +Here's my solution. A few useful scripts and programs are supplied +with the SVGA binaries. "savetextmode" is a script that will write +register and font information to "/tmp/textregs" and "/tmp/fontdata". +Run this from the console as root while you're in text mode. If +you've got a cron job that clears out your "/tmp" directory, you'll +probably want to copy these someplace safe. + +The next time "dosemu" or something similar takes out your video, use +the "textmode" script (which reads the register and font from those +temporary files and also restores the palette), and everything should +be back to normal. Of course, this assumes you're able to get enough +control of your computer to enter the "textmode" command as root at +the console ("restoretextmode" complains if executed from a terminal +other than the console). One solution is to modify the source for +"restoretextmode" to operate correctly from off-console. + +I'm lazy, so I use a little program called "switchcon" (source +attached) that takes a single integer argument and switches to that +virtual console. + +So, if "dosemu" dies hard (so that "ctrl-alt-pagedown" doesn't work) +or exits without restoring text mode, I do this: + + (1) Log in from another terminal + (2) Kill "dosemu", if necessary. Killing with SIGTERM will + usually restore text mode automatically, but if I have + to SIGKILL it, I continue... + (3) Run "switchcon" as root to switch to another VC + (4) Sometimes I have to run "kbd_mode -a", too, if I'm stuck + in raw mode + +If Linux fails to automatically restore text mode, I log in (blindly) +as root on the console and run "textmode". With the canned register +and font files in place, this inevitably brings me back to text mode +bliss. + +Kevin <buhr@stat.wisc.edu> + + * * * + +switchcon.c: + +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <linux/vt.h> +#include <sys/ioctl.h> + +main( int argc, char **argv ) { + int newvt; + int vt; + + if(argc != 2 || !(newvt = atoi(argv[1]))) { + fprintf(stderr, "syntax: switchcon number\n"); + exit(2); + } + + vt = open( "/dev/tty1", O_RDONLY ); + if( vt == -1 ) { + perror("open(/dev/tty1)"); + exit(1); + } + if( ioctl( vt, VT_ACTIVATE, newvt ) ) { + perror("ioctl(VT_ACTIVATE)"); + exit(1); + } + if( ioctl( vt, VT_WAITACTIVE, newvt ) ) { + perror("ioctl(VT_WAITACTIVE)"); + exit(1); + } + + close(vt); + + return(0); +} + + + + + + + + + diff --git a/src/doc/README/runasuser b/src/doc/README/runasuser new file mode 100644 index 0000000..d9dc2af --- /dev/null +++ b/src/doc/README/runasuser @@ -0,0 +1,153 @@ + +Running dosemu as a normal user + + +This section of the document by Hans, +<lermen@fgan.de>. Last +updated on Jan 21, 2003. + + + + + + + + +In the default setup, DOSEMU does not have root privileges. This means it +will not have direct access to ports, external DOSish hardware and won't +use the console other than in normal terminal mode, but is fully capable +to do anything else. See the previous section on how to enable privileged +operation if you really need to. + + + + +If a user needs access to privileged resources other than console graphics, +then you may need to explicitly allow the user to do so by editing the file +/etc/dosemu.users (or /etc/dosemu/dosemu.users). +The format is: + + + + + + loginname [ c_strict ] [ classes ...] [ other ] + + + + + +For example, to allow joeuser full access you can use + + + joeuser c_all + + + + + + + The msdos partitions, that you want to be accessable through + should +be mounted with proper permissions. I recommend doing this via 'group's, +not via user ownership. Given you have a group 'dosemu' for this and want +to give the user 'lermen' access, then the following should be + + + + + + + + in /etc/passwd: + + + lermen:x:500:100:Hans Lermen:/home/lermen:/bin/bash + ^^^-- note: this is NOT the group id of 'dosemu' + + + + + + + in /etc/group: + + + users:x:100: + dosemu:x:200:dosemu,lermen + ^^^ + + + + + + + in /etc/fstab: + + + /dev/hda1 /dosc msdos defaults,gid=200,umask=002 0 0 + ^^^ + + + + + + + + + + +Note: the changes to /etc/passwd and /etc/group only take place the +next time you login, so don't forget to re-login. + + + +The fstab entry will mount /dosc such that is has the proper permissions + + + ( drwxrwxr-x 22 root dosemu 16384 Jan 1 1970 /dosc ) + + + + + +You can do the same with an explicit mount command: + + + mount -t msdos -o gid=200,umask=002 /dev/hda1 /dosc + + + + + +Of course normal lredir'ed unix directories should have the same +permissions. + + + + + + Make sure you have read/write permissions of the devices you +configured (in /etc/dosemu.conf) for serial and mouse. + + + + + + + + +Starting with dosemu-0.66.1.4 there should be no reason against running +dosemu as a normal user. The privilege stuff has been extensively reworked, +and there was no program that I tested under root, that didn't also run +as user. Normally dosemu will permanently run as user and only temporarily +use root privilege when needed (during initialization) and then drop its +root privileges permanently. In case of non-suid root +(as of dosemu-0.97.10), it will run in lowfeature mode without any privileges. + + + + diff --git a/src/doc/README/serial b/src/doc/README/serial new file mode 100644 index 0000000..e035741 --- /dev/null +++ b/src/doc/README/serial @@ -0,0 +1,343 @@ + +MARK REJHON'S 16550 UART EMULATOR + + +The ./src/base/serial directory contains Mark Rejhon's 16550A +UART emulation code. + + + +Please send bug reports related to DOSEMU serial to either +<marky@magmacom.com> or +<ag115@freenet.carleton.ca> + + + +The files in this subdirectory are: + + + +Makefile + + + The Makefile for this subdir. + + + +ser_init.c + + + Serial initialization code. + + + +ser_ports.c + + + Serial Port I/O emulation code. + + + +ser_irq.c + + + Serial Interrupts emulation code. + + + +int14.c + + + BIOS int14 emulation code. + + + +fossil.c + + + FOSSIL driver emulation code. + + + +../commands/fossil.S + + + DOS part of FOSSIL emulation. + + + +ser_defs.h + + + Include file for above files only. + + + +../include/serial.h + + + General include file for all DOSEMU code. + + + + + + + +Redistribution of these files is permitted under the terms of the +GNU Public License (GPL). See end of this file for more information. + + + +PROGRAMMING INFORMATION + + +Information on a 16550 is based on information from +HELPPC.EXE 2.1 and results from National Semiconductor's +COMTEST.EXE diagnostics program. This code aims to emulate +a 16550A as accurately as possible, using just reasonably +POSIX.2 compliant code. In some cases, this code does a +better job than OS/2 serial emulation (in non-direct mode) +done by its COM.SYS driver! There may be about 100 kilobytes +of source code, but nearly 50% of this size are comments! + + + +This 16550A emulator never even touches the real serial ports, +it merely traps port writes, and does the I/O via file functions +on a device in /dev. Interrupts are also simulated as necessary. + + + +One of the most important things to know before programming +this serial code, is to understand how the com[] array works. + + + +Most of the serial variables are stored in the com[] array. +The com[] array is a structure in itself. Take a look at the +'serial_t' struct declaration in the ../include/serial.h module +for more info about this. Only the most commonly referenced +global variables are listed here: + + + + + + +config.num_ser + + + Number of serial ports active. + + + +com[x].base_port + + + The base port address of emulated serial port. + + + +com[x].real_comport + + + The COM port number. + + + +com[x].interrupt + + + The PIC interrupt level (based on IRQ number) + + + +com[x].mouse + + + Flag mouse (to enable extended features) + + + +com[x].fd + + + File descriptor for port device + + + +com[x].dev[] + + + Filename of port port device + + + +com[x].dev_locked + + + Flag whether device has been locked + + + + + + + +The arbritary example variable 'x' in com[x] can have a minimum +value of 0 and a maximum value of (config.numser - 1). There +can be no gaps for the value 'x', even though gaps between actual +COM ports are permitted. It is strongly noted that the 'x' does +not equal the COM port number. This example code illustrates +the fact, and how the com[x] array works: + + + for (i = 0; i < config.numser; i++) + s_printf("COM port number %d has a base address of %x", + com[i].real_comport, com[i].base_port); + + + + + + + +DEBUGGING HELP + + +If you require heavy debugging output for serial operations, +please take a look in ./ser_defs.h for the following defines: + + + +SER_DEBUG_MAIN (0 or 1) + + +extra debug output on the most critical information. + + + +SER_DEBUG_HEAVY (0 or 1) + + +super-heavy extra debug output, including all ports reads +and writes, and every character received and transmitted! + + + +SER_DEBUG_INTERRUPT (0 or 1) + + +additional debug output related to serial interrupt code, +including flagging serial interrupts, or PIC-driven code. + + + +SER_DEBUG_FOSSIL_RW (0 or 1) + + +heavy FOSSIL debug output, including all reads and writes. + + + +SER_DEBUG_FOSSIL_STATUS (0 or 1) + + +super-heavy FOSSIL debug output, including all status checks. + + + + + + + + + +FOSSIL EMULATION + + +The FOSSIL emulation requires a memory-resident DOS module, +FOSSIL.COM. If you don't load it, the code in fossil.c does nothing, +permitting you to use another FOSSIL driver such as X00 or BNU. + + + +The emulation isn't as complete as it could be. Some calls aren't +implemented at all. However, the programs I've tried don't seem to +use these calls. Check fossil.c if you're interested in details. + + + +I've done only minimal testing on this code, but at least the +performance seems to be quite good. Depending on system load, I got +Zmodem speeds ranging from 1500 to nearly 3800 cps over a 38400 bps +null-modem cable when sending data to another PC. Zmodem receive, +however, was only about 2200 cps, since Linux tried to slow down the +sender with flow control (I'm not sure why). + + + + + +COPYRIGHTS + + +Most of the code in the DOSEMU 'serial' subdirectory is: +Copyright (C) 1995 by Mark D. Rejhon +<marky@magmacom.com> +with the following exceptions: + + + + +FOSSIL driver emulation is Copyright (C) 1995 by +Pasi Eronen +<pasi@forum.nullnet.fi>. + + + +Lock-file functions were derived from Taylor UUCP: +Copyright (C) 1991, 1992 Ian Lance Taylor +Uri Blumenthal +<uri@watson.ibm.com> +(C) 1994 +Paul Cadach, +<paul@paul.east.alma-ata.su> +(C) 1994 + + + +UART definitions were derived from /linux/include/linux/serial.h +Copyright (C) 1992 by Theodore Ts'o. + + + +All of this serial code 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 2 of +the License, or (at your option) any later version. + + + + + + diff --git a/src/doc/README/simx86 b/src/doc/README/simx86 new file mode 100644 index 0000000..7abf3f2 --- /dev/null +++ b/src/doc/README/simx86 @@ -0,0 +1,53 @@ +This short description from + + Alberto Vignani Sat Jul 8 23:27:58 2000 + +only in order to not loose valuable information. + +I _really_ expect some more worked out documentation --Hans, 2000/07/09 + +What's new: + +1) ZeroBaseTime is now in cycles instead of us. This simplifies things in + the cpuemu even if his required moving some #include . +2) SIGTIME has been un-macroed to its standard value of SIGALRM, and another + time signal (currently SIGPROF) has been introduced for future use in the + cpuemu. +3) TRACE_DPMI is currently enabled only if cpuemu is compiled in; use the + -D{n}t option to enable it, where if n>1 registers and disassembly are + written to the logfile. It traces the "regular", not the emulated, DPMI, + and doesn't trace vm86 code, only PM. +4) FP coprocessor error full decoding. +5) Support for BOUND (int5) exception. +6) Moved some structs and defines from port.c to port.h, for use by the + cpuemu. +7) The cpuemu itself. It is now called SIM86X. + The user interface has not changed from previous version; you always + have to use ecpuon/ecpuoff to enable/disable it, + and -D{n}e for debug logging. + The makefile has some optimization options: + OPTIMIZE_BACK_JUMPS + tries to resolve short branches without breaking a compiled + sequence. Quite safe. + OPTIMIZE_COMMON_SEQ + tries to compact push..push, pop..pop, movs etc. to generate + shorter code + OPTIMIZE_FW_JUMPS + tries to resolve short forward branches without breaking a + compiled sequence, + but it's a bit dangerous with self-modifying code and it's + enabled only for DPMI (PM) code. + USE_BOUND + someone proposed to use it for emulating PM features, + I implemented it for CS: accesses, just for fun. + SINGLESTEP + as the name says. +8) I used a lot of double-slash C++ style comments. + +What works under cpuemu: DOS (at least IBM DOS 7.0), Norton Commander, +ndiags, djgpp (at least the latest version 2.0.3), some CPU test programs, +tasm, text-mode demos, sourcer. +What doesn't yet work: doom and many graphic demos. +Not yet tested: Turbo C, Win3.1. + +Alberto diff --git a/src/doc/README/sound b/src/doc/README/sound new file mode 100644 index 0000000..6e92e35 --- /dev/null +++ b/src/doc/README/sound @@ -0,0 +1,102 @@ + +Sound + + +The SB code is currently in a state of flux. Some changes to the code have been +made which mean that I can separate the DSP handling from the rest of the SB +code, making the main case statements simpler. In the meantime, Rutger +Nijlunsing has provided a method for redirecting access to the MPU-401 chip +into the main OS. + + + +Using the MPU-401 "Emulation" + + +The Sound driver opens "/var/run/dosemu-midi" and +writes the Raw MIDI data to this. A daemon is provided which can be can be used +to seletc the instruments required for use on some soundcards. It is also +possible to get various instruments by redirecting '/var/run/dosemu-midi' to +the relevant part of the sound driver eg: + + + + + +% ln -s /dev/midi /var/run/dosemu-midi + + + + + +This will send all output straight to the default midi device and use whatever +instruments happen to be loaded. + + + + + +The MIDI daemon + + + + + make midid + + + + + +This compiles and installs the midi daemon. The daemon currently has support +for the 'ultra' driver and partial support for the 'OSS' driver (as supplied +with the kernel) and for no midi system. Support for the 'ultra' driver will +be compiled in automatically if available on your system. + + + +Copy the executable './bin/midid' so that it is on your path, or somewhere you +can run it easily. + + + +Before you run DOSEMU for the first time, do the following: + + + mkdir -p ~/.dosemu/run # if it doesen't exist + rm -f ~/.dosemu/run/dosemu-midi + mknod ~/.dosemu/run/dosemu-midi p + + + + + +Then you can use the midi daemon like this: + + + ./midid < ~/.dosemu/run/dosemu-midi &; dosemu + + + + + +(Assuming that you put the midid executeable in the directory you run DOSEMU +from.) + + + + + +Disabling the Emulation at Runtime + + +You can disable the SB emulation by changing the 'sound' variable in +/etc/dosemu.conf to 'off'. There is currently no way to specify at runtime +which SB model DOSEMU should emulate; the best you can do is set the T value +of the BLASTER environment variable (see sound-usage.txt), but not all +programs will take note of this. + + + + + + diff --git a/src/doc/README/sound-tech b/src/doc/README/sound-tech new file mode 100644 index 0000000..2b4beb9 --- /dev/null +++ b/src/doc/README/sound-tech @@ -0,0 +1,127 @@ + +Sound Code + + +Current DOSEMU sound code + + +Unfortunately I haven't documented this yet. However, the current code has been +completely rewritten and has been designed to support multiple operating +systems and sound systems. + + + +For details of the internal interface and any available patches see my WWW page +at +http://www.slitesys.demon.co.uk/a.macdonald/dosemu/sound/ + + + +At a later stages the code was extensively reworked (again) by Stas Sergeev. +Instead of direct pipes connected to /dev/dsp the DMA code now uses callbacks. +If pipes will be advantageous for other types of DMA in the future, they +will be readded. Opinions differ on this very subject (whether pipes +are more flexible than callbacks in certain cases). + + + + + +Original DOSEMU sound code + + + + + Copyright (C) 1995 Joel N. Weber II + + This sound code 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 2 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. + + +This is a very incomplete sound blaster pro emulator. I recomend you get the +PC Game Programmer's Encycolpedia; I give the URL at the end of this document. + + + +The mixer emulator probably doesn't do the math precisely enough (why did Hannu +use a round base 10 number when all the sound boards I know use base 2?) It +ignores obscure registers. So repeatedly reading and writing data will zero +the volume. If you want to fix this, send Hannu a message indicating that +you want the volume to be out of 255. It will probably fix the problem that +he advertises with his driver: if you read the volume and write it back as is, +you'll get zero volume in the end. And on the obscure registers issue, it +only pays attention to volumes and the dsp stereo bit. The filters probably +don't matter much, but the record source will need to be added some day. + + + +The dsp only supports reset and your basic dma playpack. Recording hasn't been +implemented, directly reading and writing samples won't work too well because +it's too timing sensitive, and compression isn't supported. + + + +FM currently has been ignored. Maybe there's a PCGPE newer than 1.0 which +describes the OPL3. But I have an OPL3, and it would be nice if it emulated +that. + + + +MIDI and joystick functions are ignored. And I think that DOSEMU is supposed +to already have good CDROM support, but I don't know how well audio CD players +will work. + + + +If you're having performance problems with wavesample playback, read +/usr/src/linux/drivers/sound/Readme.v30, which describes the fragment parameter +which you currently can adjust in ../include/sound.h + + + +I haven't tested this code extensively. All the software that came with my +system is for Windows. (My father claimed that one of Compaq's advantages +is user friendlyness. Well, user friendlyness and hackerfriendlyness +don't exactly go hand in hand. I also haven't found a way to disable bios +shadowing or even know if it's happening...) I can't get either of my DOS +games to work, either (Descent and Sim City 2000). Can't you guys support +the Cirrus? (Oh, and while I'm complaining, those mystery ports that SimEarth +needs are for the FM synthesiser. Watch it guys, you might generate interrupts +with that....) + + + +Reference + + +PC Game Programers Encyclopedia +ftp://teeri.oulu.fi/pub/msdos/programming/gpe/ + + + +Joel Weber (age 15) + + + +July 17, 1995 + + + + + + + + diff --git a/src/doc/README/sound-usage.sgml b/src/doc/README/sound-usage.sgml new file mode 100644 index 0000000..9e56f60 --- /dev/null +++ b/src/doc/README/sound-usage.sgml @@ -0,0 +1,533 @@ + + +
+ + + + Using The DOSEMU Sound System (SBEMU) + + + Alistair MacDonald, updated by Stas Sergeev and subsequently by Ben Davis. + + + + + +<A.MacDonald@slitesys.demon.co.uk> + + + + + + +dosemu-1.2 + + + + +The document describes the use of the DOSEMU Sound code (SBEMU), and +what you can expect from it. + + + + + + + +Introduction + + +It is much better to be pessimistic about the capabilities of DOSEMU's +sound code. The code itself provides an EMULATION of the popular +SB cards. The actual level of emulation that can be achieved is +dependent upon the capabilities of the sound driver on your +system. Currently the emulation can only talk to the OSS driver +(included in the Linux kernel) although it has been written in a +modular fashion to allow other drivers to be written. For more details +on the internals please see the WWW pages at http://www.slitesys.demon.co.uk/a.macdonald/dosemu/sound/. + + + +The SB and DMA emulation was extensively reworked a while ago by Stas +Sergeev to the point of emulating the SB Pro quite well. Ben Davis +has since added SB16 support. So the pessimism in the last paragraph +may no longer be justified. However, there are programs that don't +work or don't work well. (SB16 support implemented 22-24 May 2003.) + + + + + +What to (and what not to!) expect + + +The emulation consists of a number of parts, and I'll examine these +separately. + + + + FM + + +The FM driver is the least complete. Most of the chip functions are +there, but no audio output occurs. This means that software which uses +the presence/operation of the timer should detect the timer presence, +but if the audio needs or uses FM you will hear nothing. + + + + + + MPU-401 + + +This is the MIDI emulation. This should be fairly complete. +For using with OSS you need either an external daemon 'midid' +(this is supplied), or your sound card must have a Wave Table +synthesizer and /dev/midi configured and working. +Note that if you use ALSA, you don't need a Wave Table. +It is possible to use TiMidity++ instead. + + + + + + SB (Digital) + + +This is the digital aspect of the audio output. It is the most mature. +Most of the functionality up to and including SB-16 is implemented. +Note that SB MIDI is not implemented, although MPU-401 +emulation (above) is. + + + + + + + + Using SBEMU + + +SBEMU needs to be turned on at +run-time. Then your DOS applications need to be configured to use the +emulation (just as they need to be configured to use the real Sound +Blaster card). The basic steps are given below. + + + + DOSEMU Run-time Configuration + + +The default settings are: + + + Base Address: 220 + IRQ: 5 + DMA: 1 + Midi Port: 330 (Not normally needed) + + + + + +Note that these do NOT need to be the same as your real sound +card. + + + + Changing the SBEMU settings + + +The DOSEMU Sound settings are controlled by the following entries in +/etc/dosemu.conf + + + +Note that there is some overlap +here with the configuration notes in the README. This will be +addressed at a later stage. + + + +: + + + + + +$_sound = (off) # sound support on/off +$_sb_base = (0x220) +$_sb_irq = (5) +$_sb_dma = (1) +$_sb_hdma = (5) +$_sb_dsp = "/dev/dsp" +$_sb_mixer = "" +$_mpu_base = (0x330) + + + + + +Note that these are the default values. $_sb_dsp +and $_sb_mixer are pre-configured to work with +the standard OSS-Free driver. You are unlikely to need to change these values +unless you have multiple drivers in your kernel (such +as the Ultra project drivers). +Note: $_sb_mixer can be set to "/dev/mixer" +if you want DOSEMU to be able to change the volume on your sound card, +but most people don't like that, so it is disabled by default. + + + +There are several driver-specific settings for each supported underlying +driver (only OSS right now, or the OSS emulation of ALSA). In general +you don't need to alter the default values, but many OSS drivers appear +to have some very nasty quirks, so some tweaking might be necessary +if you have some problems with your particular driver. + + +$_oss_min_frags = (4) +$_oss_max_frags = (0x20) +$_oss_stalled_frags = (1) +$_oss_do_post = (off) +$_oss_min_extra_frags = (2) + + + + +$_oss_min_frags specifies the minimum number +of fragments to divide your card's DMA buffer to. If you buffer underruns, +try increasing this value. + + +$_oss_max_frags specifies the maximum number +of fragments to divide your card's DMA buffer to. + + +$_oss_stalled_frags is a hack to work around +a bug found in most of the existing OSS drivers, including the SB one. +Even when the playback is completed, these drivers are reporting that +one fragment is still in use. If your driver doesn't have that problem, +feel free to set this to 0. + + +$_oss_do_post is a hack that makes it possible +to work with drivers that do not honour the SNDCTL_DSP_SETFRAGMENT ioctl +command (unpatched Aureal Vortex binary driver is a known offender). This +hack is incompatible with the standard OSS drivers and must be disables +unless your driver doesn't work otherwise. + + +$_oss_min_extra_frags can be increased if +you have underruns. + + + + + + Configuring Midi + + +The midi driver can be used in two way. It is possible to direct the +output of the SBEMU midi driver directly to a sound card, or through +the midid daemon. All midi traffic is directed at +~/.dosemu/run/dosemu-midi. You will need to create this, +or link it to the appropriate device, according to your +configuration. Details about this are given below. + + + + Using Midid + + +This is the preferred method of producing MIDI output. This daemon is +built by default and lives in the same directory as your other DOSEMU +binaries. You will need to create the output pipe for the SBEMU driver: + + + + + +% mkdir -p ~/.dosemu/run +% rm -f ~/.dosemu/run/dosemu-midi +% mknod ~/.dosemu/run/dosemu-midi p + + + + + +If you already have a ~/.dosemu/run directory then the +mkdir will fail. This is not a problem. + + + +Finally, you need to start the midid program: + + + + + +% midid ~/.dosemu/run/dosemu-midi & + + + + + +This will run the driver in the background, giving you your prompt +back.You should now run DOSEMU. Midid will terminate when DOSEMU +terminates. + + + + + + Sending Midi direct to a device + + +To use this method with OSS, you simply need to link the SBEMU midi output file +to the input of your MIDI device. The simplest method of doing this +is: + + + + + +% mkdir -p ~/.dosemu/run +% rm -f ~/.dosemu/run/dosemu-midi +% ln -s /dev/MyMidiDevice ~/.dosemu/run/dosemu-midi + + + + + +Where /dev/MyMidiDevice is the path to your midi +device. If you already have a ~/.dosemu/run directory +then the mkdir will fail. This is not a problem. + + + +For ALSA, things are a bit trickier. +Here is an explanation from Robert Komar: + + + +The kernel-side basics for getting it working +are described at http://www.midi-howto.com. +For ALSA, I load the snd-seq-oss +and snd-virmidi kernel modules along with the usual ones started by +alsasound. I then start timidity as a daemon as follows: + + + + +% timidity -iA -A100 -B2,8 -Os -EFreverb=0 -EFchorus=0 > /dev/null 2>&1 & + + + + +Note, that you have to get a recent development version of Timidity++ +(I use TiMidity++-2.12.0-pre1), and you have to specify support for the +server when running configure before building Timidity++. + + + +At this point, you should be able to use `pmidi' to list the various +midi ports and to play music on them. Check to see that you can play +midi music via the Timidity++ server. + + + +I then connect the timidity server to a virtual midi device so that +I can access it through the /dev/snd/ interface (rather than only +through an ALSA-aware program like pmidi): + + + + +% /usr/local/bin/aconnect 75:0 128:1 + + + + +The addresses would probably be different in your case, but the midi-howto +site covers these details fairly well. + + + +Listing the midi ports with aconnect on my machine shows (I have a +SoundBlaster Live! Value card): + + + + +# aconnect -o -l +client 65: 'Emu10k1 WaveTable' [type=kernel] + 0 'Emu10k1 Port 0 ' + 1 'Emu10k1 Port 1 ' + 2 'Emu10k1 Port 2 ' + 3 'Emu10k1 Port 3 ' + Connected From: 74:0 +client 72: 'Virtual Raw MIDI 1-0' [type=kernel] + 0 'VirMIDI 1-0 ' +client 73: 'Virtual Raw MIDI 1-1' [type=kernel] + 0 'VirMIDI 1-1 ' +client 74: 'Virtual Raw MIDI 1-2' [type=kernel] + 0 'VirMIDI 1-2 ' + Connecting To: 65:3 +client 75: 'Virtual Raw MIDI 1-3' [type=kernel] + 0 'VirMIDI 1-3 ' + Connecting To: 128:1 +client 128: 'Client-128' [type=user] + 0 'TiMidity port 0 ' + 1 'TiMidity port 1 ' + Connected From: 75:0 + + + + +Before running dosemu, I go to the .dosemu/run directory and redirect +the output from dosemu-midi to the virtual midi device connected to the +timidity daemon: + + + + +% cd ~/.dosemu/run +% cat dosemu-midi > /dev/snd/midiC1D3 + +[ note that the permanent symlink should also work, see above --SS ] + + + +Note that midid isn't required in this case. In another terminal, I start +up dosemu and run the games with music playing via General Midi. Et Voila! +I get sound effects and music while playing Duke3d! (and just in time, +since my version of Win2K won't play these old DOS games with sound and +VESA video modes). + + + + + + + + + + Configuring DOS + + +You should set your BLASTER environment variable to match your DOSEMU +settings. Note that these don't need to correspond to your real sound +card. For a base SB with the values above, use the following DOS command: + + + + + +set BLASTER=A220 I5 D1 H5 T6 + + + + + +The T6 identifies this as a Type 6 SB device, i.e. an SB 16. +Here is a table of SB types: + +Legacy SB +SB Pro +SB 2.0 +SB Pro 2.0 +SB Pro MCV +SB 16 + + + + + + + Configuring the Applications + + +Bearing in mind what was stated above, the following are the +recommended methods for configuring applications to use SBEMU. The +first is the preferred method. If you need to manually set any +parameters remember to use those you configured DOSEMU with. + + + + Midi with Digital Audio + + +Configure the applications to use 'General Midi' for Music, and 'SB' +for Digital Audio. +This will work if your card have a Wave Table (/dev/midi) +or hardware mixing ability (TiMidity++). + + + + + + SB Music with SB Audio + + +Configure the application with 'SB' for Music and Audio. Note that if +the system plans to use FM for the music you will not get music. + + + + + + Midi Music, No Audio + + +Configure the application with 'General Midi' for Music and no Audio. + + + + + + + + + + Debugging SBEMU + + +The debugging output for SBEMU is enabled in the same way as all +DOSEMU debug, and collected in the same manner. The debug flag for +sound is 'S'. Because the DMA controller is hardware it uses the debug +flag 'h'. To get basic debugging information into a file called +sound.out use: + + + + + +% dosemu.bin -D-a+Sh -o sound.out + + + + + +You can get slightly more verbose output if you use level 2 debugging +(e.g. use -D-a+2Sh). If you need someone +else to look at the +debug output then feel free to send it to the list or report it at + +the SourceForge bug tracker at +http://sourceforge.net/tracker/?atid=457447&group_id=49784&func=browse +, +but PLEASE, +remove any unnecessary debug output, and give a DETAILED description +of the problem. + + + + +
diff --git a/src/doc/README/timing b/src/doc/README/timing new file mode 100644 index 0000000..8bc4eaa --- /dev/null +++ b/src/doc/README/timing @@ -0,0 +1,207 @@ + +Timing issues in dosemu + + +This section written by Alberto Vignani +<vignani@mbox.vol.it> +, Aug 10, 1997 + + + +The 64-bit timers + + +The idea for the 64-bit timers came, of course, from using the pentium cycle +counter, and has been extended in dosemu to the whole timing system. + + + +All timer variables are now defined of the type hitimer_t (an +unsigned long long, see include/types.h). + + + +Timers in dosemu can have one of the following resolutions: + + + + + + MICROSECOND, for general-purpose timing + TICK (838ns), for PIT/PIC emulation + + + + + +You can get the current time with the two functions + + + + + + GETusTIME for 1-usec resolution + GETtickTIME for 838ns resolution + + + + + +The actual timer used (kernel or pentium TSC) is configured at runtime +(it defaults to on for 586 and higher CPUs, but can be overridden both +by the -[2345] command-line switch or by the "rdtsc on|off" statement +in the config file). + + + +The DOS time is now kept in the global variable pic_sys_time. +See the DANG notes about emu-i386/cputime.c for details. + + + + + +DOS 'view of time' and time stretching + + +The time stretcher implements DOS 'view of time', as opposed to the +system time. +It would be very easy to just give DOS its time, by incrementing it +only when the dosemu process is active. To do that, it is enough to +get SIGPROF from the kernel and (with some adjustments for sleeps) +calculate the CPU% used by dosemu. +The real tricky part comes when we try to get this stuff back in +sync with real time. We must 'stretch' the time, slowing it down +and speeding it up, so that the DOS time is always trying to catch +the real one. + + + +To enable the time stretcher start dosemu with the command-line option +-t. If your CPU is not a 586 or higher, dosemu will exit with +an error message. +Not that this algorithm doesn't run on a 486 - it was tested and +was quite successful - but the use of gettimeofday() makes it a bit +too heavy for such machines. Hence, it has been restricted to the +pentium-class CPUs only, which can use the CPU timer to reduce use +of kernel resources. + + + + + +Non-periodic timer modes in PIT + + +Normally, PIT timer 0 runs in a periodic mode (mode 2 or 3), it counts down +to 0 then it issues an int8 and reinitializes itself. But many demos and +games use it in one of the non-periodic (NP) modes (0 or 1): the timer counts +down to 0, issues the interrupt and then stops. In NP modes, +software intervention is required to keep the timer running. +The NP modes were not implemented by dosemu-0.66, and this is the reason why +some programs failed to run or required strange hacks in the dosemu code. +To be fair, most of these games ran also in dosemu-0.66 without any specific +hack, but looking at the debug log always made me call for some type of +divine intervention :-) + + + +The most common situation is: the DOS program first calibrates itself by +calculating the time between two vertical screen retraces, then programs the +timer 0 in a NP mode with a time slightly less than this interval; the int8 +routine looks at the screen retrace to sync itself and then restarts the +timer. You can understand how this kind of setup is very tricky to emulate +in a multitasking environment, and sometimes requires using the +emuretrace feature. But, at the end, many demos that do not +run under Win95 actually run under dosemu. + + + + + +Fast timing + + +By "fast timing", I define a timer 0 period less than half of the Linux +"jiffie" time (10ms). This is empirically determined - programs that use a +timer period greater than 5ms usually run well with the 'standard' timing of +dosemu-0.66. + + + +There will always be a speed limit (the PIT can be programmed up to 500kHz), +but this is surely higher now. As an example, if you run M$ Flight +Simulator 0.5 with PC speaker enabled, as soon as a sound starts the PIT is +set for an interrupt rate of 15kHz (and the int8 routine is really nasty, in +that it _keeps_count_ of the events); dosemu-0.66 just crashes, while now +the sound is not perfect but reasonabily near to the real thing (but do not +try to activate the debug log, or it will take forever!) + + + +Fast timing was not easy to achieve, it needs many tricks esp. to avoid +interrupt bursts in PIC; it is still NOT completely working on slow machines +(386,486) - but it will maybe never work for all cases. + + + + + +PIC/PIT synchronization and interrupt delay + + +Another tricky issue... There are actually two timing systems for int8, the +one connected to the interrupt in PIC, the other to port 0x43 access in PIT. +Things are not yet perfectly clean, but at least now the two timings are +kept in sync and can help one another. + + + +One of the new features in PIC is the correct emulation of interrupt delay +when for any reason the interrupt gets masked for some time; as the time for +int8 is delayed, it loses its sync, so the PIT value will be useful for +recalculating the correct int8 time. (This case was the source for many int +bursts and stack overflows in dosemu-0.66). + + + +The contrary happens when the PIT is in a NP mode; any time the timer is +restarted, the PIT must be reset too. And so on. + + + + + +The RTC emulation + + +There is a totally new emulation of the CMOS Real Time Clock, complete +with alarm interrupt. A process ticks at exactly 1sec, always in +real (=system) time; it is normally implemented with a thread, but can be +run from inside the SIGALRM call when threads are not used. + + + +Also, int0x1a has been mostly rewritten to keep in sync with the new CMOS +and time-stretching features. + + + + + +General warnings + + +Do not try to use games or programs with hi-freq timers while running heavy +tasks in the background. I tried to make dosemu quite robust in such +respects, so it will probably NOT crash, but, apart being unplayable, +the game could behave in unpredictable ways. + + + + + + diff --git a/src/doc/README/vif b/src/doc/README/vif new file mode 100644 index 0000000..a2ce9c0 --- /dev/null +++ b/src/doc/README/vif @@ -0,0 +1,207 @@ + +The Virtual Flags + + +Info written by Hans +<lermen@fgan.de> +to describe the virtual flags used by DOSEMU + + + + + + + + DOS sees only IF, but IF will never really set in the CPU flagregister, +because this would block Linux. So Linus maintains a virtual IF (VIF), +which he sets and resets accordingly to CLI, STI, POPFx, IRETx. +Because the DOS programm cannot look directly into the flagregister +(exception the low 8 bits, but the IF is in bit 9), it does not +realize, that the IF isn't set. To see it, it has to perform +PUSHF and look at the stack. + + + +Well, but PUSHF or INTxx is intercepted by vm86 and then Linus looks +at his virtual IF to set the IF on the stack. +Also, if IRETx or POPFx happen, Linus is getting the IF from the +stack, sets VIF accordingly, but leaves the real IF untouched. + + + + + Now, how is this realized? This is a bit more complicated. +We have 3 places were the eflag register is stored in memory: + + + + + + +vm86s.regs.eflags + + + in user space, seen by dosemu + + + +current->tss.v86flags + + + virtual flags, macro VEFLAGS + + + +current->tss.vm86_info->regs.eflags + + + the real flags, CPU reg. VM86 + + + + + + + +The last one is a kernel space copy of vm86_struct.regs.eflags. + + + +Also there are some masks to define, which bits of the flag +should be passed to DOS and which should be taken from DOS: + + + + + + +current->tss.v86mask + + + CPU model dependent bits + + + +SAFE_MASK (0xDD5) + + + used the way to DOS + + + +RETURN_MASK (0xDFF) + + + used the way from DOS + + + + + + + +When sys_vm86 is entered, it first makes a copy of the whole +vm86_struct vm86s (containing regs.eflags) and saves a pointer to it +to current->tss.vm86_info. It merges the flags from 32-bit user +space (NOTE: IF is always set) over SAFE_MASK and current->tss.v86mask +into current->tss.v86mask. From this point on, all changes to +VIF, VIP (virtual interrupt pending) are only done in VEFLAGS. +To handle the flag setting there are macros within vm86.c, +which do the following: + + + + + + +set_IF, clear_IF + + + only modifies VEFLAGS; + + + +clear_TF + + + sets a bit in the real flags; + + + +set_vflags(x) + + + set flags x over SAFE_MASK to real flags +(IF is not touched) + + + +x=get_vflags + + + returns real flags over RETURN_MASK and translates +VIF of VEFLAGS to IF in x; + + + + + + + +When it's time to return from vm86() to user space, the real flags +are merged together with VIF and VIP from VEFLAGS and put into +the userspace vm86s.regs.eflags. This is done by save_v86_state() +and this does not translate the VIF to IF, it should be as it +was on entry of sys_vm86: set to 1. + + + + + Now what are we doing with eflags in dosemu ? +Well, this I don't really know. I saw IF used (told it Larry), I saw +VIF tested an set, I saw TF cleared, and NT flag e.t.c. + + + +But I know what Linus thinks that we should do: +Always interpret and set VIF, and let IF untouched, it will nevertheless +set to 1 at entry of sys_vm86. + + + +How I think we should proceed? Well this I did describe in my last mail. + + + +,,,, and this from a follow-up mail: + + + +NOTE VIF and VIP in DOS-CPU-flagsregister are inherited from 32-bit, +so actually they are both ZERO. + + + +On return to 32-bit, only VIF will appear in vm86s.regs.eflags ! +VIP will be ZERO, again: VIP will be used only once !!!! + + + +[ ,,, ] + + + +I have to add, that VIP will be cleared, because it is not in any +of the masks of vm86. + + + + + + + + + diff --git a/src/doc/README/vmodem b/src/doc/README/vmodem new file mode 100644 index 0000000..f13ef21 --- /dev/null +++ b/src/doc/README/vmodem @@ -0,0 +1,44 @@ + +Out of a posting from Julia A. Case + +I'm not much of a documentation writer so if all this doesn't make sense +feel free to write me at julie@MageNet.com for more info. I wanted a +better way of running doors than in local mode via dosemu. And this is what +I managed to do to fix that problem. + +This patch will allow you to add an additional keyword to the serial +configuration in dosemu.conf. The 'virtual' flag will allow you to set the +current tty that dosemu is running on to be a virtual modem. For things to +work right you need to make the following changes in your dosemu.conf file. + +1. Remove all video definitions but the terminal. This forces terminal +mode. Which we need to avoid local video. +2. add serial { virtual com 1 } this makes the current tty COM1 + ^^^^^^^^^^^^^^^^^^^^^^^^ +(**Note from Hans: this is $_comX = "virtual" for dosemu-1.0.1 + X being one of 1,2,3,4) + +Now when you run an dosemu it will only display the data that is going to +the COM port. A DOS door can be run in remote mode vs. the local mode that +we have all been running them in. On bug that I've found is that on a +non-modem tty the speed defaults to 2400 baud. I tried to change this in +dosemu, but it really didn't work. What I did find that works though is to +get down X00v150.ZIP. X00 is a very good fossil, which some doors will +need, beyond that though it has a utility called XU.EXE that lets you set +the port speed, I call this from autoexec and set the port speed to 57600 +and everything works fine. + +This should allow for RIPScrip doors to be run, but at present my testing +has show the acceptance of my terminal being RIP compatable to be less than +reliable. I'm not sure what is causing that. I'll keep looking at it +though. + +I've run the following doors via this mode. + 1. Solar Realms Elite + 2. Barren Realms Elite + 3. Falcons Eye + 4. Global Wars + 5. Terra Firma + 6. Legend of the Red Dragon + 7. Exitilus + diff --git a/src/doc/tools/doSgmlTools.pl b/src/doc/tools/doSgmlTools.pl new file mode 100755 index 0000000..d6651aa --- /dev/null +++ b/src/doc/tools/doSgmlTools.pl @@ -0,0 +1,396 @@ +#!/usr/bin/perl -w + +# +# This script wraps the DOSEMU calls to sgmltools to try to get the best +# output on as many systems as possible. It can also be used to upgrade +# parts of your SGML system. +# + +use strict; +#use Getopt::Mixed; +use Getopt::Std; +use File::Basename; + +my ($openJadeOptions) = "-V %generate-article-toc% -V %section-autolabel% -V %shade-verbatim% -V %indent-screen-lines%=' ' -V '(define (toc-depth nd) 3 )'"; +my ($lynxOptions) = "-force_html -nolist -dump"; + +my ($softVer) = "0.99"; + +#&Getopt::Mixed::getOptions("v verbose>v V Version>V t text>t h html>h ? help>? c check>c"); +&getopts('vVth?c'); + +my ($verbose) = 0; + +if (defined $main::opt_V && $main::opt_V == 1) { + &displayVersion; + exit(0); +} + +if ((defined $main::opt__ && $main::opt__ == 1) + || (!defined $main::opt_h && !defined $main::opt_t + && !defined $main::opt_c)) { + &usage(); + exit(0); +} + +if (defined $main::opt_v && $main::opt_v == 1) { + $verbose = 1; + $| = 1; + print "Being verbose\n"; +} + +my (%versions) = &getSoftwareVersions(); +my (%theStylesheets) = &getStylesheets(); + +if (defined $main::opt_c && $main::opt_c == 1) { + &checkSetup(); + exit(0); +} + +my ($theFile); + +# Process the files + +if (defined $main::opt_h && $main::opt_h == 1) { + &convertToHTML($ARGV[0], $ARGV[1]); +} +if (defined $main::opt_t && $main::opt_t == 1) { + &convertToText($ARGV[0], $ARGV[1]); +} + + +sub usage { +print <<"EO_USAGE"; +$0 usage: + + $0 [options] [actions] [file ...] + + Options: + -v, --verbose + Turn on verbose output + + Actions: + -h, --html + Convert to HTML + + -s, --htmls + Convert to multiple HTML files + + -t, --text + Convert to Text + + -V, --Version + Print version number and exit + + -c, --check + Check the setup + +At least one of the actions conversions must be specified. + +EO_USAGE +} + +sub getSoftwareVersions { + my (%versions, $result); + + # Check for SGMLTOOLS + + $result = `sgmltools -V`; + + if ($result =~ /version ([^ \n]+)/) { + $versions{'sgmltools'} = $1; + if ($verbose) { + print "Found SGMLtools version $1\n"; + } + } else { + $result = `sgml2html --wibblefoo`; + if ($result =~ /papersize/) { + $versions{'sgmltools'} = 1; + if ($verbose) { + print "Found SGMLtools version 1\n"; + } + } + } + + # Check for (open)Jade + + $result = `openjade -v --wibblefoo 2>&1`; + + if ($result =~ /\"?[Oo]pen[Jj]ade\"? version "([^\"]+)"/) { + $versions{'openjade'} = $1; + if ($verbose) { + print "Found OpenJade version $1\n"; + } + } + + $result = `jade -v --wibblefoo 2>&1`; + if ($result =~ /Jade version "([^\"]+)"/) { + $versions{'jade'} = $1; + if ($verbose) { + print "Found Jade version $1\n"; + } + } + + # Check for Lynx + + $result = `lynx -version`; + + if ($result =~ /Lynx Version ([^ ]+)/) { + $versions{'lynx'} = $1; + if ($verbose) { + print "Found Lynx version $1\n"; + } + } + + return %versions; +} + +sub getStylesheets { + my (%theStylsheet); + my (@theCatalogs) = (); + my ($thisCatalog); + my ($line, $file); + my ($type, $fname); + + if (exists $ENV{'SGML_CATALOG_FILES'}) { + @theCatalogs = split(/:/,$ENV{'SGML_CATALOG_FILES'}); + } else { + if ( -e "/etc/sgml/catalog") { + + if ($verbose) { + print "Added entry for \"/etc/sgml/catalog\" to catalog list\n"; + } + + unshift (@theCatalogs, "/etc/sgml/catalog"); + } + if ( -e "/usr/local/lib/sgml/catalog") { + + if ($verbose) { + print "Added entry for \"/usr/local/lib/sgml/catalog\" to catalog list.\n"; + } + + unshift (@theCatalogs, "/usr/local/lib/sgml/catalog"); + } + } + + while ($#theCatalogs >= 0) { + $thisCatalog = shift (@theCatalogs); + + if (! defined $thisCatalog || length($thisCatalog) < 1) { + if ($verbose) { + print "Skipping null catalog entru\n"; + } + next; + } + + open (CATFILE, $thisCatalog) + or do { + warn "Failed to open catalog file: $thisCatalog\n"; + next; + }; + + if ($verbose) { + print "Scanning catalog file: $thisCatalog\n"; + } + + $file = ""; + while (defined ($line = )) { + $file .= $line; + } + close (CATFILE); + + while ($file =~ /CATALOG\s+\"([^\"]+)\"/gm) { + if ($verbose) { + print "Adding entry for \"$1\" to catalog list.\n"; + } + + unshift (@theCatalogs, $1); + } + + while ($file =~ m!PUBLIC\s+\"-//.*//.*DocBook (Print|HTML) Stylesheet//EN\"\s+(\S+)!gm) { + $type = lc($1); + $fname = $2; + if (substr($fname,0,1) ne "/") { + $fname = &dirname($thisCatalog) ."/" .$fname; + } + + if ($verbose) { + print "Set \"$type\" stylesheet to \"$fname\".\n"; + } + + $theStylesheets{$type} = $fname; + } + + } + + return %theStylesheets; +} + +sub convertToHTML { + my ($theFile, $theOutputFile) = @_; + my ($command); + + if (! defined $theOutputFile) { + print "Output file not specified\n"; + exit(1); + } + + if ($verbose) { + print "Converting $theFile to HTML: $theOutputFile\n"; + } + + $command = ""; + + if (exists $versions{'sgmltools'} && $versions{'sgmltools'} ge "3.0.2") { + # Assume Version 3.0.2 and above are. + $command = "sgmltools -b onehtml --jade-opt=\"$openJadeOptions\" $theFile"; + + } elsif (exists $versions{'sgmltools'} && $versions{'sgmltools'} ge "3.0") { + # Assume Version 3 and above are. + $command = "sgmltools -b html -jade-opt=\"-V nochunks $openJadeOptions -o $theOutputFile \" $theFile"; + + } elsif (exists $versions{'openjade'} && $versions{'openjade'} ge "1.3") { + # Try running openjade (>= 1.3) directly. + $command = "openjade -t sgml -V nochunks $openJadeOptions -d " .$theStylesheets{'html'} ." $theFile > $theOutputFile"; + + } elsif (exists $versions{'openjade'}) { + # Try running openjade directly. + $command = "openjade -t sgml -V nochunks $openJadeOptions -d " .$theStylesheets{'html'} ." $theFile > $theOutputFile"; + + } elsif (exists $versions{'jade'}) { + # Try running jade directly. + $command = "jade -t sgml -d " .$theStylesheets{'html'} ." $theFile > $theOutputFile"; + + } elsif (exists $versions{'sgmltools'} && $versions{'sgmltools'} ge "2.0") { + # Assume Version 2 + $command = "sgmltools -b html -o $theOutputFile $theFile"; + + } + + if (length($command)) { + if ($verbose) { + print "About to execute:\n$command\n"; + } + + system $command; + } else { + print "No command to execute -- can't convert.\n"; + } +} + +sub convertToText { + my ($theFile, $theOutputFile) = @_; + if (! defined $theOutputFile) { + print "Output file not specified\n"; + exit(1); + } + my ($theHTMLFile) = ""; + + my ($command); + + if ($verbose) { + print "Converting $theFile to text: $theOutputFile\n"; + } + + if (exists $versions{'sgmltools'} && $versions{'sgmltools'} ge "3.0.2") { + # Assume Version 3.0.2 and above are. + $command = "sgmltools -b lynx --jade-opt=\"$openJadeOptions\" $theFile"; + } else { + if (!defined $main::opt_h) { + # Need to build the HTML first + $theHTMLFile = $theOutputFile.".t.html"; + &convertToHTML($theFile, $theHTMLFile); + } else { + $theHTMLFile = $theOutputFile; + $theHTMLFile =~ s/\.txt$/\.html/; + } + +# if (exists $versions{'lynx'} && $versions{'lynx'} ge "2.8.3") { +# $command = "lynx $lynxOptions -with_backspaces $theHTMLFile > $theOutputFile"; +# } elsif (exists $versions{'lynx'}) { + $command = "lynx $lynxOptions $theHTMLFile > $theOutputFile"; +# } + } + + if (length($command)) { + if ($verbose) { + print "About to execute:\n$command\n"; + } + + system $command; + } else { + print "No command to execute -- can't convert.\n"; + } + + if (!defined $main::opt_h) { + if ($verbose) { + print "Cleaning up temporary HTML file\n"; + } + + if (length($theHTMLFile)) { + unlink ($theHTMLFile); + } + } +} + +sub displayVersion { + print "$0: version $softVer\n"; +} + +sub checkSetup { + print "OpenJade [", + (exists $versions{'openjade'}) ? $versions{'openjade'} : "Not installed", + "] - "; + if (exists $versions{'openjade'}) { + if ($versions{'openjade'} ge "1.3") { + print "OK\n"; + } else { + print "upgrade to at least version 1.3 (http://openjade.sourcefourge.net/)\n"; + } + } else { + print "install at least OpenJade version 1.3 (http://openjade.sourcefourge.net/)\n"; + } + + print "Jade [", + (exists $versions{'jade'}) ? $versions{'jade'} : "Not installed", + "] - OpenJade is preferred\n"; + + my ($styleVer) = "Not installed"; + + my ($line); + if (exists $theStylesheets{'html'}) { + open (STYLE,$theStylesheets{'html'}) + or warn ("Failed to open HTML stylesheet \"" .$theStylesheets{'html'} ."\": $!\n"); + while (defined ($line =