[
  {
    "path": ".gitignore",
    "content": "*.o\n\n"
  },
  {
    "path": "CLAUDE.md",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nGSplus is a cross-platform Apple IIgs emulator based on KEGS (Kent's Emulated GS) by Kent Dickey. It emulates the 65816 CPU, all Apple IIgs graphics/sound modes, disk controllers, serial ports, and more. Licensed under GPLv3.\n\n## Repository Structure\n\n- `gsplus/src/` — Active source code and build files (this is where you build)\n- `gsplus/lib/` — Icons, NIB files, and asset resources\n- `upstream/kegs/` — Upstream KEGS tracked separately, merged to main when updated\n- `upstream/kegs/doc/` — Comprehensive documentation (architecture internals, platform setup, compatibility)\n\nThe `upstream` branch tracks KEGS releases; `main` is the primary development branch.\n\n## Build Commands\n\n### macOS (default target)\n```bash\ncd gsplus/src\nmake -j 20\n```\nProduces `gsplus/KEGSMAC.app`. Requires Xcode with command-line tools installed.\n\n### Linux (X11)\n```bash\ncd gsplus/src\nrm vars; ln -s vars_x86linux vars\nmake -j 20\n```\nProduces `xkegs`. Requires `libX11-devel`, `libXext-devel`, `pulseaudio-libs-devel`.\n\n### Windows\nOpen `gsplus/src/kegswin.vcxproj` in Visual Studio Community Edition and press F7.\n\n### Clean\n```bash\ncd gsplus/src\nmake clean\n```\n\n## Architecture\n\n### CPU & Core Loop\n- `sim65816.c` — Main simulation loop, event scheduling, interrupt handling\n- `engine_c.c` + `engine.h` — 65816 CPU instruction emulation (macro-heavy for performance)\n- `defs_instr.h`, `instable.h`, `op_routs.h` — Instruction definitions, opcode table, operation macros\n\n### Memory\n- `moremem.c` — Memory management, page table fixup, I/O register reads/writes ($C000-$C0FF area)\n- Page-table-based MMU for dynamic address mapping\n\n### Video\n- `video.c` — All Apple IIgs/II graphics mode rendering (text, lores, hires, super hires)\n\n### Audio\n- `sound.c` — Sound generation (mixing, output buffering)\n- `doc.c` — Ensoniq DOC 32-voice synthesizer emulation\n- `mockingboard.c` — Mockingboard A card (6522 VIA + AY-8913)\n\n### Disk I/O\n- `iwm.c` — IWM disk controller (5.25\" and 3.5\" drives, nibble-level accuracy)\n- `smartport.c` — SmartPort controller for hard drive images\n- `dynapro.c` — Host directory mounting as virtual ProDOS volumes\n- `woz.c` — WOZ disk image format support\n- `unshk.c`, `undeflate.c`, `applesingle.c` — Archive/compression format support\n\n### Input\n- `adb.c` — Apple Desktop Bus (keyboard, mouse)\n- `scc.c` + `scc_socket_driver.c` — Serial Communications Controller with TCP/IP modem emulation\n- `paddles.c`, `joystick_driver.c` — Game input\n\n### Configuration & Debug\n- `config.c` — Runtime configuration UI, disk mounting, settings persistence (`config.kegs`)\n- `debugger.c` — Built-in 65816 debugger/monitor\n\n### Platform Drivers\nThe emulator core is platform-independent. Platform-specific code is isolated in driver files:\n\n| Component | macOS | Linux | Windows |\n|-----------|-------|-------|---------|\n| Display | `AppDelegate.swift` + `MainView.swift` | `xdriver.c` | `windriver.c` |\n| Audio | `macsnd_driver.c` (CoreAudio) | `pulseaudio_driver.c` | `win32snd_driver.c` |\n| Serial | `scc_unixdriver.c` | `scc_unixdriver.c` | `scc_windriver.c` |\n\n### Key Headers\n- `defc.h` — Global defines, structs, macros (included by nearly every .c file)\n- `defcomm.h` — Shared defines for C and assembly\n- `protos_base.h` — Function prototypes for all modules\n\n## Build System Details\n\nThe Makefile includes `vars` (platform config) and `ldvars` (object file list). To change platforms, symlink the appropriate vars file (`vars_mac`, `vars_x86linux`, etc.) to `vars`. Swift files are compiled via the `comp_swift` wrapper script. The `dependency` file contains header dependency rules.\n\nCompiler flags are set in `vars`: `-Wall -O2 -DMAC` for macOS. The `-DMAC` define selects macOS-specific code paths throughout the codebase.\n\n## Runtime Requirements\n\nThe emulator needs an Apple IIgs ROM file to run. Demo disk images (`NUCLEUS03.gz`, `XMAS_DEMO.gz`) are included in `upstream/kegs/`. Configuration is stored in `config.kegs`.\n\n## No Test Suite\n\nThere is no automated test infrastructure. Testing is done manually by running Apple IIgs software and ROM self-tests.\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "README.md",
    "content": "# gsplus\nCross-platform Apple IIgs emulator and tools based on KEGS\n\n\n### About Branches\n- KEGS latest is tracked in `./upstream` directory\n- There is an upstream branch that is updated whenever there are new version and merged to main.\n- This makes it easy to track kegs changes somewhat independently of the gsplus work. \n"
  },
  {
    "path": "TODO.md",
    "content": "# TODO\n\n## Backlog\n\n- [ ] Remove `#ifdef INCLUDE_RCSID_C` / `#endif` guards from header files and `#define`/`#undef INCLUDE_RCSID_C` from `sim65816.c` and `sound.c` in `gsplus/src/`. The `const char rcsid_` lines were already removed; these are the leftover scaffolding.\n\n- [ ] Update Menubar titles for X/Win to match..  Mac build already updated to \"GS+\""
  },
  {
    "path": "gsplus/lib/make_mac_icon",
    "content": "#!/usr/bin/perl -w\n\n# Based on https://gist.github.com/ansarizafar/6fa64f44aa933794c4d6638eec32b9aa\n# and https://github.com/retifrav/generate-iconset\n# We need to create a directory of the icon in several scaled sizes,\n#  (the Mac command \"sips\" can do this), then run iconutil to form the\n#  .icns file.\n# kegsicon.png created by Alex Lee\n\nmy $icondir;\nmy $img_file = \"\";\nmy $ext = \".png\";\nmy $scale;\nmy $sz;\nmy $pixels;\nmy $scale_str;\n\nif($#ARGV == 0) {\n\t$img_file = shift;\n\tif($img_file =~ /^.*\\.(^\\.*)$/) {\n\t\t$ext = $1;\n\t\tprint \"Set ext to $ext\\n\";\n\t}\n} else {\n\tdie \"Usage: $0 image_file.jpg/.png\"\n}\n\n$icondir = \"./icon.iconset\";\t# Must have .iconset extension\nif(-d $icondir) {\n\t`rm -rf $icondir`;\n}\n\n`mkdir $icondir`;\nfor($scale = 1; $scale <= 2; $scale++) {\n\tfor($sz = 16; $sz <= 512; $sz = $sz * 2) {\n\t\tif($sz == 64) {\n\t\t\tnext;\n\t\t}\n\t\t$pixels = $sz * $scale;\n\t\t$scale_str = \"\";\n\t\tif($scale == 2) {\n\t\t\t$scale_str = '@2x';\n\t\t}\n\t\t@cmd = (\"sips\", \"-z\", $pixels, $pixels, $img_file,\n\t\t\t\"--matchTo\",\n\t\t\t\"/System/Library/ColorSync/Profiles/sRGB\\\\ Profile.icc\",\n\t\t\t\"--out\", $icondir . \"/\" .  \"icon_\" . $sz . \"x\" . $sz .\n\t\t\t$scale_str . $ext);\n\t\tprint \"cmd: @cmd\\n\";\n\t\t`@cmd`;\n\t}\n}\n\nprint \"Calling: iconutil -o kegs.icns -c icns $icondir\";\n`iconutil -o kegs.icns -c icns $icondir`;\n`rm -rf $icondir`;\n\n"
  },
  {
    "path": "gsplus/src/AppDelegate.swift",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2024 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\nimport Cocoa\n\nlet Context_draw = false\n\t// Default: use safe draw function.\n\t// If true, use NSGraphicsContext.current.data to try to write\n\t//  directly to screen memory (in a different ARGB format)\n\nclass Window_info {\n\tvar x_win : NSWindow? = nil\n\tvar mac_view : MainView? = nil\n\tvar kimage_ptr : UnsafeMutablePointer<Kimage>! = nil\n\tvar title : String = \"\"\n\tvar app_delegate : AppDelegate! = nil\n\tvar mac_a2_height : Int = 0\n\n//\tinit(_ new_is_main: Bool) {\n//\t\tis_main = new_is_main\n//\t}\n\n\tfunc set_kimage(_ kimage_ptr : UnsafeMutablePointer<Kimage>!,\n\t\t\t\ttitle: String, delegate: AppDelegate!) {\n\t\tself.kimage_ptr = kimage_ptr\n\t\tself.title = title\n\t\tself.app_delegate = delegate\n\t}\n\n\tfunc create_window() {\n\t\tlet x_xpos = Int(video_get_x_xpos(kimage_ptr))\n\t\tlet x_ypos = Int(video_get_x_ypos(kimage_ptr))\n\t\tlet width = Int(video_get_x_width(kimage_ptr))\n\t\tlet height = Int(video_get_x_height(kimage_ptr))\n\t\tlet windowRect = NSRect(x: x_xpos, y: x_ypos, width: width,\n\t\t\t\t\t\t\theight: height)\n\t\tvar window : NSWindow!\n\t\tvar view : MainView!\n\t\tlet style : NSWindow.StyleMask = [.titled, .closable,\n\t\t\t\t\t\t\t\t.resizable]\n\n\t\twindow = NSWindow(contentRect: windowRect,\n\t\t\tstyleMask: style,\n\t\t\tbacking: .buffered, defer: false)\n\n\t\tlet viewRect = NSRect(x: 0, y: 0, width: windowRect.size.width,\n\t\t\t\t\t\theight: windowRect.size.height)\n\t\tprint(\"About to init MainView\");\n\t\tview = MainView(frame: viewRect)\n\t\tprint(\"About to set kimage_ptr\");\n\t\tview.kimage_ptr = kimage_ptr;\n\t\tprint(\"About to call initialize\");\n\t\tview.initialize()\n\t\tview.closed = false\n\n\t\twindow.delegate = app_delegate\n\t\twindow.contentView = view\n\t\twindow.makeKeyAndOrderFront(app_delegate)\n\t\twindow.acceptsMouseMovedEvents = true\n\t\twindow.title = title\n\t\twindow.showsToolbarButton = true\n\t\twindow.contentAspectRatio = NSSize(width: width, height: height)\n\n\t\tvideo_set_active(kimage_ptr, Int32(1))\n\t\tvideo_update_scale(kimage_ptr, Int32(width), Int32(height),\n\t\t\t\t\t\t\t\tInt32(1))\n\n\t\tx_win = window\n\t\tmac_view = view\n\t\tmac_a2_height = height;\n\t\twindow.makeKey()\n\t}\n\n\tfunc update() {\n\t\t// Decide if window should be opened/closed (if it's the\n\t\t//  debugger window), and call mac_update_display() to update\n\t\tlet new_height = Int(video_get_a2_height(kimage_ptr))\n\t\tlet a2_active = video_get_active(kimage_ptr)\n\t\tif let view = mac_view {\n\t\t\tif(new_height != mac_a2_height) {\n\t\t\t\tmac_resize_window()\n\t\t\t}\n\t\t\tif(a2_active == 0 && !view.closed) {\n\t\t\t\tprint(\"a2_active 0 on \\(title), calling close\")\n\t\t\t\tx_win!.orderOut(x_win)\n\t\t\t\tview.closed = true\n\t\t\t} else if(a2_active != 0 && view.closed) {\n\t\t\t\tprint(\"Opening closed window \\(title)\")\n\t\t\t\tview.closed = false\n\t\t\t\tx_win!.orderFront(x_win)\n\t\t\t\tx_win!.makeKey()\t\t// Move to front\n\t\t\t} else if(a2_active != 0) {\n\t\t\t\tview.mac_update_display()\n\t\t\t}\n\t\t\tif((a2_active != 0) && !view.closed) {\n\t\t\t\tif(adb_get_copy_requested() != 0) {\n\t\t\t\t\tview.do_copy_text(view);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif(a2_active != 0) {\n\t\t\t\tprint(\"Opening window \\(title)\")\n\t\t\t\tcreate_window()\n\t\t\t}\n\t\t}\n\t}\n\tfunc mac_resize_window() {\n\t\tlet a2_height = Int(video_get_a2_height(kimage_ptr))\n\t\tlet a2_width = Int(video_get_a2_width(kimage_ptr))\n\t\tlet ratio = CGFloat(a2_height) / CGFloat(a2_width)\n\n\t\tlet cur_width = x_win!.frame.size.width\n\t\tlet new_height = cur_width * ratio\t// CGFloat\n\t\tvar newframe = x_win!.frame\t\t// NSRect\n\t\tmac_a2_height = a2_height\n\t\tnewframe.size.height = new_height\n\t\tx_win!.contentAspectRatio = NSSize(width: a2_width,\n\t\t\t\t\t\t\theight: a2_height)\n\t\tx_win!.setFrame(newframe, display: true, animate: true)\n\t\tmac_view!.initialize()\n\t\t\t// Must call initialize for the case where the\n\t\t\t//  status lines were enabled, we need more lines\n\t\t// print(\"Call video_update_scale from mac_resize_window\\n\");\n\t\tvideo_update_scale(kimage_ptr, Int32(x_win!.frame.width),\n\t\t\t\t\t\tInt32(x_win!.frame.height), 0)\n\t\tvideo_update_xpos_ypos(kimage_ptr, Int32(x_win!.frame.origin.x),\n\t\t\t\t\t\tInt32(x_win!.frame.origin.y))\n\t\t//print(\"Did mac_resize window to \\(a2_width), \\(a2_height)\" +\n\t\t//\t\"  frame:\\(x_win!.frame.width), \" +\n\t\t//\t\t\t\t\"\\(x_win!.frame.height)\" +\n\t\t//\t\" ratio:\\(ratio)\\n\")\n\t}\n\n\tfunc update_window_size(width: Int, height: Int) {\n\t\t// print(\"Call video_update_scale from update_window_size\\n\");\n\t\tvideo_update_scale(kimage_ptr, Int32(width), Int32(height), 0);\n\t}\n}\n\n@main\nclass AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {\n\n\tstatic func main() {\n\t\tlet delegate = AppDelegate()\n\t\tNSApplication.shared.delegate = delegate\n\t\t_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)\n\t}\n\n\tvar mainwin_info = Window_info();\n\tvar debugwin_info = Window_info();\n\n\tfunc find_win_info(_ window: NSWindow) -> Window_info {\n\t\tif(mainwin_info.x_win == window) {\n\t\t\treturn mainwin_info\n\t\t}\n\t\treturn debugwin_info\n\t}\n\t@objc func do_about(_:AnyObject) {\n\t\tprint(\"About\")\n\t\tif let ver_str = Bundle.main.infoDictionary?[\n\t\t\t\t\"CFBundleShortVersionString\"] as? String {\n\t\t\tNSApplication.shared.orderFrontStandardAboutPanel(\n\t\t\t\toptions: [.applicationVersion: ver_str,\n\t\t\t\t\t.version: \"\", .applicationName: \"GS+\"])\n\t\t}\n\t}\n\tfunc applicationDidFinishLaunching(_ aNotification: Notification) {\n\t\t// This is your first real entry point into the app\n\t\tprint(\"start!\")\n\t\tset_menu_for_kegs()\n\t\tNSApp.activate(ignoringOtherApps: true)  // Bring window to front\n\t\tmain_init()\n\t}\n\n\tfunc applicationWillTerminate(_ aNotification: Notification) {\n\t\t// Insert code here to tear down your application\n\t}\n\n\tfunc applicationShouldTerminateAfterLastWindowClosed(\n\t\t\t\t_ theApplication: NSApplication) -> Bool {\n\t\t// Application will close if main window is closed\n\t\treturn true\n\t}\n\tfunc windowDidBecomeKey(_ notification: Notification) {\n\t\tif let w = notification.object as? NSWindow {\n\t\t\tif(w == mainwin_info.x_win) {\n\t\t\t\tadb_mainwin_focus(Int32(1));\n\t\t\t\t//print(\"Main window became KEY\")\n\t\t\t}\n\t\t}\n\t\t//print(\"DidbecomeKey\")\n\t\t// If window focus is changing, turn off key repeat\n\t\tadb_kbd_repeat_off()\n\t}\n\tfunc windowDidResignKey(_ notification: Notification) {\n\t\t//print(\"DidResignKey\")\n\t\tadb_kbd_repeat_off()\n\t\tadb_mainwin_focus(Int32(0))\n\t\tCGDisplayShowCursor(CGMainDisplayID())\n\t}\n\tfunc windowDidMove(_ notification: Notification) {\n\t\t//print(\"DidMove\")\n\t\tif let w = notification.object as? NSWindow {\n\t\t\tlet win_info = find_win_info(w)\n\t\t\tvideo_update_xpos_ypos(win_info.kimage_ptr,\n\t\t\t\tInt32(w.frame.origin.x),\n\t\t\t\tInt32(w.frame.origin.y))\n\t\t}\n\t}\n\n\tfunc windowWillResize(_ window: NSWindow, to frameSize: NSSize)\n\t\t\t\t\t\t\t\t-> NSSize {\n\t\t// print(\"WILL RESIZE app \\(frameSize)\")\n\t\tlet width = Int(frameSize.width)\n\t\tlet height = Int(frameSize.height)\n\t\tlet win_info = find_win_info(window)\n\t\twin_info.update_window_size(width: width, height: height)\n\t\treturn frameSize\n\t}\n\tfunc windowShouldClose(_ window: NSWindow) -> Bool {\n\t\tprint(\"windowShouldClose\")\n\t\tlet win_info = find_win_info(window)\n\t\tif(mainwin_info.x_win == window) {\n\t\t\t// User has clicked the close box on the main emulator\n\t\t\t//  window.  Just exit the app\n\t\t\tNSApp.terminate(window)\n\t\t\treturn true\t\t// Let main window close\n\t\t} else {\n\t\t\tvideo_set_active(win_info.kimage_ptr, Int32(0))\n\t\t\twin_info.update()\n\t\t\treturn false\n\t\t}\n\t}\n\n\tfunc set_menu_for_kegs() {\n\t\tlet appname = \"Kegs\"\n\t\tlet menu = NSMenu(title: \"MainMenu\")\n\t\tNSApp.mainMenu = menu\n\n\t\tprint(\"Installing my menu now\")\n\n\t\t//   Add \"Kegs\" menu\n\t\tlet kegs = NSMenu(title: appname)\n\t\tkegs.addItem(withTitle: \"About \\(appname)\",\n\t\t\taction: #selector(AppDelegate.do_about(_:)),\n\t\t\tkeyEquivalent: \"\")\n\t\tkegs.addItem(NSMenuItem.separator())\n\t\tlet quit_item = NSMenuItem(title: \"Quit \\(appname)\",\n\t\t\taction: #selector(NSApplication.terminate(_:)),\n\t\t\tkeyEquivalent: \"q\")\n\t\tquit_item.keyEquivalentModifierMask = [.option,\n\t\t\t\t\t\t\t.command ]\n\t\tkegs.addItem(quit_item)\n\t\tlet kegs_item = NSMenuItem()\n\t\tkegs_item.title = appname\n\t\tkegs_item.submenu = kegs\n\t\tmenu.addItem(kegs_item)\n\n\t\t//   Add \"Edit\" menu\n\t\tlet edit = NSMenu(title: \"Edit\")\n\t\tedit.addItem(withTitle: \"Copy Text Screen\",\n\t\t\taction: #selector(MainView.do_copy_text(_:)),\n\t\t\tkeyEquivalent: \"\")\n\t\tedit.addItem(NSMenuItem.separator())\n\t\tedit.addItem(withTitle: \"Paste\",\n\t\t\taction: #selector(MainView.do_paste(_:)),\n\t\t\tkeyEquivalent: \"\")\n\t\tlet edit_item = NSMenuItem()\n\t\tedit_item.title = \"Edit\"\n\t\tedit_item.submenu = edit\n\t\tmenu.addItem(edit_item)\n\n\t\t//   Add \"Config\" menu\n\t\tlet config = NSMenu(title: \"Config\")\n\t\tconfig.addItem(withTitle: \"Configuration  F4\",\n\t\t\taction: #selector(MainView.do_config(_:)),\n\t\t\tkeyEquivalent: \"\")\n\t\tlet config_item = NSMenuItem()\n\t\tconfig_item.title = \"Config\"\n\t\tconfig_item.submenu = config\n\t\tmenu.addItem(config_item)\n\n\t\tshow_menu(menu, depth: 0)\n\t}\n\n\tfunc show_menu(_ menu: NSMenu, depth: Int) {\n\t\tif(depth >= -10) {\n\t\t\treturn\t// HACK: remove to see debug output!\n\t\t}\n\t\tprint(\"menu at depth \\(depth): \\(menu.title)\")\n\t\tif(depth > 5) {\t\t// Prevent infinite recursion\n\t\t\treturn\n\t\t}\n\t\tfor menuit in menu.items {\n\t\t\tprint(\"menuit: depth:\\(depth) \\(menuit.title)\")\n\t\t\tprint(\"  keyeq:\\(menuit.keyEquivalent)\")\n\t\t\tprint(\"  modifiers:\\(menuit.keyEquivalentModifierMask)\")\n\t\t\tprint(\"  isSeparator:\\(menuit.isSeparatorItem)\")\n\t\t\tif let sub = menuit.submenu {\n\t\t\t\tshow_menu(sub, depth: depth+1)\n\t\t\t}\n\t\t}\n\t}\n\n\tvar mainWindow : NSWindow!\n\tvar mainwin_view : MainView!\n\n\tfunc main_init() {\n\t\tvar kimage_ptr : UnsafeMutablePointer<Kimage>!\n\t\tvar rect = NSRect.zero\n\n\t\tlet argc = CommandLine.argc\n\t\tlet argv = CommandLine.unsafeArgv\n\t\tparse_argv(argc, argv, 3);\n\n\t\trect.size.width = 2560\n\t\trect.size.height = 1440\n\t\tif let screen = NSScreen.main {\n\t\t\trect = screen.frame\n\t\t}\n\t\tkegs_init(24, Int32(rect.size.width), Int32(rect.size.height),\n\t\t\t\t\t\tInt32(1))\n\t\tif(Context_draw) {\n\t\t\tvideo_set_blue_mask(UInt32(0x0000ff))\n\t\t\tvideo_set_green_mask(UInt32(0x00ff00))\n\t\t\tvideo_set_red_mask(UInt32(0xff0000))\n\t\t} else {\n\t\t\tvideo_set_red_mask(UInt32(0x0000ff))\n\t\t\tvideo_set_green_mask(UInt32(0x00ff00))\n\t\t\tvideo_set_blue_mask(UInt32(0xff0000))\n\t\t}\n\t\tvideo_set_palette()\n\t\tkimage_ptr = video_get_kimage(Int32(0))\n\t\tmainwin_info.set_kimage(kimage_ptr, title: \"GS+\",\n\t\t\t\t\t\t\tdelegate: self)\n\t\tkimage_ptr = video_get_kimage(Int32(1))\n\t\tdebugwin_info.set_kimage(kimage_ptr, title: \"Debugger\",\n\t\t\t\t\t\t\tdelegate: self)\n\n\t\tmainwin_info.create_window()\n\t\tNSApp.activate(ignoringOtherApps: true)\n\t\tmain_run_loop()\n\t}\n\n\tfunc main_run_loop() {\n\t\tDispatchQueue.main.asyncAfter(deadline: .now() +\n\t\t\t\t\t\t\t.milliseconds(1)) {\n\t\t\tself.main_run_loop()\n\t\t}\n\t\tlet ret = run_16ms()\n\t\tif(ret != 0) {\n\t\t\texit(ret);\n\t\t}\n\t\tmainwin_info.update()\n\t\tdebugwin_info.update()\n\t}\n}\n\n"
  },
  {
    "path": "gsplus/src/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>BuildMachineOSBuild</key>\n\t<string>18G103</string>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleExecutable</key>\n\t<string>KEGSMAC</string>\n\t<key>CFBundleIconFile</key>\n\t<string>kegs.icns</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>com.provalid.Kegs</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>Kegs</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.38</string>\n\t<key>CFBundleSupportedPlatforms</key>\n\t<array>\n\t\t<string>MacOSX</string>\n\t</array>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>DTCompiler</key>\n\t<string>com.apple.compilers.llvm.clang.1_0</string>\n\t<key>DTPlatformBuild</key>\n\t<string>11A1027</string>\n\t<key>DTPlatformVersion</key>\n\t<string>GM</string>\n\t<key>DTSDKBuild</key>\n\t<string>19A547</string>\n\t<key>DTSDKName</key>\n\t<string>macosx10.15</string>\n\t<key>DTXcode</key>\n\t<string>1110</string>\n\t<key>DTXcodeBuild</key>\n\t<string>11A1027</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>10.13</string>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>Copyright © 2025 Kent Dickey. All rights reserved.</string>\n\t<key>NSHighResolutionCapable</key>\n\t<true/>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "gsplus/src/Kegs-Bridging-Header.h",
    "content": "// $KmKId: Kegs-Bridging-Header.h,v 1.1 2019-10-14 22:33:09+00 kentd Exp $\n//  Use this file to import your target's public headers that you would like to expose to Swift.\n//\n\n#import \"defc.h\"\n"
  },
  {
    "path": "gsplus/src/MainView.swift",
    "content": "// $KmKId: MainView.swift,v 1.42 2024-09-15 13:55:35+00 kentd Exp $\n\n//\tCopyright 2019-2024 by Kent Dickey\n//\tThis code is covered by the GNU GPL v3\n//\tSee the file COPYING.txt or https://www.gnu.org/licenses/\n//\n\nimport Cocoa\nimport CoreGraphics\nimport AudioToolbox\n\nclass MainView: NSView {\n\n\tvar bitmapContext : CGContext!\n\tvar bitmapData : UnsafeMutableRawPointer!\n\tvar rawData : UnsafeMutablePointer<UInt32>!\n\tvar current_flags : UInt = 0\n\tvar mouse_moved : Bool = false\n\tvar mac_warp_pointer : Int32 = 0\n\tvar mac_hide_pointer : Int32 = 0\n\tvar kimage_ptr : UnsafeMutablePointer<Kimage>!\n\tvar closed : Bool = false\n\tvar pixels_per_line = 640\n\tvar max_height = 600\n\n\tlet is_cmd = UInt(NSEvent.ModifierFlags.command.rawValue)\n\tlet is_control = UInt(NSEvent.ModifierFlags.control.rawValue)\n\tlet is_shift = NSEvent.ModifierFlags.shift.rawValue\n\tlet is_capslock = NSEvent.ModifierFlags.capsLock.rawValue\n\tlet is_option = NSEvent.ModifierFlags.option.rawValue\n\n\n\toverride init(frame frameRect: NSRect) {\n\t\tsuper.init(frame: frameRect)\n\t}\n\n\trequired init?(coder: NSCoder) {\n\t\tsuper.init(coder: coder)\n\t}\n\n\tfunc windowDidResize(_ notification: Notification) {\n\t\tprint(\"DID RESIZE\")\n\t}\n\toverride func performKeyEquivalent(with event: NSEvent) -> Bool {\n\t\tlet keycode = event.keyCode\n\t\tlet is_repeat = event.isARepeat\n\t\tlet unicode_key = get_unicode_key_from_event(event)\n\t\t// print(\".performKeyEquiv keycode: \\(keycode), is_repeat: \" +\n\t\t//\t\t\t\t\t\"\\(is_repeat)\")\n\t\tif(((current_flags & is_cmd) == 0) || is_repeat) {\n\t\t\t// If CMD isn't being held down, just ignore this\n\t\t\treturn false\n\t\t}\n\t\t// Otherwise, manually do down, then up, for this key\n\t\tadb_physical_key_update(kimage_ptr, Int32(keycode),\n\t\t\tUInt32(unicode_key), 0);\n\t\tadb_physical_key_update(kimage_ptr, Int32(keycode),\n\t\t\tUInt32(unicode_key), 1);\n\t\treturn true\n\t}\n\n\toverride var acceptsFirstResponder: Bool {\n\t\treturn true\n\t}\n\n\tfunc get_unicode_key_from_event(_ event: NSEvent) -> UInt32 {\n\t\tvar unicode_key = UInt32(0);\n\t\tif let str = event.charactersIgnoringModifiers {\n\t\t\t//print(\" keydown unmod str: \\(str), \" +\n\t\t\t//\t\t\"code:\\(event.keyCode)\")\n\t\t\tlet arr_chars = Array(str.unicodeScalars)\n\t\t\t//print(\" arr_chars: \\(arr_chars)\")\n\t\t\tif(arr_chars.count == 1) {\n\t\t\t\tunicode_key = UInt32(arr_chars[0]);\n\t\t\t\t//print(\"key1:\\(unicode_key)\")\n\t\t\t}\n\t\t}\n\t\treturn unicode_key\n\t}\n\toverride func keyDown(with event: NSEvent) {\n\t\tlet keycode = event.keyCode\n\t\tlet is_repeat = event.isARepeat;\n\t\t// print(\".keyDown code: \\(keycode), repeat: \\(is_repeat)\")\n\t\t//if let str = event.characters {\n\t\t//\tprint(\" keydown str: \\(str), code:\\(keycode)\")\n\t\t//}\n\t\tlet unicode_key = get_unicode_key_from_event(event)\n\t\tif(is_repeat) {\n\t\t\t// If we do CMD-E, then we never get a down for the E,\n\t\t\t//  but we will get repeat events for that E.  Let's\n\t\t\t//  ignore those\n\t\t\treturn\n\t\t}\n\t\tadb_physical_key_update(kimage_ptr, Int32(keycode),\n\t\t\tUInt32(unicode_key), 0);\n\t}\n\n\toverride func keyUp(with event: NSEvent) {\n\t\tlet keycode = event.keyCode\n\t\t// let is_repeat = event.isARepeat;\n\t\t// print(\".keyUp code: \\(keycode), repeat: \\(is_repeat)\")\n\t\tlet unicode_key = get_unicode_key_from_event(event)\n\t\tadb_physical_key_update(kimage_ptr, Int32(keycode),\n\t\t\tUInt32(unicode_key), 1);\n\t}\n\n\toverride func flagsChanged(with event: NSEvent) {\n\t\tlet flags = event.modifierFlags.rawValue &\n\t\t\t\t(is_cmd | is_control | is_shift | is_capslock |\n\t\t\t\t\t\t\t\tis_option)\n\t\tvar c025_val = UInt32(0);\n\t\tif((flags & is_shift) != 0) {\n\t\t\tc025_val |= 1;\n\t\t}\n\t\tif((flags & is_control) != 0) {\n\t\t\tc025_val |= 2;\n\t\t}\n\t\tif((flags & is_capslock) != 0) {\n\t\t\tc025_val |= 4;\n\t\t}\n\t\tif((flags & is_option) != 0) {\n\t\t\tc025_val |= 0x40;\n\t\t}\n\t\tif((flags & is_cmd) != 0) {\n\t\t\tc025_val |= 0x80;\n\t\t}\n\t\tadb_update_c025_mask(kimage_ptr, c025_val, UInt32(0xc7));\n\t\tcurrent_flags = flags\n\t\t//print(\"flagsChanged: \\(flags) and keycode: \\(event.keyCode)\")\n\t}\n\toverride func acceptsFirstMouse(for event: NSEvent?) -> Bool {\n\t\t// This is to let the first click when changing to this window\n\t\t//  through to the app, I probably don't want this.\n\t\treturn false\n\t}\n\toverride func mouseMoved(with event: NSEvent) {\n\t\t//let type = event.eventNumber\n\t\t//print(\" event type: \\(type)\")\n\t\tmac_update_mouse(event: event, buttons_state:0, buttons_valid:0)\n\n\t}\n\toverride func mouseDragged(with event: NSEvent) {\n\t\tmac_update_mouse(event: event, buttons_state: 0,\n\t\t\t\t\t\t\tbuttons_valid: 0)\n\t}\n\toverride func otherMouseDown(with event: NSEvent) {\n\t\tmac_update_mouse(event: event, buttons_state:2, buttons_valid:2)\n\t}\n\toverride func otherMouseUp(with event: NSEvent) {\n\t\tmac_update_mouse(event: event, buttons_state:0, buttons_valid:2)\n\t}\n\n\toverride func mouseEntered(with event: NSEvent) {\n\t\tprint(\"mouse entered\")\n\t}\n\toverride func rightMouseUp(with event: NSEvent) {\n\t\tmac_update_mouse(event: event, buttons_state:0, buttons_valid:4)\n\t}\n\toverride func rightMouseDown(with event: NSEvent) {\n\t\tprint(\"Right mouse down\")\n\t\tmac_update_mouse(event: event, buttons_state:4, buttons_valid:4)\n\t}\n\toverride func mouseUp(with event: NSEvent) {\n\t\t// print(\"Mouse up \\(event.locationInWindow.x),\" +\n\t\t//\t\t\t\t\"\\(event.locationInWindow.y)\")\n\t\tmac_update_mouse(event: event, buttons_state:0, buttons_valid:1)\n\t}\n\toverride func mouseDown(with event: NSEvent) {\n\t\t//print(\"Mouse down \\(event.locationInWindow.x),\" +\n\t\t//\t\t\t\t\"\\(event.locationInWindow.y)\")\n\t\tmac_update_mouse(event: event, buttons_state:1, buttons_valid:1)\n\t}\n\n\tfunc mac_update_mouse(event: NSEvent, buttons_state: Int,\n\t\t\t\t\t\t\tbuttons_valid: Int) {\n\t\tvar warp = Int32(0)\n\t\tvar x_width = 0\n\t\tvar y_height = 0\n\t\tvar x = Int32(0)\n\t\tvar y = Int32(0)\n\t\tvar do_delta = Int(0)\n\t\tlet hide = adb_get_hide_warp_info(kimage_ptr, &warp)\n\t\tif(warp != mac_warp_pointer) {\n\t\t\tmouse_moved = true\n\t\t}\n\t\tmac_warp_pointer = warp\n\t\tif(mac_hide_pointer != hide) {\n\t\t\tmac_hide_pointer(hide: hide)\n\t\t}\n\t\tmac_hide_pointer = hide\n\t\tlet location = event.locationInWindow\n\t\tif(!Context_draw) {\n\t\t\t// We're letting the Mac scale the window for us,\n\t\t\t//  so video_scale_mouse*() doesn't know the scale\n\t\t\t//  factor, so pass it in\n\t\t\tx_width = Int(bounds.size.width);\n\t\t\ty_height = Int(bounds.size.height);\n\t\t}\n\t\tlet raw_x = location.x\n\t\tlet raw_y = bounds.size.height - 1 - location.y\n\t\t\t// raw_y is 0 at the top of the window now\n\t\tx = video_scale_mouse_x(kimage_ptr, Int32(raw_x),\n\t\t\t\t\t\tInt32(x_width))\n\t\ty = video_scale_mouse_y(kimage_ptr, Int32(raw_y),\n\t\t\t\t\t\tInt32(y_height))\n\t\tdo_delta = 0;\n\t\tif(mac_warp_pointer != 0) {\n\t\t\tdo_delta |= 0x1000;\t// x,y are deltas\n\t\t\tx = Int32(event.deltaX)\n\t\t\ty = Int32(event.deltaY)\n\t\t}\n\t\tlet ret = adb_update_mouse(kimage_ptr, x, y,\n\t\t\t\tInt32(buttons_state),\n\t\t\t\tInt32(buttons_valid | do_delta))\n\t\tif(ret != 0) {\n\t\t\tmouse_moved = true\n\t\t}\n\t\tguard let win = window else {\n\t\t\treturn\t\t\t// No valid window\n\t\t}\n\t\tif(mouse_moved) {\n\t\t\tvar rect1 = NSRect.zero\n\n\t\t\t// If warp, warp cursor to middle of window.  Moving\n\t\t\t//  the cursor requires absolute screen coordinates,\n\t\t\t//  where y=0 is the top of the screen.  We must convert\n\t\t\t//  window coords (where y=0 is the bottom of the win).\n\t\t\tmouse_moved = false\n\t\t\tlet warp_x = CGFloat(video_unscale_mouse_x(kimage_ptr,\n\t\t\t\tInt32(A2_WINDOW_WIDTH/2), Int32(x_width)))\n\t\t\tlet warp_y = CGFloat(video_unscale_mouse_y(kimage_ptr,\n\t\t\t\tInt32(A2_WINDOW_HEIGHT/2), Int32(y_height)))\n\t\t\tlet scr_height = CGDisplayPixelsHigh(CGMainDisplayID());\n\t\t\trect1.origin.x = CGFloat(warp_x)\n\t\t\trect1.origin.y = bounds.size.height - 1 -\n\t\t\t\t\t\t\t\tCGFloat(warp_y)\n\t\t\trect1.size.width = 1;\n\t\t\trect1.size.height = 0;\n\t\t\tlet screen_rect = win.convertToScreen(rect1)\n\t\t\tlet screen_rect_y = CGFloat(scr_height) -\n\t\t\t\t\t\t\tscreen_rect.origin.y\n\t\t\tlet cg_loc = CGPoint(x: screen_rect.origin.x,\n\t\t\t\ty: CGFloat(screen_rect_y))\n\t\t\t//print(\"scr_rect:\\(screen_rect)\")\n\t\t\tif(warp != 0) {\n\t\t\t\t// Warp to middle of the window\n\t\t\t\tCGDisplayMoveCursorToPoint(CGMainDisplayID(),\n\t\t\t\t\t\t\t\tcg_loc);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunc mac_hide_pointer(hide: Int32) {\n\t\t// print(\"Hide called: \\(hide)\")\n\t\tif(hide != 0) {\n\t\t\tCGDisplayHideCursor(CGMainDisplayID())\n\t\t} else {\n\t\t\tCGDisplayShowCursor(CGMainDisplayID())\n\t\t}\n\t}\n\tfunc initialize() {\n\t\t//let colorSpace = CGColorSpace(name: CGColorSpace.sRGB)\n\t\tprint(\"Initialize view called\")\n\t\t// Get width,height from video.c to handle toggling status lines\n\t\tlet width = Int(video_get_a2_width(kimage_ptr))\n\t\tlet height = Int(video_get_a2_height(kimage_ptr))\n\t\t//if let screen = NSScreen.main {\n\t\t//\tlet rect = screen.frame\n\t\t//\twidth = Int(rect.size.width)\n\t\t//\theight = Int(rect.size.height)\n\t\t//}\n\t\tpixels_per_line = width\n\t\tmax_height = height\n\t\t//print(\"pixels_per_line: \\(pixels_per_line), \" +\n\t\t//\t\t\"max_height: \\(max_height)\")\n\n\t\tlet color_space = CGDisplayCopyColorSpace(CGMainDisplayID())\n\t\t//let colorSpace = CGColorSpaceCreateDeviceRGB()\n\t\tbitmapContext = CGContext(data: nil, width: width,\n\t\t\theight: height, bitsPerComponent: 8,\n\t\t\tbytesPerRow: width * 4,\n\t\t\tspace: color_space,\n\t\t\tbitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue)\n\n\t\t//CGImageAlphaInfo.noneSkipLast.rawValue\n\t\tbitmapData = bitmapContext.data!\n\t\t// Set the intial value of the data to black (0)\n\t\tbitmapData.initializeMemory(as: UInt32.self, repeating: 0,\n\t\t\t\t\t\tcount: height * width)\n\t\trawData = bitmapData.bindMemory(to: UInt32.self,\n\t\t\t\t\t\tcapacity: height * width)\n\t\t//print(\"Calling video_update_scale from MainViewinitialize()\")\n\t\tlet x_width = Int(video_get_x_width(kimage_ptr))\n\t\tlet x_height = Int(video_get_x_height(kimage_ptr))\n\t\tvideo_update_scale(kimage_ptr, Int32(x_width), Int32(x_height),\n\t\t\t\t\t\t\t\tInt32(1))\n\t\tif(Context_draw) {\n\t\t\tvideo_set_alpha_mask(UInt32(0xff000000))\n\t\t\t// Set video.c alpha mask, since 0 means transparent\n\t\t}\n\t}\n\n\toverride func draw(_ dirtyRect: NSRect) {\n\t\tvar rect : Change_rect\n\t\t//super.draw(dirtyRect)\n\t\t\t// Documentation says super.draw not needed...\n\t\t// Draw the current image buffer to the screen\n\t\tlet context = NSGraphicsContext.current!.cgContext\n\t\tif(!Context_draw) {\n\t\t\t// Safe, simple drawing\n\t\t\tlet image = bitmapContext.makeImage()!\n\t\t\tcontext.draw(image, in: bounds)\n\t\t\t\t// The above call does the actual copy of\n\t\t\t\t//  data to the screen, and can take a while\n\t\t\t//print(\"Draw, bounds:\\(bounds), dirtyr:\\(dirtyRect)\")\n\t\t} else {\n\t\t\t// Unsafe, more direct drawing by peeking into\n\t\t\t// NSGraphicsContext.current.data\n\t\t\tif let data = context.data {\n\t\t\t\trect = Change_rect(x:0, y:0,\n\t\t\t\t\twidth:Int32(context.width),\n\t\t\t\t\theight:Int32(context.height));\n\t\t\t\tvideo_update_scale(kimage_ptr,\n\t\t\t\t\tInt32(context.width),\n\t\t\t\t\tInt32(context.height), Int32(0))\n\t\t\t\tvideo_out_data_scaled(data, kimage_ptr,\n\t\t\t\t\tInt32(context.bytesPerRow/4), &rect);\n\t\t\t\tvideo_out_done(kimage_ptr);\n\t\t\t\tprint(\"Did out_data_scaled, rect:\\(rect)\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@objc func do_config(_ : AnyObject) {\n\t\t// Create a \"virtual\" F4 press\n\t\t//print(\"do_config\")\n\t\t// Create a keydown for the F4 key (keycode:0x76)\n\t\tadb_physical_key_update(kimage_ptr, Int32(0x76), 0, 0);\n\n\t\t// and create a keyup for the F4 key (keycode:0x76)\n\t\tadb_physical_key_update(kimage_ptr, Int32(0x76), 0, 1);\n\t}\n\n\t@objc func do_copy_text(_ : AnyObject) {\n\t\t// print(\"do_copy\");\n\t\t//let text_buf_cstr = UnsafeMutablePointer<Int8>.allocate(\n\t\t//\t\t\t\tcapacity: 2100);\n\t\tif let cstr = cfg_text_screen_str() {\n\t\t\tlet str = String(cString: cstr);\n\t\t\tNSPasteboard.general.clearContents();\n\t\t\tNSPasteboard.general.setString(str,\n\t\t\t\tforType: NSPasteboard.PasteboardType.string);\n\t\t}\n\t}\n\t@objc func do_paste(_ : AnyObject) {\n\t\t// print(\"do_paste\")\n\t\tlet general = NSPasteboard.general;\n\t\tguard let str = general.string(forType: .string) else {\n\t\t\tprint(\"Cannot paste, nothing in clipboard\");\n\t\t\treturn\n\t\t}\n\t\t//print(\"str: \\(str)\")\n\t\tfor raw_c in str.utf8 {\n\t\t\tlet c = UInt32(raw_c)\n\t\t\tlet ret = adb_paste_add_buf(c)\n\t\t\tif(ret != 0) {\n\t\t\t\tprint(\"Paste too large!\")\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tfunc mac_update_display() {\n\t\tvar valid : Int32\n\t\tvar rect : Change_rect\n\t\tvar dirty_rect = NSRect.zero\n\n\t\tif(Context_draw) {\n\t\t\t// We just need to know if there are any changes,\n\t\t\t//  don't actually do the copies now\n\t\t\tvalid = video_out_query(kimage_ptr);\n\t\t\tif(valid != 0) {\n\t\t\t\tself.setNeedsDisplay(bounds)\n\t\t\t\tprint(\"Needs display\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Otherwise, update rawData in our Bitmap now\n\t\trect = Change_rect(x:0, y:0, width:0, height:0)\n\t\tfor i in 0..<MAX_CHANGE_RECTS {\t\t// MAX_CHANGE_RECTS\n\t\t\tvalid = video_out_data(rawData, kimage_ptr,\n\t\t\t\tInt32(pixels_per_line), &rect, Int32(i))\n\t\t\tif(valid == 0) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdirty_rect.origin.x = CGFloat(rect.x)\n\t\t\tdirty_rect.origin.y = bounds.size.height -\n\t\t\t\t\tCGFloat(rect.y) - CGFloat(rect.height)\n\t\t\tdirty_rect.size.width = CGFloat(rect.width)\n\t\t\tdirty_rect.size.height = CGFloat(rect.height)\n\n\t\t\tself.setNeedsDisplay(bounds)\n\t\t\t\t// It's necessary to redraw the whole screen,\n\t\t\t\t//  there's no mechanism to just redraw part\n\t\t\t\t// The coordinates would need transformation\n\t\t\t\t//  (since Mac 0,0 is the lower left corner)\n\t\t\t//print(\"bounds: w:\\(bounds.size.width) \" +\n\t\t\t//\t\t\t\"h:\\(bounds.size.height)\\n\")\n\t\t\t//self.setNeedsDisplay(dirty_rect)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "gsplus/src/Makefile",
    "content": "# $KmKId: makefile,v 1.48 2025-04-28 15:12:19+00 kentd Exp $\n\nXOPTS_WIN = -Wall -fomit-frame-pointer -march=pentium\n\nSWIFTOBJS = AppDelegate.o MainView.o\n\ninclude vars\ninclude ldvars\n\n.SUFFIXES: .dep .proto\n\nAS = $(CC)\n\nXLIBS = -L/usr/X11/lib\nPERL = perl\n\nall: $(TARGET)\n\nspecials:\n\nspecials_clean:\n\nclean:\n\trm -f *.o gsplus\n\n\n# Mac builds:\ngsplus: $(OBJECTS) $(OBJECTS1) compile_time.o $(SWIFTOBJS)\n\tclang $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) $(SWIFTOBJS) \\\n\t\tcompile_time.o $(LDFLAGS) -o gsplus $(EXTRA_LIBS) \\\n\t\t-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \\\n\t\t-Xlinker -rpath -Xlinker @executable_path/../Frameworks \\\n\t\t-Xlinker -rpath -Xlinker /usr/lib/swift \\\n\t\t-Xlinker -no_deduplicate -fobjc-link-runtime \\\n\t\t-L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx \\\n\t\t-L/usr/lib/swift\n\tmkdir -p ../GSplus.app/Contents/MacOS\n\tmkdir -p ../GSplus.app/Contents/Frameworks\n\t$(PERL) cp_gsplus_libs gsplus /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx ../GSplus.app/Contents/Frameworks\n\tmv gsplus ../GSplus.app/Contents/MacOS/GSplus\n\techo \"APPL????\" > ../GSplus.app/Contents/PkgInfo\n\tcp -f Info.plist ../GSplus.app/Contents/\n\t$(PROJROOT)/lib/make_mac_icon $(PROJROOT)/lib/kegsicon.png\n\tcp -f kegs.icns ../GSplus.app/Contents/Resources/\n\ttouch '../GSplus.app/Icon?'\n\t#cp -f $(PROJROOT)/lib/2mg.icns ../GSplus.app/Contents/Resources/\n\t#cp -f $(PROJROOT)/lib/525.icns ../GSplus.app/Contents/Resources/\n\n\n# Linux for X builds:\ngsplus-x: $(OBJECTS) $(OBJECTS1) compile_time.o\n\t$(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) compile_time.o \\\n\t\t$(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) \\\n\t\t-lX11 -lXext\n\tmv gsplus ..\n\n# Cygwin for X builds:\ngsplus.exe: $(OBJECTS) $(OBJECTS1) compile_time.o\n\t$(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) compile_time.o \\\n\t\t$(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) \\\n\t\t-lXext -lX11 -lm\n\tmv gsplus.exe ..\n\n# Mingw32 (native windows) builds: (broken, doesn't work currently)\ngspluswin.exe: $(OBJECTS) $(OBJECTS1) compile_time.o\n\t$(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) compile_time.o \\\n\t\t$(LDFLAGS) -o $(NAME)$(SUFFIX) $(EXTRA_LIBS) \\\n\t\t-lwinmm -lgdi32 -ldsound -lcomctl32 -lws2_32\n\tmv $(NAME)$(SUFFIX) ..\n\n\n.s.o:\n\t$(AS) -c $(OPTS) -I. $*.s\n\n.c.o:\n\t$(CC) $(CCOPTS) $(XOPTS) -c $(OPTS) -I. $*.c\n\n.m.o:\n\t$(CC) $(CCOPTS) $(XOPTS) -c $(OPTS) -I. $*.m\n\nAppDelegate.o: AppDelegate.swift\n\tsh ./comp_swift -c $(OPTS) -I. -primary-file $*.swift \\\n\t\tMainView.swift -o $*.o\n\nMainView.o: MainView.swift\n\tsh ./comp_swift -c $(OPTS) -I. -primary-file $*.swift \\\n\t\tAppDelegate.swift -o $*.o\n\nwin32.o: win32.rc\n\twindres -o win32.o win32.rc\n\n.c.proto:\n\t$(KMKROOT)/bin/kmkproto $(XOPTS) $*.c > $*.proto\n\techo >> $*.proto\n\n.m.proto:\n\t$(KMKROOT)/bin/kmkproto $(XOPTS) $*.m > $*.proto\n\techo >> $*.proto\n\n$(PROTO_OUT): $(PROTO_FILE_LIST) ldvars proto_vars\n\t$(KMKROOT)/bin/kmkproto_head $(PROTO_OUT) tmp_protos.h\n\tcat /dev/null $(PROTO_FILE_LIST) >> tmp_protos.h\n\trm -f $(PROTO_OUT)\n\tmv tmp_protos.h $(PROTO_OUT)\n\tkmk_cp_if_diff $(PROTO_OUT) ..\n\trm -f *.proto\n\n\ncompile_time.o: $(OBJECTS) $(OBJECTS1)\n\ninclude dependency\n\n"
  },
  {
    "path": "gsplus/src/adb.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2024 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n/* adb_mode bit 3 and bit 2 (faster repeats for arrows and space/del) not done*/\n\n#include \"defc.h\"\n\nextern int Verbose;\nextern word32 g_vbl_count;\nextern int g_num_lines_prev_superhires640;\nextern int g_num_lines_prev_superhires;\nextern int g_rom_version;\nextern int g_fast_disk_emul_en;\nextern int g_limit_speed;\nextern int g_irq_pending;\nextern int g_swap_paddles;\nextern int g_invert_paddles;\nextern int g_joystick_type;\nextern int g_config_control_panel;\nextern int g_status_enable;\nextern dword64 g_cur_dfcyc;\n\nextern byte *g_slow_memory_ptr;\nextern byte *g_memory_ptr;\nextern word32 g_mem_size_total;\nextern Kimage g_mainwin_kimage;\nextern Kimage g_debugwin_kimage;\n\nenum {\n\tADB_IDLE = 0,\n\tADB_IN_CMD,\n\tADB_SENDING_DATA,\n};\n\n#define ADB_C027_MOUSE_DATA\t0x80\n#define ADB_C027_MOUSE_INT\t0x40\n#define ADB_C027_DATA_VALID\t0x20\n#define ADB_C027_DATA_INT\t0x10\n#define ADB_C027_KBD_VALID\t0x08\n#define ADB_C027_KBD_INT\t0x04\n#define ADB_C027_MOUSE_COORD\t0x02\n#define ADB_C027_CMD_FULL\t0x01\n\n#define ADB_C027_NEG_MASK\t( ~ (\t\t\t\t\\\n\t\tADB_C027_MOUSE_DATA | ADB_C027_DATA_VALID |\t\\\n\t\tADB_C027_KBD_VALID | ADB_C027_MOUSE_COORD |\t\\\n\t\tADB_C027_CMD_FULL))\n\n\nint halt_on_all_c027 = 0;\n\nword32\tg_adb_repeat_delay = 45;\nword32\tg_adb_repeat_rate = 3;\nword32\tg_adb_repeat_info = 0x23;\nword32\tg_adb_char_set = 0x0;\nword32\tg_adb_layout_lang = 0x0;\n\nword32\tg_adb_interrupt_byte = 0;\nint\tg_adb_state = ADB_IDLE;\n\nword32\tg_adb_cmd = (word32)-1;\nint\tg_adb_cmd_len = 0;\nint\tg_adb_cmd_so_far = 0;\nword32\tg_adb_cmd_data[16];\n\n#define MAX_ADB_DATA_PEND\t16\n\nword32\tg_adb_data[MAX_ADB_DATA_PEND];\nint\tg_adb_data_pending = 0;\n\nword32\tg_c027_val = 0;\nword32\tg_c025_val = 0;\n\nbyte\tadb_memory[256];\n\nword32 g_adb_mode = 0;\t\t/* mode set via set_modes, clear_modes */\n\nint g_warp_pointer = 0;\nint g_hide_pointer = 0;\nint g_unhide_pointer = 0;\nint\tg_adb_copy_requested = 0;\n\nint g_mouse_a2_x = 0;\nint g_mouse_a2_y = 0;\nint g_mouse_a2_button = 0;\nint g_mouse_fifo_pos = 0;\nint g_mouse_raw_x = 0;\nint g_mouse_raw_y = 0;\n\n#define ADB_MOUSE_FIFO\t\t8\n\nSTRUCT(Mouse_fifo) {\n\tdword64\tdfcyc;\n\tint\tx;\n\tint\ty;\n\tint\tbuttons;\n};\n\nMouse_fifo g_mouse_fifo[ADB_MOUSE_FIFO] = { { 0, 0, 0, 0 } };\n\nint\tg_adb_mouse_valid_data = 0;\nint\tg_adb_mouse_coord = 0;\n\n#define MAX_KBD_BUF\t\t8\n#define MAX_KBD_PASTE_BUF\t32768\n\nint\tg_adb_mainwin_has_focus = 1;\n#if defined(__linux__) || defined(_WIN32)\nint\tg_adb_swap_command_option = 1;\t\t// Default to swap on Linux/Win\n#else\nint\tg_adb_swap_command_option = 0;\n#endif\nint\tg_key_down = 0;\nint\tg_hard_key_down = 0;\nint\tg_a2code_down = 0;\nint\tg_kbd_read_no_update = 0;\nint\tg_kbd_chars_buffered = 0;\nint\tg_kbd_buf[MAX_KBD_BUF];\nint\tg_kbd_paste_rd_pos = 0;\nint\tg_kbd_paste_wr_pos = 0;\nbyte\tg_kbd_paste_buf[MAX_KBD_PASTE_BUF];\nword32\tg_kbd_paste_last_key = 0;\nword32\tg_adb_repeat_vbl = 0;\n\nint\tg_kbd_dev_addr = 2;\t\t/* ADB physical kbd addr */\nint\tg_mouse_dev_addr = 3;\t\t/* ADB physical mouse addr */\n\nint\tg_kbd_ctl_addr = 2;\t\t/* ADB microcontroller's kbd addr */\nint\tg_mouse_ctl_addr = 3;\t\t/* ADB ucontroller's mouse addr*/\n\t\t\t/* above are ucontroller's VIEW of where mouse/kbd */\n\t\t\t/*  are...if they are moved, mouse/keyboard funcs */\n\t\t\t/*  should stop (c025, c000, c024, etc). */\n\nword32\tg_virtual_key_up[4];\t/* bitmask of all possible 128 a2codes */\n\t\t\t\t/* indicates which keys are up=1 by bit */\nint\tg_rawa2_to_a2code[128];\n\nint\tg_keypad_key_is_down[10] = { 0 };/* List from 0-9 of which keypad */\n\t\t\t\t\t/*  keys are currently pressed */\n\n\n#define SHIFT_DOWN\t( (g_c025_val & 0x01) )\n#define CTRL_DOWN\t( (g_c025_val & 0x02) )\n#define CAPS_LOCK_DOWN\t( (g_c025_val & 0x04) )\n#define OPTION_DOWN\t( (g_c025_val & 0x40) )\n#define CMD_DOWN\t( (g_c025_val & 0x80) )\n\n\n#define MAX_ADB_KBD_REG3\t16\n\nint g_kbd_reg0_pos = 0;\nint g_kbd_reg0_data[MAX_ADB_KBD_REG3];\nint g_kbd_reg3_16bit = 0x602;\t\t\t/* also set in adb_reset()! */\n\nint\tg_adb_init = 0;\n\n/* Format: a2code, ascii if no shift, ascii if shift, ascii if ctl */\nconst int g_a2_key_to_ascii[][4] = {\n\t{ 0x00,\t'a',\t'A',\t0x01 },\n\t{ 0x01,\t's',\t'S',\t0x13 },\n\t{ 0x02,\t'd',\t'D',\t0x04 },\n\t{ 0x03,\t'f',\t'F',\t0x06 },\n\t{ 0x04,\t'h',\t'H',\t0x08 },\n\t{ 0x05,\t'g',\t'G',\t0x07 },\n\t{ 0x06,\t'z',\t'Z',\t0x1a },\n\t{ 0x07,\t'x',\t'X',\t0x18 },\n\n\t{ 0x08,\t'c',\t'C',\t0x03 },\n\t{ 0x09,\t'v',\t'V',\t0x16 },\n\t{ 0x0a, -1, -1, -1 },\n\t{ 0x0b,\t'b',\t'B',\t0x02 },\n\t{ 0x0c,\t'q',\t'Q',\t0x11 },\n\t{ 0x0d,\t'w',\t'W',\t0x17 },\n\t{ 0x0e,\t'e',\t'E',\t0x05 },\n\t{ 0x0f,\t'r',\t'R',\t0x12 },\n\n\t{ 0x10,\t'y',\t'Y',\t0x19 },\n\t{ 0x11,\t't',\t'T',\t0x14 },\n\t{ 0x12,\t'1',\t'!',\t-1 },\n\t{ 0x13,\t'2',\t'@',\t0x00 },\n\t{ 0x14,\t'3',\t'#',\t-1 },\n\t{ 0x15,\t'4',\t'$',\t-1 },\n\t{ 0x16,\t'6',\t'^',\t0x1e },\n\t{ 0x17,\t'5',\t'%',\t-1 },\n\n\t{ 0x18,\t'=',\t'+',\t-1 },\n\t{ 0x19,\t'9',\t'(',\t-1 },\n\t{ 0x1a,\t'7',\t'&',\t-1 },\n\t{ 0x1b,\t'-',\t'_',\t0x1f },\n\t{ 0x1c,\t'8',\t'*',\t-1 },\n\t{ 0x1d,\t'0',\t')',\t-1 },\n\t{ 0x1e,\t']',\t'}',\t0x1d },\n\t{ 0x1f,\t'o',\t'O',\t0x0f },\n\n\t{ 0x20,\t'u',\t'U',\t0x15 },\n\t{ 0x21,\t'[',\t'{',\t0x1b },\n\t{ 0x22,\t'i',\t'I',\t0x09 },\n\t{ 0x23,\t'p',\t'P',\t0x10 },\n\t{ 0x24,\t0x0d,\t0x0d,\t-1 },\t/* return */\n\t{ 0x25,\t'l',\t'L',\t0x0c },\n\t{ 0x26,\t'j',\t'J',\t0x0a },\n\t{ 0x27,\t0x27,\t'\"',\t-1 },\t/* single quote */\n\n\t{ 0x28,\t'k',\t'K',\t0x0b },\n\t{ 0x29,\t';',\t':',\t-1 },\n\t{ 0x2a,\t0x5c,\t'|',\t0x1c },\t/* \\, | */\n\t{ 0x2b,\t',',\t'<',\t-1 },\n\t{ 0x2c,\t'/',\t'?',\t0x7f },\n\t{ 0x2d,\t'n',\t'N',\t0x0e },\n\t{ 0x2e,\t'm',\t'M',\t0x0d },\n\t{ 0x2f,\t'.',\t'>',\t-1 },\n\n\t{ 0x30,\t0x09,\t0x09,\t-1 },\t/* tab */\n\t{ 0x31,\t' ',\t' ',\t-1 },\n\t{ 0x32,\t'`',\t'~',\t-1 },\n\t{ 0x33,\t0x7f,\t0x7f,\t-1 },\t/* Delete */\n\t{ 0x34, -1, -1, -1 },\n\t{ 0x35,\t0x1b,\t0x1b,\t-1 },\t/* Esc */\n\t{ 0x36,\t0x0200,\t0x0200,\t-1 },\t/* control */\n\t{ 0x37,\t0x8000,\t0x8000,\t-1 },\t/* Command */\n\n\t{ 0x38,\t0x0100,\t0x0100, -1 },\t/* shift */\n\t{ 0x39,\t0x0400,\t0x0400,\t-1 },\t/* caps lock */\n\t{ 0x3a,\t0x4000,\t0x4000,\t-1 },\t/* Option */\n\t{ 0x3b,\t0x08,\t0x08,\t-1 },\t/* left */\n\t{ 0x3c,\t0x15,\t0x15,\t-1 },\t/* right */\n\t{ 0x3d,\t0x0a,\t0x0a,\t-1 },\t/* down */\n\t{ 0x3e,\t0x0b,\t0x0b,\t-1 },\t/* up arrow */\n\t{ 0x3f, -1, -1, -1 },\n\n\t{ 0x40, -1, -1, -1 },\n\t{ 0x41,\t0x102e,\t0x102c,\t-1 },\t/* keypad . */\n\t{ 0x42, -1, -1, -1 },\n\t{ 0x43,\t0x102a,\t0x102a,\t-1 },\t/* keypad * */\n\t{ 0x44, -1, -1, -1 },\n\t{ 0x45,\t0x102b, 0x102b, -1 },\t/* keypad + */\n\t{ 0x46, -1, -1, -1 },\n\t{ 0x47,\t0x1018,\t0x1018,\t-1 },\t/* keypad Clear */\n\n\t{ 0x48, -1, -1, -1 },\n\t{ 0x49, -1, -1, -1 },\n\t{ 0x4a, -1, -1, -1 },\n\t{ 0x4b,\t0x102f,\t0x102f,\t-1 },\t/* keypad / */\n\t{ 0x4c,\t0x100d,\t0x100d,\t-1 },\t/* keypad enter */\n\t{ 0x4d, -1, -1, -1 },\n\t{ 0x4e,\t0x102d,\t0x102d,\t-1 },\t/* keypad - */\n\t{ 0x4f, -1, -1, -1 },\n\n\t{ 0x50, -1, -1, -1 },\n\t{ 0x51,\t0x103d,\t0x103d,\t-1 },\t/* keypad = */\n\t{ 0x52,\t0x1030,\t0x1030,\t-1 },\t/* keypad 0 */\n\t{ 0x53,\t0x1031,\t0x1031,\t-1 },\t/* keypad 1 */\n\t{ 0x54,\t0x1032,\t0x1032,\t-1 },\t/* keypad 2 */\n\t{ 0x55,\t0x1033,\t0x1033,\t-1 },\t/* keypad 3 */\n\t{ 0x56,\t0x1034,\t0x1034,\t-1 },\t/* keypad 4 */\n\t{ 0x57,\t0x1035, 0x1035, -1 },\t/* keypad 5 */\n\n\t{ 0x58,\t0x1036, 0x1036, -1 },\t/* keypad 6 */\n\t{ 0x59,\t0x1037, 0x1037,\t-1 },\t/* keypad 7 */\n\t{ 0x5a,\t'a',\t'A',\t0x01 },\t/* probably not necessary */\n\t{ 0x5b,\t0x1038,\t0x1038,\t-1 },\t/* keypad 8 */\n\t{ 0x5c,\t0x1039,\t0x1039,\t-1 },\t/* keypad 9 */\n\t{ 0x5d, -1, -1, -1 },\n\t{ 0x5e, -1, -1, -1 },\n\t{ 0x5f, -1, -1, -1 },\n\n\t{ 0x60,\t0x8005,\t0x1060,\t-1 },\t/* F5 */\n\t{ 0x61,\t0x8006,\t0x1061,\t-1 },\t/* F6 */\n\t{ 0x62,\t0x8007,\t0x1062,\t-1 },\t/* F7 */\n\t{ 0x63,\t0x8003,\t0x1063,\t-1 },\t/* F3 */\n\t{ 0x64,\t0x8008,\t0x1064,\t-1 },\t/* F8 */\n\t{ 0x65,\t0x8009,\t0x1065,\t-1 },\t/* F9 */\n\t{ 0x66, -1, -1, -1 },\n\t{ 0x67,\t0x800b,\t0x1067,\t-1 },\t/* F11 */\n\n\t{ 0x68, -1, -1, -1 },\n\t{ 0x69,\t0x800d,\t0x1069,\t-1 },\t/* F13 */\n\t{ 0x6a, -1, -1, -1 },\n\t{ 0x6b,\t0x800e,\t0x106b,\t-1 },\t/* F14 */\n\t{ 0x6c, -1, -1, -1 },\n\t{ 0x6d,\t0x800a,\t0x106d,\t-1 },\t/* F10 */\n\t{ 0x6e, 0x4000, 0x4000, -1 },\t/* windows key alias to option */\n\t{ 0x6f,\t0x800c,\t0x106f,\t-1 },\t/* F12 */\n\n\t{ 0x70, -1, -1, -1 },\n\t{ 0x71,\t0x800f,\t0x1071,\t-1 },\t/* F15 */\n\t{ 0x72,\t0x1072,\t0x1072,\t-1 },\t/* Help, insert */\n\t{ 0x73,\t0x1073,\t0x1073,\t-1 },\t/* Home */\n\t{ 0x74,\t0x1074,\t0x1074,\t-1 },\t/* Page up */\n\t{ 0x75,\t0x1075,\t0x1075,\t-1 },\t/* keypad delete */\n\t{ 0x76,\t0x8004,\t0x1076,\t-1 },\t/* F4 */\n\t{ 0x77,\t0x1077,\t0x1077,\t-1 },\t/* keypad end */\n\n\t{ 0x78,\t0x8002,\t0x1078,\t-1 },\t/* F2 */\n\t{ 0x79,\t0x1079,\t0x1079,\t-1 },\t/* keypad page down */\n\t{ 0x7a,\t0x8001,\t0x107a,\t-1 },\t/* F1 */\n\t{ 0x7b,\t0x08,\t0x08,\t-1 },\t/* left */\t/* remapped to 0x3b */\n\t{ 0x7c,\t0x15,\t0x15,\t-1 },\t/* right */\t/* remapped to 0x3c */\n\t{ 0x7d,\t0x0a,\t0x0a,\t-1 },\t/* down */\t/* remapped to 0x3d */\n\t{ 0x7e,\t0x0b,\t0x0b,\t-1 },\t/* up arrow */\t/* remapped to 0x3e */\n\t{ 0x7f, -1,\t-1,\t-1 },\t/* Reset */\n};\n\nint\nadb_get_hide_warp_info(Kimage *kimage_ptr, int *warpptr)\n{\n\tif((kimage_ptr == &g_mainwin_kimage) && g_adb_mainwin_has_focus) {\n\t\t*warpptr = g_warp_pointer;\n\t\treturn g_hide_pointer;\n\t}\n\t*warpptr = 0;\n\treturn 0;\n}\n\nint\nadb_get_copy_requested()\n{\n\tint\tret;\n\n\tret = g_adb_copy_requested;\n\tg_adb_copy_requested = 0;\n\treturn ret;\n}\n\nvoid\nadb_nonmain_check()\n{\n\t// Debug window active.  Undo F8 pointer warping\n\tg_warp_pointer = 0;\n\tg_hide_pointer = 0;\n}\n\nvoid\nadb_init()\n{\n\tint\tkeycode;\n\tint\ti;\n\n\tif(g_adb_init) {\n\t\thalt_printf(\"g_adb_init = %d!\\n\", g_adb_init);\n\t}\n\tg_adb_init = 1;\n\n\tfor(i = 0; i < 128; i++) {\n\t\tkeycode = g_a2_key_to_ascii[i][0];\n\t\tif(keycode != i) {\n\t\t\tprintf(\"ADB keycode lost/skipped: i=%x: keycode=%x\\n\",\n\t\t\t\ti, keycode);\n\t\t\tmy_exit(1);\n\t\t}\n\t\tg_rawa2_to_a2code[i] = -1;\n\t}\n\n\tg_c025_val = 0;\n\n\tfor(i = 0; i < 4; i++) {\n\t\tg_virtual_key_up[i] = -1;\n\t}\n\n\tfor(i = 0; i < 10; i++) {\n\t\tg_keypad_key_is_down[i] = 0;\n\t}\n}\n\nvoid\nadb_reset()\n{\n\tg_c027_val = 0;\n\n\tg_key_down = 0;\n\tg_kbd_paste_rd_pos = 0;\n\tg_kbd_paste_wr_pos = 0;\n\tg_kbd_chars_buffered = 0;\n\n\tg_kbd_dev_addr = 2;\n\tg_mouse_dev_addr = 3;\n\n\tg_kbd_ctl_addr = 2;\n\tg_mouse_ctl_addr = 3;\n\n\tadb_clear_data_int();\n\tadb_clear_mouse_int();\n\tadb_clear_kbd_srq();\n\n\tg_adb_data_pending = 0;\n\tg_adb_interrupt_byte = 0;\n\tg_adb_state = ADB_IDLE;\n\tg_adb_mouse_coord = 0;\n\tg_adb_mouse_valid_data = 0;\n\n\tg_kbd_reg0_pos = 0;\n\tg_kbd_reg3_16bit = 0x602;\n}\n\n#define LEN_ADB_LOG\t16\nSTRUCT(Adb_log) {\n\tword32\taddr;\n\tint\tval;\n\tint\tstate;\n};\n\nAdb_log g_adb_log[LEN_ADB_LOG];\nint\tg_adb_log_pos = 0;\n\nvoid\nadb_log(word32 addr, int val)\n{\n\tint\tpos;\n\n\tpos = g_adb_log_pos;\n\tg_adb_log[pos].addr = addr;\n\tg_adb_log[pos].val = val;\n\tg_adb_log[pos].state = g_adb_state;\n\tpos++;\n\tif(pos >= LEN_ADB_LOG) {\n\t\tpos = 0;\n\t}\n\tg_adb_log_pos = pos;\n}\n\nvoid\nshow_adb_log(void)\n{\n\tint\tpos;\n\tint\ti;\n\n\tpos = g_adb_log_pos;\n\tprintf(\"ADB log pos: %d\\n\", pos);\n\tfor(i = 0; i < LEN_ADB_LOG; i++) {\n\t\tpos--;\n\t\tif(pos < 0) {\n\t\t\tpos = LEN_ADB_LOG - 1;\n\t\t}\n\t\tprintf(\"%d:%d:  addr:%04x = %02x, st:%d\\n\", i, pos,\n\t\t\tg_adb_log[pos].addr, g_adb_log[pos].val,\n\t\t\tg_adb_log[pos].state);\n\t}\n\tprintf(\"kbd: dev: %x, ctl: %x; mouse: dev: %x, ctl: %x\\n\",\n\t\tg_kbd_dev_addr, g_kbd_ctl_addr,\n\t\tg_mouse_dev_addr, g_mouse_ctl_addr);\n\tprintf(\"g_adb_state: %d, g_adb_interrupt_byte: %02x\\n\",\n\t\tg_adb_state, g_adb_interrupt_byte);\n}\n\nvoid\nadb_error(void)\n{\n\thalt_printf(\"Adb Error\\n\");\n\n\tshow_adb_log();\n}\n\n\n\nvoid\nadb_add_kbd_srq()\n{\n\tif(g_kbd_reg3_16bit & 0x200) {\n\t\t/* generate SRQ */\n\t\tg_adb_interrupt_byte |= 0x08;\n\t\tadd_irq(IRQ_PENDING_ADB_KBD_SRQ);\n\t} else {\n\t\tprintf(\"Got keycode but no kbd SRQ!\\n\");\n\t}\n}\n\nvoid\nadb_clear_kbd_srq()\n{\n\tremove_irq(IRQ_PENDING_ADB_KBD_SRQ);\n\n\t/* kbd SRQ's are the only ones to handle now, so just clean it out */\n\tg_adb_interrupt_byte &= (~(0x08));\n}\n\nvoid\nadb_add_data_int()\n{\n\tif(g_c027_val & ADB_C027_DATA_INT) {\n\t\tadd_irq(IRQ_PENDING_ADB_DATA);\n\t}\n}\n\nvoid\nadb_add_mouse_int()\n{\n\tif(g_c027_val & ADB_C027_MOUSE_INT) {\n\t\tadd_irq(IRQ_PENDING_ADB_MOUSE);\n\t}\n}\n\nvoid\nadb_clear_data_int()\n{\n\tremove_irq(IRQ_PENDING_ADB_DATA);\n}\n\nvoid\nadb_clear_mouse_int()\n{\n\tremove_irq(IRQ_PENDING_ADB_MOUSE);\n}\n\n\nvoid\nadb_send_bytes(int num_bytes, word32 val0, word32 val1, word32 val2)\n{\n\tword32\tval;\n\tint\tshift_amount;\n\tint\ti;\n\n\tif((num_bytes >= 12) || (num_bytes >= MAX_ADB_DATA_PEND))  {\n\t\thalt_printf(\"adb_send_bytes: %d is too many!\\n\", num_bytes);\n\t}\n\n\tg_adb_state = ADB_SENDING_DATA;\n\tg_adb_data_pending = num_bytes;\n\tadb_add_data_int();\n\n\tfor(i = 0; i < num_bytes; i++) {\n\t\tif(i < 4) {\n\t\t\tval = val0;\n\t\t} else if(i < 8) {\n\t\t\tval = val1;\n\t\t} else {\n\t\t\tval = val2;\n\t\t}\n\n\t\tshift_amount = 8*(3 - i);\n\t\tg_adb_data[i] = (val >> shift_amount) & 0xff;\n\t\tadb_printf(\"adb_send_bytes[%d] = %02x\\n\", i, g_adb_data[i]);\n\t}\n}\n\n\nvoid\nadb_send_1byte(word32 val)\n{\n\n\tif(g_adb_data_pending != 0) {\n\t\thalt_printf(\"g_adb_data_pending: %d\\n\", g_adb_data_pending);\n\t}\n\n\tadb_send_bytes(1, val << 24, 0, 0);\n}\n\nvoid\nadb_response_packet(int num_bytes, word32 val)\n{\n\n\tif(g_adb_data_pending != 0) {\n\t\thalt_printf(\"adb_response_packet, but pending: %d\\n\",\n\t\t\tg_adb_data_pending);\n\t}\n\n\tg_adb_state = ADB_IDLE;\n\tg_adb_data_pending = num_bytes;\n\tg_adb_data[0] = val & 0xff;\n\tg_adb_data[1] = (val >> 8) & 0xff;\n\tg_adb_data[2] = (val >> 16) & 0xff;\n\tg_adb_data[3] = (val >> 24) & 0xff;\n\tif(num_bytes) {\n\t\tg_adb_interrupt_byte |= 0x80 + num_bytes - 1;\n\t} else {\n\t\tg_adb_interrupt_byte |= 0x80;\n\t}\n\n\tadb_printf(\"adb_response packet: %d: %08x\\n\",\n\t\tnum_bytes, val);\n\n\tadb_add_data_int();\n}\n\nvoid\nadb_kbd_reg0_data(int a2code, int is_up)\n{\n\tif(g_kbd_reg0_pos >= MAX_ADB_KBD_REG3) {\n\t\t/* too many keys, toss */\n\t\thalt_printf(\"Had to toss key: %02x, %d\\n\", a2code, is_up);\n\t\treturn;\n\t}\n\n\tg_kbd_reg0_data[g_kbd_reg0_pos] = a2code + (is_up << 7);\n\n\tadb_printf(\"g_kbd_reg0_data[%d] = %02x\\n\", g_kbd_reg0_pos,\n\t\tg_kbd_reg0_data[g_kbd_reg0_pos]);\n\n\tg_kbd_reg0_pos++;\n\n\tadb_add_kbd_srq();\n}\n\nvoid\nadb_kbd_talk_reg0()\n{\n\tword32\tval0, val1, reg;\n\tint\tnum_bytes, num;\n\tint\ti;\n\n\tnum = 0;\n\tval0 = g_kbd_reg0_data[0];\n\tval1 = g_kbd_reg0_data[1];\n\n\tnum_bytes = 0;\n\tif(g_kbd_reg0_pos > 0) {\n\t\tnum_bytes = 2;\n\t\tnum = 1;\n\t\tif((val0 & 0x7f) == 0x7f) {\n\t\t\t/* reset */\n\t\t\tval1 = val0;\n\t\t} else if(g_kbd_reg0_pos > 1) {\n\t\t\tnum = 2;\n\t\t\tif((val1 & 0x7f) == 0x7f) {\n\t\t\t\t/* If first byte some other key, don't */\n\t\t\t\t/*  put RESET next! */\n\t\t\t\tnum = 1;\n\t\t\t\tval1 = 0xff;\n\t\t\t}\n\t\t} else {\n\t\t\tval1 = 0xff;\n\t\t}\n\t}\n\n\tif(num) {\n\t\tfor(i = num; i < g_kbd_reg0_pos; i++) {\n\t\t\tg_kbd_reg0_data[i-1] = g_kbd_reg0_data[i];\n\t\t}\n\t\tg_kbd_reg0_pos -= num;\n\t}\n\n\treg = (val0 << 8) + val1;\n\n\tadb_printf(\"adb_kbd_talk0: %04x\\n\", reg);\n\n\tadb_response_packet(num_bytes, reg);\n\tif(g_kbd_reg0_pos == 0) {\n\t\tadb_clear_kbd_srq();\n\t}\n}\n\nvoid\nadb_set_config(word32 val0, word32 val1, word32 val2)\n{\n\tint\tnew_mouse;\n\tint\tnew_kbd;\n\tint\ttmp1;\n\n\tnew_mouse = val0 >> 4;\n\tnew_kbd = val0  & 0xf;\n\tif(new_mouse != g_mouse_ctl_addr) {\n\t\tprintf(\"ADB config: mouse from %x to %x!\\n\",\n\t\t\tg_mouse_ctl_addr, new_mouse);\n\t\tadb_error();\n\t\tg_mouse_ctl_addr = new_mouse;\n\t}\n\tif(new_kbd != g_kbd_ctl_addr) {\n\t\tprintf(\"ADB config: kbd from %x to %x!\\n\",\n\t\t\tg_kbd_ctl_addr, new_kbd);\n\t\tadb_error();\n\t\tg_kbd_ctl_addr = new_kbd;\n\t}\n\n\tif(val1) {\n\t\t// Do nothing\n\t}\n\n\ttmp1 = val2 >> 4;\n\tif(tmp1 == 4) {\n\t\tg_adb_repeat_delay = 0;\n\t} else if(tmp1 < 4) {\n\t\tg_adb_repeat_delay = (tmp1 + 1) * 15;\n\t} else {\n\t\thalt_printf(\"Bad ADB repeat delay: %02x\\n\", tmp1);\n\t}\n\n\ttmp1 = val2 & 0xf;\n\tif(g_rom_version >= 3) {\n\t\ttmp1 = 9 - tmp1;\n\t}\n\n\tswitch(tmp1) {\n\tcase 0:\n\t\tg_adb_repeat_rate = 1;\n\t\tbreak;\n\tcase 1:\n\t\tg_adb_repeat_rate = 2;\n\t\tbreak;\n\tcase 2:\n\t\tg_adb_repeat_rate = 3;\n\t\tbreak;\n\tcase 3:\n\t\tg_adb_repeat_rate = 3;\n\t\tbreak;\n\tcase 4:\n\t\tg_adb_repeat_rate = 4;\n\t\tbreak;\n\tcase 5:\n\t\tg_adb_repeat_rate = 5;\n\t\tbreak;\n\tcase 6:\n\t\tg_adb_repeat_rate = 7;\n\t\tbreak;\n\tcase 7:\n\t\tg_adb_repeat_rate = 15;\n\t\tbreak;\n\tcase 8:\n\t\t/* I don't know what this should be, ROM 03 uses it */\n\t\tg_adb_repeat_rate = 30;\n\t\tbreak;\n\tcase 9:\n\t\t/* I don't know what this should be, ROM 03 uses it */\n\t\tg_adb_repeat_rate = 60;\n\t\tbreak;\n\tdefault:\n\t\thalt_printf(\"Bad repeat rate: %02x\\n\", tmp1);\n\t}\n\n}\n\nvoid\nadb_set_new_mode(word32 val)\n{\n\tif(val & 0x03) {\n\t\tprintf(\"Disabling keyboard/mouse:%02x!\\n\", val);\n\t}\n\n\tif(val & 0xa2) {\n\t\thalt_printf(\"ADB set mode: %02x!\\n\", val);\n\t\tadb_error();\n\t}\n\n\tg_adb_mode = val;\n}\n\n\nint\nadb_read_c026()\n{\n\tword32\tret;\n\tint\ti;\n\n\tret = 0;\n\tswitch(g_adb_state) {\n\tcase ADB_IDLE:\n\t\tret = g_adb_interrupt_byte;\n\t\tg_adb_interrupt_byte = 0;\n\t\tif(g_irq_pending & IRQ_PENDING_ADB_KBD_SRQ) {\n\t\t\tg_adb_interrupt_byte |= 0x08;\n\t\t}\n\t\tif(g_adb_data_pending == 0) {\n\t\t\tif(ret & 0x80) {\n\t\t\t\thalt_printf(\"read_c026: ret:%02x, pend:%d\\n\",\n\t\t\t\t\tret, g_adb_data_pending);\n\t\t\t}\n\t\t\tadb_clear_data_int();\n\t\t}\n\t\tif(g_adb_data_pending) {\n\t\t\tif(g_adb_state != ADB_IN_CMD) {\n\t\t\t\tg_adb_state = ADB_SENDING_DATA;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase ADB_IN_CMD:\n\t\tret = 0;\n\t\tbreak;\n\tcase ADB_SENDING_DATA:\n\t\tret = g_adb_data[0];\n\t\tfor(i = 1; i < g_adb_data_pending; i++) {\n\t\t\tg_adb_data[i-1] = g_adb_data[i];\n\t\t}\n\t\tg_adb_data_pending--;\n\t\tif(g_adb_data_pending <= 0) {\n\t\t\tg_adb_data_pending = 0;\n\t\t\tg_adb_state = ADB_IDLE;\n\t\t\tadb_clear_data_int();\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\thalt_printf(\"Bad ADB state: %d!\\n\", g_adb_state);\n\t\tadb_clear_data_int();\n\t\tbreak;\n\t}\n\n\tadb_printf(\"Reading c026.  Returning %02x, st: %02x, pend: %d\\n\",\n\t\tret, g_adb_state, g_adb_data_pending);\n\n\tadb_log(0xc026, ret);\n\treturn (ret & 0xff);\n}\n\n\nvoid\nadb_write_c026(int val)\n{\n\tword32\ttmp;\n\tint\tdev;\n\n\tadb_printf(\"Writing c026 with %02x\\n\", val);\n\tadb_log(0x1c026, val);\n\n\n\tswitch(g_adb_state) {\n\tcase ADB_IDLE:\n\t\tg_adb_cmd = val;\n\t\tg_adb_cmd_so_far = 0;\n\t\tg_adb_cmd_len = 0;\n\n\t\tdev = val & 0xf;\n\t\tswitch(val) {\n\t\tcase 0x01:\t/* Abort */\n\t\t\tadb_printf(\"Performing adb abort\\n\");\n\t\t\t/* adb_abort() */\n\t\t\tbreak;\n\t\tcase 0x03:\t/* Flush keyboard buffer */\n\t\t\tadb_printf(\"Flushing adb keyboard buffer\\n\");\n\t\t\t/* Do nothing */\n\t\t\tbreak;\n\t\tcase 0x04:\t/* Set modes */\n\t\t\tadb_printf(\"ADB set modes\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 1;\n\t\t\tbreak;\n\t\tcase 0x05:\t/* Clear modes */\n\t\t\tadb_printf(\"ADB clear modes\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 1;\n\t\t\tbreak;\n\t\tcase 0x06:\t/* Set config */\n\t\t\tadb_printf(\"ADB set config\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 3;\n\t\t\tbreak;\n\t\tcase 0x07:\t/* Sync */\n\t\t\tadb_printf(\"Performing sync cmd!\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tif(g_rom_version == 1) {\n\t\t\t\tg_adb_cmd_len = 4;\n\t\t\t} else {\n\t\t\t\tg_adb_cmd_len = 8;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x08:\t/* Write mem */\n\t\t\tadb_printf(\"Starting write_mem cmd\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 2;\n\t\t\tbreak;\n\t\tcase 0x09:\t/* Read mem */\n\t\t\tadb_printf(\"Performing read_mem cmd!\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 2;\n\t\t\tbreak;\n\t\tcase 0x0a:\t/* Read modes byte */\n\t\t\tprintf(\"Performing read_modes cmd!\\n\");\n\t\t\t/* set_halt(1); */\n\t\t\tadb_send_1byte(g_adb_mode);\n\t\t\tbreak;\n\t\tcase 0x0b:\t/* Read config bytes */\n\t\t\tprintf(\"Performing read_configs cmd!\\n\");\n\t\t\ttmp = (g_mouse_ctl_addr << 20) +\n\t\t\t\t(g_kbd_ctl_addr << 16) +\n\t\t\t\t(g_adb_char_set << 12) +\n\t\t\t\t(g_adb_layout_lang << 8) +\n\t\t\t\t(g_adb_repeat_info << 0);\n\t\t\ttmp = (0x82U << 24) + tmp;\n\t\t\tadb_send_bytes(4, tmp, 0, 0);\n\t\t\tbreak;\n\t\tcase 0x0d:\t/* Get Version */\n\t\t\tadb_printf(\"Performing get_version cmd!\\n\");\n\t\t\tval = 0;\n\t\t\tif(g_rom_version == 1) {\n\t\t\t\t/* ROM 01 = revision 5 */\n\t\t\t\tval = 5;\n\t\t\t} else {\n\t\t\t\t/* ROM 03 checks for rev >= 6 */\n\t\t\t\tval = 6;\n\t\t\t}\n\t\t\tadb_send_1byte(val);\n\t\t\tbreak;\n\t\tcase 0x0e:\t/* Read avail char sets */\n\t\t\tadb_printf(\"Performing read avail char sets cmd!\\n\");\n\t\t\tadb_send_bytes(2,\t/* just 2 bytes */\n\t\t\t\t0x08000000,\t/* number of ch sets=0x8 */\n\t\t\t\t0, 0);\n\t\t\t/* set_halt(1); */\n\t\t\tbreak;\n\t\tcase 0x0f:\t/* Read avail kbd layouts */\n\t\t\tadb_printf(\"Performing read avail kbd layouts cmd!\\n\");\n\t\t\tadb_send_bytes(0x2,\t/* number of kbd layouts=0xa */\n\t\t\t\t0x0a000000, 0, 0);\n\t\t\t/* set_halt(1); */\n\t\t\tbreak;\n\t\tcase 0x10:\t/* Reset */\n\t\t\tprintf(\"ADB reset, cmd 0x10\\n\");\n\t\t\tdo_reset();\n\t\t\tbreak;\n\t\tcase 0x11:\t/* Send ADB keycodes */\n\t\t\tadb_printf(\"Sending ADB keycodes\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 1;\n\t\t\tbreak;\n\t\tcase 0x12:\t/* ADB cmd 12: ROM 03 only! */\n\t\t\tif(g_rom_version >= 3) {\n\t\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\t\tg_adb_cmd_len = 2;\n\t\t\t} else {\n\t\t\t\tprintf(\"ADB cmd 12, but not ROM 3!\\n\");\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x13:\t/* ADB cmd 13: ROM 03 only! */\n\t\t\tif(g_rom_version >= 3) {\n\t\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\t\tg_adb_cmd_len = 2;\n\t\t\t} else {\n\t\t\t\tprintf(\"ADB cmd 13, but not ROM 3!\\n\");\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x73:\t/* Disable SRQ device 3: mouse */\n\t\t\tadb_printf(\"Disabling Mouse SRQ's (device 3)\\n\");\n\t\t\t/* HACK HACK...should deal with SRQs on mouse */\n\t\t\tbreak;\n\t\tcase 0xb0: case 0xb1: case 0xb2: case 0xb3:\n\t\tcase 0xb4: case 0xb5: case 0xb6: case 0xb7:\n\t\tcase 0xb8: case 0xb9: case 0xba: case 0xbb:\n\t\tcase 0xbc: case 0xbd: case 0xbe: case 0xbf:\n\t\t\t/* Listen dev x reg 3 */\n\t\t\tadb_printf(\"Sending data to dev %x reg 3\\n\", dev);\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 2;\n\t\t\tbreak;\n\t\tcase 0xc0: case 0xc1: case 0xc2: case 0xc3:\n\t\tcase 0xc4: case 0xc5: case 0xc6: case 0xc7:\n\t\tcase 0xc8: case 0xc9: case 0xca: case 0xcb:\n\t\tcase 0xcc: case 0xcd: case 0xce: case 0xcf:\n\t\t\t/* Talk dev x reg 0 */\n\t\t\tadb_printf(\"Performing talk dev %x reg 0\\n\", dev);\n\t\t\tif(dev == g_kbd_dev_addr) {\n\t\t\t\tadb_kbd_talk_reg0();\n\t\t\t} else {\n\t\t\t\tprintf(\"Unknown talk dev %x reg 0!\\n\", dev);\n\t\t\t\t/* send no data, on SRQ, system polls devs */\n\t\t\t\t/*  so we don't want to send anything */\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0xf0: case 0xf1: case 0xf2: case 0xf3:\n\t\tcase 0xf4: case 0xf5: case 0xf6: case 0xf7:\n\t\tcase 0xf8: case 0xf9: case 0xfa: case 0xfb:\n\t\tcase 0xfc: case 0xfd: case 0xfe: case 0xff:\n\t\t\t/* Talk dev x reg 3 */\n\t\t\tadb_printf(\"Performing talk dev %x reg 3\\n\", dev);\n\t\t\tif(dev == g_kbd_dev_addr) {\n\t\t\t\tadb_response_packet(2, g_kbd_reg3_16bit);\n\t\t\t} else {\n\t\t\t\tprintf(\"Performing talk dev %x reg 3!!\\n\", dev);\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thalt_printf(\"ADB ucontroller cmd %02x unknown!\\n\", val);\n\t\t\t/* The Gog's says ACS Demo 2 has a bug and writes to */\n\t\t\t/*  c026 */\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase ADB_IN_CMD:\n\t\tadb_printf(\"Setting byte %d of cmd %02x to %02x\\n\",\n\t\t\tg_adb_cmd_so_far, g_adb_cmd, val);\n\n\t\tg_adb_cmd_data[g_adb_cmd_so_far] = val;\n\t\tg_adb_cmd_so_far++;\n\t\tif(g_adb_cmd_so_far >= g_adb_cmd_len) {\n\t\t\tadb_printf(\"Finished cmd %02x\\n\", g_adb_cmd);\n\t\t\tdo_adb_cmd();\n\t\t}\n\n\t\tbreak;\n\tdefault:\n\t\tprintf(\"adb_state: %02x is unknown!  Setting it to ADB_IDLE\\n\",\n\t\t\tg_adb_state);\n\t\tg_adb_state = ADB_IDLE;\n\t\tadb_error();\n\t\thalt_on_all_c027 = 1;\n\t\tbreak;\n\t}\n\treturn;\n}\n\nvoid\ndo_adb_cmd()\n{\n\tword32\tval;\n\tint\tdev, new_kbd, addr;\n\n\tdev = g_adb_cmd & 0xf;\n\n\tg_adb_state = ADB_IDLE;\n\n\tswitch(g_adb_cmd) {\n\tcase 0x04:\t/* Set modes */\n\t\tadb_printf(\"Performing ADB set mode: OR'ing in %02x\\n\",\n\t\t\tg_adb_cmd_data[0]);\n\n\t\tval = g_adb_cmd_data[0] | g_adb_mode;\n\t\tadb_set_new_mode(val);\n\n\t\tbreak;\n\tcase 0x05:\t/* clear modes */\n\t\tadb_printf(\"Performing ADB clear mode: AND'ing in ~%02x\\n\",\n\t\t\tg_adb_cmd_data[0]);\n\n\t\tval = g_adb_cmd_data[0];\n\t\tval = g_adb_mode & (~val);\n\t\tadb_set_new_mode(val);\n\t\tbreak;\n\tcase 0x06:\t/* Set config */\n\t\tadb_printf(\"Set ADB config to %02x %02x %02x\\n\",\n\t\t\tg_adb_cmd_data[0], g_adb_cmd_data[1],g_adb_cmd_data[2]);\n\n\t\tadb_set_config(g_adb_cmd_data[0], g_adb_cmd_data[1],\n\t\t\tg_adb_cmd_data[2]);\n\n\t\tbreak;\n\tcase 0x07:\t/* SYNC */\n\t\tadb_printf(\"Performing ADB SYNC\\n\");\n\t\tadb_printf(\"data: %02x %02x %02x %02x\\n\",\n\t\t\tg_adb_cmd_data[0], g_adb_cmd_data[1], g_adb_cmd_data[2],\n\t\t\tg_adb_cmd_data[3]);\n\n\t\tadb_set_new_mode(g_adb_cmd_data[0]);\n\t\tadb_set_config(g_adb_cmd_data[1], g_adb_cmd_data[2],\n\t\t\t\tg_adb_cmd_data[3]);\n\n\t\tif(g_rom_version >= 3) {\n\t\t\tadb_printf(\"  and cmd12:%02x %02x cmd13:%02x %02x\\n\",\n\t\t\t\tg_adb_cmd_data[4], g_adb_cmd_data[5],\n\t\t\t\tg_adb_cmd_data[6], g_adb_cmd_data[7]);\n\t\t}\n\t\tbreak;\n\tcase 0x08:\t/* Write mem */\n\t\taddr = g_adb_cmd_data[0];\n\t\tval = g_adb_cmd_data[1];\n\t\twrite_adb_ram(addr, val);\n\t\tbreak;\n\tcase 0x09:\t/* Read mem */\n\t\taddr = (g_adb_cmd_data[1] << 8) + g_adb_cmd_data[0];\n\t\tadb_printf(\"Performing mem read to addr %04x\\n\", addr);\n\t\tadb_send_1byte(read_adb_ram(addr));\n\t\tbreak;\n\tcase 0x11:\t/* Send ADB keycodes */\n\t\tval = g_adb_cmd_data[0];\n\t\tadb_printf(\"Performing send ADB keycodes: %02x\\n\", val);\n\t\tadb_virtual_key_update(val & 0x7f, val >> 7);\n\t\tbreak;\n\tcase 0x12:\t/* ADB cmd12 */\n\t\tadb_printf(\"Performing ADB cmd 12\\n\");\n\t\tadb_printf(\"data: %02x %02x\\n\", g_adb_cmd_data[0],\n\t\t\t\t\t\t\tg_adb_cmd_data[1]);\n\t\tbreak;\n\tcase 0x13:\t/* ADB cmd13 */\n\t\tadb_printf(\"Performing ADB cmd 13\\n\");\n\t\tadb_printf(\"data: %02x %02x\\n\", g_adb_cmd_data[0],\n\t\t\t\t\t\t\tg_adb_cmd_data[1]);\n\t\tbreak;\n\tcase 0xb0: case 0xb1: case 0xb2: case 0xb3:\n\tcase 0xb4: case 0xb5: case 0xb6: case 0xb7:\n\tcase 0xb8: case 0xb9: case 0xba: case 0xbb:\n\tcase 0xbc: case 0xbd: case 0xbe: case 0xbf:\n\t\t/* Listen dev x reg 3 */\n\t\tif(dev == g_kbd_dev_addr) {\n\t\t\tif(g_adb_cmd_data[1] == 0xfe) {\n\t\t\t\t/* change keyboard addr? */\n\t\t\t\tnew_kbd = g_adb_cmd_data[0] & 0xf;\n\t\t\t\tif(new_kbd != dev) {\n\t\t\t\t\tprintf(\"Moving kbd to dev %x!\\n\",\n\t\t\t\t\t\t\t\tnew_kbd);\n\t\t\t\t\tadb_error();\n\t\t\t\t}\n\t\t\t\tg_kbd_dev_addr = new_kbd;\n\t\t\t} else if(g_adb_cmd_data[1] != 1) {\n\t\t\t\t/* see what new device handler id is */\n\t\t\t\tprintf(\"KBD listen to dev %x reg 3: 1:%02x\\n\",\n\t\t\t\t\tdev, g_adb_cmd_data[1]);\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tif(g_adb_cmd_data[0] != (word32)g_kbd_dev_addr) {\n\t\t\t\t/* see if app is trying to change addr */\n\t\t\t\tprintf(\"KBD listen to dev %x reg 3: 0:%02x!\\n\",\n\t\t\t\t\tdev, g_adb_cmd_data[0]);\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tg_kbd_reg3_16bit = ((g_adb_cmd_data[0] & 0xf) << 12) +\n\t\t\t\t(g_kbd_reg3_16bit & 0x0fff);\n\t\t} else if(dev == g_mouse_dev_addr) {\n\t\t\tif(g_adb_cmd_data[0] != (word32)dev) {\n\t\t\t\t/* see if app is trying to change mouse addr */\n\t\t\t\tprintf(\"MOUS listen to dev %x reg3: 0:%02x!\\n\",\n\t\t\t\t\tdev, g_adb_cmd_data[0]);\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tif(g_adb_cmd_data[1] != 1 && g_adb_cmd_data[1] != 2) {\n\t\t\t\t/* see what new device handler id is */\n\t\t\t\tprintf(\"MOUS listen to dev %x reg 3: 1:%02x\\n\",\n\t\t\t\t\tdev, g_adb_cmd_data[1]);\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t} else {\n\t\t\tprintf(\"Listen cmd to dev %x reg3????\\n\", dev);\n\t\t\tprintf(\"data0: %02x, data1: %02x ????\\n\",\n\t\t\t\tg_adb_cmd_data[0], g_adb_cmd_data[1]);\n\t\t\tadb_error();\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tprintf(\"Doing adb_cmd %02x: UNKNOWN!\\n\", g_adb_cmd);\n\t\tbreak;\n\t}\n}\n\n\nint\nadb_read_c027()\n{\n\tword32\tret;\n\n\tif(halt_on_all_c027) {\n\t\thalt_printf(\"halting on all c027 reads!\\n\");\n\t}\n\n\tif(g_c027_val & (~ADB_C027_NEG_MASK)) {\n\t\thalt_printf(\"read_c027: g_c027_val: %02x\\n\", g_c027_val);\n\t}\n\n\tret = (g_c027_val & ADB_C027_NEG_MASK);\n\n\tif(g_adb_mouse_valid_data) {\n\t\tret |= ADB_C027_MOUSE_DATA;\n\t}\n\n\tif(g_adb_interrupt_byte != 0) {\n\t\tret |= ADB_C027_DATA_VALID;\n\t} else if(g_adb_data_pending > 0) {\n\t\tif((g_adb_state != ADB_IN_CMD)) {\n\t\t\tret |= ADB_C027_DATA_VALID;\n\t\t}\n\t}\n\n\tif(g_adb_mouse_coord) {\n\t\tret |= ADB_C027_MOUSE_COORD;\n\t}\n\n#if 0\n\tadb_printf(\"Read c027: %02x, int_byte: %02x, d_pend: %d\\n\",\n\t\tret, g_adb_interrupt_byte, g_adb_data_pending);\n#endif\n\n#if 0\n\tadb_log(0xc027, ret);\n#endif\n\treturn ret;\n}\n\nvoid\nadb_write_c027(int val)\n{\n\tword32\told_val;\n\tword32\tnew_int;\n\tword32\told_int;\n\n\tadb_printf(\"Writing c027 with %02x\\n\", val);\n\tadb_log(0x1c027, val);\n\n\n\told_val = g_c027_val;\n\n\tg_c027_val = (val & ADB_C027_NEG_MASK);\n\tnew_int = g_c027_val & ADB_C027_MOUSE_INT;\n\told_int = old_val & ADB_C027_MOUSE_INT;\n\tif(!new_int && old_int) {\n\t\tadb_clear_mouse_int();\n\t}\n\n\tnew_int = g_c027_val & ADB_C027_DATA_INT;\n\told_int = old_val & ADB_C027_DATA_INT;\n\tif(!new_int && old_int) {\n\t\t/* ints were on, now off */\n\t\tadb_clear_data_int();\n\t}\n\n\tif(g_c027_val & ADB_C027_KBD_INT) {\n\t\thalt_printf(\"Can't support kbd interrupts!\\n\");\n\t}\n\n\treturn;\n}\n\nint\nread_adb_ram(word32 addr)\n{\n\tint val;\n\n\tadb_printf(\"Reading adb ram addr: %02x\\n\", addr);\n\n\tif(addr >= 0x100) {\n\t\tif(addr >= 0x1000 && addr < 0x2000) {\n\t\t\t/* ROM self-test checksum */\n\t\t\tif(addr == 0x1400) {\n\t\t\t\tval = 0x72;\n\t\t\t} else if(addr == 0x1401) {\n\t\t\t\tval = 0xf7;\n\t\t\t} else {\n\t\t\t\tval = 0;\n\t\t\t}\n\t\t} else {\n\t\t\tprintf(\"adb ram addr out of range: %04x!\\n\", addr);\n\t\t\tval = 0;\n\t\t}\n\t} else {\n\t\tval = adb_memory[addr];\n\t\tif((addr == 0xb) && (g_rom_version == 1)) {\n\t\t\t// read special key state byte for Out of This World\n\t\t\tval = (g_c025_val >> 1) & 0x43;\n\t\t\tval |= (g_c025_val << 2) & 0x4;\n\t\t\tval |= (g_c025_val >> 2) & 0x10;\n\t\t}\n\t\tif((addr == 0xc) && (g_rom_version >= 3)) {\n\t\t\t// read special key state byte for Out of This World\n\t\t\tval = g_c025_val & 0xc7;\n\t\t\tprintf(\"val is %02x\\n\", val);\n\t\t}\n\t}\n\n\tadb_printf(\"adb_ram returning %02x\\n\", val);\n\treturn val;\n}\n\nvoid\nwrite_adb_ram(word32 addr, int val)\n{\n\n\tadb_printf(\"Writing adb_ram addr: %02x: %02x\\n\", addr, val);\n\n\tif(addr >= 0x100) {\n\t\tprintf(\"write adb_ram addr: %02x: %02x!\\n\", addr, val);\n\t\tadb_error();\n\t} else {\n\t\tadb_memory[addr] = val;\n\t}\n}\n\nint\nadb_get_keypad_xy(int get_y)\n{\n\tint\tx, y, key, num_keys;\n\tint\ti, j;\n\n\tkey = 1;\n\tnum_keys = 0;\n\tx = 0;\n\ty = 0;\n\tfor(i = 0; i < 3; i++) {\n\t\tfor(j = 0; j < 3; j++) {\n\t\t\tif(g_keypad_key_is_down[key]) {\n\t\t\t\tnum_keys++;\n\t\t\t\tx = x + (j - 1)*32768;\n\t\t\t\ty = y + (1 - i)*32768;\n\t\t\t}\n\t\t\tkey++;\n\t\t}\n\t}\n\tif(num_keys == 0) {\n\t\tnum_keys = 1;\n\t}\n\n\tadb_printf(\"get_xy=%d, num_keys: %d, x:%d, y:%d\\n\", get_y,\n\t\t\t\t\t\t\tnum_keys, x, y);\n\n\tif(get_y) {\n\t\treturn y / num_keys;\n\t} else {\n\t\treturn x / num_keys;\n\t}\n}\n\n// g_mouse_raw_x/y: Current position (in A2 coordinates) of mouse on host screen\n// g_mouse_fifo[0].x/y: Current position (in A2 coords) of where we \"want\"\n//\t\tmouse on the A2 screen.\n// g_mouse_a2_x/y: last x,y returned through $c024 to software.\n// So, reading $c024 return g_mouse_fifo[].x - g_mouse_a2_x.\n// And, in simple cases, host mouse movement just sets g_mouse_fifo[0].x=raw_x\nint\nadb_update_mouse(Kimage *kimage_ptr, int x, int y, int button_states,\n\t\t\t\t\t\t\tint buttons_valid)\n{\n\tdword64\tdfcyc;\n\tint\tbutton1_changed, mouse_moved, unhide, pos;\n\tint\ti;\n\n\tif(kimage_ptr != &g_mainwin_kimage) {\n\t\tadb_nonmain_check();\n\t}\n\tdfcyc = g_cur_dfcyc;\n\n\tunhide = (g_adb_mainwin_has_focus == 0);\n\tif((buttons_valid >= 0) && (buttons_valid & 0x1000)) {\n\t\t// x, y are really deltas\n\t\tbuttons_valid &= 0xfff;\n\t\tx = g_mouse_raw_x + x;\n\t\ty = g_mouse_raw_y + y;\n\t\tg_mouse_raw_x = x;\n\t\tg_mouse_raw_y = y;\n\t} else {\n\t\tg_mouse_raw_x = x;\n\t\tg_mouse_raw_y = y;\n\n\t\t// Clamp mouse to 0-639, 0-399 to make GSOS work nicely\n\t\tif(x < 0) {\n\t\t\tx = 0;\n\t\t\tunhide = 1;\n\t\t}\n\t\tif(x >= 640) {\n\t\t\tx = 639;\n\t\t\tunhide = 1;\n\t\t}\n\t\tif(y < 0) {\n\t\t\ty = 0;\n\t\t\tunhide = 1;\n\t\t}\n\t\tif(y >= 400) {\n\t\t\ty = 399;\n\t\t\tunhide = 1;\n\t\t}\n\t}\n\n\tg_unhide_pointer = unhide && !g_warp_pointer;\n\n\tif(kimage_ptr != &g_mainwin_kimage) {\n\t\t// In debugger window...just get out\n\t\treturn 0;\n\t}\n\n\tif(!g_warp_pointer) {\n\t\tif(g_hide_pointer && g_unhide_pointer) {\n\t\t\t/* cursor has left a2 window, show it */\n\t\t\tg_hide_pointer = 0;\n\t\t}\n\t\tif((g_num_lines_prev_superhires == 200) &&\n\t\t\t\t(g_num_lines_prev_superhires640 == 0) &&\n\t\t\t\t((g_slow_memory_ptr[0x19d00] & 0x80) == 0)) {\n\t\t\t// In 320-mode superhires, cut mouse range in half\n\t\t\tx = x >> 1;\n\t\t}\n\t\ty = y >> 1;\n\t}\n\n\tmouse_compress_fifo(dfcyc);\n\n#if 0\n\tprintf(\"Update Mouse called with buttons:%d x,y:%d,%d, fifo:%d,%d, \"\n\t\t\" a2: %d,%d\\n\", buttons_valid, x, y,\n\t\tg_mouse_fifo[0].x, g_mouse_fifo[0].y,\n\t\tg_mouse_a2_x, g_mouse_a2_y);\n#endif\n\n\tif((buttons_valid < 0) && g_warp_pointer) {\n\t\t/* Warping the pointer causes it to jump here...this is not */\n\t\t/*  real motion, just update info and get out */\n\t\tg_mouse_a2_x += (x - g_mouse_fifo[0].x);\n\t\tg_mouse_a2_y += (y - g_mouse_fifo[0].y);\n\t\tg_mouse_fifo[0].x = x;\n\t\tg_mouse_fifo[0].y = y;\n\t\treturn 0;\n\t}\n\n#if 0\n\tprintf(\"...real move, new x: %d, %d, a2:%d,%d\\n\", g_mouse_fifo[0].x,\n\t\t\tg_mouse_fifo[0].y, g_mouse_a2_x, g_mouse_a2_y);\n#endif\n\n\tmouse_moved = (g_mouse_fifo[0].x != x) || (g_mouse_fifo[0].y != y);\n\n\tg_mouse_fifo[0].x = x;\n\tg_mouse_fifo[0].y = y;\n\tg_mouse_fifo[0].dfcyc = dfcyc;\n\n\tbutton1_changed = (buttons_valid & 1) &&\n\t\t\t((button_states & 1) != (g_mouse_fifo[0].buttons & 1));\n\n\tif((button_states & 4) && !(g_mouse_fifo[0].buttons & 4) &&\n\t\t\t\t\t\t\t(buttons_valid & 4)) {\n\t\t/* right button pressed */\n\t\tadb_increment_speed();\n\t}\n\tif((button_states & 2) && !(g_mouse_fifo[0].buttons & 2) &&\n\t\t\t\t\t\t\t(buttons_valid & 2)) {\n\t\t/* middle button pressed */\n\t\thalt2_printf(\"Middle button pressed\\n\");\n\t}\n\n\tpos = g_mouse_fifo_pos;\n\tif((pos < (ADB_MOUSE_FIFO - 2)) && button1_changed) {\n\t\t/* copy delta to overflow, set overflow */\n\t\t/* overflow ensures the mouse button state is precise at */\n\t\t/*  button up/down times.  Using a mouse event list where */\n\t\t/*  deltas accumulate until a button change would work, too */\n\t\tfor(i = pos; i >= 0; i--) {\n\t\t\tg_mouse_fifo[i + 1] = g_mouse_fifo[i];\t/* copy struct*/\n\t\t}\n\t\tg_mouse_fifo_pos = pos + 1;\n\t}\n\n\tg_mouse_fifo[0].buttons = (button_states & buttons_valid) |\n\t\t\t\t(g_mouse_fifo[0].buttons & ~buttons_valid);\n\n\tif(mouse_moved || button1_changed) {\n\t\tif( (g_mouse_ctl_addr == g_mouse_dev_addr) &&\n\t\t\t\t\t\t((g_adb_mode & 0x2) == 0)) {\n\t\t\tg_adb_mouse_valid_data = 1;\n\t\t\tadb_add_mouse_int();\n\t\t}\n\t}\n\n\treturn mouse_moved;\n}\n\nint\nmouse_read_c024(dword64 dfcyc)\n{\n\tword32\tret, tool_start;\n\tint\tem_active, target_x, target_y, delta_x, delta_y, a2_x, a2_y;\n\tint\tmouse_button, clamped, pos;\n\n\tif(((g_adb_mode & 0x2) != 0) || (g_mouse_dev_addr != g_mouse_ctl_addr)){\n\t\t/* mouse is off, return 0, or mouse is not autopoll */\n\t\tg_adb_mouse_valid_data = 0;\n\t\tadb_clear_mouse_int();\n\t\treturn 0;\n\t}\n\n\tmouse_compress_fifo(dfcyc);\n\n\tpos = g_mouse_fifo_pos;\n\ttarget_x = g_mouse_fifo[pos].x;\n\ttarget_y = g_mouse_fifo[pos].y;\n\tmouse_button = (g_mouse_fifo[pos].buttons & 1);\n\tdelta_x = target_x - g_mouse_a2_x;\n\tdelta_y = target_y - g_mouse_a2_y;\n\n\tclamped = 0;\n\tif(delta_x > 0x3f) {\n\t\tdelta_x = 0x3f;\n\t\tclamped = 1;\n\t} else if(delta_x < -0x3f) {\n\t\tdelta_x = -0x3f;\n\t\tclamped = 1;\n\t}\n\tif(delta_y > 0x3f) {\n\t\tdelta_y = 0x3f;\n\t\tclamped = 1;\n\t} else if(delta_y < -0x3f) {\n\t\tdelta_y = -0x3f;\n\t\tclamped = 1;\n\t}\n\n\tif(pos > 0) {\n\t\t/* peek into next entry's button info if we are not clamped */\n\t\t/*  and we're returning the y-coord */\n\t\tif(!clamped && g_adb_mouse_coord) {\n\t\t\tmouse_button = g_mouse_fifo[pos - 1].buttons & 1;\n\t\t}\n\t}\n\n\tif(g_adb_mouse_coord) {\n\t\t/* y coord */\n\t\tdelta_x = 0;\t/* clear unneeded x delta */\n\t} else {\n\t\tdelta_y = 0;\t/* clear unneeded y delta */\n\t}\n\n\n\tadb_printf(\" pre a2_x:%02x,%02x,%02x,%02x\\n\",\n\t\tg_slow_memory_ptr[0x100e9], g_slow_memory_ptr[0x100ea],\n\t\tg_slow_memory_ptr[0x100eb], g_slow_memory_ptr[0x100ec]);\n\tadb_printf(\" pre a2_x:%02x,%02x,%02x,%02x\\n\",\n\t\tg_slow_memory_ptr[0x10190], g_slow_memory_ptr[0x10192],\n\t\tg_slow_memory_ptr[0x10191], g_slow_memory_ptr[0x10193]);\n\n\t/* Update event manager internal state */\n\ttool_start = (g_slow_memory_ptr[0x103ca] << 16) +\n\t\t\t(g_slow_memory_ptr[0x103c9] << 8) +\n\t\t\tg_slow_memory_ptr[0x103c8];\n\n\tem_active = 0;\n\tif((tool_start >= 0x20000) && (tool_start < (g_mem_size_total - 28)) ) {\n\t\t/* seems to be valid ptr to addr of mem space for tools */\n\t\t/* see if event manager appears to be active */\n\t\tem_active = g_memory_ptr[tool_start + 6*4] +\n\t\t\t\t(g_memory_ptr[tool_start + 6*4 + 1] << 8);\n\t\tif(g_warp_pointer) {\n\t\t\tem_active = 0;\n\t\t}\n\t}\n\n\t//em_active = 0;\t\t// HACK!\n\ta2_x = g_mouse_a2_x;\n\ta2_y = g_mouse_a2_y;\n\n\tif(em_active) {\n\t\tif((!g_hide_pointer) && (g_num_lines_prev_superhires == 200) &&\n\t\t\t\t!g_unhide_pointer) {\n\t\t\t/* if super-hires and forcing tracking, then hide */\n\t\t\tg_hide_pointer = 1;\n\t\t}\n\t\tif(g_adb_mouse_coord == 0) {\n\t\t\t/* update x coord values */\n\t\t\tg_slow_memory_ptr[0x47c] = a2_x & 0xff;\n\t\t\tg_slow_memory_ptr[0x57c] = a2_x >> 8;\n\t\t\tg_memory_ptr[0x47c] = a2_x & 0xff;\n\t\t\tg_memory_ptr[0x57c] = a2_x >> 8;\n\n\t\t\tg_slow_memory_ptr[0x10190] = a2_x & 0xff;\n\t\t\tg_slow_memory_ptr[0x10192] = a2_x >> 8;\n\t\t} else {\n\t\t\tg_slow_memory_ptr[0x4fc] = a2_y & 0xff;\n\t\t\tg_slow_memory_ptr[0x5fc] = a2_y >> 8;\n\t\t\tg_memory_ptr[0x4fc] = a2_y & 0xff;\n\t\t\tg_memory_ptr[0x5fc] = a2_y >> 8;\n\n\t\t\tg_slow_memory_ptr[0x10191] = a2_y & 0xff;\n\t\t\tg_slow_memory_ptr[0x10193] = a2_y >> 8;\n\t\t}\n\t} else {\n\t\tif(g_hide_pointer && !g_warp_pointer) {\n\t\t\tg_hide_pointer = 0;\n\t\t}\n\t}\n\n\tret = ((!mouse_button) << 7) + ((delta_x | delta_y) & 0x7f);\n\tif(g_adb_mouse_coord) {\n\t\tg_mouse_a2_button = mouse_button;\t/* y coord has button*/\n\t} else {\n\t\tret |= 0x80;\t/* mouse button not down on x coord rd */\n\t}\n\n\ta2_x += delta_x;\n\ta2_y += delta_y;\n\tg_mouse_a2_x = a2_x;\n\tg_mouse_a2_y = a2_y;\n\tif(g_mouse_fifo_pos) {\n\t\tif((target_x == a2_x) && (target_y == a2_y) &&\n\t\t\t\t\t(g_mouse_a2_button == mouse_button)) {\n\t\t\tg_mouse_fifo_pos--;\n\t\t}\n\t}\n\n\n\tadb_printf(\"Rd c024, mouse is_y:%d, %02x, vbl:%08x, dfcyc:%016llx, em:\"\n\t\t\"%d\\n\", g_adb_mouse_coord, ret, g_vbl_count, dfcyc, em_active);\n\tadb_printf(\"...mouse targ_x:%d,%d delta_x,y:%d,%d fifo:%d, a2:%d,%d\\n\",\n\t\ttarget_x, target_y, delta_x, delta_y, g_mouse_fifo_pos,\n\t\ta2_x, a2_y);\n\tadb_printf(\"   post a2_x:%02x,%02x,%02x,%02x\\n\",\n\t\tg_slow_memory_ptr[0x10190], g_slow_memory_ptr[0x10192],\n\t\tg_slow_memory_ptr[0x10191], g_slow_memory_ptr[0x10193]);\n\n\tif((g_mouse_fifo_pos == 0) && (g_mouse_fifo[0].x == a2_x) &&\n\t\t\t(g_mouse_fifo[0].y == a2_y) &&\n\t\t\t((g_mouse_fifo[0].buttons & 1) == g_mouse_a2_button)) {\n\t\tg_adb_mouse_valid_data = 0;\n\t\tadb_clear_mouse_int();\n\t}\n\n\tg_adb_mouse_coord = !g_adb_mouse_coord;\n\treturn ret;\n}\n\nvoid\nmouse_compress_fifo(dword64 dfcyc)\n{\n\tdword64\tddelta;\n\tint\tpos;\n\n\t/* The mouse fifo exists so that fast button changes don't get lost */\n\t/*  if the emulator lags behind the mouse events */\n\t/* But the FIFO means really old mouse events are saved if */\n\t/*  the emulated code isn't looking at the mouse registers */\n\t/* This routine compresses all mouse events > 0.5 seconds old */\n\n\tddelta = (500LL*1000) << 16;\n\tfor(pos = g_mouse_fifo_pos; pos >= 1; pos--) {\n\t\tif((g_mouse_fifo[pos].dfcyc + ddelta) < dfcyc) {\n\t\t\t/* Remove this entry */\n\t\t\tadb_printf(\"Old mouse FIFO pos %d removed\\n\", pos);\n\t\t\tg_mouse_fifo_pos = pos - 1;\n\t\t\tcontinue;\n\t\t}\n\t\t/* Else, stop searching the FIFO */\n\t\tbreak;\n\t}\n}\n\nvoid\nadb_paste_update_state()\n{\n\tint\trd_pos, wr_pos;\n\n\trd_pos = g_kbd_paste_rd_pos;\n\twr_pos = g_kbd_paste_wr_pos;\n\tif(rd_pos >= wr_pos) {\n\t\tg_kbd_paste_rd_pos = 0;\n\t\tg_kbd_paste_wr_pos = 0;\n\t\treturn;\n\t}\n\tif(g_kbd_chars_buffered == 0) {\n\t\tg_kbd_buf[0] = g_kbd_paste_buf[rd_pos];\n\t\tg_kbd_paste_rd_pos = rd_pos + 1;\n\t\tg_kbd_chars_buffered = 1;\n\t}\n}\n\nint\nadb_paste_add_buf(word32 key)\n{\n\tword32\tlast_key;\n\tint\tpos;\n\n\t// Applesoft reads $C000 to check for ctrl-C after each statement.\n\t//  So if we dropped all chars into g_kbd_buf[], we could end up\n\t//  losing chars due to multiple reads of $C000 without writes to $C010\n\t//  causing g_kbd_read_no_update to toss a paste char.\n\t// Instead, have a separate buffer, and when g_kbd_chars_buffered==0,\n\t//  copy one paste char to g_kbd_buf[0].  This also solves a problem\n\t//  where Applesoft is doing: 10 GOTO 10 and it needs to see a Ctrl-C\n\t//  to stop--but a paste buffer is in the way.\n\t// But, now pressing keys while a paste is pending causes those keys\n\t//  to take priority during the paste.\n\tlast_key = g_kbd_paste_last_key;\n\tg_kbd_paste_last_key = key;\n\tif(key == 10) {\t\t\t// \\n, newline on Unix\n\t\tkey = 13;\t\t// \\r, return\n\t\tif(last_key == 13) {\n\t\t\tkey = 0;\t// CR, then LF--eat the LF\n\t\t}\n\t}\n\tif((key == 0) || (key >= 0x80)) {\n\t\treturn 0;\t\t// Just skip these keys\n\t}\n\tpos = g_kbd_paste_wr_pos;\n\tif(pos >= MAX_KBD_PASTE_BUF) {\n\t\treturn 1;\n\t}\n\tg_kbd_paste_buf[pos] = key | 0x80;\n\tg_kbd_paste_wr_pos = pos + 1;\n\n\tadb_paste_update_state();\n\treturn 0;\n}\n\nvoid\nadb_key_event(int a2code, int is_up)\n{\n\tword32\tspecial, vbl_count;\n\tint\tkey, hard_key, pos, tmp_ascii, ascii;\n\n\tif(is_up) {\n\t\tadb_printf(\"adb_key_event, key:%02x, is up, g_key_down: %02x\\n\",\n\t\t\ta2code, g_key_down);\n\t}\n\n\tif(a2code < 0 || a2code > 0x7f) {\n\t\thalt_printf(\"add_key_event: a2code: %04x!\\n\", a2code);\n\t\treturn;\n\t}\n\n\tif(!is_up && a2code == 0x35) {\n\t\t/* ESC pressed, see if ctrl & cmd key down */\n\t\tif(CTRL_DOWN && CMD_DOWN) {\n\t\t\t/* Desk mgr int */\n\t\t\tprintf(\"Desk mgr int!\\n\");\n\n\t\t\tg_adb_interrupt_byte |= 0x20;\n\t\t\tadb_add_data_int();\n\t\t}\n\t}\n\n\t/* convert key to ascii, if possible */\n\thard_key = 0;\n\tif(g_a2_key_to_ascii[a2code][1] & 0xef00) {\n\t\t/* special key */\n\t} else {\n\t\t/* we have ascii */\n\t\thard_key = 1;\n\t}\n\n\tpos = 1;\n\tascii = g_a2_key_to_ascii[a2code][1];\n\tif(CAPS_LOCK_DOWN && (ascii >= 'a') && (ascii <= 'z')) {\n\t\tpos = 2;\n\t\tif(SHIFT_DOWN && (g_adb_mode & 0x40)) {\n\t\t\t/* xor shift mode--capslock and shift == lowercase */\n\t\t\tpos = 1;\n\t\t}\n\t} else if(SHIFT_DOWN) {\n\t\tpos = 2;\n\t}\n\n\tascii = g_a2_key_to_ascii[a2code][pos];\n\tif(CTRL_DOWN) {\n\t\ttmp_ascii = g_a2_key_to_ascii[a2code][3];\n\t\tif(tmp_ascii >= 0) {\n\t\t\tascii = tmp_ascii;\n\t\t}\n\t}\n\tkey = (ascii & 0x7f) + 0x80;\n\n\tspecial = (ascii >> 8) & 0xff;\n\tif(ascii < 0) {\n\t\tprintf(\"ascii1: %d, a2code: %02x, pos: %d\\n\", ascii,a2code,pos);\n\t\tascii = 0;\n\t\tspecial = 0;\n\t}\n\n\tif(!is_up) {\n\t\tif(hard_key) {\n\t\t\tg_kbd_buf[g_kbd_chars_buffered] = key;\n\t\t\tg_kbd_chars_buffered++;\n\t\t\tif(g_kbd_chars_buffered >= MAX_KBD_BUF) {\n\t\t\t\tg_kbd_chars_buffered = MAX_KBD_BUF - 1;\n\t\t\t}\n\t\t\tg_key_down = 1;\n\t\t\tg_a2code_down = a2code;\n\n\t\t\t/* first key down, set up autorepeat */\n\t\t\tvbl_count = g_vbl_count;\n\t\t\tg_adb_repeat_vbl = vbl_count + g_adb_repeat_delay;\n\t\t\tif(g_adb_repeat_delay == 0) {\n\t\t\t\tg_key_down = 0;\n\t\t\t}\n\t\t\tg_hard_key_down = 1;\n\t\t}\n\n\t\tg_c025_val = g_c025_val | special;\n\t\tadb_printf(\"new c025_or: %02x\\n\", g_c025_val);\n\t} else {\n\t\tif(hard_key && (a2code == g_a2code_down)) {\n\t\t\tg_hard_key_down = 0;\n\t\t\t/* Turn off repeat */\n\t\t\tg_key_down = 0;\n\t\t}\n\n\t\tg_c025_val = g_c025_val & (~ special);\n\t\tadb_printf(\"new c025_and: %02x\\n\", g_c025_val);\n\t}\n\n\tif(g_key_down) {\n\t\tg_c025_val = g_c025_val & (~0x20);\n\t} else {\n\t\t/* If no hard key down, set update mod latch */\n\t\tg_c025_val = g_c025_val | 0x20;\n\t}\n\n}\n\nword32\nadb_read_c000()\n{\n\tword32\tvbl_count;\n\n\tif( ((g_kbd_buf[0] & 0x80) == 0) && (g_key_down == 0)) {\n\t\t/* nothing happening, just get out */\n\t\treturn g_kbd_buf[0];\n\t}\n\tif(g_kbd_buf[0] & 0x80) {\n\t\t/* got one */\n\t\tif((g_kbd_read_no_update++ > 5) && (g_kbd_chars_buffered > 1)) {\n\t\t\t/* read 5 times, keys pending, let's move it along */\n\t\t\tprintf(\"Read %02x %d times, tossing\\n\", g_kbd_buf[0],\n\t\t\t\t\tg_kbd_read_no_update);\n\t\t\tadb_access_c010();\n\t\t}\n\t} else {\n\t\tvbl_count = g_vbl_count;\n\t\tif(g_key_down && (vbl_count >= g_adb_repeat_vbl)) {\n\t\t\t/* repeat the g_key_down */\n\t\t\tg_c025_val |= 0x8;\n\t\t\tadb_key_event(g_a2code_down, 0);\n\t\t\tg_adb_repeat_vbl = vbl_count + g_adb_repeat_rate;\n\t\t}\n\t}\n\n\treturn g_kbd_buf[0];\n}\n\nword32\nadb_access_c010()\n{\n\tint\ttmp;\n\tint\ti;\n\n\tg_kbd_read_no_update = 0;\n\n\ttmp = g_kbd_buf[0] & 0x7f;\n\tg_kbd_buf[0] = tmp;\n\n\ttmp = tmp | (g_hard_key_down << 7);\n\tif(g_kbd_chars_buffered) {\n\t\tfor(i = 1; i < g_kbd_chars_buffered; i++) {\n\t\t\tg_kbd_buf[i - 1] = g_kbd_buf[i];\n\t\t}\n\t\tg_kbd_chars_buffered--;\n\t\tif(g_kbd_chars_buffered == 0) {\n\t\t\tadb_paste_update_state();\n\t\t}\n\t}\n\n\tg_c025_val = g_c025_val & (~ (0x08));\n\n\treturn tmp;\n}\n\nword32\nadb_read_c025()\n{\n\treturn\tg_c025_val;\n}\n\nint\nadb_is_cmd_key_down()\n{\n\treturn\tCMD_DOWN;\n}\n\nint\nadb_is_option_key_down()\n{\n\treturn\tOPTION_DOWN;\n}\n\nvoid\nadb_increment_speed()\n{\n\tconst char *str;\n\n\tg_limit_speed++;\n\tif(g_limit_speed > 3) {\n\t\tg_limit_speed = 0;\n\t}\n\n\tstr = \"\";\n\tswitch(g_limit_speed) {\n\tcase 0:\n\t\tstr = \"...as fast as possible!\";\n\t\tbreak;\n\tcase 1:\n\t\tstr = \"...1.024MHz!\";\n\t\tbreak;\n\tcase 2:\n\t\tstr = \"...2.8MHz!\";\n\t\tbreak;\n\tcase 3:\n\t\tstr = \"...8.0MHz!\";\n\t\tbreak;\n\t}\n\tprintf(\"Toggling g_limit_speed to %d%s\\n\", g_limit_speed, str);\n}\n\nvoid\nadb_update_c025_mask(Kimage *kimage_ptr, word32 new_c025_val, word32 mask)\n{\n\t// Called by *driver.c host drivers to handle focus changes and\n\t//  capslock state (so if capslock is on, we leave the window, release\n\t//  capslock, then reenter the window, we update things properly).\n\tif(kimage_ptr == &g_mainwin_kimage) {\n\t\tg_c025_val = (g_c025_val & (~mask)) | new_c025_val;\n\t} else {\n\t\tkimage_ptr->c025_val = (kimage_ptr->c025_val & (~mask)) |\n\t\t\t\t\t\t\t\tnew_c025_val;\n\t}\n}\n\nint\nadb_ascii_to_a2code(int unicode_c, int a2code, int *shift_down_ptr)\n{\n\tint\ti;\n\n\tswitch(unicode_c) {\n\tcase 0x00a3:\t\t// British pound\n\t\tunicode_c = '#';\n\t\tbreak;\n\tcase 0x00e0:\t\t// a with left accent\n\t\tunicode_c = '@';\n\t\tbreak;\n\tcase 0x00b0:\t\t// degrees (French)\n\tcase 0x00c4:\t\t// A with umlaut (German, Swedish)\n\tcase 0x00a1:\t\t// ! upside down (Spanish)\n\tcase 0x00c6:\t\t// AE (Danish)\n\t\tunicode_c = '[';\n\t\tbreak;\n\tcase 0x00e7:\t\t// c with tail (French/Italian)\n\tcase 0x00d1:\t\t// N with ~ (Spanish)\n\tcase 0x00d6:\t\t// O with umlaut (German, Swedish)\n\tcase 0x00d8:\t\t// O with slash (Danish)\n\t\tunicode_c = '\\\\';\n\t\tbreak;\n\tcase 0x00a7:\t\t// ss like thing (French)\n\tcase 0x00dc:\t\t// U with umlaut\n\tcase 0x00bf:\t\t// ? upside down (Spanish)\n\tcase 0x00c5:\t\t// A with circle (Danish)\n\t//case 0x00e9:\t\t// e with right accent (Italian)\n\t\tunicode_c = ']';\n\t\tbreak;\n\t//case 0x0000:\t\t// u with left accent (Italian)\n\t//\tunicode_c = '`';\n\t//\tbreak;\n\tcase 0x00e4:\t\t// a with umlaut (german)\n\tcase 0x00e9:\t\t// e with accent (french)\n\tcase 0x0000:\t\t// ae (Danish)\n\t\tunicode_c = '{';\n\t\tbreak;\n\tcase 0x00f6:\t\t// o with umlaut (German/Swedish)\n\tcase 0x00f9:\t\t// u with left accent (French)\n\tcase 0x00f8:\t\t// o with slash (Danish)\n\tcase 0x00f1:\t\t// n with ~ (Spanish)\n\tcase 0x00f2:\t\t// o with ` (Italian)\n\t\tunicode_c = '|';\n\t\tbreak;\n\tcase 0x00e8:\t\t// e with ` (French, Italian)\n\tcase 0x00fc:\t\t// u with umlaut (German)\n\tcase 0x00e5:\t\t// a with circle (Danish/Swedish)\n\t\tunicode_c = '}';\n\t\tbreak;\n\tcase 0x00a8:\t\t// two high dots (French)\n\tcase 0x00ec:\t\t// i with ` (Italian)\n\tcase 0x00df:\t\t// german B thing (German)\n\t\tunicode_c = '~';\n\t\tbreak;\n\t}\n\n\tif(unicode_c > 0x7f) {\n\t\treturn a2code;\t\t\t// Use a2code instead\n\t}\n\tif((g_a2_key_to_ascii[a2code][1] & 0xf000) == 0x1000) {\t// Keypad\n\t\t// Don't remap keypad keys, we need them for Keypad Joystick\n\t\tif((unicode_c >= '0') && (unicode_c <= '9')) {\n\t\t\treturn a2code;\n\t\t}\n\t}\n\n\tfor(i = 0; i < 128; i++) {\n\t\tif(g_a2_key_to_ascii[i][1] == unicode_c) {\t// Not-shifted\n\t\t\t*shift_down_ptr = 0;\n\t\t\treturn g_a2_key_to_ascii[i][0];\n\t\t}\n\t\tif(g_a2_key_to_ascii[i][2] == unicode_c) {\t// Shifted\n\t\t\t*shift_down_ptr = 1;\n\t\t\treturn g_a2_key_to_ascii[i][0];\n\t\t}\n\t}\n\n\treturn a2code;\t\t// Not found, use default a2code\n}\n\nvoid\nadb_physical_key_update(Kimage *kimage_ptr, int raw_a2code, word32 unicode_c,\n\t\tint is_up)\n{\n\tword32\trestore_c025_val, restorek_c025_val;\n\tint\tspecial, ascii_and_type, ascii, new_shift, a2code, other_a2code;\n\n\t/* this routine called by xdriver to pass raw codes--handle */\n\t/*  ucontroller and ADB bus protocol issues here */\n\t/* if autopoll on, pass it on through to c025,c000 regs */\n\t/*  else only put it in kbd reg 3, and pull SRQ if needed */\n\n\tadb_printf(\"adb_phys_key_update: %02x, %d\\n\", raw_a2code, is_up);\n\n\tif((raw_a2code < 0) || (raw_a2code > 0x7f)) {\n\t\thalt_printf(\"raw_a2code: %04x!\\n\", raw_a2code);\n\t\treturn;\n\t}\n\ta2code = raw_a2code;\n\trestore_c025_val = 0;\n\trestorek_c025_val = 0;\n\tif(unicode_c > 0) {\n\t\t// To enable international keyboards, ignore a2code, look up\n\t\t//  what U.S. keycode would be and return that\n\t\tnew_shift = g_c025_val & 1;\n\t\ta2code = adb_ascii_to_a2code(unicode_c, a2code, &new_shift);\n\t\tif(a2code && ((g_c025_val & 1) != new_shift)) {\n\t\t\trestore_c025_val = g_c025_val | 0x100;\n\t\t\trestorek_c025_val = kimage_ptr->c025_val;\n\t\t\tg_c025_val = (g_c025_val & -2) | new_shift;\n\t\t\tkimage_ptr->c025_val = (kimage_ptr->c025_val & -2) |\n\t\t\t\t\t\t\tnew_shift;\n\t\t}\n\t\tif(!is_up) {\n\t\t\tg_rawa2_to_a2code[raw_a2code & 0x7f] = a2code;\n\t\t}\n\t}\n\n\t/* Remap 0x7b-0x7e to 0x3b-0x3e (arrow keys on new mac keyboards) */\n\tif((a2code >= 0x7b) && (a2code <= 0x7e)) {\n\t\ta2code = a2code - 0x40;\n\t}\n\tif(g_adb_swap_command_option) {\n\t\tif(a2code == 0x37) {\t\t// Command?\n\t\t\ta2code = 0x3a;\t\t//  -> Option\n\t\t} else if(a2code == 0x3a) {\t// Option?\n\t\t\ta2code = 0x37;\t\t//  -> Command\n\t\t}\n\t}\n\n\t/* Now check for special keys (function keys, etc) */\n\tascii_and_type = g_a2_key_to_ascii[a2code][1];\n\tspecial = 0;\n\tif((ascii_and_type & 0xf000) == 0x8000) {\n\t\t/* special function key */\n\t\tspecial = ascii_and_type & 0xff;\n\t\tswitch(special) {\n\t\tcase 0x01: /* F1 - remap to cmd */\n\t\t\ta2code = 0x37;\n\t\t\tspecial = 0;\n\t\t\tbreak;\n\t\tcase 0x02: /* F2 - remap to option */\n\t\t\ta2code = 0x3a;\n\t\t\tspecial = 0;\n\t\t\tbreak;\n\t\tcase 0x03: /* F3 - remap to escape for OS/2 */\n\t\t\ta2code = 0x35;\n\t\t\tspecial = 0;\n\t\t\tbreak;\n\t\tcase 0x0c: /* F12 - remap to reset */\n\t\t\ta2code = 0x7f;\n\t\t\tspecial = 0;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* Only process reset requests here */\n\tif((is_up == 0) && (a2code == 0x7f) && CTRL_DOWN) {\n\t\t/* Reset pressed! */\n\t\tprintf(\"Reset pressed since CTRL_DOWN: %d\\n\", CTRL_DOWN);\n\t\tdo_reset();\n\t\treturn;\n\t}\n\n\tif(special && !is_up) {\n\t\tswitch(special) {\n\t\tcase 0x04: /* F4 - Emulator config panel */\n\t\t\tcfg_toggle_config_panel();\n\t\t\tbreak;\n\t\tcase 0x05: /* F5 - Force Refresh */\n\t\t\tg_status_enable = !g_status_enable;\n\t\t\t// video_update() will call video_update_status_enable()\n\t\t\tbreak;\n\t\tcase 0x06: /* F6 - emulator speed */\n\t\t\tif(SHIFT_DOWN) {\n\t\t\t\thalt2_printf(\"Shift-F6 pressed\\n\");\n\t\t\t} else {\n\t\t\t\tadb_increment_speed();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x07: /* F7 - toggle debugger window, SHIFT:fast disk */\n\t\t\tif(SHIFT_DOWN) {\n\t\t\t\tg_fast_disk_emul_en = !g_fast_disk_emul_en;\n\t\t\t\tiwm_update_fast_disk_emul(g_fast_disk_emul_en);\n\t\t\t\tprintf(\"g_fast_disk_emul_en is now %d\\n\",\n\t\t\t\t\t\t\tg_fast_disk_emul_en);\n\t\t\t} else {\n\t\t\t\tvideo_set_active(&g_debugwin_kimage,\n\t\t\t\t\t\t!g_debugwin_kimage.active);\n\t\t\t\tprintf(\"Toggled debugger window to:%d\\n\",\n\t\t\t\t\t\tg_debugwin_kimage.active);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x08: /* F8 - warp pointer */\n\t\t\tg_warp_pointer = !g_warp_pointer;\n\t\t\tg_hide_pointer = g_warp_pointer;\n\t\t\tprintf(\"New warp:%d, new hide:%d\\n\", g_warp_pointer,\n\t\t\t\t\t\t\t\tg_hide_pointer);\n\t\t\tbreak;\n\t\tcase 0x09: /* F9 - swap paddles */\n\t\t\tif(CTRL_DOWN) {\n\t\t\t\tg_adb_copy_requested = 1;\n\t\t\t} else if(SHIFT_DOWN) {\n\t\t\t\tg_swap_paddles = !g_swap_paddles;\n\t\t\t\tprintf(\"Swap paddles is now: %d\\n\",\n\t\t\t\t\t\t\tg_swap_paddles);\n\t\t\t} else {\n\t\t\t\tg_invert_paddles = !g_invert_paddles;\n\t\t\t\tprintf(\"Invert paddles is now: %d\\n\",\n\t\t\t\t\t\t\tg_invert_paddles);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x0a: /* F10 - nothing */\n\t\t\tbreak;\n\t\tcase 0x0b: /* F11 - full screen */\n\t\t\tbreak;\n\t\t}\n\n\t\treturn;\n\t}\n\n\tif(kimage_ptr == &g_debugwin_kimage) {\n\t\tdebugger_key_event(kimage_ptr, a2code, is_up);\n\t\tif(restore_c025_val) {\n\t\t\tg_c025_val = restore_c025_val & 0xff;\t// Restore shift\n\t\t\tkimage_ptr->c025_val = restorek_c025_val;\n\t\t}\n\t\treturn;\n\t}\n\n\t/* Handle Keypad Joystick here partly...if keypad key pressed */\n\t/*  while in Keypad Joystick mode, do not pass it on as a key press */\n\tif((ascii_and_type & 0xff00) == 0x1000) {\n\t\t/* Keep track of keypad number keys being up or down even */\n\t\t/*  if joystick mode isn't keypad.  This avoid funny cases */\n\t\t/*  if joystick mode is changed while a key is pressed */\n\t\tascii = ascii_and_type & 0xff;\n\t\tif(ascii > 0x30 && ascii <= 0x39) {\n\t\t\tg_keypad_key_is_down[ascii - 0x30] = !is_up;\n\t\t}\n\t\tif(g_joystick_type == 0) {\n\t\t\t/* If Joystick type is keypad, then do not let these */\n\t\t\t/*  keypress pass on further, except for cmd/opt */\n\t\t\tif(ascii == 0x30) {\n\t\t\t\t/* remap '0' to cmd */\n\t\t\t\ta2code = 0x37;\n\t\t\t} else if(ascii == 0x2e || ascii == 0x2c) {\n\t\t\t\t/* remap '.' and ',' to option */\n\t\t\t\ta2code = 0x3a;\n\t\t\t} else {\n\t\t\t\t/* Just ignore it in this mode */\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tadb_maybe_virtual_key_update(a2code, is_up);\n\tother_a2code = g_rawa2_to_a2code[raw_a2code & 0x7f];\n\tif((other_a2code >= 0) && is_up) {\n\t\tadb_maybe_virtual_key_update(other_a2code, is_up);\n\t\tg_rawa2_to_a2code[raw_a2code & 0x7f] = -1;\n\t}\n\n\tif(restore_c025_val) {\n\t\tg_c025_val = restore_c025_val & 0xff;\t\t// Restore shift\n\t}\n}\n\nvoid\nadb_maybe_virtual_key_update(int a2code, int is_up)\n{\n\tint\tautopoll;\n\n\tautopoll = 1;\n\tif(g_adb_mode & 1) {\n\t\t/* autopoll is explicitly off */\n\t\tautopoll = 0;\n\t}\n\tif(g_kbd_dev_addr != g_kbd_ctl_addr) {\n\t\t/* autopoll is off because ucontroller doesn't know kbd moved */\n\t\tautopoll = 0;\n\t}\n\tif(g_config_control_panel) {\n\t\t/* always do autopoll */\n\t\tautopoll = 1;\n\t}\n\n\tif(is_up) {\n\t\tif(!autopoll) {\n\t\t\t/* no auto keys, generate SRQ! */\n\t\t\tadb_kbd_reg0_data(a2code, is_up);\n\t\t} else {\n\t\t\tadb_virtual_key_update(a2code, is_up);\n\t\t}\n\t} else {\n\t\tif(!autopoll) {\n\t\t\t/* no auto keys, generate SRQ! */\n\t\t\tadb_kbd_reg0_data(a2code, is_up);\n\t\t} else {\n\t\t\t/* was up, now down */\n\t\t\tadb_virtual_key_update(a2code, is_up);\n\t\t}\n\t}\n}\n\nvoid\nadb_virtual_key_update(int a2code, int is_up)\n{\n\tword32\tmask;\n\tint\tbitpos;\n\tint\ti;\n\n\tadb_printf(\"Virtual handle a2code: %02x, is_up: %d\\n\", a2code, is_up);\n\n\tif(a2code < 0 || a2code > 0x7f) {\n\t\thalt_printf(\"a2code: %04x!\\n\", a2code);\n\t\treturn;\n\t}\n\n\ti = (a2code >> 5) & 3;\n\tbitpos = a2code & 0x1f;\n\tmask = (1 << bitpos);\n\n\tif(is_up) {\n\t\tif(g_virtual_key_up[i] & mask) {\n\t\t\t/* already up, do nothing */\n\t\t} else {\n\t\t\tg_virtual_key_up[i] |= mask;\n\t\t\tadb_key_event(a2code, is_up);\n\t\t}\n\t} else {\n\t\tif(g_virtual_key_up[i] & mask) {\n\t\t\tg_virtual_key_up[i] &= (~mask);\n\t\t\tadb_key_event(a2code, is_up);\n\t\t}\n\t}\n}\n\n#if 0\nvoid\nadb_all_keys_up()\n{\n\tword32\tmask;\n\tint\ti, j;\n\n\tfor(i = 0; i < 4; i++) {\n\t\tfor(j = 0; j < 32; j++) {\n\t\t\tmask = 1 << j;\n\t\t\tif((g_virtual_key_up[i] & mask) == 0) {\n\t\t\t\t/* create key-up event */\n\t\t\t\tadb_physical_key_update(i*32 + j, 1);\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\nvoid\nadb_kbd_repeat_off()\n{\n\tg_key_down = 0;\n}\n\nvoid\nadb_mainwin_focus(int has_focus)\n{\n\tg_adb_mainwin_has_focus = has_focus;\n\t// printf(\"g_adb_mainwin_has_focus=%d\\n\", g_adb_mainwin_has_focus);\n\tif(!has_focus) {\n\t\tadb_nonmain_check();\n\t}\n}\n"
  },
  {
    "path": "gsplus/src/applesingle.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2021 Kent Dickey                      */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// From Wikipedia AppleSingle_and_AppleDouble_formats):\n// https://web.archive.org/web/20180311140826/http://kaiser-edv.de/\n//\t\tdocuments/AppleSingle_AppleDouble.pdf\n// All fields in an Applesingle file are in big-endian format\n// ProDOS forked files are described in Technote tn-pdos-025.\n\n#include \"defc.h\"\n\nword32\napplesingle_get_be32(const byte *bptr)\n{\n\treturn (bptr[0] << 24) | (bptr[1] << 16) | (bptr[2] << 8) | bptr[3];\n}\n\nword32\napplesingle_get_be16(const byte *bptr)\n{\n\treturn (bptr[0] << 8) | bptr[1];\n}\n\nvoid\napplesingle_set_be32(byte *bptr, word32 val)\n{\n\tbptr[3] = val;\n\tbptr[2] = val >> 8;\n\tbptr[1] = val >> 16;\n\tbptr[0] = val >> 24;\n}\n\nvoid\napplesingle_set_be16(byte *bptr, word32 val)\n{\n\tbptr[1] = val;\n\tbptr[0] = val >> 8;\n}\n\nword32\napplesingle_map_from_prodos(Disk *dsk, Dynapro_file *fileptr, int do_file_data)\n{\n\tbyte\t*fptr, *bptr;\n\tword32\tdata_size, resource_size, rounded_data_size, num_entries;\n\tword32\trounded_resource_size, hdr_size, max_size, hdr_pos, data_pos;\n\tword32\tblock, key_block, ret, good, has_finder_info, offset;\n\tint\tlevel;\n\tint\ti, j;\n\n#if 0\n\tprintf(\"applesingle_map_from_prodos: %s do_file_data:%d\\n\",\n\t\t\t\t\tfileptr->unix_path, do_file_data);\n#endif\n\n\t// First, handle mini directory describing the forks\n\tkey_block = fileptr->key_block;\n\tret = dynapro_map_one_file_block(dsk, fileptr, key_block, 1U << 30, 0);\n\tif(ret == 0) {\n\t\tprintf(\" dynapro_map_one_file_block ret 0, applesingle done\\n\");\n\t\treturn 0;\n\t}\n\tbptr = &(dsk->raw_data[key_block << 9]);\n\tdata_size = dynapro_get_word24(&bptr[5]);\n\tresource_size = dynapro_get_word24(&bptr[0x100 + 5]);\n\thas_finder_info = bptr[9] | bptr[27];\n\n\tnum_entries = 1;\t\t// ProDOS info always\n\tif(has_finder_info) {\n\t\tnum_entries++;\n\t}\n\trounded_data_size = data_size;\n\tif(data_size) {\n\t\trounded_data_size = (data_size + 0x200) & -0x200;\n\t\tnum_entries++;\n\t}\n\trounded_resource_size = resource_size;\n\tif(resource_size) {\n\t\trounded_resource_size = (resource_size + 0x200) & -0x200;\n\t\tnum_entries++;\n\t}\n\thdr_size = 256;\n\tmax_size = hdr_size + rounded_resource_size + rounded_data_size;\n\tfileptr->buffer_ptr = 0;\n\tfptr = 0;\n\tif(do_file_data) {\n\t\tfptr = calloc(1, max_size + 0x200);\n#if 0\n\t\tprintf(\" fptr:%p, max_size:%08x, res:%08x, data:%08x\\n\",\n\t\t\tfptr, max_size, rounded_resource_size,\n\t\t\trounded_data_size);\n#endif\n\t}\n\n\t// From now on, errors cannot return without free'ing fptr\n\tgood = 1;\n\tif(resource_size) {\n\t\tblock = dynapro_get_word16(&bptr[0x100 + 1]);\n\t\tlevel = bptr[0x100];\n\t\tif(fptr) {\n\t\t\tfileptr->buffer_ptr = fptr + 256;\n\t\t}\n\t\tret = dynapro_map_file_blocks(dsk, fileptr, block, level, 0,\n\t\t\t\t\t\t\t\tresource_size);\n\t\tif(ret == 0) {\n\t\t\tgood = 0;\n\t\t}\n\t}\n\n\tif(data_size) {\n\t\tblock = dynapro_get_word16(&bptr[1]);\n\t\tlevel = bptr[0];\n\t\tif(fptr) {\n\t\t\tfileptr->buffer_ptr = fptr + 256 +\n\t\t\t\t\t\t\trounded_resource_size;\n\t\t}\n\t\tret = dynapro_map_file_blocks(dsk, fileptr, block, level, 0,\n\t\t\t\t\t\t\t\tdata_size);\n\t\tif(ret == 0) {\n\t\t\tgood = 0;\n\t\t}\n\t}\n\n\tfileptr->buffer_ptr = 0;\n\n\t// Now prepare the header\n\tif(fptr) {\n\t\tapplesingle_set_be32(&fptr[0], 0x00051600);\t// Magic\n\t\tapplesingle_set_be32(&fptr[4], 0x00020000);\t// Version\n\t\tapplesingle_set_be16(&fptr[24], num_entries);\t// Version\n\t\thdr_pos = 26;\n\t\tdata_pos = 192;\n\n\t\t// First do ProDOS entry\n\t\tapplesingle_set_be32(&fptr[hdr_pos + 0], 11);\t// ProDOS Info\n\t\tapplesingle_set_be32(&fptr[hdr_pos + 4], data_pos);\n\t\tapplesingle_set_be32(&fptr[hdr_pos + 8], 8);\n\n\t\tapplesingle_set_be16(&fptr[data_pos + 0], 0xc3);\n\t\tapplesingle_set_be16(&fptr[data_pos + 2], fileptr->file_type);\n\t\tapplesingle_set_be32(&fptr[data_pos + 4], fileptr->aux_type);\n\t\thdr_pos += 12;\n\t\tdata_pos += 8;\n\n\t\t// Then do FinderInfo\n\t\tif(has_finder_info) {\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 0], 9);\t//Finder\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 4], data_pos);\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 8], 32);\n\n\t\t\tfor(i = 0; i < 2; i++) {\n\t\t\t\toffset = bptr[9 + 18*i];\n\t\t\t\tif(!offset) {\n\t\t\t\t\tcontinue;\t\t// skip it\n\t\t\t\t}\n\t\t\t\toffset = ((offset - 1) & 1) * 8;\n\t\t\t\tfor(j = 0; j < 9; j++) {\n\t\t\t\t\tfptr[data_pos + offset + j] =\n\t\t\t\t\t\tbptr[10 + 18*i + j];\n\t\t\t\t}\n\t\t\t}\n\t\t\thdr_pos += 12;\n\t\t\tdata_pos += 32;\n\t\t}\n\n\t\tif(data_pos >= 256) {\n\t\t\tprintf(\"data_pos:%08x is too big\\n\", data_pos);\n\t\t\tgood = 0;\n\t\t}\n\n\t\t// First, do data fork\n\t\tif(data_size) {\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 0], 1);\t// Data\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 4],\n\t\t\t\t\t\t256 + rounded_resource_size);\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 8], data_size);\n\t\t\thdr_pos += 12;\n\t\t}\n\t\t// Then do resource fork\n\t\tif(resource_size) {\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 0], 2);\t// Rsrc\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 4], 256);\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 8], resource_size);\n\t\t\thdr_pos += 12;\n\t\t}\n\t\tif(hdr_pos > 192) {\n\t\t\tprintf(\"hdr:%08x stomped on data\\n\", hdr_pos);\n\t\t\tgood = 0;\n\t\t}\n\t\tif(good) {\n\t\t\tret = dynapro_write_to_unix_file(fileptr->unix_path,\n\t\t\t\tfptr, 256 + rounded_resource_size + data_size);\n\t\t\tif(ret == 0) {\n\t\t\t\tgood = 0;\n\t\t\t}\n\t\t}\n\n\t\tfree(fptr);\n\t}\n\n\t// printf(\"applesingle_map_from_prodos done, good:%d\\n\", good);\n\treturn good;\n}\n\nword32\napplesingle_from_unix(Disk *dsk, Dynapro_file *fileptr, byte *fptr,\n\t\t\t\t\t\t\t\tdword64 dsize)\n{\n\tbyte\t*bptr, *tptr;\n\tword32\tkey_block, blocks_used, entry_id, blocks_out, offset, length;\n\tword32\tmagic, version, hdr_pos, did_fork;\n\tint\tnum_entries;\n\tint\ti;\n\n\t// Return 0 if anything is wrong with the .applesingle file\n\t// Otherwise, return (blocks_used << 16) | (key_block & 0xffff)\n\n#if 0\n\tprintf(\"applesingle_from_unix %s, size:%08llx\\n\", fileptr->unix_path,\n\t\t\t\t\t\t\t\tdsize);\n#endif\n\n\tkey_block = fileptr->key_block;\n\tbptr = &(dsk->raw_data[key_block << 9]);\n\n\tif(dsize < 50) {\n\t\tprintf(\"Applesingle is too small\\n\");\n\t\treturn 0;\n\t}\n\tmagic = applesingle_get_be32(&fptr[0]);\n\tversion = applesingle_get_be32(&fptr[4]);\n\tnum_entries = applesingle_get_be16(&fptr[24]);\n\tif((magic != 0x00051600) || (version < 0x00020000)) {\n\t\tprintf(\"Bad Applesingle magic number is: %08x, vers:%08x\\n\",\n\t\t\t\t\tmagic, version);\n\t\treturn 0;\n\t}\n\thdr_pos = 26;\n\tblocks_used = 1;\n\tdid_fork = 0;\n\t// printf(\" num_entries:%d\\n\", num_entries);\n\tfor(i = 0; i < num_entries; i++) {\n\t\tif((hdr_pos + 24) > dsize) {\n\t\t\tprintf(\"Applesingle header exceeds file size i:%d of \"\n\t\t\t\t\"%d, hdr_pos:%04x dsize:%08llx\\n\", i,\n\t\t\t\tnum_entries, hdr_pos, dsize);\n\t\t\treturn 0;\n\t\t}\n\t\tentry_id = applesingle_get_be32(&fptr[hdr_pos + 0]);\n\t\toffset = applesingle_get_be32(&fptr[hdr_pos + 4]);\n\t\tlength = applesingle_get_be32(&fptr[hdr_pos + 8]);\n#if 0\n\t\tprintf(\" header[%d] at +%04x: id:%d, offset:%08x, len:%08x\\n\",\n\t\t\ti, hdr_pos, entry_id, offset, length);\n#endif\n\t\tif((offset + length) > dsize) {\n\t\t\tprintf(\"Applesingle entry_id:%d exceeds file size\\n\",\n\t\t\t\tentry_id);\n\t\t\treturn 0;\n\t\t}\n\t\tswitch(entry_id) {\n\t\tcase 1:\t\t// Data fork\n\t\tcase 2:\t\t// Resource fork\n\t\t\ttptr = bptr;\n\t\t\tif(entry_id == 2) {\t\t// Resource fork\n\t\t\t\ttptr += 0x100;\n\t\t\t}\n#if 0\n\t\t\tprintf(\" for entry_id %d, offset:%08x, length:%08x, \"\n\t\t\t\t\"fptr:%p\\n\", entry_id, offset, length, fptr);\n#endif\n\t\t\tif(did_fork & (1 << entry_id)) {\n\t\t\t\tprintf(\"fork %d repeated!\\n\", entry_id);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tdid_fork |= (1 << entry_id);\n\t\t\tblocks_out = applesingle_make_prodos_fork(dsk,\n\t\t\t\t\t\tfptr + offset, tptr, length);\n\t\t\tif(blocks_out == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tblocks_used += (blocks_out >> 16);\n\t\t\tbreak;\n\t\tcase 9:\t\t// Finder Info\n\t\t\tif(length < 32) {\n\t\t\t\tprintf(\"Invalid Finder info, len:%d\\n\", length);\n\t\t\t}\n\t\t\tbptr[8] = 0x12;\n\t\t\tbptr[8 + 18] = 0x12;\n\t\t\tbptr[9] = 1;\n\t\t\tbptr[9 + 18] = 2;\n\t\t\tfor(i = 0; i < 16; i++) {\n\t\t\t\tbptr[10 + i] = fptr[offset + i];\n\t\t\t\tbptr[10 + 18 + i] = fptr[offset + 16 + i];\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 11:\t// ProDOS File Info\n\t\t\tfileptr->file_type = fptr[offset + 3];\n\t\t\tfileptr->aux_type = (fptr[offset + 6] << 8) |\n\t\t\t\t\t\t\tfptr[offset + 7];\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\t\t// Ignore it\n\t\t}\n\t\thdr_pos += 12;\n\t}\n\n\tfor(i = 1; i < 3; i++) {\n\t\tif((did_fork >> i) & 1) {\n\t\t\tcontinue;\n\t\t}\n\t\t// Create one block for this fork even though it's length==0\n\t\t// i==1: no data fork; i==2: no resource fork\n\t\tprintf(\" Doing dummy fork, i:%d, fptr:%p\\n\", i, fptr);\n\t\tblocks_out = applesingle_make_prodos_fork(dsk, fptr,\n\t\t\t\t\t\tbptr + ((i & 2) * 0x80), 0);\n\t\tif(blocks_out == 0) {\n\t\t\treturn blocks_out;\n\t\t}\n\t\tblocks_used += (blocks_out >> 16);\n\t}\n\n\tfileptr->eof = 0x200;\n\treturn (blocks_used << 16) | key_block;\n}\n\nword32\napplesingle_make_prodos_fork(Disk *dsk, byte *fptr, byte *tptr, word32 length)\n{\n\tword32\tblock, blocks_out, storage_type;\n\n#if 0\n\tprintf(\"applesingle_make_prodos_fork: fptr:%p, tptr:%p, length:%08x\\n\",\n\t\t\tfptr, tptr, length);\n#endif\n\n\t// Handle creating either a resource or data fork\n\tblock = dynapro_find_free_block(dsk);\n\tif(block == 0) {\n\t\treturn 0;\n\t}\n\tblocks_out = dynapro_fork_from_unix(dsk, fptr, &storage_type, block,\n\t\t\t\t\t\t\t\tlength);\n\n\t// printf(\" dynapro_fork_from_unix ret: %08x, storage:%02x\\n\",\n\t//\t\t\t\t\tblocks_out, storage_type);\n\ttptr[0] = storage_type >> 4;\n\ttptr[1] = blocks_out & 0xff;\t\t// key_block lo\n\ttptr[2] = (blocks_out >> 8) & 0xff;\t// key_block hi\n\ttptr[3] = (blocks_out >> 16) & 0xff;\t// blocks_used lo\n\ttptr[4] = (blocks_out >> 24) & 0xff;\t// blocks_used hi\n\ttptr[5] = length & 0xff;\t\t// eof lo\n\ttptr[6] = (length >> 8) & 0xff;\t\t// eof mid\n\ttptr[7] = (length >> 16) & 0xff;\t// eof hi\n\treturn blocks_out;\n}\n"
  },
  {
    "path": "gsplus/src/clock.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2022 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include \"defc.h\"\n#include <time.h>\n#ifdef _WIN32\n# include <windows.h>\n# include <mmsystem.h>\n#else\n# include <sys/time.h>\n#endif\n\nextern int Verbose;\nextern word32 g_vbl_count;\nextern int g_rom_version;\nextern int g_config_kegs_update_needed;\n\n#define CLK_IDLE\t\t1\n#define CLK_TIME\t\t2\n#define CLK_INTERNAL\t\t3\n#define CLK_BRAM1\t\t4\n#define CLK_BRAM2\t\t5\n\nint\tg_clk_mode = CLK_IDLE;\nint\tg_clk_read = 0;\nint\tg_clk_reg1 = 0;\n\nextern word32 g_c033_data;\nextern word32 g_c034_val;\n\nbyte\tg_bram[2][256];\nbyte\t*g_bram_ptr = &(g_bram[0][0]);\n\nword32 g_clk_cur_time = 0xa0000000;\nint\tg_clk_next_vbl_update = 0;\n\ndouble\nget_dtime()\n{\n\n#ifdef _WIN32\n\tFILETIME filetime;\n\tdword64\tdlow, dhigh;\n#else\n\tstruct timeval tp1;\n\tdouble\tdsec;\n\tdouble\tdusec;\n#endif\n\tdouble\tdtime;\n\n\t/* Routine used to return actual system time as a double */\n\t/* No routine cares about the absolute value, only deltas--maybe */\n\t/*  take advantage of that in future to increase usec accuracy */\n\n#ifdef _WIN32\n\t//dtime = timeGetTime() / 1000.0;\n\tGetSystemTimePreciseAsFileTime(&filetime);\n\tdlow = filetime.dwLowDateTime;\n\tdhigh = filetime.dwHighDateTime;\n\tdlow = (dhigh << 32) | dlow;\n\tdtime = (double)dlow;\n\tdtime = dtime / (1000*1000*10.0);\t// FILETIME is in 100ns incs\n#else\n\n# ifdef SOLARIS\n\tgettimeofday(&tp1, (void *)0);\n# else\n\tgettimeofday(&tp1, (struct timezone *)0);\n# endif\n\n\tdsec = (double)tp1.tv_sec;\n\tdusec = (double)tp1.tv_usec;\n\n\tdtime = dsec + (dusec / (1000.0 * 1000.0));\n#endif\n\n\treturn dtime;\n}\n\nint\nmicro_sleep(double dtime)\n{\n#ifndef _WIN32\n\tstruct timeval Timer;\n\tint\tret;\n#endif\n\n\tif(dtime <= 0.0) {\n\t\treturn 0;\n\t}\n\tif(dtime >= 1.0) {\n\t\thalt_printf(\"micro_sleep called with %f!!\\n\", dtime);\n\t\treturn -1;\n\t}\n\n#if 0\n\tprintf(\"usleep: %f\\n\", dtime);\n#endif\n\n#ifdef _WIN32\n\tSleep((word32)(dtime * 1000));\n#else\n\tTimer.tv_sec = 0;\n\tTimer.tv_usec = (dtime * 1000000.0);\n\tif( (ret = select(0, 0, 0, 0, &Timer)) < 0) {\n\t\tfprintf(stderr, \"micro_sleep (select) ret: %d, errno: %d\\n\",\n\t\t\tret, errno);\n\t\treturn -1;\n\t}\n#endif\n\treturn 0;\n}\n\nvoid\nclk_bram_zero()\n{\n\tint\ti, j;\n\n\t/* zero out all bram */\n\tfor(i = 0; i < 2; i++) {\n\t\tfor(j = 0; j < 256; j++) {\n\t\t\tg_bram[i][j] = 0;\n\t\t}\n\t}\n\tg_bram_ptr = &(g_bram[0][0]);\n}\n\nvoid\nclk_bram_set(int bram_num, int offset, int val)\n{\n\tif((bram_num < 0) || (bram_num > 2)) {\n\t\tprintf(\"bram_num %d out of range\\n\", bram_num);\n\t\treturn;\n\t}\n\tif((offset < 0) || (offset > 0x100)) {\n\t\tprintf(\"bram offset %05x out of range\\n\", offset);\n\t\treturn;\n\t}\n\tg_bram[bram_num][offset] = val;\n}\n\nvoid\nclk_setup_bram_version()\n{\n\tif(g_rom_version < 3) {\n\t\tg_bram_ptr = (&g_bram[0][0]);\t// ROM 01\n\t} else {\n\t\tg_bram_ptr = (&g_bram[1][0]);\t// ROM 03\n\t}\n}\n\nvoid\nclk_write_bram(FILE *fconf)\n{\n\tint\ti, j, k;\n\n\tfor(i = 0; i < 2; i++) {\n\t\tfprintf(fconf, \"\\n\");\n\t\tfor(j = 0; j < 256; j += 16) {\n\t\t\tfprintf(fconf, \"bram%d[%02x] =\", 2*i + 1, j);\n\t\t\tfor(k = 0; k < 16; k++) {\n\t\t\t\tfprintf(fconf, \" %02x\", g_bram[i][j+k]);\n\t\t\t}\n\t\t\tfprintf(fconf, \"\\n\");\n\t\t}\n\t}\n}\n\nvoid\nupdate_cur_time()\n{\n\tstruct tm *tm_ptr;\n\ttime_t\tcur_time, secs, secs2;\n\n\tcur_time = time(0);\n\n\t/* Figure out the timezone (effectively) by diffing two times. */\n\t/* this is probably not right for a few hours around daylight savings*/\n\t/*  time transition */\n\tsecs2 = mktime(gmtime(&cur_time));\n\ttm_ptr = localtime(&cur_time);\n\tsecs = mktime(tm_ptr);\n\n\tsecs2 = secs2 - secs;\t\t// this is the timezone offset\n#ifdef MAC\n\t/* Mac OS X's mktime function modifies the tm_ptr passed in for */\n\t/*  the CDT timezone and breaks this algorithm.  So on a Mac, we */\n\t/*  will use the tm_ptr->gmtoff member to correct the time */\n\tsecs = secs + tm_ptr->tm_gmtoff;\n#else\n\tsecs = cur_time - secs2;\n\n\tif(tm_ptr->tm_isdst) {\n\t\t/* adjust for daylight savings time */\n\t\tsecs += 3600;\n\t}\n#endif\n\n\t/* add in secs to make date based on Apple Jan 1, 1904 instead of */\n\t/*  Unix's Jan 1, 1970 */\n\t/* So add in 66 years and 17 leap year days (1904 is a leap year) */\n\tsecs += ((66*365) + 17) * (24*3600);\n\n\tg_clk_cur_time = (word32)secs;\n\n\tclk_printf(\"Update g_clk_cur_time to %08x\\n\", g_clk_cur_time);\n\tg_clk_next_vbl_update = g_vbl_count + 5;\n}\n\n/* clock_update called by sim65816 every VBL */\nvoid\nclock_update()\n{\n\t/* Nothing to do */\n}\n\nvoid\nclock_update_if_needed()\n{\n\tint\tdiff;\n\n\tdiff = g_clk_next_vbl_update - g_vbl_count;\n\tif(diff < 0 || diff > 60) {\n\t\t/* Been a while, re-read the clock */\n\t\tupdate_cur_time();\n\t}\n}\n\nvoid\nclock_write_c034(word32 val)\n{\n\tg_c034_val = val & 0x7f;\n\tif((val & 0x80) != 0) {\n\t\tif((val & 0x20) == 0) {\n\t\t\tprintf(\"c034 write not last = 1\\n\");\n\t\t\t/* set_halt(1); */\n\t\t}\n\t\tdo_clock_data();\n\t}\n}\n\n\nvoid\ndo_clock_data()\n{\n\tword32\tmask, read, op;\n\n\tclk_printf(\"In do_clock_data, g_clk_mode: %02x\\n\", g_clk_mode);\n\n\tread = g_c034_val & 0x40;\n\tswitch(g_clk_mode) {\n\tcase CLK_IDLE:\n\t\tg_clk_read = (g_c033_data >> 7) & 1;\n\t\tg_clk_reg1 = (g_c033_data >> 2) & 3;\n\t\top = (g_c033_data >> 4) & 7;\n\t\tif(!read) {\n\t\t\t/* write */\n\t\t\tswitch(op) {\n\t\t\tcase 0x0:\t/* Read/write seconds register */\n\t\t\t\tg_clk_mode = CLK_TIME;\n\t\t\t\tclock_update_if_needed();\n\t\t\t\tbreak;\n\t\t\tcase 0x3:\t/* internal registers */\n\t\t\t\tg_clk_mode = CLK_INTERNAL;\n\t\t\t\tif(g_clk_reg1 & 0x2) {\n\t\t\t\t\t/* extend BRAM read */\n\t\t\t\t\tg_clk_mode = CLK_BRAM2;\n\t\t\t\t\tg_clk_reg1 = (g_c033_data & 7) << 5;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 0x2:\t/* read/write ram 0x10-0x13 */\n\t\t\t\tg_clk_mode = CLK_BRAM1;\n\t\t\t\tg_clk_reg1 += 0x10;\n\t\t\t\tbreak;\n\t\t\tcase 0x4:\t/* read/write ram 0x00-0x0f */\n\t\t\tcase 0x5: case 0x6: case 0x7:\n\t\t\t\tg_clk_mode = CLK_BRAM1;\n\t\t\t\tg_clk_reg1 = (g_c033_data >> 2) & 0xf;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\thalt_printf(\"Bad c033_data in CLK_IDLE: %02x\\n\",\n\t\t\t\t\tg_c033_data);\n\t\t\t}\n\t\t} else {\n\t\t\tprintf(\"clk read from IDLE mode!\\n\");\n\t\t\t/* set_halt(1); */\n\t\t\tg_clk_mode = CLK_IDLE;\n\t\t}\n\t\tbreak;\n\tcase CLK_BRAM2:\n\t\tif(!read) {\n\t\t\t/* get more bits of bram addr */\n\t\t\tif((g_c033_data & 0x83) == 0x00) {\n\t\t\t\t/* more address bits */\n\t\t\t\tg_clk_reg1 |= ((g_c033_data >> 2) & 0x1f);\n\t\t\t\tg_clk_mode = CLK_BRAM1;\n\t\t\t} else {\n\t\t\t\thalt_printf(\"CLK_BRAM2: c033_data: %02x!\\n\",\n\t\t\t\t\t\tg_c033_data);\n\t\t\t\tg_clk_mode = CLK_IDLE;\n\t\t\t}\n\t\t} else {\n\t\t\thalt_printf(\"CLK_BRAM2: clock read!\\n\");\n\t\t\tg_clk_mode = CLK_IDLE;\n\t\t}\n\t\tbreak;\n\tcase CLK_BRAM1:\n\t\t/* access battery ram addr g_clk_reg1 */\n\t\tif(read) {\n\t\t\tif(g_clk_read) {\n\t\t\t\t/* Yup, read */\n\t\t\t\tg_c033_data = g_bram_ptr[g_clk_reg1];\n\t\t\t\tclk_printf(\"Reading BRAM loc %02x: %02x\\n\",\n\t\t\t\t\tg_clk_reg1, g_c033_data);\n\t\t\t} else {\n\t\t\t\thalt_printf(\"CLK_BRAM1: said wr, now read\\n\");\n\t\t\t}\n\t\t} else {\n\t\t\tif(g_clk_read) {\n\t\t\t\thalt_printf(\"CLK_BRAM1: said rd, now write\\n\");\n\t\t\t} else {\n\t\t\t\t/* Yup, write */\n\t\t\t\tclk_printf(\"Writing BRAM loc %02x with %02x\\n\",\n\t\t\t\t\tg_clk_reg1, g_c033_data);\n\t\t\t\tg_bram_ptr[g_clk_reg1] = g_c033_data;\n\t\t\t\tg_config_kegs_update_needed = 1;\n\t\t\t}\n\t\t}\n\t\tg_clk_mode = CLK_IDLE;\n\t\tbreak;\n\tcase CLK_TIME:\n\t\tif(read) {\n\t\t\tif(g_clk_read == 0) {\n\t\t\t\thalt_printf(\"Reading time, but in set mode!\\n\");\n\t\t\t}\n\t\t\tg_c033_data = (g_clk_cur_time >> (g_clk_reg1 * 8)) &\n\t\t\t\t\t\t\t\t\t0xff;\n\t\t\tclk_printf(\"Returning time byte %d: %02x\\n\",\n\t\t\t\tg_clk_reg1, g_c033_data);\n\t\t} else {\n\t\t\t/* Write */\n\t\t\tif(g_clk_read) {\n\t\t\t\thalt_printf(\"Write time, but in read mode!\\n\");\n\t\t\t}\n\t\t\tclk_printf(\"Writing TIME loc %d with %02x\\n\",\n\t\t\t\tg_clk_reg1, g_c033_data);\n\t\t\tmask = 0xff << (8 * g_clk_reg1);\n\n\t\t\tg_clk_cur_time = (g_clk_cur_time & (~mask)) |\n\t\t\t\t((g_c033_data & 0xff) << (8 * g_clk_reg1));\n\t\t}\n\t\tg_clk_mode = CLK_IDLE;\n\t\tbreak;\n\tcase CLK_INTERNAL:\n\t\tif(read) {\n\t\t\tprintf(\"Attempting to read internal reg %02x!\\n\",\n\t\t\t\tg_clk_reg1);\n\t\t} else {\n\t\t\tswitch(g_clk_reg1) {\n\t\t\tcase 0x0:\t/* test register */\n\t\t\t\tif(g_c033_data & 0xc0) {\n\t\t\t\t\tprintf(\"Writing test reg: %02x!\\n\",\n\t\t\t\t\t\tg_c033_data);\n\t\t\t\t\t/* set_halt(1); */\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 0x1:\t/* write protect reg */\n\t\t\t\tclk_printf(\"Writing clk wr_protect: %02x\\n\",\n\t\t\t\t\tg_c033_data);\n\t\t\t\tif(g_c033_data & 0x80) {\n\t\t\t\t\tprintf(\"Stop, wr clk wr_prot: %02x\\n\",\n\t\t\t\t\t\tg_c033_data);\n\t\t\t\t\t/* set_halt(1); */\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\thalt_printf(\"Writing int reg: %02x with %02x\\n\",\n\t\t\t\t\tg_clk_reg1, g_c033_data);\n\t\t\t}\n\t\t}\n\t\tg_clk_mode = CLK_IDLE;\n\t\tbreak;\n\tdefault:\n\t\thalt_printf(\"clk mode: %d unknown!\\n\", g_clk_mode);\n\t\tg_clk_mode = CLK_IDLE;\n\t\tbreak;\n\t}\n}\n\n"
  },
  {
    "path": "gsplus/src/comp_swift",
    "content": "#!/bin/bash\n# $KmKId: comp_swift,v 1.2 2020-12-11 22:58:32+00 kentd Exp $\n\necho \"args are: \" \"$@\"\n/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -c \\\n\t-enable-objc-interop \\\n\t-sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \\\n\t-swift-version 4 -Onone \\\n\t-serialize-debugging-options \\\n\t-import-objc-header Kegs-Bridging-Header.h \\\n\t-module-name Kegs \\\n\t\"$@\"\n\n"
  },
  {
    "path": "gsplus/src/compile_time.c",
    "content": "char g_compile_time[] = \"Compiled: \" __DATE__ \" \" __TIME__ ;\n\n"
  },
  {
    "path": "gsplus/src/config.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2025 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// g_cfg_slotdrive: 0: not doing file selection at all\n//\t\t\t1-0x7ff: doing file selection for given slot/drive\n//\t\t\t0xfff: doing file selection for ROM or charrom\n#include \"defc.h\"\n#include <stdarg.h>\n#include \"config.h\"\n\n#ifdef _WIN32\n# include \"win_dirent.h\"\n#else\n# include <dirent.h>\n#endif\n\nextern int Verbose;\nextern word32 g_vbl_count;\n\nextern int g_track_bytes_35[];\nextern int g_c031_disk35;\n\nextern int g_cur_a2_stat;\nextern byte *g_slow_memory_ptr;\nextern byte *g_rom_fc_ff_ptr;\nextern byte *g_rom_cards_ptr;\nextern double g_cur_dcycs;\nextern int g_rom_version;\n\nextern word32 g_adb_repeat_vbl;\nextern int g_adb_swap_command_option;\n\nextern int g_limit_speed;\nextern int g_zip_speed_mhz;\nextern int g_force_depth;\nint g_serial_cfg[2] = { 0, 1 };\t\t// Slot 1=0=Real serial (printer?)\n\t\t\t\t\t// Slot 2=1=Virt Modem\nint g_serial_mask[2] = { 0, 0 };\nchar *g_serial_remote_ip[2] = { \"\", \"\" };\t// cfg_init_menus will malloc()\nint g_serial_remote_port[2] = { 9100, 9100 };\nchar *g_serial_device[2] = { \"/dev/tty.USB.0\", \"/dev/tty.USB.1\" };\n\t\t\t\t// cfg_init_menus() will malloc() the above\nint g_serial_win_device[2] = { 0, 0 };\t\t// Disabled\nint g_serial_modem_response_code = 10;\t\t// 10 - 2400\nint g_serial_modem_allow_incoming = 0;\t\t// 1 for BBS'es\nint g_serial_modem_init_telnet = 1;\t\t// 1 for BBS'es\n\nextern word32 g_mem_size_base;\nextern word32 g_mem_size_exp;\nextern int g_video_line_update_interval;\nextern int g_user_halt_bad;\nextern int g_joystick_type;\nextern int g_joystick_scale_factor_x;\nextern int g_joystick_scale_factor_y;\nextern int g_joystick_trim_amount_x;\nextern int g_joystick_trim_amount_y;\nextern int g_swap_paddles;\nextern int g_invert_paddles;\nextern int g_voc_enable;\nextern int g_status_enable;\nextern int g_mainwin_width;\nextern int g_mainwin_height;\nextern int g_mainwin_xpos;\nextern int g_mainwin_ypos;\n\nextern int g_screen_index[];\nextern word32 g_full_refresh_needed;\nextern word32 g_a2_screen_buffer_changed;\n\nextern int g_key_down;\nextern const char g_kegs_version_str[];\n\nint g_config_control_panel = 0;\nchar g_config_kegs_name[1024] = { 0 };\nchar g_cfg_cwd_str[CFG_PATH_MAX] = { 0 };\n\nint g_config_kegs_auto_update = 1;\nint g_config_kegs_update_needed = 0;\nint g_cfg_newdisk_select = 0;\nint g_cfg_newdisk_blocks = 0;\nint g_cfg_newdisk_blocks_default = 140*2;\nint g_cfg_newdisk_type = 1;\nint g_cfg_newdisk_type_default = 1;\nword32 g_cfg_newdisk_slotdrive = 0;\n\nconst char *g_config_kegs_name_list[] = {\n\t\t\"config.kegs\", \"kegs_conf\", \".config.kegs\", 0\n};\n\nint\tg_highest_smartport_unit = -1;\nint\tg_reparse_delay = 0;\nint\tg_user_page2_shadow = 1;\n\nchar\tg_cfg_printf_buf[CFG_PRINTF_BUFSIZE];\nchar\tg_config_kegs_buf[CONF_BUF_LEN];\n\n#define CFG_ERR_BUFSIZE\t\t80\n#define CFG_ERR_MAX\t\t5\n\nint\tg_cfg_err_pos = 0;\nchar\tg_cfg_err_bufs[CFG_ERR_MAX][CFG_ERR_BUFSIZE];\n\nint\tg_cfg_curs_x = 0;\nint\tg_cfg_curs_y = 0;\nint\tg_cfg_curs_inv = 0;\nint\tg_cfg_curs_mousetext = 0;\nint\tg_cfg_screen_changed = 0;\nbyte\tg_cfg_screen[24][80];\n\n#if defined(MAC) || defined(_WIN32)\nint\tg_cfg_ignorecase = 1;\t\t// Ignore case in filenames\n#else\nint\tg_cfg_ignorecase = 0;\n#endif\n\n#define CFG_MAX_OPTS\t34\n#define CFG_OPT_MAXSTR\t100\n\nint g_cfg_opts_vals[CFG_MAX_OPTS];\nchar g_cfg_opts_str[CFG_PATH_MAX];\nchar g_cfg_opt_buf[CFG_OPT_MAXSTR];\nchar g_cfg_edit_buf[CFG_OPT_MAXSTR];\n\nchar *g_cfg_rom_path = \"ROM\";\t\t\t// config_init_menus will malloc\nchar *g_cfg_charrom_path = \"Undefined\";\t\t// config_init_menus will malloc\nint g_cfg_charrom_pos = 0;\nchar *g_cfg_file_def_name = \"Undefined\";\nchar **g_cfg_file_strptr = 0;\nint g_cfg_file_min_size = 1024;\nint g_cfg_file_max_size = 2047*1024*1024;\nint\tg_cfg_edit_type = 0;\nvoid\t*g_cfg_edit_ptr = 0;\n\n#define MAX_PARTITION_BLK_SIZE\t\t65536\n\nchar *g_argv0_path = \".\";\n\nconst char *g_kegs_default_paths[] = { \"\", \"./\", \"${HOME}/\",\n\t\"${HOME}/Library/KEGS/\", \"${0}/../\", \"${0}/\",\n\t\"${0}/Contents/Resources/\", 0 };\n\n\nextern Cfg_menu g_cfg_main_menu[];\n\n#define KNMP(a)\t\t&a, #a, 0\n#define KNM(a)\t\t&a, #a\n\nCfg_menu g_cfg_disk_menu[] = {\n{ \"Disk Configuration\", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU },\n{ \"s5d1 = \", 0, 0, 0, CFGTYPE_DISK + 0x5000 },\n{ \"s5d2 = \", 0, 0, 0, CFGTYPE_DISK + 0x5010 },\n{ \"\", 0, 0, 0, 0 },\n{ \"s6d1 = \", 0, 0, 0, CFGTYPE_DISK + 0x6000 },\n{ \"s6d2 = \", 0, 0, 0, CFGTYPE_DISK + 0x6010 },\n{ \"\", 0, 0, 0, 0 },\n{ \"s7d1 = \", 0, 0, 0, CFGTYPE_DISK + 0x7000 },\n{ \"s7d2 = \", 0, 0, 0, CFGTYPE_DISK + 0x7010 },\n{ \"s7d3 = \", 0, 0, 0, CFGTYPE_DISK + 0x7020 },\n{ \"s7d4 = \", 0, 0, 0, CFGTYPE_DISK + 0x7030 },\n{ \"s7d5 = \", 0, 0, 0, CFGTYPE_DISK + 0x7040 },\n{ \"s7d6 = \", 0, 0, 0, CFGTYPE_DISK + 0x7050 },\n{ \"s7d7 = \", 0, 0, 0, CFGTYPE_DISK + 0x7060 },\n{ \"s7d8 = \", 0, 0, 0, CFGTYPE_DISK + 0x7070 },\n{ \"s7d9 = \", 0, 0, 0, CFGTYPE_DISK + 0x7080 },\n{ \"s7d10= \", 0, 0, 0, CFGTYPE_DISK + 0x7090 },\n{ \"s7d11= \", 0, 0, 0, CFGTYPE_DISK + 0x70a0 },\n{ \"s7d12= \", 0, 0, 0, CFGTYPE_DISK + 0x70b0 },\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_newslot6_menu[] = {\n{ \"New 5.25\\\" disk image Configuration\", g_cfg_newslot6_menu, 0, 0,\n\t\t\t\t\t\t\t\tCFGTYPE_MENU },\n{ \"size,280,140KB\", KNM(g_cfg_newdisk_blocks),\n\t\t\t&g_cfg_newdisk_blocks_default, CFGTYPE_INT },\n{ \"Type,1,ProDOS/DOS 3.3,2,WOZ image,3,Dynamic ProDOS directory\",\n\t\t\tKNM(g_cfg_newdisk_type),\n\t\t\t&g_cfg_newdisk_type_default, CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Create and name the image\", (void *)cfg_name_new_image, 0, 0, CFGTYPE_FUNC },\n{ \"\", 0, 0, 0, 0 },\n{ \"Cancel, go back to Disk Config\", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_newslot5_menu[] = {\n{ \"New 3.5\\\" disk image Configuration\", g_cfg_newslot5_menu, 0, 0,CFGTYPE_MENU},\n{ \"size,1600,800KB\", KNM(g_cfg_newdisk_blocks),\n\t\t\t&g_cfg_newdisk_blocks_default, CFGTYPE_INT },\n{ \"Type,1,ProDOS,2,WOZ image,3,Dynamic ProDOS directory\",\n\t\t\tKNM(g_cfg_newdisk_type),\n\t\t\t&g_cfg_newdisk_type_default, CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Create and name the image\", (void *)cfg_name_new_image, 0, 0, CFGTYPE_FUNC },\n{ \"\", 0, 0, 0, 0 },\n{ \"Cancel, go back to Disk Config\", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_newslot7_menu[] = {\n{ \"New Smartport disk image Configuration\", g_cfg_newslot7_menu, 0, 0,\n\t\t\t\t\t\t\t\tCFGTYPE_MENU},\n{ \"size,1600,800KB,3200,1600KB,16384,8MB,32768,16MB,65535,32MB\",\n\t\tKNM(g_cfg_newdisk_blocks), &g_cfg_newdisk_blocks_default,\n\t\t\tCFGTYPE_INT },\n{ \"Type,1,ProDOS,3,Dynamic ProDOS directory\", KNM(g_cfg_newdisk_type),\n\t\t\t&g_cfg_newdisk_type_default, CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Create and name the image\", (void *)cfg_name_new_image, 0, 0, CFGTYPE_FUNC },\n{ \"\", 0, 0, 0, 0 },\n{ \"Cancel, go back to Disk Config\", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_joystick_menu[] = {\n{ \"Joystick Configuration\", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU },\n{ \"Joystick Emulation,0,Keypad Joystick,1,Mouse Joystick,2,Native Joystick 1,\"\n\t\"3,Native Joystick 2\", KNMP(g_joystick_type), CFGTYPE_INT },\n{ \"Joystick Scale X,0x100,Standard,0x119,+10%,0x133,+20%,\"\n\t\"0x150,+30%,0xb0,-30%,0xcd,-20%,0xe7,-10%\",\n\t\tKNMP(g_joystick_scale_factor_x), CFGTYPE_INT },\n{ \"Joystick Scale Y,0x100,Standard,0x119,+10%,0x133,+20%,\"\n\t\"0x150,+30%,0xb0,-30%,0xcd,-20%,0xe7,-10%\",\n\t\tKNMP(g_joystick_scale_factor_y), CFGTYPE_INT },\n{ \"Joystick Trim X\", KNMP(g_joystick_trim_amount_x), CFGTYPE_INT },\n{ \"Joystick Trim Y\", KNMP(g_joystick_trim_amount_y), CFGTYPE_INT },\n{ \"Swap Joystick X and Y,0,Normal operation,1,Paddle 1 and Paddle 0 swapped\",\n\t\tKNMP(g_swap_paddles), CFGTYPE_INT },\n{ \"Invert Joystick,0,Normal operation,1,Left becomes right and up becomes down\",\n\t\tKNMP(g_invert_paddles), CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_rom_menu[] = {\n{ \"ROM File Selection\", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU },\n{ \"ROM File\", KNMP(g_cfg_rom_path), CFGTYPE_FILE },\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_charrom_menu[] = {\n{ \"Character ROM File Selection\", g_cfg_charrom_menu, 0, 0, CFGTYPE_MENU },\n{ \"Character ROM File\", KNMP(g_cfg_charrom_path), CFGTYPE_FILE },\n{ \"Character Set,0,0x00 US Enhanced,1,0x01 US Un-enhanced,\"\n\t\"2,0x02 Clinton Turner V1 Enhanced,3,0x03 ReActiveMicro Enhanced,\"\n\t\"4,0x04 Dan Paymar Enhanced,5,0x05 Blippo Black Enhanced,\"\n\t\"6,0x06 Byte Enhanced,7,0x07 Colossal Enhanced,\"\n\t\"8,0x08 Count Enhanced,9,0x09 Flow Enhanced,\"\n\t\"10,0x0a Gothic Enhanced,11,0x0b Outline Enhanced,\"\n\t\"12,0x0c Pigfont Enhanced,13,0x0d Pinocchio Enhanced,\"\n\t\"14,0x0e Slant Enhanced,15,0x0f Stop Enhanced,\"\n\t\"16,0x10 Euro Un-Enhanced,17,0x11 Euro Enhanced,\"\n\t\"18,0x12 Clinton Turner V2 Enhanced,19,0x13 Improved German Enhanced,\"\n\t\"20,0x14 Improved German Un-Enhanced,21,0x15 Franch Canadian Enhanced,\"\n\t\"22,0x16 French Canadian Un-Enhanced,23,0x17 Hebrew Enhanced,\"\n\t\"24,0x18 Hebrew Un-Enhanced,25,0x19 Apple II+ Enhanced,\"\n\t\"26,0x1a Apple II+ Un-Enhanced,27,0x1b Katakana Enhanced,\"\n\t\"28,0x1c Cyrillic Enhanced,29,0x1d Greek Enhanced,\"\n\t\"30,0x1e Esperanto Enhanced,31,0x1f Videx Enhanced\",\n\tKNMP(g_cfg_charrom_pos), CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_serial_menu[] = {\n{ \"Serial Port Configuration\", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU },\n{ \"Slot 1 (port 0) settings\", 0, 0, 0, 0 },\n{ \"  Main setting  ,0,Use Real Device below,1,Use a virtual modem,\"\n\t\t\"2,Use Remote IP below,3,Use incoming port 6501\",\n\t\tKNMP(g_serial_cfg[0]), CFGTYPE_INT },\n{ \"  Status        \", (void *)cfg_get_serial0_status, 0, 0, CFGTYPE_FUNC },\n{ \"  Real Device   \", KNMP(g_serial_device[0]), CFGTYPE_FILE },\n{ \"  Windows Device,0,Disabled,1,COM1,2,COM2,3,COM3,4,COM4\",\n\t\tKNMP(g_serial_win_device[0]), CFGTYPE_INT },\n{ \"  Remote IP     \", KNMP(g_serial_remote_ip[0]), CFGTYPE_STR },\n{ \"  Remote Port   \", KNMP(g_serial_remote_port[0]), CFGTYPE_INT },\n{ \"  Serial Mask   ,0,Send full 8-bit data,1,Mask off high bit\",\n\t\tKNMP(g_serial_mask[0]), CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Slot 2 (port 1) settings\", 0, 0, 0, 0, },\n{ \"  Main setting  ,0,Use Real Device below,1,Use a virtual modem,\"\n\t\t\"2,Use Remote IP below,3,Use incoming port 6502\",\n\t\tKNMP(g_serial_cfg[1]), CFGTYPE_INT },\n{ \"  Status        \", (void *)cfg_get_serial1_status, 0, 0, CFGTYPE_FUNC },\n{ \"  Real Device   \", KNMP(g_serial_device[1]), CFGTYPE_FILE },\n{ \"  Windows Device,1,COM1,2,COM2,3,COM3,4,COM4\",\n\t\tKNMP(g_serial_win_device[1]), CFGTYPE_INT },\n{ \"  Remote IP     \", KNMP(g_serial_remote_ip[1]), CFGTYPE_STR },\n{ \"  Remote Port   \", KNMP(g_serial_remote_port[1]), CFGTYPE_INT },\n{ \"  Serial Mask   ,0,Send full 8-bit data,1,Mask off high bit\",\n\t\tKNMP(g_serial_mask[1]), CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_modem_menu[] = {\n{ \"Virtual Modem Configuration\", g_cfg_modem_menu, 0, 0, CFGTYPE_MENU },\n{ \"Modem Speed Response Code          ,5,5 - CONNECT 1200,10,10 - CONNECT 2400,\"\n\t\t\"12,12 - CONNECT 9600 (HAYES/Warp6),\"\n\t\t\"13,13 - CONNECT 9600 (USR/HST),\"\n\t\t\"14,14 - CONNECT 19200 (HAYES/Warp6),\"\n\t\t\"28,28 - CONNECT 38400 (HAYES/Warp6)\",\n\t\tKNMP(g_serial_modem_response_code), CFGTYPE_INT },\n{ \"Allow Modem incoming on 6501/6502  ,0,Outgoing only,\"\n\t\t\"1,Incoming and outgoing (BBS)\",\n\t\tKNMP(g_serial_modem_allow_incoming), CFGTYPE_INT },\n{ \"Send Telnet Escape codes           ,0,Disable Telnet,1,Send Telnet codes\",\n\t\tKNMP(g_serial_modem_init_telnet), CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\n\n\nCfg_menu g_cfg_video_menu[] = {\n{ \"Force X-windows display depth\", KNMP(g_force_depth), CFGTYPE_INT },\n{ \"Enable VOC,0,Disabled,1,Enabled\", KNMP(g_voc_enable), CFGTYPE_INT },\n{ \"Default Main Window width\", KNMP(g_mainwin_width), CFGTYPE_INT },\n{ \"Default Main Window height\", KNMP(g_mainwin_height), CFGTYPE_INT },\n{ \"Main Window X position\", KNMP(g_mainwin_xpos), CFGTYPE_INT },\n{ \"Main Window Y position\", KNMP(g_mainwin_ypos), CFGTYPE_INT },\n{ \"3200 Color Enable,0,Auto (Full if fast enough),1,Full (Update every line),\"\n\t\"8,Off (Update video every 8 lines)\",\n\t\tKNMP(g_video_line_update_interval), CFGTYPE_INT },\n{ \"Dump text screen to file\", (void *)cfg_text_screen_dump, 0, 0, CFGTYPE_FUNC},\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_main_menu[] = {\n{ \"KEGS Configuration\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ \"Disk Configuration\", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU },\n{ \"Joystick Configuration\", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU },\n{ \"ROM File Selection\", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU },\n{ \"Character ROM Selection\", g_cfg_charrom_menu, 0, 0, CFGTYPE_MENU },\n{ \"Serial Port Configuration\", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU },\n{ \"Virtual Modem Configuration\", g_cfg_modem_menu, 0, 0, CFGTYPE_MENU },\n{ \"Video Settings\", g_cfg_video_menu, 0, 0, CFGTYPE_MENU },\n{ \"Auto-update config.kegs,0,Manual,1,Immediately\",\n\t\tKNMP(g_config_kegs_auto_update), CFGTYPE_INT },\n{ \"Speed,0,Unlimited,1,1.0MHz,2,2.8MHz,3,8.0MHz (Zip)\",\n\t\tKNMP(g_limit_speed), CFGTYPE_INT },\n{ \"ZipGS Speed,8,8MHz,16,16MHz,32,32MHz,64,64MHz,128,128MHz\",\n\t\tKNMP(g_zip_speed_mhz), CFGTYPE_INT },\n{ \"Expansion Mem Size,0,0MB,0x100000,1MB,0x200000,2MB,0x300000,3MB,\"\n\t\"0x400000,4MB,0x600000,6MB,0x800000,8MB,0xa00000,10MB,0xc00000,12MB,\"\n\t\"0xe00000,14MB\", KNMP(g_mem_size_exp), CFGTYPE_INT },\n{ \"Show Status lines,0,Disabled,1,Enabled\", KNMP(g_status_enable), CFGTYPE_INT},\n{ \"Code Red Halts,0,Do not stop on bad accesses,1,Enter debugger on bad \"\n\t\t\"accesses\", KNMP(g_user_halt_bad), CFGTYPE_INT },\n{ \"Enable Text Page 2 Shadow,0,Disabled on ROM 01 (matches real hardware),\"\n\t\"1,Enabled on ROM 01 and 03\",\n\t\tKNMP(g_user_page2_shadow), CFGTYPE_INT },\n{ \"Swap Command/Option keys,0,Disabled,1,Swapped\",\n\t\t\t\tKNMP(g_adb_swap_command_option), CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Save changes to config.kegs\", (void *)config_write_config_kegs_file, 0, 0,\n\t\tCFGTYPE_FUNC },\n{ \"\", 0, 0, 0, 0 },\n{ \"Exit Config (or press F4)\", (void *)cfg_exit, 0, 0, CFGTYPE_FUNC },\n{ 0, 0, 0, 0, 0 },\n};\n\n\n#define CFG_MAX_DEFVALS\t128\nCfg_defval g_cfg_defvals[CFG_MAX_DEFVALS];\nint g_cfg_defval_index = 0;\n\nword32 g_cfg_slotdrive = 0;\nint g_cfg_select_partition = -1;\nchar g_cfg_tmp_path[CFG_PATH_MAX];\nchar g_cfg_file_path[CFG_PATH_MAX];\nchar g_cfg_file_cachedpath[CFG_PATH_MAX];\nchar g_cfg_file_cachedreal[CFG_PATH_MAX];\nchar g_cfg_file_curpath[CFG_PATH_MAX];\nchar g_cfg_file_shortened[CFG_PATH_MAX];\nchar g_cfg_file_match[CFG_PATH_MAX];\n\nchar g_cfg_part_path[CFG_PATH_MAX];\nint\tg_cfg_partition_is_zip = 0;\n\nCfg_listhdr g_cfg_dirlist = { 0 };\nCfg_listhdr g_cfg_partitionlist = { 0 };\n\nint g_cfg_file_pathfield = 0;\n\nconst char *g_kegs_rom_names[] = { \"ROM\", \"ROM\", \"ROM.01\", \"ROM.03\",\n\t\"APPLE2GS.ROM\", \"APPLE2GS.ROM2\", \"xgs.rom\", \"XGS.ROM\", \"Rom03gd\",\n\t\"342-0077-b\", // MAME ROM.01\n\t0 };\n\t/* First entry is special--it will be overwritten by g_cfg_rom_path */\n\nconst char *g_kegs_c1rom_names[] = { 0 };\nconst char *g_kegs_c2rom_names[] = { 0 };\nconst char *g_kegs_c3rom_names[] = { 0 };\nconst char *g_kegs_c4rom_names[] = { 0 };\nconst char *g_kegs_c5rom_names[] = { 0 };\nconst char *g_kegs_c6rom_names[] = { \"c600.rom\", \"controller.rom\", \"disk.rom\",\n\t\t\t\t\"DISK.ROM\", \"diskII.prom\", 0 };\nconst char *g_kegs_c7rom_names[] = { 0 };\n\nconst char **g_kegs_rom_card_list[8] = {\n\t0,\t\t\tg_kegs_c1rom_names,\n\tg_kegs_c2rom_names,\tg_kegs_c3rom_names,\n\tg_kegs_c4rom_names,\tg_kegs_c5rom_names,\n\tg_kegs_c6rom_names,\tg_kegs_c7rom_names };\n\nbyte g_rom_c600_rom01_diffs[256] = {\n\t0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xe2, 0x00,\n\t0xd0, 0x50, 0x0f, 0x77, 0x5b, 0xb9, 0xc3, 0xb1,\n\t0xb1, 0xf8, 0xcb, 0x4e, 0xb8, 0x60, 0xc7, 0x2e,\n\t0xfc, 0xe0, 0xbf, 0x1f, 0x66, 0x37, 0x4a, 0x70,\n\t0x55, 0x2c, 0x3c, 0xfc, 0xc2, 0xa5, 0x08, 0x29,\n\t0xac, 0x21, 0xcc, 0x09, 0x55, 0x03, 0x17, 0x35,\n\t0x4e, 0xe2, 0x0c, 0xe9, 0x3f, 0x9d, 0xc2, 0x06,\n\t0x18, 0x88, 0x0d, 0x58, 0x57, 0x6d, 0x83, 0x8c,\n\t0x22, 0xd3, 0x4f, 0x0a, 0xe5, 0xb7, 0x9f, 0x7d,\n\t0x2c, 0x3e, 0xae, 0x7f, 0x24, 0x78, 0xfd, 0xd0,\n\t0xb5, 0xd6, 0xe5, 0x26, 0x85, 0x3d, 0x8d, 0xc9,\n\t0x79, 0x0c, 0x75, 0xec, 0x98, 0xcc, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x39, 0x00, 0x35, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,\n\t0x6c, 0x44, 0xce, 0x4c, 0x01, 0x08, 0x00, 0x00\n};\n\nbyte g_rom_c700[256] = {\n\t//0xa2, 0x20, 0xa2, 0x00, 0xa2, 0x03, 0xc9, 0x3c,\t// For Apple //e\n\t0xa2, 0x20, 0xa2, 0x00, 0xa2, 0x03, 0xc9, 0x00,\n\t//^^= LDX #$20; LDY #$00, LDX #$03  CMP #$3c\n\t0x80, 0x0c, 0x18, 0xb8, 0x70, 0x38, 0xb8, 0x42,\n\t//^^= BRA $c716; CLC; CLV; BVS $c746 (SEC); CLV; WDM $c7,$00\n\t0xc7, 0x00, 0x60, 0x00, 0x00, 0xea, 0xe2, 0x41,\n\t//^^=  ...; RTS..............; NOP; SEP #$41\n\t0x70, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t//^^= BVS $c70f\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t// So does WDM $c7,$00 with psr.v=1 for $c700; v=0,c=0 for $c70a,\n\t//  and v=0,c=1 for $c70d\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0xbf, 0x0a\n};\n\nCfg_menu *g_menuptr = 0;\nint\tg_menu_line = 1;\nint\tg_menu_inc = 1;\nint\tg_menu_max_line = 1;\nint\tg_menu_redraw_needed = 1;\n\n#define MAX_CFG_ARGV_OVERRIDES\t\t64\n\nint g_cfg_argv_num_overrides = 0;\nchar *g_cfg_argv_overrides[MAX_CFG_ARGV_OVERRIDES];\n\nint\nconfig_add_argv_override(const char *str1, const char *str2)\n{\n\tconst char *equal_ptr;\n\tchar\t*str;\n\tint\tret, pos, len;\n\n\t// Handle things like \"rom=rompath\" and \"rom\", \"rompath\"\n\t// Look through str1, see if there is '=', if so ignore str2\n\tequal_ptr = strchr(str1, '=');\n\tret = 1;\n\tif(equal_ptr) {\t\t\t// str1 has '=' in it\n\t\tret = 0;\t\t// Don't eat up str2 argument\n\t\tstr = kegs_malloc_str(str1);\n\t} else {\n\t\t// We need to form a new string of str1, =, str2\n\t\tlen = (int)(strlen(str1) + strlen(str2) + 2);\n\t\tstr = malloc(len);\n\t\tcfg_strncpy(str, str1, len);\n\t\tcfg_strlcat(str, \"=\", len);\n\t\tcfg_strlcat(str, str2, len);\n\t}\n\tpos = g_cfg_argv_num_overrides++;\n\tif(pos >= MAX_CFG_ARGV_OVERRIDES) {\n\t\tg_cfg_argv_num_overrides = MAX_CFG_ARGV_OVERRIDES;\n\t\tfatal_printf(\"MAX_CFG_ARGV_OVERRIDES overflow\\n\");\n\t\tmy_exit(5);\n\t\treturn ret;\n\t}\n\tg_cfg_argv_overrides[pos] = str;\n\tprintf(\"Added config override %d, %s\\n\", pos, str);\n\n\treturn ret;\n}\n\nvoid\nconfig_set_config_kegs_name(const char *str1)\n{\n\tint\tmaxlen;\n\n\t//\tCommand line override \"-cfg cfg_file\"\n\tg_config_kegs_name[0] = 0;\n\tmaxlen = (int)sizeof(g_config_kegs_name);\n\tcfg_strncpy(&g_config_kegs_name[0], str1, maxlen);\n}\n\nvoid\nconfig_init_menus(Cfg_menu *menuptr)\n{\n\tvoid\t*voidptr;\n\tconst char *name_str;\n\tCfg_defval *defptr;\n\tchar\t**str_ptr;\n\tchar\t*str;\n\tint\ttype, pos, val;\n\n\tif(menuptr[0].defptr != 0) {\n\t\treturn;\n\t}\n\tmenuptr[0].defptr = (void *)1;\n\tpos = 0;\n\twhile(pos < 100) {\n\t\ttype = menuptr->cfgtype;\n\t\tvoidptr = menuptr->ptr;\n\t\tname_str = menuptr->name_str;\n\t\tif(menuptr->str == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tif(name_str != 0) {\n\t\t\tdefptr = &(g_cfg_defvals[g_cfg_defval_index++]);\n\t\t\tif(g_cfg_defval_index >= CFG_MAX_DEFVALS) {\n\t\t\t\tfatal_printf(\"CFG_MAX_DEFVAL overflow\\n\");\n\t\t\t\tmy_exit(5);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdefptr->menuptr = menuptr;\n\t\t\tdefptr->intval = 0;\n\t\t\tdefptr->strval = 0;\n\t\t\tswitch(type) {\n\t\t\tcase CFGTYPE_INT:\n\t\t\t\tval = *((int *)voidptr);\n\t\t\t\tdefptr->intval = val;\n\t\t\t\tmenuptr->defptr = &(defptr->intval);\n\t\t\t\tbreak;\n\t\t\tcase CFGTYPE_FILE:\n\t\t\tcase CFGTYPE_STR:\n\t\t\t\tstr_ptr = (char **)menuptr->ptr;\n\t\t\t\tstr = *str_ptr;\n\t\t\t\t// We need to malloc this string since all\n\t\t\t\t//  string values must be dynamically alloced\n\t\t\t\tdefptr->strval = str;\t// this can have a copy\n\t\t\t\t*str_ptr = kegs_malloc_str(str);\n\t\t\t\tmenuptr->defptr = &(defptr->strval);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tfatal_printf(\"name_str is %p = %s, but type: \"\n\t\t\t\t\t\"%d\\n\", name_str, name_str, type);\n\t\t\t\tmy_exit(5);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif(type == CFGTYPE_MENU) {\n\t\t\tconfig_init_menus((Cfg_menu *)voidptr);\n\t\t}\n\t\tpos++;\n\t\tmenuptr++;\n\t}\n}\n\nvoid\nconfig_init()\n{\n\tconfig_init_menus(g_cfg_main_menu);\n\n\t// Find the config.kegs file\n\tif(g_config_kegs_name[0] == 0) {\n\t\tcfg_find_config_kegs_file();\n\t}\n\n\tconfig_parse_config_kegs_file();\n}\n\nvoid\ncfg_find_config_kegs_file()\n{\n\tconst char **path_ptr;\n\tint\tmaxlen, fd;\n\n\tg_config_kegs_name[0] = 0;\n\tmaxlen = sizeof(g_config_kegs_name);\n\tfd = 0;\n\tif(!config_setup_kegs_file(&g_config_kegs_name[0], maxlen,\n\t\t\t\t\t\t&g_config_kegs_name_list[0])) {\n\t\t// Try to create config.kegs\n\t\tfd = -1;\n\t\tpath_ptr = &g_kegs_default_paths[0];\n\t\twhile(*path_ptr) {\n\t\t\tconfig_expand_path(&g_config_kegs_name[0], *path_ptr,\n\t\t\t\t\t\t\t\t\tmaxlen);\n\t\t\tcfg_strlcat(&g_config_kegs_name[0], \"config.kegs\",\n\t\t\t\t\t\t\t\t\tmaxlen);\n\t\t\tprintf(\"Trying to create %s\\n\", &g_config_kegs_name[0]);\n\t\t\tfd = open(&g_config_kegs_name[0],\n\t\t\t\t\tO_CREAT | O_TRUNC | O_WRONLY, 0x1b6);\n\t\t\tclose(fd);\n\t\t\tif(fd >= 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tpath_ptr++;\n\t\t}\n\t}\n\n\tif(fd < 0) {\n\t\tfatal_printf(\"Could not create config.kegs!\\n\");\n\t\tmy_exit(2);\n\t}\n}\n\nint\nconfig_setup_kegs_file(char *outname, int maxlen, const char **name_ptr)\n{\n\tstruct stat stat_buf;\n\tconst char **path_ptr, **cur_name_ptr;\n\tmode_t\tfmt;\n\tint\tret, len;\n\n\toutname[0] = 0;\n\n\tpath_ptr = &g_kegs_default_paths[0];\t\t// Array of strings\n\n\twhile(*path_ptr) {\n\t\tlen = config_expand_path(outname, *path_ptr, maxlen);\n\t\tif(len != (int)strlen(outname)) {\n\t\t\tprintf(\"config_expand_path ret %d, but strlen:%d!\\n\",\n\t\t\t\t\t\tlen, (int)strlen(outname));\n\t\t}\n\t\tcur_name_ptr = name_ptr;\n\t\twhile(*cur_name_ptr && (len < maxlen)) {\n\t\t\toutname[len] = 0;\n\t\t\tcfg_strlcat(outname, *cur_name_ptr, maxlen);\n\t\t\t// printf(\"Doing stat on %s\\n\", outname);\n\t\t\tret = cfg_stat(outname, &stat_buf, 0);\n\t\t\tif(ret == 0) {\n\t\t\t\tfmt = stat_buf.st_mode & S_IFMT;\n\t\t\t\tif(fmt != S_IFDIR) {\n\t\t\t\t\t/* got it! */\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcur_name_ptr++;\n\t\t}\n\t\tpath_ptr++;\n\t}\n\n\treturn 0;\n}\n\nint\nconfig_expand_path(char *out_ptr, const char *in_ptr, int maxlen)\n{\n\tchar\tname_buf[256];\n\tchar\t*tmp_ptr;\n\tint\tname_len, in_char, state, pos;\n\n\tout_ptr[0] = 0;\n\n\tpos = 0;\n\tname_len = 0;\n\tstate = 0;\n\n\t/* See if in_ptr has ${} notation, replace with getenv or argv0 */\n\twhile(pos < (maxlen - 1)) {\n\t\tin_char = *in_ptr++;\n\t\tout_ptr[pos++] = in_char;\n\t\tout_ptr[pos] = 0;\n\t\tif(in_char == 0) {\n\t\t\treturn pos - 1;\n\t\t}\n\t\tif(state == 0) {\n\t\t\t/* No $ seen yet, look for it */\n\t\t\tif(in_char == '$') {\n\t\t\t\tstate = 1;\n\t\t\t}\n\t\t} else if(state == 1) {\n\t\t\t/* See if next char is '{' (dummy }) */\n\t\t\tif(in_char == '{') {\t\t/* add dummy } */\n\t\t\t\tstate = 2;\n\t\t\t\tname_len = 0;\n\t\t\t\tpos -= 2;\n\t\t\t\tout_ptr[pos] = 0;\n\t\t\t} else {\n\t\t\t\tstate = 0;\n\t\t\t}\n\t\t} else if(state == 2) {\n\t\t\t/* fill name_buf ... dummy '{' */\n\t\t\tpos--;\n\t\t\tout_ptr[pos] = 0;\n\t\t\tif(in_char == '}') {\n\t\t\t\tname_buf[name_len] = 0;\n\n\t\t\t\t/* got token, now look it up */\n\t\t\t\ttmp_ptr = \"\";\n\t\t\t\tif(!strncmp(\"0\", name_buf, 128)) {\n\t\t\t\t\t/* Replace ${0} with g_argv0_path */\n\t\t\t\t\ttmp_ptr = g_argv0_path;\n\t\t\t\t} else {\n\t\t\t\t\ttmp_ptr = getenv(name_buf);\n\t\t\t\t\tif(tmp_ptr == 0) {\n\t\t\t\t\t\ttmp_ptr = \"\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpos = cfg_strlcat(out_ptr, tmp_ptr, maxlen);\n\t\t\t\tstate = 0;\n\t\t\t} else {\n\t\t\t\tname_buf[name_len++] = in_char;\n\t\t\t\tif(name_len >= 250) {\n\t\t\t\t\tname_len--;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn pos;\n}\n\nchar *\ncfg_exit(int get_status)\n{\n\t/* printf(\"In cfg exit\\n\"); */\n\tif(get_status) {\n\t\treturn 0;\n\t}\n\tcfg_toggle_config_panel();\n\n\treturn 0;\n}\n\nvoid\ncfg_err_vprintf(const char *pre_str, const char *fmt, va_list ap)\n{\n\tchar\t*bufptr;\n\tint\tpos, len, bufsize;\n\n\tpos = g_cfg_err_pos;\n\tif(pos >= CFG_ERR_MAX) {\n\t\tpos = CFG_ERR_MAX - 1;\n\t}\n\tbufptr = &g_cfg_err_bufs[pos][0];\n\tlen = 0;\n\tbufsize = CFG_ERR_BUFSIZE;\n\tif(pre_str && *pre_str) {\n\t\tcfg_strncpy(bufptr, pre_str, CFG_ERR_BUFSIZE);\n\t\tcfg_strlcat(bufptr, \" error: \", CFG_ERR_BUFSIZE);\n\t\tlen = (int)strlen(bufptr);\n\t\tbufsize = CFG_ERR_BUFSIZE - len;\n\t}\n\tif(bufsize > 0) {\n\t\tvsnprintf(&bufptr[len], bufsize, fmt, ap);\n\t}\n\n\tfputs(bufptr, stderr);\n\tg_cfg_err_pos = pos + 1;\n}\n\nvoid\ncfg_err_printf(const char *pre_str, const char *fmt, ...)\n{\n\tva_list\tap;\n\n\tva_start(ap, fmt);\n\tcfg_err_vprintf(pre_str, fmt, ap);\n\tva_end(ap);\n}\n\nvoid\ncfg_toggle_config_panel()\n{\n\tint\tpanel;\n\n\tpanel = !g_config_control_panel;\n\tif(g_rom_version < 0) {\n\t\tpanel = 1;\t/* Stay in config mode */\n\t}\n\tif(panel != g_config_control_panel) {\n\t\tcfg_set_config_panel(panel);\n\t}\n}\n\nvoid\ncfg_set_config_panel(int panel)\n{\n\tint\ti;\n\n\tg_config_control_panel = panel;\n\tif(panel) {\n\t\t// Entering configuration panel\n\t\tvideo_force_reparse();\n\n\t\tcfg_printf(\"Entering config_control_panel\\n\");\n\n\t\tfor(i = 0; i < 20; i++) {\n\t\t\t// Toss any queued-up keypresses\n\t\t\tif(adb_read_c000() & 0x80) {\n\t\t\t\t(void)adb_access_c010();\n\t\t\t}\n\t\t}\n\t\t// HACK: Force adb keyboard (and probably mouse) to \"normal\"...\n\n\t\tcfg_home();\n\n\t\tg_menu_line = 1;\n\t\tg_menu_inc = 1;\n\t\tg_menu_redraw_needed = 1;\n\t\tg_cfg_slotdrive = 0;\n\t\tg_cfg_newdisk_select = 0;\n\t\tg_cfg_select_partition = -1;\n\t} else {\n\t\t// Leave config panel, go back to A2 emulation\n\n\t\tvideo_force_reparse();\n\t}\n\tg_full_refresh_needed = -1;\n\tg_a2_screen_buffer_changed = -1;\n\tg_adb_repeat_vbl = g_vbl_count + 60;\n}\n\nchar *\ncfg_text_screen_dump(int get_status)\n{\n\tFILE\t*ofile;\n\tchar\t*bufptr;\n\tchar\t*filename;\n\n\tif(get_status) {\n\t\treturn 0;\n\t}\n\tfilename = \"kegs.screen.dump\";\n\tprintf(\"Writing text screen to the file %s\\n\", filename);\n\tofile = fopen(filename, \"w\");\n\tif(ofile == 0) {\n\t\tfatal_printf(\"Could not write to file %s, (%d)\\n\", filename,\n\t\t\t\terrno);\n\t\treturn 0;\n\t}\n\tbufptr = cfg_text_screen_str();\n\tfputs(bufptr, ofile);\n\tfclose(ofile);\n\treturn 0;\n}\n\nchar g_text_screen_buf[2100] = { 0 };\n\nchar *\ncfg_text_screen_str()\n{\n\tchar\t*bufptr;\n\tint\tpos, start_pos, c, offset;\n\tint\ti, j;\n\n\t// bufptr must be at least (81*24)+2 characters\n\tbufptr = &g_text_screen_buf[0];\n\tpos = 0;\n\tfor(i = 0; i < 24; i++) {\n\t\tstart_pos = pos;\n\t\tfor(j = 0; j < 40; j++) {\n\t\t\toffset = g_screen_index[i] + j;\n\t\t\tif(g_cur_a2_stat & ALL_STAT_VID80) {\n\t\t\t\tc = g_slow_memory_ptr[0x10400 + offset] & 0x7f;\n\t\t\t\tif(c < 0x20) {\n\t\t\t\t\tc += 0x40;\n\t\t\t\t}\n\t\t\t\tbufptr[pos++] = c;\n\t\t\t}\n\t\t\tc = g_slow_memory_ptr[0x0400 + offset] & 0x7f;\n\t\t\tif(c < 0x20) {\n\t\t\t\tc += 0x40;\n\t\t\t}\n\t\t\tif(c == 0x7f) {\n\t\t\t\tc = ' ';\n\t\t\t}\n\t\t\tbufptr[pos++] = c;\n\t\t}\n\t\twhile((pos > start_pos) && (bufptr[pos-1] == ' ')) {\n\t\t\t/* try to strip out trailing spaces */\n\t\t\tpos--;\n\t\t}\n\t\tbufptr[pos++] = '\\n';\n\t\tbufptr[pos] = 0;\n\t}\n\n\treturn bufptr;\n}\n\nchar *\ncfg_get_serial0_status(int get_status)\n{\n\treturn scc_get_serial_status(get_status, 0);\n}\n\nchar *\ncfg_get_serial1_status(int get_status)\n{\n\treturn scc_get_serial_status(get_status, 1);\n}\n\nchar *\ncfg_get_current_copy_selection()\n{\n\treturn &g_text_screen_buf[0];\n}\n\nvoid\nconfig_vbl_update(int doit_3_persec)\n{\n\tif(doit_3_persec) {\n\t\tif(g_config_kegs_auto_update && g_config_kegs_update_needed) {\n\t\t\t(void)config_write_config_kegs_file(0);\n\t\t}\n\t}\n\treturn;\n}\n\nvoid\ncfg_file_update_rom(const char *str)\n{\n\tcfg_file_update_ptr(&g_cfg_rom_path, str, 1);\n}\n\nvoid\ncfg_file_update_ptr(char **strptr, const char *str, int need_update)\n{\n\tchar\t*newstr;\n\tint\tremote_changed, serial_changed;\n\tint\ti;\n\n\t// Update whatever g_cfg_file_strptr points to.  If changing\n\t//  ROM path or Charrom, then do the proper updates\n\n\tnewstr = kegs_malloc_str(str);\n\tif(!strptr) {\n\t\treturn;\n\t}\n\tif(*strptr) {\n\t\tfree(*strptr);\n\t}\n\t*strptr = newstr;\n\tif(strptr == &(g_cfg_rom_path)) {\n\t\tprintf(\"Updated ROM file\\n\");\n\t\tload_roms_init_memory();\n\t\tdo_reset();\n\t}\n\tif(strptr == &(g_cfg_charrom_path)) {\n\t\tprintf(\"Updated Char ROM file\\n\");\n\t\tcfg_load_charrom();\n\t}\n\tfor(i = 0; i < 2; i++) {\n\t\tremote_changed = 0;\n\t\tserial_changed = 0;\n\t\tif(strptr == &g_serial_remote_ip[i]) {\n\t\t\tremote_changed = 1;\n\t\t}\n\t\tif(strptr == &g_serial_device[i]) {\n\t\t\tserial_changed = 1;\n\t\t}\n\t\tif(remote_changed || serial_changed) {\n\t\t\tscc_config_changed(i, 0, remote_changed,\n\t\t\t\t\t\t\t\tserial_changed);\n\t\t}\n\t}\n\tif(need_update) {\n\t\tg_config_kegs_update_needed = 1;\n\t\tprintf(\"Set g_config_kegs_update_needed = 1\\n\");\n\t}\n}\n\nvoid\ncfg_int_update(int *iptr, int new_val)\n{\n\tint\told_val, cfg_changed, remote_changed, serial_changed;\n\tint\ti;\n\n\t// Called to handle an integer being changed in the F4 config menus\n\t//  where it's value may need special handling\n\n\told_val = *iptr;\n\t*iptr = new_val;\n\tif(old_val == new_val) {\n\t\treturn;\n\t}\n\tif(iptr == &g_cfg_charrom_pos) {\n\t\tcfg_load_charrom();\n\t}\n\n\tfor(i = 0; i < 2; i++) {\n\t\tremote_changed = 0;\n\t\tserial_changed = 0;\n\t\tcfg_changed = 0;\n\t\tif(iptr == &g_serial_cfg[i]) {\n\t\t\tcfg_changed = 1;\n\t\t}\n\t\tif(iptr == &g_serial_remote_port[i]) {\n\t\t\tremote_changed = 1;\n\t\t}\n\t\tif(iptr == &g_serial_win_device[i]) {\n\t\t\tserial_changed = 1;\n\t\t}\n\t\tif(cfg_changed || remote_changed || serial_changed) {\n\t\t\tscc_config_changed(i, cfg_changed, remote_changed,\n\t\t\t\t\t\t\tserial_changed);\n\t\t}\n\t}\n}\n\nvoid\ncfg_load_charrom()\n{\n\tbyte\tbuffer[4096];\n\tdword64\tdsize, dret;\n\tword32\tupos;\n\tint\tfd;\n\n\tprintf(\"Loading character ROM from: %s\\n\", g_cfg_charrom_path);\n\tfd = open(g_cfg_charrom_path, O_RDONLY | O_BINARY);\n\tif(fd < 0) {\n\t\tprintf(\"Cannot open %s\\n\", g_cfg_charrom_path);\n\t\treturn;\n\t}\n\tdsize = cfg_get_fd_size(fd);\n\tupos = g_cfg_charrom_pos * 0x1000U;\n\tif(dsize < (upos + 0x1000)) {\n\t\tg_cfg_charrom_pos = 0;\n\t\treturn;\n\t}\n\tdret = cfg_read_from_fd(fd, &buffer[0], upos, 4096);\n\tif(dret != 0) {\n\t\tprepare_a2_romx_font(&buffer[0]);\n\t}\n}\n\nvoid\nconfig_load_roms()\n{\n\tstruct stat stat_buf;\n\tconst char **names_ptr;\n\tint\tmore_than_8mb, changed_rom, len, fd, ret;\n\tint\ti;\n\n\tg_rom_version = -1;\n\n\t/* set first entry of g_kegs_rom_names[] to g_cfg_rom_path so that */\n\t/*  it becomes the first place searched. */\n\tg_kegs_rom_names[0] = g_cfg_rom_path;\n\tret = config_setup_kegs_file(&g_cfg_tmp_path[0], CFG_PATH_MAX,\n\t\t\t\t\t\t\t&g_kegs_rom_names[0]);\n\tif(ret == 0) {\n\t\t// Just get out, let config interface select ROM\n\t\tg_config_control_panel = 1;\n\t\tprintf(\"No ROM, set g_config_control_panel=1\\n\");\n\t\treturn;\n\t}\n\tprintf(\"Found ROM at path: %s\\n\", g_cfg_tmp_path);\n\tfd = open(&g_cfg_tmp_path[0], O_RDONLY | O_BINARY);\n\tif(fd < 0) {\n\t\tfatal_printf(\"Open ROM file %s failed:%d, errno:%d\\n\",\n\t\t\t\t&g_cfg_tmp_path[0], fd, errno);\n\t\tg_config_control_panel = 1;\n\t\treturn;\n\t}\n\n\tret = fstat(fd, &stat_buf);\n\tif(ret != 0) {\n\t\tfatal_printf(\"fstat returned %d on fd %d, errno: %d\\n\",\n\t\t\tret, fd, errno);\n\t\tg_config_control_panel = 1;\n\t\treturn;\n\t}\n\n\tlen = (int)stat_buf.st_size;\n\tmemset(&g_rom_fc_ff_ptr[0], 0, 4*65536);\n\t\t\t\t/* Clear banks fc-ff to 0 */\n\tif(len == 32*1024) {\t\t// Apple //e\n\t\tg_rom_version = 0;\n\t\tg_mem_size_base = 128*1024;\n\t\tret = (int)read(fd, &g_rom_fc_ff_ptr[3*65536 + 32768], len);\n\t} else if(len == 128*1024) {\n\t\tg_rom_version = 1;\n\t\tg_mem_size_base = 128*1024;\n\t\tret = (int)read(fd, &g_rom_fc_ff_ptr[2*65536], len);\n\t} else if(len == 256*1024) {\n\t\tg_rom_version = 3;\n\t\tg_mem_size_base = 1024*1024;\n\t\tret = (int)read(fd, &g_rom_fc_ff_ptr[0], len);\n\t} else {\n\t\tfatal_printf(\"The ROM size should be 128K or 256K, this file \"\n\t\t\t\t\t\t\"is %d bytes\\n\", len);\n\t\tg_config_control_panel = 1;\n\t\treturn;\n\t}\n\n\tprintf(\"Read: %d bytes of ROM\\n\", ret);\n\tif(ret != len) {\n\t\tfatal_printf(\"errno: %d\\n\", errno);\n\t\tg_config_control_panel = 1;\n\t\treturn;\n\t}\n\tclose(fd);\n\n\tmemset(&g_rom_cards_ptr[0], 0, 256*16);\n\n\tfor(i = 0; i < 256; i++) {\n\t\t// Place HD PROM in slot 7\n\t\tg_rom_cards_ptr[0x700 + i] = g_rom_c700[i];\n\t\t// g_rom_cards_ptr[0x500 + i] = g_rom_c700[i];\n\t}\n\t/* initialize c600 rom to be diffs from the real ROM, to build-in */\n\t/*  Apple II compatibility without distributing ROMs */\n\tif(g_rom_version == 0) {\t\t\t// Apple //e\n\t\tfor(i = 0; i < 256; i++) {\n\t\t\t// Place Disk II PROM in slot 6\n\t\t\tg_rom_cards_ptr[0x600 + i] = g_rom_fc_ff_ptr[0x38600+i];\n\t\t}\n\t} else {\n\t\tfor(i = 0; i < 256; i++) {\n\t\t\tg_rom_cards_ptr[0x600 + i] =\n\t\t\t\t\tg_rom_fc_ff_ptr[0x3c600 + i] ^\n\t\t\t\t\tg_rom_c600_rom01_diffs[i];\n\t\t}\n\t}\n\tif(g_rom_version >= 3) {\n\t\t/* some patches */\n\t\tg_rom_cards_ptr[0x61b] ^= 0x40;\n\t\tg_rom_cards_ptr[0x61c] ^= 0x33;\n\t\tg_rom_cards_ptr[0x632] ^= 0xc0;\n\t\tg_rom_cards_ptr[0x633] ^= 0x33;\n\t}\n\n\tfor(i = 1; i < 8; i++) {\n\t\tnames_ptr = g_kegs_rom_card_list[i];\n\t\tif(names_ptr == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tif(*names_ptr == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tret = config_setup_kegs_file(&g_cfg_tmp_path[0], CFG_PATH_MAX,\n\t\t\t\t\t\t\t\tnames_ptr);\n\n\t\tif(ret != 0) {\n\t\t\tfd = open(&(g_cfg_tmp_path[0]), O_RDONLY | O_BINARY);\n\t\t\tif(fd < 0) {\n\t\t\t\tfatal_printf(\"Open card ROM file %s failed: %d \"\n\t\t\t\t\t\"err:%d\\n\", &g_cfg_tmp_path[0], fd,\n\t\t\t\t\terrno);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlen = 256;\n\t\t\tret = (int)read(fd, &g_rom_cards_ptr[i*0x100], len);\n\n\t\t\tif(ret != len) {\n\t\t\t\tfatal_printf(\"While reading card ROM %s, file \"\n\t\t\t\t\t\"is too short. (%d) Expected %d bytes, \"\n\t\t\t\t\t\"read %d bytes\\n\", errno, len, ret);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tclose(fd);\n\t\t}\n\t}\n\n\tmore_than_8mb = (g_mem_size_exp > 0x800000);\n\t/* Only do the patch if users wants more than 8MB of expansion mem */\n\n\tchanged_rom = 0;\n\tif(g_rom_version == 1) {\n\t\t/* make some patches to ROM 01 */\n#if 0\n\t\t/* 1: Patch ROM selftest to not do speed test */\n\t\tprintf(\"Patching out speed test failures from ROM 01\\n\");\n\t\tg_rom_fc_ff_ptr[0x3785a] = 0x18;\n\t\tchanged_rom = 1;\n#endif\n\n#if 0\n\t\t/* 2: Patch ROM selftests not to do tests 2,4 */\n\t\t/* 0 = skip, 1 = do it, test 1 is bit 0 of LSByte */\n\t\tg_rom_fc_ff_ptr[0x371e9] = 0xf5;\n\t\tg_rom_fc_ff_ptr[0x371ea] = 0xff;\n\t\tchanged_rom = 1;\n#endif\n\n\t\tif(more_than_8mb) {\n\t\t\t/* Geoff Weiss patch to use up to 14MB of RAM */\n\t\t\tg_rom_fc_ff_ptr[0x30302] = 0xdf;\n\t\t\tg_rom_fc_ff_ptr[0x30314] = 0xdf;\n\t\t\tg_rom_fc_ff_ptr[0x3031c] = 0x00;\n\t\t\tchanged_rom = 1;\n\t\t}\n\n\t\t/* Patch ROM selftest to not do ROM cksum if any changes*/\n\t\tif(changed_rom) {\n\t\t\tg_rom_fc_ff_ptr[0x37a06] = 0x18;\n\t\t\tg_rom_fc_ff_ptr[0x37a07] = 0x18;\n\t\t}\n\t} else if(g_rom_version == 3) {\n\t\t/* patch ROM 03 */\n\t\tprintf(\"Patching ROM 03 smartport bug\\n\");\n\t\t/* 1: Patch Smartport code to fix a stupid bug */\n\t\t/*  that causes it to write the IWM status reg into c036, */\n\t\t/*  which is the system speed reg...it's \"safe\" since */\n\t\t/*  IWM status reg bit 4 must be 0 (7MHz)..., otherwise */\n\t\t/*  it might have turned on shadowing in all banks! */\n\t\tg_rom_fc_ff_ptr[0x357c9] = 0x00;\n\t\tchanged_rom = 1;\n\n#if 0\n\t\t/* patch ROM 03 to not to speed test */\n\t\t/*  skip fast speed test */\n\t\tg_rom_fc_ff_ptr[0x36ad7] = 0x18;\n\t\tg_rom_fc_ff_ptr[0x36ad8] = 0x18;\n\t\tchanged_rom = 1;\n#endif\n\n#if 0\n\t\t/*  skip slow speed test */\n\t\tg_rom_fc_ff_ptr[0x36ae7] = 0x18;\n\t\tg_rom_fc_ff_ptr[0x36ae8] = 0x6b;\n\t\tchanged_rom = 1;\n#endif\n\n#if 0\n\t\t/* 4: Patch ROM 03 selftests not to do tests 1-4 */\n\t\tg_rom_fc_ff_ptr[0x364a9] = 0xf0;\n\t\tg_rom_fc_ff_ptr[0x364aa] = 0xff;\n\t\tchanged_rom = 1;\n#endif\n\n\t\t/* ROM tests are in ff/6403-642x, where 6403 = addr of */\n\t\t/*  test 1, etc. */\n\n\t\tif(more_than_8mb) {\n\t\t\t/* Geoff Weiss patch to use up to 14MB of RAM */\n\t\t\tg_rom_fc_ff_ptr[0x30b] = 0xdf;\n\t\t\tg_rom_fc_ff_ptr[0x31d] = 0xdf;\n\t\t\tg_rom_fc_ff_ptr[0x325] = 0x00;\n\t\t\tchanged_rom = 1;\n\t\t}\n\n\t\tif(changed_rom) {\n\t\t\t/* patch ROM 03 selftest to not do ROM cksum */\n\t\t\tg_rom_fc_ff_ptr[0x36cb0] = 0x18;\n\t\t\tg_rom_fc_ff_ptr[0x36cb1] = 0x18;\n\t\t}\n\n\t}\n}\n\nvoid\nconfig_parse_config_kegs_file()\n{\n\tchar\t*bufptr;\n\tconst char *str;\n\tdword64\tdsize;\n\tint\tfd, pos, start, size, last_c, line, ret, c, maxlen;\n\tint\ti;\n\n\tprintf(\"Parsing config.kegs file: %s\\n\", g_config_kegs_name);\n\n\tclk_bram_zero();\n\n\tg_highest_smartport_unit = -1;\n\n\tcfg_get_base_path(&g_cfg_cwd_str[0], g_config_kegs_name, 0);\n\tif(g_cfg_cwd_str[0] != 0) {\n\t\tret = chdir(&g_cfg_cwd_str[0]);\n\t\tif(ret != 0) {\n\t\t\tprintf(\"chdir to %s, errno:%d\\n\", g_cfg_cwd_str, errno);\n\t\t}\n\t\t// Do basename(g_config_kegs_name)--on it's own buffer\n\t\tstr = cfg_str_basename(g_config_kegs_name);\n\t\tmaxlen = sizeof(g_config_kegs_name);\n\t\tcfg_strncpy(&g_config_kegs_name[0], str, maxlen);\n\t}\n\n\t// In any case, copy the current directory path to g_cfg_cwd_str\n\t(void)!getcwd(&g_cfg_cwd_str[0], CFG_PATH_MAX);\n\tprintf(\"CWD is now: %s\\n\", &g_cfg_cwd_str[0]);\n\n\tfd = open(g_config_kegs_name, O_RDONLY | O_BINARY);\n\tdsize = 0;\n\tif(fd >= 0) {\n\t\tdsize = cfg_get_fd_size(fd);\n\t}\n\tif((fd < 0) || (dsize >= (1 << 30))) {\n\t\tfatal_printf(\"cannot open config.kegs at %s, or it is too \"\n\t\t\t\"large!  Stopping!\\n\", g_config_kegs_name);\n\t\tmy_exit(3);\n\t\treturn;\n\t}\n\tsize = (int)dsize;\n\tbufptr = malloc(size + 2);\n\tret = (int)cfg_read_from_fd(fd, (byte *)bufptr, 0, size);\n\tclose(fd);\n\tif(ret != size) {\n\t\tfree(bufptr);\n\t\tfatal_printf(\"Could not read config.kegs at %s\\n\",\n\t\t\t\t\t\t\tg_config_kegs_name);\n\t\tmy_exit(3);\n\t\treturn;\n\t}\n\tbufptr[size] = 0;\t\t// Ensure it's null terminated\n\n\tline = 0;\n\tpos = 0;\n\tlast_c = 0;\n\twhile(pos < size) {\n\t\tline++;\n\t\tif((bufptr[pos] == '\\n') && (last_c == '\\r')) {\n\t\t\t// CR,LF, just eat the LF\n\t\t\tpos++;\n\t\t}\n\t\tstart = pos;\n\t\twhile(pos < size) {\n\t\t\tc = bufptr[pos];\n\t\t\tif((c == 0) || (c == '\\n') || (c == '\\r')) {\n\t\t\t\tlast_c = c;\n\t\t\t\tbufptr[pos] = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tpos++;\n\t\t}\n\t\tcfg_parse_one_line(&bufptr[start], line);\n\t\tpos++;\n\t}\n\n\tfree(bufptr);\n\n\t// And now do command line argument overrides\n\tfor(i = 0; i < g_cfg_argv_num_overrides; i++) {\n\t\tprintf(\"Doing override %d, %s\\n\", i, g_cfg_argv_overrides[i]);\n\t\tcfg_parse_one_line(g_cfg_argv_overrides[i], 1001 + i);\n\t\tg_config_kegs_update_needed = 1;\n\t}\n}\n\nvoid\ncfg_parse_one_line(char *buf, int line)\n{\n\tCfg_menu *menuptr;\n\tCfg_defval *defptr;\n\tint\t*iptr;\n\tconst char *nameptr;\n\tint\tpos, num_equals, type, val, c, len;\n\tint\ti;\n\n\t// warning: modifies memory of bufptr (turns spaces to nulls)\n\tif(line) {\t\t// Avoid unused parameter warning\n\t}\n\n\tlen = (int)strlen(buf);\n\tif(len <= 1) {\t\t// Not a valid line, just get out\n\t\treturn;\n\t}\n\n\t// printf(\"disk_conf[%d]: %s\\n\", line, buf);\n\tif(buf[0] == '#') {\n\t\tiwm_printf(\"Skipping comment\\n\");\n\t\treturn;\n\t}\n\n\tpos = 0;\n\n\twhile((pos < len) && (buf[pos] == ' ' || buf[pos] == '\\t') ) {\n\t\tpos++;\t\t// Eat whitespace\n\t}\n\tif(pos >= len) {\n\t\treturn;\t\t// blank line\n\t}\n\tif((pos + 5) < len) {\n\t\tc = buf[pos+1];\t\t// Slot number\n\t\tif((buf[pos] == 's') && (buf[pos+2] == 'd') &&\n\t\t\t\t\t(c >= '5' && c <= '7')) {\n\t\t\t// looks like s5d1 through s7d15, parse that\n\t\t\tcfg_parse_sxdx(buf, pos, len);\n\t\t\treturn;\n\t\t}\n\t}\n\n// parse buf from pos into option, \"=\" and then \"rest\"\n\n\tif(strncmp(&buf[pos], \"bram\", 4) == 0) {\n\t\tcfg_parse_bram(buf, pos+4, len);\n\t\treturn;\n\t}\n\n\t// find \"name\" as first contiguous string\n\t//printf(\"...parse_option: line %d, %s (%s) len:%d\\n\", line, buf,\n\t//\t\t\t\t\t\t&buf[pos], len);\n\n\tnameptr = &buf[pos];\n\twhile(pos < len) {\n\t\tc = buf[pos];\n\t\tif((c == 0) || (c == ' ') || (c == '\\t') || (c == '\\n') ||\n\t\t\t\t\t\t\t\t(c == '=')) {\n\t\t\tbreak;\n\t\t}\n\t\tpos++;\n\t}\n\tbuf[pos] = 0;\n\tif(strcmp(nameptr, \"rom\") == 0) {\n\t\t// Translate argument of \"-rom\" to \"g_cfg_rom_path\"\n\t\tnameptr = \"g_cfg_rom_path\";\n\t}\n\tpos++;\n\n\t// Eat up all whitespace and '='\n\tnum_equals = 0;\n\twhile(pos < len) {\n\t\tc = buf[pos];\n\t\tif((c == '=') && (num_equals == 0)) {\n\t\t\tpos++;\n\t\t\tnum_equals++;\n\t\t} else if(c == ' ' || c == '\\t') {\n\t\t\tpos++;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* Look up nameptr to find type */\n\ttype = -1;\n\tdefptr = 0;\n\tmenuptr = 0;\n\tfor(i = 0; i < g_cfg_defval_index; i++) {\n\t\tdefptr = &(g_cfg_defvals[i]);\n\t\tmenuptr = defptr->menuptr;\n\t\tif(strcmp(menuptr->name_str, nameptr) == 0) {\n\t\t\ttype = menuptr->cfgtype;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tswitch(type) {\n\tcase CFGTYPE_INT:\n\t\t/* use strtol */\n\t\tval = (int)strtol(&buf[pos], 0, 0);\n\t\tiptr = (int *)menuptr->ptr;\n\t\tcfg_int_update(iptr, val);\n\t\tbreak;\n\tcase CFGTYPE_FILE:\n\tcase CFGTYPE_STR:\n\t\tcfg_file_update_ptr(menuptr->ptr, &buf[pos], 0);\n\t\tbreak;\n\tdefault:\n\t\tprintf(\"Config file variable %s is unknown type: %d\\n\",\n\t\t\tnameptr, type);\n\t}\n}\n\nvoid\ncfg_parse_bram(char *buf, int pos, int len)\n{\n\tword32\tval;\n\tint\tbram_num, offset;\n\n\t// Format: \"bram1[00] = xx yy...\" or \"bram3[00] = xx yy ...\"\n\t// pos = position just after \"bram\"\n\tif((len < (pos+5)) || (buf[pos+1] != '[') || (buf[pos+4] != ']')) {\n\t\tfatal_printf(\"While reading config.kegs, found malformed bram \"\n\t\t\t\"statement: %s\\n\", buf);\n\t\treturn;\n\t}\n\tbram_num = buf[pos] - '0';\n\tif((bram_num != 1) && (bram_num != 3)) {\n\t\tfatal_printf(\"While reading config.kegs, found bad bram \"\n\t\t\t\"num: %s\\n\", buf);\n\t\treturn;\n\t}\n\n\tbram_num = bram_num >> 1;\t// turn 3->1 and 1->0\n\n\toffset = (int)strtoul(&(buf[pos+2]), 0, 16);\n\tpos += 5;\n\twhile(pos < len) {\n\t\tif((buf[pos] == ' ') || (buf[pos] == '\\t') ||\n\t\t\t\t(buf[pos] == 0x0a) || (buf[pos] == 0x0d) ||\n\t\t\t\t(buf[pos] == '=')) {\n\t\t\tpos++;\n\t\t\tcontinue;\n\t\t}\n\t\tval = (word32)strtoul(&buf[pos], 0, 16);\t// As hex\n\t\tclk_bram_set(bram_num, offset, val);\n\t\toffset++;\n\t\tpos += 2;\n\t}\n}\n\nvoid\ncfg_parse_sxdx(char *buf, int pos, int len)\n{\n\tchar\t*name_ptr, *partition_name;\n\tword32\tdynamic_blocks;\n\tint\tpart_num, ejected, slot, drive, c;\n\n\tslot = buf[pos+1] - '0';\n\tdrive = buf[pos+3] - '0';\n\n\t/* skip over slot, drive */\n\tpos += 4;\n\tif((buf[pos] >= '0') && (buf[pos] <= '9')) {\n\t\t// Second digit of drive is valid\n\t\tdrive = (drive * 10) + buf[pos] - '0';\n\t\tpos++;\n\t}\n\n\tdrive--;\t// make sxd1 mean index 0\n\n\twhile((pos < len) && ((buf[pos] == ' ') || (buf[pos] == '\\t') ||\n\t\t\t\t\t\t\t(buf[pos] == '=')) ) {\n\t\tpos++;\n\t}\n\n\tejected = 0;\n\tif(buf[pos] == '#') {\t// disk is ejected, but read info anyway\n\t\tejected = 1;\n\t\tpos++;\n\t}\n\n\tpartition_name = 0;\n\tpart_num = -1;\n\tdynamic_blocks = 0;\n\tif(buf[pos] == ':') {\t\t// yup, it's got a partition name!\n\t\tpos++;\n\t\tpartition_name = &buf[pos];\n\t\twhile((pos < len) && (buf[pos] != ':')) {\n\t\t\tpos++;\n\t\t}\n\t\tbuf[pos] = 0;\t/* null terminate partition name */\n\t\tpos++;\n\t}\n\tc = buf[pos];\n\tif((c == ';') || (c == '@')) {\n\t\tpos++;\n\t\t// ; - partition number;  @ - Dynamic ProDOS dir size\n\t\tpart_num = 0;\n\t\twhile((pos < len) && (buf[pos] != ':')) {\n\t\t\tpart_num = (10*part_num) + buf[pos] - '0';\n\t\t\tpos++;\n\t\t}\n\t\tpos++;\n\t\tif(c == '@') {\t\t// Dynamic ProDOS directory\n\t\t\tdynamic_blocks = part_num * 2;\n\t\t\tpart_num = -1;\n\t\t}\n\t}\n\n\t/* Get filename */\n\tname_ptr = &(buf[pos]);\n\tif((pos >= len) || (name_ptr[0] == 0)) {\n\t\treturn;\n\t}\n\n\tinsert_disk(slot, drive, name_ptr, ejected, partition_name,\n\t\t\t\t\t\tpart_num, dynamic_blocks);\n}\n\nvoid\nconfig_generate_config_kegs_name(char *outstr, int maxlen, Disk *dsk,\n\t\tint with_extras)\n{\n\tchar\t*str;\n\n\tstr = outstr;\n\n\tif(with_extras) {\n\t\tif(dsk->fd < 0) {\n\t\t\tsnprintf(str, maxlen - (str - outstr), \"#\");\n\t\t\tstr = &outstr[strlen(outstr)];\n\t\t}\n\t\tif(dsk->dynapro_blocks) {\n\t\t\tsnprintf(str, maxlen - (str - outstr), \"@%d:\",\n\t\t\t\t\t(dsk->dynapro_blocks + 1) / 2);\n\t\t\tstr = &outstr[strlen(outstr)];\n\t\t} else if(dsk->partition_name != 0) {\n\t\t\tsnprintf(str, maxlen - (str - outstr), \":%s:\",\n\t\t\t\t\t\t\tdsk->partition_name);\n\t\t\tstr = &outstr[strlen(outstr)];\n\t\t} else if(dsk->partition_num >= 0) {\n\t\t\tsnprintf(str, maxlen - (str - outstr), \";%d:\",\n\t\t\t\t\t\t\tdsk->partition_num);\n\t\t\tstr = &outstr[strlen(outstr)];\n\t\t}\n\t}\n\tsnprintf(str, maxlen - (str - outstr), \"%s\", dsk->name_ptr);\n}\n\nchar *\nconfig_write_config_kegs_file(int get_status)\n{\n\tFILE\t*fconf;\n\tDisk\t*dsk;\n\tCfg_defval *defptr;\n\tCfg_menu *menuptr;\n\tchar\t*curstr, *defstr;\n\tint\tdefval, curval, type, slot, drive;\n\tint\ti;\n\n\tif(get_status) {\n\t\treturn 0;\n\t}\n\tprintf(\"Writing config.kegs file to %s\\n\", g_config_kegs_name);\n\n\tfconf = fopen(g_config_kegs_name, \"w+\");\n\tif(fconf == 0) {\n\t\thalt_printf(\"cannot open %s!  Stopping!\\n\", g_config_kegs_name);\n\t\treturn 0;\n\t}\n\n\tfprintf(fconf, \"# KEGS configuration file version %s\\n\",\n\t\t\t\t\t\tg_kegs_version_str);\n\n\tfor(i = 0; i < MAX_C7_DISKS + 4; i++) {\n\t\tslot = 7;\n\t\tdrive = i - 4;\n\t\tif(i < 4) {\n\t\t\tslot = (i >> 1) + 5;\n\t\t\tdrive = i & 1;\n\t\t}\n\t\tif(drive == 0) {\n\t\t\tfprintf(fconf, \"\\n\");\t/* an extra blank line */\n\t\t}\n\n\t\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\t\tif(dsk->name_ptr == 0 && (i > 4)) {\n\t\t\t/* No disk, not even ejected--just skip */\n\t\t\tcontinue;\n\t\t}\n\t\tfprintf(fconf, \"s%dd%d = \", slot, drive + 1);\n\t\tif(dsk->name_ptr == 0) {\n\t\t\tfprintf(fconf, \"\\n\");\n\t\t\tcontinue;\n\t\t}\n\t\tconfig_generate_config_kegs_name(&g_cfg_tmp_path[0],\n\t\t\t\t\t\t\tCFG_PATH_MAX, dsk, 1);\n\t\tfprintf(fconf, \"%s\\n\", &g_cfg_tmp_path[0]);\n\t}\n\n\tfprintf(fconf, \"\\n\");\n\n\t/* See if any variables are different than their default */\n\tfor(i = 0; i < g_cfg_defval_index; i++) {\n\t\tdefptr = &(g_cfg_defvals[i]);\n\t\tmenuptr = defptr->menuptr;\n\t\tdefval = defptr->intval;\n\t\ttype = menuptr->cfgtype;\n\t\tif(type == CFGTYPE_INT) {\n\t\t\tcurval = *((int *)menuptr->ptr);\n\t\t\tif(curval != defval) {\n\t\t\t\tfprintf(fconf, \"%s = %d\\n\", menuptr->name_str,\n\t\t\t\t\t\t\t\tcurval);\n\t\t\t}\n\t\t}\n\t\tif((type == CFGTYPE_FILE) || (type == CFGTYPE_STR)) {\n\t\t\tcurstr = *((char **)menuptr->ptr);\n\t\t\tdefstr = *((char **)menuptr->defptr);\n\t\t\tif(strcmp(curstr, defstr) != 0) {\n\t\t\t\tfprintf(fconf, \"%s = %s\\n\", menuptr->name_str,\n\t\t\t\t\t\t\t\tcurstr);\n\t\t\t}\n\t\t}\n\t}\n\n\tfprintf(fconf, \"\\n\");\n\n\t/* write bram state */\n\tclk_write_bram(fconf);\n\n\tfclose(fconf);\n\n\tg_config_kegs_update_needed = 0;\n\treturn 0;\n}\n\nvoid\ninsert_disk(int slot, int drive, const char *name, int ejected,\n\t\tconst char *partition_name, int part_num, word32 dynamic_blocks)\n{\n\tbyte\tbuf_2img[512];\n\tDisk\t*dsk;\n\tchar\t*name_ptr, *part_ptr;\n\tdword64\tdsize, dunix_pos, exp_dsize, dtmp;\n\tword32\tlen_bits, save_ftrack;\n\tint\timage_type, part_len, ret, locked, len, is_po, name_len;\n\tint\ti;\n\n\tg_config_kegs_update_needed = 1;\n\n\tif((slot < 5) || (slot > 7)) {\n\t\tfatal_printf(\"Invalid slot for insertiing disk: %d\\n\", slot);\n\t\treturn;\n\t}\n\tif(drive < 0 || ((slot == 7) && (drive >= MAX_C7_DISKS)) ||\n\t\t\t\t\t((slot < 7) && (drive > 1))) {\n\t\tfatal_printf(\"Invalid drive for inserting disk: %d\\n\", drive);\n\t\treturn;\n\t}\n\n\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\n#if 1\n\tprintf(\"Inserting disk %s (%s or %d) in slot %d, drive: %d, \"\n\t\t\"dyna_blocks:%d\\n\", name, partition_name, part_num, slot, drive,\n\t\tdynamic_blocks);\n#endif\n\n\t// DO NOT change dsk->just_ejected.  If a disk was just ejected, then\n\t//  leave it alone.  Otherwise, if we are a newly inserted disk,\n\t//  it should already be 0, so leave it alone\n\t//dsk->just_ejected = 1;\n\n\tif(dsk->fd >= 0) {\n\t\tiwm_eject_disk(dsk);\n\t}\n\n\t/* Before opening, make sure no other mounted disk has this name */\n\t/* If so, unmount it */\n\tif(!ejected) {\n\t\tfor(i = 0; i < 2; i++) {\n\t\t\tiwm_eject_named_disk(5, i, name, partition_name);\n\t\t\tiwm_eject_named_disk(6, i, name, partition_name);\n\t\t}\n\t\tfor(i = 0; i < MAX_C7_DISKS; i++) {\n\t\t\tiwm_eject_named_disk(7, i, name, partition_name);\n\t\t}\n\t}\n\n\t/* free old name_ptr, partition_name */\n\tfree(dsk->name_ptr);\n\tfree(dsk->partition_name);\n\tdsk->name_ptr = 0;\n\tdsk->partition_name = 0;\n\n\tname_ptr = kegs_malloc_str(name);\n\tdsk->name_ptr = name_ptr;\n\tname_len = (int)strlen(dsk->name_ptr);\n\n\tpart_len = 0;\n\tpart_ptr = 0;\n\tif(partition_name != 0) {\n\t\tpart_ptr = kegs_malloc_str(partition_name);\n\t\tpart_len = (int)strlen(part_ptr);\n\t\tdsk->partition_name = part_ptr;\n\t}\n\tdsk->partition_num = part_num;\n\tdsk->dynapro_blocks = dynamic_blocks;\n\n\tiwm_printf(\"Opening up disk image named: %s\\n\", name_ptr);\n\n\tif(ejected) {\n\t\t/* just get out of here */\n\t\tdsk->fd = -1;\n\t\treturn;\n\t}\n\n\tdsk->fd = -1;\n\tdsk->raw_data = 0;\n\tdsk->image_type = 0;\n\tdsk->dimage_start = 0;\n\tdsk->dimage_size = 0;\n\tdsk->write_prot = 0;\n\tdsk->write_through_to_unix = 1;\n\timage_type = 0;\n\tlocked = 0;\n\n\tif(dynamic_blocks) {\n\t\tret = dynapro_mount(dsk, name_ptr, dynamic_blocks);\n\t\tif(ret < 0) {\n\t\t\tiwm_eject_disk(dsk);\n\t\t\treturn;\n\t\t}\n\t\timage_type = DSK_TYPE_DYNAPRO;\n\t\tprintf(\"After dynapro_mount, write_through:%d\\n\",\n\t\t\t\t\t\tdsk->write_through_to_unix);\n\t}\n\tif((partition_name != 0) || (part_num >= 0)) {\n\t\tret = cfg_partition_find_by_name_or_num(dsk, partition_name,\n\t\t\t\t\t\t\t\tpart_num);\n\t\tprintf(\"partition %s (num %d) mounted, wr_prot: %d, ret:%d\\n\",\n\t\t\t\tpartition_name, part_num, dsk->write_prot, ret);\n\n\t\tif(ret < 0) {\n\t\t\tiwm_eject_disk(dsk);\n\t\t\treturn;\n\t\t}\n\t\tlocked = dsk->write_prot;\n\t\tif(dsk->raw_data) {\n\t\t\t// .zip file or something similar.  Do name matching on\n\t\t\t//  partition name\n\t\t\tname_len = part_len;\n\t\t\tname_ptr = part_ptr;\n\t\t\tlocked = 1;\n\t\t}\n\t}\n\n\tif((name_len > 3) && !image_type &&\n\t\t\t\t!cfgcasecmp(\".gz\", &name_ptr[name_len - 3])) {\n\t\t// it's gzip'ed, try to gunzip it to dsk->raw_data\n\t\tundeflate_gzip(dsk, name_ptr);\n\n\t\tlocked = 1;\n\t\tdsk->dimage_start = 0;\n\t\tdsk->dimage_size = dsk->raw_dsize;\n\t\tname_len -= 3;\t\t// So .dsk, .po look for correct chars\n\t}\n\n\tif((name_len > 4) && !image_type &&\n\t\t\t\t!cfgcasecmp(\".bz2\", &name_ptr[name_len - 4])) {\n\t\t// it's bzip2'ed, try to bunzip2 it to dsk->raw_data\n\t\tconfig_file_to_pipe(dsk, \"bunzip2\", name_ptr);\n\t\tlocked = 1;\n\n\t\t// Reduce name_len by 4 so that subsequent compares for .po\n\t\t//  look at the correct chars\n\t\tname_len -= 4;\n\t}\n\n\tif((name_len > 4) && !image_type &&\n\t\t\t\t!cfgcasecmp(&name_ptr[name_len - 4], \".sdk\")) {\n\t\t// it's a ShrinkIt archive with a disk image in it\n\t\tunshk(dsk, name_ptr);\n\t\tlocked = 1;\n\t\timage_type = DSK_TYPE_PRODOS;\n\t\tprintf(\"dsk->fd:%d dsk->raw_data:%p, raw_dsize:%lld\\n\", dsk->fd,\n\t\t\t\t\t\tdsk->raw_data, dsk->raw_dsize);\n\t\tdsk->dimage_start = 0;\n\t\tdsk->dimage_size = dsk->raw_dsize;\n\t}\n\n\tif((name_len > 4) && !image_type && dsk->disk_525 &&\n\t\t\t\t!cfgcasecmp(\".nib\", &name_ptr[name_len-4])) {\n\t\t// Old, obsolete .nib 5.25\" nibblized format.  Support is\n\t\t//  read-only\n\t\timage_type = DSK_TYPE_NIB;\n\t\tlocked = 1;\n\t}\n\n\tif((dsk->fd < 0) && !locked && !dynamic_blocks) {\n\t\tdsk->fd = open(name_ptr, O_RDWR | O_BINARY, 0x1b6);\n\t}\n\n\tif((dsk->fd < 0) && !dynamic_blocks) {\n\t\tprintf(\"Trying to open %s read-only, errno: %d\\n\", name_ptr,\n\t\t\t\t\t\t\t\terrno);\n\t\tdsk->fd = open(name_ptr, O_RDONLY | O_BINARY, 0x1b6);\n\t\tlocked = 2;\n\t}\n\n\tiwm_printf(\"open returned: %d\\n\", dsk->fd);\n\n\tif((dsk->fd < 0) && !dynamic_blocks) {\n\t\tfatal_printf(\"Disk image %s does not exist!\\n\", name_ptr);\n\t\tfree(dsk->raw_data);\n\t\treturn;\n\t}\n\n\t//printf(\"Checking if it's woz, name_ptr:%s. %d vs %d\\n\", name_ptr,\n\t//\tname_len, (int)strlen(name_ptr));\n\tif((name_len > 4) && !image_type &&\n\t\t\t\t!cfgcasecmp(&name_ptr[name_len - 4], \".woz\")) {\n\t\t// it's a WOZ applesauce disk image\n\t\timage_type = DSK_TYPE_WOZ;\n\t\tprintf(\"It is woz!\\n\");\n\t}\n\n\tif(locked) {\n\t\tdsk->write_prot = locked;\n\t}\n\n\tsave_ftrack = dsk->cur_frac_track;\t/* save arm position */\n\n\t/* See if it is in 2IMG format */\n\tif(dsk->raw_data) {\n\t\t// Just do a copy from raw_data\n\t\tfor(i = 0; i < 512; i++) {\n\t\t\tbuf_2img[i] = dsk->raw_data[i];\n\t\t}\n\t\tdsize = dsk->raw_dsize;\n\t\tif(!dsk->dynapro_info_ptr) {\n\t\t\tdsk->write_through_to_unix = 0;\n\t\t}\n\t} else {\n\t\tret = (int)read(dsk->fd, (char *)&buf_2img[0], 512);\n\t\tdsize = cfg_get_fd_size(dsk->fd);\n\t}\n\n#if 0\n\t/* Try to guess that there is a Mac Binary header of 128 bytes */\n\t/* See if image size & 0xfff = 0x080 which indicates extra 128 bytes */\n\tif(((dsize & 0xfff) == 0x080) && (dsize < (801*1024)) && !image_type) {\n\t\tprintf(\"Assuming Mac Binary header on %s\\n\", dsk->name_ptr);\n\t\tdsk->dimage_start += 0x80;\n\t\tdsize -= 0x80;\n\t}\n#endif\n\n\tdsk->dimage_size = dsize;\n\n\tif(!image_type && (buf_2img[0] == '2') && (buf_2img[1] == 'I') &&\n\t\t\t\t(buf_2img[2] == 'M') && (buf_2img[3] == 'G')) {\n\t\t/* It's a 2IMG disk */\n\t\tprintf(\"Image named %s is in 2IMG format\\n\", dsk->name_ptr);\n\t\timage_type = DSK_TYPE_PRODOS;\n\n\t\tif(buf_2img[12] == 0) {\n\t\t\tprintf(\"2IMG is in DOS 3.3 sector order\\n\");\n\t\t\timage_type = DSK_TYPE_DOS33;\n\t\t}\n\t\tif(buf_2img[19] & 0x80) {\n\t\t\t/* disk is locked */\n\t\t\tprintf(\"2IMG is write protected\\n\");\n\t\t\tif(dsk->write_prot == 0) {\n\t\t\t\tdsk->write_prot = 1;\n\t\t\t}\n\t\t}\n\t\tif((buf_2img[17] & 1) && (dsk->image_type == DSK_TYPE_DOS33)) {\n\t\t\tdsk->vol_num = buf_2img[16];\n\t\t\tprintf(\"Setting DOS 3.3 vol num to %d\\n\", dsk->vol_num);\n\t\t}\n\t\t//\tSome 2IMG archives have the size byte reversed\n\t\tdsize = (buf_2img[31] << 24) + (buf_2img[30] << 16) +\n\t\t\t\t(buf_2img[29] << 8) + buf_2img[28];\n\t\tdunix_pos = (buf_2img[27] << 24) + (buf_2img[26] << 16) +\n\t\t\t\t(buf_2img[25] << 8) + buf_2img[24];\n\t\tif(dsize == 0x800c00) {\n\t\t\t//\tByte reversed 0x0c8000\n\t\t\tdsize = 0x0c8000;\n\t\t}\n\t\tif(dsize == 0) {\n\t\t\t/* Sweet-16 makes some images with size == 0 */\n\t\t\t/* Example: Prosel from */\n\t\t\t/*  www.whatisthe2gs.apple2.org.za/the_ring/ */\n\t\t\tif(buf_2img[12] == 1) {\n\t\t\t\t/* then get the size from 0x14 in blocks */\n\t\t\t\tdsize = (buf_2img[23] << 24) +\n\t\t\t\t\t(buf_2img[22] << 16) +\n\t\t\t\t\t(buf_2img[21] << 8) + buf_2img[20];\n\t\t\t\tdsize = dsize * 512;\t/* it was blocks */\n\t\t\t}\n\t\t}\n\t\tdsk->dimage_start = dunix_pos;\n\t\tdsk->dimage_size = dsize;\n\t}\n\texp_dsize = 800*1024;\n\tdsk->fbit_mult = 256;\n\tif(dsk->disk_525) {\n\t\texp_dsize = 140*1024;\n\t\tdsk->fbit_mult = 128;\n\t}\n\tif(!image_type) {\n\t\t/* See if it might be the Mac diskcopy format */\n\t\tdtmp = cfg_detect_dc42(&buf_2img[0], dsize, exp_dsize, slot);\n\t\tif(dtmp != 0) {\n\t\t\t// It's diskcopy 4.2\n\t\t\tprintf(\"Image named %s is in Mac diskcopy format\\n\",\n\t\t\t\t\t\t\t\tdsk->name_ptr);\n\t\t\tdsk->dimage_start += 0x54;\n\t\t\tdsk->dimage_size = dtmp;\n\t\t\timage_type = DSK_TYPE_PRODOS;\t/* ProDOS */\n\t\t}\n\t}\n\tif(!image_type) {\n\t\t/* Assume raw image */\n\t\tdsk->dimage_size = dsize;\n\t\timage_type = DSK_TYPE_PRODOS;\n\t\tis_po = (name_len > 3) &&\n\t\t\t\t!cfgcasecmp(\".po\", &name_ptr[name_len-3]);\n\t\tif(dsk->disk_525) {\n\t\t\timage_type = DSK_TYPE_DOS33;\n\t\t\tif(is_po) {\n\t\t\t\timage_type = DSK_TYPE_PRODOS;\n\t\t\t}\n\t\t}\n\t}\n\n\tdsk->image_type = image_type;\n\tdsk->disk_dirty = 0;\n\tdsk->cur_fbit_pos = 0;\n\tdsk->cur_track_bits = 0;\n\tdsk->cur_trk_ptr = 0;\n\n\tif(image_type == DSK_TYPE_WOZ) {\n\t\t// Special handling\n\t\tret = woz_open(dsk, 0);\n\t\tif(!ret) {\n\t\t\tiwm_eject_disk(dsk);\n\t\t\treturn;\n\t\t}\n\t} else if(dsk->smartport) {\n\t\tg_highest_smartport_unit = MY_MAX(dsk->drive,\n\t\t\t\t\t\tg_highest_smartport_unit);\n\n\t\tiwm_printf(\"adding smartport device[%d], img_sz:%08llx\\n\",\n\t\t\tdsk->drive, dsk->dimage_size);\n\t} else if(dsk->disk_525) {\n\t\tdunix_pos = dsk->dimage_start;\n\t\tdsize = dsk->dimage_size;\n\t\tdisk_set_num_tracks(dsk, 4*35);\n\t\tlen = 0x1000;\n\t\tif(dsk->image_type == DSK_TYPE_NIB) {\n\t\t\t// Weird .nib format, has no sync bits\n\t\t\tlen = (int)(dsk->dimage_size / 35);\n\t\t\tfor(i = 0; i < 35; i++) {\n\t\t\t\tdisk_unix_to_nib(dsk, 4*i, dunix_pos, len,\n\t\t\t\t\t\t\t\tlen * 8, 0);\n\t\t\t\tdunix_pos += len;\n\t\t\t}\n\t\t} else {\n\t\t\tfor(i = 0; i < 35; i++) {\n\t\t\t\tlen_bits = iwm_get_default_track_bits(dsk, 4*i);\n\t\t\t\tdisk_unix_to_nib(dsk, 4*i, dunix_pos, len,\n\t\t\t\t\t\t\t\tlen_bits, 0);\n\t\t\t\tdunix_pos += len;\n\t\t\t}\n\t\t}\n\t\tif(dsize != (dword64)35*len) {\n\t\t\tfatal_printf(\"Disk 5.25 error: size is %lld, not %d.  \"\n\t\t\t\t\"Will try to mount anyway\\n\", dsize, 35*len);\n\t\t}\n\t} else {\n\t\t/* disk_35 */\n\t\tdunix_pos = dsk->dimage_start;\n\t\tdsize = dsk->dimage_size;\n\t\tif(dsize != 800*1024) {\n\t\t\tprintf(\"Disk 3.5 Drive %d (Image File: %s), Error: \"\n\t\t\t\t\"size is %lld, not 800K.  Will try to mount \"\n\t\t\t\t\"anyway\\n\", drive+1, name, dsize);\n\t\t}\n\t\tdisk_set_num_tracks(dsk, 2*80);\n\t\tfor(i = 0; i < 2*80; i++) {\n\t\t\tlen = g_track_bytes_35[i >> 5];\n\t\t\tlen_bits = iwm_get_default_track_bits(dsk, i);\n\t\t\tiwm_printf(\"Trk: %d.%d = unix: %08llx, %04x, %04x\\n\",\n\t\t\t\ti>>1, i & 1, dunix_pos, len, len_bits);\n\t\t\tdisk_unix_to_nib(dsk, i, dunix_pos, len, len_bits, 0);\n\t\t\tdunix_pos += len;\n\n\t\t\tiwm_printf(\" trk_bits:%05x\\n\", dsk->trks[i].track_bits);\n\t\t}\n\t}\n\n\tiwm_move_to_ftrack(dsk, save_ftrack, 0, 0);\n}\n\ndword64\ncfg_detect_dc42(byte *bptr, dword64 dsize, dword64 exp_dsize, int slot)\n{\n\tdword64\tddata_size, dtag_size;\n\tint\ti;\n\n\t// Detect Mac DiskCopy4.2 disk image (often .dmg or .dc or .dc42)\n\t// bptr points to just the first 512 bytes of the file\n\n\tif((bptr[0x52] != 1) || (bptr[0x53] != 0)) {\n\t\treturn 0;\t// No \"magic number 0x01, 0x00 for DiskCopy4.2\n\t}\n\tif(bptr[0] > 0x3f) {\n\t\treturn 0;\t// not a valid image name length (1-0x3f)\n\t}\n\tddata_size = 0;\n\tdtag_size = 0;\n\tfor(i = 0; i < 4; i++) {\n\t\tddata_size = (ddata_size << 8) | bptr[0x40 + i];\n\t\tdtag_size = (dtag_size << 8) | bptr[0x44 + i];\n\t}\n\tif((dtag_size != 0) && (dtag_size != ((ddata_size >> 9) * 12))) {\n\t\treturn 0;\t// Tags are either 0, or 12 bytes per block\n\t}\n\tif(slot == 7) {\n\t\tif(ddata_size < 140*1024) {\n\t\t\treturn 0;\t\t// Allow any size >=140K slot 7\n\t\t}\n\t} else if(ddata_size != exp_dsize) {\n\t\treturn 0;\t\t\t// Must be 140K or 800K\n\t}\n\n\tif(ddata_size > (dsize - 0x54)) {\n\t\treturn 0;\t// data_size doesn't make sense\n\t}\n\tif((ddata_size & 0x1ff) || ((ddata_size >> 31) > 1)) {\n\t\treturn 0;\t// data_size not a multiple of 512 bytes\n\t}\n\tif((ddata_size + dtag_size + 0x54) != dsize) {\n\t\treturn 0;\t// File doesn't appear to be well-formed\n\t}\n\n\treturn ddata_size;\n}\n\ndword64\ncfg_get_fd_size(int fd)\n{\n\tstruct stat stat_buf;\n\tint\tret;\n\n\tret = fstat(fd, &stat_buf);\n\tif(ret != 0) {\n\t\tfprintf(stderr,\"fstat returned %d on fd %d, errno: %d\\n\",\n\t\t\tret, fd, errno);\n\t\tstat_buf.st_size = 0;\n\t}\n\n\treturn stat_buf.st_size;\n}\n\ndword64\ncfg_read_from_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize)\n{\n\tdword64\tdret, doff;\n\tword32\tthis_len;\n\n\tdret = kegs_lseek(fd, dpos, SEEK_SET);\n\tif(dret != dpos) {\n\t\tprintf(\"lseek failed: %lld\\n\", dret);\n\t\treturn 0;\n\t}\n\tdoff = 0;\n\twhile(1) {\n\t\tif(doff >= dsize) {\n\t\t\tbreak;\n\t\t}\n\t\tthis_len = 1UL << 30;\n\t\tif((dsize - doff) < this_len) {\n\t\t\tthis_len = (word32)(dsize - doff);\n\t\t}\n\t\tdret = read(fd, bufptr + doff, this_len);\n\t\tif((dret + 1) == 0) {\t\t// dret==-1\n\t\t\tprintf(\"read failed\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\tif(dret == 0) {\n\t\t\tprintf(\"Unexpected end of file, tried to read from \"\n\t\t\t\t\"dpos:%lld dsize:%lld\\n\", dpos, dsize);\n\t\t\treturn 0;\n\t\t}\n\t\tdoff += dret;\n\t}\n\treturn doff;\n}\n\ndword64\ncfg_write_to_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize)\n{\n\tdword64\tdret;\n\n\tdret = kegs_lseek(fd, dpos, SEEK_SET);\n\tif(dret != dpos) {\n\t\tprintf(\"lseek failed: %lld\\n\", dret);\n\t\treturn 0;\n\t}\n\treturn must_write(fd, bufptr, dsize);\n}\n\nint\ncfg_partition_maybe_add_dotdot()\n{\n\tint\tpart_len;\n\n\tpart_len = (int)strlen(&(g_cfg_part_path[0]));\n\tif(part_len > 0) {\n\t\t// Add .. entry here\n\t\tcfg_file_add_dirent(&g_cfg_partitionlist, \"..\", 1, 0, 0, 0, 0);\n\t}\n\treturn part_len;\n}\n\nint\ncfg_partition_name_check(const byte *name_ptr, int name_len)\n{\n\tint\tpart_len;\n\tint\ti;\n\n\t// Return 0 if name_ptr is not at the right path depth, 1 if OK\n\n\tpart_len = (int)strlen(&g_cfg_part_path[0]);\n\tfor(i = 0; i < part_len; i++) {\n\t\tif(i >= name_len) {\n\t\t\treturn 0;\n\t\t}\n\t\tif(name_ptr[i] != g_cfg_part_path[i]) {\n\t\t\treturn 0;\n\t\t}\n\t}\n\treturn 1;\n}\n\nint\ncfg_partition_read_block(int fd, void *buf, dword64 blk, int blk_size)\n{\n\tif(!cfg_read_from_fd(fd, buf, blk * blk_size, blk_size)) {\n\t\t// Read failed\n\t\treturn 0;\n\t}\n\treturn blk_size;\n}\n\nint\ncfg_partition_find_by_name_or_num(Disk *dsk, const char *in_partnamestr,\n\t\t\t\t\t\t\t\tint part_num)\n{\n\tCfg_dirent *direntptr;\n\tconst char *partnamestr;\n\tint\tmatch, num_parts, ret, fd, len, c;\n\tint\ti;\n\n#if 0\n\tprintf(\"cfg_partition_find_by_name_or_num: %s, part_num:%d\\n\",\n\t\t\t\t\t\t\tpartnamestr, part_num);\n#endif\n\n\t// We need to copy partnamestr up to the last / to g_cfg_part_path[],\n\t//  and use just the end as the partition name.\n\tcfg_strncpy(&g_cfg_part_path[0], in_partnamestr, CFG_PATH_MAX);\n\tlen = (int)strlen(in_partnamestr);\n\tpartnamestr = in_partnamestr;\n\tfor(i = len - 1; i >= 0; i--) {\n\t\tc = g_cfg_part_path[i];\n\t\tif(c == '/') {\n\t\t\tpartnamestr = &(in_partnamestr[i+1]);\n\t\t\tbreak;\n\t\t}\n\t\tg_cfg_part_path[i] = 0;\n\t}\n\n\tfd = open(dsk->name_ptr, O_RDONLY | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\tfatal_printf(\"Cannot open disk image: %s\\n\", dsk->name_ptr);\n\t\treturn -1;\n\t}\n\n\tnum_parts = cfg_partition_make_list(fd);\n\n\tif(num_parts <= 0) {\n\t\tprintf(\"num_parts: %d\\n\", num_parts);\n\t\tclose(fd);\n\t\treturn -1;\n\t}\n\n\tfor(i = 0; i < g_cfg_partitionlist.last; i++) {\n\t\tdirentptr = &(g_cfg_partitionlist.direntptr[i]);\n\t\tmatch = 0;\n\t\tif((strcmp(partnamestr, direntptr->name) == 0) &&\n\t\t\t\t\t\t\t(part_num < 0)) {\n\t\t\t//printf(\"partition, match1, name:%s %s, part_num:%d\\n\",\n\t\t\t//\tpartnamestr, direntptr->name, part_num);\n\n\t\t\tmatch = 1;\n\t\t}\n\t\tif((partnamestr == 0) && (direntptr->part_num == part_num)) {\n\t\t\t//printf(\"partition, match2, n:%s, part_num:%d == %d\\n\",\n\t\t\t//\tdirentptr->name, direntptr->part_num, part_num);\n\t\t\tmatch = 1;\n\t\t}\n\t\tif(match) {\n\t\t\t//printf(\"match with dimage_start:%08llx, dimage_size:\"\n\t\t\t//\t\"%08llx\\n\", dsk->dimage_start,\n\t\t\t//\tdsk->dimage_size);\n\n\t\t\tprintf(\"match with dimage_start:%08llx, dimage_size:\"\n\t\t\t\t\"%08llx\\n\", direntptr->dimage_start,\n\t\t\t\tdirentptr->dsize);\n\t\t\tret = i;\n\t\t\tif(g_cfg_partition_is_zip) {\n\t\t\t\tret = undeflate_zipfile(dsk, fd,\n\t\t\t\t\tdirentptr->dimage_start,\n\t\t\t\t\tdirentptr->dsize,\n\t\t\t\t\tdirentptr->compr_dsize);\n\t\t\t\tclose(fd);\n\t\t\t} else {\n\t\t\t\tdsk->fd = fd;\n\t\t\t\tdsk->dimage_start = direntptr->dimage_start;\n\t\t\t\tdsk->dimage_size = direntptr->dsize;\n\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\tclose(fd);\n\t// printf(\"No matches, ret -1\\n\");\n\treturn -1;\n}\n\nint\ncfg_partition_make_list_from_name(const char *namestr)\n{\n\tint\tfd, ret;\n\n\tfd = open(namestr, O_RDONLY | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\tfatal_printf(\"Cannot open part image: %s\\n\", namestr);\n\t\treturn 0;\n\t}\n\tret = cfg_partition_make_list(fd);\n\n\tclose(fd);\n\treturn ret;\n}\n\nint\ncfg_partition_make_list(int fd)\n{\n\tDriver_desc *driver_desc_ptr;\n\tPart_map *part_map_ptr;\n\tword32\t*blk_bufptr;\n\tdword64\tdimage_start, dimage_size, dsize;\n\tword32\tstart, len, data_off, data_len, sig, map_blk_cnt, cur_blk;\n\tword32\tmap_blks, block_size;\n\tint\tis_dir, ret;\n\n\tblock_size = 512;\n\tg_cfg_partition_is_zip = 0;\n\n\tcfg_free_alldirents(&g_cfg_partitionlist);\n\n\tblk_bufptr = (word32 *)malloc(MAX_PARTITION_BLK_SIZE);\n\n\tcfg_partition_read_block(fd, blk_bufptr, 0, block_size);\n\n\tdriver_desc_ptr = (Driver_desc *)blk_bufptr;\n\tsig = cfg_get_be_word16(&(driver_desc_ptr->sig));\n\tblock_size = cfg_get_be_word16(&(driver_desc_ptr->blk_size));\n\tif(block_size == 0) {\n\t\tblock_size = 512;\n\t}\n\tif((sig != 0x4552) || (block_size < 0x200) ||\n\t\t\t\t(block_size > MAX_PARTITION_BLK_SIZE)) {\n\t\tprintf(\"Partition error: No driver descriptor map found\\n\");\n\t\tfree(blk_bufptr);\n\t\tret = undeflate_zipfile_make_list(fd);\n\t\tif(ret > 0) {\n\t\t\tg_cfg_partition_is_zip = 1;\n\t\t}\n\t\treturn ret;\n\t}\n\n\tmap_blks = 1;\n\tcur_blk = 0;\n\tdsize = cfg_get_fd_size(fd);\n\tcfg_file_add_dirent(&g_cfg_partitionlist, \"None - Whole image\",\n\t\t\tis_dir=0, dsize, 0, 0, -1);\n\n\twhile(cur_blk < map_blks) {\n\t\tcur_blk++;\n\t\tcfg_partition_read_block(fd, blk_bufptr, cur_blk, block_size);\n\t\tpart_map_ptr = (Part_map *)blk_bufptr;\n\t\tsig = cfg_get_be_word16(&(part_map_ptr->sig));\n\t\tmap_blk_cnt = cfg_get_be_word32(&(part_map_ptr->map_blk_cnt));\n\t\tif(cur_blk <= 1) {\n\t\t\tmap_blks = MY_MIN(20, map_blk_cnt);\n\t\t}\n\t\tif(sig != 0x504d) {\n\t\t\tprintf(\"Partition entry %d bad signature:%04x\\n\",\n\t\t\t\tcur_blk, sig);\n\t\t\tfree(blk_bufptr);\n\t\t\treturn g_cfg_partitionlist.last;\n\t\t}\n\n\t\t/* found it, check for consistency */\n\t\tstart = cfg_get_be_word32(&(part_map_ptr->phys_part_start));\n\t\tlen = cfg_get_be_word32(&(part_map_ptr->part_blk_cnt));\n\t\tdata_off = cfg_get_be_word32(&(part_map_ptr->data_start));\n\t\tdata_len = cfg_get_be_word32(&(part_map_ptr->data_cnt));\n\t\tif(data_off + data_len > len) {\n\t\t\tprintf(\"Poorly formed entry\\n\");\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(data_len < 10 || start < 1) {\n\t\t\tprintf(\"Poorly formed entry %d, datalen:%d, \"\n\t\t\t\t\"start:%08x\\n\", cur_blk, data_len, start);\n\t\t\tcontinue;\n\t\t}\n\n\t\tdimage_size = (dword64)data_len * block_size;\n\t\tdimage_start = ((dword64)start + data_off) * block_size;\n\t\tis_dir = 2*(dimage_size < 800*1024);\n#if 0\n\t\tprintf(\" partition add entry %d = %s %d %08llx %08llx\\n\",\n\t\t\tcur_blk, part_map_ptr->part_name, is_dir,\n\t\t\tdimage_size, dimage_start);\n#endif\n\n\t\tcfg_file_add_dirent(&g_cfg_partitionlist,\n\t\t\tpart_map_ptr->part_name, is_dir, dimage_size,\n\t\t\tdimage_start, 0, cur_blk);\n\t}\n\n\tfree(blk_bufptr);\n\treturn g_cfg_partitionlist.last;\n}\n\nint\ncfg_maybe_insert_disk(int slot, int drive, const char *namestr)\n{\n\tint\tnum_parts;\n\n\tg_cfg_part_path[0] = 0;\n\tnum_parts = cfg_partition_make_list_from_name(namestr);\n\n\tif(num_parts > 0) {\n\t\tprintf(\"Choose a partition\\n\");\n\t\tg_cfg_select_partition = 1;\n\t\tg_cfg_file_pathfield = 0;\n\t} else {\n\t\tinsert_disk(slot, drive, namestr, 0, 0, -1, 0);\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nvoid\ncfg_insert_disk_dynapro(int slot, int drive, const char *name)\n{\n\tint\tdynapro_blocks;\n\n\tdynapro_blocks = 280;\n\tif(slot == 5) {\n\t\tdynapro_blocks = 1600;\n\t} else if(slot == 7) {\n\t\tdynapro_blocks = 65535;\n\t}\n\tif(g_cfg_newdisk_select && (g_cfg_newdisk_type == 3) &&\n\t\t\t\t\t\t\tg_cfg_newdisk_blocks) {\n\t\tdynapro_blocks = g_cfg_newdisk_blocks;\n\t}\n\tinsert_disk(slot, drive, name, 0, 0, -1, dynapro_blocks);\n}\n\nint\ncfg_stat(char *path, struct stat *sb, int do_lstat)\n{\n\tint\tret, len, removed_slash;\n\n\tremoved_slash = 0;\n\tlen = 0;\n\n\t/* Windows doesn't like to stat paths ending in a /, so remove it */\n\tlen = (int)strlen(path);\n#ifdef _WIN32\n\tif((len > 1) && (path[len - 1] == '/') ) {\n\t\tpath[len - 1] = 0;\t/* remove the slash */\n\t\tremoved_slash = 1;\n\t}\n#endif\n\n\tif(do_lstat) {\n\t\tret = lstat(path, sb);\n\t} else {\n\t\tret = stat(path, sb);\n\t}\n\n\t/* put the slash back */\n\tif(removed_slash) {\n\t\tpath[len - 1] = '/';\n\t}\n\n\treturn ret;\n}\n\nword32\ncfg_get_le16(byte *bptr)\n{\n\treturn bptr[0] | (bptr[1] << 8);\n}\n\nword32\ncfg_get_le32(byte *bptr)\n{\n\treturn bptr[0] | (bptr[1] << 8) | (bptr[2] << 16) | (bptr[3] << 24);\n}\n\ndword64\ncfg_get_le64(byte *bptr)\n{\n\tdword64\tdval;\n\tint\ti;\n\n\tdval = 0;\n\tfor(i = 7; i >= 0; i--) {\n\t\tdval = (dval << 8) | bptr[i];\n\t}\n\treturn dval;\n}\n\nword32\ncfg_get_be_word16(word16 *ptr)\n{\n\tbyte\t*bptr;\n\n\tbptr = (byte *)ptr;\n\treturn (bptr[0] << 8) | bptr[1];\n}\n\nword32\ncfg_get_be_word32(word32 *ptr)\n{\n\tbyte\t*bptr;\n\n\tbptr = (byte *)ptr;\n\treturn (bptr[0] << 24) | (bptr[1] << 16) | (bptr[2] << 8) | bptr[3];\n}\n\nvoid\ncfg_set_le32(byte *bptr, word32 val)\n{\n\t*bptr++ = val;\n\t*bptr++ = val >> 8;\n\t*bptr++ = val >> 16;\n\t*bptr++ = val >> 24;\n}\n\nvoid\nconfig_file_to_pipe(Disk *dsk, const char *cmd_ptr, const char *name_ptr)\n{\n#ifdef _WIN32\n\tprintf(\"Cannot do pipe from cmd %s to %s\\n\", cmd_ptr, name_ptr);\n\treturn;\n#else\n\tint\toutput_pipe[2];\n\tbyte\t*bptr2;\n\tchar\t*bufptr;\n\tpid_t\tpid;\n\tint\tstat_loc, ret, bufsize, pos, fd;\n\tint\ti;\n\n\t// Create a pipe to cmd_ptr, and send the contents of name_ptr to it\n\t//  Collect the output in a 32MB buffer.  Once complete, allocate\n\t//  a buffer of the correct size, copy to it, and free the giant buffer\n\t// Sample usage: \"gunzip\", \"{filename}.gz\" will run gunzip and collect\n\t//  uncompressed data\n\tret = pipe(&output_pipe[0]);\n\tif(ret < 0) {\n\t\treturn;\n\t}\n\tprintf(\"output_pipe[0]=%d, [1]=%d\\n\", output_pipe[0], output_pipe[1]);\n\tbufsize = 32*1024*1024;\n\tbufptr = malloc(bufsize);\n\tif(bufptr == 0) {\n\t\treturn;\n\t}\n\tpos = 0;\n\tpid = fork();\n\tif(pid == 0) {\n\t\tclose(output_pipe[0]);\n\t\tret = dup2(output_pipe[1], 1);\n\t\tif(ret < 0) {\n\t\t\texit(1);\n\t\t}\n\t\t// The child.  Open 0 as the file, and then do system\n\t\tclose(0);\n\t\tfd = open(name_ptr, O_RDONLY | O_BINARY, 0x1b6);\n\t\tif(fd != 0) {\n\t\t\texit(1);\n\t\t}\n\t\t// Now just run the command.  Input is from name_ptr, output is\n\t\t//  to a pipe\n\t\t(void)!system(cmd_ptr);\n\t\texit(0);\n\t} else if(pid > 0) {\n\t\t// Parent.  Collect output from output_pipe[0], and write it\n\t\t//  to bufptr.\n\t\tclose(output_pipe[1]);\n\t\twhile(1) {\n\t\t\tif(pos >= bufsize) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tret = read(output_pipe[0], bufptr + pos, bufsize - pos);\n\t\t\tif(ret <= 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tpos += ret;\n\t\t}\n\t\tclose(output_pipe[0]);\n\t\twaitpid(pid, &stat_loc, 0);\n\t} else {\n\t\t// Error case\n\t\tclose(output_pipe[1]);\n\t\tclose(output_pipe[0]);\n\t}\n\n\t// See what we got\n\tbptr2 = 0;\n\tprintf(\"Read %d bytes from %s\\n\", pos, name_ptr);\n\tif(pos >= 140*1024) {\n\t\t// Looks like it could be an image\n\t\tbptr2 = malloc(pos);\n\t\tfor(i = 0; i < pos; i++) {\n\t\t\tbptr2[i] = bufptr[i];\n\t\t}\n\t\tdsk->raw_data = bptr2;\n\t\tdsk->fd = 0;\t\t\t// Indicates raw_data is valid\n\t\tdsk->raw_dsize = pos;\n\t}\n\tfree(bufptr);\n#endif\n}\n\nvoid\ncfg_htab_vtab(int x, int y)\n{\n\tif(x > 79) {\n\t\tx = 0;\n\t}\n\tif(y > 23) {\n\t\ty = 0;\n\t}\n\tg_cfg_curs_x = x;\n\tg_cfg_curs_y = y;\n\tg_cfg_curs_inv = 0;\n\tg_cfg_curs_mousetext = 0;\n}\n\nvoid\ncfg_home()\n{\n\tint\ti;\n\n\tcfg_htab_vtab(0, 0);\n\tfor(i = 0; i < 24; i++) {\n\t\tcfg_cleol();\n\t}\n}\n\nvoid\ncfg_cleol()\n{\n\tg_cfg_curs_inv = 0;\n\tg_cfg_curs_mousetext = 0;\n\tcfg_putchar(' ');\n\twhile(g_cfg_curs_x != 0) {\n\t\tcfg_putchar(' ');\n\t}\n}\n\nvoid\ncfg_putchar(int c)\n{\n\tint\tx, y;\n\n\tif(c == '\\n') {\n\t\tcfg_cleol();\n\t\treturn;\n\t}\n\tif(c == '\\b') {\n\t\tg_cfg_curs_inv = !g_cfg_curs_inv;\n\t\treturn;\n\t}\n\tif(c == '\\t') {\n\t\tg_cfg_curs_mousetext = !g_cfg_curs_mousetext;\n\t\treturn;\n\t}\n\ty = g_cfg_curs_y;\n\tx = g_cfg_curs_x;\n\n\t// Normal: 0xa0-0xff for space through lowercase\n\t// Inverse: 0x00-0x3f for upper case and numbers, 0x60-0x7f for lcase\n\t// Mousetext: 0x40-0x5f\n\tif(g_cfg_curs_inv) {\n\t\tif(c >= 0x40 && c < 0x60) {\n\t\t\tc = c & 0x1f;\n\t\t}\n\t} else {\n\t\tc = c | 0x80;\n\t}\n\tif(g_cfg_curs_mousetext) {\n\t\tc = (c & 0x1f) | 0x40;\n\t}\n\tg_cfg_screen[y][x] = c;\n\tx++;\n\tif(x >= 80) {\n\t\tx = 0;\n\t\ty++;\n\t\tif(y >= 24) {\n\t\t\ty = 0;\n\t\t}\n\t}\n\tg_cfg_curs_y = y;\n\tg_cfg_curs_x = x;\n\tg_cfg_screen_changed = 1;\n}\n\nvoid\ncfg_printf(const char *fmt, ...)\n{\n\tva_list ap;\n\tint\tc;\n\tint\ti;\n\n\tva_start(ap, fmt);\n\t(void)vsnprintf(g_cfg_printf_buf, CFG_PRINTF_BUFSIZE, fmt, ap);\n\tva_end(ap);\n\n\tfor(i = 0; i < CFG_PRINTF_BUFSIZE; i++) {\n\t\tc = g_cfg_printf_buf[i];\n\t\tif(c == 0) {\n\t\t\treturn;\n\t\t}\n\t\tcfg_putchar(c);\n\t}\n}\n\nvoid\ncfg_print_dnum(dword64 dnum, int max_len)\n{\n\tchar\tbuf[64];\n\tchar\tbuf2[64];\n\tint\tlen, cnt, c;\n\tint\ti, j;\n\n\t/* Prints right-adjusted \"num\" in field \"max_len\" wide */\n\tsnprintf(&buf[0], 64, \"%lld\", dnum);\n\tlen = (int)strlen(buf);\n\tfor(i = 0; i < 64; i++) {\n\t\tbuf2[i] = ' ';\n\t}\n\tj = max_len + 1;\n\tbuf2[j] = 0;\n\tj--;\n\tcnt = 0;\n\tfor(i = len - 1; (i >= 0) && (j >= 1); i--) {\n\t\tc = buf[i];\n\t\tif(c >= '0' && c <= '9') {\n\t\t\tif(cnt >= 3) {\n\t\t\t\tbuf2[j--] = ',';\n\t\t\t\tcnt = 0;\n\t\t\t}\n\t\t\tcnt++;\n\t\t}\n\t\tbuf2[j--] = c;\n\t}\n\tcfg_printf(&buf2[1]);\n}\n\nint\ncfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras)\n{\n\tDisk\t*dsk;\n\tint\tslot, drive;\n\n\tslot = type_ext >> 8;\n\tdrive = type_ext & 0xff;\n\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\n\toutstr[0] = 0;\n\tif(dsk->name_ptr == 0) {\n\t\treturn 0;\n\t}\n\n\tconfig_generate_config_kegs_name(outstr, maxlen, dsk, with_extras);\n\treturn dsk->dynapro_blocks;\n}\n\nint\ncfg_get_disk_locked(int type_ext)\n{\n\tDisk\t*dsk;\n\tint\tslot, drive;\n\n\tslot = type_ext >> 8;\n\tdrive = type_ext & 0xff;\n\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\tif(dsk->fd < 0) {\n\t\treturn 0;\n\t}\n\n\tif(dsk->write_prot) {\n\t\treturn 1;\n\t} else if(!dsk->write_through_to_unix) {\n\t\treturn 2;\n\t}\n\n\treturn 0;\n}\n\nvoid\ncfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change)\n{\n\tchar\tvalbuf[CFG_OPT_MAXSTR];\n\tchar\t*(*fn_ptr)(int);\n\tint\t*iptr;\n\tchar\t**str_ptr;\n\tconst char *menustr;\n\tchar\t*curstr, *defstr, *str, *outstr;\n\tvoid\t*edit_ptr;\n\tint\tval, num_opts, opt_num, bufpos, outpos, curval, defval, type;\n\tint\ttype_ext, opt_get_str, separator, len, c, locked;\n\tint\ti;\n\n\t// For this menu_pos line, create output in g_cfg_opt_buf[] string\n\t//  Highlight it if menu_pos==highlight_pos\n\t// Allow arrows to modify the currently selected item of a list using\n\t//  change: -1 moves to a previous item, +1 moves to the next\n\n\tg_cfg_opt_buf[0] = 0;\n\n\tnum_opts = 0;\n\topt_get_str = 0;\n\tseparator = ',';\n\n\tmenuptr += menu_pos;\t\t/* move forward to entry menu_pos */\n\n\tmenustr = menuptr->str;\n\ttype = menuptr->cfgtype;\n\ttype_ext = (type >> 4);\n\ttype = type & 0xf;\n\tlen = (int)strlen(menustr) + 1;\n\n\tbufpos = 0;\n\toutstr = &(g_cfg_opt_buf[0]);\n\n\toutstr[bufpos++] = ' ';\t\t// 0\n\toutstr[bufpos++] = ' ';\t\t// 1\n\toutstr[bufpos++] = '\\t';\t// 2\n\toutstr[bufpos++] = '\\t';\t// 3\n\toutstr[bufpos++] = ' ';\t\t// 4\n\toutstr[bufpos++] = ' ';\t\t// 5\n\n\t// Figure out if we should get a checkmark\n\tcurval = -1;\n\tdefval = -1;\n\tcurstr = 0;\n\tif(type == CFGTYPE_INT) {\n\t\tiptr = menuptr->ptr;\n\t\tcurval = *iptr;\n\t\tiptr = menuptr->defptr;\n\t\tif(!iptr) {\n\t\t\tprintf(\"BAD MENU, defptr is 0!\\n\");\n\t\t} else {\n\t\t\tdefval = *iptr;\n\t\t}\n\t\tif(curval == defval) {\n\t\t\tg_cfg_opt_buf[3] = 'D';\t/* checkmark */\n\t\t\tg_cfg_opt_buf[4] = '\\t';\n\t\t}\n\t}\n\n\tif((type == CFGTYPE_FILE) || (type == CFGTYPE_STR)) {\n\t\tstr_ptr = (char **)menuptr->ptr;\n\t\tcurstr = *str_ptr;\n\t\tstr_ptr = (char **)menuptr->defptr;\n\t\tif(!str_ptr) {\n\t\t\tprintf(\"BAD MENU, defptr str is 0!\\n\");\n\t\t\tdefstr = \"\";\n\t\t} else {\n\t\t\tdefstr = *str_ptr;\n\t\t}\n\t\tif(strcmp(curstr, defstr) == 0) {\n\t\t\tg_cfg_opt_buf[3] = 'D';\t/* checkmark */\n\t\t\tg_cfg_opt_buf[4] = '\\t';\n\t\t}\n\t}\n\n\t// If it's a menu, give it a special menu indicator\n\tif(type == CFGTYPE_MENU) {\n\t\tg_cfg_opt_buf[1] = '\\t';\n\t\tg_cfg_opt_buf[2] = 'M';\t\t/* return-like symbol */\n\t\tg_cfg_opt_buf[3] = '\\t';\n\t\tg_cfg_opt_buf[4] = ' ';\n\t}\n\n\tif(type == CFGTYPE_DISK) {\n\t\tlocked = cfg_get_disk_locked(type_ext);\n\t\tif(locked) {\n\t\t\tg_cfg_opt_buf[4] = '*';\n\t\t\tif(locked == 2) {\t\t// inverse\n\t\t\t\tg_cfg_opt_buf[2] = '\\b';\n\t\t\t\tg_cfg_opt_buf[3] = '*';\n\t\t\t\tg_cfg_opt_buf[4] = '\\b';\n\t\t\t}\n\t\t}\n\t}\n\n\tif(menu_pos == highlight_pos) {\n\t\toutstr[bufpos++] = '\\b';\n\t}\n\n\topt_get_str = 2;\n\ti = -1;\n\toutpos = bufpos;\n#if 0\n\tprintf(\"cfg menu_pos: %d str len: %d\\n\", menu_pos, len);\n#endif\n\twhile(++i < len) {\n\t\tc = menustr[i];\n\t\tif(c == separator) {\t\t// ','?\n\t\t\tif(i == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tc = 0;\n\t\t}\n\t\toutstr[outpos++] = c;\n\t\toutstr[outpos] = 0;\n\t\tif(outpos >= CFG_OPT_MAXSTR) {\n\t\t\tfprintf(stderr, \"CFG_OPT_MAXSTR exceeded\\n\");\n\t\t\tmy_exit(1);\n\t\t\treturn;\n\t\t}\n\t\tif(c == 0) {\n\t\t\tif(opt_get_str == 2) {\n\t\t\t\toutstr = &(valbuf[0]);\n\t\t\t\tbufpos = outpos - 1;\n\t\t\t\topt_get_str = 0;\n\t\t\t} else if(opt_get_str) {\n#if 0\n\t\t\t\tif(menu_pos == highlight_pos) {\n\t\t\t\t\tprintf(\"menu_pos %d opt %d = %s=%d\\n\",\n\t\t\t\t\t\tmenu_pos, num_opts,\n\t\t\t\t\t\tg_cfg_opts_str[0],\n\t\t\t\t\t\tg_cfg_opts_vals[num_opts]);\n\t\t\t\t}\n#endif\n\t\t\t\tnum_opts++;\n\t\t\t\toutstr = &(valbuf[0]);\n\t\t\t\topt_get_str = 0;\n\t\t\t\tif(num_opts >= CFG_MAX_OPTS) {\n\t\t\t\t\tfprintf(stderr, \"CFG_MAX_OPTS oflow\\n\");\n\t\t\t\t\tmy_exit(1);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tval = (word32)strtoul(valbuf, 0, 0);\n\t\t\t\tg_cfg_opts_vals[num_opts] = val;\n\t\t\t\toutstr = &(g_cfg_opts_str[0]);\n\t\t\t\tif(val != curval) {\n\t\t\t\t\toutstr = valbuf;\n\t\t\t\t}\n\t\t\t\topt_get_str = 1;\n\t\t\t}\n\t\t\toutpos = 0;\n\t\t\toutstr[0] = 0;\n\t\t}\n\t}\n\n\tif(menu_pos == highlight_pos) {\n\t\tg_cfg_opt_buf[bufpos++] = '\\b';\n\t}\n\n\tg_cfg_opt_buf[bufpos] = 0;\n\n\t// Decide what to display on the \"right\" side\n\tstr = 0;\n\topt_num = -1;\n\tif((type == CFGTYPE_INT) || (type == CFGTYPE_FILE) ||\n\t\t\t\t\t\t(type == CFGTYPE_STR)) {\n\t\tg_cfg_opt_buf[bufpos++] = ' ';\n\t\tg_cfg_opt_buf[bufpos++] = '=';\n\t\tg_cfg_opt_buf[bufpos++] = ' ';\n\t\tg_cfg_opt_buf[bufpos] = 0;\n\t\tfor(i = 0; i < num_opts; i++) {\n\t\t\tif(curval == g_cfg_opts_vals[i]) {\n\t\t\t\topt_num = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(change != 0) {\n\t\tif(type == CFGTYPE_INT) {\n\t\t\tif(num_opts > 0) {\n\t\t\t\topt_num += change;\n\t\t\t\tif(opt_num >= num_opts) {\n\t\t\t\t\topt_num = 0;\n\t\t\t\t}\n\t\t\t\tif(opt_num < 0) {\n\t\t\t\t\topt_num = num_opts - 1;\n\t\t\t\t}\n\t\t\t\tcurval = g_cfg_opts_vals[opt_num];\n\t\t\t} else {\n\t\t\t\tcurval += change;\n\t\t\t\t/* HACK: min_val, max_val testing here */\n\t\t\t}\n\t\t\tiptr = (int *)menuptr->ptr;\n\t\t\tcfg_int_update(iptr, curval);\n\t\t}\n\t\tg_config_kegs_update_needed = 1;\n\t}\n\n#if 0\n\tif(menu_pos == highlight_pos) {\n\t\tprintf(\"menu_pos %d opt_num %d\\n\", menu_pos, opt_num);\n\t}\n#endif\n\n\tedit_ptr = g_cfg_edit_ptr;\n\tif((edit_ptr == menuptr->ptr) && edit_ptr) {\n\t\t// Just show the current edit string\n\t\tstr = cfg_shorten_filename(&(g_cfg_edit_buf[0]), 68 - 1);\n\t\tcfg_strlcat(str, \"\\b \\b\", CFG_PATH_MAX);\n\t} else if(opt_num >= 0) {\n\t\tstr = &(g_cfg_opts_str[0]);\n\t} else {\n\t\tif(type == CFGTYPE_INT) {\n\t\t\tstr = &(g_cfg_opts_str[0]);\n\t\t\tsnprintf(str, CFG_OPT_MAXSTR, \"%d\", curval);\n\t\t} else if (type == CFGTYPE_DISK) {\n\t\t\tstr = &(g_cfg_opts_str[0]);\n\t\t\t(void)cfg_get_disk_name(str, CFG_PATH_MAX, type_ext, 1);\n\t\t\tstr = cfg_shorten_filename(str, 68);\n\t\t} else if ((type == CFGTYPE_FILE) || (type == CFGTYPE_STR)) {\n\t\t\tstr = cfg_shorten_filename(curstr, 68);\n\t\t} else if(type == CFGTYPE_FUNC) {\n\t\t\tfn_ptr = (char *(*)(int))menuptr->ptr;\n\t\t\tstr = \"\";\n\t\t\tcurstr = (*fn_ptr)(1);\n\t\t\tif(curstr) {\n\t\t\t\tg_cfg_opt_buf[bufpos++] = ' ';\n\t\t\t\tg_cfg_opt_buf[bufpos++] = ':';\n\t\t\t\tg_cfg_opt_buf[bufpos++] = ' ';\n\t\t\t\tg_cfg_opt_buf[bufpos] = 0;\n\t\t\t\tstr = cfg_shorten_filename(curstr, 68);\n\t\t\t\tfree(curstr);\n\t\t\t}\n\t\t} else {\n\t\t\tstr = \"\";\n\t\t}\n\t}\n\n#if 0\n\tif(menu_pos == highlight_pos) {\n\t\tprintf(\"menu_pos %d buf_pos %d, str is %s, %02x, %02x, \"\n\t\t\t\"%02x %02x\\n\",\n\t\t\tmenu_pos, bufpos, str, g_cfg_opt_buf[bufpos-1],\n\t\t\tg_cfg_opt_buf[bufpos-2],\n\t\t\tg_cfg_opt_buf[bufpos-3],\n\t\t\tg_cfg_opt_buf[bufpos-4]);\n\t}\n#endif\n\n\tg_cfg_opt_buf[bufpos] = 0;\n\tcfg_strncpy(&(g_cfg_opt_buf[bufpos]), str, CFG_OPT_MAXSTR - bufpos - 1);\n\tg_cfg_opt_buf[CFG_OPT_MAXSTR-1] = 0;\n}\n\nvoid\ncfg_get_base_path(char *pathptr, const char *inptr, int go_up)\n{\n\tconst char *tmpptr;\n\tchar\t*slashptr;\n\tchar\t*outptr;\n\tint\tadd_dotdot, is_dotdot;\n\tint\tlen;\n\tint\tc;\n\n\t// Take full filename, copy it to pathptr, and truncate at last slash\n\t//  inptr and pathptr can be the same.\n\t// If go_up is set, then replace a blank dir with \"..\"\n\t//  but first, see if path is currently just ../ over and over\n\t// if so, just tack .. onto the end and return\n\n\t//printf(\"cfg_get_base start with %s\\n\", inptr);\n\n\tg_cfg_file_match[0] = 0;\n\ttmpptr = inptr;\n\tis_dotdot = 1;\n\twhile(1) {\n\t\tif(tmpptr[0] == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tif((tmpptr[0] == '.') && (tmpptr[1] == '.') &&\n\t\t\t\t\t\t\t(tmpptr[2] == '/')) {\n\t\t\ttmpptr += 3;\n\t\t} else {\n\t\t\tis_dotdot = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\tslashptr = 0;\n\toutptr = pathptr;\n\tc = -1;\n\twhile(c != 0) {\n\t\tc = *inptr++;\n\t\tif(c == '/') {\n\t\t\tif(*inptr != 0) {\t/* if not a trailing slash... */\n\t\t\t\tslashptr = outptr;\n\t\t\t}\n\t\t}\n\t\t*outptr++ = c;\n\t}\n\tif(!go_up) {\n\t\t/* if not go_up, copy chopped part to g_cfg_file_match*/\n\t\t/* copy from slashptr+1 to end */\n\t\ttmpptr = slashptr+1;\n\t\tif(slashptr == 0) {\n\t\t\ttmpptr = pathptr;\n\t\t}\n\t\tcfg_strncpy(&g_cfg_file_match[0], tmpptr, CFG_PATH_MAX);\n\t\t/* remove trailing / from g_cfg_file_match */\n\t\tlen = (int)strlen(&g_cfg_file_match[0]);\n\t\tif((len > 1) && (len < (CFG_PATH_MAX - 1)) &&\n\t\t\t\t\tg_cfg_file_match[len - 1] == '/') {\n\t\t\tg_cfg_file_match[len - 1] = 0;\n\t\t}\n\t\t//printf(\"set g_cfg_file_match to %s\\n\", &g_cfg_file_match[0]);\n\t}\n\tif(!is_dotdot && (slashptr != 0)) {\n\t\tslashptr[0] = '/';\n\t\tslashptr[1] = 0;\n\t\toutptr = slashptr + 2;\n\t}\n\tadd_dotdot = 0;\n\tif(pathptr[0] == 0 || is_dotdot) {\n\t\t/* path was blank, or consisted of just ../ pattern */\n\t\tif(go_up) {\n\t\t\tadd_dotdot = 1;\n\t\t}\n\t} else if(slashptr == 0) {\n\t\t/* no slashes found, but path was not blank--make it blank */\n\t\tif(pathptr[0] == '/') {\n\t\t\tpathptr[1] = 0;\n\t\t} else {\n\t\t\tpathptr[0] = 0;\n\t\t}\n\t}\n\n\tif(add_dotdot) {\n\t\t--outptr;\n\t\toutptr[0] = '.';\n\t\toutptr[1] = '.';\n\t\toutptr[2] = '/';\n\t\toutptr[3] = 0;\n\t}\n\n\t//printf(\"cfg_get_base end with %s, is_dotdot:%d, add_dotdot:%d\\n\",\n\t//\t\tpathptr, is_dotdot, add_dotdot);\n}\n\nchar *\ncfg_name_new_image(int get_status)\n{\n\t// Called from menu to create a new disk image, this will pop up the\n\t//  file selection dialog.  Once name is selected,\n\t//  cfg_create_new_image() is called.\n\tif(get_status) {\n\t\treturn 0;\n\t}\n\tprintf(\"cfg_name_new_image called!\\n\");\n\tg_cfg_slotdrive = g_cfg_newdisk_slotdrive;\n\tg_cfg_newdisk_select = 1;\n\tcfg_file_init();\n\treturn 0;\n}\n\nvoid\ncfg_dup_existing_image(word32 slotdrive)\n{\n\t// Set g_cfg_newdisk_* to copy the slotdrive image\n\tg_cfg_slotdrive = slotdrive;\n\tg_cfg_newdisk_select = 2;\t\t\t// Do DUP\n\tcfg_file_init();\n}\n\nvoid\ncfg_dup_image_selected()\n{\n\tDisk\t*dsk;\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*bufptr;\n\tchar\t*str;\n\tdword64\tdsize, dret;\n\tword32\tslotdrive;\n\tint\tfd;\n\n\t// printf(\"cfg_dup_image_selected\\n\");\n\n\tslotdrive = g_cfg_slotdrive;\n\tg_cfg_slotdrive = 0;\n\tg_menuptr = &g_cfg_disk_menu[0];\n\tg_menu_redraw_needed = 1;\n\tg_cfg_newdisk_select = 0;\n\tg_cfg_newdisk_slotdrive = 0;\n\tprintf(\"slotdrive:%04x\\n\", slotdrive);\n\tif(slotdrive < 0x500) {\n\t\treturn;\t\t// Invalid slot,drive: Do nothing\n\t}\n\tdsk = iwm_get_dsk_from_slot_drive((slotdrive >> 8) & 7,\n\t\t\t\t\t\t\tslotdrive & 0xff);\n\tif(dsk->fd < 0) {\n\t\treturn;\t\t// No disk\n\t}\n\tdsize = dsk->dimage_size;\n\n\tstr = &g_cfg_file_path[0],\n\tprintf(\"Create dup image %s, dsize:%lld\\n\", str, dsize);\n\tif((word32)dsize != dsize) {\n\t\treturn;\n\t}\n\tfd = open(str, O_RDWR | O_CREAT | O_TRUNC, 0x1b6);\n\tif(fd < 0) {\n\t\tprintf(\"Open %s failed, errno:%d\\n\", str, errno);\n\t\treturn;\n\t}\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(wozinfo_ptr && !dsk->write_through_to_unix) {\n\t\t// Just write out the WOZ image, and then fully enable this\n\t\t//  image\n\t\tprintf(\"Writing out .WOZ image to %s, size:%d\\n\", str,\n\t\t\t\t\t\t\twozinfo_ptr->woz_size);\n\t\tif((dsk->raw_data == 0) && (dsk->fd >= 0)) {\n\t\t\tclose(dsk->fd);\n\t\t}\n\t\tdsk->fd = fd;\n\t\twoz_rewrite_crc(dsk, wozinfo_ptr->woz_size);\n\t\t\t// Above will recalc CRC and write out woz_size bytes\n\t\tdsk->raw_data = 0;\n\t\tdsk->write_through_to_unix = 1;\n\t\tfree(dsk->name_ptr);\n\t\tdsk->name_ptr = kegs_malloc_str(str);\n\t\tfree(dsk->partition_name);\n\t\tdsk->partition_name = 0;\n\t\tdsk->image_type = DSK_TYPE_WOZ;\n\t\tdsk->dimage_size = wozinfo_ptr->woz_size;\n\t\tdsk->dimage_start = 0;\n\t\tg_config_kegs_update_needed = 1;\n\t\twoz_check_file(dsk);\n\t} else if(dsk->raw_data) {\n\t\tcfg_write_to_fd(fd, dsk->raw_data, 0, dsize);\n\t\tclose(fd);\n\t} else {\n\t\tbufptr = malloc((size_t)dsize);\n\t\tif((bufptr != 0) && ((size_t)dsize == dsize)) {\n\t\t\tdret = cfg_read_from_fd(dsk->fd, bufptr, 0, dsize);\n\t\t\tif(dret == dsize) {\n\t\t\t\tcfg_write_to_fd(fd, bufptr, 0, dsize);\n\t\t\t}\n\t\t}\n\t\tfree(bufptr);\n\t\tclose(fd);\n\t}\n}\n\nvoid\ncfg_validate_image(word32 slotdrive)\n{\n\tDisk\t*dsk;\n\n\tdsk = iwm_get_dsk_from_slot_drive((slotdrive >> 8) & 7,\n\t\t\t\t\t\t\tslotdrive & 0xff);\n\tdynapro_validate_any_image(dsk);\n}\n\nvoid\ncfg_toggle_lock_disk(word32 slotdrive)\n{\n\tDisk\t*dsk;\n\n\tdsk = iwm_get_dsk_from_slot_drive((slotdrive >> 8) & 7,\n\t\t\t\t\t\t\tslotdrive & 0xff);\n\tiwm_toggle_lock(dsk);\n}\n\nint\ncfg_create_new_image_act(const char *str, int type, int size_blocks)\n{\n\tbyte\tbuf[512];\n\tdword64\tdret;\n\tint\tfd, ret;\n\tint\ti;\n\n\t// Called after file dialog selects a new image name, this creates it\n\n\tif(size_blocks == 65536) {\n\t\tsize_blocks--;\t\t// Shrink to 65535 total blocks\n\t}\n\tprintf(\"Create new image type:%d, size:%dKB\\n\", type, size_blocks/2);\n\tfd = open(str, O_RDWR | O_CREAT | O_TRUNC, 0x1b6);\n\tif(fd < 0) {\n\t\tprintf(\"Open %s failed, errno:%d\\n\", str, errno);\n\t\treturn fd;\n\t}\n\tfor(i = 0; i < 512; i++) {\n\t\tbuf[i] = 0;\n\t}\n\n\tret = 0;\n\tif(type == 2) {\t\t// WOZ\n\t\t(void)woz_new(fd, str, size_blocks/2);\n\t} else {\n\t\tfor(i = 0; i < size_blocks; i++) {\n\t\t\tdret = cfg_write_to_fd(fd, &(buf[0]), i * 512U, 512);\n\t\t\tif(dret != 512) {\n\t\t\t\tret = -1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tclose(fd);\n\n\treturn ret;\t\t// 0=success, -1 is a failure\n}\n\nvoid\ncfg_create_new_image()\n{\n\tword32\tdynamic_blocks;\n\tint\tret;\n\n\t// Type is in g_cfg_file_path.  Create this file and prepare it\n\tprintf(\"Creating new image: %s\\n\", &g_cfg_file_path[0]);\n\n\tif(g_cfg_newdisk_select == 2) {\n\t\tcfg_dup_image_selected();\n\t\treturn;\n\t}\n\tret = 0;\n\tdynamic_blocks = 0;\n\tif(g_cfg_newdisk_type == 3) {\n\t\tdynamic_blocks = g_cfg_newdisk_blocks;\n\t} else {\n\t\tret = cfg_create_new_image_act(&g_cfg_file_path[0],\n\t\t\t\tg_cfg_newdisk_type, g_cfg_newdisk_blocks);\n\t}\n\tif(ret < 0) {\n\t\t// Maybe open a dialog?  Oh well...do nothing\n\t} else {\n\t\tinsert_disk((g_cfg_slotdrive >> 8) & 0xf,\n\t\t\tg_cfg_slotdrive & 0xff, &(g_cfg_file_path[0]),\n\t\t\t0, 0, -2, dynamic_blocks);\n\t}\n\tg_cfg_slotdrive = 0;\n\tg_menuptr = &g_cfg_disk_menu[0];\n\tg_menu_redraw_needed = 1;\n\tg_cfg_newdisk_select = 0;\n\tg_cfg_newdisk_slotdrive = 0;\n}\n\nvoid\ncfg_file_init()\n{\n\tint\tslot, drive, is_dynapro;\n\tint\ti;\n\n\tis_dynapro = 0;\n\tif((g_cfg_slotdrive & 0xfff) == 0xfff) {\t// File selection\n\t\t// Just use g_cfg_file_def_name\n\t\tcfg_strncpy(&g_cfg_tmp_path[0], g_cfg_file_def_name,\n\t\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t} else {\n\t\tis_dynapro = cfg_get_disk_name(&g_cfg_tmp_path[0], CFG_PATH_MAX,\n\t\t\t\t\t\t\tg_cfg_slotdrive, 0);\n\n\t\tslot = (g_cfg_slotdrive >> 8) & 7;\n\t\tdrive = g_cfg_slotdrive & 1;\n\t\tfor(i = 0; i < 6; i++) {\n\t\t\tif(g_cfg_tmp_path[0] != 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t/* try to get a starting path from some mounted drive */\n\t\t\tdrive = !drive;\n\t\t\tif(i & 1) {\n\t\t\t\tslot++;\n\t\t\t\tif(slot >= 8) {\n\t\t\t\t\tslot = 5;\n\t\t\t\t}\n\t\t\t}\n\t\t\tis_dynapro = cfg_get_disk_name(&g_cfg_tmp_path[0],\n\t\t\t\t\tCFG_PATH_MAX, (slot << 8) + drive, 0);\n\t\t}\n\t}\n\n\tcfg_get_base_path(&g_cfg_file_curpath[0], &g_cfg_tmp_path[0], 0);\n\tif(is_dynapro) {\n\t\t// Use the full path to the dir (don't strip off last part)\n\t\tcfg_strncpy(&g_cfg_file_curpath[0], &g_cfg_tmp_path[0],\n\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t}\n\tg_cfg_dirlist.invalid = 1;\n}\n\nvoid\ncfg_free_alldirents(Cfg_listhdr *listhdrptr)\n{\n\tint\ti;\n\n\tif(listhdrptr->max > 0) {\n\t\t// Free the old directory listing\n\t\tfor(i = 0; i < listhdrptr->last; i++) {\n\t\t\tfree(listhdrptr->direntptr[i].name);\n\t\t}\n\t\tfree(listhdrptr->direntptr);\n\t}\n\n\tlisthdrptr->direntptr = 0;\n\tlisthdrptr->last = 0;\n\tlisthdrptr->max = 0;\n\tlisthdrptr->invalid = 0;\n\n\tlisthdrptr->topent = 0;\n\tlisthdrptr->curent = 0;\n}\n\nvoid\ncfg_file_add_dirent_unique(Cfg_listhdr *listhdrptr, const char *nameptr,\n\tint is_dir, dword64 dsize, dword64 dimage_start, dword64 compr_dsize,\n\tint part_num)\n{\n\tCfg_dirent *direntptr;\n\tint\tnum, namelen, this_len;\n\tint\ti;\n\n\t// Loop through all entries, make sure name is unique\n\tnum = listhdrptr->last;\n\tnamelen = (int)strlen(nameptr);\n\tfor(i = 0; i < num; i++) {\n\t\tdirentptr = &(listhdrptr->direntptr[i]);\n\t\tthis_len = (int)strlen(direntptr->name);\n\t\tif(cfg_str_match(direntptr->name, nameptr, namelen) == 0) {\n\t\t\t// It's a match...check len\n\t\t\tif(namelen == this_len) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\tcfg_file_add_dirent(listhdrptr, nameptr, is_dir, dsize, dimage_start,\n\t\t\t\t\t\tcompr_dsize, part_num);\n}\n\nvoid\ncfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir,\n\tdword64 dsize, dword64 dimage_start, dword64 compr_dsize, int part_num)\n{\n\tCfg_dirent *direntptr;\n\tchar\t*ptr;\n\tint\tinc_amt, namelen;\n\n\tnamelen = (int)strlen(nameptr);\n\tif(listhdrptr->last >= listhdrptr->max) {\n\t\t// realloc\n\t\tinc_amt = MY_MAX(64, listhdrptr->max);\n\t\tinc_amt = MY_MIN(inc_amt, 1024);\n\t\tlisthdrptr->max += inc_amt;\n\t\tlisthdrptr->direntptr = realloc(listhdrptr->direntptr,\n\t\t\t\t\tlisthdrptr->max * sizeof(Cfg_dirent));\n\t}\n\tptr = malloc(namelen+1+is_dir);\n\tcfg_strncpy(ptr, nameptr, namelen+1);\n\tif(is_dir && (namelen >= 1) && (ptr[namelen - 1] != '/')) {\n\t\t// Add a trailing '/' to directories, unless already there\n\t\tcfg_strlcat(ptr, \"/\", namelen + 1 + is_dir);\n\t}\n#if 0\n\tprintf(\"...file entry %d is %s\\n\", listhdrptr->last, ptr);\n#endif\n\tdirentptr = &(listhdrptr->direntptr[listhdrptr->last]);\n\tdirentptr->name = ptr;\n\tdirentptr->is_dir = is_dir;\n\tdirentptr->dsize = dsize;\n\tdirentptr->dimage_start = dimage_start;\n\tdirentptr->compr_dsize = compr_dsize;\n\tdirentptr->part_num = part_num;\n\tlisthdrptr->last++;\n}\n\nint\ncfg_dirent_sortfn(const void *obj1, const void *obj2)\n{\n\tconst Cfg_dirent *direntptr1, *direntptr2;\n\tint\tret;\n\n\t/* Called by qsort to sort directory listings */\n\tdirentptr1 = (const Cfg_dirent *)obj1;\n\tdirentptr2 = (const Cfg_dirent *)obj2;\n\tret = cfg_str_match(direntptr1->name, direntptr2->name, CFG_PATH_MAX);\n\treturn ret;\n}\n\nint\ncfg_str_match(const char *str1, const char *str2, int len)\n{\n\treturn cfg_str_match_maybecase(str1, str2, len, g_cfg_ignorecase);\n}\n\nint\ncfg_str_match_maybecase(const char *str1, const char *str2, int len,\n\t\t\t\t\t\t\tint ignorecase)\n{\n\tconst byte *bptr1, *bptr2;\n\tint\tc, c2;\n\tint\ti;\n\n\t/* basically, work like strcmp or strcasecmp */\n\n\tbptr1 = (const byte *)str1;\n\tbptr2 = (const byte *)str2;\n\tfor(i = 0; i < len; i++) {\n\t\tc = *bptr1++;\n\t\tc2 = *bptr2++;\n\t\tif(ignorecase) {\n\t\t\tc = tolower(c);\n\t\t\tc2 = tolower(c2);\n\t\t}\n\t\tif((c == 0) || (c2 == 0) || (c != c2)) {\n\t\t\treturn c - c2;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nint\ncfgcasecmp(const char *str1, const char *str2)\n{\n\treturn cfg_str_match_maybecase(str1, str2, 32767, 1);\n}\n\nint\ncfg_strlcat(char *dstptr, const char *srcptr, int dstsize)\n{\n\tchar\t*ptr;\n\tint\tdestlen, srclen, ret, c;\n\n\t// Concat srcptr to the end of dstptr, ensuring a null within dstsize\n\t//  Return the total buffer size that would be needed, even if dstsize\n\t//  is too small.  Compat with strlcat()\n\tdestlen = (int)strlen(dstptr);\n\tsrclen = (int)strlen(srcptr);\n\tret = destlen + srclen;\n\tdstsize--;\n\tif(destlen >= dstsize) {\n\t\treturn ret;\t\t// Do nothing, buf too small\n\t}\n\tptr = dstptr + destlen;\n\twhile(destlen < dstsize) {\n\t\tc = *srcptr++;\n\t\t*ptr++ = c;\n\t\tif(c == 0) {\n\t\t\treturn ret;\n\t\t}\n\t\tdestlen++;\n\t}\n\tdstptr[dstsize] = 0;\n\treturn ret;\n}\n\nchar *\ncfg_strncpy(char *dstptr, const char *srcptr, int dstsize)\n{\n\tchar\t*ptr;\n\tint\tc;\n\n\t// Copy srcptr to dstptr, ensuring there is room for a null\n\t// Compatible with strncpy()--except dstptr is ALWAYS null terminated\n\tptr = dstptr;\n\twhile(--dstsize > 0) {\n\t\tc = *srcptr++;\n\t\t*ptr++ = c;\n\t\tif(c == 0) {\n\t\t\treturn dstptr;\n\t\t}\n\t}\n\t*ptr = 0;\n\treturn dstptr;\n}\n\nconst char *\ncfg_str_basename(const char *str)\n{\n\tint\tlen;\n\tint\ti;\n\n\t// If str is /aa/bb/cc, this routine returns cc\n\tlen = (int)strlen(str);\n\twhile(len && (str[len - 1] == '/')) {\n\t\tlen--;\t\t// Ignore trailing '/' if there are any\n\t}\n\tfor(i = len - 1; i > 0; i--) {\n\t\tif(str[i] == '/') {\n\t\t\treturn str + i + 1;\n\t\t}\n\t}\n\n\treturn str;\n}\n\nchar *\ncfg_strncpy_dirname(char *dstptr, const char *srcptr, int dstsize)\n{\n\tchar\t*ptr;\n\tint\tc;\n\n\t// If srcptr is /aa/bb/cc, this routine returns /aa/bb/\n\t// Copy srcptr to dstptr, ensuring there is room for a null\n\t// Compatible with strncpy()--except dstptr is ALWAYS null terminated\n\tptr = dstptr;\n\twhile(--dstsize > 0) {\n\t\tc = *srcptr++;\n\t\t*ptr++ = c;\n\t\tif(c == 0) {\n\t\t\t// Remove any trailing /'s\n\t\t\tptr--;\n\t\t\twhile((ptr > dstptr) && (ptr[0] == '/')) {\n\t\t\t\tptr[0] = 0;\n\t\t\t\tptr--;\n\t\t\t}\n\t\t\twhile(ptr > dstptr) {\n\t\t\t\tif(ptr[0] == '/') {\n\t\t\t\t\tptr[1] = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tptr--;\n\t\t\t}\n\t\t\treturn dstptr;\n\t\t}\n\t}\n\t*ptr = 0;\n\treturn dstptr;\n}\n\nvoid\ncfg_file_readdir(const char *pathptr)\n{\n\tstruct dirent *direntptr;\n\tstruct stat stat_buf;\n\tDIR\t*dirptr;\n\tmode_t\tfmt;\n\tchar\t*str;\n\tconst char *tmppathptr;\n\tint\tsize, ret, is_dir, is_gz, len;\n\tint\ti;\n\n\tif(!strncmp(pathptr, &g_cfg_file_cachedpath[0], CFG_PATH_MAX) &&\n\t\t\t(g_cfg_dirlist.last > 0) && (g_cfg_dirlist.invalid==0)){\n\t\treturn;\n\t}\n\t// No match, must read new directory\n\n\t// Free all dirents that were cached previously\n\tcfg_free_alldirents(&g_cfg_dirlist);\n\n\tcfg_strncpy(&g_cfg_file_cachedpath[0], pathptr, CFG_PATH_MAX);\n\tcfg_strncpy(&g_cfg_file_cachedreal[0], pathptr, CFG_PATH_MAX);\n\n\tstr = &g_cfg_file_cachedreal[0];\n\n\tfor(i = 0; i < 200; i++) {\n\t\tlen = (int)strlen(str);\n\t\tif(len <= 0) {\n\t\t\tbreak;\n\t\t} else if(len < CFG_PATH_MAX-2) {\n\t\t\tif(str[len-1] != '/') {\n\t\t\t\t// append / to make various routines work\n\t\t\t\tstr[len] = '/';\n\t\t\t\tstr[len+1] = 0;\n\t\t\t}\n\t\t}\n\t\tret = cfg_stat(str, &stat_buf, 0);\n\t\tis_dir = 0;\n\t\tif(ret == 0) {\n\t\t\tfmt = stat_buf.st_mode & S_IFMT;\n\t\t\tif(fmt == S_IFDIR) {\n\t\t\t\t/* it's a directory */\n\t\t\t\tis_dir = 1;\n\t\t\t}\n\t\t}\n\t\tif(is_dir) {\n\t\t\tbreak;\n\t\t} else {\n\t\t\t// user is entering more path, use base for display\n\t\t\tcfg_get_base_path(str, str, 0);\n\t\t}\n\t}\n\n\ttmppathptr = str;\n\tif(str[0] == 0) {\n\t\ttmppathptr = \".\";\n\t}\n\tcfg_file_add_dirent(&g_cfg_dirlist, \"..\", 1, 0, 0, 0, -1);\n\n\tdirptr = opendir(tmppathptr);\n\tif(dirptr == 0) {\n\t\tprintf(\"Could not open %s as a directory\\n\", tmppathptr);\n\t\treturn;\n\t}\n\twhile(1) {\n\t\tdirentptr = readdir(dirptr);\n\t\tif(direntptr == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tif(!strcmp(\".\", direntptr->d_name)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif(!strcmp(\"..\", direntptr->d_name)) {\n\t\t\tcontinue;\n\t\t}\n\t\t/* Else, see if it is a directory or a file */\n\t\tcfg_strncpy(&(g_cfg_tmp_path[0]), &(g_cfg_file_cachedreal[0]),\n\t\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t\tcfg_strlcat(&(g_cfg_tmp_path[0]), direntptr->d_name,\n\t\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t\tret = cfg_stat(&g_cfg_tmp_path[0], &stat_buf, 0);\n\t\tlen = (int)strlen(g_cfg_tmp_path);\n\t\tis_dir = 0;\n\t\tis_gz = 0;\n\t\tif((len > 3) && !cfgcasecmp(&g_cfg_tmp_path[len - 3], \".gz\")) {\n\t\t\tis_gz = 1;\n\t\t}\n\t\tif((len > 4) && !cfgcasecmp(&g_cfg_tmp_path[len - 4], \".bz2\")) {\n\t\t\tis_gz = 1;\n\t\t}\n\t\tif((len > 4) && !cfgcasecmp(&g_cfg_tmp_path[len - 4], \".zip\")) {\n\t\t\tis_gz = 1;\n\t\t}\n\t\tif((len > 4) && !cfgcasecmp(&g_cfg_tmp_path[len - 4], \".woz\")) {\n\t\t\tis_gz = 1;\n\t\t}\n\t\tif((len > 4) && !cfgcasecmp(&g_cfg_tmp_path[len - 4], \".sdk\")) {\n\t\t\tis_gz = 1;\n\t\t}\n\t\tif(ret != 0) {\n\t\t\tprintf(\"stat %s ret %d, errno:%d\\n\", &g_cfg_tmp_path[0],\n\t\t\t\t\t\tret, errno);\n\t\t\tstat_buf.st_size = 0;\n\t\t\tcontinue;\t/* skip it */\n\t\t} else {\n\t\t\tfmt = stat_buf.st_mode & S_IFMT;\n\t\t\tsize = (int)stat_buf.st_size;\n\t\t\tif(fmt == S_IFDIR) {\n\t\t\t\t/* it's a directory */\n\t\t\t\tis_dir = 1;\n\t\t\t} else if((fmt == S_IFREG) && (is_gz == 0)) {\n\t\t\t\tif((g_cfg_slotdrive & 0xfff) == 0xfff) {\n\t\t\t\t\t/* see if there are size limits */\n\t\t\t\t\tif((size < g_cfg_file_min_size) ||\n\t\t\t\t\t\t(size > g_cfg_file_max_size)) {\n\t\t\t\t\t\tcontinue;\t/* skip it */\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif(size < 140*1024) {\n\t\t\t\t\t\tcontinue;\t/* skip it */\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcfg_file_add_dirent(&g_cfg_dirlist, direntptr->d_name, is_dir,\n\t\t\t\t\t(dword64)stat_buf.st_size, 0, 0, -1);\n\t}\n\n\tclosedir(dirptr);\n\n\t/* then sort the results (Mac's HFS+ is sorted, but other FS won't be)*/\n\tqsort(&(g_cfg_dirlist.direntptr[0]), g_cfg_dirlist.last,\n\t\t\t\t\tsizeof(Cfg_dirent), cfg_dirent_sortfn);\n\n\tg_cfg_dirlist.curent = g_cfg_dirlist.last - 1;\n\tfor(i = g_cfg_dirlist.last - 1; i >= 0; i--) {\n\t\tret = cfg_str_match(&(g_cfg_file_match[0]),\n\t\t\t\tg_cfg_dirlist.direntptr[i].name, CFG_PATH_MAX);\n\t\tif(ret <= 0) {\n\t\t\t/* set cur ent to closest filename to the match name */\n\t\t\tg_cfg_dirlist.curent = i;\n\t\t}\n\t}\n}\n\nchar *\ncfg_shorten_filename(const char *in_ptr, int maxlen)\n{\n\tchar\t*out_ptr;\n\tint\tlen, c;\n\tint\ti;\n\n\t/* Warning: uses a static string, not reentrant! */\n\n\tout_ptr = &(g_cfg_file_shortened[0]);\n\tlen = (int)strlen(in_ptr);\n\tmaxlen = MY_MIN(len, maxlen);\n\tfor(i = 0; i < maxlen; i++) {\n\t\tc = in_ptr[i] & 0x7f;\n\t\tif(c < 0x20) {\n\t\t\tc = '*';\n\t\t}\n\t\tout_ptr[i] = c;\n\t}\n\tout_ptr[maxlen] = 0;\n\tif(len > maxlen) {\n\t\tfor(i = 0; i < (maxlen/2); i++) {\n\t\t\tc = in_ptr[len-i-1] & 0x7f;\n\t\t\tif(c < 0x20) {\n\t\t\t\tc = '*';\n\t\t\t}\n\t\t\tout_ptr[maxlen-i-1] = c;\n\t\t}\n\t\tout_ptr[(maxlen/2) - 1] = '.';\n\t\tout_ptr[maxlen/2] = '.';\n\t\tout_ptr[(maxlen/2) + 1] = '.';\n\t}\n\n\treturn out_ptr;\n}\n\nvoid\ncfg_fix_topent(Cfg_listhdr *listhdrptr)\n{\n\tint\tnum_to_show;\n\n\tnum_to_show = listhdrptr->num_to_show;\n\n\t/* Force curent and topent to make sense */\n\tif(listhdrptr->curent >= listhdrptr->last) {\n\t\tlisthdrptr->curent = listhdrptr->last - 1;\n\t}\n\tif(listhdrptr->curent < 0) {\n\t\tlisthdrptr->curent = 0;\n\t}\n\tif(abs(listhdrptr->curent - listhdrptr->topent) >= num_to_show) {\n\t\tlisthdrptr->topent = listhdrptr->curent - (num_to_show/2);\n\t}\n\tif(listhdrptr->topent > listhdrptr->curent) {\n\t\tlisthdrptr->topent = listhdrptr->curent - (num_to_show/2);\n\t}\n\tif(listhdrptr->topent < 0) {\n\t\tlisthdrptr->topent = 0;\n\t}\n}\n\nvoid\ncfg_file_draw()\n{\n\tCfg_listhdr *listhdrptr;\n\tCfg_dirent *direntptr;\n\tconst char *tmp_str;\n\tchar\t*str, *fmt;\n\tint\tnum_to_show;\n\tint\tyoffset;\n\tint\tx, y;\n\tint\ti;\n\n\t//printf(\"cfg_file_draw called\\n\");\n\n\tcfg_file_readdir(&g_cfg_file_curpath[0]);\n\n\tfor(y = 0; y < 21; y++) {\n\t\tcfg_htab_vtab(0, y);\n\t\tcfg_printf(\"\\tZ\\t\");\n\t\tfor(x = 1; x < 79; x++) {\n\t\t\tcfg_htab_vtab(x, y);\n\t\t\tcfg_putchar(' ');\n\t\t}\n\t\tcfg_htab_vtab(79, y);\n\t\tcfg_printf(\"\\t_\\t\");\n\t}\n\n\tcfg_htab_vtab(1, 0);\n\tcfg_putchar('\\b');\n\tfor(x = 1; x < 79; x++) {\n\t\tcfg_putchar(' ');\n\t}\n\tif((g_cfg_slotdrive & 0xfff) == 0xfff) {\n\t\tcfg_htab_vtab(5, 0);\n\t\tcfg_printf(\"\\bSelect file to use as %-40s\\b\",\n\t\t\tcfg_shorten_filename(g_cfg_file_def_name, 40));\n\t} else {\n\t\tcfg_htab_vtab(30, 0);\n\t\ttmp_str = \"Select\";\n\t\tif(g_cfg_newdisk_select == 2) {\n\t\t\ttmp_str = \"Create duplicate\";\n\t\t} else if(g_cfg_newdisk_select) {\n\t\t\ttmp_str = \"Create new\";\n\t\t}\n\t\tcfg_printf(\"\\b%s image for s%dd%d\\b\", tmp_str,\n\t\t\t(g_cfg_slotdrive >> 8) & 0xf,\n\t\t\t(g_cfg_slotdrive & 0xff) + 1);\n\t}\n\n\tcfg_htab_vtab(2, 1);\n\tcfg_printf(\"config.kegs path: %-56s\",\n\t\t\tcfg_shorten_filename(&g_config_kegs_name[0], 56));\n\n\tcfg_htab_vtab(2, 2);\n\tcfg_printf(\"Current KEGS directory: %-50s\",\n\t\t\tcfg_shorten_filename(&g_cfg_cwd_str[0], 50));\n\n\tcfg_htab_vtab(2, 3);\n\n\tstr = \"\";\n\tif(g_cfg_file_pathfield) {\n\t\tstr = \"\\b \\b\";\n\t}\n\tcfg_printf(\"Path: %s%s\",\n\t\t\tcfg_shorten_filename(&g_cfg_file_curpath[0], 68), str);\n\n\tcfg_htab_vtab(0, 4);\n\tcfg_printf(\" \\t\");\n\tfor(x = 1; x < 79; x++) {\n\t\tcfg_putchar('\\\\');\n\t}\n\tcfg_printf(\"\\t \");\n\n\t/* Force curent and topent to make sense */\n\tlisthdrptr = &g_cfg_dirlist;\n\tnum_to_show = CFG_NUM_SHOWENTS;\n\tyoffset = 5;\n\tif(g_cfg_select_partition > 0) {\n\t\tlisthdrptr = &g_cfg_partitionlist;\n\t\tnum_to_show -= 2;\n\t\tcfg_htab_vtab(2, yoffset);\n\t\tcfg_printf(\"Select partition of %-50s\",\n\t\t\tcfg_shorten_filename(&g_cfg_file_path[0], 50), \"\");\n\t\tcfg_htab_vtab(2, yoffset + 1);\n\t\tcfg_printf(\"Current partition: %-50s\",\n\t\t\tcfg_shorten_filename(&g_cfg_part_path[0], 50), \"\");\n\t\tyoffset += 2;\n\t}\n\n\tlisthdrptr->num_to_show = num_to_show;\n\tcfg_fix_topent(listhdrptr);\n\tfor(i = 0; i < num_to_show; i++) {\n\t\ty = i + yoffset;\n\t\tif(listhdrptr->last > (i + listhdrptr->topent)) {\n\t\t\tdirentptr = &(listhdrptr->\n\t\t\t\t\tdirentptr[i + listhdrptr->topent]);\n\t\t\tcfg_htab_vtab(2, y);\n\t\t\tif(direntptr->is_dir) {\n\t\t\t\tcfg_printf(\"\\tXY\\t \");\n\t\t\t} else {\n\t\t\t\tcfg_printf(\"   \");\n\t\t\t}\n\t\t\tif(direntptr->part_num >= 0) {\n\t\t\t\tcfg_printf(\"%3d: \", direntptr->part_num);\n\t\t\t}\n\t\t\tstr = cfg_shorten_filename(direntptr->name, 50);\n\t\t\tfmt = \"%-50s\";\n\t\t\tif((i + listhdrptr->topent) == listhdrptr->curent) {\n\t\t\t\tif(g_cfg_file_pathfield == 0) {\n\t\t\t\t\tfmt = \"\\b%-50s\\b\";\n\t\t\t\t} else {\n\t\t\t\t\tfmt = \"%-49s\\b \\b\";\n\t\t\t\t}\n\t\t\t\t//printf(\"file highlight l %d top:%d cur:%d\\n\",\n\t\t\t\t//\ti, listhdrptr->topent,\n\t\t\t\t//\tlisthdrptr->curent);\n\t\t\t}\n\t\t\tcfg_printf(fmt, str);\n\t\t\tif(!direntptr->is_dir) {\n\t\t\t\tcfg_print_dnum(direntptr->dsize, 18);\n\t\t\t}\n\t\t\t//printf(\" :%s:%lld:\\n\", str, direntptr->dsize);\n\t\t}\n\t}\n\n\tcfg_htab_vtab(1, 5 + CFG_NUM_SHOWENTS);\n\tcfg_putchar('\\t');\n\tfor(x = 1; x < 79; x++) {\n\t\tcfg_putchar('L');\n\t}\n\tcfg_putchar('\\t');\n\n\t//printf(\"cfg_file_draw done\\n\");\n}\n\nvoid\ncfg_partition_select_all()\n{\n\tword32\tslot_drive;\n\tint\tpart_path_len, curent;\n\n\tslot_drive = g_cfg_slotdrive;\n\tpart_path_len = (int)strlen(&g_cfg_part_path[0]);\n\tcurent = g_cfg_partitionlist.curent;\n\twhile(1) {\n\t\tg_cfg_slotdrive = slot_drive;\n\t\tg_cfg_partitionlist.curent = curent;\n\t\tcfg_partition_selected();\n\t\tif(g_cfg_slotdrive != 0) {\n\t\t\t// Something went wrong, get out\n\t\t\treturn;\n\t\t}\n\t\tslot_drive++;\n\t\tcurent++;\n\t\tg_cfg_part_path[part_path_len] = 0;\n\t\tif(curent >= g_cfg_partitionlist.last) {\n\t\t\treturn;\n\t\t}\n\t\tif((slot_drive >> 8) == 7) {\n\t\t\tif((slot_drive & 0xff) >= MAX_C7_DISKS) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif((slot_drive & 0xff) >= 12) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if((slot_drive & 0xff) >= 2) {\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid\ncfg_partition_selected()\n{\n\tchar\t*str;\n\tconst char *part_str;\n\tchar\t*part_str2;\n\tint\tpos;\n\tint\tpart_num;\n\n\tpos = g_cfg_partitionlist.curent;\n\tstr = g_cfg_partitionlist.direntptr[pos].name;\n\tif(g_cfg_partitionlist.direntptr[pos].is_dir) {\n\t\t// Add this path to the partition path, and try again\n\t\tif(!strcmp(str, \"../\")) {\n\t\t\t/* go up one directory */\n\t\t\tcfg_get_base_path(&g_cfg_part_path[0],\n\t\t\t\t\t\t&g_cfg_part_path[0], 1);\n\t\t} else {\n\t\t\tcfg_strlcat(&(g_cfg_part_path[0]), str, CFG_PATH_MAX);\n\t\t}\n\t\tcfg_partition_make_list_from_name(&g_cfg_file_path[0]);\n\t\treturn;\n\t}\n\n\tpart_num = -2;\n\tpart_str = 0;\n\tif(str[0] == 0 || (str[0] >= '0' && str[0] <= '9')) {\n\t\tpart_num = g_cfg_partitionlist.direntptr[pos].part_num;\n\t} else {\n\t\tpart_str = str;\n\t}\n\tpart_str2 = 0;\n\tif(part_str != 0) {\n\t\tcfg_strlcat(&g_cfg_part_path[0], part_str, CFG_PATH_MAX);\n\t\tpart_str2 = kegs_malloc_str(&g_cfg_part_path[0]);\n\t\tg_cfg_part_path[0] = 0;\n\t}\n\tprintf(\"cfg_partition_selected, pos:%d, g_cfg_file_path[0]:%s, \"\n\t\t\"part:%s\\n\", pos, g_cfg_file_path, part_str2);\n\n\tinsert_disk((g_cfg_slotdrive >> 8) & 0xf, g_cfg_slotdrive & 0xff,\n\t\t\t&(g_cfg_file_path[0]), 0, part_str2, part_num, 0);\n\tfree(part_str2);\n\tg_cfg_slotdrive = 0;\n\tg_cfg_newdisk_select = 0;\n\tg_cfg_select_partition = -1;\n}\n\nvoid\ncfg_file_selected()\n{\n\tstruct stat stat_buf;\n\tchar\t*str;\n\tint\tfmt, stat_errno, is_cmd_key_down;\n\tint\tret;\n\n\tis_cmd_key_down = adb_is_cmd_key_down() &&\n\t\t\t\t\t((g_cfg_slotdrive & 0xfff) != 0xfff);\n\t\t// Cmd-Return means create DynaPro image when using slot/drive\n\n\tif(g_cfg_select_partition > 0) {\n\t\tcfg_partition_selected();\n\t\treturn;\n\t}\n\n\tif(!is_cmd_key_down && (g_cfg_file_pathfield == 0)) {\n\t\t// in file section area of window\n\t\tstr = g_cfg_dirlist.direntptr[g_cfg_dirlist.curent].name;\n\t\tif(!strcmp(str, \"../\")) {\n\t\t\t/* go up one directory */\n\t\t\tcfg_get_base_path(&g_cfg_file_curpath[0],\n\t\t\t\t\t\t&g_cfg_file_curpath[0], 1);\n\t\t\treturn;\n\t\t}\n\n\t\tcfg_strncpy(&(g_cfg_file_path[0]), &(g_cfg_file_cachedreal[0]),\n\t\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t\tcfg_strlcat(&(g_cfg_file_path[0]), str, CFG_PATH_MAX);\n\t} else {\n\t\t// just use cfg_file_curpath directly\n\t\tcfg_strncpy(&g_cfg_file_path[0], &g_cfg_file_curpath[0],\n\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t}\n\n\tret = cfg_stat(&g_cfg_file_path[0], &stat_buf, 0);\n\tstat_errno = errno;\n\tfmt = stat_buf.st_mode & S_IFMT;\n\tcfg_printf(\"Stat'ing %s, st_mode is: %08x\\n\", &g_cfg_file_path[0],\n\t\t\t(int)stat_buf.st_mode);\n\n\tif((ret == 0) && (fmt == S_IFDIR) && is_cmd_key_down &&\n\t\t\t\t\t\t(g_cfg_newdisk_select != 2)) {\n\t\t// Make a new DynaPro disk\n\t\tcfg_insert_disk_dynapro((g_cfg_slotdrive >> 8) & 0xf,\n\t\t\t\tg_cfg_slotdrive & 0xff, &g_cfg_file_path[0]);\n\t\tg_cfg_slotdrive = 0;\t\t\t// End file selection\n\t\tg_cfg_newdisk_select = 0;\n\t\tg_menuptr = &g_cfg_disk_menu[0];\n\t} else if((g_cfg_newdisk_select == 1) && (g_cfg_newdisk_type == 3) &&\n\t\t\tg_cfg_file_pathfield && (fmt == S_IFDIR)) {\n\t\t// Special handling for Dynamic ProDOS directories.  User hit\n\t\t//  return in the Path field on a directory, use this directory\n\t\tcfg_create_new_image();\n\t} if(ret != 0) {\n\t\tif(g_cfg_newdisk_select && (g_cfg_newdisk_type != 3)) {\n\t\t\t// This looks good, a new file name was entered\n\t\t\tif(stat_errno == ENOENT) {\n\t\t\t\tcfg_create_new_image();\n\t\t\t} else {\n\t\t\t\tprintf(\"Unknown errno:%d while checking %s\\n\",\n\t\t\t\t\tstat_errno, &g_cfg_file_path[0]);\n\t\t\t}\n\t\t} else {\n\t\t\tprintf(\"stat %s returned %d, errno: %d\\n\",\n\t\t\t\t\t&g_cfg_file_path[0], ret, stat_errno);\n\t\t}\n\t} else if(fmt == S_IFDIR) {\n\t\t/* it's a directory */\n\t\tcfg_strncpy(&g_cfg_file_curpath[0], &g_cfg_file_path[0],\n\t\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t} else if(g_cfg_newdisk_select) {\n\t\t// Do not allow selecting files, just ignore it\n\t} else if((g_cfg_slotdrive & 0xfff) < 0xfff) {\n\t\t/* select it */\n\t\tret = cfg_maybe_insert_disk((g_cfg_slotdrive >> 8) & 0xf,\n\t\t\t\tg_cfg_slotdrive & 0xff, &g_cfg_file_path[0]);\n\t\tif(ret > 0) {\n\t\t\tg_cfg_slotdrive = 0;\n\t\t\tg_cfg_newdisk_select = 0;\n\t\t}\n\t} else {\n\t\tcfg_file_update_ptr(g_cfg_file_strptr, &g_cfg_file_path[0], 1);\n\t\tg_cfg_slotdrive = 0;\n\t\tg_cfg_newdisk_select = 0;\n\t}\n}\n\nvoid\ncfg_file_handle_key(int key)\n{\n\tCfg_listhdr *listhdrptr;\n\tint\tlen, lowkey, got_match_key, is_cmd_key_down;\n\n\t// Modes: g_cfg_slotdrive: 1 to 0xfff: File selection dialog\n\t//\t\totherwise: normal menu being shown\n\t//\tg_cfg_file_pathfield: File selection with cursor in Path: field\n\t//\t\totherwise: in scrolling file selection field\n\t//\tg_cfg_select_partition: file selection for partition name\n\tif(g_cfg_file_pathfield) {\n\t\tif(key >= 0x20 && key < 0x7f) {\n\t\t\tlen = (int)strlen(&g_cfg_file_curpath[0]);\n\t\t\tif(len < CFG_PATH_MAX-4) {\n\t\t\t\tg_cfg_file_curpath[len] = key;\n\t\t\t\tg_cfg_file_curpath[len+1] = 0;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\n\tlisthdrptr = &g_cfg_dirlist;\n\tis_cmd_key_down = 0;\n\tif(g_cfg_select_partition > 0) {\n\t\tlisthdrptr = &g_cfg_partitionlist;\n\t\tis_cmd_key_down = adb_is_cmd_key_down() &&\n\t\t\t\t\t((g_cfg_slotdrive & 0xfff) != 0xfff);\n\t}\n\tlowkey = tolower(key);\n\tgot_match_key = 0;\n\tif((g_cfg_file_pathfield == 0) && (lowkey >= 'a') && (lowkey <= 'z') &&\n\t\t\t\t\t\t!is_cmd_key_down) {\n\t\t/* jump to file starting with this letter */\n\t\tg_cfg_file_match[0] = key;\n\t\tg_cfg_file_match[1] = 0;\n\t\tg_cfg_dirlist.invalid = 1;\t/* re-read directory */\n\t\tgot_match_key = 1;\n\t}\n\n\tswitch(key) {\n\tcase 0x1b:\t\t\t\t// ESC\n\t\tif(((g_cfg_slotdrive & 0xfff) < 0xfff) &&\n\t\t\t\t\t\t\t!g_cfg_newdisk_select) {\n\t\t\tiwm_eject_disk_by_num((g_cfg_slotdrive >> 8) & 0xf,\n\t\t\t\t\t\tg_cfg_slotdrive & 0xff);\n\t\t}\n\t\tg_cfg_slotdrive = 0;\n\t\tg_cfg_select_partition = -1;\n\t\tg_cfg_dirlist.invalid = 1;\n\t\tg_cfg_newdisk_select = 0;\n\t\tbreak;\n\tcase 0x0a:\t/* down arrow */\n\t\tif(g_cfg_file_pathfield == 0) {\n\t\t\tlisthdrptr->curent++;\n\t\t\tcfg_fix_topent(listhdrptr);\n\t\t}\n\t\tbreak;\n\tcase 0x0b:\t/* up arrow */\n\t\tif(g_cfg_file_pathfield == 0) {\n\t\t\tlisthdrptr->curent--;\n\t\t\tcfg_fix_topent(listhdrptr);\n\t\t}\n\t\tbreak;\n\tcase 0x0d:\t/* return */\n\t\t//printf(\"handling return press\\n\");\n\t\tcfg_file_selected();\n\t\tbreak;\n\tcase 0x61:\t/* 'a' */\n\t\tif(is_cmd_key_down && (g_cfg_select_partition > 0)) {\n\t\t\tcfg_partition_select_all();\n\t\t}\n\t\tbreak;\n\tcase 0x09:\t/* tab */\n\t\tg_cfg_file_pathfield = !g_cfg_file_pathfield;\n\t\tif(g_cfg_select_partition > 0) {\n\t\t\t// If selecting file inside zip or partition, don't\n\t\t\t//  allow editing of the Path info\n\t\t\tg_cfg_file_pathfield = 0;\n\t\t}\n\t\tbreak;\n\tcase 0x08:\t/* left arrow */\n\tcase 0x7f:\t/* delete key */\n\t\tif(g_cfg_file_pathfield) {\n\t\t\t// printf(\"left arrow/delete\\n\");\n\t\t\tlen = (int)strlen(&g_cfg_file_curpath[0]) - 1;\n\t\t\tif(len >= 0) {\n\t\t\t\tg_cfg_file_curpath[len] = 0;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tif(!got_match_key) {\n\t\t\tprintf(\"key: %02x\\n\", key);\n\t\t}\n\t}\n#if 0\n\tprintf(\"curent: %d, topent: %d, last: %d\\n\",\n\t\tg_cfg_dirlist.curent, g_cfg_dirlist.topent, g_cfg_dirlist.last);\n#endif\n}\n\nvoid\ncfg_draw_menu()\n{\n\tconst char *str;\n\tCfg_menu *menuptr;\n\tint\tprint_eject_help, line, type, match_found, menu_line, max_line;\n\n\tg_menu_redraw_needed = 0;\n\n\tmenuptr = g_menuptr;\n\tif(menuptr == 0) {\n\t\tmenuptr = g_cfg_main_menu;\n\t}\n\tif(g_rom_version < 0) {\n\t\t/* Must select ROM file */\n\t\tmenuptr = g_cfg_rom_menu;\n\t}\n\tg_menuptr = menuptr;\n\n\tcfg_home();\n\tline = 1;\n\tmax_line = 1;\n\tmatch_found = 0;\n\tprint_eject_help = 0;\n\tmenu_line = g_menu_line;\n\tcfg_printf(\"%s\\n\\n\", menuptr[0].str);\n\twhile(line < 24) {\n\t\tstr = menuptr[line].str;\n\t\ttype = menuptr[line].cfgtype;\n\t\tif(str == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tif((type & 0xf) == CFGTYPE_DISK) {\n\t\t\tprint_eject_help = 1;\n\t\t}\n#if 0\n\t\tprintf(\"Calling parse_menu line:%d, menu_line:%d, %p\\n\", line,\n\t\t\t\t\t\t\tmenu_line, menuptr);\n#endif\n\t\tcfg_parse_menu(menuptr, line, menu_line, 0);\n\t\tif(line == g_menu_line) {\n\t\t\tif(type != 0) {\n\t\t\t\tmatch_found = 1;\n\t\t\t} else if(g_menu_inc) {\n\t\t\t\tmenu_line++;\n\t\t\t} else {\n\t\t\t\tmenu_line--;\n\t\t\t}\n\t\t}\n\t\tif(line > max_line) {\n\t\t\tmax_line = line;\n\t\t}\n\n\t\tcfg_printf(\"%s\\n\", g_cfg_opt_buf);\n\t\tline++;\n\t}\n\tif((menu_line < 1) && !match_found) {\n\t\tmenu_line = 1;\n\t}\n\tif((menu_line >= max_line) && !match_found) {\n\t\tmenu_line = max_line;\n\t}\n\n\tg_menu_line = menu_line;\n\tg_menu_max_line = max_line;\n\tif(!match_found) {\n\t\tg_menu_redraw_needed = 1;\n\t}\n\tif(g_rom_version < 0) {\n\t\tcfg_htab_vtab(0, 21);\n\t\tcfg_printf(\"\\bYOU MUST SELECT A VALID ROM FILE\\b\\n\");\n\t}\n\n\tcfg_htab_vtab(0, 23);\n\tcfg_printf(\"Move: \\tJ\\t \\tK\\t Change: \\tH\\t \\tU\\t \\tM\\t\");\n\tif(print_eject_help) {\n\t\tcfg_printf(\"  Eject: \");\n\t\tif((g_cfg_slotdrive & 0xfff) > 0) {\n\t\t\tcfg_printf(\"\\bESC\\b\");\n\t\t} else {\n\t\t\tcfg_printf(\"E\");\n\t\t\tcfg_printf(\"  New image: N  Dup image: D  Verify: V\");\n\t\t}\n\t}\n\tif((g_cfg_slotdrive & 0xfff) > 0) {\n\t\tcfg_printf(\"  Edit Path: \\bTAB\\b\");\n\t\tif(g_cfg_select_partition > 0) {\n\t\t\tcfg_printf(\"  (\\bCmd\\b-\\bA\\b to mount all)\");\n\t\t} else if((g_cfg_newdisk_type == 3) || !g_cfg_newdisk_select) {\n\t\t\t// Dynamic ProDOS, select a directory\n\t\t\tcfg_printf(\"  (\\bCmd\\b-\\bEnter\\b for DynaPro)\");\n\t\t}\n\t\tif(g_cfg_newdisk_select && (g_cfg_newdisk_type != 3)) {\n\t\t\tcfg_printf(\"  (Enter new name on Path)\");\n\t\t}\n\t}\n#if 0\n\tcfg_htab_vtab(0, 22);\n\tcfg_printf(\"menu_line: %d line: %d, vbl:%d, adb:%d key_dn:%d\\n\",\n\t\t\tmenu_line, line, g_cfg_vbl_count, g_adb_repeat_vbl,\n\t\t\tg_key_down);\n#endif\n\n\tif((g_cfg_slotdrive & 0xfff) > 0) {\n\t\tcfg_file_draw();\n\t}\n}\n\nvoid\ncfg_newdisk_pick_menu(word32 slotdrive)\n{\n\tslotdrive = slotdrive & 0xfff;\n\tg_cfg_newdisk_slotdrive = slotdrive;\t// 0x601: s6d2, 0x500: s5d1\n\tg_menu_line = 1;\n\t//printf(\"N key, g_menuptr=%p\\n\", g_menuptr);\n\tg_cfg_newdisk_type_default = 1;\n\tg_cfg_newdisk_type = 1;\n\tg_cfg_newdisk_blocks_default = 140*2;\n\tg_cfg_newdisk_blocks = 280;\n\tif((slotdrive >> 8) == 6) {\n\t\tg_menuptr = g_cfg_newslot6_menu;\n\t} else if((slotdrive >> 8) == 5) {\n\t\tg_menuptr = g_cfg_newslot5_menu;\n\t\tg_cfg_newdisk_blocks_default = 1600;\n\t\tg_cfg_newdisk_blocks = 1600;\n\t} else {\n\t\tg_menuptr = g_cfg_newslot7_menu;\n\t\tg_cfg_newdisk_blocks_default = 65535;\n\t\tg_cfg_newdisk_blocks = 65535;\n\t}\n}\n\nint\ncfg_control_panel_update()\n{\n\tint\tret;\n\tint\ti;\n\n\tret = cfg_control_panel_update1();\n\tif(g_cfg_screen_changed) {\n\t\tfor(i = 0; i < 24; i++) {\n\t\t\tvideo_draw_a2_string(i, &g_cfg_screen[i][0]);\n\t\t}\n\t}\n\tg_cfg_screen_changed = 0;\n\n\treturn ret;\n}\n\nvoid\ncfg_edit_mode_key(int key)\n{\n\tchar\t*new_str;\n\tint\t*iptr;\n\tint\tlen, ival;\n\n\tlen = (int)strlen(&g_cfg_edit_buf[0]);\n\tif(key == 0x0d) {\t\t// Return\n\t\t// Try to accept the change\n\t\tnew_str = kegs_malloc_str(&g_cfg_edit_buf[0]);\n\t\tif(g_cfg_edit_type == CFGTYPE_STR) {\n\t\t\tcfg_file_update_ptr(g_cfg_edit_ptr, new_str, 1);\n\t\t} else if(g_cfg_edit_type == CFGTYPE_INT) {\n\t\t\tival = strtol(&g_cfg_edit_buf[0], 0, 0);\n\t\t\tiptr = (int *)g_cfg_edit_ptr;\n\t\t\tcfg_int_update(iptr, ival);\n\t\t}\n\t\tg_cfg_edit_ptr = 0;\n\t\tg_config_kegs_update_needed = 1;\n\t} else if(key == 0x1b) {\t// ESC\n\t\tg_cfg_edit_ptr = 0;\t// Abort out of edit mode, no changes\n\t} else if((key == 0x08) || (key == 0x7f)) {\t// Left arrow or Delete\n\t\tlen--;\n\t\tif(len >= 0) {\n\t\t\tg_cfg_edit_buf[len] = 0;\n\t\t}\n\t} else if((key >= 0x20) && (key < 0x7f)) {\n\t\tif(len < (CFG_OPT_MAXSTR - 3)) {\n\t\t\tg_cfg_edit_buf[len] = key;\n\t\t\tg_cfg_edit_buf[len+1] = 0;\n\t\t}\n\t}\n}\n\nint\ncfg_control_panel_update1()\n{\n\tchar\t*(*fn_ptr)(int);\n\tvoid\t*ptr;\n\tchar\t**str_ptr;\n\tint\t*iptr;\n\tint\ttype, key;\n\n\twhile(g_config_control_panel) {\n\t\tif(g_menu_redraw_needed) {\n\t\t\tcfg_draw_menu();\n\t\t}\n\t\tif(g_menu_redraw_needed) {\n\t\t\tcfg_draw_menu();\n\t\t}\n\t\tkey = adb_read_c000();\n\t\tif(key & 0x80) {\n\t\t\tkey = key & 0x7f;\n\t\t\t(void)adb_access_c010();\n\t\t} else {\n\t\t\treturn 0;\t\t// No keys\n\t\t}\n\t\tg_menu_redraw_needed = 1;\n\t\t// If we get here, we got a key, figure out what to do with it\n\t\tif(g_cfg_slotdrive & 0xfff) {\n\t\t\tcfg_file_handle_key(key);\n\t\t\tcontinue;\n\t\t}\n\t\tif(g_cfg_edit_ptr) {\n\t\t\tcfg_edit_mode_key(key);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Normal menu system\n\t\tswitch(key) {\n\t\tcase 0x0a: /* down arrow */\n\t\t\tg_menu_line++;\n\t\t\tg_menu_inc = 1;\n\t\t\tbreak;\n\t\tcase 0x0b: /* up arrow */\n\t\t\tg_menu_line--;\n\t\t\tg_menu_inc = 0;\n\t\t\tif(g_menu_line < 1) {\n\t\t\t\tg_menu_line = 1;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x15: /* right arrow */\n\t\t\tcfg_parse_menu(g_menuptr, g_menu_line, g_menu_line, 1);\n\t\t\tbreak;\n\t\tcase 0x08: /* left arrow */\n\t\t\tcfg_parse_menu(g_menuptr, g_menu_line, g_menu_line, -1);\n\t\t\tbreak;\n\t\tcase 0x0d:\n\t\t\ttype = g_menuptr[g_menu_line].cfgtype;\n\t\t\tptr = g_menuptr[g_menu_line].ptr;\n\t\t\tswitch(type & 0xf) {\n\t\t\tcase CFGTYPE_MENU:\n\t\t\t\tg_menuptr = (Cfg_menu *)ptr;\n\t\t\t\tg_menu_line = 1;\n\t\t\t\tbreak;\n\t\t\tcase CFGTYPE_DISK:\n\t\t\t\tg_cfg_slotdrive = (type >> 4) & 0xfff;\n\t\t\t\tcfg_file_init();\n\t\t\t\tbreak;\n\t\t\tcase CFGTYPE_FUNC:\n\t\t\t\tfn_ptr = (char * (*)(int))ptr;\n\t\t\t\t(void)(*fn_ptr)(0);\n\t\t\t\tbreak;\n\t\t\tcase CFGTYPE_FILE:\n\t\t\t\tg_cfg_slotdrive = 0xfff;\n\t\t\t\tg_cfg_file_def_name = *((char **)ptr);\n\t\t\t\tg_cfg_file_strptr = (char **)ptr;\n\t\t\t\tcfg_file_init();\n\t\t\t\tbreak;\n\t\t\tcase CFGTYPE_STR:\n\t\t\t\tstr_ptr = (char **)ptr;\n\t\t\t\tif(str_ptr) {\n\t\t\t\t\tg_cfg_edit_type = type & 0xf;\n\t\t\t\t\tg_cfg_edit_ptr = str_ptr;\n\t\t\t\t\tcfg_strncpy(&g_cfg_edit_buf[0],\n\t\t\t\t\t\t*str_ptr, CFG_OPT_MAXSTR);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase CFGTYPE_INT:\n\t\t\t\t// If there are no ',' in the menu str, then\n\t\t\t\t//  allow user to enter a manual number\n\t\t\t\tif(!strchr(g_menuptr[g_menu_line].str, ',')) {\n\t\t\t\t\tg_cfg_edit_type = type & 0xf;\n\t\t\t\t\tg_cfg_edit_ptr = ptr;\n\t\t\t\t\tiptr = (int *)ptr;\n\t\t\t\t\tsnprintf(&g_cfg_edit_buf[0],\n\t\t\t\t\t\tCFG_OPT_MAXSTR, \"%d\", *iptr);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x1b:\n\t\t\t// Jump to last menu entry\n\t\t\tg_menu_line = g_menu_max_line;\n\t\t\tbreak;\n\t\tcase 'd':\n\t\tcase 'D':\t\t\t// Duplicate an image\n\t\t\ttype = g_menuptr[g_menu_line].cfgtype;\n\t\t\tif((type & 0xf) == CFGTYPE_DISK) {\n\t\t\t\tcfg_dup_existing_image(type >> 4);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'e':\n\t\tcase 'E':\n\t\t\ttype = g_menuptr[g_menu_line].cfgtype;\n\t\t\tif((type & 0xf) == CFGTYPE_DISK) {\n\t\t\t\tiwm_eject_disk_by_num(type >> 12,\n\t\t\t\t\t\t\t(type >> 4) & 0xff);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'l':\n\t\tcase 'L':\n\t\t\ttype = g_menuptr[g_menu_line].cfgtype;\n\t\t\tif((type & 0xf) == CFGTYPE_DISK) {\n\t\t\t\tcfg_toggle_lock_disk(type >> 4);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'n':\n\t\tcase 'N':\n\t\t\ttype = g_menuptr[g_menu_line].cfgtype;\n\t\t\tif((type & 0xf) == CFGTYPE_DISK) {\n\t\t\t\tcfg_newdisk_pick_menu(type >> 4);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'v':\n\t\tcase 'V':\n\t\t\ttype = g_menuptr[g_menu_line].cfgtype;\n\t\t\tif((type & 0xf) == CFGTYPE_DISK) {\n\t\t\t\tcfg_validate_image(type >> 4);\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tprintf(\"key: %02x\\n\", key);\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "gsplus/src/config.h",
    "content": "#ifdef INCLUDE_RCSID_C\n#endif\n\n/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2019 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#define CONF_BUF_LEN\t\t1024\n#define COPY_BUF_SIZE\t\t4096\n#define CFG_PRINTF_BUFSIZE\t2048\n\n#define CFG_PATH_MAX\t\t1024\n\n#define CFG_NUM_SHOWENTS\t16\n\n#define CFGTYPE_MENU\t\t1\n#define CFGTYPE_INT\t\t2\n#define CFGTYPE_DISK\t\t3\n#define CFGTYPE_FUNC\t\t4\n#define CFGTYPE_FILE\t\t5\n#define CFGTYPE_STR\t\t6\n/* CFGTYPE limited to just 4 bits: 0-15 */\n\n/* Cfg_menu, Cfg_dirent and Cfg_listhdr are defined in defc.h */\n\nSTRUCT(Cfg_defval) {\n\tCfg_menu *menuptr;\n\tint\tintval;\n\tchar\t*strval;\n};\n"
  },
  {
    "path": "gsplus/src/cp_gsplus_libs",
    "content": "#!/usr/bin/perl -w\n# $KmKId: cp_kegs_libs,v 1.2 2021-02-09 00:35:48+00 kentd Exp $\n\nuse strict;\nuse English;\n\nif($#ARGV < 2) {\n\tdie \"Usage: executable srclib_dir destlib_dir\";\n}\n\n# Runs objdump on the executable, finds all unresolved dependencies, and\n#  then copies each of those libraries from srclib_dir to destlib_dir\n# destlib_dir should be APPLICATION/Contents/Frameworks/\n\nmy $exe = shift;\nmy $srcdir = shift;\nmy $destdir = shift;\nif(! -f $exe || ! -d $srcdir || ! -d $destdir) {\n\tdie \"$exe is not a file, or $srcdir or $destdir are not a dir\";\n}\n\nmy $do_swiftos = 0;\nopen(RPATHS, \"objdump -macho -dylibs-used $exe|\") or die \"Open failed: $!\";\nmy $line;\nmy $lib;\nforeach $line (<RPATHS>) {\n\tchomp($line);\n\tif($line =~ m:\\@rpath/([^ ]*) :) {\n\t\t$lib = $1;\n\t\tprint \"lib: $lib\\n\";\n\t\t`cp $srcdir/$lib $destdir/`;\n\t}\n\t$do_swiftos = 1;\n}\n\n# And copy libswiftos.dylib if we copied any files\nif($do_swiftos) {\n\t`cp $srcdir/libswiftos.dylib $destdir/`;\n}\n"
  },
  {
    "path": "gsplus/src/debugger.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n\n#include <stdio.h>\n#include <stdarg.h>\n#include \"defc.h\"\n\n#include \"disas.h\"\n\n#define LINE_SIZE\t\t160\t\t/* Input buffer size */\n#define PRINTF_BUF_SIZE\t\t239\n#define DEBUG_ENTRY_MAX_CHARS\t80\n\nSTRUCT(Debug_entry) {\n\tbyte str_buf[DEBUG_ENTRY_MAX_CHARS];\n};\n\nchar g_debug_printf_buf[PRINTF_BUF_SIZE];\nchar g_debug_stage_buf[PRINTF_BUF_SIZE];\nint\tg_debug_stage_pos = 0;\n\nDebug_entry *g_debug_lines_ptr = 0;\nint\tg_debug_lines_total = 0;\nint\tg_debug_lines_pos = 0;\nint\tg_debug_lines_alloc = 0;\nint\tg_debug_lines_max = 1024*1024;\nint\tg_debug_lines_view = -1;\nint\tg_debug_lines_viewable_lines = 20;\nint\tg_debugwin_changed = 1;\nint\tg_debug_to_stdout = 1;\n\nextern byte *g_memory_ptr;\nextern byte *g_slow_memory_ptr;\nextern int g_halt_sim;\nextern word32 g_c068_statereg;\nextern word32 stop_run_at;\nextern int Verbose;\nextern int Halt_on;\nextern int g_a2_key_to_ascii[][4];\nextern Kimage g_debugwin_kimage;\n\nextern int g_config_control_panel;\nextern word32 g_mem_size_total;\n\nextern char *g_sound_file_str;\nextern word32 g_sound_file_bytes;\n\nint\tg_num_breakpoints = 0;\nBreak_point g_break_pts[MAX_BREAK_POINTS];\n\nextern int g_irq_pending;\n\nextern dword64 g_last_vbl_dcyc;\nextern int g_ret1;\nextern Engine_reg engine;\nextern dword64 g_dcycles_end;\n\nint g_stepping = 0;\n\nword32\tg_list_kpc;\nint\tg_hex_line_len = 0x10;\nword32\tg_a1 = 0;\nword32\tg_a2 = 0;\nword32\tg_a3 = 0;\nword32\tg_a4 = 0;\nword32\tg_a1bank = 0;\nword32\tg_a2bank = 0;\nword32\tg_a3bank = 0;\nword32\tg_a4bank = 0;\n\n#define\tMAX_CMD_BUFFER\t\t229\n\n#define PC_LOG_LEN\t\t(2*1024*1024)\n\nPc_log g_pc_log_array[PC_LOG_LEN + 2];\nData_log g_data_log_array[PC_LOG_LEN + 2];\n\nword32\tg_log_pc_enable = 0;\nPc_log\t*g_log_pc_ptr = &(g_pc_log_array[0]);\nPc_log\t*g_log_pc_start_ptr = &(g_pc_log_array[0]);\nPc_log\t*g_log_pc_end_ptr = &(g_pc_log_array[PC_LOG_LEN]);\n\nData_log *g_log_data_ptr = &(g_data_log_array[0]);\nData_log *g_log_data_start_ptr = &(g_data_log_array[0]);\nData_log *g_log_data_end_ptr = &(g_data_log_array[PC_LOG_LEN]);\n\nchar\tg_cmd_buffer[MAX_CMD_BUFFER + 2] = { 0 };\nint\tg_cmd_buffer_len = 2;\n\n#define MAX_DISAS_BUF\t\t150\nchar g_disas_buffer[MAX_DISAS_BUF];\n\nvoid\ndebugger_init()\n{\n\tdebugger_help();\n\tg_list_kpc = engine.kpc;\n#if 0\n\tif(g_num_breakpoints == 0) {\n\t\tset_bp(0xff5a0e, 0xff5a0e, 4);\n\t\tset_bp(0x00c50a, 0x00c50a, 4);\n\t\tset_bp(0x00c50d, 0x00c50d, 4);\n\t}\n#endif\n}\n\nint g_dbg_new_halt = 0;\n\nvoid\ncheck_breakpoints(word32 addr, dword64 dfcyc, word32 maybe_stack,\n\t\t\t\t\t\t\tword32 type)\n{\n\tBreak_point *bp_ptr;\n\tint\tcount;\n\tint\ti;\n\n\tcount = g_num_breakpoints;\n\tfor(i = 0; i < count; i++) {\n\t\tbp_ptr = &(g_break_pts[i]);\n\t\tif((type & bp_ptr->acc_type) == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tif((addr >= (bp_ptr->start_addr & 0xffffff)) &&\n\t\t\t\t(addr <= (bp_ptr->end_addr & 0xffffff))) {\n\t\t\tdebug_hit_bp(addr, dfcyc, maybe_stack, type, i);\n\t\t}\n\t}\n\n\tif((type == 4) && ((addr == 0xe10000) || (addr == 0xe10004))) {\n\t\tFINISH(RET_TOOLTRACE, 0);\n\t}\n}\n\nvoid\ndebug_hit_bp(word32 addr, dword64 dfcyc, word32 maybe_stack, word32 type,\n\t\t\t\t\t\t\t\tint pos)\n{\n\tword32\ttrk_side, side, trk, cmd, unit, buf, blk, param_cnt, list_ptr;\n\tword32\tstatus_code, cmd_list, stack, rts;\n\n\tif((addr == 0xff5a0e) && (type == 4)) {\n\t\ttrk_side = get_memory_c(0xe10f32);\n\t\tside = (trk_side >> 5) & 1;\n\t\ttrk = get_memory_c(0xe10f34) + ((trk_side & 0x1f) << 6);\n\t\tbuf = get_memory_c(0x42) | (get_memory_c(0x43) << 8) |\n\t\t\t\t\t\t(get_memory_c(0x44) << 16);\n\t\tprintf(\"ff5a0e: 3.5 read of track %03x side:%d sector:%03x to \"\n\t\t\t\"%06x at %016llx\\n\", trk, side, get_memory_c(0xe10f33),\n\t\t\tbuf, dfcyc);\n\t\treturn;\n\t}\n\tif((addr == 0x00c50a) && (type == 4)) {\n\t\tcmd = get_memory_c(0x42);\n\t\tunit = get_memory_c(0x43);\n\t\tbuf = get_memory_c(0x44) | (get_memory_c(0x45) << 8);\n\t\tblk = get_memory_c(0x46) | (get_memory_c(0x47) << 8);\n\t\tprintf(\"00c50a: cmd %02x u:%02x buf:%04x blk:%04x at %016llx\\n\",\n\t\t\tcmd, unit, buf, blk, dfcyc);\n\t\treturn;\n\t}\n\tif((addr == 0x00c50d) && (type == 4)) {\n\t\tstack = maybe_stack & 0xffff;\n\t\trts = get_memory_c(stack + 1) | (get_memory_c(stack + 2) << 8);\n\t\tcmd = get_memory_c(rts + 1);\n\t\tcmd_list = get_memory_c(rts + 2) | (get_memory_c(rts+3) << 8);\n\t\tparam_cnt = get_memory_c(cmd_list);\n\t\tunit = get_memory_c(cmd_list + 1);\n\t\tlist_ptr = get_memory_c(cmd_list + 2) |\n\t\t\t\t\t(get_memory_c(cmd_list + 3) << 8);\n\t\tstatus_code = get_memory_c(cmd_list + 4);\n\t\tprintf(\"00c50d: stack:%04x rts:%04x cmd:%02x cmd_list:%04x \"\n\t\t\t\"param_cnt:%02x unit:%02x listptr:%04x \"\n\t\t\t\"status:%02x at %016llx\\n\", stack, rts, cmd, cmd_list,\n\t\t\tparam_cnt, unit, list_ptr, status_code, dfcyc);\n\t\tprintf(\"  list_ptr: %04x: %02x %02x %02x %02x %02x %02x %02x\\n\",\n\t\t\tlist_ptr, get_memory_c(list_ptr),\n\t\t\tget_memory_c(list_ptr + 1), get_memory_c(list_ptr + 2),\n\t\t\tget_memory_c(list_ptr + 3), get_memory_c(list_ptr + 4),\n\t\t\tget_memory_c(list_ptr + 5), get_memory_c(list_ptr + 6));\n\t\treturn;\n\t}\n\n\tdbg_log_info(dfcyc, addr, pos, 0x6270);\n\thalt2_printf(\"Hit breakpoint at %06x\\n\", addr);\n}\n\nint\ndebugger_run_16ms()\n{\n\t// Called when g_halt_sim is set\n\tif(g_dbg_new_halt) {\n\t\tg_list_kpc = engine.kpc;\n\t\tshow_regs();\n\t}\n\tg_dbg_new_halt = 0;\n\tadb_nonmain_check();\n\t// printf(\"debugger_run_16ms: g_halt_sim:%d\\n\", g_halt_sim);\n\treturn 0;\n}\n\nvoid\ndbg_log_info(dword64 dfcyc, word32 info1, word32 info2, word32 type)\n{\n\tif(dfcyc == 0) {\n\t\treturn;\t\t// Ignore some IWM t:00e7 events and others\n\t}\n\tg_log_data_ptr->dfcyc = dfcyc;\n\tg_log_data_ptr->stat = 0;\n\tg_log_data_ptr->addr = info1;\n\tg_log_data_ptr->val = info2;\n\tg_log_data_ptr->size = type;\t\t// type must be > 4\n\tg_log_data_ptr++;\n\tif(g_log_data_ptr >= g_log_data_end_ptr) {\n\t\tg_log_data_ptr = g_log_data_start_ptr;\n\t}\n}\n\nvoid\ndebugger_update_list_kpc()\n{\n\tg_dbg_new_halt = 1;\n}\n\nvoid\ndebugger_key_event(Kimage *kimage_ptr, int a2code, int is_up)\n{\n\tword32\tc025_val, special;\n\tint\tkey, pos, changed;\n\n\tpos = 1;\n\n\tc025_val = kimage_ptr->c025_val;\n\n\tif(c025_val & 1) {\t\t// Shift is down\n\t\tpos = 2;\n\t} else if(c025_val & 4) {\t// Capslock is down\n\t\tkey = g_a2_key_to_ascii[a2code][1];\n\t\tif((key >= 'a') && (key <= 'z')) {\n\t\t\tpos = 2;\t\t// CAPS LOCK on\n\t\t}\n\t}\n\tif(c025_val & 2) {\t\t// Ctrl is down\n\t\tpos = 3;\n\t}\n\tkey = g_a2_key_to_ascii[a2code][pos];\n\tif(key < 0) {\n\t\treturn;\n\t}\n\tspecial = (key >> 8) & 0xff;\t\t// c025 changes\n\tif(is_up) {\n\t\tc025_val = c025_val & (~special);\n\t} else {\n\t\tc025_val = c025_val | special;\n\t}\n\tkimage_ptr->c025_val = c025_val;\n\tif(is_up) {\n\t\treturn;\t\t// Nothing else to do\n\t}\n\tif(key >= 0x80) {\n\t\t// printf(\"key: %04x\\n\", key);\n\t\tif(key == 0x8007) {\t\t\t// F7 - close debugger\n\t\t\tvideo_set_active(kimage_ptr, !kimage_ptr->active);\n\t\t\tprintf(\"Toggled debugger window to:%d\\n\",\n\t\t\t\t\t\tkimage_ptr->active);\n\t\t}\n\t\tif((key & 0xff) == 0x74) {\t\t// Page up keycode\n\t\t\tdebugger_page_updown(1);\n\t\t}\n\t\tif((key & 0xff) == 0x79) {\t\t// Page down keycode\n\t\t\tdebugger_page_updown(-1);\n\t\t}\n\t\treturn;\n\t}\n\tpos = g_cmd_buffer_len;\n\tchanged = 0;\n\tif((key >= 0x20) && (key < 0x7f)) {\n\t\t// printable character, add it\n\t\tif(pos < MAX_CMD_BUFFER) {\n\t\t\t// printf(\"cmd[%d]=%c\\n\", pos, key);\n\t\t\tg_cmd_buffer[pos++] = key;\n\t\t\tchanged = 1;\n\t\t}\n\t} else if((key == 0x08) || (key == 0x7f)) {\n\t\t// Left arrow or backspace\n\t\tif(pos > 2) {\n\t\t\tpos--;\n\t\t\tchanged = 1;\n\t\t}\n\t} else if((key == 0x0d) || (key == 0x0a)) {\n\t\t//dbg_printf(\"Did return, pos:%d, str:%s\\n\", pos, g_cmd_buffer);\n\t\tdo_debug_cmd(&g_cmd_buffer[2]);\n\t\tpos = 2;\n\t\tchanged = 1;\n\t} else {\n\t\t// printf(\"ctrl key:%04x\\n\", key);\n\t}\n\tg_cmd_buffer[pos] = 0;\n\tg_cmd_buffer_len = pos;\n\tg_debug_lines_view = -1;\n\tg_debugwin_changed |= changed;\n\t// printf(\"g_cmd_buffer: %s\\n\", g_cmd_buffer);\n}\n\nvoid\ndebugger_page_updown(int isup)\n{\n\tint\tview, max;\n\n\tview = g_debug_lines_view;\n\tif(view < 0) {\n\t\tview = 0;\n\t}\n\tview = view + (isup*g_debug_lines_viewable_lines);\n\tif(view < 0) {\n\t\tview = -1;\n\t}\n\tmax = g_debug_lines_pos;\n\tif(g_debug_lines_alloc >= g_debug_lines_max) {\n\t\tmax = g_debug_lines_alloc - 4;\n\t}\n\tview = MY_MIN(view, max - g_debug_lines_viewable_lines);\n\n\t// printf(\"new view:%d, was:%d\\n\", view, g_debug_lines_view);\n\tif(view != g_debug_lines_view) {\n\t\tg_debug_lines_view = view;\n\t\tg_debugwin_changed++;\n\t}\n}\n\nvoid\ndebugger_redraw_screen(Kimage *kimage_ptr)\n{\n\tint\tline, vid_line, back, border_top, save_pos, num, lines_done;\n\tint\tsave_view, save_to_stdout;\n\tint\ti;\n\n\tif((g_debugwin_changed == 0) || (kimage_ptr->active == 0)) {\n\t\treturn;\t\t\t\t// Nothing to do\n\t}\n\n\tsave_pos = g_debug_lines_pos;\n\tsave_view = g_debug_lines_view;\n\t// printf(\"DEBUGGER drawing SCREEN!\\n\");\n\tg_cmd_buffer[0] = '>';\n\tg_cmd_buffer[1] = ' ';\n\tg_cmd_buffer[g_cmd_buffer_len] = 0xa0;\t\t// Cursor: inverse space\n\tg_cmd_buffer[g_cmd_buffer_len+1] = 0;\n\tsave_to_stdout = g_debug_to_stdout;\n\tg_debug_to_stdout = 0;\n\tdbg_printf(\"%s\\n\", &g_cmd_buffer[0]);\n\tg_cmd_buffer[g_cmd_buffer_len] = 0;\n\tdbg_printf(\"g_halt_sim:%02x\\n\", g_halt_sim);\n\tborder_top = 8;\n\tg_debug_to_stdout = save_to_stdout;\n\n\tvid_line = (((kimage_ptr->a2_height - 2*border_top) / 16) * 8) - 1;\n\tnum = g_debug_lines_pos - save_pos;\n\tif(num < 0) {\n\t\tnum = num + g_debug_lines_alloc;\n\t}\n\tif(num > 4) {\n\t\t// printf(\"num is > 4!\\n\");\n\t\tnum = 4;\n\t}\n\tfor(i = 0; i < num; i++) {\n\t\tline = debug_get_view_line(i);\n\t\tdebug_draw_debug_line(kimage_ptr, line, vid_line);\n\t\tvid_line -= 8;\n\t}\n\tg_debug_lines_pos = save_pos;\n\tg_debug_lines_view = save_view;\n\tback = save_view;\n\tif(back < 0) {\t\t\t// -1 means always show most recent\n\t\tback = 0;\n\t}\n\tlines_done = 0;\n\twhile(vid_line >= border_top) {\n\t\tline = debug_get_view_line(back);\n\t\tdebug_draw_debug_line(kimage_ptr, line, vid_line);\n\t\tback++;\n\t\tvid_line -= 8;\n\t\tlines_done++;\n#if 0\n\t\tprintf(\" did a line, line is now: %d after str:%s\\n\", line,\n\t\t\t\t\t\t\t\t\tstr);\n#endif\n\t}\n\tg_debug_lines_viewable_lines = lines_done;\n\tg_debugwin_changed = 0;\n\tkimage_ptr->x_refresh_needed = 1;\n\t// printf(\"x_refresh_needed = 1, viewable_lines:%d\\n\", lines_done);\n}\n\nvoid\ndebug_draw_debug_line(Kimage *kimage_ptr, int line, int vid_line)\n{\n\tword32\tline_bytes;\n\tint\ti;\n\n\t// printf(\"draw debug line:%d at vid_line:%d\\n\", line, vid_line);\n\tfor(i = 7; i >= 0; i--) {\n\t\tline_bytes = (vid_line << 16) | (40 << 8) | 0;\n\t\tredraw_changed_string(&(g_debug_lines_ptr[line].str_buf[0]),\n\t\t\tline_bytes, -1L, kimage_ptr->wptr + 8, 0, 0x00ffffff,\n\t\t\tkimage_ptr->a2_width_full, 1);\n\t\tvid_line--;\n\t}\n}\n\nDbg_longcmd g_debug_bp_clear[] = {\n\t{ \"all\",\tdebug_bp_clear_all,\t0,\n\t\t\t\t\t\"clear all breakpoints\" },\n\t{ 0, 0, 0, 0 }\n};\nDbg_longcmd g_debug_bp[] = {\n\t{ \"set\",\tdebug_bp_set,\t0,\n\t\t\t\t\t\"Set breakpoint: ADDR or ADDR0-ADDR1\" },\n\t{ \"clear\",\tdebug_bp_clear,\t&g_debug_bp_clear[0],\n\t\t\t\t\"Clear breakpoint: ADDR OR ADDR0-ADDR1\"},\n\t{ 0, 0, 0, 0 }\n};\n\nDbg_longcmd g_debug_logpc[] = {\n\t{ \"on\",\t\tdebug_logpc_on,\t0, \"Turn on logging of pc and data\" },\n\t{ \"off\",\tdebug_logpc_off,0, \"Turn off logging of pc and data\" },\n\t{ \"save\",\tdebug_logpc_save,0, \"logpc save FILE: save to file\" },\n\t{ 0, 0, 0, 0 }\n};\n\nDbg_longcmd g_debug_iwm[] = {\n\t{ \"check\",\tdebug_iwm_check, 0, \"Denibblize current track\" },\n\t{ 0, 0, 0, 0 }\n};\n\n// Main table of commands\nDbg_longcmd g_debug_longcmds[] = {\n\t{ \"help\",\tdebug_help,\t0,\t\"Help\" },\n\t{ \"bp\",\t\tdebug_bp,\t&g_debug_bp[0],\n\t\t\t\t\t\"bp ADDR: sets breakpoint on addr\" },\n\t{ \"logpc\",\tdebug_logpc,\t&g_debug_logpc[0], \"Log PC\" },\n\t{ \"iwm\",\tdebug_iwm,\t&g_debug_iwm[0], \"IWM\" },\n\t{ \"soundfile\",\tdebug_soundfile, 0, \"Save sound to a WAV file\" },\n\t{ 0, 0, 0, 0 }\n};\n\nvoid\ndebugger_help()\n{\n\tdbg_printf(\"KEGS Debugger help (courtesy Fredric Devernay\\n\");\n\tdbg_printf(\"General command syntax: [bank]/[address][command]\\n\");\n\tdbg_printf(\"e.g. 'e1/0010B' to set a breakpoint at the interrupt jump \"\n\t\t\t\t\t\t\t\t\"pt\\n\");\n\tdbg_printf(\"Enter all addresses using lower-case\\n\");\n\tdbg_printf(\"As with the IIgs monitor, you can omit the bank number \"\n\t\t\t\t\t\t\t\t\"after\\n\");\n\tdbg_printf(\"having set it: 'e1/0010B' followed by '14B' will set\\n\");\n\tdbg_printf(\"breakpoints at e1/0010 and e1/0014\\n\");\n\tdbg_printf(\"\\n\");\n\tdbg_printf(\"g                       Go\\n\");\n\tdbg_printf(\"[bank]/[addr]g          Go from [bank]/[address]\\n\");\n\tdbg_printf(\"s                       Step one instruction\\n\");\n\tdbg_printf(\"[bank]/[addr]s          Step one instr at [bank]/[addr]\\n\");\n\tdbg_printf(\"[bank]/[addr]B          Set breakpoint at [bank]/[addr]\\n\");\n\tdbg_printf(\"B                       Show all breakpoints\\n\");\n\tdbg_printf(\"[bank]/[addr]D          Delete breakpoint at [bank]/\"\n\t\t\t\t\t\t\t\t\"[addr]\\n\");\n\tdbg_printf(\"[bank]/[addr1].[addr2]  View memory\\n\");\n\tdbg_printf(\"[bank]/[addr]L          Disassemble memory\\n\");\n\n\tdbg_printf(\"Z                       Dump SCC state\\n\");\n\tdbg_printf(\"I                       Dump IWM state\\n\");\n\tdbg_printf(\"[drive].[track]I        Dump IWM state\\n\");\n\tdbg_printf(\"E                       Dump Ensoniq state\\n\");\n\tdbg_printf(\"[osc]E                  Dump oscillator [osc] state\\n\");\n\tdbg_printf(\"R                       Dump dtime array and events\\n\");\n\tdbg_printf(\"T                       Show toolbox log\\n\");\n\tdbg_printf(\"[bank]/[addr]T          Dump tools using ptr [bank]/\"\n\t\t\t\t\t\t\t\t\"[addr]\\n\");\n\tdbg_printf(\"                            as 'tool_set_info'\\n\");\n\tdbg_printf(\"[mode]V                 XOR verbose with 1=DISK, 2=IRQ,\\n\");\n\tdbg_printf(\"                         4=CLK,8=SHADOW,10=IWM,20=DOC,\\n\");\n\tdbg_printf(\"                         40=ABD,80=SCC, 100=TEST, 200=\"\n\t\t\t\t\t\t\t\t\"VIDEO\\n\");\n\tdbg_printf(\"[mode]H                 XOR halt_on with 1=SCAN_INT,\\n\");\n\tdbg_printf(\"                         2=IRQ, 4=SHADOW_REG, 8=\"\n\t\t\t\t\t\t\t\"C70D_WRITES\\n\");\n\tdbg_printf(\"r                       Reset\\n\");\n\tdbg_printf(\"[0/1]=m                 Changes m bit for l listings\\n\");\n\tdbg_printf(\"[0/1]=x                 Changes x bit for l listings\\n\");\n\tdbg_printf(\"S                       show_bankptr_bank0 & smartport \"\n\t\t\t\t\t\t\t\t\"errs\\n\");\n\tdbg_printf(\"P                       show_pmhz\\n\");\n\tdbg_printf(\"A                       show_a2_line_stuff show_adb_log\\n\");\n\tdbg_printf(\"Ctrl-e                  Dump registers\\n\");\n\tdbg_printf(\"[bank]/[addr1].[addr2]us[file]  Save mem area to [file]\\n\");\n\tdbg_printf(\"[bank]/[addr1].[addr2]ul[file]  Load mem area from \"\n\t\t\t\t\t\t\t\t\"[file]\\n\");\n\tdbg_printf(\"v                       Show video information\\n\");\n\tdbg_printf(\"q                       Exit Debugger (and KEGS)\\n\");\n}\n\nvoid\ndbg_help_show_strs(int help_depth, const char *str, const char *help_str)\n{\n\tconst char *blank_str, *pre_str, *post_str;\n\tint\tcolumn, len, blank_len, pre_len, post_len;\n\n\t// Indent by 3*help_depth chars, then output str, then hit\n\t//  column 14, then output help_str.  This can be done in just 2-3\n\t//  lines, but I made it longer and clearer to avoid any \"overflow\"\n\t//  cases\n\tif(help_str == 0) {\n\t\treturn;\n\t}\n\tblank_str = \"        \" \"        \" \"        \";\n\tblank_len = (int)strlen(blank_str);\t\t// should be >=17\n\tcolumn = 17;\n\tlen = (int)strlen(str);\n\tif(help_depth < 0) {\n\t\thelp_depth = 0;\n\t}\n\tpre_str = blank_str;\n\tpre_len = 3 * help_depth;\n\tif(pre_len < blank_len) {\n\t\tpre_str = blank_str + blank_len - pre_len;\n\t}\n\tpost_str = \"\";\n\tpost_len = column - pre_len - len;\n\tif((post_len >= 1) && (post_len < blank_len)) {\n\t\tpost_str = blank_str + blank_len - post_len;\n\t}\n\tdbg_printf(\"%s%s%s: %s\\n\", pre_str, str, post_str, help_str);\n}\n\nconst char *\ndebug_find_cmd_in_table(const char *line_ptr, Dbg_longcmd *longptr,\n\t\t\t\t\t\t\tint help_depth)\n{\n\tDbg_fn\t*fnptr;\n\tDbg_longcmd *subptr;\n\tconst char *str, *newstr;\n\tint\tlen, c;\n\tint\ti;\n\n\t// See if the command is from the longcmd list\n\twhile(*line_ptr == ' ') {\n\t\tline_ptr++;\t\t// eat spaces\n\t}\n\t// Output \"   str     :\" where : is at column 14 always\n\t// printf(\"dfcit: %s, help_depth:%d\\n\", line_ptr, help_depth);\n\tfor(i = 0; i < 1000; i++) {\n\t\t// Provide a limit to avoid hang if table not terminated right\n\t\tstr = longptr[i].str;\n\t\tfnptr = longptr[i].fnptr;\n\t\tif(!str) {\t\t\t// End of table\n\t\t\tbreak;\t\t\t// No match found\n\t\t}\n\t\tif(help_depth < 0) {\n\t\t\t// Print the help string for all entries in this table\n\t\t\tdbg_help_show_strs(-1 - help_depth, str,\n\t\t\t\t\t\t\tlongptr[i].help_str);\n\t\t\tcontinue;\n\t\t}\n\t\tlen = (int)strlen(str);\n\t\tif(strncmp(line_ptr, str, len) != 0) {\n\t\t\tcontinue;\t\t// Not a match\n\t\t}\n\t\t// Ensure next char is either a space, or 0\n\t\t// Let's us avoid commands which are prefixes, or\n\t\t//  which are old Apple II monitor hex+commands\n\t\tc = line_ptr[len];\n\t\tif((c != 0) && (c != ' ')) {\n\t\t\tcontinue;\t\t// Not valid\n\t\t}\n\t\tif(help_depth) {\n\t\t\tdbg_help_show_strs(help_depth, str,\n\t\t\t\t\t\t\tlongptr[i].help_str);\n\t\t}\n\t\tsubptr = longptr[i].subptr;\n\t\t// Try a subcmd first\n\t\tnewstr = line_ptr + len;\n\t\tif(subptr != 0) {\n\t\t\tif(help_depth) {\n\t\t\t\thelp_depth++;\n\t\t\t}\n\t\t\tnewstr = debug_find_cmd_in_table(newstr, subptr,\n\t\t\t\t\t\t\t\thelp_depth);\n\t\t\t// If a subcmd was found, newstr is now 0\n\t\t}\n\t\tif((newstr == 0) || help_depth) {\n\t\t\treturn 0;\n\t\t}\n\t\tif((newstr != 0) && (fnptr != 0)) {\n\t\t\t(*fnptr)(line_ptr + len);\n\t\t\treturn 0;\t\t// Success\n\t\t}\n\t}\n\tif(help_depth >= 1) {\n\t\t// No subcommands found, print out all entries in this table\n\t\tdebug_find_cmd_in_table(line_ptr, longptr, -1 - help_depth);\n\t\treturn 0;\n\t}\n\treturn line_ptr;\n}\n\nvoid\ndo_debug_cmd(const char *in_str)\n{\n\tconst char *line_ptr;\n\tconst char *newstr;\n\tint\tslot_drive, track, ret_val, mode, old_mode, got_num;\n\tint\tsave_to_stdout;\n\n\tmode = 0;\n\told_mode = 0;\n\n\tsave_to_stdout = g_debug_to_stdout;\n\tg_debug_to_stdout = 1;\n\tdbg_printf(\"*%s\\n\", in_str);\n\tline_ptr = in_str;\n\n\t// See if the command is from the longcmd list\n\tnewstr = debug_find_cmd_in_table(in_str, &(g_debug_longcmds[0]), 0);\n\tif(newstr == 0) {\n\t\tg_debug_to_stdout = save_to_stdout;\n\t\treturn;\t\t\t// Command found get out\n\t}\n\n\t// If we get here, parse an Apple II monitor like command:\n\t//  {address}{cmd} repeat.\n\twhile(1) {\n\t\tret_val = 0;\n\t\tg_a2 = 0;\n\t\tgot_num = 0;\n\t\twhile(1) {\n\t\t\tif((mode == 0) && (got_num != 0)) {\n\t\t\t\tg_a3 = g_a2;\n\t\t\t\tg_a3bank = g_a2bank;\n\t\t\t\tg_a1 = g_a2;\n\t\t\t\tg_a1bank = g_a2bank;\n\t\t\t}\n\t\t\tret_val = *line_ptr++ & 0x7f;\n\t\t\tif((ret_val >= '0') && (ret_val <= '9')) {\n\t\t\t\tg_a2 = (g_a2 << 4) + ret_val - '0';\n\t\t\t\tgot_num = 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif((ret_val >= 'a') && (ret_val <= 'f')) {\n\t\t\t\tg_a2 = (g_a2 << 4) + ret_val - 'a' + 10;\n\t\t\t\tgot_num = 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(ret_val == '/') {\n\t\t\t\tg_a2bank = g_a2;\n\t\t\t\tg_a2 = 0;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\told_mode = mode;\n\t\tmode = 0;\n\t\tswitch(ret_val) {\n\t\tcase 'h':\n\t\t\tdebugger_help();\n\t\t\tbreak;\n\t\tcase 'R':\n\t\t\tshow_dtime_array();\n\t\t\tshow_all_events();\n\t\t\tbreak;\n\t\tcase 'I':\n\t\t\tslot_drive = -1;\n\t\t\ttrack = -1;\n\t\t\tif(got_num) {\n\t\t\t\tif(old_mode == '.') {\n\t\t\t\t\tslot_drive = g_a1;\n\t\t\t\t}\n\t\t\t\ttrack = g_a2;\n\t\t\t}\n\t\t\tiwm_show_track(slot_drive, track, 0);\n\t\t\tiwm_show_stats(slot_drive);\n\t\t\tbreak;\n\t\tcase 'E':\n\t\t\tdoc_show_ensoniq_state();\n\t\t\tbreak;\n\t\tcase 'T':\n\t\t\tif(got_num) {\n\t\t\t\tshow_toolset_tables(g_a2bank, g_a2);\n\t\t\t} else {\n\t\t\t\tshow_toolbox_log();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'v':\n\t\t\tif(got_num) {\n\t\t\t\tdis_do_compare();\n\t\t\t} else {\n\t\t\t\tvideo_show_debug_info();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'V':\n\t\t\tdbg_printf(\"g_irq_pending: %05x\\n\", g_irq_pending);\n\t\t\tdbg_printf(\"Setting Verbose ^= %04x\\n\", g_a1);\n\t\t\tVerbose ^= g_a1;\n\t\t\tdbg_printf(\"Verbose is now: %04x\\n\", Verbose);\n\t\t\tbreak;\n\t\tcase 'H':\n\t\t\tdbg_printf(\"Setting Halt_on ^= %04x\\n\", g_a1);\n\t\t\tHalt_on ^= g_a1;\n\t\t\tdbg_printf(\"Halt_on is now: %04x\\n\", Halt_on);\n\t\t\tbreak;\n\t\tcase 'r':\n\t\t\tdo_reset();\n\t\t\tg_list_kpc = engine.kpc;\n\t\t\tbreak;\n\t\tcase 'm':\n\t\t\tif(old_mode == '=') {\n\t\t\t\tif(!g_a1) {\n\t\t\t\t\tengine.psr &= ~0x20;\n\t\t\t\t} else {\n\t\t\t\t\tengine.psr |= 0x20;\n\t\t\t\t}\n\t\t\t\tif(engine.psr & 0x100) {\n\t\t\t\t\tengine.psr |= 0x30;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdis_do_memmove();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t\tdis_do_pattern_search();\n\t\t\tbreak;\n\t\tcase 'x':\n\t\t\tif(old_mode == '=') {\n\t\t\t\tif(!g_a1) {\n\t\t\t\t\tengine.psr &= ~0x10;\n\t\t\t\t} else {\n\t\t\t\t\tengine.psr |= 0x10;\n\t\t\t\t}\n\t\t\t\tif(engine.psr & 0x100) {\n\t\t\t\t\tengine.psr |= 0x30;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'z':\n\t\t\tif(old_mode == '=') {\n\t\t\t\tstop_run_at = g_a1;\n\t\t\t\tdbg_printf(\"Calling add_event for t:%08x\\n\",\n\t\t\t\t\t\t\t\t\tg_a1);\n\t\t\t\tadd_event_stop(((dword64)g_a1) << 16);\n\t\t\t\tdbg_printf(\"set stop_run_at = %x\\n\", g_a1);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'l': case 'L':\n\t\t\tif(got_num) {\n\t\t\t\tg_list_kpc = (g_a2bank << 16) + (g_a2 & 0xffff);\n\t\t\t}\n\t\t\tdo_debug_list();\n\t\t\tbreak;\n\t\tcase 'Z':\n\t\t\tshow_scc_state();\n\t\t\tbreak;\n\t\tcase 'S':\n\t\t\tshow_bankptrs_bank0rdwr();\n\t\t\tsmartport_error();\n\t\t\tbreak;\n\t\tcase 'M':\n\t\t\tshow_pmhz();\n\t\t\tmockingboard_show(got_num, g_a1);\n\t\t\tbreak;\n\t\tcase 'A':\n\t\t\tshow_a2_line_stuff();\n\t\t\tshow_adb_log();\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\tg_stepping = 1;\n\t\t\tif(got_num) {\n\t\t\t\tengine.kpc = (g_a2bank << 16) + (g_a2 & 0xffff);\n\t\t\t}\n\t\t\tmode = 's';\n\t\t\tg_list_kpc = engine.kpc;\n\t\t\tbreak;\n\t\tcase 'B':\n\t\t\tif(got_num) {\n\t\t\t\tdbg_printf(\"got_num:%d, a2bank:%x, g_a2:%x\\n\",\n\t\t\t\t\t\tgot_num, g_a2bank, g_a2);\n\t\t\t\tset_bp((g_a2bank << 16) + g_a2,\n\t\t\t\t\t\t(g_a2bank << 16) + g_a2, 4);\n\t\t\t} else {\n\t\t\t\tshow_bp();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'D':\n\t\t\tif(got_num) {\n\t\t\t\tdbg_printf(\"got_num: %d, a2bank: %x, a2: %x\\n\",\n\t\t\t\t\t\tgot_num, g_a2bank, g_a2);\n\t\t\t\tdelete_bp((g_a2bank << 16) + g_a2,\n\t\t\t\t\t\t(g_a2bank << 16) + g_a2);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'g':\n\t\tcase 'G':\n\t\t\tdbg_printf(\"Going..\\n\");\n\t\t\tg_stepping = 0;\n\t\t\tif(got_num) {\n\t\t\t\tengine.kpc = (g_a2bank << 16) + (g_a2 & 0xffff);\n\t\t\t}\n\t\t\tdo_go();\n\t\t\tg_list_kpc = engine.kpc;\n\t\t\tbreak;\n\t\tcase 'u':\n\t\t\tdbg_printf(\"Unix commands\\n\");\n\t\t\tline_ptr = do_debug_unix(line_ptr, old_mode);\n\t\t\tbreak;\n\t\tcase ':': case '.':\n\t\tcase '+': case '-':\n\t\tcase '=': case ',':\n\t\t\tmode = ret_val;\n\t\t\tdbg_printf(\"Setting mode = %x\\n\", mode);\n\t\t\tbreak;\n\t\tcase ' ': case '\\t':\n\t\t\tif(!got_num) {\n\t\t\t\tmode = old_mode;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmode = do_blank(mode, old_mode);\n\t\t\tbreak;\n\t\tcase '<':\n\t\t\tg_a4 = g_a2;\n\t\t\tg_a4bank = g_a2bank;\n\t\t\tbreak;\n\t\tcase 0x05: /* ctrl-e */\n\t\tcase 'Q':\n\t\tcase 'q':\n\t\t\tshow_regs();\n\t\t\tbreak;\n\t\tcase 0:\t\t\t// The final null char\n\t\t\tif(old_mode == 's') {\n\t\t\t\tmode = do_blank(mode, old_mode);\n\t\t\t\tg_debug_to_stdout = save_to_stdout;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif(line_ptr == &in_str[1]) {\n\t\t\t\tg_a2 = g_a1 | (g_hex_line_len - 1);\n\t\t\t\tshow_hex_mem(g_a1bank, g_a1, g_a2, -1);\n\t\t\t\tg_a1 = g_a2 + 1;\n\t\t\t} else {\n\t\t\t\tif((got_num == 1) || (mode == 's')) {\n\t\t\t\t\tmode = do_blank(mode, old_mode);\n\t\t\t\t}\n\t\t\t}\n\t\t\tg_debug_to_stdout = save_to_stdout;\n\t\t\treturn;\t\t\t// Get out, all done\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tdbg_printf(\"\\nUnrecognized command: %s\\n\", in_str);\n\t\t\tg_debug_to_stdout = save_to_stdout;\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nword32\ndis_get_memory_ptr(word32 addr)\n{\n\tword32\ttmp1, tmp2, tmp3;\n\n\ttmp1 = get_memory_c(addr);\n\ttmp2 = get_memory_c(addr + 1);\n\ttmp3 = get_memory_c(addr + 2);\n\n\treturn (tmp3 << 16) + (tmp2 << 8) + tmp1;\n}\n\nvoid\nshow_one_toolset(FILE *toolfile, int toolnum, word32 addr)\n{\n\tword32\trout_addr;\n\tint\tnum_routs;\n\tint\ti;\n\n\tnum_routs = dis_get_memory_ptr(addr);\n\tfprintf(toolfile, \"Tool 0x%02x, table: 0x%06x, num_routs:%03x\\n\",\n\t\ttoolnum, addr, num_routs);\n\tif((addr < 0x10000) || (num_routs > 0x100)) {\n\t\tfprintf(toolfile, \"addr in page 0, or num_routs too large\\n\");\n\t\treturn;\n\t}\n\n\tfor(i = 1; i < num_routs; i++) {\n\t\trout_addr = dis_get_memory_ptr(addr + 4*i);\n\t\tfprintf(toolfile, \"%06x = %02x%02x\\n\", rout_addr, i, toolnum);\n\t}\n}\n\nvoid\nshow_toolset_tables(word32 a2bank, word32 addr)\n{\n\tFILE\t*toolfile;\n\tword32\ttool_addr;\n\tint\tnum_tools;\n\tint\ti;\n\n\taddr = (a2bank << 16) + (addr & 0xffff);\n\n\ttoolfile = fopen(\"tool_set_info\", \"w\");\n\tif(toolfile == 0) {\n\t\tfprintf(stderr, \"fopen of tool_set_info failed: %d\\n\", errno);\n\t\texit(2);\n\t}\n\n\tnum_tools = dis_get_memory_ptr(addr);\n\tfprintf(toolfile, \"There are 0x%02x tools using ptr at %06x\\n\",\n\t\t\tnum_tools, addr);\n\n\tif(num_tools > 40) {\n\t\tfprintf(toolfile, \"Too many tools, aborting\\n\");\n\t\tnum_tools = 0;\n\t}\n\tfor(i = 1; i < num_tools; i++) {\n\t\ttool_addr = dis_get_memory_ptr(addr + 4*i);\n\t\tshow_one_toolset(toolfile, i, tool_addr);\n\t}\n\n\tfclose(toolfile);\n}\n\nword32\ndebug_getnum(const char **str_ptr)\n{\n\tconst char *str;\n\tword32\tval;\n\tint\tc, got_num;\n\n\tstr = *str_ptr;\n\twhile(*str == ' ') {\n\t\tstr++;\n\t}\n\tgot_num = 0;\n\tval = 0;\n\twhile(1) {\n\t\tc = tolower(*str);\n\t\t//printf(\"got c:%02x %c val was %08x got_num:%d\\n\", c, c, val,\n\t\t//\t\t\t\t\t\tgot_num);\n\t\tif((c >= '0') && (c <= '9')) {\n\t\t\tval = (val << 4) + (c - '0');\n\t\t\tgot_num = 1;\n\t\t} else if((c >= 'a') && (c <= 'f')) {\n\t\t\tval = (val << 4) + 10 + (c - 'a');\n\t\t\tgot_num = 1;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t\tstr++;\n\t}\n\t*str_ptr = str;\n\tif(got_num) {\n\t\treturn val;\n\t}\n\treturn (word32)-1L;\n}\n\nchar *\ndebug_get_filename(const char **str_ptr)\n{\n\tconst char *str, *start_str;\n\tchar\t*new_str;\n\tint\tc, len;\n\n\t// Go to first whitespace (or end of str), then kegs_malloc_str()\n\t//  the string and copy to it\n\tstr = *str_ptr;\n\tstart_str = 0;\n\t//printf(\"get_filename, str now :%s:\\n\", str);\n\twhile(1) {\n\t\tc = *str++;\n\t\tif(c == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tif((c == ' ') || (c == '\\t') || (c == '\\n')) {\n\t\t\t//printf(\"c:%02x at str :%s: , start_str:%p\\n\", c, str,\n\t\t\t//\t\t\t\t\tstart_str);\n\t\t\tif(start_str) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\t// Else it's a valid char, set start_str if needed\n\t\tif(!start_str) {\n\t\t\tstart_str = str - 1;\n\t\t\t//printf(\"Got c:%02x, start_str :%s:\\n\", c, start_str);\n\t\t}\n\t}\n\tnew_str = 0;\n\tif(start_str) {\n\t\tlen = (int)(str - start_str);\n\t\tif(len > 1) {\n\t\t\tnew_str = malloc(len);\n\t\t\tmemcpy(new_str, start_str, len);\n\t\t\tnew_str[len - 1] = 0;\n\t\t}\n\t}\n\t*str_ptr = str;\n\treturn new_str;\n}\n\nvoid\ndebug_help(const char *str)\n{\n\tdbg_printf(\"Help:\\n\");\n\t(void)debug_find_cmd_in_table(str, &(g_debug_longcmds[0]), 1);\n}\n\nvoid\ndebug_bp(const char *str)\n{\n\t// bp without a following set/clear command.  Set a breakpoint if\n\t//  an address range follows, otherwise just print current breakpoints\n\tdebug_bp_setclr(str, 0);\n}\n\nvoid\ndebug_bp_set(const char *str)\n{\n\tdebug_bp_setclr(str, 1);\n}\n\nvoid\ndebug_bp_clear(const char *str)\n{\n\tdebug_bp_setclr(str, 2);\n}\n\nvoid\ndebug_bp_clear_all(const char *str)\n{\n\tif(str) {\n\t\t// Use str to avoid warning\n\t}\n\tif(g_num_breakpoints) {\n\t\tg_num_breakpoints = 0;\n\t\tsetup_pageinfo();\n\t\tdbg_printf(\"Deleted all breakpoints\\n\");\n\t}\n}\n\nvoid\ndebug_bp_setclr(const char *str, int is_set_clear)\n{\n\tword32\taddr, end_addr, acc_type;\n\n\tprintf(\"In debug_bp: %s\\n\", str);\n\n\taddr = debug_getnum(&str);\n\t// printf(\"getnum ret:%08x\\n\", addr);\n\tif(addr == (word32)-1L) {\t\t// No argument\n\t\tshow_bp();\n\t\treturn;\n\t}\n\tend_addr = addr;\n\tif(*str == '-') {\t\t// Range\n\t\tstr++;\n\t\tend_addr = debug_getnum(&str);\n\t\t// printf(\"end_addr is %08x\\n\", end_addr);\n\t\tif(end_addr == (word32)-1L) {\n\t\t\tend_addr = addr;\n\t\t}\n\t}\n\tacc_type = 4;\n\tacc_type = debug_getnum(&str);\n\tif(acc_type == (word32)-1L) {\n\t\tacc_type = 4;\t\t\t// Code breakpoint\n\t}\n\tif(is_set_clear == 2) {\t\t\t// clear\n\t\tdelete_bp(addr, end_addr);\n\t} else {\t\t\t\t// set, or nothing\n\t\tset_bp(addr, end_addr, acc_type);\n\t}\n}\n\nvoid\ndebug_soundfile(const char *cmd_str)\n{\n\tchar\t*str;\n\n\t// See if there's an argument\n\tstr = debug_get_filename(&cmd_str);\t// str=0 if no argument\n\tsound_file_start(str);\t\t\t// str==0 means close file\n}\n\nvoid\ndebug_logpc(const char *str)\n{\n\tif(str) {\n\t\t// Dummy use of argument\n\t}\n\tdbg_printf(\"logpc enable:%d, cur offset:%08lx\\n\", g_log_pc_enable,\n\t\t\t(long)(g_log_pc_ptr - g_log_pc_start_ptr));\n}\n\nvoid\ndebug_logpc_on(const char *str)\n{\n\tif(str) {\n\t\t// Dummy use of argument\n\t}\n\tg_log_pc_enable = 1;\n\tg_dcycles_end = 0;\n\tdbg_printf(\"Enabled logging of PC and data accesses\\n\");\n}\n\nvoid\ndebug_logpc_off(const char *str)\n{\n\tif(str) {\n\t\t// Dummy use of argument\n\t}\n\tg_log_pc_enable = 0;\n\tg_dcycles_end = 0;\n\tdbg_printf(\"Disabled logging of PC and data accesses\\n\");\n}\n\nvoid\ndebug_logpc_out_data(FILE *pcfile, Data_log *log_data_ptr, dword64 start_dcyc)\n{\n\tchar\t*str, *shadow_str;\n\tdword64\tlstat, offset64, offset64slow, addr64;\n\tword32\twstat, addr, size, val;\n\n\taddr = log_data_ptr->addr;\n\tlstat = (dword64)(log_data_ptr->stat);\n\twstat = lstat & 0xff;\n\taddr64 = lstat - wstat + (addr & 0xff);\n\toffset64 = addr64 - (dword64)&(g_memory_ptr[0]);\n\tstr = \"IO\";\n\tshadow_str = \"\";\n\tif((wstat & BANK_SHADOW) || (wstat & BANK_SHADOW2)) {\n\t\tshadow_str = \"SHADOWED\";\n\t}\n\tsize = log_data_ptr->size;\n\tif(size > 32) {\n\t\tfprintf(pcfile, \"INFO %08x %08x %04x t:%04x %lld.%02lld\\n\",\n\t\t\tlog_data_ptr->addr, log_data_ptr->val, size >> 16,\n\t\t\tsize & 0xffff, (log_data_ptr->dfcyc - start_dcyc)>>16,\n\t\t\t((log_data_ptr->dfcyc & 0xffff) * 100) >> 16);\n\t} else {\n\t\toffset64slow = addr64 - (dword64)&(g_slow_memory_ptr[0]);\n\t\tif(offset64 < g_mem_size_total) {\n\t\t\tstr = \"mem\";\n\t\t} else if(offset64slow < 0x20000) {\n\t\t\tstr = \"slow_mem\";\n\t\t\toffset64 = offset64slow;\n\t\t} else {\n\t\t\tstr = \"IO\";\n\t\t\toffset64 = offset64 & 0xff;\n\t\t}\n\t\tval = log_data_ptr->val;\n\t\tfprintf(pcfile, \"DATA set %06x = \", addr);\n\t\tif(size == 8) {\n\t\t\tfprintf(pcfile, \"%02x (8) \", val & 0xff);\n\t\t} else if(size == 16) {\n\t\t\tfprintf(pcfile, \"%04x (16) \", val & 0xffff);\n\t\t} else {\n\t\t\tfprintf(pcfile, \"%06x (%d) \", val, size);\n\t\t}\n\t\tfprintf(pcfile, \"%lld.%02lld, %s[%06llx] %s\\n\",\n\t\t\t\t(log_data_ptr->dfcyc - start_dcyc) >> 16,\n\t\t\t\t((log_data_ptr->dfcyc & 0xffff) * 100) >> 16,\n\t\t\t\tstr, offset64 & 0xffffffULL, shadow_str);\n\t}\n}\n\nData_log *\ndebug_show_data_info(FILE *pcfile, Data_log *log_data_ptr, dword64 base_dcyc,\n\t\t\tdword64 dfcyc, dword64 start_dcyc, int *data_wrap_ptr,\n\t\t\tint *count_ptr)\n{\n\n\twhile((*data_wrap_ptr < 2) && (log_data_ptr->dfcyc <= dfcyc) &&\n\t\t\t\t\t(log_data_ptr->dfcyc >= start_dcyc)) {\n\t\tif(*count_ptr >= PC_LOG_LEN) {\n\t\t\tbreak;\n\t\t}\n\t\tdebug_logpc_out_data(pcfile, log_data_ptr, base_dcyc);\n\t\tif(log_data_ptr->dfcyc == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tlog_data_ptr++;\n\t\t(*count_ptr)++;\n\t\tif(log_data_ptr >= g_log_data_end_ptr) {\n\t\t\tlog_data_ptr = g_log_data_start_ptr;\n\t\t\t(*data_wrap_ptr)++;\n\t\t}\n\t}\n\treturn log_data_ptr;\n}\n\nvoid\ndebug_logpc_save(const char *cmd_str)\n{\n\tFILE\t*pcfile;\n\tPc_log\t*log_pc_ptr;\n\tData_log *log_data_ptr;\n\tchar\t*str;\n\tdword64\tdfcyc, start_dcyc, base_dcyc, max_dcyc;\n\tword32\tinstr, psr, acc, xreg, yreg, stack, direct, dbank, kpc, num;\n\tint\tdata_wrap, accsize, xsize, abs_time, data_count;\n\tint\ti;\n\n\t// See if there's an argument\n\tnum = debug_getnum(&cmd_str);\n\tabs_time = 1;\n\tif(num != (word32)-1L) {\n\t\tdbg_printf(\"Doing relative time\\n\");\n\t\tabs_time = 0;\n\t}\n\n\tpcfile = fopen(\"logpc_out\", \"w\");\n\tif(pcfile == 0) {\n\t\tfprintf(stderr,\"fopen failed...errno: %d\\n\", errno);\n\t\texit(2);\n\t}\n\n\tlog_pc_ptr = g_log_pc_ptr;\n\tlog_data_ptr = g_log_data_ptr;\n#if 0\n\tprintf(\"debug_logpc_save called, log_pc_ptr:%p, %p,%p log_data_ptr:%p, \"\n\t\t\"%p,%p\\n\", log_pc_ptr, g_log_pc_start_ptr, g_log_pc_end_ptr,\n\t\tlog_data_ptr, g_log_data_start_ptr, g_log_data_end_ptr);\n#endif\n#if 0\n\tfprintf(pcfile, \"current pc_log_ptr: %p, start: %p, end: %p\\n\",\n\t\tlog_pc_ptr, g_log_pc_start_ptr, g_log_pc_end_ptr);\n#endif\n\n\t// See if we haven't filled buffer yet\n\tif(log_pc_ptr->dfcyc == 0) {\n\t\tlog_pc_ptr = g_log_pc_start_ptr;\n\t}\n\tif(log_data_ptr->dfcyc == 0) {\n\t\tlog_data_ptr = g_log_data_start_ptr;\n\t\tdata_wrap = 1;\n\t}\n\n\tstart_dcyc = log_pc_ptr->dfcyc;\n\t// Round to an exact usec\n\tstart_dcyc = (start_dcyc >> 16) << 16;\n\tbase_dcyc = start_dcyc;\n\tif(abs_time) {\n\t\tbase_dcyc = 0;\t\t\t\t// Show absolute time\n\t}\n\tdfcyc = start_dcyc;\n\n\tdata_wrap = 0;\n\tdata_count = 0;\n\t/* find first data entry */\n\twhile((data_wrap < 2) && (log_data_ptr->dfcyc < dfcyc)) {\n\t\tlog_data_ptr++;\n\t\tif(log_data_ptr >= g_log_data_end_ptr) {\n\t\t\tlog_data_ptr = g_log_data_start_ptr;\n\t\t\tdata_wrap++;\n\t\t}\n\t}\n\tfprintf(pcfile, \"start_dcyc: %016llx, first entry:%016llx\\n\",\n\t\t\t\t\t\tstart_dcyc, log_pc_ptr->dfcyc);\n\n\tdfcyc = start_dcyc;\n\tmax_dcyc = dfcyc;\n\tfor(i = 0; i < PC_LOG_LEN; i++) {\n\t\tdfcyc = log_pc_ptr->dfcyc;\n\t\tlog_data_ptr = debug_show_data_info(pcfile, log_data_ptr,\n\t\t\t\tbase_dcyc, dfcyc, start_dcyc,\n\t\t\t\t&data_wrap, &data_count);\n\t\tdbank = (log_pc_ptr->dbank_kpc >> 24) & 0xff;\n\t\tkpc = log_pc_ptr->dbank_kpc & 0xffffff;\n\t\tinstr = log_pc_ptr->instr;\n\t\tpsr = (log_pc_ptr->psr_acc >> 16) & 0xffff;\n\t\tacc = log_pc_ptr->psr_acc & 0xffff;\n\t\txreg = (log_pc_ptr->xreg_yreg >> 16) & 0xffff;\n\t\tyreg = log_pc_ptr->xreg_yreg & 0xffff;\n\t\tstack = (log_pc_ptr->stack_direct >> 16) & 0xffff;\n\t\tdirect = log_pc_ptr->stack_direct & 0xffff;\n\n\t\taccsize = 2;\n\t\txsize = 2;\n\t\tif(psr & 0x20) {\n\t\t\taccsize = 1;\n\t\t}\n\t\tif(psr & 0x10) {\n\t\t\txsize = 1;\n\t\t}\n\n\t\tstr = do_dis(kpc, accsize, xsize, 1, instr, 0);\n\t\tfprintf(pcfile, \"%06x] A:%04x X:%04x Y:%04x P:%03x \"\n\t\t\t\"S:%04x D:%04x B:%02x %lld.%02lld %s\\n\", i,\n\t\t\tacc, xreg, yreg, psr, stack, direct, dbank,\n\t\t\t(dfcyc - base_dcyc) >> 16,\n\t\t\t((dfcyc & 0xffff) * 100) >> 16, str);\n\n\t\tif((dfcyc == 0) && (i != 0)) {\n\t\t\tbreak;\n\t\t}\n\t\tmax_dcyc = dfcyc;\n\t\tlog_pc_ptr++;\n\t\tif(log_pc_ptr >= g_log_pc_end_ptr) {\n\t\t\tlog_pc_ptr = g_log_pc_start_ptr;\n\t\t}\n\t}\n\n\t// Print any more DATA or INFO after last PC entry\n\tlog_data_ptr = debug_show_data_info(pcfile, log_data_ptr,\n\t\t\tbase_dcyc, max_dcyc + 10 * 65536, start_dcyc,\n\t\t\t&data_wrap, &data_count);\n\n\tfclose(pcfile);\n}\n\nvoid\nset_bp(word32 addr, word32 end_addr, word32 acc_type)\n{\n\tint\tcount;\n\n\tdbg_printf(\"About to set BP at %06x - %06x, type:%02x\\n\", addr,\n\t\t\t\t\t\t\tend_addr, acc_type);\n\tcount = g_num_breakpoints;\n\tif(count >= MAX_BREAK_POINTS) {\n\t\tdbg_printf(\"Too many (0x%02x) breakpoints set!\\n\", count);\n\t\treturn;\n\t}\n\n\tg_break_pts[count].start_addr = addr;\n\tg_break_pts[count].end_addr = end_addr;\n\tg_break_pts[count].acc_type = acc_type;\n\tg_num_breakpoints = count + 1;\n\tfixup_brks();\n}\n\nvoid\nshow_bp()\n{\n\tchar\tacc_str[4];\n\tword32\taddr, end_addr, acc_type;\n\tint\ti;\n\n\tdbg_printf(\"Showing breakpoints set\\n\");\n\tfor(i = 0; i < g_num_breakpoints; i++) {\n\t\taddr = g_break_pts[i].start_addr;\n\t\tend_addr = g_break_pts[i].end_addr;\n\t\tacc_type = g_break_pts[i].acc_type;\n\t\tacc_str[0] = ' ';\n\t\tacc_str[1] = ' ';\n\t\tacc_str[2] = ' ';\n\t\tacc_str[3] = 0;\n\t\tif(acc_type & 4) {\n\t\t\tacc_str[2] = 'X';\n\t\t}\n\t\tif(acc_type & 2) {\n\t\t\tacc_str[1] = 'W';\n\t\t}\n\t\tif(acc_type & 1) {\n\t\t\tacc_str[0] = 'R';\n\t\t}\n\t\tif(end_addr != addr) {\n\t\t\tdbg_printf(\"bp:%02x: %06x-%06x, t:%02x %s\\n\", i, addr,\n\t\t\t\t\t\tend_addr, acc_type, acc_str);\n\t\t} else {\n\t\t\tdbg_printf(\"bp:%02x: %06x, t:%02x %s\\n\", i, addr,\n\t\t\t\t\t\t\tacc_type, acc_str);\n\t\t}\n\t}\n}\n\nvoid\ndelete_bp(word32 addr, word32 end_addr)\n{\n\tint\tcount, hit;\n\tint\ti, j;\n\n\tdbg_printf(\"About to delete BP at %06x\\n\", addr);\n\tcount = g_num_breakpoints;\n\n\thit = -1;\n\tfor(i = count - 1; i >= 0; i--) {\n\t\tif((g_break_pts[i].start_addr > end_addr) ||\n\t\t\t\t\t(g_break_pts[i].end_addr < addr)) {\n\t\t\tcontinue;\t\t// Not this entry\n\t\t}\n\t\thit = i;\n\t\tdbg_printf(\"Deleting brkpoint #0x%02x\\n\", hit);\n\t\tfor(j = i+1; j < count; j++) {\n\t\t\tg_break_pts[j-1] = g_break_pts[j];\n\t\t}\n\t\tcount--;\n\t}\n\tg_num_breakpoints = count;\n\tif(hit < 0) {\n\t\tdbg_printf(\"Breakpoint not found!\\n\");\n\t} else {\n\t\tsetup_pageinfo();\n\t}\n\n\tshow_bp();\n}\n\nvoid\ndebug_iwm(const char *str)\n{\n\tif(str) {\n\t\t// Dummy use of argument\n\t}\n\tiwm_show_track(-1, -1, 0);\n}\n\nvoid\ndebug_iwm_check(const char *str)\n{\n\tif(str) {\n\t\t// Dummy use of argument\n\t}\n\tiwm_check_nibblization(0);\n}\n\nint\ndo_blank(int mode, int old_mode)\n{\n\tint\ttmp;\n\n\tswitch(old_mode) {\n\tcase 's':\n\t\ttmp = g_a2;\n\t\tif(tmp == 0) {\n\t\t\ttmp = 1;\n\t\t}\n#if 0\n\t\tfor(i = 0; i < tmp; i++) {\n\t\t\tg_stepping = 1;\n\t\t\tdo_step();\n\t\t\tif(g_halt_sim != 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n#endif\n\t\tg_list_kpc = engine.kpc;\n\t\t/* video_update_through_line(262); */\n\t\tbreak;\n\tcase ':':\n\t\tset_memory_c(((g_a3bank << 16) + g_a3), g_a2, 0);\n\t\tg_a3++;\n\t\tmode = old_mode;\n\t\tbreak;\n\tcase '.':\n\tcase 0:\n\t\txam_mem(-1);\n\t\tbreak;\n\tcase ',':\n\t\txam_mem(16);\n\t\tbreak;\n\tcase '+':\n\t\tdbg_printf(\"%x\\n\", g_a1 + g_a2);\n\t\tbreak;\n\tcase '-':\n\t\tdbg_printf(\"%x\\n\", g_a1 - g_a2);\n\t\tbreak;\n\tdefault:\n\t\tdbg_printf(\"Unknown mode at space: %d\\n\", old_mode);\n\t\tbreak;\n\t}\n\treturn mode;\n}\n\nvoid\ndo_go()\n{\n\t/* also called by do_step */\n\n\tg_config_control_panel = 0;\n\tclear_halt();\n}\n\nvoid\ndo_step()\n{\n\tint\tsize_mem_imm, size_x_imm;\n\n\treturn;\t\t\t// This is not correct\n\n\tdo_go();\n\n\tsize_mem_imm = 2;\n\tif(engine.psr & 0x20) {\n\t\tsize_mem_imm = 1;\n\t}\n\tsize_x_imm = 2;\n\tif(engine.psr & 0x10) {\n\t\tsize_x_imm = 1;\n\t}\n\tdbg_printf(\"%s\\n\",\n\t\t\tdo_dis(engine.kpc, size_mem_imm, size_x_imm, 0, 0, 0));\n}\n\nvoid\nxam_mem(int count)\n{\n\tshow_hex_mem(g_a1bank, g_a1, g_a2, count);\n\tg_a1 = g_a2 + 1;\n}\n\nvoid\nshow_hex_mem(word32 startbank, word32 start, word32 end, int count)\n{\n\tchar\tascii[MAXNUM_HEX_PER_LINE];\n\tword32\ti;\n\tint\tval, offset;\n\n\tif(count < 0) {\n\t\tcount = 16 - (start & 0xf);\n\t}\n\n\toffset = 0;\n\tascii[0] = 0;\n\tdbg_printf(\"Showing hex mem: bank: %x, start: %x, end: %x\\n\",\n\t\tstartbank, start, end);\n\tfor(i = start; i <= end; i++) {\n\t\tif( (i==start) || (count == 16) ) {\n\t\t\tdbg_printf(\"%04x:\",i);\n\t\t}\n\t\tdbg_printf(\" %02x\", get_memory_c((startbank <<16) + i));\n\t\tval = get_memory_c((startbank << 16) + i) & 0x7f;\n\t\tif((val < 32) || (val >= 0x7f)) {\n\t\t\tval = '.';\n\t\t}\n\t\tascii[offset++] = val;\n\t\tascii[offset] = 0;\n\t\tcount--;\n\t\tif(count <= 0) {\n\t\t\tdbg_printf(\"   %s\\n\", ascii);\n\t\t\toffset = 0;\n\t\t\tascii[0] = 0;\n\t\t\tcount = 16;\n\t\t}\n\t}\n\tif(offset > 0) {\n\t\tdbg_printf(\"   %s\\n\", ascii);\n\t}\n}\n\nvoid\ndo_debug_list()\n{\n\tchar\t*str;\n\tint\tsize, size_mem_imm, size_x_imm;\n\tint\ti;\n\n\tdbg_printf(\"%d=m %d=x %d=LCBANK\\n\", (engine.psr >> 5)&1,\n\t\t(engine.psr >> 4) & 1, (g_c068_statereg & 0x4) >> 2);\n\n\tsize_mem_imm = 2;\n\tif(engine.psr & 0x20) {\n\t\tsize_mem_imm = 1;\n\t}\n\tsize_x_imm = 2;\n\tif(engine.psr & 0x10) {\n\t\tsize_x_imm = 1;\n\t}\n\tfor(i = 0; i < 20; i++) {\n\t\tstr = do_dis(g_list_kpc, size_mem_imm, size_x_imm, 0, 0, &size);\n\t\tg_list_kpc += size;\n\t\tdbg_printf(\"%s\\n\", str);\n\t}\n}\n\nvoid\ndis_do_memmove()\n{\n\tword32\tval;\n\n\tdbg_printf(\"Memory move from %02x/%04x.%04x to %02x/%04x\\n\", g_a1bank,\n\t\t\t\t\t\tg_a1, g_a2, g_a4bank, g_a4);\n\twhile(g_a1 <= (g_a2 & 0xffff)) {\n\t\tval = get_memory_c((g_a1bank << 16) + g_a1);\n\t\tset_memory_c((g_a4bank << 16) + g_a4, val, 0);\n\t\tg_a1++;\n\t\tg_a4++;\n\t}\n\tg_a1 = g_a1 & 0xffff;\n\tg_a4 = g_a4 & 0xffff;\n}\n\nvoid\ndis_do_pattern_search()\n{\n#if 0\n\tword32\tmatch_val, val;\n\tint\tmatch_shift, count;\n\n\tdbg_printf(\"Memory pattern search for %04x in %02x/%04x to %02x/%04x\\n\",\n\t\t\tg_a4, g_a1bank, g_a1, g_a2bank, g_a2);\n\tmatch_shift = 0;\n\tcount = 0;\n\tmatch_val = g_a4;\n\twhile(1) {\n\t\tif(g_a1bank > g_a2bank) {\n\t\t\tbreak;\n\t\t}\n\t\tif(g_a1 > g_a2) {\n\t\t\tbreak;\n\t\t}\n\t\tval = get_memory_c((g_a1bank << 16) + g_a1);\n\t\tif(val == ((match_val >> match_shift) & 0xff)) {\n\t\t\tmatch_shift += 8;\n\t\t\tif(match_shift >= 16) {\n\t\t\t\tdbg_printf(\"Found %04x at %02x/%04x\\n\",\n\t\t\t\t\t\tmatch_val, g_a1bank, g_a1);\n\t\t\t\tcount++;\n\t\t\t}\n\t\t} else {\n\t\t\tmatch_shift = 0;\n\t\t}\n\t\tg_a1++;\n\t\tif(g_a1 >= 0x10000) {\n\t\t\tg_a1 = 0;\n\t\t\tg_a1bank++;\n\t\t}\n\t}\n#endif\n}\n\nvoid\ndis_do_compare()\n{\n\tword32\tval1, val2;\n\n\tdbg_printf(\"Memory Compare from %02x/%04x.%04x with %02x/%04x\\n\",\n\t\t\t\t\tg_a1bank, g_a1, g_a2, g_a4bank, g_a4);\n\twhile(g_a1 <= (g_a2 & 0xffff)) {\n\t\tval1 = get_memory_c((g_a1bank << 16) + g_a1);\n\t\tval2 = get_memory_c((g_a4bank << 16) + g_a4);\n\t\tif(val1 != val2) {\n\t\t\tdbg_printf(\"%02x/%04x: %02x vs %02x\\n\", g_a1bank, g_a1,\n\t\t\t\t\t\t\t\tval1, val2);\n\t\t}\n\t\tg_a1++;\n\t\tg_a4++;\n\t}\n\tg_a1 = g_a1 & 0xffff;\n\tg_a4 = g_a4 & 0xffff;\n}\n\nconst char *\ndo_debug_unix(const char *str, int old_mode)\n{\n\tchar\tlocalbuf[LINE_SIZE+2];\n\tbyte\t*bptr;\n\tword32\toffset, len, a1_val;\n\tlong\tret;\n\tint\tfd, load;\n\tint\ti;\n\n\tload = 0;\n\tswitch(*str++) {\n\tcase 'l': case 'L':\n\t\tdbg_printf(\"Loading..\");\n\t\tload = 1;\n\t\tbreak;\n\tcase 's': case 'S':\n\t\tdbg_printf(\"Saving...\");\n\t\tbreak;\n\tdefault:\n\t\tdbg_printf(\"Unknown unix command: %c\\n\", *(str - 1));\n\t\tif(str[-1] == 0) {\n\t\t\treturn str - 1;\n\t\t}\n\t\treturn str;\n\t}\n\twhile((*str == ' ') || (*str == '\\t')) {\n\t\tstr++;\n\t}\n\ti = 0;\n\twhile(i < LINE_SIZE) {\n\t\tlocalbuf[i++] = *str++;\n\t\tif((*str==' ') || (*str == '\\t') || (*str == '\\n') ||\n\t\t\t\t\t\t\t\t(*str == 0)) {\n\t\t\tbreak;\n\t\t}\n\t}\n\tlocalbuf[i] = 0;\n\n\tdbg_printf(\"About to open: %s,len: %d\\n\", localbuf,\n\t\t\t\t\t\t(int)strlen(localbuf));\n\tif(load) {\n\t\tfd = open(localbuf, O_RDONLY | O_BINARY);\n\t} else {\n\t\tfd = open(localbuf, O_WRONLY | O_CREAT | O_BINARY, 0x1b6);\n\t}\n\tif(fd < 0) {\n\t\tdbg_printf(\"Open %s failed: %d. errno:%d\\n\", localbuf, fd,\n\t\t\t\t\t\t\t\terrno);\n\t\treturn str;\n\t}\n\tif(load) {\n\t\toffset = g_a1 & 0xffff;\n\t\tlen = 0x20000 - offset;\n\t} else {\n\t\tif(old_mode == '.') {\n\t\t\tlen = g_a2 - g_a1 + 1;\n\t\t} else {\n\t\t\tlen = 0x100;\n\t\t}\n\t}\n\ta1_val = (g_a1bank << 16) | g_a1;\n\tbptr = &g_memory_ptr[a1_val];\n\tif((g_a1bank >= 0xe0) && (g_a1bank < 0xe2)) {\n\t\tbptr = &g_slow_memory_ptr[a1_val & 0x1ffff];\n\t}\n\tif(load) {\n\t\tret = read(fd, bptr, len);\n\t} else {\n\t\tret = write(fd, bptr, len);\n\t}\n\tdbg_printf(\"Read/write: addr %06x for %04x bytes, ret: %lx bytes\\n\",\n\t\ta1_val, len, ret);\n\tif(ret < 0) {\n\t\tdbg_printf(\"errno: %d\\n\", errno);\n\t}\n\tg_a1 = g_a1 + (int)ret;\n\treturn str;\n}\n\nvoid\ndo_debug_load()\n{\n\tdbg_printf(\"Sorry, can't load now\\n\");\n}\n\nchar *\ndo_dis(word32 kpc, int accsize, int xsize, int op_provided, word32 instr,\n\t\t\t\t\t\t\tint *size_ptr)\n{\n\tchar\tbuffer[MAX_DISAS_BUF];\n\tchar\tbuffer2[MAX_DISAS_BUF];\n\tconst char *str;\n\tword32\tval, oldkpc, dtype;\n\tint\targs, type, opcode, signed_val;\n\tint\ti;\n\n\toldkpc = kpc;\n\tif(op_provided) {\n\t\topcode = (instr >> 24) & 0xff;\n\t} else {\n\t\topcode = (int)get_memory_c(kpc) & 0xff;\n\t}\n\n\tkpc++;\n\n\tdtype = disas_types[opcode];\n\tstr = disas_opcodes[opcode];\n\ttype = dtype & 0xff;\n\targs = dtype >> 8;\n\n\tif(args > 3) {\n\t\tif(args == 4) {\n\t\t\targs = accsize;\n\t\t} else if(args == 5) {\n\t\t\targs = xsize;\n\t\t}\n\t}\n\n\tval = -1;\n\tswitch(args) {\n\tcase 0:\n\t\tval = 0;\n\t\tbreak;\n\tcase 1:\n\t\tif(op_provided) {\n\t\t\tval = instr & 0xff;\n\t\t} else {\n\t\t\tval = get_memory_c(kpc);\n\t\t}\n\t\tbreak;\n\tcase 2:\n\t\tif(op_provided) {\n\t\t\tval = instr & 0xffff;\n\t\t} else {\n\t\t\tval = get_memory16_c(kpc);\n\t\t}\n\t\tbreak;\n\tcase 3:\n\t\tif(op_provided) {\n\t\t\tval = instr & 0xffffff;\n\t\t} else {\n\t\t\tval = get_memory24_c(kpc);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tfprintf(stderr, \"args out of rang: %d, opcode: %08x\\n\",\n\t\t\targs, opcode);\n\t\tbreak;\n\t}\n\tkpc += args;\n\n\tif(!op_provided) {\n\t\tinstr = (opcode << 24) | (val & 0xffffff);\n\t}\n\n\tswitch(type) {\n\tcase ABS:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%04x\", str, val);\n\t\tbreak;\n\tcase ABSX:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%04x,X\", str, val);\n\t\tbreak;\n\tcase ABSY:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%04x,Y\", str, val);\n\t\tbreak;\n\tcase ABSLONG:\n\t\tif(args != 3) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%06x\", str, val);\n\t\tbreak;\n\tcase ABSIND:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s ($%04x)\", str, val);\n\t\tbreak;\n\tcase ABSXIND:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s ($%04x,X)\", str, val);\n\t\tbreak;\n\tcase IMPLY:\n\tcase ACCUM:\n\t\tif(args != 0) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF,  \"%s\", str);\n\t\tbreak;\n\tcase IMMED:\n\t\tif(args == 1) {\n\t\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  #$%02x\", str,\n\t\t\t\t\t\t\t\t\tval);\n\t\t} else if(args == 2) {\n\t\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  #$%04x\", str,\n\t\t\t\t\t\t\t\t\tval);\n\t\t} else {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tbreak;\n\tcase JUST8:\n\tcase DLOC:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%02x\", str, val);\n\t\tbreak;\n\tcase DLOCX:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%02x,X\", str, val);\n\t\tbreak;\n\tcase DLOCY:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%02x,Y\", str, val);\n\t\tbreak;\n\tcase LONG:\n\t\tif(args != 3) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%06x\", str, val);\n\t\tbreak;\n\tcase LONGX:\n\t\tif(args != 3) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%06x,X\", str, val);\n\t\tbreak;\n\tcase DLOCIND:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  ($%02x)\", str, val);\n\t\tbreak;\n\tcase DLOCINDY:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  ($%02x),Y\", str, val);\n\t\tbreak;\n\tcase DLOCXIND:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  ($%02x,X)\", str, val);\n\t\tbreak;\n\tcase DLOCBRAK:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  [$%02x]\", str, val);\n\t\tbreak;\n\tcase DLOCBRAKY:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  [$%02x],y\", str, val);\n\t\tbreak;\n\tcase DISP8:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsigned_val = (signed char)val;\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%04x\", str,\n\t\t\t\t\t\t(kpc + signed_val) & 0xffff);\n\t\tbreak;\n\tcase DISP8S:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%02x,S\", str,\n\t\t\t\t\t\t\t\tval & 0xff);\n\t\tbreak;\n\tcase DISP8SINDY:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  ($%02x,S),Y\", str,\n\t\t\t\t\t\t\t\tval & 0xff);\n\t\tbreak;\n\tcase DISP16:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%04x\", str,\n\t\t\t\t(word32)(kpc+(signed)(word16)(val)) & 0xffff);\n\t\tbreak;\n\tcase MVPMVN:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%02x,$%02x\", str,\n\t\t\t\t\t\t\tval & 0xff, val >> 8);\n\t\tbreak;\n\tcase SEPVAL:\n\tcase REPVAL:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  #$%02x\", str, val);\n\t\tbreak;\n\tdefault:\n\t\tdbg_printf(\"argument type: %d unexpected\\n\", type);\n\t\tbreak;\n\t}\n\n\tg_disas_buffer[0] = 0;\n\tsnprintf(&g_disas_buffer[0], MAX_DISAS_BUF, \"%02x/%04x: %02x \",\n\t\toldkpc >> 16, oldkpc & 0xffff, opcode);\n\tfor(i = 1; i <= args; i++) {\n\t\tsnprintf(&buffer2[0], MAX_DISAS_BUF, \"%02x \", instr & 0xff);\n\t\tcfg_strlcat(&g_disas_buffer[0], &buffer2[0], MAX_DISAS_BUF);\n\t\tinstr = instr >> 8;\n\t}\n\tfor(; i < 4; i++) {\n\t\tcfg_strlcat(&g_disas_buffer[0], \"   \", MAX_DISAS_BUF);\n\t}\n\tcfg_strlcat(&g_disas_buffer[0], \" \", MAX_DISAS_BUF);\n\tcfg_strlcat(&g_disas_buffer[0], &buffer[0], MAX_DISAS_BUF);\n\n\tif(size_ptr) {\n\t\t*size_ptr = args + 1;\n\t}\n\treturn (&g_disas_buffer[0]);\n}\n\nint\ndebug_get_view_line(int back)\n{\n\tint\tpos;\n\n\t// where back==0 means return pos - 1.\n\tpos = g_debug_lines_pos - 1;\n\tpos = pos - back;\n\tif(pos < 0) {\n\t\tif(g_debug_lines_alloc >= g_debug_lines_max) {\n\t\t\tpos += g_debug_lines_alloc;\n\t\t} else {\n\t\t\treturn 0;\t\t\t// HACK: return -1\n\t\t}\n\t}\n\treturn pos;\n}\n\nint\ndebug_add_output_line(char *in_str)\n{\n\tDebug_entry *line_ptr;\n\tbyte\t*out_bptr;\n\tint\tpos, alloc, view, used_len, c;\n\tint\ti;\n\n\t// printf(\"debug_add_output_line %s len:%d\\n\", in_str, len);\n\tpos = g_debug_lines_pos;\n\tline_ptr = g_debug_lines_ptr;\n\talloc = g_debug_lines_alloc;\n\tif(pos >= alloc) {\n\t\tif(alloc < g_debug_lines_max) {\n\t\t\talloc = MY_MAX(2048, alloc*3);\n\t\t\talloc = MY_MAX(alloc, pos*3);\n\t\t\talloc = MY_MIN(alloc, g_debug_lines_max);\n\t\t\tline_ptr = realloc(line_ptr,\n\t\t\t\t\t\talloc * sizeof(Debug_entry));\n\t\t\tprintf(\"realloc.  now %p, alloc:%d\\n\", line_ptr, alloc);\n\t\t\tg_debug_lines_ptr = line_ptr;\n\t\t\tg_debug_lines_alloc = alloc;\n\t\t\tprintf(\"Alloced debug lines to %d\\n\", alloc);\n\t\t} else {\n\t\t\tpos = 0;\n\t\t}\n\t}\n\t// Convert to A2 format chars: set high bit of each byte, 80 chars\n\t//  per line\n\tout_bptr = &(line_ptr[pos].str_buf[0]);\n\tused_len = 0;\n\tfor(i = 0; i < DEBUG_ENTRY_MAX_CHARS; i++) {\n\t\tc = ' ';\n\t\tif(*in_str) {\n\t\t\tc = *in_str++;\n\t\t\tused_len++;\n\t\t}\n\t\tc = c ^ 0x80;\t\t// Set highbit if not already set\n\t\tout_bptr[i] = c;\n\t}\n\tpos++;\n\tg_debug_lines_pos = pos;\n\tg_debug_lines_total++;\t\t// For updating the window\n\tg_debugwin_changed++;\n\tview = g_debug_lines_view;\n\tif(view >= 0) {\n\t\tview++;\t\t// view is back from pos, so to stay the same,\n\t\t\t\t//  it must be incremented when pos incs\n\t\tif((view - 50) >= g_debug_lines_max) {\n\t\t\t// We were viewing the oldest page, and by wrapping\n\t\t\t//  around we're about to wipe out this old data\n\t\t\t// Jump to most recent data\n\t\t\tview = -1;\n\t\t}\n\t\tg_debug_lines_view = view;\n\t}\n\n\treturn used_len;\n}\n\nvoid\ndebug_add_output_string(char *in_str, int len)\n{\n\tint\tret, tries;\n\n\ttries = 0;\n\tret = 0;\n\tif(g_debug_to_stdout) {\n\t\tputs(in_str);\t\t// Send output to stdout, too\n\t}\n\twhile((len > 0) || (tries == 0)) {\n\t\t// printf(\"DEBUG: adding str: %s, len:%d, ret:%d\\n\", in_str,\n\t\t//\t\t\t\t\t\tlen, ret);\n\t\tret = debug_add_output_line(in_str);\n\t\tlen -= ret;\n\t\tin_str += ret;\n\t\ttries++;\n\t}\n}\n\nvoid\ndebug_add_output_chars(char *str)\n{\n\tint\tpos, c, tab_spaces;\n\n\tpos = g_debug_stage_pos;\n\ttab_spaces = 0;\n\twhile(1) {\n\t\tif(tab_spaces > 0) {\n\t\t\tc = ' ';\n\t\t\ttab_spaces--;\n\t\t} else {\n\t\t\tc = *str++;\n\t\t\tif(c == '\\t') {\n\t\t\t\ttab_spaces = 7 - (pos & 7);\n\t\t\t\tc = ' ';\n\t\t\t}\n\t\t}\n\t\tpos = MY_MIN(pos, (PRINTF_BUF_SIZE - 1));\n\t\tif((c == '\\n') || (pos >= (PRINTF_BUF_SIZE - 1))) {\n\t\t\tg_debug_stage_buf[pos] = 0;\n\t\t\tdebug_add_output_string(&g_debug_stage_buf[0], pos);\n\t\t\tpos = 0;\n\t\t\tg_debug_stage_pos = 0;\n\t\t\tcontinue;\n\t\t}\n\t\tif(c == 0) {\n\t\t\tg_debug_stage_pos = pos;\n\t\t\treturn;\n\t\t}\n\t\tg_debug_stage_buf[pos++] = c;\n\t}\n}\n\nint\ndbg_printf(const char *fmt, ...)\n{\n\tva_list args;\n\tint\tret;\n\n\tva_start(args, fmt);\n\tret = dbg_vprintf(fmt, args);\n\tva_end(args);\n\treturn ret;\n}\n\nint\ndbg_vprintf(const char *fmt, va_list args)\n{\n\tint\tret;\n\n\tret = vsnprintf(&g_debug_printf_buf[0], PRINTF_BUF_SIZE, fmt, args);\n\tdebug_add_output_chars(&g_debug_printf_buf[0]);\n\treturn ret;\n}\n\nvoid\nhalt_printf(const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tdbg_vprintf(fmt, args);\n\tva_end(args);\n\n\tset_halt(1);\n}\n\nvoid\nhalt2_printf(const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tdbg_vprintf(fmt, args);\n\tva_end(args);\n\n\tset_halt(2);\n}\n\n"
  },
  {
    "path": "gsplus/src/defc.h",
    "content": "#ifdef INCLUDE_RCSID_C\n#endif\n\n/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2024 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include \"defcomm.h\"\n\n#define STRUCT(a) typedef struct a ## _st a; struct a ## _st\n\ntypedef unsigned char byte;\ntypedef unsigned short word16;\ntypedef unsigned int word32;\n#if _MSC_VER\ntypedef unsigned __int64 dword64;\n#else\ntypedef unsigned long long dword64;\n#endif\n\n/* 28MHz crystal, plus every 65th 1MHz cycle is stretched 140ns */\n#define CYCS_28_MHZ\t\t(28636360)\n#define DCYCS_28_MHZ\t\t(1.0*CYCS_28_MHZ)\n#define CYCS_3_5_MHZ\t\t(CYCS_28_MHZ/8)\n#define DCYCS_1_MHZ\t\t((DCYCS_28_MHZ/28.0)*(65.0*7/(65.0*7+1.0)))\n\n// DCYCS_1_MHZ is 1020484.32016\n\n#define CYCLES_IN_16MS_RAW\t(262 * 65)\n/* Use precisely 17030 instead of forcing 60 Hz since this is the number of */\n/*  1MHz cycles per screen */\n\n#define DCYCS_IN_16MS\t\t((double)(CYCLES_IN_16MS_RAW))\n#define DRECIP_DCYCS_IN_16MS\t(1.0 / (DCYCS_IN_16MS))\n#define VBL_RATE\t\t(DCYCS_1_MHZ / DCYCS_IN_16MS)\n// VBL rate is about 59.9227 frames/sec\n\n#define MAXNUM_HEX_PER_LINE\t32\n#define MAX_SCALE_SIZE\t\t5100\n\n#ifdef __NeXT__\n# include <libc.h>\n#endif\n\n#ifndef _WIN32\n# include <unistd.h>\n# include <sys/ioctl.h>\n# include <sys/wait.h>\n#endif\n\n#include <stdio.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <stdarg.h>\n#include <ctype.h>\n#ifdef HPUX\n# include <machine/inline.h>\t\t/* for GET_ITIMER */\n#endif\n\n#ifdef SOLARIS\n# include <sys/filio.h>\n#endif\n\n#ifdef _WIN32\n# include <direct.h>\n# include <io.h>\n# pragma warning(disable : 4996)\t/* open() is deprecated...sigh */\nint ftruncate(int fd, word32 length);\nint lstat(const char *path, struct stat *bufptr);\n#endif\n\n#ifndef O_BINARY\n/* work around some Windows junk */\n# define O_BINARY\t0\n#endif\n\n#define MAX_CHANGE_RECTS\t20\n\n#ifdef __GNUC__\nint dbg_printf(const char *fmt, ...) __attribute__ ((\n\t\t\t\t\t\t__format__(printf, 1, 2)));\n#endif\n\nSTRUCT(Pc_log) {\n\tdword64\tdfcyc;\n\tword32\tdbank_kpc;\n\tword32\tinstr;\n\tword32\tpsr_acc;\n\tword32\txreg_yreg;\n\tword32\tstack_direct;\n\tword32\tpad;\n};\n\nSTRUCT(Data_log) {\n\tdword64\tdfcyc;\n\tbyte\t*stat;\n\tword32\taddr;\n\tword32\tval;\n\tword32\tsize;\n};\n\nSTRUCT(Event) {\n\tdword64\tdfcyc;\n\tint\ttype;\n\tEvent\t*next;\n};\n\nSTRUCT(Fplus) {\n\tdword64\tdplus_1;\n\tdword64\tdplus_x_minus_1;\n};\n\nSTRUCT(Engine_reg) {\n\tdword64\tdfcyc;\n\tword32\tkpc;\n\tword32\tacc;\n\n\tword32\txreg;\n\tword32\tyreg;\n\n\tword32\tstack;\n\tword32\tdbank;\n\n\tword32\tdirect;\n\tword32\tpsr;\n\tFplus\t*fplus_ptr;\n};\n\nSTRUCT(Break_point) {\n\tword32\tstart_addr;\n\tword32\tend_addr;\n\tword32\tacc_type;\n};\n\nSTRUCT(Change_rect) {\n\tint\tx;\n\tint\ty;\n\tint\twidth;\n\tint\theight;\n};\n\n\nSTRUCT(Kimage) {\n\tword32\t*wptr;\n\tint\ta2_width_full;\n\tint\ta2_height_full;\n\tint\ta2_width;\n\tint\ta2_height;\n\tint\tx_width;\n\tint\tx_height;\n\tint\tx_refresh_needed;\n\tint\tx_max_width;\n\tint\tx_max_height;\n\tint\tx_xpos;\n\tint\tx_ypos;\n\tint\tactive;\n\tword32\tvbl_of_last_resize;\n\tword32\tc025_val;\n\tword32\tscale_width_to_a2;\n\tword32\tscale_width_a2_to_x;\n\tword32\tscale_height_to_a2;\n\tword32\tscale_height_a2_to_x;\n\tint\tnum_change_rects;\n\tChange_rect change_rect[MAX_CHANGE_RECTS];\n\tword32\tscale_width[MAX_SCALE_SIZE + 1];\n\tword32\tscale_height[MAX_SCALE_SIZE + 1];\n};\n\ntypedef byte *Pg_info;\nSTRUCT(Page_info) {\n\tPg_info rd_wr;\n};\n\nSTRUCT(Cfg_menu) {\n\tconst char *str;\n\tvoid\t*ptr;\n\tconst char *name_str;\n\tvoid\t*defptr;\n\tint\tcfgtype;\n};\n\nSTRUCT(Cfg_dirent) {\n\tchar\t*name;\n\tint\tis_dir;\n\tint\tpart_num;\n\tdword64\tdsize;\n\tdword64\tdimage_start;\n\tdword64\tcompr_dsize;\n};\n\nSTRUCT(Cfg_listhdr) {\n\tCfg_dirent *direntptr;\n\tint\tmax;\n\tint\tlast;\n\tint\tinvalid;\n\n\tint\tcurent;\n\tint\ttopent;\n\n\tint\tnum_to_show;\n};\n\ntypedef void (Dbg_fn)(const char *str);\n\nSTRUCT(Dbg_longcmd) {\n\tconst char *str;\n\tDbg_fn\t*fnptr;\n\tDbg_longcmd *subptr;\n\tconst char *help_str;\n};\n\nSTRUCT(Emustate_intlist) {\n\tconst char *str;\n\tword32\t*iptr;\n};\n\nSTRUCT(Emustate_dword64list) {\n\tconst char *str;\n\tdword64\t*dptr;\n};\n\nSTRUCT(Emustate_word32list) {\n\tconst char *str;\n\tword32\t*wptr;\n};\n\nSTRUCT(Lzw_state) {\n\tword32\ttable[4096 + 2];\n\tword32\tentry;\n\tint\tbits;\n};\n\n#ifdef __LP64__\n# define PTR2WORD(a)\t((word32)(unsigned long long)(a))\n#else\n# define PTR2WORD(a)\t((word32)(unsigned long long)(a))\n#endif\n\n\n#define ALTZP\t(g_c068_statereg & 0x80)\n/* #define PAGE2 (g_c068_statereg & 0x40) */\n#define RAMRD\t(g_c068_statereg & 0x20)\n#define RAMWRT\t(g_c068_statereg & 0x10)\n#define RDROM\t(g_c068_statereg & 0x08)\n#define LCBANK2\t(g_c068_statereg & 0x04)\n#define ROMB\t(g_c068_statereg & 0x02)\n// #define INTCX\t(g_c068_statereg & 0x01)\n\n#define C041_EN_25SEC_INTS\t0x10\n#define C041_EN_VBL_INTS\t0x08\n#define C041_EN_SWITCH_INTS\t0x04\n#define C041_EN_MOVE_INTS\t0x02\n#define C041_EN_MOUSE\t\t0x01\n\n/* WARNING: SCC1 and SCC0 interrupts must be in this order for scc.c */\n/*  This order matches the SCC hardware, and SCC1_ZEROCNT must be 0x0001 */\n#define IRQ_PENDING_SCC1_ZEROCNT\t0x00001\n#define IRQ_PENDING_SCC1_TX\t\t0x00002\n#define IRQ_PENDING_SCC1_RX\t\t0x00004\n#define IRQ_PENDING_SCC0_ZEROCNT\t0x00008\n#define IRQ_PENDING_SCC0_TX\t\t0x00010\n#define IRQ_PENDING_SCC0_RX\t\t0x00020\n#define IRQ_PENDING_C023_SCAN\t\t0x00100\n#define IRQ_PENDING_C023_1SEC\t\t0x00200\n#define IRQ_PENDING_C046_25SEC\t\t0x00400\n#define IRQ_PENDING_C046_VBL\t\t0x00800\n#define IRQ_PENDING_ADB_KBD_SRQ\t\t0x01000\n#define IRQ_PENDING_ADB_DATA\t\t0x02000\n#define IRQ_PENDING_ADB_MOUSE\t\t0x04000\n#define IRQ_PENDING_DOC\t\t\t0x08000\n#define IRQ_PENDING_MOCKINGBOARDA\t0x10000\n#define IRQ_PENDING_MOCKINGBOARDB\t0x20000\t\t/* must be BOARDA*2 */\n\n\n#define EXTRU(val, pos, len)\t\t\t\t\t\\\n\t( ( (len) >= (pos) + 1) ? ((val) >> (31-(pos))) :\t\\\n\t\t(((val) >> (31-(pos)) ) & ( (1<<(len) ) - 1) ) )\n\n#define DEP1(val, pos, old_val)\t\t\t\t\t\\\n\t\t(((old_val) & ~(1 << (31 - (pos))) ) |\t\t\\\n\t\t\t( ((val) & 1) << (31 - (pos))) )\n\n#define set_halt(val) \\\n\tif(val) { set_halt_act(val); }\n\n#define clear_halt() \\\n\tclr_halt_act()\n\n#define GET_PAGE_INFO_RD(page) \\\n\t(page_info_rd_wr[page].rd_wr)\n\n#define GET_PAGE_INFO_WR(page) \\\n\t(page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr)\n\n#define SET_PAGE_INFO_RD(page,val) \\\n\t;page_info_rd_wr[page].rd_wr = (Pg_info)val;\n\n#define SET_PAGE_INFO_WR(page,val) \\\n\t;page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr = \\\n\t\t\t\t\t\t\t(Pg_info)val;\n\n#define VERBOSE_DISK\t0x001\n#define VERBOSE_IRQ\t0x002\n#define VERBOSE_CLK\t0x004\n#define VERBOSE_SHADOW\t0x008\n#define VERBOSE_IWM\t0x010\n#define VERBOSE_DOC\t0x020\n#define VERBOSE_ADB\t0x040\n#define VERBOSE_SCC\t0x080\n#define VERBOSE_TEST\t0x100\n#define VERBOSE_VIDEO\t0x200\n#define VERBOSE_MAC\t0x400\n#define VERBOSE_DYNA\t0x800\n\n#ifdef NO_VERB\n# define DO_VERBOSE\t0\n#else\n# define DO_VERBOSE\t1\n#endif\n\n#define disk_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_DISK)) printf\n#define irq_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_IRQ)) printf\n#define clk_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_CLK)) printf\n#define shadow_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_SHADOW)) printf\n#define iwm_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_IWM)) printf\n#define doc_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_DOC)) printf\n#define adb_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_ADB)) printf\n#define scc_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_SCC)) printf\n#define test_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_TEST)) printf\n#define vid_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_VIDEO)) printf\n#define mac_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_MAC)) printf\n#define dyna_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_DYNA)) printf\n\n\n#define HALT_ON_SCAN_INT\t0x001\n#define HALT_ON_IRQ\t\t0x002\n#define HALT_ON_SHADOW_REG\t0x004\n#define HALT_ON_C70D_WRITES\t0x008\n\n#define HALT_ON(a, msg)\t\t\t\\\n\tif(Halt_on & a) {\t\t\\\n\t\thalt_printf(msg);\t\\\n\t}\n\n\n#define MY_MIN(a,b)\t(((a) < (b)) ? (a) : (b))\n#define MY_MAX(a,b)\t(((a) > (b)) ? (a) : (b))\n\n#define GET_ITIMER(dest)\tdest = get_itimer();\n\n#define FINISH(arg1, arg2)\tg_ret1 = arg1 | ((arg2) << 8); g_dcycles_end=0;\n\n#include \"iwm.h\"\n#include \"protos.h\"\n"
  },
  {
    "path": "gsplus/src/defcomm.h",
    "content": "#ifdef INCLUDE_RCSID_C\nconst char rcsdif_defcomm_h[] = \"@(#)$KmKId: defcomm.h,v 1.109 2023-11-12 15:29:41+00 kentd Exp $\";\n#endif\n\n/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#define SHIFT_PER_CHANGE\t3\n#define CHANGE_SHIFT\t\t(5 + SHIFT_PER_CHANGE)\n\n#define SLOW_MEM_CH_SIZE\t(0x20000 >> CHANGE_SHIFT)\n\n#define MAXNUM_HEX_PER_LINE\t32\n\n/* Different Joystick defines */\n#define JOYSTICK_MOUSE\t\t1\n#define JOYSTICK_LINUX\t\t2\n#define JOYSTICK_KEYPAD\t\t3\n#define JOYSTICK_WIN32_1\t4\n#define JOYSTICK_WIN32_2\t5\n\n#define MAX_BREAK_POINTS\t0x20\n\n#define MAX_BP_INDEX\t\t0x100\n#define MAX_BP_PER_INDEX\t3\t/* 4 word32s total = 16 bytes */\n#define SIZE_BREAKPT_ENTRY_BITS\t4\t/* 16 bytes = 4 bits */\n\n/* Warning--next defines used by asm! */\n#define PAGE_INFO_PAD_SIZE\t0x800\n#define PAGE_INFO_WR_OFFSET\t0x10000+PAGE_INFO_PAD_SIZE\n\n#define BANK_IO_BIT\t\t31\n#define BANK_SHADOW_BIT\t\t30\n#define BANK_SHADOW2_BIT\t29\n#define BANK_IO2_BIT\t\t28\n#define BANK_BREAK_BIT\t\t27\n#define BANK_BREAK\t\t(1 << (31 - BANK_BREAK_BIT))\n#define BANK_IO2_TMP\t\t(1 << (31 - BANK_IO2_BIT))\n#define BANK_IO_TMP\t\t(1 << (31 - BANK_IO_BIT))\n#define BANK_SHADOW\t\t(1 << (31 - BANK_SHADOW_BIT))\n#define BANK_SHADOW2\t\t(1 << (31 - BANK_SHADOW2_BIT))\n#define SET_BANK_IO\t\\\n\t\t(&g_dummy_memory1_ptr[BANK_IO_TMP | BANK_IO2_TMP])\n\n#define BANK_BAD_MEM\t\t(&g_dummy_memory1_ptr[0xff])\n\n\n#define RET_BREAK\t0x1\n#define RET_COP\t\t0x2\n#define RET_WDM\t\t0x3\n#define RET_WAI\t\t0x4\n#define RET_STP\t\t0x5\n#define RET_PSR\t\t0x6\n#define RET_IRQ\t\t0x7\n#define RET_TOOLTRACE\t0x8\n\n\n#define BIT_ALL_STAT_TEXT\t\t0\n#define BIT_ALL_STAT_VID80\t\t1\n#define BIT_ALL_STAT_ST80\t\t2\n#define BIT_ALL_STAT_COLOR_C021\t\t3\n#define BIT_ALL_STAT_MIX_T_GR\t\t4\n#define BIT_ALL_STAT_DIS_COLOR_DHIRES\t5\t/* special val, c029 */\n#define BIT_ALL_STAT_PAGE2\t\t6\t/* special val, statereg */\n#define BIT_ALL_STAT_SUPER_HIRES\t7\t/* special, c029 */\n#define BIT_ALL_STAT_HIRES\t\t8\n#define BIT_ALL_STAT_ANNUNC3\t\t9\n#define BIT_ALL_STAT_ALTCHARSET\t\t10\n#define BIT_ALL_STAT_FLASH_STATE\t11\n#define BIT_ALL_STAT_BG_COLOR\t\t12\t/* 4 bits */\n#define BIT_ALL_STAT_TEXT_COLOR\t\t16\t/* 4 bits */\n\t\t\t\t\t\t/* Text must be just above */\n\t\t\t\t\t\t/* bg to match c022 reg */\n#define BIT_ALL_STAT_VOC_INTERLACE\t20\n#define BIT_ALL_STAT_VOC_MAIN\t\t21\n#define BIT_ALL_STAT_BORDER\t\t22\n\n#define ALL_STAT_SUPER_HIRES\t\t(1 << (BIT_ALL_STAT_SUPER_HIRES))\n#define ALL_STAT_TEXT\t\t\t(1 << (BIT_ALL_STAT_TEXT))\n#define ALL_STAT_VID80\t\t\t(1 << (BIT_ALL_STAT_VID80))\n#define ALL_STAT_PAGE2\t\t\t(1 << (BIT_ALL_STAT_PAGE2))\n#define ALL_STAT_ST80\t\t\t(1 << (BIT_ALL_STAT_ST80))\n#define ALL_STAT_COLOR_C021\t\t(1 << (BIT_ALL_STAT_COLOR_C021))\n#define ALL_STAT_DIS_COLOR_DHIRES\t(1 << (BIT_ALL_STAT_DIS_COLOR_DHIRES))\n#define ALL_STAT_MIX_T_GR\t\t(1 << (BIT_ALL_STAT_MIX_T_GR))\n#define ALL_STAT_HIRES\t\t\t(1 << (BIT_ALL_STAT_HIRES))\n#define ALL_STAT_ANNUNC3\t\t(1 << (BIT_ALL_STAT_ANNUNC3))\n#define ALL_STAT_TEXT_COLOR\t\t(0xf << (BIT_ALL_STAT_TEXT_COLOR))\n#define ALL_STAT_BG_COLOR\t\t(0xf << (BIT_ALL_STAT_BG_COLOR))\n#define ALL_STAT_ALTCHARSET\t\t(1 << (BIT_ALL_STAT_ALTCHARSET))\n#define ALL_STAT_FLASH_STATE\t\t(1 << (BIT_ALL_STAT_FLASH_STATE))\n#define ALL_STAT_VOC_INTERLACE\t\t(1 << (BIT_ALL_STAT_VOC_INTERLACE))\n#define ALL_STAT_VOC_MAIN\t\t(1 << (BIT_ALL_STAT_VOC_MAIN))\n#define ALL_STAT_BORDER\t\t\t(1 << (BIT_ALL_STAT_BORDER))\n\n#define BORDER_WIDTH\t\t32\n\n#define EFF_BORDER_WIDTH\t(BORDER_WIDTH + (640-560))\n\n/* BASE_MARGIN_BOTTOM+MARGIN_TOP must equal 62.  There are 262 scan lines */\n/*  at 60Hz (15.7KHz line rate) and so we just make 62 border lines */\n#define BASE_MARGIN_TOP\t\t32\n#define BASE_MARGIN_BOTTOM\t30\n#define BASE_MARGIN_LEFT\tBORDER_WIDTH\n#define BASE_MARGIN_RIGHT\tBORDER_WIDTH\n\n#define A2_WINDOW_WIDTH\t\t640\n#define A2_WINDOW_HEIGHT\t400\n\n#define X_A2_WINDOW_WIDTH\t(A2_WINDOW_WIDTH + BASE_MARGIN_LEFT + \\\n\t\t\t\t\t\t\tBASE_MARGIN_RIGHT)\n#define X_A2_WINDOW_HEIGHT\t(A2_WINDOW_HEIGHT + BASE_MARGIN_TOP + \\\n\t\t\t\t\t\t\tBASE_MARGIN_BOTTOM)\n\n#define MAX_STATUS_LINES\t4\n#define STATUS_LINE_LENGTH\t88\n\n#define BASE_WINDOW_WIDTH\t(X_A2_WINDOW_WIDTH)\n\n\n#define A2_BORDER_COLOR_NUM\t0xfe\n\n"
  },
  {
    "path": "gsplus/src/defs_instr.h",
    "content": "// $KmKId: defs_instr.h,v 1.70 2023-11-05 16:22:26+00 kentd Exp $\n\n/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2021 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#undef GET_DLOC_X_IND_RD\n\n#ifdef ACC8\n# define GET_DLOC_X_IND_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DLOC_X_IND_WR();\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DLOC_X_IND_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DLOC_X_IND_WR();\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#undef GET_DISP8_S_RD\n\n#ifdef ACC8\n# define GET_DISP8_S_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DISP8_S_WR();\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DISP8_S_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DISP8_S_WR();\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#undef GET_DLOC_RD\n\n#ifdef ACC8\n# define GET_DLOC_RD()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\\\n\t}\t\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\\\n\tGET_MEMORY8((direct + arg) & 0xffff, arg);\n#else\n# define GET_DLOC_RD()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\\\n\t}\t\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\\\n\tGET_MEMORY16((direct + arg) & 0xffff, arg, 1);\n#endif\n\n#undef GET_DLOC_RD_RMW\n#undef GET_MEM_RMW\n\n#define GET_MEM_RMW()\t\t\t\t\t\t\\\n\tif(!IS_ACC16) {\t\t\t\t\t\t\\\n\t\tif(psr & 0x100) {\t\t\t\t\\\n\t\t\t/* emulation re-writes the address */\t\\\n\t\t\tSET_MEMORY8(addr_latch, arg);\t\t\\\n\t\t} else {\t\t\t\t\t\\\n\t\t\t/* otherwise, just read addr again */\t\\\n\t\t\tGET_MEMORY8(addr_latch, dummy1);\t\\\n\t\t}\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\t/* 16-bit re-reads addr+1 again */\t\t\\\n\t\tdummy1 = addr_latch + 1;\t\t\t\\\n\t\tGET_MEMORY8(dummy1, dummy1);\t\t\t\\\n\t\taddr_latch--;\t\t\t\t\t\\\n\t}\n\n#define GET_DLOC_RD_RMW()\t\t\t\\\n\tGET_DLOC_RD();\t\t\t\t\\\n\tGET_MEM_RMW();\n\n\n#undef GET_DLOC_L_IND_RD\n\n#ifdef ACC8\n# define GET_DLOC_L_IND_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DLOC_L_IND_WR();\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DLOC_L_IND_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DLOC_L_IND_WR();\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#undef GET_IMM_MEM\n\n#ifdef ACC8\n# define GET_IMM_MEM()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tINC_KPC_2;\n#else\n# define GET_IMM_MEM()\t\t\t\t\\\n\tGET_2BYTE_ARG;\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\\\n\tINC_KPC_3;\n#endif\n\n#undef GET_ABS_RD\n\n#ifdef ACC8\n# define GET_ABS_RD()\t\t\t\t\\\n\tGET_2BYTE_ARG;\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\\\n\tGET_MEMORY8((dbank << 16) + arg, arg);\t\\\n\tINC_KPC_3;\n#else\n# define GET_ABS_RD()\t\t\t\t\\\n\tGET_2BYTE_ARG;\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\\\n\tGET_MEMORY16((dbank << 16) + arg, arg, 0); \\\n\tINC_KPC_3;\n#endif\n\n#undef GET_ABS_RD_RMW\n\n#define GET_ABS_RD_RMW()\t\t\t\\\n\tGET_ABS_RD();\t\t\t\t\\\n\tGET_MEM_RMW();\n\n#undef GET_LONG_RD\n\n#ifdef ACC8\n# define GET_LONG_RD()\t\t\t\t\\\n\tGET_3BYTE_ARG;\t\t\t\t\\\n\tCYCLES_PLUS_2;\t\t\t\t\\\n\tGET_MEMORY8(arg, arg);\t\t\t\\\n\tINC_KPC_4;\n#else\n# define GET_LONG_RD()\t\t\t\t\\\n\tGET_3BYTE_ARG;\t\t\t\t\\\n\tCYCLES_PLUS_2;\t\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\t\t\\\n\tINC_KPC_4;\n#endif\n\n#undef GET_DLOC_IND_Y_RD\n#undef GET_DLOC_IND_Y_ADDR\n\n#ifdef ACC8\n# define GET_DLOC_IND_Y_RD()\t\t\t\\\n\tGET_DLOC_IND_Y_ADDR(0)\t\t\t\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DLOC_IND_Y_RD()\t\t\t\t\t\t\\\n\tGET_DLOC_IND_Y_ADDR(0)\t\t\t\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#define GET_DLOC_IND_Y_ADDR(is_write)\t\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tGET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, tmp1, 0);\t\\\n\ttmp1 += (dbank << 16);\t\t\t\t\t\t\\\n\targ = (tmp1 + yreg) & 0xffffff;\t\t\t\t\t\\\n\ttmp2 = (tmp1 & 0xffff00) | (arg & 0xff);\t\t\t\\\n\tif((psr & 0x10) && ((arg != tmp2) | is_write)) {\t\t\\\n\t\tGET_MEMORY8(tmp2, tmp1);\t\t\t\t\\\n\t} else if(((psr & 0x10) == 0) | (arg != tmp2) | is_write) {\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tINC_KPC_2;\n\n#undef GET_DLOC_IND_RD\n\n#ifdef ACC8\n# define GET_DLOC_IND_RD()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\\\n\t}\t\t\t\t\t\t\\\n\tGET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg, 0);\t\\\n\tGET_MEMORY8((dbank << 16) + arg, arg);\n#else\n# define GET_DLOC_IND_RD()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\\\n\t}\t\t\t\t\t\t\\\n\tGET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg, 0);\t\\\n\tGET_MEMORY16((dbank << 16) + arg, arg, 0);\n#endif\n\n#undef GET_DLOC_X_RD\n\n#ifdef ACC8\n# define GET_DLOC_X_RD()\t\t\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\t\t\t\\\n\targ = (arg + xreg + direct) & 0xffff;\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\t\\\n\t\tif((direct & 0xff) == 0) {\t\t\t\t\\\n\t\t\targ = (direct & 0xff00) | (arg & 0xff);\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DLOC_X_RD()\t\t\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\t\t\t\\\n\targ = (arg + xreg + direct) & 0xffff;\t\t\t\t\\\n\tif(!IS_ACC16 && (psr & 0x100)) {\t\t\t\t\\\n\t\tif((direct & 0xff) == 0) {\t\t\t\t\\\n\t\t\targ = (direct & 0xff00) | (arg & 0xff);\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tGET_MEMORY16(arg, arg, 1);\n#endif\n\n#undef GET_DLOC_X_RD_RMW\n\n#define GET_DLOC_X_RD_RMW()\t\t\t\t\t\t\\\n\tGET_DLOC_X_RD();\t\t\t\t\t\t\\\n\tGET_MEM_RMW();\n\n\n#undef GET_DISP8_S_IND_Y_RD\n\n#ifdef ACC8\n# define GET_DISP8_S_IND_Y_RD()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\\\n\tGET_DISP8_S_IND_Y_WR();\t\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DISP8_S_IND_Y_RD()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\\\n\tGET_DISP8_S_IND_Y_WR();\t\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#undef GET_DLOC_L_IND_Y_RD\n\n#ifdef ACC8\n# define GET_DLOC_L_IND_Y_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DLOC_L_IND_Y_WR();\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DLOC_L_IND_Y_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DLOC_L_IND_Y_WR();\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#undef GET_ABS_INDEX_ADDR\n\n#define GET_ABS_INDEX_ADDR(index_reg, is_write)\t\t\t\\\n\tGET_2BYTE_ARG;\t\t\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\t\t\\\n\tINC_KPC_3;\t\t\t\t\t\t\\\n\ttmp1 = (dbank << 16) + arg;\t\t\t\t\\\n\targ = tmp1 + index_reg;\t\t\t\t\t\\\n\ttmp1 = (tmp1 & 0xffff00) + (arg & 0xff);\t\t\\\n\tif((psr & 0x10) && ((tmp1 != arg) | is_write)) {\t\\\n\t\tGET_MEMORY8(tmp1, tmp2);\t\t\t\\\n\t} else if(((psr & 0x10) == 0) | (tmp1 != arg) | is_write) {\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\t\\\n\t}\n\n#undef GET_ABS_Y_RD\n\n#ifdef ACC8\n# define GET_ABS_Y_RD()\t\t\t\t\\\n\tGET_ABS_INDEX_ADDR(yreg, 0);\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_ABS_Y_RD()\t\t\t\t\\\n\tGET_ABS_INDEX_ADDR(yreg, 0);\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#undef GET_ABS_X_RD\n#undef GET_ABS_X_RD_RMW\n\n#ifdef ACC8\n# define GET_ABS_X_RD()\t\t\t\t\t\\\n\tGET_ABS_INDEX_ADDR(xreg, 0);\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#define GET_ABS_X_RD_RMW()\t\t\t\t\\\n\tGET_ABS_INDEX_ADDR(xreg, 1);\t\t\t\\\n\tGET_MEMORY8(arg, arg);\t\t\t\t\\\n\tGET_MEM_RMW();\n#else\n# define GET_ABS_X_RD()\t\t\t\t\t\\\n\tGET_ABS_INDEX_ADDR(xreg, 0);\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#define GET_ABS_X_RD_RMW()\t\t\t\t\\\n\tGET_ABS_INDEX_ADDR(xreg, 1);\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\t\t\t\\\n\tGET_MEM_RMW();\n#endif\n\n#undef GET_LONG_X_RD\n\n#ifdef ACC8\n# define GET_LONG_X_RD()\t\t\t\\\n\tGET_3BYTE_ARG;\t\t\t\t\\\n\targ = (arg + xreg) & 0xffffff;\t\t\\\n\tINC_KPC_4;\t\t\t\t\\\n\tCYCLES_PLUS_2;\t\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_LONG_X_RD()\t\t\t\\\n\tGET_3BYTE_ARG;\t\t\t\t\\\n\targ = (arg + xreg) & 0xffffff;\t\t\\\n\tINC_KPC_4;\t\t\t\t\\\n\tCYCLES_PLUS_2;\t\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#define SET_NEG_ZERO16(val)\t\\\n\tzero = val;\t\t\\\n\tneg7 = (val) >> 8;\n\n#define SET_NEG_ZERO8(val)\t\\\n\tzero = val;\t\t\\\n\tneg7 = val;\n\n#define SET_CARRY8(val)\t\t\\\n\tpsr = (psr & ~1) + (((val) >> 8) & 1);\n\n#define SET_CARRY16(val)\t\\\n\tpsr = (psr & ~1) + (((val) >> 16) & 1);\n\n#define SET_INDEX_REG(src, dest)\t\t\\\n\tif(psr & 0x10) {\t\t\t\\\n\t\tdest = (src) & 0xff;\t\t\\\n\t\tSET_NEG_ZERO8(dest);\t\t\\\n\t} else {\t\t\t\t\\\n\t\tdest = (src) & 0xffff;\t\t\\\n\t\tSET_NEG_ZERO16(dest);\t\t\\\n\t}\n\n#define LD_INDEX_INST(index_reg, in_bank)\t\\\n\tif(psr & 0x10) {\t\t\t\\\n\t\tGET_MEMORY8(arg, arg);\t\t\\\n\t} else {\t\t\t\t\\\n\t\tGET_MEMORY16(arg, arg, in_bank);\\\n\t}\t\t\t\t\t\\\n\tSET_INDEX_REG(arg, index_reg);\n\n#define LDX_INST(in_bank)\tLD_INDEX_INST(xreg, in_bank)\n#define LDY_INST(in_bank)\tLD_INDEX_INST(yreg, in_bank)\n\n#undef ORA_INST\n\n#ifdef ACC8\n# define ORA_INST()\t\t\t\t\\\n\ttmp1 = (acc | arg) & 0xff;\t\t\\\n\tacc = (acc & 0xff00) + tmp1;\t\t\\\n\tSET_NEG_ZERO8(tmp1);\n#else\n# define ORA_INST()\t\t\t\t\\\n\tacc = (acc | arg);\t\t\t\\\n\tSET_NEG_ZERO16(acc);\n#endif\n\n#undef AND_INST\n\n#ifdef ACC8\n# define AND_INST()\t\t\t\t\\\n\ttmp1 = (acc & arg) & 0xff;\t\t\\\n\tacc = (acc & 0xff00) + tmp1;\t\t\\\n\tSET_NEG_ZERO8(tmp1);\n#else\n# define AND_INST()\t\t\t\t\\\n\tacc = (acc & arg);\t\t\t\\\n\tSET_NEG_ZERO16(acc);\n#endif\n\n#undef EOR_INST\n\n#ifdef ACC8\n# define EOR_INST()\t\t\t\t\\\n\ttmp1 = (acc ^ arg) & 0xff;\t\t\\\n\tacc = (acc & 0xff00) + tmp1;\t\t\\\n\tSET_NEG_ZERO8(tmp1);\n#else\n# define EOR_INST()\t\t\t\t\\\n\tacc = (acc ^ arg);\t\t\t\\\n\tSET_NEG_ZERO16(acc);\n#endif\n\n#undef LDA_INST\n\n#ifdef ACC8\n# define LDA_INST()\t\t\t\t\\\n\tacc = (acc & 0xff00) + (arg & 0xff);\t\\\n\tSET_NEG_ZERO8(arg & 0xff);\n#else\n# define LDA_INST()\t\t\t\t\\\n\tacc = (arg & 0xffff);\t\t\t\\\n\tSET_NEG_ZERO16(acc);\n#endif\n\n#undef ADC_INST\n\n#ifdef ACC8\n# define ADC_INST()\t\t\t\t\t\t\\\n\ttmp1 = do_adc_sbc8(acc & 0xff, arg & 0xff, psr, 0);\t\\\n\tacc = (acc & 0xff00) + (tmp1 & 0xff);\t\t\t\\\n\tpsr = (tmp1 >> 16);\t\t\t\t\t\\\n\tzero = !(psr & 0x2);\t\t\t\t\t\\\n\tneg7 = psr;\n#else\n# define ADC_INST()\t\t\t\t\t\t\\\n\ttmp1 = do_adc_sbc16(acc, arg & 0xffff, psr, 0);\t\t\\\n\tacc = (tmp1 & 0xffff);\t\t\t\t\t\\\n\tpsr = (tmp1 >> 16);\t\t\t\t\t\\\n\tzero = !(psr & 0x2);\t\t\t\t\t\\\n\tneg7 = psr;\n#endif\n\n#undef SBC_INST\n\n#ifdef ACC8\n# define SBC_INST()\t\t\t\t\t\t\\\n\ttmp1 = do_adc_sbc8(acc & 0xff, arg & 0xff, psr, 1);\t\\\n\tacc = (acc & 0xff00) + (tmp1 & 0xff);\t\t\t\\\n\tpsr = (tmp1 >> 16);\t\t\t\t\t\\\n\tzero = !(psr & 0x2);\t\t\t\t\t\\\n\tneg7 = psr;\n#else\n# define SBC_INST()\t\t\t\t\t\t\\\n\ttmp1 = do_adc_sbc16(acc, arg & 0xffff, psr, 1);\t\t\\\n\tacc = (tmp1 & 0xffff);\t\t\t\t\t\\\n\tpsr = (tmp1 >> 16);\t\t\t\t\t\\\n\tzero = !(psr & 0x2);\t\t\t\t\t\\\n\tneg7 = psr;\n#endif\n\n\n#undef CMP_INST\n\n#ifdef ACC8\n# define CMP_INST()\t\t\t\t\t\\\n\targ = (acc & 0xff) + (0x100 - arg);\t\t\\\n\tSET_CARRY8(arg);\t\t\t\t\\\n\targ = arg & 0xff;\t\t\t\t\\\n\tSET_NEG_ZERO8(arg & 0xff);\n#else\n# define CMP_INST()\t\t\t\t\t\\\n\targ = (acc & 0xffff) + (0x10000 - arg);\t\t\\\n\tSET_CARRY16(arg);\t\t\t\t\\\n\targ = arg & 0xffff;\t\t\t\t\\\n\tSET_NEG_ZERO16(arg & 0xffff);\n#endif\n\n#undef BIT_INST\n\n#ifdef ACC8\n# define BIT_INST()\t\t\t\t\\\n\tneg7 = arg;\t\t\t\t\\\n\tzero = (acc & arg & 0xff);\t\t\\\n\tpsr = (psr & (~0x40)) | (arg & 0x40);\n#else\n# define BIT_INST()\t\t\t\t\\\n\tneg7 = arg >> 8;\t\t\t\\\n\tzero = (acc & arg & 0xffff);\t\t\\\n\tpsr = (psr & (~0x40)) | ((arg >> 8) & 0x40);\n#endif\n\n#undef STA_INST\n\n#ifdef ACC8\n# define STA_INST(in_bank)\t\t\t\\\n\tSET_MEMORY8(arg, acc);\n#else\n# define STA_INST(in_bank)\t\t\t\\\n\tSET_MEMORY16(arg, acc, in_bank);\n#endif\n\n#undef TSB_INST\n\n#ifdef ACC8\n# define TSB_INST(in_bank)\t\t\t\\\n\targ = arg & 0xff;\t\t\t\\\n\ttmp1 = arg | acc;\t\t\t\\\n\tzero = arg & acc;\t\t\t\\\n\tSET_MEMORY8(addr_latch, tmp1);\n#else\n# define TSB_INST(in_bank)\t\t\t\\\n\ttmp1 = arg | acc;\t\t\t\\\n\tzero = arg & acc;\t\t\t\\\n\tSET_MEMORY16(addr_latch, tmp1, in_bank);\n#endif\n\n#undef ASL_INST\n\n#ifdef ACC8\n# define ASL_INST(in_bank)\t\t\t\\\n\tpsr = (psr & 0x1fe) + ((arg >> 7) & 1);\t\\\n\ttmp1 = (arg << 1) & 0xff;\t\t\\\n\tSET_NEG_ZERO8(tmp1);\t\t\t\\\n\tSET_MEMORY8(addr_latch, tmp1);\n#else\n# define ASL_INST(in_bank)\t\t\t\\\n\tpsr = (psr & 0x1fe) + ((arg >> 15) & 1);\\\n\ttmp1 = (arg << 1) & 0xffff;\t\t\\\n\tSET_NEG_ZERO16(tmp1);\t\t\t\\\n\tSET_MEMORY16(addr_latch, tmp1, in_bank);\n#endif\n\n#undef ROL_INST\n\n#ifdef ACC8\n# define ROL_INST(in_bank)\t\t\t\\\n\targ = arg & 0xff;\t\t\t\\\n\targ = (arg << 1) | (psr & 1);\t\t\\\n\tSET_MEMORY8(addr_latch, arg);\t\t\\\n\tSET_NEG_ZERO8(arg & 0xff);\t\t\\\n\tSET_CARRY8(arg);\n#else\n# define ROL_INST(in_bank)\t\t\t\\\n\targ = (arg << 1) | (psr & 1);\t\t\\\n\tSET_MEMORY16(addr_latch, arg, in_bank);\t\\\n\tSET_NEG_ZERO16(arg & 0xffff);\t\t\\\n\tSET_CARRY16(arg);\n#endif\n\n#undef LSR_INST\n\n#ifdef ACC8\n# define LSR_INST(in_bank)\t\t\t\t\\\n\tSET_CARRY8(arg << 8);\t\t\t\t\\\n\targ = (arg >> 1) & 0x7f;\t\t\t\\\n\tSET_NEG_ZERO8(arg);\t\t\t\t\\\n\tSET_MEMORY8(addr_latch, arg);\n#else\n# define LSR_INST(in_bank)\t\t\t\t\\\n\tSET_CARRY16(arg << 16);\t\t\t\t\\\n\targ = (arg >> 1) & 0x7fff;\t\t\t\\\n\tSET_NEG_ZERO16(arg);\t\t\t\t\\\n\tSET_MEMORY16(addr_latch, arg, in_bank);\n#endif\n\n#undef ROR_INST\n\n#ifdef ACC8\n# define ROR_INST(in_bank)\t\t\t\t\\\n\ttmp1 = psr & 1;\t\t\t\t\t\\\n\tSET_CARRY8(arg << 8);\t\t\t\t\\\n\targ = ((arg >> 1) & 0x7f) | (tmp1 << 7);\t\\\n\tSET_MEMORY8(addr_latch, arg);\t\t\t\\\n\tSET_NEG_ZERO8(arg);\n#else\n# define ROR_INST(in_bank)\t\t\t\t\\\n\ttmp1 = psr & 1;\t\t\t\t\t\\\n\tSET_CARRY16(arg << 16);\t\t\t\t\\\n\targ = ((arg >> 1) & 0x7fff) | (tmp1 << 15);\t\\\n\tSET_MEMORY16(addr_latch, arg, in_bank);\t\t\\\n\tSET_NEG_ZERO16(arg);\n#endif\n\n#undef TRB_INST\n\n#ifdef ACC8\n# define TRB_INST(in_bank)\t\t\t\\\n\targ = arg & 0xff;\t\t\t\\\n\ttmp1 = arg & ~acc;\t\t\t\\\n\tzero = arg & acc;\t\t\t\\\n\tSET_MEMORY8(addr_latch, tmp1);\n#else\n# define TRB_INST(in_bank)\t\t\t\\\n\ttmp1 = arg & ~acc;\t\t\t\\\n\tzero = arg & acc;\t\t\t\\\n\tSET_MEMORY16(addr_latch, tmp1, in_bank);\n#endif\n\n#undef DEC_INST\n\n#ifdef ACC8\n# define DEC_INST(in_bank)\t\t\t\\\n\targ = (arg - 1) & 0xff;\t\t\t\\\n\tSET_MEMORY8(addr_latch, arg);\t\t\\\n\tSET_NEG_ZERO8(arg);\n#else\n# define DEC_INST(in_bank)\t\t\t\\\n\targ = (arg - 1) & 0xffff;\t\t\\\n\tSET_MEMORY16(addr_latch, arg, in_bank);\t\\\n\tSET_NEG_ZERO16(arg);\n#endif\n\n#undef INC_INST\n\n#ifdef ACC8\n# define INC_INST(in_bank)\t\t\t\\\n\targ = (arg + 1) & 0xff;\t\t\t\\\n\tSET_MEMORY8(addr_latch, arg);\t\t\\\n\tSET_NEG_ZERO8(arg);\n#else\n# define INC_INST(in_bank)\t\t\t\\\n\targ = (arg + 1) & 0xffff;\t\t\\\n\tSET_MEMORY16(addr_latch, arg, in_bank);\t\\\n\tSET_NEG_ZERO16(arg);\n#endif\n\n#undef STZ_INST\n\n#ifdef ACC8\n# define STZ_INST(in_bank)\t\t\t\t\\\n\tSET_MEMORY8(arg, 0);\n#else\n# define STZ_INST(in_bank)\t\t\t\t\\\n\tSET_MEMORY16(arg, 0, in_bank);\n#endif\n\n#undef BRANCH_DISP8\n\n#define BRANCH_DISP8(cond)\t\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\t\\\n\ttmp2 = kpc & 0xff0000;\t\t\t\t\t\\\n\tkpc += 2;\t\t\t\t\t\t\\\n\ttmp1 = kpc;\t\t\t\t\t\t\\\n\tif(cond) {\t\t\t\t\t\t\\\n\t\tkpc = kpc + arg - ((arg & 0x80) << 1);\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\t\\\n\t\tif((tmp1 ^ kpc) & psr & 0x100) {\t\t\\\n\t\t\tCYCLES_PLUS_1;\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\\\n\tkpc = tmp2 + (kpc & 0xffff);\n\n#undef STY_INST\n#undef STX_INST\n\n#define STY_INST(in_bank)\t\t\t\\\n\tif(psr & 0x10) {\t\t\t\\\n\t\tSET_MEMORY8(arg, yreg);\t\t\\\n\t} else {\t\t\t\t\\\n\t\tSET_MEMORY16(arg, yreg, in_bank);\\\n\t}\n#define STX_INST(in_bank)\t\t\t\\\n\tif(psr & 0x10) {\t\t\t\\\n\t\tSET_MEMORY8(arg, xreg);\t\t\\\n\t} else {\t\t\t\t\\\n\t\tSET_MEMORY16(arg, xreg, in_bank);\\\n\t}\n\n#define C_LDX_ABS_Y()\t\t\t\\\n\tGET_ABS_INDEX_ADDR(yreg, 0);\t\\\n\tLDX_INST(0);\n\n#define C_LDY_ABS_X()\t\t\t\\\n\tGET_ABS_INDEX_ADDR(xreg, 0);\t\\\n\tLDY_INST(0);\n\n#define C_LDX_ABS()\t\t\\\n\tGET_ABS_ADDR();\t\t\\\n\tLDX_INST(0);\n\n#define C_LDY_ABS()\t\t\\\n\tGET_ABS_ADDR();\t\t\\\n\tLDY_INST(0);\n\n#define C_LDX_DLOC()\t\t\\\n\tGET_DLOC_ADDR();\t\\\n\tLDX_INST(1);\n\n#define C_LDY_DLOC()\t\t\\\n\tGET_DLOC_ADDR();\t\\\n\tLDY_INST(1);\n\n#define C_LDY_DLOC_X()\t\t\\\n\tGET_DLOC_X_ADDR();\t\\\n\tLDY_INST(1);\n\n#define C_LDX_DLOC_Y()\t\t\\\n\tGET_DLOC_Y_ADDR();\t\\\n\tLDX_INST(1);\n\n#define CP_INDEX_VAL(index_reg)\t\\\n\targ = 0x100 - arg + index_reg;\t\\\n\tif((psr & 0x10) == 0) {\t\t\\\n\t\targ += 0xff00;\t\t\\\n\t\tSET_NEG_ZERO16(arg & 0xffff);\t\\\n\t\tSET_CARRY16(arg);\t\\\n\t} else {\t\t\t\\\n\t\tSET_NEG_ZERO8(arg & 0xff);\\\n\t\tSET_CARRY8(arg);\t\\\n\t}\n\n#define CP_INDEX_LOAD(index_reg, in_bank)\t\\\n\tif((psr & 0x10) != 0) {\t\t\t\\\n\t\tGET_MEMORY8(arg, arg);\t\t\\\n\t} else {\t\t\t\t\\\n\t\tGET_MEMORY16(arg, arg, in_bank);\\\n\t}\t\t\t\t\t\\\n\tCP_INDEX_VAL(index_reg)\n\n#define CPX_INST(in_bank)\t\t\\\n\tCP_INDEX_LOAD(xreg, in_bank);\n\n#define CPY_INST(in_bank)\t\t\\\n\tCP_INDEX_LOAD(yreg, in_bank);\n\n#define C_CPX_IMM()\t\t\\\n\tINC_KPC_2;\t\t\\\n\tif((psr & 0x10) == 0) {\t\\\n\t\tGET_2BYTE_ARG;\t\\\n\t\tCYCLES_PLUS_1;\t\\\n\t\tINC_KPC_1;\t\\\n\t} else {\t\t\\\n\t\tGET_1BYTE_ARG;\t\\\n\t}\t\t\t\\\n\tCP_INDEX_VAL(xreg);\n\n#define C_CPY_IMM()\t\t\\\n\tINC_KPC_2;\t\t\\\n\tif((psr & 0x10) == 0) {\t\\\n\t\tGET_2BYTE_ARG;\t\\\n\t\tCYCLES_PLUS_1;\t\\\n\t\tINC_KPC_1;\t\\\n\t} else {\t\t\\\n\t\tGET_1BYTE_ARG;\t\\\n\t}\t\t\t\\\n\tCP_INDEX_VAL(yreg);\n\n#define C_CPX_DLOC()\t\t\\\n\tGET_DLOC_ADDR();\t\\\n\tCPX_INST(1);\n\n#define C_CPY_DLOC()\t\t\\\n\tGET_DLOC_ADDR();\t\\\n\tCPY_INST(1);\n\n#define C_CPX_ABS()\t\t\\\n\tGET_ABS_ADDR();\t\t\\\n\tCPX_INST(0);\n\n#define C_CPY_ABS()\t\t\\\n\tGET_ABS_ADDR();\t\t\\\n\tCPY_INST(0);\n\n"
  },
  {
    "path": "gsplus/src/dependency",
    "content": "adb.o: adb.c defc.h defcomm.h iwm.h protos.h protos_base.h\nengine_c.o: engine_c.c defc.h defcomm.h iwm.h protos.h protos_base.h size_c.h op_routs.h engine.h defs_instr.h instable.h\nclock.o: clock.c defc.h defcomm.h iwm.h protos.h protos_base.h\ncompile_time.o: compile_time.c\nconfig.o: config.c defc.h defcomm.h iwm.h protos.h protos_base.h config.h win_dirent.h\ndebugger.o: debugger.c defc.h defcomm.h iwm.h protos.h protos_base.h disas.h\nscc.o: scc.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h\nscc_socket_driver.o: scc_socket_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h\nscc_windriver.o: scc_windriver.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h\nscc_unixdriver.o: scc_unixdriver.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h\niwm.o: iwm.c defc.h defcomm.h iwm.h protos.h protos_base.h\njoystick_driver.o: joystick_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h\nmoremem.o: moremem.c defc.h defcomm.h iwm.h protos.h protos_base.h\npaddles.o: paddles.c defc.h defcomm.h iwm.h protos.h protos_base.h\nmockingboard.o: mockingboard.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h\nsim65816.o: sim65816.c defc.h defcomm.h iwm.h protos.h protos_base.h\nsmartport.o: smartport.c defc.h defcomm.h iwm.h protos.h protos_base.h\ndoc.o: doc.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h\nsound.o: sound.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h\nsound_driver.o: sound_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h\nwoz.o: woz.c defc.h defcomm.h iwm.h protos.h protos_base.h\nunshk.o: unshk.c defc.h defcomm.h iwm.h protos.h protos_base.h\nundeflate.o: undeflate.c defc.h defcomm.h iwm.h protos.h protos_base.h\ndynapro.o: dynapro.c defc.h defcomm.h iwm.h protos.h protos_base.h win_dirent.h\ndyna_type.o: dyna_type.c defc.h defcomm.h iwm.h protos.h protos_base.h\ndyna_filt.o: dyna_filt.c defc.h defcomm.h iwm.h protos.h protos_base.h\ndyna_validate.o: dyna_validate.c defc.h defcomm.h iwm.h protos.h protos_base.h\napplesingle.o: applesingle.c defc.h defcomm.h iwm.h protos.h protos_base.h\nvideo.o: video.c defc.h defcomm.h iwm.h protos.h protos_base.h kegsfont.h\nvoc.o: voc.c defc.h defcomm.h iwm.h protos.h protos_base.h\nmacsnd_driver.o: macsnd_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h\n"
  },
  {
    "path": "gsplus/src/disas.h",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2020 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\nenum {\n\tABS = 1,\n\tABSX,\n\tABSY,\n\tABSLONG,\n\tABSIND,\n\tABSXIND,\n\tIMPLY,\n\tACCUM,\n\tIMMED,\n\tJUST8,\n\tDLOC,\n\tDLOCX,\n\tDLOCY,\n\tLONG,\n\tLONGX,\n\tDLOCIND,\n\tDLOCINDY,\n\tDLOCXIND,\n\tDLOCBRAK,\n\tDLOCBRAKY,\n\tDISP8,\n\tDISP8S,\n\tDISP8SINDY,\n\tDISP16,\n\tMVPMVN,\n\tREPVAL,\n\tSEPVAL\n};\n\n\nconst char * const disas_opcodes[256] = {\n\t\"BRK\", \"ORA\", \"COP\", \"ORA\", \"TSB\", \"ORA\", \"ASL\", \"ORA\",  /* 00-07 */\n\t\"PHP\", \"ORA\", \"ASL\", \"PHD\", \"TSB\", \"ORA\", \"ASL\", \"ORA\",  /* 08-0f */\n\t\"BPL\", \"ORA\", \"ORA\", \"ORA\", \"TRB\", \"ORA\", \"ASL\", \"ORA\",  /* 10-17 */\n\t\"CLC\", \"ORA\", \"INC\", \"TCS\", \"TRB\", \"ORA\", \"ASL\", \"ORA\",  /* 18-1f */\n\t\"JSR\", \"AND\", \"JSL\", \"AND\", \"BIT\", \"AND\", \"ROL\", \"AND\",  /* 20-27 */\n\t\"PLP\", \"AND\", \"ROL\", \"PLD\", \"BIT\", \"AND\", \"ROL\", \"AND\",  /* 28-2f */\n\t\"BMI\", \"AND\", \"AND\", \"AND\", \"BIT\", \"AND\", \"ROL\", \"AND\",  /* 30-37 */\n\t\"SEC\", \"AND\", \"DEC\", \"TSC\", \"BIT\", \"AND\", \"ROL\", \"AND\",  /* 38-3f */\n\t\"RTI\", \"EOR\", \"WDM\", \"EOR\", \"MVP\", \"EOR\", \"LSR\", \"EOR\",  /* 40-47 */\n\t\"PHA\", \"EOR\", \"LSR\", \"PHK\", \"JMP\", \"EOR\", \"LSR\", \"EOR\",  /* 48-4f */\n\t\"BVC\", \"EOR\", \"EOR\", \"EOR\", \"MVN\", \"EOR\", \"LSR\", \"EOR\",  /* 50-57 */\n\t\"CLI\", \"EOR\", \"PHY\", \"TCD\", \"JMP\", \"EOR\", \"LSR\", \"EOR\",  /* 58-5f */\n\t\"RTS\", \"ADC\", \"PER\", \"ADC\", \"STZ\", \"ADC\", \"ROR\", \"ADC\",  /* 60-67 */\n\t\"PLA\", \"ADC\", \"ROR\", \"RTL\", \"JMP\", \"ADC\", \"ROR\", \"ADC\",  /* 68-6f */\n\t\"BVS\", \"ADC\", \"ADC\", \"ADC\", \"STZ\", \"ADC\", \"ROR\", \"ADC\",  /* 70-77 */\n\t\"SEI\", \"ADC\", \"PLY\", \"TDC\", \"JMP\", \"ADC\", \"ROR\", \"ADC\",  /* 78-7f */\n\t\"BRA\", \"STA\", \"BRL\", \"STA\", \"STY\", \"STA\", \"STX\", \"STA\",  /* 80-87 */\n\t\"DEY\", \"BIT\", \"TXA\", \"PHB\", \"STY\", \"STA\", \"STX\", \"STA\",  /* 88-8f */\n\t\"BCC\", \"STA\", \"STA\", \"STA\", \"STY\", \"STA\", \"STX\", \"STA\",  /* 90-97 */\n\t\"TYA\", \"STA\", \"TXS\", \"TXY\", \"STZ\", \"STA\", \"STZ\", \"STA\",  /* 98-9f */\n\t\"LDY\", \"LDA\", \"LDX\", \"LDA\", \"LDY\", \"LDA\", \"LDX\", \"LDA\",  /* a0-a7 */\n\t\"TAY\", \"LDA\", \"TAX\", \"PLB\", \"LDY\", \"LDA\", \"LDX\", \"LDA\",  /* a8-af */\n\t\"BCS\", \"LDA\", \"LDA\", \"LDA\", \"LDY\", \"LDA\", \"LDX\", \"LDA\",  /* b0-b7 */\n\t\"CLV\", \"LDA\", \"TSX\", \"TYX\", \"LDY\", \"LDA\", \"LDX\", \"LDA\",  /* b8-bf */\n\t\"CPY\", \"CMP\", \"REP\", \"CMP\", \"CPY\", \"CMP\", \"DEC\", \"CMP\",  /* c0-c7 */\n\t\"INY\", \"CMP\", \"DEX\", \"WAI\", \"CPY\", \"CMP\", \"DEC\", \"CMP\",  /* c8-cf */\n\t\"BNE\", \"CMP\", \"CMP\", \"CMP\", \"PEI\", \"CMP\", \"DEC\", \"CMP\",  /* d0-d7 */\n\t\"CLD\", \"CMP\", \"PHX\", \"STP\", \"JML\", \"CMP\", \"DEC\", \"CMP\",  /* d8-df */\n\t\"CPX\", \"SBC\", \"SEP\", \"SBC\", \"CPX\", \"SBC\", \"INC\", \"SBC\",  /* e0-e7 */\n\t\"INX\", \"SBC\", \"NOP\", \"XBA\", \"CPX\", \"SBC\", \"INC\", \"SBC\",  /* e8-ef */\n\t\"BEQ\", \"SBC\", \"SBC\", \"SBC\", \"PEA\", \"SBC\", \"INC\", \"SBC\",  /* f0-f7 */\n\t\"SED\", \"SBC\", \"PLX\", \"XCE\", \"JSR\", \"SBC\", \"INC\", \"SBC\",  /* f8-ff */\n};\n\n\nconst word32 disas_types[256] = {\n\tJUST8+0x100, DLOCXIND+0x100,\t\t/* 00-01 */\n\tJUST8+0x100, DISP8S+0x100,\t\t/* 02-03 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* 04-05 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* 06-07 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* 08-9 */\n\tACCUM+0x000, IMPLY+0x000,\t\t/* 0a-b */\n\tABS+0x200, ABS+0x200,\t\t\t/* c-d */\n\tABS+0x200, LONG+0x300,\t\t\t/* e-f */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* 10-11 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* 12-13 */\n\tDLOC+0x100, DLOCX+0x100,\t\t/* 14-15 */\n\tDLOCX+0x100, DLOCBRAKY+0x100,\t\t/* 16-17 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* 18-19 */\n\tACCUM+0x000, IMPLY+0x000,\t\t/* 1a-1b */\n\tABS+0x200, ABSX+0x200,\t\t\t/* 1c-1d */\n\tABSX+0x200, LONGX+0x300,\t\t/* 1e-1f */\n\tABS+0x200, DLOCXIND+0x100,\t\t/* 20-21 */\n\tABSLONG+0x300, DISP8S+0x100,\t\t/* 22-23 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* 24-25 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* 26-27 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* 28-29 */\n\tACCUM+0x000, IMPLY+0x000,\t\t/* 2a-2b */\n\tABS+0x200, ABS+0x200,\t\t\t/* 2c-2d */\n\tABS+0x200, LONG+0x300,\t\t\t/* 2e-2f */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* 30-31 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* 32-33 */\n\tDLOCX+0x100, DLOCX+0x100,\t\t/* 34-35 */\n\tDLOCX+0x100, DLOCBRAKY+0x100,\t\t/* 36-37 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* 38-39 */\n\tACCUM+0x000, IMPLY+0x000,\t\t/* 3a-3b */\n\tABSX+0x200, ABSX+0x200,\t\t\t/* 3c-3d */\n\tABSX+0x200, LONGX+0x300,\t\t/* 3e-3f */\n\tIMPLY+0x000, DLOCXIND+0x100,\t\t/* 40-41 */\n\tJUST8+0x100, DISP8S+0x100,\t\t/* 42-43 */\n\tMVPMVN+0x200, DLOC+0x100,\t\t/* 44-45 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* 46-47 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* 48-49 */\n\tACCUM+0x000, IMPLY+0x000,\t\t/* 4a-4b */\n\tABS+0x200, ABS+0x200,\t\t\t/* 4c-4d */\n\tABS+0x200, LONG+0x300,\t\t\t/* 4e-4f */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* 50-51 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* 52-53 */\n\tMVPMVN+0x200, DLOCX+0x100,\t\t/* 54-55 */\n\tDLOCX+0x100, DLOCBRAKY+0x100,\t\t/* 56-57 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* 58-59 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* 5a-5b */\n\tLONG+0x300, ABSX+0x200,\t\t\t/* 5c-5d */\n\tABSX+0x200, LONGX+0x300,\t\t/* 5e-5f */\n\tIMPLY+0x000, DLOCXIND+0x100,\t\t/* 60-61 */\n\tDISP16+0x200, DISP8S+0x100,\t\t/* 62-63 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* 64-65 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* 66-67 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* 68-69 */\n\tACCUM+0x000, IMPLY+0x000,\t\t/* 6a-6b */\n\tABSIND+0x200, ABS+0x200,\t\t/* 6c-6d */\n\tABS+0x200, LONG+0x300,\t\t\t/* 6e-6f */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* 70-71 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* 72-73 */\n\tDLOCX+0x100, DLOCX+0x100,\t\t/* 74-75 */\n\tDLOCX+0x100, DLOCBRAKY+0x100,\t\t/* 76-77 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* 78-79 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* 7a-7b */\n\tABSXIND+0x200, ABSX+0x200,\t\t/* 7c-7d */\n\tABSX+0x200, LONGX+0x300,\t\t/* 7e-7f */\n\tDISP8+0x100, DLOCXIND+0x100,\t\t/* 80-81 */\n\tDISP16+0x200, DISP8S+0x100,\t\t/* 82-83 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* 84-85 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* 86-87 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* 88-89 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* 8a-8b */\n\tABS+0x200, ABS+0x200,\t\t\t/* 8c-8d */\n\tABS+0x200, LONG+0x300,\t\t\t/* 8e-8f */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* 90-91 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* 92-93 */\n\tDLOCX+0x100, DLOCX+0x100,\t\t/* 94-95 */\n\tDLOCY+0x100, DLOCBRAKY+0x100,\t\t/* 96-97 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* 98-99 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* 9a-9b */\n\tABS+0x200, ABSX+0x200,\t\t\t/* 9c-9d */\n\tABSX+0x200, LONGX+0x300,\t\t/* 9e-9f */\n\tIMMED+0x500, DLOCXIND+0x100,\t\t/* a0-a1 */\n\tIMMED+0x500, DISP8S+0x100,\t\t/* a2-a3 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* a4-a5 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* a6-a7 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* a8-a9 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* aa-ab */\n\tABS+0x200, ABS+0x200,\t\t\t/* ac-ad */\n\tABS+0x200, LONG+0x300,\t\t\t/* ae-af */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* b0-b1 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* b2-b3 */\n\tDLOCX+0x100, DLOCX+0x100,\t\t/* b4-b5 */\n\tDLOCY+0x100, DLOCBRAKY+0x100,\t\t/* b6-b7 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* b8-b9 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* ba-bb */\n\tABSX+0x200, ABSX+0x200,\t\t\t/* bc-bd */\n\tABSY+0x200, LONGX+0x300,\t\t/* be-bf */\n\tIMMED+0x500, DLOCXIND+0x100,\t\t/* c0-c1 */\n\tREPVAL+0x100, DISP8S+0x100,\t\t/* c2-c3 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* c4-c5 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* c6-c7 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* c8-c9 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* ca-cb */\n\tABS+0x200, ABS+0x200,\t\t\t/* cc-cd */\n\tABS+0x200, LONG+0x300,\t\t\t/* ce-cf */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* d0-d1 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* d2-d3 */\n\tDLOC+0x100, DLOCX+0x100,\t\t/* d4-d5 */\n\tDLOCX+0x100, DLOCBRAKY+0x100,\t\t/* d6-d7 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* d8-d9 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* da-db */\n\tABSIND+0x200, ABSX+0x200,\t\t/* dc-dd */\n\tABSX+0x200, LONGX+0x300,\t\t/* de-df */\n\tIMMED+0x500, DLOCXIND+0x100,\t\t/* e0-e1 */\n\tSEPVAL+0x100, DISP8S+0x100,\t\t/* e2-e3 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* e4-e5 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* e6-e7 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* e8-e9 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* ea-eb */\n\tABS+0x200, ABS+0x200,\t\t\t/* ec-ed */\n\tABS+0x200, LONG+0x300,\t\t\t/* ee-ef */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* f0-f1 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* f2-f3 */\n\tIMMED+0x200, DLOCX+0x100,\t\t/* f4-f5 */\n\tDLOCX+0x100, DLOCBRAKY+0x100,\t\t/* f6-f7 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* f8-f9 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* fa-fb */\n\tABSXIND+0x200, ABSX+0x200,\t\t/* fc-fd */\n\tABSX+0x200, LONGX+0x300,\t\t/* fe-ff */\n};\n\n"
  },
  {
    "path": "gsplus/src/doc.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// Ensoniq 5503 DOC routines\n// The Ensoniq DOC is controlled through the Sound GLU at $C03C-$C03F,\n//  where $C03E-$C03F are a 16-bit pointer to \"what\" to access, $C03D is\n//  the data register, and $C03C is a control register with volume.\n// The DOC operates at 894.886KHZ (7M clock divided by 8).  It visits each\n//  oscillator (up to 32) once per clock, plus 2 extra clocks for DOC RAM\n//  refresh.\n// KEGS cheats and pretends the DOC runs at 48KHz, and visits all oscillators\n//  each \"sample\".  KEGS adjusts the internal accumulators so the right\n//  frequency is achieved.  This allows the sample calculations to be\n//  greatly simplified, and achieves higher fidelity when all 32 osc are\n//  enabled (which is generally how most Apple IIgs code works).\n\n#include \"defc.h\"\n#include \"sound.h\"\n\n#define DOC_LOG(a,b,c,d)\n\nextern int Verbose;\nextern int g_use_shmem;\nextern word32 g_vbl_count;\nextern int g_preferred_rate;\n\nextern word32 g_c03ef_doc_ptr;\n\nextern dword64 g_last_vbl_dfcyc;\n\nbyte doc_ram[0x10000 + 16];\n\nword32 g_doc_sound_ctl = 0;\nword32 g_doc_saved_val = 0;\nint\tg_doc_num_osc_en = 1;\ndouble\tg_drecip_osc_en_plus_2 = 1.0 / (double)(1 + 2);\n\nint\tg_doc_vol = 8;\nint\tg_doc_saved_ctl = 0;\nint\tg_num_osc_interrupting = 0;\nDoc_reg g_doc_regs[32];\n\nword32 doc_reg_e0 = 0xff;\n\nextern double g_drecip_audio_rate;\nextern double g_dsamps_per_dfcyc;\nextern double g_fcyc_per_samp;\n\n/* local function prototypes */\nvoid doc_write_ctl_reg(dword64 dfcyc, int osc, int val);\n\n#define DOC_SCAN_RATE\t(DCYCS_28_MHZ/32.0)\n\n#define UPDATE_G_DCYCS_PER_DOC_UPDATE(osc_en)\t\t\t\t\\\n\tg_drecip_osc_en_plus_2 = 1.0 / (double)(osc_en + 2);\n\n#define SND_PTR_SHIFT\t\t14\n#define SND_PTR_SHIFT_DBL\t((double)(1 << SND_PTR_SHIFT))\n\nvoid\ndoc_init()\n{\n\tDoc_reg\t*rptr;\n\tint\ti;\n\n\tfor(i = 0; i < 32; i++) {\n\t\trptr = &(g_doc_regs[i]);\n\t\trptr->dsamp_ev = 0.0;\n\t\trptr->dsamp_ev2 = 0.0;\n\t\trptr->complete_dsamp = 0.0;\n\t\trptr->samps_left = 0;\n\t\trptr->cur_acc = 0;\n\t\trptr->cur_inc = 0;\n\t\trptr->cur_start = 0;\n\t\trptr->cur_end = 0;\n\t\trptr->cur_mask = 0;\n\t\trptr->size_bytes = 0;\n\t\trptr->event = 0;\n\t\trptr->running = 0;\n\t\trptr->has_irq_pending = 0;\n\t\trptr->freq = 0;\n\t\trptr->vol = 0;\n\t\trptr->waveptr = 0;\n\t\trptr->ctl = 1;\n\t\trptr->wavesize = 0;\n\t\trptr->last_samp_val = 0;\n\t}\n}\nvoid\ndoc_reset(dword64 dfcyc)\n{\n\tint\ti;\n\n\tfor(i = 0; i < 32; i++) {\n\t\tdoc_write_ctl_reg(dfcyc, i, g_doc_regs[i].ctl | 1);\n\t\tdoc_reg_e0 = 0xff;\n\t\tif(g_doc_regs[i].has_irq_pending) {\n\t\t\thalt_printf(\"reset: has_irq[%02x] = %d\\n\", i,\n\t\t\t\tg_doc_regs[i].has_irq_pending);\n\t\t}\n\t\tg_doc_regs[i].has_irq_pending = 0;\n\t}\n\tif(g_num_osc_interrupting) {\n\t\thalt_printf(\"reset: num_osc_int:%d\\n\", g_num_osc_interrupting);\n\t}\n\tg_num_osc_interrupting = 0;\n\n\tg_doc_num_osc_en = 1;\n\tUPDATE_G_DCYCS_PER_DOC_UPDATE(1);\n}\n\nint\ndoc_play(dword64 dfcyc, double last_dsamp, double dsamp_now, int num_samps,\n\t\tint snd_buf_init, int *outptr_start)\n{\n\tDoc_reg *rptr;\n\tint\t*outptr;\n\tdouble\tcomplete_dsamp, cur_dsamp;\n\tword32\tcur_acc, cur_pos, cur_mask, cur_inc, cur_end;\n\tint\tval, val2, imul, off, ctl, num_osc_en;\n\tint\tsamps_left, samps_to_do, samp_offset, pos, osc, done;\n\tint\ti, j;\n\n\tnum_osc_en = g_doc_num_osc_en;\n\n\tdbg_log_info(dfcyc, num_samps, 0, 0xd0c0);\n\n\tdone = 0;\n\t// Do Ensoniq oscillators\n\twhile(!done) {\n\t\tdone = 1;\n\t\tfor(j = 0; j < num_osc_en; j++) {\n\t\t\tosc = j;\n\t\t\trptr = &(g_doc_regs[osc]);\n\t\t\tcomplete_dsamp = rptr->complete_dsamp;\n\t\t\tsamps_left = rptr->samps_left;\n\t\t\tcur_acc = rptr->cur_acc;\n\t\t\tcur_mask = rptr->cur_mask;\n\t\t\tcur_inc = rptr->cur_inc;\n\t\t\tcur_end = rptr->cur_end;\n\t\t\tif(!rptr->running || cur_inc == 0 ||\n\t\t\t\t\t\t(complete_dsamp >= dsamp_now)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tdone = 0;\n\t\t\tctl = rptr->ctl;\n\n\t\t\tsamp_offset = 0;\n\t\t\tif(complete_dsamp > last_dsamp) {\n\t\t\t\tsamp_offset = (int)(complete_dsamp- last_dsamp);\n\t\t\t\tif(samp_offset > num_samps) {\n\t\t\t\t\trptr->complete_dsamp = dsamp_now;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\toutptr = outptr_start + 2 * samp_offset;\n\t\t\tif(ctl & 0x10) {\n\t\t\t\t/* other channel */\n\t\t\t\toutptr += 1;\n\t\t\t}\n\n\t\t\timul = (rptr->vol * g_doc_vol);\n\t\t\toff = imul * 128;\n\n\t\t\tsamps_to_do = MY_MIN(samps_left,\n\t\t\t\t\t\tnum_samps - samp_offset);\n\t\t\tif(imul == 0 || samps_to_do == 0) {\n\t\t\t\t/* produce no sound */\n\t\t\t\tsamps_left = samps_left - samps_to_do;\n\t\t\t\tcur_acc += cur_inc * samps_to_do;\n\t\t\t\trptr->samps_left = samps_left;\n\t\t\t\trptr->cur_acc = cur_acc;\n\t\t\t\tcur_dsamp = last_dsamp +\n\t\t\t\t\t(double)(samps_to_do + samp_offset);\n\t\t\t\tDOC_LOG(\"nosnd\", osc, cur_dsamp, samps_to_do);\n\t\t\t\trptr->complete_dsamp = dsamp_now;\n\t\t\t\tcur_pos = rptr->cur_start+(cur_acc & cur_mask);\n\t\t\t\tif(samps_left <= 0) {\n\t\t\t\t\tdoc_sound_end(osc, 1, cur_dsamp,\n\t\t\t\t\t\t\t\tdsamp_now);\n\t\t\t\t\tval = 0;\n\t\t\t\t\tj--;\n\t\t\t\t} else {\n\t\t\t\t\tval = doc_ram[cur_pos >> SND_PTR_SHIFT];\n\t\t\t\t}\n\t\t\t\trptr->last_samp_val = val;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(snd_buf_init == 0) {\n\t\t\t\tmemset(outptr_start, 0,\n\t\t\t\t\t2*sizeof(outptr_start[0])*num_samps);\n\t\t\t\tsnd_buf_init++;\n\t\t\t}\n\t\t\tval = 0;\n\t\t\trptr->complete_dsamp = dsamp_now;\n\t\t\tcur_pos = rptr->cur_start + (cur_acc & cur_mask);\n\t\t\tpos = 0;\n\t\t\tfor(i = 0; i < samps_to_do; i++) {\n\t\t\t\tpos = cur_pos >> SND_PTR_SHIFT;\n\t\t\t\tcur_pos += cur_inc;\n\t\t\t\tcur_acc += cur_inc;\n\t\t\t\tval = doc_ram[pos];\n\t\t\t\tval2 = (val * imul - off) >> 4;\n\t\t\t\tif((val == 0) || (cur_pos >= cur_end)) {\n\t\t\t\t\tcur_dsamp = last_dsamp +\n\t\t\t\t\t\t(double)(samp_offset + i + 1);\n\t\t\t\t\trptr->cur_acc = cur_acc;\n\t\t\t\t\trptr->samps_left = 0;\n\t\t\t\t\tDOC_LOG(\"end or 0\", osc, cur_dsamp,\n\t\t\t\t\t\t((pos & 0xffffU) << 16) |\n\t\t\t\t\t\t((i &0xff) << 8) | val);\n\t\t\t\t\tdoc_sound_end(osc, val, cur_dsamp,\n\t\t\t\t\t\t\t\tdsamp_now);\n\t\t\t\t\tval = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tval2 = outptr[0] + val2;\n\t\t\t\tsamps_left--;\n\t\t\t\t*outptr = val2;\n\t\t\t\toutptr += 2;\n\t\t\t}\n\t\t\trptr->last_samp_val = val;\n\n\t\t\tif(val != 0) {\n\t\t\t\trptr->cur_acc = cur_acc;\n\t\t\t\trptr->samps_left = samps_left;\n\t\t\t\trptr->complete_dsamp = dsamp_now;\n\t\t\t}\n\t\t\tDOC_LOG(\"splayed\", osc, dsamp_now,\n\t\t\t\t(samps_to_do << 16) + (pos & 0xffff));\n\t\t}\n\t}\n\n\treturn snd_buf_init;\n}\nvoid\ndoc_handle_event(int osc, dword64 dfcyc)\n{\n\t/* handle osc stopping and maybe interrupting */\n\n\t//DOC_LOG(\"doc_ev\", osc, dsamps, 0);\n\n\tg_doc_regs[osc].event = 0;\n\n\tsound_play(dfcyc);\n}\n\nvoid\ndoc_sound_end(int osc, int can_repeat, double eff_dsamps, double dsamps)\n{\n\tDoc_reg\t*rptr, *orptr;\n\tint\tmode, omode;\n\tint\tother_osc;\n\tint\tone_shot_stop;\n\tint\tctl;\n\n\t/* handle osc stopping and maybe interrupting */\n\n\tif(osc < 0 || osc > 31) {\n\t\tprintf(\"doc_handle_event: osc: %d!\\n\", osc);\n\t\treturn;\n\t}\n\n\trptr = &(g_doc_regs[osc]);\n\tctl = rptr->ctl;\n\n\tif(rptr->event) {\n\t\tremove_event_doc(osc);\n\t}\n\trptr->event = 0;\n\trptr->cur_acc = 0;\t\t/* reset internal accumulator*/\n\n\t/* check to make sure osc is running */\n\tif(ctl & 0x01) {\n\t\t/* Oscillator already stopped. */\n\t\thalt_printf(\"Osc %d interrupt, but it was already stop!\\n\",osc);\n#ifdef HPUX\n\t\tU_STACK_TRACE();\n#endif\n\t\treturn;\n\t}\n\n\tif(ctl & 0x08) {\n\t\tif(rptr->has_irq_pending == 0) {\n\t\t\tdoc_add_sound_irq(osc);\n\t\t}\n\t}\n\n\tif(!rptr->running) {\n\t\thalt_printf(\"Doc event for osc %d, but ! running\\n\", osc);\n\t}\n\n\trptr->running = 0;\n\n\tmode = (ctl >> 1) & 3;\n\tother_osc = osc ^ 1;\n\torptr = &(g_doc_regs[other_osc]);\n\tomode = (orptr->ctl >> 1) & 3;\n\n\t/* If either this osc or it's partner is in swap mode, treat the */\n\t/*  pair as being in swap mode.  This Ensoniq feature pointed out */\n\t/*  by Ian Schmidt */\n\tif(mode == 0 && can_repeat) {\n\t\t/* free-running mode with no 0 byte! */\n\t\t/* start doing it again */\n\n\t\tdoc_start_sound(osc, eff_dsamps, dsamps);\n\n\t\treturn;\n\t} else if((mode == 3) || (omode == 3)) {\n\t\t/* swap mode (even if we're one_shot and partner is swap)! */\n\t\t/* unless we're one-shot and we hit a 0-byte--then */\n\t\t/* Olivier Goguel says just stop */\n\t\trptr->ctl |= 1;\n\t\tone_shot_stop = (mode == 1) && (!can_repeat);\n\t\tif(!one_shot_stop && !orptr->running &&\n\t\t\t\t\t\t\t(orptr->ctl & 0x1)) {\n\t\t\torptr->ctl = orptr->ctl & (~1);\n\t\t\tdoc_start_sound(other_osc, eff_dsamps, dsamps);\n\t\t}\n\t\treturn;\n\t} else {\n\t\t/* stop the oscillator */\n\t\trptr->ctl |= 1;\n\t}\n\n\treturn;\n}\n\nvoid\ndoc_add_sound_irq(int osc)\n{\n\tint\tnum_osc_interrupting;\n\n\tif(g_doc_regs[osc].has_irq_pending) {\n\t\thalt_printf(\"Adding sound_irq for %02x, but irq_p: %d\\n\", osc,\n\t\t\tg_doc_regs[osc].has_irq_pending);\n\t}\n\n\tnum_osc_interrupting = g_num_osc_interrupting + 1;\n\tg_doc_regs[osc].has_irq_pending = num_osc_interrupting;\n\tg_num_osc_interrupting = num_osc_interrupting;\n\n\tadd_irq(IRQ_PENDING_DOC);\n\tif(num_osc_interrupting == 1) {\n\t\tdoc_reg_e0 = 0x00 + (osc << 1);\n\t}\n\n\tDOC_LOG(\"add_irq\", osc, g_cur_dfcyc * g_dsamps_per_dfcyc, 0);\n}\n\nvoid\ndoc_remove_sound_irq(int osc, int must)\n{\n\tDoc_reg\t*rptr;\n\tint\tnum_osc_interrupt;\n\tint\thas_irq_pending;\n\tint\tfirst;\n\tint\ti;\n\n\tdoc_printf(\"remove irq for osc: %d, has_irq: %d\\n\",\n\t\tosc, g_doc_regs[osc].has_irq_pending);\n\n\tnum_osc_interrupt = g_doc_regs[osc].has_irq_pending;\n\tfirst = 0;\n\tif(num_osc_interrupt) {\n\t\tg_num_osc_interrupting--;\n\t\tg_doc_regs[osc].has_irq_pending = 0;\n\t\tDOC_LOG(\"rem_irq\", osc, g_cur_dfcyc * g_dsamps_per_dfcyc, 0);\n\t\tif(g_num_osc_interrupting == 0) {\n\t\t\tremove_irq(IRQ_PENDING_DOC);\n\t\t}\n\n\t\tfirst = 0x40 | (doc_reg_e0 >> 1);\n\t\t\t\t\t/* if none found, then def = no ints */\n\t\tfor(i = 0; i < g_doc_num_osc_en; i++) {\n\t\t\trptr = &(g_doc_regs[i]);\n\t\t\thas_irq_pending = rptr->has_irq_pending;\n\t\t\tif(has_irq_pending > num_osc_interrupt) {\n\t\t\t\thas_irq_pending--;\n\t\t\t\trptr->has_irq_pending = has_irq_pending;\n\t\t\t}\n\t\t\tif(has_irq_pending == 1) {\n\t\t\t\tfirst = i;\n\t\t\t}\n\t\t}\n\t\tif(num_osc_interrupt == 1) {\n\t\t\tdoc_reg_e0 = (first << 1);\n\t\t} else {\n#if 0\n\t\t\thalt_printf(\"remove_sound_irq[%02x]=%d, first:%d\\n\",\n\t\t\t\tosc, num_osc_interrupt, first);\n#endif\n\t\t}\n\t} else {\n#if 0\n\t\t/* make sure no int pending */\n\t\tif(doc_reg_e0 != 0xff) {\n\t\t\thalt_printf(\"remove_sound_irq[%02x]=0, but e0: %02x\\n\",\n\t\t\t\tosc, doc_reg_e0);\n\t\t}\n#endif\n\t\tif(must) {\n\t\t\thalt_printf(\"REMOVE_sound_irq[%02x]=0, but e0: %02x\\n\",\n\t\t\t\tosc, doc_reg_e0);\n\t\t}\n\t}\n\n\tif(doc_reg_e0 & 0x80) {\n\t\tfor(i = 0; i < 0x20; i++) {\n\t\t\thas_irq_pending = g_doc_regs[i].has_irq_pending;\n\t\t\tif(has_irq_pending) {\n\t\t\t\thalt_printf(\"remove_sound_irq[%02x], but \"\n\t\t\t\t\t\"[%02x]=%d!\\n\", osc,i,has_irq_pending);\n\t\t\t\tprintf(\"num_osc_int: %d, first: %02x\\n\",\n\t\t\t\t\tnum_osc_interrupt, first);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid\ndoc_start_sound2(int osc, dword64 dfcyc)\n{\n\tdouble\tdsamps;\n\n\tdsamps = dfcyc * g_dsamps_per_dfcyc;\n\tdoc_start_sound(osc, dsamps, dsamps);\n}\n\nvoid\ndoc_start_sound(int osc, double eff_dsamps, double dsamps)\n{\n\tDoc_reg\t*rptr;\n\tint\tctl;\n\tint\tmode;\n\tword32\tsz;\n\tword32\tsize;\n\tword32\twave_size;\n\n\tif(osc < 0 || osc > 31) {\n\t\thalt_printf(\"start_sound: osc: %02x!\\n\", osc);\n\t}\n\n\trptr = &(g_doc_regs[osc]);\n\n\tif(osc >= g_doc_num_osc_en) {\n\t\trptr->ctl |= 1;\n\t\treturn;\n\t}\n\n\tctl = rptr->ctl;\n\tmode = (ctl >> 1) & 3;\n\twave_size = rptr->wavesize;\n\tsz = ((wave_size >> 3) & 7) + 8;\n\tsize = 1 << sz;\n\n\tif(size < 0x100) {\n\t\thalt_printf(\"size: %08x is too small, sz: %08x!\\n\", size, sz);\n\t}\n\n\tif(rptr->running) {\n\t\thalt_printf(\"start_sound osc: %d, already running!\\n\", osc);\n\t}\n\n\trptr->running = 1;\n\n\trptr->complete_dsamp = eff_dsamps;\n\n\tdoc_printf(\"Starting osc %02x, dsamp: %f\\n\", osc, dsamps);\n\tdoc_printf(\"size: %04x\\n\", size);\n\n\tif((mode == 2) && ((osc & 1) == 0)) {\n\t\tprintf(\"Sync mode osc %d starting!\\n\", osc);\n\t\t/* set_halt(1); */\n\n\t\t/* see if we should start our odd partner */\n\t\tif((rptr[1].ctl & 7) == 5) {\n\t\t\t/* odd partner stopped in sync mode--start him */\n\t\t\trptr[1].ctl &= (~1);\n\t\t\tdoc_start_sound(osc + 1, eff_dsamps, dsamps);\n\t\t} else {\n\t\t\tprintf(\"Osc %d starting sync, but osc %d ctl: %02x\\n\",\n\t\t\t\tosc, osc+1, rptr[1].ctl);\n\t\t}\n\t}\n\n\tdoc_wave_end_estimate(osc, eff_dsamps, dsamps);\n\n\tDOC_LOG(\"st playing\", osc, eff_dsamps, size);\n#if 0\n\tif(rptr->cur_acc != 0) {\n\t\thalt_printf(\"Start osc %02x, acc: %08x\\n\", osc, rptr->cur_acc);\n\t}\n#endif\n}\n\nvoid\ndoc_wave_end_estimate2(int osc, dword64 dfcyc)\n{\n\tdouble\tdsamps;\n\n\tdsamps = dfcyc * g_dsamps_per_dfcyc;\n\tdoc_wave_end_estimate(osc, dsamps, dsamps);\n}\n\nvoid\ndoc_wave_end_estimate(int osc, double eff_dsamps, double dsamps)\n{\n\tDoc_reg *rptr;\n\tbyte\t*ptr1;\n\tdword64\tevent_dfcyc;\n\tdouble\tevent_dsamp, dfcycs_per_samp, dsamps_per_byte, num_dsamps;\n\tdouble\tdcur_inc;\n\tword32\ttmp1, cur_inc, save_val;\n\tint\tsave_size, pos, size, estimate;\n\n\tdfcycs_per_samp = g_fcyc_per_samp;\n\n\trptr = &(g_doc_regs[osc]);\n\n\tcur_inc = rptr->cur_inc;\n\tdcur_inc = (double)cur_inc;\n\tdsamps_per_byte = 0.0;\n\tif(cur_inc) {\n\t\tdsamps_per_byte = SND_PTR_SHIFT_DBL / (double)dcur_inc;\n\t}\n\n\t/* see if there's a zero byte */\n\ttmp1 = rptr->cur_start + (rptr->cur_acc & rptr->cur_mask);\n\tpos = tmp1 >> SND_PTR_SHIFT;\n\tsize = ((rptr->cur_end) >> SND_PTR_SHIFT) - pos;\n\n\tptr1 = &doc_ram[pos];\n\n\testimate = 0;\n\tif(rptr->ctl & 0x08 || g_doc_regs[osc ^ 1].ctl & 0x08) {\n\t\testimate = 1;\n\t}\n\n#if 0\n\testimate = 1;\n#endif\n\tif(estimate) {\n\t\tsave_size = size;\n\t\tsave_val = ptr1[size];\n\t\tptr1[size] = 0;\n\t\tsize = (int)strlen((char *)ptr1);\n\t\tptr1[save_size] = save_val;\n\t}\n\n\t/* calc samples to play */\n\tnum_dsamps = (dsamps_per_byte * (double)size) + 1.0;\n\n\trptr->samps_left = (int)num_dsamps;\n\n\tif(rptr->event) {\n\t\tremove_event_doc(osc);\n\t}\n\trptr->event = 0;\n\n\tevent_dsamp = eff_dsamps + num_dsamps;\n\tif(estimate) {\n\t\trptr->event = 1;\n\t\trptr->dsamp_ev = event_dsamp;\n\t\trptr->dsamp_ev2 = dsamps;\n\t\tevent_dfcyc = (dword64)(event_dsamp * dfcycs_per_samp) +\n\t\t\t\t\t\t\t\t65536LL;\n\t\tadd_event_doc(event_dfcyc, osc);\n\t}\n}\n\nvoid\ndoc_remove_sound_event(int osc)\n{\n\tif(g_doc_regs[osc].event) {\n\t\tg_doc_regs[osc].event = 0;\n\t\tremove_event_doc(osc);\n\t}\n}\n\n\nvoid\ndoc_write_ctl_reg(dword64 dfcyc, int osc, int val)\n{\n\tDoc_reg *rptr;\n\tword32\told_halt, new_halt;\n\tint\told_val;\n\n\tif(osc < 0 || osc >= 0x20) {\n\t\thalt_printf(\"doc_write_ctl_reg: osc: %02x, val: %02x\\n\",\n\t\t\tosc, val);\n\t\treturn;\n\t}\n\n\trptr = &(g_doc_regs[osc]);\n\told_val = rptr->ctl;\n\tg_doc_saved_ctl = old_val;\n\n\tif(old_val == val) {\n\t\treturn;\n\t}\n\n\t//DOC_LOG(\"ctl_reg\", osc, dsamps, (old_val << 16) + val);\n\n\told_halt = (old_val & 1);\n\tnew_halt = (val & 1);\n\n\t/* bits are:\t28: old int bit */\n\t/*\t\t29: old halt bit */\n\t/*\t\t30: new int bit */\n\t/*\t\t31: new halt bit */\n\n#if 0\n\tif(osc == 0x10) {\n\t\tprintf(\"osc %d new_ctl: %02x, old: %02x\\n\", osc, val, old_val);\n\t}\n#endif\n\n\t/* no matter what, remove any pending IRQs on this osc */\n\tdoc_remove_sound_irq(osc, 0);\n\n#if 0\n\tif(old_halt) {\n\t\tprintf(\"doc_write_ctl to osc %d, val: %02x, old: %02x\\n\",\n\t\t\tosc, val, old_val);\n\t}\n#endif\n\n\tif(new_halt != 0) {\n\t\t/* make sure sound is stopped */\n\t\tdoc_remove_sound_event(osc);\n\t\tif(old_halt == 0) {\n\t\t\t/* it was playing, finish it up */\n#if 0\n\t\t\thalt_printf(\"Aborted osc %d at eff_dsamps: %f, ctl: \"\n\t\t\t\t\"%02x, oldctl: %02x\\n\", osc, eff_dsamps,\n\t\t\t\tval, old_val);\n#endif\n\t\t\tsound_play(dfcyc);\n\t\t}\n\t\tif(((old_val >> 1) & 3) > 0) {\n\t\t\t/* don't clear acc if free-running */\n\t\t\tg_doc_regs[osc].cur_acc = 0;\n\t\t}\n\n\t\tg_doc_regs[osc].ctl = val;\n\t\tg_doc_regs[osc].running = 0;\n\t} else {\n\t\t/* new halt == 0 = make sure sound is running */\n\t\tif(old_halt != 0) {\n\t\t\t/* start sound */\n\t\t\t//DOC_LOG(\"ctl_sound_play\", osc, eff_dsamps, val);\n\t\t\tsound_play(dfcyc);\n\t\t\tg_doc_regs[osc].ctl = val;\n\n\t\t\tdoc_start_sound2(osc, dfcyc);\n\t\t} else {\n\t\t\t/* was running, and something changed */\n\t\t\tdoc_printf(\"osc %d old ctl:%02x new:%02x!\\n\",\n\t\t\t\tosc, old_val, val);\n\t\t\tsound_play(dfcyc);\n\t\t\tg_doc_regs[osc].ctl = val;\n\t\t\tif((old_val ^ val) & val & 0x8) {\n\t\t\t\t/* now has ints on */\n\t\t\t\tdoc_wave_end_estimate2(osc, dfcyc);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid\ndoc_recalc_sound_parms(dword64 dfcyc, int osc)\n{\n\tDoc_reg\t*rptr;\n\tdouble\tdfreq, dtmp1, dacc, dacc_recip;\n\tword32\tres, sz, size, wave_size, cur_start, shifted_size;\n\n\trptr = &(g_doc_regs[osc]);\n\n\twave_size = rptr->wavesize;\n\n\tdfreq = (double)rptr->freq;\n\n\tsz = ((wave_size >> 3) & 7) + 8;\n\tsize = 1 << sz;\n\trptr->size_bytes = size;\n\tres = wave_size & 7;\n\n\tshifted_size = size << SND_PTR_SHIFT;\n\tcur_start = (rptr->waveptr << (8 + SND_PTR_SHIFT)) & (0-shifted_size);\n\n\tdtmp1 = dfreq * (DOC_SCAN_RATE * g_drecip_audio_rate);\n\tdacc = (double)(1 << (20 - (17 - sz + res)));\n\tdacc_recip = (SND_PTR_SHIFT_DBL) / ((double)(1 << 20));\n\tdtmp1 = dtmp1 * g_drecip_osc_en_plus_2 * dacc * dacc_recip;\n\n\trptr->cur_inc = (int)(dtmp1);\n\trptr->cur_start = cur_start;\n\trptr->cur_end = cur_start + shifted_size;\n\trptr->cur_mask = (shifted_size - 1);\n\n\tdbg_log_info(dfcyc, (rptr->waveptr << 16) + wave_size, osc, 0xd0cf);\n}\n\nint\ndoc_read_c03c()\n{\n\treturn g_doc_sound_ctl;\n}\n\nint\ndoc_read_c03d(dword64 dfcyc)\n{\n\tDoc_reg\t*rptr;\n\tint\tosc, type, ret;\n\n\tret = g_doc_saved_val;\n\n\tif(g_doc_sound_ctl & 0x40) {\n\t\t/* Read RAM */\n\t\tg_doc_saved_val = doc_ram[g_c03ef_doc_ptr];\n\t} else {\n\t\t/* Read DOC */\n\t\tg_doc_saved_val = 0;\n\n\t\tosc = g_c03ef_doc_ptr & 0x1f;\n\t\ttype = (g_c03ef_doc_ptr >> 5) & 0x7;\n\t\trptr = &(g_doc_regs[osc]);\n\n\t\tswitch(type) {\n\t\tcase 0x0:\t/* freq lo */\n\t\t\tg_doc_saved_val = rptr->freq & 0xff;\n\t\t\tbreak;\n\t\tcase 0x1:\t/* freq hi */\n\t\t\tg_doc_saved_val = rptr->freq >> 8;\n\t\t\tbreak;\n\t\tcase 0x2:\t/* vol */\n\t\t\tg_doc_saved_val = rptr->vol;\n\t\t\tbreak;\n\t\tcase 0x3:\t/* data register */\n\t\t\tsound_play(dfcyc);\t\t// Fix for Paperboy GS\n\t\t\tg_doc_saved_val = rptr->last_samp_val;\n\t\t\tbreak;\n\t\tcase 0x4:\t/* wave ptr register */\n\t\t\tg_doc_saved_val = rptr->waveptr;\n\t\t\tbreak;\n\t\tcase 0x5:\t/* control register */\n\t\t\tg_doc_saved_val = rptr->ctl;\n\t\t\tbreak;\n\t\tcase 0x6:\t/* control register */\n\t\t\tg_doc_saved_val = rptr->wavesize;\n\t\t\tbreak;\n\t\tcase 0x7:\t/* 0xe0-0xff */\n\t\t\tswitch(osc) {\n\t\t\tcase 0x00:\t/* 0xe0 */\n\t\t\t\tg_doc_saved_val = doc_reg_e0;\n\t\t\t\tdoc_printf(\"Reading doc 0xe0, ret: %02x\\n\",\n\t\t\t\t\t\t\tg_doc_saved_val);\n\n\t\t\t\t/* Clear IRQ on read of e0, if any irq pend */\n\t\t\t\tif((doc_reg_e0 & 0x80) == 0) {\n\t\t\t\t\tdoc_remove_sound_irq(doc_reg_e0 >> 1,\n\t\t\t\t\t\t\t\t\t1);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 0x01:\t/* 0xe1 */\n\t\t\t\tg_doc_saved_val = (g_doc_num_osc_en - 1) << 1;\n\t\t\t\tbreak;\n\t\t\tcase 0x02:\t/* 0xe2 */\n\t\t\t\tg_doc_saved_val = 0x80;\n#if 0\n\t\t\t\thalt_printf(\"Reading doc 0xe2, ret: %02x\\n\",\n\t\t\t\t\t\t\tg_doc_saved_val);\n#endif\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tg_doc_saved_val = 0;\n\t\t\t\thalt_printf(\"Reading bad doc_reg[%04x]: %02x\\n\",\n\t\t\t\t\tg_c03ef_doc_ptr, g_doc_saved_val);\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tg_doc_saved_val = 0;\n\t\t\thalt_printf(\"Reading bad doc_reg[%04x]: %02x\\n\",\n\t\t\t\t\tg_c03ef_doc_ptr, g_doc_saved_val);\n\t\t}\n\t}\n\n\tdoc_printf(\"read c03d, doc_ptr: %04x, ret: %02x, saved: %02x\\n\",\n\t\tg_c03ef_doc_ptr, ret, g_doc_saved_val);\n\n\t//DOC_LOG(\"read c03d\", -1, dsamps, (g_c03ef_doc_ptr << 16) +\n\t//\t\t(g_doc_saved_val << 8) + ret);\n\n\tif(g_doc_sound_ctl & 0x20) {\n\t\tg_c03ef_doc_ptr = (g_c03ef_doc_ptr + 1) & 0xffff;\n\t}\n\n\n\treturn ret;\n}\n\nvoid\ndoc_write_c03c(dword64 dfcyc, word32 val)\n{\n\tint\tvol;\n\n\tvol = val & 0xf;\n\tdbg_log_info(dfcyc, val, g_doc_vol, 0xc03c);\n\tif(g_doc_vol != vol) {\n\t\tsound_play(dfcyc);\n\n\t\tg_doc_vol = vol;\n\t\tdoc_printf(\"Setting doc vol to 0x%x at %016llx\\n\", vol, dfcyc);\n\t}\n\n\tg_doc_sound_ctl = val;\n}\n\nvoid\ndoc_write_c03d(dword64 dfcyc, word32 val)\n{\n\tDoc_reg\t*rptr;\n\tint\tosc, type, ctl, tmp;\n\tint\ti;\n\n\tval = val & 0xff;\n\n\tdoc_printf(\"write c03d, doc_ptr: %04x, val: %02x\\n\",\n\t\tg_c03ef_doc_ptr, val);\n\n\tdbg_log_info(dfcyc, g_c03ef_doc_ptr, val, 0xc03d);\n\n\tif(g_doc_sound_ctl & 0x40) {\n\t\t/* RAM */\n\t\tdoc_ram[g_c03ef_doc_ptr] = val;\n\t} else {\n\t\t/* DOC */\n\t\tosc = g_c03ef_doc_ptr & 0x1f;\n\t\ttype = (g_c03ef_doc_ptr >> 5) & 0x7;\n\t\trptr = &(g_doc_regs[osc]);\n\t\tctl = rptr->ctl;\n#if 0\n\t\tif((ctl & 1) == 0) {\n\t\t\tif(type < 2 || type == 4 || type == 6) {\n\t\t\t\thalt_printf(\"Osc %d is running, old ctl: %02x, \"\n\t\t\t\t\t\"but write reg %02x=%02x\\n\",\n\t\t\t\t\tosc, ctl, g_c03ef_doc_ptr & 0xff, val);\n\t\t\t}\n\t\t}\n#endif\n\n\t\tswitch(type) {\n\t\tcase 0x0:\t/* freq lo */\n\t\t\tif((rptr->freq & 0xff) == (word32)val) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif((ctl & 1) == 0) {\n\t\t\t\t/* play through current status */\n\t\t\t\t//DOC_LOG(\"flo_sound_play\", osc, dsamps, val);\n\t\t\t\tsound_play(dfcyc);\n\t\t\t}\n\t\t\trptr->freq = (rptr->freq & 0xff00) + val;\n\t\t\tdoc_recalc_sound_parms(dfcyc, osc);\n\t\t\tbreak;\n\t\tcase 0x1:\t/* freq hi */\n\t\t\tif((rptr->freq >> 8) == (word32)val) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif((ctl & 1) == 0) {\n\t\t\t\t/* play through current status */\n\t\t\t\t//DOC_LOG(\"fhi_sound_play\", osc, dsamps, val);\n\t\t\t\tsound_play(dfcyc);\n\t\t\t}\n\t\t\trptr->freq = (rptr->freq & 0xff) + (val << 8);\n\t\t\tdoc_recalc_sound_parms(dfcyc, osc);\n\t\t\tbreak;\n\t\tcase 0x2:\t/* vol */\n\t\t\tif(rptr->vol == (word32)val) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif((ctl & 1) == 0) {\n\t\t\t\t/* play through current status */\n\t\t\t\t//DOC_LOG(\"vol_sound_play\", osc, dsamps, val);\n\t\t\t\tsound_play(dfcyc);\n\t\t\t}\n\t\t\trptr->vol = val;\n\t\t\tbreak;\n\t\tcase 0x3:\t/* data register */\n#if 0\n\t\t\tprintf(\"Writing %02x into doc_data_reg[%02x]!\\n\",\n\t\t\t\tval, osc);\n#endif\n\t\t\tbreak;\n\t\tcase 0x4:\t/* wave ptr register */\n\t\t\tif(rptr->waveptr == (word32)val) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif((ctl & 1) == 0) {\n\t\t\t\t/* play through current status */\n\t\t\t\t//DOC_LOG(\"wptr_sound_play\", osc, dsamps, val);\n\t\t\t\tsound_play(dfcyc);\n\t\t\t}\n\t\t\trptr->waveptr = val;\n\t\t\tdoc_recalc_sound_parms(dfcyc, osc);\n\t\t\tbreak;\n\t\tcase 0x5:\t/* control register */\n#if 0\n\t\t\tprintf(\"doc_write ctl osc %d, val: %02x\\n\", osc, val);\n#endif\n\t\t\tif(rptr->ctl == (word32)val) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdoc_write_ctl_reg(dfcyc, osc, val);\n\t\t\tbreak;\n\t\tcase 0x6:\t/* wavesize register */\n\t\t\tif(rptr->wavesize == (word32)val) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif((ctl & 1) == 0) {\n\t\t\t\t/* play through current status */\n\t\t\t\t//DOC_LOG(\"wsz_sound_play\", osc, dsamps, val);\n\t\t\t\tsound_play(dfcyc);\n\t\t\t}\n\t\t\trptr->wavesize = val;\n\t\t\tdoc_recalc_sound_parms(dfcyc, osc);\n\t\t\tbreak;\n\t\tcase 0x7:\t/* 0xe0-0xff */\n\t\t\tswitch(osc) {\n\t\t\tcase 0x00:\t/* 0xe0 */\n\t\t\t\tdoc_printf(\"writing doc 0xe0 with %02x, \"\n\t\t\t\t\t\"was:%02x\\n\", val, doc_reg_e0);\n#if 0\n\t\t\t\tif(val != doc_reg_e0) {\n\t\t\t\t\thalt_printf(\"writing doc 0xe0 with \"\n\t\t\t\t\t\t\"%02x, was:%02x\\n\", val,\n\t\t\t\t\t\tdoc_reg_e0);\n\t\t\t\t}\n#endif\n\t\t\t\tbreak;\n\t\t\tcase 0x01:\t/* 0xe1 */\n\t\t\t\tdoc_printf(\"Writing doc 0xe1 with %02x\\n\", val);\n\t\t\t\ttmp = val & 0x3e;\n\t\t\t\ttmp = (tmp >> 1) + 1;\n\t\t\t\tif(tmp < 1) {\n\t\t\t\t\ttmp = 1;\n\t\t\t\t}\n\t\t\t\tif(tmp > 32) {\n\t\t\t\t\thalt_printf(\"doc 0xe1: %02x!\\n\", val);\n\t\t\t\t\ttmp = 32;\n\t\t\t\t}\n\t\t\t\tg_doc_num_osc_en = tmp;\n\t\t\t\tUPDATE_G_DCYCS_PER_DOC_UPDATE(tmp);\n\n\t\t\t\t/* Stop any oscs that were running but now */\n\t\t\t\t/*  are disabled */\n\t\t\t\tfor(i = g_doc_num_osc_en; i < 0x20; i++) {\n\t\t\t\t\tdoc_write_ctl_reg(dfcyc, i,\n\t\t\t\t\t\t\tg_doc_regs[i].ctl | 1);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t/* this should be illegal, but Turkey Shoot */\n\t\t\t\t/* and apparently TaskForce, OOTW, etc */\n\t\t\t\t/*  writes to e2-ff, for no apparent reason */\n\t\t\t\tdoc_printf(\"Writing doc 0x%x with %02x\\n\",\n\t\t\t\t\t\tg_c03ef_doc_ptr, val);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thalt_printf(\"Writing %02x into bad doc_reg[%04x]\\n\",\n\t\t\t\tval, g_c03ef_doc_ptr);\n\t\t}\n\t}\n\n\tif(g_doc_sound_ctl & 0x20) {\n\t\tg_c03ef_doc_ptr = (g_c03ef_doc_ptr + 1) & 0xffff;\n\t}\n\n\tg_doc_saved_val = val;\n}\n\nvoid\ndoc_show_ensoniq_state()\n{\n\tDoc_reg\t*rptr;\n\tint\ti;\n\n\tprintf(\"Ensoniq state\\n\");\n\tprintf(\"c03c doc_sound_ctl: %02x, doc_saved_val: %02x\\n\",\n\t\tg_doc_sound_ctl, g_doc_saved_val);\n\tprintf(\"doc_ptr: %04x,    num_osc_en: %02x, e0: %02x\\n\",\n\t\tg_c03ef_doc_ptr, g_doc_num_osc_en, doc_reg_e0);\n\n\tfor(i = 0; i < 32; i += 8) {\n\t\tprintf(\"irqp: %02x: %04x %04x %04x %04x %04x %04x %04x %04x\\n\",\n\t\t\ti,\n\t\t\tg_doc_regs[i].has_irq_pending,\n\t\t\tg_doc_regs[i + 1].has_irq_pending,\n\t\t\tg_doc_regs[i + 2].has_irq_pending,\n\t\t\tg_doc_regs[i + 3].has_irq_pending,\n\t\t\tg_doc_regs[i + 4].has_irq_pending,\n\t\t\tg_doc_regs[i + 5].has_irq_pending,\n\t\t\tg_doc_regs[i + 6].has_irq_pending,\n\t\t\tg_doc_regs[i + 7].has_irq_pending);\n\t}\n\n\tfor(i = 0; i < 32; i += 8) {\n\t\tprintf(\"freq: %02x: %04x %04x %04x %04x %04x %04x %04x %04x\\n\",\n\t\t\ti,\n\t\t\tg_doc_regs[i].freq, g_doc_regs[i + 1].freq,\n\t\t\tg_doc_regs[i + 2].freq, g_doc_regs[i + 3].freq,\n\t\t\tg_doc_regs[i + 4].freq, g_doc_regs[i + 5].freq,\n\t\t\tg_doc_regs[i + 6].freq, g_doc_regs[i + 7].freq);\n\t}\n\n\tfor(i = 0; i < 32; i += 8) {\n\t\tprintf(\"vol: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\\n\",\n\t\t\ti,\n\t\t\tg_doc_regs[i].vol, g_doc_regs[i + 1].vol,\n\t\t\tg_doc_regs[i + 2].vol, g_doc_regs[i + 3].vol,\n\t\t\tg_doc_regs[i + 4].vol, g_doc_regs[i + 5].vol,\n\t\t\tg_doc_regs[i + 6].vol, g_doc_regs[i + 6].vol);\n\t}\n\n\tfor(i = 0; i < 32; i += 8) {\n\t\tprintf(\"wptr: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\\n\",\n\t\t\ti,\n\t\t\tg_doc_regs[i].waveptr, g_doc_regs[i + 1].waveptr,\n\t\t\tg_doc_regs[i + 2].waveptr, g_doc_regs[i + 3].waveptr,\n\t\t\tg_doc_regs[i + 4].waveptr, g_doc_regs[i + 5].waveptr,\n\t\t\tg_doc_regs[i + 6].waveptr, g_doc_regs[i + 7].waveptr);\n\t}\n\n\tfor(i = 0; i < 32; i += 8) {\n\t\tprintf(\"ctl: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\\n\",\n\t\t\ti,\n\t\t\tg_doc_regs[i].ctl, g_doc_regs[i + 1].ctl,\n\t\t\tg_doc_regs[i + 2].ctl, g_doc_regs[i + 3].ctl,\n\t\t\tg_doc_regs[i + 4].ctl, g_doc_regs[i + 5].ctl,\n\t\t\tg_doc_regs[i + 6].ctl, g_doc_regs[i + 7].ctl);\n\t}\n\n\tfor(i = 0; i < 32; i += 8) {\n\t\tprintf(\"wsize: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\\n\",\n\t\t\ti,\n\t\t\tg_doc_regs[i].wavesize, g_doc_regs[i + 1].wavesize,\n\t\t\tg_doc_regs[i + 2].wavesize, g_doc_regs[i + 3].wavesize,\n\t\t\tg_doc_regs[i + 4].wavesize, g_doc_regs[i + 5].wavesize,\n\t\t\tg_doc_regs[i + 6].wavesize, g_doc_regs[i + 7].wavesize);\n\t}\n\n\tfor(i = 0; i < 32; i++) {\n\t\trptr = &(g_doc_regs[i]);\n\t\tprintf(\"%2d: ctl:%02x wp:%02x ws:%02x freq:%04x vol:%02x \"\n\t\t\t\"ev:%d run:%d irq:%d sz:%04x\\n\", i,\n\t\t\trptr->ctl, rptr->waveptr, rptr->wavesize, rptr->freq,\n\t\t\trptr->vol, rptr->event, rptr->running,\n\t\t\trptr->has_irq_pending, rptr->size_bytes);\n\t\tprintf(\"    acc:%08x inc:%08x st:%08x end:%08x m:%08x\\n\",\n\t\t\trptr->cur_acc, rptr->cur_inc, rptr->cur_start,\n\t\t\trptr->cur_end, rptr->cur_mask);\n\t\tprintf(\"    compl_ds:%f samps_left:%d ev:%f ev2:%f\\n\",\n\t\t\trptr->complete_dsamp, rptr->samps_left,\n\t\t\trptr->dsamp_ev, rptr->dsamp_ev2);\n\t}\n\n#if 0\n\tfor(osc = 0; osc < 32; osc++) {\n\t\tfmax = 0.0;\n\t\tprintf(\"osc %d has %d samps\\n\", osc, g_fsamp_num[osc]);\n\t\tfor(i = 0; i < g_fsamp_num[osc]; i++) {\n\t\t\tprintf(\"%4d: %f\\n\", i, g_fsamps[osc][i]);\n\t\t\tfmax = MY_MAX(fmax, g_fsamps[osc][i]);\n\t\t}\n\t\tprintf(\"osc %d, fmax: %f\\n\", osc, fmax);\n\t}\n#endif\n}\n"
  },
  {
    "path": "gsplus/src/dyna_filt.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2021 Kent Dickey                      */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include \"defc.h\"\n\n// Provide filters for Dynapro to use for copying files from the host to\n//  a ProDOS volume, and for writing changes to the ProDOS volume back to\n//  host files.\n\n"
  },
  {
    "path": "gsplus/src/dyna_type.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2021-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include \"defc.h\"\n\n// Provide routines for Dynapro to use for detecting the file type on the\n//  host system.  Host files can be \"basic1.bas\", \"basic2,tbas,a$801\"\n\nSTRUCT(Dynatype_extensions) {\n\tchar\tstr[16];\n\tword16\tfile_type;\n\tword16\taux_type;\n};\n\nDynatype_extensions g_dynatype_extensions[] = {\n{\t\"applesingle\",\t\t0xfff,\t0xffff },\n{\t\"txt\",\t\t\t0x04,\t0 },\n{\t\"c\",\t\t\t0x04,\t0 },\t\t\t// ,ttxt\n{\t\"s\",\t\t\t0x04,\t0 },\t\t\t// ,ttxt\n{\t\"h\",\t\t\t0x04,\t0 },\t\t\t// ,ttxt\n{\t\"bin\",\t\t\t0x06,\t0x2000 },\t\t// ,tbin\n{\t\"bas\",\t\t\t0xfc,\t0x0801 },\t\t// ,tbas\n{\t\"system\",\t\t0xff,\t0x2000 },\t\t// ,tsys\n//{\t\"shr\",\t\t\t0xc0,\t0x0002 },\t\t// ,t$c0\n{\t\"shk\",\t\t\t0xe0,\t0x8002 },\t\t// ,t$e0\n{\t\"sdk\",\t\t\t0xe0,\t0x8002 },\t\t// ,t$e0\n{\t\"\",\t\t\t0,\t0 }\n};\n\nSTRUCT(Dynatype_types) {\n\tchar\tstr[16];\n\tword16\tfile_type;\n\tword16\taux_type;\n};\n\nDynatype_types g_dynatype_types[] = {\n{\t\"non\",\t\t0x00,\t0 },\n{\t\"bad\",\t\t0x01,\t0 },\n{\t\"txt\",\t\t0x04,\t0 },\n{\t\"bin\",\t\t0x06,\t0x2000 },\n{\t\"pnt\",\t\t0xc0,\t0x0002 },\n{\t\"fnd\",\t\t0xc9,\t0 },\n{\t\"icn\",\t\t0xca,\t0 },\n{\t\"cmd\",\t\t0xf0,\t0 },\n{\t\"bas\",\t\t0xfc,\t0x0801 },\n{\t\"sys\",\t\t0xff,\t0x2000 },\n{\t\"\",\t\t0,\t0 }\n};\n\nword32\ndynatype_scan_extensions(const char *str)\n{\n\tint\tlen;\n\tint\ti;\n\n\tlen = (int)(sizeof(g_dynatype_extensions) /\n\t\t\t\t\tsizeof(g_dynatype_extensions[0]));\n\tfor(i = 0; i < len; i++) {\n\t\tif(cfgcasecmp(str, g_dynatype_extensions[i].str) == 0) {\n\t\t\treturn (g_dynatype_extensions[i].file_type << 16) |\n\t\t\t\t\tg_dynatype_extensions[i].aux_type |\n\t\t\t\t\t0x1000000;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nword32\ndynatype_find_prodos_type(const char *str)\n{\n\tword32\tfile_type;\n\tint\tlen;\n\tint\ti;\n\n\tlen = (int)(sizeof(g_dynatype_types) / sizeof(g_dynatype_types[0]));\n\tfor(i = 0; i < len; i++) {\n\t\tif(cfgcasecmp(str, g_dynatype_types[i].str) == 0) {\n\t\t\tfile_type = g_dynatype_types[i].file_type;\n\t\t\treturn (file_type << 16) | g_dynatype_types[i].aux_type;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nconst char *\ndynatype_find_file_type(word32 file_type)\n{\n\tint\tlen;\n\tint\ti;\n\n\tlen = (int)(sizeof(g_dynatype_types) / sizeof(g_dynatype_types[0]));\n\tfor(i = 0; i < len; i++) {\n\t\tif(g_dynatype_types[i].file_type == file_type) {\n\t\t\treturn g_dynatype_types[i].str;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nword32\ndynatype_detect_file_type(Dynapro_file *fileptr, const char *path_ptr,\n\t\t\t\t\t\t\tword32 storage_type)\n{\n\tchar\text_buf[32];\n\tconst char *str;\n\tchar\t*endstr;\n\tword32\tfile_type, aux_type, type_or_aux;\n\tint\tlen, this_len, c, pos;\n\n\t// Look for ,tbas and ,a$2000 to get filetype and aux_type info\n\n\tstr = cfg_str_basename(path_ptr);\n\tlen = (int)strlen(str);\n\n\t// Look for .ext and ,tbas, etc.\n\tpos = 0;\n\text_buf[0] = 0;\n\tfile_type = 0x06;\t\t\t// Default to BIN\n\taux_type = 0;\n\twhile(pos < len) {\n\t\tc = str[pos++];\n\t\tif(c == '.') {\n\t\t\tthis_len = dynatype_get_extension(&str[pos],\n\t\t\t\t\t\t\t&ext_buf[0], 30);\n\t\t\tpos += this_len;\n\t\t\tcontinue;\n\t\t} else if(c == ',') {\n\t\t\tthis_len = dynatype_comma_arg(&str[pos], &type_or_aux);\n\t\t\tif(type_or_aux & 0x1000000) {\n\t\t\t\tfile_type = type_or_aux;\n\t\t\t} else if(type_or_aux & 0x2000000) {\n\t\t\t\taux_type = type_or_aux;\n\t\t\t} else {\n\t\t\t\tprintf(\"Unknown , extension, %s ignored\\n\",\n\t\t\t\t\t\t\t\t&str[pos]);\n\t\t\t}\n\t\t\tpos += this_len;\n\t\t\tcontinue;\n\t\t} else if(c == '#') {\n\t\t\t// Cadius style encoding: #ff2000 is type=$ff, aux=$2000\n\t\t\ttype_or_aux = strtol(&str[pos], &endstr, 16);\n\t\t\tfile_type = (type_or_aux & 0xffffff) | 0x1000000;\n\t\t\taux_type = 0;\n\t\t\tpos += (int)(endstr - str);\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\t// Handle extensions and type.  First do extension mapping\n\tif(ext_buf[0]) {\n\t\ttype_or_aux = dynatype_scan_extensions(&ext_buf[0]);\n\t\tif((type_or_aux) >= 0x0f000000UL) {\n\t\t\t// AppleSingle\n\t\t\tstorage_type = 0x50;\t\t// Forked file\n\t\t}\n\t\tif(file_type < 0x1000000) {\n\t\t\tfile_type = type_or_aux;\n\t\t}\n\t\tif(aux_type < 0x1000000) {\n\t\t\taux_type = type_or_aux;\n\t\t}\n\t}\n#if 0\n\tprintf(\"After parsing ext, file_type:%08x, aux_type:%08x\\n\",\n\t\t\t\t\t\tfile_type, aux_type);\n#endif\n\n\tfileptr->file_type = (file_type >> 16) & 0xff;\n\tif(aux_type == 0) {\n\t\taux_type = file_type & 0xffff;\n\t}\n\tfileptr->aux_type = aux_type & 0xffff;\n\n\treturn storage_type;\n}\n\nint\ndynatype_get_extension(const char *str, char *out_ptr, int buf_len)\n{\n\tint\tc, len;\n\n\t// Will write up to buf_len chars to out_ptr\n\tif(buf_len < 1) {\n\t\treturn 0;\n\t}\n\tbuf_len--;\n\tlen = 0;\n\twhile(1) {\n\t\tc = *str++;\n\t\t*out_ptr = c;\n\t\tif((c == 0) || (c == '.') || (c == ',') || (c == '#') ||\n\t\t\t\t\t\t\t(len >= buf_len)) {\n\t\t\t*out_ptr = 0;\n\t\t\treturn len;\n\t\t}\n\t\tout_ptr++;\n\t\tlen++;\n\t}\n}\n\nint\ndynatype_comma_arg(const char *str, word32 *type_or_aux_ptr)\n{\n\tchar\ttype_buf[8];\n\tchar\t*endstr;\n\tword32\tval, type_or_aux;\n\tint\tc, len, base, this_len;\n\tint\ti;\n\n\t// Read next char\n\t*type_or_aux_ptr = 0;\n\n\tc = *str++;\n\tif(c == 0) {\n\t\treturn 0;\n\t}\n\tlen = 1;\n\tc = tolower(c);\n\ttype_or_aux = c;\n\t// See if next char is $ for hex\n\tc = *str;\n\tbase = 0;\n\tif(c == '$') {\n\t\tbase = 16;\n\t\tstr++;\n\t\tlen++;\n\t}\n\tval = strtol(str, &endstr, base);\n\tthis_len = (int)(endstr - str);\n\tif((val == 0) && (this_len < 2) && (base == 0) &&\n\t\t\t\t\t\t\t(type_or_aux == 't')) {\n\t\t// Not a valid number\n\t\tfor(i = 0; i < 3; i++) {\n\t\t\tc = *str++;\n\t\t\tif(c == 0) {\n\t\t\t\treturn len;\n\t\t\t}\n\t\t\ttype_buf[i] = c;\n\t\t\tlen++;\n\t\t}\n\t\ttype_buf[3] = 0;\n\t\tval = dynatype_find_prodos_type(&type_buf[0]);\n\t\t*type_or_aux_ptr = 0x1000000 | val;\n\t} else {\n\t\tlen += this_len;\n\t}\n\tif(type_or_aux == 't') {\n\t\tif(val < 0x100) {\n\t\t\t*type_or_aux_ptr = 0x1000000 | ((val << 16) & 0xffffff);\n\t\t}\n\t} else if(type_or_aux == 'a') {\n\t\t*type_or_aux_ptr = 0x2000000 | (val & 0xffff);\n\t}\n\n\treturn len;\n}\n\nvoid\ndynatype_fix_unix_name(Dynapro_file *fileptr, char *outbuf_ptr, int path_max)\n{\n\tchar\tbuf[16];\n\tDynapro_file tmpfile;\n\tconst char *str;\n\tword32\taux_type;\n\n#if 0\n\tprintf(\"Looking at %s ftype:%02x aux:%04x\\n\", outbuf_ptr,\n\t\t\t\t\tfileptr->file_type, fileptr->aux_type);\n#endif\n\n\tif(fileptr->prodos_name[0] >= 0xd0) {\n\t\treturn;\t\t\t// Directory, or Dir/Volume Header\n\t}\n\tif((fileptr->prodos_name[0] & 0xf0) == 0x50) {\n\t\t// Forked file, add .applesingle\n\t\tcfg_strlcat(outbuf_ptr, \".applesingle\", path_max);\n\t\treturn;\n\t}\n\n\tmemset(&tmpfile, 0, sizeof(Dynapro_file));\n\n\t// See what this file defaults to as to type/aux\n\t(void)dynatype_detect_file_type(&tmpfile, outbuf_ptr, 0x10);\n\n\t// Otherwise, add ,ttype and ,a$aux as needed\n\tif(tmpfile.file_type != fileptr->file_type) {\n\t\tstr = dynatype_find_file_type(fileptr->file_type);\n\t\tif(str) {\n\t\t\taux_type = dynatype_find_prodos_type(str);\n\t\t} else {\n\t\t\tstr = &buf[0];\n\t\t\tbuf[15] = 0;\n\t\t\tsnprintf(&buf[0], 15, \"$%02x\", fileptr->file_type);\n\t\t}\n\t\tcfg_strlcat(outbuf_ptr, \",t\", path_max);\n\t\tcfg_strlcat(outbuf_ptr, str, path_max);\n\t}\n\n\t(void)dynatype_detect_file_type(&tmpfile, outbuf_ptr, 0x10);\n\taux_type = fileptr->aux_type;\n\n\tif(aux_type != tmpfile.aux_type) {\n\t\tbuf[15] = 0;\n\t\tsnprintf(&buf[0], 15, \",a$%04x\", aux_type & 0xffff);\n\t\tcfg_strlcat(outbuf_ptr, &buf[0], path_max);\n\t}\n\n\t// printf(\"dynatype_new_unix_name: %s\\n\", outbuf_ptr);\n\n\t// Check that it succeeded\n\t(void)dynatype_detect_file_type(&tmpfile, outbuf_ptr, 0x10);\n\tif((tmpfile.file_type != fileptr->file_type) ||\n\t\t\t\t(tmpfile.aux_type != fileptr->aux_type)) {\n\t\thalt_printf(\"File %s want ftype:%02x aux:%04x, got:%02x %04x\\n\",\n\t\t\toutbuf_ptr, fileptr->file_type, fileptr->aux_type,\n\t\t\ttmpfile.file_type, tmpfile.aux_type);\n\t\texit(1);\n\t}\n}\n"
  },
  {
    "path": "gsplus/src/dyna_validate.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2021-2022 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// Main information is from Beneath Apple ProDOS which has disk layout\n//  descriptions.  Forked files are described in Technote tn-pdos-025.\n\n#include \"defc.h\"\n\nword32\ndynapro_validate_header(Disk *dsk, Dynapro_file *fileptr, word32 dir_byte,\n\t\t\t\t\t\tword32 parent_dir_byte)\n{\n\tword32\tstorage_type, exp_type, val, parent_block, exp_val;\n\n\tstorage_type = fileptr->prodos_name[0] & 0xf0;\n\texp_type = 0xe0;\n\tif(dir_byte == 0x0404) {\n\t\texp_type = 0xf0;\t\t// Volume header\n\t}\n\tif(storage_type != exp_type) {\n\t\tprintf(\"Volume/Dir header is %02x at %07x\\n\",\n\t\t\t\t\t\tstorage_type, dir_byte);\n\t\treturn 0;\n\t}\n\n\tif(fileptr->aux_type != 0x0d27) {\n\t\tprintf(\"entry_length, entries_per_block:%04x at %07x\\n\",\n\t\t\tfileptr->aux_type, dir_byte);\n\t\treturn 0;\n\t}\n\n\tif(exp_type == 0xf0) {\t\t\t// Volume header\n\t\tval = fileptr->lastmod_time >> 16;\n\t\tif(val != 6) {\n\t\t\tprintf(\"bit_map_ptr:%04x, should be 6\\n\", val);\n\t\t\treturn 0;\n\t\t}\n\t\tval = fileptr->header_pointer;\n\t\tif(val != (dsk->dimage_size >> 9)) {\n\t\t\tprintf(\"Num blocks at %07x is wrong: %04x\\n\", dir_byte,\n\t\t\t\t\t\t\tval);\n\t\t\treturn 0;\n\t\t}\n\t} else {\t\t\t\t// Directory header\n\t\tval = fileptr->lastmod_time >> 16;\t// parent_pointer\n\t\tparent_block = parent_dir_byte >> 9;\n\t\tif(val != parent_block) {\n\t\t\tprintf(\"Dir at %07x parent:%04x should be %04x\\n\",\n\t\t\t\tdir_byte, val, parent_block);\n\t\t\treturn 0;\n\t\t}\n\t\tval = fileptr->header_pointer;\n\t\texp_val = ((parent_dir_byte & 0x1ff) - 4) / 0x27;\n\t\texp_val = (exp_val + 1) | 0x2700;\n\t\tif(val != exp_val) {\n\t\t\tprintf(\"Parent entry at %07x is:%04x, should be:%04x\\n\",\n\t\t\t\tdir_byte, val, exp_val);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\treturn 1;\n}\n\nvoid\ndynapro_validate_init_freeblks(byte *freeblks_ptr, word32 num_blocks)\n{\n\tword32\tnum_map_blocks, mask;\n\tint\tpos;\n\tword32\tui;\n\n\tfor(ui = 0; ui < (num_blocks + 7)/8; ui++) {\n\t\tfreeblks_ptr[ui] = 0xff;\n\t}\n\tfreeblks_ptr[0] &= 0x3f;\n\tif(num_blocks & 7) {\n\t\tfreeblks_ptr[num_blocks / 8] = 0xff00 >> (num_blocks & 7);\n\t}\n\n\tnum_map_blocks = (num_blocks + 4095) >> 12;\t// 4096 bits per block\n\tfor(ui = 0; ui < num_map_blocks; ui++) {\n\t\t// Mark blocks used in the bitmap as in use\n\t\tpos = (ui + 6) >> 3;\n\t\tmask = 0x80 >> ((ui + 6) & 7);\n\t\tfreeblks_ptr[pos] &= (~mask);\n\t}\n}\n\nword32\ndynapro_validate_freeblk(Disk *dsk, byte *freeblks_ptr, word32 block)\n{\n\tword32\tmask, ret;\n\tint\tpos;\n\n\t// Return != 0 if block is free (which is success), returns == 0\n\t//  if it is in use (which is an error).  Marks block as in use\n\tpos = block >> 3;\n\tif(block >= (dsk->dimage_size >> 9)) {\n\t\treturn 0x100;\t\t// Out of range\n\t}\n\tmask = 0x80 >> (block & 7);\n\tret = freeblks_ptr[pos] & mask;\n\tfreeblks_ptr[pos] &= (~mask);\n\n\tif(!ret) {\n\t\tprintf(\"Block %04x was already in use\\n\", block);\n\t}\n\treturn ret;\n}\n\nword32\ndynapro_validate_file(Disk *dsk, byte *freeblks_ptr, word32 block_num,\n\t\t\t\t\t\tword32 eof, int level_first)\n{\n\tbyte\t*bptr;\n\tword32\tnum_blocks, tmp, ret, exp_blocks, extra_blocks;\n\tint\tlevel, first;\n\tint\ti;\n\n\tlevel = level_first & 0xf;\n\tfirst = level_first & 0x10;\n\n\tif(!dynapro_validate_freeblk(dsk, freeblks_ptr, block_num)) {\n\t\treturn 0;\n\t}\n\tif(level_first == 0x15) {\n\t\treturn dynapro_validate_forked_file(dsk, freeblks_ptr,\n\t\t\t\t\t\t\tblock_num, eof);\n\t}\n\tif((level < 1) || (level >= 4)) {\n\t\tprintf(\"level %d out of range, %08x\\n\", level, level_first);\n\t\treturn 0;\n\t}\n\tif(level == 1) {\n\t\treturn 1;\n\t}\n\tnum_blocks = 1;\n\tbptr = &(dsk->raw_data[block_num * 0x200]);\n\tfor(i = 0; i < 256; i++) {\n\t\ttmp = bptr[i] + (bptr[256 + i] << 8);\n\t\tif(tmp == 0) {\n\t\t\tif(first) {\n\t\t\t\tprintf(\"First block is spare, illegal!\\n\");\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tret = dynapro_validate_file(dsk, freeblks_ptr, tmp, eof,\n\t\t\t\t\t\t\tfirst | (level - 1));\n\t\tif(ret == 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tnum_blocks += ret;\n\t\tfirst = 0;\n\t}\n\n\tif(level_first & 0x10) {\n\t\t// Try to estimate exp_blocks based on eof\n\t\texp_blocks = (eof + 0x1ff) >> 9;\n\t\tif(exp_blocks == 0) {\n\t\t\texp_blocks = 1;\n\t\t} else if(exp_blocks > 1) {\n\t\t\t// Add in sapling blocks\n\t\t\textra_blocks = ((exp_blocks + 255) >> 8);\n\t\t\tif(exp_blocks > 256) {\n\t\t\t\textra_blocks++;\n\t\t\t}\n\t\t\texp_blocks += extra_blocks;\n\t\t}\n\t\tif(num_blocks > exp_blocks) {\n\t\t\tprintf(\"blocks_used:%04x, eof:%07x, exp:%04x\\n\",\n\t\t\t\tnum_blocks, eof, exp_blocks);\n\t\t\treturn 0;\n\t\t}\n\t}\n\treturn num_blocks;\n}\n\nword32\ndynapro_validate_forked_file(Disk *dsk, byte *freeblks_ptr, word32 block_num,\n\t\t\t\t\t\t\t\tword32 eof)\n{\n\tbyte\t*bptr;\n\tword32\tnum_blocks, tmp, ret, size, type, exp_blocks;\n\tint\tlevel;\n\tint\ti;\n\n\tbptr = &(dsk->raw_data[block_num * 0x200]);\n\n\tif(eof != 0x200) {\n\t\tprintf(\"In forked file block %04x, eof in dir:%08x, exp 0200\\n\",\n\t\t\t\t\t\t\t\tblock_num, eof);\n\t\treturn 0;\n\t}\n\t// Check that most of the block is 0\n\tfor(i = 44; i < 512; i++) {\n\t\tif((i >= 0x100) && (i < 0x108)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif(bptr[i] != 0) {\n\t\t\tprintf(\"In forked file block:%04x, byte %03x is %02x\\n\",\n\t\t\t\tblock_num, i, bptr[i]);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t// Check for basic Finder Info format\n\tfor(i = 0; i < 2; i++) {\n\t\tsize = bptr[8 + 18*i];\n\t\ttype = bptr[9 + 18*i];\n\t\tif(((size != 0) && (size != 18)) || (type > 2)) {\n\t\t\tprintf(\"Finder Info size %04x+%03x=%02x, type:%02x\\n\",\n\t\t\t\t\tblock_num, 8 + 18*i, size, type);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tnum_blocks = 1;\n\tfor(i = 0; i < 2; i++) {\n\t\ttmp = bptr[1 + 0x100*i] + (bptr[2 + 0x100*i] << 8);\n\t\tif(tmp == 0) {\n\t\t\tprintf(\"First fork %d block is spare, illegal!\\n\", i);\n\t\t\treturn 0;\n\t\t}\n\t\teof = bptr[5 + 0x100*i] + (bptr[6 + 0x100*i] << 8) +\n\t\t\t\t\t\t(bptr[7 + 0x100*i] << 16);\n\t\tlevel = bptr[0 + 0x100*i];\n\t\tret = dynapro_validate_file(dsk, freeblks_ptr, tmp, eof,\n\t\t\t\t\t\t\t\t0x10 | level);\n\t\tif(ret == 0) {\n\t\t\tprintf(\"Fork %d failed, eof:%08x, block:%04x \"\n\t\t\t\t\"fork:%04x, level:%d\\n\", i, eof, block_num,\n\t\t\t\ttmp, level);\n\t\t\treturn 0;\n\t\t}\n\t\texp_blocks = bptr[3 + 0x100*i] + (bptr[4 + 0x100*i] << 8);\n\t\tif(ret != exp_blocks) {\n\t\t\tprintf(\"Fork %d at %04x, blocks:%04x, exp:%04x\\n\",\n\t\t\t\ti, block_num, ret, exp_blocks);\n\t\t}\n\t\tnum_blocks += ret;\n\t}\n\n\treturn num_blocks;\n}\n\nword32\ndynapro_validate_dir(Disk *dsk, byte *freeblks_ptr, word32 dir_byte,\n\t\t\t\tword32 parent_dir_byte, word32 exp_blocks_used)\n{\n\tchar\tbuf32[32];\n\tDynapro_file localfile;\n\tbyte\t*bptr;\n\tword32\tstart_dir_block, last_block, max_block, tmp_byte, sub_blocks;\n\tword32\tret, act_entries, exp_entries, blocks_used, prev, next;\n\tint\tcnt, is_header;\n\n\t// Read directory, make sure each entry is consistent\n\t// Return 0 if there is damage, != 0 if OK.\n\tbptr = dsk->raw_data;\n\tstart_dir_block = dir_byte >> 9;\n\tlast_block = 0;\n\tmax_block = (word32)(dsk->dimage_size >> 9);\n\tcnt = 0;\n\tis_header = 1;\n\texp_entries = 0xdeadbeef;\n\tact_entries = 0;\n\tblocks_used = 0;\n\twhile(dir_byte) {\n\t\tif((dir_byte & 0x1ff) == 4) {\n\t\t\t// First entry in this block, check prev/next\n\t\t\ttmp_byte = dir_byte & -0x200;\t\t// Block align\n\t\t\tprev = dynapro_get_word16(&bptr[tmp_byte + 0]);\n\t\t\tnext = dynapro_get_word16(&bptr[tmp_byte + 2]);\n\t\t\tif((prev != last_block) || (next >= max_block)) {\n\t\t\t\tprintf(\"dir at %07x is damaged in links\\n\",\n\t\t\t\t\t\t\t\tdir_byte);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tlast_block = dir_byte >> 9;\n\t\t\tret = dynapro_validate_freeblk(dsk, freeblks_ptr,\n\t\t\t\t\t\t\t\tdir_byte >> 9);\n\t\t\tif(!ret) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tblocks_used++;\n\t\t}\n\t\tif(cnt++ >= 65536) {\n\t\t\tprintf(\"Loop detected, dir_byte:%07x\\n\", dir_byte);\n\t\t\treturn 0;\n\t\t}\n\t\tret = dynapro_fill_fileptr_from_prodos(dsk, &localfile,\n\t\t\t\t\t\t\t&buf32[0], dir_byte);\n\t\tif(ret == 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tif(ret != 1) {\n\t\t\tact_entries = act_entries + 1 - is_header;\n\t\t}\n\t\tif(is_header) {\n\t\t\tif(ret == 1) {\n\t\t\t\tprintf(\"Volume/Dir header is erased\\n\");\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tret = dynapro_validate_header(dsk, &localfile, dir_byte,\n\t\t\t\t\t\t\tparent_dir_byte);\n\t\t\tif(ret == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\texp_entries = localfile.lastmod_time & 0xffff;\n\t\t} else if(ret != 1) {\n\t\t\tif(localfile.header_pointer != start_dir_block) {\n\t\t\t\tprintf(\"At %07x, header_ptr:%04x != %04x\\n\",\n\t\t\t\t\tdir_byte, localfile.header_pointer,\n\t\t\t\t\tstart_dir_block);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif(localfile.prodos_name[0] >= 0xd0) {\n\t\t\t\tsub_blocks = localfile.blocks_used;\n\t\t\t\tif(localfile.eof != (sub_blocks * 0x200UL)) {\n\t\t\t\t\tprintf(\"At %07x, eof:%08x != %08x\\n\",\n\t\t\t\t\t\tdir_byte, localfile.eof,\n\t\t\t\t\t\tsub_blocks * 0x200U);\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tret = dynapro_validate_dir(dsk, freeblks_ptr,\n\t\t\t\t\t(localfile.key_block * 0x200) + 4,\n\t\t\t\t\tdir_byte, sub_blocks);\n\t\t\t\tif(ret == 0) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tret = dynapro_validate_file(dsk, freeblks_ptr,\n\t\t\t\t\tlocalfile.key_block, localfile.eof,\n\t\t\t\t\t0x10 | (localfile.prodos_name[0] >> 4));\n\t\t\t\tif(ret == 0) {\n\t\t\t\t\tprintf(\"At %07x, bad file\\n\", dir_byte);\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tif(localfile.blocks_used != ret) {\n\t\t\t\t\tprintf(\"At %07x, blocks_used prodos \"\n\t\t\t\t\t\t\"%04x != %04x calc\\n\", dir_byte,\n\t\t\t\t\t\tlocalfile.blocks_used, ret);\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tis_header = 0;\n\t\tdir_byte = dir_byte + 0x27;\n\t\ttmp_byte = (dir_byte & 0x1ff) + 0x27;\n\t\tif(tmp_byte < 0x200) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttmp_byte = (dir_byte - 0x27) & (0 - 0x200UL);\n\t\tdir_byte = dynapro_get_word16(&bptr[tmp_byte + 2]) * 0x200UL;\n\t\tif(dir_byte == 0) {\n\t\t\tif(act_entries != exp_entries) {\n\t\t\t\tprintf(\"act_entries:%04x != exp:%04x, \"\n\t\t\t\t\t\"dir_block:%04x\\n\", act_entries,\n\t\t\t\t\texp_entries, start_dir_block);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif(blocks_used != exp_blocks_used) {\n\t\t\t\tprintf(\"At dir %07x, blocks_used:%04x!=%04x \"\n\t\t\t\t\t\"exp\\n\", tmp_byte, blocks_used,\n\t\t\t\t\texp_blocks_used);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n\t\tdir_byte += 4;\n\t\tif(dir_byte >= (max_block * 0x200L)) {\n\t\t\tprintf(\" invalid link pointer %07x\\n\", dir_byte);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nint\ndynapro_validate_disk(Disk *dsk)\n{\n\tbyte\tfreeblks[65536/8];\t\t// 8KB\n\tbyte\t*bptr;\n\tword32\tnum_blocks, ret;\n\tword32\tui;\n\n\tnum_blocks = (word32)(dsk->dimage_size >> 9);\n\tprintf(\"******************************\\n\");\n\tprintf(\"Validate disk: %s, blocks:%05x\\n\", dsk->name_ptr, num_blocks);\n\tdynapro_validate_init_freeblks(&freeblks[0], num_blocks);\n\n\t// Validate starting at directory in block 2\n\tret = dynapro_validate_dir(dsk, &freeblks[0], 0x0404, 0, 4);\n\tif(!ret) {\n\t\tprintf(\"Disk does not validate!\\n\");\n\t\texit(1);\n\t\treturn ret;\n\t}\n\n\t// Check freeblks\n\tbptr = &(dsk->raw_data[6*0x200]);\n\tfor(ui = 0; ui < (num_blocks + 7)/8; ui++) {\n\t\tif(freeblks[ui] != bptr[ui]) {\n\t\t\tprintf(\"Expected free mask for blocks %04x-%04x:%02x, \"\n\t\t\t\t\"but it is %02x\\n\", ui*8, ui*8 + 7,\n\t\t\t\tfreeblks[ui], bptr[ui]);\n\t\t\texit(1);\n\t\t\treturn 0;\n\t\t}\n\t}\n\treturn 1;\n}\n\nvoid\ndynapro_validate_any_image(Disk *dsk)\n{\n\tbyte\t*bufptr;\n\tdword64\tdsize;\n\tint\tret;\n\n\t// If dsk->raw_data already set, just use it.  Otherwise, we need to\n\t//  temporarily read in entire image, set it, do validate, and then\n\t//  free it\n\n\tif(dsk->fd < 0) {\n\t\treturn;\t\t// No disk\n\t}\n\tif(dsk->wozinfo_ptr) {\n\t\treturn;\n\t}\n\tdsize = dsk->dimage_size;\n\tbufptr = 0;\n\tif((dsize >> 31) != 0) {\n\t\tprintf(\"Disk is too large, not valid\\n\");\n\t\tret = 0;\n\t} else if(dsk->raw_data == 0) {\n\t\tbufptr = malloc((size_t)dsize);\n\t\tdsk->raw_data = bufptr;\n\t\tcfg_read_from_fd(dsk->fd, bufptr, 0, dsize);\n\t\tret = dynapro_validate_disk(dsk);\n\t\tdsk->raw_data = 0;\n\t\tfree(bufptr);\n\t} else {\n\t\tret = dynapro_validate_disk(dsk);\n\t}\n\tprintf(\"validate_disk returned is_good: %d (0 is bad)\\n\", ret);\n}\n\n"
  },
  {
    "path": "gsplus/src/dynapro.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2021-2022 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// Main information is from Beneath Apple ProDOS which has disk layout\n//  descriptions.  Upper/lowercase is from Technote tn-gsos-008, and\n//  forked files are storage_type $5 from Technote tn-pdos-025.\n\n#include \"defc.h\"\n#ifdef _WIN32\n# include \"win_dirent.h\"\n#else\n# include <dirent.h>\n#endif\n#include <time.h>\n\nextern int Verbose;\n\n#define DYNAPRO_PATH_MAX\t\t2048\nchar g_dynapro_path_buf[DYNAPRO_PATH_MAX];\n\nextern word32 g_vbl_count, g_iwm_dynapro_last_vbl_count;\n\nbyte g_prodos_block0[512] = {\n\t// From Beagle Bros Pro-Byter disk\n\t// This is a ProDOS boot block, able to load PRODOS and jump to it\n\t0x01, 0x38, 0xb0, 0x03, 0x4c, 0x32, 0xa1, 0x86,\t// 0x000\n\t0x43, 0xc9, 0x03, 0x08, 0x8a, 0x29, 0x70, 0x4a,\n\t0x4a, 0x4a, 0x4a, 0x09, 0xc0, 0x85, 0x49, 0xa0,\t// 0x010\n\t0xff, 0x84, 0x48, 0x28, 0xc8, 0xb1, 0x48, 0xd0,\n\t0x3a, 0xb0, 0x0e, 0xa9, 0x03, 0x8d, 0x00, 0x08,\t// 0x020\n\t0xe6, 0x3d, 0xa5, 0x49, 0x48, 0xa9, 0x5b, 0x48,\n\t0x60, 0x85, 0x40, 0x85, 0x48, 0xa0, 0x63, 0xb1,\t// 0x030\n\t0x48, 0x99, 0x94, 0x09, 0xc8, 0xc0, 0xeb, 0xd0,\n\t0xf6, 0xa2, 0x06, 0xbc, 0x1d, 0x09, 0xbd, 0x24,\t// 0x040\n\t0x09, 0x99, 0xf2, 0x09, 0xbd, 0x2b, 0x09, 0x9d,\n\t0x7f, 0x0a, 0xca, 0x10, 0xee, 0xa9, 0x09, 0x85,\t// 0x050\n\t0x49, 0xa9, 0x86, 0xa0, 0x00, 0xc9, 0xf9, 0xb0,\n\t0x2f, 0x85, 0x48, 0x84, 0x60, 0x84, 0x4a, 0x84,\t// 0x060\n\t0x4c, 0x84, 0x4e, 0x84, 0x47, 0xc8, 0x84, 0x42,\n\t0xc8, 0x84, 0x46, 0xa9, 0x0c, 0x85, 0x61, 0x85,\t// 0x070\n\t0x4b, 0x20, 0x12, 0x09, 0xb0, 0x68, 0xe6, 0x61,\n\t0xe6, 0x61, 0xe6, 0x46, 0xa5, 0x46, 0xc9, 0x06,\t// 0x080\n\t0x90, 0xef, 0xad, 0x00, 0x0c, 0x0d, 0x01, 0x0c,\n\t0xd0, 0x6d, 0xa9, 0x04, 0xd0, 0x02, 0xa5, 0x4a,\t// 0x090\n\t0x18, 0x6d, 0x23, 0x0c, 0xa8, 0x90, 0x0d, 0xe6,\n\t0x4b, 0xa5, 0x4b, 0x4a, 0xb0, 0x06, 0xc9, 0x0a,\t// 0x0a0\n\t0xf0, 0x55, 0xa0, 0x04, 0x84, 0x4a, 0xad, 0x02,\n\t0x09, 0x29, 0x0f, 0xa8, 0xb1, 0x4a, 0xd9, 0x02,\t// 0x0b0\n\t0x09, 0xd0, 0xdb, 0x88, 0x10, 0xf6, 0x29, 0xf0,\n\t0xc9, 0x20, 0xd0, 0x3b, 0xa0, 0x10, 0xb1, 0x4a,\t// 0x0c0\n\t0xc9, 0xff, 0xd0, 0x33, 0xc8, 0xb1, 0x4a, 0x85,\n\t0x46, 0xc8, 0xb1, 0x4a, 0x85, 0x47, 0xa9, 0x00,\t// 0x0d0\n\t0x85, 0x4a, 0xa0, 0x1e, 0x84, 0x4b, 0x84, 0x61,\n\t0xc8, 0x84, 0x4d, 0x20, 0x12, 0x09, 0xb0, 0x17,\t// 0x0e0\n\t0xe6, 0x61, 0xe6, 0x61, 0xa4, 0x4e, 0xe6, 0x4e,\n\t0xb1, 0x4a, 0x85, 0x46, 0xb1, 0x4c, 0x85, 0x47,\t// 0x0f0\n\t0x11, 0x4a, 0xd0, 0xe7, 0x4c, 0x00, 0x20, 0x4c,\n\n\t0x3f, 0x09, 0x26, 0x50, 0x52, 0x4f, 0x44, 0x4f,\t// 0x100\n\t0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\n\t0x20, 0x20, 0xa5, 0x60, 0x85, 0x44, 0xa5, 0x61,\t// 0x110\n\t0x85, 0x45, 0x6c, 0x48, 0x00, 0x08, 0x1e, 0x24,\n\t0x3f, 0x45, 0x47, 0x76, 0xf4, 0xd7, 0xd1, 0xb6,\t// 0x120\n\t0x4b, 0xb4, 0xac, 0xa6, 0x2b, 0x18, 0x60, 0x4c,\n\t0xbc, 0x09, 0xa9, 0x9f, 0x48, 0xa9, 0xff, 0x48,\t// 0x130\n\t0xa9, 0x01, 0xa2, 0x00, 0x4c, 0x79, 0xf4, 0x20,\n\t0x58, 0xfc, 0xa0, 0x1c, 0xb9, 0x50, 0x09, 0x99,\t// 0x140\n\t0xae, 0x05, 0x88, 0x10, 0xf7, 0x4c, 0x4d, 0x09,\n\t0xaa, 0xaa, 0xaa, 0xa0, 0xd5, 0xce, 0xc1, 0xc2,\t// 0x150\n\t0xcc, 0xc5, 0xa0, 0xd4, 0xcf, 0xa0, 0xcc, 0xcf,\n\t0xc1, 0xc4, 0xa0, 0xd0, 0xd2, 0xcf, 0xc4, 0xcf,\t// 0x160\n\t0xd3, 0xa0, 0xaa, 0xaa, 0xaa, 0xa5, 0x53, 0x29,\n\t0x03, 0x2a, 0x05, 0x2b, 0xaa, 0xbd, 0x80, 0xc0,\t// 0x170\n\t0xa9, 0x2c, 0xa2, 0x11, 0xca, 0xd0, 0xfd, 0xe9,\n\t0x01, 0xd0, 0xf7, 0xa6, 0x2b, 0x60, 0xa5, 0x46,\t// 0x180\n\t0x29, 0x07, 0xc9, 0x04, 0x29, 0x03, 0x08, 0x0a,\n\t0x28, 0x2a, 0x85, 0x3d, 0xa5, 0x47, 0x4a, 0xa5,\t// 0x190\n\t0x46, 0x6a, 0x4a, 0x4a, 0x85, 0x41, 0x0a, 0x85,\n\t0x51, 0xa5, 0x45, 0x85, 0x27, 0xa6, 0x2b, 0xbd,\t// 0x1a0\n\t0x89, 0xc0, 0x20, 0xbc, 0x09, 0xe6, 0x27, 0xe6,\n\t0x3d, 0xe6, 0x3d, 0xb0, 0x03, 0x20, 0xbc, 0x09,\t// 0x1b0\n\t0xbc, 0x88, 0xc0, 0x60, 0xa5, 0x40, 0x0a, 0x85,\n\t0x53, 0xa9, 0x00, 0x85, 0x54, 0xa5, 0x53, 0x85,\t// 0x1c0\n\t0x50, 0x38, 0xe5, 0x51, 0xf0, 0x14, 0xb0, 0x04,\n\t0xe6, 0x53, 0x90, 0x02, 0xc6, 0x53, 0x38, 0x20,\t// 0x1d0\n\t0x6d, 0x09, 0xa5, 0x50, 0x18, 0x20, 0x6f, 0x09,\n\t0xd0, 0xe3, 0xa0, 0x7f, 0x84, 0x52, 0x08, 0x28,\t// 0x1e0\n\t0x38, 0xc6, 0x52, 0xf0, 0xce, 0x18, 0x08, 0x88,\n\t0xf0, 0xf5, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0x00,\t// 0x1f0\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\n};\n\nword32\ndynapro_get_word32(byte *bptr)\n{\n\treturn (bptr[3] << 24) | (bptr[2] << 16) | (bptr[1] << 8) | bptr[0];\n}\n\nword32\ndynapro_get_word24(byte *bptr)\n{\n\treturn (bptr[2] << 16) | (bptr[1] << 8) | bptr[0];\n}\n\nword32\ndynapro_get_word16(byte *bptr)\n{\n\treturn (bptr[1] << 8) | bptr[0];\n}\n\nvoid\ndynapro_set_word24(byte *bptr, word32 val)\n{\n\t// Write 3 bytes in little-endian form\n\t*bptr++ = val;\n\t*bptr++ = (val >> 8);\n\t*bptr++ = (val >> 16);\n}\n\nvoid\ndynapro_set_word32(byte *bptr, word32 val)\n{\n\t// Write 4 bytes in little-endian form\n\t*bptr++ = val;\n\t*bptr++ = (val >> 8);\n\t*bptr++ = (val >> 16);\n\t*bptr++ = (val >> 24);\n}\n\nvoid\ndynapro_set_word16(byte *bptr, word32 val)\n{\n\t// Write 2 bytes in little-endian form\n\t*bptr++ = val;\n\t*bptr++ = (val >> 8);\n}\n\nvoid\ndynapro_error(Disk *dsk, const char *fmt, ...)\n{\n\tDynapro_info *info_ptr;\n\tva_list\targs;\n\n\tva_start(args, fmt);\n\tcfg_err_vprintf(\"Dynapro\", fmt, args);\n\tva_end(args);\n\tinfo_ptr = dsk->dynapro_info_ptr;\n\tif(info_ptr) {\n\t\tcfg_err_printf(\"\", \"Path: %s\\n\", info_ptr->root_path);\n\t}\n}\n\nDynapro_file *\ndynapro_alloc_file()\n{\n\tDynapro_file *fileptr;\n\n\tfileptr = calloc(1, sizeof(Dynapro_file));\n\treturn fileptr;\n}\n\nvoid\ndynapro_free_file(Dynapro_file *fileptr, int check_map)\n{\n\tif(!fileptr) {\n\t\treturn;\n\t}\n\tif(fileptr->subdir_ptr) {\n\t\tdynapro_free_recursive_file(fileptr->subdir_ptr, check_map);\n\t}\n\tfileptr->subdir_ptr = 0;\n\tfree(fileptr->unix_path);\n\tfileptr->unix_path = 0;\n\tfree(fileptr->buffer_ptr);\n\tfileptr->buffer_ptr = 0;\n\tfileptr->next_ptr = 0;\n\t// printf(\"FREE %p\\n\", fileptr);\n\tif(check_map && (fileptr->map_first_block != 0)) {\n\t\tprintf(\" ERROR: map_first_block is %08x\\n\",\n\t\t\t\t\t\tfileptr->map_first_block);\n\t\texit(1);\n\t}\n\tfree(fileptr);\n}\n\nvoid\ndynapro_free_recursive_file(Dynapro_file *fileptr, int check_map)\n{\n\tDynapro_file *nextptr;\n\n\tif(!fileptr) {\n\t\treturn;\n\t}\n\t// printf(\"free_recursive %s\\n\", fileptr->unix_path);\n\twhile(fileptr) {\n\t\tnextptr = fileptr->next_ptr;\n\t\tdynapro_free_file(fileptr, check_map);\n\t\tfileptr = nextptr;\n\t};\n}\n\nvoid\ndynapro_free_dynapro_info(Disk *dsk)\n{\n\tDynapro_info *info_ptr;\n\n\tinfo_ptr = dsk->dynapro_info_ptr;\n\tif(info_ptr) {\n\t\tfree(info_ptr->root_path);\n\n\t\tdynapro_free_recursive_file(info_ptr->volume_ptr, 0);\n\t\tinfo_ptr->volume_ptr = 0;\n\t}\n\tfree(info_ptr);\n\tdsk->dynapro_info_ptr = 0;\n}\n\nword32\ndynapro_find_free_block_internal(Disk *dsk)\n{\n\tbyte\t*bptr;\n\tword32\tnum_blocks, bitmap_size_bytes, val, mask;\n\tword32\tui;\n\tint\tj;\n\n\tnum_blocks = (word32)(dsk->raw_dsize >> 9);\n\tbitmap_size_bytes = (num_blocks + 7) >> 3;\n\tbptr = &(dsk->raw_data[6 * 512]);\t\t// Block 6\n\tfor(ui = 0; ui < bitmap_size_bytes; ui++) {\n\t\tval = bptr[ui];\n\t\tif(val == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tmask = 0x80;\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\tif(val & mask) {\n\t\t\t\tbptr[ui] = val & (~mask);\n\t\t\t\treturn 8*ui + j;\n\t\t\t}\n\t\t\tmask = mask >> 1;\n\t\t}\n\t\treturn 0;\n\t}\n\treturn 0;\n}\n\nword32\ndynapro_find_free_block(Disk *dsk)\n{\n\tbyte\t*bptr;\n\tword32\tblock;\n\tint\ti;\n\n\t// Find first free block, and zero it out\n\n\tblock = dynapro_find_free_block_internal(dsk);\n\tif(block == 0) {\n\t\treturn 0;\n\t}\n\tbptr = &(dsk->raw_data[block * 512]);\n\tfor(i = 0; i < 512; i++) {\n\t\tbptr[i] = 0;\n\t}\n\n\treturn block;\n}\n\nbyte *\ndynapro_malloc_file(char *path_ptr, dword64 *dsize_ptr, int extra_size)\n{\n\tbyte\t*bptr;\n\tdword64\tdsize, dpos;\n\tint\tfd;\n\tint\ti;\n\n\t*dsize_ptr = 0;\n\tfd = open(path_ptr, O_RDONLY | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\treturn 0;\n\t}\n\tdsize = cfg_get_fd_size(fd);\n\n\tif((size_t)(dsize + extra_size) != (dsize + extra_size)) {\n\t\treturn 0;\n\t}\n\tbptr = malloc((size_t)(dsize + extra_size));\n\tif(bptr == 0) {\n\t\treturn bptr;\n\t}\n\t// printf(\"dynapro_malloc_file %p, size:%08lld\\n\", bptr, dsize);\n\tfor(i = 0; i < extra_size; i++) {\n\t\tbptr[dsize + i] = 0;\n\t}\n\tdpos = cfg_read_from_fd(fd, bptr, 0, dsize);\n\tclose(fd);\n\tif(dpos != dsize) {\n\t\tfree(bptr);\n\t\treturn 0;\n\t}\n\t*dsize_ptr = dsize;\n\treturn bptr;\n}\n\nvoid\ndynapro_join_path_and_file(char *outstr, const char *unix_path, const char *str,\n\t\t\t\t\t\tint path_max)\n{\n\tint\tlen;\n\n\t// Create \"unix_path\" + \"/\" + \"str\" in outstr (which has size path_max)\n\tcfg_strncpy(outstr, unix_path, path_max);\n\tlen = (int)strlen(outstr);\n\tif((len > 0) && (outstr[len - 1] != '/')) {\n\t\tcfg_strlcat(outstr, \"/\", path_max);\n\t}\n\tcfg_strlcat(outstr, str, path_max);\n}\n\n\nword32\ndynapro_fill_fileptr_from_prodos(Disk *dsk, Dynapro_file *fileptr,\n\t\t\t\tchar *buf32_ptr, word32 dir_byte)\n{\n\tbyte\t*bptr;\n\tword32\tupper_lower;\n\tint\tlen, c;\n\tint\ti;\n\n\tbuf32_ptr[0] = 0;\n\tif((dir_byte < 0x400) || (dir_byte >= dsk->dimage_size)) {\n\t\treturn 0;\t\t\t// Directory is damaged\n\t}\n\tif(!fileptr) {\n\t\treturn 0;\n\t}\n\tbptr = &(dsk->raw_data[dir_byte]);\n\tmemset(fileptr, 0, sizeof(Dynapro_file));\n\n\tfileptr->dir_byte = dir_byte;\n\tfileptr->file_type = bptr[0x10];\n\tfileptr->key_block = dynapro_get_word16(&bptr[0x11]);\n\tfileptr->blocks_used = dynapro_get_word16(&bptr[0x13]);\n\tfileptr->eof = dynapro_get_word24(&bptr[0x15]);\n\t//printf(\"Filling from entry %07x, eof:%06x\\n\", dir_byte, fileptr->eof);\n\tfileptr->creation_time = dynapro_get_word32(&bptr[0x18]);\n\tfileptr->upper_lower = dynapro_get_word16(&bptr[0x1c]);\n\tfileptr->aux_type = dynapro_get_word16(&bptr[0x1f]);\n\tfileptr->lastmod_time = dynapro_get_word32(&bptr[0x21]);\n\tfileptr->header_pointer = dynapro_get_word16(&bptr[0x25]);\n\tif(dir_byte == 0x404) {\t\t\t// Volume header\n\t\tfileptr->upper_lower = dynapro_get_word32(&bptr[0x1a]);\n\t\tfileptr->creation_time &= 0xffff;\n\t}\n\n\tlen = (bptr[0] & 0xf) + 1;\n\tupper_lower = fileptr->upper_lower;\n\tif((upper_lower & 0x8000) == 0) {\t\t// Not valid\n\t\tupper_lower = 0;\n\t}\n\tfor(i = 0; i < 16; i++) {\n\t\tc = bptr[i];\n\t\tif(i > len) {\n\t\t\tc = 0;\n\t\t}\n\t\tfileptr->prodos_name[i] = c;\n\t\tif(i > 0) {\n\t\t\tif(upper_lower & 0x4000) {\n\t\t\t\tif((c >= 'A') && (c <= 'Z')) {\n\t\t\t\t\tc = c - 'A' + 'a';\t// Make lower\n\t\t\t\t}\n\t\t\t}\n\t\t\tupper_lower = upper_lower << 1;\n\t\t\tbuf32_ptr[i - 1] = c;\n\t\t\tbuf32_ptr[i] = 0;\n\t\t}\n\t}\n\tif(((bptr[0] & 0xf0) == 0) || ((bptr[0] & 0xf) == 0)) {\n\t\tfileptr->prodos_name[0] = 0;\n\t\treturn 1;\t\t// Invalid entry\n\t}\n\tif(fileptr->prodos_name[0] >= 0xe0) {\t\t// Dir/Volume header\n\t\tfileptr->key_block = dir_byte >> 9;\n\t\tif((dir_byte & 0x1ff) != 4) {\n\t\t\tprintf(\"Header at dir_byte:%07x != 4\\n\", dir_byte);\n\t\t\treturn 0;\t\t\t// Not in first pos\n\t\t}\n\t\tif(bptr[-4] || bptr[-3]) {\n\t\t\tprintf(\"prev_link %02x,%02x should be 0\\n\",\n\t\t\t\t\t\tbptr[-4], bptr[-3]);\n\t\t\treturn 0;\t\t\t// Not first dir block\n\t\t}\n\t\tif(fileptr->prodos_name[0] >= 0xf0) {\n\t\t\tif(dir_byte != 0x0404) {\n\t\t\t\tprintf(\"Volume head dir_byte:%07x\\n\", dir_byte);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t} else if(dir_byte == 0x0404) {\n\t\t\tprintf(\"Directory head dir_byte 0x0404\\n\");\n\t\t\treturn 0;\t\t\t// 0xe0 in block 2->bad\n\t\t}\n\t} else {\n\t\t// Normal entry.  Make sure it's not the first entry in a dir\n\t\tif((bptr[-4] == 0) && (bptr[-3] == 0) &&\n\t\t\t\t\t\t((dir_byte & 0x1ff) == 4)) {\n\t\t\tprintf(\"dir_byte:%07x, normal, prev:0\\n\", dir_byte);\n\t\t\treturn 0;\t\t// This is a dir/volume header!\n\t\t}\n\t}\n#if 0\n\tprintf(\"Fill resulted in buf32:%s, upper_lower:%04x\\n\", buf32_ptr,\n\t\t\t\t\t\tfileptr->upper_lower);\n#endif\n\n\treturn 2;\t\t// OK\n}\n\nword32\ndynapro_diff_fileptrs(Dynapro_file *oldfileptr, Dynapro_file *newfileptr)\n{\n\tword32\tret, new_storage, old_storage;\n\tint\ti;\n\n\t// Return 0 if the directory is damaged\n\t// Return 1 if the entry is invalid (and not case 3!)\n\t// Return 3 if the entry was valid and is now deleted\n\t// Return 4 if no changes are needed\n\t// Return 5 if oldfileptr needs to be rewritten\n\t// Return 7 if oldfileptr needs to be erased and replaced with newfile\n\n\told_storage = oldfileptr->prodos_name[0];\n\tnew_storage = newfileptr->prodos_name[0];\n\tif(new_storage == 0) {\t\t\t\t// Erased\n\t\tif(old_storage >= 0xe0) {\t// Vol/Dir header\n\t\t\treturn 0;\n\t\t}\n\t\tif(oldfileptr->dir_byte == newfileptr->dir_byte) {\n\t\t\treturn 3;\t// Entry just deleted\n\t\t}\n\t\treturn 1;\t\t// Just an invalid entry\n\t}\n\tif(oldfileptr->dir_byte != newfileptr->dir_byte) {\n\t\treturn 0;\n\t}\n\tret = 4;\t\t// No changes needed\n\n\t// Handle file expanding from seedling to tree\n\tif((new_storage >= 0x10) && (new_storage < 0x40) &&\n\t\t\t(old_storage >= 0x10) && (old_storage < 0x40)) {\n\t\t// Copy upper 4 bits from new_storage to old_storage\n\t\told_storage = (old_storage & 0x0f) | (new_storage & 0xf0);\n\t\tif(oldfileptr->prodos_name[0] != old_storage) {\n\t\t\t// Storage type changed, rewrite the file\n\t\t\toldfileptr->prodos_name[0] = old_storage;\n\t\t\tret |= 5;\n\t\t}\n\t}\n\tfor(i = 0; i < 16; i++) {\n\t\tif(oldfileptr->prodos_name[i] != newfileptr->prodos_name[i]) {\n\t\t\tret |= 7;\t\t// Name changed\n\t\t}\n\t\toldfileptr->prodos_name[i] = newfileptr->prodos_name[i];\n\t}\n\n\tif(oldfileptr->file_type != newfileptr->file_type) {\n\t\tret |= 7;\t\t// Filetype changed\n\t\toldfileptr->file_type = newfileptr->file_type;\n\t}\n\tif(newfileptr->prodos_name[0] < 0xe0) {\n\t\t// Not a directory or volume header\n\t\tif(oldfileptr->key_block != newfileptr->key_block) {\n\t\t\tret |= 5;\t\t// Key block has changed\n\t\t\toldfileptr->key_block = newfileptr->key_block;\n\t\t}\n\t\tif(oldfileptr->blocks_used != newfileptr->blocks_used) {\n\t\t\t// ret stays 1, we don't care about this field\n\t\t\toldfileptr->blocks_used = newfileptr->blocks_used;\n\t\t}\n\t\tif(oldfileptr->eof != newfileptr->eof) {\n\t\t\tret |= 5;\t\t// eof has changed\n\t\t\toldfileptr->eof = newfileptr->eof;\n\t\t}\n\t} else {\n\t\t// Directory or volume header\n\t\t// Ignore key_block (used internally by dynapro.c, but not in\n\t\t//  the ProDOS disk image), blocks_used, eof.\n\t\t// We ignore file_count at +0x21,0x22.  But bitmap_ptr matters\n\t\t//  and if it moves, we are damaged\n\t\tif((oldfileptr->lastmod_time >> 16) !=\n\t\t\t\t\t(newfileptr->lastmod_time >> 16)) {\n\t\t\treturn 0;\t// Bitmap_ptr moved, we are damaged\n\t\t}\n\t}\n\tif(oldfileptr->upper_lower != newfileptr->upper_lower) {\n\t\tret |= 7;\t\t// lowercase flags have changed\n\t\toldfileptr->upper_lower = newfileptr->upper_lower;\n\t}\n\tif(oldfileptr->aux_type != newfileptr->aux_type) {\n\t\tret |= 5;\t\t// aux_type has changed\n\t\toldfileptr->aux_type = newfileptr->aux_type;\n\t}\n\tif(oldfileptr->header_pointer != newfileptr->header_pointer) {\n\t\treturn 0;\t\t// We are damaged\n\t}\n\tif(newfileptr->prodos_name[0] >= 0xe0) {\n\t\tif(ret > 5) {\n\t\t\tret = 5;\t// No renaming volume or dir headers\n\t\t}\n\t}\n\treturn ret;\n}\n\nword32\ndynapro_do_one_dir_entry(Disk *dsk, Dynapro_file *fileptr,\n\t\tDynapro_file *localfile_ptr, char *buf32_ptr, word32 dir_byte)\n{\n\tword32\tret, diffs;\n\n\tret = dynapro_fill_fileptr_from_prodos(dsk, localfile_ptr,\n\t\t\t\t\t\tbuf32_ptr, dir_byte);\n\tif((ret == 0) || ((ret == 1) && !fileptr)) {\n\t\treturn ret;\t\t// Damaged or not valid\n\t}\n\tif(!fileptr) {\n\t\treturn 2;\t\t// must allocate new\n\t}\n\n\t// Now, head_ptr must be non-null\n\tdiffs = dynapro_diff_fileptrs(fileptr, localfile_ptr);\n\treturn diffs;\n}\n\nvoid\ndynapro_fix_damaged_entry(Disk *dsk, Dynapro_file *fileptr)\n{\n\tif(fileptr->prodos_name[0] >= 0xe0) {\n\t\t// This is a volume/directory header.  Re-parse entire dir\n\t\tdynapro_handle_write_dir(dsk, fileptr->parent_ptr, fileptr,\n\t\t\t\t\t(fileptr->key_block * 0x200UL) + 4);\n\t} else if(fileptr->prodos_name[0] >= 0xd0) {\n\t\t// This is a directory entry.\n\t\tdynapro_handle_write_dir(dsk, fileptr, fileptr->subdir_ptr,\n\t\t\t\t\t(fileptr->key_block * 0x200UL) + 4);\n\t} else {\n\t\tdynapro_handle_write_file(dsk, fileptr);\n\t}\n}\n\nvoid\ndynapro_try_fix_damage(Disk *dsk, Dynapro_file *fileptr)\n{\n\t// Walk entire tree (recursing to dynapro_try_fix_damage)\n\tif(!fileptr) {\n\t\treturn;\n\t}\n\twhile(fileptr) {\n\t\tif(fileptr->damaged) {\n\t\t\tdyna_printf(\"try_fix_damage %p %s\\n\", fileptr,\n\t\t\t\t\t\t\tfileptr->unix_path);\n\t\t\tdynapro_fix_damaged_entry(dsk, fileptr);\n\t\t}\n\t\tdynapro_try_fix_damage(dsk, fileptr->subdir_ptr);\n\t\tfileptr = fileptr->next_ptr;\n\t}\n}\n\nvoid\ndynapro_try_fix_damaged_disk(Disk *dsk)\n{\n\tDynapro_info *info_ptr;\n\n\tinfo_ptr = dsk->dynapro_info_ptr;\n\tif(!info_ptr) {\t\t\t\t// This is impossible\n\t\treturn;\n\t}\n\tif(info_ptr->damaged == 0) {\n\t\treturn;\n\t}\n\n\tdyna_printf(\"************************************\\n\");\n\tdyna_printf(\"try_fix_damaged_dsk called, damaged:%d\\n\",\n\t\t\t\t\t\t\tinfo_ptr->damaged);\n\tdyna_printf(\" vbl_count:%d, g_iwm_dynapro_last_vbl_count:%d\\n\",\n\t\tg_vbl_count, g_iwm_dynapro_last_vbl_count);\n\n\tinfo_ptr->damaged = 0;\n\tdynapro_try_fix_damage(dsk, info_ptr->volume_ptr);\n\n\tdyna_printf(\"try_fix_damaged_dsk, damaged:%d\\n\", info_ptr->damaged);\n}\n\n\nvoid\ndynapro_new_unix_path(Dynapro_file *fileptr, const char *path_str,\n\t\t\t\t\t\tconst char *name_str)\n{\n\tif(fileptr->unix_path) {\n\t\tfree(fileptr->unix_path);\n\t}\n\tdynapro_join_path_and_file(&g_dynapro_path_buf[0], path_str, name_str,\n\t\t\t\t\t\t\tDYNAPRO_PATH_MAX);\n\tdynatype_fix_unix_name(fileptr, &g_dynapro_path_buf[0],\n\t\t\t\t\t\t\tDYNAPRO_PATH_MAX);\n\tfileptr->unix_path = kegs_malloc_str(&g_dynapro_path_buf[0]);\n}\n\nDynapro_file *\ndynapro_process_write_dir(Disk *dsk, Dynapro_file *parent_ptr,\n\t\t\t\tDynapro_file **head_ptr_ptr, word32 dir_byte)\n{\n\tchar\tbuf32[32];\n\tDynapro_file localfile;\n\tDynapro_file *fileptr, *prev_ptr, *head_ptr;\n\tbyte\t*bptr;\n\tconst char *str;\n\tword32\ttmp_byte, prev, next, ret, last_block, parent_dir_byte;\n\tint\tcnt, error, iret, is_header;\n\n\thead_ptr = *head_ptr_ptr;\t\t// head_ptr_ptr must be valid\n\t\t\t\t\t\t//  but head_ptr can be 0\n\t// We can be called with parent_ptr=0, head_ptr != 0: this is for the\n\t//  volume header.  Otherwise, parent_ptr should be valid.\n\t// If head_ptr==0, it means we need to allocate directory header and\n\t//  all other dir entries\n\t// head_ptr is a pointer to a directory or volume header.\n\t// For all entries, see if anything changed.  We need to also\n\t//  possibly update head_ptr->parent_ptr\n\t// Return 0 if the directory is damaged.  If directory only contains\n\t//  damaged files, try to fix them, and always return success\n\n\t// First, unmap the directory blocks (this is done even if nothing\n\t//  changed, we'll map them back at the end).\n\tif(head_ptr) {\n\t\tdynapro_unmap_file(dsk, head_ptr);\n\t}\n\n\tparent_dir_byte = 0;\n\tif(parent_ptr) {\n\t\tstr = parent_ptr->unix_path;\n\t\tparent_dir_byte = parent_ptr->dir_byte;\n\t\tif(head_ptr == 0) {\n\t\t\t// Do mkdir to make sure it exists\n#ifdef _WIN32\n\t\t\tiret = _mkdir(str);\n#else\n\t\t\tiret = mkdir(str, 0x1ff);\n#endif\n\t\t\terror = errno;\n\t\t\tdyna_printf(\"Did mkdir %s, iret:%d\\n\", str, iret);\n\t\t\tif(iret < 0) {\n\t\t\t\tif((error == EEXIST) || (error == EISDIR)) {\n\t\t\t\t\terror = 0;\t// These are OK errors\n\t\t\t\t}\n\t\t\t\tif(error) {\n\t\t\t\t\tprintf(\"mkdir(%s) failed, error=%d\\n\",\n\t\t\t\t\t\t\tstr, error);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tstr = head_ptr->unix_path;\t\t// volume header\n\t}\n#if 0\n\tprintf(\"process_write_dir str:%s %p parent:%p\\n\", str, head_ptr,\n\t\t\t\t\t\t\t\tparent_ptr);\n#endif\n\n\t// The directory blocks have already been unmapped\n\n\t// Then, walk the directory, noting if anything changed.  If new\n\t//  files appear in the directory, add then to the chain.  We may need\n\t//  to erase existing entries which no longer exist (or their directory\n\t//  entry was changed to a different file)\n\tbptr = dsk->raw_data;\n\tprev_ptr = 0;\n\tfileptr = head_ptr;\n\tlast_block = 0;\n\tcnt = 0;\n\tis_header = 1;\n\twhile(dir_byte) {\n\t\t//printf(\"process_write_dir, dir_byte:%07x, prev_ptr:%p\\n\",\n\t\t//\t\t\tdir_byte, prev_ptr);\n\t\tif((dir_byte & 0x1ff) == 4) {\n\t\t\t// First entry in this block: check prev/next\n\t\t\ttmp_byte = dir_byte & -0x200;\t\t// Block align\n\t\t\tprev = dynapro_get_word16(&bptr[tmp_byte + 0]);\n\t\t\tnext = dynapro_get_word16(&bptr[tmp_byte + 2]);\n\t\t\tif((prev != last_block) ||\n\t\t\t\t\t(next >= (dsk->raw_dsize >> 9))) {\n\t\t\t\t// This is a damaged directory\n\t\t\t\tprintf(\"dir %s is damaged in the link fields\\n\",\n\t\t\t\t\t\t\tstr);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tlast_block = dir_byte >> 9;\n\t\t}\n\t\tif(cnt++ >= 65536) {\n\t\t\tprintf(\"dir %s has a loop in block pointers\\n\",\n\t\t\t\t\t\t\thead_ptr->unix_path);\n\t\t\treturn 0;\n\t\t}\n\n\t\tret = dynapro_do_one_dir_entry(dsk, fileptr, &localfile,\n\t\t\t\t\t\t\t&buf32[0], dir_byte);\n#if 0\n\t\tprintf(\" do_one_dir_entry ret:%08x fileptr:%p, &localfile:%p\\n\",\n\t\t\t\tret, fileptr, &localfile);\n#endif\n\t\tif((ret == 7) && !is_header) {\t// Entry dramatically changed\n\t\t\t// Erase this file\n\t\t\tdynapro_mark_damaged(dsk, fileptr);\n\t\t}\n\t\tif(ret == 0) {\n\t\t\treturn 0;\n\t\t} else if((ret == 1) || (ret == 3)) {\n\t\t\tif((ret == 3) && fileptr) {\n\t\t\t\t// This entry was valid and is now deleted.\n\t\t\t\t//  Erase it right now and fix links\n\t\t\t\tdyna_printf(\"fileptr %p deleted\\n\", fileptr);\n\t\t\t\tprev_ptr->next_ptr = fileptr->next_ptr;\n\t\t\t\tdynapro_erase_free_entry(dsk, fileptr);\n\t\t\t}\n\t\t\tif(head_ptr == 0) {\n\t\t\t\tprintf(\"return, head_ptr==0, deleted file at \"\n\t\t\t\t\t\t\t\"%07x\\n\", dir_byte);\n\t\t\t\treturn 0;\t\t// Directory damaged\n\t\t\t}\n\t\t\tfileptr = prev_ptr;\n\t\t}\n\t\tif(ret == 2) {\n\t\t\t// prev_ptr->next_ptr is 0, this is a new entry we\n\t\t\t//  need to put on the list\n\t\t\tif(fileptr) {\n\t\t\t\thalt_printf(\"file %s was ignored!\\n\",\n\t\t\t\t\t\t\tfileptr->unix_path);\n\t\t\t\texit(1);\n\t\t\t}\n\t\t\tfileptr = dynapro_alloc_file();\n\t\t\tif(!fileptr) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\t*fileptr = localfile;\t\t// STRUCT copy!\n\t\t\tdyna_printf(\"Allocated new fileptr:%p\\n\", fileptr);\n\t\t\tfileptr->parent_ptr = parent_ptr;\n\t\t\tif(head_ptr) {\n\t\t\t\tfileptr->parent_ptr = head_ptr;\n\t\t\t}\n\t\t}\n\t\tif((ret == 2) || (ret == 7)) {\n\t\t\t// New entry, or dramatically changed, update path\n\t\t\tif(!head_ptr) {\n\t\t\t\tif(!parent_ptr) {\n\t\t\t\t\tprintf(\"parent_ptr is 0!\\n\");\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tparent_ptr->subdir_ptr = fileptr;\n\t\t\t\tprintf(\"2/7 set %p %s subdir=%p\\n\", parent_ptr,\n\t\t\t\t\tparent_ptr->unix_path, fileptr);\n\t\t\t\tfileptr->unix_path = kegs_malloc_str(str);\n\t\t\t\thead_ptr = fileptr;\n\t\t\t\t*head_ptr_ptr = head_ptr;\n\t\t\t} else {\n\t\t\t\tdyna_printf(\"Forming new path: %s buf32:%s\\n\",\n\t\t\t\t\t\t\t\tstr, buf32);\n\t\t\t\tdynapro_new_unix_path(fileptr, str, buf32);\n\t\t\t}\n\t\t\t// If we are a directory entry (fileptr->subdir_ptr!=0)\n\t\t\t//  then now fileptr->unix_path != subdirptr->unix_path\n\t\t\t// The subdir will be erased in\n\t\t\t//  dynapro_handle_changed_entry()\n\t\t\tif(prev_ptr) {\n\t\t\t\tprev_ptr->next_ptr = fileptr;\n\t\t\t}\n\t\t}\n\t\tif((ret == 5) || (ret == 2) || (ret == 7)) {\n\t\t\t// Changed, or new entry\n\t\t\tif(is_header) {\n\t\t\t\tret = dynapro_validate_header(dsk, fileptr,\n\t\t\t\t\t\tdir_byte, parent_dir_byte);\n\t\t\t\tif(ret == 0) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdynapro_handle_changed_entry(dsk, fileptr);\n\t\t\t}\n\t\t}\n\t\tprev_ptr = fileptr;\n\t\tfileptr = prev_ptr->next_ptr;\n\t\tdir_byte = dir_byte + 0x27;\n\t\ttmp_byte = (dir_byte & 0x1ff) + 0x27;\n\t\tis_header = 0;\n\t\tif(tmp_byte < 0x200) {\n\t\t\tcontinue;\n\t\t}\n\t\ttmp_byte = (dir_byte - 0x27) & (0 - 0x200UL);\n\t\tdir_byte = dynapro_get_word16(&bptr[tmp_byte + 2]) * 0x200UL;\n\t\tdyna_printf(\" dir link at %07x = %04x\\n\", tmp_byte + 2,\n\t\t\t\t\t\t\t\tdir_byte);\n\t\tif(dir_byte == 0) {\n\t\t\tif(fileptr) {\n\t\t\t\tprintf(\"At dir end, fileptr: %p\\n\", fileptr);\n\t\t\t\tprev_ptr->next_ptr = 0;\n\t\t\t\tdynapro_erase_free_dir(dsk, fileptr);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tret = dynapro_map_dir_blocks(dsk, head_ptr);\n\t\t\t// printf(\"process_write_dir %s done, remap ret:%d\\n\",\n\t\t\t//\t\t\thead_ptr->unix_path, ret);\n\t\t\tif(ret == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn head_ptr;\t// Success\n\t\t}\n\t\tdir_byte += 4;\n\t\tif(dir_byte >= dsk->dimage_size) {\n\t\t\t// printf(\" invalid link pointer, dir_byte:%08x\\n\",\n\t\t\t//\t\t\tdir_byte);\n\t\t\treturn 0;\t\t// Bad link, get out\n\t\t}\n\t}\n\tdyna_printf(\"At end of process_write_dir, returning 0\\n\");\n\treturn 0;\n}\n\nvoid\ndynapro_handle_write_dir(Disk *dsk, Dynapro_file *parent_ptr,\n\t\t\t\tDynapro_file *head_ptr, word32 dir_byte)\n{\n\tDynapro_file *fileptr;\n\n\t//save_headptr = head_ptr;\n\tdyna_printf(\"handle_write_dir parent_ptr:%p, head_ptr:%p, dir_b:%07x\\n\",\n\t\t\tparent_ptr, head_ptr, dir_byte);\n\tif(parent_ptr && head_ptr) {\n\t\tdyna_printf(\" parent:%s head:%s\\n\", parent_ptr->unix_path,\n\t\t\t\t\t\t\thead_ptr->unix_path);\n\t\tif(parent_ptr->subdir_ptr != head_ptr) {\n\t\t\tprintf(\"parent subdir:%p does not match %p\\n\",\n\t\t\t\tparent_ptr->subdir_ptr, head_ptr);\n\t\t\texit(1);\n\t\t}\n\t}\n\tif(parent_ptr == 0) {\n\t\tif(head_ptr != dsk->dynapro_info_ptr->volume_ptr) {\n\t\t\tprintf(\"handle_write_dir %p %p %07x\\n\", parent_ptr,\n\t\t\t\t\t\thead_ptr, dir_byte);\n\t\t\texit(1);\n\t\t}\n\t}\n\tfileptr = dynapro_process_write_dir(dsk, parent_ptr, &head_ptr,\n\t\t\t\t\t\t\t\tdir_byte);\n\tif(fileptr == 0) {\n\t\t// Directory is damaged.  Free and erase it\n\t\tdynapro_erase_free_dir(dsk, head_ptr);\n\t\thead_ptr = 0;\n\t}\n\t//printf(\"handle_write_dir, process returned %p (was %p), parent:%p, \"\n\t//\t\"save:%p\\n\", fileptr, head_ptr, parent_ptr, save_headptr);\n\tif(parent_ptr) {\n\t\tparent_ptr->subdir_ptr = head_ptr;\n\t\tif(!fileptr) {\n\t\t\tparent_ptr->damaged = 1;\n\t\t\tdsk->dynapro_info_ptr->damaged = 1;\n\t\t}\n\t\tdyna_printf(\"hwd set parent %p %s subdir=%p\\n\", parent_ptr,\n\t\t\tparent_ptr->unix_path, head_ptr);\n\t}\n}\n\nword32\ndynapro_process_write_file(Disk *dsk, Dynapro_file *fileptr)\n{\n\tword32\tret;\n\n\t//printf(\"dynapro_process_write_file %p %s\\n\", fileptr,\n\t//\t\t\t\t\t\tfileptr->unix_path);\n\n\tif(fileptr->subdir_ptr) {\n\t\tprintf(\"dynapro_handle_write_file, has subdir: %s\\n\",\n\t\t\tfileptr->unix_path);\n\t\thalt_printf(\"dynapro_handle_write_file, has subdir: %s\\n\",\n\t\t\tfileptr->unix_path);\n\t\treturn 0;\n\t}\n\n\t// First, unmap the file (the sapling/tree blocks may have changed).\n\tdynapro_unmap_file(dsk, fileptr);\n\n\t// Then remap the blocks. This will copy the new data to buffer_ptr\n\n\tdynapro_debug_map(dsk, \"handle_write_file, right before re-map\");\n\n\tret = dynapro_map_file(dsk, fileptr, 1);\n\n\t// printf(\"dynapro_handle_write_file ending, ret:%d\\n\", ret);\n\n\treturn ret;\n}\n\nvoid\ndynapro_handle_write_file(Disk *dsk, Dynapro_file *fileptr)\n{\n\tword32\tret;\n\n\tret = dynapro_process_write_file(dsk, fileptr);\n\tif(ret == 0) {\n\t\tdynapro_mark_damaged(dsk, fileptr);\n\t}\n}\n\nvoid\ndynapro_handle_changed_entry(Disk *dsk, Dynapro_file *fileptr)\n{\n\t// printf(\"handle_changed_entry with fileptr:%p\\n\", fileptr);\n\tfileptr->damaged = 0;\n\tif(fileptr->prodos_name[0] >= 0xe0) {\n\t\t// Directory header, not valid to be called here\n\t\tfileptr->damaged = 1;\n\t\tdsk->dynapro_info_ptr->damaged = 1;\n\t} else if(fileptr->prodos_name[0] >= 0xd0) {\n\t\t// Directory entry\n\t\tif(fileptr->subdir_ptr) {\n\t\t\tdynapro_erase_free_dir(dsk, fileptr->subdir_ptr);\n\t\t\tfileptr->subdir_ptr = 0;\n\t\t}\n\t\tdynapro_handle_write_dir(dsk, fileptr, 0,\n\t\t\t\t\t(fileptr->key_block * 0x200UL) + 4);\n\t} else {\n\t\tdynapro_handle_write_file(dsk, fileptr);\n#if 0\n\t\tprintf(\"handle_changed_entry called handle_write_file for %p, \"\n\t\t\t\t\t\"%s\\n\", fileptr, fileptr->unix_path);\n#endif\n\t}\n}\n\nword32\ndynapro_write_to_unix_file(const char *unix_path, byte *data_ptr, word32 size)\n{\n\tdword64\tdret;\n\tint\tfd;\n\n\tfd = open(unix_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\tprintf(\"Open %s for writing failed\\n\", unix_path);\n\t\texit(1);\n\t\treturn 0;\n\t}\n\tdret = cfg_write_to_fd(fd, data_ptr, 0, size);\n\tclose(fd);\n\tdyna_printf(\"dynapro_write_to_unix: %s size:%d, dret:%lld\\n\",\n\t\t\t\t\t\tunix_path, size, dret);\n\n\n\tif(size == 0) {\n\t\treturn 1;\n\t}\n\treturn (word32)dret;\n}\n\nvoid\ndynapro_unmap_file(Disk *dsk, Dynapro_file *fileptr)\n{\n\tDynapro_file *this_fileptr;\n\tDynapro_map *map_ptr;\n\t//const char *str;\n\tword32\tmap_block, next_map_block, max_blocks;\n\tint\ti;\n\n\t//printf(\"File %p: %s is unmapped\\n\", fileptr, fileptr->unix_path);\n\tdynapro_debug_map(dsk, \"start unmap file\");\n\n\t// Unmap all blocks to this file/dir\n\tmap_ptr = dsk->dynapro_info_ptr->block_map_ptr;\n\tmax_blocks = (word32)(dsk->dimage_size >> 9);\n\tmap_block = fileptr->map_first_block;\n\t//printf(\" map_block:%04x, fileptr:%p %s\\n\", map_block, fileptr,\n\t//\t\t\t\t\tfileptr->unix_path);\n\tfileptr->map_first_block = 0;\n\t//printf(\"  unmap starting, map_block:%08x, max_blocks:%07x\\n\",\n\t//\t\t\tmap_block, max_blocks);\n\tfor(i = 0; i < 65536; i++) {\n\t\tif((map_block == 0) || (map_block >= max_blocks)) {\n\t\t\tbreak;\n\t\t}\n\t\tnext_map_block = map_ptr[map_block].next_map_block;\n\t\tthis_fileptr = map_ptr[map_block].file_ptr;\n\t\tif(this_fileptr != fileptr) {\n\t\t\t//str = \"??\";\n\t\t\tif(this_fileptr) {\n\t\t\t\t//str = this_fileptr->unix_path;\n\t\t\t\tthis_fileptr->damaged = 1;\n\t\t\t}\n#if 0\n\t\t\tprintf(\"Found map[%04x]=%s while walking %s\\n\",\n\t\t\t\tmap_block, str, fileptr->unix_path);\n#endif\n\t\t}\n\t\tmap_ptr[map_block].file_ptr = 0;\n\t\tmap_ptr[map_block].next_map_block = 0;\n\t\tmap_ptr[map_block].modified = 0;\n\t\t//printf(\"  just unmapped block %05x\\n\", map_block);\n\t\tmap_block = next_map_block;\n\t}\n\n\t// printf(\" unmap ending\\n\");\n}\n\nvoid\ndynapro_unlink_file(Dynapro_file *fileptr)\n{\n\tconst char *str;\n\tint\tret, err;\n\n\t// Try to unlink unix_path\n\tdyna_printf(\"Unlink %s (%p)\\n\", fileptr->unix_path, fileptr);\n\tif(fileptr->unix_path == 0) {\n\t\tprintf(\"unix_path of %p is null!\\n\", fileptr);\n\t\texit(1);\n\t}\n\tif(fileptr->subdir_ptr != 0) {\n\t\tprintf(\"unlink_file %s, but subdirptr is valid!\\n\",\n\t\t\t\tfileptr->unix_path);\n\t\texit(1);\n\t}\n\n\tret = unlink(fileptr->unix_path);\n\tif(ret != 0) {\n\t\t// Maybe it's a directory, rmdir\n\t\tret = rmdir(fileptr->unix_path);\n\t}\n\tif(ret != 0) {\n\t\t// Cannot erase, try to rename\n\t\tcfg_strncpy_dirname(&g_dynapro_path_buf[0], fileptr->unix_path,\n\t\t\t\t\t\t\tDYNAPRO_PATH_MAX);\n\t\tcfg_strlcat(&g_dynapro_path_buf[0], \".kegsrm_\",\n\t\t\t\t\t\t\tDYNAPRO_PATH_MAX);\n\t\tstr = cfg_str_basename(fileptr->unix_path);\n\t\tcfg_strlcat(&g_dynapro_path_buf[0], str, DYNAPRO_PATH_MAX);\n\t\tprintf(\"Could not erase %s, renaming to: %s\\n\",\n\t\t\tfileptr->unix_path, &g_dynapro_path_buf[0]);\n\t\tret = rename(fileptr->unix_path, &g_dynapro_path_buf[0]);\n\t\terr = errno;\n\t\tif(ret != 0) {\n\t\t\tprintf(\"Rename of %s failed, err:%d\\n\",\n\t\t\t\t\t\tfileptr->unix_path, err);\n\t\t}\n\t}\n}\n\nvoid\ndynapro_erase_free_entry(Disk *dsk, Dynapro_file *fileptr)\n{\n\tif(!fileptr) {\n\t\treturn;\n\t}\n\tdynapro_mark_damaged(dsk, fileptr);\n\n\tfileptr->next_ptr = 0;\n\tif(fileptr != dsk->dynapro_info_ptr->volume_ptr) {\n\t\t// Free everything--except the volume header\n\t\tdyna_printf(\"erase_free_entry erasing %p since it != %p\\n\",\n\t\t\tfileptr, dsk->dynapro_info_ptr->volume_ptr);\n\t\tdynapro_free_file(fileptr, 1);\n\t}\n}\n\nvoid\ndynapro_erase_free_dir(Disk *dsk, Dynapro_file *fileptr)\n{\n\tDynapro_file *nextptr, *parent_ptr, *save_fileptr;\n\n\tdyna_printf(\"dynapro_erase_free_dir of %p\\n\", fileptr);\n\tif(fileptr == 0) {\n\t\treturn;\n\t}\n\tdyna_printf(\"  dynapro_erase_free_dir of %s\\n\", fileptr->unix_path);\n\tdsk->dynapro_info_ptr->damaged = 1;\n\tparent_ptr = fileptr->parent_ptr;\n\tif(parent_ptr) {\n\t\tif(parent_ptr->subdir_ptr) {\n\t\t\tparent_ptr->damaged = 1;\n\t\t}\n\t}\n\tsave_fileptr = fileptr;\n\tnextptr = fileptr->next_ptr;\n\tfileptr->next_ptr = 0;\n\tfileptr = nextptr;\n\twhile(fileptr) {\n\t\tnextptr = fileptr->next_ptr;\n\t\tdynapro_erase_free_entry(dsk, fileptr);\n\t\tfileptr = nextptr;\n\t}\n\tdynapro_erase_free_entry(dsk, save_fileptr);\n}\n\nvoid\ndynapro_mark_damaged(Disk *dsk, Dynapro_file *fileptr)\n{\n\tif(fileptr == 0) {\n\t\treturn;\n\t}\n\tdyna_printf(\"dynapro_mark_damaged: %s damaged\\n\", fileptr->unix_path);\n\tfileptr->damaged = 1;\n\tdsk->dynapro_info_ptr->damaged = 1;\n\tdynapro_unmap_file(dsk, fileptr);\n\tif(fileptr->subdir_ptr) {\n\t\tdynapro_erase_free_dir(dsk, fileptr->subdir_ptr);\n\t\tfileptr->subdir_ptr = 0;\n\t}\n\n\tif((fileptr->prodos_name[0] >= 0xe0) && fileptr->parent_ptr) {\n\t\t// We are a directory header, mark the directory entry of our\n\t\t//  parent as damaged (but don't actually damage it)\n\t\tfileptr->parent_ptr->damaged = 1;\n\t} else if(fileptr != dsk->dynapro_info_ptr->volume_ptr) {\n\t\tdynapro_unlink_file(fileptr);\n\t}\n}\n\nint\ndynapro_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size)\n{\n\tDynapro_info *info_ptr;\n\tDynapro_map *map_ptr;\n\tDynapro_file *fileptr;\n\tbyte\t*bptr;\n\tword32\tui, block;\n\tint\tnum;\n\tint\ti;\n\n\t// Return 1 if write was done.  Return < 0 if an error occurs\n\n\tdyna_printf(\"\\n\");\n\tdyna_printf(\"------------------------------------------------\\n\");\n\tdyna_printf(\"dynapro_write to %08llx, size:%08x\\n\", doffset, size);\n\tdynapro_debug_update(dsk);\n\n\tbptr = dsk->raw_data;\n\tif((doffset + size) > dsk->dimage_size) {\n\t\tprintf(\"Write past end of disk, ignored\\n\");\n\t\treturn -1;\n\t}\n\tfor(ui = 0; ui < size; ui++) {\n#if 0\n\t\tif((bptr[doffset + ui] != bufptr[ui]) && (diffs < 500)) {\n\t\t\tprintf(\"%07llx:%02x (was %02x)\\n\", doffset+ui,\n\t\t\t\tbufptr[ui], bptr[doffset + ui]);\n\t\t\tdiffs++;\n\t\t}\n#endif\n\t\tbptr[doffset + ui] = bufptr[ui];\n\t}\n\n\tinfo_ptr = dsk->dynapro_info_ptr;\n\tif(info_ptr == 0) {\n\t\tprintf(\"dynapro_info_ptr==0\\n\");\n\t\treturn -1;\n\t}\n\n\tnum = (size + 511) >> 9;\n\tblock = (word32)(doffset >> 9);\n\tdyna_printf(\"Marking blocks %05x-%05x modified\\n\", block,\n\t\t\t\t\t\t\tblock + num - 1);\n\tfor(i = 0; i < num; i++) {\n\t\tmap_ptr = &(info_ptr->block_map_ptr[block + i]);\n\t\tmap_ptr->modified = 1;\n\t}\n\tfor(i = 0; i < num; i++) {\n\t\tmap_ptr = &(info_ptr->block_map_ptr[block + i]);\n\t\tif(!map_ptr->modified) {\n\t\t\tcontinue;\t\t\t// Already cleared\n\t\t}\n\t\tfileptr = map_ptr->file_ptr;\n\t\tif(fileptr == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tif(fileptr->prodos_name[0] >= 0xe0) {\n\t\t\tdynapro_handle_write_dir(dsk, fileptr->parent_ptr,\n\t\t\t\tfileptr, fileptr->dir_byte);\n\t\t} else {\n\t\t\tdynapro_handle_write_file(dsk, fileptr);\n\t\t}\n\t}\n\n\treturn 1;\n}\n\n\nvoid\ndynapro_debug_update(Disk *dsk)\n{\n\tdyna_printf(\"Writing out DYNAPRO_IMAGE, %p\\n\", dsk);\n#if 0\n\t// This causes the file DYNAPRO_IMAGE to be written out as the raw\n\t//  image after any write to the Dynapro volume.  This is for manual\n\t//  debugging.\n\tdynapro_write_to_unix_file(\"DYNAPRO_IMAGE\", dsk->raw_data,\n\t\t\t\t\t\t(word32)dsk->dimage_size);\n#endif\n}\n\nvoid\ndynapro_debug_map(Disk *dsk, const char *str)\n{\n\tDynapro_map *map_ptr;\n\tDynapro_file *lastfileptr, *fileptr;\n\tconst char *newstr;\n\tint\tnum_blocks;\n\tint\ti;\n\n\treturn;\t\t\t// HACK!\n\n\tnum_blocks = (word32)((dsk->dimage_size + 511) >> 9);\n\tmap_ptr = dsk->dynapro_info_ptr->block_map_ptr;\n\tlastfileptr = 0;\n\tprintf(\" Showing map for %s, %05x blocks, %s\\n\",\n\t\t\tdsk->dynapro_info_ptr->root_path, num_blocks, str);\n\tfor(i = 0; i < num_blocks; i++) {\n\t\tfileptr = map_ptr[i].file_ptr;\n\t\tif(fileptr != lastfileptr) {\n\t\t\tnewstr = \"\";\n\t\t\tif(fileptr) {\n\t\t\t\tnewstr = fileptr->unix_path;\n\t\t\t}\n\t\t\tprintf(\"  %04x (%07x): %p %s\\n\", i, i << 9, fileptr,\n\t\t\t\t\t\t\t\t\tnewstr);\n\t\t}\n\t\tlastfileptr = fileptr;\n\t}\n\tprintf(\"Recursive file map:\\n\");\n\tdynapro_debug_recursive_file_map(dsk->dynapro_info_ptr->volume_ptr, 1);\n}\n\nvoid\ndynapro_debug_recursive_file_map(Dynapro_file *fileptr, int start)\n{\n\tif(!fileptr) {\n\t\treturn;\n\t}\n\twhile(fileptr) {\n\t\tprintf(\"  file %p %s map_first_block:%05x, storage:%02x key:\"\n\t\t\t\"%04x\\n\", fileptr, fileptr->unix_path,\n\t\t\tfileptr->map_first_block, fileptr->prodos_name[0],\n\t\t\tfileptr->key_block);\n\t\tprintf(\"      n:%p, sub:%p, eof:%06x, parent:%p dam:%d\\n\",\n\t\t\tfileptr->next_ptr, fileptr->subdir_ptr,\n\t\t\tfileptr->eof, fileptr->parent_ptr, fileptr->damaged);\n\t\tif(fileptr->unix_path == 0) {\n\t\t\tprintf(\"Filename is invalid, exiting\\n\");\n\t\t\texit(1);\n\t\t}\n\t\tif(!fileptr->parent_ptr && !start) {\n\t\t\tprintf(\"parent_ptr is 0, exiting\\n\");\n\t\t\texit(1);\n\t\t}\n\t\tdynapro_debug_recursive_file_map(fileptr->subdir_ptr, 0);\n\t\tstart = 0;\n\t\tfileptr = fileptr->next_ptr;\n\t}\n}\n\nword32\ndynapro_unix_to_prodos_time(const time_t *time_ptr)\n{\n\tstruct tm *tm_ptr;\n\tword32\tymd, hours_mins, date_time;\n\tint\tyear;\n\n\ttm_ptr = localtime(time_ptr);\n\thours_mins = (tm_ptr->tm_hour << 8) | tm_ptr->tm_min;\n\tyear = tm_ptr->tm_year;\t\t// years since 1900\n\tif(year < 80) {\n\t\tyear = 80;\n\t} else if(year >= 100) {\n\t\tyear -= 100;\n\t\tif(year >= 80) {\n\t\t\tyear = 79;\n\t\t}\n\t}\n\tymd = (year << 9) | ((tm_ptr->tm_mon + 1) << 5) | (tm_ptr->tm_mday);\n\tdate_time = (ymd & 0xffff) | (hours_mins << 16);\n\t// printf(\"Unix time:%s results in:%08x\\n\", asctime(tm_ptr), date_time);\n\n\treturn date_time;\n}\n\nint\ndynapro_create_prodos_name(Dynapro_file *newfileptr, Dynapro_file *matchptr,\n\t\tword32 storage_type)\n{\n\tDynapro_file *thisptr;\n\tchar\t*str;\n\tword32\tupper_lower;\n\tint\tlen, outpos, inpos, max_inpos, c, done, dot_pos, inc_pos;\n\tint\ti;\n\n#if 0\n\tprintf(\"dynapro_create_prodos_name to %s, match:%p, st:%03x\\n\",\n\t\tnewfileptr->unix_path, matchptr, storage_type);\n#endif\n\n\tfor(i = 0; i < 17; i++) {\n\t\tnewfileptr->prodos_name[i] = 0;\n\t}\n\tstr = newfileptr->unix_path;\n\tif(!str) {\n\t\treturn 0;\n\t}\n\tinpos = (int)strlen(str);\n\tmax_inpos = inpos + 1;\n\twhile(inpos >= 1) {\n\t\tinpos--;\n\t\tif(str[inpos] == '/') {\n\t\t\tinpos++;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif(storage_type == 0x50) {\n\t\t// printf(\"max_inpos:%d, inpos:%d\\n\", max_inpos, inpos);\n\t}\n\tif((storage_type == 0x50) && ((max_inpos - inpos) > 12)) {\n\t\t// Remove .applesingle extension\n\t\tmax_inpos -= 13;\n\t}\n\t//printf(\" inpos:%d max_inpos:%d str:%s\\n\", inpos, max_inpos,\n\t//\t\t\t\t\t\t&(str[inpos]));\n\toutpos = 0;\n\twhile(outpos < (DYNAPRO_PATH_MAX - 1)) {\n\t\tc = 0;\n\t\tif(inpos < max_inpos) {\n\t\t\tc = str[inpos++];\n\t\t}\n\t\tg_dynapro_path_buf[outpos] = c;\n\t\tif(c == 0) {\n\t\t\tbreak;\n\t\t}\n\t\toutpos++;\n\t\tif((c >= 'A') && (c <= 'Z')) {\n\t\t\tcontinue;\t\t// This is legal\n\t\t}\n\t\tif((c >= 'a') && (c <= 'z')) {\n\t\t\tcontinue;\t\t// Also legal\n\t\t}\n\t\tif((outpos > 1) && (c >= '0') && (c <= '9')) {\n\t\t\tcontinue;\t\t// Also legal\n\t\t}\n\t\tif((outpos > 1) && (c == '.')) {\n\t\t\tcontinue;\t\t// Also legal\n\t\t}\n\t\t// If this is the first character, make it \"A\" and continue\n\t\tif(outpos == 1) {\n\t\t\tg_dynapro_path_buf[0] = 'A';\n\t\t\tcontinue;\n\t\t}\n\t\tif((c == ',') || (c == '#')) {\t\t// ,ttxt,a$2000, ignore\n\t\t\toutpos--;\n\t\t\tbreak;\t\t\t// All done\n\t\t}\n\n\t\t// This is not legal.  Make it a '.'\n\t\tif((c >= 0x20) && (c <= 0x7e)) {\n\t\t\tg_dynapro_path_buf[outpos - 1] = '@';\t// do '.' later\n\t\t} else {\n\t\t\toutpos--;\t\t// Ignore it\n\t\t}\n\t}\n\n\tg_dynapro_path_buf[outpos] = 0;\n\t// printf(\" initial path_buf:%s, %d\\n\", &g_dynapro_path_buf[0], outpos);\n\twhile((outpos >= 0) && (g_dynapro_path_buf[outpos-1] == '@')) {\n\t\t// Remove trailing '@' since they are not useful\n\t\toutpos--;\n\t\tg_dynapro_path_buf[outpos] = 0;\n\t}\n\tfor(i = 1; i < outpos; i++) {\n\t\t// Convert '@' to '.' to make name legal\n\t\tif(g_dynapro_path_buf[i] == '@') {\n\t\t\tg_dynapro_path_buf[i] = '.';\n\t\t}\n\t}\n\tif(outpos == 0) {\n\t\t// Not a valid file, just skip it\n\t\treturn 0;\n\t}\n\t// Now, it's valid.  Squeeze it to 15 character but saving extension\n\tlen = (int)strlen(&g_dynapro_path_buf[0]);\n\tif(len > 15) {\n\t\t// Copy last 8 characters to be in positions 7..14\n\t\tfor(i = 7; i < 16; i++) {\n\t\t\tg_dynapro_path_buf[i] = g_dynapro_path_buf[len-15 + i];\n\t\t}\n\t}\n\n\tlen = (int)strlen(&g_dynapro_path_buf[0]);\n\tif((len > 15) || (len == 0)) {\n\t\tprintf(\"Bad filename handling: %s\\n\", &g_dynapro_path_buf[0]);\n\t\treturn 0;\n\t}\n\n\t// See if it conflicts with matchptr\n\tthisptr = matchptr;\n\tfor(i = 0; i < 10000; i++) {\n\t\tif(!thisptr || (thisptr == newfileptr)) {\n\t\t\tthisptr = 0;\n\t\t\tbreak;\n\t\t}\n\t\t//printf(\"Comparing %s to %s\\n\", &g_dynapro_path_buf[0],\n\t\t//\t\t\t(char *)&(thisptr->prodos_name[1]));\n\t\tlen = (int)strlen(&g_dynapro_path_buf[0]);\n\t\tif((len == (thisptr->prodos_name[0] & 0xf)) &&\n\t\t\t\t(cfgcasecmp(&g_dynapro_path_buf[0],\n\t\t\t\t(char *)&(thisptr->prodos_name[1])) == 0)) {\n\t\t\tdyna_printf(\" that was a match\\n\");\n\t\t\tdot_pos = 0;\n\t\t\tinc_pos = len - 1;\n\t\t\tfor(i = len - 2; i >= 1; i--) {\n\t\t\t\tif(g_dynapro_path_buf[i] == '.') {\n\t\t\t\t\tdot_pos = i;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(len < 15) {\n\t\t\t\t// Append \"1\" to the end\n\t\t\t\tlen++;\n\t\t\t\tinc_pos = len - 1;\n\t\t\t\tg_dynapro_path_buf[len] = 0;\n\t\t\t\tg_dynapro_path_buf[len - 1] = '1';\n\t\t\t\tif(dot_pos > 1) {\n\t\t\t\t\tfor(i = len - 1; i >= dot_pos; i--) {\n\t\t\t\t\t\tg_dynapro_path_buf[i] =\n\t\t\t\t\t\t\tg_dynapro_path_buf[i-1];\n\t\t\t\t\t}\n\t\t\t\t\tinc_pos = dot_pos;\n\t\t\t\t}\n\t\t\t\tg_dynapro_path_buf[inc_pos] = '1';\n\t\t\t} else if(dot_pos > 3) {\n\t\t\t\tinc_pos = dot_pos - 1;\n\t\t\t}\n\t\t\tdone = 0;\n\t\t\tfor(i = inc_pos; i >= 1; i--) {\n\t\t\t\tc = g_dynapro_path_buf[i];\n\t\t\t\tc++;\n\t\t\t\tif(c == ('9' + 1)) {\n\t\t\t\t\tc = '0';\n\t\t\t\t} else if(c == ('z' + 1)) {\n\t\t\t\t\tc = 'a';\n\t\t\t\t} else if(c == ('Z' + 1)) {\n\t\t\t\t\tc = 'A';\n\t\t\t\t} else {\n\t\t\t\t\tdone = 1;\n\t\t\t\t}\n\t\t\t\tg_dynapro_path_buf[i] = c;\n\t\t\t\tif(done) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthisptr = matchptr;\n\t\t} else {\n\t\t\tthisptr = thisptr->next_ptr;\n\t\t}\n\t}\n\tif(thisptr) {\n\t\t// File could not be made unique\n\t\tprintf(\"Could not make a unique ProDOS filename: %s\\n\",\n\t\t\t\t\t\tnewfileptr->unix_path);\n\t\treturn 0;\n\t}\n\n\tupper_lower = 0;\n\tfor(i = 0; i < len; i++) {\n\t\tc = g_dynapro_path_buf[i];\n\t\tif((c >= 'a') && (c <= 'z')) {\n\t\t\tc = c - 'a' + 'A';\n\t\t\tupper_lower |= 0x8000 | (0x4000 >> i);\n\t\t}\n\t\tnewfileptr->prodos_name[1 + i] = c;\n\t}\n\tnewfileptr->prodos_name[0] = len | storage_type;\n\tnewfileptr->upper_lower = upper_lower;\n\n\treturn len;\n}\n\nDynapro_file *\ndynapro_new_unix_file(const char *path, Dynapro_file *parent_ptr,\n\t\t\t\tDynapro_file *match_ptr, word32 storage_type)\n{\n\tDynapro_file *fileptr;\n\tint\tlen;\n\tint\ti;\n\n#if 0\n\tprintf(\"dynapro_new_unix_file for %s, parent:%p, m:%p, st:%03x\\n\",\n\t\tpath, parent_ptr, match_ptr, storage_type);\n#endif\n\tfileptr = dynapro_alloc_file();\n\tif(!fileptr) {\n\t\treturn 0;\n\t}\n\n\tfileptr->next_ptr = 0;\n\tfileptr->parent_ptr = parent_ptr;\n\tfileptr->subdir_ptr = 0;\n\tfileptr->buffer_ptr = 0;\n\tfileptr->unix_path = kegs_malloc_str(path);\n\tfor(i = 0; i < 17; i++) {\n\t\tfileptr->prodos_name[i] = 0;\n\t}\n\tfileptr->dir_byte = 0;\n\tfileptr->eof = 0;\n\tfileptr->blocks_used = 0;\n\tfileptr->creation_time = 0;\n\tfileptr->lastmod_time = 0;\n\tfileptr->upper_lower = 0;\n\tfileptr->key_block = 0;\n\tfileptr->aux_type = 0;\n\tfileptr->header_pointer = 0;\n\tfileptr->map_first_block = 0;\n\tfileptr->file_type = 0x0f;\t\t\t// Default to \"DIR\"\n\tfileptr->modified_flag = 0;\n\tfileptr->damaged = 0;\n\n\tlen = (int)strlen(fileptr->unix_path);\n\tfor(i = len - 1; i >= 0; i--) {\n\t\tif(fileptr->unix_path[i] == '/') {\n\t\t\tfileptr->unix_path[i] = 0;\t// Strip trailing /\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(storage_type < 0xd0) {\n\t\tstorage_type = dynatype_detect_file_type(fileptr,\n\t\t\t\t\tfileptr->unix_path, storage_type);\n\t}\n\n\tlen = dynapro_create_prodos_name(fileptr, match_ptr, storage_type);\n\tif(len == 0) {\n\t\tprintf(\"Could not create prodos name for: %s\\n\", path);\n\t\tfree(fileptr);\n\t\treturn 0;\n\t}\n\n#if 0\n\tprintf(\"dynapro_create_new_unix_file: %s prodos:%s, st:%02x, ft:%02x, \"\n\t\t\"aux:%04x\\n\", fileptr->unix_path, &(fileptr->prodos_name[1]),\n\t\tfileptr->prodos_name[0], fileptr->file_type, fileptr->aux_type);\n#endif\n\treturn fileptr;\n}\n\nint\ndynapro_create_dir(Disk *dsk, char *unix_path, Dynapro_file *parent_ptr,\n\t\t\t\tword32 dir_byte)\n{\n\tstruct stat stat_buf;\n\tstruct dirent *direntptr;\n\tDIR\t*opendirptr;\n\tDynapro_file *fileptr, *head_ptr, *prev_ptr;\n\tmode_t\tfmt;\n\tword32\tstorage_type, val;\n\tint\tret;\n\n\t// Create a directory entry at dir_byte first\n#if 0\n\tprintf(\"\\n\");\n\tprintf(\"dynapro_add_files to %s, %p dir_byte:%08x\\n\", unix_path,\n\t\t\t\t\t\t\tparent_ptr, dir_byte);\n#endif\n\n\tstorage_type = 0xe0;\t\t// Directory header\n\tif(dir_byte < 0x600) {\t\t// Block 2: volume header\n\t\tstorage_type = 0xf0;\n\t}\n\thead_ptr = dynapro_new_unix_file(unix_path, parent_ptr, 0,\n\t\t\t\t\t\t\t\tstorage_type);\n\tif(parent_ptr) {\n\t\tparent_ptr->subdir_ptr = head_ptr;\n#if 0\n\t\tprintf(\"set parent %s subdir_ptr=%p\\n\", parent_ptr->unix_path,\n\t\t\t\t\t\thead_ptr);\n#endif\n\t}\n\tif(dsk->dynapro_info_ptr->volume_ptr == 0) {\n\t\tdsk->dynapro_info_ptr->volume_ptr = head_ptr;\n\t}\n\tif(head_ptr == 0) {\n\t\tprintf(\"new_file returned 0, skipping %s\\n\", unix_path);\n\t\treturn dir_byte;\n\t}\n\thead_ptr->key_block = dir_byte >> 9;\n\thead_ptr->aux_type = 0x0d27;\t\t\t// 0x27,0x0d\n\thead_ptr->file_type = 0x75;\t\t\t// Directory header type\n\tif(storage_type >= 0xf0) {\n\t\thead_ptr->file_type = 0x00;\n\t}\n\tret = cfg_stat(unix_path, &stat_buf, 0);\n\tif(ret != 0) {\n\t\tprintf(\"stat %s ret %d, errno:%d\\n\", unix_path, ret, errno);\n\t\treturn 0;\n\t}\n\thead_ptr->creation_time = dynapro_unix_to_prodos_time(\n\t\t\t\t\t\t\t&stat_buf.st_ctime);\n\n\tdir_byte = dynapro_add_file_entry(dsk, head_ptr, 0, dir_byte, 0);\n\n\topendirptr = opendir(unix_path);\n\tif(opendirptr == 0) {\n\t\tprintf(\"Could not open %s as a dir\\n\", unix_path);\n\t\treturn 0;\n\t}\n\tprev_ptr = head_ptr;\n\twhile(1) {\n\t\tdirentptr = readdir(opendirptr);\n\t\tif(direntptr == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tif(direntptr->d_name[0] == '.') {\n\t\t\tcontinue;\t\t\t// Ignore all '.' files\n\t\t}\n\t\tdynapro_join_path_and_file(&(g_dynapro_path_buf[0]), unix_path,\n\t\t\t\t\tdirentptr->d_name, DYNAPRO_PATH_MAX);\n\t\tret = cfg_stat(&(g_dynapro_path_buf[0]), &stat_buf, 0);\n\t\tif(ret != 0) {\n\t\t\tprintf(\"stat %s ret %d, errno:%d\\n\",\n\t\t\t\t\t&g_dynapro_path_buf[0], ret, errno);\n\t\t\tcontinue;\t// skip it\n\t\t}\n\n\t\tfmt = stat_buf.st_mode & S_IFMT;\n\t\tstorage_type = 0;\n\t\tif(fmt == S_IFDIR) {\n\t\t\t// Ignore symlinks to directories (since they may point\n\t\t\t//  outside the base directory, and so dynamically\n\t\t\t//  removing files could be a security issue).\n\t\t\tret = cfg_stat(&g_dynapro_path_buf[0], &stat_buf, 1);\n\t\t\tif(ret != 0) {\n\t\t\t\tprintf(\"lstat %s ret %d, errno:%d\\n\",\n\t\t\t\t\t&g_dynapro_path_buf[0], ret, errno);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tstorage_type = 0xd0;\t\t// Directory\n\t\t} else if(fmt != S_IFREG) {\n\t\t\tcontinue;\t\t// Skip this\n\t\t}\n#if 0\n\t\tprintf(\"GOT file: %s, is_dir:%d (%s), parent:%p\\n\",\n\t\t\t&(g_dynapro_path_buf[0]), is_dir, direntptr->d_name,\n\t\t\tparent_ptr);\n#endif\n\t\tfileptr = dynapro_new_unix_file(&(g_dynapro_path_buf[0]),\n\t\t\thead_ptr, head_ptr->next_ptr, storage_type);\n\t\tif(fileptr == 0) {\n\t\t\tclosedir(opendirptr);\n\t\t\treturn 0;\n\t\t}\n\t\tprev_ptr->next_ptr = fileptr;\n\t\tfileptr->key_block = dynapro_find_free_block(dsk);\n\t\tfileptr->creation_time = dynapro_unix_to_prodos_time(\n\t\t\t\t\t\t\t&stat_buf.st_ctime);\n\t\tfileptr->lastmod_time = dynapro_unix_to_prodos_time(\n\t\t\t\t\t\t\t&stat_buf.st_mtime);\n\t\tif(fileptr->key_block == 0) {\n\t\t\tprintf(\"Allocating directory block failed\\n\");\n\t\t\tclosedir(opendirptr);\n\t\t\treturn 0;\n\t\t}\n\t\tfileptr->blocks_used = 1;\n\t\tfileptr->eof = 1*0x200;\n\t\tdir_byte = dynapro_add_file_entry(dsk, fileptr, head_ptr,\n\t\t\t\t\t\t\tdir_byte, 0x27);\n\t\tif(dir_byte == 0) {\n\t\t\tclosedir(opendirptr);\n\t\t\treturn 0;\n\t\t}\n\t\tif(fmt == S_IFDIR) {\n\t\t\tval = dynapro_create_dir(dsk, fileptr->unix_path,\n\t\t\t\tfileptr, (fileptr->key_block << 9) + 4);\n\t\t\tif(val == 0) {\n\t\t\t\tclosedir(opendirptr);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t} else {\n\t\t\tval = dynapro_file_from_unix(dsk, fileptr);\n\t\t\tif(val == 0) {\n\t\t\t\tclosedir(opendirptr);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t\tprev_ptr = fileptr;\n\t}\n\n\tclosedir(opendirptr);\n\treturn dir_byte;\n}\n\nword32\ndynapro_add_file_entry(Disk *dsk, Dynapro_file *fileptr, Dynapro_file *head_ptr,\n\tword32 dir_byte, word32 inc)\n{\n\tDynapro_file *parent_ptr;\n\tbyte\t*bptr, *pkeyptr;\n\tword32\tstorage_type, val, ent, new_dir_blk, new_dir_byte;\n\tword32\theader_pointer;\n\tint\ti;\n\n#if 0\n\tprintf(\"dynapro_add_file_entry: %p %p %s head:%p dir_byte:%08x \"\n\t\t\"inc:%03x\\n\", dsk, fileptr, fileptr->unix_path, head_ptr,\n\t\tdir_byte, inc);\n#endif\n\tbptr = dsk->raw_data;\n\tif(((dir_byte & 0x1ff) + inc + inc) >= 0x200) {\n\t\t// This entry will not fit in this directory block.\n\t\t//  Try to step to next block, otherwise allocate a new one\n\t\tnew_dir_byte = dir_byte & -0x200L;\n\t\tnew_dir_blk = dynapro_get_word16(&bptr[new_dir_byte + 2]);\n\t\tdyna_printf(\" Entry does not fit, new_dir_blk:%04x\\n\",\n\t\t\t\t\t\t\tnew_dir_blk);\n\t\tif(new_dir_blk != 0) {\n\t\t\t// Follow to the next block\n\t\t\tdir_byte = (new_dir_blk * 0x200) + 4;\n\t\t} else if(dir_byte < (6 * 0x200)) {\n\t\t\t// Otherwise, allocate a new block (not for volume dir)\n\t\t\t// This is a volume header, always 4 blocks, don't\n\t\t\t//  allocate any more, this is now full\n\t\t\tprintf(\"Too many file in volume directory\\n\");\n\t\t\treturn 0;\t\t// Out of space\n\t\t} else {\n\t\t\tnew_dir_blk = dynapro_find_free_block(dsk);\n\t\t\tif(new_dir_blk == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tnew_dir_byte = new_dir_blk * 512;\n\t\t\tdynapro_set_word16(&bptr[new_dir_byte], dir_byte >> 9);\n\t\t\tdynapro_set_word16(&bptr[new_dir_byte + 2], 0);\n\t\t\tdir_byte = (dir_byte >> 9) << 9;\n\t\t\tdynapro_set_word16(&bptr[dir_byte + 2], new_dir_blk);\n\t\t\tdir_byte = new_dir_byte + 4;\n\t\t\tif(!head_ptr) {\n\t\t\t\tdyna_printf(\"No head:%s\\n\", fileptr->unix_path);\n\t\t\t}\n\t\t\tparent_ptr = head_ptr->parent_ptr;\n\t\t\tif(!parent_ptr) {\n\t\t\t\tprintf(\"No parent: %s\\n\", fileptr->unix_path);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tparent_ptr->blocks_used++;\n\t\t\tparent_ptr->eof += 0x200;\n\t\t\tnew_dir_byte = parent_ptr->dir_byte;\n\t\t\tif(new_dir_byte == 0) {\n\t\t\t\tprintf(\"Invalid dir_byte for %s\\n\",\n\t\t\t\t\t\tparent_ptr->unix_path);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tdynapro_set_word16(&bptr[new_dir_byte + 0x13],\n\t\t\t\t\t\tparent_ptr->blocks_used);\n\t\t\tdynapro_set_word24(&bptr[new_dir_byte + 0x15],\n\t\t\t\t\t\tparent_ptr->eof);\n\t\t}\n\t} else {\n\t\tdir_byte += inc;\n\t}\n\tbptr = &(dsk->raw_data[dir_byte]);\n\n\tfileptr->dir_byte = dir_byte;\n\tfor(i = 0; i < 0x27; i++) {\n\t\tbptr[i] = 0;\n\t}\n\tfor(i = 0; i < 16; i++) {\n\t\tbptr[i] = fileptr->prodos_name[i];\t// [0] = len,storage_t\n\t}\n\tbptr[0x10] = fileptr->file_type;\n\tdynapro_set_word16(&bptr[0x11], fileptr->key_block);\n\tdynapro_set_word16(&bptr[0x13], fileptr->blocks_used);\n\tdynapro_set_word24(&bptr[0x15], fileptr->eof);\n\tdynapro_set_word32(&bptr[0x18], fileptr->creation_time);\n\t\t\t\t\t\t\t// creation date&time\n\tbptr[0x1c] = fileptr->upper_lower & 0xff;\t// Version\n\tbptr[0x1d] = fileptr->upper_lower >> 8;\t\t// Min_Version\n\tbptr[0x1e] = 0xe3;\t\t\t\t// Access\n\tdynapro_set_word16(&bptr[0x1f], fileptr->aux_type);\n\tstorage_type = bptr[0];\n\tif(storage_type >= 0xf0) {\t\t// Volume header\n\t\tdynapro_set_word16(&bptr[0x11], 0);\n\t\tfileptr->lastmod_time = 0x00060000;\n\t\t\t// low 16 bits: file_count, upper 16 bits: bitmap_block\n\t\tfileptr->header_pointer = (word32)(dsk->raw_dsize >> 9);\n\t\t\t\t\t\t\t// Total blocks\n\t\tdynapro_set_word16(&bptr[0x1c], 0x0005);\n\t\tdynapro_set_word16(&bptr[0x16], fileptr->upper_lower);\n\t} else if(storage_type >= 0xe0) {\t\t// Directory header\n\t\tdynapro_set_word16(&bptr[0x11], 0);\n\t\tdynapro_set_word16(&bptr[0x1c], 0x0005);\n\t\tparent_ptr = fileptr->parent_ptr;\t\t// subdir entry\n\t\tif(parent_ptr == 0) {\n\t\t\tprintf(\"parent_ptr of %s is 0\\n\", fileptr->unix_path);\n\t\t\treturn 0;\n\t\t}\n\t\tval = parent_ptr->dir_byte >> 9;\n\t\tfileptr->lastmod_time = (val << 16);\t\t// Parent block\n\t\tval = parent_ptr->dir_byte & 0x1ff;\n\t\tent = (val - 4) / 0x27;\n\t\tfileptr->header_pointer = 0x2700 | (ent + 1);\n\t} else {\n\t\t// Directory entry, or normal file\n\t\tif(head_ptr == 0) {\n\t\t\tprintf(\"head_ptr of %s is 0\\n\", fileptr->unix_path);\n\t\t\treturn 0;\n\t\t}\n\t\theader_pointer = head_ptr->key_block;\n\t\tfileptr->header_pointer = header_pointer;\n\t\tdynapro_set_word16(&bptr[0x25], header_pointer);\n\t\tpkeyptr = &(dsk->raw_data[header_pointer << 9]);\n\t\tval = head_ptr->lastmod_time + 1;\n\t\thead_ptr->lastmod_time = val;\n\t\tdynapro_set_word16(&pkeyptr[4 + 0x21], val);\t// File count\n\t}\n\tdynapro_set_word32(&bptr[0x21], fileptr->lastmod_time);\n\t\t\t\t// Last Modified date&time (or header info)\n\tdynapro_set_word16(&bptr[0x25], fileptr->header_pointer);\n#if 0\n\tprintf(\"Set dir_byte %07x=%04x\\n\", dir_byte + 0x25,\n\t\t\t\t\t\tfileptr->header_pointer);\n#endif\n\n\treturn dir_byte;\n}\n\n// When creating sparse files, always ensure first block is not sparse.  GS/OS\n//  does not treat the first block as sparse, it will actually read block 0\n// This handles normal files, and one fork of a forked file\nword32\ndynapro_fork_from_unix(Disk *dsk, byte *fptr, word32 *storage_type_ptr,\n\t\t\t\t\tword32 key_block, dword64 dsize)\n{\n\tbyte\t*bptr;\n\tword32\tsap_block, tree_block, sap_byte, tree_byte, sparse, block_num;\n\tword32\tnum_blocks, blocks_used, block_off, storage_type;\n\tint\tnum_bytes;\n\tint\ti;\n\n\tbptr = &(dsk->raw_data[0]);\n\t*storage_type_ptr = 0;\n\tsap_block = 0;\n\ttree_block = 0;\n\tnum_blocks = (word32)((dsize + 511) >> 9);\n\tif(num_blocks == 0) {\t\t\t// 0-length file\n\t\tnum_blocks = 1;\n\t} else if(num_blocks > 0x8000) {\t// >= 16MB (32K*512)\n\t\tprintf(\"File is too large, failing\\n\");\n\t\treturn 0;\n\t}\n\tblock_off = 0;\n\tblocks_used = 1;\n\twhile(block_off < num_blocks) {\n\t\tsparse = (block_off > 0);\t// sparse=0 for first block\n\t\tfor(i = 0; i < 0x200; i++) {\n\t\t\tif(fptr[(block_off << 9) + i] != 0) {\n\t\t\t\tsparse = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(sparse) {\n\t\t\tblock_off++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif((tree_block == 0) && (num_blocks > 256)) {\n\t\t\ttree_block = dynapro_find_free_block(dsk);\n\t\t\tif(tree_block == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tblocks_used++;\n\t\t}\n\t\ttree_byte = (tree_block << 9) + ((block_off >> 8) & 0xff);\n\t\tif(tree_block) {\n\t\t\tsap_block = bptr[tree_byte + 0] |\n\t\t\t\t\t\t(bptr[tree_byte + 256] << 8);\n\t\t}\n\t\tif((sap_block == 0) && (num_blocks > 1)) {\n\t\t\tsap_block = dynapro_find_free_block(dsk);\n\t\t\tif(sap_block == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tblocks_used++;\n\t\t\tif(tree_block) {\n\t\t\t\tbptr[tree_byte + 0] = sap_block;\n\t\t\t\tbptr[tree_byte + 256] = sap_block >> 8;\n\t\t\t}\n\t\t}\n\t\tif(block_off == 0) {\n\t\t\tblock_num = key_block;\n\t\t} else {\n\t\t\tblock_num = dynapro_find_free_block(dsk);\n\t\t\tif(block_num == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tblocks_used++;\n\t\t}\n\t\tsap_byte = (sap_block << 9) | (block_off & 0xff);\n\t\tif(sap_block) {\n\t\t\tbptr[sap_byte + 0] = block_num;\n\t\t\tbptr[sap_byte + 256] = block_num >> 8;\n\t\t}\n\n\t\tnum_bytes = 0x200;\n\t\tif(block_off == (dsize >> 9)) {\t\t\t// Last block\n\t\t\tnum_bytes = dsize & 0x1ff;\n\t\t}\n\t\tfor(i = 0; i < num_bytes; i++) {\n\t\t\tbptr[(block_num << 9) + i] = fptr[(block_off << 9) + i];\n\t\t}\n\n\t\tblock_off++;\n\t}\n\n\tstorage_type = 0x10;\n\tif(tree_block) {\n\t\tstorage_type = 0x30;\n\t\tkey_block = tree_block;\n\t} else if(sap_block) {\n\t\tstorage_type = 0x20;\n\t\tkey_block = sap_block;\n\t}\n\t*storage_type_ptr = storage_type;\n\treturn (blocks_used << 16) | key_block;\n}\n\nword32\ndynapro_file_from_unix(Disk *dsk, Dynapro_file *fileptr)\n{\n\tbyte\t*bptr, *fptr;\n\tdword64\tdsize;\n\tword32\tstorage_type, blocks_out, dir_byte;\n\n\tfptr = dynapro_malloc_file(fileptr->unix_path, &dsize, 0x200);\n\tfileptr->eof = (word32)dsize;\n#if 0\n\tprintf(\"file_from_unix %s, size:%08llx, file_type:%02x, dir_byte:\"\n\t\t\"%07x, storage:%02x\\n\", fileptr->unix_path, dsize,\n\t\tfileptr->file_type, fileptr->dir_byte, fileptr->prodos_name[0]);\n#endif\n\tstorage_type = 0;\n\tif((fileptr->prodos_name[0] & 0xf0) == 0x50) {\n\t\t// .applesingle file with data and/or resource forks\n\t\tblocks_out = applesingle_from_unix(dsk, fileptr, fptr, dsize);\n\t} else {\n\t\t// Normal file\n\t\tfileptr->prodos_name[0] = (fileptr->prodos_name[0] & 0xf);\n\n\t\tblocks_out = dynapro_fork_from_unix(dsk, fptr, &storage_type,\n\t\t\t\t\tfileptr->key_block, dsize);\n\t}\n\tfree(fptr);\n\tfileptr->prodos_name[0] |= storage_type;\n\tfileptr->key_block = blocks_out & 0xffff;\n\tfileptr->blocks_used = (blocks_out >> 16) & 0xffff;\n\n\t// Update dir_byte information for this file\n\tdir_byte = fileptr->dir_byte;\n\tif(dir_byte == 0) {\n\t\tdyna_printf(\"dir_byte is 0 for %s\\n\", fileptr->unix_path);\n\t}\n\tbptr = &(dsk->raw_data[dir_byte]);\n\tbptr[0] = fileptr->prodos_name[0];\n\tbptr[0x10] = fileptr->file_type;\n\tdynapro_set_word16(&bptr[0x11], fileptr->key_block);\n\tdynapro_set_word16(&bptr[0x13], fileptr->blocks_used);\n\tdynapro_set_word24(&bptr[0x15], fileptr->eof);\n\tdynapro_set_word16(&bptr[0x1f], fileptr->aux_type);\n#if 0\n\tprintf(\"Set %s dir_byte:%07x+0x10=%02x (file_type)\\n\",\n\t\tfileptr->unix_path, dir_byte, fileptr->file_type);\n#endif\n\n\treturn blocks_out;\n}\n\nword32\ndynapro_prep_image(Disk *dsk, const char *dir_path, word32 num_blocks)\n{\n\tDynapro_info *infoptr;\n\tbyte\t*bptr;\n\tword32\tbitmap_size_bytes, bitmap_size_blocks;\n\tint\tpos;\n\tword32\tui;\n\tint\ti;\n\n\tdsk->raw_data = calloc(num_blocks, 512);\n\tif(dsk->raw_data == 0) {\n\t\tdynapro_error(dsk, \"Could not allocate %d bytes\\n\",\n\t\t\t\t\t\t\tnum_blocks * 512);\n\t\treturn 0;\n\t}\n\tdsk->dimage_size = num_blocks * 512LL;\n\tdsk->dimage_start = 0;\n\tdsk->raw_dsize = num_blocks * 512LL;\n\n\tbptr = &(dsk->raw_data[0]);\n\tfor(i = 0; i < 512; i++) {\n\t\tbptr[i] = g_prodos_block0[i];\n\t}\n\n\t// Directory is from blocks 2 through 5.  Set up prev and next ptrs\n\tbptr = &(dsk->raw_data[2 * 0x200]);\n\tfor(i = 0; i < 3; i++) {\t\t// Blocks 2,3,4 (or 3,4,5)\n\t\tdynapro_set_word16(&bptr[(i + 1)*0x200], i + 2); // Prev_blk\n\t\tdynapro_set_word16(&bptr[i*0x200 + 2], i + 3);\t// Next_blk\n\t}\n\n\t// Calculate bitmap to go in blocks 6...\n\tbitmap_size_bytes = (num_blocks + 7) >> 3;\n\tbitmap_size_blocks = (bitmap_size_bytes + 512 - 1) >> 9;\n\tbptr = &(dsk->raw_data[6 * 512]);\t\t// Block 6\n\tbptr[0] = 0;\n\tfor(ui = (6 + bitmap_size_blocks); ui < num_blocks; ui++) {\n\t\tpos = (ui >> 3);\n\t\tbptr[pos] |= (0x80U >> (ui & 7));\n\t}\n\n\tinfoptr = calloc(sizeof(Dynapro_info), 1);\n\tif(!infoptr) {\n\t\treturn 0;\n\t}\n\tinfoptr->root_path = kegs_malloc_str(dir_path);\n\tinfoptr->volume_ptr = 0;\n\tinfoptr->block_map_ptr = calloc(num_blocks * sizeof(Dynapro_map), 1);\n\tinfoptr->damaged = 0;\n\tif((infoptr->root_path == 0) || (infoptr->block_map_ptr == 0)) {\n\t\tdynapro_error(dsk, \"Could not allocate memory!\\n\");\n\t\treturn 0;\n\t}\n\tdsk->dynapro_info_ptr = infoptr;\n\treturn 1;\n}\n\nword32\ndynapro_map_one_file_block(Disk *dsk, Dynapro_file *fileptr, word32 block_num,\n\t\t\tword32 file_offset, word32 eof)\n{\n\tDynapro_info *info_ptr;\n\tDynapro_map *map_ptr;\n\tbyte\t*buffer_ptr;\n\tword32\tsize, size_to_end;\n\n\tinfo_ptr = dsk->dynapro_info_ptr;\n\tif(!info_ptr || (block_num >= (dsk->dimage_size >> 9))) {\n\t\tprintf(\" mapping file %s, block %04x is invalid\\n\",\n\t\t\t\t\t\tfileptr->unix_path, block_num);\n\t\treturn 0;\n\t}\n\tif(info_ptr->block_map_ptr == 0) {\n\t\treturn 0;\n\t}\n\tif(block_num == 0) {\n\t\treturn 1;\n\t}\n\tmap_ptr = &(info_ptr->block_map_ptr[block_num]);\n\tif((map_ptr->file_ptr != 0) || (map_ptr->next_map_block != 0)) {\n\t\tdyna_printf(\"Mapping %s to block %04x, already has file_ptr:\"\n\t\t\t\"%p, next_map:%04x, mod:%d\\n\", fileptr->unix_path,\n\t\t\tblock_num, map_ptr->file_ptr, map_ptr->next_map_block,\n\t\t\tmap_ptr->modified);\n\t\tif(map_ptr->file_ptr) {\n\t\t\tdyna_printf(\" Existing file: %s\\n\",\n\t\t\t\t\t\tmap_ptr->file_ptr->unix_path);\n\t\t}\n\t\treturn 0;\n\t}\n\t//printf(\" map file %s block %05x off:%08x\\n\", fileptr->unix_path,\n\t//\t\t\t\t\tblock_num, file_offset);\n\n\tmap_ptr->next_map_block = fileptr->map_first_block;\n\tfileptr->map_first_block = block_num;\n\tmap_ptr->modified = 0;\n\tmap_ptr->file_ptr = fileptr;\n\n\tif(file_offset >= eof) {\n\t\treturn 1;\t\t// This block was an \"overhead\" block\n\t}\n\n\tbuffer_ptr = fileptr->buffer_ptr;\n\tif(buffer_ptr) {\n\t\t// Copy this block in at file_offset\n\t\tsize = 0x200;\n\t\tsize_to_end = eof - file_offset;\n\t\tif(size_to_end < size) {\n\t\t\tsize = size_to_end;\n\t\t}\n#if 0\n\t\tprintf(\"mofb: Write to %p + %07x from block %04x, size:%04x\\n\",\n\t\t\tbuffer_ptr, file_offset, block_num, size);\n#endif\n\t\tmemcpy(buffer_ptr + file_offset,\n\t\t\t\t&(dsk->raw_data[block_num * 0x200]), size);\n\t}\n\treturn 1;\n}\n\nword32\ndynapro_map_file_blocks(Disk *dsk, Dynapro_file *fileptr, word32 block_num,\n\tint level, word32 file_offset, word32 eof)\n{\n\tbyte\t*bptr;\n\tword32\tentry_inc, tmp, ret;\n\tint\ti;\n\n#if 0\n\tprintf(\"dynapro_map_file_blocks %s block_num %05x level:%d off:%08x\\n\",\n\t\t\tfileptr->unix_path, block_num, level, file_offset);\n#endif\n\tif(level == 0) {\n\t\treturn 0;\t\t// Bad value, should not happen\n\t}\n\tif(level == 1) {\n\t\treturn dynapro_map_one_file_block(dsk, fileptr, block_num,\n\t\t\t\t\t\t\tfile_offset, eof);\n\t}\n\tret = dynapro_map_one_file_block(dsk, fileptr, block_num, 1U << 30, 0);\n\tif(ret == 0) {\n\t\treturn ret;\n\t}\n\tentry_inc = 512;\n\tif(level == 3) {\t\t// Tree\n\t\tentry_inc = 256*512;\n\t}\n\tbptr = &(dsk->raw_data[block_num * 0x200]);\n\tfor(i = 0; i < 256; i++) {\n\t\ttmp = bptr[i] + (bptr[256 + i] << 8);\n\t\tif(tmp == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tret = dynapro_map_file_blocks(dsk, fileptr, tmp, level - 1,\n\t\t\t\t\t\tfile_offset + i*entry_inc, eof);\n\t\tif(ret == 0) {\n\t\t\treturn ret;\n\t\t}\n\t}\n\tdynapro_debug_map(dsk, \"post map_file_blocks\");\n\n\treturn 1;\n}\n\nword32\ndynapro_map_file(Disk *dsk, Dynapro_file *fileptr, int do_file_data)\n{\n\tword32\tblock_num, ret;\n\tint\tlevel;\n\n\tlevel = (fileptr->prodos_name[0] >> 4) & 0xf;\n\tblock_num = fileptr->key_block;\n\tif(level == 5) {\t\t\t// Forked file\n\t\treturn applesingle_map_from_prodos(dsk, fileptr,\n\t\t\t\t\t\t\tdo_file_data);\n\t} else if((level < 0) || (level >= 4)) {\n\t\tprintf(\"Storage_type: %02x for %s is bad\\n\", level,\n\t\t\t\t\t\t\tfileptr->unix_path);\n\t\treturn 0;\n\t}\n\n\tfileptr->buffer_ptr = 0;\n\tif(do_file_data) {\n\t\t// Create a place for data.  We will free before returning\n\t\tfileptr->buffer_ptr = calloc(1, fileptr->eof + 0x200);\n\t\tif(fileptr->buffer_ptr == 0) {\n\t\t\tprintf(\"malloc failed!\\n\");\n\t\t\treturn 0;\n\t\t}\n\t}\n\t// Must not return now before free'ing fileptr->buffer_ptr!\n\n\tret = dynapro_map_file_blocks(dsk, fileptr, block_num, level, 0,\n\t\t\t\t\t\t\t\tfileptr->eof);\n\n\t// printf(\" dynapro_map_file, map_file_blocks ret:%04x\\n\", ret);\n\tif((ret != 0) && (do_file_data)) {\n\t\t// Then, write buffer_ptr to the unix file\n\t\tret = dynapro_write_to_unix_file(fileptr->unix_path,\n\t\t\t\tfileptr->buffer_ptr, fileptr->eof);\n\t\t// printf(\" map_file, write_to_unix_file ret:%04x\\n\", ret);\n\t}\n\n\t// And free the buffer_ptr\n\tfree(fileptr->buffer_ptr);\n\tfileptr->buffer_ptr = 0;\n\n\treturn ret;\n}\n\nword32\ndynapro_map_dir_blocks(Disk *dsk, Dynapro_file *fileptr)\n{\n\tbyte\t*bptr;\n\tword32\tblock_num, ret;\n\tint\tcnt;\n\n\t// Loop over all directory blocks marking the map\n\tblock_num = fileptr->key_block;\n\tif(block_num == 0) {\n\t\tprintf(\"dynapro_map_dir_blocks, block_num is 0\\n\");\n\t\treturn 0;\n\t}\n\tbptr = &(dsk->raw_data[0]);\n\tcnt = 0;\n\tfileptr->map_first_block = 0;\n\twhile(block_num != 0) {\n\t\tret = dynapro_map_one_file_block(dsk, fileptr, block_num,\n\t\t\t\t\t\t\t1U << 30, 0);\n\t\tif(ret == 0) {\n\t\t\tprintf(\"dynapro_map_dir_on_block, ret 0, block:%04x\\n\",\n\t\t\t\t\t\t\t\tblock_num);\n\t\t\treturn 0;\n\t\t}\n\t\tblock_num = dynapro_get_word16(&bptr[(block_num * 0x200) + 2]);\n\t\tcnt++;\n\t\tif(cnt > 1000) {\n\t\t\tprintf(\"Directory had loop in it, error\\n\");\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tdynapro_debug_map(dsk, \"post map_dir_blocks\");\n\treturn 1;\n}\n\nword32\ndynapro_build_map(Disk *dsk, Dynapro_file *fileptr)\n{\n\tword32\tret;\n\n\tif(fileptr == 0) {\n\t\treturn 0;\n\t}\n\n\t// printf(\"### dynapro_build_map for dir:%s\\n\", fileptr->unix_path);\n\n\t// fileptr points to a directory header (volume or subdir).  Walk\n\t//  all siblings and build a map\n\tret = 1;\n\twhile(fileptr && ret) {\n\t\tif(fileptr->prodos_name[0] >= 0xe0) {\n\t\t\t// Directory/Volume header\n\t\t\tret = dynapro_map_dir_blocks(dsk, fileptr);\n\t\t} else if(fileptr->subdir_ptr) {\n\t\t\t// Recurse to handle subdirectory\n\t\t\tret = dynapro_build_map(dsk, fileptr->subdir_ptr);\n\t\t} else {\n\t\t\tret = dynapro_map_file(dsk, fileptr, 0);\n\t\t}\n\t\tfileptr = fileptr->next_ptr;\n\t}\n\tdynapro_debug_map(dsk, \"post build_map\");\n\treturn ret;\n}\n\nint\ndynapro_mount(Disk *dsk, char *dir_path, word32 num_blocks)\n{\n\tword32\tret;\n\n#if 0\n\tprintf(\"dynapro_mount: %p, %s %08x\\n\", dsk, dir_path, num_blocks);\n#endif\n\tif(num_blocks >= 65536) {\n\t\tnum_blocks = 65535;\n\t}\n\tret = dynapro_prep_image(dsk, dir_path, num_blocks);\n\tif(ret == 0) {\n\t\treturn -1;\n\t}\n\n\tret = dynapro_create_dir(dsk, dir_path, 0, 0x404);\t// Block 2, +4\n\t// printf(\"dynapro_mount will end with ret:%05x\\n\", ret);\n\tif(ret != 0) {\n\t\tret = dynapro_build_map(dsk, dsk->dynapro_info_ptr->volume_ptr);\n\t}\n\t// dynapro_debug_update(dsk);\n\tif(ret == 0) {\n\t\tdynapro_error(dsk, \"Folder too large.  dynapro_build_map \"\n\t\t\t\t\t\t\t\t\"ret:0\\n\");\n\t} else {\n\t\tret = dynapro_validate_disk(dsk);\n\t}\n\tif(ret == 0) {\n\t\tdynapro_error(dsk, \"dynapro_validate_disk ret:0\\n\");\n\t\tdynapro_free_dynapro_info(dsk);\n\t\treturn -1;\n\t}\n#ifndef _WIN32\n\tsetvbuf(stdout, 0, _IOLBF, 0);\n#endif\n\tdsk->fd = 0;\n\treturn 0;\n}\n\n"
  },
  {
    "path": "gsplus/src/engine.h",
    "content": "// \"@(#)$KmKId: engine.h,v 1.9 2023-09-11 12:55:16+00 kentd Exp $\"\n\n/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\nint\nENGINE_TYPE (Engine_reg *engine_ptr)\n{\n\tregister byte\t*ptr;\n\tbyte\t*arg_ptr;\n\tPc_log\t*tmp_pc_ptr;\n\tFplus\t*fplus_ptr;\n\tbyte\t*stat;\n\tdword64\tdfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;\n\tregister word32\tkpc, acc, xreg, yreg, direct, psr, zero, neg7, addr;\n\tword32\twstat, arg, stack, dbank, opcode, addr_latch, tmp1, tmp2;\n\tword32\tgetmem_tmp, save_addr, pull_tmp, tmp_bytes, dummy1;\n\n\ttmp_pc_ptr = 0;\n\tdummy1 = 0;\n\tif(tmp_pc_ptr || dummy1) {\t// \"use\" tmp_pc_ptr to avoid warning\n\t}\n\n\tkpc = engine_ptr->kpc;\n\tacc = engine_ptr->acc;\n\txreg = engine_ptr->xreg;\n\tyreg = engine_ptr->yreg;\n\tstack = engine_ptr->stack;\n\tdbank = engine_ptr->dbank;\n\tdirect = engine_ptr->direct;\n\tpsr = engine_ptr->psr;\n\tfplus_ptr = engine_ptr->fplus_ptr;\n\tzero = !(psr & 2);\n\tneg7 = psr;\n\n\tdplus_1 = fplus_ptr->dplus_1;\n\tdplus_x_m1 = fplus_ptr->dplus_x_minus_1;\n\tdfcyc = engine_ptr->dfcyc;\n\n\tg_ret1 = 0;\n\n\twhile(dfcyc <= g_dcycles_end) {\n\n\t\tFETCH_OPCODE;\n\n\t\tLOG_PC_MACRO();\n\n\t\tswitch(opcode) {\n\t\tdefault:\n\t\t\thalt_printf(\"acc8 unk op: %02x\\n\", opcode);\n\t\t\targ = 9\n#include \"defs_instr.h\"\n\t\t\t* 2;\n\t\t\tbreak;\n#include \"instable.h\"\n\t\t\tbreak;\n\t\t}\n\t\tLOG_PC_MACRO2();\n\t}\n\n\tengine_ptr->kpc = kpc;\n\tengine_ptr->acc = acc;\n\tengine_ptr->xreg = xreg;\n\tengine_ptr->yreg = yreg;\n\tengine_ptr->stack = stack;\n\tengine_ptr->dbank = dbank;\n\tengine_ptr->direct = direct;\n\tengine_ptr->dfcyc = dfcyc;\n\n\tpsr = psr & (~0x82);\n\tpsr |= (neg7 & 0x80);\n\tpsr |= ((!zero) << 1);\n\n\tengine_ptr->psr = psr;\n\n\treturn g_ret1;\n}\n"
  },
  {
    "path": "gsplus/src/engine_c.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2025 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include \"defc.h\"\n\n// PSR[8:0] is E_NVMX_DIZC\n\nextern int g_limit_speed;\nextern int g_halt_sim;\nextern int g_engine_recalc_event;\nextern int g_code_red;\nextern int g_ignore_halts;\nextern int g_user_halt_bad;\nextern dword64 g_dcycles_end;\nextern dword64 g_last_vbl_dfcyc;\nextern dword64 g_cur_dfcyc;\nextern int g_wait_pending;\nextern int g_irq_pending;\nextern int g_num_brk;\nextern int g_num_cop;\nextern int g_emul_6502_ind_page_cross_bug;\nextern byte *g_slow_memory_ptr;\nextern byte *g_memory_ptr;\nextern byte *g_rom_fc_ff_ptr;\nextern byte *g_rom_cards_ptr;\nextern byte *g_dummy_memory1_ptr;\n\nextern int g_num_breakpoints;\nextern Break_point g_break_pts[];\n\nextern Kimage g_debugwin_kimage;\n\nextern word32 g_log_pc_enable;\nextern Pc_log *g_log_pc_ptr;\nextern Pc_log *g_log_pc_start_ptr;\nextern Pc_log *g_log_pc_end_ptr;\n\nextern Data_log *g_log_data_ptr;\nextern Data_log *g_log_data_start_ptr;\nextern Data_log *g_log_data_end_ptr;\n\nint\tg_ret1 = 0;\n\nint size_tab[] = {\n#include \"size_c.h\"\n};\n\nint bogus[] = {\n\t0,\n#include \"op_routs.h\"\n};\n\n#define INC_KPC_1\tkpc = (kpc & 0xff0000) + ((kpc + 1) & 0xffff);\n#define INC_KPC_2\tkpc = (kpc & 0xff0000) + ((kpc + 2) & 0xffff);\n#define INC_KPC_3\tkpc = (kpc & 0xff0000) + ((kpc + 3) & 0xffff);\n#define INC_KPC_4\tkpc = (kpc & 0xff0000) + ((kpc + 4) & 0xffff);\n\n#define CYCLES_PLUS_1\tdfcyc += dplus_1;\n#define CYCLES_PLUS_2\tdfcyc += dplus_1 * 2;\n#define CYCLES_PLUS_3\tdfcyc += dplus_1 * 3;\n#define CYCLES_PLUS_4\tdfcyc += dplus_1 * 4;\n#define CYCLES_PLUS_5\tdfcyc += dplus_1 * 5;\n#define CYCLES_MINUS_1\tdfcyc -= dplus_1;\n#define CYCLES_MINUS_2\tdfcyc -= dplus_1 * 2;\n\n#define FCYCLES_ROUND\tdfcyc = dfcyc + dplus_x_m1;\t\t\\\n\t\t\tdfcyc = (dfcyc >> 16) << 16;\n\n\n#define\tGET_1BYTE_ARG\targ = arg_ptr[1];\n#define\tGET_2BYTE_ARG\targ = arg_ptr[1] + (arg_ptr[2] << 8);\n#define\tGET_3BYTE_ARG\targ = arg_ptr[1] + (arg_ptr[2] << 8) + (arg_ptr[3]<<16);\n\n#define LOG_DATA_MACRO_ACT(in_addr, in_val, in_size, in_stat)\t\t\\\n\t\tg_log_data_ptr->dfcyc = dfcyc;\t\t\t\t\\\n\t\tg_log_data_ptr->stat = in_stat;\t\t\t\t\\\n\t\tg_log_data_ptr->addr = in_addr;\t\t\t\t\\\n\t\tg_log_data_ptr->val = in_val;\t\t\t\t\\\n\t\tg_log_data_ptr->size = in_size;\t\t\t\t\\\n\t\tg_log_data_ptr++;\t\t\t\t\t\\\n\t\tif(g_log_data_ptr >= g_log_data_end_ptr) {\t\t\\\n\t\t\tg_log_data_ptr = g_log_data_start_ptr;\t\t\\\n\t\t}\n\n/* HACK HACK HACK */\n#define\tUPDATE_PSR(dummy, old_psr)\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tpsr |= 0x30;\t\t\t\t\t\\\n\t\tstack = 0x100 + (stack & 0xff);\t\t\t\\\n\t}\t\t\t\t\t\t\t\\\n\tif((~old_psr & psr) & 0x10) {\t\t\t\t\\\n\t\txreg = xreg & 0xff;\t\t\t\t\\\n\t\tyreg = yreg & 0xff;\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\\\n\tif(((psr & 4) == 0) && g_irq_pending) {\t\t\t\\\n\t\tFINISH(RET_IRQ, 0);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\\\n\tif((old_psr ^ psr) & 0x20) {\t\t\t\t\\\n\t\tFINISH(RET_PSR, 0);\t\t\t\t\\\n\t}\n\nextern Page_info page_info_rd_wr[];\nextern word32 g_slow_mem_changed[];\n\n#define GET_MEMORY8(addr,dest)\t\t\t\t\t\\\n\taddr_latch = (addr);\t\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\t\t\\\n\tstat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff);\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\\\n\tif(wstat & (1 << (31 - BANK_IO_BIT))) {\t\t\t\\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\\\n\t\tdest = get_memory8_io_stub((addr), stat,\t\\\n\t\t\t\t&dcycles_tmp1, dplus_x_m1);\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\tdest = *ptr;\t\t\t\t\t\\\n\t}\n\n#define GET_MEMORY(addr,dest)\tGET_MEMORY8(addr, dest)\n\n#define GET_MEMORY16(addr, dest, in_bank)\t\t\t\\\n\tsave_addr = addr;\t\t\t\t\t\\\n\tstat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff);\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\\\n\tif((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xff) == 0xff)) { \\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\\\n\t\tdest = get_memory16_pieces_stub((addr), stat,\t\\\n\t\t\t&dcycles_tmp1, fplus_ptr, in_bank);\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_2;\t\t\t\t\t\\\n\t\tdest = ptr[0] + (ptr[1] << 8);\t\t\t\\\n\t}\t\t\t\t\t\t\t\\\n\taddr_latch = save_addr;\n\n#define GET_MEMORY24(addr, dest, in_bank)\t\t\t\\\n\tsave_addr = addr;\t\t\t\t\t\\\n\tstat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff);\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\\\n\tif((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xfe) == 0xfe)) { \\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\\\n\t\tdest = get_memory24_pieces_stub((addr), stat,\t\\\n\t\t\t&dcycles_tmp1, fplus_ptr, in_bank);\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_3;\t\t\t\t\t\\\n\t\tdest = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16);\t\\\n\t}\t\t\t\t\t\t\t\\\n\taddr_latch = save_addr;\n\n#define GET_MEMORY_DIRECT_PAGE16(addr, dest, dloc_x_wrap)\t\t\\\n\tsave_addr = addr;\t\t\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\t\\\n\t\tif((direct & 0xff) == 0) {\t\t\t\t\\\n\t\t\tsave_addr = (save_addr & 0xff) + direct;\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tif((psr & 0x100) && (((addr) & 0xff) == 0xff)) {\t\t\\\n\t\tGET_MEMORY8(save_addr, getmem_tmp);\t\t\t\\\n\t\tif(dloc_x_wrap) {\t\t\t\t\t\\\n\t\t\tsave_addr = (save_addr & 0xff00) |\t\t\\\n\t\t\t\t\t((save_addr + 1) & 0xff);\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\tsave_addr = (save_addr + 1) & 0xffff;\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tif((direct & 0xff) == 0) {\t\t\t\t\\\n\t\t\tsave_addr = (save_addr & 0xff) + direct;\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tGET_MEMORY8(save_addr, dest);\t\t\t\t\\\n\t\tdest = (dest << 8) + getmem_tmp;\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tGET_MEMORY16(save_addr, dest, 1);\t\t\t\\\n\t}\n\n\n#define PUSH8(arg)\t\t\t\t\t\t\\\n\tSET_MEMORY8(stack, arg);\t\t\t\t\\\n\tstack = (stack - 1) & 0xffff;\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\n\n#define PUSH16(arg)\t\t\t\t\t\t\\\n\tif((stack & 0xfe) == 0) {\t\t\t\t\\\n\t\t/* stack will cross page! */\t\t\t\\\n\t\tPUSH8((arg) >> 8);\t\t\t\t\\\n\t\tPUSH8(arg);\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\tstack = (stack - 2) & 0xffff;\t\t\t\\\n\t\tSET_MEMORY16(stack + 1, arg, 1);\t\t\\\n\t}\n\n#define PUSH16_UNSAFE(arg)\t\t\t\t\t\\\n\tsave_addr = (stack - 1) & 0xffff;\t\t\t\\\n\tstack = (stack - 2) & 0xffff;\t\t\t\t\\\n\tSET_MEMORY16(save_addr, arg, 1);\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\n\n#define PUSH24_UNSAFE(arg)\t\t\t\t\t\\\n\tsave_addr = (stack - 2) & 0xffff;\t\t\t\\\n\tstack = (stack - 3) & 0xffff;\t\t\t\t\\\n\tSET_MEMORY24(save_addr, arg, 1);\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\n\n#define PULL8(dest)\t\t\t\t\t\t\\\n\tstack++;\t\t\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\t\t\t\t\t\t\t\\\n\tstack = stack & 0xffff;\t\t\t\t\t\\\n\tGET_MEMORY8(stack, dest);\n\n#define PULL8_UNSAFE(dest)\t\t\t\t\t\\\n\tstack = (stack + 1) & 0xffff;\t\t\t\t\\\n\tGET_MEMORY8(stack, dest);\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\n\n#define PULL16(dest)\t\t\t\t\t\t\\\n\tif((stack & 0xfe) == 0xfe) {\t/* page cross */\t\\\n\t\tPULL8(dest);\t\t\t\t\t\\\n\t\tPULL8(pull_tmp);\t\t\t\t\\\n\t\tdest = (pull_tmp << 8) + dest;\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\tGET_MEMORY16(stack + 1, dest, 1);\t\t\\\n\t\tstack = (stack + 2) & 0xffff;\t\t\t\\\n\t\tif(psr & 0x100) {\t\t\t\t\\\n\t\t\tstack = 0x100 | (stack & 0xff);\t\t\\\n\t\t}\t\t\t\t\t\t\\\n\t}\n\n#define PULL16_UNSAFE(dest)\t\t\t\t\t\\\n\tstack = (stack + 1) & 0xffff;\t\t\t\t\\\n\tGET_MEMORY16(stack, dest, 1);\t\t\t\t\\\n\tstack = (stack + 1) & 0xffff;\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\n\n#define PULL24(dest)\t\t\t\t\t\t\\\n\tif((stack & 0xfc) == 0xfc) {\t/* page cross */\t\\\n\t\tPULL8(dest);\t\t\t\t\t\\\n\t\tPULL8(pull_tmp);\t\t\t\t\\\n\t\tpull_tmp = (pull_tmp << 8) + dest;\t\t\\\n\t\tPULL8(dest);\t\t\t\t\t\\\n\t\tdest = (dest << 16) + pull_tmp;\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\tGET_MEMORY24(stack + 1, dest, 1);\t\t\\\n\t\tstack = (stack + 3) & 0xffff;\t\t\t\\\n\t\tif(psr & 0x100) {\t\t\t\t\\\n\t\t\tstack = 0x100 | (stack & 0xff);\t\t\\\n\t\t}\t\t\t\t\t\t\\\n\t}\n\n#define PULL24_UNSAFE(dest)\t\t\t\t\t\\\n\tstack = (stack + 1) & 0xffff;\t\t\t\t\\\n\tGET_MEMORY24(stack, dest, 1);\t\t\t\t\\\n\tstack = (stack + 2) & 0xffff;\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\n\n#define SET_MEMORY8(addr, val)\t\t\t\t\t\t\\\n\tstat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff);\t\t\\\n\tLOG_DATA_MACRO(addr, val, 8, stat);\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\t\t\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\t\\\n\tif(wstat) {\t\t\t\t\t\t\t\\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\t\\\n\t\tset_memory8_io_stub((addr), val, stat, &dcycles_tmp1,\t\\\n\t\t\t\tdplus_x_m1);\t\t\t\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\t*ptr = val;\t\t\t\t\t\t\\\n\t}\n\n\n#define SET_MEMORY16(addr, val, in_bank)\t\t\t\t\\\n\tstat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff);\t\t\\\n\tLOG_DATA_MACRO(addr, val, 16, stat);\t\t\t\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\t\\\n\tif((wstat) || (((addr) & 0xff) == 0xff)) {\t\t\t\\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\t\\\n\t\tset_memory16_pieces_stub((addr), (val),\t\t\t\\\n\t\t\t&dcycles_tmp1, dplus_1, dplus_x_m1, in_bank);\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_2;\t\t\t\t\t\t\\\n\t\tptr[0] = (val);\t\t\t\t\t\t\\\n\t\tptr[1] = (val) >> 8;\t\t\t\t\t\\\n\t}\n\n#define SET_MEMORY24(addr, val, in_bank)\t\t\t\t\\\n\tstat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff);\t\t\\\n\tLOG_DATA_MACRO(addr, val, 24, stat);\t\t\t\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\t\\\n\tif((wstat) || (((addr) & 0xfe) == 0xfe)) {\t\t\t\\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\t\\\n\t\tset_memory24_pieces_stub((addr), (val),\t\t\t\\\n\t\t\t&dcycles_tmp1, fplus_ptr, in_bank);\t\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_3;\t\t\t\t\t\t\\\n\t\tptr[0] = (val);\t\t\t\t\t\t\\\n\t\tptr[1] = (val) >> 8;\t\t\t\t\t\\\n\t\tptr[2] = (val) >> 16;\t\t\t\t\t\\\n\t}\n\n\nword32\nget_memory8_io_stub(word32 addr, byte *stat, dword64 *dcycs_ptr,\n\t\t\t\t\t\t\tdword64 dplus_x_m1)\n{\n\tdword64\tdfcyc;\n\tword32\twstat;\n\tbyte\t*ptr;\n\n\twstat = PTR2WORD(stat) & 0xff;\n\tdfcyc = *dcycs_ptr;\n\tif(wstat & BANK_BREAK) {\n\t\tcheck_breakpoints(addr, dfcyc, 0, 1);\n\t}\n\tif(wstat & BANK_IO2_TMP) {\n\t\tFCYCLES_ROUND;\n\t\t*dcycs_ptr = dfcyc;\n\t\treturn get_memory_io((addr), dcycs_ptr);\n\t} else {\n\t\tptr = stat - wstat + (addr & 0xff);\n\t\treturn *ptr;\n\t}\n}\n\nword32\nget_memory16_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr,\n\t\tFplus\t*fplus_ptr, int in_bank)\n{\n\tbyte\t*ptr;\n\tdword64\tdfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;\n\tword32\taddrp1, wstat, ret, tmp1, addr_latch;\n\n\taddr_latch = 0;\n\tif(addr_latch != 0) {\t// \"Use\" addr_latch to avoid warning\n\t}\n\tdfcyc = *dcycs_ptr;\n\tdplus_1 = fplus_ptr->dplus_1;\n\tdplus_x_m1 = fplus_ptr->dplus_x_minus_1;\n\tGET_MEMORY8(addr, tmp1);\n\taddrp1 = addr + 1;\n\tif(in_bank) {\n\t\taddrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);\n\t}\n\tGET_MEMORY8(addrp1, ret);\n\t*dcycs_ptr = dfcyc;\n\treturn (ret << 8) + (tmp1);\n}\n\nword32\nget_memory24_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr,\n\t\tFplus *fplus_ptr, int in_bank)\n{\n\tbyte\t*ptr;\n\tdword64\tdfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;\n\tword32\taddrp1, addrp2, wstat, addr_latch, ret, tmp1, tmp2;\n\n\taddr_latch = 0;\n\tif(addr_latch != 0) {\t// \"Use\" addr_latch to avoid warning\n\t}\n\tdfcyc = *dcycs_ptr;\n\tdplus_1 = fplus_ptr->dplus_1;\n\tdplus_x_m1 = fplus_ptr->dplus_x_minus_1;\n\tGET_MEMORY8(addr, tmp1);\n\taddrp1 = addr + 1;\n\tif(in_bank) {\n\t\taddrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);\n\t}\n\tGET_MEMORY8(addrp1, tmp2);\n\taddrp2 = addr + 2;\n\tif(in_bank) {\n\t\taddrp2 = (addr & 0xff0000) + (addrp2 & 0xffff);\n\t}\n\tGET_MEMORY8(addrp2, ret);\n\t*dcycs_ptr = dfcyc;\n\treturn (ret << 16) + (tmp2 << 8) + tmp1;\n}\n\nvoid\nset_memory8_io_stub(word32 addr, word32 val, byte *stat, dword64 *dcycs_ptr,\n\t\tdword64 dplus_x_m1)\n{\n\tbyte\t*ptr;\n\tdword64\tdfcyc;\n\tword32\tsetmem_tmp1, tmp1, tmp2, wstat;\n\n\twstat = PTR2WORD(stat) & 0xff;\n\tdfcyc = *dcycs_ptr;\n\tif(wstat & (1 << (31 - BANK_BREAK_BIT))) {\n\t\tcheck_breakpoints(addr, dfcyc, 0, 2);\n\t}\n\tptr = stat - wstat + ((addr) & 0xff);\n\tif(wstat & (1 << (31 - BANK_IO2_BIT))) {\n\t\tFCYCLES_ROUND;\n\t\t*dcycs_ptr = dfcyc;\n\t\tset_memory_io((addr), val, dcycs_ptr);\n\t} else if(wstat & (1 << (31 - BANK_SHADOW_BIT))) {\n\t\tif(g_limit_speed) {\n\t\t\tFCYCLES_ROUND;\n\t\t\t*dcycs_ptr = dfcyc;\n\t\t}\n\t\ttmp1 = (addr & 0xffff);\n\t\tsetmem_tmp1 = g_slow_memory_ptr[tmp1];\n\t\t*ptr = val;\n\t\tg_slow_memory_ptr[tmp1] = val;\n\t\tif(setmem_tmp1 != ((val) & 0xff)) {\n\t\t\tg_slow_mem_changed[tmp1 >> CHANGE_SHIFT] |=\n\t\t\t\t(1U << ((tmp1 >> SHIFT_PER_CHANGE) & 31));\n\t\t}\n\t} else if(wstat & (1 << (31 - BANK_SHADOW2_BIT))) {\n\t\tif(g_limit_speed) {\n\t\t\tFCYCLES_ROUND;\n\t\t\t*dcycs_ptr = dfcyc;\n\t\t}\n\t\ttmp2 = (addr & 0xffff);\n\t\ttmp1 = 0x10000 + tmp2;\n\t\tsetmem_tmp1 = g_slow_memory_ptr[tmp1];\n\t\t*ptr = val;\n\t\tg_slow_memory_ptr[tmp1] = val;\n\t\tif(setmem_tmp1 != ((val) & 0xff)) {\n\t\t\tg_slow_mem_changed[tmp1 >> CHANGE_SHIFT] |=\n\t\t\t\t(1U << ((tmp1 >> SHIFT_PER_CHANGE) & 31));\n\t\t\tif((tmp1 & 0xff00) == 0x9d00) {\n\t\t\t\tscb_changed(dfcyc, tmp1, val, setmem_tmp1);\n\t\t\t}\n\t\t}\n\t} else {\n\t\t/* breakpoint only */\n\t\t*ptr = val;\n\t}\n}\n\n#define LOG_PC_MACRO()\n#define LOG_PC_MACRO2()\n#define LOG_DATA_MACRO(addr, val, size, in_stat)\n\nvoid\nset_memory16_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr,\n\t\tdword64 dplus_1, dword64 dplus_x_m1, int in_bank)\n{\n\tbyte\t*ptr;\n\tbyte\t*stat;\n\tdword64\tdfcyc, dcycles_tmp1;\n\tword32\taddrp1, wstat;\n\n\tdfcyc = *dcycs_ptr;\n\tSET_MEMORY8(addr, val);\n\taddrp1 = addr + 1;\n\tif(in_bank) {\n\t\taddrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);\n\t}\n\tSET_MEMORY8(addrp1, val >> 8);\n\n\t*dcycs_ptr = dfcyc;\n}\n\nvoid\nset_memory24_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr,\n\t\tFplus\t*fplus_ptr, int in_bank)\n{\n\tbyte\t*ptr;\n\tbyte\t*stat;\n\tdword64\tdfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;\n\tword32\taddrp1, addrp2;\n\tword32\twstat;\n\n\tdfcyc = *dcycs_ptr;\n\tdplus_1 = fplus_ptr->dplus_1;\n\tdplus_x_m1 = fplus_ptr->dplus_x_minus_1;\n\tSET_MEMORY8(addr, val);\n\taddrp1 = addr + 1;\n\tif(in_bank) {\n\t\taddrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);\n\t}\n\tSET_MEMORY8(addrp1, val >> 8);\n\taddrp2 = addr + 2;\n\tif(in_bank) {\n\t\taddrp2 = (addr & 0xff0000) + (addrp2 & 0xffff);\n\t}\n\tSET_MEMORY8(addrp2, val >> 16);\n\n\t*dcycs_ptr = dfcyc;\n}\n\nword32\nget_memory_c(word32 addr)\n{\n\tbyte\t*stat, *ptr;\n\tdword64\tdfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;\n\tword32\taddr_latch, wstat, ret;\n\n\tdfcyc = 0;\n\tdplus_1 = 0;\n\tdplus_x_m1 = 0;\n\taddr_latch = 0;\n\tif(addr_latch != 0) {\t// \"Use\" addr_latch to avoid warning\n\t}\n\tGET_MEMORY8(addr, ret);\n\treturn ret;\n}\n\nword32\nget_memory16_c(word32 addr)\n{\n\treturn get_memory_c(addr) +\n\t\t\t(get_memory_c(addr+1) << 8);\n}\n\nword32\nget_memory24_c(word32 addr)\n{\n\treturn get_memory_c(addr) +\n\t\t\t(get_memory_c(addr+1) << 8) +\n\t\t\t(get_memory_c(addr+2) << 16);\n}\n\nvoid\nset_memory_c(word32 addr, word32 val, int do_log)\n{\n\tbyte\t*stat, *ptr;\n\tdword64\tdfcyc, dcycles_tmp1, dplus_1, dplus_x_m1;\n\tword32\twstat;\n\n\tdfcyc = g_cur_dfcyc;\n\tdplus_1 = 0;\n\tdplus_x_m1 = 0;\n\tSET_MEMORY8(addr, val);\n\tif(g_log_pc_enable && do_log) {\n\t\tLOG_DATA_MACRO_ACT(addr, val, 8, stat)\n\t}\n}\n\nvoid\nset_memory16_c(word32 addr, word32 val, int do_log)\n{\n\tbyte\t*stat, *ptr;\n\tdword64\tdfcyc, dcycles_tmp1, dplus_1, dplus_x_m1;\n\tword32\twstat;\n\n\tdfcyc = g_cur_dfcyc;\n\tdplus_1 = 0;\n\tdplus_x_m1 = 0;\n\tSET_MEMORY16(addr, val, 0);\n\tif(g_log_pc_enable && do_log) {\n\t\tLOG_DATA_MACRO_ACT(addr, val, 16, stat)\n\t}\n}\n\nvoid\nset_memory24_c(word32 addr, word32 val)\n{\n\tset_memory_c(addr, val, 1);\n\tset_memory_c(addr + 1, val >> 8, 1);\n\tset_memory_c(addr + 2, val >> 16, 1);\n}\n\nword32\ndo_adc_sbc8(word32 in1, word32 in2, word32 psr, int sub)\n{\n\tword32\tsum, carry, overflow;\n\tword32\tzero;\n\tint\tdecimal;\n\n\toverflow = 0;\n\tdecimal = psr & 8;\n\tif(sub) {\n\t\tin2 = (in2 ^ 0xff);\n\t}\n\tif(!decimal) {\n\t\tsum = (in1 & 0xff) + in2 + (psr & 1);\n\t\toverflow = ((sum ^ in2) >> 1) & 0x40;\n\t} else {\n\t\t/* decimal */\n\t\tsum = (in1 & 0xf) + (in2 & 0xf) + (psr & 1);\n\t\tif(sub) {\n\t\t\tif(sum < 0x10) {\n\t\t\t\tsum = (sum - 0x6) & 0xf;\n\t\t\t}\n\t\t} else {\n\t\t\tif(sum >= 0xa) {\n\t\t\t\tsum = (sum - 0xa) | 0x10;\n\t\t\t}\n\t\t}\n\n\t\tsum = (in1 & 0xf0) + (in2 & 0xf0) + sum;\n\t\toverflow = ((sum >> 2) ^ (sum >> 1)) & 0x40;\n\t\tif(sub) {\n\t\t\tif(sum < 0x100) {\n\t\t\t\tsum = (sum + 0xa0) & 0xff;\n\t\t\t}\n\t\t} else {\n\t\t\tif(sum >= 0xa0) {\n\t\t\t\tsum += 0x60;\n\t\t\t}\n\t\t}\n\t}\n\n\tzero = ((sum & 0xff) == 0);\n\tcarry = (sum >= 0x100);\n\tif((in1 ^ in2) & 0x80) {\n\t\toverflow = 0;\n\t}\n\n\tpsr = psr & (~0xc3);\n\tpsr = psr + (sum & 0x80) + overflow + (zero << 1) + carry;\n\n\treturn (psr << 16) + (sum & 0xff);\n}\n\nword32\ndo_adc_sbc16(word32 in1, word32 in2, word32 psr, int sub)\n{\n\tword32\tsum, carry, overflow;\n\tword32\ttmp1, tmp2;\n\tword32\tzero;\n\tint\tdecimal;\n\n\toverflow = 0;\n\tdecimal = psr & 8;\n\tif(!decimal) {\n\t\tif(sub) {\n\t\t\tin2 = (in2 ^ 0xffff);\n\t\t}\n\t\tsum = in1 + in2 + (psr & 1);\n\t\toverflow = ((sum ^ in2) >> 9) & 0x40;\n\t} else {\n\t\t/* decimal */\n\t\tif(sub) {\n\t\t\ttmp1 = do_adc_sbc8(in1 & 0xff, in2 & 0xff, psr, sub);\n\t\t\tpsr = (tmp1 >> 16);\n\t\t\ttmp2 = do_adc_sbc8((in1 >> 8) & 0xff,\n\t\t\t\t\t\t(in2 >> 8) & 0xff, psr, sub);\n\t\t\tin2 = (in2 ^ 0xfffff);\n\t\t} else {\n\t\t\ttmp1 = do_adc_sbc8(in1 & 0xff, in2 & 0xff, psr, sub);\n\t\t\tpsr = (tmp1 >> 16);\n\t\t\ttmp2 = do_adc_sbc8((in1 >> 8) & 0xff,\n\t\t\t\t\t\t(in2 >> 8) &0xff, psr, sub);\n\t\t}\n\t\tsum = ((tmp2 & 0xff) << 8) + (tmp1 & 0xff) +\n\t\t\t\t\t(((tmp2 >> 16) & 1) << 16);\n\t\toverflow = (tmp2 >> 16) & 0x40;\n\t}\n\n\tzero = ((sum & 0xffff) == 0);\n\tcarry = (sum >= 0x10000);\n\tif((in1 ^ in2) & 0x8000) {\n\t\toverflow = 0;\n\t}\n\n\tpsr = psr & (~0xc3);\n\tpsr = psr + ((sum & 0x8000) >> 8) + overflow + (zero << 1) + carry;\n\n\treturn (psr << 16) + (sum & 0xffff);\n}\n\nvoid\nfixed_memory_ptrs_init()\n{\n\t/* set g_slow_memory_ptr, g_rom_fc_ff_ptr, g_dummy_memory1_ptr, */\n\t/*  and rom_cards_ptr */\n\n\tg_slow_memory_ptr = memalloc_align(128*1024, 0, 0);\n\tg_dummy_memory1_ptr = memalloc_align(256, 1024, 0);\n\tg_rom_fc_ff_ptr = memalloc_align(256*1024, 512, 0);\n\tg_rom_cards_ptr = memalloc_align(16*256, 256, 0);\n\n#if 0\n\tprintf(\"g_memory_ptr: %08x, dummy_mem: %08x, slow_mem_ptr: %08x\\n\",\n\t\t(word32)g_memory_ptr, (word32)g_dummy_memory1_ptr,\n\t\t(word32)g_slow_memory_ptr);\n\tprintf(\"g_rom_fc_ff_ptr: %08x, g_rom_cards_ptr: %08x\\n\",\n\t\t(word32)g_rom_fc_ff_ptr, (word32)g_rom_cards_ptr);\n\tprintf(\"page_info_rd = %08x, page_info_wr end = %08x\\n\",\n\t\t(word32)&(page_info_rd_wr[0]),\n\t\t(word32)&(page_info_rd_wr[PAGE_INFO_PAD_SIZE+0x1ffff].rd_wr));\n#endif\n}\n\nword32\nget_itimer()\n{\n#if defined(__i386) && defined(__GNUC__)\n\t/* Here's my bad ia32 asm code to do rdtsc */\n\t/* Linux source uses: */\n\t/* asm volatile(\"rdtsc\" : \"=a\"(ret) : : \"edx\"); */\n\t/* asm volatile(\"rdtsc\" : \"=%eax\"(ret) : : \"%edx\"); */\n\n\t/* GCC bug report 2001-03/msg00786.html used: */\n\t/*register dword64 dtmp; */\n\t/*asm volatile (\"rdtsc\" : \"=A\" (dtmp)); */\n\t/*return (word32)dtmp; */\n\n\tregister word32 ret;\n\n\tasm volatile (\"rdtsc;movl %%eax,%0\" : \"=r\"(ret) : : \"%eax\",\"%edx\");\n\n\treturn ret;\n#else\n# if defined(__POWERPC__) && defined(__GNUC__)\n\tregister word32 ret;\n\n\tasm volatile (\"mftb %0\" : \"=r\"(ret));\n\treturn ret;\n# else\n#  if defined(__x86_64__)\n\tregister word32 ret, hi;\n\t//ret = __rdtsc();\n\tasm volatile (\"rdtsc\" : \"=a\" (ret), \"=d\" (hi));\n\treturn ret;\n#  else\n#\tif defined(__aarch64__)\t\t/* 64-bit ARM architecture */\n\t\tregister dword64 ret;\n\t\tasm volatile(\"mrs %0,CNTVCT_EL0\" : \"=r\"(ret));\n\t\treturn ret;\n#\telse\n\t\treturn 0;\n#\tendif\n#  endif\n# endif\n#endif\n}\n\nvoid\nengine_recalc_events()\n{\n\tg_dcycles_end = 0;\t\t// End inner loop\n\tg_engine_recalc_event++;\n}\n\nvoid\nset_halt_act(int val)\n{\n\tif((val == 1) && g_ignore_halts && !g_user_halt_bad) {\n\t\tg_code_red++;\n\t} else {\n\t\tif(g_halt_sim == 0) {\n\t\t\tdebugger_update_list_kpc();\n\t\t}\n\t\tg_halt_sim |= val;\n\t\tif(g_halt_sim) {\n\t\t\tvideo_set_active(&g_debugwin_kimage, 1);\n\t\t}\n\t\tg_dcycles_end = 0;\n\t}\n}\n\nvoid\nclr_halt_act()\n{\n\tg_halt_sim = 0;\n}\n\nword32\nget_remaining_operands(word32 addr, word32 opcode, word32 psr,\n\t\t\t\t\tdword64 *dcyc_ptr, Fplus *fplus_ptr)\n{\n\tbyte\t*stat, *ptr;\n\tdword64\tdfcyc, dcycles_tmp1, dplus_1, dplus_x_m1;\n\tword32\taddr_latch, wstat, save_addr, arg, addrp1;\n\tint\tsize;\n\n\tdfcyc = *dcyc_ptr;\n\tdplus_1 = fplus_ptr->dplus_1;\n\tdplus_x_m1 = fplus_ptr->dplus_x_minus_1;\n\taddr_latch = 0;\n\tif(addr_latch != 0) {\t// \"Use\" addr_latch to avoid warning\n\t}\n\n\tsize = size_tab[opcode];\n\n\taddrp1 = (addr & 0xff0000) + ((addr + 1) & 0xffff);\n\tswitch(size) {\n\tcase 0:\n\t\t// Always read pc+1 for single-byte opcodes\n\t\tGET_MEMORY8(addrp1, arg);\n\t\targ = 0;\t/* no args */\n\t\tbreak;\n\tcase 1:\n\t\tGET_MEMORY8(addrp1, arg);\n\t\tbreak;\t\t/* 1 arg, already done */\n\tcase 2:\n\t\tGET_MEMORY16(addrp1, arg, 1);\n\t\tdfcyc -= dplus_1;\n\t\tbreak;\n\tcase 3:\n\t\tGET_MEMORY24(addrp1, arg, 1);\n\t\tdfcyc = dfcyc - dplus_1 - dplus_1;\n\t\tbreak;\n\tcase 4:\n\t\tif(psr & 0x20) {\n\t\t\tGET_MEMORY8(addrp1, arg);\n\t\t} else {\n\t\t\tGET_MEMORY16(addrp1, arg, 1);\n\t\t\tdfcyc -= dplus_1;\n\t\t}\n\t\tbreak;\n\tcase 5:\n\t\tif(psr & 0x10) {\n\t\t\tGET_MEMORY8(addrp1, arg);\n\t\t} else {\n\t\t\tGET_MEMORY16(addrp1, arg, 1);\n\t\t\tdfcyc -= dplus_1;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tprintf(\"Unknown size: %d\\n\", size);\n\t\targ = 0;\n\t\texit(-2);\n\t}\n\t*dcyc_ptr = dfcyc;\n\n\treturn arg;\n}\n\n#define FETCH_OPCODE\t\t\t\t\t\t\t\\\n\taddr = kpc;\t\t\t\t\t\t\t\\\n\tCYCLES_PLUS_2;\t\t\t\t\t\t\t\\\n\tstat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff);\t\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\t\\\n\targ_ptr = ptr;\t\t\t\t\t\t\t\\\n\topcode = *ptr;\t\t\t\t\t\t\t\\\n\tif((wstat & (1 << (31-BANK_IO_BIT))) || ((addr & 0xff) > 0xfc)) {\\\n\t\tCYCLES_MINUS_1;\t\t\t\t\t\t\\\n\t\tif(wstat & BANK_BREAK) {\t\t\t\t\\\n\t\t\tcheck_breakpoints(addr, dfcyc, stack, 4);\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tif(wstat & (1 << (31 - BANK_IO2_BIT))) {\t\t\\\n\t\t\tFCYCLES_ROUND;\t\t\t\t\t\\\n\t\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\\\n\t\t\topcode = get_memory_io((addr), &dcycles_tmp1);\t\\\n\t\t\tdfcyc = dcycles_tmp1;\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\topcode = *ptr;\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\t\\\n\t\targ = get_remaining_operands(addr, opcode, psr,\t\t\\\n\t\t\t\t\t&dcycles_tmp1, fplus_ptr);\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\t\\\n\t\targ_ptr = (byte *)&tmp_bytes;\t\t\t\t\\\n\t\targ_ptr[1] = arg;\t\t\t\t\t\\\n\t\targ_ptr[2] = arg >> 8;\t\t\t\t\t\\\n\t\targ_ptr[3] = arg >> 16;\t\t\t\t\t\\\n\t}\n\n\n#define ACC8\n#define IS_ACC16\t0\n#define ENGINE_TYPE enter_engine_acc8\n#include \"engine.h\"\n// The above creates enter_engine_acc8\n\n#undef ACC8\n#undef IS_ACC16\n#undef ENGINE_TYPE\n#define IS_ACC16\t1\n#define ENGINE_TYPE enter_engine_acc16\n#include \"engine.h\"\n// The above creates enter_engine_acc16\n\n#undef LOG_PC_MACRO\n#undef LOG_PC_MACRO2\n#undef LOG_DATA_MACRO\n\n#define LOG_PC_MACRO()\t\t\t\t\t\t\t\\\n\t\ttmp_pc_ptr = g_log_pc_ptr;\t\t\t\t\\\n\t\ttmp_pc_ptr->dbank_kpc = (dbank << 24) + kpc;\t\t\\\n\t\ttmp_pc_ptr->instr = (opcode << 24) + arg_ptr[1] +\t\\\n\t\t\t(arg_ptr[2] << 8) + (arg_ptr[3] << 16);\t\t\\\n\t\ttmp_pc_ptr->dfcyc = dfcyc - dplus_1 * 2;\n\n#define LOG_PC_MACRO2()\t\t\t\t\t\t\\\n\t\ttmp_pc_ptr->psr_acc = ((psr & ~(0x82)) << 16) | acc |\t\\\n\t\t\t((neg7 & 0x80) << 16) | ((!zero) << 17);\t\\\n\t\ttmp_pc_ptr->xreg_yreg = (xreg << 16) + yreg;\t\t\\\n\t\ttmp_pc_ptr->stack_direct = (stack << 16) + direct;\t\\\n\t\ttmp_pc_ptr++;\t\t\t\t\t\t\\\n\t\tif(tmp_pc_ptr >= g_log_pc_end_ptr) {\t\t\t\\\n\t\t\ttmp_pc_ptr = g_log_pc_start_ptr;\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tg_log_pc_ptr = tmp_pc_ptr;\n\n#define LOG_DATA_MACRO(in_addr, in_val, in_size, in_stat)\t\t\\\n\t\tLOG_DATA_MACRO_ACT(in_addr, in_val, in_size, in_stat)\n\n#undef ACC8\n#undef IS_ACC16\n#undef ENGINE_TYPE\n#define ACC8\n#define IS_ACC16\t0\n#define ENGINE_TYPE enter_engine_acc8_log\n#include \"engine.h\"\n// The above creates enter_engine_acc8_log\n\n#undef ACC8\n#undef IS_ACC16\n#undef ENGINE_TYPE\n#define IS_ACC16\t1\n#define ENGINE_TYPE enter_engine_acc16_log\n#include \"engine.h\"\n// The above creates enter_engine_acc16_log\n\n\nint\nenter_engine(Engine_reg *engine_ptr)\n{\n\tdword64\tdcycles_end_save;\n\tint\tret;\n\n\tdcycles_end_save = g_dcycles_end;\n\twhile(1) {\n\t\tif(g_log_pc_enable) {\n\t\t\tif(engine_ptr->psr & 0x20) {\t// 8-bit accumulator\n\t\t\t\tret = enter_engine_acc8_log(engine_ptr);\n\t\t\t} else {\n\t\t\t\tret = enter_engine_acc16_log(engine_ptr);\n\t\t\t}\n\t\t} else {\n\t\t\tif(engine_ptr->psr & 0x20) {\t// 8-bit accumulator\n\t\t\t\tret = enter_engine_acc8(engine_ptr);\n\t\t\t} else {\n\t\t\t\tret = enter_engine_acc16(engine_ptr);\n\t\t\t}\n\t\t}\n\t\tif((ret == RET_PSR) && !g_halt_sim) {\n\t\t\tg_dcycles_end = dcycles_end_save;\n\t\t\tcontinue;\n\t\t}\n\t\treturn ret;\n\t}\n}\n\n"
  },
  {
    "path": "gsplus/src/instable.h",
    "content": "// \"@(#)$KmKId: instable.h,v 1.121 2023-11-12 15:31:14+00 kentd Exp $\"\n\n/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2021 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\ncase 0x00:\t\t\t/*  brk */\n\tGET_1BYTE_ARG;\n\tg_num_brk++;\n\tINC_KPC_2;\n\tpsr = (psr & (~0x82)) | (neg7 & 0x80) | ((!zero) << 1);\n\tif(psr & 0x100) {\n\t\tPUSH16(kpc & 0xffff);\n\t\tPUSH8(psr & 0xff);\n\t\ttmp1 = 0xfffffe;\n\t\tdbank = 0;\n\t} else {\n\t\tPUSH8(kpc >> 16);\n\t\tPUSH16(kpc);\n\t\tPUSH8(psr & 0xff);\n\t\ttmp1 = 0xffffe6;\n\t\thalt_printf(\"Halting for native break!\\n\");\n\t}\n\ttmp1 = moremem_fix_vector_pull(tmp1);\n\tGET_MEMORY16(tmp1, kpc, 0);\n\tkpc = kpc & 0xffff;\n\tpsr |= 0x4;\n\tpsr &= ~(0x8);\n\tbreak;\n\ncase 0x01:\t\t\t/*  ORA (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x02:\t\t\t/*  COP */\n\tg_num_cop++;\n\tINC_KPC_2;\n\tpsr = (psr & ~0x82) | (neg7 & 0x80) | ((!zero) << 1);\n\tif(psr & 0x100) {\n\t\thalt_printf(\"Halting for emul COP at %04x\\n\", kpc);\n\t\tPUSH16(kpc & 0xffff);\n\t\tPUSH8(psr & 0xff);\n\t\ttmp1 = 0xfffff4;\n\t\tdbank = 0;\n\t} else {\n\t\tPUSH8(kpc >> 16);\n\t\tPUSH16(kpc & 0xffff);\n\t\tPUSH8(psr & 0xff);\n\t\ttmp1 = 0xffffe4;\n\t}\n\ttmp1 = moremem_fix_vector_pull(tmp1);\n\tGET_MEMORY16(tmp1, kpc, 0);\n\tkpc = kpc & 0xffff;\n\tpsr |= 4;\n\tpsr &= ~(0x8);\n\tbreak;\n\ncase 0x03:\t\t\t/*  ORA Disp8,S */\n\tGET_DISP8_S_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x04:\t\t\t/*  TSB Dloc */\n\tGET_DLOC_RD_RMW();\n\tTSB_INST(1);\n\tbreak;\n\ncase 0x05:\t\t\t/*  ORA Dloc */\n\tGET_DLOC_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x06:\t\t\t/*  ASL Dloc */\n\tGET_DLOC_RD_RMW();\n\tASL_INST(1);\n\tbreak;\n\ncase 0x07:\t\t\t/*  ORA [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x08:\t\t\t/*  PHP */\n\tINC_KPC_1;\n\tpsr = (psr & ~0x82) | (neg7 & 0x80) | ((!zero) << 1);\n\tPUSH8(psr);\n\tbreak;\n\ncase 0x09:\t\t\t/*  ORA #imm */\n\tGET_IMM_MEM();\n\tORA_INST();\n\tbreak;\n\ncase 0x0a:\t\t\t/*  ASL a */\n\tINC_KPC_1;\n\ttmp1 = acc + acc;\n#ifdef ACC8\n\tSET_CARRY8(tmp1);\n\tacc = (acc & 0xff00) + (tmp1 & 0xff);\n\tSET_NEG_ZERO8(acc & 0xff);\n#else\n\tSET_CARRY16(tmp1);\n\tacc = tmp1 & 0xffff;\n\tSET_NEG_ZERO16(acc);\n#endif\n\tbreak;\n\ncase 0x0b:\t\t\t/*  PHD */\n\tINC_KPC_1;\n\tPUSH16_UNSAFE(direct);\n\tbreak;\n\ncase 0x0c:\t\t\t/*  TSB abs */\n\tGET_ABS_RD_RMW();\n\tTSB_INST(0);\n\tbreak;\n\ncase 0x0d:\t\t\t/*  ORA abs */\n\tGET_ABS_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x0e:\t\t\t/*  ASL abs */\n\tGET_ABS_RD_RMW();\n\tASL_INST(0);\n\tbreak;\n\ncase 0x0f:\t\t\t/*  ORA long */\n\tGET_LONG_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x10:\t\t\t/*  BPL disp8 */\n\tBRANCH_DISP8((neg7 & 0x80) == 0);\n\tbreak;\n\ncase 0x11:\t\t\t/*  ORA (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x12:\t\t\t/*  ORA (Dloc) */\n\tGET_DLOC_IND_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x13:\t\t\t/*  ORA (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x14:\t\t\t/*  TRB Dloc */\n\tGET_DLOC_RD_RMW();\n\tTRB_INST(1);\n\tbreak;\n\ncase 0x15:\t\t\t/*  ORA Dloc,x */\n\tGET_DLOC_X_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x16:\t\t\t/*  ASL Dloc,X */\n\tGET_DLOC_X_RD_RMW();\n\tASL_INST(1);\n\tbreak;\n\ncase 0x17:\t\t\t/*  ORA [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x18:\t\t\t/*  CLC */\n\tpsr = psr & (~1);\n\tINC_KPC_1;\n\tbreak;\n\ncase 0x19:\t\t\t/*  ORA abs,y */\n\tGET_ABS_Y_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x1a:\t\t\t/*  INC a */\n\tINC_KPC_1;\n#ifdef ACC8\n\tacc = (acc & 0xff00) | ((acc + 1) & 0xff);\n\tSET_NEG_ZERO8(acc & 0xff);\n#else\n\tacc = (acc + 1) & 0xffff;\n\tSET_NEG_ZERO16(acc);\n#endif\n\tbreak;\n\ncase 0x1b:\t\t\t/*  TCS */\n\tstack = acc;\n\tINC_KPC_1;\n\tif(psr & 0x100) {\n\t\tstack = (stack & 0xff) + 0x100;\n\t}\n\tbreak;\n\ncase 0x1c:\t\t\t/*  TRB Abs */\n\tGET_ABS_RD_RMW();\n\tTRB_INST(0);\n\tbreak;\n\ncase 0x1d:\t\t\t/*  ORA Abs,X */\n\tGET_ABS_X_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x1e:\t\t\t/*  ASL Abs,X */\n\tGET_ABS_X_RD_RMW();\n\tASL_INST(0);\n\tbreak;\n\ncase 0x1f:\t\t\t/*  ORA Long,X */\n\tGET_LONG_X_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x20:\t\t\t/*  JSR abs */\n\tGET_2BYTE_ARG;\n\tINC_KPC_2;\n\tPUSH16(kpc);\n\tkpc = (kpc & 0xff0000) + arg;\n\tCYCLES_PLUS_2;\n\tbreak;\n\ncase 0x21:\t\t\t/*  AND (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x22:\t\t\t/*  JSL Long */\n\tGET_3BYTE_ARG;\n\ttmp1 = arg;\n\tCYCLES_PLUS_3;\n\tINC_KPC_3;\n\tPUSH24_UNSAFE(kpc);\n\tkpc = tmp1 & 0xffffff;\n\tbreak;\n\ncase 0x23:\t\t\t/*  AND Disp8,S */\n\tGET_DISP8_S_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x24:\t\t\t/*  BIT Dloc */\n\tGET_DLOC_RD();\n\tBIT_INST();\n\tbreak;\n\ncase 0x25:\t\t\t/*  AND Dloc */\n\tGET_DLOC_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x26:\t\t\t/*  ROL Dloc */\n\tGET_DLOC_RD_RMW();\n\tROL_INST(1);\n\tbreak;\n\ncase 0x27:\t\t\t/*  AND [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x28:\t\t\t/*  PLP */\n\tPULL8(tmp1);\n\ttmp2 = psr;\n\tCYCLES_PLUS_1;\n\tINC_KPC_1;\n\tpsr = (psr & ~0xff) | (tmp1 & 0xff);\n\tzero = !(psr & 2);\n\tneg7 = psr;\n\tUPDATE_PSR(psr, tmp2);\n\tbreak;\n\ncase 0x29:\t\t\t/*  AND #imm */\n\tGET_IMM_MEM();\n\tAND_INST();\n\tbreak;\n\ncase 0x2a:\t\t\t/*  ROL a */\n\tINC_KPC_1;\n#ifdef ACC8\n\ttmp1 = ((acc & 0xff) << 1) + (psr & 1);\n\tSET_CARRY8(tmp1);\n\tacc = (acc & 0xff00) + (tmp1 & 0xff);\n\tSET_NEG_ZERO8(tmp1 & 0xff);\n#else\n\ttmp1 = (acc << 1) + (psr & 1);\n\tSET_CARRY16(tmp1);\n\tacc = (tmp1 & 0xffff);\n\tSET_NEG_ZERO16(acc);\n#endif\n\tbreak;\n\ncase 0x2b:\t\t\t/*  PLD */\n\tINC_KPC_1;\n\tPULL16_UNSAFE(direct);\n\tCYCLES_PLUS_1;\n\tSET_NEG_ZERO16(direct);\n\tbreak;\n\ncase 0x2c:\t\t\t/*  BIT abs */\n\tGET_ABS_RD();\n\tBIT_INST();\n\tbreak;\n\ncase 0x2d:\t\t\t/*  AND abs */\n\tGET_ABS_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x2e:\t\t\t/*  ROL abs */\n\tGET_ABS_RD_RMW();\n\tROL_INST(0);\n\tbreak;\n\ncase 0x2f:\t\t\t/*  AND long */\n\tGET_LONG_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x30:\t\t\t/*  BMI disp8 */\n\tBRANCH_DISP8(neg7 & 0x80);\n\tbreak;\n\ncase 0x31:\t\t\t/*  AND (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x32:\t\t\t/*  AND (Dloc) */\n\tGET_DLOC_IND_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x33:\t\t\t/*  AND (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x34:\t\t\t/*  BIT Dloc,x */\n\tGET_DLOC_X_RD();\n\tBIT_INST();\n\tbreak;\n\ncase 0x35:\t\t\t/*  AND Dloc,x */\n\tGET_DLOC_X_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x36:\t\t\t/*  ROL Dloc,X */\n\tGET_DLOC_X_RD_RMW();\n\tROL_INST(1);\n\tbreak;\n\ncase 0x37:\t\t\t/*  AND [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x38:\t\t\t/*  SEC */\n\tpsr = psr | 1;\n\tINC_KPC_1;\n\tbreak;\n\ncase 0x39:\t\t\t/*  AND abs,y */\n\tGET_ABS_Y_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x3a:\t\t\t/*  DEC a */\n\tINC_KPC_1;\n#ifdef ACC8\n\tacc = (acc & 0xff00) | ((acc - 1) & 0xff);\n\tSET_NEG_ZERO8(acc & 0xff);\n#else\n\tacc = (acc - 1) & 0xffff;\n\tSET_NEG_ZERO16(acc);\n#endif\n\tbreak;\n\ncase 0x3b:\t\t\t/*  TSC */\n/*  set N,Z according to 16 bit acc */\n\tINC_KPC_1;\n\tacc = stack;\n\tSET_NEG_ZERO16(acc);\n\tbreak;\n\ncase 0x3c:\t\t\t/*  BIT Abs,x */\n\tGET_ABS_X_RD();\n\tBIT_INST();\n\tbreak;\n\ncase 0x3d:\t\t\t/*  AND Abs,X */\n\tGET_ABS_X_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x3e:\t\t\t/*  ROL Abs,X */\n\tGET_ABS_X_RD_RMW();\n\tROL_INST(0);\n\tbreak;\n\ncase 0x3f:\t\t\t/*  AND Long,X */\n\tGET_LONG_X_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x40:\t\t\t/*  RTI */\n\tCYCLES_PLUS_1\n\tif(psr & 0x100) {\n\t\tPULL24(tmp1);\n\t\tkpc = (kpc & 0xff0000) + ((tmp1 >> 8) & 0xffff);\n\t\ttmp2 = psr;\n\t\tpsr = (psr & ~0xff) + (tmp1 & 0xff);\n\t\tneg7 = psr;\n\t\tzero = !(psr & 2);\n\t\tUPDATE_PSR(psr, tmp2);\n\t} else {\n\t\tPULL8(tmp1);\n\t\ttmp2 = psr;\n\t\tpsr = (tmp1 & 0xff);\n\t\tneg7 = psr;\n\t\tzero = !(psr & 2);\n\t\tPULL24(kpc);\n\t\tUPDATE_PSR(psr, tmp2);\n\t}\n\tbreak;\n\ncase 0x41:\t\t\t/*  EOR (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x42:\t\t\t/*  WDM */\n\tGET_2BYTE_ARG;\n\tINC_KPC_2;\n\tif(arg < 0x100) {\t// Next byte is 00\n\t\tINC_KPC_1;\t// Skip over the BRK\n\t}\n\tFINISH(RET_WDM, arg);\n\tbreak;\n\ncase 0x43:\t\t\t/*  EOR Disp8,S */\n\tGET_DISP8_S_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x44:\t\t\t/*  MVP */\n\tGET_2BYTE_ARG;\n\t/* arg & 0xff = dest bank, arg & 0xff00 = src bank */\n\tif(psr & 0x100) {\n\t\thalt_printf(\"MVP in emulation!\\n\");\n\t\tbreak;\n\t}\n\tdbank = arg & 0xff;\n\ttmp1 = (arg >> 8) & 0xff;\n\tCYCLES_PLUS_1;\n\tGET_MEMORY8((tmp1 << 16) + xreg, arg);\n\tSET_MEMORY8((dbank << 16) + yreg, arg);\n\tCYCLES_PLUS_2;\n\txreg = (xreg - 1) & 0xffff;\n\tyreg = (yreg - 1) & 0xffff;\n\tif(psr & 0x10) {\t// 8-bit index registers\n\t\txreg = xreg & 0xff;\n\t\tyreg = yreg & 0xff;\n\t}\n\tacc = (acc - 1) & 0xffff;\n\tif(acc == 0xffff) {\n\t\tINC_KPC_3;\n\t}\n\tbreak;\n\ncase 0x45:\t\t\t/*  EOR Dloc */\n\tGET_DLOC_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x46:\t\t\t/*  LSR Dloc */\n\tGET_DLOC_RD_RMW();\n\tLSR_INST(1);\n\tbreak;\n\ncase 0x47:\t\t\t/*  EOR [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x48:\t\t\t/*  PHA */\n\tINC_KPC_1;\n#ifdef ACC8\n\tPUSH8(acc);\n#else\n\tPUSH16(acc);\n#endif\n\tbreak;\n\ncase 0x49:\t\t\t/*  EOR #imm */\n\tGET_IMM_MEM();\n\tEOR_INST();\n\tbreak;\n\ncase 0x4a:\t\t\t/*  LSR a */\n\tINC_KPC_1;\n#ifdef ACC8\n\ttmp1 = ((acc & 0xff) >> 1);\n\tSET_CARRY8(acc << 8);\n\tacc = (acc & 0xff00) + (tmp1 & 0xff);\n\tSET_NEG_ZERO8(tmp1 & 0xff);\n#else\n\ttmp1 = (acc >> 1);\n\tSET_CARRY8((acc << 8));\n\tacc = (tmp1 & 0xffff);\n\tSET_NEG_ZERO16(acc);\n#endif\n\tbreak;\n\ncase 0x4b:\t\t\t/*  PHK */\n\tPUSH8(kpc >> 16);\n\tINC_KPC_1;\n\tbreak;\n\ncase 0x4c:\t\t\t/*  JMP abs */\n\tGET_2BYTE_ARG;\n\tCYCLES_PLUS_1;\n\tkpc = (kpc & 0xff0000) + arg;\n\tbreak;\n\ncase 0x4d:\t\t\t/*  EOR abs */\n\tGET_ABS_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x4e:\t\t\t/*  LSR abs */\n\tGET_ABS_RD_RMW();\n\tLSR_INST(0);\n\tbreak;\n\ncase 0x4f:\t\t\t/*  EOR long */\n\tGET_LONG_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x50:\t\t\t/*  BVC disp8 */\n\tBRANCH_DISP8((psr & 0x40) == 0);\n\tbreak;\n\ncase 0x51:\t\t\t/*  EOR (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x52:\t\t\t/*  EOR (Dloc) */\n\tGET_DLOC_IND_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x53:\t\t\t/*  EOR (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x54:\t\t\t/*  MVN  */\n\tGET_2BYTE_ARG;\n\t/* arg & 0xff = dest bank, arg & 0xff00 = src bank */\n\tif(psr & 0x100) {\n\t\thalt_printf(\"MVN in emulation!\\n\");\n\t\tbreak;\n\t}\n\tdbank = arg & 0xff;\n\ttmp1 = (arg >> 8) & 0xff;\n\tCYCLES_PLUS_1;\n\tGET_MEMORY8((tmp1 << 16) + xreg, arg);\n\tSET_MEMORY8((dbank << 16) + yreg, arg);\n\tCYCLES_PLUS_2;\n\txreg = (xreg + 1) & 0xffff;\n\tyreg = (yreg + 1) & 0xffff;\n\tif(psr & 0x10) {\t// 8-bit index registers\n\t\txreg = xreg & 0xff;\n\t\tyreg = yreg & 0xff;\n\t}\n\tacc = (acc - 1) & 0xffff;\n\tif(acc == 0xffff) {\n\t\tINC_KPC_3;\n\t}\n\tbreak;\n\ncase 0x55:\t\t\t/*  EOR Dloc,x */\n\tGET_DLOC_X_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x56:\t\t\t/*  LSR Dloc,X */\n\tGET_DLOC_X_RD_RMW();\n\tLSR_INST(1);\n\tbreak;\n\ncase 0x57:\t\t\t/*  EOR [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x58:\t\t\t/*  CLI */\n\tpsr = psr & (~4);\n\tINC_KPC_1;\n\tif(((psr & 0x4) == 0) && g_irq_pending) {\n\t\tFINISH(RET_IRQ, 0);\n\t}\n\tbreak;\n\ncase 0x59:\t\t\t/*  EOR abs,y */\n\tGET_ABS_Y_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x5a:\t\t\t/*  PHY */\n\tINC_KPC_1;\n\tif(psr & 0x10) {\n\t\tPUSH8(yreg);\n\t} else {\n\t\tPUSH16(yreg);\n\t}\n\tbreak;\n\ncase 0x5b:\t\t\t/*  TCD */\n\tINC_KPC_1;\n\tdirect = acc;\n\tSET_NEG_ZERO16(acc);\n\tbreak;\n\ncase 0x5c:\t\t\t/*  JMP Long */\n\tGET_3BYTE_ARG;\n\tCYCLES_PLUS_2;\n\tkpc = arg;\n\tbreak;\n\ncase 0x5d:\t\t\t/*  EOR Abs,X */\n\tGET_ABS_X_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x5e:\t\t\t/*  LSR Abs,X */\n\tGET_ABS_X_RD_RMW();\n\tLSR_INST(0);\n\tbreak;\n\ncase 0x5f:\t\t\t/*  EOR Long,X */\n\tGET_LONG_X_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x60:\t\t\t/*  RTS */\n\tCYCLES_PLUS_2\n\tPULL16(tmp1);\n\tkpc = (kpc & 0xff0000) + ((tmp1 + 1) & 0xffff);\n\tbreak;\n\ncase 0x61:\t\t\t/*  ADC (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x62:\t\t\t/*  PER */\n\tGET_2BYTE_ARG;\n\tCYCLES_PLUS_2;\n\tINC_KPC_3;\n\tPUSH16_UNSAFE(kpc + arg);\n\tbreak;\n\ncase 0x63:\t\t\t/*  ADC Disp8,S */\n\tGET_DISP8_S_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x64:\t\t\t/*  STZ Dloc */\n\tGET_DLOC_ADDR();\n\tSTZ_INST(1);\n\tbreak;\n\ncase 0x65:\t\t\t/*  ADC Dloc */\n\tGET_DLOC_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x66:\t\t\t/*  ROR Dloc */\n\tGET_DLOC_RD_RMW();\n\tROR_INST(1);\n\tbreak;\n\ncase 0x67:\t\t\t/*  ADC [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x68:\t\t\t/*  PLA */\n\tINC_KPC_1;\n\tCYCLES_PLUS_1;\n#ifdef ACC8\n\tPULL8(tmp1);\n\tacc = (acc & 0xff00) + tmp1;\n\tSET_NEG_ZERO8(tmp1);\n#else\n\tPULL16(tmp1);\n\tacc = tmp1;\n\tSET_NEG_ZERO16(tmp1);\n#endif\n\tbreak;\n\ncase 0x69:\t\t\t/*  ADC #imm */\n\tGET_IMM_MEM();\n\tADC_INST();\n\tbreak;\n\ncase 0x6a:\t\t\t/*  ROR a */\n\tINC_KPC_1;\n#ifdef ACC8\n\ttmp1 = ((acc & 0xff) >> 1) + ((psr & 1) << 7);\n\tSET_CARRY8((acc << 8));\n\tacc = (acc & 0xff00) + (tmp1 & 0xff);\n\tSET_NEG_ZERO8(tmp1 & 0xff);\n#else\n\ttmp1 = (acc >> 1) + ((psr & 1) << 15);\n\tSET_CARRY16((acc << 16));\n\tacc = (tmp1 & 0xffff);\n\tSET_NEG_ZERO16(acc);\n#endif\n\tbreak;\n\ncase 0x6b:\t\t\t/*  RTL */\n\tCYCLES_PLUS_1;\n\tPULL24_UNSAFE(tmp1);\n\tkpc = (tmp1 & 0xff0000) + ((tmp1 + 1) & 0xffff);\n\tbreak;\n\ncase 0x6c:\t\t\t/*  JMP (abs) */\n\tGET_2BYTE_ARG;\n\tCYCLES_PLUS_1;\n\tGET_MEMORY16(arg, tmp1, 1);\n\tif((psr & 0x100) && g_emul_6502_ind_page_cross_bug &&\n\t\t\t\t\t((arg & 0xff) == 0xff)) {\n\t\tGET_MEMORY8(arg - 0xff, tmp2);\n\t\ttmp1 = (tmp1 & 0xff) + (tmp2 << 8);\n\t\thalt_printf(\"Halting for emul ind jmp\\n\");\n\t}\n\tkpc = (kpc & 0xff0000) + tmp1;\n\tbreak;\n\ncase 0x6d:\t\t\t/*  ADC abs */\n\tGET_ABS_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x6e:\t\t\t/*  ROR abs */\n\tGET_ABS_RD_RMW();\n\tROR_INST(0);\n\tbreak;\n\ncase 0x6f:\t\t\t/*  ADC long */\n\tGET_LONG_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x70:\t\t\t/*  BVS disp8 */\n\tBRANCH_DISP8((psr & 0x40));\n\tbreak;\n\ncase 0x71:\t\t\t/*  ADC (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x72:\t\t\t/*  ADC (Dloc) */\n\tGET_DLOC_IND_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x73:\t\t\t/*  ADC (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x74:\t\t\t/*  STZ Dloc,x */\n\tGET_1BYTE_ARG;\n\tGET_DLOC_X_WR();\n\tSTZ_INST(1);\n\tbreak;\n\ncase 0x75:\t\t\t/*  ADC Dloc,x */\n\tGET_DLOC_X_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x76:\t\t\t/*  ROR Dloc,X */\n\tGET_DLOC_X_RD_RMW();\n\tROR_INST(1);\n\tbreak;\n\ncase 0x77:\t\t\t/*  ADC [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x78:\t\t\t/*  SEI */\n\tpsr = psr | 4;\n\tINC_KPC_1;\n\tbreak;\n\ncase 0x79:\t\t\t/*  ADC abs,y */\n\tGET_ABS_Y_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x7a:\t\t\t/*  PLY */\n\tINC_KPC_1;\n\tCYCLES_PLUS_1\n\tif(psr & 0x10) {\n\t\tPULL8(yreg);\n\t\tSET_NEG_ZERO8(yreg);\n\t} else {\n\t\tPULL16(yreg);\n\t\tSET_NEG_ZERO16(yreg);\n\t}\n\tbreak;\n\ncase 0x7b:\t\t\t/*  TDC */\n\tINC_KPC_1;\n\tacc = direct;\n\tSET_NEG_ZERO16(direct);\n\tbreak;\n\ncase 0x7c:\t\t\t/*  JMP (Abs,x) */\n/*  always access kbank, xreg cannot wrap into next bank */\n\tGET_2BYTE_ARG;\n\targ = (kpc & 0xff0000) + ((xreg + arg) & 0xffff);\n\tCYCLES_PLUS_2;\n\tGET_MEMORY16(arg, tmp1, 1);\n\tkpc = (kpc & 0xff0000) + tmp1;\n\tbreak;\n\ncase 0x7d:\t\t\t/*  ADC Abs,X */\n\tGET_ABS_X_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x7e:\t\t\t/*  ROR Abs,X */\n\tGET_ABS_X_RD_RMW();\n\tROR_INST(0);\n\tbreak;\n\ncase 0x7f:\t\t\t/*  ADC Long,X */\n\tGET_LONG_X_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x80:\t\t\t/*  BRA */\n\tBRANCH_DISP8(1);\n\tbreak;\n\ncase 0x81:\t\t\t/*  STA (Dloc,X) */\n\tGET_DLOC_X_IND_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x82:\t\t\t/*  BRL disp16 */\n\tGET_2BYTE_ARG;\n\tCYCLES_PLUS_1;\n\tkpc = (kpc & 0xff0000) + ((kpc + 3 + arg) & 0xffff);\n\tbreak;\n\ncase 0x83:\t\t\t/*  STA Disp8,S */\n\tGET_DISP8_S_ADDR();\n\tSTA_INST(1);\n\tbreak;\n\ncase 0x84:\t\t\t/*  STY Dloc */\n\tGET_DLOC_ADDR();\n\tSTY_INST(1);\n\tbreak;\n\ncase 0x85:\t\t\t/*  STA Dloc */\n\tGET_DLOC_ADDR();\n\tSTA_INST(1);\n\tbreak;\n\ncase 0x86:\t\t\t/*  STX Dloc */\n\tGET_DLOC_ADDR();\n\tSTX_INST(1);\n\tbreak;\n\ncase 0x87:\t\t\t/*  STA [Dloc] */\n\tGET_DLOC_L_IND_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x88:\t\t\t/*  DEY */\n\tINC_KPC_1;\n\tSET_INDEX_REG(yreg - 1, yreg);\n\tbreak;\n\ncase 0x89:\t\t\t/*  BIT #imm */\n\tGET_IMM_MEM();\n#ifdef ACC8\n\tzero = (acc & arg) & 0xff;\n#else\n\tzero = (acc & arg) & 0xffff;\n#endif\n\tbreak;\n\ncase 0x8a:\t\t\t/*  TXA */\n\tINC_KPC_1;\n\targ = xreg;\n\tLDA_INST();\n\tbreak;\n\ncase 0x8b:\t\t\t/*  PHB */\n\tINC_KPC_1;\n\tPUSH8(dbank);\n\tbreak;\n\ncase 0x8c:\t\t\t/*  STY abs */\n\tGET_ABS_ADDR();\n\tSTY_INST(0);\n\tbreak;\n\ncase 0x8d:\t\t\t/*  STA abs */\n\tGET_ABS_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x8e:\t\t\t/*  STX abs */\n\tGET_ABS_ADDR();\n\tSTX_INST(0);\n\tbreak;\n\ncase 0x8f:\t\t\t/*  STA long */\n\tGET_LONG_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x90:\t\t\t/*  BCC disp8 */\n\tBRANCH_DISP8((psr & 0x01) == 0);\n\tbreak;\n\ncase 0x91:\t\t\t/*  STA (Dloc),y */\n\tGET_DLOC_IND_Y_ADDR(1);\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x92:\t\t\t/*  STA (Dloc) */\n\tGET_DLOC_IND_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x93:\t\t\t/*  STA (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x94:\t\t\t/*  STY Dloc,x */\n\tGET_DLOC_X_ADDR();\n\tSTY_INST(1);\n\tbreak;\n\ncase 0x95:\t\t\t/*  STA Dloc,x */\n\tGET_DLOC_X_ADDR();\n\tSTA_INST(1);\n\tbreak;\n\ncase 0x96:\t\t\t/*  STX Dloc,Y */\n\tGET_DLOC_Y_ADDR();\n\tSTX_INST(1);\n\tbreak;\n\ncase 0x97:\t\t\t/*  STA [Dloc],Y */\n\tGET_DLOC_L_IND_Y_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x98:\t\t\t/*  TYA */\n\tINC_KPC_1;\n\targ = yreg;\n\tLDA_INST();\n\tbreak;\n\ncase 0x99:\t\t\t/*  STA abs,y */\n\tGET_ABS_INDEX_ADDR(yreg, 1)\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x9a:\t\t\t/*  TXS */\n\tstack = xreg;\n\tif(psr & 0x100) {\n\t\tstack = 0x100 | (stack & 0xff);\n\t}\n\tINC_KPC_1;\n\tbreak;\n\ncase 0x9b:\t\t\t/*  TXY */\n\tSET_INDEX_REG(xreg, yreg);\n\tINC_KPC_1;\n\tbreak;\n\ncase 0x9c:\t\t\t/*  STZ Abs */\n\tGET_ABS_ADDR();\n\tSTZ_INST(0);\n\tbreak;\n\ncase 0x9d:\t\t\t/*  STA Abs,X */\n\tGET_ABS_INDEX_ADDR(xreg, 1);\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x9e:\t\t\t/*  STZ Abs,X */\n\tGET_ABS_INDEX_ADDR(xreg, 1);\n\tSTZ_INST(0);\n\tbreak;\n\ncase 0x9f:\t\t\t/*  STA Long,X */\n\tGET_LONG_X_ADDR_FOR_WR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0xa0:\t\t\t/*  LDY #imm */\n\tINC_KPC_2;\n\tif((psr & 0x10) == 0) {\n\t\tGET_2BYTE_ARG;\n\t\tCYCLES_PLUS_1\n\t\tINC_KPC_1;\n\t} else {\n\t\tGET_1BYTE_ARG;\n\t}\n\tSET_INDEX_REG(arg, yreg);\n\tbreak;\n\ncase 0xa1:\t\t\t/*  LDA (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xa2:\t\t\t/*  LDX #imm */\n\tINC_KPC_2;\n\tif((psr & 0x10) == 0) {\n\t\tGET_2BYTE_ARG;\n\t\tCYCLES_PLUS_1\n\t\tINC_KPC_1;\n\t} else {\n\t\tGET_1BYTE_ARG;\n\t}\n\tSET_INDEX_REG(arg, xreg);\n\tbreak;\n\ncase 0xa3:\t\t\t/*  LDA Disp8,S */\n\tGET_DISP8_S_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xa4:\t\t\t/*  LDY Dloc */\n\tC_LDY_DLOC();\n\tbreak;\n\ncase 0xa5:\t\t\t/*  LDA Dloc */\n\tGET_DLOC_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xa6:\t\t\t/*  LDX Dloc */\n\tC_LDX_DLOC();\n\tbreak;\n\ncase 0xa7:\t\t\t/*  LDA [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xa8:\t\t\t/*  TAY */\n\tINC_KPC_1;\n\tSET_INDEX_REG(acc, yreg);\n\tbreak;\n\ncase 0xa9:\t\t\t/*  LDA #imm */\n\tGET_IMM_MEM();\n\tLDA_INST();\n\tbreak;\n\ncase 0xaa:\t\t\t/*  TAX */\n\tINC_KPC_1;\n\tSET_INDEX_REG(acc, xreg);\n\tbreak;\n\ncase 0xab:\t\t\t/*  PLB */\n\tINC_KPC_1;\n\tCYCLES_PLUS_1\n\tPULL8_UNSAFE(dbank);\n\tSET_NEG_ZERO8(dbank);\n\tbreak;\n\ncase 0xac:\t\t\t/*  LDY abs */\n\tC_LDY_ABS();\n\tbreak;\n\ncase 0xad:\t\t\t/*  LDA abs */\n\tGET_ABS_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xae:\t\t\t/*  LDX abs */\n\tC_LDX_ABS();\n\tbreak;\n\ncase 0xaf:\t\t\t/*  LDA long */\n\tGET_LONG_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xb0:\t\t\t/*  BCS disp8 */\n\tBRANCH_DISP8((psr & 0x01));\n\tbreak;\n\ncase 0xb1:\t\t\t/*  LDA (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xb2:\t\t\t/*  LDA (Dloc) */\n\tGET_DLOC_IND_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xb3:\t\t\t/*  LDA (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xb4:\t\t\t/*  LDY Dloc,x */\n\tC_LDY_DLOC_X();\n\tbreak;\n\ncase 0xb5:\t\t\t/*  LDA Dloc,x */\n\tGET_DLOC_X_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xb6:\t\t\t/*  LDX Dloc,y */\n\tC_LDX_DLOC_Y();\n\tbreak;\n\ncase 0xb7:\t\t\t/*  LDA [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xb8:\t\t\t/*  CLV */\n\tpsr = psr & ~0x40;\n\tINC_KPC_1;\n\tbreak;\n\ncase 0xb9:\t\t\t/*  LDA abs,y */\n\tGET_ABS_Y_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xba:\t\t\t/*  TSX */\n\tINC_KPC_1;\n\tSET_INDEX_REG(stack, xreg);\n\tbreak;\n\ncase 0xbb:\t\t\t/*  TYX */\n\tINC_KPC_1;\n\tSET_INDEX_REG(yreg, xreg);\n\tbreak;\n\ncase 0xbc:\t\t\t/*  LDY Abs,X */\n\tC_LDY_ABS_X();\n\tbreak;\n\ncase 0xbd:\t\t\t/*  LDA Abs,X */\n\tGET_ABS_X_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xbe:\t\t\t/*  LDX Abs,y */\n\tC_LDX_ABS_Y();\n\tbreak;\n\ncase 0xbf:\t\t\t/*  LDA Long,X */\n\tGET_LONG_X_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xc0:\t\t\t/*  CPY #imm */\n\tC_CPY_IMM();\n\tbreak;\n\ncase 0xc1:\t\t\t/*  CMP (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xc2:\t\t\t/*  REP #imm */\n\tGET_1BYTE_ARG;\n\ttmp2 = psr;\n\tCYCLES_PLUS_1;\n\tINC_KPC_2;\n\tpsr = (psr & ~0x82) | (neg7 & 0x80) | ((!zero) << 1);\n\tpsr = psr & ~(arg & 0xff);\n\tzero = !(psr & 2);\n\tneg7 = psr;\n\tUPDATE_PSR(psr, tmp2);\n\tbreak;\n\ncase 0xc3:\t\t\t/*  CMP Disp8,S */\n\tGET_DISP8_S_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xc4:\t\t\t/*  CPY Dloc */\n\tC_CPY_DLOC();\n\tbreak;\n\ncase 0xc5:\t\t\t/*  CMP Dloc */\n\tGET_DLOC_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xc6:\t\t\t/*  DEC Dloc */\n\tGET_DLOC_RD_RMW();\n\tDEC_INST(1);\n\tbreak;\n\ncase 0xc7:\t\t\t/*  CMP [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xc8:\t\t\t/*  INY */\n\tINC_KPC_1;\n\tSET_INDEX_REG(yreg + 1, yreg);\n\tbreak;\n\ncase 0xc9:\t\t\t/*  CMP #imm */\n\tGET_IMM_MEM();\n\tCMP_INST();\n\tbreak;\n\ncase 0xca:\t\t\t/*  DEX */\n\tINC_KPC_1;\n\tSET_INDEX_REG(xreg - 1, xreg);\n\tbreak;\n\ncase 0xcb:\t\t\t/*  WAI */\n\tif(g_irq_pending) {\n\t\tg_wait_pending = 0;\n\t\tINC_KPC_1;\n\t} else {\n\t\tg_wait_pending = 1;\n\t}\n\tbreak;\n\ncase 0xcc:\t\t\t/*  CPY abs */\n\tC_CPY_ABS();\n\tbreak;\n\ncase 0xcd:\t\t\t/*  CMP abs */\n\tGET_ABS_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xce:\t\t\t/*  DEC abs */\n\tGET_ABS_RD_RMW();\n\tDEC_INST(0);\n\tbreak;\n\ncase 0xcf:\t\t\t/*  CMP long */\n\tGET_LONG_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xd0:\t\t\t/*  BNE disp8 */\n\tBRANCH_DISP8(zero != 0);\n\tbreak;\n\ncase 0xd1:\t\t\t/*  CMP (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xd2:\t\t\t/*  CMP (Dloc) */\n\tGET_DLOC_IND_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xd3:\t\t\t/*  CMP (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xd4:\t\t\t/*  PEI Dloc */\n\tGET_DLOC_ADDR()\n\tGET_MEMORY16(arg, arg, 1);\n\tCYCLES_PLUS_1;\n\tPUSH16_UNSAFE(arg);\n\tbreak;\n\ncase 0xd5:\t\t\t/*  CMP Dloc,x */\n\tGET_DLOC_X_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xd6:\t\t\t/*  DEC Dloc,x */\n\tGET_DLOC_X_RD_RMW();\n\tDEC_INST(1);\n\tbreak;\n\ncase 0xd7:\t\t\t/*  CMP [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xd8:\t\t\t/*  CLD */\n\tpsr = psr & (~0x8);\n\tINC_KPC_1;\n\tbreak;\n\ncase 0xd9:\t\t\t/*  CMP abs,y */\n\tGET_ABS_Y_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xda:\t\t\t/*  PHX */\n\tINC_KPC_1;\n\tif(psr & 0x10) {\n\t\tPUSH8(xreg);\n\t} else {\n\t\tPUSH16(xreg);\n\t}\n\tbreak;\n\ncase 0xdb:\t\t\t/*  STP */\n\tFINISH(RET_STP, 0);\n\tbreak;\n\ncase 0xdc:\t\t\t/*  JML (Abs) */\n\tGET_2BYTE_ARG;\n\tCYCLES_PLUS_1;\n\tGET_MEMORY24(arg, kpc, 1);\n\tbreak;\n\ncase 0xdd:\t\t\t/*  CMP Abs,X */\n\tGET_ABS_X_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xde:\t\t\t/*  DEC Abs,X */\n\tGET_ABS_X_RD_RMW();\n\tDEC_INST(0);\n\tbreak;\n\ncase 0xdf:\t\t\t/*  CMP Long,X */\n\tGET_LONG_X_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xe0:\t\t\t/*  CPX #imm */\n\tC_CPX_IMM();\n\tbreak;\n\ncase 0xe1:\t\t\t/*  SBC (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xe2:\t\t\t/*  SEP #imm */\n\tGET_1BYTE_ARG;\n\ttmp2 = psr;\n\tCYCLES_PLUS_1;\n\tINC_KPC_2;\n\tpsr = (psr & ~0x82) | (neg7 & 0x80) | ((!zero) << 1);\n\tpsr = psr | (arg & 0xff);\n\tzero = !(psr & 2);\n\tneg7 = psr;\n\tUPDATE_PSR(psr, tmp2);\n\tbreak;\n\ncase 0xe3:\t\t\t/*  SBC Disp8,S */\n\tGET_DISP8_S_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xe4:\t\t\t/*  CPX Dloc */\n\tC_CPX_DLOC();\n\tbreak;\n\ncase 0xe5:\t\t\t/*  SBC Dloc */\n\tGET_DLOC_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xe6:\t\t\t/*  INC Dloc */\n\tGET_DLOC_RD_RMW();\n\tINC_INST(1);\n\tbreak;\n\ncase 0xe7:\t\t\t/*  SBC [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xe8:\t\t\t/*  INX */\n\tINC_KPC_1;\n\tSET_INDEX_REG(xreg + 1, xreg);\n\tbreak;\n\ncase 0xe9:\t\t\t/*  SBC #imm */\n\tGET_IMM_MEM();\n\tSBC_INST();\n\tbreak;\n\ncase 0xea:\t\t\t/*  NOP */\n\tINC_KPC_1;\n\tbreak;\n\ncase 0xeb:\t\t\t/*  XBA */\n\ttmp1 = acc & 0xff;\n\tCYCLES_PLUS_1\n\tacc = (tmp1 << 8) + (acc >> 8);\n\tINC_KPC_1;\n\tSET_NEG_ZERO8(acc & 0xff);\n\tbreak;\n\ncase 0xec:\t\t\t/*  CPX abs */\n\tC_CPX_ABS();\n\tbreak;\n\ncase 0xed:\t\t\t/*  SBC abs */\n\tGET_ABS_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xee:\t\t\t/*  INC abs */\n\tGET_ABS_RD_RMW();\n\tINC_INST(0);\n\tbreak;\n\ncase 0xef:\t\t\t/*  SBC long */\n\tGET_LONG_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xf0:\t\t\t/*  BEQ disp8 */\n\tBRANCH_DISP8(zero == 0);\n\tbreak;\n\ncase 0xf1:\t\t\t/*  SBC (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xf2:\t\t\t/*  SBC (Dloc) */\n\tGET_DLOC_IND_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xf3:\t\t\t/*  SBC (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xf4:\t\t\t/*  PEA Abs */\n\tGET_2BYTE_ARG;\n\tCYCLES_PLUS_1;\n\tINC_KPC_3;\n\tPUSH16_UNSAFE(arg);\n\tbreak;\n\ncase 0xf5:\t\t\t/*  SBC Dloc,x */\n\tGET_DLOC_X_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xf6:\t\t\t/*  INC Dloc,x */\n\tGET_DLOC_X_RD_RMW();\n\tINC_INST(1);\n\tbreak;\n\ncase 0xf7:\t\t\t/*  SBC [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xf8:\t\t\t/*  SED */\n\tINC_KPC_1;\n\tpsr |= 0x8;\n\tbreak;\n\ncase 0xf9:\t\t\t/*  SBC abs,y */\n\tGET_ABS_Y_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xfa:\t\t\t/*  PLX */\n\tINC_KPC_1;\n\tCYCLES_PLUS_1;\n\tif(psr & 0x10) {\n\t\tPULL8(xreg);\n\t\tSET_NEG_ZERO8(xreg);\n\t} else {\n\t\tPULL16(xreg);\n\t\tSET_NEG_ZERO16(xreg);\n\t}\n\tbreak;\n\ncase 0xfb:\t\t\t/*  XCE */\n\ttmp2 = psr;\n\tINC_KPC_1;\n\tpsr = (tmp2 & 0xfe) | ((tmp2 & 1) << 8) | ((tmp2 >> 8) & 1);\n\tUPDATE_PSR(psr, tmp2);\n\tbreak;\n\ncase 0xfc:\t\t\t/*  JSR (Abs,X) */\n\tGET_2BYTE_ARG;\n\tINC_KPC_2;\n\ttmp1 = kpc;\n\targ = (kpc & 0xff0000) + ((arg + xreg) & 0xffff);\n\tGET_MEMORY16(arg, tmp2, 1);\n\tkpc = (kpc & 0xff0000) + tmp2;\n\tCYCLES_PLUS_2\n\tPUSH16_UNSAFE(tmp1);\n\tbreak;\n\ncase 0xfd:\t\t\t/*  SBC Abs,X */\n\tGET_ABS_X_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xfe:\t\t\t/*  INC Abs,X */\n\tGET_ABS_X_RD_RMW();\n\tINC_INST(0);\n\tbreak;\n\ncase 0xff:\t\t\t/*  SBC Long,X */\n\tGET_LONG_X_RD();\n\tSBC_INST();\n\tbreak;\n\n"
  },
  {
    "path": "gsplus/src/iwm.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// Information from Beneath Apple DOS, Apple IIgs HW reference, Apple IIgs\n//  Firmware reference, and Inside Macintosh Vol 3 (for status35 info).\n// Neil Parker wrote about the Apple 3.5 drive using IWM at\n//  http://nparker.llx.com/a2/disk and it is very useful.\n//  http://nparker.llx.com/a2/sethook lists hooks for IWM routines, from\n//   $e1/0c00 - 0fab.\n// ff/5fa4 is STAT35.  ff/5fae is CONT35\n// When testing DOS3.3, set bp at $b942, which is bad sector detected\n// IWM mode register: 7:reserved; 6:5: 0; 4: 1=8MHz,0=7MHz (should always be 0);\n//\t3: bit-cells are 2usec, 2: 1sec timer off; 1: async handshake (writes)\n//\t0: latch mode enabled for reads (holds data for 8 bit times)\n// dsk->fd >= 0 indicates a valid disk.  dsk->raw_data != 0 indicates this is\n//  a compressed disk mounted read-only, and ignore dsk->fd (which will always\n//  be 0, to indicate a valid disk).\n// fbit_pos encodes head position on track in increments of 1/64 usecs for 5.25\"\n//  { byte_offset, bit[2:0], sub_bit[8:0] }, so fbit_pos >> 12 gives byte offset\n// fbit_mult turns dfcyc into fbit_pos, where (512/fbit_mult) = the bit cell\n//  5.25\" bit cell of 4usec has fbit_mult=128; cell of 3.75usec has mult=136.\n// For 3.5\", fbit_mult=256 indicates a 2usec bit cell.\n// https://support.apple.com/kb/TA39910?locale=en_US&viewlocale=en_US gives\n//  the RPMs of 800KB disks\n\n#include \"defc.h\"\n\nint g_halt_arm_move = 0;\n\nextern int Verbose;\nextern word32 g_vbl_count;\nextern word32 g_c036_val_speed;\nextern int g_config_kegs_update_needed;\nextern Engine_reg engine;\n\nconst byte phys_to_dos_sec[] = {\n\t0x00, 0x07, 0x0e, 0x06,  0x0d, 0x05, 0x0c, 0x04,\n\t0x0b, 0x03, 0x0a, 0x02,  0x09, 0x01, 0x08, 0x0f\n};\n\nconst byte phys_to_prodos_sec[] = {\n\t0x00, 0x08, 0x01, 0x09,  0x02, 0x0a, 0x03, 0x0b,\n\t0x04, 0x0c, 0x05, 0x0d,  0x06, 0x0e, 0x07, 0x0f\n};\n\n\nconst byte to_disk_byte[] = {\n\t0x96, 0x97, 0x9a, 0x9b,  0x9d, 0x9e, 0x9f, 0xa6,\n\t0xa7, 0xab, 0xac, 0xad,  0xae, 0xaf, 0xb2, 0xb3,\n/* 0x10 */\n\t0xb4, 0xb5, 0xb6, 0xb7,  0xb9, 0xba, 0xbb, 0xbc,\n\t0xbd, 0xbe, 0xbf, 0xcb,  0xcd, 0xce, 0xcf, 0xd3,\n/* 0x20 */\n\t0xd6, 0xd7, 0xd9, 0xda,  0xdb, 0xdc, 0xdd, 0xde,\n\t0xdf, 0xe5, 0xe6, 0xe7,  0xe9, 0xea, 0xeb, 0xec,\n/* 0x30 */\n\t0xed, 0xee, 0xef, 0xf2,  0xf3, 0xf4, 0xf5, 0xf6,\n\t0xf7, 0xf9, 0xfa, 0xfb,  0xfc, 0xfd, 0xfe, 0xff\n};\n\nint\tg_track_bytes_35[] = {\n\t0x200*12,\n\t0x200*11,\n\t0x200*10,\n\t0x200*9,\n\t0x200*8\n};\n\nint\tg_track_bits_35[] = {\n\t// Do 1.001 * (1020484 * (60/rpm)) / 2 usec = bits per track\n\t77779,\t\t\t// Trks 0-15: 394 rpm\n\t71433,\t\t\t// Trks 16-31: 429 rpm\n\t64926,\t\t\t// Trks 32-47: 472 rpm\n\t58371,\t\t\t// Trks 48-63: 525 rpm\n\t51940\t\t\t// Trks 64-79: 590 rpm\n};\n\nint\tg_fast_disk_emul_en = 1;\nint\tg_fast_disk_emul = 0;\nint\tg_slow_525_emul_wr = 0;\ndword64\tg_dfcyc_end_emul_wr = 0;\nint\tg_fast_disk_unnib = 0;\nint\tg_iwm_fake_fast = 0;\n\nword32\tg_from_disk_byte[256];\nint\tg_from_disk_byte_valid = 0;\n\nIwm\tg_iwm;\n\nint\tg_iwm_motor_on = 0;\n// g_iwm_motor_on is set when drive turned on, 0 when $c0e8 is touched.\n//  Use this to throttle speed to 1MHz.\n// g_iwm.motor_on=1 means drive is spinning.  g_iwm.motor_off=1 means we're\n//  in the 1-second countdown of g_iwm.motor_off_vbl_count.\n\nint\tg_check_nibblization = 1;\n\nvoid\niwm_init_drive(Disk *dsk, int smartport, int drive, int disk_525)\n{\n\tint\tnum_tracks;\n\tint\ti;\n\n\tdsk->dfcyc_last_read = 0;\n\tdsk->raw_data = 0;\n\tdsk->wozinfo_ptr = 0;\n\tdsk->dynapro_info_ptr = 0;\n\tdsk->name_ptr = 0;\n\tdsk->partition_name = 0;\n\tdsk->partition_num = -1;\n\tdsk->fd = -1;\n\tdsk->dynapro_blocks = 0;\n\tdsk->raw_dsize = 0;\n\tdsk->dimage_start = 0;\n\tdsk->dimage_size = 0;\n\tdsk->smartport = smartport;\n\tdsk->disk_525 = disk_525;\n\tdsk->drive = drive;\n\tdsk->cur_frac_track = 0;\n\tdsk->image_type = 0;\n\tdsk->vol_num = 254;\n\tdsk->write_prot = 1;\n\tdsk->write_through_to_unix = 0;\n\tdsk->disk_dirty = 0;\n\tdsk->just_ejected = 0;\n\tdsk->last_phases = 0;\n\tdsk->cur_fbit_pos = 0;\n\tdsk->fbit_mult = 128;\t\t\t// 128 for 5.25\", 256 for 3.5\"\n\tdsk->cur_track_bits = 0;\n\tdsk->raw_bptr_malloc = 0;\n\tdsk->cur_trk_ptr = 0;\n\tdsk->num_tracks = 0;\n\tdsk->trks = 0;\n\tif(!smartport) {\n\t\t// 3.5\" and 5.25\" drives.  This is only called at init time,\n\t\t//  so one-time malloc can be done now\n\t\tnum_tracks = 2*80;\t\t// 3.5\" needs 160\n\t\tdsk->trks = (Trk *)malloc(num_tracks * sizeof(Trk));\n\n\t\tfor(i = 0; i < num_tracks; i++) {\n\t\t\tdsk->trks[i].raw_bptr = 0;\n\t\t\tdsk->trks[i].sync_ptr = 0;\n\t\t\tdsk->trks[i].dunix_pos = 0;\n\t\t\tdsk->trks[i].unix_len = 0;\n\t\t\tdsk->trks[i].dirty = 0;\n\t\t\tdsk->trks[i].track_bits = 0;\n\t\t}\n\t\t// num_tracks != 0 indicates a valid disk, do not set it here\n\t}\n}\n\nvoid\niwm_init()\n{\n\tword32\tval;\n\tint\ti;\n\n\tmemset(&g_iwm, 0, sizeof(g_iwm));\n\n\tfor(i = 0; i < 2; i++) {\n\t\tiwm_init_drive(&(g_iwm.drive525[i]), 0, i, 1);\n\t\tiwm_init_drive(&(g_iwm.drive35[i]), 0, i, 0);\n\t}\n\n\tfor(i = 0; i < MAX_C7_DISKS; i++) {\n\t\tiwm_init_drive(&(g_iwm.smartport[i]), 1, i, 0);\n\t}\n\n\tif(g_from_disk_byte_valid == 0) {\n\t\tfor(i = 0; i < 256; i++) {\n\t\t\tg_from_disk_byte[i] = 0x100 + i;\n\t\t}\n\t\tfor(i = 0; i < 64; i++) {\n\t\t\tval = to_disk_byte[i];\n\t\t\tg_from_disk_byte[val] = i;\n\t\t}\n\t\tg_from_disk_byte_valid = 1;\n\t} else {\n\t\thalt_printf(\"iwm_init called twice!\\n\");\n\t}\n}\n\nvoid\niwm_reset()\n{\n\tint\ti;\n\n\tg_iwm.state = 0;\n\tg_iwm.motor_off_vbl_count = 0;\n\tg_iwm.forced_sync_bit = 32*12345;\n\tg_iwm.last_rd_bit = 32*12345;\n\tg_iwm.write_val = 0;\n\tfor(i = 0; i < 5; i++) {\n\t\tg_iwm.wr_last_bit[i] = 0;\n\t\tg_iwm.wr_qtr_track[i] = 0;\n\t\tg_iwm.wr_num_bits[i] = 0;\n\t\tg_iwm.wr_prior_num_bits[i] = 0;\n\t\tg_iwm.wr_delta[i] = 0;\n\t}\n\tg_iwm.num_active_writes = 0;\n\n\tg_iwm_motor_on = 0;\n}\n\nvoid\ndisk_set_num_tracks(Disk *dsk, int num_tracks)\n{\n\tif(num_tracks <= 2*80) {\n\t\tdsk->num_tracks = num_tracks;\n\t} else {\n\t\thalt_printf(\"num_tracks out of range: %d\\n\", num_tracks);\n\t}\n}\n\nword32\niwm_get_default_track_bits(Disk *dsk, word32 qtr_trk)\n{\n\tword32\ttrack_bits, extra;\n\n\textra = (qtr_trk + (qtr_trk >> 5)) & 0xf;\n\t\t// up to 15 extra bits\n\tif(dsk->disk_525) {\n\t\ttrack_bits = NIB_LEN_525 * 8;\t\t// 0x18f2 = 51088\n\t} else {\n\t\ttrack_bits = g_track_bits_35[qtr_trk >> 5];\n\t}\n\treturn track_bits + extra;\n}\n\nvoid\ndraw_iwm_status(int line, char *buf)\n{\n\tchar\t*flag[2][2];\n\tint\tapple35_sel, drive_select;\n\n\tflag[0][0] = \" \";\n\tflag[0][1] = \" \";\n\tflag[1][0] = \" \";\n\tflag[1][1] = \" \";\n\n\tapple35_sel = (g_iwm.state >> IWM_BIT_C031_APPLE35SEL) & 1;\n\tdrive_select = (g_iwm.state >> IWM_BIT_DRIVE_SEL) & 1;\n\tif(g_iwm.state & IWM_STATE_MOTOR_ON) {\n\t\tflag[apple35_sel][drive_select] = \"*\";\n\t}\n\n\tsprintf(buf, \"s6d1:%2d%s   s6d2:%2d%s   s5d1:%2d/%d%s   \"\n\t\t\"s5d2:%2d/%d%s fast_disk_emul:%d,%d c036:%02x\",\n\t\tg_iwm.drive525[0].cur_frac_track >> 18, flag[0][0],\n\t\tg_iwm.drive525[1].cur_frac_track >> 18, flag[0][1],\n\t\tg_iwm.drive35[0].cur_frac_track >> 17,\n\t\t(g_iwm.drive35[0].cur_frac_track >> 16) & 1, flag[1][0],\n\t\tg_iwm.drive35[1].cur_frac_track >> 17,\n\t\t(g_iwm.drive35[1].cur_frac_track >> 16) & 1, flag[1][1],\n\t\tg_fast_disk_emul, g_slow_525_emul_wr, g_c036_val_speed);\n\n\tvideo_update_status_line(line, buf);\n}\n\nvoid\niwm_flush_cur_disk()\n{\n\tDisk\t*dsk;\n\n\tdsk = iwm_get_dsk(g_iwm.state);\n\tiwm_flush_disk_to_unix(dsk);\n}\n\nvoid\niwm_flush_disk_to_unix(Disk *dsk)\n{\n\tbyte\tbuffer[0x4000];\n\tint\tret, did_write;\n\tint\ti;\n\n\tif((dsk->disk_dirty == 0) || (dsk->write_through_to_unix == 0)) {\n\t\treturn;\n\t}\n\tif((dsk->raw_data != 0) && !dsk->dynapro_info_ptr) {\n\t\treturn;\n\t}\n\n\tprintf(\"Writing disk %s to Unix\\n\", dsk->name_ptr);\n\n\tfor(i = 0; i < 160; i++) {\n\t\tret = iwm_track_to_unix(dsk, i, &(buffer[0]));\n\n\t\tif((ret != 1) && (ret != 0)) {\n\t\t\tprintf(\"iwm_flush_disk_to_unix ret: %d, cannot write \"\n\t\t\t\t\"image to unix, qtrk:%04x\\n\", ret, i);\n\t\t\thalt_printf(\"Converting to WOZ format!\\n\");\n\t\t\twoz_reparse_woz(dsk);\n\t\t\treturn;\t\t// Don't clear dsk->disk_dirty\n\t\t}\n\t\tif(ret == 1) {\n\t\t\tdid_write = 1;\n\t\t}\n\t}\n\tif(dsk->wozinfo_ptr && did_write) {\n\t\twoz_check_file(dsk);\n\t}\n\n\tdsk->disk_dirty = 0;\n}\n\n/* Check for dirty disk 3 times a second */\n\nword32\tg_iwm_dynapro_last_vbl_count = 0;\n\nvoid\niwm_vbl_update()\n{\n\tword32\tstate;\n\tint\ti;\n\n\tstate = g_iwm.state;\n\tif((state & IWM_STATE_MOTOR_ON) && (state & IWM_STATE_MOTOR_OFF)) {\n\t\tif(g_iwm.motor_off_vbl_count <= g_vbl_count) {\n\t\t\tprintf(\"Disk timer expired, drive off: %08x\\n\",\n\t\t\t\tg_vbl_count);\n\t\t\tiwm_flush_cur_disk();\n\t\t\tg_iwm.state = state &\n\t\t\t\t(~(IWM_STATE_MOTOR_ON | IWM_STATE_MOTOR_OFF));\n\t\t\tg_iwm.drive525[0].dfcyc_last_phases = 0;\n\t\t\tg_iwm.drive525[1].dfcyc_last_phases = 0;\n\t\t}\n\t}\n\tif((g_vbl_count - g_iwm_dynapro_last_vbl_count) >= 60) {\n\t\tfor(i = 0; i < 2; i++) {\n\t\t\tdynapro_try_fix_damaged_disk(&(g_iwm.drive525[i]));\n\t\t\tdynapro_try_fix_damaged_disk(&(g_iwm.drive35[i]));\n\t\t}\n\t\tfor(i = 0; i < MAX_C7_DISKS; i++) {\n\t\t\tdynapro_try_fix_damaged_disk(&(g_iwm.smartport[i]));\n\t\t}\n\t\tg_iwm_dynapro_last_vbl_count = g_vbl_count;\n\t}\n}\n\nvoid\niwm_update_fast_disk_emul(int fast_disk_emul_en)\n{\n\tif(g_iwm_motor_on == 0) {\n\t\tg_fast_disk_emul = fast_disk_emul_en;\n\t}\n}\n\nvoid\niwm_show_stats(int slot_drive)\n{\n\tTrk\t*trkptr;\n\tDisk\t*dsk;\n\tint\tslot, drive;\n\tint\ti;\n\n\tdbg_printf(\"IWM state: %07x (slot_drive:%d)\\n\", g_iwm.state,\n\t\t\t\t\t\t\t\tslot_drive);\n\tdbg_printf(\"g_iwm.drive525[0].fd: %d, [1].fd: %d\\n\",\n\t\tg_iwm.drive525[0].fd, g_iwm.drive525[1].fd);\n\tdbg_printf(\"g_iwm.drive525[0].last_phases: %d, [1].last_phases: %d\\n\",\n\t\tg_iwm.drive525[0].last_phases, g_iwm.drive525[1].last_phases);\n\tdbg_printf(\"g_iwm.write_val:%02x, wr_num_bits[0]:%d, last_rd_bit:\"\n\t\t\"%06x, forced_sync_bit:%06x\\n\", g_iwm.write_val,\n\t\tg_iwm.wr_num_bits[0], g_iwm.last_rd_bit,\n\t\tg_iwm.forced_sync_bit);\n\tif(slot_drive < 0) {\n\t\treturn;\n\t}\n\tdrive = slot_drive & 1;\n\tslot = 5 + ((slot_drive >> 1) & 1);\n\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\tif(dsk->trks == 0) {\n\t\treturn;\n\t}\n\tfor(i = 0; i < 160; i++) {\n\t\ttrkptr = &(dsk->trks[i]);\n\t\tprintf(\"Qtrk:%02x: bits:%05x dunix_pos:%08llx unix_len:%06x, \"\n\t\t\t\"d:%d %p,%p\\n\", i, trkptr->track_bits,\n\t\t\ttrkptr->dunix_pos, trkptr->unix_len, trkptr->dirty,\n\t\t\ttrkptr->raw_bptr, trkptr->sync_ptr);\n\t}\n}\n\nDisk *\niwm_get_dsk(word32 state)\n{\n\tDisk\t*dsk;\n\tint\tdrive;\n\n\tdrive = (state >> IWM_BIT_DRIVE_SEL) & 1;\n\tif(state & IWM_STATE_C031_APPLE35SEL) {\n\t\tdsk = &(g_iwm.drive35[drive]);\n\t} else {\n\t\tdsk = &(g_iwm.drive525[drive]);\n\t}\n\n\treturn dsk;\n}\n\nDisk *\niwm_touch_switches(int loc, dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tdword64\tdadd, dcycs_passed;\n\tword32\ttrack_bits, fbit_pos, forced_sync_bit, mask, mask_on, state;\n\tint\tphase, on, motor_on;\n\n\tif(g_iwm.state & IWM_STATE_RESET) {\n\t\tiwm_printf(\"IWM under reset: %06x\\n\", g_iwm.state);\n\t}\n\n\tdsk = iwm_get_dsk(g_iwm.state);\n\n\ttrack_bits = dsk->cur_track_bits;\n\tfbit_pos = track_bits * 4096;\t\t// Set to an illegal value\n\tmotor_on = g_iwm.state & IWM_STATE_MOTOR_ON;\n\tif(motor_on) {\n\t\tdcycs_passed = (dfcyc - dsk->dfcyc_last_read) >> 16;\n\t\tdsk->dfcyc_last_read = dfcyc;\n\n\t\t// track_bits can be 0, indicating no valid data\n\t\tdadd = dcycs_passed * dsk->fbit_mult;\n\t\tif(track_bits) {\n\t\t\tif(dadd >= (track_bits * 512)) {\n\t\t\t\tdadd = dadd % (track_bits * 512);\n\t\t\t}\n\t\t}\n\t\tfbit_pos = dsk->cur_fbit_pos + (word32)dadd;\n\t\tif(fbit_pos >= (track_bits * 512)) {\n\t\t\tfbit_pos -= (track_bits * 512);\n\t\t}\n\t\tif(g_slow_525_emul_wr || (g_iwm.state & IWM_STATE_Q7) ||\n\t\t\t\t\t!g_fast_disk_emul || !track_bits) {\n\t\t\tdsk->cur_fbit_pos = fbit_pos;\n\t\t} else {\n\t\t\tfbit_pos = track_bits << 12;\t// out of range value\n\t\t}\n#if 0\n\t\tprintf(\"dsk->cur_fbit:%08x, fbit_pos:%08x\\n\",\n\t\t\t\t\tdsk->cur_fbit_pos, fbit_pos);\n#endif\n\t}\n\n\tdbg_log_info(dfcyc, dsk->cur_fbit_pos,\n\t\t((loc & 0xff) << 24) | g_iwm.state,\n\t\t((dsk->cur_frac_track + 0x8000) & 0x1ff0000) | 0xe0);\n\n\tif(loc < 0) {\n\t\t// Not a real access, just a way to update fbit_pos\n\t\treturn dsk;\n\t}\n\n\tif(dsk->dfcyc_last_phases) {\n\t\tiwm525_update_head(dsk, dfcyc);\n\t}\n\n\ton = loc & 1;\n\tphase = loc >> 1;\n\tstate = g_iwm.state;\n\tif(loc < 8) {\n\t\t/* phase adjustments.  See if motor is on */\n\n\t\tmask = (1 << (phase & 3)) << IWM_BIT_PHASES;\n\t\tmask_on = (on << (phase & 3)) << IWM_BIT_PHASES;\n\t\tstate = (state & (~mask)) | mask_on;\n\t\tg_iwm.state = state;\n\t\tiwm_printf(\"Iwm phase %d=%d, all phases: %06x (%016llx)\\n\",\n\t\t\tphase, on, state, dfcyc);\n\n\t\tif(state & IWM_STATE_MOTOR_ON) {\n\t\t\tif(state & IWM_STATE_C031_APPLE35SEL) {\n\t\t\t\tif((phase == 3) && on) {\n\t\t\t\t\tiwm_do_action35(dfcyc);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Move apple525 head\n\t\t\t\tiwm525_update_phases(dsk, dfcyc);\n\t\t\t}\n\t\t}\n\t\tstate = g_iwm.state;\n\t\t/* See if enable or reset is asserted */\n\t\tif(((state >> IWM_BIT_PHASES) & 5) == 5) {\t// Ph 0, 2 set\n\t\t\tstate |= IWM_STATE_RESET;\n\t\t\tiwm_printf(\"IWM reset active\\n\");\n\t\t} else {\n\t\t\tstate &= (~IWM_STATE_RESET);\n\t\t}\n\t\tif(((state >> IWM_BIT_PHASES) & 0xa) == 0xa) {\t// Ph 1, 3 set\n\t\t\tstate |= IWM_STATE_ENABLE2;\n\t\t\tiwm_printf(\"IWM ENABLE2 active\\n\");\n\t\t} else {\n\t\t\tstate &= (~IWM_STATE_ENABLE2);\n\t\t}\n\t\tg_iwm.state = state;\n\t} else {\n\t\t/* loc >= 8 */\n\t\tswitch(loc) {\n\t\tcase 0x8:\n\t\t\tiwm_printf(\"Turning IWM motor off!\\n\");\n\t\t\tif(g_iwm.state & 0x04) {\t\t// iwm MODE\n\t\t\t\t/* Turn off immediately */\n\t\t\t\tstate &= (~(IWM_STATE_MOTOR_ON |\n\t\t\t\t\t\t\tIWM_STATE_MOTOR_OFF));\n\t\t\t} else {\n\t\t\t\t/* 1 second delay */\n\t\t\t\tif((state & IWM_STATE_MOTOR_ON) &&\n\t\t\t\t\t\t!(state & IWM_STATE_MOTOR_OFF)){\n\t\t\t\t\tstate |= IWM_STATE_MOTOR_OFF;\n\t\t\t\t\tg_iwm.motor_off_vbl_count = g_vbl_count\n\t\t\t\t\t\t\t\t\t+ 60;\n\t\t\t\t}\n\t\t\t}\n\t\t\tg_iwm.state = state;\n\n#if 0\n\t\t\tprintf(\"IWM motor off, g_iwm_motor_on:%d, slow:%d\\n\",\n\t\t\t\tg_iwm_motor_on, g_slow_525_emul_wr);\n#endif\n\t\t\tif(g_iwm_motor_on || g_slow_525_emul_wr) {\n\t\t\t\t/* recalc current speed */\n\t\t\t\tg_fast_disk_emul = g_fast_disk_emul_en;\n\t\t\t\tengine_recalc_events();\n\t\t\t\tiwm_flush_cur_disk();\n\t\t\t}\n\n\t\t\tg_iwm_motor_on = 0;\n\t\t\tg_slow_525_emul_wr = 0;\n\t\t\tbreak;\n\t\tcase 0x9:\n\t\t\tiwm_printf(\"Turning IWM motor on!\\n\");\n\t\t\tstate |= IWM_STATE_MOTOR_ON;\n\t\t\tstate &= (~IWM_STATE_MOTOR_OFF);\n\t\t\tstate &= (~IWM_STATE_LAST_SEL35);\n\t\t\tstate |= ((state >> 6) & 1) << IWM_BIT_LAST_SEL35;\n\t\t\tg_iwm.state = state;\n\t\t\tdsk->dfcyc_last_read = dfcyc;\n\n\t\t\tif(g_iwm_motor_on == 0) {\n\t\t\t\t/* recalc current speed */\n\t\t\t\tif(dsk->wozinfo_ptr) {\n\t\t\t\t\tg_fast_disk_emul = 0;\n\t\t\t\t}\n\t\t\t\tengine_recalc_events();\n\t\t\t}\n\t\t\tg_iwm_motor_on = 1;\n\n\t\t\tbreak;\n\t\tcase 0xa:\n\t\tcase 0xb:\n\t\t\tif(((state >> IWM_BIT_DRIVE_SEL) & 1) != on) {\n\t\t\t\tiwm_flush_cur_disk();\n\t\t\t}\n\t\t\tstate &= (~IWM_STATE_DRIVE_SEL);\n\t\t\tstate |= (on << IWM_BIT_DRIVE_SEL);\n\t\t\tg_iwm.state = state;\n\t\t\tdsk = iwm_get_dsk(state);\n\t\t\tif(dsk->wozinfo_ptr && g_iwm_motor_on) {\n\t\t\t\tg_fast_disk_emul = 0;\n\t\t\t} else {\n\t\t\t\tg_fast_disk_emul = g_fast_disk_emul_en;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0xc:\n\t\tcase 0xd:\n\t\t\tmask = IWM_STATE_Q6 | IWM_STATE_Q7 |\n\t\t\t\t\tIWM_STATE_MOTOR_ON | IWM_STATE_ENABLE2;\n\t\t\tmask_on = IWM_STATE_Q6 | IWM_STATE_MOTOR_ON;\n\t\t\tif(((state & mask) == mask_on) && !on) {\n\t\t\t\t// q6 on, q7 off, !on, motor_on, !enable2\n\t\t\t\t// Switched from $c08d to $c08c, resync now\n\t\t\t\t// If latch mode, back up one more bit\n\t\t\t\t//  (for The School Speller - Tools.woz)\n\t\t\t\tforced_sync_bit = iwm_calc_bit_sum(\n\t\t\t\t\tfbit_pos >> 9, 0 - (state & 1),\n\t\t\t\t\ttrack_bits);\n\t\t\t\tg_iwm.forced_sync_bit = forced_sync_bit;\n\t\t\t\tg_iwm.last_rd_bit = forced_sync_bit;\n\t\t\t\tdbg_log_info(dfcyc, forced_sync_bit*2, 0,\n\t\t\t\t\t\t\t\t0x500ec);\n\t\t\t}\n\t\t\tstate &= (~IWM_STATE_Q6);\n\t\t\tstate |= (on << IWM_BIT_Q6);\n\t\t\tg_iwm.state = state;\n\t\t\tbreak;\n\t\tcase 0xe:\n\t\tcase 0xf:\n\t\t\tmask = IWM_STATE_Q7 | IWM_STATE_MOTOR_ON |\n\t\t\t\t\t\t\tIWM_STATE_ENABLE2;\n\t\t\tmask_on = IWM_STATE_Q7 | IWM_STATE_MOTOR_ON;\n\t\t\tif(((state & mask) == mask_on) && !on) {\n\t\t\t\t// q7, !on, motor_on, !enable2\n#if 0\n\t\t\t\tprintf(\"state:%04x and new q7:%d, write_end\\n\",\n\t\t\t\t\tstate, on);\n#endif\n\t\t\t\tdbg_log_info(dfcyc, state, mask, 0xed);\n\t\t\t\tiwm_write_end(dsk, 1, dfcyc);\n\t\t\t\t// printf(\"write end complete, the track:\\n\");\n\t\t\t\t// iwm_check_nibblization(dfcyc);\n\t\t\t}\n\t\t\tstate &= (~IWM_STATE_Q7);\n\t\t\tstate |= (on << IWM_BIT_Q7);\n\t\t\tg_iwm.state = state;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tprintf(\"iwm_touch_switches: loc: %02x unknown!\\n\", loc);\n\t\t\texit(2);\n\t\t}\n\t}\n\n\tif(!(state & IWM_STATE_Q7)) {\n\t\tg_iwm.num_active_writes = 0;\n\t\tif(g_slow_525_emul_wr) {\n\t\t\tg_slow_525_emul_wr = 0;\n\t\t\tengine_recalc_events();\n\t\t}\n\t}\n\n\treturn dsk;\n}\n\nvoid\niwm_move_to_ftrack(Disk *dsk, word32 new_frac_track, int delta, dword64 dfcyc)\n{\n\tTrk\t*trkptr;\n\tword32\ttrack_bits, cur_frac_track, wr_last_bit, write_val;\n\tint\tdisk_525, drive, new_track, cur_track, max_track;\n\tint\tnum_active_writes;\n\n\tif(dsk->smartport) {\n\t\treturn;\n\t}\n\tcur_frac_track = dsk->cur_frac_track;\n\tif(delta != 0) {\n\t\t// 3.5\" move, clear out lower fractional track bits\n\t\tnew_frac_track = new_frac_track & (-0x10000);\n\t\tif((delta < 0) && (new_frac_track < (word32)(-delta))) {\n\t\t\t// Moving down past track 0...stop, but preserve side\n\t\t\tnew_frac_track = cur_frac_track & 0x10000;\n\t\t\tdelta = 0;\n\t\t}\n\t}\n\tnew_frac_track = new_frac_track + delta;\n\n\tdisk_525 = dsk->disk_525;\n#if 0\n\tprintf(\"iwm_move_to_track: %07x, num_tracks:%03x (cur:%07x) %016llx\\n\",\n\t\tnew_frac_track, dsk->num_tracks, dsk->cur_frac_track, dfcyc);\n#endif\n\n\tmax_track = 36*4 - 1;\t\t\t// Go to track 35.75 on 5.25\"\n\tif(dsk->num_tracks >= (max_track + 1)) {\n\t\tmax_track = dsk->num_tracks - 1;\n\t}\n\tif(max_track >= 159) {\n\t\tmax_track = 159;\t\t// Limit to 159 always\n\t}\n\tif(new_frac_track > (word32)(max_track << 16)) {\n\t\tnew_frac_track = max_track << 16;\n\t}\n\tnew_track = (new_frac_track + 0x8000) >> 16;\n\n\tcur_track = (cur_frac_track + 0x8000) >> 16;\n\tnum_active_writes = g_iwm.num_active_writes;\n\twr_last_bit = g_iwm.wr_last_bit[0];\n\twrite_val = g_iwm.write_val;\n\tif(num_active_writes) {\n\t\tiwm_write_end(dsk, 0, dfcyc);\n\t\tprintf(\"moving arm to new_track:%d, write active:%d, bit:%08x, \"\n\t\t\t\"write_val:%02x\\n\", new_track, num_active_writes,\n\t\t\twr_last_bit, write_val);\n\t}\n\tif((cur_track != new_track) || (dsk->cur_trk_ptr == 0)) {\n\t\tdrive = dsk->drive + 1;\n\t\tif(1) {\n\t\t\t// Don't do this printf\n\t\t} else if(disk_525) {\n\t\t\tprintf(\"s6d%d Track: %d.%02d\\n\", drive,\n\t\t\t\tnew_track >> 2, 25* (new_track & 3));\n\t\t} else {\n\t\t\tprintf(\"s5d%d Track: %d Side: %d\\n\", drive,\n\t\t\t\tnew_track >> 1, new_track & 1);\n\t\t}\n\n\t\ttrkptr = &(dsk->trks[new_track]);\n\t\ttrack_bits = trkptr->track_bits;\n\t\tif((track_bits == 0) && disk_525) {\n\t\t\t// Use an adjacent .25 track if it is valid\n\t\t\tif((new_track > 0) && (trkptr[-1].track_bits != 0)) {\n\t\t\t\tnew_track--;\n\t\t\t\t// printf(\"Moved dn to qtrk:%04x\\n\", new_track);\n\t\t\t} else if((new_track < max_track) &&\n\t\t\t\t\t\t(trkptr[1].track_bits != 0)) {\n\t\t\t\tnew_track++;\n\t\t\t\t// printf(\"Moved up to qtrk:%04x\\n\", new_track);\n\t\t\t}\n\t\t}\n\t\tiwm_flush_disk_to_unix(dsk);\n\t\tiwm_move_to_qtr_track(dsk, new_track);\n\t\tif(dfcyc != 0) {\n\t\t\tdbg_log_info(dfcyc, cur_frac_track, new_frac_track,\n\t\t\t\t(g_iwm.state << 16) | 0x00f000e1);\n#if 0\n\t\t\tprintf(\"Just moved from track %08x to %08x %016llx\\n\",\n\t\t\t\tcur_frac_track, new_frac_track, dfcyc);\n#endif\n\t\t}\n\t}\n\tdsk->cur_frac_track = new_frac_track;\n\tif(num_active_writes) {\n\t\tiwm_start_write(dsk, wr_last_bit, write_val, 1);\n\t}\n}\n\nvoid\niwm_move_to_qtr_track(Disk *dsk, word32 qtr_track)\n{\n\tTrk\t*trkptr;\n\tword32\ttrack_bits, fbit_pos;\n\n\ttrkptr = &(dsk->trks[qtr_track]);\n\ttrack_bits = trkptr->track_bits;\n\n\tdsk->cur_trk_ptr = trkptr;\n\tdsk->cur_track_bits = track_bits;\n\tfbit_pos = dsk->cur_fbit_pos;\n\tif(track_bits) {\n\t\t// Moving to a valid track.  Ensure fbit_pos in range\n\t\tfbit_pos = fbit_pos % (track_bits * 512);\n\t}\n\tdsk->cur_fbit_pos = fbit_pos;\n}\n\ndword64 g_iwm_last_phase_dfcyc = 0;\n\nvoid\niwm525_update_phases(Disk *dsk, dword64 dfcyc)\n{\n\tword32\tign_mask;\n\tint\tlast_phases, new_phases, eff_last_phases, eff_new_phases;\n\tint\tmy_phase;\n\n\t// Decide if dsk->last_phases needs to change.\n\tlast_phases = dsk->last_phases;\n\tnew_phases = (g_iwm.state >> IWM_BIT_PHASES) & 0xf;\n\tif(last_phases != new_phases) {\n\t\tiwm_printf(\"Phases changing %02x -> %02x, ftrack:%08x at %lld, \"\n\t\t\t\"diff:%.2fmsec\\n\", last_phases, new_phases,\n\t\t\tdsk->cur_frac_track, dfcyc >> 16,\n\t\t\t((dfcyc - g_iwm_last_phase_dfcyc) >> 16) / 1000.0);\n\t\tg_iwm_last_phase_dfcyc = dfcyc;\n\t}\n\tmy_phase = (dsk->cur_frac_track >> 17) & 3;\n\tign_mask = 1 << ((2 + my_phase) & 3);\n\teff_last_phases = last_phases & (~ign_mask);\n\teff_new_phases = new_phases & (~ign_mask);\n\tif(eff_last_phases == eff_new_phases) {\n\t\tdsk->last_phases = new_phases;\n\t\treturn;\t\t\t\t// Nothing to do\n\t}\n\n\t// Update last_phases\n\tiwm525_update_head(dsk, dfcyc);\n\n\tdsk->last_phases = new_phases;\n\tdsk->dfcyc_last_phases = dfcyc;\n\tdbg_log_info(dfcyc, new_phases, dsk->cur_frac_track, 0x100e1);\n}\n\nvoid\niwm525_update_head(Disk *dsk, dword64 dfcyc)\n{\n\tdouble\tdinc;\n\tdword64\tdiff_dusec, dfcyc_last_phases;\n\tword32\tcur_frac_track, frac_track, sum, num, phases, diff;\n\tint\tnew_qtrk, cur_qtrk, my_phase, prev_phase, grind, inc;\n\tint\ti;\n\n\tcur_frac_track = dsk->cur_frac_track;\n\tmy_phase = (dsk->cur_frac_track >> 17) & 3;\n\n\t// If my_phase is 1, then phase 0 being on should decrement the trk\n\t//  number, and phase 2 being on should increment the trk number\n\tphases = dsk->last_phases & 0xf;\t// one bit for each phase\n\tphases = (phases << 4) | phases;\n\tprev_phase = (my_phase + 4 - 1) & 3;\n\tphases = (phases >> prev_phase) & 0xf;\n\n\t// Now, phases[0] means decrement trk, phases[1] is where we are,\n\t//  phases[2] means increment trk, and phases[3] MIGHT mean increment\n\tsum = 0;\n\tnum = 0;\n\tgrind = 0;\n\tfor(i = 0; i < 4; i++) {\n\t\tif(((phases >> i) & 1) == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tfrac_track = cur_frac_track & -0x20000;\n\t\tswitch(i) {\n\t\tcase 0:\t\t// Previous phase is on, decrement trk\n\t\t\tif(frac_track >= 0x20000) {\n\t\t\t\tfrac_track -= 0x20000;\n\t\t\t} else {\n\t\t\t\tfrac_track = 0;\n\t\t\t}\n\t\t\tsum += frac_track;\n\t\t\tnum++;\n\t\t\tif(cur_frac_track == 0) {\n\t\t\t\tgrind++;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 1:\t\t// \"our\" phase, pull to aligned track\n\t\t\tsum += frac_track;\n\t\t\tnum++;\n\t\t\tbreak;\n\t\tcase 2:\t\t// Next phase, increment track\n\t\t\tsum += frac_track + 0x20000;\n\t\t\tnum++;\n\t\t\tbreak;\n\t\tcase 3:\t\t// Next next phase: might pull\n\t\t\t// Phase is nominally 2 away...but if cur_frac_track\n\t\t\t//  is just a little less than a new halftrack,\n\t\t\t//  (0xxx1ffff), then it may be within a half track\n\t\t\t//  and we'll let it pull us\n\t\t\tfrac_track += 0x20000;\n\t\t\tif((frac_track - cur_frac_track) < 0x30000) {\n\t\t\t\tsum += frac_track;\n\t\t\t\tnum++;\n\t\t\t}\n\t\t}\n\t}\n\n\tfrac_track = cur_frac_track;\n\tdfcyc_last_phases = dsk->dfcyc_last_phases;\n\tdsk->dfcyc_last_phases = 0;\n\tif(num) {\n\t\tfrac_track = sum / num;\t\t// New desired track\n\t\t// Now see if enough time has elapsed that we got to frac_track\n\t\tdiff_dusec = (dfcyc - dfcyc_last_phases) >> 16;\n\t\tdinc = 0x20000 / 2800.0;\t// 2.8msec to move one phase\n\t\tinc = (int)dinc;\n\t\tif(cur_frac_track >= frac_track) {\n\t\t\tdiff = cur_frac_track - frac_track;\n\t\t\tinc = -inc;\n\t\t} else {\n\t\t\tdiff = frac_track - cur_frac_track;\n\t\t}\n\t\tif(g_fast_disk_emul) {\n\t\t\tdiff = 0;\t\t// Always moved enough\n\t\t}\n\t\t// Add inc*diff_dusec to cur_frac_track, unless we already reach\n\t\tif(diff > (dinc * diff_dusec)) {\n\t\t\t// Enough time has NOT elapsed, so calc where head is\n\t\t\t// Update frac_track to be cur_frac_track + inc * dusec\n\t\t\tfrac_track = cur_frac_track +\n\t\t\t\t\t\t(word32)(inc * diff_dusec);\n\t\t\tdsk->dfcyc_last_phases = dfcyc;\n\t\t}\n\t}\n\n\tnew_qtrk = (frac_track + 0x8000) >> 16;\n\tcur_qtrk = (cur_frac_track + 0x8000) >> 16;\n\tif(new_qtrk != cur_qtrk) {\n\t\tif(g_halt_arm_move) {\n\t\t\thalt_printf(\"Halt on arm move\\n\");\n\t\t\tg_halt_arm_move = 0;\n\t\t}\n\t\tif(grind) {\n\t\t\tprintf(\"GRIND GRIND GRIND\\n\");\n\t\t}\n\n\t\tdbg_log_info(dfcyc, frac_track, dsk->cur_frac_track,\n\t\t\t\t\t\t(phases << 24) | 0x00e1);\n\t\tiwm_move_to_ftrack(dsk, frac_track, 0, dfcyc);\n\n\t\tif(new_qtrk > 2) {\n#if 0\n\t\t\tprintf(\"Moving to qtr track: %04x (trk:%d.%02d), %d, \"\n\t\t\t\t\"%02x, %08x dfcyc:%lld\\n\", new_qtrk,\n\t\t\t\tnew_qtrk >> 2, 25*(new_qtrk & 3), my_phase,\n\t\t\t\tphases, g_iwm.state, dfcyc >> 16);\n#endif\n\t\t}\n\t} else {\n\t\t// On the same qtr_track, but update the fraction\n\t\tdsk->cur_frac_track = frac_track;\n\t}\n\n#if 0\n\t/* sanity check stepping algorithm */\n\tif((qtr_track & 7) == 0) {\n\t\t/* check for just access phase 0 */\n\t\tif(last_phase != 0) {\n\t\t\thalt_printf(\"last_phase: %d!\\n\", last_phase);\n\t\t}\n\t}\n#endif\n}\n\nint\niwm_read_status35(dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tword32\tstate;\n\tint\tdrive, stat35, tmp;\n\n\t// This is usually done by STAT35 at ff/5fa4\n\t//  This code is called on the rising edge of Q6 asserted\n\tstate = g_iwm.state;\n\tdrive = (state >> IWM_BIT_DRIVE_SEL) & 1;\n\tdsk = &(g_iwm.drive35[drive]);\n\n\tif(state & IWM_STATE_MOTOR_ON) {\n\t\t/* Read status: ph[1], ph[0], disk35, ph[2] */\n\t\tstat35 = (((state >> IWM_BIT_PHASES) & 3) << 2) |\n\t\t\t((state >> 6) & 2) |\n\t\t\t(((state >> IWM_BIT_PHASES) >> 2) & 1);\n\n\t\tiwm_printf(\"Iwm status read state: %02x\\n\", state);\n\t\tdbg_log_info(dfcyc, state, stat35, 0xe7);\n\n\t\tswitch(stat35) {\n\t\tcase 0x00:\t/* step direction */\n\t\t\treturn (state >> IWM_BIT_STEP_DIRECTION35) & 1;\n\t\t\tbreak;\n\t\tcase 0x01:\t/* lower head activate */\n\t\t\t/* also return instantaneous data from head */\n\t\t\tiwm_move_to_ftrack(dsk,\n\t\t\t\t(dsk->cur_frac_track & (-0x20000)), 0, dfcyc);\n\t\t\treturn (dsk->cur_fbit_pos >> 15) & 1;\n\t\t\tbreak;\n\t\tcase 0x02:\t/* disk in place */\n\t\t\t/* 1 = no disk, 0 = disk */\n\t\t\tiwm_printf(\"read disk in place, num_tracks: %d\\n\",\n\t\t\t\tdsk->num_tracks);\n\t\t\ttmp = (dsk->num_tracks <= 0);\n\t\t\tdbg_log_info(dfcyc, 0, dsk->num_tracks, 0x100e7);\n\t\t\treturn tmp;\n\t\t\tbreak;\n\t\tcase 0x03:\t/* upper head activate */\n\t\t\t/* also return instantaneous data from head */\n\t\t\tiwm_move_to_ftrack(dsk, dsk->cur_frac_track | 0x10000,\n\t\t\t\t\t0, dfcyc);\n\t\t\treturn (dsk->cur_fbit_pos >> 15) & 1;\n\t\t\tbreak;\n\t\tcase 0x04:\t/* disk is stepping? */\n\t\t\t/* 1 = not stepping, 0 = stepping */\n\t\t\treturn 1;\n\t\t\tbreak;\n\t\tcase 0x05:\t/* Unknown function of ROM 03? */\n\t\t\t/* 1 = or $20 into 0xe1/f24+drive, 0 = don't */\n\t\t\treturn 1;\n\t\t\tbreak;\n\t\tcase 0x06:\t/* disk is locked */\n\t\t\t/* 0 = locked, 1 = unlocked */\n\t\t\treturn (!dsk->write_prot);\n\t\t\tbreak;\n\t\tcase 0x08:\t/* motor on */\n\t\t\t/* 0 = on, 1 = off */\n\t\t\treturn !(state & IWM_STATE_MOTOR_ON35);\n\t\t\tbreak;\n\t\tcase 0x09:\t/* number of sides */\n\t\t\t/* 1 = 2 sides, 0 = 1 side */\n\t\t\treturn 1;\n\t\t\tbreak;\n\t\tcase 0x0a:\t/* at track 0 */\n\t\t\t/* 1 = not at track 0, 0 = there */\n\t\t\ttmp = (dsk->cur_frac_track != 0);\n\t\t\tiwm_printf(\"Read at track0_35: %d\\n\", tmp);\n\t\t\treturn tmp;\n\t\t\tbreak;\n\t\tcase 0x0b:\t/* disk ready??? */\n\t\t\t/* 0 = ready, 1 = not ready? */\n\t\t\ttmp = !(state & IWM_STATE_MOTOR_ON35);\n\t\t\tiwm_printf(\"Read disk ready, ret: %d\\n\", tmp);\n\t\t\treturn tmp;\n\t\t\tbreak;\n\t\tcase 0x0c:\t/* disk switched?? */\n\t\t\t/* 0 = not switched, 1 = switched? */\n\t\t\ttmp = (dsk->just_ejected != 0);\n\t\t\tiwm_printf(\"Read disk switched: %d\\n\", tmp);\n\t\t\treturn tmp;\n\t\t\tbreak;\n\t\tcase 0x0d:\t/* false read when ejecting disk */\n\t\t\treturn 1;\n\t\tcase 0x0e:\t/* tachometer */\n\t\t\thalt_printf(\"Reading tachometer!\\n\");\n\t\t\treturn (dsk->cur_fbit_pos >> 11) & 1;\n\t\t\tbreak;\n\t\tcase 0x0f:\t/* drive installed? */\n\t\t\t/* 0 = drive exists, 1 = no drive */\n\t\t\tif(drive) {\n\t\t\t\t/* pretend no drive 1 */\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\treturn 0;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thalt_printf(\"Read 3.5 status, stat35: %02x\\n\", stat35);\n\t\t\treturn 1;\n\t\t}\n\t} else {\n\t\tiwm_printf(\"Read 3.5 status with drive off!\\n\");\n\t\treturn 1;\n\t}\n}\n\nvoid\niwm_do_action35(dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tword32\tstate;\n\tint\tdrive, stat35;\n\n\t// Actions done by CONT35 routine at ff/5fae\n\t//  This code is called on the rising edge of phase[3] asserted\n\tstate = g_iwm.state;\n\tdrive = (state >> IWM_BIT_DRIVE_SEL) & 1;\n\tdsk = &(g_iwm.drive35[drive]);\n\n\tif(state & IWM_STATE_MOTOR_ON) {\n\t\t/* Perform action */\n\t\t/* stat35: ph[1], ph[0], disk35_ctrl, ph[2] */\n\t\tstat35 = (((state >> IWM_BIT_PHASES) & 3) << 2) |\n\t\t\t((state >> 6) & 2) |\n\t\t\t(((state >> IWM_BIT_PHASES) >> 2) & 1);\n\t\tdbg_log_info(dfcyc, state, stat35, 0xf00e7);\n\n\t\tswitch(stat35) {\n\t\tcase 0x00:\t/* Set step direction inward (higher tracks) */\n\t\t\t/* towards higher tracks, clear STEP_DIRECTION35 */\n\t\t\tstate &= (~IWM_STATE_STEP_DIRECTION35);\n\t\t\tiwm_printf(\"Iwm set step dir35 = 0\\n\");\n\t\t\tbreak;\n\t\tcase 0x01:\t/* Set step direction outward (lower tracks) */\n\t\t\t/* towards lower tracks */\n\t\t\tstate |= IWM_STATE_STEP_DIRECTION35;\n\t\t\tdbg_log_info(dfcyc, state, stat35, 0x300e7);\n\t\t\tiwm_printf(\"Iwm set step dir35 = 1\\n\");\n\t\t\tbreak;\n\t\tcase 0x03:\t/* reset disk-switched flag? */\n\t\t\tiwm_printf(\"Iwm reset disk switch\\n\");\n\t\t\tdsk->just_ejected = 0;\n\t\t\tbreak;\n\t\tcase 0x04:\t/* step disk */\n\t\t\tif(state & IWM_STATE_STEP_DIRECTION35) {\n\t\t\t\tiwm_move_to_ftrack(dsk, dsk->cur_frac_track,\n\t\t\t\t\t-0x20000, dfcyc);\n\t\t\t} else {\n\t\t\t\tiwm_move_to_ftrack(dsk, dsk->cur_frac_track,\n\t\t\t\t\t0x20000, dfcyc);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x08:\t/* turn motor on */\n\t\t\tiwm_printf(\"Iwm set motor_on35 = 1\\n\");\n\t\t\tstate |= IWM_STATE_MOTOR_ON35;\n\t\t\tbreak;\n\t\tcase 0x09:\t/* turn motor off */\n\t\t\tiwm_printf(\"Iwm set motor_on35 = 0\\n\");\n\t\t\tstate &= (~IWM_STATE_MOTOR_ON35);\n\t\t\tbreak;\n\t\tcase 0x0d:\t/* eject disk */\n\t\t\tprintf(\"Action 0x0d, will eject disk\\n\");\n\t\t\tiwm_eject_disk(dsk);\n\t\t\tbreak;\n\t\tcase 0x02:\n\t\tcase 0x07:\n\t\tcase 0x0b: /* hacks to allow AE 1.6MB driver to not crash me */\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thalt_printf(\"Do 3.5 action, state: %02x\\n\", state);\n\t\t\treturn;\n\t\t}\n\t} else {\n\t\thalt_printf(\"Set 3.5 status with drive off!\\n\");\n\t\treturn;\n\t}\n\tg_iwm.state = state;\n\tdbg_log_info(dfcyc, state, stat35, 0x400e7);\n}\n\nint\nread_iwm(int loc, dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tword32\tstatus, bit_diff, bit_pos, state;\n\tint\ton, q7_q6, val;\n\n\tloc = loc & 0xf;\n\ton = loc & 1;\n\n\tdsk = iwm_touch_switches(loc, dfcyc);\n\n\tstate = g_iwm.state;\n\tq7_q6 = (state >> IWM_BIT_Q6) & 3;\n\n\tif(on) {\n\t\t/* odd address, return 0 */\n\t\treturn 0;\n\t} else {\n\t\t/* even address */\n\t\tswitch(q7_q6) {\n\t\tcase 0x00:\t/* q7 = 0, q6 = 0 */\n\t\t\tif(state & IWM_STATE_ENABLE2) {\n\t\t\t\treturn iwm_read_enable2(dfcyc);\n\t\t\t} else {\n\t\t\t\tif(state & IWM_STATE_MOTOR_ON) {\n\t\t\t\t\treturn iwm_read_data(dsk, dfcyc);\n\t\t\t\t} else {\n\t\t\t\t\tiwm_printf(\"read iwm st 0, m off!\\n\");\n/* HACK!!!! */\n\t\t\t\t\treturn 0xff;\n\t\t\t\t\t//return (((int)dfcyc) & 0x7f) + 0x80;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x01:\t/* q7 = 0, q6 = 1 */\n\t\t\t/* read IWM status reg */\n\t\t\tif(state & IWM_STATE_ENABLE2) {\n\t\t\t\tiwm_printf(\"Read status under enable2: 1\\n\");\n\t\t\t\tstatus = 1;\n\t\t\t} else {\n\t\t\t\tif(state & IWM_STATE_C031_APPLE35SEL) {\n\t\t\t\t\tstatus = iwm_read_status35(dfcyc);\n\t\t\t\t} else {\n\t\t\t\t\tstatus = dsk->write_prot;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tval = ((status & 1) << 7) | (state & 0x3f);\n\t\t\t// bit 5 is motor_on, bits[4:0] are iwm_mode\n\t\t\tiwm_printf(\"Read status: %02x\\n\", val);\n\n\t\t\treturn val;\n\t\t\tbreak;\n\t\tcase 0x02:\t/* q7 = 1, q6 = 0 */\n\t\t\t/* read handshake register */\n\t\t\tif(state & IWM_STATE_ENABLE2) {\n\t\t\t\treturn iwm_read_enable2_handshake(dfcyc);\n\t\t\t} else {\n\t\t\t\tstatus = 0xc0;\n\t\t\t\tbit_pos = dsk->cur_fbit_pos >> 9;\n\t\t\t\tbit_diff = iwm_calc_bit_diff(bit_pos,\n\t\t\t\t\t\tg_iwm.wr_last_bit[0],\n\t\t\t\t\t\tdsk->cur_track_bits);\n\t\t\t\tif(bit_diff > 8) {\n\t\t\t\t\tiwm_printf(\"Write underrun!\\n\");\n\t\t\t\t\tstatus = status & 0xbf;\n\t\t\t\t}\n\t\t\t\treturn status;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x03:\t/* q7 = 1, q6 = 1 */\n\t\t\tiwm_printf(\"read iwm q7_q6=3!\\n\");\n\t\t\treturn 0;\n\t\tbreak;\n\t\t}\n\t}\n\thalt_printf(\"Got to end of read_iwm, loc: %02x!\\n\", loc);\n\n\treturn 0;\n}\n\nvoid\nwrite_iwm(int loc, int val, dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tword32\tstate;\n\tint\ton, q7_q6, drive, fast_writes;\n\n\tloc = loc & 0xf;\n\ton = loc & 1;\n\n\tdsk = iwm_touch_switches(loc, dfcyc);\n\n\tstate = g_iwm.state;\n\tq7_q6 = (state >> IWM_BIT_Q6) & 3;\n\tdrive = (state >> IWM_BIT_DRIVE_SEL) & 1;\n\tfast_writes = g_fast_disk_emul;\n\tif(state & IWM_STATE_C031_APPLE35SEL) {\n\t\tdsk = &(g_iwm.drive35[drive]);\n\t} else {\n\t\tdsk = &(g_iwm.drive525[drive]);\n\t\tfast_writes = !g_slow_525_emul_wr && fast_writes;\n\t}\n\n\tif(on) {\n\t\t/* odd address, write something */\n\t\tif(q7_q6 == 3) {\n\t\t\t/* q7, q6 = 1,1 */\n\t\t\tif(state & IWM_STATE_MOTOR_ON) {\n\t\t\t\tif(state & IWM_STATE_ENABLE2) {\n\t\t\t\t\tiwm_write_enable2(val);\n\t\t\t\t} else {\n\t\t\t\t\tiwm_write_data(dsk, val, dfcyc);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t/* write mode register */\n\t\t\t\t// bit 0: latch mode (should set if async hand)\n\t\t\t\t// bit 1: async handshake\n\t\t\t\t// bit 2: immediate motor off (no 1 sec delay)\n\t\t\t\t// bit 3: 2us bit timing\n\t\t\t\t// bit 4: Divide input clock by 8 (instead of 7)\n\t\t\t\tval = val & 0x1f;\n\t\t\t\tstate = (state & (~0x1f)) | val;\n\t\t\t\tg_iwm.state = state;\n\t\t\t\tif(val & 0x10) {\n\t\t\t\t\tiwm_printf(\"set iwm_mode:%02x!\\n\",val);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif(state & IWM_STATE_ENABLE2) {\n\t\t\t\tiwm_write_enable2(val);\n\t\t\t} else {\n#if 0\n// Flobynoid writes to 0xc0e9 causing these messages...\n\t\t\t\tprintf(\"Write iwm1, st: %02x, loc: %x: %02x\\n\",\n\t\t\t\t\tq7_q6, loc, val);\n#endif\n\t\t\t}\n\t\t}\n\t\treturn;\n\t} else {\n\t\t/* even address */\n\t\tif(state & IWM_STATE_ENABLE2) {\n\t\t\tiwm_write_enable2(val);\n\t\t} else {\n\t\t\tiwm_printf(\"Write iwm2, st: %02x, loc: %x: %02x\\n\",\n\t\t\t\tq7_q6, loc, val);\n\t\t}\n\t\treturn;\n\t}\n\n\treturn;\n}\n\n\nint\niwm_read_enable2(dword64 dfcyc)\n{\n\tiwm_printf(\"Read under enable2 %016llx!\\n\", dfcyc);\n\treturn 0xff;\n}\n\nint g_cnt_enable2_handshake = 0;\n\nint\niwm_read_enable2_handshake(dword64 dfcyc)\n{\n\tint\tval;\n\n\tiwm_printf(\"Read handshake under enable2, %016llx!\\n\", dfcyc);\n\n\tval = 0xc0;\n\tg_cnt_enable2_handshake++;\n\tif(g_cnt_enable2_handshake > 3) {\n\t\tg_cnt_enable2_handshake = 0;\n\t\tval = 0x80;\n\t}\n\n\treturn val;\n}\n\nvoid\niwm_write_enable2(int val)\n{\n\t// Smartport is selected (PH3=1, PH1=1, Sel35=0), just ignore this data\n\tiwm_printf(\"Write under enable2: %02x!\\n\", val);\n\n\treturn;\n}\n\nword32\niwm_fastemul_start_write(Disk *dsk, dword64 dfcyc)\n{\n\tdouble\tnew_fast_cycs;\n\tdword64\tdfcyc_passed;\n\tword32\tfbit_pos, fbit_diff, track_bits;\n\n\t// Nox Archaist doesn't finish reading sector header's checksum, but\n\t//  instead waits for 7 bytes to pass and then writes.  This code\n\t//  tries to allow fast_disk_emul mode to not overwrite the checksum.\n\t// Move the fbit_pos forward to try to account for a delay from the\n\t//  last read to the current write.  But accesses to I/O locations\n\t//  would still take a lot of time.  Let's assume there were\n\t//  2 slow cycles, and clamp the skip to a min of 1 byte, max 3 bytes.\n\tdfcyc_passed = dfcyc - g_iwm.dfcyc_last_fastemul_read;\n\tnew_fast_cycs = (((double)dfcyc_passed) - 0x20000) /\n\t\t\t\t\t((double)engine.fplus_ptr->dplus_1);\n\t// new_fast_cycs approximates the number of fast cycles that have\n\t//  passed, so 4.0 means 4 fast cycles have passed\n\n\tiwm_printf(\"start write, dfcyc:%016llx, new_f_cycs:%f, plus_1:%08llx\\n\",\n\t\t\tdfcyc_passed, new_fast_cycs, engine.fplus_ptr->dplus_1);\n\n\tfbit_diff = (word32)(new_fast_cycs * dsk->fbit_mult);\n\tif(new_fast_cycs < 0.0) {\n\t\tfbit_diff = 8*512;\t\t// 8 bits\n\t} else {\n\t\tif(fbit_diff < 8*512) {\t\t// 8 bits\n\t\t\tfbit_diff = 8*512;\n\t\t} else if(fbit_diff > (32*512)) {\n\t\t\tfbit_diff = 32*512;\n\t\t}\n\t}\n\ttrack_bits = dsk->cur_track_bits;\n\tfbit_pos = dsk->cur_fbit_pos;\n\n\tif(track_bits == 0) {\n\t\treturn fbit_pos;\n\t}\n\n\tfbit_pos = fbit_pos + fbit_diff;\n\tif(fbit_pos >= (track_bits * 512)) {\n\t\tfbit_pos = fbit_pos - (track_bits * 512);\n\t}\n#if 0\n\tprintf(\" adjusted fbit_pos from %07x to %07x\\n\",\n\t\t\t\t\tdsk->cur_fbit_pos, fbit_pos);\n#endif\n\tdbg_log_info(dfcyc, fbit_pos, dsk->cur_fbit_pos, 0xee);\n\tdsk->cur_fbit_pos = fbit_pos;\n\n\treturn fbit_pos;\n}\n\nword32\niwm_read_data_fast(Disk *dsk, dword64 dfcyc)\n{\n\tdword64\tdval, dsync_val_tmp, dsync_val;\n\tword32\tbit_pos, new_bit_pos, track_bits, val;\n\tint\tmsb, dec;\n\n\tif(!g_fast_disk_unnib) {\n\t\tg_iwm.dfcyc_last_fastemul_read = dfcyc;\n\t}\n\ttrack_bits = dsk->cur_track_bits;\n\tbit_pos = dsk->cur_fbit_pos >> 9;\n\n\t// fbit_pos points just after the LSB of the last nibble.  Read +8\n\t//  past to get the next nibble.  Get more to ensure a valid nibble\n\tnew_bit_pos = iwm_calc_bit_sum(bit_pos, 15, track_bits);\n\tdval = iwm_get_raw_bits(dsk, new_bit_pos, 16, &dsync_val_tmp);\n\tdsync_val = dsync_val_tmp;\n#if 0\n\tprintf(\"fast %010llx syncs:%010llx bit:%07x\\n\", dval, dsync_val,\n\t\t\t\t\t\t\tbit_pos * 2);\n#endif\n\n\tif(!g_fast_disk_unnib) {\n\t\t//dbg_log_info(dfcyc, dval, dsync_val, 0xeb);\n\t}\n\t// Find the sync val closest to 15 without going over\n\tmsb = (dsync_val >> 8) & 0xff;\n\tif((msb > 15) || (msb == 0)) {\n\t\tmsb = dsync_val & 0xff;\n\t}\n\tif(msb > 15) {\n\t\tmsb = 8;\t// Just return something\n\t}\n\tif(msb < 7) {\n\t\t// This can happen when the arm is moved from a long track to a\n\t\t//  shorter track, fbit_pos at an arbitrary position at the\n\t\t//  track start, placing us at dsync_val=0x1006.  Just ignore\n\t\tmsb = 7;\n\t}\n\tval = (word32)(dval >> (msb - 7));\n\n\t// We've moved new_fbit_pos forward 15 bits, move back 7 bits for msb=15\n\tdec = 15 - msb - 7;\n\tif(!g_iwm_motor_on && (msb == 15) && !g_fast_disk_unnib) {\n\t\t// Return this byte properly...but not the next one for the\n\t\t//  DOS3.3 RWTS motor on detect code, which reads the drive\n\t\t//  without enabling the motor, to see if the motor is on\n\t\tdec--;\n\t}\n\tif((msb != 15) && !g_fast_disk_unnib) {\n\t\t// Pull a trick to make the disk motor-on test pass ($bd34 in\n\t\t//  DOS 3.3 RWTS): if this is a sync byte, don't return whole\n\t\t//  byte, but return whole byte next time\n\t\tval = val & 0x7f;\n\t\tdec = dec - 8;\n\t}\n\tdsk->cur_fbit_pos = iwm_calc_bit_sum(new_bit_pos, dec, track_bits) << 9;\n#if 0\n\tprintf(\"  val:%02x fbit:%07x new_fbit:%07x dec:%d, msb:%d\\n\",\n\t\tval & 0xff, dsk->cur_fbit_pos, new_fbit_pos, dec, msb);\n#endif\n\tif(!g_fast_disk_unnib) {\n\t\tdbg_log_info(dfcyc, dsk->cur_fbit_pos,\n\t\t\t(dec << 24) | (bit_pos * 2), (val << 16) | 0xeb);\n\t}\n\treturn val & 0xff;\n}\n\ndword64\niwm_return_rand_data(Disk *dsk, dword64 dfcyc)\n{\n\tdword64\tdval, dval2;\n\n\tif(dsk) {\n\t\t// Use dsk\n\t}\n\tdval = dfcyc >> 16;\n\tdval2 = dval ^ (dval >> 16) ^ (dval << 10) ^ (dval << 26) ^\n\t\t\t\t\t\t\t(dval << 41);\n\tdval = dval2 & ((dval >> 6) ^ (dval >> 17));\n\treturn dval;\n}\n\ndword64\niwm_get_raw_bits(Disk *dsk, word32 bit_pos, int num_bits, dword64 *dsyncs_ptr)\n{\n\tbyte\t*bptr, *sync_ptr;\n\tdword64\tdval, dtmp_val, sync_dval;\n\tword32\ttrack_bits;\n\tint\tpos, bits, total_bits, sync_pos, sync;\n\n\ttrack_bits = dsk->cur_track_bits;\n\tif(track_bits == 0) {\n\t\thalt_printf(\"iwm_get_raw_bits track_bits 0, %08x\\n\",\n\t\t\t\t\t\tdsk->cur_frac_track);\n\t\treturn 0;\n\t}\n\ttotal_bits = 0;\n\tbits = 1 + (bit_pos & 7);\t\t// 1..8\n\tpos = bit_pos >> 3;\n\tdval = 0;\n\tsync_dval = 0;\n\tbptr = &(dsk->cur_trk_ptr->raw_bptr[0]);\n\tsync_ptr = &(dsk->cur_trk_ptr->sync_ptr[0]);\n\tsync_pos = 0;\n\tif((bptr == 0) || (sync_ptr == 0)) {\n\t\thalt_printf(\"bptr:%p, sync:%p, bit_pos:%08x, track_bits:%08x, \"\n\t\t\t\"cur_trk_ptr eff:%05lx\\n\", bptr, sync_ptr, bit_pos,\n\t\t\ttrack_bits, dsk->cur_trk_ptr - &(dsk->trks[0]));\n\t\t*dsyncs_ptr = 0xffffffffffffffULL;\n\t\treturn 0;\n\t}\n\twhile(total_bits < num_bits) {\n\t\tdtmp_val = bptr[pos];\n\t\tdtmp_val = dtmp_val >> (8 - bits);\n\t\tdval = dval | (dtmp_val << total_bits);\n\t\tsync = sync_ptr[pos];\n\t\tif((sync < 8) && (sync >= (8 - bits))) {\t// MSB is here\n\t\t\tsync = sync - (8 - bits);\n\t\t\tsync = sync + total_bits;\n\t\t\tif((sync_pos < 64) && (sync != (sync_dval & 0xff))){\n\t\t\t\tsync_dval |= ((dword64)sync & 0xff) << sync_pos;\n\t\t\t\tsync_pos += 8;\n\t\t\t}\n\t\t}\n\t\ttotal_bits += bits;\n\t\tbits = 8;\n\t\tpos--;\n\t\tif(pos < 0) {\n\t\t\tpos = (track_bits - 1) >> 3;\n\t\t\tbits = 1 + ((track_bits - 1) & 7);\n\t\t}\n\t}\n\n\tif(sync_pos == 0) {\n\t\tsync_dval = 0xff;\n\t}\n\t*dsyncs_ptr = sync_dval;\n\treturn dval;\n}\n\nword32\niwm_calc_bit_diff(word32 first, word32 last, word32 track_bits)\n{\n\tword32\tval;\n\n\tval = first - last;\n\tif(val >= track_bits) {\n\t\tval = val + track_bits;\n\t}\n\treturn val;\n}\n\nword32\niwm_calc_bit_sum(word32 bit_pos, int add_ival, word32 track_bits)\n{\n\tword32\tval;\n\n\tval = bit_pos + add_ival;\n\tif(val >= track_bits) {\n\t\tif(add_ival < 0) {\n\t\t\tval = val + track_bits;\n\t\t} else {\n\t\t\tval = val - track_bits;\n\t\t}\n\t}\n\treturn val;\n}\n\ndword64\niwm_calc_forced_sync(dword64 dval, int forced_bit)\n{\n\tdword64\tsync_dval;\n\n\t// Something like the \"E7\" protection scheme has toggled\n\t//  $c0ed in the middle of reading a nibble, causing a new sync.  This\n\t//  is used to \"move\" sync bits inside disk nibbles, to defeat\n\t//  nibble copiers (since a 36-cycle sync nibble cannot be\n\t//  differentiated from a 40-cycle sync nibble in one read pass)\n\t// Return these misaligned nibbles until we re-sync (then clear\n\t//  g_iwm.forced_sync_bit.  Toggling $c0ed can set the data latch\n\t//  to $ff is the disk is write-protected, but this gets cleared\n\t//  to $00 within 4 CPU cycles always (or less).  A phantom 1 will\n\t//  also shift in\n\n\tdval |= (1ULL << forced_bit);\n\tsync_dval = 30;\t\t\t// A default for the previous nibble\n\twhile(forced_bit >= 0) {\n\t\t// Scan until this bit is set\n\t\tif((dval >> forced_bit) & 1) {\n\t\t\t// this bit is the MSB of a nibble, note it\n\t\t\tsync_dval = (sync_dval << 8) | forced_bit;\n\t\t\tforced_bit -= 7;\n\t\t}\n\t\tforced_bit--;\n\t}\n\treturn sync_dval;\n}\n\nint\niwm_calc_forced_sync_0s(dword64 sync_dval, int bits)\n{\n\tint\tsync, forced_bits, done;\n\tint\ti;\n\n\t// Return number between 7 and bits as to the oldest valid sync bit\n\t//  in the sync_dval array.  0xff in the first position means no syncs\n\tif(bits <= 8) {\n\t\treturn bits;\n\t}\n\tforced_bits = 8;\n\tdone = 0;\n\tif((sync_dval & 0xff) <= 7) {\n\t\tsync_dval = sync_dval >> 8;\n\t}\n\tfor(i = 0; i < 8; i++) {\n\t\tsync = sync_dval & 0xff;\n\t\tif(sync == 0xff) {\n\t\t\treturn bits;\n\t\t}\n\t\tsync_dval = sync_dval >> 8;\n\t\twhile(sync > bits) {\n\t\t\tsync -= 8;\t\t// Bring it back into range\n\t\t\tdone = 1;\n\t\t}\n\t\tif(sync < forced_bits) {\n\t\t\tbreak;\n\t\t}\n\t\tif(sync < bits) {\n\t\t\tforced_bits = sync;\n\t\t}\n\t\tif(done) {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn forced_bits;\n}\n\nword32\niwm_read_data(Disk *dsk, dword64 dfcyc)\n{\n\tdword64\tdval, sync_dval, dsync_val_tmp, dval0, dval2;\n\tword32\ttrack_bits, fbit_pos, bit_pos, val, forced_sync_bit;\n\tint\tmsb_bit, bits, forced_bits, diff;\n\n\ttrack_bits = dsk->cur_track_bits;\n\n\tfbit_pos = dsk->cur_fbit_pos;\n\tbit_pos = fbit_pos >> 9;\n\tif((track_bits == 0) || (dsk->cur_trk_ptr == 0)) {\n\t\tval = ((fbit_pos * 25) >> 11) & 0xff;\n\t\tiwm_printf(\"Reading c0ec, track_len 0, returning %02x\\n\", val);\n\t\treturn val;\n\t}\n\n\tif(g_fast_disk_emul) {\n\t\treturn iwm_read_data_fast(dsk, dfcyc);\n\t}\n\n\t// First, get the last few bytes of data\n\tbits = iwm_calc_bit_diff(bit_pos, g_iwm.last_rd_bit, track_bits) + 10;\n\tif(bits < 25) {\n\t\tbits = 25;\n\t} else if(bits > 60) {\n\t\tbits = 60;\n\t}\n\tforced_sync_bit = g_iwm.forced_sync_bit;\n\tforced_bits = -1;\n\tif(forced_sync_bit < track_bits) {\n\t\tforced_bits = iwm_calc_bit_diff(bit_pos, forced_sync_bit,\n\t\t\t\t\t\t\t\ttrack_bits);\n\t\tif(forced_bits >= 64) {\n\t\t\tforced_bits = 63;\n\t\t}\n\t\tif(forced_bits > bits) {\n\t\t\tbits = forced_bits;\n\t\t}\n\t}\n\tdval = iwm_get_raw_bits(dsk, bit_pos, bits, &dsync_val_tmp);\n\tsync_dval = dsync_val_tmp;\n\n\t// See if there are runs of more than three 0 bits...introduce noise\n\tdval0 = (1ULL << bits) | dval;\n\tdval0 = dval0 | (dval0 >> 1) | (dval0 >> 2) | (dval0 >> 3);\n\tdval0 = (~dval0) & ((1ULL << bits) - 1);\n\n\tif(dval0) {\n\t\t// Each set bit of dval0 indicates the previous 3 bits are 0\n\t\tdval2 = dval0 | (dval0 << 1);\n\t\tdval2 = dval0 & iwm_return_rand_data(dsk, dfcyc);\n#if 0\n\t\tprintf(\"dval0 is %016llx, dval2:%016llx, bits:%d\\n\", dval0,\n\t\t\t\t\t\tdval2, bits);\n#endif\n\t\tdval = dval ^ dval2;\n\t\tif(forced_bits < 0) {\n\t\t\tforced_bits = iwm_calc_forced_sync_0s(sync_dval, bits);\n\t\t\tg_iwm.forced_sync_bit = iwm_calc_bit_sum(bit_pos,\n\t\t\t\t\t0 - forced_bits, track_bits);\n\t\t\tdbg_log_info(dfcyc, g_iwm.forced_sync_bit * 2,\n\t\t\t\t(forced_bits << 24) | (bit_pos * 2), 0x400ec);\n#if 0\n\t\t\tprintf(\"Forced bits are %d at %06x, %016llx \"\n\t\t\t\t\"%016llx\\n\", forced_bits, bit_pos * 2, dval,\n\t\t\t\tsync_dval);\n#endif\n\t\t}\n\t}\n\tif(forced_bits >= 0) {\n\t\tsync_dval = iwm_calc_forced_sync(dval, forced_bits);\n\t\tdval = dval & ((2ULL << forced_bits) - 1LL);\n\t\tdbg_log_info(dfcyc, (word32)dval, forced_sync_bit * 32,\n\t\t\t\t\t\t(forced_bits << 16) | 0xea);\n\t\tif(((dsync_val_tmp & 0xff) == (sync_dval & 0xff)) && (!dval0)) {\n\t\t\t// Only clear it if there are no 0's in the dval,\n\t\t\t//  otherwise we'll reenter and could calc a diff sync\n\t\t\tg_iwm.forced_sync_bit = 234567*4;\n\t\t\t//printf(\"cleared forced_sync\\n\");\n\t\t} else {\n\t\t\tg_iwm.forced_sync_bit = iwm_calc_bit_sum(bit_pos,\n\t\t\t\t\t0 - (sync_dval & 0xf), track_bits);\n\t\t}\n\n#if 0\n\t\tprintf(\"Forced_bits were %d, sync_was %016llx\\n\",\n\t\t\t\t\t\tforced_bits, dsync_val_tmp);\n#endif\n\t}\n#if 0\n\tprintf(\" Raw bits: %016llx, dsync:%016llx, fbit:%08x\\n\", dval,\n\t\t\t\t\tsync_dval, fbit_pos);\n#endif\n\n\tdbg_log_info(dfcyc, (word32)dval,\n\t\t(bits << 24) | ((word32)sync_dval & 0x00ffffffUL), 0x300ec);\n\tmsb_bit = sync_dval & 0xff;\n\tif(g_iwm.state & 1) {\t\t// Latch mode (3.5\" disk)\n\t\t// last_rd_bit points just past the last bit read (it is\n\t\t//  fbit_pos normally, which is one bit past the read bit).\n\t\t// Use latched data if that bit is before the latched LSB\n\t\tdiff = iwm_calc_bit_diff(bit_pos, g_iwm.last_rd_bit,\n\t\t\t\t\t\t\t\ttrack_bits);\n\t\tdiff = diff + 8;\t\t// Calc to the MSB\n\t\tmsb_bit = (sync_dval >> 8) & 0xff;\n\t\tif((msb_bit >= 8) && (diff > msb_bit)) {\n\t\t\t// We've not seen the LSB of this data: ret latched data\n\t\t\tdbg_log_info(dfcyc, bit_pos * 2,\n\t\t\t\t\t(msb_bit << 16) | (diff & 0xffff),\n\t\t\t\t\t\t\t\t0x2200ec);\n\t\t\tbit_pos = iwm_calc_bit_sum(bit_pos, -msb_bit + 8,\n\t\t\t\t\t\t\t\ttrack_bits);\n\t\t\tdbg_log_info(dfcyc, bit_pos * 2, g_iwm.last_rd_bit*2,\n\t\t\t\t\t\t\t\t0x200ec);\n\t\t} else {\n\t\t\tmsb_bit = sync_dval & 0xff;\n\t\t\tif(diff <= msb_bit) {\n\t\t\t\t// Handle case of 10-bit nibble which we've\n\t\t\t\t//  already returned...don't return it again!\n\t\t\t\tmsb_bit = 1;\n\t\t\t}\n\t\t}\n#if 0\n\t\tprintf(\"Latch mode diff:%d, msb_bit:%d, new_msb:%d\\n\",\n\t\t\t\t\tdiff, msb_bit, (int)(sync_dval & 0xff));\n#endif\n\t\tdbg_log_info(dfcyc, ((word32)diff << 12) | (msb_bit & 0xff),\n\t\t\t\t\t\t(word32)sync_dval, 0x100ec);\n\t} else {\n\t\tif((sync_dval & 0xff) <= 1) {\n\t\t\t// The LSbit or the next one is a sync bit--but we need\n\t\t\t//  to ignore it.  The LSbit is not valid yet, and\n\t\t\t//  the next bit being the MSB of the next nibble will\n\t\t\t//  be ignored to make the 7usec hold time of the\n\t\t\t//  previous nibble work\n\t\t\tsync_dval = sync_dval >> 8;\n\t\t\tmsb_bit = sync_dval & 0xff;\n\t\t}\n\t}\n\tval = (word32)dval;\n\tif(msb_bit < 8) {\n\t\t// We only have a partial byte\n\t\tval = val & ((2ULL << msb_bit) - 1);\n\t} else if(msb_bit < 63) {\n\t\tval = (word32)(dval >> (msb_bit - 8));\n\t}\n\n\t// val is now valid from bit 8 down to bit 1\n\t// We've allowed in to dval an extra bit which we must toss\n\tval = val >> 1;\n\n\tg_iwm.last_rd_bit = bit_pos;\n\tdbg_log_info(dfcyc, (msb_bit << 24) | (fbit_pos >> 8),\n\t\t\t(word32)(dval0 << 8) | (val & 0xff), 0xec);\n#if 0\n\tif(forced_bits >= 0) {\n\t\tprintf(\"Forced bits, msb:%d, val:%02x, dval:%016llx b_p:%06x\\n\",\n\t\t\tmsb_bit, val, dval, bit_pos * 2);\n\t}\n#endif\n\treturn val & 0xff;\n}\n\nvoid\niwm_write_data(Disk *dsk, word32 val, dword64 dfcyc)\n{\n\tTrk\t*trk;\n\tword32\ttrack_bits, bit_pos, fbit_pos, bit_last, tmp_val;\n\tint\tbits;\n\n\ttrk = dsk->cur_trk_ptr;\n\tif((trk == 0) || dsk->write_prot) {\n\t\treturn;\n\t}\n\ttrack_bits = dsk->cur_track_bits;\n\n\tbit_pos = (dsk->cur_fbit_pos + 511) >> 9;\n\tif(track_bits && (bit_pos >= track_bits)) {\n\t\tbit_pos = bit_pos - track_bits;\n\t\tif(bit_pos >= track_bits) {\n\t\t\tbit_pos = bit_pos % track_bits;\n\t\t}\n\t}\n#if 0\n\tprintf(\"iwm_write_data: %02x %016llx, bit*2:%06x\\n\", val, dfcyc,\n\t\t\t\t\t\t\t\tbit_pos *2);\n#endif\n\tif(dsk->disk_525) {\n\t\tif(!g_slow_525_emul_wr) {\n\t\t\tg_slow_525_emul_wr = 1;\n\t\t\tengine_recalc_events();\n\t\t\tif(track_bits && g_fast_disk_emul) {\n\t\t\t\tfbit_pos = iwm_fastemul_start_write(dsk, dfcyc);\n\t\t\t\tbit_pos = fbit_pos >> 9;\n\t\t\t}\n\t\t}\n\t}\n\tdsk->cur_fbit_pos = bit_pos * 512;\n\tif(g_iwm.num_active_writes == 0) {\n\t\t// No write was pending, enter write mode now\n\t\t// printf(\"Starting write of data to the track, track now:\\n\");\n\t\t// iwm_show_track(-1, -1, dfcyc);\n\t\t// printf(\"Write data to track at bit*2:%06x\\n\", bit_pos);\n\t\tiwm_start_write(dsk, bit_pos, val, 0);\n\t\treturn;\n\t}\n\tif(track_bits == 0) {\n\t\thalt_printf(\"Impossible: track_bits: 0\\n\");\n\t\treturn;\n\t}\n\tif(g_iwm.state & 2) {\t\t// async handshake mode, 3.5\"\n\t\tiwm_write_data35(dsk, val, dfcyc);\n\t\treturn;\n\t}\n\n\t// From here on, it's 5.25\" disks only\n\tbit_last = g_iwm.wr_last_bit[0];\n\tbits = iwm_calc_bit_diff(bit_pos, bit_last, track_bits);\n\tif(bits >= 500) {\n\t\thalt_printf(\"bits are %d. bit*2:%06x, bit_last*2:%06x\\n\",\n\t\t\t\tbits, bit_pos * 2, bit_last * 2);\n\t\tbits = 40;\n\t}\n\tiwm_write_one_nib(dsk, bits, dfcyc);\n\n\ttmp_val = g_iwm.write_val;\n\tdbg_log_info(dfcyc, (g_iwm.wr_last_bit[0] << 25) | (bit_last * 2),\n\t\t(bits << 16) | ((val & 0xff) << 8) | (tmp_val & 0xff), 0xed);\n\tg_iwm.write_val = val;\n}\n\nvoid\niwm_start_write(Disk *dsk, word32 bit_pos, word32 val, int no_prior)\n{\n\tint\tnum;\n\n\tg_iwm.write_val = val;\n\tnum = 0;\n\tnum = iwm_start_write_act(dsk, bit_pos, num, no_prior, 0);\n\tnum = iwm_start_write_act(dsk, bit_pos, num, no_prior, -1);\t// -0.25\n\tnum = iwm_start_write_act(dsk, bit_pos, num, no_prior, +1);\t// +0.25\n\tnum = iwm_start_write_act(dsk, bit_pos, num, no_prior, -2);\t// -0.50\n\tnum = iwm_start_write_act(dsk, bit_pos, num, no_prior, +2);\t// +0.50\n\tg_iwm.num_active_writes = num;\n\n\twoz_maybe_reparse(dsk);\n}\n\nint\niwm_start_write_act(Disk *dsk, word32 bit_pos, int num, int no_prior, int delta)\n{\n\tTrk\t*trkptr;\n\tword32\tqtr_track, track_bits, bit_diff, prior_sum, allow_diff;\n\n\tqtr_track = (dsk->cur_frac_track + 0x8000) >> 16;\n\tqtr_track += delta;\n\tif(qtr_track >= 160) {\t\t\t// Could be unsigned wrap around\n\t\tif(delta == 0) {\n\t\t\thalt_printf(\"iwm_start_write_act0, qtr_track:%04x\\n\",\n\t\t\t\t\t\t\t\tqtr_track);\n\t\t\tg_iwm.num_active_writes = 0;\n\t\t}\n\t\treturn num;\n\t}\n\tif(!dsk->disk_525 && delta) {\n\t\treturn num;\t\t// 3.5\" does not affect adjacent trks\n\t}\n\n\ttrkptr = &(dsk->trks[qtr_track]);\n\ttrack_bits = trkptr->track_bits;\n\tif(track_bits == 0) {\n\t\tif((delta == -2) || (delta == 2)) {\n\t\t\treturn num;\t\t// Nothing to do\n\t\t}\n\t\tif((delta == -1) && (qtr_track > 0)) {\n\t\t\tif(trkptr[-1].track_bits == 0) {\n\t\t\t\treturn num;\t// Nothing to do\n\t\t\t}\n\t\t}\n\t\tif((delta == 1) && (qtr_track < 159)) {\n\t\t\tif(trkptr[1].track_bits == 0) {\n\t\t\t\treturn num;\t// Nothing to do\n\t\t\t}\n\t\t}\n\t\t// Otherwise, we need to create this track and write to it\n\t\ttrack_bits = woz_add_a_track(dsk, qtr_track);\n\t}\n\tif(track_bits) {\n\t\tbit_pos = bit_pos % track_bits;\n\t}\n\tbit_diff = iwm_calc_bit_diff(bit_pos, g_iwm.wr_last_bit[num],\n\t\t\t\t\t\t\t\ttrack_bits);\n\tprior_sum = 0;\n\tallow_diff = 16 + (16 * g_fast_disk_emul);\n\tif((g_iwm.wr_qtr_track[num] == qtr_track) && (bit_diff < allow_diff)) {\n\t\t// consider this write a continuation of the previous write\n\t\tprior_sum = g_iwm.wr_prior_num_bits[num] +\n\t\t\t\t\tg_iwm.wr_num_bits[num] + bit_diff;\n#if 0\n\t\tprintf(\"prior_sum is %d, qtr_track:%04x, bit_diff:%d\\n\",\n\t\t\tprior_sum, qtr_track, bit_diff);\n#endif\n\t} else {\n#if 0\n\t\tprintf(\"No prior_sum, qtr_track:%04x vs %04x, bit_diff:%08x, \"\n\t\t\t\"bit_pos:%05x wr_last_bit:%05x\\n\",\n\t\t\tg_iwm.wr_qtr_track[num], qtr_track, bit_diff,\n\t\t\tbit_pos * 2, g_iwm.wr_last_bit[num]*2);\n#endif\n\t}\n\tif(no_prior) {\n\t\tprior_sum = 0;\n\t}\n\tif(delta == 0) {\n\t\tdsk->cur_fbit_pos = bit_pos << 9;\n\t\tiwm_move_to_qtr_track(dsk, qtr_track);\n\t}\n\tg_iwm.wr_last_bit[num] = bit_pos;\n\tg_iwm.wr_qtr_track[num] = qtr_track;\n\tg_iwm.wr_num_bits[num] = 0;\n\tg_iwm.wr_prior_num_bits[num] = prior_sum;\n\tg_iwm.wr_delta[num] = abs(delta);\t\t// 0, 1 or 2\n\treturn num + 1;\n}\n\nvoid\niwm_write_data35(Disk *dsk, word32 val, dword64 dfcyc)\n{\n\t// Just always write 8 bits to the track\n\tiwm_write_one_nib(dsk, 8, dfcyc);\n\tg_iwm.write_val = val;\n\tdsk->cur_fbit_pos = g_iwm.wr_last_bit[0] * 512;\n}\n\nvoid\niwm_write_end(Disk *dsk, int write_through_now, dword64 dfcyc)\n{\n\tTrk\t*trkptr;\n\tword32\tlast_bit, qtr_track, num_bits, bit_start, delta, track_bits;\n\tword32\tprior_sum;\n\tint\tnum_active_writes;\n\tint\ti;\n\n\t// Flush out previous write, then turn writing off\n\tnum_active_writes = g_iwm.num_active_writes;\n#if 0\n\tprintf(\"In iwm_write_end at %016llx, num:%d, %d\\n\", dfcyc,\n\t\t\t\t\tnum_active_writes, dsk->disk_dirty);\n#endif\n\tif(num_active_writes == 0) {\n\t\treturn;\t\t\t// Invalid, not in a write\n\t}\n\tif(write_through_now) {\n\t\tiwm_write_data(dsk, 0, dfcyc);\n\t}\n\n\tfor(i = 0; i < num_active_writes; i++) {\n\t\tlast_bit = g_iwm.wr_last_bit[i];\n\t\tqtr_track = g_iwm.wr_qtr_track[i];\n\t\tnum_bits = g_iwm.wr_num_bits[i];\n\t\tdelta = g_iwm.wr_delta[i];\n#if 0\n\t\tprintf(\" end %d, last_bit:%05x, qtrk:%04x, num_b:%d, \"\n\t\t\t\"delta:%d\\n\", i, last_bit * 2, qtr_track, num_bits,\n\t\t\tdelta);\n#endif\n\t\tif((num_bits == 0) || (qtr_track >= 160)) {\n\t\t\tcontinue;\n\t\t}\n\t\ttrkptr = &(dsk->trks[qtr_track]);\n\t\ttrack_bits = trkptr->track_bits;\n\t\tprior_sum = g_iwm.wr_prior_num_bits[i];\n\t\tif((num_bits + prior_sum) >= track_bits) {\n\t\t\t// Full track write.  If delta != 0, erase this track\n\t\t\tprintf(\"Full track write at qtrk:%04x %016llx\\n\",\n\t\t\t\t\t\t\tqtr_track, dfcyc);\n#if 0\n\t\t\tif(qtr_track == 4) {\n\t\t\t\thalt_printf(\"Full track at qtr_trk:4\\n\");\n\t\t\t}\n#endif\n\t\t\tif(delta != 0) {\n\t\t\t\twoz_remove_a_track(dsk, qtr_track);\n\t\t\t\tprintf(\"TRACK %04x REMOVED\\n\", qtr_track);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tg_iwm.wr_prior_num_bits[i] = 0;\n\t\t}\n\n\t\t// Otherwise, recalc sync for this track\n\t\tif(num_bits >= track_bits) {\n\t\t\tnum_bits = num_bits % track_bits;\n\t\t}\n\t\tbit_start = iwm_calc_bit_sum(last_bit, -(int)num_bits - 24,\n\t\t\t\t\t\t\t\ttrack_bits);\n\t\tiwm_recalc_sync_from(dsk, qtr_track, bit_start, dfcyc);\n#if 0\n\t\tprintf(\"Wrote %d bits to qtrk:%04x at %05x %016llx, i:%d,%d, \"\n\t\t\t\"%d\\n\", num_bits, qtr_track, bit_start*2, dfcyc, i,\n\t\t\tnum_active_writes, dsk->disk_dirty);\n#endif\n\t}\n\tg_iwm.num_active_writes = 0;\n\n\twoz_maybe_reparse(dsk);\n}\n\nvoid\niwm_write_one_nib(Disk *dsk, int bits, dword64 dfcyc)\n{\n\tword32\tqtr_track, bit_pos, delta, val, track_bits;\n\tint\tnum;\n\tint\ti;\n\n\tnum = g_iwm.num_active_writes;\n\tval = g_iwm.write_val;\n\tfor(i = 0; i < num; i++) {\n\t\tqtr_track = g_iwm.wr_qtr_track[i];\n\t\tbit_pos = g_iwm.wr_last_bit[i];\n\t\tdelta = g_iwm.wr_delta[i];\n\t\tdbg_log_info(dfcyc, val, bit_pos * 2, 0x200ed);\n\t\tif(delta == 2) {\t\t// Trk +0.50 and -0.50: corrupt\n\t\t\tval = (val & 0x7f) ^ i ^ 0x0c;\n\t\t}\n\t\tbit_pos = disk_nib_out_raw(dsk, qtr_track, val, bits, bit_pos,\n\t\t\t\t\t\t\t\t\tdfcyc);\n\t\ttrack_bits = dsk->trks[qtr_track].track_bits;\n\t\tif(bit_pos >= track_bits) {\n\t\t\tbit_pos = bit_pos - track_bits;\n\t\t}\n\t\tg_iwm.wr_last_bit[i] = bit_pos;\n\t\tg_iwm.wr_num_bits[i] += bits;\n\t\tdbg_log_info(dfcyc, (bits << 24) | (bit_pos * 2),\n\t\t\t2*iwm_calc_bit_sum(bit_pos, 0-g_iwm.wr_num_bits[i],\n\t\t\t\t\t\t\ttrack_bits), 0x300ed);\n\t}\n}\n\nvoid\niwm_recalc_sync_from(Disk *dsk, word32 qtr_track, word32 bit_pos, dword64 dfcyc)\n{\n\tTrk\t*trkptr;\n\tbyte\t*bptr, *sync_ptr;\n\tword32\ttrack_bits, val, this_sync, sync0;\n\tint\tpos, next_pos, wrap, bit, last_bit, last_byte, match, this_bits;\n\n\t// We are called with a bit_pos 3 bytes before the desired byte.\n\t//  Look at pos, pos-1, pos+1 in a safe way, and find a valid sync_num\n\tif(dfcyc) {\n\t\t//printf(\"new sync from %d, %016llx %p\\n\", bit_pos, dfcyc, dsk);\n\t}\n\tif(qtr_track >= 160) {\n\t\thalt_printf(\"iwm_recalc_sync_from bad qtr:%04x bit_pos:%06x\\n\",\n\t\t\t\t\t\t\tqtr_track, bit_pos);\n\t\treturn;\n\t}\n\ttrkptr = &(dsk->trks[qtr_track]);\n\n\ttrack_bits = trkptr->track_bits;\n\tif(track_bits == 0) {\n\t\thalt_printf(\"iwm_recalc_sync_from track_bits 0 for %04x\\n\",\n\t\t\tqtr_track);\n\t\treturn;\n\t}\n\tlast_byte = (track_bits - 1) >> 3;\n\tlast_bit = ((track_bits - 1) & 7) + 1;\t\t// 1...8\n\n\tsync_ptr = &(trkptr->sync_ptr[0]);\n\tbptr = &(trkptr->raw_bptr[0]);\n\tpos = bit_pos >> 3;\n\tbit = bit_pos & 7;\t// 0...7\n\tsync0 = sync_ptr[pos];\n\tif(sync0 >= 8) {\n\t\tif(pos > 0) {\n\t\t\tpos--;\n\t\t} else {\n\t\t\tpos++;\t\t// pos is now 1\n\t\t}\n\t\tsync0 = sync_ptr[pos];\n\t}\n\tbit = (7 - sync0) & 7;\n\tmatch = 0;\n\twrap = 0;\n\tnext_pos = pos + 1;\n\t// cnt = 0;\n\t// printf(\"recalc_sync_from pos:%04x, bit:%d\\n\", pos, bit);\n\twhile(1) {\n\t\tval = bptr[pos];\n\t\tthis_bits = 8;\n\t\tthis_sync = sync_ptr[pos];\n\t\tsync_ptr[pos] = 0x20;\n\t\tnext_pos = pos + 1;\n\t\tif(pos >= last_byte) {\n#if 0\n\t\t\tprintf(\"At last_byte, val:%02x, pos:%04x, last_bit:%d, \"\n\t\t\t\t\"bit:%d\\n\", val, pos, last_bit, bit);\n#endif\n\t\t\tthis_bits = last_bit;\n\t\t\tval = val & (0xff00 >> last_bit);\n\t\t\tnext_pos = 0;\n\t\t\twrap++;\n\t\t\tif(wrap >= 3) {\n\t\t\t\thalt_printf(\"no stable sync found\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif(bit >= this_bits) {\n\t\t\t\t// Skip over this partial byte to byte 0\n\t\t\t\tbit -= this_bits;\n\t\t\t\tpos = 0;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n#if 0\n\t\tif(wrap || (pos >= 0x2630)) {\n\t\t\tprintf(\"pos:%04x, bit:%d, val:%04x, this_bits:%d\\n\",\n\t\t\t\tpos, bit, val, this_bits);\n\t\t}\n#endif\n#if 0\n\t\tif((cnt++ < 10) && (dsk->cur_frac_track == 0)) {\n\t\t\tprintf(\"sync[%04x]=%02x, val:%02x bit:%d, this_b:%d\\n\",\n\t\t\t\tpos, this_sync, val, bit, this_bits);\n\t\t}\n#endif\n\t\t// bit is within this byte.  Find next set bit\n\t\twhile(bit < this_bits) {\n\t\t\tif(((val << bit) & 0x80) == 0) {\n\t\t\t\t// Slide to next bit\n\t\t\t\tbit++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tsync_ptr[pos] = 7 - bit;\n\t\t\tif(this_sync == (word32)(7 - bit)) {\n\t\t\t\tmatch++;\n\t\t\t\tif(match >= 7) {\n#if 0\n\t\t\t\t\tprintf(\"match %d at pos:%04x wrap:%d\\n\",\n\t\t\t\t\t\tmatch, pos, wrap);\n#endif\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tmatch = 0;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tbit = (bit + 8 - this_bits) & 7;\n\t\tpos = next_pos;\n\t}\n}\n\n/* c600 */\nvoid\nsector_to_partial_nib(byte *in, byte *nib_ptr)\n{\n\tbyte\t*aux_buf, *nib_out;\n\tword32\tval, val2;\n\tint\tx;\n\tint\ti;\n\n\t/* Convert 256(+1) data bytes to 342+1 disk nibbles */\n\n\taux_buf = nib_ptr;\n\tnib_out = nib_ptr + 0x56;\n\n\tfor(i = 0; i < 0x56; i++) {\n\t\taux_buf[i] = 0;\n\t}\n\n\tx = 0x55;\n\tfor(i = 0x101; i >= 0; i--) {\n\t\tval = in[i];\n\t\tif(i >= 0x100) {\n\t\t\tval = 0;\n\t\t}\n\t\tval2 = (aux_buf[x] << 1) + (val & 1);\n\t\tval = val >> 1;\n\t\tval2 = (val2 << 1) + (val & 1);\n\t\tval = val >> 1;\n\t\tnib_out[i] = val;\n\t\taux_buf[x] = val2;\n\t\tx--;\n\t\tif(x < 0) {\n\t\t\tx = 0x55;\n\t\t}\n\t}\n}\n\n\nint\ndisk_unnib_4x4(Disk *dsk)\n{\n\tint\tval1;\n\tint\tval2;\n\n\tval1 = iwm_read_data_fast(dsk, 0);\n\tval2 = iwm_read_data_fast(dsk, 0);\n\n\treturn ((val1 << 1) + 1) & val2;\n}\n\nint\niwm_denib_track525(Disk *dsk, word32 qtr_track, byte *outbuf)\n{\n\tbyte\taux_buf[0x80];\n\tint\tsector_done[16];\n\tbyte\t*buf;\n\tword32\tval, val2, prev_val, save_frac_track;\n\tint\ttrack_len, vol, track, phys_sec, log_sec, cksum, x, my_nib_cnt;\n\tint\tsave_fbit_pos, tmp_fbit_pos, status, ret, num_sectors_done;\n\tint\ti;\n\n\t//printf(\"iwm_denib_track525\\n\");\n\n\tsave_fbit_pos = dsk->cur_fbit_pos;\n\tsave_frac_track = dsk->cur_frac_track;\n\tiwm_move_to_qtr_track(dsk, qtr_track);\n\n\tdsk->cur_fbit_pos = 0;\n\tg_fast_disk_unnib = 1;\n\n\ttrack_len = (dsk->cur_track_bits + 7) >> 3;\n\n\tfor(i = 0; i < 16; i++) {\n\t\tsector_done[i] = 0;\n\t}\n\n\tnum_sectors_done = 0;\n\n\tval = 0;\n\tstatus = -1;\n\tmy_nib_cnt = 0;\n\twhile(my_nib_cnt++ < 2*track_len) {\n\t\t/* look for start of a sector */\n\t\tif(val != 0xd5) {\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tcontinue;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xaa) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0x96) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* It's a sector start */\n\t\tvol = disk_unnib_4x4(dsk);\n\t\ttrack = disk_unnib_4x4(dsk);\n\t\tphys_sec = disk_unnib_4x4(dsk);\n\t\tif(phys_sec < 0 || phys_sec > 15) {\n\t\t\tprintf(\"Track %02x, read sec as %02x\\n\",\n\t\t\t\t\t\tqtr_track >> 2, phys_sec);\n\t\t\tbreak;\n\t\t}\n\t\tif(dsk->image_type == DSK_TYPE_DOS33) {\n\t\t\tlog_sec = phys_to_dos_sec[phys_sec];\n\t\t} else {\n\t\t\tlog_sec = phys_to_prodos_sec[phys_sec];\n\t\t}\n\t\tcksum = disk_unnib_4x4(dsk);\n\t\tif((vol ^ track ^ phys_sec ^ cksum) != 0) {\n\t\t\t/* not correct format */\n\t\t\tprintf(\"Track %02x not DOS 3.3 since hdr cksum, %02x \"\n\t\t\t\t\"%02x %02x %02x\\n\", qtr_track >> 2,\n\t\t\t\tvol, track, phys_sec, cksum);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* see what sector it is */\n\t\tif(track != (qtr_track >> 2) || (phys_sec < 0) ||\n\t\t\t\t\t\t\t(phys_sec > 15)) {\n\t\t\tprintf(\"Track %02x bad since track: %02x, sec: %02x\\n\",\n\t\t\t\tqtr_track>>2, track, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\tif(sector_done[phys_sec]) {\n\t\t\tprintf(\"Already done sector %02x on track %02x!\\n\",\n\t\t\t\tphys_sec, qtr_track>>2);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* So far so good, let's do it! */\n\t\tval = 0;\n\t\ti = 0;\n\t\twhile(i < NIBS_FROM_ADDR_TO_DATA) {\n\t\t\ti++;\n\t\t\tif(val != 0xd5) {\n\t\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tif(val != 0xaa) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tif(val != 0xad) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* got it, just break */\n\t\t\tbreak;\n\t\t}\n\n\t\tif(i >= NIBS_FROM_ADDR_TO_DATA) {\n\t\t\tprintf(\"No data header, track %02x, sec %02x at %07x\\n\",\n\t\t\t\tqtr_track>>2, phys_sec, dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\tbuf = outbuf + 0x100*log_sec;\n\n\t\t/* Data start! */\n\t\tprev_val = 0;\n\t\tfor(i = 0x55; i >= 0; i--) {\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tval2 = g_from_disk_byte[val];\n\t\t\tif(val2 >= 0x100) {\n\t\t\t\tprintf(\"Bad data area1, val:%02x,val2:%03x\\n\",\n\t\t\t\t\t\t\t\tval, val2);\n\t\t\t\tprintf(\" i:%03x, fbit_pos:%08x\\n\", i,\n\t\t\t\t\t\t\tdsk->cur_fbit_pos);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tprev_val = val2 ^ prev_val;\n\t\t\taux_buf[i] = prev_val;\n\t\t}\n\n\t\t/* rest of data area */\n\t\tfor(i = 0; i < 0x100; i++) {\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tval2 = g_from_disk_byte[val];\n\t\t\tif(val2 >= 0x100) {\n\t\t\t\tprintf(\"Bad data area2, read: %02x\\n\", val);\n\t\t\t\tprintf(\"  fbit_pos: %07x\\n\", dsk->cur_fbit_pos);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tprev_val = val2 ^ prev_val;\n\t\t\tbuf[i] = prev_val;\n\t\t}\n\n\t\t/* checksum */\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tval2 = g_from_disk_byte[val];\n\t\tif(val2 >= 0x100) {\n\t\t\tprintf(\"Bad data area3, read: %02x\\n\", val);\n\t\t\tprintf(\"  fbit_pos: %07x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\t\tif(val2 != prev_val) {\n\t\t\tprintf(\"Bad data cksum, got %02x, wanted: %02x\\n\",\n\t\t\t\tval2, prev_val);\n\t\t\tprintf(\"  fbit_pos: %07x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xde) {\n\t\t\tprintf(\"No 0xde at end of sector data:%02x\\n\", val);\n\t\t\tprintf(\"  fbit_pos: %07x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xaa) {\n\t\t\tprintf(\"No 0xde,0xaa at end of sector:%02x\\n\", val);\n\t\t\tprintf(\"  fbit_pos: %07x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Got this far, data is good, merge aux_buf into buf */\n\t\tx = 0x55;\n\t\tfor(i = 0; i < 0x100; i++) {\n\t\t\tval = aux_buf[x];\n\t\t\tval2 = (buf[i] << 1) + (val & 1);\n\t\t\tval = val >> 1;\n\t\t\tval2 = (val2 << 1) + (val & 1);\n\t\t\tbuf[i] = val2;\n\t\t\tval = val >> 1;\n\t\t\taux_buf[x] = val;\n\t\t\tx--;\n\t\t\tif(x < 0) {\n\t\t\t\tx = 0x55;\n\t\t\t}\n\t\t}\n\t\tsector_done[phys_sec] = 1;\n\t\tnum_sectors_done++;\n\t\tif(num_sectors_done >= 16) {\n\t\t\tstatus = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\ttmp_fbit_pos = dsk->cur_fbit_pos;\n\tg_fast_disk_unnib = 0;\n\n\tret = 0;\n\tif(status != 0) {\n\t\tprintf(\"Nibblization not done, %02x sectors found qtrk %04x, \"\n\t\t\t\"drive:%d, slot:%d\\n\", num_sectors_done, qtr_track,\n\t\t\tdsk->drive, dsk->disk_525 + 5);\n\t\tprintf(\"my_nib_cnt: %05x, fbit_pos:%07x, trk_len:%05x\\n\",\n\t\t\tmy_nib_cnt, tmp_fbit_pos, track_len);\n\t\tret = 16;\n\t\tfor(i = 0; i < 16; i++) {\n\t\t\tprintf(\"sector_done[%d] = %d\\n\", i, sector_done[i]);\n\t\t\tif(sector_done[i]) {\n\t\t\t\tret--;\n\t\t\t}\n\t\t}\n\t\tiwm_show_a_track(dsk, dsk->cur_trk_ptr, 0);\n\t\tif(!ret) {\n\t\t\tret = -1;\n\t\t}\n\t} else {\n\t\t//printf(\"iwm_denib_track525 succeeded\\n\");\n\t}\n\n\tdsk->cur_fbit_pos = save_fbit_pos;\n\tiwm_move_to_ftrack(dsk, save_frac_track, 0, 0);\n\n\treturn ret;\n}\n\nint\niwm_denib_track35(Disk *dsk, word32 qtr_track, byte *outbuf)\n{\n\tword32\tbuf_c00[0x100];\n\tword32\tbuf_d00[0x100];\n\tword32\tbuf_e00[0x100];\n\tint\tsector_done[16];\n\tbyte\t*buf;\n\tword32\ttmp_5c, tmp_5d, tmp_5e, tmp_66, tmp_67, val, val2;\n\tword32\tsave_frac_track;\n\tint\tnum_sectors_done, track_len, phys_track, phys_sec, phys_side;\n\tint\tphys_capacity, cksum, tmp, track, side, num_sectors, x, y;\n\tint\tcarry, my_nib_cnt, save_fbit_pos, status, ret;\n\tint\ti;\n\n\tsave_fbit_pos = dsk->cur_fbit_pos;\n\tsave_frac_track = dsk->cur_frac_track;\n\tiwm_move_to_qtr_track(dsk, qtr_track);\n\n\tif(dsk->cur_trk_ptr == 0) {\n\t\treturn 0;\n\t}\n\n\tdsk->cur_fbit_pos = 0;\n\tg_fast_disk_unnib = 1;\n\n\ttrack_len = dsk->cur_track_bits >> 3;\n\n\tnum_sectors = g_track_bytes_35[qtr_track >> 5] >> 9;\n\n\tfor(i = 0; i < num_sectors; i++) {\n\t\tsector_done[i] = 0;\n\t}\n\n\tnum_sectors_done = 0;\n\n\tval = 0;\n\tstatus = -1;\n\tmy_nib_cnt = 0;\n\n\ttrack = qtr_track >> 1;\n\tside = qtr_track & 1;\n\n\twhile(my_nib_cnt++ < 2*track_len) {\n\t\t/* look for start of a sector */\n\t\tif(val != 0xd5) {\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tcontinue;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xaa) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0x96) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* It's a sector start */\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tphys_track = g_from_disk_byte[val];\n\t\tif(phys_track != (track & 0x3f)) {\n\t\t\tprintf(\"Track %02x.%d, read track %02x, %02x\\n\",\n\t\t\t\ttrack, side, phys_track, val);\n\t\t\tbreak;\n\t\t}\n\n\t\tphys_sec = g_from_disk_byte[iwm_read_data_fast(dsk, 0)];\n\t\tif((phys_sec < 0) || (phys_sec >= num_sectors)) {\n\t\t\tprintf(\"Track %02x.%d, read sector %02x??\\n\",\n\t\t\t\ttrack, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\t\tphys_side = g_from_disk_byte[iwm_read_data_fast(dsk, 0)];\n\n\t\tif(phys_side != ((side << 5) + (track >> 6))) {\n\t\t\tprintf(\"Track %02x.%d, read side %02x??\\n\",\n\t\t\t\ttrack, side, phys_side);\n\t\t\tbreak;\n\t\t}\n\t\tphys_capacity = g_from_disk_byte[iwm_read_data_fast(dsk, 0)];\n\t\tif(phys_capacity != 0x24 && phys_capacity != 0x22) {\n\t\t\tprintf(\"Track %02x.%x capacity: %02x != 0x24/22\\n\",\n\t\t\t\ttrack, side, phys_capacity);\n\t\t}\n\t\tcksum = g_from_disk_byte[iwm_read_data_fast(dsk, 0)];\n\n\t\ttmp = phys_track ^ phys_sec ^ phys_side ^ phys_capacity;\n\t\tif(cksum != tmp) {\n\t\t\tprintf(\"Track %02x.%d, sector %02x, cksum: %02x.%02x\\n\",\n\t\t\t\ttrack, side, phys_sec, cksum, tmp);\n\t\t\tbreak;\n\t\t}\n\n\n\t\tif(sector_done[phys_sec]) {\n\t\t\tprintf(\"Already done sector %02x on track %02x.%x!\\n\",\n\t\t\t\tphys_sec, track, side);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* So far so good, let's do it! */\n\t\tval = 0;\n\t\tfor(i = 0; i < 38; i++) {\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tif(val == 0xd5) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(val != 0xd5) {\n\t\t\tprintf(\"No data header, track %02x.%x, sec %02x\\n\",\n\t\t\t\ttrack, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xaa) {\n\t\t\tprintf(\"Bad data hdr1,val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tprintf(\"fbit_pos: %08x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xad) {\n\t\t\tprintf(\"Bad data hdr2,val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tprintf(\"dsk->cur_fbit_pos:%07x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\tbuf = outbuf + (phys_sec << 9);\n\n\t\t/* check sector again */\n\t\ttmp = g_from_disk_byte[iwm_read_data_fast(dsk, 0)];\n\t\tif(tmp != phys_sec) {\n\t\t\tprintf(\"Bad data hdr3,val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Data start! */\n\t\ttmp_5c = 0;\n\t\ttmp_5d = 0;\n\t\ttmp_5e = 0;\n\t\ty = 0xaf;\n\t\tcarry = 0;\n\n\t\twhile(y > 0) {\n/* 626f */\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tval2 = g_from_disk_byte[val];\n\t\t\tif(val2 >= 0x100) {\n\t\t\t\tprintf(\"Bad data area1b, read: %02x\\n\", val);\n\t\t\t\tprintf(\" i:%03x, fbit_pos:%07x\\n\", i,\n\t\t\t\t\t\t\tdsk->cur_fbit_pos);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttmp_66 = val2;\n\n\t\t\ttmp_5c = tmp_5c << 1;\n\t\t\tcarry = (tmp_5c >> 8);\n\t\t\ttmp_5c = (tmp_5c + carry) & 0xff;\n\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tval2 = g_from_disk_byte[val];\n\t\t\tif(val2 >= 0x100) {\n\t\t\t\tprintf(\"Bad data area2, read: %02x\\n\", val);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tval2 = val2 + ((tmp_66 << 2) & 0xc0);\n\n\t\t\tval2 = val2 ^ tmp_5c;\n\t\t\tbuf_c00[y] = val2;\n\n\t\t\ttmp_5e = val2 + tmp_5e + carry;\n\t\t\tcarry = (tmp_5e >> 8);\n\t\t\ttmp_5e = tmp_5e & 0xff;\n/* 62b8 */\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tval2 = g_from_disk_byte[val];\n\t\t\tval2 = val2 + ((tmp_66 << 4) & 0xc0);\n\t\t\tval2 = val2 ^ tmp_5e;\n\t\t\tbuf_d00[y] = val2;\n\t\t\ttmp_5d = val2 + tmp_5d + carry;\n\n\t\t\tcarry = (tmp_5d >> 8);\n\t\t\ttmp_5d = tmp_5d & 0xff;\n\n\t\t\ty--;\n\t\t\tif(y <= 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\n/* 6274 */\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tval2 = g_from_disk_byte[val];\n\t\t\tval2 = val2 + ((tmp_66 << 6) & 0xc0);\n\t\t\tval2 = val2 ^ tmp_5d;\n\t\t\tbuf_e00[y+1] = val2;\n\n\t\t\ttmp_5c = val2 + tmp_5c + carry;\n\t\t\tcarry = (tmp_5c >> 8);\n\t\t\ttmp_5c = tmp_5c & 0xff;\n\t\t}\n\n/* 62d0 */\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tval2 = g_from_disk_byte[val];\n\n\t\ttmp_66 = (val2 << 6) & 0xc0;\n\t\ttmp_67 = (val2 << 4) & 0xc0;\n\t\tval2 = (val2 << 2) & 0xc0;\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tval2 = g_from_disk_byte[val] + val2;\n\t\tif(tmp_5e != (word32)val2) {\n\t\t\tprintf(\"Checksum 5e bad: %02x vs %02x\\n\", tmp_5e, val2);\n\t\t\tprintf(\"val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tval2 = g_from_disk_byte[val] + tmp_67;\n\t\tif(tmp_5d != (word32)val2) {\n\t\t\tprintf(\"Checksum 5d bad: %02x vs %02x\\n\", tmp_5e, val2);\n\t\t\tprintf(\"val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tval2 = g_from_disk_byte[val] + tmp_66;\n\t\tif(tmp_5c != (word32)val2) {\n\t\t\tprintf(\"Checksum 5c bad: %02x vs %02x\\n\", tmp_5e, val2);\n\t\t\tprintf(\"val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Whew, got it!...check for DE AA */\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xde) {\n\t\t\tprintf(\"Bad data epi1,val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tprintf(\"fbit_pos: %08x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xaa) {\n\t\t\tprintf(\"Bad data epi2,val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Now, convert buf_c/d/e to output */\n/* 6459 */\n\t\ty = 0;\n\t\tfor(x = 0xab; x >= 0; x--) {\n\t\t\t*buf++ = buf_c00[x];\n\t\t\ty++;\n\t\t\tif(y >= 0x200) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t*buf++ = buf_d00[x];\n\t\t\ty++;\n\t\t\tif(y >= 0x200) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t*buf++ = buf_e00[x];\n\t\t\ty++;\n\t\t\tif(y >= 0x200) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tsector_done[phys_sec] = 1;\n\t\tnum_sectors_done++;\n\t\tif(num_sectors_done >= num_sectors) {\n\t\t\tstatus = 0;\n\t\t\tbreak;\n\t\t}\n\t\tval = 0;\n\t}\n\n\tg_fast_disk_unnib = 0;\n\n\tret = 0;\n\tif(status != 0) {\n\t\tprintf(\"dsk->fbit_pos: %07x, status: %d\\n\", dsk->cur_fbit_pos,\n\t\t\t\t\t\t\t\tstatus);\n\t\tfor(i = 0; i < num_sectors; i++) {\n\t\t\tprintf(\"sector done[%d] = %d\\n\", i, sector_done[i]);\n\t\t}\n\t\tprintf(\"Nibblization not done, %02x blocks found qtrk %04x\\n\",\n\t\t\tnum_sectors_done, qtr_track);\n\n\t\tret = -1;\n\t}\n\n\tdsk->cur_fbit_pos = save_fbit_pos;\n\tiwm_move_to_ftrack(dsk, save_frac_track, 0, 0);\n\n\treturn ret;\n\n}\n\n/* ret = 1 -> dirty data written out */\n/* ret = 0 -> not dirty, no error */\n/* ret < 0 -> error */\nint\niwm_track_to_unix(Disk *dsk, word32 qtr_track, byte *outbuf)\n{\n\tTrk\t*trk;\n\tdword64\tdunix_pos, dret, unix_len;\n\tint\tret;\n\n\ttrk = &(dsk->trks[qtr_track]);\n\tif((trk->track_bits == 0) || (trk->dirty == 0)) {\n\t\treturn 0;\n\t}\n\n\tprintf(\"iwm_track_to_unix dirty qtr:%04x, dirty:%d\\n\", qtr_track,\n\t\t\t\t\t\t\ttrk->dirty);\n#if 0\n\tif((qtr_track & 3) && disk_525) {\n\t\thalt_printf(\"You wrote to phase %02x!  Can't wr bk to unix!\\n\",\n\t\t\tqtr_track);\n\t\tdsk->write_through_to_unix = 0;\n\t\treturn -1;\n\t}\n#endif\n\n\tif(dsk->wozinfo_ptr) {\t\t\t\t// WOZ disk\n\t\toutbuf = trk->raw_bptr;\n\t\tret = 0;\n\t} else {\n\t\tif(dsk->disk_525) {\n\t\t\tif(qtr_track & 3) {\n\t\t\t\t// Not a valid track\n\t\t\t\tret = -1;\n\t\t\t} else {\n\t\t\t\tret = iwm_denib_track525(dsk, qtr_track,\n\t\t\t\t\t\t\t\t\toutbuf);\n\t\t\t}\n\t\t} else {\n\t\t\tret = iwm_denib_track35(dsk, qtr_track, outbuf);\n\t\t}\n\t}\n\n\tif(ret != 0) {\n\t\treturn -1;\n\t}\n\n\t/* Write it out */\n\tdunix_pos = trk->dunix_pos;\n\tunix_len = trk->unix_len;\n\tif(unix_len < 0x1000) {\n\t\thalt_printf(\"Disk:%s trk:%06x, dunix_pos:%08llx, len:%08llx\\n\",\n\t\t\tdsk->name_ptr, dsk->cur_frac_track, dunix_pos,\n\t\t\tunix_len);\n\t\treturn -1;\n\t}\n\n\ttrk->dirty = 0;\n\tif(dsk->dynapro_info_ptr) {\n\t\treturn dynapro_write(dsk, outbuf, dunix_pos, (word32)unix_len);\n\t}\n\n\tdret = cfg_write_to_fd(dsk->fd, outbuf, dunix_pos, unix_len);\n#if 0\n\tprintf(\"Write: qtr_trk:%04x, dunix_pos:%08llx, %s\\n\", qtr_track,\n\t\t\tdunix_pos, dsk->name_ptr);\n#endif\n\tif(dret != unix_len) {\n\t\tprintf(\"write: %08llx, errno:%d, trk: %06x, disk: %s\\n\",\n\t\t\tdret, errno, dsk->cur_frac_track, dsk->name_ptr);\n\t\treturn -1;\n\t}\n\tif(dsk->wozinfo_ptr) {\t\t\t\t// WOZ disk\n\t\tprintf(\"Wrote track %07x to fd:%d off:%08llx, len:%07llx\\n\",\n\t\t\tdsk->cur_frac_track, dsk->fd, dunix_pos, unix_len);\n\t\twoz_rewrite_crc(dsk, 0);\n\t}\n\n\treturn 1;\n}\n\nvoid\nshow_hex_data(byte *buf, int count)\n{\n\tint\ti;\n\n\tfor(i = 0; i < count; i += 16) {\n\t\tprintf(\"%04x: %02x %02x %02x %02x %02x %02x %02x %02x \"\n\t\t\t\"%02x %02x %02x %02x %02x %02x %02x %02x\\n\", i,\n\t\t\tbuf[i+0], buf[i+1], buf[i+2], buf[i+3],\n\t\t\tbuf[i+4], buf[i+5], buf[i+6], buf[i+7],\n\t\t\tbuf[i+8], buf[i+9], buf[i+10], buf[i+11],\n\t\t\tbuf[i+12], buf[i+13], buf[i+14], buf[i+15]);\n\t}\n}\n\nvoid\niwm_check_nibblization(dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tint\tslot, drive, sel35;\n\n\tdrive = (g_iwm.state >> IWM_BIT_DRIVE_SEL) & 1;\n\tslot = 6;\n\tif(g_iwm.state & IWM_STATE_MOTOR_ON) {\n\t\tsel35 = (g_iwm.state >> IWM_BIT_C031_APPLE35SEL) & 1;\n\t} else {\n\t\tsel35 = (g_iwm.state >> IWM_BIT_LAST_SEL35) & 1;\n\t}\n\tif(sel35) {\n\t\tdsk = &(g_iwm.drive35[drive]);\n\t\tslot = 5;\n\t} else {\n\t\tdsk = &(g_iwm.drive525[drive]);\n\t}\n\tprintf(\"iwm_check_nibblization, s%d d%d\\n\", slot, drive);\n\tdisk_check_nibblization(dsk, 0, 4096, dfcyc);\n}\n\nvoid\ndisk_check_nibblization(Disk *dsk, byte *in_buf, int size, dword64 dfcyc)\n{\n\tbyte\tbuffer[0x3000];\n\tword32\tqtr_track;\n\tint\tret, ret2;\n\tint\ti;\n\n\tif(size > 0x3000) {\n\t\tprintf(\"size %08x is > 0x3000, disk_check_nibblization\\n\",size);\n\t\texit(3);\n\t}\n\n\tfor(i = 0; i < size; i++) {\n\t\tbuffer[i] = 0;\n\t}\n\n\t//printf(\"About to call iwm_denib_track*, here's the track:\\n\");\n\t//iwm_show_a_track(dsk, dsk->cur_trk_ptr, dfcyc);\n\n\tqtr_track = (word32)(dsk->cur_trk_ptr - &(dsk->trks[0]));\n\tif(qtr_track >= 160) {\n\t\thalt_printf(\"cur_trk_ptr points to bad qtr_track:%08x\\n\",\n\t\t\t\t\t\t\t\tqtr_track);\n\t\treturn;\n\t}\n\tif(dsk->disk_525) {\n\t\tret = iwm_denib_track525(dsk, qtr_track, &(buffer[0]));\n\t} else {\n\t\tret = iwm_denib_track35(dsk, qtr_track, &(buffer[0]));\n\t}\n\n\tret2 = -1;\n\tif(in_buf) {\n\t\tfor(i = 0; i < size; i++) {\n\t\t\tif(buffer[i] != in_buf[i]) {\n\t\t\t\tprintf(\"buffer[%04x]: %02x != %02x\\n\", i,\n\t\t\t\t\t\t\tbuffer[i], in_buf[i]);\n\t\t\t\tret2 = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif((ret != 0) || (ret2 >= 0)) {\n\t\tprintf(\"disk_check_nib ret:%d, ret2:%d for track %06x\\n\",\n\t\t\tret, ret2, dsk->cur_frac_track);\n\t\tif(in_buf) {\n\t\t\tshow_hex_data(in_buf, 0x1000);\n\t\t}\n\t\tshow_hex_data(buffer, size);\n\t\tiwm_show_a_track(dsk, dsk->cur_trk_ptr, dfcyc);\n\t\tif(ret == 16) {\n\t\t\tprintf(\"No sectors found, ignore it\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\thalt_printf(\"Stop\\n\");\n\t\texit(2);\n\t}\n}\n\n#define TRACK_BUF_LEN\t\t0x2000\n\nvoid\ndisk_unix_to_nib(Disk *dsk, int qtr_track, dword64 dunix_pos, word32 unix_len,\n\tint len_bits, dword64 dfcyc)\n{\n\tbyte\ttrack_buf[TRACK_BUF_LEN];\n\tTrk\t*trk;\n\tbyte\t*bptr;\n\tdword64\tdret, dlen;\n\tword32\tnum_bytes, must_clear_track;\n\tint\ti;\n\n\t/* Read track from dsk int track_buf */\n#if 0\n\tprintf(\"disk_unix_to_nib: qtr:%04x, unix_pos:%08llx, unix_len:%08x, \"\n\t\t\"len_bits:%06x\\n\", qtr_track, dunix_pos, unix_len, len_bits);\n#endif\n\n\tmust_clear_track = 0;\n\n\tif(unix_len > TRACK_BUF_LEN) {\n\t\tprintf(\"diks_unix_to_nib: requested len of image %s = %05x\\n\",\n\t\t\tdsk->name_ptr, unix_len);\n\t}\n\n\tbptr = dsk->raw_data;\n\tif(bptr != 0) {\n\t\t// raw_data is valid, so use it\n\t\tif((dunix_pos + unix_len) > dsk->raw_dsize) {\n\t\t\tmust_clear_track = 1;\n\t\t} else {\n\t\t\tbptr += dunix_pos;\n\t\t\tfor(i = 0; i < (int)unix_len; i++) {\n\t\t\t\ttrack_buf[i] = bptr[i];\n\t\t\t}\n\t\t}\n\t} else if(unix_len > 0) {\n\t\tdret = kegs_lseek(dsk->fd, dunix_pos, SEEK_SET);\n\t\tif(dret != dunix_pos) {\n\t\t\tprintf(\"lseek of disk %s len 0x%llx ret: %lld, errno:\"\n\t\t\t\t\"%d\\n\", dsk->name_ptr, dunix_pos, dret, errno);\n\t\t\tmust_clear_track = 1;\n\t\t}\n\n\t\tdlen = read(dsk->fd, track_buf, unix_len);\n\t\tif(dlen != unix_len) {\n\t\t\tprintf(\"read of disk %s q_trk %d ret: %lld, errno:%d\\n\",\n\t\t\t\tdsk->name_ptr, qtr_track, dlen, errno);\n\t\t\tmust_clear_track = 1;\n\t\t}\n\t}\n\n\tif(must_clear_track) {\n\t\tfor(i = 0; i < TRACK_BUF_LEN; i++) {\n\t\t\ttrack_buf[i] = 0;\n\t\t}\n\t}\n\n#if 0\n\tprintf(\"Q_track %02x dumped out\\n\", qtr_track);\n\n\tfor(i = 0; i < 4096; i += 32) {\n\t\tprintf(\"%04x: %02x%02x%02x%02x%02x%02x%02x%02x \"\n\t\t\t\"%02x%02x%02x%02x%02x%02x%02x%02x \"\n\t\t\t\"%02x%02x%02x%02x%02x%02x%02x%02x \"\n\t\t\t\"%02x%02x%02x%02x%02x%02x%02x%02x\\n\", i,\n\t\t\ttrack_buf[i+0], track_buf[i+1], track_buf[i+2],\n\t\t\ttrack_buf[i+3], track_buf[i+4], track_buf[i+5],\n\t\t\ttrack_buf[i+6], track_buf[i+7], track_buf[i+8],\n\t\t\ttrack_buf[i+9], track_buf[i+10], track_buf[i+11],\n\t\t\ttrack_buf[i+12], track_buf[i+13], track_buf[i+14],\n\t\t\ttrack_buf[i+15], track_buf[i+16], track_buf[i+17],\n\t\t\ttrack_buf[i+18], track_buf[i+19], track_buf[i+20],\n\t\t\ttrack_buf[i+21], track_buf[i+22], track_buf[i+23],\n\t\t\ttrack_buf[i+24], track_buf[i+25], track_buf[i+26],\n\t\t\ttrack_buf[i+27], track_buf[i+28], track_buf[i+29],\n\t\t\ttrack_buf[i+30], track_buf[i+31]);\n\t}\n#endif\n\n\tdsk->cur_fbit_pos = 0;\t\t/* for consistency */\n\tdsk->raw_bptr_malloc = 1;\n\n\ttrk = &(dsk->trks[qtr_track]);\n\tnum_bytes = 2 + ((len_bits + 7) >> 3) + 4;\n\ttrk->track_bits = len_bits;\n\ttrk->dunix_pos = dunix_pos;\n\ttrk->unix_len = unix_len;\n\ttrk->dirty = 0;\n\ttrk->raw_bptr = (byte *)malloc(num_bytes);\n\ttrk->sync_ptr = (byte *)malloc(num_bytes);\n\n\tiwm_move_to_ftrack(dsk, qtr_track << 16, 0, dfcyc);\n\n\t/* create nibblized image */\n\n\tif(dsk->disk_525 && (dsk->image_type == DSK_TYPE_NIB)) {\n\t\tiwm_nibblize_track_nib525(dsk, track_buf, qtr_track, unix_len);\n\t} else if(dsk->disk_525) {\n\t\tiwm_nibblize_track_525(dsk, track_buf, qtr_track, dfcyc);\n\t} else {\n\t\tiwm_nibblize_track_35(dsk, track_buf, qtr_track, unix_len,\n\t\t\t\t\t\t\t\t\tdfcyc);\n\t}\n\n\t//printf(\"For qtr_track:%04x, trk->dirty:%d\\n\", qtr_track, trk->dirty);\n\ttrk->dirty = 0;\n}\n\nvoid\niwm_nibblize_track_nib525(Disk *dsk, byte *track_buf, int qtr_track,\n\t\t\t\t\t\tword32 unix_len)\n{\n\tbyte\t*bptr, *sync_ptr;\n\tint\tlen;\n\tint\ti;\n\n\t// This is the old, dumb .nib format.  It consists of 0x1a00 bytes\n\t//  per track, but there's no sync information.  Just mark each byte\n\t//  as being sync=7\n\tlen = unix_len;\n\tbptr = &(dsk->cur_trk_ptr->raw_bptr[0]);\n\tsync_ptr = &(dsk->cur_trk_ptr->sync_ptr[0]);\n\tfor(i = 0; i < len; i++) {\n\t\tbptr[i] = track_buf[i];\n\t}\n\tfor(i = 0; i < len; i++) {\n\t\tsync_ptr[i] = 7;\n\t}\n\tif(dsk->cur_track_bits != (unix_len * 8)) {\n\t\tfatal_printf(\"Track %d.%02d of nib image should be bits:%06x \"\n\t\t\t\"but it is: %06x\\n\", qtr_track >> 2, (qtr_track & 3)*25,\n\t\t\tunix_len * 8, dsk->cur_track_bits);\n\t}\n\n\tiwm_printf(\"Nibblized q_track %02x\\n\", qtr_track);\n}\n\nvoid\niwm_nibblize_track_525(Disk *dsk, byte *track_buf, int qtr_track, dword64 dfcyc)\n{\n\tbyte\tpartial_nib_buf[0x300];\n\tword32\tval, last_val;\n\tint\tphys_sec, log_sec, num_sync;\n\tint\ti;\n\n#if 0\n\tprintf(\"nibblize track 525, qtr_track:%04x, trk:%p, trk->raw_bptr:%p, \"\n\t\t\"sync_ptr:%p\\n\", qtr_track, trk, trk->raw_bptr, trk->sync_ptr);\n#endif\n\n\tfor(phys_sec = 0; phys_sec < 16; phys_sec++) {\n\t\tif(dsk->image_type == DSK_TYPE_DOS33) {\n\t\t\tlog_sec = phys_to_dos_sec[phys_sec];\n\t\t} else {\n\t\t\tlog_sec = phys_to_prodos_sec[phys_sec];\n\t\t}\n\n\t\t/* Create sync headers */\n\t\tif(phys_sec == 0) {\n\t\t\tnum_sync = 70;\n\t\t} else {\n\t\t\tnum_sync = 22;\n\t\t}\n\n\t\tfor(i = 0; i < num_sync; i++) {\n\t\t\tdisk_nib_out(dsk, 0xff, 10);\n\t\t}\n\t\tdisk_nib_out(dsk, 0xd5, 8);\t\t// prolog: d5,aa,96\n\t\tdisk_nib_out(dsk, 0xaa, 8);\n\t\tdisk_nib_out(dsk, 0x96, 8);\n\t\tdisk_4x4_nib_out(dsk, dsk->vol_num);\n\t\tdisk_4x4_nib_out(dsk, qtr_track >> 2);\n\t\tdisk_4x4_nib_out(dsk, phys_sec);\n\t\tdisk_4x4_nib_out(dsk, dsk->vol_num ^ (qtr_track>>2) ^ phys_sec);\n\t\tdisk_nib_out(dsk, 0xde, 8);\t\t// epilog: de,aa,eb\n\t\tdisk_nib_out(dsk, 0xaa, 8);\n\t\tdisk_nib_out(dsk, 0xeb, 8);\n\n\t\t/* Inter sync */\n\t\tdisk_nib_out(dsk, 0xff, 10);\n\t\tfor(i = 0; i < 6; i++) {\n\t\t\tdisk_nib_out(dsk, 0xff, 10);\n\t\t}\n\t\tdisk_nib_out(dsk, 0xd5, 8);\t// data prolog: d5,aa,ad\n\t\tdisk_nib_out(dsk, 0xaa, 8);\n\t\tdisk_nib_out(dsk, 0xad, 8);\n\n\t\tsector_to_partial_nib( &(track_buf[log_sec*256]),\n\t\t\t&(partial_nib_buf[0]));\n\n\t\tlast_val = 0;\n\t\tfor(i = 0; i < 0x156; i++) {\n\t\t\tval = partial_nib_buf[i];\n\t\t\tdisk_nib_out(dsk, to_disk_byte[last_val ^ val], 8);\n\t\t\tlast_val = val;\n\t\t}\n\t\tdisk_nib_out(dsk, to_disk_byte[last_val], 8);\n\n\t\t/* data epilog */\n\t\tdisk_nib_out(dsk, 0xde, 8);\t// data epilog: de,aa,eb\n\t\tdisk_nib_out(dsk, 0xaa, 8);\n\t\tdisk_nib_out(dsk, 0xeb, 8);\n\t}\n\n\t/* finish nibblization */\n\tdisk_nib_end_track(dsk, dfcyc);\n\n\tiwm_printf(\"Nibblized q_track %02x\\n\", qtr_track);\n\n\tif(g_check_nibblization) {\n\t\tdisk_check_nibblization(dsk, &(track_buf[0]), 0x1000, dfcyc);\n\t}\n\n\t//printf(\"Showing track after nibblization:\\n\");\n\t//iwm_show_a_track(dsk, dsk->cur_trk_ptr, dfcyc);\n}\n\nvoid\niwm_nibblize_track_35(Disk *dsk, byte *track_buf, int qtr_track,\n\t\t\t\t\t\tword32 unix_len, dword64 dfcyc)\n{\n\tint\tphys_to_log_sec[16];\n\tword32\tbuf_c00[0x100];\n\tword32\tbuf_d00[0x100];\n\tword32\tbuf_e00[0x100];\n\tbyte\t*buf;\n\tword32\tval, phys_track, phys_side, capacity, cksum, acc_hi;\n\tword32\ttmp_5c, tmp_5d, tmp_5e, tmp_5f, tmp_63, tmp_64, tmp_65;\n\tint\tnum_sectors, log_sec, track, side, num_sync, carry;\n\tint\tinterleave, x, y;\n\tint\ti, phys_sec;\n\n\tif(dsk->cur_fbit_pos & 511) {\n\t\thalt_printf(\"fbit_pos:%07x is not bit-aligned!\\n\",\n\t\t\t\t\t\t\tdsk->cur_fbit_pos);\n\t}\n\n\tnum_sectors = (unix_len >> 9);\n\n\tfor(i = 0; i < num_sectors; i++) {\n\t\tphys_to_log_sec[i] = -1;\n\t}\n\n\tphys_sec = 0;\n\tinterleave = 2;\n\tfor(log_sec = 0; log_sec < num_sectors; log_sec++) {\n\t\twhile(phys_to_log_sec[phys_sec] >= 0) {\n\t\t\tphys_sec++;\n\t\t\tif(phys_sec >= num_sectors) {\n\t\t\t\tphys_sec = 0;\n\t\t\t}\n\t\t}\n\t\tphys_to_log_sec[phys_sec] = log_sec;\n\t\tphys_sec += interleave;\n\t\tif(phys_sec >= num_sectors) {\n\t\t\tphys_sec -= num_sectors;\n\t\t}\n\t}\n\n\ttrack = qtr_track >> 1;\n\tside = qtr_track & 1;\n\tfor(phys_sec = 0; phys_sec < num_sectors; phys_sec++) {\n\n\t\tlog_sec = phys_to_log_sec[phys_sec];\n\t\tif(log_sec < 0) {\n\t\t\tprintf(\"Track: %02x.%x phys_sec: %02x = %d!\\n\",\n\t\t\t\ttrack, side, phys_sec, log_sec);\n\t\t\texit(2);\n\t\t}\n\n\t\t/* Create sync headers */\n\t\tif(phys_sec == 0) {\n\t\t\tnum_sync = 400;\n\t\t} else {\n\t\t\tnum_sync = 54;\n\t\t}\n\n\t\tfor(i = 0; i < num_sync; i++) {\n\t\t\tdisk_nib_out(dsk, 0xff, 10);\n\t\t}\n\n\t\tdisk_nib_out(dsk, 0xd5, 8);\t\t/* prolog */\n\t\tdisk_nib_out(dsk, 0xaa, 8);\t\t/* prolog */\n\t\tdisk_nib_out(dsk, 0x96, 8);\t\t/* prolog */\n\n\t\tphys_track = track & 0x3f;\n\t\tphys_side = (side << 5) + (track >> 6);\n\t\tcapacity = 0x22;\n\t\tdisk_nib_out(dsk, to_disk_byte[phys_track], 8);\t/* trk */\n\t\tdisk_nib_out(dsk, to_disk_byte[log_sec], 8);\t/* sec */\n\t\tdisk_nib_out(dsk, to_disk_byte[phys_side], 8);\t/* sides+trk */\n\t\tdisk_nib_out(dsk, to_disk_byte[capacity], 8);\t/* capacity*/\n\n\t\tcksum = (phys_track ^ log_sec ^ phys_side ^ capacity) & 0x3f;\n\t\tdisk_nib_out(dsk, to_disk_byte[cksum], 8);\t/* cksum*/\n\n\t\tdisk_nib_out(dsk, 0xde, 8);\t\t/* epi */\n\t\tdisk_nib_out(dsk, 0xaa, 8);\t\t/* epi */\n\n\t\t/* Inter sync */\n\t\tfor(i = 0; i < 5; i++) {\n\t\t\tdisk_nib_out(dsk, 0xff, 10);\n\t\t}\n\t\tdisk_nib_out(dsk, 0xd5, 8);\t/* data prolog */\n\t\tdisk_nib_out(dsk, 0xaa, 8);\t/* data prolog */\n\t\tdisk_nib_out(dsk, 0xad, 8);\t/* data prolog */\n\t\tdisk_nib_out(dsk, to_disk_byte[log_sec], 8);\t/* sec again */\n\n\t\t/* do nibblizing! */\n\t\tbuf = track_buf + (log_sec << 9);\n\n/* 6320 */\n\t\ttmp_5e = 0;\n\t\ttmp_5d = 0;\n\t\ttmp_5c = 0;\n\t\ty = 0;\n\t\tx = 0xaf;\n\t\tbuf_c00[0] = 0;\n\t\tbuf_d00[0] = 0;\n\t\tbuf_e00[0] = 0;\n\t\tbuf_e00[1] = 0;\n\t\tfor(y = 0x4; y > 0; y--) {\n\t\t\tbuf_c00[x] = 0;\n\t\t\tbuf_d00[x] = 0;\n\t\t\tbuf_e00[x] = 0;\n\t\t\tx--;\n\t\t}\n\n\t\twhile(x >= 0) {\n/* 6338 */\n\t\t\ttmp_5c = tmp_5c << 1;\n\t\t\tcarry = (tmp_5c >> 8);\n\t\t\ttmp_5c = (tmp_5c + carry) & 0xff;\n\n\t\t\tval = buf[y];\n\t\t\ttmp_5e = val + tmp_5e + carry;\n\t\t\tcarry = (tmp_5e >> 8);\n\t\t\ttmp_5e = tmp_5e & 0xff;\n\n\t\t\tval = val ^ tmp_5c;\n\t\t\tbuf_c00[x] = val;\n\t\t\ty++;\n/* 634c */\n\t\t\tval = buf[y];\n\t\t\ttmp_5d = tmp_5d + val + carry;\n\t\t\tcarry = (tmp_5d >> 8);\n\t\t\ttmp_5d = tmp_5d & 0xff;\n\t\t\tval = val ^ tmp_5e;\n\t\t\tbuf_d00[x] = val;\n\t\t\ty++;\n\t\t\tx--;\n\t\t\tif(x <= 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\n/* 632a */\n\t\t\tval = buf[y];\n\t\t\ttmp_5c = tmp_5c + val + carry;\n\t\t\tcarry = (tmp_5c >> 8);\n\t\t\ttmp_5c = tmp_5c & 0xff;\n\n\t\t\tval = val ^ tmp_5d;\n\t\t\tbuf_e00[x+1] = val;\n\t\t\ty++;\n\t\t}\n\n/* 635f */\n\t\tval = ((tmp_5c >> 2) ^ tmp_5d) & 0x3f;\n/* 6367 */\n\t\tval = (val ^ tmp_5d) >> 2;\n/* 636b */\n\t\tval = (val ^ tmp_5e) & 0x3f;\n/* 636f */\n\t\tval = (val ^ tmp_5e) >> 2;\n/* 6373 */\n\t\ttmp_5f = val;\n/* 6375 */\n\t\ttmp_63 = 0;\n\t\ttmp_64 = 0;\n\t\ttmp_65 = 0;\n\t\tacc_hi = 0;\n\n\n\t\ty = 0xae;\n\t\twhile(y >= 0) {\n/* 63e4 */\n\t\t\t/* write out acc_hi */\n\t\t\tval = to_disk_byte[acc_hi & 0x3f];\n\t\t\tdisk_nib_out(dsk, val, 8);\n\n/* 63f2 */\n\t\t\tval = to_disk_byte[tmp_63 & 0x3f];\n\t\t\ttmp_63 = buf_c00[y];\n\t\t\tacc_hi = tmp_63 >> 6;\n\t\t\tdisk_nib_out(dsk, val, 8);\n/* 640b */\n\t\t\tval = to_disk_byte[tmp_64 & 0x3f];\n\t\t\ttmp_64 = buf_d00[y];\n\t\t\tacc_hi = (acc_hi << 2) + (tmp_64 >> 6);\n\t\t\tdisk_nib_out(dsk, val, 8);\n\t\t\ty--;\n\t\t\tif(y < 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\n/* 63cb */\n\t\t\tval = to_disk_byte[tmp_65 & 0x3f];\n\t\t\ttmp_65 = buf_e00[y+1];\n\t\t\tacc_hi = (acc_hi << 2) + (tmp_65 >> 6);\n\t\t\tdisk_nib_out(dsk, val, 8);\n\t\t}\n/* 6429 */\n\t\tval = to_disk_byte[tmp_5f & 0x3f];\n\t\tdisk_nib_out(dsk, val, 8);\n\n\t\tval = to_disk_byte[tmp_5e & 0x3f];\n\t\tdisk_nib_out(dsk, val, 8);\n\n\t\tval = to_disk_byte[tmp_5d & 0x3f];\n\t\tdisk_nib_out(dsk, val, 8);\n\n\t\tval = to_disk_byte[tmp_5c & 0x3f];\n\t\tdisk_nib_out(dsk, val, 8);\n\n/* 6440 */\n\t\t/* data epilog */\n\t\tdisk_nib_out(dsk, 0xde, 8);\t/* epi */\n\t\tdisk_nib_out(dsk, 0xaa, 8);\t/* epi */\n\t\tdisk_nib_out(dsk, 0xff, 8);\n\t}\n\n\tdisk_nib_end_track(dsk, dfcyc);\n\n\tif(g_check_nibblization) {\n\t\tdisk_check_nibblization(dsk, &(track_buf[0]), unix_len, dfcyc);\n\t}\n}\n\nvoid\ndisk_4x4_nib_out(Disk *dsk, word32 val)\n{\n\tdisk_nib_out(dsk, 0xaa | (val >> 1), 8);\n\tdisk_nib_out(dsk, 0xaa | val, 8);\n}\n\nvoid\ndisk_nib_out(Disk *dsk, word32 val, int size)\n{\n\tword32\tbit_pos;\n\n\tbit_pos = dsk->cur_fbit_pos >> 9;\n\tdsk->cur_fbit_pos = disk_nib_out_raw(dsk,\n\t\t(dsk->cur_frac_track + 0x8000) >> 16, val, size, bit_pos, 0) *\n\t\t\t\t\t\t\t\t\t512;\n}\n\nvoid\ndisk_nib_end_track(Disk *dsk, dword64 dfcyc)\n{\n\t// printf(\"disk_nib_end_track %p\\n\", dsk);\n\n\tdsk->cur_fbit_pos = 0;\n\tdsk->disk_dirty = 0;\n\tiwm_recalc_sync_from(dsk, (word32)(dsk->cur_trk_ptr - &(dsk->trks[0])),\n\t\t\t\t\t\t\t\t0, dfcyc);\n}\n\nword32\ndisk_nib_out_raw(Disk *dsk, word32 qtr_track, word32 val, int bits,\n\t\t\t\tword32 bit_pos, dword64 dfcyc)\n{\n\tTrk\t*trkptr;\n\tbyte\t*bptr, *sync_ptr;\n\tword32\ttrack_bits, tmp, mask;\n\tint\tpos, next_pos, bit, to_do, shift_left, shift_right, last_byte;\n\tint\tthis_bits;\n\tint\tdo_print;\n\n\t// write bits from val[7:x].  If bits=3 and val=0xaf, write bits 101.\n\t// If bits=10 and val=0xaf, write 0xaf then bits 00.\n\n\tif(qtr_track >= 160) {\n\t\treturn bit_pos;\n\t}\n\ttrkptr = &(dsk->trks[qtr_track]);\n\ttrack_bits = trkptr->track_bits;\n\tif(track_bits == 0) {\n\t\thalt_printf(\"disk_nib_out_raw track_bits=0, %04x\\n\", qtr_track);\n\t\treturn bit_pos;\n\t}\n\n\tlast_byte = (track_bits - 1) >> 3;\n\tbit = bit_pos & 7;\n\tpos = bit_pos >> 3;\n\tbptr = &(trkptr->raw_bptr[0]);\n\tsync_ptr = &(trkptr->sync_ptr[0]);\n\tif(dfcyc != 0) {\n\t\tdbg_log_info(dfcyc, (bits << 24) | (bit_pos << 1),\n\t\t\t\t(track_bits << 16) | (val & 0xffff), 0x100ed);\n\t}\n\tdsk->disk_dirty = 1;\n\ttrkptr->dirty = 1;\n\tdo_print = ((pos <= 0x10) || (pos >= 0x18e0)) &&\n\t\t\t\t\t(dsk->cur_frac_track == 0xb0000);\n\tdo_print = 0;\n\tif(do_print) {\n\t\tprintf(\"disk_nib_out %02x, %d, %06x\\n\", val, bits, bit_pos*2);\n\t}\n\twhile(1) {\n\t\tthis_bits = 8;\n\t\tnext_pos = pos + 1;\n\t\tif(pos >= last_byte) {\n\t\t\tthis_bits = ((track_bits - 1) & 7) + 1;\t// 1..8\n\t\t\tnext_pos = 0;\n\t\t}\n\t\tthis_bits = (this_bits - bit);\t\t// 1..8\n\t\tto_do = bits;\t\t\t\t// 1...inf\n\t\tif(to_do > this_bits) {\n\t\t\tto_do = this_bits;\t\t// 1..8\n\t\t}\n\t\tshift_left = (8 - bit - to_do) & 7;\n\t\tshift_right = 8 - to_do;\n\t\tmask = (1U << to_do) - 1;\n\t\ttmp = (val >> shift_right) & mask;\n\t\tmask = mask << shift_left;\n\t\tif(do_print) {\n\t\t\tprintf(\" pos:%04x bit:%d tmp:%02x mask:%02x bits:%d \"\n\t\t\t\t\"bptr[]=%02x new:%02x todo:%d, r:%d l:%d\\n\",\n\t\t\t\tpos, bit, tmp, mask, bits, bptr[pos],\n\t\t\t\t(bptr[pos] & (~mask)) |\n\t\t\t\t\t\t((tmp << shift_left) & mask),\n\t\t\t\tto_do, shift_right, shift_left);\n\t\t}\n\t\tbptr[pos] = (bptr[pos] & (~mask)) |\n\t\t\t\t\t\t((tmp << shift_left) & mask);\n\t\tsync_ptr[pos] = 0xff;\n\t\tbits -= to_do;\n\t\tif(bits <= 0) {\n\t\t\tpos = (pos * 8) + bit + to_do;\n\t\t\tif((bit + to_do) >= 8) {\n\t\t\t\tpos = next_pos * 8;\n\t\t\t}\n\t\t\tif((word32)pos >= track_bits) {\n\t\t\t\tpos -= track_bits;\n\t\t\t}\n\t\t\tif(do_print) {\n\t\t\t\tprintf(\" returning %05x, bits:%d orig:%05x \"\n\t\t\t\t\t\"%05x\\n\",\n\t\t\t\t\tpos*2, bits, bit_pos*2, last_byte);\n\t\t\t}\n\t\t\treturn pos;\n\t\t}\n\t\tval = (val << to_do) & 0xff;\n\t\tbit = 0;\n\t\tpos = next_pos;\n\t}\n}\n\nDisk *\niwm_get_dsk_from_slot_drive(int slot, int drive)\n{\n\tDisk\t*dsk;\n\tint\tmax_drive;\n\n\t// pass in slot=5,6,7 drive=0,1 (or more for slot 7)\n\tmax_drive = 2;\n\tswitch(slot) {\n\tcase 5:\n\t\tdsk = &(g_iwm.drive35[drive]);\n\t\tbreak;\n\tcase 6:\n\t\tdsk = &(g_iwm.drive525[drive]);\n\t\tbreak;\n\tdefault:\t// slot 7\n\t\tmax_drive = MAX_C7_DISKS;\n\t\tdsk = &(g_iwm.smartport[drive]);\n\t}\n\tif(drive >= max_drive) {\n\t\tdsk -= drive;\t\t// Move back to drive 0 effectively\n\t}\n\n\treturn dsk;\n}\n\nvoid\niwm_toggle_lock(Disk *dsk)\n{\n\tprintf(\"iwm_toggle_lock: write_prot:%d, write_through:%d\\n\",\n\t\tdsk->write_prot, dsk->write_through_to_unix);\n\tif(dsk->write_prot == 2) {\n\t\t// nothing to do\n\t\treturn;\n\t}\n\n\tif(dsk->write_prot) {\n\t\tdsk->write_prot = 0;\n\t} else {\n\t\tdsk->write_prot = 1;\n\t}\n\tprintf(\"New dsk->write_prot: %d\\n\", dsk->write_prot);\n\tif(dsk->wozinfo_ptr && dsk->write_through_to_unix) {\n\t\twoz_rewrite_lock(dsk);\n\t\tprintf(\"Called woz_rewrite_lock()\\n\");\n\t\treturn;\n\t}\n}\n\nvoid\niwm_eject_named_disk(int slot, int drive, const char *name,\n\t\t\t\t\t\tconst char *partition_name)\n{\n\tDisk\t*dsk;\n\n\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\tif(dsk->fd < 0) {\n\t\treturn;\n\t}\n\n\t/* If name matches, eject the disk! */\n\tif(!strcmp(dsk->name_ptr, name)) {\n\t\t/* It matches, eject it */\n\t\tif((partition_name != 0) && (dsk->partition_name != 0)) {\n\t\t\t/* If both have partitions, and they differ, then */\n\t\t\t/*  don't eject.  Otherwise, eject */\n\t\t\tif(strcmp(dsk->partition_name, partition_name) != 0) {\n\t\t\t\t/* Don't eject */\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tiwm_eject_disk(dsk);\n\t}\n}\n\nvoid\niwm_eject_disk_by_num(int slot, int drive)\n{\n\tDisk\t*dsk;\n\n\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\n\tiwm_eject_disk(dsk);\n}\n\nvoid\niwm_eject_disk(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\tword32\tstate;\n\tint\tmotor_on;\n\tint\ti;\n\n\tprintf(\"Ejecting dsk: %s, fd:%d\\n\", dsk->name_ptr, dsk->fd);\n\n\tif(dsk->fd < 0) {\n\t\treturn;\n\t}\n\n\tg_config_kegs_update_needed = 1;\n\n\tstate = g_iwm.state;\n\tmotor_on = state & IWM_STATE_MOTOR_ON;\n\tif(state & IWM_STATE_C031_APPLE35SEL) {\n\t\tmotor_on = state & IWM_STATE_MOTOR_ON35;\n\t}\n\tif(motor_on) {\n\t\thalt_printf(\"Try eject dsk:%s, but motor_on!\\n\", dsk->name_ptr);\n\t}\n\n\tdynapro_try_fix_damaged_disk(dsk);\n\tiwm_flush_disk_to_unix(dsk);\n\n\tprintf(\"Ejecting disk: %s\\n\", dsk->name_ptr);\n\n\t/* Free all memory, close file */\n\n\t/* free the tracks first */\n\tif(dsk->trks != 0) {\n\t\tfor(i = 0; i < MAX_TRACKS; i++) {\n\t\t\tif(dsk->raw_bptr_malloc) {\n\t\t\t\tfree(dsk->trks[i].raw_bptr);\n\t\t\t}\n\t\t\tfree(dsk->trks[i].sync_ptr);\n\t\t\tdsk->trks[i].raw_bptr = 0;\n\t\t\tdsk->trks[i].sync_ptr = 0;\n\t\t\tdsk->trks[i].track_bits = 0;\n\t\t}\n\t}\n\tdsk->num_tracks = 0;\n\tdsk->raw_bptr_malloc = 0;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(wozinfo_ptr) {\n\t\tif(dsk->raw_data == 0) {\n\t\t\tfree(wozinfo_ptr->wozptr);\n\t\t}\n\t\twozinfo_ptr->wozptr = 0;\n\t\tfree(wozinfo_ptr);\n\t}\n\tdsk->wozinfo_ptr = 0;\n\n\tdynapro_free_dynapro_info(dsk);\n\n\t/* close file, clean up dsk struct */\n\tif(dsk->raw_data) {\n\t\tfree(dsk->raw_data);\n\t} else {\n\t\tclose(dsk->fd);\n\t}\n\n\tdsk->fd = -1;\n\tdsk->raw_dsize = 0;\n\tdsk->raw_data = 0;\n\tdsk->dimage_start = 0;\n\tdsk->dimage_size = 0;\n\tdsk->cur_fbit_pos = 0;\n\tdsk->cur_track_bits = 0;\n\tdsk->disk_dirty = 0;\n\tdsk->write_through_to_unix = 0;\n\tdsk->write_prot = 1;\n\tdsk->just_ejected = 1;\n\n\t/* Leave name_ptr valid */\n}\n\nvoid\niwm_show_track(int slot_drive, int track, dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tTrk\t*trk;\n\tword32\tstate;\n\tint\tdrive, sel35, qtr_track;\n\n\tstate = g_iwm.state;\n\tif(slot_drive < 0) {\n\t\tdrive = (state >> IWM_BIT_DRIVE_SEL) & 1;\n\t\tif(state & IWM_STATE_MOTOR_ON) {\n\t\t\tsel35 = (state >> IWM_BIT_C031_APPLE35SEL) & 1;\n\t\t} else {\n\t\t\tsel35 = (state >> IWM_BIT_LAST_SEL35) & 1;\n\t\t}\n\t} else {\n\t\tdrive = slot_drive & 1;\n\t\tsel35 = !((slot_drive >> 1) & 1);\n\t}\n\tif(sel35) {\n\t\tdsk = &(g_iwm.drive35[drive]);\n\t} else {\n\t\tdsk = &(g_iwm.drive525[drive]);\n\t}\n\n\tif(track < 0) {\n\t\tqtr_track = dsk->cur_frac_track >> 16;\n\t} else {\n\t\tqtr_track = track;\n\t}\n\tif((dsk->trks == 0) || (qtr_track >= 160)) {\n\t\treturn;\n\t}\n\ttrk = &(dsk->trks[qtr_track]);\n\n\tif(trk->track_bits == 0) {\n\t\tdbg_printf(\"Track_bits: %d\\n\", trk->track_bits);\n\t\tdbg_printf(\"No track for type: %d, drive: %d, qtrk:0x%02x\\n\",\n\t\t\tsel35, drive, qtr_track);\n\t\treturn;\n\t}\n\n\tdbg_printf(\"Current s%dd%d, q_track:0x%02x\\n\", 6 - sel35,\n\t\t\t\t\t\t\tdrive + 1, qtr_track);\n\n\tiwm_show_a_track(dsk, trk, dfcyc);\n}\n\nvoid\niwm_show_a_track(Disk *dsk, Trk *trk, dword64 dfcyc)\n{\n\tbyte\t*bptr;\n\tbyte\t*sync_ptr;\n\tword32\tval, track_bits, len, shift, line_len, sync;\n\tint\ti, j;\n\n\ttrack_bits = trk->track_bits;\n\tdbg_printf(\"  Showtrack:track_bits*2: %06x, fbit_pos: %07x, \"\n\t\t\"trk:%06x, dfcyc:%016llx\\n\", track_bits*2, dsk->cur_fbit_pos,\n\t\tdsk->cur_frac_track, dfcyc);\n\tdbg_printf(\"  disk_525:%d, drive:%d name:%s fd:%d, dimage_start:\"\n\t\t\"%08llx, dimage_size:%08llx\\n\", dsk->disk_525, dsk->drive,\n\t\tdsk->name_ptr, dsk->fd, dsk->dimage_start, dsk->dimage_size);\n\tdbg_printf(\"  image_type:%d, vol_num:%02x, write_prot:%d, \"\n\t\t\"write_through:%d, disk_dirty:%d\\n\", dsk->image_type,\n\t\tdsk->vol_num, dsk->write_prot, dsk->write_through_to_unix,\n\t\tdsk->disk_dirty);\n\tdbg_printf(\"  just_ejected:%d, last_phases:%d, num_tracks:%d\\n\",\n\t\tdsk->just_ejected, dsk->last_phases, dsk->num_tracks);\n\n\tlen = (track_bits + 7) >> 3;\n\tif(len >= 0x3000) {\n\t\tlen = 0x3000;\n\t\tdbg_printf(\"len too big, using %04x\\n\", len);\n\t}\n\n\tbptr = trk->raw_bptr;\n\tsync_ptr = trk->sync_ptr;\n\n\tlen = len + 2;\t\t// Show an extra 2 bytes\n\tfor(i = 0; i < (int)len; i += 16) {\n\t\tline_len = 16;\n\t\tif((i + line_len) > len) {\n\t\t\tline_len = len - i;\n\t\t}\n\t\t// First, print raw bptr bytes\n\t\tdbg_printf(\"%04x:  \", i);\n\t\tfor(j = 0; j < (int)line_len; j++) {\n\t\t\tdbg_printf(\" %02x\", bptr[i + j]);\n\t\t\tif(((i + j) * 8U) >= track_bits) {\n\t\t\t\tdbg_printf(\"*\");\n\t\t\t}\n\t\t}\n\t\tdbg_printf(\"\\n\");\n\t\tdbg_printf(\"  sync:\");\n\t\tfor(j = 0; j < (int)line_len; j++) {\n\t\t\tdbg_printf(\" %2d\", sync_ptr[i + j]);\n\t\t}\n\t\tdbg_printf(\"\\n\");\n\t\tdbg_printf(\"  nibs:\");\n\t\tfor(j = 0; j < (int)line_len; j++) {\n\t\t\tsync = sync_ptr[i+j];\n\t\t\tif(sync >= 8) {\n\t\t\t\tdbg_printf(\" XX\");\n\t\t\t} else {\n\t\t\t\tshift = (7 - sync) & 7;\n\t\t\t\tval = (bptr[i + j] << 8) | bptr[i + j + 1];\n\t\t\t\tval = ((val << shift) >> 8) & 0xff;\n\t\t\t\tdbg_printf(\" %02x\", val);\n\t\t\t}\n\t\t}\n\t\tdbg_printf(\"\\n\");\n\t}\n}\n\n\nvoid\ndummy1(word32 psr)\n{\n\tprintf(\"dummy1 psr: %05x\\n\", psr);\n}\n\nvoid\ndummy2(word32 psr)\n{\n\tprintf(\"dummy2 psr: %05x\\n\", psr);\n}\n"
  },
  {
    "path": "gsplus/src/iwm.h",
    "content": "#ifdef INCLUDE_RCSID_C\n#endif\n\n/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#define MAX_TRACKS\t(2*80)\n#define MAX_C7_DISKS\t16\n\n#define NIB_LEN_525\t\t0x18f2\t\t/* 51088 bits per track */\n// Expected bits per track: (1020484/5)/4 = 51024.  A little extra seems good\n\n#define NIBS_FROM_ADDR_TO_DATA\t28\n// Copy II+ Manual Sector Copy fails if this is 20, so make it 28\n\n// image_type settings.  0 means unknown type\n#define DSK_TYPE_PRODOS\t\t1\n#define DSK_TYPE_DOS33\t\t2\n#define DSK_TYPE_DYNAPRO\t3\n#define DSK_TYPE_NIB\t\t4\n#define DSK_TYPE_WOZ\t\t5\n\n// Note: C031_APPLE35SEL must be 6, C031_CTRL must be 7, MOTOR_ON must be 5!\n// Q7 needs to be adjacent and higher than Q6\n// Bits 4:0 are IWM mode register: 0: latch mode; 1: async handshake;\n//   2: immediate motor off (no 1 sec delay); 3: 2us bit timing;\n//   4: Divide input clock by 8 (instead of 7)\n#define IWM_BIT_MOTOR_ON\t\t5\n#define IWM_BIT_C031_APPLE35SEL\t\t6\n#define IWM_BIT_C031_CTRL\t\t7\n#define IWM_BIT_STEP_DIRECTION35\t8\n#define IWM_BIT_MOTOR_ON35\t\t9\n#define IWM_BIT_MOTOR_OFF\t\t10\n#define IWM_BIT_DRIVE_SEL\t\t11\n#define IWM_BIT_Q6\t\t\t12\n#define IWM_BIT_Q7\t\t\t13\n#define IWM_BIT_ENABLE2\t\t\t14\n#define IWM_BIT_LAST_SEL35\t\t15\n#define IWM_BIT_PHASES\t\t\t16\n#define IWM_BIT_RESET\t\t\t20\n\n#define IWM_STATE_MOTOR_ON\t\t(1 << IWM_BIT_MOTOR_ON)\n#define IWM_STATE_C031_APPLE35SEL\t(1 << IWM_BIT_C031_APPLE35SEL)\n#define IWM_STATE_C031_CTRL\t\t(1 << IWM_BIT_C031_CTRL)\n#define IWM_STATE_STEP_DIRECTION35\t(1 << IWM_BIT_STEP_DIRECTION35)\n#define IWM_STATE_MOTOR_ON35\t\t(1 << IWM_BIT_MOTOR_ON35)\n#define IWM_STATE_MOTOR_OFF\t\t(1 << IWM_BIT_MOTOR_OFF)\n#define IWM_STATE_DRIVE_SEL\t\t(1 << IWM_BIT_DRIVE_SEL)\n#define IWM_STATE_Q6\t\t\t(1 << IWM_BIT_Q6)\n#define IWM_STATE_Q7\t\t\t(1 << IWM_BIT_Q7)\n#define IWM_STATE_ENABLE2\t\t(1 << IWM_BIT_ENABLE2)\n#define IWM_STATE_LAST_SEL35\t\t(1 << IWM_BIT_LAST_SEL35)\n#define IWM_STATE_PHASES\t\t(1 << IWM_BIT_PHASES)\n#define IWM_STATE_RESET\t\t\t(1 << IWM_BIT_RESET)\n\nSTRUCT(Trk) {\n\tbyte\t*raw_bptr;\n\tbyte\t*sync_ptr;\n\tdword64\tdunix_pos;\n\tword16\tunix_len;\n\tword16\tdirty;\n\tword32\ttrack_bits;\n};\n\nSTRUCT(Woz_info) {\n\tbyte\t*wozptr;\n\tword32\twoz_size;\n\tint\tversion;\n\tint\treparse_needed;\n\tword32\tmax_trk_blocks;\n\tint\tmeta_size;\n\tint\ttrks_size;\n\tint\ttmap_offset;\n\tint\ttrks_offset;\n\tint\tinfo_offset;\n\tint\tmeta_offset;\n};\n\ntypedef struct Dynapro_map_st Dynapro_map;\n\nSTRUCT(Dynapro_file) {\n\tDynapro_file *next_ptr;\n\tDynapro_file *parent_ptr;\n\tDynapro_file *subdir_ptr;\n\tchar\t*unix_path;\n\tbyte\t*buffer_ptr;\n\tbyte\tprodos_name[17];\t// +0x00-0x0f: [0] is len, nul at end\n\tword32\tdir_byte;\t\t// Byte address of this file's dir ent\n\tword32\teof;\t\t\t// +0x15-0x17\n\tword32\tblocks_used;\t\t// +0x13-0x14\n\tword32\tcreation_time;\t\t// +0x18-0x1b\n\tword32\tlastmod_time;\t\t// +0x21-0x24\n\tword16\tupper_lower;\t\t// +0x1c-0x1d: Versions: lowercase flags\n\tword16\tkey_block;\t\t// +0x11-0x12\n\tword16\taux_type;\t\t// +0x1f-0x20\n\tword16\theader_pointer;\t\t// +0x25-0x26\n\tword16\tmap_first_block;\n\tbyte\tfile_type;\t\t// +0x10\n\tbyte\tmodified_flag;\n\tbyte\tdamaged;\n};\n\nstruct Dynapro_map_st {\n\tDynapro_file *file_ptr;\n\tword16\tnext_map_block;\n\tword16\tmodified;\n};\n\nSTRUCT(Dynapro_info) {\n\tchar\t*root_path;\n\tDynapro_file *volume_ptr;\n\tDynapro_map *block_map_ptr;\n\tint\tdamaged;\n};\n\nSTRUCT(Disk) {\n\tdword64\tdfcyc_last_read;\n\tbyte\t*raw_data;\n\tWoz_info *wozinfo_ptr;\n\tDynapro_info *dynapro_info_ptr;\n\tchar\t*name_ptr;\n\tchar\t*partition_name;\n\tint\tpartition_num;\n\tint\tfd;\n\tword32\tdynapro_blocks;\n\tdword64\traw_dsize;\n\tdword64\tdimage_start;\n\tdword64\tdimage_size;\n\tint\tsmartport;\n\tint\tdisk_525;\n\tint\tdrive;\n\tword32\tcur_frac_track;\n\tint\timage_type;\n\tint\tvol_num;\n\tint\twrite_prot;\n\tint\twrite_through_to_unix;\n\tint\tdisk_dirty;\n\tint\tjust_ejected;\n\tint\tlast_phases;\n\tdword64\tdfcyc_last_phases;\n\tword32\tcur_fbit_pos;\n\tword32\tfbit_mult;\n\tword32\tcur_track_bits;\n\tint\traw_bptr_malloc;\n\tTrk\t*cur_trk_ptr;\n\tint\tnum_tracks;\n\tTrk\t*trks;\n};\n\nSTRUCT(Iwm) {\n\tDisk\tdrive525[2];\n\tDisk\tdrive35[2];\n\tDisk\tsmartport[MAX_C7_DISKS];\n\tdword64\tdfcyc_last_fastemul_read;\n\tword32\tstate;\n\tword32\tmotor_off_vbl_count;\n\tword32\tforced_sync_bit;\n\tword32\tlast_rd_bit;\n\tword32\twrite_val;\n\tword32\twr_last_bit[5];\n\tword32\twr_qtr_track[5];\n\tword32\twr_num_bits[5];\n\tword32\twr_prior_num_bits[5];\n\tword32\twr_delta[5];\n\tint\tnum_active_writes;\n};\n\nSTRUCT(Driver_desc) {\n\tword16\tsig;\n\tword16\tblk_size;\n\tword32\tblk_count;\n\tword16\tdev_type;\n\tword16\tdev_id;\n\tword32\tdata;\n\tword16\tdrvr_count;\n};\n\nSTRUCT(Part_map) {\n\tword16\tsig;\n\tword16\tsigpad;\n\tword32\tmap_blk_cnt;\n\tword32\tphys_part_start;\n\tword32\tpart_blk_cnt;\n\tchar\tpart_name[32];\n\tchar\tpart_type[32];\n\tword32\tdata_start;\n\tword32\tdata_cnt;\n\tword32\tpart_status;\n\tword32\tlog_boot_start;\n\tword32\tboot_size;\n\tword32\tboot_load;\n\tword32\tboot_load2;\n\tword32\tboot_entry;\n\tword32\tboot_entry2;\n\tword32\tboot_cksum;\n\tchar\tprocessor[16];\n\tchar\tjunk[128];\n};\n\n"
  },
  {
    "path": "gsplus/src/joystick_driver.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include \"defc.h\"\n\n#ifdef __linux__\n# include <linux/joystick.h>\n#endif\n\n#ifdef _WIN32\n# include <windows.h>\n# include <mmsystem.h>\n#endif\n\nextern int g_joystick_native_type1;\t\t/* in paddles.c */\nextern int g_joystick_native_type2;\t\t/* in paddles.c */\nextern int g_joystick_native_type;\t\t/* in paddles.c */\nextern int g_paddle_buttons;\nextern int g_paddle_val[];\n\n\nconst char *g_joystick_dev = \"/dev/js0\";\t/* default joystick dev file */\n#define MAX_JOY_NAME\t128\n\nint\tg_joystick_native_fd = -1;\nint\tg_joystick_num_axes = 0;\nint\tg_joystick_num_buttons = 0;\n\nint\tg_joystick_callback_buttons = 0;\nint\tg_joystick_callback_x = 32767;\nint\tg_joystick_callback_y = 32767;\n\n#ifdef __linux__\n# define JOYSTICK_DEFINED\nvoid\njoystick_init()\n{\n\tchar\tjoy_name[MAX_JOY_NAME];\n\tint\tversion, fd;\n\tint\ti;\n\n\tfd = open(g_joystick_dev, O_RDONLY | O_NONBLOCK);\n\tif(fd < 0) {\n\t\tprintf(\"Unable to open joystick dev file: %s, errno: %d\\n\",\n\t\t\tg_joystick_dev, errno);\n\t\tprintf(\"Defaulting to mouse joystick\\n\");\n\t\treturn;\n\t}\n\n\tstrcpy(&joy_name[0], \"Unknown Joystick\");\n\tversion = 0x800;\n\n\tioctl(fd, JSIOCGNAME(MAX_JOY_NAME), &joy_name[0]);\n\tioctl(fd, JSIOCGAXES, &g_joystick_num_axes);\n\tioctl(fd, JSIOCGBUTTONS, &g_joystick_num_buttons);\n\tioctl(fd, JSIOCGVERSION, &version);\n\n\tprintf(\"Detected joystick: %s [%d axes, %d buttons vers: %08x]\\n\",\n\t\tjoy_name, g_joystick_num_axes, g_joystick_num_buttons,\n\t\tversion);\n\n\tg_joystick_native_type1 = 1;\n\tg_joystick_native_type2 = -1;\n\tg_joystick_native_fd = fd;\n\tfor(i = 0; i < 4; i++) {\n\t\tg_paddle_val[i] = 32767;\n\t}\n\tg_paddle_buttons = 0xc;\n}\n\n/* joystick_update_linux() called from paddles.c.  Update g_paddle_val[] */\n/*  and g_paddle_buttons with current information */\nvoid\njoystick_update(dword64 dfcyc)\n{\n\tstruct js_event js;\t/* the linux joystick event record */\n\tint\tmask, val, num, type, ret, len;\n\tint\ti;\n\n\t/* suck up to 20 events, then give up */\n\tlen = sizeof(struct js_event);\n\tfor(i = 0; i < 20; i++) {\n\t\tret = read(g_joystick_native_fd, &js, len);\n\t\tif(ret != len) {\n\t\t\t/* just get out */\n\t\t\tbreak;\n\t\t}\n\t\ttype = js.type & ~JS_EVENT_INIT;\n\t\tval = js.value;\n\t\tnum = js.number & 3;\t\t/* clamp to 0-3 */\n\t\tswitch(type) {\n\t\tcase JS_EVENT_BUTTON:\n\t\t\tmask = 1 << num;\n\t\t\tif(val) {\n\t\t\t\tval = mask;\n\t\t\t}\n\t\t\tg_paddle_buttons = (g_paddle_buttons & ~mask) | val;\n\t\t\tbreak;\n\t\tcase JS_EVENT_AXIS:\n\t\t\t/* val is -32767 to +32767 */\n\t\t\tg_paddle_val[num] = val;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(i > 0) {\n\t\tpaddle_update_trigger_dcycs(dfcyc);\n\t}\n}\n\nvoid\njoystick_update_buttons()\n{\n}\n#endif /* LINUX */\n\n#ifdef _WIN32\n# define JOYSTICK_DEFINED\n#undef JOYSTICK_DEFINED\n\t// HACK: remove\n#if 0\nvoid\njoystick_init()\n{\n\tJOYINFO info;\n\tJOYCAPS joycap;\n\tMMRESULT ret1, ret2;\n\tint\ti;\n\n\t//\tCheck that there is a joystick device\n\tif(joyGetNumDevs() <= 0) {\n\t\tprintf(\"No joystick hardware detected\\n\");\n\t\tg_joystick_native_type1 = -1;\n\t\tg_joystick_native_type2 = -1;\n\t\treturn;\n\t}\n\n\tg_joystick_native_type1 = -1;\n\tg_joystick_native_type2 = -1;\n\n\t//\tCheck that at least joystick 1 or joystick 2 is available\n\tret1 = joyGetPos(JOYSTICKID1, &info);\n\tret2 = joyGetDevCaps(JOYSTICKID1, &joycap, sizeof(joycap));\n\tif(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {\n\t\tg_joystick_native_type1 = JOYSTICKID1;\n\t\tprintf(\"Joystick #1 = %s\\n\", joycap.szPname);\n\t\tg_joystick_native_type = JOYSTICKID1;\n\t}\n\tret1 = joyGetPos(JOYSTICKID2, &info);\n\tret2 = joyGetDevCaps(JOYSTICKID2, &joycap, sizeof(joycap));\n\tif(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {\n\t\tg_joystick_native_type2 = JOYSTICKID2;\n\t\tprintf(\"Joystick #2 = %s\\n\", joycap.szPname);\n\t\tif(g_joystick_native_type < 0) {\n\t\t\tg_joystick_native_type = JOYSTICKID2;\n\t\t}\n\t}\n\n\tfor(i = 0; i < 4; i++) {\n\t\tg_paddle_val[i] = 32767;\n\t}\n\tg_paddle_buttons = 0xc;\n}\n\nvoid\njoystick_update(dword64 dfcyc)\n{\n\tJOYCAPS joycap;\n\tJOYINFO info;\n\tUINT\tid;\n\tMMRESULT ret1, ret2;\n\n\tid = g_joystick_native_type;\n\n\tret1 = joyGetDevCaps(id, &joycap, sizeof(joycap));\n\tret2 = joyGetPos(id, &info);\n\tif(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {\n\t\tg_paddle_val[0] = (info.wXpos - joycap.wXmin) * 32768 /\n\t\t\t\t\t\t(joycap.wXmax - joycap.wXmin);\n\t\tg_paddle_val[1] = (info.wYpos - joycap.wYmin) * 32768 /\n\t\t\t\t\t\t(joycap.wYmax - joycap.wYmin);\n\t\tif(info.wButtons & JOY_BUTTON1) {\n\t\t\tg_paddle_buttons = g_paddle_buttons | 1;\n\t\t} else {\n\t\t\tg_paddle_buttons = g_paddle_buttons & (~1);\n\t\t}\n\t\tif(info.wButtons & JOY_BUTTON2) {\n\t\t\tg_paddle_buttons = g_paddle_buttons | 2;\n\t\t} else {\n\t\t\tg_paddle_buttons = g_paddle_buttons & (~2);\n\t\t}\n\t\tpaddle_update_trigger_dcycs(dfcyc);\n\t}\n}\n\nvoid\njoystick_update_buttons()\n{\n\tJOYINFOEX info;\n\tUINT id;\n\n\tid = g_joystick_native_type;\n\n\tinfo.dwSize = sizeof(JOYINFOEX);\n\tinfo.dwFlags = JOY_RETURNBUTTONS;\n\tif(joyGetPosEx(id, &info) == JOYERR_NOERROR) {\n\t\tif(info.dwButtons & JOY_BUTTON1) {\n\t\t\tg_paddle_buttons = g_paddle_buttons | 1;\n\t\t} else {\n\t\t\tg_paddle_buttons = g_paddle_buttons & (~1);\n\t\t}\n\t\tif(info.dwButtons & JOY_BUTTON2) {\n\t\t\tg_paddle_buttons = g_paddle_buttons | 2;\n\t\t} else {\n\t\t\tg_paddle_buttons = g_paddle_buttons & (~2);\n\t\t}\n\t}\n}\n#endif\n#endif\n\n#ifdef MAC\n# define JOYSTICK_DEFINED\n\n#include <IOKit/IOKitLib.h>\n#include <IOKit/hid/IOHIDManager.h>\n#include <IOKit/hid/IOHIDKeys.h>\n#include <IOKit/usb/USBSpec.h>\n#include <CoreFoundation/CoreFoundation.h>\n// Headers are at: /Applications/Xcode.app/Contents/Developer/Platforms/\n//      MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/\n//      Frameworks/IOKit.framework/Headers\n// Thanks to VirtualC64 and hidapi library for coding example\n\nCFIndex g_joystick_min = 0;\nCFIndex g_joystick_range = 256;\nint\tg_joystick_minmax_valid = 0;\nint\tg_joystick_dummy = 0;\n\nvoid\nhid_device_callback(void *ptr, IOReturn result, void *sender,\n\t\t\t\t\t\tIOHIDValueRef value)\n{\n\tIOHIDElementRef element;\n\tword32\tmask;\n\tint\tusage_page, usage, ival, button;\n\n\t// This is a callback routine, and it's unclear to me what the state is\n\t//  For safety, do no printfs() (other than for debug).\n\tif((ptr || result || sender || 1) == 0) {\n\t\tprintf(\"Bad\\n\");\t\t// Avoid unused var warning\n\t}\n\n\telement = IOHIDValueGetElement(value);\n\tusage_page = IOHIDElementGetUsagePage(element);\n\tusage = IOHIDElementGetUsage(element);\n\tival = IOHIDValueGetIntegerValue(value);\n#if 0\n\tprintf(\" usage_page:%d, usage:%d, value:%d\\n\", usage_page, usage, ival);\n#endif\n\tif((usage_page == kHIDPage_GenericDesktop) &&\n\t\t\t\t((usage >= kHIDUsage_GD_X) &&\n\t\t\t\t(usage <= kHIDUsage_GD_Y)) &&\n\t\t\t\t!g_joystick_minmax_valid) {\n\t\tg_joystick_min = IOHIDElementGetLogicalMin(element);\n\t\tg_joystick_range = IOHIDElementGetLogicalMax(element) + 1 -\n\t\t\t\t\t\t\tg_joystick_min;\n\t\t// printf(\"min:%lld range:%lld\\n\", (dword64)g_joystick_min,\n\t\t//\t\t\t\t(dword64)g_joystick_range);\n\t\tif(g_joystick_range == 0) {\n\t\t\tg_joystick_range = 1;\n\t\t}\n\t\tg_joystick_minmax_valid = 1;\n\t}\n\tif((usage_page == kHIDPage_GenericDesktop) &&\n\t\t\t\t\t\t(usage == kHIDUsage_GD_X)) {\n\t\tg_joystick_callback_x = ((ival * 65536) / g_joystick_range) -\n\t\t\t\t\t\t\t\t\t32768;\n\t\t//printf(\"g_joystick_callback_x = %d\\n\", g_joystick_callback_x);\n\t}\n\tif((usage_page == kHIDPage_GenericDesktop) &&\n\t\t\t\t\t\t(usage == kHIDUsage_GD_Y)) {\n\t\tg_joystick_callback_y = ((ival * 65536) / g_joystick_range) -\n\t\t\t\t\t\t\t\t\t32768;\n\t\t//printf(\"g_joystick_callback_y = %d\\n\", g_joystick_callback_y);\n\t}\n\tif((usage_page == kHIDPage_Button) && (usage >= 1) && (usage <= 10)) {\n\t\t// Buttons: usage=1 is button 0, usage=2 is button 1, etc.\n\t\tbutton = (~usage) & 1;\n\t\tmask = 1 << button;\n\t\t//printf(\"Button %d (%d) pressed:%d\\n\", button, usage, ival);\n\t\tif(ival == 0) {\t\t// Button released\n\t\t\tg_joystick_callback_buttons &= (~mask);\n\t\t} else {\t\t// Button pressed\n\t\t\tg_joystick_callback_buttons |= mask;\n\t\t}\n\t}\n}\n\nint\nhid_get_int_property(IOHIDDeviceRef device, CFStringRef key_cfstr)\n{\n\tCFTypeRef ref;\n\tBoolean\tbret;\n\tint\tint_val;\n\n\tref = IOHIDDeviceGetProperty(device, key_cfstr);\n\tif(ref) {\n\t\tbret = CFNumberGetValue((CFNumberRef)ref, kCFNumberIntType,\n\t\t\t\t\t\t\t\t&int_val);\n\t\tif(bret) {\n\t\t\treturn int_val;\n\t\t}\n\t}\n\treturn 0;\n}\n\nvoid\njoystick_init()\n{\n\tIOHIDManagerRef hid_mgr;\n\tCFSetRef devices_set;\n\tCFIndex\tnum_devices;\n\tIOHIDDeviceRef *devices_array, device;\n\tint\tvendor, usage_page, usage;\n\tint\ti;\n\n\tg_joystick_native_type1 = -1;\n\tg_joystick_native_type2 = -1;\n\n\thid_mgr = IOHIDManagerCreate(kCFAllocatorDefault,\n\t\t\t\t\t\t\tkIOHIDOptionsTypeNone);\n\tIOHIDManagerSetDeviceMatching(hid_mgr, 0);\n\tIOHIDManagerOpen(hid_mgr, kIOHIDOptionsTypeNone);\n\n\tdevices_set = IOHIDManagerCopyDevices(hid_mgr);\n\tnum_devices = CFSetGetCount(devices_set);\n\n\t// Sets are hashtables, so we cannot directly access it.  The only way\n\t//  to iterate over all values is to use CFSetGetValues to get a simple\n\t//  array of the values, and iterate over that\n\tdevices_array = calloc(num_devices, sizeof(IOHIDDeviceRef));\n\tCFSetGetValues(devices_set, (const void **)devices_array);\n\n\tfor(i = 0; i < num_devices; i++) {\n\t\tdevice = devices_array[i];\n\t\tvendor = hid_get_int_property(device, CFSTR(kIOHIDVendorIDKey));\n\t\t// printf(\" vendor: %d\\n\", vendor);\n\t\tusage_page = hid_get_int_property(device,\n\t\t\t\t\tCFSTR(kIOHIDDeviceUsagePageKey));\n\t\tusage = hid_get_int_property(device,\n\t\t\t\t\tCFSTR(kIOHIDDeviceUsageKey));\n\t\t// printf(\" usage_page:%d, usage:%d\\n\", usage_page, usage);\n\t\tusage_page = hid_get_int_property(device,\n\t\t\t\t\tCFSTR(kIOHIDPrimaryUsagePageKey));\n\t\tusage = hid_get_int_property(device,\n\t\t\t\t\tCFSTR(kIOHIDPrimaryUsageKey));\n\t\t// printf(\" primary_usage_page:%d, usage:%d\\n\", usage_page,\n\t\t//\t\t\t\t\t\tusage);\n\t\tif(usage_page != kHIDPage_GenericDesktop) {\n\t\t\tcontinue;\n\t\t}\n\t\tif((usage != kHIDUsage_GD_Joystick) &&\n\t\t\t\t(usage != kHIDUsage_GD_GamePad) &&\n\t\t\t\t(usage != kHIDUsage_GD_MultiAxisController)) {\n\t\t\tcontinue;\n\t\t}\n\t\tprintf(\" JOYSTICK FOUND, vendor:%08x!\\n\", vendor);\n\t\tIOHIDDeviceOpen(device, kIOHIDOptionsTypeNone);\n\t\tIOHIDDeviceScheduleWithRunLoop(device, CFRunLoopGetCurrent(),\n\t\t\t\t\t\t\tkCFRunLoopCommonModes);\n\t\tIOHIDDeviceRegisterInputValueCallback(device,\n\t\t\t\t\thid_device_callback, 0);\n\t\tg_joystick_native_type1 = 1;\n\t\treturn;\n\t\t// Now, hid_device_callback will be called whenever a joystick\n\t\t//  value changes.  Only set global variables for joystick.\n\t}\n}\n\nvoid\njoystick_update(dword64 dfcyc)\n{\n\tint\ti;\n\n\tif(dfcyc) {\n\t\t// Avoid unused parameter warnings\n\t}\n\tfor(i = 0; i < 4; i++) {\n\t\tg_paddle_val[i] = 32767;\n\t}\n\tg_paddle_buttons = 0xc;\n\tif(g_joystick_native_type1 >= 0) {\n\t\tg_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3);\n\t\tg_paddle_val[0] = g_joystick_callback_x;\n\t\tg_paddle_val[1] = g_joystick_callback_y;\n\t\tpaddle_update_trigger_dcycs(dfcyc);\n\t}\n}\n\nvoid\njoystick_update_buttons()\n{\n\tif(g_joystick_native_type1 >= 0) {\n\t\tg_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3);\n\t}\n}\n#endif\n\n#ifndef JOYSTICK_DEFINED\n/* stubs for the routines */\nvoid\njoystick_init()\n{\n\tg_joystick_native_type1 = -1;\n\tg_joystick_native_type2 = -1;\n\tg_joystick_native_type = -1;\n}\n\nvoid\njoystick_update(dword64 dfcyc)\n{\n\tint\ti;\n\n\tif(dfcyc) {\n\t\t// Avoid unused parameter warnings\n\t}\n\tfor(i = 0; i < 4; i++) {\n\t\tg_paddle_val[i] = 32767;\n\t}\n\tg_paddle_buttons = 0xc;\n\tif(g_joystick_native_type1 >= 0) {\n\t\tg_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3);\n\t\tg_paddle_val[0] = g_joystick_callback_x;\n\t\tg_paddle_val[1] = g_joystick_callback_y;\n\t\tpaddle_update_trigger_dcycs(dfcyc);\n\t}\n}\n\nvoid\njoystick_update_buttons()\n{\n\tif(g_joystick_native_type1 >= 0) {\n\t\tg_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3);\n\t}\n}\n#endif\n\nvoid\njoystick_callback_init(int native_type)\n{\n\tg_joystick_native_type1 = native_type;\n}\n\nvoid\njoystick_callback_update(word32 buttons, int paddle_x, int paddle_y)\n{\n\tg_joystick_callback_buttons = (g_paddle_buttons & (~3)) | (buttons & 3);\n\tg_joystick_callback_x = paddle_x;\n\tg_joystick_callback_y = paddle_y;\n}\n"
  },
  {
    "path": "gsplus/src/kegsfont.h",
    "content": "/* $KmKId: kegsfont.h,v 1.1 2002-11-10 03:31:51-05 kadickey Exp $ */\n\n/* char 0x00 (raw 0x40)  */\n\t{ 0xc7, 0xbb, 0xab, 0xa3, 0xa7, 0xbf, 0xc3, 0xff },\n/* char 0x01 (raw 0x41)  */\n\t{ 0xef, 0xd7, 0xbb, 0xbb, 0x83, 0xbb, 0xbb, 0xff },\n/* char 0x02 (raw 0x42)  */\n\t{ 0x87, 0xbb, 0xbb, 0x87, 0xbb, 0xbb, 0x87, 0xff },\n/* char 0x03 (raw 0x43)  */\n\t{ 0xc7, 0xbb, 0xbf, 0xbf, 0xbf, 0xbb, 0xc7, 0xff },\n/* char 0x04 (raw 0x44)  */\n\t{ 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x87, 0xff },\n/* char 0x05 (raw 0x45)  */\n\t{ 0x83, 0xbf, 0xbf, 0x87, 0xbf, 0xbf, 0x83, 0xff },\n/* char 0x06 (raw 0x46)  */\n\t{ 0x83, 0xbf, 0xbf, 0x87, 0xbf, 0xbf, 0xbf, 0xff },\n/* char 0x07 (raw 0x47)  */\n\t{ 0xc3, 0xbf, 0xbf, 0xbf, 0xb3, 0xbb, 0xc3, 0xff },\n/* char 0x08 (raw 0x48)  */\n\t{ 0xbb, 0xbb, 0xbb, 0x83, 0xbb, 0xbb, 0xbb, 0xff },\n/* char 0x09 (raw 0x49)  */\n\t{ 0xc7, 0xef, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff },\n/* char 0x0a (raw 0x4a)  */\n\t{ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xbb, 0xc7, 0xff },\n/* char 0x0b (raw 0x4b)  */\n\t{ 0xbb, 0xb7, 0xaf, 0x9f, 0xaf, 0xb7, 0xbb, 0xff },\n/* char 0x0c (raw 0x4c)  */\n\t{ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0x83, 0xff },\n/* char 0x0d (raw 0x4d)  */\n\t{ 0xbb, 0x93, 0xab, 0xab, 0xbb, 0xbb, 0xbb, 0xff },\n/* char 0x0e (raw 0x4e)  */\n\t{ 0xbb, 0xbb, 0x9b, 0xab, 0xb3, 0xbb, 0xbb, 0xff },\n/* char 0x0f (raw 0x4f)  */\n\t{ 0xc7, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xc7, 0xff },\n/* char 0x10 (raw 0x50)  */\n\t{ 0x87, 0xbb, 0xbb, 0x87, 0xbf, 0xbf, 0xbf, 0xff },\n/* char 0x11 (raw 0x51)  */\n\t{ 0xc7, 0xbb, 0xbb, 0xbb, 0xab, 0xb7, 0xcb, 0xff },\n/* char 0x12 (raw 0x52)  */\n\t{ 0x87, 0xbb, 0xbb, 0x87, 0xaf, 0xb7, 0xbb, 0xff },\n/* char 0x13 (raw 0x53)  */\n\t{ 0xc7, 0xbb, 0xbf, 0xc7, 0xfb, 0xbb, 0xc7, 0xff },\n/* char 0x14 (raw 0x54)  */\n\t{ 0x83, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xff },\n/* char 0x15 (raw 0x55)  */\n\t{ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xc7, 0xff },\n/* char 0x16 (raw 0x56)  */\n\t{ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xd7, 0xef, 0xff },\n/* char 0x17 (raw 0x57)  */\n\t{ 0xbb, 0xbb, 0xbb, 0xab, 0xab, 0x93, 0xbb, 0xff },\n/* char 0x18 (raw 0x58)  */\n\t{ 0xbb, 0xbb, 0xd7, 0xef, 0xd7, 0xbb, 0xbb, 0xff },\n/* char 0x19 (raw 0x59)  */\n\t{ 0xbb, 0xbb, 0xd7, 0xef, 0xef, 0xef, 0xef, 0xff },\n/* char 0x1a (raw 0x5a)  */\n\t{ 0x83, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x83, 0xff },\n/* char 0x1b (raw 0x5b)  */\n\t{ 0x83, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x83, 0xff },\n/* char 0x1c (raw 0x5c)  */\n\t{ 0xff, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xff, 0xff },\n/* char 0x1d (raw 0x5d)  */\n\t{ 0x83, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0x83, 0xff },\n/* char 0x1e (raw 0x5e)  */\n\t{ 0xff, 0xff, 0xef, 0xd7, 0xbb, 0xff, 0xff, 0xff },\n/* char 0x1f (raw 0x5f)  */\n\t{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 },\n/* char 0x20 (raw 0x20)  */\n\t{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },\n/* char 0x21 (raw 0x21)  */\n\t{ 0xef, 0xef, 0xef, 0xef, 0xef, 0xff, 0xef, 0xff },\n/* char 0x22 (raw 0x22)  */\n\t{ 0xd7, 0xd7, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff },\n/* char 0x23 (raw 0x23)  */\n\t{ 0xd7, 0xd7, 0x83, 0xd7, 0x83, 0xd7, 0xd7, 0xff },\n/* char 0x24 (raw 0x24)  */\n\t{ 0xef, 0xc3, 0xaf, 0xc7, 0xeb, 0x87, 0xef, 0xff },\n/* char 0x25 (raw 0x25)  */\n\t{ 0x9f, 0x9b, 0xf7, 0xef, 0xdf, 0xb3, 0xf3, 0xff },\n/* char 0x26 (raw 0x26)  */\n\t{ 0xdf, 0xaf, 0xaf, 0xdf, 0xab, 0xb7, 0xcb, 0xff },\n/* char 0x27 (raw 0x27)  */\n\t{ 0xef, 0xef, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff },\n/* char 0x28 (raw 0x28)  */\n\t{ 0xef, 0xdf, 0xbf, 0xbf, 0xbf, 0xdf, 0xef, 0xff },\n/* char 0x29 (raw 0x29)  */\n\t{ 0xef, 0xf7, 0xfb, 0xfb, 0xfb, 0xf7, 0xef, 0xff },\n/* char 0x2a (raw 0x2a)  */\n\t{ 0xef, 0xab, 0xc7, 0xef, 0xc7, 0xab, 0xef, 0xff },\n/* char 0x2b (raw 0x2b)  */\n\t{ 0xff, 0xef, 0xef, 0x83, 0xef, 0xef, 0xff, 0xff },\n/* char 0x2c (raw 0x2c)  */\n\t{ 0xff, 0xff, 0xff, 0xff, 0xef, 0xef, 0xdf, 0xff },\n/* char 0x2d (raw 0x2d)  */\n\t{ 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff },\n/* char 0x2e (raw 0x2e)  */\n\t{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff },\n/* char 0x2f (raw 0x2f)  */\n\t{ 0xff, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0xff, 0xff },\n/* char 0x30 (raw 0x30)  */\n\t{ 0xc7, 0xbb, 0xb3, 0xab, 0x9b, 0xbb, 0xc7, 0xff },\n/* char 0x31 (raw 0x31)  */\n\t{ 0xef, 0xcf, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff },\n/* char 0x32 (raw 0x32)  */\n\t{ 0xc7, 0xbb, 0xfb, 0xe7, 0xdf, 0xbf, 0x83, 0xff },\n/* char 0x33 (raw 0x33)  */\n\t{ 0x83, 0xfb, 0xf7, 0xe7, 0xfb, 0xbb, 0xc7, 0xff },\n/* char 0x34 (raw 0x34)  */\n\t{ 0xf7, 0xe7, 0xd7, 0xb7, 0x83, 0xf7, 0xf7, 0xff },\n/* char 0x35 (raw 0x35)  */\n\t{ 0x83, 0xbf, 0x87, 0xfb, 0xfb, 0xbb, 0xc7, 0xff },\n/* char 0x36 (raw 0x36)  */\n\t{ 0xe3, 0xdf, 0xbf, 0x87, 0xbb, 0xbb, 0xc7, 0xff },\n/* char 0x37 (raw 0x37)  */\n\t{ 0x83, 0xfb, 0xf7, 0xef, 0xdf, 0xdf, 0xdf, 0xff },\n/* char 0x38 (raw 0x38)  */\n\t{ 0xc7, 0xbb, 0xbb, 0xc7, 0xbb, 0xbb, 0xc7, 0xff },\n/* char 0x39 (raw 0x39)  */\n\t{ 0xc7, 0xbb, 0xbb, 0xc3, 0xfb, 0xf7, 0x8f, 0xff },\n/* char 0x3a (raw 0x3a)  */\n\t{ 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xff, 0xff },\n/* char 0x3b (raw 0x3b)  */\n\t{ 0xff, 0xff, 0xef, 0xff, 0xef, 0xef, 0xdf, 0xff },\n/* char 0x3c (raw 0x3c)  */\n\t{ 0xf7, 0xef, 0xdf, 0xbf, 0xdf, 0xef, 0xf7, 0xff },\n/* char 0x3d (raw 0x3d)  */\n\t{ 0xff, 0xff, 0x83, 0xff, 0x83, 0xff, 0xff, 0xff },\n/* char 0x3e (raw 0x3e)  */\n\t{ 0xdf, 0xef, 0xf7, 0xfb, 0xf7, 0xef, 0xdf, 0xff },\n/* char 0x3f (raw 0x3f)  */\n\t{ 0xc7, 0xbb, 0xf7, 0xef, 0xef, 0xff, 0xef, 0xff },\n/* char 0x40 (raw 0x14)  */\n\t{ 0x08, 0x10, 0x6c, 0xfe, 0xfc, 0xfc, 0x7e, 0x6c },\n/* char 0x41 (raw 0x11)  */\n\t{ 0x08, 0x10, 0x6c, 0x82, 0x84, 0x84, 0x52, 0x6c },\n/* char 0x42 (raw 0xf5)  */\n\t{ 0x00, 0x00, 0x40, 0x60, 0x70, 0x78, 0x6c, 0x42 },\n/* char 0x43 (raw 0x82)  */\n\t{ 0xfe, 0x44, 0x28, 0x10, 0x10, 0x28, 0x54, 0xfe },\n/* char 0x44 (raw 0xeb)  */\n\t{ 0x00, 0x02, 0x04, 0x88, 0x50, 0x20, 0x20, 0x00 },\n/* char 0x45 (raw 0xe4)  */\n\t{ 0xfe, 0xfc, 0xfa, 0x36, 0xae, 0xde, 0xde, 0xfe },\n/* char 0x46 (raw 0xec)  */\n\t{ 0xfc, 0xfc, 0xfc, 0xdc, 0x9c, 0x00, 0x9e, 0xde },\n/* char 0x47 (raw 0xed)  */\n\t{ 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0x00, 0xfe },\n/* char 0x48 (raw 0xee)  */\n\t{ 0x10, 0x20, 0x40, 0xfe, 0x40, 0x20, 0x10, 0x00 },\n/* char 0x49 (raw 0xe9)  */\n\t{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54 },\n/* char 0x4a (raw 0xef)  */\n\t{ 0x10, 0x10, 0x10, 0x10, 0x92, 0x54, 0x38, 0x10 },\n/* char 0x4b (raw 0xf0)  */\n\t{ 0x10, 0x38, 0x54, 0x92, 0x10, 0x10, 0x10, 0x10 },\n/* char 0x4c (raw 0xf1)  */\n\t{ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },\n/* char 0x4d (raw 0xf7)  */\n\t{ 0x02, 0x02, 0x02, 0x22, 0x62, 0xfe, 0x60, 0x20 },\n/* char 0x4e (raw 0xf6)  */\n\t{ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc },\n/* char 0x4f (raw 0xaf)  */\n\t{ 0xc8, 0x18, 0x38, 0x7e, 0x38, 0x18, 0x08, 0xf6 },\n/* char 0x50 (raw 0xb8)  */\n\t{ 0x26, 0x30, 0x38, 0xfc, 0x38, 0x30, 0x20, 0xde },\n/* char 0x51 (raw 0xce)  */\n\t{ 0x02, 0x12, 0x10, 0xfe, 0x7c, 0x38, 0x12, 0x02 },\n/* char 0x52 (raw 0xe5)  */\n\t{ 0x02, 0x12, 0x38, 0x7c, 0xfe, 0x10, 0x12, 0x02 },\n/* char 0x53 (raw 0xea)  */\n\t{ 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00 },\n/* char 0x54 (raw 0xe6)  */\n\t{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe },\n/* char 0x55 (raw 0xe8)  */\n\t{ 0x10, 0x08, 0x04, 0xfe, 0x04, 0x08, 0x10, 0x00 },\n/* char 0x56 (raw 0xd7)  */\n\t{ 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa },\n/* char 0x57 (raw 0xe3)  */\n\t{ 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54 },\n/* char 0x58 (raw 0xf4)  */\n\t{ 0x00, 0x7c, 0x82, 0x80, 0x80, 0x80, 0xfe, 0x00 },\n/* char 0x59 (raw 0xe7)  */\n\t{ 0x00, 0x00, 0xfc, 0x02, 0x02, 0x02, 0xfe, 0x00 },\n/* char 0x5a (raw 0xf3)  */\n\t{ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 },\n/* char 0x5b (raw 0xd2)  */\n\t{ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00 },\n/* char 0x5c (raw 0xc7)  */\n\t{ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe },\n/* char 0x5d (raw 0xd4)  */\n\t{ 0x28, 0x28, 0xee, 0x00, 0xee, 0x28, 0x28, 0x00 },\n/* char 0x5e (raw 0xdf)  */\n\t{ 0xfe, 0x02, 0x02, 0x32, 0x32, 0x02, 0x02, 0xfe },\n/* char 0x5f (raw 0xd1)  */\n\t{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 },\n/* char 0x60 (raw 0x60)  */\n\t{ 0xdf, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff },\n/* char 0x61 (raw 0x61)  */\n\t{ 0xff, 0xff, 0xc7, 0xfb, 0xc3, 0xbb, 0xc3, 0xff },\n/* char 0x62 (raw 0x62)  */\n\t{ 0xbf, 0xbf, 0x87, 0xbb, 0xbb, 0xbb, 0x87, 0xff },\n/* char 0x63 (raw 0x63)  */\n\t{ 0xff, 0xff, 0xc3, 0xbf, 0xbf, 0xbf, 0xc3, 0xff },\n/* char 0x64 (raw 0x64)  */\n\t{ 0xfb, 0xfb, 0xc3, 0xbb, 0xbb, 0xbb, 0xc3, 0xff },\n/* char 0x65 (raw 0x65)  */\n\t{ 0xff, 0xff, 0xc7, 0xbb, 0x83, 0xbf, 0xc3, 0xff },\n/* char 0x66 (raw 0x66)  */\n\t{ 0xe7, 0xdb, 0xdf, 0x87, 0xdf, 0xdf, 0xdf, 0xff },\n/* char 0x67 (raw 0x67)  */\n\t{ 0xff, 0xff, 0xc7, 0xbb, 0xbb, 0xc3, 0xfb, 0xc7 },\n/* char 0x68 (raw 0x68)  */\n\t{ 0xbf, 0xbf, 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xff },\n/* char 0x69 (raw 0x69)  */\n\t{ 0xef, 0xff, 0xcf, 0xef, 0xef, 0xef, 0xc7, 0xff },\n/* char 0x6a (raw 0x6a)  */\n\t{ 0xf7, 0xff, 0xe7, 0xf7, 0xf7, 0xf7, 0xb7, 0xcf },\n/* char 0x6b (raw 0x6b)  */\n\t{ 0xbf, 0xbf, 0xbb, 0xb7, 0x8f, 0xb7, 0xbb, 0xff },\n/* char 0x6c (raw 0x6c)  */\n\t{ 0xcf, 0xef, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff },\n/* char 0x6d (raw 0x6d)  */\n\t{ 0xff, 0xff, 0x93, 0xab, 0xab, 0xab, 0xbb, 0xff },\n/* char 0x6e (raw 0x6e)  */\n\t{ 0xff, 0xff, 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xff },\n/* char 0x6f (raw 0x6f)  */\n\t{ 0xff, 0xff, 0xc7, 0xbb, 0xbb, 0xbb, 0xc7, 0xff },\n/* char 0x70 (raw 0x70)  */\n\t{ 0xff, 0xff, 0x87, 0xbb, 0xbb, 0x87, 0xbf, 0xbf },\n/* char 0x71 (raw 0x71)  */\n\t{ 0xff, 0xff, 0xc3, 0xbb, 0xbb, 0xc3, 0xfb, 0xfb },\n/* char 0x72 (raw 0x72)  */\n\t{ 0xff, 0xff, 0xa3, 0x9f, 0xbf, 0xbf, 0xbf, 0xff },\n/* char 0x73 (raw 0x73)  */\n\t{ 0xff, 0xff, 0xc3, 0xbf, 0xc7, 0xfb, 0x87, 0xff },\n/* char 0x74 (raw 0x74)  */\n\t{ 0xdf, 0xdf, 0x87, 0xdf, 0xdf, 0xdb, 0xe7, 0xff },\n/* char 0x75 (raw 0x75)  */\n\t{ 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xb3, 0xcb, 0xff },\n/* char 0x76 (raw 0x76)  */\n\t{ 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xd7, 0xef, 0xff },\n/* char 0x77 (raw 0x77)  */\n\t{ 0xff, 0xff, 0xbb, 0xbb, 0xab, 0xab, 0x93, 0xff },\n/* char 0x78 (raw 0x78)  */\n\t{ 0xff, 0xff, 0xbb, 0xd7, 0xef, 0xd7, 0xbb, 0xff },\n/* char 0x79 (raw 0x79)  */\n\t{ 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xc3, 0xfb, 0xc7 },\n/* char 0x7a (raw 0x7a)  */\n\t{ 0xff, 0xff, 0x83, 0xf7, 0xef, 0xdf, 0x83, 0xff },\n/* char 0x7b (raw 0x7b)  */\n\t{ 0xe3, 0xcf, 0xcf, 0x9f, 0xcf, 0xcf, 0xe3, 0xff },\n/* char 0x7c (raw 0x7c)  */\n\t{ 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef },\n/* char 0x7d (raw 0x7d)  */\n\t{ 0x8f, 0xe7, 0xe7, 0xf3, 0xe7, 0xe7, 0x8f, 0xff },\n/* char 0x7e (raw 0x7e)  */\n\t{ 0xcb, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },\n/* char 0x7f (raw 0x7f)  */\n\t{ 0xff, 0xab, 0xd7, 0xab, 0xd7, 0xab, 0xff, 0xff },\n/* char 0x80 (raw 0x40)  */\n\t{ 0x38, 0x44, 0x54, 0x5c, 0x58, 0x40, 0x3c, 0x00 },\n/* char 0x81 (raw 0x41)  */\n\t{ 0x10, 0x28, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x00 },\n/* char 0x82 (raw 0x42)  */\n\t{ 0x78, 0x44, 0x44, 0x78, 0x44, 0x44, 0x78, 0x00 },\n/* char 0x83 (raw 0x43)  */\n\t{ 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00 },\n/* char 0x84 (raw 0x44)  */\n\t{ 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00 },\n/* char 0x85 (raw 0x45)  */\n\t{ 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7c, 0x00 },\n/* char 0x86 (raw 0x46)  */\n\t{ 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00 },\n/* char 0x87 (raw 0x47)  */\n\t{ 0x3c, 0x40, 0x40, 0x40, 0x4c, 0x44, 0x3c, 0x00 },\n/* char 0x88 (raw 0x48)  */\n\t{ 0x44, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x00 },\n/* char 0x89 (raw 0x49)  */\n\t{ 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 },\n/* char 0x8a (raw 0x4a)  */\n\t{ 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00 },\n/* char 0x8b (raw 0x4b)  */\n\t{ 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00 },\n/* char 0x8c (raw 0x4c)  */\n\t{ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7c, 0x00 },\n/* char 0x8d (raw 0x4d)  */\n\t{ 0x44, 0x6c, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00 },\n/* char 0x8e (raw 0x4e)  */\n\t{ 0x44, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x00 },\n/* char 0x8f (raw 0x4f)  */\n\t{ 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 },\n/* char 0x90 (raw 0x50)  */\n\t{ 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00 },\n/* char 0x91 (raw 0x51)  */\n\t{ 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00 },\n/* char 0x92 (raw 0x52)  */\n\t{ 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00 },\n/* char 0x93 (raw 0x53)  */\n\t{ 0x38, 0x44, 0x40, 0x38, 0x04, 0x44, 0x38, 0x00 },\n/* char 0x94 (raw 0x54)  */\n\t{ 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00 },\n/* char 0x95 (raw 0x55)  */\n\t{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 },\n/* char 0x96 (raw 0x56)  */\n\t{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 },\n/* char 0x97 (raw 0x57)  */\n\t{ 0x44, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x44, 0x00 },\n/* char 0x98 (raw 0x58)  */\n\t{ 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00 },\n/* char 0x99 (raw 0x59)  */\n\t{ 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x00 },\n/* char 0x9a (raw 0x5a)  */\n\t{ 0x7c, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7c, 0x00 },\n/* char 0x9b (raw 0x5b)  */\n\t{ 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x00 },\n/* char 0x9c (raw 0x5c)  */\n\t{ 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00 },\n/* char 0x9d (raw 0x5d)  */\n\t{ 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00 },\n/* char 0x9e (raw 0x5e)  */\n\t{ 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00 },\n/* char 0x9f (raw 0x5f)  */\n\t{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe },\n/* char 0xa0 (raw 0x20)  */\n\t{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },\n/* char 0xa1 (raw 0x21)  */\n\t{ 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00 },\n/* char 0xa2 (raw 0x22)  */\n\t{ 0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00 },\n/* char 0xa3 (raw 0x23)  */\n\t{ 0x28, 0x28, 0x7c, 0x28, 0x7c, 0x28, 0x28, 0x00 },\n/* char 0xa4 (raw 0x24)  */\n\t{ 0x10, 0x3c, 0x50, 0x38, 0x14, 0x78, 0x10, 0x00 },\n/* char 0xa5 (raw 0x25)  */\n\t{ 0x60, 0x64, 0x08, 0x10, 0x20, 0x4c, 0x0c, 0x00 },\n/* char 0xa6 (raw 0x26)  */\n\t{ 0x20, 0x50, 0x50, 0x20, 0x54, 0x48, 0x34, 0x00 },\n/* char 0xa7 (raw 0x27)  */\n\t{ 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 },\n/* char 0xa8 (raw 0x28)  */\n\t{ 0x10, 0x20, 0x40, 0x40, 0x40, 0x20, 0x10, 0x00 },\n/* char 0xa9 (raw 0x29)  */\n\t{ 0x10, 0x08, 0x04, 0x04, 0x04, 0x08, 0x10, 0x00 },\n/* char 0xaa (raw 0x2a)  */\n\t{ 0x10, 0x54, 0x38, 0x10, 0x38, 0x54, 0x10, 0x00 },\n/* char 0xab (raw 0x2b)  */\n\t{ 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x00 },\n/* char 0xac (raw 0x2c)  */\n\t{ 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x20, 0x00 },\n/* char 0xad (raw 0x2d)  */\n\t{ 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00 },\n/* char 0xae (raw 0x2e)  */\n\t{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 },\n/* char 0xaf (raw 0x2f)  */\n\t{ 0x00, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00 },\n/* char 0xb0 (raw 0x30)  */\n\t{ 0x38, 0x44, 0x4c, 0x54, 0x64, 0x44, 0x38, 0x00 },\n/* char 0xb1 (raw 0x31)  */\n\t{ 0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 },\n/* char 0xb2 (raw 0x32)  */\n\t{ 0x38, 0x44, 0x04, 0x18, 0x20, 0x40, 0x7c, 0x00 },\n/* char 0xb3 (raw 0x33)  */\n\t{ 0x7c, 0x04, 0x08, 0x18, 0x04, 0x44, 0x38, 0x00 },\n/* char 0xb4 (raw 0x34)  */\n\t{ 0x08, 0x18, 0x28, 0x48, 0x7c, 0x08, 0x08, 0x00 },\n/* char 0xb5 (raw 0x35)  */\n\t{ 0x7c, 0x40, 0x78, 0x04, 0x04, 0x44, 0x38, 0x00 },\n/* char 0xb6 (raw 0x36)  */\n\t{ 0x1c, 0x20, 0x40, 0x78, 0x44, 0x44, 0x38, 0x00 },\n/* char 0xb7 (raw 0x37)  */\n\t{ 0x7c, 0x04, 0x08, 0x10, 0x20, 0x20, 0x20, 0x00 },\n/* char 0xb8 (raw 0x38)  */\n\t{ 0x38, 0x44, 0x44, 0x38, 0x44, 0x44, 0x38, 0x00 },\n/* char 0xb9 (raw 0x39)  */\n\t{ 0x38, 0x44, 0x44, 0x3c, 0x04, 0x08, 0x70, 0x00 },\n/* char 0xba (raw 0x3a)  */\n\t{ 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00 },\n/* char 0xbb (raw 0x3b)  */\n\t{ 0x00, 0x00, 0x10, 0x00, 0x10, 0x10, 0x20, 0x00 },\n/* char 0xbc (raw 0x3c)  */\n\t{ 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x00 },\n/* char 0xbd (raw 0x3d)  */\n\t{ 0x00, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x00, 0x00 },\n/* char 0xbe (raw 0x3e)  */\n\t{ 0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x00 },\n/* char 0xbf (raw 0x3f)  */\n\t{ 0x38, 0x44, 0x08, 0x10, 0x10, 0x00, 0x10, 0x00 },\n/* char 0xc0 (raw 0x40)  */\n\t{ 0x38, 0x44, 0x54, 0x5c, 0x58, 0x40, 0x3c, 0x00 },\n/* char 0xc1 (raw 0x41)  */\n\t{ 0x10, 0x28, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x00 },\n/* char 0xc2 (raw 0x42)  */\n\t{ 0x78, 0x44, 0x44, 0x78, 0x44, 0x44, 0x78, 0x00 },\n/* char 0xc3 (raw 0x43)  */\n\t{ 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00 },\n/* char 0xc4 (raw 0x44)  */\n\t{ 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00 },\n/* char 0xc5 (raw 0x45)  */\n\t{ 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7c, 0x00 },\n/* char 0xc6 (raw 0x46)  */\n\t{ 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00 },\n/* char 0xc7 (raw 0x47)  */\n\t{ 0x3c, 0x40, 0x40, 0x40, 0x4c, 0x44, 0x3c, 0x00 },\n/* char 0xc8 (raw 0x48)  */\n\t{ 0x44, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x00 },\n/* char 0xc9 (raw 0x49)  */\n\t{ 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 },\n/* char 0xca (raw 0x4a)  */\n\t{ 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00 },\n/* char 0xcb (raw 0x4b)  */\n\t{ 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00 },\n/* char 0xcc (raw 0x4c)  */\n\t{ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7c, 0x00 },\n/* char 0xcd (raw 0x4d)  */\n\t{ 0x44, 0x6c, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00 },\n/* char 0xce (raw 0x4e)  */\n\t{ 0x44, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x00 },\n/* char 0xcf (raw 0x4f)  */\n\t{ 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 },\n/* char 0xd0 (raw 0x50)  */\n\t{ 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00 },\n/* char 0xd1 (raw 0x51)  */\n\t{ 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00 },\n/* char 0xd2 (raw 0x52)  */\n\t{ 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00 },\n/* char 0xd3 (raw 0x53)  */\n\t{ 0x38, 0x44, 0x40, 0x38, 0x04, 0x44, 0x38, 0x00 },\n/* char 0xd4 (raw 0x54)  */\n\t{ 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00 },\n/* char 0xd5 (raw 0x55)  */\n\t{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 },\n/* char 0xd6 (raw 0x56)  */\n\t{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 },\n/* char 0xd7 (raw 0x57)  */\n\t{ 0x44, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x44, 0x00 },\n/* char 0xd8 (raw 0x58)  */\n\t{ 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00 },\n/* char 0xd9 (raw 0x59)  */\n\t{ 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x00 },\n/* char 0xda (raw 0x5a)  */\n\t{ 0x7c, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7c, 0x00 },\n/* char 0xdb (raw 0x5b)  */\n\t{ 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x00 },\n/* char 0xdc (raw 0x5c)  */\n\t{ 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00 },\n/* char 0xdd (raw 0x5d)  */\n\t{ 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00 },\n/* char 0xde (raw 0x5e)  */\n\t{ 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00 },\n/* char 0xdf (raw 0x5f)  */\n\t{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe },\n/* char 0xe0 (raw 0x60)  */\n\t{ 0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },\n/* char 0xe1 (raw 0x61)  */\n\t{ 0x00, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00 },\n/* char 0xe2 (raw 0x62)  */\n\t{ 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x78, 0x00 },\n/* char 0xe3 (raw 0x63)  */\n\t{ 0x00, 0x00, 0x3c, 0x40, 0x40, 0x40, 0x3c, 0x00 },\n/* char 0xe4 (raw 0x64)  */\n\t{ 0x04, 0x04, 0x3c, 0x44, 0x44, 0x44, 0x3c, 0x00 },\n/* char 0xe5 (raw 0x65)  */\n\t{ 0x00, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00 },\n/* char 0xe6 (raw 0x66)  */\n\t{ 0x18, 0x24, 0x20, 0x78, 0x20, 0x20, 0x20, 0x00 },\n/* char 0xe7 (raw 0x67)  */\n\t{ 0x00, 0x00, 0x38, 0x44, 0x44, 0x3c, 0x04, 0x38 },\n/* char 0xe8 (raw 0x68)  */\n\t{ 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00 },\n/* char 0xe9 (raw 0x69)  */\n\t{ 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x38, 0x00 },\n/* char 0xea (raw 0x6a)  */\n\t{ 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x48, 0x30 },\n/* char 0xeb (raw 0x6b)  */\n\t{ 0x40, 0x40, 0x44, 0x48, 0x70, 0x48, 0x44, 0x00 },\n/* char 0xec (raw 0x6c)  */\n\t{ 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 },\n/* char 0xed (raw 0x6d)  */\n\t{ 0x00, 0x00, 0x6c, 0x54, 0x54, 0x54, 0x44, 0x00 },\n/* char 0xee (raw 0x6e)  */\n\t{ 0x00, 0x00, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00 },\n/* char 0xef (raw 0x6f)  */\n\t{ 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00 },\n/* char 0xf0 (raw 0x70)  */\n\t{ 0x00, 0x00, 0x78, 0x44, 0x44, 0x78, 0x40, 0x40 },\n/* char 0xf1 (raw 0x71)  */\n\t{ 0x00, 0x00, 0x3c, 0x44, 0x44, 0x3c, 0x04, 0x04 },\n/* char 0xf2 (raw 0x72)  */\n\t{ 0x00, 0x00, 0x5c, 0x60, 0x40, 0x40, 0x40, 0x00 },\n/* char 0xf3 (raw 0x73)  */\n\t{ 0x00, 0x00, 0x3c, 0x40, 0x38, 0x04, 0x78, 0x00 },\n/* char 0xf4 (raw 0x74)  */\n\t{ 0x20, 0x20, 0x78, 0x20, 0x20, 0x24, 0x18, 0x00 },\n/* char 0xf5 (raw 0x75)  */\n\t{ 0x00, 0x00, 0x44, 0x44, 0x44, 0x4c, 0x34, 0x00 },\n/* char 0xf6 (raw 0x76)  */\n\t{ 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 },\n/* char 0xf7 (raw 0x77)  */\n\t{ 0x00, 0x00, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x00 },\n/* char 0xf8 (raw 0x78)  */\n\t{ 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00 },\n/* char 0xf9 (raw 0x79)  */\n\t{ 0x00, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x04, 0x38 },\n/* char 0xfa (raw 0x7a)  */\n\t{ 0x00, 0x00, 0x7c, 0x08, 0x10, 0x20, 0x7c, 0x00 },\n/* char 0xfb (raw 0x7b)  */\n\t{ 0x1c, 0x30, 0x30, 0x60, 0x30, 0x30, 0x1c, 0x00 },\n/* char 0xfc (raw 0x7c)  */\n\t{ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 },\n/* char 0xfd (raw 0x7d)  */\n\t{ 0x70, 0x18, 0x18, 0x0c, 0x18, 0x18, 0x70, 0x00 },\n/* char 0xfe (raw 0x7e)  */\n\t{ 0x34, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },\n/* char 0xff (raw 0x7f)  */\n\t{ 0x00, 0x54, 0x28, 0x54, 0x28, 0x54, 0x00, 0x00 },\n"
  },
  {
    "path": "gsplus/src/kegswin.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 17\r\nVisualStudioVersion = 17.0.32112.339\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"kegswin\", \"kegswin.vcxproj\", \"{C24D318A-501E-4B8C-901A-15977CB9C00C}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|x64 = Debug|x64\r\n\t\tDebug|x86 = Debug|x86\r\n\t\tRelease|x64 = Release|x64\r\n\t\tRelease|x86 = Release|x86\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x64.ActiveCfg = Release|x64\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x64.Build.0 = Release|x64\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x86.ActiveCfg = Debug|Win32\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x86.Build.0 = Debug|Win32\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x64.ActiveCfg = Release|x64\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x64.Build.0 = Release|x64\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x86.ActiveCfg = Release|Win32\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x86.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {32BE13D0-68A7-486D-874C-EA158125775B}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "gsplus/src/kegswin.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <VCProjectVersion>17.0</VCProjectVersion>\r\n    <ProjectGuid>{C24D318A-501E-4B8C-901A-15977CB9C00C}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n      <Optimization>Disabled</Optimization>\r\n    </ClCompile>\r\n    <Link>\r\n      <TargetMachine>MachineX86</TargetMachine>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <SubSystem>Windows</SubSystem>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n    </ClCompile>\r\n    <Link>\r\n      <TargetMachine>MachineX86</TargetMachine>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <AdditionalDependencies>wsock32.lib;dsound.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Link>\r\n      <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;wsock32.lib;dsound.lib;winmm.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n      <SubSystem>Console</SubSystem>\r\n    </Link>\r\n    <ClCompile>\r\n      <TreatWarningAsError>true</TreatWarningAsError>\r\n      <WarningLevel>Level3</WarningLevel>\r\n    </ClCompile>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"adb.c\" />\r\n    <ClCompile Include=\"applesingle.c\" />\r\n    <ClCompile Include=\"clock.c\" />\r\n    <ClCompile Include=\"compile_time.c\" />\r\n    <ClCompile Include=\"config.c\" />\r\n    <ClCompile Include=\"debugger.c\" />\r\n    <ClCompile Include=\"dynapro.c\" />\r\n    <ClCompile Include=\"dyna_filt.c\" />\r\n    <ClCompile Include=\"dyna_type.c\" />\r\n    <ClCompile Include=\"dyna_validate.c\" />\r\n    <ClCompile Include=\"engine_c.c\" />\r\n    <ClCompile Include=\"iwm.c\" />\r\n    <ClCompile Include=\"joystick_driver.c\" />\r\n    <ClCompile Include=\"mockingboard.c\" />\r\n    <ClCompile Include=\"moremem.c\" />\r\n    <ClCompile Include=\"paddles.c\" />\r\n    <ClCompile Include=\"scc.c\" />\r\n    <ClCompile Include=\"scc_unixdriver.c\" />\r\n    <ClCompile Include=\"scc_socket_driver.c\" />\r\n    <ClCompile Include=\"scc_windriver.c\" />\r\n    <ClCompile Include=\"sim65816.c\" />\r\n    <ClCompile Include=\"smartport.c\" />\r\n    <ClCompile Include=\"doc.c\" />\r\n    <ClCompile Include=\"sound.c\" />\r\n    <ClCompile Include=\"sound_driver.c\" />\r\n    <ClCompile Include=\"undeflate.c\" />\r\n    <ClCompile Include=\"unshk.c\" />\r\n    <ClCompile Include=\"video.c\" />\r\n    <ClCompile Include=\"voc.c\" />\r\n    <ClCompile Include=\"win32snd_driver.c\" />\r\n    <ClCompile Include=\"windriver.c\" />\r\n    <ClCompile Include=\"woz.c\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"config.h\" />\r\n    <ClInclude Include=\"defc.h\" />\r\n    <ClInclude Include=\"defcomm.h\" />\r\n    <ClInclude Include=\"defs_instr.h\" />\r\n    <ClInclude Include=\"disas.h\" />\r\n    <ClInclude Include=\"engine.h\" />\r\n    <ClInclude Include=\"instable.h\" />\r\n    <ClInclude Include=\"iwm.h\" />\r\n    <ClInclude Include=\"Kegs-Bridging-Header.h\" />\r\n    <ClInclude Include=\"kegsfont.h\" />\r\n    <ClInclude Include=\"op_routs.h\" />\r\n    <ClInclude Include=\"protos.h\" />\r\n    <ClInclude Include=\"protos_base.h\" />\r\n    <ClInclude Include=\"protos_windriver.h\" />\r\n    <ClInclude Include=\"protos_xdriver.h\" />\r\n    <ClInclude Include=\"scc.h\" />\r\n    <ClInclude Include=\"size_c.h\" />\r\n    <ClInclude Include=\"sound.h\" />\r\n    <ClInclude Include=\"winresource.h\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>\n"
  },
  {
    "path": "gsplus/src/ldvars",
    "content": "OBJECTS = adb.o engine_c.o clock.o config.o debugger.o scc.o scc_socket_driver.o scc_windriver.o scc_unixdriver.o iwm.o joystick_driver.o moremem.o paddles.o mockingboard.o sim65816.o smartport.o doc.o sound.o sound_driver.o woz.o unshk.o undeflate.o dynapro.o dyna_type.o dyna_filt.o dyna_validate.o applesingle.o video.o voc.o\nPROJROOT = ..\n"
  },
  {
    "path": "gsplus/src/macsnd_driver.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2022 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// Headers at: /Applications/Xcode.app/Contents/Developer/Platforms/\n//      MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/\n//      Frameworks/AudioToolbox.framework/Headers\n\n// Some ideas from SDL code:\n//  http://www-personal.umich.edu/~bazald/l/api/_s_d_l__coreaudio_8c_source.html\n\n#include \"defc.h\"\n#include \"sound.h\"\n\n#include <AudioToolbox/AudioToolbox.h>\n#include <CoreAudio/CoreAudio.h>\n#include <unistd.h>\n\n// Mac OS X 12.0 deprecates kAudioObjectPropertyElementMaster.  So now we\n//  need to use kAudioObjectPropertyElementMain, and set it to ...Master if it\n//  isn't already set.  This is beyond dumb\n#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED)\n# if __MAC_OS_X_VERSION_MAX_ALLOWED < 120000\n#  define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster\n# endif\n#endif\n\n#define MACSND_REBUF_SIZE\t(64*1024)\n\nword32\tg_macsnd_rebuf[MACSND_REBUF_SIZE];\nvolatile int g_macsnd_rd_pos;\nvolatile int g_macsnd_wr_pos;\nvolatile int g_macsnd_playing = 0;\nint g_macsnd_channel_warn = 0;\nextern int g_sound_min_samples;\t\t\t// About 33ms\nextern int g_sound_max_multiplier;\t\t// About 6, so 6*33 ~= 200ms.\n\nextern int Verbose;\n\nextern int g_preferred_rate;\nextern word32 *g_sound_shm_addr;\nextern int g_sound_size;\n\nint g_call_num = 0;\n\nAURenderCallbackStruct g_callback_struct = { 0 };\n\nstatic OSStatus\naudio_callback(void *in_ref_ptr, AudioUnitRenderActionFlags *aura_flags_ptr,\n\t\tconst AudioTimeStamp *a_timestamp_ptr, UInt32 bus_number,\n\t\tUInt32 in_num_frame, AudioBufferList *abuflist_ptr)\n{\n\tword32\t*wptr;\n\tint\tnum_buffers, num_channels, num_samps, sample_num, samps_avail;\n\tint\tmax_samples, min_samples;\n\tint\ti, j;\n\n\tif(in_ref_ptr || aura_flags_ptr || a_timestamp_ptr || bus_number) {\n\t\t// Avoid unused parameter warning\n\t}\n#if 0\n\tprintf(\"CB: audio_callback called, in_ref:%p, bus:%d, in_frame:%d\\n\",\n\t\tin_ref_ptr, bus_number, in_num_frame);\n#endif\n\tnum_buffers = abuflist_ptr->mNumberBuffers;\n\tmin_samples = g_sound_min_samples;\n\tmax_samples = min_samples * g_sound_max_multiplier;\n#if 0\n\tprintf(\"CB: num_buffers: %d. sample_time:%lf, host_time:%016llx \"\n\t\t\"rate_scalar:%lf, word_clock_time:%016llx, flags:%04x\\n\",\n\t\tnum_buffers, a_timestamp_ptr->mSampleTime,\n\t\ta_timestamp_ptr->mHostTime, a_timestamp_ptr->mRateScalar,\n\t\ta_timestamp_ptr->mWordClockTime, a_timestamp_ptr->mFlags);\n#endif\n\n\tsample_num = g_macsnd_rd_pos;\n\tsamps_avail = (g_macsnd_wr_pos - sample_num) & (MACSND_REBUF_SIZE - 1);\n\tif((int)in_num_frame > samps_avail) {\n\t\t// We don't have enough samples, must pause\n\t\tg_macsnd_playing = 0;\n\t\tsample_num = g_macsnd_wr_pos;\t\t// Eat remaining samps\n\t}\n\tif((g_macsnd_playing == 0) &&\n\t\t\t(samps_avail > (min_samples + (int)in_num_frame))) {\n\t\t// We can unpause\n\t\tg_macsnd_playing = 1;\n\t}\n\tif(g_macsnd_playing && (samps_avail > max_samples)) {\n\t\tprintf(\"JUMP SAMPLE_NUM by %d samples!\\n\", max_samples / 2);\n\t\tsample_num += (max_samples / 2);\n\t\tsample_num = sample_num & (MACSND_REBUF_SIZE - 1);\n\t}\n#if 0\n\tprintf(\"CB: in_frame:%d, samps_avail:%d, playing:%d\\n\", in_num_frame,\n\t\t\tsamps_avail, g_macsnd_playing);\n#endif\n\n\tfor(i = 0; i < num_buffers; i++) {\n\t\tnum_channels = abuflist_ptr->mBuffers[i].mNumberChannels;\n\t\tif((num_channels != 2) && !g_macsnd_channel_warn) {\n\t\t\tprintf(\"mNumberChannels:%d\\n\", num_channels);\n\t\t\tg_macsnd_channel_warn = 1;\n\t\t}\n\t\tnum_samps = abuflist_ptr->mBuffers[i].mDataByteSize / 4;\n\t\twptr = (word32 *)abuflist_ptr->mBuffers[i].mData;\n#if 0\n\t\tprintf(\"CB buf[%d]: num_ch:%d, num_samps:%d, wptr:%p\\n\", i,\n\t\t\tnum_channels, num_samps, wptr);\n#endif\n#if 0\n\t\tif((g_call_num & 31) == 0) {\n\t\t\tprintf(\"%d play %d samples, samps_avail:%d\\n\",\n\t\t\t\tg_call_num, num_samps, samps_avail);\n\t\t}\n#endif\n\t\tif(g_macsnd_playing) {\n\t\t\tfor(j = 0; j < num_samps; j++) {\n\t\t\t\twptr[j] = g_macsnd_rebuf[sample_num];\n\t\t\t\tsample_num++;\n\t\t\t\tif(sample_num >= MACSND_REBUF_SIZE) {\n\t\t\t\t\tsample_num = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor(j = 0; j < num_samps; j++) {\n\t\t\t\twptr[j] = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tg_call_num++;\n\tg_macsnd_rd_pos = sample_num;\n\n\treturn 0;\n}\n\nint\nmac_send_audio(byte *ptr, int in_size)\n{\n\tword32\t*wptr, *macptr;\n\tint\tsamps, sample_num;\n\tint\ti;\n\n\tsamps = in_size / 4;\n\twptr = (word32 *)ptr;\n\tsample_num = g_macsnd_wr_pos;\n\tmacptr = &(g_macsnd_rebuf[0]);\n\tfor(i = 0; i < samps; i++) {\n\t\tmacptr[sample_num] = *wptr++;\n\t\tsample_num++;\n\t\tif(sample_num >= MACSND_REBUF_SIZE) {\n\t\t\tsample_num = 0;\n\t\t}\n\t}\n\n\tg_macsnd_wr_pos = sample_num;\n\n\treturn in_size;\n}\n\nAudioObjectPropertyAddress g_aopa = { 0 };\n\nvoid\nmacsnd_init()\n{\n\tAudioComponentInstance ac_inst;\n\tAudioStreamBasicDescription *str_desc_ptr;\n\tAudioComponentDescription *ac_descr_ptr;\n\tAudioComponent\tac;\n\tAudioDeviceID\tdevice;\n\tOSStatus\tresult;\n\tUInt32\t\tsize;\n\n\tg_macsnd_rd_pos = 0;\n\tg_macsnd_wr_pos = 0;\n\tmac_printf(\"macsnd_init called\\n\");\n\n\tg_aopa.mSelector = kAudioHardwarePropertyDefaultOutputDevice;\n\tg_aopa.mScope = kAudioObjectPropertyScopeGlobal;\n\tg_aopa.mElement = kAudioObjectPropertyElementMain;\n\n\tsize = 4;\n\tresult = AudioObjectGetPropertyData(kAudioObjectSystemObject, &g_aopa,\n\t\t\t\t\t0, 0, &size, &device);\n\tif(result != 0) {\n\t\tprintf(\"AudioObjectGetPropertData on DefaultOutputDevice:%d\\n\",\n\t\t\t\t\t\t\t\t\tresult);\n\t\treturn;\n\t}\n\tmac_printf(\"Audio Device number: %d\\n\", device);\n\n\tstr_desc_ptr = calloc(1, sizeof(AudioStreamBasicDescription));\n\tstr_desc_ptr->mFormatID = kAudioFormatLinearPCM;\n\tstr_desc_ptr->mFormatFlags = kLinearPCMFormatFlagIsPacked |\n\t\t\t\t\tkLinearPCMFormatFlagIsSignedInteger;\n\tstr_desc_ptr->mChannelsPerFrame = 2;\n\tstr_desc_ptr->mSampleRate = g_preferred_rate;\n\tstr_desc_ptr->mFramesPerPacket = 1;\n\tstr_desc_ptr->mBitsPerChannel = 16;\t\t// 16-bit samples\n\tstr_desc_ptr->mBytesPerFrame = (16 * 2) / 8;\n\tstr_desc_ptr->mBytesPerPacket = str_desc_ptr->mBytesPerFrame;\n\n\tac_descr_ptr = calloc(1, sizeof(AudioComponentDescription));\n\tac_descr_ptr->componentType = kAudioUnitType_Output;\n\tac_descr_ptr->componentManufacturer = kAudioUnitManufacturer_Apple;\n\tac_descr_ptr->componentSubType = kAudioUnitSubType_DefaultOutput;\n\tac = AudioComponentFindNext(0, ac_descr_ptr);\n\tmac_printf(\"AudioComponentFindNext ret: %p\\n\", ac);\n\tif(ac == 0) {\n\t\texit(1);\n\t}\n\n\tresult = AudioComponentInstanceNew(ac, &ac_inst);\n\tmac_printf(\"AudioComponentInstanceNew ret:%d\\n\", result);\n\tif(result != 0) {\n\t\texit(1);\n\t}\n\n\tresult = AudioUnitSetProperty(ac_inst,\n\t\t\tkAudioOutputUnitProperty_CurrentDevice,\n\t\t\tkAudioUnitScope_Global, 0, &device,\n\t\t\tsizeof(device));\n\tmac_printf(\"AudioUnitSetProperty CurrentDevice ret: %d\\n\", result);\n\tif(result != 0) {\n\t\texit(1);\n\t}\n\n\tresult = AudioUnitSetProperty(ac_inst,\n\t\t\tkAudioUnitProperty_StreamFormat,\n\t\t\tkAudioUnitScope_Input, 0, str_desc_ptr,\n\t\t\tsizeof(*str_desc_ptr));\n\tmac_printf(\"AudioUnitSetProperty StreamFormat ret: %d\\n\", result);\n\tif(result != 0) {\n\t\texit(1);\n\t}\n\n\tg_callback_struct.inputProc = audio_callback;\n\tg_callback_struct.inputProcRefCon = (void *)1;\n\tresult = AudioUnitSetProperty(ac_inst,\n\t\t\tkAudioUnitProperty_SetRenderCallback,\n\t\t\tkAudioUnitScope_Input, 0, &g_callback_struct,\n\t\t\tsizeof(g_callback_struct));\n\n\tmac_printf(\"AudioUnitSetProperty SetRenderCallback ret: %d\\n\", result);\n\tif(result != 0) {\n\t\texit(1);\n\t}\n\n\tresult = AudioUnitInitialize(ac_inst);\n\tmac_printf(\"AudioUnitInitialize ret: %d\\n\", result);\n\tif(result != 0) {\n\t\texit(1);\n\t}\n\n\tresult = AudioOutputUnitStart(ac_inst);\n\tmac_printf(\"AudioOutputUnitStart ret: %d\\n\", result);\n\tif(result != 0) {\n\t\texit(1);\n\t}\n\n\tsound_set_audio_rate(g_preferred_rate);\n\n\tmac_printf(\"End of child_sound_init_mac\\n\");\n\tfflush(stdout);\n}\n\n"
  },
  {
    "path": "gsplus/src/mockingboard.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include \"defc.h\"\n#include \"sound.h\"\n\n// Mockingboard contains two pairs of a 6522/AY-8913, where the 6522 interfaces\n//  the AY-8913 (which makes the sounds) to the Apple II.  Each AY-8913\n//  contains 3 channels of sound: A,B,C.  Model each pair separately.\n// The AY-8913 has 16 registers.  The documentation numbers them using octal!\n// The AY8913 is accessed using ORB of the 6522 as control: 0 = Reset, 4=Idle\n//\t5=Reg read; 6=Write reg; 7=Latch reg address\n\nextern Mockingboard g_mockingboard;\ndword64\tg_mockingboard_last_int_dusec = 0ULL;\ndword64\tg_mockingboard_event_int_dusec = 0ULL;\t// 0 -> no event pending\n\nextern int g_irq_pending;\nextern double g_dsamps_per_dfcyc;\n\nvoid\nmock_ay8913_reset(int pair_num, dword64 dfcyc)\n{\n\tAy8913\t*ay8913ptr;\n\tint\ti;\n\n\tif(dfcyc) {\n\t\t// Avoid unused parameter warning\n\t}\n\tay8913ptr = &(g_mockingboard.pair[pair_num].ay8913);\n\tay8913ptr->reg_addr_latch = 16;\t\t// out-of-range, mb-audit1.3\n\tfor(i = 0; i < 16; i++) {\n\t\tay8913ptr->regs[i] = 0;\n\t}\n\tfor(i = 0; i < 3; i++) {\n\t\tay8913ptr->toggle_tone[i] = 0;\n\t\tay8913ptr->tone_samp[i] = 0;\n\t}\n\tay8913ptr->noise_val = 0x12345678;\n\tay8913ptr->noise_samp = 0;\n\tay8913ptr->env_dsamp = 0;\n}\n\nvoid\nmockingboard_reset(dword64 dfcyc)\n{\n\tword32\ttimer1_latch;\n\n\ttimer1_latch = g_mockingboard.pair[0].mos6522.timer1_latch;\n\tmemset(&g_mockingboard, 0, sizeof(g_mockingboard));\n\n\tg_mockingboard_last_int_dusec = (dfcyc >> 16) << 16;\n\tif(g_mockingboard_event_int_dusec != 0) {\n\t\t(void)remove_event_mockingboard();\n\t}\n\tg_mockingboard_event_int_dusec = 0;\n\tprintf(\"At reset, timer1_latch: %08x\\n\", timer1_latch);\n\tif((timer1_latch & 0xffff) == 0x234) {\t\t\t// MB-audit\n\t\tg_mockingboard.pair[0].mos6522.timer1_latch = timer1_latch;\n\t} else {\n\t\tg_mockingboard.pair[0].mos6522.timer1_latch = 0xff00;\n\t}\n\tg_mockingboard.pair[0].mos6522.timer1_counter = 0x2ff00;\n\tg_mockingboard.pair[0].mos6522.timer2_counter = 0x2ff00;\n\tg_mockingboard.pair[1].mos6522.timer1_latch = 0xff00;\n\tg_mockingboard.pair[1].mos6522.timer1_counter = 0x2ff00;\n\tg_mockingboard.pair[1].mos6522.timer2_counter = 0x2ff00;\n\n\tmock_ay8913_reset(0, dfcyc);\n\tmock_ay8913_reset(1, dfcyc);\n}\n\nvoid\nmock_show_pair(int pair_num, dword64 dfcyc, const char *str)\n{\n\tMos6522\t*mos6522ptr;\n\n\tmos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);\n\tprintf(\"Mock %d %s, t1_lat:%05x, t1_c:%05x, t2_l:%05x t2_c:%05x, ifr:\"\n\t\t\"%02x, acr:%02x, ier:%02x\\n\", pair_num, str,\n\t\tmos6522ptr->timer1_latch, mos6522ptr->timer1_counter,\n\t\tmos6522ptr->timer2_latch, mos6522ptr->timer2_counter,\n\t\tmos6522ptr->ifr, mos6522ptr->acr, mos6522ptr->ier);\n\tprintf(\"  dfcyc:%016llx, event_int:%lld\\n\", dfcyc,\n\t\t\t\t\tg_mockingboard_event_int_dusec);\n\n}\n\n// Timers work as follows: if written with '10' in cycle 0, on cycle 1 the\n//  counters loads with '10', and counts down to 9 on cycle 2...down to 1\n//  on cycle 10, then 0 on cycle 11, and then signals an interrupt on cycle 12\n// To handle this, timers are \"read value + 1\" so that timer==0 means the\n//  timer should interrupt right now.  So to write '10' to a timer, the code\n//  needs to write 10+2=12 to the variable, so that on the next cycle, it is\n//  11, which will be returned as 10 to software reading the reg.\n\nvoid\nmock_update_timers(int doit, dword64 dfcyc)\n{\n\tMos6522\t*mos6522ptr;\n\tdword64\tdusec, ddiff, dleft, timer1_int_dusec, timer2_int_dusec;\n\tdword64\tclosest_int_dusec, event_int_dusec;\n\tword32\ttimer_val, ier, timer_eff, timer_latch, log_ifr;\n\tint\ti;\n\n\tdbg_log_info(dfcyc, doit, g_mockingboard.pair[0].mos6522.ifr |\n\t\t\t(g_mockingboard.pair[0].mos6522.ier << 8), 0xc0);\n\n\tdusec = dfcyc >> 16;\n\tddiff = dusec - g_mockingboard_last_int_dusec;\n\tif(!doit && (dusec <= g_mockingboard_last_int_dusec)) {\n\t\treturn;\t\t\t\t\t// Nothing more to do\n\t}\n\n\t// printf(\"mock_update_timers at %lf %016llx, ddiff:%llx\\n\", dfcyc,\n\t//\t\t\t\t\t\tdusec, ddiff);\n\t// Update timers by ddiff integer cycles, calculate next event time\n\tg_mockingboard_last_int_dusec = dusec;\n\tclosest_int_dusec = 0;\n\tfor(i = 0; i < 2; i++) {\t\t// pair_num\n\t\tmos6522ptr = &(g_mockingboard.pair[i].mos6522);\n\t\ttimer1_int_dusec = 0;\n\t\ttimer_val = mos6522ptr->timer1_counter;\n\t\tier = mos6522ptr->ier;\n\t\ttimer_eff = (timer_val & 0x1ffff);\n\t\tdleft = ddiff;\n\t\ttimer_latch = mos6522ptr->timer1_latch + 2;\n\t\tdbg_log_info(dfcyc, (word32)dleft, timer_val, 0xcb);\n\t\tdbg_log_info(dfcyc, ier, timer_latch, 0xcb);\n\t\tif(dleft < timer_eff) {\n\t\t\t// Move ahead only a little, no triggering\n\t\t\ttimer_val = timer_val - (word32)dleft;\n\t\t\tif(ddiff) {\n\t\t\t\t// printf(\"New timer1_val:%05x, dleft:%08llx\\n\",\n\t\t\t\t//\t\ttimer_val, dleft);\n\t\t\t}\n\t\t\tif(((mos6522ptr->ifr & 0x40) == 0) && (ier & 0x40)) {\n\t\t\t\t// IFR not set yet, prepare an event\n\t\t\t\ttimer1_int_dusec = dusec +\n\t\t\t\t\t\t\t(timer_val & 0x1ffff);\n\t\t\t\tdbg_log_info(dfcyc, (word32)timer1_int_dusec,\n\t\t\t\t\t\t\ttimer_val, 0xcd);\n\t\t\t\t// printf(\"t1_int_dusec: %016llx\\n\",\n\t\t\t\t//\t\t\ttimer1_int_dusec);\n\t\t\t}\n\t\t} else {\n\t\t\t// Timer1 has triggered now (maybe rolled over more\n\t\t\t//  than once).\n\t\t\tlog_ifr = 0;\n\t\t\tif((timer_val & 0x20000) == 0) {\n\t\t\t\t// Either free-running, or not one-shot already\n\t\t\t\t//  triggered\n\t\t\t\t// Set interrupt: Ensure IFR | 0x40 is set\n\t\t\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, i,\n\t\t\t\t\t\tmos6522ptr->ifr | 0x40, ier);\n\t\t\t\tlog_ifr = 1;\n\t\t\t}\n\t\t\tdleft -= timer_eff;\n\t\t\tif(dleft >= timer_latch) {\n\t\t\t\t// It's rolled over several times, remove those\n\t\t\t\tdleft = dleft % timer_latch;\n\t\t\t\tdbg_log_info(dfcyc, (word32)dleft, timer_latch,\n\t\t\t\t\t\t\t\t\t0xcc);\n\t\t\t}\n\t\t\tif(dleft == 0) {\n\t\t\t\tdleft = timer_latch;\n\t\t\t}\n\t\t\ttimer_val = (timer_latch - dleft) & 0x1ffff;\n\t\t\tif((mos6522ptr->acr & 0x40) == 0) {\n\t\t\t\t// ACR6=0: One shot mode, mark it as triggered\n\t\t\t\ttimer_val |= 0x20000;\n\t\t\t}\n\t\t\tif(log_ifr) {\n\t\t\t\tdbg_log_info(dfcyc,\n\t\t\t\t\tmos6522ptr->ifr | (ier << 8),\n\t\t\t\t\ttimer_val, 0xc3);\n\t\t\t}\n\t\t}\n\n#if 0\n\t\tprintf(\"%016llx ch%d timer1 was %05x, now %05x\\n\", dfcyc, i,\n\t\t\t\t\tmos6522ptr->timer1_counter, timer_val);\n#endif\n\n\t\tmos6522ptr->timer1_counter = timer_val;\n\t\tdbg_log_info(dfcyc, timer_val, timer_latch, 0xce);\n\n\t\t// Handle timer2\n\t\ttimer2_int_dusec = 0;\n\t\ttimer_val = mos6522ptr->timer2_counter;\n\t\ttimer_eff = timer_val & 0x1ffff;\n\t\tdleft = ddiff;\n\t\tif(mos6522ptr->acr & 0x20) {\n\t\t\t// Count pulses mode.  Just don't count\n\t\t\tdleft = 0;\n\t\t}\n\t\tif(dleft < timer_eff) {\n\t\t\t// Move ahead only a little, no triggering\n\t\t\ttimer_val = timer_val - (word32)dleft;\n\t\t\tif(((mos6522ptr->ifr & 0x20) == 0) && (ier & 0x20)) {\n\t\t\t\t// IFR not set yet, prepare an event\n\t\t\t\ttimer2_int_dusec = dusec +\n\t\t\t\t\t\t\t(timer_val & 0x1ffff);\n\t\t\t\t//printf(\"t2_int_dusec: %016llx\\n\",\n\t\t\t\t//\t\t\ttimer1_int_dusec);\n\t\t\t}\n\t\t} else if(timer_val & 0x20000) {\n\t\t\t// And already triggered once, just update count\n\t\t\ttimer_val = ((timer_eff - dleft) & 0xffff) | 0x20000;\n\t\t} else {\n\t\t\t// Has not triggered once yet, but it will now\n\t\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, i,\n\t\t\t\t\t\tmos6522ptr->ifr | 0x20, ier);\n\t\t\ttimer_val = ((timer_val - dleft) & 0xffff) | 0x20000;\n\t\t\tdbg_log_info(dfcyc, mos6522ptr->ifr | (ier << 8),\n\t\t\t\t\ttimer_val, 0xc4);\n\t\t}\n\n\t\t// printf(\"ch%d timer2 was %05x, now %05x\\n\", i,\n\t\t//\t\t\tmos6522ptr->timer2_counter, timer_val);\n\n\t\tmos6522ptr->timer2_counter = timer_val;\n\n\t\tif(timer1_int_dusec && timer2_int_dusec) {\n\t\t\ttimer1_int_dusec = MY_MIN(timer1_int_dusec,\n\t\t\t\t\t\t\ttimer2_int_dusec);\n\t\t}\n\t\tif(timer1_int_dusec) {\n\t\t\tif(closest_int_dusec) {\n\t\t\t\tclosest_int_dusec = MY_MIN(closest_int_dusec,\n\t\t\t\t\t\t\ttimer1_int_dusec);\n\t\t\t} else {\n\t\t\t\tclosest_int_dusec = timer1_int_dusec;\n\t\t\t}\n\t\t}\n\t}\n\n\tevent_int_dusec = g_mockingboard_event_int_dusec;\n\tif(closest_int_dusec) {\n\t\t// See if this is sooner than the current pending event\n\t\t// printf(\"closest_int_dusec: %016llx, event_int:%016llx\\n\",\n\t\t//\t\t\tclosest_int_dusec, event_int_dusec);\n\t\tdoit = 0;\n\t\tif(event_int_dusec && (closest_int_dusec < event_int_dusec)) {\n\t\t\t// There was a pending event.  Discard it\n\t\t\t// printf(\"Call remove_event_mockingboard\\n\");\n\t\t\tremove_event_mockingboard();\n\t\t\tdoit = 1;\n\t\t}\n\t\tif(!event_int_dusec || doit) {\n\t\t\t//printf(\"Call add_event_mockingboard: %016llx %lld\\n\",\n\t\t\t//\tclosest_int_dusec, closest_int_dusec);\n\t\t\tadd_event_mockingboard(closest_int_dusec << 16);\n\t\t\tg_mockingboard_event_int_dusec = closest_int_dusec;\n\t\t\tdbg_log_info(dfcyc,\n\t\t\t\t(word32)(closest_int_dusec - (dfcyc >> 16)),\n\t\t\t\t\t\t\t\t0, 0xc1);\n\t\t}\n\t}\n}\n\nvoid\nmockingboard_event(dword64 dfcyc)\n{\n\t// Received an event--we believe we may need to set an IRQ now.\n\t// Event was already removed from the event queue\n\t// printf(\"Mockingboard_event received at %016llx\\n\", dfcyc);\n\tdbg_log_info(dfcyc, 0, 0, 0xc2);\n\tg_mockingboard_event_int_dusec = 0;\n\tmock_update_timers(1, dfcyc);\n}\n\nword32\nmockingboard_read(word32 loc, dword64 dfcyc)\n{\n\tint\tpair_num;\n\n\t// printf(\"mockingboard read: %04x\\n\", loc);\n\tpair_num = (loc >> 7) & 1;\t\t// 0 or 1\n\treturn mock_6522_read(pair_num, loc & 0xf, dfcyc);\n}\n\nvoid\nmockingboard_write(word32 loc, word32 val, dword64 dfcyc)\n{\n\tint\tpair_num;\n\n\t// printf(\"mockingboard write: %04x=%02x\\n\", loc, val);\n\tpair_num = (loc >> 7) & 1;\t\t// 0 or 1\n\tmock_6522_write(pair_num, loc & 0xf, val, dfcyc);\n}\n\nword32\nmock_6522_read(int pair_num, word32 loc, dword64 dfcyc)\n{\n\tMos6522\t*mos6522ptr;\n\tword32\tval;\n\n\t// Read from 6522 #pair_num loc (0-15)\n\tmos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);\n\tval = 0;\n\tswitch(loc) {\n\tcase 0x0:\t\t// ORB/IRB\n\t\t// Connected to AY8913 { RESET, BDIR, BC1 }\n\t\tval = mos6522ptr->orb;\n\t\t\t// There are no outputs from AY8913 to the 6522 B Port\n\t\tbreak;\n\tcase 0x1:\t\t// ORA\n\tcase 0xf:\t\t// ORA, no handshake\n\t\tval = mos6522ptr->ora;\n\t\tbreak;\n\tcase 0x2:\t\t// DDRB\n\t\tval = mos6522ptr->ddrb;\n\t\tbreak;\n\tcase 0x3:\t\t// DDRA\n\t\tval = mos6522ptr->ddra;\n\t\tbreak;\n\tcase 0x4:\t\t// T1C-L (timer[0])\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = (mos6522ptr->timer1_counter - 1) & 0xff;\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\t\tmos6522ptr->ifr & (~0x40), mos6522ptr->ier);\n\t\t\t// Clear Bit 6\n\t\tmock_update_timers(1, dfcyc);\t// Prepare another int (maybe)\n\t\tdbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc5);\n\t\tbreak;\n\tcase 0x5:\t\t// T1C-H\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = ((mos6522ptr->timer1_counter - 1) >> 8) & 0xff;\n\t\tbreak;\n\tcase 0x6:\t\t// T1L-L\n\t\tval = mos6522ptr->timer1_latch & 0xff;\n\t\tbreak;\n\tcase 0x7:\t\t// T1L-H\n\t\tval = (mos6522ptr->timer1_latch >> 8) & 0xff;\n\t\tbreak;\n\tcase 0x8:\t\t// T2C-L\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = (mos6522ptr->timer2_counter - 1) & 0xff;\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\tmos6522ptr->ifr & (~0x20), mos6522ptr->ier);\n\t\t\t// Clear Bit 5\n\t\tmock_update_timers(1, dfcyc);\t// Prepare another int (maybe)\n\t\tdbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc6);\n\t\tbreak;\n\tcase 0x9:\t\t// T2C-H\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = ((mos6522ptr->timer2_counter - 1) >> 8) & 0xff;\n\t\tbreak;\n\tcase 0xa:\t\t// SR\n\t\tval = mos6522ptr->sr;\n\t\t//halt_printf(\"Reading SR %d %02x\\n\", pair_num, val);\n\t\tbreak;\n\tcase 0xb:\t\t// ACR\n\t\tval = mos6522ptr->acr;\n\t\tbreak;\n\tcase 0xc:\t\t// PCR\n\t\tval = mos6522ptr->pcr;\n\t\tbreak;\n\tcase 0xd:\t\t// IFR\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = mos6522ptr->ifr;\n\t\tbreak;\n\tcase 0xe:\t\t// IER\n\t\tval = mos6522ptr->ier | 0x80;\t\t// Despite MOS6522\n\t\tbreak;\t\t\t\t\t//  datasheet, bit 7 = 1\n\t}\n\t// printf(\"6522 %d loc:%x ret:%02x\\n\", pair_num, loc, val);\n\treturn val;\n}\n\nvoid\nmock_6522_write(int pair_num, word32 loc, word32 val, dword64 dfcyc)\n{\n\tMos6522\t*mos6522ptr;\n\tword32\tora, orb, new_val, mask;\n\n\t// Write to 6522 #num6522 loc (0-15)\n\n\t// printf(\"6522 %d loc:%x write:%02x\\n\", pair_num, loc, val);\n\n\tmos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);\n\tswitch(loc) {\n\tcase 0x0:\t\t// ORB\n\t\tmask = mos6522ptr->ddrb;\n\t\torb = mos6522ptr->orb;\n\t\tnew_val = (val & mask) | (orb & (~mask));\n\t\tif(orb != new_val) {\n\t\t\tmock_ay8913_control_update(pair_num, new_val, orb,\n\t\t\t\t\t\t\t\t\tdfcyc);\n\t\t}\n\t\tmos6522ptr->orb = new_val;\n\t\tbreak;\n\tcase 0x1:\t\t// ORA\n\tcase 0xf:\t\t// ORA, no handshake\n\t\tmask = mos6522ptr->ddra;\n\t\tora = mos6522ptr->ora;\n\t\tnew_val = (val & mask) | (ora & (~mask));\n\t\tmos6522ptr->ora = new_val;\n\t\tbreak;\n\tcase 0x2:\t\t// DDRB\n\t\torb = mos6522ptr->orb;\n\t\tnew_val = (orb & val) | (orb & (~val));\n\t\tif(orb != new_val) {\n\t\t\tmock_ay8913_control_update(pair_num, new_val, orb,\n\t\t\t\t\t\t\t\t\tdfcyc);\n\t\t}\n\t\tmos6522ptr->orb = new_val;\n\t\tmos6522ptr->ddrb = val;\n\t\treturn;\n\tcase 0x3:\t\t// DDRA\n\t\tora = mos6522ptr->ora;\n\t\tmos6522ptr->ora = (ora & val) | (ora & (~val));\n\t\tmos6522ptr->ddra = val;\n\t\treturn;\n\tcase 0x4:\t\t// T1C-L\n\t\tmock_update_timers(0, dfcyc);\n\t\tmos6522ptr->timer1_latch =\n\t\t\t\t(mos6522ptr->timer1_latch & 0x1ff00) | val;\n\t\t// printf(\"Set T1C-L, timer1_latch=%05x\\n\",\n\t\t//\t\t\t\tmos6522ptr->timer1_latch);\n\t\tbreak;\n\tcase 0x5:\t\t// T1C-H\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = (mos6522ptr->timer1_latch & 0xff) | (val << 8);\n\t\tmos6522ptr->timer1_latch = val;\n\t\tmos6522ptr->timer1_counter = val + 2;\n\t\t\t// The actual timer1_counter update happens next cycle,\n\t\t\t//  so we want val+1, plus another 1\n\t\t// printf(\"Set T1C-H, timer1_latch=%05x\\n\",\n\t\t//\t\t\t\tmos6522ptr->timer1_latch);\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\tmos6522ptr->ifr & (~0x40), mos6522ptr->ier);\n\t\t\t// Clear Bit 6\n\t\tmock_update_timers(1, dfcyc);\n\t\tdbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc7);\n\t\tbreak;\n\tcase 0x6:\t\t// T1L-L\n\t\tmock_update_timers(0, dfcyc);\n\t\tmos6522ptr->timer1_latch =\n\t\t\t\t(mos6522ptr->timer1_latch & 0x1ff00) | val;\n\t\tbreak;\n\tcase 0x7:\t\t// T1L-H\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = (mos6522ptr->timer1_latch & 0xff) | (val << 8);\n\t\tmos6522ptr->timer1_latch = val;\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\tmos6522ptr->ifr & (~0x40), mos6522ptr->ier);\n\t\t\t// Clear Bit 6\n\t\tmock_update_timers(1, dfcyc);\n\t\t// mock_show_pair(pair_num, dfcyc, \"Wrote T1L-H\");\n\t\tdbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc8);\n\t\tbreak;\n\tcase 0x8:\t\t// T2C-L\n\t\tmos6522ptr->timer2_latch = (mos6522ptr->timer2_latch & 0xff00) |\n\t\t\t\t\t\t\t\t\tval;\n\t\tbreak;\n\tcase 0x9:\t\t// T2C-H\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = (mos6522ptr->timer2_latch & 0xff) | (val << 8);\n\t\tmos6522ptr->timer2_latch = val;\n\t\tmos6522ptr->timer2_counter = val + 2;\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\tmos6522ptr->ifr & (~0x20), mos6522ptr->ier);\n\t\t\t// Clear bit 5\n\t\tmock_update_timers(1, dfcyc);\n\t\tbreak;\n\tcase 0xa:\t\t// SR\n\t\tmos6522ptr->sr = val;\n\t\thalt_printf(\"Wrote SR reg: %d %02x\\n\", pair_num, val);\n\t\tbreak;\n\tcase 0xb:\t\t// ACR\n\t\tmock_update_timers(0, dfcyc);\n\t\tmos6522ptr->acr = val;\n\t\tmock_update_timers(1, dfcyc);\n\t\tbreak;\n\tcase 0xc:\t\t// PCR\n\t\tmos6522ptr->pcr = val;\n\t\tbreak;\n\tcase 0xd:\t\t// IFR\n\t\tmock_update_timers(1, dfcyc);\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\t\tmos6522ptr->ifr & (~val), mos6522ptr->ier);\n\t\tmock_update_timers(1, dfcyc);\n\t\tdbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc9);\n\t\tbreak;\n\tcase 0xe:\t\t// IER\n\t\t// Recalculate effective IFR with new IER\n\t\tmock_update_timers(1, dfcyc);\n\t\tif(val & 0x80) {\t\t\t// Set EIR bits\n\t\t\tval = mos6522ptr->ier | val;\n\t\t} else {\t\t\t\t// Clear EIR bits\n\t\t\tval = mos6522ptr->ier & (~val);\n\t\t}\n\t\tval = val & 0x7f;\n\t\tmos6522ptr->ier = val;\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\t\t\t\t\tmos6522ptr->ifr, val);\n\t\tmock_update_timers(1, dfcyc);\n\t\t// mock_show_pair(pair_num, dfcyc, \"Wrote IER\");\n\t\tdbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xca);\n\t\tbreak;\n\t}\n}\n\nword32\nmock_6522_new_ifr(dword64 dfcyc, int pair_num, word32 ifr, word32 ier)\n{\n\tword32\tirq_mask;\n\n\t// Determine if there are any interrupts pending now\n\tirq_mask = IRQ_PENDING_MOCKINGBOARDA << pair_num;\n\tif((ifr & ier & 0x7f) == 0) {\n\t\t// No IRQ pending anymore\n\t\tifr = ifr & 0x7f;\t\t// Clear bit 7\n\t\tif(g_irq_pending & irq_mask) {\n\t\t\t// printf(\"MOCK INT OFF\\n\");\n\t\t}\n\t\tremove_irq(irq_mask);\n\t\tdbg_log_info(dfcyc, (ier << 8) | ifr, g_irq_pending, 0xcf);\n\t} else {\n\t\t// IRQ is pending\n\t\tifr = ifr | 0x80;\t\t// Set bit 7\n\t\tif(!(g_irq_pending & irq_mask)) {\n\t\t\t// printf(\"MOCK INT ON\\n\");\n\t\t}\n\t\tadd_irq(irq_mask);\n\t\tdbg_log_info(dfcyc, (ier << 8) | ifr, g_irq_pending, 0xcf);\n\t}\n\treturn ifr;\n}\n\nvoid\nmock_ay8913_reg_read(int pair_num)\n{\n\tMos6522\t*mos6522ptr;\n\tAy8913\t*ay8913ptr;\n\tword32\treg_addr_latch, mask, val, ora;\n\n\tmos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);\n\tay8913ptr = &(g_mockingboard.pair[pair_num].ay8913);\n\treg_addr_latch = ay8913ptr->reg_addr_latch;\n\tval = 0;\n\tif(reg_addr_latch < 16) {\n\t\tval = ay8913ptr->regs[reg_addr_latch];\n\t}\n\t// ORA at 6522 is merge of ORA using DDRA\n\tmask = mos6522ptr->ddra;\n\tora = mos6522ptr->ora;\n\tmos6522ptr->ora = (ora & mask) | (val & (~mask));\n}\n\nword32 g_mock_channel_regs[3] = {\n\t0x39c3,\t\t// channel A: regs 0,1,6,7,8,11,12,13\n\t0x3acc,\t\t// channel B: regs 2,3,6,7,9,11,12,13\n\t0x3cf0\t\t// channel C: regs 4,5,6,7,10,11,12,13\n};\n\nvoid\nmock_ay8913_reg_write(int pair_num, dword64 dfcyc)\n{\n\tMos6522\t*mos6522ptr;\n\tAy8913\t*ay8913ptr;\n\tdouble\tdsamps;\n\tword32\treg_addr_latch, ora, mask, rmask, do_print;\n\tint\ti;\n\n\tmos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);\n\tay8913ptr = &(g_mockingboard.pair[pair_num].ay8913);\n\treg_addr_latch = ay8913ptr->reg_addr_latch;\n\tora = mos6522ptr->ora;\n\tdsamps = dfcyc * g_dsamps_per_dfcyc;\n\tif(reg_addr_latch < 16) {\n\t\tmask = (g_mockingboard.disable_mask >> (3*pair_num)) & 7;\n\t\trmask = 0;\n\t\tdo_print = 0;\n\t\tfor(i = 0; i < 3; i++) {\n\t\t\tif(((mask >> i) & 1) == 0) {\n\t\t\t\trmask |= g_mock_channel_regs[i];\n\t\t\t}\n\t\t}\n\t\tdo_print = (rmask >> reg_addr_latch) & 1;\n\t\tif((ora != ay8913ptr->regs[reg_addr_latch]) ||\n\t\t\t\t\t\t(reg_addr_latch == 13)) {\n\t\t\t// New value, or writing to Envelope control\n\t\t\tdo_print = 0;\n\t\t\tif(do_print) {\n\t\t\t\tprintf(\"%.2lf %016llx mock pair%d reg[%d]=\"\n\t\t\t\t\t\"%02x. [2,3]=%02x_%02x [67]=%02x,%02x, \"\n\t\t\t\t\t\"[9]=%02x, [12,11]=%02x_%02x [13]=\"\n\t\t\t\t\t\"%02x\\n\", dsamps, dfcyc, pair_num,\n\t\t\t\t\treg_addr_latch, ora,\n\t\t\t\t\tay8913ptr->regs[3], ay8913ptr->regs[2],\n\t\t\t\t\tay8913ptr->regs[6], ay8913ptr->regs[7],\n\t\t\t\t\tay8913ptr->regs[9], ay8913ptr->regs[12],\n\t\t\t\t\tay8913ptr->regs[11],\n\t\t\t\t\tay8913ptr->regs[13]);\n\t\t\t}\n\t\t\tsound_play(dfcyc);\n\t\t}\n\t\tay8913ptr->regs[reg_addr_latch] = ora;\n\t\tif(reg_addr_latch == 13) {\t\t// Envelope control\n\t\t\tay8913ptr->env_dsamp &= 0x1fffffffffffULL;\n\t\t\t// Clear \"hold\" in (env_val & (0x80 << 40))\n\t\t}\n\t}\n}\n\nvoid\nmock_ay8913_control_update(int pair_num, word32 new_val, word32 prev_val,\n\t\t\t\t\t\t\tdword64 dfcyc)\n{\n\tMos6522\t*mos6522ptr;\n\tAy8913\t*ay8913ptr;\n\n\tmos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);\n\tay8913ptr = &(g_mockingboard.pair[pair_num].ay8913);\n\t// printf(\"ay8913 %d control now:%02x\\n\", pair_num, new_val);\n\n\t// new_val and prev_val are { reset_l, BDIR, BC1 }\n\t// 4=Idle; 5=Read; 6=Write; 7=Latch_addr\n\t// Latch new address and write data at the time the ctl changes to Idle\n\t// Do read as soon as the ctl indicates to do a read.\n\n\tif((new_val & 4) == 0) {\n\t\tmock_ay8913_reset(pair_num, dfcyc);\n\t\treturn;\n\t}\n\tnew_val = new_val & 7;\n\tprev_val = prev_val & 7;\n\tif(prev_val == 7) {\t\t// Latch new address, latch it now\n\t\tay8913ptr->reg_addr_latch = mos6522ptr->ora;\n\t} else if(prev_val == 6) {\t// Write data, do it now\n\t\tmock_ay8913_reg_write(pair_num, dfcyc);\n\t}\n\tif(new_val == 5) {\n\t\tmock_ay8913_reg_read(pair_num);\n\t}\n}\n\nvoid\nmockingboard_show(int got_num, word32 disable_mask)\n{\n\tint\ti, j;\n\n\tif(got_num) {\n\t\tg_mockingboard.disable_mask = disable_mask;\n\t}\n\tprintf(\"g_mockingboard.disable_mask:%02x\\n\",\n\t\t\t\t\t\tg_mockingboard.disable_mask);\n\tfor(i = 0; i < 2; i++) {\n\t\tfor(j = 0; j < 14; j++) {\n\t\t\tprintf(\"Mockingboard pair[%d].reg[%d]=%02x\\n\", i, j,\n\t\t\t\tg_mockingboard.pair[i].ay8913.regs[j]);\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "gsplus/src/moremem.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2024 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include \"defc.h\"\n\nextern char g_kegs_version_str[];\n\nextern byte *g_memory_ptr;\nextern byte *g_dummy_memory1_ptr;\nextern byte *g_slow_memory_ptr;\nextern byte *g_rom_fc_ff_ptr;\nextern byte *g_rom_cards_ptr;\n\nextern word32 g_slow_mem_changed[];\n\nextern int g_num_breakpoints;\nextern Break_point g_break_pts[];\n\nextern Page_info page_info_rd_wr[];\n\nextern int Verbose;\nextern int g_rom_version;\nextern int g_user_page2_shadow;\nextern Iwm g_iwm;\nextern int g_halt_sim;\nextern int g_config_control_panel;\n\n\n/* from iwm.c */\nint\tg_num_shadow_all_banks = 0;\n\n#define IOR(val) ( (val) ? 0x80 : 0x00 )\n\n\nextern int g_cur_a2_stat;\n\nint\tg_em_emubyte_cnt = 0;\nint\tg_paddle_buttons = 0;\nint\tg_irq_pending = 0;\n\nword32\tg_c023_val = 0;\nword32\tg_c029_val_some = 0x41;\nword32\tg_c02b_val = 0x08;\nword32\tg_c02d_int_crom = 0;\nword32\tg_c033_data = 0;\nword32\tg_c034_val = 0;\nword32\tg_c035_shadow_reg = 0x08;\t// A bit set inhibits that shadowing\n\t\t// [6]=Inhibit I/O and LC, [4]=Inhibit Aux hires, [3]=Inh SHR\n\t\t// [2]=Inh hires pg 2, [1]=Inh hires pg 1, [0]=Inh text pages\nword32\tg_c036_val_speed = 0x80;\t// [7]=Fast, [4]=Shadow in all banks,\n\t\t\t\t\t// [3:0]=slot 7..4 disk motor detect\nword32\tg_c03ef_doc_ptr = 0;\nword32\tg_c041_val = 0;\t\t/* C041_EN_25SEC_INTS, C041_EN_MOVE_INTS */\nword32\tg_c046_val = 0;\nword32\tg_c05x_annuncs = 0;\nword32\tg_c068_statereg = 0;\t// [7]=ALTZP, [6]=PAGE2, [5]=RAMRD, [4]=RAMWRT\n\t\t\t// [3]=RDROM, [2]=LCBANK2, [1]=ROMBANK, [0]=INTCXROM\n\t\t\t// KEGS special: [8]=PREWRITE_LC, [9]=WRDEFRAM,\n\t\t\t//\t[10]=INTC8ROM\nword32\tg_c06d_val = 0;\nword32\tg_c06f_val = 0;\nword32\tg_zipgs_unlock = 0;\nword32\tg_zipgs_reg_c059 = 0x5f;\n\t// 7=LC cache dis, 6==5ms paddle del en, 5==5ms ext del en,\n\t// 4==5ms c02e enab, 3==CPS follow enab, 2-0: 111\nword32\tg_zipgs_reg_c05a = 0x0f;\n\t// 7:4 = current ZIP speed, 0=100%, 1=93.75%, F=6.25%\n\t// 3:0: always 1111\nword32\tg_zipgs_reg_c05b = 0x40;\n\t// 7==1ms clock, 6==cshupd: tag data at c05f updated\n\t// 5==LC cache disable, 4==bd is disabled, 3==delay in effect,\n\t// 2==rombank, 1-0==ram size (00:8K, 01=16K, 10=32K, 11=64K)\nword32\tg_zipgs_reg_c05c = 0x00;\n\t// 7:1==slot delay enable (for 52-54ms), 0==speaker 5ms delay\n\nword32\tg_c06c_latched_cyc = 0;\n\n#define SLINKY_RAM_SIZE\t0x100000\t\t/* 1MB */\nword32\tg_slinky_addr = 0;\nbyte g_slinky_ram[SLINKY_RAM_SIZE];\n\n\n#define EMUSTATE(a)\t{ #a, &a }\n\nEmustate_intlist g_emustate_intlist[] = {\n\t// EMUSTATE(g_cur_a2_stat),\n\t// EMUSTATE(g_paddle_buttons),\n\n\t// EMUSTATE(g_em_emubyte_cnt),\n\t// EMUSTATE(g_irq_pending),\n\tEMUSTATE(g_c023_val),\n\tEMUSTATE(g_c029_val_some),\n\tEMUSTATE(g_c02b_val),\n\tEMUSTATE(g_c02d_int_crom),\n\tEMUSTATE(g_c033_data),\n\tEMUSTATE(g_c034_val),\n\tEMUSTATE(g_c035_shadow_reg),\n\tEMUSTATE(g_c036_val_speed),\n\tEMUSTATE(g_c041_val),\n\tEMUSTATE(g_c046_val),\n\tEMUSTATE(g_c05x_annuncs),\n\tEMUSTATE(g_c068_statereg),\n\tEMUSTATE(g_zipgs_unlock),\n\tEMUSTATE(g_zipgs_reg_c059),\n\tEMUSTATE(g_zipgs_reg_c05a),\n\tEMUSTATE(g_zipgs_reg_c05b),\n\tEMUSTATE(g_zipgs_reg_c05c),\n\t{ 0, 0, }\n};\n\nextern dword64 g_paddle_trig_dfcyc;\nextern dword64 g_last_vbl_dfcyc;\n\nEmustate_dword64list g_emustate_dword64list[] = {\n\tEMUSTATE(g_paddle_trig_dfcyc),\n\tEMUSTATE(g_last_vbl_dfcyc),\n\t{ 0, 0, }\n};\n\nextern word32 g_mem_size_total;\n\nEmustate_word32list g_emustate_word32list[] = {\n\tEMUSTATE(g_mem_size_total),\n\tEMUSTATE(g_c03ef_doc_ptr),\n\t{ 0, 0, }\n};\n\n\n#define UNIMPL_READ\t\\\n\thalt_printf(\"UNIMP READ to addr %08x\\n\", loc);\t\\\n\treturn 0;\n\n#define UNIMPL_WRITE\t\\\n\thalt_printf(\"UNIMP WRITE to addr %08x, val: %04x\\n\", loc, val);\t\\\n\treturn;\n\nvoid\nfixup_brks()\n{\n\tword32\tpage, tmp, tmp2, end_page;\n\tPg_info\tval;\n\tint\tis_wr_only, num;\n\tint\ti;\n\n\tnum = g_num_breakpoints;\n\tfor(i = 0; i < num; i++) {\n\t\tpage = (g_break_pts[i].start_addr >> 8) & 0xffff;\n\t\tend_page = (g_break_pts[i].end_addr >> 8) & 0xffff;\n\t\tis_wr_only = (g_break_pts[i].start_addr >> 24) & 1;\n\t\twhile(page <= end_page) {\n\t\t\tif(!is_wr_only) {\n\t\t\t\tval = GET_PAGE_INFO_RD(page);\n\t\t\t\ttmp = PTR2WORD(val) & 0xff;\n\t\t\t\ttmp2 = tmp | BANK_IO_TMP | BANK_BREAK;\n\t\t\t\tSET_PAGE_INFO_RD(page, val - tmp + tmp2);\n\t\t\t}\n\t\t\tval = GET_PAGE_INFO_WR(page);\n\t\t\ttmp = PTR2WORD(val) & 0xff;\n\t\t\ttmp2 = tmp | BANK_IO_TMP | BANK_BREAK;\n\t\t\tSET_PAGE_INFO_WR(page, val - tmp + tmp2);\n\t\t\tpage++;\n\t\t}\n\t}\n}\n\nvoid\nfixup_hires_on()\n{\n\tif((g_cur_a2_stat & ALL_STAT_ST80) == 0) {\n\t\treturn;\n\t}\n\n\tfixup_bank0_2000_4000();\n\tfixup_brks();\n}\n\nvoid\nfixup_bank0_2000_4000()\n{\n\tbyte\t*mem0rd;\n\tbyte\t*mem0wr;\n\tword32\tstart_page;\n\tint\ti;\n\n\t// Do banks $00 and $E0\n\n\tfor(i = 0; i < 2; i++) {\n\t\tmem0rd = &(g_memory_ptr[0x2000]);\n\t\tstart_page = 0x20;\n\t\tif(i) {\n\t\t\tstart_page += 0xe000;\t\t\t// Bank $E0\n\t\t\tmem0rd = &(g_slow_memory_ptr[0x2000]);\n\t\t}\n\t\tmem0wr = mem0rd;\n\t\tif((g_cur_a2_stat & ALL_STAT_ST80) &&\n\t\t\t\t\t(g_cur_a2_stat & ALL_STAT_HIRES)) {\n\t\t\tif(g_cur_a2_stat & ALL_STAT_PAGE2) {\n\t\t\t\tmem0rd += 0x10000;\n\t\t\t\tmem0wr += 0x10000;\n\t\t\t\tif(((g_c035_shadow_reg & 0x12) == 0) ||\n\t\t\t\t\t((g_c035_shadow_reg & 0x8) == 0) || i) {\n\t\t\t\t\tmem0wr += BANK_SHADOW2;\n\t\t\t\t}\n\t\t\t} else if(((g_c035_shadow_reg & 0x02) == 0) || i) {\n\t\t\t\tmem0wr += BANK_SHADOW;\n\t\t\t}\n\t\t} else {\n\t\t\tif(RAMRD) {\n\t\t\t\tmem0rd += 0x10000;\n\t\t\t}\n\t\t\tif(RAMWRT) {\n\t\t\t\tmem0wr += 0x10000;\n\t\t\t\tif(((g_c035_shadow_reg & 0x12) == 0) ||\n\t\t\t\t\t((g_c035_shadow_reg & 0x8) == 0) || i) {\n\t\t\t\t\tmem0wr += BANK_SHADOW2;\n\t\t\t\t}\n\t\t\t} else if(((g_c035_shadow_reg & 0x02) == 0) || i) {\n\t\t\t\tmem0wr += BANK_SHADOW;\n\t\t\t}\n\t\t}\n\t\tfixup_any_bank_any_page(start_page, 0x20, mem0rd, mem0wr);\n\t}\n}\n\nvoid\nfixup_bank0_0400_0800()\n{\n\tbyte\t*mem0rd;\n\tbyte\t*mem0wr;\n\tword32\tstart_page, shadow;\n\tint\ti;\n\n\tfor(i = 0; i < 2; i++) {\n\t\tmem0rd = &(g_memory_ptr[0x400]);\n\t\tstart_page = 4;\n\t\tif(i) {\n\t\t\tstart_page += 0xe000;\t\t\t// Bank $E0\n\t\t\tmem0rd = &(g_slow_memory_ptr[0x400]);\n\t\t}\n\t\tmem0wr = mem0rd;\n\t\tshadow = BANK_SHADOW;\n\t\tif(g_cur_a2_stat & ALL_STAT_ST80) {\n\t\t\tif(g_cur_a2_stat & ALL_STAT_PAGE2) {\n\t\t\t\tshadow = BANK_SHADOW2;\n\t\t\t\tmem0rd += 0x10000;\n\t\t\t\tmem0wr += 0x10000;\n\t\t\t}\n\t\t} else {\n\t\t\tif(RAMWRT) {\n\t\t\t\tshadow = BANK_SHADOW2;\n\t\t\t\tmem0wr += 0x10000;\n\t\t\t}\n\t\t\tif(RAMRD) {\n\t\t\t\tmem0rd += 0x10000;\n\t\t\t}\n\t\t}\n\t\tif(((g_c035_shadow_reg & 0x01) == 0) || i) {\n\t\t\tmem0wr += shadow;\n\t\t}\n\n\t\tfixup_any_bank_any_page(start_page, 4, mem0rd, mem0wr);\n\t}\n}\n\nvoid\nfixup_any_bank_any_page(word32 start_page, int num_pages, byte *mem0rd,\n\t\tbyte *mem0wr)\n{\n\tint\ti;\n\n\tfor(i = 0; i < num_pages; i++) {\n\t\tSET_PAGE_INFO_RD(i + start_page, mem0rd);\n\t\tmem0rd += 0x100;\n\t}\n\n\tfor(i = 0; i < num_pages; i++) {\n\t\tSET_PAGE_INFO_WR(i + start_page, mem0wr);\n\t\tmem0wr += 0x100;\n\t}\n\n}\n\nvoid\nfixup_intcx()\n{\n\tbyte\t*rom10000;\n\tbyte\t*rom_inc;\n\tword32\tintcx, mask;\n\tint\tno_io_shadow, off, start_k;\n\tint\tj, k;\n\n\trom10000 = &(g_rom_fc_ff_ptr[0x30000]);\n\n\tno_io_shadow = (g_c035_shadow_reg & 0x40);\n\n\tstart_k = 0;\n\tif(no_io_shadow) {\n\t\t/* if not shadowing, banks 0 and 1 are not affected by intcx */\n\t\tstart_k = 2;\n\t}\n\n\tintcx = (g_c068_statereg & 1);\t// set means use internal rom\n\t// printf(\"fixup_intcx, intcx:%d, no_io:%d, c02d:%02x\\n\", intcx,\n\t//\t\t\t\tno_io_shadow, g_c02d_int_crom);\n\tfor(k = start_k; k < 4; k++) {\n\t\toff = k;\n\t\tif(k >= 2) {\n\t\t\toff += (0xe0 - 2);\n\t\t}\n\t\t/* step off through 0x00, 0x01, 0xe0, 0xe1 */\n\n\t\toff = off << 8;\t\t// Now 0x0000, 0x0100, 0xe000, 0xe100\n\t\tSET_PAGE_INFO_RD(0xc0 + off, SET_BANK_IO);\n\n\t\tfor(j = 0xc1; j < 0xc8; j++) {\n\t\t\tmask = 1 << (j & 0xf);\n\t\t\trom_inc = SET_BANK_IO;\n\t\t\tif(((g_c02d_int_crom & mask) == 0) || intcx) {\n\t\t\t\trom_inc = rom10000 + (j << 8);\n\t\t\t} else {\n\t\t\t\t// User-slot rom\n\t\t\t\trom_inc = &(g_rom_cards_ptr[0]) +\n\t\t\t\t\t\t\t((j - 0xc0) << 8);\n\t\t\t\tif(j == 0xc4) {\t\t// Mockingboard\n\t\t\t\t\trom_inc = SET_BANK_IO;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif((j == 0xc3) && !(g_c068_statereg & 0x400)) {\n\t\t\t\t// If INTC8ROM not set, we need to\n\t\t\t\t//  watch for reads from C3xx to set it\n\t\t\t\trom_inc = SET_BANK_IO;\n\t\t\t}\n\t\t\tSET_PAGE_INFO_RD(j + off, rom_inc);\n\t\t}\n\t\tfor(j = 0xc8; j < 0xd0; j++) {\n\t\t\t/* c800 - cfff */\n\t\t\tif(intcx || (g_c068_statereg & 0x400)) {\n\t\t\t\t// INTCXROM or INTC8ROM is set\n\t\t\t\trom_inc = rom10000 + (j << 8);\n\t\t\t\tif((j == 0xcf) && (g_c068_statereg & 0x400)) {\n\t\t\t\t\trom_inc = SET_BANK_IO;\n\t\t\t\t}\n\t\t\t} else {\t// c800 space not necessarily mapped\n\t\t\t\trom_inc = SET_BANK_IO;\n\t\t\t}\n\t\t\tSET_PAGE_INFO_RD(j + off, rom_inc);\n\t\t}\n\t\tfor(j = 0xc0; j < 0xd0; j++) {\n\t\t\tSET_PAGE_INFO_WR(j + off, SET_BANK_IO);\n\t\t}\n\t}\n\n\tfixup_brks();\n}\n\nvoid\nfixup_st80col(dword64 dfcyc)\n{\n\tint\tcur_a2_stat;\n\n\tcur_a2_stat = g_cur_a2_stat;\n\n\tfixup_bank0_0400_0800();\n\n\tif(cur_a2_stat & ALL_STAT_HIRES) {\n\t\t/* fixup no matter what PAGE2 since PAGE2 and RAMRD/WR */\n\t\t/*  can work against each other */\n\t\tfixup_bank0_2000_4000();\n\t}\n\n\tif(cur_a2_stat & ALL_STAT_PAGE2) {\n\t\tchange_display_mode(dfcyc);\n\t}\n\n\tfixup_brks();\n}\n\nvoid\nfixup_altzp()\n{\n\tbyte\t*mem0rd;\n\tword32\tstart_page, altzp;\n\tint\ti;\n\n\taltzp = ALTZP;\n\tfor(i = 0; i < 2; i++) {\n\t\tstart_page = 0;\n\t\tmem0rd = &(g_memory_ptr[0]);\n\t\tif(i) {\n\t\t\tstart_page = 0xe000;\n\t\t\tmem0rd = &(g_slow_memory_ptr[0]);\n\t\t}\n\t\tif(altzp) {\n\t\t\tmem0rd += 0x10000;\n\t\t}\n\t\tfixup_any_bank_any_page(start_page, 2, mem0rd, mem0rd);\n\t}\n\n\t/* No need for fixup_brks since called from set_statereg() */\n}\n\nvoid\nfixup_page2(dword64 dfcyc)\n{\n\tif((g_cur_a2_stat & ALL_STAT_ST80)) {\n\t\tfixup_bank0_0400_0800();\n\t\tif((g_cur_a2_stat & ALL_STAT_HIRES)) {\n\t\t\tfixup_bank0_2000_4000();\n\t\t}\n\t} else {\n\t\tchange_display_mode(dfcyc);\n\t}\n}\n\nvoid\nfixup_ramrd()\n{\n\tbyte\t*mem0rd;\n\tword32\tstart_page, cur_a2_stat;\n\tint\ti, j;\n\n\tcur_a2_stat = g_cur_a2_stat;\n\n\tif((cur_a2_stat & ALL_STAT_ST80) == 0) {\n\t\tfixup_bank0_0400_0800();\n\t}\n\tif( ((cur_a2_stat & ALL_STAT_ST80) == 0) ||\n\t\t\t\t((cur_a2_stat & ALL_STAT_HIRES) == 0) ) {\n\t\tfixup_bank0_2000_4000();\n\t}\n\n\tfor(i = 0; i < 2; i++) {\n\t\tstart_page = 0;\n\t\tmem0rd = &(g_memory_ptr[0x0000]);\n\t\tif(i) {\n\t\t\tstart_page = 0xe000;\n\t\t\tmem0rd = &(g_slow_memory_ptr[0]);\n\t\t}\n\n\t\tif(RAMRD) {\n\t\t\tmem0rd += 0x10000;\n\t\t}\n\n\t\tSET_PAGE_INFO_RD(start_page + 2, mem0rd + 0x200);\n\t\tSET_PAGE_INFO_RD(start_page + 3, mem0rd + 0x300);\n\n\t\tfor(j = 8; j < 0x20; j++) {\n\t\t\tSET_PAGE_INFO_RD(start_page + j, mem0rd + j*0x100);\n\t\t}\n\n\t\tfor(j = 0x40; j < 0xc0; j++) {\n\t\t\tSET_PAGE_INFO_RD(start_page + j, mem0rd + j*0x100);\n\t\t}\n\t}\n\n\t/* No need for fixup_brks since only called from set_statereg() */\n}\n\nvoid\nfixup_ramwrt()\n{\n\tbyte\t*mem0wr;\n\tword32\tstart_page, cur_a2_stat, shadow, ramwrt;\n\tint\ti, j;\n\n\tcur_a2_stat = g_cur_a2_stat;\n\n\tif((cur_a2_stat & ALL_STAT_ST80) == 0) {\n\t\tfixup_bank0_0400_0800();\n\t}\n\tif( ((cur_a2_stat & ALL_STAT_ST80) == 0) ||\n\t\t\t\t((cur_a2_stat & ALL_STAT_HIRES) == 0) ) {\n\t\tfixup_bank0_2000_4000();\n\t}\n\n\tfor(i = 0; i < 2; i++) {\n\t\tstart_page = 0;\n\t\tmem0wr = &(g_memory_ptr[0x0000]);\n\t\tif(i) {\n\t\t\tstart_page = 0xe000;\n\t\t\tmem0wr = &(g_slow_memory_ptr[0]);\n\t\t}\n\n\t\tramwrt = RAMWRT;\n\t\tif(ramwrt) {\n\t\t\tmem0wr += 0x10000;\n\t\t}\n\n\t\tSET_PAGE_INFO_WR(start_page + 2, mem0wr + 0x200);\n\t\tSET_PAGE_INFO_WR(start_page + 3, mem0wr + 0x300);\n\n\t\tshadow = BANK_SHADOW;\n\t\tif(ramwrt) {\n\t\t\tshadow = BANK_SHADOW2;\n\t\t}\n\t\tif( ((g_c035_shadow_reg & 0x20) != 0) ||\n\t\t\t((g_rom_version == 1) && !g_user_page2_shadow)) {\n\t\t\tif(!i) {\n\t\t\t\tshadow = 0;\n\t\t\t}\n\t\t}\n\t\tfor(j = 8; j < 0x0c; j++) {\n\t\t\tSET_PAGE_INFO_WR(start_page + j,\n\t\t\t\t\t\tmem0wr + j*0x100 + shadow);\n\t\t}\n\n\t\tfor(j = 0xc; j < 0x20; j++) {\n\t\t\tSET_PAGE_INFO_WR(start_page + j, mem0wr + j*0x100);\n\t\t}\n\n\t\tshadow = 0;\n\t\tif(ramwrt) {\n\t\t\tif(((g_c035_shadow_reg & 0x14) == 0) ||\n\t\t\t\t((g_c035_shadow_reg & 0x08) == 0) || i) {\n\t\t\t\tshadow = BANK_SHADOW2;\n\t\t\t}\n\t\t} else if(((g_c035_shadow_reg & 0x04) == 0) || i) {\n\t\t\tshadow = BANK_SHADOW;\n\t\t}\n\t\tfor(j = 0x40; j < 0x60; j++) {\n\t\t\tSET_PAGE_INFO_WR(start_page + j,\n\t\t\t\t\t\tmem0wr + j*0x100 + shadow);\n\t\t}\n\n\t\tshadow = 0;\n\t\tif(ramwrt && (((g_c035_shadow_reg & 0x08) == 0) || i)) {\n\t\t\t/* shr shadowing */\n\t\t\tshadow = BANK_SHADOW2;\n\t\t}\n\t\tfor(j = 0x60; j < 0xa0; j++) {\n\t\t\tSET_PAGE_INFO_WR(start_page + j,\n\t\t\t\t\t\tmem0wr + j*0x100 + shadow);\n\t\t}\n\n\t\tfor(j = 0xa0; j < 0xc0; j++) {\n\t\t\tSET_PAGE_INFO_WR(start_page + j, mem0wr + j*0x100);\n\t\t}\n\t}\n\n\t/* No need for fixup_brks() since only called from set_statereg() */\n}\n\nvoid\nfixup_lc()\n{\n\tbyte\t*mem0rd, *mem0wr;\n\tword32\tbank, lcbank2, c08x_wrdefram, rdrom;\n\tint\tk;\n\n\t// Fixup memory from 0xd000 - 0xffff in banks 0, 1, $e0, $e1\n\tfor(k = 0; k < 4; k++) {\n\t\tbank = k;\n\t\tmem0rd = &(g_memory_ptr[k << 16]);\n\t\tif(k >= 2) {\n\t\t\tbank += (0xe0 - 2);\t//k==2->bank=$e0, k==3->bank=$e1\n\t\t\tmem0rd = &(g_slow_memory_ptr[(k & 1) << 16]);\n\t\t}\n\t\t/* step bank through 0x00, 0x01, 0xe0, 0xe1 */\n\n\t\tif(((k & 1) == 0) && ALTZP) {\n\t\t\tmem0rd += 0x10000;\n\t\t}\n\t\tlcbank2 = LCBANK2;\n\t\tc08x_wrdefram = g_c068_statereg & 0x200;\n\t\trdrom = RDROM;\n\t\tif((k < 2) && (g_c035_shadow_reg & 0x40)) {\n\t\t\tlcbank2 = 0;\n\t\t\tc08x_wrdefram = 1;\n\t\t\trdrom = 0;\n\t\t}\n\t\tmem0wr = mem0rd;\n\t\tif((k < 2) && !c08x_wrdefram) {\n\t\t\tmem0wr += (BANK_IO_TMP | BANK_IO2_TMP);\n\t\t}\n\t\tif((k < 2) && rdrom) {\n\t\t\tmem0rd = &(g_rom_fc_ff_ptr[0x30000]);\n\t\t}\n\t\tfixup_any_bank_any_page(bank*0x100 + 0xe0, 0x20,\n\t\t\t\t\tmem0rd + 0xe000, mem0wr + 0xe000);\n\t\tif(! lcbank2) {\t\t// lcbank1, 0xd000 is ram at 0xc000-cfff\n\t\t\tif(!rdrom) {\n\t\t\t\tmem0rd -= 0x1000;\n\t\t\t}\n\t\t\tmem0wr -= 0x1000;\n\t\t}\n\t\tfixup_any_bank_any_page(bank*0x100 + 0xd0, 0x10,\n\t\t\t\t\tmem0rd + 0xd000, mem0wr + 0xd000);\n\t}\n\n\t/* No need for fixup_brks() since only called from set_statereg(), */\n\t/*  or from other routines which will handle it */\n}\n\nvoid\nset_statereg(dword64 dfcyc, word32 val)\n{\n\tword32\txor;\n\n\tdbg_log_info(dfcyc, val, g_c068_statereg, 0xc068);\n\n\txor = val ^ g_c068_statereg;\n\tg_c068_statereg = val;\n\tif(xor == 0) {\n\t\treturn;\n\t}\n\n\tif(xor & 0x28c) {\t\t// WRDEFRAM, ALTZP, RDROM, LCBANK2\n\t\tfixup_lc();\n\t\tif(xor & 0x80) {\t\t/* altzp */\n\t\t\tfixup_altzp();\n\t\t}\n\t}\n\tif(xor & 0x40) {\t\t// page2\n\t\tg_cur_a2_stat = (g_cur_a2_stat & ~ALL_STAT_PAGE2) |\n\t\t\t\t\t\t(val & ALL_STAT_PAGE2);\n\t\tfixup_page2(dfcyc);\n\t}\n\n\tif(xor & 0x20) {\t\t// RAMRD\n\t\tfixup_ramrd();\n\t}\n\n\tif(xor & 0x10) {\t\t// RAMWRT\n\t\tfixup_ramwrt();\n\t}\n\n\tif(xor & 0x02) {\t\t// ROMBANK\n\t\thalt_printf(\"Just set rombank = %d\\n\", ROMB);\n\t}\n\n\tif(xor & 0x401) {\t\t// INTC8ROM, INTCX\n\t\tfixup_intcx();\n\t}\n\n\tif(xor) {\n\t\tfixup_brks();\n\t}\n}\n\nvoid\nfixup_shadow_txt1()\n{\n\tbyte\t*mem0wr;\n\tint\tj;\n\n\tfixup_bank0_0400_0800();\n\n\tmem0wr = &(g_memory_ptr[0x10000]);\n\tif((g_c035_shadow_reg & 0x01) == 0) {\n\t\tmem0wr += BANK_SHADOW2;\n\t}\n\tfor(j = 4; j < 8; j++) {\n\t\tSET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100);\n\t}\n}\n\nvoid\nfixup_shadow_txt2()\n{\n\tbyte\t*mem0wr;\n\tint\tshadow;\n\tint\tj;\n\n\t/* bank 0 */\n\tmem0wr = &(g_memory_ptr[0x00000]);\n\tshadow = BANK_SHADOW;\n\tif(RAMWRT) {\n\t\tmem0wr += 0x10000;\n\t\tshadow = BANK_SHADOW2;\n\t}\n\tif(((g_c035_shadow_reg & 0x20) == 0) &&\n\t\t\t((g_rom_version != 1) || g_user_page2_shadow)) {\n\t\tmem0wr += shadow;\n\t}\n\tfor(j = 8; j < 0xc; j++) {\n\t\tSET_PAGE_INFO_WR(j, mem0wr + j*0x100);\n\t}\n\n\t/* and bank 1 */\n\tmem0wr = &(g_memory_ptr[0x10000]);\n\tif(((g_c035_shadow_reg & 0x20) == 0) &&\n\t\t\t((g_rom_version != 1) || g_user_page2_shadow)) {\n\t\tmem0wr += BANK_SHADOW2;\n\t}\n\tfor(j = 8; j < 0xc; j++) {\n\t\tSET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100);\n\t}\n}\n\nvoid\nfixup_shadow_hires1()\n{\n\tbyte\t*mem0wr;\n\tint\tj;\n\n\tfixup_bank0_2000_4000();\n\n\t/* and bank 1 */\n\tmem0wr = &(g_memory_ptr[0x10000]);\n\tif((g_c035_shadow_reg & 0x12) == 0 || (g_c035_shadow_reg & 0x8) == 0) {\n\t\tmem0wr += BANK_SHADOW2;\n\t}\n\tfor(j = 0x20; j < 0x40; j++) {\n\t\tSET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100);\n\t}\n}\n\nvoid\nfixup_shadow_hires2()\n{\n\tbyte\t*mem0wr;\n\tint\tj;\n\n\t/* bank 0 */\n\tmem0wr = &(g_memory_ptr[0x00000]);\n\tif(RAMWRT) {\n\t\tmem0wr += 0x10000;\n\t\tif((g_c035_shadow_reg & 0x14) == 0 ||\n\t\t\t\t\t(g_c035_shadow_reg & 0x8) == 0) {\n\t\t\tmem0wr += BANK_SHADOW2;\n\t\t}\n\t} else if((g_c035_shadow_reg & 0x04) == 0) {\n\t\tmem0wr += BANK_SHADOW;\n\t}\n\tfor(j = 0x40; j < 0x60; j++) {\n\t\tSET_PAGE_INFO_WR(j, mem0wr + j*0x100);\n\t}\n\n\t/* and bank 1 */\n\tmem0wr = &(g_memory_ptr[0x10000]);\n\tif((g_c035_shadow_reg & 0x14) == 0 || (g_c035_shadow_reg & 0x8) == 0) {\n\t\tmem0wr += BANK_SHADOW2;\n\t}\n\tfor(j = 0x40; j < 0x60; j++) {\n\t\tSET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100);\n\t}\n}\n\nvoid\nfixup_shadow_shr()\n{\n\tbyte\t*mem0wr;\n\tint\tj;\n\n\t/* bank 0, only pages 0x60 - 0xa0 */\n\tmem0wr = &(g_memory_ptr[0x00000]);\n\tif(RAMWRT) {\n\t\tmem0wr += 0x10000;\n\t\tif((g_c035_shadow_reg & 0x8) == 0) {\n\t\t\tmem0wr += BANK_SHADOW2;\n\t\t}\n\t}\n\tfor(j = 0x60; j < 0xa0; j++) {\n\t\tSET_PAGE_INFO_WR(j, mem0wr + j*0x100);\n\t}\n\n\t/* and bank 1, only pages 0x60 - 0xa0 */\n\tmem0wr = &(g_memory_ptr[0x10000]);\n\tif((g_c035_shadow_reg & 0x8) == 0) {\n\t\tmem0wr += BANK_SHADOW2;\n\t}\n\tfor(j = 0x60; j < 0xa0; j++) {\n\t\tSET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100);\n\t}\n}\n\nvoid\nfixup_shadow_iolc()\n{\n\tbyte\t*mem0rd;\n\tint\tk;\n\n\tif(g_c035_shadow_reg & 0x40) {\n\t\t/* Disable language card area */\n\t\tfor(k = 0; k < 2; k++) {\n\t\t\tmem0rd = &(g_memory_ptr[k << 16]);\n\t\t\tfixup_any_bank_any_page((k << 8) + 0xc0, 0x10,\n\t\t\t\tmem0rd + 0xd000, mem0rd + 0xd000);\n\t\t\tif(k == 0 && ALTZP) {\n\t\t\t\tmem0rd += 0x10000;\n\t\t\t}\n\t\t\tfixup_any_bank_any_page((k << 8) + 0xd0, 0x10,\n\t\t\t\tmem0rd + 0xc000, mem0rd + 0xc000);\n\t\t\tfixup_any_bank_any_page((k << 8) + 0xe0, 0x20,\n\t\t\t\tmem0rd + 0xe000, mem0rd + 0xe000);\n\t\t}\n\t} else {\n\t\t/* 0xc000 area */\n\t\tfixup_intcx();\n\n\t\t// Fix 0xd000-0xffff banks 0, 1, 0xe0, 0xe1\n\t\tfixup_lc();\n\t}\n}\n\nvoid\nupdate_shadow_reg(dword64 dfcyc, word32 val)\n{\n\tint\txor;\n\n\tdbg_log_info(dfcyc, val, g_c035_shadow_reg, 0xc035);\n\n\tif(g_c035_shadow_reg == val) {\n\t\treturn;\n\t}\n\n\txor = g_c035_shadow_reg ^ val;\n\tg_c035_shadow_reg = val;\n\n\tif(xor & 8) {\n\t\tfixup_shadow_hires1();\n\t\tfixup_shadow_hires2();\n\t\tfixup_shadow_shr();\n\t\txor = xor & (~0x16);\n\t}\n\tif(xor & 0x10) {\n\t\tfixup_shadow_hires1();\n\t\tfixup_shadow_hires2();\n\t\txor = xor & (~0x6);\n\t}\n\tif(xor & 2) {\n\t\tfixup_shadow_hires1();\n\t}\n\tif(xor & 4) {\n\t\tfixup_shadow_hires2();\n\t}\n\tif(xor & 1) {\n\t\tfixup_shadow_txt1();\n\t}\n\tif((xor & 0x20) && ((g_rom_version != 1) || g_user_page2_shadow)) {\n\t\tfixup_shadow_txt2();\n\t}\n\tif(xor & 0x40) {\n\t\tfixup_shadow_iolc();\n\t}\n\tif(xor) {\n\t\tfixup_brks();\n\t}\n}\n\nvoid\nfixup_shadow_all_banks()\n{\n\tbyte\t*mem0rd;\n\tint\tshadow;\n\tint\tnum_banks;\n\tint\tj, k;\n\n\t/* Assume Ninja Force Megademo */\n\t/* only do banks 3 - num_banks by 2, shadowing into e1 */\n\n\tshadow = 0;\n\tif((g_c036_val_speed & 0x10) && ((g_c035_shadow_reg & 0x08) == 0)) {\n\t\tshadow = BANK_SHADOW2;\n\t}\n\tnum_banks = g_mem_size_total >> 16;\n\tfor(k = 3; k < num_banks; k += 2) {\n\t\tmem0rd = &(g_memory_ptr[k*0x10000 + 0x2000]) + shadow;\n\t\tfor(j = 0x20; j < 0xa0; j++) {\n\t\t\tSET_PAGE_INFO_WR(k*0x100 + j, mem0rd);\n\t\t\tmem0rd += 0x100;\n\t\t}\n\t}\n\n\tfixup_brks();\n}\n\nvoid\nsetup_pageinfo()\n{\n\tbyte\t*mem0rd;\n\tword32\tmem_size_pages;\n\n\t/* first, set all of memory to point to itself */\n\tmem_size_pages = g_mem_size_total >> 8;\n\tmem0rd = &(g_memory_ptr[0]);\n\tfixup_any_bank_any_page(0, mem_size_pages, mem0rd, mem0rd);\n\n\t/* mark unused memory as BAD_MEM */\n\tfixup_any_bank_any_page(mem_size_pages, 0xfc00-mem_size_pages,\n\t\t\tBANK_BAD_MEM, BANK_BAD_MEM);\n\n\tfixup_shadow_all_banks();\n\n\t/* ROM */\n\tmem0rd = &(g_rom_fc_ff_ptr[0]);\n\tfixup_any_bank_any_page(0xfc00, 0x400, mem0rd,\n\t\t\t\tmem0rd + (BANK_IO_TMP | BANK_IO2_TMP));\n\n\t/* banks e0, e1 */\n\tmem0rd = &(g_slow_memory_ptr[0]);\n\tfixup_any_bank_any_page(0xe000, 0x200, mem0rd, mem0rd);\n\t\t\t\t// First, did all 128KB\n\n\tfixup_any_bank_any_page(0xe004, 0x08, mem0rd + 0x0400,\n\t\t\t\t\t\tmem0rd + 0x0400 + BANK_SHADOW);\n\tfixup_any_bank_any_page(0xe020, 0x80, mem0rd + 0x2000,\n\t\t\t\t\t\tmem0rd + 0x2000 + BANK_SHADOW);\n\n\tmem0rd = &(g_slow_memory_ptr[0x10000]);\n\tfixup_any_bank_any_page(0xe104, 0x08, mem0rd + 0x0400,\n\t\t\t\t\t\tmem0rd + 0x0400 + BANK_SHADOW2);\n\tfixup_any_bank_any_page(0xe120, 0x80, mem0rd + 0x2000,\n\t\t\t\t\t\tmem0rd + 0x2000 + BANK_SHADOW2);\n\n\tfixup_intcx();\t/* correct banks 0xe0,0xe1, 0xc000-0xcfff area */\n\tfixup_lc();\t/* correct 0xd000-0xdfff area */\n\n\tfixup_bank0_2000_4000();\n\tfixup_bank0_0400_0800();\n\tfixup_altzp();\n\tfixup_ramrd();\n\tfixup_ramwrt();\n\tfixup_shadow_txt1();\n\tfixup_shadow_txt2();\n\tfixup_shadow_hires1();\n\tfixup_shadow_hires2();\n\tfixup_shadow_shr();\n\tfixup_shadow_iolc();\n\tfixup_brks();\n}\n\nvoid\nshow_bankptrs_bank0rdwr()\n{\n\tshow_bankptrs(0);\n\tshow_bankptrs(1);\n\tshow_bankptrs(0xe0);\n\tshow_bankptrs(0xe1);\n\tprintf(\"statereg: %02x\\n\", g_c068_statereg);\n}\n\nvoid\nshow_bankptrs(int bnk)\n{\n\tint i;\n\tPg_info rd, wr;\n\tbyte *ptr_rd, *ptr_wr;\n\n\tprintf(\"g_memory_ptr: %p, dummy_mem: %p, slow_mem_ptr: %p\\n\",\n\t\tg_memory_ptr, g_dummy_memory1_ptr, g_slow_memory_ptr);\n\tprintf(\"g_rom_fc_ff_ptr: %p\\n\", g_rom_fc_ff_ptr);\n\n\tprintf(\"Showing bank_info array for %02x\\n\", bnk);\n\tfor(i = 0; i < 256; i++) {\n\t\trd = GET_PAGE_INFO_RD(bnk*0x100 + i);\n\t\twr = GET_PAGE_INFO_WR(bnk*0x100 + i);\n\t\tptr_rd = (byte *)rd;\n\t\tptr_wr = (byte *)wr;\n\t\tprintf(\"%04x rd: \", bnk*256 + i);\n\t\tshow_addr(ptr_rd);\n\t\tprintf(\" wr: \");\n\t\tshow_addr(ptr_wr);\n\t\tprintf(\"\\n\");\n\t}\n}\n\nvoid\nshow_addr(byte *ptr)\n{\n\tword32\tmem_size;\n\n\tmem_size = g_mem_size_total;\n\tif(ptr >= g_memory_ptr && ptr < &g_memory_ptr[mem_size]) {\n\t\tprintf(\"%p--memory[%06x]\", ptr,\n\t\t\t\t\t(word32)(ptr - g_memory_ptr));\n\t} else if(ptr >= g_rom_fc_ff_ptr && ptr < &g_rom_fc_ff_ptr[256*1024]) {\n\t\tprintf(\"%p--rom_fc_ff[%06x]\", ptr,\n\t\t\t\t\t(word32)(ptr - g_rom_fc_ff_ptr));\n\t} else if(ptr >= g_slow_memory_ptr && ptr<&g_slow_memory_ptr[128*1024]){\n\t\tprintf(\"%p--slow_memory[%06x]\", ptr,\n\t\t\t\t\t(word32)(ptr - g_slow_memory_ptr));\n\t} else if(ptr >=g_dummy_memory1_ptr && ptr < &g_dummy_memory1_ptr[256]){\n\t\tprintf(\"%p--dummy_memory[%06x]\", ptr,\n\t\t\t\t\t(word32)(ptr - g_dummy_memory1_ptr));\n\t} else {\n\t\tprintf(\"%p--unknown\", ptr);\n\t}\n}\n\nword32\nmoremem_fix_vector_pull(word32 addr)\n{\n\t// Default vector for BRK will come from 0xfffffe.  But if\n\t//  I/O shadowing is off, or we're a //e, then get from bank0\n\tif((g_c035_shadow_reg & 0x40) || (g_rom_version == 0)) {\n\t\t// I/O shadowing off, or Apple //e: use RAM loc\n\t\taddr = addr & 0xffff;\n\t}\n\treturn addr;\n}\n\nword32\nio_read(word32 loc, dword64 *cyc_ptr)\n{\n\tdword64\tdfcyc;\n\tword32\tval;\n\tword32\tnew_lcbank2, new_wrdefram, new_prewrite, new_rdrom, tmp;\n\tint\ti;\n\n\tdfcyc = *cyc_ptr;\n\n/* IO space */\n\tswitch((loc >> 8) & 0xf) {\n\tcase 0: /* 0xc000 - 0xc0ff */\n\t\tswitch(loc & 0xff) {\n\t\t/* 0xc000 - 0xc00f */\n\t\tcase 0x00: case 0x01: case 0x02: case 0x03:\n\t\tcase 0x04: case 0x05: case 0x06: case 0x07:\n\t\tcase 0x08: case 0x09: case 0x0a: case 0x0b:\n\t\tcase 0x0c: case 0x0d: case 0x0e: case 0x0f:\n\t\t\treturn(adb_read_c000());\n\n\t\t/* 0xc010 - 0xc01f */\n\t\tcase 0x10: /* c010 */\n\t\t\treturn(adb_access_c010());\n\t\tcase 0x11: /* c011 = RDLCBANK2 */\n\t\t\treturn IOR(LCBANK2);\n\t\tcase 0x12: /* c012= RDLCRAM */\n\t\t\treturn IOR(!RDROM);\n\t\tcase 0x13: /* c013=rdramd */\n\t\t\treturn IOR(RAMRD);\n\t\tcase 0x14: /* c014=rdramwrt */\n\t\t\treturn IOR(RAMWRT);\n\t\tcase 0x15: /* c015 = RDCXROM = INTCX */\n\t\t\treturn IOR(g_c068_statereg & 1);\n\t\tcase 0x16: /* c016: Read ALTZP, 0 = Read Main ZP; 1 = Alt ZP */\n\t\t\treturn IOR(ALTZP);\n\t\tcase 0x17: /* c017: rdc3rom */\n\t\t\treturn IOR(g_c02d_int_crom & (1 << 3));\n\t\tcase 0x18: /* c018: rd80c0l */\n\t\t\treturn IOR((g_cur_a2_stat & ALL_STAT_ST80));\n\t\tcase 0x19: /* c019: rdvblbar: MSB set if in VBL */\n\t\t\ttmp = in_vblank(dfcyc);\n\t\t\tif(g_rom_version == 0) {\t// Apple //e\n\t\t\t\ttmp = tmp ^ 1;\t\t// 1=not in VBL on //e\n\t\t\t}\n\t\t\treturn IOR(tmp);\n\t\tcase 0x1a: /* c01a: rdtext */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_TEXT);\n\t\tcase 0x1b: /* c01b: rdmix */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_MIX_T_GR);\n\t\tcase 0x1c: /* c01c: rdpage2 */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_PAGE2);\n\t\tcase 0x1d: /* c01d: rdhires */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_HIRES);\n\t\tcase 0x1e: /* c01e: altcharset on? */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_ALTCHARSET);\n\t\tcase 0x1f: /* c01f: rd80vid */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_VID80);\n\n\t\t/* 0xc020 - 0xc02f */\n\t\tcase 0x20: /* 0xc020 */\n\t\t\t/* Click cassette port */\n\t\t\treturn float_bus(dfcyc);\n\t\tcase 0x21: /* 0xc021 */\n\t\t\t/* Not documented, but let's return COLOR_C021 */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_COLOR_C021);\n\t\tcase 0x22: /* 0xc022 */\n\t\t\treturn (g_cur_a2_stat >> BIT_ALL_STAT_BG_COLOR) & 0xff;\n\t\tcase 0x23: /* 0xc023 */\n\t\t\treturn g_c023_val;\n\t\tcase 0x24: /* 0xc024 */\n\t\t\treturn mouse_read_c024(dfcyc);\n\t\tcase 0x25: /* 0xc025 */\n\t\t\treturn adb_read_c025();\n\t\tcase 0x26: /* 0xc026 */\n\t\t\treturn adb_read_c026();\n\t\tcase 0x27: /* 0xc027 */\n\t\t\treturn adb_read_c027();\n\t\tcase 0x28: /* 0xc028 */\n\t\t\treturn 0;\n\t\tcase 0x29: /* 0xc029 */\n\t\t\treturn((g_cur_a2_stat & 0xa0) | g_c029_val_some);\n\t\tcase 0x2a: /* 0xc02a */\n#if 0\n\t\t\tprintf(\"Reading c02a...returning 0\\n\");\n#endif\n\t\t\treturn 0;\n\t\tcase 0x2b: /* 0xc02b */\n\t\t\treturn g_c02b_val;\n\t\tcase 0x2c: /* 0xc02c */\n\t\t\t/* printf(\"reading c02c, returning 0\\n\"); */\n\t\t\tif(g_c06d_val == 0x40) {\t// Test mode $40\n\t\t\t\treturn read_video_data(dfcyc);\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x2d: /* 0xc02d */\n\t\t\treturn g_c02d_int_crom;\n\t\tcase 0x2e: /* 0xc02e */\n\t\tcase 0x2f: /* 0xc02f */\n\t\t\treturn read_vid_counters(loc, dfcyc);\n\n\t\t/* 0xc030 - 0xc03f */\n\t\tcase 0x30: /* 0xc030 */\n\t\t\t/* click speaker */\n\t\t\treturn sound_read_c030(dfcyc);\n\t\tcase 0x31: /* 0xc031 */\n\t\t\t/* 3.5\" control */\n\t\t\treturn g_iwm.state & 0xc0;\n\t\tcase 0x32: /* 0xc032 */\n\t\t\t/* scan int */\n\t\t\treturn 0;\n\t\tcase 0x33: /* 0xc033 = CLOCKDATA*/\n\t\t\treturn g_c033_data;\n\t\tcase 0x34: /* 0xc034 = CLOCKCTL */\n\t\t\treturn g_c034_val;\n\t\tcase 0x35: /* 0xc035 */\n\t\t\treturn g_c035_shadow_reg;\n\t\tcase 0x36: /* 0xc036 = CYAREG */\n\t\t\treturn g_c036_val_speed;\n\t\tcase 0x37: /* 0xc037 */\n\t\t\treturn 0;\n\t\tcase 0x38: /* 0xc038 */\n\t\t\treturn scc_read_reg(dfcyc, 1);\n\t\tcase 0x39: /* 0xc039 */\n\t\t\treturn scc_read_reg(dfcyc, 0);\n\t\tcase 0x3a: /* 0xc03a */\n\t\t\treturn scc_read_data(dfcyc, 1);\n\t\tcase 0x3b: /* 0xc03b */\n\t\t\treturn scc_read_data(dfcyc, 0);\n\t\tcase 0x3c: /* 0xc03c */\n\t\t\t/* doc control */\n\t\t\treturn doc_read_c03c();\n\t\tcase 0x3d: /* 0xc03d */\n\t\t\treturn doc_read_c03d(dfcyc);\n\t\tcase 0x3e: /* 0xc03e */\n\t\t\treturn (g_c03ef_doc_ptr & 0xff);\n\t\tcase 0x3f: /* 0xc03f */\n\t\t\treturn (g_c03ef_doc_ptr >> 8);\n\n\t\t/* 0xc040 - 0xc04f */\n\t\tcase 0x40: /* 0xc040 */\n\t\t\t/* cassette */\n\t\t\treturn 0;\n\t\tcase 0x41: /* 0xc041 */\n\t\t\treturn g_c041_val;\n\t\tcase 0x45: /* 0xc045 */\n\t\t\t//halt_printf(\"Mega II mouse read: c045\\n\");\n\t\t\treturn 0;\n\t\tcase 0x46: /* 0xc046 */\n\t\t\ttmp = g_c046_val;\n\t\t\tg_c046_val = (tmp & 0xbf) + ((tmp & 0x80) >> 1);\n\t\t\treturn tmp;\n\t\tcase 0x47: /* 0xc047 */\n\t\t\tremove_irq(IRQ_PENDING_C046_25SEC |\n\t\t\t\t\t\t\tIRQ_PENDING_C046_VBL);\n\t\t\tg_c046_val &= 0xe7;\t/* clear vbl_int, 1/4sec int*/\n\t\t\treturn 0;\n\t\tcase 0x42: /* 0xc042 */\n\t\tcase 0x43: /* 0xc043 */\n\t\t\treturn 0;\n\t\tcase 0x4f: /* 0xc04f */\n\t\t\t/* for information on c04f, see: */\n\t\t\t/* www.sheppyware.net/tech/hardware/softswitches.html */\n\t\t\t/* write to $c04f to start.  Then read $c04f to get */\n\t\t\t/* emulator ($16=sweet16, $fe=bernie II). */\n\t\t\t/* Then read again to get version: $21 == 2.1 */\n\t\t\tswitch(g_em_emubyte_cnt) {\n\t\t\tcase 1:\n\t\t\t\tg_em_emubyte_cnt = 2;\n\t\t\t\treturn 'K';\n\t\t\tcase 2:\n\t\t\t\tg_em_emubyte_cnt = 0;\n\t\t\t\ttmp = g_kegs_version_str[0] - '0';\n\t\t\t\ti = g_kegs_version_str[2] - '0';\n\t\t\t\treturn ((tmp & 0xf) << 4) + (i & 0xf);\n\t\t\tdefault:\n\t\t\t\tg_em_emubyte_cnt = 0;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\tcase 0x44: /* 0xc044 */\n\t\tcase 0x48: /* 0xc048 */\n\t\tcase 0x49: /* 0xc049 */\n\t\tcase 0x4a: /* 0xc04a */\n\t\tcase 0x4b: /* 0xc04b */\n\t\tcase 0x4c: /* 0xc04c */\n\t\tcase 0x4d: /* 0xc04d */\n\t\tcase 0x4e: /* 0xc04e */\n\t\t\tUNIMPL_READ;\n\n\t\t/* 0xc050 - 0xc05f */\n\t\tcase 0x50: /* 0xc050 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tif(g_cur_a2_stat & ALL_STAT_TEXT) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_TEXT);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn val;\n\t\tcase 0x51: /* 0xc051 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tif((g_cur_a2_stat & ALL_STAT_TEXT) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_TEXT);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn val;\n\t\tcase 0x52: /* 0xc052 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tif(g_cur_a2_stat & ALL_STAT_MIX_T_GR) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_MIX_T_GR);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn val;\n\t\tcase 0x53: /* 0xc053 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tif((g_cur_a2_stat & ALL_STAT_MIX_T_GR) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_MIX_T_GR);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn val;\n\t\tcase 0x54: /* 0xc054 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tset_statereg(dfcyc, g_c068_statereg & (~0x40));\n\t\t\treturn val;\n\t\t\treturn float_bus(dfcyc);\n\t\tcase 0x55: /* 0xc055 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x40);\n\t\t\treturn val;\n\t\tcase 0x56: /* 0xc056 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tif(g_cur_a2_stat & ALL_STAT_HIRES) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_HIRES);\n\t\t\t\tfixup_hires_on();\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn val;\n\t\tcase 0x57: /* 0xc057 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tif((g_cur_a2_stat & ALL_STAT_HIRES) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_HIRES);\n\t\t\t\tfixup_hires_on();\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn val;\n\t\tcase 0x58: /* 0xc058 */\n\t\t\tif(g_zipgs_unlock < 4) {\n\t\t\t\tg_c05x_annuncs &= (~1);\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x59: /* 0xc059 */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\treturn g_zipgs_reg_c059;\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs |= 1;\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x5a: /* 0xc05a */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\treturn g_zipgs_reg_c05a;\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs &= (~2);\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x5b: /* 0xc05b */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\t// Bit 7 is 1msclk, clock with 1ms period.\n\t\t\t\ttmp = (dfcyc >> 25) & 1;\n\t\t\t\treturn (tmp << 7) + (g_zipgs_reg_c05b & 0x7f);\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs |= 2;\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x5c: /* 0xc05c */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\treturn g_zipgs_reg_c05c;\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs &= (~4);\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x5d: /* 0xc05d */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\thalt_printf(\"Reading ZipGS $c05d!\\n\");\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs |= 4;\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x5e: /* 0xc05e */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\thalt_printf(\"Reading ZipGS $c05e!\\n\");\n\t\t\t} else if(g_cur_a2_stat & ALL_STAT_ANNUNC3) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_ANNUNC3);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x5f: /* 0xc05f */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\thalt_printf(\"Reading ZipGS $c05f!\\n\");\n\t\t\t} else if((g_cur_a2_stat & ALL_STAT_ANNUNC3) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_ANNUNC3);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn 0;\n\n\n\t\t/* 0xc060 - 0xc06f */\n\t\tcase 0x60: /* 0xc060 */\n\t\t\treturn IOR(g_paddle_buttons & 8);\n\t\tcase 0x61: /* 0xc061 */\n\t\t\treturn IOR(adb_is_cmd_key_down() ||\n\t\t\t\t\t\t\tg_paddle_buttons & 1);\n\t\tcase 0x62: /* 0xc062 */\n\t\t\treturn IOR(adb_is_option_key_down() ||\n\t\t\t\t\t\t\tg_paddle_buttons & 2);\n\t\tcase 0x63: /* 0xc063 */\n\t\t\treturn IOR(g_paddle_buttons & 4);\n\t\tcase 0x64: /* 0xc064 */\n\t\t\treturn read_paddles(dfcyc, 0);\n\t\tcase 0x65: /* 0xc065 */\n\t\t\treturn read_paddles(dfcyc, 1);\n\t\tcase 0x66: /* 0xc066 */\n\t\t\treturn read_paddles(dfcyc, 2);\n\t\tcase 0x67: /* 0xc067 */\n\t\t\treturn read_paddles(dfcyc, 3);\n\t\tcase 0x68: /* 0xc068 = STATEREG */\n\t\t\treturn g_c068_statereg & 0xff;\n\t\tcase 0x69: /* 0xc069 */\n\t\t\t/* Reserved reg, return 0 */\n\t\t\treturn 0;\n\t\tcase 0x6a: /* 0xc06a */\n\t\tcase 0x6b: /* 0xc06b */\n\t\t\tUNIMPL_READ;\n\t\tcase 0x6c: /* 0xc06c */\n\t\tcase 0x6d: /* 0xc06d */\n\t\tcase 0x6e: /* 0xc06e */\n\t\tcase 0x6f: /* 0xc06f */\n\t\t\treturn g_c06c_latched_cyc >> (8 * (loc & 3)) & 0xff;\n\n\t\t/* 0xc070 - 0xc07f */\n\t\tcase 0x70: /* c070 */\n\t\t\tpaddle_trigger(dfcyc);\n\t\t\treturn float_bus(dfcyc);\n\t\tcase 0x71:\t/* 0xc071 */\n\t\tcase 0x72: case 0x73:\n\t\tcase 0x74: case 0x75: case 0x76: case 0x77:\n\t\tcase 0x78: case 0x79: case 0x7a: case 0x7b:\n\t\tcase 0x7c: case 0x7d: case 0x7e: case 0x7f:\n\t\t\treturn g_rom_fc_ff_ptr[3*65536 + 0xc000 + (loc & 0xff)];\n\n\t\t/* 0xc080 - 0xc08f */\n\t\tcase 0x80: case 0x81: case 0x82: case 0x83:\n\t\tcase 0x84: case 0x85: case 0x86: case 0x87:\n\t\tcase 0x88: case 0x89: case 0x8a: case 0x8b:\n\t\tcase 0x8c: case 0x8d: case 0x8e: case 0x8f:\n\t\t\tnew_lcbank2 = ((loc >> 1) & 0x4) ^ 0x4;\n\t\t\tnew_rdrom = ((loc << 3) ^ (loc << 2)) & 8;\n\t\t\t\t// new_rdrom is set if loc[0] ^ loc[1] is true\n\t\t\t\t// 8=RDROM, 0=read from LC RAM\n\t\t\tnew_prewrite = (loc & 1) << 8;\t// Odd read set PREWRITE\n\t\t\tnew_wrdefram = g_c068_statereg & 0x200;\n\t\t\tif((loc & 1) == 0) {\n\t\t\t\tnew_wrdefram = 0;\t// Any even access clrs\n\t\t\t} else {\n\t\t\t\tnew_wrdefram |= (g_c068_statereg << 1) & 0x200;\n\t\t\t\t// Odd read also makes Ram wr if PREWR was set\n\t\t\t}\n\t\t\tset_statereg(dfcyc, (g_c068_statereg & ~(0x30c)) |\n\t\t\t\t\tnew_lcbank2 | new_rdrom |\n\t\t\t\t\tnew_prewrite | new_wrdefram);\n\t\t\t// access 0-7: lcbank1, 8-f: lcbank0\n\t\t\t// a0!=a1: set rdrom (c081,c082,c085,c086, etc.)\n\t\t\t// a0=1 && rd set prewrite.  a0=0, or wr: clear prewrite\n\t\t\t// wrdefram is clr if a0=0, set if prewrite and\n\t\t\t//  old_prewrite are set, otherwise stays same.\n\t\t\t// From Apple language card schematics:\n\t\t\t//  wr_def = (wr_def_ff || (prewr && prewr_ff)) && a0;\n\t\t\t//  prewr = read && a0\n\t\t\treturn float_bus(dfcyc);\n\t\t/* 0xc090 - 0xc09f */\n\t\tcase 0x90: case 0x91: case 0x92: case 0x93:\n\t\tcase 0x94: case 0x95: case 0x96: case 0x97:\n\t\tcase 0x98: case 0x99: case 0x9a: case 0x9b:\n\t\tcase 0x9c: case 0x9d: case 0x9e: case 0x9f:\n\t\t\t/* UNIMPL_READ; */\n\t\t\treturn 0;\n\t\t/* 0xc0a0 - 0xc0af */\n\t\tcase 0xa0: case 0xa1: case 0xa2: case 0xa3:\n\t\tcase 0xa4: case 0xa5: case 0xa6: case 0xa7:\n\t\tcase 0xa8: case 0xa9: case 0xaa: case 0xab:\n\t\tcase 0xac: case 0xad: case 0xae: case 0xaf:\n\t\t\treturn 0;\n\t\t\t/* UNIMPL_READ; */\n\n\t\t/* 0xc0b0 - 0xc0bf */\n\t\tcase 0xb0: case 0xb1: case 0xb2: case 0xb3:\n\t\tcase 0xb4: case 0xb5: case 0xb6: case 0xb7:\n\t\tcase 0xb8: case 0xb9: case 0xba: case 0xbb:\n\t\tcase 0xbc: case 0xbd: case 0xbe: case 0xbf:\n\t\t\treturn voc_devsel_read(loc, dfcyc);\n\n\t\t/* 0xc0c0 - 0xc0cf */\n\t\tcase 0xc0: case 0xc1: case 0xc2: case 0xc3:\n\t\t\t// Slot 4 has a Slinky RAM card\n\t\t\treturn slinky_devsel_read(dfcyc, loc);\n\t\tcase 0xc4: case 0xc5: case 0xc6: case 0xc7:\n\t\tcase 0xc8: case 0xc9: case 0xca: case 0xcb:\n\t\tcase 0xcc: case 0xcd: case 0xce: case 0xcf:\n\t\t\treturn 0;\n\t\t/* 0xc0d0 - 0xc0df */\n\t\tcase 0xd0: case 0xd1: case 0xd2: case 0xd3:\n\t\tcase 0xd4: case 0xd5: case 0xd6: case 0xd7:\n\t\tcase 0xd8: case 0xd9: case 0xda: case 0xdb:\n\t\tcase 0xdc: case 0xdd: case 0xde: case 0xdf:\n\t\t\treturn 0;\n\t\t/* 0xc0e0 - 0xc0ef */\n\t\tcase 0xe0: case 0xe1: case 0xe2: case 0xe3:\n\t\tcase 0xe4: case 0xe5: case 0xe6: case 0xe7:\n\t\tcase 0xe8: case 0xe9: case 0xea: case 0xeb:\n\t\tcase 0xec: case 0xed: case 0xee: case 0xef:\n\t\t\treturn read_iwm(loc, dfcyc);\n\t\t/* 0xc0f0 - 0xc0ff */\n\t\tcase 0xf0: case 0xf1: case 0xf2: case 0xf3:\n\t\tcase 0xf4: case 0xf5: case 0xf6: case 0xf7:\n\t\tcase 0xf8: case 0xf9: case 0xfa: case 0xfb:\n\t\tcase 0xfc: case 0xfd: case 0xfe: case 0xff:\n\t\t\treturn 0;\n\n\t\tdefault:\n\t\t\tprintf(\"loc: %04x bad\\n\", loc);\n\t\t\tUNIMPL_READ;\n\t\t}\n\tcase 1: case 2: case 5: case 6: case 7:\n\t\t/* c100 - c7ff, (except c3xx, c4xx) */\n\t\treturn float_bus(dfcyc);\n\tcase 3:\t// c300\n\t\treturn c3xx_read(dfcyc, loc);\n\tcase 4:\n\t\treturn mockingboard_read(loc, dfcyc);\n\tcase 8: case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0xe:\n\t\treturn float_bus(dfcyc);\n\t\t// UNIMPL_READ;\n\tcase 0xf:\t// $CFxx\n\t\tif(g_c068_statereg & 0x401) {\t\t// INTC8ROM or INTCX\n\t\t\tval = g_rom_fc_ff_ptr[0x3cf00 + (loc & 0xff)];\n\t\t} else {\n\t\t\tval = float_bus(dfcyc);\n\t\t}\n\t\tif((loc & 0xfff) == 0xfff) {\n\t\t\tif(g_c068_statereg & 0x400) {\n\t\t\t\tset_statereg(dfcyc, g_c068_statereg & (~0x400));\n\t\t\t}\n\t\t}\n\t\treturn val;\n\t\t// UNIMPL_READ;\n\t}\n\n\thalt_printf(\"io_read: hit end, loc: %06x\\n\", loc);\n\n\treturn 0xff;\n}\n\nvoid\nio_write(word32 loc, word32 val, dword64 *cyc_ptr)\n{\n\tdword64\tdfcyc;\n\tword32\tnew_lcbank2, new_wrdefram, new_rdrom, new_prewrite, tmp;\n\tword32\tnew_tmp;\n\tint\tfixup;\n\n\tdfcyc = *cyc_ptr;\n\n\tval = val & 0xff;\n\tswitch((loc >> 8) & 0xf) {\n\tcase 0: /* 0xc000 - 0xc0ff */\n\t\tswitch(loc & 0xff) {\n\t\t/* 0xc000 - 0xc00f */\n\t\tcase 0x00: /* 0xc000: CLR80COL */\n\t\t\tif(g_cur_a2_stat & ALL_STAT_ST80) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_ST80);\n\t\t\t\tfixup_st80col(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x01: /* 0xc001: SET80COL, enable 80-column store */\n\t\t\tif((g_cur_a2_stat & ALL_STAT_ST80) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_ST80);\n\t\t\t\tfixup_st80col(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x02: /* 0xc002: Set RDMAINRAM */\n\t\t\tset_statereg(dfcyc, g_c068_statereg & ~0x20);\n\t\t\treturn;\n\t\tcase 0x03: /* 0xc003: Set RDCARDRAM (alt) */\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x20);\n\t\t\treturn;\n\t\tcase 0x04: /* 0xc004: Set WRMAINRAM */\n\t\t\tset_statereg(dfcyc, g_c068_statereg & ~0x10);\n\t\t\treturn;\n\t\tcase 0x05: /* 0xc005: Set WRCARDRAM (alt) */\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x10);\n\t\t\treturn;\n\t\tcase 0x06: /* 0xc006 = SETSLOTCXROM, use slot rom c800 */\n\t\t\tset_statereg(dfcyc, g_c068_statereg & ~0x01);\n\t\t\treturn;\n\t\tcase 0x07: /* 0xc007 = SETINTXROM, use int rom c800 */\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x01);\n\t\t\treturn;\n\t\tcase 0x08: /* 0xc008: SETSTDZP (main ZP/LC) */\n\t\t\tset_statereg(dfcyc, g_c068_statereg & ~0x80);\n\t\t\treturn;\n\t\tcase 0x09: /* 0xc009: SETALTZP (alt ZP/LC) */\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x80);\n\t\t\treturn;\n\t\tcase 0x0a: /* 0xc00a = SETINTC3ROM, use internal ROM slot 3*/\n\t\t\ttmp = 1 << 3;\n\t\t\tif((g_c02d_int_crom & tmp) != 0) {\n\t\t\t\tg_c02d_int_crom &= ~tmp;\n\t\t\t\tfixup_intcx();\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x0b: /* 0xc00b = SETSLOTC3ROM, use slot rom slot 3 */\n\t\t\ttmp = 1 << 3;\n\t\t\tif((g_c02d_int_crom & tmp) == 0) {\n\t\t\t\tg_c02d_int_crom |= tmp;\n\t\t\t\tfixup_intcx();\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x0c: /* 0xc00c = CLR80VID, disable 80 col hardware */\n\t\t\tif(g_cur_a2_stat & ALL_STAT_VID80) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_VID80);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x0d: /* 0xc00d: SET80VID, enable 80 col hardware */\n\t\t\tif((g_cur_a2_stat & ALL_STAT_VID80) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_VID80);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x0e: /* 0xc00e: CLRALTCHAR */\n\t\t\tif(g_cur_a2_stat & ALL_STAT_ALTCHARSET) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_ALTCHARSET);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x0f: /* 0xc00f: SETALTCHAR */\n\t\t\tif((g_cur_a2_stat & ALL_STAT_ALTCHARSET) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_ALTCHARSET);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\t/* 0xc010 - 0xc01f */\n\t\tcase 0x10: case 0x11: case 0x12: case 0x13:\n\t\tcase 0x14: case 0x15: case 0x16: case 0x17:\n\t\tcase 0x18: case 0x19: case 0x1a: case 0x1b:\n\t\tcase 0x1c: case 0x1d: case 0x1e: case 0x1f:\n\t\t\tadb_access_c010();\n\t\t\treturn;\n\t\t/* 0xc020 - 0xc02f */\n\t\tcase 0x20: /* 0xc020 */\n\t\t\t/* WRITE CASSETTE?? */\n\t\t\treturn;\n\t\tcase 0x21: /* 0xc021 */\n\t\t\tnew_tmp = ((val >> 7) & 1) <<\n\t\t\t\t\t\t(31 - BIT_ALL_STAT_COLOR_C021);\n\t\t\tif((g_cur_a2_stat & ALL_STAT_COLOR_C021) != new_tmp) {\n\t\t\t\tg_cur_a2_stat ^= new_tmp;\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x22: /* 0xc022 */\n\t\t\t/* change text color */\n\t\t\ttmp = (g_cur_a2_stat >> BIT_ALL_STAT_BG_COLOR) & 0xff;\n\t\t\tif(val != tmp) {\n\t\t\t\t/* change text/bg color! */\n\t\t\t\tg_cur_a2_stat &= ~(ALL_STAT_TEXT_COLOR |\n\t\t\t\t\t\t\tALL_STAT_BG_COLOR);\n\t\t\t\tg_cur_a2_stat += (val << BIT_ALL_STAT_BG_COLOR);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x23: /* 0xc023 */\n\t\t\tif((val & 0x19) != 0) {\n\t\t\t\thalt_printf(\"c023 write of %02x!!!\\n\", val);\n\t\t\t}\n\t\t\ttmp = (g_c023_val & 0x70) | (val & 0x0f);\n\t\t\tif((tmp & 0x22) == 0x22) {\n\t\t\t\tadd_irq(IRQ_PENDING_C023_SCAN);\n\t\t\t}\n\t\t\tif(!(tmp & 2)) {\n\t\t\t\tremove_irq(IRQ_PENDING_C023_SCAN);\n\t\t\t}\n\t\t\tif((tmp & 0x44) == 0x44) {\n\t\t\t\tadd_irq(IRQ_PENDING_C023_1SEC);\n\t\t\t}\n\t\t\tif(!(tmp & 0x4)) {\n\t\t\t\tremove_irq(IRQ_PENDING_C023_1SEC);\n\t\t\t}\n\n\t\t\tif(g_irq_pending & (IRQ_PENDING_C023_SCAN |\n\t\t\t\t\t\tIRQ_PENDING_C023_1SEC)) {\n\t\t\t\ttmp |= 0x80;\n\t\t\t}\n\t\t\tg_c023_val = tmp;\n\t\t\treturn;\n\t\tcase 0x24: /* 0xc024 */\n\t\t\t/* Write to mouse reg: Throw it away */\n\t\t\treturn;\n\t\tcase 0x26: /* 0xc026 */\n\t\t\tadb_write_c026(val);\n\t\t\treturn;\n\t\tcase 0x27: /* 0xc027 */\n\t\t\tadb_write_c027(val);\n\t\t\treturn;\n\t\tcase 0x29: /* 0xc029 */\n\t\t\tg_c029_val_some = val & 0x41;\n\t\t\tif((val & 1) == 0) {\n\t\t\t\thalt_printf(\"c029: %02x\\n\", val);\n\t\t\t}\n\t\t\tnew_tmp = val & 0xa0;\n\t\t\tif(new_tmp != (g_cur_a2_stat & 0xa0)) {\n\t\t\t\tg_cur_a2_stat = (g_cur_a2_stat & (~0xa0)) +\n\t\t\t\t\tnew_tmp;\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x2a: /* 0xc02a */\n#if 0\n\t\t\tprintf(\"Writing c02a with %02x\\n\", val);\n#endif\n\t\t\treturn;\n\t\tcase 0x2b: /* 0xc02b */\n\t\t\tg_c02b_val = val;\n\t\t\tif((val != 0x08) && (val != 0x00)) {\n\t\t\t\tprintf(\"Writing c02b with %02x\\n\", val);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x2d: /* 0xc02d = Slot Reg. Bit set means use slot rom */\n\t\t\tif((val & 0x9) != 0) {\n\t\t\t\thalt_printf(\"Illegal c02d write: %02x!\\n\", val);\n\t\t\t}\n\t\t\tfixup = (val != g_c02d_int_crom);\n\t\t\tg_c02d_int_crom = val;\n\t\t\tif(fixup) {\n\t\t\t\tvid_printf(\"Write c02d of %02x\\n\", val);\n\t\t\t\tfixup_intcx();\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x28: /* 0xc028 */\n\t\tcase 0x2c: /* 0xc02c */\n\t\t\tUNIMPL_WRITE;\n\t\tcase 0x25: /* 0xc025 */\n\t\t\t/* Space Shark writes to c025--ignore */\n\t\tcase 0x2e: /* 0xc02e */\n\t\tcase 0x2f: /* 0xc02f */\n\t\t\t/* Modulae writes to this--just ignore them */\n\t\t\treturn;\n\t\t\tbreak;\n\n\t\t/* 0xc030 - 0xc03f */\n\t\tcase 0x30: /* 0xc030 */\n#if 0\n\t\t\tprintf(\"Write speaker?\\n\");\n#endif\n\t\t\tsound_write_c030(dfcyc);\n\t\t\treturn;\n\t\tcase 0x31: /* 0xc031 */\n\t\t\ttmp = val ^ g_iwm.state;\n\t\t\tiwm_flush_cur_disk();\t// In case APPLE35SEL changes\n\t\t\tg_iwm.state = (g_iwm.state & (~0xc0)) | (val & 0xc0);\n\t\t\tif(tmp & IWM_STATE_C031_APPLE35SEL) {\n\t\t\t\t/* apple35_sel changed, maybe speed change */\n\t\t\t\tengine_recalc_events();\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x32: /* 0xc032 */\n\t\t\ttmp = g_c023_val & 0x7f;\n\t\t\tif(((val & 0x40) == 0) && (tmp & 0x40)) {\n\t\t\t\t/* clear 1 sec int */\n\t\t\t\tremove_irq(IRQ_PENDING_C023_1SEC);\n\t\t\t\ttmp &= 0xbf;\n\t\t\t\tg_c023_val = tmp;\n\t\t\t}\n\t\t\tif(((val & 0x20) == 0) && (tmp & 0x20)) {\n\t\t\t\t/* clear scan line int */\n\t\t\t\tremove_irq(IRQ_PENDING_C023_SCAN);\n\t\t\t\tg_c023_val = tmp & 0xdf;\n\t\t\t\tcheck_for_new_scan_int(dfcyc);\n\t\t\t}\n\t\t\tif(g_irq_pending & (IRQ_PENDING_C023_1SEC |\n\t\t\t\t\t\tIRQ_PENDING_C023_SCAN)) {\n\t\t\t\tg_c023_val |= 0x80;\n\t\t\t}\n\t\t\tif((val & 0x9f) != 0x9f) {\n\t\t\t\tirq_printf(\"c032: wrote %02x!\\n\", val);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x33: /* 0xc033 = CLOCKDATA*/\n\t\t\tg_c033_data = val;\n\t\t\treturn;\n\t\tcase 0x34: /* 0xc034 = CLOCKCTL */\n\t\t\ttmp = val ^ g_c034_val;\n\t\t\tclock_write_c034(val);\n\t\t\tif(tmp & 0xf) {\n\t\t\t\tchange_border_color(dfcyc, val & 0xf);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x35: /* 0xc035 */\n\t\t\tupdate_shadow_reg(dfcyc, val);\n\t\t\treturn;\n\t\tcase 0x36: /* 0xc036 = CYAREG */\n\t\t\ttmp = val ^ g_c036_val_speed;\n\t\t\tg_c036_val_speed = (val & ~0x20);\t/* clr bit 5 */\n\t\t\tif(tmp & 0x80) {\n\t\t\t\t/* to recalculate times since speed changing */\n\t\t\t\tengine_recalc_events();\n\t\t\t}\n\t\t\tif(tmp & 0xf) {\n\t\t\t\t/* slot_motor_detect changed */\n\t\t\t\tengine_recalc_events();\n\t\t\t}\n\n\t\t\tif((val & 0x60) != 0) {\n\t\t\t\t/* for ROM 03, 0x40 is the power-on status */\n\t\t\t\t/*  and can be read/write */\n\t\t\t\tif(((val & 0x60) != 0x40) ||\n\t\t\t\t\t\t\t(g_rom_version < 3)) {\n\t\t\t\t\tg_c036_val_speed &= (~0x60);\n\t\t\t\t\thalt_printf(\"c036: %2x\\n\", val);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(tmp & 0x10) {\t/* shadow in all banks! */\n\t\t\t\tif(g_num_shadow_all_banks++ == 0) {\n\t\t\t\t\tprintf(\"Shadowing all banks...This \"\n\t\t\t\t\t\t\"must be the NFC Megademo\\n\");\n\t\t\t\t}\n\t\t\t\tfixup_shadow_all_banks();\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x37: /* 0xc037 */\n\t\t\t/* just ignore, probably someone writing c036 m=0 */\n\t\t\treturn;\n\t\tcase 0x38: /* 0xc038 */\n\t\t\tscc_write_reg(dfcyc, 1, val);\n\t\t\treturn;\n\t\tcase 0x39: /* 0xc039 */\n\t\t\tscc_write_reg(dfcyc, 0, val);\n\t\t\treturn;\n\t\tcase 0x3a: /* 0xc03a */\n\t\t\tscc_write_data(dfcyc, 1, val);\n\t\t\treturn;\n\t\tcase 0x3b: /* 0xc03b */\n\t\t\tscc_write_data(dfcyc, 0, val);\n\t\t\treturn;\n\t\tcase 0x3c: /* 0xc03c */\n\t\t\t/* doc ctl */\n\t\t\tdoc_write_c03c(dfcyc, val);\n\t\t\treturn;\n\t\tcase 0x3d: /* 0xc03d */\n\t\t\t/* doc data reg */\n\t\t\tdoc_write_c03d(dfcyc, val);\n\t\t\treturn;\n\t\tcase 0x3e: /* 0xc03e */\n\t\t\tg_c03ef_doc_ptr = (g_c03ef_doc_ptr & 0xff00) + val;\n\t\t\treturn;\n\t\tcase 0x3f: /* 0xc03f */\n\t\t\tg_c03ef_doc_ptr = (g_c03ef_doc_ptr & 0xff) + (val << 8);\n\t\t\treturn;\n\n\t\t/* 0xc040 - 0xc04f */\n\t\tcase 0x41: /* c041 */\n\t\t\tg_c041_val = val & 0x1f;\n\t\t\tif((val & 0xe7) != 0) {\n\t\t\t\thalt_printf(\"write c041: %02x\\n\", val);\n\t\t\t}\n\n\t\t\tif(!(val & C041_EN_VBL_INTS)) {\n\t\t\t\t/* no more vbl interrupt */\n\t\t\t\tremove_irq(IRQ_PENDING_C046_VBL);\n\t\t\t}\n\t\t\tif(!(val & C041_EN_25SEC_INTS)) {\n\t\t\t\tremove_irq(IRQ_PENDING_C046_25SEC);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x46: /* c046 */\n\t\t\t/* ignore writes to c046 */\n\t\t\treturn;\n\t\tcase 0x47: /* c047 */\n\t\t\tremove_irq(IRQ_PENDING_C046_VBL |\n\t\t\t\t\t\tIRQ_PENDING_C046_25SEC);\n\t\t\tg_c046_val &= 0xe7;\t/* clear vblint, 1/4sec int*/\n\t\t\treturn;\n\t\tcase 0x48: /* c048 */\n\t\t\t/* diversitune writes this--ignore it */\n\t\t\treturn;\n\t\tcase 0x42: /* c042 */\n\t\tcase 0x43: /* c043 */\n\t\t\treturn;\n\t\tcase 0x4f: /* c04f */\n\t\t\tg_em_emubyte_cnt = 1;\n\t\t\treturn;\n\t\tcase 0x45: /* c045 */\n\t\t\treturn;\n\t\tcase 0x40: /* c040 */\n\t\tcase 0x44: /* c044 */\n\t\tcase 0x49: /* c049 */\n\t\tcase 0x4a: /* c04a */\n\t\tcase 0x4b: /* c04b */\n\t\tcase 0x4c: /* c04c */\n\t\tcase 0x4d: /* c04d */\n\t\tcase 0x4e: /* c04e */\n\t\t\tUNIMPL_WRITE;\n\n\t\t/* 0xc050 - 0xc05f */\n\t\tcase 0x50: /* 0xc050 */\n\t\t\tif(g_cur_a2_stat & ALL_STAT_TEXT) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_TEXT);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x51: /* 0xc051 */\n\t\t\tif((g_cur_a2_stat & ALL_STAT_TEXT) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_TEXT);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x52: /* 0xc052 */\n\t\t\tif(g_cur_a2_stat & ALL_STAT_MIX_T_GR) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_MIX_T_GR);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x53: /* 0xc053 */\n\t\t\tif((g_cur_a2_stat & ALL_STAT_MIX_T_GR) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_MIX_T_GR);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x54: /* 0xc054 */\n\t\t\tset_statereg(dfcyc, g_c068_statereg & (~0x40));\n\t\t\treturn;\n\t\tcase 0x55: /* 0xc055 */\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x40);\n\t\t\treturn;\n\t\tcase 0x56: /* 0xc056 */\n\t\t\tif(g_cur_a2_stat & ALL_STAT_HIRES) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_HIRES);\n\t\t\t\tfixup_hires_on();\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x57: /* 0xc057 */\n\t\t\tif((g_cur_a2_stat & ALL_STAT_HIRES) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_HIRES);\n\t\t\t\tfixup_hires_on();\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x58: /* 0xc058 */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\tg_zipgs_reg_c059 &= 0x4;  /* last reset cold */\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs &= (~1);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x59: /* 0xc059 */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\tg_zipgs_reg_c059 = (val & 0xf8) |\n\t\t\t\t\t\t(g_zipgs_reg_c059 & 0x7);\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs |= 1;\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x5a: /* 0xc05a */\n\t\t\tg_c05x_annuncs &= (~2);\n\t\t\tif((val & 0xf0) == 0x50) {\n\t\t\t\tg_zipgs_unlock++;\n\t\t\t} else if((val & 0xf0) == 0xa0) {\n\t\t\t\tg_zipgs_unlock = 0;\n\t\t\t} else if(g_zipgs_unlock >= 4) {\n\t\t\t\tif((g_zipgs_reg_c05b & 0x10) == 0) {\n\t\t\t\t\t/* to recalculate times */\n\t\t\t\t\tengine_recalc_events();\n\t\t\t\t}\n\t\t\t\tg_zipgs_reg_c05b |= 0x10;\t// disable\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x5b: /* 0xc05b */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\tif((g_zipgs_reg_c05b & 0x10) != 0) {\n\t\t\t\t\t/* to recalculate times */\n\t\t\t\t\tengine_recalc_events();\n\t\t\t\t}\n\t\t\t\tg_zipgs_reg_c05b &= (~0x10);\t// enable\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs |= 2;\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x5c: /* 0xc05c */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\tg_zipgs_reg_c05c = val;\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs &= (~4);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x5d: /* 0xc05d */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\tif(((g_zipgs_reg_c05a ^ val) >= 0x10) &&\n\t\t\t\t\t((g_zipgs_reg_c05b & 0x10) == 0)) {\n\t\t\t\t\tengine_recalc_events();\n\t\t\t\t}\n\t\t\t\tg_zipgs_reg_c05a = val | 0xf;\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs |= 4;\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x5e: /* 0xc05e: SETAN3, clear AN3, double-hires on */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\t/* Zippy writes 0x80 and 0x00 here... */\n\t\t\t} else if(g_cur_a2_stat & ALL_STAT_ANNUNC3) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_ANNUNC3);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x5f: /* 0xc05f: CLRAN3, set AN3, double-hires off */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\thalt_printf(\"Wrote ZipGS $c05f: %02x\\n\", val);\n\t\t\t} else if((g_cur_a2_stat & ALL_STAT_ANNUNC3) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_ANNUNC3);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\n\n\t\t/* 0xc060 - 0xc06f */\n\t\tcase 0x60: /* 0xc060 */\n\t\tcase 0x61: /* 0xc061 */\n\t\tcase 0x62: /* 0xc062 */\n\t\tcase 0x63: /* 0xc063 */\n\t\tcase 0x64: /* 0xc064 */\n\t\tcase 0x65: /* 0xc065 */\n\t\tcase 0x66: /* 0xc066 */\n\t\tcase 0x67: /* 0xc067 */\n\t\t\t/* all the above do nothing--return */\n\t\t\treturn;\n\t\tcase 0x68: /* 0xc068 = STATEREG */\n\t\t\tset_statereg(dfcyc, (g_c068_statereg & ~0xff) | val);\n\t\t\treturn;\n\t\tcase 0x69: /* 0xc069 */\n\t\t\t/* just ignore, someone writing c068 with m=0 */\n\t\t\treturn;\n\t\tcase 0x6a: /* 0xc06a */\n\t\tcase 0x6b: /* 0xc06b */\n\t\t\tUNIMPL_WRITE;\n\t\tcase 0x6c: /* 0xc06c */\n\t\t\tg_c06c_latched_cyc = (word32)(dfcyc >> 16);\n\t\t\treturn;\n\t\tcase 0x6d: /* 0xc06d */\n\t\t\t// Affect what reads to $C02C can see, only $40 now\n\t\t\tif((g_c06f_val & 0x100) == 0) {\t\t// Locked\n\t\t\t\tval = 0;\n\t\t\t}\n\t\t\tg_c06d_val = val;\t\t\t// Test mode\n\t\t\treturn;\n\t\tcase 0x6e: /* 0xc06e */\n\t\t\tg_c06f_val = 0;\n\t\t\treturn;\n\t\tcase 0x6f: /* 0xc06f */\n\t\t\tif((g_c06f_val == 0xda) && (val == 0x61)) {\n\t\t\t\tval |= 0x100;\t// Unlock\n\t\t\t}\n\t\t\tg_c06f_val = val;\n\t\t\treturn;\n\n\t\t/* 0xc070 - 0xc07f */\n\t\tcase 0x70: /* 0xc070 = Trigger paddles */\n\t\t\tpaddle_trigger(dfcyc);\n\t\t\treturn;\n\t\tcase 0x73: /* 0xc073 = multibank ram card bank addr? */\n\t\t\treturn;\n\t\tcase 0x71: /* 0xc071 = another multibank ram card enable? */\n\t\tcase 0x7e: /* 0xc07e */\n\t\tcase 0x7f: /* 0xc07f */\n\t\t\treturn;\n\t\tcase 0x72: /* 0xc072 */\n\t\tcase 0x74: /* 0xc074 */\n\t\tcase 0x75: /* 0xc075 */\n\t\tcase 0x76: /* 0xc076 */\n\t\tcase 0x77: /* 0xc077 */\n\t\tcase 0x78: /* 0xc078 */\n\t\tcase 0x79: /* 0xc079 */\n\t\tcase 0x7a: /* 0xc07a */\n\t\tcase 0x7b: /* 0xc07b */\n\t\tcase 0x7c: /* 0xc07c */\n\t\tcase 0x7d: /* 0xc07d */\n\t\t\treturn;\n\n\t\t/* 0xc080 - 0xc08f */\n\t\tcase 0x80: case 0x81: case 0x82: case 0x83:\n\t\tcase 0x84: case 0x85: case 0x86: case 0x87:\n\t\tcase 0x88: case 0x89: case 0x8a: case 0x8b:\n\t\tcase 0x8c: case 0x8d: case 0x8e: case 0x8f:\n\t\t\tnew_lcbank2 = ((loc >> 1) & 0x4) ^ 0x4;\n\t\t\tnew_rdrom = ((loc << 3) ^ (loc << 2)) & 8;\n\t\t\t\t// new_rdrom is set if loc0 ^ loc1 is set\n\t\t\t\t// 8=RDROM, 0=read from LC RAM\n\t\t\tnew_prewrite = 0;\t\t// Writes clear PREWRITE\n\t\t\tnew_wrdefram = g_c068_statereg & 0x200;\n\t\t\tif((loc & 1) == 0) {\n\t\t\t\tnew_wrdefram = 0;\t// Any even access clrs\n\t\t\t}\n\t\t\tset_statereg(dfcyc, (g_c068_statereg & ~(0x30c)) |\n\t\t\t\t\tnew_lcbank2 | new_rdrom |\n\t\t\t\t\tnew_prewrite | new_wrdefram);\n\t\t\treturn;\n\n\t\t/* 0xc090 - 0xc09f */\n\t\tcase 0x90: case 0x91: case 0x92: case 0x93:\n\t\tcase 0x94: case 0x95: case 0x96: case 0x97:\n\t\tcase 0x98: case 0x99: case 0x9a: case 0x9b:\n\t\tcase 0x9c: case 0x9d: case 0x9e: case 0x9f:\n\t\t\tUNIMPL_WRITE;\n\n\t\t/* 0xc0a0 - 0xc0af */\n\t\tcase 0xa0: case 0xa1: case 0xa3:\n\t\tcase 0xa4: case 0xa5: case 0xa6: case 0xa7:\n\t\tcase 0xa9: case 0xaa: case 0xab:\n\t\tcase 0xac: case 0xad: case 0xae: case 0xaf:\n\t\t\tUNIMPL_WRITE;\n\t\tcase 0xa2:\t/* Burger Times writes here on error */\n\t\tcase 0xa8:\n\t\t\t/* Kurzweil SMP writes to 0xc0a8, ignore it */\n\t\t\treturn;\n\n\t\t/* 0xc0b0 - 0xc0bf */\n\t\tcase 0xb0: case 0xb1: case 0xb2: case 0xb3:\n\t\tcase 0xb4: case 0xb5: case 0xb6: case 0xb7:\n\t\tcase 0xb8: case 0xb9: case 0xba: case 0xbb:\n\t\tcase 0xbc: case 0xbd: case 0xbe: case 0xbf:\n\t\t\tvoc_devsel_write(loc, val, dfcyc);\n\t\t\treturn;\n\n\t\t/* 0xc0c0 - 0xc0cf */\n\t\tcase 0xc0: case 0xc1: case 0xc2: case 0xc3:\n\t\t\t// Slot 4 has a Slinky RAM card\n\t\t\tslinky_devsel_write(dfcyc, loc, val);\n\t\t\treturn;\n\t\tcase 0xc4: case 0xc5: case 0xc6: case 0xc7:\n\t\tcase 0xc8: case 0xc9: case 0xca: case 0xcb:\n\t\tcase 0xcc: case 0xcd: case 0xce: case 0xcf:\n\t\t\tUNIMPL_WRITE;\n\n\t\t/* 0xc0d0 - 0xc0df */\n\t\tcase 0xd0: case 0xd1: case 0xd2: case 0xd3:\n\t\tcase 0xd4: case 0xd5: case 0xd6: case 0xd7:\n\t\tcase 0xd8: case 0xd9: case 0xda: case 0xdb:\n\t\tcase 0xdc: case 0xdd: case 0xde: case 0xdf:\n\t\t\tUNIMPL_WRITE;\n\n\t\t/* 0xc0e0 - 0xc0ef */\n\t\tcase 0xe0: case 0xe1: case 0xe2: case 0xe3:\n\t\tcase 0xe4: case 0xe5: case 0xe6: case 0xe7:\n\t\tcase 0xe8: case 0xe9: case 0xea: case 0xeb:\n\t\tcase 0xec: case 0xed: case 0xee: case 0xef:\n\t\t\twrite_iwm(loc, val, dfcyc);\n\t\t\treturn;\n\n\t\t/* 0xc0f0 - 0xc0ff */\n\t\tcase 0xf0: case 0xf1: case 0xf2: case 0xf3:\n\t\tcase 0xf4: case 0xf5: case 0xf6: case 0xf7:\n\t\tcase 0xf8: case 0xf9: case 0xfa: case 0xfb:\n\t\tcase 0xfc: case 0xfd: case 0xfe: case 0xff:\n\t\t\tUNIMPL_WRITE;\n\t\tdefault:\n\t\t\tprintf(\"Write loc: %x\\n\",loc);\n\t\t\texit(-300);\n\t\t}\n\t\tbreak;\n\tcase 1: case 2: case 5: case 6: case 7:\n\t\t/* c1000 - c7ff (but not c3xx,c4xx) */\n\t\tif((loc & 0xff) == 0) {\t\t// Frogger writes $ff to $Cx00\n\t\t\treturn;\n\t\t}\n\t\tUNIMPL_WRITE;\n\tcase 3:\n\t\tif(((g_c02d_int_crom & 8) == 0) &&\n\t\t\t\t\t((g_c068_statereg & 0x400) == 0)) {\n\t\t\t// SLOTC3ROM clear, INTC8rom clear: Set INTC8ROM\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x400);\n\t\t}\n\t\treturn;\n\tcase 4:\n\t\tif((g_c02d_int_crom & 0x10) && !(g_c068_statereg & 1)) {\n\t\t\t// Slot 4 is set to Your Card and not INTCX\n\t\t\tmockingboard_write(loc, val, dfcyc);\n\t\t\treturn;\n\t\t}\n\t\treturn;\n\tcase 8: case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0xe:\n\t\t// UNIMPL_WRITE;\n\t\treturn;\n\tcase 0xf:\n\t\tif((loc & 0xfff) == 0xfff) {\n\t\t\t/* cfff */\n\t\t\tif(g_c068_statereg & 0x400) {\n\t\t\t\tset_statereg(dfcyc, g_c068_statereg & (~0x400));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// UNIMPL_WRITE;\n\t\t// Wings of Fury writes to $cf00-$cfff when reading a 0x300\n\t\t//  sector where it wants to load 0x200 to 0xd000-0xd1ff\n\t\treturn;\n\t}\n\tprintf(\"Huh2? Write loc: %x\\n\", loc);\n\texit(-290);\n}\n\nword32\nslinky_devsel_read(dword64 dfcyc, word32 loc)\n{\n\tword32\tval;\n\n\tloc = loc & 0xf;\n\tval = 0;\n\tif(loc <= 2) {\n\t\tval = (g_slinky_addr >> (8*loc)) & 0xff;\n\t}\n\tif(loc == 3) {\n\t\tval = g_slinky_ram[g_slinky_addr & (SLINKY_RAM_SIZE - 1)];\n\t\tdbg_log_info(dfcyc, g_slinky_addr, val, 0xc0c3);\n\t\tg_slinky_addr++;\n\t}\n\treturn val;\n}\n\nvoid\nslinky_devsel_write(dword64 dfcyc, word32 loc, word32 val)\n{\n\tword32\tmask;\n\n\tloc = loc & 0xf;\n\tdbg_log_info(dfcyc, g_slinky_addr, val, 0xc0c0 + loc);\n\tif(loc <= 2) {\n\t\tmask = 0xff << (8 * loc);\n\t\tval = val * 0x010101;\n\t\tg_slinky_addr = (g_slinky_addr & (~mask)) | (val & mask);\n\t}\n\tif(loc == 3) {\n\t\tg_slinky_ram[g_slinky_addr & (SLINKY_RAM_SIZE - 1)] = val;\n\t\tg_slinky_addr++;\n\t}\n}\n\nword32\nc3xx_read(dword64 dfcyc, word32 loc)\n{\n\t// We may have been marked IO so that we could set INTC8ROM,\n\t//  but we still need to return ROM\n\tif(((g_c02d_int_crom & 8) == 0) && ((g_c068_statereg & 0x400) == 0)) {\n\t\t// SLOTC3ROM is not set:  Set INTC8ROM\n\t\tset_statereg(dfcyc, g_c068_statereg | 0x400);\n\t}\n\tif(((g_c02d_int_crom & 8) == 0) || (g_c068_statereg & 1) ||\n\t\t\t\t\t\t\t(g_rom_version == 0)) {\n\t\t// Access ROM for slot 3\n\t\treturn g_rom_fc_ff_ptr[0x3c300 + (loc & 0xff)];\n\t}\n\treturn float_bus(dfcyc);\n}\n\n// IIgs vertical line counters\n// 0x7d - 0x7f: in vbl, top of screen\n// 0x80 - 0xdf: not in vbl, drawing screen\n// 0xe0 - 0xff: in vbl, bottom of screen\n// Note: lines are then 0-0x60 effectively, for 192 lines, from 0x80-0xdf\n// vertical blanking engages on line 192, even if in super hires mode\n// (Last 8 lines in SHR are drawn with vbl_active set\n\nword32\nget_lines_since_vbl(dword64 dfcyc)\n{\n\tdouble\tdusecs_since_last_vbl, dlines_since_vbl, dcyc_line_start;\n\tword32\tlines_since_vbl;\n\tint\toffset;\n\n\tdusecs_since_last_vbl = (double)((dfcyc >> 16) -\n\t\t\t\t\t\t(g_last_vbl_dfcyc >> 16));\n\n\tdlines_since_vbl = dusecs_since_last_vbl * (1.0 / 65.0);\n\tlines_since_vbl = (int)dlines_since_vbl;\n\tdcyc_line_start = (double)lines_since_vbl * 65.0;\n\n\toffset = ((int)(dusecs_since_last_vbl - dcyc_line_start)) & 0xff;\n\n\tlines_since_vbl = (lines_since_vbl << 8) + offset;\n\n\tif(!g_halt_sim && !g_config_control_panel) {\n\t\tdbg_log_info(dfcyc, (word32)dusecs_since_last_vbl,\n\t\t\t\t\t\tlines_since_vbl, 0xc02e);\n\t}\n\tif(lines_since_vbl < 0x10541) {\n\t\treturn lines_since_vbl;\n\t} else {\n\t\t// We've entered the next frame, but update_60hz() hasn't been\n\t\t//  called yet.  Produce the proper c02e/c02f counter values\n\t\t//  for the first line of the display\n\t\tlines_since_vbl = lines_since_vbl - 0x10600;\n#if 0\n\t\tprintf(\"lines_since_vbl was:%08x, now:%08x\\n\",\n\t\t\t\tlines_since_vbl + 0x10600, lines_since_vbl);\n\t\thalt_printf(\"lines_since_vbl: %08x!\\n\", lines_since_vbl);\n\t\tprintf(\"du_s_l_v: %f, dfcyc: %016llx, last_vbl_cycs: %016llx\\n\",\n\t\t\tdusecs_since_last_vbl, dfcyc, g_last_vbl_dfcyc);\n\t\tshow_dtime_array();\n\t\tshow_all_events();\n\t\t/* U_STACK_TRACE(); */\n#endif\n\t}\n\n\treturn lines_since_vbl;\n}\n\nint\nin_vblank(dword64 dfcyc)\n{\n\tword32\tlines_since_vbl;\n\n\tlines_since_vbl = get_lines_since_vbl(dfcyc);\n\n\t// Testing indicates $c019 is a cycle delayed from $C02F counter\n\tif(lines_since_vbl > 0xc000) {\t// Exclude line 192 first cycle!\n\t\treturn 1;\t\t// 1=in VBL\n\t}\n\tif(lines_since_vbl == 0) {\n\t\treturn 1;\t\t// Handle 1-cycle delay in reading c019\n\t}\n\n\treturn 0;\n}\n\n// horizontal video counter goes from 0x00,0x40 - 0x7f, then 0x80,0xc0-0xff\n// over 2*65 cycles.  The last visible screen pos is 0x7f and 0xff\n// KEGS starts it's \"line 0\" at the start of the right border for line -1\n// See get_lines_since_vbl comment for the format of the vertical counter\nint\nread_vid_counters(int loc, dword64 dfcyc)\n{\n\tword32\tmask;\n\tint\tlines_since_vbl;\n\n\tloc = loc & 0xf;\n\n\tlines_since_vbl = get_lines_since_vbl(dfcyc);\n\n\tlines_since_vbl += 0x10000;\n\tif(lines_since_vbl >= 0x20000) {\n\t\tlines_since_vbl = lines_since_vbl - 0x20000 + 0xfa00;\n\t}\n\n\tif(lines_since_vbl > 0x1ffff) {\n\t\thalt_printf(\"lines_since_vbl: %04x, dfcyc: %016llx, last_vbl:\"\n\t\t\t\"%016llx\\n\", lines_since_vbl, dfcyc, g_last_vbl_dfcyc);\n\t}\n\n\tif(loc == 0xe) {\t\t// c02e:  Vertical count\n\t\treturn (lines_since_vbl >> 9) & 0xff;\n\t}\n\n\tmask = (lines_since_vbl >> 1) & 0x80;\n\n\tlines_since_vbl = (lines_since_vbl & 0xff);\n\tif(lines_since_vbl >= 0x01) {\n\t\tlines_since_vbl = (lines_since_vbl + 0x3f) & 0x7f;\n\t}\n\treturn (mask | (lines_since_vbl & 0xff));\n}\n\n"
  },
  {
    "path": "gsplus/src/op_routs.h",
    "content": "// $KmKId: op_routs.h,v 1.47 2023-11-05 16:21:51+00 kentd Exp $\n\n/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2021 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n\n#define GET_DLOC_X_IND_WR()\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\\\n\t}\t\t\t\t\t\\\n\targ = arg + xreg + direct;\t\t\\\n\tGET_MEMORY_DIRECT_PAGE16(arg & 0xffff, arg, 1);\t\\\n\targ = (dbank << 16) + arg;\n\n\n#define GET_DLOC_X_IND_ADDR()\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DLOC_X_IND_WR()\n\n#define GET_DISP8_S_WR()\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\\\n\targ = (arg + stack) & 0xffff;\t\\\n\tINC_KPC_2;\n\n\n#define GET_DISP8_S_ADDR()\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DISP8_S_WR()\n\n#define GET_DLOC_WR()\t\t\t\\\n\targ = (arg + direct) & 0xffff;\t\\\n\tif(direct & 0xff) {\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\\\n\t}\t\t\t\t\\\n\tINC_KPC_2;\n\n#define GET_DLOC_ADDR()\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DLOC_WR()\n\n#define GET_DLOC_L_IND_WR()\t\t\\\n\targ = (arg + direct) & 0xffff;\t\\\n\tif(direct & 0xff) {\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\\\n\t}\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\\\n\tGET_MEMORY24(arg, arg, 1);\n\n#define GET_DLOC_L_IND_ADDR()\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DLOC_L_IND_WR()\n\n\n#define GET_DLOC_IND_WR()\t\t\\\n\tINC_KPC_2;\t\t\t\\\n\tif(direct & 0xff) {\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\\\n\t}\t\t\t\t\\\n\tGET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg, 0);\t\\\n\targ = (dbank << 16) + arg;\n\n\n#define GET_DLOC_IND_ADDR()\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DLOC_IND_WR();\n\n#define GET_DLOC_INDEX_WR(index_reg)\t\\\n\tCYCLES_PLUS_1;\t\t\t\\\n\targ = (arg & 0xff) + index_reg;\t\\\n\tINC_KPC_2;\t\t\t\\\n\tif(direct & 0xff) {\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\\\n\t}\t\t\t\t\\\n\tif((psr & 0x100) && ((direct & 0xff) == 0)) {\t\\\n\t\targ = (arg & 0xff);\t\\\n\t}\t\t\t\t\\\n\targ = (arg + direct) & 0xffff;\n\n#define GET_DLOC_X_WR()\t\\\n\tGET_DLOC_INDEX_WR(xreg)\n#define GET_DLOC_Y_WR()\t\\\n\tGET_DLOC_INDEX_WR(yreg)\n\n#define GET_DLOC_X_ADDR()\t\\\n\tGET_1BYTE_ARG;\t\t\\\n\tGET_DLOC_INDEX_WR(xreg)\n\n#define GET_DLOC_Y_ADDR()\t\\\n\tGET_1BYTE_ARG;\t\t\\\n\tGET_DLOC_INDEX_WR(yreg)\n\n#define GET_DISP8_S_IND_Y_WR()\t\t\\\n\targ = (stack + arg) & 0xffff;\t\\\n\tGET_MEMORY16(arg,arg,1);\t\\\n\tCYCLES_PLUS_2;\t\t\t\\\n\targ += (dbank << 16);\t\t\\\n\tINC_KPC_2;\t\t\t\\\n\targ = (arg + yreg) & 0xffffff;\n\n#define GET_DISP8_S_IND_Y_ADDR()\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DISP8_S_IND_Y_WR()\n\n#define GET_DLOC_L_IND_Y_WR()\t\t\\\n\targ = (direct + arg) & 0xffff;\t\\\n\tif(direct & 0xff) {\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\\\n\t}\t\t\t\t\\\n\tGET_MEMORY24(arg,arg,1);\t\\\n\tINC_KPC_2;\t\t\t\\\n\targ = (arg + yreg) & 0xffffff;\n\n#define GET_DLOC_L_IND_Y_ADDR()\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DLOC_L_IND_Y_WR()\n\n\n#define GET_ABS_ADDR()\t\t\t\\\n\tGET_2BYTE_ARG;\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\\\n\targ = arg + (dbank << 16);\t\\\n\tINC_KPC_3;\n\n#define GET_LONG_ADDR()\t\t\\\n\tGET_3BYTE_ARG;\t\t\t\\\n\tCYCLES_PLUS_2;\t\t\t\\\n\tINC_KPC_4;\n\n#define GET_LONG_X_ADDR_FOR_WR()\t\t\\\n\tGET_3BYTE_ARG;\t\t\t\\\n\tINC_KPC_4;\t\t\t\\\n\targ = (arg + xreg) & 0xffffff;\t\\\n\tCYCLES_PLUS_2;\n\n"
  },
  {
    "path": "gsplus/src/paddles.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include \"defc.h\"\n\nextern int g_mouse_raw_x;\t/* from adb.c */\nextern int g_mouse_raw_y;\n\ndword64\tg_paddle_trig_dfcyc = 0;\nint\tg_swap_paddles = 0;\nint\tg_invert_paddles = 0;\nint\tg_joystick_scale_factor_x = 0x100;\nint\tg_joystick_scale_factor_y = 0x100;\nint\tg_joystick_trim_amount_x = 0;\nint\tg_joystick_trim_amount_y = 0;\n\nint\tg_joystick_type = 0;\t/* 0 = Keypad Joystick */\nint\tg_joystick_native_type1 = -1;\nint\tg_joystick_native_type2 = -1;\nint\tg_joystick_native_type = -1;\n\nextern int g_paddle_buttons;\n\nint\tg_paddle_val[4] = { 0, 0, 0, 0 };\n\t\t/* g_paddle_val[0]: Joystick X coord, [1]:Y coord */\n\ndword64\tg_paddle_dfcyc[4] = { 0, 0, 0, 0 };\n\t\t/* g_paddle_dfcyc are the dfcyc the paddle goes to 0 */\n\n\nvoid\npaddle_fixup_joystick_type()\n{\n\t/* If g_joystick_type points to an illegal value, change it */\n\tif(g_joystick_type == 2) {\n\t\tg_joystick_native_type = g_joystick_native_type1;\n\t\tif(g_joystick_native_type1 < 0) {\n\t\t\tg_joystick_type = 0;\n\t\t}\n\t}\n\tif(g_joystick_type == 3) {\n\t\tg_joystick_native_type = g_joystick_native_type2;\n\t\tif(g_joystick_native_type2 < 0) {\n\t\t\tg_joystick_type = 0;\n\t\t}\n\t}\n}\n\nvoid\npaddle_trigger(dword64 dfcyc)\n{\n\t/* Called by read/write to $c070 */\n\tg_paddle_trig_dfcyc = dfcyc;\n\n\t/* Determine what all the paddle values are right now */\n\tpaddle_fixup_joystick_type();\n\n\tswitch(g_joystick_type) {\n\tcase 0:\t\t/* Keypad Joystick */\n\t\tpaddle_trigger_keypad(dfcyc);\n\t\tbreak;\n\tcase 1:\t\t/* Mouse Joystick */\n\t\tpaddle_trigger_mouse(dfcyc);\n\t\tbreak;\n\tdefault:\n\t\tjoystick_update(dfcyc);\n\t}\n}\n\nvoid\npaddle_trigger_mouse(dword64 dfcyc)\n{\n\tint\tval_x, val_y;\n\tint\tmouse_x, mouse_y;\n\n\tval_x = 0;\n\n\tmouse_x = g_mouse_raw_x;\n\tmouse_y = g_mouse_raw_y;\n\t/* mous_phys_x is 0->560, convert that to -32768 to + 32767 cyc */\n\t/*  So subtract 280 then mult by 117 */\n\tval_x = (mouse_x - 280) * 117;\n\n\t/* mous_phys_y is 0->384, convert that to -32768 to + 32767 cyc */\n\t/*  so subtract 192 then mult by 180 to overscale it a bit */\n\tval_y = (mouse_y - 192) * 180;\n\n\tg_paddle_val[0] = val_x;\n\tg_paddle_val[1] = val_y;\n\tg_paddle_val[2] = 32767;\n\tg_paddle_val[3] = 32767;\n\tg_paddle_buttons |= 0xc;\n\tpaddle_update_trigger_dcycs(dfcyc);\n}\n\nvoid\npaddle_trigger_keypad(dword64 dfcyc)\n{\n\tint\tget_y, val_x, val_y;\n\n\tval_x = adb_get_keypad_xy(get_y=0);\n\tval_y = adb_get_keypad_xy(get_y=1);\n\t/* val_x and val_y are already scale -32768 to +32768 */\n\n\tg_paddle_val[0] = val_x;\n\tg_paddle_val[1] = val_y;\n\tg_paddle_val[2] = 32767;\n\tg_paddle_val[3] = 32767;\n\tg_paddle_buttons |= 0xc;\n\tpaddle_update_trigger_dcycs(dfcyc);\n}\n\nvoid\npaddle_update_trigger_dcycs(dword64 dfcyc)\n{\n\tdword64\ttrig_dfcyc;\n\tint\tval, paddle_num, scale, trim;\n\tint\ti;\n\n\tfor(i = 0; i < 4; i++) {\n\t\tpaddle_num = i;\n\t\tif(g_swap_paddles) {\n\t\t\tpaddle_num = i ^ 1;\n\t\t}\n\t\tval = g_paddle_val[paddle_num];\n\t\tif(g_invert_paddles) {\n\t\t\tval = -val;\n\t\t}\n\t\t/* convert -32768 to +32768 into 0->2816.0 cycles (the */\n\t\t/* paddle delay const) */\n\t\t/* First multiply by the scale factor to adjust range */\n\t\tif(paddle_num & 1) {\n\t\t\tscale = g_joystick_scale_factor_y;\n\t\t\ttrim = g_joystick_trim_amount_y;\n\t\t} else {\n\t\t\tscale = g_joystick_scale_factor_x;\n\t\t\ttrim = g_joystick_trim_amount_x;\n\t\t}\n#if 0\n\t\tif(i == 0) {\n\t\t\tprintf(\"val was %04x(%d) * scale %03x = %d\\n\",\n\t\t\t\tval, val, scale, (val*scale)>>16);\n\t\t}\n#endif\n\t\tval = (val * scale) >> 16;\n\t\t/* Val is now from -128 to + 128 since scale is */\n\t\t/*  256=1.0, 128 = 0.5 */\n\t\tval = val + 128 + trim;\n\t\tif(val >= 255) {\n\t\t\tval = 280;\t/* increase range */\n\t\t}\n\t\ttrig_dfcyc = dfcyc + (dword64)((val * (2816/255.0)) * 65536);\n\t\tg_paddle_dfcyc[i] = trig_dfcyc;\n\t\tif(i < 2) {\n\t\t\tdbg_log_info(dfcyc, (scale << 16) | (val & 0xffff),\n\t\t\t\t\t(trim << 16) | i, 0x70);\n\t\t}\n\t}\n}\n\nint\nread_paddles(dword64 dfcyc, int paddle)\n{\n\tdword64\ttrig_dfcyc;\n\n\ttrig_dfcyc = g_paddle_dfcyc[paddle & 3];\n\n\tif(dfcyc < trig_dfcyc) {\n\t\treturn 0x80;\n\t} else {\n\t\treturn 0x00;\n\t}\n}\n\nvoid\npaddle_update_buttons()\n{\n\tpaddle_fixup_joystick_type();\n\tjoystick_update_buttons();\n}\n"
  },
  {
    "path": "gsplus/src/protos.h",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2019 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#ifdef INCLUDE_RCSID_C\n#endif\n\n#include \"protos_base.h\"\n"
  },
  {
    "path": "gsplus/src/protos_base.h",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2024 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#ifdef INCLUDE_RCSID_C\n#endif\n\n#ifdef __GNUC__\nvoid halt_printf(const char *fmt, ...) __attribute__ ((\n\t\t\t\t\t\t__format__(printf, 1, 2)));\nvoid cfg_err_printf(const char *pre_str, const char *fmt, ...) __attribute__ ((\n\t\t\t\t\t\t__format__(printf, 2, 3)));\nvoid dynapro_error(Disk *dsk, const char *fmt, ...) __attribute__ ((\n\t\t\t\t\t\t__format__(printf, 2, 3)));\n#endif\n\n/* xdriver.c and macdriver.c and windriver.c */\nint win_nonblock_read_stdin(int fd, char *bufptr, int len);\n\n/* special scc_unixdriver.c prototypes */\nvoid scc_serial_unix_open(int port);\nvoid scc_serial_unix_close(int port);\nvoid scc_serial_unix_change_params(int port);\nvoid scc_serial_unix_fill_readbuf(dword64 dfcyc, int port, int space_left);\nvoid scc_serial_unix_empty_writebuf(int port);\n\n/* special scc_windriver.c prototypes */\nvoid scc_serial_win_open(int port);\nvoid scc_serial_win_close(int port);\nvoid scc_serial_win_change_params(int port);\nvoid scc_serial_win_fill_readbuf(dword64 dfcyc, int port, int space_left);\nvoid scc_serial_win_empty_writebuf(int port);\n\n/* special joystick_driver.c prototypes */\nvoid joystick_init(void);\nvoid joystick_update(dword64 dfcyc);\nvoid joystick_update_buttons(void);\n\n/* END_HDR */\n\n/* adb.c */\nint adb_get_hide_warp_info(Kimage *kimage_ptr, int *warpptr);\nint adb_get_copy_requested(void);\nvoid adb_nonmain_check(void);\nvoid adb_init(void);\nvoid adb_reset(void);\nvoid adb_log(word32 addr, int val);\nvoid show_adb_log(void);\nvoid adb_error(void);\nvoid adb_add_kbd_srq(void);\nvoid adb_clear_kbd_srq(void);\nvoid adb_add_data_int(void);\nvoid adb_add_mouse_int(void);\nvoid adb_clear_data_int(void);\nvoid adb_clear_mouse_int(void);\nvoid adb_send_bytes(int num_bytes, word32 val0, word32 val1, word32 val2);\nvoid adb_send_1byte(word32 val);\nvoid adb_response_packet(int num_bytes, word32 val);\nvoid adb_kbd_reg0_data(int a2code, int is_up);\nvoid adb_kbd_talk_reg0(void);\nvoid adb_set_config(word32 val0, word32 val1, word32 val2);\nvoid adb_set_new_mode(word32 val);\nint adb_read_c026(void);\nvoid adb_write_c026(int val);\nvoid do_adb_cmd(void);\nint adb_read_c027(void);\nvoid adb_write_c027(int val);\nint read_adb_ram(word32 addr);\nvoid write_adb_ram(word32 addr, int val);\nint adb_get_keypad_xy(int get_y);\nint adb_update_mouse(Kimage *kimage_ptr, int x, int y, int button_states, int buttons_valid);\nint mouse_read_c024(dword64 dfcyc);\nvoid mouse_compress_fifo(dword64 dfcyc);\nvoid adb_paste_update_state(void);\nint adb_paste_add_buf(word32 key);\nvoid adb_key_event(int a2code, int is_up);\nword32 adb_read_c000(void);\nword32 adb_access_c010(void);\nword32 adb_read_c025(void);\nint adb_is_cmd_key_down(void);\nint adb_is_option_key_down(void);\nvoid adb_increment_speed(void);\nvoid adb_update_c025_mask(Kimage *kimage_ptr, word32 new_c025_val, word32 mask);\nint adb_ascii_to_a2code(int unicode_c, int a2code, int *shift_down_ptr);\nvoid adb_physical_key_update(Kimage *kimage_ptr, int raw_a2code, word32 unicode_c, int is_up);\nvoid adb_maybe_virtual_key_update(int a2code, int is_up);\nvoid adb_virtual_key_update(int a2code, int is_up);\nvoid adb_kbd_repeat_off(void);\nvoid adb_mainwin_focus(int has_focus);\n\n\n\n/* engine_c.c */\nword32 get_memory8_io_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, dword64 dplus_x_m1);\nword32 get_memory16_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, Fplus *fplus_ptr, int in_bank);\nword32 get_memory24_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, Fplus *fplus_ptr, int in_bank);\nvoid set_memory8_io_stub(word32 addr, word32 val, byte *stat, dword64 *dcycs_ptr, dword64 dplus_x_m1);\nvoid set_memory16_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr, dword64 dplus_1, dword64 dplus_x_m1, int in_bank);\nvoid set_memory24_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr, Fplus *fplus_ptr, int in_bank);\nword32 get_memory_c(word32 addr);\nword32 get_memory16_c(word32 addr);\nword32 get_memory24_c(word32 addr);\nvoid set_memory_c(word32 addr, word32 val, int do_log);\nvoid set_memory16_c(word32 addr, word32 val, int do_log);\nvoid set_memory24_c(word32 addr, word32 val);\nword32 do_adc_sbc8(word32 in1, word32 in2, word32 psr, int sub);\nword32 do_adc_sbc16(word32 in1, word32 in2, word32 psr, int sub);\nvoid fixed_memory_ptrs_init(void);\nword32 get_itimer(void);\nvoid engine_recalc_events(void);\nvoid set_halt_act(int val);\nvoid clr_halt_act(void);\nword32 get_remaining_operands(word32 addr, word32 opcode, word32 psr, dword64 *dcyc_ptr, Fplus *fplus_ptr);\nint enter_engine(Engine_reg *engine_ptr);\n\n\n\n/* clock.c */\ndouble get_dtime(void);\nint micro_sleep(double dtime);\nvoid clk_bram_zero(void);\nvoid clk_bram_set(int bram_num, int offset, int val);\nvoid clk_setup_bram_version(void);\nvoid clk_write_bram(FILE *fconf);\nvoid update_cur_time(void);\nvoid clock_update(void);\nvoid clock_update_if_needed(void);\nvoid clock_write_c034(word32 val);\nvoid do_clock_data(void);\n\n\n\n/* compile_time.c */\n\n\n\n/* config.c */\nint config_add_argv_override(const char *str1, const char *str2);\nvoid config_set_config_kegs_name(const char *str1);\nvoid config_init_menus(Cfg_menu *menuptr);\nvoid config_init(void);\nvoid cfg_find_config_kegs_file(void);\nint config_setup_kegs_file(char *outname, int maxlen, const char **name_ptr);\nint config_expand_path(char *out_ptr, const char *in_ptr, int maxlen);\nchar *cfg_exit(int get_status);\nvoid cfg_err_vprintf(const char *pre_str, const char *fmt, va_list ap);\nvoid cfg_err_printf(const char *pre_str, const char *fmt, ...);\nvoid cfg_toggle_config_panel(void);\nvoid cfg_set_config_panel(int panel);\nchar *cfg_text_screen_dump(int get_status);\nchar *cfg_text_screen_str(void);\nchar *cfg_get_serial0_status(int get_status);\nchar *cfg_get_serial1_status(int get_status);\nchar *cfg_get_current_copy_selection(void);\nvoid config_vbl_update(int doit_3_persec);\nvoid cfg_file_update_rom(const char *str);\nvoid cfg_file_update_ptr(char **strptr, const char *str, int need_update);\nvoid cfg_int_update(int *iptr, int new_val);\nvoid cfg_load_charrom(void);\nvoid config_load_roms(void);\nvoid config_parse_config_kegs_file(void);\nvoid cfg_parse_one_line(char *buf, int line);\nvoid cfg_parse_bram(char *buf, int pos, int len);\nvoid cfg_parse_sxdx(char *buf, int pos, int len);\nvoid config_generate_config_kegs_name(char *outstr, int maxlen, Disk *dsk, int with_extras);\nchar *config_write_config_kegs_file(int get_status);\nvoid insert_disk(int slot, int drive, const char *name, int ejected, const char *partition_name, int part_num, word32 dynamic_blocks);\ndword64 cfg_detect_dc42(byte *bptr, dword64 dsize, dword64 exp_dsize, int slot);\ndword64 cfg_get_fd_size(int fd);\ndword64 cfg_read_from_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize);\ndword64 cfg_write_to_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize);\nint cfg_partition_maybe_add_dotdot(void);\nint cfg_partition_name_check(const byte *name_ptr, int name_len);\nint cfg_partition_read_block(int fd, void *buf, dword64 blk, int blk_size);\nint cfg_partition_find_by_name_or_num(Disk *dsk, const char *in_partnamestr, int part_num);\nint cfg_partition_make_list_from_name(const char *namestr);\nint cfg_partition_make_list(int fd);\nint cfg_maybe_insert_disk(int slot, int drive, const char *namestr);\nvoid cfg_insert_disk_dynapro(int slot, int drive, const char *name);\nint cfg_stat(char *path, struct stat *sb, int do_lstat);\nword32 cfg_get_le16(byte *bptr);\nword32 cfg_get_le32(byte *bptr);\ndword64 cfg_get_le64(byte *bptr);\nword32 cfg_get_be_word16(word16 *ptr);\nword32 cfg_get_be_word32(word32 *ptr);\nvoid cfg_set_le32(byte *bptr, word32 val);\nvoid config_file_to_pipe(Disk *dsk, const char *cmd_ptr, const char *name_ptr);\nvoid cfg_htab_vtab(int x, int y);\nvoid cfg_home(void);\nvoid cfg_cleol(void);\nvoid cfg_putchar(int c);\nvoid cfg_printf(const char *fmt, ...);\nvoid cfg_print_dnum(dword64 dnum, int max_len);\nint cfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras);\nint cfg_get_disk_locked(int type_ext);\nvoid cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change);\nvoid cfg_get_base_path(char *pathptr, const char *inptr, int go_up);\nchar *cfg_name_new_image(int get_status);\nvoid cfg_dup_existing_image(word32 slotdrive);\nvoid cfg_dup_image_selected(void);\nvoid cfg_validate_image(word32 slotdrive);\nvoid cfg_toggle_lock_disk(word32 slotdrive);\nint cfg_create_new_image_act(const char *str, int type, int size_blocks);\nvoid cfg_create_new_image(void);\nvoid cfg_file_init(void);\nvoid cfg_free_alldirents(Cfg_listhdr *listhdrptr);\nvoid cfg_file_add_dirent_unique(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, dword64 dsize, dword64 dimage_start, dword64 compr_dsize, int part_num);\nvoid cfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, dword64 dsize, dword64 dimage_start, dword64 compr_dsize, int part_num);\nint cfg_dirent_sortfn(const void *obj1, const void *obj2);\nint cfg_str_match(const char *str1, const char *str2, int len);\nint cfg_str_match_maybecase(const char *str1, const char *str2, int len, int ignorecase);\nint cfgcasecmp(const char *str1, const char *str2);\nint cfg_strlcat(char *dstptr, const char *srcptr, int dstsize);\nchar *cfg_strncpy(char *dstptr, const char *srcptr, int dstsize);\nconst char *cfg_str_basename(const char *str);\nchar *cfg_strncpy_dirname(char *dstptr, const char *srcptr, int dstsize);\nvoid cfg_file_readdir(const char *pathptr);\nchar *cfg_shorten_filename(const char *in_ptr, int maxlen);\nvoid cfg_fix_topent(Cfg_listhdr *listhdrptr);\nvoid cfg_file_draw(void);\nvoid cfg_partition_select_all(void);\nvoid cfg_partition_selected(void);\nvoid cfg_file_selected(void);\nvoid cfg_file_handle_key(int key);\nvoid cfg_draw_menu(void);\nvoid cfg_newdisk_pick_menu(word32 slotdrive);\nint cfg_control_panel_update(void);\nvoid cfg_edit_mode_key(int key);\nint cfg_control_panel_update1(void);\n\n\n\n/* debugger.c */\nvoid debugger_init(void);\nvoid check_breakpoints(word32 addr, dword64 dfcyc, word32 maybe_stack, word32 type);\nvoid debug_hit_bp(word32 addr, dword64 dfcyc, word32 maybe_stack, word32 type, int pos);\nint debugger_run_16ms(void);\nvoid dbg_log_info(dword64 dfcyc, word32 info1, word32 info2, word32 type);\nvoid debugger_update_list_kpc(void);\nvoid debugger_key_event(Kimage *kimage_ptr, int a2code, int is_up);\nvoid debugger_page_updown(int isup);\nvoid debugger_redraw_screen(Kimage *kimage_ptr);\nvoid debug_draw_debug_line(Kimage *kimage_ptr, int line, int vid_line);\nvoid debugger_help(void);\nvoid dbg_help_show_strs(int help_depth, const char *str, const char *help_str);\nconst char *debug_find_cmd_in_table(const char *line_ptr, Dbg_longcmd *longptr, int help_depth);\nvoid do_debug_cmd(const char *in_str);\nword32 dis_get_memory_ptr(word32 addr);\nvoid show_one_toolset(FILE *toolfile, int toolnum, word32 addr);\nvoid show_toolset_tables(word32 a2bank, word32 addr);\nword32 debug_getnum(const char **str_ptr);\nchar *debug_get_filename(const char **str_ptr);\nvoid debug_help(const char *str);\nvoid debug_bp(const char *str);\nvoid debug_bp_set(const char *str);\nvoid debug_bp_clear(const char *str);\nvoid debug_bp_clear_all(const char *str);\nvoid debug_bp_setclr(const char *str, int is_set_clear);\nvoid debug_soundfile(const char *cmd_str);\nvoid debug_logpc(const char *str);\nvoid debug_logpc_on(const char *str);\nvoid debug_logpc_off(const char *str);\nvoid debug_logpc_out_data(FILE *pcfile, Data_log *log_data_ptr, dword64 start_dcyc);\nData_log *debug_show_data_info(FILE *pcfile, Data_log *log_data_ptr, dword64 base_dcyc, dword64 dfcyc, dword64 start_dcyc, int *data_wrap_ptr, int *count_ptr);\nvoid debug_logpc_save(const char *cmd_str);\nvoid set_bp(word32 addr, word32 end_addr, word32 acc_type);\nvoid show_bp(void);\nvoid delete_bp(word32 addr, word32 end_addr);\nvoid debug_iwm(const char *str);\nvoid debug_iwm_check(const char *str);\nint do_blank(int mode, int old_mode);\nvoid do_go(void);\nvoid do_step(void);\nvoid xam_mem(int count);\nvoid show_hex_mem(word32 startbank, word32 start, word32 end, int count);\nvoid do_debug_list(void);\nvoid dis_do_memmove(void);\nvoid dis_do_pattern_search(void);\nvoid dis_do_compare(void);\nconst char *do_debug_unix(const char *str, int old_mode);\nvoid do_debug_load(void);\nchar *do_dis(word32 kpc, int accsize, int xsize, int op_provided, word32 instr, int *size_ptr);\nint debug_get_view_line(int back);\nint debug_add_output_line(char *in_str);\nvoid debug_add_output_string(char *in_str, int len);\nvoid debug_add_output_chars(char *str);\nint dbg_printf(const char *fmt, ...);\nint dbg_vprintf(const char *fmt, va_list args);\nvoid halt_printf(const char *fmt, ...);\nvoid halt2_printf(const char *fmt, ...);\n\n\n\n/* scc.c */\nvoid scc_init(void);\nvoid scc_reset(void);\nvoid scc_hard_reset_port(int port);\nvoid scc_reset_port(int port);\nvoid scc_regen_clocks(int port);\nvoid scc_port_close(int port);\nvoid scc_port_open(dword64 dfcyc, int port);\nint scc_is_port_closed(dword64 dfcyc, int port);\nchar *scc_get_serial_status(int get_status, int port);\nvoid scc_config_changed(int port, int cfg_changed, int remote_changed, int serial_dev_changed);\nvoid scc_update(dword64 dfcyc);\nvoid scc_try_to_empty_writebuf(dword64 dfcyc, int port);\nvoid scc_try_fill_readbuf(dword64 dfcyc, int port);\nvoid scc_do_event(dword64 dfcyc, int type);\nvoid show_scc_state(void);\nword32 scc_read_reg(dword64 dfcyc, int port);\nvoid scc_write_reg(dword64 dfcyc, int port, word32 val);\nword32 scc_read_data(dword64 dfcyc, int port);\nvoid scc_write_data(dword64 dfcyc, int port, word32 val);\nword32 scc_do_read_rr2b(void);\nvoid scc_maybe_br_event(dword64 dfcyc, int port);\nvoid scc_evaluate_ints(int port);\nvoid scc_maybe_rx_event(dword64 dfcyc, int port);\nvoid scc_maybe_rx_int(int port);\nvoid scc_clr_rx_int(int port);\nvoid scc_handle_tx_event(int port);\nvoid scc_maybe_tx_event(dword64 dfcyc, int port);\nvoid scc_clr_tx_int(int port);\nvoid scc_set_zerocnt_int(int port);\nvoid scc_clr_zerocnt_int(int port);\nvoid scc_add_to_readbuf(dword64 dfcyc, int port, word32 val);\nvoid scc_add_to_readbufv(dword64 dfcyc, int port, const char *fmt, ...);\nvoid scc_transmit(dword64 dfcyc, int port, word32 val);\nvoid scc_add_to_writebuf(dword64 dfcyc, int port, word32 val);\n\n\n\n/* scc_socket_driver.c */\nvoid scc_socket_open(dword64 dfcyc, int port, int cfg);\nvoid scc_socket_close(int port);\nvoid scc_socket_close_extended(dword64 dfcyc, int port, int allow_retry);\nvoid scc_socket_maybe_open(dword64 dfcyc, int port, int must);\nvoid scc_socket_open_incoming(dword64 dfcyc, int port);\nvoid scc_socket_open_outgoing(dword64 dfcyc, int port, const char *remote_ip_str, int remote_port);\nvoid scc_socket_make_nonblock(dword64 dfcyc, int port);\nvoid scc_accept_socket(dword64 dfcyc, int port);\nvoid scc_socket_telnet_reqs(dword64 dfcyc, int port);\nvoid scc_socket_fill_readbuf(dword64 dfcyc, int port, int space_left);\nvoid scc_socket_recvd_char(dword64 dfcyc, int port, int c);\nvoid scc_socket_empty_writebuf(dword64 dfcyc, int port);\nvoid scc_socket_modem_write(dword64 dfcyc, int port, int c);\nvoid scc_socket_do_cmd_str(dword64 dfcyc, int port);\nvoid scc_socket_send_modem_code(dword64 dfcyc, int port, int code);\nvoid scc_socket_modem_connect(dword64 dfcyc, int port);\nvoid scc_socket_modem_do_ring(dword64 dfcyc, int port);\nvoid scc_socket_do_answer(dword64 dfcyc, int port);\n\n\n\n/* scc_windriver.c */\n\n\n\n/* scc_unixdriver.c */\n\n\n\n/* iwm.c */\nvoid iwm_init_drive(Disk *dsk, int smartport, int drive, int disk_525);\nvoid iwm_init(void);\nvoid iwm_reset(void);\nvoid disk_set_num_tracks(Disk *dsk, int num_tracks);\nword32 iwm_get_default_track_bits(Disk *dsk, word32 qtr_trk);\nvoid draw_iwm_status(int line, char *buf);\nvoid iwm_flush_cur_disk(void);\nvoid iwm_flush_disk_to_unix(Disk *dsk);\nvoid iwm_vbl_update(void);\nvoid iwm_update_fast_disk_emul(int fast_disk_emul_en);\nvoid iwm_show_stats(int slot_drive);\nDisk *iwm_get_dsk(word32 state);\nDisk *iwm_touch_switches(int loc, dword64 dfcyc);\nvoid iwm_move_to_ftrack(Disk *dsk, word32 new_frac_track, int delta, dword64 dfcyc);\nvoid iwm_move_to_qtr_track(Disk *dsk, word32 qtr_track);\nvoid iwm525_update_phases(Disk *dsk, dword64 dfcyc);\nvoid iwm525_update_head(Disk *dsk, dword64 dfcyc);\nint iwm_read_status35(dword64 dfcyc);\nvoid iwm_do_action35(dword64 dfcyc);\nint read_iwm(int loc, dword64 dfcyc);\nvoid write_iwm(int loc, int val, dword64 dfcyc);\nint iwm_read_enable2(dword64 dfcyc);\nint iwm_read_enable2_handshake(dword64 dfcyc);\nvoid iwm_write_enable2(int val);\nword32 iwm_fastemul_start_write(Disk *dsk, dword64 dfcyc);\nword32 iwm_read_data_fast(Disk *dsk, dword64 dfcyc);\ndword64 iwm_return_rand_data(Disk *dsk, dword64 dfcyc);\ndword64 iwm_get_raw_bits(Disk *dsk, word32 bit_pos, int num_bits, dword64 *dsyncs_ptr);\nword32 iwm_calc_bit_diff(word32 first, word32 last, word32 track_bits);\nword32 iwm_calc_bit_sum(word32 bit_pos, int add_ival, word32 track_bits);\ndword64 iwm_calc_forced_sync(dword64 dval, int forced_bit);\nint iwm_calc_forced_sync_0s(dword64 sync_dval, int bits);\nword32 iwm_read_data(Disk *dsk, dword64 dfcyc);\nvoid iwm_write_data(Disk *dsk, word32 val, dword64 dfcyc);\nvoid iwm_start_write(Disk *dsk, word32 bit_pos, word32 val, int no_prior);\nint iwm_start_write_act(Disk *dsk, word32 bit_pos, int num, int no_prior, int delta);\nvoid iwm_write_data35(Disk *dsk, word32 val, dword64 dfcyc);\nvoid iwm_write_end(Disk *dsk, int write_through_now, dword64 dfcyc);\nvoid iwm_write_one_nib(Disk *dsk, int bits, dword64 dfcyc);\nvoid iwm_recalc_sync_from(Disk *dsk, word32 qtr_track, word32 bit_pos, dword64 dfcyc);\nvoid sector_to_partial_nib(byte *in, byte *nib_ptr);\nint disk_unnib_4x4(Disk *dsk);\nint iwm_denib_track525(Disk *dsk, word32 qtr_track, byte *outbuf);\nint iwm_denib_track35(Disk *dsk, word32 qtr_track, byte *outbuf);\nint iwm_track_to_unix(Disk *dsk, word32 qtr_track, byte *outbuf);\nvoid show_hex_data(byte *buf, int count);\nvoid iwm_check_nibblization(dword64 dfcyc);\nvoid disk_check_nibblization(Disk *dsk, byte *in_buf, int size, dword64 dfcyc);\nvoid disk_unix_to_nib(Disk *dsk, int qtr_track, dword64 dunix_pos, word32 unix_len, int len_bits, dword64 dfcyc);\nvoid iwm_nibblize_track_nib525(Disk *dsk, byte *track_buf, int qtr_track, word32 unix_len);\nvoid iwm_nibblize_track_525(Disk *dsk, byte *track_buf, int qtr_track, dword64 dfcyc);\nvoid iwm_nibblize_track_35(Disk *dsk, byte *track_buf, int qtr_track, word32 unix_len, dword64 dfcyc);\nvoid disk_4x4_nib_out(Disk *dsk, word32 val);\nvoid disk_nib_out(Disk *dsk, word32 val, int size);\nvoid disk_nib_end_track(Disk *dsk, dword64 dfcyc);\nword32 disk_nib_out_raw(Disk *dsk, word32 qtr_track, word32 val, int bits, word32 bit_pos, dword64 dfcyc);\nDisk *iwm_get_dsk_from_slot_drive(int slot, int drive);\nvoid iwm_toggle_lock(Disk *dsk);\nvoid iwm_eject_named_disk(int slot, int drive, const char *name, const char *partition_name);\nvoid iwm_eject_disk_by_num(int slot, int drive);\nvoid iwm_eject_disk(Disk *dsk);\nvoid iwm_show_track(int slot_drive, int track, dword64 dfcyc);\nvoid iwm_show_a_track(Disk *dsk, Trk *trk, dword64 dfcyc);\nvoid dummy1(word32 psr);\nvoid dummy2(word32 psr);\n\n\n\n/* joystick_driver.c */\nvoid joystick_callback_init(int native_type);\nvoid joystick_callback_update(word32 buttons, int paddle_x, int paddle_y);\n\n\n\n/* moremem.c */\nvoid fixup_brks(void);\nvoid fixup_hires_on(void);\nvoid fixup_bank0_2000_4000(void);\nvoid fixup_bank0_0400_0800(void);\nvoid fixup_any_bank_any_page(word32 start_page, int num_pages, byte *mem0rd, byte *mem0wr);\nvoid fixup_intcx(void);\nvoid fixup_st80col(dword64 dfcyc);\nvoid fixup_altzp(void);\nvoid fixup_page2(dword64 dfcyc);\nvoid fixup_ramrd(void);\nvoid fixup_ramwrt(void);\nvoid fixup_lc(void);\nvoid set_statereg(dword64 dfcyc, word32 val);\nvoid fixup_shadow_txt1(void);\nvoid fixup_shadow_txt2(void);\nvoid fixup_shadow_hires1(void);\nvoid fixup_shadow_hires2(void);\nvoid fixup_shadow_shr(void);\nvoid fixup_shadow_iolc(void);\nvoid update_shadow_reg(dword64 dfcyc, word32 val);\nvoid fixup_shadow_all_banks(void);\nvoid setup_pageinfo(void);\nvoid show_bankptrs_bank0rdwr(void);\nvoid show_bankptrs(int bnk);\nvoid show_addr(byte *ptr);\nword32 moremem_fix_vector_pull(word32 addr);\nword32 io_read(word32 loc, dword64 *cyc_ptr);\nvoid io_write(word32 loc, word32 val, dword64 *cyc_ptr);\nword32 slinky_devsel_read(dword64 dfcyc, word32 loc);\nvoid slinky_devsel_write(dword64 dfcyc, word32 loc, word32 val);\nword32 c3xx_read(dword64 dfcyc, word32 loc);\nword32 get_lines_since_vbl(dword64 dfcyc);\nint in_vblank(dword64 dfcyc);\nint read_vid_counters(int loc, dword64 dfcyc);\n\n\n\n/* paddles.c */\nvoid paddle_fixup_joystick_type(void);\nvoid paddle_trigger(dword64 dfcyc);\nvoid paddle_trigger_mouse(dword64 dfcyc);\nvoid paddle_trigger_keypad(dword64 dfcyc);\nvoid paddle_update_trigger_dcycs(dword64 dfcyc);\nint read_paddles(dword64 dfcyc, int paddle);\nvoid paddle_update_buttons(void);\n\n\n\n/* mockingboard.c */\nvoid mock_ay8913_reset(int pair_num, dword64 dfcyc);\nvoid mockingboard_reset(dword64 dfcyc);\nvoid mock_show_pair(int pair_num, dword64 dfcyc, const char *str);\nvoid mock_update_timers(int doit, dword64 dfcyc);\nvoid mockingboard_event(dword64 dfcyc);\nword32 mockingboard_read(word32 loc, dword64 dfcyc);\nvoid mockingboard_write(word32 loc, word32 val, dword64 dfcyc);\nword32 mock_6522_read(int pair_num, word32 loc, dword64 dfcyc);\nvoid mock_6522_write(int pair_num, word32 loc, word32 val, dword64 dfcyc);\nword32 mock_6522_new_ifr(dword64 dfcyc, int pair_num, word32 ifr, word32 ier);\nvoid mock_ay8913_reg_read(int pair_num);\nvoid mock_ay8913_reg_write(int pair_num, dword64 dfcyc);\nvoid mock_ay8913_control_update(int pair_num, word32 new_val, word32 prev_val, dword64 dfcyc);\nvoid mockingboard_show(int got_num, word32 disable_mask);\n\n\n\n/* sim65816.c */\nint sim_get_force_depth(void);\nint sim_get_use_shmem(void);\nvoid sim_set_use_shmem(int use_shmem);\nword32 toolbox_debug_4byte(word32 addr);\nvoid toolbox_debug_c(word32 xreg, word32 stack, dword64 *dcyc_ptr);\nvoid show_toolbox_log(void);\nword32 get_memory_io(word32 loc, dword64 *dcyc_ptr);\nvoid set_memory_io(word32 loc, int val, dword64 *dcyc_ptr);\nvoid show_regs_act(Engine_reg *eptr);\nvoid show_regs(void);\nvoid my_exit(int ret);\nvoid do_reset(void);\nbyte *memalloc_align(int size, int skip_amt, void **alloc_ptr);\nvoid memory_ptr_init(void);\nint parse_argv(int argc, char **argv, int slashes_to_find);\nint kegs_init(int mdepth, int screen_width, int screen_height, int no_scale_window);\nvoid load_roms_init_memory(void);\nvoid initialize_events(void);\nvoid check_for_one_event_type(int type, word32 mask);\nvoid add_event_entry(dword64 dfcyc, int type);\ndword64 remove_event_entry(int type, word32 mask);\nvoid add_event_stop(dword64 dfcyc);\nvoid add_event_doc(dword64 dfcyc, int osc);\nvoid add_event_scc(dword64 dfcyc, int type);\nvoid add_event_vbl(void);\nvoid add_event_vid_upd(int line);\nvoid add_event_mockingboard(dword64 dfcyc);\nvoid add_event_scan_int(dword64 dfcyc, int line);\ndword64 remove_event_doc(int osc);\ndword64 remove_event_scc(int type);\nvoid remove_event_mockingboard(void);\nvoid show_all_events(void);\nvoid show_pmhz(void);\nvoid setup_zip_speeds(void);\nint run_16ms(void);\nint run_a2_one_vbl(void);\nvoid add_irq(word32 irq_mask);\nvoid remove_irq(word32 irq_mask);\nvoid take_irq(void);\nvoid show_dtime_array(void);\nvoid update_60hz(dword64 dfcyc, double dtime_now);\nvoid do_vbl_int(void);\nvoid do_scan_int(dword64 dfcyc, int line);\nvoid check_scan_line_int(int cur_video_line);\nvoid check_for_new_scan_int(dword64 dfcyc);\nvoid scb_changed(dword64 dfcyc, word32 addr, word32 new_val, word32 old_val);\nvoid init_reg(void);\nvoid handle_action(word32 ret);\nvoid do_break(word32 ret);\nvoid do_cop(word32 ret);\nvoid do_wdm(word32 arg);\nvoid do_wai(void);\nvoid do_stp(void);\nvoid do_wdm_emulator_id(void);\nvoid size_fail(int val, word32 v1, word32 v2);\nint fatal_printf(const char *fmt, ...);\nint kegs_vprintf(const char *fmt, va_list ap);\ndword64 must_write(int fd, byte *bufptr, dword64 dsize);\nvoid clear_fatal_logs(void);\nchar *kegs_malloc_str(const char *in_str);\ndword64 kegs_lseek(int fd, dword64 offs, int whence);\n\n\n\n/* smartport.c */\nvoid smartport_error(void);\nvoid smartport_log(word32 start_addr, word32 cmd, word32 rts_addr, word32 cmd_list);\nvoid do_c70d(word32 arg0);\nvoid do_c70a(word32 arg0);\nint do_read_c7(int unit_num, word32 buf, word32 blk);\nint do_write_c7(int unit_num, word32 buf, word32 blk);\nint smartport_memory_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size);\nint do_format_c7(int unit_num);\nvoid do_c700(word32 ret);\n\n\n\n/* doc.c */\nvoid doc_init(void);\nvoid doc_reset(dword64 dfcyc);\nint doc_play(dword64 dfcyc, double last_dsamp, double dsamp_now, int num_samps, int snd_buf_init, int *outptr_start);\nvoid doc_handle_event(int osc, dword64 dfcyc);\nvoid doc_sound_end(int osc, int can_repeat, double eff_dsamps, double dsamps);\nvoid doc_add_sound_irq(int osc);\nvoid doc_remove_sound_irq(int osc, int must);\nvoid doc_start_sound2(int osc, dword64 dfcyc);\nvoid doc_start_sound(int osc, double eff_dsamps, double dsamps);\nvoid doc_wave_end_estimate2(int osc, dword64 dfcyc);\nvoid doc_wave_end_estimate(int osc, double eff_dsamps, double dsamps);\nvoid doc_remove_sound_event(int osc);\nvoid doc_write_ctl_reg(dword64 dfcyc, int osc, int val);\nvoid doc_recalc_sound_parms(dword64 dfcyc, int osc);\nint doc_read_c03c(void);\nint doc_read_c03d(dword64 dfcyc);\nvoid doc_write_c03c(dword64 dfcyc, word32 val);\nvoid doc_write_c03d(dword64 dfcyc, word32 val);\nvoid doc_show_ensoniq_state(void);\n\n\n\n/* sound.c */\nvoid sound_init(void);\nvoid sound_set_audio_rate(int rate);\nvoid sound_reset(dword64 dfcyc);\nvoid sound_shutdown(void);\nvoid sound_update(dword64 dfcyc);\nvoid sound_file_start(char *filename);\nvoid sound_file_open(void);\nvoid sound_file_close(void);\nvoid send_sound_to_file(word32 *wptr, int shm_pos, int num_samps, int real_samps);\nvoid show_c030_state(dword64 dfcyc);\nvoid show_c030_samps(dword64 dfcyc, int *outptr, int num);\nint sound_play_c030(dword64 dfcyc, dword64 dsamp, int *outptr_start, int num_samps);\nvoid sound_play(dword64 dfcyc);\nvoid sound_mock_envelope(int pair, int *env_ptr, int num_samps, int *vol_ptr);\nvoid sound_mock_noise(int pair, byte *noise_ptr, int num_samps);\nvoid sound_mock_play(int pair, int channel, int *outptr, int *env_ptr, byte *noise_ptr, int *vol_ptr, int num_samps);\nword32 sound_read_c030(dword64 dfcyc);\nvoid sound_write_c030(dword64 dfcyc);\n\n\n\n/* sound_driver.c */\nvoid snddrv_init(void);\nvoid sound_child_fork(int size);\nvoid parent_sound_get_sample_rate(int read_fd);\nvoid snddrv_shutdown(void);\nvoid snddrv_send_sound(int real_samps, int size);\nvoid child_sound_playit(word32 tmp);\nvoid reliable_buf_write(word32 *shm_addr, int pos, int size);\nvoid reliable_zero_write(int amt);\nint child_send_samples(byte *ptr, int size);\nvoid child_sound_loop(int read_fd, int write_fd, word32 *shm_addr);\n\n\n\n/* woz.c */\nvoid woz_crc_init(void);\nword32 woz_calc_crc32(byte *bptr, dword64 dlen, word32 bytes_to_skip);\nvoid woz_rewrite_crc(Disk *dsk, int min_write_size);\nvoid woz_rewrite_lock(Disk *dsk);\nvoid woz_check_file(Disk *dsk);\nvoid woz_parse_meta(Disk *dsk, int offset, int size);\nvoid woz_parse_info(Disk *dsk, int offset, int size);\nvoid woz_parse_tmap(Disk *dsk, int offset, int size);\nvoid woz_parse_trks(Disk *dsk, int offset, int size);\nint woz_add_track(Disk *dsk, int qtr_track, word32 tmap, dword64 dfcyc);\nint woz_parse_header(Disk *dsk);\nWoz_info *woz_malloc(byte *wozptr, word32 woz_size);\nint woz_reopen(Disk *dsk, dword64 dfcyc);\nint woz_open(Disk *dsk, dword64 dfcyc);\nbyte *woz_append_bytes(byte *wozptr, byte *in_bptr, int len);\nbyte *woz_append_word32(byte *wozptr, word32 val);\nint woz_append_chunk(Woz_info *wozinfo_ptr, word32 chunk_id, word32 length, byte *bptr);\nbyte *woz_append_a_trk(Woz_info *wozinfo_ptr, Disk *dsk, int trk_num, byte *bptr, word32 *num_blocks_ptr, dword64 *tmap_dptr);\nWoz_info *woz_new_from_woz(Disk *dsk, int disk_525);\nint woz_new(int fd, const char *str, int size_kb);\nvoid woz_maybe_reparse(Disk *dsk);\nvoid woz_set_reparse(Disk *dsk);\nvoid woz_reparse_woz(Disk *dsk);\nvoid woz_remove_a_track(Disk *dsk, word32 qtr_track);\nword32 woz_add_a_track(Disk *dsk, word32 qtr_track);\n\n\n\n/* unshk.c */\nword32 unshk_get_long4(byte *bptr);\nword32 unshk_get_word2(byte *bptr);\nword32 unshk_calc_crc(byte *bptr, int size, word32 start_crc);\nint unshk_unrle(byte *cptr, int len, word32 rle_delim, byte *ucptr);\nvoid unshk_lzw_clear(Lzw_state *lzw_ptr);\nbyte *unshk_unlzw(byte *cptr, Lzw_state *lzw_ptr, byte *ucptr, word32 uclen);\nvoid unshk_data(Disk *dsk, byte *cptr, word32 compr_size, byte *ucptr, word32 uncompr_size, word32 thread_format, byte *base_cptr);\nvoid unshk_parse_header(Disk *dsk, byte *cptr, int compr_size, byte *base_cptr);\nvoid unshk(Disk *dsk, const char *name_str);\nvoid unshk_dsk_raw_data(Disk *dsk);\n\n\n\n/* undeflate.c */\nvoid *undeflate_realloc(void *ptr, dword64 dsize);\nvoid *undeflate_malloc(dword64 dsize);\nvoid show_bits(unsigned *llptr, int nl);\nvoid show_huftb(unsigned *tabptr, int bits);\nvoid undeflate_init_len_dist_tab(word32 *tabptr, dword64 drepeats, word32 start);\nvoid undeflate_init_bit_rev_tab(word32 *tabptr, int num);\nword32 undeflate_bit_reverse(word32 val, word32 bits);\nword32 undeflate_calc_crc32(byte *bptr, word32 len);\nbyte *undeflate_ensure_dest_len(Disk *dsk, byte *ucptr, word32 len);\nvoid undeflate_add_tab_code(word32 *tabptr, word32 tabsz_lg2, word32 code, word32 entry);\nword32 *undeflate_init_fixed_tabs(void);\nword32 *undeflate_init_tables(void);\nvoid undeflate_free_tables(void);\nvoid undeflate_check_bit_reverse(void);\nword32 *undeflate_build_huff_tab(word32 *tabptr, word32 *entry_ptr, word32 len_size, word32 *bl_count_ptr, int max_bits);\nword32 *undeflate_dynamic_table(byte *cptr, word32 *bit_pos_ptr, byte *cptr_base);\nbyte *undeflate_block(Disk *dsk, byte *cptr, word32 *bit_pos_ptr, byte *cptr_base, byte *cptr_end);\nbyte *undeflate_gzip_header(Disk *dsk, byte *cptr, word32 compr_size);\nvoid undeflate_gzip(Disk *dsk, const char *name_str);\nbyte *undeflate_zipfile_blocks(Disk *dsk, byte *cptr, dword64 dcompr_size);\nint undeflate_zipfile(Disk *dsk, int fd, dword64 dlocal_header_off, dword64 uncompr_dsize, dword64 compr_dsize);\nint undeflate_zipfile_search(byte *bptr, byte *cmp_ptr, int size, int cmp_len, int min_size);\nint undeflate_zipfile_make_list(int fd);\n\n\n\n/* dynapro.c */\nword32 dynapro_get_word32(byte *bptr);\nword32 dynapro_get_word24(byte *bptr);\nword32 dynapro_get_word16(byte *bptr);\nvoid dynapro_set_word24(byte *bptr, word32 val);\nvoid dynapro_set_word32(byte *bptr, word32 val);\nvoid dynapro_set_word16(byte *bptr, word32 val);\nvoid dynapro_error(Disk *dsk, const char *fmt, ...);\nDynapro_file *dynapro_alloc_file(void);\nvoid dynapro_free_file(Dynapro_file *fileptr, int check_map);\nvoid dynapro_free_recursive_file(Dynapro_file *fileptr, int check_map);\nvoid dynapro_free_dynapro_info(Disk *dsk);\nword32 dynapro_find_free_block_internal(Disk *dsk);\nword32 dynapro_find_free_block(Disk *dsk);\nbyte *dynapro_malloc_file(char *path_ptr, dword64 *dsize_ptr, int extra_size);\nvoid dynapro_join_path_and_file(char *outstr, const char *unix_path, const char *str, int path_max);\nword32 dynapro_fill_fileptr_from_prodos(Disk *dsk, Dynapro_file *fileptr, char *buf32_ptr, word32 dir_byte);\nword32 dynapro_diff_fileptrs(Dynapro_file *oldfileptr, Dynapro_file *newfileptr);\nword32 dynapro_do_one_dir_entry(Disk *dsk, Dynapro_file *fileptr, Dynapro_file *localfile_ptr, char *buf32_ptr, word32 dir_byte);\nvoid dynapro_fix_damaged_entry(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_try_fix_damage(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_try_fix_damaged_disk(Disk *dsk);\nvoid dynapro_new_unix_path(Dynapro_file *fileptr, const char *path_str, const char *name_str);\nDynapro_file *dynapro_process_write_dir(Disk *dsk, Dynapro_file *parent_ptr, Dynapro_file **head_ptr_ptr, word32 dir_byte);\nvoid dynapro_handle_write_dir(Disk *dsk, Dynapro_file *parent_ptr, Dynapro_file *head_ptr, word32 dir_byte);\nword32 dynapro_process_write_file(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_handle_write_file(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_handle_changed_entry(Disk *dsk, Dynapro_file *fileptr);\nword32 dynapro_write_to_unix_file(const char *unix_path, byte *data_ptr, word32 size);\nvoid dynapro_unmap_file(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_unlink_file(Dynapro_file *fileptr);\nvoid dynapro_erase_free_entry(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_erase_free_dir(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_mark_damaged(Disk *dsk, Dynapro_file *fileptr);\nint dynapro_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size);\nvoid dynapro_debug_update(Disk *dsk);\nvoid dynapro_debug_map(Disk *dsk, const char *str);\nvoid dynapro_debug_recursive_file_map(Dynapro_file *fileptr, int start);\nword32 dynapro_unix_to_prodos_time(const time_t *time_ptr);\nint dynapro_create_prodos_name(Dynapro_file *newfileptr, Dynapro_file *matchptr, word32 storage_type);\nDynapro_file *dynapro_new_unix_file(const char *path, Dynapro_file *parent_ptr, Dynapro_file *match_ptr, word32 storage_type);\nint dynapro_create_dir(Disk *dsk, char *unix_path, Dynapro_file *parent_ptr, word32 dir_byte);\nword32 dynapro_add_file_entry(Disk *dsk, Dynapro_file *fileptr, Dynapro_file *head_ptr, word32 dir_byte, word32 inc);\nword32 dynapro_fork_from_unix(Disk *dsk, byte *fptr, word32 *storage_type_ptr, word32 key_block, dword64 dsize);\nword32 dynapro_file_from_unix(Disk *dsk, Dynapro_file *fileptr);\nword32 dynapro_prep_image(Disk *dsk, const char *dir_path, word32 num_blocks);\nword32 dynapro_map_one_file_block(Disk *dsk, Dynapro_file *fileptr, word32 block_num, word32 file_offset, word32 eof);\nword32 dynapro_map_file_blocks(Disk *dsk, Dynapro_file *fileptr, word32 block_num, int level, word32 file_offset, word32 eof);\nword32 dynapro_map_file(Disk *dsk, Dynapro_file *fileptr, int do_file_data);\nword32 dynapro_map_dir_blocks(Disk *dsk, Dynapro_file *fileptr);\nword32 dynapro_build_map(Disk *dsk, Dynapro_file *fileptr);\nint dynapro_mount(Disk *dsk, char *dir_path, word32 num_blocks);\n\n\n\n/* dyna_type.c */\nword32 dynatype_scan_extensions(const char *str);\nword32 dynatype_find_prodos_type(const char *str);\nconst char *dynatype_find_file_type(word32 file_type);\nword32 dynatype_detect_file_type(Dynapro_file *fileptr, const char *path_ptr, word32 storage_type);\nint dynatype_get_extension(const char *str, char *out_ptr, int buf_len);\nint dynatype_comma_arg(const char *str, word32 *type_or_aux_ptr);\nvoid dynatype_fix_unix_name(Dynapro_file *fileptr, char *outbuf_ptr, int path_max);\n\n\n\n/* dyna_filt.c */\n\n\n\n/* dyna_validate.c */\nword32 dynapro_validate_header(Disk *dsk, Dynapro_file *fileptr, word32 dir_byte, word32 parent_dir_byte);\nvoid dynapro_validate_init_freeblks(byte *freeblks_ptr, word32 num_blocks);\nword32 dynapro_validate_freeblk(Disk *dsk, byte *freeblks_ptr, word32 block);\nword32 dynapro_validate_file(Disk *dsk, byte *freeblks_ptr, word32 block_num, word32 eof, int level_first);\nword32 dynapro_validate_forked_file(Disk *dsk, byte *freeblks_ptr, word32 block_num, word32 eof);\nword32 dynapro_validate_dir(Disk *dsk, byte *freeblks_ptr, word32 dir_byte, word32 parent_dir_byte, word32 exp_blocks_used);\nint dynapro_validate_disk(Disk *dsk);\nvoid dynapro_validate_any_image(Disk *dsk);\n\n\n\n/* applesingle.c */\nword32 applesingle_get_be32(const byte *bptr);\nword32 applesingle_get_be16(const byte *bptr);\nvoid applesingle_set_be32(byte *bptr, word32 val);\nvoid applesingle_set_be16(byte *bptr, word32 val);\nword32 applesingle_map_from_prodos(Disk *dsk, Dynapro_file *fileptr, int do_file_data);\nword32 applesingle_from_unix(Disk *dsk, Dynapro_file *fileptr, byte *fptr, dword64 dsize);\nword32 applesingle_make_prodos_fork(Disk *dsk, byte *fptr, byte *tptr, word32 length);\n\n\n\n/* video.c */\nvoid video_set_red_mask(word32 red_mask);\nvoid video_set_green_mask(word32 green_mask);\nvoid video_set_blue_mask(word32 blue_mask);\nvoid video_set_alpha_mask(word32 alpha_mask);\nvoid video_set_mask_and_shift(word32 x_mask, word32 *mask_ptr, int *shift_left_ptr, int *shift_right_ptr);\nvoid video_set_palette(void);\nvoid video_set_redraw_skip_amt(int amt);\nKimage *video_get_kimage(int win_id);\nchar *video_get_status_ptr(int line);\nvoid video_set_x_refresh_needed(Kimage *kimage_ptr, int do_refresh);\nint video_get_active(Kimage *kimage_ptr);\nvoid video_set_active(Kimage *kimage_ptr, int active);\nvoid video_init(int mdepth, int screen_width, int screen_height, int no_scale_window);\nvoid video_init_kimage(Kimage *kimage_ptr, int width, int height, int screen_width, int screen_height);\nvoid show_a2_line_stuff(void);\nvoid video_reset(void);\nvoid video_update(void);\nword32 video_all_stat_to_filt_stat(int line, word32 new_all_stat);\nvoid change_display_mode(dword64 dfcyc);\nvoid video_add_new_all_stat(dword64 dfcyc, word32 lines_since_vbl);\nvoid change_border_color(dword64 dfcyc, int val);\nvoid update_border_info(void);\nvoid update_border_line(int st_line_offset, int end_line_offset, int color);\nvoid video_border_pixel_write(Kimage *kimage_ptr, int starty, int num_lines, int color, int st_off, int end_off);\nword32 video_get_ch_mask(word32 mem_ptr, word32 filt_stat, int reparse);\nvoid video_update_edges(int line, int left, int right, const char *str);\nvoid redraw_changed_text(word32 line_bytes, int reparse, word32 *in_wptr, int pixels_per_line, word32 filt_stat);\nvoid redraw_changed_string(const byte *bptr, word32 line_bytes, word32 ch_mask, word32 *in_wptr, word32 bg_pixel, word32 fg_pixel, int pixels_per_line, int dbl);\nvoid redraw_changed_gr(word32 line_bytes, int reparse, word32 *in_wptr, int pixels_per_line, word32 filt_stat);\nvoid video_hgr_line_segment(byte *slow_mem_ptr, word32 *wptr, int start_byte, int end_byte, int pixels_per_line, word32 filt_stat);\nvoid redraw_changed_hgr(word32 line_bytes, int reparse, word32 *in_wptr, int pixels_per_line, word32 filt_stat);\nint video_rebuild_super_hires_palette(int bank, word32 scan_info, int line, int reparse);\nword32 redraw_changed_super_hires_oneline(int bank, word32 *in_wptr, int pixels_per_line, int y, int scan, word32 ch_mask);\nvoid redraw_changed_super_hires_bank(int bank, int start_line, int reparse, word32 *wptr, int pixels_per_line);\nvoid redraw_changed_super_hires(word32 line_bytes, int reparse, word32 *wptr, int pixels_per_line, word32 filt_stat);\nvoid video_copy_changed2(void);\nvoid video_update_event_line(int line);\nvoid video_force_reparse(void);\nvoid video_update_through_line(int line);\nvoid video_do_partial_line(word32 lines_since_vbl, int end, word32 cur_all_stat);\nvoid video_refresh_line(word32 line_bytes, int must_reparse, word32 filt_stat);\nvoid prepare_a2_font(void);\nvoid prepare_a2_romx_font(byte *font_ptr);\nvoid video_add_rect(Kimage *kimage_ptr, int x, int y, int width, int height);\nvoid video_add_a2_rect(int start_line, int end_line, int left_pix, int right_pix);\nvoid video_form_change_rects(void);\nint video_get_a2_width(Kimage *kimage_ptr);\nint video_get_x_width(Kimage *kimage_ptr);\nint video_get_a2_height(Kimage *kimage_ptr);\nint video_get_x_height(Kimage *kimage_ptr);\nint video_get_x_xpos(Kimage *kimage_ptr);\nint video_get_x_ypos(Kimage *kimage_ptr);\nvoid video_update_xpos_ypos(Kimage *kimage_ptr, int x_xpos, int x_ypos);\nint video_change_aspect_needed(Kimage *kimage_ptr, int x_width, int x_height);\nvoid video_update_status_enable(Kimage *kimage_ptr);\nint video_out_query(Kimage *kimage_ptr);\nvoid video_out_done(Kimage *kimage_ptr);\nint video_out_data(void *vptr, Kimage *kimage_ptr, int out_width_act, Change_rect *rectptr, int pos);\nint video_out_data_intscaled(void *vptr, Kimage *kimage_ptr, int out_width_act, Change_rect *rectptr);\nint video_out_data_scaled(void *vptr, Kimage *kimage_ptr, int out_width_act, Change_rect *rectptr);\nword32 video_scale_calc_frac(int pos, word32 max, word32 frac_inc, word32 frac_inc_inv);\nvoid video_update_scale(Kimage *kimage_ptr, int out_width, int out_height, int must_update);\nint video_scale_mouse_x(Kimage *kimage_ptr, int raw_x, int x_width);\nint video_scale_mouse_y(Kimage *kimage_ptr, int raw_y, int y_height);\nint video_unscale_mouse_x(Kimage *kimage_ptr, int a2_x, int x_width);\nint video_unscale_mouse_y(Kimage *kimage_ptr, int a2_y, int y_height);\nvoid video_update_color_raw(int bank, int col_num, int a2_color);\nvoid video_update_status_line(int line, const char *string);\nvoid video_draw_a2_string(int line, const byte *bptr);\nvoid video_show_debug_info(void);\nword32 read_video_data(dword64 dfcyc);\nword32 float_bus(dword64 dfcyc);\nword32 float_bus_lines(dword64 dfcyc, word32 lines_since_vbl);\n\n\n\n/* voc.c */\nword32 voc_devsel_read(word32 loc, dword64 dfcyc);\nvoid voc_devsel_write(word32 loc, word32 val, dword64 dfcyc);\nvoid voc_iosel_c300_write(word32 loc, word32 val, dword64 dfcyc);\nvoid voc_reset(void);\nword32 voc_read_reg0(dword64 dfcyc);\nvoid voc_update_interlace(dword64 dfcyc);\n\n\n"
  },
  {
    "path": "gsplus/src/protos_macdriver.h",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2019 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n\n/* END_HDR */\n\n/* macdriver.c */\npascal OSStatus quit_event_handler(EventHandlerCallRef call_ref, EventRef event, void *ignore);\nvoid show_simple_alert(char *str1, char *str2, char *str3, int num);\nvoid x_dialog_create_kegs_conf(const char *str);\nint x_show_alert(int is_fatal, const char *str);\npascal OSStatus my_cmd_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata);\nvoid update_window(void);\nvoid show_event(UInt32 event_class, UInt32 event_kind, int handled);\npascal OSStatus my_win_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata);\npascal OSStatus dummy_event_handler(EventHandlerCallRef call_ref, EventRef in_event, void *ignore);\nvoid mac_update_modifiers(word32 state);\nvoid mac_warp_mouse(void);\nvoid check_input_events(void);\nvoid temp_run_application_event_loop(void);\nint main(int argc, char *argv[]);\nvoid x_update_color(int col_num, int red, int green, int blue, word32 rgb);\nvoid x_update_physical_colormap(void);\nvoid show_xcolor_array(void);\nvoid xdriver_end(void);\nvoid x_get_kimage(Kimage *kimage_ptr);\nvoid dev_video_init(void);\nvoid x_redraw_status_lines(void);\nvoid x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, int width, int height);\nvoid x_push_done(void);\nvoid x_auto_repeat_on(int must);\nvoid x_auto_repeat_off(int must);\nvoid x_hide_pointer(int do_hide);\nvoid x_full_screen(int do_full);\nvoid update_main_window_size(void);\n\n"
  },
  {
    "path": "gsplus/src/protos_macsnd_driver.h",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n\n/* END_HDR */\n\n/* macsnd_driver.c */\nint mac_send_audio(byte *ptr, int in_size);\nvoid macsnd_init(void);\n\n\n"
  },
  {
    "path": "gsplus/src/protos_pulseaudio_driver.h",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2020 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n\n/* END_HDR */\n\n/* pulseaudio_driver.c */\nint pulse_audio_send_audio(byte *ptr, int in_size);\nvoid pulse_audio_main_events(void);\nvoid pulse_audio_write_to_stream(int dbg_count, int in_sz);\nint pulse_audio_start_stream(void);\nint pulse_audio_do_init(void);\nint pulse_audio_init(void);\nvoid pulse_audio_shutdown(void);\n\n"
  },
  {
    "path": "gsplus/src/protos_windriver.h",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2022 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// $KmKId: protos_windriver.h,v 1.15 2023-05-17 22:37:57+00 kentd Exp $\n\n/* END_HDR */\n\n/* windriver.c */\nWindow_info *win_find_win_info_ptr(HWND hwnd);\nvoid win_hide_pointer(Window_info *win_info_ptr, int do_hide);\nint win_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y, int button_states, int buttons_valid);\nvoid win_event_mouse(HWND hwnd, WPARAM wParam, LPARAM lParam);\nvoid win_event_key(HWND hwnd, WPARAM wParam, LPARAM lParam, int down);\nvoid win_event_redraw(HWND hwnd);\nvoid win_event_destroy(HWND hwnd);\nvoid win_event_size(HWND hwnd, WPARAM wParam, LPARAM lParam);\nvoid win_event_minmaxinfo(HWND hwnd, LPARAM lParam);\nvoid win_event_focus(HWND hwnd, int gain_focus);\nLRESULT CALLBACK win_event_handler(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam);\nint main(int argc, char **argv);\nvoid check_input_events(void);\nvoid win_video_init(int mdepth);\nvoid win_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str, int mdepth);\nvoid win_create_window(Window_info *win_info_ptr);\nvoid xdriver_end(void);\nvoid win_resize_window(Window_info *win_info_ptr);\nvoid x_update_display(Window_info *win_info_ptr);\nvoid x_hide_pointer(int do_hide);\nint opendir_int(DIR *dirp, const char *in_filename);\nDIR *opendir(const char *in_filename);\nstruct dirent *readdir(DIR *dirp);\nint closedir(DIR *dirp);\nint lstat(const char *path, struct stat *bufptr);\nint ftruncate(int fd, word32 length);\n\n\n\n/* win32snd_driver.c */\nvoid win32snd_init(word32 *shmaddr);\nvoid win32snd_shutdown(void);\nvoid CALLBACK handle_wav_snd(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);\nvoid check_wave_error(int res, char *str);\nvoid child_sound_init_win32(void);\nvoid win32snd_set_playing(int snd_playing);\nvoid win32_send_audio2(byte *ptr, int size);\nint win32_send_audio(byte *ptr, int in_size);\n\n\n"
  },
  {
    "path": "gsplus/src/protos_xdriver.h",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n\n/* END_HDR */\n\n/* xdriver.c */\nint main(int argc, char **argv);\nint my_error_handler(Display *display, XErrorEvent *ev);\nvoid xdriver_end(void);\nvoid x_try_xset_r(void);\nvoid x_badpipe(int signum);\nint kegs_x_io_error_handler(Display *display);\nint x_video_get_mdepth(void);\nint x_try_find_visual(int depth, int screen_num);\nvoid x_video_init(void);\nvoid x_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str);\nvoid x_create_window(Window_info *win_info_ptr);\nint xhandle_shm_error(Display *display, XErrorEvent *event);\nvoid x_allocate_window_data(Window_info *win_info_ptr);\nvoid get_shm(Window_info *win_info_ptr, int width, int height);\nvoid get_ximage(Window_info *win_info_ptr, int width, int height);\nvoid x_set_size_hints(Window_info *win_info_ptr);\nvoid x_resize_window(Window_info *win_info_ptr);\nvoid x_update_display(Window_info *win_info_ptr);\nWindow_info *x_find_xwin(Window in_win);\nvoid x_send_copy_data(Window_info *win_info_ptr);\nvoid x_handle_copy(XSelectionRequestEvent *req_ev_ptr);\nvoid x_handle_targets(XSelectionRequestEvent *req_ev_ptr);\nvoid x_request_paste_data(Window_info *win_info_ptr);\nvoid x_handle_paste(Window w, Atom property);\nint x_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y, int button_states, int buttons_valid);\nvoid x_input_events(void);\nvoid x_hide_pointer(Window_info *win_info_ptr, int do_hide);\nvoid x_handle_keysym(XEvent *xev_in);\nint x_keysym_to_a2code(Window_info *win_info_ptr, int keysym, int is_up);\nvoid x_update_modifier_state(Window_info *win_info_ptr, int state);\nvoid x_auto_repeat_on(int must);\nvoid x_auto_repeat_off(int must);\nvoid x_full_screen(int do_full);\n\n\n"
  },
  {
    "path": "gsplus/src/pulseaudio_driver.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2020 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#ifdef PULSE_AUDIO\n// Ignore entire file if PULSE_AUDIO is not defined!\n\n// Some ideas from Sample code:\n//  https://github.com/gavv/snippets/blob/master/pa/pa_play_async_poll.c\n\n#include \"defc.h\"\n#include \"sound.h\"\n#include \"protos_pulseaudio_driver.h\"\n\n#include <pulse/pulseaudio.h>\n#include <unistd.h>\n\n#define PULSE_REBUF_SIZE\t(64*1024)\n\nword32\tg_pulseaudio_rebuf[PULSE_REBUF_SIZE];\nvolatile int g_pulseaudio_rd_pos;\nvolatile int g_pulseaudio_wr_pos;\nvolatile int g_pulseaudio_playing = 0;\n\nextern int Verbose;\n\nextern int g_preferred_rate;\nextern int g_audio_enable;\nextern word32 *g_sound_shm_addr;\nextern int g_sound_min_samples;\nextern int g_sound_max_multiplier;\nextern int g_sound_size;\nextern word32 g_vbl_count;\n\nint g_call_num = 0;\n\npa_mainloop *g_pa_mainloop_ptr = 0;\npa_context *g_pa_context_ptr = 0;\npa_stream *g_pa_stream_ptr = 0;\npa_sample_spec g_pa_sample_spec = { 0 };\npa_buffer_attr g_pa_buffer_attr = { 0 };\n\n\nint\npulse_audio_send_audio(byte *ptr, int in_size)\n{\n\tword32\t*wptr, *pa_wptr;\n\tint\tsamps, sample_num;\n\tint\ti;\n\n\tsamps = in_size / 4;\n\twptr = (word32 *)ptr;\n\tsample_num = g_pulseaudio_wr_pos;\n\tpa_wptr = &(g_pulseaudio_rebuf[0]);\n\tfor(i = 0; i < samps; i++) {\n\t\tpa_wptr[sample_num] = *wptr++;\n\t\tsample_num++;\n\t\tif(sample_num >= PULSE_REBUF_SIZE) {\n\t\t\tsample_num = 0;\n\t\t}\n\t}\n\n\tg_pulseaudio_wr_pos = sample_num;\n\n\tpulse_audio_main_events();\n\n\treturn in_size;\n}\n\nvoid\npulse_audio_main_events()\n{\n\tpa_stream_state_t stream_state;\n\tpa_context_state_t context_state;\n\tint\tcount, num, sz, do_write;\n\n\tcount = 0;\n\tdo_write = 1;\n\twhile(1) {\n\t\t// Do a few mainloop cycles to see if samples are needed\n\t\tnum = pa_mainloop_iterate(g_pa_mainloop_ptr, 0, 0);\n\t\t//printf(\"pa_mainloop_iterate ret:%d count:%d\\n\", num, count);\n\t\tif(num < 0) {\n\t\t\treturn;\n\t\t}\n\t\tcontext_state = pa_context_get_state(g_pa_context_ptr);\n\t\tif((context_state == PA_CONTEXT_FAILED) ||\n\t\t\t\t(context_state == PA_CONTEXT_TERMINATED)) {\n\t\t\tprintf(\"context_state is bad: %d\\n\", context_state);\n\t\t\tg_audio_enable = 0;\n\t\t\treturn;\n\t\t}\n\t\tstream_state = pa_stream_get_state(g_pa_stream_ptr);\n\t\tif((stream_state == PA_STREAM_FAILED) ||\n\t\t\t\t(stream_state == PA_STREAM_TERMINATED)) {\n\t\t\tprintf(\"stream state bad: %d\\n\", stream_state);\n\t\t\tg_audio_enable = 0;\n\t\t\treturn;\n\t\t}\n\t\tif(do_write) {\n\t\t\tsz = pa_stream_writable_size(g_pa_stream_ptr);\n\t\t\tif(sz > 0) {\n\t\t\t\tpulse_audio_write_to_stream(count, sz);\n\t\t\t\tdo_write = 0;\n\t\t\t}\n\t\t}\n\t\tcount++;\n\t\tif(count > 50) {\n\t\t\tprintf(\"pulse_audio_main_events() looped %d times\\n\",\n\t\t\t\t\t\t\t\tcount);\n\t\t\treturn;\n\t\t}\n\t\tif(num == 0) {\t\t\t// Nothing else to do\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid\npulse_audio_write_to_stream(int dbg_count, int in_sz)\n{\n\tword32\t*wptr;\n\tvoid\t*vptr;\n\t// const pa_timing_info *pa_timing_info_ptr;\n\t// pa_usec_t\tpa_latency;\n\tsize_t\tsz;\n\tint\tnum_samps, sample_num, samps_avail, err, samps_needed;\n\tint\tmin_samples, max_samples;\n\tint\ti;\n\n\tsamps_needed = in_sz / 4;\n\tsample_num = g_pulseaudio_rd_pos;\n\tsamps_avail = (g_pulseaudio_wr_pos - sample_num) &\n\t\t\t\t\t\t\t(PULSE_REBUF_SIZE - 1);\n\tmin_samples = g_sound_min_samples;\n\tmax_samples = min_samples * g_sound_max_multiplier;\n\tif(samps_needed > min_samples) {\n\t\tmin_samples = samps_needed;\n\t}\n#if 0\n\tif(samps_needed > samps_avail) {\n\t\t// We don't have enough samples, must pause\n\t\tg_pulseaudio_playing = 0;\n\t\tsample_num = g_pulseaudio_wr_pos;\t// Eat remaining samps\n\t}\n#endif\n\tif((g_pulseaudio_playing == 0) && (samps_avail > min_samples)) {\n\t\t// We can unpause\n\t\tg_pulseaudio_playing = 1;\n\t}\n\tif(g_pulseaudio_playing && (samps_avail > max_samples)) {\n\t\tprintf(\"JUMP SAMPLE_NUM by %d samples!\\n\", max_samples / 2);\n\t\tsample_num += (max_samples / 2);\n\t\tsample_num = sample_num & (PULSE_REBUF_SIZE - 1);\n\t}\n\n#if 0\n\tif(g_call_num < 100) {\n\t\tprintf(\"call_num:%d playing:%d g_vbl_count:%d samps_needed:%d, \"\n\t\t\t\"samps_avail:%d\\n\", g_call_num, g_pulseaudio_playing,\n\t\t\tg_vbl_count, samps_needed, samps_avail);\n\t}\n#endif\n\tg_call_num++;\n\tnum_samps = MIN(samps_avail, samps_needed);\n\tif(g_pulseaudio_playing) {\n\t\tvptr = 0;\t\t\t// Let it allocate for us\n\t\tsz = num_samps * 4;\n\t\terr = pa_stream_begin_write(g_pa_stream_ptr, &vptr, &sz);\n\t\twptr = vptr;\n\t\tif(err) {\n\t\t\tg_audio_enable = 0;\n\t\t\tprintf(\"pa_stream_begin_write failed: %s\\n\",\n\t\t\t\t\t\t\tpa_strerror(err));\n\t\t\treturn;\n\t\t}\n\t\tnum_samps = sz / 4;\n\t\tfor(i = 0; i < num_samps; i++) {\n\t\t\twptr[i] = g_pulseaudio_rebuf[sample_num];\n\t\t\tsample_num++;\n\t\t\tif(sample_num >= PULSE_REBUF_SIZE) {\n\t\t\t\tsample_num = 0;\n\t\t\t}\n\t\t}\n\t} else {\n\t\terr = pa_stream_cancel_write(g_pa_stream_ptr);\n\t\t// Just get out...don't let us get further behind by sending\n\t\t//  silence frames.\n\t\treturn;\n\t}\n\n\tg_pulseaudio_rd_pos = sample_num;\n\n#if 0\n\tpa_timing_info_ptr = pa_stream_get_timing_info(g_pa_stream_ptr);\n\terr = pa_stream_get_latency(g_pa_stream_ptr, &pa_latency, 0);\n\tprintf(\" will send %d samples to the stream, write_index:%lld, \"\n\t\t\"latency:%lld\\n\", num_samps * 4,\n\t\t(word64)pa_timing_info_ptr->write_index, (word64)pa_latency);\n#endif\n\terr = pa_stream_write(g_pa_stream_ptr, wptr, num_samps * 4, 0, 0,\n\t\t\t\t\t\t\tPA_SEEK_RELATIVE);\n\tif(err) {\n\t\tprintf(\"pa_stream_write: %s\\n\", pa_strerror(err));\n\t\tg_audio_enable = 0;\n\t}\n}\n\nint\npulse_audio_start_stream()\n{\n\tint\tflags, ret;\n\n\tg_pa_sample_spec.format = PA_SAMPLE_S16LE;\n\tg_pa_sample_spec.rate = g_preferred_rate;\n\tg_pa_sample_spec.channels = 2;\n\tprintf(\"Set requested rate=%d\\n\", g_pa_sample_spec.rate);\n\tg_pa_stream_ptr = pa_stream_new(g_pa_context_ptr, \"KEGS\",\n\t\t\t\t\t\t&g_pa_sample_spec, 0);\n\tif(!g_pa_stream_ptr) {\n\t\tprintf(\"pa_stream_new failed\\n\");\n\t\treturn 1;\n\t}\n\n\tg_pa_buffer_attr.maxlength = -1;\t\t// Maximum server buffer\n\tg_pa_buffer_attr.tlength = 4*g_preferred_rate/10;\t// 1/10th sec\n\t//g_pa_buffer_attr.prebuf = 4*g_preferred_rate/100;\t// 1/100th sec\n\tg_pa_buffer_attr.prebuf = -1;\n\tg_pa_buffer_attr.minreq = 4*g_preferred_rate/60;\t// 1/60th sec\n\n\tflags = PA_STREAM_ADJUST_LATENCY;\n\t//flags = PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |\n\t//\t\tPA_STREAM_ADJUST_LATENCY;\n\t// PA_STREAM_AUTO_TIMING_UPDATE and PA_STREAM_INTERPOLATE_TIMING are\n\t//  to get latency info from the server.  PA_STREAM_ADJUST_LATENCY\n\t//  means the total latency including the server output sink buffer\n\t//  tries to be tlength\n\n\tret = pa_stream_connect_playback(g_pa_stream_ptr, 0, &g_pa_buffer_attr,\n\t\t\t\t\t\t\tflags, 0, 0);\n\tif(ret) {\n\t\tprintf(\"pa_stream_connect_playback failed: %d\\n\", ret);\n\t\treturn ret;\n\t}\n\n\treturn 0;\t\t// Success!\n}\n\nint\npulse_audio_do_init()\n{\n\tpa_stream_state_t stream_state;\n\tpa_context_state_t context_state;\n\tint\tret, count, num;\n\n\tg_pa_mainloop_ptr = pa_mainloop_new();\n\tg_pa_context_ptr = pa_context_new(\n\t\t\tpa_mainloop_get_api(g_pa_mainloop_ptr), \"KEGS\");\n\tif(!g_pa_context_ptr) {\n\t\tprintf(\"Pulse Audio pa_context_new() failed\\n\");\n\t\treturn 1;\n\t}\n\n\tret = pa_context_connect(g_pa_context_ptr, 0, 0, 0);\n\tif(ret != 0) {\n\t\tprintf(\"pa_context_connect failed: %d\\n\", ret);\n\t\treturn 1;\n\t}\n\n\tcount = 0;\n\twhile(1) {\n\t\t// Do a few mainloop cycles to get stream initialized\n\t\tnum = pa_mainloop_iterate(g_pa_mainloop_ptr, 0, 0);\n#if 0\n\t\tprintf(\"pa_mainloop_iterate ret: %d, count:%d g_pa_stream_ptr:\"\n\t\t\t\t\"%p\\n\", num, count, g_pa_stream_ptr);\n#endif\n\t\tif(num < 0) {\n\t\t\treturn 1;\n\t\t}\n\t\tif(num == 0) {\n\t\t\tusleep(10*1000);\n\t\t\tif(count++ > 50) {\n\t\t\t\t// Waited more than 500ms, just give up\n\t\t\t\tprintf(\"Timed out waiting for Pulse Audio to \"\n\t\t\t\t\t\t\t\t\"start\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t\t// See if context is ready\n\t\tcontext_state = pa_context_get_state(g_pa_context_ptr);\n\t\tif((context_state == PA_CONTEXT_FAILED) ||\n\t\t\t\t(context_state == PA_CONTEXT_TERMINATED)) {\n\t\t\tprintf(\"context_state is bad: %d\\n\", context_state);\n\t\t\treturn 1;\n\t\t}\n\t\tif((context_state == PA_CONTEXT_READY) && !g_pa_stream_ptr) {\n\t\t\tret = pulse_audio_start_stream();\n\t\t\tif(ret) {\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t}\n\t\tif(g_pa_stream_ptr) {\n\t\t\tstream_state = pa_stream_get_state(g_pa_stream_ptr);\n\t\t\tif((stream_state == PA_STREAM_FAILED) ||\n\t\t\t\t\t(stream_state == PA_STREAM_TERMINATED)){\n\t\t\t\tprintf(\"stream state bad: %d\\n\", stream_state);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif(stream_state == PA_STREAM_READY) {\n\t\t\t\tprintf(\"Pulse Audio stream is now ready!\\n\");\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\nint\npulse_audio_init()\n{\n\tint\tret;\n\n\tg_pulseaudio_rd_pos = 0;\n\tg_pulseaudio_wr_pos = 0;\n\n\tret = pulse_audio_do_init();\n\t// printf(\"pulse_audio_init ret:%d\\n\", ret);\n\tif(ret != 0) {\n\t\t// Free structures, disable sound\n\t\tif(g_pa_stream_ptr) {\n\t\t\tpa_stream_disconnect(g_pa_stream_ptr);\n\t\t\tpa_stream_unref(g_pa_stream_ptr);\n\t\t\tg_pa_stream_ptr = 0;\n\t\t}\n\t\tif(g_pa_context_ptr) {\n\t\t\tpa_context_disconnect(g_pa_context_ptr);\n\t\t\tpa_context_unref(g_pa_context_ptr);\n\t\t\tg_pa_context_ptr = 0;\n\t\t}\n\t\tif(g_pa_mainloop_ptr) {\n\t\t\tpa_mainloop_free(g_pa_mainloop_ptr);\n\t\t\tg_pa_mainloop_ptr = 0;\n\t\t}\n\t\treturn ret;\n\t}\n\n\tsound_set_audio_rate(g_preferred_rate);\n\n\treturn 0;\n}\n\nvoid\npulse_audio_shutdown()\n{\n\tprintf(\"pulse_audio_shutdown\\n\");\n}\n\n#endif\t\t/* PULSE_AUDIO */\n"
  },
  {
    "path": "gsplus/src/scc.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2025 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// Driver for the Zilog SCC Z8530, which implements two channels (A,B) of\n//  serial ports, controlled by $C038-$C03B\n\n#include \"defc.h\"\n\nextern int Verbose;\nextern int g_code_yellow;\nextern dword64 g_cur_dfcyc;\nextern int g_serial_cfg[2];\nextern int g_serial_mask[2];\nextern char *g_serial_remote_ip[2];\nextern int g_serial_remote_port[2];\nextern char *g_serial_device[2];\nextern int g_serial_win_device[2];\nextern int g_irq_pending;\n\n/* scc\tport 0 == channel A = slot 1 = c039/c03b */\n/*\tport 1 == channel B = slot 2 = c038/c03a */\n\n#include \"scc.h\"\n#define SCC_R14_DPLL_SOURCE_BRG\t\t0x100\n#define SCC_R14_DPLL_SOURCE_RTXC\t0x200\n\n#define SCC_DCYCS_PER_PCLK\t((DCYCS_1_MHZ) / ((DCYCS_28_MHZ) /8))\n#define SCC_DCYCS_PER_XTAL\t((DCYCS_1_MHZ) / 3686400.0)\n\n// PCLK is 3.5795MHz\n\n#define SCC_BR_EVENT\t\t\t1\n#define SCC_TX_EVENT\t\t\t2\n#define SCC_RX_EVENT\t\t\t3\n#define SCC_MAKE_EVENT(port, a)\t\t(((a) << 1) + (port))\n\nScc\tg_scc[2];\n\nint g_baud_table[] = {\n\t110, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200\n};\n\nint g_scc_overflow = 0;\nint\tg_scc_init = 0;\n\n// cur_state >= 0 and matches g_serial_cfg[port]: port is in that mode\n// cur_state = -1: port is in no particular mode and should go to g_serial_cfg[]\n// cur_state = -2: port failed to enter g_serial_cfg[], do not try again until\n//\t\tsomething changes\n\nvoid\nscc_init()\n{\n\tScc\t*scc_ptr;\n\tint\ti, j;\n\n\tfor(i = 0; i < 2; i++) {\n\t\tscc_ptr = &(g_scc[i]);\n\t\tmemset(scc_ptr, 0, sizeof(*scc_ptr));\n\t\tscc_ptr->cur_state = -1;\n\t\tscc_ptr->modem_state = 0;\n\t\tscc_ptr->sockfd = INVALID_SOCKET;\n\t\tscc_ptr->rdwrfd = INVALID_SOCKET;\n\t\tscc_ptr->sockaddr_ptr = 0;\n\t\tscc_ptr->sockaddr_size = 0;\n\t\tscc_ptr->unix_dev_fd = -1;\n\t\tscc_ptr->win_com_handle = 0;\n\t\tscc_ptr->win_dcb_ptr = 0;\n\t\tscc_ptr->br_event_pending = 0;\n\t\tscc_ptr->rx_event_pending = 0;\n\t\tscc_ptr->tx_event_pending = 0;\n\t\tscc_ptr->char_size = 8;\n\t\tscc_ptr->baud_rate = 9600;\n\t\tscc_ptr->telnet_mode = 0;\n\t\tscc_ptr->telnet_iac = 0;\n\t\tscc_ptr->out_char_dfcyc = 0;\n\t\tscc_ptr->socket_error = 0;\n\t\tscc_ptr->socket_num_rings = 0;\n\t\tscc_ptr->socket_last_ring_dfcyc = 0;\n\t\tscc_ptr->modem_mode = 0;\n\t\tscc_ptr->modem_plus_mode = 0;\n\t\tscc_ptr->modem_s0_val = 0;\n\t\tscc_ptr->modem_s2_val = '+';\n\t\tscc_ptr->modem_cmd_len = 0;\n\t\tscc_ptr->modem_out_portnum = 23;\n\t\tscc_ptr->modem_cmd_str[0] = 0;\n\t\tfor(j = 0; j < 2; j++) {\n\t\t\tscc_ptr->telnet_local_mode[j] = 0;\n\t\t\tscc_ptr->telnet_remote_mode[j] = 0;\n\t\t\tscc_ptr->telnet_reqwill_mode[j] = 0;\n\t\t\tscc_ptr->telnet_reqdo_mode[j] = 0;\n\t\t}\n\t}\n\n\tg_scc_init = 1;\n}\n\nvoid\nscc_reset()\n{\n\tScc\t*scc_ptr;\n\tint\ti;\n\n\tif(!g_scc_init) {\n\t\thalt_printf(\"scc_reset called before init\\n\");\n\t\treturn;\n\t}\n\tfor(i = 0; i < 2; i++) {\n\t\tscc_ptr = &(g_scc[i]);\n\n\t\tscc_ptr->mode = 0;\n\t\tscc_ptr->reg_ptr = 0;\n\t\tscc_ptr->in_rdptr = 0;\n\t\tscc_ptr->in_wrptr = 0;\n\t\tscc_ptr->out_rdptr = 0;\n\t\tscc_ptr->out_wrptr = 0;\n\t\tscc_ptr->dcd = 1 - i;\t\t// 1 for slot 1, 0 for slot 2\n\t\tscc_ptr->wantint_rx = 0;\n\t\tscc_ptr->wantint_tx = 0;\n\t\tscc_ptr->wantint_zerocnt = 0;\n\t\tscc_ptr->read_called_this_vbl = 0;\n\t\tscc_ptr->write_called_this_vbl = 0;\n\t\tscc_evaluate_ints(i);\n\t\tscc_hard_reset_port(i);\n\t}\n}\n\nvoid\nscc_hard_reset_port(int port)\n{\n\tScc\t*scc_ptr;\n\n\tscc_reset_port(port);\n\n\tscc_ptr = &(g_scc[port]);\n\tscc_ptr->reg[14] = 0;\t\t/* zero bottom two bits */\n\tscc_ptr->reg[13] = 0;\n\tscc_ptr->reg[12] = 0;\n\tscc_ptr->reg[11] = 0x08;\n\tscc_ptr->reg[10] = 0;\n\tscc_ptr->reg[7] = 0;\n\tscc_ptr->reg[6] = 0;\n\tscc_ptr->reg[5] = 0;\n\tscc_ptr->reg[4] = 0x04;\n\tscc_ptr->reg[3] = 0;\n\tscc_ptr->reg[2] = 0;\n\tscc_ptr->reg[1] = 0;\n\n\t/* HACK HACK: */\n\tg_scc[0].reg[9] = 0;\t\t/* Clear all interrupts */\n\n\tscc_evaluate_ints(port);\n\n\tscc_regen_clocks(port);\n}\n\nvoid\nscc_reset_port(int port)\n{\n\tScc\t*scc_ptr;\n\n\tscc_ptr = &(g_scc[port]);\n\tscc_ptr->reg[15] = 0xf8;\n\tscc_ptr->reg[14] &= 0x03;\t/* 0 most (including >= 0x100) bits */\n\tscc_ptr->reg[10] = 0;\n\tscc_ptr->reg[5] &= 0x65;\t/* leave tx bits and sdlc/crc bits */\n\tscc_ptr->reg[4] |= 0x04;\t/* Set async mode */\n\tscc_ptr->reg[3] &= 0xfe;\t/* clear receiver enable */\n\tscc_ptr->reg[1] &= 0xfe;\t/* clear ext int enable */\n\n\tscc_ptr->br_is_zero = 0;\n\tscc_ptr->tx_buf_empty = 1;\n\n\tscc_ptr->wantint_rx = 0;\n\tscc_ptr->wantint_tx = 0;\n\tscc_ptr->wantint_zerocnt = 0;\n\n\tscc_ptr->rx_queue_depth = 0;\n\n\tscc_evaluate_ints(port);\n\n\tscc_regen_clocks(port);\n\n\tscc_clr_tx_int(port);\n\tscc_clr_rx_int(port);\n}\n\nvoid\nscc_regen_clocks(int port)\n{\n\tScc\t*scc_ptr;\n\tdouble\tbr_dcycs, tx_dcycs, rx_dcycs, rx_char_size, tx_char_size;\n\tdouble\tclock_mult, dpll_dcycs;\n\tword32\treg4, reg11, reg14, br_const, max_diff, diff;\n\tint\tbaud, cur_state, baud_entries, pos;\n\tint\ti;\n\n\t/*\tAlways do baud rate generator */\n\tscc_ptr = &(g_scc[port]);\n\tbr_const = (scc_ptr->reg[13] << 8) + scc_ptr->reg[12];\n\tbr_const += 2;\t/* counts down past 0 */\n\n\treg4 = scc_ptr->reg[4];\t\t// Transmit/Receive misc params\n\tclock_mult = 1.0;\n\tswitch((reg4 >> 6) & 3) {\n\tcase 0:\t\t/* x1 */\n\t\tclock_mult = 1.0;\n\t\tbreak;\n\tcase 1:\t\t/* x16 */\n\t\tclock_mult = 16.0;\n\t\tbreak;\n\tcase 2:\t\t/* x32 */\n\t\tclock_mult = 32.0;\n\t\tbreak;\n\tcase 3:\t\t/* x64 */\n\t\tclock_mult = 64.0;\n\t\tbreak;\n\t}\n\n\tbr_dcycs = 0.01;\n\treg14 = scc_ptr->reg[14];\n\tif(reg14 & 0x1) {\n\t\tbr_dcycs = SCC_DCYCS_PER_XTAL;\n\t\tif(reg14 & 0x2) {\n\t\t\tbr_dcycs = SCC_DCYCS_PER_PCLK;\n\t\t}\n\t}\n\n\tbr_dcycs = br_dcycs * (double)br_const * 2.0;\n\n\tdpll_dcycs = 0.1;\n\tif(reg14 & SCC_R14_DPLL_SOURCE_BRG) {\n\t\tdpll_dcycs = br_dcycs;\n\t} else if(reg14 & SCC_R14_DPLL_SOURCE_RTXC) {\n\t\tdpll_dcycs = SCC_DCYCS_PER_XTAL;\n\t}\n\n\ttx_dcycs = 1;\n\treg11 = scc_ptr->reg[11];\n\tswitch((reg11 >> 3) & 3) {\n\tcase 0:\t\t// /RTxC pin\n\t\ttx_dcycs = SCC_DCYCS_PER_XTAL;\n\t\tbreak;\n\tcase 2:\t\t// BR generator output\n\t\ttx_dcycs = br_dcycs;\n\t\tbreak;\n\tcase 3:\t\t// DPLL output\n\t\ttx_dcycs = dpll_dcycs;\n\t\tbreak;\n\t}\n\n\ttx_dcycs = tx_dcycs * clock_mult;\n\n\trx_dcycs = 1;\n\tswitch((reg11 >> 5) & 3) {\n\tcase 0:\t\t// /RTxC pin\n\t\trx_dcycs = SCC_DCYCS_PER_XTAL;\n\t\tbreak;\n\tcase 2:\t\t// BR generator output\n\t\trx_dcycs = br_dcycs;\n\t\tbreak;\n\tcase 3:\t\t// DPLL output\n\t\trx_dcycs = dpll_dcycs;\n\t\tbreak;\n\t}\n\trx_dcycs = rx_dcycs * clock_mult;\n\n\ttx_char_size = 8.0;\n\tswitch((scc_ptr->reg[5] >> 5) & 0x3) {\n\tcase 0:\t// 5 bits\n\t\ttx_char_size = 5.0;\n\t\tbreak;\n\tcase 1:\t// 7 bits\n\t\ttx_char_size = 7.0;\n\t\tbreak;\n\tcase 2:\t// 6 bits\n\t\ttx_char_size = 6.0;\n\t\tbreak;\n\t}\n\n\tscc_ptr->char_size = (int)tx_char_size;\n\n\tswitch((scc_ptr->reg[4] >> 2) & 0x3) {\n\tcase 1:\t// 1 stop bit\n\t\ttx_char_size += 2.0;\t// 1 stop + 1 start bit\n\t\tbreak;\n\tcase 2:\t// 1.5 stop bit\n\t\ttx_char_size += 2.5;\t// 1.5 stop + 1 start bit\n\t\tbreak;\n\tcase 3:\t// 2 stop bits\n\t\ttx_char_size += 3.0;\t// 2.0 stop + 1 start bit\n\t\tbreak;\n\t}\n\n\tif(scc_ptr->reg[4] & 1) {\n\t\t// parity enabled\n\t\ttx_char_size += 1.0;\n\t}\n\n\tif(scc_ptr->reg[14] & 0x10) {\n\t\t/* loopback mode, make it go faster...*/\n\t\trx_char_size = 1.0;\n\t\ttx_char_size = 1.0;\n\t}\n\n\trx_char_size = tx_char_size;\t/* HACK */\n\n\tbaud = (int)(DCYCS_1_MHZ / tx_dcycs);\n\tmax_diff = 5000000;\n\tpos = 0;\n\tbaud_entries = sizeof(g_baud_table)/sizeof(g_baud_table[0]);\n\tfor(i = 0; i < baud_entries; i++) {\n\t\tdiff = abs(g_baud_table[i] - baud);\n\t\tif(diff < max_diff) {\n\t\t\tpos = i;\n\t\t\tmax_diff = diff;\n\t\t}\n\t}\n\n\tscc_ptr->baud_rate = g_baud_table[pos];\n\n\tscc_ptr->br_dcycs = br_dcycs;\n\tscc_ptr->tx_dcycs = tx_dcycs * tx_char_size;\n\tscc_ptr->rx_dcycs = rx_dcycs * rx_char_size;\n\n\tcur_state = scc_ptr->cur_state;\n\tif(cur_state == 0) {\t\t// real serial ports\n#ifdef _WIN32\n\t\tscc_serial_win_change_params(port);\n#else\n\t\tscc_serial_unix_change_params(port);\n#endif\n\t}\n}\n\nvoid\nscc_port_close(int port)\n{\n\tScc\t*scc_ptr;\n\n\tscc_ptr = &(g_scc[port]);\n\n#ifdef _WIN32\n\tscc_serial_win_close(port);\n#else\n\tscc_serial_unix_close(port);\n#endif\n\tscc_socket_close(port);\n\n\tscc_ptr->cur_state = -1;\t\t// Nothing open\n}\n\nvoid\nscc_port_open(dword64 dfcyc, int port)\n{\n\tint\tcfg;\n\n\tcfg = g_serial_cfg[port];\n\tprintf(\"scc_port_open port:%d cfg:%d\\n\", port, cfg);\n\n\tif(cfg == 0) {\t\t// Real host serial port\n#ifdef _WIN32\n\t\tscc_serial_win_open(port);\n#else\n\t\tscc_serial_unix_open(port);\n#endif\n\t} else if(cfg >= 1) {\n\t\tscc_socket_open(dfcyc, port, cfg);\n\t}\n\tprintf(\" open socketfd:%ld\\n\", (long)g_scc[port].sockfd);\n}\n\nint\nscc_is_port_closed(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\n\t// Returns 1 is the port is closed (not working).  Returns 0\n\t//  if the port is open.  Tries to open the port if it is not in error\n\tscc_ptr = &(g_scc[port]);\n\tif(scc_ptr->cur_state == -1) {\n\t\tscc_port_open(dfcyc, port);\n\t}\n\tif(scc_ptr->cur_state < 0) {\n\t\tscc_ptr->cur_state = -2;\n\t\t//printf(\"scc_is_port_closed p:%d returning 0\\n\", port);\n\t\treturn 1;\t\t// Not open\n\t}\n\treturn 0;\t\t\t// Port is open!\n}\n\nchar *\nscc_get_serial_status(int get_status, int port)\n{\n\tchar\tbuf[80];\n\tchar\t*str;\n\tint\tcur_state;\n\n\tif(get_status == 0) {\n\t\treturn 0;\n\t}\n\tcur_state = g_scc[port].cur_state;\n\tstr = \"\";\n\tswitch(cur_state) {\n\tcase -1:\n\t\tstr = \"Not initialized yet\";\n\t\tbreak;\n\tcase 0:\n\t\tsnprintf(buf, 80, \"Opened %s OK\", g_serial_device[port]);\n\t\tstr = buf;\n\t\tbreak;\n\tcase 1:\n\t\tsnprintf(buf, 80, \"Virtual modem, sockfd:%d\",\n\t\t\t(int)g_scc[port].sockfd);\n\t\tstr = buf;\n\t\tbreak;\n\tcase 2:\n\t\tsnprintf(buf, 80, \"Outgoing to %s:%d\",\n\t\t\tg_serial_remote_ip[port], g_serial_remote_port[port]);\n\t\tstr = buf;\n\t\tbreak;\n\tcase 3:\n\t\tsnprintf(buf, 80, \"Opened %d, sockfd:%d\", 6501 + port,\n\t\t\t\t\t(int)g_scc[port].sockfd);\n\t\tstr = buf;\n\t\tbreak;\n\tdefault:\n\t\tstr = \"Open failed, port is closed\";\n\t}\n\treturn kegs_malloc_str(str);\n}\n\n\nvoid\nscc_config_changed(int port, int cfg_changed, int remote_changed,\n\t\t\t\t\t\tint serial_dev_changed)\n{\n\tScc\t*scc_ptr;\n\tint\tmust_change;\n\n\t// Check if scc_init() was called, if not get out\n\tif(!g_scc_init) {\n\t\treturn;\n\t}\n\n\t// F4 may have changed the serial port config.  If so, close old\n\t//  port, open new one.\n\n\tscc_ptr = &(g_scc[port]);\n\tmust_change = cfg_changed;\n\tswitch(scc_ptr->cur_state) {\n\tcase 0:\t\t// Using serial port\n\t\tmust_change |= serial_dev_changed;\n\t\tbreak;\n\tcase 2:\t\t// Using remote connection\n\t\tmust_change |= remote_changed;\n\t\tbreak;\n\t}\n\tif(must_change) {\n\t\tscc_port_close(port);\n\t}\n}\n\nvoid\nscc_update(dword64 dfcyc)\n{\n\tint\ti;\n\n\t// called each VBL update\n\tfor(i = 0; i < 2; i++) {\n\t\tg_scc[i].write_called_this_vbl = 0;\n\t\tg_scc[i].read_called_this_vbl = 0;\n\n\t\t// These calls will try to open the port if it's closed\n\t\tscc_try_to_empty_writebuf(dfcyc, i);\n\t\tscc_try_fill_readbuf(dfcyc, i);\n\n\t\tg_scc[i].write_called_this_vbl = 0;\n\t\tg_scc[i].read_called_this_vbl = 0;\n\t}\n}\n\nvoid\nscc_try_to_empty_writebuf(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tint\tcur_state;\n\n\tscc_ptr = &(g_scc[port]);\n\tcur_state = scc_ptr->cur_state;\n\tif(scc_ptr->write_called_this_vbl) {\n\t\treturn;\n\t}\n\tif(scc_is_port_closed(dfcyc, port)) {\n\t\treturn;\t\t\t\t// Port is not open\n\t}\n\n\tscc_ptr->write_called_this_vbl = 1;\n\n\tif(cur_state == 0) {\n#if defined(_WIN32)\n\t\tscc_serial_win_empty_writebuf(port);\n#else\n\t\tscc_serial_unix_empty_writebuf(port);\n#endif\n\t} else if(cur_state >= 1) {\n\t\tscc_socket_empty_writebuf(dfcyc, port);\n\t}\n}\n\nvoid\nscc_try_fill_readbuf(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tint\tspace_used, space_left, cur_state;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tspace_used = scc_ptr->in_wrptr - scc_ptr->in_rdptr;\n\tif(space_used < 0) {\n\t\tspace_used += SCC_INBUF_SIZE;\n\t}\n\tspace_left = (7*SCC_INBUF_SIZE/8) - space_used;\n\tif(space_left < 1) {\n\t\t/* Buffer is pretty full, don't try to get more */\n\t\treturn;\n\t}\n\n\tif(scc_is_port_closed(dfcyc, port)) {\n\t\treturn;\t\t\t\t// Port is not open\n\t}\n\n#if 0\n\tif(scc_ptr->read_called_this_vbl) {\n\t\treturn;\n\t}\n#endif\n\n\tscc_ptr->read_called_this_vbl = 1;\n\n\tcur_state = scc_ptr->cur_state;\n\tif(cur_state == 0) {\n#if defined(_WIN32)\n\t\tscc_serial_win_fill_readbuf(dfcyc, port, space_left);\n#else\n\t\tscc_serial_unix_fill_readbuf(dfcyc, port, space_left);\n#endif\n\t} else if(cur_state >= 1) {\n\t\tscc_socket_fill_readbuf(dfcyc, port, space_left);\n\t}\n}\n\nvoid\nscc_do_event(dword64 dfcyc, int type)\n{\n\tScc\t*scc_ptr;\n\tint\tport;\n\n\tport = type & 1;\n\ttype = (type >> 1);\n\n\tscc_ptr = &(g_scc[port]);\n\tif(type == SCC_BR_EVENT) {\n\t\t/* baud rate generator counted down to 0 */\n\t\tscc_ptr->br_event_pending = 0;\n\t\tscc_set_zerocnt_int(port);\n\t\tscc_maybe_br_event(dfcyc, port);\n\t} else if(type == SCC_TX_EVENT) {\n\t\tscc_ptr->tx_event_pending = 0;\n\t\tscc_ptr->tx_buf_empty = 1;\n\t\tscc_handle_tx_event(port);\n\t} else if(type == SCC_RX_EVENT) {\n\t\tscc_ptr->rx_event_pending = 0;\n\t\tscc_maybe_rx_event(dfcyc, port);\n\t\tscc_maybe_rx_int(port);\n\t} else {\n\t\thalt_printf(\"scc_do_event: %08x!\\n\", type);\n\t}\n\treturn;\n}\n\nvoid\nshow_scc_state()\n{\n\tScc\t*scc_ptr;\n\tint\ti, j;\n\n\tfor(i = 0; i < 2; i++) {\n\t\tscc_ptr = &(g_scc[i]);\n\t\tprintf(\"SCC port: %d\\n\", i);\n\t\tfor(j = 0; j < 16; j += 4) {\n\t\t\tprintf(\"Reg %2d-%2d: %02x %02x %02x %02x\\n\", j, j+3,\n\t\t\t\tscc_ptr->reg[j], scc_ptr->reg[j+1],\n\t\t\t\tscc_ptr->reg[j+2], scc_ptr->reg[j+3]);\n\t\t}\n\t\tprintf(\"state: %d, sockfd:%llx rdwrfd:%llx, win_com:%p, \"\n\t\t\t\"win_dcb:%p\\n\", scc_ptr->cur_state,\n\t\t\t(dword64)scc_ptr->sockfd, (dword64)scc_ptr->rdwrfd,\n\t\t\tscc_ptr->win_com_handle, scc_ptr->win_dcb_ptr);\n\t\tprintf(\"in_rdptr: %04x, in_wr:%04x, out_rd:%04x, out_wr:%04x\\n\",\n\t\t\tscc_ptr->in_rdptr, scc_ptr->in_wrptr,\n\t\t\tscc_ptr->out_rdptr, scc_ptr->out_wrptr);\n\t\tprintf(\"rx_queue_depth: %d, queue: %02x, %02x, %02x, %02x\\n\",\n\t\t\tscc_ptr->rx_queue_depth, scc_ptr->rx_queue[0],\n\t\t\tscc_ptr->rx_queue[1], scc_ptr->rx_queue[2],\n\t\t\tscc_ptr->rx_queue[3]);\n\t\tprintf(\"want_ints: rx:%d, tx:%d, zc:%d\\n\",\n\t\t\tscc_ptr->wantint_rx, scc_ptr->wantint_tx,\n\t\t\tscc_ptr->wantint_zerocnt);\n\t\tprintf(\"ev_pendings: rx:%d, tx:%d, br:%d\\n\",\n\t\t\tscc_ptr->rx_event_pending,\n\t\t\tscc_ptr->tx_event_pending,\n\t\t\tscc_ptr->br_event_pending);\n\t\tprintf(\"br_dcycs: %f, tx_dcycs: %f, rx_dcycs: %f\\n\",\n\t\t\tscc_ptr->br_dcycs, scc_ptr->tx_dcycs,\n\t\t\tscc_ptr->rx_dcycs);\n\t\tprintf(\"char_size: %d, baud_rate: %d, mode: %d\\n\",\n\t\t\tscc_ptr->char_size, scc_ptr->baud_rate,\n\t\t\tscc_ptr->mode);\n\t\tprintf(\"modem_state: %dtelnet_mode:%d iac:%d, \"\n\t\t\t\"modem_cmd_len:%d\\n\", scc_ptr->modem_state,\n\t\t\tscc_ptr->telnet_mode, scc_ptr->telnet_iac,\n\t\t\tscc_ptr->modem_cmd_len);\n\t\tprintf(\"telnet_loc_modes:%08x %08x, telnet_rem_motes:\"\n\t\t\t\"%08x %08x\\n\", scc_ptr->telnet_local_mode[0],\n\t\t\tscc_ptr->telnet_local_mode[1],\n\t\t\tscc_ptr->telnet_remote_mode[0],\n\t\t\tscc_ptr->telnet_remote_mode[1]);\n\t\tprintf(\"modem_mode:%08x plus_mode:%d, out_char_dfcyc:%016llx\\n\",\n\t\t\tscc_ptr->modem_mode, scc_ptr->modem_plus_mode,\n\t\t\tscc_ptr->out_char_dfcyc);\n\t}\n\n}\n\nword32\nscc_read_reg(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tword32\tret;\n\tint\tregnum;\n\n\tscc_ptr = &(g_scc[port]);\n\tscc_ptr->mode = 0;\n\tregnum = scc_ptr->reg_ptr;\n\n\t/* port 0 is channel A, port 1 is channel B */\n\tswitch(regnum) {\n\tcase 0:\n\tcase 4:\n\t\tret = 0x60;\t/* 0x44 = no dcd, no cts,0x6c = dcd ok, cts ok*/\n\t\tif(scc_ptr->dcd) {\n\t\t\tret |= 0x08;\n\t\t}\n\t\t//ret |= 0x8;\t/* HACK HACK */\n\t\tif(scc_ptr->rx_queue_depth) {\n\t\t\tret |= 0x01;\n\t\t}\n\t\tif(scc_ptr->tx_buf_empty) {\n\t\t\tret |= 0x04;\n\t\t}\n\t\tif(scc_ptr->br_is_zero) {\n\t\t\tret |= 0x02;\n\t\t}\n\t\t//printf(\"Read scc[%d] stat: %016llx : %02x dcd:%d\\n\", port,\n\t\t//\tdfcyc, ret, scc_ptr->dcd);\n\t\tbreak;\n\tcase 1:\n\tcase 5:\n\t\t/* HACK: residue codes not right */\n\t\tret = 0x07;\t/* all sent */\n\t\tbreak;\n\tcase 2:\n\tcase 6:\n\t\tif(port == 0) {\n\t\t\tret = scc_ptr->reg[2];\n\t\t} else {\t\t\t// Port B, read RR2B int stat\n\t\t\t// The TELNET.SYSTEM by Colin Leroy-Mira uses RR2B\n\t\t\tret = scc_do_read_rr2b() << 1;\n\t\t\tif(g_scc[0].reg[9] & 0x10) {\t// wr9 status high\n\t\t\t\t// Map bit 3->4, 2->5, 1->6\n\t\t\t\tret = ((ret << 1) & 0x10) |\n\t\t\t\t\t((ret << 3) & 0x20) |\n\t\t\t\t\t((ret << 5) & 0x40);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase 3:\n\tcase 7:\n\t\tif(port == 0) {\n\t\t\tret = (g_irq_pending & 0x3f);\n\t\t} else {\n\t\t\tret = 0;\n\t\t}\n\t\tbreak;\n\tcase 8:\n\t\tret = scc_read_data(dfcyc, port);\n\t\tbreak;\n\tcase 9:\n\tcase 13:\n\t\tret = scc_ptr->reg[13];\n\t\tbreak;\n\tcase 10:\n\tcase 14:\n\t\tret = 0;\n\t\tbreak;\n\tcase 11:\n\tcase 15:\n\t\tret = scc_ptr->reg[15];\n\t\tbreak;\n\tcase 12:\n\t\tret = scc_ptr->reg[12];\n\t\tbreak;\n\tdefault:\n\t\thalt_printf(\"Tried reading c03%x with regnum: %d!\\n\", 8+port,\n\t\t\tregnum);\n\t\tret = 0;\n\t}\n\n\tscc_ptr->reg_ptr = 0;\n\tscc_printf(\"Read c03%x, rr%d, ret: %02x\\n\", 8+port, regnum, ret);\n\tdbg_log_info(dfcyc, 0, ret, (regnum << 20) | (0xc039 - port));\n\n\treturn ret;\n}\n\nvoid\nscc_write_reg(dword64 dfcyc, int port, word32 val)\n{\n\tScc\t*scc_ptr;\n\tword32\told_val, changed_bits, irq_mask;\n\tint\tregnum, mode, tmp1;\n\n\tscc_ptr = &(g_scc[port]);\n\tregnum = scc_ptr->reg_ptr & 0xf;\n\tmode = scc_ptr->mode;\n\n\tif(mode == 0) {\n\t\tif((val & 0xf0) == 0) {\n\t\t\t/* Set reg_ptr */\n\t\t\tscc_ptr->reg_ptr = val & 0xf;\n\t\t\tregnum = 0;\n\t\t\tscc_ptr->mode = 1;\n\t\t}\n\t} else {\n\t\tscc_ptr->reg_ptr = 0;\n\t\tscc_ptr->mode = 0;\n\t}\n\told_val = scc_ptr->reg[regnum];\n\tchanged_bits = (old_val ^ val) & 0xff;\n\n\tdbg_log_info(dfcyc, (mode << 16) | scc_ptr->reg_ptr,\n\t\t\t(changed_bits << 16) | val,\n\t\t\t(regnum << 20) | (0x1c039 - port));\n\n\t/* Set reg reg */\n\tswitch(regnum) {\n\tcase 0: /* wr0 */\n\t\ttmp1 = (val >> 3) & 0x7;\n\t\tswitch(tmp1) {\n\t\tcase 0x0:\n\t\tcase 0x1:\n\t\t\tbreak;\n\t\tcase 0x2:\t/* reset ext/status ints */\n\t\t\t/* should clear other ext ints */\n\t\t\tscc_clr_zerocnt_int(port);\n\t\t\tbreak;\n\t\tcase 0x5:\t/* reset tx int pending */\n\t\t\tscc_clr_tx_int(port);\n\t\t\tbreak;\n\t\tcase 0x6:\t/* reset rr1 bits */\n\t\t\tbreak;\n\t\tcase 0x7:\t/* reset highest pri int pending */\n\t\t\tirq_mask = g_irq_pending;\n\t\t\tif(port == 0) {\n\t\t\t\t/* Move SCC0 ints into SCC1 positions */\n\t\t\t\tirq_mask = irq_mask >> 3;\n\t\t\t}\n\t\t\tif(irq_mask & IRQ_PENDING_SCC1_RX) {\n\t\t\t\tscc_clr_rx_int(port);\n\t\t\t} else if(irq_mask & IRQ_PENDING_SCC1_TX) {\n\t\t\t\tscc_clr_tx_int(port);\n\t\t\t} else if(irq_mask & IRQ_PENDING_SCC1_ZEROCNT) {\n\t\t\t\tscc_clr_zerocnt_int(port);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x4:\t/* enable int on next rx char */\n\t\tdefault:\n\t\t\thalt_printf(\"Wr c03%x to wr0 of %02x, bad cmd cd:%x!\\n\",\n\t\t\t\t9-port, val, tmp1);\n\t\t}\n\t\ttmp1 = (val >> 6) & 0x3;\n\t\tswitch(tmp1) {\n\t\tcase 0x0:\t/* null code */\n\t\t\tbreak;\n\t\tcase 0x1:\t/* reset rx crc */\n\t\tcase 0x2:\t/* reset tx crc */\n\t\t\tprintf(\"Wr c03%x to wr0 of %02x!\\n\", 9-port, val);\n\t\t\tbreak;\n\t\tcase 0x3:\t/* reset tx underrun/eom latch */\n\t\t\t/* if no extern status pending, or being reset now */\n\t\t\t/*  and tx disabled, ext int with tx underrun */\n\t\t\t/* ah, just do nothing */\n\t\t\tbreak;\n\t\t}\n\t\treturn;\n\tcase 1: /* wr1 */\n\t\t/* proterm sets this == 0x10, which is int on all rx */\n\t\tscc_ptr->reg[regnum] = val;\n\t\treturn;\n\tcase 2: /* wr2 */\n\t\t/* All values do nothing, let 'em all through! */\n\t\tscc_ptr->reg[regnum] = val;\n\t\treturn;\n\tcase 3: /* wr3 */\n\t\tif((val & 0x1e) != 0x0) {\n\t\t\thalt_printf(\"Wr c03%x to wr3 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\treturn;\n\tcase 4: /* wr4 */\n\t\tif((val & 0x30) != 0x00 || (val & 0x0c) == 0) {\n\t\t\thalt_printf(\"Wr c03%x to wr4 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\tif(changed_bits) {\n\t\t\tscc_regen_clocks(port);\n\t\t}\n\t\treturn;\n\tcase 5: /* wr5 */\n\t\tif((val & 0x15) != 0x0) {\n\t\t\thalt_printf(\"Wr c03%x to wr5 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\tif(changed_bits & 0x60) {\n\t\t\tscc_regen_clocks(port);\n\t\t}\n\t\tif(changed_bits & 0x80) {\n\t\t\tscc_printf(\"SCC port %d DTR:%d\\n\", port, val & 0x80);\n\t\t}\n\t\treturn;\n\tcase 6: /* wr6 */\n\t\tif(val != 0) {\n\t\t\thalt_printf(\"Wr c03%x to wr6 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\treturn;\n\tcase 7: /* wr7 */\n\t\tif(val != 0) {\n\t\t\thalt_printf(\"Wr c03%x to wr7 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\treturn;\n\tcase 8: /* wr8 */\n\t\tscc_write_data(dfcyc, port, val);\n\t\treturn;\n\tcase 9: /* wr9 */\n\t\tif((val & 0xc0)) {\n\t\t\tif(val & 0x80) {\n\t\t\t\tscc_reset_port(0);\n\t\t\t}\n\t\t\tif(val & 0x40) {\n\t\t\t\tscc_reset_port(1);\n\t\t\t}\n\t\t\tif((val & 0xc0) == 0xc0) {\n\t\t\t\tscc_hard_reset_port(0);\n\t\t\t\tscc_hard_reset_port(1);\n\t\t\t}\n\t\t}\n\t\t// Bit 5 is software interrupt ack, which does not exist on NMOS\n\t\t// Bit 2 sets IEO pin low, which doesn't exist either\n\t\told_val = g_scc[0].reg[9];\n\t\tg_scc[0].reg[9] = val;\n\t\tscc_evaluate_ints(0);\n\t\tscc_evaluate_ints(1);\n\t\treturn;\n\tcase 10: /* wr10 */\n\t\tif((val & 0xff) != 0x00) {\n\t\t\tprintf(\"Wr c03%x to wr10 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\treturn;\n\tcase 11: /* wr11 */\n\t\tscc_ptr->reg[regnum] = val;\n\t\tif(changed_bits) {\n\t\t\tscc_regen_clocks(port);\n\t\t}\n\t\treturn;\n\tcase 12: /* wr12 */\n\t\tscc_ptr->reg[regnum] = val;\n\t\tif(changed_bits) {\n\t\t\tscc_regen_clocks(port);\n\t\t}\n\t\treturn;\n\tcase 13: /* wr13 */\n\t\tscc_ptr->reg[regnum] = val;\n\t\tif(changed_bits) {\n\t\t\tscc_regen_clocks(port);\n\t\t}\n\t\treturn;\n\tcase 14: /* wr14 */\n\t\tval = val | (old_val & (~0xffU));\n\t\tswitch((val >> 5) & 0x7) {\n\t\tcase 0x0:\t// Null command (change nothing)\n\t\t\tbreak;\n\t\tcase 0x1:\t// Enter search mode\n\t\tcase 0x2:\t// Reset missing clock\n\t\tcase 0x3:\t// Disable PLL\n\t\tcase 0x6:\t// Set FM mode\n\t\tcase 0x7:\t// Set NRZI mode\n\t\t\t// Disable the PLL effectively\n\t\t\tval = val & 0xff;\t// Clear all upper bits\n\t\t\tbreak;\n\t\tcase 0x4:\t// DPLL source is BR gen\n\t\t\tval = (val & 0xff) | SCC_R14_DPLL_SOURCE_BRG;\n\t\t\tbreak;\n\t\tcase 0x5:\t// DPLL source is RTxC\n\t\t\tval = (val & 0xff) | SCC_R14_DPLL_SOURCE_RTXC;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thalt_printf(\"Wr c03%x to wr14 of %02x, bad dpll cd!\\n\",\n\t\t\t\t8+port, val);\n\t\t}\n\t\tif((val & 0x0c) != 0x0) {\n\t\t\thalt_printf(\"Wr c03%x to wr14 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\tif(changed_bits || (val != old_val)) {\n\t\t\tscc_regen_clocks(port);\n\t\t}\n\t\tscc_maybe_br_event(dfcyc, port);\n\t\treturn;\n\tcase 15: /* wr15 */\n\t\t/* ignore all accesses since IIgs self test messes with it */\n\t\tif((val & 0xff) != 0x0) {\n\t\t\tscc_printf(\"Write c03%x to wr15 of %02x!\\n\", 8+port,\n\t\t\t\tval);\n\t\t}\n\t\tif((g_scc[0].reg[9] & 0x8) && (val != 0)) {\n\t\t\tprintf(\"Write wr15:%02x and master int en = 1!\\n\",val);\n\t\t\t/* set_halt(1); */\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\tscc_maybe_br_event(dfcyc, port);\n\t\tscc_evaluate_ints(port);\n\t\treturn;\n\tdefault:\n\t\thalt_printf(\"Wr c03%x to wr%d of %02x!\\n\", 8+port, regnum, val);\n\t\treturn;\n\t}\n}\n\n// scc_read_data: Read from 0xc03b or 0xc03a\nword32\nscc_read_data(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tword32\tret;\n\tint\tdepth;\n\tint\ti;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tscc_try_fill_readbuf(dfcyc, port);\n\n\tdepth = scc_ptr->rx_queue_depth;\n\n\tret = 0;\n\tif(depth != 0) {\n\t\tret = scc_ptr->rx_queue[0];\n\t\tfor(i = 1; i < depth; i++) {\n\t\t\tscc_ptr->rx_queue[i-1] = scc_ptr->rx_queue[i];\n\t\t}\n\t\tscc_ptr->rx_queue_depth = depth - 1;\n\t\tscc_maybe_rx_event(dfcyc, port);\n\t\tscc_maybe_rx_int(port);\n\t}\n\n\tscc_printf(\"SCC read %04x: ret %02x, depth:%d\\n\", 0xc03b-port, ret,\n\t\t\tdepth);\n\n\tdbg_log_info(dfcyc, 0, ret, 0xc03b - port);\n\n\treturn ret;\n}\n\nvoid\nscc_write_data(dword64 dfcyc, int port, word32 val)\n{\n\tScc\t*scc_ptr;\n\n\tscc_printf(\"SCC write %04x: %02x\\n\", 0xc03b-port, val);\n\tdbg_log_info(dfcyc, val, 0, 0x1c03b - port);\n\n\tscc_ptr = &(g_scc[port]);\n\tif(scc_ptr->reg[14] & 0x10) {\n\t\t/* local loopback! */\n\t\tscc_add_to_readbuf(dfcyc, port, val);\n\t} else {\n\t\tscc_transmit(dfcyc, port, val);\n\t}\n\tscc_try_to_empty_writebuf(dfcyc, port);\n\n\tscc_maybe_tx_event(dfcyc, port);\n}\n\nword32\nscc_do_read_rr2b()\n{\n\tword32\tval;\n\n\tval = g_irq_pending & 0x3f;\n\tif(val == 0) {\n\t\treturn 3;\t// 011 if no interrupts pending\n\t}\n\t// Do Channel A first.  Priority order from SCC documentation\n\tif(val & IRQ_PENDING_SCC0_RX) {\n\t\treturn 6;\t// 110 Ch A Rx char available\n\t}\n\tif(val & IRQ_PENDING_SCC0_TX) {\n\t\treturn 4;\t// 100 Ch A Tx buffer empty\n\t}\n\tif(val & IRQ_PENDING_SCC0_ZEROCNT) {\n\t\treturn 5;\t// 101 Ch A External/Status change\n\t}\n\tif(val & IRQ_PENDING_SCC1_RX) {\n\t\treturn 2;\t// 010 Ch B Rx char available\n\t}\n\tif(val & IRQ_PENDING_SCC1_TX) {\n\t\treturn 0;\t// 000 Ch B Tx buffer empty\n\t}\n\tif(val & IRQ_PENDING_SCC1_ZEROCNT) {\n\t\treturn 1;\t// 001 Ch B External/Status change\n\t}\n\n\treturn 3;\n}\n\nvoid\nscc_maybe_br_event(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tdouble\tbr_dcycs;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(((scc_ptr->reg[14] & 0x01) == 0) || scc_ptr->br_event_pending) {\n\t\treturn;\n\t}\n\t/* also, if ext ints not enabled, don't do baud rate ints */\n\tif(((scc_ptr->reg[15] & 0x02) == 0) || ((scc_ptr->reg[9] & 8) == 0)) {\n\t\treturn;\n\t}\n\n\tbr_dcycs = scc_ptr->br_dcycs;\n\tif(br_dcycs < 1.0) {\n\t\thalt_printf(\"br_dcycs: %f!\\n\", br_dcycs);\n#if 0\n\t\tprintf(\"br_dcycs: %f!\\n\", br_dcycs);\n\t\tdbg_log_info(dfcyc, (word32)(br_dcycs * 65536),\n\t\t\t(scc_ptr->reg[15] << 24) | (scc_ptr->reg[14] << 16) |\n\t\t\t(scc_ptr->reg[13] << 8) | scc_ptr->reg[12], 0xdc1c);\n\t\tdbg_log_info(dfcyc,\n\t\t\t(scc_ptr->reg[11] << 24) | (scc_ptr->reg[10] << 16) |\n\t\t\t(scc_ptr->reg[9] << 8) | scc_ptr->reg[5],\n\t\t\t(scc_ptr->reg[4] << 24) | (scc_ptr->reg[3] << 16) |\n\t\t\t(scc_ptr->reg[1] << 8) | scc_ptr->reg[0], 0xdc1b);\n#endif\n\t}\n\n\tscc_ptr->br_event_pending = 1;\n\tadd_event_scc(dfcyc + (dword64)(br_dcycs * 65536.0),\n\t\t\t\t\tSCC_MAKE_EVENT(port, SCC_BR_EVENT));\n}\n\nvoid\nscc_evaluate_ints(int port)\n{\n\tScc\t*scc_ptr;\n\tword32\tirq_add_mask, irq_remove_mask;\n\tint\tmie;\n\n\tscc_ptr = &(g_scc[port]);\n\tmie = g_scc[0].reg[9] & 0x8;\t\t\t/* Master int en */\n\n\tif(!mie) {\n\t\t/* There can be no interrupts if MIE=0 */\n\t\tremove_irq(IRQ_PENDING_SCC1_RX | IRQ_PENDING_SCC1_TX |\n\t\t\t\t\t\tIRQ_PENDING_SCC1_ZEROCNT |\n\t\t\tIRQ_PENDING_SCC0_RX | IRQ_PENDING_SCC0_TX |\n\t\t\t\t\t\tIRQ_PENDING_SCC0_ZEROCNT);\n\t\treturn;\n\t}\n\n\tirq_add_mask = 0;\n\tirq_remove_mask = 0;\n\tif(scc_ptr->wantint_rx) {\n\t\tirq_add_mask |= IRQ_PENDING_SCC1_RX;\n\t} else {\n\t\tirq_remove_mask |= IRQ_PENDING_SCC1_RX;\n\t}\n\tif(scc_ptr->wantint_tx) {\n\t\tirq_add_mask |= IRQ_PENDING_SCC1_TX;\n\t} else {\n\t\tirq_remove_mask |= IRQ_PENDING_SCC1_TX;\n\t}\n\tif(scc_ptr->wantint_zerocnt) {\n\t\tirq_add_mask |= IRQ_PENDING_SCC1_ZEROCNT;\n\t} else {\n\t\tirq_remove_mask |= IRQ_PENDING_SCC1_ZEROCNT;\n\t}\n\tif(port == 0) {\n\t\t/* Port 1 is in bits 0-2 and port 0 is in bits 3-5 */\n\t\tirq_add_mask = irq_add_mask << 3;\n\t\tirq_remove_mask = irq_remove_mask << 3;\n\t}\n\tif(irq_add_mask) {\n\t\tadd_irq(irq_add_mask);\n\t}\n\tif(irq_remove_mask) {\n\t\tremove_irq(irq_remove_mask);\n\t}\n}\n\nvoid\nscc_maybe_rx_event(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tdouble\trx_dcycs;\n\tint\tin_rdptr, in_wrptr, depth;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->rx_event_pending) {\n\t\t/* one pending already, wait for the event to arrive */\n\t\treturn;\n\t}\n\n\tin_rdptr = scc_ptr->in_rdptr;\n\tin_wrptr = scc_ptr->in_wrptr;\n\tdepth = scc_ptr->rx_queue_depth;\n\tif((in_rdptr == in_wrptr) || (depth >= 3)) {\n\t\t/* no more chars or no more space, just get out */\n\t\treturn;\n\t}\n\n\tif(depth < 0) {\n\t\tdepth = 0;\n\t}\n\n\t/* pull char from in_rdptr into queue */\n\tscc_ptr->rx_queue[depth] = scc_ptr->in_buf[in_rdptr];\n\tscc_ptr->in_rdptr = (in_rdptr + 1) & (SCC_INBUF_SIZE - 1);\n\tscc_ptr->rx_queue_depth = depth + 1;\n\tscc_maybe_rx_int(port);\n\trx_dcycs = scc_ptr->rx_dcycs;\n\tscc_ptr->rx_event_pending = 1;\n\tadd_event_scc(dfcyc + (dword64)(rx_dcycs*65536.0),\n\t\t\t\t\tSCC_MAKE_EVENT(port, SCC_RX_EVENT));\n}\n\nvoid\nscc_maybe_rx_int(int port)\n{\n\tScc\t*scc_ptr;\n\tint\tdepth;\n\tint\trx_int_mode;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tdepth = scc_ptr->rx_queue_depth;\n\tif(depth <= 0) {\n\t\t/* no more chars, just get out */\n\t\tscc_clr_rx_int(port);\n\t\treturn;\n\t}\n\trx_int_mode = (scc_ptr->reg[1] >> 3) & 0x3;\n\tif((rx_int_mode == 1) || (rx_int_mode == 2)) {\n\t\tscc_ptr->wantint_rx = 1;\n\t}\n\tscc_evaluate_ints(port);\n}\n\nvoid\nscc_clr_rx_int(int port)\n{\n\tg_scc[port].wantint_rx = 0;\n\tscc_evaluate_ints(port);\n}\n\nvoid\nscc_handle_tx_event(int port)\n{\n\tScc\t*scc_ptr;\n\tint\ttx_int_mode;\n\n\tscc_ptr = &(g_scc[port]);\n\n\t/* nothing pending, see if ints on */\n\ttx_int_mode = (scc_ptr->reg[1] & 0x2);\n\tif(tx_int_mode) {\n\t\tscc_ptr->wantint_tx = 1;\n\t}\n\tscc_evaluate_ints(port);\n}\n\nvoid\nscc_maybe_tx_event(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tdouble\ttx_dcycs;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->tx_event_pending) {\n\t\t/* one pending already, tx_buf is full */\n\t\tscc_ptr->tx_buf_empty = 0;\n\t} else {\n\t\t/* nothing pending, see ints on */\n\t\tscc_ptr->tx_buf_empty = 0;\n\t\tscc_evaluate_ints(port);\n\t\ttx_dcycs = scc_ptr->tx_dcycs;\n\t\tscc_ptr->tx_event_pending = 1;\n\t\tadd_event_scc(dfcyc + (dword64)(tx_dcycs * 65536.0),\n\t\t\t\tSCC_MAKE_EVENT(port, SCC_TX_EVENT));\n\t}\n}\n\nvoid\nscc_clr_tx_int(int port)\n{\n\tg_scc[port].wantint_tx = 0;\n\tscc_evaluate_ints(port);\n}\n\nvoid\nscc_set_zerocnt_int(int port)\n{\n\tScc\t*scc_ptr;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->reg[15] & 0x2) {\n\t\tscc_ptr->wantint_zerocnt = 1;\n\t}\n\tscc_evaluate_ints(port);\n}\n\nvoid\nscc_clr_zerocnt_int(int port)\n{\n\tg_scc[port].wantint_zerocnt = 0;\n\tscc_evaluate_ints(port);\n}\n\nvoid\nscc_add_to_readbuf(dword64 dfcyc, int port, word32 val)\n{\n\tScc\t*scc_ptr;\n\tint\tin_wrptr, in_wrptr_next, in_rdptr, safe_val;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif((scc_ptr->reg[5] & 0x60) != 0x60) {\t// HACK: this is tx char size!\n\t\tval = val & 0x7f;\n\t}\n\tin_wrptr = scc_ptr->in_wrptr;\n\tin_rdptr = scc_ptr->in_rdptr;\n\tin_wrptr_next = (in_wrptr + 1) & (SCC_INBUF_SIZE - 1);\n\tif(in_wrptr_next != in_rdptr) {\n\t\tscc_ptr->in_buf[in_wrptr] = val;\n\t\tscc_ptr->in_wrptr = in_wrptr_next;\n\t\tsafe_val = val & 0x7f;\n\t\tif((safe_val < 0x20) || (safe_val >= 0x7f)) {\n\t\t\tsafe_val = '.';\n\t\t}\n\t\tscc_printf(\"scc in port[%d] add 0x%02x (%c), %d,%d != %d\\n\",\n\t\t\tport, val, safe_val, in_wrptr, in_wrptr_next, in_rdptr);\n\t\tg_scc_overflow = 0;\n\t} else {\n\t\tif(g_scc_overflow == 0) {\n\t\t\tg_code_yellow++;\n\t\t\tprintf(\"scc inbuf overflow port %d\\n\", port);\n\t\t}\n\t\tg_scc_overflow = 1;\n\t}\n\n\tscc_maybe_rx_event(dfcyc, port);\n\tscc_maybe_rx_int(port);\n}\n\nvoid\nscc_add_to_readbufv(dword64 dfcyc, int port, const char *fmt, ...)\n{\n\tva_list\tap;\n\tchar\t*bufptr;\n\tint\tlen, c;\n\tint\ti;\n\n\tva_start(ap, fmt);\n\tbufptr = malloc(4096);\n\tbufptr[0] = 0;\n\tvsnprintf(bufptr, 4090, fmt, ap);\n\tlen = (int)strlen(bufptr);\n\tfor(i = 0; i < len; i++) {\n\t\tc = bufptr[i];\n\t\tif(c == 0x0a) {\n\t\t\tscc_add_to_readbuf(dfcyc, port, 0x0d);\n\t\t}\n\t\tscc_add_to_readbuf(dfcyc, port, c);\n\t}\n\tva_end(ap);\n}\n\nvoid\nscc_transmit(dword64 dfcyc, int port, word32 val)\n{\n\tScc\t*scc_ptr;\n\tint\tout_wrptr, out_rdptr;\n\n\tscc_ptr = &(g_scc[port]);\n\n\t// printf(\"scc_transmit port:%d val:%02x \\n\", port, val);\n\t/* See if port initialized, if not, do so now */\n\tif(scc_is_port_closed(dfcyc, port)) {\n\t\tprintf(\"  port %d is closed, cur_state:%d\\n\", port,\n\t\t\t\tscc_ptr->cur_state);\n\t\treturn;\t\t// No working serial port, just toss it and go\n\t}\n\tif(!scc_ptr->tx_buf_empty) {\n\t\t/* toss character! */\n\t\tprintf(\"Tossing char\\n\");\n\t\treturn;\n\t}\n\n\tout_wrptr = scc_ptr->out_wrptr;\n\tout_rdptr = scc_ptr->out_rdptr;\n\tif(scc_ptr->tx_dcycs < 1.0) {\n\t\tif(out_wrptr != out_rdptr) {\n\t\t\t/* do just one char, then get out */\n\t\t\tprintf(\"tx_dcycs < 1\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\tif(g_serial_mask[port] || (scc_ptr->reg[5] & 0x60) != 0x60) {\n\t\tval = val & 0x7f;\n\t}\n\n\tscc_add_to_writebuf(dfcyc, port, val);\n}\n\nvoid\nscc_add_to_writebuf(dword64 dfcyc, int port, word32 val)\n{\n\tScc\t*scc_ptr;\n\tint\tout_wrptr, out_wrptr_next, out_rdptr;\n\n\t// printf(\"scc_add_to_writebuf p:%d, val:%02x\\n\", port, val);\n\tif(scc_is_port_closed(dfcyc, port)) {\n\t\treturn;\t\t\t// Port is closed\n\t}\n\tscc_ptr = &(g_scc[port]);\n\n\tout_wrptr = scc_ptr->out_wrptr;\n\tout_rdptr = scc_ptr->out_rdptr;\n\n\tout_wrptr_next = (out_wrptr + 1) & (SCC_OUTBUF_SIZE - 1);\n\tif(out_wrptr_next != out_rdptr) {\n\t\tscc_ptr->out_buf[out_wrptr] = val;\n\t\tscc_ptr->out_wrptr = out_wrptr_next;\n\t\tscc_printf(\"scc wrbuf port %d had char 0x%02x added\\n\",\n\t\t\tport, val);\n\t\tg_scc_overflow = 0;\n\t} else {\n\t\tif(g_scc_overflow == 0) {\n\t\t\tg_code_yellow++;\n\t\t\tprintf(\"scc outbuf overflow port %d\\n\", port);\n\t\t}\n\t\tg_scc_overflow = 1;\n\t}\n}\n\n"
  },
  {
    "path": "gsplus/src/scc.h",
    "content": "#ifdef INCLUDE_RCSID_C\n#endif\n\n/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2025 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include <ctype.h>\n\n#ifdef _WIN32\n# include <winsock2.h>\n#else\n# include <sys/socket.h>\n# include <netinet/in.h>\n# include <netdb.h>\n#endif\n\n#if defined(HPUX) || defined(__linux__) || defined(SOLARIS)\n# define SCC_SOCKETS\n#endif\n#if defined(MAC) || defined(__MACH__) || defined(_WIN32)\n# define SCC_SOCKETS\n#endif\n\n\n/* my scc port 0 == channel A, port 1 = channel B */\n\n#define\tSCC_INBUF_SIZE\t\t512\t\t/* must be a power of 2 */\n#define\tSCC_OUTBUF_SIZE\t\t512\t\t/* must be a power of 2 */\n\n#define SCC_MODEM_MAX_CMD_STR\t128\n\n#ifndef _WIN32\n# define SOCKET\t\t\tint\t\t/* For non-Windows */\n# define INVALID_SOCKET\t\t(-1)\t\t/* For non-Windows */\n#endif\n\nSTRUCT(Scc) {\n\tint\tcur_state;\n\tint\tmodem_state;\n\tSOCKET\tsockfd;\n\tSOCKET\trdwrfd;\n\tvoid\t*sockaddr_ptr;\t\t// Socket: pointer to sockaddr struct\n\tint\tsockaddr_size;\t\t// Socket: sizeof(sockaddr_in)\n\tint\tunix_dev_fd;\t\t// Unix fd to real serial device\n\tvoid\t*win_com_handle;\t// Win32 handle to COMx port\n\tvoid\t*win_dcb_ptr;\t\t// Win32 ptr to COMx DCB\n\tint\tread_called_this_vbl;\n\tint\twrite_called_this_vbl;\n\n\tbyte\tdcd;\n\tbyte\treg_ptr;\n\tbyte\tbr_is_zero;\n\tbyte\ttx_buf_empty;\n\tword32\tmode;\n\tbyte\treg[16];\n\n\tint\trx_queue_depth;\n\tbyte\trx_queue[4];\n\n\tint\tin_rdptr;\n\tint\tin_wrptr;\n\tbyte\tin_buf[SCC_INBUF_SIZE];\n\n\tint\tout_rdptr;\n\tint\tout_wrptr;\n\tbyte\tout_buf[SCC_OUTBUF_SIZE];\n\n\tint\twantint_rx;\n\tint\twantint_tx;\n\tint\twantint_zerocnt;\n\n\tdouble\tbr_dcycs;\n\tdouble\ttx_dcycs;\n\tdouble\trx_dcycs;\n\n\tint\tbr_event_pending;\n\tint\trx_event_pending;\n\tint\ttx_event_pending;\n\n\tint\tchar_size;\n\tint\tbaud_rate;\n\tdword64\tout_char_dfcyc;\n\n\tint\tsocket_error;\n\tint\tsocket_num_rings;\n\tdword64\tsocket_last_ring_dfcyc;\n\tword32\tmodem_mode;\n\tint\tmodem_plus_mode;\n\tint\tmodem_s0_val;\n\tint\tmodem_s2_val;\n\tint\ttelnet_mode;\n\tint\ttelnet_iac;\n\tword32\ttelnet_local_mode[2];\n\tword32\ttelnet_remote_mode[2];\n\tword32\ttelnet_reqwill_mode[2];\n\tword32\ttelnet_reqdo_mode[2];\n\tword32\tmodem_out_portnum;\n\tint\tmodem_cmd_len;\n\tbyte\tmodem_cmd_str[SCC_MODEM_MAX_CMD_STR + 5];\n};\n\n#define SCCMODEM_NOECHO\t\t0x0001\n#define SCCMODEM_NOVERBOSE\t0x0002\n\n"
  },
  {
    "path": "gsplus/src/scc_socket_driver.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2025 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// This file contains the socket calls for Win32 and Mac/Linux\n// Win32: see: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/\n\n// Modem init string GBBS GSPORT.HST: ats0=1s2=128v0\n// Modem init string Warp6: atm0e0v0s0=0s7=25   atx4h0  at&c1&d2\n\n#include \"defc.h\"\n\n#include \"scc.h\"\n#include <signal.h>\n\nextern int Verbose;\nextern Scc g_scc[2];\nextern int g_serial_cfg[2];\nextern char *g_serial_remote_ip[2];\nextern int g_serial_remote_port[2];\nextern int g_serial_modem_response_code;\nextern int g_serial_modem_allow_incoming;\nextern int g_serial_modem_init_telnet;\n\n#ifdef _WIN32\n#include <winsock2.h>\ntypedef int socklen_t;\n#endif\n\n#ifndef _WIN32\nextern int h_errno;\n#endif\n\nint g_wsastartup_called = 0;\n\n#ifndef _WIN32\n// SOCKET is defined in scc.h, and is an \"int\" for non-Windows systems\n// INVALID_SOCKET is -1 for non-WINDOWS\n# define closesocket(s)\t\tclose(s)\n#endif\n\n/* Usage: scc_socket_open() called to init socket mode */\n/*  At all times, we try to have a listen running on the incoming socket */\n/* If we want to dial out, we close the incoming socket and create a new */\n/*  outgoing socket.  Any hang-up causes the socket to be closed and it will */\n/*  then re-open on a subsequent call to scc_socket_open */\n\nvoid\nscc_socket_open(dword64 dfcyc, int port, int cfg)\n{\n\tScc\t*scc_ptr;\n\n\t// printf(\"scc_socket_open p:%d cfg:%d\\n\", port, cfg);\n#if defined(_WIN32) && defined(SCC_SOCKETS)\n\tWSADATA\twsadata;\n\tint\tret;\n\n\tif(g_wsastartup_called == 0) {\n\t\tret = WSAStartup(MAKEWORD(2,0), &wsadata);\n\t\tprintf(\"WSAStartup ret: %d\\n\", ret);\n\t\tg_wsastartup_called = 1;\n\t}\n#endif\n\tscc_ptr = &(g_scc[port]);\n\tscc_ptr->cur_state = cfg;\t\t// successful socket\n\tscc_ptr->sockfd = INVALID_SOCKET; /* Indicate no socket open yet */\n\tscc_ptr->rdwrfd = INVALID_SOCKET; /* Indicate no socket open yet */\n\tscc_ptr->modem_state = 0;\t/* 0 means talk to socket */\n\t\t\t\t\t/* 1 means talk to modem */\n\tscc_ptr->socket_error = 0;\n\tscc_ptr->socket_num_rings = 0;\n\tscc_ptr->socket_last_ring_dfcyc = 0;\n\tscc_ptr->dcd = 1;\t\t/* 1 means carrier */\n\tscc_ptr->modem_s0_val = 0;\t\t// Number of rings before answer\n\tscc_ptr->sockaddr_size = sizeof(struct sockaddr_in);\n\tfree(scc_ptr->sockaddr_ptr);\n\tscc_ptr->sockaddr_ptr = malloc(scc_ptr->sockaddr_size);\n\n\t// cfg==1: Virtual Modem.  All set up, wait for AT commands now\n\t// cfg==2: Outgoing connection to g_serial_remote_ip[], do it now\n\t// cfg==3: Incoming connection on 6501/6502.  Do it now\n\tif(cfg == 2) {\n\t\tscc_ptr->modem_state = 0;\n\t\t// Do not open it now, it will be opened when output is\n\t\t//  sent to the port\n\t}\n\tif(cfg == 1) {\n\t\tscc_ptr->modem_state = 1;\n\t\tscc_ptr->dcd = 0;\n\t\tscc_socket_open_incoming(dfcyc, port);\n\t} else if(cfg == 3) {\n\t\tscc_ptr->modem_state = 0;\n\t\tscc_socket_open_incoming(dfcyc, port);\n\t}\n}\n\nvoid\nscc_socket_close(int port)\n{\n#ifdef SCC_SOCKETS\n\tScc\t*scc_ptr;\n\tSOCKET\trdwrfd;\n\tSOCKET\tsockfd;\n\tint\tdo_init;\n\tint\ti;\n\n\tscc_ptr = &(g_scc[port]);\n\n\t// printf(\"In scc_socket_close, %d\\n\", port);\n\n\trdwrfd = scc_ptr->rdwrfd;\n\tdo_init = 0;\n\tif(rdwrfd != INVALID_SOCKET) {\n\t\tprintf(\"socket_close: rdwrfd=%llx, closing\\n\", (dword64)rdwrfd);\n\t\tclosesocket(rdwrfd);\n\t\tdo_init = 1;\n\t}\n\tsockfd = scc_ptr->sockfd;\n\tif((sockfd != INVALID_SOCKET) && (rdwrfd != sockfd)) {\n\t\tprintf(\"socket_close: sockfd=%llx, closing\\n\", (dword64)sockfd);\n\t\tclosesocket(sockfd);\n\t\tdo_init = 1;\n\t}\n\n\tscc_ptr->modem_state = 0;\n\tscc_ptr->socket_num_rings = 0;\n\tif(scc_ptr->cur_state == 1) {\t\t\t// Virtual modem\n\t\tscc_ptr->modem_state = 1;\n\t\tscc_ptr->dcd = 0;\n\t}\n\tif(do_init) {\n\t\tscc_ptr->modem_cmd_len = 0;\n\t\tscc_ptr->telnet_iac = 0;\n\t\tfor(i = 0; i < 2; i++) {\n\t\t\tscc_ptr->telnet_local_mode[i] = 0;\n\t\t\tscc_ptr->telnet_remote_mode[i] = 0;\n\t\t\tscc_ptr->telnet_reqwill_mode[i] = 0;\n\t\t\tscc_ptr->telnet_reqdo_mode[i] = 0;\n\t\t}\n\t\tscc_ptr->rdwrfd = INVALID_SOCKET;\n\t\tscc_ptr->sockfd = INVALID_SOCKET;\n\t}\n#endif\n}\n\nvoid\nscc_socket_close_extended(dword64 dfcyc, int port, int allow_retry)\n{\n#ifdef SCC_SOCKETS\n\tScc\t*scc_ptr;\n\n\t// printf(\"In scc_socket_close_extended, %d, %016llx\\n\", port, dfcyc);\n\tscc_socket_close(port);\n\n\tscc_ptr = &(g_scc[port]);\n\tif(scc_ptr->cur_state == 1) {\t\t// Virtual modem mode\n\t\tscc_socket_send_modem_code(dfcyc, port, 3);\n\t\tscc_ptr->modem_state = 1;\t// and go back to modem mode\n\t} else if((scc_ptr->cur_state == 2) && !allow_retry) {\t// Remote IP\n\t\tscc_ptr->cur_state = -2;\t// Error, give up\n\t}\n#endif\n}\n\nvoid\nscc_socket_maybe_open(dword64 dfcyc, int port, int must)\n{\n\tScc\t*scc_ptr;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->sockfd != INVALID_SOCKET) {\n\t\t// Valid socket.  See if we should hang up\n\t\tif((scc_ptr->reg[5] & 0x80) && (scc_ptr->cur_state == 1)) {\n\t\t\t// Handle DTR forcing modem hang-up now\n\t\t\tprintf(\"DTR is deasserted, hanging up\\n\");\n\t\t\tscc_socket_close(port);\n\t\t}\n\t\treturn;\n\t}\n\tif(scc_ptr->cur_state == 2) {\n\t\tif(must) {\n\t\t\tscc_socket_open_outgoing(dfcyc, port,\n\t\t\t\t\tg_serial_remote_ip[port],\n\t\t\t\t\tg_serial_remote_port[port]);\n\t\t}\n\t} else {\n\t\tscc_socket_open_incoming(dfcyc, port);\n\t}\n}\n\nvoid\nscc_socket_open_incoming(dword64 dfcyc, int port)\n{\n#ifdef SCC_SOCKETS\n\tSOCKET\tsockfd;\n\tstruct sockaddr_in sa_in;\n\tScc\t*scc_ptr;\n\tint\ton, ret, inc;\n\n\tinc = 0;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->sockfd != INVALID_SOCKET) {\n\t\t/* it's already open, get out */\n\t\treturn;\n\t}\n\n\t//printf(\"scc_socket_open_incoming: scc socket close being called\\n\");\n\tscc_socket_close(port);\n\n\tscc_ptr->socket_num_rings = 0;\n\n\tmemset(scc_ptr->sockaddr_ptr, 0, scc_ptr->sockaddr_size);\n\n\tif(scc_ptr->cur_state == 2) {\n\t\t// Outgoing connection only, never accept an incoming connect\n\t\treturn;\n\t}\n\tif(scc_ptr->cur_state == 1) {\n\t\tif(!g_serial_modem_allow_incoming) {\n\t\t\t// Virtual modem with incoming connections disallowed\n\t\t\treturn;\n\t\t}\n\t\tif(scc_ptr->reg[5] & 0x80) {\n\t\t\t// DTR is forcing hang-up.  Don't open incoming socket\n\t\t\treturn;\n\t\t}\n\t}\n\n\twhile(1) {\n\t\tsockfd = socket(AF_INET, SOCK_STREAM, 0);\n\t\t//printf(\"sockfd ret: %llx\\n\", (dword64)sockfd);\n\t\tif(sockfd == INVALID_SOCKET) {\n\t\t\tprintf(\"socket ret: -1, errno: %d\\n\", errno);\n\t\t\tscc_socket_close(port);\n\t\t\tscc_ptr->socket_error = -1;\n\t\t\treturn;\n\t\t}\n\t\t/* printf(\"socket ret: %d\\n\", sockfd); */\n\n\t\ton = 1;\n\t\tret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,\n\t\t\t\t\t(char *)&on, sizeof(on));\n\t\tif(ret < 0) {\n\t\t\tprintf(\"setsockopt REUSEADDR ret: %d, err:%d\\n\",\n\t\t\t\tret, errno);\n\t\t\tscc_socket_close(port);\n\t\t\treturn;\n\t\t}\n\n\t\tmemset(&sa_in, 0, sizeof(sa_in));\n\t\tsa_in.sin_family = AF_INET;\n\t\tsa_in.sin_port = htons(6501 + port + inc);\n\t\tsa_in.sin_addr.s_addr = htonl(INADDR_ANY);\n\n\t\tret = bind(sockfd, (struct sockaddr *)&sa_in, sizeof(sa_in));\n\t\tprintf(\"bind ret:%d\\n\", ret);\n\n\t\tif(ret >= 0) {\n\t\t\tret = listen(sockfd, 1);\n\t\t\tbreak;\n\t\t}\n\t\t/* else ret to bind was < 0 */\n\t\tprintf(\"bind or listen ret: %d, errno: %d\\n\", ret, errno);\n\t\tinc++;\n\t\tclosesocket(sockfd);\n\t\tprintf(\"Trying next port: %d\\n\", 6501 + port + inc);\n\t\tif(inc >= 10) {\n\t\t\tprintf(\"Too many retries, quitting\\n\");\n\t\t\tscc_socket_close(port);\n\t\t\tscc_ptr->socket_error = -1;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprintf(\"SCC port %d is at unix port %d\\n\", port, 6501 + port + inc);\n\n\tscc_ptr->sockfd = sockfd;\n\tscc_ptr->socket_error = 0;\n\n\tscc_socket_make_nonblock(dfcyc, port);\n#endif\n}\n\nvoid\nscc_socket_open_outgoing(dword64 dfcyc, int port, const char *remote_ip_str,\n\t\t\t\t\t\tint remote_port)\n{\n#ifdef SCC_SOCKETS\n\tScc\t*scc_ptr;\n\tstruct sockaddr_in sa_in;\n\tstruct hostent *hostentptr;\n\tint\ton;\n\tint\tret;\n\tSOCKET\tsockfd;\n\n\tscc_ptr = &(g_scc[port]);\n\n\t// printf(\"scc socket close being called from socket_open_out\\n\");\n\tscc_socket_close(port);\n\n\tmemset(scc_ptr->sockaddr_ptr, 0, scc_ptr->sockaddr_size);\n\n\tsockfd = socket(AF_INET, SOCK_STREAM, 0);\n\t// printf(\"outgoing sockfd ret: %llx\\n\", (dword64)sockfd);\n\tif(sockfd == INVALID_SOCKET) {\n\t\tprintf(\"socket ret: %llx, errno: %d\\n\", (dword64)sockfd, errno);\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\treturn;\n\t}\n\t/* printf(\"socket ret: %d\\n\", sockfd); */\n\n\ton = 1;\n\tret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,\n\t\t\t\t\t(char *)&on, sizeof(on));\n\tif(ret < 0) {\n\t\tprintf(\"setsockopt REUSEADDR ret: %d, err:%d\\n\",\n\t\t\tret, errno);\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\treturn;\n\t}\n\n\tmemset(&sa_in, 0, sizeof(sa_in));\n\tsa_in.sin_family = AF_INET;\n\tsa_in.sin_port = htons(remote_port);\n\twhile(*remote_ip_str == ' ') {\n\t\tremote_ip_str++;\t\t// Skip leading blanks\n\t}\n\thostentptr = gethostbyname(remote_ip_str);\n\tprintf(\"Connecting to %s, port:%d\\n\", remote_ip_str, remote_port);\n\tif(hostentptr == 0) {\n#ifdef _WIN32\n\t\tfatal_printf(\"Lookup host %s failed\\n\",\n\t\t\t\t\t\t&scc_ptr->modem_cmd_str[0]);\n#else\n\t\tfatal_printf(\"Lookup host %s failed, herrno: %d\\n\",\n\t\t\t\t\t&scc_ptr->modem_cmd_str[0], h_errno);\n#endif\n\t\tclosesocket(sockfd);\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\treturn;\n\t}\n\tmemcpy(&sa_in.sin_addr.s_addr, hostentptr->h_addr,\n\t\t\t\t\t\t\thostentptr->h_length);\n\t/* The above copies the 32-bit internet address into */\n\t/*  sin_addr.s_addr.  It's in correct network format */\n\n\tret = connect(sockfd, (struct sockaddr *)&sa_in, sizeof(sa_in));\n\tif(ret < 0) {\n\t\tprintf(\"connect ret: %d, errno: %d\\n\", ret, errno);\n\t\tclosesocket(sockfd);\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\treturn;\n\t}\n\tscc_socket_modem_connect(dfcyc, port);\n\tscc_ptr->dcd = 1;\t\t/* carrier on */\n\tscc_ptr->modem_state = 0;\t/* talk to socket */\n\tscc_ptr->socket_num_rings = 0;\n\n\tprintf(\"SCC port %d is now outgoing to %s:%d\\n\", port, remote_ip_str,\n\t\t\t\t\t\t\tremote_port);\n\n\tscc_ptr->sockfd = sockfd;\n\n\tscc_socket_make_nonblock(dfcyc, port);\n\tscc_ptr->rdwrfd = scc_ptr->sockfd;\n#endif\n}\n\nvoid\nscc_socket_make_nonblock(dword64 dfcyc, int port)\n{\n#ifdef SCC_SOCKETS\n\tScc\t*scc_ptr;\n\tSOCKET\tsockfd;\n\tint\tret;\n# ifdef _WIN32\n\tu_long\tflags;\n# else\n\tint\tflags;\n# endif\n\n\tscc_ptr = &(g_scc[port]);\n\tsockfd = scc_ptr->sockfd;\n\n# ifdef _WIN32\n\tflags = 1;\n\tret = ioctlsocket(sockfd, FIONBIO, &flags);\n\tif(ret != 0) {\n\t\tprintf(\"ioctlsocket ret: %d\\n\", ret);\n\t}\n# else\n\tflags = fcntl(sockfd, F_GETFL, 0);\n\tif(flags == -1) {\n\t\tprintf(\"fcntl GETFL ret: %d, errno: %d\\n\", flags, errno);\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\treturn;\n\t}\n\tret = fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);\n\tif(ret == -1) {\n\t\tprintf(\"fcntl SETFL ret: %d, errno: %d\\n\", ret, errno);\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\treturn;\n\t}\n# endif\n#endif\n}\n\nvoid\nscc_accept_socket(dword64 dfcyc, int port)\n{\n#ifdef SCC_SOCKETS\n\tScc\t*scc_ptr;\n\tSOCKET\trdwrfd;\n\tsocklen_t address_len;\n\tint\tflags, num_rings, ret;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->sockfd == INVALID_SOCKET) {\n\t\t// printf(\"in accept_socket, call socket_open\\n\");\n\t\tscc_socket_open_incoming(dfcyc, port);\n\t}\n\tif(scc_ptr->sockfd == INVALID_SOCKET) {\n\t\treturn;\t\t/* just give up */\n\t}\n\tif(scc_ptr->rdwrfd == INVALID_SOCKET) {\n\t\taddress_len = scc_ptr->sockaddr_size;\n\t\trdwrfd = accept(scc_ptr->sockfd, scc_ptr->sockaddr_ptr,\n\t\t\t\t\t\t\t\t&address_len);\n\t\tscc_ptr->sockaddr_size = (int)address_len;\n\t\tif(rdwrfd == INVALID_SOCKET) {\n\t\t\treturn;\n\t\t}\n\n\t\tflags = 0;\n\t\tret = 0;\n#ifndef _WIN32\n\t\t/* For Linux, we need to set O_NONBLOCK on the rdwrfd */\n\t\tflags = fcntl(rdwrfd, F_GETFL, 0);\n\t\tif(flags == -1) {\n\t\t\tprintf(\"fcntl GETFL ret: %d, errno: %d\\n\", flags,errno);\n\t\t\treturn;\n\t\t}\n\t\tret = fcntl(rdwrfd, F_SETFL, flags | O_NONBLOCK);\n\t\tif(ret == -1) {\n\t\t\tprintf(\"fcntl SETFL ret: %d, errno: %d\\n\", ret, errno);\n\t\t\treturn;\n\t\t}\n#endif\n\t\tscc_ptr->rdwrfd = rdwrfd;\n\t\tprintf(\"Set port[%d].rdwrfd = %llx\\n\", port, (dword64)rdwrfd);\n\n\t\tnum_rings = 4;\n\t\tif(scc_ptr->modem_s0_val > 0) {\n\t\t\tnum_rings = scc_ptr->modem_s0_val;\n\t\t}\n\t\tscc_ptr->socket_num_rings = num_rings;\n\t\tscc_ptr->socket_last_ring_dfcyc = 0;\t/* do ring now*/\n\n\t\t/* and send some telnet codes */\n\t\tif(g_serial_modem_init_telnet) {\n\t\t\tscc_ptr->telnet_reqwill_mode[0] = 0xa;\n\t\t\tscc_ptr->telnet_reqdo_mode[0] = 0xa;\n\t\t\t\t/* 3=GO_AH and 1=ECHO */\n\t\t\tscc_ptr->telnet_reqdo_mode[1] = 0x4;\t// 34=LINEMODE\n\t\t}\n\t\tprintf(\"Telnet reqwill and reqdo's initialized: %08x_%08x,\"\n\t\t\t\t\"%08x_%08x\\n\",\n\t\t\t\tscc_ptr->telnet_reqwill_mode[1],\n\t\t\t\tscc_ptr->telnet_reqwill_mode[0],\n\t\t\t\tscc_ptr->telnet_reqdo_mode[1],\n\t\t\t\tscc_ptr->telnet_reqdo_mode[0]);\n\n\t\tscc_socket_modem_do_ring(dfcyc, port);\n\t}\n#endif\n}\n\nvoid\nscc_socket_telnet_reqs(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tword32\tmask, willmask, domask;\n\tint\ti, j;\n\n\tscc_ptr = &(g_scc[port]);\n\tfor(i = 0; i < 64; i++) {\n\t\tj = i >> 5;\n\t\tmask = 1 << (i & 31);\n\t\twillmask = scc_ptr->telnet_reqwill_mode[j];\n\t\tif(willmask & mask) {\n\t\t\tscc_printf(\"Telnet reqwill %d\\n\", i);\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xff);\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xfb);\t/* WILL */\n\t\t\tscc_add_to_writebuf(dfcyc, port, i);\n\t\t}\n\t\tdomask = scc_ptr->telnet_reqdo_mode[j];\n\t\tif(domask & mask) {\n\t\t\tscc_printf(\"Telnet reqdo %d\\n\", i);\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xff);\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xfd);\t/* DO */\n\t\t\tscc_add_to_writebuf(dfcyc, port, i);\n\t\t}\n\t}\n}\n\nvoid\nscc_socket_fill_readbuf(dword64 dfcyc, int port, int space_left)\n{\n#ifdef SCC_SOCKETS\n\tbyte\ttmp_buf[256];\n\tScc\t*scc_ptr;\n\tSOCKET\trdwrfd;\n\tint\tret;\n\tint\ti;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tscc_accept_socket(dfcyc, port);\n\tscc_socket_modem_do_ring(dfcyc, port);\n\n\tif(scc_ptr->modem_state) {\n\t\t/* Just get out, this is modem mode */\n\t\t/*  The transmit function stuffs any bytes in receive buf */\n\t\treturn;\n\t}\n\trdwrfd = scc_ptr->rdwrfd;\n\tif(rdwrfd == INVALID_SOCKET) {\n\t\treturn;\t\t/* just get out */\n\t}\n\n\t/* Try reading some bytes */\n\tspace_left = MY_MIN(space_left, 256);\n\tret = (int)recv(rdwrfd, tmp_buf, space_left, 0);\n\tif(ret > 0) {\n\t\tfor(i = 0; i < ret; i++) {\n\t\t\tif(tmp_buf[i] == 0) {\n\t\t\t\t/* Skip null chars */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tscc_socket_recvd_char(dfcyc, port, tmp_buf[i]);\n\t\t}\n\t} else if(ret == 0) {\n\t\t/* assume socket close */\n\t\tprintf(\"recv got 0 from rdwrfd=%llx, closing\\n\",\n\t\t\t\t\t\t\t(dword64)rdwrfd);\n\t\tscc_socket_close_extended(dfcyc, port, 1);\n\t}\n#endif\n}\n\nvoid\nscc_socket_recvd_char(dword64 dfcyc, int port, int c)\n{\n\tScc\t*scc_ptr;\n\tword32\tlocmask, remmask, mask, reqwillmask, reqdomask;\n\tint\ttelnet_mode, telnet_iac, eff_c, cpos, reply;\n\n\tscc_ptr = &(g_scc[port]);\n\n\ttelnet_mode = scc_ptr->telnet_mode;\n\ttelnet_iac = scc_ptr->telnet_iac;\n\n\teff_c = c;\n\tif(scc_ptr->cur_state == 2) {\n\t\t// Outgoing only connection, support 8-bit transfers with\n\t\t//  no telnet command support\n\t\ttelnet_mode = 0;\n\t\ttelnet_iac = 0;\n\t} else if(telnet_iac == 0) {\n\t\tif(c == 0xff) {\n\t\t\tscc_ptr->telnet_iac = 0xff;\n\t\t\treturn;\t\t\t/* and just get out */\n\t\t}\n\t} else {\n\t\t/* last char was 0xff, see if this is 0xff */\n\t\tif(c != 0xff) {\n\t\t\t/* this is some kind of command */\n\t\t\teff_c = eff_c | 0x100;\t/* indicate prev char IAC */\n\t\t}\n\t}\n\tscc_ptr->telnet_iac = 0;\n\n\tmask = 1 << (c & 31);\n\tcpos = (c >> 5) & 1;\n\tlocmask = scc_ptr->telnet_local_mode[cpos] & mask;\n\tremmask = scc_ptr->telnet_remote_mode[cpos] & mask;\n\treqwillmask = scc_ptr->telnet_reqwill_mode[cpos] & mask;\n\treqdomask = scc_ptr->telnet_reqdo_mode[cpos] & mask;\n\tswitch(telnet_mode) {\n\tcase 0:\t/* just passing through bytes */\n\t\tswitch(eff_c) {\n\t\tcase 0x1fe:\t/* DON'T */\n\t\tcase 0x1fd:\t/* DO */\n\t\tcase 0x1fc:\t/* WON'T */\n\t\tcase 0x1fb:\t/* WILL */\n\t\tcase 0x1fa:\t/* SB */\n\t\t\ttelnet_mode = c;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif(eff_c < 0x100) {\n\t\t\t\tscc_add_to_readbuf(dfcyc, port, c);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase 3: /* LINEMODE SB SLC, octet 0 */\n\t\tif(eff_c == 0x1f0) {\n\t\t\t/* SE, the end */\n\t\t\ttelnet_mode = 0;\n\t\t}\n\t\tscc_printf(\"LINEMODE SLC octet 0: %02x\\n\", c);\n\t\ttelnet_mode = 4;\n\t\tbreak;\n\tcase 4: /* LINEMODE SB SLC, octet 1 */\n\t\tscc_printf(\"LINEMODE SLC octet 1: %02x\\n\", c);\n\t\ttelnet_mode = 5;\n\t\tif(eff_c == 0x1f0) {\n\t\t\t/* SE, the end */\n\t\t\tscc_printf(\"Got SE at octet 1...\\n\");\n\t\t\ttelnet_mode = 0;\n\t\t}\n\t\tbreak;\n\tcase 5: /* LINEMODE SB SLC, octet 2 */\n\t\tscc_printf(\"LINEMODE SLC octet 2: %02x\\n\", c);\n\t\ttelnet_mode = 3;\n\t\tif(eff_c == 0x1f0) {\n\t\t\t/* SE, the end */\n\t\t\tprintf(\"Got Telnet SE at octet 2...\\n\");\n\t\t\ttelnet_mode = 0;\n\t\t}\n\t\tbreak;\n\tcase 34: /* LINEMODE SB beginning */\n\t\tswitch(c) {\n\t\tcase 3:\t/* SLC */\n\t\t\ttelnet_mode = 3;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttelnet_mode = 0xee;\t/* go to SB eat */\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase 0xfa: /* in 0xfa = SB mode, eat up subcommands */\n\t\tswitch(c) {\n\t\tcase 34:\t/* LINEMODE */\n\t\t\ttelnet_mode = 34;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttelnet_mode = 0xee;\t/* SB eat mode */\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase 0xee: /* in SB eat mode */\n\t\tif(eff_c == 0x1f0) {\t\t/* SE, end of sub-command */\n\t\t\ttelnet_mode = 0;\n\t\t} else {\n\t\t\t/* just stay in eat mode */\n\t\t}\n\t\tbreak;\n\tcase 0xfe:\t/* previous char was \"DON'T\" */\n\t\tif(locmask && (reqwillmask == 0)) {\n\t\t\t/* it's a mode change */\n\t\t\t/* always OK to turn off a mode that we had on */\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xff);\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xfc);\t/* WON'T */\n\t\t\tscc_add_to_writebuf(dfcyc, port, c);\n\t\t}\n\t\tscc_ptr->telnet_local_mode[cpos] &= ~mask;\n\t\tscc_ptr->telnet_reqwill_mode[cpos] &= ~mask;\n\t\ttelnet_mode = 0;\n\t\tbreak;\n\tcase 0xfd:\t/* previous char was \"DO\" */\n\t\treply = 0xfc;\n\t\tif(locmask == 0 && (reqwillmask == 0)) {\n\t\t\t/* it's a mode change, send some response  */\n\t\t\treply = 0xfc;\t/* nack it with WON'T */\n\t\t\tif(c == 0x03 || c == 0x01) {\n\t\t\t\treply = 0xfb;\t/* Ack with WILL */\n\t\t\t}\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xff);\n\t\t\tscc_add_to_writebuf(dfcyc, port, reply);\n\t\t\tscc_add_to_writebuf(dfcyc, port, c);\n\t\t}\n\t\tif(reqwillmask || (reply == 0xfb)) {\n\t\t\tscc_ptr->telnet_local_mode[cpos] |= mask;\n\t\t}\n\t\tscc_ptr->telnet_reqwill_mode[cpos] &= ~mask;\n\t\ttelnet_mode = 0;\n\t\tbreak;\n\tcase 0xfc:\t/* previous char was \"WON'T\" */\n\t\tif(remmask && (reqdomask == 0)) {\n\t\t\t/* it's a mode change, ack with DON'T */\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xff);\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xfe);\t/* DON'T */\n\t\t\tscc_add_to_writebuf(dfcyc, port, c);\n\t\t}\n\t\tscc_ptr->telnet_remote_mode[cpos] &= ~mask;\n\t\tscc_ptr->telnet_reqdo_mode[cpos] &= ~mask;\n\t\ttelnet_mode = 0;\n\t\tbreak;\n\tcase 0xfb:\t/* previous char was \"WILL\" */\n\t\treply = 0xfe;\t/* nack it with DON'T */\n\t\tif(remmask == 0 && (reqdomask == 0)) {\n\t\t\t/* it's a mode change, send some response  */\n\t\t\tif(c == 0x03 || c == 0x01) {\n\t\t\t\treply = 0xfd;\t/* Ack with DO */\n\t\t\t}\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xff);\n\t\t\tscc_add_to_writebuf(dfcyc, port, reply);\n\t\t\tscc_add_to_writebuf(dfcyc, port, c);\n\t\t}\n\t\tif(reqdomask || (reply == 0xfd)) {\n\t\t\tscc_ptr->telnet_remote_mode[cpos] |= mask;\n\t\t}\n\t\tscc_ptr->telnet_reqdo_mode[cpos] &= ~mask;\n\t\ttelnet_mode = 0;\n\t\tbreak;\n\tdefault:\n\t\ttelnet_mode = 0;\n\t\tbreak;\n\t}\n\tscc_ptr->telnet_mode = telnet_mode;\n}\n\nvoid\nscc_socket_empty_writebuf(dword64 dfcyc, int port)\n{\n#ifdef SCC_SOCKETS\n# ifndef _WIN32\n\tstruct sigaction newact, oldact;\n# endif\n\tScc\t*scc_ptr;\n\tdword64\tdiff_dusec;\n\tSOCKET\trdwrfd;\n\tint\tplus_mode, plus_char, rdptr, wrptr, done, ret, len, c;\n\tint\ti;\n\n\tscc_socket_maybe_open(dfcyc, port, 0);\n\n\tscc_ptr = &(g_scc[port]);\n\n\t/* See if +++ done and we should go to command mode */\n\tdiff_dusec = (dfcyc - scc_ptr->out_char_dfcyc) >> 16;\n\tif((diff_dusec > 900LL*1000) && (scc_ptr->modem_plus_mode == 3) &&\n\t\t\t\t(scc_ptr->modem_state == 0) &&\n\t\t\t\t(scc_ptr->cur_state == 1)) {\n\t\tscc_ptr->modem_state = 1;\t/* go modem mode, stay connect*/\n\t\tscc_ptr->modem_plus_mode = 0;\n\t\tscc_socket_send_modem_code(dfcyc, port, 0);\t// \"OK\"\n\t}\n\n\t/* Try writing some bytes */\n\tdone = 0;\n\twhile(!done) {\n\t\trdptr = scc_ptr->out_rdptr;\n\t\twrptr = scc_ptr->out_wrptr;\n\t\tif(rdptr == wrptr) {\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t}\n\t\trdwrfd = scc_ptr->rdwrfd;\n\t\tlen = wrptr - rdptr;\n\t\tif(len < 0) {\n\t\t\tlen = SCC_OUTBUF_SIZE - rdptr;\n\t\t}\n\t\tif(len > 32) {\n\t\t\tlen = 32;\n\t\t}\n\t\t//printf(\"Writing data, %d bytes, modem_state:%d\\n\", len,\n\t\t//\t\t\t\t\tscc_ptr->modem_state);\n\t\tif(len <= 0) {\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t}\n\t\tscc_socket_maybe_open(dfcyc, port, 1);\n\n\t\tif(scc_ptr->modem_state) {\n\t\t\tlen = 1;\n\t\t\tscc_socket_modem_write(dfcyc, port,\n\t\t\t\t\t\tscc_ptr->out_buf[rdptr]);\n\t\t\tscc_ptr->write_called_this_vbl = 0;\n\t\t\tret = 1;\n\t\t} else {\n\t\t\tif(rdwrfd == INVALID_SOCKET) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tplus_char = scc_ptr->modem_s2_val;\n\t\t\tif((plus_char == 0) || (plus_char >= 128)) {\n\t\t\t\tplus_char = 0xfff;\t\t// Invalid\n\t\t\t\tscc_ptr->modem_plus_mode = 0;\n\t\t\t}\n\t\t\t// Look for '+++' within .8 seconds\n\t\t\tfor(i = 0; i < len; i++) {\n\t\t\t\tc = scc_ptr->out_buf[rdptr + i];\n\t\t\t\tplus_mode = scc_ptr->modem_plus_mode;\n\t\t\t\tdiff_dusec =\n\t\t\t\t\t(dfcyc - scc_ptr->out_char_dfcyc) >> 16;\n\t\t\t\tif(c == plus_char && plus_mode == 0) {\n\t\t\t\t\tif(diff_dusec > 500LL*1000) {\n\t\t\t\t\t\tscc_ptr->modem_plus_mode = 1;\n\t\t\t\t\t}\n\t\t\t\t} else if(c == plus_char) {\n\t\t\t\t\tif(diff_dusec < 800LL*1000) {\n\t\t\t\t\t\tplus_mode++;\n\t\t\t\t\t\tscc_ptr->modem_plus_mode =\n\t\t\t\t\t\t\t\tplus_mode;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tscc_ptr->modem_plus_mode = 0;\n\t\t\t\t}\n\t\t\t\tscc_ptr->out_char_dfcyc = dfcyc;\n\t\t\t}\n# ifndef _WIN32\n\t\t\t// ignore SIGPIPE around writes to the socket, so we\n\t\t\t//  can catch a closed socket and prepare to accept\n\t\t\t//  a new connection.  Otherwise, SIGPIPE kills KEGS\n\t\t\tsigemptyset(&newact.sa_mask);\n\t\t\tnewact.sa_handler = SIG_IGN;\n\t\t\tnewact.sa_flags = 0;\n\t\t\tsigaction(SIGPIPE, &newact, &oldact);\n# endif\n\n\t\t\tret = (int)send(rdwrfd, &(scc_ptr->out_buf[rdptr]),\n\t\t\t\t\t\t\t\t\tlen, 0);\n\n# ifndef _WIN32\n\t\t\tsigaction(SIGPIPE, &oldact, 0);\n\t\t\t/* restore previous SIGPIPE behavior */\n# endif\n\n#if 0\n\t\t\tprintf(\"sock output: %02x, len:%d\\n\",\n\t\t\t\t\tscc_ptr->out_buf[rdptr], len);\n#endif\n\n\t\t}\n\n\t\tif(ret == 0) {\n\t\t\tdone = 1;\t/* give up for now */\n\t\t\tbreak;\n\t\t} else if(ret < 0) {\n\t\t\t/* assume socket is dead */\n\t\t\tprintf(\"socket write failed, resuming modem mode\\n\");\n\t\t\tscc_socket_close_extended(dfcyc, port, 1);\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t} else {\n\t\t\trdptr = rdptr + ret;\n\t\t\tif(rdptr >= SCC_OUTBUF_SIZE) {\n\t\t\t\trdptr = rdptr - SCC_OUTBUF_SIZE;\n\t\t\t}\n\t\t\tscc_ptr->out_rdptr = rdptr;\n\t\t}\n\t}\n#endif\n}\n\nvoid\nscc_socket_modem_write(dword64 dfcyc, int port, int c)\n{\n\tScc\t*scc_ptr;\n\tchar\t*str;\n\tword32\tmodem_mode;\n\tint\tdo_echo, got_at, len;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->sockfd == INVALID_SOCKET) {\n\t\tscc_socket_open_incoming(dfcyc, port);\n\t}\n\n\tmodem_mode = scc_ptr->modem_mode;\n\tstr = (char *)&(scc_ptr->modem_cmd_str[0]);\n\n\tdo_echo = ((modem_mode & SCCMODEM_NOECHO) == 0);\n\tlen = scc_ptr->modem_cmd_len;\n\tgot_at = 0;\n#if 0\n\tprintf(\"T:%016llx M[%d][%d]: %02x\\n\", dfcyc, port, len, c);\n#endif\n\tif(len >= 2 && str[0] == 'a' && str[1] == 't') {\n\t\t/* we've got an 'at', do not back up past it */\n\t\tgot_at = 1;\n\t}\n\tif(c == 0x0d) {\n\t\tif(do_echo) {\n\t\t\tscc_add_to_readbuf(dfcyc, port, c);\t/* echo cr */\n\t\t\tscc_add_to_readbuf(dfcyc, port, 0x0a);\t/* echo lf */\n\t\t}\n\t\tdo_echo = 0;\t/* already did the echo */\n\t\tscc_socket_do_cmd_str(dfcyc, port);\n\t\tscc_ptr->modem_cmd_len = 0;\n\t\tlen = 0;\n\t\tstr[0] = 0;\n\t} else if(c == 0x08) {\n\t\tif(len <= 0) {\n\t\t\tdo_echo = 0;\t/* do not go past left margin */\n\t\t} else if(len == 2 && got_at) {\n\t\t\tdo_echo = 0;\t/* do not erase \"AT\" */\n\t\t} else {\n\t\t\t/* erase a character */\n\t\t\tlen--;\n\t\t\tstr[len] = 0;\n\t\t}\n\t} else if(c < 0x20) {\n\t\t/* ignore all control characters, don't echo */\n\t\t/* includes line feeds and nulls */\n\t\tdo_echo = 0;\n\t} else {\n\t\t/* other characters */\n\t\tif(len < SCC_MODEM_MAX_CMD_STR) {\n\t\t\tstr[len] = tolower(c);\n\t\t\tstr[len+1] = 0;\n\t\t\tlen++;\n\t\t}\n\t}\n\tscc_ptr->modem_cmd_len = len;\n\tif(do_echo) {\n\t\tscc_add_to_readbuf(dfcyc, port, c);\t/* echo */\n\t}\n}\n\nvoid\nscc_socket_do_cmd_str(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tchar\t*str;\n\tint\tpos, len, ret_val, reg, reg_val, was_amp, out_port, c;\n\tint\ti;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tstr = (char *)&(scc_ptr->modem_cmd_str[0]);\n\tprintf(\"Got modem string :%s:=%02x %02x %02x\\n\", str, str[0], str[1],\n\t\t\t\t\t\tstr[2]);\n\n\tlen = scc_ptr->modem_cmd_len;\n\tstr[len] = 0;\n\tstr[len+1] = 0;\n\tstr[len+2] = 0;\n\tpos = -1;\n\tif(len < 2) {\n\t\t/* just ignore it */\n\t\treturn;\n\t}\n\tif(str[0] != 'a' || str[1] != 't') {\n\t\treturn;\n\t}\n\n\t/* Some AT command received--make sure socket 6501/6502 is open */\n\tprintf(\"Some AT command received, sockfd=%llx\\n\",\n\t\t\t\t\t\t(dword64)scc_ptr->sockfd);\n\n\tpos = 2 - 1;\n\tret_val = 0;\t\t/* \"OK\" */\n\twas_amp = 0;\n\twhile(++pos < len) {\n\t\tc = str[pos] + was_amp;\n\t\twas_amp = 0;\n\t\tswitch(c) {\n\t\tcase '&':\t/* at& */\n\t\t\twas_amp = 0x100;\n\t\t\tbreak;\n\t\tcase 'z':\t/* atz */\n\t\t\tscc_ptr->modem_mode = 0;\n\t\t\tscc_ptr->modem_s0_val = 0;\n\t\t\tpos = len;\t\t/* ignore any other commands */\n\t\t\tbreak;\n\t\tcase 'e':\t/* ate = echo */\n\t\t\tc = str[pos+1];\n\t\t\tif(c == '1') {\n\t\t\t\tscc_ptr->modem_mode &= ~SCCMODEM_NOECHO;\n\t\t\t\tpos++;\n\t\t\t} else {\n\t\t\t\tscc_ptr->modem_mode |= SCCMODEM_NOECHO;\n\t\t\t\tpos++;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'v':\t/* atv = verbose */\n\t\t\tc = str[pos+1];\n\t\t\tif(c == '1') {\n\t\t\t\tscc_ptr->modem_mode &= ~SCCMODEM_NOVERBOSE;\n\t\t\t\tpos++;\n\t\t\t} else {\n\t\t\t\tscc_ptr->modem_mode |= SCCMODEM_NOVERBOSE;\n\t\t\t\tpos++;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'o':\t/* ato = go online */\n\t\t\tprintf(\"ato\\n\");\n\t\t\tif(scc_ptr->dcd && scc_ptr->modem_state &&\n\t\t\t\t\t(scc_ptr->rdwrfd != INVALID_SOCKET)) {\n\t\t\t\tprintf(\"Going back online\\n\");\n\t\t\t\tscc_socket_modem_connect(dfcyc, port);\n\t\t\t\tscc_ptr->modem_state = 0;\n\t\t\t\t\t\t// talk to socket\n\t\t\t\tret_val = -1;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'h':\t/* ath = hang up */\n\t\t\tprintf(\"ath, hanging up\\n\");\n\t\t\tscc_socket_close(port);\n\t\t\tif(scc_ptr->rdwrfd != INVALID_SOCKET) {\n\t\t\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\t\t}\n\t\t\t/* scc_socket_maybe_open_incoming(dfcyc, port); */\n\t\t\t\t\t\t\t/* reopen listen */\n\t\t\tbreak;\n\t\tcase 'a':\t/* ata */\n\t\t\tprintf(\"Doing ATA\\n\");\n\t\t\tscc_socket_do_answer(dfcyc, port);\n\t\t\tret_val = -1;\n\t\t\tbreak;\n\t\tcase 'd':\t/* atd */\n\t\t\tscc_ptr->modem_out_portnum = 23;\n\t\t\tpos++;\n\t\t\tc = str[pos];\n\t\t\tif(c == 't' || c == 'p') {\n\t\t\t\t/* skip tone or pulse */\n\t\t\t\tpos++;\n\t\t\t}\n\t\t\t/* see if it is 111 */\n\t\t\tif(strcmp(&str[pos], \"111\") == 0) {\n\t\t\t\t/* Do PPP! */\n\t\t\t} else {\n\t\t\t\t/* get string to connect to */\n\t\t\t\t/* Shift string so hostname moves to str[0] */\n\t\t\t\tfor(i = 0; i < len; i++) {\n\t\t\t\t\tc = str[pos];\n\t\t\t\t\tif(c == ':') {\n\t\t\t\t\t\t/* get port number now */\n\t\t\t\t\t\tout_port = (int)strtol(\n\t\t\t\t\t\t\t&str[pos+1], 0, 10);\n\t\t\t\t\t\tif(out_port <= 1) {\n\t\t\t\t\t\t\tout_port = 23;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tscc_ptr->modem_out_portnum =\n\t\t\t\t\t\t\t\tout_port;\n\t\t\t\t\t\tc = 0;\n\t\t\t\t\t}\n\t\t\t\t\tstr[i] = c;\n\t\t\t\t\tif((pos >= len) || (c == 0)) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tpos++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tscc_socket_open_outgoing(dfcyc, port,\n\t\t\t\t(char *)&scc_ptr->modem_cmd_str[0],\n\t\t\t\tscc_ptr->modem_out_portnum);\n\t\t\tret_val = -1;\n\t\t\tpos = len;\t/* always eat rest of the line */\n\t\t\tbreak;\n\t\tcase 's':\t/* atsnn=yy */\n\t\t\tpos++;\n\t\t\treg = 0;\n\t\t\twhile(1) {\n\t\t\t\tc = str[pos];\n\t\t\t\tif(c < '0' || c > '9') {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treg = (reg * 10) + c - '0';\n\t\t\t\tpos++;\n\t\t\t}\n\t\t\tif(c == '?') {\n\t\t\t\t/* display S-register */\n\t\t\t\tif(reg == 0) {\n\t\t\t\t\tscc_add_to_readbufv(dfcyc, port,\n\t\t\t\t\t\t\"S0=%d\\n\",\n\t\t\t\t\t\tscc_ptr->modem_s0_val);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif(c != '=') {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tpos++;\n\t\t\treg_val = 0;\n\t\t\twhile(1) {\n\t\t\t\tc = str[pos];\n\t\t\t\tif(c < '0' || c >'9') {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treg_val = (reg_val * 10) + c - '0';\n\t\t\t\tpos++;\n\t\t\t}\n\t\t\tprintf(\"ats%d = %d\\n\", reg, reg_val);\n\t\t\tif(reg == 0) {\n\t\t\t\tscc_ptr->modem_s0_val = reg_val;\n\t\t\t}\n\t\t\tif(reg == 2) {\n\t\t\t\tscc_ptr->modem_s2_val = reg_val;\n\t\t\t}\n\t\t\tpos--;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t/* some command--peek into next chars to finish it */\n\t\t\twhile(1) {\n\t\t\t\tc = str[pos+1];\n\t\t\t\tif(c >= '0' && c <= '9') {\n\t\t\t\t\t/* eat numbers */\n\t\t\t\t\tpos++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(c == '=') {\n\t\t\t\t\t/* eat this as well */\n\t\t\t\t\tpos++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t/* else get out */\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(ret_val >= 0) {\n\t\tscc_socket_send_modem_code(dfcyc, port, ret_val);\n\t}\n}\n\nvoid\nscc_socket_send_modem_code(dword64 dfcyc, int port, int code)\n{\n\tScc\t*scc_ptr;\n\tchar\t*str;\n\tword32\tmodem_mode;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tswitch(code) {\n\tcase 0: str = \"OK\"; break;\n\tcase 1: str = \"CONNECT\"; break;\n\tcase 2: str = \"RING\"; break;\n\tcase 3: str = \"NO CARRIER\"; break;\n\tcase 4: str = \"ERROR\"; break;\n\tcase 5: str = \"CONNECT 1200\"; break;\n\tcase 10: str = \"CONNECT 2400\"; break;\n\tcase 12: str = \"CONNECT 9600\"; break;\t// Generic AT docs/Warp6 BBS\n\tcase 13: str = \"CONNECT 9600\"; break;\t// US Robotics Sportster/HST\n\tcase 14: str = \"CONNECT 19200\"; break;\t// Warp6 BBS\n\tcase 16: str = \"CONNECT 19200\"; break;\t// Generic AT docs\n\tcase 25: str = \"CONNECT 14400\"; break;\t// US Robotics Sportster\n\tcase 85: str = \"CONNECT 19200\"; break;\t// US Robotics Sportster\n\tcase 18: str = \"CONNECT 57600\"; break;\t// Generic AT docs/Warp6 BBS\n\tcase 28: str = \"CONNECT 38400\"; break;\t// Warp6 BBS/Hayes\n\tdefault:\n\t\tstr = \"ERROR\";\n\t}\n\n\tprintf(\"Sending modem code %d = %s\\n\", code, str);\n\n\tmodem_mode = scc_ptr->modem_mode;\n\tif(modem_mode & SCCMODEM_NOVERBOSE) {\n\t\t/* just the number */\n\t\tscc_add_to_readbufv(dfcyc, port, \"%d\", code);\n\t\tscc_add_to_readbuf(dfcyc, port, 0x0d);\n\t} else {\n\t\tscc_add_to_readbufv(dfcyc, port, \"%s\\n\", str);\n\t}\n}\n\nvoid\nscc_socket_modem_connect(dword64 dfcyc, int port)\n{\n\t// Send code telling Term program the connect speed\n\tif(g_scc[port].cur_state == 1) {\t\t// Virtual modem\n\t\tscc_socket_send_modem_code(dfcyc, port,\n\t\t\tg_serial_modem_response_code);\t/*28=38400*/\n\t}\n}\n\nvoid\nscc_socket_modem_do_ring(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tdword64\tdiff_dusecs;\n\tint\tnum_rings;\n\n\tscc_ptr = &(g_scc[port]);\n\tnum_rings = scc_ptr->socket_num_rings;\n\tif((num_rings > 0) && scc_ptr->modem_state) {\n\t\tnum_rings--;\n\t\tdiff_dusecs = (dfcyc - scc_ptr->socket_last_ring_dfcyc) >> 16;\n\t\tif(diff_dusecs < 2LL*1000*1000) {\n\t\t\treturn;\t\t/* nothing more to do */\n\t\t}\n\n\t\tscc_ptr->socket_num_rings = num_rings;\n\t\tscc_ptr->socket_last_ring_dfcyc = dfcyc;\n\t\tif(num_rings <= 0) {\n\t\t\t/* decide on answering */\n\t\t\tif(scc_ptr->modem_s0_val) {\n\t\t\t\tscc_socket_do_answer(dfcyc, port);\n\t\t\t} else {\n\t\t\t\tprintf(\"No answer, closing socket\\n\");\n\t\t\t\tscc_socket_close(port);\n\t\t\t}\n\t\t} else {\n\t\t\tscc_socket_send_modem_code(dfcyc, port, 2); /* RING */\n\t\t}\n\t}\n}\n\nvoid\nscc_socket_do_answer(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\n\tscc_ptr = &(g_scc[port]);\n\tscc_accept_socket(dfcyc, port);\n\tif(scc_ptr->rdwrfd == INVALID_SOCKET) {\n\t\tprintf(\"Answer when rdwrfd=-1, closing\\n\");\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\t/* send NO CARRIER message */\n\t} else {\n\t\tscc_socket_telnet_reqs(dfcyc, port);\n\t\tprintf(\"Send telnet reqs\\n\");\n\t\tif(scc_ptr->modem_state) {\n\t\t\tscc_socket_modem_connect(dfcyc, port);\n\t\t}\n\t\tscc_ptr->modem_state = 0;\t\t// Talk to socket\n\t\tscc_ptr->dcd = 1;\t\t\t// carrier on\n\t\tscc_ptr->socket_num_rings = 0;\n\t}\n}\n\n"
  },
  {
    "path": "gsplus/src/scc_unixdriver.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2025 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n/* This file contains the Mac/Linux calls to a real serial port */\n\n#include \"defc.h\"\n#include \"scc.h\"\n\n#ifndef _WIN32\n# include <termios.h>\n#endif\n\nextern Scc g_scc[2];\nextern word32 g_c025_val;\nextern char *g_serial_device[2];\n\n#ifndef _WIN32\nvoid\nscc_serial_unix_open(int port)\n{\n\tScc\t*scc_ptr;\n\tint\tfd;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tfd = open(&g_serial_device[port][0], O_RDWR | O_NONBLOCK);\n\tscc_ptr->unix_dev_fd = fd;\n\n\tprintf(\"scc_serial_unix_init %d called, fd: %d\\n\", port, fd);\n\n\tif(fd < 0) {\n\t\tscc_ptr->unix_dev_fd = -1;\n\t\tscc_ptr->cur_state = -1;\t// Failed to open\n\t\treturn;\n\t}\n\n\tscc_serial_unix_change_params(port);\n\n\tscc_ptr->cur_state = 0;\t\t// Actual Serial device\n}\n\nvoid\nscc_serial_unix_close(int port)\n{\n\tScc\t*scc_ptr;\n\tint\tfd;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tfd = scc_ptr->unix_dev_fd;\n\tif(fd >= 0) {\n\t\tclose(fd);\n\t}\n\tscc_ptr->unix_dev_fd = -1;\n}\n\nvoid\nscc_serial_unix_change_params(int port)\n{\n\tstruct termios termios_buf;\n\tScc\t*scc_ptr;\n\tint\tfd, csz, ret;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tfd = scc_ptr->unix_dev_fd;\n\tprintf(\"scc_serial_unix_change_parms port: %d, fd: %d\\n\", port, fd);\n\tif(fd <= 0) {\n\t\treturn;\n\t}\n\n\tret = tcgetattr(fd, &termios_buf);\n\tif(ret != 0) {\n\t\tprintf(\"tcgetattr port%d ret: %d\\n\", port, ret);\n\t}\n\n#if 1\n\tprintf(\"baudrate: %d, iflag:%x, oflag:%x, cflag:%x, lflag:%x\\n\",\n\t\t(int)termios_buf.c_ispeed, (int)termios_buf.c_iflag,\n\t\t(int)termios_buf.c_oflag, (int)termios_buf.c_cflag,\n\t\t(int)termios_buf.c_lflag);\n#endif\n\n\tmemset(&termios_buf, 0, sizeof(struct termios));\n\tcfmakeraw(&termios_buf);\n\tcfsetspeed(&termios_buf, scc_ptr->baud_rate);\n\n\tcsz = scc_ptr->char_size;\n\ttermios_buf.c_cflag = CREAD | CLOCAL;\n\ttermios_buf.c_cflag |= (csz == 5) ? CS5 :\n\t\t\t\t(csz == 6) ? CS6 :\n\t\t\t\t(csz == 7) ? CS7 :\n\t\t\t\t\tCS8;\n\tswitch((scc_ptr->reg[4] >> 2) & 0x3) {\n\tcase 2: // 1.5 stop bits\n\t\ttermios_buf.c_cflag |= CSTOPB;\t/* no 1.5 stop bit setting.*/\n\t\tbreak;\n\tcase 3: // 2 stop bits\n\t\ttermios_buf.c_cflag |= CSTOPB;\n\t\tbreak;\n\t}\n\n\tswitch((scc_ptr->reg[4]) & 0x3) {\n\tcase 1:\t// Odd parity\n\t\ttermios_buf.c_cflag |= (PARENB | PARODD);\n\t\tbreak;\n\tcase 3:\t// Even parity\n\t\ttermios_buf.c_cflag |= PARENB;\n\t\tbreak;\n\t}\n\n\t/* always enabled DTR and RTS control */\n#ifdef CRTSCTS\n\ttermios_buf.c_cflag |= CRTSCTS;\t\t\t// Linux: CTS/RTS\n#endif\n#ifdef CRTS_IFLOW\n\ttermios_buf.c_cflag |= CDTR_IFLOW | CRTS_IFLOW;\t\t// Mac: CTS/RTS\n#endif\n\n\tprintf(\"fd: %d, baudrate: %d, iflag:%x, oflag:%x, cflag:%x, lflag:%x\\n\",\n\t\tfd, (int)termios_buf.c_ispeed, (int)termios_buf.c_iflag,\n\t\t(int)termios_buf.c_oflag, (int)termios_buf.c_cflag,\n\t\t(int)termios_buf.c_lflag);\n\tret = tcsetattr(fd, TCSANOW, &termios_buf);\n\tif(ret != 0) {\n\t\tprintf(\"tcsetattr ret: %d\\n\", ret);\n\t}\n}\n\nvoid\nscc_serial_unix_fill_readbuf(dword64 dfcyc, int port, int space_left)\n{\n\tbyte\ttmp_buf[256];\n\tScc\t*scc_ptr;\n\tint\tfd, ret, flags, dcd;\n\tint\ti;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tfd = scc_ptr->unix_dev_fd;\n\tif(fd <= 0) {\n\t\treturn;\n\t}\n\n\t/* Try reading some bytes */\n\tspace_left = MY_MIN(space_left, 256);\n\tret = read(fd, tmp_buf, space_left);\n\n\tif(ret > 0) {\n\t\tfor(i = 0; i < ret; i++) {\n\t\t\tscc_add_to_readbuf(dfcyc, port, tmp_buf[i]);\n\t\t}\n\t}\n\tflags = 0;\n\tdcd = 0;\n\n#if defined(TIOCMGET) && defined(TIOCM_CAR)\n\tret = ioctl(fd, TIOCMGET, &flags);\n\tif(ret == 0) {\n\t\tdcd = 0;\n\t\tif(flags & TIOCM_CAR) {\t\t// DCD\n\t\t\tdcd = 1;\n\t\t}\n\t\tscc_ptr->dcd = dcd;\n\t}\n#endif\n}\n\nvoid\nscc_serial_unix_empty_writebuf(int port)\n{\n\tScc\t*scc_ptr;\n\tint\tfd, rdptr, wrptr, done, ret, len;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tfd = scc_ptr->unix_dev_fd;\n\tif(fd <= 0) {\n\t\treturn;\n\t}\n\n\t/* Try writing some bytes */\n\tdone = 0;\n\twhile(!done) {\n\t\trdptr = scc_ptr->out_rdptr;\n\t\twrptr = scc_ptr->out_wrptr;\n\t\tif(rdptr == wrptr) {\n\t\t\t//printf(\"...rdptr == wrptr\\n\");\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t}\n\t\tlen = wrptr - rdptr;\n\t\tif(len < 0) {\n\t\t\tlen = SCC_OUTBUF_SIZE - rdptr;\n\t\t}\n\t\tif(len > 32) {\n\t\t\tlen = 32;\n\t\t}\n\t\tif(len <= 0) {\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t}\n\t\tret = write(fd, &(scc_ptr->out_buf[rdptr]), len);\n\n\t\tif(ret <= 0) {\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t} else {\n\t\t\trdptr = rdptr + ret;\n\t\t\tif(rdptr >= SCC_OUTBUF_SIZE) {\n\t\t\t\trdptr = rdptr - SCC_OUTBUF_SIZE;\n\t\t\t}\n\t\t\tscc_ptr->out_rdptr = rdptr;\n\t\t}\n\t}\n}\n#endif\t/* !_WIN32 */\n"
  },
  {
    "path": "gsplus/src/scc_windriver.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n/* This file contains the Win32 COM1/COM2 calls */\n\n#include \"defc.h\"\n#include \"scc.h\"\n\nextern Scc g_scc[2];\nextern word32 g_c025_val;\nextern int g_serial_win_device[2];\n\n#ifdef _WIN32\nvoid\nscc_serial_win_open(int port)\n{\n\tCOMMTIMEOUTS commtimeouts;\n\tchar\tstr_buf[32];\n\tScc\t*scc_ptr;\n\tHANDLE\tcom_handle;\n\tint\tret;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tsnprintf(&str_buf[0], sizeof(str_buf), \"COM%d\",\n\t\t\t\t\t\tg_serial_win_device[port]);\n\n\tcom_handle = CreateFile(&str_buf[0], GENERIC_READ | GENERIC_WRITE,\n\t\t\t0, NULL, OPEN_EXISTING, 0, NULL);\n\n\tscc_ptr->win_com_handle = com_handle;\n\n\tprintf(\"scc_serial_win_init %d called, com_handle: %p\\n\", port,\n\t\t\t\t\t\t\t\tcom_handle);\n\n\tif(com_handle == INVALID_HANDLE_VALUE) {\n\t\tscc_ptr->cur_state = -1;\t\t// Failed to open\n\t\treturn;\n\t}\n\tscc_ptr->win_dcb_ptr = malloc(sizeof(DCB));\n\n\tscc_serial_win_change_params(port);\n\n\tcommtimeouts.ReadIntervalTimeout = MAXDWORD;\n\tcommtimeouts.ReadTotalTimeoutMultiplier = 0;\n\tcommtimeouts.ReadTotalTimeoutConstant = 0;\n\tcommtimeouts.WriteTotalTimeoutMultiplier = 0;\n\tcommtimeouts.WriteTotalTimeoutConstant = 10;\n\tret = SetCommTimeouts(com_handle, &commtimeouts);\n\tif(ret == 0) {\n\t\tprintf(\"setcommtimeout ret: %d\\n\", ret);\n\t}\n\tscc_ptr->cur_state = 0;\t\t// COM* is open\n}\n\nvoid\nscc_serial_win_close(int port)\n{\n\tScc\t*scc_ptr;\n\tHANDLE\tcom_handle;\n\n\tscc_ptr = &(g_scc[port]);\n\tcom_handle = scc_ptr->win_com_handle;\n\tscc_ptr->win_com_handle = INVALID_HANDLE_VALUE;\n\tif(com_handle == INVALID_HANDLE_VALUE) {\n\t\treturn;\n\t}\n\tCloseHandle(com_handle);\n\tfree(scc_ptr->win_dcb_ptr);\n\tscc_ptr->win_dcb_ptr = 0;\n}\n\nvoid\nscc_serial_win_change_params(int port)\n{\n\tDCB\t*dcbptr;\n\tHANDLE\tcom_handle;\n\tScc\t*scc_ptr;\n\tint\tret;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tcom_handle = scc_ptr->win_com_handle;\n\tdcbptr = scc_ptr->win_dcb_ptr;\n\tif((com_handle == INVALID_HANDLE_VALUE) || (scc_ptr->cur_state != 0)) {\n\t\treturn;\n\t}\n\n\tret = GetCommState(com_handle, dcbptr);\n\tif(ret == 0) {\n\t\tprintf(\"getcomm port%d ret: %d\\n\", port, ret);\n\t}\n\n#if 1\n\tprintf(\"dcb.baudrate: %d, bytesize:%d, stops:%d, parity:%d\\n\",\n\t\t(int)dcbptr->BaudRate, (int)dcbptr->ByteSize,\n\t\t(int)dcbptr->StopBits, (int)dcbptr->Parity);\n\tprintf(\"dcb.binary: %d, ctsflow: %d, dsrflow: %d, dtr: %d, dsr: %d\\n\",\n\t\t(int)dcbptr->fBinary,\n\t\t(int)dcbptr->fOutxCtsFlow,\n\t\t(int)dcbptr->fOutxDsrFlow,\n\t\t(int)dcbptr->fDtrControl,\n\t\t(int)dcbptr->fDsrSensitivity);\n\tprintf(\"dcb.txonxoff:%d, outx:%d, inx: %d, null: %d, rts: %d\\n\",\n\t\t(int)dcbptr->fTXContinueOnXoff,\n\t\t(int)dcbptr->fOutX,\n\t\t(int)dcbptr->fInX,\n\t\t(int)dcbptr->fNull,\n\t\t(int)dcbptr->fRtsControl);\n\tprintf(\"dcb.fAbortOnErr:%d, fParity:%d\\n\", (int)dcbptr->fAbortOnError,\n\t\t(int)dcbptr->fParity);\n#endif\n\n\tdcbptr->fAbortOnError = 0;\n\n\tdcbptr->BaudRate = scc_ptr->baud_rate;\n\tdcbptr->ByteSize = scc_ptr->char_size;\n\tdcbptr->StopBits = ONESTOPBIT;\n\tswitch((scc_ptr->reg[4] >> 2) & 0x3) {\n\tcase 2: // 1.5 stop bits\n\t\tdcbptr->StopBits = ONE5STOPBITS;\n\t\tbreak;\n\tcase 3: // 2 stop bits\n\t\tdcbptr->StopBits = TWOSTOPBITS;\n\t\tbreak;\n\t}\n\n\tdcbptr->Parity = NOPARITY;\n\tswitch((scc_ptr->reg[4]) & 0x3) {\n\tcase 1:\t// Odd parity\n\t\tdcbptr->Parity = ODDPARITY;\n\t\tbreak;\n\tcase 3:\t// Even parity\n\t\tdcbptr->Parity = EVENPARITY;\n\t\tbreak;\n\t}\n\n\tdcbptr->fNull = 0;\n\tdcbptr->fDtrControl = DTR_CONTROL_ENABLE;\n\tdcbptr->fDsrSensitivity = 0;\n\tdcbptr->fOutxCtsFlow = 0;\n\tdcbptr->fOutxDsrFlow = 0;\n\tdcbptr->fParity = 0;\n\tdcbptr->fInX = 0;\n\tdcbptr->fOutX = 0;\n\tdcbptr->fRtsControl = RTS_CONTROL_ENABLE;\n\n\tret = SetCommState(com_handle, dcbptr);\n\tif(ret == 0) {\n\t\tprintf(\"SetCommState ret: %d, new baud: %d\\n\", ret,\n\t\t\t(int)dcbptr->BaudRate);\n\t}\n}\n\nvoid\nscc_serial_win_fill_readbuf(dword64 dfcyc, int port, int space_left)\n{\n\tbyte\ttmp_buf[256];\n\tScc\t*scc_ptr;\n\tHANDLE\tcom_handle;\n\tDWORD\tbytes_read;\n\tint\tret;\n\tint\ti;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tcom_handle = scc_ptr->win_com_handle;\n\tif(com_handle == INVALID_HANDLE_VALUE) {\n\t\treturn;\n\t}\n\n\t/* Try reading some bytes */\n\tspace_left = MY_MIN(256, space_left);\n\tret = ReadFile(com_handle, tmp_buf, space_left, &bytes_read, NULL);\n\n\tif(ret == 0) {\n\t\tprintf(\"ReadFile ret 0\\n\");\n\t}\n\n\tif(ret && (bytes_read > 0)) {\n\t\tfor(i = 0; i < (int)bytes_read; i++) {\n\t\t\tscc_add_to_readbuf(dfcyc, port, tmp_buf[i]);\n\t\t}\n\t}\n}\n\nvoid\nscc_serial_win_empty_writebuf(int port)\n{\n\tScc\t*scc_ptr;\n\tHANDLE\tcom_handle;\n\tDWORD\tbytes_written;\n\tword32\terr_code;\n\tint\trdptr, wrptr, done, ret, len;\n\n\tscc_ptr = &(g_scc[port]);\n\n\t//printf(\"win_empty_writebuf, com_h: %d\\n\", scc_ptr->win_com_handle);\n\tcom_handle = scc_ptr->win_com_handle;\n\tif(com_handle == INVALID_HANDLE_VALUE) {\n\t\treturn;\n\t}\n\n\t/* Try writing some bytes */\n\tdone = 0;\n\twhile(!done) {\n\t\trdptr = scc_ptr->out_rdptr;\n\t\twrptr = scc_ptr->out_wrptr;\n\t\tif(rdptr == wrptr) {\n\t\t\t//printf(\"...rdptr == wrptr\\n\");\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t}\n\t\tlen = wrptr - rdptr;\n\t\tif(len < 0) {\n\t\t\tlen = SCC_OUTBUF_SIZE - rdptr;\n\t\t}\n\t\tif(len > 32) {\n\t\t\tlen = 32;\n\t\t}\n\t\tif(len <= 0) {\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t}\n\t\tbytes_written = 1;\n\t\tret = WriteFile(com_handle, &(scc_ptr->out_buf[rdptr]), len,\n\t\t\t\t&bytes_written, NULL);\n\t\tprintf(\"WriteFile ret: %d, bytes_written:%d, len:%d\\n\", ret,\n\t\t\t(int)bytes_written, len);\n\n\t\terr_code = (word32)-1;\n\t\tif(ret == 0) {\n\t\t\terr_code = (word32)GetLastError();\n\t\t\tprintf(\"WriteFile ret:0, err_code: %08x\\n\", err_code);\n\t\t}\n\n\t\tif(ret == 0 || (bytes_written == 0)) {\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t} else {\n\t\t\trdptr = rdptr + bytes_written;\n\t\t\tif(rdptr >= SCC_OUTBUF_SIZE) {\n\t\t\t\trdptr = rdptr - SCC_OUTBUF_SIZE;\n\t\t\t}\n\t\t\tscc_ptr->out_rdptr = rdptr;\n\t\t}\n\t}\n}\n\n#endif\n"
  },
  {
    "path": "gsplus/src/sim65816.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2025 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include <math.h>\n\n#define INCLUDE_RCSID_C\n#include \"defc.h\"\n#undef INCLUDE_RCSID_C\n\ndouble g_dtime_sleep = 0;\ndouble g_dtime_in_sleep = 0;\nextern char *g_argv0_path;\n\n#define MAX_EVENTS\t64\n\n/* All EV_* must be less than 256, since upper bits reserved for other use */\n/*  e.g., DOC_INT uses upper bits to encode oscillator */\n#define EV_60HZ\t\t\t1\n#define EV_STOP\t\t\t2\n#define EV_SCAN_INT\t\t3\n#define EV_DOC_INT\t\t4\n#define EV_VBL_INT\t\t5\n#define EV_SCC\t\t\t6\n#define EV_VID_UPD\t\t7\n#define EV_MOCKINGBOARD\t\t8\n\nextern int g_stepping;\n\nextern word32 g_c068_statereg;\nextern int g_cur_a2_stat;\n\nextern word32 g_c02d_int_crom;\n\nextern word32 g_c035_shadow_reg;\nextern word32 g_c036_val_speed;\n\nextern word32 g_c023_val;\nextern word32 g_c041_val;\nextern word32 g_c046_val;\nextern word32 g_zipgs_reg_c059;\nextern word32 g_zipgs_reg_c05a;\nextern word32 g_zipgs_reg_c05b;\nextern word32 g_zipgs_unlock;\nextern Iwm g_iwm;\n\nEngine_reg engine;\nextern word32 table8[];\nextern word32 table16[];\n\nextern byte doc_ram[];\n\nextern int g_iwm_motor_on;\nextern int g_fast_disk_emul;\nextern int g_slow_525_emul_wr;\nextern int g_config_control_panel;\n\nextern int g_audio_enable;\nextern int g_preferred_rate;\n\nint\tg_a2_fatal_err = 0;\ndword64\tg_dcycles_end = 0;\nint\tg_halt_sim = 0;\nint\tg_rom_version = -1;\nint\tg_user_halt_bad = 0;\nint\tg_halt_on_bad_read = 0;\nint\tg_ignore_bad_acc = 1;\nint\tg_ignore_halts = 1;\nint\tg_code_red = 0;\nint\tg_code_yellow = 0;\nint\tg_emul_6502_ind_page_cross_bug = 0;\n\nint\tg_config_iwm_vbl_count = 0;\nconst char g_kegs_version_str[] = \"1.38\";\n\ndword64\tg_last_vbl_dfcyc = 0;\ndword64\tg_cur_dfcyc = 1;\n\ndouble\tg_last_vbl_dadjcycs = 0;\ndouble\tg_dadjcycs = 0;\n\n\nint\tg_wait_pending = 0;\nint\tg_stp_pending = 0;\nextern int g_irq_pending;\n\nint\tg_num_irq = 0;\nint\tg_num_brk = 0;\nint\tg_num_cop = 0;\nint\tg_num_enter_engine = 0;\nint\tg_io_amt = 0;\nint\tg_engine_action = 0;\nint\tg_engine_recalc_event = 0;\nint\tg_engine_scan_int = 0;\nint\tg_engine_doc_int = 0;\n\n#define MAX_FATAL_LOGS\t\t20\n\nint\tg_debug_file_fd = -1;\nint\tg_fatal_log = -1;\nchar *g_fatal_log_strs[MAX_FATAL_LOGS];\n\nword32 stop_run_at;\n\nint g_25sec_cntr = 0;\nint g_1sec_cntr = 0;\n\ndouble g_dnatcycs_1sec = 0.0;\nword32 g_natcycs_lastvbl = 0;\n\nint Verbose = 0;\nint Halt_on = 0;\n\nword32 g_mem_size_base = 128*1024;\t/* size of motherboard memory */\nword32 g_mem_size_exp = 8*1024*1024;\t/* size of expansion RAM card */\nword32 g_mem_size_total = 128*1024;\t/* Total contiguous RAM from 0 */\n\nextern word32 g_slow_mem_changed[];\n\nbyte *g_slow_memory_ptr = 0;\nbyte *g_memory_ptr = 0;\nbyte *g_dummy_memory1_ptr = 0;\nbyte *g_rom_fc_ff_ptr = 0;\nbyte *g_rom_cards_ptr = 0;\n\nvoid *g_memory_alloc_ptr = 0;\t\t/* for freeing memory area */\n\nPage_info page_info_rd_wr[2*65536 + PAGE_INFO_PAD_SIZE];\n\nword32\tg_word32_tmp = 0;\nint\tg_force_depth = -1;\nint\tg_use_shmem = 1;\n\nextern word32 g_cycs_in_40col;\nextern word32 g_cycs_in_xredraw;\nextern word32 g_cycs_in_refresh_line;\nextern word32 g_cycs_outside_run_16ms;\nextern word32 g_refresh_bytes_xfer;\n\nextern int g_num_snd_plays;\nextern int g_num_recalc_snd_parms;\n\nextern int g_doc_vol;\n\nextern int g_status_refresh_needed;\n\nint\nsim_get_force_depth()\n{\n\treturn g_force_depth;\n}\n\nint\nsim_get_use_shmem()\n{\n\treturn g_use_shmem;\n}\n\nvoid\nsim_set_use_shmem(int use_shmem)\n{\n\tg_use_shmem = use_shmem;\n}\n\n#define TOOLBOX_LOG_LEN\t\t64\n\nint g_toolbox_log_pos = 0;\nword32 g_toolbox_log_array[TOOLBOX_LOG_LEN][8];\n\nword32\ntoolbox_debug_4byte(word32 addr)\n{\n\tword32\tpart1, part2;\n\n\t/* If addr looks safe, use it */\n\tif(addr > 0xbffc) {\n\t\treturn (word32)-1;\n\t}\n\n\tpart1 = get_memory16_c(addr);\n\tpart1 = (part1 >> 8) + ((part1 & 0xff) << 8);\n\tpart2 = get_memory16_c(addr+2);\n\tpart2 = (part2 >> 8) + ((part2 & 0xff) << 8);\n\n\treturn (part1 << 16) + part2;\n}\n\nvoid\ntoolbox_debug_c(word32 xreg, word32 stack, dword64 *dcyc_ptr)\n{\n\tint\tpos;\n\n\tpos = g_toolbox_log_pos;\n\n\tstack += 9;\n\tg_toolbox_log_array[pos][0] = (word32)\n\t\t\t\t\t((g_last_vbl_dfcyc + *dcyc_ptr) >> 16);\n\tg_toolbox_log_array[pos][1] = stack+1;\n\tg_toolbox_log_array[pos][2] = xreg;\n\tg_toolbox_log_array[pos][3] = toolbox_debug_4byte(stack+1);\n\tg_toolbox_log_array[pos][4] = toolbox_debug_4byte(stack+5);\n\tg_toolbox_log_array[pos][5] = toolbox_debug_4byte(stack+9);\n\tg_toolbox_log_array[pos][6] = toolbox_debug_4byte(stack+13);\n\tg_toolbox_log_array[pos][7] = toolbox_debug_4byte(stack+17);\n\n\tpos++;\n\tif(pos >= TOOLBOX_LOG_LEN) {\n\t\tpos = 0;\n\t}\n\n\tg_toolbox_log_pos = pos;\n}\n\nvoid\nshow_toolbox_log()\n{\n\tint\tpos;\n\tint\ti;\n\n\tpos = g_toolbox_log_pos;\n\n\tfor(i = TOOLBOX_LOG_LEN - 1; i >= 0; i--) {\n\t\tprintf(\"%2d:%2d: %08x %06x  %04x: %08x %08x %08x %08x %08x\\n\",\n\t\t\ti, pos,\n\t\t\tg_toolbox_log_array[pos][0],\n\t\t\tg_toolbox_log_array[pos][1],\n\t\t\tg_toolbox_log_array[pos][2],\n\t\t\tg_toolbox_log_array[pos][3],\n\t\t\tg_toolbox_log_array[pos][4],\n\t\t\tg_toolbox_log_array[pos][5],\n\t\t\tg_toolbox_log_array[pos][6],\n\t\t\tg_toolbox_log_array[pos][7]);\n\t\tpos++;\n\t\tif(pos >= TOOLBOX_LOG_LEN) {\n\t\t\tpos = 0;\n\t\t}\n\t}\n}\n\nword32\nget_memory_io(word32 loc, dword64 *dcyc_ptr)\n{\n\tint\ttmp;\n\n\tif(loc > 0xffffff) {\n\t\thalt_printf(\"get_memory_io:%08x out of range==halt!\\n\", loc);\n\t\treturn 0;\n\t}\n\n\ttmp = loc & 0xfef000;\n\tif(tmp == 0xc000 || tmp == 0xe0c000) {\n\t\treturn(io_read(loc & 0xfff, dcyc_ptr));\n\t}\n\n\t/* Else it's an illegal addr...skip if memory sizing */\n\tif(loc >= g_mem_size_total) {\n\t\tif((loc & 0xfffe) == 0) {\n\t\t\t//printf(\"get_io assuming mem sizing, not halting\\n\");\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/* Skip reads to f80000 and f00000, just return 0 */\n\tif((loc & 0xf70000) == 0xf00000) {\n\t\treturn 0;\n\t}\n\n\tif((loc & 0xff0000) == 0xef0000) {\n\t\t/* DOC RAM */\n\t\treturn (doc_ram[loc & 0xffff]);\n\t}\n\tif((loc & 0xffff00) == 0xbcff00) {\n\t\t/* TWGS mapped some ROM here, we'll force in all 0's */\n\t\t/* If user has selected >= 12MB of memory, then mem will be */\n\t\t/*  returned and we won't ever get here */\n\t\treturn 0;\n\t}\n\n\tg_code_yellow++;\n\tif(g_ignore_bad_acc && !g_user_halt_bad) {\n\t\t/* print no message, just get out.  User doesn't want */\n\t\t/*  to be bothered by buggy programs */\n\t\treturn 0;\n\t}\n\n\tprintf(\"get_memory_io for addr: %06x\\n\", loc);\n\tprintf(\"stat for addr: %06x = %p\\n\", loc,\n\t\t\t\tGET_PAGE_INFO_RD((loc >> 8) & 0xffff));\n\tset_halt(g_halt_on_bad_read | g_user_halt_bad);\n\n\treturn 0;\n}\n\nvoid\nset_memory_io(word32 loc, int val, dword64 *dcyc_ptr)\n{\n\tword32\ttmp;\n\n\ttmp = loc & 0xfef000;\n\tif(tmp == 0xc000 || tmp == 0xe0c000) {\n\t\tio_write(loc, val, dcyc_ptr);\n\t\treturn;\n\t}\n\n\t/* Else it's an illegal addr */\n\tif(loc >= g_mem_size_total) {\n\t\tif((loc & 0xfffe) == 0) {\n\t\t\t//printf(\"set_io assuming mem sizing, not halting\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/* ignore writes to ROM */\n\tif((loc & 0xfc0000) == 0xfc0000) {\n\t\treturn;\n\t}\n\n\tif((loc & 0xff0000) == 0xef0000) {\n\t\t/* DOC RAM */\n\t\tdoc_ram[loc & 0xffff] = val;\n\t\treturn;\n\t}\n\n\tif(g_ignore_bad_acc && !g_user_halt_bad) {\n\t\t/* print no message, just get out.  User doesn't want */\n\t\t/*  to be bothered by buggy programs */\n\t\treturn;\n\t}\n\n\tif((loc & 0xffc000) == 0x00c000) {\n\t\tprintf(\"set_memory %06x = %02x, warning\\n\", loc, val);\n\t\treturn;\n\t}\n\n\thalt_printf(\"set_memory %06x = %02x, stopping\\n\", loc, val);\n\n\treturn;\n}\n\nvoid\nshow_regs_act(Engine_reg *eptr)\n{\n\tint\ttmp_acc, tmp_x, tmp_y, tmp_psw, kpc, direct_page, dbank, stack;\n\n\tkpc = eptr->kpc;\n\ttmp_acc = eptr->acc;\n\tdirect_page = eptr->direct;\n\tdbank = eptr->dbank;\n\tstack = eptr->stack;\n\n\ttmp_x = eptr->xreg;\n\ttmp_y = eptr->yreg;\n\n\ttmp_psw = eptr->psr;\n\n\tdbg_printf(\"  PC=%02x.%04x A=%04x X=%04x Y=%04x P=%03x\",\n\t\tkpc>>16, kpc & 0xffff ,tmp_acc,tmp_x,tmp_y,tmp_psw);\n\tdbg_printf(\" S=%04x D=%04x B=%02x,cyc:%016llx\\n\", stack, direct_page,\n\t\tdbank, g_cur_dfcyc);\n}\n\nvoid\nshow_regs()\n{\n\tshow_regs_act(&engine);\n}\n\nvoid\nmy_exit(int ret)\n{\n\tg_a2_fatal_err = 0x10 + ret;\n\tprintf(\"exiting\\n\");\n}\n\n\nvoid\ndo_reset()\n{\n\tg_c035_shadow_reg = 0;\n\n\tg_c068_statereg = 0x200 | 0x08 | 0x04;\t// Set wrdefram, rdrom, lcbank2\n\tg_c02d_int_crom = 0xff;\n\tif(g_rom_version != 0) {\t\t// IIgs ROM01 or ROM03\n\t\tg_c068_statereg |= 0x01;\t// also set intcx\n\t\tg_c02d_int_crom = 0;\n\t}\n\tg_c023_val = 0;\n\tg_c041_val = 0;\n\n\tengine.psr = (engine.psr | 0x134) & ~(0x08);\n\tengine.stack = 0x100 + (engine.stack & 0xff);\n\tengine.dbank = 0;\n\tengine.direct = 0;\n\tengine.xreg &= 0xff;\n\tengine.yreg &= 0xff;\n\tg_wait_pending = 0;\n\tg_stp_pending = 0;\n\n\tvideo_reset();\n\tadb_reset();\n\tiwm_reset();\n\tscc_reset();\n\tsound_reset(g_cur_dfcyc);\n\tsetup_pageinfo();\n\tchange_display_mode(g_cur_dfcyc);\n\n\tg_irq_pending = 0;\n\tg_code_yellow = 0;\n\tg_code_red = 0;\n\n\tengine.kpc = get_memory16_c(0x00fffc);\n\n\tg_stepping = 0;\n}\n\n#define CHECK(start, var, value, var1, var2)\t\t\t\t\\\n\tvar2 = PTR2WORD(&(var));\t\t\t\t\t\\\n\tvar1 = PTR2WORD((start));\t\t\t\t\t\\\n\tif((var2 - var1) != value) {\t\t\t\t\t\\\n\t\tprintf(\"CHECK: \" #var \" is 0x%x, but \" #value \" is 0x%x\\n\", \\\n\t\t\t(var2 - var1), value);\t\t\t\t\\\n\t\texit(5);\t\t\t\t\t\t\\\n\t}\n\nbyte *\nmemalloc_align(int size, int skip_amt, void **alloc_ptr)\n{\n\tbyte\t*bptr;\n\tword32\taddr;\n\tword32\toffset;\n\n\tskip_amt = MY_MAX(256, skip_amt);\n\tbptr = calloc(size + skip_amt, 1);\n\tif(alloc_ptr) {\n\t\t/* Save allocation address */\n\t\t*alloc_ptr = bptr;\n\t}\n\n\taddr = PTR2WORD(bptr) & 0xff;\n\n\t/* must align bptr to be 256-byte aligned */\n\t/* this code should work even if ptrs are > 32 bits */\n\n\toffset = ((addr + skip_amt - 1) & (~0xff)) - addr;\n\n\treturn (bptr + offset);\n}\n\nvoid\nmemory_ptr_init()\n{\n\tword32\tmem_size;\n\n\t/* This routine may be called several times--each time the ROM file */\n\t/*  changes this will be called */\n\tmem_size = MY_MIN(0xdf0000, g_mem_size_base + g_mem_size_exp);\n\tif(g_rom_version == 0) {\t\t\t// Apple //e\n\t\tmem_size = g_mem_size_base;\n\t}\n\tg_mem_size_total = mem_size;\n\tif(g_memory_alloc_ptr) {\n\t\tfree(g_memory_alloc_ptr);\n\t\tg_memory_alloc_ptr = 0;\n\t}\n\tg_memory_ptr = memalloc_align(mem_size, 256, &g_memory_alloc_ptr);\n\n\tprintf(\"RAM size is 0 - %06x (%.2fMB)\\n\", mem_size,\n\t\t(double)mem_size/(1024.0*1024.0));\n}\n\nextern int g_screen_redraw_skip_amt;\nextern int g_use_dhr140;\nextern int g_use_bw_hires;\n\nchar g_display_env[512];\nint\tg_screen_depth = 8;\n\nint\nparse_argv(int argc, char **argv, int slashes_to_find)\n{\n\tbyte\t*bptr;\n\tchar\t*str, *arg2_str;\n\tint\tskip_amt, tmp1, len;\n\tint\ti;\n\n#if 0\n\t// If launched from Finder, no stdout, so send it to /tmp/out1\n\tfflush(stdout);\n\tsetvbuf(stdout, 0, _IONBF, 0);\n\tclose(1);\n\t(void)open(\"/tmp/out1\", O_WRONLY | O_CREAT | O_TRUNC, 0x1b6);\n#endif\n\n\tprintf(\"Starting KEGS v%s\\n\", &g_kegs_version_str[0]);\n\n\t// parse arguments\n\t// First, Check if KEGS_BIG_ENDIAN is set correctly\n\tg_word32_tmp = 0x01020304;\n\tbptr = (byte *)&(g_word32_tmp);\n#ifdef KEGS_BIG_ENDIAN\n\tbptr[0] = 6;\n\tbptr[3] = 5;\n#else\n\tbptr[0] = 5;\n\tbptr[3] = 6;\n#endif\n\tif(g_word32_tmp != 0x06020305) {\n\t\tfatal_printf(\"KEGS_BIG_ENDIAN is not properly set\\n\");\n\t\treturn 1;\n\t}\n\n\tstr = kegs_malloc_str(argv[0]);\n\tlen = (int)strlen(str);\n\tfor(i = len; i > 0; i--) {\n\t\tif(str[i] == '/') {\n\t\t\tif(--slashes_to_find <= 0) {\n\t\t\t\tstr[i] = 0;\n\t\t\t\tg_argv0_path = str;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tprintf(\"g_argv0_path: %s\\n\", g_argv0_path);\n\n\ti = 0;\n\twhile(++i < argc) {\n\t\tprintf(\"argv[%d] = %s\\n\", i, argv[i]);\n\t\tif(!strcmp(\"-badrd\", argv[i])) {\n\t\t\tprintf(\"Halting on bad reads\\n\");\n\t\t\tg_halt_on_bad_read = 2;\n\t\t} else if(!strcmp(\"-noignbadacc\", argv[i])) {\n\t\t\tprintf(\"Not ignoring bad memory accesses\\n\");\n\t\t\tg_ignore_bad_acc = 0;\n\t\t} else if(!strcmp(\"-noignhalt\", argv[i])) {\n\t\t\tprintf(\"Not ignoring code red halts\\n\");\n\t\t\tg_ignore_halts = 0;\n\t\t} else if(!strcmp(\"-24\", argv[i])) {\n\t\t\tprintf(\"Using 24-bit visual\\n\");\n\t\t\tg_force_depth = 24;\n\t\t} else if(!strcmp(\"-16\", argv[i])) {\n\t\t\tprintf(\"Using 16-bit visual\\n\");\n\t\t\tg_force_depth = 16;\n\t\t} else if(!strcmp(\"-15\", argv[i])) {\n\t\t\tprintf(\"Using 15-bit visual\\n\");\n\t\t\tg_force_depth = 15;\n\t\t} else if(!strcmp(\"-mem\", argv[i])) {\n\t\t\tif((i+1) >= argc) {\n\t\t\t\tprintf(\"Missing argument\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tg_mem_size_exp = strtol(argv[i+1], 0, 0) & 0x00ff0000;\n\t\t\tprintf(\"Using %d as memory size\\n\", g_mem_size_exp);\n\t\t\ti++;\n\t\t} else if(!strcmp(\"-skip\", argv[i])) {\n\t\t\tif((i+1) >= argc) {\n\t\t\t\tprintf(\"Missing to skip argument\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tskip_amt = (int)strtol(argv[i+1], 0, 0);\n\t\t\tprintf(\"Using %d as skip_amt\\n\", skip_amt);\n\t\t\tg_screen_redraw_skip_amt = skip_amt;\n\t\t\ti++;\n\t\t} else if(!strcmp(\"-audio\", argv[i])) {\n\t\t\tif((i+1) >= argc) {\n\t\t\t\tprintf(\"Missing argument to -audio\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\ttmp1 = (int)strtol(argv[i+1], 0, 0);\n\t\t\tprintf(\"Using %d as audio enable val\\n\", tmp1);\n\t\t\tg_audio_enable = tmp1;\n\t\t\ti++;\n\t\t} else if(!strcmp(\"-arate\", argv[i])) {\n\t\t\tif((i+1) >= argc) {\n\t\t\t\tprintf(\"Missing argument to -arate\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\ttmp1 = (int)strtol(argv[i+1], 0, 0);\n\t\t\tprintf(\"Using %d as preferred audio rate\\n\", tmp1);\n\t\t\tg_preferred_rate = tmp1;\n\t\t\ti++;\n\t\t} else if(!strcmp(\"-v\", argv[i])) {\n\t\t\tif((i+1) >= argc) {\n\t\t\t\tprintf(\"Missing argument to -v\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\ttmp1 = (int)strtol(argv[i+1], 0, 0);\n\t\t\tprintf(\"Setting Verbose = 0x%03x\\n\", tmp1);\n\t\t\tVerbose = tmp1;\n\t\t\ti++;\n\t\t} else if(!strcmp(\"-display\", argv[i])) {\n\t\t\tif((i+1) >= argc) {\n\t\t\t\tprintf(\"Missing argument\\n\");\n\t\t\t\texit(1);\n\t\t\t}\n\t\t\tprintf(\"Using %s as display\\n\", argv[i+1]);\n\t\t\tsnprintf(g_display_env, sizeof(g_display_env),\n\t\t\t\t\t\t\"DISPLAY=%s\", argv[i+1]);\n\t\t\tputenv(&g_display_env[0]);\n\t\t\ti++;\n\t\t} else if(!strcmp(\"-noshm\", argv[i])) {\n\t\t\tprintf(\"Not using X shared memory\\n\");\n\t\t\tg_use_shmem = 0;\n\t\t} else if(!strcmp(\"-joystick\", argv[i])) {\n\t\t\tprintf(\"Ignoring -joystick option\\n\");\n\t\t} else if(!strcmp(\"-dhr140\", argv[i])) {\n\t\t\tprintf(\"Using simple dhires color map\\n\");\n\t\t\tg_use_dhr140 = 1;\n\t\t} else if(!strcmp(\"-bw\", argv[i])) {\n\t\t\tprintf(\"Forcing black-and-white hires modes\\n\");\n\t\t\tg_cur_a2_stat |= ALL_STAT_COLOR_C021;\n\t\t\tg_use_bw_hires = 1;\n\t\t} else if(!strncmp(\"-NS\", argv[i], 3)) {\n\t\t\t// Some Mac argument, just ignore it\n\t\t\tif((i + 1) < argc) {\n\t\t\t\t// If the next argument doesn't start with '-',\n\t\t\t\t//  then ignore it, too\n\t\t\t\tif(argv[i+1][0] != '-') {\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if(!strcmp(\"-logpc\", argv[i])) {\n\t\t\tprintf(\"Force logpc enable\\n\");\n\t\t\tdebug_logpc_on(\"on\");\n\t\t} else if(!strncmp(\"-cfg\", argv[i], 4)) {\n\t\t\tif((i + 1) < argc) {\n\t\t\t\tconfig_set_config_kegs_name(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t} else if(argv[i][0] == '-') {\n\t\t\targ2_str = 0;\n\t\t\tif((i + 1) < argc) {\n\t\t\t\targ2_str = argv[i+1];\n\t\t\t}\n\t\t\ti += config_add_argv_override(&argv[i][1], arg2_str);\n\t\t} else {\n\t\t\tprintf(\"Bad option: %s\\n\", argv[i]);\n\t\t\treturn 3;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nint\nkegs_init(int mdepth, int screen_width, int screen_height, int no_scale_window)\n{\n\tg_config_control_panel = 0;\n\n\twoz_crc_init();\n\tfixed_memory_ptrs_init();\n\n\tif(sizeof(word32) != 4) {\n\t\tprintf(\"sizeof(word32) = %d, must be 4!\\n\",\n\t\t\t\t\t\t\t(int)sizeof(word32));\n\t\treturn 1;\n\t}\n\tprepare_a2_font();\t\t// Prepare default built-in font\n\tscc_init();\n\tiwm_init();\n\tinit_reg();\n\tadb_init();\n\tinitialize_events();\n\tdebugger_init();\n\tsetup_pageinfo();\n\n\tconfig_init();\n\tload_roms_init_memory();\n\n\tclear_halt();\n\n\tvideo_init(mdepth, screen_width, screen_height, no_scale_window);\n\n\tsound_init();\n\tjoystick_init();\n\n\tif(g_rom_version >= 3) {\n\t\tg_c036_val_speed |= 0x40;\t/* set power-on bit */\n\t}\n\n\tdo_reset();\n\n\tg_stepping = 0;\n\tclear_halt();\n\tcfg_set_config_panel(g_config_control_panel);\n\n\treturn 0;\n}\n\nvoid\nload_roms_init_memory()\n{\n\tconfig_load_roms();\n\tmemory_ptr_init();\n\tclk_setup_bram_version();\t/* Must be after config_load_roms */\n\tif(g_rom_version >= 3) {\n\t\tg_c036_val_speed |= 0x40;\t/* set power-on bit */\n\t} else {\n\t\tg_c036_val_speed &= (~0x40);\t/* clear the bit */\n\t}\n\t// Do not call do_reset(), caller is responsible for that\n\n\t/* if user booted ROM 01, switches to ROM 03, then switches back */\n\t/*  to ROM 01, then the reset routines call to Tool $0102 looks */\n\t/*  at uninitialized $e1/15fe and if it is negative it will JMP */\n\t/*  through $e1/1688 which ROM 03 left pointing to fc/0199 */\n\t/* So set e1/15fe = 0 */\n\tset_memory16_c(0xe115fe, 0, 1);\n}\n\nEvent g_event_list[MAX_EVENTS];\nEvent g_event_free;\nEvent g_event_start;\n\nvoid\ninitialize_events()\n{\n\tint\ti;\n\n\tfor(i = 1; i < MAX_EVENTS; i++) {\n\t\tg_event_list[i-1].next = &g_event_list[i];\n\t}\n\tg_event_free.next = &g_event_list[0];\n\tg_event_list[MAX_EVENTS-1].next = 0;\n\n\tg_event_start.next = 0;\n\tg_event_start.dfcyc = 0;\n\n\tadd_event_entry(CYCLES_IN_16MS_RAW << 16, EV_60HZ);\n}\n\nvoid\ncheck_for_one_event_type(int type, word32 mask)\n{\n\tEvent\t*ptr;\n\tint\tcount, depth;\n\n\ttype = type & 0xff;\n\tcount = 0;\n\tdepth = 0;\n\tptr = g_event_start.next;\n\twhile(ptr != 0) {\n\t\tdepth++;\n\t\tif((ptr->type & mask) == (word32)type) {\n\t\t\tcount++;\n\t\t\tif(count != 1) {\n\t\t\t\thalt_printf(\"in check_for_1, type %04x found \"\n\t\t\t\t\t\"at depth: %d, count: %d, at %016llx\\n\",\n\t\t\t\t\tptr->type, depth, count, ptr->dfcyc);\n\t\t\t}\n\t\t}\n\t\tptr = ptr->next;\n\t}\n}\n\nvoid\nadd_event_entry(dword64 dfcyc, int type)\n{\n\tEvent\t*this_event;\n\tEvent\t*ptr, *prev_ptr;\n\tint\tdone;\n\n\tthis_event = g_event_free.next;\n\tif(this_event == 0) {\n\t\thalt_printf(\"Out of queue entries!\\n\");\n\t\tshow_all_events();\n\t\treturn;\n\t}\n\tg_event_free.next = this_event->next;\n\n\tthis_event->type = type;\n\n\tif((dfcyc > (g_cur_dfcyc + (50LL*1000*1000 << 16))) ||\n\t\t\t\t\t\t(dfcyc < g_cur_dfcyc)) {\n\t\thalt_printf(\"add_event bad dfcyc:%016llx, type:%05x, \"\n\t\t\t\"cur_dfcyc: %016llx!\\n\", dfcyc, type, g_cur_dfcyc);\n\t\tdfcyc = g_cur_dfcyc + (1000LL << 16);\n\t}\n\n\tptr = g_event_start.next;\n\tif(ptr && (dfcyc < ptr->dfcyc)) {\n\t\t/* create event before next expected event */\n\t\t/* do this by calling engine_recalc_events */\n\t\tengine_recalc_events();\n\t}\n\n\tprev_ptr = &g_event_start;\n\tptr = g_event_start.next;\n\n\tdone = 0;\n\twhile(!done) {\n\t\tif(ptr == 0) {\n\t\t\tthis_event->next = ptr;\n\t\t\tthis_event->dfcyc = dfcyc;\n\t\t\tprev_ptr->next = this_event;\n\t\t\tbreak;\n\t\t} else {\n\t\t\tif(ptr->dfcyc < dfcyc) {\t// step across this guy\n\t\t\t\tprev_ptr = ptr;\n\t\t\t\tptr = ptr->next;\n\t\t\t} else {\t\t\t// go before this guy */\n\t\t\t\tthis_event->dfcyc = dfcyc;\n\t\t\t\tthis_event->next = ptr;\n\t\t\t\tprev_ptr->next = this_event;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tcheck_for_one_event_type(type, 0xffff);\n}\n\nextern int g_doc_saved_ctl;\n\ndword64\nremove_event_entry(int type, word32 mask)\n{\n\tEvent\t*ptr, *prev_ptr;\n\tEvent\t*next_ptr;\n\n\tptr = g_event_start.next;\n\tprev_ptr = &g_event_start;\n\n\twhile(ptr != 0) {\n\t\tif((ptr->type & mask) == (word32)type) {\t// got it\n\t\t\tnext_ptr = ptr->next;\t\t\t// remove it\n\t\t\tprev_ptr->next = next_ptr;\n\n\t\t\t/* Add ptr to free list */\n\t\t\tptr->next = g_event_free.next;\n\t\t\tg_event_free.next = ptr;\n\n\t\t\treturn ptr->dfcyc;\n\t\t}\n\t\tprev_ptr = ptr;\n\t\tptr = ptr->next;\n\t}\n\n\thalt_printf(\"remove event_entry: %08x, but not found!\\n\", type);\n\tif((type & 0xff) == EV_DOC_INT) {\n\t\tprintf(\"DOC, g_doc_saved_ctl = %02x\\n\", g_doc_saved_ctl);\n\t}\n\tshow_all_events();\n\n\treturn 0;\n}\n\nvoid\nadd_event_stop(dword64 dfcyc)\n{\n\tadd_event_entry(dfcyc, EV_STOP);\n}\n\nvoid\nadd_event_doc(dword64 dfcyc, int osc)\n{\n\tif(dfcyc < g_cur_dfcyc) {\n\t\tdfcyc = g_cur_dfcyc;\n\t\t//halt_printf(\"add_event_doc: dfcyc:%016llx, cur_dfcyc:\"\n\t\t//\t\"%016llx\\n\", dfcyc, g_cur_dfcyc);\n\t}\n\n\tadd_event_entry(dfcyc, EV_DOC_INT + (osc << 8));\n}\n\nvoid\nadd_event_scc(dword64 dfcyc, int type)\n{\n\tif(dfcyc < g_cur_dfcyc) {\n\t\tdfcyc = g_cur_dfcyc;\n\t}\n\n\tadd_event_entry(dfcyc, EV_SCC + (type << 8));\n}\n\nvoid\nadd_event_vbl()\n{\n\tdword64\tdfcyc;\n\n\tdfcyc = g_last_vbl_dfcyc + ((192*65LL) << 16);\n\tadd_event_entry(dfcyc, EV_VBL_INT);\n}\n\nvoid\nadd_event_vid_upd(int line)\n{\n\tdword64\tdfcyc;\n\n\tdfcyc = g_last_vbl_dfcyc + (((line + 1) * 65LL) << 16);\n\tadd_event_entry(dfcyc, EV_VID_UPD + (line << 8));\n\t\t// Redraw line when video counters first read video data\n}\n\nvoid\nadd_event_mockingboard(dword64 dfcyc)\n{\n\tif(dfcyc < g_cur_dfcyc) {\n\t\tdfcyc = g_cur_dfcyc;\n\t}\n\tadd_event_entry(dfcyc, EV_MOCKINGBOARD);\n}\n\ndword64 g_dfcyc_scan_int = 0;\n\nvoid\nadd_event_scan_int(dword64 dfcyc, int line)\n{\n\tdword64\tdfcyc_scan_int;\n\n\tdfcyc_scan_int = g_dfcyc_scan_int;\n\tif(dfcyc_scan_int) {\t\t\t\t// Event is pending\n\t\tif(dfcyc >= g_dfcyc_scan_int) {\n\t\t\t// We are after (or the same) as current, do nothing\n\t\t\treturn;\n\t\t}\n\t\tremove_event_entry(EV_SCAN_INT, 0xff);\n\t}\n\tif(dfcyc < g_cur_dfcyc) {\n\t\t// scan_int for line 0 is found during EV_60HZ, and some\n\t\t//  cycles may have passed before the EV_60HZ was handled.\n\t\t// We need it to happen now, so just adjust dfcyc\n\t\tdfcyc = g_cur_dfcyc;\n\t}\n\tadd_event_entry(dfcyc, EV_SCAN_INT + (line << 8));\n\tg_dfcyc_scan_int = dfcyc;\n\n\tcheck_for_one_event_type(EV_SCAN_INT, 0xff);\n}\n\n\ndword64\nremove_event_doc(int osc)\n{\n\treturn remove_event_entry(EV_DOC_INT + (osc << 8), 0xffff);\n}\n\ndword64\nremove_event_scc(int type)\n{\n\treturn remove_event_entry(EV_SCC + (type << 8), 0xffff);\n}\n\nvoid\nremove_event_mockingboard()\n{\n\t(void)remove_event_entry(EV_MOCKINGBOARD, 0xff);\n}\n\nvoid\nshow_all_events()\n{\n\tEvent\t*ptr;\n\tdword64\tdfcyc;\n\tint\tcount;\n\n\tcount = 0;\n\tptr = g_event_start.next;\n\twhile(ptr != 0) {\n\t\tdfcyc = ptr->dfcyc;\n\t\tprintf(\"Event: %02x: type: %05x, dfcyc: %016llx (%08llx)\\n\",\n\t\t\tcount, ptr->type, dfcyc, dfcyc - g_cur_dfcyc);\n\t\tptr = ptr->next;\n\t\tcount++;\n\t}\n\n}\n\nword32\tg_vbl_count = 0;\nint\tg_vbl_index_count = 0;\ndouble\tdtime_array[60];\ndouble\tg_dadjcycs_array[60];\ndouble\tg_dtime_diff3_array[60];\ndouble\tg_dtime_this_vbl_array[60];\ndouble\tg_dtime_exp_array[60];\ndouble\tg_dtime_pmhz_array[60];\ndouble\tg_dtime_eff_pmhz_array[60];\ndouble\tg_dtime_in_run_16ms = 0;\ndouble\tg_dtime_outside_run_16ms = 0;\ndouble\tg_dtime_end_16ms = 0;\nint\tg_limit_speed = 3;\nint\tg_zip_speed_mhz = 16;\t\t// 16MHz default\ndouble\tsim_time[60];\ndouble\tg_sim_sum = 0.0;\n\ndouble\tg_cur_sim_dtime = 0.0;\ndouble\tg_projected_pmhz = 1.0;\ndouble\tg_zip_pmhz = 8.0;\ndouble\tg_sim_mhz = 100.0;\nint\tg_line_ref_amt = 1;\nint\tg_video_line_update_interval = 0;\ndword64\tg_video_pixel_dcount = 0;\n\nFplus\tg_recip_projected_pmhz_slow;\nFplus\tg_recip_projected_pmhz_fast;\nFplus\tg_recip_projected_pmhz_zip;\nFplus\tg_recip_projected_pmhz_unl;\n\nvoid\nshow_pmhz()\n{\n\tprintf(\"Pmhz: %f, c036:%02x, limit: %d\\n\",\n\t\tg_projected_pmhz, g_c036_val_speed, g_limit_speed);\n\n}\n\nvoid\nsetup_zip_speeds()\n{\n\tdword64\tdrecip;\n\tdouble\tfmhz;\n\tint\tmult;\n\n\tmult = 16 - ((g_zipgs_reg_c05a >> 4) & 0xf);\n\t\t// 16 = full speed, 1 = 1/16th speed\n\tfmhz = (g_zip_speed_mhz * mult) / 16.0;\n#if 0\n\tif(mult == 16) {\n\t\t/* increase full speed by 19% to make zipgs freq measuring */\n\t\t/* programs work correctly */\n\t\tfmhz = fmhz * 1.19;\n\t}\n#endif\n\tdrecip = (dword64)(65536 / fmhz);\n\tg_zip_pmhz = fmhz;\n\tg_recip_projected_pmhz_zip.dplus_1 = drecip;\n\tif(fmhz <= 2.0) {\n\t\tg_recip_projected_pmhz_zip.dplus_x_minus_1 =\n\t\t\t\t\t\t(dword64)(1.01 * 65536);\n\t} else {\n\t\tg_recip_projected_pmhz_zip.dplus_x_minus_1 =\n\t\t\t\t\t(dword64)(1.01 * 65536 - drecip);\n\t}\n}\n\nword32 g_cycs_end_16ms = 0;\n\nint\nrun_16ms()\n{\n\tdouble\tdtime_start, dtime_end, dtime_end2, dtime_outside;\n\tint\tret;\n\n\tdtime_start = get_dtime();\n\tret = 0;\n\tfflush(stdout);\n\tg_dtime_sleep = 1.0/61.0;\t\t// For control_panel/debugger\n\tif(g_config_control_panel) {\n\t\tret = cfg_control_panel_update();\n\t\tif(!g_config_control_panel) {\n\t\t\treturn 0;\t// Was just switched off, get out\n\t\t}\n\t} else {\n\t\tif(g_halt_sim) {\n\t\t\tret = debugger_run_16ms();\n\t\t} else {\n\t\t\tret = run_a2_one_vbl();\n\t\t}\n\t}\n\tvideo_update();\n\tg_vbl_count++;\n\tdtime_end = get_dtime();\n\tg_dtime_in_run_16ms += (dtime_end - dtime_start);\n\n\t// If we are ahead, then do the sleep now\n\tmicro_sleep(g_dtime_sleep);\n\tdtime_end2 = get_dtime();\n\t//printf(\"Did sleep for %f, dtime passed:%f\\n\", g_dtime_sleep,\n\t//\t\t\t\t\tdtime_end2 - dtime_end);\n\tg_dtime_sleep = 0.0;\n\n\tg_dtime_in_sleep += (dtime_end2 - dtime_end);\n\n\tdtime_outside = 0.0;\n\tif(g_vbl_count > 1) {\n\t\tdtime_outside += (dtime_start - g_dtime_end_16ms);\n\t}\n\tg_dtime_outside_run_16ms += dtime_outside;\n\tg_dtime_end_16ms = dtime_end2;\n#if 0\n\tif((g_vbl_count & 0xf) == 0) {\n\t\tprintf(\"run_16ms end at %.3lf, dtime_16ms:%1.5lf out:%1.5lf\\n\",\n\t\t\tdtime_end, dtime_end - dtime_start, dtime_outside);\n\t}\n#endif\n\treturn ret | g_a2_fatal_err;\n}\n\nint\nrun_a2_one_vbl()\n{\n\tFplus\t*fplus_ptr;\n\tEvent\t*this_event, *db1;\n\tdouble\tnow_dtime, prev_dtime, fspeed_mult;\n\tdword64\tdfcyc, dfcyc_stop, prerun_dfcyc;\n\tword32\tret, zip_speed_0tof, zip_speed_0tof_new;\n\tint\tzip_en, zip_follow_cps, type, motor_on, iwm_1, iwm_25, fast;\n\tint\tlimit_speed, apple35_sel, zip_speed, faster_than_28, unl_speed;\n\n\tfflush(stdout);\n\n\tg_cur_sim_dtime = 0.0;\n\n\tg_recip_projected_pmhz_slow.dplus_1 = 0x10000;\n\tg_recip_projected_pmhz_slow.dplus_x_minus_1 = (dword64)(0.9 * 0x10000);\n\n\tg_recip_projected_pmhz_fast.dplus_1 = (dword64)(0x10000 / 2.8);\n\tg_recip_projected_pmhz_fast.dplus_x_minus_1 = (dword64)\n\t\t\t\t((1.98 - (1.0/2.8)) * 0x10000);\n\n\tzip_speed_0tof = g_zipgs_reg_c05a & 0xf0;\n\tsetup_zip_speeds();\n\n\tif(engine.fplus_ptr == 0) {\n\t\tg_recip_projected_pmhz_unl = g_recip_projected_pmhz_slow;\n\t}\n\n\twhile(1) {\n\t\tfflush(stdout);\n\n\t\tif(g_irq_pending && !(engine.psr & 0x4)) {\n\t\t\tirq_printf(\"taking an irq!\\n\");\n\t\t\ttake_irq();\n\t\t\t/* Interrupt! */\n\t\t}\n\n\t\tmotor_on = g_iwm_motor_on;\n\t\tlimit_speed = g_limit_speed;\n\t\tapple35_sel = g_iwm.state & IWM_STATE_C031_APPLE35SEL;\n\t\tzip_en = ((g_zipgs_reg_c05b & 0x10) == 0);\n\t\tzip_follow_cps = ((g_zipgs_reg_c059 & 0x8) != 0);\n\t\tzip_speed_0tof_new = g_zipgs_reg_c05a & 0xf0;\n\t\tfast = (g_c036_val_speed & 0x80) || (zip_en && !zip_follow_cps);\n\n\t\tif(zip_speed_0tof_new != zip_speed_0tof) {\n\t\t\tzip_speed_0tof = zip_speed_0tof_new;\n\t\t\tsetup_zip_speeds();\n\t\t}\n\n\t\tiwm_1 = motor_on && !apple35_sel &&\n\t\t\t\t(g_c036_val_speed & 0x4) &&\n\t\t\t\t(g_slow_525_emul_wr || !g_fast_disk_emul);\n\t\tiwm_25 = (motor_on && apple35_sel) && !g_fast_disk_emul;\n\t\tfaster_than_28 = fast && (!iwm_1 && !iwm_25) && zip_en &&\n\t\t\t((limit_speed == 0) || (limit_speed == 3));\n\t\tzip_speed = faster_than_28 &&\n\t\t\t((zip_speed_0tof != 0) || (limit_speed == 3) ||\n\t\t\t\t\t\t\t(g_zipgs_unlock >= 4) );\n\t\tunl_speed = faster_than_28 && !zip_speed;\n\t\tif(unl_speed) {\n\t\t\t/* use unlimited speed */\n\t\t\tfspeed_mult = g_projected_pmhz;\n\t\t\tfplus_ptr = &g_recip_projected_pmhz_unl;\n\t\t} else if(zip_speed) {\n\t\t\tfspeed_mult = g_zip_pmhz;\n\t\t\tfplus_ptr = &g_recip_projected_pmhz_zip;\n\t\t} else if(fast && !iwm_1 && (iwm_25 || (limit_speed != 1))) {\n\t\t\tfspeed_mult = 2.5;\n\t\t\tfplus_ptr = &g_recip_projected_pmhz_fast;\n\t\t} else {\n\t\t\t/* else run slow */\n\t\t\tfspeed_mult = 1.0;\n\t\t\tfplus_ptr = &g_recip_projected_pmhz_slow;\n\t\t}\n\n\t\tengine.fplus_ptr = fplus_ptr;\n\n\t\tprerun_dfcyc = g_cur_dfcyc;\n\t\tengine.dfcyc = prerun_dfcyc;\n\t\tdfcyc_stop = g_event_start.next->dfcyc + 1;\n\t\tif(g_stepping) {\n\t\t\tdfcyc_stop = prerun_dfcyc + 1;\n\t\t}\n\t\tg_dcycles_end = dfcyc_stop;\n\n#if 0\n\t\tprintf(\"Enter engine, fcycs: %f, stop: %f\\n\",\n\t\t\tprerun_fcycles, fcycles_stop);\n\t\tprintf(\"g_cur_dfcyc:%016llx, last_vbl_dfcyc:%016llx\\n\",\n\t\t\tg_cur_dfcyc, g_last_vbl_dfcyc);\n#endif\n\n\t\tg_num_enter_engine++;\n\t\tprev_dtime = get_dtime();\n\n\t\tret = enter_engine(&engine);\n\n\t\tnow_dtime = get_dtime();\n\n\t\tg_cur_sim_dtime += (now_dtime - prev_dtime);\n\n\t\tdfcyc = engine.dfcyc;\n\t\tg_cur_dfcyc = dfcyc;\n\n\t\tg_dadjcycs += (engine.dfcyc - prerun_dfcyc) * (1/65536.0) *\n\t\t\t\t\tfspeed_mult;\n\n#if 0\n\t\tprintf(\"...back, engine.dfcyc: %016llx, dfcyc: %016llx\\n\",\n\t\t\t(double)engine.dfcyc, dfcyc);\n#endif\n\n\t\tif(ret != 0) {\n\t\t\tg_engine_action++;\n\t\t\thandle_action(ret);\n\t\t}\n\n\t\tthis_event = g_event_start.next;\n\t\twhile(dfcyc >= this_event->dfcyc) {\n\t\t\t/* Pop this guy off of the queue */\n\t\t\tg_event_start.next = this_event->next;\n\n\t\t\ttype = this_event->type;\n\t\t\tthis_event->next = g_event_free.next;\n\t\t\tg_event_free.next = this_event;\n\t\t\tdbg_log_info(dfcyc, type, 0, 0x101);\n\t\t\tswitch(type & 0xff) {\n\t\t\tcase EV_60HZ:\n\t\t\t\tupdate_60hz(dfcyc, now_dtime);\n\t\t\t\treturn 0;\n\t\t\t\tbreak;\n\t\t\tcase EV_STOP:\n\t\t\t\tprintf(\"type: EV_STOP\\n\");\n\t\t\t\tprintf(\"next: %p, dfcyc: %016llx\\n\",\n\t\t\t\t\t\tg_event_start.next, dfcyc);\n\t\t\t\tdb1 = g_event_start.next;\n\t\t\t\thalt_printf(\"next.dfcyc:%016llx\\n\", db1->dfcyc);\n\t\t\t\tbreak;\n\t\t\tcase EV_SCAN_INT:\n\t\t\t\tg_engine_scan_int++;\n\t\t\t\tirq_printf(\"type: scan int\\n\");\n\t\t\t\tdo_scan_int(dfcyc, type >> 8);\n\t\t\t\tbreak;\n\t\t\tcase EV_DOC_INT:\n\t\t\t\tg_engine_doc_int++;\n\t\t\t\tdoc_handle_event(type >> 8, dfcyc);\n\t\t\t\tbreak;\n\t\t\tcase EV_VBL_INT:\n\t\t\t\tdo_vbl_int();\n\t\t\t\tbreak;\n\t\t\tcase EV_SCC:\n\t\t\t\tscc_do_event(dfcyc, type >> 8);\n\t\t\t\tbreak;\n\t\t\tcase EV_VID_UPD:\n\t\t\t\tvideo_update_event_line(type >> 8);\n\t\t\t\tbreak;\n\t\t\tcase EV_MOCKINGBOARD:\n\t\t\t\tmockingboard_event(dfcyc);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tprintf(\"Unknown event: %d!\\n\", type);\n\t\t\t\texit(3);\n\t\t\t}\n\n\t\t\tthis_event = g_event_start.next;\n\n\t\t}\n\n\t\tif(g_event_start.next == 0) {\n\t\t\thalt_printf(\"ERROR...run_prog, event_start.n=0!\\n\");\n\t\t}\n\n\t\tif(g_halt_sim) {\n\t\t\tbreak;\n\t\t}\n\t\tif(g_stepping) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tprintf(\"leaving run_prog, g_halt_sim:%d\\n\", g_halt_sim);\n\n\treturn 0;\n}\n\nvoid\nadd_irq(word32 irq_mask)\n{\n\tif(g_irq_pending & irq_mask) {\n\t\t/* Already requested, just get out */\n\t\treturn;\n\t}\n\tg_irq_pending |= irq_mask;\n\tengine_recalc_events();\n}\n\nvoid\nremove_irq(word32 irq_mask)\n{\n\tg_irq_pending = g_irq_pending & (~irq_mask);\n}\n\nvoid\ntake_irq()\n{\n\tword32\tnew_kpc, va;\n\n\tirq_printf(\"Taking irq, at: %02x/%04x, psw: %02x, dfcyc:%016llx\\n\",\n\t\t\tengine.kpc >> 16, engine.kpc & 0xffff, engine.psr,\n\t\t\tg_cur_dfcyc);\n\n\tg_num_irq++;\n\tif(g_wait_pending) {\n\t\t/* step over WAI instruction */\n\t\tengine.kpc = (engine.kpc & 0xff0000) |\n\t\t\t\t\t\t((engine.kpc + 1) & 0xffff);\n\t\tg_wait_pending = 0;\n\t}\n\n\tif(engine.psr & 0x100) {\n\t\t/* Emulation */\n\t\tset_memory_c(engine.stack, (engine.kpc >> 8) & 0xff, 1);\n\t\tengine.stack = ((engine.stack -1) & 0xff) + 0x100;\n\n\t\tset_memory_c(engine.stack, engine.kpc & 0xff, 1);\n\t\tengine.stack = ((engine.stack -1) & 0xff) + 0x100;\n\n\t\tset_memory_c(engine.stack, (engine.psr & 0xef), 1);\n\t\t\t/* Clear B bit in psr on stack */\n\t\tengine.stack = ((engine.stack -1) & 0xff) + 0x100;\n\n\t\tva = 0xfffffe;\n\t} else {\n\t\t/* native */\n\t\tset_memory_c(engine.stack, (engine.kpc >> 16) & 0xff, 1);\n\t\tengine.stack = ((engine.stack -1) & 0xffff);\n\n\t\tset_memory_c(engine.stack, (engine.kpc >> 8) & 0xff, 1);\n\t\tengine.stack = ((engine.stack -1) & 0xffff);\n\n\t\tset_memory_c(engine.stack, engine.kpc & 0xff, 1);\n\t\tengine.stack = ((engine.stack -1) & 0xffff);\n\n\t\tset_memory_c(engine.stack, engine.psr & 0xff, 1);\n\t\tengine.stack = ((engine.stack -1) & 0xffff);\n\n\t\tva = 0xffffee;\n\t}\n\n\tva = moremem_fix_vector_pull(va);\n\tnew_kpc = get_memory_c(va);\n\tnew_kpc = new_kpc + (get_memory_c(va + 1) << 8);\n\n\tengine.psr = ((engine.psr & 0x1f3) | 0x4);\n\n\tengine.kpc = new_kpc;\n\tHALT_ON(HALT_ON_IRQ, \"Halting on IRQ\\n\");\n\n}\n\ndouble\tg_dtime_last_vbl = 0.0;\ndouble\tg_dtime_expected = (1.0/VBL_RATE);\t// Approximately 1.0/60.0\n\nint g_scan_int_events = 0;\n\nvoid\nshow_dtime_array()\n{\n\tdouble\tdfirst_time;\n\tdouble\tfirst_total_cycs;\n\tint\ti;\n\tint\tpos;\n\n\tdfirst_time = 0.0;\n\tfirst_total_cycs = 0.0;\n\n\n\tfor(i = 0; i < 60; i++) {\n\t\tpos = (g_vbl_index_count + i) % 60;\n\t\tprintf(\"%2d:%2d dt:%.5f adjc:%9.1f this_vbl:%.6f \"\n\t\t\t\"exp:%.5f p:%2.2f ep:%2.2f\\n\",\n\t\t\ti, pos,\n\t\t\tdtime_array[pos] - dfirst_time,\n\t\t\tg_dadjcycs_array[pos] - first_total_cycs,\n\t\t\tg_dtime_this_vbl_array[pos],\n\t\t\tg_dtime_exp_array[pos] - dfirst_time,\n\t\t\tg_dtime_pmhz_array[pos],\n\t\t\tg_dtime_eff_pmhz_array[pos]);\n\t\tdfirst_time = dtime_array[pos];\n\t\tfirst_total_cycs = g_dadjcycs_array[pos];\n\t}\n}\n\n\nvoid\nupdate_60hz(dword64 dfcyc, double dtime_now)\n{\n\tregister word32 end_time;\n\tchar\tstatus_buf[1024];\n\tchar\tsim_mhz_buf[128];\n\tchar\ttotal_mhz_buf[128];\n\tchar\tsp_buf[128];\n\tchar\t*sim_mhz_ptr, *total_mhz_ptr, *code_str1, *code_str2, *sp_str;\n\tdword64\tplanned_dcyc;\n\tdouble\teff_pmhz, predicted_pmhz, recip_predicted_pmhz;\n\tdouble\tdtime_this_vbl_sim, dtime_diff_1sec, dratio, dtime_diff;\n\tdouble\tdtime_till_expected, dtime_this_vbl, dadjcycs_this_vbl;\n\tdouble\tdadj_cycles_1sec, dnatcycs_1sec;\n\tint\ttmp, doit_3_persec, cur_vbl_index, prev_vbl_index;\n\n\t/* NOTE: this event is defined to occur before line 0 */\n\t/* It's actually happening at the start of the border for line (-1) */\n\t/* All other timings should be adjusted for this */\n\n\tirq_printf(\"vbl_60hz: vbl: %d, dfcyc:%016llx, last_vbl_dfcyc:%016llx\\n\",\n\t\tg_vbl_count, dfcyc, g_last_vbl_dfcyc);\n\n\tplanned_dcyc = CYCLES_IN_16MS_RAW << 16;\n\n\tg_last_vbl_dfcyc = g_last_vbl_dfcyc + planned_dcyc;\n\n\tadd_event_entry(g_last_vbl_dfcyc + planned_dcyc, EV_60HZ);\n\tcheck_for_one_event_type(EV_60HZ, 0xff);\n\n\tcur_vbl_index = g_vbl_index_count;\n\n\t/* figure out dtime spent running SIM, not all the overhead */\n\tdtime_this_vbl_sim = g_cur_sim_dtime;\n\tg_cur_sim_dtime = 0.0;\n\tg_sim_sum = g_sim_sum - sim_time[cur_vbl_index] + dtime_this_vbl_sim;\n\tsim_time[cur_vbl_index] = dtime_this_vbl_sim;\n\n\tdadj_cycles_1sec = g_dadjcycs - g_dadjcycs_array[cur_vbl_index];\n\n\t/* dtime_diff_1sec is dtime total spent over the last 60 ticks */\n\tdtime_diff_1sec = dtime_now - dtime_array[cur_vbl_index];\n\n\tdtime_array[cur_vbl_index] = dtime_now;\n\tg_dadjcycs_array[cur_vbl_index] = g_dadjcycs;\n\n\tprev_vbl_index = cur_vbl_index;\n\tcur_vbl_index = prev_vbl_index + 1;\n\tif(cur_vbl_index >= 60) {\n\t\tcur_vbl_index = 0;\n\t}\n\tg_vbl_index_count = cur_vbl_index;\n\n\tGET_ITIMER(end_time);\n\tg_dnatcycs_1sec += (double)(end_time - g_natcycs_lastvbl);\n\tg_natcycs_lastvbl = end_time;\n\n\tif(prev_vbl_index == 0) {\n\t\tif(g_sim_sum < (1.0/250.0)) {\n\t\t\tsim_mhz_ptr = \"???\";\n\t\t\tg_sim_mhz = 250.0;\n\t\t} else {\n\t\t\tg_sim_mhz = (dadj_cycles_1sec / g_sim_sum) /\n\t\t\t\t\t\t\t(1000.0*1000.0);\n\t\t\tif(g_sim_mhz > 8000.0) {\n\t\t\t\tg_sim_mhz = 8000.0;\n\t\t\t}\n\t\t\tsnprintf(sim_mhz_buf, sizeof(sim_mhz_buf), \"%6.2f\",\n\t\t\t\t\t\t\t\tg_sim_mhz);\n\t\t\tsim_mhz_ptr = sim_mhz_buf;\n\t\t}\n\t\tif(dtime_diff_1sec < (1.0/250.0)) {\n\t\t\ttotal_mhz_ptr = \"???\";\n\t\t} else {\n\t\t\tsnprintf(total_mhz_buf, sizeof(total_mhz_buf), \"%6.2f\",\n\t\t\t\t(dadj_cycles_1sec / dtime_diff_1sec) /\n\t\t\t\t\t\t\t\t(1000000.0));\n\t\t\ttotal_mhz_ptr = total_mhz_buf;\n\t\t}\n\n\t\tswitch(g_limit_speed) {\n\t\tcase 1:\tsp_str = \"1Mhz\"; break;\n\t\tcase 2:\tsp_str = \"2.8Mhz\"; break;\n\t\tcase 3:\tsp_str = \"8.0Mhz\"; break;\n\t\tdefault: sp_str = \"Unlimited\"; break;\n\t\t}\n\t\tif(g_limit_speed == 3) {\t\t// ZipGS\n\t\t\tsnprintf(sp_buf, sizeof(sp_buf), \"%1.1fMHz\",\n\t\t\t\t\t\t\tg_zip_pmhz);\n\t\t\tsp_str = sp_buf;\n\t\t}\n\n\t\tsnprintf(status_buf, sizeof(status_buf), \"dfcyc:%7.1f sim \"\n\t\t\t\"MHz:%s Eff MHz:%s, sec:%1.3f vol:%02x Limit:%s\",\n\t\t\t(double)(dfcyc >> 20)/65536.0, sim_mhz_ptr,\n\t\t\ttotal_mhz_ptr, dtime_diff_1sec, g_doc_vol, sp_str);\n\t\tvideo_update_status_line(0, status_buf);\n\n\t\tif(g_video_line_update_interval == 0) {\n\t\t\tif(g_sim_mhz > 12.0) {\n\t\t\t\t/* just set video line_ref_amt to 1 */\n\t\t\t\tg_line_ref_amt = 1;\n\t\t\t} else if(g_line_ref_amt == 1 && g_sim_mhz < 4.0) {\n\t\t\t\tg_line_ref_amt = 8;\n\t\t\t}\n\t\t} else {\n\t\t\tg_line_ref_amt = g_video_line_update_interval;\n\t\t}\n\n\t\tdnatcycs_1sec = g_dnatcycs_1sec;\n\t\tif(g_dnatcycs_1sec < (1000.0*1000.0)) {\n\t\t\t/* make it so large that all %'s become 0 */\n\t\t\tdnatcycs_1sec = 800.0*1000.0*1000.0*1000.0;\n\t\t}\n\t\tdnatcycs_1sec = dnatcycs_1sec / 100.0; /* eff mult by 100 */\n\n\t\tg_video_pixel_dcount = 0;\n\n\t\tcode_str1 = \"\";\n\t\tcode_str2 = \"\";\n\t\tif(g_code_yellow) {\n\t\t\tcode_str1 = \"Code: Yellow\";\n\t\t\tcode_str2 = \"Emulated state suspect\";\n\t\t}\n\t\tif(g_code_red) {\n\t\t\tcode_str1 = \"Code: RED\";\n\t\t\tcode_str2 = \"Emulated state corrupt?\";\n\t\t}\n\t\tsnprintf(status_buf, sizeof(status_buf), \"sleep_dtime:%8.6f, \"\n\t\t\t\"out_16ms:%8.6f, in_16ms:%8.6f, snd_pl:%d\",\n\t\t\tg_dtime_in_sleep, g_dtime_outside_run_16ms,\n\t\t\tg_dtime_in_run_16ms, g_num_snd_plays);\n\t\tvideo_update_status_line(1, status_buf);\n\n\t\tdraw_iwm_status(2, status_buf);\n\n\t\tsnprintf(status_buf, sizeof(status_buf), \" KEGS v%-6s       \"\n\t\t\t\"Press F4 for Config Menu    %s %s\",\n\t\t\tg_kegs_version_str, code_str1, code_str2);\n\t\tvideo_update_status_line(3, status_buf);\n\n\t\tg_status_refresh_needed = 1;\n\n\t\tg_num_irq = 0;\n\t\tg_num_brk = 0;\n\t\tg_num_cop = 0;\n\t\tg_num_enter_engine = 0;\n\t\tg_io_amt = 0;\n\t\tg_engine_action = 0;\n\t\tg_engine_recalc_event = 0;\n\t\tg_engine_scan_int = 0;\n\t\tg_engine_doc_int = 0;\n\n\t\tg_cycs_in_40col = 0;\n\t\tg_cycs_in_xredraw = 0;\n\t\tg_cycs_in_refresh_line = 0;\n\t\tg_dnatcycs_1sec = 0.0;\n\t\tg_dtime_outside_run_16ms = 0.0;\n\t\tg_dtime_in_run_16ms = 0.0;\n\t\tg_refresh_bytes_xfer = 0;\n\n\t\tg_dtime_in_sleep = 0;\n\t\tg_num_snd_plays = 0;\n\t\tg_num_recalc_snd_parms = 0;\n\t}\n\n\tdtime_this_vbl = dtime_now - g_dtime_last_vbl;\n\tif(dtime_this_vbl < 0.001) {\n\t\tdtime_this_vbl = 0.001;\n\t}\n\n\tg_dtime_last_vbl = dtime_now;\n\n\tdadjcycs_this_vbl = g_dadjcycs - g_last_vbl_dadjcycs;\n\tg_last_vbl_dadjcycs = g_dadjcycs;\n\n\tg_dtime_expected += (1.0/VBL_RATE);\t// Approx. 1/60\n\n\teff_pmhz = (dadjcycs_this_vbl / dtime_this_vbl) / DCYCS_1_MHZ;\n\n\t/* using eff_pmhz, predict how many cycles can be run by */\n\t/*  g_dtime_expected */\n\n\tdtime_till_expected = g_dtime_expected - dtime_now;\n\n\tdratio = VBL_RATE * dtime_till_expected;\t// Approx. 60*dtime_exp\n\n\tpredicted_pmhz = eff_pmhz * dratio;\n\n\tif(! (predicted_pmhz < (1.4 * g_projected_pmhz))) {\n\t\tpredicted_pmhz = 1.4 * g_projected_pmhz;\n\t}\n\n\tif(! (predicted_pmhz > (0.7 * g_projected_pmhz))) {\n\t\tpredicted_pmhz = 0.7 * g_projected_pmhz;\n\t}\n\n\tif(!(predicted_pmhz >= 1.0)) {\n\t\tirq_printf(\"predicted: %f, setting to 1.0\\n\", predicted_pmhz);\n\t\tpredicted_pmhz = 1.0;\n\t}\n\n\tif(!(predicted_pmhz < 4500.0)) {\n\t\tirq_printf(\"predicted: %f, set to 1900.0\\n\", predicted_pmhz);\n\t\tpredicted_pmhz = 4500.0;\n\t}\n\n\trecip_predicted_pmhz = 1.0/predicted_pmhz;\n\tg_projected_pmhz = predicted_pmhz;\n\n\tg_recip_projected_pmhz_unl.dplus_1 = (dword64)\n\t\t\t\t\t\t(65536 * recip_predicted_pmhz);\n\tg_recip_projected_pmhz_unl.dplus_x_minus_1 =\n\t\t\t(dword64)(65536 * (1.01 - recip_predicted_pmhz));\n\n\tif(dtime_till_expected < -0.125) {\n\t\t/* If we were way off, get back on track */\n\t\t/* this happens because our sim took much longer than */\n\t\t/* expected, so we're going to skip some VBL */\n\t\tirq_printf(\"adj1: dtexp:%f, dt_new:%f\\n\",\n\t\t\tg_dtime_expected, dtime_now);\n\n\t\tdtime_diff = -dtime_till_expected;\n\n\t\tirq_printf(\"dtime_till_exp:%f, dtime_diff:%f, dfcyc:%016llx\\n\",\n\t\t\tdtime_till_expected, dtime_diff, dfcyc);\n\n\t\tg_dtime_expected += dtime_diff;\n\t}\n\n\tg_dtime_sleep = 0.0;\n\tif(dtime_till_expected > (1.0/VBL_RATE)) {\n\t\t/* we're running fast, usleep */\n\t\tg_dtime_sleep = dtime_till_expected - (1.0/VBL_RATE);\n\t}\n#if 0\n\tprintf(\"Sleep %f, till_exp:%f, dtime_now:%f, exp:%f\\n\",\n\t\tg_dtime_sleep, dtime_till_expected, dtime_now,\n\t\tg_dtime_expected);\n#endif\n\n\tg_dtime_this_vbl_array[prev_vbl_index] = dtime_this_vbl;\n\tg_dtime_exp_array[prev_vbl_index] = g_dtime_expected;\n\tg_dtime_pmhz_array[prev_vbl_index] = predicted_pmhz;\n\tg_dtime_eff_pmhz_array[prev_vbl_index] = eff_pmhz;\n\n\n\tif(g_c041_val & C041_EN_VBL_INTS) {\n\t\tadd_event_vbl();\n\t}\n\n\tg_25sec_cntr++;\n\tif(g_25sec_cntr >= 16) {\n\t\tg_25sec_cntr = 0;\n\t\tif(g_c041_val & C041_EN_25SEC_INTS) {\n\t\t\tadd_irq(IRQ_PENDING_C046_25SEC);\n\t\t\tg_c046_val |= 0x10;\n\t\t\tirq_printf(\"Setting c046 .25 sec int, g_irq_pend:%d\\n\",\n\t\t\t\t\t\tg_irq_pending);\n\t\t}\n\t}\n\n\tg_1sec_cntr++;\n\tif(g_1sec_cntr >= 60) {\n\t\tg_1sec_cntr = 0;\n\t\ttmp = g_c023_val;\n\t\ttmp |= 0x40;\t/* set 1sec int */\n\t\tif(tmp & 0x04) {\n\t\t\ttmp |= 0x80;\n\t\t\tadd_irq(IRQ_PENDING_C023_1SEC);\n\t\t\tirq_printf(\"Setting c023 to %02x irq_pend: %d\\n\",\n\t\t\t\ttmp, g_irq_pending);\n\t\t}\n\t\tg_c023_val = tmp;\n\t}\n\n\tif(!g_scan_int_events) {\n\t\tcheck_scan_line_int(0);\n\t}\n\n\tdoit_3_persec = 0;\n\tif(g_config_iwm_vbl_count > 0) {\n\t\tg_config_iwm_vbl_count--;\n\t} else {\n\t\tg_config_iwm_vbl_count = 20;\n\t\tdoit_3_persec = 1;\n\t}\n\n\tiwm_vbl_update();\n\tconfig_vbl_update(doit_3_persec);\n\n\tsound_update(dfcyc);\n\tclock_update();\n\tscc_update(dfcyc);\n\tpaddle_update_buttons();\n}\n\nvoid\ndo_vbl_int()\n{\n\tif(g_c041_val & C041_EN_VBL_INTS) {\n\t\tg_c046_val |= 0x08;\n\t\tadd_irq(IRQ_PENDING_C046_VBL);\n\t\tirq_printf(\"Setting c046 vbl_int_status to 1, irq_pend: %d\\n\",\n\t\t\tg_irq_pending);\n\t}\n}\n\nvoid\ndo_scan_int(dword64 dfcyc, int line)\n{\n\tint\tc023_val;\n\n\tif(dfcyc) {\n\t\t// Avoid unused param warning\n\t}\n\n\tg_scan_int_events = 0;\n\tg_dfcyc_scan_int = 0;\n\n\tc023_val = g_c023_val;\n\tif(c023_val & 0x20) {\n\t\thalt_printf(\"c023 scan_int and another on line %03x\\n\", line);\n\t}\n\n#if 0\n\tdvbl = (dfcyc >> 16) / 17030;\n\tdline = ((dfcyc >> 16) - (dvbl * 17030)) / 65;\n\tprintf(\"do_scan_int at time %lld (%lld,line %lld), line:%d, SCB:%02x, \"\n\t\t\"a2_stat_ok:%d, c023:%02x\\n\", dfcyc >> 16, dvbl, dline, line,\n\t\t(g_slow_memory_ptr[0x19d00 + line] & 0x40),\n\t\t(g_cur_a2_stat & ALL_STAT_SUPER_HIRES) != 0, c023_val);\n\tfor(i = 0; i < 200; i++) {\n\t\tif(g_slow_memory_ptr[0x19d00 + i] & 0x40) {\n\t\t\tprintf(\"  Line %d has SCB:%02x\\n\", i,\n\t\t\t\tg_slow_memory_ptr[0x19d00 + i]);\n\t\t}\n\t}\n#endif\n\n\t/* make sure scan int is still enabled for this line */\n\tif((g_slow_memory_ptr[0x19d00 + line] & 0x40) &&\n\t\t\t\t(g_cur_a2_stat & ALL_STAT_SUPER_HIRES)) {\n\t\t/* valid interrupt, do it */\n\t\tc023_val |= 0xa0;\t/* vgc_int and scan_int */\n\t\tif(c023_val & 0x02) {\n\t\t\tadd_irq(IRQ_PENDING_C023_SCAN);\n\t\t\tirq_printf(\"Setting c023 to %02x, irq_pend: %d\\n\",\n\t\t\t\tc023_val, g_irq_pending);\n\t\t}\n\t\tg_c023_val = c023_val;\n\t\tHALT_ON(HALT_ON_SCAN_INT, \"In do_scan_int\\n\");\n\t} else {\n\t\t/* scan int bit cleared on scan line control byte */\n\t\t/* look for next line, if any */\n\t\tcheck_scan_line_int(line+1);\n\t}\n}\n\nvoid\ncheck_scan_line_int(int cur_video_line)\n{\n\tdword64\tddelay;\n\tint\tstart, line;\n\tint\ti;\n\n\t/* Called during VBL interrupt phase */\n\n\tif(!(g_cur_a2_stat & ALL_STAT_SUPER_HIRES)) {\n\t\treturn;\n\t}\n\n\tif(g_c023_val & 0x20) {\n\t\t/* don't check for any more */\n\t\treturn;\n\t}\n\n\tstart = cur_video_line;\n\tif(start < 0) {\n\t\thalt_printf(\"check_scan_line_int: cur_video_line: %d\\n\",\n\t\t\tcur_video_line);\n\t\tstart = 0;\n\t}\n\n\tfor(line = start; line < 200; line++) {\n\t\ti = line;\n\n\t\tif(i < 0 || i >= 200) {\n\t\t\thalt_printf(\"check_new_scan_int:i:%d, line:%d, st:%d\\n\",\n\t\t\t\ti, line, start);\n\t\t\ti = 0;\n\t\t}\n\t\tif(g_slow_memory_ptr[0x19d00 + i] & 0x40) {\n\t\t\tirq_printf(\"Adding scan_int for line %d\\n\", i);\n\t\t\tddelay = (65ULL * line) << 16;\n\t\t\tadd_event_scan_int(g_last_vbl_dfcyc + ddelay, line);\n\t\t\tg_scan_int_events = 1;\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid\ncheck_for_new_scan_int(dword64 dfcyc)\n{\n\tint\tcur_video_line;\n\n\tcur_video_line = get_lines_since_vbl(dfcyc) >> 8;\n\tcheck_scan_line_int(cur_video_line);\n}\n\nvoid\nscb_changed(dword64 dfcyc, word32 addr, word32 new_val, word32 old_val)\n{\n\tif(new_val & (~old_val) & 0x40) {\n\t\tcheck_for_new_scan_int(dfcyc);\n\t}\n\tif(addr) {\n\t}\n}\n\nvoid\ninit_reg()\n{\n\tmemset(&engine, 0, sizeof(engine));\n\n\tengine.acc = 0;\n\tengine.xreg = 0;\n\tengine.yreg = 0;\n\tengine.stack = 0x1ff;\n\tengine.direct = 0;\n\tengine.psr = 0x134;\n\tengine.fplus_ptr = 0;\n}\n\nvoid\nhandle_action(word32 ret)\n{\n\tint\ttype, arg;\n\n\ttype = ret & 0xff;\n\targ = ret >> 8;\n\tswitch(type) {\n\tcase RET_BREAK:\n\t\tdo_break(arg);\n\t\tbreak;\n\tcase RET_COP:\n\t\tdo_cop(arg);\n\t\tbreak;\n\tcase RET_IRQ:\n\t\tirq_printf(\"Special fast IRQ response.  irq_pending: %x\\n\",\n\t\t\tg_irq_pending);\n\t\tbreak;\n\tcase RET_WDM:\n\t\tdo_wdm(arg);\n\t\tbreak;\n\tcase RET_STP:\n\t\tdo_stp();\n\t\tbreak;\n\tcase RET_TOOLTRACE:\n\t\tdbg_log_info(g_cur_dfcyc, engine.kpc, engine.xreg,\n\t\t\t\t\t\t(engine.stack << 16) | 0xe100);\n\t\tbreak;\n\tdefault:\n\t\thalt_printf(\"Unknown special action: %08x!\\n\", ret);\n\t}\n}\n\n\nvoid\ndo_break(word32 ret)\n{\n\tprintf(\"I think I got a break, second byte: %02x!\\n\", ret);\n\tprintf(\"kpc: %06x\\n\", engine.kpc);\n\n\thalt_printf(\"do_break, kpc: %06x\\n\", engine.kpc);\n}\n\nvoid\ndo_cop(word32 ret)\n{\n\thalt_printf(\"COP instr %02x!\\n\", ret);\n\tfflush(stdout);\n}\n\nvoid\ndo_wdm(word32 arg)\n{\n\tif(arg == 0x00c7) {\n\t\t// WDM, 0xc7, 0x00: WDM in Slot 7\n\t\tif(engine.psr & 0x40) {\n\t\t\t// Overflow set: $C700 called\n\t\t\tdo_c700(arg);\n\t\t} else if(engine.psr & 1) {\t// V=0, C=1: $C70D called\n\t\t\tdo_c70d(arg);\n\t\t} else {\t\t\t// V=0, C=0, $C70A called\n\t\t\tdo_c70a(arg);\n\t\t}\n\t\treturn;\n\t}\n\tif(arg == 0x00ea) {\n\t\t// WDM, 0xea, 0x00: WDM emulator ID\n\t\tdo_wdm_emulator_id();\n\t\treturn;\n\t}\n\tif((arg == 0xeaea) && ((engine.psr & 0x171) == 0x41) &&\n\t\t\t\t\t\t(engine.acc == 0x4d44)) {\n\t\t// WDM $EA,$EA with V=1,C=1 and ACC=0x4d44 (\"EM\")\n\t\tengine.psr = engine.psr & 0x1bf;\t// V=0\n\t\t// printf(\"WDM $EA,$EA, cleared V=0, psr:%04x\\n\", engine.psr);\n\t\treturn;\n\t}\n\n\tswitch(arg & 0xff) {\n\tcase 0x8d: /* Bouncin Ferno does WDM 8d */\n\t\tbreak;\n\tcase 0xea:\t// Detectiong feature, don't flag an error\n\t\tbreak;\n\tcase 0xfc:\t// HOST.FST \"head_call\" for ATINIT for ProDOS 8\n\tcase 0xfd:\t// HOST.FST \"tail_call\" for ATINIT for ProDOS 8\n\tcase 0xff:\t// HOST.FST \"call_host\" for GS/OS driver\n\t\tbreak;\n\tdefault:\n\t\thalt_printf(\"do_wdm: %04x!\\n\", arg);\n\t}\n}\n\nvoid\ndo_wai()\n{\n\thalt_printf(\"do_wai!\\n\");\n}\n\nvoid\ndo_stp()\n{\n\tif(!g_stp_pending) {\n\t\tg_stp_pending = 1;\n\t\thalt_printf(\"Hit STP instruction at: %06x, press RESET to \"\n\t\t\t\t\"continue\\n\", engine.kpc);\n\t}\n}\n\nchar g_emulator_name[64];\n\nvoid\ndo_wdm_emulator_id()\n{\n\tword32\taddr, version, subvers;\n\tint\tmaxlen, len, c, got_dot;\n\tint\ti;\n\n\t// WDM, $EA, $00: WDM emulator ID\n\t// dbank.acc = address to write emulator description string\n\t// X = size of buffer to hold string\n\t// Y = 0 (not checked)\n\t// Returns: X: actual length of emulator string (always <=\n\t//\tvalue in X at call)\n\t// ACC: emulator version as: $VVMN as BCD, so 1.32 is $0132\n\t// Y: Emulation feature flags.  bit 0: $c06c-$c06f timer available\n\t// Works in emulation mode\n\n\tprintf(\"WDM EA at %06x.  acc:%04x, dbank:%02x xreg:%04x\\n\", engine.kpc,\n\t\t\t\tengine.acc, engine.dbank, engine.xreg);\n\tmaxlen = engine.xreg;\n\tcfg_strncpy(&g_emulator_name[0], \"KEGS v\", 64);\n\tcfg_strlcat(&g_emulator_name[0], &g_kegs_version_str[0], 64);\n\tlen = (int)strlen(&g_emulator_name[0]);\n\taddr = engine.acc;\n\tengine.xreg = 0;\n\tfor(i = 0; i < len; i++) {\n\t\tif(i >= maxlen) {\n\t\t\tbreak;\n\t\t}\n\t\taddr = (engine.dbank << 8) | (addr & 0xffff);\n\t\tset_memory_c(addr, 0x80 | g_emulator_name[i], 1);\n\t\taddr++;\n\t\tengine.xreg = i + 1;\n\t}\n\n\tversion = 0;\n\tsubvers = 0;\n\tlen = (int)strlen(&g_kegs_version_str[0]);\n\tgot_dot = 0;\n\tfor(i = 0; i < len; i++) {\n\t\tc = g_kegs_version_str[i];\n\t\tif(c == '.') {\n\t\t\tgot_dot++;\n\t\t}\n\t\tif(got_dot >= 3) {\n\t\t\tbreak;\n\t\t}\n\t\tif((c >= '0') && (c <= '9')) {\n\t\t\tc = c - '0';\n\t\t\tif(got_dot) {\n\t\t\t\tsubvers = (subvers << 4) | c;\n\t\t\t\tgot_dot++;\n\t\t\t} else {\n\t\t\t\tversion = (version << 4) | c;\n\t\t\t}\n\t\t}\n\t}\n\n\tengine.acc = ((version & 0xff) << 8) | (subvers & 0xff);\n\tengine.yreg = 0x01;\t\t// $C06C timer available\n}\n\nvoid\nsize_fail(int val, word32 v1, word32 v2)\n{\n\thalt_printf(\"Size failure, val: %08x, %08x %08x\\n\", val, v1, v2);\n}\n\nint\nfatal_printf(const char *fmt, ...)\n{\n\tva_list\tap;\n\tint\tret;\n\n\tva_start(ap, fmt);\n\n\tif(g_fatal_log < 0) {\n\t\tg_fatal_log = 0;\n\t}\n\tret = kegs_vprintf(fmt, ap);\n\tva_end(ap);\n\n\treturn ret;\n}\n\nint\nkegs_vprintf(const char *fmt, va_list ap)\n{\n\tchar\t*bufptr, *buf2ptr;\n\tint\tlen, ret;\n\n\tbufptr = malloc(4096);\n\tret = vsnprintf(bufptr, 4090, fmt, ap);\n\n\tlen = (int)strlen(bufptr);\n\tif(g_fatal_log >= 0 && g_fatal_log < MAX_FATAL_LOGS) {\n\t\tbuf2ptr = malloc(len+1);\n\t\tmemcpy(buf2ptr, bufptr, len+1);\n\t\tg_fatal_log_strs[g_fatal_log++] = buf2ptr;\n\t}\n\t(void)must_write(1, (byte *)bufptr, len);\n\tif(g_debug_file_fd >= 0) {\n\t\t(void)must_write(g_debug_file_fd, (byte *)bufptr, len);\n\t}\n\tfree(bufptr);\n\n\treturn ret;\n}\n\ndword64\nmust_write(int fd, byte *bufptr, dword64 dsize)\n{\n\tdword64\tdlen;\n\tlong long ret;\n\tword32\tthis_len;\n\n\tdlen = dsize;\n\twhile(dlen != 0) {\n\t\t// Support Windows64, which can only rd/wr 2GB max per call\n\t\tthis_len = (1UL << 30);\n\t\tif(dlen < this_len) {\n\t\t\tthis_len = (word32)dlen;\n\t\t}\n\t\tret = write(fd, bufptr, this_len);\n\t\tif(ret >= 0) {\n\t\t\tdlen -= ret;\n\t\t\tbufptr += ret;\n\t\t} else if((errno != EAGAIN) && (errno != EINTR)) {\n\t\t\treturn 0;\t\t// just get out\n\t\t}\n\t}\n\treturn dsize;\n}\n\nvoid\nclear_fatal_logs()\n{\n\tint\ti;\n\n\tfor(i = 0; i < g_fatal_log; i++) {\n\t\tfree(g_fatal_log_strs[i]);\n\t\tg_fatal_log_strs[i] = 0;\n\t}\n\tg_fatal_log = -1;\n}\n\nchar *\nkegs_malloc_str(const char *in_str)\n{\n\tchar\t*str;\n\tint\tlen;\n\n\tlen = (int)strlen(in_str) + 1;\n\tstr = malloc(len);\n\tmemcpy(str, in_str, len);\n\n\treturn str;\n}\n\ndword64\nkegs_lseek(int fd, dword64 offs, int whence)\n{\n#ifdef _WIN32\n\treturn _lseeki64(fd, offs, whence);\n#else\n\treturn lseek(fd, offs, whence);\n#endif\n}\n\n"
  },
  {
    "path": "gsplus/src/size_c.h",
    "content": "// \"@(#)$KmKId: size_c.h,v 1.2 2023-11-12 15:32:17+00 kentd Exp $\"\n/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2020 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n\n\t0x1,\t/* 00 */\t/* brk */\n\t0x1,\t/* 01 */\t/* ORA (Dloc,X) */\n\t0x1,\t/* 02 */\t/* COP */\n\t0x1,\t/* 03 */\t/* ORA Disp8,S */\n\t0x1,\t/* 04 */\t/* TSB Dloc */\n\t0x1,\t/* 05 */\t/* ORA Dloc */\n\t0x1,\t/* 06 */\t/* ASL Dloc */\n\t0x1,\t/* 07 */\t/* ORA [Dloc] */\n\t0x0,\t/* 08 */\t/* PHP */\n\t0x4,\t/* 09 */\t/* ORA #imm */\n\t0x0,\t/* 0a */\t/* ASL a */\n\t0x0,\t/* 0b */\t/* PHD */\n\t0x2,\t/* 0c */\t/* TSB abs */\n\t0x2,\t/* 0d */\t/* ORA abs */\n\t0x2,\t/* 0e */\t/* ASL abs */\n\t0x3,\t/* 0f */\t/* ORA long */\n\t0x1,\t/* 10 */\t/* BPL disp8 */\n\t0x1,\t/* 11 */\t/* ORA (),y */\n\t0x1,\t/* 12 */\t/* ORA () */\n\t0x1,\t/* 13 */\t/* ORA (disp8,s),y */\n\t0x1,\t/* 14 */\t/* TRB Dloc */\n\t0x1,\t/* 15 */\t/* ORA Dloc,x */\n\t0x1,\t/* 16 */\t/* ASL Dloc,x */\n\t0x1,\t/* 17 */\t/* ORA [],y */\n\t0x0,\t/* 18 */\t/* clc */\n\t0x2,\t/* 19 */\t/* ORA abs,y */\n\t0x0,\t/* 1a */\t/* INC a */\n\t0x0,\t/* 1b */\t/* TCS */\n\t0x2,\t/* 1c */\t/* TRB Abs */\n\t0x2,\t/* 1d */\t/* ORA Abs,X */\n\t0x2,\t/* 1e */\t/* ASL abs,x */\n\t0x3,\t/* 1f */\t/* ORA Long,x */\n\t0x2,\t/* 20 */\t/* JSR abs */\n\t0x1,\t/* 21 */\t/* AND (Dloc,X) */\n\t0x3,\t/* 22 */\t/* JSL Abslong */\n\t0x1,\t/* 23 */\t/* AND Disp8,S */\n\t0x1,\t/* 24 */\t/* BIT Dloc */\n\t0x1,\t/* 25 */\t/* AND Dloc */\n\t0x1,\t/* 26 */\t/* ROL Dloc */\n\t0x1,\t/* 27 */\t/* AND [Dloc] */\n\t0x0,\t/* 28 */\t/* PLP */\n\t0x4,\t/* 29 */\t/* AND #imm */\n\t0x0,\t/* 2a */\t/* ROL a */\n\t0x0,\t/* 2b */\t/* PLD */\n\t0x2,\t/* 2c */\t/* BIT abs */\n\t0x2,\t/* 2d */\t/* AND abs */\n\t0x2,\t/* 2e */\t/* ROL abs */\n\t0x3,\t/* 2f */\t/* AND long */\n\t0x1,\t/* 30 */\t/* BMI disp8 */\n\t0x1,\t/* 31 */\t/* AND (),y */\n\t0x1,\t/* 32 */\t/* AND () */\n\t0x1,\t/* 33 */\t/* AND (disp8,s),y */\n\t0x1,\t/* 34 */\t/* BIT Dloc,X */\n\t0x1,\t/* 35 */\t/* AND Dloc,x */\n\t0x1,\t/* 36 */\t/* ROL Dloc,x */\n\t0x1,\t/* 37 */\t/* AND [],y */\n\t0x0,\t/* 38 */\t/* SEC */\n\t0x2,\t/* 39 */\t/* AND abs,y */\n\t0x0,\t/* 3a */\t/* DEC a */\n\t0x0,\t/* 3b */\t/* TSC */\n\t0x2,\t/* 3c */\t/* BIT Abs,X */\n\t0x2,\t/* 3d */\t/* AND Abs,X */\n\t0x2,\t/* 3e */\t/* ROL abs,x */\n\t0x3,\t/* 3f */\t/* AND Long,x */\n\t0x0,\t/* 40 */\t/* RTI */\n\t0x1,\t/* 41 */\t/* EOR (Dloc,X) */\n\t0x2,\t/* 42 */\t/* WDM HACK: uses 2 args */\n\t0x1,\t/* 43 */\t/* EOR Disp8,S */\n\t0x2,\t/* 44 */\t/* MVP I,J */\n\t0x1,\t/* 45 */\t/* EOR Dloc */\n\t0x1,\t/* 46 */\t/* LSR Dloc */\n\t0x1,\t/* 47 */\t/* EOR [Dloc] */\n\t0x0,\t/* 48 */\t/* PHA */\n\t0x4,\t/* 49 */\t/* EOR #imm */\n\t0x0,\t/* 4a */\t/* LSR a */\n\t0x0,\t/* 4b */\t/* PHK */\n\t0x2,\t/* 4c */\t/* JMP abs */\n\t0x2,\t/* 4d */\t/* EOR abs */\n\t0x2,\t/* 4e */\t/* LSR abs */\n\t0x3,\t/* 4f */\t/* EOR long */\n\t0x1,\t/* 50 */\t/* BVC disp8 */\n\t0x1,\t/* 51 */\t/* EOR (),y */\n\t0x1,\t/* 52 */\t/* EOR () */\n\t0x1,\t/* 53 */\t/* EOR (disp8,s),y */\n\t0x2,\t/* 54 */\t/* MVN I,J */\n\t0x1,\t/* 55 */\t/* EOR Dloc,x */\n\t0x1,\t/* 56 */\t/* LSR Dloc,x */\n\t0x1,\t/* 57 */\t/* EOR [],y */\n\t0x0,\t/* 58 */\t/* CLI */\n\t0x2,\t/* 59 */\t/* EOR abs,y */\n\t0x0,\t/* 5a */\t/* PHY */\n\t0x0,\t/* 5b */\t/* TCD */\n\t0x3,\t/* 5c */\t/* JMP Long */\n\t0x2,\t/* 5d */\t/* EOR Abs,X */\n\t0x2,\t/* 5e */\t/* LSR abs,x */\n\t0x3,\t/* 5f */\t/* EOR Long,x */\n\t0x0,\t/* 60 */\t/* RTS */\n\t0x1,\t/* 61 */\t/* ADC (Dloc,X) */\n\t0x2,\t/* 62 */\t/* PER DISP16 */\n\t0x1,\t/* 63 */\t/* ADC Disp8,S */\n\t0x1,\t/* 64 */\t/* STZ Dloc */\n\t0x1,\t/* 65 */\t/* ADC Dloc */\n\t0x1,\t/* 66 */\t/* ROR Dloc */\n\t0x1,\t/* 67 */\t/* ADC [Dloc] */\n\t0x0,\t/* 68 */\t/* PLA */\n\t0x4,\t/* 69 */\t/* ADC #imm */\n\t0x0,\t/* 6a */\t/* ROR a */\n\t0x0,\t/* 6b */\t/* RTL */\n\t0x2,\t/* 6c */\t/* JMP (abs) */\n\t0x2,\t/* 6d */\t/* ADC abs */\n\t0x2,\t/* 6e */\t/* ROR abs */\n\t0x3,\t/* 6f */\t/* ADC long */\n\t0x1,\t/* 70 */\t/* BVS disp8 */\n\t0x1,\t/* 71 */\t/* ADC (),y */\n\t0x1,\t/* 72 */\t/* ADC () */\n\t0x1,\t/* 73 */\t/* ADC (disp8,s),y */\n\t0x1,\t/* 74 */\t/* STZ Dloc,X */\n\t0x1,\t/* 75 */\t/* ADC Dloc,x */\n\t0x1,\t/* 76 */\t/* ROR Dloc,x */\n\t0x1,\t/* 77 */\t/* ADC [],y */\n\t0x0,\t/* 78 */\t/* SEI */\n\t0x2,\t/* 79 */\t/* ADC abs,y */\n\t0x0,\t/* 7a */\t/* PLY */\n\t0x0,\t/* 7b */\t/* TDC */\n\t0x2,\t/* 7c */\t/* JMP (abs,x) */\n\t0x2,\t/* 7d */\t/* ADC Abs,X */\n\t0x2,\t/* 7e */\t/* ROR abs,x */\n\t0x3,\t/* 7f */\t/* ADC Long,x */\n\t0x1,\t/* 80 */\t/* BRA Disp8 */\n\t0x1,\t/* 81 */\t/* STA (Dloc,X) */\n\t0x2,\t/* 82 */\t/* BRL DISP16 */\n\t0x1,\t/* 83 */\t/* STA Disp8,S */\n\t0x1,\t/* 84 */\t/* STY Dloc */\n\t0x1,\t/* 85 */\t/* STA Dloc */\n\t0x1,\t/* 86 */\t/* STX Dloc */\n\t0x1,\t/* 87 */\t/* STA [Dloc] */\n\t0x0,\t/* 88 */\t/* DEY */\n\t0x4,\t/* 89 */\t/* BIT #imm */\n\t0x0,\t/* 8a */\t/* TXA */\n\t0x0,\t/* 8b */\t/* PHB */\n\t0x2,\t/* 8c */\t/* STY abs */\n\t0x2,\t/* 8d */\t/* STA abs */\n\t0x2,\t/* 8e */\t/* STX abs */\n\t0x3,\t/* 8f */\t/* STA long */\n\t0x1,\t/* 90 */\t/* BCC disp8 */\n\t0x1,\t/* 91 */\t/* STA (),y */\n\t0x1,\t/* 92 */\t/* STA () */\n\t0x1,\t/* 93 */\t/* STA (disp8,s),y */\n\t0x1,\t/* 94 */\t/* STY Dloc,X */\n\t0x1,\t/* 95 */\t/* STA Dloc,x */\n\t0x1,\t/* 96 */\t/* STX Dloc,y */\n\t0x1,\t/* 97 */\t/* STA [],y */\n\t0x0,\t/* 98 */\t/* TYA */\n\t0x2,\t/* 99 */\t/* STA abs,y */\n\t0x0,\t/* 9a */\t/* TXS */\n\t0x0,\t/* 9b */\t/* TXY */\n\t0x2,\t/* 9c */\t/* STX abs */\n\t0x2,\t/* 9d */\t/* STA Abs,X */\n\t0x2,\t/* 9e */\t/* STZ abs,x */\n\t0x3,\t/* 9f */\t/* STA Long,x */\n\t0x5,\t/* a0 */\t/* LDY #imm */\n\t0x1,\t/* a1 */\t/* LDA (Dloc,X) */\n\t0x5,\t/* a2 */\t/* LDX #imm */\n\t0x1,\t/* a3 */\t/* LDA Disp8,S */\n\t0x1,\t/* a4 */\t/* LDY Dloc */\n\t0x1,\t/* a5 */\t/* LDA Dloc */\n\t0x1,\t/* a6 */\t/* LDX Dloc */\n\t0x1,\t/* a7 */\t/* LDA [Dloc] */\n\t0x0,\t/* a8 */\t/* TAY */\n\t0x4,\t/* a9 */\t/* LDA #imm */\n\t0x0,\t/* aa */\t/* TAX */\n\t0x0,\t/* ab */\t/* PLB */\n\t0x2,\t/* ac */\t/* LDY abs */\n\t0x2,\t/* ad */\t/* LDA abs */\n\t0x2,\t/* ae */\t/* LDX abs */\n\t0x3,\t/* af */\t/* LDA long */\n\t0x1,\t/* b0 */\t/* BCS disp8 */\n\t0x1,\t/* b1 */\t/* LDA (),y */\n\t0x1,\t/* b2 */\t/* LDA () */\n\t0x1,\t/* b3 */\t/* LDA (disp8,s),y */\n\t0x1,\t/* b4 */\t/* LDY Dloc,X */\n\t0x1,\t/* b5 */\t/* LDA Dloc,x */\n\t0x1,\t/* b6 */\t/* LDX Dloc,y */\n\t0x1,\t/* b7 */\t/* LDA [],y */\n\t0x0,\t/* b8 */\t/* CLV */\n\t0x2,\t/* b9 */\t/* LDA abs,y */\n\t0x0,\t/* ba */\t/* TSX */\n\t0x0,\t/* bb */\t/* TYX */\n\t0x2,\t/* bc */\t/* LDY abs,x */\n\t0x2,\t/* bd */\t/* LDA Abs,X */\n\t0x2,\t/* be */\t/* LDX abs,y */\n\t0x3,\t/* bf */\t/* LDA Long,x */\n\t0x5,\t/* c0 */\t/* CPY #Imm */\n\t0x1,\t/* c1 */\t/* CMP (Dloc,X) */\n\t0x1,\t/* c2 */\t/* REP #8bit */\n\t0x1,\t/* c3 */\t/* CMP Disp8,S */\n\t0x1,\t/* c4 */\t/* CPY Dloc */\n\t0x1,\t/* c5 */\t/* CMP Dloc */\n\t0x1,\t/* c6 */\t/* DEC Dloc */\n\t0x1,\t/* c7 */\t/* CMP [Dloc] */\n\t0x0,\t/* c8 */\t/* INY */\n\t0x4,\t/* c9 */\t/* CMP #imm */\n\t0x0,\t/* ca */\t/* DEX */\n\t0x0,\t/* cb */\t/* WAI */\n\t0x2,\t/* cc */\t/* CPY abs */\n\t0x2,\t/* cd */\t/* CMP abs */\n\t0x2,\t/* ce */\t/* DEC abs */\n\t0x3,\t/* cf */\t/* CMP long */\n\t0x1,\t/* d0 */\t/* BNE disp8 */\n\t0x1,\t/* d1 */\t/* CMP (),y */\n\t0x1,\t/* d2 */\t/* CMP () */\n\t0x1,\t/* d3 */\t/* CMP (disp8,s),y */\n\t0x1,\t/* d4 */\t/* PEI Dloc */\n\t0x1,\t/* d5 */\t/* CMP Dloc,x */\n\t0x1,\t/* d6 */\t/* DEC Dloc,x */\n\t0x1,\t/* d7 */\t/* CMP [],y */\n\t0x0,\t/* d8 */\t/* CLD */\n\t0x2,\t/* d9 */\t/* CMP abs,y */\n\t0x0,\t/* da */\t/* PHX */\n\t0x0,\t/* db */\t/* STP */\n\t0x2,\t/* dc */\t/* JML (Abs) */\n\t0x2,\t/* dd */\t/* CMP Abs,X */\n\t0x2,\t/* de */\t/* DEC abs,x */\n\t0x3,\t/* df */\t/* CMP Long,x */\n\t0x5,\t/* e0 */\t/* CPX #Imm */\n\t0x1,\t/* e1 */\t/* SBC (Dloc,X) */\n\t0x1,\t/* e2 */\t/* SEP #8bit */\n\t0x1,\t/* e3 */\t/* SBC Disp8,S */\n\t0x1,\t/* e4 */\t/* CPX Dloc */\n\t0x1,\t/* e5 */\t/* SBC Dloc */\n\t0x1,\t/* e6 */\t/* INC Dloc */\n\t0x1,\t/* e7 */\t/* SBC [Dloc] */\n\t0x0,\t/* e8 */\t/* INX */\n\t0x4,\t/* e9 */\t/* SBC #imm */\n\t0x0,\t/* ea */\t/* NOP */\n\t0x0,\t/* eb */\t/* XBA */\n\t0x2,\t/* ec */\t/* CPX abs */\n\t0x2,\t/* ed */\t/* SBC abs */\n\t0x2,\t/* ee */\t/* INC abs */\n\t0x3,\t/* ef */\t/* SBC long */\n\t0x1,\t/* f0 */\t/* BEQ disp8 */\n\t0x1,\t/* f1 */\t/* SBC (),y */\n\t0x1,\t/* f2 */\t/* SBC () */\n\t0x1,\t/* f3 */\t/* SBC (disp8,s),y */\n\t0x2,\t/* f4 */\t/* PEA Imm */\n\t0x1,\t/* f5 */\t/* SBC Dloc,x */\n\t0x1,\t/* f6 */\t/* INC Dloc,x */\n\t0x1,\t/* f7 */\t/* SBC [],y */\n\t0x0,\t/* f8 */\t/* SED */\n\t0x2,\t/* f9 */\t/* SBC abs,y */\n\t0x0,\t/* fa */\t/* PLX */\n\t0x0,\t/* fb */\t/* XCE */\n\t0x2,\t/* fc */\t/* JSR (Abs,x) */\n\t0x2,\t/* fd */\t/* SBC Abs,X */\n\t0x2,\t/* fe */\t/* INC abs,x */\n\t0x3,\t/* ff */\t/* SBC Long,x */\n\n"
  },
  {
    "path": "gsplus/src/smartport.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2024 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include \"defc.h\"\n\nextern int Verbose;\nextern int Halt_on;\nextern int g_rom_version;\nextern int g_io_amt;\nextern int g_highest_smartport_unit;\nextern dword64 g_cur_dfcyc;\n\nextern Engine_reg engine;\n\nextern Iwm g_iwm;\n\n#define LEN_SMPT_LOG\t16\nSTRUCT(Smpt_log) {\n\tword32\tstart_addr;\n\tint\tcmd;\n\tint\trts_addr;\n\tint\tcmd_list;\n\tint\textras;\n\tint\tunit;\n\tint\tbuf;\n\tint\tblk;\n};\n\nSmpt_log g_smpt_log[LEN_SMPT_LOG];\nint\tg_smpt_log_pos = 0;\n\nvoid\nsmartport_error(void)\n{\n\tint\tpos;\n\tint\ti;\n\n\tpos = g_smpt_log_pos;\n\tprintf(\"Smartport log pos: %d\\n\", pos);\n\tfor(i = 0; i < LEN_SMPT_LOG; i++) {\n\t\tpos--;\n\t\tif(pos < 0) {\n\t\t\tpos = LEN_SMPT_LOG - 1;\n\t\t}\n\t\tprintf(\"%d:%d: t:%04x, cmd:%02x, rts:%04x, \"\n\t\t\t\"cmd_l:%04x, x:%d, unit:%d, buf:%04x, blk:%04x\\n\",\n\t\t\ti, pos,\n\t\t\tg_smpt_log[pos].start_addr,\n\t\t\tg_smpt_log[pos].cmd,\n\t\t\tg_smpt_log[pos].rts_addr,\n\t\t\tg_smpt_log[pos].cmd_list,\n\t\t\tg_smpt_log[pos].extras,\n\t\t\tg_smpt_log[pos].unit,\n\t\t\tg_smpt_log[pos].buf,\n\t\t\tg_smpt_log[pos].blk);\n\t}\n}\nvoid\nsmartport_log(word32 start_addr, word32 cmd, word32 rts_addr, word32 cmd_list)\n{\n\tint\tpos;\n\n\tpos = g_smpt_log_pos;\n\tif(start_addr != 0) {\n\t\tg_smpt_log[pos].start_addr = start_addr;\n\t\tg_smpt_log[pos].cmd = cmd;\n\t\tg_smpt_log[pos].rts_addr = rts_addr;\n\t\tg_smpt_log[pos].cmd_list = cmd_list;\n\t\tg_smpt_log[pos].extras = 0;\n\t\tg_smpt_log[pos].unit = 0;\n\t\tg_smpt_log[pos].buf = 0;\n\t\tg_smpt_log[pos].blk = 0;\n\t} else {\n\t\tpos--;\n\t\tif(pos < 0) {\n\t\t\tpos = LEN_SMPT_LOG - 1;\n\t\t}\n\t\tg_smpt_log[pos].extras = 1;\n\t\tg_smpt_log[pos].unit = cmd;\n\t\tg_smpt_log[pos].buf = rts_addr;\n\t\tg_smpt_log[pos].blk = cmd_list;\n\t}\n\tpos++;\n\tif(pos >= LEN_SMPT_LOG) {\n\t\tpos = 0;\n\t}\n\tg_smpt_log_pos = pos;\n}\n\nvoid\ndo_c70d(word32 arg0)\n{\n\tdword64\tdsize;\n\tword32\tstatus_ptr, rts_addr, cmd_list,\tcmd_list_lo, cmd_list_mid;\n\tword32\tcmd_list_hi, status_ptr_lo, status_ptr_mid, status_ptr_hi;\n\tword32\trts_lo, rts_hi, buf_ptr_lo, buf_ptr_hi, buf_ptr, mask, cmd;\n\tword32\tblock_lo, block_mid, block_hi, block_hi2, unit, ctl_code;\n\tword32\tctl_ptr_lo, ctl_ptr_hi, ctl_ptr, block, stat_val;\n\tint\tparam_cnt, ret, ext, slot;\n\tint\ti;\n\n\tslot = (engine.kpc >> 8) & 7;\n\tset_memory_c(0x7f8, 0xc0 | slot, 1);\n\n\tif((engine.psr & 0x100) == 0) {\n\t\tdisk_printf(\"c70d %02x called in native mode!\\n\", arg0);\n\t\tif((engine.psr & 0x30) != 0x30) {\n\t\t\thalt_printf(\"c70d called native, psr: %03x!\\n\",\n\t\t\t\t\t\t\tengine.psr);\n\t\t}\n\t}\n\n\tengine.stack = ((engine.stack + 1) & 0xff) + 0x100;\n\trts_lo = get_memory_c(engine.stack);\n\tengine.stack = ((engine.stack + 1) & 0xff) + 0x100;\n\trts_hi = get_memory_c(engine.stack);\n\trts_addr = (rts_lo + (256*rts_hi) + 1) & 0xffff;\n\tdisk_printf(\"rts_addr: %04x\\n\", rts_addr);\n\n\tcmd = get_memory_c(rts_addr);\n\tcmd_list_lo = get_memory_c((rts_addr + 1) & 0xffff);\n\tcmd_list_mid = get_memory_c((rts_addr + 2) & 0xffff);\n\tcmd_list_hi = 0;\n\tmask = 0xffff;\n\text = 0;\n\tif(cmd & 0x40) {\n\t\text = 2;\n\t\tmask = 0xffffff;\n\t\tcmd_list_hi = get_memory_c((rts_addr + 3) & 0xffff);\n\t}\n\n\tcmd_list = cmd_list_lo + (256*cmd_list_mid) + (65536*cmd_list_hi);\n\n\tdisk_printf(\"cmd: %02x, cmd_list: %06x\\n\", cmd, cmd_list);\n\tparam_cnt = get_memory_c(cmd_list);\n\tunit = get_memory_c((cmd_list + 1) & mask);\n\tctl_code = get_memory_c((cmd_list + 4 + ext) & mask);\n\n\tsmartport_log(0xc70d, cmd, rts_addr, cmd_list);\n\tdbg_log_info(g_cur_dfcyc, (rts_addr << 16) | (unit << 8) | cmd,\n\t\t\t\t\t\t\tcmd_list, 0xc70d);\n#if 0\n\tif(cmd != 0x41) {\n\t\tprintf(\"SMTPT: c70d %08x, %08x at %016llx\\n\",\n\t\t\t(rts_addr << 16) | (unit << 8) | cmd, cmd_list,\n\t\t\tg_cur_dfcyc);\n\t}\n#endif\n\tret = 0;\n\tif((unit >= 1) && (unit <= MAX_C7_DISKS) && ext) {\n\t\tif(g_iwm.smartport[unit-1].just_ejected) {\n\t\t\tret = 0x2e;\t\t// DISKSW error\n\t\t}\n\t\tg_iwm.smartport[unit-1].just_ejected = 0;\n\t}\n\n\tswitch(cmd & 0x3f) {\n\tcase 0x00:\t/* Status == 0x00 and 0x40 */\n\t\tif(param_cnt != 3) {\n\t\t\tdisk_printf(\"param_cnt %d is != 3!\\n\", param_cnt);\n\t\t\tret = 0x04;\t\t// BADPCNT\n\t\t\tbreak;\n\t\t}\n\t\tstatus_ptr_lo = get_memory_c((cmd_list+2) & mask);\n\t\tstatus_ptr_mid = get_memory_c((cmd_list+3) & mask);\n\t\tstatus_ptr_hi = 0;\n\t\tif(cmd & 0x40) {\n\t\t\tstatus_ptr_hi = get_memory_c((cmd_list+4) & mask);\n\t\t}\n\n\t\tstatus_ptr = status_ptr_lo + (256*status_ptr_mid) +\n\t\t\t\t\t\t\t(65536*status_ptr_hi);\n\n\t\tsmartport_log(0, unit, status_ptr, ctl_code);\n\t\tdbg_log_info(g_cur_dfcyc, (ctl_code << 16) | unit,\n\t\t\t\t\t\t\tcmd_list, 0xc700);\n\n\t\tdisk_printf(\"unit: %02x, status_ptr: %06x, code: %02x\\n\",\n\t\t\tunit, status_ptr, ctl_code);\n\t\tif((unit == 0) && (ctl_code == 0)) {\n\t\t\t/* Smartport driver status */\n\t\t\t/* see technotes/smpt/tn-smpt-002 */\n\t\t\tset_memory_c(status_ptr, MAX_C7_DISKS, 1);\n\t\t\tset_memory_c(status_ptr+1, 0xff, 1);\t// intrpt stat\n\t\t\tset_memory16_c(status_ptr+2, 0x004b, 1); // vendor id\n\t\t\tset_memory16_c(status_ptr+4, 0x1000, 1); // version\n\t\t\tset_memory16_c(status_ptr+6, 0x0000, 1);\n\t\t\t//printf(\" driver status, highest_unit:%02x\\n\",\n\t\t\t//\t\tg_highest_smartport_unit+1);\n\n\t\t\tengine.xreg = 8;\n\t\t\tengine.yreg = 0;\n\t\t} else if((unit > 0) && (ctl_code == 0)) {\n\t\t\t/* status for unit x */\n\t\t\tif((unit > MAX_C7_DISKS) ||\n\t\t\t\t\t(g_iwm.smartport[unit-1].fd < 0)) {\n\t\t\t\tstat_val = 0x80;\n\t\t\t\tdsize = 0;\n\t\t\t\tret = 0;\t// Not DISK_SWITCHed error\n\t\t\t} else {\n\t\t\t\tstat_val = 0xf8;\n\t\t\t\tdsize = g_iwm.smartport[unit-1].dimage_size;\n\t\t\t\tdsize = (dsize+511) / 512;\n\t\t\t\tif(g_iwm.smartport[unit-1].write_prot) {\n\t\t\t\t\tstat_val |= 4;\t\t// Write prot\n\t\t\t\t}\n\t\t\t}\n#if 0\n\t\t\tprintf(\"  status unit:%02x just_ejected:%d, \"\n\t\t\t\t\t\"stat_val:%02x\\n\", unit,\n\t\t\t\t\tg_iwm.smartport[unit-1].just_ejected,\n\t\t\t\t\tstat_val);\n#endif\n\t\t\tset_memory_c(status_ptr, stat_val, 1);\n\t\t\tset_memory24_c(status_ptr + 1, (word32)dsize);\n\t\t\tengine.xreg = 4;\n\t\t\tif(cmd & 0x40) {\n\t\t\t\tset_memory_c(status_ptr + 4,\n\t\t\t\t\t\t(dsize >> 24) & 0xff, 1);\n\t\t\t\tengine.xreg = 5;\n\t\t\t}\n\t\t\tengine.yreg = 0;\n\t\t\tdisk_printf(\"just finished unit %d, stat 0\\n\", unit);\n\t\t} else if(ctl_code == 3) {\n\t\t\tif((unit > MAX_C7_DISKS) ||\n\t\t\t\t\t(g_iwm.smartport[unit-1].fd < 0)) {\n\t\t\t\tstat_val = 0x80;\n\t\t\t\tdsize = 0;\n\t\t\t\tret = 0;\t// Not a disk-switched error\n\t\t\t} else {\n\t\t\t\tstat_val = 0xf8;\n\t\t\t\tdsize = g_iwm.smartport[unit-1].dimage_size;\n\t\t\t\tdsize = (dsize + 511) / 512;\n\t\t\t}\n\t\t\tif(cmd & 0x40) {\n\t\t\t\tdisk_printf(\"extended for stat_code 3!\\n\");\n\t\t\t}\n\t\t\t/* DIB for unit 1 */\n\t\t\tset_memory_c(status_ptr, stat_val, 1);\n\t\t\tset_memory24_c(status_ptr + 1, (word32)dsize);\n\t\t\tif(cmd & 0x40) {\n\t\t\t\tset_memory_c(status_ptr + 4,\n\t\t\t\t\t\t(dsize >> 24) & 0xff, 1);\n\t\t\t\tstatus_ptr++;\n\t\t\t}\n\t\t\tset_memory_c(status_ptr + 4, 4, 1);\n\t\t\tfor(i = 5; i < 21; i++) {\n\t\t\t\tset_memory_c(status_ptr + i, 0x20, 1);\n\t\t\t}\n\t\t\tset_memory_c(status_ptr + 5, 'K', 1);\n\t\t\tset_memory_c(status_ptr + 6, 'E', 1);\n\t\t\tset_memory_c(status_ptr + 7, 'G', 1);\n\t\t\tset_memory_c(status_ptr + 8, 'S', 1);\n\n\t\t\t// Profile hard disk supporting extended calls+disk_sw\n\t\t\tset_memory16_c(status_ptr + 21, 0xc002, 1);\n\t\t\tset_memory16_c(status_ptr + 23, 0x0000, 1);\n\n\t\t\tif(cmd & 0x40) {\n\t\t\t\tengine.xreg = 26;\n\t\t\t} else {\n\t\t\t\tengine.xreg = 25;\n\t\t\t}\n#if 0\n\t\t\tprintf(\"  DIB unit:%02x just_ejected:%d, \"\n\t\t\t\t\t\"stat_val:%02x\\n\", unit,\n\t\t\t\t\tg_iwm.smartport[unit-1].just_ejected,\n\t\t\t\t\tstat_val);\n#endif\n\t\t\tengine.yreg = 0;\n\n\t\t\tdisk_printf(\"Just finished unit %d, stat 3\\n\", unit);\n\t\t\tif(unit == 0 || unit > MAX_C7_DISKS) {\n\t\t\t\tret = 0x28;\t\t// NODRIVE error\n\t\t\t}\n\t\t} else {\n\t\t\tprintf(\"cmd: 00, unknown unit/status code %02x!\\n\",\n\t\t\t\t\t\t\t\tctl_code);\n\t\t\tret = 0x21;\t\t\t// BADCTL\n\t\t}\n\t\tbreak;\n\tcase 0x01:\t/* Read Block == 0x01 and 0x41 */\n\t\tif(param_cnt != 3) {\n\t\t\thalt_printf(\"param_cnt %d is != 3!\\n\", param_cnt);\n\t\t\tret = 0x04;\t\t// BADPCNT\n\t\t\tbreak;\n\t\t}\n\t\tbuf_ptr_lo = get_memory_c((cmd_list+2) & mask);\n\t\tbuf_ptr_hi = get_memory_c((cmd_list+3) & mask);\n\n\t\tbuf_ptr = buf_ptr_lo + (256*buf_ptr_hi);\n\t\tif(cmd & 0x40) {\n\t\t\tbuf_ptr_lo = get_memory_c((cmd_list+4) & mask);\n\t\t\tbuf_ptr_hi = get_memory_c((cmd_list+5) & mask);\n\t\t\tbuf_ptr += ((buf_ptr_hi*256) + buf_ptr_lo)*65536;\n\t\t\tcmd_list += 2;\n\t\t}\n\t\tblock_lo = get_memory_c((cmd_list+4) & mask);\n\t\tblock_mid = get_memory_c((cmd_list+5) & mask);\n\t\tblock_hi = get_memory_c((cmd_list+6) & mask);\n\t\tblock_hi2 = 0;\n\t\tif(cmd & 0x40) {\n\t\t\tblock_hi2 = get_memory_c((cmd_list+7) & mask);\n\t\t}\n\t\tblock = (block_hi2 << 24) | (block_hi << 16) |\n\t\t\t\t\t(block_mid << 8) | block_lo;\n\t\tdisk_printf(\"smartport read unit %d of block %06x to %06x\\n\",\n\t\t\tunit, block, buf_ptr);\n\t\tif(unit < 1 || unit > MAX_C7_DISKS) {\n\t\t\thalt_printf(\"Unknown unit #: %d\\n\", unit);\n\t\t}\n\n\t\tsmartport_log(0, unit - 1, buf_ptr, block);\n\n\t\tif(ret == 0) {\n\t\t\tret = do_read_c7(unit - 1, buf_ptr, block);\n\t\t}\n\t\tengine.xreg = 0;\n\t\tengine.yreg = 2;\n\t\tbreak;\n\tcase 0x02:\t/* Write Block == 0x02 and 0x42 */\n\t\tif(param_cnt != 3) {\n\t\t\thalt_printf(\"param_cnt %d is != 3!\\n\", param_cnt);\n\t\t\tret = 0x04;\t\t// BADPCNT\n\t\t\tbreak;\n\t\t}\n\t\tbuf_ptr_lo = get_memory_c((cmd_list+2) & mask);\n\t\tbuf_ptr_hi = get_memory_c((cmd_list+3) & mask);\n\n\t\tbuf_ptr = buf_ptr_lo + (256*buf_ptr_hi);\n\t\tif(cmd & 0x40) {\n\t\t\tbuf_ptr_lo = get_memory_c((cmd_list+4) & mask);\n\t\t\tbuf_ptr_hi = get_memory_c((cmd_list+5) & mask);\n\t\t\tbuf_ptr += ((buf_ptr_hi*256) + buf_ptr_lo)*65536;\n\t\t\tcmd_list += 2;\n\t\t}\n\t\tblock_lo = get_memory_c((cmd_list+4) & mask);\n\t\tblock_mid = get_memory_c((cmd_list+5) & mask);\n\t\tblock_hi = get_memory_c((cmd_list+6) & mask);\n\t\tblock_hi2 = 0;\n\t\tif(cmd & 0x40) {\n\t\t\tblock_hi2 = get_memory_c((cmd_list+7) & mask);\n\t\t}\n\t\tblock = (block_hi2 << 24) | (block_hi << 16) |\n\t\t\t\t\t(block_mid << 8) | block_lo;\n\t\tdisk_printf(\"smartport write unit %d of block %04x from %04x\\n\",\n\t\t\tunit, block, buf_ptr);\n\t\tif(unit < 1 || unit > MAX_C7_DISKS) {\n\t\t\thalt_printf(\"Unknown unit #: %d\\n\", unit);\n\t\t}\n\n\t\tsmartport_log(0, unit - 1, buf_ptr, block);\n\n\t\tif(ret == 0) {\n\t\t\tret = do_write_c7(unit - 1, buf_ptr, block);\n\t\t}\n\t\tengine.xreg = 0;\n\t\tengine.yreg = 2;\n\n\t\tHALT_ON(HALT_ON_C70D_WRITES, \"c70d Write done\\n\");\n\t\tbreak;\n\tcase 0x03:\t/* Format == 0x03 and 0x43 */\n\t\tif(param_cnt != 1) {\n\t\t\thalt_printf(\"param_cnt %d is != 1!\\n\", param_cnt);\n\t\t\tret = 0x04;\t\t// BADPCNT\n\t\t\tbreak;\n\t\t}\n\t\tif((unit < 1) || (unit > MAX_C7_DISKS)) {\n\t\t\thalt_printf(\"Unknown unit #: %d\\n\", unit);\n\t\t\tret = 0x11;\t\t// BADUNIT\n\t\t}\n\n\t\tsmartport_log(0, unit - 1, 0, 0);\n\n\t\tif(ret == 0) {\n\t\t\tret = do_format_c7(unit - 1);\n\t\t}\n\t\tengine.xreg = 0;\n\t\tengine.yreg = 2;\n\n\t\tHALT_ON(HALT_ON_C70D_WRITES, \"c70d Format done\\n\");\n\t\tbreak;\n\tcase 0x04:\t/* Control == 0x04 and 0x44 */\n\t\tif(cmd == 0x44) {\n\t\t\thalt_printf(\"smartport code 0x44 not supported\\n\");\n\t\t}\n\t\tif(param_cnt != 3) {\n\t\t\thalt_printf(\"param_cnt %d is != 3!\\n\", param_cnt);\n\t\t\tbreak;\n\t\t}\n\t\tctl_ptr_lo = get_memory_c((cmd_list+2) & mask);\n\t\tctl_ptr_hi = get_memory_c((cmd_list+3) & mask);\n\t\tctl_ptr = (ctl_ptr_hi << 8) + ctl_ptr_lo;\n\t\tif(cmd & 0x40) {\n\t\t\tctl_ptr_lo = get_memory_c((cmd_list+4) & mask);\n\t\t\tctl_ptr_hi = get_memory_c((cmd_list+5) & mask);\n\t\t\tctl_ptr += ((ctl_ptr_hi << 8) + ctl_ptr_lo) << 16;\n\t\t\tcmd_list += 2;\n\t\t}\n\n\t\tswitch(ctl_code) {\n\t\tcase 0x00:\n\t\t\tprintf(\"Performing a reset on unit %d\\n\", unit);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thalt_printf(\"control code: %02x ptr:%06x unknown!\\n\",\n\t\t\t\t\t\t\tctl_code, ctl_ptr);\n\t\t}\n\t\t// printf(\"CONTROL, ctl_code:%02x\\n\", ctl_code);\n\n\t\tengine.xreg = 0;\n\t\tengine.yreg = 2;\n\t\tbreak;\n\tdefault:\t/* Unknown command! */\n\t\t/* set acc = 1, and set carry, and set kpc */\n\t\tengine.xreg = (rts_addr) & 0xff;\n\t\tengine.yreg = (rts_addr >> 8) & 0xff;\n\t\tret = 0x01;\t\t// BADCMD error\n\t\tif((cmd != 0x4b) && (cmd != 0x48) && (cmd != 0x4a)) {\n\t\t\t// Finder does 0x4a before dialog for formatting disk\n\t\t\t// Finder does 0x4b call before formatting disk\n\t\t\t// Many things do 0x48 call to see online drives\n\t\t\t// So: ignore those, just return BADCMD\n\t\t\thalt_printf(\"Just did smtport cmd:%02x rts_addr:%04x, \"\n\t\t\t\t\"cmdlst:%06x\\n\", cmd, rts_addr, cmd_list);\n\t\t}\n\t}\n\n\tengine.acc = (engine.acc & 0xff00) | (ret & 0xff);\n\tengine.psr &= ~1;\n\tif(ret) {\n\t\tengine.psr |= 1;\n\t\tprintf(\"Smtport cmd:%02x unit:%02x ctl_code:%02x ret:%02x\\n\",\n\t\t\tcmd, unit, ctl_code, ret);\n\t}\n\tengine.kpc = (rts_addr + 3 + ext) & 0xffff;\n\t// printf(\"   ret:%02x psr_c:%d\\n\", ret & 0xff, engine.psr & 1);\n}\n\n// $C70A is the ProDOS entry point, documented in ProDOS 8 Technical Ref\n//  Manual, section 6.3.\nvoid\ndo_c70a(word32 arg0)\n{\n\tdword64\tdsize;\n\tword32\tcmd, unit, buf_lo, buf_hi, blk_lo, blk_hi, blk, buf;\n\tword32\tprodos_unit;\n\tint\tret, slot;\n\n\tslot = (engine.kpc >> 8) & 7;\n\tset_memory_c(0x7f8, 0xc0 | slot, 1);\n\n\tcmd = get_memory_c((engine.direct + 0x42) & 0xffff);\n\tprodos_unit = get_memory_c((engine.direct + 0x43) & 0xffff);\n\tbuf_lo = get_memory_c((engine.direct + 0x44) & 0xffff);\n\tbuf_hi = get_memory_c((engine.direct + 0x45) & 0xffff);\n\tblk_lo = get_memory_c((engine.direct + 0x46) & 0xffff);\n\tblk_hi = get_memory_c((engine.direct + 0x47) & 0xffff);\n\n\tblk = (blk_hi << 8) + blk_lo;\n\tbuf = (buf_hi << 8) + buf_lo;\n\tdisk_printf(\"c70a %02x cmd:%02x, pro_unit:%02x, buf:%04x, blk:%04x\\n\",\n\t\targ0, cmd, prodos_unit, buf, blk);\n\n\tunit = 0 + (prodos_unit >> 7);\t\t// units 0,1\n\tif((prodos_unit & 0x7f) != (slot << 4)) {\n\t\tunit += 2;\t\t\t// units 2,3\n\t}\n\n\tsmartport_log(0xc70a, cmd, blk, buf);\n\tdbg_log_info(g_cur_dfcyc,\n\t\t(buf << 16) | ((unit & 0xff) << 8) | (cmd & 0xff), blk, 0xc70a);\n\n#if 0\n\tif(cmd != 0x1ff) {\n\t\tprintf(\"SMTPT: c70a %08x %08x\\n\",\n\t\t\t(buf << 16) | ((unit & 0xff) << 8) | (cmd & 0xff), blk);\n\t}\n#endif\n\tengine.psr &= ~1;\t/* clear carry */\n\n\tret = 0x27;\t/* I/O error */\n\tif(cmd == 0x00) {\n\t\tdsize = g_iwm.smartport[unit].dimage_size;\n\t\tdsize = (dsize + 511) / 512;\n\n\t\tsmartport_log(0, unit, (word32)dsize, 0);\n\t\tdbg_log_info(g_cur_dfcyc, ((unit & 0xff) << 8) | (cmd & 0xff),\n\t\t\t\t\t\t\t(word32)dsize, 0x1c700);\n\n\t\tret = 0;\n\t\tengine.xreg = dsize & 0xff;\n\t\tengine.yreg = (word32)(dsize >> 8);\n\t} else if(cmd == 0x01) {\n\t\tsmartport_log(0, unit, buf, blk);\n\t\tret = do_read_c7(unit, buf, blk);\n\t} else if(cmd == 0x02) {\n\t\tsmartport_log(0, unit, buf, blk);\n\t\tret = do_write_c7(unit, buf, blk);\n\t} else if(cmd == 0x03) {\t/* format */\n\t\tsmartport_log(0, unit, buf, blk);\n\t\tret = do_format_c7(unit);\n\t}\n\n\tengine.acc = (engine.acc & 0xff00) | (ret & 0xff);\n\tif(ret != 0) {\n\t\tengine.psr |= 1;\t\t\t// Set carry\n\t}\n\treturn;\n}\n\nint\ndo_read_c7(int unit_num, word32 buf, word32 blk)\n{\n\tbyte\tlocal_buf[0x200];\n\tDisk\t*dsk;\n\tbyte\t*bptr;\n\tdword64\tdimage_start, dimage_size, dret;\n\tword32\tval;\n\tint\tlen, fd;\n\tint\ti;\n\n\tdbg_log_info(g_cur_dfcyc, (buf << 8) | (unit_num & 0xff), blk, 0xc701);\n\tif((unit_num < 0) || (unit_num > MAX_C7_DISKS)) {\n\t\thalt_printf(\"do_read_c7: unit_num: %d\\n\", unit_num);\n\t\tsmartport_error();\n\t\treturn 0x28;\n\t}\n\n\tdsk = &(g_iwm.smartport[unit_num]);\n\tfd = dsk->fd;\n\tdimage_start = dsk->dimage_start;\n\tdimage_size = dsk->dimage_size;\n\tif(fd < 0) {\n\t\tprintf(\"c7_fd == %d!\\n\", fd);\n#if 0\n\t\tif(blk != 2 && blk != 0) {\n\t\t\t/* don't print error if only reading directory */\n\t\t\tsmartport_error();\n\t\t\thalt_printf(\"Read unit:%02x blk:%04x\\n\", unit_num, blk);\n\t\t}\n#endif\n\t\treturn 0x2f;\n\t}\n\tif(((blk + 1) * 0x200ULL) > (dimage_start + dimage_size)) {\n\t\thalt_printf(\"Tried to read past %08llx on disk (blk:%04x)\\n\",\n\t\t\tdimage_start + dimage_size, blk);\n\t\tsmartport_error();\n\t\treturn 0x27;\n\t}\n\n\tif(dsk->raw_data) {\n\t\t// image was compressed and is in dsk->raw_data\n\t\tbptr = dsk->raw_data + dimage_start + (blk*0x200ULL);\n\t\tfor(i = 0; i < 0x200; i++) {\n\t\t\tlocal_buf[i] = bptr[i];\n\t\t}\n\t} else {\n\t\tdret = kegs_lseek(fd, dimage_start + blk*0x200ULL, SEEK_SET);\n\t\tif(dret != (dimage_start + blk*0x200ULL)) {\n\t\t\thalt_printf(\"lseek ret %08llx, errno:%d\\n\", dret,\n\t\t\t\t\t\t\t\t\terrno);\n\t\t\tsmartport_error();\n\t\t\treturn 0x27;\n\t\t}\n\n\t\tlen = (int)read(fd, &local_buf[0], 0x200);\n\t\tif(len != 0x200) {\n\t\t\tprintf(\"read returned %08x, errno:%d, blk:%04x, unit:\"\n\t\t\t\t\"%02x\\n\", len, errno, blk, unit_num);\n\t\t\thalt_printf(\"name: %s\\n\", dsk->name_ptr);\n\t\t\tsmartport_error();\n\t\t\treturn 0x27;\n\t\t}\n\t}\n\n\tg_io_amt += 0x200;\n\n\tif(buf >= 0xfc0000) {\n\t\tdisk_printf(\"reading into ROM, just returning\\n\");\n\t\treturn 0;\n\t}\n\n\tfor(i = 0; i < 0x200; i += 2) {\n\t\tval = (local_buf[i+1] << 8) + local_buf[i];\n\t\tset_memory16_c(buf + i, val, 0);\n\t}\n\n\treturn 0;\n}\n\nint\ndo_write_c7(int unit_num, word32 buf, word32 blk)\n{\n\tbyte\tlocal_buf[0x200];\n\tDisk\t*dsk;\n\tdword64\tdret, dimage_start, dimage_size;\n\tint\tlen, fd, ret;\n\tint\ti;\n\n\tdbg_log_info(g_cur_dfcyc, (buf << 16) | (unit_num & 0xff), blk, 0xc702);\n\n\tif(unit_num < 0 || unit_num > MAX_C7_DISKS) {\n\t\thalt_printf(\"do_write_c7: unit_num: %d\\n\", unit_num);\n\t\tsmartport_error();\n\t\treturn 0x28;\n\t}\n\n\tdsk = &(g_iwm.smartport[unit_num]);\n\tfd = dsk->fd;\n\tdimage_start = dsk->dimage_start;\n\tdimage_size = dsk->dimage_size;\n\tif(fd < 0) {\n\t\thalt_printf(\"c7_fd == %d!\\n\", fd);\n\t\tsmartport_error();\n\t\treturn 0x28;\n\t}\n\n\tfor(i = 0; i < 0x200; i++) {\n\t\tlocal_buf[i] = get_memory_c(buf + i);\n\t}\n\n\tif(dsk->write_prot) {\n\t\tprintf(\"Write, but s7d%d %s is write protected!\\n\",\n\t\t\t\t\t\tunit_num + 1, dsk->name_ptr);\n\t\treturn 0x2b;\n\t}\n\n\tif(dsk->write_through_to_unix == 0) {\n\t\t//halt_printf(\"Write to %s, but not wr_thru!\\n\", dsk->name_ptr);\n\t\tif(dsk->raw_data) {\n\t\t\t// Update the memory copy\n\t\t\tret = smartport_memory_write(dsk, &local_buf[0],\n\t\t\t\t\t\tblk * 0x200ULL, 0x200);\n\t\t\tif(ret) {\n\t\t\t\treturn 0x27;\t\t// I/O Error\n\t\t\t}\n\t\t}\n\t\treturn 0x00;\n\t}\n\n\tif(dsk->dynapro_info_ptr) {\n\t\tdynapro_write(dsk, &local_buf[0], blk*0x200UL, 0x200);\n\t} else {\n\t\tdret = kegs_lseek(fd, dimage_start + blk*0x200ULL, SEEK_SET);\n\t\tif(dret != (dimage_start + blk*0x200ULL)) {\n\t\t\thalt_printf(\"lseek returned %08llx, errno: %d\\n\", dret,\n\t\t\t\t\t\t\t\terrno);\n\t\t\tsmartport_error();\n\t\t\treturn 0x27;\n\t\t}\n\n\t\tif(dret >= (dimage_start + dimage_size)) {\n\t\t\thalt_printf(\"Tried to write to %08llx\\n\", dret);\n\t\t\tsmartport_error();\n\t\t\treturn 0x27;\n\t\t}\n\n\t\tlen = (int)write(fd, &local_buf[0], 0x200);\n\t\tif(len != 0x200) {\n\t\t\thalt_printf(\"write ret %08x bytes, errno: %d\\n\", len,\n\t\t\t\t\t\t\t\t\terrno);\n\t\t\tsmartport_error();\n\t\t\tdsk->write_prot = 1;\n\t\t\treturn 0x2b;\t\t// Write protected\n\t\t}\n\t}\n\n\tg_io_amt += 0x200;\n\n\treturn 0;\n}\n\nint\nsmartport_memory_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size)\n{\n\tbyte\t*bptr;\n\tword32\tui;\n\n\tbptr = dsk->raw_data;\n\tif((bptr == 0) || ((doffset + size) > dsk->dimage_size)) {\n\t\tprintf(\"Write to %s failed, %08llx past end %08llx\\n\",\n\t\t\tdsk->name_ptr, doffset, dsk->dimage_size);\n\t\treturn -1;\n\t}\n\tfor(ui = 0; ui < size; ui++) {\n\t\tbptr[doffset + ui] = bufptr[ui];\n\t}\n\n\treturn 0;\n}\n\nint\ndo_format_c7(int unit_num)\n{\n\tbyte\tlocal_buf[0x1000];\n\tDisk\t*dsk;\n\tdword64\tdimage_start, dimage_size, dret, dtotal, dsum;\n\tint\tlen, max, fd, ret;\n\tint\ti;\n\n\tdbg_log_info(g_cur_dfcyc, (unit_num & 0xff), 0, 0xc703);\n\n\tif(unit_num < 0 || unit_num > MAX_C7_DISKS) {\n\t\thalt_printf(\"do_format_c7: unit_num: %d\\n\", unit_num);\n\t\tsmartport_error();\n\t\treturn 0x28;\n\t}\n\n\tdsk = &(g_iwm.smartport[unit_num]);\n\tfd = dsk->fd;\n\tdimage_start = dsk->dimage_start;\n\tdimage_size = dsk->dimage_size;\n\tif(fd < 0) {\n\t\thalt_printf(\"c7_fd == %d!\\n\", fd);\n\t\tsmartport_error();\n\t\treturn 0x28;\n\t}\n\n\tif(dsk->write_prot || (dsk->raw_data && !dsk->dynapro_info_ptr)) {\n\t\tprintf(\"Format, but %s is write protected!\\n\", dsk->name_ptr);\n\t\treturn 0x2b;\n\t}\n\n\tif(dsk->write_through_to_unix == 0) {\n\t\tif(!dsk->raw_data) {\n\t\t\tprintf(\"Format of %s ignored\\n\", dsk->name_ptr);\n\t\t\treturn 0x00;\n\t\t}\n\t}\n\n\tfor(i = 0; i < 0x1000; i++) {\n\t\tlocal_buf[i] = 0;\n\t}\n\n\tif(!dsk->dynapro_info_ptr) {\n\t\tdret = kegs_lseek(fd, dimage_start, SEEK_SET);\n\t\tif(dret != dimage_start) {\n\t\t\thalt_printf(\"lseek returned %08llx, errno: %d\\n\", dret,\n\t\t\t\t\t\t\t\t\terrno);\n\t\t\tsmartport_error();\n\t\t\treturn 0x27;\n\t\t}\n\t}\n\n\tdsum = 0;\n\tdtotal = dimage_size;\n\n\twhile(dsum < dtotal) {\n\t\tmax = (int)MY_MIN(0x1000, dtotal - dsum);\n\t\tlen = max;\n\t\tif(dsk->dynapro_info_ptr) {\n\t\t\tdynapro_write(dsk, &local_buf[0], dsum, max);\n\t\t} else if(dsk->raw_data) {\n\t\t\tret = smartport_memory_write(dsk, &local_buf[0],\n\t\t\t\t\t\t\t\tdsum, max);\n\t\t\tif(ret) {\n\t\t\t\treturn 0x27;\t\t// I/O Error\n\t\t\t}\n\t\t} else {\n\t\t\tlen = (int)write(fd, &local_buf[0], max);\n\t\t}\n\t\tif(len != max) {\n\t\t\thalt_printf(\"write ret %08x, errno:%d\\n\", len, errno);\n\t\t\tsmartport_error();\n\t\t\tdsk->write_prot = 1;\n\t\t\treturn 0x2b;\t\t// Write-protected\n\t\t}\n\t\tdsum += len;\n\t}\n\n\treturn 0;\n}\n\nvoid\ndo_c700(word32 ret)\n{\n\tint\tslot;\n\n\tdisk_printf(\"do_c700 called, ret: %08x\\n\", ret);\n\tdbg_log_info(g_cur_dfcyc, 0, 0, 0xc700);\n\n\tslot = (engine.kpc >> 8) & 7;\n\tret = do_read_c7(0, 0x800, 0);\t\t// Always read unit 0, block 0\n\n\tset_memory_c(0x7f8, slot, 1);\n\tset_memory16_c(0x42, (slot << 12) | 1, 1);\n\tset_memory16_c(0x44, 0x0800, 1);\n\tset_memory16_c(0x46, 0x0000, 1);\n\tengine.xreg = slot << 4;\t\t// 0x70 for slot 7\n\tengine.kpc = 0x801;\n\n\tif(ret != 0) {\n\t\tprintf(\"Failure reading boot disk in s7d1, trying slot 5!\\n\");\n\t\tengine.kpc = 0xc500;\t\t// Try to boot slot 5\n\t\tif((slot == 5) || (g_rom_version == 0)) {\n\t\t\tengine.kpc = 0xc600;\t// Try to boot slot 6\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "gsplus/src/sound.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include \"defc.h\"\n\n#define INCLUDE_RCSID_C\n#include \"sound.h\"\n#undef INCLUDE_RCSID_C\n\n#define DOC_LOG(a,b,c,d)\n\nextern int Verbose;\nextern int g_use_shmem;\nextern word32 g_vbl_count;\nextern int g_preferred_rate;\n\nextern word32 g_c03ef_doc_ptr;\n\nextern int g_doc_vol;\n\nextern dword64 g_last_vbl_dfcyc;\n\nint\tg_queued_samps = 0;\nint\tg_queued_nonsamps = 0;\n\n#if defined(HPUX) || defined(__linux__) || defined(_WIN32) || defined(MAC)\nint\tg_audio_enable = -1;\n#else\nint\tg_audio_enable = 0;\t\t/* Not supported: default to off */\n#endif\nint\tg_sound_min_msecs = 32;\t\t\t// 32 msecs\nint\tg_sound_min_msecs_pulse = 150;\t\t// 150 msecs\nint\tg_sound_max_multiplier = 6;\t\t// 6*32 = ~200 msecs\nint\tg_sound_min_samples = 48000 * 32/1000;\t// 32 msecs\n\nMockingboard g_mockingboard;\n\n// The AY8913 chip has non-linear amplitudes (it has 16 levels) and the\n//  documentation does not match measured results.  But all the measurements\n//  should really be done at the final speaker/jack since all the stuff in\n//  the path affects it.  But: no one's done this for Mockingboard that I\n//  have found, so I'm taking measurements from the AY8913 chip itself.\n// AY8913 amplitudes from https://groups.google.com/forum/#!original/\n//\t\t\t\tcomp.sys.sinclair/-zCR2kxMryY/XgvaDICaldUJ\n// by Matthew Westcott on December 21, 2001.\ndouble g_ay8913_ampl_factor_westcott[16] = {\t\t// NOT USED\n\t0.000,\t// level[0]\n\t0.010,\t// level[1]\n\t0.015,\t// level[2]\n\t0.022,\t// level[3]\n\t0.031,\t// level[4]\n\t0.046,\t// level[5]\n\t0.064,\t// level[6]\n\t0.106,\t// level[7]\n\t0.132,\t// level[8]\n\t0.216,\t// level[9]\n\t0.297,\t// level[10]\n\t0.391,\t// level[11]\n\t0.513,\t// level[12]\n\t0.637,\t// level[13]\n\t0.819,\t// level[14]\n\t1.000,\t// level[15]\n};\n// https://sourceforge.net/p/fuse-emulator/mailman/message/34065660/\n//  refers to some Russian-language measurements at:\n//  http://forum.tslabs.info/viewtopic.php?f=6&t=539 (translate from\n//  Russian), they give:\n// 0000,028F,03B3,0564, 07DC,0BA9,1083,1B7C,\n// 2068,347A,4ACE,5F72, 7E16,A2A4,CE3A,FFFF\ndouble g_ay8913_ampl_factor[16] = {\n\t0.000,\t// level[0]\n\t0.010,\t// level[1]\n\t0.014,\t// level[2]\n\t0.021,\t// level[3]\n\t0.031,\t// level[4]\n\t0.046,\t// level[5]\n\t0.064,\t// level[6]\n\t0.107,\t// level[7]\n\t0.127,\t// level[8]\n\t0.205,\t// level[9]\n\t0.292,\t// level[10]\n\t0.373,\t// level[11]\n\t0.493,\t// level[12]\n\t0.635,\t// level[13]\n\t0.806,\t// level[14]\n\t1.000,\t// level[15]\n};\n// MAME also appears to try to figure out how the channels get \"summed\"\n//  together.  KEGS code adds them in a completely independent way, and due\n//  to the circuit used on the AY8913, this is certainly incorrect.\n#define MAX_MOCK_ENV_SAMPLES\t2000\nint\tg_mock_env_vol[MAX_MOCK_ENV_SAMPLES];\nbyte\tg_mock_noise_bytes[MAX_MOCK_ENV_SAMPLES];\nint\tg_mock_volume[16];\t\t// Sample for each of the 16 amplitudes\nword32\tg_last_mock_vbl_count = 0;\n\n#define VAL_MOCK_RANGE\t\t(39000)\n\nint\tg_audio_rate = 0;\ndouble\tg_daudio_rate = 0.0;\ndouble\tg_drecip_audio_rate = 0.0;\ndouble\tg_dsamps_per_dfcyc = 0.0;\ndouble\tg_fcyc_per_samp = 0.0;\n\ndouble g_last_sound_play_dsamp = 0.0;\n\n#define VAL_C030_POS_VAL\t(20400*16/15)\n\t\t// C030_POS_VAL is multiplied by g_doc_vol (0-15) and then\n\t\t//  divided by 16.  So scale this value up by 16/15 so that\n\t\t//  g_doc_vol==15 gives the intended value (+/-20400)\n\n#define MAX_C030_TIMES\t\t18000\nfloat\tg_c030_fsamps[MAX_C030_TIMES + 2];\nint\tg_num_c030_fsamps = 0;\nint\tg_c030_state = 0;\nint\tg_c030_val = (VAL_C030_POS_VAL / 2);\ndword64\tg_c030_dsamp_last_toggle = 0;\n\nword32\t*g_sound_shm_addr = 0;\nint\tg_sound_shm_pos = 0;\n\nextern dword64 g_cur_dfcyc;\n\n#define MAX_SND_BUF\t65536\n\nint\tg_samp_buf[2*MAX_SND_BUF];\nbyte\tg_zero_buf[4096];\n\ndouble g_doc_dsamps_extra = 0.0;\n\nint\tg_num_snd_plays = 0;\nint\tg_num_recalc_snd_parms = 0;\n\nchar\t*g_sound_file_str = 0;\nint\tg_sound_file_fd = -1;\nint\tg_sound_file_bytes = 0;\n\n// WAV file information:\n// From https://docs.fileformat.com/audio/wav/\n// left channel is first: https://web.archive.org/web/20080113195252/\n//\t\t\thttp://www.borg.com/~jglatt/tech/wave.htm\n\nbyte g_wav_hdr[44] = {\n\t'R', 'I', 'F', 'F', 0xff, 0xff, 0xff, 0xff,\t\t// 0x00-0x07\n\t'W', 'A', 'V', 'E', 'f', 'm', 't', ' ',\t\t\t// 0x08-0x0f\n\t16, 0, 0, 0, 1, 0, 2, 0,\t\t\t\t// 0x10-0x17\n\t\t// 16=length of 'fmt ' chunk, 1=PCM, 2=stereo.\n\t0xff, 0xff, 0xff, 0xff,\t0xff, 0xff, 0xff, 0xff,\t\t// 0x18-0x1f\n\t4, 0, 16, 0, 'd', 'a', 't', 'a',\t\t\t// 0x20-0x27\n\t0xff, 0xff, 0xff, 0xff\t\t\t\t\t// 0x28-0x2b\n};\n\t// Bytes 4-7 are the total file size-8, so [0x28:0x2b]+0x24\n\t// Bytes [0x18-0x1b]is the sample rate (so 44100 or so)\n\t// [0x1c-0x1f] is bytes-per-second: [0x18-0x1b]*[0x10]*[0x16]/8\n\n\nvoid\nsound_init()\n{\n\tdoc_init();\n\tsnddrv_init();\n}\n\nvoid\nsound_set_audio_rate(int rate)\n{\n\tg_audio_rate = rate;\n\tg_daudio_rate = (rate)*1.0;\n\tg_drecip_audio_rate = 1.0/(rate);\n\tg_dsamps_per_dfcyc = ((rate*1.0) / (DCYCS_1_MHZ * 65536.0));\n\tg_fcyc_per_samp = (DCYCS_1_MHZ * 65536.0 / (rate*1.0));\n\tg_sound_min_samples = rate * g_sound_min_msecs / 1000;\n\n\tprintf(\"Set g_audio_rate = %d in main KEGS process, min_samples:%d\\n\",\n\t\trate, g_sound_min_samples);\n}\n\nvoid\nsound_reset(dword64 dfcyc)\n{\n\tdoc_reset(dfcyc);\n\tmockingboard_reset(dfcyc);\n}\n\nvoid\nsound_shutdown()\n{\n\tsnddrv_shutdown();\n}\n\nvoid\nsound_update(dword64 dfcyc)\n{\n\t/* Called every VBL time to update sound status */\n\n\t/* \"play\" sounds for this vbl */\n\n\t//DOC_LOG(\"do_snd_pl\", -1, dsamps, 0);\n\tsound_play(dfcyc);\n}\n\nvoid\nsound_file_start(char *filename)\n{\n\tsound_file_close();\n\n\tg_sound_file_str = filename;\t// Can be NULL, if so, do not start\n\tif(filename) {\n\t\tprintf(\"Set audio save file to: %s\\n\", filename);\n\t}\n}\n\nvoid\nsound_file_open()\n{\n\tchar\t*filename;\n\tword32\texp_size;\n\tint\tfd;\n\n\tfilename = g_sound_file_str;\n\tif(!filename) {\n\t\treturn;\n\t}\n\n\tfd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\tprintf(\"open_sound_file open ret: %d, errno: %d\\n\", fd, errno);\n\t\tsound_file_close();\n\t\treturn;\n\t}\n\n\texp_size = 1024*1024;\t\t// Default to 1MB, changed at close\n\tdynapro_set_word32(&g_wav_hdr[4], exp_size + 44 - 8);\t// File size\n\tdynapro_set_word32(&g_wav_hdr[0x28], exp_size);\t\t// data size\n\tdynapro_set_word32(&g_wav_hdr[0x18], g_audio_rate);\t// Sample rate\n\tdynapro_set_word32(&g_wav_hdr[0x1c], g_audio_rate * 2 * 2);\n\t\t\t\t\t\t\t\t// bytes-per-sec\n\n\t(void)cfg_write_to_fd(fd, &g_wav_hdr[0], 0, 44);\n\tg_sound_file_fd = fd;\n\tg_sound_file_bytes = 0;\n\tprintf(\"Opened file %s for sound\\n\", filename);\n}\n\nvoid\nsound_file_close()\n{\n\tint\tfd;\n\n\tfd = g_sound_file_fd;\n\tif(fd >= 0) {\n\t\tdynapro_set_word32(&g_wav_hdr[0x28], g_sound_file_bytes);\n\t\tdynapro_set_word32(&g_wav_hdr[4], g_sound_file_bytes + 44 - 8);\n\t\tcfg_write_to_fd(fd, &g_wav_hdr[0], 0, 44);\n\t\t\t// Rewrite first 44 bytes with WAV header\n\t\tprintf(\"Close sound file %s, fd:%d\\n\", g_sound_file_str, fd);\n\t\tclose(fd);\n\t}\n\n\tfree(g_sound_file_str);\n\tg_sound_file_fd = -1;\n\tg_sound_file_str = 0;\n}\n\nvoid\nsend_sound_to_file(word32 *wptr, int shm_pos, int num_samps, int real_samps)\n{\n\tint\tsize, this_size;\n\n\tif(!real_samps && g_sound_file_bytes) {\n\t\t// Don't do anything\n\t\treturn;\n\t}\n\tif(g_sound_file_fd < 0) {\n\t\tsound_file_open();\n\t}\n\n\tif(!wptr) {\n\t\t// No real samps\n\t\tsize = real_samps * 4;\n\t\twhile(size) {\n\t\t\tthis_size = size;\n\t\t\tif(this_size > 4096) {\n\t\t\t\tthis_size = 4096;\n\t\t\t}\n\t\t\tmust_write(g_sound_file_fd, &g_zero_buf[0], this_size);\n\t\t\tsize -= this_size;\n\t\t}\n\t\treturn;\n\t}\n\n\tsize = 0;\n\tif((num_samps + shm_pos) > SOUND_SHM_SAMP_SIZE) {\n\t\tsize = SOUND_SHM_SAMP_SIZE - shm_pos;\n\t\tg_sound_file_bytes += (size * 4);\n\n\t\tmust_write(g_sound_file_fd, (byte *)&(wptr[shm_pos]), 4*size);\n\t\tshm_pos = 0;\n\t\tnum_samps -= size;\n\t}\n\n\tg_sound_file_bytes += (num_samps * 4);\n\n\tmust_write(g_sound_file_fd, (byte *)&(wptr[shm_pos]), 4*num_samps);\n}\n\nvoid\nshow_c030_state(dword64 dfcyc)\n{\n\tshow_c030_samps(dfcyc, &(g_samp_buf[0]), 100);\n}\n\nvoid\nshow_c030_samps(dword64 dfcyc, int *outptr, int num)\n{\n\tint\tlast;\n\tint\ti;\n\n\tif(!g_num_c030_fsamps) {\n\t\treturn;\n\n\t}\n\tprintf(\"c030_fsamps[]: %d, dfcyc:%015llx\\n\", g_num_c030_fsamps, dfcyc);\n\n\tfor(i = 0; i < g_num_c030_fsamps+2; i++) {\n\t\tprintf(\"%3d: %5.3f\\n\", i, g_c030_fsamps[i]);\n\t}\n\n\tprintf(\"Samples[] = %d\\n\", num);\n\n\tlast = 0x0dadbeef;\n\tfor(i = 0; i < num; i++) {\n\t\tif((last != outptr[0]) || (i == (num - 1))) {\n\t\t\tprintf(\"Samp[%4d]: %d\\n\", i, outptr[0]);\n\t\t\tlast = outptr[0];\n\t\t}\n\t\toutptr += 2;\n\t}\n}\n\nint\nsound_play_c030(dword64 dfcyc, dword64 dsamp, int *outptr_start, int num_samps)\n{\n\tint\t*outptr;\n\tdword64\tdsamp_min;\n\tfloat\tftmp, fsampnum, next_fsampnum, fpercent;\n\tint\tval, num, c030_state, c030_val, pos, sampnum, next_sampnum;\n\tint\tdoc_vol, min_i, mul;\n\tint\ti, j;\n\n\t// Handle $C030 speaker clicks.  Clicks for the past num_samps are\n\t//  in g_c030_fsamps[] giving the sample position when the click\n\t//  occurred.  Turn this into samples, tracking multiple clicks per\n\t//  sample into an intermediate value.  After 500ms of no clicks,\n\t//  transition the speakers from +/-20400 to 0, so it's idle.\n\t// The speaker is affected by the DOC volume in g_doc_vol, like a real\n\t//  IIgs (this is used during the system beep).  This code reacts\n\t//  to DOC volume changes when they happen by causing sound_play()\n\t//  to be called, so all samples with the old volume are played then\n\t//  new clicks are collected.\n\n\tnum = g_num_c030_fsamps;\t\t// Number of clicks\n\tif(!num) {\n\t\tif(g_c030_val == 0) {\n\t\t\treturn 0;\t\t// Speaker is at rest\n\t\t}\n\t}\n\n\tpos = 0;\n\toutptr = outptr_start;\n\tc030_state = g_c030_state;\n\tc030_val = g_c030_val;\n\t\t// c030_val may be less than max due decay after 500ms.\n\t\t// Always use it first, until speaker toggles, which should\n\t\t//  restore the full speaker range\n\tdoc_vol = g_doc_vol;\n\n\tif(!num) {\n\t\t// No clicks.  See if we should begin transitioning the\n\t\t//  speaker output to 0.  I tried multiplying by .9999 but\n\t\t//  that seemed to take too long at the end, so just use a\n\t\t//  linear ramp down.  Do this ramp based on the last click\n\t\t//  time, not VBL, since this is more consistent\n\n\t\tdsamp_min = g_c030_dsamp_last_toggle + (g_audio_rate >> 4);\n\t\tif(dsamp >= dsamp_min) {\n\t\t\tmin_i = 0;\n\t\t} else {\n\t\t\tmin_i = (int)(dsamp_min - dsamp);\n\t\t}\n\t\tmul = (2 * c030_state - 1) * doc_vol;\n\t\tval = (c030_val * mul) >> 4;\n\t\tfor(i = 0; i < num_samps; i++) {\n\t\t\tif(i >= min_i) {\n\t\t\t\tif(c030_val > 4) {\n\t\t\t\t\tc030_val -= 4;\n\t\t\t\t} else {\n\t\t\t\t\tc030_val = 0;\n\t\t\t\t}\n\t\t\t\tval = (c030_val * mul) >> 4;\n\t\t\t}\n\t\t\toutptr[0] = val;\n\t\t\toutptr[1] = val;\n\t\t\toutptr += 2;\n\t\t}\n#if 0\n\t\tprintf(\"at %015llx, num_samps:%d val at start:%d, at end:%d, \"\n\t\t\t\"min_i:%d\\n\", dfcyc, num_samps, g_c030_val, c030_val,\n\t\t\tmin_i);\n#endif\n\t\tg_c030_val = c030_val;\n\t\tif(c030_val == 0) {\n\t\t\t//printf(\"Speaker at rest\\n\");\n\t\t}\n\n\t\treturn 1;\n\t}\n\n\tg_c030_fsamps[num] = (float)(num_samps);\n\n\tnum++;\n\tfsampnum = g_c030_fsamps[0];\n\tsampnum = (int)fsampnum;\n\tfpercent = (float)0.0;\n\ti = 0;\n\n\twhile(i < num) {\n\t\tif(sampnum < 0 || sampnum > num_samps) {\n\t\t\thalt_printf(\"play c030: [%d]:%f is %d, > %d\\n\",\n\t\t\t\t\ti, fsampnum, sampnum, num_samps);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* write in samples to all samps < me */\n\t\tval = ((2 * c030_state) - 1) * ((c030_val * doc_vol) >> 4);\n\t\tif(num <= 1) {\n\t\t\tprintf(\"num:%d i:%d pos:%d, sampnum:%d c030_state:%d \"\n\t\t\t\t\" at %015llx\\n\", num, i, pos, sampnum,\n\t\t\t\tc030_state, dfcyc);\n\t\t}\n\t\tfor(j = pos; j < sampnum; j++) {\n\t\t\toutptr[0] = val;\n\t\t\toutptr[1] = val;\n\t\t\toutptr += 2;\n\t\t\tpos++;\n\t\t}\n\n\t\tif((sampnum >= num_samps) || ((i + 1) >= num)) {\n\t\t\tbreak;\t\t// All done\n\t\t}\n\n\t\t/* now, calculate me */\n\t\tfpercent = (float)0.0;\n\t\tif(c030_state) {\n\t\t\tfpercent = (fsampnum - (float)sampnum);\n\t\t}\n\n\t\tc030_state = !c030_state;\n\t\tc030_val = VAL_C030_POS_VAL;\n\t\tg_c030_dsamp_last_toggle = dsamp + i;\n\n\t\tnext_fsampnum = g_c030_fsamps[i+1];\n\t\tnext_sampnum = (int)next_fsampnum;\n\t\t// Handle all the changes during this one sample\n\t\twhile(next_sampnum == sampnum) {\n\t\t\tif(c030_state) {\n\t\t\t\tfpercent += (next_fsampnum - fsampnum);\n\t\t\t}\n\t\t\ti++;\n\t\t\tfsampnum = next_fsampnum;\n\n\t\t\tif(i > num) {\n\t\t\t\tbreak;\t\t// This should not happen!\n\t\t\t}\n\t\t\tnext_fsampnum = g_c030_fsamps[i+1];\n\t\t\tnext_sampnum = (int)next_fsampnum;\n\t\t\tc030_state = !c030_state;\n\t\t}\n\n\t\tif(c030_state) {\n\t\t\t// add in time until next sample\n\t\t\tftmp = (float)(int)(fsampnum + (float)1.0);\n\t\t\tfpercent += (ftmp - fsampnum);\n\t\t}\n\n\t\tif((fpercent < (float)0.0) || (fpercent > (float)1.0)) {\n\t\t\thalt_printf(\"fpercent: %d = %f\\n\", i, fpercent);\n\t\t\tshow_c030_samps(dfcyc, outptr_start, num_samps);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = (int)((2*fpercent - 1) * ((c030_val * doc_vol) >> 4));\n\t\toutptr[0] = val;\n\t\toutptr[1] = val;\n\t\toutptr += 2;\n\t\tpos++;\n\t\ti++;\n\n\t\tsampnum = next_sampnum;\n\t\tfsampnum = next_fsampnum;\n\t}\n\n\tg_c030_state = c030_state;\n\tg_c030_val = c030_val;\n\n#if 0\n\tif(g_sound_file_str) {\n\t\tshow_c030_samps(dfcyc, outptr_start, num_samps);\n\t}\n#endif\n\n\t// See if there are any entries >= fsampnum, copy them back down\n\t//  to the beginning of the array\n\tpos = 0;\n\tnum--;\n\tfsampnum = (float)num_samps;\n\twhile(i < num) {\n\t\tg_c030_fsamps[pos] = g_c030_fsamps[i] - fsampnum;\n#if 0\n\t\tprintf(\"Copied [%d] %f to [%d] as %f\\n\", i, g_c030_fsamps[i],\n\t\t\tpos, g_c030_fsamps[pos]);\n#endif\n\t\ti++;\n\t\tpos++;\n\t}\n\tg_num_c030_fsamps = pos;\n\n\treturn 1;\n}\n\n\nint g_sound_play_depth = 0;\n\n// sound_play(): forms the samples from the last sample time to the current\n//  time.  Can be called anytime from anywhere.  This is how KEGS handles\n//  dynamic sound changes (say, disabling an Ensoniq oscillator manually):\n//  when it's turned off, call sound_play() to play up to this moment, then\n//  the next time sound_play() is called, it will just know this osc is off\n// So, on any sound-related state change, call sound_play().\n\nvoid\nsound_play(dword64 dfcyc)\n{\n\tAy8913\t*ay8913ptr;\n\tint\t*outptr, *outptr_start;\n\tword32\t*sndptr;\n\tdouble\tlast_dsamp, dsamp_now, dvolume, dsamps;\n\tword32\tuval1, uval0;\n\tint\tval, val0, val1, pos, snd_buf_init, num_samps, num_pairs;\n\tint\tsound_mask, ivol;\n\tint\ti, j;\n\n\tg_num_snd_plays++;\n\tif(g_sound_play_depth) {\n\t\thalt_printf(\"Nested sound_play!\\n\");\n\t}\n\n\tg_sound_play_depth++;\n\n\t/* calc sample num */\n\n\tdsamps = dfcyc * g_dsamps_per_dfcyc;\n\tlast_dsamp = g_last_sound_play_dsamp;\n\tnum_samps = (int)(dsamps - g_last_sound_play_dsamp);\n\n\tdsamp_now = last_dsamp + (double)num_samps;\n\n\tif(num_samps < 1) {\n\t\t/* just say no */\n\t\tg_sound_play_depth--;\n\t\treturn;\n\t}\n\n\tdbg_log_info(dfcyc, (word32)(dword64)dsamp_now, num_samps, 0x200);\n\n\tif(num_samps > MAX_SND_BUF) {\n\t\tprintf(\"num_samps: %d, too big!\\n\", num_samps);\n\t\tg_sound_play_depth--;\n\t\treturn;\n\t}\n\n\toutptr_start = &(g_samp_buf[0]);\n\toutptr = outptr_start;\n\n\tsnd_buf_init = sound_play_c030(dfcyc, (dword64)dsamp_now, outptr_start,\n\t\t\t\t\t\t\t\tnum_samps);\n\n\tsnd_buf_init = doc_play(dfcyc, last_dsamp, dsamp_now, num_samps,\n\t\t\t\t\t\tsnd_buf_init, outptr_start);\n\n\tnum_pairs = 0;\n\t// Do Mockinboard channels\n\tfor(i = 0; i < 2; i++) {\t\t\t// Pair: 0 or 1\n\t\tay8913ptr = &(g_mockingboard.pair[i].ay8913);\n\t\tfor(j = 0; j < 3; j++) {\t\t// Channels: A, B, or C\n\t\t\tif((ay8913ptr->regs[8 + j] & 0x1f) == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tnum_pairs = 2;\n\t\t\tg_last_mock_vbl_count = g_vbl_count;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif((g_vbl_count - g_last_mock_vbl_count) < 120) {\n\t\t// Keep playing for 2 seconds, to avoid some static issues\n\t\tnum_pairs = 2;\n\t}\n\tif(num_pairs) {\n\t\tsound_mask = -1;\n\t\tif(snd_buf_init == 0) {\n\t\t\tsound_mask = 0;\n\t\t\tsnd_buf_init++;\n\t\t}\n\t\toutptr = outptr_start;\n\t\tivol = -((VAL_MOCK_RANGE * 3 / (8 * 15)) * g_doc_vol);\n\t\t\t// Do 3/8 of range below 0, leaving 5/8 above 0\n\t\tfor(i = 0; i < num_samps; i++) {\n\t\t\toutptr[0] = (outptr[0] & sound_mask) + ivol;\n\t\t\toutptr[1] = (outptr[1] & sound_mask) + ivol;\n\t\t\toutptr += 2;\n\t\t}\n\t\tfor(i = 0; i < 16; i++) {\n\t\t\tdvolume = (g_doc_vol * VAL_MOCK_RANGE) / (15.0 * 3.0);\n\t\t\tivol = (int)(g_ay8913_ampl_factor[i] * dvolume);\n\t\t\tg_mock_volume[i] = ivol;\n\t\t}\n\t}\n\tfor(i = 0; i < num_pairs; i++) {\n\t\tif(g_mockingboard.disable_mask) {\n\t\t\tprintf(\"dsamp:%lf\\n\", dsamps);\n\t\t}\n\n\t\tsound_mock_envelope(i, &(g_mock_env_vol[0]), num_samps,\n\t\t\t\t\t\t\t&(g_mock_volume[0]));\n\t\tsound_mock_noise(i, &(g_mock_noise_bytes[0]), num_samps);\n\t\tfor(j = 0; j < 3; j++) {\n\t\t\tsound_mock_play(i, j, outptr_start,\n\t\t\t\t&(g_mock_env_vol[0]), &(g_mock_noise_bytes[0]),\n\t\t\t\t&(g_mock_volume[0]), num_samps);\n\t\t}\n\t}\n\n\tg_last_sound_play_dsamp = dsamp_now;\n\n\toutptr = outptr_start;\n\tpos = g_sound_shm_pos;\n\tsndptr = g_sound_shm_addr;\n\n#if 0\n\tprintf(\"samps_left: %d, num_samps: %d\\n\", samps_left, num_samps);\n#endif\n\n\tif(g_audio_enable != 0) {\n\t\tif(snd_buf_init) {\n\t\t\t/* convert sound buf */\n\t\t\tfor(i = 0; i < num_samps; i++) {\n\t\t\t\tval0 = outptr[0];\n\t\t\t\tval1 = outptr[1];\n\t\t\t\tval = val0;\n\t\t\t\tif(val0 > 32767) {\n\t\t\t\t\tval = 32767;\n\t\t\t\t}\n\t\t\t\tif(val0 < -32768) {\n\t\t\t\t\tval = -32768;\n\t\t\t\t}\n\t\t\t\tuval0 = val & 0xffffU;\n\t\t\t\tval = val1;\n\t\t\t\tif(val1 > 32767) {\n\t\t\t\t\tval = 32767;\n\t\t\t\t}\n\t\t\t\tif(val1 < -32768) {\n\t\t\t\t\tval = -32768;\n\t\t\t\t}\n\t\t\t\tuval1 = val & 0xffffU;\n\t\t\t\toutptr += 2;\n\n#if defined(__linux__) || defined(OSS)\n\t\t\t\t/* Linux seems to expect little-endian */\n\t\t\t\t/*  samples always, even on PowerPC */\n# ifdef KEGS_BIG_ENDIAN\n\t\t\t\tsndptr[pos] = ((uval1 & 0xff) << 24) +\n\t\t\t\t\t\t((uval1 & 0xff00) << 8) +\n\t\t\t\t\t\t((uval0 & 0xff) << 8) +\n\t\t\t\t\t\t((uval0 >> 8) & 0xff);\n# else\n\t\t\t\tsndptr[pos] = (uval1 << 16) + (uval0 & 0xffff);\n# endif\n#else\n# ifdef KEGS_BIG_ENDIAN\n\t\t\t\tsndptr[pos] = (uval0 << 16) + uval1;\n# else\n\t\t\t\tsndptr[pos] = (uval1 << 16) + uval0;\n# endif\n#endif\n\t\t\t\tpos++;\n\t\t\t\tif(pos >= SOUND_SHM_SAMP_SIZE) {\n\t\t\t\t\tpos = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(g_queued_nonsamps) {\n\t\t\t\t/* force out old 0 samps */\n\t\t\t\tsnddrv_send_sound(0, g_queued_nonsamps);\n\t\t\t\tg_queued_nonsamps = 0;\n\t\t\t}\n\t\t\tif(g_sound_file_str) {\n\t\t\t\tsend_sound_to_file(g_sound_shm_addr,\n\t\t\t\t\t\tg_sound_shm_pos, num_samps, 1);\n\t\t\t}\n\t\t\tg_queued_samps += num_samps;\n\t\t} else {\n\t\t\t/* move pos */\n\t\t\tpos += num_samps;\n\t\t\twhile(pos >= SOUND_SHM_SAMP_SIZE) {\n\t\t\t\tpos -= SOUND_SHM_SAMP_SIZE;\n\t\t\t}\n\t\t\tif(g_sound_file_str) {\n\t\t\t\tsend_sound_to_file(0, g_sound_shm_pos,\n\t\t\t\t\t\t\t\tnum_samps, 0);\n\t\t\t}\n\t\t\tif(g_queued_samps) {\n\t\t\t\t/* force out old non-0 samps */\n\t\t\t\tsnddrv_send_sound(1, g_queued_samps);\n\t\t\t\tg_queued_samps = 0;\n\t\t\t}\n\t\t\tg_queued_nonsamps += num_samps;\n\t\t}\n\t}\n\n\tg_sound_shm_pos = pos;\n\n\tif(g_audio_enable != 0) {\n\t\tif(g_queued_samps >= (g_audio_rate/60)) {\n\t\t\tsnddrv_send_sound(1, g_queued_samps);\n\t\t\tg_queued_samps = 0;\n\t\t}\n\n\t\tif(g_queued_nonsamps >= (g_audio_rate/60)) {\n\t\t\tsnddrv_send_sound(0, g_queued_nonsamps);\n\t\t\tg_queued_nonsamps = 0;\n\t\t}\n\t}\n\n\tg_last_sound_play_dsamp = dsamp_now;\n\n\tg_sound_play_depth--;\n}\n\nvoid\nsound_mock_envelope(int pair, int *env_ptr, int num_samps, int *vol_ptr)\n{\n\tAy8913\t*ay8913ptr;\n\tdouble\tdmul, denv_period, dusecs_per_samp;\n\tdword64\tenv_dsamp, dsamp_inc;\n\tword32\tampl, eff_ampl, reg13, env_val, env_period;\n\tint\ti;\n\n\t// This routine calculates a fixed-point increment to apply\n\t//  to env_dsamp, where the envelope value is in bits 44:40 (bit\n\t//  44 is to track the alternating waveform, 43:40 is the env_ampl).\n\t// This algorithm does not properly handle dynamically changing the\n\t//  envelope period in the middle of a step.  In the AY8913, the\n\t//  part counts up to the env_period, and if the period is changed\n\t//  to a value smaller than the current count, it steps immediately\n\t//  to the next step.  This routine will wait for enough fraction\n\t//  to accumulate before stepping.  At most, this can delay the step\n\t//  by almost the new count time (if the new period is smaller), but\n\t//  no more.  I suspect this is not noticeable.\n\tif(num_samps > MAX_MOCK_ENV_SAMPLES) {\n\t\thalt_printf(\"envelope overflow!: %d\\n\", num_samps);\n\t\treturn;\n\t}\n\n\tay8913ptr = &(g_mockingboard.pair[pair].ay8913);\n\tampl = ay8913ptr->regs[8] | ay8913ptr->regs[9] | ay8913ptr->regs[10];\n\tif((ampl & 0x10) == 0) {\n\t\t// No one uses the envelope\n\t\treturn;\n\t}\n\n\tenv_dsamp = ay8913ptr->env_dsamp;\n\tenv_period = ay8913ptr->regs[11] + (256 * ay8913ptr->regs[12]);\n\tif(env_period == 0) {\n\t\tdenv_period = 0.5;\t\t// To match MAME\n\t} else {\n\t\tdenv_period = (double)env_period;\n\t}\n\tdmul = (1.0 / 16.0) * (1 << 20) * (1 << 20);\t// (1ULL << 40) / 16.0\n\t// Calculate amount counter will count in one sample.\n\t// inc_per_tick 62.5KHz tick: (1/env_period)\n\t// inc_per_dfcyc: (1/(16*env_period))\n\t// inc_per_samp = inc_per_dfcyc * g_fcyc_per_samp\n\tdusecs_per_samp = g_fcyc_per_samp / 65536.0;\n\tdsamp_inc = (dword64)((dmul * dusecs_per_samp / denv_period));\n\t\t\t// Amount to inc per sample, fixed point, 40 bit frac\n\n\treg13 = ay8913ptr->regs[13];\t\t\t// \"reg15\", env ctrl\n\teff_ampl = 0;\n\tfor(i = 0; i < num_samps; i++) {\n\t\tenv_dsamp = (env_dsamp + dsamp_inc) & 0x9fffffffffffULL;\n\t\tenv_val = (env_dsamp >> 40) & 0xff;\n\t\teff_ampl = env_val & 0xf;\n\t\tif((reg13 & 4) == 0) {\n\t\t\teff_ampl = 15 - eff_ampl;\t// not attack\n\t\t}\n\t\tif((reg13 & 8) && (reg13 & 2)) {\n\t\t\t// continue and alternate\n\t\t\tif(env_val & 0x10) {\n\t\t\t\teff_ampl = 15 - eff_ampl;\n\t\t\t}\n\t\t}\n\t\tif(((reg13 & 8) == 0) && (env_val >= 0x10)) {\n\t\t\teff_ampl = 0;\n\t\t\tampl = 0;\t\t// Turn off envelope\n\t\t\tenv_dsamp |= (0x80ULL << 40);\n\t\t} else if((reg13 & 1) && (env_val >= 0x10)) {\n\t\t\teff_ampl = ((reg13 >> 1) ^ (reg13 >> 2)) & 1;\n\t\t\teff_ampl = eff_ampl * 15;\n\t\t\tampl = eff_ampl;\t// Turn off envelope\n\t\t\tenv_dsamp |= (0x80ULL << 40);\n\t\t}\n\t\t*env_ptr++ = vol_ptr[eff_ampl & 0xf];\n\t}\n\tay8913ptr->env_dsamp = env_dsamp;\n}\n\nvoid\nsound_mock_noise(int pair, byte *noise_ptr, int num_samps)\n{\n\tAy8913\t*ay8913ptr;\n\tword32\tampl, mix, noise_val, noise_samp, noise_period, xor, samp_inc;\n\tint\tdoit;\n\tint\ti;\n\n\tif(num_samps > MAX_MOCK_ENV_SAMPLES) {\n\t\thalt_printf(\"noise overflow!: %d\\n\", num_samps);\n\t\treturn;\n\t}\n\n\tay8913ptr = &(g_mockingboard.pair[pair].ay8913);\n\tdoit = 0;\n\tfor(i = 0; i < 3; i++) {\n\t\tampl = ay8913ptr->regs[8 + i];\n\t\tmix = ay8913ptr->regs[7] >> i;\n\t\tif((ampl != 0) && ((mix & 8) == 0)) {\n\t\t\tdoit = 1;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif(!doit) {\n\t\t// No channel looks at noise, don't bother\n\t\treturn;\n\t}\n\n\tnoise_val = ay8913ptr->noise_val;\n\tnoise_samp = ay8913ptr->noise_samp;\n\tnoise_period = (ay8913ptr->regs[6] & 0x1f);\n\tnoise_period = noise_period << 16;\n\tsamp_inc = (word32)(g_fcyc_per_samp / 16.0);\n\t\t\t// Amount to inc per sample\n\tif(noise_samp >= noise_period) {\n\t\t// Period changed during sound, reset\n\t\tnoise_samp = noise_period;\n\t}\n\tfor(i = 0; i < num_samps; i++) {\n\t\tnoise_samp += samp_inc;\n\t\tif(noise_samp >= noise_period) {\n\t\t\t// HACK: handle fraction\n\t\t\t// 17-bit LFSR, algorithm from MAME:sound/ay8910.cpp\n\t\t\t// val = val ^ (((val & 1) ^ ((val >> 3) & 1)) << 17)\n\t\t\txor = 0;\n\t\t\txor = (noise_val ^ (noise_val >> 3)) & 1;\n\t\t\tnoise_val = (noise_val ^ (xor << 17)) >> 1;\n\t\t\tnoise_samp -= noise_period;\n\t\t}\n\t\tnoise_ptr[i] = noise_val & 1;\n\t}\n\tay8913ptr->noise_samp = noise_samp;\n\tay8913ptr->noise_val = noise_val;\n}\n\nint g_did_mock_print = 100;\n\nvoid\nsound_mock_play(int pair, int channel, int *outptr, int *env_ptr,\n\t\t\t\tbyte *noise_ptr, int *vol_ptr, int num_samps)\n{\n\tAy8913\t*ay8913ptr;\n\tword32\tampl, mix, tone_samp, tone_period, toggle_tone;\n\tword32\tsamp_inc, noise_val;\n\tint\tout, ival, do_print;\n\tint\ti;\n\n\tif((g_mockingboard.disable_mask >> ((pair * 3) + channel)) & 1) {\n\t\t// This channel is disabled\n\t\treturn;\n\t}\n\n\tay8913ptr = &(g_mockingboard.pair[pair].ay8913);\n\tampl = ay8913ptr->regs[8 + channel] & 0x1f;\n\tif(ampl == 0) {\n\t\treturn;\n\t}\n\ttoggle_tone = ay8913ptr->toggle_tone[channel];\t\t// 0 or 1\n\tmix = (ay8913ptr->regs[7] >> channel) & 9;\n\tif(mix == 9) {\n\t\t// constant tone: output will be ampl for this channel.\n\t\tif(ampl & 0x10) {\t\t// Envelope!\n\t\t\t// The envelope can make the tone, so must calculate it\n\t\t} else {\n\t\t\t// HACK: do nothing for now\n\t\t\treturn;\n\t\t}\n\t}\n\toutptr += pair;\t\t\t// pair[1] is right\n\ttone_samp = ay8913ptr->tone_samp[channel];\n\ttone_period = ay8913ptr->regs[2*channel] +\n\t\t\t\t\t(256 * ay8913ptr->regs[2*channel + 1]);\n\ttone_period = tone_period << 16;\n\tsamp_inc = (word32)(g_fcyc_per_samp / 8.0);\n\t\t\t// Amount to inc per sample\n\tdo_print = 0;\n\tif(g_mockingboard.disable_mask) {\n\t\tprintf(\"Doing %d samps, mix:%d, ampl:%02x\\n\", num_samps, mix,\n\t\t\t\t\t\t\t\t\tampl);\n\t\tdo_print = 1;\n\t\tg_did_mock_print = 0;\n\t}\n\tif((num_samps > 500) && (g_did_mock_print == 0)) {\n\t\tdo_print = 1;\n\t\tg_did_mock_print = 1;\n\t\tprintf(\"Start of %d sample, channel %d mix:%02x ampl:%02x \"\n\t\t\t\"toggle_tone:%02x\\n\", num_samps, channel, mix, ampl,\n\t\t\ttoggle_tone);\n\t\tprintf(\" tone_period:%08x, tone_samp:%08x, samp_inc:%08x\\n\",\n\t\t\ttone_period, tone_samp, samp_inc);\n\t}\n\tif(tone_samp >= tone_period) {\n\t\t// Period changed during sound, reset it\n\t\ttone_samp = tone_period;\n\t}\n\tfor(i = 0; i < num_samps; i++) {\n\t\ttone_samp += samp_inc;\n\t\tif(tone_samp >= tone_period) {\n\t\t\t// HACK: handle toggling mid-sample...\n\t\t\ttoggle_tone ^= 1;\n\t\t\tif(do_print) {\n\t\t\t\tprintf(\"i:%d tone_toggled to %d, tone_period:\"\n\t\t\t\t\t\"%04x, pre tone_samp:%08x\\n\", i,\n\t\t\t\t\ttoggle_tone, tone_period, tone_samp);\n\t\t\t}\n\t\t\ttone_samp -= tone_period;\n\t\t\tif(do_print) {\n\t\t\t\tprintf(\"post tone_samp:%08x\\n\", tone_samp);\n\t\t\t}\n\t\t}\n\t\tnoise_val = noise_ptr[i] & 1;\n\t\tout = (toggle_tone || (mix & 1)) &\n\t\t\t\t\t\t((noise_val & 1) || (mix & 8));\n\t\t\t// Careful mix of || and & above...\n\t\tival = vol_ptr[ampl & 0xf];\n\t\tif(ampl & 0x10) {\t\t\t// Envelope\n\t\t\tival = env_ptr[i];\n\t\t}\n\t\t*outptr += ival*out;\n\t\toutptr += 2;\n\t}\n\tay8913ptr->tone_samp[channel] = tone_samp;\n\tay8913ptr->toggle_tone[channel] = toggle_tone;\n}\n\nword32\nsound_read_c030(dword64 dfcyc)\n{\n\tsound_write_c030(dfcyc);\n\treturn float_bus(dfcyc);\n}\n\nvoid\nsound_write_c030(dword64 dfcyc)\n{\n\tint\tnum;\n\n\tnum = g_num_c030_fsamps;\n\tif(num >= MAX_C030_TIMES) {\n\t\thalt_printf(\"Too many clicks per vbl: %d\\n\", num);\n\t\treturn;\n\t}\n\n\tg_c030_fsamps[num] = (float)(dfcyc * g_dsamps_per_dfcyc -\n\t\t\t\t\t\tg_last_sound_play_dsamp);\n\tg_num_c030_fsamps = num + 1;\n\tdbg_log_info(dfcyc, num, 0, 0xc030);\n\n\tdoc_printf(\"touch c030, num this vbl: %04x\\n\", num);\n}\n\n"
  },
  {
    "path": "gsplus/src/sound.h",
    "content": "#ifdef INCLUDE_RCSID_C\n#endif\n\n/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#if !defined(_WIN32) && !defined(__CYGWIN__)\n# include <sys/ipc.h>\n# include <sys/shm.h>\n#endif\n\n#define SOUND_SHM_SAMP_SIZE\t\t(32*1024)\n\n#define SAMPLE_SIZE\t\t2\n#define NUM_CHANNELS\t\t2\n#define SAMPLE_CHAN_SIZE\t(SAMPLE_SIZE * NUM_CHANNELS)\n\nSTRUCT(Doc_reg) {\n\tdouble\tdsamp_ev;\n\tdouble\tdsamp_ev2;\n\tdouble\tcomplete_dsamp;\n\tint\tsamps_left;\n\tword32\tcur_acc;\n\tword32\tcur_inc;\n\tword32\tcur_start;\n\tword32\tcur_end;\n\tword32\tcur_mask;\n\tint\tsize_bytes;\n\tint\tevent;\n\tint\trunning;\n\tint\thas_irq_pending;\n\tword32\tfreq;\n\tword32\tvol;\n\tword32\twaveptr;\n\tword32\tctl;\n\tword32\twavesize;\n\tword32\tlast_samp_val;\n};\n\n// Mockingboard contains two pairs.  Each pair is a 6522 interfacing\n//  to an AY-8913 to generate sounds.  Eacho AY-8913 contains 3 channels of\n//  sound.  Model each pair separately.\n\nSTRUCT(Mos6522) {\n\tbyte\torb;\n\tbyte\tora;\n\tbyte\tddrb;\n\tbyte\tddra;\n\tword32\ttimer1_latch;\n\tword32\ttimer1_counter;\n\tword32\ttimer2_latch;\n\tword32\ttimer2_counter;\n\tbyte\tsr;\n\tbyte\tacr;\n\tbyte\tpcr;\n\tbyte\tifr;\n\tbyte\tier;\n};\n\nSTRUCT(Ay8913) {\n\tbyte\tregs[16];\n\tbyte\treg_addr_latch;\n\tbyte\ttoggle_tone[3];\t\t// Channel A,B,C: 0 = low, 1 = high\n\tword32\ttone_samp[3];\n\tword32\tnoise_val;\n\tword32\tnoise_samp;\n\tdword64\tenv_dsamp;\n};\n\nSTRUCT(Mock_pair) {\n\tMos6522\tmos6522;\n\tAy8913\tay8913;\n};\n\nSTRUCT(Mockingboard) {\n\tMock_pair pair[2];\n\tword32\tdisable_mask;\n};\n\n/* prototypes for win32snd_driver.c functions */\nvoid win32snd_init(word32 *);\nvoid win32snd_shutdown(void);\nvoid child_sound_init_win32(void);\nint win32_send_audio(byte *ptr, int size);\n\n/* Prototypes for macsnd_driver.c functions */\nint mac_send_audio(byte *ptr, int in_size);\nvoid macsnd_init();\n\n/* Prototypes for pulseaudio_driver.c functions */\nint pulse_audio_init();\nint pulse_audio_send_audio(byte *ptr, int in_size);\nvoid pulse_audio_shutdown(void);\n\n"
  },
  {
    "path": "gsplus/src/sound_driver.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// Routines for managing sending sound samples to the hardware.  The\n//  primary routines are snddrv_init() for initializing the sound hardware,\n//  and snddrv_send_sound() which calls the driver for the sound hardware\n//  in use to play samples.\n// Linux forks a child process to manage /dev/dsp (so KEGS will not block)\n//  so the lowerlevel routines for all sound hardware start with child_().\n\n#include \"defc.h\"\n#include \"sound.h\"\n\n#if defined(__linux__) || defined(OSS)\n# include <sys/soundcard.h>\n#endif\n\n#ifndef _WIN32\n# include <sys/socket.h>\n# include <netinet/in.h>\n#endif\n#include <errno.h>\n\n#if defined(_WIN32) || defined(__CYGWIN__) || defined(MAC)\n# define KEGS_CAN_FORK\t0\n#else\n\t// Linux, or other Unix, we may fork and run sound in the child\n# define KEGS_CAN_FORK\t1\n#endif\n\nextern int Verbose;\n\nextern int g_audio_rate;\nextern int g_audio_enable;\nextern double g_last_sound_play_dsamp;\nextern word32 *g_sound_shm_addr;\n\nint\tg_preferred_rate = 48000;\nint\tg_audio_socket = -1;\nint\tg_bytes_written = 0;\nint\tg_pulse_audio = 0;\nint\tg_pipe_fd[2] = { -1, -1 };\nint\tg_pipe2_fd[2] = { -1, -1 };\n\n#define ZERO_BUF_SIZE\t\t2048\n\nword32 g_snd_zero_buf[ZERO_BUF_SIZE];\n\n#define ZERO_PAUSE_SAFETY_SAMPS\t\t(g_audio_rate >> 5)\n#define ZERO_PAUSE_NUM_SAMPS\t\t(4*g_audio_rate)\n\nint\tg_zeroes_buffered = 0;\nint\tg_zeroes_seen = 0;\nint\tg_sound_paused = 0;\nint\tg_childsnd_vbl = 0;\nint\tg_childsnd_pos = 0;\n\nint child_sound_init_linux(void);\n\nvoid\nsnddrv_init()\n{\n\tword32\t*shmaddr;\n\tint\tsize, ret, use_shm;\n\n\tret = 0;\n\tif(ret) {\t\t\t// Avoid unused var warning\n\t}\n\n\tg_zeroes_buffered = 0;\n\tg_zeroes_seen = 0;\n\tg_sound_paused = 0;\n\n\tg_childsnd_pos = 0;\n\tg_childsnd_vbl = 0;\n\n\tif(g_audio_enable == 0) {\n\t\tsound_set_audio_rate(g_preferred_rate);\n\t\treturn;\n\t}\n\tprintf(\"snddrv_init, g_audio_enable:%d\\n\", g_audio_enable);\n\n\tsize = SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE;\n\n\tuse_shm = KEGS_CAN_FORK;\n#ifdef PULSE_AUDIO\n\tuse_shm = 0;\n#endif\n\tif(!use_shm) {\n\t\t/* windows and mac, and Linux Pulse Audio */\n\t\tshmaddr = malloc(size);\n\t\tmemset(shmaddr, 0, size);\n\t\tg_sound_shm_addr = shmaddr;\n#ifdef MAC\n\t\tmacsnd_init();\n\t\treturn;\n#endif\n#ifdef _WIN32\n\t\twin32snd_init(shmaddr);\n\t\treturn;\n#endif\n#ifdef PULSE_AUDIO\n\t\tret = pulse_audio_init(shmaddr);\n\t\tif(ret == 0) {\n\t\t\tg_pulse_audio = 1;\n\t\t\treturn;\t\t// Success!\n\t\t}\n\t\tfree(shmaddr);\n\t\tg_sound_shm_addr = 0;\n\t\tuse_shm = 1;\n\t\t// Otherwise, fall back on /dev/dsp\n#endif\n\t}\n\n\tif(use_shm) {\n\t\tsound_child_fork(size);\n\t}\n}\n\nvoid\nsound_child_fork(int size)\n{\n#if KEGS_CAN_FORK\n\tword32\t*shmaddr;\n\tint\tshmid, pid, tmp, ret;\n\tint\ti;\n\n\tdoc_printf(\"In sound_child_fork, size:%d\\n\", size);\n\tshmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);\n\tif(shmid < 0) {\n\t\tprintf(\"sound_init: shmget ret: %d, errno: %d\\n\", shmid,\n\t\t\terrno);\n\t\texit(2);\n\t}\n\n\tshmaddr = shmat(shmid, 0, 0);\n\ttmp = (int)PTR2WORD(shmaddr);\n\tif(tmp == -1) {\n\t\tprintf(\"sound_init: shmat ret: %p, errno: %d\\n\", shmaddr,\n\t\t\terrno);\n\t\texit(3);\n\t}\n\n\tret = shmctl(shmid, IPC_RMID, 0);\n\tif(ret < 0) {\n\t\tprintf(\"sound_init: shmctl ret: %d, errno: %d\\n\", ret, errno);\n\t\texit(4);\n\t}\n\n\tg_sound_shm_addr = shmaddr;\n\tprintf(\"shmaddr: %p\\n\", shmaddr);\n\n\tfflush(stdout);\n\n\t/* prepare pipe so parent can signal child each other */\n\t/*  pipe[0] = read side, pipe[1] = write end */\n\tret = pipe(&g_pipe_fd[0]);\n\tif(ret < 0) {\n\t\tprintf(\"sound_init: pipe ret: %d, errno: %d\\n\", ret, errno);\n\t\texit(5);\n\t}\n\tret = pipe(&g_pipe2_fd[0]);\n\tif(ret < 0) {\n\t\tprintf(\"sound_init: pipe ret: %d, errno: %d\\n\", ret, errno);\n\t\texit(5);\n\t}\n\n\n\tdoc_printf(\"pipes: pipe_fd = %d, %d  pipe2_fd: %d,%d\\n\",\n\t\tg_pipe_fd[0], g_pipe_fd[1], g_pipe2_fd[0], g_pipe2_fd[1]);\n\tfflush(stdout);\n\n\tpid = fork();\n\tswitch(pid) {\n\tcase 0:\n\t\t/* child */\n\t\t/* close stdin and write-side of pipe */\n\t\tclose(0);\n\t\t/* Close other fds to make sure X window fd is closed */\n\t\tfor(i = 3; i < 100; i++) {\n\t\t\tif((i != g_pipe_fd[0]) && (i != g_pipe2_fd[1])) {\n\t\t\t\tclose(i);\n\t\t\t}\n\t\t}\n\t\tclose(g_pipe_fd[1]);\t\t/*make sure write pipe closed*/\n\t\tclose(g_pipe2_fd[0]);\t\t/*make sure read pipe closed*/\n\t\tchild_sound_loop(g_pipe_fd[0], g_pipe2_fd[1], g_sound_shm_addr);\n\t\tprintf(\"Child sound loop returned\\n\");\n\t\texit(0);\n\tcase -1:\n\t\t/* error */\n\t\tprintf(\"sound_init: fork ret: -1, errno: %d\\n\", errno);\n\t\texit(6);\n\tdefault:\n\t\t/* parent */\n\t\t/* close read-side of pipe1, and the write side of pipe2 */\n\t\tclose(g_pipe_fd[0]);\n\t\tclose(g_pipe2_fd[1]);\n\t\tdoc_printf(\"Child is pid: %d\\n\", pid);\n\t}\n\n\tparent_sound_get_sample_rate(g_pipe2_fd[0]);\n#endif\n\tif(size) {\n\t\t// Avoid unused param warning\n\t}\n}\n\nvoid\nparent_sound_get_sample_rate(int read_fd)\n{\n\tword32\taudio_rate, tmp;\n\tint\tret;\n\n\tret = (int)read(read_fd, &audio_rate, 4);\n\tif(ret != 4) {\n\t\tprintf(\"parent dying, could not get sample rate from child\\n\");\n\t\tprintf(\"ret: %d, fd: %d errno:%d\\n\", ret, read_fd, errno);\n\t\texit(1);\n\t}\n\tret = (int)read(read_fd, &tmp, 4);\n\tif(ret != 4) {\n\t\tprintf(\"parent dying, could not get audio status from child\\n\");\n\t\tprintf(\"ret: %d, fd: %d errno:%d\\n\", ret, read_fd, errno);\n\t\texit(1);\n\t}\n\tif(tmp == 0) {\n\t\tg_audio_enable = 0;\n\t\tprintf(\"Failed to init Sound, turning off audio\\n\");\n\t}\n\tclose(read_fd);\n\n\tsound_set_audio_rate(audio_rate);\n}\n\nvoid\nsnddrv_shutdown()\n{\n#ifdef _WIN32\n\twin32snd_shutdown();\n#else\n\tif((g_audio_enable != 0) && (g_pipe_fd[1] >= 0)) {\n\t\tclose(g_pipe_fd[1]);\n\t}\n#endif\n#ifdef PULSE_AUDIO\n\tif(g_pulse_audio) {\n\t\tpulse_audio_shutdown();\n\t}\n#endif\n}\n\nvoid\nsnddrv_send_sound(int real_samps, int size)\n{\n\tword32\ttmp;\n\tint\tret, call_playit;\n\n\tif(g_audio_enable == 0) {\n\t\tprintf(\"Entered send_sound but audio off!\\n\");\n\t\texit(2);\n\t}\n\n\tif(real_samps) {\n\t\ttmp = size + 0xa2000000;\n\t} else {\n\t\ttmp = size + 0xa1000000;\n\t}\n\t//doc_log_rout(\"send_sound\", -1, g_last_sound_play_dsamp,\n\t//\t\t\t\t\t(real_samps << 30) + size);\n\n\tcall_playit = 0;\n#if defined(MAC) || defined(_WIN32)\n\tcall_playit = 1;\t\t\t// Never fork child mac/windows\n#endif\n\tif(call_playit || g_pulse_audio) {\n\t\tchild_sound_playit(tmp);\n\t\treturn;\n\t}\n\n\t/* Although this looks like a big/little-endian issue, since the */\n\t/*  child is also reading an int, it just works with no byte swap */\n\tret = (int)write(g_pipe_fd[1], &tmp, 4);\n\tif(ret != 4) {\n\t\thalt_printf(\"send_sound, wr ret: %d, errno: %d\\n\", ret, errno);\n\t}\n}\n\nvoid\nchild_sound_playit(word32 tmp)\n{\n\tint\tsize;\n\n\tsize = tmp & 0xffffff;\n\n\t//printf(\"child_sound_playit: %08x\\n\", tmp);\n\n\tif((tmp >> 24) == 0xa2) {\t\t\t// play sound\n\t\tif(g_zeroes_buffered) {\n\t\t\treliable_zero_write(g_zeroes_buffered);\n\t\t}\n\n\t\tg_zeroes_buffered = 0;\n\t\tg_zeroes_seen = 0;\n\n\t\tif((size + g_childsnd_pos) > SOUND_SHM_SAMP_SIZE) {\n\t\t\treliable_buf_write(g_sound_shm_addr, g_childsnd_pos,\n\t\t\t\t\tSOUND_SHM_SAMP_SIZE - g_childsnd_pos);\n\t\t\tsize = (g_childsnd_pos + size) - SOUND_SHM_SAMP_SIZE;\n\t\t\tg_childsnd_pos = 0;\n\t\t}\n\n\t\treliable_buf_write(g_sound_shm_addr, g_childsnd_pos, size);\n\n\t\tif(g_sound_paused) {\n\t\t\tprintf(\"Unpausing sound, zb: %d\\n\", g_zeroes_buffered);\n\t\t\tg_sound_paused = 0;\n\t\t}\n\n\t} else if((tmp >> 24) == 0xa1) {\t\t// play zeroes\n\t\tif(g_sound_paused) {\n\t\t\tif(g_zeroes_buffered < ZERO_PAUSE_SAFETY_SAMPS) {\n\t\t\t\tg_zeroes_buffered += size;\n\t\t\t}\n\t\t} else {\n\t\t\t/* not paused, send it through */\n\t\t\tg_zeroes_seen += size;\n\n\t\t\treliable_zero_write(size);\n\n\t\t\tif(g_zeroes_seen >= ZERO_PAUSE_NUM_SAMPS) {\n\t\t\t\tprintf(\"Pausing sound\\n\");\n\t\t\t\tg_sound_paused = 1;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tprintf(\"tmp received bad: %08x\\n\", tmp);\n\t\texit(3);\n\t}\n\n\tg_childsnd_pos += size;\n\twhile(g_childsnd_pos >= SOUND_SHM_SAMP_SIZE) {\n\t\tg_childsnd_pos -= SOUND_SHM_SAMP_SIZE;\n\t}\n\n\tg_childsnd_vbl++;\n\tif(g_childsnd_vbl >= 60) {\n\t\tg_childsnd_vbl = 0;\n#if 0\n\t\tprintf(\"sound bytes written: %06x\\n\", g_bytes_written);\n#endif\n\t\tg_bytes_written = 0;\n\t}\n}\n\nvoid\nreliable_buf_write(word32 *shm_addr, int pos, int size)\n{\n\tbyte\t*ptr;\n\tint\tret;\n\n\tif(size < 1 || pos < 0 || pos > SOUND_SHM_SAMP_SIZE ||\n\t\t\t\tsize > SOUND_SHM_SAMP_SIZE ||\n\t\t\t\t(pos + size) > SOUND_SHM_SAMP_SIZE) {\n\t\tprintf(\"reliable_buf_write: pos: %04x, size: %04x\\n\",\n\t\t\tpos, size);\n\t\texit(1);\n\t}\n\n\tptr = (byte *)&(shm_addr[pos]);\n\tsize = size * 4;\n\n\twhile(size > 0) {\n\t\tret = child_send_samples(ptr, size);\n\n\t\tif(ret < 0) {\n\t\t\tprintf(\"audio write, errno: %d %s\\n\", errno,\n\t\t\t\t\t\t\tstrerror(errno));\n\t\t\texit(1);\n\t\t}\n\t\tsize = size - ret;\n\t\tptr += ret;\n\t\tg_bytes_written += ret;\n\t}\n\n}\n\nvoid\nreliable_zero_write(int amt)\n{\n\tint\tlen;\n\n\twhile(amt > 0) {\n\t\tlen = MY_MIN(amt, ZERO_BUF_SIZE);\n\t\treliable_buf_write(g_snd_zero_buf, 0, len);\n\t\tamt -= len;\n\t}\n}\n\n\nint\nchild_send_samples(byte *ptr, int size)\n{\n#ifdef _WIN32\n\treturn win32_send_audio(ptr, size);\n#else\n# ifdef MAC\n\treturn mac_send_audio(ptr, size);\n# else\n#  ifdef PULSE_AUDIO\n\tif(g_pulse_audio) {\n\t\treturn pulse_audio_send_audio(ptr, size);\n\t}\n#  endif\n\treturn (int)write(g_audio_socket, ptr, size);\n# endif\n#endif\n}\n\n// child_sound_loop(): used by Linux child process as the main loop, to read\n//  from pipe to get sample info every VBL, and use shm_addr to get samples\nvoid\nchild_sound_loop(int read_fd, int write_fd, word32 *shm_addr)\n{\n\tword32\ttmp, did_init;\n\tint\tret, ret1, ret2;\n\n\tdoc_printf(\"Child pipe fd: %d, shm_addr:%p\\n\", read_fd, shm_addr);\n\n\tg_audio_rate = g_preferred_rate;\n\n\tdid_init = 0;\n#if defined(__linux__) || defined(OSS)\n\tdid_init = child_sound_init_linux();\n#endif\n\n\ttmp = g_audio_rate;\n\tret1 = (int)write(write_fd, &tmp, 4);\n\ttmp = did_init;\n\tret2 = (int)write(write_fd, &tmp, 4);\n\tif((ret1) != 4 || (ret2 != 4)) {\n\t\tprintf(\"Unable to send back audio rate to parent\\n\");\n\t\tprintf(\"ret1: %d,%d fd: %d, errno:%d\\n\", ret1, ret2, write_fd,\n\t\t\t\t\t\t\t\terrno);\n\t\texit(1);\n\t}\n\tdoc_printf(\"Wrote to fd %d the audio rate\\n\", write_fd);\n\n\tclose(write_fd);\n\n\twhile(1) {\n\t\terrno = 0;\n\t\tret = (int)read(read_fd, &tmp, 4);\n\t\tif(ret <= 0) {\n\t\t\tprintf(\"child dying from ret: %d, errno: %d\\n\",\n\t\t\t\tret, errno);\n\t\t\tbreak;\n\t\t}\n\n\t\tchild_sound_playit(tmp);\n\t}\n\n\tclose(g_audio_socket);\n\n\texit(0);\n}\n\n\n#if defined(__linux__) || defined(OSS)\nint\nchild_sound_init_linux()\n{\n\tint\tstereo, sample_size, rate, fmt, ret;\n\n\tg_audio_socket = open(\"/dev/dsp\", O_WRONLY, 0);\n\tif(g_audio_socket < 0) {\n\t\tprintf(\"open /dev/dsp failed, ret: %d, errno:%d\\n\",\n\t\t\tg_audio_socket, errno);\n\t\treturn 0;\n\t}\n\n#if 0\n\tfragment = 0x00200009;\n\tret = ioctl(g_audio_socket, SNDCTL_DSP_SETFRAGMENT, &fragment);\n\tif(ret < 0) {\n\t\tprintf(\"ioctl SETFRAGEMNT failed, ret:%d, errno:%d\\n\",\n\t\t\tret, errno);\n\t\treturn 0;\n\t}\n#endif\n\n\tsample_size = 16;\n\tret = ioctl(g_audio_socket, SNDCTL_DSP_SAMPLESIZE, &sample_size);\n\tif(ret < 0) {\n\t\tprintf(\"ioctl SNDCTL_DSP_SAMPLESIZE failed, ret:%d, errno:%d\\n\",\n\t\t\tret, errno);\n\t\treturn 0;\n\t}\n\n#ifdef KEGS_BIG_ENDIAN\n\tfmt = AFMT_S16_BE;\n#else\n\tfmt = AFMT_S16_LE;\n#endif\n\tret = ioctl(g_audio_socket, SNDCTL_DSP_SETFMT, &fmt);\n\tif(ret < 0) {\n\t\tprintf(\"ioctl SNDCTL_DSP_SETFMT failed, ret:%d, errno:%d\\n\",\n\t\t\tret, errno);\n\t\treturn 0;\n\t}\n\n\tstereo = 1;\n\tret = ioctl(g_audio_socket, SNDCTL_DSP_STEREO, &stereo);\n\tif(ret < 0) {\n\t\tprintf(\"ioctl SNDCTL_DSP_STEREO failed, ret:%d, errno:%d\\n\",\n\t\t\tret, errno);\n\t\treturn 0;\n\t}\n\n\trate = g_audio_rate;\n\tret = ioctl(g_audio_socket, SNDCTL_DSP_SPEED, &rate);\n\tif(ret < 0) {\n\t\tprintf(\"ioctl SNDCTL_DSP_SPEED failed, ret:%d, errno:%d\\n\",\n\t\t\tret, errno);\n\t\treturn 0;\n\t}\n\tif(ret > 0) {\n\t\trate = ret;\t/* rate is returned value */\n\t}\n\tif(rate < 8000) {\n\t\tprintf(\"Audio rate of %d which is < 8000!\\n\", rate);\n\t\treturn 0;\n\t}\n\tg_audio_rate = rate;\n\n\tprintf(\"Sound initialized\\n\");\n\treturn 1;\n}\n#endif\n"
  },
  {
    "path": "gsplus/src/style_check",
    "content": "#!/usr/bin/perl -w\n# $KmKId: style_check,v 1.1 2020-06-14 02:52:13+00 kentd Exp $\n\n# Perl script to check for coding conventions\nuse English;\n\n$some_bad = 0;\n\nwhile($#ARGV >= 0) {\n\t$file = shift;\n\t$check_spaces = 0;\n\tif($file =~ /\\.c$/) {\n\t\t$check_spaces = 1;\n\t} elsif($file =~ /\\.s$/) {\n\t\t$check_spaces = 1;\n\t} elsif($file =~ /\\.k$/) {\n\t\t$check_spaces = 1;\n\t} elsif($file =~ /\\.h$/) {\n\t\t$check_spaces = 1;\n\t\tif($file =~ /^protos.*.h$/) {\n\t\t\tnext;\t\t\t# skip global_names.h\n\t\t}\n\t\tif($file =~ /^global_names.h$/) {\n\t\t\tnext;\t\t\t# skip global_names.h\n\t\t}\n\t\tif($file =~ /^knobs.h$/) {\n\t\t\tnext;\t\t\t# skip global_names.h\n\t\t}\n\t} else {\n\t\tnext;\t\t# skip this file\n\t}\n\tprint \"Style check: $file\\n\";\n\tif(-x $file) {\n\t\tprint \"File mode is executable\\n\";\n\t\t$some_bad++;\n\t}\n\topen(FILE, \"<$file\") || die \"Open: $file: $1\\n\";\n\t$line_num = 1;\n\tforeach $line (<FILE>) {\n\t\tchomp($line);\n\t\t$ign_tab_space = 0;\n\t\tif($line =~ m:^[/\\t]+{.*}:) {\n\t\t\t$ign_tab_space = 1;\n\t\t}\n\t\t$len = 0;\n\t\t$prev = 0;\n\t\t$pprev = 0;\n\t\t$bad = 0;\n\t\t$last_tab = -10;\n\t\tif($check_spaces) {\n\t\t\tif($line =~ / $/ || $line =~ /\t$/) {\n\t\t\t\tprint \"Line ends in tab or space\\n\";\n\t\t\t\t$bad++;\n\t\t\t}\n\t\t}\n\t\tif($line =~ /\\r$/) {\t\t# Line ends with Ctrl-M\n\t\t\tprint \"Windows linebreak detected\\n\";\n\t\t\t$bad = 101;\n\t\t}\n\t\twhile($line =~ m/^([^\"]*)(\"[^\"]*\")(.*)$/) {\n\t\t\t# Convert text in strings to '-' to avoid space checks\n\t\t\t$prev = $1;\n\t\t\t$post = $3;\n\t\t\t$quot = &dotify($2);\n\t\t\t$line = $prev . $quot . $post;\n\t\t}\n\t\twhile($line =~ m:^(.*)//(.*)$:) {\n\t\t\t# Convert text in comments to '-' to avoid space checks\n\t\t\t$prev = $1;\n\t\t\t$quot = &dotify($2);\n\t\t\t$line = $prev . \"::\" . $quot;\n\t\t}\n\t\t@chars = split(//, $line);\n\t\tforeach $char (@chars) {\n\t\t\t$len++;\n\t\t\tif(!$check_spaces) {\n\t\t\t\t# do nothing\n\t\t\t} elsif($char eq \"\\t\") {\n\t\t\t\t$len = (($len + 7) >> 3) * 8;\n\t\t\t\tif($prev eq ' ') {\n\t\t\t\t\tprint \"Space followed by tab\\n\";\n\t\t\t\t\t$bad++;\n\t\t\t\t}\n\t\t\t\t$last_tab = $len;\n\t\t\t} elsif($char eq \" \") {\n\t\t\t\tif($prev eq \"\\t\" && !$ign_tab_space) {\n\t\t\t\t\tprint \"Tab followed by space\\n\";\n\t\t\t\t\t$bad++;\n\t\t\t\t}\n\t\t\t\tif($prev eq \" \" && $pprev eq \" \" &&\n\t\t\t\t\t\t(($len - $last_tab) > 4)) {\n\t\t\t\t\tprint \"Too many spaces\\n\";\n\t\t\t\t\t$bad++;\n\t\t\t\t\tlast;\n\t\t\t\t}\n\t\t\t}\n\t\t\t$pprev = $prev;\n\t\t\t$prev = $char\n\t\t}\n\t\tif($check_spaces) {\n\t\t\tif(($len > 80) && ($line_num > 2)) {\n\t\t\t\tprint \"Line more than 80 columns\\n\";\n\t\t\t\t$bad++;\n\t\t\t}\n\t\t\t#print \"line $line has len $len\\n\";\n\t\t}\n\t\tif($bad) {\n\t\t\t$some_bad++;\n\t\t\tprint \"...at line $line_num in file $file\\n\";\n\t\t\tif($some_bad > 20) {\n\t\t\t\tdie \"Too many style errors\\n\";\n\t\t\t}\n\t\t\tif($bad >= 100) {\n\t\t\t\tnext;\t\t# Skip to next file\n\t\t\t}\n\t\t}\n\t\t$line_num++;\n\t}\n}\n\nif($some_bad) {\n\tdie \"Style errors\\n\";\n}\n\nexit 0;\n\nsub\ndotify\n{\n\tmy ($str) = @_;\n\tmy @chars = ();\n\tmy @outchars = ();\n\tmy ($char, $result);\n\n\t@chars = split(//, $str);\n\t@outchars = ();\n\tforeach $char (@chars) {\n\t\tif($char ne \"\\t\") {\n\t\t\t$char = \"-\";\n\t\t}\n\t\tpush(@outchars, $char);\n\t}\n\t$result = join('', @outchars);\n\t#print \"Old quote :$str:\\n\";\n\t#print \"New quote :$result:\\n\";\n\treturn $result;\n}\n"
  },
  {
    "path": "gsplus/src/undeflate.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// This file has routines for the undeflate uncompression algorithm for\n//  gzip/zip, and routines for reading .zip files.\n\n// Based on https://www.ietf.org/rfc/rfc1951.txt for Deflate algorithm,\n//  and https://www.ietf.org/rfc/rfc1952.txt for gzip file format.\n\n// .zip file format from:\n// https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.3.TXT\n\n#include \"defc.h\"\n\n// FILE *g_outf = 0;\n\n#define LENGTH_ENCODED\t0xffffff444449ULL\n\t// LENGTH_ENCODED encodes the first table of section 3.2.5 for\n\t//  fixed Huffman: 257-264 = 0 extra bits, length=3-10 (so 8 entries)\n\t//  then 265-268 = 1 extra bit, length=11... (so 4 entries), etc.\n\t//  which is encoded in each nibble of this word.  Code 285 (entry 29)\n\t//  has extra_bits=0, indicated by the encoded nibble being 0xf.\n#define DIST_ENCODED\t0xff22222222222224ULL\n\t// DIST_ENCODED encodes the second table of section 3.2.5 for the\n\t//  fixed Huffman table:  Codes 0-3 have 0 extra bits, dist=1,2,3,4\n\t//  (so 4 entries), codes 4,5 have 1 extra bit, dest=5... (so 2\n\t//  entries), etc.  0xf indicates an invalid entry\n\nword32\tg_undeflate_fixed_len_tab[512+1];\n\t\t\t// extrabits << 20 | bits << 16 | len/literal\nword32\tg_undeflate_fixed_dist_tab[32+1];\n\t\t\t// extrabits << 20 | bits << 16 | distance\nword32\tg_undeflate_length_tab[32+1];\t// extra_bits << 20 | len\nword32\tg_undeflate_dist_tab[32+1];\t// extra_bits << 20 | dist\nword32\tg_undeflate_bit_rev[512];\nword32 g_undeflate_lencode_positions[19] =\n{\t\t0x200310, 0x300311, 0x700b12,\n\t\t0x100, 0x108, 0x107, 0x109, 0x106, 0x10a, 0x105, 0x10b,\n\t\t0x104, 0x10c, 0x103, 0x10d, 0x102, 0x10e, 0x101, 0x10f };\nword32\tg_undeflate_lencode_tab[128 + 1];\nword32\t*g_undeflate_dynamic_tabptr = 0;\nword32\tg_undeflate_dynamic_bits = 0;\nword32\t*g_undeflate_dynamic_dist_tabptr = 0;\nword32\tg_undeflate_dynamic_dist_bits = 0;\n\nvoid *\nundeflate_realloc(void *ptr, dword64 dsize)\n{\n\tif((size_t)dsize != dsize) {\n\t\treturn 0;\n\t}\n\treturn realloc(ptr, (size_t)dsize);\n}\n\nvoid *\nundeflate_malloc(dword64 dsize)\n{\n\tif((size_t)dsize != dsize) {\n\t\treturn 0;\n\t}\n\treturn malloc((size_t)dsize);\n}\n\nvoid\nshow_bits(unsigned *llptr, int nl)\n{\n\tint\ti;\n\n\tfprintf(stdout, \"Showing %03x bits entries\\n\", nl);\n\tfor(i = 0; i < nl; i++) {\n\t\tfprintf(stdout, \"%03x: %03x\\n\", i, (llptr[i] >> 16) & 0xf);\n\t}\n}\n\nvoid\nshow_huftb(unsigned *tabptr, int bits)\n{\n\tunsigned char seen[512];\n\tword32\tentry, code, val;\n\tint\ti, j;\n\n\tprintf(\"Showing hufftab of %d bits\\n\", bits);\n\n\tfor(i = 0; i < 256+32; i++) {\n\t\tseen[i] = 0;\n\t}\n\tfor(i = 0; i < (1 << bits); i++) {\n\t\tentry = tabptr[i];\n\t\tcode = entry & 0xff;\n\t\tif((entry >> 24) & 1) {\n\t\t\tval = entry & 0xfff0ffffUL;\n\t\t\tfor(j = 0; j < 32; j++) {\n\t\t\t\tif(val == g_undeflate_length_tab[j]) {\n\t\t\t\t\tcode = 256 + j;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(code < 256) {\n\t\t\t\tprintf(\"entry %08x (%08x) not found, [0]=%08x \"\n\t\t\t\t\t\"[1]=%08x\\n\", entry, val,\n\t\t\t\t\tg_undeflate_length_tab[0],\n\t\t\t\t\tg_undeflate_length_tab[1]);\n\t\t\t\tcode = 256 + 31;\n\t\t\t}\n\t\t}\n\t\tif(!seen[code]) {\n\t\t\tprintf(\"code %03x has bits:%d huffcode:%04x\\n\", code,\n\t\t\t\t(entry >> 16) & 0xf, i);\n\t\t\tseen[code] = 1;\n\t\t}\n\t}\n}\n\nvoid\nundeflate_init_len_dist_tab(word32 *tabptr, dword64 drepeats, word32 start)\n{\n\tword32\tpos, repeats, extra_bits;\n\tint\ti;\n\n\t// Initializes g_undeflate_length_tab[] and g_underflate_dist_tab[]\n\t// printf(\"undeflate len_dist_tab repeats:%016llx\\n\", drepeats);\n\tpos = 0;\n\textra_bits = 0;\n\twhile(pos < 30) {\n\t\trepeats = drepeats & 0xf;\n\t\tdrepeats = drepeats >> 4;\n\t\tif(repeats == 0xf) {\n\t\t\t// Special handling for code=285 (pos=29)\n\t\t\textra_bits = 0;\n\t\t\tstart--;\t\t// 258, not 259\n\t\t\trepeats = 1;\n\t\t}\n\t\tfor(i = 0; i < (int)repeats; i++) {\n\t\t\ttabptr[pos] = start | (extra_bits << 20) | (1 << 24);\n\t\t\t//printf(\"Set table[%d]=%08x (%d) i:%d out of %d\\n\",\n\t\t\t//\tpos, tabptr[pos], start, i, repeats);\n\t\t\tpos++;\n\t\t\tstart += (1 << extra_bits);\n\t\t}\n\t\textra_bits++;\n\t}\n}\n\nvoid\nundeflate_init_bit_rev_tab(word32 *tabptr, int num)\n{\n\tword32\tpos, val;\n\tint\ti, j;\n\n\t// Initializes g_undeflate_bit_rev[]\n\tfor(i = 0; i < num; i++) {\n\t\tpos = i;\n\t\tval = 0;\n\t\tfor(j = 0; j < 9; j++) {\n\t\t\tval = (val << 1) | (pos & 1);\n\t\t\tpos = pos >> 1;\n\t\t}\n\t\ttabptr[i] = val;\n\t\t// printf(\"Bit reverse[%03x]=%03x\\n\", i, val);\n\t}\n}\n\nword32\nundeflate_bit_reverse(word32 val, word32 bits)\n{\n\tword32\tnew_val, val2, shift;\n\n\tnew_val = g_undeflate_bit_rev[val & 0x1ff];\t// at most 9 bits\n\tshift = 9 - bits;\n\tif(bits <= 9) {\n\t\treturn new_val >> shift;\n\t} else if(bits <= 18) {\n\t\tshift += 9;\t\t// bits:10->shift=8, bits:11->shift=7,..\n\t\tval2 = g_undeflate_bit_rev[(val >> 9) & 0x1ff] >> shift;\n\t\tshift = bits - 9;\n\t\treturn (new_val << shift) | val2;\n\t}\n\tprintf(\"Cannot reverse %08x bits:%d!\\n\", val, bits);\n\treturn 0;\n}\n\nword32\nundeflate_calc_crc32(byte *bptr, word32 len)\n{\n\tword32\tcrc, c, xor;\n\tint\ti;\n\n\t// Old version, don't use other than for testing purposes.\n\t//  Use woz_calc_crc32() instead.\n\n\t// Generate CCITT-32 CRC, with remainder initialized to -1 and return\n\t//  the complement of the CRC value\n\t// This is slow--but it doesn't matter for KEGS, where the images\n\t//  are generally only 800KB max.\n\tcrc = (word32)-1;\n\twhile(len != 0) {\n\t\tc = *bptr++;\n\t\tlen--;\n\t\tfor(i = 0; i < 8; i++) {\n\t\t\txor = 0;\n\t\t\tif((crc ^ c) & 1) {\n\t\t\t\txor = 0xedb88320UL;\n\t\t\t}\n\t\t\tcrc = (crc >> 1) ^ xor;\n\t\t\tc = c >> 1;\n\t\t}\n\t}\n\treturn (~crc);\n}\n\nbyte *\nundeflate_ensure_dest_len(Disk *dsk, byte *ucptr, word32 len)\n{\n\tbyte\t*raw_ptr;\n\tdword64\traw_dsize, dimage_size;\n\tword32\tnew_image_size;\n\n\traw_dsize = dsk->raw_dsize;\n\tdimage_size = dsk->dimage_size;\n\tif(ucptr) {\n\t\tnew_image_size = (word32)(ucptr - dsk->raw_data);\n\t\tif(new_image_size < dimage_size) {\n\t\t\tprintf(\"ucptr moved backwards!\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\tif((new_image_size >> 31) != 0) {\n\t\t\tprintf(\"Output file > 2GB, failing\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\tdimage_size = new_image_size;\n\t\tdsk->dimage_size = dimage_size;\n\t}\n\tif(dimage_size > raw_dsize) {\n\t\tprintf(\"dimage_size %08llx overflowed raw_dsize %08llx\\n\",\n\t\t\tdimage_size, raw_dsize);\n\t\treturn 0;\n\t}\n\tif((dimage_size + len) > raw_dsize) {\n\t\traw_dsize = ((dimage_size + len) * 3ULL) / 2;\n\t\traw_ptr = undeflate_realloc(dsk->raw_data, raw_dsize);\n\t\t//printf(\"Did realloc to %08x, new new_data:%p, was %p\\n\",\n\t\t//\t\t\traw_size, raw_ptr, dsk->raw_data);\n\t\tif(raw_ptr == 0) {\n\t\t\tprintf(\"undeflate realloc failed\\n\");\n\t\t\tfree(dsk->raw_data);\n\t\t\tdsk->raw_data = 0;\n\t\t\treturn 0;\n\t\t}\n\t\tdsk->raw_data = raw_ptr;\n\t\tdsk->raw_dsize = raw_dsize;\n\t}\n#if 0\n\tprintf(\"undeflate_ensure_dest_len will ret %p, dsk->raw_data:%p, \"\n\t\t\"image_size:%08llx, raw_dsize:%08llx\\n\",\n\t\tdsk->raw_data + dimage_size, dsk->raw_data, dimage_size,\n\t\traw_dsize);\n#endif\n\treturn dsk->raw_data + dimage_size;\n}\n\nvoid\nundeflate_add_tab_code(word32 *tabptr, word32 tabsz_lg2, word32 code,\n\t\t\t\t\t\t\tword32 entry)\n{\n\tword32\trev_code, bits, pos, tab_size;\n\tint\tnum;\n\tint\ti;\n\n\tif(tabsz_lg2 > 15) {\n\t\tprintf(\"tabsz_lg2: %04x is not supported\\n\", tabsz_lg2);\n\t\treturn;\n\t}\n\ttab_size = 1 << tabsz_lg2;\n\trev_code = 0;\n\tbits = (entry >> 16) & 0xf;\n\trev_code = undeflate_bit_reverse(code, bits);\n\tif(rev_code >= tab_size) {\n\t\tprintf(\"rev_code:%04x out of range for entry %08x\\n\", rev_code,\n\t\t\t\t\t\t\t\t\tentry);\n\t\ttabptr[tab_size] = 1;\n\t\treturn;\n\t}\n\tnum = 1 << (tabsz_lg2 - bits);\n\tif(num < 0) {\n\t\tprintf(\"num %d out of range for entry %08x\\n\", num, entry);\n\t\ttabptr[tab_size] = 1;\n\t\treturn;\n\t}\n\tfor(i = 0; i < num; i++) {\n\t\tpos = rev_code | (i << bits);\n\t\tif(tabptr[pos] != 0) {\n\t\t\tprintf(\"Overwriting old [%04x]=%08x with %08x\\n\", pos,\n\t\t\t\t\t\ttabptr[pos], entry);\n\t\t\ttabptr[tab_size] = 1;\n\t\t}\n#if 0\n\t\tif(i >= 0) {\n\t\t\tprintf(\"Set code tab[%04x]=%08x (code:%04x)\\n\", pos,\n\t\t\t\t\t\t\tentry, save_code);\n\t\t}\n#endif\n\t\ttabptr[pos] = entry;\n\t}\n}\n\nword32 *\nundeflate_init_fixed_tabs()\n{\n\tword32\t*tabptr;\n\tint\ti;\n\n\ttabptr = &(g_undeflate_fixed_len_tab[0]);\n\t// Init g_undeflate_fixed_len_tab[] for the fixed Huffman code\n\tfor(i = 0; i < 513; i++) {\n\t\ttabptr[i] = 0;\n\t}\n\t// printf(\"Add fixed_len_tab for literals 0 - 143\\n\");\n\tfor(i = 0; i < 144; i++) {\n\t\tundeflate_add_tab_code(tabptr, 9, 0x30 + i, (8 << 16) | i);\n\t}\n\t// printf(\"Add fixed_len_tab for literals 144 - 255\\n\");\n\tfor(i = 144; i < 256; i++) {\n\t\tundeflate_add_tab_code(tabptr, 9, 0x190 + i - 144,\n\t\t\t\t\t\t\t\t(9 << 16) | i);\n\t}\n\t// printf(\"Add fixed_len_tab for length codes 256 - 279\\n\");\n\tfor(i = 256; i < 280; i++) {\n\t\t// printf(\"code: %03x fixed_len_tab[%03x]=%08x\\n\", i, i - 256,\n\t\t//\t\t\tg_undeflate_length_tab[i - 256]);\n\t\tundeflate_add_tab_code(tabptr, 9, 0 + i - 256,\n\t\t\t\t(7 << 16) | g_undeflate_length_tab[i - 256]);\n\t}\n\t// printf(\"Add fixed_len_tab for length codes 280 - 287\\n\");\n\tfor(i = 280; i < 288; i++) {\n\t\tundeflate_add_tab_code(tabptr, 9, 0xc0 + i - 280,\n\t\t\t\t(8 << 16) | g_undeflate_length_tab[i - 256]);\n\t}\n\tif(tabptr[512]) {\n\t\treturn 0;\n\t}\n\n\t// And init g_undeflate_fixed_dist_tab[]\n\ttabptr = &(g_undeflate_fixed_dist_tab[0]);\n\tfor(i = 0; i < 33; i++) {\n\t\ttabptr[i] = 0;\n\t}\n\t// printf(\"Add fixed_dist_tab for codes 0 - 29\\n\");\n\tfor(i = 0; i < 30; i++) {\n\t\tundeflate_add_tab_code(tabptr, 5, i,\n\t\t\t\t\t(5 << 16) | g_undeflate_dist_tab[i]);\n\t}\n\n\tif(tabptr[32]) {\n\t\treturn 0;\n\t}\n\treturn tabptr;\n}\n\nword32 *\nundeflate_init_tables()\n{\n\tundeflate_init_len_dist_tab(&(g_undeflate_length_tab[0]),\n\t\t\t\t\t\t\tLENGTH_ENCODED, 2);\n\t\t// code=257 has length 3, but the first entry is really code=256\n\t\t//  so set 256 to length=2\n\tundeflate_init_len_dist_tab(&(g_undeflate_dist_tab[0]), DIST_ENCODED,\n\t\t\t\t\t\t\t\t\t1);\n\tundeflate_init_bit_rev_tab(&(g_undeflate_bit_rev[0]), 512);\n\t// undeflate_check_bit_reverse();\n\treturn undeflate_init_fixed_tabs();\n}\n\nvoid\nundeflate_free_tables()\n{\n\tfree(g_undeflate_dynamic_tabptr);\n\tg_undeflate_dynamic_tabptr = 0;\n\tfree(g_undeflate_dynamic_dist_tabptr);\n\tg_undeflate_dynamic_dist_tabptr = 0;\n}\n\nvoid\nundeflate_check_bit_reverse()\n{\n\tword32\trev, tmp, checked;\n\tint\ti, j, bits;\n\n\t// Check bit-reverse function.  Reverse all values from 0-32767\n\tchecked = 0;\n\tfor(bits = 1; bits <= 16; bits++) {\n\t\t// printf(\"Checking bit reverse bits=%d\\n\", bits);\n\t\tfor(i = 0; i < 65536; i++) {\n\t\t\tif(i >= (1 << bits)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttmp = i;\n\t\t\trev = 0;\n\t\t\tfor(j = 0; j < bits; j++) {\n\t\t\t\trev = (rev << 1) | (tmp & 1);\n\t\t\t\ttmp = tmp >> 1;\n\t\t\t}\n\t\t\ttmp = undeflate_bit_reverse(i, bits);\n\t\t\tif(tmp != rev) {\n\t\t\t\tprintf(\"Reverse %04x bits:%d ret:%04x, \"\n\t\t\t\t\t\"exp:%04x\\n\", i, bits, tmp, rev);\n\t\t\t\texit(2);\n\t\t\t}\n\t\t\tchecked++;\n\t\t}\n\t}\n\tprintf(\"Checked %08x values\\n\", checked);\n}\n\nword32 *\nundeflate_build_huff_tab(word32 *tabptr, word32 *entry_ptr, word32 len_size,\n\t\t\t\t\tword32 *bl_count_ptr, int max_bits)\n{\n\tword32\tnext_code[16];\n\tword32\tcode, tab_size, bits, entry;\n\tint\ti;\n\n\ttab_size = (1 << max_bits);\n\tif(max_bits > 15) {\n\t\tprintf(\"max_bits: %d out of range\\n\", max_bits);\n\t\treturn 0;\n\t}\n\tnext_code[0] = 0;\n\tbl_count_ptr[0] = 0;\t\t// Force number of 0-bit lengths to 0\n\tcode = 0;\n\t// printf(\"build_huff_tab, max_bits:%d, tab_size:%08x\\n\", max_bits,\n\t//\t\t\t\t\t\t\ttab_size);\n\tfor(i = 1; i <= max_bits; i++) {\n\t\t// printf(\"bl_count[%d] = %03x\\n\", i - 1, bl_count_ptr[i-1]);\n\t\tcode = (code + bl_count_ptr[i - 1]) << 1;\n\t\tnext_code[i] = code;\n\t\t// printf(\"Set next_code[%d] = %03x\\n\", i, code);\n\t}\n\tfor(i = 0; i < (int)tab_size; i++) {\n\t\ttabptr[i] = 0;\n\t}\n\ttabptr[tab_size] = 0;\n\n\tfor(i = 0; i < (int)len_size; i++) {\n\t\tentry = entry_ptr[i];\n\t\tbits = (entry >> 16) & 0xf;\n\t\t//printf(\"i:%03x, bits:%d, entry:%08x\\n\", i, bits, entry);\n\t\tif(!bits) {\n\t\t\tcontinue;\n\t\t}\n\t\tcode = next_code[bits]++;\n\t\t//printf(\"Set tab code:%03x = %08x\\n\", code, entry);\n\t\tundeflate_add_tab_code(tabptr, max_bits, code, entry);\n\t}\n\n\t// printf(\"All done, returning tabptr\\n\");\n\treturn tabptr;\n}\n\nword32 *\nundeflate_dynamic_table(byte *cptr, word32 *bit_pos_ptr, byte *cptr_base)\n{\n\tword32\tcode_list[256+32+32+1];\n\tword32\tlen_codes[19];\n\tword32\tbl_count[19], bl_count_dist[16];\n\tword32\t*tabptr, *tabptr_dist;\n\tbyte\t*cptr_start;\n\tword32\tbit_pos, val, hlit, hdist, hclen, pos, max_bits, code_pos;\n\tword32\ttotal_codes_needed, repeat, mask, entry, bits;\n\tword32\tmax_length_bits, max_distance_bits, extra_bits;\n\tint\ti;\n\n\t// This is compressed compressed huffman lengths.  First\n\t//  get the length codes, then get the actual lengths\n\t// Get 14 bits, which always fits in 3 bytes\n\tbit_pos = *bit_pos_ptr;\n\tcptr_start = cptr;\n\tval = (cptr[0] + (cptr[1] << 8) + (cptr[2] << 16)) >> bit_pos;\n\thlit = (val & 0x1f) + 257;\t\t// 257 - 288\n\thdist = ((val >> 5) & 0x1f) + 1;\n\thclen = (val >> 10) & 0xf;\n#if 0\n\tprintf(\"At +%06x, bit:%d, hlit:%02x hdist:%02x, hclen:%02x\\n\",\n\t\t(word32)(cptr - cptr_base), bit_pos, hlit, hdist, hclen);\n#endif\n\tif(cptr_base) {\n\t\t// Avoid unused parameter warning\n\t}\n\tbit_pos += 14;\n\tcptr += (bit_pos >> 3);\n\tbit_pos = bit_pos & 7;\n\tfor(i = 0; i < 19; i++) {\n\t\tlen_codes[i] = 0;\n\t\tbl_count[i] = 0;\n\t}\n\thclen += 4;\t\t\t// 19*3 = 57 bits, at most\n\tmax_bits = 0;\n\tfor(i = 0; i < (int)hclen; i++) {\n\t\tval = ((cptr[0] + (cptr[1] << 8)) >> bit_pos) & 7;\n\t\tentry = g_undeflate_lencode_positions[i];\n\t\tentry = entry & (~0xf0000);\t\t// clear bits from entry\n\t\tpos = entry & 0x1f;\n\t\tlen_codes[pos] = entry | (val << 16);\n\t\t// printf(\"len_codes[%d]=%08x\\n\", pos, len_codes[pos]);\n\t\tbl_count[val]++;\n\t\tif(val > max_bits) {\n\t\t\tmax_bits = val;\n\t\t}\n\t\t// printf(\"Num bits for len code %02x = %d\\n\", pos, val);\n\t\tbit_pos += 3;\n\t\tcptr += (bit_pos >> 3);\n\t\tbit_pos = bit_pos & 7;\n\t}\n\t// Build huffman table\n\ttabptr = undeflate_build_huff_tab(&(g_undeflate_lencode_tab[0]),\n\t\t\t&(len_codes[0]), 19, &(bl_count[0]), max_bits);\n\tif(tabptr == 0) {\n\t\tprintf(\"Bad table\\n\");\n\t\treturn 0;\n\t}\n\n\t// Now we've made the table in tabptr.  Read the length codes now\n\ttotal_codes_needed = hlit + hdist;\n\t// printf(\"Getting %04x total codes\\n\", total_codes_needed);\n\tcode_pos = 0;\n\tmask = (1 << max_bits) - 1;\n\tif(total_codes_needed > (256+32+32)) {\n\t\tprintf(\"total_codes_needed high: %04x\\n\", total_codes_needed);\n\t\treturn 0;\n\t}\n\tfor(i = 0; i < 16; i++) {\n\t\tbl_count[i] = 0;\n\t\tbl_count_dist[i] = 0;\n\t}\n\twhile(code_pos < total_codes_needed) {\n\t\tpos = (cptr[0] | (cptr[1] << 8)) >> bit_pos;\n\t\tpos = pos & mask;\n\t\tentry = tabptr[pos & mask];\n#if 0\n\t\tprintf(\"At +%06x, bit:%d: Raw code: %02x, entry:%08x\\n\",\n\t\t\t(word32)(cptr - cptr_base), bit_pos, pos, entry);\n#endif\n\t\tval = entry & 0x1f;\n\n\t\tbits = (entry >> 16) & 7;\n\t\textra_bits = (entry >> 20) & 7;\n\t\trepeat = (entry >> 8) & 0xf;\n\t\tentry = (val << 16);\t\t\t// Set bits\n\t\tbit_pos += bits;\n\t\tcptr += (bit_pos >> 3);\n\t\tbit_pos = bit_pos & 7;\n\t\tpos = (cptr[0] | (cptr[1] << 8)) >> bit_pos;\n#if 0\n\t\tprintf(\"At +%06x, bit:%d: Raw pos:%04x\\n\",\n\t\t\t(word32)(cptr - cptr_base), bit_pos, pos);\n#endif\n\t\tpos = pos & ((1 << extra_bits) - 1);\n\t\trepeat = repeat + pos;\n\t\tbit_pos += extra_bits;\n\t\tcptr += (bit_pos >> 3);\n\t\tbit_pos = bit_pos & 7;\n\t\tif(!repeat) {\n\t\t\tprintf(\"Bad repeat value\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\tif(val >= 0x10) {\n\t\t\tentry = 0;\n\t\t\tif(val == 0x10) {\t\t// Repeat prev entry\n\t\t\t\tentry = code_list[code_pos - 1];\n\t\t\t\tif(!code_pos) {\n\t\t\t\t\tprintf(\"Got repeat code 0x10 at 0!\\n\");\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor(i = 0; i < (int)repeat; i++) {\n\t\t\tcode_list[code_pos] = entry;\n\t\t\t// printf(\"Added code_list[%03x] = %08x\\n\", code_pos,\n\t\t\t//\t\t\t\t\t\tentry);\n\t\t\tcode_pos++;\n\t\t}\n\t}\n\n\t// Fix lengths and literals\n\tmax_length_bits = 0;\n\tfor(i = 0; i < (int)hlit; i++) {\n\t\tentry = code_list[i];\n\t\tbits = (entry >> 16) & 0xf;\n\t\tbl_count[bits]++;\n\t\tif(i >= 256) {\n\t\t\tentry |= g_undeflate_length_tab[i - 256];\n\t\t} else {\n\t\t\tentry |= i;\n\t\t}\n\t\tcode_list[i] = entry;\n\t\tif(bits > max_length_bits) {\n\t\t\tmax_length_bits = bits;\n\t\t}\n\t}\n\n\t// Fix distances\n\tmax_distance_bits = 0;\n\tfor(i = 0; i < (int)hdist; i++) {\n\t\tentry = code_list[i + hlit];\n\t\tbits = (entry >> 16) & 0xf;\n\t\tbl_count_dist[bits]++;\n\t\tentry |= g_undeflate_dist_tab[i];\n\t\tcode_list[i + hlit] = entry;\n\t\tif(bits > max_distance_bits) {\n\t\t\tmax_distance_bits = bits;\n\t\t}\n\t}\n\tif(code_pos != total_codes_needed) {\n\t\tprintf(\"Got %03x codes, needed %03x codes\\n\", code_pos,\n\t\t\t\t\t\t\ttotal_codes_needed);\n\t\treturn 0;\n\t}\n\t// printf(\"max_length_bits: %d, max_distance_bits: %d\\n\",\n\t//\t\t\t\tmax_length_bits, max_distance_bits);\n\ttabptr = g_undeflate_dynamic_tabptr;\n\tif(!tabptr) {\n\t\ttabptr = malloc(sizeof(word32)*((1 << 15) + 1));\n\t\tg_undeflate_dynamic_tabptr = tabptr;\n\t\t// printf(\"malloc literal table\\n\");\n\t}\n\tg_undeflate_dynamic_bits = max_length_bits;\n\t//printf(\"Building literal/length table, %d entries, %d bits\\n\", hlit,\n\t//\t\t\t\t\t\tmax_length_bits);\n\t//show_bits(&(code_list[0]), hlit);\n\n\ttabptr = undeflate_build_huff_tab(tabptr, &(code_list[0]),\n\t\t\thlit, &(bl_count[0]), max_length_bits);\n\tif(tabptr == 0) {\n\t\tprintf(\"Building literal table failed\\n\");\n\t\treturn 0;\n\t}\n\t//show_huftb(tabptr, max_length_bits);\n\n\ttabptr_dist = g_undeflate_dynamic_dist_tabptr;\n\tif(!tabptr_dist) {\n\t\ttabptr_dist = malloc(sizeof(word32) * ((1 << 15) + 1));\n\t\tg_undeflate_dynamic_dist_tabptr = tabptr_dist;\n\t\t// printf(\"malloc dist table\\n\");\n\t}\n\tg_undeflate_dynamic_dist_bits = max_distance_bits;\n\ttabptr_dist = undeflate_build_huff_tab(tabptr_dist, &(code_list[hlit]),\n\t\t\thdist, &(bl_count_dist[0]), max_distance_bits);\n\tif(tabptr_dist == 0) {\n\t\tprintf(\"Building dist table failed\\n\");\n\t\treturn 0;\n\t}\n\n\t// Update *bit_pos_ptr to skip over the table\n\t*bit_pos_ptr = bit_pos + (int)(8*(cptr - cptr_start));\n\treturn tabptr;\n}\n\nbyte *\nundeflate_block(Disk *dsk, byte *cptr, word32 *bit_pos_ptr, byte *cptr_base,\n\t\t\t\t\t\t\tbyte *cptr_end)\n{\n\tword32\t*lit_tabptr, *dist_tabptr;\n\tbyte\t*ucptr, *ucptr_end;\n\tword32\tbfinal, btype, bit_pos, len, pos, extra_bits, entry, dist_entry;\n\tword32\tbits, is_len, dist, lit_mask, dist_mask, tmp;\n\tint\ti;\n\n\tbit_pos = *bit_pos_ptr;\n\n\t// printf(\"At file offset %08x,bit %d cptr[0]:%02x %02x\\n\",\n\t//\t\t(word32)(cptr - cptr_base), bit_pos, cptr[0], cptr[1]);\n\tbfinal = (cptr[0] >> bit_pos) & 1;\n\tbit_pos++;\n\tbtype = (((cptr[1] << 8) | cptr[0]) >> bit_pos) & 3;\n\tbit_pos += 2;\n\tcptr += (bit_pos >> 3);\n\tbit_pos = bit_pos & 7;\n\t// printf(\"bfinal:%d, btype:%d\\n\", bfinal, btype);\n\n\tif(bfinal) {\n\t\tdsk->fd = 0;\t\t\t// Last block\n\t}\n\tif(btype == 3) {\t\t\t// Reserved: error\n\t\treturn 0;\n\t} else if(btype == 0) {\t\t\t// uncompressed\n\t\t// Align cptr to next byte\n\t\tbit_pos += 7;\n\t\tcptr += (bit_pos >> 3);\n\t\t*bit_pos_ptr = 0;\n\t\tlen = cptr[0] + (cptr[1] << 8);\n\t\tucptr = undeflate_ensure_dest_len(dsk, 0, len);\n\t\tif(!ucptr) {\n\t\t\treturn 0;\n\t\t}\n\t\tcptr += 4;\n\t\tfor(i = 0; i < (int)len; i++) {\n\t\t\t*ucptr++ = *cptr++;\n\t\t}\n\t\tdsk->dimage_size += len;\n\t\treturn cptr;\n\t}\n\n\tif(btype == 1) {\t\t\t// Fixed Huffman codes\n\t\tlit_tabptr = &(g_undeflate_fixed_len_tab[0]);\n\t\tdist_tabptr = &(g_undeflate_fixed_dist_tab[0]);\n\t\tlit_mask = 0x1ff;\n\t\tdist_mask = 0x1f;\n\t} else {\t\t\t\t// Dynamic Huffman codes\n\t\t*bit_pos_ptr = bit_pos;\n\t\tlit_tabptr = undeflate_dynamic_table(cptr, bit_pos_ptr,\n\t\t\t\t\t\t\t\tcptr_base);\n\t\tdist_tabptr = g_undeflate_dynamic_dist_tabptr;\n\t\t// printf(\"dynamic table used %d bits\\n\",\n\t\t//\t\t\t\t*bit_pos_ptr - bit_pos);\n\t\tlit_mask = (1 << g_undeflate_dynamic_bits) - 1;\n\t\tdist_mask = (1 << g_undeflate_dynamic_dist_bits) - 1;\n\t\tbit_pos = *bit_pos_ptr;\n\t\tcptr += (bit_pos >> 3);\n\t\tbit_pos = bit_pos & 7;\n\t}\n\tif(!lit_tabptr || !dist_tabptr) {\n\t\tprintf(\"Code table failure\\n\");\n\t\treturn 0;\n\t}\n\n\tucptr = undeflate_ensure_dest_len(dsk, 0, 65536);\t// Just a guess\n\tif(!ucptr) {\n\t\treturn 0;\n\t}\n\tucptr_end = dsk->raw_data + dsk->raw_dsize - 500;\n\n\twhile(cptr < cptr_end) {\n#if 0\n\t\tprintf(\"Top of loop, cptr:%p, lit_tabptr:%p, dsk->raw:%p\\n\",\n\t\t\t\tcptr, lit_tabptr, dsk->raw_data);\n#endif\n\n\t\tif(ucptr > ucptr_end) {\n\t\t\tucptr = undeflate_ensure_dest_len(dsk, ucptr, 65536);\n\t\t\tucptr_end = dsk->raw_data + dsk->raw_dsize - 500;\n\t\t\t// printf(\"Update ucptr to %p\\n\", ucptr);\n\t\t\tif(!ucptr) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t\tpos = cptr[0] | (cptr[1] << 8) | (cptr[2] << 16);\n\t\tpos = pos >> bit_pos;\n\t\tentry = lit_tabptr[pos & lit_mask];\n\t\tbits = (entry >> 16) & 0xf;\n\t\tis_len = (entry >> 24) & 1;\n\t\tlen = entry & 0xffff;\n#if 0\n\t\tprintf(\"At offset +%08x bit:%d, huffcode=%04x, is %d bits, \"\n\t\t\t\"entry=%08x\\n\", (int)(cptr - cptr_base), bit_pos,\n\t\t\tpos & lit_mask, bits, entry);\n#endif\n\t\tif(bits == 0) {\n\t\t\tprintf(\"bits=0, %08x bad table\\n\", lit_mask);\n\t\t\treturn 0;\n\t\t}\n\t\tbit_pos += bits;\n\t\tcptr += (bit_pos >> 3);\n\t\tbit_pos = bit_pos & 7;\n\t\tif(!is_len) {\t\t\t// Literal\n\t\t\t// literal byte\n\t\t\t// printf(\" Out +%06x: %02x\\n\",\n\t\t\t//\t(int)(ucptr - dsk->raw_data), len & 0xff);\n\t\t\t//putc(len, g_outf);\n\t\t\t*ucptr++ = len;\n\t\t} else {\n\t\t\tif(len == 2) {\t\t\t// Code=0x100, end block\n\t\t\t\t// All done\n\t\t\t\t// printf(\"Got the 0x100 code!  All done!\\n\");\n\t\t\t\t*bit_pos_ptr = bit_pos;\n\t\t\t\tdsk->dimage_size = ucptr - dsk->raw_data;\n\t\t\t\t// printf(\"Set dsk->image_size = %08x\\n\",\n\t\t\t\t//\t\t\tdsk->image_size);\n\t\t\t\treturn cptr;\n\t\t\t}\n\t\t\textra_bits = (entry >> 20) & 7;\n\t\t\tif(extra_bits) {\n\t\t\t\tpos = cptr[0] | (cptr[1] << 8);\n\t\t\t\tpos = pos >> bit_pos;\n\t\t\t\tpos = pos & ((1 << extra_bits) - 1);\n\t\t\t\tlen += pos;\n\t\t\t}\n#if 0\n\t\t\tprintf(\"At offset +%08x, bit:%d got extra_bits=%d, \"\n\t\t\t\t\"len=%08x\\n\", (int)(cptr - cptr_base), bit_pos,\n\t\t\t\t\t\t\textra_bits, len);\n#endif\n\t\t\tbit_pos += extra_bits;\n\t\t\tcptr += (bit_pos >> 3);\n\t\t\tbit_pos = bit_pos & 7;\n\n\t\t\t// Get distance code\n\t\t\tpos = cptr[0] | (cptr[1] << 8) | (cptr[2] << 16);\n\t\t\tpos = pos >> bit_pos;\n#if 0\n\t\t\tprintf(\"At offset +%08x, bit:%d raw distance code: \"\n\t\t\t\t\"%02x\\n\", (int)(cptr - cptr_base), bit_pos,\n\t\t\t\tpos & dist_mask);\n#endif\n\t\t\tdist_entry = dist_tabptr[pos & dist_mask];\n\t\t\tbits = (dist_entry >> 16) & 0xf;\n\t\t\tif(bits == 0) {\n\t\t\t\tprintf(\"bits=0 for dist_entry:%08x %08x\\n\",\n\t\t\t\t\tdist_entry, pos);\n\t\t\t}\n\t\t\textra_bits = (dist_entry >> 20) & 0xf;\n\t\t\tdist = dist_entry & 0xffff;\n\t\t\t//printf(\"dist_entry:%08x, extra_bits:%d, dist:%05x\\n\",\n\t\t\t//\tdist_entry, extra_bits, dist);\n\t\t\tbit_pos += bits;\n\t\t\tcptr += (bit_pos >> 3);\n\t\t\tbit_pos = bit_pos & 7;\n\t\t\tif(extra_bits) {\n\t\t\t\tpos = (cptr[0] | (cptr[1] << 8) |\n\t\t\t\t\t\t(cptr[2] << 16)) >> bit_pos;\n#if 0\n\t\t\t\tprintf(\" At offset +%08x, bit:%d, raw ex:\"\n\t\t\t\t\t\"%08x\\n\", (int)(cptr - cptr_base),\n\t\t\t\t\tbit_pos, pos);\n#endif\n\t\t\t\ttmp = pos & ((1 << extra_bits) - 1);\n\t\t\t\tdist += tmp;\n#if 0\n\t\t\t\tprintf(\"at offset +%08x, got %d extra dist \"\n\t\t\t\t\t\"for total dist=%d (%05x)\\n\",\n\t\t\t\t\t(int)(cptr - cptr_base), extra_bits,\n\t\t\t\t\tdist, pos);\n#endif\n\t\t\t\tbit_pos += extra_bits;\n\t\t\t\tcptr += (bit_pos >> 3);\n\t\t\t\tbit_pos = bit_pos & 7;\n\t\t\t}\n\t\t\t//printf(\"Repeating %d bytes from dist:%05x\\n\", len,\n\t\t\t//\t\t\t\t\t\tdist);\n\t\t\tif(ucptr < (dsk->raw_data + dist)) {\n\t\t\t\tprintf(\"Dist out of bounds:%04x %p %p\\n\",\n\t\t\t\t\t\tdist, ucptr, dsk->raw_data);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tfor(i = 0; i < (int)len; i++) {\n\t\t\t\tucptr[0] = ucptr[0-(int)dist];\n#if 0\n\t\t\t\tputc(ucptr[0], g_outf);\n\t\t\t\tprintf(\" Out +%06x: %02x\\n\",\n\t\t\t\t\t(int)(ucptr - dsk->raw_data),\n\t\t\t\t\tucptr[0]);\n#endif\n\t\t\t\tucptr++;\n\t\t\t}\n\t\t}\n\t}\n\n\tprintf(\"Ran out of compressed data, bad gzip file\\n\");\n\n\treturn 0;\n}\n\nbyte *\nundeflate_gzip_header(Disk *dsk, byte *cptr, word32 compr_size)\n{\n\tword32\t*wptr;\n\tbyte\t*cptr_base, *cptr_end;\n\tword32\tflg, xfl, xlen, bit_offset, exp_crc, len, crc;\n\n\tcptr_base = cptr;\n\tcptr_end = cptr + compr_size;\n\n\tif((cptr[0] != 0x1f) || (cptr[1] != 0x8b) || (cptr[2] != 0x08)) {\n\t\tprintf(\"Not gzip file, exiting\\n\");\n\t\treturn 0;\n\t}\n\n\tflg = cptr[3];\n\txfl = cptr[8];\n\tprintf(\"flg:%02x and xflags:%02x\\n\", flg, xfl);\n\tcptr += 10;\n\n\tif(flg & 4) {\t\t// FEXTRA set\n\t\txlen = cptr[0] + (cptr[1] * 256);\n\t\tprintf(\"FEXTRA XLEN is %d, skipping that many bytes\\n\", xlen);\n\t\tcptr += 2 + xlen;\n\t}\n\n\tif(flg & 8) {\t\t// FNAME set\n\t\tcptr += strlen((char *)cptr) + 1;\n\t}\n\tif(flg & 0x10) {\t// FCOMMENT set\n\t\tcptr += strlen((char *)cptr) + 1;\n\t}\n\tif(flg & 2) {\t\t// FHCRC set\n\t\tcptr += 2;\n\t}\n\tprintf(\"gzip header was %02x bytes long\\n\", (int)(cptr - cptr_base));\n\n\tdsk->raw_dsize = 140*1024;\t\t// Just a guess, alloc size\n\tdsk->raw_data = undeflate_malloc(dsk->raw_dsize);\n\tif(dsk->raw_data == 0) {\n\t\treturn 0;\n\t}\n\tprintf(\"Initial malloc (not realloc) set raw_data=%p\\n\", dsk->raw_data);\n\n\tdsk->dimage_size = 0;\t\t\t// Used size\n\n\twptr = undeflate_init_tables();\n\tif(wptr == 0) {\n\t\treturn 0;\t\t\t// Some sort of error, get out\n\t}\n\n\tbit_offset = 0;\n\twhile(cptr < cptr_end) {\n\t\tcptr = undeflate_block(dsk, cptr, &bit_offset, cptr_base,\n\t\t\t\t\t\t\t\tcptr_end);\n\t\tif(cptr == 0) {\n\t\t\t// Failed\n\t\t\tbreak;\n\t\t}\n\t\tif(dsk->fd == 0) {\n\t\t\tprintf(\"undeflate_block set fd=0, success\\n\");\n\t\t\t// Done, success!\n\t\t\t// Check crc\n\t\t\tif(bit_offset) {\n\t\t\t\tcptr++;\n\t\t\t}\n\t\t\tif((cptr + 8) > cptr_end) {\n\t\t\t\tprintf(\"No CRC or LEN fields at end\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\texp_crc = cptr[0] | (cptr[1] << 8) | (cptr[2] << 16) |\n\t\t\t\t\t\t(cptr[3] << 24);\n\t\t\tlen = cptr[4] | (cptr[5] << 8) | (cptr[6] << 16) |\n\t\t\t\t\t\t(cptr[7] << 24);\n\t\t\tif(len != dsk->dimage_size) {\n\t\t\t\tprintf(\"Len mismatch: exp %08x != %08llx\\n\",\n\t\t\t\t\t\t\tlen, dsk->dimage_size);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcrc = woz_calc_crc32(dsk->raw_data, len, 0);\n\t\t\tif(crc != exp_crc) {\n\t\t\t\tprintf(\"CRC mismatch: %08x != exp %08x\\n\",\n\t\t\t\t\t\tcrc, exp_crc);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// Real success, set raw_dsize\n\t\t\tdsk->raw_data = undeflate_realloc(dsk->raw_data,\n\t\t\t\t\t\t\tdsk->dimage_size);\n\t\t\tdsk->raw_dsize = dsk->dimage_size;\n\t\t\treturn cptr;\n\t\t}\n\t}\n\n\tprintf(\"Failed\\n\");\n\t// Disk image thread not found, get out\n\tfree(dsk->raw_data);\n\tdsk->fd = -1;\n\tdsk->dimage_size = 0;\n\tdsk->raw_data = 0;\n\tdsk->raw_dsize = 0;\n\treturn 0;\n}\n\nvoid\nundeflate_gzip(Disk *dsk, const char *name_str)\n{\n\tbyte\t*cptr;\n\tdword64\tcompr_dsize, dret;\n\tword32\tcompr_size;\n\tint\tfd;\n\tint\ti;\n\n\t// On success, set dsk->fd=0 and dsk->raw_data,raw_dsize properly.\n\tprintf(\"undeflate_gzip on file %s\\n\", name_str);\n\tfd = open(name_str, O_RDONLY | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\treturn;\n\t}\n\tcompr_dsize = cfg_get_fd_size(fd);\n\tprintf(\"size: %lld\\n\", compr_dsize);\n\tif((compr_dsize >> 31) != 0) {\n\t\t// > 2GB...too big for this code\n\t\tprintf(\"gzip file is too large\\n\");\n\t\tdsk->fd = -1;\n\t\treturn;\n\t}\n\tcompr_size = (word32)compr_dsize;\n\n\tcptr = malloc(compr_size + 0x1000);\n\tfor(i = 0; i < 0x1000; i++) {\n\t\tcptr[compr_size + i] = 0;\n\t}\n\tdret = cfg_read_from_fd(fd, cptr, 0, compr_size);\n\tif(dret != compr_size) {\n\t\tcompr_size = 0;\t\t// Make header searching fail\n\t}\n\t//g_outf = fopen(\"out.dbg\", \"w\");\n\tundeflate_gzip_header(dsk, cptr, compr_size);\n\n\tfree(cptr);\n\tundeflate_free_tables();\n}\n\nbyte *\nundeflate_zipfile_blocks(Disk *dsk, byte *cptr, dword64 dcompr_size)\n{\n\tword32\t*wptr;\n\tbyte\t*cptr_base, *cptr_end;\n\tword32\tbit_offset;\n\n\tcptr_base = cptr;\n\tcptr_end = cptr + dcompr_size;\n\n\tdsk->raw_data = undeflate_malloc(dsk->raw_dsize);\n\tif(dsk->raw_data == 0) {\n\t\treturn 0;\n\t}\n\tprintf(\"Initial malloc (not realloc) set raw_data=%p\\n\", dsk->raw_data);\n\n\tdsk->dimage_size = 0;\t\t\t// Used size\n\n\twptr = undeflate_init_tables();\n\tif(wptr == 0) {\n\t\treturn 0;\t\t\t// Some sort of error, get out\n\t}\n\n\tbit_offset = 0;\n\twhile(cptr < cptr_end) {\n\t\tcptr = undeflate_block(dsk, cptr, &bit_offset, cptr_base,\n\t\t\t\t\t\t\t\tcptr_end);\n\t\tif(cptr == 0) {\n\t\t\t// Failed\n\t\t\tbreak;\n\t\t}\n\t\tif(dsk->fd == 0) {\n\t\t\tprintf(\"undeflate_block set fd=0, success\\n\");\n\t\t\t// Done, success!\n\t\t\t// Check crc\n\t\t\tif(bit_offset) {\n\t\t\t\tcptr++;\n\t\t\t}\n\t\t\t// Real success, set raw_dsize\n\t\t\tdsk->raw_data = undeflate_realloc(dsk->raw_data,\n\t\t\t\t\t\t\tdsk->dimage_size);\n\t\t\tdsk->raw_dsize = dsk->dimage_size;\n\t\t\treturn cptr;\n\t\t}\n\t}\n\n\tprintf(\"Failed\\n\");\n\t// Disk image thread not found, get out\n\tfree(dsk->raw_data);\n\tdsk->fd = -1;\n\tdsk->dimage_size = 0;\n\tdsk->raw_data = 0;\n\tdsk->raw_dsize = 0;\n\treturn 0;\n}\n\nbyte g_zip_local_file_header[] = { 0x50, 0x4b, 0x03, 0x04 };\nbyte g_zip_central_file_header[] = { 0x50, 0x4b, 0x01, 0x02 };\nbyte g_zip_end_central_dir_header[] = { 0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0 };\nbyte g_zip64_end_central_dir_locator[] = { 0x50, 0x4b, 0x06, 0x07, 0, 0, 0, 0 };\nbyte g_zip64_end_central_dir_header[] = { 0x50, 0x4b, 0x06, 0x06 };\n\nextern Cfg_listhdr g_cfg_partitionlist;\n\n\nint\nundeflate_zipfile(Disk *dsk, int fd, dword64 dlocal_header_off,\n\t\t\tdword64 uncompr_dsize, dword64 compr_dsize)\n{\n\tbyte\tbuf[64];\n\tbyte\t*cptr, *cptr2;\n\tdword64\tdret, compr_doffset;\n\tword32\tcompr_method, name_len, extra_len;\n\tword32\tbit_flags;\n\tint\tret;\n\tint\ti;\n\n\t// return -1 on failure, >= 0 on success\n\n\tprintf(\"undeflate_zipfile called, fd:%d, offset:%08llx, unc:%lld \"\n\t\t\"compr:%lld\\n\", fd, dlocal_header_off, uncompr_dsize,\n\t\tcompr_dsize);\n\n\tdret = cfg_read_from_fd(fd, &buf[0], dlocal_header_off, 64);\n\tif(dret != 64) {\n\t\tprintf(\"read dret:%08llx != 64\\n\", dret);\n\t\treturn -1;\n\t}\n\n\tfor(i = 0; i < 4; i++) {\n\t\tif(buf[i] != g_zip_local_file_header[i]) {\n\t\t\tprintf(\"hdr[%d]=%02x\\n\", i, buf[i]);\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\tif(((uncompr_dsize | compr_dsize) >> 31) != 0) {\n\t\tprintf(\"Size >2GB, not supported\\n\");\n\t\treturn -1;\n\t}\n\tbit_flags = cfg_get_le16(&(buf[6]));\n\tcompr_method = cfg_get_le16(&(buf[8]));\n\t// compr_size = cfg_get_le32(&(buf[18]));\t// Probably 0\n\t// uncompr_size = cfg_get_le32(&(buf[22]));\t// Probably 0\n\tname_len = cfg_get_le16(&(buf[26]));\n\textra_len = cfg_get_le16(&(buf[28]));\n\n\t// The ZIP file format is annoying, the local header doesn't have\n\t//  compr_size and uncompr_size generally (if bit_flags bit 3 is set).\n\t// Even if it does, it's fine to always use the central directory\n\tprintf(\"bit_flags: %04x\\n\", bit_flags);\n\n\tcompr_doffset = dlocal_header_off + 30 + name_len + extra_len;\n\n\tcptr = undeflate_malloc(compr_dsize + 0x1000);\n\tfor(i = 0; i < 0x1000; i++) {\n\t\tcptr[compr_dsize + i] = 0;\n\t}\n\n\tdret = cfg_read_from_fd(fd, cptr, compr_doffset, compr_dsize);\n\tif(dret != compr_dsize) {\n\t\treturn -1;\n\t}\n\tdsk->raw_dsize = uncompr_dsize;\n\tdsk->dimage_size = uncompr_dsize;\n\n\tret = -1;\n\tif(compr_method == 0) {\t\t\t// Stored, just use cptr\n\t\tdsk->raw_data = cptr;\n\t\tdsk->raw_dsize = uncompr_dsize;\n\t\tdsk->dimage_start = 0;\n\t\tclose(fd);\n\t\tdsk->fd = 0;\n\t\tcptr = 0;\t\t\t// So free(cptr) does nothing\n\t\tret = 0;\n\t} else if(compr_method == 8) {\t\t// Deflate\n\t\tcptr2 = undeflate_zipfile_blocks(dsk, cptr, compr_dsize);\n\t\tprintf(\"undeflate_zipfile_blocks ret:%p\\n\", cptr2);\n\t\tif(cptr2 != 0) {\n\t\t\tret = 0;\n\t\t}\n\t} else {\n\t\tprintf(\"Unknown compr_method:%04x\\n\", compr_method);\n\t}\n\tfree(cptr);\n\tundeflate_free_tables();\n\n\treturn ret;\n}\n\nint\nundeflate_zipfile_search(byte *bptr, byte *cmp_ptr, int size, int cmp_len,\n\t\t\t\tint min_size)\n{\n\tint\tpos, good;\n\tint\ti;\n\n\t// Search for cmp_ptr in the bptr buffer (basically, look for \"PKxx\"\n\t//  header strings).\n\tpos = size - min_size;\n\tgood = 0;\n\twhile(pos >= 0) {\n\t\tgood = 1;\n\t\tfor(i = 0; i < cmp_len; i++) {\n\t\t\tif(bptr[pos + i] != cmp_ptr[i]) {\n\t\t\t\tgood = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(good) {\n\t\t\tbreak;\n\t\t}\n\t\tpos--;\n\t}\n\n\tif(!good) {\n\t\treturn -1;\n\t}\n\treturn pos;\n}\n\nint\nundeflate_zipfile_make_list(int fd)\n{\n\tbyte\tbuf[1024];\n\tdword64\tdret, dsize, dir_doff, dir_dsize, unc_dsize, compr_dsize;\n\tdword64\tlocal_dheader, dneg1, dval, doff, dpos, dlen;\n\tbyte\t*dirptr, *name_ptr, *bptr, *bptr2;\n\tchar\t*str;\n\tword32\textra_len, comment_len, ent, entries, part_len, inc;\n\tword32\ttmp_off, ex_off, this_size, this_id;\n\tint\tpos, good, add_it, need_compr, need_unc, need_dheader;\n\tint\tname_len;\n\tint\ti;\n\n\tdret = cfg_read_from_fd(fd, &buf[0], 0, 64);\n\tif(dret != 64) {\n\t\treturn 0;\t\t// Not a ZIP file\n\t}\n\n\t// See if it's a PKZIP file, starting 0x50, 0x4b, 0x03, 0x04\n\tfor(i = 0; i < 4; i++) {\n\t\tif(buf[i] != g_zip_local_file_header[i]) {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprintf(\"This looks like a .zip file\\n\");\n\n\t// Find end of central directory record in last 1024 bytes.  If it's\n\t//  not there, this is too complex of a ZIP file for us, give up\n\tdsize = cfg_get_fd_size(fd);\n\n\tfor(i = 0; i < 1024; i++) {\n\t\tbuf[i] = 0;\n\t}\n\tdpos = 0;\n\tdlen = dsize;\n\tif(dsize > 1024) {\n\t\tdpos = dsize - 1024;\n\t\tdlen = 1024;\n\t}\n\tdret = cfg_read_from_fd(fd, &buf[0], dpos, dlen);\n\tif(dret != dlen) {\n\t\treturn 0;\t\t// Unknown problem\n\t}\n\n\tpos = undeflate_zipfile_search(&buf[0],\n\t\t\t\t&g_zip_end_central_dir_header[0], 1024, 8, 22);\n\t\t\t// End of Central Directory is at least 22 bytes\n\tif(pos < 0) {\n\t\tprintf(\"Cannot parse this .zip file\\n\");\n\t\treturn 0;\n\t}\n\n\tentries = cfg_get_le16(&(buf[pos + 8]));\n\tdir_dsize = cfg_get_le32(&(buf[pos + 12]));\n\tdir_doff = cfg_get_le32(&(buf[pos + 16]));\n\n#if 0\n\tprintf(\".zip entries:%04x, dir_dsize:%06llx, dir_doff:%08llx\\n\",\n\t\t\t\t\tentries, dir_dsize, dir_doff);\n#endif\n\tdneg1 = 0xffffffffULL;\n\tif(dir_doff == dneg1) {\n\t\tprintf(\"We must look for the ZIP64 end dir locator\\n\");\n\t\tpos = undeflate_zipfile_search(&buf[0],\n\t\t\t&g_zip64_end_central_dir_locator[0], 1024, 8, 20);\n\t\tif(pos < 0) {\n\t\t\tprintf(\"Cannot parse this ZIP64 file\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\tdoff = cfg_get_le64(&(buf[pos + 8]));\n\t\tprintf(\"ZIP64 end of central dir record at 0x%08llx\\n\", doff);\n\t\tif((doff + 64) > dsize) {\n\t\t\tprintf(\"End Central Dir record out of bounds\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\t// Now read end of central directory record.  Just read 64 bytes\n\t\t//  It has to be at least 56 bytes, and the locator had to be\n\t\t//  after, so it must fit\n\t\tdret = cfg_read_from_fd(fd, &buf[0], doff, 64);\n\t\tif(dret != 64) {\n\t\t\treturn 0;\t\t// Unknown problem\n\t\t}\n\t\tpos = undeflate_zipfile_search(&buf[0],\n\t\t\t&g_zip64_end_central_dir_header[0], 64, 4, 64);\n\t\tif(pos != 0) {\n\t\t\tprintf(\"ZIP64 end of central dir record not found\\n\");\n\t\t\treturn 0;\n\t\t}\n\n\t\tentries = cfg_get_le32(&(buf[32]));\n\t\tdir_dsize = cfg_get_le64(&(buf[40]));\n\t\tdir_doff = cfg_get_le64(&(buf[48]));\n\t}\n\n\tif((entries < 1) || (dir_dsize > dsize) || (dir_dsize > (1L << 20)) ||\n\t\t\t\t\t((dir_doff + dir_dsize) > dsize)) {\n\t\tprintf(\"Malformed zip file\\n\");\n\t\treturn 0;\n\t}\n\n\tdirptr = undeflate_malloc(dir_dsize);\n\tdret = cfg_read_from_fd(fd, dirptr, dir_doff, dir_dsize);\n\tif(dret != dir_dsize) {\n\t\tprintf(\"Couldn't read central dir\\n\");\n\t\treturn 0;\n\t}\n\n\tpart_len = cfg_partition_maybe_add_dotdot();\n\t\t// part_len is strlen(g_cfg_part_path[]);\n\n\tpos = 0;\n\tent = 0;\n\twhile(pos < (int)dir_dsize) {\n#if 0\n\t\tprintf(\"Working on ent %d at pos %d\\n\", ent, pos);\n#endif\n\t\tif(ent >= entries) {\n\t\t\tbreak;\t\t// all done\n\t\t}\n\t\tgood = 1;\n\t\tfor(i = 0; i < 4; i++) {\n\t\t\tif(dirptr[pos + i] != g_zip_central_file_header[i]) {\n\t\t\t\t// corrupt index, get out\n\t\t\t\tprintf(\"At pos %04x, i:%d bad hdr\\n\", pos, i);\n\t\t\t\tgood = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(!good) {\n\t\t\tbreak;\n\t\t}\n\t\tcompr_dsize = cfg_get_le32(&dirptr[pos + 20]);\n\t\tunc_dsize = cfg_get_le32(&dirptr[pos + 24]);\n\t\tname_len = cfg_get_le16(&dirptr[pos + 28]);\n\t\textra_len = cfg_get_le16(&dirptr[pos + 30]);\n\t\tcomment_len = cfg_get_le16(&dirptr[pos + 32]);\n\t\tlocal_dheader = cfg_get_le32(&dirptr[pos + 42]);\n\t\tif((pos + 46UL + name_len) > dir_dsize) {\n\t\t\tprintf(\"Corrupt entry: pos:%04x, name_len:%04x, \"\n\t\t\t\t\"dir_dsize:%05llx\\n\", pos, name_len, dir_dsize);\n\t\t\tbreak;\n\t\t}\n\n\t\tneed_unc = (unc_dsize == dneg1);\n\t\tneed_compr = (compr_dsize == dneg1);\n\t\tneed_dheader = (local_dheader == dneg1);\n\n\t\t// Walk extras to update unc/compr size and file offset, if\n\t\t//  the standard fields are 0xffffffff.\n\t\tbptr = &(dirptr[pos + 46 + name_len]);\n\t\tex_off = 0;\n\t\tadd_it = 1;\n\t\twhile(ex_off < extra_len) {\n#if 0\n\t\t\tprintf(\"Working on ex_off:%d out of %d\\n\", ex_off,\n\t\t\t\t\t\t\t\textra_len);\n#endif\n\t\t\tthis_id = cfg_get_le16(&bptr[ex_off]);\n\t\t\tthis_size = cfg_get_le16(&bptr[ex_off + 2]);\n\t\t\tif((this_size + ex_off + pos + 46UL + name_len) >\n\t\t\t\t\t\t\t\tdir_dsize) {\n\t\t\t\tprintf(\"Corrupt ZIP64 extra info entry\\n\");\n\t\t\t\tadd_it = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tex_off += 4;\n\t\t\tif(this_id == 0x0001) {\n\t\t\t\ttmp_off = 0;\n\t\t\t\tbptr2 = &(bptr[ex_off]);\n\t\t\t\twhile(tmp_off < this_size) {\n\t\t\t\t\tdval = cfg_get_le64(bptr2);\n#if 0\n\t\t\t\t\tprintf(\"tmp_off %d of %d, dval:\"\n\t\t\t\t\t\t\"%016llx\\n\", tmp_off,\n\t\t\t\t\t\tthis_size, dval);\n#endif\n\t\t\t\t\tif(need_compr) {\n\t\t\t\t\t\tcompr_dsize = dval;\n\t\t\t\t\t\tneed_compr = 0;\n\t\t\t\t\t} else if(need_unc) {\n\t\t\t\t\t\tunc_dsize = dval;\n\t\t\t\t\t\tneed_unc = 0;\n\t\t\t\t\t} else if(need_dheader) {\n\t\t\t\t\t\tlocal_dheader = dval;\n\t\t\t\t\t\tneed_dheader = 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprintf(\"Corrupt ZIP64\\n\");\n\t\t\t\t\t\tadd_it = 0;\n\t\t\t\t\t}\n\t\t\t\t\ttmp_off += 8;\n\t\t\t\t\tbptr += 8;\n\t\t\t\t}\n\t\t\t}\n\t\t\tex_off += this_size;\n\t\t}\n\n\t\tif(need_unc || need_compr || need_dheader) {\n\t\t\tprintf(\"Bad ZIP64 overrides\\n\");\n\t\t\tadd_it = 0;\n\t\t}\n\n\t\t// See if filename is at the proper depth\n\t\tname_ptr = &(dirptr[pos + 46]);\n\t\tif(add_it) {\n\t\t\tadd_it = cfg_partition_name_check(name_ptr, name_len);\n\t\t}\n\n\t\t//printf(\"ent:%d name:%s len:%d had add_it:%d, part_len:%d\\n\",\n\t\t//\t\tent, name_ptr, name_len, add_it, part_len);\n\n\t\tinc = 46 + name_len + extra_len + comment_len;\n\t\tif(add_it) {\n\t\t\t// Handle directories either explicitly listed, as\n\t\t\t//  foo/, foo/bar/, foo/bar/1, foo/bar/2 ; or\n\t\t\t//  implied:  foo/bar/1 and foo/bar/2 as entries\n\t\t\t//  implies foo/ and foo/bar/ are directories.\n\t\t\t// Add any name at the current part_len level, but\n\t\t\t//  make sure it's unique (don't add lots of \"foo\"s).\n\t\t\tname_ptr += part_len;\n\t\t\tname_len -= part_len;\n\t\t\tif(name_len <= 0) {\n\t\t\t\tadd_it = 0;\n\t\t\t}\n\t\t\tfor(i = 0; i < name_len; i++) {\n\t\t\t\tif(name_ptr[i] == '/') {\n\t\t\t\t\t// This ends this name at this level\n\t\t\t\t\tif(i > 0) {\n\t\t\t\t\t\tadd_it = 2;\n\t\t\t\t\t\tname_len = i + 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tadd_it = 0;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif((add_it < 2) && (unc_dsize < 140*1024)) {\n\t\t\tadd_it = 0;\n\t\t}\n\t\tif(add_it) {\n\t\t\tstr = malloc(name_len + 1);\n\t\t\tcfg_strncpy(str, (char *)&name_ptr[0], name_len + 1);\n\t\t\tcfg_file_add_dirent_unique(&g_cfg_partitionlist, str,\n\t\t\t\tadd_it - 1, unc_dsize, local_dheader,\n\t\t\t\tcompr_dsize, ent);\n\t\t\tfree(str);\n\t\t}\n\t\tpos += inc;\n\t\tent++;\n\t}\n\tfree(dirptr);\n\n\tprintf(\"Returning %d, pos:%05x, dir_dsize:%05llx\\n\", ent, pos,\n\t\t\t\t\t\t\t\tdir_dsize);\n\treturn g_cfg_partitionlist.last;\n}\n\n"
  },
  {
    "path": "gsplus/src/unshk.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2021 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// This code is based on the official NuFX documentation in Apple II FTN.e08002\n//  available at: http://nulib.com/library/FTN.e08002.htm.  Andy McFadden has\n//  reverse-engineered GSHK (and its quirks) in Nulib, at: http://nulib.com/,\n//  and that code was very helpful in getting the basic algorithms correct.\n\n#include \"defc.h\"\n\nword32\nunshk_get_long4(byte *bptr)\n{\n\tword32\tval;\n\tint\ti;\n\n\t// Get 4 bytes in little-endian form\n\tval = 0;\n\tfor(i = 3; i >=0 ; i--) {\n\t\tval = (val << 8) | bptr[i];\n\t}\n\treturn val;\n}\n\nword32\nunshk_get_word2(byte *bptr)\n{\n\t// Get 2 bytes in little-endian form\n\treturn (bptr[1] << 8) | bptr[0];\n}\n\nword32\nunshk_calc_crc(byte *bptr, int size, word32 start_crc)\n{\n\tword32\tcrc;\n\tint\ti, j;\n\n\t// No table used: do basic CRC operation on size bytes.  For CCITT-16\n\t//  (the one used in ShrinkIt), xor the byte into the upper 8 bits of\n\t//  the current crc, then xor in 0x1021 for each '1' bit shifted out\n\t//  the top.  Use a 32-bit crc variable to get the bit shifted out.\n\tcrc = start_crc & 0xffff;\n\tfor(i = 0; i < size; i++) {\n\t\tcrc = crc ^ (bptr[i] << 8);\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\tcrc = crc << 1;\n\t\t\tif(crc & 0x10000) {\n\t\t\t\tcrc = crc ^ 0x11021;\n\t\t\t\t// XOR in 0x1021, and clear bit 16 as well.\n\t\t\t}\n\t\t}\n\t\t// printf(\"CRC after [%04x]=%02x is %04x\\n\", i, bptr[i], crc);\n\t}\n\treturn crc & 0xffff;\n}\n\nint\nunshk_unrle(byte *cptr, int len, word32 rle_delim, byte *ucptr)\n{\n\tbyte\t*start_ucptr;\n\tword32\tc;\n\tint\toutlen, count;\n\tint\ti;\n\n\t// RLE is 3 bytes: { 0xdb, char, count}, where count==0 means output\n\t//  one char.\n\tstart_ucptr = ucptr;\n\twhile(len > 0) {\n\t\tc = *cptr++;\n\t\tlen--;\n\t\tif(c == rle_delim) {\n\t\t\tc = *cptr++;\n\t\t\tcount = *cptr++;\n\t\t\tlen -= 2;\n\t\t\tfor(i = 0; i <= count; i++) {\n\t\t\t\t*ucptr++ = c;\n\t\t\t}\n\t\t} else {\n\t\t\t*ucptr++ = c;\n\t\t}\n\t}\n\toutlen = (int)(ucptr - start_ucptr);\n\tif(outlen != 0x1000) {\n\t\tprintf(\"RLE failed, output %d bytes\\n\", outlen);\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nvoid\nunshk_lzw_clear(Lzw_state *lzw_ptr)\n{\n\tint\ti;\n\n\tlzw_ptr->entry = 0x100;\t\t\t// First expected table pos\n\tlzw_ptr->bits = 9;\n\tfor(i = 0; i < 256; i++) {\n\t\tlzw_ptr->table[i] = i << 12;\t// Encodes depth==0 as well\n\t}\n\tlzw_ptr->table[0x100] = 0;\n}\n\n// LZW Table format in 32-bit word: { depth[11:0], finalc[7:0], code[11:0] }\n\nbyte *\nunshk_unlzw(byte *cptr, Lzw_state *lzw_ptr, byte *ucptr, word32 uclen)\n{\n\tbyte\t*end_ucptr, *bptr;\n\tword32\tmask, val, entry, newcode, finalc_code;\n\tint\tbit_pos, depth, bits;\n\n\t// This routine handles ShrinkIt LZW/1 and LZW/2 streams.  It expects\n\t//  the caller has set entry=0x100 and bits=9 at the start of each\n\t//  LZW/1 chunk\n\n\tentry = lzw_ptr->entry;\n\tbits = lzw_ptr->bits;\n\tend_ucptr = ucptr + uclen;\n\t//printf(\"unlzw block: format:%d, uclen:%04x\\n\", thread_format, uclen);\n\tmask = (1 << bits) - 1;\n\tbit_pos = 0;\n\n\twhile(ucptr < end_ucptr) {\n\t\tnewcode = (cptr[2] << 16) | (cptr[1] << 8) | cptr[0];\n\t\tnewcode = (newcode >> bit_pos) & mask;\n\t\tbit_pos += bits;\n\t\tcptr += (bit_pos >> 3);\n\t\tbit_pos = bit_pos & 7;\n\t\t// printf(\"At entry:%04x, bits:%d newcode:%04x\\n\", entry, bits,\n\t\t//\t\t\t\t\t\tnewcode);\n\t\tif((entry + 1) >= mask) {\n\t\t\tbits++;\n\t\t\tmask = (mask << 1) | 1;\n\t\t\t// Note this is one too early, but this is needed to\n\t\t\t//  match ShrinkIt\n\t\t}\n\n\t\t// Newcode is up to 12-bits, where <= 0xff means just this\n\t\t//  char, and >= 0x101 means chase down that code, output the\n\t\t//  character in that table entry, and repeat\n\t\tif(newcode == 0x100) {\n\t\t\t// printf(\"Got clear code\\n\");\n\t\t\tentry = 0x100;\n\t\t\tbits = 9;\n\t\t\tmask = 0x1ff;\n\t\t\tcontinue;\n\t\t}\n\t\tif(newcode > entry) {\n\t\t\tprintf(\"Bad code: %04x, entry:%04x\\n\", newcode, entry);\n\t\t\treturn 0;\n\t\t}\n#if 0\n\t\tif(newcode == entry) {\n\t\t\t// KwKwK case: operate on oldcode\n\t\t\tprintf(\"KwKwK case!\\n\");\n\t\t}\n#endif\n\t\tfinalc_code = newcode;\n\t\tdepth = lzw_ptr->table[newcode & 0xfff] >> 20;\n\t\t// depth will be 0 for 1 character, 1 for 2 characters, etc.\n\t\tbptr = ucptr + depth;\n\t\twhile(bptr >= ucptr) {\n\t\t\tfinalc_code = lzw_ptr->table[finalc_code & 0xfff];\n\t\t\t*bptr-- = (finalc_code >> 12) & 0xff;\n\t\t}\n\t\tval = lzw_ptr->table[entry];\n\t\tlzw_ptr->table[entry] = (val & (~0xff000)) |\n\t\t\t\t\t\t(finalc_code & 0xff000);\n\t\t\t// [entry] has code from last iteration (which stuck in\n\t\t\t//  the last finalc char, which we need to toss now),\n\t\t\t//  and update it with the correct finalc character.\n\t\t// printf(\"Table[%04x]=%08x\\n\", entry, lzw_ptr->table[entry]);\n\n\t\tdepth++;\n\t\tucptr += depth;\n#if 0\n\t\tbptr = ucptr - depth;\n\t\tprintf(\"src:%04x, out+%06x: \", (int)(cptr - cptr_start),\n\t\t\t\t(int)(ucptr - depth - (end_ucptr - uclen)));\n\t\tfor(i = 0; i < depth; i++) {\n\t\t\tprintf(\" %02x\", *bptr++);\n\t\t}\n\t\tprintf(\"\\n\");\n#endif\n\t\tlzw_ptr->table[entry + 1] = (depth << 20) |\n\t\t\t\t\t(finalc_code & 0xff000) | newcode;\n\t\t\t// Set tab[entry+1] for KwKwK case, with this newcode,\n\t\t\t//  and this finalc character.  This also saves this\n\t\t\t//  newcode when the next code is received.\n\t\tentry++;\n\t}\n\tlzw_ptr->entry = entry;\n\tlzw_ptr->bits = bits;\n\tif(bit_pos) {\t\t\t// We used part of this byte, use it\n\t\tcptr++;\n\t}\n\treturn cptr;\n}\n\nvoid\nunshk_data(Disk *dsk, byte *cptr, word32 compr_size, byte *ucptr,\n\t\tword32 uncompr_size, word32 thread_format, byte *base_cptr)\n{\n\tLzw_state lzw_state;\n\tbyte\t*end_cptr, *end_ucptr, *rle_inptr;\n\tword32\trle_delim, len, use_lzw, lzw_len, crc, chunk_crc;\n\tint\tret;\n\tint\ti;\n\n\tprintf(\"Uncompress %d compress bytes into %d bytes, source offset:\"\n\t\t\"%08x\\n\", compr_size, uncompr_size, (word32)(cptr - base_cptr));\n\n\t// LZW/1 format: crc_lo, crc_hi, vol, rle_delim then start the chunk\n\t//\teach chunk: rle_len_lo, rle_len_hi, lzw_used\n\t// LZW/2 format: vol, rle_delim then start the chunk\n\t//\teach chunk: rle_len_lo, rle_len_hi, lzw_len_lo, lzw_len_hi\n\t//\twhere rle_len_hi[7]==1 means LZW was used\n\tend_cptr = cptr + compr_size;\n\tend_ucptr = ucptr + uncompr_size;\n\n\tchunk_crc = 0;\n\tif(thread_format != 3) {\t\t// LZW/1\n\t\tchunk_crc = (cptr[1] << 8) | cptr[0];\n\t\tcptr += 2;\t\t\t// Skip over CRC bytes\n\t}\n\tdsk->vol_num = cptr[0];\t\t// LZW/1\n\trle_delim = cptr[1];\n\tcptr += 2;\n\tunshk_lzw_clear(&lzw_state);\n\n\t// printf(\"vol_num:%02x, rle_delim:%02x\\n\", dsk->vol_num, rle_delim);\n\n\t// LZW/1 format for each chunk: len_lo, len_hi, use_lzw\n\t// LZW/2 format for each chunk: len_lo, len_hi.  If len_hi[7]=1, then\n\t//\ttwo more bytes: lzw_len_lo, lzw_len_hi\n\twhile(cptr < (end_cptr - 4)) {\n\t\tif(ucptr >= end_ucptr) {\n\t\t\tbreak;\n\t\t}\n\t\tlen = (cptr[1] << 8) | cptr[0];\n#if 0\n\t\tprintf(\"chunk at +%08x, len:%04x, dest offset:%08x\\n\",\n\t\t\t\t(word32)(cptr - base_cptr), len,\n\t\t\t\t(word32)(ucptr - (end_ucptr - uncompr_size)));\n#endif\n\t\tcptr += 2;\n\t\tuse_lzw = (len >> 15) & 1;\n\t\tif(len & 0x6000) {\n\t\t\tprintf(\"Illegal length: %04x\\n\", len);\n\t\t\treturn;\t\t\t\t// Ilegal length\n\t\t}\n\t\tlen = len & 0x1fff;\n\t\tlzw_len = 0;\n\t\tif(thread_format == 3) {\t\t// LZW/2\n\t\t\tif(use_lzw) {\n\t\t\t\tlzw_len = (cptr[1] << 8) | cptr[0];\n\t\t\t\tif(lzw_len > 0x1004) {\n\t\t\t\t\tprintf(\"Bad lzw_len: %04x\\n\", lzw_len);\n\t\t\t\t\treturn;\t\t// Illegal\n\t\t\t\t}\n\t\t\t\tcptr += 2;\n\t\t\t\tlzw_len -= 4;\t\t// Counts from [-4]\n\t\t\t}\n\t\t} else {\t\t\t\t// LZW/1\n\t\t\tuse_lzw = *cptr++;\n\t\t\tif(use_lzw >= 2) {\n\t\t\t\tprintf(\"Bad use_lzw:%02x\\n\", use_lzw);\n\t\t\t\treturn;\t\t\t// Bad format\n\t\t\t}\n\t\t}\n\t\trle_inptr = cptr;\n\t\tif(use_lzw) {\n\t\t\t//printf(\"lzw on %02x.%02x.%02x.., %d bytes (rle:%d)\\n\",\n\t\t\t//\tcptr[0], cptr[1], cptr[2], lzw_len, len);\n\t\t\trle_inptr = ucptr;\n\t\t\tif(len != 0x1000) {\n\t\t\t\t// RLE pass is needed: Write to ucptr+0x1000,\n\t\t\t\t//  and then UnRLE down to ucptr;\n\t\t\t\trle_inptr = ucptr + 0x1000;\n\t\t\t}\n\t\t\tcptr = unshk_unlzw(cptr, &lzw_state, rle_inptr, len);\n\t\t\tif(cptr == 0) {\n\t\t\t\tprintf(\"Bad LZW stream\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif(thread_format != 3) {\t\t// LZW/1\n\t\t\t\tlzw_state.entry = 0x100;\t// Reset table\n\t\t\t\tlzw_state.bits = 9;\n\t\t\t}\n\t\t} else {\n\t\t\tlzw_state.entry = 0x100;\t// Reset table\n\t\t\tlzw_state.bits = 9;\n\t\t}\n\t\tif(len != 0x1000) {\n\t\t\t// printf(\"RLE on %02x.%02x.%02x... %d bytes\\n\",\n\t\t\t//\tcptr[0], cptr[1], cptr[2], len);\n\t\t\tret = unshk_unrle(rle_inptr, len, rle_delim, ucptr);\n\t\t\tif(ret) {\n\t\t\t\tprintf(\"unRLE failed\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif(!use_lzw) {\n\t\t\t\tcptr += len;\n\t\t\t}\n\t\t} else if(!use_lzw) {\n\t\t\t// Uncompressed\n\t\t\t// printf(\"Uncompressed %02x.%02x.%02x....%d bytes\\n\",\n\t\t\t//\tcptr[0], cptr[1], cptr[2], len);\n\t\t\tfor(i = 0; i < 0x1000; i++) {\n\t\t\t\tucptr[i] = *cptr++;\n\t\t\t}\n\t\t}\n\t\t// write(g_out_fd, ucptr, 0x1000);\n\t\tucptr += 0x1000;\n\t}\n\n\tprintf(\"cptr:%p, end_cptr:%p, uncompr_size:%08x\\n\", cptr, end_cptr,\n\t\t\t\t\t\t\t\tuncompr_size);\n\tif(thread_format != 3) {\t\t// LZW/1\n\t\tcrc = unshk_calc_crc(ucptr - uncompr_size, uncompr_size, 0);\n\t\t//printf(\"LZW/1 calc CRC %04x vs CRC %04x\\n\", crc, chunk_crc);\n\t\tif(crc != chunk_crc) {\n\t\t\tprintf(\"Bad LZW/1 CRC: %04x != %04x\\n\", crc, chunk_crc);\n\t\t\treturn;\n\t\t}\n\t}\n\tdsk->fd = 0;\n}\n\nvoid\nunshk_parse_header(Disk *dsk, byte *cptr, int compr_size, byte *base_cptr)\n{\n\tbyte\t*cptr_end, *dptr, *ucptr;\n\tword32\ttotal_records, attrib_count, total_threads, thread_class;\n\tword32\tthread_format, thread_kind, thread_eof, comp_thread_eof;\n\tword32\tthread_crc, crc, version, disk_size, block_size, num_blocks;\n\tword32\tfilename_length;\n\tint\ti;\n\n\tcptr_end = cptr + compr_size;\n\tif(compr_size < 0xa0) {\n\t\tprintf(\"Didn't read everything\\n\");\n\t\treturn;\n\t}\n\n\t// Parse NuFX format: \"NuFile\" with alternating high bits\n\tif((cptr[0] != 0x4e) || (cptr[1] != 0xf5) || (cptr[2] != 0x46) ||\n\t\t(cptr[3] != 0xe9) || (cptr[4] != 0x6c) || (cptr[5] != 0xe5)) {\n\t\tprintf(\"Not NuFile, exiting\\n\");\n\t\treturn;\n\t}\n\ttotal_records = unshk_get_long4(&cptr[8]);\n\tif(total_records < 1) {\n\t\treturn;\n\t}\n\n\t// Master Header to NuFile is apparently 48 bytes.  Look for \"NuFX\"\n\t//  Header to describe threads\n\tcptr += 0x30;\n\tif((cptr[0] != 0x4e) || (cptr[1] != 0xf5) || (cptr[2] != 0x46) ||\n\t\t\t\t\t\t\t(cptr[3] != 0xd8)) {\n\t\treturn;\n\t}\n\tattrib_count = unshk_get_word2(&cptr[6]);\n\tversion = unshk_get_word2(&cptr[8]);\t\t// >= 3 means File CRC\n\ttotal_threads = unshk_get_long4(&cptr[10]);\n\tnum_blocks = unshk_get_long4(&cptr[26]);\t// extra_type\n\tblock_size = unshk_get_word2(&cptr[30]);\t// storage_type\n\t// P8 ShrinkIt is riddled with bugs.  Disk archives have incorrect\n\t//  thread_eof for the uncompressed total size.  So we need to do\n\t//  num_blocks * block_size to get the real size.  But this can be\n\t//  buggy too!  These fixes are from NuFxLib::Thread.c actualThreadEOF\n\t//  comments\n\t// First, fix block_size.  SHK v3.0.1 stored it as a small value\n\tif(block_size < 256) {\n\t\tblock_size = 512;\n\t}\n\tdisk_size = block_size * num_blocks;\n\tif(disk_size == (70*1024)) {\n\t\t// Old GSHK apparently set block_size==256 but blocks=280 for\n\t\t//  5.25\" DOS 3.3 disks...block size must be 512 to equal 140K.\n\t\tdisk_size = 140*1024;\n\t}\n\tif(disk_size < 140*1024) {\n\t\tprintf(\"disk_size %dK is invalid\\n\", disk_size >> 10);\n\t\treturn;\n\t}\n\tcptr += attrib_count;\n\tfilename_length = unshk_get_word2(&cptr[-2]);\t// filename_length\n\tcptr += filename_length;\n\tdptr = cptr + 16*total_threads;\n\t\t// Each thread is 16 bytes, so the data is at +16*total_threads\n\t\t// The data is in the same order as the header for the threads\n\t\t// We ignore anything other than a data thread for SDK\n\tfor(i = 0; i < (int)total_threads; i++) {\n\t\tif((dptr >= cptr_end) || (cptr >= cptr_end)) {\n\t\t\treturn;\n\t\t}\n\t\tthread_class = unshk_get_word2(&cptr[0]);\n\t\tthread_format = unshk_get_word2(&cptr[2]);\n\t\tthread_kind = unshk_get_word2(&cptr[4]);\n\t\tthread_crc = unshk_get_word2(&cptr[6]);\n\t\t//thread_eof = unshk_get_long4(&cptr[8]);\n\t\t// thread_eof is wrong in P8 ShrinkIt, so just use disk_size\n\t\tthread_eof = disk_size;\n\t\tcomp_thread_eof = unshk_get_long4(&cptr[12]);\n\t\tif((dptr + comp_thread_eof) > cptr_end) {\n\t\t\treturn;\t\t\t// Corrupt\n\t\t}\n\t\tif((thread_class == 2) && (thread_kind == 1)) {\n\t\t\t// Disk image!\n\t\t\tucptr = malloc(thread_eof + 0x1000);\n\t\t\tunshk_data(dsk, dptr, comp_thread_eof, ucptr,\n\t\t\t\t\tthread_eof, thread_format, base_cptr);\n\t\t\tif(dsk->fd == 0) {\n\t\t\t\t// Success, so far.  Check CRC\n\t\t\t\tprintf(\"Version:%d, thread_crc:%04x\\n\",\n\t\t\t\t\tversion, thread_crc);\n\t\t\t\tif(version >= 3) {\t\t// CRC is valid\n\t\t\t\t\tcrc = unshk_calc_crc(ucptr, thread_eof,\n\t\t\t\t\t\t\t\t0xffff);\n#if 0\n\t\t\t\t\tprintf(\"Thread CRC:%04x, exp:%04x\\n\",\n\t\t\t\t\t\tcrc, thread_crc);\n#endif\n\t\t\t\t\tif(crc != thread_crc) {\n\t\t\t\t\t\tprintf(\"Bad CRC: %04x != exp \"\n\t\t\t\t\t\t\t\"%04x\\n\", crc,\n\t\t\t\t\t\t\tthread_crc);\n\t\t\t\t\t\tdsk->fd = -1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(dsk->fd < 0) {\n\t\t\t\tfree(ucptr);\n\t\t\t} else {\n\t\t\t\t// Real success, set raw_size\n\t\t\t\tdsk->raw_dsize = thread_eof;\n\t\t\t\tdsk->raw_data = ucptr;\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\tdptr += comp_thread_eof;\n\t\t\tcptr += 16;\n\t\t}\n\t}\n\n\t// Disk image thread not found, get out\n}\n\nvoid\nunshk(Disk *dsk, const char *name_str)\n{\n\tbyte\t*cptr;\n\tint\tcompr_size, fd, pos, ret;\n\tint\ti;\n\n\tprintf(\"unshk %s\\n\", name_str);\n\t// Handle .sdk inside a .zip\n\tif(dsk->raw_data) {\n\t\tunshk_dsk_raw_data(dsk);\n\t\treturn;\n\t}\n\n\t// File is not opened yet, try to open it\n\tfd = open(name_str, O_RDONLY | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\treturn;\n\t}\n\tcompr_size = (int)cfg_get_fd_size(fd);\n\tprintf(\"size: %d\\n\", compr_size);\n\n\tcptr = malloc(compr_size + 0x1000);\n\tpos = 0;\n\tfor(i = 0; i < 0x1000; i++) {\n\t\tcptr[compr_size + i] = 0;\n\t}\n\twhile(1) {\n\t\tif(pos >= compr_size) {\n\t\t\tbreak;\n\t\t}\n\t\tret = read(fd, cptr + pos, compr_size - pos);\n\t\tif(ret <= 0) {\n\t\t\tbreak;\n\t\t}\n\t\tpos += ret;\n\t}\n\tclose(fd);\n\n\tif(pos != compr_size) {\n\t\tcompr_size = 0;\t\t// Make header searching fail\n\t}\n\tunshk_parse_header(dsk, cptr, compr_size, cptr);\n\n\tfree(cptr);\n}\n\nvoid\nunshk_dsk_raw_data(Disk *dsk)\n{\n\tbyte\t*save_raw_data, *cptr;\n\tdword64\tsave_raw_dsize;\n\tint\tsave_fd, compr_size;\n\tint\ti;\n\t// This code handles the case of .sdk inside a .zip (for example).\n\t// Since unshk() code uses dsk->fd, dsk->raw_data, and dsk->raw_dsize\n\t//  to communicate success in unshk'ing the disk, we need to copy\n\t//  those, and restore them, if the unshk fails\n\tsave_fd = dsk->fd;\n\tsave_raw_data = dsk->raw_data;\n\tsave_raw_dsize = dsk->raw_dsize;\n\tif(save_raw_dsize >= (1ULL << 30)) {\n\t\treturn;\t\t\t// Too large\n\t}\n\n\tdsk->fd = -1;\n\tdsk->raw_data = 0;\n\tdsk->raw_dsize = 0;\n\n\tcompr_size = (int)save_raw_dsize;\n\tcptr = malloc(compr_size + 0x1000);\n\tfor(i = 0; i < 0x1000; i++) {\n\t\tcptr[compr_size + i] = 0;\n\t}\n\tfor(i = 0; i < compr_size; i++) {\n\t\tcptr[i] = save_raw_data[i];\n\t}\n\n\tunshk_parse_header(dsk, cptr, compr_size, cptr);\n\tfree(cptr);\n\n\tif(dsk->raw_data) {\n\t\t// Success, free the old raw data\n\t\tfree(save_raw_data);\n\t\treturn;\n\t}\n\tdsk->fd = save_fd;\n\tdsk->raw_data = save_raw_data;\n\tdsk->raw_dsize = save_raw_dsize;\n}\n\n"
  },
  {
    "path": "gsplus/src/vars",
    "content": "TARGET = gsplus\nOBJECTS1 = macsnd_driver.o\nCCOPTS = -Wall -O2 -DMAC\nSUFFIX =\nNAME = gsplus\n\nXOPTS =\n\n"
  },
  {
    "path": "gsplus/src/vars_mac",
    "content": "TARGET = gsplus\nOBJECTS1 = macsnd_driver.o\nCCOPTS = -Wall -O2 -DMAC\nSUFFIX =\nNAME = gsplus\n\nXOPTS =\n\n"
  },
  {
    "path": "gsplus/src/vars_mac_x",
    "content": "TARGET = gsplus\nOBJECTS1 = macsnd_driver.o xdriver.o\nCCOPTS = -O2 -DMAC -Wall -I/usr/X11/include\nSUFFIX =\nNAME = gsplus\n\nXOPTS =\nLDOPTS = -Wl,-framework,CoreAudio -Wl,-framework,CoreFoundation -Wl,-framework,AudioToolbox\n\n"
  },
  {
    "path": "gsplus/src/vars_x86linux",
    "content": "TARGET = gsplus\nOBJECTS1 = pulseaudio_driver.o xdriver.o\nCCOPTS = -O2 -Wall -fomit-frame-pointer -DPULSE_AUDIO\nNAME = gsplus\nLD = $(CC)\nEXTRA_LIBS = -lXext -lpulse\nEXTRA_SPECIALS =\n\nXOPTS = -I/usr/X11R6/include\n\n"
  },
  {
    "path": "gsplus/src/video.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2025 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n#include <time.h>\n\n#include \"defc.h\"\n\nextern int Verbose;\n\nword32 g_a2_filt_stat[200];\nint g_a2_line_left_edge[200];\nint g_a2_line_right_edge[200];\n\nbyte g_cur_border_colors[270];\n\nword32\tg_a2_screen_buffer_changed = (word32)-1;\nword32\tg_full_refresh_needed = (word32)-1;\n\nword32 g_cycs_in_40col = 0;\nword32 g_cycs_in_xredraw = 0;\nword32 g_refresh_bytes_xfer = 0;\n\nextern byte *g_slow_memory_ptr;\nextern int g_fatal_log;\n\nextern dword64 g_cur_dfcyc;\n\nextern int g_line_ref_amt;\n\nextern word32 g_c034_val;\nextern int g_config_control_panel;\nextern int g_halt_sim;\n\nword32 g_slow_mem_changed[SLOW_MEM_CH_SIZE];\nword32 g_slow_mem_ch2[SLOW_MEM_CH_SIZE];\n\nword32 g_a2font_bits[0x100][8];\n\nword32 g_superhires_scan_save[2][256];\n\nKimage g_mainwin_kimage = { 0 };\nKimage g_debugwin_kimage = { 0 };\nint g_debugwin_last_total = 0;\n\nextern int g_debug_lines_total;\n\nextern dword64 g_last_vbl_dfcyc;\nextern dword64 g_video_pixel_dcount;\n\ndword64\tg_video_dfcyc_check_input = 0;\nint\tg_video_act_margin_left = BASE_MARGIN_LEFT;\nint\tg_video_act_margin_right = BASE_MARGIN_RIGHT;\nint\tg_video_act_margin_top = BASE_MARGIN_TOP;\nint\tg_video_act_margin_bottom = BASE_MARGIN_BOTTOM;\nint\tg_video_act_width = X_A2_WINDOW_WIDTH;\nint\tg_video_act_height = X_A2_WINDOW_HEIGHT;\nint\tg_mainwin_width = X_A2_WINDOW_WIDTH;\nint\tg_mainwin_height = X_A2_WINDOW_HEIGHT + MAX_STATUS_LINES*16 + 2;\nint\tg_mainwin_xpos = 100;\nint\tg_mainwin_ypos = 300;\nint\tg_video_no_scale_window = 0;\n\nword32\tg_palette_change_cnt[2][16];\nint\tg_border_sides_refresh_needed = 1;\nint\tg_border_special_refresh_needed = 1;\nint\tg_border_line24_refresh_needed = 1;\nint\tg_status_refresh_needed = 1;\n\nint\tg_vbl_border_color = 0;\nint\tg_border_last_vbl_changes = 0;\nint\tg_border_reparse = 0;\n\nint\tg_use_dhr140 = 0;\nint\tg_use_bw_hires = 0;\n\nint\tg_vid_update_last_line = 0;\nint\tg_video_save_all_stat_pos = 0;\n\nint g_cur_a2_stat = ALL_STAT_TEXT | ALL_STAT_ANNUNC3 |\n\t\t\t\t\t(0xf << BIT_ALL_STAT_TEXT_COLOR);\n\nword32 g_palette_8to1624[2][256];\nword32 g_a2palette_1624[16];\n\nword32\tg_saved_line_palettes[2][200][8];\n\nword32 g_cycs_in_refresh_line = 0;\nword32 g_cycs_in_refresh_ximage = 0;\nword32 g_cycs_in_run_16ms = 0;\n\nint\tg_num_lines_superhires = 0;\nint\tg_num_lines_superhires640 = 0;\nint\tg_num_lines_prev_superhires = 0;\nint\tg_num_lines_prev_superhires640 = 0;\n\nint\tg_screen_redraw_skip_count = 0;\nint\tg_screen_redraw_skip_amt = -1;\n\nword32\tg_alpha_mask = 0;\nword32\tg_red_mask = 0xff;\nword32\tg_green_mask = 0xff;\nword32\tg_blue_mask = 0xff;\nint\tg_red_left_shift = 16;\nint\tg_green_left_shift = 8;\nint\tg_blue_left_shift = 0;\nint\tg_red_right_shift = 0;\nint\tg_green_right_shift = 0;\nint\tg_blue_right_shift = 0;\n\nint\tg_status_enable = 1;\nint\tg_status_enable_previous = 1;\nchar\tg_status_buf[MAX_STATUS_LINES][STATUS_LINE_LENGTH + 1];\nchar\t*g_status_ptrs[MAX_STATUS_LINES] = { 0 };\nword16\tg_pixels_widened[128];\n\nint\tg_video_scale_algorithm = 0;\n\nSTRUCT(Video_all_stat) {\n\tword32\tlines_since_vbl;\n\tword32\tcur_all_stat;\n};\n\n#define MAX_VIDEO_ALL_STAT\t((200*42) + 40)\nint g_video_all_stat_pos = 0;\nVideo_all_stat g_video_all_stat[MAX_VIDEO_ALL_STAT];\n\nSTRUCT(Video_filt_stat) {\n\tword32\tline_bytes;\n\tword32\tfilt_stat;\n};\n\n#define MAX_VIDEO_FILT_STAT\t10000\nint g_video_filt_stat_pos = 0;\nVideo_filt_stat g_video_filt_stat[MAX_VIDEO_FILT_STAT];\n\nint g_video_stat_old_pos = 0;\nVideo_filt_stat g_video_filt_stat_old[MAX_VIDEO_FILT_STAT];\n\n\nword16 g_dhires_convert[4096];\t/* look up { next4, this4, prev 4 } */\n\nconst byte g_dhires_colors_16[] = {\t\t// Convert dhires to lores color\n\t\t0x00,\t/* 0x0 black */\n\t\t0x02,\t/* 0x1 dark blue */\n\t\t0x04,\t/* 0x2 dark green */\n\t\t0x06,\t/* 0x3 medium blue */\n\t\t0x08,\t/* 0x4 brown */\n\t\t0x0a,\t/* 0x5 light gray */\n\t\t0x0c,\t/* 0x6 green */\n\t\t0x0e,\t/* 0x7 aquamarine */\n\t\t0x01,\t/* 0x8 deep red */\n\t\t0x03,\t/* 0x9 purple */\n\t\t0x05,\t/* 0xa dark gray */\n\t\t0x07,\t/* 0xb light blue */\n\t\t0x09,\t/* 0xc orange */\n\t\t0x0b,\t/* 0xd pink */\n\t\t0x0d,\t/* 0xe yellow */\n\t\t0x0f\t/* 0xf white */\n};\n\nconst int g_lores_colors[] = {\t\t// From IIgs Technote #63\n\t\t/* rgb */\n\t\t0x000,\t\t/* 0x0 black */\n\t\t0xd03,\t\t/* 0x1 deep red */\n\t\t0x009,\t\t/* 0x2 dark blue */\n\t\t0xd2d,\t\t/* 0x3 purple */\n\t\t0x072,\t\t/* 0x4 dark green */\n\t\t0x555,\t\t/* 0x5 dark gray */\n\t\t0x22f,\t\t/* 0x6 medium blue */\n\t\t0x6af,\t\t/* 0x7 light blue */\n\t\t0x850,\t\t/* 0x8 brown */\n\t\t0xf60,\t\t/* 0x9 orange */\n\t\t0xaaa,\t\t/* 0xa light gray */\n\t\t0xf98,\t\t/* 0xb pink */\n\t\t0x1d0,\t\t/* 0xc green */\n\t\t0xff0,\t\t/* 0xd yellow */\n\t\t0x4f9,\t\t/* 0xe aquamarine */\n\t\t0xfff\t\t/* 0xf white */\n};\n\nconst byte g_hires_lookup[64] = {\n// Indexed by { next_bit, this_bit, prev_bit, hibit, odd_byte, odd_col }.\n//  Return lores colors: 0, 3, 6, 9, 0xc, 0xf\n\t0x00,\t\t\t// 00,0000\t// black: this and next are 0\n\t0x00,\t\t\t// 00,0001\n\t0x00,\t\t\t// 00,0010\n\t0x00,\t\t\t// 00,0011\n\t0x00,\t\t\t// 00,0100\n\t0x00,\t\t\t// 00,0101\n\t0x00,\t\t\t// 00,0110\n\t0x00,\t\t\t// 00,0111\n\t0x00,\t\t\t// 00,1000\n\t0x00,\t\t\t// 00,1001\n\t0x00,\t\t\t// 00,1010\n\t0x00,\t\t\t// 00,1011\n\t0x00,\t\t\t// 00,1100\n\t0x00,\t\t\t// 00,1101\n\t0x00,\t\t\t// 00,1110\n\t0x00,\t\t\t// 00,1111\n\n\t0x03,\t\t\t// 01,0000\t// purple\n\t0x03,\t\t\t// 01,0001\t// purple\n\t0x0c,\t\t\t// 01,0010\t// green (odd column)\n\t0x0c,\t\t\t// 01,0011\t// green\n\t0x06,\t\t\t// 01,0100\t// blue\n\t0x06,\t\t\t// 01,0101\t// blue\n\t0x09,\t\t\t// 01,0110\t// orange\n\t0x09,\t\t\t// 01,0111\t// orange\n\t0x0f,\t\t\t// 01,1000\t// white: this and prev are 1\n\t0x0f,\t\t\t// 01,1001\n\t0x0f,\t\t\t// 01,1010\n\t0x0f,\t\t\t// 01,1011\n\t0x0f,\t\t\t// 01,1100\n\t0x0f,\t\t\t// 01,1101\n\t0x0f,\t\t\t// 01,1110\n\t0x0f,\t\t\t// 01,1111\n\n\t0x00,\t\t\t// 10,0000\t// black\n\t0x00,\t\t\t// 10,0001\t// black\n\t0x00,\t\t\t// 10,0010\t// black\n\t0x00,\t\t\t// 10,0011\t// black\n\t0x00,\t\t\t// 10,0100\t// black\n\t0x00,\t\t\t// 10,0101\t// black\n\t0x00,\t\t\t// 10,0110\t// black\n\t0x00,\t\t\t// 10,0111\t// black\n\t0x0c,\t\t\t// 10,1000\t// green\n\t0x0c,\t\t\t// 10,1001\t// green\n\t0x03,\t\t\t// 10,1010\t// purple\n\t0x03,\t\t\t// 10,1011\t// purple\n\t0x09,\t\t\t// 10,1100\t// orange\n\t0x09,\t\t\t// 10,1101\t// orange\n\t0x06,\t\t\t// 10,1110\t// blue\n\t0x06,\t\t\t// 10,1111\t// blue\n\n\t0x0f,\t\t\t// 11,0000\t// white\n\t0x0f,\t\t\t// 11,0001\n\t0x0f,\t\t\t// 11,0010\n\t0x0f,\t\t\t// 11,0011\n\t0x0f,\t\t\t// 11,0100\n\t0x0f,\t\t\t// 11,0101\n\t0x0f,\t\t\t// 11,0110\n\t0x0f,\t\t\t// 11,0111\n\t0x0f,\t\t\t// 11,1000\n\t0x0f,\t\t\t// 11,1001\n\t0x0f,\t\t\t// 11,1010\n\t0x0f,\t\t\t// 11,1011\n\t0x0f,\t\t\t// 11,1100\n\t0x0f,\t\t\t// 11,1101\n\t0x0f,\t\t\t// 11,1110\n\t0x0f\t\t\t// 11,1111\n};\n\nconst int g_screen_index[] = {\n\t\t0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380,\n\t\t0x028, 0x0a8, 0x128, 0x1a8, 0x228, 0x2a8, 0x328, 0x3a8,\n\t\t0x050, 0x0d0, 0x150, 0x1d0, 0x250, 0x2d0, 0x350, 0x3d0,\n\t\t0x078, 0x0f8, 0x178, 0x1f8, 0x278, 0x2f8, 0x378, 0x3f8\n\t\t\t// Last row is for float_bus() during VBL\n};\n\nbyte g_font_array[256][8] = {\n#include \"kegsfont.h\"\n};\n\nvoid\nvideo_set_red_mask(word32 red_mask)\n{\n\tvideo_set_mask_and_shift(red_mask, &g_red_mask, &g_red_left_shift,\n\t\t\t\t\t\t\t&g_red_right_shift);\n}\n\nvoid\nvideo_set_green_mask(word32 green_mask)\n{\n\tvideo_set_mask_and_shift(green_mask, &g_green_mask, &g_green_left_shift,\n\t\t\t\t\t\t\t&g_green_right_shift);\n}\n\nvoid\nvideo_set_blue_mask(word32 blue_mask)\n{\n\tvideo_set_mask_and_shift(blue_mask, &g_blue_mask, &g_blue_left_shift,\n\t\t\t\t\t\t\t&g_blue_right_shift);\n}\n\nvoid\nvideo_set_alpha_mask(word32 alpha_mask)\n{\n\tg_alpha_mask = alpha_mask;\n\tprintf(\"Set g_alpha_mask=%08x\\n\", alpha_mask);\n}\n\nvoid\nvideo_set_mask_and_shift(word32 x_mask, word32 *mask_ptr, int *shift_left_ptr,\n\t\t\t\t\t\tint *shift_right_ptr)\n{\n\tint\tshift;\n\tint\ti;\n\n\t/* Shift until we find first set bit in mask, then remember mask,shift*/\n\n\tshift = 0;\n\tfor(i = 0; i < 32; i++) {\n\t\tif(x_mask & 1) {\n\t\t\t/* we're done! */\n\t\t\tbreak;\n\t\t}\n\t\tx_mask = x_mask >> 1;\n\t\tshift++;\n\t}\n\t*mask_ptr = x_mask;\n\t*shift_left_ptr = shift;\n\t/* Now, calculate shift_right_ptr */\n\tshift = 0;\n\tx_mask |= 1;\t\t// make sure at least one bit is set\n\tfor(i = 0; i < 32; i++) {\n\t\tif(x_mask >= 0x80) {\n\t\t\tbreak;\n\t\t}\n\t\tshift++;\n\t\tx_mask = x_mask << 1;\n\t}\n\n\t*shift_right_ptr = shift;\n}\n\nvoid\nvideo_set_palette()\n{\n\tint\ti;\n\n\tfor(i = 0; i < 16; i++) {\n\t\tvideo_update_color_raw(0, i, g_lores_colors[i]);\n\t\tg_a2palette_1624[i] = g_palette_8to1624[0][i];\n\t}\n}\n\nvoid\nvideo_set_redraw_skip_amt(int amt)\n{\n\tif(g_screen_redraw_skip_amt < amt) {\n\t\tg_screen_redraw_skip_amt = amt;\n\t\tprintf(\"Set g_screen_redraw_skip_amt = %d\\n\", amt);\n\t}\n}\n\nKimage *\nvideo_get_kimage(int win_id)\n{\n\tif(win_id == 0) {\n\t\treturn &g_mainwin_kimage;\n\t}\n\tif(win_id == 1) {\n\t\treturn &g_debugwin_kimage;\n\t}\n\tprintf(\"win_id: %d not supported\\n\", win_id);\n\texit(1);\n}\n\nchar *\nvideo_get_status_ptr(int line)\n{\n\tif(line < MAX_STATUS_LINES) {\n\t\treturn g_status_ptrs[line];\n\t}\n\treturn 0;\n}\n\n#if 0\nint\nvideo_get_x_refresh_needed(Kimage *kimage_ptr)\n{\n\tint\tret;\n\n\tret = kimage_ptr->x_refresh_needed;\n\tkimage_ptr->x_refresh_needed = 0;\n\n\treturn ret;\n}\n#endif\n\nvoid\nvideo_set_x_refresh_needed(Kimage *kimage_ptr, int do_refresh)\n{\n\tkimage_ptr->x_refresh_needed = do_refresh;\n}\n\nint\nvideo_get_active(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->active;\n}\n\nvoid\nvideo_set_active(Kimage *kimage_ptr, int active)\n{\n\tkimage_ptr->active = active;\n\tif(kimage_ptr != &g_mainwin_kimage) {\n\t\tadb_nonmain_check();\n\t}\n}\n\nvoid\nvideo_init(int mdepth, int screen_width, int screen_height, int no_scale_window)\n{\n\tword32\tcol[4];\n\tword32\tval0, val1, val2, val3, next_col, next2_col;\n\tword32\tval, cur_col;\n\tint\ti, j;\n\n// Initialize video system (called one-time only)\n\n\tg_video_no_scale_window = no_scale_window;\n\n\tfor(i = 0; i < 200; i++) {\n\t\tg_a2_line_left_edge[i] = 0;\n\t\tg_a2_line_right_edge[i] = 0;\n\t}\n\tfor(i = 0; i < 200; i++) {\n\t\tg_a2_filt_stat[i] = -1;\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\tg_saved_line_palettes[0][i][j] = (word32)-1;\n\t\t\tg_saved_line_palettes[1][i][j] = (word32)-1;\n\t\t}\n\t}\n\tfor(i = 0; i < 262; i++) {\n\t\tg_cur_border_colors[i] = -1;\n\t}\n\tfor(i = 0; i < 128; i++) {\n\t\tval0 = i;\n\t\tval1 = 0;\n\t\tfor(j = 0; j < 7; j++) {\n\t\t\tval1 = val1 << 2;\n\t\t\tif(val0 & 0x40) {\n\t\t\t\tval1 |= 3;\n\t\t\t}\n\t\t\tval0 = val0 << 1;\n\t\t}\n\t\tg_pixels_widened[i] = val1;\n\t}\n\n\tvid_printf(\"Zeroing out video memory, mdepth:%d\\n\", mdepth);\n\n\tfor(i = 0; i < SLOW_MEM_CH_SIZE; i++) {\n\t\tg_slow_mem_changed[i] = (word32)-1;\n\t\tg_slow_mem_ch2[i] = 0;\n\t}\n\n\t// create g_dhires_convert[] array\n\t// TODO: Look at patent #4786893 for details on VGC and dhr\n\tfor(i = 0; i < 4096; i++) {\n\t\t/* Convert index bits 11:0 where 3:0 is the previous color */\n\t\t/*  and 7:4 is the current color to translate */\n\t\t/*  Bit 4 will be the first pixel displayed on the screen */\n\t\tfor(j = 0; j < 4; j++) {\n\t\t\tcur_col = (i >> (1 + j)) & 0xf;\n\t\t\tnext_col = (i >> (2 + j)) & 0xf;\n\t\t\tnext2_col = (i >> (3 + j)) & 0xf;\n\t\t\tcur_col = (((cur_col << 4) + cur_col) >> (3 - j)) & 0xf;\n\n\t\t\tif((cur_col == 0xf) || (next_col == 0xf) ||\n\t\t\t\t\t\t\t(next2_col == 0xf)) {\n\t\t\t\tcur_col = 0xf;\n\t\t\t\tcol[j] = cur_col;\n\t\t\t} else if((cur_col == 0) || (next_col == 0) ||\n\t\t\t\t\t\t(next2_col == 0)) {\n\t\t\t\tcur_col = 0;\n\t\t\t\tcol[j] = cur_col;\n\t\t\t} else {\n\t\t\t\tcol[j] = cur_col;\n\t\t\t}\n\t\t}\n\t\tif(g_use_dhr140) {\n\t\t\tfor(j = 0; j < 4; j++) {\n\t\t\t\tcol[j] = (i >> 4) & 0xf;\n\t\t\t}\n\t\t}\n\t\tval0 = g_dhires_colors_16[col[0] & 0xf];\n\t\tval1 = g_dhires_colors_16[col[1] & 0xf];\n\t\tval2 = g_dhires_colors_16[col[2] & 0xf];\n\t\tval3 = g_dhires_colors_16[col[3] & 0xf];\n\t\tval = val0 | (val1 << 4) | (val2 << 8) | (val3 << 12);\n\t\tg_dhires_convert[i] = val;\n\t\tif((i == 0x7bc) || (i == 0xfff)) {\n\t\t\t//printf(\"g_dhires_convert[%03x] = %04x\\n\", i, val);\n\t\t}\n\t}\n\n\tvideo_init_kimage(&g_mainwin_kimage, X_A2_WINDOW_WIDTH,\n\t\t\t\tX_A2_WINDOW_HEIGHT + MAX_STATUS_LINES*16 + 2,\n\t\t\t\tscreen_width, screen_height);\n\n\tvideo_init_kimage(&g_debugwin_kimage, 80*8 + 8 + 8, 25*16 + 8 + 8,\n\t\t\t\tscreen_width, screen_height);\n\n\tchange_display_mode(g_cur_dfcyc);\n\tvideo_reset();\n\tg_vid_update_last_line = 0;\n\tg_video_all_stat_pos = 1;\n\tg_video_all_stat[0].cur_all_stat = 0;\n\tg_video_all_stat[0].lines_since_vbl = 0;\n\tg_video_save_all_stat_pos = 0;\n\tg_video_filt_stat_pos = 0;\n\tvideo_update_status_enable(&g_mainwin_kimage);\n\tvideo_update_through_line(262);\n\n\tprintf(\"g_mainwin_kimage created and init'ed\\n\");\n\tfflush(stdout);\n}\n\nint\nvideo_clamp(int value, int min, int max)\n{\n\t// Ensure value is >= min and <= max.  If max <= min, return min\n\tif(value > max) {\n\t\tvalue = max;\n\t}\n\tif(value <= min) {\n\t\tvalue = min;\n\t}\n\treturn value;\n}\n\nvoid\nvideo_init_kimage(Kimage *kimage_ptr, int width, int height,\n\t\t\tint screen_width, int screen_height)\n{\n\tint\tx_width, x_height, a2_height, x_xpos, x_ypos;\n\tint\ti;\n\n\tif(screen_width < width) {\n\t\tscreen_width = width;\n\t}\n\tif(screen_height < height) {\n\t\tscreen_width = height;\n\t}\n\tx_width = width;\n\tx_height = height;\n\ta2_height = height;\n\tx_xpos = 100;\n\tx_ypos = 300;\n\tif(kimage_ptr == &g_mainwin_kimage) {\n\t\tx_width = g_mainwin_width;\n\t\tx_height = g_mainwin_height;\n\t\tx_xpos = g_mainwin_xpos;\n\t\tx_ypos = g_mainwin_ypos;\n\n\t\t// Handle status lines now\n\t\tif(!g_status_enable) {\n\t\t\ta2_height = g_video_act_margin_top + A2_WINDOW_HEIGHT +\n\t\t\t\t\t\tg_video_act_margin_bottom;\n\t\t}\n\t}\n\n\tx_width = video_clamp(x_width, width, screen_width);\n\tx_height = video_clamp(x_height, height, screen_height);\n\tx_xpos = video_clamp(x_xpos, 0, screen_width - 640);\n\tx_ypos = video_clamp(x_ypos, 0, screen_height - 420);\n\n\tkimage_ptr->wptr = (word32 *)calloc(1, width * (height + 2) * 4);\n\t\t// Scaling routines read from line+1, expect it to be 0\n\tkimage_ptr->a2_width_full = width;\n\tkimage_ptr->a2_height_full = height;\n\tkimage_ptr->a2_width = width;\n\tkimage_ptr->a2_height = a2_height;\n\tkimage_ptr->x_width = x_width;\n\tkimage_ptr->x_height = x_height;\n\tkimage_ptr->x_refresh_needed = 1;\n\tkimage_ptr->x_max_width = screen_width;\n\tkimage_ptr->x_max_height = screen_height;\n\tkimage_ptr->x_xpos = x_xpos;\n\tkimage_ptr->x_ypos = x_ypos;\n\tkimage_ptr->active = 0;\n\tkimage_ptr->vbl_of_last_resize = 0;\n\tkimage_ptr->c025_val = 0;\n\t//printf(\"Created window, width:%d x_width:%d height:%d x_height:%d\\n\",\n\t//\twidth, x_width, height, x_height);\n\n\tkimage_ptr->scale_width_to_a2 = 0x10000;\n\tkimage_ptr->scale_width_a2_to_x = 0x10000;\n\tkimage_ptr->scale_height_to_a2 = 0x10000;\n\tkimage_ptr->scale_height_a2_to_x = 0x10000;\n\tkimage_ptr->num_change_rects = 0;\n\tfor(i = 0; i <= MAX_SCALE_SIZE; i++) {\n\t\tkimage_ptr->scale_width[i] = i;\n\t\tkimage_ptr->scale_height[i] = i;\n\t}\n\n\tvideo_update_scale(kimage_ptr, x_width, x_height, 1);\n}\n\nvoid\nshow_a2_line_stuff()\n{\n\tint\tnum, num_filt;\n\tint\ti;\n\n\tfor(i = 0; i < 200; i++) {\n\t\tprintf(\"line: %d: stat: %07x, \"\n\t\t\t\"left_edge:%d, right_edge:%d\\n\",\n\t\t\ti, g_a2_filt_stat[i],\n\t\t\tg_a2_line_left_edge[i],\n\t\t\tg_a2_line_right_edge[i]);\n\t}\n\n\tnum = g_video_all_stat_pos;\n\tnum_filt = g_video_stat_old_pos;\n\tprintf(\"cur_a2_stat:%04x, all_stat_pos:%d, num_filt:%d\\n\",\n\t\t\tg_cur_a2_stat, num, num_filt);\n\tfor(i = 0; i < num; i++) {\n\t\tprintf(\"all_stat[%3d]=%08x stat:%08x\\n\", i,\n\t\t\tg_video_all_stat[i].lines_since_vbl,\n\t\t\tg_video_all_stat[i].cur_all_stat);\n\t}\n\tfor(i = 0; i < num_filt; i++) {\n\t\tprintf(\"filt[%3d]=%08x filt_stat:%08x\\n\", i,\n\t\t\tg_video_filt_stat_old[i].line_bytes,\n\t\t\tg_video_filt_stat_old[i].filt_stat);\n\t}\n}\n\nint\tg_flash_count = 0;\n\nvoid\nvideo_reset()\n{\n\tint\tstat;\n\tint\ti;\n\n\tvoc_reset();\n\n\tstat = ALL_STAT_TEXT | ALL_STAT_ANNUNC3 |\n\t\t\t\t\t(0xf << BIT_ALL_STAT_TEXT_COLOR);\n\tif(g_use_bw_hires) {\n\t\tstat |= ALL_STAT_COLOR_C021;\n\t}\n\tif(g_config_control_panel) {\n\t\t/* Don't update cur_a2_stat when in configuration panel */\n\t\t//g_save_cur_a2_stat = stat;\n\t} else {\n\t\tg_cur_a2_stat = stat;\n\t}\n\n\tfor(i = 0; i < 16; i++) {\n\t\tg_palette_change_cnt[0][i] = 0;\n\t\tg_palette_change_cnt[1][i] = 0;\n\t}\n}\n\nword32\tg_cycs_in_check_input = 0;\n\nvoid\nvideo_update()\n{\n\tint\tdid_video;\n\n\tif(g_fatal_log > 0) {\n\t\t// NOT IMPLEMENTED YET\n\t\t//adb_all_keys_up();\n\t\tclear_fatal_logs();\n\t}\n\tif(g_status_enable != g_status_enable_previous) {\n\t\tg_status_enable_previous = g_status_enable;\n\t\tvideo_update_status_enable(&g_mainwin_kimage);\n\t}\n\n\tdebugger_redraw_screen(&g_debugwin_kimage);\n\tif(g_config_control_panel) {\n\t\treturn;\t\t// Nothing else to do\n\t}\n\n\tg_screen_redraw_skip_count--;\n\tdid_video = 0;\n\tif(g_screen_redraw_skip_count < 0) {\n\t\tdid_video = 1;\n\t\tvideo_copy_changed2();\n\t\tvideo_update_event_line(262);\n\t\tupdate_border_info();\n\t\tg_screen_redraw_skip_count = g_screen_redraw_skip_amt;\n\t}\n\n\t/* update flash */\n\tg_flash_count++;\n\tif(g_flash_count >= 16) {\n\t\tg_flash_count = 0;\n\t\tg_cur_a2_stat ^= ALL_STAT_FLASH_STATE;\n\t\tchange_display_mode(g_cur_dfcyc);\n\t}\n\n\tif(did_video) {\n\t\tg_vid_update_last_line = 0;\n\t\tg_video_all_stat_pos = 1;\n\t\tg_video_all_stat[0].cur_all_stat = g_cur_a2_stat;\n\t\tg_video_all_stat[0].lines_since_vbl = 0;\n\t\tg_video_save_all_stat_pos = 0;\n\t\tg_video_filt_stat_pos = 0;\n\t}\n}\n\nword32\nvideo_all_stat_to_filt_stat(int line, word32 new_all_stat)\n{\n\tword32\tfilt_stat, merge_mask, mix_t_gr;\n\n\tfilt_stat = new_all_stat & ALL_STAT_TEXT;\n\tmerge_mask = 0;\n\tif((new_all_stat & ALL_STAT_ST80) == 0) {\n\t\tmerge_mask = ALL_STAT_PAGE2;\n\t}\n\tmix_t_gr = new_all_stat & ALL_STAT_MIX_T_GR;\n\tif(new_all_stat & ALL_STAT_SUPER_HIRES) {\n\t\tfilt_stat = ALL_STAT_SUPER_HIRES;\n\t\tmerge_mask = ALL_STAT_VOC_INTERLACE | ALL_STAT_VOC_MAIN;\n\t} else if(line >= 192) {\n\t\tfilt_stat = ALL_STAT_BORDER;\n\t} else if(filt_stat || (line >= 160 && mix_t_gr)) {\n\t\t// text mode\n\t\tfilt_stat |= ALL_STAT_TEXT;\n\t\tmerge_mask |= ALL_STAT_ALTCHARSET | ALL_STAT_BG_COLOR |\n\t\t\t\t\tALL_STAT_TEXT_COLOR | ALL_STAT_VID80;\n\t\tif((new_all_stat & ALL_STAT_ALTCHARSET) == 0) {\n\t\t\tmerge_mask |= ALL_STAT_FLASH_STATE;\n\t\t}\n\t} else {\n\t\t// GR or Hires\n\t\tmerge_mask |= ALL_STAT_ANNUNC3 | ALL_STAT_HIRES;\n\t\tif((new_all_stat & ALL_STAT_ANNUNC3) == 0) {\n\t\t\t// AN3 must be 0 to enable dbl-lores or dbl-hires\n\t\t\tmerge_mask |= ALL_STAT_VID80;\n\t\t}\n\t\tif(new_all_stat & ALL_STAT_HIRES) {\n\t\t\tmerge_mask |= ALL_STAT_COLOR_C021 |\n\t\t\t\t\t\tALL_STAT_DIS_COLOR_DHIRES;\n\t\t}\n\t}\n\tfilt_stat = filt_stat | (new_all_stat & merge_mask);\n\treturn filt_stat;\n}\n\nvoid\nchange_display_mode(dword64 dfcyc)\n{\n\tword32\tlines_since_vbl;\n\n\tlines_since_vbl = get_lines_since_vbl(dfcyc);\n\tvideo_add_new_all_stat(dfcyc, lines_since_vbl);\n\n}\n\nvoid\nvideo_add_new_all_stat(dword64 dfcyc, word32 lines_since_vbl)\n{\n\tword32\tmy_start, first_start, prev_lines_since_vbl;\n\tint\tpos, prev;\n\n\tpos = g_video_all_stat_pos;\n\tmy_start = lines_since_vbl & 0x1ff00;\n\tfirst_start = my_start + 24;\n\tif(lines_since_vbl >= (200 << 8)) {\n\t\treturn;\t\t\t// In VBL, don't log this\n\t}\n\tif(pos && (lines_since_vbl < first_start)) {\n\t\tprev = pos - 1;\n\t\tprev_lines_since_vbl = g_video_all_stat[prev].lines_since_vbl;\n\t\t// If the previous toggle has the same line, and it is before\n\t\t//  offset 24, then ignore it and overwrite it\n\t\tif((my_start <= prev_lines_since_vbl) &&\n\t\t\t\t\t(prev_lines_since_vbl < first_start)) {\n\t\t\t// needless toggling during HBL, just toss earlier\n\t\t\tpos = prev;\n\t\t}\n\t}\n\tg_video_all_stat[pos].lines_since_vbl = lines_since_vbl;\n\tg_video_all_stat[pos].cur_all_stat = g_cur_a2_stat;\n\tif(!g_halt_sim || g_config_control_panel) {\n\t\tdbg_log_info(dfcyc, g_cur_a2_stat, lines_since_vbl,\n\t\t\t\t\t\t\t(pos << 16) | 0x102);\n\t}\n\tpos++;\n\tif(pos >= MAX_VIDEO_ALL_STAT) {\n\t\tpos--;\n\t}\n\tg_video_all_stat_pos = pos;\n}\n\n#define MAX_BORDER_CHANGES\t16384\n\nSTRUCT(Border_changes) {\n\tword32\tusec;\n\tint\tval;\n};\n\nBorder_changes g_border_changes[MAX_BORDER_CHANGES];\nint\tg_num_border_changes = 0;\n\nvoid\nchange_border_color(dword64 dfcyc, int val)\n{\n\tint\tpos;\n\n\tpos = g_num_border_changes;\n\tg_border_changes[pos].usec = (word32)((dfcyc - g_last_vbl_dfcyc) >> 16);\n\tg_border_changes[pos].val = val;\n\n\tpos++;\n\tg_num_border_changes = pos;\n\n\tif(pos >= MAX_BORDER_CHANGES) {\n\t\thalt_printf(\"num border changes: %d\\n\", pos);\n\t\tg_num_border_changes = 0;\n\t}\n}\n\nvoid\nupdate_border_info()\n{\n\tdword64\tdrecip_usec, dline;\n\tword32\tusec;\n\tint\toffset, new_line_offset, last_line_offset, new_line, new_val;\n\tint\tlimit, color_now;\n\tint\ti;\n\n\t/* to get this routine to redraw the border, change */\n\t/*  g_vbl_border_color, set g_border_last_vbl_changes = 1 */\n\t/*  and change the cur_border_colors[] array */\n\n\tcolor_now = g_vbl_border_color;\n\n\tdrecip_usec = (65536LL * 65536LL) / 65;\n\tlimit = g_num_border_changes;\n\tif(g_border_last_vbl_changes || limit || g_border_reparse) {\n\t\t/* add a dummy entry */\n\t\tg_border_changes[limit].usec = CYCLES_IN_16MS_RAW + 21;\n\t\tg_border_changes[limit].val = (g_c034_val & 0xf);\n\t\tlimit++;\n\t}\n\tlast_line_offset = (((word32)-1L) << 8) + 44;\n\tfor(i = 0; i < limit; i++) {\n\t\tusec = g_border_changes[i].usec;\n\t\tdline = usec * drecip_usec;\n\t\tnew_line = dline >> 32;\n\t\toffset = ((dword64)(word32)dline * 65ULL) >> 32;\n\n\t\t/* here comes the tricky part */\n\t\t/* offset is from 0 to 65, where 0-3 is the right border of */\n\t\t/*  the previous line, 4-20 is horiz blanking, 21-24 is the */\n\t\t/*  left border and 25-64 is the main window */\n\t\t/* Convert this to a new notation which is 0-3 is the left */\n\t\t/*  border, 4-43 is the main window, and 44-47 is the right */\n\t\t/* basically, add -21 to offset, and wrap < 0 to previous ln */\n\t\t/* note this makes line -1 offset 44-47 the left hand border */\n\t\t/* for true line 261 on the screen */\n\t\toffset -= 21;\n\t\tif(offset < 0) {\n\t\t\tnew_line--;\n\t\t\toffset += 64;\n\t\t}\n\t\tnew_val = g_border_changes[i].val;\n\t\tnew_line_offset = (new_line << 8) + offset;\n\n\t\tif((new_line_offset < -256) ||\n\t\t\t\t\t(new_line_offset > (262*256 + 0x80))) {\n\t\t\tprintf(\"new_line_offset: %05x\\n\", new_line_offset);\n\t\t\tnew_line_offset = last_line_offset;\n\t\t}\n\t\twhile(last_line_offset < new_line_offset) {\n\t\t\t/* see if this will finish it */\n\t\t\tif((last_line_offset & -256)==(new_line_offset & -256)){\n\t\t\t\tupdate_border_line(last_line_offset,\n\t\t\t\t\t\tnew_line_offset, color_now);\n\t\t\t\tlast_line_offset = new_line_offset;\n\t\t\t} else {\n\t\t\t\tupdate_border_line(last_line_offset,\n\t\t\t\t\t\t(last_line_offset & -256) + 65,\n\t\t\t\t\t\tcolor_now);\n\t\t\t\tlast_line_offset =(last_line_offset & -256)+256;\n\t\t\t}\n\t\t}\n\n\t\tcolor_now = new_val;\n\t}\n\n#if 0\n\tif(g_num_border_changes) {\n\t\tprintf(\"Border changes: %d\\n\", g_num_border_changes);\n\t}\n#endif\n\n\tif(limit > 1) {\n\t\tg_border_last_vbl_changes = 1;\n\t} else {\n\t\tg_border_last_vbl_changes = 0;\n\t}\n\n\tg_num_border_changes = 0;\n\tg_border_reparse = 0;\n\tg_vbl_border_color = (g_c034_val & 0xf);\n}\n\nvoid\nupdate_border_line(int st_line_offset, int end_line_offset, int color)\n{\n\tword32\tfilt_stat;\n\tint\tst_offset, end_offset, left, right, width, line;\n\n\tline = st_line_offset >> 8;\n\tif(line != (end_line_offset >> 8)) {\n\t\thalt_printf(\"ubl, %04x %04x %02x!\\n\", st_line_offset,\n\t\t\t\t\tend_line_offset, color);\n\t}\n\tif(line < -1 || line >= 262) {\n\t\thalt_printf(\"ubl-b, mod line is %d\\n\", line);\n\t\tline = 0;\n\t}\n\tif(line < 0 || line >= 262) {\n\t\tline = 0;\n\t}\n\n\tst_offset = st_line_offset & 0xff;\n\tend_offset = end_line_offset & 0xff;\n\n\tif((st_offset == 0) && (end_offset >= 0x41) && !g_border_reparse) {\n\t\t/* might be the same as last time, save some work */\n\t\tif(g_cur_border_colors[line] == color) {\n\t\t\treturn;\n\t\t}\n\t\tg_cur_border_colors[line] = color;\n\t} else {\n\t\tg_cur_border_colors[line] = -1;\n\t}\n\n\t/* 0-3: left border, 4-43: main window, 44-47: right border */\n\t/* 48-65: horiz blanking */\n\t/* first, do the sides from line 0 to line 199 */\n\tif((line < 200) || (line >= 262)) {\n\t\tif(line >= 262) {\n\t\t\tline = 0;\n\t\t}\n\t\tif(st_offset < 4) {\n\t\t\t/* left side */\n\t\t\tleft = st_offset;\n\t\t\tright = MY_MIN(4, end_offset);\n\t\t\tvideo_border_pixel_write(&g_mainwin_kimage,\n\t\t\t\tg_video_act_margin_top + 2*line, 2, color,\n\t\t\t\t(left * BORDER_WIDTH)/4,\n\t\t\t\t(right * BORDER_WIDTH) / 4);\n\n\t\t\tg_border_sides_refresh_needed = 1;\n\t\t}\n\t\tif((st_offset < 48) && (end_offset >= 44)) {\n\t\t\t/* right side */\n\t\t\tfilt_stat = g_a2_filt_stat[line];\n\t\t\twidth = BORDER_WIDTH;\n\t\t\tif((filt_stat & ALL_STAT_SUPER_HIRES) == 0) {\n\t\t\t\twidth += 80;\n\t\t\t}\n\t\t\tleft = MY_MAX(0, st_offset - 44);\n\t\t\tright = MY_MIN(4, end_offset - 44);\n\t\t\tvideo_border_pixel_write(&g_mainwin_kimage,\n\t\t\t\tg_video_act_margin_top + 2*line, 2, color,\n\t\t\t\tX_A2_WINDOW_WIDTH - width +\n\t\t\t\t\t\t(left * width/4),\n\t\t\t\tX_A2_WINDOW_WIDTH - width +\n\t\t\t\t\t\t(right * width/4));\n\t\t\tg_border_sides_refresh_needed = 1;\n\t\t}\n\t}\n\n\tif((line >= 192) && (line < 200)) {\n\t\tfilt_stat = g_a2_filt_stat[line];\n\t\tif((filt_stat & ALL_STAT_BORDER) && (st_offset < 44) &&\n\t\t\t\t\t\t\t(end_offset > 4)) {\n\t\t\tleft = MY_MAX(0, st_offset - 4);\n\t\t\tright = MY_MIN(40, end_offset - 4);\n\t\t\tvideo_border_pixel_write(&g_mainwin_kimage,\n\t\t\t\tg_video_act_margin_top + 2*line, 2, color,\n\t\t\t\tg_video_act_margin_left + (left * 640 / 40),\n\t\t\t\tg_video_act_margin_left + (right * 640 / 40));\n\t\t\tg_border_line24_refresh_needed = 1;\n\t\t}\n\t}\n\n\t/* now do the bottom, lines 200 to 215 */\n\tif((line >= 200) && (line < (200 + BASE_MARGIN_BOTTOM/2)) ) {\n\t\tline -= 200;\n\t\tleft = st_offset;\n\t\tright = MY_MIN(48, end_offset);\n\t\tvideo_border_pixel_write(&g_mainwin_kimage,\n\t\t\tg_video_act_margin_top + 200*2 + 2*line, 2,\n\t\t\tcolor,\n\t\t\t(left * X_A2_WINDOW_WIDTH / 48),\n\t\t\t(right * X_A2_WINDOW_WIDTH / 48));\n\t\tg_border_special_refresh_needed = 1;\n\t}\n\n\t/* and top, lines 236 to 262 */\n\tif((line >= (262 - BASE_MARGIN_TOP/2)) && (line < 262)) {\n\t\tline -= (262 - BASE_MARGIN_TOP/2);\n\t\tleft = st_offset;\n\t\tright = MY_MIN(48, end_offset);\n\t\tvideo_border_pixel_write(&g_mainwin_kimage, 2*line, 2, color,\n\t\t\t(left * X_A2_WINDOW_WIDTH / 48),\n\t\t\t(right * X_A2_WINDOW_WIDTH / 48));\n\t\tg_border_special_refresh_needed = 1;\n\t}\n}\n\nvoid\nvideo_border_pixel_write(Kimage *kimage_ptr, int starty, int num_lines,\n\t\t\tint color, int st_off, int end_off)\n{\n\tword32\t*wptr, *wptr0;\n\tword32\tpixel;\n\tint\twidth, width_full, offset;\n\tint\ti, j;\n\n\tif(end_off <= st_off) {\n\t\treturn;\n\t}\n\n\twidth = end_off - st_off;\n\twidth_full = kimage_ptr->a2_width_full;\n\n\tif(width > width_full) {\n\t\thalt_printf(\"border write but width %d > act %d\\n\", width,\n\t\t\t\twidth_full);\n\t\treturn;\n\t}\n\tif((starty + num_lines) > kimage_ptr->a2_height) {\n\t\thalt_printf(\"border write line %d, > act %d\\n\",\n\t\t\t\tstarty+num_lines, kimage_ptr->a2_height);\n\t\treturn;\n\t}\n\tpixel = g_a2palette_1624[color & 0xf];\n\n\toffset = starty * width_full;\n\twptr0 = kimage_ptr->wptr;\n\twptr0 += offset;\n\tfor(i = 0; i < num_lines; i++) {\n\t\twptr = wptr0 + st_off;\n\t\tfor(j = 0; j < width; j++) {\n\t\t\t*wptr++ = pixel;\n\t\t}\n\t\twptr0 += width_full;\n\t}\n}\n\nword32\nvideo_get_ch_mask(word32 mem_ptr, word32 filt_stat, int reparse)\n{\n\tword32\tch_mask, mask;\n\tint\tshift;\n\n\tif(reparse) {\n\t\treturn (word32)-1;\n\t}\n\tshift = (mem_ptr >> SHIFT_PER_CHANGE) & 0x1f;\n\tmask = (1 << (40 >> SHIFT_PER_CHANGE)) - 1;\n\tch_mask = g_slow_mem_changed[mem_ptr >> CHANGE_SHIFT] |\n\t\t\tg_slow_mem_ch2[mem_ptr >> CHANGE_SHIFT];\n\tif(filt_stat & ALL_STAT_VID80) {\n\t\tmem_ptr += 0x10000;\n\t\tch_mask |= (g_slow_mem_changed[mem_ptr >> CHANGE_SHIFT]);\n\t\tch_mask |= (g_slow_mem_ch2[mem_ptr >> CHANGE_SHIFT]);\n\t}\n\tch_mask = (ch_mask >> shift) & mask;\n\n\treturn ch_mask;\n}\n\nvoid\nvideo_update_edges(int line, int left, int right, const char *str)\n{\n\tg_a2_line_left_edge[line] = MY_MIN(left, g_a2_line_left_edge[line]);\n\tg_a2_line_right_edge[line] = MY_MAX(right, g_a2_line_right_edge[line]);\n\n\tif((left < 0) || (right < 0) || (left > 640) || (right > 640)) {\n\t\tprintf(\"video_update_edges: %s: line %d: %d (left) >= %d \"\n\t\t\t\"(right)\\n\", str, line, left, right);\n\t}\n}\n\nvoid\nredraw_changed_text(word32 line_bytes, int reparse, word32 *in_wptr,\n\t\t\t\tint pixels_per_line, word32 filt_stat)\n{\n\tbyte\tstr_buf[81];\n\tbyte\t*slow_mem_ptr;\n\tword32\tch_mask, line_mask, mem_ptr, val0, val1, bg_pixel, fg_pixel;\n\tint\tflash_state, y, bg_color, fg_color, start_line;\n\tint\tx1, x2;\n\n\t// Redraws a single line, will be called over 8 lines to finish a byte.\n\n\tstart_line = line_bytes >> 16;\n\tbg_color = (filt_stat >> BIT_ALL_STAT_BG_COLOR) & 0xf;\n\tfg_color = (filt_stat >> BIT_ALL_STAT_TEXT_COLOR) & 0xf;\n\tbg_pixel = g_a2palette_1624[bg_color];\n\tfg_pixel = g_a2palette_1624[fg_color];\n\n\ty = start_line >> 3;\n\tline_mask = 1 << y;\n\tmem_ptr = 0x400 + g_screen_index[y];\n\tif(filt_stat & ALL_STAT_PAGE2) {\n\t\tmem_ptr += 0x400;\n\t}\n\tif((mem_ptr < 0x400) || (mem_ptr >= 0xc00)) {\n\t\thalt_printf(\"redraw_changed_text: mem_ptr: %08x, y:%d\\n\",\n\t\t\t\t\t\t\t\tmem_ptr, y);\n\t\treturn;\n\t}\n\n\tch_mask = video_get_ch_mask(mem_ptr, filt_stat, reparse);\n\tif(!ch_mask) {\n\t\treturn;\n\t}\n\n\tg_a2_screen_buffer_changed |= line_mask;\n\tx2 = 0;\n\tslow_mem_ptr = &(g_slow_memory_ptr[mem_ptr]);\n\tflash_state = -0x40;\n\tif(g_cur_a2_stat & ALL_STAT_FLASH_STATE) {\n\t\tflash_state = 0x40;\n\t}\n\n\tfor(x1 = 0; x1 < 40; x1++) {\n\t\tval0 = slow_mem_ptr[0x10000];\n\t\tval1 = *slow_mem_ptr++;\n\t\tif(!(filt_stat & ALL_STAT_ALTCHARSET)) {\n\t\t\tif((val0 >= 0x40) && (val0 < 0x80)) {\n\t\t\t\tval0 += flash_state;\n\t\t\t}\n\t\t\tif((val1 >= 0x40) && (val1 < 0x80)) {\n\t\t\t\tval1 += flash_state;\n\t\t\t}\n\t\t}\n\t\tif(filt_stat & ALL_STAT_VID80) {\n\t\t\tstr_buf[x2++] = val0;\t\t// aux mem\n\t\t}\n\t\tstr_buf[x2++] = val1;\t\t\t// main mem\n\t}\n\tstr_buf[x2] = 0;\t\t\t\t// null terminate\n\n\tredraw_changed_string(&str_buf[0], line_bytes, ch_mask, in_wptr,\n\t\tbg_pixel, fg_pixel, pixels_per_line,\n\t\t(filt_stat & ALL_STAT_VID80));\n}\n\nvoid\nredraw_changed_string(const byte *bptr, word32 line_bytes, word32 ch_mask,\n\tword32 *in_wptr, word32 bg_pixel,\n\tword32 fg_pixel, int pixels_per_line, int dbl)\n{\n\tregister word32 start_time, end_time;\n\tword32\t*wptr;\n\tword32\tval0, val1, val2, val3, pixel;\n\tint\tleft, right, st_line_mod8, offset, pos, shift, start_line;\n\tint\tstart_byte, end_byte;\n\tint\tx1, j;\n\n\tleft = 40;\n\tright = 0;\n\n\tGET_ITIMER(start_time);\n\n\tstart_line = line_bytes >> 16;\n\tstart_byte = line_bytes & 0x3f;\n\tend_byte = (line_bytes >> 8) & 0x3f;\n\tst_line_mod8 = start_line & 7;\n\n\tfor(x1 = start_byte; x1 < end_byte; x1++) {\n\t\tshift = x1 >> SHIFT_PER_CHANGE;\n\t\tif(((ch_mask >> shift) & 1) == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tleft = MY_MIN(x1, left);\n\t\tright = MY_MAX(x1 + 1, right);\n\t\toffset = (start_line * 2 * pixels_per_line) + x1*14;\n\t\tpos = x1;\n\t\tif(dbl) {\n\t\t\tpos = pos * 2;\n\t\t}\n\n\t\twptr = in_wptr + offset;\n\n\t\tval0 = bptr[pos];\n\t\tif(dbl) {\n\t\t\tpos++;\n\t\t}\n\t\tval1 = bptr[pos++];\n\t\tval2 = g_a2font_bits[val0][st_line_mod8];\n\t\tval3 = g_a2font_bits[val1][st_line_mod8];\n\t\t\t// val2, [6:0] is 80-column character bits, and\n\t\t\t//  [21:8] are the 40-column char bits (double-wide)\n\t\tif(dbl) {\n\t\t\tval2 = (val3 << 7) | (val2 & 0x7f);\n\t\t} else {\n\t\t\tval2 = val3 >> 8;\t// 40-column format\n\t\t}\n\t\tfor(j = 0; j < 14; j++) {\n\t\t\tpixel = bg_pixel;\n\t\t\tif(val2 & 1) {\t\t// LSB is first pixel\n\t\t\t\tpixel = fg_pixel;\n\t\t\t}\n\t\t\twptr[pixels_per_line] = pixel;\n\t\t\t*wptr++ = pixel;\n\t\t\tval2 = val2 >> 1;\n\t\t}\n\t}\n\tGET_ITIMER(end_time);\n\tif(start_line < 200) {\n\t\tvideo_update_edges(start_line, left * 14, right * 14, \"text\");\n\t}\n\n\tif((left >= right) || (left < 0) || (right < 0)) {\n\t\tprintf(\"str line %d, 40: left >= right: %d >= %d\\n\",\n\t\t\tstart_line, left, right);\n\t\tprintf(\" line_bytes:%08x ch_mask:%08x\\n\", line_bytes, ch_mask);\n\t}\n\n\tg_cycs_in_40col += (end_time - start_time);\n}\n\n// gr with an3=0:\n// 0=0\n// 1,0=3 (purple). 1,1=0\n// 2,0=c (green). 2,1=0\n// 3,0=f (white). 3,1=0\n// 4,0=0. 4,1=c (green)\n// 5,0=3 (purple). 5,1=c (green)\n// 6,0=c (green). 6,1=c (green)\n// 7,0=f (white). 7,1=c (green)\n// 8,0=0 (black). 7,1=3 (purple)\n// 9,0=3 (purple). 9,1=3 (purple)\n// a,0=c (green). a,1=3 (purple)\n// b,0=f (white). b,1=3 (purple)\n// c,0=0 (black). c,1=f (white)\n// d,0=3 (purple). d,1=f (white)\n// e,0=c (green). e,1=f (white)\n// f,0=f (white). e,1=f (white)\n\nvoid\nredraw_changed_gr(word32 line_bytes, int reparse, word32 *in_wptr,\n\t\t\t\tint pixels_per_line, word32 filt_stat)\n{\n\tword32\t*wptr;\n\tbyte\t*slow_mem_ptr;\n\tword32\tline_mask, mem_ptr, val0, val1, pixel0, pixel1, ch_mask;\n\tint\ty, shift, left, right, st_line_mod8, start_line, offset;\n\tint\tstart_byte, end_byte;\n\tint\tx1, i;\n\n\tstart_line = line_bytes >> 16;\n\tst_line_mod8 = start_line & 7;\n\n\ty = start_line >> 3;\n\tline_mask = 1 << (y);\n\tmem_ptr = 0x400 + g_screen_index[y];\n\tif(filt_stat & ALL_STAT_PAGE2) {\n\t\tmem_ptr += 0x400;\n\t}\n\tif((mem_ptr < 0x400) || (mem_ptr >= 0xc00)) {\n\t\thalt_printf(\"redraw_changed_gr: mem_ptr: %08x, y:%d\\n\",\n\t\t\t\t\t\t\tmem_ptr, y);\n\t\treturn;\n\t}\n\n\tch_mask = video_get_ch_mask(mem_ptr, filt_stat, reparse);\n\tif(!ch_mask) {\n\t\treturn;\n\t}\n\n\tg_a2_screen_buffer_changed |= line_mask;\n\n\tleft = 40;\n\tright = 0;\n\n\tslow_mem_ptr = &(g_slow_memory_ptr[mem_ptr]);\n\toffset = (start_line * 2 * pixels_per_line);\n\tstart_byte = line_bytes & 0x3f;\n\tend_byte = (line_bytes >> 8) & 0x3f;\n\tfor(x1 = start_byte; x1 < end_byte; x1++) {\n\t\tshift = x1 >> SHIFT_PER_CHANGE;\n\t\tif(((ch_mask >> shift) & 1) == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tleft = MY_MIN(x1, left);\n\t\tright = MY_MAX(x1 + 1, right);\n\n\t\twptr = in_wptr + offset + x1*14;\n\n\t\tval0 = slow_mem_ptr[0x10000 + x1];\n\t\tval1 = slow_mem_ptr[x1];\n\n\t\tif(st_line_mod8 >= 4) {\n\t\t\tval0 = val0 >> 4;\n\t\t\tval1 = val1 >> 4;\n\t\t}\n\t\tif(filt_stat & ALL_STAT_VID80) {\n\t\t\t// aux pixel is { [2:0],[3] }\n\t\t\tval0 = (val0 << 1) | ((val0 >> 3) & 1);\n\t\t} else if((filt_stat & ALL_STAT_ANNUNC3) == 0) {\n\t\t\tif(x1 & 1) {\t\t\t// odd cols\n\t\t\t\tval0 = ((val1 >> 1) & 2) | ((val1 >> 3) & 1);\n\t\t\t} else {\n\t\t\t\tval0 = val1 & 3;\t// even cols\n\t\t\t}\n\t\t\t// map val0: 0->0, 1->3, 2->c, 3->f\n\t\t\tval1 = 0;\n\t\t\tif(val0 & 1) {\n\t\t\t\tval1 |= 3;\n\t\t\t}\n\t\t\tif(val0 & 2) {\n\t\t\t\tval1 |= 0xc;\n\t\t\t}\n\t\t\tval0 = val1;\n\t\t} else {\n\t\t\tval0 = val1;\n\t\t}\n\t\tpixel0 = g_a2palette_1624[val0 & 0xf];\n\t\tpixel1 = g_a2palette_1624[val1 & 0xf];\n\t\tfor(i = 0; i < 7; i++) {\n\t\t\twptr[pixels_per_line] = pixel0;\n\t\t\twptr[pixels_per_line + 7] = pixel1;\n\t\t\twptr[0] = pixel0;\n\t\t\twptr[7] = pixel1;\n\t\t\twptr++;\n\t\t}\n\t}\n\n\tvideo_update_edges(start_line, left * 14, right * 14, \"gr\");\n}\n\nvoid\nvideo_hgr_line_segment(byte *slow_mem_ptr, word32 *wptr, int start_byte,\n\t\tint end_byte, int pixels_per_line, word32 filt_stat)\n{\n\tword32\tval0, val1, val2, prev_bits, val1_hi, dbl_step, pixel, color;\n\tword32\tmonochrome;\n\tint\tshift;\n\tint\tx2, i;\n\n\tmonochrome = filt_stat & (ALL_STAT_COLOR_C021 |\n\t\t\t\t\t\tALL_STAT_DIS_COLOR_DHIRES);\n\n\tprev_bits = 0;\n\tif(start_byte) {\n\t\tprev_bits = (slow_mem_ptr[-1] >> 3) & 0xf;\n\t\tif(!(filt_stat & ALL_STAT_VID80)) {\n\t\t\t// prev_bits is 4 bits, widen to 8 for std HGR\n\t\t\tprev_bits = g_pixels_widened[prev_bits] >> 4;\n\t\t}\n\t\tprev_bits = prev_bits & 0xf;\n\t}\n\tfor(x2 = start_byte; x2 < end_byte; x2++) {\n\t\tval0 = slow_mem_ptr[0x10000];\n\t\tval1 = *slow_mem_ptr++;\n\t\tval2 = slow_mem_ptr[0x10000];\t// next pixel, aux mem\n\t\tif(x2 >= 39) {\n\t\t\tval2 = 0;\n\t\t}\n\t\tval1_hi = ((val1 >> 5) & 4) | ((x2 & 1) << 1);\n\t\t\t// Hi-order bit in bit 2, odd pixel is in bit 0\n\n\t\tdbl_step = 3;\n\t\tif(filt_stat & ALL_STAT_VID80) {\n\t\t\t// aux+1[6:0], main[6:0], aux[6:0], prev[3:0]\n\t\t\tval0 = (val2 << 18) | ((val1 & 0x7f) << 11) |\n\t\t\t\t\t\t\t((val0 & 0x7f) << 4);\n\t\t\tif(!monochrome && (x2 & 1)) {\t// Get 6 bits from prev\n\t\t\t\tval0 = (val0 << 2);\n\t\t\t\tdbl_step = 1;\n\t\t\t}\n\t\t\tval0 = val0 | prev_bits;\n\t\t} else if(monochrome) {\n\t\t\tval0 = g_pixels_widened[val1 & 0x7f] << 4;\n\t\t} else {\t\t\t// color, normal hgr\n\t\t\tval2 = g_pixels_widened[*slow_mem_ptr & 0x7f];\n\t\t\tif(x2 >= 39) {\n\t\t\t\tval2 = 0;\n\t\t\t}\n\t\t\tval0 = ((val1 & 0x7f) << 4) | prev_bits | (val2 << 11);\n\t\t\tif((filt_stat & ALL_STAT_ANNUNC3) == 0) {\n\t\t\t\tval1_hi = val1_hi & 3;\n\t\t\t}\n\t\t}\n#if 0\n\t\tif(st_line < 8) {\n\t\t\tprintf(\"hgrl %d c:%d,d:%d, off:%03x val0:%02x 1:%02x\\n\",\n\t\t\t\tst_line, monochrome, dbl, x1 + x2, val0, val1);\n\t\t}\n#endif\n\t\tfor(i = 0; i < 14; i++) {\n\t\t\tcolor = 0;\t\t\t// black\n\t\t\tif(monochrome) {\n\t\t\t\tif(val0 & 0x10) {\n\t\t\t\t\tcolor = 0xf;\t// white\n\t\t\t\t}\n\t\t\t\tval0 = val0 >> 1;\n\t\t\t} else {\t\t\t// color\n\t\t\t\tif(filt_stat & ALL_STAT_VID80) {\n\t\t\t\t\tcolor = g_dhires_convert[val0 & 0xfff];\n\t\t\t\t\tshift = (x2 + x2 + i) & 3;\n\t\t\t\t\tcolor = color >> (4 * shift);\n\t\t\t\t\tif((i & 3) == dbl_step) {\n\t\t\t\t\t\tval0 = val0 >> 4;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tval2 = (val0 & 0x38) ^ val1_hi ^(i & 3);\n\t\t\t\t\tcolor = g_hires_lookup[val2 & 0x7f];\n\t\t\t\t\tif(i & 1) {\n\t\t\t\t\t\tval0 = val0 >> 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tpixel = g_a2palette_1624[color & 0xf];\n\t\t\twptr[pixels_per_line] = pixel;\n\t\t\t*wptr++ = pixel;\n\t\t}\n\t\tif((filt_stat & ALL_STAT_VID80) && ((x2 & 1) == 0)) {\n\t\t\tprev_bits = val0 & 0x3f;\n\t\t} else {\n\t\t\tprev_bits = val0 & 0xf;\n\t\t}\n\t}\n}\n\nvoid\nredraw_changed_hgr(word32 line_bytes, int reparse,\n\tword32 *in_wptr, int pixels_per_line, word32 filt_stat)\n{\n\tword32\t*wptr;\n\tbyte\t*slow_mem_ptr;\n\tword32\tch_mask, line_mask, mem_ptr;\n\tint\ty, shift, st_line_mod8, start_line, offset, start_byte;\n\tint\tend_byte;\n\tint\tx1;\n\n\tstart_line = line_bytes >> 16;\n\tstart_byte = line_bytes & 0x3f;\n\tend_byte = (line_bytes >> 8) & 0x3f;\t// Usually '40'\n\n\ty = start_line >> 3;\n\tst_line_mod8 = start_line & 7;\n\tline_mask = 1 << y;\n\tmem_ptr = 0x2000 + g_screen_index[y] + (st_line_mod8 * 0x400);\n\tif(filt_stat & ALL_STAT_PAGE2) {\n\t\tmem_ptr += 0x2000;\n\t}\n\tif((mem_ptr < 0x2000) || (mem_ptr >= 0x6000)) {\n\t\thalt_printf(\"redraw_changed_hgr: mem_ptr: %08x, y:%d\\n\",\n\t\t\t\t\t\t\t\tmem_ptr, y);\n\t\treturn;\n\t}\n\n\tch_mask = video_get_ch_mask(mem_ptr, filt_stat, reparse);\n\tif(ch_mask == 0) {\n\t\treturn;\n\t}\n\n\t// Hires depends on adjacent bits, so also reparse adjacent regions\n\t//  to handle redrawing of pixels on the boundaries\n\tch_mask = ch_mask | (ch_mask >> 1) | (ch_mask << 1);\n\n\tg_a2_screen_buffer_changed |= line_mask;\n\n\tfor(x1 = start_byte; x1 < end_byte; x1++) {\n\t\tshift = x1 >> SHIFT_PER_CHANGE;\n\t\tif(((ch_mask >> shift) & 1) == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tslow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]);\n\t\toffset = (start_line * 2 * pixels_per_line) + x1*14;\n\n\t\twptr = in_wptr + offset;\n\t\tvideo_hgr_line_segment(slow_mem_ptr, wptr, x1, end_byte,\n\t\t\t\tpixels_per_line, filt_stat);\n\n\t\tvideo_update_edges(start_line, x1 * 14, end_byte * 14, \"hgr\");\n\t\tbreak;\n\t}\n}\n\nint\nvideo_rebuild_super_hires_palette(int bank, word32 scan_info, int line,\n\t\t\t\t\t\t\t\tint reparse)\n{\n\tword32\t*word_ptr;\n\tbyte\t*byte_ptr;\n\tword32\tch_mask, mem_ptr, scan, old_scan, val0, val1;\n\tint\tdiffs, palette;\n\tint\tj;\n\n\tpalette = scan_info & 0xf;\n\n\tmem_ptr = (bank << 16) + 0x9e00 + (palette * 0x20);\n\tch_mask = video_get_ch_mask(mem_ptr, 0, 0);\n\n\told_scan = g_superhires_scan_save[bank][line];\n\tscan = (scan_info & 0xfaf) +\n\t\t\t\t(g_palette_change_cnt[bank][palette] << 12);\n\tg_superhires_scan_save[bank][line] = scan;\n\n#if 0\n\tif(line == 1) {\n\t\tword_ptr = (word32 *)&(g_slow_memory_ptr[0x19e00+palette*0x20]);\n\t\tprintf(\"y1vrshp, ch:%08x, s:%08x,os:%08x %d = %08x %08x %08x \"\n\t\t\t\"%08x %08x %08x %08x %08x\\n\",\n\t\t\tch_mask, scan, old_scan, reparse,\n\t\t\tword_ptr[0], word_ptr[1], word_ptr[2], word_ptr[3],\n\t\t\tword_ptr[4], word_ptr[5], word_ptr[6], word_ptr[7]);\n\t}\n#endif\n\n\tdiffs = reparse | ((scan ^ old_scan) & 0xf0f);\n\t\t/* we must do full reparse if palette changed for this line */\n\n\tif(!diffs && (ch_mask == 0) && (((scan ^ old_scan) & (~0xf0)) == 0)) {\n\t\t/* nothing changed, get out fast */\n\t\treturn 0;\n\t}\n\n\tif(ch_mask) {\n\t\t/* indicates the palette has changed, and other scan lines */\n\t\t/*  using this palette need to do a full 32-byte compare to */\n\t\t/*  decide if they need to update or not */\n\t\tg_palette_change_cnt[bank][palette]++;\n\t}\n\n\tword_ptr = (word32 *)&(g_slow_memory_ptr[(bank << 16) + 0x9e00 +\n\t\t\t\t\t\t\tpalette*0x20]);\n\tfor(j = 0; j < 8; j++) {\n\t\tif(word_ptr[j] != g_saved_line_palettes[bank][line][j]) {\n\t\t\tdiffs = 1;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(diffs == 0) {\n\t\treturn 0;\n\t}\n\n\t/* first, save this word_ptr into saved_line_palettes */\n\tbyte_ptr = (byte *)word_ptr;\n\tfor(j = 0; j < 8; j++) {\n\t\tg_saved_line_palettes[bank][line][j] = word_ptr[j];\n\t}\n\n\tbyte_ptr = (byte *)word_ptr;\n\t/* this palette has changed */\n\tfor(j = 0; j < 16; j++) {\n\t\tval0 = *byte_ptr++;\n\t\tval1 = *byte_ptr++;\n\t\tvideo_update_color_raw(bank, palette*16 + j, (val1<<8) + val0);\n\t}\n\n\treturn 1;\n}\n\nword32\nredraw_changed_super_hires_oneline(int bank, word32 *in_wptr,\n\t\tint pixels_per_line, int y, int scan, word32 ch_mask)\n{\n\tword32\t*palptr, *wptr;\n\tbyte\t*slow_mem_ptr;\n\tword32\tmem_ptr, val0, pal, pix0, pix1, pix2, pix3, save_pix;\n\tint\toffset, shift_per, left, right, shift;\n\tint\tx1, x2;\n\n\tmem_ptr = (bank << 16) + 0x2000 + (0xa0 * y);\n\n\tshift_per = (1 << SHIFT_PER_CHANGE);\n\tpal = (scan & 0xf);\n\n\tsave_pix = 0;\n\tif(scan & 0x20) {\t\t\t// Fill mode\n\t\tch_mask = (word32)-1;\n\t}\n\n\tpalptr = &(g_palette_8to1624[bank][pal * 16]);\n\tleft = 160;\n\tright = 0;\n\n\tfor(x1 = 0; x1 < 0xa0; x1 += shift_per) {\n\t\tshift = x1 >> SHIFT_PER_CHANGE;\n\t\tif(((ch_mask >> shift) & 1) == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tleft = MY_MIN(x1, left);\n\t\tright = MY_MAX(x1 + shift_per, right);\n\n\t\tslow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]);\n\t\toffset = x1*4;\n\n\t\twptr = in_wptr + offset;\n\n\t\tfor(x2 = 0; x2 < shift_per; x2++) {\n\t\t\tval0 = *slow_mem_ptr++;\n\n\t\t\tif(scan & 0x80) {\t\t// 640 mode\n\t\t\t\tpix0 = (val0 >> 6) & 3;\n\t\t\t\tpix1 = (val0 >> 4) & 3;\n\t\t\t\tpix2 = (val0 >> 2) & 3;\n\t\t\t\tpix3 = val0 & 3;\n\t\t\t\tpix0 = palptr[pix0 + 8];\n\t\t\t\tpix1 = palptr[pix1 + 12];\n\t\t\t\tpix2 = palptr[pix2 + 0];\n\t\t\t\tpix3 = palptr[pix3 + 4];\n\t\t\t} else {\t/* 320 mode */\n\t\t\t\tpix0 = (val0 >> 4);\n\t\t\t\tpix2 = (val0 & 0xf);\n\t\t\t\tif(scan & 0x20) {\t// Fill mode\n\t\t\t\t\tif(!pix0) {\t// 0 = repeat last color\n\t\t\t\t\t\tpix0 = save_pix;\n\t\t\t\t\t}\n\t\t\t\t\tif(!pix2) {\n\t\t\t\t\t\tpix2 = pix0;\n\t\t\t\t\t}\n\t\t\t\t\tsave_pix = pix2;\n\t\t\t\t}\n\t\t\t\tpix0 = palptr[pix0];\n\t\t\t\tpix1 = pix0;\n\t\t\t\tpix2 = palptr[pix2];\n\t\t\t\tpix3 = pix2;\n\t\t\t}\n\t\t\twptr[pixels_per_line] = pix0;\n\t\t\t*wptr++ = pix0;\n\t\t\twptr[pixels_per_line] = pix1;\n\t\t\t*wptr++ = pix1;\n\t\t\twptr[pixels_per_line] = pix2;\n\t\t\t*wptr++ = pix2;\n\t\t\twptr[pixels_per_line] = pix3;\n\t\t\t*wptr++ = pix3;\n\t\t}\n\t}\n\n\treturn (left << 16) | (right & 0xffff);\n}\n\nvoid\nredraw_changed_super_hires_bank(int bank, int start_line, int reparse,\n\t\t\t\t\tword32 *wptr, int pixels_per_line)\n{\n\tdword64\tdval, dval1;\n\tword32\tthis_check, mask, tmp, scan, old_scan, mem_ptr;\n\tint\tleft, right, ret, shift;\n\n\tmem_ptr = (bank << 16) + 0x2000 + (160 * start_line);\n\tdval1 = g_slow_mem_changed[(mem_ptr >> CHANGE_SHIFT) + 1] |\n\t\t\tg_slow_mem_ch2[(mem_ptr >> CHANGE_SHIFT) + 1];\n\tdval = g_slow_mem_changed[mem_ptr >> CHANGE_SHIFT] |\n\t\tg_slow_mem_ch2[mem_ptr >> CHANGE_SHIFT] | (dval1 << 32);\n\tshift = (mem_ptr >> SHIFT_PER_CHANGE) & 0x1f;\n\tmask = (1 << (160 >> SHIFT_PER_CHANGE)) - 1;\n\tthis_check = (dval >> shift) & mask;\n\n\tscan = g_slow_memory_ptr[(bank << 16) + 0x9d00 + start_line];\n\n\told_scan = g_superhires_scan_save[bank][start_line];\n\n\tret = video_rebuild_super_hires_palette(bank, scan, start_line,\n\t\t\t\t\t\t\t\treparse);\n\tif(ret || reparse || ((scan ^ old_scan) & 0xa0)) {\n\t\t\t\t\t/* 0x80 == mode640, 0x20 = fill */\n\t\tthis_check = (word32)-1;\n\t}\n\n\tif(!this_check) {\n\t\treturn;\t\t\t// Nothing to do, get out\n\t}\n\n\tif(scan & 0x80) {\t\t\t// 640 mode\n\t\tg_num_lines_superhires640++;\n\t}\n\n\tif((scan >> 5) & 1) {\t\t// fill mode--redraw whole line\n\t\tthis_check = (word32)-1;\n\t}\n\n\tg_a2_screen_buffer_changed |= (1 << (start_line >> 3));\n\ttmp = redraw_changed_super_hires_oneline(bank, wptr, pixels_per_line,\n\t\t\t\t\t\tstart_line, scan, this_check);\n\tleft = tmp >> 16;\n\tright = tmp & 0xffff;\n\n\tvideo_update_edges(start_line, left * 4, right * 4, \"shr\");\n}\n\nvoid\nredraw_changed_super_hires(word32 line_bytes, int reparse, word32 *wptr,\n\t\t\t\tint pixels_per_line, word32 filt_stat)\n{\n\tint\tbank, start_line;\n\n\tstart_line = line_bytes >> 16;\n\twptr += start_line * 2 * pixels_per_line;\n\n\tif(filt_stat & ALL_STAT_VOC_INTERLACE) {\n\t\t// Do 400 interlaced lines.  Do aux first, then main mem\n\t\tredraw_changed_super_hires_bank(1, start_line, reparse, wptr,\n\t\t\t\t\t\t\t\t\t0);\n\t\tredraw_changed_super_hires_bank(0, start_line, reparse,\n\t\t\t\t\t\twptr + pixels_per_line, 0);\n\t} else {\n\t\tbank = 1;\n\t\tif(filt_stat & ALL_STAT_VOC_MAIN) {\n\t\t\tbank = 0;\t\t// VOC SHR in main memory\n\t\t}\n\t\tredraw_changed_super_hires_bank(bank, start_line, reparse, wptr,\n\t\t\t\t\t\t\tpixels_per_line);\n\t}\n}\n\nvoid\nvideo_copy_changed2()\n{\n\tword32\t*ch_ptr, *ch2_ptr;\n\tint\tbank1_off;\n\tint\ti;\n\n\t// Copy entries from g_slow_mem_changed[] to g_slow_mem_ch2[] and\n\t//  clear g_slow_mem_changed[]\n\tch_ptr = &g_slow_mem_changed[0];\n\tch2_ptr = &g_slow_mem_ch2[0];\n\tbank1_off = 0x10000 >> CHANGE_SHIFT;\n\tfor(i = 4; i < 0xa0; i++) {\t\t// Pages 0x0400 through 0x9fff\n\t\tch2_ptr[i] = ch_ptr[i];\n\t\tch2_ptr[i + bank1_off] = ch_ptr[i + bank1_off];\n\t\tch_ptr[i] = 0;\n\t\tch_ptr[i + bank1_off] = 0;\n\t}\n}\n\nvoid\nvideo_update_event_line(int line)\n{\n\tint\tnew_line;\n\n\tvideo_update_through_line(line);\n\n\tnew_line = line + g_line_ref_amt;\n\tif(new_line < 200) {\n\t\tif(!g_config_control_panel && !g_halt_sim) {\n\t\t\tadd_event_vid_upd(new_line);\n\t\t}\n\t} else if(line >= 262) {\n\t\tif(!g_config_control_panel && !g_halt_sim) {\n\t\t\tadd_event_vid_upd(0);\t/* add event for new screen */\n\t\t}\n\t}\n}\n\nvoid\nvideo_force_reparse()\n{\n\tword32\t*wptr;\n\tint\theight, width_full;\n\tint\ti, j;\n\n\tg_video_stat_old_pos = 1;\n\tg_video_filt_stat_old[0].filt_stat = (word32)-1;\n\theight = g_video_act_margin_top + A2_WINDOW_HEIGHT +\n\t\t\t\t\t\tg_video_act_margin_bottom;\n\theight = MY_MIN(height, g_mainwin_kimage.a2_height);\n\twidth_full = g_mainwin_kimage.a2_width_full;\n\twptr = g_mainwin_kimage.wptr;\n\tfor(i = 0; i < height; i++) {\n\t\tfor(j = 0; j < width_full; j++) {\n\t\t\t*wptr++ = 0;\n\t\t}\n\t}\n\tg_border_reparse = 1;\n}\n\nvoid\nvideo_update_through_line(int line)\n{\n\tregister word32 start_time;\n\tregister word32 end_time;\n\tword32\tmy_start_lines, my_end_lines, prev_all_stat, next_all_stat;\n\tword32\tprev_lines_since_vbl, next_lines_since_vbl;\n\tint\tlast_line, pos, last_pos, end, num;\n\tint\ti;\n\n#if 0\n\tvid_printf(\"\\nvideo_upd for line %d, lines: %06x\\n\", line,\n\t\t\t\tget_lines_since_vbl(g_cur_dfcyc));\n#endif\n\n\tGET_ITIMER(start_time);\n\n\tlast_line = MY_MIN(200, line+1); /* go through line, but not past 200 */\n\n\tpos = g_video_save_all_stat_pos;\n\tlast_pos = g_video_all_stat_pos;\n\tprev_all_stat = g_video_all_stat[pos].cur_all_stat;\n\tprev_lines_since_vbl = g_video_all_stat[pos].lines_since_vbl;\n\tg_video_all_stat[last_pos].cur_all_stat = g_cur_a2_stat;\n\tg_video_all_stat[last_pos].lines_since_vbl = (line + 1) << 8;\n\tnext_all_stat = g_video_all_stat[pos+1].cur_all_stat;\n\tnext_lines_since_vbl = g_video_all_stat[pos+1].lines_since_vbl;\n\tfor(i = g_vid_update_last_line; i < last_line; i++) {\n\t\t// We need to step through pos in g_video_all_stat[] and find\n\t\t//  the start/end pairs for each line\n\t\tg_a2_line_left_edge[i] = 640;\n\t\tg_a2_line_right_edge[i] = 0;\n\t\tmy_start_lines = (i << 8) + 25;\n\t\tmy_end_lines = (i << 8) + 65;\n\t\tif(prev_lines_since_vbl > my_start_lines) {\n\t\t\tprintf(\"prev:%08x > %08x start at i:%d\\n\",\n\t\t\t\tprev_lines_since_vbl, my_start_lines, i);\n\t\t}\n\t\twhile(my_start_lines < my_end_lines) {\n\t\t\twhile(next_lines_since_vbl <= my_start_lines) {\n\t\t\t\t// Step into next entry\n\t\t\t\tprev_all_stat = next_all_stat;\n\t\t\t\tprev_lines_since_vbl = next_lines_since_vbl;\n\t\t\t\tpos++;\n\t\t\t\tg_video_save_all_stat_pos = pos;\n\t\t\t\tnext_all_stat =\n\t\t\t\t\tg_video_all_stat[pos+1].cur_all_stat;\n\t\t\t\tnext_lines_since_vbl =\n\t\t\t\t\tg_video_all_stat[pos+1].lines_since_vbl;\n\t\t\t\tif(pos >= last_pos) {\n\t\t\t\t\tprintf(\"FELL OFF %d %d!\\n\", pos,\n\t\t\t\t\t\t\t\tlast_pos);\n\t\t\t\t\tpos--;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tend = 65;\n\t\t\tif(next_lines_since_vbl < my_end_lines) {\n\t\t\t\tend = (next_lines_since_vbl & 0xff);\n\t\t\t\tif(end < 25) {\n\t\t\t\t\tprintf(\"i:%d next_lines_since_vbl:\"\n\t\t\t\t\t\t\"%08x!\\n\", i,\n\t\t\t\t\t\tnext_lines_since_vbl);\n\t\t\t\t\tend = 25;\n\t\t\t\t}\n\t\t\t}\n\t\t\tvideo_do_partial_line(my_start_lines, end,\n\t\t\t\t\t\t\tprev_all_stat);\n\t\t\tmy_start_lines = (i << 8) + end;\n\t\t}\n\t}\n\n\n\tg_vid_update_last_line = last_line;\n\tg_video_save_all_stat_pos = pos;\n\n\t/* deal with border and forming rects for xdriver.c to use */\n\tif(line >= 262) {\n\t\tif(g_num_lines_prev_superhires != g_num_lines_superhires) {\n\t\t\t/* switched in/out from superhires--refresh borders */\n\t\t\tg_border_sides_refresh_needed = 1;\n\t\t}\n\n\t\tvideo_form_change_rects();\n\t\tg_num_lines_prev_superhires = g_num_lines_superhires;\n\t\tg_num_lines_prev_superhires640 = g_num_lines_superhires640;\n\t\tg_num_lines_superhires = 0;\n\t\tg_num_lines_superhires640 = 0;\n\n\t\tnum = g_video_filt_stat_pos;\n\t\tg_video_stat_old_pos = num;\n\t\tfor(i = 0; i < num; i++) {\n\t\t\tg_video_filt_stat_old[i] = g_video_filt_stat[i];\n\t\t}\n\t\tg_video_filt_stat_pos = 0;\n\t}\n\tGET_ITIMER(end_time);\n\tg_cycs_in_refresh_line += (end_time - start_time);\n}\n\nextern word32 g_vbl_count;\n\nvoid\nvideo_do_partial_line(word32 lines_since_vbl, int end, word32 cur_all_stat)\n{\n\tword32\tfilt_stat, old_filt_stat, line_bytes, old_line_bytes;\n\tint\tpos, old_pos, reparse, line;\n\n\tpos = g_video_filt_stat_pos;\n\told_pos = g_video_stat_old_pos;\n\tfilt_stat = video_all_stat_to_filt_stat(lines_since_vbl >> 8,\n\t\t\t\t\t\t\t\tcur_all_stat);\n\tline_bytes = ((lines_since_vbl & 0x1ff00) << 8) |\n\t\t\t((end - 25) << 8) | ((lines_since_vbl - 25) & 0x3f);\n\tg_video_filt_stat[pos].line_bytes = line_bytes;\n\tg_video_filt_stat[pos].filt_stat = filt_stat;\n\treparse = 1;\n\told_filt_stat = (word32)-1;\n\told_line_bytes = (word32)-1;\n\tif(pos < old_pos) {\n\t\told_filt_stat = g_video_filt_stat_old[pos].filt_stat;\n\t\told_line_bytes = g_video_filt_stat_old[pos].line_bytes;\n\t}\n\tif((old_filt_stat == filt_stat) && (line_bytes == old_line_bytes)) {\n\t\treparse = 0;\n\t} else if((old_filt_stat ^ filt_stat) & ALL_STAT_SUPER_HIRES) {\n\t\tg_border_reparse = 1;\n\t}\n\tvideo_refresh_line(line_bytes, reparse, filt_stat);\n\tline = lines_since_vbl >> 8;\n\tif(line < 200) {\n\t\tg_a2_filt_stat[line] = filt_stat;\n\t} else {\n\t\tprintf(\"partial_line %08x %d %08x out of range!\\n\",\n\t\t\t\t\tlines_since_vbl, end, cur_all_stat);\n\t}\n\tif((end <= 25) || (end < (int)(lines_since_vbl & 0xff))) {\n\t\tprintf(\"Bad lsv:%08x, end:%d, stat:%08x\\n\", lines_since_vbl,\n\t\t\t\t\t\tend, filt_stat);\n\t}\n\tpos++;\n\tif(pos >= MAX_VIDEO_FILT_STAT) {\n\t\tpos--;\n\t}\n\tg_video_filt_stat_pos = pos;\n}\n\nvoid\nvideo_refresh_line(word32 line_bytes, int must_reparse, word32 filt_stat)\n{\n\tword32\t*wptr;\n\tint\tpixels_per_line, offset, line;\n\n\tline = line_bytes >> 16;\n\tif((word32)line >= 200) {\n\t\tprintf(\"video_refresh %08x %d %08x!\\n\", line_bytes,\n\t\t\t\t\t\tmust_reparse, filt_stat);\n\t\treturn;\n\t}\n\twptr = g_mainwin_kimage.wptr;\n\tpixels_per_line = g_mainwin_kimage.a2_width_full;\n\toffset = (pixels_per_line * g_video_act_margin_top) +\n\t\t\t\t\t\tg_video_act_margin_left;\n\twptr = wptr + offset;\n\n\tif(filt_stat & ALL_STAT_SUPER_HIRES) {\n\t\tg_num_lines_superhires++;\n\t\tredraw_changed_super_hires(line_bytes, must_reparse, wptr,\n\t\t\t\t\t\tpixels_per_line, filt_stat);\n\t} else if(filt_stat & ALL_STAT_BORDER) {\n\t\tif(line < 192) {\n\t\t\thalt_printf(\"Border line not 192: %d\\n\", line);\n\t\t}\n\t\tg_a2_line_left_edge[line] = 0;\n\t\tg_a2_line_right_edge[line] = 560;\n\t\tif(g_border_line24_refresh_needed) {\n\t\t\tg_border_line24_refresh_needed = 0;\n\t\t\tg_a2_screen_buffer_changed |= (1 << 24);\n\t\t}\n\t} else if(filt_stat & ALL_STAT_TEXT) {\n\t\tredraw_changed_text(line_bytes, must_reparse, wptr,\n\t\t\t\t\t\tpixels_per_line, filt_stat);\n\t} else if(filt_stat & ALL_STAT_HIRES) {\n\t\tredraw_changed_hgr(line_bytes, must_reparse, wptr,\n\t\t\t\t\t\tpixels_per_line, filt_stat);\n\t} else {\n\t\tredraw_changed_gr(line_bytes, must_reparse, wptr,\n\t\t\t\t\t\tpixels_per_line, filt_stat);\n\t}\n}\n\nvoid\nprepare_a2_font()\n{\n\tword32\tval0, val1, val2;\n\tint\ti, j, k;\n\n\t// Prepare g_a2font_bits[char][line] where each entry indicates the\n\t//  set pixels in this line of the character.  Bits 6:0 are an\n\t//  80-column character, and bits 21:8 are  the 14 expanded bits of a\n\t//  40-columns character, both with the first visible bit at the\n\t//  rightmost bit address.  But g_font_array[] is in big-endian bit\n\t//  order, which is less useful\n\tfor(i = 0; i < 256; i++) {\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\tval0 = g_font_array[i][j] >> 1;\n\t\t\tval1 = 0;\t\t// 80-column bits\n\t\t\tval2 = 0;\t\t// 40-column bits (doubled)\n\t\t\tfor(k = 0; k < 7; k++) {\n\t\t\t\tval1 = val1 << 1;\n\t\t\t\tval2 = val2 << 2;\n\t\t\t\tif(val0 & 1) {\n\t\t\t\t\tval1 |= 1;\n\t\t\t\t\tval2 |= 3;\n\t\t\t\t}\n\t\t\t\tval0 = val0 >> 1;\n\t\t\t}\n\t\t\tg_a2font_bits[i][j] = (val2 << 8) | val1;\n\t\t}\n\t}\n}\n\nvoid\nprepare_a2_romx_font(byte *font_ptr)\n{\n\tword32\tval0, val1, val2;\n\tint\ti, j, k;\n\n\t// ROMX file\n\tfor(i = 0; i < 256; i++) {\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\tval0 = font_ptr[i*8 + j];\n\t\t\tval1 = 0;\t\t// 80-column bits\n\t\t\tval2 = 0;\t\t// 40-column bits (doubled)\n\t\t\tfor(k = 0; k < 7; k++) {\n\t\t\t\tval1 = val1 << 1;\n\t\t\t\tval2 = val2 << 2;\n\t\t\t\tif((val0 & 0x40) == 0) {\n\t\t\t\t\tval1 |= 1;\n\t\t\t\t\tval2 |= 3;\n\t\t\t\t}\n\t\t\t\tval0 = val0 << 1;\n\t\t\t}\n\t\t\tg_a2font_bits[i][j] = (val2 << 8) | val1;\n\t\t}\n\t}\n}\n\nvoid\nvideo_add_rect(Kimage *kimage_ptr, int x, int y, int width, int height)\n{\n\tint\tpos;\n\n\tpos = kimage_ptr->num_change_rects++;\n\tif(pos >= MAX_CHANGE_RECTS) {\n\t\treturn;\t\t\t// This will be handled later\n\t}\n\tkimage_ptr->change_rect[pos].x = x;\n\tkimage_ptr->change_rect[pos].y = y;\n\tkimage_ptr->change_rect[pos].width = width;\n\tkimage_ptr->change_rect[pos].height = height;\n\n\tg_video_pixel_dcount += (width * height);\n#if 0\n\tprintf(\"Add rect %d, x:%d y:%d, w:%d h:%d\\n\", pos, x, y, width, height);\n#endif\n}\n\nvoid\nvideo_add_a2_rect(int start_line, int end_line, int left_pix, int right_pix)\n{\n\tint\tsrcy;\n\n\tif((left_pix >= right_pix) || (left_pix < 0) || (right_pix <= 0)) {\n\t\thalt_printf(\"video_push_lines: lines %d to %d, pix %d to %d\\n\",\n\t\t\tstart_line, end_line, left_pix, right_pix);\n\t\tprintf(\"a2_screen_buf_ch:%08x, g_full_refr:%08x\\n\",\n\t\t\tg_a2_screen_buffer_changed, g_full_refresh_needed);\n\t\treturn;\n\t}\n\n\tsrcy = 2*start_line;\n\n\tvideo_add_rect(&g_mainwin_kimage, g_video_act_margin_left + left_pix,\n\t\t\tg_video_act_margin_top + srcy,\n\t\t\t(right_pix - left_pix), 2*(end_line - start_line));\n}\n\nvoid\nvideo_form_change_rects()\n{\n\tKimage\t*kimage_ptr;\n\tregister word32 start_time;\n\tregister word32 end_time;\n\tdword64\tsave_pixel_dcount;\n\tword32\tmask;\n\tint\tstart, line, left_pix, right_pix, left, right, line_div8;\n\tint\tx, y, width, height;\n\n\tkimage_ptr = &g_mainwin_kimage;\n\tif(g_border_sides_refresh_needed) {\n\t\tg_border_sides_refresh_needed = 0;\n\t\t// Add left side border\n\t\tvideo_add_rect(kimage_ptr, 0, g_video_act_margin_top,\n\t\t\t\t\t\tBORDER_WIDTH, A2_WINDOW_HEIGHT);\n\n\t\t// Add right-side border.  Resend x from 560 through\n\t\t//  X_A2_WINDOW_WIDTH\n\t\tx = g_video_act_margin_left + 560;\n\t\twidth = X_A2_WINDOW_WIDTH - x;\n\t\tvideo_add_rect(kimage_ptr, x, g_video_act_margin_top, width,\n\t\t\t\t\t\t\tA2_WINDOW_HEIGHT);\n\t}\n\tif(g_border_special_refresh_needed) {\n\t\tg_border_special_refresh_needed = 0;\n\n\t\t// Do top border\n\t\twidth = g_video_act_width;\n\t\theight = g_video_act_margin_top;\n\t\tvideo_add_rect(kimage_ptr, 0, 0, width, height);\n\n\t\t// Do bottom border\n\t\theight = g_video_act_margin_bottom;\n\t\ty = g_video_act_margin_top + A2_WINDOW_HEIGHT;\n\t\tvideo_add_rect(kimage_ptr, 0, y, width, height);\n\t}\n\tif(g_status_refresh_needed) {\n\t\tg_status_refresh_needed = 0;\n\t\twidth = g_mainwin_kimage.a2_width;\n\t\ty = g_video_act_margin_top + A2_WINDOW_HEIGHT +\n\t\t\t\t\t\tg_video_act_margin_bottom;\n\t\theight = kimage_ptr->a2_height - y;\n\t\tif(height > 0) {\n\t\t\tsave_pixel_dcount = g_video_pixel_dcount;\n\t\t\tvideo_add_rect(kimage_ptr, 0, y, width, height);\n\t\t\tg_video_pixel_dcount = save_pixel_dcount;\n\t\t}\n\t}\n\n\tif(g_a2_screen_buffer_changed == 0) {\n\t\treturn;\n\t}\n\n\tGET_ITIMER(start_time);\n\n\tstart = -1;\n\n\tleft_pix = 640;\n\tright_pix = 0;\n\n\tfor(line = 0; line < 200; line++) {\n\t\tline_div8 = line >> 3;\n\t\tmask = 1 << (line_div8);\n\t\tif((g_full_refresh_needed & mask) != 0) {\n\t\t\tleft = 0;\n\t\t\tright = 640;\n\t\t} else {\n\t\t\tleft = g_a2_line_left_edge[line];\n\t\t\tright = g_a2_line_right_edge[line];\n\t\t}\n\n\t\tif(!(g_a2_screen_buffer_changed & mask) || (left >= right)) {\n\t\t\t/* No need to update this line */\n\t\t\t/* Refresh previous chunks of lines, if any */\n\t\t\tif(start >= 0) {\n\t\t\t\tvideo_add_a2_rect(start, line, left_pix,\n\t\t\t\t\t\t\t\tright_pix);\n\t\t\t\tstart = -1;\n\t\t\t\tleft_pix = 640;\n\t\t\t\tright_pix = 0;\n\t\t\t}\n\t\t} else {\n\t\t\t/* Need to update this line */\n\t\t\tif(start < 0) {\n\t\t\t\tstart = line;\n\t\t\t}\n\t\t\tleft_pix = MY_MIN(left, left_pix);\n\t\t\tright_pix = MY_MAX(right, right_pix);\n\t\t}\n\t}\n\n\tif(start >= 0) {\n\t\tvideo_add_a2_rect(start, 200, left_pix, right_pix);\n\t}\n\n\tg_a2_screen_buffer_changed = 0;\n\tg_full_refresh_needed = 0;\n\n\tGET_ITIMER(end_time);\n\n\tg_cycs_in_xredraw += (end_time - start_time);\n}\n\nint\nvideo_get_a2_width(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->a2_width;\n}\n\nint\nvideo_get_x_width(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->x_width;\n}\n\nint\nvideo_get_a2_height(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->a2_height;\n}\n\nint\nvideo_get_x_height(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->x_height;\n}\n\nint\nvideo_get_x_xpos(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->x_xpos;\n}\n\nint\nvideo_get_x_ypos(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->x_ypos;\n}\n\nvoid\nvideo_update_xpos_ypos(Kimage *kimage_ptr, int x_xpos, int x_ypos)\n{\n\tx_xpos = video_clamp(x_xpos, 0, kimage_ptr->x_max_width - 640);\n\tx_ypos = video_clamp(x_ypos, 0, kimage_ptr->x_max_height - 420);\n\tkimage_ptr->x_xpos = x_xpos;\n\tkimage_ptr->x_ypos = x_ypos;\n\tif(kimage_ptr == &g_mainwin_kimage) {\n\t\tg_mainwin_xpos = x_xpos;\n\t\tg_mainwin_ypos = x_ypos;\n\t\t// printf(\"Set g_mainwin_xpos:%d, ypos:%d\\n\", x_xpos, x_ypos);\n\t}\n}\n\nint\nvideo_change_aspect_needed(Kimage *kimage_ptr, int x_width, int x_height)\n{\n\t// Return 1 if the passed in height, width do not match the kimage\n\t//  aspect-corrected version, and at least 2 VBL periods have passed\n\n\tif((kimage_ptr->vbl_of_last_resize + 6) > g_vbl_count) {\n\t\treturn 0;\n\t}\n\tif((kimage_ptr->x_height != x_height) ||\n\t\t\t\t\t(kimage_ptr->x_width != x_width)) {\n#if 0\n\t\tprintf(\"change_aspect_needed, vbl:%d kimage width:%d height:%d \"\n\t\t\t\"but x width:%d height:%d\\n\", g_vbl_count,\n\t\t\tkimage_ptr->x_width, kimage_ptr->x_height,\n\t\t\tx_width, x_height);\n#endif\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nvoid\nvideo_update_status_enable(Kimage *kimage_ptr)\n{\n\tint\theight, a2_height;\n\n\theight = g_video_act_margin_top + A2_WINDOW_HEIGHT +\n\t\t\t\t\t\tg_video_act_margin_bottom;\n\ta2_height = height;\n\tif(g_status_enable) {\n\t\ta2_height = kimage_ptr->a2_height_full;\n\t}\n\tkimage_ptr->a2_height = a2_height;\n\theight = (a2_height * kimage_ptr->scale_width_a2_to_x) >> 16;\n\tif(height > kimage_ptr->x_max_height) {\n\t\theight = kimage_ptr->x_max_height;\n\t}\n\tkimage_ptr->x_height = height;\n#if 0\n\tprintf(\"new a2_height:%d, x_height:%d\\n\", kimage_ptr->a2_height,\n\t\t\t\t\t\t\tkimage_ptr->x_height);\n#endif\n\t//printf(\"Calling video_update_scale from video_update_status_en\\n\");\n\tvideo_update_scale(kimage_ptr, kimage_ptr->x_width, height, 0);\n}\n\n// video_out_query: return 0 if no screen drawing at all is needed.\n//  returns 1 or the number of change_rects if any drawing is needed\nint\nvideo_out_query(Kimage *kimage_ptr)\n{\n\tint\tnum_change_rects, x_refresh_needed;\n\n\tnum_change_rects = kimage_ptr->num_change_rects;\n\tx_refresh_needed = kimage_ptr->x_refresh_needed;\n\tif(x_refresh_needed) {\n\t\treturn 1;\n\t}\n\treturn num_change_rects;\n}\n\n// video_out_done: used by specialize xdriver platform code which needs to\n//  clear the num_change_rects=0.\nvoid\nvideo_out_done(Kimage *kimage_ptr)\n{\n\tkimage_ptr->num_change_rects = 0;\n\tkimage_ptr->x_refresh_needed = 0;\n}\n\n// Called by xdriver.c to copy KEGS's kimage data to the vptr buffer\nint\nvideo_out_data(void *vptr, Kimage *kimage_ptr, int out_width_act,\n\t\tChange_rect *rectptr, int pos)\n{\n\tword32\t*out_wptr, *wptr;\n\tint\ta2_width, a2_width_full, width, a2_height, height, x, eff_y;\n\tint\tx_width, x_height, num_change_rects, x_refresh_needed;\n\tint\ti, j;\n\n\t// Copy from kimage_ptr->wptr to vptr\n\tnum_change_rects = kimage_ptr->num_change_rects;\n\tx_refresh_needed = kimage_ptr->x_refresh_needed;\n\tif(((pos >= num_change_rects) || (pos >= MAX_CHANGE_RECTS)) &&\n\t\t\t\t\t\t\t!x_refresh_needed) {\n\t\tkimage_ptr->num_change_rects = 0;\n\t\treturn 0;\n\t}\n\ta2_width = kimage_ptr->a2_width;\n\ta2_width_full = kimage_ptr->a2_width_full;\n\ta2_height = kimage_ptr->a2_height;\n\tif((num_change_rects >= MAX_CHANGE_RECTS) || x_refresh_needed) {\n\t\t// Table overflow, just copy everything in one go\n\t\tkimage_ptr->x_refresh_needed = 0;\n\t\tif(pos >= 1) {\n\t\t\tkimage_ptr->num_change_rects = 0;\n\t\t\treturn 0;\t\t// No more to do\n\t\t}\n\t\t// Force full update\n\t\trectptr->x = 0;\n\t\trectptr->y = 0;\n\t\trectptr->width = a2_width;\n\t\trectptr->height = a2_height;\n\t} else {\n\t\t*rectptr = kimage_ptr->change_rect[pos];\t// Struct copy\n\t}\n#if 0\n\tprintf(\"video_out_data, %p rectptr:%p, pos:%d, x:%d y:%d w:%d h:%d, \"\n\t\t\"wptr:%p\\n\", vptr, rectptr, pos, rectptr->x,\n\t\trectptr->y, rectptr->width, rectptr->height,\n\t\tkimage_ptr->wptr);\n#endif\n\n\twidth = rectptr->width;\n\theight = rectptr->height;\n\tx = rectptr->x;\n\tx_width = kimage_ptr->x_width;\n\tx_height = kimage_ptr->x_height;\n\tif(!g_video_no_scale_window &&\n\t\t\t((a2_width != x_width) || (a2_height != x_height))) {\n#if 0\n\t\tprintf(\"a2_width:%d, x_width:%d, a2_height:%d, x_height:\"\n\t\t\t\"%d\\n\", a2_width, x_width, a2_height, x_height);\n#endif\n\t\treturn video_out_data_scaled(vptr, kimage_ptr, out_width_act,\n\t\t\t\t\t\t\t\trectptr);\n\t} else {\n\t\tout_wptr = (word32 *)vptr;\n\t\tfor(i = 0; i < height; i++) {\n\t\t\teff_y = rectptr->y + i;\n\t\t\twptr = kimage_ptr->wptr + (eff_y * a2_width_full) + x;\n\t\t\tout_wptr = ((word32 *)vptr) +\n\t\t\t\t\t\t(eff_y * out_width_act) + x;\n\t\t\tfor(j = 0; j < width; j++) {\n\t\t\t\t*out_wptr++ = *wptr++;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 1;\n}\n\n\nint\nvideo_out_data_intscaled(void *vptr, Kimage *kimage_ptr, int out_width_act,\n\t\t\t\t\tChange_rect *rectptr)\n{\n\tword32\t*out_wptr, *wptr;\n\tword32\tpos_scale, alpha_mask;\n\tint\ta2_width_full, eff_y, src_y, x, y, new_x, out_x, out_y;\n\tint\tout_width, out_height, max_x, max_y, out_max_x, out_max_y, pos;\n\tint\ti, j;\n\n\t// Faster scaling routine which does simple pixel replication rather\n\t//  than blending.  Intended for scales >= 3.0 (or so) since at\n\t//  these scales, replication looks fine.\n\tx = rectptr->x;\n\ty = rectptr->y;\n\tmax_x = rectptr->width + x;\n\tmax_y = rectptr->height + y;\n\tmax_x = MY_MIN(kimage_ptr->a2_width_full, max_x + 1);\n\tmax_y = MY_MIN(kimage_ptr->a2_height, max_y + 1);\n\tx = MY_MAX(0, x - 1);\n\ty = MY_MAX(0, y - 1);\n\ta2_width_full = kimage_ptr->a2_width_full;\n\n\tout_x = (x * kimage_ptr->scale_width_a2_to_x) >> 16;\n\tout_y = (y * kimage_ptr->scale_height_a2_to_x) >> 16;\n\tout_max_x = (max_x * kimage_ptr->scale_width_a2_to_x + 65535) >> 16;\n\tout_max_y = (max_y * kimage_ptr->scale_height_a2_to_x + 65535) >> 16;\n\tout_max_x = MY_MIN(out_max_x, out_width_act);\n\tout_max_y = MY_MIN(out_max_y, kimage_ptr->x_height);\n\tout_width = out_max_x - out_x;\n\tout_height = out_max_y - out_y;\n\tout_wptr = (word32 *)vptr;\n\trectptr->x = out_x;\n\trectptr->y = out_y;\n\trectptr->width = out_width;\n\trectptr->height = out_height;\n\talpha_mask = g_alpha_mask;\n\tfor(i = 0; i < out_height; i++) {\n\t\teff_y = out_y + i;\n\t\tpos_scale = kimage_ptr->scale_height[eff_y];\n\t\tsrc_y = pos_scale >> 16;\n\t\twptr = kimage_ptr->wptr + (src_y * a2_width_full);\n\t\tout_wptr = ((word32 *)vptr) + (eff_y * out_width_act) + out_x;\n\t\tfor(j = 0; j < out_width; j++) {\n\t\t\tnew_x = j + out_x;\n\t\t\tpos_scale = kimage_ptr->scale_width[new_x];\n\t\t\tpos = pos_scale >> 16;\n\t\t\t*out_wptr++ = wptr[pos] | alpha_mask;\n\t\t}\n\t}\n\trectptr->width = kimage_ptr->x_width - rectptr->x;\n\n\treturn 1;\n}\n\nint\nvideo_out_data_scaled(void *vptr, Kimage *kimage_ptr, int out_width_act,\n\t\t\t\t\tChange_rect *rectptr)\n{\n\tword32\t*out_wptr, *wptr;\n\tdword64\tdval0a, dval0b, dval1a, dval1b, dscale, dscale_y, dval;\n\tword32\tnew_val, pos_scale, alpha_mask;\n\tint\ta2_width_full, eff_y, src_y, x, y, new_x, out_x, out_y;\n\tint\tout_width, out_height, max_x, max_y, out_max_x, out_max_y, pos;\n\tint\ti, j;\n\n\tif((kimage_ptr->scale_width_a2_to_x >= 0x34000) ||\n\t\t\t(kimage_ptr->scale_height_a2_to_x >= 0x34000)) {\n\t\treturn video_out_data_intscaled(vptr, kimage_ptr,\n\t\t\t\t\t\tout_width_act, rectptr);\n\t}\n\tx = rectptr->x;\n\ty = rectptr->y;\n\tmax_x = rectptr->width + x;\n\tmax_y = rectptr->height + y;\n\tmax_x = MY_MIN(kimage_ptr->a2_width_full, max_x + 1);\n\tmax_y = MY_MIN(kimage_ptr->a2_height, max_y + 1);\n\tx = MY_MAX(0, x - 1);\n\ty = MY_MAX(0, y - 1);\n\ta2_width_full = kimage_ptr->a2_width_full;\n\n\tout_x = (x * kimage_ptr->scale_width_a2_to_x) >> 16;\n\tout_y = (y * kimage_ptr->scale_height_a2_to_x) >> 16;\n\tout_max_x = (max_x * kimage_ptr->scale_width_a2_to_x + 65535) >> 16;\n\tout_max_y = (max_y * kimage_ptr->scale_height_a2_to_x + 65535) >> 16;\n\tout_max_x = MY_MIN(out_max_x, out_width_act);\n\tout_max_y = MY_MIN(out_max_y, kimage_ptr->x_height);\n\tout_width = out_max_x - out_x;\n\tout_height = out_max_y - out_y;\n#if 0\n\tprintf(\"scaled: in %d,%d %d,%d becomes %d,%d %d,%d\\n\", x, y, width,\n\t\theight, out_x, out_y, out_width, out_height);\n#endif\n\tout_wptr = (word32 *)vptr;\n\trectptr->x = out_x;\n\trectptr->y = out_y;\n\trectptr->width = out_width;\n\trectptr->height = out_height;\n\talpha_mask = g_alpha_mask;\n\tfor(i = 0; i < out_height; i++) {\n\t\teff_y = out_y + i;\n\t\tpos_scale = kimage_ptr->scale_height[eff_y];\n\t\tsrc_y = pos_scale >> 16;\n\t\tdscale_y = (pos_scale & 0xffff) >> 8;\n\t\twptr = kimage_ptr->wptr + (src_y * a2_width_full);\n\t\tout_wptr = ((word32 *)vptr) + (eff_y * out_width_act) + out_x;\n\t\tfor(j = 0; j < out_width; j++) {\n\t\t\tnew_x = j + out_x;\n\t\t\tpos_scale = kimage_ptr->scale_width[new_x];\n\t\t\tpos = pos_scale >> 16;\n\t\t\tdscale = (pos_scale & 0xffff) >> 8;\n\t\t\tdval0a = wptr[pos];\n\t\t\tdval0a = (dval0a & 0x00ff00ffULL) |\n\t\t\t\t\t((dval0a & 0xff00ff00ULL) << 24);\n\t\t\tdval0b = wptr[pos + 1];\n\t\t\tdval0b = (dval0b & 0x00ff00ffULL) |\n\t\t\t\t\t((dval0b & 0xff00ff00ULL) << 24);\n\t\t\tdval1a = wptr[pos + a2_width_full];\n\t\t\tdval1a = (dval1a & 0x00ff00ffULL) |\n\t\t\t\t\t((dval1a & 0xff00ff00ULL) << 24);\n\t\t\tdval1b = wptr[pos + 1 + a2_width_full];\n\t\t\tdval1b = (dval1b & 0x00ff00ffULL) |\n\t\t\t\t\t((dval1b & 0xff00ff00ULL) << 24);\n\t\t\tdval0a = ((0x100 - dscale) * dval0a) +\n\t\t\t\t\t(dscale * dval0b);\n\t\t\tdval1a = ((0x100 - dscale) * dval1a) +\n\t\t\t\t\t(dscale * dval1b);\n\t\t\tdval0a = (dval0a >> 8) & 0x00ff00ff00ff00ffULL;\n\t\t\tdval1a = (dval1a >> 8) & 0x00ff00ff00ff00ffULL;\n\t\t\tdval = ((0x100 - dscale_y) * dval0a) +\n\t\t\t\t\t(dscale_y * dval1a);\n\t\t\tnew_val = ((dval >> 8) & 0x00ff00ffULL) |\n\t\t\t\t((dval >> 32) & 0xff00ff00ULL);\n\t\t\t*out_wptr++ = new_val | alpha_mask;\n#if 0\n\t\t\tif((pos == 300) && (eff_y == 100)) {\n\t\t\t\tprintf(\"x:%d pos:%d %08x.  %016llx,%016llx \"\n\t\t\t\t\t\"pos_sc:%08x, %08x\\n\", new_x, pos,\n\t\t\t\t\tnew_val, dval0a, dval0b, pos_scale,\n\t\t\t\t\twptr[pos]);\n\t\t\t}\n#endif\n\t\t}\n\t}\n\trectptr->width = kimage_ptr->x_width - rectptr->x;\n#if 0\n\tfor(i = 0; i < kimage_ptr->x_height; i++) {\n\t\tout_wptr = ((word32 *)vptr) + (i * out_width_act) +\n\t\t\t\t\t\tkimage_ptr->x_width - 1;\n\t\t*out_wptr = 0x00ff00ff;\n# if 0\n\t\tfor(j = 0; j < 10; j++) {\n\t\t\tif(*out_wptr != 0) {\n\t\t\t\tprintf(\"out_wptr:%p is %08x at %d,%d\\n\",\n\t\t\t\t\tout_wptr, *out_wptr,\n\t\t\t\t\tout_width_act - 1 - j, i);\n\t\t\t}\n\t\t\tout_wptr--;\n\t\t}\n# endif\n\t}\n#endif\n\n\treturn 1;\n}\n\nword32\nvideo_scale_calc_frac(int pos, word32 max, word32 frac_inc, word32 frac_inc_inv)\n{\n\tword32\tfrac, frac_to_next, new_frac;\n\n\tfrac = pos * frac_inc;\n\tif(frac >= max) {\n\t\treturn max;\t\t\t// Clear frac bits\n\t}\n\tif(g_video_scale_algorithm == 2) {\n\t\treturn frac & -65536;\t\t\t// nearest neighbor\n\t}\n\tif(g_video_scale_algorithm == 1) {\n\t\treturn frac;\t\t\t\t// bilinear interp\n\t}\n\t// Do proper scaling.  fraction=0 means 100% this pixel, fraction=ffff\n\t//  means 99.99% the next pixel\n\tfrac_to_next = frac_inc + (frac & 0xffff);\n\tif(frac_to_next < 65536) {\n\t\tfrac_to_next = 0;\n\t}\n\tfrac_to_next = (frac_to_next & 0xffff) * frac_inc_inv;\n\tfrac_to_next = frac_to_next >> 16;\n\tnew_frac = (frac & -65536) | (frac_to_next & 0xffff);\n#if 0\n\tif((frac >= (30 << 16)) && (frac < (38 << 16))) {\n\t\tprintf(\"scale %d (%02x) -> %08x (was %08x) %08x %08x\\n\",\n\t\t\tpos, pos, new_frac, frac, frac_inc, frac_inc_inv);\n\t}\n#endif\n\treturn new_frac;\n}\n\nvoid\nvideo_update_scale(Kimage *kimage_ptr, int out_width, int out_height,\n\t\t\t\t\t\t\tint must_update)\n{\n\tword32\tfrac_inc, frac_inc_inv, new_frac, max;\n\tint\ta2_width, a2_height, exp_width, exp_height;\n\tint\ti;\n\n\tout_width = video_clamp(out_width, 1, kimage_ptr->x_max_width);\n\tout_width = video_clamp(out_width, 1, MAX_SCALE_SIZE);\n\n\tout_height = video_clamp(out_height, 1, kimage_ptr->x_max_height);\n\tout_height = video_clamp(out_height, 1, MAX_SCALE_SIZE);\n\n\ta2_width = kimage_ptr->a2_width;\n\ta2_height = kimage_ptr->a2_height;\n\tkimage_ptr->vbl_of_last_resize = g_vbl_count;\n\n\t// Handle aspect ratio.  Calculate height/width based on the other's\n\t//  aspect ratio, and pick the smaller value\n\texp_width = (a2_width * out_height) / a2_height;\n\texp_height = (a2_height * out_width) / a2_width;\n\n\tif(exp_width < a2_width) {\n\t\texp_width = a2_width;\n\t}\n\tif(exp_height < a2_height) {\n\t\texp_height = a2_height;\n\t}\n\tif(exp_width < out_width) {\n\t\t// Allow off-by-one to be OK, so window doesn't keep resizing\n\t\tif((exp_width + 1) != out_width) {\n\t\t\tout_width = exp_width;\n\t\t}\n\t}\n\tif(exp_height < out_height) {\n\t\tif((exp_height + 1) != out_height) {\n\t\t\tout_height = exp_height;\n\t\t}\n\t}\n\tif(out_width <= 0) {\n\t\tout_width = 1;\n\t}\n\tif(out_height <= 0) {\n\t\tout_height = 1;\n\t}\n\n\t// See if anything changed.  If it's unchanged, don't do anything\n\tif((kimage_ptr->x_width == out_width) && !must_update &&\n\t\t\t(kimage_ptr->x_height == out_height)) {\n\t\treturn;\n\t}\n\tkimage_ptr->x_width = out_width;\n\tkimage_ptr->x_height = out_height;\n\tkimage_ptr->x_refresh_needed = 1;\n\tif(kimage_ptr == &g_mainwin_kimage) {\n\t\tg_mainwin_width = out_width;\n\t\tg_mainwin_height = out_height;\n\t\t//printf(\"Set g_mainwin_width=%d, g_mainwin_height=%d\\n\",\n\t\t//\t\t\t\tout_width, out_height);\n\t}\n\n\t// the per-pixel inc = a2_width / out_width.  Scale by 65536\n\tfrac_inc = (a2_width * 65536UL) / out_width;\n\tkimage_ptr->scale_width_to_a2 = frac_inc;\n\tfrac_inc_inv = (out_width * 65536UL) / a2_width;\n\tkimage_ptr->scale_width_a2_to_x = frac_inc_inv;\n#if 0\n\tprintf(\"scale_width_to_a2: %08x, a2_to_x:%08x, is_debugwin:%d\\n\",\n\t\tkimage_ptr->scale_width_to_a2, kimage_ptr->scale_width_a2_to_x,\n\t\t(kimage_ptr == &g_debugwin_kimage));\n#endif\n\tmax = (a2_width - 1) << 16;\n\tfor(i = 0; i < out_width + 1; i++) {\n\t\tnew_frac = video_scale_calc_frac(i, max, frac_inc,\n\t\t\t\t\t\t\t\tfrac_inc_inv);\n\t\tkimage_ptr->scale_width[i] = new_frac;\n\t}\n\n\tfrac_inc = (a2_height * 65536UL) / out_height;\n\tkimage_ptr->scale_height_to_a2 = frac_inc;\n\tfrac_inc_inv = (out_height * 65536UL) / a2_height;\n\tkimage_ptr->scale_height_a2_to_x = frac_inc_inv;\n#if 0\n\tprintf(\"scale_height_to_a2: %08x, a2_to_x:%08x. w:%d h:%d\\n\",\n\t\tkimage_ptr->scale_height_to_a2,\n\t\tkimage_ptr->scale_height_a2_to_x, out_width, out_height);\n#endif\n\tmax = (a2_height - 1) << 16;\n\tfor(i = 0; i < out_height + 1; i++) {\n\t\tnew_frac = video_scale_calc_frac(i, max, frac_inc,\n\t\t\t\t\t\t\t\tfrac_inc_inv);\n\t\tkimage_ptr->scale_height[i] = new_frac;\n\t}\n}\n\nint\nvideo_scale_mouse_x(Kimage *kimage_ptr, int raw_x, int x_width)\n{\n\tint\tx;\n\n\t// raw_x is in output coordinates.  Scale down to a2 coordinates\n\tif(x_width == 0) {\n\t\tx = (kimage_ptr->scale_width_to_a2 * raw_x) / 65536;\n\t} else {\n\t\t// Scale raw_x using x_width\n\t\tx = (raw_x * kimage_ptr->a2_width_full) / x_width;\n\t}\n\tx = x - BASE_MARGIN_LEFT;\n\treturn x;\n}\n\nint\nvideo_scale_mouse_y(Kimage *kimage_ptr, int raw_y, int y_height)\n{\n\tint\ty;\n\n\t// raw_y is in output coordinates.  Scale down to a2 coordinates\n\tif(y_height == 0) {\n\t\ty = (kimage_ptr->scale_height_to_a2 * raw_y) / 65536;\n\t} else {\n\t\t// Scale raw_y using y_height\n\t\ty = (raw_y * kimage_ptr->a2_height) / y_height;\n\t}\n\ty = y - BASE_MARGIN_TOP;\n\treturn y;\n}\n\nint\nvideo_unscale_mouse_x(Kimage *kimage_ptr, int a2_x, int x_width)\n{\n\tint\tx;\n\n\t// Convert a2_x to output coordinates\n\tx = a2_x + BASE_MARGIN_LEFT;\n\tif(x_width == 0) {\n\t\tx = (kimage_ptr->scale_width_a2_to_x * x) / 65536;\n\t} else {\n\t\t// Scale a2_x using x_width\n\t\tx = (x * x_width) / kimage_ptr->a2_width_full;\n\t}\n\treturn x;\n}\n\nint\nvideo_unscale_mouse_y(Kimage *kimage_ptr, int a2_y, int y_height)\n{\n\tint\ty;\n\n\t// Convert a2_y to output coordinates\n\ty = a2_y + BASE_MARGIN_TOP;\n\tif(y_height == 0) {\n\t\ty = (kimage_ptr->scale_height_a2_to_x * y) / 65536;\n\t} else {\n\t\t// Scale a2_y using y_height\n\t\ty = (y * y_height) / kimage_ptr->a2_height;\n\t}\n\treturn y;\n}\n\nvoid\nvideo_update_color_raw(int bank, int col_num, int a2_color)\n{\n\tword32\ttmp;\n\tint\tred, green, blue, newred, newgreen, newblue;\n\n\tif(col_num >= 256 || col_num < 0) {\n\t\thalt_printf(\"video_update_color_raw: col: %03x\\n\", col_num);\n\t\treturn;\n\t}\n\n\tred = (a2_color >> 8) & 0xf;\n\tgreen = (a2_color >> 4) & 0xf;\n\tblue = (a2_color) & 0xf;\n\tred = ((red << 4) + red);\n\tgreen = ((green << 4) + green);\n\tblue = ((blue << 4) + blue);\n\n\tnewred = red >> g_red_right_shift;\n\tnewgreen = green >> g_green_right_shift;\n\tnewblue = blue >> g_blue_right_shift;\n\n\ttmp = ((newred & g_red_mask) << g_red_left_shift) +\n\t\t\t((newgreen & g_green_mask) << g_green_left_shift) +\n\t\t\t((newblue & g_blue_mask) << g_blue_left_shift);\n\tg_palette_8to1624[bank][col_num] = tmp;\n}\n\nvoid\nvideo_update_status_line(int line, const char *string)\n{\n\tbyte\ta2_str_buf[STATUS_LINE_LENGTH+1];\n\tword32\t*wptr;\n\tchar\t*buf;\n\tconst char *ptr;\n\tword32\tline_bytes;\n\tint\tstart_line, c, pixels_per_line, offset;\n\tint\ti;\n\n\tif(line >= MAX_STATUS_LINES || line < 0) {\n\t\tprintf(\"update_status_line: line: %d!\\n\", line);\n\t\texit(1);\n\t}\n\n\tptr = string;\n\tbuf = &(g_status_buf[line][0]);\n\tg_status_ptrs[line] = buf;\n\tfor(i = 0; i < STATUS_LINE_LENGTH; i++) {\n\t\tif(*ptr) {\n\t\t\tc = *ptr++;\n\t\t} else {\n\t\t\tc = ' ';\n\t\t}\n\t\tbuf[i] = c;\n\t\ta2_str_buf[i] = c | 0x80;\n\t}\n\n\tbuf[STATUS_LINE_LENGTH] = 0;\n\ta2_str_buf[STATUS_LINE_LENGTH] = 0;\n\tstart_line = (200 + 2*8) + line*8;\n\tpixels_per_line = g_mainwin_kimage.a2_width_full;\n\toffset = (pixels_per_line * g_video_act_margin_top);\n\twptr = g_mainwin_kimage.wptr;\n\twptr += offset;\n\tfor(i = 0; i < 8; i++) {\n\t\tline_bytes = ((start_line + i) << 16) | (40 << 8) | 0;\n\t\tredraw_changed_string(&(a2_str_buf[0]), line_bytes, -1L,\n\t\t\twptr, 0, 0x00ffffff, pixels_per_line, 1);\n\t}\n\n\t// Don't add rectangle here, video_form_change_rects will do it\n\t//video_add_a2_rect(start_line, start_line + 8, 0, 640);\n}\n\nvoid\nvideo_draw_a2_string(int line, const byte *bptr)\n{\n\tword32\t*wptr;\n\tword32\tline_bytes;\n\tint\tstart_line, pixels_per_line, offset;\n\tint\ti;\n\n\tstart_line = line*8;\n\tpixels_per_line = g_mainwin_kimage.a2_width_full;\n\toffset = (pixels_per_line * g_video_act_margin_top) +\n\t\t\t\t\tg_video_act_margin_left;\n\twptr = g_mainwin_kimage.wptr;\n\twptr += offset;\n\tfor(i = 0; i < 8; i++) {\n\t\tline_bytes = ((start_line + i) << 16) | (40 << 8) | 0;\n\t\tredraw_changed_string(bptr, line_bytes, -1L,\n\t\t\twptr, 0, 0x00ffffff, pixels_per_line, 1);\n\t}\n\tg_mainwin_kimage.x_refresh_needed = 1;\n}\n\nvoid\nvideo_show_debug_info()\n{\n\tword32\ttmp1;\n\n\tprintf(\"g_cur_dfcyc: %016llx, last_vbl: %016llx\\n\", g_cur_dfcyc,\n\t\t\t\t\t\t\tg_last_vbl_dfcyc);\n\ttmp1 = get_lines_since_vbl(g_cur_dfcyc);\n\tprintf(\"lines since vbl: %06x\\n\", tmp1);\n\tprintf(\"Last line updated: %d\\n\", g_vid_update_last_line);\n}\n\nword32\nread_video_data(dword64 dfcyc)\n{\n\tword32\tval, val2;\n\tint\tlines_since_vbl, line;\n\n\t// Return Charrom data at $C02C for SuperConvert 4 TDM mode\n\tlines_since_vbl = get_lines_since_vbl(dfcyc);\n\tval = float_bus_lines(dfcyc, lines_since_vbl);\n\tline = lines_since_vbl >> 8;\n\tif(line < 192) {\n\t\t// Always do the character ROM\n\t\tval2 = g_a2font_bits[val & 0xff][line & 7];\n\t\tdbg_log_info(dfcyc, val,\n\t\t\t(lines_since_vbl << 8) | (val2 & 0xff), 0xc02c);\n\t\tval = ~val2;\t\t// Invert it, maybe\n\t}\n\treturn val & 0xff;\n}\n\nword32\nfloat_bus(dword64 dfcyc)\n{\n\tword32\tlines_since_vbl;\n\n\tlines_since_vbl = get_lines_since_vbl(dfcyc);\n\treturn float_bus_lines(dfcyc, lines_since_vbl);\n}\n\nword32\nfloat_bus_lines(dword64 dfcyc, word32 lines_since_vbl)\n{\n\tword32\tval;\n\tint\tline, eff_line, line24, all_stat, byte_offset;\n\tint\thires, page2, addr;\n\n/* For floating bus, model hires style: Visible lines 0-191 are simply the */\n/* data being displayed at that time.  Lines 192-255 are lines 0 - 63 again */\n/*  and lines 256-261 are lines 58-63 again */\n/* For each line, figure out starting byte at -25 mod 128 bytes from this */\n/*  line's start */\n/* This emulates an Apple II style floating bus.  A real IIgs does not */\n/*  drive anything meaningful during the 25 horizontal blanking cycles, */\n/*  nor during veritical blanking.  The data seems to be 0 or related to */\n/*  the instruction fetches on a real IIgs during blankings */\n\n\tline = lines_since_vbl >> 8;\n\tbyte_offset = lines_since_vbl & 0xff;\n\t// byte offset is from 0 through 64, where the visible screen is drawn\n\t//  from 25 through 64\n\n\teff_line = line;\n\tif(eff_line >= 0x100) {\n\t\teff_line = (eff_line - 6) & 0xff;\n\t}\n\tif(byte_offset == 0) {\n\t\tbyte_offset = 1;\n\t}\n\tall_stat = g_cur_a2_stat;\n\thires = (all_stat & ALL_STAT_HIRES) && !(all_stat & ALL_STAT_TEXT);\n\tif((all_stat & ALL_STAT_MIX_T_GR) && (line >= 160)) {\n\t\thires = 0;\n\t}\n\tpage2 = EXTRU(all_stat, 31 - BIT_ALL_STAT_PAGE2, 1);\n\tif(all_stat & ALL_STAT_ST80) {\n\t\tpage2 = 0;\n\t}\n\n\tline24 = (eff_line >> 3) & 0x1f;\n\taddr = g_screen_index[line24] & 0x3ff;\n\taddr = (addr & 0x380) + (((addr & 0x7f) - 25 + byte_offset) & 0x7f);\n\tif(hires) {\n\t\taddr = 0x2000 + addr + ((eff_line & 7) << 10) + (page2 << 13);\n\t} else {\n\t\taddr = 0x400 + addr + (page2 << 10);\n\t}\n\n\tval = g_slow_memory_ptr[addr];\n#if 0\n\tprintf(\"For %04x (%d) addr=%04x, val=%02x, dfcyc:%016llx\\n\",\n\t\tlines_since_vbl, eff_line, addr, val, dfcyc - g_last_vbl_dfcyc);\n#endif\n\tdbg_log_info(dfcyc, ((lines_since_vbl >> 11) << 24) |\n\t\t\t(lines_since_vbl - 25), (addr << 8) | val, 0xff);\n\n\treturn val;\n}\n"
  },
  {
    "path": "gsplus/src/voc.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// This file provides emulation of the Apple Video Overlay Card, which\n//  will appear to be in slot 3 if g_voc_enable=1 (there's a config.c\n//  setting to control enabling VOC).  The only currently supported VOC\n//  feature is the SHR interlaced display using both Main and Aux memory\n//  to provide a 640x400 (or 320x400) pixel display.\n\n#include \"defc.h\"\n\nextern word32 g_c02b_val;\nextern int g_cur_a2_stat;\nextern word32 g_vbl_count;\n\nint\tg_voc_enable = 0;\t\t// Default to disabled for now\nword32\tg_voc_reg1 = 0x09;\nword32\tg_voc_reg3 = 0;\nword32\tg_voc_reg4 = 0;\nword32\tg_voc_reg5 = 0;\nword32\tg_voc_reg6 = 0;\n\nword32\nvoc_devsel_read(word32 loc, dword64 dfcyc)\n{\n\t// Reads to $c0b0-$c0bf.\n\tloc = loc & 0xf;\n\tswitch(loc) {\n\tcase 0:\t\t// 0xc0b0\n\t\treturn voc_read_reg0(dfcyc);\n\t\tbreak;\n\tcase 1:\t\t// 0xc0b1\n\t\treturn g_voc_reg1;\n\t\tbreak;\n\tcase 3:\t\t// 0xc0b3\n\t\treturn g_voc_reg3;\n\t\tbreak;\n\tcase 4:\t\t// 0xc0b4\n\t\treturn g_voc_reg4;\n\t\tbreak;\n\tcase 5:\t\t// 0xc0b5\n\t\treturn g_voc_reg5;\n\t\tbreak;\n\tcase 6:\t\t// 0xc0b6\n\t\treturn g_voc_reg6;\n\t\tbreak;\n\tcase 7:\t\t// 0xc0b7, possible Uthernet 2 detection\n\t\treturn 0x00;\n\t\tbreak;\n\tcase 8:\t\t// 0xc0b8, Second Sight detection by jpeGS program\n\t\treturn 0x00;\t\t// Second Sight returns 0x01\n\t\tbreak;\n\tcase 0xd:\t// 0xc0bd, A2OSX Uthernet 1 detection code\n\t\treturn 0x00;\n\t\tbreak;\n\t}\n\n\thalt_printf(\"Tried to read: %04x\\n\", 0xc0b0 + loc);\n\n\treturn 0;\n}\n\nvoid\nvoc_devsel_write(word32 loc, word32 val, dword64 dfcyc)\n{\n\t// Writes to $c0b0-$c0bf.\n\tloc = loc & 0xf;\n\tswitch(loc) {\n\tcase 0:\t\t// 0xc0b0\n\t\t// Write 0 to clear VBL interrupts\n\t\tif(val != 0) {\n\t\t\thalt_printf(\"VOC write %04x = %02x\\n\", loc, val);\n\t\t}\n\t\treturn;\n\t\tbreak;\n\tcase 1:\t\t// 0xc0b1\n\t\t// bit 0: R/W: 1=GG Bus Enable\n\t\t//\tWhen 0, I think VOC ignores writes to $c023,etc.\n\t\t// bit 2: R/W: 0=OutChromaFilter enabled, 1=ChromaFilter disab\n\t\t//   bit 2 is also TextMonoOver somehow using bit[5]==1\n\t\t// bit 3: R/W: 1=MainPageLin\n\t\t// bits 5:4: R/W: 00=Aux mem; 01=Main Memory; 11=Interlaced\n\t\t// bit 6: R/W: 1=Enable VBL Interrupt\n\t\t// bit 7: R/W: 1=Enable Line interrupts\n\t\tif(!g_voc_enable) {\n\t\t\tval = 0;\n\t\t}\n\t\tif(val & 0xc0) {\n\t\t\thalt_printf(\"VOC write %04x = %02x\\n\", loc, val);\n\t\t}\n#if 0\n\t\tif(val != g_voc_reg1) {\n\t\t\tprintf(\"$c0b1:%02x (was %02x)\\n\", val, g_voc_reg1);\n\t\t}\n#endif\n\t\tg_voc_reg1 = val;\n\t\tvoc_update_interlace(dfcyc);\n\t\treturn;\n\t\tbreak;\n\tcase 3:\t\t// 0xc0b3\n\t\t// bits 2:0: R/W: Key Dissolve, 0=100% graphics, 7=100% video\n\t\t// bit 3: R/W: 1=Enhanced Dissolve enabled\n\t\t// bits 6:4: R/W: Non-Key Dissolve, 0=100% graphics, 7=100% vid\n\t\t// bit 7: R/W: 0=Output Setup Enabled, 1=Output Setup Disabled\n\t\tg_voc_reg3 = val;\n\t\treturn;\n\t\tbreak;\n\tcase 4:\t\t// 0xc0b4\n\t\t// bits 3:0: R/W: KeyColor Blue\n\t\t// bits 7:4: R/W: KeyColor Green\n\t\tg_voc_reg4 = val;\n\t\treturn;\n\t\tbreak;\n\tcase 5:\t\t// 0xc0b5\n\t\t// bits 3:0: R/W: KeyColor Red\n\t\t// bit 4: R/W: OutExtBlank: 0=Graphics, 1=External\n\t\t// bit 5: R/W: 0=GenLock enabled, 1=GenLock disabled\n\t\t// bit 6: R/W: 0=KeyColor enabled, 1=KeyColor disabled\n\t\t// bit 7: R/W: 1=Interlace mode enabled\n\t\tg_voc_reg5 = val;\n\t\tvoc_update_interlace(dfcyc);\n\t\treturn;\n\t\tbreak;\n\tcase 6:\t\t// 0xc0b6\n\t\t// Write 0 to cause AdjSave to occur\n\t\t// Write 8, then 9, then 8 again to cause AdjInc for Hue\n\t\t// Write a, then b, then a again to cause AdjDec for Hue\n\t\t// Write 4, then 5, then 4 again to cause AdjInc for Saturation\n\t\t// Write 6, then 7, then 6 again to cause AdjDec for Saturation\n\t\t// bit 3: hue, bit 2: saturation\n\t\tg_voc_reg6 = val;\n\t\treturn;\n\t\tbreak;\n\tcase 7:\t\t// 0xc0b7\n\t\t// Written by System Disk 1.1 Desktop.sys to 0xfd, ignore\n\t\tif(val == 0xfd) {\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tcase 0xa:\n\tcase 0xb:\t// 0xc0ba,0xc0bb written to 0 by A2OSX Uthernet1 detect\n\t\tif(val == 0) {\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\t}\n\thalt_printf(\"Unknown Write %04x = %02x %016llx\\n\", 0xc0b0 + loc, val,\n\t\t\t\t\t\t\t\tdfcyc);\n}\n\nvoid\nvoc_iosel_c300_write(word32 loc, word32 val, dword64 dfcyc)\n{\n\t// Writes to $c300-$c3ff\n\thalt_printf(\"Wrote VOC %04x = %02x %016llx\\n\", 0xc300 + (loc & 0xff),\n\t\t\t\t\t\t\t\tval, dfcyc);\n}\n\nvoid\nvoc_reset()\n{\n\tg_voc_reg1 = 0x0d;\t\t// [0]: GG Bus enable, [3]:MainPageLin\n\tg_voc_reg3 = 0x07;\n\tg_voc_reg4 = 0;\n\tg_voc_reg5 = 0x40;\n\tg_voc_reg6 = 0;\n}\n\ndouble g_voc_last_pal_vbl = 0;\n\nword32\nvoc_read_reg0(dword64 dfcyc)\n{\n\tword32\tframe, in_vbl;\n\n\tif(!g_voc_enable) {\n\t\treturn 0;\n\t}\n\t// Reading $c0b0.\n\t// c0b0: bit 2: R/O: 1=In VBL\n\t// c0b0: bit 3: R/O: 0=No Video Detected, 1=Video Detected\n\t// c0b0: bit 4: R/O: 1=Video Genlocked\n\t// c0b0: bit 5: R/O: 0=showing Field 0, 1=showing Field 1\n\t// c0b0: bit 6: R/O: 1=VBL Int Request pending\n\t// c0b0: bit 7: R/O: 1=Line Int Request pending\n\tin_vbl = in_vblank(dfcyc);\n\tdbg_log_info(dfcyc, 0, in_vbl, 0x1c0b0);\n\tframe = g_vbl_count & 1;\n\treturn (frame << 5) | (in_vbl << 2);\n}\n\nvoid\nvoc_update_interlace(dword64 dfcyc)\n{\n\tword32\tnew_stat, mask;\n\n\tnew_stat = 0;\n\tif(((g_voc_reg1 & 0x30) == 0x30) && (g_voc_reg5 & 0x80)) {\n\t\tnew_stat = ALL_STAT_VOC_INTERLACE;\n\t}\n\tif((g_voc_reg1 & 0x30) == 0x10) {\t\t// Draw SHR from mainmem\n\t\tnew_stat = ALL_STAT_VOC_MAIN;\n\t}\n\tmask = ALL_STAT_VOC_INTERLACE | ALL_STAT_VOC_MAIN;\n\tif((g_cur_a2_stat ^ new_stat) & mask) {\n\t\t// Interlace mode has changed\n\t\tg_cur_a2_stat &= (~mask);\n\t\tg_cur_a2_stat |= new_stat;\n\t\tprintf(\"Change VOC interlace mode: %08x\\n\", new_stat);\n\t\tchange_display_mode(dfcyc);\n\t}\n}\n\n"
  },
  {
    "path": "gsplus/src/win32snd_driver.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2023 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// Audio is sent every 1/60th of a second to win32_send_audio().  Play it\n//  using waveOutWrite() as long as we've got at least 2/60th of a second\n//  buffered--otherwise waveOutPause().  If we get more than 10 buffers\n//  queued, drop buffers until we get down to 6 buffers queued again.\n\n// Track headers completed in g_wavehdr_rd_pos using callback function.\n\n#include \"defc.h\"\n#include \"sound.h\"\n\n#include <windows.h>\n#include <mmsystem.h>\n\nextern int Verbose;\n\nextern int g_audio_rate;\n\nunsigned int __stdcall child_sound_loop_win32(void *param);\nvoid check_wave_error(int res, char *str);\n\n#define NUM_WAVE_HEADERS\t32\n\nHWAVEOUT g_wave_handle;\nWAVEHDR g_wavehdr[NUM_WAVE_HEADERS];\n// Each header is for 1/60th of a second of sound (generally).  Pause\n//  until 2 headers are available, then unpause.  Experimentally it appears\n//  we keep about 5 headers (5/60th of a second = 80msec) ahead, which is\n//  excellent latency\n\nextern int g_audio_enable;\nextern word32 *g_sound_shm_addr;\nextern int g_preferred_rate;\n\nint\tg_win32snd_buflen = 0x1000;\nint\tg_win32_snd_playing = 0;\nint\tg_win32_snd_to_drop = 0;\nword32\tg_win32_snd_dropped = 0;\nvolatile int g_wavehdr_rd_pos = 0;\nvolatile int g_wavehdr_wr_pos = 0;\n\nvoid\nwin32snd_init(word32 *shmaddr)\n{\n\tprintf(\"win32snd_init\\n\");\n\tchild_sound_init_win32();\n}\n\nvoid\nwin32snd_shutdown()\n{\n\t/* hmm */\n\n}\n\nvoid CALLBACK\nhandle_wav_snd(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,\n\t\t\t\t\tDWORD_PTR dwParam1, DWORD_PTR dwParam2)\n{\n\tLPWAVEHDR lpwavehdr;\n\tint\tpos;\n\n\t/* Only service \"buffer done playing messages */\n\tif(uMsg == WOM_DONE) {\n\t\tlpwavehdr = (LPWAVEHDR)dwParam1;\n\t\tif(lpwavehdr->dwFlags == (WHDR_DONE | WHDR_PREPARED)) {\n\t\t\tlpwavehdr->dwUser = FALSE;\n\t\t}\n\t\tpos = (int)(lpwavehdr - &g_wavehdr[0]);\n\t\t// printf(\"At %.3f, pos %d is done\\n\", get_dtime(), pos);\n\t\tif(pos == g_wavehdr_rd_pos) {\n\t\t\tpos = (pos + 1) % NUM_WAVE_HEADERS;\n\t\t\tg_wavehdr_rd_pos = pos;\n\t\t} else {\n\t\t\tprintf(\"wavehdr %d finished, exp %d\\n\", pos,\n\t\t\t\t\t\t\tg_wavehdr_rd_pos);\n\t\t}\n\t}\n\n\treturn;\n}\n\n\nvoid\ncheck_wave_error(int res, char *str)\n{\n\tchar\tbuf[256];\n\n\tif(res == MMSYSERR_NOERROR) {\n\t\treturn;\n\t}\n\n\twaveOutGetErrorText(res, &buf[0], sizeof(buf));\n\tprintf(\"%s: %s\\n\", str, buf);\n\texit(1);\n}\n\nvoid\nchild_sound_init_win32()\n{\n\tWAVEFORMATEX wavefmt;\n\tWAVEOUTCAPS caps;\n\tbyte\t*bptr;\n\tUINT\twave_id;\n\tint\tbits_per_sample, channels, block_align, blen, res;\n\tint\ti;\n\n\tmemset(&wavefmt, 0, sizeof(WAVEFORMATEX));\n\n\twavefmt.wFormatTag = WAVE_FORMAT_PCM;\n\tbits_per_sample = 16;\n\tchannels = 2;\n\twavefmt.wBitsPerSample = bits_per_sample;\n\twavefmt.nChannels = channels;\n\twavefmt.nSamplesPerSec = g_preferred_rate;\n\tblock_align = channels * (bits_per_sample / 8);\n\twavefmt.nBlockAlign = block_align;\n\twavefmt.nAvgBytesPerSec = block_align * g_audio_rate;\n\n\tres = waveOutOpen(&g_wave_handle, WAVE_MAPPER, &wavefmt, 0, 0,\n\t\t\t\tWAVE_FORMAT_QUERY);\n\n\tif(res != MMSYSERR_NOERROR) {\n\t\tprintf(\"Cannot open audio device, res:%d, g_audio_rate:%d\\n\",\n\t\t\t\t\t\tres, g_preferred_rate);\n\t\tg_audio_enable = 0;\n\t\treturn;\n\t}\n\n\tres = waveOutOpen(&g_wave_handle, WAVE_MAPPER, &wavefmt,\n\t\t(DWORD_PTR)handle_wav_snd, 0,\n\t\tCALLBACK_FUNCTION | WAVE_ALLOWSYNC);\n\n\tif(res != MMSYSERR_NOERROR) {\n\t\tprintf(\"Cannot register audio\\n\");\n\t\tg_audio_enable = 0;\n\t\treturn;\n\t}\n\n\tg_audio_rate = wavefmt.nSamplesPerSec;\n\tblen = (((g_audio_rate * block_align) / 60) * 5) / 4;\n\t\t// Size buffer 25% larger than expected, to add some margin\n\tblen = (blen + 15) & -16L;\n\n\tg_win32snd_buflen = blen;\n\tbptr = malloc(blen * NUM_WAVE_HEADERS);\n\tif(bptr == NULL) {\n\t\tprintf(\"Unabled to allocate sound buffer\\n\");\n\t\texit(1);\n\t}\n\n\tfor(i = 0; i < NUM_WAVE_HEADERS; i++) {\n\t\tmemset(&g_wavehdr[i], 0, sizeof(WAVEHDR));\n\t\tg_wavehdr[i].dwUser = FALSE;\n\t\tg_wavehdr[i].lpData = (char *)&(bptr[i * blen]);\n\t\tg_wavehdr[i].dwBufferLength = blen;\n\t\tg_wavehdr[i].dwFlags = 0;\n\t\tg_wavehdr[i].dwLoops = 0;\n\t\tres = waveOutPrepareHeader(g_wave_handle, &g_wavehdr[i],\n\t\t\t\t\t\tsizeof(WAVEHDR));\n\t\tcheck_wave_error(res, \"waveOutPrepareHeader\");\n\t}\n\n\tres = waveOutGetID(g_wave_handle, &wave_id);\n\tres = waveOutGetDevCaps(wave_id, &caps, sizeof(caps));\n\tcheck_wave_error(res, \"waveOutGetDevCaps\");\n\tprintf(\"Using %s, buflen:%d\\n\", caps.szPname, g_win32snd_buflen);\n\tprintf(\" Bits per Sample = %d.  Channels = %d\\n\",\n\t\twavefmt.wBitsPerSample, wavefmt.nChannels);\n\tprintf(\" Sampling rate = %d, avg_bytes_per_sec = %d\\n\",\n\t\t(int)wavefmt.nSamplesPerSec, (int)wavefmt.nAvgBytesPerSec);\n\n\tsound_set_audio_rate(g_audio_rate);\n}\n\nvoid\nwin32snd_set_playing(int snd_playing)\n{\n\tg_win32_snd_playing = snd_playing;\n\tif(snd_playing) {\n\t\twaveOutRestart(g_wave_handle);\n#if 0\n\t\tprintf(\"win32 restarted sound wr:%d rd:%d\\n\", g_wavehdr_wr_pos,\n\t\t\t\t\t\t\tg_wavehdr_rd_pos);\n#endif\n\t} else {\n\t\twaveOutPause(g_wave_handle);\n#if 0\n\t\tprintf(\"win32 paused sound wr:%d rd:%d\\n\", g_wavehdr_wr_pos,\n\t\t\t\t\t\t\tg_wavehdr_rd_pos);\n#endif\n\t}\n}\n\nvoid\nwin32_send_audio2(byte *ptr, int size)\n{\n\tint\tres, wr_pos, rd_pos, new_pos, bufs_in_use;\n\n\twr_pos = g_wavehdr_wr_pos;\n\trd_pos = g_wavehdr_rd_pos;\n#if 0\n\tif(wr_pos == 0) {\n\t\tprintf(\"send_audio2 wr:%d rd:%d sz:%d at %.3f\\n\", wr_pos,\n\t\t\t\trd_pos, size, get_dtime());\n\t}\n#endif\n\tif(g_wavehdr[wr_pos].dwUser != FALSE) {\n\t\t// Audio buffer busy...should not happen!\n\t\tprintf(\"Audio buffer %d is busy!\\n\", wr_pos);\n\t\treturn;\n\t}\n\n\tbufs_in_use = (NUM_WAVE_HEADERS + wr_pos - rd_pos) % NUM_WAVE_HEADERS;\n\tif(g_win32_snd_to_drop) {\n\t\tg_win32_snd_to_drop--;\n\t\tg_win32_snd_dropped += size;\n\t\tif((bufs_in_use < 4) && (g_win32_snd_to_drop != 0)) {\n#if 0\n\t\t\tprintf(\"bufs_in_use:%d, snd_to_drop:%d\\n\", bufs_in_use,\n\t\t\t\t\t\t\tg_win32_snd_to_drop);\n#endif\n\t\t\tg_win32_snd_to_drop = 0;\n\t\t}\n\t\tif(g_win32_snd_to_drop == 0) {\n\t\t\tprintf(\"Dropped %d bytes of sound\\n\",\n\t\t\t\t\t\tg_win32_snd_dropped);\n\t\t}\n\t\treturn;\n\t}\n#if 0\n\tif(g_win32_snd_playing && (bufs_in_use <= 2)) {\n\t\tprintf(\"bufs_in_use:%d, wr:%d, rd:%d\\n\", bufs_in_use, wr_pos,\n\t\t\t\t\t\t\t\trd_pos);\n\t}\n#endif\n\tif(bufs_in_use == 0) {\n#if 0\n\t\tprintf(\"bufs_in_use:%d, wr_pos:%d rd_pos:%d\\n\", bufs_in_use,\n\t\t\twr_pos, rd_pos);\n#endif\n\t\t// We've underflowed, so pause sound until we get some buffered\n\t\twin32snd_set_playing(0);\n\t} else if(g_win32_snd_playing == 0) {\n\t\tif(bufs_in_use >= 2) {\n\t\t\t//printf(\"bufs_in_use:%d, will start\\n\", bufs_in_use);\n\t\t\twin32snd_set_playing(1);\n\t\t}\n\t} else {\n\t\tif(bufs_in_use >= 14) {\t\t// About 230msec\n\t\t\t// Drop 6 buffers to get us back down to 100msec delay\n\t\t\tprintf(\"bufs_in_use:%d, wr:%d will drop 6\\n\",\n\t\t\t\t\t\t\tbufs_in_use, wr_pos);\n\t\t\tg_win32_snd_to_drop = 6;\n\t\t\tg_win32_snd_dropped = 0;\n\t\t}\n\t}\n\tmemcpy(g_wavehdr[wr_pos].lpData, ptr, size);\n\tg_wavehdr[wr_pos].dwBufferLength = size;\n\tg_wavehdr[wr_pos].dwUser = TRUE;\n\n\tnew_pos = (wr_pos + 1) % NUM_WAVE_HEADERS;\n\tg_wavehdr_wr_pos = new_pos;\n\tres = waveOutWrite(g_wave_handle, &g_wavehdr[wr_pos],\n\t\t\t\t\t\t\tsizeof(g_wavehdr));\n\tcheck_wave_error(res, \"waveOutWrite\");\n\n\treturn;\n}\n\nint\nwin32_send_audio(byte *ptr, int in_size)\n{\n\tint\tsize;\n\tint\ttmpsize;\n\n\t// printf(\"send_audio %d bytes at %.3f\\n\", in_size, get_dtime());\n\tsize = in_size;\n\twhile(size > 0) {\n\t\ttmpsize = size;\n\t\tif(size > g_win32snd_buflen) {\n\t\t\ttmpsize = g_win32snd_buflen;\n\t\t}\n\t\twin32_send_audio2(ptr, tmpsize);\n\t\tptr += tmpsize;\n\t\tif(size != tmpsize) {\n#if 0\n\t\t\tprintf(\"Orig size:%d, reduced to %d\\n\", in_size,\n\t\t\t\t\t\t\t\ttmpsize);\n#endif\n\t\t}\n\t\tsize = size - tmpsize;\n\t}\n\n\treturn in_size;\n}\n\n"
  },
  {
    "path": "gsplus/src/win_dirent.h",
    "content": "#ifdef INCLUDE_RCSID_C\n#endif\n\n/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2022 Kent Dickey                      */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// Hacky defines to get something to compile for now\ntypedef unsigned short mode_t;\n\nstruct dirent {\n\tchar\td_name[1024];\n};\n\nstruct DIR_t {\n\tint\tfind_data_valid;\n\tvoid\t*win_handle;\n\tvoid\t*find_data_ptr;\n\tstruct dirent dirent;\n};\ntypedef struct DIR_t DIR;\n\nDIR *opendir(const char *filename);\nstruct dirent *readdir(DIR *dirp);\nint closedir(DIR *dirp);\n\n"
  },
  {
    "path": "gsplus/src/windriver.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2024 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// Based on code from Chea Chee Keong from KEGS32, which was available at\n//  http://www.geocities.com/akilgard/kegs32 (geocities is gone now)\n\n#define WIN32_LEAN_AND_MEAN\t/* Tell windows we want less header gunk */\n#define STRICT\t\t\t/* Tell Windows we want compile type checks */\n\n#include <windows.h>\n#include <windowsx.h>\n#include <mmsystem.h>\n#include <winsock.h>\n#include <commctrl.h>\n#include <io.h>\t\t\t/* For _get_osfhandle */\n\n#include \"defc.h\"\n#include \"win_dirent.h\"\n\nextern int Verbose;\n\ntypedef struct windowinfo {\n\tHWND\twin_hwnd;\n\tHDC\twin_dc;\n\tHDC\twin_cdc;\n\tBITMAPINFO *win_bmapinfo_ptr;\n\tBITMAPINFOHEADER *win_bmaphdr_ptr;\n\tHBITMAP\twin_dev_handle;\n\n\tKimage\t*kimage_ptr;\t// KEGS Image pointer for window content\n\tchar\t*name_str;\n\tbyte\t*data_ptr;\n\tint\tmotion;\n\tint\tmdepth;\n\tint\tactive;\n\tint\tpixels_per_line;\n\tint\tx_xpos;\n\tint\tx_ypos;\n\tint\twidth;\n\tint\theight;\n\tint\textra_width;\n\tint\textra_height;\n} Window_info;\n\n#include \"protos_windriver.h\"\n\nWindow_info g_mainwin_info = { 0 };\nWindow_info g_debugwin_info = { 0 };\n\nint\tg_win_max_width = 0;\nint\tg_win_max_height = 0;\nint\tg_num_a2_keycodes = 0;\n\nint\tg_win_button_states = 0;\nint\tg_win_hide_pointer = 0;\nint\tg_win_warp_pointer = 0;\nint\tg_win_warp_x = 0;\nint\tg_win_warp_y = 0;\n\n\n/* this table is used to search for the Windows VK_* in col 1 or 2 */\n/* flags bit 8 is or'ed into the VK, so we can distinguish keypad keys */\n/* regardless of numlock */\nint g_a2_key_to_wsym[][3] = {\n\t{ 0x35,\tVK_ESCAPE,\t0 },\n\t{ 0x7a,\tVK_F1,\t0 },\n\t{ 0x78,\tVK_F2,\t0 },\n\t{ 0x63,\tVK_F3,\t0 },\n\t{ 0x76,\tVK_F4,\t0 },\n\t{ 0x60,\tVK_F5,\t0 },\n\t{ 0x61,\tVK_F6,\t0 },\n\t{ 0x62,\tVK_F7,\t0 },\n\t{ 0x64,\tVK_F8,\t0 },\n\t{ 0x65,\tVK_F9,\t0 },\n\t{ 0x6d,\tVK_F10,\t0 },\n\t{ 0x67,\tVK_F11,\t0 },\n\t{ 0x6f,\tVK_F12,\t0 },\n\t{ 0x69,\tVK_F13,\t0 },\n\t{ 0x6b,\tVK_F14,\t0 },\n\t{ 0x71,\tVK_F15,\t0 },\n\t{ 0x7f, VK_PAUSE, VK_CANCEL+0x100 },\t// Reset\n\n\t{ 0x12,\t'1', 0 },\n\t{ 0x13,\t'2', 0 },\n\t{ 0x14,\t'3', 0 },\n\t{ 0x15,\t'4', 0 },\n\t{ 0x17,\t'5', 0 },\n\t{ 0x16,\t'6', 0 },\n\t{ 0x1a,\t'7', 0 },\n\t{ 0x1c,\t'8', 0 },\n\t{ 0x19,\t'9', 0 },\n\t{ 0x1d,\t'0', 0 },\n\t{ 0x1b,\t0xbd, 0 },\t\t/* '-' */\n\t{ 0x18,\t0xbb, 0 },\t\t/* '=' */\n\t{ 0x33,\tVK_BACK, 0 },\t\t/* backspace */\n\t{ 0x72,\tVK_INSERT+0x100, 0 },\t/* Insert key */\n\t{ 0x74,\tVK_PRIOR+0x100, 0 },\t/* pageup */\n\t{ 0x47,\tVK_NUMLOCK, VK_NUMLOCK+0x100 },\t/* clear */\n\t{ 0x51,\tVK_HOME+0x100, 0 },\t\t/* KP_equal is HOME key */\n\t{ 0x4b,\tVK_DIVIDE, VK_DIVIDE+0x100 },\t\t// KP /\n\t{ 0x43,\tVK_MULTIPLY, VK_MULTIPLY+0x100 },\t// KP *\n\n\t{ 0x30,\tVK_TAB, 0 },\n\t{ 0x32,\t0xc0, 0 },\t\t/* '`' */\n\t{ 0x0c,\t'Q', 0 },\n\t{ 0x0d,\t'W', 0 },\n\t{ 0x0e,\t'E', 0 },\n\t{ 0x0f,\t'R', 0 },\n\t{ 0x11,\t'T', 0 },\n\t{ 0x10,\t'Y', 0 },\n\t{ 0x20,\t'U', 0 },\n\t{ 0x22,\t'I', 0 },\n\t{ 0x1f,\t'O', 0 },\n\t{ 0x23,\t'P', 0 },\n\t{ 0x21,\t0xdb, 0 },\t\t/* [ */\n\t{ 0x1e,\t0xdd, 0 },\t\t/* ] */\n\t{ 0x2a,\t0xdc, 0 },\t\t/* backslash, bar */\n\t{ 0x75,\tVK_DELETE+0x100, 0 },\n\t{ 0x77,\tVK_END+0x100, VK_END },\n\t{ 0x79,\tVK_NEXT+0x100, 0 },\n\t{ 0x59,\tVK_NUMPAD7, VK_HOME },\n\t{ 0x5b,\tVK_NUMPAD8, VK_UP },\n\t{ 0x5c,\tVK_NUMPAD9, VK_PRIOR },\n\t{ 0x4e,\tVK_SUBTRACT, VK_SUBTRACT+0x100 },\n\n\t{ 0x39,\tVK_CAPITAL, 0 },  // Capslock\n\t{ 0x00,\t'A', 0 },\n\t{ 0x01,\t'S', 0 },\n\t{ 0x02,\t'D', 0 },\n\t{ 0x03,\t'F', 0 },\n\t{ 0x05,\t'G', 0 },\n\t{ 0x04,\t'H', 0 },\n\t{ 0x26,\t'J', 0 },\n\t{ 0x28,\t'K', 0 },\n\t{ 0x25,\t'L', 0 },\n\t{ 0x29,\t0xba, 0 },\t/* ; */\n\t{ 0x27,\t0xde, 0 },\t/* single quote */\n\t{ 0x24,\tVK_RETURN, 0 },\n\t{ 0x56,\tVK_NUMPAD4, VK_LEFT },\n\t{ 0x57,\tVK_NUMPAD5, VK_CLEAR },\n\t{ 0x58,\tVK_NUMPAD6, VK_RIGHT },\n\t{ 0x45,\tVK_ADD, 0 },\n\n\t{ 0x38,\tVK_SHIFT, 0 },\n\t{ 0x06,\t'Z', 0 },\n\t{ 0x07,\t'X', 0 },\n\t{ 0x08,\t'C', 0 },\n\t{ 0x09,\t'V', 0 },\n\t{ 0x0b,\t'B', 0 },\n\t{ 0x2d,\t'N', 0 },\n\t{ 0x2e,\t'M', 0 },\n\t{ 0x2b,\t0xbc, 0 },\t\t/* , */\n\t{ 0x2f,\t0xbe, 0 },\t\t/* . */\n\t{ 0x2c,\t0xbf, 0 },\t\t/* / */\n\t{ 0x3e,\tVK_UP+0x100, 0 },\n\t{ 0x53,\tVK_NUMPAD1, VK_END },\n\t{ 0x54,\tVK_NUMPAD2, VK_DOWN },\n\t{ 0x55,\tVK_NUMPAD3, VK_NEXT },\n\n\t{ 0x36,\tVK_CONTROL, VK_CONTROL+0x100 },\n\t{ 0x37,\tVK_SCROLL, VK_MENU+0x100 },\t// Command=scr_lock or alt-r\n\t{ 0x3a,\tVK_SNAPSHOT+0x100, VK_MENU },\t// Opt=prntscrn or alt-l\n\t{ 0x31,\t' ', 0 },\n\t{ 0x3b,\tVK_LEFT+0x100, 0 },\n\t{ 0x3d,\tVK_DOWN+0x100, 0 },\n\t{ 0x3c,\tVK_RIGHT+0x100, 0 },\n\t{ 0x52,\tVK_NUMPAD0, VK_INSERT },\n\t{ 0x41,\tVK_DECIMAL, VK_DELETE },\n\t{ 0x4c,\tVK_RETURN+0x100, 0 },\n\t{ -1, -1, -1 }\n};\n\n#if 0\nint\nwin_nonblock_read_stdin(int fd, char *bufptr, int len)\n{\n\tHANDLE\toshandle;\n\tDWORD\tdwret;\n\tint\tret;\n\n\terrno = EAGAIN;\n\toshandle = (HANDLE)_get_osfhandle(fd);\t// get stdin handle\n\tdwret = WaitForSingleObject(oshandle, 1);\t// wait 1msec for data\n\tret = -1;\n\tif(dwret == WAIT_OBJECT_0) {\n\t\tret = read(fd, bufptr, len);\n\t}\n\treturn ret;\n}\n#endif\n\nWindow_info *\nwin_find_win_info_ptr(HWND hwnd)\n{\n\tif(hwnd == g_mainwin_info.win_hwnd) {\n\t\treturn &g_mainwin_info;\n\t}\n\tif(hwnd == g_debugwin_info.win_hwnd) {\n\t\treturn &g_debugwin_info;\n\t}\n\treturn 0;\n}\n\nvoid\nwin_hide_pointer(Window_info *win_info_ptr, int do_hide)\n{\n\tShowCursor(!do_hide);\n\t// printf(\"Doing ShowCursor(%d)\\n\", !do_hide);\n}\n\nint\nwin_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y,\n\t\t\t\tint button_states, int buttons_valid)\n{\n\tKimage\t*kimage_ptr;\n\tint\tbuttons_changed, x, y;\n\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\tx = video_scale_mouse_x(kimage_ptr, raw_x, 0);\n\ty = video_scale_mouse_y(kimage_ptr, raw_y, 0);\n\n\t// printf(\"wum: %d,%d -> %d,%d\\n\", raw_x, raw_y, x, y);\n\n\tbuttons_changed = ((g_win_button_states & buttons_valid) !=\n\t\t\t\t\t\t\t\tbutton_states);\n\tg_win_button_states = (g_win_button_states & ~buttons_valid) |\n\t\t\t\t\t(button_states & buttons_valid);\n\tif(g_win_warp_pointer && (raw_x == g_win_warp_x) &&\n\t\t\t(raw_y == g_win_warp_y) && (!buttons_changed) ) {\n\t\t/* tell adb routs to recenter but ignore this motion */\n\t\tadb_update_mouse(kimage_ptr, x, y, 0, -1);\n\t\treturn 0;\n\t}\n\treturn adb_update_mouse(kimage_ptr, x, y, button_states,\n\t\t\t\t\t\t\tbuttons_valid & 7);\n}\n\nvoid\nwin_event_mouse(HWND hwnd, WPARAM wParam, LPARAM lParam)\n{\n\tWindow_info *win_info_ptr;\n\tword32\tflags;\n\tint\tbuttons, x, y, hide, warp;\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(!win_info_ptr) {\n\t\treturn;\n\t}\n\n\tflags = (word32)wParam;\n\tx = LOWORD(lParam);\n\ty = HIWORD(lParam);\n\n\tbuttons = (flags & 1) | (((flags >> 1) & 1) << 2) |\n\t\t\t\t\t\t(((flags >> 4) & 1) << 1);\n#if 0\n\tprintf(\"Mouse at %d, %d fl: %08x, but: %d\\n\", x, y, flags, buttons);\n#endif\n\twin_info_ptr->motion |= win_update_mouse(win_info_ptr, x, y, buttons,\n\t\t\t\t\t\t\t\t\t7);\n\n\thide = 0;\n\twarp = 0;\n\thide = adb_get_hide_warp_info(win_info_ptr->kimage_ptr, &warp);\n\tif(warp != g_win_warp_pointer) {\n\t\twin_info_ptr->motion = 1;\n\t}\n\tg_win_warp_pointer = warp;\n\tif(g_win_hide_pointer != hide) {\n\t\twin_hide_pointer(win_info_ptr, hide);\n\t}\n\tg_win_hide_pointer = hide;\n}\n\nvoid\nwin_event_key(HWND hwnd, WPARAM wParam, LPARAM lParam, int down)\n{\n\tWindow_info *win_info_ptr;\n\tKimage\t*kimage_ptr;\n\tword32\tvk, raw_vk, flags, capslock_state;\n\tint\ta2code, is_up;\n\tint\ti;\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(!win_info_ptr) {\n\t\treturn;\n\t}\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\traw_vk = (word32)wParam;\n\tflags = HIWORD(lParam);\n#if 0\n\tprintf(\"win_event_key: raw:%04x lParam:%08x d:%d flags:%08x\\n\",\n\t\traw_vk, (word32)lParam, down, flags);\n#endif\n\n\tif((flags & 0x4000) && down) {\n\t\t/* auto-repeating, just ignore it */\n\t\treturn;\n\t}\n\n\tvk = raw_vk + (flags & 0x100);\n#if 0\n\tprintf(\"Key event, vk=%04x, down:%d, repeat: %d, flags: %08x\\n\",\n\t\t\tvk, down, repeat, flags);\n#endif\n\n\t/* remap a few keys here.. sigh */\n\tif((vk & 0xff) == VK_APPS) {\n\t\t/* remap to command */\n\t\tvk = VK_MENU;\n\t}\n\n\tif((vk & 0xff) == VK_CAPITAL) {\n\t\t// Fix up capslock info: Windows gives us a down, then up event\n\t\t//  when the capslock key itself is pressed and released.  We\n\t\t//  need to ask for the true toggle state instead\n\t\tcapslock_state = (GetKeyState(VK_CAPITAL) & 1);\n\t\tdown = capslock_state;\n\t}\n\n\t/* search a2key_to_wsym to find wsym in col 1 or 2 */\n\ti = 0;\n\tis_up = !down;\n\tfor(i = g_num_a2_keycodes-1; i >= 0; i--) {\n\t\ta2code = g_a2_key_to_wsym[i][0];\n\t\tif((vk == g_a2_key_to_wsym[i][1]) ||\n\t\t\t\t\t(vk == g_a2_key_to_wsym[i][2])) {\n\t\t\tvid_printf(\"Found vk:%04x = %02x\\n\", vk, a2code);\n\t\t\tadb_physical_key_update(kimage_ptr, a2code, 0, is_up);\n\t\t\treturn;\n\t\t}\n\t}\n\tprintf(\"VK: %04x unknown\\n\", vk);\n}\n\nvoid\nwin_event_redraw(HWND hwnd)\n{\n\tWindow_info *win_info_ptr;\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\n\tif(win_info_ptr) {\n\t\tvideo_set_x_refresh_needed(win_info_ptr->kimage_ptr, 1);\n\t}\n}\n\nvoid\nwin_event_destroy(HWND hwnd)\n{\n\tWindow_info *win_info_ptr;\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(win_info_ptr == 0) {\n\t\treturn;\n\t}\n\tvideo_set_active(win_info_ptr->kimage_ptr, 0);\n\twin_info_ptr->active = 0;\n\tif(win_info_ptr == &g_mainwin_info) {\n\t\tmy_exit(0);\n\t} else {\n\t\tShowWindow(win_info_ptr->win_hwnd, SW_HIDE);\n\t\tReleaseDC(hwnd, win_info_ptr->win_dc);\n\t\tDeleteDC(win_info_ptr->win_cdc);\n\t\tDeleteObject(win_info_ptr->win_dev_handle);\n\t\tGlobalFree(win_info_ptr->win_bmapinfo_ptr);\n\t\twin_info_ptr->win_hwnd = 0;\n\t\twin_info_ptr->data_ptr = 0;\n\t}\n}\n\nvoid\nwin_event_move(HWND hwnd, WPARAM wParam, LPARAM lParam)\n{\n\tWindow_info *win_info_ptr;\n\tint\tx_xpos, x_ypos;\n\n\t// These WM_MOVE events indicate the window is being moved\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(!win_info_ptr) {\n\t\treturn;\n\t}\n\t// printf(\"WM_MOVE: %04x %08x\\n\", (word32)wParam, (word32)lParam);\n\tx_xpos = lParam & 0xffff;\n\tx_ypos = (lParam >> 16) & 0xffff;\n\tvideo_update_xpos_ypos(win_info_ptr->kimage_ptr, x_xpos, x_ypos);\n}\n\nvoid\nwin_event_size(HWND hwnd, WPARAM wParam, LPARAM lParam)\n{\n\tWindow_info *win_info_ptr;\n\tint\twidth, height;\n\n\t// These WM_SIZE events indicate the window is being resized\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(!win_info_ptr) {\n\t\treturn;\n\t}\n\t// printf(\"WM_SIZE: %04x %08x\\n\", (word32)wParam, (word32)lParam);\n\twidth = lParam & 0xffff;\n\theight = (lParam >> 16) & 0xffff;\n\tvideo_update_scale(win_info_ptr->kimage_ptr, width, height, 0);\n#if 0\n\tprintf(\"Frac width: %f\\n\",\n\t\twin_info_ptr->kimage_ptr->scale_width_a2_to_x / 65536.0);\n#endif\n\n\t// The following try to do \"live updating\" of the resize\n\twin_info_ptr->kimage_ptr->x_refresh_needed = 1;\n\tx_update_display(win_info_ptr);\n}\n\nvoid\nwin_event_minmaxinfo(HWND hwnd, LPARAM lParam)\n{\n\tWindow_info *win_info_ptr;\n\tMINMAXINFO *minmax_ptr;\n\tint\ta2_width, a2_height;\n\n\t// Windows sends WM_GETMINMAXINFO events when resizing is occurring,\n\t//  and we can modify the *lParam MINMAXINFO structure to set the\n\t//  minimum and maximum Track size (the size of the window)\n\t// This code forces the minimum to be the A2 window size, and the\n\t//  maximum to be the screen size\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(!win_info_ptr) {\n\t\treturn;\n\t}\n\tminmax_ptr = (MINMAXINFO *)lParam;\n#if 0\n\tprintf(\"MinMax: mintrack.x:%d, mintrack.y:%d\\n\",\n\t\tminmax_ptr->ptMinTrackSize.x,\n\t\tminmax_ptr->ptMinTrackSize.y);\n#endif\n\ta2_width = video_get_a2_width(win_info_ptr->kimage_ptr);\n\ta2_height = video_get_a2_height(win_info_ptr->kimage_ptr);\n\tminmax_ptr->ptMinTrackSize.x = a2_width + win_info_ptr->extra_width;\n\tminmax_ptr->ptMinTrackSize.y = a2_height + win_info_ptr->extra_height;\n\tminmax_ptr->ptMaxTrackSize.x = g_win_max_width +\n\t\t\t\t\t\twin_info_ptr->extra_width;\n\tminmax_ptr->ptMaxTrackSize.y = g_win_max_height +\n\t\t\t\t\t\twin_info_ptr->extra_height;\n}\n\nvoid\nwin_event_focus(HWND hwnd, int gain_focus)\n{\n\tWindow_info *win_info_ptr;\n\tword32\tc025_val, info;\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(!win_info_ptr) {\n\t\treturn;\n\t}\n\n\tif(gain_focus) {\n\t\t// printf(\"Got focus on %p\\n\", hwnd);\n\t\t// Get shift, ctrl, capslock state\n\t\tc025_val = 0;\n\t\tinfo = GetKeyState(VK_SHIFT);\t\t// left or right\n\t\tif(info & 0x8000) {\n\t\t\tc025_val |= 1;\t\t\t// Shift key is down\n\t\t}\n\t\tinfo = GetKeyState(VK_CONTROL);\t\t// left or right\n\t\tif(info & 0x8000) {\n\t\t\tc025_val |= 2;\n\t\t}\n\t\tinfo = GetKeyState(VK_CAPITAL);\t\t// Capslock?\n\t\tif(info & 1) {\n\t\t\tc025_val |= 4;\t\t\t// Capslock key is down\n\t\t}\n\t\t//printf(\"Calling update_c025 with %03x\\n\", c025_val);\n\t\tadb_update_c025_mask(win_info_ptr->kimage_ptr, c025_val, 7);\n\t} else {\n\t\t// printf(\"Lost focus on %p\\n\", hwnd);\n\t}\n\tif(win_info_ptr == &g_mainwin_info) {\n\t\tadb_kbd_repeat_off();\n\t\tadb_mainwin_focus(gain_focus);\n\t}\n}\n\nLRESULT CALLBACK\nwin_event_handler(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam)\n{\n\n#if 0\n\tprintf(\"Message: umsg: %04x, wparam:%04x lParam:%08x\\n\", umsg,\n\t\t\t(word32)wParam, (word32)lParam);\n#endif\n\n\tswitch(umsg) {\n\tcase WM_MOUSEMOVE:\n\tcase WM_LBUTTONDOWN:\n\tcase WM_LBUTTONUP:\n\tcase WM_MBUTTONDOWN:\n\tcase WM_MBUTTONUP:\n\tcase WM_RBUTTONDOWN:\n\tcase WM_RBUTTONUP:\n\t\twin_event_mouse(hwnd, wParam, lParam);\n\t\treturn 0;\n\tcase WM_KEYUP:\n\tcase WM_SYSKEYUP:\n\t\twin_event_key(hwnd, wParam, lParam, 0);\n\t\treturn 0;\n\tcase WM_KEYDOWN:\n\tcase WM_SYSKEYDOWN:\n\t\twin_event_key(hwnd, wParam, lParam, 1);\n\t\treturn 0;\n\tcase WM_SYSCOMMAND:\n\t\t// Alt key press can cause this.  Return 0 for SC_KEYMENU\n\t\tif(wParam == SC_KEYMENU) {\n\t\t\treturn 0;\n\t\t}\n\t\tbreak;\n\tcase WM_KILLFOCUS:\n\t\twin_event_focus(hwnd, 0);\n\t\tbreak;\n\tcase WM_SETFOCUS:\n\t\twin_event_focus(hwnd, 1);\n\t\tbreak;\n\tcase WM_DESTROY:\n\t\twin_event_destroy(hwnd);\n\t\treturn 0;\n\tcase WM_PAINT:\n\t\twin_event_redraw(hwnd);\n\t\tbreak;\n\tcase WM_MOVE:\n\t\twin_event_move(hwnd, wParam, lParam);\n\t\tbreak;\n\tcase WM_SIZE:\n\t\twin_event_size(hwnd, wParam, lParam);\n\t\tbreak;\n\tcase WM_GETMINMAXINFO:\n\t\twin_event_minmaxinfo(hwnd, lParam);\n\t\tbreak;\n\t}\n#if 0\n\tswitch(umsg) {\n\t\tHANDLE_MSG(hwnd, WM_KEYUP, win_event_key);\n\t\tHANDLE_MSG(hwnd, WM_KEYDOWN, win_event_key);\n\t\tHANDLE_MSG(hwnd, WM_SYSKEYUP, win_event_key);\n\t\tHANDLE_MSG(hwnd, WM_SYSKEYDOWN, win_event_key);\n\t\tHANDLE_MSG(hwnd, WM_DESTROY, win_event_destroy);\n\t}\n#endif\n\n#if 0\n\tswitch(umsg) {\n\tcase WM_NCACTIVATE:\n\tcase WM_NCHITTEST:\n\tcase WM_NCMOUSEMOVE:\n\tcase WM_SETCURSOR:\n\tcase WM_LBUTTONDOWN:\n\tcase WM_LBUTTONUP:\n\tcase WM_RBUTTONDOWN:\n\tcase WM_CONTEXTMENU:\n\tcase WM_RBUTTONUP:\n\tcase WM_MBUTTONDOWN:\n\tcase WM_MBUTTONUP:\n\tcase WM_PAINT:\n\n\t\tbreak;\n\tdefault:\n\t\tprintf(\"Got umsg2: %d\\n\", umsg);\n\t}\n#endif\n\n\treturn DefWindowProc(hwnd, umsg, wParam, lParam);\n}\n\n\nint\nmain(int argc, char **argv)\n{\n\tint\tret, mdepth;\n\n\tret = parse_argv(argc, argv, 1);\n\tif(ret) {\n\t\tprintf(\"parse_argv ret: %d, stopping\\n\", ret);\n\t\texit(1);\n\t}\n\n\tmdepth = 32;\n\n\tvideo_set_blue_mask(0x0000ff);\n\tvideo_set_green_mask(0x00ff00);\n\tvideo_set_red_mask(0xff0000);\n\n\tg_win_max_width = GetSystemMetrics(SM_CXSCREEN);\n\tg_win_max_height = GetSystemMetrics(SM_CYSCREEN);\n\tvid_printf(\"g_win_max_width:%d, g_win_max_height:%d\\n\",\n\t\t\t\t\tg_win_max_width, g_win_max_height);\n\n\tret = kegs_init(mdepth, g_win_max_width, g_win_max_height, 0);\n\tprintf(\"kegs_init done\\n\");\n\tif(ret) {\n\t\tprintf(\"kegs_init ret: %d, stopping\\n\", ret);\n\t\texit(1);\n\t}\n\n\twin_video_init(mdepth);\n\n\tprintf(\"Entering main loop!\\n\");\n\tfflush(stdout);\n\twhile(1) {\n\t\tret = run_16ms();\n\t\tif(ret != 0) {\n\t\t\tprintf(\"run_16ms returned: %d\\n\", ret);\n\t\t\tbreak;\n\t\t}\n\t\tx_update_display(&g_mainwin_info);\n\t\tx_update_display(&g_debugwin_info);\n\t\tcheck_input_events();\n\t}\n\txdriver_end();\n\texit(0);\n}\n\nvoid\ncheck_input_events()\n{\n\tMSG\tmsg;\n\tPOINT\tpt;\n\tBOOL\tret;\n\tWindow_info *win_info_ptr;\n\n\twhile(PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) {\n\t\tif(GetMessage(&msg, 0, 0, 0) > 0) {\n\t\t\t//TranslateMessage(&msg);\n\t\t\tDispatchMessage(&msg);\n\t\t} else {\n\t\t\tprintf(\"GetMessage returned <= 0\\n\");\n\t\t\tmy_exit(2);\n\t\t}\n\t}\n\n\twin_info_ptr = &g_mainwin_info;\n\tif(win_info_ptr->motion == 0) {\n\t\treturn;\n\t}\n\n\t// ONLY look at g_mainwin_info!\n\twin_info_ptr->motion = 0;\n\n\tif(g_win_warp_pointer) {\n\t\t/* move mouse to center of screen */\n\t\tg_win_warp_x = video_unscale_mouse_x(win_info_ptr->kimage_ptr,\n\t\t\tBASE_MARGIN_LEFT + (A2_WINDOW_WIDTH/2), 0);\n\t\tg_win_warp_y = video_unscale_mouse_y(win_info_ptr->kimage_ptr,\n\t\t\tBASE_MARGIN_TOP + (A2_WINDOW_HEIGHT/2), 0);\n\t\tpt.x = g_win_warp_x;\n\t\tpt.y = g_win_warp_y;\n\t\tClientToScreen(win_info_ptr->win_hwnd, &pt);\n\t\tret = SetCursorPos(pt.x, pt.y);\n#if 0\n\t\tprintf(\"Did SetCursorPos(%d, %d) warp_x:%d,y:%d, ret:%d\\n\",\n\t\t\tpt.x, pt.y, g_win_warp_x, g_win_warp_y, ret);\n#endif\n\t}\n\n\treturn;\n}\n\nvoid\nwin_video_init(int mdepth)\n{\n\tWNDCLASS wndclass;\n\tint\ta2code;\n\tint\ti;\n\n\tvideo_set_palette();\n\n\tg_num_a2_keycodes = 0;\n\tfor(i = 0; i < 0x7f; i++) {\n\t\ta2code = g_a2_key_to_wsym[i][0];\n\t\tif(a2code < 0) {\n\t\t\tg_num_a2_keycodes = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\twndclass.style = 0;\n\twndclass.lpfnWndProc = (WNDPROC)win_event_handler;\n\twndclass.cbClsExtra = 0;\n\twndclass.cbWndExtra = 0;\n\twndclass.hInstance = GetModuleHandle(NULL);\n\twndclass.hIcon = LoadIcon((HINSTANCE)NULL, IDI_APPLICATION);\n\twndclass.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);\n\twndclass.hbrBackground = GetStockObject(WHITE_BRUSH);\n\twndclass.lpszMenuName = NULL;\n\twndclass.lpszClassName = \"kegswin\";\n\n\t// Register the window\n\tif(!RegisterClass(&wndclass)) {\n\t\tprintf(\"Registering window failed\\n\");\n\t\texit(1);\n\t}\n\n\twin_init_window(&g_mainwin_info, video_get_kimage(0), \"KEGS\", mdepth);\n\twin_init_window(&g_debugwin_info, video_get_kimage(1),\n\t\t\t\t\t\t\"KEGS Debugger\", mdepth);\n\n\twin_create_window(&g_mainwin_info);\n}\n\nvoid\nwin_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str,\n\t\t\t\t\t\t\tint mdepth)\n{\n\tint\theight, width, x_xpos, x_ypos;\n\n\theight = video_get_x_height(kimage_ptr);\n\twidth = video_get_x_width(kimage_ptr);\n\n\tx_xpos = video_get_x_xpos(kimage_ptr);\n\tx_ypos = video_get_x_ypos(kimage_ptr);\n\n\twin_info_ptr->win_hwnd = 0;\n\twin_info_ptr->win_dc = 0;\n\twin_info_ptr->win_cdc = 0;\n\twin_info_ptr->win_bmapinfo_ptr = 0;\n\twin_info_ptr->win_bmaphdr_ptr = 0;\n\twin_info_ptr->win_dev_handle = 0;\n\twin_info_ptr->kimage_ptr = kimage_ptr;\n\twin_info_ptr->name_str = name_str;\n\twin_info_ptr->data_ptr = 0;\n\twin_info_ptr->motion = 0;\n\twin_info_ptr->mdepth = mdepth;\n\twin_info_ptr->active = 0;\n\twin_info_ptr->pixels_per_line = width;\n\twin_info_ptr->x_xpos = x_xpos;\n\twin_info_ptr->x_ypos = x_ypos;\n\twin_info_ptr->width = width;\n\twin_info_ptr->height = height;\n}\n\nvoid\nwin_create_window(Window_info *win_info_ptr)\n{\n\tHWND\twin_hwnd;\n\tRECT\trect;\n\tBITMAPINFO *bmapinfo_ptr;\n\tBITMAPINFOHEADER *bmaphdr_ptr;\n\tHBITMAP\twin_dev_handle;\n\tKimage\t*kimage_ptr;\n\tint\theight, width, extra_width, extra_height;\n\tint\textra_size, w_flags;\n\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\n\theight = win_info_ptr->height;\n\twidth = win_info_ptr->width;\n\n\tprintf(\"Got height: %d, width:%d\\n\", height, width);\n\n\t// We must call CreateWindow with a width,height that accounts for\n\t//  the title bar and any other stuff.  Use AdjustWindowRect() to\n\t//  calculate this info for us\n\tw_flags = WS_TILED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |\n\t\t\t\t\t\t\t\tWS_SIZEBOX;\n\trect.left = 0;\n\trect.top = 0;\n\trect.right = width;\n\trect.bottom = height;\n\t(void)AdjustWindowRect(&rect, w_flags, 0);\n\textra_width = rect.right - rect.left - width;\n\textra_height = rect.bottom - rect.top - height;\n\twin_info_ptr->extra_width = extra_width;\n\twin_info_ptr->extra_height = extra_height;\n\twin_hwnd = CreateWindow(\"kegswin\", win_info_ptr->name_str, w_flags,\n\t\twin_info_ptr->x_xpos, win_info_ptr->x_ypos, width + extra_width,\n\t\theight + extra_height, NULL, NULL, GetModuleHandle(NULL), NULL);\n\twin_info_ptr->win_hwnd = win_hwnd;\n\twin_info_ptr->active = 0;\n\n\tvideo_set_active(kimage_ptr, 1);\n\tvideo_update_scale(kimage_ptr, win_info_ptr->width,\n\t\t\t\t\t\twin_info_ptr->height, 1);\n\n\tprintf(\"win_hwnd = %p, height = %d\\n\", win_hwnd, height);\n\tGetWindowRect(win_hwnd, &rect);\n\tprintf(\"...rect is: %ld, %ld, %ld, %ld\\n\", rect.left, rect.top,\n\t\trect.right, rect.bottom);\n\n\twin_info_ptr->win_dc = GetDC(win_hwnd);\n\n\tSetTextColor(win_info_ptr->win_dc, 0);\n\tSetBkColor(win_info_ptr->win_dc, 0xffffff);\n\n\twin_info_ptr->win_cdc = CreateCompatibleDC(win_info_ptr->win_dc);\n\tprintf(\"win_cdc: %p, win_dc:%p\\n\", win_info_ptr->win_cdc,\n\t\t\t\t\t\twin_info_ptr->win_dc);\n\n\n\tprintf(\"Getting height, kimage_ptr:%p\\n\", kimage_ptr);\n\tfflush(stdout);\n\n\twin_info_ptr->data_ptr = 0;\n\n\textra_size = sizeof(RGBQUAD);\n\tbmapinfo_ptr = (BITMAPINFO *)GlobalAlloc(GPTR,\n\t\t\tsizeof(BITMAPINFOHEADER) + extra_size);\n\twin_info_ptr->win_bmapinfo_ptr = bmapinfo_ptr;\n\n\tbmaphdr_ptr = (BITMAPINFOHEADER *)bmapinfo_ptr;\n\twin_info_ptr->win_bmaphdr_ptr = bmaphdr_ptr;\n\tbmaphdr_ptr->biSize = sizeof(BITMAPINFOHEADER);\n\tbmaphdr_ptr->biWidth = g_win_max_width;\n\tbmaphdr_ptr->biHeight = -g_win_max_height;\n\tbmaphdr_ptr->biPlanes = 1;\n\tbmaphdr_ptr->biBitCount = win_info_ptr->mdepth;\n\tbmaphdr_ptr->biCompression = BI_RGB;\n\tbmaphdr_ptr->biClrUsed = 0;\n\n\t/* Use g_bmapinfo_ptr, adjusting width, height */\n\tprintf(\"bmaphdr_ptr:%p\\n\", bmaphdr_ptr);\n\n\tprintf(\"About to call CreateDIBSection, win_dc:%p\\n\",\n\t\t\t\t\t\twin_info_ptr->win_dc);\n\tfflush(stdout);\n\twin_dev_handle = CreateDIBSection(win_info_ptr->win_dc,\n\t\t\twin_info_ptr->win_bmapinfo_ptr, DIB_RGB_COLORS,\n\t\t\t(VOID **)&(win_info_ptr->data_ptr), NULL, 0);\n\twin_info_ptr->win_dev_handle = win_dev_handle;\n\n\twin_info_ptr->pixels_per_line = g_win_max_width;\n\tprintf(\"kim: %p, dev:%p data: %p\\n\", kimage_ptr,\n\t\t\twin_dev_handle, win_info_ptr->data_ptr);\n\tfflush(stdout);\n}\n\nvoid\nxdriver_end()\n{\n\tprintf(\"xdriver_end\\n\");\n}\n\nvoid\nwin_resize_window(Window_info *win_info_ptr)\n{\n\tRECT\trect1, rect2;\n\tBOOL\tret;\n\tKimage\t*kimage_ptr;\n\tint\tx_width, x_height;\n\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\tx_width = video_get_x_width(kimage_ptr);\n\tx_height = video_get_x_height(kimage_ptr);\n\t// printf(\"win_resize_window, x_w:%d, x_h:%d\\n\", x_width, x_height);\n\n\tret = GetWindowRect(win_info_ptr->win_hwnd, &rect1);\n\tret = GetClientRect(win_info_ptr->win_hwnd, &rect2);\n#if 0\n\tprintf(\"window_rect: l:%d, t:%d, r:%d, b:%d\\n\",\n\t\t\trect1.left, rect1.top, rect1.right, rect1.bottom);\n\tprintf(\"client_rect: l:%d, t:%d, r:%d, b:%d\\n\",\n\t\t\trect2.left, rect2.top, rect2.right, rect2.bottom);\n#endif\n\tret = MoveWindow(win_info_ptr->win_hwnd, rect1.left, rect1.top,\n\t\tx_width + win_info_ptr->extra_width,\n\t\tx_height + win_info_ptr->extra_height, TRUE);\n\t// printf(\"MoveWindow ret:%d\\n\", ret);\n\twin_info_ptr->width = x_width;\n\twin_info_ptr->height = x_height;\n}\n\nvoid\nx_update_display(Window_info *win_info_ptr)\n{\n\tChange_rect rect;\n\tvoid\t*bitm_old;\n\t//POINT\tpoint;\n\tint\tvalid, a2_active, x_active;\n\tint\ti;\n\n\ta2_active = video_get_active(win_info_ptr->kimage_ptr);\n\tx_active = win_info_ptr->active;\n\n\tif(x_active && !a2_active) {\n\t\t// We need to SW_HIDE this window\n\t\tShowWindow(win_info_ptr->win_hwnd, SW_HIDE);\n\t\tx_active = 0;\n\t\twin_info_ptr->active = x_active;\n\t}\n\tif(!x_active && a2_active) {\n\t\t// We need to SW_SHOWDEFAULT this window (and maybe create it)\n\t\tif(win_info_ptr->win_hwnd == 0) {\n\t\t\twin_create_window(win_info_ptr);\n\t\t}\n\t\tShowWindow(win_info_ptr->win_hwnd, SW_SHOWDEFAULT);\n\t\tUpdateWindow(win_info_ptr->win_hwnd);\n\t\tx_active = 1;\n\t\twin_info_ptr->active = x_active;\n\t}\n\tif(x_active == 0) {\n\t\treturn;\n\t}\n\n\tif(video_change_aspect_needed(win_info_ptr->kimage_ptr,\n\t\t\t\twin_info_ptr->width, win_info_ptr->height)) {\n\t\twin_resize_window(win_info_ptr);\n\t}\n\tfor(i = 0; i < MAX_CHANGE_RECTS; i++) {\n\t\tvalid = video_out_data(win_info_ptr->data_ptr,\n\t\t\twin_info_ptr->kimage_ptr, win_info_ptr->pixels_per_line,\n\t\t\t&rect, i);\n\t\tif(!valid) {\n\t\t\tbreak;\n\t\t}\n#if 0\n\t\tpoint.x = 0;\n\t\tpoint.y = 0;\n\t\tClientToScreen(win_info_ptr->win_hwnd, &point);\n#endif\n\t\tbitm_old = SelectObject(win_info_ptr->win_cdc,\n\t\t\t\t\twin_info_ptr->win_dev_handle);\n\n\t\tBitBlt(win_info_ptr->win_dc, rect.x, rect.y, rect.width,\n\t\t\trect.height, win_info_ptr->win_cdc, rect.x, rect.y,\n\t\t\tSRCCOPY);\n\n\t\tSelectObject(win_info_ptr->win_cdc, bitm_old);\n\t}\n}\n\nvoid\nx_hide_pointer(int do_hide)\n{\n\tif(do_hide) {\n\t\tShowCursor(0);\n\t} else {\n\t\tShowCursor(1);\n\t}\n}\n\nint\nopendir_int(DIR *dirp, const char *in_filename)\n{\n\tHANDLE\thandle1;\n\twchar_t\t*wcstr;\n\tchar\t*filename;\n\tsize_t\tret_val;\n\tint\tbuflen, len;\n\tint\ti;\n\n\tprintf(\"opendir on %s\\n\", in_filename);\n\tlen = (int)strlen(in_filename);\n\tbuflen = len + 8;\n\tif(buflen >= sizeof(dirp->dirent.d_name)) {\n\t\tprintf(\"buflen %d >= d_name %d\\n\", buflen,\n\t\t\t\t\t(int)sizeof(dirp->dirent.d_name));\n\t\treturn 1;\n\t}\n\tfilename = &dirp->dirent.d_name[0];\n\tmemcpy(filename, in_filename, len + 1);\n\twhile(len && (filename[len-1] == '/')) {\n\t\tfilename[len - 1] = 0;\n\t\tlen--;\n\t}\n\tcfg_strlcat(filename, \"/*.*\", buflen);\n\tfor(i = 0; i < len; i++) {\n\t\tif(filename[i] == '/') {\n\t\t\tfilename[i] = '\\\\';\n\t\t}\n\t}\n\tlen = (int)strlen(filename);\n\twcstr = malloc(buflen * 2);\n\t(void)mbstowcs_s(&ret_val, wcstr, buflen, filename, _TRUNCATE);\n\thandle1 = FindFirstFileW(wcstr, dirp->find_data_ptr);\n\tdirp->win_handle = handle1;\n\tfree(wcstr);\n\tif(handle1) {\n\t\tdirp->find_data_valid = 1;\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n\nDIR *\nopendir(const char *in_filename)\n{\n\tDIR\t*dirp;\n\tint\tret;\n\n\tdirp = calloc(1, sizeof(DIR));\n\tif(!dirp) {\n\t\treturn 0;\n\t}\n\tdirp->find_data_valid = 1;\n\tdirp->find_data_ptr = calloc(1, sizeof(WIN32_FIND_DATAW));\n\tret = 1;\n\tif(dirp->find_data_ptr) {\n\t\tret = opendir_int(dirp, in_filename);\n\t}\n\tif(ret) {\t\t// Bad\n\t\tfree(dirp->find_data_ptr);\t\t// free(0) is OK\n\t\tfree(dirp);\n\t\treturn 0;\n\t}\n\treturn dirp;\n}\n\nstruct dirent *\nreaddir(DIR *dirp)\n{\n\tWIN32_FIND_DATAW *find_data_ptr;\n\tHANDLE\thandle1;\n\tsize_t\tret_val;\n\tBOOL\tret;\n\n\thandle1 = dirp->win_handle;\n\tfind_data_ptr = dirp->find_data_ptr;\n\tif(!handle1 || !find_data_ptr) {\n\t\treturn 0;\n\t}\n\tret = 1;\n\tif(!dirp->find_data_valid) {\n\t\tif(handle1) {\n\t\t\tfind_data_ptr->cFileName[MAX_PATH-1] = 0;\n\t\t\tret = FindNextFileW(handle1, find_data_ptr);\n\t\t}\n\t}\n\tdirp->find_data_valid = 0;\n\tif(!ret) {\n\t\treturn 0;\n\t}\n\t(void)wcstombs_s(&ret_val, &(dirp->dirent.d_name[0]),\n\t\t\t\t(int)sizeof(dirp->dirent.d_name),\n\t\t\t\t&(find_data_ptr->cFileName[0]), _TRUNCATE);\n\tprintf(\"Returning file %s\\n\", &(dirp->dirent.d_name[0]));\n\n\treturn &(dirp->dirent);;\n}\n\nint\nclosedir(DIR *dirp)\n{\n\tFindClose(dirp->win_handle);\n\tfree(dirp->find_data_ptr);\n\tfree(dirp);\n\n\treturn 0;\n}\n\nint\nlstat(const char *path, struct stat *bufptr)\n{\n\treturn stat(path, bufptr);\n}\n\nint\nftruncate(int fd, word32 length)\n{\n\tHANDLE\thandle1;\n\n\thandle1 = (HANDLE)_get_osfhandle(fd);\n\tSetFilePointer(handle1, length, 0, FILE_BEGIN);\n\tSetEndOfFile(handle1);\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "gsplus/src/woz.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2025 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n// Based on WOZ 2.0/1.0 spec at https://applesaucefdc.com/woz/reference2/\n\n#include \"defc.h\"\n\nextern const char g_kegs_version_str[];\n\nbyte g_woz_hdr_bytes[12] = { 0x57, 0x4f, 0x5a, 0x32,\t\t// \"WOZ2\"\n\t\t\t\t0xff, 0x0a, 0x0d, 0x0a, 0, 0, 0, 0 };\n\nword32 g_woz_crc32_tab[256];\n\nvoid\nwoz_crc_init()\n{\n\tword32\tcrc, val, xor;\n\tint\ti, j;\n\n\tfor(i = 0; i < 256; i++) {\n\t\tcrc = 0;\n\t\tval = i;\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\txor = 0;\n\t\t\tif((val ^ crc) & 1) {\n\t\t\t\txor = 0xedb88320UL;\n\t\t\t}\n\t\t\tcrc = (crc >> 1) ^ xor;\n\t\t\tval = val >> 1;\n\t\t}\n\t\tg_woz_crc32_tab[i] = crc;\n\t\t// printf(\"crc32_tab[%d] = %08x\\n\", i, crc);\n\t}\n}\n\nword32\nwoz_calc_crc32(byte *bptr, dword64 dlen, word32 bytes_to_skip)\n{\n\tword32\tcrc, c;\n\n\tcrc = (~0U);\n\tif(bytes_to_skip > dlen) {\n\t\tdlen = 0;\n\t} else {\n\t\tbptr += bytes_to_skip;\n\t\tdlen -= bytes_to_skip;\n\t}\n\twhile(dlen != 0) {\n\t\tc = *bptr++;\n\t\tdlen--;\n\t\tcrc = g_woz_crc32_tab[(crc ^ c) & 0xff] ^ (crc >> 8);\n\t}\n\treturn (~crc);\n}\n\nvoid\nwoz_rewrite_crc(Disk *dsk, int min_write_size)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr;\n\tword32\tcrc;\n\n\t// Recalculate WOZ image CRC and write it to disk\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(!wozinfo_ptr || (dsk->fd < 0)) {\n\t\treturn;\n\t}\n\twozptr = wozinfo_ptr->wozptr;\n\tcrc = woz_calc_crc32(&wozptr[0], wozinfo_ptr->woz_size, 12);\n\tcfg_set_le32(&wozptr[8], crc);\n\tif(min_write_size < 12) {\n\t\tmin_write_size = 12;\n\t}\n\tcfg_write_to_fd(dsk->fd, wozptr, 0, min_write_size);\n}\n\nvoid\nwoz_rewrite_lock(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr;\n\tint\toffset;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(!wozinfo_ptr || (dsk->fd < 0)) {\n\t\treturn;\n\t}\n\twozptr = wozinfo_ptr->wozptr;\n\toffset = wozinfo_ptr->info_offset;\n\n\twozptr[offset + 2] = (dsk->write_prot != 0);\t// Update locked\n\twoz_rewrite_crc(dsk, offset + 2 + 1);\n}\n\nvoid\nwoz_check_file(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr, *newwozptr, *bptr;\n\tword32\tfdval, memval, crc, mem_crc, crcnew, file_size;\n\tint\twoz_size;\n\tint\ti;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(!wozinfo_ptr) {\n\t\treturn;\n\t}\n\twoz_size = wozinfo_ptr->woz_size;\n\twozptr = wozinfo_ptr->wozptr;\n\tcrc = woz_calc_crc32(wozptr, woz_size, 12);\n\tmem_crc = wozptr[8] | (wozptr[9] << 8) | (wozptr[10] << 16) |\n\t\t\t\t\t\t\t(wozptr[11] << 24);\n\tif(crc != mem_crc) {\n\t\thalt_printf(\"WOZ CRC calc:%08x, from mem:%08x\\n\", crc, mem_crc);\n\t}\n\tif((dsk->fd < 0) || dsk->raw_data || !dsk->write_through_to_unix) {\n\t\tprintf(\"woz_check_file: CRC check done, cannot check fd\\n\");\n\t\treturn;\n\t}\n\n\tfile_size = (word32)cfg_get_fd_size(dsk->fd);\n\tif(file_size != (word32)woz_size) {\n\t\thalt_printf(\"woz_size:%08x != file_size %08x\\n\", woz_size,\n\t\t\t\t\t\t\t\tfile_size);\n\t\tif((word32)woz_size > file_size) {\n\t\t\twoz_size = file_size;\n\t\t}\n\t}\n\tnewwozptr = malloc(woz_size);\n\tcfg_read_from_fd(dsk->fd, newwozptr, 0, woz_size);\n\n\tfor(i = 0; i < woz_size; i++) {\n\t\tfdval = newwozptr[i];\n\t\tmemval = wozptr[i];\n\t\tif(fdval == memval) {\n\t\t\tcontinue;\n\t\t}\n\t\thalt_printf(\"byte %07x of %07x: mem %02x != %02x fd\\n\", i,\n\t\t\t\t\t\twoz_size, memval, fdval);\n\t}\n\n\tcrcnew = woz_calc_crc32(newwozptr, woz_size, 12);\n\tfree(newwozptr);\n\tprintf(\"Woz check file complete.  mem %08x vs fd %08x, freed %p\\n\",\n\t\tcrc, crcnew, newwozptr);\n\n\tbptr = dsk->cur_trk_ptr->raw_bptr;\n\tprintf(\"dsk->cur_trk_ptr->raw_bptr = %p.  offset:%d\\n\", bptr,\n\t\t\t(int)(bptr - wozptr));\n}\n\nvoid\nwoz_parse_meta(Disk *dsk, int offset, int size)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr, *bptr;\n\tint\tc;\n\tint\ti;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\twozptr = wozinfo_ptr->wozptr;\n\tif(wozinfo_ptr->meta_offset) {\n\t\tprintf(\"Bad WOZ file, 2 META chunks\\n\");\n\t\twozinfo_ptr->woz_size = 0;\n\t\treturn;\n\t}\n\twozinfo_ptr->meta_offset = offset;\n\twozinfo_ptr->meta_size = size;\n\tprintf(\"META field, %d bytes:\\n\", size);\n\tbptr = &(wozptr[offset]);\n\tfor(i = 0; i < size; i++) {\n\t\tc = bptr[i];\n\t\tif(c == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tputchar(c);\n\t}\n\tputchar('\\n');\n}\n\nvoid\nwoz_parse_info(Disk *dsk, int offset, int size)\n{\n\tbyte\tnew_buf[36];\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr, *bptr;\n\tint\tinfo_version, disk_type, write_protect, synchronized;\n\tint\tcleaned, ram, largest_track;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\twozptr = wozinfo_ptr->wozptr;\n\tif(wozinfo_ptr->info_offset) {\n\t\tprintf(\"Two INFO chunks, bad WOZ file\\n\");\n\t\twozinfo_ptr->woz_size = 0;\n\t\treturn;\n\t}\n\twozinfo_ptr->info_offset = offset;\n\tbptr = &(wozptr[offset]);\n\tif(size < 60) {\n\t\tprintf(\"INFO field is %d, too short\\n\", size);\n\t\twozinfo_ptr->woz_size = 0;\n\t\treturn;\n\t}\n\tinfo_version = bptr[0];\t\t// Only \"1\" or \"2\" is supported\n\tdisk_type = bptr[1];\t\t// 1==5.25\", 2=3.5\"\n\twrite_protect = bptr[2];\t// 1==write protected\n\tsynchronized = bptr[3];\t\t// 1==cross track sync during imaging\n\tcleaned = bptr[4];\t\t// 1==MC3470 fake bits have been removed\n\tmemcpy(&new_buf[0], &(bptr[5]), 32);\n\tnew_buf[32] = 0;\t\t// Null terminate\n\tprintf(\"INFO, %d bytes.  info_version:%d, disk_type:%d, wp:%d, sync:\"\n\t\t\"%d, cleaned:%d\\n\", size, info_version, disk_type,\n\t\twrite_protect, synchronized, cleaned);\n\tprintf(\"Creator: %s\\n\", (char *)&new_buf[0]);\n\tif(info_version >= 2) {\n\t\tram = bptr[42] + (bptr[43] << 8);\n\t\tlargest_track = (bptr[44] + (bptr[45] << 8)) * 512;\n\t\tprintf(\"Disk sides:%d, boot_format:%d bit_timing:%d, hw:\"\n\t\t\t\"%02x%02x, ram:%d, largest_track:0x%07x\\n\", bptr[37],\n\t\t\tbptr[38], bptr[39], bptr[41], bptr[40], ram,\n\t\t\tlargest_track);\n\t}\n\n\tif(write_protect) {\n\t\tprintf(\"Write protected\\n\");\n\t\tdsk->write_prot = 1;\n\t}\n}\n\nvoid\nwoz_parse_tmap(Disk *dsk, int offset, int size)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*bptr, *wozptr;\n\tint\ti;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\twozptr = wozinfo_ptr->wozptr;\n\tif(wozinfo_ptr->tmap_offset) {\n\t\tprintf(\"Second TMAP chunk, bad WOZ file!\\n\");\n\t\twozinfo_ptr->woz_size = 0;\n\t\treturn;\n\t}\n\twozinfo_ptr->tmap_offset = offset;\n\tprintf(\"TMAP field, %d bytes\\n\", size);\n\tbptr = &(wozptr[offset]);\n\tfor(i = 0; i < 40; i++) {\n\t\tprintf(\"Track %2d.00: %02x, %2d.25:%02x %2d.50:%02x %2d.75:\"\n\t\t\t\"%02x\\n\", i, bptr[0], i, bptr[1],\n\t\t\ti, bptr[2], i, bptr[3]);\n\t\tbptr += 4;\n\t}\n}\n\nvoid\nwoz_parse_trks(Disk *dsk, int offset, int size)\n{\n\tWoz_info *wozinfo_ptr;\n\n\tprintf(\"TRKS field, %d bytes, offset: %d\\n\", size, offset);\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(wozinfo_ptr->trks_offset) {\n\t\tprintf(\"Second TRKS chunk, illegal Woz file\\n\");\n\t\twozinfo_ptr->woz_size = 0;\n\t\treturn;\n\t}\n\twozinfo_ptr->trks_offset = offset;\n\twozinfo_ptr->trks_size = size;\n}\n\nint\nwoz_add_track(Disk *dsk, int qtr_track, word32 tmap, dword64 dfcyc)\n{\n\tWoz_info *wozinfo_ptr;\n\tTrk\t*trk;\n\tbyte\t*wozptr, *sync_ptr, *bptr;\n\tword32\traw_bytes, num_bytes, trks_size, len_bits, offset, num_blocks;\n\tword32\tblock;\n\tint\ttrks_offset;\n\tint\ti;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\twozptr = wozinfo_ptr->wozptr;\n\ttrks_offset = wozinfo_ptr->trks_offset;\n\ttrks_size = wozinfo_ptr->trks_size;\n\tif(wozinfo_ptr->version == 1) {\n\t\toffset = tmap * 6656;\n\t\tif((offset + 6656) > trks_size) {\n\t\t\tprintf(\"Trk %d is out of range!\\n\", tmap);\n\t\t\treturn 0;\n\t\t}\n\n\t\toffset = trks_offset + offset;\n\t\tbptr = &(wozptr[offset]);\n\t\tlen_bits = bptr[6648] | (bptr[6649] << 8);\n\t\tif(len_bits > (6656*8)) {\n\t\t\tprintf(\"Trk bits: %d too big\\n\", len_bits);\n\t\t\treturn 0;\n\t\t}\n\t} else {\n\t\tbptr = &(wozptr[trks_offset + (tmap * 8)]);\n\t\t// This is a TRK 8-byte structure\n\t\tblock = cfg_get_le16(&bptr[0]);\t\t// Starting Block\n\t\tnum_blocks = cfg_get_le16(&bptr[2]);\t// Block Count\n\t\tlen_bits = cfg_get_le32(&bptr[4]);\t// Bits Count\n#if 0\n\t\tprintf(\"qtr_track:%02x, block:%04x, num_blocks:%04x, \"\n\t\t\t\"len_bits:%06x\\n\", qtr_track, block, num_blocks,\n\t\t\tlen_bits);\n\t\tprintf(\"File offset: %05lx\\n\", bptr - wozinfo_ptr->wozptr);\n#endif\n\n\t\tif(block < 3) {\n\t\t\tprintf(\"block %04x is < 3\\n\", block);\n\t\t\treturn 0;\n\t\t}\n\t\toffset = (block * 512);\t\t// Offset from wozptr\n\t\tif((offset + (num_blocks * 512)) > (trks_size + trks_offset)) {\n\t\t\tprintf(\"Trk %d is out of range!\\n\", tmap);\n\t\t\treturn 0;\n\t\t}\n\t\tbptr = &(wozptr[offset]);\n#if 0\n\t\tprintf(\"Qtr_track %03x offset:%06x, bptr:%p, trks_bptr:%p\\n\",\n\t\t\tqtr_track, offset, bptr, trks_bptr);\n\t\tprintf(\" len_bits:%d %06x bptr-wozptr: %07lx\\n\", len_bits,\n\t\t\t\tlen_bits, bptr - wozinfo_ptr->wozptr);\n#endif\n\t\tif(len_bits > (num_blocks * 512 * 8)) {\n\t\t\tprintf(\"Trk bits: %d too big\\n\", len_bits);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tdsk->raw_bptr_malloc = 0;\n\n\traw_bytes = (len_bits + 7) >> 3;\n\tnum_bytes = raw_bytes + 8;\n\ttrk = &(dsk->trks[qtr_track]);\n\ttrk->raw_bptr = bptr;\n\ttrk->dunix_pos = offset;\n\ttrk->unix_len = raw_bytes;\n\ttrk->dirty = 0;\n\ttrk->track_bits = len_bits;\n#if 0\n\tprintf(\"track %d.%d dunix_pos:%08llx\\n\", qtr_track >> 2,\n\t\t\t\t(qtr_track & 3) * 25, trk->dunix_pos);\n#endif\n\ttrk->sync_ptr = (byte *)malloc(num_bytes);\n\n\tdsk->cur_trk_ptr = 0;\n\tiwm_move_to_ftrack(dsk, qtr_track << 16, 0, dfcyc);\n\tsync_ptr = &(trk->sync_ptr[0]);\n\tfor(i = 0; i < (int)raw_bytes; i++) {\n\t\tsync_ptr[i] = 0xff;\n\t}\n\n\tiwm_recalc_sync_from(dsk, qtr_track, 0, dfcyc);\n\n\tif(qtr_track == 0) {\n\t\tprintf(\"Track 0 data begins: %02x %02x %02x, offset:%d\\n\",\n\t\t\tbptr[0], bptr[1], bptr[2], offset);\n\t}\n\tfor(i = 0; i < qtr_track; i++) {\n\t\ttrk = &(dsk->trks[i]);\n\t\tif(trk->track_bits && (trk->raw_bptr == bptr)) {\n\t\t\t// Multiple tracks point to the same woz track\n\t\t\t// This is not allowed, reparse\n\t\t\twozinfo_ptr->reparse_needed = 1;\n\t\t\tprintf(\"Track %04x matchs track %04x, reparse needed\\n\",\n\t\t\t\t\t\t\tqtr_track, i);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn 1;\n}\n\nint\nwoz_parse_header(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr, *bptr;\n\tword32\tchunk_id, size, woz_size;\n\tint\tpos, version;\n\tint\ti;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\twozptr = wozinfo_ptr->wozptr;\n\twoz_size = wozinfo_ptr->woz_size;\n\tversion = 2;\n\tif(woz_size < 8) {\n\t\treturn 0;\n\t}\n\tfor(i = 0; i < 8; i++) {\n\t\tif(wozptr[i] != g_woz_hdr_bytes[i]) {\n\t\t\tif(i == 3) {\t\t// Check for WOZ1\n\t\t\t\tif(wozptr[i] == 0x31) {\t\t// WOZ1\n\t\t\t\t\tversion = 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tprintf(\"WOZ header[%d]=%02x, invalid\\n\", i, wozptr[i]);\n\t\t\treturn 0;\n\t\t}\n\t}\n\twozinfo_ptr->version = version;\n\n\tprintf(\"WOZ version: %d\\n\", version);\n\tpos = 12;\n\twhile(pos < (int)woz_size) {\n\t\tbptr = &(wozptr[pos]);\n\t\tchunk_id = bptr[0] | (bptr[1] << 8) | (bptr[2] << 16) |\n\t\t\t\t(bptr[3] << 24);\n\t\tsize = bptr[4] | (bptr[5] << 8) | (bptr[6] << 16) |\n\t\t\t\t\t\t\t(bptr[7] << 24);\n\t\tpos += 8;\n\t\tprintf(\"chunk_id: %08x, size:%08x\\n\", chunk_id, size);\n\t\tif(((pos + size) > woz_size) || (size < 8) ||\n\t\t\t\t\t\t\t((size >> 30) != 0)) {\n\t\t\treturn 0;\n\t\t}\n\t\tbptr = &(wozptr[pos]);\n\t\tif(chunk_id == 0x4f464e49) {\t\t// \"INFO\"\n\t\t\twoz_parse_info(dsk, pos, size);\n\t\t} else if(chunk_id == 0x50414d54) {\t// \"TMAP\"\n\t\t\twoz_parse_tmap(dsk, pos, size);\n\t\t} else if(chunk_id == 0x534b5254) {\t// \"TRKS\"\n\t\t\twoz_parse_trks(dsk, pos, size);\n\t\t} else if(chunk_id == 0x4154454d) {\t// \"META\"\n\t\t\twoz_parse_meta(dsk, pos, size);\n\t\t} else {\n\t\t\tprintf(\"Chunk header %08x is unknown\\n\", chunk_id);\n\t\t}\n\t\tpos += size;\n\t}\n\n\treturn 1;\t\t// Good so far\n}\n\nWoz_info *\nwoz_malloc(byte *wozptr, word32 woz_size)\n{\n\tWoz_info *wozinfo_ptr;\n\n\twozinfo_ptr = malloc(sizeof(Woz_info));\n\tprintf(\"malloc wozinfo_ptr:%p\\n\", wozinfo_ptr);\n\tif(!wozinfo_ptr) {\n\t\tfprintf(stderr, \"Out of memory\\n\");\n\t\texit(1);\n\t}\n\n\twozinfo_ptr->wozptr = wozptr;\n\twozinfo_ptr->woz_size = woz_size;\n\twozinfo_ptr->version = 0;\n\twozinfo_ptr->reparse_needed = 0;\n\twozinfo_ptr->max_trk_blocks = 0;\n\twozinfo_ptr->meta_size = 0;\n\twozinfo_ptr->trks_size = 0;\n\twozinfo_ptr->tmap_offset = 0;\n\twozinfo_ptr->trks_offset = 0;\n\twozinfo_ptr->info_offset = 0;\n\twozinfo_ptr->meta_offset = 0;\n\n\tif(woz_size < 12) {\n\t\tif(wozptr) {\n\t\t\tfree(wozptr);\n\t\t}\n\t\twozptr = 0;\n\t}\n\tif(!wozptr) {\n\t\twozptr = malloc(12);\n\t\twoz_append_bytes(wozptr, &g_woz_hdr_bytes[0], 12);\n\t\twozinfo_ptr->wozptr = wozptr;\n\t\twozinfo_ptr->woz_size = 12;\n\t}\n\n\treturn wozinfo_ptr;\n}\n\nint\nwoz_reopen(Disk *dsk, dword64 dfcyc)\n{\n\tbyte\tact_tmap[160];\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr, *tmap_bptr;\n\tword32\ttmap, prev_tmap, last_act;\n\tint\tret, num_tracks, num_match;\n\tint\ti, j;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\twozptr = wozinfo_ptr->wozptr;\n\n\tif(!wozinfo_ptr->tmap_offset) {\n\t\tprintf(\"No TMAP found\\n\");\n\t\treturn 0;\n\t}\n\tif(!wozinfo_ptr->trks_offset) {\n\t\tprintf(\"No TRKS found\\n\");\n\t\treturn 0;\n\t}\n\tif(!wozinfo_ptr->info_offset) {\n\t\tprintf(\"No INFO found\\n\");\n\t\treturn 0;\n\t}\n\tif(wozinfo_ptr->woz_size == 0) {\n\t\tprintf(\"woz_size is 0!\\n\");\n\t\treturn 0;\n\t}\n\n\ttmap_bptr = wozptr + wozinfo_ptr->tmap_offset;\n\tdsk->cur_fbit_pos = 0;\n\tnum_tracks = 4*35;\n\tfor(i = num_tracks; i < 160; i++) {\n\t\t// See what the largest track is, go to track 39.50\n\t\tif(tmap_bptr[i] != 0xff) {\n\t\t\tnum_tracks = i + 1;\n\t\t\t//printf(\"Will set num_tracks=%d\\n\", num_tracks);\n\t\t}\n\t}\n\tdsk->fbit_mult = 128;\t\t// 5.25\" multipler value\n\tif(!dsk->disk_525) {\n\t\tnum_tracks = 160;\n\t\tdsk->fbit_mult = 256;\t// 3.5\" multipler value\n\t}\n\tdisk_set_num_tracks(dsk, num_tracks);\n\tfor(i = 0; i < 160; i++) {\n\t\tact_tmap[i] = 0xff;\n\t}\n\tfor(i = 0; i < 160; i++) {\n\t\ttmap = tmap_bptr[i];\n\t\tif(tmap >= 0xff) {\n\t\t\tcontinue;\t\t// Skip\n\t\t}\n\t\t// WOZ format adds dup entries for adjacent qtr tracks, so\n\t\t//  track 2.0 has entries at 1.75 and 2.25 pointing to 2.0.\n\t\t// KEGS doesn't want these, it handles this itself, so remove\n\t\t//  them.\n\t\tif(dsk->disk_525) {\n\t\t\tnum_match = 1;\n\t\t\tfor(j = i + 1; j < 160; j++) {\n\t\t\t\tif(tmap_bptr[j] != tmap) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tnum_match++;\n\t\t\t}\n\t\t\t// From i, num_match is the number of time tmap repeats\n\t\t\tprev_tmap = 0xff;\n\t\t\tlast_act = 0xff;\n\t\t\tif(i) {\n\t\t\t\tprev_tmap = tmap_bptr[i - 1];\n\t\t\t\tlast_act = act_tmap[i - 1];\n\t\t\t} else if(num_match == 3) {\n\t\t\t\t// Weird case where WOZ starts with 0,0,0, we\n\t\t\t\t//  should treat track 0.25 as the real track\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(num_match >= 3) {\n\t\t\t\t// long run of repeats, add a track every other\n\t\t\t\t//  one.\n\t\t\t\tif(last_act == tmap) {\t\t// Just did trk\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(prev_tmap != tmap) {\t\t// Start of run\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t} else if(num_match == 2) {\n\t\t\t\t// Handle A, B, [B], B, C and A, B, B, [B], B, C\n\t\t\t\t// ALWAYS add this track\n\t\t\t} else {\t\t\t// num_match==1\n\t\t\t\tif(prev_tmap == tmap) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Otherwise, a lone track, always add it\n\t\t\t}\n\t\t}\n\t\tret = woz_add_track(dsk, i, tmap, dfcyc);\n\t\tif(ret == 0) {\n\t\t\tprintf(\"woz_add_track i:%04x tmap:%04x ret 0\\n\", i,\n\t\t\t\t\t\t\t\ttmap);\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\treturn 1;\t\t// WOZ file is good!\n}\n\nint\nwoz_open(Disk *dsk, dword64 dfcyc)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr;\n\tdword64\tdoff;\n\tword32\twoz_size, crc, file_crc;\n\tint\tfd, ret;\n\n\t// return 0 for bad WOZ file, 1 for success\n\t// We set dsk->wozinfo_ptr, and caller will free it if we return 0\n\tprintf(\"woz_open on file %s, write_prot:%d\\n\", dsk->name_ptr,\n\t\t\t\t\t\tdsk->write_prot);\n\tif(dsk->trks == 0) {\n\t\treturn 0;\t\t// Smartport?\n\t}\n\tif(dsk->raw_data) {\n\t\twozptr = dsk->raw_data;\n\t\twoz_size = (word32)dsk->raw_dsize;\n\t\tdsk->write_prot = 1;\n\t\tdsk->write_through_to_unix = 0;\n\t} else {\n\t\tfd = dsk->fd;\n\t\tif(fd < 0) {\n\t\t\treturn 0;\n\t\t}\n\t\twoz_size = (word32)cfg_get_fd_size(fd);\n\t\tprintf(\"size: %d\\n\", woz_size);\n\n\t\twozptr = malloc(woz_size);\n\t\tdoff = cfg_read_from_fd(fd, wozptr, 0, woz_size);\n\t\tif(doff != woz_size) {\n\t\t\tclose(fd);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\twozinfo_ptr = woz_malloc(wozptr, woz_size);\n\tdsk->wozinfo_ptr = wozinfo_ptr;\n\n\tif(woz_size < 16) {\n\t\treturn 0;\n\t}\n\tcrc = woz_calc_crc32(wozptr, woz_size, 12);\n\tfile_crc = wozptr[8] | (wozptr[9] << 8) | (wozptr[10] << 16) |\n\t\t\t\t\t\t\t(wozptr[11] << 24);\n\tif((crc != file_crc) && (file_crc != 0)) {\n\t\tprintf(\"Bad Woz CRC:%08x in file, calc:%08x\\n\", file_crc, crc);\n\t\treturn 0;\n\t}\n\n\tret = woz_parse_header(dsk);\n\tprintf(\"woz_parse_header ret:%d, write_prot:%d\\n\", ret,\n\t\t\t\t\t\t\tdsk->write_prot);\n\tif(ret == 0) {\n\t\treturn ret;\n\t}\n\n\tret = woz_reopen(dsk, dfcyc);\n\tprintf(\"woz_reopen ret:%d\\n\", ret);\n\n\twoz_maybe_reparse(dsk);\n\treturn ret;\n}\n\nbyte *\nwoz_append_bytes(byte *wozptr, byte *in_bptr, int len)\n{\n\tint\ti;\n\n\tfor(i = 0; i < len; i++) {\n\t\t*wozptr++ = *in_bptr++;\n\t}\n\n\treturn wozptr;\n}\n\nbyte *\nwoz_append_word32(byte *wozptr, word32 val)\n{\n\tint\ti;\n\n\tfor(i = 0; i < 4; i++) {\n\t\t*wozptr++ = val & 0xff;\n\t\tval = val >> 8;\n\t}\n\n\treturn wozptr;\n}\n\nint\nwoz_append_chunk(Woz_info *wozinfo_ptr, word32 chunk_id, word32 length,\n\t\t\t\t\t\t\tbyte *bptr)\n{\n\tbyte\t*wozptr, *new_wozptr, *save_wozptr;\n\tword32\twoz_size, new_size;\n\tint\toffset;\n\n\twozptr = wozinfo_ptr->wozptr;\n\twoz_size = wozinfo_ptr->woz_size;\n\n\tnew_size = woz_size + 4 + 4 + length;\n\tnew_wozptr = realloc(wozptr, new_size);\n\tif(new_wozptr == 0) {\n\t\treturn 0;\n\t}\n\twozptr = new_wozptr + woz_size;\n\twozptr = woz_append_word32(wozptr, chunk_id);\n\tsave_wozptr = woz_append_word32(wozptr, length);\n\twozptr = woz_append_bytes(save_wozptr, bptr, length);\n\n\twozinfo_ptr->wozptr = new_wozptr;\n\twozinfo_ptr->woz_size = new_size;\n\toffset = (int)(save_wozptr - new_wozptr);\n\tswitch(chunk_id) {\n\tcase 0x4f464e49:\t\t// \"INFO\"\n\t\twozinfo_ptr->info_offset = offset;\n\t\tbreak;\n\tcase 0x50414d54:\t\t// \"TMAP\"\n\t\twozinfo_ptr->tmap_offset = offset;\n\t\tbreak;\n\tcase 0x534b5254:\t\t// \"TRKS\"\n\t\twozinfo_ptr->trks_offset = offset;\n\t\twozinfo_ptr->trks_size = new_size;\n\t\tbreak;\n\t}\n\tif(wozptr != (new_wozptr + new_size)) {\n\t\thalt_printf(\"wozptr:%p != %p + %08x\\n\", wozptr, new_wozptr,\n\t\t\t\t\t\t\t\tnew_size);\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\nbyte *\nwoz_append_a_trk(Woz_info *wozinfo_ptr, Disk *dsk, int trk_num, byte *bptr,\n\t\tword32 *num_blocks_ptr, dword64 *tmap_dptr)\n{\n\tbyte\t*new_bptr;\n\tword32\tnum_blocks, blocks_start, this_blocks, size_bytes, track_bits;\n\tint\ti;\n\n\t// Align trks_buf_size to 512 bytes\n\tblocks_start = *num_blocks_ptr;\n\n\ttrack_bits = dsk->trks[trk_num].track_bits;\n\tsize_bytes = (dsk->trks[trk_num].track_bits + 7) >> 3;\n\tthis_blocks = (size_bytes + 511) >> 9;\n\tif(wozinfo_ptr->max_trk_blocks < this_blocks) {\n\t\twozinfo_ptr->max_trk_blocks = this_blocks;\n\t\tprintf(\"max_trk_blocks=%d from trk %03x\\n\", this_blocks,\n\t\t\t\t\t\t\t\ttrk_num);\n\t}\n\n\tnum_blocks = blocks_start + this_blocks;\n\t*num_blocks_ptr = num_blocks;\n\n\t*tmap_dptr = (((dword64)track_bits) << 32) | (this_blocks << 16) |\n\t\t\t\t\t\t\t\tblocks_start;\n\n\tnew_bptr = realloc(bptr, num_blocks << 9);\n\tif(new_bptr == 0) {\n\t\treturn 0;\n\t}\n\t// Zero out last 512 byte block, to ensure a partial track has all 0's\n\t//  in the last block\n\tfor(i = 0; i < 512; i++) {\n\t\tnew_bptr[(num_blocks << 9) - 512 + i] = 0;\n\t}\n\twoz_append_bytes(new_bptr + (blocks_start << 9),\n\t\t\tdsk->trks[trk_num].raw_bptr, size_bytes);\n\n\treturn new_bptr;\n}\n\nWoz_info *\nwoz_new_from_woz(Disk *dsk, int disk_525)\n{\n\tdword64\ttmap_dvals[160];\n\tint\ttmap_qtrk[160];\n\tbyte\tbuf[160];\n\tWoz_info *wozinfo_ptr, *in_wozinfo_ptr;\n\tTrk\t*trkptr;\n\tbyte\t*in_wozptr, *wozptr, *trks_bufptr;\n\tdword64\tdval;\n\tword32\ttype, woz_size, num_blocks, crc, track_bits;\n\tint\tc, num_valid_tmap, pos, offset, raw_bytes;\n\tint\ti, j;\n\n\twozinfo_ptr = woz_malloc(0, 0);\t\t// New wozinfo\n\tin_wozptr = 0;\n\tin_wozinfo_ptr = 0;\n\tif(dsk) {\n\t\tin_wozinfo_ptr = dsk->wozinfo_ptr;\n\t\tif(!in_wozinfo_ptr) {\n\t\t\thalt_printf(\"Changing to WOZ format!\\n\");\n\t\t}\n\t}\n\tif(in_wozinfo_ptr) {\n\t\tin_wozptr = in_wozinfo_ptr->wozptr;\n\t}\n\tprintf(\" START woz_new_from_woz, in_wozinfo_ptr:%p, in_wozptr:%p\\n\",\n\t\t\t\t\tin_wozinfo_ptr, in_wozptr);\n\tfor(i = 0; i < 60; i++) {\n\t\tbuf[i] = 0;\n\t}\n\tif(in_wozptr) {\n\t\t// Output the INFO chunk\n\t\tmemcpy(&buf[0], &in_wozptr[20], 60);\n\t} else {\n\t\t// Output an INFO chunk for KEGS\n\t\tbuf[3] = 1;\t\t\t// 1=synchronized tracks\n\t\tbuf[4] = 1;\t\t\t// 1=MC3470 fake bits removed\n\t\tfor(i = 0; i < 32; i++) {\n\t\t\tbuf[5 + i] = ' ';\t\t\t// Creator field\n\t\t}\n\t\tmemcpy(&buf[5], \"KEGS\", 4);\n\t\t// And put the revision number after it\n\t\tfor(i = 0; i < 20; i++) {\n\t\t\tc = g_kegs_version_str[i];\n\t\t\tif(c == 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbuf[5 + 5 + i] = c;\n\t\t}\n\t}\n\tbuf[0] = 2;\t\t\t// WOZ2 version\n\ttype = 2 - disk_525;\t\t// 1=5.25, 2=3.5\n\tbuf[1] = type;\n\tbuf[2] = 0;\t\t\t\t\t// write_prot=0\n\tif(buf[37] == 0) {\n\t\tbuf[37] = type;\t\t\t\t// sides, re-use type\n\t}\n\tif(buf[39] == 0) {\n\t\tbuf[39] = 16 + 16 * (type & 1);\t\t// 5.25=32, 3.5=16\n\t}\n\twoz_append_chunk(wozinfo_ptr, 0x4f464e49, 60, &buf[0]);\t\t// INFO\n\n\t// TMAP\n\tfor(i = 0; i < 160; i++) {\n\t\tbuf[i] = 0xff;\n\t}\n\tnum_blocks = 6;\t\t\t// 1280 + 256 = 6x512\n\ttrks_bufptr = malloc(num_blocks << 9);\n\tprintf(\"trk_bufptr = %p\\n\", trks_bufptr);\n\tfor(i = 0; i < (int)(num_blocks << 9); i++) {\n\t\ttrks_bufptr[i] = 0;\n\t}\n\tnum_valid_tmap = 0;\n\tif((dsk != 0) && (dsk->trks != 0)) {\n\t\t// Output all valid tracks, and set the TMAP for adjacent .25\n\t\tfor(i = 0; i < 160; i++) {\n\t\t\ttrkptr = &(dsk->trks[i]);\n\t\t\tif(trkptr->track_bits >= 10000) {\n\t\t\t\tbuf[i] = num_valid_tmap;\n\t\t\t\tif(i && (buf[i-1] == 0xff)) {\n\t\t\t\t\tbuf[i-1] = num_valid_tmap;\n\t\t\t\t}\n\t\t\t\tif((i < 159) && (trkptr[1].track_bits < 10000)){\n\t\t\t\t\tbuf[i+1] = num_valid_tmap;\n\t\t\t\t}\n\t\t\t\ttrks_bufptr = woz_append_a_trk(wozinfo_ptr, dsk,\n\t\t\t\t\ti, trks_bufptr, &num_blocks,\n\t\t\t\t\t&tmap_dvals[num_valid_tmap]);\n\t\t\t\ttmap_qtrk[num_valid_tmap] = i;\n\t\t\t\tprintf(\"Did append, tmap:%04x qtrk:%04x dval:\"\n\t\t\t\t\t\"%016llx\\n\", num_valid_tmap, i,\n\t\t\t\t\ttmap_dvals[num_valid_tmap]);\n\t\t\t\tnum_valid_tmap++;\n\t\t\t}\n\t\t}\n\t}\n\twoz_append_chunk(wozinfo_ptr, 0x50414d54, 160, &buf[0]);\t// TMAP\n\n\t// TRKS.  Already all 0 if there are no tracks.  Fill in tmap_dvals[]\n\tfor(i = 0; i < num_valid_tmap; i++) {\n\t\tpos = 256 + i*8;\n\t\tdval = tmap_dvals[i];\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\ttrks_bufptr[pos + j] = dval & 0xff;\n\t\t\tdval = dval >> 8;\n\t\t}\n\t}\n\twoz_append_chunk(wozinfo_ptr, 0x534b5254, (num_blocks << 9) - 256,\n\t\t\t\t\t\ttrks_bufptr + 256);\n\t\t\t\t\t\t// TRKS, 1280 minimum size\n\tfree(trks_bufptr);\n\n\t// Final META chunk...just remove, we've added or removed tracks from\n\t//  an original WOZ image, so the Meta data is no longer accurate\n\n\twozptr = wozinfo_ptr->wozptr;\n\twoz_size = wozinfo_ptr->woz_size;\n\n\tprintf(\" new wozptr:%p, woz_size:%08x\\n\", wozptr, woz_size);\n\twozptr[64] = wozinfo_ptr->max_trk_blocks;\t// largest track\n\n\tcrc = woz_calc_crc32(wozptr, woz_size, 12);\n\tcfg_set_le32(&wozptr[8], crc);\n\tif(dsk) {\n\t\tpos = 0;\n\t\tfor(i = 0; i < 160; i++) {\n\t\t\t// Go through and fix up trks structure to match new WOZ\n\t\t\ttrkptr = &(dsk->trks[i]);\n\t\t\tif((pos < num_valid_tmap) && (tmap_qtrk[pos] == i)) {\n\t\t\t\t// This is a valid track\n\t\t\t\tdval = tmap_dvals[pos];\n\t\t\t\ttrack_bits = dval >> 32;\n\t\t\t\toffset = (dval & 0xffff) << 9;\n\n\t\t\t\traw_bytes = (track_bits + 7) >> 3;\n\t\t\t\tif(trkptr->unix_len == 0) {\n\t\t\t\t\tfree(trkptr->raw_bptr);\n\t\t\t\t}\n\t\t\t\ttrkptr->raw_bptr = &wozptr[offset];\n\t\t\t\ttrkptr->dunix_pos = offset;\n\t\t\t\ttrkptr->unix_len = raw_bytes;\n\t\t\t\ttrkptr->dirty = 0;\n\t\t\t\ttrkptr->track_bits = track_bits;\n\t\t\t\tif(trkptr->sync_ptr == 0) {\n\t\t\t\t\thalt_printf(\"sync_ptr 0 qtrk:%04x\\n\",\n\t\t\t\t\t\t\t\t\ti);\n\t\t\t\t}\n\t\t\t\tpos++;\n\t\t\t} else {\n\t\t\t\t// No longer a valid track, free any ptrs\n\t\t\t\tif(dsk->raw_bptr_malloc) {\n\t\t\t\t\tfree(trkptr->raw_bptr);\n\t\t\t\t}\n\t\t\t\tfree(trkptr->sync_ptr);\n\t\t\t\ttrkptr->raw_bptr = 0;\n\t\t\t\ttrkptr->sync_ptr = 0;\n\t\t\t\ttrkptr->dunix_pos = 0;\n\t\t\t\ttrkptr->unix_len = 0;\n\t\t\t\ttrkptr->dirty = 0;\n\t\t\t\ttrkptr->track_bits = 0;\n\t\t\t}\n\t\t}\n\t\tdsk->raw_bptr_malloc = 0;\n\n\t\tif(dsk->raw_data) {\n\t\t\tfree(dsk->raw_data);\n\t\t\tdsk->raw_data = wozptr;\n\t\t\tdsk->raw_dsize = woz_size;\n\t\t\tdsk->dimage_start = 0;\n\t\t\tdsk->dimage_size = woz_size;\n\t\t\tin_wozptr = 0;\n\t\t}\n\t\tfree(in_wozptr);\n\t\tfree(in_wozinfo_ptr);\n\t\tif(in_wozinfo_ptr == 0) {\n\t\t\tdsk->write_through_to_unix = 0;\n\t\t\thalt_printf(\"Force write_through_to_unix since image \"\n\t\t\t\t\"changed to WOZ format\\n\");\n\t\t}\n\t}\n\n\tprintf(\" END woz_new_from_woz, wozinfo_ptr:%p wozptr:%p\\n\",\n\t\t\t\t\twozinfo_ptr, wozinfo_ptr->wozptr);\n\treturn wozinfo_ptr;\n}\n\nint\nwoz_new(int fd, const char *str, int size_kb)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr;\n\tword32\tsize, woz_size;\n\tint\tdisk_525;\n\n\tdisk_525 = 0;\n\tif(size_kb <= 140) {\n\t\tdisk_525 = 1;\n\t}\n\twozinfo_ptr = woz_new_from_woz(0, disk_525);\n\n\twozptr = wozinfo_ptr->wozptr;\n\twoz_size = wozinfo_ptr->woz_size;\n\n\n\tsize = (word32)must_write(fd, wozptr, woz_size);\n\tfree(wozptr);\n\tfree(wozinfo_ptr);\n\tif(size != woz_size) {\n\t\treturn -1;\n\t}\n\tif(str) {\n\t\t// Avoid unused var warning\n\t}\n\n\treturn 0;\n}\n\nvoid\nwoz_maybe_reparse(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(wozinfo_ptr) {\n\t\tif(wozinfo_ptr->reparse_needed) {\n\t\t\twoz_reparse_woz(dsk);\n\t\t}\n\t}\n}\n\nvoid\nwoz_set_reparse(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(wozinfo_ptr) {\n\t\twozinfo_ptr->reparse_needed = 1;\n\t} else {\n\t\twoz_reparse_woz(dsk);\n\t}\n}\n\nvoid\nwoz_reparse_woz(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\n#if 0\n\tprintf(\"In woz_reparse_woz, showing track 0\\n\");\n\n\tiwm_show_a_track(dsk, &(dsk->trks[0]), 0.0);\n\tiwm_show_stats(3);\n#endif\n\n\twozinfo_ptr = woz_new_from_woz(dsk, dsk->disk_525);\n\t\t// This wozinfo_ptr has reparse_needed==0\n\n\tdsk->wozinfo_ptr = wozinfo_ptr;\n\tif(!dsk->raw_data && dsk->write_through_to_unix) {\n\t\t(void)!ftruncate(dsk->fd, wozinfo_ptr->woz_size);\n\t\t(void)cfg_write_to_fd(dsk->fd, wozinfo_ptr->wozptr, 0,\n\t\t\t\t\t\t\twozinfo_ptr->woz_size);\n\t\tprintf(\"did ftruncate and write of WOZ to %s\\n\", dsk->name_ptr);\n\t}\n\n\t// Need to recalculate dsk->cur_track_bits, cur_trk_ptr\n\tdsk->cur_trk_ptr = 0;\n\tiwm_move_to_ftrack(dsk, dsk->cur_frac_track, 0, 0);\n\n#if 0\n\tprintf(\"End of woz_reparse_woz, showing track 0\\n\");\n\tiwm_show_a_track(dsk, &(dsk->trks[0]), 0.0);\n\tiwm_show_stats(3);\n#endif\n\n\twoz_check_file(dsk);\n\tprintf(\"woz_reparse_woz complete!\\n\");\n}\n\nvoid\nwoz_remove_a_track(Disk *dsk, word32 qtr_track)\n{\n\tTrk\t*trkptr;\n\n\tprintf(\"woz_remove_track: %s qtr_track:%03x\\n\", dsk->name_ptr,\n\t\t\t\t\t\t\t\tqtr_track);\n\ttrkptr = &(dsk->trks[qtr_track]);\n\ttrkptr->track_bits = 0;\t\t// Track invalid\n\n\twoz_set_reparse(dsk);\n}\n\nword32\nwoz_add_a_track(Disk *dsk, word32 qtr_track)\n{\n\tTrk\t*trkptr, *other_trkptr;\n\tbyte\t*bptr, *other_bptr, *sync_ptr, *other_sync_ptr;\n\tword32\ttrack_bits, val;\n\tint\traw_bytes;\n\tint\ti;\n\n\t// Return track_bits for the new track\n\n\ttrkptr = &(dsk->trks[qtr_track]);\n\n\tother_trkptr = 0;\n\tif((qtr_track > 0) && (trkptr[-1].track_bits > 0)) {\n\t\t// Copy this track\n\t\tother_trkptr = trkptr - 1;\n\t} else if((qtr_track < 159) && (trkptr[1].track_bits > 0)) {\n\t\tother_trkptr = trkptr + 1;\n\t}\n\tother_trkptr = 0;\t\t// HACK\n\tif(dsk->disk_525 && other_trkptr) {\n\t\t// We're .25 tracks away from a valid track, copy it's data\n\t\ttrack_bits = other_trkptr->track_bits;\n\t\traw_bytes = (track_bits + 7) >> 3;\n\t\ttrkptr->track_bits = track_bits;\n\t\ttrkptr->raw_bptr = malloc(raw_bytes + 8);\n\t\ttrkptr->sync_ptr = malloc(raw_bytes + 8);\n\t\tprintf(\" add a track, copy bptr:%p sync_ptr:%p size:%08x\\n\",\n\t\t\ttrkptr->raw_bptr, trkptr->sync_ptr, raw_bytes + 8);\n\t\tbptr = trkptr->raw_bptr;\n\t\tsync_ptr = trkptr->sync_ptr;\n\t\tother_bptr = other_trkptr->raw_bptr;\n\t\tother_sync_ptr = other_trkptr->sync_ptr;\n\t\tfor(i = 0; i < raw_bytes; i++) {\n\t\t\tbptr[i] = other_bptr[i];\n\t\t\tsync_ptr[i] = other_sync_ptr[i];\n\t\t}\n\t} else {\n\t\ttrack_bits = iwm_get_default_track_bits(dsk, qtr_track);\n\t\traw_bytes = (track_bits + 7) >> 3;\n\t\ttrkptr->track_bits = track_bits;\n\t\ttrkptr->raw_bptr = malloc(raw_bytes + 8);\n\t\ttrkptr->sync_ptr = malloc(raw_bytes + 8);\n\t\tprintf(\" add a track, raw_bptr:%p sync_ptr:%p size:%08x\\n\",\n\t\t\ttrkptr->raw_bptr, trkptr->sync_ptr, raw_bytes + 8);\n\n\t\tbptr = trkptr->raw_bptr;\n\t\tsync_ptr = trkptr->sync_ptr;\n\t\tfor(i = 0; i < raw_bytes; i++) {\n\t\t\tval = ((i >> 6) ^ i) & 0x7f;\n\t\t\tif(((val & 0xf0) == 0) || ((val & 0x0f) == 0)) {\n\t\t\t\tval |= 0x21;\n\t\t\t}\n\t\t\tbptr[i] = val;\n\t\t\tsync_ptr[i] = 0xff;\n\t\t}\n\t\tbptr[raw_bytes - 1] = 0;\n\n\t\tiwm_recalc_sync_from(dsk, qtr_track, 0, 0);\n\t}\n\ttrkptr->dunix_pos = 0;\n\ttrkptr->unix_len = 0;\t\t\t// Mark as a newly created trk\n\ttrkptr->dirty = 0;\n\n\tprintf(\"woz_add_new_track: %s qtr_track:%03x\\n\", dsk->name_ptr,\n\t\t\t\t\t\t\t\tqtr_track);\n\twoz_set_reparse(dsk);\n\n\treturn track_bits;\n}\n\n"
  },
  {
    "path": "gsplus/src/xdriver.c",
    "content": "/**********************************************************************/\n/*                    GSplus - Apple //gs Emulator                    */\n/*                    Based on KEGS by Kent Dickey                    */\n/*                    Copyright 2002-2025 Kent Dickey                 */\n/*                    Copyright 2025-2026 GSplus Contributors         */\n/*                                                                    */\n/*      This code is covered by the GNU GPL v3                        */\n/*      See the file COPYING.txt or https://www.gnu.org/licenses/     */\n/**********************************************************************/\n\n# if !defined(__CYGWIN__) && !defined(__POWERPC__)\n/* No shared memory on Cygwin */\n# define X_SHARED_MEM\n#endif /* CYGWIN */\n\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n#include <X11/keysym.h>\n#include <X11/Xatom.h>\n#include <time.h>\n#include <stdlib.h>\n#include <signal.h>\n\n#ifdef X_SHARED_MEM\n# include <sys/ipc.h>\n# include <sys/shm.h>\n# include <X11/extensions/XShm.h>\n#endif\n\nint XShmQueryExtension(Display *display);\nvoid _XInitImageFuncPtrs(XImage *xim);\n\n#include \"defc.h\"\n\nextern int g_video_scale_algorithm;\nextern int g_audio_enable;\n\ntypedef struct windowinfo {\n\tXShmSegmentInfo *seginfo;\n\tXImage *xim;\n\tKimage *kimage_ptr;\t\t// KEGS Image pointer for window content\n\tchar\t*name_str;\n\tWindow\tx_win;\n\tGC\tx_winGC;\n\tAtom\tdelete_atom;\n\tint\tx_use_shmem;\n\tint\tactive;\n\tint\twidth_req;\n\tint\tpixels_per_line;\n\tint\tmain_height;\n\tint\tfull_min_width;\n\tint\tfull_min_height;\n} Window_info;\n\n#include \"protos_xdriver.h\"\n\nint\tg_x_warp_x = 0;\nint\tg_x_warp_y = 0;\nint\tg_x_warp_pointer = 0;\nint\tg_x_hide_pointer = 0;\n\nextern int Verbose;\nint\tg_x_screen_depth = 24;\nint\tg_x_screen_mdepth = 32;\nint\tg_x_max_width = 0;\nint\tg_x_max_height = 0;\nint\tg_screen_num = 0;\n\nextern int _Xdebug;\n\nint\tg_auto_repeat_on = -1;\n\nDisplay *g_display = 0;\nVisual\t*g_vis = 0;\nColormap g_default_colormap = 0;\n\nWindow_info g_mainwin_info = { 0 };\nWindow_info g_debugwin_info = { 0 };\n\nCursor\tg_cursor;\nPixmap\tg_cursor_shape;\nPixmap\tg_cursor_mask;\n\nXColor\tg_xcolor_black = { 0, 0x0000, 0x0000, 0x0000, DoRed|DoGreen|DoBlue, 0 };\nXColor\tg_xcolor_white = { 0, 0xffff, 0xffff, 0xffff, DoRed|DoGreen|DoBlue, 0 };\n\nconst char *g_x_selection_strings[3] = {\n\t// If we get SelectionRequests with target=Atom(\"TARGETS\"), then\n\t//  send XA_STRING plus this list to say what we can provide for copy\n\t\"UTF8_STRING\", \"text/plain\", \"text/plain;charset=utf-8\"\n};\n\nint\tg_x_num_targets = 0;\nAtom g_x_targets_array[5] = { 0 };\n\nAtom g_x_atom_targets = None;\t\t// Will be set to \"TARGETS\"\n\nint g_depth_attempt_list[] = { 24, 16, 15 };\n\n#define X_EVENT_LIST_ALL_WIN\t\t\t\t\t\t\\\n\t(ExposureMask | ButtonPressMask | ButtonReleaseMask |\t\t\\\n\tOwnerGrabButtonMask | KeyPressMask | KeyReleaseMask |\t\t\\\n\tKeymapStateMask | FocusChangeMask)\n\n#define X_BASE_WIN_EVENT_LIST\t\t\t\t\t\t\\\n\t(X_EVENT_LIST_ALL_WIN | PointerMotionMask | ButtonMotionMask |\t\\\n\tStructureNotifyMask)\n\n#define X_A2_WIN_EVENT_LIST\t\t\t\t\t\t\\\n\t(X_BASE_WIN_EVENT_LIST)\n\nint\tg_num_a2_keycodes = 0;\n\nint\tg_x_a2_key_to_xsym[][3] = {\n\t{ 0x35,\tXK_Escape,\t0 },\n\t{ 0x7a,\tXK_F1,\t0 },\n\t{ 0x78,\tXK_F2,\t0 },\n\t{ 0x63,\tXK_F3,\t0 },\n\t{ 0x76,\tXK_F4,\t0 },\n\t{ 0x60,\tXK_F5,\t0 },\n\t{ 0x61,\tXK_F6,\t0 },\n\t{ 0x62,\tXK_F7,\t0 },\n\t{ 0x64,\tXK_F8,\t0 },\n\t{ 0x65,\tXK_F9,\t0 },\n\t{ 0x6d,\tXK_F10,\t0 },\n\t{ 0x67,\tXK_F11,\t0 },\n\t{ 0x6f,\tXK_F12,\t0 },\n\t{ 0x69,\tXK_F13,\t0 },\n\t{ 0x6b,\tXK_F14,\t0 },\n\t{ 0x71,\tXK_F15,\t0 },\n\t{ 0x7f, XK_Pause, XK_Break },\n\t{ 0x32,\t'`', '~' },\t\t/* Key number 18? */\n\t{ 0x12,\t'1', '!' },\n\t{ 0x13,\t'2', '@' },\n\t{ 0x14,\t'3', '#' },\n\t{ 0x15,\t'4', '$' },\n\t{ 0x17,\t'5', '%' },\n\t{ 0x16,\t'6', '^' },\n\t{ 0x1a,\t'7', '&' },\n\t{ 0x1c,\t'8', '*' },\n\t{ 0x19,\t'9', '(' },\n\t{ 0x1d,\t'0', ')' },\n\t{ 0x1b,\t'-', '_' },\n\t{ 0x18,\t'=', '+' },\n\t{ 0x33,\tXK_BackSpace, 0 },\n\t{ 0x72,\tXK_Insert, XK_Help },\t/* Help? */\n/*\t{ 0x73,\tXK_Home, 0 },\t\talias XK_Home to be XK_KP_Equal! */\n\t{ 0x74,\tXK_Page_Up, 0 },\n\t{ 0x47,\tXK_Num_Lock, XK_Clear },\t/* Clear */\n\t{ 0x51,\tXK_KP_Equal, XK_Home },\t\t/* Note XK_Home alias! */\n\t{ 0x4b,\tXK_KP_Divide, 0 },\n\t{ 0x43,\tXK_KP_Multiply, 0 },\n\n\t{ 0x30,\tXK_Tab, 0 },\n\t{ 0x0c,\t'q', 'Q' },\n\t{ 0x0d,\t'w', 'W' },\n\t{ 0x0e,\t'e', 'E' },\n\t{ 0x0f,\t'r', 'R' },\n\t{ 0x11,\t't', 'T' },\n\t{ 0x10,\t'y', 'Y' },\n\t{ 0x20,\t'u', 'U' },\n\t{ 0x22,\t'i', 'I' },\n\t{ 0x1f,\t'o', 'O' },\n\t{ 0x23,\t'p', 'P' },\n\t{ 0x21,\t'[', '{' },\n\t{ 0x1e,\t']', '}' },\n\t{ 0x2a,\t0x5c, '|' },\t/* backslash, bar */\n\t{ 0x75,\tXK_Delete, 0 },\n\t{ 0x77,\tXK_End, 0 },\n\t{ 0x79,\tXK_Page_Down, 0 },\n\t{ 0x59,\tXK_KP_7, XK_KP_Home },\n\t{ 0x5b,\tXK_KP_8, XK_KP_Up },\n\t{ 0x5c,\tXK_KP_9, XK_KP_Page_Up },\n\t{ 0x4e,\tXK_KP_Subtract, 0 },\n\n\t{ 0x39,\tXK_Caps_Lock, 0 },\n\t{ 0x00,\t'a', 'A' },\n\t{ 0x01,\t's', 'S' },\n\t{ 0x02,\t'd', 'D' },\n\t{ 0x03,\t'f', 'F' },\n\t{ 0x05,\t'g', 'G' },\n\t{ 0x04,\t'h', 'H' },\n\t{ 0x26,\t'j', 'J' },\n\t{ 0x28,\t'k', 'K' },\n\t{ 0x25,\t'l', 'L' },\n\t{ 0x29,\t';', ':' },\n\t{ 0x27,\t0x27, '\"' },\t/* single quote */\n\t{ 0x24,\tXK_Return, 0 },\n\t{ 0x56,\tXK_KP_4, XK_KP_Left },\n\t{ 0x57,\tXK_KP_5, XK_KP_Begin },\n\t{ 0x58,\tXK_KP_6, XK_KP_Right },\n\t{ 0x45,\tXK_KP_Add, 0 },\n\n\t{ 0x38,\tXK_Shift_L, XK_Shift_R },\n\t{ 0x06,\t'z', 'Z' },\n\t{ 0x07,\t'x', 'X' },\n\t{ 0x08,\t'c', 'C' },\n\t{ 0x09,\t'v', 'V' },\n\t{ 0x0b,\t'b', 'B' },\n\t{ 0x2d,\t'n', 'N' },\n\t{ 0x2e,\t'm', 'M' },\n\t{ 0x2b,\t',', '<' },\n\t{ 0x2f,\t'.', '>' },\n\t{ 0x2c,\t'/', '?' },\n\t{ 0x3e,\tXK_Up, 0 },\n\t{ 0x53,\tXK_KP_1, XK_KP_End },\n\t{ 0x54,\tXK_KP_2, XK_KP_Down },\n\t{ 0x55,\tXK_KP_3, XK_KP_Page_Down },\n\n\t{ 0x36,\tXK_Control_L, XK_Control_R },\n\t{ 0x3a,\tXK_Print, XK_Sys_Req },\t\t/* Option */\n\t{ 0x37,\tXK_Scroll_Lock, 0 },\t\t/* Command */\n\t{ 0x31,\t' ', 0 },\n\t{ 0x3b,\tXK_Left, 0 },\n\t{ 0x3d,\tXK_Down, 0 },\n\t{ 0x3c,\tXK_Right, 0 },\n\t{ 0x52,\tXK_KP_0, XK_KP_Insert },\n\t{ 0x41,\tXK_KP_Decimal, XK_KP_Separator },\n\t{ 0x4c,\tXK_KP_Enter, 0 },\n\t{ -1, -1, -1 }\n};\n\nint\nmain(int argc, char **argv)\n{\n\tint\tret, mdepth;\n\n\tret = parse_argv(argc, argv, 1);\n\tif(ret) {\n\t\tprintf(\"kegsmain ret: %d, stopping\\n\", ret);\n\t\texit(1);\n\t}\n\n\tmdepth = x_video_get_mdepth();\n\n\tret = kegs_init(mdepth, g_x_max_width, g_x_max_height, 0);\n\tprintf(\"kegs_init done\\n\");\n\tif(ret) {\n\t\tprintf(\"kegs_init ret: %d, stopping\\n\", ret);\n\t\texit(1);\n\t}\n\tx_video_init();\n\n\t// This is the main loop of KEGS, when this exits, KEGS exits\n\t//  run_16ms() does one video frame worth of instructions and video\n\t//  updates: 17030 1MHz clock cycles.\n\twhile(1) {\n\t\tret = run_16ms();\n\t\tif(ret != 0) {\n\t\t\tprintf(\"run_16ms returned: %d\\n\", ret);\n\t\t\tbreak;\n\t\t}\n\t\tx_input_events();\n\t\tx_update_display(&g_mainwin_info);\n\t\tx_update_display(&g_debugwin_info);\n\t}\n\txdriver_end();\n\texit(0);\n}\n\nint\nmy_error_handler(Display *display, XErrorEvent *ev)\n{\n\tchar msg[1024];\n\tXGetErrorText(display, ev->error_code, msg, 1000);\n\tprintf(\"X Error code %s\\n\", msg);\n\tfflush(stdout);\n\n\treturn 0;\n}\n\nvoid\nxdriver_end()\n{\n\tprintf(\"xdriver_end\\n\");\n\tif(g_display) {\n\t\tx_auto_repeat_on(1);\n\t\tXFlush(g_display);\n\t}\n}\n\nvoid\nx_try_xset_r()\n{\n\t/* attempt \"xset r\" */\n\t(void)!system(\"xset r\");\n\txdriver_end();\n\texit(5);\n}\n\nvoid\nx_badpipe(int signum)\n{\n\t/* restore normal sigpipe handling */\n\tsignal(SIGPIPE, SIG_DFL);\n\n\tx_try_xset_r();\n}\n\nint\nkegs_x_io_error_handler(Display *display)\n{\n\tprintf(\"kegs_x_io_error_handler called (likely window closed)\\n\");\n\tg_display = 0;\n\tx_try_xset_r();\n\treturn 0;\n}\n\nint\nx_video_get_mdepth()\n{\n\tXWindowAttributes get_attr;\n\tint\tdepth, len, ret, force_depth;\n\tint\ti;\n\n\tprintf(\"Preparing X Windows graphics system\\n\");\n\tret = 0;\n\n\tsignal(SIGPIPE, x_badpipe);\n\tsignal(SIGPIPE, x_badpipe);\n\n#if 0\n\tprintf(\"Setting _Xdebug = 1, makes X synchronous\\n\");\n\t_Xdebug = 1;\n#endif\n\n\tg_display = XOpenDisplay(NULL);\n\tif(g_display == NULL) {\n\t\tfprintf(stderr, \"Can't open display\\n\");\n\t\texit(1);\n\t}\n\n\tvid_printf(\"Just opened display = %p\\n\", g_display);\n\tfflush(stdout);\n\n\tg_screen_num = DefaultScreen(g_display);\n\n\tget_attr.width = 0;\n\tget_attr.height = 0;\n\tret = XGetWindowAttributes(g_display, DefaultRootWindow(g_display),\n\t\t\t&get_attr);\n\tprintf(\"XGetWindowAttributes ret: %d\\n\", ret);\n\tg_x_max_width = get_attr.width;\n\tg_x_max_height = get_attr.height;\n\tprintf(\"get_attr.width:%d, height:%d\\n\", g_x_max_width, g_x_max_height);\n\n\tlen = sizeof(g_depth_attempt_list)/sizeof(int);\n\tforce_depth = sim_get_force_depth();\n\tif(force_depth > 0) {\n\t\t/* Only use the requested user depth */\n\t\tlen = 1;\n\t\tg_depth_attempt_list[0] = force_depth;\n\t}\n\n\tfor(i = 0; i < len; i++) {\n\t\tdepth = g_depth_attempt_list[i];\n\n\t\tg_x_screen_mdepth = x_try_find_visual(depth, g_screen_num);\n\t\tif(g_x_screen_mdepth != 0) {\n\t\t\tbreak;\n\t\t}\n\t}\n\tif(g_x_screen_mdepth == 0) {\n\t\tfprintf(stderr, \"Couldn't find any visuals at any depth!\\n\");\n\t\texit(2);\n\t}\n\n\treturn g_x_screen_mdepth;\n}\n\nint\nx_try_find_visual(int depth, int screen_num)\n{\n\tXVisualInfo *visualList;\n\tXVisualInfo *v_chosen;\n\tXVisualInfo vTemplate;\n\tint\tvisualsMatched, visual_chosen, mdepth;\n\tint\ti;\n\n\tvTemplate.screen = screen_num;\n\tvTemplate.depth = depth;\n\n\tvisualList = XGetVisualInfo(g_display,\n\t\t(VisualScreenMask | VisualDepthMask),\n\t\t&vTemplate, &visualsMatched);\n\n\tvid_printf(\"visuals matched: %d\\n\", visualsMatched);\n\tif(visualsMatched == 0) {\n\t\treturn 0;\n\t}\n\n\tvisual_chosen = -1;\n\tfor(i = 0; i < visualsMatched; i++) {\n\t\tprintf(\"Visual %d\\n\", i);\n\t\tprintf(\"\tid: %08x, screen: %d, depth: %d, class: %d\\n\",\n\t\t\t(word32)visualList[i].visualid,\n\t\t\tvisualList[i].screen,\n\t\t\tvisualList[i].depth,\n\t\t\tvisualList[i].class);\n\t\tprintf(\"\tred: %08lx, green: %08lx, blue: %08lx\\n\",\n\t\t\tvisualList[i].red_mask,\n\t\t\tvisualList[i].green_mask,\n\t\t\tvisualList[i].blue_mask);\n\t\tprintf(\"\tcmap size: %d, bits_per_rgb: %d\\n\",\n\t\t\tvisualList[i].colormap_size,\n\t\t\tvisualList[i].bits_per_rgb);\n\t\tif((depth != 8) && (visualList[i].class == TrueColor)) {\n\t\t\tvisual_chosen = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(visual_chosen < 0) {\n\t\tprintf(\"Couldn't find any good visuals at depth %d!\\n\",\n\t\t\tdepth);\n\t\treturn 0;\n\t}\n\n\tprintf(\"Chose visual: %d\\n\", visual_chosen);\n\n\tv_chosen = &(visualList[visual_chosen]);\n\tvideo_set_red_mask((word32)v_chosen->red_mask);\n\tvideo_set_green_mask((word32)v_chosen->green_mask);\n\tvideo_set_blue_mask((word32)v_chosen->blue_mask);\n\n\tvideo_set_palette();\t// Uses above masks to initialize palettes\n\n\tg_x_screen_depth = depth;\n\tmdepth = depth;\n\tif(depth > 8) {\n\t\tmdepth = 16;\n\t}\n\tif(depth > 16) {\n\t\tmdepth = 32;\n\t}\n\n\t// XFree(visualList); -- Cannot free, still using g_vis...\n\n\tg_vis = v_chosen->visual;\n\n\treturn mdepth;\n}\n\nvoid\nx_video_init()\n{\n\tint\ttmp_array[0x80];\n\tAtom\ttarget;\n\tchar\tcursor_data;\n\tint\tkeycode, num_targets, max_targets, num;\n\tint\ti;\n\n\tprintf(\"Opening X Window now\\n\");\n\n\tg_num_a2_keycodes = 0;\n\tfor(i = 0; i <= 0x7f; i++) {\n\t\ttmp_array[i] = 0;\n\t}\n\tfor(i = 0; i < 0x7f; i++) {\n\t\tkeycode = g_x_a2_key_to_xsym[i][0];\n\t\tif(keycode < 0) {\n\t\t\tg_num_a2_keycodes = i;\n\t\t\tbreak;\n\t\t} else if(keycode > 0x7f) {\n\t\t\tprintf(\"a2_key_to_xsym[%d] = %02x!\\n\", i, keycode);\n\t\t\t\texit(2);\n\t\t} else {\n\t\t\tif(tmp_array[keycode]) {\n\t\t\t\tprintf(\"a2_key_to_x[%d] = %02x used by %d\\n\",\n\t\t\t\t\ti, keycode, tmp_array[keycode] - 1);\n\t\t\t}\n\t\t\ttmp_array[keycode] = i + 1;\n\t\t}\n\t}\n\n\n\tg_default_colormap = XDefaultColormap(g_display, g_screen_num);\n\tif(!g_default_colormap) {\n\t\tprintf(\"g_default_colormap == 0!\\n\");\n\t\texit(4);\n\t}\n\n\t/* and define cursor */\n\tcursor_data = 0;\n\tg_cursor_shape = XCreatePixmapFromBitmapData(g_display,\n\t\tRootWindow(g_display,g_screen_num), &cursor_data, 1, 1, 1, 0,1);\n\tg_cursor_mask = XCreatePixmapFromBitmapData(g_display,\n\t\tRootWindow(g_display,g_screen_num), &cursor_data, 1, 1, 1, 0,1);\n\n\tg_cursor = XCreatePixmapCursor(g_display, g_cursor_shape,\n\t\t\tg_cursor_mask, &g_xcolor_black, &g_xcolor_white, 0, 0);\n\n\tXFreePixmap(g_display, g_cursor_shape);\n\tXFreePixmap(g_display, g_cursor_mask);\n\n\tXFlush(g_display);\n\tg_x_atom_targets = XInternAtom(g_display, \"TARGETS\", 0);\n\tnum = sizeof(g_x_selection_strings)/sizeof(g_x_selection_strings[0]);\n\tg_x_targets_array[0] = XA_STRING;\n\tnum_targets = 1;\n\tfor(i = 0; i < num; i++) {\n\t\ttarget = XInternAtom(g_display, g_x_selection_strings[i], 0);\n\t\tif(target != None) {\n\t\t\tg_x_targets_array[num_targets++] = target;\n\t\t}\n\t}\n\tg_x_num_targets = num_targets;\n\tmax_targets = sizeof(g_x_targets_array)/sizeof(g_x_targets_array[0]);\n\tif(num_targets > max_targets) {\n\t\tprintf(\"Overflowed g_x_targets_array: %d out of %d\\n\",\n\t\t\t\t\t\tnum_targets, max_targets);\n\t\texit(-1);\n\t}\n\n\tx_init_window(&g_mainwin_info, video_get_kimage(0), \"KEGS\");\n\tx_init_window(&g_debugwin_info, video_get_kimage(1), \"KEGS Debugger\");\n\n\tx_create_window(&g_mainwin_info);\n\n\tvid_printf(\"Set error handler to my_x_handler\\n\");\n\tXSetIOErrorHandler(kegs_x_io_error_handler);\n}\n\nvoid\nx_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str)\n{\n\tint\twidth, height, x_use_shmem, ret;\n\n\theight = video_get_x_height(kimage_ptr);\n\twidth = video_get_x_width(kimage_ptr);\n\n\twin_info_ptr->seginfo = 0;\n\twin_info_ptr->xim = 0;\n\twin_info_ptr->kimage_ptr = kimage_ptr;\n\twin_info_ptr->name_str = name_str;\n\twin_info_ptr->x_win = 0;\n\twin_info_ptr->x_winGC = 0;\n\twin_info_ptr->delete_atom = 0;\n\twin_info_ptr->active = 0;\n\twin_info_ptr->x_use_shmem = 0;\n\twin_info_ptr->width_req = width;\n\twin_info_ptr->pixels_per_line = width;\n\twin_info_ptr->main_height = height;\n\twin_info_ptr->full_min_width = width;\n\twin_info_ptr->full_min_height = height;\n\tvid_printf(\"init win %p (main:%p) width:%d, height:%d\\n\", win_info_ptr,\n\t\t&g_mainwin_info, width, height);\n\n\tx_use_shmem = 0;\n\t\t// Allow cmd-line args to force shmem off\n/* Check for XShm */\n#ifdef X_SHARED_MEM\n\tif(sim_get_use_shmem()) {\n\t\tret = XShmQueryExtension(g_display);\n\t\tif(ret == 0) {\n\t\t\tprintf(\"XShmQueryExt ret: %d, not using shared mem\\n\",\n\t\t\t\t\t\t\t\t\tret);\n\t\t} else {\n\t\t\tvid_printf(\"Will use shared memory for X\\n\");\n\t\t\tx_use_shmem = 1;\n\t\t}\n\t}\n#endif\n\twin_info_ptr->x_use_shmem = x_use_shmem;\n\n\tvideo_update_scale(kimage_ptr, win_info_ptr->width_req,\n\t\t\t\t\t\twin_info_ptr->main_height, 1);\n}\n\nvoid\nx_create_window(Window_info *win_info_ptr)\n{\n\tXGCValues new_gc;\n\tXSetWindowAttributes win_attr;\n\tWindow\tx_win;\n\tGC\tx_winGC;\n\tword32\tcreate_win_list;\n\tint\twidth, height, x_xpos, x_ypos;\n\n\twin_attr.event_mask = X_A2_WIN_EVENT_LIST;\n\twin_attr.colormap = g_default_colormap;\n\twin_attr.backing_store = WhenMapped;\n\twin_attr.border_pixel = 1;\n\twin_attr.background_pixel = 0;\n\tif(g_x_warp_pointer) {\n\t\twin_attr.cursor = g_cursor;\n\t} else {\n\t\twin_attr.cursor = None;\n\t}\n\n\tvid_printf(\"About to win, depth: %d\\n\", g_x_screen_depth);\n\tfflush(stdout);\n\n\tcreate_win_list = CWEventMask | CWBackingStore | CWCursor;\n\tcreate_win_list |= CWColormap | CWBorderPixel | CWBackPixel;\n\n\tx_xpos = video_get_x_xpos(win_info_ptr->kimage_ptr);\n\tx_ypos = video_get_x_ypos(win_info_ptr->kimage_ptr);\n\twidth = win_info_ptr->width_req;\n\theight = win_info_ptr->main_height;\n\n\tx_win = XCreateWindow(g_display, RootWindow(g_display, g_screen_num),\n\t\tx_xpos, x_ypos, width, height, 0, g_x_screen_depth,\n\t\tInputOutput, g_vis, create_win_list, &win_attr);\n\tvid_printf(\"x_win = %d, width:%d, height:%d\\n\", (int)x_win, width,\n\t\t\t\t\t\t\t\t\theight);\n\n\tXFlush(g_display);\n\n\twin_info_ptr->x_win = x_win;\n\twin_info_ptr->active = 0;\n\tvideo_set_active(win_info_ptr->kimage_ptr, 1);\n\n\tx_allocate_window_data(win_info_ptr);\n\n\tif(!win_info_ptr->x_use_shmem) {\n\t\tprintf(\"Not using shared memory, setting skip_amt = 2, \"\n\t\t\t\t\t\t\t\"g_audio_enable=0\\n\");\n\t\tvideo_set_redraw_skip_amt(2);\n\t\tg_audio_enable = 0;\n\t}\n\n\tx_set_size_hints(win_info_ptr);\n\n\tXMapRaised(g_display, x_win);\n\n\tif(win_info_ptr != &g_mainwin_info) {\n\t\t// Debugger window\n\t\twin_info_ptr->delete_atom = XInternAtom(g_display,\n\t\t\t\t\t\t\"WM_DELETE_WINDOW\", False);\n\t\tXSetWMProtocols(g_display, x_win, &(win_info_ptr->delete_atom),\n\t\t\t\t\t\t\t\t\t1);\n\t}\n\n\tXSync(g_display, False);\n\n\tx_winGC = XCreateGC(g_display, x_win, 0, (XGCValues *) 0);\n\twin_info_ptr->x_winGC = x_winGC;\n\twin_info_ptr->active = 1;\n\n\tnew_gc.fill_style = FillSolid;\n\tXChangeGC(g_display, x_winGC, GCFillStyle, &new_gc);\n\n\t/* XSync(g_display, False); */\n\n\tXFlush(g_display);\n\tfflush(stdout);\n}\n\nint g_xshm_error = 0;\n\nint\nxhandle_shm_error(Display *display, XErrorEvent *event)\n{\n\tg_xshm_error = 1;\n\treturn 0;\n}\n\nvoid\nx_allocate_window_data(Window_info *win_info_ptr)\n{\n\tint\twidth, height;\n\n\twidth = g_x_max_width;\n\theight = g_x_max_height;\n\tif(win_info_ptr->x_use_shmem) {\n\t\twin_info_ptr->x_use_shmem = 0;\t\t// Default to no shmem\n\t\tget_shm(win_info_ptr, width, height);\n\t}\n\tif(!win_info_ptr->x_use_shmem) {\n\t\tget_ximage(win_info_ptr, width, height);\n\t}\n}\n\nvoid\nget_shm(Window_info *win_info_ptr, int width, int height)\n{\n#ifdef X_SHARED_MEM\n\tXShmSegmentInfo *seginfo;\n\tXImage *xim;\n\tint\t(*old_x_handler)(Display *, XErrorEvent *);\n\tint\tdepth, size;\n\n\tdepth = g_x_screen_depth;\t\t// 24, actual bits per pixel\n\n\tseginfo = (XShmSegmentInfo *)malloc(sizeof(XShmSegmentInfo));\n\txim = XShmCreateImage(g_display, g_vis, depth, ZPixmap,\n\t\t(char *)0, seginfo, width, height);\n\n\t/* check mdepth, which should be 32 */\n\tif(xim->bits_per_pixel != g_x_screen_mdepth) {\n\t\tprintf(\"get_shm bits_per_pix: %d != %d\\n\",\n\t\t\t\txim->bits_per_pixel, g_x_screen_mdepth);\n\t}\n\tvid_printf(\"xim: %p, DO_VERBOSE:%d, Verbose:%d, VERBOSE_VIDEO:%d\\n\",\n\t\txim, DO_VERBOSE, Verbose, VERBOSE_VIDEO);\n\n\tvid_printf(\"xim: %p\\n\", xim);\n\twin_info_ptr->seginfo = seginfo;\n\tif(xim == 0) {\n\t\treturn;\n\t}\n\tvid_printf(\"bytes_per_line:%d, height:%d\\n\", xim->bytes_per_line,\n\t\t\t\t\t\t\t\txim->height);\n\tsize = xim->bytes_per_line * xim->height;\n\tvid_printf(\"size: %d\\n\", size);\n\n\t/* It worked, we got it */\n\tseginfo->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);\n\tvid_printf(\"seginfo->shmid = %d, errno:%d, %s\\n\", seginfo->shmid,\n\t\terrno, strerror(errno));\n\tif(seginfo->shmid < 0) {\n\t\tXDestroyImage(xim);\n\t\treturn;\n\t}\n\n\t/* Still working */\n\tseginfo->shmaddr = (char *)shmat(seginfo->shmid, 0, 0);\n\tvid_printf(\"seginfo->shmaddr: %p\\n\", seginfo->shmaddr);\n\tif(seginfo->shmaddr == ((char *) -1)) {\n\t\tXDestroyImage(xim);\n\t\treturn;\n\t}\n\n\t/* Still working */\n\txim->data = seginfo->shmaddr;\n\tseginfo->readOnly = False;\n\tvid_printf(\"xim->data is %p, size:%08x\\n\", xim->data, size);\n\n\t/* XShmAttach will trigger X error if server is remote, so catch it */\n\tg_xshm_error = 0;\n\told_x_handler = XSetErrorHandler(xhandle_shm_error);\n\n\tXShmAttach(g_display, seginfo);\n\tXSync(g_display, False);\n\n\n\tvid_printf(\"about to RMID the shmid\\n\");\n\tshmctl(seginfo->shmid, IPC_RMID, 0);\n\n\tXFlush(g_display);\n\tXSetErrorHandler(old_x_handler);\n\n\tif(g_xshm_error) {\n\t\tXDestroyImage(xim);\n\t\t/* We could release the shared mem segment, but by doing the */\n\t\t/* RMID, it will go away when we die now, so just leave it */\n\t\tprintf(\"Not using shared memory\\n\");\n\t\treturn;\n\t}\n\n\twidth = xim->bytes_per_line;\n\twin_info_ptr->pixels_per_line = width / 4;\n\twin_info_ptr->xim = xim;\n\twin_info_ptr->x_use_shmem = 1;\n\n\tvid_printf(\"Sharing memory. xim: %p, xim->data: %p, width:%d\\n\", xim,\n\t\txim->data, win_info_ptr->pixels_per_line);\n#endif\t/* X_SHARED_MEM */\n}\n\nvoid\nget_ximage(Window_info *win_info_ptr, int width, int height)\n{\n\tXImage\t*xim;\n\tbyte\t*ptr;\n\tint\tdepth, mdepth, size;\n\n\tdepth = g_x_screen_depth;\n\tmdepth = g_x_screen_mdepth;\n\n\tsize = (width * height * mdepth) >> 3;\n\tprintf(\"Get_ximage, w:%d, h:%d, mdepth:%d, size:%08x\\n\", width,\n\t\t\t\t\t\theight, mdepth, size);\n\tptr = (byte *)malloc(size);\n\n\tvid_printf(\"ptr: %p\\n\", ptr);\n\n\tif(ptr == 0) {\n\t\tprintf(\"malloc for data failed, mdepth: %d\\n\", mdepth);\n\t\texit(2);\n\t}\n\n\twin_info_ptr->pixels_per_line = width;\n\n\txim = XCreateImage(g_display, g_vis, depth, ZPixmap, 0,\n\t\t(char *)ptr, width, height, 8, 0);\n\n#ifdef KEGS_BIG_ENDIAN\n\txim->byte_order = MSBFirst;\n#else\n\txim->byte_order = LSBFirst;\n#endif\n\t_XInitImageFuncPtrs(xim);\t/* adjust to new byte order */\n\n\t/* check mdepth! */\n\tif(xim->bits_per_pixel != mdepth) {\n\t\tprintf(\"shm_ximage bits_per_pix: %d != %d\\n\",\n\t\t\t\t\txim->bits_per_pixel, mdepth);\n\t}\n\n\tvid_printf(\"xim: %p\\n\", xim);\n\n\twin_info_ptr->xim = xim;\n\twin_info_ptr->x_use_shmem = 0;\n\n\treturn;\n}\n\nvoid\nx_set_size_hints(Window_info *win_info_ptr)\n{\n\tXSizeHints my_winSizeHints;\n\tXClassHint my_winClassHint;\n\tXTextProperty my_winText;\n\tKimage\t*kimage_ptr;\n\tint\twidth, height, a2_width, a2_height;\n\n\twidth = win_info_ptr->width_req;\n\theight = win_info_ptr->main_height;\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\tvideo_update_scale(kimage_ptr, width, height, 0);\n\n\ta2_width = video_get_a2_width(kimage_ptr);\n\ta2_height = video_get_a2_height(kimage_ptr);\n\n\tXStringListToTextProperty(&(win_info_ptr->name_str), 1, &my_winText);\n\n\tmy_winSizeHints.flags = PSize | PMinSize | PMaxSize | PAspect;\n\tmy_winSizeHints.width = width;\n\tmy_winSizeHints.height = height;\n\tmy_winSizeHints.min_width = a2_width;\n\tmy_winSizeHints.min_height = a2_height;\n\tmy_winSizeHints.max_width = g_x_max_width;\n\tmy_winSizeHints.max_height = g_x_max_height;\n\tmy_winSizeHints.min_aspect.x = a2_width - 1;\n\tmy_winSizeHints.min_aspect.y = a2_height;\n\tmy_winSizeHints.max_aspect.x = a2_width + 1;\n\tmy_winSizeHints.max_aspect.y = a2_height;\n\tmy_winClassHint.res_name = win_info_ptr->name_str;\n\tmy_winClassHint.res_class = win_info_ptr->name_str;\n\n\tXSetWMProperties(g_display, win_info_ptr->x_win, &my_winText,\n\t\t&my_winText, 0, 0, &my_winSizeHints, 0, &my_winClassHint);\n\t// printf(\"Did XSetWMProperties w:%d h:%d\\n\", width, height);\n}\n\nvoid\nx_resize_window(Window_info *win_info_ptr)\n{\n\tKimage\t*kimage_ptr;\n\tint\tx_width, x_height, ret;\n\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\tx_width = video_get_x_width(kimage_ptr);\n\tx_height = video_get_x_height(kimage_ptr);\n\n\twin_info_ptr->main_height = MY_MIN(x_height, g_x_max_height);\n\twin_info_ptr->width_req = MY_MIN(x_width, g_x_max_width);\n\n\tret = XResizeWindow(g_display, win_info_ptr->x_win, x_width, x_height);\n\tif(0) {\n\t\tprintf(\"XResizeWindow ret:%d, w:%d, h:%d\\n\", ret, x_width,\n\t\t\t\t\t\t\t\tx_height);\n\t}\n}\n\nvoid\nx_update_display(Window_info *win_info_ptr)\n{\n\tChange_rect rect;\n\tint\tdid_copy, valid, x_active, a2_active;\n\tint\ti;\n\n\t// Update active state\n\ta2_active = video_get_active(win_info_ptr->kimage_ptr);\n\tx_active = win_info_ptr->active;\n\tif(x_active && !a2_active) {\n\t\t// We need to unmap this window\n\t\tXUnmapWindow(g_display, win_info_ptr->x_win);\n\t\tx_active = 0;\n\t\twin_info_ptr->active = x_active;\n\t}\n\tif(!x_active && a2_active) {\n\t\t// We need to map this window (and maybe create it)\n\t\tif(win_info_ptr->xim == 0) {\n\t\t\tx_create_window(win_info_ptr);\n\t\t}\n\t\tXMapWindow(g_display, win_info_ptr->x_win);\n\t\tx_active = 1;\n\t\twin_info_ptr->active = x_active;\n\t}\n\tif(x_active == 0) {\n\t\treturn;\n\t}\n\n\tif(video_change_aspect_needed(win_info_ptr->kimage_ptr,\n\t\t\twin_info_ptr->width_req, win_info_ptr->main_height)) {\n\t\tx_resize_window(win_info_ptr);\n\t}\n\tdid_copy = 0;\n\tfor(i = 0; i < MAX_CHANGE_RECTS; i++) {\n\t\tvalid = video_out_data(win_info_ptr->xim->data,\n\t\t\t\twin_info_ptr->kimage_ptr,\n\t\t\t\twin_info_ptr->pixels_per_line, &rect, i);\n\t\tif(!valid) {\n\t\t\tbreak;\n\t\t}\n#if 0\n\t\tif(win_info_ptr == &g_debugwin_info) {\n\t\t\tprintf(\"  i:%d valid:%d, w:%d h:%d\\n\", i, valid,\n\t\t\t\t\trect.width, rect.height);\n\t\t}\n#endif\n\t\tdid_copy = 1;\n#ifdef X_SHARED_MEM\n\t\tif(win_info_ptr->x_use_shmem) {\n\t\t\tXShmPutImage(g_display, win_info_ptr->x_win,\n\t\t\t\twin_info_ptr->x_winGC,\n\t\t\t\twin_info_ptr->xim, rect.x, rect.y,\n\t\t\t\trect.x, rect.y,\n\t\t\t\trect.width, rect.height, False);\n\t\t}\n#endif\n\t\tif(!win_info_ptr->x_use_shmem) {\n\t\t\tXPutImage(g_display, win_info_ptr->x_win,\n\t\t\t\twin_info_ptr->x_winGC,\n\t\t\t\twin_info_ptr->xim, rect.x, rect.y,\n\t\t\t\trect.x, rect.y,\n\t\t\t\trect.width, rect.height);\n\t\t}\n\t}\n\n\tif(did_copy) {\n\t\tXFlush(g_display);\n\t}\n}\n\nWindow_info *\nx_find_xwin(Window in_win)\n{\n\tif(g_mainwin_info.kimage_ptr) {\n\t\tif(g_mainwin_info.x_win == in_win) {\n\t\t\treturn &g_mainwin_info;\n\t\t}\n\t}\n\tif(g_debugwin_info.kimage_ptr) {\n\t\tif(g_debugwin_info.x_win == in_win) {\n\t\t\treturn &g_debugwin_info;\n\t\t}\n\t}\n\tprintf(\"in_win:%d not found\\n\", (int)in_win);\n\texit(1);\n}\n\n#define KEYBUFLEN\t128\n\nint g_num_check_input_calls = 0;\nint g_check_input_flush_rate = 2;\n\n// https://stackoverflow.com/questions/72236711/trouble-with-xsetselectionowner\n//  See answer from \"n.m\" on May 17th.\nvoid\nx_send_copy_data(Window_info *win_info_ptr)\n{\n\tprintf(\"x_send_copy_data!\\n\");\n\tXSetSelectionOwner(g_display, XA_PRIMARY, win_info_ptr->x_win,\n\t\t\t\t\t\t\tCurrentTime);\n\t(void)cfg_text_screen_str();\n}\n\nvoid\nx_handle_copy(XSelectionRequestEvent *req_ev_ptr)\n{\n\tbyte\t*bptr;\n\tint\tret;\n\n\tbptr = (byte *)cfg_get_current_copy_selection();\n\tret = XChangeProperty(g_display, req_ev_ptr->requestor,\n\t\treq_ev_ptr->property, req_ev_ptr->target, 8,\n\t\tPropModeReplace, bptr, strlen((char *)bptr));\n\t\t// req_ev_ptr->target is either XA_STRING, or equivalent\n\tif(0) {\n\t\t// Seems to return 1, BadRequest always, but it works\n\t\tprintf(\"XChangeProperty ret: %d\\n\", ret);\n\t}\n}\n\nvoid\nx_handle_targets(XSelectionRequestEvent *req_ev_ptr)\n{\n\tint\tret;\n\n\t// Tell the other client what targets we can supply from\n\t//  g_x_targets_array[]\n\tret = XChangeProperty(g_display, req_ev_ptr->requestor,\n\t\treq_ev_ptr->property, XA_ATOM, 32,\n\t\tPropModeReplace, (byte *)&g_x_targets_array[0],\n\t\tg_x_num_targets);\n\tif(0) {\n\t\t// Seems to return 1, BadRequest always, but it works\n\t\tprintf(\"XChangeProperty TARGETS ret: %d\\n\", ret);\n\t}\n}\n\nvoid\nx_request_paste_data(Window_info *win_info_ptr)\n{\n\tprintf(\"Pasting selection\\n\");\n\t// printf(\"Calling XConvertSelection\\n\");\n\tXConvertSelection(g_display, XA_PRIMARY, XA_STRING, XA_STRING,\n\t\twin_info_ptr->x_win, CurrentTime);\n\t// This will cause a SelectionNotify event, and we get the data\n\t//  by using XGetWindowProperty on our own window.  This will eventually\n\t//  call x_handle_paste().\n}\n\nvoid\nx_handle_paste(Window w, Atom property)\n{\n\tbyte\t*bptr;\n\tAtom\tsel_type;\n\tunsigned long sel_nitems, sel_bytes_after;\n\tlong\tsel_length;\n\tint\tsel_format, ret, ret2, c;\n\tint\ti;\n\n\tsel_length = 16384;\n\tsel_type = 0;\n\tsel_format = 0;\n\tsel_nitems = 0;\n\tsel_bytes_after = 0;\n\tbptr = 0;\n\tret = XGetWindowProperty(g_display, w, property, 0, sel_length, 1,\n\t\tAnyPropertyType, &sel_type, &sel_format, &sel_nitems,\n\t\t&sel_bytes_after, &bptr);\n#if 0\n\tprintf(\"XGetWindowProperty ret:%d, sel_type:%ld, sel_format:%d, \"\n\t\t\"sel_nitems:%ld, sel_bytes_after:%ld, bptr:%p\\n\",\n\t\tret, sel_type, sel_format, sel_nitems, sel_bytes_after, bptr);\n#endif\n\tif(bptr && (sel_type == property) && sel_nitems && (sel_format == 8)) {\n\t\t//printf(\"bptr: %s\\n\", (char *)bptr);\n\t\tfor(i = 0; i < sel_nitems; i++) {\n\t\t\tc = bptr[i];\n\t\t\tret2 = adb_paste_add_buf(c);\n\t\t\tif(ret2) {\n\t\t\t\tprintf(\"Paste buffer full!\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif(ret == 0) {\n\t\tXFree(bptr);\n\t}\n}\n\nint\nx_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y,\n\t\t\t\tint button_states, int buttons_valid)\n{\n\tKimage\t*kimage_ptr;\n\tint\tx, y, ret;\n\n\tif((button_states & buttons_valid & 2) == 2) {\n\t\t// Middle button: Paste request\n\t\tx_request_paste_data(win_info_ptr);\n\t\tbutton_states = button_states & (~2);\n\t}\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\tx = video_scale_mouse_x(kimage_ptr, raw_x, 0);\n\ty = video_scale_mouse_y(kimage_ptr, raw_y, 0);\n\n\tif(g_x_warp_pointer && (raw_x == g_x_warp_x) &&\n\t\t\t(raw_y == g_x_warp_y) && (buttons_valid == 0) ) {\n\t\t/* tell adb routs to recenter but ignore this motion */\n\t\tadb_update_mouse(kimage_ptr, x, y, 0, -1);\n\t\treturn 0;\n\t}\n\tret = adb_update_mouse(kimage_ptr, x, y, button_states,\n\t\t\t\t\t\t\tbuttons_valid & 7);\n\treturn ret;\n}\n\nvoid\nx_input_events()\n{\n\tXEvent\tev, response;\n\tXSelectionRequestEvent *req_ev_ptr;\n\tWindow_info *win_info_ptr;\n\tchar\t*str;\n\tint\tlen, motion, key_or_mouse, refresh_needed, buttons, hide, warp;\n\tint\twidth, height, resp_property, match, x_xpos, x_ypos;\n\tint\ti;\n\n\tstr = 0;\n\tif(str) {\n\t\t// Use str\n\t}\n\tif(adb_get_copy_requested()) {\n\t\tx_send_copy_data(&g_mainwin_info);\n\t}\n\tg_num_check_input_calls--;\n\tif(g_num_check_input_calls < 0) {\n\t\tlen = XPending(g_display);\n\t\tg_num_check_input_calls = g_check_input_flush_rate;\n\t} else {\n\t\tlen = QLength(g_display);\n\t}\n\n\tmotion = 0;\n\twin_info_ptr = 0;\n\trefresh_needed = 0;\n\tkey_or_mouse = 0;\n\twhile(len > 0) {\n\t\tXNextEvent(g_display, &ev);\n\t\tlen--;\n\t\tif(len == 0) {\n\t\t\t/* Xaccel on linux only buffers one X event */\n\t\t\t/*  must look for more now */\n\t\t\tlen = XPending(g_display);\n\t\t}\n\t\tswitch(ev.type) {\n\t\tcase FocusIn:\n\t\tcase FocusOut:\n\t\t\twin_info_ptr = x_find_xwin(ev.xfocus.window);\n\t\t\tif(ev.xfocus.type == FocusOut) {\n\t\t\t\t/* Allow keyrepeat again! */\n\t\t\t\tvid_printf(\"Left window, auto repeat on\\n\");\n\t\t\t\tx_auto_repeat_on(0);\n\t\t\t} else if(ev.xfocus.type == FocusIn &&\n\t\t\t\t\t(win_info_ptr == &g_mainwin_info)) {\n\t\t\t\t/* Allow keyrepeat again! */\n\t\t\t\tvid_printf(\"Enter window, auto repeat off\\n\");\n\t\t\t\tx_auto_repeat_off(0);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase EnterNotify:\n\t\tcase LeaveNotify:\n\t\t\t/* These events are disabled now */\n\t\t\tprintf(\"Enter/Leave event for winow %08x, sub: %08x\\n\",\n\t\t\t\t(word32)ev.xcrossing.window,\n\t\t\t\t(word32)ev.xcrossing.subwindow);\n\t\t\tprintf(\"Enter/L mode: %08x, detail: %08x, type:%02x\\n\",\n\t\t\t\tev.xcrossing.mode, ev.xcrossing.detail,\n\t\t\t\tev.xcrossing.type);\n\t\t\tbreak;\n\t\tcase ButtonPress:\n\t\t\twin_info_ptr = x_find_xwin(ev.xbutton.window);\n\t\t\tvid_printf(\"Got button press of button %d!\\n\",\n\t\t\t\t\t\tev.xbutton.button);\n\t\t\tbuttons = (1 << ev.xbutton.button) >> 1;\n\t\t\tmotion |= x_update_mouse(win_info_ptr, ev.xbutton.x,\n\t\t\t\tev.xbutton.y, buttons, buttons & 7);\n\t\t\tkey_or_mouse = 1;\n\t\t\tbreak;\n\t\tcase ButtonRelease:\n\t\t\twin_info_ptr = x_find_xwin(ev.xbutton.window);\n\t\t\tbuttons = (1 << ev.xbutton.button) >> 1;\n\t\t\tmotion |= x_update_mouse(win_info_ptr, ev.xbutton.x,\n\t\t\t\t\t\tev.xbutton.y, 0, buttons & 7);\n\t\t\tkey_or_mouse = 1;\n\t\t\tbreak;\n\t\tcase MotionNotify:\n\t\t\twin_info_ptr = x_find_xwin(ev.xmotion.window);\n\t\t\tmotion |= x_update_mouse(win_info_ptr, ev.xmotion.x,\n\t\t\t\t\t\tev.xmotion.y, 0, 0);\n\t\t\tkey_or_mouse = 1;\n\t\t\tbreak;\n\t\tcase Expose:\n\t\t\twin_info_ptr = x_find_xwin(ev.xexpose.window);\n\t\t\trefresh_needed = -1;\n\t\t\t//printf(\"Got an Expose event\\n\");\n\t\t\tbreak;\n\t\tcase NoExpose:\n\t\t\t/* do nothing */\n\t\t\tbreak;\n\t\tcase KeyPress:\n\t\tcase KeyRelease:\n\t\t\tx_handle_keysym(&ev);\n\t\t\tkey_or_mouse = 1;\n\t\t\tbreak;\n\t\tcase KeymapNotify:\n\t\t\tbreak;\n\t\tcase DestroyNotify:\n\t\t\twin_info_ptr = x_find_xwin(ev.xdestroywindow.window);\n\t\t\tvideo_set_active(win_info_ptr->kimage_ptr, 0);\n\t\t\twin_info_ptr->active = 0;\n\t\t\tprintf(\"Destroy %s\\n\", win_info_ptr->name_str);\n\t\t\tif(win_info_ptr == &g_mainwin_info) {\n\t\t\t\tx_try_xset_r();\t\t// Mainwin: quit BURST\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ReparentNotify:\n\t\tcase UnmapNotify:\n\t\tcase MapNotify:\n\t\t\tbreak;\n\t\tcase ClientMessage:\n\t\t\twin_info_ptr = x_find_xwin(ev.xclient.window);\n\t\t\tif(ev.xclient.data.l[0] == win_info_ptr->delete_atom) {\n\t\t\t\t// This is a WM_DELETE_WINDOW event\n\t\t\t\t// Just unmap the window\n\t\t\t\twin_info_ptr->kimage_ptr->active = 0;\n\t\t\t} else {\n\t\t\t\tprintf(\"unknown ClientMessage\\n\");\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ConfigureNotify:\n\t\t\twin_info_ptr = x_find_xwin(ev.xconfigure.window);\n\t\t\twidth = ev.xconfigure.width;\n\t\t\theight = ev.xconfigure.height;\n#if 0\n\t\t\tprintf(\"ConfigureNotify, width:%d, height:%d\\n\",\n\t\t\t\twidth, height);\n#endif\n\t\t\tvideo_update_scale(win_info_ptr->kimage_ptr, width,\n\t\t\t\t\t\t\t\theight, 0);\n\t\t\tx_xpos = ev.xconfigure.x;\n\t\t\tx_ypos = ev.xconfigure.y;\n\t\t\tvideo_update_xpos_ypos(win_info_ptr->kimage_ptr,\n\t\t\t\t\t\t\tx_xpos, x_ypos);\n\t\t\tbreak;\n\t\tcase SelectionRequest:\n\t\t\t//printf(\"SelectionRequest received\\n\");\n\t\t\treq_ev_ptr = &(ev.xselectionrequest);\n\t\t\t// This is part of the dance for copy: Another client\n\t\t\t//  is asking us what format we can supply (TARGETS),\n\t\t\t//  or is doing to tell us one at a time what types\n\t\t\t//  it would like.\n#if 0\n\t\t\tprintf(\"req:%ld, property:%ld, target:%ld, \"\n\t\t\t\t\"selection:%ld\\n\", req_ev_ptr->requestor,\n\t\t\t\treq_ev_ptr->property, req_ev_ptr->target,\n\t\t\t\treq_ev_ptr->selection);\n\t\t\tstr = XGetAtomName(g_display, req_ev_ptr->target);\n\t\t\tprintf(\"XAtom target str: %s\\n\", str);\n\t\t\tXFree(str);\n\t\t\tstr = XGetAtomName(g_display, req_ev_ptr->property);\n\t\t\tprintf(\"XAtom property str: %s\\n\", str);\n\t\t\tXFree(str);\n#endif\n\t\t\tresp_property = None;\n\t\t\tmatch = 0;\n\t\t\tfor(i = 0; i < g_x_num_targets; i++) {\n\t\t\t\tif(req_ev_ptr->target == g_x_targets_array[i]) {\n\t\t\t\t\tmatch = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(match) {\n\t\t\t\tx_handle_copy(req_ev_ptr);\n\t\t\t\tresp_property = req_ev_ptr->property;\n\t\t\t} else if(req_ev_ptr->target == g_x_atom_targets) {\n\t\t\t\t// Some other agent is asking us \"TARGETS\",\n\t\t\t\t//  so send our list of targets\n\t\t\t\tx_handle_targets(req_ev_ptr);\n\t\t\t}\n\t\t\t// But no matter what the request target was, respond\n\t\t\t//  so it will send an eventual request for XA_STRING\n\t\t\tresponse.xselection.type = SelectionNotify;\n\t\t\tresponse.xselection.display = req_ev_ptr->display;\n\t\t\tresponse.xselection.requestor = req_ev_ptr->requestor;\n\t\t\tresponse.xselection.selection = req_ev_ptr->selection;\n\t\t\tresponse.xselection.target = req_ev_ptr->target;\n\t\t\tresponse.xselection.property = resp_property;\n\t\t\tresponse.xselection.time = req_ev_ptr->time;\n\t\t\tXSendEvent(g_display, req_ev_ptr->requestor, 0, 0,\n\t\t\t\t\t\t\t\t&response);\n\t\t\tXFlush(g_display);\t// Speed up getting more resp\n\t\t\tbreak;\n\t\tcase SelectionNotify:\n\t\t\t// We get this event after we requested the PRIMARY\n\t\t\t//  selection, so paste this to adb().\n\t\t\tvid_printf(\"SelectionNotify received\\n\");\n\t\t\tvid_printf(\"req:%ld, selection:%ld, target:%ld, \"\n\t\t\t\t\"property:%ld\\n\", ev.xselection.requestor,\n\t\t\t\tev.xselection.selection,\n\t\t\t\tev.xselection.target,\n\t\t\t\tev.xselection.property);\n\t\t\tif(ev.xselection.property == None) {\n\t\t\t\tprintf(\"No selection\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tx_handle_paste(ev.xselection.requestor,\n\t\t\t\t\t\t\tev.xselection.property);\n\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tprintf(\"X event 0x%08x is unknown!\\n\",\n\t\t\t\tev.type);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(key_or_mouse && (win_info_ptr == &g_mainwin_info)) {\n\t\thide = adb_get_hide_warp_info(win_info_ptr->kimage_ptr, &warp);\n\t\tif(warp != g_x_warp_pointer) {\n\t\t\tmotion = 1;\n\t\t}\n\t\tg_x_warp_pointer = warp;\n\t\tif(g_x_hide_pointer != hide) {\n\t\t\tx_hide_pointer(&g_mainwin_info, hide);\n\t\t}\n\t\tg_x_hide_pointer = hide;\n\t}\n\n\tif(motion && g_x_warp_pointer && (win_info_ptr == &g_mainwin_info)) {\n\t\t// Calculate where to warp to\n\t\tg_x_warp_x = video_unscale_mouse_x(win_info_ptr->kimage_ptr,\n\t\t\tBASE_MARGIN_LEFT + (BASE_WINDOW_WIDTH/2), 0);\n\t\tg_x_warp_y = video_unscale_mouse_y(win_info_ptr->kimage_ptr,\n\t\t\tBASE_MARGIN_TOP + (A2_WINDOW_HEIGHT/2), 0);\n\n\t\tXWarpPointer(g_display, None, win_info_ptr->x_win, 0, 0, 0, 0,\n\t\t\tg_x_warp_x, g_x_warp_y);\n\t}\n\n\tif(refresh_needed && win_info_ptr) {\n\t\t//printf(\"...at end, refresh_needed:%d\\n\", refresh_needed);\n\t\tvideo_set_x_refresh_needed(win_info_ptr->kimage_ptr, 1);\n\t}\n\n}\n\nvoid\nx_hide_pointer(Window_info *win_info_ptr, int do_hide)\n{\n\tif(do_hide) {\t\t\t\t// invisible\n\t\tXDefineCursor(g_display, win_info_ptr->x_win, g_cursor);\n\t} else {\t\t\t\t// Default cursor\n\t\tXDefineCursor(g_display, win_info_ptr->x_win, None);\n\t}\n}\n\nvoid\nx_handle_keysym(XEvent *xev_in)\n{\n\tWindow_info *win_info_ptr;\n\tKeySym\tkeysym;\n\tword32\tstate;\n\tint\tkeycode, a2code, type, is_up;\n\n\twin_info_ptr = x_find_xwin(xev_in->xkey.window);\n\n\tkeycode = xev_in->xkey.keycode;\n\ttype = xev_in->xkey.type;\n\n\tkeysym = XLookupKeysym(&(xev_in->xkey), 0);\n\n\tstate = xev_in->xkey.state;\n\n\tvid_printf(\"keycode: %d, type: %d, state:%d, sym: %08x\\n\",\n\t\tkeycode, type, state, (word32)keysym);\n\n\tx_update_modifier_state(win_info_ptr, state);\n\n\tis_up = 0;\n\tif(type == KeyRelease) {\n\t\tis_up = 1;\n\t}\n\n\t/* first, do conversions */\n\tswitch(keysym) {\n\tcase XK_Alt_L:\n\tcase XK_Meta_R:\t\t\t\t// Windows key on right side\n\tcase XK_Super_R:\n\tcase XK_Mode_switch:\n\tcase XK_Cancel:\n\t\tkeysym = XK_Print;\t\t/* option */\n\t\tbreak;\n\tcase XK_Meta_L:\t\t\t\t// Windows key on left side\n\tcase XK_Alt_R:\n\tcase XK_Super_L:\n\tcase XK_Menu:\n\t\tkeysym = XK_Scroll_Lock;\t/* cmd */\n\t\tbreak;\n\tcase XK_F5:\n\t\tbreak;\n\tcase XK_F10:\n\t\tif(!is_up) {\n\t\t\tg_video_scale_algorithm++;\n\t\t\tif(g_video_scale_algorithm >= 3) {\n\t\t\t\tg_video_scale_algorithm = 0;\n\t\t\t}\n\t\t\tprintf(\"g_video_scale_algorithm = %d\\n\",\n\t\t\t\t\t\tg_video_scale_algorithm);\n\t\t\tvideo_update_scale(win_info_ptr->kimage_ptr,\n\t\t\t\t\twin_info_ptr->width_req,\n\t\t\t\t\twin_info_ptr->main_height, 1);\n\t\t}\n\t\tbreak;\n\tcase 0x1000003:\n\t\tif(keycode == 0x3c) {\n\t\t\t/* enter key on Mac OS X laptop--make it option */\n\t\t\tkeysym = XK_Print;\n\t\t}\n\t\tbreak;\n\tcase NoSymbol:\n\t\tswitch(keycode) {\n\t\t/* 94-95 are for my PC101 kbd + windows keys on HPUX */\n\t\tcase 0x0095:\n\t\t\t/* left windows key = option */\n\t\t\tkeysym = XK_Print;\n\t\t\tbreak;\n\t\tcase 0x0096:\n\t\tcase 0x0094:\n\t\t\t/* right windows key = cmd */\n\t\t\tkeysym = XK_Scroll_Lock;\n\t\t\tbreak;\n\t\t/* 0072 is for cra@WPI.EDU who says it's Break under XFree86 */\n\t\tcase 0x0072:\n\t\t/* 006e is break according to mic@research.nj.nec.com */\n\t\tcase 0x006e:\n\t\t\tkeysym = XK_Break;\n\t\t\tbreak;\n\n\t\t/* 0x0042, 0x0046, and 0x0048 are the windows keys according */\n\t\t/*  to Geoff Weiss on Solaris x86 */\n\t\tcase 0x0042:\n\t\tcase 0x0046:\n\t\t\t/* flying windows == open apple */\n\t\t\tkeysym = XK_Scroll_Lock;\n\t\t\tbreak;\n\t\tcase 0x0048:\n\t\tcase 0x0076:\t\t/* Windows menu key on Mac OS X */\n\t\t\t/* menu windows == option */\n\t\t\tkeysym = XK_Print;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\ta2code = x_keysym_to_a2code(win_info_ptr, (int)keysym, is_up);\n\tif(a2code >= 0) {\n\t\tadb_physical_key_update(win_info_ptr->kimage_ptr, a2code,\n\t\t\t0, is_up);\n\t} else if(a2code != -2) {\n\t\tprintf(\"Keysym: %04x of keycode: %02x unknown\\n\",\n\t\t\t(word32)keysym, keycode);\n\t}\n}\n\nint\nx_keysym_to_a2code(Window_info *win_info_ptr, int keysym, int is_up)\n{\n\tint\ti;\n\n\tif(keysym == 0) {\n\t\treturn -1;\n\t}\n\n\t/* Look up Apple 2 keycode */\n\tfor(i = g_num_a2_keycodes - 1; i >= 0; i--) {\n\t\tif((keysym == g_x_a2_key_to_xsym[i][1]) ||\n\t\t\t\t\t(keysym == g_x_a2_key_to_xsym[i][2])) {\n\n\t\t\tvid_printf(\"Found keysym:%04x = a[%d] = %04x or %04x\\n\",\n\t\t\t\t(int)keysym, i, g_x_a2_key_to_xsym[i][1],\n\t\t\t\tg_x_a2_key_to_xsym[i][2]);\n\n\t\t\treturn g_x_a2_key_to_xsym[i][0];\n\t\t}\n\t}\n\n\treturn -1;\n}\n\nvoid\nx_update_modifier_state(Window_info *win_info_ptr, int state)\n{\n\tword32\tc025_val;\n\n\tc025_val = 0;\n\tif(state & ShiftMask) {\n\t\tc025_val |= 1;\n\t}\n\tif(state & ControlMask) {\n\t\tc025_val |= 2;\n\t}\n\tif(state & LockMask) {\n\t\tc025_val |= 4;\n\t}\n\tadb_update_c025_mask(win_info_ptr->kimage_ptr, c025_val, 7);\n}\n\nvoid\nx_auto_repeat_on(int must)\n{\n\tif((g_auto_repeat_on <= 0) || must) {\n\t\tg_auto_repeat_on = 1;\n\t\tXAutoRepeatOn(g_display);\n\t\tXFlush(g_display);\n\t\tadb_kbd_repeat_off();\n\t}\n}\n\nvoid\nx_auto_repeat_off(int must)\n{\n\tif((g_auto_repeat_on != 0) || must) {\n\t\tXAutoRepeatOff(g_display);\n\t\tXFlush(g_display);\n\t\tg_auto_repeat_on = 0;\n\t\tadb_kbd_repeat_off();\n\t}\n}\n\nvoid\nx_full_screen(int do_full)\n{\n\treturn;\n}\n"
  },
  {
    "path": "upstream/KEGS.version",
    "content": "kegs.1.38\n"
  },
  {
    "path": "upstream/kegs/config.kegs",
    "content": "# KEGS configuration file version 1.07\n\ns5d1 = XMAS_DEMO.gz\ns5d2 =\n\ns6d1 = #dos33.dsk\ns6d2 = \n\ns7d1 = NUCLEUS03.gz\n\ng_limit_speed = 2\n\n"
  },
  {
    "path": "upstream/kegs/doc/CHANGES.txt",
    "content": "\nChanges in KEGS v1.38 since v1.35 (04/29/25)\n- Improve serial port handling so BBS'es like GBBS and Warp6 run.\n- Fix a crash reported by Tom Charlesworth where resizing the Debugger\n\twindow would crash on Windows.\n- Fix a timing anomaly also reported by Tom Charlesworth where 3- and 4-byte\n\tinstructions just before a page crossing would take too many cycles\n\tto execute.\n- Add Alex Lee's icon on the Mac.\n\nChanges in KEGS v1.35 since v1.34 (01/07/25)\n- Fix handling of .zip files, it somehow got broken so subdirectories in\n\tzip files weren't selectable.\n- DCD fixes for the virtual modem, and fix CONNECT terse response code to\n\ttry to support the Warp6 BBS.\n\n\nChanges in KEGS v1.34 since v1.33 (01/15/24)\n- Fix bug where no config.kegs file could sometimes cause a crash.\n- Richard Bennett fixes:  Change menus to be retina, and add About dialog on\n\ta Mac\n- Save the main window size and position in config.kegs (but not automatically)\n\n\nChanges in KEGS v1.33 since v1.32 (12/10/23)\n- Add command line argument support to set any disk (-s5d1=Data.po\n\tor -s7d12 bigdisk.hdv) or any knob that is listed in config.kegs.\n- Add -cfg path_and_name_of_config_kegs file to use a particular config.kegs\n\tfile.\n- Fix Code Red when leaving the configuration screen (F4) using the last\n\tmenu item \"Exit Config\".\n- Allow pasting of control-characters such as Ctrl-D and Ctrl-H.\n\n\nChanges in KEGS v1.32 since v1.31 (11/22/23)\n- Fix (dloc,x) in emulation mode to wrap as described at\n\thttps://github.com/gilyon/snes-tests/tree/main/cputest\n- Improve virtual hard drive in slot 7 to use a small driver at $C700 which\n\tuses WDM $C7,$00 to call back to KEGS for handling commands.\n- Improve SCC8530 emulation to add support for RTxC as clock for higher speeds.\n- Fix a GS/OS visual anomaly with the mouse cursor sometimes disappearing\n\twhen moving upwards, due to Scanline interrupts not being taken\n\tproperly.\n- Support qkumba's code to run from $C050 by having get_remaing_opcodes()\n\ttrack time properly, and to have reads to $C050-$C057 call float_bus()\n\tbefore doing the softswitch action.\n\n\nChanges in KEGS v1.31 since v1.30 (11/04/23)\n- Fix Windows failure where KEGS would quit on startup if config.kegs\n\tcontained a new ROM path.\n- Fix a Code Red halt running the Printer57.6 driver where KEGS thought it\n\tmight need to generate a baud rate event every .5 cycles.\n- Fix disk image selection screen bug where s7d10-s7d12 could wrap and make\n\tit hard to leave the screen.\n- Add a Slinky RAM card in slot 4 (with no firmware), works even with\n\tMockingboard.\n- Fix scanline interrupts which were happening too early starting with\n\tversion 1.24.\n- Another false read bug was causing 16-bit RMW cycles to read the next\n\taddress (which is incorrect).\n\n\nChanges in KEGS v1.30 since v1.29 (09/23/23)\n- Proper emulation of the $C080-$C08F language card soft switches.\n- Improved INTC8ROM emulation, so a2audit passes (with Apple //e ROMs)\n- Fix SCC RR2B register emulation for Colin Leroy-Mira's telnet.system.\n- Fix SCC remote IP mode to restart the connection if the remote side ended\n\tit due to idleness, to better handle connecting to printers.\n- RAMRD/RAMWRT/ST80COL/etc. apply to bank $E0 as well as to bank $00.  This\n\twas not emulated properly before.  This fixes the AppleLink Terminal\n\tapplication.\n\n\nChanges in KEGS v1.29 since v1.28 (09/05/23)\n- Improved disk arm emulation for 5.25\" disks\n- Enable use of a real serial port on Linux, and improve real serial port\n\temulation on Mac.\n- Add serial \"outgoing IP\" to allow slot 1 serial to be sent directly to\n\ta real printer (often port 9100).\n- When mounting an image from a .zip file, you can press Cmd-A and all\n\tsubsequent images will also be mounted in consecutive drives.\n\tThis is useful for the new wita2gs_0_70.zip.\n- Allow \"unlocking\" locked images from .zip files, to allow code to write to\n\tthem (but it's all in memory, so all changes are lost when KEGS\n\texits).\n- Fix DiskCopy4.2 image detection, to handle images of sizes other than 800KB.\n\n\nChanges in KEGS v1.28 since v1.27 (06/21/23)\n- Reduce status lines under the window from 7 lines of text to 4.\n- Fix Windows10 crash when KEGS was minimized (Windows set the window size to\n\t0, which was unexpected, and led to a divide-by-0).\n- Enable live window resizing on Windows64.  Speed up the video scaling for\n\tX11 and Windows.\n- Allow the ZipGS speed, which was fixed at 8MHz previously), be set to\n\t8MHz, 16MHz, 32MHz, 64MHz, or 128MHz.\n- Fix a false read bug which broke SCC emulation.  LDA $BFFD,X where X=$3D\n\twas \"false\" reading $C039, not $BF39 as it should, leading to SCC\n\tstate being incorrect.\n\n\nChanges in KEGS v1.27 since v1.26 (06/13/23)\n- Ignore WDM 0xfc,0xfd,0xff to avoid HOST.FST causing Code Red.\n- Fix $C019 reading to match Deater's Midline demo\n- Add false reads for RMW instruction, (dloc),y, abs,x, and abs,y modes.\n\tOnly 8-bit false reads are done currently (this only affect 16-bit\n\tRMW, where I know of nothing using the false reads).\n- Fix reported bug where long paths in the file selection screens didn't\n\ttruncate the files so the endings could always be seen.\n- Allow 140K .SDK images to be selected in the image file selection screen.\n- Fix $C030 speaker toggle emulation to eliminate a 60-Hertz buzz caused by\n\tthe code being organized around 60Hz screen refresh and not counting\n\ton the last toggle correctly in each video frame in all cases.  The\n\t$C030 speaker output ramps down to 0 after about 60msec to avoid the\n\tannoying \"click\" 4 seconds later when KEGS pauses sound output.\n\n\nChanges in KEGS v1.26 since v1.25 (05/22/23)\n- Fix Win64 Dynapro issues (O_BINARY, setvbuf was causing a crash).\n- Fix a KEGS bug in the Bank $E0 memory map which could corrupt data in the\n\tApple IIgs memory from $E0/6000 - $E0/A000 introduced in KEGS 1.20.\n\n\nChanges in KEGS v1.25 since v1.24 (05/21/23)\n- Actual Win32 support.  1.24 was only Win64.\n\n\nChanges in KEGS v1.24 since v1.23 (05/17/23)\n- Win64 support.  kegswin.exe now part of the standard release.  The Windows\n\tport is still beta quality.\n- Try to fix jerky video to make KEGS seem smoother.\n\n\nChanges in KEGS v1.23 since v1.22 (05/05/23)\n- Change the way KEGS tracks time from a double to a unsigned long long,\n\twhich enables higher speeds.\n- Support video mode changes in the middle of lines.\n\n\nChanges in KEGS v1.22 since v1.21 (04/27/23)\n- Remove debugging printfs from iwm.c.\n- Fix the way video updates are done to fix Dagen Brock's HDGR demo (which\n\tswitches pages to double the GR vertical resolution).\n\n\nChanges in KEGS v1.21 since v1.20 (04/15/23)\n- Fix Antoine's reported issue where KEGS would hang after ejecting 3.5\" disks.\n\tCaused by a debug statement accidentally left in for the release.\n- Fix Stephan's reported issue with keys repeating forever with French\n\tkeyboards.\n\n\nChanges in KEGS v1.20 since v1.19 (03/31/23)\n- Ctrl-F9 is now Copy.  The text screen is copied to your host system\n\tclipboard.  On a Mac, Edit->Copy Text Screen can be selected, too.\n- Fix Mockingboard emulation to pass mb-audit.1.3 (it was a reset-related\n\tissue).\n- Fix VOC support for unreleased \"Fat Screen\" VOC SHR from main-memory to\n\twork properly.\n\n\nChanges in KEGS v1.19 since v1.18 (03/11/23)\n- 'L' on the disk selection screen locks/unlocks images.\n- Ignore case when detecting image extensions like .PO.\n- Allow setting ROM image on command line: -rom=/path/to/rom/file\n- Big changes to disk emulation for better WOZ image compability.\n\tWriting to WOZ images works properly now, recalculating CRC correctly.\n\tAutomatically changes a floppy disk image to .WOZ if writing to the\n\timage makes it no longer a standard format.  User can rename image to\n\tsave the new .WOZ changes.\n\n\nChanges in KEGS v1.18 since v1.17 (02/09/22)\n- Alpha version of KEGSWIN, KEGS can now run on Windows 10 or later.\n\t(No binary yet)\n\n\nChanges in KEGS v1.17 since v1.16 (02/09/22)\n- Implement $C02C \"Test Mode\" reading of the character ROM.  This enables\n\tSuperConvert 4 TDM conversions to work properly.\n- Add Video->Swap Command/Option menu item to support Windows keyboards better.\n\n\nChanges in KEGS v1.16 since v1.14 (01/23/22)\n- Better cursor focus tracking, less likely to have an invisible cursor\n\twhen KEGS is no longer the active window.\n- F5 now toggles the status display at the bottom on/off.  This state can\n\tbe saved in config.kegs.\n- Added Dynapro image support--mount a host directory as a ProDOS image of\n\tup to 32MB, to allow easy moving of files to/from emulation.\n\tSee README.dynapro.txt.\n- Add limited Video Overlay Card (VOC) support to add new SHR mode of 640x400\n\t(interlaced).\n- You can \"D\"uplicate any disk image to a new file, and \"V\"erify any ProDOS\n\timage.\n\n\nChanges in KEGS v1.14 since v1.13 (11/14/21)\n- Better support for disk images inside .zip files.\n- Linux sound fixes to make PULSE_AUDIO work a little better.\n- Better handle being run from the Finder, and go right to the Config\n\tpage to select a ROM file if no ROM is found.\n- Add NSHighResolutionCapable=False to speed up graphics operations on some\n\tMacs\n- Fix serial port code to properly return the DCD status as a modem.\n\n\nChanges in KEGS v1.13 since v1.12 (09/23/21)\n- Better support for joysticks on Macs.  Select Native Joystick 1.\n\n\nChanges in KEGS v1.12 since v1.11 (08/22/21)\n- Preliminary support for joysticks on Macs.  Select Native Joystick 1.\n- Fixes for Keypad Joystick to work again (the non-US keyboard support broke\n\tthem).\n\nChanges in KEGS v1.11 since v1.08 (08/19/21)\n- KEGS should support many non-US keyboards, converting your local\n\tkeys to the US equivalents: [,],|, etc.  Italian works least well,\n\tsorry.\n- KEGS can choose character ROMs out of a ROMX-compatible file.  Use F4,\n\t\"Character ROM Selection\", and then pick the file and font.\n\tIf you pick an unreadable font and cannot undo it for some reason,\n\tquit KEGS, and using a text editor, delete g_cfg_charrom_* lines from\n\tconfig.kegs.\n- Taking an IRQ logs the stack accesses in the datalog properly.\n- Fixed BRK and COP exceptions to always pull their vectors from ROM.\n\tBug was found by Applecorn: https://github.com/bobbimanners/Applecorn\n\n\nChanges in KEGS v1.08 since v1.07 (06/29/21)\n- Fixed a stupid bug in iwm.c that would cause KEGS to crash when many WOZ\n\timages were mounted.\n\n\nChanges in KEGS v1.07 since v1.05 (06/26/21)\n- Can create new disk images while running: press N in the disk image\n\tselection screen.\n- Support for loading disk images directly out of .zip archives (read-only).\n\tIn the disk image selection dialog, select the .zip file, and then\n\tit will step into the archive and you can select the file inside.\n- Major rewrite of IWM routines to now add support for WOZ version 1 and\n\tversion 2 5.25\" disk images.\n- Many Mockingboard fixes to enable mb-audit v0.7 to pass.\n\thttps://github.com/tomcw/mb-audit\n\n\nChanges in KEGS v1.05 since v1.04 (01/24/21)\n- The Mac executable is now universal, support M1 silicon and x86_64.\n- F8 works again to confine the mouse to the KEGS window.  Press F8 again\n\tto release the cursor.\n\n\nChanges in KEGS v1.04 since v1.03 (01/10/21)\n- Paste works from the host to the emulated machine.  On the Mac,\n\tselect text in another app, do Cmd-C, and then in KEGS select\n\tthe Edit menu->Paste.  On X11, select the text in another application,\n\tthen in KEGS, click the middle mouse button to paste.  Up to ~32KB\n\tcan be pasted, but I recommend smaller amounts. \n- On the Mac, the Config menu item will bring up the Configuration screen\n\t(same as pressing F4).\n- Fix bug where Nox Archaist running on 5.25\" floppies with fast_disk_emul\n\ton (which is the default) would cause KEGS to halt and not write\n\tmodified data back to the disk image.  Nox Archaist doesn't read\n\tthe entire sector header, waits about 7 disk nibble times, then\n\twrites new sector data.  This confused fast disk emulation which\n\tdoesn't move the emulated disk position just by waiting.  Now, when\n\ta write begins, KEGS will move the emulated disk position.\n- Added logpc debugger command in the F7 debugger window.  KEGS can keep\n\ttrack of all registers after each instruction for up to 2 million\n\tinstructions.  This is dumped out to the file pc_log_out in the\n\tdirectory where config.kegs was found.  \n\tIn the debugger window \"help logpc\" gives basic help.  \"logpc on\"\n\tturns on logging, and \"logpc save\" writes out the last 2 million\n\tinstructions.\n- Reading $C020 and $C030 return a floating bus value.  This fixes\n\tBeyond Castle Wolfenstain randomness as reported by Vladimir\n\tIvanov.\n\thttps://groups.google.com/g/comp.sys.apple2/c/3gH0dUpLI3Q/m/JJYnhRYBrY4J\n\n\nChanges in KEGS v1.03 since v1.00 (12/11/20)\n- This is beta quality\n- Add Mockingboard support.  In the IIgs control panel, set slot 4 to Your\n\tCard, and KEGS will emulate a stereo Mockingboard A.\n- Disk images can be gzip compressed (master.dsk.gz, for example) and KEGS\n\tcan load them as readonly.\n- .sdk disk images can be loaded directly (read only).  Only the first disk\n\timage in a .sdk archive will be opened.\n- Debugger is now a separate window.  Press F7.  \"bp\" allows setting a range,\n\tso \"bp c400-c4ff\" sets breakpoints on all addresses in that range.\n- If the debugger window pops up, it means emulation has halted.  Press 'g'\n\tand then return in the debugger window to try to restart KEGS.\n\tIt may just halt again.  This area needs improvement, you will likely\n\tneed to quit out of KEGS and start over if 'g' a few times\n\tdoesn't continue emulation.\n- KEGS allows resizing windows.  On Linux X11, KEGS does the scaling.\n- Removed HP PA-RISC assembly, which simplifies compiling a bit.\n- 2.8MHz speed now emulated at 2.8MHz, which is a little fast, but makes\n\tsome very timing-sensitive code in the NFS Megademo work better.\n\n--------------------------------------------------------------------\n\nChanges in KEGS v1.00 since v0.91 (12/15/19)\n- This is pre-alpha quality\n- Major rewrite of how the host interacts with the KEGS emulation code.\n- Now supports MAC OS X Swift native compiles on Mojave (and later)\n\n--------------------------------------------------------------------\n\nChanges in KEGS v0.91 since v0.90 (12/06/04)\n- Fixed serious bug in engine_c.c that could cause Finder file copies to\n\tsilently corrupt data.\n- Virtual Modem support--modem appears on serial port, allows outgoing\n\tand incoming connections.\n- Sockets (and Virtual Modem) supported on Windows.\n- Fixed various reset bugs (where pressing Ctrl-Reset would cause infinite\n\tbeeps, etc).\n- Allow user to select ROM file from config panel if not found.\n- Improved Mac OS X interface: Full Screen support and error dialogs.\n- Better floppy support by always having 5.25\" read nearest track regardless\n\tof head position (supports Last Gladiator game bad crack by\n\temulating other emulators).\n\nChanges in KEGS v0.90 since v0.89 (10/19/04)\n- Make Keypad Joystick the default joystick emulation\n- Fix timezone calculation on Mac OS X for central time zone.\n- Fix handling of long paths in config panel, reported by David Scruffham.\n- Always call joystick_init at startup.\n- Fix F2 keymappings for X Windows, to fix some issue reported by\n\tDavid Wilson.\n- Fixed some documentation issues reported by David Wilson.\n- Fixed a bug in joystick_driver.c reported by Doug Mitton.\n- Add README.a2.compatibility to discuss known issues with programs.\n\nChanges in KEGS v0.89 since v0.88 (10/17/04)\n- Make old mouse button presses disappear after .5 seconds.\n- Add Keypad Joystick, along with configuration menu choices to enable it.\n\tThe keypad numbers move the joystick to the indicated direction,\n\twith chording allowing in-between values.\n\tThe keypad '0' is button 0 and keypad '1' is button 1.\n- Also add jostick scaling factor and trim adjustment.\n- Allow user to increase keyboard and mouse scan rate to 240Hz from 60Hz\n\tfor some better game response.\n\nChanges in KEGS v0.88 since v0.87 (10/13/04)\n- Add configuration setting to debug halt on code red halts.  Also add\n\tconfiguration mode (on by default) to shadow text page 2 on ROM 01,\n\twhich is an enhancement over a real IIgs.\n- Handle mac binary header on images.  Handle compressed .po images.\n- Fix refresh rate to 59.923Hz from 60Hz so that exactly 17030 1MHz cycles\n\tpass in one screen refresh period.\n- Enhance trace-to-file function to also write out data values stored using\n\tthe Data_log info.\n- Debugger adds memory move and memory compare functions.\n- Support \"floating bus\" where reading certain $C0xx locations returns the\n\tcurrent video data.  This allows Bob Bishop's split-screen demos to\n\trun and enables Drol's between-level animations to work fully.\n\nChanges in KEGS v0.87 since v0.86 (10/05/04)\n- Remove all of Jonathan Kalbfeld's and Gilles Tschopp's contributions.\n\tAll of Solaris audio is removed.\n- Fix config screen not drawing correctly if emulator was currently displaying\n\tvideo page 2.\n- Fix STP instruction.\n- Fix mouse-joystick which was halving the Y dimension.\n\nChanges in KEGS v0.86 since v0.85 (03/23/04)\n- Add patch for Solaris sound by Jonathan Kalbfeld.\n- Fix so that F4 enters config panel even while running Prosel-16\n- Major mouse pointer changes, based on some ideas from Geoff Weiss.\n\tThe GSOS mouse now exactly tracks the host pointer automatically.\n- Fixed an accidental debug halt when Prosel-16 disables the keyboard/mouse.\n\nChanges in KEGS v0.85 since v0.84 (01/09/04)\n- Fix some minor 65816 bank-crossing bugs.\n- Add -noignhalt to allow user to stop on code red halts.\n- Fix Win32 capslock problem as reported by Edward Moore\n- Fixed DreamVoir app on the sample image (it was corrupt)\n\nChanges in KEGS v0.84 since v0.83 (11/21/03)\n- Add new speed, 8.0MHz directly using right-clicking or F6.\n- Sim speed and Video update interval added to Config panel.\n- Various cycle timing bugs in engine_c.c fixed.\n- Add Config Panel entry to mask serial output to 7-bit, to enable PR#2 to\n\twork better with an external telnet.\n- In Config Panel file selection, typing a letter jumps to the first file\n\tbeginning with that letter.\n- Fixed various serial socket bugs.  Now you can disconnect a telnet session\n\tand start a new one, and a Linux hang is fixed.\n- Default GS memory size increased to 8MB.\n- Small fix to double-hires color table.\n- X windows can now send displays to other-endian X servers.\n\nChanges in KEGS v0.83 since v0.82 (11/19/03)\n- Add Memory Size to config panel, with support for up to 14MB of memory\n\t(Geoff Weiss)\n- Add $C04F EMUBYTE support which Bernie II the Rescue defined. (Geoff Weiss)\n- Fix $CFFF code red's reported by David Wilson.\n- Add smartport $C70A Format routine (David Wilson).\n\nChanges in KEGS v0.82 since v0.81 (11/06/03)\n- Fix superhires display glitch introduced in v0.81.\n- Improved border handling--XMAS demo looks great.\n- Fix some X build problems introduced in v0.81.\n\nChanges in KEGS v0.81 since v0.80 (11/04/03)\n- Code Red/Yellow warnings about emulation stability\n- Windows file browsing fixes\n- Built-in C600 ROM for Apple II 5.25\" game compatibility\n- Turns key repeat back on when exiting from X-windows version\n- Windows F8 captures the cursor\n\nChanges in KEGS v0.80 since v0.71 (10/31/03)\n- Configuration Panel means no more hand-editing configuration files\n- All emulator state is now saved in \"config.kegs\"\n- 3200 color pictures!  Video system much improved for display accuracy.\n- F8 Pointer grabbing works on Mac\n- ZipGS emulation\n\nChanges in KEGS v0.71 since v0.70 (11/20/02)\n- Improved double-hires colors a lot.  -dhr140 is no longer the default\n- Airheart relies on the PC going from 0xffff to 0x0000, so I undid the\n\tchange from KEGS v0.54 which allowed PC to overflow to 0x10000.\n\tThis slows KEGS down by about 5%.\n- Fixed X shared memory bug in KEGS v0.70 with fix from Jonathan Stark.\n\nChanges in KEGS v0.70 since v0.60 (11/18/02)\n- New buttons: Middle button is enter-debugger, and right button changes speed\n- New function key mapping (see README.kegs)\n- Mac OS X port\n- Win32 port\n- Centralized much of what had been \"xdriver.c\" code into video.c, to move\n\ttrue platform-specific stuff into the various *driver.c codes.\n\tKimage struct tracks video display buffers in a dev-independent way.\n\tFrom video.c, the calls to the platform code start with \"x_\" mostly.\n\tCode in video.c cleaned up somewhat.\n\tBorders are now always in native buffer format, while text/hires/\n\tand superhires are in 8-bit buffers and translated to native later.\n- Mac and Windows sound are all done in one process--no child sound process.\n- Revamped key press handling and mouse clicks--all is now handled in\n\tadb.c for a consistent user interface.  Now KEGS implements the\n\tsame function keys on all platforms.  See README.kegs for fn key maps.\n- I copied the debugger help from Frederic Devernay's KEGS-SDL port.\n- Fixed an old IWM bug causing bad nibblization due to using uninit vars.\n- Gilles Tschopp workaround to use corrupted 2IMG files (from KEGS-OSX).\n- Gilles Tschopp provided code to zero //gs memory at startup (from KEGS-OSX)\n- Simple code to try to use Mac Diskcopy format disks\n- Ignore writes to 0xc0a8\n- Search in $HOME and the launch directory (for mac) for kegs_conf/ROM\n- Remove font65.sim file by integrating it into kegsfont.h.\n- \"-bw\" option forces black and white hires mode.\n\n\nChanges in KEGS v0.60 since v0.59 (10/03/00)\n- The 16-bit colors were still wrong due to another coding error.  It would\n\tbe much easier to get this right if I had a 16-bit color display...\n\tA user says it works now.\n\nChanges in KEGS v0.59 since v0.58 (7/07/00)\n- Added support for multiple paths to the default files and also multiple\n\tnames for many default files.  This should make the .rpm distribution\n\twork better.\n- Add another keycode to mean break according to mic@research.nj.nec.com.\n- Add support for various ROMs to get plugged into slot 1-7.\n- Fix code so that it should compile on 64-bit platforms.\n\nChanges in KEGS v0.58 since v0.57 (2/08/00)\n- Setting the execute bit on the disk image no longer means no-write-thru.\n\tToo many new users were getting confused by this.\n- Fixed another bug with Apple //e bank switching created by v0.56\n\tReported by phoenyx.\n- Add command line option \"-v\" to turn on some verbose debugging flags.\n- Fixed potential core-dump bug with non-8 bit visuals.\n- Fixed double-lo-res color problem.\n- The X driver should work with any visual depth display now and get the\n\tcolors right.  Ian Schmidt reported his 16-bit card had bad colors.\n\nChanges in KEGS v0.57 since v0.56 (12/27/99)\n- Another try at making timezone stuff work across all Unix variants.\n\tLet me know if the time in the Apple //gs control panel doesn't\n\tmatch your real local time.\n- Fix a bug created in v0.56 where the fast //e bank switch code had a typo.\n\tThis prevented ZBasic from working correctly.\n\nChanges in KEGS v0.56 since v0.55 (10/31/99)\n- Faster Apple //e bank switch emulation.\n- Simplified number of global variables for various softswitches.\n- Fixed a bug which made 3.5\" and 5.25\" disk access much slower than necessary.\n- Improved scan-line interrupt accuracy (lets MEGADEMO run).\n- Improved sound interrupt accuracy (was hoping this would fix some sound\n\tissues, but it doesn't seem to help).\n- Add Mode_switch as an alias for the Option key\n- I noticed the //gs self-tests were broken again--fixed.\n \nChanges in KEGS v0.55 since v0.54 (10/19/99)\n- In LOG_PC debug aid, add cycles to the trace\n- Fix MEGADEMO bug where 3.5\" disks weren't properly ejected.  Needed to\n\tlook at iwm.motor_on35 not iwm.motor_on.\n- Temp fix for MEGADEMO to not halt if shadow-in-all-banks is on in $c036.\n- Another MEGADEMO fix to not take a scan-line int if the SCB was cleared\n\tright before the raster got to this line.\n- Fix bug in smartport.c that was causing core dumps if you tried to init\n\ta disk is s7dx.\n\nChanges in KEGS v0.54 since v0.53 (10/10/99)\n- Add support for Out Of This World's direct reading of ADB RAM loc 0xb to\n\tget key status.  This lets shift/control work in OOTW.\n- Code simplification to get rid of most set_halt() calls and use halt_printf.\n- Speed improvement: track kpc (merged kbank and pc in one 32 bit variable)\n\twhich makes the inner loop faster.  This does make KEGS not\n\taccurately model a 65816 code crossing bank boundaries, but just\n\tabout every other emulator gets it wrong, and the speed improvement\n\tis 5-10%.  And I don't know of any code which relies on it\n\tworking correctly.\n- Fix to allow better GS/OS compatibility: after each smartport call,\n\tset 0x7f8 = 0xc7.\n- Fixed ZipGS emulation bug where KEGS was not re-locking Zip at the right\n\ttime, which made double-hires not work after booting GS/OS.\n\nChanges in KEGS v0.53 since v0.52 (8/3/99)\n- Move all the \"fcycles\" timing calculations to use double instead of float.\n- Fix display shadowing bug reported by \"phoenyx\" which caused the text\n\tdisplay to not always be updated correctly with funny bank switching.\n- Added the \"Home\" key as an alias for the '=' on the keypad.\n- Changed the way X modifiers are interpreted to increase compatibility of\n\tCaps Lock to more X servers.\n- Add -dhr140 option to use old double-hires color mode that results in\n\texactly 140 horizontal pixels with no bleeding.  It's set default\n\tto \"on\" for now while I work out double-hires colors.\n- Started adding some ZipGS compatibility--control panels run, but all\n\tthe controls are effectively ignored by KEGS.\n\nChanges in KEGS v0.52 since v0.51 (6/27/99)\n- Small speed-up of interpreter loop to avoid checking the global variable\n\t\"halt_sim\" after every instruction.\n- Smartport fixes to avoid halts when the SCSI CD player NDA is installed.\n- Fix to autodetect X visual depth (it didn't work at all in v0.51).\n- Fix to HP binary--KEGS v0.51 hit an HP linker bug which caused the\n\texecutable to not run correctly.  (It didn't obey an assembly-\n\tlanguage alignment command correctly).  Re-ordering the object\n\tlist works around the problem.\n\nChanges in KEGS v0.51 since v0.50 (6/1/99)\n- Fixed many bugs that crept into scanline interrupts over the last few months.\n- RAM size is now settable on the commandline: -mem 0x400000 will use\n\ta 4MB expansion RAM card (giving you 4.25MB of memory with ROM 01).\n- VBL time used to be a variable (which was wrong)--it's now always the\n\tsame number of cycles.\n- Typo preventing joystick_driver.c from compiling fixed.\n- Auto senses X visual depth, searching for 8 bit, then 15 bit, then 24,\n\tthen 16 bit visuals.  Can still override this with commandline.\n\nChanges in KEGS v0.50 since v0.49 (5/31/99)\n- Added Linux joystick support with code provided by Jonathan Stark.\n\tActivate with \"-joystick\" command line option.\n- Small improvements in s7 device handling.  If you have no s7 devices or no\n\tbootable devices, KEGS launches Applesoft.\n- Bug fix in scan-line interrupts--they were occurring at the wrong time\n\tpreviously.\n- Rewrote double-hires color routines.  They're still not quite right,\n\tbut it's a lot better than it used to be.\n\nChanges in KEGS v0.49 since v0.48 (5/3/99)\n- Fixed a key-repeat bug in v0.48 caused usually with shift-key sequences.\n- Fixed bug where GNO would not work with ROM 03.  ROM area at $C071-$C07F\n\tis different from ROM 01.\n- Ian Schmidt pointed out a special Ensoniq case where an oscillator in\n\tone-shot mode can cause it's partner to start if it is in swap mode.\n- Integrated in Geoff Weiss's Solaris x86 ports.  I might have broken it\n\tmaking a few last-minute changes...\n\nChanges in KEGS v0.48 since v0.47 (4/13/99)\n- Even better ADB key repeat--key rollover works more like a real Apple //gs.\n- IWM fix: some \"smarport\" modes were being activated sometimes during\n   normal 3.5\" accesses, resulting in some games not loading correctly.\n- Some fixes to serial port emulation to handle programs writing to\n   the serial port in MIDI mode when the chars will not be consumed.\n- Smartport fix to set zero-page locations $42-$47, needed by some poorly-\n   written game loaders\n- The \"oscilloscope\" effect in some sound-demos now shows the sounds\n   being played.\n\nChanges in KEGS v0.47 since v0.46 (4/7/99)\n- ADB fix #1: reading $c010 should give key-down status better\n- ADB fix #2: key repeat was stopping if modifier key pressed\n- ADB fix #3: The game \"Pirates\" was crashing on startup due to a small bug.\n- Bard's Tale 2 was freezing on startup due to a bug in the WAI instruction.\n- Major serial port rewrite.  Diversi-Tune now runs and sound OK (but there\n\tare some small problems) and serial port emulation is better.\n\nChanges in KEGS v0.46 since v0.45 (3/21/99)\n- Fix for undefined var in engine_c.c.  Oops.\n- Fix for old bug in engine_c.c causing KEGS to sometimes misinterpret\n   instructions which cross page boundaries.  Was causing Thexder not\n   to work, at least.\n\nChanges in KEGS v0.45 since v0.44 (3/20/99)\n- Fix for COP instruction in engine_c.c.  Pointed out by Kelvin Sherlock.\n- Major fixes to Ensoniq emulation, SynthLab sounds much better.\n- Fix to iwm.c to deal with corrupt 2IMG archives a little better.\n\nChanges in KEGS v0.44 since v0.43 (2/23/99)\n- -audio 0 option would often cause programs to hang.  Bug was that the\n   audio rate was defaulting to '0' which confused KEGS.\n- Made keycode 0x072 be the XK_Break key for XFree86\n\nChanges in KEGS v0.43 since v0.42 (2/19/99)\n- Support .nib 5.25\" format as read-only\n- Faster 3.5\" nibblization routines (should make startup faster)\n- Fixed a very-old 3.5\" disk writing bug that made bit-copiers not work\n\nChanges in KEGS v0.42 since v0.41 (2/1/99)\n- Include <errno.h> to fix Linux compile problem\n- Fix relative branch timing bug that was making IWM emulation flaky\n   (backward branches should count as 3 cycles if to the same page,\n    and 4 if to a different page in emulation mode.  Bug always counted\n    them as 4)\n- Gave up on fast 5.25\" writes--KEGS always slows to 1MHz for 5.25\"\n   writes since the timing and kludges just got too annoying.\n- add \"-arate 22050\" option to change audio sample rate on the command-line.\n   Slower audio rates can hit more audio bugs (I'm working on them).\n- fixed little-endian bug in smartport.c and partls.c\n- fixed side border redraw bug that would sometimes leave super-hires\n   images on the right-side border.\n\nChanges in KEGS v0.41 since v0.40 (1/19/99)\n- Fixed bug where fill-line mode would not always redraw the screen correctly\n- Changed some // comments to /* */ to help David Wilson's Solaris port\n- Fixed little-endian bugs in smartport.c preventing mounting of\n   parititioned disks.  Fix submitted by Jonathan Stark.\n- Christopher Neufeld noted that fast space/delete option in the control\n   panel caused KEGS to hit breakpoints.  I fixed this and fast arrows and\n   fast mouse options (they are now just ignored).\n- Solaris port by David Wilson now provides a Makefile_solaris\n\nChanges in KEGS v0.40 since v0.39 (10/25/98)\n- 15 and 24 bit depth displays now supported (though somewhat slower than\n\t8 bit displays).  But Super-hires displays now show 256\n\tsimultaneous colors on a 16- or 24-bit X display.\n\tSelect a 15-bit display with the cmd line option \"-15\" and\n\ta 24-bit display with \"-24\".  Otherwise, KEGS defaults to looking\n\tfor an 8-bit display, and fails if it cannot find one.\n- Some border fixes--border colors now update correctly when palette\n\tchanges occur (like via F10).\n- Alias F1 to ESC for OS/2.\n\nChanges in KEGS v0.39 since v0.38 (9/13/98)\n- OS/2 port by Tschopp Gilles\n\t- handle cr&lf better in disk_conf\n\t- Drive letters work and are not confused with partition names, so\n\t\ts7d1 = D:\\images\\cd:1 will open partition 1 correctly.\n\t- KEGS no longer uses system() to do file copies, it does it all\n\t\tusing POSIX calls.\n\t- Unix-specific socket calls moved from scc.c to scc_driver.h\n\t- Default X handler re-installed properly now for better debug\n- Nasty core dump bug found and fixed by Tschopp Gilles in disk switch code\n\nChanges in KEGS v0.38 since v0.37 (7/28/98)\n- IWM bugs:\n\t- fast_disk_emul off under GS/OS caused I/O errors.\n\t  KEGS was always slowing down to 1MHz when 5.25\" drive was on, when\n\t  it should have been obeying the $C036 register.\n\t- bug in IWM on little-endian processors\n- disk ejection should now work, but a beta user claimed some bugs on\n\tx86 Linux.\n- 2IMG support, but only lightly tested.\n- Removed some internal breaks on access to $C0B0 for tool033.\n- Modulae also stumbled into some breakpoints by writing to $C02F,\n\twhich does nothing.\n- Screen refresh simplified (for me) by redrawing the screen while\n\traster is on first scan-line, rather than line 200.\n\tHowever, a side effect is some of the graphics during the XMAS DEMO\n\tlook a bit choppier.\n- More SCC fixes to avoid breakpoints under GNO.\n- Start support for sound under Linux, but it sounds horrible right now.\n\tAny Linux sound gurus want to help out?\n- Fixed possible array-overrun bug in video.c around border effects.\n\tMaybe shared memory works under x86 Linux now?\n- Made changes for OS/2 port to fopen() text files.  From Blue Neon.\n\n\nChanges in KEGS v0.37 since v0.36 (7/13/98)\n- Linux PPC port completed and functional.  KEGS has been tested to\n  run quite well and quite fast on a 240MHz 604e running\n  MkLinux pre-DR3.\n- Change LITTLE_ENDIAN define to KEGS_LITTLE_ENDIAN since Linux\n  always defines LITTLE_ENDIAN as a silly macro.\n- Dumb bug in IWM 3.5\" routines could cause core dumps if disk arm moved\n  from outer track to inner track very quickly.\n- Deleted some breakpoints that some Second Sight searching code would hit.\n- Ignore some SCC reset commands GNO would use that caused KEGS to stop.\n- Handle odd partitions better--some //gs formatted Zips had a blocksize\n  of 0, which defaults to 512 now.\n- Handle some keysyms better to avoid MkLinux bug with keysym 0.\n\nChanges in KEGS v0.36 since v0.35 (5/30/98)\n\n- Linux x86 port completed and functional with help from Karl Pfleger\n- Linux clock fixes--should handle daylight savings better on Linux\n- LITTLE_ENDIAN defines\n- Start making fixes for NeXTStep due to Eric Sunshine\n- Fixed bug in HP asm code with I/O fetches--caused //gs selftests to fail\n  and a bug in scc.c was also causing self-tests to fail.\n\nChanges in KEGS v0.35 since v0.34 (5/17/98)\n\n- engine_c.c fully implemented--KEGS now has a version completely written\n   in C, and now portable to other Unix machines.\n- KEGS got another 5% faster with more tweaks to the asm dispatch loop.\n\nChanges in KEGS v0.34 since v0.33\n\n- KEGS is 10-15% faster due to finally implementing a planned recoding\n   of the dispatch loop.\n\nChanges in KEGS v0.33 since v0.32 (5/7/98)\n\n- Fixed bug in engine_s.s that prevented compiling on pre-10.20 systems.\n- ADB mouse interrupts work now.  Fixed \"bug\" where GSHK would think\n   mouse button was depressed at startup. (GS/OS is looking at mouse\n   button 1 status, which accidentally was reading as down).\n- ADB emulation of read char_sets and read_kbd_layouts now matches a real\n   //gs.\n- optimization to allow dereferencing page_info[] even if BANK_IO is set,\n   to get a small speed improvement in engines_s:dispatch().\n- SCC logs are 'Z' at the disas prompt.\n- Tool decoded is 'T' at the disas prompt.\n- SCC changes to support slot 1 == port 6501 and slot 2 == port 6502,\n   with limited interrupt support.  Most serial tasks won't work still,\n   but some do.  PR#1/2 and IN#1/2 work fine.  getty under GNO doesn't.\n- -audio [0/1] forces audio off/on.  This just stops the sound playing--\n   internally all Ensoniq interrupts/etc are fully emulated.  If display\n   is not using shared memory (i.e., it's remote), audio defaults to off.\n   (but can be forced on with -audio 1).\n- -display {foo} sends X display to {foo}.\n\nChanges in KEGS v0.32 since v0.31 (10/23/97)\n\n- Faster dispatch loop, for a 10-15% overall performance improvement\n- Fixed sound bug where Oversampler would make KEGS halt (Oversampler\n   said turn on 128 oscillators, and KEGS tried to...)\n- Fixed bug where KEGS would not work on 24-bit displays due to a typo.\n- Added frame skipping support (-skip n) and auto frame skipping if you\n   are not using shared memory (like displaying KEGS to a remote machine).\n- Added -noshm support for forcing off shared memory, so you can see how\n   much it helps.\n\nChanges in KEGS v0.31 since v0.30 (9/23/97)\n\n- New mouse handling--Press F8 to hide X windows cursor and constrain\n\tcursor inside window.  Makes using the mouse much easier.\n\tF8 toggles back to normal.\n- Add revision to status area.\n- Remove \"slow memory\" calculation.  KEGS was emulating slowing down to\n\t1MHz to write to slow memory (bank $E0 or $E1).  But true //gs\n\taccelerators have a smarter trick, so I just removed it from\n\tKEGS.  KEGS still slows down for I/O reads and writes.\n\tThis eliminates the confusing 40MHz speed numbers you'd sometimes get.\n\tKEGS can also now run faster when it would have slowed down to\n\t1MHz before.\n- Turn off accurate IWM emulation be default, for much faster emulation.\n\tBit copiers won't work by default now.  Toggle accurate IWM\n\twith F7.  Accurate IWM forces 1MHz speed for 5.25\" and 2.5MHz for\n\t3.5\", but less accurate IWM runs as fast as possible.\n- Add optional size to s7dx entries in disk_conf, to allow using /dev/rfloppy.\n- Allow mounting partitions by number, instead of just by name, since some\n\tMac-formatted Zip disks don't have partition names.\n- Add -ignbadacc to ignore bad memory accesses.\n- Increase MAX_C030_TIMES.  Otherwise, fast workstations could generate too\n\tmany clicks per VBL, causing an assertion to fail.\n- Small speed increase detecting changes in the superhires screen.\n- Alt_L is now Open-Apple, and Alt_R is Closed-Apple.\n- KEGS now uses just one private colormap, so xwd can get screendumps.\n\n\n"
  },
  {
    "path": "upstream/kegs/doc/COPYING.txt",
    "content": "\n                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "upstream/kegs/doc/INTERNALS.iwm.txt",
    "content": "// $Id: INTERNALS.iwm.txt,v 1.3 2021/10/03 19:44:15 kentd Exp $\n\nKEGS's Apple IIgs IWM emulation routines.\n\nThe IWM code does 5.25\" and 3.5\" reads & writes, and updates the Unix disk\nimage on writes.  All 5.25\" and 3.5\" disks are always emulated internally in a\nlow-level format that is compatible with WOZ disk images.  Support for\nWOZ images is very good, but it's not complete yet.\n\nHow the disk emulation works: The routines have a nibblized image of each\ntrack of each drive (two 5.25\" and two 3.5\" drives are supported) in memory.\n\nEach track's data is stored as an array of bytes of the raw disk bits.\nThis is basically the raw data from WOZ images for that track.  A second\narray for the track, of the same length, is allocated to calculate the sync\nbit positions.  For each byte, the sync byte indicates the nearest\nsynchronized MSB from the LSB of the same disk byte.\n\nWith this encoding, if a disk read is done, and then 1000 cycles pass,\nthe code can quickly index to the proper disk byte, and then use the sync\ninformation to return the correct data.  It is just as much work for 4 cycles\nto pass as 5000 cycles.\n\nSeveral levels of emulation accuracy are provided.  By default, KEGS\ndefaults to Fast Disk Emulation, where the following changes are done:\n\n- KEGS does not slow down to 1MHz (or 2.8MHz for 3.5\" accesses) for reads.\n- However, KEGS always slows down to 1MHz for 5.25\" writes.\n- Each read of IWM data from $C0EC will return the next aligned disk nibble.\n- No matter how much time passes, the read position does not move.\n\nWhen accessing a WOZ image, or if Fast Disk Emulation is toggled off\n(using Shift-F6), or if a write is being done, then:\n\n- KEGS slows to 1MHz (2.8MHz for 3.5\" accesses) when the drive motor is on.\n- Exact timing is maintained--a valid disk nibble is held in the read latch\n\tfor 7 clock cycles plus the amount of time for the next sync bit to\n\tshift in, just like the hardware works.\n\nThe arm stepping code is pretty dumb, it does not support 1/4 tracks since\nI have no mechanism to test them.  I'll look for a WOZ image with 1/4 tracks\nand then implement it properly.\n\nSmartport support is sufficient to claim that there are no smartport\ndevices.  This is necessary since the ROM tries to see if there are\nsmartport devices at power-on.\n\nI used Copy II+ to nibble copy some disks, and it worked fine.  Note:\nKEGS does not support writing to \"empty\" WOZ images.  This is on the TO-DO\nlist.\n\nIWM description:\n\nThe IWM in the IIgs supports 3.5\" and 5.25\" disks, as well as Smartport\ndevices (KEGS does not support real Smartport devices, but it does cheat\nand make large images available on slot 7).\n\nThe IWM has several modes:\n\n- Motor off.  Accesses go to internal Mode register and Status register.\n- Motor on ($C0E9 is on; touch $C0E8 to turn motor off).\n\t- $C031 enables 3.5\" if bit 6 is set\n\t\t- The disk phases $C0E0-$C0E7 + $C031 bit 7 provide a 4-bit\n\t\t\tcommand code to the 3.5\" controller, and can be used\n\t\t\tto move the disk arm, eject a disk, etc.\n\t\t\tThe action is triggered by phase 3 going on.\n\t\t\tNote the actual 3.5\" motor is not controlled by $C0E9,\n\t\t\tbut a special command must be given through this\n\t\t\tphase-3-latch mechanism to turn the motor on and off.\n\t\t- Phases 0,1,2 and $C031 bit 7 select a status bit to return\n\t\t\twhen Q7=0,Q6=1 is read (the Status Register).\n\t\t-If phase 0 and 2 are enabled, this resets the IWM.\n\t\t-If phase 1 and 3 are enabled, this enables accessing the\n\t\t\tSmartport Bus.  KEGS supports this enough to indicate\n\t\t\tthat there are no Smartport devices.\n\t\t\tThis is called ENABLE2 mode.\n\t- $C031 disables 3.5\" if bit 6 is clear:\n\t\t- The disk phases $C0E0-$C0E7 move the 5.25\" disk arm.\n\t\t- Reading any even address returns:\n\t\t\tQ7=0,Q6=0: The data register (byte from the drive)\n\t\t\tQ7=0,Q6=1: Status register (write protect)\n\t\t\tQ7=1,Q6=0: Handshake register (not used by software)\n\nThe 3.5\" IWM modes provide for much faster emulation--the controller returns\na status when the head has stepped a track, when the motor is up to speed,\netc., and KEGS returns \"it's ready now\" always to make the accesses faster.\nWith Fast Disk Emulation, an entire 800K disk can be read in under 2 seconds,\nand an entire 140K disk can be read in well under a second.\n\n\nCode description:\n\nMost code is in iwm.c, with some defines in iwm.h.  Each track is stored in\nraw data form--so a 10-bit 0xff, 10-bit 0xff, 0xd5, 0xaa, 0x96 would be\nstored as: 0x3f, 0xcf, 0xfd, 0x5a, 0xa9, 0x6X.  To remember the bit position\non the track with sufficient accuracy, the head position on the track is in\n\"quarter-bits\", or \"qbits\".  There are 32-qbits to an 8-bit byte.  For 5.25\"\ndisks, a qbit is exactly one 1MHz cycle.\n\nIwm state is encoded in the Iwm structure.\n\n\tdrive525[2]: Disk structure for each 5.25\" disk in slot 6\n\tdrive35[2]: Disk structure for each 3.5\" disk in slot 5\n\tsmarport[32]: Disk structure for each \"smartport\" device emulated\n\t\tvia slot 7 (see smartport.c)\n\tmotor_on:  True if IWM motor_on signal (c0e9) is asserted.  Some\n\t\tdrive is on.\n\tmotor_off: True if motor has been turned off in software, but the\n\t\t\t1 second timeout has not expired yet.\n\tmotor_on35: True if 3.5\" motor is on (controlled differently than\n\t\t\t5.25\" c0e9).\n\tmotor_off_vbl_count:  VBL count to turn motor off.\n\thead35, step_direction35: 3.5\" controls.\n\tiwm_phase[4]: Has '1' for each 5.25\" phase that is on.\n\tiwm_mode:\tIWM mode register.\n\tdrive_select: 0 = drive 1, 1 = drive 2.\n\tq6, q7:\tIWM q6, q7 registers.\n\tenable2:\tSmartport /ENABLE2 asserted.\n\treset:\t\tSmartport /RESET asserted.\n\twrite_val: Value written to the disk data latch, will be written\n\t\tafter 8 bit times has passed.\n\tqbit_wr_start: Qbit position when writes started, a large value if\n\t\tnot doing a write currently.\n\tqbit_wr_last: Qbit position where write_val should go.\n\tforced_sync_qbit: Overrides the calculated sync_bits temporarily, and\n\t\tforces this to be the qbit of the next sync byte.\n\nEach disk (3.5\" and 5.25\") is encoded in the Disk struct:\n\tdycs_last_read: Time (in 1MHz cycles) when last access occurred.\n\tfd:\tUnix file descriptor.  If < 0, no disk.\n\traw_data: For an image from a .zip file, this is a pointer to the\n\t\traw disk data.  fd will be 0 when this is valid.\n\tname_ptr: Unix file name for this disk.\n\tpartition_name: .zip file name.\n\traw_dsize: size of raw_data\n\tdimage_start: offset from beginning of file for this partition (0 if\n\t\t\tnot a partition or zip file entry).\n\tdimage_size: size of this partition.\n\tsmartport: 1 if this is a smartport image, 0 if it is 5.25\" or 3.5\"\n\tdisk_525: 1 if this is a 5.25\" image, 0 if it is 3.5\"\n\tdrive: 0 = drive 1, 1 = drive 2.\n\tcur_qtr_track:\tCurrent qtr track.  So track 1 == qtr_track 4.\n\t\tFor 3.5\", there are no half-tracks or quarter tracks, so\n\t\tcur_qtr_track encodes the (track << 1) + side.\n\tvol_num:\tDOS 3.3 volume number to use.  Generally 254.\n\twrite_prot:\tTrue if disk is write protected.\n\twrite_through_to_unix: True if writes should be passed through to\n\t\t\tthe unix image.  If this is false, you can write\n\t\t\tto the image in memory, but it won't get reflected\n\t\t\tinto the Unix file.  \n\tdisk_dirty:\tSome track has dirty data that need to be flushed.\n\tjust_ejected:\tEjection flag.\n\tlast_phase:\tPhase number last accessed.\n\tcur_qbit_pos:\tQbit position for reading.\n\tcur_track_qbits: Number of qbits on this track.\n\tnum_tracks:\tNumber of tracks: 140 for 5.25\" (it counts quarter-\n\t\t\ttracks) and 160 for 3.5\" (80 tracks on 2 sides)\n\t*trks: pointer to array of raw Track data\n\t*cur_trk_ptr:  Pointer to the current track\n\nEach track is represented by the Trk structure:\n\t*raw_bptr:\tPointer to the raw disk data.\n\t*sync_ptr:\tPointer to the sync values for each byte.\n\tdunix_pos:\tOffset into disk image for this track data.\n\tunix_len:\talways 4096 for 5.25\" disks, variable for 3.5\" disks\n\ttrack_qbits:\tLength of raw_bptr in qbits.\n\n\nExternally callable routines:\niwm_init():\tInit various data structures at simulation start.\niwm_reset():\tCalled at Apple IIgs reset time.\niwm_vbl_update():\tCalled every VBL (60 Hz) period.  Used to turn motor\n\t\t\toff, and flush out dirty data.\n\t\t\tg_vbl_count is the count of VBL ticks (so it counts\n\t\t\tat 60 times a second).\niwm_touch_switches(int loc, double dcycs): Handles all accesses to $C0E0-$C0EF,\n\t\tchanging state as needed.\nread_iwm(loc, dcycs):\n\t\tRead from 0xc0e0 + loc.  Loc is between 0x0 and 0xf.\n\t\tDcycs is a double holding the number of Apple IIgs cycles since\n\t\tthe emulator started.  Dcycs always counts at 1.024MHz.  If\n\t\tyou are running at 2.8MHz, it increments by ~0.4 every\n\t\t\"cycle\".  Reading from even addresses with the drive on will\n\t\tread the current disk data if q6=0,q7=0.\nwrite_iwm(int loc, int val, double dcycs):\n\tWrite to 0xc0e0 + loc.  Just like read_iwm, but write \"val\" into\n\tloc.  If q6=1 and q7=1, and the motor is on, writes data to the track.\n\n\nTricky routines:\n\niwm_read_data():\tcalled by read_iwm() if q6,q7 = 0,0.\n\t\tdsk->cur_qbit_pos is the current head position, the code\n\t\treturns the data to be returned at this position.\n\t\t16.0 dcycs need to pass for an 8 bit nibble for 3.5\"\n\t\taccesses, and 32.0 dcycs for an 8 bit nibble for 5.25\".\n\t\tg_fast_disk_emul == 1 says don't mess around with accuracy,\n\t\tand always return the next fully-formed nibble.\n\t\tThere is a lot of complexity in this routine.  For 5.25\",\n\t\tit looks backwards to see where the most recent MSB of a disk\n\t\tbyte would have occurred using sync_info, and then forms:\n\t\t\t- That byte, if that MSB was 8 bits ago, plus 7\n\t\t\t\tcycles, plus any time for 0 sync bits that\n\t\t\t\tmay be after it.\n\t\t\t- The current effective IWM shift register value--if\n\t\t\t\tit is determined we are 4 bits into 0xaa, then\n\t\t\t\tit will return the first 4 bits only as 0x0a.\n\t\tFor 3.5\", it uses iwm_read_data_latch(), which re-uses\n\t\t\tg_iwm.qbit_wr_last to remember if the latch is valid.\n\niwm_write_data():\tcalled by write_iwm() if q6,q7 = 1,1.\n\t\tSimilar to above.  Handles async and sync mode writes.\n\t\tHandles partial writes.\n\t\tRoutine disk_nib_out_raw(dsk, val, bits, qbit_pos, dcycs) does\n\t\tthe actual work of merging the bits into the track image.\n\ndisk_nib_out_raw(): called by iwm_write_data() and iwm_nibblize_track_*().\n\t\tWrites byte into cur_trk_ptr->raw_bptr[].\n\n\nIn iwm_nibblize_track_35(), the comments with hex numbers correspond\nto the ROM 01 addresses which I disassembled to determine the checksum\nalgorithm.  The code is not well written--it's basically hand-translated\n65816 assembly code.  I'll clean it up someday.\n\nTo avoid special casing reading and writing data at the end of the track,\ntrk_ptr->raw_bptr[] has the actual track data start at byte 2, and there\nare at least 2 bytes spare at the end.  The iwm.c code keeps the raw_bptr[0]\nand raw_bptr[1] always equal to the last 16 bits on the track--so that most\ncode can look at raw_bptr[-1] and raw_ptr[-2] to peer backwards to the previous\ntrack data, without worrying about wraparound.\n\n"
  },
  {
    "path": "upstream/kegs/doc/INTERNALS.overview.txt",
    "content": "\nKEGS Internals\n--------------\n\nThe INTERNALS* files describe the internal structure of KEGS and how\nit works.  It is meant to be useful to those who would attempt to\nport KEGS to non-Unix platforms, or just want to know how KEGS works.\n\nDocumentation files:\n--------------------\n\nINTERNALS.overview:  Provides overview of KEGS's file structure\n\n\nKEGS SOURCE FILES:\n-----------------\n\nadb.c:\t\tADB emulation routines.  This includes keyboard, mouse, and\n\t\t  joystick.\nadb.h:\t\tDefines for ADB routines.\nclock.c:\tClock, BRAM, and some timing routines.\ncompile_time.c: Trick file to put the time you compiled in g_compile_time[].\ndefc.h:\t\tGlobal defines included by all .c files.  Useful trick\n\t\t  to avoid complex multi-level include problems.\ndefcomm.h:\tGlobal defines included by all files (must be assembly and\n\t\t  C safe, such as #defines).\ndefs_instr.h:\tDefinitions for various addressing modes\n\t\t  and for some repeated instructions (like LDA, STA, etc).\ndebugger.c:\tDisassembler and debugger interface.  The debugger interface\n\t\t  is similar to the Apple // monitor, but has many\n\t\t  more commands.\ndisas.h:\tTables for disassembling 65816 instructions.  Not very\n\t\t  efficient, but it works.\nengine_c.c:\t65816 emulation and helper routines\nengine.h:\tActual 65816 inner loop, so it can be instantiated with and\n\t\t  without logging, and 8-bit and 16-bit accumulator.\ninstable.h:\tInstruction table. \niwm.c:\t\tIWM routines for 5.25\" and 3.5\" disks.  See INTERNALS.iwm\niwm.h:\t\tIWM defines\nmoremem.c:\tAwful name--this file contains the page table change\n\t\t  routines (fixup_*) and io_read() and io_write() to\n\t\t  emulate all $C000 I/O accesses.\nop_routs.h:\tMore macros for 65816 emulation.\nprotos.h:\tPrototypes for all C functions.  Auto-generated through\n\t\t  the \"cproto\" program (not included).\nprotos_xdriver.h: Prototypes for functions in xdriver.c.\nscc.c:\t\tSerial chip emulation.\nscc_driver.h:\tUnix-specific socket routines, stubbed out if you're not\n\t\t  on Linux or HP-UX.\nscc.h:\t\tDefines for scc.c.\nsim65816.c:\tmain() is here along with many other housekeeping\n\t\t  functions, such as events, and interrupts.\n\t\t  The main loop of KEGS is run_16ms().\nsize_c.h:\tUsed to get the size of instructions\nsmartport.c:\tSmartport emulation, emulates s7dx devices.\nsound.c:\tSound emulation.  Builds sound samples, but does not\n\t\t  send sound to output device.\nsound.h:\tHeader file for sound.c.\nsound_driver.c:\tSound driver routines.  Takes samples generated by sound.c\n\t\t  and sends them to the correct output device.  Supports\n\t\t  /dev/audio, /dev/dsp, Linux PulseAudio, and Mac audio.\nvideo.c:\tDisplay routines.  Builds 32-bit video buffers in a\n\t\t  device independent way.  Functions here know nothing\n\t\t  about X windows or Macs.\nxdriver.c:\tX windows driver routines.  Takes buffers from video.c\n\t\t  and sends them on to X windows.  Get keypresses from\n\t\t  X and sends them to adb.c.\nmockingboard.c:\tMockingboard A emulation (two 6522, two AY-8913 sound chips).\nunshk.c:\tCan open disk images in .sdk files\nundeflate.c:\tZip routines, for selecting images insize .zip files, or\n\t\t  gzip compressed files.\nwoz.c:\t\tSupport for .woz images, Version 1 or Version 2.\n\nPorting Advice:\n--------------\n\nTo a non-unix platform, disabling scc emulation would be a good start.\nJust make sure you get the dummy stub routines in scc_driver.h.\n\nIf you don't have gettimeofday(), clock.c will need to modified with\nwhatever timer facilities are available.  A high-resolution clock works\nbest, but even a low-resolution clock will work.\n\nModify sound_driver.c to get it to compile, if necessary.  Always run with\n\"-audio 0\" to turn audio off.  No routines in sound_driver.c will get\ncalled if you run with -audio 0.\n\nReplace xdriver.c with new routines for whatever platform you are porting to.\nSee INTERNALS.xdriver for more information.\n\nUseful IIgs information:\n-----------------------\n\nThe Apple manuals (Hardware reference, Firmware Reference) are the most\nuseful.  But they leave a lot out, or have innaccuracies.\n\nSome details on IIgs fast/slow mode and refresh information:\nhttps://www.kansasfest.org/wp-content/uploads/2011-krue-fpi.pdf\n\n"
  },
  {
    "path": "upstream/kegs/doc/INTERNALS.video.txt",
    "content": "\nThe video.c code in KEGS is responsible for handling the Apple IIgs video\nmodes.\n\nA detailed description of how Apple II video works is in Understanding\nthe Apple //e by Jim Sather.  This is a carefully researched book (I'm in awe\nof the amount of time that went into writing it) with a great deal of\ndetails in chapters 3, 5, and 8 on how Apple II video works.\n\nKEGS video overview:\n-------------------\n\nKEGS' emulates one screen draw period per call to run_16ms().  The screen\nshown by a monitor starts at the top left of the screen, and is the top border\nlines.  Then the Apple II graphics modes are shown as lines 0 through 199\n(or 191 for non-Super-Hires modes), with a left border and right border,\nthen the bottom border lines are shown.\n\n                      Top Border Lines....\n                      Top Border Lines....\n        |-----------------------------------------------| <- Time 0\nLine0   |                                               |\nLine1   |                                               |\n...\nLine198 |                                               |\nLine199 |                                               |\n        |-----------------------------------------------|\n                      Bottom Border Lines...\n                      Bottom Border Lines...\n\nA screen draw period is exactly 17030 1MHz cycles, which is 262 lines of\n65 cycles each.  KEGS calls the start of its screen Time 0, and it is\njust at what would be the start of the right border for Line -1 (so just\nbefore Line 0, the first Apple II video mode line).  KEGS time 0 corresponds\nto when the $C019 VBL signal transitions from 1 to 0 (this sense is inverted\nwhen KEGS is in //e mode).  Time 0 corresponds to $C02E=0x80 (vertical\nline count / 2, encoded) and $C02F=00 (horizontal count, encoded).\n$C02E counts from 0x80 until 0xe0 (lines 0 to 192) when $C019 VBL will be\nset again.  $C02E counts through $FF, then wraps to $7D, then counts through\n$7F.  $C02F's has a high order bit which is the low bit of the line counter,\nso if $C02E=0x90 and $C02F=0x80, then the current line is $21.  The\nhorizontal counter in the low 7 bits of $C02F counts as follows: $00,\n$40-$7f, and then repeating.  The rightmost pixel of each Apple II video\nmode is fetched from memory when $C02f bits 6:0 = $7f.  The rightmost border\npixel is drawn whtn $C02F bits 6:0 = $00.  The leftmost pixel of each\nApple II video mode is fetched from memory with $C02F bits 6:0 = $58.  So\nthe 40 cycles drawing data are when $C02F = $58 through $7f = 40 values.\n\nKEGS currently draws each line of the screen at the moment when $C02F=$58.\nKEGS uses the memory in banks $E0 (and $E1) for the line being drawn,\nbut does track the mode changes to handle changes within a line.  Toggling\nthe mode ($C050-$C057, for example) in the middle of a line will be drawn\nroughly correctly (it's complex when changing from 80-column to hires,\nfor example, one cycle of data may not be shown like the real hardware).\n\n"
  },
  {
    "path": "upstream/kegs/doc/INTERNALS.xdriver.txt",
    "content": "\nxdriver.c contains the routines for interfacing to X windows.  The rest\nof KEGS interacts with X windows only through xdriver.c routines.\n\nExternally called routines are:\nshow_xcolor_array():\tDebug routine, it does not need to do anything.\ndev_video_init():\tCalled at startup, it should open up the\n\t\t\t window and do other initialization.\nupdate_physical_colormap(): Updates the X windows palette with the colors\n\t\t\tfrom xcolor_a2vid_array[], which is maintained by\n\t\t\tother xdriver routines.\nupdate_status_line():\tCall to update the internal array of chars\n\t\t\t  representing the status lines at the bottom of\n\t\t\t  the window.  Does not draw the chars to the screen.\nxdriver_end():\t\tShutdown routine\ncheck_input_events():\tCalled up to 60 times a second (see video_update() in\n\t\t\t video.c) to handle any X window events and get\n\t\t\t keypresses.\n\t\t\t On a mouse press, call update_mouse() with the\n\t\t\t new x, y coordinates, and the status of the mouse\n\t\t\t button.\n\t\t\t If g_warp_pointer is set, constrain mouse within\n\t\t\t the window.\n\t\t\t On keypress, calls handle_keysym().\n\thandle_keysym(): Takes X keysym, and converts to the appropriate\n\t\t\ta2code using the a2_key_to_xsym[] lookup table.\n\t\t\tThe a2codes are the Apple // ADB keycodes.\n\t\t\tSpecial work is done to handle shift and control\n\t\t\tproperly since Apple only has one keycode for both\n\t\t\tshift and control keys.  Then call\n\t\t\tadb_physical_key_update() with the a2 keycode and\n\t\t\tis_up = 1 if keyup, 0 = if key down.\n\t\t\tIn addition, this routine handles all the Function\n\t\t\tkeys doing special actions, which should be easy to\n\t\t\tport.\nx_refresh_ximage():\tRedraws the window using the a2_line_* arrays.\n\t\t\tDescribed in more detail below.\nupdate_color_array():\tInterface to the color map.  Sets color[col_num]\n\t\t\tof the internal colormap array to a2_color.\n\t\t\ta2_color is the 12 bit apple color of the form:\n\t\t\t(red << 8) + (green << 4) + (blue).\n\t\t\tThere are 16 palettes of 16 colors each, managed as\n\t\t\tone 256-color colormap.  See discussion of\n\t\t\tg_a2vid_palette below.\nx_auto_repeat_on():\tThe X routines turn off key repeat when the cursor\n\t\t\tenters the graphics window automatically, and turn\n\t\t\tit back on when the cursor leaves.  But if the\n\t\t\tdebugger gets control due to a breakpoint, keyrepeat\n\t\t\twould be left off.  So the debugger calls this\n\t\t\troutine to make sure key repeat is back on.\nredraw_status_lines():\tDraw the status lines from the g_status_buf[][] array\n\t\t\tto the graphics window.\n\nExternally referenced data:\n\ng_use_shmem:\t\tSet by main() to enable/disable MIT-SHM for X.\n\t\t\tAlso used by sound routines to auto-turn-off sound\n\t\t\tif not using MIT-SHM.\n\nBytes representing screen data:\nbyte *data_text[2]:\tArray of bytes for the lores and text pages 1 and 2.\n\t\t\tJust 400*640 bytes.\nbyte *data_hires[2]:\tArray of bytes for the hires pages 1 and 2.\nbyte *data_superhires:\tArray of bytes for superhires screen.\nbyte *data_border_sides: Array of bytes representing the border sides.\n\t\t\tBasically just A2_WINDOW_HEIGHT*EFF_BORDER_WIDTH bytes.\nbyte *data_border_special: Top and bottom border bytes.\n\t\t\t(X_A2_WINDOW_HEIGHT - A2_WINDOW_HEIGHT + 2*8) *\n\t\t\t\t(X_A2_WINDOW_WIDTH) bytes.\n\nHandles used for X windows drawing:\nXImage *ximage_hires[2]: Opaque handle to XImage object for hires page 1 and\n\t\t\t page 2.\nXImage *ximage_text[2]:\tText pages 1 and 2.\nXImage *ximage_superhires: Superhires graphics XImage\nXImage *ximage_border_special: Top and bottom border XImage.\nXImage *ximage_border_sides: Left and right sides (only one copy, it is\n\t\t\tdrawn at two different locations to be both sides).\n\nBasic operation of xdriver:\n--------------------------\n\nX windows can push arrays of bytes to the screen through structures\ncalled XImages.  An XImage is a structure describing an offscreen bitmap.\nFor efficiency of page flipping, KEGS maintains separate bitmaps for the\ntwo lores/text screens, the two hires screens, and the superhires screen.\nIt also maintains bitmaps for the border.  For MIT-SHM to work, X\nrequires a unique XImage for each bitmap, and the bitmap must be allocated\nwithin xdriver.c since it must be obtained through an shmat() call.\nThe X code also has non-MIT-SHM code which allocates the data_* buffers\njust through malloc().\n\nAll bitmaps are 8-bits of Pseudo-color.  The color arrays are managed\nthrough the update_color_array() and update_physical_colormap() routines.\nKEGS manages all 256 colors in the colormap as 16 palettes of 16 colors.\nOne of the palettes is reserved for the 16 lores colors, and is\nindicated by the variable g_a2vid_palette.  It defaults to 0xe.\nUpdate_color_array() is called to update superhires colormap entries.\nUpdate_color_array must not update colors corresponding to g_a2vid_palette.\nUpdate_physical_colormap() pushes the color array managed by\nupdate_color_array() to the screen, but first forces the lores colors into\nthe g_a2vid_palette palette.  g_installed_full_superhires_colormap is\nalways false in KEGS for now.  video.c calls update_color_array and changes\ng_a2vid_palette.  No xdriver routines gets notified when g_a2vid_palette\nchanges, so update_physical_colormap must handle the case where\ng_a2vid_palette might have changed since it was last called.\n\nx_redraw_ximage():\nRoutines in video.c are free to draw into the corresponding data_*\narrays to change any byte at any time.  video.c manages remembering\nwhich lines need to be redrawn and which parts of the screen are in\nwhich video mode via the a2_line_* arrays.\n\nKEGS divides the video screen up into 25 groups, corresponding to each\ntext line.  Each of these groups consists of 16 sublines.  25*8 = 400 lines.\n(video.c has already doubled the vertical resolution in all video modes).\nKEGS can allow any group to be from any of the five screens it manages:\nThe two text/lores pages, the two hires pages, and the superhires screen.\nFor each group, KEGS keeps track of what part of it needs to be redrawn.\ng_a2_screen_buffer_changed has a bit set for each group which has changed\nsince the last call to x_redraw_ximage().  The rightmost bit (bit 0)\ncorresponds to group 0.  If g_a2_screen_buffer_changed == 0, no groups\nneed to be redrawn.  x_redraw_ximage clears out g_a2_screen_buffer_changed\nafter drawing the screen.\n\nFor each group, a2_line_left_edge[] and a2_line_right_edge give the pixel\noffsets of what should be redrawn.  a2_line_xim[] gives the ximage handle\nof what needs to be redrawn.  KEGS always redraws 8 verticals of a group.\ng_full_refresh_needed also has one bit set in it for each group, which\nindicates overriding the a2_line_*_edge functions and redraw from 0 to\n640 pixels of each group that needs to be redrawn.  x_redraw_ximage()\ninterprets this information now using a simple algorithm: Skip over\ngroups which have not changed (using g_a2_screen_buffer_changed).\nSave the ximage of this group, the left pixel and the right pixel.\nContinue with the next group if it has changed.  Widen the pixel region\nand keep sucking up new groups to the same ximage.  At group 25, or\nwhen the ximage changes, call x_refresh_lines to redraw this large\nrectangle from this ximage.  x_refresh_lines() knows the ximage\ncorresponding to the border for the last group has to be handled\nspecially since the border group is not 640*400 pixels like the others.\n\nOther porting info:\na2_key_to_xsym[][3] contains the mapping function from X keysyms to\na2 keycodes.  The first element is the a2 keycode, the second element\nis the unshifted X keysym, and the third element is the shifted keysym.\nA port must make the conversion to a2 keycodes, and provide up and\ndown events.\n\n"
  },
  {
    "path": "upstream/kegs/doc/README.ROM.files.txt",
    "content": "\nPlace a ROM, ROM.01 or ROM.03 file in your home directory, or in the\ndirectory you run KEGS from, or in the directory you placed a config.kegs\nfile.  You may manually select the ROM to use while running KEGS by\npressing F4, and then selecting \"ROM File Selection\", then \"ROM File\", and\nthen using the file selector to locate the ROM file.\n\nKEGS supports Apple //e ROMs as well.  The ROM needs to be 32KB, with the\nactual 16KB of useful ROM in the last 16KB, and requires a Disk II PROM\nat offset $600 in the file (this just seems to be the format other emulators\nuse, and I'm just following it).\n\nThere are several places that have Apple IIgs ROMs.  Most work, here's a\nlist of ROM files which will work with KEGS:\n\nKEGS accepts the following ROM file names (if you have a file with this name\nat the right location, it will just work):\n\nROM, ROM.01, ROM.03, APPLE2GS.ROM, APPLE2GS.ROM2, xgs.rom, XGS.ROM,\nRom03gd, 342-0077-b.\n\nrom01.zip contains APPLE2GS.ROM.  This file is a good ROM01\n\ta rom with this name and use it.\nrom03.zip contains APPLE2GS.ROM2.  This file is a good ROM03\nxgs.rom is a good ROM01.\niigs_rom01.zip contains APPLE2GS.ROM.  This file is a good ROM01.\niigs_rom03.zip contains APPLE2GS.ROM2.  This file is a good ROM03.\ngsrom01.zip contains xgs.rom.  This file is a good ROM01.\ngsrom03.zip contains Rom03gd.  This file is a good ROM03.\nappleiigs_rom01.zip contains XGS.ROM.  This file is a good ROM01.\napple2_roms.zip contains xgs.rom.  This file is a good ROM01.\n\tIt also contains gsrom03.zip which contains Rom03gd which is a\n\tgood ROM03.\n\tIt also contains gsrom01.zip which contains xgs.rom again, which is\n\ta good ROM01.\n\n\nFiles that won't work:\nAPPLE Computer and Peripheral Card Roms Collection.zip: Don't use, the\n\tfile \"Apple IIgs ROM1 - 342-0077-B -  27C1001.bin\" is not in the\n\tformat KEGS expects.\n\nMAME ROM files can often be found with the search\n\"site:archive.org mame-merged\", with the GS ROM in apple2gs.zip.\nThe file 342-0077-b is a ROM.01 file and KEGS will just use it (if you place\nit in the proper location).  The 341-0728 and 341-0748 files can be a\ngood ROM03 file, but it's in 2 files (KEGS expects ROMs in a single file),\nand the second file is in a weird order (MAME expects contributors to\nremove ROMs from systems, use a ROM reader to dump out the raw contents,\nand then it uses that, and apparently this puts the data in a weird order\nfor ROM.03 images).  You can convert these to files to the ROM.03 KEGS\nexpects as follows on Mac or Linux:\n\ndd if=341-0748 of=tmp1 bs=64k skip=1\ndd if=341-0748 of=tmp2 bs=64k count=1\ncat 341-0728 tmp1 tmp2 > ROM.03\n\nKEGS does not support ROM0 ROMs.\n\n--------------------------------------------------------------------------\n\nHow to capture the ROM file from an Apple IIgs\n----------------------------------------------\n\nEnter this BASIC program after booting PRODOS 8, and have available at least\n128KB (or 256KB for ROM.03) on the disk:\n\n10 D$ = CHR$ (4)\n20  GOSUB 200:P = 0\n30  FOR I = 254 TO 255\n40  POKE 778,I\n50  FOR J = 0 TO 192 STEP 64\n60  POKE 777,J\n70  CALL 768\n80  PRINT D$;\"BSAVE ROM,A$2000,L$4000,B\";P\n90  PRINT P:P = P + 16384\n100  NEXT : NEXT\n150  END\n200  FOR I = 0 TO 20\n210  READ D: POKE 768 + I,D\n220  NEXT : RETURN\n230  DATA 24,251,194,48,162,254,63,191\n240  DATA 0,0,254,157,0,32,202,202\n250  DATA 16,245,56,251,96\n\nRUN\n\nWhen typing it in, the spaces are all optional you don't need to type them.\n\nThis program captures the 128KB of a ROM.01 into the file \"ROM\".\nTo capture the 256KB of a ROM.03, change line 30 to:\n\n30  FOR I = 252 TO 255\n\n----------------------------------------------------------------------------\n\n"
  },
  {
    "path": "upstream/kegs/doc/README.a2.compatibility.txt",
    "content": "# $Id: README.a2.compatibility.txt,v 1.6 2021/06/26 16:54:23 kentd Exp $\n\nBard's Tale II GS: Problem: Doesn't recognize any save disk as a ProDOS disk.\n\tIt's detecting a \"ProDOS\" disk by checking for a string on block\n\t0 at offset 0x15e.  GSOS on system 6 has moved the string to 0x162,\n\tso disks inited under GSOS will be detected as \"Not a PRODOS disk\".\n\tJust make a copy of the Bard's Tale disk image to another file and\n\tthen mount that new image and remove all the files using the Finder.\n\tThen rename the volume and you have a working save disk.\n\nBeyond Castle Wolfenstein:  Make sure your disk is writeable (not compressed!)\n\nBreakout: Has trouble loading from the cracked copy.\n\tFrom the BASIC prompt, do: \"CALL -151\" then \"C083 N C083\" then\n\t\"BLOAD INTBASIC\" then run breakout.  Then it runs fine.\n\nBurgertime:  This is a bad crack.  Loader starts the game by writing\n\tthe game entry point into $0036/$0037, and then executing a BRK\n\tinstruction.  The BRK handler on an old Apple II will try to write\n\tout a message by calling through $0036/$0037, and this will start\n\tthe game.  But on a IIgs, the ROM sets the $0036/$0037 vectors\n\tback to the default, and so we crash into the monitor instead.\n\tHere's a memory fix and a disk-image fix: From the crack screen,\n\tpress F7 to open the KEGS debugger.\n\tIn the KEGS debugger window enter: \"1d0a:ea 6c 36 0\".  Then just\n\tcontinue the game from the crack screen by pressing a key.  You can\n\tmake this fix to the disk image using a sector editor and change\n\tTrack $1E sector $09 offset $0A from \"60 78 A9 03\" to \"EA 6C 36 00\"\n\tand write it back.\n\nCaverns of Callisto: Requires old C600 ROM in slot 6 (Slot 6==Your Card).\n\nChampionship Loderunner: Requires disk to be writeable or else it starts\n\tthe level and then jumps immediately back to the title page.\n\nCopy II+ 8.1: When doing a bit-copy, if the pattern at $5c13-$5c1b is detected\n\t($d5,$aa,$96,xx,xx,xx,xx,$aa,$aa, which is DOS 3.3 sector $00) at the\n\tstart of the track, then it writes $5c21-$5c24 out just before it:\n\t$d5,$ab,$cd,$df when doing bit copies.  This may be either a serial\n\tnumber code, or just a way to detect bit-copied disks.  5.25\" bit\n\tcopies always seem to write 9-bit sync bytes, which is wasteful.\n\nDrol: Needs slot 6 set to \"Your Card\" from the IIgs control panel\n\t(Ctrl-Cmd-ESC, then choose \"Slots\").\n\tI found Drol hard, so here are some cheats.  First, the standard cheat\n\tfor Drol is to have infinite lives, this cheat is to edit the disk\n\timage:\n\tTrack $0B, Sector $0A, byte $22 to EA EA\n\tTrack $11, Sector $0A, byte $10 to EA EA\n\tTrack $17, Sector $09, byte $B2 to EA EA\n\tI didn't create those cheats, I got it from textfiles.com.\n\tMy cheats are for the monsters to never kill you--just run right\n\tthrough them.\n\tWhile playing Drol, press F7 to open the KEGS debugger, and then in\n\tthe debugger window type:\n\t0/f28:18 18\t\t\t# Monsters' missiles won't kill you\n\t0/e05:90 0c\t\t\t# Monsters won't kill you\n\tOther things, like the bird, axes, swords still kill you.\n\tTo easily solve the third screen, move your man to the far right\n\tside on the top level, so that you are directly above the woman\n\ton the bottom row.  Fly into the air \"A\" and then get to the KEGS\n\tdebugger, and type:\n\t0/c:4\n\tand then continue playing by pressing \"Z\" and you will go all\n\tthe way down and land on the woman and end the level.  It's\n\tuseful to have made the two above patches so that touching monsters\n\twon't kill you.\n\tTwo more patches that only apply to level 3, and so most be made\n\tin memory each time you enter level 3:\n\t6cf3:18 18 18\t\t\t# Axes won't kill you\n\t6f70:38 38\t\t\t# Swords/arrows won't kill you\n\tThe bird and the guy you can't kill will still kill you.  These\n\tcheats were enough to make the game easily playable.\n\tIn the game, your death is indicated by setting location $001E to\n\t$FF.  Setting breakpoints there can let you find other cheats.\n\nFlobynoid: Must disable Fast Disk Emul (hit F7 to toggle it off) since\n\tgame's loader relies on the sector order on the disk (reads 8\n\tsectors from the start without checking headers, assumes every other\n\tphysical sector is skipped due to decode delays).\n\nJeopardy: Disk must be writeable or else it displays \"DISK ERROR\" and\n\tthen crashes into the monitor.\n\nmb-audit v0.7: You must run KEGS at 2.8MHz, but in the IIgs control panel\n\t(Ctrl-Cmd-ESC) set the speed to slow.  Ensure slot 4 is \"Your Card\".\n\nMicrobe: Crashes upon booting.\n\tCode at $599E tries to pull a return address off of a location\n\tbeneath the stack pointer and jump to it, but it doesn't add 1\n\tcorrectly so it jumps to $5917 when it meant to jump to $5918.\n\tOn a IIgs, this causes a BRK to be executed and the game to crash.\n\tThis can be patched in memory in two places:\n\t\t0/599e:ba ca 9a 7d 00 01 48 98 7d 01 01 9d 01 01 60\n\t\t0/6f1d:ba ca 9a 7d 00 01 48 98 7d 01 01 9d 01 01 60\n\tThe original byte sequence for both locations is:\n\t\t00/599e: ba             TSX\n\t\t00/599f: 7d ff 00       ADC     $00ff,X\n\t\t00/59a2: 85 94          STA     $94\n\t\t00/59a4: 98             TYA\n\t\t00/59a5: 7d 00 01       ADC     $0100,X\n\t\t00/59a8: 85 95          STA     $95\n\t\t00/59aa: 6c 94 00       JMP     ($0094)\n\tYou can also patch the code onto the disk image.  I found\n\tthe $599E version on Track $05, Sector $06, Byte $9E.\n\tI found the $6F1D version on the image at Track $0C, Sector $00,\n\tat byte $1D.\n\nMoon Patrol: Crashes into the monitor after completing checkpoint E.\n\tTo fix the Moon Patrol/Dung beetles version, from within KEGS:\n\t\tBLOAD MOON PATROL\n\t\tCALL -151\n\t\t1E15:EA\n\t\t919G\n\tand it will work fine.\n\tIf you have the booting version that just has Moon Patrol on it,\n\tthen from any point after the crack screen is shown, open the\n\tKEGS debugger with F7 and in that window enter:\n\t\t0/1e15:ea\n\tand then it will play fine.\n\tThe bug is that the code executes an instruction with opcode $02,\n\twhich is a NOP on 6502, but is a COP instruction to 65816.  The\n\tCOP instruction acts like BRK and will crash.  The patch just\n\tmakes it a real NOP.\n\nPlanetfall.woz: Do not start it by booting DOS/ProDOS and then doing PR#6.\n\tIt doesn't reset the output vector value at $36/$37, so as soon as\n\tit tries to do any output, it reboots!  Oops!  You can boot it from\n\tProDOS/DOS 3.3 by:  call -151  then  c600g\n\nRobotron 2084:\nRobot Battle:\n\tThese cracks use a \"Fastloader\" which is buggy.\n\tIt tries to JMP $F3D4 and expects to hit an RTS soon.\n\tBut on a IIgs it will access some illegal memory causing a code\n\tyellow.  You can just ignore the code yellow.\n\nUltima IV: Play fine, but Mockingboard won't work.  There is apparently\n\ta patch in U4MBonGSv22.SHK, but I haven't tried it.\n\nUltima V:  Plays fine, but doesn't work with the Mockingboard enabled.\n\tThis is an Ultima V bug: it's running code with ALTZP=1 which\n\ttakes an interrupt.  it expects to run on the ALTZP stack.  But on\n\ta IIgs, the ROM will clear ALTZP and restores a safe stack in\n\tmain memory, which Ultima V is not expecting.  So it will hang/crash.\n\tThere is apparently a patch in U5MBonGSv11.SHK which can fix this,\n\tbut I haven't tried it.\n\n"
  },
  {
    "path": "upstream/kegs/doc/README.compile.txt",
    "content": "# $Id: README.compile.txt,v 1.26 2023/06/14 02:56:10 kentd Exp $\n\nGeneral build instructions:\n--------------------------\n\nKEGS supports Mac OS X, Linux, and Win64 compiles.\n\nFor Mac and Linux, the build is done from the command line using a \"make\"\nutility.  There's a default Makefile, which should work for nearly any\nenvironment.  The Makefile includes a file called \"vars\" which defines the\nplatform-dependent variables.  You need to make vars point to the appropriate\nfile for your machine.\n\nFor Windows, see below on using Visual Studio Community Edition.\n\nA KEGSMAC.app pre-built application for Mac OS 10.13 (High Sierra) is\nprovided.\n\nA xkegs Linux app pre-built for 64-bit Redhat RHEL 7.2 is also provided.\nI'm hoping it works on other Linuxes.\n\nMac OS X build instructions (the default):\n------------------------------------------\n\nKEGS is easy to compile, but you must install XCode first.  Go to the Apple\nApp store, select Xcode, and install it.  You then must execute the Xcode.app\nand agree to terms.  It will want to download additional components, you\nneed those, too.\n\nApple only makes the latest XCode available from the App Store, so if you're\non Mojave, for example, you can no longer download that XCode.  You need to\ncreate a free developer account, and then go to\ndeveloper.apple.com/download/more/ and download the right version.  For\nMojave (10.14), you want XCode 11.3.1.  The latest list of XCode versions for\neach Mac OS version is at en.wikipedia.org/wiki/XCode\n\nThen, cd to the src directory of the KEGS release and type \"make -j 20\".\nKEGS requires perl to be in your path, which is /usr/bin/ on the MAC\n(which should be in your $PATH).\n\nEven after installing XCode, the \"make\" may pop up a dialog saying something\nlike \"The make command requires the command line developer tools.  Would\nyou like to install the tools now?\".  Click \"Install\".\n\nAfter the \"make\" has finished, it will create the application KEGSMAC.\n\nTo run, see README.mac.\n\nLinux build instructions:\n------------------------\n\nUse the vars_x86linux file with:\n\nrm vars; ln -s vars_x86linux vars\nmake -j 20\n\nThe resulting executable is called \"xkegs\".\n\nThe build scripts assume perl is in your path. If it is somewhere else,\nyou need to edit the \"PERL = perl\" line in the Makefile to point\nto the correct place.\n\nKEGS by default requires Pulse Audio to compile.  To compile to use /dev/dsp\n(not really supported by any current Linux as far as I know), edit\nvars to remove pulseaudio_driver.o, the -lpulse of EXTRA_LIBS, and\nremove -DPULSE_AUDIO from CCOPTS.\n\nYou may not have the required include files on your system to compile BURST.\nOn RHEL, use \"yum provides {type path to file you got an error on}\".\nFor example, pulseaudio requires pulse/pulseadio.h which is indicated by the\nerror message \"pulse/pulseaudio.h: No such file\".  All include files are\nunder /usr/include, so do:\n\nyum provides /usr/include/pulse/pulseadio.h\n\nto see that you need something like pulseaudio-libs-devel-10.0-5.el7.x86_64,\nand then do:\n\nsu\nyum install pulseaudio-libs-devel-10.0-5.el7.x86_64\n\nOn my machine, I also had to install the 32-bit libs and headers:\nyum install pulseaudio-libs-devel-10.0-5.el7.i686\n\nAnd I got an error for:\n\"xdriver.c: fatal error: X11/Xlib.h: No such file or directory\".\n\nSo:\n\nyum provides /usr/include/X11/Xlib.h\n\ngives libX11-devel-1.6.7-3.el7_9.x86_64, so:\n\nyum install libX11-devel-1.6.7-3.el7_9.x86_64\n\nAnd /usr/include/X11/extensions/XShm.h is needed, so:\n\nyum install libXext-devel-1.3.3-3.el7.x86_64\n\nThen, /usr/lib64/libpulse.so wasn't found, so:\n\nyum install pulseaudio-libs-devel-10.0-6.el7_9.x86_64\n\n\nMac X11 build instructions:\n----------------------------\n\nUse the vars_mac_x vars file by:\n\nrm vars; ln -s vars_mac_x vars\nmake -j 20\n\nThe build assumes you've installed XQuartz and run it to initialize itself.\n\nThe executable is called xkegs.  KEGS generally does the fastest display\nupdates in X11 mode--but requires Xshm.  Mac misconfigures the shared memory\nlimits to be too small to be used as Xshm memory (4MB limit, we need 40MB for\na retina display), so do:\n\nsudo sysctl  kern.sysv.shmmax=67108864          # One segment max: 64MB\nsudo sysctl  kern.sysv.shmall=131072            # in 4KB pages Total mem: 512MB\n\n\nWindows WIN64 build instructions (Windows 10 and later)\n-------------------------------------------------------\n\nKEGS is easy to compile, but you must install Visual Studio (Community Edition)\nfirst, which is free.  https://visualstudio.microsoft.com/vs/community/\nI'm using the 2022 version.  The executable in the KEGS release was built\non Windows 10 for 64-bit Windows.\n\nOpen a PowerShell (or any terminal window) and go to where you unpacked\nthe KEGS source:\n\ncd src\nstart kegswin.vcxproj\n\nYou may also be able to just double-click the kegswin.vcproj file.\n\nPress 'F7' to generate an executable in x64\\Release\\kegswin.exe.\n\n\nOther platform \"C\" build instructions:\n-------------------------------------\n\nIf you are porting to an X-windows and Unix-based machine, it should be\neasy.  Start with vars_x86linux.  Remove things causing problems, add things\nas needed.  Good luck!\n\n"
  },
  {
    "path": "upstream/kegs/doc/README.dynapro.txt",
    "content": "\nDYNAPRO \n-------\n\nDynapro is a KEGS feature where an emulated disk (140KB 5.25\" in slot 6, 800KB\n3.5\" in slot 5, or any size up to 32MB in slot 7) volume is mapped to a host\n(Mac, Linux) directory (and subdirectories).  This allows easy access to move\nfiles from emulation--just look at the files from outside KEGS on your host\ncomputer, they are always up to date.  It also provide a simple way to move\nfiles into emulation: update the file under the Dynapro mount point, and then\nremount the volume to ensure KEGS/ProDOS can see the changes.\n\nHow to enable\n-------------\n\nCreate a folder on your Mac/Linux system.  You can place files in it, or leave\nit empty.  From within KEGS, press F4, select Disk Configuration, and then use\narrows to move to the slot and drive you want to mount that folder as.\n\nYou have two choices: A slightly longer process where you can set the DynaPro\nimage size (if you want less than 32MB for a slot 7 image), or a shorter\nprocess that always mounts the largest size for that slot.\n\n   Selecting the size:\n   ------------------\n\nPress \"n\" to create a new image.  Select the size you would like with\nleft/right arrows if you picked slot 7.  (slot 5 is always 800KB, and slot 6\nis always 140KB).  It's fine to always pick 32MB, the default.  Scroll down to\nType, select \"Dynamic ProDOS directory\".  Scroll down to \"Create and name the\nimage\".  Use the normal image selection interface to navigate to the directory\nyou want to use.  Press tab to switch to the path view (you can manually enter\nparts of the path if you like), and while on the path, press Cmd-Enter to\nselect the named directory.  Note: The directory that will be selected is the\npath listed on the \"Path: \" line (relative to the Current KEGS Directory if it\ndoesn't start with a \"/\").\n\n   Using the maximum size:\n   ----------------------\n\nPress return on the slot/drive line you want to mount as a Dynapro directory.\nUsing selection, select into the directory you want to use, then press\nCmd-Enter to select that directory.  A maximum size Dynapro directory will be\nmounted.  The maximum size for slot 7 images is 32767.5KB (65535 blocks).\n\nHow to use\n----------\n\nIf you pick a directory that has files whose total size exceeds the size you\nselected, it will fail to mount.  The slot/drive entry will be commented out\nshowing no disk mounted.\n\nNow, from within KEGS, you can do any operation to this volume that you would\nhave if it were a normal disk image--copy files, initialize it, etc.  This\nworks under ProDOS 8 or GS/OS, or any program that supports ProDOS images.\nAll changes to the image as ProDOS volume are immediately reflected in the\ndirectory on your host machine.\n\nGotchas to watch out for\n------------------------\n\nKEGS aggressively makes the directory match the ProDOS volume.  If you erase a\nfile using GS/OS (as an example), KEGS will erase that file from the directory\nyou selected.  If you format the volume, KEGS will erase all files in the\ndirectory you selected.  So: be careful what you point KEGS at!\n\nHow it works\n------------\n\nKEGS keeps a version of the ProDOS volume in memory (so 32MB if you mount a\n32MB Dynapro directory).  Whenever any writes happen, KEGS looks to see what\nfile(s) are affected, and reparses them to recreate the files in the Dynapro\ndirectory on your host system.  Since ProDOS and GS/OS write one block at a\ntime, there are many times during a write to a file where the file is invalid.\nKEGS erases all files from the Dynapro host directory when the ProDOS file\nappears invalid, even briefly.  So, writing to a file can cause it to be\nerased from the Dynapro host directory until the file is completely written\nand appears valid again.\n\nHow to copy files from your host system to KEGS emulation\n---------------------------------------------------------\n\nProDOS and GS/OS do not expect ProDOS volumes to change behind their back.  To\nreliably transfer files from your host system into the emulated //gs system,\nyou should unmount and re-mount the Dynapro directory.  This tells ProDOS 8\nand GS/OS to expect changes to the volume, and to see any changes you've made.\n\nSo if you create a new file called \"NEW.TXT\" in the Dynapro host directory, it\ndoesn't show up in KEGS right away.\n\nPress F4, go to the Disk Configuration page (you may already be there if you\nleft the F4 configuration here), select the slot/drive of the Dynapro volume\nto remount and press Return.  A file selection dialog appears.  Just press\nCmd-Enter to re-mount the image (the selection for the existing volume will\nalready be correct).  Press F4 to return to the emulated system.\n\n"
  },
  {
    "path": "upstream/kegs/doc/README.kegs.txt",
    "content": "\nKEGS: Kent's Emulated GS version 1.34\nhttp://kegs.sourceforge.net/\n\nWhat is this?\n-------------\n\nKEGS is an Apple IIgs emulator for Mac OS X, Linux, and Windows.\nThe Apple IIgs was first sold in 1986 and was the most powerful computer in\nthe Apple II line.  An Apple IIgs has the capability to run almost all\nApple II, Apple II+, Apple IIe, and Apple IIc programs.\n\nKEGS supports all Apple IIgs graphics modes (which include all Apple //e\nmodes), plus plays all Apple IIgs sounds accurately.  It supports\nserial port emulation through TCP/IP connections, or can use real\nserial ports on Linux and Mac OS X.\n\nThe ROMs and GS/OS (the Apple IIgs operating system) are not included\nwith KEGS since they are not freely distributable.  KEGS is a little\nuser-hostile now, so if something doesn't work, let me know what went\nwrong, and I'll try to help you out.  See my email address at the end of\nthis file.\n\nKEGS QuickStart:\n---------------\n\nRun the KEGSMAC executable (see README.mac.txt on how to get around Mac\nsecurity restrictions), or kegswin.exe on Windows 10 or later, or xkegs on\nLinux.\n\nYou must download and place a ROM file in the directory you run KEGS from,\nor in your $HOME directory.  GS ROM version 01 should be named ROM.01, and\nGS ROM version 03 should be named ROM.03.  See Getting ROMs below.\n\nOnce KEGS is running, if it cannot find a ROM file, it will ask you to\nselect one using an Apple-II style directory selection screen.  When done,\nPress F4 to start.\n\nPress F4 while KEGS is running to select Disk images.  The default config.kegs\nwill boot Nucleus in s7d1.  If you unmount it (F4->Disk Configuration, go\ndown to s7d1, press E to eject, then F4, then do Ctrl-F1-F12 to reset and\nreboot, then the XMAS demo will start).\n\nApple IIgs programs need Command and Option keys, and these sometimes are\nused by windowing systems on Linux and Windows.  F1 is always Command (or\nOpen-Apple) and F2 is always Option (or closed-Apple), and F3 is an alias\nfor ESC.  So entering the IIgs control panel can be done with Ctrl-F1-F3.\n\nPress F5 to toggle the status display.  Press F6 to toggle speed between\n1MHz, 2.8MHz, 8MHz, and Unlimited.  You can only see the current speed with\nthe status lines enabled.\n\nYou can resize the KEGS window.\n\n\nKEGS features:\n-------------\n\nFast 65816 emulation:\n\tAbout 500-600MHz on a modern Mac Intel machines.  1GHz on Mac M1.\nEmulates low-level 5.25\" and 3.5\" drive accesses (even nibble-copiers work!).\nEmulates classic Apple II sound and 32-voice Ensoniq sound.\n\tAll sound is played in 16-bit stereo at 48KHz.\nEmulates all Apple IIgs and Apple II graphics modes, including border effects.\n\tCan handle display changes at any time (superhires at the top, lores\n\tat the bottom).  Always does 60 full screen video updates per second.\n\tSupports 3200-color pictures.\nMouse and joystick support.\nEmulated battery RAM remembers control panel settings.\nLimited SCC (serial port) emulation to enable PR#1/2 IN#1/2 and Virtual\n\tModem enables standard Apple terminal programs to telnet to any\n\tinternet address (or receive connections from another telnet).\nMockingboard A support.  Set slot 4 to \"Your card\" in the IIgs control panel.\n\tNote that the mouse doesn't work in GS/OS on ROM01 if slot 4 is\n\tset to \"Your card\".\nDynapro: You can mount host directories as volumes for ProDOS 8 and GS/OS,\n\tallowing easy transfer of data to/from KEGS emulation.  Changes to\n\tfiles inside KEGS emulation are immediately made to the files\n\tvisible in the host directory.\n\nKEGS by default emulates a 8MB Apple IIgs, but you can change this from\nthe Configuration Panel.\n\nKEGS is so accurate, even the built-in ROM selftests pass (you must be in\n2.8MHz speed mode to pass the self-tests and you must set the Configuration\nPanel entry \"Enable Text Page 2 shadow\" to \"Disabled on ROM 01\" for ROM 01.\nOtherwise, the selftests will fail with code 040000).\n\nRelease info:\n------------\n\nIncluded files:\n\tCHANGES\t\t\t- Description of changes since last release\n\tREADME.kegs.txt\t\t- you're here\n\tREADME.compile.txt\t- Describes how to build KEGS\n\tREADME.linux.rpm.txt\t- Describes how to install KEGS's RPM for Linux\n\tREADME.mac.txt\t\t- Mac OS X special directions\n\tREADME.a2.compatibility.txt - List of programs which need tweaking\n\tsrc/INTERNALS.overview\t- description of how KEGS code works\n\tsrc/INTERNALS.xdriver\t- Describes the xdriver.c routines for porting\n\tsrc/INTERNALS.iwm\t- Describes the internal 3.5\" and 5.25\" disk\n\t\t\t\t   handling routines\n\tKEGSMAC\t\t\t- the Mac OS X executable\n\tkegswin.exe\t\t- the Windows executable\n\tconfig.kegs\t\t- disk image configuration info\n\tsrc/\t\t\t- All the source code, with a Makefile\n\nYou need to provide:\n\n\t1) Patience.\n\t2) a ROM file called \"ROM\", \"ROM.01\" or \"ROM.03\" in the KEGS directory\n\t\t(or in your home directory).  It can be either from a ROM 01\n\t\t(131072 bytes long) or from a ROM 03 machine (262144 bytes\n\t\tlong.)\n\t3) A disk image to boot.  This can be either \"raw\" format, 2IMG, or\n\t\tWoz.  See discussion below.\n\nGetting ROMs\n------------\n\nYou need a copy of the memory from fe/0000 - ff/ffff from a ROM 01 GS\nor fc/0000 - ff/ffff from a ROM 03 GS, and put that in a file called\n\"ROM\".  I'll eventually write detailed instructions on how to do this.\nThere are links to download ROM 01 and ROM 03 at\nhttps://www.whatisthe2gs.apple2.org.za.\n\n\nRunning KEGS:\n------------\n\nThe distribution comes with the full source code for all platforms in\nthe src/ directory, the Linux executable as xkegs, the Windows executable\nis kegswin.exe, and the Mac OS X executable as KEGSMAC.app.\n\nSee the README.compile.txt file for more info about compiling for Linux.\n\nOn Linux, you must start KEGS from a terminal window.  You can double-click\non KEGSMAC.app or kegswin.exe to run those executables.\n\nYou need to place the \"config.kegs\" file someplace where KEGS\ncan find it.  The simplest place is in your home directory, so copy it there\nwith the Finder (or using the Terminal).  You can also make the directory\nLibrary/KEGS from your home directory, and then place config.kegs there\nalong with the ROM file.  You do not need a starting config.kegs file\non a Mac--KEGS will offer to make it for you if it cannot find one.\n\nStart kegs by Double-clicking the KEGSMAC icon on a MAC, or by running\nthe executable (kegswin on Windows, and kegs on Linux).  KEGSMAC can\nbe run by the Terminal window on a Mac as well (which enables access to\nmore debug information) by typing: \"./KEGSMAC.app/Contents/MacOS/KEGSMAC\".\nThere may be permissions/security issues on a Mac, see README.mac.txt for\ndetails. \n\nAssuming all goes well, KEGS will then boot up but probably not find any\ndisk images.  See below for how to tell KEGS what disk images to use.\nTip: Hitting \"F8\" locks the mouse in the window (and hides the host cursor)\nuntil you hit \"F8\" again.\n\n\nConfiguration Panel:\n-------------------\n\nYou enter the Configuration panel by pressing F4 at any time.  You tell\nKEGS what disk images to use through the Configuration panel.  (If KEGS\ncouldn't find a ROM file, you will be forced into the Configuration\nPanel mode until you select a valid ROM file).\n\nTo select a ROM file, select \"ROM File Selection\" and then select your\nROM file.  If you were not forced into the panel at startup, the KEGS\nfound one and you can skip this step.\n\nDisk Images\n-----------\n\nThe primary use of the Configuration Panel is to select disk images.  To\nchange disk images being used, select \"Disk Configuration\".  Each slot\nand drive that can be loaded with an image is listed.  \"s5d1\" means slot\n5, drive 1.  Slot 5 devices are 3.5\" 800K disks, and slot 6 devices are\n5.25\" 140K disks.  Slot 7 devices are virtual hard drives, and can be\nany size at all (although ProDOS-formatted images should be less than\n32MB).\n\nJust use the arrow keys to navigate to the device entry to change, and\nthen select it by pressing Return.  A scrollable file selection\ninterface is presented, letting you locate your image files.  To quickly\njump to a particular path, you can press Tab to toggle between entering\na path manually, and using the file selector. Press Return on \"..\"\nentries to go up a directory level.  When you find the image you want,\njust press Return.\n\nIf the image has partitions that KEGS supports, another selection\ndialog will have you select which partition to mount.  You will probably\nonly have partitions on direct devices you mount (or on a Mac, of .dmg\nimages of CDs).  For instance, on a Mac, /dev/disk1 can sometimes be the\nCDROM drive.\n\nKEGS can handle \"raw\", .dsk, .po, 2IMG, 5.25\" \".nib\" images, most Mac Diskcopy\nimages and partitioned images, .zip files, .sdk archives, and .woz files.  The\n.dsk and .po formats you often find on the web are really \"raw\" formats, and\nso they work fine.  KEGS uses the host file permissions to encode the\nread/write status of the image.  KEGS can open any image file compressed with\ngzip (with the extension \".gz\") or inside a .zip archive automatically as a\nread-only disk image.\n\nAn image is the representation of an Apple IIgs disk, but in a file on\nyour computer.  For 3.5\" disks, for example, a raw image would be exactly\n800K bytes long (819200 bytes).  KEGS directs the emulated GS accesses to\nthe image, and does the correct reads and writes of the Unix file instead.\n\nTo do \"useful\" things with KEGS, you need to get a bootable disk image.\nYou can go to: https://archive.org/details/softwarelibrary_apple2gs\nto download GS/OS 6.0.1 (the last official Apple release).\n\nYou can also get Apple II programs in \".dsk\" format from a variety of\nsites on the internet, and these should all work on KEGS as well.\n\nKEGS also supports partitioned devices.  For instance, if you have a CD-ROM\non your computer, just pop an Apple II CD in, and KEGS can mount it, if\nyou have a Unix-based system (Linux, any Unix, and Mac OS X).\n\nAvoid having KEGS access a writeable image which have mounted on your Mac\nat the same time (always unmount it from your Mac before letting KEGS\naccess it)!\n\nIf you do not have any disk mounted in s7d1, KEGS will jump into the monitor.\nTo boot slot 6 (or slot 5), use the Apple IIgs Control Panel by pressing\nCtrl-Command-ESC (Ctrl-F1-F3 on Windows, for example).\n\nSupport for 5.25\" Woz nibblized images is limited to using provided images--\nKEGS cannot create new images currently.\n\nIn addition to changing disks, you can also just \"eject\" and image by\nmoving the cursor to select that slot/drive and then press \"E\".  The\nemulated IIgs will immediately detect changes to s5d1 and s5d2.\n\nCare should be taken when changing images in slot 7--KEGS does not notify\nGSOS that images have changed (or been ejected), and so it's best to make\nchanges when GSOS is not running.\n\n\nKey summary:\n-----------\n\nF1:\tAlias of Command\nF2:\tAlias of Option\nF3:\tAlias of ESC for Windows compatibility.\nF4:\tConfiguration Panel\nF5:\tToggle status lines on/off\nF6:\tToggle through the 4 speeds: Unlimited, 1MHz, 2.8MHz, 8.0MHz (ZipGS)\nShift-F6: Enter KEGS debugger\nF7:\tToggle debugger window\nShift-F7: Toggle fast_disk_emul on/off\nF8:\tToggle pointer hiding on/off.\nF9:\tInvert the sense of the joystick.\nShift-F9: Swap x and y joystick/paddle axes.\nCtrl-F9: Copy Text screen to host system clipboard\nF12:\tAlias of Pause/Break which is treated as Reset\n\nF2, Alt_R, Meta_r, Menu, Print, Mode_switch, Option:   Option key\nF1, Alt_L, Meta_L, Cancel, Scroll_lock, Command:       Command key\nNum_Lock:\t\tKeypad \"Clear\".\nF12, Pause, Break:\tReset.  Must press Ctrl to get Apple IIgs to reset\n\n\"Home\": Alias for \"=\" on the keypad (since my Unix keyboard doesn't have an =).\n\nAlt keys don't work well for Command and Option on Windows.  They only\ncan be detected when another key is pressed.  So Alt_L-Q will be detected\nas Command-Q.  But just pressing Alt_L alone will not do anything.\n\n\nUsing KEGS:\n----------\n\nThe host computer mouse is the Apple IIgs mouse and joystick by default.\nBy default, the host pointer is not constrained inside the window and\nremains visible.  Press F8 to hide the cursor and constrain the mouse.  F8\nagain toggles out of constrain mode.\n\nThe middle mouse button or Shift-F6 causes KEGS to stop emulation, and enter\nthe debugger.  You can continue with \"g\" then return in the debug window.\nYou can also disassemble memory, etc.  The section \"Debugging KEGS\"\nabove describes the debugger interface a little more.\n\nKEGS has no pop-up menus or other interactive interfaces (other than\nthe debug window, and the occasional error dialogs on Mac OS X).  Input to\nthe debug window is only acted upon when the emulation is stopped\n(Shift-F6, middle mouse button, or hitting a breakpoint).\n\nQuitting KEGS:\n-------------\n\nJust close the main KEGS window, and KEGS will exit cleanly.  Or you\ncan select Quit from the menu.  Or enter ctrl-c in the debugger window.\nOr press the middle-mouse button in the emulation window, and then type\n\"q\" return in the debug window.\n\nCommand/Option keys:\n-------------------\n\nIf you have a keyboard with the special Windows keys, you can\nuse them as the command/option keys.  For those without those keys,\nthere are several alternatives.\n\nThe following keys are Option (closed-apple) (not all keyboards have all\nkeys):  F2, Meta_R, Alt_R, Cancel, Print_screen, Mode_switch, Option,\nor the Windows key just to the right of the spacebar.  The following keys are\nCommand (open-apple):  F1, Meta_L, Alt_L, Menu, Scroll_lock, Command,\nthe Windows key left of the spacebar, and the Windows key on the far right\nthat looks like a pull-down menu.  You can use F1 and F2 if you cannot make\nanything else work (especially useful if your OS is intercepting some\nAlt or Command key sequences).\n\nIf you can't get any of these to work on your machine, let me know.\nNote that X Windows often has other things mapped to Meta- and Alt-\nkey sequences, so they often don't get passed through to KEGS.  So it's\nbest to use another key instead of Alt or Meta.\n\nThe joystick/paddle buttons are just the Command and Option keys.\n\n\nReset:\n-----\n\nThe reset key is Pause/Break or F12.  You must hit it with Ctrl to get it to\ntake effect (just like a real Apple IIgs).  Ctrl-Command-Reset\nforces a reboot.  Ctrl-Command-Option-Reset enters selftests.\nSelftests will pass if you force speed to 2.8MHz using the middle\nbutton or F6 (and also set Enable Text Page 2 shadow = Disabled for ROM 01).\nWatch out for ctrl-shift-Break--it will likely kill an X Windows session.\nAlso note that the Unix olvwm X window manager interprets ctrl-F12 and will\nnot pass it on to KEGS--you'll need to use Break for reset in that case.\n\nFull Screen mode (MAC OS X ONLY):\n--------------------------------\n\nKEGS can run in full screen mode--which is especially useful when letting\nsmall kids use KEGS (but it is not really a lock, so do not let a 2 year\nold bang on the keyboard while running KEGS).\n\nFull Screen mode is toggled by clicking the green maximize button in the\ntitle bar.  To exit full screen mode, move the mouse to the top of the screen,\nand the title bar will reappear, and you can click the green maximize button\nto shrink the window down.\n\nResizing the KEGS window:\n------------------------\n\nAll versions of KEGS allow the window to be resized, so you can make the\nwindow take up nearly the full screen without being \"full screen\" mode.\nKEGS will save the window position and size when it updates config.kegs,\nbut currently doesn't automatically update config.kegs.  So if you resize\nthe window, and then do something which changes config.kegs (inserting or\nejecting disks always immediately update config.kegs, or selecting the\nF4 menu option \"Save changes to config.kegs\" will update config.kegs).\n\n\nJoystick Emulation (Mouse, Keypad, or real native joystick):\n-----------------------------------------------------------\n\nThe default joystick is the mouse position.  Upper left is 0,0.  Lower right\nis 255,255.  Press Shift-F9 to swap the X and Y axes.  Press F9 to reverse\nthe sense of both paddles (so 0 becomes 255, etc).  Swapping and\nreversing are convenient with paddle-based games like \"Little Brick Out\"\nso that the mouse will be moving like the paddle on the screen.  \"Little\nBrick Out\" is on the DOS 3.3 master disk.  The joystick does not work\nproperly if the pointer is constrained in the window.\n\nYou can also select from a \"Keypad Joystick\" or a real joystick from\nthe Configuration panel.  Press return on the \"Joystick Configuration\"\nentry, and then select between Mouse Joystick, Keypad Joystick, or one\nof two native joysticks.  The Keypad Joystick uses your keypad number\nkeys as a joystick, where keypad 7 means move to the upper left, and\nkeypad 3 means move to the lower right.  Pressing multiple keys together\naverages the results, allowing finer control than just 8 directions.\nAlso, joystick scaling is selectable here for games which require\na greater range of motion to work correctly, along with trim adjustment\nwhich moves the centering point.  Adjusting scaling usually means you\nwill need to adjust the trim as well.\n\nThe left mouse button is the mouse button for KEGS.  The right mouse\nbutton (if you have it) or F6 toggles between four speed modes.  Mode 0\n(the default) means run as fast as possible.  Mode 1 means run at 1MHz.\nMode 2 means run at 2.8MHz.  Mode 3 means run at 8.0MHz (about the speed\nof a ZipGS accelerator).  Most Apple //e (or earlier) games need to be\nrun at 1MHz.  Many Apple IIgs demos must run at 2.8MHz or they will not\noperate correctly.  Try running ornery programs at 2.8MHz.  3200 pictures\ngenerally only display correctly at 2.8MHz or sometimes 8.0MHz.\n\n\nDebugging KEGS:\n--------------\n\nKEGS by default now continues emulation even when it detects buggy programs\nrunning.  (Now I know why Appleworks GS always seemed to crash!).\n\nKEGS divides buggy programs into two severities: Code Yellow and Code Red.\nThe status is displayed in words in the text area under the emulation window.\nIf nothing is wrong, nothing is printed.\n\nA Yellow bug is a mild bug where an Apple IIgs program merely read an\ninvalid location.  Although completely harmless, it indicates the potential\nfor some Apple IIgs program bug which may become more severe shortly.\nFor instance, closing the \"About This Apple IIgs\" window in the Finder\ncauses a code yellow alert, but it seems quite harmless.\n\nA Code Red bug is a more serious problem.  The Apple IIgs program either\ntried to write non-existent memory, entered an invalid system state, or\nperhaps just tried to use an Apple IIgs feature which KEGS does not implement\nyet.  Note that entering GSBUG tends to cause a Code Red alert always, so if\nyou intended to enter it, you can ignore it.  My recommendation is to\nsave work immediately (to new files) and restart KEGS if you get into the\nRed mode.\n\nKEGS also supports breakpoints and watchpoints.  In the debug window, you\nset a breakpoint at an address by typing 'bp' and then the address.\nTo set a breakpoint on the interrupt jump point, type:\n\nbp e10010\n\nTo list all breakpoints, just type 'bp' with no number after it.\nTo delete a breakpoint, enter 'bp clear\" and then the address:\n\nbp clear e10010\n\ndeletes the above breakpoint.  You can also break on a range of addresses:\n\nbp e10400-e107ff\n\nYou can erase all breakpoints with:\n\nbp clear all\n\nThis is a \"transparent\" breakpoint--memory is not changed.  But any\nread (including instruction fetch) or write to that address will cause KEGS to\nhalt right after the access occurs.  So you can\nset breakpoints on I/O addresses, or ROM, or whatever.  Setting a breakpoint\nslows KEGS down somewhat, but only on accesses to the 256 byte \"page\"\nthe breakpoint is on. Breakpoints are not just instruction breakpoints,\nthey also cause KEGS to halt on any data access, too (usually called\nwatchpoints).\n\nYou can get more debugger window help with \"help\" and \"help bp\".\n\nUseful locations for setting breakpoints:\nbp 3f0\t\t- Break handler\nbp c000\t\t- Keyboard latch, programs read keys from this address\n\nFor debug, KEGS can log the instruction executed, saving the last 2 million\ninstructions.  Enable logging with (in the debug window, press F7):\n\nlogpc on\n\nThen run whatever you want, and stop KEGS (Shift-F6, middle mouse, or hitting\na breakpoint).  Then in the debug window do:\n\nlogpc save\n\nThis will create the text file \"logpc_out\" which you can look at.\n\nYou can start KEGS with \"logpc on\" by passing in the \"-logpc\" option to\nKEGS when you start it.\n\n\nKEGS command-line option summary:\n--------------------------------\n\nThere are others, but the Configuration panel provides a better way to\nset them so they are no longer listed here.\n-skip:\tKEGS will \"skip\" that many screen redraws between refreshes.\n\t-skip 0 will do 60 frames per second, -skip 1 will do 30 fps,\n\t-skip 5 will do 10 fps.\n-audio [0/1]: Forces audio [off/on].  By default, audio is on unless\n\tthe X display is a remote machine or shared memory is off.\n\tThis switch can override the default.  -audio 0 causes KEGS to\n\tnot fork the background audio process, but Ensoniq emulation\n\tis still 100% accurate, just the sound is not sent to the\n\tworkstation speaker.  Audio defaults off on Linux for now.\n-arate {num}: Forces audio sample rate to {num}.  44100 and 48000 are\n\tusual, you can try 22050 to reduce KEGS's overhead.  On a reasonably\n\tfast machine (>250MHz or so), you shouldn't need to mess with this.\n-dhr140: Will use the old Double-hires color algorithm that results in\n\texactly 140 colors across the screen, as opposed to the blending\n\tbeing done by default.\n-logpc: Turn on PC logging.  Useful for debugging early startup problems.\n\nX-Windows/Linux options\n-15:\tKEGS will only look for a 15-bit X-Window display.\n-16:\tKEGS will only look for a 16-bit X-Window display (not tested, probably\n\t will get red colors wrong).\n-24:\tKEGS will only look for a 24-bit X-Window display.\n-display {machine:0.0}: Same as setting the environment variable DISPLAY.\n\tSends X display to {machine:0.0}.\n-noshm:\tKEGS will not try to used shared memory for the X graphics display.\n\tThis will make KEGS much slower on graphics-intensive tasks,\n\tby as much as a factor of 10!  By default, -noshm causes an\n\teffective -skip of 3 which is 15 fps.  You can override this\n\tdefault by specifying a -skip explicitly.\n-cfg:  Next argument gives the path and name of the config.kegs file to use\n-rom=path_to_rom or -rom path_to_rom: Selects the ROM file, either a //e\n\tROM file, or a IIgs ROM 01 or ROM 03 file.\n-s5d1=path_to_image or -s5d1 path_to_image mounts the selected image in\n\tSlot 5 Drive 1 (800KB disk).  Can use -s5d1 through -s6d2, as\n\twell as -s7d1 through -s7d12.\n\n\nApple IIgs Control Panel:\n------------------------\n\nYou can get to the Apple IIgs control panel (unless some application\nhas locked it out) using Ctrl-Command-ESC.  On Windows, Command and ESC can\nbe tricky to enter (Windows grabs them for itself), so Ctrl-F1-F3 is the\nsame as Ctrl-Command-ESC.\n\nImportant things you can do here: Change the speed to 1MHz (for Apple II\ncompatibility) (or just F6 to change the speed to 1MHz), change the boot\ndevice (so you can boot s6d1 directly), and change Slot 4 to Your Card to\nenable Mockingboard.\n\n\nMoving data into and out of KEGS\n--------------------------------\n\nUse the Dynapro image support to mount a folder on your host machine as\na ProDOS volume for ProDOS 8 or GS/OS.  Then use GS/OS (or any other program)\nto move files to emulated volumes, or just keep using Dynapro.  See\nREADME.dynapro.txt for more details.\n\nFrom the debugger (press F7 to open the Debugger window), you can load and\nsave any memory address to a Unix host file.  To save the hires page 1\nscreen, use:\n\n2000.3fffus hgr1\n\nSaves from $2000 through $3fff to the file \"hgr1\".  You can also load memory\nfrom a file using:\n\n300ul helper\n\nLoads the contents of the file \"helper\" at $300.\n\n\nUsing the VOC\n-------------\n\nKEGS has limited support for the Apple Video Overlay Card (VOC).  The VOC\nsupports a special 640x400 interlaced SHR video mode which is what KEGS also\nsupports.  To turn on this mode, you need to do:\n\nEnable VOC (Press F4, select \"Video setting\"\", then select\n\"Enable VOC = Enabled\")\n\nThe following write done manually will enable the VOC:\n\nCALL -151\nc029:c1\t\t# Turn on SHR\nc0b1:39\t\t# Set bits [5:4]=11 to enable Interlaced mode\nc0b5:80\t\t# Set bit 7 to enable Interlace mode\n\nAnd then KEGS will show 640x400 (or 320x400) SHR screen, where even lines\n(0,2,4,etc) come from bank $e1 like normal SHR mode, and odd lines come from\nbank $e0 (from $2000-$9fff, using it's own palettes, etc.).  The real VOC\nshows interlaced data--it draws one frame of data from bank $e1, then the\nnext from of data from bank $e0, shifted down one line, etc., where each\nfrom takes 16msec, for an effective display of 30fps.  KEGS just draws both\nbank $e1 and bank $e1 changes every frame at 60fps.  This can be fixed if\nanyone uses this mode and wants better accuracy.\n\n\nDetails on config.kegs and disk images\n--------------------------------------\n\nThe file \"config.kegs\" describes the images KEGS will use.  Although you\ncan edit the file manually, in general you can use the Configuration Panel\nto make all the changes you need.  This information is for reference.\n\nKEGS by default will boot s7d1 (unless you've changed that using the\nApple IIgs control panel), so you should put an image in that slot.\n\nKEGS, by default, runs the IWM (3.5\" and 5.25\" disks) emulation in an\n\"approximate\" mode, called \"fast_disk_emul\".  In this mode, KEGS\nemulates the hardware \"faster\" than real, meaning the data the code\nbeing emulated expects is made available much faster than on a real\nApple IIgs, providing a nice speed boost.  For instance, the 5.25\"\ndrives run 100x the real speed usually.  Almost everything will work\nexcept for nibble copiers, which don't like the data coming this fast.\nAlso, formatting new 5.25\" disks doesn't work will in fast_disk_emul mode.\n(Meaning, unless you're using a nibble copier or formatting a disk, you\nshouldn't run into an issue.  Almost all games/demos/etc run fine in this\nmode).  To make nibble copiers work, Press Shift-F7.\n\nIf you start with a .dsk or other raw image, and do writes to it that are no\nlonger ProDOS or DOS 3.3 standard, KEGS will automatically treat the image as\n\"Not-write-through-to-Image\" from then on.  This mode means KEGS will continue\nto emulate the disk properly in memory, but it cannot encode the changes in\nthe standard .dsk or .po image format.  It prints a message saying it has done\nso.  However, the \"disk\" in emulation is fully useable as long as KEGS is\nrunning.  A standard reformatting will not cause an image to flip to\nnot-write- through-to-Image, but running things like a \"drive-speed\" test will\ncause further changes not to propagate to the Unix file.  You will need to\n\"eject\" the image and re-insert it before writes will take effect.\n\nIn full accuracy mode (i.e., not fast_disk_emul), 5.25\" drive accesses\nforce KEGS to run at 1MHz, and 3.5\" drive accesses force KEGS to run at\n2.5MHz.\n\nKEGS supports WOZ version 1 and version 2 images.  These are fully read/write,\nbut most Woz images are marked read-only in the header.  When accessing a\nWoz image, KEGS automatically acts as if fast_disk_emul is disabled.\n\nKEGS Timing:\n-----------\n\nKEGS supports running at four speeds:  1MHz, 2.8MHz, 8.0MHz-128MHz (ZipGS), and\nUnlimited.  Pressing the the F6 key cycles between these modes.  The\n1MHz and 2.8MHz speeds force KEGS to run at exactly those speeds,\nproviding accurate reproduction of a real Apple IIgs.\n\nOn regular host machines now, sitting at the BASIC prompt can make KEGS\nsay the speed is 4000MHz or higher.  This is because the emulated code\nis reading the keyboard (or other softswitch) more often than 500,000\ntimes per second, and due to how slow accesses work, that's the most\nthat can be done in one second, and so KEGS has lots of spare time, and\nmakes the Eff MHz or Sim MHz be too high.  You have to run a BASIC\nprogram or do something not checking for keypresses so often to see what\nthe true emulation speed is, so something like:\n\n10 A = SIN(A) + 1\n20 GOTO 10\n\nis a fine test.\n\nKEGS will always run at 1MHz at least.  If it is unable to keep up,\nit will extend the emulated time to maintain the illusion of running\nat 1MHz.  That is, it may do just 40 screen refreshes per real second,\ninstead of the usual 60.  This happens rarely.\n\n(There's a Mac OS X bug in recent releases after 10.13 which can cause KEGS\nto draw VERY slowly to the screen, such that KEGS falls below 1MHz.  On your\nMac, go to the Apple Menu->System Settings, then select Displays.  Select\nthe \"Color\" tab.  Change to a different setting, usually one with \"RGB\" in\nthe name.  See if KEGS now easily exceeds 1MHz).\n\nIf you force KEGS to run at 1MHz, it will strive to run at exactly\n1MHz (well, really 1.022MHz).  If it is running faster (almost always),\nit will pause briefly several times a second to maintain the 1MHz speed.  It\ndoes this in a friendly way that makes time available to other tasks.\nThis makes older Apple II games very playable just like a\nreal Apple IIgs on slow speed.  KEGS is running at exactly the same\nspeed as an Apple //e when in 1MHz mode.  The 1MHz mode you set\nthrough the F6 key overrides the \"fast\" mode you can access\nthrough the control panel.  But, 3.5\" accesses will \"speed up\" to 2.8MHz\nto enable that code to operate correctly while the 3.5\" disk is being\naccessed.\n\nIf you force KEGS to run at 2.8MHz, KEGS tries to run at exactly 2.8MHz.\nBut like a real unaccelerated Apple IIgs, if you set the control panel\nto \"slow\", it will really be running at 1MHz.  Accesses to 5.25\" disk\nautomatically slow down to 1MHz, when running the IWM in accurate mode\n(F7) or when accessing a Woz image.  Many Apple IIgs demos must be run\nat 2.8MHz.  The built-in selftests (cmd-option-ctrl-Reset) must run at\n2.8MHz.  Many Apple IIgs action games are more playable at 2.8MHz.\n\nThe 8.0MHz setting means follow the ZipGS-selected speed, but don't go\nfaster than 8.0MHz.  This 8.0MHz value can be changed to 8MHz, 16MHz,\n32MHz, 64MHz, or 128MHz at the Configuration settings (Press F4), and\nchange \"ZipGS Speed = \".  If your host computer cannot keep up, then the\nemulated second will be extended, and KEGS will have choppy audio and\nvideo.  You can use the ZipGS control panel, or ZIPPY.GS on the sample\ndisk image to set the emulated ZipGS speed to any 1/16th increment of\nthe selected speed (so if you pick a ZipGS speed of 64MHz on the KEGS\nConfiguration screen (F4), you can set the emulation speed to any value\nfrom 4-64MHz in 4MHz increments)\n\nThe Unlimited setting means run as fast as possible, whatever speed that\nis (but always above 1MHz).  Eff MHz gives you the current Apple IIgs\nequivalent speed.  Many games will be unplayable at the unlimited\nsetting.  Setting the IIgs control panel speed to \"slow\" will slow down\nto 1MHz.\n\nSound output has an important relationship to KEGS timing.  KEGS must\nplay one second of sound per second of emulated time.  Normally, this\nworks out exactly right.  But as noted above, if KEGS can't maintain the\nneeded speed, it extends the emulated second.  If it extends the second\nto 1.4 real seconds, that means KEGS only produces 1.0 second of sound\ndata every 1.4 seconds--the sound breaks up!\n\nIn all cases, 1MHz to KEGS is 1.022MHz.  It's just easier to say 1MHz.\n\n\nKEGS SAMPLE_DISK:\n----------------\n\nI'm providing a sample disk of freely available utilities/programs to\ndemonstrate a little of what KEGS can do.  I'm also including my simple\nchanges to a benchmark called \"SPEEDTEST\" to make it run under ProDOS and\ntime itself automatically.  The SAMPLE_DISK is not bootable since I'm\nnot sure if I can distribute PRODOS (the OS).\n\n\n   SPEEDTEST:\n   ---------\n\nIn the folder \"SPEEDTEST\", there are two BASIC programs.  OLD.SPEEDTEST\nis the old, unmodified DOS 3.3 emulator benchmark by Clayten Hamacher.\nIt does not run properly under ProDOS 8.  My modified version is\nSPEED.PRO, meaning converted to ProDOS.  I made few modifications, other\nthan to make the benchmarks time themselves.\n\nTo run, just say \"RUN SPEED.PRO\".  To run benchmarks, press \"B\".  If\nyou say \"A)ll tests\", make sure you have a 5.25\" disk image in s6d1!\n(A blank 140K image will work fine).\n\nThis modified SPEED.PRO can run on ANY Apple IIgs emulator (or on the real\nthing).\n\n   GSOS7, GSOS5, BYE.SYSTEM:\n   ------------------------\n\nThese are handy utilities I use on my s7d1 boot disk.  Get a GS/OS 6.x\nbootable disk image.  (See GSOS.INFO file for how to get GS/OS).\nRemove \"PRODOS\" from that disk's root directory, and copy GSOS7 to\nthe root directory.  Then copy SYSTEM/P8 to PRODOS.  Then move\nBASIC.System into SYSTEM/.  Then copy BYE.SYSTEM to the root directory,\nthen move BASIC.SYSTEM back to the root directory.\n\nWhat all this means is that now the root directory of your system disk\nis:  GSOS7, (other stuff), PRODOS, BYE.SYSTEM, and BASIC.SYSTEM.\nWhen you boot, ProDOS will boot (this is PRODOS 8) and will search\nfor the first *.SYSTEM file, and run it.  BYE.SYSTEM just does a BYE\ncommand, which puts you in the PRODOS 8 textual launcher.\nIf you now select GSOS7 (the first entry, already highlighted, just\nhit return), it will boot GSOS on slot 7.  (Use GSOS5 to boot slot5).\nOr, just move down and select BASIC.SYSTEM to go to BASIC.  A very simple\nprogram launcher!?\n\nNote that I didn't write GSOS5 or GSOS7--I just made a one byte hack\nto the default GS/OS launcher.  No real wizardry is going on here.\n\n\n   SHRINKIT3.4, GSHK1.1:\n   --------------------\n\nUseful for unpacking .SHK files you can download off of the net.\nAlways use GSHK (GS/OS version of ShrinkIt) for GS programs since\nthey may have resource forks.  It's also faster.  GSHK must be run from GS/OS.\n\n   LISTV2.0:\n   --------\n\nProDOS 8 text file lister, useful for viewing text files.\n\n   Wolfenstein3D:\n   ------\n\nWolfenstein 3D for the Apple IIgs.  No kidding!  Must be run from GS/OS.\n\n   SOUND22:\n   -------\n\nCool little ProDOS 8 program (SOUND.EDITOR) that plays hi-fidelity\n(relatively) through the old Apple II speaker.  This is included as a\ndemonstration of how accurate KEGS sound emulation is.\n\n   Sound.Smith.95:\n   --------------\n\nGS/OS application that plays SoundSmith songs, which are spreadsheet music,\nlike MODs.  I included some sample songs--FILE.11, FILE.16, FILE.17, and\nSPACE.HARRIER.  Enjoy!\n\n   SOLITAIRE:\n   ---------\n\nKlondike.  I like the interface on this game.\n\n   CAT.DOCTOR:\n   ----------\n\nFrom Prosel8 (which is now public domain), this utility is very handy for\nsorting directories (among other things).  Useful for arranging GSOS7,\nand BYE.SYSTEM mentioned above.\n\n   BGSOUND:\n   -------\n\nThis CDA lets you play Soundsmith songs in the background while other\napplications are running.  Very handy for playing Solitaire with some music.\n\n   DOCVu.CDA:\n   ---------\n\nThis CDA shows the current DOC contents in real-time.  It has neat visual\neffects while playing Soundsmith songs.\n\n   Zippy.gs\n   --------\n\nVery useful ProDOS 8 program by Andy McFadden for setting ZipGS parameters.\nIn KEGS, you'll want to use this to change the Zip speed to less than\n100% to make the \"Unlimited\" speed become limited to 7.5MHz, which is\nuseful for some games.\n\n\nKEGS:  What works:\n-----------------\n\nBasically, just about every Apple II program works.  See the file\nREADME.a2.compatibility for directions on how to make certain games/programs\nwork.\n\nKEGS is EXTREMELY compatible.  But, I haven't tested everything.  Let\nme know if you find a program which is not working correctly.\n\nSome old Apple II 5.25\" games require the old C600 ROM image, and don't work\nwith the default Apple IIgs ROM.  This is not KEGS's fault--these games\ndon't run on a real Apple IIgs either.  KEGS has built-in the old Apple II\nDisk PROM which you can enable by using the IIgs control panel to set\nSlot 6 to \"Your Card\".  This allows many more Apple II games to run, and\nis the recommended setting.\n\nThe NinjaForce Megademo mostly works, but sometimes hangs in the BBS Demo.\nJust skip that demo if it happens.\n\nThe California Demo hangs at startup unless you use the IIgs control panel\nto boot from slot 5, and then do a ctrl-Open_Apple-Reset to boot--doing\nthe above lets it work fine. This seems to be a bug in the demo.\n\n\nKEGS bugs:\n---------\n\nOn a ROM03, KEGS makes a patch to the ROM image (inside emulation, not\nto the Unix file) to fix a bug in the ROM code.  Both ROM01 and ROM03\nare patched to enable use of more than 8MB of memory.  I then patch the ROM\nself-tests to make the ROM checksum pass.  But other programs, like\nthe Apple IIgs Diagnostic Disk, will detect a ROM checksum mismatch.\nDon't worry about it.\n\nSound breaks up if KEGS is unable to keep up, this should not happen on\nany modern computer, but resizing the window can cause issues.\n\n\nSound emulation:\n---------------\n\nKEGS supports very accurate classic Apple II sound (clicking of the\nspeaker using $C030) and fairly accurate Ensoniq sound.\n\nWhen KEGS determines that no sound has been produced for more than\n5 seconds, it turns off the sound calculation routines for a small\nspeedup.  It describes that it has done this by saying \"Pausing sound\"\nin the debug window.\n\nIf your display is not using shared memory, audio defaults to off unless\nyou override it with \"-audio 1\".\n\nMockingboard emulation is just for the AY8913 sound chip, not for the\nSC01 speech chip.  To use the Mockingboard, you must do Ctrl-Cmd-ESC\n(Ctrl-F1-F3 on Windows, for example) to get to the IIgs control panel,\nselect Control Panel, then Slots, then set slot 4 to \"Your card\".  Make\nsure you press return.  Then get out, and then do Ctrl-Cmd-Reset.  The\nreset is a limitation of the IIgs ROM, it often doesn't seem to make\nproper register changes until the next Reset).\n\n\nSCC (Serial Port) emulation:\n---------------------------\n\nSee README.serial.ports.txt.\n\n\nKEGS status area:\n----------------\n\nThe status area is updated once each second.  It displays info I am\n(or was at some time) interested in seeing.\n\nLine 1: (Emulation speed info)\ndfcyc:  number of seconds since KEGS was started\nsim MHz:  Effective speed of KEGS instruction emulation, not counting\n\t\toverhead for video or sound routines.\nEff MHz:  Above, but with overhead accounted for.  Eff MHz is the\n\t\tspeed of an equivalent true Apple IIgs.  This is extremely\n\t\taccurate.\nsec:\tThe number of real seconds that have passed during on of KEGS's\n\t\temulated seconds. Should be 1.00 +/- .01.  Under 1\n\t\tmeans KEGS is running a bit fast, over 1 means KEGS is\n\t\trunning slow.  When you force speed to 2.8MHz, if KEGS\n\t\tcan't keep up, it extends sec, so you can see how slow\n\t\tit's really going here.\nvol:\tApple IIgs main audio volume control, in hex, from 0-F.\nLimit:\tPrints which speed setting the user has requested: 1MHz, 2.8MHz,\n\t\t8MHz=128MHz, or Unlimited.\n\nLine 2: (Host overhead)\nsleep_dtime:\tAmount of time, in seconds, where KEGS \"slept\" for the last\n\t\tsecond.  Indicates how idle KEGS was.\nout_16ms:\tAmount of time, in seconds, KEGS spent drawing to the host\n\t\tscreen and other overhead, in the last second.\nin_16ms:\tAmount of time, in seconds, KEGS spent emulating the Apple IIgs\n\t\tin the last second.\nsnd_pl:\t\tNumber of times a sound parameter was changed while it\n\t\twas playing, but there will always be 60 per second minimum.\n\nLine 3: (IWM info)\nFor each IWM device, this line displays the current track (and side for\n3.5\" disks).  If a disk is spinning, there will be an \"*\" next to the\ntrack number.  Only updated once a second, so the disk arm moving may\nappear to jump by several tracks.  \"fast_disk_emul:1\" shows that KEGS\nis using less accurate, but faster, IWM emulation.  Press F7 to toggle\nto accurate disk emulation.\n\n\nDocumentation To-Do:\n-------------------\n\nDescribe the tracing and breakpoint debug features.\nDescribe the debug interface.\nDescribe how the code works.\nDescribe more of what's known to work.\nDescribe my changes to SPEEDTEST.\n\nKEGS To-Do:\n----------\n\nFix the Ensoniq bugs to make sound more accurate.\n\n-------------------\n\nIf you have any problems/questions/etc., just let me know.\n\nSpecial thanks to Jeff Smoot of climbingwashington.com for letting me use\nthe picture of a keg for the Mac icon.\n\nKent Dickey\nkadickey@alumni.princeton.edu\n\n\nX Window (Linux) interface information:\n--------------------------------------------\n\nEvery version of Linux is different.  Supporting this is very difficult\nespecially since I do not run Linux myself.\n\nIf KEGS fails to start, try the following options:\n\nkegs -audio 0 -noshm\n\nThere may be a bug with drawing the border on x86 Linux with Shared Memory--\nadd the options \"-noshm -skip 0\" to fix this up (but lose some graphics\nperformance, sorry).  Try KEGS without these options first, but use\nthis as a workaround if necessary.\n\nIf you want the display to go somewhere different, make sure the shell\nenvironment variable $DISPLAY is set, or give the command-line argument\n\"-display {foo}\".\n\nKEGS also forks off a subprocess to help handle the sound if audio is\nactive.  If KEGS crashes in a unusual way (a core dump, for instance),\nyou may have to manually kill the subprocess.  (\"ps -ef| grep kegs;kill\nxxxxx\").\n\nUser geoff@gwlink.net adds some notes for mounting disks/floppies/CDs under\nSolaris:\n\n  To use a CDROM, insert the CD and let Volume Management mount it.\n  Edit kegs_conf and use the filesystem that shows up in the \"df -k\"\n  listing.  The volume name of the CDROM must be included.  For example,\n  a CDROM in an IDE drive would look like this:\n\n  /vol/dev/dsk/c1t0d0/ciscocd\n\n  A CDROM in a SCSI drive would look like this:\n\n  /vol/dev/dsk/c0t6d0/j1170_10804\n\nTo provide low-level ADB emulation, KEGS turns off Unix key repeat when the\nfocus is in the KEGS window.  It should be turned back on every time\nthe pointer leaves the KEGS window, but sometimes it doesn't.  Re-running\nKEGS (and then quitting it quickly) should turn key-repeat back on,\nor you can type 'xset r' in another terminal window.\n\nSometimes the converse is true--key repeat is \"on\" when the cursor is\nin the KEGS window.  Moving the cursor out of the window and then\nback in should solve it.  This is sometimes noticeable when running\nWolfenstein 3D GS.  I haven't spent much time debugging the problem.\nI think it may be the X Server.\n\nKEGS uses a private color-map for its X-window in 8-bit mode.  This\nmay cause colormap \"flash\" when your cursor enters the window.\n\nKEGS details/troubleshooting\n----------------------------\n\nKEGS will work on all platforms with a 15/16-bit, 24-bit, or 32-bit\ncolor display.  KEGS also supports an 8-bit display on X windows only.\nOn all platforms, it autodetects the color depth--no color switching\nis necessary as long as you're at a supported depth.\n\n\nDisk Image Details\n\nImages loaded into slot 6 (drive 1 or 2) are assumed to be 140K\n5.25\" disks, which is usually have the extension \".dsk\".  Images\nloaded into slot 5 (drive 1 or 2) are assumed to be 800K disk images\nand can be in any supported imahe format (including partitions, if\nyou have 800K partitions).  Images loaded into slot 7 (drives 1\nthrough 32) can be in any format and can be any size up to 4GB.\n\nKEGS boots s7d1 by default.  You can change this using the emulated IIgs\ncontrol panel, just like a real Apple IIgs.  KEGS emulates a IIgs with\ntwo 5.25\" drives in slot 6, two 3.5\" drives in slot 5, and up to 32\n\"hard drives\" in slot 7.  However, the current Configuration Panel only\nlets you set through s7d11.  ProDOS 8 can access disks up to s7d8, but GSOS\nhas no limit, so it's best to put HFS images past s7d8 in order to leave\nmore slots for ProDOS images.\n\nIf you're trying to use a real host device (CD-ROM, or hard drive, or\nfloppy), you should make the permissions on the /dev/disk* files something\nlike (meaning, everyone should have read permission):\n\nbrw-r--r--  1 root  operator  14, 0 Jun 10 00:01 /dev/disk2\n\nYou can do this on a Mac with:\n\nsudo chmod 644 /dev/disk2\n\nDO NOT RUN KEGS AS ROOT.  It is not designed for this and it's almost certain\nproblems will ensue.\n\n"
  },
  {
    "path": "upstream/kegs/doc/README.linux.partitions.txt",
    "content": "[ This info provided by Mike Thomas <phoenyx@texas.net> ]\n[ Updated 10/30/2003 by Kent: This file mentions editing \"kegs.conf\" to ]\n[  mount images--this is now replaced by the built-in Configuration Panel. ]\n\nSetup and configuration for x86 Linux:\n--------------------------------------\n\nKEGS is very easy to setup on your Linux box, but not foolproof.  First\nyou will need to decide where it will live.  When doing this you will\nhave to take into consideration any users of your machine.  It really\ndoesn't matter where it goes but it should have it's own directory and\nany supplied sub-directories should be there.  You may decide to use the\n/usr/local path where most systems recommend you install applications.\nAgain, this is entirely up to you.  On my system I have a separate\npartition for KEGS and all the miscellaneous files I've accumulated for\nit.  This makes it easy for me to reinstall the system should the need\narise.  Since I fool around with a large variety of software and OS's\nthis happens more often than it would for normal users.\n\nOnce you have put the files into the proper place you will need to\ncompile it.  You should not need to be 'root' to do this part.  The file\nREADME.compile explains the steps required for this.  Basically all you\nshould need to do is set the vars link to point to the file\nvars_x86linux.  You will want to check the file and make sure it is\naccurate for your system.  On my Redhat 6.0 system the default compile\nsetup works fine.  I use the pentium GCC compiler instead of the\nsupplied EGCS since it seems to build better binaries.  I do not\nrecommend using optimization levels higher than 2.  Once you have\nsuccessfully built the binaries you will need to copy them to the KEGS\ndirectory.  At a minimum copy the file kegs and to_pro.\n\nOk, now that you have the binaries you're almost ready.  You will need a\ncopy of the IIgs rom placed in the KEGS directory.  It should be named\nROM.  You will also need some disk images.  This is the hardest part.\nYou will need to create an HD image to work with.  Kent mentions an easy\nway in his readme.  From the shell type this:\n\necho \"testfile\" > testfile\nto_pro -16384 testfile\n\nIf you're using bash try this:\n\necho \"testfile\" > testfile\n./to_pro -16384 testfile\n\nThis should create a 16 megabyte HD image. This image will NOT be properly \nformatted to boot a system. The block zero is not properly setup. There is no \neasy way to fix this with the current KEGS/Linux system. There seems to be a \nproblem formating HD files for Prodos using KEGS. The system will easily erase \nthem but this doesn't help you. One solution is to make the primary boot drive\nuse a disk partition. This is more involved and will be explained later. \nAnother solution is to have access to the utility Block.Warden or some other \nP8 block utility. What you need to do is copy the boot blocks (blocks 0 and 1)\nfrom any bootable disk to the HD image. With that done you can now install \nGS/OS.\n\nMake sure you set the proper file permissions on files needed by KEGS. You \nwill not be able to properly use it while logged on as root. KEGS uses the \nfile permissions to test if it should write the image to disk or the memory \nimage. As root, KEGS will never write the image since it thinks root\nalways has execute privilege. The main files which you will need read/write \naccess to are bram.data.1 and disk_conf. I suggest you have read access to all\nthe other files in the directory.\n\nOnce you've got all the proper permissions set, log onto the system with your\nnormal account. Start up X and a shell and cd to the KEGS directory. Assuming\nyou have the disk images for GS/OS edit your disk_conf file so it looks \nsimilar to this:\n\n# boot from install disk\ns7d1 = /usr/local/fst/gsos1\n\n# our HD image\n# you should rename POOF1 file created with to_pro\n# I use *.hdv to be compatible with other utilities\ns7d2 = /usr/local/fst/boot.hdv\n\n# other GSOS images\ns7d3 = /usr/local/fst/gsos2\n...\n\nIf you include all the GSOS images this way you will have a simple setup.\nExecute KEGS. For now go with the simplest command line possible. Since the\nnewer versions support auto detect on video resolutions this should be the\ncommand kegs by itself. Hopefully you will boot into the emulator. If so,\ninstall GSOS and you're ready to go.\n\n\nSound\n-----\n\nKent says sound doesn't work under Linux. This is partially true and much\ndepends on the sound system you have installed. I have been successful with\nsound on both Soundblaster and Soundpro systems. For long compositions the\nsound may break up but it is functional for games and system sounds.\n\n\nSystem Video Setup\n------------------\n\nThis is rather personal and based upon system hardware so I'll just give you my\nthoughts and setup info on this. My normal X system is configured \n@ 1152x864 15bpp due to constraints in the X server for my video system. I\nhave custom configured it to boot into this mode and then I change to 800x600\nby using the CTRL+ALT+(keypad)PLUS sequence when I use KEGS. This makes the\nsystem much easier to read.\n\n\nKEGS and disk partitions\n------------------------\n\nKent mentions using partitions in his readme file but doesn't go into much\ndetails. I suspect this is mostly for accessing CD-roms. But it also works\nwell for HFS and Prodos formatted partitions also. Linux can also handle HFS\npartitions but not Prodos. To accomplish this you will need some free space on\nyour hard disk to create the partitions. You should not attempt this unless you\nknow what you are doing. You have been warned!\n\nThis task is not easy, nor is it supported by Kent. This was done and tested\non my own system so I know it works. You will need the HFS support utilities\nfor Linux. These are available on several Linux software sites. The primary\nneed for these utilities is to change an ext2fs partition to an HFS partition.\nYou can also use them to format the HFS volumes and copy files to and from\nthe partition. Newer versions of the Linux kernel support HFS file systems but\nyou will still need the utilities to create the original volume.\n\nYou must decide how you want to divide this partition. You can use it all for\nHFS or you can create Prodos volumes and HFS volumes. There are pros and cons\nfor using Prodos partitions instead of files. The pros, it is a little faster\nthan using an HD file. It is a real Prodos partition, formatted by KEGS. The\ncons, It must be backed up by using software on the emulator. You can't just\ncopy the HD file to another drive.\n\nYou must weigh these pros and cons and decide for yourself. Of course you\nare not limited to using partitions. I have a mix of partitions and files\nwhich works quite well. I like the P8 partitions for holding my boot system\nand applications. I back them up with GSHK to an HFS volume which I can then\ntransfer to another drive if I need even more security.\n\nIf you decide to use the whole partition for HFS then all you need to do is\nrun the HFS utilities and setup the HFS volume. Read the documentation which\ncomes with the utility package and follow it faithfully.\n\nIf you want to use P8 and HFS partitions you have some more work to do. If\nyou have never worked with low level partitions or are worried about destroying\nyour HD then you should probably forget this. For this to work you will have\nto change the partition table on your HD. This can and most likely will ruin\nany data you already have on there. I can not state this enough. Back up any\nimportant data on the hard drive! It is possible to change the partitions in\nLinux and not destroy the system. I have done this on mine but I also used\nthe last defined partition to make the changes and designed the system for\nchange should it be necessary.\n\nFor those of you who know how to handle this, take the partition you have\ndecided to use for KEGS and divide it into at least one 32 meg partition.\nMore is better but you will have to use the emulator to back up any drives\nsetup this way since Linux can't access a Prodos partition (yet). I have setup\n4 32 meg P8 partitions and several smaller HFS partitions of about 96 megs.\nThe reason I use smaller HFS partitions is because HFS isn't real efficient\nwith larger drives, but that's another story. The reason for the separate\nHFS partitions is so Linux can mount the HFS volumes like any other file system.\nI find this works quite easily for accessing files instead of using the HFS\nutilities. Just my opinion though. For P8 utilities you will still need to\nuse the HFS utility and configure the drive as an HFS volume. This lets KEGS\nread the partition when it loads the partition the first time. KEGS doesn't\nlike the Linux file system.\n\nOk, everybody got their partitions defined? You want to use the HFS tools\nand setup all the partitions (P8 and HFS) as HFS volumes. Next you will have\nto setup the HFS partitions. No, I'm not repeating myself. This is not the same\nthing as the low level partitions. HFS volumes have their own partition table.\nFor our purposes the table will only hold one partition on each whole volume.\nThe utility will give you the block count when you setup the partitions,\nwrite it down so you don't forget. After you have the volume partition setup\nyou can format the drive. Yeah I know you made a Prodos partition but it\ndoesn't hurt anything and KEGS will be able to read the partition when it\nboots up.\n\nWell, I think I covered everything needed to set them up. Now you will need to\nedit the /etc/fstab file. Make sure there are no references to the original\npartition. If you want to access the HFS volumes you will need to add them to\nthis file. You will also need to make sure that your Linux can understand the\nHFS format. Most newer kernels will as long as you've compiled it into the\nkernel or set it up as a module. KEGS doesn't need these entries to access\nthe volumes, they are just here for your convenience. In fact, if you don't\nneed Linux access I suggest you either leave them out or set them up as\nNOAUTO and mount them only when needed. Unmount them when finished to avoid\nany potential problems also. Do not give Linux access to any P8 partitions.\nFor one thing it can't recognize them and most likely will give you problems\nwhen you boot the system. For safety's sake the only partition I have listed\nin my /etc/fstab is a volume called transfer. You must set the filetype to HFS\nso Linux doesn't complain about the partitions if you mount them.\n\nOk, all partitions are defined, the /etc/fstab is setup correct, now you need\nto change the permissions on the device files associated with the partitions.\nThis allows you to access the files as a normal user. (Thanks Kent, guess I\ngot too involved and forgot it should be treated like the CD). You will need\nto reboot to ensure the system sees the new partitions and has the correct\n/dev/hd?# device files. If you setup the partitions with fdisk you should know\nthe correct hd info to select the files. For the sake of example let's assume\nthe device is HDB and the partitions numbers are 1,2,3. From the shell,\n\ncd /dev\nchmod 666 /dev/hdb1\nchmod 666 /dev/hdb2\nchmod 666 /dev/hdb3\n\nAfter you start KEGS you can format the Prodos partitions. If you use the\nmethod mentioned earlier for installing GS/OS you will want to quit the\ninstaller and run the advanced disk utilities on the utilities disk, then\nquit back to the installer program or reboot.\n\nNow I know this all sounds like a lot of trouble but (IMHO) it's worth it. For\none thing, KEGS will format a Prodos partition without problems (unlike an HD\nfile image) which makes a great boot system. And with GS/OS 6.01 you can access\nlarge HFS volumes for storage and GS applications. You can also download from\nthe net to the HFS volume (if it's mounted) and avoid the trouble of copying\nfiles to an image with to_pro. Not to mention the fact that the newest GNO\nworks with HFS drives.\n\nOne more note, if you use HFS you will need to patch the HFS fst. There is a\none byte bug which mis-calculates HFS block sizes and WILL trash your HFS\ndrive. The patch is at several places on the net or ask someone in one of\nthe comp.sys.apple2 news groups.\n\nMiscellanea\n-----------\n\nTo ease access to the KEGS binary, make an executable script which contains\nany command line parms you need. Then put this script somewhere in the path\nso you can execute it from any shell. Depending on the desktop you use you\nmay want to setup links for editing the disk_conf file also. With GNO/ME you\ncan launch KEGS without the shell but I don't recommend this since you won't\nknow what happened when it dies. With KDE you can set up the launcher to use\na shell, this is much better but until you have your setup stable you will\nwant to use a regular terminal so you can keep track of what's going on. Like\nGNO/ME, the KDE shell will close as soon as KEGS quits with an error.\n\n\nThanks\n------\n\nI hope this info helps you enjoy KEGS. Many thanks to Kent for creating this\nfine emulator and for putting up with me bugging him with 'bug' reports. Many\nof these weren't actually bugs but were my own fault due to lack of knowledge\nabout *nix systems. But Kent was prompt in fixing the ones which truly were\nbugs. Thanks to him I can now play my favorite game, Questron 2 (gs version)\nwhich requires a player disk in slot 5. I know no other emulator which can\nactually play this game.\n\nMike Thomas\n\n"
  },
  {
    "path": "upstream/kegs/doc/README.mac.txt",
    "content": "\nMAC OS X port of KEGS (KEGSMAC): http://kegs.sourceforge.net\n-------------------------------------------------------------\n\nThis is a Mac OS X Swift port.  Most of KEGS is written in C, but the\nUI is in Swift.\n\nDownloading, and making it runnable:\n-----------------------------------\n\nDownload from http://kegs.sourceforge.net/.  If you're using Safari in it's\ndefault mode (unpack \"Safe\" files after downloading), you'll end up with\nkegs.1.xx.tar in your Downloads/ directory.  (where xx will be the version).\nMac OS X will not let you run unsigned quarantined code you find on the net\n(which is good, generally), so you need to remove quarantine.  At a Terminal\nwindow, cd to where you want to install KEGS (it can just be your home\ndirectory):\n\ncd\ncat ~/Downloads/kegs.1.xx.tar | tar xvf -\n\nIf you have a compressed file like kegs.1.xx.tar.gz, do:\n\ncd\ngunzip < ~/Downloads/kegs.1.xx.tar.gz | tar xvf -\n\nYou will now have a directory names kegs.1.xx and in that, KEGSMACK.app/.\n\nIf you've let Safari or the Finder unpack KEGS, then you can do this command\nfrom the Terminal in the directory you unpacked KEGSMAC to remove the\nquarantine:\n\nxattr -r -d com.apple.quarantine KEGSMAC.app\n\nUsage:\n-----\n\nLike most other KEGS versions, KEGSMAC is best run from a Terminal\nwindow.  Just type \"./KEGSMAC.app/Contents/MacOS/KEGSMAC\" in the directory\nyou installed/compiled it in.  You need to have a ROM file (named\nROM, ROM.01, or ROM.03) and a config.kegs in the same directory or in your\nhome directory (read the README--these files are searched for in various\nplaces).\n\nKEGSMAC can also be run from the Finder by double-clicking on the icon, or\nby doing \"open KEGSMAC\", but if you do this, debugging problems is a little\ntougher.\n\nTo quit, either click the window close box, or select Quit from the menu.\nTo go full screen, click the Maximize button in the window.  You can\nresize the window to any size you like.\n\nMac OS X now has \"quarantine\" and Disk Access Permissions, which can be a\npain for users of KEGSMAC.  You must remove Quarantine or it won't work at\nall, but the disk access permissions are used to protect special Folders like\nDocuments and Downloads.  System Preferences->Security&Privacy->Files and\nFolders should let you add permissions to KEGS--but it doesn't seem to\nalways stick.  I'm not sure why this is.  A simple workaround is to\nplace your ROMs and disk images in a directory like \"Apple2\" or something\nsimilar in your home directory.  Then KEGSMAC won't need access to Desktop\nor Downloads and should work better.  If you have code development\nsuggestions on how to have KEGSMAC work better, please let me know.\n\nCompile directions\n------------------\n\nSee README.compile.txt\n\n"
  },
  {
    "path": "upstream/kegs/doc/README.serial.ports.txt",
    "content": "\nKEGS documentation: Serial Ports\n\nSCC (Serial Port) emulation:\n---------------------------\n\nKEGS emulates the two serial ports on a IIgs as being sockets (outgoing or\nincoming), or connected to real devices, or being a Virtual Modem.  The\nincoming ports KEGS will use is 6501 for slot 1 (the printer port) and\n6502 for slot 2 (the usual modem port).  By default, slot 1 is emulated as\na real device (but defaults to desiabled), and slot 2 emulates a Virtual\nModem.\n\nYou configure how you want the serial emulation to work on the Configuration\nscreen (F4), then select \"Serial Configuration\".  There are settings for\nslot 1 and slot 2 separately.  \"Main Setting\" sets the overall connection\ntype: \"Use Real Device\", \"Use a virtual modem\", \"Use Remote IP\", and \"Use\nincoming port 6501\" (or 6502).\n\nThe \"Status\" line under Serial Port Configuration says whether the port is\nopened.  Status doesn't change while you're in the F4 page, so if you\nmake a change and want to see if it worked, press F4 (to leave the\nConfiguration screen) and F4 again (to come right back) and see what the\nstatus is.\n\nApple II communications programs often want to initialize modems themselves,\nand so using them with any direct connection can cause them to fail to\ninitialize themselves properly.  For instance, if you use ProTerm, it will\noften say \"failed to initialize hardware\"--just keep selecting \"ok\", and then\nforce it online with Cmd-Ctrl-T.  Once \"online\", I always do \"ATZ\" to get\nthe text modem responses (OK, RING, etc.).\n\n  Real Device\n  -----------\n\nOn Windows, you can select COM1 through COM4 as the serial device.  You\nselect which one on the \"Windows Device\" line, and then KEGS will try to\nopen it.  On a Mac or Linux, KEGS let's you select any /dev/ device to be\nyour serial connection (if it's a USB to serial port, pretty likely since\nserial ports don't exist much any more, it'll be something like\n/dev/tty.usbserial-1480 on a Mac, or /dev/USB0 on Linux).  Select this on\nthe \"Real Device\" line, and it's a file selector like for disk images.\nMake sure you check \"Status\" to make sure the device opens.\n\n\n  Virtual Modem\n  -------------\n\nA Virtual Modem means KEGS acts as if a modem is on the serial port\nallowing Apple II communcation programs to fully work, but connect to\ninternet-enabled sockets.  KEGS emulates a \"Hayes-Compatible\" modem,\nmeaning it accepts \"AT\" commands.  You can use KEGS to connect to free\ntelnet-BBSs, or run a BBS program on KEGS and become a telnet BBS yourself.\nAll \"AT\" commands in KEGS work in upper or lower case.  This description\ngives all commands in upper case since it's a little easier to read.\n\nThe two main AT commands are: ATDT for dialing out, and ATA for receiving\ncalls.  To dial out, enter \"ATDThostname\", or for example,\n\"ATDTboycot.no-ip.com\" (which is down at the moment, unfortunately).\nYou can also enter an IP address, like \"ATDT127.0.0.1\".  On a Mac or Linux,\nto create a simple socket server to allow connections for testing, do in\na terminal window:\n\n\tnc -l 127.0.0.1 9111\n\nwhere nc is the NetCat program (generally available), 127.0.0.1 is the IP\naddress of the local machine always, and 9111 is the port number (any\nrandom number you want above 1024).  Then in your terminal program running\ninside KEGS, do:\n\n\tATDT127.0.0.1:9111\n\nAnd then you can type back and forth.  \"nc\" buffers all input from the\nkeyboard, and sends it once you hit return.  And nc isn't going to do proper\nterminal emulation, so return doesn't scroll to the next line.\n\nModems use \"+++\" to get the attention of the modem: press +++ within 1\nsecond, and then wait 1 second.  KEGS will say \"OK\", and then you can hang\nup with \"ATH\".\n\nKEGS' virtual modem also accepts incoming \"calls\".  While using the virtual\nmodem on slot 2, port 6502 is open ready for incoming \"calls\" (unless you're\nconnected using ATDT to some other address).  So in your terminal program,\nturn on verbose settings with \"ATZ\" and then connect to port 6502 using\nnc:\n\tnc -t 127.0.0.1 6502\n\nKEGS will start printing \"RING\" every second, for 4 seconds.  You need to\ntype \"ATA\" within that time, and then you will connect.  Alternatively,\nyou can tell the virtual modem to automatically pick up after 2 rings:\n\n\tATS0=2\n\nKEGS will answer after 2 rings.  ATS0=0 means never answer.\n\nSummary of AT commands:\n\tATA\tanswer if RING'ing\n\tATZ\tReset all settings to default (which is verbose)\n\tATE1\tTurns on echo\n\tATE0\tTurns off echo\n\tATV1\tTurns on verbose\n\tATV0\tTurns off verbose\n\tATO\tGoes online (if you did +++ and want to stay connected)\n\tATH\tHang up\n\tATDThostname:port_num\tOpens connection to \"hostname\" on port_num\n\tATS0=2\tAnswers after 2 rings without needing ATA.\n\nOn Windows, when KEGS tries to open this incoming socket, you'll\nneed to enable it and click Unblock to the dialog that Windows pops up.\nThis may happen when you first start KEGS since the virtual modem is now\non by default.\n\n  Remote IP\n  ---------\n\nIn this mode, KEGS tries to open a socket to the address in \"Remote IP\",\nto the port in \"Remote Port\".  If you set Remote IP to your printer,\nand Remote Port to 9100, then KEGS will open a connection to your printer.\n\n  Use incoming port 6501 (or 6502)\n  --------------------------------\n\nKEGS also supports an older, simpler socket interface where it accepts any\nconnection to port 6501 (or 6502 for slot 2). In KEGS, from APPLESOFT, if\nyou PR#1, all output will then be sent to socket port 6501.  You can see this\ndata by connecting to the port using nc:  In another terminal window, do:\n\"nc -t localhost 6501\" and then you will see all the output going to the\n\"printer\".\n\nUnder APPLESOFT, you can PR#1 and IN#1.  This gets input from the socket\nalso.  You can type in the telnet window, it will be sent on to the\nemulated IIgs.  You may want to edit the Serial Configuration option\n\"Serial Mask\" to \"Mask off high bit\" to make the PR#1 output easier to\nhandle (Apple II's set the high-bit in most bytes output, which is not\nwhat any modern computer wants).\n\nYou can \"print\" from BASIC by using something like PR#1 in KEGS and\n\"nc -t localhost 6501 | tee file.out\" in another window.\n\n"
  },
  {
    "path": "upstream/kegs/doc/README.win32.txt",
    "content": "\nHow to compile KEGS for Windows\n-------------------------------\n\nGo to the src/ directory in the KEGS release, and double-click the\nkegswin.vcxproj file.  Alternatively, from a PowerShell prompt, you can do:\n\nstart ./kegswin.vcxproj\n\nThis will launch Visual Studio.  Microsoft has a free Visual Studio Community\nEdition which is what I use.\n\nTo compile, click Build->Build Solution, or just press F7.\nIt will leave an executable in x64/Release/kegswin.exe if you use the\ndefault 64-bit build.\n\n\n\nOLD Information (probably best to ignore this)\n---------------\n\nThese are raw notes to myself.  Probably not useful for others, just\nignore, it's how I got KEGS into Visual Studio in the first place.\n\n----\n\nOpen Visual Studio 2022 (community edition).\nOn the right side, \"Get Started\" list, select the small link underneath\n\tthat says \"Continue without code ->\".\n\nIn this new VS2022 window, select File->New->Project from Existing Code.\nSelect Visual C++.\n\nProject name: kegswin\nProject file location: Select \\win10\\kegs.1.16\\src\n\nSelect Finish\n\nOn the left hand side, the main window has Solution explorer.  Open\nSource Files.  Right click on \"macsnd_driver.c\" and \"xdriver.c\" to\nselect \"Exclude from Project\"\n\nThen, we need to add 3 DLLs.  In the left view area, select \"kegswin\", and\nthen Do Project->Properties.  You should see a dialog \"kegswin Property\nPages\".  If it says anything else, and if the line underneath says stuff\nlike \"Configuration: N/A\" then change view (Solution Explorer is good), and\nmake sure you select kegswin.\n\nSet Configuration: Release, Platform: x64.\nLinker->Input, then on the right, the first item \"Additional Dependencies:\"\nadd: \"wsock32.lib;dsound.lib;winmm.lib;\".  I added it near the end, before\nodbccp32.lib.\n\nSet Platform: x86, and add the same libraries for Win32.\nTo fix \"unresolved external symbol _WinMain@16 referenced in function\",\n\n\nTo compile:\n\nmkkegswinmac -j 20\ncd obj\nstart ./kegswin.vcxproj\n\nThen Build->Build kegswin (Ctrl-b)\n\n"
  },
  {
    "path": "upstream/kegs/lib/make_mac_icon",
    "content": "#!/usr/bin/perl -w\n\n# Based on https://gist.github.com/ansarizafar/6fa64f44aa933794c4d6638eec32b9aa\n# and https://github.com/retifrav/generate-iconset\n# We need to create a directory of the icon in several scaled sizes,\n#  (the Mac command \"sips\" can do this), then run iconutil to form the\n#  .icns file.\n# kegsicon.png created by Alex Lee\n\nmy $icondir;\nmy $img_file = \"\";\nmy $ext = \".png\";\nmy $scale;\nmy $sz;\nmy $pixels;\nmy $scale_str;\n\nif($#ARGV == 0) {\n\t$img_file = shift;\n\tif($img_file =~ /^.*\\.(^\\.*)$/) {\n\t\t$ext = $1;\n\t\tprint \"Set ext to $ext\\n\";\n\t}\n} else {\n\tdie \"Usage: $0 image_file.jpg/.png\"\n}\n\n$icondir = \"./icon.iconset\";\t# Must have .iconset extension\nif(-d $icondir) {\n\t`rm -rf $icondir`;\n}\n\n`mkdir $icondir`;\nfor($scale = 1; $scale <= 2; $scale++) {\n\tfor($sz = 16; $sz <= 512; $sz = $sz * 2) {\n\t\tif($sz == 64) {\n\t\t\tnext;\n\t\t}\n\t\t$pixels = $sz * $scale;\n\t\t$scale_str = \"\";\n\t\tif($scale == 2) {\n\t\t\t$scale_str = '@2x';\n\t\t}\n\t\t@cmd = (\"sips\", \"-z\", $pixels, $pixels, $img_file,\n\t\t\t\"--matchTo\",\n\t\t\t\"/System/Library/ColorSync/Profiles/sRGB\\\\ Profile.icc\",\n\t\t\t\"--out\", $icondir . \"/\" .  \"icon_\" . $sz . \"x\" . $sz .\n\t\t\t$scale_str . $ext);\n\t\tprint \"cmd: @cmd\\n\";\n\t\t`@cmd`;\n\t}\n}\n\nprint \"Calling: iconutil -o kegs.icns -c icns $icondir\";\n`iconutil -o kegs.icns -c icns $icondir`;\n`rm -rf $icondir`;\n\n"
  },
  {
    "path": "upstream/kegs/src/AppDelegate.swift",
    "content": "// $KmKId: AppDelegate.swift,v 1.32 2024-09-15 13:55:35+00 kentd Exp $\n\n//\tCopyright 2019-2024 by Kent Dickey\n//\tThis code is covered by the GNU GPL v3\n//\tSee the file COPYING.txt or https://www.gnu.org/licenses/\n//\n// /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/\n//  Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreAudio.framework/\n//  Versions/A/Headers\n\nimport Cocoa\n\nlet Context_draw = false\n\t// Default: use safe draw function.\n\t// If true, use NSGraphicsContext.current.data to try to write\n\t//  directly to screen memory (in a different ARGB format)\n\nclass Window_info {\n\tvar x_win : NSWindow? = nil\n\tvar mac_view : MainView? = nil\n\tvar kimage_ptr : UnsafeMutablePointer<Kimage>! = nil\n\tvar title : String = \"\"\n\tvar app_delegate : AppDelegate! = nil\n\tvar mac_a2_height : Int = 0\n\n//\tinit(_ new_is_main: Bool) {\n//\t\tis_main = new_is_main\n//\t}\n\n\tfunc set_kimage(_ kimage_ptr : UnsafeMutablePointer<Kimage>!,\n\t\t\t\ttitle: String, delegate: AppDelegate!) {\n\t\tself.kimage_ptr = kimage_ptr\n\t\tself.title = title\n\t\tself.app_delegate = delegate\n\t}\n\n\tfunc create_window() {\n\t\tlet x_xpos = Int(video_get_x_xpos(kimage_ptr))\n\t\tlet x_ypos = Int(video_get_x_ypos(kimage_ptr))\n\t\tlet width = Int(video_get_x_width(kimage_ptr))\n\t\tlet height = Int(video_get_x_height(kimage_ptr))\n\t\tlet windowRect = NSRect(x: x_xpos, y: x_ypos, width: width,\n\t\t\t\t\t\t\theight: height)\n\t\tvar window : NSWindow!\n\t\tvar view : MainView!\n\t\tlet style : NSWindow.StyleMask = [.titled, .closable,\n\t\t\t\t\t\t\t\t.resizable]\n\n\t\twindow = NSWindow(contentRect: windowRect,\n\t\t\tstyleMask: style,\n\t\t\tbacking: .buffered, defer: false)\n\n\t\tlet viewRect = NSRect(x: 0, y: 0, width: windowRect.size.width,\n\t\t\t\t\t\theight: windowRect.size.height)\n\t\tprint(\"About to init MainView\");\n\t\tview = MainView(frame: viewRect)\n\t\tprint(\"About to set kimage_ptr\");\n\t\tview.kimage_ptr = kimage_ptr;\n\t\tprint(\"About to call initialize\");\n\t\tview.initialize()\n\t\tview.closed = false \n\n\t\twindow.delegate = app_delegate\n\t\twindow.contentView = view\n\t\twindow.makeKeyAndOrderFront(app_delegate)\n\t\twindow.acceptsMouseMovedEvents = true\n\t\twindow.title = title\n\t\twindow.showsToolbarButton = true\n\t\twindow.contentAspectRatio = NSSize(width: width, height: height)\n\n\t\tvideo_set_active(kimage_ptr, Int32(1))\n\t\tvideo_update_scale(kimage_ptr, Int32(width), Int32(height),\n\t\t\t\t\t\t\t\tInt32(1))\n\n\t\tx_win = window\n\t\tmac_view = view\n\t\tmac_a2_height = height;\n\t\twindow.makeKey()\n\t}\n\n\tfunc update() {\n\t\t// Decide if window should be opened/closed (if it's the\n\t\t//  debugger window), and call mac_update_display() to update\n\t\tlet new_height = Int(video_get_a2_height(kimage_ptr))\n\t\tlet a2_active = video_get_active(kimage_ptr)\n\t\tif let view = mac_view {\n\t\t\tif(new_height != mac_a2_height) {\n\t\t\t\tmac_resize_window()\n\t\t\t}\n\t\t\tif(a2_active == 0 && !view.closed) {\n\t\t\t\tprint(\"a2_active 0 on \\(title), calling close\")\n\t\t\t\tx_win!.orderOut(x_win)\n\t\t\t\tview.closed = true\n\t\t\t} else if(a2_active != 0 && view.closed) {\n\t\t\t\tprint(\"Opening closed window \\(title)\")\n\t\t\t\tview.closed = false\n\t\t\t\tx_win!.orderFront(x_win)\n\t\t\t\tx_win!.makeKey()\t\t// Move to front\n\t\t\t} else if(a2_active != 0) {\n\t\t\t\tview.mac_update_display()\n\t\t\t}\n\t\t\tif((a2_active != 0) && !view.closed) {\n\t\t\t\tif(adb_get_copy_requested() != 0) {\n\t\t\t\t\tview.do_copy_text(view);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif(a2_active != 0) {\n\t\t\t\tprint(\"Opening window \\(title)\")\n\t\t\t\tcreate_window()\n\t\t\t}\n\t\t}\n\t}\n\tfunc mac_resize_window() {\n\t\tlet a2_height = Int(video_get_a2_height(kimage_ptr))\n\t\tlet a2_width = Int(video_get_a2_width(kimage_ptr))\n\t\tlet ratio = CGFloat(a2_height) / CGFloat(a2_width)\n\n\t\tlet cur_width = x_win!.frame.size.width\n\t\tlet new_height = cur_width * ratio\t// CGFloat\n\t\tvar newframe = x_win!.frame\t\t// NSRect\n\t\tmac_a2_height = a2_height\n\t\tnewframe.size.height = new_height\n\t\tx_win!.contentAspectRatio = NSSize(width: a2_width,\n\t\t\t\t\t\t\theight: a2_height)\n\t\tx_win!.setFrame(newframe, display: true, animate: true)\n\t\tmac_view!.initialize()\n\t\t\t// Must call initialize for the case where the\n\t\t\t//  status lines were enabled, we need more lines\n\t\t// print(\"Call video_update_scale from mac_resize_window\\n\");\n\t\tvideo_update_scale(kimage_ptr, Int32(x_win!.frame.width),\n\t\t\t\t\t\tInt32(x_win!.frame.height), 0)\n\t\tvideo_update_xpos_ypos(kimage_ptr, Int32(x_win!.frame.origin.x),\n\t\t\t\t\t\tInt32(x_win!.frame.origin.y))\n\t\t//print(\"Did mac_resize window to \\(a2_width), \\(a2_height)\" +\n\t\t//\t\"  frame:\\(x_win!.frame.width), \" +\n\t\t//\t\t\t\t\"\\(x_win!.frame.height)\" +\n\t\t//\t\" ratio:\\(ratio)\\n\")\n\t}\n\n\tfunc update_window_size(width: Int, height: Int) {\n\t\t// print(\"Call video_update_scale from update_window_size\\n\");\n\t\tvideo_update_scale(kimage_ptr, Int32(width), Int32(height), 0);\n\t}\n}\n\n@NSApplicationMain\nclass AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {\n\n\tvar mainwin_info = Window_info();\n\tvar debugwin_info = Window_info();\n\n\tfunc find_win_info(_ window: NSWindow) -> Window_info {\n\t\tif(mainwin_info.x_win == window) {\n\t\t\treturn mainwin_info\n\t\t}\n\t\treturn debugwin_info\n\t}\n\t@objc func do_about(_:AnyObject) {\n\t\tprint(\"About\")\n\t\tif let ver_str = Bundle.main.infoDictionary?[\n\t\t\t\t\"CFBundleShortVersionString\"] as? String {\n\t\t\tNSApplication.shared.orderFrontStandardAboutPanel(\n\t\t\t\toptions: [.applicationVersion: ver_str,\n\t\t\t\t\t.version: \"\", .applicationName: \"KEGS\"])\n\t\t}\n\t}\n\tfunc applicationDidFinishLaunching(_ aNotification: Notification) {\n\t\t// This is your first real entry point into the app\n\t\tprint(\"start!\")\n\t\tset_menu_for_kegs()\n\t\tmain_init()\n\t}\n\n\tfunc applicationWillTerminate(_ aNotification: Notification) {\n\t\t// Insert code here to tear down your application\n\t}\n\n\tfunc applicationShouldTerminateAfterLastWindowClosed(\n\t\t\t\t_ theApplication: NSApplication) -> Bool {\n\t\t// Application will close if main window is closed\n\t\treturn true\n\t}\n\tfunc windowDidBecomeKey(_ notification: Notification) {\n\t\tif let w = notification.object as? NSWindow {\n\t\t\tif(w == mainwin_info.x_win) {\n\t\t\t\tadb_mainwin_focus(Int32(1));\n\t\t\t\t//print(\"Main window became KEY\")\n\t\t\t}\n\t\t}\n\t\t//print(\"DidbecomeKey\")\n\t\t// If window focus is changing, turn off key repeat\n\t\tadb_kbd_repeat_off()\n\t}\n\tfunc windowDidResignKey(_ notification: Notification) {\n\t\t//print(\"DidResignKey\")\n\t\tadb_kbd_repeat_off()\n\t\tadb_mainwin_focus(Int32(0))\n\t\tCGDisplayShowCursor(CGMainDisplayID())\n\t}\n\tfunc windowDidMove(_ notification: Notification) {\n\t\t//print(\"DidMove\")\n\t\tif let w = notification.object as? NSWindow {\n\t\t\tlet win_info = find_win_info(w)\n\t\t\tvideo_update_xpos_ypos(win_info.kimage_ptr,\n\t\t\t\tInt32(w.frame.origin.x),\n\t\t\t\tInt32(w.frame.origin.y))\n\t\t}\n\t}\n\n\tfunc windowWillResize(_ window: NSWindow, to frameSize: NSSize)\n\t\t\t\t\t\t\t\t-> NSSize {\n\t\t// print(\"WILL RESIZE app \\(frameSize)\")\n\t\tlet width = Int(frameSize.width)\n\t\tlet height = Int(frameSize.width)\n\t\tlet win_info = find_win_info(window)\n\t\twin_info.update_window_size(width: width, height: height)\n\t\treturn frameSize\n\t}\n\tfunc windowShouldClose(_ window: NSWindow) -> Bool {\n\t\tprint(\"windowShouldClose\")\n\t\tlet win_info = find_win_info(window)\n\t\tif(mainwin_info.x_win == window) {\n\t\t\t// User has clicked the close box on the main emulator\n\t\t\t//  window.  Just exit the app\n\t\t\tNSApp.terminate(window)\n\t\t\treturn true\t\t// Let main window close\n\t\t} else {\n\t\t\tvideo_set_active(win_info.kimage_ptr, Int32(0))\n\t\t\twin_info.update()\n\t\t\treturn false\n\t\t}\n\t}\n\n\tfunc set_menu_for_kegs() {\n\t\tlet appname = \"Kegs\"\n\t\tif let menu = NSApp.mainMenu {\n\t\t\tshow_menu(menu, depth: 0)\n\t\t\tmenu.removeAllItems()\n\n\t\t\tprint(\"Installing my menu now\")\n\t\t\tlet kegs = NSMenu(title: appname)\n\t\t\tkegs.addItem(withTitle: \"About \\(appname)\",\n\t\t\t\taction: #selector(AppDelegate.do_about(_:)),\n\t\t\t\tkeyEquivalent: \"\")\n\t\t\tkegs.addItem(NSMenuItem.separator())\n\t\t\tlet quit_item = NSMenuItem(title: \"Quit \\(appname)\",\n\t\t\t\taction: #selector(NSApplication.terminate(_:)),\n\t\t\t\tkeyEquivalent: \"q\")\n\t\t\tquit_item.keyEquivalentModifierMask = [.option,\n\t\t\t\t\t\t\t\t.command ]\n\t\t\tkegs.addItem(quit_item)\n\t\t\tlet kegs_item = NSMenuItem()\n\t\t\tkegs_item.title = appname\n\t\t\tkegs_item.submenu = kegs\n\t\t\tmenu.addItem(kegs_item)\n\n\t\t\t// First menu of \"Kegs\" now done.  Add \"Edit\" menu\n\t\t\tlet edit = NSMenu(title: \"Edit\")\n\t\t\tedit.addItem(withTitle: \"Copy Text Screen\",\n\t\t\t\taction: #selector(MainView.do_copy_text(_:)),\n\t\t\t\tkeyEquivalent: \"\")\n\t\t\tedit.addItem(NSMenuItem.separator())\n\t\t\tedit.addItem(withTitle: \"Paste\",\n\t\t\t\taction: #selector(MainView.do_paste(_:)),\n\t\t\t\tkeyEquivalent: \"\")\n\t\t\tlet edit_item = NSMenuItem()\n\t\t\tedit_item.title = \"Edit\"\n\t\t\tedit_item.submenu = edit\n\t\t\tmenu.addItem(edit_item)\n\n\t\t\t// Edit menu of \"Kegs\" now done.  Add \"Config\" menu\n\t\t\tlet config = NSMenu(title: \"Config\")\n\t\t\tconfig.addItem(withTitle: \"Configuration  F4\",\n\t\t\t\taction: #selector(MainView.do_config(_:)),\n\t\t\t\tkeyEquivalent: \"\")\n\t\t\tlet config_item = NSMenuItem()\n\t\t\tconfig_item.title = \"Config\"\n\t\t\tconfig_item.submenu = config\n\t\t\tmenu.addItem(config_item)\n\t\t\n\t\t\tshow_menu(menu, depth: 0)\n\t\t}\n\t}\n\n\tfunc show_menu(_ menu: NSMenu, depth: Int) {\n\t\tif(depth >= -10) {\n\t\t\treturn\t// HACK: remove to see debug output!\n\t\t}\n\t\tprint(\"menu at depth \\(depth): \\(menu.title)\")\n\t\tif(depth > 5) {\t\t// Prevent infinite recursion\n\t\t\treturn\n\t\t}\n\t\tfor menuit in menu.items {\n\t\t\tprint(\"menuit: depth:\\(depth) \\(menuit.title)\")\n\t\t\tprint(\"  keyeq:\\(menuit.keyEquivalent)\")\n\t\t\tprint(\"  modifiers:\\(menuit.keyEquivalentModifierMask)\")\n\t\t\tprint(\"  isSeparator:\\(menuit.isSeparatorItem)\")\n\t\t\tif let sub = menuit.submenu {\n\t\t\t\tshow_menu(sub, depth: depth+1)\n\t\t\t}\n\t\t}\n\t}\n\n\tvar mainWindow : NSWindow!\n\tvar mainwin_view : MainView!\n    \n\tfunc main_init() {\n\t\tvar kimage_ptr : UnsafeMutablePointer<Kimage>!\n\t\tvar rect = NSRect.zero\n\n\t\tlet argc = CommandLine.argc\n\t\tlet argv = CommandLine.unsafeArgv\n\t\tparse_argv(argc, argv, 3);\n\n\t\trect.size.width = 2560\n\t\trect.size.height = 1440\n\t\tif let screen = NSScreen.main {\n\t\t\trect = screen.frame\n\t\t}\n\t\tkegs_init(24, Int32(rect.size.width), Int32(rect.size.height),\n\t\t\t\t\t\tInt32(1))\n\t\tif(Context_draw) {\n\t\t\tvideo_set_blue_mask(UInt32(0x0000ff))\n\t\t\tvideo_set_green_mask(UInt32(0x00ff00))\n\t\t\tvideo_set_red_mask(UInt32(0xff0000))\n\t\t} else {\n\t\t\tvideo_set_red_mask(UInt32(0x0000ff))\n\t\t\tvideo_set_green_mask(UInt32(0x00ff00))\n\t\t\tvideo_set_blue_mask(UInt32(0xff0000))\n\t\t}\n\t\tvideo_set_palette()\n\t\tkimage_ptr = video_get_kimage(Int32(0))\n\t\tmainwin_info.set_kimage(kimage_ptr, title: \"KEGS\",\n\t\t\t\t\t\t\tdelegate: self)\n\t\tkimage_ptr = video_get_kimage(Int32(1))\n\t\tdebugwin_info.set_kimage(kimage_ptr, title: \"Debugger\",\n\t\t\t\t\t\t\tdelegate: self)\n\n\t\tmainwin_info.create_window()\n\t\tmain_run_loop()\n\t}\n\n\tfunc main_run_loop() {\n\t\tDispatchQueue.main.asyncAfter(deadline: .now() +\n\t\t\t\t\t\t\t.milliseconds(1)) {\n\t\t\tself.main_run_loop()\n\t\t}\n\t\tlet ret = run_16ms()\n\t\tif(ret != 0) {\n\t\t\texit(ret);\n\t\t}\n\t\tmainwin_info.update()\n\t\tdebugwin_info.update()\n\t}\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>BuildMachineOSBuild</key>\n\t<string>18G103</string>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleExecutable</key>\n\t<string>KEGSMAC</string>\n\t<key>CFBundleIconFile</key>\n\t<string>kegs.icns</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>com.provalid.Kegs</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>Kegs</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.38</string>\n\t<key>CFBundleSupportedPlatforms</key>\n\t<array>\n\t\t<string>MacOSX</string>\n\t</array>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>DTCompiler</key>\n\t<string>com.apple.compilers.llvm.clang.1_0</string>\n\t<key>DTPlatformBuild</key>\n\t<string>11A1027</string>\n\t<key>DTPlatformVersion</key>\n\t<string>GM</string>\n\t<key>DTSDKBuild</key>\n\t<string>19A547</string>\n\t<key>DTSDKName</key>\n\t<string>macosx10.15</string>\n\t<key>DTXcode</key>\n\t<string>1110</string>\n\t<key>DTXcodeBuild</key>\n\t<string>11A1027</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>10.13</string>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>Copyright © 2025 Kent Dickey. All rights reserved.</string>\n\t<key>NSHighResolutionCapable</key>\n\t<true/>\n\t<key>NSMainNibFile</key>\n\t<string>MainMenu</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "upstream/kegs/src/Kegs-Bridging-Header.h",
    "content": "// $KmKId: Kegs-Bridging-Header.h,v 1.1 2019-10-14 22:33:09+00 kentd Exp $\n//  Use this file to import your target's public headers that you would like to expose to Swift.\n//\n\n#import \"defc.h\"\n"
  },
  {
    "path": "upstream/kegs/src/MainView.swift",
    "content": "// $KmKId: MainView.swift,v 1.42 2024-09-15 13:55:35+00 kentd Exp $\n\n//\tCopyright 2019-2024 by Kent Dickey\n//\tThis code is covered by the GNU GPL v3\n//\tSee the file COPYING.txt or https://www.gnu.org/licenses/\n//\n\nimport Cocoa\nimport CoreGraphics\nimport AudioToolbox\n\nclass MainView: NSView {\n\n\tvar bitmapContext : CGContext!\n\tvar bitmapData : UnsafeMutableRawPointer!\n\tvar rawData : UnsafeMutablePointer<UInt32>!\n\tvar current_flags : UInt = 0\n\tvar mouse_moved : Bool = false\n\tvar mac_warp_pointer : Int32 = 0\n\tvar mac_hide_pointer : Int32 = 0\n\tvar kimage_ptr : UnsafeMutablePointer<Kimage>!\n\tvar closed : Bool = false\n\tvar pixels_per_line = 640\n\tvar max_height = 600\n\n\tlet is_cmd = UInt(NSEvent.ModifierFlags.command.rawValue)\n\tlet is_control = UInt(NSEvent.ModifierFlags.control.rawValue)\n\tlet is_shift = NSEvent.ModifierFlags.shift.rawValue\n\tlet is_capslock = NSEvent.ModifierFlags.capsLock.rawValue\n\tlet is_option = NSEvent.ModifierFlags.option.rawValue\n\n\n\toverride init(frame frameRect: NSRect) {\n\t\tsuper.init(frame: frameRect)\n\t}\n\n\trequired init?(coder: NSCoder) {\n\t\tsuper.init(coder: coder)\n\t}\n\n\tfunc windowDidResize(_ notification: Notification) {\n\t\tprint(\"DID RESIZE\")\n\t}\n\toverride func performKeyEquivalent(with event: NSEvent) -> Bool {\n\t\tlet keycode = event.keyCode\n\t\tlet is_repeat = event.isARepeat\n\t\tlet unicode_key = get_unicode_key_from_event(event)\n\t\t// print(\".performKeyEquiv keycode: \\(keycode), is_repeat: \" +\n\t\t//\t\t\t\t\t\"\\(is_repeat)\")\n\t\tif(((current_flags & is_cmd) == 0) || is_repeat) {\n\t\t\t// If CMD isn't being held down, just ignore this\n\t\t\treturn false\n\t\t}\n\t\t// Otherwise, manually do down, then up, for this key\n\t\tadb_physical_key_update(kimage_ptr, Int32(keycode),\n\t\t\tUInt32(unicode_key), 0);\n\t\tadb_physical_key_update(kimage_ptr, Int32(keycode),\n\t\t\tUInt32(unicode_key), 1);\n\t\treturn true\n\t}\n\n\toverride var acceptsFirstResponder: Bool {\n\t\treturn true\n\t}\n\n\tfunc get_unicode_key_from_event(_ event: NSEvent) -> UInt32 {\n\t\tvar unicode_key = UInt32(0);\n\t\tif let str = event.charactersIgnoringModifiers {\n\t\t\t//print(\" keydown unmod str: \\(str), \" +\n\t\t\t//\t\t\"code:\\(event.keyCode)\")\n\t\t\tlet arr_chars = Array(str.unicodeScalars)\n\t\t\t//print(\" arr_chars: \\(arr_chars)\")\n\t\t\tif(arr_chars.count == 1) {\n\t\t\t\tunicode_key = UInt32(arr_chars[0]);\n\t\t\t\t//print(\"key1:\\(unicode_key)\")\n\t\t\t}\n\t\t}\n\t\treturn unicode_key\n\t}\n\toverride func keyDown(with event: NSEvent) {\n\t\tlet keycode = event.keyCode\n\t\tlet is_repeat = event.isARepeat;\n\t\t// print(\".keyDown code: \\(keycode), repeat: \\(is_repeat)\")\n\t\t//if let str = event.characters {\n\t\t//\tprint(\" keydown str: \\(str), code:\\(keycode)\")\n\t\t//}\n\t\tlet unicode_key = get_unicode_key_from_event(event)\n\t\tif(is_repeat) {\n\t\t\t// If we do CMD-E, then we never get a down for the E,\n\t\t\t//  but we will get repeat events for that E.  Let's\n\t\t\t//  ignore those\n\t\t\treturn\n\t\t}\n\t\tadb_physical_key_update(kimage_ptr, Int32(keycode),\n\t\t\tUInt32(unicode_key), 0);\n\t}\n\n\toverride func keyUp(with event: NSEvent) {\n\t\tlet keycode = event.keyCode\n\t\t// let is_repeat = event.isARepeat;\n\t\t// print(\".keyUp code: \\(keycode), repeat: \\(is_repeat)\")\n\t\tlet unicode_key = get_unicode_key_from_event(event)\n\t\tadb_physical_key_update(kimage_ptr, Int32(keycode),\n\t\t\tUInt32(unicode_key), 1);\n\t}\n\n\toverride func flagsChanged(with event: NSEvent) {\n\t\tlet flags = event.modifierFlags.rawValue &\n\t\t\t\t(is_cmd | is_control | is_shift | is_capslock |\n\t\t\t\t\t\t\t\tis_option)\n\t\tvar c025_val = UInt32(0);\n\t\tif((flags & is_shift) != 0) {\n\t\t\tc025_val |= 1;\n\t\t}\n\t\tif((flags & is_control) != 0) {\n\t\t\tc025_val |= 2;\n\t\t}\n\t\tif((flags & is_capslock) != 0) {\n\t\t\tc025_val |= 4;\n\t\t}\n\t\tif((flags & is_option) != 0) {\n\t\t\tc025_val |= 0x40;\n\t\t}\n\t\tif((flags & is_cmd) != 0) {\n\t\t\tc025_val |= 0x80;\n\t\t}\n\t\tadb_update_c025_mask(kimage_ptr, c025_val, UInt32(0xc7));\n\t\tcurrent_flags = flags\n\t\t//print(\"flagsChanged: \\(flags) and keycode: \\(event.keyCode)\")\n\t}\n\toverride func acceptsFirstMouse(for event: NSEvent?) -> Bool {\n\t\t// This is to let the first click when changing to this window\n\t\t//  through to the app, I probably don't want this.\n\t\treturn false\n\t}\n\toverride func mouseMoved(with event: NSEvent) {\n\t\t//let type = event.eventNumber\n\t\t//print(\" event type: \\(type)\")\n\t\tmac_update_mouse(event: event, buttons_state:0, buttons_valid:0)\n\n\t}\n\toverride func mouseDragged(with event: NSEvent) {\n\t\tmac_update_mouse(event: event, buttons_state: 0,\n\t\t\t\t\t\t\tbuttons_valid: 0)\n\t}\n\toverride func otherMouseDown(with event: NSEvent) {\n\t\tmac_update_mouse(event: event, buttons_state:2, buttons_valid:2)\n\t}\n\toverride func otherMouseUp(with event: NSEvent) {\n\t\tmac_update_mouse(event: event, buttons_state:0, buttons_valid:2)\n\t}\n\n\toverride func mouseEntered(with event: NSEvent) {\n\t\tprint(\"mouse entered\")\n\t}\n\toverride func rightMouseUp(with event: NSEvent) {\n\t\tmac_update_mouse(event: event, buttons_state:0, buttons_valid:4)\n\t}\n\toverride func rightMouseDown(with event: NSEvent) {\n\t\tprint(\"Right mouse down\")\n\t\tmac_update_mouse(event: event, buttons_state:4, buttons_valid:4)\n\t}\n\toverride func mouseUp(with event: NSEvent) {\n\t\t// print(\"Mouse up \\(event.locationInWindow.x),\" +\n\t\t//\t\t\t\t\"\\(event.locationInWindow.y)\")\n\t\tmac_update_mouse(event: event, buttons_state:0, buttons_valid:1)\n\t}\n\toverride func mouseDown(with event: NSEvent) {\n\t\t//print(\"Mouse down \\(event.locationInWindow.x),\" +\n\t\t//\t\t\t\t\"\\(event.locationInWindow.y)\")\n\t\tmac_update_mouse(event: event, buttons_state:1, buttons_valid:1)\n\t}\n\n\tfunc mac_update_mouse(event: NSEvent, buttons_state: Int,\n\t\t\t\t\t\t\tbuttons_valid: Int) {\n\t\tvar warp = Int32(0)\n\t\tvar x_width = 0\n\t\tvar y_height = 0\n\t\tvar x = Int32(0)\n\t\tvar y = Int32(0)\n\t\tvar do_delta = Int(0)\n\t\tlet hide = adb_get_hide_warp_info(kimage_ptr, &warp)\n\t\tif(warp != mac_warp_pointer) {\n\t\t\tmouse_moved = true\n\t\t}\n\t\tmac_warp_pointer = warp\n\t\tif(mac_hide_pointer != hide) {\n\t\t\tmac_hide_pointer(hide: hide)\n\t\t}\n\t\tmac_hide_pointer = hide\n\t\tlet location = event.locationInWindow\n\t\tif(!Context_draw) {\n\t\t\t// We're letting the Mac scale the window for us,\n\t\t\t//  so video_scale_mouse*() doesn't know the scale\n\t\t\t//  factor, so pass it in\n\t\t\tx_width = Int(bounds.size.width);\n\t\t\ty_height = Int(bounds.size.height);\n\t\t}\n\t\tlet raw_x = location.x\n\t\tlet raw_y = bounds.size.height - 1 - location.y\n\t\t\t// raw_y is 0 at the top of the window now\n\t\tx = video_scale_mouse_x(kimage_ptr, Int32(raw_x),\n\t\t\t\t\t\tInt32(x_width))\n\t\ty = video_scale_mouse_y(kimage_ptr, Int32(raw_y),\n\t\t\t\t\t\tInt32(y_height))\n\t\tdo_delta = 0;\n\t\tif(mac_warp_pointer != 0) {\n\t\t\tdo_delta |= 0x1000;\t// x,y are deltas\n\t\t\tx = Int32(event.deltaX)\n\t\t\ty = Int32(event.deltaY)\n\t\t}\n\t\tlet ret = adb_update_mouse(kimage_ptr, x, y,\n\t\t\t\tInt32(buttons_state),\n\t\t\t\tInt32(buttons_valid | do_delta))\n\t\tif(ret != 0) {\n\t\t\tmouse_moved = true\n\t\t}\n\t\tguard let win = window else {\n\t\t\treturn\t\t\t// No valid window\n\t\t}\n\t\tif(mouse_moved) {\n\t\t\tvar rect1 = NSRect.zero\n\n\t\t\t// If warp, warp cursor to middle of window.  Moving\n\t\t\t//  the cursor requires absolute screen coordinates,\n\t\t\t//  where y=0 is the top of the screen.  We must convert\n\t\t\t//  window coords (where y=0 is the bottom of the win).\n\t\t\tmouse_moved = false\n\t\t\tlet warp_x = CGFloat(video_unscale_mouse_x(kimage_ptr,\n\t\t\t\tInt32(A2_WINDOW_WIDTH/2), Int32(x_width)))\n\t\t\tlet warp_y = CGFloat(video_unscale_mouse_y(kimage_ptr,\n\t\t\t\tInt32(A2_WINDOW_HEIGHT/2), Int32(y_height)))\n\t\t\tlet scr_height = CGDisplayPixelsHigh(CGMainDisplayID());\n\t\t\trect1.origin.x = CGFloat(warp_x)\n\t\t\trect1.origin.y = bounds.size.height - 1 -\n\t\t\t\t\t\t\t\tCGFloat(warp_y)\n\t\t\trect1.size.width = 1;\n\t\t\trect1.size.height = 0;\n\t\t\tlet screen_rect = win.convertToScreen(rect1)\n\t\t\tlet screen_rect_y = CGFloat(scr_height) -\n\t\t\t\t\t\t\tscreen_rect.origin.y\n\t\t\tlet cg_loc = CGPoint(x: screen_rect.origin.x,\n\t\t\t\ty: CGFloat(screen_rect_y))\n\t\t\t//print(\"scr_rect:\\(screen_rect)\")\n\t\t\tif(warp != 0) {\n\t\t\t\t// Warp to middle of the window\n\t\t\t\tCGDisplayMoveCursorToPoint(CGMainDisplayID(),\n\t\t\t\t\t\t\t\tcg_loc);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunc mac_hide_pointer(hide: Int32) {\n\t\t// print(\"Hide called: \\(hide)\")\n\t\tif(hide != 0) {\n\t\t\tCGDisplayHideCursor(CGMainDisplayID())\n\t\t} else {\n\t\t\tCGDisplayShowCursor(CGMainDisplayID())\n\t\t}\n\t}\n\tfunc initialize() {\n\t\t//let colorSpace = CGColorSpace(name: CGColorSpace.sRGB)\n\t\tprint(\"Initialize view called\")\n\t\t// Get width,height from video.c to handle toggling status lines\n\t\tlet width = Int(video_get_a2_width(kimage_ptr))\n\t\tlet height = Int(video_get_a2_height(kimage_ptr))\n\t\t//if let screen = NSScreen.main {\n\t\t//\tlet rect = screen.frame\n\t\t//\twidth = Int(rect.size.width)\n\t\t//\theight = Int(rect.size.height)\n\t\t//}\n\t\tpixels_per_line = width\n\t\tmax_height = height\n\t\t//print(\"pixels_per_line: \\(pixels_per_line), \" +\n\t\t//\t\t\"max_height: \\(max_height)\")\n\n\t\tlet color_space = CGDisplayCopyColorSpace(CGMainDisplayID())\n\t\t//let colorSpace = CGColorSpaceCreateDeviceRGB()\n\t\tbitmapContext = CGContext(data: nil, width: width,\n\t\t\theight: height, bitsPerComponent: 8,\n\t\t\tbytesPerRow: width * 4,\n\t\t\tspace: color_space,\n\t\t\tbitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue)\n\n\t\t//CGImageAlphaInfo.noneSkipLast.rawValue\n\t\tbitmapData = bitmapContext.data!\n\t\t// Set the intial value of the data to black (0)\n\t\tbitmapData.initializeMemory(as: UInt32.self, repeating: 0,\n\t\t\t\t\t\tcount: height * width)\n\t\trawData = bitmapData.bindMemory(to: UInt32.self,\n\t\t\t\t\t\tcapacity: height * width)\n\t\t//print(\"Calling video_update_scale from MainViewinitialize()\")\n\t\tlet x_width = Int(video_get_x_width(kimage_ptr))\n\t\tlet x_height = Int(video_get_x_height(kimage_ptr))\n\t\tvideo_update_scale(kimage_ptr, Int32(x_width), Int32(x_height),\n\t\t\t\t\t\t\t\tInt32(1))\n\t\tif(Context_draw) {\n\t\t\tvideo_set_alpha_mask(UInt32(0xff000000))\n\t\t\t// Set video.c alpha mask, since 0 means transparent\n\t\t}\n\t}\n\n\toverride func draw(_ dirtyRect: NSRect) {\n\t\tvar rect : Change_rect\n\t\t//super.draw(dirtyRect)\n\t\t\t// Documentation says super.draw not needed...\n\t\t// Draw the current image buffer to the screen\n\t\tlet context = NSGraphicsContext.current!.cgContext\n\t\tif(!Context_draw) {\n\t\t\t// Safe, simple drawing\n\t\t\tlet image = bitmapContext.makeImage()!\n\t\t\tcontext.draw(image, in: bounds)\n\t\t\t\t// The above call does the actual copy of\n\t\t\t\t//  data to the screen, and can take a while\n\t\t\t//print(\"Draw, bounds:\\(bounds), dirtyr:\\(dirtyRect)\")\n\t\t} else {\n\t\t\t// Unsafe, more direct drawing by peeking into\n\t\t\t// NSGraphicsContext.current.data\n\t\t\tif let data = context.data {\n\t\t\t\trect = Change_rect(x:0, y:0,\n\t\t\t\t\twidth:Int32(context.width),\n\t\t\t\t\theight:Int32(context.height));\n\t\t\t\tvideo_update_scale(kimage_ptr,\n\t\t\t\t\tInt32(context.width),\n\t\t\t\t\tInt32(context.height), Int32(0))\n\t\t\t\tvideo_out_data_scaled(data, kimage_ptr,\n\t\t\t\t\tInt32(context.bytesPerRow/4), &rect);\n\t\t\t\tvideo_out_done(kimage_ptr);\n\t\t\t\tprint(\"Did out_data_scaled, rect:\\(rect)\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@objc func do_config(_ : AnyObject) {\n\t\t// Create a \"virtual\" F4 press\n\t\t//print(\"do_config\")\n\t\t// Create a keydown for the F4 key (keycode:0x76)\n\t\tadb_physical_key_update(kimage_ptr, Int32(0x76), 0, 0);\n\n\t\t// and create a keyup for the F4 key (keycode:0x76)\n\t\tadb_physical_key_update(kimage_ptr, Int32(0x76), 0, 1);\n\t}\n\n\t@objc func do_copy_text(_ : AnyObject) {\n\t\t// print(\"do_copy\");\n\t\t//let text_buf_cstr = UnsafeMutablePointer<Int8>.allocate(\n\t\t//\t\t\t\tcapacity: 2100);\n\t\tif let cstr = cfg_text_screen_str() {\n\t\t\tlet str = String(cString: cstr);\n\t\t\tNSPasteboard.general.clearContents();\n\t\t\tNSPasteboard.general.setString(str,\n\t\t\t\tforType: NSPasteboard.PasteboardType.string);\n\t\t}\n\t}\n\t@objc func do_paste(_ : AnyObject) {\n\t\t// print(\"do_paste\")\n\t\tlet general = NSPasteboard.general;\n\t\tguard let str = general.string(forType: .string) else {\n\t\t\tprint(\"Cannot paste, nothing in clipboard\");\n\t\t\treturn\n\t\t}\n\t\t//print(\"str: \\(str)\")\n\t\tfor raw_c in str.utf8 {\n\t\t\tlet c = UInt32(raw_c)\n\t\t\tlet ret = adb_paste_add_buf(c)\n\t\t\tif(ret != 0) {\n\t\t\t\tprint(\"Paste too large!\")\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tfunc mac_update_display() {\n\t\tvar valid : Int32\n\t\tvar rect : Change_rect\n\t\tvar dirty_rect = NSRect.zero\n\n\t\tif(Context_draw) {\n\t\t\t// We just need to know if there are any changes,\n\t\t\t//  don't actually do the copies now\n\t\t\tvalid = video_out_query(kimage_ptr);\n\t\t\tif(valid != 0) {\n\t\t\t\tself.setNeedsDisplay(bounds)\n\t\t\t\tprint(\"Needs display\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Otherwise, update rawData in our Bitmap now\n\t\trect = Change_rect(x:0, y:0, width:0, height:0)\n\t\tfor i in 0...MAX_CHANGE_RECTS {\t\t// MAX_CHANGE_RECTS\n\t\t\tvalid = video_out_data(rawData, kimage_ptr,\n\t\t\t\tInt32(pixels_per_line), &rect, Int32(i))\n\t\t\tif(valid == 0) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tdirty_rect.origin.x = CGFloat(rect.x)\n\t\t\tdirty_rect.origin.y = bounds.size.height -\n\t\t\t\t\tCGFloat(rect.y) - CGFloat(rect.height)\n\t\t\tdirty_rect.size.width = CGFloat(rect.width)\n\t\t\tdirty_rect.size.height = CGFloat(rect.height)\n\n\t\t\tself.setNeedsDisplay(bounds)\n\t\t\t\t// It's necessary to redraw the whole screen,\n\t\t\t\t//  there's no mechanism to just redraw part\n\t\t\t\t// The coordinates would need transformation\n\t\t\t\t//  (since Mac 0,0 is the lower left corner)\n\t\t\t//print(\"bounds: w:\\(bounds.size.width) \" +\n\t\t\t//\t\t\t\"h:\\(bounds.size.height)\\n\")\n\t\t\t//self.setNeedsDisplay(dirty_rect)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "upstream/kegs/src/Makefile",
    "content": "# $KmKId: makefile,v 1.48 2025-04-28 15:12:19+00 kentd Exp $\n\nXOPTS_WIN = -Wall -fomit-frame-pointer -march=pentium\n\nSWIFTOBJS = AppDelegate.o MainView.o\n\ninclude vars\ninclude ldvars\n\n.SUFFIXES: .dep .proto\n\nAS = $(CC)\n\nXLIBS = -L/usr/X11/lib\nPERL = perl\n\nall: $(TARGET)\n\nspecials:\n\nspecials_clean:\n\nclean:\n\trm -f *.o kegsmac\n\n\n# Mac builds:\nkegsmac: $(OBJECTS) $(OBJECTS1) compile_time.o $(SWIFTOBJS)\n\tclang $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) $(SWIFTOBJS) \\\n\t\tcompile_time.o $(LDFLAGS) -o kegsmac $(EXTRA_LIBS) \\\n\t\t-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \\\n\t\t-Xlinker -rpath -Xlinker @executable_path/../Frameworks \\\n\t\t-Xlinker -rpath -Xlinker /usr/lib/swift \\\n\t\t-Xlinker -no_deduplicate -fobjc-link-runtime \\\n\t\t-L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx \\\n\t\t-L/usr/lib/swift\n\tmkdir -p ../KEGSMAC.app/Contents/Resources/Base.lproj/\n\tmkdir -p ../KEGSMAC.app/Contents/MacOS\n\tmkdir -p ../KEGSMAC.app/Contents/Frameworks\n\t$(PERL) cp_kegs_libs kegsmac /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx ../KEGSMAC.app/Contents/Frameworks\n\tmv kegsmac ../KEGSMAC.app/Contents/MacOS/KEGSMAC\n\techo \"APPL????\" > ../KEGSMAC.app/Contents/PkgInfo\n\tcp -f Info.plist ../KEGSMAC.app/Contents/\n\tcp -f $(PROJROOT)/lib/MainMenu.nib ../KEGSMAC.app/Contents/Resources/Base.lproj/\n\t$(PROJROOT)/lib/make_mac_icon $(PROJROOT)/lib/kegsicon.png\n\tcp -f kegs.icns ../KEGSMAC.app/Contents/Resources/\n\ttouch '../KEGSMAC.app/Icon?'\n\t#cp -f $(PROJROOT)/lib/2mg.icns ../KEGSMAC.app/Contents/Resources/\n\t#cp -f $(PROJROOT)/lib/525.icns ../KEGSMAC.app/Contents/Resources/\n\n\n# Linux for X builds:\nxkegs: $(OBJECTS) $(OBJECTS1) compile_time.o\n\t$(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) compile_time.o \\\n\t\t$(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) \\\n\t\t-lX11 -lXext\n\tmv xkegs ..\n\n# Cygwin for X builds:\nkegs.exe: $(OBJECTS) $(OBJECTS1) compile_time.o\n\t$(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) compile_time.o \\\n\t\t$(LDFLAGS) -o $(NAME)$(SUFFIX) $(XLIBS) $(EXTRA_LIBS) \\\n\t\t-lXext -lX11 -lm\n\tmv kegs.exe ..\n\n# Mingw32 (native windows) builds: (broken, doesn't work currently)\nkegswin.exe: $(OBJECTS) $(OBJECTS1) compile_time.o\n\t$(CC) $(CCOPTS) $(LDOPTS) $(OBJECTS) $(OBJECTS1) compile_time.o \\\n\t\t$(LDFLAGS) -o $(NAME)$(SUFFIX) $(EXTRA_LIBS) \\\n\t\t-lwinmm -lgdi32 -ldsound -lcomctl32 -lws2_32\n\tmv $(NAME)$(SUFFIX) ..\n\n\n.s.o:\n\t$(AS) -c $(OPTS) -I. $*.s\n\n.c.o:\n\t$(CC) $(CCOPTS) $(XOPTS) -c $(OPTS) -I. $*.c\n\n.m.o:\n\t$(CC) $(CCOPTS) $(XOPTS) -c $(OPTS) -I. $*.m\n\nAppDelegate.o: AppDelegate.swift\n\tsh ./comp_swift -c $(OPTS) -I. -primary-file $*.swift \\\n\t\tMainView.swift -o $*.o\n\nMainView.o: MainView.swift\n\tsh ./comp_swift -c $(OPTS) -I. -primary-file $*.swift \\\n\t\tAppDelegate.swift -o $*.o\n\nwin32.o: win32.rc\n\twindres -o win32.o win32.rc\n\n.c.proto:\n\t$(KMKROOT)/bin/kmkproto $(XOPTS) $*.c > $*.proto\n\techo >> $*.proto\n\n.m.proto:\n\t$(KMKROOT)/bin/kmkproto $(XOPTS) $*.m > $*.proto\n\techo >> $*.proto\n\n$(PROTO_OUT): $(PROTO_FILE_LIST) ldvars proto_vars\n\t$(KMKROOT)/bin/kmkproto_head $(PROTO_OUT) tmp_protos.h\n\tcat /dev/null $(PROTO_FILE_LIST) >> tmp_protos.h\n\trm -f $(PROTO_OUT)\n\tmv tmp_protos.h $(PROTO_OUT)\n\tkmk_cp_if_diff $(PROTO_OUT) ..\n\trm -f *.proto\n\n\ncompile_time.o: $(OBJECTS) $(OBJECTS1)\n\ninclude dependency\n\n"
  },
  {
    "path": "upstream/kegs/src/adb.c",
    "content": "const char rcsid_adb_c[] = \"@(#)$KmKId: adb.c,v 1.116 2024-09-15 13:56:12+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2024 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n/* adb_mode bit 3 and bit 2 (faster repeats for arrows and space/del) not done*/\n\n#include \"defc.h\"\n\nextern int Verbose;\nextern word32 g_vbl_count;\nextern int g_num_lines_prev_superhires640;\nextern int g_num_lines_prev_superhires;\nextern int g_rom_version;\nextern int g_fast_disk_emul_en;\nextern int g_limit_speed;\nextern int g_irq_pending;\nextern int g_swap_paddles;\nextern int g_invert_paddles;\nextern int g_joystick_type;\nextern int g_config_control_panel;\nextern int g_status_enable;\nextern dword64 g_cur_dfcyc;\n\nextern byte *g_slow_memory_ptr;\nextern byte *g_memory_ptr;\nextern word32 g_mem_size_total;\nextern Kimage g_mainwin_kimage;\nextern Kimage g_debugwin_kimage;\n\nenum {\n\tADB_IDLE = 0,\n\tADB_IN_CMD,\n\tADB_SENDING_DATA,\n};\n\n#define ADB_C027_MOUSE_DATA\t0x80\n#define ADB_C027_MOUSE_INT\t0x40\n#define ADB_C027_DATA_VALID\t0x20\n#define ADB_C027_DATA_INT\t0x10\n#define ADB_C027_KBD_VALID\t0x08\n#define ADB_C027_KBD_INT\t0x04\n#define ADB_C027_MOUSE_COORD\t0x02\n#define ADB_C027_CMD_FULL\t0x01\n\n#define ADB_C027_NEG_MASK\t( ~ (\t\t\t\t\\\n\t\tADB_C027_MOUSE_DATA | ADB_C027_DATA_VALID |\t\\\n\t\tADB_C027_KBD_VALID | ADB_C027_MOUSE_COORD |\t\\\n\t\tADB_C027_CMD_FULL))\n\n\nint halt_on_all_c027 = 0;\n\nword32\tg_adb_repeat_delay = 45;\nword32\tg_adb_repeat_rate = 3;\nword32\tg_adb_repeat_info = 0x23;\nword32\tg_adb_char_set = 0x0;\nword32\tg_adb_layout_lang = 0x0;\n\nword32\tg_adb_interrupt_byte = 0;\nint\tg_adb_state = ADB_IDLE;\n\nword32\tg_adb_cmd = (word32)-1;\nint\tg_adb_cmd_len = 0;\nint\tg_adb_cmd_so_far = 0;\nword32\tg_adb_cmd_data[16];\n\n#define MAX_ADB_DATA_PEND\t16\n\nword32\tg_adb_data[MAX_ADB_DATA_PEND];\nint\tg_adb_data_pending = 0;\n\nword32\tg_c027_val = 0;\nword32\tg_c025_val = 0;\n\nbyte\tadb_memory[256];\n\nword32 g_adb_mode = 0;\t\t/* mode set via set_modes, clear_modes */\n\nint g_warp_pointer = 0;\nint g_hide_pointer = 0;\nint g_unhide_pointer = 0;\nint\tg_adb_copy_requested = 0;\n\nint g_mouse_a2_x = 0;\nint g_mouse_a2_y = 0;\nint g_mouse_a2_button = 0;\nint g_mouse_fifo_pos = 0;\nint g_mouse_raw_x = 0;\nint g_mouse_raw_y = 0;\n\n#define ADB_MOUSE_FIFO\t\t8\n\nSTRUCT(Mouse_fifo) {\n\tdword64\tdfcyc;\n\tint\tx;\n\tint\ty;\n\tint\tbuttons;\n};\n\nMouse_fifo g_mouse_fifo[ADB_MOUSE_FIFO] = { { 0, 0, 0, 0 } };\n\nint\tg_adb_mouse_valid_data = 0;\nint\tg_adb_mouse_coord = 0;\n\n#define MAX_KBD_BUF\t\t8\n#define MAX_KBD_PASTE_BUF\t32768\n\nint\tg_adb_mainwin_has_focus = 1;\n#if defined(__linux__) || defined(_WIN32)\nint\tg_adb_swap_command_option = 1;\t\t// Default to swap on Linux/Win\n#else\nint\tg_adb_swap_command_option = 0;\n#endif\nint\tg_key_down = 0;\nint\tg_hard_key_down = 0;\nint\tg_a2code_down = 0;\nint\tg_kbd_read_no_update = 0;\nint\tg_kbd_chars_buffered = 0;\nint\tg_kbd_buf[MAX_KBD_BUF];\nint\tg_kbd_paste_rd_pos = 0;\nint\tg_kbd_paste_wr_pos = 0;\nbyte\tg_kbd_paste_buf[MAX_KBD_PASTE_BUF];\nword32\tg_kbd_paste_last_key = 0;\nword32\tg_adb_repeat_vbl = 0;\n\nint\tg_kbd_dev_addr = 2;\t\t/* ADB physical kbd addr */\nint\tg_mouse_dev_addr = 3;\t\t/* ADB physical mouse addr */\n\nint\tg_kbd_ctl_addr = 2;\t\t/* ADB microcontroller's kbd addr */\nint\tg_mouse_ctl_addr = 3;\t\t/* ADB ucontroller's mouse addr*/\n\t\t\t/* above are ucontroller's VIEW of where mouse/kbd */\n\t\t\t/*  are...if they are moved, mouse/keyboard funcs */\n\t\t\t/*  should stop (c025, c000, c024, etc). */\n\nword32\tg_virtual_key_up[4];\t/* bitmask of all possible 128 a2codes */\n\t\t\t\t/* indicates which keys are up=1 by bit */\nint\tg_rawa2_to_a2code[128];\n\nint\tg_keypad_key_is_down[10] = { 0 };/* List from 0-9 of which keypad */\n\t\t\t\t\t/*  keys are currently pressed */\n\n\n#define SHIFT_DOWN\t( (g_c025_val & 0x01) )\n#define CTRL_DOWN\t( (g_c025_val & 0x02) )\n#define CAPS_LOCK_DOWN\t( (g_c025_val & 0x04) )\n#define OPTION_DOWN\t( (g_c025_val & 0x40) )\n#define CMD_DOWN\t( (g_c025_val & 0x80) )\n\n\n#define MAX_ADB_KBD_REG3\t16\n\nint g_kbd_reg0_pos = 0;\nint g_kbd_reg0_data[MAX_ADB_KBD_REG3];\nint g_kbd_reg3_16bit = 0x602;\t\t\t/* also set in adb_reset()! */\n\nint\tg_adb_init = 0;\n\n/* Format: a2code, ascii if no shift, ascii if shift, ascii if ctl */\nconst int g_a2_key_to_ascii[][4] = {\n\t{ 0x00,\t'a',\t'A',\t0x01 },\n\t{ 0x01,\t's',\t'S',\t0x13 },\n\t{ 0x02,\t'd',\t'D',\t0x04 },\n\t{ 0x03,\t'f',\t'F',\t0x06 },\n\t{ 0x04,\t'h',\t'H',\t0x08 },\n\t{ 0x05,\t'g',\t'G',\t0x07 },\n\t{ 0x06,\t'z',\t'Z',\t0x1a },\n\t{ 0x07,\t'x',\t'X',\t0x18 },\n\n\t{ 0x08,\t'c',\t'C',\t0x03 },\n\t{ 0x09,\t'v',\t'V',\t0x16 },\n\t{ 0x0a, -1, -1, -1 },\n\t{ 0x0b,\t'b',\t'B',\t0x02 },\n\t{ 0x0c,\t'q',\t'Q',\t0x11 },\n\t{ 0x0d,\t'w',\t'W',\t0x17 },\n\t{ 0x0e,\t'e',\t'E',\t0x05 },\n\t{ 0x0f,\t'r',\t'R',\t0x12 },\n\n\t{ 0x10,\t'y',\t'Y',\t0x19 },\n\t{ 0x11,\t't',\t'T',\t0x14 },\n\t{ 0x12,\t'1',\t'!',\t-1 },\n\t{ 0x13,\t'2',\t'@',\t0x00 },\n\t{ 0x14,\t'3',\t'#',\t-1 },\n\t{ 0x15,\t'4',\t'$',\t-1 },\n\t{ 0x16,\t'6',\t'^',\t0x1e },\n\t{ 0x17,\t'5',\t'%',\t-1 },\n\n\t{ 0x18,\t'=',\t'+',\t-1 },\n\t{ 0x19,\t'9',\t'(',\t-1 },\n\t{ 0x1a,\t'7',\t'&',\t-1 },\n\t{ 0x1b,\t'-',\t'_',\t0x1f },\n\t{ 0x1c,\t'8',\t'*',\t-1 },\n\t{ 0x1d,\t'0',\t')',\t-1 },\n\t{ 0x1e,\t']',\t'}',\t0x1d },\n\t{ 0x1f,\t'o',\t'O',\t0x0f },\n\n\t{ 0x20,\t'u',\t'U',\t0x15 },\n\t{ 0x21,\t'[',\t'{',\t0x1b },\n\t{ 0x22,\t'i',\t'I',\t0x09 },\n\t{ 0x23,\t'p',\t'P',\t0x10 },\n\t{ 0x24,\t0x0d,\t0x0d,\t-1 },\t/* return */\n\t{ 0x25,\t'l',\t'L',\t0x0c },\n\t{ 0x26,\t'j',\t'J',\t0x0a },\n\t{ 0x27,\t0x27,\t'\"',\t-1 },\t/* single quote */\n\n\t{ 0x28,\t'k',\t'K',\t0x0b },\n\t{ 0x29,\t';',\t':',\t-1 },\n\t{ 0x2a,\t0x5c,\t'|',\t0x1c },\t/* \\, | */\n\t{ 0x2b,\t',',\t'<',\t-1 },\n\t{ 0x2c,\t'/',\t'?',\t0x7f },\n\t{ 0x2d,\t'n',\t'N',\t0x0e },\n\t{ 0x2e,\t'm',\t'M',\t0x0d },\n\t{ 0x2f,\t'.',\t'>',\t-1 },\n\n\t{ 0x30,\t0x09,\t0x09,\t-1 },\t/* tab */\n\t{ 0x31,\t' ',\t' ',\t-1 },\n\t{ 0x32,\t'`',\t'~',\t-1 },\n\t{ 0x33,\t0x7f,\t0x7f,\t-1 },\t/* Delete */\n\t{ 0x34, -1, -1, -1 },\n\t{ 0x35,\t0x1b,\t0x1b,\t-1 },\t/* Esc */\n\t{ 0x36,\t0x0200,\t0x0200,\t-1 },\t/* control */\n\t{ 0x37,\t0x8000,\t0x8000,\t-1 },\t/* Command */\n\n\t{ 0x38,\t0x0100,\t0x0100, -1 },\t/* shift */\n\t{ 0x39,\t0x0400,\t0x0400,\t-1 },\t/* caps lock */\n\t{ 0x3a,\t0x4000,\t0x4000,\t-1 },\t/* Option */\n\t{ 0x3b,\t0x08,\t0x08,\t-1 },\t/* left */\n\t{ 0x3c,\t0x15,\t0x15,\t-1 },\t/* right */\n\t{ 0x3d,\t0x0a,\t0x0a,\t-1 },\t/* down */\n\t{ 0x3e,\t0x0b,\t0x0b,\t-1 },\t/* up arrow */\n\t{ 0x3f, -1, -1, -1 },\n\n\t{ 0x40, -1, -1, -1 },\n\t{ 0x41,\t0x102e,\t0x102c,\t-1 },\t/* keypad . */\n\t{ 0x42, -1, -1, -1 },\n\t{ 0x43,\t0x102a,\t0x102a,\t-1 },\t/* keypad * */\n\t{ 0x44, -1, -1, -1 },\n\t{ 0x45,\t0x102b, 0x102b, -1 },\t/* keypad + */\n\t{ 0x46, -1, -1, -1 },\n\t{ 0x47,\t0x1018,\t0x1018,\t-1 },\t/* keypad Clear */\n\n\t{ 0x48, -1, -1, -1 },\n\t{ 0x49, -1, -1, -1 },\n\t{ 0x4a, -1, -1, -1 },\n\t{ 0x4b,\t0x102f,\t0x102f,\t-1 },\t/* keypad / */\n\t{ 0x4c,\t0x100d,\t0x100d,\t-1 },\t/* keypad enter */\n\t{ 0x4d, -1, -1, -1 },\n\t{ 0x4e,\t0x102d,\t0x102d,\t-1 },\t/* keypad - */\n\t{ 0x4f, -1, -1, -1 },\n\n\t{ 0x50, -1, -1, -1 },\n\t{ 0x51,\t0x103d,\t0x103d,\t-1 },\t/* keypad = */\n\t{ 0x52,\t0x1030,\t0x1030,\t-1 },\t/* keypad 0 */\n\t{ 0x53,\t0x1031,\t0x1031,\t-1 },\t/* keypad 1 */\n\t{ 0x54,\t0x1032,\t0x1032,\t-1 },\t/* keypad 2 */\n\t{ 0x55,\t0x1033,\t0x1033,\t-1 },\t/* keypad 3 */\n\t{ 0x56,\t0x1034,\t0x1034,\t-1 },\t/* keypad 4 */\n\t{ 0x57,\t0x1035, 0x1035, -1 },\t/* keypad 5 */\n\n\t{ 0x58,\t0x1036, 0x1036, -1 },\t/* keypad 6 */\n\t{ 0x59,\t0x1037, 0x1037,\t-1 },\t/* keypad 7 */\n\t{ 0x5a,\t'a',\t'A',\t0x01 },\t/* probably not necessary */\n\t{ 0x5b,\t0x1038,\t0x1038,\t-1 },\t/* keypad 8 */\n\t{ 0x5c,\t0x1039,\t0x1039,\t-1 },\t/* keypad 9 */\n\t{ 0x5d, -1, -1, -1 },\n\t{ 0x5e, -1, -1, -1 },\n\t{ 0x5f, -1, -1, -1 },\n\n\t{ 0x60,\t0x8005,\t0x1060,\t-1 },\t/* F5 */\n\t{ 0x61,\t0x8006,\t0x1061,\t-1 },\t/* F6 */\n\t{ 0x62,\t0x8007,\t0x1062,\t-1 },\t/* F7 */\n\t{ 0x63,\t0x8003,\t0x1063,\t-1 },\t/* F3 */\n\t{ 0x64,\t0x8008,\t0x1064,\t-1 },\t/* F8 */\n\t{ 0x65,\t0x8009,\t0x1065,\t-1 },\t/* F9 */\n\t{ 0x66, -1, -1, -1 },\n\t{ 0x67,\t0x800b,\t0x1067,\t-1 },\t/* F11 */\n\n\t{ 0x68, -1, -1, -1 },\n\t{ 0x69,\t0x800d,\t0x1069,\t-1 },\t/* F13 */\n\t{ 0x6a, -1, -1, -1 },\n\t{ 0x6b,\t0x800e,\t0x106b,\t-1 },\t/* F14 */\n\t{ 0x6c, -1, -1, -1 },\n\t{ 0x6d,\t0x800a,\t0x106d,\t-1 },\t/* F10 */\n\t{ 0x6e, 0x4000, 0x4000, -1 },\t/* windows key alias to option */\n\t{ 0x6f,\t0x800c,\t0x106f,\t-1 },\t/* F12 */\n\n\t{ 0x70, -1, -1, -1 },\n\t{ 0x71,\t0x800f,\t0x1071,\t-1 },\t/* F15 */\n\t{ 0x72,\t0x1072,\t0x1072,\t-1 },\t/* Help, insert */\n\t{ 0x73,\t0x1073,\t0x1073,\t-1 },\t/* Home */\n\t{ 0x74,\t0x1074,\t0x1074,\t-1 },\t/* Page up */\n\t{ 0x75,\t0x1075,\t0x1075,\t-1 },\t/* keypad delete */\n\t{ 0x76,\t0x8004,\t0x1076,\t-1 },\t/* F4 */\n\t{ 0x77,\t0x1077,\t0x1077,\t-1 },\t/* keypad end */\n\n\t{ 0x78,\t0x8002,\t0x1078,\t-1 },\t/* F2 */\n\t{ 0x79,\t0x1079,\t0x1079,\t-1 },\t/* keypad page down */\n\t{ 0x7a,\t0x8001,\t0x107a,\t-1 },\t/* F1 */\n\t{ 0x7b,\t0x08,\t0x08,\t-1 },\t/* left */\t/* remapped to 0x3b */\n\t{ 0x7c,\t0x15,\t0x15,\t-1 },\t/* right */\t/* remapped to 0x3c */\n\t{ 0x7d,\t0x0a,\t0x0a,\t-1 },\t/* down */\t/* remapped to 0x3d */\n\t{ 0x7e,\t0x0b,\t0x0b,\t-1 },\t/* up arrow */\t/* remapped to 0x3e */\n\t{ 0x7f, -1,\t-1,\t-1 },\t/* Reset */\n};\n\nint\nadb_get_hide_warp_info(Kimage *kimage_ptr, int *warpptr)\n{\n\tif((kimage_ptr == &g_mainwin_kimage) && g_adb_mainwin_has_focus) {\n\t\t*warpptr = g_warp_pointer;\n\t\treturn g_hide_pointer;\n\t}\n\t*warpptr = 0;\n\treturn 0;\n}\n\nint\nadb_get_copy_requested()\n{\n\tint\tret;\n\n\tret = g_adb_copy_requested;\n\tg_adb_copy_requested = 0;\n\treturn ret;\n}\n\nvoid\nadb_nonmain_check()\n{\n\t// Debug window active.  Undo F8 pointer warping\n\tg_warp_pointer = 0;\n\tg_hide_pointer = 0;\n}\n\nvoid\nadb_init()\n{\n\tint\tkeycode;\n\tint\ti;\n\n\tif(g_adb_init) {\n\t\thalt_printf(\"g_adb_init = %d!\\n\", g_adb_init);\n\t}\n\tg_adb_init = 1;\n\n\tfor(i = 0; i < 128; i++) {\n\t\tkeycode = g_a2_key_to_ascii[i][0];\n\t\tif(keycode != i) {\n\t\t\tprintf(\"ADB keycode lost/skipped: i=%x: keycode=%x\\n\",\n\t\t\t\ti, keycode);\n\t\t\tmy_exit(1);\n\t\t}\n\t\tg_rawa2_to_a2code[i] = -1;\n\t}\n\n\tg_c025_val = 0;\n\n\tfor(i = 0; i < 4; i++) {\n\t\tg_virtual_key_up[i] = -1;\n\t}\n\n\tfor(i = 0; i < 10; i++) {\n\t\tg_keypad_key_is_down[i] = 0;\n\t}\n}\n\nvoid\nadb_reset()\n{\n\tg_c027_val = 0;\n\n\tg_key_down = 0;\n\tg_kbd_paste_rd_pos = 0;\n\tg_kbd_paste_wr_pos = 0;\n\tg_kbd_chars_buffered = 0;\n\n\tg_kbd_dev_addr = 2;\n\tg_mouse_dev_addr = 3;\n\n\tg_kbd_ctl_addr = 2;\n\tg_mouse_ctl_addr = 3;\n\n\tadb_clear_data_int();\n\tadb_clear_mouse_int();\n\tadb_clear_kbd_srq();\n\n\tg_adb_data_pending = 0;\n\tg_adb_interrupt_byte = 0;\n\tg_adb_state = ADB_IDLE;\n\tg_adb_mouse_coord = 0;\n\tg_adb_mouse_valid_data = 0;\n\n\tg_kbd_reg0_pos = 0;\n\tg_kbd_reg3_16bit = 0x602;\n}\n\n#define LEN_ADB_LOG\t16\nSTRUCT(Adb_log) {\n\tword32\taddr;\n\tint\tval;\n\tint\tstate;\n};\n\nAdb_log g_adb_log[LEN_ADB_LOG];\nint\tg_adb_log_pos = 0;\n\nvoid\nadb_log(word32 addr, int val)\n{\n\tint\tpos;\n\n\tpos = g_adb_log_pos;\n\tg_adb_log[pos].addr = addr;\n\tg_adb_log[pos].val = val;\n\tg_adb_log[pos].state = g_adb_state;\n\tpos++;\n\tif(pos >= LEN_ADB_LOG) {\n\t\tpos = 0;\n\t}\n\tg_adb_log_pos = pos;\n}\n\nvoid\nshow_adb_log(void)\n{\n\tint\tpos;\n\tint\ti;\n\n\tpos = g_adb_log_pos;\n\tprintf(\"ADB log pos: %d\\n\", pos);\n\tfor(i = 0; i < LEN_ADB_LOG; i++) {\n\t\tpos--;\n\t\tif(pos < 0) {\n\t\t\tpos = LEN_ADB_LOG - 1;\n\t\t}\n\t\tprintf(\"%d:%d:  addr:%04x = %02x, st:%d\\n\", i, pos,\n\t\t\tg_adb_log[pos].addr, g_adb_log[pos].val,\n\t\t\tg_adb_log[pos].state);\n\t}\n\tprintf(\"kbd: dev: %x, ctl: %x; mouse: dev: %x, ctl: %x\\n\",\n\t\tg_kbd_dev_addr, g_kbd_ctl_addr,\n\t\tg_mouse_dev_addr, g_mouse_ctl_addr);\n\tprintf(\"g_adb_state: %d, g_adb_interrupt_byte: %02x\\n\",\n\t\tg_adb_state, g_adb_interrupt_byte);\n}\n\nvoid\nadb_error(void)\n{\n\thalt_printf(\"Adb Error\\n\");\n\n\tshow_adb_log();\n}\n\n\n\nvoid\nadb_add_kbd_srq()\n{\n\tif(g_kbd_reg3_16bit & 0x200) {\n\t\t/* generate SRQ */\n\t\tg_adb_interrupt_byte |= 0x08;\n\t\tadd_irq(IRQ_PENDING_ADB_KBD_SRQ);\n\t} else {\n\t\tprintf(\"Got keycode but no kbd SRQ!\\n\");\n\t}\n}\n\nvoid\nadb_clear_kbd_srq()\n{\n\tremove_irq(IRQ_PENDING_ADB_KBD_SRQ);\n\n\t/* kbd SRQ's are the only ones to handle now, so just clean it out */\n\tg_adb_interrupt_byte &= (~(0x08));\n}\n\nvoid\nadb_add_data_int()\n{\n\tif(g_c027_val & ADB_C027_DATA_INT) {\n\t\tadd_irq(IRQ_PENDING_ADB_DATA);\n\t}\n}\n\nvoid\nadb_add_mouse_int()\n{\n\tif(g_c027_val & ADB_C027_MOUSE_INT) {\n\t\tadd_irq(IRQ_PENDING_ADB_MOUSE);\n\t}\n}\n\nvoid\nadb_clear_data_int()\n{\n\tremove_irq(IRQ_PENDING_ADB_DATA);\n}\n\nvoid\nadb_clear_mouse_int()\n{\n\tremove_irq(IRQ_PENDING_ADB_MOUSE);\n}\n\n\nvoid\nadb_send_bytes(int num_bytes, word32 val0, word32 val1, word32 val2)\n{\n\tword32\tval;\n\tint\tshift_amount;\n\tint\ti;\n\n\tif((num_bytes >= 12) || (num_bytes >= MAX_ADB_DATA_PEND))  {\n\t\thalt_printf(\"adb_send_bytes: %d is too many!\\n\", num_bytes);\n\t}\n\n\tg_adb_state = ADB_SENDING_DATA;\n\tg_adb_data_pending = num_bytes;\n\tadb_add_data_int();\n\n\tfor(i = 0; i < num_bytes; i++) {\n\t\tif(i < 4) {\n\t\t\tval = val0;\n\t\t} else if(i < 8) {\n\t\t\tval = val1;\n\t\t} else {\n\t\t\tval = val2;\n\t\t}\n\n\t\tshift_amount = 8*(3 - i);\n\t\tg_adb_data[i] = (val >> shift_amount) & 0xff;\n\t\tadb_printf(\"adb_send_bytes[%d] = %02x\\n\", i, g_adb_data[i]);\n\t}\n}\n\n\nvoid\nadb_send_1byte(word32 val)\n{\n\n\tif(g_adb_data_pending != 0) {\n\t\thalt_printf(\"g_adb_data_pending: %d\\n\", g_adb_data_pending);\n\t}\n\n\tadb_send_bytes(1, val << 24, 0, 0);\n}\n\nvoid\nadb_response_packet(int num_bytes, word32 val)\n{\n\n\tif(g_adb_data_pending != 0) {\n\t\thalt_printf(\"adb_response_packet, but pending: %d\\n\",\n\t\t\tg_adb_data_pending);\n\t}\n\n\tg_adb_state = ADB_IDLE;\n\tg_adb_data_pending = num_bytes;\n\tg_adb_data[0] = val & 0xff;\n\tg_adb_data[1] = (val >> 8) & 0xff;\n\tg_adb_data[2] = (val >> 16) & 0xff;\n\tg_adb_data[3] = (val >> 24) & 0xff;\n\tif(num_bytes) {\n\t\tg_adb_interrupt_byte |= 0x80 + num_bytes - 1;\n\t} else {\n\t\tg_adb_interrupt_byte |= 0x80;\n\t}\n\n\tadb_printf(\"adb_response packet: %d: %08x\\n\",\n\t\tnum_bytes, val);\n\n\tadb_add_data_int();\n}\n\nvoid\nadb_kbd_reg0_data(int a2code, int is_up)\n{\n\tif(g_kbd_reg0_pos >= MAX_ADB_KBD_REG3) {\n\t\t/* too many keys, toss */\n\t\thalt_printf(\"Had to toss key: %02x, %d\\n\", a2code, is_up);\n\t\treturn;\n\t}\n\n\tg_kbd_reg0_data[g_kbd_reg0_pos] = a2code + (is_up << 7);\n\n\tadb_printf(\"g_kbd_reg0_data[%d] = %02x\\n\", g_kbd_reg0_pos,\n\t\tg_kbd_reg0_data[g_kbd_reg0_pos]);\n\n\tg_kbd_reg0_pos++;\n\n\tadb_add_kbd_srq();\n}\n\nvoid\nadb_kbd_talk_reg0()\n{\n\tword32\tval0, val1, reg;\n\tint\tnum_bytes, num;\n\tint\ti;\n\n\tnum = 0;\n\tval0 = g_kbd_reg0_data[0];\n\tval1 = g_kbd_reg0_data[1];\n\n\tnum_bytes = 0;\n\tif(g_kbd_reg0_pos > 0) {\n\t\tnum_bytes = 2;\n\t\tnum = 1;\n\t\tif((val0 & 0x7f) == 0x7f) {\n\t\t\t/* reset */\n\t\t\tval1 = val0;\n\t\t} else if(g_kbd_reg0_pos > 1) {\n\t\t\tnum = 2;\n\t\t\tif((val1 & 0x7f) == 0x7f) {\n\t\t\t\t/* If first byte some other key, don't */\n\t\t\t\t/*  put RESET next! */\n\t\t\t\tnum = 1;\n\t\t\t\tval1 = 0xff;\n\t\t\t}\n\t\t} else {\n\t\t\tval1 = 0xff;\n\t\t}\n\t}\n\n\tif(num) {\n\t\tfor(i = num; i < g_kbd_reg0_pos; i++) {\n\t\t\tg_kbd_reg0_data[i-1] = g_kbd_reg0_data[i];\n\t\t}\n\t\tg_kbd_reg0_pos -= num;\n\t}\n\n\treg = (val0 << 8) + val1;\n\n\tadb_printf(\"adb_kbd_talk0: %04x\\n\", reg);\n\n\tadb_response_packet(num_bytes, reg);\n\tif(g_kbd_reg0_pos == 0) {\n\t\tadb_clear_kbd_srq();\n\t}\n}\n\nvoid\nadb_set_config(word32 val0, word32 val1, word32 val2)\n{\n\tint\tnew_mouse;\n\tint\tnew_kbd;\n\tint\ttmp1;\n\n\tnew_mouse = val0 >> 4;\n\tnew_kbd = val0  & 0xf;\n\tif(new_mouse != g_mouse_ctl_addr) {\n\t\tprintf(\"ADB config: mouse from %x to %x!\\n\",\n\t\t\tg_mouse_ctl_addr, new_mouse);\n\t\tadb_error();\n\t\tg_mouse_ctl_addr = new_mouse;\n\t}\n\tif(new_kbd != g_kbd_ctl_addr) {\n\t\tprintf(\"ADB config: kbd from %x to %x!\\n\",\n\t\t\tg_kbd_ctl_addr, new_kbd);\n\t\tadb_error();\n\t\tg_kbd_ctl_addr = new_kbd;\n\t}\n\n\tif(val1) {\n\t\t// Do nothing\n\t}\n\n\ttmp1 = val2 >> 4;\n\tif(tmp1 == 4) {\n\t\tg_adb_repeat_delay = 0;\n\t} else if(tmp1 < 4) {\n\t\tg_adb_repeat_delay = (tmp1 + 1) * 15;\n\t} else {\n\t\thalt_printf(\"Bad ADB repeat delay: %02x\\n\", tmp1);\n\t}\n\n\ttmp1 = val2 & 0xf;\n\tif(g_rom_version >= 3) {\n\t\ttmp1 = 9 - tmp1;\n\t}\n\n\tswitch(tmp1) {\n\tcase 0:\n\t\tg_adb_repeat_rate = 1;\n\t\tbreak;\n\tcase 1:\n\t\tg_adb_repeat_rate = 2;\n\t\tbreak;\n\tcase 2:\n\t\tg_adb_repeat_rate = 3;\n\t\tbreak;\n\tcase 3:\n\t\tg_adb_repeat_rate = 3;\n\t\tbreak;\n\tcase 4:\n\t\tg_adb_repeat_rate = 4;\n\t\tbreak;\n\tcase 5:\n\t\tg_adb_repeat_rate = 5;\n\t\tbreak;\n\tcase 6:\n\t\tg_adb_repeat_rate = 7;\n\t\tbreak;\n\tcase 7:\n\t\tg_adb_repeat_rate = 15;\n\t\tbreak;\n\tcase 8:\n\t\t/* I don't know what this should be, ROM 03 uses it */\n\t\tg_adb_repeat_rate = 30;\n\t\tbreak;\n\tcase 9:\n\t\t/* I don't know what this should be, ROM 03 uses it */\n\t\tg_adb_repeat_rate = 60;\n\t\tbreak;\n\tdefault:\n\t\thalt_printf(\"Bad repeat rate: %02x\\n\", tmp1);\n\t}\n\n}\n\nvoid\nadb_set_new_mode(word32 val)\n{\n\tif(val & 0x03) {\n\t\tprintf(\"Disabling keyboard/mouse:%02x!\\n\", val);\n\t}\n\n\tif(val & 0xa2) {\n\t\thalt_printf(\"ADB set mode: %02x!\\n\", val);\n\t\tadb_error();\n\t}\n\n\tg_adb_mode = val;\n}\n\n\nint\nadb_read_c026()\n{\n\tword32\tret;\n\tint\ti;\n\n\tret = 0;\n\tswitch(g_adb_state) {\n\tcase ADB_IDLE:\n\t\tret = g_adb_interrupt_byte;\n\t\tg_adb_interrupt_byte = 0;\n\t\tif(g_irq_pending & IRQ_PENDING_ADB_KBD_SRQ) {\n\t\t\tg_adb_interrupt_byte |= 0x08;\n\t\t}\n\t\tif(g_adb_data_pending == 0) {\n\t\t\tif(ret & 0x80) {\n\t\t\t\thalt_printf(\"read_c026: ret:%02x, pend:%d\\n\",\n\t\t\t\t\tret, g_adb_data_pending);\n\t\t\t}\n\t\t\tadb_clear_data_int();\n\t\t}\n\t\tif(g_adb_data_pending) {\n\t\t\tif(g_adb_state != ADB_IN_CMD) {\n\t\t\t\tg_adb_state = ADB_SENDING_DATA;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase ADB_IN_CMD:\n\t\tret = 0;\n\t\tbreak;\n\tcase ADB_SENDING_DATA:\n\t\tret = g_adb_data[0];\n\t\tfor(i = 1; i < g_adb_data_pending; i++) {\n\t\t\tg_adb_data[i-1] = g_adb_data[i];\n\t\t}\n\t\tg_adb_data_pending--;\n\t\tif(g_adb_data_pending <= 0) {\n\t\t\tg_adb_data_pending = 0;\n\t\t\tg_adb_state = ADB_IDLE;\n\t\t\tadb_clear_data_int();\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\thalt_printf(\"Bad ADB state: %d!\\n\", g_adb_state);\n\t\tadb_clear_data_int();\n\t\tbreak;\n\t}\n\n\tadb_printf(\"Reading c026.  Returning %02x, st: %02x, pend: %d\\n\",\n\t\tret, g_adb_state, g_adb_data_pending);\n\n\tadb_log(0xc026, ret);\n\treturn (ret & 0xff);\n}\n\n\nvoid\nadb_write_c026(int val)\n{\n\tword32\ttmp;\n\tint\tdev;\n\n\tadb_printf(\"Writing c026 with %02x\\n\", val);\n\tadb_log(0x1c026, val);\n\n\n\tswitch(g_adb_state) {\n\tcase ADB_IDLE:\n\t\tg_adb_cmd = val;\n\t\tg_adb_cmd_so_far = 0;\n\t\tg_adb_cmd_len = 0;\n\n\t\tdev = val & 0xf;\n\t\tswitch(val) {\n\t\tcase 0x01:\t/* Abort */\n\t\t\tadb_printf(\"Performing adb abort\\n\");\n\t\t\t/* adb_abort() */\n\t\t\tbreak;\n\t\tcase 0x03:\t/* Flush keyboard buffer */\n\t\t\tadb_printf(\"Flushing adb keyboard buffer\\n\");\n\t\t\t/* Do nothing */\n\t\t\tbreak;\n\t\tcase 0x04:\t/* Set modes */\n\t\t\tadb_printf(\"ADB set modes\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 1;\n\t\t\tbreak;\n\t\tcase 0x05:\t/* Clear modes */\n\t\t\tadb_printf(\"ADB clear modes\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 1;\n\t\t\tbreak;\n\t\tcase 0x06:\t/* Set config */\n\t\t\tadb_printf(\"ADB set config\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 3;\n\t\t\tbreak;\n\t\tcase 0x07:\t/* Sync */\n\t\t\tadb_printf(\"Performing sync cmd!\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tif(g_rom_version == 1) {\n\t\t\t\tg_adb_cmd_len = 4;\n\t\t\t} else {\n\t\t\t\tg_adb_cmd_len = 8;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x08:\t/* Write mem */\n\t\t\tadb_printf(\"Starting write_mem cmd\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 2;\n\t\t\tbreak;\n\t\tcase 0x09:\t/* Read mem */\n\t\t\tadb_printf(\"Performing read_mem cmd!\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 2;\n\t\t\tbreak;\n\t\tcase 0x0a:\t/* Read modes byte */\n\t\t\tprintf(\"Performing read_modes cmd!\\n\");\n\t\t\t/* set_halt(1); */\n\t\t\tadb_send_1byte(g_adb_mode);\n\t\t\tbreak;\n\t\tcase 0x0b:\t/* Read config bytes */\n\t\t\tprintf(\"Performing read_configs cmd!\\n\");\n\t\t\ttmp = (g_mouse_ctl_addr << 20) +\n\t\t\t\t(g_kbd_ctl_addr << 16) +\n\t\t\t\t(g_adb_char_set << 12) +\n\t\t\t\t(g_adb_layout_lang << 8) +\n\t\t\t\t(g_adb_repeat_info << 0);\n\t\t\ttmp = (0x82U << 24) + tmp;\n\t\t\tadb_send_bytes(4, tmp, 0, 0);\n\t\t\tbreak;\n\t\tcase 0x0d:\t/* Get Version */\n\t\t\tadb_printf(\"Performing get_version cmd!\\n\");\n\t\t\tval = 0;\n\t\t\tif(g_rom_version == 1) {\n\t\t\t\t/* ROM 01 = revision 5 */\n\t\t\t\tval = 5;\n\t\t\t} else {\n\t\t\t\t/* ROM 03 checks for rev >= 6 */\n\t\t\t\tval = 6;\n\t\t\t}\n\t\t\tadb_send_1byte(val);\n\t\t\tbreak;\n\t\tcase 0x0e:\t/* Read avail char sets */\n\t\t\tadb_printf(\"Performing read avail char sets cmd!\\n\");\n\t\t\tadb_send_bytes(2,\t/* just 2 bytes */\n\t\t\t\t0x08000000,\t/* number of ch sets=0x8 */\n\t\t\t\t0, 0);\n\t\t\t/* set_halt(1); */\n\t\t\tbreak;\n\t\tcase 0x0f:\t/* Read avail kbd layouts */\n\t\t\tadb_printf(\"Performing read avail kbd layouts cmd!\\n\");\n\t\t\tadb_send_bytes(0x2,\t/* number of kbd layouts=0xa */\n\t\t\t\t0x0a000000, 0, 0);\n\t\t\t/* set_halt(1); */\n\t\t\tbreak;\n\t\tcase 0x10:\t/* Reset */\n\t\t\tprintf(\"ADB reset, cmd 0x10\\n\");\n\t\t\tdo_reset();\n\t\t\tbreak;\n\t\tcase 0x11:\t/* Send ADB keycodes */\n\t\t\tadb_printf(\"Sending ADB keycodes\\n\");\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 1;\n\t\t\tbreak;\n\t\tcase 0x12:\t/* ADB cmd 12: ROM 03 only! */\n\t\t\tif(g_rom_version >= 3) {\n\t\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\t\tg_adb_cmd_len = 2;\n\t\t\t} else {\n\t\t\t\tprintf(\"ADB cmd 12, but not ROM 3!\\n\");\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x13:\t/* ADB cmd 13: ROM 03 only! */\n\t\t\tif(g_rom_version >= 3) {\n\t\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\t\tg_adb_cmd_len = 2;\n\t\t\t} else {\n\t\t\t\tprintf(\"ADB cmd 13, but not ROM 3!\\n\");\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x73:\t/* Disable SRQ device 3: mouse */\n\t\t\tadb_printf(\"Disabling Mouse SRQ's (device 3)\\n\");\n\t\t\t/* HACK HACK...should deal with SRQs on mouse */\n\t\t\tbreak;\n\t\tcase 0xb0: case 0xb1: case 0xb2: case 0xb3:\n\t\tcase 0xb4: case 0xb5: case 0xb6: case 0xb7:\n\t\tcase 0xb8: case 0xb9: case 0xba: case 0xbb:\n\t\tcase 0xbc: case 0xbd: case 0xbe: case 0xbf:\n\t\t\t/* Listen dev x reg 3 */\n\t\t\tadb_printf(\"Sending data to dev %x reg 3\\n\", dev);\n\t\t\tg_adb_state = ADB_IN_CMD;\n\t\t\tg_adb_cmd_len = 2;\n\t\t\tbreak;\n\t\tcase 0xc0: case 0xc1: case 0xc2: case 0xc3:\n\t\tcase 0xc4: case 0xc5: case 0xc6: case 0xc7:\n\t\tcase 0xc8: case 0xc9: case 0xca: case 0xcb:\n\t\tcase 0xcc: case 0xcd: case 0xce: case 0xcf:\n\t\t\t/* Talk dev x reg 0 */\n\t\t\tadb_printf(\"Performing talk dev %x reg 0\\n\", dev);\n\t\t\tif(dev == g_kbd_dev_addr) {\n\t\t\t\tadb_kbd_talk_reg0();\n\t\t\t} else {\n\t\t\t\tprintf(\"Unknown talk dev %x reg 0!\\n\", dev);\n\t\t\t\t/* send no data, on SRQ, system polls devs */\n\t\t\t\t/*  so we don't want to send anything */\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0xf0: case 0xf1: case 0xf2: case 0xf3:\n\t\tcase 0xf4: case 0xf5: case 0xf6: case 0xf7:\n\t\tcase 0xf8: case 0xf9: case 0xfa: case 0xfb:\n\t\tcase 0xfc: case 0xfd: case 0xfe: case 0xff:\n\t\t\t/* Talk dev x reg 3 */\n\t\t\tadb_printf(\"Performing talk dev %x reg 3\\n\", dev);\n\t\t\tif(dev == g_kbd_dev_addr) {\n\t\t\t\tadb_response_packet(2, g_kbd_reg3_16bit);\n\t\t\t} else {\n\t\t\t\tprintf(\"Performing talk dev %x reg 3!!\\n\", dev);\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thalt_printf(\"ADB ucontroller cmd %02x unknown!\\n\", val);\n\t\t\t/* The Gog's says ACS Demo 2 has a bug and writes to */\n\t\t\t/*  c026 */\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase ADB_IN_CMD:\n\t\tadb_printf(\"Setting byte %d of cmd %02x to %02x\\n\",\n\t\t\tg_adb_cmd_so_far, g_adb_cmd, val);\n\n\t\tg_adb_cmd_data[g_adb_cmd_so_far] = val;\n\t\tg_adb_cmd_so_far++;\n\t\tif(g_adb_cmd_so_far >= g_adb_cmd_len) {\n\t\t\tadb_printf(\"Finished cmd %02x\\n\", g_adb_cmd);\n\t\t\tdo_adb_cmd();\n\t\t}\n\n\t\tbreak;\n\tdefault:\n\t\tprintf(\"adb_state: %02x is unknown!  Setting it to ADB_IDLE\\n\",\n\t\t\tg_adb_state);\n\t\tg_adb_state = ADB_IDLE;\n\t\tadb_error();\n\t\thalt_on_all_c027 = 1;\n\t\tbreak;\n\t}\n\treturn;\n}\n\nvoid\ndo_adb_cmd()\n{\n\tword32\tval;\n\tint\tdev, new_kbd, addr;\n\n\tdev = g_adb_cmd & 0xf;\n\n\tg_adb_state = ADB_IDLE;\n\n\tswitch(g_adb_cmd) {\n\tcase 0x04:\t/* Set modes */\n\t\tadb_printf(\"Performing ADB set mode: OR'ing in %02x\\n\",\n\t\t\tg_adb_cmd_data[0]);\n\n\t\tval = g_adb_cmd_data[0] | g_adb_mode;\n\t\tadb_set_new_mode(val);\n\n\t\tbreak;\n\tcase 0x05:\t/* clear modes */\n\t\tadb_printf(\"Performing ADB clear mode: AND'ing in ~%02x\\n\",\n\t\t\tg_adb_cmd_data[0]);\n\n\t\tval = g_adb_cmd_data[0];\n\t\tval = g_adb_mode & (~val);\n\t\tadb_set_new_mode(val);\n\t\tbreak;\n\tcase 0x06:\t/* Set config */\n\t\tadb_printf(\"Set ADB config to %02x %02x %02x\\n\",\n\t\t\tg_adb_cmd_data[0], g_adb_cmd_data[1],g_adb_cmd_data[2]);\n\n\t\tadb_set_config(g_adb_cmd_data[0], g_adb_cmd_data[1],\n\t\t\tg_adb_cmd_data[2]);\n\n\t\tbreak;\n\tcase 0x07:\t/* SYNC */\n\t\tadb_printf(\"Performing ADB SYNC\\n\");\n\t\tadb_printf(\"data: %02x %02x %02x %02x\\n\",\n\t\t\tg_adb_cmd_data[0], g_adb_cmd_data[1], g_adb_cmd_data[2],\n\t\t\tg_adb_cmd_data[3]);\n\n\t\tadb_set_new_mode(g_adb_cmd_data[0]);\n\t\tadb_set_config(g_adb_cmd_data[1], g_adb_cmd_data[2],\n\t\t\t\tg_adb_cmd_data[3]);\n\n\t\tif(g_rom_version >= 3) {\n\t\t\tadb_printf(\"  and cmd12:%02x %02x cmd13:%02x %02x\\n\",\n\t\t\t\tg_adb_cmd_data[4], g_adb_cmd_data[5],\n\t\t\t\tg_adb_cmd_data[6], g_adb_cmd_data[7]);\n\t\t}\n\t\tbreak;\n\tcase 0x08:\t/* Write mem */\n\t\taddr = g_adb_cmd_data[0];\n\t\tval = g_adb_cmd_data[1];\n\t\twrite_adb_ram(addr, val);\n\t\tbreak;\n\tcase 0x09:\t/* Read mem */\n\t\taddr = (g_adb_cmd_data[1] << 8) + g_adb_cmd_data[0];\n\t\tadb_printf(\"Performing mem read to addr %04x\\n\", addr);\n\t\tadb_send_1byte(read_adb_ram(addr));\n\t\tbreak;\n\tcase 0x11:\t/* Send ADB keycodes */\n\t\tval = g_adb_cmd_data[0];\n\t\tadb_printf(\"Performing send ADB keycodes: %02x\\n\", val);\n\t\tadb_virtual_key_update(val & 0x7f, val >> 7);\n\t\tbreak;\n\tcase 0x12:\t/* ADB cmd12 */\n\t\tadb_printf(\"Performing ADB cmd 12\\n\");\n\t\tadb_printf(\"data: %02x %02x\\n\", g_adb_cmd_data[0],\n\t\t\t\t\t\t\tg_adb_cmd_data[1]);\n\t\tbreak;\n\tcase 0x13:\t/* ADB cmd13 */\n\t\tadb_printf(\"Performing ADB cmd 13\\n\");\n\t\tadb_printf(\"data: %02x %02x\\n\", g_adb_cmd_data[0],\n\t\t\t\t\t\t\tg_adb_cmd_data[1]);\n\t\tbreak;\n\tcase 0xb0: case 0xb1: case 0xb2: case 0xb3:\n\tcase 0xb4: case 0xb5: case 0xb6: case 0xb7:\n\tcase 0xb8: case 0xb9: case 0xba: case 0xbb:\n\tcase 0xbc: case 0xbd: case 0xbe: case 0xbf:\n\t\t/* Listen dev x reg 3 */\n\t\tif(dev == g_kbd_dev_addr) {\n\t\t\tif(g_adb_cmd_data[1] == 0xfe) {\n\t\t\t\t/* change keyboard addr? */\n\t\t\t\tnew_kbd = g_adb_cmd_data[0] & 0xf;\n\t\t\t\tif(new_kbd != dev) {\n\t\t\t\t\tprintf(\"Moving kbd to dev %x!\\n\",\n\t\t\t\t\t\t\t\tnew_kbd);\n\t\t\t\t\tadb_error();\n\t\t\t\t}\n\t\t\t\tg_kbd_dev_addr = new_kbd;\n\t\t\t} else if(g_adb_cmd_data[1] != 1) {\n\t\t\t\t/* see what new device handler id is */\n\t\t\t\tprintf(\"KBD listen to dev %x reg 3: 1:%02x\\n\",\n\t\t\t\t\tdev, g_adb_cmd_data[1]);\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tif(g_adb_cmd_data[0] != (word32)g_kbd_dev_addr) {\n\t\t\t\t/* see if app is trying to change addr */\n\t\t\t\tprintf(\"KBD listen to dev %x reg 3: 0:%02x!\\n\",\n\t\t\t\t\tdev, g_adb_cmd_data[0]);\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tg_kbd_reg3_16bit = ((g_adb_cmd_data[0] & 0xf) << 12) +\n\t\t\t\t(g_kbd_reg3_16bit & 0x0fff);\n\t\t} else if(dev == g_mouse_dev_addr) {\n\t\t\tif(g_adb_cmd_data[0] != (word32)dev) {\n\t\t\t\t/* see if app is trying to change mouse addr */\n\t\t\t\tprintf(\"MOUS listen to dev %x reg3: 0:%02x!\\n\",\n\t\t\t\t\tdev, g_adb_cmd_data[0]);\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t\tif(g_adb_cmd_data[1] != 1 && g_adb_cmd_data[1] != 2) {\n\t\t\t\t/* see what new device handler id is */\n\t\t\t\tprintf(\"MOUS listen to dev %x reg 3: 1:%02x\\n\",\n\t\t\t\t\tdev, g_adb_cmd_data[1]);\n\t\t\t\tadb_error();\n\t\t\t}\n\t\t} else {\n\t\t\tprintf(\"Listen cmd to dev %x reg3????\\n\", dev);\n\t\t\tprintf(\"data0: %02x, data1: %02x ????\\n\",\n\t\t\t\tg_adb_cmd_data[0], g_adb_cmd_data[1]);\n\t\t\tadb_error();\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tprintf(\"Doing adb_cmd %02x: UNKNOWN!\\n\", g_adb_cmd);\n\t\tbreak;\n\t}\n}\n\n\nint\nadb_read_c027()\n{\n\tword32\tret;\n\n\tif(halt_on_all_c027) {\n\t\thalt_printf(\"halting on all c027 reads!\\n\");\n\t}\n\n\tif(g_c027_val & (~ADB_C027_NEG_MASK)) {\n\t\thalt_printf(\"read_c027: g_c027_val: %02x\\n\", g_c027_val);\n\t}\n\n\tret = (g_c027_val & ADB_C027_NEG_MASK);\n\n\tif(g_adb_mouse_valid_data) {\n\t\tret |= ADB_C027_MOUSE_DATA;\n\t}\n\n\tif(g_adb_interrupt_byte != 0) {\n\t\tret |= ADB_C027_DATA_VALID;\n\t} else if(g_adb_data_pending > 0) {\n\t\tif((g_adb_state != ADB_IN_CMD)) {\n\t\t\tret |= ADB_C027_DATA_VALID;\n\t\t}\n\t}\n\n\tif(g_adb_mouse_coord) {\n\t\tret |= ADB_C027_MOUSE_COORD;\n\t}\n\n#if 0\n\tadb_printf(\"Read c027: %02x, int_byte: %02x, d_pend: %d\\n\",\n\t\tret, g_adb_interrupt_byte, g_adb_data_pending);\n#endif\n\n#if 0\n\tadb_log(0xc027, ret);\n#endif\n\treturn ret;\n}\n\nvoid\nadb_write_c027(int val)\n{\n\tword32\told_val;\n\tword32\tnew_int;\n\tword32\told_int;\n\n\tadb_printf(\"Writing c027 with %02x\\n\", val);\n\tadb_log(0x1c027, val);\n\n\n\told_val = g_c027_val;\n\n\tg_c027_val = (val & ADB_C027_NEG_MASK);\n\tnew_int = g_c027_val & ADB_C027_MOUSE_INT;\n\told_int = old_val & ADB_C027_MOUSE_INT;\n\tif(!new_int && old_int) {\n\t\tadb_clear_mouse_int();\n\t}\n\n\tnew_int = g_c027_val & ADB_C027_DATA_INT;\n\told_int = old_val & ADB_C027_DATA_INT;\n\tif(!new_int && old_int) {\n\t\t/* ints were on, now off */\n\t\tadb_clear_data_int();\n\t}\n\n\tif(g_c027_val & ADB_C027_KBD_INT) {\n\t\thalt_printf(\"Can't support kbd interrupts!\\n\");\n\t}\n\n\treturn;\n}\n\nint\nread_adb_ram(word32 addr)\n{\n\tint val;\n\n\tadb_printf(\"Reading adb ram addr: %02x\\n\", addr);\n\n\tif(addr >= 0x100) {\n\t\tif(addr >= 0x1000 && addr < 0x2000) {\n\t\t\t/* ROM self-test checksum */\n\t\t\tif(addr == 0x1400) {\n\t\t\t\tval = 0x72;\n\t\t\t} else if(addr == 0x1401) {\n\t\t\t\tval = 0xf7;\n\t\t\t} else {\n\t\t\t\tval = 0;\n\t\t\t}\n\t\t} else {\n\t\t\tprintf(\"adb ram addr out of range: %04x!\\n\", addr);\n\t\t\tval = 0;\n\t\t}\n\t} else {\n\t\tval = adb_memory[addr];\n\t\tif((addr == 0xb) && (g_rom_version == 1)) {\n\t\t\t// read special key state byte for Out of This World\n\t\t\tval = (g_c025_val >> 1) & 0x43;\n\t\t\tval |= (g_c025_val << 2) & 0x4;\n\t\t\tval |= (g_c025_val >> 2) & 0x10;\n\t\t}\n\t\tif((addr == 0xc) && (g_rom_version >= 3)) {\n\t\t\t// read special key state byte for Out of This World\n\t\t\tval = g_c025_val & 0xc7;\n\t\t\tprintf(\"val is %02x\\n\", val);\n\t\t}\n\t}\n\n\tadb_printf(\"adb_ram returning %02x\\n\", val);\n\treturn val;\n}\n\nvoid\nwrite_adb_ram(word32 addr, int val)\n{\n\n\tadb_printf(\"Writing adb_ram addr: %02x: %02x\\n\", addr, val);\n\n\tif(addr >= 0x100) {\n\t\tprintf(\"write adb_ram addr: %02x: %02x!\\n\", addr, val);\n\t\tadb_error();\n\t} else {\n\t\tadb_memory[addr] = val;\n\t}\n}\n\nint\nadb_get_keypad_xy(int get_y)\n{\n\tint\tx, y, key, num_keys;\n\tint\ti, j;\n\n\tkey = 1;\n\tnum_keys = 0;\n\tx = 0;\n\ty = 0;\n\tfor(i = 0; i < 3; i++) {\n\t\tfor(j = 0; j < 3; j++) {\n\t\t\tif(g_keypad_key_is_down[key]) {\n\t\t\t\tnum_keys++;\n\t\t\t\tx = x + (j - 1)*32768;\n\t\t\t\ty = y + (1 - i)*32768;\n\t\t\t}\n\t\t\tkey++;\n\t\t}\n\t}\n\tif(num_keys == 0) {\n\t\tnum_keys = 1;\n\t}\n\n\tadb_printf(\"get_xy=%d, num_keys: %d, x:%d, y:%d\\n\", get_y,\n\t\t\t\t\t\t\tnum_keys, x, y);\n\n\tif(get_y) {\n\t\treturn y / num_keys;\n\t} else {\n\t\treturn x / num_keys;\n\t}\n}\n\n// g_mouse_raw_x/y: Current position (in A2 coordinates) of mouse on host screen\n// g_mouse_fifo[0].x/y: Current position (in A2 coords) of where we \"want\"\n//\t\tmouse on the A2 screen.\n// g_mouse_a2_x/y: last x,y returned through $c024 to software.\n// So, reading $c024 return g_mouse_fifo[].x - g_mouse_a2_x.\n// And, in simple cases, host mouse movement just sets g_mouse_fifo[0].x=raw_x\nint\nadb_update_mouse(Kimage *kimage_ptr, int x, int y, int button_states,\n\t\t\t\t\t\t\tint buttons_valid)\n{\n\tdword64\tdfcyc;\n\tint\tbutton1_changed, mouse_moved, unhide, pos;\n\tint\ti;\n\n\tif(kimage_ptr != &g_mainwin_kimage) {\n\t\tadb_nonmain_check();\n\t}\n\tdfcyc = g_cur_dfcyc;\n\n\tunhide = (g_adb_mainwin_has_focus == 0);\n\tif((buttons_valid >= 0) && (buttons_valid & 0x1000)) {\n\t\t// x, y are really deltas\n\t\tbuttons_valid &= 0xfff;\n\t\tx = g_mouse_raw_x + x;\n\t\ty = g_mouse_raw_y + y;\n\t\tg_mouse_raw_x = x;\n\t\tg_mouse_raw_y = y;\n\t} else {\n\t\tg_mouse_raw_x = x;\n\t\tg_mouse_raw_y = y;\n\n\t\t// Clamp mouse to 0-639, 0-399 to make GSOS work nicely\n\t\tif(x < 0) {\n\t\t\tx = 0;\n\t\t\tunhide = 1;\n\t\t}\n\t\tif(x >= 640) {\n\t\t\tx = 639;\n\t\t\tunhide = 1;\n\t\t}\n\t\tif(y < 0) {\n\t\t\ty = 0;\n\t\t\tunhide = 1;\n\t\t}\n\t\tif(y >= 400) {\n\t\t\ty = 399;\n\t\t\tunhide = 1;\n\t\t}\n\t}\n\n\tg_unhide_pointer = unhide && !g_warp_pointer;\n\n\tif(kimage_ptr != &g_mainwin_kimage) {\n\t\t// In debugger window...just get out\n\t\treturn 0;\n\t}\n\n\tif(!g_warp_pointer) {\n\t\tif(g_hide_pointer && g_unhide_pointer) {\n\t\t\t/* cursor has left a2 window, show it */\n\t\t\tg_hide_pointer = 0;\n\t\t}\n\t\tif((g_num_lines_prev_superhires == 200) &&\n\t\t\t\t(g_num_lines_prev_superhires640 == 0) &&\n\t\t\t\t((g_slow_memory_ptr[0x19d00] & 0x80) == 0)) {\n\t\t\t// In 320-mode superhires, cut mouse range in half\n\t\t\tx = x >> 1;\n\t\t}\n\t\ty = y >> 1;\n\t}\n\n\tmouse_compress_fifo(dfcyc);\n\n#if 0\n\tprintf(\"Update Mouse called with buttons:%d x,y:%d,%d, fifo:%d,%d, \"\n\t\t\" a2: %d,%d\\n\", buttons_valid, x, y,\n\t\tg_mouse_fifo[0].x, g_mouse_fifo[0].y,\n\t\tg_mouse_a2_x, g_mouse_a2_y);\n#endif\n\n\tif((buttons_valid < 0) && g_warp_pointer) {\n\t\t/* Warping the pointer causes it to jump here...this is not */\n\t\t/*  real motion, just update info and get out */\n\t\tg_mouse_a2_x += (x - g_mouse_fifo[0].x);\n\t\tg_mouse_a2_y += (y - g_mouse_fifo[0].y);\n\t\tg_mouse_fifo[0].x = x;\n\t\tg_mouse_fifo[0].y = y;\n\t\treturn 0;\n\t}\n\n#if 0\n\tprintf(\"...real move, new x: %d, %d, a2:%d,%d\\n\", g_mouse_fifo[0].x,\n\t\t\tg_mouse_fifo[0].y, g_mouse_a2_x, g_mouse_a2_y);\n#endif\n\n\tmouse_moved = (g_mouse_fifo[0].x != x) || (g_mouse_fifo[0].y != y);\n\n\tg_mouse_fifo[0].x = x;\n\tg_mouse_fifo[0].y = y;\n\tg_mouse_fifo[0].dfcyc = dfcyc;\n\n\tbutton1_changed = (buttons_valid & 1) &&\n\t\t\t((button_states & 1) != (g_mouse_fifo[0].buttons & 1));\n\n\tif((button_states & 4) && !(g_mouse_fifo[0].buttons & 4) &&\n\t\t\t\t\t\t\t(buttons_valid & 4)) {\n\t\t/* right button pressed */\n\t\tadb_increment_speed();\n\t}\n\tif((button_states & 2) && !(g_mouse_fifo[0].buttons & 2) &&\n\t\t\t\t\t\t\t(buttons_valid & 2)) {\n\t\t/* middle button pressed */\n\t\thalt2_printf(\"Middle button pressed\\n\");\n\t}\n\n\tpos = g_mouse_fifo_pos;\n\tif((pos < (ADB_MOUSE_FIFO - 2)) && button1_changed) {\n\t\t/* copy delta to overflow, set overflow */\n\t\t/* overflow ensures the mouse button state is precise at */\n\t\t/*  button up/down times.  Using a mouse event list where */\n\t\t/*  deltas accumulate until a button change would work, too */\n\t\tfor(i = pos; i >= 0; i--) {\n\t\t\tg_mouse_fifo[i + 1] = g_mouse_fifo[i];\t/* copy struct*/\n\t\t}\n\t\tg_mouse_fifo_pos = pos + 1;\n\t}\n\n\tg_mouse_fifo[0].buttons = (button_states & buttons_valid) |\n\t\t\t\t(g_mouse_fifo[0].buttons & ~buttons_valid);\n\n\tif(mouse_moved || button1_changed) {\n\t\tif( (g_mouse_ctl_addr == g_mouse_dev_addr) &&\n\t\t\t\t\t\t((g_adb_mode & 0x2) == 0)) {\n\t\t\tg_adb_mouse_valid_data = 1;\n\t\t\tadb_add_mouse_int();\n\t\t}\n\t}\n\n\treturn mouse_moved;\n}\n\nint\nmouse_read_c024(dword64 dfcyc)\n{\n\tword32\tret, tool_start;\n\tint\tem_active, target_x, target_y, delta_x, delta_y, a2_x, a2_y;\n\tint\tmouse_button, clamped, pos;\n\n\tif(((g_adb_mode & 0x2) != 0) || (g_mouse_dev_addr != g_mouse_ctl_addr)){\n\t\t/* mouse is off, return 0, or mouse is not autopoll */\n\t\tg_adb_mouse_valid_data = 0;\n\t\tadb_clear_mouse_int();\n\t\treturn 0;\n\t}\n\n\tmouse_compress_fifo(dfcyc);\n\n\tpos = g_mouse_fifo_pos;\n\ttarget_x = g_mouse_fifo[pos].x;\n\ttarget_y = g_mouse_fifo[pos].y;\n\tmouse_button = (g_mouse_fifo[pos].buttons & 1);\n\tdelta_x = target_x - g_mouse_a2_x;\n\tdelta_y = target_y - g_mouse_a2_y;\n\n\tclamped = 0;\n\tif(delta_x > 0x3f) {\n\t\tdelta_x = 0x3f;\n\t\tclamped = 1;\n\t} else if(delta_x < -0x3f) {\n\t\tdelta_x = -0x3f;\n\t\tclamped = 1;\n\t}\n\tif(delta_y > 0x3f) {\n\t\tdelta_y = 0x3f;\n\t\tclamped = 1;\n\t} else if(delta_y < -0x3f) {\n\t\tdelta_y = -0x3f;\n\t\tclamped = 1;\n\t}\n\n\tif(pos > 0) {\n\t\t/* peek into next entry's button info if we are not clamped */\n\t\t/*  and we're returning the y-coord */\n\t\tif(!clamped && g_adb_mouse_coord) {\n\t\t\tmouse_button = g_mouse_fifo[pos - 1].buttons & 1;\n\t\t}\n\t}\n\n\tif(g_adb_mouse_coord) {\n\t\t/* y coord */\n\t\tdelta_x = 0;\t/* clear unneeded x delta */\n\t} else {\n\t\tdelta_y = 0;\t/* clear unneeded y delta */\n\t}\n\n\n\tadb_printf(\" pre a2_x:%02x,%02x,%02x,%02x\\n\",\n\t\tg_slow_memory_ptr[0x100e9], g_slow_memory_ptr[0x100ea],\n\t\tg_slow_memory_ptr[0x100eb], g_slow_memory_ptr[0x100ec]);\n\tadb_printf(\" pre a2_x:%02x,%02x,%02x,%02x\\n\",\n\t\tg_slow_memory_ptr[0x10190], g_slow_memory_ptr[0x10192],\n\t\tg_slow_memory_ptr[0x10191], g_slow_memory_ptr[0x10193]);\n\n\t/* Update event manager internal state */\n\ttool_start = (g_slow_memory_ptr[0x103ca] << 16) +\n\t\t\t(g_slow_memory_ptr[0x103c9] << 8) +\n\t\t\tg_slow_memory_ptr[0x103c8];\n\n\tem_active = 0;\n\tif((tool_start >= 0x20000) && (tool_start < (g_mem_size_total - 28)) ) {\n\t\t/* seems to be valid ptr to addr of mem space for tools */\n\t\t/* see if event manager appears to be active */\n\t\tem_active = g_memory_ptr[tool_start + 6*4] +\n\t\t\t\t(g_memory_ptr[tool_start + 6*4 + 1] << 8);\n\t\tif(g_warp_pointer) {\n\t\t\tem_active = 0;\n\t\t}\n\t}\n\n\t//em_active = 0;\t\t// HACK!\n\ta2_x = g_mouse_a2_x;\n\ta2_y = g_mouse_a2_y;\n\n\tif(em_active) {\n\t\tif((!g_hide_pointer) && (g_num_lines_prev_superhires == 200) &&\n\t\t\t\t!g_unhide_pointer) {\n\t\t\t/* if super-hires and forcing tracking, then hide */\n\t\t\tg_hide_pointer = 1;\n\t\t}\n\t\tif(g_adb_mouse_coord == 0) {\n\t\t\t/* update x coord values */\n\t\t\tg_slow_memory_ptr[0x47c] = a2_x & 0xff;\n\t\t\tg_slow_memory_ptr[0x57c] = a2_x >> 8;\n\t\t\tg_memory_ptr[0x47c] = a2_x & 0xff;\n\t\t\tg_memory_ptr[0x57c] = a2_x >> 8;\n\n\t\t\tg_slow_memory_ptr[0x10190] = a2_x & 0xff;\n\t\t\tg_slow_memory_ptr[0x10192] = a2_x >> 8;\n\t\t} else {\n\t\t\tg_slow_memory_ptr[0x4fc] = a2_y & 0xff;\n\t\t\tg_slow_memory_ptr[0x5fc] = a2_y >> 8;\n\t\t\tg_memory_ptr[0x4fc] = a2_y & 0xff;\n\t\t\tg_memory_ptr[0x5fc] = a2_y >> 8;\n\n\t\t\tg_slow_memory_ptr[0x10191] = a2_y & 0xff;\n\t\t\tg_slow_memory_ptr[0x10193] = a2_y >> 8;\n\t\t}\n\t} else {\n\t\tif(g_hide_pointer && !g_warp_pointer) {\n\t\t\tg_hide_pointer = 0;\n\t\t}\n\t}\n\n\tret = ((!mouse_button) << 7) + ((delta_x | delta_y) & 0x7f);\n\tif(g_adb_mouse_coord) {\n\t\tg_mouse_a2_button = mouse_button;\t/* y coord has button*/\n\t} else {\n\t\tret |= 0x80;\t/* mouse button not down on x coord rd */\n\t}\n\n\ta2_x += delta_x;\n\ta2_y += delta_y;\n\tg_mouse_a2_x = a2_x;\n\tg_mouse_a2_y = a2_y;\n\tif(g_mouse_fifo_pos) {\n\t\tif((target_x == a2_x) && (target_y == a2_y) &&\n\t\t\t\t\t(g_mouse_a2_button == mouse_button)) {\n\t\t\tg_mouse_fifo_pos--;\n\t\t}\n\t}\n\n\n\tadb_printf(\"Rd c024, mouse is_y:%d, %02x, vbl:%08x, dfcyc:%016llx, em:\"\n\t\t\"%d\\n\", g_adb_mouse_coord, ret, g_vbl_count, dfcyc, em_active);\n\tadb_printf(\"...mouse targ_x:%d,%d delta_x,y:%d,%d fifo:%d, a2:%d,%d\\n\",\n\t\ttarget_x, target_y, delta_x, delta_y, g_mouse_fifo_pos,\n\t\ta2_x, a2_y);\n\tadb_printf(\"   post a2_x:%02x,%02x,%02x,%02x\\n\",\n\t\tg_slow_memory_ptr[0x10190], g_slow_memory_ptr[0x10192],\n\t\tg_slow_memory_ptr[0x10191], g_slow_memory_ptr[0x10193]);\n\n\tif((g_mouse_fifo_pos == 0) && (g_mouse_fifo[0].x == a2_x) &&\n\t\t\t(g_mouse_fifo[0].y == a2_y) &&\n\t\t\t((g_mouse_fifo[0].buttons & 1) == g_mouse_a2_button)) {\n\t\tg_adb_mouse_valid_data = 0;\n\t\tadb_clear_mouse_int();\n\t}\n\n\tg_adb_mouse_coord = !g_adb_mouse_coord;\n\treturn ret;\n}\n\nvoid\nmouse_compress_fifo(dword64 dfcyc)\n{\n\tdword64\tddelta;\n\tint\tpos;\n\n\t/* The mouse fifo exists so that fast button changes don't get lost */\n\t/*  if the emulator lags behind the mouse events */\n\t/* But the FIFO means really old mouse events are saved if */\n\t/*  the emulated code isn't looking at the mouse registers */\n\t/* This routine compresses all mouse events > 0.5 seconds old */\n\n\tddelta = (500LL*1000) << 16;\n\tfor(pos = g_mouse_fifo_pos; pos >= 1; pos--) {\n\t\tif((g_mouse_fifo[pos].dfcyc + ddelta) < dfcyc) {\n\t\t\t/* Remove this entry */\n\t\t\tadb_printf(\"Old mouse FIFO pos %d removed\\n\", pos);\n\t\t\tg_mouse_fifo_pos = pos - 1;\n\t\t\tcontinue;\n\t\t}\n\t\t/* Else, stop searching the FIFO */\n\t\tbreak;\n\t}\n}\n\nvoid\nadb_paste_update_state()\n{\n\tint\trd_pos, wr_pos;\n\n\trd_pos = g_kbd_paste_rd_pos;\n\twr_pos = g_kbd_paste_wr_pos;\n\tif(rd_pos >= wr_pos) {\n\t\tg_kbd_paste_rd_pos = 0;\n\t\tg_kbd_paste_wr_pos = 0;\n\t\treturn;\n\t}\n\tif(g_kbd_chars_buffered == 0) {\n\t\tg_kbd_buf[0] = g_kbd_paste_buf[rd_pos];\n\t\tg_kbd_paste_rd_pos = rd_pos + 1;\n\t\tg_kbd_chars_buffered = 1;\n\t}\n}\n\nint\nadb_paste_add_buf(word32 key)\n{\n\tword32\tlast_key;\n\tint\tpos;\n\n\t// Applesoft reads $C000 to check for ctrl-C after each statement.\n\t//  So if we dropped all chars into g_kbd_buf[], we could end up\n\t//  losing chars due to multiple reads of $C000 without writes to $C010\n\t//  causing g_kbd_read_no_update to toss a paste char.\n\t// Instead, have a separate buffer, and when g_kbd_chars_buffered==0,\n\t//  copy one paste char to g_kbd_buf[0].  This also solves a problem\n\t//  where Applesoft is doing: 10 GOTO 10 and it needs to see a Ctrl-C\n\t//  to stop--but a paste buffer is in the way.\n\t// But, now pressing keys while a paste is pending causes those keys\n\t//  to take priority during the paste.\n\tlast_key = g_kbd_paste_last_key;\n\tg_kbd_paste_last_key = key;\n\tif(key == 10) {\t\t\t// \\n, newline on Unix\n\t\tkey = 13;\t\t// \\r, return\n\t\tif(last_key == 13) {\n\t\t\tkey = 0;\t// CR, then LF--eat the LF\n\t\t}\n\t}\n\tif((key == 0) || (key >= 0x80)) {\n\t\treturn 0;\t\t// Just skip these keys\n\t}\n\tpos = g_kbd_paste_wr_pos;\n\tif(pos >= MAX_KBD_PASTE_BUF) {\n\t\treturn 1;\n\t}\n\tg_kbd_paste_buf[pos] = key | 0x80;\n\tg_kbd_paste_wr_pos = pos + 1;\n\n\tadb_paste_update_state();\n\treturn 0;\n}\n\nvoid\nadb_key_event(int a2code, int is_up)\n{\n\tword32\tspecial, vbl_count;\n\tint\tkey, hard_key, pos, tmp_ascii, ascii;\n\n\tif(is_up) {\n\t\tadb_printf(\"adb_key_event, key:%02x, is up, g_key_down: %02x\\n\",\n\t\t\ta2code, g_key_down);\n\t}\n\n\tif(a2code < 0 || a2code > 0x7f) {\n\t\thalt_printf(\"add_key_event: a2code: %04x!\\n\", a2code);\n\t\treturn;\n\t}\n\n\tif(!is_up && a2code == 0x35) {\n\t\t/* ESC pressed, see if ctrl & cmd key down */\n\t\tif(CTRL_DOWN && CMD_DOWN) {\n\t\t\t/* Desk mgr int */\n\t\t\tprintf(\"Desk mgr int!\\n\");\n\n\t\t\tg_adb_interrupt_byte |= 0x20;\n\t\t\tadb_add_data_int();\n\t\t}\n\t}\n\n\t/* convert key to ascii, if possible */\n\thard_key = 0;\n\tif(g_a2_key_to_ascii[a2code][1] & 0xef00) {\n\t\t/* special key */\n\t} else {\n\t\t/* we have ascii */\n\t\thard_key = 1;\n\t}\n\n\tpos = 1;\n\tascii = g_a2_key_to_ascii[a2code][1];\n\tif(CAPS_LOCK_DOWN && (ascii >= 'a') && (ascii <= 'z')) {\n\t\tpos = 2;\n\t\tif(SHIFT_DOWN && (g_adb_mode & 0x40)) {\n\t\t\t/* xor shift mode--capslock and shift == lowercase */\n\t\t\tpos = 1;\n\t\t}\n\t} else if(SHIFT_DOWN) {\n\t\tpos = 2;\n\t}\n\n\tascii = g_a2_key_to_ascii[a2code][pos];\n\tif(CTRL_DOWN) {\n\t\ttmp_ascii = g_a2_key_to_ascii[a2code][3];\n\t\tif(tmp_ascii >= 0) {\n\t\t\tascii = tmp_ascii;\n\t\t}\n\t}\n\tkey = (ascii & 0x7f) + 0x80;\n\n\tspecial = (ascii >> 8) & 0xff;\n\tif(ascii < 0) {\n\t\tprintf(\"ascii1: %d, a2code: %02x, pos: %d\\n\", ascii,a2code,pos);\n\t\tascii = 0;\n\t\tspecial = 0;\n\t}\n\n\tif(!is_up) {\n\t\tif(hard_key) {\n\t\t\tg_kbd_buf[g_kbd_chars_buffered] = key;\n\t\t\tg_kbd_chars_buffered++;\n\t\t\tif(g_kbd_chars_buffered >= MAX_KBD_BUF) {\n\t\t\t\tg_kbd_chars_buffered = MAX_KBD_BUF - 1;\n\t\t\t}\n\t\t\tg_key_down = 1;\n\t\t\tg_a2code_down = a2code;\n\n\t\t\t/* first key down, set up autorepeat */\n\t\t\tvbl_count = g_vbl_count;\n\t\t\tg_adb_repeat_vbl = vbl_count + g_adb_repeat_delay;\n\t\t\tif(g_adb_repeat_delay == 0) {\n\t\t\t\tg_key_down = 0;\n\t\t\t}\n\t\t\tg_hard_key_down = 1;\n\t\t}\n\n\t\tg_c025_val = g_c025_val | special;\n\t\tadb_printf(\"new c025_or: %02x\\n\", g_c025_val);\n\t} else {\n\t\tif(hard_key && (a2code == g_a2code_down)) {\n\t\t\tg_hard_key_down = 0;\n\t\t\t/* Turn off repeat */\n\t\t\tg_key_down = 0;\n\t\t}\n\n\t\tg_c025_val = g_c025_val & (~ special);\n\t\tadb_printf(\"new c025_and: %02x\\n\", g_c025_val);\n\t}\n\n\tif(g_key_down) {\n\t\tg_c025_val = g_c025_val & (~0x20);\n\t} else {\n\t\t/* If no hard key down, set update mod latch */\n\t\tg_c025_val = g_c025_val | 0x20;\n\t}\n\n}\n\nword32\nadb_read_c000()\n{\n\tword32\tvbl_count;\n\n\tif( ((g_kbd_buf[0] & 0x80) == 0) && (g_key_down == 0)) {\n\t\t/* nothing happening, just get out */\n\t\treturn g_kbd_buf[0];\n\t}\n\tif(g_kbd_buf[0] & 0x80) {\n\t\t/* got one */\n\t\tif((g_kbd_read_no_update++ > 5) && (g_kbd_chars_buffered > 1)) {\n\t\t\t/* read 5 times, keys pending, let's move it along */\n\t\t\tprintf(\"Read %02x %d times, tossing\\n\", g_kbd_buf[0],\n\t\t\t\t\tg_kbd_read_no_update);\n\t\t\tadb_access_c010();\n\t\t}\n\t} else {\n\t\tvbl_count = g_vbl_count;\n\t\tif(g_key_down && (vbl_count >= g_adb_repeat_vbl)) {\n\t\t\t/* repeat the g_key_down */\n\t\t\tg_c025_val |= 0x8;\n\t\t\tadb_key_event(g_a2code_down, 0);\n\t\t\tg_adb_repeat_vbl = vbl_count + g_adb_repeat_rate;\n\t\t}\n\t}\n\n\treturn g_kbd_buf[0];\n}\n\nword32\nadb_access_c010()\n{\n\tint\ttmp;\n\tint\ti;\n\n\tg_kbd_read_no_update = 0;\n\n\ttmp = g_kbd_buf[0] & 0x7f;\n\tg_kbd_buf[0] = tmp;\n\n\ttmp = tmp | (g_hard_key_down << 7);\n\tif(g_kbd_chars_buffered) {\n\t\tfor(i = 1; i < g_kbd_chars_buffered; i++) {\n\t\t\tg_kbd_buf[i - 1] = g_kbd_buf[i];\n\t\t}\n\t\tg_kbd_chars_buffered--;\n\t\tif(g_kbd_chars_buffered == 0) {\n\t\t\tadb_paste_update_state();\n\t\t}\n\t}\n\n\tg_c025_val = g_c025_val & (~ (0x08));\n\n\treturn tmp;\n}\n\nword32\nadb_read_c025()\n{\n\treturn\tg_c025_val;\n}\n\nint\nadb_is_cmd_key_down()\n{\n\treturn\tCMD_DOWN;\n}\n\nint\nadb_is_option_key_down()\n{\n\treturn\tOPTION_DOWN;\n}\n\nvoid\nadb_increment_speed()\n{\n\tconst char *str;\n\n\tg_limit_speed++;\n\tif(g_limit_speed > 3) {\n\t\tg_limit_speed = 0;\n\t}\n\n\tstr = \"\";\n\tswitch(g_limit_speed) {\n\tcase 0:\n\t\tstr = \"...as fast as possible!\";\n\t\tbreak;\n\tcase 1:\n\t\tstr = \"...1.024MHz!\";\n\t\tbreak;\n\tcase 2:\n\t\tstr = \"...2.8MHz!\";\n\t\tbreak;\n\tcase 3:\n\t\tstr = \"...8.0MHz!\";\n\t\tbreak;\n\t}\n\tprintf(\"Toggling g_limit_speed to %d%s\\n\", g_limit_speed, str);\n}\n\nvoid\nadb_update_c025_mask(Kimage *kimage_ptr, word32 new_c025_val, word32 mask)\n{\n\t// Called by *driver.c host drivers to handle focus changes and\n\t//  capslock state (so if capslock is on, we leave the window, release\n\t//  capslock, then reenter the window, we update things properly).\n\tif(kimage_ptr == &g_mainwin_kimage) {\n\t\tg_c025_val = (g_c025_val & (~mask)) | new_c025_val;\n\t} else {\n\t\tkimage_ptr->c025_val = (kimage_ptr->c025_val & (~mask)) |\n\t\t\t\t\t\t\t\tnew_c025_val;\n\t}\n}\n\nint\nadb_ascii_to_a2code(int unicode_c, int a2code, int *shift_down_ptr)\n{\n\tint\ti;\n\n\tswitch(unicode_c) {\n\tcase 0x00a3:\t\t// British pound\n\t\tunicode_c = '#';\n\t\tbreak;\n\tcase 0x00e0:\t\t// a with left accent\n\t\tunicode_c = '@';\n\t\tbreak;\n\tcase 0x00b0:\t\t// degrees (French)\n\tcase 0x00c4:\t\t// A with umlaut (German, Swedish)\n\tcase 0x00a1:\t\t// ! upside down (Spanish)\n\tcase 0x00c6:\t\t// AE (Danish)\n\t\tunicode_c = '[';\n\t\tbreak;\n\tcase 0x00e7:\t\t// c with tail (French/Italian)\n\tcase 0x00d1:\t\t// N with ~ (Spanish)\n\tcase 0x00d6:\t\t// O with umlaut (German, Swedish)\n\tcase 0x00d8:\t\t// O with slash (Danish)\n\t\tunicode_c = '\\\\';\n\t\tbreak;\n\tcase 0x00a7:\t\t// ss like thing (French)\n\tcase 0x00dc:\t\t// U with umlaut\n\tcase 0x00bf:\t\t// ? upside down (Spanish)\n\tcase 0x00c5:\t\t// A with circle (Danish)\n\t//case 0x00e9:\t\t// e with right accent (Italian)\n\t\tunicode_c = ']';\n\t\tbreak;\n\t//case 0x0000:\t\t// u with left accent (Italian)\n\t//\tunicode_c = '`';\n\t//\tbreak;\n\tcase 0x00e4:\t\t// a with umlaut (german)\n\tcase 0x00e9:\t\t// e with accent (french)\n\tcase 0x0000:\t\t// ae (Danish)\n\t\tunicode_c = '{';\n\t\tbreak;\n\tcase 0x00f6:\t\t// o with umlaut (German/Swedish)\n\tcase 0x00f9:\t\t// u with left accent (French)\n\tcase 0x00f8:\t\t// o with slash (Danish)\n\tcase 0x00f1:\t\t// n with ~ (Spanish)\n\tcase 0x00f2:\t\t// o with ` (Italian)\n\t\tunicode_c = '|';\n\t\tbreak;\n\tcase 0x00e8:\t\t// e with ` (French, Italian)\n\tcase 0x00fc:\t\t// u with umlaut (German)\n\tcase 0x00e5:\t\t// a with circle (Danish/Swedish)\n\t\tunicode_c = '}';\n\t\tbreak;\n\tcase 0x00a8:\t\t// two high dots (French)\n\tcase 0x00ec:\t\t// i with ` (Italian)\n\tcase 0x00df:\t\t// german B thing (German)\n\t\tunicode_c = '~';\n\t\tbreak;\n\t}\n\n\tif(unicode_c > 0x7f) {\n\t\treturn a2code;\t\t\t// Use a2code instead\n\t}\n\tif((g_a2_key_to_ascii[a2code][1] & 0xf000) == 0x1000) {\t// Keypad\n\t\t// Don't remap keypad keys, we need them for Keypad Joystick\n\t\tif((unicode_c >= '0') && (unicode_c <= '9')) {\n\t\t\treturn a2code;\n\t\t}\n\t}\n\n\tfor(i = 0; i < 128; i++) {\n\t\tif(g_a2_key_to_ascii[i][1] == unicode_c) {\t// Not-shifted\n\t\t\t*shift_down_ptr = 0;\n\t\t\treturn g_a2_key_to_ascii[i][0];\n\t\t}\n\t\tif(g_a2_key_to_ascii[i][2] == unicode_c) {\t// Shifted\n\t\t\t*shift_down_ptr = 1;\n\t\t\treturn g_a2_key_to_ascii[i][0];\n\t\t}\n\t}\n\n\treturn a2code;\t\t// Not found, use default a2code\n}\n\nvoid\nadb_physical_key_update(Kimage *kimage_ptr, int raw_a2code, word32 unicode_c,\n\t\tint is_up)\n{\n\tword32\trestore_c025_val, restorek_c025_val;\n\tint\tspecial, ascii_and_type, ascii, new_shift, a2code, other_a2code;\n\n\t/* this routine called by xdriver to pass raw codes--handle */\n\t/*  ucontroller and ADB bus protocol issues here */\n\t/* if autopoll on, pass it on through to c025,c000 regs */\n\t/*  else only put it in kbd reg 3, and pull SRQ if needed */\n\n\tadb_printf(\"adb_phys_key_update: %02x, %d\\n\", raw_a2code, is_up);\n\n\tif((raw_a2code < 0) || (raw_a2code > 0x7f)) {\n\t\thalt_printf(\"raw_a2code: %04x!\\n\", raw_a2code);\n\t\treturn;\n\t}\n\ta2code = raw_a2code;\n\trestore_c025_val = 0;\n\trestorek_c025_val = 0;\n\tif(unicode_c > 0) {\n\t\t// To enable international keyboards, ignore a2code, look up\n\t\t//  what U.S. keycode would be and return that\n\t\tnew_shift = g_c025_val & 1;\n\t\ta2code = adb_ascii_to_a2code(unicode_c, a2code, &new_shift);\n\t\tif(a2code && ((g_c025_val & 1) != new_shift)) {\n\t\t\trestore_c025_val = g_c025_val | 0x100;\n\t\t\trestorek_c025_val = kimage_ptr->c025_val;\n\t\t\tg_c025_val = (g_c025_val & -2) | new_shift;\n\t\t\tkimage_ptr->c025_val = (kimage_ptr->c025_val & -2) |\n\t\t\t\t\t\t\tnew_shift;\n\t\t}\n\t\tif(!is_up) {\n\t\t\tg_rawa2_to_a2code[raw_a2code & 0x7f] = a2code;\n\t\t}\n\t}\n\n\t/* Remap 0x7b-0x7e to 0x3b-0x3e (arrow keys on new mac keyboards) */\n\tif((a2code >= 0x7b) && (a2code <= 0x7e)) {\n\t\ta2code = a2code - 0x40;\n\t}\n\tif(g_adb_swap_command_option) {\n\t\tif(a2code == 0x37) {\t\t// Command?\n\t\t\ta2code = 0x3a;\t\t//  -> Option\n\t\t} else if(a2code == 0x3a) {\t// Option?\n\t\t\ta2code = 0x37;\t\t//  -> Command\n\t\t}\n\t}\n\n\t/* Now check for special keys (function keys, etc) */\n\tascii_and_type = g_a2_key_to_ascii[a2code][1];\n\tspecial = 0;\n\tif((ascii_and_type & 0xf000) == 0x8000) {\n\t\t/* special function key */\n\t\tspecial = ascii_and_type & 0xff;\n\t\tswitch(special) {\n\t\tcase 0x01: /* F1 - remap to cmd */\n\t\t\ta2code = 0x37;\n\t\t\tspecial = 0;\n\t\t\tbreak;\n\t\tcase 0x02: /* F2 - remap to option */\n\t\t\ta2code = 0x3a;\n\t\t\tspecial = 0;\n\t\t\tbreak;\n\t\tcase 0x03: /* F3 - remap to escape for OS/2 */\n\t\t\ta2code = 0x35;\n\t\t\tspecial = 0;\n\t\t\tbreak;\n\t\tcase 0x0c: /* F12 - remap to reset */\n\t\t\ta2code = 0x7f;\n\t\t\tspecial = 0;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* Only process reset requests here */\n\tif((is_up == 0) && (a2code == 0x7f) && CTRL_DOWN) {\n\t\t/* Reset pressed! */\n\t\tprintf(\"Reset pressed since CTRL_DOWN: %d\\n\", CTRL_DOWN);\n\t\tdo_reset();\n\t\treturn;\n\t}\n\n\tif(special && !is_up) {\n\t\tswitch(special) {\n\t\tcase 0x04: /* F4 - Emulator config panel */\n\t\t\tcfg_toggle_config_panel();\n\t\t\tbreak;\n\t\tcase 0x05: /* F5 - Force Refresh */\n\t\t\tg_status_enable = !g_status_enable;\n\t\t\t// video_update() will call video_update_status_enable()\n\t\t\tbreak;\n\t\tcase 0x06: /* F6 - emulator speed */\n\t\t\tif(SHIFT_DOWN) {\n\t\t\t\thalt2_printf(\"Shift-F6 pressed\\n\");\n\t\t\t} else {\n\t\t\t\tadb_increment_speed();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x07: /* F7 - toggle debugger window, SHIFT:fast disk */\n\t\t\tif(SHIFT_DOWN) {\n\t\t\t\tg_fast_disk_emul_en = !g_fast_disk_emul_en;\n\t\t\t\tiwm_update_fast_disk_emul(g_fast_disk_emul_en);\n\t\t\t\tprintf(\"g_fast_disk_emul_en is now %d\\n\",\n\t\t\t\t\t\t\tg_fast_disk_emul_en);\n\t\t\t} else {\n\t\t\t\tvideo_set_active(&g_debugwin_kimage,\n\t\t\t\t\t\t!g_debugwin_kimage.active);\n\t\t\t\tprintf(\"Toggled debugger window to:%d\\n\",\n\t\t\t\t\t\tg_debugwin_kimage.active);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x08: /* F8 - warp pointer */\n\t\t\tg_warp_pointer = !g_warp_pointer;\n\t\t\tg_hide_pointer = g_warp_pointer;\n\t\t\tprintf(\"New warp:%d, new hide:%d\\n\", g_warp_pointer,\n\t\t\t\t\t\t\t\tg_hide_pointer);\n\t\t\tbreak;\n\t\tcase 0x09: /* F9 - swap paddles */\n\t\t\tif(CTRL_DOWN) {\n\t\t\t\tg_adb_copy_requested = 1;\n\t\t\t} else if(SHIFT_DOWN) {\n\t\t\t\tg_swap_paddles = !g_swap_paddles;\n\t\t\t\tprintf(\"Swap paddles is now: %d\\n\",\n\t\t\t\t\t\t\tg_swap_paddles);\n\t\t\t} else {\n\t\t\t\tg_invert_paddles = !g_invert_paddles;\n\t\t\t\tprintf(\"Invert paddles is now: %d\\n\",\n\t\t\t\t\t\t\tg_invert_paddles);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x0a: /* F10 - nothing */\n\t\t\tbreak;\n\t\tcase 0x0b: /* F11 - full screen */\n\t\t\tbreak;\n\t\t}\n\n\t\treturn;\n\t}\n\n\tif(kimage_ptr == &g_debugwin_kimage) {\n\t\tdebugger_key_event(kimage_ptr, a2code, is_up);\n\t\tif(restore_c025_val) {\n\t\t\tg_c025_val = restore_c025_val & 0xff;\t// Restore shift\n\t\t\tkimage_ptr->c025_val = restorek_c025_val;\n\t\t}\n\t\treturn;\n\t}\n\n\t/* Handle Keypad Joystick here partly...if keypad key pressed */\n\t/*  while in Keypad Joystick mode, do not pass it on as a key press */\n\tif((ascii_and_type & 0xff00) == 0x1000) {\n\t\t/* Keep track of keypad number keys being up or down even */\n\t\t/*  if joystick mode isn't keypad.  This avoid funny cases */\n\t\t/*  if joystick mode is changed while a key is pressed */\n\t\tascii = ascii_and_type & 0xff;\n\t\tif(ascii > 0x30 && ascii <= 0x39) {\n\t\t\tg_keypad_key_is_down[ascii - 0x30] = !is_up;\n\t\t}\n\t\tif(g_joystick_type == 0) {\n\t\t\t/* If Joystick type is keypad, then do not let these */\n\t\t\t/*  keypress pass on further, except for cmd/opt */\n\t\t\tif(ascii == 0x30) {\n\t\t\t\t/* remap '0' to cmd */\n\t\t\t\ta2code = 0x37;\n\t\t\t} else if(ascii == 0x2e || ascii == 0x2c) {\n\t\t\t\t/* remap '.' and ',' to option */\n\t\t\t\ta2code = 0x3a;\n\t\t\t} else {\n\t\t\t\t/* Just ignore it in this mode */\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tadb_maybe_virtual_key_update(a2code, is_up);\n\tother_a2code = g_rawa2_to_a2code[raw_a2code & 0x7f];\n\tif((other_a2code >= 0) && is_up) {\n\t\tadb_maybe_virtual_key_update(other_a2code, is_up);\n\t\tg_rawa2_to_a2code[raw_a2code & 0x7f] = -1;\n\t}\n\n\tif(restore_c025_val) {\n\t\tg_c025_val = restore_c025_val & 0xff;\t\t// Restore shift\n\t}\n}\n\nvoid\nadb_maybe_virtual_key_update(int a2code, int is_up)\n{\n\tint\tautopoll;\n\n\tautopoll = 1;\n\tif(g_adb_mode & 1) {\n\t\t/* autopoll is explicitly off */\n\t\tautopoll = 0;\n\t}\n\tif(g_kbd_dev_addr != g_kbd_ctl_addr) {\n\t\t/* autopoll is off because ucontroller doesn't know kbd moved */\n\t\tautopoll = 0;\n\t}\n\tif(g_config_control_panel) {\n\t\t/* always do autopoll */\n\t\tautopoll = 1;\n\t}\n\n\tif(is_up) {\n\t\tif(!autopoll) {\n\t\t\t/* no auto keys, generate SRQ! */\n\t\t\tadb_kbd_reg0_data(a2code, is_up);\n\t\t} else {\n\t\t\tadb_virtual_key_update(a2code, is_up);\n\t\t}\n\t} else {\n\t\tif(!autopoll) {\n\t\t\t/* no auto keys, generate SRQ! */\n\t\t\tadb_kbd_reg0_data(a2code, is_up);\n\t\t} else {\n\t\t\t/* was up, now down */\n\t\t\tadb_virtual_key_update(a2code, is_up);\n\t\t}\n\t}\n}\n\nvoid\nadb_virtual_key_update(int a2code, int is_up)\n{\n\tword32\tmask;\n\tint\tbitpos;\n\tint\ti;\n\n\tadb_printf(\"Virtual handle a2code: %02x, is_up: %d\\n\", a2code, is_up);\n\n\tif(a2code < 0 || a2code > 0x7f) {\n\t\thalt_printf(\"a2code: %04x!\\n\", a2code);\n\t\treturn;\n\t}\n\n\ti = (a2code >> 5) & 3;\n\tbitpos = a2code & 0x1f;\n\tmask = (1 << bitpos);\n\n\tif(is_up) {\n\t\tif(g_virtual_key_up[i] & mask) {\n\t\t\t/* already up, do nothing */\n\t\t} else {\n\t\t\tg_virtual_key_up[i] |= mask;\n\t\t\tadb_key_event(a2code, is_up);\n\t\t}\n\t} else {\n\t\tif(g_virtual_key_up[i] & mask) {\n\t\t\tg_virtual_key_up[i] &= (~mask);\n\t\t\tadb_key_event(a2code, is_up);\n\t\t}\n\t}\n}\n\n#if 0\nvoid\nadb_all_keys_up()\n{\n\tword32\tmask;\n\tint\ti, j;\n\n\tfor(i = 0; i < 4; i++) {\n\t\tfor(j = 0; j < 32; j++) {\n\t\t\tmask = 1 << j;\n\t\t\tif((g_virtual_key_up[i] & mask) == 0) {\n\t\t\t\t/* create key-up event */\n\t\t\t\tadb_physical_key_update(i*32 + j, 1);\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\nvoid\nadb_kbd_repeat_off()\n{\n\tg_key_down = 0;\n}\n\nvoid\nadb_mainwin_focus(int has_focus)\n{\n\tg_adb_mainwin_has_focus = has_focus;\n\t// printf(\"g_adb_mainwin_has_focus=%d\\n\", g_adb_mainwin_has_focus);\n\tif(!has_focus) {\n\t\tadb_nonmain_check();\n\t}\n}\n"
  },
  {
    "path": "upstream/kegs/src/applesingle.c",
    "content": "const char rcsid_applesing_c[] = \"@(#)$KmKId: applesingle.c,v 1.5 2021-12-19 04:14:31+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2021 by Kent Dickey\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// From Wikipedia AppleSingle_and_AppleDouble_formats):\n// https://web.archive.org/web/20180311140826/http://kaiser-edv.de/\n//\t\tdocuments/AppleSingle_AppleDouble.pdf\n// All fields in an Applesingle file are in big-endian format\n// ProDOS forked files are described in Technote tn-pdos-025.\n\n#include \"defc.h\"\n\nword32\napplesingle_get_be32(const byte *bptr)\n{\n\treturn (bptr[0] << 24) | (bptr[1] << 16) | (bptr[2] << 8) | bptr[3];\n}\n\nword32\napplesingle_get_be16(const byte *bptr)\n{\n\treturn (bptr[0] << 8) | bptr[1];\n}\n\nvoid\napplesingle_set_be32(byte *bptr, word32 val)\n{\n\tbptr[3] = val;\n\tbptr[2] = val >> 8;\n\tbptr[1] = val >> 16;\n\tbptr[0] = val >> 24;\n}\n\nvoid\napplesingle_set_be16(byte *bptr, word32 val)\n{\n\tbptr[1] = val;\n\tbptr[0] = val >> 8;\n}\n\nword32\napplesingle_map_from_prodos(Disk *dsk, Dynapro_file *fileptr, int do_file_data)\n{\n\tbyte\t*fptr, *bptr;\n\tword32\tdata_size, resource_size, rounded_data_size, num_entries;\n\tword32\trounded_resource_size, hdr_size, max_size, hdr_pos, data_pos;\n\tword32\tblock, key_block, ret, good, has_finder_info, offset;\n\tint\tlevel;\n\tint\ti, j;\n\n#if 0\n\tprintf(\"applesingle_map_from_prodos: %s do_file_data:%d\\n\",\n\t\t\t\t\tfileptr->unix_path, do_file_data);\n#endif\n\n\t// First, handle mini directory describing the forks\n\tkey_block = fileptr->key_block;\n\tret = dynapro_map_one_file_block(dsk, fileptr, key_block, 1U << 30, 0);\n\tif(ret == 0) {\n\t\tprintf(\" dynapro_map_one_file_block ret 0, applesingle done\\n\");\n\t\treturn 0;\n\t}\n\tbptr = &(dsk->raw_data[key_block << 9]);\n\tdata_size = dynapro_get_word24(&bptr[5]);\n\tresource_size = dynapro_get_word24(&bptr[0x100 + 5]);\n\thas_finder_info = bptr[9] | bptr[27];\n\n\tnum_entries = 1;\t\t// ProDOS info always\n\tif(has_finder_info) {\n\t\tnum_entries++;\n\t}\n\trounded_data_size = data_size;\n\tif(data_size) {\n\t\trounded_data_size = (data_size + 0x200) & -0x200;\n\t\tnum_entries++;\n\t}\n\trounded_resource_size = resource_size;\n\tif(resource_size) {\n\t\trounded_resource_size = (resource_size + 0x200) & -0x200;\n\t\tnum_entries++;\n\t}\n\thdr_size = 256;\n\tmax_size = hdr_size + rounded_resource_size + rounded_data_size;\n\tfileptr->buffer_ptr = 0;\n\tfptr = 0;\n\tif(do_file_data) {\n\t\tfptr = calloc(1, max_size + 0x200);\n#if 0\n\t\tprintf(\" fptr:%p, max_size:%08x, res:%08x, data:%08x\\n\",\n\t\t\tfptr, max_size, rounded_resource_size,\n\t\t\trounded_data_size);\n#endif\n\t}\n\n\t// From now on, errors cannot return without free'ing fptr\n\tgood = 1;\n\tif(resource_size) {\n\t\tblock = dynapro_get_word16(&bptr[0x100 + 1]);\n\t\tlevel = bptr[0x100];\n\t\tif(fptr) {\n\t\t\tfileptr->buffer_ptr = fptr + 256;\n\t\t}\n\t\tret = dynapro_map_file_blocks(dsk, fileptr, block, level, 0,\n\t\t\t\t\t\t\t\tresource_size);\n\t\tif(ret == 0) {\n\t\t\tgood = 0;\n\t\t}\n\t}\n\n\tif(data_size) {\n\t\tblock = dynapro_get_word16(&bptr[1]);\n\t\tlevel = bptr[0];\n\t\tif(fptr) {\n\t\t\tfileptr->buffer_ptr = fptr + 256 +\n\t\t\t\t\t\t\trounded_resource_size;\n\t\t}\n\t\tret = dynapro_map_file_blocks(dsk, fileptr, block, level, 0,\n\t\t\t\t\t\t\t\tdata_size);\n\t\tif(ret == 0) {\n\t\t\tgood = 0;\n\t\t}\n\t}\n\n\tfileptr->buffer_ptr = 0;\n\n\t// Now prepare the header\n\tif(fptr) {\n\t\tapplesingle_set_be32(&fptr[0], 0x00051600);\t// Magic\n\t\tapplesingle_set_be32(&fptr[4], 0x00020000);\t// Version\n\t\tapplesingle_set_be16(&fptr[24], num_entries);\t// Version\n\t\thdr_pos = 26;\n\t\tdata_pos = 192;\n\n\t\t// First do ProDOS entry\n\t\tapplesingle_set_be32(&fptr[hdr_pos + 0], 11);\t// ProDOS Info\n\t\tapplesingle_set_be32(&fptr[hdr_pos + 4], data_pos);\n\t\tapplesingle_set_be32(&fptr[hdr_pos + 8], 8);\n\n\t\tapplesingle_set_be16(&fptr[data_pos + 0], 0xc3);\n\t\tapplesingle_set_be16(&fptr[data_pos + 2], fileptr->file_type);\n\t\tapplesingle_set_be32(&fptr[data_pos + 4], fileptr->aux_type);\n\t\thdr_pos += 12;\n\t\tdata_pos += 8;\n\n\t\t// Then do FinderInfo\n\t\tif(has_finder_info) {\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 0], 9);\t//Finder\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 4], data_pos);\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 8], 32);\n\n\t\t\tfor(i = 0; i < 2; i++) {\n\t\t\t\toffset = bptr[9 + 18*i];\n\t\t\t\tif(!offset) {\n\t\t\t\t\tcontinue;\t\t// skip it\n\t\t\t\t}\n\t\t\t\toffset = ((offset - 1) & 1) * 8;\n\t\t\t\tfor(j = 0; j < 9; j++) {\n\t\t\t\t\tfptr[data_pos + offset + j] =\n\t\t\t\t\t\tbptr[10 + 18*i + j];\n\t\t\t\t}\n\t\t\t}\n\t\t\thdr_pos += 12;\n\t\t\tdata_pos += 32;\n\t\t}\n\n\t\tif(data_pos >= 256) {\n\t\t\tprintf(\"data_pos:%08x is too big\\n\", data_pos);\n\t\t\tgood = 0;\n\t\t}\n\n\t\t// First, do data fork\n\t\tif(data_size) {\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 0], 1);\t// Data\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 4],\n\t\t\t\t\t\t256 + rounded_resource_size);\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 8], data_size);\n\t\t\thdr_pos += 12;\n\t\t}\n\t\t// Then do resource fork\n\t\tif(resource_size) {\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 0], 2);\t// Rsrc\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 4], 256);\n\t\t\tapplesingle_set_be32(&fptr[hdr_pos + 8], resource_size);\n\t\t\thdr_pos += 12;\n\t\t}\n\t\tif(hdr_pos > 192) {\n\t\t\tprintf(\"hdr:%08x stomped on data\\n\", hdr_pos);\n\t\t\tgood = 0;\n\t\t}\n\t\tif(good) {\n\t\t\tret = dynapro_write_to_unix_file(fileptr->unix_path,\n\t\t\t\tfptr, 256 + rounded_resource_size + data_size);\n\t\t\tif(ret == 0) {\n\t\t\t\tgood = 0;\n\t\t\t}\n\t\t}\n\n\t\tfree(fptr);\n\t}\n\n\t// printf(\"applesingle_map_from_prodos done, good:%d\\n\", good);\n\treturn good;\n}\n\nword32\napplesingle_from_unix(Disk *dsk, Dynapro_file *fileptr, byte *fptr,\n\t\t\t\t\t\t\t\tdword64 dsize)\n{\n\tbyte\t*bptr, *tptr;\n\tword32\tkey_block, blocks_used, entry_id, blocks_out, offset, length;\n\tword32\tmagic, version, hdr_pos, did_fork;\n\tint\tnum_entries;\n\tint\ti;\n\n\t// Return 0 if anything is wrong with the .applesingle file\n\t// Otherwise, return (blocks_used << 16) | (key_block & 0xffff)\n\n#if 0\n\tprintf(\"applesingle_from_unix %s, size:%08llx\\n\", fileptr->unix_path,\n\t\t\t\t\t\t\t\tdsize);\n#endif\n\n\tkey_block = fileptr->key_block;\n\tbptr = &(dsk->raw_data[key_block << 9]);\n\n\tif(dsize < 50) {\n\t\tprintf(\"Applesingle is too small\\n\");\n\t\treturn 0;\n\t}\n\tmagic = applesingle_get_be32(&fptr[0]);\n\tversion = applesingle_get_be32(&fptr[4]);\n\tnum_entries = applesingle_get_be16(&fptr[24]);\n\tif((magic != 0x00051600) || (version < 0x00020000)) {\n\t\tprintf(\"Bad Applesingle magic number is: %08x, vers:%08x\\n\",\n\t\t\t\t\tmagic, version);\n\t\treturn 0;\n\t}\n\thdr_pos = 26;\n\tblocks_used = 1;\n\tdid_fork = 0;\n\t// printf(\" num_entries:%d\\n\", num_entries);\n\tfor(i = 0; i < num_entries; i++) {\n\t\tif((hdr_pos + 24) > dsize) {\n\t\t\tprintf(\"Applesingle header exceeds file size i:%d of \"\n\t\t\t\t\"%d, hdr_pos:%04x dsize:%08llx\\n\", i,\n\t\t\t\tnum_entries, hdr_pos, dsize);\n\t\t\treturn 0;\n\t\t}\n\t\tentry_id = applesingle_get_be32(&fptr[hdr_pos + 0]);\n\t\toffset = applesingle_get_be32(&fptr[hdr_pos + 4]);\n\t\tlength = applesingle_get_be32(&fptr[hdr_pos + 8]);\n#if 0\n\t\tprintf(\" header[%d] at +%04x: id:%d, offset:%08x, len:%08x\\n\",\n\t\t\ti, hdr_pos, entry_id, offset, length);\n#endif\n\t\tif((offset + length) > dsize) {\n\t\t\tprintf(\"Applesingle entry_id:%d exceeds file size\\n\",\n\t\t\t\tentry_id);\n\t\t\treturn 0;\n\t\t}\n\t\tswitch(entry_id) {\n\t\tcase 1:\t\t// Data fork\n\t\tcase 2:\t\t// Resource fork\n\t\t\ttptr = bptr;\n\t\t\tif(entry_id == 2) {\t\t// Resource fork\n\t\t\t\ttptr += 0x100;\n\t\t\t}\n#if 0\n\t\t\tprintf(\" for entry_id %d, offset:%08x, length:%08x, \"\n\t\t\t\t\"fptr:%p\\n\", entry_id, offset, length, fptr);\n#endif\n\t\t\tif(did_fork & (1 << entry_id)) {\n\t\t\t\tprintf(\"fork %d repeated!\\n\", entry_id);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tdid_fork |= (1 << entry_id);\n\t\t\tblocks_out = applesingle_make_prodos_fork(dsk,\n\t\t\t\t\t\tfptr + offset, tptr, length);\n\t\t\tif(blocks_out == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tblocks_used += (blocks_out >> 16);\n\t\t\tbreak;\n\t\tcase 9:\t\t// Finder Info\n\t\t\tif(length < 32) {\n\t\t\t\tprintf(\"Invalid Finder info, len:%d\\n\", length);\n\t\t\t}\n\t\t\tbptr[8] = 0x12;\n\t\t\tbptr[8 + 18] = 0x12;\n\t\t\tbptr[9] = 1;\n\t\t\tbptr[9 + 18] = 2;\n\t\t\tfor(i = 0; i < 16; i++) {\n\t\t\t\tbptr[10 + i] = fptr[offset + i];\n\t\t\t\tbptr[10 + 18 + i] = fptr[offset + 16 + i];\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 11:\t// ProDOS File Info\n\t\t\tfileptr->file_type = fptr[offset + 3];\n\t\t\tfileptr->aux_type = (fptr[offset + 6] << 8) |\n\t\t\t\t\t\t\tfptr[offset + 7];\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\t\t// Ignore it\n\t\t}\n\t\thdr_pos += 12;\n\t}\n\n\tfor(i = 1; i < 3; i++) {\n\t\tif((did_fork >> i) & 1) {\n\t\t\tcontinue;\n\t\t}\n\t\t// Create one block for this fork even though it's length==0\n\t\t// i==1: no data fork; i==2: no resource fork\n\t\tprintf(\" Doing dummy fork, i:%d, fptr:%p\\n\", i, fptr);\n\t\tblocks_out = applesingle_make_prodos_fork(dsk, fptr,\n\t\t\t\t\t\tbptr + ((i & 2) * 0x80), 0);\n\t\tif(blocks_out == 0) {\n\t\t\treturn blocks_out;\n\t\t}\n\t\tblocks_used += (blocks_out >> 16);\n\t}\n\n\tfileptr->eof = 0x200;\n\treturn (blocks_used << 16) | key_block;\n}\n\nword32\napplesingle_make_prodos_fork(Disk *dsk, byte *fptr, byte *tptr, word32 length)\n{\n\tword32\tblock, blocks_out, storage_type;\n\n#if 0\n\tprintf(\"applesingle_make_prodos_fork: fptr:%p, tptr:%p, length:%08x\\n\",\n\t\t\tfptr, tptr, length);\n#endif\n\n\t// Handle creating either a resource or data fork\n\tblock = dynapro_find_free_block(dsk);\n\tif(block == 0) {\n\t\treturn 0;\n\t}\n\tblocks_out = dynapro_fork_from_unix(dsk, fptr, &storage_type, block,\n\t\t\t\t\t\t\t\tlength);\n\n\t// printf(\" dynapro_fork_from_unix ret: %08x, storage:%02x\\n\",\n\t//\t\t\t\t\tblocks_out, storage_type);\n\ttptr[0] = storage_type >> 4;\n\ttptr[1] = blocks_out & 0xff;\t\t// key_block lo\n\ttptr[2] = (blocks_out >> 8) & 0xff;\t// key_block hi\n\ttptr[3] = (blocks_out >> 16) & 0xff;\t// blocks_used lo\n\ttptr[4] = (blocks_out >> 24) & 0xff;\t// blocks_used hi\n\ttptr[5] = length & 0xff;\t\t// eof lo\n\ttptr[6] = (length >> 8) & 0xff;\t\t// eof mid\n\ttptr[7] = (length >> 16) & 0xff;\t// eof hi\n\treturn blocks_out;\n}\n"
  },
  {
    "path": "upstream/kegs/src/clock.c",
    "content": "const char rcsid_clock_c[] = \"@(#)$KmKId: clock.c,v 1.40 2023-09-23 17:51:22+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2022 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include \"defc.h\"\n#include <time.h>\n#ifdef _WIN32\n# include <windows.h>\n# include <mmsystem.h>\n#else\n# include <sys/time.h>\n#endif\n\nextern int Verbose;\nextern word32 g_vbl_count;\nextern int g_rom_version;\nextern int g_config_kegs_update_needed;\n\n#define CLK_IDLE\t\t1\n#define CLK_TIME\t\t2\n#define CLK_INTERNAL\t\t3\n#define CLK_BRAM1\t\t4\n#define CLK_BRAM2\t\t5\n\nint\tg_clk_mode = CLK_IDLE;\nint\tg_clk_read = 0;\nint\tg_clk_reg1 = 0;\n\nextern word32 g_c033_data;\nextern word32 g_c034_val;\n\nbyte\tg_bram[2][256];\nbyte\t*g_bram_ptr = &(g_bram[0][0]);\n\nword32 g_clk_cur_time = 0xa0000000;\nint\tg_clk_next_vbl_update = 0;\n\ndouble\nget_dtime()\n{\n\n#ifdef _WIN32\n\tFILETIME filetime;\n\tdword64\tdlow, dhigh;\n#else\n\tstruct timeval tp1;\n\tdouble\tdsec;\n\tdouble\tdusec;\n#endif\n\tdouble\tdtime;\n\n\t/* Routine used to return actual system time as a double */\n\t/* No routine cares about the absolute value, only deltas--maybe */\n\t/*  take advantage of that in future to increase usec accuracy */\n\n#ifdef _WIN32\n\t//dtime = timeGetTime() / 1000.0;\n\tGetSystemTimePreciseAsFileTime(&filetime);\n\tdlow = filetime.dwLowDateTime;\n\tdhigh = filetime.dwHighDateTime;\n\tdlow = (dhigh << 32) | dlow;\n\tdtime = (double)dlow;\n\tdtime = dtime / (1000*1000*10.0);\t// FILETIME is in 100ns incs\n#else\n\n# ifdef SOLARIS\n\tgettimeofday(&tp1, (void *)0);\n# else\n\tgettimeofday(&tp1, (struct timezone *)0);\n# endif\n\n\tdsec = (double)tp1.tv_sec;\n\tdusec = (double)tp1.tv_usec;\n\n\tdtime = dsec + (dusec / (1000.0 * 1000.0));\n#endif\n\n\treturn dtime;\n}\n\nint\nmicro_sleep(double dtime)\n{\n#ifndef _WIN32\n\tstruct timeval Timer;\n\tint\tret;\n#endif\n\n\tif(dtime <= 0.0) {\n\t\treturn 0;\n\t}\n\tif(dtime >= 1.0) {\n\t\thalt_printf(\"micro_sleep called with %f!!\\n\", dtime);\n\t\treturn -1;\n\t}\n\n#if 0\n\tprintf(\"usleep: %f\\n\", dtime);\n#endif\n\n#ifdef _WIN32\n\tSleep((word32)(dtime * 1000));\n#else\n\tTimer.tv_sec = 0;\n\tTimer.tv_usec = (dtime * 1000000.0);\n\tif( (ret = select(0, 0, 0, 0, &Timer)) < 0) {\n\t\tfprintf(stderr, \"micro_sleep (select) ret: %d, errno: %d\\n\",\n\t\t\tret, errno);\n\t\treturn -1;\n\t}\n#endif\n\treturn 0;\n}\n\nvoid\nclk_bram_zero()\n{\n\tint\ti, j;\n\n\t/* zero out all bram */\n\tfor(i = 0; i < 2; i++) {\n\t\tfor(j = 0; j < 256; j++) {\n\t\t\tg_bram[i][j] = 0;\n\t\t}\n\t}\n\tg_bram_ptr = &(g_bram[0][0]);\n}\n\nvoid\nclk_bram_set(int bram_num, int offset, int val)\n{\n\tif((bram_num < 0) || (bram_num > 2)) {\n\t\tprintf(\"bram_num %d out of range\\n\", bram_num);\n\t\treturn;\n\t}\n\tif((offset < 0) || (offset > 0x100)) {\n\t\tprintf(\"bram offset %05x out of range\\n\", offset);\n\t\treturn;\n\t}\n\tg_bram[bram_num][offset] = val;\n}\n\nvoid\nclk_setup_bram_version()\n{\n\tif(g_rom_version < 3) {\n\t\tg_bram_ptr = (&g_bram[0][0]);\t// ROM 01\n\t} else {\n\t\tg_bram_ptr = (&g_bram[1][0]);\t// ROM 03\n\t}\n}\n\nvoid\nclk_write_bram(FILE *fconf)\n{\n\tint\ti, j, k;\n\n\tfor(i = 0; i < 2; i++) {\n\t\tfprintf(fconf, \"\\n\");\n\t\tfor(j = 0; j < 256; j += 16) {\n\t\t\tfprintf(fconf, \"bram%d[%02x] =\", 2*i + 1, j);\n\t\t\tfor(k = 0; k < 16; k++) {\n\t\t\t\tfprintf(fconf, \" %02x\", g_bram[i][j+k]);\n\t\t\t}\n\t\t\tfprintf(fconf, \"\\n\");\n\t\t}\n\t}\n}\n\nvoid\nupdate_cur_time()\n{\n\tstruct tm *tm_ptr;\n\ttime_t\tcur_time, secs, secs2;\n\n\tcur_time = time(0);\n\n\t/* Figure out the timezone (effectively) by diffing two times. */\n\t/* this is probably not right for a few hours around daylight savings*/\n\t/*  time transition */\n\tsecs2 = mktime(gmtime(&cur_time));\n\ttm_ptr = localtime(&cur_time);\n\tsecs = mktime(tm_ptr);\n\n\tsecs2 = secs2 - secs;\t\t// this is the timezone offset\n#ifdef MAC\n\t/* Mac OS X's mktime function modifies the tm_ptr passed in for */\n\t/*  the CDT timezone and breaks this algorithm.  So on a Mac, we */\n\t/*  will use the tm_ptr->gmtoff member to correct the time */\n\tsecs = secs + tm_ptr->tm_gmtoff;\n#else\n\tsecs = cur_time - secs2;\n\n\tif(tm_ptr->tm_isdst) {\n\t\t/* adjust for daylight savings time */\n\t\tsecs += 3600;\n\t}\n#endif\n\n\t/* add in secs to make date based on Apple Jan 1, 1904 instead of */\n\t/*  Unix's Jan 1, 1970 */\n\t/* So add in 66 years and 17 leap year days (1904 is a leap year) */\n\tsecs += ((66*365) + 17) * (24*3600);\n\n\tg_clk_cur_time = (word32)secs;\n\n\tclk_printf(\"Update g_clk_cur_time to %08x\\n\", g_clk_cur_time);\n\tg_clk_next_vbl_update = g_vbl_count + 5;\n}\n\n/* clock_update called by sim65816 every VBL */\nvoid\nclock_update()\n{\n\t/* Nothing to do */\n}\n\nvoid\nclock_update_if_needed()\n{\n\tint\tdiff;\n\n\tdiff = g_clk_next_vbl_update - g_vbl_count;\n\tif(diff < 0 || diff > 60) {\n\t\t/* Been a while, re-read the clock */\n\t\tupdate_cur_time();\n\t}\n}\n\nvoid\nclock_write_c034(word32 val)\n{\n\tg_c034_val = val & 0x7f;\n\tif((val & 0x80) != 0) {\n\t\tif((val & 0x20) == 0) {\n\t\t\tprintf(\"c034 write not last = 1\\n\");\n\t\t\t/* set_halt(1); */\n\t\t}\n\t\tdo_clock_data();\n\t}\n}\n\n\nvoid\ndo_clock_data()\n{\n\tword32\tmask, read, op;\n\n\tclk_printf(\"In do_clock_data, g_clk_mode: %02x\\n\", g_clk_mode);\n\n\tread = g_c034_val & 0x40;\n\tswitch(g_clk_mode) {\n\tcase CLK_IDLE:\n\t\tg_clk_read = (g_c033_data >> 7) & 1;\n\t\tg_clk_reg1 = (g_c033_data >> 2) & 3;\n\t\top = (g_c033_data >> 4) & 7;\n\t\tif(!read) {\n\t\t\t/* write */\n\t\t\tswitch(op) {\n\t\t\tcase 0x0:\t/* Read/write seconds register */\n\t\t\t\tg_clk_mode = CLK_TIME;\n\t\t\t\tclock_update_if_needed();\n\t\t\t\tbreak;\n\t\t\tcase 0x3:\t/* internal registers */\n\t\t\t\tg_clk_mode = CLK_INTERNAL;\n\t\t\t\tif(g_clk_reg1 & 0x2) {\n\t\t\t\t\t/* extend BRAM read */\n\t\t\t\t\tg_clk_mode = CLK_BRAM2;\n\t\t\t\t\tg_clk_reg1 = (g_c033_data & 7) << 5;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 0x2:\t/* read/write ram 0x10-0x13 */\n\t\t\t\tg_clk_mode = CLK_BRAM1;\n\t\t\t\tg_clk_reg1 += 0x10;\n\t\t\t\tbreak;\n\t\t\tcase 0x4:\t/* read/write ram 0x00-0x0f */\n\t\t\tcase 0x5: case 0x6: case 0x7:\n\t\t\t\tg_clk_mode = CLK_BRAM1;\n\t\t\t\tg_clk_reg1 = (g_c033_data >> 2) & 0xf;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\thalt_printf(\"Bad c033_data in CLK_IDLE: %02x\\n\",\n\t\t\t\t\tg_c033_data);\n\t\t\t}\n\t\t} else {\n\t\t\tprintf(\"clk read from IDLE mode!\\n\");\n\t\t\t/* set_halt(1); */\n\t\t\tg_clk_mode = CLK_IDLE;\n\t\t}\n\t\tbreak;\n\tcase CLK_BRAM2:\n\t\tif(!read) {\n\t\t\t/* get more bits of bram addr */\n\t\t\tif((g_c033_data & 0x83) == 0x00) {\n\t\t\t\t/* more address bits */\n\t\t\t\tg_clk_reg1 |= ((g_c033_data >> 2) & 0x1f);\n\t\t\t\tg_clk_mode = CLK_BRAM1;\n\t\t\t} else {\n\t\t\t\thalt_printf(\"CLK_BRAM2: c033_data: %02x!\\n\",\n\t\t\t\t\t\tg_c033_data);\n\t\t\t\tg_clk_mode = CLK_IDLE;\n\t\t\t}\n\t\t} else {\n\t\t\thalt_printf(\"CLK_BRAM2: clock read!\\n\");\n\t\t\tg_clk_mode = CLK_IDLE;\n\t\t}\n\t\tbreak;\n\tcase CLK_BRAM1:\n\t\t/* access battery ram addr g_clk_reg1 */\n\t\tif(read) {\n\t\t\tif(g_clk_read) {\n\t\t\t\t/* Yup, read */\n\t\t\t\tg_c033_data = g_bram_ptr[g_clk_reg1];\n\t\t\t\tclk_printf(\"Reading BRAM loc %02x: %02x\\n\",\n\t\t\t\t\tg_clk_reg1, g_c033_data);\n\t\t\t} else {\n\t\t\t\thalt_printf(\"CLK_BRAM1: said wr, now read\\n\");\n\t\t\t}\n\t\t} else {\n\t\t\tif(g_clk_read) {\n\t\t\t\thalt_printf(\"CLK_BRAM1: said rd, now write\\n\");\n\t\t\t} else {\n\t\t\t\t/* Yup, write */\n\t\t\t\tclk_printf(\"Writing BRAM loc %02x with %02x\\n\",\n\t\t\t\t\tg_clk_reg1, g_c033_data);\n\t\t\t\tg_bram_ptr[g_clk_reg1] = g_c033_data;\n\t\t\t\tg_config_kegs_update_needed = 1;\n\t\t\t}\n\t\t}\n\t\tg_clk_mode = CLK_IDLE;\n\t\tbreak;\n\tcase CLK_TIME:\n\t\tif(read) {\n\t\t\tif(g_clk_read == 0) {\n\t\t\t\thalt_printf(\"Reading time, but in set mode!\\n\");\n\t\t\t}\n\t\t\tg_c033_data = (g_clk_cur_time >> (g_clk_reg1 * 8)) &\n\t\t\t\t\t\t\t\t\t0xff;\n\t\t\tclk_printf(\"Returning time byte %d: %02x\\n\",\n\t\t\t\tg_clk_reg1, g_c033_data);\n\t\t} else {\n\t\t\t/* Write */\n\t\t\tif(g_clk_read) {\n\t\t\t\thalt_printf(\"Write time, but in read mode!\\n\");\n\t\t\t}\n\t\t\tclk_printf(\"Writing TIME loc %d with %02x\\n\",\n\t\t\t\tg_clk_reg1, g_c033_data);\n\t\t\tmask = 0xff << (8 * g_clk_reg1);\n\n\t\t\tg_clk_cur_time = (g_clk_cur_time & (~mask)) |\n\t\t\t\t((g_c033_data & 0xff) << (8 * g_clk_reg1));\n\t\t}\n\t\tg_clk_mode = CLK_IDLE;\n\t\tbreak;\n\tcase CLK_INTERNAL:\n\t\tif(read) {\n\t\t\tprintf(\"Attempting to read internal reg %02x!\\n\",\n\t\t\t\tg_clk_reg1);\n\t\t} else {\n\t\t\tswitch(g_clk_reg1) {\n\t\t\tcase 0x0:\t/* test register */\n\t\t\t\tif(g_c033_data & 0xc0) {\n\t\t\t\t\tprintf(\"Writing test reg: %02x!\\n\",\n\t\t\t\t\t\tg_c033_data);\n\t\t\t\t\t/* set_halt(1); */\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 0x1:\t/* write protect reg */\n\t\t\t\tclk_printf(\"Writing clk wr_protect: %02x\\n\",\n\t\t\t\t\tg_c033_data);\n\t\t\t\tif(g_c033_data & 0x80) {\n\t\t\t\t\tprintf(\"Stop, wr clk wr_prot: %02x\\n\",\n\t\t\t\t\t\tg_c033_data);\n\t\t\t\t\t/* set_halt(1); */\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\thalt_printf(\"Writing int reg: %02x with %02x\\n\",\n\t\t\t\t\tg_clk_reg1, g_c033_data);\n\t\t\t}\n\t\t}\n\t\tg_clk_mode = CLK_IDLE;\n\t\tbreak;\n\tdefault:\n\t\thalt_printf(\"clk mode: %d unknown!\\n\", g_clk_mode);\n\t\tg_clk_mode = CLK_IDLE;\n\t\tbreak;\n\t}\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/comp_swift",
    "content": "#!/bin/bash\n# $KmKId: comp_swift,v 1.2 2020-12-11 22:58:32+00 kentd Exp $\n\necho \"args are: \" \"$@\"\n/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -c \\\n\t-enable-objc-interop \\\n\t-sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \\\n\t-swift-version 4 -Onone \\\n\t-serialize-debugging-options \\\n\t-import-objc-header Kegs-Bridging-Header.h \\\n\t-module-name Kegs \\\n\t\"$@\"\n\n"
  },
  {
    "path": "upstream/kegs/src/compile_time.c",
    "content": "\nconst char rcsid_compile_time_c[] = \"@(#)$KmKId: compile_time.c,v 1.2 2002-11-14 06:02:44+00 kadickey Exp $\";\n\nchar g_compile_time[] = \"Compiled: \" __DATE__ \" \" __TIME__ ;\n\n"
  },
  {
    "path": "upstream/kegs/src/config.c",
    "content": "const char rcsid_config_c[] = \"@(#)$KmKId: config.c,v 1.169 2025-01-11 23:42:49+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2025 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// g_cfg_slotdrive: 0: not doing file selection at all\n//\t\t\t1-0x7ff: doing file selection for given slot/drive\n//\t\t\t0xfff: doing file selection for ROM or charrom\n#include \"defc.h\"\n#include <stdarg.h>\n#include \"config.h\"\n\n#ifdef _WIN32\n# include \"win_dirent.h\"\n#else\n# include <dirent.h>\n#endif\n\nextern int Verbose;\nextern word32 g_vbl_count;\n\nextern int g_track_bytes_35[];\nextern int g_c031_disk35;\n\nextern int g_cur_a2_stat;\nextern byte *g_slow_memory_ptr;\nextern byte *g_rom_fc_ff_ptr;\nextern byte *g_rom_cards_ptr;\nextern double g_cur_dcycs;\nextern int g_rom_version;\n\nextern word32 g_adb_repeat_vbl;\nextern int g_adb_swap_command_option;\n\nextern int g_limit_speed;\nextern int g_zip_speed_mhz;\nextern int g_force_depth;\nint g_serial_cfg[2] = { 0, 1 };\t\t// Slot 1=0=Real serial (printer?)\n\t\t\t\t\t// Slot 2=1=Virt Modem\nint g_serial_mask[2] = { 0, 0 };\nchar *g_serial_remote_ip[2] = { \"\", \"\" };\t// cfg_init_menus will malloc()\nint g_serial_remote_port[2] = { 9100, 9100 };\nchar *g_serial_device[2] = { \"/dev/tty.USB.0\", \"/dev/tty.USB.1\" };\n\t\t\t\t// cfg_init_menus() will malloc() the above\nint g_serial_win_device[2] = { 0, 0 };\t\t// Disabled\nint g_serial_modem_response_code = 10;\t\t// 10 - 2400\nint g_serial_modem_allow_incoming = 0;\t\t// 1 for BBS'es\nint g_serial_modem_init_telnet = 1;\t\t// 1 for BBS'es\n\nextern word32 g_mem_size_base;\nextern word32 g_mem_size_exp;\nextern int g_video_line_update_interval;\nextern int g_user_halt_bad;\nextern int g_joystick_type;\nextern int g_joystick_scale_factor_x;\nextern int g_joystick_scale_factor_y;\nextern int g_joystick_trim_amount_x;\nextern int g_joystick_trim_amount_y;\nextern int g_swap_paddles;\nextern int g_invert_paddles;\nextern int g_voc_enable;\nextern int g_status_enable;\nextern int g_mainwin_width;\nextern int g_mainwin_height;\nextern int g_mainwin_xpos;\nextern int g_mainwin_ypos;\n\nextern int g_screen_index[];\nextern word32 g_full_refresh_needed;\nextern word32 g_a2_screen_buffer_changed;\n\nextern int g_key_down;\nextern const char g_kegs_version_str[];\n\nint g_config_control_panel = 0;\nchar g_config_kegs_name[1024] = { 0 };\nchar g_cfg_cwd_str[CFG_PATH_MAX] = { 0 };\n\nint g_config_kegs_auto_update = 1;\nint g_config_kegs_update_needed = 0;\nint g_cfg_newdisk_select = 0;\nint g_cfg_newdisk_blocks = 0;\nint g_cfg_newdisk_blocks_default = 140*2;\nint g_cfg_newdisk_type = 1;\nint g_cfg_newdisk_type_default = 1;\nword32 g_cfg_newdisk_slotdrive = 0;\n\nconst char *g_config_kegs_name_list[] = {\n\t\t\"config.kegs\", \"kegs_conf\", \".config.kegs\", 0\n};\n\nint\tg_highest_smartport_unit = -1;\nint\tg_reparse_delay = 0;\nint\tg_user_page2_shadow = 1;\n\nchar\tg_cfg_printf_buf[CFG_PRINTF_BUFSIZE];\nchar\tg_config_kegs_buf[CONF_BUF_LEN];\n\n#define CFG_ERR_BUFSIZE\t\t80\n#define CFG_ERR_MAX\t\t5\n\nint\tg_cfg_err_pos = 0;\nchar\tg_cfg_err_bufs[CFG_ERR_MAX][CFG_ERR_BUFSIZE];\n\nint\tg_cfg_curs_x = 0;\nint\tg_cfg_curs_y = 0;\nint\tg_cfg_curs_inv = 0;\nint\tg_cfg_curs_mousetext = 0;\nint\tg_cfg_screen_changed = 0;\nbyte\tg_cfg_screen[24][80];\n\n#if defined(MAC) || defined(_WIN32)\nint\tg_cfg_ignorecase = 1;\t\t// Ignore case in filenames\n#else\nint\tg_cfg_ignorecase = 0;\n#endif\n\n#define CFG_MAX_OPTS\t34\n#define CFG_OPT_MAXSTR\t100\n\nint g_cfg_opts_vals[CFG_MAX_OPTS];\nchar g_cfg_opts_str[CFG_PATH_MAX];\nchar g_cfg_opt_buf[CFG_OPT_MAXSTR];\nchar g_cfg_edit_buf[CFG_OPT_MAXSTR];\n\nchar *g_cfg_rom_path = \"ROM\";\t\t\t// config_init_menus will malloc\nchar *g_cfg_charrom_path = \"Undefined\";\t\t// config_init_menus will malloc\nint g_cfg_charrom_pos = 0;\nchar *g_cfg_file_def_name = \"Undefined\";\nchar **g_cfg_file_strptr = 0;\nint g_cfg_file_min_size = 1024;\nint g_cfg_file_max_size = 2047*1024*1024;\nint\tg_cfg_edit_type = 0;\nvoid\t*g_cfg_edit_ptr = 0;\n\n#define MAX_PARTITION_BLK_SIZE\t\t65536\n\nchar *g_argv0_path = \".\";\n\nconst char *g_kegs_default_paths[] = { \"\", \"./\", \"${HOME}/\",\n\t\"${HOME}/Library/KEGS/\", \"${0}/../\", \"${0}/\",\n\t\"${0}/Contents/Resources/\", 0 };\n\n\nextern Cfg_menu g_cfg_main_menu[];\n\n#define KNMP(a)\t\t&a, #a, 0\n#define KNM(a)\t\t&a, #a\n\nCfg_menu g_cfg_disk_menu[] = {\n{ \"Disk Configuration\", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU },\n{ \"s5d1 = \", 0, 0, 0, CFGTYPE_DISK + 0x5000 },\n{ \"s5d2 = \", 0, 0, 0, CFGTYPE_DISK + 0x5010 },\n{ \"\", 0, 0, 0, 0 },\n{ \"s6d1 = \", 0, 0, 0, CFGTYPE_DISK + 0x6000 },\n{ \"s6d2 = \", 0, 0, 0, CFGTYPE_DISK + 0x6010 },\n{ \"\", 0, 0, 0, 0 },\n{ \"s7d1 = \", 0, 0, 0, CFGTYPE_DISK + 0x7000 },\n{ \"s7d2 = \", 0, 0, 0, CFGTYPE_DISK + 0x7010 },\n{ \"s7d3 = \", 0, 0, 0, CFGTYPE_DISK + 0x7020 },\n{ \"s7d4 = \", 0, 0, 0, CFGTYPE_DISK + 0x7030 },\n{ \"s7d5 = \", 0, 0, 0, CFGTYPE_DISK + 0x7040 },\n{ \"s7d6 = \", 0, 0, 0, CFGTYPE_DISK + 0x7050 },\n{ \"s7d7 = \", 0, 0, 0, CFGTYPE_DISK + 0x7060 },\n{ \"s7d8 = \", 0, 0, 0, CFGTYPE_DISK + 0x7070 },\n{ \"s7d9 = \", 0, 0, 0, CFGTYPE_DISK + 0x7080 },\n{ \"s7d10= \", 0, 0, 0, CFGTYPE_DISK + 0x7090 },\n{ \"s7d11= \", 0, 0, 0, CFGTYPE_DISK + 0x70a0 },\n{ \"s7d12= \", 0, 0, 0, CFGTYPE_DISK + 0x70b0 },\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_newslot6_menu[] = {\n{ \"New 5.25\\\" disk image Configuration\", g_cfg_newslot6_menu, 0, 0,\n\t\t\t\t\t\t\t\tCFGTYPE_MENU },\n{ \"size,280,140KB\", KNM(g_cfg_newdisk_blocks),\n\t\t\t&g_cfg_newdisk_blocks_default, CFGTYPE_INT },\n{ \"Type,1,ProDOS/DOS 3.3,2,WOZ image,3,Dynamic ProDOS directory\",\n\t\t\tKNM(g_cfg_newdisk_type),\n\t\t\t&g_cfg_newdisk_type_default, CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Create and name the image\", (void *)cfg_name_new_image, 0, 0, CFGTYPE_FUNC },\n{ \"\", 0, 0, 0, 0 },\n{ \"Cancel, go back to Disk Config\", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_newslot5_menu[] = {\n{ \"New 3.5\\\" disk image Configuration\", g_cfg_newslot5_menu, 0, 0,CFGTYPE_MENU},\n{ \"size,1600,800KB\", KNM(g_cfg_newdisk_blocks),\n\t\t\t&g_cfg_newdisk_blocks_default, CFGTYPE_INT },\n{ \"Type,1,ProDOS,2,WOZ image,3,Dynamic ProDOS directory\",\n\t\t\tKNM(g_cfg_newdisk_type),\n\t\t\t&g_cfg_newdisk_type_default, CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Create and name the image\", (void *)cfg_name_new_image, 0, 0, CFGTYPE_FUNC },\n{ \"\", 0, 0, 0, 0 },\n{ \"Cancel, go back to Disk Config\", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_newslot7_menu[] = {\n{ \"New Smartport disk image Configuration\", g_cfg_newslot7_menu, 0, 0,\n\t\t\t\t\t\t\t\tCFGTYPE_MENU},\n{ \"size,1600,800KB,3200,1600KB,16384,8MB,32768,16MB,65535,32MB\",\n\t\tKNM(g_cfg_newdisk_blocks), &g_cfg_newdisk_blocks_default,\n\t\t\tCFGTYPE_INT },\n{ \"Type,1,ProDOS,3,Dynamic ProDOS directory\", KNM(g_cfg_newdisk_type),\n\t\t\t&g_cfg_newdisk_type_default, CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Create and name the image\", (void *)cfg_name_new_image, 0, 0, CFGTYPE_FUNC },\n{ \"\", 0, 0, 0, 0 },\n{ \"Cancel, go back to Disk Config\", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_joystick_menu[] = {\n{ \"Joystick Configuration\", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU },\n{ \"Joystick Emulation,0,Keypad Joystick,1,Mouse Joystick,2,Native Joystick 1,\"\n\t\"3,Native Joystick 2\", KNMP(g_joystick_type), CFGTYPE_INT },\n{ \"Joystick Scale X,0x100,Standard,0x119,+10%,0x133,+20%,\"\n\t\"0x150,+30%,0xb0,-30%,0xcd,-20%,0xe7,-10%\",\n\t\tKNMP(g_joystick_scale_factor_x), CFGTYPE_INT },\n{ \"Joystick Scale Y,0x100,Standard,0x119,+10%,0x133,+20%,\"\n\t\"0x150,+30%,0xb0,-30%,0xcd,-20%,0xe7,-10%\",\n\t\tKNMP(g_joystick_scale_factor_y), CFGTYPE_INT },\n{ \"Joystick Trim X\", KNMP(g_joystick_trim_amount_x), CFGTYPE_INT },\n{ \"Joystick Trim Y\", KNMP(g_joystick_trim_amount_y), CFGTYPE_INT },\n{ \"Swap Joystick X and Y,0,Normal operation,1,Paddle 1 and Paddle 0 swapped\",\n\t\tKNMP(g_swap_paddles), CFGTYPE_INT },\n{ \"Invert Joystick,0,Normal operation,1,Left becomes right and up becomes down\",\n\t\tKNMP(g_invert_paddles), CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_rom_menu[] = {\n{ \"ROM File Selection\", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU },\n{ \"ROM File\", KNMP(g_cfg_rom_path), CFGTYPE_FILE },\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_charrom_menu[] = {\n{ \"Character ROM File Selection\", g_cfg_charrom_menu, 0, 0, CFGTYPE_MENU },\n{ \"Character ROM File\", KNMP(g_cfg_charrom_path), CFGTYPE_FILE },\n{ \"Character Set,0,0x00 US Enhanced,1,0x01 US Un-enhanced,\"\n\t\"2,0x02 Clinton Turner V1 Enhanced,3,0x03 ReActiveMicro Enhanced,\"\n\t\"4,0x04 Dan Paymar Enhanced,5,0x05 Blippo Black Enhanced,\"\n\t\"6,0x06 Byte Enhanced,7,0x07 Colossal Enhanced,\"\n\t\"8,0x08 Count Enhanced,9,0x09 Flow Enhanced,\"\n\t\"10,0x0a Gothic Enhanced,11,0x0b Outline Enhanced,\"\n\t\"12,0x0c Pigfont Enhanced,13,0x0d Pinocchio Enhanced,\"\n\t\"14,0x0e Slant Enhanced,15,0x0f Stop Enhanced,\"\n\t\"16,0x10 Euro Un-Enhanced,17,0x11 Euro Enhanced,\"\n\t\"18,0x12 Clinton Turner V2 Enhanced,19,0x13 Improved German Enhanced,\"\n\t\"20,0x14 Improved German Un-Enhanced,21,0x15 Franch Canadian Enhanced,\"\n\t\"22,0x16 French Canadian Un-Enhanced,23,0x17 Hebrew Enhanced,\"\n\t\"24,0x18 Hebrew Un-Enhanced,25,0x19 Apple II+ Enhanced,\"\n\t\"26,0x1a Apple II+ Un-Enhanced,27,0x1b Katakana Enhanced,\"\n\t\"28,0x1c Cyrillic Enhanced,29,0x1d Greek Enhanced,\"\n\t\"30,0x1e Esperanto Enhanced,31,0x1f Videx Enhanced\",\n\tKNMP(g_cfg_charrom_pos), CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_serial_menu[] = {\n{ \"Serial Port Configuration\", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU },\n{ \"Slot 1 (port 0) settings\", 0, 0, 0, 0 },\n{ \"  Main setting  ,0,Use Real Device below,1,Use a virtual modem,\"\n\t\t\"2,Use Remote IP below,3,Use incoming port 6501\",\n\t\tKNMP(g_serial_cfg[0]), CFGTYPE_INT },\n{ \"  Status        \", (void *)cfg_get_serial0_status, 0, 0, CFGTYPE_FUNC },\n{ \"  Real Device   \", KNMP(g_serial_device[0]), CFGTYPE_FILE },\n{ \"  Windows Device,0,Disabled,1,COM1,2,COM2,3,COM3,4,COM4\",\n\t\tKNMP(g_serial_win_device[0]), CFGTYPE_INT },\n{ \"  Remote IP     \", KNMP(g_serial_remote_ip[0]), CFGTYPE_STR },\n{ \"  Remote Port   \", KNMP(g_serial_remote_port[0]), CFGTYPE_INT },\n{ \"  Serial Mask   ,0,Send full 8-bit data,1,Mask off high bit\",\n\t\tKNMP(g_serial_mask[0]), CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Slot 2 (port 1) settings\", 0, 0, 0, 0, },\n{ \"  Main setting  ,0,Use Real Device below,1,Use a virtual modem,\"\n\t\t\"2,Use Remote IP below,3,Use incoming port 6502\",\n\t\tKNMP(g_serial_cfg[1]), CFGTYPE_INT },\n{ \"  Status        \", (void *)cfg_get_serial1_status, 0, 0, CFGTYPE_FUNC },\n{ \"  Real Device   \", KNMP(g_serial_device[1]), CFGTYPE_FILE },\n{ \"  Windows Device,1,COM1,2,COM2,3,COM3,4,COM4\",\n\t\tKNMP(g_serial_win_device[1]), CFGTYPE_INT },\n{ \"  Remote IP     \", KNMP(g_serial_remote_ip[1]), CFGTYPE_STR },\n{ \"  Remote Port   \", KNMP(g_serial_remote_port[1]), CFGTYPE_INT },\n{ \"  Serial Mask   ,0,Send full 8-bit data,1,Mask off high bit\",\n\t\tKNMP(g_serial_mask[1]), CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_modem_menu[] = {\n{ \"Virtual Modem Configuration\", g_cfg_modem_menu, 0, 0, CFGTYPE_MENU },\n{ \"Modem Speed Response Code          ,5,5 - CONNECT 1200,10,10 - CONNECT 2400,\"\n\t\t\"12,12 - CONNECT 9600 (HAYES/Warp6),\"\n\t\t\"13,13 - CONNECT 9600 (USR/HST),\"\n\t\t\"14,14 - CONNECT 19200 (HAYES/Warp6),\"\n\t\t\"28,28 - CONNECT 38400 (HAYES/Warp6)\",\n\t\tKNMP(g_serial_modem_response_code), CFGTYPE_INT },\n{ \"Allow Modem incoming on 6501/6502  ,0,Outgoing only,\"\n\t\t\"1,Incoming and outgoing (BBS)\",\n\t\tKNMP(g_serial_modem_allow_incoming), CFGTYPE_INT },\n{ \"Send Telnet Escape codes           ,0,Disable Telnet,1,Send Telnet codes\",\n\t\tKNMP(g_serial_modem_init_telnet), CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\n\n\nCfg_menu g_cfg_video_menu[] = {\n{ \"Force X-windows display depth\", KNMP(g_force_depth), CFGTYPE_INT },\n{ \"Enable VOC,0,Disabled,1,Enabled\", KNMP(g_voc_enable), CFGTYPE_INT },\n{ \"Default Main Window width\", KNMP(g_mainwin_width), CFGTYPE_INT },\n{ \"Default Main Window height\", KNMP(g_mainwin_height), CFGTYPE_INT },\n{ \"Main Window X position\", KNMP(g_mainwin_xpos), CFGTYPE_INT },\n{ \"Main Window Y position\", KNMP(g_mainwin_ypos), CFGTYPE_INT },\n{ \"3200 Color Enable,0,Auto (Full if fast enough),1,Full (Update every line),\"\n\t\"8,Off (Update video every 8 lines)\",\n\t\tKNMP(g_video_line_update_interval), CFGTYPE_INT },\n{ \"Dump text screen to file\", (void *)cfg_text_screen_dump, 0, 0, CFGTYPE_FUNC},\n{ \"\", 0, 0, 0, 0 },\n{ \"Back to Main Config\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ 0, 0, 0, 0, 0 },\n};\n\nCfg_menu g_cfg_main_menu[] = {\n{ \"KEGS Configuration\", g_cfg_main_menu, 0, 0, CFGTYPE_MENU },\n{ \"Disk Configuration\", g_cfg_disk_menu, 0, 0, CFGTYPE_MENU },\n{ \"Joystick Configuration\", g_cfg_joystick_menu, 0, 0, CFGTYPE_MENU },\n{ \"ROM File Selection\", g_cfg_rom_menu, 0, 0, CFGTYPE_MENU },\n{ \"Character ROM Selection\", g_cfg_charrom_menu, 0, 0, CFGTYPE_MENU },\n{ \"Serial Port Configuration\", g_cfg_serial_menu, 0, 0, CFGTYPE_MENU },\n{ \"Virtual Modem Configuration\", g_cfg_modem_menu, 0, 0, CFGTYPE_MENU },\n{ \"Video Settings\", g_cfg_video_menu, 0, 0, CFGTYPE_MENU },\n{ \"Auto-update config.kegs,0,Manual,1,Immediately\",\n\t\tKNMP(g_config_kegs_auto_update), CFGTYPE_INT },\n{ \"Speed,0,Unlimited,1,1.0MHz,2,2.8MHz,3,8.0MHz (Zip)\",\n\t\tKNMP(g_limit_speed), CFGTYPE_INT },\n{ \"ZipGS Speed,8,8MHz,16,16MHz,32,32MHz,64,64MHz,128,128MHz\",\n\t\tKNMP(g_zip_speed_mhz), CFGTYPE_INT },\n{ \"Expansion Mem Size,0,0MB,0x100000,1MB,0x200000,2MB,0x300000,3MB,\"\n\t\"0x400000,4MB,0x600000,6MB,0x800000,8MB,0xa00000,10MB,0xc00000,12MB,\"\n\t\"0xe00000,14MB\", KNMP(g_mem_size_exp), CFGTYPE_INT },\n{ \"Show Status lines,0,Disabled,1,Enabled\", KNMP(g_status_enable), CFGTYPE_INT},\n{ \"Code Red Halts,0,Do not stop on bad accesses,1,Enter debugger on bad \"\n\t\t\"accesses\", KNMP(g_user_halt_bad), CFGTYPE_INT },\n{ \"Enable Text Page 2 Shadow,0,Disabled on ROM 01 (matches real hardware),\"\n\t\"1,Enabled on ROM 01 and 03\",\n\t\tKNMP(g_user_page2_shadow), CFGTYPE_INT },\n{ \"Swap Command/Option keys,0,Disabled,1,Swapped\",\n\t\t\t\tKNMP(g_adb_swap_command_option), CFGTYPE_INT },\n{ \"\", 0, 0, 0, 0 },\n{ \"Save changes to config.kegs\", (void *)config_write_config_kegs_file, 0, 0,\n\t\tCFGTYPE_FUNC },\n{ \"\", 0, 0, 0, 0 },\n{ \"Exit Config (or press F4)\", (void *)cfg_exit, 0, 0, CFGTYPE_FUNC },\n{ 0, 0, 0, 0, 0 },\n};\n\n\n#define CFG_MAX_DEFVALS\t128\nCfg_defval g_cfg_defvals[CFG_MAX_DEFVALS];\nint g_cfg_defval_index = 0;\n\nword32 g_cfg_slotdrive = 0;\nint g_cfg_select_partition = -1;\nchar g_cfg_tmp_path[CFG_PATH_MAX];\nchar g_cfg_file_path[CFG_PATH_MAX];\nchar g_cfg_file_cachedpath[CFG_PATH_MAX];\nchar g_cfg_file_cachedreal[CFG_PATH_MAX];\nchar g_cfg_file_curpath[CFG_PATH_MAX];\nchar g_cfg_file_shortened[CFG_PATH_MAX];\nchar g_cfg_file_match[CFG_PATH_MAX];\n\nchar g_cfg_part_path[CFG_PATH_MAX];\nint\tg_cfg_partition_is_zip = 0;\n\nCfg_listhdr g_cfg_dirlist = { 0 };\nCfg_listhdr g_cfg_partitionlist = { 0 };\n\nint g_cfg_file_pathfield = 0;\n\nconst char *g_kegs_rom_names[] = { \"ROM\", \"ROM\", \"ROM.01\", \"ROM.03\",\n\t\"APPLE2GS.ROM\", \"APPLE2GS.ROM2\", \"xgs.rom\", \"XGS.ROM\", \"Rom03gd\",\n\t\"342-0077-b\", // MAME ROM.01\n\t0 };\n\t/* First entry is special--it will be overwritten by g_cfg_rom_path */\n\nconst char *g_kegs_c1rom_names[] = { 0 };\nconst char *g_kegs_c2rom_names[] = { 0 };\nconst char *g_kegs_c3rom_names[] = { 0 };\nconst char *g_kegs_c4rom_names[] = { 0 };\nconst char *g_kegs_c5rom_names[] = { 0 };\nconst char *g_kegs_c6rom_names[] = { \"c600.rom\", \"controller.rom\", \"disk.rom\",\n\t\t\t\t\"DISK.ROM\", \"diskII.prom\", 0 };\nconst char *g_kegs_c7rom_names[] = { 0 };\n\nconst char **g_kegs_rom_card_list[8] = {\n\t0,\t\t\tg_kegs_c1rom_names,\n\tg_kegs_c2rom_names,\tg_kegs_c3rom_names,\n\tg_kegs_c4rom_names,\tg_kegs_c5rom_names,\n\tg_kegs_c6rom_names,\tg_kegs_c7rom_names };\n\nbyte g_rom_c600_rom01_diffs[256] = {\n\t0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xe2, 0x00,\n\t0xd0, 0x50, 0x0f, 0x77, 0x5b, 0xb9, 0xc3, 0xb1,\n\t0xb1, 0xf8, 0xcb, 0x4e, 0xb8, 0x60, 0xc7, 0x2e,\n\t0xfc, 0xe0, 0xbf, 0x1f, 0x66, 0x37, 0x4a, 0x70,\n\t0x55, 0x2c, 0x3c, 0xfc, 0xc2, 0xa5, 0x08, 0x29,\n\t0xac, 0x21, 0xcc, 0x09, 0x55, 0x03, 0x17, 0x35,\n\t0x4e, 0xe2, 0x0c, 0xe9, 0x3f, 0x9d, 0xc2, 0x06,\n\t0x18, 0x88, 0x0d, 0x58, 0x57, 0x6d, 0x83, 0x8c,\n\t0x22, 0xd3, 0x4f, 0x0a, 0xe5, 0xb7, 0x9f, 0x7d,\n\t0x2c, 0x3e, 0xae, 0x7f, 0x24, 0x78, 0xfd, 0xd0,\n\t0xb5, 0xd6, 0xe5, 0x26, 0x85, 0x3d, 0x8d, 0xc9,\n\t0x79, 0x0c, 0x75, 0xec, 0x98, 0xcc, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x39, 0x00, 0x35, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,\n\t0x6c, 0x44, 0xce, 0x4c, 0x01, 0x08, 0x00, 0x00\n};\n\nbyte g_rom_c700[256] = {\n\t//0xa2, 0x20, 0xa2, 0x00, 0xa2, 0x03, 0xc9, 0x3c,\t// For Apple //e\n\t0xa2, 0x20, 0xa2, 0x00, 0xa2, 0x03, 0xc9, 0x00,\n\t//^^= LDX #$20; LDY #$00, LDX #$03  CMP #$3c\n\t0x80, 0x0c, 0x18, 0xb8, 0x70, 0x38, 0xb8, 0x42,\n\t//^^= BRA $c716; CLC; CLV; BVS $c746 (SEC); CLV; WDM $c7,$00\n\t0xc7, 0x00, 0x60, 0x00, 0x00, 0xea, 0xe2, 0x41,\n\t//^^=  ...; RTS..............; NOP; SEP #$41\n\t0x70, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t//^^= BVS $c70f\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t// So does WDM $c7,$00 with psr.v=1 for $c700; v=0,c=0 for $c70a,\n\t//  and v=0,c=1 for $c70d\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0xbf, 0x0a\n};\n\nCfg_menu *g_menuptr = 0;\nint\tg_menu_line = 1;\nint\tg_menu_inc = 1;\nint\tg_menu_max_line = 1;\nint\tg_menu_redraw_needed = 1;\n\n#define MAX_CFG_ARGV_OVERRIDES\t\t64\n\nint g_cfg_argv_num_overrides = 0;\nchar *g_cfg_argv_overrides[MAX_CFG_ARGV_OVERRIDES];\n\nint\nconfig_add_argv_override(const char *str1, const char *str2)\n{\n\tconst char *equal_ptr;\n\tchar\t*str;\n\tint\tret, pos, len;\n\n\t// Handle things like \"rom=rompath\" and \"rom\", \"rompath\"\n\t// Look through str1, see if there is '=', if so ignore str2\n\tequal_ptr = strchr(str1, '=');\n\tret = 1;\n\tif(equal_ptr) {\t\t\t// str1 has '=' in it\n\t\tret = 0;\t\t// Don't eat up str2 argument\n\t\tstr = kegs_malloc_str(str1);\n\t} else {\n\t\t// We need to form a new string of str1, =, str2\n\t\tlen = (int)(strlen(str1) + strlen(str2) + 2);\n\t\tstr = malloc(len);\n\t\tcfg_strncpy(str, str1, len);\n\t\tcfg_strlcat(str, \"=\", len);\n\t\tcfg_strlcat(str, str2, len);\n\t}\n\tpos = g_cfg_argv_num_overrides++;\n\tif(pos >= MAX_CFG_ARGV_OVERRIDES) {\n\t\tg_cfg_argv_num_overrides = MAX_CFG_ARGV_OVERRIDES;\n\t\tfatal_printf(\"MAX_CFG_ARGV_OVERRIDES overflow\\n\");\n\t\tmy_exit(5);\n\t\treturn ret;\n\t}\n\tg_cfg_argv_overrides[pos] = str;\n\tprintf(\"Added config override %d, %s\\n\", pos, str);\n\n\treturn ret;\n}\n\nvoid\nconfig_set_config_kegs_name(const char *str1)\n{\n\tint\tmaxlen;\n\n\t//\tCommand line override \"-cfg cfg_file\"\n\tg_config_kegs_name[0] = 0;\n\tmaxlen = (int)sizeof(g_config_kegs_name);\n\tcfg_strncpy(&g_config_kegs_name[0], str1, maxlen);\n}\n\nvoid\nconfig_init_menus(Cfg_menu *menuptr)\n{\n\tvoid\t*voidptr;\n\tconst char *name_str;\n\tCfg_defval *defptr;\n\tchar\t**str_ptr;\n\tchar\t*str;\n\tint\ttype, pos, val;\n\n\tif(menuptr[0].defptr != 0) {\n\t\treturn;\n\t}\n\tmenuptr[0].defptr = (void *)1;\n\tpos = 0;\n\twhile(pos < 100) {\n\t\ttype = menuptr->cfgtype;\n\t\tvoidptr = menuptr->ptr;\n\t\tname_str = menuptr->name_str;\n\t\tif(menuptr->str == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tif(name_str != 0) {\n\t\t\tdefptr = &(g_cfg_defvals[g_cfg_defval_index++]);\n\t\t\tif(g_cfg_defval_index >= CFG_MAX_DEFVALS) {\n\t\t\t\tfatal_printf(\"CFG_MAX_DEFVAL overflow\\n\");\n\t\t\t\tmy_exit(5);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdefptr->menuptr = menuptr;\n\t\t\tdefptr->intval = 0;\n\t\t\tdefptr->strval = 0;\n\t\t\tswitch(type) {\n\t\t\tcase CFGTYPE_INT:\n\t\t\t\tval = *((int *)voidptr);\n\t\t\t\tdefptr->intval = val;\n\t\t\t\tmenuptr->defptr = &(defptr->intval);\n\t\t\t\tbreak;\n\t\t\tcase CFGTYPE_FILE:\n\t\t\tcase CFGTYPE_STR:\n\t\t\t\tstr_ptr = (char **)menuptr->ptr;\n\t\t\t\tstr = *str_ptr;\n\t\t\t\t// We need to malloc this string since all\n\t\t\t\t//  string values must be dynamically alloced\n\t\t\t\tdefptr->strval = str;\t// this can have a copy\n\t\t\t\t*str_ptr = kegs_malloc_str(str);\n\t\t\t\tmenuptr->defptr = &(defptr->strval);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tfatal_printf(\"name_str is %p = %s, but type: \"\n\t\t\t\t\t\"%d\\n\", name_str, name_str, type);\n\t\t\t\tmy_exit(5);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif(type == CFGTYPE_MENU) {\n\t\t\tconfig_init_menus((Cfg_menu *)voidptr);\n\t\t}\n\t\tpos++;\n\t\tmenuptr++;\n\t}\n}\n\nvoid\nconfig_init()\n{\n\tconfig_init_menus(g_cfg_main_menu);\n\n\t// Find the config.kegs file\n\tif(g_config_kegs_name[0] == 0) {\n\t\tcfg_find_config_kegs_file();\n\t}\n\n\tconfig_parse_config_kegs_file();\n}\n\nvoid\ncfg_find_config_kegs_file()\n{\n\tconst char **path_ptr;\n\tint\tmaxlen, fd;\n\n\tg_config_kegs_name[0] = 0;\n\tmaxlen = sizeof(g_config_kegs_name);\n\tfd = 0;\n\tif(!config_setup_kegs_file(&g_config_kegs_name[0], maxlen,\n\t\t\t\t\t\t&g_config_kegs_name_list[0])) {\n\t\t// Try to create config.kegs\n\t\tfd = -1;\n\t\tpath_ptr = &g_kegs_default_paths[0];\n\t\twhile(*path_ptr) {\n\t\t\tconfig_expand_path(&g_config_kegs_name[0], *path_ptr,\n\t\t\t\t\t\t\t\t\tmaxlen);\n\t\t\tcfg_strlcat(&g_config_kegs_name[0], \"config.kegs\",\n\t\t\t\t\t\t\t\t\tmaxlen);\n\t\t\tprintf(\"Trying to create %s\\n\", &g_config_kegs_name[0]);\n\t\t\tfd = open(&g_config_kegs_name[0],\n\t\t\t\t\tO_CREAT | O_TRUNC | O_WRONLY, 0x1b6);\n\t\t\tclose(fd);\n\t\t\tif(fd >= 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tpath_ptr++;\n\t\t}\n\t}\n\n\tif(fd < 0) {\n\t\tfatal_printf(\"Could not create config.kegs!\\n\");\n\t\tmy_exit(2);\n\t}\n}\n\nint\nconfig_setup_kegs_file(char *outname, int maxlen, const char **name_ptr)\n{\n\tstruct stat stat_buf;\n\tconst char **path_ptr, **cur_name_ptr;\n\tmode_t\tfmt;\n\tint\tret, len;\n\n\toutname[0] = 0;\n\n\tpath_ptr = &g_kegs_default_paths[0];\t\t// Array of strings\n\n\twhile(*path_ptr) {\n\t\tlen = config_expand_path(outname, *path_ptr, maxlen);\n\t\tif(len != (int)strlen(outname)) {\n\t\t\tprintf(\"config_expand_path ret %d, but strlen:%d!\\n\",\n\t\t\t\t\t\tlen, (int)strlen(outname));\n\t\t}\n\t\tcur_name_ptr = name_ptr;\n\t\twhile(*cur_name_ptr && (len < maxlen)) {\n\t\t\toutname[len] = 0;\n\t\t\tcfg_strlcat(outname, *cur_name_ptr, maxlen);\n\t\t\t// printf(\"Doing stat on %s\\n\", outname);\n\t\t\tret = cfg_stat(outname, &stat_buf, 0);\n\t\t\tif(ret == 0) {\n\t\t\t\tfmt = stat_buf.st_mode & S_IFMT;\n\t\t\t\tif(fmt != S_IFDIR) {\n\t\t\t\t\t/* got it! */\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcur_name_ptr++;\n\t\t}\n\t\tpath_ptr++;\n\t}\n\n\treturn 0;\n}\n\nint\nconfig_expand_path(char *out_ptr, const char *in_ptr, int maxlen)\n{\n\tchar\tname_buf[256];\n\tchar\t*tmp_ptr;\n\tint\tname_len, in_char, state, pos;\n\n\tout_ptr[0] = 0;\n\n\tpos = 0;\n\tname_len = 0;\n\tstate = 0;\n\n\t/* See if in_ptr has ${} notation, replace with getenv or argv0 */\n\twhile(pos < (maxlen - 1)) {\n\t\tin_char = *in_ptr++;\n\t\tout_ptr[pos++] = in_char;\n\t\tout_ptr[pos] = 0;\n\t\tif(in_char == 0) {\n\t\t\treturn pos - 1;\n\t\t}\n\t\tif(state == 0) {\n\t\t\t/* No $ seen yet, look for it */\n\t\t\tif(in_char == '$') {\n\t\t\t\tstate = 1;\n\t\t\t}\n\t\t} else if(state == 1) {\n\t\t\t/* See if next char is '{' (dummy }) */\n\t\t\tif(in_char == '{') {\t\t/* add dummy } */\n\t\t\t\tstate = 2;\n\t\t\t\tname_len = 0;\n\t\t\t\tpos -= 2;\n\t\t\t\tout_ptr[pos] = 0;\n\t\t\t} else {\n\t\t\t\tstate = 0;\n\t\t\t}\n\t\t} else if(state == 2) {\n\t\t\t/* fill name_buf ... dummy '{' */\n\t\t\tpos--;\n\t\t\tout_ptr[pos] = 0;\n\t\t\tif(in_char == '}') {\n\t\t\t\tname_buf[name_len] = 0;\n\n\t\t\t\t/* got token, now look it up */\n\t\t\t\ttmp_ptr = \"\";\n\t\t\t\tif(!strncmp(\"0\", name_buf, 128)) {\n\t\t\t\t\t/* Replace ${0} with g_argv0_path */\n\t\t\t\t\ttmp_ptr = g_argv0_path;\n\t\t\t\t} else {\n\t\t\t\t\ttmp_ptr = getenv(name_buf);\n\t\t\t\t\tif(tmp_ptr == 0) {\n\t\t\t\t\t\ttmp_ptr = \"\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpos = cfg_strlcat(out_ptr, tmp_ptr, maxlen);\n\t\t\t\tstate = 0;\n\t\t\t} else {\n\t\t\t\tname_buf[name_len++] = in_char;\n\t\t\t\tif(name_len >= 250) {\n\t\t\t\t\tname_len--;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn pos;\n}\n\nchar *\ncfg_exit(int get_status)\n{\n\t/* printf(\"In cfg exit\\n\"); */\n\tif(get_status) {\n\t\treturn 0;\n\t}\n\tcfg_toggle_config_panel();\n\n\treturn 0;\n}\n\nvoid\ncfg_err_vprintf(const char *pre_str, const char *fmt, va_list ap)\n{\n\tchar\t*bufptr;\n\tint\tpos, len, bufsize;\n\n\tpos = g_cfg_err_pos;\n\tif(pos >= CFG_ERR_MAX) {\n\t\tpos = CFG_ERR_MAX - 1;\n\t}\n\tbufptr = &g_cfg_err_bufs[pos][0];\n\tlen = 0;\n\tbufsize = CFG_ERR_BUFSIZE;\n\tif(pre_str && *pre_str) {\n\t\tcfg_strncpy(bufptr, pre_str, CFG_ERR_BUFSIZE);\n\t\tcfg_strlcat(bufptr, \" error: \", CFG_ERR_BUFSIZE);\n\t\tlen = (int)strlen(bufptr);\n\t\tbufsize = CFG_ERR_BUFSIZE - len;\n\t}\n\tif(bufsize > 0) {\n\t\tvsnprintf(&bufptr[len], bufsize, fmt, ap);\n\t}\n\n\tfputs(bufptr, stderr);\n\tg_cfg_err_pos = pos + 1;\n}\n\nvoid\ncfg_err_printf(const char *pre_str, const char *fmt, ...)\n{\n\tva_list\tap;\n\n\tva_start(ap, fmt);\n\tcfg_err_vprintf(pre_str, fmt, ap);\n\tva_end(ap);\n}\n\nvoid\ncfg_toggle_config_panel()\n{\n\tint\tpanel;\n\n\tpanel = !g_config_control_panel;\n\tif(g_rom_version < 0) {\n\t\tpanel = 1;\t/* Stay in config mode */\n\t}\n\tif(panel != g_config_control_panel) {\n\t\tcfg_set_config_panel(panel);\n\t}\n}\n\nvoid\ncfg_set_config_panel(int panel)\n{\n\tint\ti;\n\n\tg_config_control_panel = panel;\n\tif(panel) {\n\t\t// Entering configuration panel\n\t\tvideo_force_reparse();\n\n\t\tcfg_printf(\"Entering config_control_panel\\n\");\n\n\t\tfor(i = 0; i < 20; i++) {\n\t\t\t// Toss any queued-up keypresses\n\t\t\tif(adb_read_c000() & 0x80) {\n\t\t\t\t(void)adb_access_c010();\n\t\t\t}\n\t\t}\n\t\t// HACK: Force adb keyboard (and probably mouse) to \"normal\"...\n\n\t\tcfg_home();\n\n\t\tg_menu_line = 1;\n\t\tg_menu_inc = 1;\n\t\tg_menu_redraw_needed = 1;\n\t\tg_cfg_slotdrive = 0;\n\t\tg_cfg_newdisk_select = 0;\n\t\tg_cfg_select_partition = -1;\n\t} else {\n\t\t// Leave config panel, go back to A2 emulation\n\n\t\tvideo_force_reparse();\n\t}\n\tg_full_refresh_needed = -1;\n\tg_a2_screen_buffer_changed = -1;\n\tg_adb_repeat_vbl = g_vbl_count + 60;\n}\n\nchar *\ncfg_text_screen_dump(int get_status)\n{\n\tFILE\t*ofile;\n\tchar\t*bufptr;\n\tchar\t*filename;\n\n\tif(get_status) {\n\t\treturn 0;\n\t}\n\tfilename = \"kegs.screen.dump\";\n\tprintf(\"Writing text screen to the file %s\\n\", filename);\n\tofile = fopen(filename, \"w\");\n\tif(ofile == 0) {\n\t\tfatal_printf(\"Could not write to file %s, (%d)\\n\", filename,\n\t\t\t\terrno);\n\t\treturn 0;\n\t}\n\tbufptr = cfg_text_screen_str();\n\tfputs(bufptr, ofile);\n\tfclose(ofile);\n\treturn 0;\n}\n\nchar g_text_screen_buf[2100] = { 0 };\n\nchar *\ncfg_text_screen_str()\n{\n\tchar\t*bufptr;\n\tint\tpos, start_pos, c, offset;\n\tint\ti, j;\n\n\t// bufptr must be at least (81*24)+2 characters\n\tbufptr = &g_text_screen_buf[0];\n\tpos = 0;\n\tfor(i = 0; i < 24; i++) {\n\t\tstart_pos = pos;\n\t\tfor(j = 0; j < 40; j++) {\n\t\t\toffset = g_screen_index[i] + j;\n\t\t\tif(g_cur_a2_stat & ALL_STAT_VID80) {\n\t\t\t\tc = g_slow_memory_ptr[0x10400 + offset] & 0x7f;\n\t\t\t\tif(c < 0x20) {\n\t\t\t\t\tc += 0x40;\n\t\t\t\t}\n\t\t\t\tbufptr[pos++] = c;\n\t\t\t}\n\t\t\tc = g_slow_memory_ptr[0x0400 + offset] & 0x7f;\n\t\t\tif(c < 0x20) {\n\t\t\t\tc += 0x40;\n\t\t\t}\n\t\t\tif(c == 0x7f) {\n\t\t\t\tc = ' ';\n\t\t\t}\n\t\t\tbufptr[pos++] = c;\n\t\t}\n\t\twhile((pos > start_pos) && (bufptr[pos-1] == ' ')) {\n\t\t\t/* try to strip out trailing spaces */\n\t\t\tpos--;\n\t\t}\n\t\tbufptr[pos++] = '\\n';\n\t\tbufptr[pos] = 0;\n\t}\n\n\treturn bufptr;\n}\n\nchar *\ncfg_get_serial0_status(int get_status)\n{\n\treturn scc_get_serial_status(get_status, 0);\n}\n\nchar *\ncfg_get_serial1_status(int get_status)\n{\n\treturn scc_get_serial_status(get_status, 1);\n}\n\nchar *\ncfg_get_current_copy_selection()\n{\n\treturn &g_text_screen_buf[0];\n}\n\nvoid\nconfig_vbl_update(int doit_3_persec)\n{\n\tif(doit_3_persec) {\n\t\tif(g_config_kegs_auto_update && g_config_kegs_update_needed) {\n\t\t\t(void)config_write_config_kegs_file(0);\n\t\t}\n\t}\n\treturn;\n}\n\nvoid\ncfg_file_update_rom(const char *str)\n{\n\tcfg_file_update_ptr(&g_cfg_rom_path, str, 1);\n}\n\nvoid\ncfg_file_update_ptr(char **strptr, const char *str, int need_update)\n{\n\tchar\t*newstr;\n\tint\tremote_changed, serial_changed;\n\tint\ti;\n\n\t// Update whatever g_cfg_file_strptr points to.  If changing\n\t//  ROM path or Charrom, then do the proper updates\n\n\tnewstr = kegs_malloc_str(str);\n\tif(!strptr) {\n\t\treturn;\n\t}\n\tif(*strptr) {\n\t\tfree(*strptr);\n\t}\n\t*strptr = newstr;\n\tif(strptr == &(g_cfg_rom_path)) {\n\t\tprintf(\"Updated ROM file\\n\");\n\t\tload_roms_init_memory();\n\t\tdo_reset();\n\t}\n\tif(strptr == &(g_cfg_charrom_path)) {\n\t\tprintf(\"Updated Char ROM file\\n\");\n\t\tcfg_load_charrom();\n\t}\n\tfor(i = 0; i < 2; i++) {\n\t\tremote_changed = 0;\n\t\tserial_changed = 0;\n\t\tif(strptr == &g_serial_remote_ip[i]) {\n\t\t\tremote_changed = 1;\n\t\t}\n\t\tif(strptr == &g_serial_device[i]) {\n\t\t\tserial_changed = 1;\n\t\t}\n\t\tif(remote_changed || serial_changed) {\n\t\t\tscc_config_changed(i, 0, remote_changed,\n\t\t\t\t\t\t\t\tserial_changed);\n\t\t}\n\t}\n\tif(need_update) {\n\t\tg_config_kegs_update_needed = 1;\n\t\tprintf(\"Set g_config_kegs_update_needed = 1\\n\");\n\t}\n}\n\nvoid\ncfg_int_update(int *iptr, int new_val)\n{\n\tint\told_val, cfg_changed, remote_changed, serial_changed;\n\tint\ti;\n\n\t// Called to handle an integer being changed in the F4 config menus\n\t//  where it's value may need special handling\n\n\told_val = *iptr;\n\t*iptr = new_val;\n\tif(old_val == new_val) {\n\t\treturn;\n\t}\n\tif(iptr == &g_cfg_charrom_pos) {\n\t\tcfg_load_charrom();\n\t}\n\n\tfor(i = 0; i < 2; i++) {\n\t\tremote_changed = 0;\n\t\tserial_changed = 0;\n\t\tcfg_changed = 0;\n\t\tif(iptr == &g_serial_cfg[i]) {\n\t\t\tcfg_changed = 1;\n\t\t}\n\t\tif(iptr == &g_serial_remote_port[i]) {\n\t\t\tremote_changed = 1;\n\t\t}\n\t\tif(iptr == &g_serial_win_device[i]) {\n\t\t\tserial_changed = 1;\n\t\t}\n\t\tif(cfg_changed || remote_changed || serial_changed) {\n\t\t\tscc_config_changed(i, cfg_changed, remote_changed,\n\t\t\t\t\t\t\tserial_changed);\n\t\t}\n\t}\n}\n\nvoid\ncfg_load_charrom()\n{\n\tbyte\tbuffer[4096];\n\tdword64\tdsize, dret;\n\tword32\tupos;\n\tint\tfd;\n\n\tprintf(\"Loading character ROM from: %s\\n\", g_cfg_charrom_path);\n\tfd = open(g_cfg_charrom_path, O_RDONLY | O_BINARY);\n\tif(fd < 0) {\n\t\tprintf(\"Cannot open %s\\n\", g_cfg_charrom_path);\n\t\treturn;\n\t}\n\tdsize = cfg_get_fd_size(fd);\n\tupos = g_cfg_charrom_pos * 0x1000U;\n\tif(dsize < (upos + 0x1000)) {\n\t\tg_cfg_charrom_pos = 0;\n\t\treturn;\n\t}\n\tdret = cfg_read_from_fd(fd, &buffer[0], upos, 4096);\n\tif(dret != 0) {\n\t\tprepare_a2_romx_font(&buffer[0]);\n\t}\n}\n\nvoid\nconfig_load_roms()\n{\n\tstruct stat stat_buf;\n\tconst char **names_ptr;\n\tint\tmore_than_8mb, changed_rom, len, fd, ret;\n\tint\ti;\n\n\tg_rom_version = -1;\n\n\t/* set first entry of g_kegs_rom_names[] to g_cfg_rom_path so that */\n\t/*  it becomes the first place searched. */\n\tg_kegs_rom_names[0] = g_cfg_rom_path;\n\tret = config_setup_kegs_file(&g_cfg_tmp_path[0], CFG_PATH_MAX,\n\t\t\t\t\t\t\t&g_kegs_rom_names[0]);\n\tif(ret == 0) {\n\t\t// Just get out, let config interface select ROM\n\t\tg_config_control_panel = 1;\n\t\tprintf(\"No ROM, set g_config_control_panel=1\\n\");\n\t\treturn;\n\t}\n\tprintf(\"Found ROM at path: %s\\n\", g_cfg_tmp_path);\n\tfd = open(&g_cfg_tmp_path[0], O_RDONLY | O_BINARY);\n\tif(fd < 0) {\n\t\tfatal_printf(\"Open ROM file %s failed:%d, errno:%d\\n\",\n\t\t\t\t&g_cfg_tmp_path[0], fd, errno);\n\t\tg_config_control_panel = 1;\n\t\treturn;\n\t}\n\n\tret = fstat(fd, &stat_buf);\n\tif(ret != 0) {\n\t\tfatal_printf(\"fstat returned %d on fd %d, errno: %d\\n\",\n\t\t\tret, fd, errno);\n\t\tg_config_control_panel = 1;\n\t\treturn;\n\t}\n\n\tlen = (int)stat_buf.st_size;\n\tmemset(&g_rom_fc_ff_ptr[0], 0, 4*65536);\n\t\t\t\t/* Clear banks fc-ff to 0 */\n\tif(len == 32*1024) {\t\t// Apple //e\n\t\tg_rom_version = 0;\n\t\tg_mem_size_base = 128*1024;\n\t\tret = (int)read(fd, &g_rom_fc_ff_ptr[3*65536 + 32768], len);\n\t} else if(len == 128*1024) {\n\t\tg_rom_version = 1;\n\t\tg_mem_size_base = 128*1024;\n\t\tret = (int)read(fd, &g_rom_fc_ff_ptr[2*65536], len);\n\t} else if(len == 256*1024) {\n\t\tg_rom_version = 3;\n\t\tg_mem_size_base = 1024*1024;\n\t\tret = (int)read(fd, &g_rom_fc_ff_ptr[0], len);\n\t} else {\n\t\tfatal_printf(\"The ROM size should be 128K or 256K, this file \"\n\t\t\t\t\t\t\"is %d bytes\\n\", len);\n\t\tg_config_control_panel = 1;\n\t\treturn;\n\t}\n\n\tprintf(\"Read: %d bytes of ROM\\n\", ret);\n\tif(ret != len) {\n\t\tfatal_printf(\"errno: %d\\n\", errno);\n\t\tg_config_control_panel = 1;\n\t\treturn;\n\t}\n\tclose(fd);\n\n\tmemset(&g_rom_cards_ptr[0], 0, 256*16);\n\n\tfor(i = 0; i < 256; i++) {\n\t\t// Place HD PROM in slot 7\n\t\tg_rom_cards_ptr[0x700 + i] = g_rom_c700[i];\n\t\t// g_rom_cards_ptr[0x500 + i] = g_rom_c700[i];\n\t}\n\t/* initialize c600 rom to be diffs from the real ROM, to build-in */\n\t/*  Apple II compatibility without distributing ROMs */\n\tif(g_rom_version == 0) {\t\t\t// Apple //e\n\t\tfor(i = 0; i < 256; i++) {\n\t\t\t// Place Disk II PROM in slot 6\n\t\t\tg_rom_cards_ptr[0x600 + i] = g_rom_fc_ff_ptr[0x38600+i];\n\t\t}\n\t} else {\n\t\tfor(i = 0; i < 256; i++) {\n\t\t\tg_rom_cards_ptr[0x600 + i] =\n\t\t\t\t\tg_rom_fc_ff_ptr[0x3c600 + i] ^\n\t\t\t\t\tg_rom_c600_rom01_diffs[i];\n\t\t}\n\t}\n\tif(g_rom_version >= 3) {\n\t\t/* some patches */\n\t\tg_rom_cards_ptr[0x61b] ^= 0x40;\n\t\tg_rom_cards_ptr[0x61c] ^= 0x33;\n\t\tg_rom_cards_ptr[0x632] ^= 0xc0;\n\t\tg_rom_cards_ptr[0x633] ^= 0x33;\n\t}\n\n\tfor(i = 1; i < 8; i++) {\n\t\tnames_ptr = g_kegs_rom_card_list[i];\n\t\tif(names_ptr == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tif(*names_ptr == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tret = config_setup_kegs_file(&g_cfg_tmp_path[0], CFG_PATH_MAX,\n\t\t\t\t\t\t\t\tnames_ptr);\n\n\t\tif(ret != 0) {\n\t\t\tfd = open(&(g_cfg_tmp_path[0]), O_RDONLY | O_BINARY);\n\t\t\tif(fd < 0) {\n\t\t\t\tfatal_printf(\"Open card ROM file %s failed: %d \"\n\t\t\t\t\t\"err:%d\\n\", &g_cfg_tmp_path[0], fd,\n\t\t\t\t\terrno);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlen = 256;\n\t\t\tret = (int)read(fd, &g_rom_cards_ptr[i*0x100], len);\n\n\t\t\tif(ret != len) {\n\t\t\t\tfatal_printf(\"While reading card ROM %s, file \"\n\t\t\t\t\t\"is too short. (%d) Expected %d bytes, \"\n\t\t\t\t\t\"read %d bytes\\n\", errno, len, ret);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tclose(fd);\n\t\t}\n\t}\n\n\tmore_than_8mb = (g_mem_size_exp > 0x800000);\n\t/* Only do the patch if users wants more than 8MB of expansion mem */\n\n\tchanged_rom = 0;\n\tif(g_rom_version == 1) {\n\t\t/* make some patches to ROM 01 */\n#if 0\n\t\t/* 1: Patch ROM selftest to not do speed test */\n\t\tprintf(\"Patching out speed test failures from ROM 01\\n\");\n\t\tg_rom_fc_ff_ptr[0x3785a] = 0x18;\n\t\tchanged_rom = 1;\n#endif\n\n#if 0\n\t\t/* 2: Patch ROM selftests not to do tests 2,4 */\n\t\t/* 0 = skip, 1 = do it, test 1 is bit 0 of LSByte */\n\t\tg_rom_fc_ff_ptr[0x371e9] = 0xf5;\n\t\tg_rom_fc_ff_ptr[0x371ea] = 0xff;\n\t\tchanged_rom = 1;\n#endif\n\n\t\tif(more_than_8mb) {\n\t\t\t/* Geoff Weiss patch to use up to 14MB of RAM */\n\t\t\tg_rom_fc_ff_ptr[0x30302] = 0xdf;\n\t\t\tg_rom_fc_ff_ptr[0x30314] = 0xdf;\n\t\t\tg_rom_fc_ff_ptr[0x3031c] = 0x00;\n\t\t\tchanged_rom = 1;\n\t\t}\n\n\t\t/* Patch ROM selftest to not do ROM cksum if any changes*/\n\t\tif(changed_rom) {\n\t\t\tg_rom_fc_ff_ptr[0x37a06] = 0x18;\n\t\t\tg_rom_fc_ff_ptr[0x37a07] = 0x18;\n\t\t}\n\t} else if(g_rom_version == 3) {\n\t\t/* patch ROM 03 */\n\t\tprintf(\"Patching ROM 03 smartport bug\\n\");\n\t\t/* 1: Patch Smartport code to fix a stupid bug */\n\t\t/*  that causes it to write the IWM status reg into c036, */\n\t\t/*  which is the system speed reg...it's \"safe\" since */\n\t\t/*  IWM status reg bit 4 must be 0 (7MHz)..., otherwise */\n\t\t/*  it might have turned on shadowing in all banks! */\n\t\tg_rom_fc_ff_ptr[0x357c9] = 0x00;\n\t\tchanged_rom = 1;\n\n#if 0\n\t\t/* patch ROM 03 to not to speed test */\n\t\t/*  skip fast speed test */\n\t\tg_rom_fc_ff_ptr[0x36ad7] = 0x18;\n\t\tg_rom_fc_ff_ptr[0x36ad8] = 0x18;\n\t\tchanged_rom = 1;\n#endif\n\n#if 0\n\t\t/*  skip slow speed test */\n\t\tg_rom_fc_ff_ptr[0x36ae7] = 0x18;\n\t\tg_rom_fc_ff_ptr[0x36ae8] = 0x6b;\n\t\tchanged_rom = 1;\n#endif\n\n#if 0\n\t\t/* 4: Patch ROM 03 selftests not to do tests 1-4 */\n\t\tg_rom_fc_ff_ptr[0x364a9] = 0xf0;\n\t\tg_rom_fc_ff_ptr[0x364aa] = 0xff;\n\t\tchanged_rom = 1;\n#endif\n\n\t\t/* ROM tests are in ff/6403-642x, where 6403 = addr of */\n\t\t/*  test 1, etc. */\n\n\t\tif(more_than_8mb) {\n\t\t\t/* Geoff Weiss patch to use up to 14MB of RAM */\n\t\t\tg_rom_fc_ff_ptr[0x30b] = 0xdf;\n\t\t\tg_rom_fc_ff_ptr[0x31d] = 0xdf;\n\t\t\tg_rom_fc_ff_ptr[0x325] = 0x00;\n\t\t\tchanged_rom = 1;\n\t\t}\n\n\t\tif(changed_rom) {\n\t\t\t/* patch ROM 03 selftest to not do ROM cksum */\n\t\t\tg_rom_fc_ff_ptr[0x36cb0] = 0x18;\n\t\t\tg_rom_fc_ff_ptr[0x36cb1] = 0x18;\n\t\t}\n\n\t}\n}\n\nvoid\nconfig_parse_config_kegs_file()\n{\n\tchar\t*bufptr;\n\tconst char *str;\n\tdword64\tdsize;\n\tint\tfd, pos, start, size, last_c, line, ret, c, maxlen;\n\tint\ti;\n\n\tprintf(\"Parsing config.kegs file: %s\\n\", g_config_kegs_name);\n\n\tclk_bram_zero();\n\n\tg_highest_smartport_unit = -1;\n\n\tcfg_get_base_path(&g_cfg_cwd_str[0], g_config_kegs_name, 0);\n\tif(g_cfg_cwd_str[0] != 0) {\n\t\tret = chdir(&g_cfg_cwd_str[0]);\n\t\tif(ret != 0) {\n\t\t\tprintf(\"chdir to %s, errno:%d\\n\", g_cfg_cwd_str, errno);\n\t\t}\n\t\t// Do basename(g_config_kegs_name)--on it's own buffer\n\t\tstr = cfg_str_basename(g_config_kegs_name);\n\t\tmaxlen = sizeof(g_config_kegs_name);\n\t\tcfg_strncpy(&g_config_kegs_name[0], str, maxlen);\n\t}\n\n\t// In any case, copy the current directory path to g_cfg_cwd_str\n\t(void)!getcwd(&g_cfg_cwd_str[0], CFG_PATH_MAX);\n\tprintf(\"CWD is now: %s\\n\", &g_cfg_cwd_str[0]);\n\n\tfd = open(g_config_kegs_name, O_RDONLY | O_BINARY);\n\tdsize = 0;\n\tif(fd >= 0) {\n\t\tdsize = cfg_get_fd_size(fd);\n\t}\n\tif((fd < 0) || (dsize >= (1 << 30))) {\n\t\tfatal_printf(\"cannot open config.kegs at %s, or it is too \"\n\t\t\t\"large!  Stopping!\\n\", g_config_kegs_name);\n\t\tmy_exit(3);\n\t\treturn;\n\t}\n\tsize = (int)dsize;\n\tbufptr = malloc(size + 2);\n\tret = (int)cfg_read_from_fd(fd, (byte *)bufptr, 0, size);\n\tclose(fd);\n\tif(ret != size) {\n\t\tfree(bufptr);\n\t\tfatal_printf(\"Could not read config.kegs at %s\\n\",\n\t\t\t\t\t\t\tg_config_kegs_name);\n\t\tmy_exit(3);\n\t\treturn;\n\t}\n\tbufptr[size] = 0;\t\t// Ensure it's null terminated\n\n\tline = 0;\n\tpos = 0;\n\tlast_c = 0;\n\twhile(pos < size) {\n\t\tline++;\n\t\tif((bufptr[pos] == '\\n') && (last_c == '\\r')) {\n\t\t\t// CR,LF, just eat the LF\n\t\t\tpos++;\n\t\t}\n\t\tstart = pos;\n\t\twhile(pos < size) {\n\t\t\tc = bufptr[pos];\n\t\t\tif((c == 0) || (c == '\\n') || (c == '\\r')) {\n\t\t\t\tlast_c = c;\n\t\t\t\tbufptr[pos] = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tpos++;\n\t\t}\n\t\tcfg_parse_one_line(&bufptr[start], line);\n\t\tpos++;\n\t}\n\n\tfree(bufptr);\n\n\t// And now do command line argument overrides\n\tfor(i = 0; i < g_cfg_argv_num_overrides; i++) {\n\t\tprintf(\"Doing override %d, %s\\n\", i, g_cfg_argv_overrides[i]);\n\t\tcfg_parse_one_line(g_cfg_argv_overrides[i], 1001 + i);\n\t\tg_config_kegs_update_needed = 1;\n\t}\n}\n\nvoid\ncfg_parse_one_line(char *buf, int line)\n{\n\tCfg_menu *menuptr;\n\tCfg_defval *defptr;\n\tint\t*iptr;\n\tconst char *nameptr;\n\tint\tpos, num_equals, type, val, c, len;\n\tint\ti;\n\n\t// warning: modifies memory of bufptr (turns spaces to nulls)\n\tif(line) {\t\t// Avoid unused parameter warning\n\t}\n\n\tlen = (int)strlen(buf);\n\tif(len <= 1) {\t\t// Not a valid line, just get out\n\t\treturn;\n\t}\n\n\t// printf(\"disk_conf[%d]: %s\\n\", line, buf);\n\tif(buf[0] == '#') {\n\t\tiwm_printf(\"Skipping comment\\n\");\n\t\treturn;\n\t}\n\n\tpos = 0;\n\n\twhile((pos < len) && (buf[pos] == ' ' || buf[pos] == '\\t') ) {\n\t\tpos++;\t\t// Eat whitespace\n\t}\n\tif(pos >= len) {\n\t\treturn;\t\t// blank line\n\t}\n\tif((pos + 5) < len) {\n\t\tc = buf[pos+1];\t\t// Slot number\n\t\tif((buf[pos] == 's') && (buf[pos+2] == 'd') &&\n\t\t\t\t\t(c >= '5' && c <= '7')) {\n\t\t\t// looks like s5d1 through s7d15, parse that\n\t\t\tcfg_parse_sxdx(buf, pos, len);\n\t\t\treturn;\n\t\t}\n\t}\n\n// parse buf from pos into option, \"=\" and then \"rest\"\n\n\tif(strncmp(&buf[pos], \"bram\", 4) == 0) {\n\t\tcfg_parse_bram(buf, pos+4, len);\n\t\treturn;\n\t}\n\n\t// find \"name\" as first contiguous string\n\t//printf(\"...parse_option: line %d, %s (%s) len:%d\\n\", line, buf,\n\t//\t\t\t\t\t\t&buf[pos], len);\n\n\tnameptr = &buf[pos];\n\twhile(pos < len) {\n\t\tc = buf[pos];\n\t\tif((c == 0) || (c == ' ') || (c == '\\t') || (c == '\\n') ||\n\t\t\t\t\t\t\t\t(c == '=')) {\n\t\t\tbreak;\n\t\t}\n\t\tpos++;\n\t}\n\tbuf[pos] = 0;\n\tif(strcmp(nameptr, \"rom\") == 0) {\n\t\t// Translate argument of \"-rom\" to \"g_cfg_rom_path\"\n\t\tnameptr = \"g_cfg_rom_path\";\n\t}\n\tpos++;\n\n\t// Eat up all whitespace and '='\n\tnum_equals = 0;\n\twhile(pos < len) {\n\t\tc = buf[pos];\n\t\tif((c == '=') && (num_equals == 0)) {\n\t\t\tpos++;\n\t\t\tnum_equals++;\n\t\t} else if(c == ' ' || c == '\\t') {\n\t\t\tpos++;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* Look up nameptr to find type */\n\ttype = -1;\n\tdefptr = 0;\n\tmenuptr = 0;\n\tfor(i = 0; i < g_cfg_defval_index; i++) {\n\t\tdefptr = &(g_cfg_defvals[i]);\n\t\tmenuptr = defptr->menuptr;\n\t\tif(strcmp(menuptr->name_str, nameptr) == 0) {\n\t\t\ttype = menuptr->cfgtype;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tswitch(type) {\n\tcase CFGTYPE_INT:\n\t\t/* use strtol */\n\t\tval = (int)strtol(&buf[pos], 0, 0);\n\t\tiptr = (int *)menuptr->ptr;\n\t\tcfg_int_update(iptr, val);\n\t\tbreak;\n\tcase CFGTYPE_FILE:\n\tcase CFGTYPE_STR:\n\t\tcfg_file_update_ptr(menuptr->ptr, &buf[pos], 0);\n\t\tbreak;\n\tdefault:\n\t\tprintf(\"Config file variable %s is unknown type: %d\\n\",\n\t\t\tnameptr, type);\n\t}\n}\n\nvoid\ncfg_parse_bram(char *buf, int pos, int len)\n{\n\tword32\tval;\n\tint\tbram_num, offset;\n\n\t// Format: \"bram1[00] = xx yy...\" or \"bram3[00] = xx yy ...\"\n\t// pos = position just after \"bram\"\n\tif((len < (pos+5)) || (buf[pos+1] != '[') || (buf[pos+4] != ']')) {\n\t\tfatal_printf(\"While reading config.kegs, found malformed bram \"\n\t\t\t\"statement: %s\\n\", buf);\n\t\treturn;\n\t}\n\tbram_num = buf[pos] - '0';\n\tif((bram_num != 1) && (bram_num != 3)) {\n\t\tfatal_printf(\"While reading config.kegs, found bad bram \"\n\t\t\t\"num: %s\\n\", buf);\n\t\treturn;\n\t}\n\n\tbram_num = bram_num >> 1;\t// turn 3->1 and 1->0\n\n\toffset = (int)strtoul(&(buf[pos+2]), 0, 16);\n\tpos += 5;\n\twhile(pos < len) {\n\t\tif((buf[pos] == ' ') || (buf[pos] == '\\t') ||\n\t\t\t\t(buf[pos] == 0x0a) || (buf[pos] == 0x0d) ||\n\t\t\t\t(buf[pos] == '=')) {\n\t\t\tpos++;\n\t\t\tcontinue;\n\t\t}\n\t\tval = (word32)strtoul(&buf[pos], 0, 16);\t// As hex\n\t\tclk_bram_set(bram_num, offset, val);\n\t\toffset++;\n\t\tpos += 2;\n\t}\n}\n\nvoid\ncfg_parse_sxdx(char *buf, int pos, int len)\n{\n\tchar\t*name_ptr, *partition_name;\n\tword32\tdynamic_blocks;\n\tint\tpart_num, ejected, slot, drive, c;\n\n\tslot = buf[pos+1] - '0';\n\tdrive = buf[pos+3] - '0';\n\n\t/* skip over slot, drive */\n\tpos += 4;\n\tif((buf[pos] >= '0') && (buf[pos] <= '9')) {\n\t\t// Second digit of drive is valid\n\t\tdrive = (drive * 10) + buf[pos] - '0';\n\t\tpos++;\n\t}\n\n\tdrive--;\t// make sxd1 mean index 0\n\n\twhile((pos < len) && ((buf[pos] == ' ') || (buf[pos] == '\\t') ||\n\t\t\t\t\t\t\t(buf[pos] == '=')) ) {\n\t\tpos++;\n\t}\n\n\tejected = 0;\n\tif(buf[pos] == '#') {\t// disk is ejected, but read info anyway\n\t\tejected = 1;\n\t\tpos++;\n\t}\n\n\tpartition_name = 0;\n\tpart_num = -1;\n\tdynamic_blocks = 0;\n\tif(buf[pos] == ':') {\t\t// yup, it's got a partition name!\n\t\tpos++;\n\t\tpartition_name = &buf[pos];\n\t\twhile((pos < len) && (buf[pos] != ':')) {\n\t\t\tpos++;\n\t\t}\n\t\tbuf[pos] = 0;\t/* null terminate partition name */\n\t\tpos++;\n\t}\n\tc = buf[pos];\n\tif((c == ';') || (c == '@')) {\n\t\tpos++;\n\t\t// ; - partition number;  @ - Dynamic ProDOS dir size\n\t\tpart_num = 0;\n\t\twhile((pos < len) && (buf[pos] != ':')) {\n\t\t\tpart_num = (10*part_num) + buf[pos] - '0';\n\t\t\tpos++;\n\t\t}\n\t\tpos++;\n\t\tif(c == '@') {\t\t// Dynamic ProDOS directory\n\t\t\tdynamic_blocks = part_num * 2;\n\t\t\tpart_num = -1;\n\t\t}\n\t}\n\n\t/* Get filename */\n\tname_ptr = &(buf[pos]);\n\tif((pos >= len) || (name_ptr[0] == 0)) {\n\t\treturn;\n\t}\n\n\tinsert_disk(slot, drive, name_ptr, ejected, partition_name,\n\t\t\t\t\t\tpart_num, dynamic_blocks);\n}\n\nvoid\nconfig_generate_config_kegs_name(char *outstr, int maxlen, Disk *dsk,\n\t\tint with_extras)\n{\n\tchar\t*str;\n\n\tstr = outstr;\n\n\tif(with_extras) {\n\t\tif(dsk->fd < 0) {\n\t\t\tsnprintf(str, maxlen - (str - outstr), \"#\");\n\t\t\tstr = &outstr[strlen(outstr)];\n\t\t}\n\t\tif(dsk->dynapro_blocks) {\n\t\t\tsnprintf(str, maxlen - (str - outstr), \"@%d:\",\n\t\t\t\t\t(dsk->dynapro_blocks + 1) / 2);\n\t\t\tstr = &outstr[strlen(outstr)];\n\t\t} else if(dsk->partition_name != 0) {\n\t\t\tsnprintf(str, maxlen - (str - outstr), \":%s:\",\n\t\t\t\t\t\t\tdsk->partition_name);\n\t\t\tstr = &outstr[strlen(outstr)];\n\t\t} else if(dsk->partition_num >= 0) {\n\t\t\tsnprintf(str, maxlen - (str - outstr), \";%d:\",\n\t\t\t\t\t\t\tdsk->partition_num);\n\t\t\tstr = &outstr[strlen(outstr)];\n\t\t}\n\t}\n\tsnprintf(str, maxlen - (str - outstr), \"%s\", dsk->name_ptr);\n}\n\nchar *\nconfig_write_config_kegs_file(int get_status)\n{\n\tFILE\t*fconf;\n\tDisk\t*dsk;\n\tCfg_defval *defptr;\n\tCfg_menu *menuptr;\n\tchar\t*curstr, *defstr;\n\tint\tdefval, curval, type, slot, drive;\n\tint\ti;\n\n\tif(get_status) {\n\t\treturn 0;\n\t}\n\tprintf(\"Writing config.kegs file to %s\\n\", g_config_kegs_name);\n\n\tfconf = fopen(g_config_kegs_name, \"w+\");\n\tif(fconf == 0) {\n\t\thalt_printf(\"cannot open %s!  Stopping!\\n\", g_config_kegs_name);\n\t\treturn 0;\n\t}\n\n\tfprintf(fconf, \"# KEGS configuration file version %s\\n\",\n\t\t\t\t\t\tg_kegs_version_str);\n\n\tfor(i = 0; i < MAX_C7_DISKS + 4; i++) {\n\t\tslot = 7;\n\t\tdrive = i - 4;\n\t\tif(i < 4) {\n\t\t\tslot = (i >> 1) + 5;\n\t\t\tdrive = i & 1;\n\t\t}\n\t\tif(drive == 0) {\n\t\t\tfprintf(fconf, \"\\n\");\t/* an extra blank line */\n\t\t}\n\n\t\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\t\tif(dsk->name_ptr == 0 && (i > 4)) {\n\t\t\t/* No disk, not even ejected--just skip */\n\t\t\tcontinue;\n\t\t}\n\t\tfprintf(fconf, \"s%dd%d = \", slot, drive + 1);\n\t\tif(dsk->name_ptr == 0) {\n\t\t\tfprintf(fconf, \"\\n\");\n\t\t\tcontinue;\n\t\t}\n\t\tconfig_generate_config_kegs_name(&g_cfg_tmp_path[0],\n\t\t\t\t\t\t\tCFG_PATH_MAX, dsk, 1);\n\t\tfprintf(fconf, \"%s\\n\", &g_cfg_tmp_path[0]);\n\t}\n\n\tfprintf(fconf, \"\\n\");\n\n\t/* See if any variables are different than their default */\n\tfor(i = 0; i < g_cfg_defval_index; i++) {\n\t\tdefptr = &(g_cfg_defvals[i]);\n\t\tmenuptr = defptr->menuptr;\n\t\tdefval = defptr->intval;\n\t\ttype = menuptr->cfgtype;\n\t\tif(type == CFGTYPE_INT) {\n\t\t\tcurval = *((int *)menuptr->ptr);\n\t\t\tif(curval != defval) {\n\t\t\t\tfprintf(fconf, \"%s = %d\\n\", menuptr->name_str,\n\t\t\t\t\t\t\t\tcurval);\n\t\t\t}\n\t\t}\n\t\tif((type == CFGTYPE_FILE) || (type == CFGTYPE_STR)) {\n\t\t\tcurstr = *((char **)menuptr->ptr);\n\t\t\tdefstr = *((char **)menuptr->defptr);\n\t\t\tif(strcmp(curstr, defstr) != 0) {\n\t\t\t\tfprintf(fconf, \"%s = %s\\n\", menuptr->name_str,\n\t\t\t\t\t\t\t\tcurstr);\n\t\t\t}\n\t\t}\n\t}\n\n\tfprintf(fconf, \"\\n\");\n\n\t/* write bram state */\n\tclk_write_bram(fconf);\n\n\tfclose(fconf);\n\n\tg_config_kegs_update_needed = 0;\n\treturn 0;\n}\n\nvoid\ninsert_disk(int slot, int drive, const char *name, int ejected,\n\t\tconst char *partition_name, int part_num, word32 dynamic_blocks)\n{\n\tbyte\tbuf_2img[512];\n\tDisk\t*dsk;\n\tchar\t*name_ptr, *part_ptr;\n\tdword64\tdsize, dunix_pos, exp_dsize, dtmp;\n\tword32\tlen_bits, save_ftrack;\n\tint\timage_type, part_len, ret, locked, len, is_po, name_len;\n\tint\ti;\n\n\tg_config_kegs_update_needed = 1;\n\n\tif((slot < 5) || (slot > 7)) {\n\t\tfatal_printf(\"Invalid slot for insertiing disk: %d\\n\", slot);\n\t\treturn;\n\t}\n\tif(drive < 0 || ((slot == 7) && (drive >= MAX_C7_DISKS)) ||\n\t\t\t\t\t((slot < 7) && (drive > 1))) {\n\t\tfatal_printf(\"Invalid drive for inserting disk: %d\\n\", drive);\n\t\treturn;\n\t}\n\n\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\n#if 1\n\tprintf(\"Inserting disk %s (%s or %d) in slot %d, drive: %d, \"\n\t\t\"dyna_blocks:%d\\n\", name, partition_name, part_num, slot, drive,\n\t\tdynamic_blocks);\n#endif\n\n\t// DO NOT change dsk->just_ejected.  If a disk was just ejected, then\n\t//  leave it alone.  Otherwise, if we are a newly inserted disk,\n\t//  it should already be 0, so leave it alone\n\t//dsk->just_ejected = 1;\n\n\tif(dsk->fd >= 0) {\n\t\tiwm_eject_disk(dsk);\n\t}\n\n\t/* Before opening, make sure no other mounted disk has this name */\n\t/* If so, unmount it */\n\tif(!ejected) {\n\t\tfor(i = 0; i < 2; i++) {\n\t\t\tiwm_eject_named_disk(5, i, name, partition_name);\n\t\t\tiwm_eject_named_disk(6, i, name, partition_name);\n\t\t}\n\t\tfor(i = 0; i < MAX_C7_DISKS; i++) {\n\t\t\tiwm_eject_named_disk(7, i, name, partition_name);\n\t\t}\n\t}\n\n\t/* free old name_ptr, partition_name */\n\tfree(dsk->name_ptr);\n\tfree(dsk->partition_name);\n\tdsk->name_ptr = 0;\n\tdsk->partition_name = 0;\n\n\tname_ptr = kegs_malloc_str(name);\n\tdsk->name_ptr = name_ptr;\n\tname_len = (int)strlen(dsk->name_ptr);\n\n\tpart_len = 0;\n\tpart_ptr = 0;\n\tif(partition_name != 0) {\n\t\tpart_ptr = kegs_malloc_str(partition_name);\n\t\tpart_len = (int)strlen(part_ptr);\n\t\tdsk->partition_name = part_ptr;\n\t}\n\tdsk->partition_num = part_num;\n\tdsk->dynapro_blocks = dynamic_blocks;\n\n\tiwm_printf(\"Opening up disk image named: %s\\n\", name_ptr);\n\n\tif(ejected) {\n\t\t/* just get out of here */\n\t\tdsk->fd = -1;\n\t\treturn;\n\t}\n\n\tdsk->fd = -1;\n\tdsk->raw_data = 0;\n\tdsk->image_type = 0;\n\tdsk->dimage_start = 0;\n\tdsk->dimage_size = 0;\n\tdsk->write_prot = 0;\n\tdsk->write_through_to_unix = 1;\n\timage_type = 0;\n\tlocked = 0;\n\n\tif(dynamic_blocks) {\n\t\tret = dynapro_mount(dsk, name_ptr, dynamic_blocks);\n\t\tif(ret < 0) {\n\t\t\tiwm_eject_disk(dsk);\n\t\t\treturn;\n\t\t}\n\t\timage_type = DSK_TYPE_DYNAPRO;\n\t\tprintf(\"After dynapro_mount, write_through:%d\\n\",\n\t\t\t\t\t\tdsk->write_through_to_unix);\n\t}\n\tif((partition_name != 0) || (part_num >= 0)) {\n\t\tret = cfg_partition_find_by_name_or_num(dsk, partition_name,\n\t\t\t\t\t\t\t\tpart_num);\n\t\tprintf(\"partition %s (num %d) mounted, wr_prot: %d, ret:%d\\n\",\n\t\t\t\tpartition_name, part_num, dsk->write_prot, ret);\n\n\t\tif(ret < 0) {\n\t\t\tiwm_eject_disk(dsk);\n\t\t\treturn;\n\t\t}\n\t\tlocked = dsk->write_prot;\n\t\tif(dsk->raw_data) {\n\t\t\t// .zip file or something similar.  Do name matching on\n\t\t\t//  partition name\n\t\t\tname_len = part_len;\n\t\t\tname_ptr = part_ptr;\n\t\t\tlocked = 1;\n\t\t}\n\t}\n\n\tif((name_len > 3) && !image_type &&\n\t\t\t\t!cfgcasecmp(\".gz\", &name_ptr[name_len - 3])) {\n\t\t// it's gzip'ed, try to gunzip it to dsk->raw_data\n\t\tundeflate_gzip(dsk, name_ptr);\n\n\t\tlocked = 1;\n\t\tdsk->dimage_start = 0;\n\t\tdsk->dimage_size = dsk->raw_dsize;\n\t\tname_len -= 3;\t\t// So .dsk, .po look for correct chars\n\t}\n\n\tif((name_len > 4) && !image_type &&\n\t\t\t\t!cfgcasecmp(\".bz2\", &name_ptr[name_len - 4])) {\n\t\t// it's bzip2'ed, try to bunzip2 it to dsk->raw_data\n\t\tconfig_file_to_pipe(dsk, \"bunzip2\", name_ptr);\n\t\tlocked = 1;\n\n\t\t// Reduce name_len by 4 so that subsequent compares for .po\n\t\t//  look at the correct chars\n\t\tname_len -= 4;\n\t}\n\n\tif((name_len > 4) && !image_type &&\n\t\t\t\t!cfgcasecmp(&name_ptr[name_len - 4], \".sdk\")) {\n\t\t// it's a ShrinkIt archive with a disk image in it\n\t\tunshk(dsk, name_ptr);\n\t\tlocked = 1;\n\t\timage_type = DSK_TYPE_PRODOS;\n\t\tprintf(\"dsk->fd:%d dsk->raw_data:%p, raw_dsize:%lld\\n\", dsk->fd,\n\t\t\t\t\t\tdsk->raw_data, dsk->raw_dsize);\n\t\tdsk->dimage_start = 0;\n\t\tdsk->dimage_size = dsk->raw_dsize;\n\t}\n\n\tif((name_len > 4) && !image_type && dsk->disk_525 &&\n\t\t\t\t!cfgcasecmp(\".nib\", &name_ptr[name_len-4])) {\n\t\t// Old, obsolete .nib 5.25\" nibblized format.  Support is\n\t\t//  read-only\n\t\timage_type = DSK_TYPE_NIB;\n\t\tlocked = 1;\n\t}\n\n\tif((dsk->fd < 0) && !locked && !dynamic_blocks) {\n\t\tdsk->fd = open(name_ptr, O_RDWR | O_BINARY, 0x1b6);\n\t}\n\n\tif((dsk->fd < 0) && !dynamic_blocks) {\n\t\tprintf(\"Trying to open %s read-only, errno: %d\\n\", name_ptr,\n\t\t\t\t\t\t\t\terrno);\n\t\tdsk->fd = open(name_ptr, O_RDONLY | O_BINARY, 0x1b6);\n\t\tlocked = 2;\n\t}\n\n\tiwm_printf(\"open returned: %d\\n\", dsk->fd);\n\n\tif((dsk->fd < 0) && !dynamic_blocks) {\n\t\tfatal_printf(\"Disk image %s does not exist!\\n\", name_ptr);\n\t\tfree(dsk->raw_data);\n\t\treturn;\n\t}\n\n\t//printf(\"Checking if it's woz, name_ptr:%s. %d vs %d\\n\", name_ptr,\n\t//\tname_len, (int)strlen(name_ptr));\n\tif((name_len > 4) && !image_type &&\n\t\t\t\t!cfgcasecmp(&name_ptr[name_len - 4], \".woz\")) {\n\t\t// it's a WOZ applesauce disk image\n\t\timage_type = DSK_TYPE_WOZ;\n\t\tprintf(\"It is woz!\\n\");\n\t}\n\n\tif(locked) {\n\t\tdsk->write_prot = locked;\n\t}\n\n\tsave_ftrack = dsk->cur_frac_track;\t/* save arm position */\n\n\t/* See if it is in 2IMG format */\n\tif(dsk->raw_data) {\n\t\t// Just do a copy from raw_data\n\t\tfor(i = 0; i < 512; i++) {\n\t\t\tbuf_2img[i] = dsk->raw_data[i];\n\t\t}\n\t\tdsize = dsk->raw_dsize;\n\t\tif(!dsk->dynapro_info_ptr) {\n\t\t\tdsk->write_through_to_unix = 0;\n\t\t}\n\t} else {\n\t\tret = (int)read(dsk->fd, (char *)&buf_2img[0], 512);\n\t\tdsize = cfg_get_fd_size(dsk->fd);\n\t}\n\n#if 0\n\t/* Try to guess that there is a Mac Binary header of 128 bytes */\n\t/* See if image size & 0xfff = 0x080 which indicates extra 128 bytes */\n\tif(((dsize & 0xfff) == 0x080) && (dsize < (801*1024)) && !image_type) {\n\t\tprintf(\"Assuming Mac Binary header on %s\\n\", dsk->name_ptr);\n\t\tdsk->dimage_start += 0x80;\n\t\tdsize -= 0x80;\n\t}\n#endif\n\n\tdsk->dimage_size = dsize;\n\n\tif(!image_type && (buf_2img[0] == '2') && (buf_2img[1] == 'I') &&\n\t\t\t\t(buf_2img[2] == 'M') && (buf_2img[3] == 'G')) {\n\t\t/* It's a 2IMG disk */\n\t\tprintf(\"Image named %s is in 2IMG format\\n\", dsk->name_ptr);\n\t\timage_type = DSK_TYPE_PRODOS;\n\n\t\tif(buf_2img[12] == 0) {\n\t\t\tprintf(\"2IMG is in DOS 3.3 sector order\\n\");\n\t\t\timage_type = DSK_TYPE_DOS33;\n\t\t}\n\t\tif(buf_2img[19] & 0x80) {\n\t\t\t/* disk is locked */\n\t\t\tprintf(\"2IMG is write protected\\n\");\n\t\t\tif(dsk->write_prot == 0) {\n\t\t\t\tdsk->write_prot = 1;\n\t\t\t}\n\t\t}\n\t\tif((buf_2img[17] & 1) && (dsk->image_type == DSK_TYPE_DOS33)) {\n\t\t\tdsk->vol_num = buf_2img[16];\n\t\t\tprintf(\"Setting DOS 3.3 vol num to %d\\n\", dsk->vol_num);\n\t\t}\n\t\t//\tSome 2IMG archives have the size byte reversed\n\t\tdsize = (buf_2img[31] << 24) + (buf_2img[30] << 16) +\n\t\t\t\t(buf_2img[29] << 8) + buf_2img[28];\n\t\tdunix_pos = (buf_2img[27] << 24) + (buf_2img[26] << 16) +\n\t\t\t\t(buf_2img[25] << 8) + buf_2img[24];\n\t\tif(dsize == 0x800c00) {\n\t\t\t//\tByte reversed 0x0c8000\n\t\t\tdsize = 0x0c8000;\n\t\t}\n\t\tif(dsize == 0) {\n\t\t\t/* Sweet-16 makes some images with size == 0 */\n\t\t\t/* Example: Prosel from */\n\t\t\t/*  www.whatisthe2gs.apple2.org.za/the_ring/ */\n\t\t\tif(buf_2img[12] == 1) {\n\t\t\t\t/* then get the size from 0x14 in blocks */\n\t\t\t\tdsize = (buf_2img[23] << 24) +\n\t\t\t\t\t(buf_2img[22] << 16) +\n\t\t\t\t\t(buf_2img[21] << 8) + buf_2img[20];\n\t\t\t\tdsize = dsize * 512;\t/* it was blocks */\n\t\t\t}\n\t\t}\n\t\tdsk->dimage_start = dunix_pos;\n\t\tdsk->dimage_size = dsize;\n\t}\n\texp_dsize = 800*1024;\n\tdsk->fbit_mult = 256;\n\tif(dsk->disk_525) {\n\t\texp_dsize = 140*1024;\n\t\tdsk->fbit_mult = 128;\n\t}\n\tif(!image_type) {\n\t\t/* See if it might be the Mac diskcopy format */\n\t\tdtmp = cfg_detect_dc42(&buf_2img[0], dsize, exp_dsize, slot);\n\t\tif(dtmp != 0) {\n\t\t\t// It's diskcopy 4.2\n\t\t\tprintf(\"Image named %s is in Mac diskcopy format\\n\",\n\t\t\t\t\t\t\t\tdsk->name_ptr);\n\t\t\tdsk->dimage_start += 0x54;\n\t\t\tdsk->dimage_size = dtmp;\n\t\t\timage_type = DSK_TYPE_PRODOS;\t/* ProDOS */\n\t\t}\n\t}\n\tif(!image_type) {\n\t\t/* Assume raw image */\n\t\tdsk->dimage_size = dsize;\n\t\timage_type = DSK_TYPE_PRODOS;\n\t\tis_po = (name_len > 3) &&\n\t\t\t\t!cfgcasecmp(\".po\", &name_ptr[name_len-3]);\n\t\tif(dsk->disk_525) {\n\t\t\timage_type = DSK_TYPE_DOS33;\n\t\t\tif(is_po) {\n\t\t\t\timage_type = DSK_TYPE_PRODOS;\n\t\t\t}\n\t\t}\n\t}\n\n\tdsk->image_type = image_type;\n\tdsk->disk_dirty = 0;\n\tdsk->cur_fbit_pos = 0;\n\tdsk->cur_track_bits = 0;\n\tdsk->cur_trk_ptr = 0;\n\n\tif(image_type == DSK_TYPE_WOZ) {\n\t\t// Special handling\n\t\tret = woz_open(dsk, 0);\n\t\tif(!ret) {\n\t\t\tiwm_eject_disk(dsk);\n\t\t\treturn;\n\t\t}\n\t} else if(dsk->smartport) {\n\t\tg_highest_smartport_unit = MY_MAX(dsk->drive,\n\t\t\t\t\t\tg_highest_smartport_unit);\n\n\t\tiwm_printf(\"adding smartport device[%d], img_sz:%08llx\\n\",\n\t\t\tdsk->drive, dsk->dimage_size);\n\t} else if(dsk->disk_525) {\n\t\tdunix_pos = dsk->dimage_start;\n\t\tdsize = dsk->dimage_size;\n\t\tdisk_set_num_tracks(dsk, 4*35);\n\t\tlen = 0x1000;\n\t\tif(dsk->image_type == DSK_TYPE_NIB) {\n\t\t\t// Weird .nib format, has no sync bits\n\t\t\tlen = (int)(dsk->dimage_size / 35);\n\t\t\tfor(i = 0; i < 35; i++) {\n\t\t\t\tdisk_unix_to_nib(dsk, 4*i, dunix_pos, len,\n\t\t\t\t\t\t\t\tlen * 8, 0);\n\t\t\t\tdunix_pos += len;\n\t\t\t}\n\t\t} else {\n\t\t\tfor(i = 0; i < 35; i++) {\n\t\t\t\tlen_bits = iwm_get_default_track_bits(dsk, 4*i);\n\t\t\t\tdisk_unix_to_nib(dsk, 4*i, dunix_pos, len,\n\t\t\t\t\t\t\t\tlen_bits, 0);\n\t\t\t\tdunix_pos += len;\n\t\t\t}\n\t\t}\n\t\tif(dsize != (dword64)35*len) {\n\t\t\tfatal_printf(\"Disk 5.25 error: size is %lld, not %d.  \"\n\t\t\t\t\"Will try to mount anyway\\n\", dsize, 35*len);\n\t\t}\n\t} else {\n\t\t/* disk_35 */\n\t\tdunix_pos = dsk->dimage_start;\n\t\tdsize = dsk->dimage_size;\n\t\tif(dsize != 800*1024) {\n\t\t\tprintf(\"Disk 3.5 Drive %d (Image File: %s), Error: \"\n\t\t\t\t\"size is %lld, not 800K.  Will try to mount \"\n\t\t\t\t\"anyway\\n\", drive+1, name, dsize);\n\t\t}\n\t\tdisk_set_num_tracks(dsk, 2*80);\n\t\tfor(i = 0; i < 2*80; i++) {\n\t\t\tlen = g_track_bytes_35[i >> 5];\n\t\t\tlen_bits = iwm_get_default_track_bits(dsk, i);\n\t\t\tiwm_printf(\"Trk: %d.%d = unix: %08llx, %04x, %04x\\n\",\n\t\t\t\ti>>1, i & 1, dunix_pos, len, len_bits);\n\t\t\tdisk_unix_to_nib(dsk, i, dunix_pos, len, len_bits, 0);\n\t\t\tdunix_pos += len;\n\n\t\t\tiwm_printf(\" trk_bits:%05x\\n\", dsk->trks[i].track_bits);\n\t\t}\n\t}\n\n\tiwm_move_to_ftrack(dsk, save_ftrack, 0, 0);\n}\n\ndword64\ncfg_detect_dc42(byte *bptr, dword64 dsize, dword64 exp_dsize, int slot)\n{\n\tdword64\tddata_size, dtag_size;\n\tint\ti;\n\n\t// Detect Mac DiskCopy4.2 disk image (often .dmg or .dc or .dc42)\n\t// bptr points to just the first 512 bytes of the file\n\n\tif((bptr[0x52] != 1) || (bptr[0x53] != 0)) {\n\t\treturn 0;\t// No \"magic number 0x01, 0x00 for DiskCopy4.2\n\t}\n\tif(bptr[0] > 0x3f) {\n\t\treturn 0;\t// not a valid image name length (1-0x3f)\n\t}\n\tddata_size = 0;\n\tdtag_size = 0;\n\tfor(i = 0; i < 4; i++) {\n\t\tddata_size = (ddata_size << 8) | bptr[0x40 + i];\n\t\tdtag_size = (dtag_size << 8) | bptr[0x44 + i];\n\t}\n\tif((dtag_size != 0) && (dtag_size != ((ddata_size >> 9) * 12))) {\n\t\treturn 0;\t// Tags are either 0, or 12 bytes per block\n\t}\n\tif(slot == 7) {\n\t\tif(ddata_size < 140*1024) {\n\t\t\treturn 0;\t\t// Allow any size >=140K slot 7\n\t\t}\n\t} else if(ddata_size != exp_dsize) {\n\t\treturn 0;\t\t\t// Must be 140K or 800K\n\t}\n\n\tif(ddata_size > (dsize - 0x54)) {\n\t\treturn 0;\t// data_size doesn't make sense\n\t}\n\tif((ddata_size & 0x1ff) || ((ddata_size >> 31) > 1)) {\n\t\treturn 0;\t// data_size not a multiple of 512 bytes\n\t}\n\tif((ddata_size + dtag_size + 0x54) != dsize) {\n\t\treturn 0;\t// File doesn't appear to be well-formed\n\t}\n\n\treturn ddata_size;\n}\n\ndword64\ncfg_get_fd_size(int fd)\n{\n\tstruct stat stat_buf;\n\tint\tret;\n\n\tret = fstat(fd, &stat_buf);\n\tif(ret != 0) {\n\t\tfprintf(stderr,\"fstat returned %d on fd %d, errno: %d\\n\",\n\t\t\tret, fd, errno);\n\t\tstat_buf.st_size = 0;\n\t}\n\n\treturn stat_buf.st_size;\n}\n\ndword64\ncfg_read_from_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize)\n{\n\tdword64\tdret, doff;\n\tword32\tthis_len;\n\n\tdret = kegs_lseek(fd, dpos, SEEK_SET);\n\tif(dret != dpos) {\n\t\tprintf(\"lseek failed: %lld\\n\", dret);\n\t\treturn 0;\n\t}\n\tdoff = 0;\n\twhile(1) {\n\t\tif(doff >= dsize) {\n\t\t\tbreak;\n\t\t}\n\t\tthis_len = 1UL << 30;\n\t\tif((dsize - doff) < this_len) {\n\t\t\tthis_len = (word32)(dsize - doff);\n\t\t}\n\t\tdret = read(fd, bufptr + doff, this_len);\n\t\tif((dret + 1) == 0) {\t\t// dret==-1\n\t\t\tprintf(\"read failed\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\tif(dret == 0) {\n\t\t\tprintf(\"Unexpected end of file, tried to read from \"\n\t\t\t\t\"dpos:%lld dsize:%lld\\n\", dpos, dsize);\n\t\t\treturn 0;\n\t\t}\n\t\tdoff += dret;\n\t}\n\treturn doff;\n}\n\ndword64\ncfg_write_to_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize)\n{\n\tdword64\tdret;\n\n\tdret = kegs_lseek(fd, dpos, SEEK_SET);\n\tif(dret != dpos) {\n\t\tprintf(\"lseek failed: %lld\\n\", dret);\n\t\treturn 0;\n\t}\n\treturn must_write(fd, bufptr, dsize);\n}\n\nint\ncfg_partition_maybe_add_dotdot()\n{\n\tint\tpart_len;\n\n\tpart_len = (int)strlen(&(g_cfg_part_path[0]));\n\tif(part_len > 0) {\n\t\t// Add .. entry here\n\t\tcfg_file_add_dirent(&g_cfg_partitionlist, \"..\", 1, 0, 0, 0, 0);\n\t}\n\treturn part_len;\n}\n\nint\ncfg_partition_name_check(const byte *name_ptr, int name_len)\n{\n\tint\tpart_len;\n\tint\ti;\n\n\t// Return 0 if name_ptr is not at the right path depth, 1 if OK\n\n\tpart_len = (int)strlen(&g_cfg_part_path[0]);\n\tfor(i = 0; i < part_len; i++) {\n\t\tif(i >= name_len) {\n\t\t\treturn 0;\n\t\t}\n\t\tif(name_ptr[i] != g_cfg_part_path[i]) {\n\t\t\treturn 0;\n\t\t}\n\t}\n\treturn 1;\n}\n\nint\ncfg_partition_read_block(int fd, void *buf, dword64 blk, int blk_size)\n{\n\tif(!cfg_read_from_fd(fd, buf, blk * blk_size, blk_size)) {\n\t\t// Read failed\n\t\treturn 0;\n\t}\n\treturn blk_size;\n}\n\nint\ncfg_partition_find_by_name_or_num(Disk *dsk, const char *in_partnamestr,\n\t\t\t\t\t\t\t\tint part_num)\n{\n\tCfg_dirent *direntptr;\n\tconst char *partnamestr;\n\tint\tmatch, num_parts, ret, fd, len, c;\n\tint\ti;\n\n#if 0\n\tprintf(\"cfg_partition_find_by_name_or_num: %s, part_num:%d\\n\",\n\t\t\t\t\t\t\tpartnamestr, part_num);\n#endif\n\n\t// We need to copy partnamestr up to the last / to g_cfg_part_path[],\n\t//  and use just the end as the partition name.\n\tcfg_strncpy(&g_cfg_part_path[0], in_partnamestr, CFG_PATH_MAX);\n\tlen = (int)strlen(in_partnamestr);\n\tpartnamestr = in_partnamestr;\n\tfor(i = len - 1; i >= 0; i--) {\n\t\tc = g_cfg_part_path[i];\n\t\tif(c == '/') {\n\t\t\tpartnamestr = &(in_partnamestr[i+1]);\n\t\t\tbreak;\n\t\t}\n\t\tg_cfg_part_path[i] = 0;\n\t}\n\n\tfd = open(dsk->name_ptr, O_RDONLY | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\tfatal_printf(\"Cannot open disk image: %s\\n\", dsk->name_ptr);\n\t\treturn -1;\n\t}\n\n\tnum_parts = cfg_partition_make_list(fd);\n\n\tif(num_parts <= 0) {\n\t\tprintf(\"num_parts: %d\\n\", num_parts);\n\t\tclose(fd);\n\t\treturn -1;\n\t}\n\n\tfor(i = 0; i < g_cfg_partitionlist.last; i++) {\n\t\tdirentptr = &(g_cfg_partitionlist.direntptr[i]);\n\t\tmatch = 0;\n\t\tif((strcmp(partnamestr, direntptr->name) == 0) &&\n\t\t\t\t\t\t\t(part_num < 0)) {\n\t\t\t//printf(\"partition, match1, name:%s %s, part_num:%d\\n\",\n\t\t\t//\tpartnamestr, direntptr->name, part_num);\n\n\t\t\tmatch = 1;\n\t\t}\n\t\tif((partnamestr == 0) && (direntptr->part_num == part_num)) {\n\t\t\t//printf(\"partition, match2, n:%s, part_num:%d == %d\\n\",\n\t\t\t//\tdirentptr->name, direntptr->part_num, part_num);\n\t\t\tmatch = 1;\n\t\t}\n\t\tif(match) {\n\t\t\t//printf(\"match with dimage_start:%08llx, dimage_size:\"\n\t\t\t//\t\"%08llx\\n\", dsk->dimage_start,\n\t\t\t//\tdsk->dimage_size);\n\n\t\t\tprintf(\"match with dimage_start:%08llx, dimage_size:\"\n\t\t\t\t\"%08llx\\n\", direntptr->dimage_start,\n\t\t\t\tdirentptr->dsize);\n\t\t\tret = i;\n\t\t\tif(g_cfg_partition_is_zip) {\n\t\t\t\tret = undeflate_zipfile(dsk, fd,\n\t\t\t\t\tdirentptr->dimage_start,\n\t\t\t\t\tdirentptr->dsize,\n\t\t\t\t\tdirentptr->compr_dsize);\n\t\t\t\tclose(fd);\n\t\t\t} else {\n\t\t\t\tdsk->fd = fd;\n\t\t\t\tdsk->dimage_start = direntptr->dimage_start;\n\t\t\t\tdsk->dimage_size = direntptr->dsize;\n\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\tclose(fd);\n\t// printf(\"No matches, ret -1\\n\");\n\treturn -1;\n}\n\nint\ncfg_partition_make_list_from_name(const char *namestr)\n{\n\tint\tfd, ret;\n\n\tfd = open(namestr, O_RDONLY | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\tfatal_printf(\"Cannot open part image: %s\\n\", namestr);\n\t\treturn 0;\n\t}\n\tret = cfg_partition_make_list(fd);\n\n\tclose(fd);\n\treturn ret;\n}\n\nint\ncfg_partition_make_list(int fd)\n{\n\tDriver_desc *driver_desc_ptr;\n\tPart_map *part_map_ptr;\n\tword32\t*blk_bufptr;\n\tdword64\tdimage_start, dimage_size, dsize;\n\tword32\tstart, len, data_off, data_len, sig, map_blk_cnt, cur_blk;\n\tword32\tmap_blks, block_size;\n\tint\tis_dir, ret;\n\n\tblock_size = 512;\n\tg_cfg_partition_is_zip = 0;\n\n\tcfg_free_alldirents(&g_cfg_partitionlist);\n\n\tblk_bufptr = (word32 *)malloc(MAX_PARTITION_BLK_SIZE);\n\n\tcfg_partition_read_block(fd, blk_bufptr, 0, block_size);\n\n\tdriver_desc_ptr = (Driver_desc *)blk_bufptr;\n\tsig = cfg_get_be_word16(&(driver_desc_ptr->sig));\n\tblock_size = cfg_get_be_word16(&(driver_desc_ptr->blk_size));\n\tif(block_size == 0) {\n\t\tblock_size = 512;\n\t}\n\tif((sig != 0x4552) || (block_size < 0x200) ||\n\t\t\t\t(block_size > MAX_PARTITION_BLK_SIZE)) {\n\t\tprintf(\"Partition error: No driver descriptor map found\\n\");\n\t\tfree(blk_bufptr);\n\t\tret = undeflate_zipfile_make_list(fd);\n\t\tif(ret > 0) {\n\t\t\tg_cfg_partition_is_zip = 1;\n\t\t}\n\t\treturn ret;\n\t}\n\n\tmap_blks = 1;\n\tcur_blk = 0;\n\tdsize = cfg_get_fd_size(fd);\n\tcfg_file_add_dirent(&g_cfg_partitionlist, \"None - Whole image\",\n\t\t\tis_dir=0, dsize, 0, 0, -1);\n\n\twhile(cur_blk < map_blks) {\n\t\tcur_blk++;\n\t\tcfg_partition_read_block(fd, blk_bufptr, cur_blk, block_size);\n\t\tpart_map_ptr = (Part_map *)blk_bufptr;\n\t\tsig = cfg_get_be_word16(&(part_map_ptr->sig));\n\t\tmap_blk_cnt = cfg_get_be_word32(&(part_map_ptr->map_blk_cnt));\n\t\tif(cur_blk <= 1) {\n\t\t\tmap_blks = MY_MIN(20, map_blk_cnt);\n\t\t}\n\t\tif(sig != 0x504d) {\n\t\t\tprintf(\"Partition entry %d bad signature:%04x\\n\",\n\t\t\t\tcur_blk, sig);\n\t\t\tfree(blk_bufptr);\n\t\t\treturn g_cfg_partitionlist.last;\n\t\t}\n\n\t\t/* found it, check for consistency */\n\t\tstart = cfg_get_be_word32(&(part_map_ptr->phys_part_start));\n\t\tlen = cfg_get_be_word32(&(part_map_ptr->part_blk_cnt));\n\t\tdata_off = cfg_get_be_word32(&(part_map_ptr->data_start));\n\t\tdata_len = cfg_get_be_word32(&(part_map_ptr->data_cnt));\n\t\tif(data_off + data_len > len) {\n\t\t\tprintf(\"Poorly formed entry\\n\");\n\t\t\tcontinue;\n\t\t}\n\n\t\tif(data_len < 10 || start < 1) {\n\t\t\tprintf(\"Poorly formed entry %d, datalen:%d, \"\n\t\t\t\t\"start:%08x\\n\", cur_blk, data_len, start);\n\t\t\tcontinue;\n\t\t}\n\n\t\tdimage_size = (dword64)data_len * block_size;\n\t\tdimage_start = ((dword64)start + data_off) * block_size;\n\t\tis_dir = 2*(dimage_size < 800*1024);\n#if 0\n\t\tprintf(\" partition add entry %d = %s %d %08llx %08llx\\n\",\n\t\t\tcur_blk, part_map_ptr->part_name, is_dir,\n\t\t\tdimage_size, dimage_start);\n#endif\n\n\t\tcfg_file_add_dirent(&g_cfg_partitionlist,\n\t\t\tpart_map_ptr->part_name, is_dir, dimage_size,\n\t\t\tdimage_start, 0, cur_blk);\n\t}\n\n\tfree(blk_bufptr);\n\treturn g_cfg_partitionlist.last;\n}\n\nint\ncfg_maybe_insert_disk(int slot, int drive, const char *namestr)\n{\n\tint\tnum_parts;\n\n\tg_cfg_part_path[0] = 0;\n\tnum_parts = cfg_partition_make_list_from_name(namestr);\n\n\tif(num_parts > 0) {\n\t\tprintf(\"Choose a partition\\n\");\n\t\tg_cfg_select_partition = 1;\n\t\tg_cfg_file_pathfield = 0;\n\t} else {\n\t\tinsert_disk(slot, drive, namestr, 0, 0, -1, 0);\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nvoid\ncfg_insert_disk_dynapro(int slot, int drive, const char *name)\n{\n\tint\tdynapro_blocks;\n\n\tdynapro_blocks = 280;\n\tif(slot == 5) {\n\t\tdynapro_blocks = 1600;\n\t} else if(slot == 7) {\n\t\tdynapro_blocks = 65535;\n\t}\n\tif(g_cfg_newdisk_select && (g_cfg_newdisk_type == 3) &&\n\t\t\t\t\t\t\tg_cfg_newdisk_blocks) {\n\t\tdynapro_blocks = g_cfg_newdisk_blocks;\n\t}\n\tinsert_disk(slot, drive, name, 0, 0, -1, dynapro_blocks);\n}\n\nint\ncfg_stat(char *path, struct stat *sb, int do_lstat)\n{\n\tint\tret, len, removed_slash;\n\n\tremoved_slash = 0;\n\tlen = 0;\n\n\t/* Windows doesn't like to stat paths ending in a /, so remove it */\n\tlen = (int)strlen(path);\n#ifdef _WIN32\n\tif((len > 1) && (path[len - 1] == '/') ) {\n\t\tpath[len - 1] = 0;\t/* remove the slash */\n\t\tremoved_slash = 1;\n\t}\n#endif\n\n\tif(do_lstat) {\n\t\tret = lstat(path, sb);\n\t} else {\n\t\tret = stat(path, sb);\n\t}\n\n\t/* put the slash back */\n\tif(removed_slash) {\n\t\tpath[len - 1] = '/';\n\t}\n\n\treturn ret;\n}\n\nword32\ncfg_get_le16(byte *bptr)\n{\n\treturn bptr[0] | (bptr[1] << 8);\n}\n\nword32\ncfg_get_le32(byte *bptr)\n{\n\treturn bptr[0] | (bptr[1] << 8) | (bptr[2] << 16) | (bptr[3] << 24);\n}\n\ndword64\ncfg_get_le64(byte *bptr)\n{\n\tdword64\tdval;\n\tint\ti;\n\n\tdval = 0;\n\tfor(i = 7; i >= 0; i--) {\n\t\tdval = (dval << 8) | bptr[i];\n\t}\n\treturn dval;\n}\n\nword32\ncfg_get_be_word16(word16 *ptr)\n{\n\tbyte\t*bptr;\n\n\tbptr = (byte *)ptr;\n\treturn (bptr[0] << 8) | bptr[1];\n}\n\nword32\ncfg_get_be_word32(word32 *ptr)\n{\n\tbyte\t*bptr;\n\n\tbptr = (byte *)ptr;\n\treturn (bptr[0] << 24) | (bptr[1] << 16) | (bptr[2] << 8) | bptr[3];\n}\n\nvoid\ncfg_set_le32(byte *bptr, word32 val)\n{\n\t*bptr++ = val;\n\t*bptr++ = val >> 8;\n\t*bptr++ = val >> 16;\n\t*bptr++ = val >> 24;\n}\n\nvoid\nconfig_file_to_pipe(Disk *dsk, const char *cmd_ptr, const char *name_ptr)\n{\n#ifdef _WIN32\n\tprintf(\"Cannot do pipe from cmd %s to %s\\n\", cmd_ptr, name_ptr);\n\treturn;\n#else\n\tint\toutput_pipe[2];\n\tbyte\t*bptr2;\n\tchar\t*bufptr;\n\tpid_t\tpid;\n\tint\tstat_loc, ret, bufsize, pos, fd;\n\tint\ti;\n\n\t// Create a pipe to cmd_ptr, and send the contents of name_ptr to it\n\t//  Collect the output in a 32MB buffer.  Once complete, allocate\n\t//  a buffer of the correct size, copy to it, and free the giant buffer\n\t// Sample usage: \"gunzip\", \"{filename}.gz\" will run gunzip and collect\n\t//  uncompressed data\n\tret = pipe(&output_pipe[0]);\n\tif(ret < 0) {\n\t\treturn;\n\t}\n\tprintf(\"output_pipe[0]=%d, [1]=%d\\n\", output_pipe[0], output_pipe[1]);\n\tbufsize = 32*1024*1024;\n\tbufptr = malloc(bufsize);\n\tif(bufptr == 0) {\n\t\treturn;\n\t}\n\tpos = 0;\n\tpid = fork();\n\tif(pid == 0) {\n\t\tclose(output_pipe[0]);\n\t\tret = dup2(output_pipe[1], 1);\n\t\tif(ret < 0) {\n\t\t\texit(1);\n\t\t}\n\t\t// The child.  Open 0 as the file, and then do system\n\t\tclose(0);\n\t\tfd = open(name_ptr, O_RDONLY | O_BINARY, 0x1b6);\n\t\tif(fd != 0) {\n\t\t\texit(1);\n\t\t}\n\t\t// Now just run the command.  Input is from name_ptr, output is\n\t\t//  to a pipe\n\t\t(void)!system(cmd_ptr);\n\t\texit(0);\n\t} else if(pid > 0) {\n\t\t// Parent.  Collect output from output_pipe[0], and write it\n\t\t//  to bufptr.\n\t\tclose(output_pipe[1]);\n\t\twhile(1) {\n\t\t\tif(pos >= bufsize) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tret = read(output_pipe[0], bufptr + pos, bufsize - pos);\n\t\t\tif(ret <= 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tpos += ret;\n\t\t}\n\t\tclose(output_pipe[0]);\n\t\twaitpid(pid, &stat_loc, 0);\n\t} else {\n\t\t// Error case\n\t\tclose(output_pipe[1]);\n\t\tclose(output_pipe[0]);\n\t}\n\n\t// See what we got\n\tbptr2 = 0;\n\tprintf(\"Read %d bytes from %s\\n\", pos, name_ptr);\n\tif(pos >= 140*1024) {\n\t\t// Looks like it could be an image\n\t\tbptr2 = malloc(pos);\n\t\tfor(i = 0; i < pos; i++) {\n\t\t\tbptr2[i] = bufptr[i];\n\t\t}\n\t\tdsk->raw_data = bptr2;\n\t\tdsk->fd = 0;\t\t\t// Indicates raw_data is valid\n\t\tdsk->raw_dsize = pos;\n\t}\n\tfree(bufptr);\n#endif\n}\n\nvoid\ncfg_htab_vtab(int x, int y)\n{\n\tif(x > 79) {\n\t\tx = 0;\n\t}\n\tif(y > 23) {\n\t\ty = 0;\n\t}\n\tg_cfg_curs_x = x;\n\tg_cfg_curs_y = y;\n\tg_cfg_curs_inv = 0;\n\tg_cfg_curs_mousetext = 0;\n}\n\nvoid\ncfg_home()\n{\n\tint\ti;\n\n\tcfg_htab_vtab(0, 0);\n\tfor(i = 0; i < 24; i++) {\n\t\tcfg_cleol();\n\t}\n}\n\nvoid\ncfg_cleol()\n{\n\tg_cfg_curs_inv = 0;\n\tg_cfg_curs_mousetext = 0;\n\tcfg_putchar(' ');\n\twhile(g_cfg_curs_x != 0) {\n\t\tcfg_putchar(' ');\n\t}\n}\n\nvoid\ncfg_putchar(int c)\n{\n\tint\tx, y;\n\n\tif(c == '\\n') {\n\t\tcfg_cleol();\n\t\treturn;\n\t}\n\tif(c == '\\b') {\n\t\tg_cfg_curs_inv = !g_cfg_curs_inv;\n\t\treturn;\n\t}\n\tif(c == '\\t') {\n\t\tg_cfg_curs_mousetext = !g_cfg_curs_mousetext;\n\t\treturn;\n\t}\n\ty = g_cfg_curs_y;\n\tx = g_cfg_curs_x;\n\n\t// Normal: 0xa0-0xff for space through lowercase\n\t// Inverse: 0x00-0x3f for upper case and numbers, 0x60-0x7f for lcase\n\t// Mousetext: 0x40-0x5f\n\tif(g_cfg_curs_inv) {\n\t\tif(c >= 0x40 && c < 0x60) {\n\t\t\tc = c & 0x1f;\n\t\t}\n\t} else {\n\t\tc = c | 0x80;\n\t}\n\tif(g_cfg_curs_mousetext) {\n\t\tc = (c & 0x1f) | 0x40;\n\t}\n\tg_cfg_screen[y][x] = c;\n\tx++;\n\tif(x >= 80) {\n\t\tx = 0;\n\t\ty++;\n\t\tif(y >= 24) {\n\t\t\ty = 0;\n\t\t}\n\t}\n\tg_cfg_curs_y = y;\n\tg_cfg_curs_x = x;\n\tg_cfg_screen_changed = 1;\n}\n\nvoid\ncfg_printf(const char *fmt, ...)\n{\n\tva_list ap;\n\tint\tc;\n\tint\ti;\n\n\tva_start(ap, fmt);\n\t(void)vsnprintf(g_cfg_printf_buf, CFG_PRINTF_BUFSIZE, fmt, ap);\n\tva_end(ap);\n\n\tfor(i = 0; i < CFG_PRINTF_BUFSIZE; i++) {\n\t\tc = g_cfg_printf_buf[i];\n\t\tif(c == 0) {\n\t\t\treturn;\n\t\t}\n\t\tcfg_putchar(c);\n\t}\n}\n\nvoid\ncfg_print_dnum(dword64 dnum, int max_len)\n{\n\tchar\tbuf[64];\n\tchar\tbuf2[64];\n\tint\tlen, cnt, c;\n\tint\ti, j;\n\n\t/* Prints right-adjusted \"num\" in field \"max_len\" wide */\n\tsnprintf(&buf[0], 64, \"%lld\", dnum);\n\tlen = (int)strlen(buf);\n\tfor(i = 0; i < 64; i++) {\n\t\tbuf2[i] = ' ';\n\t}\n\tj = max_len + 1;\n\tbuf2[j] = 0;\n\tj--;\n\tcnt = 0;\n\tfor(i = len - 1; (i >= 0) && (j >= 1); i--) {\n\t\tc = buf[i];\n\t\tif(c >= '0' && c <= '9') {\n\t\t\tif(cnt >= 3) {\n\t\t\t\tbuf2[j--] = ',';\n\t\t\t\tcnt = 0;\n\t\t\t}\n\t\t\tcnt++;\n\t\t}\n\t\tbuf2[j--] = c;\n\t}\n\tcfg_printf(&buf2[1]);\n}\n\nint\ncfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras)\n{\n\tDisk\t*dsk;\n\tint\tslot, drive;\n\n\tslot = type_ext >> 8;\n\tdrive = type_ext & 0xff;\n\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\n\toutstr[0] = 0;\n\tif(dsk->name_ptr == 0) {\n\t\treturn 0;\n\t}\n\n\tconfig_generate_config_kegs_name(outstr, maxlen, dsk, with_extras);\n\treturn dsk->dynapro_blocks;\n}\n\nint\ncfg_get_disk_locked(int type_ext)\n{\n\tDisk\t*dsk;\n\tint\tslot, drive;\n\n\tslot = type_ext >> 8;\n\tdrive = type_ext & 0xff;\n\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\tif(dsk->fd < 0) {\n\t\treturn 0;\n\t}\n\n\tif(dsk->write_prot) {\n\t\treturn 1;\n\t} else if(!dsk->write_through_to_unix) {\n\t\treturn 2;\n\t}\n\n\treturn 0;\n}\n\nvoid\ncfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change)\n{\n\tchar\tvalbuf[CFG_OPT_MAXSTR];\n\tchar\t*(*fn_ptr)(int);\n\tint\t*iptr;\n\tchar\t**str_ptr;\n\tconst char *menustr;\n\tchar\t*curstr, *defstr, *str, *outstr;\n\tvoid\t*edit_ptr;\n\tint\tval, num_opts, opt_num, bufpos, outpos, curval, defval, type;\n\tint\ttype_ext, opt_get_str, separator, len, c, locked;\n\tint\ti;\n\n\t// For this menu_pos line, create output in g_cfg_opt_buf[] string\n\t//  Highlight it if menu_pos==highlight_pos\n\t// Allow arrows to modify the currently selected item of a list using\n\t//  change: -1 moves to a previous item, +1 moves to the next\n\n\tg_cfg_opt_buf[0] = 0;\n\n\tnum_opts = 0;\n\topt_get_str = 0;\n\tseparator = ',';\n\n\tmenuptr += menu_pos;\t\t/* move forward to entry menu_pos */\n\n\tmenustr = menuptr->str;\n\ttype = menuptr->cfgtype;\n\ttype_ext = (type >> 4);\n\ttype = type & 0xf;\n\tlen = (int)strlen(menustr) + 1;\n\n\tbufpos = 0;\n\toutstr = &(g_cfg_opt_buf[0]);\n\n\toutstr[bufpos++] = ' ';\t\t// 0\n\toutstr[bufpos++] = ' ';\t\t// 1\n\toutstr[bufpos++] = '\\t';\t// 2\n\toutstr[bufpos++] = '\\t';\t// 3\n\toutstr[bufpos++] = ' ';\t\t// 4\n\toutstr[bufpos++] = ' ';\t\t// 5\n\n\t// Figure out if we should get a checkmark\n\tcurval = -1;\n\tdefval = -1;\n\tcurstr = 0;\n\tif(type == CFGTYPE_INT) {\n\t\tiptr = menuptr->ptr;\n\t\tcurval = *iptr;\n\t\tiptr = menuptr->defptr;\n\t\tif(!iptr) {\n\t\t\tprintf(\"BAD MENU, defptr is 0!\\n\");\n\t\t} else {\n\t\t\tdefval = *iptr;\n\t\t}\n\t\tif(curval == defval) {\n\t\t\tg_cfg_opt_buf[3] = 'D';\t/* checkmark */\n\t\t\tg_cfg_opt_buf[4] = '\\t';\n\t\t}\n\t}\n\n\tif((type == CFGTYPE_FILE) || (type == CFGTYPE_STR)) {\n\t\tstr_ptr = (char **)menuptr->ptr;\n\t\tcurstr = *str_ptr;\n\t\tstr_ptr = (char **)menuptr->defptr;\n\t\tif(!str_ptr) {\n\t\t\tprintf(\"BAD MENU, defptr str is 0!\\n\");\n\t\t\tdefstr = \"\";\n\t\t} else {\n\t\t\tdefstr = *str_ptr;\n\t\t}\n\t\tif(strcmp(curstr, defstr) == 0) {\n\t\t\tg_cfg_opt_buf[3] = 'D';\t/* checkmark */\n\t\t\tg_cfg_opt_buf[4] = '\\t';\n\t\t}\n\t}\n\n\t// If it's a menu, give it a special menu indicator\n\tif(type == CFGTYPE_MENU) {\n\t\tg_cfg_opt_buf[1] = '\\t';\n\t\tg_cfg_opt_buf[2] = 'M';\t\t/* return-like symbol */\n\t\tg_cfg_opt_buf[3] = '\\t';\n\t\tg_cfg_opt_buf[4] = ' ';\n\t}\n\n\tif(type == CFGTYPE_DISK) {\n\t\tlocked = cfg_get_disk_locked(type_ext);\n\t\tif(locked) {\n\t\t\tg_cfg_opt_buf[4] = '*';\n\t\t\tif(locked == 2) {\t\t// inverse\n\t\t\t\tg_cfg_opt_buf[2] = '\\b';\n\t\t\t\tg_cfg_opt_buf[3] = '*';\n\t\t\t\tg_cfg_opt_buf[4] = '\\b';\n\t\t\t}\n\t\t}\n\t}\n\n\tif(menu_pos == highlight_pos) {\n\t\toutstr[bufpos++] = '\\b';\n\t}\n\n\topt_get_str = 2;\n\ti = -1;\n\toutpos = bufpos;\n#if 0\n\tprintf(\"cfg menu_pos: %d str len: %d\\n\", menu_pos, len);\n#endif\n\twhile(++i < len) {\n\t\tc = menustr[i];\n\t\tif(c == separator) {\t\t// ','?\n\t\t\tif(i == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tc = 0;\n\t\t}\n\t\toutstr[outpos++] = c;\n\t\toutstr[outpos] = 0;\n\t\tif(outpos >= CFG_OPT_MAXSTR) {\n\t\t\tfprintf(stderr, \"CFG_OPT_MAXSTR exceeded\\n\");\n\t\t\tmy_exit(1);\n\t\t\treturn;\n\t\t}\n\t\tif(c == 0) {\n\t\t\tif(opt_get_str == 2) {\n\t\t\t\toutstr = &(valbuf[0]);\n\t\t\t\tbufpos = outpos - 1;\n\t\t\t\topt_get_str = 0;\n\t\t\t} else if(opt_get_str) {\n#if 0\n\t\t\t\tif(menu_pos == highlight_pos) {\n\t\t\t\t\tprintf(\"menu_pos %d opt %d = %s=%d\\n\",\n\t\t\t\t\t\tmenu_pos, num_opts,\n\t\t\t\t\t\tg_cfg_opts_str[0],\n\t\t\t\t\t\tg_cfg_opts_vals[num_opts]);\n\t\t\t\t}\n#endif\n\t\t\t\tnum_opts++;\n\t\t\t\toutstr = &(valbuf[0]);\n\t\t\t\topt_get_str = 0;\n\t\t\t\tif(num_opts >= CFG_MAX_OPTS) {\n\t\t\t\t\tfprintf(stderr, \"CFG_MAX_OPTS oflow\\n\");\n\t\t\t\t\tmy_exit(1);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tval = (word32)strtoul(valbuf, 0, 0);\n\t\t\t\tg_cfg_opts_vals[num_opts] = val;\n\t\t\t\toutstr = &(g_cfg_opts_str[0]);\n\t\t\t\tif(val != curval) {\n\t\t\t\t\toutstr = valbuf;\n\t\t\t\t}\n\t\t\t\topt_get_str = 1;\n\t\t\t}\n\t\t\toutpos = 0;\n\t\t\toutstr[0] = 0;\n\t\t}\n\t}\n\n\tif(menu_pos == highlight_pos) {\n\t\tg_cfg_opt_buf[bufpos++] = '\\b';\n\t}\n\n\tg_cfg_opt_buf[bufpos] = 0;\n\n\t// Decide what to display on the \"right\" side\n\tstr = 0;\n\topt_num = -1;\n\tif((type == CFGTYPE_INT) || (type == CFGTYPE_FILE) ||\n\t\t\t\t\t\t(type == CFGTYPE_STR)) {\n\t\tg_cfg_opt_buf[bufpos++] = ' ';\n\t\tg_cfg_opt_buf[bufpos++] = '=';\n\t\tg_cfg_opt_buf[bufpos++] = ' ';\n\t\tg_cfg_opt_buf[bufpos] = 0;\n\t\tfor(i = 0; i < num_opts; i++) {\n\t\t\tif(curval == g_cfg_opts_vals[i]) {\n\t\t\t\topt_num = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(change != 0) {\n\t\tif(type == CFGTYPE_INT) {\n\t\t\tif(num_opts > 0) {\n\t\t\t\topt_num += change;\n\t\t\t\tif(opt_num >= num_opts) {\n\t\t\t\t\topt_num = 0;\n\t\t\t\t}\n\t\t\t\tif(opt_num < 0) {\n\t\t\t\t\topt_num = num_opts - 1;\n\t\t\t\t}\n\t\t\t\tcurval = g_cfg_opts_vals[opt_num];\n\t\t\t} else {\n\t\t\t\tcurval += change;\n\t\t\t\t/* HACK: min_val, max_val testing here */\n\t\t\t}\n\t\t\tiptr = (int *)menuptr->ptr;\n\t\t\tcfg_int_update(iptr, curval);\n\t\t}\n\t\tg_config_kegs_update_needed = 1;\n\t}\n\n#if 0\n\tif(menu_pos == highlight_pos) {\n\t\tprintf(\"menu_pos %d opt_num %d\\n\", menu_pos, opt_num);\n\t}\n#endif\n\n\tedit_ptr = g_cfg_edit_ptr;\n\tif((edit_ptr == menuptr->ptr) && edit_ptr) {\n\t\t// Just show the current edit string\n\t\tstr = cfg_shorten_filename(&(g_cfg_edit_buf[0]), 68 - 1);\n\t\tcfg_strlcat(str, \"\\b \\b\", CFG_PATH_MAX);\n\t} else if(opt_num >= 0) {\n\t\tstr = &(g_cfg_opts_str[0]);\n\t} else {\n\t\tif(type == CFGTYPE_INT) {\n\t\t\tstr = &(g_cfg_opts_str[0]);\n\t\t\tsnprintf(str, CFG_OPT_MAXSTR, \"%d\", curval);\n\t\t} else if (type == CFGTYPE_DISK) {\n\t\t\tstr = &(g_cfg_opts_str[0]);\n\t\t\t(void)cfg_get_disk_name(str, CFG_PATH_MAX, type_ext, 1);\n\t\t\tstr = cfg_shorten_filename(str, 68);\n\t\t} else if ((type == CFGTYPE_FILE) || (type == CFGTYPE_STR)) {\n\t\t\tstr = cfg_shorten_filename(curstr, 68);\n\t\t} else if(type == CFGTYPE_FUNC) {\n\t\t\tfn_ptr = (char *(*)(int))menuptr->ptr;\n\t\t\tstr = \"\";\n\t\t\tcurstr = (*fn_ptr)(1);\n\t\t\tif(curstr) {\n\t\t\t\tg_cfg_opt_buf[bufpos++] = ' ';\n\t\t\t\tg_cfg_opt_buf[bufpos++] = ':';\n\t\t\t\tg_cfg_opt_buf[bufpos++] = ' ';\n\t\t\t\tg_cfg_opt_buf[bufpos] = 0;\n\t\t\t\tstr = cfg_shorten_filename(curstr, 68);\n\t\t\t\tfree(curstr);\n\t\t\t}\n\t\t} else {\n\t\t\tstr = \"\";\n\t\t}\n\t}\n\n#if 0\n\tif(menu_pos == highlight_pos) {\n\t\tprintf(\"menu_pos %d buf_pos %d, str is %s, %02x, %02x, \"\n\t\t\t\"%02x %02x\\n\",\n\t\t\tmenu_pos, bufpos, str, g_cfg_opt_buf[bufpos-1],\n\t\t\tg_cfg_opt_buf[bufpos-2],\n\t\t\tg_cfg_opt_buf[bufpos-3],\n\t\t\tg_cfg_opt_buf[bufpos-4]);\n\t}\n#endif\n\n\tg_cfg_opt_buf[bufpos] = 0;\n\tcfg_strncpy(&(g_cfg_opt_buf[bufpos]), str, CFG_OPT_MAXSTR - bufpos - 1);\n\tg_cfg_opt_buf[CFG_OPT_MAXSTR-1] = 0;\n}\n\nvoid\ncfg_get_base_path(char *pathptr, const char *inptr, int go_up)\n{\n\tconst char *tmpptr;\n\tchar\t*slashptr;\n\tchar\t*outptr;\n\tint\tadd_dotdot, is_dotdot;\n\tint\tlen;\n\tint\tc;\n\n\t// Take full filename, copy it to pathptr, and truncate at last slash\n\t//  inptr and pathptr can be the same.\n\t// If go_up is set, then replace a blank dir with \"..\"\n\t//  but first, see if path is currently just ../ over and over\n\t// if so, just tack .. onto the end and return\n\n\t//printf(\"cfg_get_base start with %s\\n\", inptr);\n\n\tg_cfg_file_match[0] = 0;\n\ttmpptr = inptr;\n\tis_dotdot = 1;\n\twhile(1) {\n\t\tif(tmpptr[0] == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tif((tmpptr[0] == '.') && (tmpptr[1] == '.') &&\n\t\t\t\t\t\t\t(tmpptr[2] == '/')) {\n\t\t\ttmpptr += 3;\n\t\t} else {\n\t\t\tis_dotdot = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\tslashptr = 0;\n\toutptr = pathptr;\n\tc = -1;\n\twhile(c != 0) {\n\t\tc = *inptr++;\n\t\tif(c == '/') {\n\t\t\tif(*inptr != 0) {\t/* if not a trailing slash... */\n\t\t\t\tslashptr = outptr;\n\t\t\t}\n\t\t}\n\t\t*outptr++ = c;\n\t}\n\tif(!go_up) {\n\t\t/* if not go_up, copy chopped part to g_cfg_file_match*/\n\t\t/* copy from slashptr+1 to end */\n\t\ttmpptr = slashptr+1;\n\t\tif(slashptr == 0) {\n\t\t\ttmpptr = pathptr;\n\t\t}\n\t\tcfg_strncpy(&g_cfg_file_match[0], tmpptr, CFG_PATH_MAX);\n\t\t/* remove trailing / from g_cfg_file_match */\n\t\tlen = (int)strlen(&g_cfg_file_match[0]);\n\t\tif((len > 1) && (len < (CFG_PATH_MAX - 1)) &&\n\t\t\t\t\tg_cfg_file_match[len - 1] == '/') {\n\t\t\tg_cfg_file_match[len - 1] = 0;\n\t\t}\n\t\t//printf(\"set g_cfg_file_match to %s\\n\", &g_cfg_file_match[0]);\n\t}\n\tif(!is_dotdot && (slashptr != 0)) {\n\t\tslashptr[0] = '/';\n\t\tslashptr[1] = 0;\n\t\toutptr = slashptr + 2;\n\t}\n\tadd_dotdot = 0;\n\tif(pathptr[0] == 0 || is_dotdot) {\n\t\t/* path was blank, or consisted of just ../ pattern */\n\t\tif(go_up) {\n\t\t\tadd_dotdot = 1;\n\t\t}\n\t} else if(slashptr == 0) {\n\t\t/* no slashes found, but path was not blank--make it blank */\n\t\tif(pathptr[0] == '/') {\n\t\t\tpathptr[1] = 0;\n\t\t} else {\n\t\t\tpathptr[0] = 0;\n\t\t}\n\t}\n\n\tif(add_dotdot) {\n\t\t--outptr;\n\t\toutptr[0] = '.';\n\t\toutptr[1] = '.';\n\t\toutptr[2] = '/';\n\t\toutptr[3] = 0;\n\t}\n\n\t//printf(\"cfg_get_base end with %s, is_dotdot:%d, add_dotdot:%d\\n\",\n\t//\t\tpathptr, is_dotdot, add_dotdot);\n}\n\nchar *\ncfg_name_new_image(int get_status)\n{\n\t// Called from menu to create a new disk image, this will pop up the\n\t//  file selection dialog.  Once name is selected,\n\t//  cfg_create_new_image() is called.\n\tif(get_status) {\n\t\treturn 0;\n\t}\n\tprintf(\"cfg_name_new_image called!\\n\");\n\tg_cfg_slotdrive = g_cfg_newdisk_slotdrive;\n\tg_cfg_newdisk_select = 1;\n\tcfg_file_init();\n\treturn 0;\n}\n\nvoid\ncfg_dup_existing_image(word32 slotdrive)\n{\n\t// Set g_cfg_newdisk_* to copy the slotdrive image\n\tg_cfg_slotdrive = slotdrive;\n\tg_cfg_newdisk_select = 2;\t\t\t// Do DUP\n\tcfg_file_init();\n}\n\nvoid\ncfg_dup_image_selected()\n{\n\tDisk\t*dsk;\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*bufptr;\n\tchar\t*str;\n\tdword64\tdsize, dret;\n\tword32\tslotdrive;\n\tint\tfd;\n\n\t// printf(\"cfg_dup_image_selected\\n\");\n\n\tslotdrive = g_cfg_slotdrive;\n\tg_cfg_slotdrive = 0;\n\tg_menuptr = &g_cfg_disk_menu[0];\n\tg_menu_redraw_needed = 1;\n\tg_cfg_newdisk_select = 0;\n\tg_cfg_newdisk_slotdrive = 0;\n\tprintf(\"slotdrive:%04x\\n\", slotdrive);\n\tif(slotdrive < 0x500) {\n\t\treturn;\t\t// Invalid slot,drive: Do nothing\n\t}\n\tdsk = iwm_get_dsk_from_slot_drive((slotdrive >> 8) & 7,\n\t\t\t\t\t\t\tslotdrive & 0xff);\n\tif(dsk->fd < 0) {\n\t\treturn;\t\t// No disk\n\t}\n\tdsize = dsk->dimage_size;\n\n\tstr = &g_cfg_file_path[0],\n\tprintf(\"Create dup image %s, dsize:%lld\\n\", str, dsize);\n\tif((word32)dsize != dsize) {\n\t\treturn;\n\t}\n\tfd = open(str, O_RDWR | O_CREAT | O_TRUNC, 0x1b6);\n\tif(fd < 0) {\n\t\tprintf(\"Open %s failed, errno:%d\\n\", str, errno);\n\t\treturn;\n\t}\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(wozinfo_ptr && !dsk->write_through_to_unix) {\n\t\t// Just write out the WOZ image, and then fully enable this\n\t\t//  image\n\t\tprintf(\"Writing out .WOZ image to %s, size:%d\\n\", str,\n\t\t\t\t\t\t\twozinfo_ptr->woz_size);\n\t\tif((dsk->raw_data == 0) && (dsk->fd >= 0)) {\n\t\t\tclose(dsk->fd);\n\t\t}\n\t\tdsk->fd = fd;\n\t\twoz_rewrite_crc(dsk, wozinfo_ptr->woz_size);\n\t\t\t// Above will recalc CRC and write out woz_size bytes\n\t\tdsk->raw_data = 0;\n\t\tdsk->write_through_to_unix = 1;\n\t\tfree(dsk->name_ptr);\n\t\tdsk->name_ptr = kegs_malloc_str(str);\n\t\tfree(dsk->partition_name);\n\t\tdsk->partition_name = 0;\n\t\tdsk->image_type = DSK_TYPE_WOZ;\n\t\tdsk->dimage_size = wozinfo_ptr->woz_size;\n\t\tdsk->dimage_start = 0;\n\t\tg_config_kegs_update_needed = 1;\n\t\twoz_check_file(dsk);\n\t} else if(dsk->raw_data) {\n\t\tcfg_write_to_fd(fd, dsk->raw_data, 0, dsize);\n\t\tclose(fd);\n\t} else {\n\t\tbufptr = malloc((size_t)dsize);\n\t\tif((bufptr != 0) && ((size_t)dsize == dsize)) {\n\t\t\tdret = cfg_read_from_fd(dsk->fd, bufptr, 0, dsize);\n\t\t\tif(dret == dsize) {\n\t\t\t\tcfg_write_to_fd(fd, bufptr, 0, dsize);\n\t\t\t}\n\t\t}\n\t\tfree(bufptr);\n\t\tclose(fd);\n\t}\n}\n\nvoid\ncfg_validate_image(word32 slotdrive)\n{\n\tDisk\t*dsk;\n\n\tdsk = iwm_get_dsk_from_slot_drive((slotdrive >> 8) & 7,\n\t\t\t\t\t\t\tslotdrive & 0xff);\n\tdynapro_validate_any_image(dsk);\n}\n\nvoid\ncfg_toggle_lock_disk(word32 slotdrive)\n{\n\tDisk\t*dsk;\n\n\tdsk = iwm_get_dsk_from_slot_drive((slotdrive >> 8) & 7,\n\t\t\t\t\t\t\tslotdrive & 0xff);\n\tiwm_toggle_lock(dsk);\n}\n\nint\ncfg_create_new_image_act(const char *str, int type, int size_blocks)\n{\n\tbyte\tbuf[512];\n\tdword64\tdret;\n\tint\tfd, ret;\n\tint\ti;\n\n\t// Called after file dialog selects a new image name, this creates it\n\n\tif(size_blocks == 65536) {\n\t\tsize_blocks--;\t\t// Shrink to 65535 total blocks\n\t}\n\tprintf(\"Create new image type:%d, size:%dKB\\n\", type, size_blocks/2);\n\tfd = open(str, O_RDWR | O_CREAT | O_TRUNC, 0x1b6);\n\tif(fd < 0) {\n\t\tprintf(\"Open %s failed, errno:%d\\n\", str, errno);\n\t\treturn fd;\n\t}\n\tfor(i = 0; i < 512; i++) {\n\t\tbuf[i] = 0;\n\t}\n\n\tret = 0;\n\tif(type == 2) {\t\t// WOZ\n\t\t(void)woz_new(fd, str, size_blocks/2);\n\t} else {\n\t\tfor(i = 0; i < size_blocks; i++) {\n\t\t\tdret = cfg_write_to_fd(fd, &(buf[0]), i * 512U, 512);\n\t\t\tif(dret != 512) {\n\t\t\t\tret = -1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tclose(fd);\n\n\treturn ret;\t\t// 0=success, -1 is a failure\n}\n\nvoid\ncfg_create_new_image()\n{\n\tword32\tdynamic_blocks;\n\tint\tret;\n\n\t// Type is in g_cfg_file_path.  Create this file and prepare it\n\tprintf(\"Creating new image: %s\\n\", &g_cfg_file_path[0]);\n\n\tif(g_cfg_newdisk_select == 2) {\n\t\tcfg_dup_image_selected();\n\t\treturn;\n\t}\n\tret = 0;\n\tdynamic_blocks = 0;\n\tif(g_cfg_newdisk_type == 3) {\n\t\tdynamic_blocks = g_cfg_newdisk_blocks;\n\t} else {\n\t\tret = cfg_create_new_image_act(&g_cfg_file_path[0],\n\t\t\t\tg_cfg_newdisk_type, g_cfg_newdisk_blocks);\n\t}\n\tif(ret < 0) {\n\t\t// Maybe open a dialog?  Oh well...do nothing\n\t} else {\n\t\tinsert_disk((g_cfg_slotdrive >> 8) & 0xf,\n\t\t\tg_cfg_slotdrive & 0xff, &(g_cfg_file_path[0]),\n\t\t\t0, 0, -2, dynamic_blocks);\n\t}\n\tg_cfg_slotdrive = 0;\n\tg_menuptr = &g_cfg_disk_menu[0];\n\tg_menu_redraw_needed = 1;\n\tg_cfg_newdisk_select = 0;\n\tg_cfg_newdisk_slotdrive = 0;\n}\n\nvoid\ncfg_file_init()\n{\n\tint\tslot, drive, is_dynapro;\n\tint\ti;\n\n\tis_dynapro = 0;\n\tif((g_cfg_slotdrive & 0xfff) == 0xfff) {\t// File selection\n\t\t// Just use g_cfg_file_def_name\n\t\tcfg_strncpy(&g_cfg_tmp_path[0], g_cfg_file_def_name,\n\t\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t} else {\n\t\tis_dynapro = cfg_get_disk_name(&g_cfg_tmp_path[0], CFG_PATH_MAX,\n\t\t\t\t\t\t\tg_cfg_slotdrive, 0);\n\n\t\tslot = (g_cfg_slotdrive >> 8) & 7;\n\t\tdrive = g_cfg_slotdrive & 1;\n\t\tfor(i = 0; i < 6; i++) {\n\t\t\tif(g_cfg_tmp_path[0] != 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t/* try to get a starting path from some mounted drive */\n\t\t\tdrive = !drive;\n\t\t\tif(i & 1) {\n\t\t\t\tslot++;\n\t\t\t\tif(slot >= 8) {\n\t\t\t\t\tslot = 5;\n\t\t\t\t}\n\t\t\t}\n\t\t\tis_dynapro = cfg_get_disk_name(&g_cfg_tmp_path[0],\n\t\t\t\t\tCFG_PATH_MAX, (slot << 8) + drive, 0);\n\t\t}\n\t}\n\n\tcfg_get_base_path(&g_cfg_file_curpath[0], &g_cfg_tmp_path[0], 0);\n\tif(is_dynapro) {\n\t\t// Use the full path to the dir (don't strip off last part)\n\t\tcfg_strncpy(&g_cfg_file_curpath[0], &g_cfg_tmp_path[0],\n\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t}\n\tg_cfg_dirlist.invalid = 1;\n}\n\nvoid\ncfg_free_alldirents(Cfg_listhdr *listhdrptr)\n{\n\tint\ti;\n\n\tif(listhdrptr->max > 0) {\n\t\t// Free the old directory listing\n\t\tfor(i = 0; i < listhdrptr->last; i++) {\n\t\t\tfree(listhdrptr->direntptr[i].name);\n\t\t}\n\t\tfree(listhdrptr->direntptr);\n\t}\n\n\tlisthdrptr->direntptr = 0;\n\tlisthdrptr->last = 0;\n\tlisthdrptr->max = 0;\n\tlisthdrptr->invalid = 0;\n\n\tlisthdrptr->topent = 0;\n\tlisthdrptr->curent = 0;\n}\n\nvoid\ncfg_file_add_dirent_unique(Cfg_listhdr *listhdrptr, const char *nameptr,\n\tint is_dir, dword64 dsize, dword64 dimage_start, dword64 compr_dsize,\n\tint part_num)\n{\n\tCfg_dirent *direntptr;\n\tint\tnum, namelen, this_len;\n\tint\ti;\n\n\t// Loop through all entries, make sure name is unique\n\tnum = listhdrptr->last;\n\tnamelen = (int)strlen(nameptr);\n\tfor(i = 0; i < num; i++) {\n\t\tdirentptr = &(listhdrptr->direntptr[i]);\n\t\tthis_len = (int)strlen(direntptr->name);\n\t\tif(cfg_str_match(direntptr->name, nameptr, namelen) == 0) {\n\t\t\t// It's a match...check len\n\t\t\tif(namelen == this_len) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\tcfg_file_add_dirent(listhdrptr, nameptr, is_dir, dsize, dimage_start,\n\t\t\t\t\t\tcompr_dsize, part_num);\n}\n\nvoid\ncfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir,\n\tdword64 dsize, dword64 dimage_start, dword64 compr_dsize, int part_num)\n{\n\tCfg_dirent *direntptr;\n\tchar\t*ptr;\n\tint\tinc_amt, namelen;\n\n\tnamelen = (int)strlen(nameptr);\n\tif(listhdrptr->last >= listhdrptr->max) {\n\t\t// realloc\n\t\tinc_amt = MY_MAX(64, listhdrptr->max);\n\t\tinc_amt = MY_MIN(inc_amt, 1024);\n\t\tlisthdrptr->max += inc_amt;\n\t\tlisthdrptr->direntptr = realloc(listhdrptr->direntptr,\n\t\t\t\t\tlisthdrptr->max * sizeof(Cfg_dirent));\n\t}\n\tptr = malloc(namelen+1+is_dir);\n\tcfg_strncpy(ptr, nameptr, namelen+1);\n\tif(is_dir && (namelen >= 1) && (ptr[namelen - 1] != '/')) {\n\t\t// Add a trailing '/' to directories, unless already there\n\t\tcfg_strlcat(ptr, \"/\", namelen + 1 + is_dir);\n\t}\n#if 0\n\tprintf(\"...file entry %d is %s\\n\", listhdrptr->last, ptr);\n#endif\n\tdirentptr = &(listhdrptr->direntptr[listhdrptr->last]);\n\tdirentptr->name = ptr;\n\tdirentptr->is_dir = is_dir;\n\tdirentptr->dsize = dsize;\n\tdirentptr->dimage_start = dimage_start;\n\tdirentptr->compr_dsize = compr_dsize;\n\tdirentptr->part_num = part_num;\n\tlisthdrptr->last++;\n}\n\nint\ncfg_dirent_sortfn(const void *obj1, const void *obj2)\n{\n\tconst Cfg_dirent *direntptr1, *direntptr2;\n\tint\tret;\n\n\t/* Called by qsort to sort directory listings */\n\tdirentptr1 = (const Cfg_dirent *)obj1;\n\tdirentptr2 = (const Cfg_dirent *)obj2;\n\tret = cfg_str_match(direntptr1->name, direntptr2->name, CFG_PATH_MAX);\n\treturn ret;\n}\n\nint\ncfg_str_match(const char *str1, const char *str2, int len)\n{\n\treturn cfg_str_match_maybecase(str1, str2, len, g_cfg_ignorecase);\n}\n\nint\ncfg_str_match_maybecase(const char *str1, const char *str2, int len,\n\t\t\t\t\t\t\tint ignorecase)\n{\n\tconst byte *bptr1, *bptr2;\n\tint\tc, c2;\n\tint\ti;\n\n\t/* basically, work like strcmp or strcasecmp */\n\n\tbptr1 = (const byte *)str1;\n\tbptr2 = (const byte *)str2;\n\tfor(i = 0; i < len; i++) {\n\t\tc = *bptr1++;\n\t\tc2 = *bptr2++;\n\t\tif(ignorecase) {\n\t\t\tc = tolower(c);\n\t\t\tc2 = tolower(c2);\n\t\t}\n\t\tif((c == 0) || (c2 == 0) || (c != c2)) {\n\t\t\treturn c - c2;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nint\ncfgcasecmp(const char *str1, const char *str2)\n{\n\treturn cfg_str_match_maybecase(str1, str2, 32767, 1);\n}\n\nint\ncfg_strlcat(char *dstptr, const char *srcptr, int dstsize)\n{\n\tchar\t*ptr;\n\tint\tdestlen, srclen, ret, c;\n\n\t// Concat srcptr to the end of dstptr, ensuring a null within dstsize\n\t//  Return the total buffer size that would be needed, even if dstsize\n\t//  is too small.  Compat with strlcat()\n\tdestlen = (int)strlen(dstptr);\n\tsrclen = (int)strlen(srcptr);\n\tret = destlen + srclen;\n\tdstsize--;\n\tif(destlen >= dstsize) {\n\t\treturn ret;\t\t// Do nothing, buf too small\n\t}\n\tptr = dstptr + destlen;\n\twhile(destlen < dstsize) {\n\t\tc = *srcptr++;\n\t\t*ptr++ = c;\n\t\tif(c == 0) {\n\t\t\treturn ret;\n\t\t}\n\t\tdestlen++;\n\t}\n\tdstptr[dstsize] = 0;\n\treturn ret;\n}\n\nchar *\ncfg_strncpy(char *dstptr, const char *srcptr, int dstsize)\n{\n\tchar\t*ptr;\n\tint\tc;\n\n\t// Copy srcptr to dstptr, ensuring there is room for a null\n\t// Compatible with strncpy()--except dstptr is ALWAYS null terminated\n\tptr = dstptr;\n\twhile(--dstsize > 0) {\n\t\tc = *srcptr++;\n\t\t*ptr++ = c;\n\t\tif(c == 0) {\n\t\t\treturn dstptr;\n\t\t}\n\t}\n\t*ptr = 0;\n\treturn dstptr;\n}\n\nconst char *\ncfg_str_basename(const char *str)\n{\n\tint\tlen;\n\tint\ti;\n\n\t// If str is /aa/bb/cc, this routine returns cc\n\tlen = (int)strlen(str);\n\twhile(len && (str[len - 1] == '/')) {\n\t\tlen--;\t\t// Ignore trailing '/' if there are any\n\t}\n\tfor(i = len - 1; i > 0; i--) {\n\t\tif(str[i] == '/') {\n\t\t\treturn str + i + 1;\n\t\t}\n\t}\n\n\treturn str;\n}\n\nchar *\ncfg_strncpy_dirname(char *dstptr, const char *srcptr, int dstsize)\n{\n\tchar\t*ptr;\n\tint\tc;\n\n\t// If srcptr is /aa/bb/cc, this routine returns /aa/bb/\n\t// Copy srcptr to dstptr, ensuring there is room for a null\n\t// Compatible with strncpy()--except dstptr is ALWAYS null terminated\n\tptr = dstptr;\n\twhile(--dstsize > 0) {\n\t\tc = *srcptr++;\n\t\t*ptr++ = c;\n\t\tif(c == 0) {\n\t\t\t// Remove any trailing /'s\n\t\t\tptr--;\n\t\t\twhile((ptr > dstptr) && (ptr[0] == '/')) {\n\t\t\t\tptr[0] = 0;\n\t\t\t\tptr--;\n\t\t\t}\n\t\t\twhile(ptr > dstptr) {\n\t\t\t\tif(ptr[0] == '/') {\n\t\t\t\t\tptr[1] = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tptr--;\n\t\t\t}\n\t\t\treturn dstptr;\n\t\t}\n\t}\n\t*ptr = 0;\n\treturn dstptr;\n}\n\nvoid\ncfg_file_readdir(const char *pathptr)\n{\n\tstruct dirent *direntptr;\n\tstruct stat stat_buf;\n\tDIR\t*dirptr;\n\tmode_t\tfmt;\n\tchar\t*str;\n\tconst char *tmppathptr;\n\tint\tsize, ret, is_dir, is_gz, len;\n\tint\ti;\n\n\tif(!strncmp(pathptr, &g_cfg_file_cachedpath[0], CFG_PATH_MAX) &&\n\t\t\t(g_cfg_dirlist.last > 0) && (g_cfg_dirlist.invalid==0)){\n\t\treturn;\n\t}\n\t// No match, must read new directory\n\n\t// Free all dirents that were cached previously\n\tcfg_free_alldirents(&g_cfg_dirlist);\n\n\tcfg_strncpy(&g_cfg_file_cachedpath[0], pathptr, CFG_PATH_MAX);\n\tcfg_strncpy(&g_cfg_file_cachedreal[0], pathptr, CFG_PATH_MAX);\n\n\tstr = &g_cfg_file_cachedreal[0];\n\n\tfor(i = 0; i < 200; i++) {\n\t\tlen = (int)strlen(str);\n\t\tif(len <= 0) {\n\t\t\tbreak;\n\t\t} else if(len < CFG_PATH_MAX-2) {\n\t\t\tif(str[len-1] != '/') {\n\t\t\t\t// append / to make various routines work\n\t\t\t\tstr[len] = '/';\n\t\t\t\tstr[len+1] = 0;\n\t\t\t}\n\t\t}\n\t\tret = cfg_stat(str, &stat_buf, 0);\n\t\tis_dir = 0;\n\t\tif(ret == 0) {\n\t\t\tfmt = stat_buf.st_mode & S_IFMT;\n\t\t\tif(fmt == S_IFDIR) {\n\t\t\t\t/* it's a directory */\n\t\t\t\tis_dir = 1;\n\t\t\t}\n\t\t}\n\t\tif(is_dir) {\n\t\t\tbreak;\n\t\t} else {\n\t\t\t// user is entering more path, use base for display\n\t\t\tcfg_get_base_path(str, str, 0);\n\t\t}\n\t}\n\n\ttmppathptr = str;\n\tif(str[0] == 0) {\n\t\ttmppathptr = \".\";\n\t}\n\tcfg_file_add_dirent(&g_cfg_dirlist, \"..\", 1, 0, 0, 0, -1);\n\n\tdirptr = opendir(tmppathptr);\n\tif(dirptr == 0) {\n\t\tprintf(\"Could not open %s as a directory\\n\", tmppathptr);\n\t\treturn;\n\t}\n\twhile(1) {\n\t\tdirentptr = readdir(dirptr);\n\t\tif(direntptr == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tif(!strcmp(\".\", direntptr->d_name)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif(!strcmp(\"..\", direntptr->d_name)) {\n\t\t\tcontinue;\n\t\t}\n\t\t/* Else, see if it is a directory or a file */\n\t\tcfg_strncpy(&(g_cfg_tmp_path[0]), &(g_cfg_file_cachedreal[0]),\n\t\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t\tcfg_strlcat(&(g_cfg_tmp_path[0]), direntptr->d_name,\n\t\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t\tret = cfg_stat(&g_cfg_tmp_path[0], &stat_buf, 0);\n\t\tlen = (int)strlen(g_cfg_tmp_path);\n\t\tis_dir = 0;\n\t\tis_gz = 0;\n\t\tif((len > 3) && !cfgcasecmp(&g_cfg_tmp_path[len - 3], \".gz\")) {\n\t\t\tis_gz = 1;\n\t\t}\n\t\tif((len > 4) && !cfgcasecmp(&g_cfg_tmp_path[len - 4], \".bz2\")) {\n\t\t\tis_gz = 1;\n\t\t}\n\t\tif((len > 4) && !cfgcasecmp(&g_cfg_tmp_path[len - 4], \".zip\")) {\n\t\t\tis_gz = 1;\n\t\t}\n\t\tif((len > 4) && !cfgcasecmp(&g_cfg_tmp_path[len - 4], \".woz\")) {\n\t\t\tis_gz = 1;\n\t\t}\n\t\tif((len > 4) && !cfgcasecmp(&g_cfg_tmp_path[len - 4], \".sdk\")) {\n\t\t\tis_gz = 1;\n\t\t}\n\t\tif(ret != 0) {\n\t\t\tprintf(\"stat %s ret %d, errno:%d\\n\", &g_cfg_tmp_path[0],\n\t\t\t\t\t\tret, errno);\n\t\t\tstat_buf.st_size = 0;\n\t\t\tcontinue;\t/* skip it */\n\t\t} else {\n\t\t\tfmt = stat_buf.st_mode & S_IFMT;\n\t\t\tsize = (int)stat_buf.st_size;\n\t\t\tif(fmt == S_IFDIR) {\n\t\t\t\t/* it's a directory */\n\t\t\t\tis_dir = 1;\n\t\t\t} else if((fmt == S_IFREG) && (is_gz == 0)) {\n\t\t\t\tif((g_cfg_slotdrive & 0xfff) == 0xfff) {\n\t\t\t\t\t/* see if there are size limits */\n\t\t\t\t\tif((size < g_cfg_file_min_size) ||\n\t\t\t\t\t\t(size > g_cfg_file_max_size)) {\n\t\t\t\t\t\tcontinue;\t/* skip it */\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif(size < 140*1024) {\n\t\t\t\t\t\tcontinue;\t/* skip it */\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcfg_file_add_dirent(&g_cfg_dirlist, direntptr->d_name, is_dir,\n\t\t\t\t\t(dword64)stat_buf.st_size, 0, 0, -1);\n\t}\n\n\tclosedir(dirptr);\n\n\t/* then sort the results (Mac's HFS+ is sorted, but other FS won't be)*/\n\tqsort(&(g_cfg_dirlist.direntptr[0]), g_cfg_dirlist.last,\n\t\t\t\t\tsizeof(Cfg_dirent), cfg_dirent_sortfn);\n\n\tg_cfg_dirlist.curent = g_cfg_dirlist.last - 1;\n\tfor(i = g_cfg_dirlist.last - 1; i >= 0; i--) {\n\t\tret = cfg_str_match(&(g_cfg_file_match[0]),\n\t\t\t\tg_cfg_dirlist.direntptr[i].name, CFG_PATH_MAX);\n\t\tif(ret <= 0) {\n\t\t\t/* set cur ent to closest filename to the match name */\n\t\t\tg_cfg_dirlist.curent = i;\n\t\t}\n\t}\n}\n\nchar *\ncfg_shorten_filename(const char *in_ptr, int maxlen)\n{\n\tchar\t*out_ptr;\n\tint\tlen, c;\n\tint\ti;\n\n\t/* Warning: uses a static string, not reentrant! */\n\n\tout_ptr = &(g_cfg_file_shortened[0]);\n\tlen = (int)strlen(in_ptr);\n\tmaxlen = MY_MIN(len, maxlen);\n\tfor(i = 0; i < maxlen; i++) {\n\t\tc = in_ptr[i] & 0x7f;\n\t\tif(c < 0x20) {\n\t\t\tc = '*';\n\t\t}\n\t\tout_ptr[i] = c;\n\t}\n\tout_ptr[maxlen] = 0;\n\tif(len > maxlen) {\n\t\tfor(i = 0; i < (maxlen/2); i++) {\n\t\t\tc = in_ptr[len-i-1] & 0x7f;\n\t\t\tif(c < 0x20) {\n\t\t\t\tc = '*';\n\t\t\t}\n\t\t\tout_ptr[maxlen-i-1] = c;\n\t\t}\n\t\tout_ptr[(maxlen/2) - 1] = '.';\n\t\tout_ptr[maxlen/2] = '.';\n\t\tout_ptr[(maxlen/2) + 1] = '.';\n\t}\n\n\treturn out_ptr;\n}\n\nvoid\ncfg_fix_topent(Cfg_listhdr *listhdrptr)\n{\n\tint\tnum_to_show;\n\n\tnum_to_show = listhdrptr->num_to_show;\n\n\t/* Force curent and topent to make sense */\n\tif(listhdrptr->curent >= listhdrptr->last) {\n\t\tlisthdrptr->curent = listhdrptr->last - 1;\n\t}\n\tif(listhdrptr->curent < 0) {\n\t\tlisthdrptr->curent = 0;\n\t}\n\tif(abs(listhdrptr->curent - listhdrptr->topent) >= num_to_show) {\n\t\tlisthdrptr->topent = listhdrptr->curent - (num_to_show/2);\n\t}\n\tif(listhdrptr->topent > listhdrptr->curent) {\n\t\tlisthdrptr->topent = listhdrptr->curent - (num_to_show/2);\n\t}\n\tif(listhdrptr->topent < 0) {\n\t\tlisthdrptr->topent = 0;\n\t}\n}\n\nvoid\ncfg_file_draw()\n{\n\tCfg_listhdr *listhdrptr;\n\tCfg_dirent *direntptr;\n\tconst char *tmp_str;\n\tchar\t*str, *fmt;\n\tint\tnum_to_show;\n\tint\tyoffset;\n\tint\tx, y;\n\tint\ti;\n\n\t//printf(\"cfg_file_draw called\\n\");\n\n\tcfg_file_readdir(&g_cfg_file_curpath[0]);\n\n\tfor(y = 0; y < 21; y++) {\n\t\tcfg_htab_vtab(0, y);\n\t\tcfg_printf(\"\\tZ\\t\");\n\t\tfor(x = 1; x < 79; x++) {\n\t\t\tcfg_htab_vtab(x, y);\n\t\t\tcfg_putchar(' ');\n\t\t}\n\t\tcfg_htab_vtab(79, y);\n\t\tcfg_printf(\"\\t_\\t\");\n\t}\n\n\tcfg_htab_vtab(1, 0);\n\tcfg_putchar('\\b');\n\tfor(x = 1; x < 79; x++) {\n\t\tcfg_putchar(' ');\n\t}\n\tif((g_cfg_slotdrive & 0xfff) == 0xfff) {\n\t\tcfg_htab_vtab(5, 0);\n\t\tcfg_printf(\"\\bSelect file to use as %-40s\\b\",\n\t\t\tcfg_shorten_filename(g_cfg_file_def_name, 40));\n\t} else {\n\t\tcfg_htab_vtab(30, 0);\n\t\ttmp_str = \"Select\";\n\t\tif(g_cfg_newdisk_select == 2) {\n\t\t\ttmp_str = \"Create duplicate\";\n\t\t} else if(g_cfg_newdisk_select) {\n\t\t\ttmp_str = \"Create new\";\n\t\t}\n\t\tcfg_printf(\"\\b%s image for s%dd%d\\b\", tmp_str,\n\t\t\t(g_cfg_slotdrive >> 8) & 0xf,\n\t\t\t(g_cfg_slotdrive & 0xff) + 1);\n\t}\n\n\tcfg_htab_vtab(2, 1);\n\tcfg_printf(\"config.kegs path: %-56s\",\n\t\t\tcfg_shorten_filename(&g_config_kegs_name[0], 56));\n\n\tcfg_htab_vtab(2, 2);\n\tcfg_printf(\"Current KEGS directory: %-50s\",\n\t\t\tcfg_shorten_filename(&g_cfg_cwd_str[0], 50));\n\n\tcfg_htab_vtab(2, 3);\n\n\tstr = \"\";\n\tif(g_cfg_file_pathfield) {\n\t\tstr = \"\\b \\b\";\n\t}\n\tcfg_printf(\"Path: %s%s\",\n\t\t\tcfg_shorten_filename(&g_cfg_file_curpath[0], 68), str);\n\n\tcfg_htab_vtab(0, 4);\n\tcfg_printf(\" \\t\");\n\tfor(x = 1; x < 79; x++) {\n\t\tcfg_putchar('\\\\');\n\t}\n\tcfg_printf(\"\\t \");\n\n\t/* Force curent and topent to make sense */\n\tlisthdrptr = &g_cfg_dirlist;\n\tnum_to_show = CFG_NUM_SHOWENTS;\n\tyoffset = 5;\n\tif(g_cfg_select_partition > 0) {\n\t\tlisthdrptr = &g_cfg_partitionlist;\n\t\tnum_to_show -= 2;\n\t\tcfg_htab_vtab(2, yoffset);\n\t\tcfg_printf(\"Select partition of %-50s\",\n\t\t\tcfg_shorten_filename(&g_cfg_file_path[0], 50), \"\");\n\t\tcfg_htab_vtab(2, yoffset + 1);\n\t\tcfg_printf(\"Current partition: %-50s\",\n\t\t\tcfg_shorten_filename(&g_cfg_part_path[0], 50), \"\");\n\t\tyoffset += 2;\n\t}\n\n\tlisthdrptr->num_to_show = num_to_show;\n\tcfg_fix_topent(listhdrptr);\n\tfor(i = 0; i < num_to_show; i++) {\n\t\ty = i + yoffset;\n\t\tif(listhdrptr->last > (i + listhdrptr->topent)) {\n\t\t\tdirentptr = &(listhdrptr->\n\t\t\t\t\tdirentptr[i + listhdrptr->topent]);\n\t\t\tcfg_htab_vtab(2, y);\n\t\t\tif(direntptr->is_dir) {\n\t\t\t\tcfg_printf(\"\\tXY\\t \");\n\t\t\t} else {\n\t\t\t\tcfg_printf(\"   \");\n\t\t\t}\n\t\t\tif(direntptr->part_num >= 0) {\n\t\t\t\tcfg_printf(\"%3d: \", direntptr->part_num);\n\t\t\t}\n\t\t\tstr = cfg_shorten_filename(direntptr->name, 50);\n\t\t\tfmt = \"%-50s\";\n\t\t\tif((i + listhdrptr->topent) == listhdrptr->curent) {\n\t\t\t\tif(g_cfg_file_pathfield == 0) {\n\t\t\t\t\tfmt = \"\\b%-50s\\b\";\n\t\t\t\t} else {\n\t\t\t\t\tfmt = \"%-49s\\b \\b\";\n\t\t\t\t}\n\t\t\t\t//printf(\"file highlight l %d top:%d cur:%d\\n\",\n\t\t\t\t//\ti, listhdrptr->topent,\n\t\t\t\t//\tlisthdrptr->curent);\n\t\t\t}\n\t\t\tcfg_printf(fmt, str);\n\t\t\tif(!direntptr->is_dir) {\n\t\t\t\tcfg_print_dnum(direntptr->dsize, 18);\n\t\t\t}\n\t\t\t//printf(\" :%s:%lld:\\n\", str, direntptr->dsize);\n\t\t}\n\t}\n\n\tcfg_htab_vtab(1, 5 + CFG_NUM_SHOWENTS);\n\tcfg_putchar('\\t');\n\tfor(x = 1; x < 79; x++) {\n\t\tcfg_putchar('L');\n\t}\n\tcfg_putchar('\\t');\n\n\t//printf(\"cfg_file_draw done\\n\");\n}\n\nvoid\ncfg_partition_select_all()\n{\n\tword32\tslot_drive;\n\tint\tpart_path_len, curent;\n\n\tslot_drive = g_cfg_slotdrive;\n\tpart_path_len = (int)strlen(&g_cfg_part_path[0]);\n\tcurent = g_cfg_partitionlist.curent;\n\twhile(1) {\n\t\tg_cfg_slotdrive = slot_drive;\n\t\tg_cfg_partitionlist.curent = curent;\n\t\tcfg_partition_selected();\n\t\tif(g_cfg_slotdrive != 0) {\n\t\t\t// Something went wrong, get out\n\t\t\treturn;\n\t\t}\n\t\tslot_drive++;\n\t\tcurent++;\n\t\tg_cfg_part_path[part_path_len] = 0;\n\t\tif(curent >= g_cfg_partitionlist.last) {\n\t\t\treturn;\n\t\t}\n\t\tif((slot_drive >> 8) == 7) {\n\t\t\tif((slot_drive & 0xff) >= MAX_C7_DISKS) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif((slot_drive & 0xff) >= 12) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if((slot_drive & 0xff) >= 2) {\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid\ncfg_partition_selected()\n{\n\tchar\t*str;\n\tconst char *part_str;\n\tchar\t*part_str2;\n\tint\tpos;\n\tint\tpart_num;\n\n\tpos = g_cfg_partitionlist.curent;\n\tstr = g_cfg_partitionlist.direntptr[pos].name;\n\tif(g_cfg_partitionlist.direntptr[pos].is_dir) {\n\t\t// Add this path to the partition path, and try again\n\t\tif(!strcmp(str, \"../\")) {\n\t\t\t/* go up one directory */\n\t\t\tcfg_get_base_path(&g_cfg_part_path[0],\n\t\t\t\t\t\t&g_cfg_part_path[0], 1);\n\t\t} else {\n\t\t\tcfg_strlcat(&(g_cfg_part_path[0]), str, CFG_PATH_MAX);\n\t\t}\n\t\tcfg_partition_make_list_from_name(&g_cfg_file_path[0]);\n\t\treturn;\n\t}\n\n\tpart_num = -2;\n\tpart_str = 0;\n\tif(str[0] == 0 || (str[0] >= '0' && str[0] <= '9')) {\n\t\tpart_num = g_cfg_partitionlist.direntptr[pos].part_num;\n\t} else {\n\t\tpart_str = str;\n\t}\n\tpart_str2 = 0;\n\tif(part_str != 0) {\n\t\tcfg_strlcat(&g_cfg_part_path[0], part_str, CFG_PATH_MAX);\n\t\tpart_str2 = kegs_malloc_str(&g_cfg_part_path[0]);\n\t\tg_cfg_part_path[0] = 0;\n\t}\n\tprintf(\"cfg_partition_selected, pos:%d, g_cfg_file_path[0]:%s, \"\n\t\t\"part:%s\\n\", pos, g_cfg_file_path, part_str2);\n\n\tinsert_disk((g_cfg_slotdrive >> 8) & 0xf, g_cfg_slotdrive & 0xff,\n\t\t\t&(g_cfg_file_path[0]), 0, part_str2, part_num, 0);\n\tfree(part_str2);\n\tg_cfg_slotdrive = 0;\n\tg_cfg_newdisk_select = 0;\n\tg_cfg_select_partition = -1;\n}\n\nvoid\ncfg_file_selected()\n{\n\tstruct stat stat_buf;\n\tchar\t*str;\n\tint\tfmt, stat_errno, is_cmd_key_down;\n\tint\tret;\n\n\tis_cmd_key_down = adb_is_cmd_key_down() &&\n\t\t\t\t\t((g_cfg_slotdrive & 0xfff) != 0xfff);\n\t\t// Cmd-Return means create DynaPro image when using slot/drive\n\n\tif(g_cfg_select_partition > 0) {\n\t\tcfg_partition_selected();\n\t\treturn;\n\t}\n\n\tif(!is_cmd_key_down && (g_cfg_file_pathfield == 0)) {\n\t\t// in file section area of window\n\t\tstr = g_cfg_dirlist.direntptr[g_cfg_dirlist.curent].name;\n\t\tif(!strcmp(str, \"../\")) {\n\t\t\t/* go up one directory */\n\t\t\tcfg_get_base_path(&g_cfg_file_curpath[0],\n\t\t\t\t\t\t&g_cfg_file_curpath[0], 1);\n\t\t\treturn;\n\t\t}\n\n\t\tcfg_strncpy(&(g_cfg_file_path[0]), &(g_cfg_file_cachedreal[0]),\n\t\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t\tcfg_strlcat(&(g_cfg_file_path[0]), str, CFG_PATH_MAX);\n\t} else {\n\t\t// just use cfg_file_curpath directly\n\t\tcfg_strncpy(&g_cfg_file_path[0], &g_cfg_file_curpath[0],\n\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t}\n\n\tret = cfg_stat(&g_cfg_file_path[0], &stat_buf, 0);\n\tstat_errno = errno;\n\tfmt = stat_buf.st_mode & S_IFMT;\n\tcfg_printf(\"Stat'ing %s, st_mode is: %08x\\n\", &g_cfg_file_path[0],\n\t\t\t(int)stat_buf.st_mode);\n\n\tif((ret == 0) && (fmt == S_IFDIR) && is_cmd_key_down &&\n\t\t\t\t\t\t(g_cfg_newdisk_select != 2)) {\n\t\t// Make a new DynaPro disk\n\t\tcfg_insert_disk_dynapro((g_cfg_slotdrive >> 8) & 0xf,\n\t\t\t\tg_cfg_slotdrive & 0xff, &g_cfg_file_path[0]);\n\t\tg_cfg_slotdrive = 0;\t\t\t// End file selection\n\t\tg_cfg_newdisk_select = 0;\n\t\tg_menuptr = &g_cfg_disk_menu[0];\n\t} else if((g_cfg_newdisk_select == 1) && (g_cfg_newdisk_type == 3) &&\n\t\t\tg_cfg_file_pathfield && (fmt == S_IFDIR)) {\n\t\t// Special handling for Dynamic ProDOS directories.  User hit\n\t\t//  return in the Path field on a directory, use this directory\n\t\tcfg_create_new_image();\n\t} if(ret != 0) {\n\t\tif(g_cfg_newdisk_select && (g_cfg_newdisk_type != 3)) {\n\t\t\t// This looks good, a new file name was entered\n\t\t\tif(stat_errno == ENOENT) {\n\t\t\t\tcfg_create_new_image();\n\t\t\t} else {\n\t\t\t\tprintf(\"Unknown errno:%d while checking %s\\n\",\n\t\t\t\t\tstat_errno, &g_cfg_file_path[0]);\n\t\t\t}\n\t\t} else {\n\t\t\tprintf(\"stat %s returned %d, errno: %d\\n\",\n\t\t\t\t\t&g_cfg_file_path[0], ret, stat_errno);\n\t\t}\n\t} else if(fmt == S_IFDIR) {\n\t\t/* it's a directory */\n\t\tcfg_strncpy(&g_cfg_file_curpath[0], &g_cfg_file_path[0],\n\t\t\t\t\t\t\t\tCFG_PATH_MAX);\n\t} else if(g_cfg_newdisk_select) {\n\t\t// Do not allow selecting files, just ignore it\n\t} else if((g_cfg_slotdrive & 0xfff) < 0xfff) {\n\t\t/* select it */\n\t\tret = cfg_maybe_insert_disk((g_cfg_slotdrive >> 8) & 0xf,\n\t\t\t\tg_cfg_slotdrive & 0xff, &g_cfg_file_path[0]);\n\t\tif(ret > 0) {\n\t\t\tg_cfg_slotdrive = 0;\n\t\t\tg_cfg_newdisk_select = 0;\n\t\t}\n\t} else {\n\t\tcfg_file_update_ptr(g_cfg_file_strptr, &g_cfg_file_path[0], 1);\n\t\tg_cfg_slotdrive = 0;\n\t\tg_cfg_newdisk_select = 0;\n\t}\n}\n\nvoid\ncfg_file_handle_key(int key)\n{\n\tCfg_listhdr *listhdrptr;\n\tint\tlen, lowkey, got_match_key, is_cmd_key_down;\n\n\t// Modes: g_cfg_slotdrive: 1 to 0xfff: File selection dialog\n\t//\t\totherwise: normal menu being shown\n\t//\tg_cfg_file_pathfield: File selection with cursor in Path: field\n\t//\t\totherwise: in scrolling file selection field\n\t//\tg_cfg_select_partition: file selection for partition name\n\tif(g_cfg_file_pathfield) {\n\t\tif(key >= 0x20 && key < 0x7f) {\n\t\t\tlen = (int)strlen(&g_cfg_file_curpath[0]);\n\t\t\tif(len < CFG_PATH_MAX-4) {\n\t\t\t\tg_cfg_file_curpath[len] = key;\n\t\t\t\tg_cfg_file_curpath[len+1] = 0;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n\n\tlisthdrptr = &g_cfg_dirlist;\n\tis_cmd_key_down = 0;\n\tif(g_cfg_select_partition > 0) {\n\t\tlisthdrptr = &g_cfg_partitionlist;\n\t\tis_cmd_key_down = adb_is_cmd_key_down() &&\n\t\t\t\t\t((g_cfg_slotdrive & 0xfff) != 0xfff);\n\t}\n\tlowkey = tolower(key);\n\tgot_match_key = 0;\n\tif((g_cfg_file_pathfield == 0) && (lowkey >= 'a') && (lowkey <= 'z') &&\n\t\t\t\t\t\t!is_cmd_key_down) {\n\t\t/* jump to file starting with this letter */\n\t\tg_cfg_file_match[0] = key;\n\t\tg_cfg_file_match[1] = 0;\n\t\tg_cfg_dirlist.invalid = 1;\t/* re-read directory */\n\t\tgot_match_key = 1;\n\t}\n\n\tswitch(key) {\n\tcase 0x1b:\t\t\t\t// ESC\n\t\tif(((g_cfg_slotdrive & 0xfff) < 0xfff) &&\n\t\t\t\t\t\t\t!g_cfg_newdisk_select) {\n\t\t\tiwm_eject_disk_by_num((g_cfg_slotdrive >> 8) & 0xf,\n\t\t\t\t\t\tg_cfg_slotdrive & 0xff);\n\t\t}\n\t\tg_cfg_slotdrive = 0;\n\t\tg_cfg_select_partition = -1;\n\t\tg_cfg_dirlist.invalid = 1;\n\t\tg_cfg_newdisk_select = 0;\n\t\tbreak;\n\tcase 0x0a:\t/* down arrow */\n\t\tif(g_cfg_file_pathfield == 0) {\n\t\t\tlisthdrptr->curent++;\n\t\t\tcfg_fix_topent(listhdrptr);\n\t\t}\n\t\tbreak;\n\tcase 0x0b:\t/* up arrow */\n\t\tif(g_cfg_file_pathfield == 0) {\n\t\t\tlisthdrptr->curent--;\n\t\t\tcfg_fix_topent(listhdrptr);\n\t\t}\n\t\tbreak;\n\tcase 0x0d:\t/* return */\n\t\t//printf(\"handling return press\\n\");\n\t\tcfg_file_selected();\n\t\tbreak;\n\tcase 0x61:\t/* 'a' */\n\t\tif(is_cmd_key_down && (g_cfg_select_partition > 0)) {\n\t\t\tcfg_partition_select_all();\n\t\t}\n\t\tbreak;\n\tcase 0x09:\t/* tab */\n\t\tg_cfg_file_pathfield = !g_cfg_file_pathfield;\n\t\tif(g_cfg_select_partition > 0) {\n\t\t\t// If selecting file inside zip or partition, don't\n\t\t\t//  allow editing of the Path info\n\t\t\tg_cfg_file_pathfield = 0;\n\t\t}\n\t\tbreak;\n\tcase 0x08:\t/* left arrow */\n\tcase 0x7f:\t/* delete key */\n\t\tif(g_cfg_file_pathfield) {\n\t\t\t// printf(\"left arrow/delete\\n\");\n\t\t\tlen = (int)strlen(&g_cfg_file_curpath[0]) - 1;\n\t\t\tif(len >= 0) {\n\t\t\t\tg_cfg_file_curpath[len] = 0;\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tif(!got_match_key) {\n\t\t\tprintf(\"key: %02x\\n\", key);\n\t\t}\n\t}\n#if 0\n\tprintf(\"curent: %d, topent: %d, last: %d\\n\",\n\t\tg_cfg_dirlist.curent, g_cfg_dirlist.topent, g_cfg_dirlist.last);\n#endif\n}\n\nvoid\ncfg_draw_menu()\n{\n\tconst char *str;\n\tCfg_menu *menuptr;\n\tint\tprint_eject_help, line, type, match_found, menu_line, max_line;\n\n\tg_menu_redraw_needed = 0;\n\n\tmenuptr = g_menuptr;\n\tif(menuptr == 0) {\n\t\tmenuptr = g_cfg_main_menu;\n\t}\n\tif(g_rom_version < 0) {\n\t\t/* Must select ROM file */\n\t\tmenuptr = g_cfg_rom_menu;\n\t}\n\tg_menuptr = menuptr;\n\n\tcfg_home();\n\tline = 1;\n\tmax_line = 1;\n\tmatch_found = 0;\n\tprint_eject_help = 0;\n\tmenu_line = g_menu_line;\n\tcfg_printf(\"%s\\n\\n\", menuptr[0].str);\n\twhile(line < 24) {\n\t\tstr = menuptr[line].str;\n\t\ttype = menuptr[line].cfgtype;\n\t\tif(str == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tif((type & 0xf) == CFGTYPE_DISK) {\n\t\t\tprint_eject_help = 1;\n\t\t}\n#if 0\n\t\tprintf(\"Calling parse_menu line:%d, menu_line:%d, %p\\n\", line,\n\t\t\t\t\t\t\tmenu_line, menuptr);\n#endif\n\t\tcfg_parse_menu(menuptr, line, menu_line, 0);\n\t\tif(line == g_menu_line) {\n\t\t\tif(type != 0) {\n\t\t\t\tmatch_found = 1;\n\t\t\t} else if(g_menu_inc) {\n\t\t\t\tmenu_line++;\n\t\t\t} else {\n\t\t\t\tmenu_line--;\n\t\t\t}\n\t\t}\n\t\tif(line > max_line) {\n\t\t\tmax_line = line;\n\t\t}\n\n\t\tcfg_printf(\"%s\\n\", g_cfg_opt_buf);\n\t\tline++;\n\t}\n\tif((menu_line < 1) && !match_found) {\n\t\tmenu_line = 1;\n\t}\n\tif((menu_line >= max_line) && !match_found) {\n\t\tmenu_line = max_line;\n\t}\n\n\tg_menu_line = menu_line;\n\tg_menu_max_line = max_line;\n\tif(!match_found) {\n\t\tg_menu_redraw_needed = 1;\n\t}\n\tif(g_rom_version < 0) {\n\t\tcfg_htab_vtab(0, 21);\n\t\tcfg_printf(\"\\bYOU MUST SELECT A VALID ROM FILE\\b\\n\");\n\t}\n\n\tcfg_htab_vtab(0, 23);\n\tcfg_printf(\"Move: \\tJ\\t \\tK\\t Change: \\tH\\t \\tU\\t \\tM\\t\");\n\tif(print_eject_help) {\n\t\tcfg_printf(\"  Eject: \");\n\t\tif((g_cfg_slotdrive & 0xfff) > 0) {\n\t\t\tcfg_printf(\"\\bESC\\b\");\n\t\t} else {\n\t\t\tcfg_printf(\"E\");\n\t\t\tcfg_printf(\"  New image: N  Dup image: D  Verify: V\");\n\t\t}\n\t}\n\tif((g_cfg_slotdrive & 0xfff) > 0) {\n\t\tcfg_printf(\"  Edit Path: \\bTAB\\b\");\n\t\tif(g_cfg_select_partition > 0) {\n\t\t\tcfg_printf(\"  (\\bCmd\\b-\\bA\\b to mount all)\");\n\t\t} else if((g_cfg_newdisk_type == 3) || !g_cfg_newdisk_select) {\n\t\t\t// Dynamic ProDOS, select a directory\n\t\t\tcfg_printf(\"  (\\bCmd\\b-\\bEnter\\b for DynaPro)\");\n\t\t}\n\t\tif(g_cfg_newdisk_select && (g_cfg_newdisk_type != 3)) {\n\t\t\tcfg_printf(\"  (Enter new name on Path)\");\n\t\t}\n\t}\n#if 0\n\tcfg_htab_vtab(0, 22);\n\tcfg_printf(\"menu_line: %d line: %d, vbl:%d, adb:%d key_dn:%d\\n\",\n\t\t\tmenu_line, line, g_cfg_vbl_count, g_adb_repeat_vbl,\n\t\t\tg_key_down);\n#endif\n\n\tif((g_cfg_slotdrive & 0xfff) > 0) {\n\t\tcfg_file_draw();\n\t}\n}\n\nvoid\ncfg_newdisk_pick_menu(word32 slotdrive)\n{\n\tslotdrive = slotdrive & 0xfff;\n\tg_cfg_newdisk_slotdrive = slotdrive;\t// 0x601: s6d2, 0x500: s5d1\n\tg_menu_line = 1;\n\t//printf(\"N key, g_menuptr=%p\\n\", g_menuptr);\n\tg_cfg_newdisk_type_default = 1;\n\tg_cfg_newdisk_type = 1;\n\tg_cfg_newdisk_blocks_default = 140*2;\n\tg_cfg_newdisk_blocks = 280;\n\tif((slotdrive >> 8) == 6) {\n\t\tg_menuptr = g_cfg_newslot6_menu;\n\t} else if((slotdrive >> 8) == 5) {\n\t\tg_menuptr = g_cfg_newslot5_menu;\n\t\tg_cfg_newdisk_blocks_default = 1600;\n\t\tg_cfg_newdisk_blocks = 1600;\n\t} else {\n\t\tg_menuptr = g_cfg_newslot7_menu;\n\t\tg_cfg_newdisk_blocks_default = 65535;\n\t\tg_cfg_newdisk_blocks = 65535;\n\t}\n}\n\nint\ncfg_control_panel_update()\n{\n\tint\tret;\n\tint\ti;\n\n\tret = cfg_control_panel_update1();\n\tif(g_cfg_screen_changed) {\n\t\tfor(i = 0; i < 24; i++) {\n\t\t\tvideo_draw_a2_string(i, &g_cfg_screen[i][0]);\n\t\t}\n\t}\n\tg_cfg_screen_changed = 0;\n\n\treturn ret;\n}\n\nvoid\ncfg_edit_mode_key(int key)\n{\n\tchar\t*new_str;\n\tint\t*iptr;\n\tint\tlen, ival;\n\n\tlen = (int)strlen(&g_cfg_edit_buf[0]);\n\tif(key == 0x0d) {\t\t// Return\n\t\t// Try to accept the change\n\t\tnew_str = kegs_malloc_str(&g_cfg_edit_buf[0]);\n\t\tif(g_cfg_edit_type == CFGTYPE_STR) {\n\t\t\tcfg_file_update_ptr(g_cfg_edit_ptr, new_str, 1);\n\t\t} else if(g_cfg_edit_type == CFGTYPE_INT) {\n\t\t\tival = strtol(&g_cfg_edit_buf[0], 0, 0);\n\t\t\tiptr = (int *)g_cfg_edit_ptr;\n\t\t\tcfg_int_update(iptr, ival);\n\t\t}\n\t\tg_cfg_edit_ptr = 0;\n\t\tg_config_kegs_update_needed = 1;\n\t} else if(key == 0x1b) {\t// ESC\n\t\tg_cfg_edit_ptr = 0;\t// Abort out of edit mode, no changes\n\t} else if((key == 0x08) || (key == 0x7f)) {\t// Left arrow or Delete\n\t\tlen--;\n\t\tif(len >= 0) {\n\t\t\tg_cfg_edit_buf[len] = 0;\n\t\t}\n\t} else if((key >= 0x20) && (key < 0x7f)) {\n\t\tif(len < (CFG_OPT_MAXSTR - 3)) {\n\t\t\tg_cfg_edit_buf[len] = key;\n\t\t\tg_cfg_edit_buf[len+1] = 0;\n\t\t}\n\t}\n}\n\nint\ncfg_control_panel_update1()\n{\n\tchar\t*(*fn_ptr)(int);\n\tvoid\t*ptr;\n\tchar\t**str_ptr;\n\tint\t*iptr;\n\tint\ttype, key;\n\n\twhile(g_config_control_panel) {\n\t\tif(g_menu_redraw_needed) {\n\t\t\tcfg_draw_menu();\n\t\t}\n\t\tif(g_menu_redraw_needed) {\n\t\t\tcfg_draw_menu();\n\t\t}\n\t\tkey = adb_read_c000();\n\t\tif(key & 0x80) {\n\t\t\tkey = key & 0x7f;\n\t\t\t(void)adb_access_c010();\n\t\t} else {\n\t\t\treturn 0;\t\t// No keys\n\t\t}\n\t\tg_menu_redraw_needed = 1;\n\t\t// If we get here, we got a key, figure out what to do with it\n\t\tif(g_cfg_slotdrive & 0xfff) {\n\t\t\tcfg_file_handle_key(key);\n\t\t\tcontinue;\n\t\t}\n\t\tif(g_cfg_edit_ptr) {\n\t\t\tcfg_edit_mode_key(key);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Normal menu system\n\t\tswitch(key) {\n\t\tcase 0x0a: /* down arrow */\n\t\t\tg_menu_line++;\n\t\t\tg_menu_inc = 1;\n\t\t\tbreak;\n\t\tcase 0x0b: /* up arrow */\n\t\t\tg_menu_line--;\n\t\t\tg_menu_inc = 0;\n\t\t\tif(g_menu_line < 1) {\n\t\t\t\tg_menu_line = 1;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x15: /* right arrow */\n\t\t\tcfg_parse_menu(g_menuptr, g_menu_line, g_menu_line, 1);\n\t\t\tbreak;\n\t\tcase 0x08: /* left arrow */\n\t\t\tcfg_parse_menu(g_menuptr, g_menu_line, g_menu_line, -1);\n\t\t\tbreak;\n\t\tcase 0x0d:\n\t\t\ttype = g_menuptr[g_menu_line].cfgtype;\n\t\t\tptr = g_menuptr[g_menu_line].ptr;\n\t\t\tswitch(type & 0xf) {\n\t\t\tcase CFGTYPE_MENU:\n\t\t\t\tg_menuptr = (Cfg_menu *)ptr;\n\t\t\t\tg_menu_line = 1;\n\t\t\t\tbreak;\n\t\t\tcase CFGTYPE_DISK:\n\t\t\t\tg_cfg_slotdrive = (type >> 4) & 0xfff;\n\t\t\t\tcfg_file_init();\n\t\t\t\tbreak;\n\t\t\tcase CFGTYPE_FUNC:\n\t\t\t\tfn_ptr = (char * (*)(int))ptr;\n\t\t\t\t(void)(*fn_ptr)(0);\n\t\t\t\tbreak;\n\t\t\tcase CFGTYPE_FILE:\n\t\t\t\tg_cfg_slotdrive = 0xfff;\n\t\t\t\tg_cfg_file_def_name = *((char **)ptr);\n\t\t\t\tg_cfg_file_strptr = (char **)ptr;\n\t\t\t\tcfg_file_init();\n\t\t\t\tbreak;\n\t\t\tcase CFGTYPE_STR:\n\t\t\t\tstr_ptr = (char **)ptr;\n\t\t\t\tif(str_ptr) {\n\t\t\t\t\tg_cfg_edit_type = type & 0xf;\n\t\t\t\t\tg_cfg_edit_ptr = str_ptr;\n\t\t\t\t\tcfg_strncpy(&g_cfg_edit_buf[0],\n\t\t\t\t\t\t*str_ptr, CFG_OPT_MAXSTR);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase CFGTYPE_INT:\n\t\t\t\t// If there are no ',' in the menu str, then\n\t\t\t\t//  allow user to enter a manual number\n\t\t\t\tif(!strchr(g_menuptr[g_menu_line].str, ',')) {\n\t\t\t\t\tg_cfg_edit_type = type & 0xf;\n\t\t\t\t\tg_cfg_edit_ptr = ptr;\n\t\t\t\t\tiptr = (int *)ptr;\n\t\t\t\t\tsnprintf(&g_cfg_edit_buf[0],\n\t\t\t\t\t\tCFG_OPT_MAXSTR, \"%d\", *iptr);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x1b:\n\t\t\t// Jump to last menu entry\n\t\t\tg_menu_line = g_menu_max_line;\n\t\t\tbreak;\n\t\tcase 'd':\n\t\tcase 'D':\t\t\t// Duplicate an image\n\t\t\ttype = g_menuptr[g_menu_line].cfgtype;\n\t\t\tif((type & 0xf) == CFGTYPE_DISK) {\n\t\t\t\tcfg_dup_existing_image(type >> 4);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'e':\n\t\tcase 'E':\n\t\t\ttype = g_menuptr[g_menu_line].cfgtype;\n\t\t\tif((type & 0xf) == CFGTYPE_DISK) {\n\t\t\t\tiwm_eject_disk_by_num(type >> 12,\n\t\t\t\t\t\t\t(type >> 4) & 0xff);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'l':\n\t\tcase 'L':\n\t\t\ttype = g_menuptr[g_menu_line].cfgtype;\n\t\t\tif((type & 0xf) == CFGTYPE_DISK) {\n\t\t\t\tcfg_toggle_lock_disk(type >> 4);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'n':\n\t\tcase 'N':\n\t\t\ttype = g_menuptr[g_menu_line].cfgtype;\n\t\t\tif((type & 0xf) == CFGTYPE_DISK) {\n\t\t\t\tcfg_newdisk_pick_menu(type >> 4);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'v':\n\t\tcase 'V':\n\t\t\ttype = g_menuptr[g_menu_line].cfgtype;\n\t\t\tif((type & 0xf) == CFGTYPE_DISK) {\n\t\t\t\tcfg_validate_image(type >> 4);\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tprintf(\"key: %02x\\n\", key);\n\t\t}\n\t}\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/config.h",
    "content": "#ifdef INCLUDE_RCSID_C\nconst char rcsid_config_h[] = \"@(#)$KmKId: config.h,v 1.12 2023-08-28 01:59:55+00 kentd Exp $\";\n#endif\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2019 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#define CONF_BUF_LEN\t\t1024\n#define COPY_BUF_SIZE\t\t4096\n#define CFG_PRINTF_BUFSIZE\t2048\n\n#define CFG_PATH_MAX\t\t1024\n\n#define CFG_NUM_SHOWENTS\t16\n\n#define CFGTYPE_MENU\t\t1\n#define CFGTYPE_INT\t\t2\n#define CFGTYPE_DISK\t\t3\n#define CFGTYPE_FUNC\t\t4\n#define CFGTYPE_FILE\t\t5\n#define CFGTYPE_STR\t\t6\n/* CFGTYPE limited to just 4 bits: 0-15 */\n\n/* Cfg_menu, Cfg_dirent and Cfg_listhdr are defined in defc.h */\n\nSTRUCT(Cfg_defval) {\n\tCfg_menu *menuptr;\n\tint\tintval;\n\tchar\t*strval;\n};\n"
  },
  {
    "path": "upstream/kegs/src/cp_kegs_libs",
    "content": "#!/usr/bin/perl -w\n# $KmKId: cp_kegs_libs,v 1.2 2021-02-09 00:35:48+00 kentd Exp $\n\nuse strict;\nuse English;\n\nif($#ARGV < 2) {\n\tdie \"Usage: executable srclib_dir destlib_dir\";\n}\n\n# Runs objdump on the executable, finds all unresolved dependencies, and\n#  then copies each of those libraries from srclib_dir to destlib_dir\n# destlib_dir should be APPLICATION/Contents/Frameworks/\n\nmy $exe = shift;\nmy $srcdir = shift;\nmy $destdir = shift;\nif(! -f $exe || ! -d $srcdir || ! -d $destdir) {\n\tdie \"$exe is not a file, or $srcdir or $destdir are not a dir\";\n}\n\nmy $do_swiftos = 0;\nopen(RPATHS, \"objdump -macho -dylibs-used $exe|\") or die \"Open failed: $!\";\nmy $line;\nmy $lib;\nforeach $line (<RPATHS>) {\n\tchomp($line);\n\tif($line =~ m:\\@rpath/([^ ]*) :) {\n\t\t$lib = $1;\n\t\tprint \"lib: $lib\\n\";\n\t\t`cp $srcdir/$lib $destdir/`;\n\t}\n\t$do_swiftos = 1;\n}\n\n# And copy libswiftos.dylib if we copied any files\nif($do_swiftos) {\n\t`cp $srcdir/libswiftos.dylib $destdir/`;\n}\n"
  },
  {
    "path": "upstream/kegs/src/debugger.c",
    "content": "const char rcsid_debugger_c[] = \"@(#)$KmKId: debugger.c,v 1.60 2023-09-11 12:55:28+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n\n#include <stdio.h>\n#include <stdarg.h>\n#include \"defc.h\"\n\n#include \"disas.h\"\n\n#define LINE_SIZE\t\t160\t\t/* Input buffer size */\n#define PRINTF_BUF_SIZE\t\t239\n#define DEBUG_ENTRY_MAX_CHARS\t80\n\nSTRUCT(Debug_entry) {\n\tbyte str_buf[DEBUG_ENTRY_MAX_CHARS];\n};\n\nchar g_debug_printf_buf[PRINTF_BUF_SIZE];\nchar g_debug_stage_buf[PRINTF_BUF_SIZE];\nint\tg_debug_stage_pos = 0;\n\nDebug_entry *g_debug_lines_ptr = 0;\nint\tg_debug_lines_total = 0;\nint\tg_debug_lines_pos = 0;\nint\tg_debug_lines_alloc = 0;\nint\tg_debug_lines_max = 1024*1024;\nint\tg_debug_lines_view = -1;\nint\tg_debug_lines_viewable_lines = 20;\nint\tg_debugwin_changed = 1;\nint\tg_debug_to_stdout = 1;\n\nextern byte *g_memory_ptr;\nextern byte *g_slow_memory_ptr;\nextern int g_halt_sim;\nextern word32 g_c068_statereg;\nextern word32 stop_run_at;\nextern int Verbose;\nextern int Halt_on;\nextern int g_a2_key_to_ascii[][4];\nextern Kimage g_debugwin_kimage;\n\nextern int g_config_control_panel;\nextern word32 g_mem_size_total;\n\nextern char *g_sound_file_str;\nextern word32 g_sound_file_bytes;\n\nint\tg_num_breakpoints = 0;\nBreak_point g_break_pts[MAX_BREAK_POINTS];\n\nextern int g_irq_pending;\n\nextern dword64 g_last_vbl_dcyc;\nextern int g_ret1;\nextern Engine_reg engine;\nextern dword64 g_dcycles_end;\n\nint g_stepping = 0;\n\nword32\tg_list_kpc;\nint\tg_hex_line_len = 0x10;\nword32\tg_a1 = 0;\nword32\tg_a2 = 0;\nword32\tg_a3 = 0;\nword32\tg_a4 = 0;\nword32\tg_a1bank = 0;\nword32\tg_a2bank = 0;\nword32\tg_a3bank = 0;\nword32\tg_a4bank = 0;\n\n#define\tMAX_CMD_BUFFER\t\t229\n\n#define PC_LOG_LEN\t\t(2*1024*1024)\n\nPc_log g_pc_log_array[PC_LOG_LEN + 2];\nData_log g_data_log_array[PC_LOG_LEN + 2];\n\nword32\tg_log_pc_enable = 0;\nPc_log\t*g_log_pc_ptr = &(g_pc_log_array[0]);\nPc_log\t*g_log_pc_start_ptr = &(g_pc_log_array[0]);\nPc_log\t*g_log_pc_end_ptr = &(g_pc_log_array[PC_LOG_LEN]);\n\nData_log *g_log_data_ptr = &(g_data_log_array[0]);\nData_log *g_log_data_start_ptr = &(g_data_log_array[0]);\nData_log *g_log_data_end_ptr = &(g_data_log_array[PC_LOG_LEN]);\n\nchar\tg_cmd_buffer[MAX_CMD_BUFFER + 2] = { 0 };\nint\tg_cmd_buffer_len = 2;\n\n#define MAX_DISAS_BUF\t\t150\nchar g_disas_buffer[MAX_DISAS_BUF];\n\nvoid\ndebugger_init()\n{\n\tdebugger_help();\n\tg_list_kpc = engine.kpc;\n#if 0\n\tif(g_num_breakpoints == 0) {\n\t\tset_bp(0xff5a0e, 0xff5a0e, 4);\n\t\tset_bp(0x00c50a, 0x00c50a, 4);\n\t\tset_bp(0x00c50d, 0x00c50d, 4);\n\t}\n#endif\n}\n\nint g_dbg_new_halt = 0;\n\nvoid\ncheck_breakpoints(word32 addr, dword64 dfcyc, word32 maybe_stack,\n\t\t\t\t\t\t\tword32 type)\n{\n\tBreak_point *bp_ptr;\n\tint\tcount;\n\tint\ti;\n\n\tcount = g_num_breakpoints;\n\tfor(i = 0; i < count; i++) {\n\t\tbp_ptr = &(g_break_pts[i]);\n\t\tif((type & bp_ptr->acc_type) == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tif((addr >= (bp_ptr->start_addr & 0xffffff)) &&\n\t\t\t\t(addr <= (bp_ptr->end_addr & 0xffffff))) {\n\t\t\tdebug_hit_bp(addr, dfcyc, maybe_stack, type, i);\n\t\t}\n\t}\n\n\tif((type == 4) && ((addr == 0xe10000) || (addr == 0xe10004))) {\n\t\tFINISH(RET_TOOLTRACE, 0);\n\t}\n}\n\nvoid\ndebug_hit_bp(word32 addr, dword64 dfcyc, word32 maybe_stack, word32 type,\n\t\t\t\t\t\t\t\tint pos)\n{\n\tword32\ttrk_side, side, trk, cmd, unit, buf, blk, param_cnt, list_ptr;\n\tword32\tstatus_code, cmd_list, stack, rts;\n\n\tif((addr == 0xff5a0e) && (type == 4)) {\n\t\ttrk_side = get_memory_c(0xe10f32);\n\t\tside = (trk_side >> 5) & 1;\n\t\ttrk = get_memory_c(0xe10f34) + ((trk_side & 0x1f) << 6);\n\t\tbuf = get_memory_c(0x42) | (get_memory_c(0x43) << 8) |\n\t\t\t\t\t\t(get_memory_c(0x44) << 16);\n\t\tprintf(\"ff5a0e: 3.5 read of track %03x side:%d sector:%03x to \"\n\t\t\t\"%06x at %016llx\\n\", trk, side, get_memory_c(0xe10f33),\n\t\t\tbuf, dfcyc);\n\t\treturn;\n\t}\n\tif((addr == 0x00c50a) && (type == 4)) {\n\t\tcmd = get_memory_c(0x42);\n\t\tunit = get_memory_c(0x43);\n\t\tbuf = get_memory_c(0x44) | (get_memory_c(0x45) << 8);\n\t\tblk = get_memory_c(0x46) | (get_memory_c(0x47) << 8);\n\t\tprintf(\"00c50a: cmd %02x u:%02x buf:%04x blk:%04x at %016llx\\n\",\n\t\t\tcmd, unit, buf, blk, dfcyc);\n\t\treturn;\n\t}\n\tif((addr == 0x00c50d) && (type == 4)) {\n\t\tstack = maybe_stack & 0xffff;\n\t\trts = get_memory_c(stack + 1) | (get_memory_c(stack + 2) << 8);\n\t\tcmd = get_memory_c(rts + 1);\n\t\tcmd_list = get_memory_c(rts + 2) | (get_memory_c(rts+3) << 8);\n\t\tparam_cnt = get_memory_c(cmd_list);\n\t\tunit = get_memory_c(cmd_list + 1);\n\t\tlist_ptr = get_memory_c(cmd_list + 2) |\n\t\t\t\t\t(get_memory_c(cmd_list + 3) << 8);\n\t\tstatus_code = get_memory_c(cmd_list + 4);\n\t\tprintf(\"00c50d: stack:%04x rts:%04x cmd:%02x cmd_list:%04x \"\n\t\t\t\"param_cnt:%02x unit:%02x listptr:%04x \"\n\t\t\t\"status:%02x at %016llx\\n\", stack, rts, cmd, cmd_list,\n\t\t\tparam_cnt, unit, list_ptr, status_code, dfcyc);\n\t\tprintf(\"  list_ptr: %04x: %02x %02x %02x %02x %02x %02x %02x\\n\",\n\t\t\tlist_ptr, get_memory_c(list_ptr),\n\t\t\tget_memory_c(list_ptr + 1), get_memory_c(list_ptr + 2),\n\t\t\tget_memory_c(list_ptr + 3), get_memory_c(list_ptr + 4),\n\t\t\tget_memory_c(list_ptr + 5), get_memory_c(list_ptr + 6));\n\t\treturn;\n\t}\n\n\tdbg_log_info(dfcyc, addr, pos, 0x6270);\n\thalt2_printf(\"Hit breakpoint at %06x\\n\", addr);\n}\n\nint\ndebugger_run_16ms()\n{\n\t// Called when g_halt_sim is set\n\tif(g_dbg_new_halt) {\n\t\tg_list_kpc = engine.kpc;\n\t\tshow_regs();\n\t}\n\tg_dbg_new_halt = 0;\n\tadb_nonmain_check();\n\t// printf(\"debugger_run_16ms: g_halt_sim:%d\\n\", g_halt_sim);\n\treturn 0;\n}\n\nvoid\ndbg_log_info(dword64 dfcyc, word32 info1, word32 info2, word32 type)\n{\n\tif(dfcyc == 0) {\n\t\treturn;\t\t// Ignore some IWM t:00e7 events and others\n\t}\n\tg_log_data_ptr->dfcyc = dfcyc;\n\tg_log_data_ptr->stat = 0;\n\tg_log_data_ptr->addr = info1;\n\tg_log_data_ptr->val = info2;\n\tg_log_data_ptr->size = type;\t\t// type must be > 4\n\tg_log_data_ptr++;\n\tif(g_log_data_ptr >= g_log_data_end_ptr) {\n\t\tg_log_data_ptr = g_log_data_start_ptr;\n\t}\n}\n\nvoid\ndebugger_update_list_kpc()\n{\n\tg_dbg_new_halt = 1;\n}\n\nvoid\ndebugger_key_event(Kimage *kimage_ptr, int a2code, int is_up)\n{\n\tword32\tc025_val, special;\n\tint\tkey, pos, changed;\n\n\tpos = 1;\n\n\tc025_val = kimage_ptr->c025_val;\n\n\tif(c025_val & 1) {\t\t// Shift is down\n\t\tpos = 2;\n\t} else if(c025_val & 4) {\t// Capslock is down\n\t\tkey = g_a2_key_to_ascii[a2code][1];\n\t\tif((key >= 'a') && (key <= 'z')) {\n\t\t\tpos = 2;\t\t// CAPS LOCK on\n\t\t}\n\t}\n\tif(c025_val & 2) {\t\t// Ctrl is down\n\t\tpos = 3;\n\t}\n\tkey = g_a2_key_to_ascii[a2code][pos];\n\tif(key < 0) {\n\t\treturn;\n\t}\n\tspecial = (key >> 8) & 0xff;\t\t// c025 changes\n\tif(is_up) {\n\t\tc025_val = c025_val & (~special);\n\t} else {\n\t\tc025_val = c025_val | special;\n\t}\n\tkimage_ptr->c025_val = c025_val;\n\tif(is_up) {\n\t\treturn;\t\t// Nothing else to do\n\t}\n\tif(key >= 0x80) {\n\t\t// printf(\"key: %04x\\n\", key);\n\t\tif(key == 0x8007) {\t\t\t// F7 - close debugger\n\t\t\tvideo_set_active(kimage_ptr, !kimage_ptr->active);\n\t\t\tprintf(\"Toggled debugger window to:%d\\n\",\n\t\t\t\t\t\tkimage_ptr->active);\n\t\t}\n\t\tif((key & 0xff) == 0x74) {\t\t// Page up keycode\n\t\t\tdebugger_page_updown(1);\n\t\t}\n\t\tif((key & 0xff) == 0x79) {\t\t// Page down keycode\n\t\t\tdebugger_page_updown(-1);\n\t\t}\n\t\treturn;\n\t}\n\tpos = g_cmd_buffer_len;\n\tchanged = 0;\n\tif((key >= 0x20) && (key < 0x7f)) {\n\t\t// printable character, add it\n\t\tif(pos < MAX_CMD_BUFFER) {\n\t\t\t// printf(\"cmd[%d]=%c\\n\", pos, key);\n\t\t\tg_cmd_buffer[pos++] = key;\n\t\t\tchanged = 1;\n\t\t}\n\t} else if((key == 0x08) || (key == 0x7f)) {\n\t\t// Left arrow or backspace\n\t\tif(pos > 2) {\n\t\t\tpos--;\n\t\t\tchanged = 1;\n\t\t}\n\t} else if((key == 0x0d) || (key == 0x0a)) {\n\t\t//dbg_printf(\"Did return, pos:%d, str:%s\\n\", pos, g_cmd_buffer);\n\t\tdo_debug_cmd(&g_cmd_buffer[2]);\n\t\tpos = 2;\n\t\tchanged = 1;\n\t} else {\n\t\t// printf(\"ctrl key:%04x\\n\", key);\n\t}\n\tg_cmd_buffer[pos] = 0;\n\tg_cmd_buffer_len = pos;\n\tg_debug_lines_view = -1;\n\tg_debugwin_changed |= changed;\n\t// printf(\"g_cmd_buffer: %s\\n\", g_cmd_buffer);\n}\n\nvoid\ndebugger_page_updown(int isup)\n{\n\tint\tview, max;\n\n\tview = g_debug_lines_view;\n\tif(view < 0) {\n\t\tview = 0;\n\t}\n\tview = view + (isup*g_debug_lines_viewable_lines);\n\tif(view < 0) {\n\t\tview = -1;\n\t}\n\tmax = g_debug_lines_pos;\n\tif(g_debug_lines_alloc >= g_debug_lines_max) {\n\t\tmax = g_debug_lines_alloc - 4;\n\t}\n\tview = MY_MIN(view, max - g_debug_lines_viewable_lines);\n\n\t// printf(\"new view:%d, was:%d\\n\", view, g_debug_lines_view);\n\tif(view != g_debug_lines_view) {\n\t\tg_debug_lines_view = view;\n\t\tg_debugwin_changed++;\n\t}\n}\n\nvoid\ndebugger_redraw_screen(Kimage *kimage_ptr)\n{\n\tint\tline, vid_line, back, border_top, save_pos, num, lines_done;\n\tint\tsave_view, save_to_stdout;\n\tint\ti;\n\n\tif((g_debugwin_changed == 0) || (kimage_ptr->active == 0)) {\n\t\treturn;\t\t\t\t// Nothing to do\n\t}\n\n\tsave_pos = g_debug_lines_pos;\n\tsave_view = g_debug_lines_view;\n\t// printf(\"DEBUGGER drawing SCREEN!\\n\");\n\tg_cmd_buffer[0] = '>';\n\tg_cmd_buffer[1] = ' ';\n\tg_cmd_buffer[g_cmd_buffer_len] = 0xa0;\t\t// Cursor: inverse space\n\tg_cmd_buffer[g_cmd_buffer_len+1] = 0;\n\tsave_to_stdout = g_debug_to_stdout;\n\tg_debug_to_stdout = 0;\n\tdbg_printf(\"%s\\n\", &g_cmd_buffer[0]);\n\tg_cmd_buffer[g_cmd_buffer_len] = 0;\n\tdbg_printf(\"g_halt_sim:%02x\\n\", g_halt_sim);\n\tborder_top = 8;\n\tg_debug_to_stdout = save_to_stdout;\n\n\tvid_line = (((kimage_ptr->a2_height - 2*border_top) / 16) * 8) - 1;\n\tnum = g_debug_lines_pos - save_pos;\n\tif(num < 0) {\n\t\tnum = num + g_debug_lines_alloc;\n\t}\n\tif(num > 4) {\n\t\t// printf(\"num is > 4!\\n\");\n\t\tnum = 4;\n\t}\n\tfor(i = 0; i < num; i++) {\n\t\tline = debug_get_view_line(i);\n\t\tdebug_draw_debug_line(kimage_ptr, line, vid_line);\n\t\tvid_line -= 8;\n\t}\n\tg_debug_lines_pos = save_pos;\n\tg_debug_lines_view = save_view;\n\tback = save_view;\n\tif(back < 0) {\t\t\t// -1 means always show most recent\n\t\tback = 0;\n\t}\n\tlines_done = 0;\n\twhile(vid_line >= border_top) {\n\t\tline = debug_get_view_line(back);\n\t\tdebug_draw_debug_line(kimage_ptr, line, vid_line);\n\t\tback++;\n\t\tvid_line -= 8;\n\t\tlines_done++;\n#if 0\n\t\tprintf(\" did a line, line is now: %d after str:%s\\n\", line,\n\t\t\t\t\t\t\t\t\tstr);\n#endif\n\t}\n\tg_debug_lines_viewable_lines = lines_done;\n\tg_debugwin_changed = 0;\n\tkimage_ptr->x_refresh_needed = 1;\n\t// printf(\"x_refresh_needed = 1, viewable_lines:%d\\n\", lines_done);\n}\n\nvoid\ndebug_draw_debug_line(Kimage *kimage_ptr, int line, int vid_line)\n{\n\tword32\tline_bytes;\n\tint\ti;\n\n\t// printf(\"draw debug line:%d at vid_line:%d\\n\", line, vid_line);\n\tfor(i = 7; i >= 0; i--) {\n\t\tline_bytes = (vid_line << 16) | (40 << 8) | 0;\n\t\tredraw_changed_string(&(g_debug_lines_ptr[line].str_buf[0]),\n\t\t\tline_bytes, -1L, kimage_ptr->wptr + 8, 0, 0x00ffffff,\n\t\t\tkimage_ptr->a2_width_full, 1);\n\t\tvid_line--;\n\t}\n}\n\nDbg_longcmd g_debug_bp_clear[] = {\n\t{ \"all\",\tdebug_bp_clear_all,\t0,\n\t\t\t\t\t\"clear all breakpoints\" },\n\t{ 0, 0, 0, 0 }\n};\nDbg_longcmd g_debug_bp[] = {\n\t{ \"set\",\tdebug_bp_set,\t0,\n\t\t\t\t\t\"Set breakpoint: ADDR or ADDR0-ADDR1\" },\n\t{ \"clear\",\tdebug_bp_clear,\t&g_debug_bp_clear[0],\n\t\t\t\t\"Clear breakpoint: ADDR OR ADDR0-ADDR1\"},\n\t{ 0, 0, 0, 0 }\n};\n\nDbg_longcmd g_debug_logpc[] = {\n\t{ \"on\",\t\tdebug_logpc_on,\t0, \"Turn on logging of pc and data\" },\n\t{ \"off\",\tdebug_logpc_off,0, \"Turn off logging of pc and data\" },\n\t{ \"save\",\tdebug_logpc_save,0, \"logpc save FILE: save to file\" },\n\t{ 0, 0, 0, 0 }\n};\n\nDbg_longcmd g_debug_iwm[] = {\n\t{ \"check\",\tdebug_iwm_check, 0, \"Denibblize current track\" },\n\t{ 0, 0, 0, 0 }\n};\n\n// Main table of commands\nDbg_longcmd g_debug_longcmds[] = {\n\t{ \"help\",\tdebug_help,\t0,\t\"Help\" },\n\t{ \"bp\",\t\tdebug_bp,\t&g_debug_bp[0],\n\t\t\t\t\t\"bp ADDR: sets breakpoint on addr\" },\n\t{ \"logpc\",\tdebug_logpc,\t&g_debug_logpc[0], \"Log PC\" },\n\t{ \"iwm\",\tdebug_iwm,\t&g_debug_iwm[0], \"IWM\" },\n\t{ \"soundfile\",\tdebug_soundfile, 0, \"Save sound to a WAV file\" },\n\t{ 0, 0, 0, 0 }\n};\n\nvoid\ndebugger_help()\n{\n\tdbg_printf(\"KEGS Debugger help (courtesy Fredric Devernay\\n\");\n\tdbg_printf(\"General command syntax: [bank]/[address][command]\\n\");\n\tdbg_printf(\"e.g. 'e1/0010B' to set a breakpoint at the interrupt jump \"\n\t\t\t\t\t\t\t\t\"pt\\n\");\n\tdbg_printf(\"Enter all addresses using lower-case\\n\");\n\tdbg_printf(\"As with the IIgs monitor, you can omit the bank number \"\n\t\t\t\t\t\t\t\t\"after\\n\");\n\tdbg_printf(\"having set it: 'e1/0010B' followed by '14B' will set\\n\");\n\tdbg_printf(\"breakpoints at e1/0010 and e1/0014\\n\");\n\tdbg_printf(\"\\n\");\n\tdbg_printf(\"g                       Go\\n\");\n\tdbg_printf(\"[bank]/[addr]g          Go from [bank]/[address]\\n\");\n\tdbg_printf(\"s                       Step one instruction\\n\");\n\tdbg_printf(\"[bank]/[addr]s          Step one instr at [bank]/[addr]\\n\");\n\tdbg_printf(\"[bank]/[addr]B          Set breakpoint at [bank]/[addr]\\n\");\n\tdbg_printf(\"B                       Show all breakpoints\\n\");\n\tdbg_printf(\"[bank]/[addr]D          Delete breakpoint at [bank]/\"\n\t\t\t\t\t\t\t\t\"[addr]\\n\");\n\tdbg_printf(\"[bank]/[addr1].[addr2]  View memory\\n\");\n\tdbg_printf(\"[bank]/[addr]L          Disassemble memory\\n\");\n\n\tdbg_printf(\"Z                       Dump SCC state\\n\");\n\tdbg_printf(\"I                       Dump IWM state\\n\");\n\tdbg_printf(\"[drive].[track]I        Dump IWM state\\n\");\n\tdbg_printf(\"E                       Dump Ensoniq state\\n\");\n\tdbg_printf(\"[osc]E                  Dump oscillator [osc] state\\n\");\n\tdbg_printf(\"R                       Dump dtime array and events\\n\");\n\tdbg_printf(\"T                       Show toolbox log\\n\");\n\tdbg_printf(\"[bank]/[addr]T          Dump tools using ptr [bank]/\"\n\t\t\t\t\t\t\t\t\"[addr]\\n\");\n\tdbg_printf(\"                            as 'tool_set_info'\\n\");\n\tdbg_printf(\"[mode]V                 XOR verbose with 1=DISK, 2=IRQ,\\n\");\n\tdbg_printf(\"                         4=CLK,8=SHADOW,10=IWM,20=DOC,\\n\");\n\tdbg_printf(\"                         40=ABD,80=SCC, 100=TEST, 200=\"\n\t\t\t\t\t\t\t\t\"VIDEO\\n\");\n\tdbg_printf(\"[mode]H                 XOR halt_on with 1=SCAN_INT,\\n\");\n\tdbg_printf(\"                         2=IRQ, 4=SHADOW_REG, 8=\"\n\t\t\t\t\t\t\t\"C70D_WRITES\\n\");\n\tdbg_printf(\"r                       Reset\\n\");\n\tdbg_printf(\"[0/1]=m                 Changes m bit for l listings\\n\");\n\tdbg_printf(\"[0/1]=x                 Changes x bit for l listings\\n\");\n\tdbg_printf(\"S                       show_bankptr_bank0 & smartport \"\n\t\t\t\t\t\t\t\t\"errs\\n\");\n\tdbg_printf(\"P                       show_pmhz\\n\");\n\tdbg_printf(\"A                       show_a2_line_stuff show_adb_log\\n\");\n\tdbg_printf(\"Ctrl-e                  Dump registers\\n\");\n\tdbg_printf(\"[bank]/[addr1].[addr2]us[file]  Save mem area to [file]\\n\");\n\tdbg_printf(\"[bank]/[addr1].[addr2]ul[file]  Load mem area from \"\n\t\t\t\t\t\t\t\t\"[file]\\n\");\n\tdbg_printf(\"v                       Show video information\\n\");\n\tdbg_printf(\"q                       Exit Debugger (and KEGS)\\n\");\n}\n\nvoid\ndbg_help_show_strs(int help_depth, const char *str, const char *help_str)\n{\n\tconst char *blank_str, *pre_str, *post_str;\n\tint\tcolumn, len, blank_len, pre_len, post_len;\n\n\t// Indent by 3*help_depth chars, then output str, then hit\n\t//  column 14, then output help_str.  This can be done in just 2-3\n\t//  lines, but I made it longer and clearer to avoid any \"overflow\"\n\t//  cases\n\tif(help_str == 0) {\n\t\treturn;\n\t}\n\tblank_str = \"        \" \"        \" \"        \";\n\tblank_len = (int)strlen(blank_str);\t\t// should be >=17\n\tcolumn = 17;\n\tlen = (int)strlen(str);\n\tif(help_depth < 0) {\n\t\thelp_depth = 0;\n\t}\n\tpre_str = blank_str;\n\tpre_len = 3 * help_depth;\n\tif(pre_len < blank_len) {\n\t\tpre_str = blank_str + blank_len - pre_len;\n\t}\n\tpost_str = \"\";\n\tpost_len = column - pre_len - len;\n\tif((post_len >= 1) && (post_len < blank_len)) {\n\t\tpost_str = blank_str + blank_len - post_len;\n\t}\n\tdbg_printf(\"%s%s%s: %s\\n\", pre_str, str, post_str, help_str);\n}\n\nconst char *\ndebug_find_cmd_in_table(const char *line_ptr, Dbg_longcmd *longptr,\n\t\t\t\t\t\t\tint help_depth)\n{\n\tDbg_fn\t*fnptr;\n\tDbg_longcmd *subptr;\n\tconst char *str, *newstr;\n\tint\tlen, c;\n\tint\ti;\n\n\t// See if the command is from the longcmd list\n\twhile(*line_ptr == ' ') {\n\t\tline_ptr++;\t\t// eat spaces\n\t}\n\t// Output \"   str     :\" where : is at column 14 always\n\t// printf(\"dfcit: %s, help_depth:%d\\n\", line_ptr, help_depth);\n\tfor(i = 0; i < 1000; i++) {\n\t\t// Provide a limit to avoid hang if table not terminated right\n\t\tstr = longptr[i].str;\n\t\tfnptr = longptr[i].fnptr;\n\t\tif(!str) {\t\t\t// End of table\n\t\t\tbreak;\t\t\t// No match found\n\t\t}\n\t\tif(help_depth < 0) {\n\t\t\t// Print the help string for all entries in this table\n\t\t\tdbg_help_show_strs(-1 - help_depth, str,\n\t\t\t\t\t\t\tlongptr[i].help_str);\n\t\t\tcontinue;\n\t\t}\n\t\tlen = (int)strlen(str);\n\t\tif(strncmp(line_ptr, str, len) != 0) {\n\t\t\tcontinue;\t\t// Not a match\n\t\t}\n\t\t// Ensure next char is either a space, or 0\n\t\t// Let's us avoid commands which are prefixes, or\n\t\t//  which are old Apple II monitor hex+commands\n\t\tc = line_ptr[len];\n\t\tif((c != 0) && (c != ' ')) {\n\t\t\tcontinue;\t\t// Not valid\n\t\t}\n\t\tif(help_depth) {\n\t\t\tdbg_help_show_strs(help_depth, str,\n\t\t\t\t\t\t\tlongptr[i].help_str);\n\t\t}\n\t\tsubptr = longptr[i].subptr;\n\t\t// Try a subcmd first\n\t\tnewstr = line_ptr + len;\n\t\tif(subptr != 0) {\n\t\t\tif(help_depth) {\n\t\t\t\thelp_depth++;\n\t\t\t}\n\t\t\tnewstr = debug_find_cmd_in_table(newstr, subptr,\n\t\t\t\t\t\t\t\thelp_depth);\n\t\t\t// If a subcmd was found, newstr is now 0\n\t\t}\n\t\tif((newstr == 0) || help_depth) {\n\t\t\treturn 0;\n\t\t}\n\t\tif((newstr != 0) && (fnptr != 0)) {\n\t\t\t(*fnptr)(line_ptr + len);\n\t\t\treturn 0;\t\t// Success\n\t\t}\n\t}\n\tif(help_depth >= 1) {\n\t\t// No subcommands found, print out all entries in this table\n\t\tdebug_find_cmd_in_table(line_ptr, longptr, -1 - help_depth);\n\t\treturn 0;\n\t}\n\treturn line_ptr;\n}\n\nvoid\ndo_debug_cmd(const char *in_str)\n{\n\tconst char *line_ptr;\n\tconst char *newstr;\n\tint\tslot_drive, track, ret_val, mode, old_mode, got_num;\n\tint\tsave_to_stdout;\n\n\tmode = 0;\n\told_mode = 0;\n\n\tsave_to_stdout = g_debug_to_stdout;\n\tg_debug_to_stdout = 1;\n\tdbg_printf(\"*%s\\n\", in_str);\n\tline_ptr = in_str;\n\n\t// See if the command is from the longcmd list\n\tnewstr = debug_find_cmd_in_table(in_str, &(g_debug_longcmds[0]), 0);\n\tif(newstr == 0) {\n\t\tg_debug_to_stdout = save_to_stdout;\n\t\treturn;\t\t\t// Command found get out\n\t}\n\n\t// If we get here, parse an Apple II monitor like command:\n\t//  {address}{cmd} repeat.\n\twhile(1) {\n\t\tret_val = 0;\n\t\tg_a2 = 0;\n\t\tgot_num = 0;\n\t\twhile(1) {\n\t\t\tif((mode == 0) && (got_num != 0)) {\n\t\t\t\tg_a3 = g_a2;\n\t\t\t\tg_a3bank = g_a2bank;\n\t\t\t\tg_a1 = g_a2;\n\t\t\t\tg_a1bank = g_a2bank;\n\t\t\t}\n\t\t\tret_val = *line_ptr++ & 0x7f;\n\t\t\tif((ret_val >= '0') && (ret_val <= '9')) {\n\t\t\t\tg_a2 = (g_a2 << 4) + ret_val - '0';\n\t\t\t\tgot_num = 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif((ret_val >= 'a') && (ret_val <= 'f')) {\n\t\t\t\tg_a2 = (g_a2 << 4) + ret_val - 'a' + 10;\n\t\t\t\tgot_num = 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(ret_val == '/') {\n\t\t\t\tg_a2bank = g_a2;\n\t\t\t\tg_a2 = 0;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\told_mode = mode;\n\t\tmode = 0;\n\t\tswitch(ret_val) {\n\t\tcase 'h':\n\t\t\tdebugger_help();\n\t\t\tbreak;\n\t\tcase 'R':\n\t\t\tshow_dtime_array();\n\t\t\tshow_all_events();\n\t\t\tbreak;\n\t\tcase 'I':\n\t\t\tslot_drive = -1;\n\t\t\ttrack = -1;\n\t\t\tif(got_num) {\n\t\t\t\tif(old_mode == '.') {\n\t\t\t\t\tslot_drive = g_a1;\n\t\t\t\t}\n\t\t\t\ttrack = g_a2;\n\t\t\t}\n\t\t\tiwm_show_track(slot_drive, track, 0);\n\t\t\tiwm_show_stats(slot_drive);\n\t\t\tbreak;\n\t\tcase 'E':\n\t\t\tdoc_show_ensoniq_state();\n\t\t\tbreak;\n\t\tcase 'T':\n\t\t\tif(got_num) {\n\t\t\t\tshow_toolset_tables(g_a2bank, g_a2);\n\t\t\t} else {\n\t\t\t\tshow_toolbox_log();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'v':\n\t\t\tif(got_num) {\n\t\t\t\tdis_do_compare();\n\t\t\t} else {\n\t\t\t\tvideo_show_debug_info();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'V':\n\t\t\tdbg_printf(\"g_irq_pending: %05x\\n\", g_irq_pending);\n\t\t\tdbg_printf(\"Setting Verbose ^= %04x\\n\", g_a1);\n\t\t\tVerbose ^= g_a1;\n\t\t\tdbg_printf(\"Verbose is now: %04x\\n\", Verbose);\n\t\t\tbreak;\n\t\tcase 'H':\n\t\t\tdbg_printf(\"Setting Halt_on ^= %04x\\n\", g_a1);\n\t\t\tHalt_on ^= g_a1;\n\t\t\tdbg_printf(\"Halt_on is now: %04x\\n\", Halt_on);\n\t\t\tbreak;\n\t\tcase 'r':\n\t\t\tdo_reset();\n\t\t\tg_list_kpc = engine.kpc;\n\t\t\tbreak;\n\t\tcase 'm':\n\t\t\tif(old_mode == '=') {\n\t\t\t\tif(!g_a1) {\n\t\t\t\t\tengine.psr &= ~0x20;\n\t\t\t\t} else {\n\t\t\t\t\tengine.psr |= 0x20;\n\t\t\t\t}\n\t\t\t\tif(engine.psr & 0x100) {\n\t\t\t\t\tengine.psr |= 0x30;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdis_do_memmove();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t\tdis_do_pattern_search();\n\t\t\tbreak;\n\t\tcase 'x':\n\t\t\tif(old_mode == '=') {\n\t\t\t\tif(!g_a1) {\n\t\t\t\t\tengine.psr &= ~0x10;\n\t\t\t\t} else {\n\t\t\t\t\tengine.psr |= 0x10;\n\t\t\t\t}\n\t\t\t\tif(engine.psr & 0x100) {\n\t\t\t\t\tengine.psr |= 0x30;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'z':\n\t\t\tif(old_mode == '=') {\n\t\t\t\tstop_run_at = g_a1;\n\t\t\t\tdbg_printf(\"Calling add_event for t:%08x\\n\",\n\t\t\t\t\t\t\t\t\tg_a1);\n\t\t\t\tadd_event_stop(((dword64)g_a1) << 16);\n\t\t\t\tdbg_printf(\"set stop_run_at = %x\\n\", g_a1);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'l': case 'L':\n\t\t\tif(got_num) {\n\t\t\t\tg_list_kpc = (g_a2bank << 16) + (g_a2 & 0xffff);\n\t\t\t}\n\t\t\tdo_debug_list();\n\t\t\tbreak;\n\t\tcase 'Z':\n\t\t\tshow_scc_state();\n\t\t\tbreak;\n\t\tcase 'S':\n\t\t\tshow_bankptrs_bank0rdwr();\n\t\t\tsmartport_error();\n\t\t\tbreak;\n\t\tcase 'M':\n\t\t\tshow_pmhz();\n\t\t\tmockingboard_show(got_num, g_a1);\n\t\t\tbreak;\n\t\tcase 'A':\n\t\t\tshow_a2_line_stuff();\n\t\t\tshow_adb_log();\n\t\t\tbreak;\n\t\tcase 's':\n\t\t\tg_stepping = 1;\n\t\t\tif(got_num) {\n\t\t\t\tengine.kpc = (g_a2bank << 16) + (g_a2 & 0xffff);\n\t\t\t}\n\t\t\tmode = 's';\n\t\t\tg_list_kpc = engine.kpc;\n\t\t\tbreak;\n\t\tcase 'B':\n\t\t\tif(got_num) {\n\t\t\t\tdbg_printf(\"got_num:%d, a2bank:%x, g_a2:%x\\n\",\n\t\t\t\t\t\tgot_num, g_a2bank, g_a2);\n\t\t\t\tset_bp((g_a2bank << 16) + g_a2,\n\t\t\t\t\t\t(g_a2bank << 16) + g_a2, 4);\n\t\t\t} else {\n\t\t\t\tshow_bp();\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'D':\n\t\t\tif(got_num) {\n\t\t\t\tdbg_printf(\"got_num: %d, a2bank: %x, a2: %x\\n\",\n\t\t\t\t\t\tgot_num, g_a2bank, g_a2);\n\t\t\t\tdelete_bp((g_a2bank << 16) + g_a2,\n\t\t\t\t\t\t(g_a2bank << 16) + g_a2);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'g':\n\t\tcase 'G':\n\t\t\tdbg_printf(\"Going..\\n\");\n\t\t\tg_stepping = 0;\n\t\t\tif(got_num) {\n\t\t\t\tengine.kpc = (g_a2bank << 16) + (g_a2 & 0xffff);\n\t\t\t}\n\t\t\tdo_go();\n\t\t\tg_list_kpc = engine.kpc;\n\t\t\tbreak;\n\t\tcase 'u':\n\t\t\tdbg_printf(\"Unix commands\\n\");\n\t\t\tline_ptr = do_debug_unix(line_ptr, old_mode);\n\t\t\tbreak;\n\t\tcase ':': case '.':\n\t\tcase '+': case '-':\n\t\tcase '=': case ',':\n\t\t\tmode = ret_val;\n\t\t\tdbg_printf(\"Setting mode = %x\\n\", mode);\n\t\t\tbreak;\n\t\tcase ' ': case '\\t':\n\t\t\tif(!got_num) {\n\t\t\t\tmode = old_mode;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmode = do_blank(mode, old_mode);\n\t\t\tbreak;\n\t\tcase '<':\n\t\t\tg_a4 = g_a2;\n\t\t\tg_a4bank = g_a2bank;\n\t\t\tbreak;\n\t\tcase 0x05: /* ctrl-e */\n\t\tcase 'Q':\n\t\tcase 'q':\n\t\t\tshow_regs();\n\t\t\tbreak;\n\t\tcase 0:\t\t\t// The final null char\n\t\t\tif(old_mode == 's') {\n\t\t\t\tmode = do_blank(mode, old_mode);\n\t\t\t\tg_debug_to_stdout = save_to_stdout;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif(line_ptr == &in_str[1]) {\n\t\t\t\tg_a2 = g_a1 | (g_hex_line_len - 1);\n\t\t\t\tshow_hex_mem(g_a1bank, g_a1, g_a2, -1);\n\t\t\t\tg_a1 = g_a2 + 1;\n\t\t\t} else {\n\t\t\t\tif((got_num == 1) || (mode == 's')) {\n\t\t\t\t\tmode = do_blank(mode, old_mode);\n\t\t\t\t}\n\t\t\t}\n\t\t\tg_debug_to_stdout = save_to_stdout;\n\t\t\treturn;\t\t\t// Get out, all done\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tdbg_printf(\"\\nUnrecognized command: %s\\n\", in_str);\n\t\t\tg_debug_to_stdout = save_to_stdout;\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nword32\ndis_get_memory_ptr(word32 addr)\n{\n\tword32\ttmp1, tmp2, tmp3;\n\n\ttmp1 = get_memory_c(addr);\n\ttmp2 = get_memory_c(addr + 1);\n\ttmp3 = get_memory_c(addr + 2);\n\n\treturn (tmp3 << 16) + (tmp2 << 8) + tmp1;\n}\n\nvoid\nshow_one_toolset(FILE *toolfile, int toolnum, word32 addr)\n{\n\tword32\trout_addr;\n\tint\tnum_routs;\n\tint\ti;\n\n\tnum_routs = dis_get_memory_ptr(addr);\n\tfprintf(toolfile, \"Tool 0x%02x, table: 0x%06x, num_routs:%03x\\n\",\n\t\ttoolnum, addr, num_routs);\n\tif((addr < 0x10000) || (num_routs > 0x100)) {\n\t\tfprintf(toolfile, \"addr in page 0, or num_routs too large\\n\");\n\t\treturn;\n\t}\n\n\tfor(i = 1; i < num_routs; i++) {\n\t\trout_addr = dis_get_memory_ptr(addr + 4*i);\n\t\tfprintf(toolfile, \"%06x = %02x%02x\\n\", rout_addr, i, toolnum);\n\t}\n}\n\nvoid\nshow_toolset_tables(word32 a2bank, word32 addr)\n{\n\tFILE\t*toolfile;\n\tword32\ttool_addr;\n\tint\tnum_tools;\n\tint\ti;\n\n\taddr = (a2bank << 16) + (addr & 0xffff);\n\n\ttoolfile = fopen(\"tool_set_info\", \"w\");\n\tif(toolfile == 0) {\n\t\tfprintf(stderr, \"fopen of tool_set_info failed: %d\\n\", errno);\n\t\texit(2);\n\t}\n\n\tnum_tools = dis_get_memory_ptr(addr);\n\tfprintf(toolfile, \"There are 0x%02x tools using ptr at %06x\\n\",\n\t\t\tnum_tools, addr);\n\n\tif(num_tools > 40) {\n\t\tfprintf(toolfile, \"Too many tools, aborting\\n\");\n\t\tnum_tools = 0;\n\t}\n\tfor(i = 1; i < num_tools; i++) {\n\t\ttool_addr = dis_get_memory_ptr(addr + 4*i);\n\t\tshow_one_toolset(toolfile, i, tool_addr);\n\t}\n\n\tfclose(toolfile);\n}\n\nword32\ndebug_getnum(const char **str_ptr)\n{\n\tconst char *str;\n\tword32\tval;\n\tint\tc, got_num;\n\n\tstr = *str_ptr;\n\twhile(*str == ' ') {\n\t\tstr++;\n\t}\n\tgot_num = 0;\n\tval = 0;\n\twhile(1) {\n\t\tc = tolower(*str);\n\t\t//printf(\"got c:%02x %c val was %08x got_num:%d\\n\", c, c, val,\n\t\t//\t\t\t\t\t\tgot_num);\n\t\tif((c >= '0') && (c <= '9')) {\n\t\t\tval = (val << 4) + (c - '0');\n\t\t\tgot_num = 1;\n\t\t} else if((c >= 'a') && (c <= 'f')) {\n\t\t\tval = (val << 4) + 10 + (c - 'a');\n\t\t\tgot_num = 1;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t\tstr++;\n\t}\n\t*str_ptr = str;\n\tif(got_num) {\n\t\treturn val;\n\t}\n\treturn (word32)-1L;\n}\n\nchar *\ndebug_get_filename(const char **str_ptr)\n{\n\tconst char *str, *start_str;\n\tchar\t*new_str;\n\tint\tc, len;\n\n\t// Go to first whitespace (or end of str), then kegs_malloc_str()\n\t//  the string and copy to it\n\tstr = *str_ptr;\n\tstart_str = 0;\n\t//printf(\"get_filename, str now :%s:\\n\", str);\n\twhile(1) {\n\t\tc = *str++;\n\t\tif(c == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tif((c == ' ') || (c == '\\t') || (c == '\\n')) {\n\t\t\t//printf(\"c:%02x at str :%s: , start_str:%p\\n\", c, str,\n\t\t\t//\t\t\t\t\tstart_str);\n\t\t\tif(start_str) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\t// Else it's a valid char, set start_str if needed\n\t\tif(!start_str) {\n\t\t\tstart_str = str - 1;\n\t\t\t//printf(\"Got c:%02x, start_str :%s:\\n\", c, start_str);\n\t\t}\n\t}\n\tnew_str = 0;\n\tif(start_str) {\n\t\tlen = (int)(str - start_str);\n\t\tif(len > 1) {\n\t\t\tnew_str = malloc(len);\n\t\t\tmemcpy(new_str, start_str, len);\n\t\t\tnew_str[len - 1] = 0;\n\t\t}\n\t}\n\t*str_ptr = str;\n\treturn new_str;\n}\n\nvoid\ndebug_help(const char *str)\n{\n\tdbg_printf(\"Help:\\n\");\n\t(void)debug_find_cmd_in_table(str, &(g_debug_longcmds[0]), 1);\n}\n\nvoid\ndebug_bp(const char *str)\n{\n\t// bp without a following set/clear command.  Set a breakpoint if\n\t//  an address range follows, otherwise just print current breakpoints\n\tdebug_bp_setclr(str, 0);\n}\n\nvoid\ndebug_bp_set(const char *str)\n{\n\tdebug_bp_setclr(str, 1);\n}\n\nvoid\ndebug_bp_clear(const char *str)\n{\n\tdebug_bp_setclr(str, 2);\n}\n\nvoid\ndebug_bp_clear_all(const char *str)\n{\n\tif(str) {\n\t\t// Use str to avoid warning\n\t}\n\tif(g_num_breakpoints) {\n\t\tg_num_breakpoints = 0;\n\t\tsetup_pageinfo();\n\t\tdbg_printf(\"Deleted all breakpoints\\n\");\n\t}\n}\n\nvoid\ndebug_bp_setclr(const char *str, int is_set_clear)\n{\n\tword32\taddr, end_addr, acc_type;\n\n\tprintf(\"In debug_bp: %s\\n\", str);\n\n\taddr = debug_getnum(&str);\n\t// printf(\"getnum ret:%08x\\n\", addr);\n\tif(addr == (word32)-1L) {\t\t// No argument\n\t\tshow_bp();\n\t\treturn;\n\t}\n\tend_addr = addr;\n\tif(*str == '-') {\t\t// Range\n\t\tstr++;\n\t\tend_addr = debug_getnum(&str);\n\t\t// printf(\"end_addr is %08x\\n\", end_addr);\n\t\tif(end_addr == (word32)-1L) {\n\t\t\tend_addr = addr;\n\t\t}\n\t}\n\tacc_type = 4;\n\tacc_type = debug_getnum(&str);\n\tif(acc_type == (word32)-1L) {\n\t\tacc_type = 4;\t\t\t// Code breakpoint\n\t}\n\tif(is_set_clear == 2) {\t\t\t// clear\n\t\tdelete_bp(addr, end_addr);\n\t} else {\t\t\t\t// set, or nothing\n\t\tset_bp(addr, end_addr, acc_type);\n\t}\n}\n\nvoid\ndebug_soundfile(const char *cmd_str)\n{\n\tchar\t*str;\n\n\t// See if there's an argument\n\tstr = debug_get_filename(&cmd_str);\t// str=0 if no argument\n\tsound_file_start(str);\t\t\t// str==0 means close file\n}\n\nvoid\ndebug_logpc(const char *str)\n{\n\tif(str) {\n\t\t// Dummy use of argument\n\t}\n\tdbg_printf(\"logpc enable:%d, cur offset:%08lx\\n\", g_log_pc_enable,\n\t\t\t(long)(g_log_pc_ptr - g_log_pc_start_ptr));\n}\n\nvoid\ndebug_logpc_on(const char *str)\n{\n\tif(str) {\n\t\t// Dummy use of argument\n\t}\n\tg_log_pc_enable = 1;\n\tg_dcycles_end = 0;\n\tdbg_printf(\"Enabled logging of PC and data accesses\\n\");\n}\n\nvoid\ndebug_logpc_off(const char *str)\n{\n\tif(str) {\n\t\t// Dummy use of argument\n\t}\n\tg_log_pc_enable = 0;\n\tg_dcycles_end = 0;\n\tdbg_printf(\"Disabled logging of PC and data accesses\\n\");\n}\n\nvoid\ndebug_logpc_out_data(FILE *pcfile, Data_log *log_data_ptr, dword64 start_dcyc)\n{\n\tchar\t*str, *shadow_str;\n\tdword64\tlstat, offset64, offset64slow, addr64;\n\tword32\twstat, addr, size, val;\n\n\taddr = log_data_ptr->addr;\n\tlstat = (dword64)(log_data_ptr->stat);\n\twstat = lstat & 0xff;\n\taddr64 = lstat - wstat + (addr & 0xff);\n\toffset64 = addr64 - (dword64)&(g_memory_ptr[0]);\n\tstr = \"IO\";\n\tshadow_str = \"\";\n\tif((wstat & BANK_SHADOW) || (wstat & BANK_SHADOW2)) {\n\t\tshadow_str = \"SHADOWED\";\n\t}\n\tsize = log_data_ptr->size;\n\tif(size > 32) {\n\t\tfprintf(pcfile, \"INFO %08x %08x %04x t:%04x %lld.%02lld\\n\",\n\t\t\tlog_data_ptr->addr, log_data_ptr->val, size >> 16,\n\t\t\tsize & 0xffff, (log_data_ptr->dfcyc - start_dcyc)>>16,\n\t\t\t((log_data_ptr->dfcyc & 0xffff) * 100) >> 16);\n\t} else {\n\t\toffset64slow = addr64 - (dword64)&(g_slow_memory_ptr[0]);\n\t\tif(offset64 < g_mem_size_total) {\n\t\t\tstr = \"mem\";\n\t\t} else if(offset64slow < 0x20000) {\n\t\t\tstr = \"slow_mem\";\n\t\t\toffset64 = offset64slow;\n\t\t} else {\n\t\t\tstr = \"IO\";\n\t\t\toffset64 = offset64 & 0xff;\n\t\t}\n\t\tval = log_data_ptr->val;\n\t\tfprintf(pcfile, \"DATA set %06x = \", addr);\n\t\tif(size == 8) {\n\t\t\tfprintf(pcfile, \"%02x (8) \", val & 0xff);\n\t\t} else if(size == 16) {\n\t\t\tfprintf(pcfile, \"%04x (16) \", val & 0xffff);\n\t\t} else {\n\t\t\tfprintf(pcfile, \"%06x (%d) \", val, size);\n\t\t}\n\t\tfprintf(pcfile, \"%lld.%02lld, %s[%06llx] %s\\n\",\n\t\t\t\t(log_data_ptr->dfcyc - start_dcyc) >> 16,\n\t\t\t\t((log_data_ptr->dfcyc & 0xffff) * 100) >> 16,\n\t\t\t\tstr, offset64 & 0xffffffULL, shadow_str);\n\t}\n}\n\nData_log *\ndebug_show_data_info(FILE *pcfile, Data_log *log_data_ptr, dword64 base_dcyc,\n\t\t\tdword64 dfcyc, dword64 start_dcyc, int *data_wrap_ptr,\n\t\t\tint *count_ptr)\n{\n\n\twhile((*data_wrap_ptr < 2) && (log_data_ptr->dfcyc <= dfcyc) &&\n\t\t\t\t\t(log_data_ptr->dfcyc >= start_dcyc)) {\n\t\tif(*count_ptr >= PC_LOG_LEN) {\n\t\t\tbreak;\n\t\t}\n\t\tdebug_logpc_out_data(pcfile, log_data_ptr, base_dcyc);\n\t\tif(log_data_ptr->dfcyc == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tlog_data_ptr++;\n\t\t(*count_ptr)++;\n\t\tif(log_data_ptr >= g_log_data_end_ptr) {\n\t\t\tlog_data_ptr = g_log_data_start_ptr;\n\t\t\t(*data_wrap_ptr)++;\n\t\t}\n\t}\n\treturn log_data_ptr;\n}\n\nvoid\ndebug_logpc_save(const char *cmd_str)\n{\n\tFILE\t*pcfile;\n\tPc_log\t*log_pc_ptr;\n\tData_log *log_data_ptr;\n\tchar\t*str;\n\tdword64\tdfcyc, start_dcyc, base_dcyc, max_dcyc;\n\tword32\tinstr, psr, acc, xreg, yreg, stack, direct, dbank, kpc, num;\n\tint\tdata_wrap, accsize, xsize, abs_time, data_count;\n\tint\ti;\n\n\t// See if there's an argument\n\tnum = debug_getnum(&cmd_str);\n\tabs_time = 1;\n\tif(num != (word32)-1L) {\n\t\tdbg_printf(\"Doing relative time\\n\");\n\t\tabs_time = 0;\n\t}\n\n\tpcfile = fopen(\"logpc_out\", \"w\");\n\tif(pcfile == 0) {\n\t\tfprintf(stderr,\"fopen failed...errno: %d\\n\", errno);\n\t\texit(2);\n\t}\n\n\tlog_pc_ptr = g_log_pc_ptr;\n\tlog_data_ptr = g_log_data_ptr;\n#if 0\n\tprintf(\"debug_logpc_save called, log_pc_ptr:%p, %p,%p log_data_ptr:%p, \"\n\t\t\"%p,%p\\n\", log_pc_ptr, g_log_pc_start_ptr, g_log_pc_end_ptr,\n\t\tlog_data_ptr, g_log_data_start_ptr, g_log_data_end_ptr);\n#endif\n#if 0\n\tfprintf(pcfile, \"current pc_log_ptr: %p, start: %p, end: %p\\n\",\n\t\tlog_pc_ptr, g_log_pc_start_ptr, g_log_pc_end_ptr);\n#endif\n\n\t// See if we haven't filled buffer yet\n\tif(log_pc_ptr->dfcyc == 0) {\n\t\tlog_pc_ptr = g_log_pc_start_ptr;\n\t}\n\tif(log_data_ptr->dfcyc == 0) {\n\t\tlog_data_ptr = g_log_data_start_ptr;\n\t\tdata_wrap = 1;\n\t}\n\n\tstart_dcyc = log_pc_ptr->dfcyc;\n\t// Round to an exact usec\n\tstart_dcyc = (start_dcyc >> 16) << 16;\n\tbase_dcyc = start_dcyc;\n\tif(abs_time) {\n\t\tbase_dcyc = 0;\t\t\t\t// Show absolute time\n\t}\n\tdfcyc = start_dcyc;\n\n\tdata_wrap = 0;\n\tdata_count = 0;\n\t/* find first data entry */\n\twhile((data_wrap < 2) && (log_data_ptr->dfcyc < dfcyc)) {\n\t\tlog_data_ptr++;\n\t\tif(log_data_ptr >= g_log_data_end_ptr) {\n\t\t\tlog_data_ptr = g_log_data_start_ptr;\n\t\t\tdata_wrap++;\n\t\t}\n\t}\n\tfprintf(pcfile, \"start_dcyc: %016llx, first entry:%016llx\\n\",\n\t\t\t\t\t\tstart_dcyc, log_pc_ptr->dfcyc);\n\n\tdfcyc = start_dcyc;\n\tmax_dcyc = dfcyc;\n\tfor(i = 0; i < PC_LOG_LEN; i++) {\n\t\tdfcyc = log_pc_ptr->dfcyc;\n\t\tlog_data_ptr = debug_show_data_info(pcfile, log_data_ptr,\n\t\t\t\tbase_dcyc, dfcyc, start_dcyc,\n\t\t\t\t&data_wrap, &data_count);\n\t\tdbank = (log_pc_ptr->dbank_kpc >> 24) & 0xff;\n\t\tkpc = log_pc_ptr->dbank_kpc & 0xffffff;\n\t\tinstr = log_pc_ptr->instr;\n\t\tpsr = (log_pc_ptr->psr_acc >> 16) & 0xffff;\n\t\tacc = log_pc_ptr->psr_acc & 0xffff;\n\t\txreg = (log_pc_ptr->xreg_yreg >> 16) & 0xffff;\n\t\tyreg = log_pc_ptr->xreg_yreg & 0xffff;\n\t\tstack = (log_pc_ptr->stack_direct >> 16) & 0xffff;\n\t\tdirect = log_pc_ptr->stack_direct & 0xffff;\n\n\t\taccsize = 2;\n\t\txsize = 2;\n\t\tif(psr & 0x20) {\n\t\t\taccsize = 1;\n\t\t}\n\t\tif(psr & 0x10) {\n\t\t\txsize = 1;\n\t\t}\n\n\t\tstr = do_dis(kpc, accsize, xsize, 1, instr, 0);\n\t\tfprintf(pcfile, \"%06x] A:%04x X:%04x Y:%04x P:%03x \"\n\t\t\t\"S:%04x D:%04x B:%02x %lld.%02lld %s\\n\", i,\n\t\t\tacc, xreg, yreg, psr, stack, direct, dbank,\n\t\t\t(dfcyc - base_dcyc) >> 16,\n\t\t\t((dfcyc & 0xffff) * 100) >> 16, str);\n\n\t\tif((dfcyc == 0) && (i != 0)) {\n\t\t\tbreak;\n\t\t}\n\t\tmax_dcyc = dfcyc;\n\t\tlog_pc_ptr++;\n\t\tif(log_pc_ptr >= g_log_pc_end_ptr) {\n\t\t\tlog_pc_ptr = g_log_pc_start_ptr;\n\t\t}\n\t}\n\n\t// Print any more DATA or INFO after last PC entry\n\tlog_data_ptr = debug_show_data_info(pcfile, log_data_ptr,\n\t\t\tbase_dcyc, max_dcyc + 10 * 65536, start_dcyc,\n\t\t\t&data_wrap, &data_count);\n\n\tfclose(pcfile);\n}\n\nvoid\nset_bp(word32 addr, word32 end_addr, word32 acc_type)\n{\n\tint\tcount;\n\n\tdbg_printf(\"About to set BP at %06x - %06x, type:%02x\\n\", addr,\n\t\t\t\t\t\t\tend_addr, acc_type);\n\tcount = g_num_breakpoints;\n\tif(count >= MAX_BREAK_POINTS) {\n\t\tdbg_printf(\"Too many (0x%02x) breakpoints set!\\n\", count);\n\t\treturn;\n\t}\n\n\tg_break_pts[count].start_addr = addr;\n\tg_break_pts[count].end_addr = end_addr;\n\tg_break_pts[count].acc_type = acc_type;\n\tg_num_breakpoints = count + 1;\n\tfixup_brks();\n}\n\nvoid\nshow_bp()\n{\n\tchar\tacc_str[4];\n\tword32\taddr, end_addr, acc_type;\n\tint\ti;\n\n\tdbg_printf(\"Showing breakpoints set\\n\");\n\tfor(i = 0; i < g_num_breakpoints; i++) {\n\t\taddr = g_break_pts[i].start_addr;\n\t\tend_addr = g_break_pts[i].end_addr;\n\t\tacc_type = g_break_pts[i].acc_type;\n\t\tacc_str[0] = ' ';\n\t\tacc_str[1] = ' ';\n\t\tacc_str[2] = ' ';\n\t\tacc_str[3] = 0;\n\t\tif(acc_type & 4) {\n\t\t\tacc_str[2] = 'X';\n\t\t}\n\t\tif(acc_type & 2) {\n\t\t\tacc_str[1] = 'W';\n\t\t}\n\t\tif(acc_type & 1) {\n\t\t\tacc_str[0] = 'R';\n\t\t}\n\t\tif(end_addr != addr) {\n\t\t\tdbg_printf(\"bp:%02x: %06x-%06x, t:%02x %s\\n\", i, addr,\n\t\t\t\t\t\tend_addr, acc_type, acc_str);\n\t\t} else {\n\t\t\tdbg_printf(\"bp:%02x: %06x, t:%02x %s\\n\", i, addr,\n\t\t\t\t\t\t\tacc_type, acc_str);\n\t\t}\n\t}\n}\n\nvoid\ndelete_bp(word32 addr, word32 end_addr)\n{\n\tint\tcount, hit;\n\tint\ti, j;\n\n\tdbg_printf(\"About to delete BP at %06x\\n\", addr);\n\tcount = g_num_breakpoints;\n\n\thit = -1;\n\tfor(i = count - 1; i >= 0; i--) {\n\t\tif((g_break_pts[i].start_addr > end_addr) ||\n\t\t\t\t\t(g_break_pts[i].end_addr < addr)) {\n\t\t\tcontinue;\t\t// Not this entry\n\t\t}\n\t\thit = i;\n\t\tdbg_printf(\"Deleting brkpoint #0x%02x\\n\", hit);\n\t\tfor(j = i+1; j < count; j++) {\n\t\t\tg_break_pts[j-1] = g_break_pts[j];\n\t\t}\n\t\tcount--;\n\t}\n\tg_num_breakpoints = count;\n\tif(hit < 0) {\n\t\tdbg_printf(\"Breakpoint not found!\\n\");\n\t} else {\n\t\tsetup_pageinfo();\n\t}\n\n\tshow_bp();\n}\n\nvoid\ndebug_iwm(const char *str)\n{\n\tif(str) {\n\t\t// Dummy use of argument\n\t}\n\tiwm_show_track(-1, -1, 0);\n}\n\nvoid\ndebug_iwm_check(const char *str)\n{\n\tif(str) {\n\t\t// Dummy use of argument\n\t}\n\tiwm_check_nibblization(0);\n}\n\nint\ndo_blank(int mode, int old_mode)\n{\n\tint\ttmp;\n\n\tswitch(old_mode) {\n\tcase 's':\n\t\ttmp = g_a2;\n\t\tif(tmp == 0) {\n\t\t\ttmp = 1;\n\t\t}\n#if 0\n\t\tfor(i = 0; i < tmp; i++) {\n\t\t\tg_stepping = 1;\n\t\t\tdo_step();\n\t\t\tif(g_halt_sim != 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n#endif\n\t\tg_list_kpc = engine.kpc;\n\t\t/* video_update_through_line(262); */\n\t\tbreak;\n\tcase ':':\n\t\tset_memory_c(((g_a3bank << 16) + g_a3), g_a2, 0);\n\t\tg_a3++;\n\t\tmode = old_mode;\n\t\tbreak;\n\tcase '.':\n\tcase 0:\n\t\txam_mem(-1);\n\t\tbreak;\n\tcase ',':\n\t\txam_mem(16);\n\t\tbreak;\n\tcase '+':\n\t\tdbg_printf(\"%x\\n\", g_a1 + g_a2);\n\t\tbreak;\n\tcase '-':\n\t\tdbg_printf(\"%x\\n\", g_a1 - g_a2);\n\t\tbreak;\n\tdefault:\n\t\tdbg_printf(\"Unknown mode at space: %d\\n\", old_mode);\n\t\tbreak;\n\t}\n\treturn mode;\n}\n\nvoid\ndo_go()\n{\n\t/* also called by do_step */\n\n\tg_config_control_panel = 0;\n\tclear_halt();\n}\n\nvoid\ndo_step()\n{\n\tint\tsize_mem_imm, size_x_imm;\n\n\treturn;\t\t\t// This is not correct\n\n\tdo_go();\n\n\tsize_mem_imm = 2;\n\tif(engine.psr & 0x20) {\n\t\tsize_mem_imm = 1;\n\t}\n\tsize_x_imm = 2;\n\tif(engine.psr & 0x10) {\n\t\tsize_x_imm = 1;\n\t}\n\tdbg_printf(\"%s\\n\",\n\t\t\tdo_dis(engine.kpc, size_mem_imm, size_x_imm, 0, 0, 0));\n}\n\nvoid\nxam_mem(int count)\n{\n\tshow_hex_mem(g_a1bank, g_a1, g_a2, count);\n\tg_a1 = g_a2 + 1;\n}\n\nvoid\nshow_hex_mem(word32 startbank, word32 start, word32 end, int count)\n{\n\tchar\tascii[MAXNUM_HEX_PER_LINE];\n\tword32\ti;\n\tint\tval, offset;\n\n\tif(count < 0) {\n\t\tcount = 16 - (start & 0xf);\n\t}\n\n\toffset = 0;\n\tascii[0] = 0;\n\tdbg_printf(\"Showing hex mem: bank: %x, start: %x, end: %x\\n\",\n\t\tstartbank, start, end);\n\tfor(i = start; i <= end; i++) {\n\t\tif( (i==start) || (count == 16) ) {\n\t\t\tdbg_printf(\"%04x:\",i);\n\t\t}\n\t\tdbg_printf(\" %02x\", get_memory_c((startbank <<16) + i));\n\t\tval = get_memory_c((startbank << 16) + i) & 0x7f;\n\t\tif((val < 32) || (val >= 0x7f)) {\n\t\t\tval = '.';\n\t\t}\n\t\tascii[offset++] = val;\n\t\tascii[offset] = 0;\n\t\tcount--;\n\t\tif(count <= 0) {\n\t\t\tdbg_printf(\"   %s\\n\", ascii);\n\t\t\toffset = 0;\n\t\t\tascii[0] = 0;\n\t\t\tcount = 16;\n\t\t}\n\t}\n\tif(offset > 0) {\n\t\tdbg_printf(\"   %s\\n\", ascii);\n\t}\n}\n\nvoid\ndo_debug_list()\n{\n\tchar\t*str;\n\tint\tsize, size_mem_imm, size_x_imm;\n\tint\ti;\n\n\tdbg_printf(\"%d=m %d=x %d=LCBANK\\n\", (engine.psr >> 5)&1,\n\t\t(engine.psr >> 4) & 1, (g_c068_statereg & 0x4) >> 2);\n\n\tsize_mem_imm = 2;\n\tif(engine.psr & 0x20) {\n\t\tsize_mem_imm = 1;\n\t}\n\tsize_x_imm = 2;\n\tif(engine.psr & 0x10) {\n\t\tsize_x_imm = 1;\n\t}\n\tfor(i = 0; i < 20; i++) {\n\t\tstr = do_dis(g_list_kpc, size_mem_imm, size_x_imm, 0, 0, &size);\n\t\tg_list_kpc += size;\n\t\tdbg_printf(\"%s\\n\", str);\n\t}\n}\n\nvoid\ndis_do_memmove()\n{\n\tword32\tval;\n\n\tdbg_printf(\"Memory move from %02x/%04x.%04x to %02x/%04x\\n\", g_a1bank,\n\t\t\t\t\t\tg_a1, g_a2, g_a4bank, g_a4);\n\twhile(g_a1 <= (g_a2 & 0xffff)) {\n\t\tval = get_memory_c((g_a1bank << 16) + g_a1);\n\t\tset_memory_c((g_a4bank << 16) + g_a4, val, 0);\n\t\tg_a1++;\n\t\tg_a4++;\n\t}\n\tg_a1 = g_a1 & 0xffff;\n\tg_a4 = g_a4 & 0xffff;\n}\n\nvoid\ndis_do_pattern_search()\n{\n#if 0\n\tword32\tmatch_val, val;\n\tint\tmatch_shift, count;\n\n\tdbg_printf(\"Memory pattern search for %04x in %02x/%04x to %02x/%04x\\n\",\n\t\t\tg_a4, g_a1bank, g_a1, g_a2bank, g_a2);\n\tmatch_shift = 0;\n\tcount = 0;\n\tmatch_val = g_a4;\n\twhile(1) {\n\t\tif(g_a1bank > g_a2bank) {\n\t\t\tbreak;\n\t\t}\n\t\tif(g_a1 > g_a2) {\n\t\t\tbreak;\n\t\t}\n\t\tval = get_memory_c((g_a1bank << 16) + g_a1);\n\t\tif(val == ((match_val >> match_shift) & 0xff)) {\n\t\t\tmatch_shift += 8;\n\t\t\tif(match_shift >= 16) {\n\t\t\t\tdbg_printf(\"Found %04x at %02x/%04x\\n\",\n\t\t\t\t\t\tmatch_val, g_a1bank, g_a1);\n\t\t\t\tcount++;\n\t\t\t}\n\t\t} else {\n\t\t\tmatch_shift = 0;\n\t\t}\n\t\tg_a1++;\n\t\tif(g_a1 >= 0x10000) {\n\t\t\tg_a1 = 0;\n\t\t\tg_a1bank++;\n\t\t}\n\t}\n#endif\n}\n\nvoid\ndis_do_compare()\n{\n\tword32\tval1, val2;\n\n\tdbg_printf(\"Memory Compare from %02x/%04x.%04x with %02x/%04x\\n\",\n\t\t\t\t\tg_a1bank, g_a1, g_a2, g_a4bank, g_a4);\n\twhile(g_a1 <= (g_a2 & 0xffff)) {\n\t\tval1 = get_memory_c((g_a1bank << 16) + g_a1);\n\t\tval2 = get_memory_c((g_a4bank << 16) + g_a4);\n\t\tif(val1 != val2) {\n\t\t\tdbg_printf(\"%02x/%04x: %02x vs %02x\\n\", g_a1bank, g_a1,\n\t\t\t\t\t\t\t\tval1, val2);\n\t\t}\n\t\tg_a1++;\n\t\tg_a4++;\n\t}\n\tg_a1 = g_a1 & 0xffff;\n\tg_a4 = g_a4 & 0xffff;\n}\n\nconst char *\ndo_debug_unix(const char *str, int old_mode)\n{\n\tchar\tlocalbuf[LINE_SIZE+2];\n\tbyte\t*bptr;\n\tword32\toffset, len, a1_val;\n\tlong\tret;\n\tint\tfd, load;\n\tint\ti;\n\n\tload = 0;\n\tswitch(*str++) {\n\tcase 'l': case 'L':\n\t\tdbg_printf(\"Loading..\");\n\t\tload = 1;\n\t\tbreak;\n\tcase 's': case 'S':\n\t\tdbg_printf(\"Saving...\");\n\t\tbreak;\n\tdefault:\n\t\tdbg_printf(\"Unknown unix command: %c\\n\", *(str - 1));\n\t\tif(str[-1] == 0) {\n\t\t\treturn str - 1;\n\t\t}\n\t\treturn str;\n\t}\n\twhile((*str == ' ') || (*str == '\\t')) {\n\t\tstr++;\n\t}\n\ti = 0;\n\twhile(i < LINE_SIZE) {\n\t\tlocalbuf[i++] = *str++;\n\t\tif((*str==' ') || (*str == '\\t') || (*str == '\\n') ||\n\t\t\t\t\t\t\t\t(*str == 0)) {\n\t\t\tbreak;\n\t\t}\n\t}\n\tlocalbuf[i] = 0;\n\n\tdbg_printf(\"About to open: %s,len: %d\\n\", localbuf,\n\t\t\t\t\t\t(int)strlen(localbuf));\n\tif(load) {\n\t\tfd = open(localbuf, O_RDONLY | O_BINARY);\n\t} else {\n\t\tfd = open(localbuf, O_WRONLY | O_CREAT | O_BINARY, 0x1b6);\n\t}\n\tif(fd < 0) {\n\t\tdbg_printf(\"Open %s failed: %d. errno:%d\\n\", localbuf, fd,\n\t\t\t\t\t\t\t\terrno);\n\t\treturn str;\n\t}\n\tif(load) {\n\t\toffset = g_a1 & 0xffff;\n\t\tlen = 0x20000 - offset;\n\t} else {\n\t\tif(old_mode == '.') {\n\t\t\tlen = g_a2 - g_a1 + 1;\n\t\t} else {\n\t\t\tlen = 0x100;\n\t\t}\n\t}\n\ta1_val = (g_a1bank << 16) | g_a1;\n\tbptr = &g_memory_ptr[a1_val];\n\tif((g_a1bank >= 0xe0) && (g_a1bank < 0xe2)) {\n\t\tbptr = &g_slow_memory_ptr[a1_val & 0x1ffff];\n\t}\n\tif(load) {\n\t\tret = read(fd, bptr, len);\n\t} else {\n\t\tret = write(fd, bptr, len);\n\t}\n\tdbg_printf(\"Read/write: addr %06x for %04x bytes, ret: %lx bytes\\n\",\n\t\ta1_val, len, ret);\n\tif(ret < 0) {\n\t\tdbg_printf(\"errno: %d\\n\", errno);\n\t}\n\tg_a1 = g_a1 + (int)ret;\n\treturn str;\n}\n\nvoid\ndo_debug_load()\n{\n\tdbg_printf(\"Sorry, can't load now\\n\");\n}\n\nchar *\ndo_dis(word32 kpc, int accsize, int xsize, int op_provided, word32 instr,\n\t\t\t\t\t\t\tint *size_ptr)\n{\n\tchar\tbuffer[MAX_DISAS_BUF];\n\tchar\tbuffer2[MAX_DISAS_BUF];\n\tconst char *str;\n\tword32\tval, oldkpc, dtype;\n\tint\targs, type, opcode, signed_val;\n\tint\ti;\n\n\toldkpc = kpc;\n\tif(op_provided) {\n\t\topcode = (instr >> 24) & 0xff;\n\t} else {\n\t\topcode = (int)get_memory_c(kpc) & 0xff;\n\t}\n\n\tkpc++;\n\n\tdtype = disas_types[opcode];\n\tstr = disas_opcodes[opcode];\n\ttype = dtype & 0xff;\n\targs = dtype >> 8;\n\n\tif(args > 3) {\n\t\tif(args == 4) {\n\t\t\targs = accsize;\n\t\t} else if(args == 5) {\n\t\t\targs = xsize;\n\t\t}\n\t}\n\n\tval = -1;\n\tswitch(args) {\n\tcase 0:\n\t\tval = 0;\n\t\tbreak;\n\tcase 1:\n\t\tif(op_provided) {\n\t\t\tval = instr & 0xff;\n\t\t} else {\n\t\t\tval = get_memory_c(kpc);\n\t\t}\n\t\tbreak;\n\tcase 2:\n\t\tif(op_provided) {\n\t\t\tval = instr & 0xffff;\n\t\t} else {\n\t\t\tval = get_memory16_c(kpc);\n\t\t}\n\t\tbreak;\n\tcase 3:\n\t\tif(op_provided) {\n\t\t\tval = instr & 0xffffff;\n\t\t} else {\n\t\t\tval = get_memory24_c(kpc);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tfprintf(stderr, \"args out of rang: %d, opcode: %08x\\n\",\n\t\t\targs, opcode);\n\t\tbreak;\n\t}\n\tkpc += args;\n\n\tif(!op_provided) {\n\t\tinstr = (opcode << 24) | (val & 0xffffff);\n\t}\n\n\tswitch(type) {\n\tcase ABS:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%04x\", str, val);\n\t\tbreak;\n\tcase ABSX:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%04x,X\", str, val);\n\t\tbreak;\n\tcase ABSY:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%04x,Y\", str, val);\n\t\tbreak;\n\tcase ABSLONG:\n\t\tif(args != 3) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%06x\", str, val);\n\t\tbreak;\n\tcase ABSIND:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s ($%04x)\", str, val);\n\t\tbreak;\n\tcase ABSXIND:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s ($%04x,X)\", str, val);\n\t\tbreak;\n\tcase IMPLY:\n\tcase ACCUM:\n\t\tif(args != 0) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF,  \"%s\", str);\n\t\tbreak;\n\tcase IMMED:\n\t\tif(args == 1) {\n\t\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  #$%02x\", str,\n\t\t\t\t\t\t\t\t\tval);\n\t\t} else if(args == 2) {\n\t\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  #$%04x\", str,\n\t\t\t\t\t\t\t\t\tval);\n\t\t} else {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tbreak;\n\tcase JUST8:\n\tcase DLOC:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%02x\", str, val);\n\t\tbreak;\n\tcase DLOCX:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%02x,X\", str, val);\n\t\tbreak;\n\tcase DLOCY:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%02x,Y\", str, val);\n\t\tbreak;\n\tcase LONG:\n\t\tif(args != 3) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%06x\", str, val);\n\t\tbreak;\n\tcase LONGX:\n\t\tif(args != 3) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%06x,X\", str, val);\n\t\tbreak;\n\tcase DLOCIND:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  ($%02x)\", str, val);\n\t\tbreak;\n\tcase DLOCINDY:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  ($%02x),Y\", str, val);\n\t\tbreak;\n\tcase DLOCXIND:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  ($%02x,X)\", str, val);\n\t\tbreak;\n\tcase DLOCBRAK:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  [$%02x]\", str, val);\n\t\tbreak;\n\tcase DLOCBRAKY:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  [$%02x],y\", str, val);\n\t\tbreak;\n\tcase DISP8:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsigned_val = (signed char)val;\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%04x\", str,\n\t\t\t\t\t\t(kpc + signed_val) & 0xffff);\n\t\tbreak;\n\tcase DISP8S:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%02x,S\", str,\n\t\t\t\t\t\t\t\tval & 0xff);\n\t\tbreak;\n\tcase DISP8SINDY:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  ($%02x,S),Y\", str,\n\t\t\t\t\t\t\t\tval & 0xff);\n\t\tbreak;\n\tcase DISP16:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%04x\", str,\n\t\t\t\t(word32)(kpc+(signed)(word16)(val)) & 0xffff);\n\t\tbreak;\n\tcase MVPMVN:\n\t\tif(args != 2) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  $%02x,$%02x\", str,\n\t\t\t\t\t\t\tval & 0xff, val >> 8);\n\t\tbreak;\n\tcase SEPVAL:\n\tcase REPVAL:\n\t\tif(args != 1) {\n\t\t\tdbg_printf(\"arg # mismatch for opcode %x\\n\", opcode);\n\t\t}\n\t\tsnprintf(&buffer[0], MAX_DISAS_BUF, \"%s  #$%02x\", str, val);\n\t\tbreak;\n\tdefault:\n\t\tdbg_printf(\"argument type: %d unexpected\\n\", type);\n\t\tbreak;\n\t}\n\n\tg_disas_buffer[0] = 0;\n\tsnprintf(&g_disas_buffer[0], MAX_DISAS_BUF, \"%02x/%04x: %02x \",\n\t\toldkpc >> 16, oldkpc & 0xffff, opcode);\n\tfor(i = 1; i <= args; i++) {\n\t\tsnprintf(&buffer2[0], MAX_DISAS_BUF, \"%02x \", instr & 0xff);\n\t\tcfg_strlcat(&g_disas_buffer[0], &buffer2[0], MAX_DISAS_BUF);\n\t\tinstr = instr >> 8;\n\t}\n\tfor(; i < 4; i++) {\n\t\tcfg_strlcat(&g_disas_buffer[0], \"   \", MAX_DISAS_BUF);\n\t}\n\tcfg_strlcat(&g_disas_buffer[0], \" \", MAX_DISAS_BUF);\n\tcfg_strlcat(&g_disas_buffer[0], &buffer[0], MAX_DISAS_BUF);\n\n\tif(size_ptr) {\n\t\t*size_ptr = args + 1;\n\t}\n\treturn (&g_disas_buffer[0]);\n}\n\nint\ndebug_get_view_line(int back)\n{\n\tint\tpos;\n\n\t// where back==0 means return pos - 1.\n\tpos = g_debug_lines_pos - 1;\n\tpos = pos - back;\n\tif(pos < 0) {\n\t\tif(g_debug_lines_alloc >= g_debug_lines_max) {\n\t\t\tpos += g_debug_lines_alloc;\n\t\t} else {\n\t\t\treturn 0;\t\t\t// HACK: return -1\n\t\t}\n\t}\n\treturn pos;\n}\n\nint\ndebug_add_output_line(char *in_str)\n{\n\tDebug_entry *line_ptr;\n\tbyte\t*out_bptr;\n\tint\tpos, alloc, view, used_len, c;\n\tint\ti;\n\n\t// printf(\"debug_add_output_line %s len:%d\\n\", in_str, len);\n\tpos = g_debug_lines_pos;\n\tline_ptr = g_debug_lines_ptr;\n\talloc = g_debug_lines_alloc;\n\tif(pos >= alloc) {\n\t\tif(alloc < g_debug_lines_max) {\n\t\t\talloc = MY_MAX(2048, alloc*3);\n\t\t\talloc = MY_MAX(alloc, pos*3);\n\t\t\talloc = MY_MIN(alloc, g_debug_lines_max);\n\t\t\tline_ptr = realloc(line_ptr,\n\t\t\t\t\t\talloc * sizeof(Debug_entry));\n\t\t\tprintf(\"realloc.  now %p, alloc:%d\\n\", line_ptr, alloc);\n\t\t\tg_debug_lines_ptr = line_ptr;\n\t\t\tg_debug_lines_alloc = alloc;\n\t\t\tprintf(\"Alloced debug lines to %d\\n\", alloc);\n\t\t} else {\n\t\t\tpos = 0;\n\t\t}\n\t}\n\t// Convert to A2 format chars: set high bit of each byte, 80 chars\n\t//  per line\n\tout_bptr = &(line_ptr[pos].str_buf[0]);\n\tused_len = 0;\n\tfor(i = 0; i < DEBUG_ENTRY_MAX_CHARS; i++) {\n\t\tc = ' ';\n\t\tif(*in_str) {\n\t\t\tc = *in_str++;\n\t\t\tused_len++;\n\t\t}\n\t\tc = c ^ 0x80;\t\t// Set highbit if not already set\n\t\tout_bptr[i] = c;\n\t}\n\tpos++;\n\tg_debug_lines_pos = pos;\n\tg_debug_lines_total++;\t\t// For updating the window\n\tg_debugwin_changed++;\n\tview = g_debug_lines_view;\n\tif(view >= 0) {\n\t\tview++;\t\t// view is back from pos, so to stay the same,\n\t\t\t\t//  it must be incremented when pos incs\n\t\tif((view - 50) >= g_debug_lines_max) {\n\t\t\t// We were viewing the oldest page, and by wrapping\n\t\t\t//  around we're about to wipe out this old data\n\t\t\t// Jump to most recent data\n\t\t\tview = -1;\n\t\t}\n\t\tg_debug_lines_view = view;\n\t}\n\n\treturn used_len;\n}\n\nvoid\ndebug_add_output_string(char *in_str, int len)\n{\n\tint\tret, tries;\n\n\ttries = 0;\n\tret = 0;\n\tif(g_debug_to_stdout) {\n\t\tputs(in_str);\t\t// Send output to stdout, too\n\t}\n\twhile((len > 0) || (tries == 0)) {\n\t\t// printf(\"DEBUG: adding str: %s, len:%d, ret:%d\\n\", in_str,\n\t\t//\t\t\t\t\t\tlen, ret);\n\t\tret = debug_add_output_line(in_str);\n\t\tlen -= ret;\n\t\tin_str += ret;\n\t\ttries++;\n\t}\n}\n\nvoid\ndebug_add_output_chars(char *str)\n{\n\tint\tpos, c, tab_spaces;\n\n\tpos = g_debug_stage_pos;\n\ttab_spaces = 0;\n\twhile(1) {\n\t\tif(tab_spaces > 0) {\n\t\t\tc = ' ';\n\t\t\ttab_spaces--;\n\t\t} else {\n\t\t\tc = *str++;\n\t\t\tif(c == '\\t') {\n\t\t\t\ttab_spaces = 7 - (pos & 7);\n\t\t\t\tc = ' ';\n\t\t\t}\n\t\t}\n\t\tpos = MY_MIN(pos, (PRINTF_BUF_SIZE - 1));\n\t\tif((c == '\\n') || (pos >= (PRINTF_BUF_SIZE - 1))) {\n\t\t\tg_debug_stage_buf[pos] = 0;\n\t\t\tdebug_add_output_string(&g_debug_stage_buf[0], pos);\n\t\t\tpos = 0;\n\t\t\tg_debug_stage_pos = 0;\n\t\t\tcontinue;\n\t\t}\n\t\tif(c == 0) {\n\t\t\tg_debug_stage_pos = pos;\n\t\t\treturn;\n\t\t}\n\t\tg_debug_stage_buf[pos++] = c;\n\t}\n}\n\nint\ndbg_printf(const char *fmt, ...)\n{\n\tva_list args;\n\tint\tret;\n\n\tva_start(args, fmt);\n\tret = dbg_vprintf(fmt, args);\n\tva_end(args);\n\treturn ret;\n}\n\nint\ndbg_vprintf(const char *fmt, va_list args)\n{\n\tint\tret;\n\n\tret = vsnprintf(&g_debug_printf_buf[0], PRINTF_BUF_SIZE, fmt, args);\n\tdebug_add_output_chars(&g_debug_printf_buf[0]);\n\treturn ret;\n}\n\nvoid\nhalt_printf(const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tdbg_vprintf(fmt, args);\n\tva_end(args);\n\n\tset_halt(1);\n}\n\nvoid\nhalt2_printf(const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tdbg_vprintf(fmt, args);\n\tva_end(args);\n\n\tset_halt(2);\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/defc.h",
    "content": "#ifdef INCLUDE_RCSID_C\nconst char rcsid_defc_h[] = \"@(#)$KmKId: defc.h,v 1.142 2024-09-15 13:56:12+00 kentd Exp $\";\n#endif\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2024 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include \"defcomm.h\"\n\n#define STRUCT(a) typedef struct a ## _st a; struct a ## _st\n\ntypedef unsigned char byte;\ntypedef unsigned short word16;\ntypedef unsigned int word32;\n#if _MSC_VER\ntypedef unsigned __int64 dword64;\n#else\ntypedef unsigned long long dword64;\n#endif\n\n/* 28MHz crystal, plus every 65th 1MHz cycle is stretched 140ns */\n#define CYCS_28_MHZ\t\t(28636360)\n#define DCYCS_28_MHZ\t\t(1.0*CYCS_28_MHZ)\n#define CYCS_3_5_MHZ\t\t(CYCS_28_MHZ/8)\n#define DCYCS_1_MHZ\t\t((DCYCS_28_MHZ/28.0)*(65.0*7/(65.0*7+1.0)))\n\n// DCYCS_1_MHZ is 1020484.32016\n\n#define CYCLES_IN_16MS_RAW\t(262 * 65)\n/* Use precisely 17030 instead of forcing 60 Hz since this is the number of */\n/*  1MHz cycles per screen */\n\n#define DCYCS_IN_16MS\t\t((double)(CYCLES_IN_16MS_RAW))\n#define DRECIP_DCYCS_IN_16MS\t(1.0 / (DCYCS_IN_16MS))\n#define VBL_RATE\t\t(DCYCS_1_MHZ / DCYCS_IN_16MS)\n// VBL rate is about 59.9227 frames/sec\n\n#define MAXNUM_HEX_PER_LINE\t32\n#define MAX_SCALE_SIZE\t\t5100\n\n#ifdef __NeXT__\n# include <libc.h>\n#endif\n\n#ifndef _WIN32\n# include <unistd.h>\n# include <sys/ioctl.h>\n# include <sys/wait.h>\n#endif\n\n#include <stdio.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include <stdarg.h>\n#include <ctype.h>\n#ifdef HPUX\n# include <machine/inline.h>\t\t/* for GET_ITIMER */\n#endif\n\n#ifdef SOLARIS\n# include <sys/filio.h>\n#endif\n\n#ifdef _WIN32\n# include <direct.h>\n# include <io.h>\n# pragma warning(disable : 4996)\t/* open() is deprecated...sigh */\nint ftruncate(int fd, word32 length);\nint lstat(const char *path, struct stat *bufptr);\n#endif\n\n#ifndef O_BINARY\n/* work around some Windows junk */\n# define O_BINARY\t0\n#endif\n\n#define MAX_CHANGE_RECTS\t20\n\n#ifdef __GNUC__\nint dbg_printf(const char *fmt, ...) __attribute__ ((\n\t\t\t\t\t\t__format__(printf, 1, 2)));\n#endif\n\nSTRUCT(Pc_log) {\n\tdword64\tdfcyc;\n\tword32\tdbank_kpc;\n\tword32\tinstr;\n\tword32\tpsr_acc;\n\tword32\txreg_yreg;\n\tword32\tstack_direct;\n\tword32\tpad;\n};\n\nSTRUCT(Data_log) {\n\tdword64\tdfcyc;\n\tbyte\t*stat;\n\tword32\taddr;\n\tword32\tval;\n\tword32\tsize;\n};\n\nSTRUCT(Event) {\n\tdword64\tdfcyc;\n\tint\ttype;\n\tEvent\t*next;\n};\n\nSTRUCT(Fplus) {\n\tdword64\tdplus_1;\n\tdword64\tdplus_x_minus_1;\n};\n\nSTRUCT(Engine_reg) {\n\tdword64\tdfcyc;\n\tword32\tkpc;\n\tword32\tacc;\n\n\tword32\txreg;\n\tword32\tyreg;\n\n\tword32\tstack;\n\tword32\tdbank;\n\n\tword32\tdirect;\n\tword32\tpsr;\n\tFplus\t*fplus_ptr;\n};\n\nSTRUCT(Break_point) {\n\tword32\tstart_addr;\n\tword32\tend_addr;\n\tword32\tacc_type;\n};\n\nSTRUCT(Change_rect) {\n\tint\tx;\n\tint\ty;\n\tint\twidth;\n\tint\theight;\n};\n\n\nSTRUCT(Kimage) {\n\tword32\t*wptr;\n\tint\ta2_width_full;\n\tint\ta2_height_full;\n\tint\ta2_width;\n\tint\ta2_height;\n\tint\tx_width;\n\tint\tx_height;\n\tint\tx_refresh_needed;\n\tint\tx_max_width;\n\tint\tx_max_height;\n\tint\tx_xpos;\n\tint\tx_ypos;\n\tint\tactive;\n\tword32\tvbl_of_last_resize;\n\tword32\tc025_val;\n\tword32\tscale_width_to_a2;\n\tword32\tscale_width_a2_to_x;\n\tword32\tscale_height_to_a2;\n\tword32\tscale_height_a2_to_x;\n\tint\tnum_change_rects;\n\tChange_rect change_rect[MAX_CHANGE_RECTS];\n\tword32\tscale_width[MAX_SCALE_SIZE + 1];\n\tword32\tscale_height[MAX_SCALE_SIZE + 1];\n};\n\ntypedef byte *Pg_info;\nSTRUCT(Page_info) {\n\tPg_info rd_wr;\n};\n\nSTRUCT(Cfg_menu) {\n\tconst char *str;\n\tvoid\t*ptr;\n\tconst char *name_str;\n\tvoid\t*defptr;\n\tint\tcfgtype;\n};\n\nSTRUCT(Cfg_dirent) {\n\tchar\t*name;\n\tint\tis_dir;\n\tint\tpart_num;\n\tdword64\tdsize;\n\tdword64\tdimage_start;\n\tdword64\tcompr_dsize;\n};\n\nSTRUCT(Cfg_listhdr) {\n\tCfg_dirent *direntptr;\n\tint\tmax;\n\tint\tlast;\n\tint\tinvalid;\n\n\tint\tcurent;\n\tint\ttopent;\n\n\tint\tnum_to_show;\n};\n\ntypedef void (Dbg_fn)(const char *str);\n\nSTRUCT(Dbg_longcmd) {\n\tconst char *str;\n\tDbg_fn\t*fnptr;\n\tDbg_longcmd *subptr;\n\tconst char *help_str;\n};\n\nSTRUCT(Emustate_intlist) {\n\tconst char *str;\n\tword32\t*iptr;\n};\n\nSTRUCT(Emustate_dword64list) {\n\tconst char *str;\n\tdword64\t*dptr;\n};\n\nSTRUCT(Emustate_word32list) {\n\tconst char *str;\n\tword32\t*wptr;\n};\n\nSTRUCT(Lzw_state) {\n\tword32\ttable[4096 + 2];\n\tword32\tentry;\n\tint\tbits;\n};\n\n#ifdef __LP64__\n# define PTR2WORD(a)\t((word32)(unsigned long long)(a))\n#else\n# define PTR2WORD(a)\t((word32)(unsigned long long)(a))\n#endif\n\n\n#define ALTZP\t(g_c068_statereg & 0x80)\n/* #define PAGE2 (g_c068_statereg & 0x40) */\n#define RAMRD\t(g_c068_statereg & 0x20)\n#define RAMWRT\t(g_c068_statereg & 0x10)\n#define RDROM\t(g_c068_statereg & 0x08)\n#define LCBANK2\t(g_c068_statereg & 0x04)\n#define ROMB\t(g_c068_statereg & 0x02)\n// #define INTCX\t(g_c068_statereg & 0x01)\n\n#define C041_EN_25SEC_INTS\t0x10\n#define C041_EN_VBL_INTS\t0x08\n#define C041_EN_SWITCH_INTS\t0x04\n#define C041_EN_MOVE_INTS\t0x02\n#define C041_EN_MOUSE\t\t0x01\n\n/* WARNING: SCC1 and SCC0 interrupts must be in this order for scc.c */\n/*  This order matches the SCC hardware, and SCC1_ZEROCNT must be 0x0001 */\n#define IRQ_PENDING_SCC1_ZEROCNT\t0x00001\n#define IRQ_PENDING_SCC1_TX\t\t0x00002\n#define IRQ_PENDING_SCC1_RX\t\t0x00004\n#define IRQ_PENDING_SCC0_ZEROCNT\t0x00008\n#define IRQ_PENDING_SCC0_TX\t\t0x00010\n#define IRQ_PENDING_SCC0_RX\t\t0x00020\n#define IRQ_PENDING_C023_SCAN\t\t0x00100\n#define IRQ_PENDING_C023_1SEC\t\t0x00200\n#define IRQ_PENDING_C046_25SEC\t\t0x00400\n#define IRQ_PENDING_C046_VBL\t\t0x00800\n#define IRQ_PENDING_ADB_KBD_SRQ\t\t0x01000\n#define IRQ_PENDING_ADB_DATA\t\t0x02000\n#define IRQ_PENDING_ADB_MOUSE\t\t0x04000\n#define IRQ_PENDING_DOC\t\t\t0x08000\n#define IRQ_PENDING_MOCKINGBOARDA\t0x10000\n#define IRQ_PENDING_MOCKINGBOARDB\t0x20000\t\t/* must be BOARDA*2 */\n\n\n#define EXTRU(val, pos, len)\t\t\t\t\t\\\n\t( ( (len) >= (pos) + 1) ? ((val) >> (31-(pos))) :\t\\\n\t\t(((val) >> (31-(pos)) ) & ( (1<<(len) ) - 1) ) )\n\n#define DEP1(val, pos, old_val)\t\t\t\t\t\\\n\t\t(((old_val) & ~(1 << (31 - (pos))) ) |\t\t\\\n\t\t\t( ((val) & 1) << (31 - (pos))) )\n\n#define set_halt(val) \\\n\tif(val) { set_halt_act(val); }\n\n#define clear_halt() \\\n\tclr_halt_act()\n\n#define GET_PAGE_INFO_RD(page) \\\n\t(page_info_rd_wr[page].rd_wr)\n\n#define GET_PAGE_INFO_WR(page) \\\n\t(page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr)\n\n#define SET_PAGE_INFO_RD(page,val) \\\n\t;page_info_rd_wr[page].rd_wr = (Pg_info)val;\n\n#define SET_PAGE_INFO_WR(page,val) \\\n\t;page_info_rd_wr[0x10000 + PAGE_INFO_PAD_SIZE + (page)].rd_wr = \\\n\t\t\t\t\t\t\t(Pg_info)val;\n\n#define VERBOSE_DISK\t0x001\n#define VERBOSE_IRQ\t0x002\n#define VERBOSE_CLK\t0x004\n#define VERBOSE_SHADOW\t0x008\n#define VERBOSE_IWM\t0x010\n#define VERBOSE_DOC\t0x020\n#define VERBOSE_ADB\t0x040\n#define VERBOSE_SCC\t0x080\n#define VERBOSE_TEST\t0x100\n#define VERBOSE_VIDEO\t0x200\n#define VERBOSE_MAC\t0x400\n#define VERBOSE_DYNA\t0x800\n\n#ifdef NO_VERB\n# define DO_VERBOSE\t0\n#else\n# define DO_VERBOSE\t1\n#endif\n\n#define disk_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_DISK)) printf\n#define irq_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_IRQ)) printf\n#define clk_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_CLK)) printf\n#define shadow_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_SHADOW)) printf\n#define iwm_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_IWM)) printf\n#define doc_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_DOC)) printf\n#define adb_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_ADB)) printf\n#define scc_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_SCC)) printf\n#define test_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_TEST)) printf\n#define vid_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_VIDEO)) printf\n#define mac_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_MAC)) printf\n#define dyna_printf\tif(DO_VERBOSE && (Verbose & VERBOSE_DYNA)) printf\n\n\n#define HALT_ON_SCAN_INT\t0x001\n#define HALT_ON_IRQ\t\t0x002\n#define HALT_ON_SHADOW_REG\t0x004\n#define HALT_ON_C70D_WRITES\t0x008\n\n#define HALT_ON(a, msg)\t\t\t\\\n\tif(Halt_on & a) {\t\t\\\n\t\thalt_printf(msg);\t\\\n\t}\n\n\n#define MY_MIN(a,b)\t(((a) < (b)) ? (a) : (b))\n#define MY_MAX(a,b)\t(((a) > (b)) ? (a) : (b))\n\n#define GET_ITIMER(dest)\tdest = get_itimer();\n\n#define FINISH(arg1, arg2)\tg_ret1 = arg1 | ((arg2) << 8); g_dcycles_end=0;\n\n#include \"iwm.h\"\n#include \"protos.h\"\n"
  },
  {
    "path": "upstream/kegs/src/defcomm.h",
    "content": "#ifdef INCLUDE_RCSID_C\nconst char rcsdif_defcomm_h[] = \"@(#)$KmKId: defcomm.h,v 1.109 2023-11-12 15:29:41+00 kentd Exp $\";\n#endif\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#define SHIFT_PER_CHANGE\t3\n#define CHANGE_SHIFT\t\t(5 + SHIFT_PER_CHANGE)\n\n#define SLOW_MEM_CH_SIZE\t(0x20000 >> CHANGE_SHIFT)\n\n#define MAXNUM_HEX_PER_LINE\t32\n\n/* Different Joystick defines */\n#define JOYSTICK_MOUSE\t\t1\n#define JOYSTICK_LINUX\t\t2\n#define JOYSTICK_KEYPAD\t\t3\n#define JOYSTICK_WIN32_1\t4\n#define JOYSTICK_WIN32_2\t5\n\n#define MAX_BREAK_POINTS\t0x20\n\n#define MAX_BP_INDEX\t\t0x100\n#define MAX_BP_PER_INDEX\t3\t/* 4 word32s total = 16 bytes */\n#define SIZE_BREAKPT_ENTRY_BITS\t4\t/* 16 bytes = 4 bits */\n\n/* Warning--next defines used by asm! */\n#define PAGE_INFO_PAD_SIZE\t0x800\n#define PAGE_INFO_WR_OFFSET\t0x10000+PAGE_INFO_PAD_SIZE\n\n#define BANK_IO_BIT\t\t31\n#define BANK_SHADOW_BIT\t\t30\n#define BANK_SHADOW2_BIT\t29\n#define BANK_IO2_BIT\t\t28\n#define BANK_BREAK_BIT\t\t27\n#define BANK_BREAK\t\t(1 << (31 - BANK_BREAK_BIT))\n#define BANK_IO2_TMP\t\t(1 << (31 - BANK_IO2_BIT))\n#define BANK_IO_TMP\t\t(1 << (31 - BANK_IO_BIT))\n#define BANK_SHADOW\t\t(1 << (31 - BANK_SHADOW_BIT))\n#define BANK_SHADOW2\t\t(1 << (31 - BANK_SHADOW2_BIT))\n#define SET_BANK_IO\t\\\n\t\t(&g_dummy_memory1_ptr[BANK_IO_TMP | BANK_IO2_TMP])\n\n#define BANK_BAD_MEM\t\t(&g_dummy_memory1_ptr[0xff])\n\n\n#define RET_BREAK\t0x1\n#define RET_COP\t\t0x2\n#define RET_WDM\t\t0x3\n#define RET_WAI\t\t0x4\n#define RET_STP\t\t0x5\n#define RET_PSR\t\t0x6\n#define RET_IRQ\t\t0x7\n#define RET_TOOLTRACE\t0x8\n\n\n#define BIT_ALL_STAT_TEXT\t\t0\n#define BIT_ALL_STAT_VID80\t\t1\n#define BIT_ALL_STAT_ST80\t\t2\n#define BIT_ALL_STAT_COLOR_C021\t\t3\n#define BIT_ALL_STAT_MIX_T_GR\t\t4\n#define BIT_ALL_STAT_DIS_COLOR_DHIRES\t5\t/* special val, c029 */\n#define BIT_ALL_STAT_PAGE2\t\t6\t/* special val, statereg */\n#define BIT_ALL_STAT_SUPER_HIRES\t7\t/* special, c029 */\n#define BIT_ALL_STAT_HIRES\t\t8\n#define BIT_ALL_STAT_ANNUNC3\t\t9\n#define BIT_ALL_STAT_ALTCHARSET\t\t10\n#define BIT_ALL_STAT_FLASH_STATE\t11\n#define BIT_ALL_STAT_BG_COLOR\t\t12\t/* 4 bits */\n#define BIT_ALL_STAT_TEXT_COLOR\t\t16\t/* 4 bits */\n\t\t\t\t\t\t/* Text must be just above */\n\t\t\t\t\t\t/* bg to match c022 reg */\n#define BIT_ALL_STAT_VOC_INTERLACE\t20\n#define BIT_ALL_STAT_VOC_MAIN\t\t21\n#define BIT_ALL_STAT_BORDER\t\t22\n\n#define ALL_STAT_SUPER_HIRES\t\t(1 << (BIT_ALL_STAT_SUPER_HIRES))\n#define ALL_STAT_TEXT\t\t\t(1 << (BIT_ALL_STAT_TEXT))\n#define ALL_STAT_VID80\t\t\t(1 << (BIT_ALL_STAT_VID80))\n#define ALL_STAT_PAGE2\t\t\t(1 << (BIT_ALL_STAT_PAGE2))\n#define ALL_STAT_ST80\t\t\t(1 << (BIT_ALL_STAT_ST80))\n#define ALL_STAT_COLOR_C021\t\t(1 << (BIT_ALL_STAT_COLOR_C021))\n#define ALL_STAT_DIS_COLOR_DHIRES\t(1 << (BIT_ALL_STAT_DIS_COLOR_DHIRES))\n#define ALL_STAT_MIX_T_GR\t\t(1 << (BIT_ALL_STAT_MIX_T_GR))\n#define ALL_STAT_HIRES\t\t\t(1 << (BIT_ALL_STAT_HIRES))\n#define ALL_STAT_ANNUNC3\t\t(1 << (BIT_ALL_STAT_ANNUNC3))\n#define ALL_STAT_TEXT_COLOR\t\t(0xf << (BIT_ALL_STAT_TEXT_COLOR))\n#define ALL_STAT_BG_COLOR\t\t(0xf << (BIT_ALL_STAT_BG_COLOR))\n#define ALL_STAT_ALTCHARSET\t\t(1 << (BIT_ALL_STAT_ALTCHARSET))\n#define ALL_STAT_FLASH_STATE\t\t(1 << (BIT_ALL_STAT_FLASH_STATE))\n#define ALL_STAT_VOC_INTERLACE\t\t(1 << (BIT_ALL_STAT_VOC_INTERLACE))\n#define ALL_STAT_VOC_MAIN\t\t(1 << (BIT_ALL_STAT_VOC_MAIN))\n#define ALL_STAT_BORDER\t\t\t(1 << (BIT_ALL_STAT_BORDER))\n\n#define BORDER_WIDTH\t\t32\n\n#define EFF_BORDER_WIDTH\t(BORDER_WIDTH + (640-560))\n\n/* BASE_MARGIN_BOTTOM+MARGIN_TOP must equal 62.  There are 262 scan lines */\n/*  at 60Hz (15.7KHz line rate) and so we just make 62 border lines */\n#define BASE_MARGIN_TOP\t\t32\n#define BASE_MARGIN_BOTTOM\t30\n#define BASE_MARGIN_LEFT\tBORDER_WIDTH\n#define BASE_MARGIN_RIGHT\tBORDER_WIDTH\n\n#define A2_WINDOW_WIDTH\t\t640\n#define A2_WINDOW_HEIGHT\t400\n\n#define X_A2_WINDOW_WIDTH\t(A2_WINDOW_WIDTH + BASE_MARGIN_LEFT + \\\n\t\t\t\t\t\t\tBASE_MARGIN_RIGHT)\n#define X_A2_WINDOW_HEIGHT\t(A2_WINDOW_HEIGHT + BASE_MARGIN_TOP + \\\n\t\t\t\t\t\t\tBASE_MARGIN_BOTTOM)\n\n#define MAX_STATUS_LINES\t4\n#define STATUS_LINE_LENGTH\t88\n\n#define BASE_WINDOW_WIDTH\t(X_A2_WINDOW_WIDTH)\n\n\n#define A2_BORDER_COLOR_NUM\t0xfe\n\n"
  },
  {
    "path": "upstream/kegs/src/defs_instr.h",
    "content": "// $KmKId: defs_instr.h,v 1.70 2023-11-05 16:22:26+00 kentd Exp $\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2021 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#undef GET_DLOC_X_IND_RD\n\n#ifdef ACC8\n# define GET_DLOC_X_IND_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DLOC_X_IND_WR();\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DLOC_X_IND_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DLOC_X_IND_WR();\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#undef GET_DISP8_S_RD\n\n#ifdef ACC8\n# define GET_DISP8_S_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DISP8_S_WR();\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DISP8_S_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DISP8_S_WR();\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#undef GET_DLOC_RD\n\n#ifdef ACC8\n# define GET_DLOC_RD()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\\\n\t}\t\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\\\n\tGET_MEMORY8((direct + arg) & 0xffff, arg);\n#else\n# define GET_DLOC_RD()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\\\n\t}\t\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\\\n\tGET_MEMORY16((direct + arg) & 0xffff, arg, 1);\n#endif\n\n#undef GET_DLOC_RD_RMW\n#undef GET_MEM_RMW\n\n#define GET_MEM_RMW()\t\t\t\t\t\t\\\n\tif(!IS_ACC16) {\t\t\t\t\t\t\\\n\t\tif(psr & 0x100) {\t\t\t\t\\\n\t\t\t/* emulation re-writes the address */\t\\\n\t\t\tSET_MEMORY8(addr_latch, arg);\t\t\\\n\t\t} else {\t\t\t\t\t\\\n\t\t\t/* otherwise, just read addr again */\t\\\n\t\t\tGET_MEMORY8(addr_latch, dummy1);\t\\\n\t\t}\t\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\t/* 16-bit re-reads addr+1 again */\t\t\\\n\t\tdummy1 = addr_latch + 1;\t\t\t\\\n\t\tGET_MEMORY8(dummy1, dummy1);\t\t\t\\\n\t\taddr_latch--;\t\t\t\t\t\\\n\t}\n\n#define GET_DLOC_RD_RMW()\t\t\t\\\n\tGET_DLOC_RD();\t\t\t\t\\\n\tGET_MEM_RMW();\n\n\n#undef GET_DLOC_L_IND_RD\n\n#ifdef ACC8\n# define GET_DLOC_L_IND_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DLOC_L_IND_WR();\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DLOC_L_IND_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DLOC_L_IND_WR();\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#undef GET_IMM_MEM\n\n#ifdef ACC8\n# define GET_IMM_MEM()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tINC_KPC_2;\n#else\n# define GET_IMM_MEM()\t\t\t\t\\\n\tGET_2BYTE_ARG;\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\\\n\tINC_KPC_3;\n#endif\n\n#undef GET_ABS_RD\n\n#ifdef ACC8\n# define GET_ABS_RD()\t\t\t\t\\\n\tGET_2BYTE_ARG;\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\\\n\tGET_MEMORY8((dbank << 16) + arg, arg);\t\\\n\tINC_KPC_3;\n#else\n# define GET_ABS_RD()\t\t\t\t\\\n\tGET_2BYTE_ARG;\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\\\n\tGET_MEMORY16((dbank << 16) + arg, arg, 0); \\\n\tINC_KPC_3;\n#endif\n\n#undef GET_ABS_RD_RMW\n\n#define GET_ABS_RD_RMW()\t\t\t\\\n\tGET_ABS_RD();\t\t\t\t\\\n\tGET_MEM_RMW();\n\n#undef GET_LONG_RD\n\n#ifdef ACC8\n# define GET_LONG_RD()\t\t\t\t\\\n\tGET_3BYTE_ARG;\t\t\t\t\\\n\tCYCLES_PLUS_2;\t\t\t\t\\\n\tGET_MEMORY8(arg, arg);\t\t\t\\\n\tINC_KPC_4;\n#else\n# define GET_LONG_RD()\t\t\t\t\\\n\tGET_3BYTE_ARG;\t\t\t\t\\\n\tCYCLES_PLUS_2;\t\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\t\t\\\n\tINC_KPC_4;\n#endif\n\n#undef GET_DLOC_IND_Y_RD\n#undef GET_DLOC_IND_Y_ADDR\n\n#ifdef ACC8\n# define GET_DLOC_IND_Y_RD()\t\t\t\\\n\tGET_DLOC_IND_Y_ADDR(0)\t\t\t\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DLOC_IND_Y_RD()\t\t\t\t\t\t\\\n\tGET_DLOC_IND_Y_ADDR(0)\t\t\t\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#define GET_DLOC_IND_Y_ADDR(is_write)\t\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tGET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, tmp1, 0);\t\\\n\ttmp1 += (dbank << 16);\t\t\t\t\t\t\\\n\targ = (tmp1 + yreg) & 0xffffff;\t\t\t\t\t\\\n\ttmp2 = (tmp1 & 0xffff00) | (arg & 0xff);\t\t\t\\\n\tif((psr & 0x10) && ((arg != tmp2) | is_write)) {\t\t\\\n\t\tGET_MEMORY8(tmp2, tmp1);\t\t\t\t\\\n\t} else if(((psr & 0x10) == 0) | (arg != tmp2) | is_write) {\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tINC_KPC_2;\n\n#undef GET_DLOC_IND_RD\n\n#ifdef ACC8\n# define GET_DLOC_IND_RD()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\\\n\t}\t\t\t\t\t\t\\\n\tGET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg, 0);\t\\\n\tGET_MEMORY8((dbank << 16) + arg, arg);\n#else\n# define GET_DLOC_IND_RD()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\\\n\t}\t\t\t\t\t\t\\\n\tGET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg, 0);\t\\\n\tGET_MEMORY16((dbank << 16) + arg, arg, 0);\n#endif\n\n#undef GET_DLOC_X_RD\n\n#ifdef ACC8\n# define GET_DLOC_X_RD()\t\t\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\t\t\t\\\n\targ = (arg + xreg + direct) & 0xffff;\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\t\\\n\t\tif((direct & 0xff) == 0) {\t\t\t\t\\\n\t\t\targ = (direct & 0xff00) | (arg & 0xff);\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DLOC_X_RD()\t\t\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\t\t\t\\\n\targ = (arg + xreg + direct) & 0xffff;\t\t\t\t\\\n\tif(!IS_ACC16 && (psr & 0x100)) {\t\t\t\t\\\n\t\tif((direct & 0xff) == 0) {\t\t\t\t\\\n\t\t\targ = (direct & 0xff00) | (arg & 0xff);\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tGET_MEMORY16(arg, arg, 1);\n#endif\n\n#undef GET_DLOC_X_RD_RMW\n\n#define GET_DLOC_X_RD_RMW()\t\t\t\t\t\t\\\n\tGET_DLOC_X_RD();\t\t\t\t\t\t\\\n\tGET_MEM_RMW();\n\n\n#undef GET_DISP8_S_IND_Y_RD\n\n#ifdef ACC8\n# define GET_DISP8_S_IND_Y_RD()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\\\n\tGET_DISP8_S_IND_Y_WR();\t\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DISP8_S_IND_Y_RD()\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\\\n\tGET_DISP8_S_IND_Y_WR();\t\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#undef GET_DLOC_L_IND_Y_RD\n\n#ifdef ACC8\n# define GET_DLOC_L_IND_Y_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DLOC_L_IND_Y_WR();\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_DLOC_L_IND_Y_RD()\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\\\n\tGET_DLOC_L_IND_Y_WR();\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#undef GET_ABS_INDEX_ADDR\n\n#define GET_ABS_INDEX_ADDR(index_reg, is_write)\t\t\t\\\n\tGET_2BYTE_ARG;\t\t\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\t\t\\\n\tINC_KPC_3;\t\t\t\t\t\t\\\n\ttmp1 = (dbank << 16) + arg;\t\t\t\t\\\n\targ = tmp1 + index_reg;\t\t\t\t\t\\\n\ttmp1 = (tmp1 & 0xffff00) + (arg & 0xff);\t\t\\\n\tif((psr & 0x10) && ((tmp1 != arg) | is_write)) {\t\\\n\t\tGET_MEMORY8(tmp1, tmp2);\t\t\t\\\n\t} else if(((psr & 0x10) == 0) | (tmp1 != arg) | is_write) {\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\t\\\n\t}\n\n#undef GET_ABS_Y_RD\n\n#ifdef ACC8\n# define GET_ABS_Y_RD()\t\t\t\t\\\n\tGET_ABS_INDEX_ADDR(yreg, 0);\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_ABS_Y_RD()\t\t\t\t\\\n\tGET_ABS_INDEX_ADDR(yreg, 0);\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#undef GET_ABS_X_RD\n#undef GET_ABS_X_RD_RMW\n\n#ifdef ACC8\n# define GET_ABS_X_RD()\t\t\t\t\t\\\n\tGET_ABS_INDEX_ADDR(xreg, 0);\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#define GET_ABS_X_RD_RMW()\t\t\t\t\\\n\tGET_ABS_INDEX_ADDR(xreg, 1);\t\t\t\\\n\tGET_MEMORY8(arg, arg);\t\t\t\t\\\n\tGET_MEM_RMW();\n#else\n# define GET_ABS_X_RD()\t\t\t\t\t\\\n\tGET_ABS_INDEX_ADDR(xreg, 0);\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#define GET_ABS_X_RD_RMW()\t\t\t\t\\\n\tGET_ABS_INDEX_ADDR(xreg, 1);\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\t\t\t\\\n\tGET_MEM_RMW();\n#endif\n\n#undef GET_LONG_X_RD\n\n#ifdef ACC8\n# define GET_LONG_X_RD()\t\t\t\\\n\tGET_3BYTE_ARG;\t\t\t\t\\\n\targ = (arg + xreg) & 0xffffff;\t\t\\\n\tINC_KPC_4;\t\t\t\t\\\n\tCYCLES_PLUS_2;\t\t\t\t\\\n\tGET_MEMORY8(arg, arg);\n#else\n# define GET_LONG_X_RD()\t\t\t\\\n\tGET_3BYTE_ARG;\t\t\t\t\\\n\targ = (arg + xreg) & 0xffffff;\t\t\\\n\tINC_KPC_4;\t\t\t\t\\\n\tCYCLES_PLUS_2;\t\t\t\t\\\n\tGET_MEMORY16(arg, arg, 0);\n#endif\n\n#define SET_NEG_ZERO16(val)\t\\\n\tzero = val;\t\t\\\n\tneg7 = (val) >> 8;\n\n#define SET_NEG_ZERO8(val)\t\\\n\tzero = val;\t\t\\\n\tneg7 = val;\n\n#define SET_CARRY8(val)\t\t\\\n\tpsr = (psr & ~1) + (((val) >> 8) & 1);\n\n#define SET_CARRY16(val)\t\\\n\tpsr = (psr & ~1) + (((val) >> 16) & 1);\n\n#define SET_INDEX_REG(src, dest)\t\t\\\n\tif(psr & 0x10) {\t\t\t\\\n\t\tdest = (src) & 0xff;\t\t\\\n\t\tSET_NEG_ZERO8(dest);\t\t\\\n\t} else {\t\t\t\t\\\n\t\tdest = (src) & 0xffff;\t\t\\\n\t\tSET_NEG_ZERO16(dest);\t\t\\\n\t}\n\n#define LD_INDEX_INST(index_reg, in_bank)\t\\\n\tif(psr & 0x10) {\t\t\t\\\n\t\tGET_MEMORY8(arg, arg);\t\t\\\n\t} else {\t\t\t\t\\\n\t\tGET_MEMORY16(arg, arg, in_bank);\\\n\t}\t\t\t\t\t\\\n\tSET_INDEX_REG(arg, index_reg);\n\n#define LDX_INST(in_bank)\tLD_INDEX_INST(xreg, in_bank)\n#define LDY_INST(in_bank)\tLD_INDEX_INST(yreg, in_bank)\n\n#undef ORA_INST\n\n#ifdef ACC8\n# define ORA_INST()\t\t\t\t\\\n\ttmp1 = (acc | arg) & 0xff;\t\t\\\n\tacc = (acc & 0xff00) + tmp1;\t\t\\\n\tSET_NEG_ZERO8(tmp1);\n#else\n# define ORA_INST()\t\t\t\t\\\n\tacc = (acc | arg);\t\t\t\\\n\tSET_NEG_ZERO16(acc);\n#endif\n\n#undef AND_INST\n\n#ifdef ACC8\n# define AND_INST()\t\t\t\t\\\n\ttmp1 = (acc & arg) & 0xff;\t\t\\\n\tacc = (acc & 0xff00) + tmp1;\t\t\\\n\tSET_NEG_ZERO8(tmp1);\n#else\n# define AND_INST()\t\t\t\t\\\n\tacc = (acc & arg);\t\t\t\\\n\tSET_NEG_ZERO16(acc);\n#endif\n\n#undef EOR_INST\n\n#ifdef ACC8\n# define EOR_INST()\t\t\t\t\\\n\ttmp1 = (acc ^ arg) & 0xff;\t\t\\\n\tacc = (acc & 0xff00) + tmp1;\t\t\\\n\tSET_NEG_ZERO8(tmp1);\n#else\n# define EOR_INST()\t\t\t\t\\\n\tacc = (acc ^ arg);\t\t\t\\\n\tSET_NEG_ZERO16(acc);\n#endif\n\n#undef LDA_INST\n\n#ifdef ACC8\n# define LDA_INST()\t\t\t\t\\\n\tacc = (acc & 0xff00) + (arg & 0xff);\t\\\n\tSET_NEG_ZERO8(arg & 0xff);\n#else\n# define LDA_INST()\t\t\t\t\\\n\tacc = (arg & 0xffff);\t\t\t\\\n\tSET_NEG_ZERO16(acc);\n#endif\n\n#undef ADC_INST\n\n#ifdef ACC8\n# define ADC_INST()\t\t\t\t\t\t\\\n\ttmp1 = do_adc_sbc8(acc & 0xff, arg & 0xff, psr, 0);\t\\\n\tacc = (acc & 0xff00) + (tmp1 & 0xff);\t\t\t\\\n\tpsr = (tmp1 >> 16);\t\t\t\t\t\\\n\tzero = !(psr & 0x2);\t\t\t\t\t\\\n\tneg7 = psr;\n#else\n# define ADC_INST()\t\t\t\t\t\t\\\n\ttmp1 = do_adc_sbc16(acc, arg & 0xffff, psr, 0);\t\t\\\n\tacc = (tmp1 & 0xffff);\t\t\t\t\t\\\n\tpsr = (tmp1 >> 16);\t\t\t\t\t\\\n\tzero = !(psr & 0x2);\t\t\t\t\t\\\n\tneg7 = psr;\n#endif\n\n#undef SBC_INST\n\n#ifdef ACC8\n# define SBC_INST()\t\t\t\t\t\t\\\n\ttmp1 = do_adc_sbc8(acc & 0xff, arg & 0xff, psr, 1);\t\\\n\tacc = (acc & 0xff00) + (tmp1 & 0xff);\t\t\t\\\n\tpsr = (tmp1 >> 16);\t\t\t\t\t\\\n\tzero = !(psr & 0x2);\t\t\t\t\t\\\n\tneg7 = psr;\n#else\n# define SBC_INST()\t\t\t\t\t\t\\\n\ttmp1 = do_adc_sbc16(acc, arg & 0xffff, psr, 1);\t\t\\\n\tacc = (tmp1 & 0xffff);\t\t\t\t\t\\\n\tpsr = (tmp1 >> 16);\t\t\t\t\t\\\n\tzero = !(psr & 0x2);\t\t\t\t\t\\\n\tneg7 = psr;\n#endif\n\n\n#undef CMP_INST\n\n#ifdef ACC8\n# define CMP_INST()\t\t\t\t\t\\\n\targ = (acc & 0xff) + (0x100 - arg);\t\t\\\n\tSET_CARRY8(arg);\t\t\t\t\\\n\targ = arg & 0xff;\t\t\t\t\\\n\tSET_NEG_ZERO8(arg & 0xff);\n#else\n# define CMP_INST()\t\t\t\t\t\\\n\targ = (acc & 0xffff) + (0x10000 - arg);\t\t\\\n\tSET_CARRY16(arg);\t\t\t\t\\\n\targ = arg & 0xffff;\t\t\t\t\\\n\tSET_NEG_ZERO16(arg & 0xffff);\n#endif\n\n#undef BIT_INST\n\n#ifdef ACC8\n# define BIT_INST()\t\t\t\t\\\n\tneg7 = arg;\t\t\t\t\\\n\tzero = (acc & arg & 0xff);\t\t\\\n\tpsr = (psr & (~0x40)) | (arg & 0x40);\n#else\n# define BIT_INST()\t\t\t\t\\\n\tneg7 = arg >> 8;\t\t\t\\\n\tzero = (acc & arg & 0xffff);\t\t\\\n\tpsr = (psr & (~0x40)) | ((arg >> 8) & 0x40);\n#endif\n\n#undef STA_INST\n\n#ifdef ACC8\n# define STA_INST(in_bank)\t\t\t\\\n\tSET_MEMORY8(arg, acc);\n#else\n# define STA_INST(in_bank)\t\t\t\\\n\tSET_MEMORY16(arg, acc, in_bank);\n#endif\n\n#undef TSB_INST\n\n#ifdef ACC8\n# define TSB_INST(in_bank)\t\t\t\\\n\targ = arg & 0xff;\t\t\t\\\n\ttmp1 = arg | acc;\t\t\t\\\n\tzero = arg & acc;\t\t\t\\\n\tSET_MEMORY8(addr_latch, tmp1);\n#else\n# define TSB_INST(in_bank)\t\t\t\\\n\ttmp1 = arg | acc;\t\t\t\\\n\tzero = arg & acc;\t\t\t\\\n\tSET_MEMORY16(addr_latch, tmp1, in_bank);\n#endif\n\n#undef ASL_INST\n\n#ifdef ACC8\n# define ASL_INST(in_bank)\t\t\t\\\n\tpsr = (psr & 0x1fe) + ((arg >> 7) & 1);\t\\\n\ttmp1 = (arg << 1) & 0xff;\t\t\\\n\tSET_NEG_ZERO8(tmp1);\t\t\t\\\n\tSET_MEMORY8(addr_latch, tmp1);\n#else\n# define ASL_INST(in_bank)\t\t\t\\\n\tpsr = (psr & 0x1fe) + ((arg >> 15) & 1);\\\n\ttmp1 = (arg << 1) & 0xffff;\t\t\\\n\tSET_NEG_ZERO16(tmp1);\t\t\t\\\n\tSET_MEMORY16(addr_latch, tmp1, in_bank);\n#endif\n\n#undef ROL_INST\n\n#ifdef ACC8\n# define ROL_INST(in_bank)\t\t\t\\\n\targ = arg & 0xff;\t\t\t\\\n\targ = (arg << 1) | (psr & 1);\t\t\\\n\tSET_MEMORY8(addr_latch, arg);\t\t\\\n\tSET_NEG_ZERO8(arg & 0xff);\t\t\\\n\tSET_CARRY8(arg);\n#else\n# define ROL_INST(in_bank)\t\t\t\\\n\targ = (arg << 1) | (psr & 1);\t\t\\\n\tSET_MEMORY16(addr_latch, arg, in_bank);\t\\\n\tSET_NEG_ZERO16(arg & 0xffff);\t\t\\\n\tSET_CARRY16(arg);\n#endif\n\n#undef LSR_INST\n\n#ifdef ACC8\n# define LSR_INST(in_bank)\t\t\t\t\\\n\tSET_CARRY8(arg << 8);\t\t\t\t\\\n\targ = (arg >> 1) & 0x7f;\t\t\t\\\n\tSET_NEG_ZERO8(arg);\t\t\t\t\\\n\tSET_MEMORY8(addr_latch, arg);\n#else\n# define LSR_INST(in_bank)\t\t\t\t\\\n\tSET_CARRY16(arg << 16);\t\t\t\t\\\n\targ = (arg >> 1) & 0x7fff;\t\t\t\\\n\tSET_NEG_ZERO16(arg);\t\t\t\t\\\n\tSET_MEMORY16(addr_latch, arg, in_bank);\n#endif\n\n#undef ROR_INST\n\n#ifdef ACC8\n# define ROR_INST(in_bank)\t\t\t\t\\\n\ttmp1 = psr & 1;\t\t\t\t\t\\\n\tSET_CARRY8(arg << 8);\t\t\t\t\\\n\targ = ((arg >> 1) & 0x7f) | (tmp1 << 7);\t\\\n\tSET_MEMORY8(addr_latch, arg);\t\t\t\\\n\tSET_NEG_ZERO8(arg);\n#else\n# define ROR_INST(in_bank)\t\t\t\t\\\n\ttmp1 = psr & 1;\t\t\t\t\t\\\n\tSET_CARRY16(arg << 16);\t\t\t\t\\\n\targ = ((arg >> 1) & 0x7fff) | (tmp1 << 15);\t\\\n\tSET_MEMORY16(addr_latch, arg, in_bank);\t\t\\\n\tSET_NEG_ZERO16(arg);\n#endif\n\n#undef TRB_INST\n\n#ifdef ACC8\n# define TRB_INST(in_bank)\t\t\t\\\n\targ = arg & 0xff;\t\t\t\\\n\ttmp1 = arg & ~acc;\t\t\t\\\n\tzero = arg & acc;\t\t\t\\\n\tSET_MEMORY8(addr_latch, tmp1);\n#else\n# define TRB_INST(in_bank)\t\t\t\\\n\ttmp1 = arg & ~acc;\t\t\t\\\n\tzero = arg & acc;\t\t\t\\\n\tSET_MEMORY16(addr_latch, tmp1, in_bank);\n#endif\n\n#undef DEC_INST\n\n#ifdef ACC8\n# define DEC_INST(in_bank)\t\t\t\\\n\targ = (arg - 1) & 0xff;\t\t\t\\\n\tSET_MEMORY8(addr_latch, arg);\t\t\\\n\tSET_NEG_ZERO8(arg);\n#else\n# define DEC_INST(in_bank)\t\t\t\\\n\targ = (arg - 1) & 0xffff;\t\t\\\n\tSET_MEMORY16(addr_latch, arg, in_bank);\t\\\n\tSET_NEG_ZERO16(arg);\n#endif\n\n#undef INC_INST\n\n#ifdef ACC8\n# define INC_INST(in_bank)\t\t\t\\\n\targ = (arg + 1) & 0xff;\t\t\t\\\n\tSET_MEMORY8(addr_latch, arg);\t\t\\\n\tSET_NEG_ZERO8(arg);\n#else\n# define INC_INST(in_bank)\t\t\t\\\n\targ = (arg + 1) & 0xffff;\t\t\\\n\tSET_MEMORY16(addr_latch, arg, in_bank);\t\\\n\tSET_NEG_ZERO16(arg);\n#endif\n\n#undef STZ_INST\n\n#ifdef ACC8\n# define STZ_INST(in_bank)\t\t\t\t\\\n\tSET_MEMORY8(arg, 0);\n#else\n# define STZ_INST(in_bank)\t\t\t\t\\\n\tSET_MEMORY16(arg, 0, in_bank);\n#endif\n\n#undef BRANCH_DISP8\n\n#define BRANCH_DISP8(cond)\t\t\t\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\t\t\t\\\n\ttmp2 = kpc & 0xff0000;\t\t\t\t\t\\\n\tkpc += 2;\t\t\t\t\t\t\\\n\ttmp1 = kpc;\t\t\t\t\t\t\\\n\tif(cond) {\t\t\t\t\t\t\\\n\t\tkpc = kpc + arg - ((arg & 0x80) << 1);\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\t\t\\\n\t\tif((tmp1 ^ kpc) & psr & 0x100) {\t\t\\\n\t\t\tCYCLES_PLUS_1;\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\\\n\tkpc = tmp2 + (kpc & 0xffff);\n\n#undef STY_INST\n#undef STX_INST\n\n#define STY_INST(in_bank)\t\t\t\\\n\tif(psr & 0x10) {\t\t\t\\\n\t\tSET_MEMORY8(arg, yreg);\t\t\\\n\t} else {\t\t\t\t\\\n\t\tSET_MEMORY16(arg, yreg, in_bank);\\\n\t}\n#define STX_INST(in_bank)\t\t\t\\\n\tif(psr & 0x10) {\t\t\t\\\n\t\tSET_MEMORY8(arg, xreg);\t\t\\\n\t} else {\t\t\t\t\\\n\t\tSET_MEMORY16(arg, xreg, in_bank);\\\n\t}\n\n#define C_LDX_ABS_Y()\t\t\t\\\n\tGET_ABS_INDEX_ADDR(yreg, 0);\t\\\n\tLDX_INST(0);\n\n#define C_LDY_ABS_X()\t\t\t\\\n\tGET_ABS_INDEX_ADDR(xreg, 0);\t\\\n\tLDY_INST(0);\n\n#define C_LDX_ABS()\t\t\\\n\tGET_ABS_ADDR();\t\t\\\n\tLDX_INST(0);\n\n#define C_LDY_ABS()\t\t\\\n\tGET_ABS_ADDR();\t\t\\\n\tLDY_INST(0);\n\n#define C_LDX_DLOC()\t\t\\\n\tGET_DLOC_ADDR();\t\\\n\tLDX_INST(1);\n\n#define C_LDY_DLOC()\t\t\\\n\tGET_DLOC_ADDR();\t\\\n\tLDY_INST(1);\n\n#define C_LDY_DLOC_X()\t\t\\\n\tGET_DLOC_X_ADDR();\t\\\n\tLDY_INST(1);\n\n#define C_LDX_DLOC_Y()\t\t\\\n\tGET_DLOC_Y_ADDR();\t\\\n\tLDX_INST(1);\n\n#define CP_INDEX_VAL(index_reg)\t\\\n\targ = 0x100 - arg + index_reg;\t\\\n\tif((psr & 0x10) == 0) {\t\t\\\n\t\targ += 0xff00;\t\t\\\n\t\tSET_NEG_ZERO16(arg & 0xffff);\t\\\n\t\tSET_CARRY16(arg);\t\\\n\t} else {\t\t\t\\\n\t\tSET_NEG_ZERO8(arg & 0xff);\\\n\t\tSET_CARRY8(arg);\t\\\n\t}\n\n#define CP_INDEX_LOAD(index_reg, in_bank)\t\\\n\tif((psr & 0x10) != 0) {\t\t\t\\\n\t\tGET_MEMORY8(arg, arg);\t\t\\\n\t} else {\t\t\t\t\\\n\t\tGET_MEMORY16(arg, arg, in_bank);\\\n\t}\t\t\t\t\t\\\n\tCP_INDEX_VAL(index_reg)\n\n#define CPX_INST(in_bank)\t\t\\\n\tCP_INDEX_LOAD(xreg, in_bank);\n\n#define CPY_INST(in_bank)\t\t\\\n\tCP_INDEX_LOAD(yreg, in_bank);\n\n#define C_CPX_IMM()\t\t\\\n\tINC_KPC_2;\t\t\\\n\tif((psr & 0x10) == 0) {\t\\\n\t\tGET_2BYTE_ARG;\t\\\n\t\tCYCLES_PLUS_1;\t\\\n\t\tINC_KPC_1;\t\\\n\t} else {\t\t\\\n\t\tGET_1BYTE_ARG;\t\\\n\t}\t\t\t\\\n\tCP_INDEX_VAL(xreg);\n\n#define C_CPY_IMM()\t\t\\\n\tINC_KPC_2;\t\t\\\n\tif((psr & 0x10) == 0) {\t\\\n\t\tGET_2BYTE_ARG;\t\\\n\t\tCYCLES_PLUS_1;\t\\\n\t\tINC_KPC_1;\t\\\n\t} else {\t\t\\\n\t\tGET_1BYTE_ARG;\t\\\n\t}\t\t\t\\\n\tCP_INDEX_VAL(yreg);\n\n#define C_CPX_DLOC()\t\t\\\n\tGET_DLOC_ADDR();\t\\\n\tCPX_INST(1);\n\n#define C_CPY_DLOC()\t\t\\\n\tGET_DLOC_ADDR();\t\\\n\tCPY_INST(1);\n\n#define C_CPX_ABS()\t\t\\\n\tGET_ABS_ADDR();\t\t\\\n\tCPX_INST(0);\n\n#define C_CPY_ABS()\t\t\\\n\tGET_ABS_ADDR();\t\t\\\n\tCPY_INST(0);\n\n"
  },
  {
    "path": "upstream/kegs/src/dependency",
    "content": "adb.o: adb.c defc.h defcomm.h iwm.h protos.h protos_base.h\nengine_c.o: engine_c.c defc.h defcomm.h iwm.h protos.h protos_base.h size_c.h op_routs.h engine.h defs_instr.h instable.h\nclock.o: clock.c defc.h defcomm.h iwm.h protos.h protos_base.h\ncompile_time.o: compile_time.c\nconfig.o: config.c defc.h defcomm.h iwm.h protos.h protos_base.h config.h win_dirent.h\ndebugger.o: debugger.c defc.h defcomm.h iwm.h protos.h protos_base.h disas.h\nscc.o: scc.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h\nscc_socket_driver.o: scc_socket_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h\nscc_windriver.o: scc_windriver.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h\nscc_unixdriver.o: scc_unixdriver.c defc.h defcomm.h iwm.h protos.h protos_base.h scc.h\niwm.o: iwm.c defc.h defcomm.h iwm.h protos.h protos_base.h\njoystick_driver.o: joystick_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h\nmoremem.o: moremem.c defc.h defcomm.h iwm.h protos.h protos_base.h\npaddles.o: paddles.c defc.h defcomm.h iwm.h protos.h protos_base.h\nmockingboard.o: mockingboard.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h\nsim65816.o: sim65816.c defc.h defcomm.h iwm.h protos.h protos_base.h\nsmartport.o: smartport.c defc.h defcomm.h iwm.h protos.h protos_base.h\ndoc.o: doc.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h\nsound.o: sound.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h\nsound_driver.o: sound_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h\nwoz.o: woz.c defc.h defcomm.h iwm.h protos.h protos_base.h\nunshk.o: unshk.c defc.h defcomm.h iwm.h protos.h protos_base.h\nundeflate.o: undeflate.c defc.h defcomm.h iwm.h protos.h protos_base.h\ndynapro.o: dynapro.c defc.h defcomm.h iwm.h protos.h protos_base.h win_dirent.h\ndyna_type.o: dyna_type.c defc.h defcomm.h iwm.h protos.h protos_base.h\ndyna_filt.o: dyna_filt.c defc.h defcomm.h iwm.h protos.h protos_base.h\ndyna_validate.o: dyna_validate.c defc.h defcomm.h iwm.h protos.h protos_base.h\napplesingle.o: applesingle.c defc.h defcomm.h iwm.h protos.h protos_base.h\nvideo.o: video.c defc.h defcomm.h iwm.h protos.h protos_base.h kegsfont.h\nvoc.o: voc.c defc.h defcomm.h iwm.h protos.h protos_base.h\nmacsnd_driver.o: macsnd_driver.c defc.h defcomm.h iwm.h protos.h protos_base.h sound.h\n"
  },
  {
    "path": "upstream/kegs/src/disas.h",
    "content": "const char rcsid_disas_h[] = \"@(#)$KmKId: disas.h,v 1.12 2020-06-17 02:25:23+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2020 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\nenum {\n\tABS = 1,\n\tABSX,\n\tABSY,\n\tABSLONG,\n\tABSIND,\n\tABSXIND,\n\tIMPLY,\n\tACCUM,\n\tIMMED,\n\tJUST8,\n\tDLOC,\n\tDLOCX,\n\tDLOCY,\n\tLONG,\n\tLONGX,\n\tDLOCIND,\n\tDLOCINDY,\n\tDLOCXIND,\n\tDLOCBRAK,\n\tDLOCBRAKY,\n\tDISP8,\n\tDISP8S,\n\tDISP8SINDY,\n\tDISP16,\n\tMVPMVN,\n\tREPVAL,\n\tSEPVAL\n};\n\n\nconst char * const disas_opcodes[256] = {\n\t\"BRK\", \"ORA\", \"COP\", \"ORA\", \"TSB\", \"ORA\", \"ASL\", \"ORA\",  /* 00-07 */\n\t\"PHP\", \"ORA\", \"ASL\", \"PHD\", \"TSB\", \"ORA\", \"ASL\", \"ORA\",  /* 08-0f */\n\t\"BPL\", \"ORA\", \"ORA\", \"ORA\", \"TRB\", \"ORA\", \"ASL\", \"ORA\",  /* 10-17 */\n\t\"CLC\", \"ORA\", \"INC\", \"TCS\", \"TRB\", \"ORA\", \"ASL\", \"ORA\",  /* 18-1f */\n\t\"JSR\", \"AND\", \"JSL\", \"AND\", \"BIT\", \"AND\", \"ROL\", \"AND\",  /* 20-27 */\n\t\"PLP\", \"AND\", \"ROL\", \"PLD\", \"BIT\", \"AND\", \"ROL\", \"AND\",  /* 28-2f */\n\t\"BMI\", \"AND\", \"AND\", \"AND\", \"BIT\", \"AND\", \"ROL\", \"AND\",  /* 30-37 */\n\t\"SEC\", \"AND\", \"DEC\", \"TSC\", \"BIT\", \"AND\", \"ROL\", \"AND\",  /* 38-3f */\n\t\"RTI\", \"EOR\", \"WDM\", \"EOR\", \"MVP\", \"EOR\", \"LSR\", \"EOR\",  /* 40-47 */\n\t\"PHA\", \"EOR\", \"LSR\", \"PHK\", \"JMP\", \"EOR\", \"LSR\", \"EOR\",  /* 48-4f */\n\t\"BVC\", \"EOR\", \"EOR\", \"EOR\", \"MVN\", \"EOR\", \"LSR\", \"EOR\",  /* 50-57 */\n\t\"CLI\", \"EOR\", \"PHY\", \"TCD\", \"JMP\", \"EOR\", \"LSR\", \"EOR\",  /* 58-5f */\n\t\"RTS\", \"ADC\", \"PER\", \"ADC\", \"STZ\", \"ADC\", \"ROR\", \"ADC\",  /* 60-67 */\n\t\"PLA\", \"ADC\", \"ROR\", \"RTL\", \"JMP\", \"ADC\", \"ROR\", \"ADC\",  /* 68-6f */\n\t\"BVS\", \"ADC\", \"ADC\", \"ADC\", \"STZ\", \"ADC\", \"ROR\", \"ADC\",  /* 70-77 */\n\t\"SEI\", \"ADC\", \"PLY\", \"TDC\", \"JMP\", \"ADC\", \"ROR\", \"ADC\",  /* 78-7f */\n\t\"BRA\", \"STA\", \"BRL\", \"STA\", \"STY\", \"STA\", \"STX\", \"STA\",  /* 80-87 */\n\t\"DEY\", \"BIT\", \"TXA\", \"PHB\", \"STY\", \"STA\", \"STX\", \"STA\",  /* 88-8f */\n\t\"BCC\", \"STA\", \"STA\", \"STA\", \"STY\", \"STA\", \"STX\", \"STA\",  /* 90-97 */\n\t\"TYA\", \"STA\", \"TXS\", \"TXY\", \"STZ\", \"STA\", \"STZ\", \"STA\",  /* 98-9f */\n\t\"LDY\", \"LDA\", \"LDX\", \"LDA\", \"LDY\", \"LDA\", \"LDX\", \"LDA\",  /* a0-a7 */\n\t\"TAY\", \"LDA\", \"TAX\", \"PLB\", \"LDY\", \"LDA\", \"LDX\", \"LDA\",  /* a8-af */\n\t\"BCS\", \"LDA\", \"LDA\", \"LDA\", \"LDY\", \"LDA\", \"LDX\", \"LDA\",  /* b0-b7 */\n\t\"CLV\", \"LDA\", \"TSX\", \"TYX\", \"LDY\", \"LDA\", \"LDX\", \"LDA\",  /* b8-bf */\n\t\"CPY\", \"CMP\", \"REP\", \"CMP\", \"CPY\", \"CMP\", \"DEC\", \"CMP\",  /* c0-c7 */\n\t\"INY\", \"CMP\", \"DEX\", \"WAI\", \"CPY\", \"CMP\", \"DEC\", \"CMP\",  /* c8-cf */\n\t\"BNE\", \"CMP\", \"CMP\", \"CMP\", \"PEI\", \"CMP\", \"DEC\", \"CMP\",  /* d0-d7 */\n\t\"CLD\", \"CMP\", \"PHX\", \"STP\", \"JML\", \"CMP\", \"DEC\", \"CMP\",  /* d8-df */\n\t\"CPX\", \"SBC\", \"SEP\", \"SBC\", \"CPX\", \"SBC\", \"INC\", \"SBC\",  /* e0-e7 */\n\t\"INX\", \"SBC\", \"NOP\", \"XBA\", \"CPX\", \"SBC\", \"INC\", \"SBC\",  /* e8-ef */\n\t\"BEQ\", \"SBC\", \"SBC\", \"SBC\", \"PEA\", \"SBC\", \"INC\", \"SBC\",  /* f0-f7 */\n\t\"SED\", \"SBC\", \"PLX\", \"XCE\", \"JSR\", \"SBC\", \"INC\", \"SBC\",  /* f8-ff */\n};\n\n\nconst word32 disas_types[256] = {\n\tJUST8+0x100, DLOCXIND+0x100,\t\t/* 00-01 */\n\tJUST8+0x100, DISP8S+0x100,\t\t/* 02-03 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* 04-05 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* 06-07 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* 08-9 */\n\tACCUM+0x000, IMPLY+0x000,\t\t/* 0a-b */\n\tABS+0x200, ABS+0x200,\t\t\t/* c-d */\n\tABS+0x200, LONG+0x300,\t\t\t/* e-f */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* 10-11 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* 12-13 */\n\tDLOC+0x100, DLOCX+0x100,\t\t/* 14-15 */\n\tDLOCX+0x100, DLOCBRAKY+0x100,\t\t/* 16-17 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* 18-19 */\n\tACCUM+0x000, IMPLY+0x000,\t\t/* 1a-1b */\n\tABS+0x200, ABSX+0x200,\t\t\t/* 1c-1d */\n\tABSX+0x200, LONGX+0x300,\t\t/* 1e-1f */\n\tABS+0x200, DLOCXIND+0x100,\t\t/* 20-21 */\n\tABSLONG+0x300, DISP8S+0x100,\t\t/* 22-23 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* 24-25 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* 26-27 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* 28-29 */\n\tACCUM+0x000, IMPLY+0x000,\t\t/* 2a-2b */\n\tABS+0x200, ABS+0x200,\t\t\t/* 2c-2d */\n\tABS+0x200, LONG+0x300,\t\t\t/* 2e-2f */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* 30-31 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* 32-33 */\n\tDLOCX+0x100, DLOCX+0x100,\t\t/* 34-35 */\n\tDLOCX+0x100, DLOCBRAKY+0x100,\t\t/* 36-37 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* 38-39 */\n\tACCUM+0x000, IMPLY+0x000,\t\t/* 3a-3b */\n\tABSX+0x200, ABSX+0x200,\t\t\t/* 3c-3d */\n\tABSX+0x200, LONGX+0x300,\t\t/* 3e-3f */\n\tIMPLY+0x000, DLOCXIND+0x100,\t\t/* 40-41 */\n\tJUST8+0x100, DISP8S+0x100,\t\t/* 42-43 */\n\tMVPMVN+0x200, DLOC+0x100,\t\t/* 44-45 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* 46-47 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* 48-49 */\n\tACCUM+0x000, IMPLY+0x000,\t\t/* 4a-4b */\n\tABS+0x200, ABS+0x200,\t\t\t/* 4c-4d */\n\tABS+0x200, LONG+0x300,\t\t\t/* 4e-4f */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* 50-51 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* 52-53 */\n\tMVPMVN+0x200, DLOCX+0x100,\t\t/* 54-55 */\n\tDLOCX+0x100, DLOCBRAKY+0x100,\t\t/* 56-57 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* 58-59 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* 5a-5b */\n\tLONG+0x300, ABSX+0x200,\t\t\t/* 5c-5d */\n\tABSX+0x200, LONGX+0x300,\t\t/* 5e-5f */\n\tIMPLY+0x000, DLOCXIND+0x100,\t\t/* 60-61 */\n\tDISP16+0x200, DISP8S+0x100,\t\t/* 62-63 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* 64-65 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* 66-67 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* 68-69 */\n\tACCUM+0x000, IMPLY+0x000,\t\t/* 6a-6b */\n\tABSIND+0x200, ABS+0x200,\t\t/* 6c-6d */\n\tABS+0x200, LONG+0x300,\t\t\t/* 6e-6f */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* 70-71 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* 72-73 */\n\tDLOCX+0x100, DLOCX+0x100,\t\t/* 74-75 */\n\tDLOCX+0x100, DLOCBRAKY+0x100,\t\t/* 76-77 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* 78-79 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* 7a-7b */\n\tABSXIND+0x200, ABSX+0x200,\t\t/* 7c-7d */\n\tABSX+0x200, LONGX+0x300,\t\t/* 7e-7f */\n\tDISP8+0x100, DLOCXIND+0x100,\t\t/* 80-81 */\n\tDISP16+0x200, DISP8S+0x100,\t\t/* 82-83 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* 84-85 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* 86-87 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* 88-89 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* 8a-8b */\n\tABS+0x200, ABS+0x200,\t\t\t/* 8c-8d */\n\tABS+0x200, LONG+0x300,\t\t\t/* 8e-8f */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* 90-91 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* 92-93 */\n\tDLOCX+0x100, DLOCX+0x100,\t\t/* 94-95 */\n\tDLOCY+0x100, DLOCBRAKY+0x100,\t\t/* 96-97 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* 98-99 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* 9a-9b */\n\tABS+0x200, ABSX+0x200,\t\t\t/* 9c-9d */\n\tABSX+0x200, LONGX+0x300,\t\t/* 9e-9f */\n\tIMMED+0x500, DLOCXIND+0x100,\t\t/* a0-a1 */\n\tIMMED+0x500, DISP8S+0x100,\t\t/* a2-a3 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* a4-a5 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* a6-a7 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* a8-a9 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* aa-ab */\n\tABS+0x200, ABS+0x200,\t\t\t/* ac-ad */\n\tABS+0x200, LONG+0x300,\t\t\t/* ae-af */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* b0-b1 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* b2-b3 */\n\tDLOCX+0x100, DLOCX+0x100,\t\t/* b4-b5 */\n\tDLOCY+0x100, DLOCBRAKY+0x100,\t\t/* b6-b7 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* b8-b9 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* ba-bb */\n\tABSX+0x200, ABSX+0x200,\t\t\t/* bc-bd */\n\tABSY+0x200, LONGX+0x300,\t\t/* be-bf */\n\tIMMED+0x500, DLOCXIND+0x100,\t\t/* c0-c1 */\n\tREPVAL+0x100, DISP8S+0x100,\t\t/* c2-c3 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* c4-c5 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* c6-c7 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* c8-c9 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* ca-cb */\n\tABS+0x200, ABS+0x200,\t\t\t/* cc-cd */\n\tABS+0x200, LONG+0x300,\t\t\t/* ce-cf */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* d0-d1 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* d2-d3 */\n\tDLOC+0x100, DLOCX+0x100,\t\t/* d4-d5 */\n\tDLOCX+0x100, DLOCBRAKY+0x100,\t\t/* d6-d7 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* d8-d9 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* da-db */\n\tABSIND+0x200, ABSX+0x200,\t\t/* dc-dd */\n\tABSX+0x200, LONGX+0x300,\t\t/* de-df */\n\tIMMED+0x500, DLOCXIND+0x100,\t\t/* e0-e1 */\n\tSEPVAL+0x100, DISP8S+0x100,\t\t/* e2-e3 */\n\tDLOC+0x100, DLOC+0x100,\t\t\t/* e4-e5 */\n\tDLOC+0x100, DLOCBRAK+0x100,\t\t/* e6-e7 */\n\tIMPLY+0x000, IMMED+0x400,\t\t/* e8-e9 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* ea-eb */\n\tABS+0x200, ABS+0x200,\t\t\t/* ec-ed */\n\tABS+0x200, LONG+0x300,\t\t\t/* ee-ef */\n\tDISP8+0x100, DLOCINDY+0x100,\t\t/* f0-f1 */\n\tDLOCIND+0x100, DISP8SINDY+0x100,\t/* f2-f3 */\n\tIMMED+0x200, DLOCX+0x100,\t\t/* f4-f5 */\n\tDLOCX+0x100, DLOCBRAKY+0x100,\t\t/* f6-f7 */\n\tIMPLY+0x000, ABSY+0x200,\t\t/* f8-f9 */\n\tIMPLY+0x000, IMPLY+0x000,\t\t/* fa-fb */\n\tABSXIND+0x200, ABSX+0x200,\t\t/* fc-fd */\n\tABSX+0x200, LONGX+0x300,\t\t/* fe-ff */\n};\n\n"
  },
  {
    "path": "upstream/kegs/src/doc.c",
    "content": "const char rcsid_doc_c[] = \"@(#)$KmKId: doc.c,v 1.2 2023-06-05 18:59:51+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// Ensoniq 5503 DOC routines\n// The Ensoniq DOC is controlled through the Sound GLU at $C03C-$C03F,\n//  where $C03E-$C03F are a 16-bit pointer to \"what\" to access, $C03D is\n//  the data register, and $C03C is a control register with volume.\n// The DOC operates at 894.886KHZ (7M clock divided by 8).  It visits each\n//  oscillator (up to 32) once per clock, plus 2 extra clocks for DOC RAM\n//  refresh.\n// KEGS cheats and pretends the DOC runs at 48KHz, and visits all oscillators\n//  each \"sample\".  KEGS adjusts the internal accumulators so the right\n//  frequency is achieved.  This allows the sample calculations to be\n//  greatly simplified, and achieves higher fidelity when all 32 osc are\n//  enabled (which is generally how most Apple IIgs code works).\n\n#include \"defc.h\"\n#include \"sound.h\"\n\n#define DOC_LOG(a,b,c,d)\n\nextern int Verbose;\nextern int g_use_shmem;\nextern word32 g_vbl_count;\nextern int g_preferred_rate;\n\nextern word32 g_c03ef_doc_ptr;\n\nextern dword64 g_last_vbl_dfcyc;\n\nbyte doc_ram[0x10000 + 16];\n\nword32 g_doc_sound_ctl = 0;\nword32 g_doc_saved_val = 0;\nint\tg_doc_num_osc_en = 1;\ndouble\tg_drecip_osc_en_plus_2 = 1.0 / (double)(1 + 2);\n\nint\tg_doc_vol = 8;\nint\tg_doc_saved_ctl = 0;\nint\tg_num_osc_interrupting = 0;\nDoc_reg g_doc_regs[32];\n\nword32 doc_reg_e0 = 0xff;\n\nextern double g_drecip_audio_rate;\nextern double g_dsamps_per_dfcyc;\nextern double g_fcyc_per_samp;\n\n/* local function prototypes */\nvoid doc_write_ctl_reg(dword64 dfcyc, int osc, int val);\n\n#define DOC_SCAN_RATE\t(DCYCS_28_MHZ/32.0)\n\n#define UPDATE_G_DCYCS_PER_DOC_UPDATE(osc_en)\t\t\t\t\\\n\tg_drecip_osc_en_plus_2 = 1.0 / (double)(osc_en + 2);\n\n#define SND_PTR_SHIFT\t\t14\n#define SND_PTR_SHIFT_DBL\t((double)(1 << SND_PTR_SHIFT))\n\nvoid\ndoc_init()\n{\n\tDoc_reg\t*rptr;\n\tint\ti;\n\n\tfor(i = 0; i < 32; i++) {\n\t\trptr = &(g_doc_regs[i]);\n\t\trptr->dsamp_ev = 0.0;\n\t\trptr->dsamp_ev2 = 0.0;\n\t\trptr->complete_dsamp = 0.0;\n\t\trptr->samps_left = 0;\n\t\trptr->cur_acc = 0;\n\t\trptr->cur_inc = 0;\n\t\trptr->cur_start = 0;\n\t\trptr->cur_end = 0;\n\t\trptr->cur_mask = 0;\n\t\trptr->size_bytes = 0;\n\t\trptr->event = 0;\n\t\trptr->running = 0;\n\t\trptr->has_irq_pending = 0;\n\t\trptr->freq = 0;\n\t\trptr->vol = 0;\n\t\trptr->waveptr = 0;\n\t\trptr->ctl = 1;\n\t\trptr->wavesize = 0;\n\t\trptr->last_samp_val = 0;\n\t}\n}\nvoid\ndoc_reset(dword64 dfcyc)\n{\n\tint\ti;\n\n\tfor(i = 0; i < 32; i++) {\n\t\tdoc_write_ctl_reg(dfcyc, i, g_doc_regs[i].ctl | 1);\n\t\tdoc_reg_e0 = 0xff;\n\t\tif(g_doc_regs[i].has_irq_pending) {\n\t\t\thalt_printf(\"reset: has_irq[%02x] = %d\\n\", i,\n\t\t\t\tg_doc_regs[i].has_irq_pending);\n\t\t}\n\t\tg_doc_regs[i].has_irq_pending = 0;\n\t}\n\tif(g_num_osc_interrupting) {\n\t\thalt_printf(\"reset: num_osc_int:%d\\n\", g_num_osc_interrupting);\n\t}\n\tg_num_osc_interrupting = 0;\n\n\tg_doc_num_osc_en = 1;\n\tUPDATE_G_DCYCS_PER_DOC_UPDATE(1);\n}\n\nint\ndoc_play(dword64 dfcyc, double last_dsamp, double dsamp_now, int num_samps,\n\t\tint snd_buf_init, int *outptr_start)\n{\n\tDoc_reg *rptr;\n\tint\t*outptr;\n\tdouble\tcomplete_dsamp, cur_dsamp;\n\tword32\tcur_acc, cur_pos, cur_mask, cur_inc, cur_end;\n\tint\tval, val2, imul, off, ctl, num_osc_en;\n\tint\tsamps_left, samps_to_do, samp_offset, pos, osc, done;\n\tint\ti, j;\n\n\tnum_osc_en = g_doc_num_osc_en;\n\n\tdbg_log_info(dfcyc, num_samps, 0, 0xd0c0);\n\n\tdone = 0;\n\t// Do Ensoniq oscillators\n\twhile(!done) {\n\t\tdone = 1;\n\t\tfor(j = 0; j < num_osc_en; j++) {\n\t\t\tosc = j;\n\t\t\trptr = &(g_doc_regs[osc]);\n\t\t\tcomplete_dsamp = rptr->complete_dsamp;\n\t\t\tsamps_left = rptr->samps_left;\n\t\t\tcur_acc = rptr->cur_acc;\n\t\t\tcur_mask = rptr->cur_mask;\n\t\t\tcur_inc = rptr->cur_inc;\n\t\t\tcur_end = rptr->cur_end;\n\t\t\tif(!rptr->running || cur_inc == 0 ||\n\t\t\t\t\t\t(complete_dsamp >= dsamp_now)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tdone = 0;\n\t\t\tctl = rptr->ctl;\n\n\t\t\tsamp_offset = 0;\n\t\t\tif(complete_dsamp > last_dsamp) {\n\t\t\t\tsamp_offset = (int)(complete_dsamp- last_dsamp);\n\t\t\t\tif(samp_offset > num_samps) {\n\t\t\t\t\trptr->complete_dsamp = dsamp_now;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\toutptr = outptr_start + 2 * samp_offset;\n\t\t\tif(ctl & 0x10) {\n\t\t\t\t/* other channel */\n\t\t\t\toutptr += 1;\n\t\t\t}\n\n\t\t\timul = (rptr->vol * g_doc_vol);\n\t\t\toff = imul * 128;\n\n\t\t\tsamps_to_do = MY_MIN(samps_left,\n\t\t\t\t\t\tnum_samps - samp_offset);\n\t\t\tif(imul == 0 || samps_to_do == 0) {\n\t\t\t\t/* produce no sound */\n\t\t\t\tsamps_left = samps_left - samps_to_do;\n\t\t\t\tcur_acc += cur_inc * samps_to_do;\n\t\t\t\trptr->samps_left = samps_left;\n\t\t\t\trptr->cur_acc = cur_acc;\n\t\t\t\tcur_dsamp = last_dsamp +\n\t\t\t\t\t(double)(samps_to_do + samp_offset);\n\t\t\t\tDOC_LOG(\"nosnd\", osc, cur_dsamp, samps_to_do);\n\t\t\t\trptr->complete_dsamp = dsamp_now;\n\t\t\t\tcur_pos = rptr->cur_start+(cur_acc & cur_mask);\n\t\t\t\tif(samps_left <= 0) {\n\t\t\t\t\tdoc_sound_end(osc, 1, cur_dsamp,\n\t\t\t\t\t\t\t\tdsamp_now);\n\t\t\t\t\tval = 0;\n\t\t\t\t\tj--;\n\t\t\t\t} else {\n\t\t\t\t\tval = doc_ram[cur_pos >> SND_PTR_SHIFT];\n\t\t\t\t}\n\t\t\t\trptr->last_samp_val = val;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(snd_buf_init == 0) {\n\t\t\t\tmemset(outptr_start, 0,\n\t\t\t\t\t2*sizeof(outptr_start[0])*num_samps);\n\t\t\t\tsnd_buf_init++;\n\t\t\t}\n\t\t\tval = 0;\n\t\t\trptr->complete_dsamp = dsamp_now;\n\t\t\tcur_pos = rptr->cur_start + (cur_acc & cur_mask);\n\t\t\tpos = 0;\n\t\t\tfor(i = 0; i < samps_to_do; i++) {\n\t\t\t\tpos = cur_pos >> SND_PTR_SHIFT;\n\t\t\t\tcur_pos += cur_inc;\n\t\t\t\tcur_acc += cur_inc;\n\t\t\t\tval = doc_ram[pos];\n\t\t\t\tval2 = (val * imul - off) >> 4;\n\t\t\t\tif((val == 0) || (cur_pos >= cur_end)) {\n\t\t\t\t\tcur_dsamp = last_dsamp +\n\t\t\t\t\t\t(double)(samp_offset + i + 1);\n\t\t\t\t\trptr->cur_acc = cur_acc;\n\t\t\t\t\trptr->samps_left = 0;\n\t\t\t\t\tDOC_LOG(\"end or 0\", osc, cur_dsamp,\n\t\t\t\t\t\t((pos & 0xffffU) << 16) |\n\t\t\t\t\t\t((i &0xff) << 8) | val);\n\t\t\t\t\tdoc_sound_end(osc, val, cur_dsamp,\n\t\t\t\t\t\t\t\tdsamp_now);\n\t\t\t\t\tval = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tval2 = outptr[0] + val2;\n\t\t\t\tsamps_left--;\n\t\t\t\t*outptr = val2;\n\t\t\t\toutptr += 2;\n\t\t\t}\n\t\t\trptr->last_samp_val = val;\n\n\t\t\tif(val != 0) {\n\t\t\t\trptr->cur_acc = cur_acc;\n\t\t\t\trptr->samps_left = samps_left;\n\t\t\t\trptr->complete_dsamp = dsamp_now;\n\t\t\t}\n\t\t\tDOC_LOG(\"splayed\", osc, dsamp_now,\n\t\t\t\t(samps_to_do << 16) + (pos & 0xffff));\n\t\t}\n\t}\n\n\treturn snd_buf_init;\n}\nvoid\ndoc_handle_event(int osc, dword64 dfcyc)\n{\n\t/* handle osc stopping and maybe interrupting */\n\n\t//DOC_LOG(\"doc_ev\", osc, dsamps, 0);\n\n\tg_doc_regs[osc].event = 0;\n\n\tsound_play(dfcyc);\n}\n\nvoid\ndoc_sound_end(int osc, int can_repeat, double eff_dsamps, double dsamps)\n{\n\tDoc_reg\t*rptr, *orptr;\n\tint\tmode, omode;\n\tint\tother_osc;\n\tint\tone_shot_stop;\n\tint\tctl;\n\n\t/* handle osc stopping and maybe interrupting */\n\n\tif(osc < 0 || osc > 31) {\n\t\tprintf(\"doc_handle_event: osc: %d!\\n\", osc);\n\t\treturn;\n\t}\n\n\trptr = &(g_doc_regs[osc]);\n\tctl = rptr->ctl;\n\n\tif(rptr->event) {\n\t\tremove_event_doc(osc);\n\t}\n\trptr->event = 0;\n\trptr->cur_acc = 0;\t\t/* reset internal accumulator*/\n\n\t/* check to make sure osc is running */\n\tif(ctl & 0x01) {\n\t\t/* Oscillator already stopped. */\n\t\thalt_printf(\"Osc %d interrupt, but it was already stop!\\n\",osc);\n#ifdef HPUX\n\t\tU_STACK_TRACE();\n#endif\n\t\treturn;\n\t}\n\n\tif(ctl & 0x08) {\n\t\tif(rptr->has_irq_pending == 0) {\n\t\t\tdoc_add_sound_irq(osc);\n\t\t}\n\t}\n\n\tif(!rptr->running) {\n\t\thalt_printf(\"Doc event for osc %d, but ! running\\n\", osc);\n\t}\n\n\trptr->running = 0;\n\n\tmode = (ctl >> 1) & 3;\n\tother_osc = osc ^ 1;\n\torptr = &(g_doc_regs[other_osc]);\n\tomode = (orptr->ctl >> 1) & 3;\n\n\t/* If either this osc or it's partner is in swap mode, treat the */\n\t/*  pair as being in swap mode.  This Ensoniq feature pointed out */\n\t/*  by Ian Schmidt */\n\tif(mode == 0 && can_repeat) {\n\t\t/* free-running mode with no 0 byte! */\n\t\t/* start doing it again */\n\n\t\tdoc_start_sound(osc, eff_dsamps, dsamps);\n\n\t\treturn;\n\t} else if((mode == 3) || (omode == 3)) {\n\t\t/* swap mode (even if we're one_shot and partner is swap)! */\n\t\t/* unless we're one-shot and we hit a 0-byte--then */\n\t\t/* Olivier Goguel says just stop */\n\t\trptr->ctl |= 1;\n\t\tone_shot_stop = (mode == 1) && (!can_repeat);\n\t\tif(!one_shot_stop && !orptr->running &&\n\t\t\t\t\t\t\t(orptr->ctl & 0x1)) {\n\t\t\torptr->ctl = orptr->ctl & (~1);\n\t\t\tdoc_start_sound(other_osc, eff_dsamps, dsamps);\n\t\t}\n\t\treturn;\n\t} else {\n\t\t/* stop the oscillator */\n\t\trptr->ctl |= 1;\n\t}\n\n\treturn;\n}\n\nvoid\ndoc_add_sound_irq(int osc)\n{\n\tint\tnum_osc_interrupting;\n\n\tif(g_doc_regs[osc].has_irq_pending) {\n\t\thalt_printf(\"Adding sound_irq for %02x, but irq_p: %d\\n\", osc,\n\t\t\tg_doc_regs[osc].has_irq_pending);\n\t}\n\n\tnum_osc_interrupting = g_num_osc_interrupting + 1;\n\tg_doc_regs[osc].has_irq_pending = num_osc_interrupting;\n\tg_num_osc_interrupting = num_osc_interrupting;\n\n\tadd_irq(IRQ_PENDING_DOC);\n\tif(num_osc_interrupting == 1) {\n\t\tdoc_reg_e0 = 0x00 + (osc << 1);\n\t}\n\n\tDOC_LOG(\"add_irq\", osc, g_cur_dfcyc * g_dsamps_per_dfcyc, 0);\n}\n\nvoid\ndoc_remove_sound_irq(int osc, int must)\n{\n\tDoc_reg\t*rptr;\n\tint\tnum_osc_interrupt;\n\tint\thas_irq_pending;\n\tint\tfirst;\n\tint\ti;\n\n\tdoc_printf(\"remove irq for osc: %d, has_irq: %d\\n\",\n\t\tosc, g_doc_regs[osc].has_irq_pending);\n\n\tnum_osc_interrupt = g_doc_regs[osc].has_irq_pending;\n\tfirst = 0;\n\tif(num_osc_interrupt) {\n\t\tg_num_osc_interrupting--;\n\t\tg_doc_regs[osc].has_irq_pending = 0;\n\t\tDOC_LOG(\"rem_irq\", osc, g_cur_dfcyc * g_dsamps_per_dfcyc, 0);\n\t\tif(g_num_osc_interrupting == 0) {\n\t\t\tremove_irq(IRQ_PENDING_DOC);\n\t\t}\n\n\t\tfirst = 0x40 | (doc_reg_e0 >> 1);\n\t\t\t\t\t/* if none found, then def = no ints */\n\t\tfor(i = 0; i < g_doc_num_osc_en; i++) {\n\t\t\trptr = &(g_doc_regs[i]);\n\t\t\thas_irq_pending = rptr->has_irq_pending;\n\t\t\tif(has_irq_pending > num_osc_interrupt) {\n\t\t\t\thas_irq_pending--;\n\t\t\t\trptr->has_irq_pending = has_irq_pending;\n\t\t\t}\n\t\t\tif(has_irq_pending == 1) {\n\t\t\t\tfirst = i;\n\t\t\t}\n\t\t}\n\t\tif(num_osc_interrupt == 1) {\n\t\t\tdoc_reg_e0 = (first << 1);\n\t\t} else {\n#if 0\n\t\t\thalt_printf(\"remove_sound_irq[%02x]=%d, first:%d\\n\",\n\t\t\t\tosc, num_osc_interrupt, first);\n#endif\n\t\t}\n\t} else {\n#if 0\n\t\t/* make sure no int pending */\n\t\tif(doc_reg_e0 != 0xff) {\n\t\t\thalt_printf(\"remove_sound_irq[%02x]=0, but e0: %02x\\n\",\n\t\t\t\tosc, doc_reg_e0);\n\t\t}\n#endif\n\t\tif(must) {\n\t\t\thalt_printf(\"REMOVE_sound_irq[%02x]=0, but e0: %02x\\n\",\n\t\t\t\tosc, doc_reg_e0);\n\t\t}\n\t}\n\n\tif(doc_reg_e0 & 0x80) {\n\t\tfor(i = 0; i < 0x20; i++) {\n\t\t\thas_irq_pending = g_doc_regs[i].has_irq_pending;\n\t\t\tif(has_irq_pending) {\n\t\t\t\thalt_printf(\"remove_sound_irq[%02x], but \"\n\t\t\t\t\t\"[%02x]=%d!\\n\", osc,i,has_irq_pending);\n\t\t\t\tprintf(\"num_osc_int: %d, first: %02x\\n\",\n\t\t\t\t\tnum_osc_interrupt, first);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid\ndoc_start_sound2(int osc, dword64 dfcyc)\n{\n\tdouble\tdsamps;\n\n\tdsamps = dfcyc * g_dsamps_per_dfcyc;\n\tdoc_start_sound(osc, dsamps, dsamps);\n}\n\nvoid\ndoc_start_sound(int osc, double eff_dsamps, double dsamps)\n{\n\tDoc_reg\t*rptr;\n\tint\tctl;\n\tint\tmode;\n\tword32\tsz;\n\tword32\tsize;\n\tword32\twave_size;\n\n\tif(osc < 0 || osc > 31) {\n\t\thalt_printf(\"start_sound: osc: %02x!\\n\", osc);\n\t}\n\n\trptr = &(g_doc_regs[osc]);\n\n\tif(osc >= g_doc_num_osc_en) {\n\t\trptr->ctl |= 1;\n\t\treturn;\n\t}\n\n\tctl = rptr->ctl;\n\tmode = (ctl >> 1) & 3;\n\twave_size = rptr->wavesize;\n\tsz = ((wave_size >> 3) & 7) + 8;\n\tsize = 1 << sz;\n\n\tif(size < 0x100) {\n\t\thalt_printf(\"size: %08x is too small, sz: %08x!\\n\", size, sz);\n\t}\n\n\tif(rptr->running) {\n\t\thalt_printf(\"start_sound osc: %d, already running!\\n\", osc);\n\t}\n\n\trptr->running = 1;\n\n\trptr->complete_dsamp = eff_dsamps;\n\n\tdoc_printf(\"Starting osc %02x, dsamp: %f\\n\", osc, dsamps);\n\tdoc_printf(\"size: %04x\\n\", size);\n\n\tif((mode == 2) && ((osc & 1) == 0)) {\n\t\tprintf(\"Sync mode osc %d starting!\\n\", osc);\n\t\t/* set_halt(1); */\n\n\t\t/* see if we should start our odd partner */\n\t\tif((rptr[1].ctl & 7) == 5) {\n\t\t\t/* odd partner stopped in sync mode--start him */\n\t\t\trptr[1].ctl &= (~1);\n\t\t\tdoc_start_sound(osc + 1, eff_dsamps, dsamps);\n\t\t} else {\n\t\t\tprintf(\"Osc %d starting sync, but osc %d ctl: %02x\\n\",\n\t\t\t\tosc, osc+1, rptr[1].ctl);\n\t\t}\n\t}\n\n\tdoc_wave_end_estimate(osc, eff_dsamps, dsamps);\n\n\tDOC_LOG(\"st playing\", osc, eff_dsamps, size);\n#if 0\n\tif(rptr->cur_acc != 0) {\n\t\thalt_printf(\"Start osc %02x, acc: %08x\\n\", osc, rptr->cur_acc);\n\t}\n#endif\n}\n\nvoid\ndoc_wave_end_estimate2(int osc, dword64 dfcyc)\n{\n\tdouble\tdsamps;\n\n\tdsamps = dfcyc * g_dsamps_per_dfcyc;\n\tdoc_wave_end_estimate(osc, dsamps, dsamps);\n}\n\nvoid\ndoc_wave_end_estimate(int osc, double eff_dsamps, double dsamps)\n{\n\tDoc_reg *rptr;\n\tbyte\t*ptr1;\n\tdword64\tevent_dfcyc;\n\tdouble\tevent_dsamp, dfcycs_per_samp, dsamps_per_byte, num_dsamps;\n\tdouble\tdcur_inc;\n\tword32\ttmp1, cur_inc, save_val;\n\tint\tsave_size, pos, size, estimate;\n\n\tdfcycs_per_samp = g_fcyc_per_samp;\n\n\trptr = &(g_doc_regs[osc]);\n\n\tcur_inc = rptr->cur_inc;\n\tdcur_inc = (double)cur_inc;\n\tdsamps_per_byte = 0.0;\n\tif(cur_inc) {\n\t\tdsamps_per_byte = SND_PTR_SHIFT_DBL / (double)dcur_inc;\n\t}\n\n\t/* see if there's a zero byte */\n\ttmp1 = rptr->cur_start + (rptr->cur_acc & rptr->cur_mask);\n\tpos = tmp1 >> SND_PTR_SHIFT;\n\tsize = ((rptr->cur_end) >> SND_PTR_SHIFT) - pos;\n\n\tptr1 = &doc_ram[pos];\n\n\testimate = 0;\n\tif(rptr->ctl & 0x08 || g_doc_regs[osc ^ 1].ctl & 0x08) {\n\t\testimate = 1;\n\t}\n\n#if 0\n\testimate = 1;\n#endif\n\tif(estimate) {\n\t\tsave_size = size;\n\t\tsave_val = ptr1[size];\n\t\tptr1[size] = 0;\n\t\tsize = (int)strlen((char *)ptr1);\n\t\tptr1[save_size] = save_val;\n\t}\n\n\t/* calc samples to play */\n\tnum_dsamps = (dsamps_per_byte * (double)size) + 1.0;\n\n\trptr->samps_left = (int)num_dsamps;\n\n\tif(rptr->event) {\n\t\tremove_event_doc(osc);\n\t}\n\trptr->event = 0;\n\n\tevent_dsamp = eff_dsamps + num_dsamps;\n\tif(estimate) {\n\t\trptr->event = 1;\n\t\trptr->dsamp_ev = event_dsamp;\n\t\trptr->dsamp_ev2 = dsamps;\n\t\tevent_dfcyc = (dword64)(event_dsamp * dfcycs_per_samp) +\n\t\t\t\t\t\t\t\t65536LL;\n\t\tadd_event_doc(event_dfcyc, osc);\n\t}\n}\n\nvoid\ndoc_remove_sound_event(int osc)\n{\n\tif(g_doc_regs[osc].event) {\n\t\tg_doc_regs[osc].event = 0;\n\t\tremove_event_doc(osc);\n\t}\n}\n\n\nvoid\ndoc_write_ctl_reg(dword64 dfcyc, int osc, int val)\n{\n\tDoc_reg *rptr;\n\tword32\told_halt, new_halt;\n\tint\told_val;\n\n\tif(osc < 0 || osc >= 0x20) {\n\t\thalt_printf(\"doc_write_ctl_reg: osc: %02x, val: %02x\\n\",\n\t\t\tosc, val);\n\t\treturn;\n\t}\n\n\trptr = &(g_doc_regs[osc]);\n\told_val = rptr->ctl;\n\tg_doc_saved_ctl = old_val;\n\n\tif(old_val == val) {\n\t\treturn;\n\t}\n\n\t//DOC_LOG(\"ctl_reg\", osc, dsamps, (old_val << 16) + val);\n\n\told_halt = (old_val & 1);\n\tnew_halt = (val & 1);\n\n\t/* bits are:\t28: old int bit */\n\t/*\t\t29: old halt bit */\n\t/*\t\t30: new int bit */\n\t/*\t\t31: new halt bit */\n\n#if 0\n\tif(osc == 0x10) {\n\t\tprintf(\"osc %d new_ctl: %02x, old: %02x\\n\", osc, val, old_val);\n\t}\n#endif\n\n\t/* no matter what, remove any pending IRQs on this osc */\n\tdoc_remove_sound_irq(osc, 0);\n\n#if 0\n\tif(old_halt) {\n\t\tprintf(\"doc_write_ctl to osc %d, val: %02x, old: %02x\\n\",\n\t\t\tosc, val, old_val);\n\t}\n#endif\n\n\tif(new_halt != 0) {\n\t\t/* make sure sound is stopped */\n\t\tdoc_remove_sound_event(osc);\n\t\tif(old_halt == 0) {\n\t\t\t/* it was playing, finish it up */\n#if 0\n\t\t\thalt_printf(\"Aborted osc %d at eff_dsamps: %f, ctl: \"\n\t\t\t\t\"%02x, oldctl: %02x\\n\", osc, eff_dsamps,\n\t\t\t\tval, old_val);\n#endif\n\t\t\tsound_play(dfcyc);\n\t\t}\n\t\tif(((old_val >> 1) & 3) > 0) {\n\t\t\t/* don't clear acc if free-running */\n\t\t\tg_doc_regs[osc].cur_acc = 0;\n\t\t}\n\n\t\tg_doc_regs[osc].ctl = val;\n\t\tg_doc_regs[osc].running = 0;\n\t} else {\n\t\t/* new halt == 0 = make sure sound is running */\n\t\tif(old_halt != 0) {\n\t\t\t/* start sound */\n\t\t\t//DOC_LOG(\"ctl_sound_play\", osc, eff_dsamps, val);\n\t\t\tsound_play(dfcyc);\n\t\t\tg_doc_regs[osc].ctl = val;\n\n\t\t\tdoc_start_sound2(osc, dfcyc);\n\t\t} else {\n\t\t\t/* was running, and something changed */\n\t\t\tdoc_printf(\"osc %d old ctl:%02x new:%02x!\\n\",\n\t\t\t\tosc, old_val, val);\n\t\t\tsound_play(dfcyc);\n\t\t\tg_doc_regs[osc].ctl = val;\n\t\t\tif((old_val ^ val) & val & 0x8) {\n\t\t\t\t/* now has ints on */\n\t\t\t\tdoc_wave_end_estimate2(osc, dfcyc);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid\ndoc_recalc_sound_parms(dword64 dfcyc, int osc)\n{\n\tDoc_reg\t*rptr;\n\tdouble\tdfreq, dtmp1, dacc, dacc_recip;\n\tword32\tres, sz, size, wave_size, cur_start, shifted_size;\n\n\trptr = &(g_doc_regs[osc]);\n\n\twave_size = rptr->wavesize;\n\n\tdfreq = (double)rptr->freq;\n\n\tsz = ((wave_size >> 3) & 7) + 8;\n\tsize = 1 << sz;\n\trptr->size_bytes = size;\n\tres = wave_size & 7;\n\n\tshifted_size = size << SND_PTR_SHIFT;\n\tcur_start = (rptr->waveptr << (8 + SND_PTR_SHIFT)) & (0-shifted_size);\n\n\tdtmp1 = dfreq * (DOC_SCAN_RATE * g_drecip_audio_rate);\n\tdacc = (double)(1 << (20 - (17 - sz + res)));\n\tdacc_recip = (SND_PTR_SHIFT_DBL) / ((double)(1 << 20));\n\tdtmp1 = dtmp1 * g_drecip_osc_en_plus_2 * dacc * dacc_recip;\n\n\trptr->cur_inc = (int)(dtmp1);\n\trptr->cur_start = cur_start;\n\trptr->cur_end = cur_start + shifted_size;\n\trptr->cur_mask = (shifted_size - 1);\n\n\tdbg_log_info(dfcyc, (rptr->waveptr << 16) + wave_size, osc, 0xd0cf);\n}\n\nint\ndoc_read_c03c()\n{\n\treturn g_doc_sound_ctl;\n}\n\nint\ndoc_read_c03d(dword64 dfcyc)\n{\n\tDoc_reg\t*rptr;\n\tint\tosc, type, ret;\n\n\tret = g_doc_saved_val;\n\n\tif(g_doc_sound_ctl & 0x40) {\n\t\t/* Read RAM */\n\t\tg_doc_saved_val = doc_ram[g_c03ef_doc_ptr];\n\t} else {\n\t\t/* Read DOC */\n\t\tg_doc_saved_val = 0;\n\n\t\tosc = g_c03ef_doc_ptr & 0x1f;\n\t\ttype = (g_c03ef_doc_ptr >> 5) & 0x7;\n\t\trptr = &(g_doc_regs[osc]);\n\n\t\tswitch(type) {\n\t\tcase 0x0:\t/* freq lo */\n\t\t\tg_doc_saved_val = rptr->freq & 0xff;\n\t\t\tbreak;\n\t\tcase 0x1:\t/* freq hi */\n\t\t\tg_doc_saved_val = rptr->freq >> 8;\n\t\t\tbreak;\n\t\tcase 0x2:\t/* vol */\n\t\t\tg_doc_saved_val = rptr->vol;\n\t\t\tbreak;\n\t\tcase 0x3:\t/* data register */\n\t\t\tsound_play(dfcyc);\t\t// Fix for Paperboy GS\n\t\t\tg_doc_saved_val = rptr->last_samp_val;\n\t\t\tbreak;\n\t\tcase 0x4:\t/* wave ptr register */\n\t\t\tg_doc_saved_val = rptr->waveptr;\n\t\t\tbreak;\n\t\tcase 0x5:\t/* control register */\n\t\t\tg_doc_saved_val = rptr->ctl;\n\t\t\tbreak;\n\t\tcase 0x6:\t/* control register */\n\t\t\tg_doc_saved_val = rptr->wavesize;\n\t\t\tbreak;\n\t\tcase 0x7:\t/* 0xe0-0xff */\n\t\t\tswitch(osc) {\n\t\t\tcase 0x00:\t/* 0xe0 */\n\t\t\t\tg_doc_saved_val = doc_reg_e0;\n\t\t\t\tdoc_printf(\"Reading doc 0xe0, ret: %02x\\n\",\n\t\t\t\t\t\t\tg_doc_saved_val);\n\n\t\t\t\t/* Clear IRQ on read of e0, if any irq pend */\n\t\t\t\tif((doc_reg_e0 & 0x80) == 0) {\n\t\t\t\t\tdoc_remove_sound_irq(doc_reg_e0 >> 1,\n\t\t\t\t\t\t\t\t\t1);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase 0x01:\t/* 0xe1 */\n\t\t\t\tg_doc_saved_val = (g_doc_num_osc_en - 1) << 1;\n\t\t\t\tbreak;\n\t\t\tcase 0x02:\t/* 0xe2 */\n\t\t\t\tg_doc_saved_val = 0x80;\n#if 0\n\t\t\t\thalt_printf(\"Reading doc 0xe2, ret: %02x\\n\",\n\t\t\t\t\t\t\tg_doc_saved_val);\n#endif\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tg_doc_saved_val = 0;\n\t\t\t\thalt_printf(\"Reading bad doc_reg[%04x]: %02x\\n\",\n\t\t\t\t\tg_c03ef_doc_ptr, g_doc_saved_val);\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tg_doc_saved_val = 0;\n\t\t\thalt_printf(\"Reading bad doc_reg[%04x]: %02x\\n\",\n\t\t\t\t\tg_c03ef_doc_ptr, g_doc_saved_val);\n\t\t}\n\t}\n\n\tdoc_printf(\"read c03d, doc_ptr: %04x, ret: %02x, saved: %02x\\n\",\n\t\tg_c03ef_doc_ptr, ret, g_doc_saved_val);\n\n\t//DOC_LOG(\"read c03d\", -1, dsamps, (g_c03ef_doc_ptr << 16) +\n\t//\t\t(g_doc_saved_val << 8) + ret);\n\n\tif(g_doc_sound_ctl & 0x20) {\n\t\tg_c03ef_doc_ptr = (g_c03ef_doc_ptr + 1) & 0xffff;\n\t}\n\n\n\treturn ret;\n}\n\nvoid\ndoc_write_c03c(dword64 dfcyc, word32 val)\n{\n\tint\tvol;\n\n\tvol = val & 0xf;\n\tdbg_log_info(dfcyc, val, g_doc_vol, 0xc03c);\n\tif(g_doc_vol != vol) {\n\t\tsound_play(dfcyc);\n\n\t\tg_doc_vol = vol;\n\t\tdoc_printf(\"Setting doc vol to 0x%x at %016llx\\n\", vol, dfcyc);\n\t}\n\n\tg_doc_sound_ctl = val;\n}\n\nvoid\ndoc_write_c03d(dword64 dfcyc, word32 val)\n{\n\tDoc_reg\t*rptr;\n\tint\tosc, type, ctl, tmp;\n\tint\ti;\n\n\tval = val & 0xff;\n\n\tdoc_printf(\"write c03d, doc_ptr: %04x, val: %02x\\n\",\n\t\tg_c03ef_doc_ptr, val);\n\n\tdbg_log_info(dfcyc, g_c03ef_doc_ptr, val, 0xc03d);\n\n\tif(g_doc_sound_ctl & 0x40) {\n\t\t/* RAM */\n\t\tdoc_ram[g_c03ef_doc_ptr] = val;\n\t} else {\n\t\t/* DOC */\n\t\tosc = g_c03ef_doc_ptr & 0x1f;\n\t\ttype = (g_c03ef_doc_ptr >> 5) & 0x7;\n\t\trptr = &(g_doc_regs[osc]);\n\t\tctl = rptr->ctl;\n#if 0\n\t\tif((ctl & 1) == 0) {\n\t\t\tif(type < 2 || type == 4 || type == 6) {\n\t\t\t\thalt_printf(\"Osc %d is running, old ctl: %02x, \"\n\t\t\t\t\t\"but write reg %02x=%02x\\n\",\n\t\t\t\t\tosc, ctl, g_c03ef_doc_ptr & 0xff, val);\n\t\t\t}\n\t\t}\n#endif\n\n\t\tswitch(type) {\n\t\tcase 0x0:\t/* freq lo */\n\t\t\tif((rptr->freq & 0xff) == (word32)val) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif((ctl & 1) == 0) {\n\t\t\t\t/* play through current status */\n\t\t\t\t//DOC_LOG(\"flo_sound_play\", osc, dsamps, val);\n\t\t\t\tsound_play(dfcyc);\n\t\t\t}\n\t\t\trptr->freq = (rptr->freq & 0xff00) + val;\n\t\t\tdoc_recalc_sound_parms(dfcyc, osc);\n\t\t\tbreak;\n\t\tcase 0x1:\t/* freq hi */\n\t\t\tif((rptr->freq >> 8) == (word32)val) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif((ctl & 1) == 0) {\n\t\t\t\t/* play through current status */\n\t\t\t\t//DOC_LOG(\"fhi_sound_play\", osc, dsamps, val);\n\t\t\t\tsound_play(dfcyc);\n\t\t\t}\n\t\t\trptr->freq = (rptr->freq & 0xff) + (val << 8);\n\t\t\tdoc_recalc_sound_parms(dfcyc, osc);\n\t\t\tbreak;\n\t\tcase 0x2:\t/* vol */\n\t\t\tif(rptr->vol == (word32)val) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif((ctl & 1) == 0) {\n\t\t\t\t/* play through current status */\n\t\t\t\t//DOC_LOG(\"vol_sound_play\", osc, dsamps, val);\n\t\t\t\tsound_play(dfcyc);\n\t\t\t}\n\t\t\trptr->vol = val;\n\t\t\tbreak;\n\t\tcase 0x3:\t/* data register */\n#if 0\n\t\t\tprintf(\"Writing %02x into doc_data_reg[%02x]!\\n\",\n\t\t\t\tval, osc);\n#endif\n\t\t\tbreak;\n\t\tcase 0x4:\t/* wave ptr register */\n\t\t\tif(rptr->waveptr == (word32)val) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif((ctl & 1) == 0) {\n\t\t\t\t/* play through current status */\n\t\t\t\t//DOC_LOG(\"wptr_sound_play\", osc, dsamps, val);\n\t\t\t\tsound_play(dfcyc);\n\t\t\t}\n\t\t\trptr->waveptr = val;\n\t\t\tdoc_recalc_sound_parms(dfcyc, osc);\n\t\t\tbreak;\n\t\tcase 0x5:\t/* control register */\n#if 0\n\t\t\tprintf(\"doc_write ctl osc %d, val: %02x\\n\", osc, val);\n#endif\n\t\t\tif(rptr->ctl == (word32)val) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdoc_write_ctl_reg(dfcyc, osc, val);\n\t\t\tbreak;\n\t\tcase 0x6:\t/* wavesize register */\n\t\t\tif(rptr->wavesize == (word32)val) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif((ctl & 1) == 0) {\n\t\t\t\t/* play through current status */\n\t\t\t\t//DOC_LOG(\"wsz_sound_play\", osc, dsamps, val);\n\t\t\t\tsound_play(dfcyc);\n\t\t\t}\n\t\t\trptr->wavesize = val;\n\t\t\tdoc_recalc_sound_parms(dfcyc, osc);\n\t\t\tbreak;\n\t\tcase 0x7:\t/* 0xe0-0xff */\n\t\t\tswitch(osc) {\n\t\t\tcase 0x00:\t/* 0xe0 */\n\t\t\t\tdoc_printf(\"writing doc 0xe0 with %02x, \"\n\t\t\t\t\t\"was:%02x\\n\", val, doc_reg_e0);\n#if 0\n\t\t\t\tif(val != doc_reg_e0) {\n\t\t\t\t\thalt_printf(\"writing doc 0xe0 with \"\n\t\t\t\t\t\t\"%02x, was:%02x\\n\", val,\n\t\t\t\t\t\tdoc_reg_e0);\n\t\t\t\t}\n#endif\n\t\t\t\tbreak;\n\t\t\tcase 0x01:\t/* 0xe1 */\n\t\t\t\tdoc_printf(\"Writing doc 0xe1 with %02x\\n\", val);\n\t\t\t\ttmp = val & 0x3e;\n\t\t\t\ttmp = (tmp >> 1) + 1;\n\t\t\t\tif(tmp < 1) {\n\t\t\t\t\ttmp = 1;\n\t\t\t\t}\n\t\t\t\tif(tmp > 32) {\n\t\t\t\t\thalt_printf(\"doc 0xe1: %02x!\\n\", val);\n\t\t\t\t\ttmp = 32;\n\t\t\t\t}\n\t\t\t\tg_doc_num_osc_en = tmp;\n\t\t\t\tUPDATE_G_DCYCS_PER_DOC_UPDATE(tmp);\n\n\t\t\t\t/* Stop any oscs that were running but now */\n\t\t\t\t/*  are disabled */\n\t\t\t\tfor(i = g_doc_num_osc_en; i < 0x20; i++) {\n\t\t\t\t\tdoc_write_ctl_reg(dfcyc, i,\n\t\t\t\t\t\t\tg_doc_regs[i].ctl | 1);\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t/* this should be illegal, but Turkey Shoot */\n\t\t\t\t/* and apparently TaskForce, OOTW, etc */\n\t\t\t\t/*  writes to e2-ff, for no apparent reason */\n\t\t\t\tdoc_printf(\"Writing doc 0x%x with %02x\\n\",\n\t\t\t\t\t\tg_c03ef_doc_ptr, val);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thalt_printf(\"Writing %02x into bad doc_reg[%04x]\\n\",\n\t\t\t\tval, g_c03ef_doc_ptr);\n\t\t}\n\t}\n\n\tif(g_doc_sound_ctl & 0x20) {\n\t\tg_c03ef_doc_ptr = (g_c03ef_doc_ptr + 1) & 0xffff;\n\t}\n\n\tg_doc_saved_val = val;\n}\n\nvoid\ndoc_show_ensoniq_state()\n{\n\tDoc_reg\t*rptr;\n\tint\ti;\n\n\tprintf(\"Ensoniq state\\n\");\n\tprintf(\"c03c doc_sound_ctl: %02x, doc_saved_val: %02x\\n\",\n\t\tg_doc_sound_ctl, g_doc_saved_val);\n\tprintf(\"doc_ptr: %04x,    num_osc_en: %02x, e0: %02x\\n\",\n\t\tg_c03ef_doc_ptr, g_doc_num_osc_en, doc_reg_e0);\n\n\tfor(i = 0; i < 32; i += 8) {\n\t\tprintf(\"irqp: %02x: %04x %04x %04x %04x %04x %04x %04x %04x\\n\",\n\t\t\ti,\n\t\t\tg_doc_regs[i].has_irq_pending,\n\t\t\tg_doc_regs[i + 1].has_irq_pending,\n\t\t\tg_doc_regs[i + 2].has_irq_pending,\n\t\t\tg_doc_regs[i + 3].has_irq_pending,\n\t\t\tg_doc_regs[i + 4].has_irq_pending,\n\t\t\tg_doc_regs[i + 5].has_irq_pending,\n\t\t\tg_doc_regs[i + 6].has_irq_pending,\n\t\t\tg_doc_regs[i + 7].has_irq_pending);\n\t}\n\n\tfor(i = 0; i < 32; i += 8) {\n\t\tprintf(\"freq: %02x: %04x %04x %04x %04x %04x %04x %04x %04x\\n\",\n\t\t\ti,\n\t\t\tg_doc_regs[i].freq, g_doc_regs[i + 1].freq,\n\t\t\tg_doc_regs[i + 2].freq, g_doc_regs[i + 3].freq,\n\t\t\tg_doc_regs[i + 4].freq, g_doc_regs[i + 5].freq,\n\t\t\tg_doc_regs[i + 6].freq, g_doc_regs[i + 7].freq);\n\t}\n\n\tfor(i = 0; i < 32; i += 8) {\n\t\tprintf(\"vol: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\\n\",\n\t\t\ti,\n\t\t\tg_doc_regs[i].vol, g_doc_regs[i + 1].vol,\n\t\t\tg_doc_regs[i + 2].vol, g_doc_regs[i + 3].vol,\n\t\t\tg_doc_regs[i + 4].vol, g_doc_regs[i + 5].vol,\n\t\t\tg_doc_regs[i + 6].vol, g_doc_regs[i + 6].vol);\n\t}\n\n\tfor(i = 0; i < 32; i += 8) {\n\t\tprintf(\"wptr: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\\n\",\n\t\t\ti,\n\t\t\tg_doc_regs[i].waveptr, g_doc_regs[i + 1].waveptr,\n\t\t\tg_doc_regs[i + 2].waveptr, g_doc_regs[i + 3].waveptr,\n\t\t\tg_doc_regs[i + 4].waveptr, g_doc_regs[i + 5].waveptr,\n\t\t\tg_doc_regs[i + 6].waveptr, g_doc_regs[i + 7].waveptr);\n\t}\n\n\tfor(i = 0; i < 32; i += 8) {\n\t\tprintf(\"ctl: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\\n\",\n\t\t\ti,\n\t\t\tg_doc_regs[i].ctl, g_doc_regs[i + 1].ctl,\n\t\t\tg_doc_regs[i + 2].ctl, g_doc_regs[i + 3].ctl,\n\t\t\tg_doc_regs[i + 4].ctl, g_doc_regs[i + 5].ctl,\n\t\t\tg_doc_regs[i + 6].ctl, g_doc_regs[i + 7].ctl);\n\t}\n\n\tfor(i = 0; i < 32; i += 8) {\n\t\tprintf(\"wsize: %02x: %02x %02x %02x %02x %02x %02x %02x %02x\\n\",\n\t\t\ti,\n\t\t\tg_doc_regs[i].wavesize, g_doc_regs[i + 1].wavesize,\n\t\t\tg_doc_regs[i + 2].wavesize, g_doc_regs[i + 3].wavesize,\n\t\t\tg_doc_regs[i + 4].wavesize, g_doc_regs[i + 5].wavesize,\n\t\t\tg_doc_regs[i + 6].wavesize, g_doc_regs[i + 7].wavesize);\n\t}\n\n\tfor(i = 0; i < 32; i++) {\n\t\trptr = &(g_doc_regs[i]);\n\t\tprintf(\"%2d: ctl:%02x wp:%02x ws:%02x freq:%04x vol:%02x \"\n\t\t\t\"ev:%d run:%d irq:%d sz:%04x\\n\", i,\n\t\t\trptr->ctl, rptr->waveptr, rptr->wavesize, rptr->freq,\n\t\t\trptr->vol, rptr->event, rptr->running,\n\t\t\trptr->has_irq_pending, rptr->size_bytes);\n\t\tprintf(\"    acc:%08x inc:%08x st:%08x end:%08x m:%08x\\n\",\n\t\t\trptr->cur_acc, rptr->cur_inc, rptr->cur_start,\n\t\t\trptr->cur_end, rptr->cur_mask);\n\t\tprintf(\"    compl_ds:%f samps_left:%d ev:%f ev2:%f\\n\",\n\t\t\trptr->complete_dsamp, rptr->samps_left,\n\t\t\trptr->dsamp_ev, rptr->dsamp_ev2);\n\t}\n\n#if 0\n\tfor(osc = 0; osc < 32; osc++) {\n\t\tfmax = 0.0;\n\t\tprintf(\"osc %d has %d samps\\n\", osc, g_fsamp_num[osc]);\n\t\tfor(i = 0; i < g_fsamp_num[osc]; i++) {\n\t\t\tprintf(\"%4d: %f\\n\", i, g_fsamps[osc][i]);\n\t\t\tfmax = MY_MAX(fmax, g_fsamps[osc][i]);\n\t\t}\n\t\tprintf(\"osc %d, fmax: %f\\n\", osc, fmax);\n\t}\n#endif\n}\n"
  },
  {
    "path": "upstream/kegs/src/dyna_filt.c",
    "content": "const char rcsid_dynafilt_c[] = \"@(#)$KmKId: dyna_filt.c,v 1.1 2021-08-09 03:13:55+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2021 by Kent Dickey\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include \"defc.h\"\n\n// Provide filters for Dynapro to use for copying files from the host to\n//  a ProDOS volume, and for writing changes to the ProDOS volume back to\n//  host files.\n\n"
  },
  {
    "path": "upstream/kegs/src/dyna_type.c",
    "content": "const char rcsid_dynatype_c[] = \"@(#)$KmKId: dyna_type.c,v 1.9 2023-05-19 13:52:30+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2021-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include \"defc.h\"\n\n// Provide routines for Dynapro to use for detecting the file type on the\n//  host system.  Host files can be \"basic1.bas\", \"basic2,tbas,a$801\"\n\nSTRUCT(Dynatype_extensions) {\n\tchar\tstr[16];\n\tword16\tfile_type;\n\tword16\taux_type;\n};\n\nDynatype_extensions g_dynatype_extensions[] = {\n{\t\"applesingle\",\t\t0xfff,\t0xffff },\n{\t\"txt\",\t\t\t0x04,\t0 },\n{\t\"c\",\t\t\t0x04,\t0 },\t\t\t// ,ttxt\n{\t\"s\",\t\t\t0x04,\t0 },\t\t\t// ,ttxt\n{\t\"h\",\t\t\t0x04,\t0 },\t\t\t// ,ttxt\n{\t\"bin\",\t\t\t0x06,\t0x2000 },\t\t// ,tbin\n{\t\"bas\",\t\t\t0xfc,\t0x0801 },\t\t// ,tbas\n{\t\"system\",\t\t0xff,\t0x2000 },\t\t// ,tsys\n//{\t\"shr\",\t\t\t0xc0,\t0x0002 },\t\t// ,t$c0\n{\t\"shk\",\t\t\t0xe0,\t0x8002 },\t\t// ,t$e0\n{\t\"sdk\",\t\t\t0xe0,\t0x8002 },\t\t// ,t$e0\n{\t\"\",\t\t\t0,\t0 }\n};\n\nSTRUCT(Dynatype_types) {\n\tchar\tstr[16];\n\tword16\tfile_type;\n\tword16\taux_type;\n};\n\nDynatype_types g_dynatype_types[] = {\n{\t\"non\",\t\t0x00,\t0 },\n{\t\"bad\",\t\t0x01,\t0 },\n{\t\"txt\",\t\t0x04,\t0 },\n{\t\"bin\",\t\t0x06,\t0x2000 },\n{\t\"pnt\",\t\t0xc0,\t0x0002 },\n{\t\"fnd\",\t\t0xc9,\t0 },\n{\t\"icn\",\t\t0xca,\t0 },\n{\t\"cmd\",\t\t0xf0,\t0 },\n{\t\"bas\",\t\t0xfc,\t0x0801 },\n{\t\"sys\",\t\t0xff,\t0x2000 },\n{\t\"\",\t\t0,\t0 }\n};\n\nword32\ndynatype_scan_extensions(const char *str)\n{\n\tint\tlen;\n\tint\ti;\n\n\tlen = (int)(sizeof(g_dynatype_extensions) /\n\t\t\t\t\tsizeof(g_dynatype_extensions[0]));\n\tfor(i = 0; i < len; i++) {\n\t\tif(cfgcasecmp(str, g_dynatype_extensions[i].str) == 0) {\n\t\t\treturn (g_dynatype_extensions[i].file_type << 16) |\n\t\t\t\t\tg_dynatype_extensions[i].aux_type |\n\t\t\t\t\t0x1000000;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nword32\ndynatype_find_prodos_type(const char *str)\n{\n\tword32\tfile_type;\n\tint\tlen;\n\tint\ti;\n\n\tlen = (int)(sizeof(g_dynatype_types) / sizeof(g_dynatype_types[0]));\n\tfor(i = 0; i < len; i++) {\n\t\tif(cfgcasecmp(str, g_dynatype_types[i].str) == 0) {\n\t\t\tfile_type = g_dynatype_types[i].file_type;\n\t\t\treturn (file_type << 16) | g_dynatype_types[i].aux_type;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nconst char *\ndynatype_find_file_type(word32 file_type)\n{\n\tint\tlen;\n\tint\ti;\n\n\tlen = (int)(sizeof(g_dynatype_types) / sizeof(g_dynatype_types[0]));\n\tfor(i = 0; i < len; i++) {\n\t\tif(g_dynatype_types[i].file_type == file_type) {\n\t\t\treturn g_dynatype_types[i].str;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nword32\ndynatype_detect_file_type(Dynapro_file *fileptr, const char *path_ptr,\n\t\t\t\t\t\t\tword32 storage_type)\n{\n\tchar\text_buf[32];\n\tconst char *str;\n\tchar\t*endstr;\n\tword32\tfile_type, aux_type, type_or_aux;\n\tint\tlen, this_len, c, pos;\n\n\t// Look for ,tbas and ,a$2000 to get filetype and aux_type info\n\n\tstr = cfg_str_basename(path_ptr);\n\tlen = (int)strlen(str);\n\n\t// Look for .ext and ,tbas, etc.\n\tpos = 0;\n\text_buf[0] = 0;\n\tfile_type = 0x06;\t\t\t// Default to BIN\n\taux_type = 0;\n\twhile(pos < len) {\n\t\tc = str[pos++];\n\t\tif(c == '.') {\n\t\t\tthis_len = dynatype_get_extension(&str[pos],\n\t\t\t\t\t\t\t&ext_buf[0], 30);\n\t\t\tpos += this_len;\n\t\t\tcontinue;\n\t\t} else if(c == ',') {\n\t\t\tthis_len = dynatype_comma_arg(&str[pos], &type_or_aux);\n\t\t\tif(type_or_aux & 0x1000000) {\n\t\t\t\tfile_type = type_or_aux;\n\t\t\t} else if(type_or_aux & 0x2000000) {\n\t\t\t\taux_type = type_or_aux;\n\t\t\t} else {\n\t\t\t\tprintf(\"Unknown , extension, %s ignored\\n\",\n\t\t\t\t\t\t\t\t&str[pos]);\n\t\t\t}\n\t\t\tpos += this_len;\n\t\t\tcontinue;\n\t\t} else if(c == '#') {\n\t\t\t// Cadius style encoding: #ff2000 is type=$ff, aux=$2000\n\t\t\ttype_or_aux = strtol(&str[pos], &endstr, 16);\n\t\t\tfile_type = (type_or_aux & 0xffffff) | 0x1000000;\n\t\t\taux_type = 0;\n\t\t\tpos += (int)(endstr - str);\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\t// Handle extensions and type.  First do extension mapping\n\tif(ext_buf[0]) {\n\t\ttype_or_aux = dynatype_scan_extensions(&ext_buf[0]);\n\t\tif((type_or_aux) >= 0x0f000000UL) {\n\t\t\t// AppleSingle\n\t\t\tstorage_type = 0x50;\t\t// Forked file\n\t\t}\n\t\tif(file_type < 0x1000000) {\n\t\t\tfile_type = type_or_aux;\n\t\t}\n\t\tif(aux_type < 0x1000000) {\n\t\t\taux_type = type_or_aux;\n\t\t}\n\t}\n#if 0\n\tprintf(\"After parsing ext, file_type:%08x, aux_type:%08x\\n\",\n\t\t\t\t\t\tfile_type, aux_type);\n#endif\n\n\tfileptr->file_type = (file_type >> 16) & 0xff;\n\tif(aux_type == 0) {\n\t\taux_type = file_type & 0xffff;\n\t}\n\tfileptr->aux_type = aux_type & 0xffff;\n\n\treturn storage_type;\n}\n\nint\ndynatype_get_extension(const char *str, char *out_ptr, int buf_len)\n{\n\tint\tc, len;\n\n\t// Will write up to buf_len chars to out_ptr\n\tif(buf_len < 1) {\n\t\treturn 0;\n\t}\n\tbuf_len--;\n\tlen = 0;\n\twhile(1) {\n\t\tc = *str++;\n\t\t*out_ptr = c;\n\t\tif((c == 0) || (c == '.') || (c == ',') || (c == '#') ||\n\t\t\t\t\t\t\t(len >= buf_len)) {\n\t\t\t*out_ptr = 0;\n\t\t\treturn len;\n\t\t}\n\t\tout_ptr++;\n\t\tlen++;\n\t}\n}\n\nint\ndynatype_comma_arg(const char *str, word32 *type_or_aux_ptr)\n{\n\tchar\ttype_buf[8];\n\tchar\t*endstr;\n\tword32\tval, type_or_aux;\n\tint\tc, len, base, this_len;\n\tint\ti;\n\n\t// Read next char\n\t*type_or_aux_ptr = 0;\n\n\tc = *str++;\n\tif(c == 0) {\n\t\treturn 0;\n\t}\n\tlen = 1;\n\tc = tolower(c);\n\ttype_or_aux = c;\n\t// See if next char is $ for hex\n\tc = *str;\n\tbase = 0;\n\tif(c == '$') {\n\t\tbase = 16;\n\t\tstr++;\n\t\tlen++;\n\t}\n\tval = strtol(str, &endstr, base);\n\tthis_len = (int)(endstr - str);\n\tif((val == 0) && (this_len < 2) && (base == 0) &&\n\t\t\t\t\t\t\t(type_or_aux == 't')) {\n\t\t// Not a valid number\n\t\tfor(i = 0; i < 3; i++) {\n\t\t\tc = *str++;\n\t\t\tif(c == 0) {\n\t\t\t\treturn len;\n\t\t\t}\n\t\t\ttype_buf[i] = c;\n\t\t\tlen++;\n\t\t}\n\t\ttype_buf[3] = 0;\n\t\tval = dynatype_find_prodos_type(&type_buf[0]);\n\t\t*type_or_aux_ptr = 0x1000000 | val;\n\t} else {\n\t\tlen += this_len;\n\t}\n\tif(type_or_aux == 't') {\n\t\tif(val < 0x100) {\n\t\t\t*type_or_aux_ptr = 0x1000000 | ((val << 16) & 0xffffff);\n\t\t}\n\t} else if(type_or_aux == 'a') {\n\t\t*type_or_aux_ptr = 0x2000000 | (val & 0xffff);\n\t}\n\n\treturn len;\n}\n\nvoid\ndynatype_fix_unix_name(Dynapro_file *fileptr, char *outbuf_ptr, int path_max)\n{\n\tchar\tbuf[16];\n\tDynapro_file tmpfile;\n\tconst char *str;\n\tword32\taux_type;\n\n#if 0\n\tprintf(\"Looking at %s ftype:%02x aux:%04x\\n\", outbuf_ptr,\n\t\t\t\t\tfileptr->file_type, fileptr->aux_type);\n#endif\n\n\tif(fileptr->prodos_name[0] >= 0xd0) {\n\t\treturn;\t\t\t// Directory, or Dir/Volume Header\n\t}\n\tif((fileptr->prodos_name[0] & 0xf0) == 0x50) {\n\t\t// Forked file, add .applesingle\n\t\tcfg_strlcat(outbuf_ptr, \".applesingle\", path_max);\n\t\treturn;\n\t}\n\n\tmemset(&tmpfile, 0, sizeof(Dynapro_file));\n\n\t// See what this file defaults to as to type/aux\n\t(void)dynatype_detect_file_type(&tmpfile, outbuf_ptr, 0x10);\n\n\t// Otherwise, add ,ttype and ,a$aux as needed\n\tif(tmpfile.file_type != fileptr->file_type) {\n\t\tstr = dynatype_find_file_type(fileptr->file_type);\n\t\tif(str) {\n\t\t\taux_type = dynatype_find_prodos_type(str);\n\t\t} else {\n\t\t\tstr = &buf[0];\n\t\t\tbuf[15] = 0;\n\t\t\tsnprintf(&buf[0], 15, \"$%02x\", fileptr->file_type);\n\t\t}\n\t\tcfg_strlcat(outbuf_ptr, \",t\", path_max);\n\t\tcfg_strlcat(outbuf_ptr, str, path_max);\n\t}\n\n\t(void)dynatype_detect_file_type(&tmpfile, outbuf_ptr, 0x10);\n\taux_type = fileptr->aux_type;\n\n\tif(aux_type != tmpfile.aux_type) {\n\t\tbuf[15] = 0;\n\t\tsnprintf(&buf[0], 15, \",a$%04x\", aux_type & 0xffff);\n\t\tcfg_strlcat(outbuf_ptr, &buf[0], path_max);\n\t}\n\n\t// printf(\"dynatype_new_unix_name: %s\\n\", outbuf_ptr);\n\n\t// Check that it succeeded\n\t(void)dynatype_detect_file_type(&tmpfile, outbuf_ptr, 0x10);\n\tif((tmpfile.file_type != fileptr->file_type) ||\n\t\t\t\t(tmpfile.aux_type != fileptr->aux_type)) {\n\t\thalt_printf(\"File %s want ftype:%02x aux:%04x, got:%02x %04x\\n\",\n\t\t\toutbuf_ptr, fileptr->file_type, fileptr->aux_type,\n\t\t\ttmpfile.file_type, tmpfile.aux_type);\n\t\texit(1);\n\t}\n}\n"
  },
  {
    "path": "upstream/kegs/src/dyna_validate.c",
    "content": "const char rcsid_dyna_validate_c[] = \"@(#)$KmKId: dyna_validate.c,v 1.8 2023-05-21 20:06:24+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2021-2022 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// Main information is from Beneath Apple ProDOS which has disk layout\n//  descriptions.  Forked files are described in Technote tn-pdos-025.\n\n#include \"defc.h\"\n\nword32\ndynapro_validate_header(Disk *dsk, Dynapro_file *fileptr, word32 dir_byte,\n\t\t\t\t\t\tword32 parent_dir_byte)\n{\n\tword32\tstorage_type, exp_type, val, parent_block, exp_val;\n\n\tstorage_type = fileptr->prodos_name[0] & 0xf0;\n\texp_type = 0xe0;\n\tif(dir_byte == 0x0404) {\n\t\texp_type = 0xf0;\t\t// Volume header\n\t}\n\tif(storage_type != exp_type) {\n\t\tprintf(\"Volume/Dir header is %02x at %07x\\n\",\n\t\t\t\t\t\tstorage_type, dir_byte);\n\t\treturn 0;\n\t}\n\n\tif(fileptr->aux_type != 0x0d27) {\n\t\tprintf(\"entry_length, entries_per_block:%04x at %07x\\n\",\n\t\t\tfileptr->aux_type, dir_byte);\n\t\treturn 0;\n\t}\n\n\tif(exp_type == 0xf0) {\t\t\t// Volume header\n\t\tval = fileptr->lastmod_time >> 16;\n\t\tif(val != 6) {\n\t\t\tprintf(\"bit_map_ptr:%04x, should be 6\\n\", val);\n\t\t\treturn 0;\n\t\t}\n\t\tval = fileptr->header_pointer;\n\t\tif(val != (dsk->dimage_size >> 9)) {\n\t\t\tprintf(\"Num blocks at %07x is wrong: %04x\\n\", dir_byte,\n\t\t\t\t\t\t\tval);\n\t\t\treturn 0;\n\t\t}\n\t} else {\t\t\t\t// Directory header\n\t\tval = fileptr->lastmod_time >> 16;\t// parent_pointer\n\t\tparent_block = parent_dir_byte >> 9;\n\t\tif(val != parent_block) {\n\t\t\tprintf(\"Dir at %07x parent:%04x should be %04x\\n\",\n\t\t\t\tdir_byte, val, parent_block);\n\t\t\treturn 0;\n\t\t}\n\t\tval = fileptr->header_pointer;\n\t\texp_val = ((parent_dir_byte & 0x1ff) - 4) / 0x27;\n\t\texp_val = (exp_val + 1) | 0x2700;\n\t\tif(val != exp_val) {\n\t\t\tprintf(\"Parent entry at %07x is:%04x, should be:%04x\\n\",\n\t\t\t\tdir_byte, val, exp_val);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\treturn 1;\n}\n\nvoid\ndynapro_validate_init_freeblks(byte *freeblks_ptr, word32 num_blocks)\n{\n\tword32\tnum_map_blocks, mask;\n\tint\tpos;\n\tword32\tui;\n\n\tfor(ui = 0; ui < (num_blocks + 7)/8; ui++) {\n\t\tfreeblks_ptr[ui] = 0xff;\n\t}\n\tfreeblks_ptr[0] &= 0x3f;\n\tif(num_blocks & 7) {\n\t\tfreeblks_ptr[num_blocks / 8] = 0xff00 >> (num_blocks & 7);\n\t}\n\n\tnum_map_blocks = (num_blocks + 4095) >> 12;\t// 4096 bits per block\n\tfor(ui = 0; ui < num_map_blocks; ui++) {\n\t\t// Mark blocks used in the bitmap as in use\n\t\tpos = (ui + 6) >> 3;\n\t\tmask = 0x80 >> ((ui + 6) & 7);\n\t\tfreeblks_ptr[pos] &= (~mask);\n\t}\n}\n\nword32\ndynapro_validate_freeblk(Disk *dsk, byte *freeblks_ptr, word32 block)\n{\n\tword32\tmask, ret;\n\tint\tpos;\n\n\t// Return != 0 if block is free (which is success), returns == 0\n\t//  if it is in use (which is an error).  Marks block as in use\n\tpos = block >> 3;\n\tif(block >= (dsk->dimage_size >> 9)) {\n\t\treturn 0x100;\t\t// Out of range\n\t}\n\tmask = 0x80 >> (block & 7);\n\tret = freeblks_ptr[pos] & mask;\n\tfreeblks_ptr[pos] &= (~mask);\n\n\tif(!ret) {\n\t\tprintf(\"Block %04x was already in use\\n\", block);\n\t}\n\treturn ret;\n}\n\nword32\ndynapro_validate_file(Disk *dsk, byte *freeblks_ptr, word32 block_num,\n\t\t\t\t\t\tword32 eof, int level_first)\n{\n\tbyte\t*bptr;\n\tword32\tnum_blocks, tmp, ret, exp_blocks, extra_blocks;\n\tint\tlevel, first;\n\tint\ti;\n\n\tlevel = level_first & 0xf;\n\tfirst = level_first & 0x10;\n\n\tif(!dynapro_validate_freeblk(dsk, freeblks_ptr, block_num)) {\n\t\treturn 0;\n\t}\n\tif(level_first == 0x15) {\n\t\treturn dynapro_validate_forked_file(dsk, freeblks_ptr,\n\t\t\t\t\t\t\tblock_num, eof);\n\t}\n\tif((level < 1) || (level >= 4)) {\n\t\tprintf(\"level %d out of range, %08x\\n\", level, level_first);\n\t\treturn 0;\n\t}\n\tif(level == 1) {\n\t\treturn 1;\n\t}\n\tnum_blocks = 1;\n\tbptr = &(dsk->raw_data[block_num * 0x200]);\n\tfor(i = 0; i < 256; i++) {\n\t\ttmp = bptr[i] + (bptr[256 + i] << 8);\n\t\tif(tmp == 0) {\n\t\t\tif(first) {\n\t\t\t\tprintf(\"First block is spare, illegal!\\n\");\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tret = dynapro_validate_file(dsk, freeblks_ptr, tmp, eof,\n\t\t\t\t\t\t\tfirst | (level - 1));\n\t\tif(ret == 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tnum_blocks += ret;\n\t\tfirst = 0;\n\t}\n\n\tif(level_first & 0x10) {\n\t\t// Try to estimate exp_blocks based on eof\n\t\texp_blocks = (eof + 0x1ff) >> 9;\n\t\tif(exp_blocks == 0) {\n\t\t\texp_blocks = 1;\n\t\t} else if(exp_blocks > 1) {\n\t\t\t// Add in sapling blocks\n\t\t\textra_blocks = ((exp_blocks + 255) >> 8);\n\t\t\tif(exp_blocks > 256) {\n\t\t\t\textra_blocks++;\n\t\t\t}\n\t\t\texp_blocks += extra_blocks;\n\t\t}\n\t\tif(num_blocks > exp_blocks) {\n\t\t\tprintf(\"blocks_used:%04x, eof:%07x, exp:%04x\\n\",\n\t\t\t\tnum_blocks, eof, exp_blocks);\n\t\t\treturn 0;\n\t\t}\n\t}\n\treturn num_blocks;\n}\n\nword32\ndynapro_validate_forked_file(Disk *dsk, byte *freeblks_ptr, word32 block_num,\n\t\t\t\t\t\t\t\tword32 eof)\n{\n\tbyte\t*bptr;\n\tword32\tnum_blocks, tmp, ret, size, type, exp_blocks;\n\tint\tlevel;\n\tint\ti;\n\n\tbptr = &(dsk->raw_data[block_num * 0x200]);\n\n\tif(eof != 0x200) {\n\t\tprintf(\"In forked file block %04x, eof in dir:%08x, exp 0200\\n\",\n\t\t\t\t\t\t\t\tblock_num, eof);\n\t\treturn 0;\n\t}\n\t// Check that most of the block is 0\n\tfor(i = 44; i < 512; i++) {\n\t\tif((i >= 0x100) && (i < 0x108)) {\n\t\t\tcontinue;\n\t\t}\n\t\tif(bptr[i] != 0) {\n\t\t\tprintf(\"In forked file block:%04x, byte %03x is %02x\\n\",\n\t\t\t\tblock_num, i, bptr[i]);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t// Check for basic Finder Info format\n\tfor(i = 0; i < 2; i++) {\n\t\tsize = bptr[8 + 18*i];\n\t\ttype = bptr[9 + 18*i];\n\t\tif(((size != 0) && (size != 18)) || (type > 2)) {\n\t\t\tprintf(\"Finder Info size %04x+%03x=%02x, type:%02x\\n\",\n\t\t\t\t\tblock_num, 8 + 18*i, size, type);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tnum_blocks = 1;\n\tfor(i = 0; i < 2; i++) {\n\t\ttmp = bptr[1 + 0x100*i] + (bptr[2 + 0x100*i] << 8);\n\t\tif(tmp == 0) {\n\t\t\tprintf(\"First fork %d block is spare, illegal!\\n\", i);\n\t\t\treturn 0;\n\t\t}\n\t\teof = bptr[5 + 0x100*i] + (bptr[6 + 0x100*i] << 8) +\n\t\t\t\t\t\t(bptr[7 + 0x100*i] << 16);\n\t\tlevel = bptr[0 + 0x100*i];\n\t\tret = dynapro_validate_file(dsk, freeblks_ptr, tmp, eof,\n\t\t\t\t\t\t\t\t0x10 | level);\n\t\tif(ret == 0) {\n\t\t\tprintf(\"Fork %d failed, eof:%08x, block:%04x \"\n\t\t\t\t\"fork:%04x, level:%d\\n\", i, eof, block_num,\n\t\t\t\ttmp, level);\n\t\t\treturn 0;\n\t\t}\n\t\texp_blocks = bptr[3 + 0x100*i] + (bptr[4 + 0x100*i] << 8);\n\t\tif(ret != exp_blocks) {\n\t\t\tprintf(\"Fork %d at %04x, blocks:%04x, exp:%04x\\n\",\n\t\t\t\ti, block_num, ret, exp_blocks);\n\t\t}\n\t\tnum_blocks += ret;\n\t}\n\n\treturn num_blocks;\n}\n\nword32\ndynapro_validate_dir(Disk *dsk, byte *freeblks_ptr, word32 dir_byte,\n\t\t\t\tword32 parent_dir_byte, word32 exp_blocks_used)\n{\n\tchar\tbuf32[32];\n\tDynapro_file localfile;\n\tbyte\t*bptr;\n\tword32\tstart_dir_block, last_block, max_block, tmp_byte, sub_blocks;\n\tword32\tret, act_entries, exp_entries, blocks_used, prev, next;\n\tint\tcnt, is_header;\n\n\t// Read directory, make sure each entry is consistent\n\t// Return 0 if there is damage, != 0 if OK.\n\tbptr = dsk->raw_data;\n\tstart_dir_block = dir_byte >> 9;\n\tlast_block = 0;\n\tmax_block = (word32)(dsk->dimage_size >> 9);\n\tcnt = 0;\n\tis_header = 1;\n\texp_entries = 0xdeadbeef;\n\tact_entries = 0;\n\tblocks_used = 0;\n\twhile(dir_byte) {\n\t\tif((dir_byte & 0x1ff) == 4) {\n\t\t\t// First entry in this block, check prev/next\n\t\t\ttmp_byte = dir_byte & -0x200;\t\t// Block align\n\t\t\tprev = dynapro_get_word16(&bptr[tmp_byte + 0]);\n\t\t\tnext = dynapro_get_word16(&bptr[tmp_byte + 2]);\n\t\t\tif((prev != last_block) || (next >= max_block)) {\n\t\t\t\tprintf(\"dir at %07x is damaged in links\\n\",\n\t\t\t\t\t\t\t\tdir_byte);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tlast_block = dir_byte >> 9;\n\t\t\tret = dynapro_validate_freeblk(dsk, freeblks_ptr,\n\t\t\t\t\t\t\t\tdir_byte >> 9);\n\t\t\tif(!ret) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tblocks_used++;\n\t\t}\n\t\tif(cnt++ >= 65536) {\n\t\t\tprintf(\"Loop detected, dir_byte:%07x\\n\", dir_byte);\n\t\t\treturn 0;\n\t\t}\n\t\tret = dynapro_fill_fileptr_from_prodos(dsk, &localfile,\n\t\t\t\t\t\t\t&buf32[0], dir_byte);\n\t\tif(ret == 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tif(ret != 1) {\n\t\t\tact_entries = act_entries + 1 - is_header;\n\t\t}\n\t\tif(is_header) {\n\t\t\tif(ret == 1) {\n\t\t\t\tprintf(\"Volume/Dir header is erased\\n\");\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tret = dynapro_validate_header(dsk, &localfile, dir_byte,\n\t\t\t\t\t\t\tparent_dir_byte);\n\t\t\tif(ret == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\texp_entries = localfile.lastmod_time & 0xffff;\n\t\t} else if(ret != 1) {\n\t\t\tif(localfile.header_pointer != start_dir_block) {\n\t\t\t\tprintf(\"At %07x, header_ptr:%04x != %04x\\n\",\n\t\t\t\t\tdir_byte, localfile.header_pointer,\n\t\t\t\t\tstart_dir_block);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif(localfile.prodos_name[0] >= 0xd0) {\n\t\t\t\tsub_blocks = localfile.blocks_used;\n\t\t\t\tif(localfile.eof != (sub_blocks * 0x200UL)) {\n\t\t\t\t\tprintf(\"At %07x, eof:%08x != %08x\\n\",\n\t\t\t\t\t\tdir_byte, localfile.eof,\n\t\t\t\t\t\tsub_blocks * 0x200U);\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tret = dynapro_validate_dir(dsk, freeblks_ptr,\n\t\t\t\t\t(localfile.key_block * 0x200) + 4,\n\t\t\t\t\tdir_byte, sub_blocks);\n\t\t\t\tif(ret == 0) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tret = dynapro_validate_file(dsk, freeblks_ptr,\n\t\t\t\t\tlocalfile.key_block, localfile.eof,\n\t\t\t\t\t0x10 | (localfile.prodos_name[0] >> 4));\n\t\t\t\tif(ret == 0) {\n\t\t\t\t\tprintf(\"At %07x, bad file\\n\", dir_byte);\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tif(localfile.blocks_used != ret) {\n\t\t\t\t\tprintf(\"At %07x, blocks_used prodos \"\n\t\t\t\t\t\t\"%04x != %04x calc\\n\", dir_byte,\n\t\t\t\t\t\tlocalfile.blocks_used, ret);\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tis_header = 0;\n\t\tdir_byte = dir_byte + 0x27;\n\t\ttmp_byte = (dir_byte & 0x1ff) + 0x27;\n\t\tif(tmp_byte < 0x200) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttmp_byte = (dir_byte - 0x27) & (0 - 0x200UL);\n\t\tdir_byte = dynapro_get_word16(&bptr[tmp_byte + 2]) * 0x200UL;\n\t\tif(dir_byte == 0) {\n\t\t\tif(act_entries != exp_entries) {\n\t\t\t\tprintf(\"act_entries:%04x != exp:%04x, \"\n\t\t\t\t\t\"dir_block:%04x\\n\", act_entries,\n\t\t\t\t\texp_entries, start_dir_block);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif(blocks_used != exp_blocks_used) {\n\t\t\t\tprintf(\"At dir %07x, blocks_used:%04x!=%04x \"\n\t\t\t\t\t\"exp\\n\", tmp_byte, blocks_used,\n\t\t\t\t\texp_blocks_used);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn 1;\n\t\t}\n\t\tdir_byte += 4;\n\t\tif(dir_byte >= (max_block * 0x200L)) {\n\t\t\tprintf(\" invalid link pointer %07x\\n\", dir_byte);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nint\ndynapro_validate_disk(Disk *dsk)\n{\n\tbyte\tfreeblks[65536/8];\t\t// 8KB\n\tbyte\t*bptr;\n\tword32\tnum_blocks, ret;\n\tword32\tui;\n\n\tnum_blocks = (word32)(dsk->dimage_size >> 9);\n\tprintf(\"******************************\\n\");\n\tprintf(\"Validate disk: %s, blocks:%05x\\n\", dsk->name_ptr, num_blocks);\n\tdynapro_validate_init_freeblks(&freeblks[0], num_blocks);\n\n\t// Validate starting at directory in block 2\n\tret = dynapro_validate_dir(dsk, &freeblks[0], 0x0404, 0, 4);\n\tif(!ret) {\n\t\tprintf(\"Disk does not validate!\\n\");\n\t\texit(1);\n\t\treturn ret;\n\t}\n\n\t// Check freeblks\n\tbptr = &(dsk->raw_data[6*0x200]);\n\tfor(ui = 0; ui < (num_blocks + 7)/8; ui++) {\n\t\tif(freeblks[ui] != bptr[ui]) {\n\t\t\tprintf(\"Expected free mask for blocks %04x-%04x:%02x, \"\n\t\t\t\t\"but it is %02x\\n\", ui*8, ui*8 + 7,\n\t\t\t\tfreeblks[ui], bptr[ui]);\n\t\t\texit(1);\n\t\t\treturn 0;\n\t\t}\n\t}\n\treturn 1;\n}\n\nvoid\ndynapro_validate_any_image(Disk *dsk)\n{\n\tbyte\t*bufptr;\n\tdword64\tdsize;\n\tint\tret;\n\n\t// If dsk->raw_data already set, just use it.  Otherwise, we need to\n\t//  temporarily read in entire image, set it, do validate, and then\n\t//  free it\n\n\tif(dsk->fd < 0) {\n\t\treturn;\t\t// No disk\n\t}\n\tif(dsk->wozinfo_ptr) {\n\t\treturn;\n\t}\n\tdsize = dsk->dimage_size;\n\tbufptr = 0;\n\tif((dsize >> 31) != 0) {\n\t\tprintf(\"Disk is too large, not valid\\n\");\n\t\tret = 0;\n\t} else if(dsk->raw_data == 0) {\n\t\tbufptr = malloc((size_t)dsize);\n\t\tdsk->raw_data = bufptr;\n\t\tcfg_read_from_fd(dsk->fd, bufptr, 0, dsize);\n\t\tret = dynapro_validate_disk(dsk);\n\t\tdsk->raw_data = 0;\n\t\tfree(bufptr);\n\t} else {\n\t\tret = dynapro_validate_disk(dsk);\n\t}\n\tprintf(\"validate_disk returned is_good: %d (0 is bad)\\n\", ret);\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/dynapro.c",
    "content": "const char rcsid_dynapro_c[] = \"@(#)$KmKId: dynapro.c,v 1.49 2023-09-23 17:53:24+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2021-2022 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// Main information is from Beneath Apple ProDOS which has disk layout\n//  descriptions.  Upper/lowercase is from Technote tn-gsos-008, and\n//  forked files are storage_type $5 from Technote tn-pdos-025.\n\n#include \"defc.h\"\n#ifdef _WIN32\n# include \"win_dirent.h\"\n#else\n# include <dirent.h>\n#endif\n#include <time.h>\n\nextern int Verbose;\n\n#define DYNAPRO_PATH_MAX\t\t2048\nchar g_dynapro_path_buf[DYNAPRO_PATH_MAX];\n\nextern word32 g_vbl_count, g_iwm_dynapro_last_vbl_count;\n\nbyte g_prodos_block0[512] = {\n\t// From Beagle Bros Pro-Byter disk\n\t// This is a ProDOS boot block, able to load PRODOS and jump to it\n\t0x01, 0x38, 0xb0, 0x03, 0x4c, 0x32, 0xa1, 0x86,\t// 0x000\n\t0x43, 0xc9, 0x03, 0x08, 0x8a, 0x29, 0x70, 0x4a,\n\t0x4a, 0x4a, 0x4a, 0x09, 0xc0, 0x85, 0x49, 0xa0,\t// 0x010\n\t0xff, 0x84, 0x48, 0x28, 0xc8, 0xb1, 0x48, 0xd0,\n\t0x3a, 0xb0, 0x0e, 0xa9, 0x03, 0x8d, 0x00, 0x08,\t// 0x020\n\t0xe6, 0x3d, 0xa5, 0x49, 0x48, 0xa9, 0x5b, 0x48,\n\t0x60, 0x85, 0x40, 0x85, 0x48, 0xa0, 0x63, 0xb1,\t// 0x030\n\t0x48, 0x99, 0x94, 0x09, 0xc8, 0xc0, 0xeb, 0xd0,\n\t0xf6, 0xa2, 0x06, 0xbc, 0x1d, 0x09, 0xbd, 0x24,\t// 0x040\n\t0x09, 0x99, 0xf2, 0x09, 0xbd, 0x2b, 0x09, 0x9d,\n\t0x7f, 0x0a, 0xca, 0x10, 0xee, 0xa9, 0x09, 0x85,\t// 0x050\n\t0x49, 0xa9, 0x86, 0xa0, 0x00, 0xc9, 0xf9, 0xb0,\n\t0x2f, 0x85, 0x48, 0x84, 0x60, 0x84, 0x4a, 0x84,\t// 0x060\n\t0x4c, 0x84, 0x4e, 0x84, 0x47, 0xc8, 0x84, 0x42,\n\t0xc8, 0x84, 0x46, 0xa9, 0x0c, 0x85, 0x61, 0x85,\t// 0x070\n\t0x4b, 0x20, 0x12, 0x09, 0xb0, 0x68, 0xe6, 0x61,\n\t0xe6, 0x61, 0xe6, 0x46, 0xa5, 0x46, 0xc9, 0x06,\t// 0x080\n\t0x90, 0xef, 0xad, 0x00, 0x0c, 0x0d, 0x01, 0x0c,\n\t0xd0, 0x6d, 0xa9, 0x04, 0xd0, 0x02, 0xa5, 0x4a,\t// 0x090\n\t0x18, 0x6d, 0x23, 0x0c, 0xa8, 0x90, 0x0d, 0xe6,\n\t0x4b, 0xa5, 0x4b, 0x4a, 0xb0, 0x06, 0xc9, 0x0a,\t// 0x0a0\n\t0xf0, 0x55, 0xa0, 0x04, 0x84, 0x4a, 0xad, 0x02,\n\t0x09, 0x29, 0x0f, 0xa8, 0xb1, 0x4a, 0xd9, 0x02,\t// 0x0b0\n\t0x09, 0xd0, 0xdb, 0x88, 0x10, 0xf6, 0x29, 0xf0,\n\t0xc9, 0x20, 0xd0, 0x3b, 0xa0, 0x10, 0xb1, 0x4a,\t// 0x0c0\n\t0xc9, 0xff, 0xd0, 0x33, 0xc8, 0xb1, 0x4a, 0x85,\n\t0x46, 0xc8, 0xb1, 0x4a, 0x85, 0x47, 0xa9, 0x00,\t// 0x0d0\n\t0x85, 0x4a, 0xa0, 0x1e, 0x84, 0x4b, 0x84, 0x61,\n\t0xc8, 0x84, 0x4d, 0x20, 0x12, 0x09, 0xb0, 0x17,\t// 0x0e0\n\t0xe6, 0x61, 0xe6, 0x61, 0xa4, 0x4e, 0xe6, 0x4e,\n\t0xb1, 0x4a, 0x85, 0x46, 0xb1, 0x4c, 0x85, 0x47,\t// 0x0f0\n\t0x11, 0x4a, 0xd0, 0xe7, 0x4c, 0x00, 0x20, 0x4c,\n\n\t0x3f, 0x09, 0x26, 0x50, 0x52, 0x4f, 0x44, 0x4f,\t// 0x100\n\t0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,\n\t0x20, 0x20, 0xa5, 0x60, 0x85, 0x44, 0xa5, 0x61,\t// 0x110\n\t0x85, 0x45, 0x6c, 0x48, 0x00, 0x08, 0x1e, 0x24,\n\t0x3f, 0x45, 0x47, 0x76, 0xf4, 0xd7, 0xd1, 0xb6,\t// 0x120\n\t0x4b, 0xb4, 0xac, 0xa6, 0x2b, 0x18, 0x60, 0x4c,\n\t0xbc, 0x09, 0xa9, 0x9f, 0x48, 0xa9, 0xff, 0x48,\t// 0x130\n\t0xa9, 0x01, 0xa2, 0x00, 0x4c, 0x79, 0xf4, 0x20,\n\t0x58, 0xfc, 0xa0, 0x1c, 0xb9, 0x50, 0x09, 0x99,\t// 0x140\n\t0xae, 0x05, 0x88, 0x10, 0xf7, 0x4c, 0x4d, 0x09,\n\t0xaa, 0xaa, 0xaa, 0xa0, 0xd5, 0xce, 0xc1, 0xc2,\t// 0x150\n\t0xcc, 0xc5, 0xa0, 0xd4, 0xcf, 0xa0, 0xcc, 0xcf,\n\t0xc1, 0xc4, 0xa0, 0xd0, 0xd2, 0xcf, 0xc4, 0xcf,\t// 0x160\n\t0xd3, 0xa0, 0xaa, 0xaa, 0xaa, 0xa5, 0x53, 0x29,\n\t0x03, 0x2a, 0x05, 0x2b, 0xaa, 0xbd, 0x80, 0xc0,\t// 0x170\n\t0xa9, 0x2c, 0xa2, 0x11, 0xca, 0xd0, 0xfd, 0xe9,\n\t0x01, 0xd0, 0xf7, 0xa6, 0x2b, 0x60, 0xa5, 0x46,\t// 0x180\n\t0x29, 0x07, 0xc9, 0x04, 0x29, 0x03, 0x08, 0x0a,\n\t0x28, 0x2a, 0x85, 0x3d, 0xa5, 0x47, 0x4a, 0xa5,\t// 0x190\n\t0x46, 0x6a, 0x4a, 0x4a, 0x85, 0x41, 0x0a, 0x85,\n\t0x51, 0xa5, 0x45, 0x85, 0x27, 0xa6, 0x2b, 0xbd,\t// 0x1a0\n\t0x89, 0xc0, 0x20, 0xbc, 0x09, 0xe6, 0x27, 0xe6,\n\t0x3d, 0xe6, 0x3d, 0xb0, 0x03, 0x20, 0xbc, 0x09,\t// 0x1b0\n\t0xbc, 0x88, 0xc0, 0x60, 0xa5, 0x40, 0x0a, 0x85,\n\t0x53, 0xa9, 0x00, 0x85, 0x54, 0xa5, 0x53, 0x85,\t// 0x1c0\n\t0x50, 0x38, 0xe5, 0x51, 0xf0, 0x14, 0xb0, 0x04,\n\t0xe6, 0x53, 0x90, 0x02, 0xc6, 0x53, 0x38, 0x20,\t// 0x1d0\n\t0x6d, 0x09, 0xa5, 0x50, 0x18, 0x20, 0x6f, 0x09,\n\t0xd0, 0xe3, 0xa0, 0x7f, 0x84, 0x52, 0x08, 0x28,\t// 0x1e0\n\t0x38, 0xc6, 0x52, 0xf0, 0xce, 0x18, 0x08, 0x88,\n\t0xf0, 0xf5, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0x00,\t// 0x1f0\n\t0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\n};\n\nword32\ndynapro_get_word32(byte *bptr)\n{\n\treturn (bptr[3] << 24) | (bptr[2] << 16) | (bptr[1] << 8) | bptr[0];\n}\n\nword32\ndynapro_get_word24(byte *bptr)\n{\n\treturn (bptr[2] << 16) | (bptr[1] << 8) | bptr[0];\n}\n\nword32\ndynapro_get_word16(byte *bptr)\n{\n\treturn (bptr[1] << 8) | bptr[0];\n}\n\nvoid\ndynapro_set_word24(byte *bptr, word32 val)\n{\n\t// Write 3 bytes in little-endian form\n\t*bptr++ = val;\n\t*bptr++ = (val >> 8);\n\t*bptr++ = (val >> 16);\n}\n\nvoid\ndynapro_set_word32(byte *bptr, word32 val)\n{\n\t// Write 4 bytes in little-endian form\n\t*bptr++ = val;\n\t*bptr++ = (val >> 8);\n\t*bptr++ = (val >> 16);\n\t*bptr++ = (val >> 24);\n}\n\nvoid\ndynapro_set_word16(byte *bptr, word32 val)\n{\n\t// Write 2 bytes in little-endian form\n\t*bptr++ = val;\n\t*bptr++ = (val >> 8);\n}\n\nvoid\ndynapro_error(Disk *dsk, const char *fmt, ...)\n{\n\tDynapro_info *info_ptr;\n\tva_list\targs;\n\n\tva_start(args, fmt);\n\tcfg_err_vprintf(\"Dynapro\", fmt, args);\n\tva_end(args);\n\tinfo_ptr = dsk->dynapro_info_ptr;\n\tif(info_ptr) {\n\t\tcfg_err_printf(\"\", \"Path: %s\\n\", info_ptr->root_path);\n\t}\n}\n\nDynapro_file *\ndynapro_alloc_file()\n{\n\tDynapro_file *fileptr;\n\n\tfileptr = calloc(1, sizeof(Dynapro_file));\n\treturn fileptr;\n}\n\nvoid\ndynapro_free_file(Dynapro_file *fileptr, int check_map)\n{\n\tif(!fileptr) {\n\t\treturn;\n\t}\n\tif(fileptr->subdir_ptr) {\n\t\tdynapro_free_recursive_file(fileptr->subdir_ptr, check_map);\n\t}\n\tfileptr->subdir_ptr = 0;\n\tfree(fileptr->unix_path);\n\tfileptr->unix_path = 0;\n\tfree(fileptr->buffer_ptr);\n\tfileptr->buffer_ptr = 0;\n\tfileptr->next_ptr = 0;\n\t// printf(\"FREE %p\\n\", fileptr);\n\tif(check_map && (fileptr->map_first_block != 0)) {\n\t\tprintf(\" ERROR: map_first_block is %08x\\n\",\n\t\t\t\t\t\tfileptr->map_first_block);\n\t\texit(1);\n\t}\n\tfree(fileptr);\n}\n\nvoid\ndynapro_free_recursive_file(Dynapro_file *fileptr, int check_map)\n{\n\tDynapro_file *nextptr;\n\n\tif(!fileptr) {\n\t\treturn;\n\t}\n\t// printf(\"free_recursive %s\\n\", fileptr->unix_path);\n\twhile(fileptr) {\n\t\tnextptr = fileptr->next_ptr;\n\t\tdynapro_free_file(fileptr, check_map);\n\t\tfileptr = nextptr;\n\t};\n}\n\nvoid\ndynapro_free_dynapro_info(Disk *dsk)\n{\n\tDynapro_info *info_ptr;\n\n\tinfo_ptr = dsk->dynapro_info_ptr;\n\tif(info_ptr) {\n\t\tfree(info_ptr->root_path);\n\n\t\tdynapro_free_recursive_file(info_ptr->volume_ptr, 0);\n\t\tinfo_ptr->volume_ptr = 0;\n\t}\n\tfree(info_ptr);\n\tdsk->dynapro_info_ptr = 0;\n}\n\nword32\ndynapro_find_free_block_internal(Disk *dsk)\n{\n\tbyte\t*bptr;\n\tword32\tnum_blocks, bitmap_size_bytes, val, mask;\n\tword32\tui;\n\tint\tj;\n\n\tnum_blocks = (word32)(dsk->raw_dsize >> 9);\n\tbitmap_size_bytes = (num_blocks + 7) >> 3;\n\tbptr = &(dsk->raw_data[6 * 512]);\t\t// Block 6\n\tfor(ui = 0; ui < bitmap_size_bytes; ui++) {\n\t\tval = bptr[ui];\n\t\tif(val == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tmask = 0x80;\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\tif(val & mask) {\n\t\t\t\tbptr[ui] = val & (~mask);\n\t\t\t\treturn 8*ui + j;\n\t\t\t}\n\t\t\tmask = mask >> 1;\n\t\t}\n\t\treturn 0;\n\t}\n\treturn 0;\n}\n\nword32\ndynapro_find_free_block(Disk *dsk)\n{\n\tbyte\t*bptr;\n\tword32\tblock;\n\tint\ti;\n\n\t// Find first free block, and zero it out\n\n\tblock = dynapro_find_free_block_internal(dsk);\n\tif(block == 0) {\n\t\treturn 0;\n\t}\n\tbptr = &(dsk->raw_data[block * 512]);\n\tfor(i = 0; i < 512; i++) {\n\t\tbptr[i] = 0;\n\t}\n\n\treturn block;\n}\n\nbyte *\ndynapro_malloc_file(char *path_ptr, dword64 *dsize_ptr, int extra_size)\n{\n\tbyte\t*bptr;\n\tdword64\tdsize, dpos;\n\tint\tfd;\n\tint\ti;\n\n\t*dsize_ptr = 0;\n\tfd = open(path_ptr, O_RDONLY | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\treturn 0;\n\t}\n\tdsize = cfg_get_fd_size(fd);\n\n\tif((size_t)(dsize + extra_size) != (dsize + extra_size)) {\n\t\treturn 0;\n\t}\n\tbptr = malloc((size_t)(dsize + extra_size));\n\tif(bptr == 0) {\n\t\treturn bptr;\n\t}\n\t// printf(\"dynapro_malloc_file %p, size:%08lld\\n\", bptr, dsize);\n\tfor(i = 0; i < extra_size; i++) {\n\t\tbptr[dsize + i] = 0;\n\t}\n\tdpos = cfg_read_from_fd(fd, bptr, 0, dsize);\n\tclose(fd);\n\tif(dpos != dsize) {\n\t\tfree(bptr);\n\t\treturn 0;\n\t}\n\t*dsize_ptr = dsize;\n\treturn bptr;\n}\n\nvoid\ndynapro_join_path_and_file(char *outstr, const char *unix_path, const char *str,\n\t\t\t\t\t\tint path_max)\n{\n\tint\tlen;\n\n\t// Create \"unix_path\" + \"/\" + \"str\" in outstr (which has size path_max)\n\tcfg_strncpy(outstr, unix_path, path_max);\n\tlen = (int)strlen(outstr);\n\tif((len > 0) && (outstr[len - 1] != '/')) {\n\t\tcfg_strlcat(outstr, \"/\", path_max);\n\t}\n\tcfg_strlcat(outstr, str, path_max);\n}\n\n\nword32\ndynapro_fill_fileptr_from_prodos(Disk *dsk, Dynapro_file *fileptr,\n\t\t\t\tchar *buf32_ptr, word32 dir_byte)\n{\n\tbyte\t*bptr;\n\tword32\tupper_lower;\n\tint\tlen, c;\n\tint\ti;\n\n\tbuf32_ptr[0] = 0;\n\tif((dir_byte < 0x400) || (dir_byte >= dsk->dimage_size)) {\n\t\treturn 0;\t\t\t// Directory is damaged\n\t}\n\tif(!fileptr) {\n\t\treturn 0;\n\t}\n\tbptr = &(dsk->raw_data[dir_byte]);\n\tmemset(fileptr, 0, sizeof(Dynapro_file));\n\n\tfileptr->dir_byte = dir_byte;\n\tfileptr->file_type = bptr[0x10];\n\tfileptr->key_block = dynapro_get_word16(&bptr[0x11]);\n\tfileptr->blocks_used = dynapro_get_word16(&bptr[0x13]);\n\tfileptr->eof = dynapro_get_word24(&bptr[0x15]);\n\t//printf(\"Filling from entry %07x, eof:%06x\\n\", dir_byte, fileptr->eof);\n\tfileptr->creation_time = dynapro_get_word32(&bptr[0x18]);\n\tfileptr->upper_lower = dynapro_get_word16(&bptr[0x1c]);\n\tfileptr->aux_type = dynapro_get_word16(&bptr[0x1f]);\n\tfileptr->lastmod_time = dynapro_get_word32(&bptr[0x21]);\n\tfileptr->header_pointer = dynapro_get_word16(&bptr[0x25]);\n\tif(dir_byte == 0x404) {\t\t\t// Volume header\n\t\tfileptr->upper_lower = dynapro_get_word32(&bptr[0x1a]);\n\t\tfileptr->creation_time &= 0xffff;\n\t}\n\n\tlen = (bptr[0] & 0xf) + 1;\n\tupper_lower = fileptr->upper_lower;\n\tif((upper_lower & 0x8000) == 0) {\t\t// Not valid\n\t\tupper_lower = 0;\n\t}\n\tfor(i = 0; i < 16; i++) {\n\t\tc = bptr[i];\n\t\tif(i > len) {\n\t\t\tc = 0;\n\t\t}\n\t\tfileptr->prodos_name[i] = c;\n\t\tif(i > 0) {\n\t\t\tif(upper_lower & 0x4000) {\n\t\t\t\tif((c >= 'A') && (c <= 'Z')) {\n\t\t\t\t\tc = c - 'A' + 'a';\t// Make lower\n\t\t\t\t}\n\t\t\t}\n\t\t\tupper_lower = upper_lower << 1;\n\t\t\tbuf32_ptr[i - 1] = c;\n\t\t\tbuf32_ptr[i] = 0;\n\t\t}\n\t}\n\tif(((bptr[0] & 0xf0) == 0) || ((bptr[0] & 0xf) == 0)) {\n\t\tfileptr->prodos_name[0] = 0;\n\t\treturn 1;\t\t// Invalid entry\n\t}\n\tif(fileptr->prodos_name[0] >= 0xe0) {\t\t// Dir/Volume header\n\t\tfileptr->key_block = dir_byte >> 9;\n\t\tif((dir_byte & 0x1ff) != 4) {\n\t\t\tprintf(\"Header at dir_byte:%07x != 4\\n\", dir_byte);\n\t\t\treturn 0;\t\t\t// Not in first pos\n\t\t}\n\t\tif(bptr[-4] || bptr[-3]) {\n\t\t\tprintf(\"prev_link %02x,%02x should be 0\\n\",\n\t\t\t\t\t\tbptr[-4], bptr[-3]);\n\t\t\treturn 0;\t\t\t// Not first dir block\n\t\t}\n\t\tif(fileptr->prodos_name[0] >= 0xf0) {\n\t\t\tif(dir_byte != 0x0404) {\n\t\t\t\tprintf(\"Volume head dir_byte:%07x\\n\", dir_byte);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t} else if(dir_byte == 0x0404) {\n\t\t\tprintf(\"Directory head dir_byte 0x0404\\n\");\n\t\t\treturn 0;\t\t\t// 0xe0 in block 2->bad\n\t\t}\n\t} else {\n\t\t// Normal entry.  Make sure it's not the first entry in a dir\n\t\tif((bptr[-4] == 0) && (bptr[-3] == 0) &&\n\t\t\t\t\t\t((dir_byte & 0x1ff) == 4)) {\n\t\t\tprintf(\"dir_byte:%07x, normal, prev:0\\n\", dir_byte);\n\t\t\treturn 0;\t\t// This is a dir/volume header!\n\t\t}\n\t}\n#if 0\n\tprintf(\"Fill resulted in buf32:%s, upper_lower:%04x\\n\", buf32_ptr,\n\t\t\t\t\t\tfileptr->upper_lower);\n#endif\n\n\treturn 2;\t\t// OK\n}\n\nword32\ndynapro_diff_fileptrs(Dynapro_file *oldfileptr, Dynapro_file *newfileptr)\n{\n\tword32\tret, new_storage, old_storage;\n\tint\ti;\n\n\t// Return 0 if the directory is damaged\n\t// Return 1 if the entry is invalid (and not case 3!)\n\t// Return 3 if the entry was valid and is now deleted\n\t// Return 4 if no changes are needed\n\t// Return 5 if oldfileptr needs to be rewritten\n\t// Return 7 if oldfileptr needs to be erased and replaced with newfile\n\n\told_storage = oldfileptr->prodos_name[0];\n\tnew_storage = newfileptr->prodos_name[0];\n\tif(new_storage == 0) {\t\t\t\t// Erased\n\t\tif(old_storage >= 0xe0) {\t// Vol/Dir header\n\t\t\treturn 0;\n\t\t}\n\t\tif(oldfileptr->dir_byte == newfileptr->dir_byte) {\n\t\t\treturn 3;\t// Entry just deleted\n\t\t}\n\t\treturn 1;\t\t// Just an invalid entry\n\t}\n\tif(oldfileptr->dir_byte != newfileptr->dir_byte) {\n\t\treturn 0;\n\t}\n\tret = 4;\t\t// No changes needed\n\n\t// Handle file expanding from seedling to tree\n\tif((new_storage >= 0x10) && (new_storage < 0x40) &&\n\t\t\t(old_storage >= 0x10) && (old_storage < 0x40)) {\n\t\t// Copy upper 4 bits from new_storage to old_storage\n\t\told_storage = (old_storage & 0x0f) | (new_storage & 0xf0);\n\t\tif(oldfileptr->prodos_name[0] != old_storage) {\n\t\t\t// Storage type changed, rewrite the file\n\t\t\toldfileptr->prodos_name[0] = old_storage;\n\t\t\tret |= 5;\n\t\t}\n\t}\n\tfor(i = 0; i < 16; i++) {\n\t\tif(oldfileptr->prodos_name[i] != newfileptr->prodos_name[i]) {\n\t\t\tret |= 7;\t\t// Name changed\n\t\t}\n\t\toldfileptr->prodos_name[i] = newfileptr->prodos_name[i];\n\t}\n\n\tif(oldfileptr->file_type != newfileptr->file_type) {\n\t\tret |= 7;\t\t// Filetype changed\n\t\toldfileptr->file_type = newfileptr->file_type;\n\t}\n\tif(newfileptr->prodos_name[0] < 0xe0) {\n\t\t// Not a directory or volume header\n\t\tif(oldfileptr->key_block != newfileptr->key_block) {\n\t\t\tret |= 5;\t\t// Key block has changed\n\t\t\toldfileptr->key_block = newfileptr->key_block;\n\t\t}\n\t\tif(oldfileptr->blocks_used != newfileptr->blocks_used) {\n\t\t\t// ret stays 1, we don't care about this field\n\t\t\toldfileptr->blocks_used = newfileptr->blocks_used;\n\t\t}\n\t\tif(oldfileptr->eof != newfileptr->eof) {\n\t\t\tret |= 5;\t\t// eof has changed\n\t\t\toldfileptr->eof = newfileptr->eof;\n\t\t}\n\t} else {\n\t\t// Directory or volume header\n\t\t// Ignore key_block (used internally by dynapro.c, but not in\n\t\t//  the ProDOS disk image), blocks_used, eof.\n\t\t// We ignore file_count at +0x21,0x22.  But bitmap_ptr matters\n\t\t//  and if it moves, we are damaged\n\t\tif((oldfileptr->lastmod_time >> 16) !=\n\t\t\t\t\t(newfileptr->lastmod_time >> 16)) {\n\t\t\treturn 0;\t// Bitmap_ptr moved, we are damaged\n\t\t}\n\t}\n\tif(oldfileptr->upper_lower != newfileptr->upper_lower) {\n\t\tret |= 7;\t\t// lowercase flags have changed\n\t\toldfileptr->upper_lower = newfileptr->upper_lower;\n\t}\n\tif(oldfileptr->aux_type != newfileptr->aux_type) {\n\t\tret |= 5;\t\t// aux_type has changed\n\t\toldfileptr->aux_type = newfileptr->aux_type;\n\t}\n\tif(oldfileptr->header_pointer != newfileptr->header_pointer) {\n\t\treturn 0;\t\t// We are damaged\n\t}\n\tif(newfileptr->prodos_name[0] >= 0xe0) {\n\t\tif(ret > 5) {\n\t\t\tret = 5;\t// No renaming volume or dir headers\n\t\t}\n\t}\n\treturn ret;\n}\n\nword32\ndynapro_do_one_dir_entry(Disk *dsk, Dynapro_file *fileptr,\n\t\tDynapro_file *localfile_ptr, char *buf32_ptr, word32 dir_byte)\n{\n\tword32\tret, diffs;\n\n\tret = dynapro_fill_fileptr_from_prodos(dsk, localfile_ptr,\n\t\t\t\t\t\tbuf32_ptr, dir_byte);\n\tif((ret == 0) || ((ret == 1) && !fileptr)) {\n\t\treturn ret;\t\t// Damaged or not valid\n\t}\n\tif(!fileptr) {\n\t\treturn 2;\t\t// must allocate new\n\t}\n\n\t// Now, head_ptr must be non-null\n\tdiffs = dynapro_diff_fileptrs(fileptr, localfile_ptr);\n\treturn diffs;\n}\n\nvoid\ndynapro_fix_damaged_entry(Disk *dsk, Dynapro_file *fileptr)\n{\n\tif(fileptr->prodos_name[0] >= 0xe0) {\n\t\t// This is a volume/directory header.  Re-parse entire dir\n\t\tdynapro_handle_write_dir(dsk, fileptr->parent_ptr, fileptr,\n\t\t\t\t\t(fileptr->key_block * 0x200UL) + 4);\n\t} else if(fileptr->prodos_name[0] >= 0xd0) {\n\t\t// This is a directory entry.\n\t\tdynapro_handle_write_dir(dsk, fileptr, fileptr->subdir_ptr,\n\t\t\t\t\t(fileptr->key_block * 0x200UL) + 4);\n\t} else {\n\t\tdynapro_handle_write_file(dsk, fileptr);\n\t}\n}\n\nvoid\ndynapro_try_fix_damage(Disk *dsk, Dynapro_file *fileptr)\n{\n\t// Walk entire tree (recursing to dynapro_try_fix_damage)\n\tif(!fileptr) {\n\t\treturn;\n\t}\n\twhile(fileptr) {\n\t\tif(fileptr->damaged) {\n\t\t\tdyna_printf(\"try_fix_damage %p %s\\n\", fileptr,\n\t\t\t\t\t\t\tfileptr->unix_path);\n\t\t\tdynapro_fix_damaged_entry(dsk, fileptr);\n\t\t}\n\t\tdynapro_try_fix_damage(dsk, fileptr->subdir_ptr);\n\t\tfileptr = fileptr->next_ptr;\n\t}\n}\n\nvoid\ndynapro_try_fix_damaged_disk(Disk *dsk)\n{\n\tDynapro_info *info_ptr;\n\n\tinfo_ptr = dsk->dynapro_info_ptr;\n\tif(!info_ptr) {\t\t\t\t// This is impossible\n\t\treturn;\n\t}\n\tif(info_ptr->damaged == 0) {\n\t\treturn;\n\t}\n\n\tdyna_printf(\"************************************\\n\");\n\tdyna_printf(\"try_fix_damaged_dsk called, damaged:%d\\n\",\n\t\t\t\t\t\t\tinfo_ptr->damaged);\n\tdyna_printf(\" vbl_count:%d, g_iwm_dynapro_last_vbl_count:%d\\n\",\n\t\tg_vbl_count, g_iwm_dynapro_last_vbl_count);\n\n\tinfo_ptr->damaged = 0;\n\tdynapro_try_fix_damage(dsk, info_ptr->volume_ptr);\n\n\tdyna_printf(\"try_fix_damaged_dsk, damaged:%d\\n\", info_ptr->damaged);\n}\n\n\nvoid\ndynapro_new_unix_path(Dynapro_file *fileptr, const char *path_str,\n\t\t\t\t\t\tconst char *name_str)\n{\n\tif(fileptr->unix_path) {\n\t\tfree(fileptr->unix_path);\n\t}\n\tdynapro_join_path_and_file(&g_dynapro_path_buf[0], path_str, name_str,\n\t\t\t\t\t\t\tDYNAPRO_PATH_MAX);\n\tdynatype_fix_unix_name(fileptr, &g_dynapro_path_buf[0],\n\t\t\t\t\t\t\tDYNAPRO_PATH_MAX);\n\tfileptr->unix_path = kegs_malloc_str(&g_dynapro_path_buf[0]);\n}\n\nDynapro_file *\ndynapro_process_write_dir(Disk *dsk, Dynapro_file *parent_ptr,\n\t\t\t\tDynapro_file **head_ptr_ptr, word32 dir_byte)\n{\n\tchar\tbuf32[32];\n\tDynapro_file localfile;\n\tDynapro_file *fileptr, *prev_ptr, *head_ptr;\n\tbyte\t*bptr;\n\tconst char *str;\n\tword32\ttmp_byte, prev, next, ret, last_block, parent_dir_byte;\n\tint\tcnt, error, iret, is_header;\n\n\thead_ptr = *head_ptr_ptr;\t\t// head_ptr_ptr must be valid\n\t\t\t\t\t\t//  but head_ptr can be 0\n\t// We can be called with parent_ptr=0, head_ptr != 0: this is for the\n\t//  volume header.  Otherwise, parent_ptr should be valid.\n\t// If head_ptr==0, it means we need to allocate directory header and\n\t//  all other dir entries\n\t// head_ptr is a pointer to a directory or volume header.\n\t// For all entries, see if anything changed.  We need to also\n\t//  possibly update head_ptr->parent_ptr\n\t// Return 0 if the directory is damaged.  If directory only contains\n\t//  damaged files, try to fix them, and always return success\n\n\t// First, unmap the directory blocks (this is done even if nothing\n\t//  changed, we'll map them back at the end).\n\tif(head_ptr) {\n\t\tdynapro_unmap_file(dsk, head_ptr);\n\t}\n\n\tparent_dir_byte = 0;\n\tif(parent_ptr) {\n\t\tstr = parent_ptr->unix_path;\n\t\tparent_dir_byte = parent_ptr->dir_byte;\n\t\tif(head_ptr == 0) {\n\t\t\t// Do mkdir to make sure it exists\n#ifdef _WIN32\n\t\t\tiret = _mkdir(str);\n#else\n\t\t\tiret = mkdir(str, 0x1ff);\n#endif\n\t\t\terror = errno;\n\t\t\tdyna_printf(\"Did mkdir %s, iret:%d\\n\", str, iret);\n\t\t\tif(iret < 0) {\n\t\t\t\tif((error == EEXIST) || (error == EISDIR)) {\n\t\t\t\t\terror = 0;\t// These are OK errors\n\t\t\t\t}\n\t\t\t\tif(error) {\n\t\t\t\t\tprintf(\"mkdir(%s) failed, error=%d\\n\",\n\t\t\t\t\t\t\tstr, error);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tstr = head_ptr->unix_path;\t\t// volume header\n\t}\n#if 0\n\tprintf(\"process_write_dir str:%s %p parent:%p\\n\", str, head_ptr,\n\t\t\t\t\t\t\t\tparent_ptr);\n#endif\n\n\t// The directory blocks have already been unmapped\n\n\t// Then, walk the directory, noting if anything changed.  If new\n\t//  files appear in the directory, add then to the chain.  We may need\n\t//  to erase existing entries which no longer exist (or their directory\n\t//  entry was changed to a different file)\n\tbptr = dsk->raw_data;\n\tprev_ptr = 0;\n\tfileptr = head_ptr;\n\tlast_block = 0;\n\tcnt = 0;\n\tis_header = 1;\n\twhile(dir_byte) {\n\t\t//printf(\"process_write_dir, dir_byte:%07x, prev_ptr:%p\\n\",\n\t\t//\t\t\tdir_byte, prev_ptr);\n\t\tif((dir_byte & 0x1ff) == 4) {\n\t\t\t// First entry in this block: check prev/next\n\t\t\ttmp_byte = dir_byte & -0x200;\t\t// Block align\n\t\t\tprev = dynapro_get_word16(&bptr[tmp_byte + 0]);\n\t\t\tnext = dynapro_get_word16(&bptr[tmp_byte + 2]);\n\t\t\tif((prev != last_block) ||\n\t\t\t\t\t(next >= (dsk->raw_dsize >> 9))) {\n\t\t\t\t// This is a damaged directory\n\t\t\t\tprintf(\"dir %s is damaged in the link fields\\n\",\n\t\t\t\t\t\t\tstr);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tlast_block = dir_byte >> 9;\n\t\t}\n\t\tif(cnt++ >= 65536) {\n\t\t\tprintf(\"dir %s has a loop in block pointers\\n\",\n\t\t\t\t\t\t\thead_ptr->unix_path);\n\t\t\treturn 0;\n\t\t}\n\n\t\tret = dynapro_do_one_dir_entry(dsk, fileptr, &localfile,\n\t\t\t\t\t\t\t&buf32[0], dir_byte);\n#if 0\n\t\tprintf(\" do_one_dir_entry ret:%08x fileptr:%p, &localfile:%p\\n\",\n\t\t\t\tret, fileptr, &localfile);\n#endif\n\t\tif((ret == 7) && !is_header) {\t// Entry dramatically changed\n\t\t\t// Erase this file\n\t\t\tdynapro_mark_damaged(dsk, fileptr);\n\t\t}\n\t\tif(ret == 0) {\n\t\t\treturn 0;\n\t\t} else if((ret == 1) || (ret == 3)) {\n\t\t\tif((ret == 3) && fileptr) {\n\t\t\t\t// This entry was valid and is now deleted.\n\t\t\t\t//  Erase it right now and fix links\n\t\t\t\tdyna_printf(\"fileptr %p deleted\\n\", fileptr);\n\t\t\t\tprev_ptr->next_ptr = fileptr->next_ptr;\n\t\t\t\tdynapro_erase_free_entry(dsk, fileptr);\n\t\t\t}\n\t\t\tif(head_ptr == 0) {\n\t\t\t\tprintf(\"return, head_ptr==0, deleted file at \"\n\t\t\t\t\t\t\t\"%07x\\n\", dir_byte);\n\t\t\t\treturn 0;\t\t// Directory damaged\n\t\t\t}\n\t\t\tfileptr = prev_ptr;\n\t\t}\n\t\tif(ret == 2) {\n\t\t\t// prev_ptr->next_ptr is 0, this is a new entry we\n\t\t\t//  need to put on the list\n\t\t\tif(fileptr) {\n\t\t\t\thalt_printf(\"file %s was ignored!\\n\",\n\t\t\t\t\t\t\tfileptr->unix_path);\n\t\t\t\texit(1);\n\t\t\t}\n\t\t\tfileptr = dynapro_alloc_file();\n\t\t\tif(!fileptr) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\t*fileptr = localfile;\t\t// STRUCT copy!\n\t\t\tdyna_printf(\"Allocated new fileptr:%p\\n\", fileptr);\n\t\t\tfileptr->parent_ptr = parent_ptr;\n\t\t\tif(head_ptr) {\n\t\t\t\tfileptr->parent_ptr = head_ptr;\n\t\t\t}\n\t\t}\n\t\tif((ret == 2) || (ret == 7)) {\n\t\t\t// New entry, or dramatically changed, update path\n\t\t\tif(!head_ptr) {\n\t\t\t\tif(!parent_ptr) {\n\t\t\t\t\tprintf(\"parent_ptr is 0!\\n\");\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t\tparent_ptr->subdir_ptr = fileptr;\n\t\t\t\tprintf(\"2/7 set %p %s subdir=%p\\n\", parent_ptr,\n\t\t\t\t\tparent_ptr->unix_path, fileptr);\n\t\t\t\tfileptr->unix_path = kegs_malloc_str(str);\n\t\t\t\thead_ptr = fileptr;\n\t\t\t\t*head_ptr_ptr = head_ptr;\n\t\t\t} else {\n\t\t\t\tdyna_printf(\"Forming new path: %s buf32:%s\\n\",\n\t\t\t\t\t\t\t\tstr, buf32);\n\t\t\t\tdynapro_new_unix_path(fileptr, str, buf32);\n\t\t\t}\n\t\t\t// If we are a directory entry (fileptr->subdir_ptr!=0)\n\t\t\t//  then now fileptr->unix_path != subdirptr->unix_path\n\t\t\t// The subdir will be erased in\n\t\t\t//  dynapro_handle_changed_entry()\n\t\t\tif(prev_ptr) {\n\t\t\t\tprev_ptr->next_ptr = fileptr;\n\t\t\t}\n\t\t}\n\t\tif((ret == 5) || (ret == 2) || (ret == 7)) {\n\t\t\t// Changed, or new entry\n\t\t\tif(is_header) {\n\t\t\t\tret = dynapro_validate_header(dsk, fileptr,\n\t\t\t\t\t\tdir_byte, parent_dir_byte);\n\t\t\t\tif(ret == 0) {\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdynapro_handle_changed_entry(dsk, fileptr);\n\t\t\t}\n\t\t}\n\t\tprev_ptr = fileptr;\n\t\tfileptr = prev_ptr->next_ptr;\n\t\tdir_byte = dir_byte + 0x27;\n\t\ttmp_byte = (dir_byte & 0x1ff) + 0x27;\n\t\tis_header = 0;\n\t\tif(tmp_byte < 0x200) {\n\t\t\tcontinue;\n\t\t}\n\t\ttmp_byte = (dir_byte - 0x27) & (0 - 0x200UL);\n\t\tdir_byte = dynapro_get_word16(&bptr[tmp_byte + 2]) * 0x200UL;\n\t\tdyna_printf(\" dir link at %07x = %04x\\n\", tmp_byte + 2,\n\t\t\t\t\t\t\t\tdir_byte);\n\t\tif(dir_byte == 0) {\n\t\t\tif(fileptr) {\n\t\t\t\tprintf(\"At dir end, fileptr: %p\\n\", fileptr);\n\t\t\t\tprev_ptr->next_ptr = 0;\n\t\t\t\tdynapro_erase_free_dir(dsk, fileptr);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tret = dynapro_map_dir_blocks(dsk, head_ptr);\n\t\t\t// printf(\"process_write_dir %s done, remap ret:%d\\n\",\n\t\t\t//\t\t\thead_ptr->unix_path, ret);\n\t\t\tif(ret == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\treturn head_ptr;\t// Success\n\t\t}\n\t\tdir_byte += 4;\n\t\tif(dir_byte >= dsk->dimage_size) {\n\t\t\t// printf(\" invalid link pointer, dir_byte:%08x\\n\",\n\t\t\t//\t\t\tdir_byte);\n\t\t\treturn 0;\t\t// Bad link, get out\n\t\t}\n\t}\n\tdyna_printf(\"At end of process_write_dir, returning 0\\n\");\n\treturn 0;\n}\n\nvoid\ndynapro_handle_write_dir(Disk *dsk, Dynapro_file *parent_ptr,\n\t\t\t\tDynapro_file *head_ptr, word32 dir_byte)\n{\n\tDynapro_file *fileptr;\n\n\t//save_headptr = head_ptr;\n\tdyna_printf(\"handle_write_dir parent_ptr:%p, head_ptr:%p, dir_b:%07x\\n\",\n\t\t\tparent_ptr, head_ptr, dir_byte);\n\tif(parent_ptr && head_ptr) {\n\t\tdyna_printf(\" parent:%s head:%s\\n\", parent_ptr->unix_path,\n\t\t\t\t\t\t\thead_ptr->unix_path);\n\t\tif(parent_ptr->subdir_ptr != head_ptr) {\n\t\t\tprintf(\"parent subdir:%p does not match %p\\n\",\n\t\t\t\tparent_ptr->subdir_ptr, head_ptr);\n\t\t\texit(1);\n\t\t}\n\t}\n\tif(parent_ptr == 0) {\n\t\tif(head_ptr != dsk->dynapro_info_ptr->volume_ptr) {\n\t\t\tprintf(\"handle_write_dir %p %p %07x\\n\", parent_ptr,\n\t\t\t\t\t\thead_ptr, dir_byte);\n\t\t\texit(1);\n\t\t}\n\t}\n\tfileptr = dynapro_process_write_dir(dsk, parent_ptr, &head_ptr,\n\t\t\t\t\t\t\t\tdir_byte);\n\tif(fileptr == 0) {\n\t\t// Directory is damaged.  Free and erase it\n\t\tdynapro_erase_free_dir(dsk, head_ptr);\n\t\thead_ptr = 0;\n\t}\n\t//printf(\"handle_write_dir, process returned %p (was %p), parent:%p, \"\n\t//\t\"save:%p\\n\", fileptr, head_ptr, parent_ptr, save_headptr);\n\tif(parent_ptr) {\n\t\tparent_ptr->subdir_ptr = head_ptr;\n\t\tif(!fileptr) {\n\t\t\tparent_ptr->damaged = 1;\n\t\t\tdsk->dynapro_info_ptr->damaged = 1;\n\t\t}\n\t\tdyna_printf(\"hwd set parent %p %s subdir=%p\\n\", parent_ptr,\n\t\t\tparent_ptr->unix_path, head_ptr);\n\t}\n}\n\nword32\ndynapro_process_write_file(Disk *dsk, Dynapro_file *fileptr)\n{\n\tword32\tret;\n\n\t//printf(\"dynapro_process_write_file %p %s\\n\", fileptr,\n\t//\t\t\t\t\t\tfileptr->unix_path);\n\n\tif(fileptr->subdir_ptr) {\n\t\tprintf(\"dynapro_handle_write_file, has subdir: %s\\n\",\n\t\t\tfileptr->unix_path);\n\t\thalt_printf(\"dynapro_handle_write_file, has subdir: %s\\n\",\n\t\t\tfileptr->unix_path);\n\t\treturn 0;\n\t}\n\n\t// First, unmap the file (the sapling/tree blocks may have changed).\n\tdynapro_unmap_file(dsk, fileptr);\n\n\t// Then remap the blocks. This will copy the new data to buffer_ptr\n\n\tdynapro_debug_map(dsk, \"handle_write_file, right before re-map\");\n\n\tret = dynapro_map_file(dsk, fileptr, 1);\n\n\t// printf(\"dynapro_handle_write_file ending, ret:%d\\n\", ret);\n\n\treturn ret;\n}\n\nvoid\ndynapro_handle_write_file(Disk *dsk, Dynapro_file *fileptr)\n{\n\tword32\tret;\n\n\tret = dynapro_process_write_file(dsk, fileptr);\n\tif(ret == 0) {\n\t\tdynapro_mark_damaged(dsk, fileptr);\n\t}\n}\n\nvoid\ndynapro_handle_changed_entry(Disk *dsk, Dynapro_file *fileptr)\n{\n\t// printf(\"handle_changed_entry with fileptr:%p\\n\", fileptr);\n\tfileptr->damaged = 0;\n\tif(fileptr->prodos_name[0] >= 0xe0) {\n\t\t// Directory header, not valid to be called here\n\t\tfileptr->damaged = 1;\n\t\tdsk->dynapro_info_ptr->damaged = 1;\n\t} else if(fileptr->prodos_name[0] >= 0xd0) {\n\t\t// Directory entry\n\t\tif(fileptr->subdir_ptr) {\n\t\t\tdynapro_erase_free_dir(dsk, fileptr->subdir_ptr);\n\t\t\tfileptr->subdir_ptr = 0;\n\t\t}\n\t\tdynapro_handle_write_dir(dsk, fileptr, 0,\n\t\t\t\t\t(fileptr->key_block * 0x200UL) + 4);\n\t} else {\n\t\tdynapro_handle_write_file(dsk, fileptr);\n#if 0\n\t\tprintf(\"handle_changed_entry called handle_write_file for %p, \"\n\t\t\t\t\t\"%s\\n\", fileptr, fileptr->unix_path);\n#endif\n\t}\n}\n\nword32\ndynapro_write_to_unix_file(const char *unix_path, byte *data_ptr, word32 size)\n{\n\tdword64\tdret;\n\tint\tfd;\n\n\tfd = open(unix_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\tprintf(\"Open %s for writing failed\\n\", unix_path);\n\t\texit(1);\n\t\treturn 0;\n\t}\n\tdret = cfg_write_to_fd(fd, data_ptr, 0, size);\n\tclose(fd);\n\tdyna_printf(\"dynapro_write_to_unix: %s size:%d, dret:%lld\\n\",\n\t\t\t\t\t\tunix_path, size, dret);\n\n\n\tif(size == 0) {\n\t\treturn 1;\n\t}\n\treturn (word32)dret;\n}\n\nvoid\ndynapro_unmap_file(Disk *dsk, Dynapro_file *fileptr)\n{\n\tDynapro_file *this_fileptr;\n\tDynapro_map *map_ptr;\n\t//const char *str;\n\tword32\tmap_block, next_map_block, max_blocks;\n\tint\ti;\n\n\t//printf(\"File %p: %s is unmapped\\n\", fileptr, fileptr->unix_path);\n\tdynapro_debug_map(dsk, \"start unmap file\");\n\n\t// Unmap all blocks to this file/dir\n\tmap_ptr = dsk->dynapro_info_ptr->block_map_ptr;\n\tmax_blocks = (word32)(dsk->dimage_size >> 9);\n\tmap_block = fileptr->map_first_block;\n\t//printf(\" map_block:%04x, fileptr:%p %s\\n\", map_block, fileptr,\n\t//\t\t\t\t\tfileptr->unix_path);\n\tfileptr->map_first_block = 0;\n\t//printf(\"  unmap starting, map_block:%08x, max_blocks:%07x\\n\",\n\t//\t\t\tmap_block, max_blocks);\n\tfor(i = 0; i < 65536; i++) {\n\t\tif((map_block == 0) || (map_block >= max_blocks)) {\n\t\t\tbreak;\n\t\t}\n\t\tnext_map_block = map_ptr[map_block].next_map_block;\n\t\tthis_fileptr = map_ptr[map_block].file_ptr;\n\t\tif(this_fileptr != fileptr) {\n\t\t\t//str = \"??\";\n\t\t\tif(this_fileptr) {\n\t\t\t\t//str = this_fileptr->unix_path;\n\t\t\t\tthis_fileptr->damaged = 1;\n\t\t\t}\n#if 0\n\t\t\tprintf(\"Found map[%04x]=%s while walking %s\\n\",\n\t\t\t\tmap_block, str, fileptr->unix_path);\n#endif\n\t\t}\n\t\tmap_ptr[map_block].file_ptr = 0;\n\t\tmap_ptr[map_block].next_map_block = 0;\n\t\tmap_ptr[map_block].modified = 0;\n\t\t//printf(\"  just unmapped block %05x\\n\", map_block);\n\t\tmap_block = next_map_block;\n\t}\n\n\t// printf(\" unmap ending\\n\");\n}\n\nvoid\ndynapro_unlink_file(Dynapro_file *fileptr)\n{\n\tconst char *str;\n\tint\tret, err;\n\n\t// Try to unlink unix_path\n\tdyna_printf(\"Unlink %s (%p)\\n\", fileptr->unix_path, fileptr);\n\tif(fileptr->unix_path == 0) {\n\t\tprintf(\"unix_path of %p is null!\\n\", fileptr);\n\t\texit(1);\n\t}\n\tif(fileptr->subdir_ptr != 0) {\n\t\tprintf(\"unlink_file %s, but subdirptr is valid!\\n\",\n\t\t\t\tfileptr->unix_path);\n\t\texit(1);\n\t}\n\n\tret = unlink(fileptr->unix_path);\n\tif(ret != 0) {\n\t\t// Maybe it's a directory, rmdir\n\t\tret = rmdir(fileptr->unix_path);\n\t}\n\tif(ret != 0) {\n\t\t// Cannot erase, try to rename\n\t\tcfg_strncpy_dirname(&g_dynapro_path_buf[0], fileptr->unix_path,\n\t\t\t\t\t\t\tDYNAPRO_PATH_MAX);\n\t\tcfg_strlcat(&g_dynapro_path_buf[0], \".kegsrm_\",\n\t\t\t\t\t\t\tDYNAPRO_PATH_MAX);\n\t\tstr = cfg_str_basename(fileptr->unix_path);\n\t\tcfg_strlcat(&g_dynapro_path_buf[0], str, DYNAPRO_PATH_MAX);\n\t\tprintf(\"Could not erase %s, renaming to: %s\\n\",\n\t\t\tfileptr->unix_path, &g_dynapro_path_buf[0]);\n\t\tret = rename(fileptr->unix_path, &g_dynapro_path_buf[0]);\n\t\terr = errno;\n\t\tif(ret != 0) {\n\t\t\tprintf(\"Rename of %s failed, err:%d\\n\",\n\t\t\t\t\t\tfileptr->unix_path, err);\n\t\t}\n\t}\n}\n\nvoid\ndynapro_erase_free_entry(Disk *dsk, Dynapro_file *fileptr)\n{\n\tif(!fileptr) {\n\t\treturn;\n\t}\n\tdynapro_mark_damaged(dsk, fileptr);\n\n\tfileptr->next_ptr = 0;\n\tif(fileptr != dsk->dynapro_info_ptr->volume_ptr) {\n\t\t// Free everything--except the volume header\n\t\tdyna_printf(\"erase_free_entry erasing %p since it != %p\\n\",\n\t\t\tfileptr, dsk->dynapro_info_ptr->volume_ptr);\n\t\tdynapro_free_file(fileptr, 1);\n\t}\n}\n\nvoid\ndynapro_erase_free_dir(Disk *dsk, Dynapro_file *fileptr)\n{\n\tDynapro_file *nextptr, *parent_ptr, *save_fileptr;\n\n\tdyna_printf(\"dynapro_erase_free_dir of %p\\n\", fileptr);\n\tif(fileptr == 0) {\n\t\treturn;\n\t}\n\tdyna_printf(\"  dynapro_erase_free_dir of %s\\n\", fileptr->unix_path);\n\tdsk->dynapro_info_ptr->damaged = 1;\n\tparent_ptr = fileptr->parent_ptr;\n\tif(parent_ptr) {\n\t\tif(parent_ptr->subdir_ptr) {\n\t\t\tparent_ptr->damaged = 1;\n\t\t}\n\t}\n\tsave_fileptr = fileptr;\n\tnextptr = fileptr->next_ptr;\n\tfileptr->next_ptr = 0;\n\tfileptr = nextptr;\n\twhile(fileptr) {\n\t\tnextptr = fileptr->next_ptr;\n\t\tdynapro_erase_free_entry(dsk, fileptr);\n\t\tfileptr = nextptr;\n\t}\n\tdynapro_erase_free_entry(dsk, save_fileptr);\n}\n\nvoid\ndynapro_mark_damaged(Disk *dsk, Dynapro_file *fileptr)\n{\n\tif(fileptr == 0) {\n\t\treturn;\n\t}\n\tdyna_printf(\"dynapro_mark_damaged: %s damaged\\n\", fileptr->unix_path);\n\tfileptr->damaged = 1;\n\tdsk->dynapro_info_ptr->damaged = 1;\n\tdynapro_unmap_file(dsk, fileptr);\n\tif(fileptr->subdir_ptr) {\n\t\tdynapro_erase_free_dir(dsk, fileptr->subdir_ptr);\n\t\tfileptr->subdir_ptr = 0;\n\t}\n\n\tif((fileptr->prodos_name[0] >= 0xe0) && fileptr->parent_ptr) {\n\t\t// We are a directory header, mark the directory entry of our\n\t\t//  parent as damaged (but don't actually damage it)\n\t\tfileptr->parent_ptr->damaged = 1;\n\t} else if(fileptr != dsk->dynapro_info_ptr->volume_ptr) {\n\t\tdynapro_unlink_file(fileptr);\n\t}\n}\n\nint\ndynapro_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size)\n{\n\tDynapro_info *info_ptr;\n\tDynapro_map *map_ptr;\n\tDynapro_file *fileptr;\n\tbyte\t*bptr;\n\tword32\tui, block;\n\tint\tnum;\n\tint\ti;\n\n\t// Return 1 if write was done.  Return < 0 if an error occurs\n\n\tdyna_printf(\"\\n\");\n\tdyna_printf(\"------------------------------------------------\\n\");\n\tdyna_printf(\"dynapro_write to %08llx, size:%08x\\n\", doffset, size);\n\tdynapro_debug_update(dsk);\n\n\tbptr = dsk->raw_data;\n\tif((doffset + size) > dsk->dimage_size) {\n\t\tprintf(\"Write past end of disk, ignored\\n\");\n\t\treturn -1;\n\t}\n\tfor(ui = 0; ui < size; ui++) {\n#if 0\n\t\tif((bptr[doffset + ui] != bufptr[ui]) && (diffs < 500)) {\n\t\t\tprintf(\"%07llx:%02x (was %02x)\\n\", doffset+ui,\n\t\t\t\tbufptr[ui], bptr[doffset + ui]);\n\t\t\tdiffs++;\n\t\t}\n#endif\n\t\tbptr[doffset + ui] = bufptr[ui];\n\t}\n\n\tinfo_ptr = dsk->dynapro_info_ptr;\n\tif(info_ptr == 0) {\n\t\tprintf(\"dynapro_info_ptr==0\\n\");\n\t\treturn -1;\n\t}\n\n\tnum = (size + 511) >> 9;\n\tblock = (word32)(doffset >> 9);\n\tdyna_printf(\"Marking blocks %05x-%05x modified\\n\", block,\n\t\t\t\t\t\t\tblock + num - 1);\n\tfor(i = 0; i < num; i++) {\n\t\tmap_ptr = &(info_ptr->block_map_ptr[block + i]);\n\t\tmap_ptr->modified = 1;\n\t}\n\tfor(i = 0; i < num; i++) {\n\t\tmap_ptr = &(info_ptr->block_map_ptr[block + i]);\n\t\tif(!map_ptr->modified) {\n\t\t\tcontinue;\t\t\t// Already cleared\n\t\t}\n\t\tfileptr = map_ptr->file_ptr;\n\t\tif(fileptr == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tif(fileptr->prodos_name[0] >= 0xe0) {\n\t\t\tdynapro_handle_write_dir(dsk, fileptr->parent_ptr,\n\t\t\t\tfileptr, fileptr->dir_byte);\n\t\t} else {\n\t\t\tdynapro_handle_write_file(dsk, fileptr);\n\t\t}\n\t}\n\n\treturn 1;\n}\n\n\nvoid\ndynapro_debug_update(Disk *dsk)\n{\n\tdyna_printf(\"Writing out DYNAPRO_IMAGE, %p\\n\", dsk);\n#if 0\n\t// This causes the file DYNAPRO_IMAGE to be written out as the raw\n\t//  image after any write to the Dynapro volume.  This is for manual\n\t//  debugging.\n\tdynapro_write_to_unix_file(\"DYNAPRO_IMAGE\", dsk->raw_data,\n\t\t\t\t\t\t(word32)dsk->dimage_size);\n#endif\n}\n\nvoid\ndynapro_debug_map(Disk *dsk, const char *str)\n{\n\tDynapro_map *map_ptr;\n\tDynapro_file *lastfileptr, *fileptr;\n\tconst char *newstr;\n\tint\tnum_blocks;\n\tint\ti;\n\n\treturn;\t\t\t// HACK!\n\n\tnum_blocks = (word32)((dsk->dimage_size + 511) >> 9);\n\tmap_ptr = dsk->dynapro_info_ptr->block_map_ptr;\n\tlastfileptr = 0;\n\tprintf(\" Showing map for %s, %05x blocks, %s\\n\",\n\t\t\tdsk->dynapro_info_ptr->root_path, num_blocks, str);\n\tfor(i = 0; i < num_blocks; i++) {\n\t\tfileptr = map_ptr[i].file_ptr;\n\t\tif(fileptr != lastfileptr) {\n\t\t\tnewstr = \"\";\n\t\t\tif(fileptr) {\n\t\t\t\tnewstr = fileptr->unix_path;\n\t\t\t}\n\t\t\tprintf(\"  %04x (%07x): %p %s\\n\", i, i << 9, fileptr,\n\t\t\t\t\t\t\t\t\tnewstr);\n\t\t}\n\t\tlastfileptr = fileptr;\n\t}\n\tprintf(\"Recursive file map:\\n\");\n\tdynapro_debug_recursive_file_map(dsk->dynapro_info_ptr->volume_ptr, 1);\n}\n\nvoid\ndynapro_debug_recursive_file_map(Dynapro_file *fileptr, int start)\n{\n\tif(!fileptr) {\n\t\treturn;\n\t}\n\twhile(fileptr) {\n\t\tprintf(\"  file %p %s map_first_block:%05x, storage:%02x key:\"\n\t\t\t\"%04x\\n\", fileptr, fileptr->unix_path,\n\t\t\tfileptr->map_first_block, fileptr->prodos_name[0],\n\t\t\tfileptr->key_block);\n\t\tprintf(\"      n:%p, sub:%p, eof:%06x, parent:%p dam:%d\\n\",\n\t\t\tfileptr->next_ptr, fileptr->subdir_ptr,\n\t\t\tfileptr->eof, fileptr->parent_ptr, fileptr->damaged);\n\t\tif(fileptr->unix_path == 0) {\n\t\t\tprintf(\"Filename is invalid, exiting\\n\");\n\t\t\texit(1);\n\t\t}\n\t\tif(!fileptr->parent_ptr && !start) {\n\t\t\tprintf(\"parent_ptr is 0, exiting\\n\");\n\t\t\texit(1);\n\t\t}\n\t\tdynapro_debug_recursive_file_map(fileptr->subdir_ptr, 0);\n\t\tstart = 0;\n\t\tfileptr = fileptr->next_ptr;\n\t}\n}\n\nword32\ndynapro_unix_to_prodos_time(const time_t *time_ptr)\n{\n\tstruct tm *tm_ptr;\n\tword32\tymd, hours_mins, date_time;\n\tint\tyear;\n\n\ttm_ptr = localtime(time_ptr);\n\thours_mins = (tm_ptr->tm_hour << 8) | tm_ptr->tm_min;\n\tyear = tm_ptr->tm_year;\t\t// years since 1900\n\tif(year < 80) {\n\t\tyear = 80;\n\t} else if(year >= 100) {\n\t\tyear -= 100;\n\t\tif(year >= 80) {\n\t\t\tyear = 79;\n\t\t}\n\t}\n\tymd = (year << 9) | ((tm_ptr->tm_mon + 1) << 5) | (tm_ptr->tm_mday);\n\tdate_time = (ymd & 0xffff) | (hours_mins << 16);\n\t// printf(\"Unix time:%s results in:%08x\\n\", asctime(tm_ptr), date_time);\n\n\treturn date_time;\n}\n\nint\ndynapro_create_prodos_name(Dynapro_file *newfileptr, Dynapro_file *matchptr,\n\t\tword32 storage_type)\n{\n\tDynapro_file *thisptr;\n\tchar\t*str;\n\tword32\tupper_lower;\n\tint\tlen, outpos, inpos, max_inpos, c, done, dot_pos, inc_pos;\n\tint\ti;\n\n#if 0\n\tprintf(\"dynapro_create_prodos_name to %s, match:%p, st:%03x\\n\",\n\t\tnewfileptr->unix_path, matchptr, storage_type);\n#endif\n\n\tfor(i = 0; i < 17; i++) {\n\t\tnewfileptr->prodos_name[i] = 0;\n\t}\n\tstr = newfileptr->unix_path;\n\tif(!str) {\n\t\treturn 0;\n\t}\n\tinpos = (int)strlen(str);\n\tmax_inpos = inpos + 1;\n\twhile(inpos >= 1) {\n\t\tinpos--;\n\t\tif(str[inpos] == '/') {\n\t\t\tinpos++;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif(storage_type == 0x50) {\n\t\t// printf(\"max_inpos:%d, inpos:%d\\n\", max_inpos, inpos);\n\t}\n\tif((storage_type == 0x50) && ((max_inpos - inpos) > 12)) {\n\t\t// Remove .applesingle extension\n\t\tmax_inpos -= 13;\n\t}\n\t//printf(\" inpos:%d max_inpos:%d str:%s\\n\", inpos, max_inpos,\n\t//\t\t\t\t\t\t&(str[inpos]));\n\toutpos = 0;\n\twhile(outpos < (DYNAPRO_PATH_MAX - 1)) {\n\t\tc = 0;\n\t\tif(inpos < max_inpos) {\n\t\t\tc = str[inpos++];\n\t\t}\n\t\tg_dynapro_path_buf[outpos] = c;\n\t\tif(c == 0) {\n\t\t\tbreak;\n\t\t}\n\t\toutpos++;\n\t\tif((c >= 'A') && (c <= 'Z')) {\n\t\t\tcontinue;\t\t// This is legal\n\t\t}\n\t\tif((c >= 'a') && (c <= 'z')) {\n\t\t\tcontinue;\t\t// Also legal\n\t\t}\n\t\tif((outpos > 1) && (c >= '0') && (c <= '9')) {\n\t\t\tcontinue;\t\t// Also legal\n\t\t}\n\t\tif((outpos > 1) && (c == '.')) {\n\t\t\tcontinue;\t\t// Also legal\n\t\t}\n\t\t// If this is the first character, make it \"A\" and continue\n\t\tif(outpos == 1) {\n\t\t\tg_dynapro_path_buf[0] = 'A';\n\t\t\tcontinue;\n\t\t}\n\t\tif((c == ',') || (c == '#')) {\t\t// ,ttxt,a$2000, ignore\n\t\t\toutpos--;\n\t\t\tbreak;\t\t\t// All done\n\t\t}\n\n\t\t// This is not legal.  Make it a '.'\n\t\tif((c >= 0x20) && (c <= 0x7e)) {\n\t\t\tg_dynapro_path_buf[outpos - 1] = '@';\t// do '.' later\n\t\t} else {\n\t\t\toutpos--;\t\t// Ignore it\n\t\t}\n\t}\n\n\tg_dynapro_path_buf[outpos] = 0;\n\t// printf(\" initial path_buf:%s, %d\\n\", &g_dynapro_path_buf[0], outpos);\n\twhile((outpos >= 0) && (g_dynapro_path_buf[outpos-1] == '@')) {\n\t\t// Remove trailing '@' since they are not useful\n\t\toutpos--;\n\t\tg_dynapro_path_buf[outpos] = 0;\n\t}\n\tfor(i = 1; i < outpos; i++) {\n\t\t// Convert '@' to '.' to make name legal\n\t\tif(g_dynapro_path_buf[i] == '@') {\n\t\t\tg_dynapro_path_buf[i] = '.';\n\t\t}\n\t}\n\tif(outpos == 0) {\n\t\t// Not a valid file, just skip it\n\t\treturn 0;\n\t}\n\t// Now, it's valid.  Squeeze it to 15 character but saving extension\n\tlen = (int)strlen(&g_dynapro_path_buf[0]);\n\tif(len > 15) {\n\t\t// Copy last 8 characters to be in positions 7..14\n\t\tfor(i = 7; i < 16; i++) {\n\t\t\tg_dynapro_path_buf[i] = g_dynapro_path_buf[len-15 + i];\n\t\t}\n\t}\n\n\tlen = (int)strlen(&g_dynapro_path_buf[0]);\n\tif((len > 15) || (len == 0)) {\n\t\tprintf(\"Bad filename handling: %s\\n\", &g_dynapro_path_buf[0]);\n\t\treturn 0;\n\t}\n\n\t// See if it conflicts with matchptr\n\tthisptr = matchptr;\n\tfor(i = 0; i < 10000; i++) {\n\t\tif(!thisptr || (thisptr == newfileptr)) {\n\t\t\tthisptr = 0;\n\t\t\tbreak;\n\t\t}\n\t\t//printf(\"Comparing %s to %s\\n\", &g_dynapro_path_buf[0],\n\t\t//\t\t\t(char *)&(thisptr->prodos_name[1]));\n\t\tlen = (int)strlen(&g_dynapro_path_buf[0]);\n\t\tif((len == (thisptr->prodos_name[0] & 0xf)) &&\n\t\t\t\t(cfgcasecmp(&g_dynapro_path_buf[0],\n\t\t\t\t(char *)&(thisptr->prodos_name[1])) == 0)) {\n\t\t\tdyna_printf(\" that was a match\\n\");\n\t\t\tdot_pos = 0;\n\t\t\tinc_pos = len - 1;\n\t\t\tfor(i = len - 2; i >= 1; i--) {\n\t\t\t\tif(g_dynapro_path_buf[i] == '.') {\n\t\t\t\t\tdot_pos = i;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(len < 15) {\n\t\t\t\t// Append \"1\" to the end\n\t\t\t\tlen++;\n\t\t\t\tinc_pos = len - 1;\n\t\t\t\tg_dynapro_path_buf[len] = 0;\n\t\t\t\tg_dynapro_path_buf[len - 1] = '1';\n\t\t\t\tif(dot_pos > 1) {\n\t\t\t\t\tfor(i = len - 1; i >= dot_pos; i--) {\n\t\t\t\t\t\tg_dynapro_path_buf[i] =\n\t\t\t\t\t\t\tg_dynapro_path_buf[i-1];\n\t\t\t\t\t}\n\t\t\t\t\tinc_pos = dot_pos;\n\t\t\t\t}\n\t\t\t\tg_dynapro_path_buf[inc_pos] = '1';\n\t\t\t} else if(dot_pos > 3) {\n\t\t\t\tinc_pos = dot_pos - 1;\n\t\t\t}\n\t\t\tdone = 0;\n\t\t\tfor(i = inc_pos; i >= 1; i--) {\n\t\t\t\tc = g_dynapro_path_buf[i];\n\t\t\t\tc++;\n\t\t\t\tif(c == ('9' + 1)) {\n\t\t\t\t\tc = '0';\n\t\t\t\t} else if(c == ('z' + 1)) {\n\t\t\t\t\tc = 'a';\n\t\t\t\t} else if(c == ('Z' + 1)) {\n\t\t\t\t\tc = 'A';\n\t\t\t\t} else {\n\t\t\t\t\tdone = 1;\n\t\t\t\t}\n\t\t\t\tg_dynapro_path_buf[i] = c;\n\t\t\t\tif(done) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthisptr = matchptr;\n\t\t} else {\n\t\t\tthisptr = thisptr->next_ptr;\n\t\t}\n\t}\n\tif(thisptr) {\n\t\t// File could not be made unique\n\t\tprintf(\"Could not make a unique ProDOS filename: %s\\n\",\n\t\t\t\t\t\tnewfileptr->unix_path);\n\t\treturn 0;\n\t}\n\n\tupper_lower = 0;\n\tfor(i = 0; i < len; i++) {\n\t\tc = g_dynapro_path_buf[i];\n\t\tif((c >= 'a') && (c <= 'z')) {\n\t\t\tc = c - 'a' + 'A';\n\t\t\tupper_lower |= 0x8000 | (0x4000 >> i);\n\t\t}\n\t\tnewfileptr->prodos_name[1 + i] = c;\n\t}\n\tnewfileptr->prodos_name[0] = len | storage_type;\n\tnewfileptr->upper_lower = upper_lower;\n\n\treturn len;\n}\n\nDynapro_file *\ndynapro_new_unix_file(const char *path, Dynapro_file *parent_ptr,\n\t\t\t\tDynapro_file *match_ptr, word32 storage_type)\n{\n\tDynapro_file *fileptr;\n\tint\tlen;\n\tint\ti;\n\n#if 0\n\tprintf(\"dynapro_new_unix_file for %s, parent:%p, m:%p, st:%03x\\n\",\n\t\tpath, parent_ptr, match_ptr, storage_type);\n#endif\n\tfileptr = dynapro_alloc_file();\n\tif(!fileptr) {\n\t\treturn 0;\n\t}\n\n\tfileptr->next_ptr = 0;\n\tfileptr->parent_ptr = parent_ptr;\n\tfileptr->subdir_ptr = 0;\n\tfileptr->buffer_ptr = 0;\n\tfileptr->unix_path = kegs_malloc_str(path);\n\tfor(i = 0; i < 17; i++) {\n\t\tfileptr->prodos_name[i] = 0;\n\t}\n\tfileptr->dir_byte = 0;\n\tfileptr->eof = 0;\n\tfileptr->blocks_used = 0;\n\tfileptr->creation_time = 0;\n\tfileptr->lastmod_time = 0;\n\tfileptr->upper_lower = 0;\n\tfileptr->key_block = 0;\n\tfileptr->aux_type = 0;\n\tfileptr->header_pointer = 0;\n\tfileptr->map_first_block = 0;\n\tfileptr->file_type = 0x0f;\t\t\t// Default to \"DIR\"\n\tfileptr->modified_flag = 0;\n\tfileptr->damaged = 0;\n\n\tlen = (int)strlen(fileptr->unix_path);\n\tfor(i = len - 1; i >= 0; i--) {\n\t\tif(fileptr->unix_path[i] == '/') {\n\t\t\tfileptr->unix_path[i] = 0;\t// Strip trailing /\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(storage_type < 0xd0) {\n\t\tstorage_type = dynatype_detect_file_type(fileptr,\n\t\t\t\t\tfileptr->unix_path, storage_type);\n\t}\n\n\tlen = dynapro_create_prodos_name(fileptr, match_ptr, storage_type);\n\tif(len == 0) {\n\t\tprintf(\"Could not create prodos name for: %s\\n\", path);\n\t\tfree(fileptr);\n\t\treturn 0;\n\t}\n\n#if 0\n\tprintf(\"dynapro_create_new_unix_file: %s prodos:%s, st:%02x, ft:%02x, \"\n\t\t\"aux:%04x\\n\", fileptr->unix_path, &(fileptr->prodos_name[1]),\n\t\tfileptr->prodos_name[0], fileptr->file_type, fileptr->aux_type);\n#endif\n\treturn fileptr;\n}\n\nint\ndynapro_create_dir(Disk *dsk, char *unix_path, Dynapro_file *parent_ptr,\n\t\t\t\tword32 dir_byte)\n{\n\tstruct stat stat_buf;\n\tstruct dirent *direntptr;\n\tDIR\t*opendirptr;\n\tDynapro_file *fileptr, *head_ptr, *prev_ptr;\n\tmode_t\tfmt;\n\tword32\tstorage_type, val;\n\tint\tret;\n\n\t// Create a directory entry at dir_byte first\n#if 0\n\tprintf(\"\\n\");\n\tprintf(\"dynapro_add_files to %s, %p dir_byte:%08x\\n\", unix_path,\n\t\t\t\t\t\t\tparent_ptr, dir_byte);\n#endif\n\n\tstorage_type = 0xe0;\t\t// Directory header\n\tif(dir_byte < 0x600) {\t\t// Block 2: volume header\n\t\tstorage_type = 0xf0;\n\t}\n\thead_ptr = dynapro_new_unix_file(unix_path, parent_ptr, 0,\n\t\t\t\t\t\t\t\tstorage_type);\n\tif(parent_ptr) {\n\t\tparent_ptr->subdir_ptr = head_ptr;\n#if 0\n\t\tprintf(\"set parent %s subdir_ptr=%p\\n\", parent_ptr->unix_path,\n\t\t\t\t\t\thead_ptr);\n#endif\n\t}\n\tif(dsk->dynapro_info_ptr->volume_ptr == 0) {\n\t\tdsk->dynapro_info_ptr->volume_ptr = head_ptr;\n\t}\n\tif(head_ptr == 0) {\n\t\tprintf(\"new_file returned 0, skipping %s\\n\", unix_path);\n\t\treturn dir_byte;\n\t}\n\thead_ptr->key_block = dir_byte >> 9;\n\thead_ptr->aux_type = 0x0d27;\t\t\t// 0x27,0x0d\n\thead_ptr->file_type = 0x75;\t\t\t// Directory header type\n\tif(storage_type >= 0xf0) {\n\t\thead_ptr->file_type = 0x00;\n\t}\n\tret = cfg_stat(unix_path, &stat_buf, 0);\n\tif(ret != 0) {\n\t\tprintf(\"stat %s ret %d, errno:%d\\n\", unix_path, ret, errno);\n\t\treturn 0;\n\t}\n\thead_ptr->creation_time = dynapro_unix_to_prodos_time(\n\t\t\t\t\t\t\t&stat_buf.st_ctime);\n\n\tdir_byte = dynapro_add_file_entry(dsk, head_ptr, 0, dir_byte, 0);\n\n\topendirptr = opendir(unix_path);\n\tif(opendirptr == 0) {\n\t\tprintf(\"Could not open %s as a dir\\n\", unix_path);\n\t\treturn 0;\n\t}\n\tprev_ptr = head_ptr;\n\twhile(1) {\n\t\tdirentptr = readdir(opendirptr);\n\t\tif(direntptr == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tif(direntptr->d_name[0] == '.') {\n\t\t\tcontinue;\t\t\t// Ignore all '.' files\n\t\t}\n\t\tdynapro_join_path_and_file(&(g_dynapro_path_buf[0]), unix_path,\n\t\t\t\t\tdirentptr->d_name, DYNAPRO_PATH_MAX);\n\t\tret = cfg_stat(&(g_dynapro_path_buf[0]), &stat_buf, 0);\n\t\tif(ret != 0) {\n\t\t\tprintf(\"stat %s ret %d, errno:%d\\n\",\n\t\t\t\t\t&g_dynapro_path_buf[0], ret, errno);\n\t\t\tcontinue;\t// skip it\n\t\t}\n\n\t\tfmt = stat_buf.st_mode & S_IFMT;\n\t\tstorage_type = 0;\n\t\tif(fmt == S_IFDIR) {\n\t\t\t// Ignore symlinks to directories (since they may point\n\t\t\t//  outside the base directory, and so dynamically\n\t\t\t//  removing files could be a security issue).\n\t\t\tret = cfg_stat(&g_dynapro_path_buf[0], &stat_buf, 1);\n\t\t\tif(ret != 0) {\n\t\t\t\tprintf(\"lstat %s ret %d, errno:%d\\n\",\n\t\t\t\t\t&g_dynapro_path_buf[0], ret, errno);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tstorage_type = 0xd0;\t\t// Directory\n\t\t} else if(fmt != S_IFREG) {\n\t\t\tcontinue;\t\t// Skip this\n\t\t}\n#if 0\n\t\tprintf(\"GOT file: %s, is_dir:%d (%s), parent:%p\\n\",\n\t\t\t&(g_dynapro_path_buf[0]), is_dir, direntptr->d_name,\n\t\t\tparent_ptr);\n#endif\n\t\tfileptr = dynapro_new_unix_file(&(g_dynapro_path_buf[0]),\n\t\t\thead_ptr, head_ptr->next_ptr, storage_type);\n\t\tif(fileptr == 0) {\n\t\t\tclosedir(opendirptr);\n\t\t\treturn 0;\n\t\t}\n\t\tprev_ptr->next_ptr = fileptr;\n\t\tfileptr->key_block = dynapro_find_free_block(dsk);\n\t\tfileptr->creation_time = dynapro_unix_to_prodos_time(\n\t\t\t\t\t\t\t&stat_buf.st_ctime);\n\t\tfileptr->lastmod_time = dynapro_unix_to_prodos_time(\n\t\t\t\t\t\t\t&stat_buf.st_mtime);\n\t\tif(fileptr->key_block == 0) {\n\t\t\tprintf(\"Allocating directory block failed\\n\");\n\t\t\tclosedir(opendirptr);\n\t\t\treturn 0;\n\t\t}\n\t\tfileptr->blocks_used = 1;\n\t\tfileptr->eof = 1*0x200;\n\t\tdir_byte = dynapro_add_file_entry(dsk, fileptr, head_ptr,\n\t\t\t\t\t\t\tdir_byte, 0x27);\n\t\tif(dir_byte == 0) {\n\t\t\tclosedir(opendirptr);\n\t\t\treturn 0;\n\t\t}\n\t\tif(fmt == S_IFDIR) {\n\t\t\tval = dynapro_create_dir(dsk, fileptr->unix_path,\n\t\t\t\tfileptr, (fileptr->key_block << 9) + 4);\n\t\t\tif(val == 0) {\n\t\t\t\tclosedir(opendirptr);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t} else {\n\t\t\tval = dynapro_file_from_unix(dsk, fileptr);\n\t\t\tif(val == 0) {\n\t\t\t\tclosedir(opendirptr);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t\tprev_ptr = fileptr;\n\t}\n\n\tclosedir(opendirptr);\n\treturn dir_byte;\n}\n\nword32\ndynapro_add_file_entry(Disk *dsk, Dynapro_file *fileptr, Dynapro_file *head_ptr,\n\tword32 dir_byte, word32 inc)\n{\n\tDynapro_file *parent_ptr;\n\tbyte\t*bptr, *pkeyptr;\n\tword32\tstorage_type, val, ent, new_dir_blk, new_dir_byte;\n\tword32\theader_pointer;\n\tint\ti;\n\n#if 0\n\tprintf(\"dynapro_add_file_entry: %p %p %s head:%p dir_byte:%08x \"\n\t\t\"inc:%03x\\n\", dsk, fileptr, fileptr->unix_path, head_ptr,\n\t\tdir_byte, inc);\n#endif\n\tbptr = dsk->raw_data;\n\tif(((dir_byte & 0x1ff) + inc + inc) >= 0x200) {\n\t\t// This entry will not fit in this directory block.\n\t\t//  Try to step to next block, otherwise allocate a new one\n\t\tnew_dir_byte = dir_byte & -0x200L;\n\t\tnew_dir_blk = dynapro_get_word16(&bptr[new_dir_byte + 2]);\n\t\tdyna_printf(\" Entry does not fit, new_dir_blk:%04x\\n\",\n\t\t\t\t\t\t\tnew_dir_blk);\n\t\tif(new_dir_blk != 0) {\n\t\t\t// Follow to the next block\n\t\t\tdir_byte = (new_dir_blk * 0x200) + 4;\n\t\t} else if(dir_byte < (6 * 0x200)) {\n\t\t\t// Otherwise, allocate a new block (not for volume dir)\n\t\t\t// This is a volume header, always 4 blocks, don't\n\t\t\t//  allocate any more, this is now full\n\t\t\tprintf(\"Too many file in volume directory\\n\");\n\t\t\treturn 0;\t\t// Out of space\n\t\t} else {\n\t\t\tnew_dir_blk = dynapro_find_free_block(dsk);\n\t\t\tif(new_dir_blk == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tnew_dir_byte = new_dir_blk * 512;\n\t\t\tdynapro_set_word16(&bptr[new_dir_byte], dir_byte >> 9);\n\t\t\tdynapro_set_word16(&bptr[new_dir_byte + 2], 0);\n\t\t\tdir_byte = (dir_byte >> 9) << 9;\n\t\t\tdynapro_set_word16(&bptr[dir_byte + 2], new_dir_blk);\n\t\t\tdir_byte = new_dir_byte + 4;\n\t\t\tif(!head_ptr) {\n\t\t\t\tdyna_printf(\"No head:%s\\n\", fileptr->unix_path);\n\t\t\t}\n\t\t\tparent_ptr = head_ptr->parent_ptr;\n\t\t\tif(!parent_ptr) {\n\t\t\t\tprintf(\"No parent: %s\\n\", fileptr->unix_path);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tparent_ptr->blocks_used++;\n\t\t\tparent_ptr->eof += 0x200;\n\t\t\tnew_dir_byte = parent_ptr->dir_byte;\n\t\t\tif(new_dir_byte == 0) {\n\t\t\t\tprintf(\"Invalid dir_byte for %s\\n\",\n\t\t\t\t\t\tparent_ptr->unix_path);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tdynapro_set_word16(&bptr[new_dir_byte + 0x13],\n\t\t\t\t\t\tparent_ptr->blocks_used);\n\t\t\tdynapro_set_word24(&bptr[new_dir_byte + 0x15],\n\t\t\t\t\t\tparent_ptr->eof);\n\t\t}\n\t} else {\n\t\tdir_byte += inc;\n\t}\n\tbptr = &(dsk->raw_data[dir_byte]);\n\n\tfileptr->dir_byte = dir_byte;\n\tfor(i = 0; i < 0x27; i++) {\n\t\tbptr[i] = 0;\n\t}\n\tfor(i = 0; i < 16; i++) {\n\t\tbptr[i] = fileptr->prodos_name[i];\t// [0] = len,storage_t\n\t}\n\tbptr[0x10] = fileptr->file_type;\n\tdynapro_set_word16(&bptr[0x11], fileptr->key_block);\n\tdynapro_set_word16(&bptr[0x13], fileptr->blocks_used);\n\tdynapro_set_word24(&bptr[0x15], fileptr->eof);\n\tdynapro_set_word32(&bptr[0x18], fileptr->creation_time);\n\t\t\t\t\t\t\t// creation date&time\n\tbptr[0x1c] = fileptr->upper_lower & 0xff;\t// Version\n\tbptr[0x1d] = fileptr->upper_lower >> 8;\t\t// Min_Version\n\tbptr[0x1e] = 0xe3;\t\t\t\t// Access\n\tdynapro_set_word16(&bptr[0x1f], fileptr->aux_type);\n\tstorage_type = bptr[0];\n\tif(storage_type >= 0xf0) {\t\t// Volume header\n\t\tdynapro_set_word16(&bptr[0x11], 0);\n\t\tfileptr->lastmod_time = 0x00060000;\n\t\t\t// low 16 bits: file_count, upper 16 bits: bitmap_block\n\t\tfileptr->header_pointer = (word32)(dsk->raw_dsize >> 9);\n\t\t\t\t\t\t\t// Total blocks\n\t\tdynapro_set_word16(&bptr[0x1c], 0x0005);\n\t\tdynapro_set_word16(&bptr[0x16], fileptr->upper_lower);\n\t} else if(storage_type >= 0xe0) {\t\t// Directory header\n\t\tdynapro_set_word16(&bptr[0x11], 0);\n\t\tdynapro_set_word16(&bptr[0x1c], 0x0005);\n\t\tparent_ptr = fileptr->parent_ptr;\t\t// subdir entry\n\t\tif(parent_ptr == 0) {\n\t\t\tprintf(\"parent_ptr of %s is 0\\n\", fileptr->unix_path);\n\t\t\treturn 0;\n\t\t}\n\t\tval = parent_ptr->dir_byte >> 9;\n\t\tfileptr->lastmod_time = (val << 16);\t\t// Parent block\n\t\tval = parent_ptr->dir_byte & 0x1ff;\n\t\tent = (val - 4) / 0x27;\n\t\tfileptr->header_pointer = 0x2700 | (ent + 1);\n\t} else {\n\t\t// Directory entry, or normal file\n\t\tif(head_ptr == 0) {\n\t\t\tprintf(\"head_ptr of %s is 0\\n\", fileptr->unix_path);\n\t\t\treturn 0;\n\t\t}\n\t\theader_pointer = head_ptr->key_block;\n\t\tfileptr->header_pointer = header_pointer;\n\t\tdynapro_set_word16(&bptr[0x25], header_pointer);\n\t\tpkeyptr = &(dsk->raw_data[header_pointer << 9]);\n\t\tval = head_ptr->lastmod_time + 1;\n\t\thead_ptr->lastmod_time = val;\n\t\tdynapro_set_word16(&pkeyptr[4 + 0x21], val);\t// File count\n\t}\n\tdynapro_set_word32(&bptr[0x21], fileptr->lastmod_time);\n\t\t\t\t// Last Modified date&time (or header info)\n\tdynapro_set_word16(&bptr[0x25], fileptr->header_pointer);\n#if 0\n\tprintf(\"Set dir_byte %07x=%04x\\n\", dir_byte + 0x25,\n\t\t\t\t\t\tfileptr->header_pointer);\n#endif\n\n\treturn dir_byte;\n}\n\n// When creating sparse files, always ensure first block is not sparse.  GS/OS\n//  does not treat the first block as sparse, it will actually read block 0\n// This handles normal files, and one fork of a forked file\nword32\ndynapro_fork_from_unix(Disk *dsk, byte *fptr, word32 *storage_type_ptr,\n\t\t\t\t\tword32 key_block, dword64 dsize)\n{\n\tbyte\t*bptr;\n\tword32\tsap_block, tree_block, sap_byte, tree_byte, sparse, block_num;\n\tword32\tnum_blocks, blocks_used, block_off, storage_type;\n\tint\tnum_bytes;\n\tint\ti;\n\n\tbptr = &(dsk->raw_data[0]);\n\t*storage_type_ptr = 0;\n\tsap_block = 0;\n\ttree_block = 0;\n\tnum_blocks = (word32)((dsize + 511) >> 9);\n\tif(num_blocks == 0) {\t\t\t// 0-length file\n\t\tnum_blocks = 1;\n\t} else if(num_blocks > 0x8000) {\t// >= 16MB (32K*512)\n\t\tprintf(\"File is too large, failing\\n\");\n\t\treturn 0;\n\t}\n\tblock_off = 0;\n\tblocks_used = 1;\n\twhile(block_off < num_blocks) {\n\t\tsparse = (block_off > 0);\t// sparse=0 for first block\n\t\tfor(i = 0; i < 0x200; i++) {\n\t\t\tif(fptr[(block_off << 9) + i] != 0) {\n\t\t\t\tsparse = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(sparse) {\n\t\t\tblock_off++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif((tree_block == 0) && (num_blocks > 256)) {\n\t\t\ttree_block = dynapro_find_free_block(dsk);\n\t\t\tif(tree_block == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tblocks_used++;\n\t\t}\n\t\ttree_byte = (tree_block << 9) + ((block_off >> 8) & 0xff);\n\t\tif(tree_block) {\n\t\t\tsap_block = bptr[tree_byte + 0] |\n\t\t\t\t\t\t(bptr[tree_byte + 256] << 8);\n\t\t}\n\t\tif((sap_block == 0) && (num_blocks > 1)) {\n\t\t\tsap_block = dynapro_find_free_block(dsk);\n\t\t\tif(sap_block == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tblocks_used++;\n\t\t\tif(tree_block) {\n\t\t\t\tbptr[tree_byte + 0] = sap_block;\n\t\t\t\tbptr[tree_byte + 256] = sap_block >> 8;\n\t\t\t}\n\t\t}\n\t\tif(block_off == 0) {\n\t\t\tblock_num = key_block;\n\t\t} else {\n\t\t\tblock_num = dynapro_find_free_block(dsk);\n\t\t\tif(block_num == 0) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tblocks_used++;\n\t\t}\n\t\tsap_byte = (sap_block << 9) | (block_off & 0xff);\n\t\tif(sap_block) {\n\t\t\tbptr[sap_byte + 0] = block_num;\n\t\t\tbptr[sap_byte + 256] = block_num >> 8;\n\t\t}\n\n\t\tnum_bytes = 0x200;\n\t\tif(block_off == (dsize >> 9)) {\t\t\t// Last block\n\t\t\tnum_bytes = dsize & 0x1ff;\n\t\t}\n\t\tfor(i = 0; i < num_bytes; i++) {\n\t\t\tbptr[(block_num << 9) + i] = fptr[(block_off << 9) + i];\n\t\t}\n\n\t\tblock_off++;\n\t}\n\n\tstorage_type = 0x10;\n\tif(tree_block) {\n\t\tstorage_type = 0x30;\n\t\tkey_block = tree_block;\n\t} else if(sap_block) {\n\t\tstorage_type = 0x20;\n\t\tkey_block = sap_block;\n\t}\n\t*storage_type_ptr = storage_type;\n\treturn (blocks_used << 16) | key_block;\n}\n\nword32\ndynapro_file_from_unix(Disk *dsk, Dynapro_file *fileptr)\n{\n\tbyte\t*bptr, *fptr;\n\tdword64\tdsize;\n\tword32\tstorage_type, blocks_out, dir_byte;\n\n\tfptr = dynapro_malloc_file(fileptr->unix_path, &dsize, 0x200);\n\tfileptr->eof = (word32)dsize;\n#if 0\n\tprintf(\"file_from_unix %s, size:%08llx, file_type:%02x, dir_byte:\"\n\t\t\"%07x, storage:%02x\\n\", fileptr->unix_path, dsize,\n\t\tfileptr->file_type, fileptr->dir_byte, fileptr->prodos_name[0]);\n#endif\n\tstorage_type = 0;\n\tif((fileptr->prodos_name[0] & 0xf0) == 0x50) {\n\t\t// .applesingle file with data and/or resource forks\n\t\tblocks_out = applesingle_from_unix(dsk, fileptr, fptr, dsize);\n\t} else {\n\t\t// Normal file\n\t\tfileptr->prodos_name[0] = (fileptr->prodos_name[0] & 0xf);\n\n\t\tblocks_out = dynapro_fork_from_unix(dsk, fptr, &storage_type,\n\t\t\t\t\tfileptr->key_block, dsize);\n\t}\n\tfree(fptr);\n\tfileptr->prodos_name[0] |= storage_type;\n\tfileptr->key_block = blocks_out & 0xffff;\n\tfileptr->blocks_used = (blocks_out >> 16) & 0xffff;\n\n\t// Update dir_byte information for this file\n\tdir_byte = fileptr->dir_byte;\n\tif(dir_byte == 0) {\n\t\tdyna_printf(\"dir_byte is 0 for %s\\n\", fileptr->unix_path);\n\t}\n\tbptr = &(dsk->raw_data[dir_byte]);\n\tbptr[0] = fileptr->prodos_name[0];\n\tbptr[0x10] = fileptr->file_type;\n\tdynapro_set_word16(&bptr[0x11], fileptr->key_block);\n\tdynapro_set_word16(&bptr[0x13], fileptr->blocks_used);\n\tdynapro_set_word24(&bptr[0x15], fileptr->eof);\n\tdynapro_set_word16(&bptr[0x1f], fileptr->aux_type);\n#if 0\n\tprintf(\"Set %s dir_byte:%07x+0x10=%02x (file_type)\\n\",\n\t\tfileptr->unix_path, dir_byte, fileptr->file_type);\n#endif\n\n\treturn blocks_out;\n}\n\nword32\ndynapro_prep_image(Disk *dsk, const char *dir_path, word32 num_blocks)\n{\n\tDynapro_info *infoptr;\n\tbyte\t*bptr;\n\tword32\tbitmap_size_bytes, bitmap_size_blocks;\n\tint\tpos;\n\tword32\tui;\n\tint\ti;\n\n\tdsk->raw_data = calloc(num_blocks, 512);\n\tif(dsk->raw_data == 0) {\n\t\tdynapro_error(dsk, \"Could not allocate %d bytes\\n\",\n\t\t\t\t\t\t\tnum_blocks * 512);\n\t\treturn 0;\n\t}\n\tdsk->dimage_size = num_blocks * 512LL;\n\tdsk->dimage_start = 0;\n\tdsk->raw_dsize = num_blocks * 512LL;\n\n\tbptr = &(dsk->raw_data[0]);\n\tfor(i = 0; i < 512; i++) {\n\t\tbptr[i] = g_prodos_block0[i];\n\t}\n\n\t// Directory is from blocks 2 through 5.  Set up prev and next ptrs\n\tbptr = &(dsk->raw_data[2 * 0x200]);\n\tfor(i = 0; i < 3; i++) {\t\t// Blocks 2,3,4 (or 3,4,5)\n\t\tdynapro_set_word16(&bptr[(i + 1)*0x200], i + 2); // Prev_blk\n\t\tdynapro_set_word16(&bptr[i*0x200 + 2], i + 3);\t// Next_blk\n\t}\n\n\t// Calculate bitmap to go in blocks 6...\n\tbitmap_size_bytes = (num_blocks + 7) >> 3;\n\tbitmap_size_blocks = (bitmap_size_bytes + 512 - 1) >> 9;\n\tbptr = &(dsk->raw_data[6 * 512]);\t\t// Block 6\n\tbptr[0] = 0;\n\tfor(ui = (6 + bitmap_size_blocks); ui < num_blocks; ui++) {\n\t\tpos = (ui >> 3);\n\t\tbptr[pos] |= (0x80U >> (ui & 7));\n\t}\n\n\tinfoptr = calloc(sizeof(Dynapro_info), 1);\n\tif(!infoptr) {\n\t\treturn 0;\n\t}\n\tinfoptr->root_path = kegs_malloc_str(dir_path);\n\tinfoptr->volume_ptr = 0;\n\tinfoptr->block_map_ptr = calloc(num_blocks * sizeof(Dynapro_map), 1);\n\tinfoptr->damaged = 0;\n\tif((infoptr->root_path == 0) || (infoptr->block_map_ptr == 0)) {\n\t\tdynapro_error(dsk, \"Could not allocate memory!\\n\");\n\t\treturn 0;\n\t}\n\tdsk->dynapro_info_ptr = infoptr;\n\treturn 1;\n}\n\nword32\ndynapro_map_one_file_block(Disk *dsk, Dynapro_file *fileptr, word32 block_num,\n\t\t\tword32 file_offset, word32 eof)\n{\n\tDynapro_info *info_ptr;\n\tDynapro_map *map_ptr;\n\tbyte\t*buffer_ptr;\n\tword32\tsize, size_to_end;\n\n\tinfo_ptr = dsk->dynapro_info_ptr;\n\tif(!info_ptr || (block_num >= (dsk->dimage_size >> 9))) {\n\t\tprintf(\" mapping file %s, block %04x is invalid\\n\",\n\t\t\t\t\t\tfileptr->unix_path, block_num);\n\t\treturn 0;\n\t}\n\tif(info_ptr->block_map_ptr == 0) {\n\t\treturn 0;\n\t}\n\tif(block_num == 0) {\n\t\treturn 1;\n\t}\n\tmap_ptr = &(info_ptr->block_map_ptr[block_num]);\n\tif((map_ptr->file_ptr != 0) || (map_ptr->next_map_block != 0)) {\n\t\tdyna_printf(\"Mapping %s to block %04x, already has file_ptr:\"\n\t\t\t\"%p, next_map:%04x, mod:%d\\n\", fileptr->unix_path,\n\t\t\tblock_num, map_ptr->file_ptr, map_ptr->next_map_block,\n\t\t\tmap_ptr->modified);\n\t\tif(map_ptr->file_ptr) {\n\t\t\tdyna_printf(\" Existing file: %s\\n\",\n\t\t\t\t\t\tmap_ptr->file_ptr->unix_path);\n\t\t}\n\t\treturn 0;\n\t}\n\t//printf(\" map file %s block %05x off:%08x\\n\", fileptr->unix_path,\n\t//\t\t\t\t\tblock_num, file_offset);\n\n\tmap_ptr->next_map_block = fileptr->map_first_block;\n\tfileptr->map_first_block = block_num;\n\tmap_ptr->modified = 0;\n\tmap_ptr->file_ptr = fileptr;\n\n\tif(file_offset >= eof) {\n\t\treturn 1;\t\t// This block was an \"overhead\" block\n\t}\n\n\tbuffer_ptr = fileptr->buffer_ptr;\n\tif(buffer_ptr) {\n\t\t// Copy this block in at file_offset\n\t\tsize = 0x200;\n\t\tsize_to_end = eof - file_offset;\n\t\tif(size_to_end < size) {\n\t\t\tsize = size_to_end;\n\t\t}\n#if 0\n\t\tprintf(\"mofb: Write to %p + %07x from block %04x, size:%04x\\n\",\n\t\t\tbuffer_ptr, file_offset, block_num, size);\n#endif\n\t\tmemcpy(buffer_ptr + file_offset,\n\t\t\t\t&(dsk->raw_data[block_num * 0x200]), size);\n\t}\n\treturn 1;\n}\n\nword32\ndynapro_map_file_blocks(Disk *dsk, Dynapro_file *fileptr, word32 block_num,\n\tint level, word32 file_offset, word32 eof)\n{\n\tbyte\t*bptr;\n\tword32\tentry_inc, tmp, ret;\n\tint\ti;\n\n#if 0\n\tprintf(\"dynapro_map_file_blocks %s block_num %05x level:%d off:%08x\\n\",\n\t\t\tfileptr->unix_path, block_num, level, file_offset);\n#endif\n\tif(level == 0) {\n\t\treturn 0;\t\t// Bad value, should not happen\n\t}\n\tif(level == 1) {\n\t\treturn dynapro_map_one_file_block(dsk, fileptr, block_num,\n\t\t\t\t\t\t\tfile_offset, eof);\n\t}\n\tret = dynapro_map_one_file_block(dsk, fileptr, block_num, 1U << 30, 0);\n\tif(ret == 0) {\n\t\treturn ret;\n\t}\n\tentry_inc = 512;\n\tif(level == 3) {\t\t// Tree\n\t\tentry_inc = 256*512;\n\t}\n\tbptr = &(dsk->raw_data[block_num * 0x200]);\n\tfor(i = 0; i < 256; i++) {\n\t\ttmp = bptr[i] + (bptr[256 + i] << 8);\n\t\tif(tmp == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tret = dynapro_map_file_blocks(dsk, fileptr, tmp, level - 1,\n\t\t\t\t\t\tfile_offset + i*entry_inc, eof);\n\t\tif(ret == 0) {\n\t\t\treturn ret;\n\t\t}\n\t}\n\tdynapro_debug_map(dsk, \"post map_file_blocks\");\n\n\treturn 1;\n}\n\nword32\ndynapro_map_file(Disk *dsk, Dynapro_file *fileptr, int do_file_data)\n{\n\tword32\tblock_num, ret;\n\tint\tlevel;\n\n\tlevel = (fileptr->prodos_name[0] >> 4) & 0xf;\n\tblock_num = fileptr->key_block;\n\tif(level == 5) {\t\t\t// Forked file\n\t\treturn applesingle_map_from_prodos(dsk, fileptr,\n\t\t\t\t\t\t\tdo_file_data);\n\t} else if((level < 0) || (level >= 4)) {\n\t\tprintf(\"Storage_type: %02x for %s is bad\\n\", level,\n\t\t\t\t\t\t\tfileptr->unix_path);\n\t\treturn 0;\n\t}\n\n\tfileptr->buffer_ptr = 0;\n\tif(do_file_data) {\n\t\t// Create a place for data.  We will free before returning\n\t\tfileptr->buffer_ptr = calloc(1, fileptr->eof + 0x200);\n\t\tif(fileptr->buffer_ptr == 0) {\n\t\t\tprintf(\"malloc failed!\\n\");\n\t\t\treturn 0;\n\t\t}\n\t}\n\t// Must not return now before free'ing fileptr->buffer_ptr!\n\n\tret = dynapro_map_file_blocks(dsk, fileptr, block_num, level, 0,\n\t\t\t\t\t\t\t\tfileptr->eof);\n\n\t// printf(\" dynapro_map_file, map_file_blocks ret:%04x\\n\", ret);\n\tif((ret != 0) && (do_file_data)) {\n\t\t// Then, write buffer_ptr to the unix file\n\t\tret = dynapro_write_to_unix_file(fileptr->unix_path,\n\t\t\t\tfileptr->buffer_ptr, fileptr->eof);\n\t\t// printf(\" map_file, write_to_unix_file ret:%04x\\n\", ret);\n\t}\n\n\t// And free the buffer_ptr\n\tfree(fileptr->buffer_ptr);\n\tfileptr->buffer_ptr = 0;\n\n\treturn ret;\n}\n\nword32\ndynapro_map_dir_blocks(Disk *dsk, Dynapro_file *fileptr)\n{\n\tbyte\t*bptr;\n\tword32\tblock_num, ret;\n\tint\tcnt;\n\n\t// Loop over all directory blocks marking the map\n\tblock_num = fileptr->key_block;\n\tif(block_num == 0) {\n\t\tprintf(\"dynapro_map_dir_blocks, block_num is 0\\n\");\n\t\treturn 0;\n\t}\n\tbptr = &(dsk->raw_data[0]);\n\tcnt = 0;\n\tfileptr->map_first_block = 0;\n\twhile(block_num != 0) {\n\t\tret = dynapro_map_one_file_block(dsk, fileptr, block_num,\n\t\t\t\t\t\t\t1U << 30, 0);\n\t\tif(ret == 0) {\n\t\t\tprintf(\"dynapro_map_dir_on_block, ret 0, block:%04x\\n\",\n\t\t\t\t\t\t\t\tblock_num);\n\t\t\treturn 0;\n\t\t}\n\t\tblock_num = dynapro_get_word16(&bptr[(block_num * 0x200) + 2]);\n\t\tcnt++;\n\t\tif(cnt > 1000) {\n\t\t\tprintf(\"Directory had loop in it, error\\n\");\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tdynapro_debug_map(dsk, \"post map_dir_blocks\");\n\treturn 1;\n}\n\nword32\ndynapro_build_map(Disk *dsk, Dynapro_file *fileptr)\n{\n\tword32\tret;\n\n\tif(fileptr == 0) {\n\t\treturn 0;\n\t}\n\n\t// printf(\"### dynapro_build_map for dir:%s\\n\", fileptr->unix_path);\n\n\t// fileptr points to a directory header (volume or subdir).  Walk\n\t//  all siblings and build a map\n\tret = 1;\n\twhile(fileptr && ret) {\n\t\tif(fileptr->prodos_name[0] >= 0xe0) {\n\t\t\t// Directory/Volume header\n\t\t\tret = dynapro_map_dir_blocks(dsk, fileptr);\n\t\t} else if(fileptr->subdir_ptr) {\n\t\t\t// Recurse to handle subdirectory\n\t\t\tret = dynapro_build_map(dsk, fileptr->subdir_ptr);\n\t\t} else {\n\t\t\tret = dynapro_map_file(dsk, fileptr, 0);\n\t\t}\n\t\tfileptr = fileptr->next_ptr;\n\t}\n\tdynapro_debug_map(dsk, \"post build_map\");\n\treturn ret;\n}\n\nint\ndynapro_mount(Disk *dsk, char *dir_path, word32 num_blocks)\n{\n\tword32\tret;\n\n#if 0\n\tprintf(\"dynapro_mount: %p, %s %08x\\n\", dsk, dir_path, num_blocks);\n#endif\n\tif(num_blocks >= 65536) {\n\t\tnum_blocks = 65535;\n\t}\n\tret = dynapro_prep_image(dsk, dir_path, num_blocks);\n\tif(ret == 0) {\n\t\treturn -1;\n\t}\n\n\tret = dynapro_create_dir(dsk, dir_path, 0, 0x404);\t// Block 2, +4\n\t// printf(\"dynapro_mount will end with ret:%05x\\n\", ret);\n\tif(ret != 0) {\n\t\tret = dynapro_build_map(dsk, dsk->dynapro_info_ptr->volume_ptr);\n\t}\n\t// dynapro_debug_update(dsk);\n\tif(ret == 0) {\n\t\tdynapro_error(dsk, \"Folder too large.  dynapro_build_map \"\n\t\t\t\t\t\t\t\t\"ret:0\\n\");\n\t} else {\n\t\tret = dynapro_validate_disk(dsk);\n\t}\n\tif(ret == 0) {\n\t\tdynapro_error(dsk, \"dynapro_validate_disk ret:0\\n\");\n\t\tdynapro_free_dynapro_info(dsk);\n\t\treturn -1;\n\t}\n#ifndef _WIN32\n\tsetvbuf(stdout, 0, _IOLBF, 0);\n#endif\n\tdsk->fd = 0;\n\treturn 0;\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/engine.h",
    "content": "// \"@(#)$KmKId: engine.h,v 1.9 2023-09-11 12:55:16+00 kentd Exp $\"\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\nint\nENGINE_TYPE (Engine_reg *engine_ptr)\n{\n\tregister byte\t*ptr;\n\tbyte\t*arg_ptr;\n\tPc_log\t*tmp_pc_ptr;\n\tFplus\t*fplus_ptr;\n\tbyte\t*stat;\n\tdword64\tdfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;\n\tregister word32\tkpc, acc, xreg, yreg, direct, psr, zero, neg7, addr;\n\tword32\twstat, arg, stack, dbank, opcode, addr_latch, tmp1, tmp2;\n\tword32\tgetmem_tmp, save_addr, pull_tmp, tmp_bytes, dummy1;\n\n\ttmp_pc_ptr = 0;\n\tdummy1 = 0;\n\tif(tmp_pc_ptr || dummy1) {\t// \"use\" tmp_pc_ptr to avoid warning\n\t}\n\n\tkpc = engine_ptr->kpc;\n\tacc = engine_ptr->acc;\n\txreg = engine_ptr->xreg;\n\tyreg = engine_ptr->yreg;\n\tstack = engine_ptr->stack;\n\tdbank = engine_ptr->dbank;\n\tdirect = engine_ptr->direct;\n\tpsr = engine_ptr->psr;\n\tfplus_ptr = engine_ptr->fplus_ptr;\n\tzero = !(psr & 2);\n\tneg7 = psr;\n\n\tdplus_1 = fplus_ptr->dplus_1;\n\tdplus_x_m1 = fplus_ptr->dplus_x_minus_1;\n\tdfcyc = engine_ptr->dfcyc;\n\n\tg_ret1 = 0;\n\n\twhile(dfcyc <= g_dcycles_end) {\n\n\t\tFETCH_OPCODE;\n\n\t\tLOG_PC_MACRO();\n\n\t\tswitch(opcode) {\n\t\tdefault:\n\t\t\thalt_printf(\"acc8 unk op: %02x\\n\", opcode);\n\t\t\targ = 9\n#include \"defs_instr.h\"\n\t\t\t* 2;\n\t\t\tbreak;\n#include \"instable.h\"\n\t\t\tbreak;\n\t\t}\n\t\tLOG_PC_MACRO2();\n\t}\n\n\tengine_ptr->kpc = kpc;\n\tengine_ptr->acc = acc;\n\tengine_ptr->xreg = xreg;\n\tengine_ptr->yreg = yreg;\n\tengine_ptr->stack = stack;\n\tengine_ptr->dbank = dbank;\n\tengine_ptr->direct = direct;\n\tengine_ptr->dfcyc = dfcyc;\n\n\tpsr = psr & (~0x82);\n\tpsr |= (neg7 & 0x80);\n\tpsr |= ((!zero) << 1);\n\n\tengine_ptr->psr = psr;\n\n\treturn g_ret1;\n}\n"
  },
  {
    "path": "upstream/kegs/src/engine_c.c",
    "content": "const char rcsid_engine_c_c[] = \"@(#)$KmKId: engine_c.c,v 1.99 2025-04-27 18:54:08+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2025 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include \"defc.h\"\n\n// PSR[8:0] is E_NVMX_DIZC\n\nextern int g_limit_speed;\nextern int g_halt_sim;\nextern int g_engine_recalc_event;\nextern int g_code_red;\nextern int g_ignore_halts;\nextern int g_user_halt_bad;\nextern dword64 g_dcycles_end;\nextern dword64 g_last_vbl_dfcyc;\nextern dword64 g_cur_dfcyc;\nextern int g_wait_pending;\nextern int g_irq_pending;\nextern int g_num_brk;\nextern int g_num_cop;\nextern int g_emul_6502_ind_page_cross_bug;\nextern byte *g_slow_memory_ptr;\nextern byte *g_memory_ptr;\nextern byte *g_rom_fc_ff_ptr;\nextern byte *g_rom_cards_ptr;\nextern byte *g_dummy_memory1_ptr;\n\nextern int g_num_breakpoints;\nextern Break_point g_break_pts[];\n\nextern Kimage g_debugwin_kimage;\n\nextern word32 g_log_pc_enable;\nextern Pc_log *g_log_pc_ptr;\nextern Pc_log *g_log_pc_start_ptr;\nextern Pc_log *g_log_pc_end_ptr;\n\nextern Data_log *g_log_data_ptr;\nextern Data_log *g_log_data_start_ptr;\nextern Data_log *g_log_data_end_ptr;\n\nint\tg_ret1 = 0;\n\nint size_tab[] = {\n#include \"size_c.h\"\n};\n\nint bogus[] = {\n\t0,\n#include \"op_routs.h\"\n};\n\n#define INC_KPC_1\tkpc = (kpc & 0xff0000) + ((kpc + 1) & 0xffff);\n#define INC_KPC_2\tkpc = (kpc & 0xff0000) + ((kpc + 2) & 0xffff);\n#define INC_KPC_3\tkpc = (kpc & 0xff0000) + ((kpc + 3) & 0xffff);\n#define INC_KPC_4\tkpc = (kpc & 0xff0000) + ((kpc + 4) & 0xffff);\n\n#define CYCLES_PLUS_1\tdfcyc += dplus_1;\n#define CYCLES_PLUS_2\tdfcyc += dplus_1 * 2;\n#define CYCLES_PLUS_3\tdfcyc += dplus_1 * 3;\n#define CYCLES_PLUS_4\tdfcyc += dplus_1 * 4;\n#define CYCLES_PLUS_5\tdfcyc += dplus_1 * 5;\n#define CYCLES_MINUS_1\tdfcyc -= dplus_1;\n#define CYCLES_MINUS_2\tdfcyc -= dplus_1 * 2;\n\n#define FCYCLES_ROUND\tdfcyc = dfcyc + dplus_x_m1;\t\t\\\n\t\t\tdfcyc = (dfcyc >> 16) << 16;\n\n\n#define\tGET_1BYTE_ARG\targ = arg_ptr[1];\n#define\tGET_2BYTE_ARG\targ = arg_ptr[1] + (arg_ptr[2] << 8);\n#define\tGET_3BYTE_ARG\targ = arg_ptr[1] + (arg_ptr[2] << 8) + (arg_ptr[3]<<16);\n\n#define LOG_DATA_MACRO_ACT(in_addr, in_val, in_size, in_stat)\t\t\\\n\t\tg_log_data_ptr->dfcyc = dfcyc;\t\t\t\t\\\n\t\tg_log_data_ptr->stat = in_stat;\t\t\t\t\\\n\t\tg_log_data_ptr->addr = in_addr;\t\t\t\t\\\n\t\tg_log_data_ptr->val = in_val;\t\t\t\t\\\n\t\tg_log_data_ptr->size = in_size;\t\t\t\t\\\n\t\tg_log_data_ptr++;\t\t\t\t\t\\\n\t\tif(g_log_data_ptr >= g_log_data_end_ptr) {\t\t\\\n\t\t\tg_log_data_ptr = g_log_data_start_ptr;\t\t\\\n\t\t}\n\n/* HACK HACK HACK */\n#define\tUPDATE_PSR(dummy, old_psr)\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tpsr |= 0x30;\t\t\t\t\t\\\n\t\tstack = 0x100 + (stack & 0xff);\t\t\t\\\n\t}\t\t\t\t\t\t\t\\\n\tif((~old_psr & psr) & 0x10) {\t\t\t\t\\\n\t\txreg = xreg & 0xff;\t\t\t\t\\\n\t\tyreg = yreg & 0xff;\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\\\n\tif(((psr & 4) == 0) && g_irq_pending) {\t\t\t\\\n\t\tFINISH(RET_IRQ, 0);\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\\\n\tif((old_psr ^ psr) & 0x20) {\t\t\t\t\\\n\t\tFINISH(RET_PSR, 0);\t\t\t\t\\\n\t}\n\nextern Page_info page_info_rd_wr[];\nextern word32 g_slow_mem_changed[];\n\n#define GET_MEMORY8(addr,dest)\t\t\t\t\t\\\n\taddr_latch = (addr);\t\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\t\t\\\n\tstat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff);\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\\\n\tif(wstat & (1 << (31 - BANK_IO_BIT))) {\t\t\t\\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\\\n\t\tdest = get_memory8_io_stub((addr), stat,\t\\\n\t\t\t\t&dcycles_tmp1, dplus_x_m1);\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\tdest = *ptr;\t\t\t\t\t\\\n\t}\n\n#define GET_MEMORY(addr,dest)\tGET_MEMORY8(addr, dest)\n\n#define GET_MEMORY16(addr, dest, in_bank)\t\t\t\\\n\tsave_addr = addr;\t\t\t\t\t\\\n\tstat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff);\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\\\n\tif((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xff) == 0xff)) { \\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\\\n\t\tdest = get_memory16_pieces_stub((addr), stat,\t\\\n\t\t\t&dcycles_tmp1, fplus_ptr, in_bank);\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_2;\t\t\t\t\t\\\n\t\tdest = ptr[0] + (ptr[1] << 8);\t\t\t\\\n\t}\t\t\t\t\t\t\t\\\n\taddr_latch = save_addr;\n\n#define GET_MEMORY24(addr, dest, in_bank)\t\t\t\\\n\tsave_addr = addr;\t\t\t\t\t\\\n\tstat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff);\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\\\n\tif((wstat & (1 << (31 - BANK_IO_BIT))) || (((addr) & 0xfe) == 0xfe)) { \\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\\\n\t\tdest = get_memory24_pieces_stub((addr), stat,\t\\\n\t\t\t&dcycles_tmp1, fplus_ptr, in_bank);\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_3;\t\t\t\t\t\\\n\t\tdest = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16);\t\\\n\t}\t\t\t\t\t\t\t\\\n\taddr_latch = save_addr;\n\n#define GET_MEMORY_DIRECT_PAGE16(addr, dest, dloc_x_wrap)\t\t\\\n\tsave_addr = addr;\t\t\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\t\\\n\t\tif((direct & 0xff) == 0) {\t\t\t\t\\\n\t\t\tsave_addr = (save_addr & 0xff) + direct;\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\tif((psr & 0x100) && (((addr) & 0xff) == 0xff)) {\t\t\\\n\t\tGET_MEMORY8(save_addr, getmem_tmp);\t\t\t\\\n\t\tif(dloc_x_wrap) {\t\t\t\t\t\\\n\t\t\tsave_addr = (save_addr & 0xff00) |\t\t\\\n\t\t\t\t\t((save_addr + 1) & 0xff);\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\tsave_addr = (save_addr + 1) & 0xffff;\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tif((direct & 0xff) == 0) {\t\t\t\t\\\n\t\t\tsave_addr = (save_addr & 0xff) + direct;\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tGET_MEMORY8(save_addr, dest);\t\t\t\t\\\n\t\tdest = (dest << 8) + getmem_tmp;\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tGET_MEMORY16(save_addr, dest, 1);\t\t\t\\\n\t}\n\n\n#define PUSH8(arg)\t\t\t\t\t\t\\\n\tSET_MEMORY8(stack, arg);\t\t\t\t\\\n\tstack = (stack - 1) & 0xffff;\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\n\n#define PUSH16(arg)\t\t\t\t\t\t\\\n\tif((stack & 0xfe) == 0) {\t\t\t\t\\\n\t\t/* stack will cross page! */\t\t\t\\\n\t\tPUSH8((arg) >> 8);\t\t\t\t\\\n\t\tPUSH8(arg);\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\tstack = (stack - 2) & 0xffff;\t\t\t\\\n\t\tSET_MEMORY16(stack + 1, arg, 1);\t\t\\\n\t}\n\n#define PUSH16_UNSAFE(arg)\t\t\t\t\t\\\n\tsave_addr = (stack - 1) & 0xffff;\t\t\t\\\n\tstack = (stack - 2) & 0xffff;\t\t\t\t\\\n\tSET_MEMORY16(save_addr, arg, 1);\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\n\n#define PUSH24_UNSAFE(arg)\t\t\t\t\t\\\n\tsave_addr = (stack - 2) & 0xffff;\t\t\t\\\n\tstack = (stack - 3) & 0xffff;\t\t\t\t\\\n\tSET_MEMORY24(save_addr, arg, 1);\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\n\n#define PULL8(dest)\t\t\t\t\t\t\\\n\tstack++;\t\t\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\t\t\t\t\t\t\t\\\n\tstack = stack & 0xffff;\t\t\t\t\t\\\n\tGET_MEMORY8(stack, dest);\n\n#define PULL8_UNSAFE(dest)\t\t\t\t\t\\\n\tstack = (stack + 1) & 0xffff;\t\t\t\t\\\n\tGET_MEMORY8(stack, dest);\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\n\n#define PULL16(dest)\t\t\t\t\t\t\\\n\tif((stack & 0xfe) == 0xfe) {\t/* page cross */\t\\\n\t\tPULL8(dest);\t\t\t\t\t\\\n\t\tPULL8(pull_tmp);\t\t\t\t\\\n\t\tdest = (pull_tmp << 8) + dest;\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\tGET_MEMORY16(stack + 1, dest, 1);\t\t\\\n\t\tstack = (stack + 2) & 0xffff;\t\t\t\\\n\t\tif(psr & 0x100) {\t\t\t\t\\\n\t\t\tstack = 0x100 | (stack & 0xff);\t\t\\\n\t\t}\t\t\t\t\t\t\\\n\t}\n\n#define PULL16_UNSAFE(dest)\t\t\t\t\t\\\n\tstack = (stack + 1) & 0xffff;\t\t\t\t\\\n\tGET_MEMORY16(stack, dest, 1);\t\t\t\t\\\n\tstack = (stack + 1) & 0xffff;\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\n\n#define PULL24(dest)\t\t\t\t\t\t\\\n\tif((stack & 0xfc) == 0xfc) {\t/* page cross */\t\\\n\t\tPULL8(dest);\t\t\t\t\t\\\n\t\tPULL8(pull_tmp);\t\t\t\t\\\n\t\tpull_tmp = (pull_tmp << 8) + dest;\t\t\\\n\t\tPULL8(dest);\t\t\t\t\t\\\n\t\tdest = (dest << 16) + pull_tmp;\t\t\t\\\n\t} else {\t\t\t\t\t\t\\\n\t\tGET_MEMORY24(stack + 1, dest, 1);\t\t\\\n\t\tstack = (stack + 3) & 0xffff;\t\t\t\\\n\t\tif(psr & 0x100) {\t\t\t\t\\\n\t\t\tstack = 0x100 | (stack & 0xff);\t\t\\\n\t\t}\t\t\t\t\t\t\\\n\t}\n\n#define PULL24_UNSAFE(dest)\t\t\t\t\t\\\n\tstack = (stack + 1) & 0xffff;\t\t\t\t\\\n\tGET_MEMORY24(stack, dest, 1);\t\t\t\t\\\n\tstack = (stack + 2) & 0xffff;\t\t\t\t\\\n\tif(psr & 0x100) {\t\t\t\t\t\\\n\t\tstack = 0x100 | (stack & 0xff);\t\t\t\\\n\t}\n\n#define SET_MEMORY8(addr, val)\t\t\t\t\t\t\\\n\tstat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff);\t\t\\\n\tLOG_DATA_MACRO(addr, val, 8, stat);\t\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\t\t\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\t\\\n\tif(wstat) {\t\t\t\t\t\t\t\\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\t\\\n\t\tset_memory8_io_stub((addr), val, stat, &dcycles_tmp1,\t\\\n\t\t\t\tdplus_x_m1);\t\t\t\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\t*ptr = val;\t\t\t\t\t\t\\\n\t}\n\n\n#define SET_MEMORY16(addr, val, in_bank)\t\t\t\t\\\n\tstat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff);\t\t\\\n\tLOG_DATA_MACRO(addr, val, 16, stat);\t\t\t\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\t\\\n\tif((wstat) || (((addr) & 0xff) == 0xff)) {\t\t\t\\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\t\\\n\t\tset_memory16_pieces_stub((addr), (val),\t\t\t\\\n\t\t\t&dcycles_tmp1, dplus_1, dplus_x_m1, in_bank);\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_2;\t\t\t\t\t\t\\\n\t\tptr[0] = (val);\t\t\t\t\t\t\\\n\t\tptr[1] = (val) >> 8;\t\t\t\t\t\\\n\t}\n\n#define SET_MEMORY24(addr, val, in_bank)\t\t\t\t\\\n\tstat = GET_PAGE_INFO_WR(((addr) >> 8) & 0xffff);\t\t\\\n\tLOG_DATA_MACRO(addr, val, 24, stat);\t\t\t\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\t\\\n\tif((wstat) || (((addr) & 0xfe) == 0xfe)) {\t\t\t\\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\t\\\n\t\tset_memory24_pieces_stub((addr), (val),\t\t\t\\\n\t\t\t&dcycles_tmp1, fplus_ptr, in_bank);\t\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tCYCLES_PLUS_3;\t\t\t\t\t\t\\\n\t\tptr[0] = (val);\t\t\t\t\t\t\\\n\t\tptr[1] = (val) >> 8;\t\t\t\t\t\\\n\t\tptr[2] = (val) >> 16;\t\t\t\t\t\\\n\t}\n\n\nword32\nget_memory8_io_stub(word32 addr, byte *stat, dword64 *dcycs_ptr,\n\t\t\t\t\t\t\tdword64 dplus_x_m1)\n{\n\tdword64\tdfcyc;\n\tword32\twstat;\n\tbyte\t*ptr;\n\n\twstat = PTR2WORD(stat) & 0xff;\n\tdfcyc = *dcycs_ptr;\n\tif(wstat & BANK_BREAK) {\n\t\tcheck_breakpoints(addr, dfcyc, 0, 1);\n\t}\n\tif(wstat & BANK_IO2_TMP) {\n\t\tFCYCLES_ROUND;\n\t\t*dcycs_ptr = dfcyc;\n\t\treturn get_memory_io((addr), dcycs_ptr);\n\t} else {\n\t\tptr = stat - wstat + (addr & 0xff);\n\t\treturn *ptr;\n\t}\n}\n\nword32\nget_memory16_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr,\n\t\tFplus\t*fplus_ptr, int in_bank)\n{\n\tbyte\t*ptr;\n\tdword64\tdfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;\n\tword32\taddrp1, wstat, ret, tmp1, addr_latch;\n\n\taddr_latch = 0;\n\tif(addr_latch != 0) {\t// \"Use\" addr_latch to avoid warning\n\t}\n\tdfcyc = *dcycs_ptr;\n\tdplus_1 = fplus_ptr->dplus_1;\n\tdplus_x_m1 = fplus_ptr->dplus_x_minus_1;\n\tGET_MEMORY8(addr, tmp1);\n\taddrp1 = addr + 1;\n\tif(in_bank) {\n\t\taddrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);\n\t}\n\tGET_MEMORY8(addrp1, ret);\n\t*dcycs_ptr = dfcyc;\n\treturn (ret << 8) + (tmp1);\n}\n\nword32\nget_memory24_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr,\n\t\tFplus *fplus_ptr, int in_bank)\n{\n\tbyte\t*ptr;\n\tdword64\tdfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;\n\tword32\taddrp1, addrp2, wstat, addr_latch, ret, tmp1, tmp2;\n\n\taddr_latch = 0;\n\tif(addr_latch != 0) {\t// \"Use\" addr_latch to avoid warning\n\t}\n\tdfcyc = *dcycs_ptr;\n\tdplus_1 = fplus_ptr->dplus_1;\n\tdplus_x_m1 = fplus_ptr->dplus_x_minus_1;\n\tGET_MEMORY8(addr, tmp1);\n\taddrp1 = addr + 1;\n\tif(in_bank) {\n\t\taddrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);\n\t}\n\tGET_MEMORY8(addrp1, tmp2);\n\taddrp2 = addr + 2;\n\tif(in_bank) {\n\t\taddrp2 = (addr & 0xff0000) + (addrp2 & 0xffff);\n\t}\n\tGET_MEMORY8(addrp2, ret);\n\t*dcycs_ptr = dfcyc;\n\treturn (ret << 16) + (tmp2 << 8) + tmp1;\n}\n\nvoid\nset_memory8_io_stub(word32 addr, word32 val, byte *stat, dword64 *dcycs_ptr,\n\t\tdword64 dplus_x_m1)\n{\n\tbyte\t*ptr;\n\tdword64\tdfcyc;\n\tword32\tsetmem_tmp1, tmp1, tmp2, wstat;\n\n\twstat = PTR2WORD(stat) & 0xff;\n\tdfcyc = *dcycs_ptr;\n\tif(wstat & (1 << (31 - BANK_BREAK_BIT))) {\n\t\tcheck_breakpoints(addr, dfcyc, 0, 2);\n\t}\n\tptr = stat - wstat + ((addr) & 0xff);\n\tif(wstat & (1 << (31 - BANK_IO2_BIT))) {\n\t\tFCYCLES_ROUND;\n\t\t*dcycs_ptr = dfcyc;\n\t\tset_memory_io((addr), val, dcycs_ptr);\n\t} else if(wstat & (1 << (31 - BANK_SHADOW_BIT))) {\n\t\tif(g_limit_speed) {\n\t\t\tFCYCLES_ROUND;\n\t\t\t*dcycs_ptr = dfcyc;\n\t\t}\n\t\ttmp1 = (addr & 0xffff);\n\t\tsetmem_tmp1 = g_slow_memory_ptr[tmp1];\n\t\t*ptr = val;\n\t\tg_slow_memory_ptr[tmp1] = val;\n\t\tif(setmem_tmp1 != ((val) & 0xff)) {\n\t\t\tg_slow_mem_changed[tmp1 >> CHANGE_SHIFT] |=\n\t\t\t\t(1U << ((tmp1 >> SHIFT_PER_CHANGE) & 31));\n\t\t}\n\t} else if(wstat & (1 << (31 - BANK_SHADOW2_BIT))) {\n\t\tif(g_limit_speed) {\n\t\t\tFCYCLES_ROUND;\n\t\t\t*dcycs_ptr = dfcyc;\n\t\t}\n\t\ttmp2 = (addr & 0xffff);\n\t\ttmp1 = 0x10000 + tmp2;\n\t\tsetmem_tmp1 = g_slow_memory_ptr[tmp1];\n\t\t*ptr = val;\n\t\tg_slow_memory_ptr[tmp1] = val;\n\t\tif(setmem_tmp1 != ((val) & 0xff)) {\n\t\t\tg_slow_mem_changed[tmp1 >> CHANGE_SHIFT] |=\n\t\t\t\t(1U << ((tmp1 >> SHIFT_PER_CHANGE) & 31));\n\t\t\tif((tmp1 & 0xff00) == 0x9d00) {\n\t\t\t\tscb_changed(dfcyc, tmp1, val, setmem_tmp1);\n\t\t\t}\n\t\t}\n\t} else {\n\t\t/* breakpoint only */\n\t\t*ptr = val;\n\t}\n}\n\n#define LOG_PC_MACRO()\n#define LOG_PC_MACRO2()\n#define LOG_DATA_MACRO(addr, val, size, in_stat)\n\nvoid\nset_memory16_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr,\n\t\tdword64 dplus_1, dword64 dplus_x_m1, int in_bank)\n{\n\tbyte\t*ptr;\n\tbyte\t*stat;\n\tdword64\tdfcyc, dcycles_tmp1;\n\tword32\taddrp1, wstat;\n\n\tdfcyc = *dcycs_ptr;\n\tSET_MEMORY8(addr, val);\n\taddrp1 = addr + 1;\n\tif(in_bank) {\n\t\taddrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);\n\t}\n\tSET_MEMORY8(addrp1, val >> 8);\n\n\t*dcycs_ptr = dfcyc;\n}\n\nvoid\nset_memory24_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr,\n\t\tFplus\t*fplus_ptr, int in_bank)\n{\n\tbyte\t*ptr;\n\tbyte\t*stat;\n\tdword64\tdfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;\n\tword32\taddrp1, addrp2;\n\tword32\twstat;\n\n\tdfcyc = *dcycs_ptr;\n\tdplus_1 = fplus_ptr->dplus_1;\n\tdplus_x_m1 = fplus_ptr->dplus_x_minus_1;\n\tSET_MEMORY8(addr, val);\n\taddrp1 = addr + 1;\n\tif(in_bank) {\n\t\taddrp1 = (addr & 0xff0000) + (addrp1 & 0xffff);\n\t}\n\tSET_MEMORY8(addrp1, val >> 8);\n\taddrp2 = addr + 2;\n\tif(in_bank) {\n\t\taddrp2 = (addr & 0xff0000) + (addrp2 & 0xffff);\n\t}\n\tSET_MEMORY8(addrp2, val >> 16);\n\n\t*dcycs_ptr = dfcyc;\n}\n\nword32\nget_memory_c(word32 addr)\n{\n\tbyte\t*stat, *ptr;\n\tdword64\tdfcyc, dplus_1, dcycles_tmp1, dplus_x_m1;\n\tword32\taddr_latch, wstat, ret;\n\n\tdfcyc = 0;\n\tdplus_1 = 0;\n\tdplus_x_m1 = 0;\n\taddr_latch = 0;\n\tif(addr_latch != 0) {\t// \"Use\" addr_latch to avoid warning\n\t}\n\tGET_MEMORY8(addr, ret);\n\treturn ret;\n}\n\nword32\nget_memory16_c(word32 addr)\n{\n\treturn get_memory_c(addr) +\n\t\t\t(get_memory_c(addr+1) << 8);\n}\n\nword32\nget_memory24_c(word32 addr)\n{\n\treturn get_memory_c(addr) +\n\t\t\t(get_memory_c(addr+1) << 8) +\n\t\t\t(get_memory_c(addr+2) << 16);\n}\n\nvoid\nset_memory_c(word32 addr, word32 val, int do_log)\n{\n\tbyte\t*stat, *ptr;\n\tdword64\tdfcyc, dcycles_tmp1, dplus_1, dplus_x_m1;\n\tword32\twstat;\n\n\tdfcyc = g_cur_dfcyc;\n\tdplus_1 = 0;\n\tdplus_x_m1 = 0;\n\tSET_MEMORY8(addr, val);\n\tif(g_log_pc_enable && do_log) {\n\t\tLOG_DATA_MACRO_ACT(addr, val, 8, stat)\n\t}\n}\n\nvoid\nset_memory16_c(word32 addr, word32 val, int do_log)\n{\n\tbyte\t*stat, *ptr;\n\tdword64\tdfcyc, dcycles_tmp1, dplus_1, dplus_x_m1;\n\tword32\twstat;\n\n\tdfcyc = g_cur_dfcyc;\n\tdplus_1 = 0;\n\tdplus_x_m1 = 0;\n\tSET_MEMORY16(addr, val, 0);\n\tif(g_log_pc_enable && do_log) {\n\t\tLOG_DATA_MACRO_ACT(addr, val, 16, stat)\n\t}\n}\n\nvoid\nset_memory24_c(word32 addr, word32 val)\n{\n\tset_memory_c(addr, val, 1);\n\tset_memory_c(addr + 1, val >> 8, 1);\n\tset_memory_c(addr + 2, val >> 16, 1);\n}\n\nword32\ndo_adc_sbc8(word32 in1, word32 in2, word32 psr, int sub)\n{\n\tword32\tsum, carry, overflow;\n\tword32\tzero;\n\tint\tdecimal;\n\n\toverflow = 0;\n\tdecimal = psr & 8;\n\tif(sub) {\n\t\tin2 = (in2 ^ 0xff);\n\t}\n\tif(!decimal) {\n\t\tsum = (in1 & 0xff) + in2 + (psr & 1);\n\t\toverflow = ((sum ^ in2) >> 1) & 0x40;\n\t} else {\n\t\t/* decimal */\n\t\tsum = (in1 & 0xf) + (in2 & 0xf) + (psr & 1);\n\t\tif(sub) {\n\t\t\tif(sum < 0x10) {\n\t\t\t\tsum = (sum - 0x6) & 0xf;\n\t\t\t}\n\t\t} else {\n\t\t\tif(sum >= 0xa) {\n\t\t\t\tsum = (sum - 0xa) | 0x10;\n\t\t\t}\n\t\t}\n\n\t\tsum = (in1 & 0xf0) + (in2 & 0xf0) + sum;\n\t\toverflow = ((sum >> 2) ^ (sum >> 1)) & 0x40;\n\t\tif(sub) {\n\t\t\tif(sum < 0x100) {\n\t\t\t\tsum = (sum + 0xa0) & 0xff;\n\t\t\t}\n\t\t} else {\n\t\t\tif(sum >= 0xa0) {\n\t\t\t\tsum += 0x60;\n\t\t\t}\n\t\t}\n\t}\n\n\tzero = ((sum & 0xff) == 0);\n\tcarry = (sum >= 0x100);\n\tif((in1 ^ in2) & 0x80) {\n\t\toverflow = 0;\n\t}\n\n\tpsr = psr & (~0xc3);\n\tpsr = psr + (sum & 0x80) + overflow + (zero << 1) + carry;\n\n\treturn (psr << 16) + (sum & 0xff);\n}\n\nword32\ndo_adc_sbc16(word32 in1, word32 in2, word32 psr, int sub)\n{\n\tword32\tsum, carry, overflow;\n\tword32\ttmp1, tmp2;\n\tword32\tzero;\n\tint\tdecimal;\n\n\toverflow = 0;\n\tdecimal = psr & 8;\n\tif(!decimal) {\n\t\tif(sub) {\n\t\t\tin2 = (in2 ^ 0xffff);\n\t\t}\n\t\tsum = in1 + in2 + (psr & 1);\n\t\toverflow = ((sum ^ in2) >> 9) & 0x40;\n\t} else {\n\t\t/* decimal */\n\t\tif(sub) {\n\t\t\ttmp1 = do_adc_sbc8(in1 & 0xff, in2 & 0xff, psr, sub);\n\t\t\tpsr = (tmp1 >> 16);\n\t\t\ttmp2 = do_adc_sbc8((in1 >> 8) & 0xff,\n\t\t\t\t\t\t(in2 >> 8) & 0xff, psr, sub);\n\t\t\tin2 = (in2 ^ 0xfffff);\n\t\t} else {\n\t\t\ttmp1 = do_adc_sbc8(in1 & 0xff, in2 & 0xff, psr, sub);\n\t\t\tpsr = (tmp1 >> 16);\n\t\t\ttmp2 = do_adc_sbc8((in1 >> 8) & 0xff,\n\t\t\t\t\t\t(in2 >> 8) &0xff, psr, sub);\n\t\t}\n\t\tsum = ((tmp2 & 0xff) << 8) + (tmp1 & 0xff) +\n\t\t\t\t\t(((tmp2 >> 16) & 1) << 16);\n\t\toverflow = (tmp2 >> 16) & 0x40;\n\t}\n\n\tzero = ((sum & 0xffff) == 0);\n\tcarry = (sum >= 0x10000);\n\tif((in1 ^ in2) & 0x8000) {\n\t\toverflow = 0;\n\t}\n\n\tpsr = psr & (~0xc3);\n\tpsr = psr + ((sum & 0x8000) >> 8) + overflow + (zero << 1) + carry;\n\n\treturn (psr << 16) + (sum & 0xffff);\n}\n\nvoid\nfixed_memory_ptrs_init()\n{\n\t/* set g_slow_memory_ptr, g_rom_fc_ff_ptr, g_dummy_memory1_ptr, */\n\t/*  and rom_cards_ptr */\n\n\tg_slow_memory_ptr = memalloc_align(128*1024, 0, 0);\n\tg_dummy_memory1_ptr = memalloc_align(256, 1024, 0);\n\tg_rom_fc_ff_ptr = memalloc_align(256*1024, 512, 0);\n\tg_rom_cards_ptr = memalloc_align(16*256, 256, 0);\n\n#if 0\n\tprintf(\"g_memory_ptr: %08x, dummy_mem: %08x, slow_mem_ptr: %08x\\n\",\n\t\t(word32)g_memory_ptr, (word32)g_dummy_memory1_ptr,\n\t\t(word32)g_slow_memory_ptr);\n\tprintf(\"g_rom_fc_ff_ptr: %08x, g_rom_cards_ptr: %08x\\n\",\n\t\t(word32)g_rom_fc_ff_ptr, (word32)g_rom_cards_ptr);\n\tprintf(\"page_info_rd = %08x, page_info_wr end = %08x\\n\",\n\t\t(word32)&(page_info_rd_wr[0]),\n\t\t(word32)&(page_info_rd_wr[PAGE_INFO_PAD_SIZE+0x1ffff].rd_wr));\n#endif\n}\n\nword32\nget_itimer()\n{\n#if defined(__i386) && defined(__GNUC__)\n\t/* Here's my bad ia32 asm code to do rdtsc */\n\t/* Linux source uses: */\n\t/* asm volatile(\"rdtsc\" : \"=a\"(ret) : : \"edx\"); */\n\t/* asm volatile(\"rdtsc\" : \"=%eax\"(ret) : : \"%edx\"); */\n\n\t/* GCC bug report 2001-03/msg00786.html used: */\n\t/*register dword64 dtmp; */\n\t/*asm volatile (\"rdtsc\" : \"=A\" (dtmp)); */\n\t/*return (word32)dtmp; */\n\n\tregister word32 ret;\n\n\tasm volatile (\"rdtsc;movl %%eax,%0\" : \"=r\"(ret) : : \"%eax\",\"%edx\");\n\n\treturn ret;\n#else\n# if defined(__POWERPC__) && defined(__GNUC__)\n\tregister word32 ret;\n\n\tasm volatile (\"mftb %0\" : \"=r\"(ret));\n\treturn ret;\n# else\n#  if defined(__x86_64__)\n\tregister word32 ret, hi;\n\t//ret = __rdtsc();\n\tasm volatile (\"rdtsc\" : \"=a\" (ret), \"=d\" (hi));\n\treturn ret;\n#  else\n#\tif defined(__aarch64__)\t\t/* 64-bit ARM architecture */\n\t\tregister dword64 ret;\n\t\tasm volatile(\"mrs %0,CNTVCT_EL0\" : \"=r\"(ret));\n\t\treturn ret;\n#\telse\n\t\treturn 0;\n#\tendif\n#  endif\n# endif\n#endif\n}\n\nvoid\nengine_recalc_events()\n{\n\tg_dcycles_end = 0;\t\t// End inner loop\n\tg_engine_recalc_event++;\n}\n\nvoid\nset_halt_act(int val)\n{\n\tif((val == 1) && g_ignore_halts && !g_user_halt_bad) {\n\t\tg_code_red++;\n\t} else {\n\t\tif(g_halt_sim == 0) {\n\t\t\tdebugger_update_list_kpc();\n\t\t}\n\t\tg_halt_sim |= val;\n\t\tif(g_halt_sim) {\n\t\t\tvideo_set_active(&g_debugwin_kimage, 1);\n\t\t}\n\t\tg_dcycles_end = 0;\n\t}\n}\n\nvoid\nclr_halt_act()\n{\n\tg_halt_sim = 0;\n}\n\nword32\nget_remaining_operands(word32 addr, word32 opcode, word32 psr,\n\t\t\t\t\tdword64 *dcyc_ptr, Fplus *fplus_ptr)\n{\n\tbyte\t*stat, *ptr;\n\tdword64\tdfcyc, dcycles_tmp1, dplus_1, dplus_x_m1;\n\tword32\taddr_latch, wstat, save_addr, arg, addrp1;\n\tint\tsize;\n\n\tdfcyc = *dcyc_ptr;\n\tdplus_1 = fplus_ptr->dplus_1;\n\tdplus_x_m1 = fplus_ptr->dplus_x_minus_1;\n\taddr_latch = 0;\n\tif(addr_latch != 0) {\t// \"Use\" addr_latch to avoid warning\n\t}\n\n\tsize = size_tab[opcode];\n\n\taddrp1 = (addr & 0xff0000) + ((addr + 1) & 0xffff);\n\tswitch(size) {\n\tcase 0:\n\t\t// Always read pc+1 for single-byte opcodes\n\t\tGET_MEMORY8(addrp1, arg);\n\t\targ = 0;\t/* no args */\n\t\tbreak;\n\tcase 1:\n\t\tGET_MEMORY8(addrp1, arg);\n\t\tbreak;\t\t/* 1 arg, already done */\n\tcase 2:\n\t\tGET_MEMORY16(addrp1, arg, 1);\n\t\tdfcyc -= dplus_1;\n\t\tbreak;\n\tcase 3:\n\t\tGET_MEMORY24(addrp1, arg, 1);\n\t\tdfcyc = dfcyc - dplus_1 - dplus_1;\n\t\tbreak;\n\tcase 4:\n\t\tif(psr & 0x20) {\n\t\t\tGET_MEMORY8(addrp1, arg);\n\t\t} else {\n\t\t\tGET_MEMORY16(addrp1, arg, 1);\n\t\t\tdfcyc -= dplus_1;\n\t\t}\n\t\tbreak;\n\tcase 5:\n\t\tif(psr & 0x10) {\n\t\t\tGET_MEMORY8(addrp1, arg);\n\t\t} else {\n\t\t\tGET_MEMORY16(addrp1, arg, 1);\n\t\t\tdfcyc -= dplus_1;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tprintf(\"Unknown size: %d\\n\", size);\n\t\targ = 0;\n\t\texit(-2);\n\t}\n\t*dcyc_ptr = dfcyc;\n\n\treturn arg;\n}\n\n#define FETCH_OPCODE\t\t\t\t\t\t\t\\\n\taddr = kpc;\t\t\t\t\t\t\t\\\n\tCYCLES_PLUS_2;\t\t\t\t\t\t\t\\\n\tstat = GET_PAGE_INFO_RD(((addr) >> 8) & 0xffff);\t\t\\\n\twstat = PTR2WORD(stat) & 0xff;\t\t\t\t\t\\\n\tptr = stat - wstat + ((addr) & 0xff);\t\t\t\t\\\n\targ_ptr = ptr;\t\t\t\t\t\t\t\\\n\topcode = *ptr;\t\t\t\t\t\t\t\\\n\tif((wstat & (1 << (31-BANK_IO_BIT))) || ((addr & 0xff) > 0xfc)) {\\\n\t\tCYCLES_MINUS_1;\t\t\t\t\t\t\\\n\t\tif(wstat & BANK_BREAK) {\t\t\t\t\\\n\t\t\tcheck_breakpoints(addr, dfcyc, stack, 4);\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tif(wstat & (1 << (31 - BANK_IO2_BIT))) {\t\t\\\n\t\t\tFCYCLES_ROUND;\t\t\t\t\t\\\n\t\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\\\n\t\t\topcode = get_memory_io((addr), &dcycles_tmp1);\t\\\n\t\t\tdfcyc = dcycles_tmp1;\t\t\t\t\\\n\t\t} else {\t\t\t\t\t\t\\\n\t\t\topcode = *ptr;\t\t\t\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tdcycles_tmp1 = dfcyc;\t\t\t\t\t\\\n\t\targ = get_remaining_operands(addr, opcode, psr,\t\t\\\n\t\t\t\t\t&dcycles_tmp1, fplus_ptr);\t\\\n\t\tdfcyc = dcycles_tmp1;\t\t\t\t\t\\\n\t\targ_ptr = (byte *)&tmp_bytes;\t\t\t\t\\\n\t\targ_ptr[1] = arg;\t\t\t\t\t\\\n\t\targ_ptr[2] = arg >> 8;\t\t\t\t\t\\\n\t\targ_ptr[3] = arg >> 16;\t\t\t\t\t\\\n\t}\n\n\n#define ACC8\n#define IS_ACC16\t0\n#define ENGINE_TYPE enter_engine_acc8\n#include \"engine.h\"\n// The above creates enter_engine_acc8\n\n#undef ACC8\n#undef IS_ACC16\n#undef ENGINE_TYPE\n#define IS_ACC16\t1\n#define ENGINE_TYPE enter_engine_acc16\n#include \"engine.h\"\n// The above creates enter_engine_acc16\n\n#undef LOG_PC_MACRO\n#undef LOG_PC_MACRO2\n#undef LOG_DATA_MACRO\n\n#define LOG_PC_MACRO()\t\t\t\t\t\t\t\\\n\t\ttmp_pc_ptr = g_log_pc_ptr;\t\t\t\t\\\n\t\ttmp_pc_ptr->dbank_kpc = (dbank << 24) + kpc;\t\t\\\n\t\ttmp_pc_ptr->instr = (opcode << 24) + arg_ptr[1] +\t\\\n\t\t\t(arg_ptr[2] << 8) + (arg_ptr[3] << 16);\t\t\\\n\t\ttmp_pc_ptr->dfcyc = dfcyc - dplus_1 * 2;\n\n#define LOG_PC_MACRO2()\t\t\t\t\t\t\\\n\t\ttmp_pc_ptr->psr_acc = ((psr & ~(0x82)) << 16) | acc |\t\\\n\t\t\t((neg7 & 0x80) << 16) | ((!zero) << 17);\t\\\n\t\ttmp_pc_ptr->xreg_yreg = (xreg << 16) + yreg;\t\t\\\n\t\ttmp_pc_ptr->stack_direct = (stack << 16) + direct;\t\\\n\t\ttmp_pc_ptr++;\t\t\t\t\t\t\\\n\t\tif(tmp_pc_ptr >= g_log_pc_end_ptr) {\t\t\t\\\n\t\t\ttmp_pc_ptr = g_log_pc_start_ptr;\t\t\\\n\t\t}\t\t\t\t\t\t\t\\\n\t\tg_log_pc_ptr = tmp_pc_ptr;\n\n#define LOG_DATA_MACRO(in_addr, in_val, in_size, in_stat)\t\t\\\n\t\tLOG_DATA_MACRO_ACT(in_addr, in_val, in_size, in_stat)\n\n#undef ACC8\n#undef IS_ACC16\n#undef ENGINE_TYPE\n#define ACC8\n#define IS_ACC16\t0\n#define ENGINE_TYPE enter_engine_acc8_log\n#include \"engine.h\"\n// The above creates enter_engine_acc8_log\n\n#undef ACC8\n#undef IS_ACC16\n#undef ENGINE_TYPE\n#define IS_ACC16\t1\n#define ENGINE_TYPE enter_engine_acc16_log\n#include \"engine.h\"\n// The above creates enter_engine_acc16_log\n\n\nint\nenter_engine(Engine_reg *engine_ptr)\n{\n\tdword64\tdcycles_end_save;\n\tint\tret;\n\n\tdcycles_end_save = g_dcycles_end;\n\twhile(1) {\n\t\tif(g_log_pc_enable) {\n\t\t\tif(engine_ptr->psr & 0x20) {\t// 8-bit accumulator\n\t\t\t\tret = enter_engine_acc8_log(engine_ptr);\n\t\t\t} else {\n\t\t\t\tret = enter_engine_acc16_log(engine_ptr);\n\t\t\t}\n\t\t} else {\n\t\t\tif(engine_ptr->psr & 0x20) {\t// 8-bit accumulator\n\t\t\t\tret = enter_engine_acc8(engine_ptr);\n\t\t\t} else {\n\t\t\t\tret = enter_engine_acc16(engine_ptr);\n\t\t\t}\n\t\t}\n\t\tif((ret == RET_PSR) && !g_halt_sim) {\n\t\t\tg_dcycles_end = dcycles_end_save;\n\t\t\tcontinue;\n\t\t}\n\t\treturn ret;\n\t}\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/instable.h",
    "content": "// \"@(#)$KmKId: instable.h,v 1.121 2023-11-12 15:31:14+00 kentd Exp $\"\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2021 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\ncase 0x00:\t\t\t/*  brk */\n\tGET_1BYTE_ARG;\n\tg_num_brk++;\n\tINC_KPC_2;\n\tpsr = (psr & (~0x82)) | (neg7 & 0x80) | ((!zero) << 1);\n\tif(psr & 0x100) {\n\t\tPUSH16(kpc & 0xffff);\n\t\tPUSH8(psr & 0xff);\n\t\ttmp1 = 0xfffffe;\n\t\tdbank = 0;\n\t} else {\n\t\tPUSH8(kpc >> 16);\n\t\tPUSH16(kpc);\n\t\tPUSH8(psr & 0xff);\n\t\ttmp1 = 0xffffe6;\n\t\thalt_printf(\"Halting for native break!\\n\");\n\t}\n\ttmp1 = moremem_fix_vector_pull(tmp1);\n\tGET_MEMORY16(tmp1, kpc, 0);\n\tkpc = kpc & 0xffff;\n\tpsr |= 0x4;\n\tpsr &= ~(0x8);\n\tbreak;\n\ncase 0x01:\t\t\t/*  ORA (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x02:\t\t\t/*  COP */\n\tg_num_cop++;\n\tINC_KPC_2;\n\tpsr = (psr & ~0x82) | (neg7 & 0x80) | ((!zero) << 1);\n\tif(psr & 0x100) {\n\t\thalt_printf(\"Halting for emul COP at %04x\\n\", kpc);\n\t\tPUSH16(kpc & 0xffff);\n\t\tPUSH8(psr & 0xff);\n\t\ttmp1 = 0xfffff4;\n\t\tdbank = 0;\n\t} else {\n\t\tPUSH8(kpc >> 16);\n\t\tPUSH16(kpc & 0xffff);\n\t\tPUSH8(psr & 0xff);\n\t\ttmp1 = 0xffffe4;\n\t}\n\ttmp1 = moremem_fix_vector_pull(tmp1);\n\tGET_MEMORY16(tmp1, kpc, 0);\n\tkpc = kpc & 0xffff;\n\tpsr |= 4;\n\tpsr &= ~(0x8);\n\tbreak;\n\ncase 0x03:\t\t\t/*  ORA Disp8,S */\n\tGET_DISP8_S_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x04:\t\t\t/*  TSB Dloc */\n\tGET_DLOC_RD_RMW();\n\tTSB_INST(1);\n\tbreak;\n\ncase 0x05:\t\t\t/*  ORA Dloc */\n\tGET_DLOC_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x06:\t\t\t/*  ASL Dloc */\n\tGET_DLOC_RD_RMW();\n\tASL_INST(1);\n\tbreak;\n\ncase 0x07:\t\t\t/*  ORA [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x08:\t\t\t/*  PHP */\n\tINC_KPC_1;\n\tpsr = (psr & ~0x82) | (neg7 & 0x80) | ((!zero) << 1);\n\tPUSH8(psr);\n\tbreak;\n\ncase 0x09:\t\t\t/*  ORA #imm */\n\tGET_IMM_MEM();\n\tORA_INST();\n\tbreak;\n\ncase 0x0a:\t\t\t/*  ASL a */\n\tINC_KPC_1;\n\ttmp1 = acc + acc;\n#ifdef ACC8\n\tSET_CARRY8(tmp1);\n\tacc = (acc & 0xff00) + (tmp1 & 0xff);\n\tSET_NEG_ZERO8(acc & 0xff);\n#else\n\tSET_CARRY16(tmp1);\n\tacc = tmp1 & 0xffff;\n\tSET_NEG_ZERO16(acc);\n#endif\n\tbreak;\n\ncase 0x0b:\t\t\t/*  PHD */\n\tINC_KPC_1;\n\tPUSH16_UNSAFE(direct);\n\tbreak;\n\ncase 0x0c:\t\t\t/*  TSB abs */\n\tGET_ABS_RD_RMW();\n\tTSB_INST(0);\n\tbreak;\n\ncase 0x0d:\t\t\t/*  ORA abs */\n\tGET_ABS_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x0e:\t\t\t/*  ASL abs */\n\tGET_ABS_RD_RMW();\n\tASL_INST(0);\n\tbreak;\n\ncase 0x0f:\t\t\t/*  ORA long */\n\tGET_LONG_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x10:\t\t\t/*  BPL disp8 */\n\tBRANCH_DISP8((neg7 & 0x80) == 0);\n\tbreak;\n\ncase 0x11:\t\t\t/*  ORA (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x12:\t\t\t/*  ORA (Dloc) */\n\tGET_DLOC_IND_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x13:\t\t\t/*  ORA (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x14:\t\t\t/*  TRB Dloc */\n\tGET_DLOC_RD_RMW();\n\tTRB_INST(1);\n\tbreak;\n\ncase 0x15:\t\t\t/*  ORA Dloc,x */\n\tGET_DLOC_X_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x16:\t\t\t/*  ASL Dloc,X */\n\tGET_DLOC_X_RD_RMW();\n\tASL_INST(1);\n\tbreak;\n\ncase 0x17:\t\t\t/*  ORA [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x18:\t\t\t/*  CLC */\n\tpsr = psr & (~1);\n\tINC_KPC_1;\n\tbreak;\n\ncase 0x19:\t\t\t/*  ORA abs,y */\n\tGET_ABS_Y_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x1a:\t\t\t/*  INC a */\n\tINC_KPC_1;\n#ifdef ACC8\n\tacc = (acc & 0xff00) | ((acc + 1) & 0xff);\n\tSET_NEG_ZERO8(acc & 0xff);\n#else\n\tacc = (acc + 1) & 0xffff;\n\tSET_NEG_ZERO16(acc);\n#endif\n\tbreak;\n\ncase 0x1b:\t\t\t/*  TCS */\n\tstack = acc;\n\tINC_KPC_1;\n\tif(psr & 0x100) {\n\t\tstack = (stack & 0xff) + 0x100;\n\t}\n\tbreak;\n\ncase 0x1c:\t\t\t/*  TRB Abs */\n\tGET_ABS_RD_RMW();\n\tTRB_INST(0);\n\tbreak;\n\ncase 0x1d:\t\t\t/*  ORA Abs,X */\n\tGET_ABS_X_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x1e:\t\t\t/*  ASL Abs,X */\n\tGET_ABS_X_RD_RMW();\n\tASL_INST(0);\n\tbreak;\n\ncase 0x1f:\t\t\t/*  ORA Long,X */\n\tGET_LONG_X_RD();\n\tORA_INST();\n\tbreak;\n\ncase 0x20:\t\t\t/*  JSR abs */\n\tGET_2BYTE_ARG;\n\tINC_KPC_2;\n\tPUSH16(kpc);\n\tkpc = (kpc & 0xff0000) + arg;\n\tCYCLES_PLUS_2;\n\tbreak;\n\ncase 0x21:\t\t\t/*  AND (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x22:\t\t\t/*  JSL Long */\n\tGET_3BYTE_ARG;\n\ttmp1 = arg;\n\tCYCLES_PLUS_3;\n\tINC_KPC_3;\n\tPUSH24_UNSAFE(kpc);\n\tkpc = tmp1 & 0xffffff;\n\tbreak;\n\ncase 0x23:\t\t\t/*  AND Disp8,S */\n\tGET_DISP8_S_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x24:\t\t\t/*  BIT Dloc */\n\tGET_DLOC_RD();\n\tBIT_INST();\n\tbreak;\n\ncase 0x25:\t\t\t/*  AND Dloc */\n\tGET_DLOC_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x26:\t\t\t/*  ROL Dloc */\n\tGET_DLOC_RD_RMW();\n\tROL_INST(1);\n\tbreak;\n\ncase 0x27:\t\t\t/*  AND [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x28:\t\t\t/*  PLP */\n\tPULL8(tmp1);\n\ttmp2 = psr;\n\tCYCLES_PLUS_1;\n\tINC_KPC_1;\n\tpsr = (psr & ~0xff) | (tmp1 & 0xff);\n\tzero = !(psr & 2);\n\tneg7 = psr;\n\tUPDATE_PSR(psr, tmp2);\n\tbreak;\n\ncase 0x29:\t\t\t/*  AND #imm */\n\tGET_IMM_MEM();\n\tAND_INST();\n\tbreak;\n\ncase 0x2a:\t\t\t/*  ROL a */\n\tINC_KPC_1;\n#ifdef ACC8\n\ttmp1 = ((acc & 0xff) << 1) + (psr & 1);\n\tSET_CARRY8(tmp1);\n\tacc = (acc & 0xff00) + (tmp1 & 0xff);\n\tSET_NEG_ZERO8(tmp1 & 0xff);\n#else\n\ttmp1 = (acc << 1) + (psr & 1);\n\tSET_CARRY16(tmp1);\n\tacc = (tmp1 & 0xffff);\n\tSET_NEG_ZERO16(acc);\n#endif\n\tbreak;\n\ncase 0x2b:\t\t\t/*  PLD */\n\tINC_KPC_1;\n\tPULL16_UNSAFE(direct);\n\tCYCLES_PLUS_1;\n\tSET_NEG_ZERO16(direct);\n\tbreak;\n\ncase 0x2c:\t\t\t/*  BIT abs */\n\tGET_ABS_RD();\n\tBIT_INST();\n\tbreak;\n\ncase 0x2d:\t\t\t/*  AND abs */\n\tGET_ABS_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x2e:\t\t\t/*  ROL abs */\n\tGET_ABS_RD_RMW();\n\tROL_INST(0);\n\tbreak;\n\ncase 0x2f:\t\t\t/*  AND long */\n\tGET_LONG_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x30:\t\t\t/*  BMI disp8 */\n\tBRANCH_DISP8(neg7 & 0x80);\n\tbreak;\n\ncase 0x31:\t\t\t/*  AND (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x32:\t\t\t/*  AND (Dloc) */\n\tGET_DLOC_IND_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x33:\t\t\t/*  AND (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x34:\t\t\t/*  BIT Dloc,x */\n\tGET_DLOC_X_RD();\n\tBIT_INST();\n\tbreak;\n\ncase 0x35:\t\t\t/*  AND Dloc,x */\n\tGET_DLOC_X_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x36:\t\t\t/*  ROL Dloc,X */\n\tGET_DLOC_X_RD_RMW();\n\tROL_INST(1);\n\tbreak;\n\ncase 0x37:\t\t\t/*  AND [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x38:\t\t\t/*  SEC */\n\tpsr = psr | 1;\n\tINC_KPC_1;\n\tbreak;\n\ncase 0x39:\t\t\t/*  AND abs,y */\n\tGET_ABS_Y_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x3a:\t\t\t/*  DEC a */\n\tINC_KPC_1;\n#ifdef ACC8\n\tacc = (acc & 0xff00) | ((acc - 1) & 0xff);\n\tSET_NEG_ZERO8(acc & 0xff);\n#else\n\tacc = (acc - 1) & 0xffff;\n\tSET_NEG_ZERO16(acc);\n#endif\n\tbreak;\n\ncase 0x3b:\t\t\t/*  TSC */\n/*  set N,Z according to 16 bit acc */\n\tINC_KPC_1;\n\tacc = stack;\n\tSET_NEG_ZERO16(acc);\n\tbreak;\n\ncase 0x3c:\t\t\t/*  BIT Abs,x */\n\tGET_ABS_X_RD();\n\tBIT_INST();\n\tbreak;\n\ncase 0x3d:\t\t\t/*  AND Abs,X */\n\tGET_ABS_X_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x3e:\t\t\t/*  ROL Abs,X */\n\tGET_ABS_X_RD_RMW();\n\tROL_INST(0);\n\tbreak;\n\ncase 0x3f:\t\t\t/*  AND Long,X */\n\tGET_LONG_X_RD();\n\tAND_INST();\n\tbreak;\n\ncase 0x40:\t\t\t/*  RTI */\n\tCYCLES_PLUS_1\n\tif(psr & 0x100) {\n\t\tPULL24(tmp1);\n\t\tkpc = (kpc & 0xff0000) + ((tmp1 >> 8) & 0xffff);\n\t\ttmp2 = psr;\n\t\tpsr = (psr & ~0xff) + (tmp1 & 0xff);\n\t\tneg7 = psr;\n\t\tzero = !(psr & 2);\n\t\tUPDATE_PSR(psr, tmp2);\n\t} else {\n\t\tPULL8(tmp1);\n\t\ttmp2 = psr;\n\t\tpsr = (tmp1 & 0xff);\n\t\tneg7 = psr;\n\t\tzero = !(psr & 2);\n\t\tPULL24(kpc);\n\t\tUPDATE_PSR(psr, tmp2);\n\t}\n\tbreak;\n\ncase 0x41:\t\t\t/*  EOR (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x42:\t\t\t/*  WDM */\n\tGET_2BYTE_ARG;\n\tINC_KPC_2;\n\tif(arg < 0x100) {\t// Next byte is 00\n\t\tINC_KPC_1;\t// Skip over the BRK\n\t}\n\tFINISH(RET_WDM, arg);\n\tbreak;\n\ncase 0x43:\t\t\t/*  EOR Disp8,S */\n\tGET_DISP8_S_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x44:\t\t\t/*  MVP */\n\tGET_2BYTE_ARG;\n\t/* arg & 0xff = dest bank, arg & 0xff00 = src bank */\n\tif(psr & 0x100) {\n\t\thalt_printf(\"MVP in emulation!\\n\");\n\t\tbreak;\n\t}\n\tdbank = arg & 0xff;\n\ttmp1 = (arg >> 8) & 0xff;\n\tCYCLES_PLUS_1;\n\tGET_MEMORY8((tmp1 << 16) + xreg, arg);\n\tSET_MEMORY8((dbank << 16) + yreg, arg);\n\tCYCLES_PLUS_2;\n\txreg = (xreg - 1) & 0xffff;\n\tyreg = (yreg - 1) & 0xffff;\n\tif(psr & 0x10) {\t// 8-bit index registers\n\t\txreg = xreg & 0xff;\n\t\tyreg = yreg & 0xff;\n\t}\n\tacc = (acc - 1) & 0xffff;\n\tif(acc == 0xffff) {\n\t\tINC_KPC_3;\n\t}\n\tbreak;\n\ncase 0x45:\t\t\t/*  EOR Dloc */\n\tGET_DLOC_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x46:\t\t\t/*  LSR Dloc */\n\tGET_DLOC_RD_RMW();\n\tLSR_INST(1);\n\tbreak;\n\ncase 0x47:\t\t\t/*  EOR [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x48:\t\t\t/*  PHA */\n\tINC_KPC_1;\n#ifdef ACC8\n\tPUSH8(acc);\n#else\n\tPUSH16(acc);\n#endif\n\tbreak;\n\ncase 0x49:\t\t\t/*  EOR #imm */\n\tGET_IMM_MEM();\n\tEOR_INST();\n\tbreak;\n\ncase 0x4a:\t\t\t/*  LSR a */\n\tINC_KPC_1;\n#ifdef ACC8\n\ttmp1 = ((acc & 0xff) >> 1);\n\tSET_CARRY8(acc << 8);\n\tacc = (acc & 0xff00) + (tmp1 & 0xff);\n\tSET_NEG_ZERO8(tmp1 & 0xff);\n#else\n\ttmp1 = (acc >> 1);\n\tSET_CARRY8((acc << 8));\n\tacc = (tmp1 & 0xffff);\n\tSET_NEG_ZERO16(acc);\n#endif\n\tbreak;\n\ncase 0x4b:\t\t\t/*  PHK */\n\tPUSH8(kpc >> 16);\n\tINC_KPC_1;\n\tbreak;\n\ncase 0x4c:\t\t\t/*  JMP abs */\n\tGET_2BYTE_ARG;\n\tCYCLES_PLUS_1;\n\tkpc = (kpc & 0xff0000) + arg;\n\tbreak;\n\ncase 0x4d:\t\t\t/*  EOR abs */\n\tGET_ABS_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x4e:\t\t\t/*  LSR abs */\n\tGET_ABS_RD_RMW();\n\tLSR_INST(0);\n\tbreak;\n\ncase 0x4f:\t\t\t/*  EOR long */\n\tGET_LONG_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x50:\t\t\t/*  BVC disp8 */\n\tBRANCH_DISP8((psr & 0x40) == 0);\n\tbreak;\n\ncase 0x51:\t\t\t/*  EOR (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x52:\t\t\t/*  EOR (Dloc) */\n\tGET_DLOC_IND_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x53:\t\t\t/*  EOR (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x54:\t\t\t/*  MVN  */\n\tGET_2BYTE_ARG;\n\t/* arg & 0xff = dest bank, arg & 0xff00 = src bank */\n\tif(psr & 0x100) {\n\t\thalt_printf(\"MVN in emulation!\\n\");\n\t\tbreak;\n\t}\n\tdbank = arg & 0xff;\n\ttmp1 = (arg >> 8) & 0xff;\n\tCYCLES_PLUS_1;\n\tGET_MEMORY8((tmp1 << 16) + xreg, arg);\n\tSET_MEMORY8((dbank << 16) + yreg, arg);\n\tCYCLES_PLUS_2;\n\txreg = (xreg + 1) & 0xffff;\n\tyreg = (yreg + 1) & 0xffff;\n\tif(psr & 0x10) {\t// 8-bit index registers\n\t\txreg = xreg & 0xff;\n\t\tyreg = yreg & 0xff;\n\t}\n\tacc = (acc - 1) & 0xffff;\n\tif(acc == 0xffff) {\n\t\tINC_KPC_3;\n\t}\n\tbreak;\n\ncase 0x55:\t\t\t/*  EOR Dloc,x */\n\tGET_DLOC_X_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x56:\t\t\t/*  LSR Dloc,X */\n\tGET_DLOC_X_RD_RMW();\n\tLSR_INST(1);\n\tbreak;\n\ncase 0x57:\t\t\t/*  EOR [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x58:\t\t\t/*  CLI */\n\tpsr = psr & (~4);\n\tINC_KPC_1;\n\tif(((psr & 0x4) == 0) && g_irq_pending) {\n\t\tFINISH(RET_IRQ, 0);\n\t}\n\tbreak;\n\ncase 0x59:\t\t\t/*  EOR abs,y */\n\tGET_ABS_Y_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x5a:\t\t\t/*  PHY */\n\tINC_KPC_1;\n\tif(psr & 0x10) {\n\t\tPUSH8(yreg);\n\t} else {\n\t\tPUSH16(yreg);\n\t}\n\tbreak;\n\ncase 0x5b:\t\t\t/*  TCD */\n\tINC_KPC_1;\n\tdirect = acc;\n\tSET_NEG_ZERO16(acc);\n\tbreak;\n\ncase 0x5c:\t\t\t/*  JMP Long */\n\tGET_3BYTE_ARG;\n\tCYCLES_PLUS_2;\n\tkpc = arg;\n\tbreak;\n\ncase 0x5d:\t\t\t/*  EOR Abs,X */\n\tGET_ABS_X_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x5e:\t\t\t/*  LSR Abs,X */\n\tGET_ABS_X_RD_RMW();\n\tLSR_INST(0);\n\tbreak;\n\ncase 0x5f:\t\t\t/*  EOR Long,X */\n\tGET_LONG_X_RD();\n\tEOR_INST();\n\tbreak;\n\ncase 0x60:\t\t\t/*  RTS */\n\tCYCLES_PLUS_2\n\tPULL16(tmp1);\n\tkpc = (kpc & 0xff0000) + ((tmp1 + 1) & 0xffff);\n\tbreak;\n\ncase 0x61:\t\t\t/*  ADC (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x62:\t\t\t/*  PER */\n\tGET_2BYTE_ARG;\n\tCYCLES_PLUS_2;\n\tINC_KPC_3;\n\tPUSH16_UNSAFE(kpc + arg);\n\tbreak;\n\ncase 0x63:\t\t\t/*  ADC Disp8,S */\n\tGET_DISP8_S_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x64:\t\t\t/*  STZ Dloc */\n\tGET_DLOC_ADDR();\n\tSTZ_INST(1);\n\tbreak;\n\ncase 0x65:\t\t\t/*  ADC Dloc */\n\tGET_DLOC_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x66:\t\t\t/*  ROR Dloc */\n\tGET_DLOC_RD_RMW();\n\tROR_INST(1);\n\tbreak;\n\ncase 0x67:\t\t\t/*  ADC [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x68:\t\t\t/*  PLA */\n\tINC_KPC_1;\n\tCYCLES_PLUS_1;\n#ifdef ACC8\n\tPULL8(tmp1);\n\tacc = (acc & 0xff00) + tmp1;\n\tSET_NEG_ZERO8(tmp1);\n#else\n\tPULL16(tmp1);\n\tacc = tmp1;\n\tSET_NEG_ZERO16(tmp1);\n#endif\n\tbreak;\n\ncase 0x69:\t\t\t/*  ADC #imm */\n\tGET_IMM_MEM();\n\tADC_INST();\n\tbreak;\n\ncase 0x6a:\t\t\t/*  ROR a */\n\tINC_KPC_1;\n#ifdef ACC8\n\ttmp1 = ((acc & 0xff) >> 1) + ((psr & 1) << 7);\n\tSET_CARRY8((acc << 8));\n\tacc = (acc & 0xff00) + (tmp1 & 0xff);\n\tSET_NEG_ZERO8(tmp1 & 0xff);\n#else\n\ttmp1 = (acc >> 1) + ((psr & 1) << 15);\n\tSET_CARRY16((acc << 16));\n\tacc = (tmp1 & 0xffff);\n\tSET_NEG_ZERO16(acc);\n#endif\n\tbreak;\n\ncase 0x6b:\t\t\t/*  RTL */\n\tCYCLES_PLUS_1;\n\tPULL24_UNSAFE(tmp1);\n\tkpc = (tmp1 & 0xff0000) + ((tmp1 + 1) & 0xffff);\n\tbreak;\n\ncase 0x6c:\t\t\t/*  JMP (abs) */\n\tGET_2BYTE_ARG;\n\tCYCLES_PLUS_1;\n\tGET_MEMORY16(arg, tmp1, 1);\n\tif((psr & 0x100) && g_emul_6502_ind_page_cross_bug &&\n\t\t\t\t\t((arg & 0xff) == 0xff)) {\n\t\tGET_MEMORY8(arg - 0xff, tmp2);\n\t\ttmp1 = (tmp1 & 0xff) + (tmp2 << 8);\n\t\thalt_printf(\"Halting for emul ind jmp\\n\");\n\t}\n\tkpc = (kpc & 0xff0000) + tmp1;\n\tbreak;\n\ncase 0x6d:\t\t\t/*  ADC abs */\n\tGET_ABS_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x6e:\t\t\t/*  ROR abs */\n\tGET_ABS_RD_RMW();\n\tROR_INST(0);\n\tbreak;\n\ncase 0x6f:\t\t\t/*  ADC long */\n\tGET_LONG_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x70:\t\t\t/*  BVS disp8 */\n\tBRANCH_DISP8((psr & 0x40));\n\tbreak;\n\ncase 0x71:\t\t\t/*  ADC (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x72:\t\t\t/*  ADC (Dloc) */\n\tGET_DLOC_IND_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x73:\t\t\t/*  ADC (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x74:\t\t\t/*  STZ Dloc,x */\n\tGET_1BYTE_ARG;\n\tGET_DLOC_X_WR();\n\tSTZ_INST(1);\n\tbreak;\n\ncase 0x75:\t\t\t/*  ADC Dloc,x */\n\tGET_DLOC_X_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x76:\t\t\t/*  ROR Dloc,X */\n\tGET_DLOC_X_RD_RMW();\n\tROR_INST(1);\n\tbreak;\n\ncase 0x77:\t\t\t/*  ADC [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x78:\t\t\t/*  SEI */\n\tpsr = psr | 4;\n\tINC_KPC_1;\n\tbreak;\n\ncase 0x79:\t\t\t/*  ADC abs,y */\n\tGET_ABS_Y_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x7a:\t\t\t/*  PLY */\n\tINC_KPC_1;\n\tCYCLES_PLUS_1\n\tif(psr & 0x10) {\n\t\tPULL8(yreg);\n\t\tSET_NEG_ZERO8(yreg);\n\t} else {\n\t\tPULL16(yreg);\n\t\tSET_NEG_ZERO16(yreg);\n\t}\n\tbreak;\n\ncase 0x7b:\t\t\t/*  TDC */\n\tINC_KPC_1;\n\tacc = direct;\n\tSET_NEG_ZERO16(direct);\n\tbreak;\n\ncase 0x7c:\t\t\t/*  JMP (Abs,x) */\n/*  always access kbank, xreg cannot wrap into next bank */\n\tGET_2BYTE_ARG;\n\targ = (kpc & 0xff0000) + ((xreg + arg) & 0xffff);\n\tCYCLES_PLUS_2;\n\tGET_MEMORY16(arg, tmp1, 1);\n\tkpc = (kpc & 0xff0000) + tmp1;\n\tbreak;\n\ncase 0x7d:\t\t\t/*  ADC Abs,X */\n\tGET_ABS_X_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x7e:\t\t\t/*  ROR Abs,X */\n\tGET_ABS_X_RD_RMW();\n\tROR_INST(0);\n\tbreak;\n\ncase 0x7f:\t\t\t/*  ADC Long,X */\n\tGET_LONG_X_RD();\n\tADC_INST();\n\tbreak;\n\ncase 0x80:\t\t\t/*  BRA */\n\tBRANCH_DISP8(1);\n\tbreak;\n\ncase 0x81:\t\t\t/*  STA (Dloc,X) */\n\tGET_DLOC_X_IND_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x82:\t\t\t/*  BRL disp16 */\n\tGET_2BYTE_ARG;\n\tCYCLES_PLUS_1;\n\tkpc = (kpc & 0xff0000) + ((kpc + 3 + arg) & 0xffff);\n\tbreak;\n\ncase 0x83:\t\t\t/*  STA Disp8,S */\n\tGET_DISP8_S_ADDR();\n\tSTA_INST(1);\n\tbreak;\n\ncase 0x84:\t\t\t/*  STY Dloc */\n\tGET_DLOC_ADDR();\n\tSTY_INST(1);\n\tbreak;\n\ncase 0x85:\t\t\t/*  STA Dloc */\n\tGET_DLOC_ADDR();\n\tSTA_INST(1);\n\tbreak;\n\ncase 0x86:\t\t\t/*  STX Dloc */\n\tGET_DLOC_ADDR();\n\tSTX_INST(1);\n\tbreak;\n\ncase 0x87:\t\t\t/*  STA [Dloc] */\n\tGET_DLOC_L_IND_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x88:\t\t\t/*  DEY */\n\tINC_KPC_1;\n\tSET_INDEX_REG(yreg - 1, yreg);\n\tbreak;\n\ncase 0x89:\t\t\t/*  BIT #imm */\n\tGET_IMM_MEM();\n#ifdef ACC8\n\tzero = (acc & arg) & 0xff;\n#else\n\tzero = (acc & arg) & 0xffff;\n#endif\n\tbreak;\n\ncase 0x8a:\t\t\t/*  TXA */\n\tINC_KPC_1;\n\targ = xreg;\n\tLDA_INST();\n\tbreak;\n\ncase 0x8b:\t\t\t/*  PHB */\n\tINC_KPC_1;\n\tPUSH8(dbank);\n\tbreak;\n\ncase 0x8c:\t\t\t/*  STY abs */\n\tGET_ABS_ADDR();\n\tSTY_INST(0);\n\tbreak;\n\ncase 0x8d:\t\t\t/*  STA abs */\n\tGET_ABS_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x8e:\t\t\t/*  STX abs */\n\tGET_ABS_ADDR();\n\tSTX_INST(0);\n\tbreak;\n\ncase 0x8f:\t\t\t/*  STA long */\n\tGET_LONG_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x90:\t\t\t/*  BCC disp8 */\n\tBRANCH_DISP8((psr & 0x01) == 0);\n\tbreak;\n\ncase 0x91:\t\t\t/*  STA (Dloc),y */\n\tGET_DLOC_IND_Y_ADDR(1);\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x92:\t\t\t/*  STA (Dloc) */\n\tGET_DLOC_IND_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x93:\t\t\t/*  STA (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x94:\t\t\t/*  STY Dloc,x */\n\tGET_DLOC_X_ADDR();\n\tSTY_INST(1);\n\tbreak;\n\ncase 0x95:\t\t\t/*  STA Dloc,x */\n\tGET_DLOC_X_ADDR();\n\tSTA_INST(1);\n\tbreak;\n\ncase 0x96:\t\t\t/*  STX Dloc,Y */\n\tGET_DLOC_Y_ADDR();\n\tSTX_INST(1);\n\tbreak;\n\ncase 0x97:\t\t\t/*  STA [Dloc],Y */\n\tGET_DLOC_L_IND_Y_ADDR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x98:\t\t\t/*  TYA */\n\tINC_KPC_1;\n\targ = yreg;\n\tLDA_INST();\n\tbreak;\n\ncase 0x99:\t\t\t/*  STA abs,y */\n\tGET_ABS_INDEX_ADDR(yreg, 1)\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x9a:\t\t\t/*  TXS */\n\tstack = xreg;\n\tif(psr & 0x100) {\n\t\tstack = 0x100 | (stack & 0xff);\n\t}\n\tINC_KPC_1;\n\tbreak;\n\ncase 0x9b:\t\t\t/*  TXY */\n\tSET_INDEX_REG(xreg, yreg);\n\tINC_KPC_1;\n\tbreak;\n\ncase 0x9c:\t\t\t/*  STZ Abs */\n\tGET_ABS_ADDR();\n\tSTZ_INST(0);\n\tbreak;\n\ncase 0x9d:\t\t\t/*  STA Abs,X */\n\tGET_ABS_INDEX_ADDR(xreg, 1);\n\tSTA_INST(0);\n\tbreak;\n\ncase 0x9e:\t\t\t/*  STZ Abs,X */\n\tGET_ABS_INDEX_ADDR(xreg, 1);\n\tSTZ_INST(0);\n\tbreak;\n\ncase 0x9f:\t\t\t/*  STA Long,X */\n\tGET_LONG_X_ADDR_FOR_WR();\n\tSTA_INST(0);\n\tbreak;\n\ncase 0xa0:\t\t\t/*  LDY #imm */\n\tINC_KPC_2;\n\tif((psr & 0x10) == 0) {\n\t\tGET_2BYTE_ARG;\n\t\tCYCLES_PLUS_1\n\t\tINC_KPC_1;\n\t} else {\n\t\tGET_1BYTE_ARG;\n\t}\n\tSET_INDEX_REG(arg, yreg);\n\tbreak;\n\ncase 0xa1:\t\t\t/*  LDA (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xa2:\t\t\t/*  LDX #imm */\n\tINC_KPC_2;\n\tif((psr & 0x10) == 0) {\n\t\tGET_2BYTE_ARG;\n\t\tCYCLES_PLUS_1\n\t\tINC_KPC_1;\n\t} else {\n\t\tGET_1BYTE_ARG;\n\t}\n\tSET_INDEX_REG(arg, xreg);\n\tbreak;\n\ncase 0xa3:\t\t\t/*  LDA Disp8,S */\n\tGET_DISP8_S_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xa4:\t\t\t/*  LDY Dloc */\n\tC_LDY_DLOC();\n\tbreak;\n\ncase 0xa5:\t\t\t/*  LDA Dloc */\n\tGET_DLOC_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xa6:\t\t\t/*  LDX Dloc */\n\tC_LDX_DLOC();\n\tbreak;\n\ncase 0xa7:\t\t\t/*  LDA [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xa8:\t\t\t/*  TAY */\n\tINC_KPC_1;\n\tSET_INDEX_REG(acc, yreg);\n\tbreak;\n\ncase 0xa9:\t\t\t/*  LDA #imm */\n\tGET_IMM_MEM();\n\tLDA_INST();\n\tbreak;\n\ncase 0xaa:\t\t\t/*  TAX */\n\tINC_KPC_1;\n\tSET_INDEX_REG(acc, xreg);\n\tbreak;\n\ncase 0xab:\t\t\t/*  PLB */\n\tINC_KPC_1;\n\tCYCLES_PLUS_1\n\tPULL8_UNSAFE(dbank);\n\tSET_NEG_ZERO8(dbank);\n\tbreak;\n\ncase 0xac:\t\t\t/*  LDY abs */\n\tC_LDY_ABS();\n\tbreak;\n\ncase 0xad:\t\t\t/*  LDA abs */\n\tGET_ABS_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xae:\t\t\t/*  LDX abs */\n\tC_LDX_ABS();\n\tbreak;\n\ncase 0xaf:\t\t\t/*  LDA long */\n\tGET_LONG_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xb0:\t\t\t/*  BCS disp8 */\n\tBRANCH_DISP8((psr & 0x01));\n\tbreak;\n\ncase 0xb1:\t\t\t/*  LDA (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xb2:\t\t\t/*  LDA (Dloc) */\n\tGET_DLOC_IND_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xb3:\t\t\t/*  LDA (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xb4:\t\t\t/*  LDY Dloc,x */\n\tC_LDY_DLOC_X();\n\tbreak;\n\ncase 0xb5:\t\t\t/*  LDA Dloc,x */\n\tGET_DLOC_X_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xb6:\t\t\t/*  LDX Dloc,y */\n\tC_LDX_DLOC_Y();\n\tbreak;\n\ncase 0xb7:\t\t\t/*  LDA [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xb8:\t\t\t/*  CLV */\n\tpsr = psr & ~0x40;\n\tINC_KPC_1;\n\tbreak;\n\ncase 0xb9:\t\t\t/*  LDA abs,y */\n\tGET_ABS_Y_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xba:\t\t\t/*  TSX */\n\tINC_KPC_1;\n\tSET_INDEX_REG(stack, xreg);\n\tbreak;\n\ncase 0xbb:\t\t\t/*  TYX */\n\tINC_KPC_1;\n\tSET_INDEX_REG(yreg, xreg);\n\tbreak;\n\ncase 0xbc:\t\t\t/*  LDY Abs,X */\n\tC_LDY_ABS_X();\n\tbreak;\n\ncase 0xbd:\t\t\t/*  LDA Abs,X */\n\tGET_ABS_X_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xbe:\t\t\t/*  LDX Abs,y */\n\tC_LDX_ABS_Y();\n\tbreak;\n\ncase 0xbf:\t\t\t/*  LDA Long,X */\n\tGET_LONG_X_RD();\n\tLDA_INST();\n\tbreak;\n\ncase 0xc0:\t\t\t/*  CPY #imm */\n\tC_CPY_IMM();\n\tbreak;\n\ncase 0xc1:\t\t\t/*  CMP (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xc2:\t\t\t/*  REP #imm */\n\tGET_1BYTE_ARG;\n\ttmp2 = psr;\n\tCYCLES_PLUS_1;\n\tINC_KPC_2;\n\tpsr = (psr & ~0x82) | (neg7 & 0x80) | ((!zero) << 1);\n\tpsr = psr & ~(arg & 0xff);\n\tzero = !(psr & 2);\n\tneg7 = psr;\n\tUPDATE_PSR(psr, tmp2);\n\tbreak;\n\ncase 0xc3:\t\t\t/*  CMP Disp8,S */\n\tGET_DISP8_S_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xc4:\t\t\t/*  CPY Dloc */\n\tC_CPY_DLOC();\n\tbreak;\n\ncase 0xc5:\t\t\t/*  CMP Dloc */\n\tGET_DLOC_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xc6:\t\t\t/*  DEC Dloc */\n\tGET_DLOC_RD_RMW();\n\tDEC_INST(1);\n\tbreak;\n\ncase 0xc7:\t\t\t/*  CMP [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xc8:\t\t\t/*  INY */\n\tINC_KPC_1;\n\tSET_INDEX_REG(yreg + 1, yreg);\n\tbreak;\n\ncase 0xc9:\t\t\t/*  CMP #imm */\n\tGET_IMM_MEM();\n\tCMP_INST();\n\tbreak;\n\ncase 0xca:\t\t\t/*  DEX */\n\tINC_KPC_1;\n\tSET_INDEX_REG(xreg - 1, xreg);\n\tbreak;\n\ncase 0xcb:\t\t\t/*  WAI */\n\tif(g_irq_pending) {\n\t\tg_wait_pending = 0;\n\t\tINC_KPC_1;\n\t} else {\n\t\tg_wait_pending = 1;\n\t}\n\tbreak;\n\ncase 0xcc:\t\t\t/*  CPY abs */\n\tC_CPY_ABS();\n\tbreak;\n\ncase 0xcd:\t\t\t/*  CMP abs */\n\tGET_ABS_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xce:\t\t\t/*  DEC abs */\n\tGET_ABS_RD_RMW();\n\tDEC_INST(0);\n\tbreak;\n\ncase 0xcf:\t\t\t/*  CMP long */\n\tGET_LONG_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xd0:\t\t\t/*  BNE disp8 */\n\tBRANCH_DISP8(zero != 0);\n\tbreak;\n\ncase 0xd1:\t\t\t/*  CMP (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xd2:\t\t\t/*  CMP (Dloc) */\n\tGET_DLOC_IND_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xd3:\t\t\t/*  CMP (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xd4:\t\t\t/*  PEI Dloc */\n\tGET_DLOC_ADDR()\n\tGET_MEMORY16(arg, arg, 1);\n\tCYCLES_PLUS_1;\n\tPUSH16_UNSAFE(arg);\n\tbreak;\n\ncase 0xd5:\t\t\t/*  CMP Dloc,x */\n\tGET_DLOC_X_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xd6:\t\t\t/*  DEC Dloc,x */\n\tGET_DLOC_X_RD_RMW();\n\tDEC_INST(1);\n\tbreak;\n\ncase 0xd7:\t\t\t/*  CMP [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xd8:\t\t\t/*  CLD */\n\tpsr = psr & (~0x8);\n\tINC_KPC_1;\n\tbreak;\n\ncase 0xd9:\t\t\t/*  CMP abs,y */\n\tGET_ABS_Y_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xda:\t\t\t/*  PHX */\n\tINC_KPC_1;\n\tif(psr & 0x10) {\n\t\tPUSH8(xreg);\n\t} else {\n\t\tPUSH16(xreg);\n\t}\n\tbreak;\n\ncase 0xdb:\t\t\t/*  STP */\n\tFINISH(RET_STP, 0);\n\tbreak;\n\ncase 0xdc:\t\t\t/*  JML (Abs) */\n\tGET_2BYTE_ARG;\n\tCYCLES_PLUS_1;\n\tGET_MEMORY24(arg, kpc, 1);\n\tbreak;\n\ncase 0xdd:\t\t\t/*  CMP Abs,X */\n\tGET_ABS_X_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xde:\t\t\t/*  DEC Abs,X */\n\tGET_ABS_X_RD_RMW();\n\tDEC_INST(0);\n\tbreak;\n\ncase 0xdf:\t\t\t/*  CMP Long,X */\n\tGET_LONG_X_RD();\n\tCMP_INST();\n\tbreak;\n\ncase 0xe0:\t\t\t/*  CPX #imm */\n\tC_CPX_IMM();\n\tbreak;\n\ncase 0xe1:\t\t\t/*  SBC (Dloc,X) */\n\tGET_DLOC_X_IND_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xe2:\t\t\t/*  SEP #imm */\n\tGET_1BYTE_ARG;\n\ttmp2 = psr;\n\tCYCLES_PLUS_1;\n\tINC_KPC_2;\n\tpsr = (psr & ~0x82) | (neg7 & 0x80) | ((!zero) << 1);\n\tpsr = psr | (arg & 0xff);\n\tzero = !(psr & 2);\n\tneg7 = psr;\n\tUPDATE_PSR(psr, tmp2);\n\tbreak;\n\ncase 0xe3:\t\t\t/*  SBC Disp8,S */\n\tGET_DISP8_S_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xe4:\t\t\t/*  CPX Dloc */\n\tC_CPX_DLOC();\n\tbreak;\n\ncase 0xe5:\t\t\t/*  SBC Dloc */\n\tGET_DLOC_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xe6:\t\t\t/*  INC Dloc */\n\tGET_DLOC_RD_RMW();\n\tINC_INST(1);\n\tbreak;\n\ncase 0xe7:\t\t\t/*  SBC [Dloc] */\n\tGET_DLOC_L_IND_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xe8:\t\t\t/*  INX */\n\tINC_KPC_1;\n\tSET_INDEX_REG(xreg + 1, xreg);\n\tbreak;\n\ncase 0xe9:\t\t\t/*  SBC #imm */\n\tGET_IMM_MEM();\n\tSBC_INST();\n\tbreak;\n\ncase 0xea:\t\t\t/*  NOP */\n\tINC_KPC_1;\n\tbreak;\n\ncase 0xeb:\t\t\t/*  XBA */\n\ttmp1 = acc & 0xff;\n\tCYCLES_PLUS_1\n\tacc = (tmp1 << 8) + (acc >> 8);\n\tINC_KPC_1;\n\tSET_NEG_ZERO8(acc & 0xff);\n\tbreak;\n\ncase 0xec:\t\t\t/*  CPX abs */\n\tC_CPX_ABS();\n\tbreak;\n\ncase 0xed:\t\t\t/*  SBC abs */\n\tGET_ABS_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xee:\t\t\t/*  INC abs */\n\tGET_ABS_RD_RMW();\n\tINC_INST(0);\n\tbreak;\n\ncase 0xef:\t\t\t/*  SBC long */\n\tGET_LONG_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xf0:\t\t\t/*  BEQ disp8 */\n\tBRANCH_DISP8(zero == 0);\n\tbreak;\n\ncase 0xf1:\t\t\t/*  SBC (Dloc),y */\n\tGET_DLOC_IND_Y_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xf2:\t\t\t/*  SBC (Dloc) */\n\tGET_DLOC_IND_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xf3:\t\t\t/*  SBC (Disp8,s),y */\n\tGET_DISP8_S_IND_Y_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xf4:\t\t\t/*  PEA Abs */\n\tGET_2BYTE_ARG;\n\tCYCLES_PLUS_1;\n\tINC_KPC_3;\n\tPUSH16_UNSAFE(arg);\n\tbreak;\n\ncase 0xf5:\t\t\t/*  SBC Dloc,x */\n\tGET_DLOC_X_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xf6:\t\t\t/*  INC Dloc,x */\n\tGET_DLOC_X_RD_RMW();\n\tINC_INST(1);\n\tbreak;\n\ncase 0xf7:\t\t\t/*  SBC [Dloc],Y */\n\tGET_DLOC_L_IND_Y_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xf8:\t\t\t/*  SED */\n\tINC_KPC_1;\n\tpsr |= 0x8;\n\tbreak;\n\ncase 0xf9:\t\t\t/*  SBC abs,y */\n\tGET_ABS_Y_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xfa:\t\t\t/*  PLX */\n\tINC_KPC_1;\n\tCYCLES_PLUS_1;\n\tif(psr & 0x10) {\n\t\tPULL8(xreg);\n\t\tSET_NEG_ZERO8(xreg);\n\t} else {\n\t\tPULL16(xreg);\n\t\tSET_NEG_ZERO16(xreg);\n\t}\n\tbreak;\n\ncase 0xfb:\t\t\t/*  XCE */\n\ttmp2 = psr;\n\tINC_KPC_1;\n\tpsr = (tmp2 & 0xfe) | ((tmp2 & 1) << 8) | ((tmp2 >> 8) & 1);\n\tUPDATE_PSR(psr, tmp2);\n\tbreak;\n\ncase 0xfc:\t\t\t/*  JSR (Abs,X) */\n\tGET_2BYTE_ARG;\n\tINC_KPC_2;\n\ttmp1 = kpc;\n\targ = (kpc & 0xff0000) + ((arg + xreg) & 0xffff);\n\tGET_MEMORY16(arg, tmp2, 1);\n\tkpc = (kpc & 0xff0000) + tmp2;\n\tCYCLES_PLUS_2\n\tPUSH16_UNSAFE(tmp1);\n\tbreak;\n\ncase 0xfd:\t\t\t/*  SBC Abs,X */\n\tGET_ABS_X_RD();\n\tSBC_INST();\n\tbreak;\n\ncase 0xfe:\t\t\t/*  INC Abs,X */\n\tGET_ABS_X_RD_RMW();\n\tINC_INST(0);\n\tbreak;\n\ncase 0xff:\t\t\t/*  SBC Long,X */\n\tGET_LONG_X_RD();\n\tSBC_INST();\n\tbreak;\n\n"
  },
  {
    "path": "upstream/kegs/src/iwm.c",
    "content": "const char rcsid_iwm_c[] = \"@(#)$KmKId: iwm.c,v 1.207 2023-09-26 02:59:52+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// Information from Beneath Apple DOS, Apple IIgs HW reference, Apple IIgs\n//  Firmware reference, and Inside Macintosh Vol 3 (for status35 info).\n// Neil Parker wrote about the Apple 3.5 drive using IWM at\n//  http://nparker.llx.com/a2/disk and it is very useful.\n//  http://nparker.llx.com/a2/sethook lists hooks for IWM routines, from\n//   $e1/0c00 - 0fab.\n// ff/5fa4 is STAT35.  ff/5fae is CONT35\n// When testing DOS3.3, set bp at $b942, which is bad sector detected\n// IWM mode register: 7:reserved; 6:5: 0; 4: 1=8MHz,0=7MHz (should always be 0);\n//\t3: bit-cells are 2usec, 2: 1sec timer off; 1: async handshake (writes)\n//\t0: latch mode enabled for reads (holds data for 8 bit times)\n// dsk->fd >= 0 indicates a valid disk.  dsk->raw_data != 0 indicates this is\n//  a compressed disk mounted read-only, and ignore dsk->fd (which will always\n//  be 0, to indicate a valid disk).\n// fbit_pos encodes head position on track in increments of 1/64 usecs for 5.25\"\n//  { byte_offset, bit[2:0], sub_bit[8:0] }, so fbit_pos >> 12 gives byte offset\n// fbit_mult turns dfcyc into fbit_pos, where (512/fbit_mult) = the bit cell\n//  5.25\" bit cell of 4usec has fbit_mult=128; cell of 3.75usec has mult=136.\n// For 3.5\", fbit_mult=256 indicates a 2usec bit cell.\n// https://support.apple.com/kb/TA39910?locale=en_US&viewlocale=en_US gives\n//  the RPMs of 800KB disks\n\n#include \"defc.h\"\n\nint g_halt_arm_move = 0;\n\nextern int Verbose;\nextern word32 g_vbl_count;\nextern word32 g_c036_val_speed;\nextern int g_config_kegs_update_needed;\nextern Engine_reg engine;\n\nconst byte phys_to_dos_sec[] = {\n\t0x00, 0x07, 0x0e, 0x06,  0x0d, 0x05, 0x0c, 0x04,\n\t0x0b, 0x03, 0x0a, 0x02,  0x09, 0x01, 0x08, 0x0f\n};\n\nconst byte phys_to_prodos_sec[] = {\n\t0x00, 0x08, 0x01, 0x09,  0x02, 0x0a, 0x03, 0x0b,\n\t0x04, 0x0c, 0x05, 0x0d,  0x06, 0x0e, 0x07, 0x0f\n};\n\n\nconst byte to_disk_byte[] = {\n\t0x96, 0x97, 0x9a, 0x9b,  0x9d, 0x9e, 0x9f, 0xa6,\n\t0xa7, 0xab, 0xac, 0xad,  0xae, 0xaf, 0xb2, 0xb3,\n/* 0x10 */\n\t0xb4, 0xb5, 0xb6, 0xb7,  0xb9, 0xba, 0xbb, 0xbc,\n\t0xbd, 0xbe, 0xbf, 0xcb,  0xcd, 0xce, 0xcf, 0xd3,\n/* 0x20 */\n\t0xd6, 0xd7, 0xd9, 0xda,  0xdb, 0xdc, 0xdd, 0xde,\n\t0xdf, 0xe5, 0xe6, 0xe7,  0xe9, 0xea, 0xeb, 0xec,\n/* 0x30 */\n\t0xed, 0xee, 0xef, 0xf2,  0xf3, 0xf4, 0xf5, 0xf6,\n\t0xf7, 0xf9, 0xfa, 0xfb,  0xfc, 0xfd, 0xfe, 0xff\n};\n\nint\tg_track_bytes_35[] = {\n\t0x200*12,\n\t0x200*11,\n\t0x200*10,\n\t0x200*9,\n\t0x200*8\n};\n\nint\tg_track_bits_35[] = {\n\t// Do 1.001 * (1020484 * (60/rpm)) / 2 usec = bits per track\n\t77779,\t\t\t// Trks 0-15: 394 rpm\n\t71433,\t\t\t// Trks 16-31: 429 rpm\n\t64926,\t\t\t// Trks 32-47: 472 rpm\n\t58371,\t\t\t// Trks 48-63: 525 rpm\n\t51940\t\t\t// Trks 64-79: 590 rpm\n};\n\nint\tg_fast_disk_emul_en = 1;\nint\tg_fast_disk_emul = 0;\nint\tg_slow_525_emul_wr = 0;\ndword64\tg_dfcyc_end_emul_wr = 0;\nint\tg_fast_disk_unnib = 0;\nint\tg_iwm_fake_fast = 0;\n\nword32\tg_from_disk_byte[256];\nint\tg_from_disk_byte_valid = 0;\n\nIwm\tg_iwm;\n\nint\tg_iwm_motor_on = 0;\n// g_iwm_motor_on is set when drive turned on, 0 when $c0e8 is touched.\n//  Use this to throttle speed to 1MHz.\n// g_iwm.motor_on=1 means drive is spinning.  g_iwm.motor_off=1 means we're\n//  in the 1-second countdown of g_iwm.motor_off_vbl_count.\n\nint\tg_check_nibblization = 1;\n\nvoid\niwm_init_drive(Disk *dsk, int smartport, int drive, int disk_525)\n{\n\tint\tnum_tracks;\n\tint\ti;\n\n\tdsk->dfcyc_last_read = 0;\n\tdsk->raw_data = 0;\n\tdsk->wozinfo_ptr = 0;\n\tdsk->dynapro_info_ptr = 0;\n\tdsk->name_ptr = 0;\n\tdsk->partition_name = 0;\n\tdsk->partition_num = -1;\n\tdsk->fd = -1;\n\tdsk->dynapro_blocks = 0;\n\tdsk->raw_dsize = 0;\n\tdsk->dimage_start = 0;\n\tdsk->dimage_size = 0;\n\tdsk->smartport = smartport;\n\tdsk->disk_525 = disk_525;\n\tdsk->drive = drive;\n\tdsk->cur_frac_track = 0;\n\tdsk->image_type = 0;\n\tdsk->vol_num = 254;\n\tdsk->write_prot = 1;\n\tdsk->write_through_to_unix = 0;\n\tdsk->disk_dirty = 0;\n\tdsk->just_ejected = 0;\n\tdsk->last_phases = 0;\n\tdsk->cur_fbit_pos = 0;\n\tdsk->fbit_mult = 128;\t\t\t// 128 for 5.25\", 256 for 3.5\"\n\tdsk->cur_track_bits = 0;\n\tdsk->raw_bptr_malloc = 0;\n\tdsk->cur_trk_ptr = 0;\n\tdsk->num_tracks = 0;\n\tdsk->trks = 0;\n\tif(!smartport) {\n\t\t// 3.5\" and 5.25\" drives.  This is only called at init time,\n\t\t//  so one-time malloc can be done now\n\t\tnum_tracks = 2*80;\t\t// 3.5\" needs 160\n\t\tdsk->trks = (Trk *)malloc(num_tracks * sizeof(Trk));\n\n\t\tfor(i = 0; i < num_tracks; i++) {\n\t\t\tdsk->trks[i].raw_bptr = 0;\n\t\t\tdsk->trks[i].sync_ptr = 0;\n\t\t\tdsk->trks[i].dunix_pos = 0;\n\t\t\tdsk->trks[i].unix_len = 0;\n\t\t\tdsk->trks[i].dirty = 0;\n\t\t\tdsk->trks[i].track_bits = 0;\n\t\t}\n\t\t// num_tracks != 0 indicates a valid disk, do not set it here\n\t}\n}\n\nvoid\niwm_init()\n{\n\tword32\tval;\n\tint\ti;\n\n\tmemset(&g_iwm, 0, sizeof(g_iwm));\n\n\tfor(i = 0; i < 2; i++) {\n\t\tiwm_init_drive(&(g_iwm.drive525[i]), 0, i, 1);\n\t\tiwm_init_drive(&(g_iwm.drive35[i]), 0, i, 0);\n\t}\n\n\tfor(i = 0; i < MAX_C7_DISKS; i++) {\n\t\tiwm_init_drive(&(g_iwm.smartport[i]), 1, i, 0);\n\t}\n\n\tif(g_from_disk_byte_valid == 0) {\n\t\tfor(i = 0; i < 256; i++) {\n\t\t\tg_from_disk_byte[i] = 0x100 + i;\n\t\t}\n\t\tfor(i = 0; i < 64; i++) {\n\t\t\tval = to_disk_byte[i];\n\t\t\tg_from_disk_byte[val] = i;\n\t\t}\n\t\tg_from_disk_byte_valid = 1;\n\t} else {\n\t\thalt_printf(\"iwm_init called twice!\\n\");\n\t}\n}\n\nvoid\niwm_reset()\n{\n\tint\ti;\n\n\tg_iwm.state = 0;\n\tg_iwm.motor_off_vbl_count = 0;\n\tg_iwm.forced_sync_bit = 32*12345;\n\tg_iwm.last_rd_bit = 32*12345;\n\tg_iwm.write_val = 0;\n\tfor(i = 0; i < 5; i++) {\n\t\tg_iwm.wr_last_bit[i] = 0;\n\t\tg_iwm.wr_qtr_track[i] = 0;\n\t\tg_iwm.wr_num_bits[i] = 0;\n\t\tg_iwm.wr_prior_num_bits[i] = 0;\n\t\tg_iwm.wr_delta[i] = 0;\n\t}\n\tg_iwm.num_active_writes = 0;\n\n\tg_iwm_motor_on = 0;\n}\n\nvoid\ndisk_set_num_tracks(Disk *dsk, int num_tracks)\n{\n\tif(num_tracks <= 2*80) {\n\t\tdsk->num_tracks = num_tracks;\n\t} else {\n\t\thalt_printf(\"num_tracks out of range: %d\\n\", num_tracks);\n\t}\n}\n\nword32\niwm_get_default_track_bits(Disk *dsk, word32 qtr_trk)\n{\n\tword32\ttrack_bits, extra;\n\n\textra = (qtr_trk + (qtr_trk >> 5)) & 0xf;\n\t\t// up to 15 extra bits\n\tif(dsk->disk_525) {\n\t\ttrack_bits = NIB_LEN_525 * 8;\t\t// 0x18f2 = 51088\n\t} else {\n\t\ttrack_bits = g_track_bits_35[qtr_trk >> 5];\n\t}\n\treturn track_bits + extra;\n}\n\nvoid\ndraw_iwm_status(int line, char *buf)\n{\n\tchar\t*flag[2][2];\n\tint\tapple35_sel, drive_select;\n\n\tflag[0][0] = \" \";\n\tflag[0][1] = \" \";\n\tflag[1][0] = \" \";\n\tflag[1][1] = \" \";\n\n\tapple35_sel = (g_iwm.state >> IWM_BIT_C031_APPLE35SEL) & 1;\n\tdrive_select = (g_iwm.state >> IWM_BIT_DRIVE_SEL) & 1;\n\tif(g_iwm.state & IWM_STATE_MOTOR_ON) {\n\t\tflag[apple35_sel][drive_select] = \"*\";\n\t}\n\n\tsprintf(buf, \"s6d1:%2d%s   s6d2:%2d%s   s5d1:%2d/%d%s   \"\n\t\t\"s5d2:%2d/%d%s fast_disk_emul:%d,%d c036:%02x\",\n\t\tg_iwm.drive525[0].cur_frac_track >> 18, flag[0][0],\n\t\tg_iwm.drive525[1].cur_frac_track >> 18, flag[0][1],\n\t\tg_iwm.drive35[0].cur_frac_track >> 17,\n\t\t(g_iwm.drive35[0].cur_frac_track >> 16) & 1, flag[1][0],\n\t\tg_iwm.drive35[1].cur_frac_track >> 17,\n\t\t(g_iwm.drive35[1].cur_frac_track >> 16) & 1, flag[1][1],\n\t\tg_fast_disk_emul, g_slow_525_emul_wr, g_c036_val_speed);\n\n\tvideo_update_status_line(line, buf);\n}\n\nvoid\niwm_flush_cur_disk()\n{\n\tDisk\t*dsk;\n\n\tdsk = iwm_get_dsk(g_iwm.state);\n\tiwm_flush_disk_to_unix(dsk);\n}\n\nvoid\niwm_flush_disk_to_unix(Disk *dsk)\n{\n\tbyte\tbuffer[0x4000];\n\tint\tret, did_write;\n\tint\ti;\n\n\tif((dsk->disk_dirty == 0) || (dsk->write_through_to_unix == 0)) {\n\t\treturn;\n\t}\n\tif((dsk->raw_data != 0) && !dsk->dynapro_info_ptr) {\n\t\treturn;\n\t}\n\n\tprintf(\"Writing disk %s to Unix\\n\", dsk->name_ptr);\n\n\tfor(i = 0; i < 160; i++) {\n\t\tret = iwm_track_to_unix(dsk, i, &(buffer[0]));\n\n\t\tif((ret != 1) && (ret != 0)) {\n\t\t\tprintf(\"iwm_flush_disk_to_unix ret: %d, cannot write \"\n\t\t\t\t\"image to unix, qtrk:%04x\\n\", ret, i);\n\t\t\thalt_printf(\"Converting to WOZ format!\\n\");\n\t\t\twoz_reparse_woz(dsk);\n\t\t\treturn;\t\t// Don't clear dsk->disk_dirty\n\t\t}\n\t\tif(ret == 1) {\n\t\t\tdid_write = 1;\n\t\t}\n\t}\n\tif(dsk->wozinfo_ptr && did_write) {\n\t\twoz_check_file(dsk);\n\t}\n\n\tdsk->disk_dirty = 0;\n}\n\n/* Check for dirty disk 3 times a second */\n\nword32\tg_iwm_dynapro_last_vbl_count = 0;\n\nvoid\niwm_vbl_update()\n{\n\tword32\tstate;\n\tint\ti;\n\n\tstate = g_iwm.state;\n\tif((state & IWM_STATE_MOTOR_ON) && (state & IWM_STATE_MOTOR_OFF)) {\n\t\tif(g_iwm.motor_off_vbl_count <= g_vbl_count) {\n\t\t\tprintf(\"Disk timer expired, drive off: %08x\\n\",\n\t\t\t\tg_vbl_count);\n\t\t\tiwm_flush_cur_disk();\n\t\t\tg_iwm.state = state &\n\t\t\t\t(~(IWM_STATE_MOTOR_ON | IWM_STATE_MOTOR_OFF));\n\t\t\tg_iwm.drive525[0].dfcyc_last_phases = 0;\n\t\t\tg_iwm.drive525[1].dfcyc_last_phases = 0;\n\t\t}\n\t}\n\tif((g_vbl_count - g_iwm_dynapro_last_vbl_count) >= 60) {\n\t\tfor(i = 0; i < 2; i++) {\n\t\t\tdynapro_try_fix_damaged_disk(&(g_iwm.drive525[i]));\n\t\t\tdynapro_try_fix_damaged_disk(&(g_iwm.drive35[i]));\n\t\t}\n\t\tfor(i = 0; i < MAX_C7_DISKS; i++) {\n\t\t\tdynapro_try_fix_damaged_disk(&(g_iwm.smartport[i]));\n\t\t}\n\t\tg_iwm_dynapro_last_vbl_count = g_vbl_count;\n\t}\n}\n\nvoid\niwm_update_fast_disk_emul(int fast_disk_emul_en)\n{\n\tif(g_iwm_motor_on == 0) {\n\t\tg_fast_disk_emul = fast_disk_emul_en;\n\t}\n}\n\nvoid\niwm_show_stats(int slot_drive)\n{\n\tTrk\t*trkptr;\n\tDisk\t*dsk;\n\tint\tslot, drive;\n\tint\ti;\n\n\tdbg_printf(\"IWM state: %07x (slot_drive:%d)\\n\", g_iwm.state,\n\t\t\t\t\t\t\t\tslot_drive);\n\tdbg_printf(\"g_iwm.drive525[0].fd: %d, [1].fd: %d\\n\",\n\t\tg_iwm.drive525[0].fd, g_iwm.drive525[1].fd);\n\tdbg_printf(\"g_iwm.drive525[0].last_phases: %d, [1].last_phases: %d\\n\",\n\t\tg_iwm.drive525[0].last_phases, g_iwm.drive525[1].last_phases);\n\tdbg_printf(\"g_iwm.write_val:%02x, wr_num_bits[0]:%d, last_rd_bit:\"\n\t\t\"%06x, forced_sync_bit:%06x\\n\", g_iwm.write_val,\n\t\tg_iwm.wr_num_bits[0], g_iwm.last_rd_bit,\n\t\tg_iwm.forced_sync_bit);\n\tif(slot_drive < 0) {\n\t\treturn;\n\t}\n\tdrive = slot_drive & 1;\n\tslot = 5 + ((slot_drive >> 1) & 1);\n\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\tif(dsk->trks == 0) {\n\t\treturn;\n\t}\n\tfor(i = 0; i < 160; i++) {\n\t\ttrkptr = &(dsk->trks[i]);\n\t\tprintf(\"Qtrk:%02x: bits:%05x dunix_pos:%08llx unix_len:%06x, \"\n\t\t\t\"d:%d %p,%p\\n\", i, trkptr->track_bits,\n\t\t\ttrkptr->dunix_pos, trkptr->unix_len, trkptr->dirty,\n\t\t\ttrkptr->raw_bptr, trkptr->sync_ptr);\n\t}\n}\n\nDisk *\niwm_get_dsk(word32 state)\n{\n\tDisk\t*dsk;\n\tint\tdrive;\n\n\tdrive = (state >> IWM_BIT_DRIVE_SEL) & 1;\n\tif(state & IWM_STATE_C031_APPLE35SEL) {\n\t\tdsk = &(g_iwm.drive35[drive]);\n\t} else {\n\t\tdsk = &(g_iwm.drive525[drive]);\n\t}\n\n\treturn dsk;\n}\n\nDisk *\niwm_touch_switches(int loc, dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tdword64\tdadd, dcycs_passed;\n\tword32\ttrack_bits, fbit_pos, forced_sync_bit, mask, mask_on, state;\n\tint\tphase, on, motor_on;\n\n\tif(g_iwm.state & IWM_STATE_RESET) {\n\t\tiwm_printf(\"IWM under reset: %06x\\n\", g_iwm.state);\n\t}\n\n\tdsk = iwm_get_dsk(g_iwm.state);\n\n\ttrack_bits = dsk->cur_track_bits;\n\tfbit_pos = track_bits * 4096;\t\t// Set to an illegal value\n\tmotor_on = g_iwm.state & IWM_STATE_MOTOR_ON;\n\tif(motor_on) {\n\t\tdcycs_passed = (dfcyc - dsk->dfcyc_last_read) >> 16;\n\t\tdsk->dfcyc_last_read = dfcyc;\n\n\t\t// track_bits can be 0, indicating no valid data\n\t\tdadd = dcycs_passed * dsk->fbit_mult;\n\t\tif(track_bits) {\n\t\t\tif(dadd >= (track_bits * 512)) {\n\t\t\t\tdadd = dadd % (track_bits * 512);\n\t\t\t}\n\t\t}\n\t\tfbit_pos = dsk->cur_fbit_pos + (word32)dadd;\n\t\tif(fbit_pos >= (track_bits * 512)) {\n\t\t\tfbit_pos -= (track_bits * 512);\n\t\t}\n\t\tif(g_slow_525_emul_wr || (g_iwm.state & IWM_STATE_Q7) ||\n\t\t\t\t\t!g_fast_disk_emul || !track_bits) {\n\t\t\tdsk->cur_fbit_pos = fbit_pos;\n\t\t} else {\n\t\t\tfbit_pos = track_bits << 12;\t// out of range value\n\t\t}\n#if 0\n\t\tprintf(\"dsk->cur_fbit:%08x, fbit_pos:%08x\\n\",\n\t\t\t\t\tdsk->cur_fbit_pos, fbit_pos);\n#endif\n\t}\n\n\tdbg_log_info(dfcyc, dsk->cur_fbit_pos,\n\t\t((loc & 0xff) << 24) | g_iwm.state,\n\t\t((dsk->cur_frac_track + 0x8000) & 0x1ff0000) | 0xe0);\n\n\tif(loc < 0) {\n\t\t// Not a real access, just a way to update fbit_pos\n\t\treturn dsk;\n\t}\n\n\tif(dsk->dfcyc_last_phases) {\n\t\tiwm525_update_head(dsk, dfcyc);\n\t}\n\n\ton = loc & 1;\n\tphase = loc >> 1;\n\tstate = g_iwm.state;\n\tif(loc < 8) {\n\t\t/* phase adjustments.  See if motor is on */\n\n\t\tmask = (1 << (phase & 3)) << IWM_BIT_PHASES;\n\t\tmask_on = (on << (phase & 3)) << IWM_BIT_PHASES;\n\t\tstate = (state & (~mask)) | mask_on;\n\t\tg_iwm.state = state;\n\t\tiwm_printf(\"Iwm phase %d=%d, all phases: %06x (%016llx)\\n\",\n\t\t\tphase, on, state, dfcyc);\n\n\t\tif(state & IWM_STATE_MOTOR_ON) {\n\t\t\tif(state & IWM_STATE_C031_APPLE35SEL) {\n\t\t\t\tif((phase == 3) && on) {\n\t\t\t\t\tiwm_do_action35(dfcyc);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Move apple525 head\n\t\t\t\tiwm525_update_phases(dsk, dfcyc);\n\t\t\t}\n\t\t}\n\t\tstate = g_iwm.state;\n\t\t/* See if enable or reset is asserted */\n\t\tif(((state >> IWM_BIT_PHASES) & 5) == 5) {\t// Ph 0, 2 set\n\t\t\tstate |= IWM_STATE_RESET;\n\t\t\tiwm_printf(\"IWM reset active\\n\");\n\t\t} else {\n\t\t\tstate &= (~IWM_STATE_RESET);\n\t\t}\n\t\tif(((state >> IWM_BIT_PHASES) & 0xa) == 0xa) {\t// Ph 1, 3 set\n\t\t\tstate |= IWM_STATE_ENABLE2;\n\t\t\tiwm_printf(\"IWM ENABLE2 active\\n\");\n\t\t} else {\n\t\t\tstate &= (~IWM_STATE_ENABLE2);\n\t\t}\n\t\tg_iwm.state = state;\n\t} else {\n\t\t/* loc >= 8 */\n\t\tswitch(loc) {\n\t\tcase 0x8:\n\t\t\tiwm_printf(\"Turning IWM motor off!\\n\");\n\t\t\tif(g_iwm.state & 0x04) {\t\t// iwm MODE\n\t\t\t\t/* Turn off immediately */\n\t\t\t\tstate &= (~(IWM_STATE_MOTOR_ON |\n\t\t\t\t\t\t\tIWM_STATE_MOTOR_OFF));\n\t\t\t} else {\n\t\t\t\t/* 1 second delay */\n\t\t\t\tif((state & IWM_STATE_MOTOR_ON) &&\n\t\t\t\t\t\t!(state & IWM_STATE_MOTOR_OFF)){\n\t\t\t\t\tstate |= IWM_STATE_MOTOR_OFF;\n\t\t\t\t\tg_iwm.motor_off_vbl_count = g_vbl_count\n\t\t\t\t\t\t\t\t\t+ 60;\n\t\t\t\t}\n\t\t\t}\n\t\t\tg_iwm.state = state;\n\n#if 0\n\t\t\tprintf(\"IWM motor off, g_iwm_motor_on:%d, slow:%d\\n\",\n\t\t\t\tg_iwm_motor_on, g_slow_525_emul_wr);\n#endif\n\t\t\tif(g_iwm_motor_on || g_slow_525_emul_wr) {\n\t\t\t\t/* recalc current speed */\n\t\t\t\tg_fast_disk_emul = g_fast_disk_emul_en;\n\t\t\t\tengine_recalc_events();\n\t\t\t\tiwm_flush_cur_disk();\n\t\t\t}\n\n\t\t\tg_iwm_motor_on = 0;\n\t\t\tg_slow_525_emul_wr = 0;\n\t\t\tbreak;\n\t\tcase 0x9:\n\t\t\tiwm_printf(\"Turning IWM motor on!\\n\");\n\t\t\tstate |= IWM_STATE_MOTOR_ON;\n\t\t\tstate &= (~IWM_STATE_MOTOR_OFF);\n\t\t\tstate &= (~IWM_STATE_LAST_SEL35);\n\t\t\tstate |= ((state >> 6) & 1) << IWM_BIT_LAST_SEL35;\n\t\t\tg_iwm.state = state;\n\t\t\tdsk->dfcyc_last_read = dfcyc;\n\n\t\t\tif(g_iwm_motor_on == 0) {\n\t\t\t\t/* recalc current speed */\n\t\t\t\tif(dsk->wozinfo_ptr) {\n\t\t\t\t\tg_fast_disk_emul = 0;\n\t\t\t\t}\n\t\t\t\tengine_recalc_events();\n\t\t\t}\n\t\t\tg_iwm_motor_on = 1;\n\n\t\t\tbreak;\n\t\tcase 0xa:\n\t\tcase 0xb:\n\t\t\tif(((state >> IWM_BIT_DRIVE_SEL) & 1) != on) {\n\t\t\t\tiwm_flush_cur_disk();\n\t\t\t}\n\t\t\tstate &= (~IWM_STATE_DRIVE_SEL);\n\t\t\tstate |= (on << IWM_BIT_DRIVE_SEL);\n\t\t\tg_iwm.state = state;\n\t\t\tdsk = iwm_get_dsk(state);\n\t\t\tif(dsk->wozinfo_ptr && g_iwm_motor_on) {\n\t\t\t\tg_fast_disk_emul = 0;\n\t\t\t} else {\n\t\t\t\tg_fast_disk_emul = g_fast_disk_emul_en;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0xc:\n\t\tcase 0xd:\n\t\t\tmask = IWM_STATE_Q6 | IWM_STATE_Q7 |\n\t\t\t\t\tIWM_STATE_MOTOR_ON | IWM_STATE_ENABLE2;\n\t\t\tmask_on = IWM_STATE_Q6 | IWM_STATE_MOTOR_ON;\n\t\t\tif(((state & mask) == mask_on) && !on) {\n\t\t\t\t// q6 on, q7 off, !on, motor_on, !enable2\n\t\t\t\t// Switched from $c08d to $c08c, resync now\n\t\t\t\t// If latch mode, back up one more bit\n\t\t\t\t//  (for The School Speller - Tools.woz)\n\t\t\t\tforced_sync_bit = iwm_calc_bit_sum(\n\t\t\t\t\tfbit_pos >> 9, 0 - (state & 1),\n\t\t\t\t\ttrack_bits);\n\t\t\t\tg_iwm.forced_sync_bit = forced_sync_bit;\n\t\t\t\tg_iwm.last_rd_bit = forced_sync_bit;\n\t\t\t\tdbg_log_info(dfcyc, forced_sync_bit*2, 0,\n\t\t\t\t\t\t\t\t0x500ec);\n\t\t\t}\n\t\t\tstate &= (~IWM_STATE_Q6);\n\t\t\tstate |= (on << IWM_BIT_Q6);\n\t\t\tg_iwm.state = state;\n\t\t\tbreak;\n\t\tcase 0xe:\n\t\tcase 0xf:\n\t\t\tmask = IWM_STATE_Q7 | IWM_STATE_MOTOR_ON |\n\t\t\t\t\t\t\tIWM_STATE_ENABLE2;\n\t\t\tmask_on = IWM_STATE_Q7 | IWM_STATE_MOTOR_ON;\n\t\t\tif(((state & mask) == mask_on) && !on) {\n\t\t\t\t// q7, !on, motor_on, !enable2\n#if 0\n\t\t\t\tprintf(\"state:%04x and new q7:%d, write_end\\n\",\n\t\t\t\t\tstate, on);\n#endif\n\t\t\t\tdbg_log_info(dfcyc, state, mask, 0xed);\n\t\t\t\tiwm_write_end(dsk, 1, dfcyc);\n\t\t\t\t// printf(\"write end complete, the track:\\n\");\n\t\t\t\t// iwm_check_nibblization(dfcyc);\n\t\t\t}\n\t\t\tstate &= (~IWM_STATE_Q7);\n\t\t\tstate |= (on << IWM_BIT_Q7);\n\t\t\tg_iwm.state = state;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tprintf(\"iwm_touch_switches: loc: %02x unknown!\\n\", loc);\n\t\t\texit(2);\n\t\t}\n\t}\n\n\tif(!(state & IWM_STATE_Q7)) {\n\t\tg_iwm.num_active_writes = 0;\n\t\tif(g_slow_525_emul_wr) {\n\t\t\tg_slow_525_emul_wr = 0;\n\t\t\tengine_recalc_events();\n\t\t}\n\t}\n\n\treturn dsk;\n}\n\nvoid\niwm_move_to_ftrack(Disk *dsk, word32 new_frac_track, int delta, dword64 dfcyc)\n{\n\tTrk\t*trkptr;\n\tword32\ttrack_bits, cur_frac_track, wr_last_bit, write_val;\n\tint\tdisk_525, drive, new_track, cur_track, max_track;\n\tint\tnum_active_writes;\n\n\tif(dsk->smartport) {\n\t\treturn;\n\t}\n\tcur_frac_track = dsk->cur_frac_track;\n\tif(delta != 0) {\n\t\t// 3.5\" move, clear out lower fractional track bits\n\t\tnew_frac_track = new_frac_track & (-0x10000);\n\t\tif((delta < 0) && (new_frac_track < (word32)(-delta))) {\n\t\t\t// Moving down past track 0...stop, but preserve side\n\t\t\tnew_frac_track = cur_frac_track & 0x10000;\n\t\t\tdelta = 0;\n\t\t}\n\t}\n\tnew_frac_track = new_frac_track + delta;\n\n\tdisk_525 = dsk->disk_525;\n#if 0\n\tprintf(\"iwm_move_to_track: %07x, num_tracks:%03x (cur:%07x) %016llx\\n\",\n\t\tnew_frac_track, dsk->num_tracks, dsk->cur_frac_track, dfcyc);\n#endif\n\n\tmax_track = 36*4 - 1;\t\t\t// Go to track 35.75 on 5.25\"\n\tif(dsk->num_tracks >= (max_track + 1)) {\n\t\tmax_track = dsk->num_tracks - 1;\n\t}\n\tif(max_track >= 159) {\n\t\tmax_track = 159;\t\t// Limit to 159 always\n\t}\n\tif(new_frac_track > (word32)(max_track << 16)) {\n\t\tnew_frac_track = max_track << 16;\n\t}\n\tnew_track = (new_frac_track + 0x8000) >> 16;\n\n\tcur_track = (cur_frac_track + 0x8000) >> 16;\n\tnum_active_writes = g_iwm.num_active_writes;\n\twr_last_bit = g_iwm.wr_last_bit[0];\n\twrite_val = g_iwm.write_val;\n\tif(num_active_writes) {\n\t\tiwm_write_end(dsk, 0, dfcyc);\n\t\tprintf(\"moving arm to new_track:%d, write active:%d, bit:%08x, \"\n\t\t\t\"write_val:%02x\\n\", new_track, num_active_writes,\n\t\t\twr_last_bit, write_val);\n\t}\n\tif((cur_track != new_track) || (dsk->cur_trk_ptr == 0)) {\n\t\tdrive = dsk->drive + 1;\n\t\tif(1) {\n\t\t\t// Don't do this printf\n\t\t} else if(disk_525) {\n\t\t\tprintf(\"s6d%d Track: %d.%02d\\n\", drive,\n\t\t\t\tnew_track >> 2, 25* (new_track & 3));\n\t\t} else {\n\t\t\tprintf(\"s5d%d Track: %d Side: %d\\n\", drive,\n\t\t\t\tnew_track >> 1, new_track & 1);\n\t\t}\n\n\t\ttrkptr = &(dsk->trks[new_track]);\n\t\ttrack_bits = trkptr->track_bits;\n\t\tif((track_bits == 0) && disk_525) {\n\t\t\t// Use an adjacent .25 track if it is valid\n\t\t\tif((new_track > 0) && (trkptr[-1].track_bits != 0)) {\n\t\t\t\tnew_track--;\n\t\t\t\t// printf(\"Moved dn to qtrk:%04x\\n\", new_track);\n\t\t\t} else if((new_track < max_track) &&\n\t\t\t\t\t\t(trkptr[1].track_bits != 0)) {\n\t\t\t\tnew_track++;\n\t\t\t\t// printf(\"Moved up to qtrk:%04x\\n\", new_track);\n\t\t\t}\n\t\t}\n\t\tiwm_flush_disk_to_unix(dsk);\n\t\tiwm_move_to_qtr_track(dsk, new_track);\n\t\tif(dfcyc != 0) {\n\t\t\tdbg_log_info(dfcyc, cur_frac_track, new_frac_track,\n\t\t\t\t(g_iwm.state << 16) | 0x00f000e1);\n#if 0\n\t\t\tprintf(\"Just moved from track %08x to %08x %016llx\\n\",\n\t\t\t\tcur_frac_track, new_frac_track, dfcyc);\n#endif\n\t\t}\n\t}\n\tdsk->cur_frac_track = new_frac_track;\n\tif(num_active_writes) {\n\t\tiwm_start_write(dsk, wr_last_bit, write_val, 1);\n\t}\n}\n\nvoid\niwm_move_to_qtr_track(Disk *dsk, word32 qtr_track)\n{\n\tTrk\t*trkptr;\n\tword32\ttrack_bits, fbit_pos;\n\n\ttrkptr = &(dsk->trks[qtr_track]);\n\ttrack_bits = trkptr->track_bits;\n\n\tdsk->cur_trk_ptr = trkptr;\n\tdsk->cur_track_bits = track_bits;\n\tfbit_pos = dsk->cur_fbit_pos;\n\tif(track_bits) {\n\t\t// Moving to a valid track.  Ensure fbit_pos in range\n\t\tfbit_pos = fbit_pos % (track_bits * 512);\n\t}\n\tdsk->cur_fbit_pos = fbit_pos;\n}\n\ndword64 g_iwm_last_phase_dfcyc = 0;\n\nvoid\niwm525_update_phases(Disk *dsk, dword64 dfcyc)\n{\n\tword32\tign_mask;\n\tint\tlast_phases, new_phases, eff_last_phases, eff_new_phases;\n\tint\tmy_phase;\n\n\t// Decide if dsk->last_phases needs to change.\n\tlast_phases = dsk->last_phases;\n\tnew_phases = (g_iwm.state >> IWM_BIT_PHASES) & 0xf;\n\tif(last_phases != new_phases) {\n\t\tiwm_printf(\"Phases changing %02x -> %02x, ftrack:%08x at %lld, \"\n\t\t\t\"diff:%.2fmsec\\n\", last_phases, new_phases,\n\t\t\tdsk->cur_frac_track, dfcyc >> 16,\n\t\t\t((dfcyc - g_iwm_last_phase_dfcyc) >> 16) / 1000.0);\n\t\tg_iwm_last_phase_dfcyc = dfcyc;\n\t}\n\tmy_phase = (dsk->cur_frac_track >> 17) & 3;\n\tign_mask = 1 << ((2 + my_phase) & 3);\n\teff_last_phases = last_phases & (~ign_mask);\n\teff_new_phases = new_phases & (~ign_mask);\n\tif(eff_last_phases == eff_new_phases) {\n\t\tdsk->last_phases = new_phases;\n\t\treturn;\t\t\t\t// Nothing to do\n\t}\n\n\t// Update last_phases\n\tiwm525_update_head(dsk, dfcyc);\n\n\tdsk->last_phases = new_phases;\n\tdsk->dfcyc_last_phases = dfcyc;\n\tdbg_log_info(dfcyc, new_phases, dsk->cur_frac_track, 0x100e1);\n}\n\nvoid\niwm525_update_head(Disk *dsk, dword64 dfcyc)\n{\n\tdouble\tdinc;\n\tdword64\tdiff_dusec, dfcyc_last_phases;\n\tword32\tcur_frac_track, frac_track, sum, num, phases, diff;\n\tint\tnew_qtrk, cur_qtrk, my_phase, prev_phase, grind, inc;\n\tint\ti;\n\n\tcur_frac_track = dsk->cur_frac_track;\n\tmy_phase = (dsk->cur_frac_track >> 17) & 3;\n\n\t// If my_phase is 1, then phase 0 being on should decrement the trk\n\t//  number, and phase 2 being on should increment the trk number\n\tphases = dsk->last_phases & 0xf;\t// one bit for each phase\n\tphases = (phases << 4) | phases;\n\tprev_phase = (my_phase + 4 - 1) & 3;\n\tphases = (phases >> prev_phase) & 0xf;\n\n\t// Now, phases[0] means decrement trk, phases[1] is where we are,\n\t//  phases[2] means increment trk, and phases[3] MIGHT mean increment\n\tsum = 0;\n\tnum = 0;\n\tgrind = 0;\n\tfor(i = 0; i < 4; i++) {\n\t\tif(((phases >> i) & 1) == 0) {\n\t\t\tcontinue;\n\t\t}\n\t\tfrac_track = cur_frac_track & -0x20000;\n\t\tswitch(i) {\n\t\tcase 0:\t\t// Previous phase is on, decrement trk\n\t\t\tif(frac_track >= 0x20000) {\n\t\t\t\tfrac_track -= 0x20000;\n\t\t\t} else {\n\t\t\t\tfrac_track = 0;\n\t\t\t}\n\t\t\tsum += frac_track;\n\t\t\tnum++;\n\t\t\tif(cur_frac_track == 0) {\n\t\t\t\tgrind++;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 1:\t\t// \"our\" phase, pull to aligned track\n\t\t\tsum += frac_track;\n\t\t\tnum++;\n\t\t\tbreak;\n\t\tcase 2:\t\t// Next phase, increment track\n\t\t\tsum += frac_track + 0x20000;\n\t\t\tnum++;\n\t\t\tbreak;\n\t\tcase 3:\t\t// Next next phase: might pull\n\t\t\t// Phase is nominally 2 away...but if cur_frac_track\n\t\t\t//  is just a little less than a new halftrack,\n\t\t\t//  (0xxx1ffff), then it may be within a half track\n\t\t\t//  and we'll let it pull us\n\t\t\tfrac_track += 0x20000;\n\t\t\tif((frac_track - cur_frac_track) < 0x30000) {\n\t\t\t\tsum += frac_track;\n\t\t\t\tnum++;\n\t\t\t}\n\t\t}\n\t}\n\n\tfrac_track = cur_frac_track;\n\tdfcyc_last_phases = dsk->dfcyc_last_phases;\n\tdsk->dfcyc_last_phases = 0;\n\tif(num) {\n\t\tfrac_track = sum / num;\t\t// New desired track\n\t\t// Now see if enough time has elapsed that we got to frac_track\n\t\tdiff_dusec = (dfcyc - dfcyc_last_phases) >> 16;\n\t\tdinc = 0x20000 / 2800.0;\t// 2.8msec to move one phase\n\t\tinc = (int)dinc;\n\t\tif(cur_frac_track >= frac_track) {\n\t\t\tdiff = cur_frac_track - frac_track;\n\t\t\tinc = -inc;\n\t\t} else {\n\t\t\tdiff = frac_track - cur_frac_track;\n\t\t}\n\t\tif(g_fast_disk_emul) {\n\t\t\tdiff = 0;\t\t// Always moved enough\n\t\t}\n\t\t// Add inc*diff_dusec to cur_frac_track, unless we already reach\n\t\tif(diff > (dinc * diff_dusec)) {\n\t\t\t// Enough time has NOT elapsed, so calc where head is\n\t\t\t// Update frac_track to be cur_frac_track + inc * dusec\n\t\t\tfrac_track = cur_frac_track +\n\t\t\t\t\t\t(word32)(inc * diff_dusec);\n\t\t\tdsk->dfcyc_last_phases = dfcyc;\n\t\t}\n\t}\n\n\tnew_qtrk = (frac_track + 0x8000) >> 16;\n\tcur_qtrk = (cur_frac_track + 0x8000) >> 16;\n\tif(new_qtrk != cur_qtrk) {\n\t\tif(g_halt_arm_move) {\n\t\t\thalt_printf(\"Halt on arm move\\n\");\n\t\t\tg_halt_arm_move = 0;\n\t\t}\n\t\tif(grind) {\n\t\t\tprintf(\"GRIND GRIND GRIND\\n\");\n\t\t}\n\n\t\tdbg_log_info(dfcyc, frac_track, dsk->cur_frac_track,\n\t\t\t\t\t\t(phases << 24) | 0x00e1);\n\t\tiwm_move_to_ftrack(dsk, frac_track, 0, dfcyc);\n\n\t\tif(new_qtrk > 2) {\n#if 0\n\t\t\tprintf(\"Moving to qtr track: %04x (trk:%d.%02d), %d, \"\n\t\t\t\t\"%02x, %08x dfcyc:%lld\\n\", new_qtrk,\n\t\t\t\tnew_qtrk >> 2, 25*(new_qtrk & 3), my_phase,\n\t\t\t\tphases, g_iwm.state, dfcyc >> 16);\n#endif\n\t\t}\n\t} else {\n\t\t// On the same qtr_track, but update the fraction\n\t\tdsk->cur_frac_track = frac_track;\n\t}\n\n#if 0\n\t/* sanity check stepping algorithm */\n\tif((qtr_track & 7) == 0) {\n\t\t/* check for just access phase 0 */\n\t\tif(last_phase != 0) {\n\t\t\thalt_printf(\"last_phase: %d!\\n\", last_phase);\n\t\t}\n\t}\n#endif\n}\n\nint\niwm_read_status35(dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tword32\tstate;\n\tint\tdrive, stat35, tmp;\n\n\t// This is usually done by STAT35 at ff/5fa4\n\t//  This code is called on the rising edge of Q6 asserted\n\tstate = g_iwm.state;\n\tdrive = (state >> IWM_BIT_DRIVE_SEL) & 1;\n\tdsk = &(g_iwm.drive35[drive]);\n\n\tif(state & IWM_STATE_MOTOR_ON) {\n\t\t/* Read status: ph[1], ph[0], disk35, ph[2] */\n\t\tstat35 = (((state >> IWM_BIT_PHASES) & 3) << 2) |\n\t\t\t((state >> 6) & 2) |\n\t\t\t(((state >> IWM_BIT_PHASES) >> 2) & 1);\n\n\t\tiwm_printf(\"Iwm status read state: %02x\\n\", state);\n\t\tdbg_log_info(dfcyc, state, stat35, 0xe7);\n\n\t\tswitch(stat35) {\n\t\tcase 0x00:\t/* step direction */\n\t\t\treturn (state >> IWM_BIT_STEP_DIRECTION35) & 1;\n\t\t\tbreak;\n\t\tcase 0x01:\t/* lower head activate */\n\t\t\t/* also return instantaneous data from head */\n\t\t\tiwm_move_to_ftrack(dsk,\n\t\t\t\t(dsk->cur_frac_track & (-0x20000)), 0, dfcyc);\n\t\t\treturn (dsk->cur_fbit_pos >> 15) & 1;\n\t\t\tbreak;\n\t\tcase 0x02:\t/* disk in place */\n\t\t\t/* 1 = no disk, 0 = disk */\n\t\t\tiwm_printf(\"read disk in place, num_tracks: %d\\n\",\n\t\t\t\tdsk->num_tracks);\n\t\t\ttmp = (dsk->num_tracks <= 0);\n\t\t\tdbg_log_info(dfcyc, 0, dsk->num_tracks, 0x100e7);\n\t\t\treturn tmp;\n\t\t\tbreak;\n\t\tcase 0x03:\t/* upper head activate */\n\t\t\t/* also return instantaneous data from head */\n\t\t\tiwm_move_to_ftrack(dsk, dsk->cur_frac_track | 0x10000,\n\t\t\t\t\t0, dfcyc);\n\t\t\treturn (dsk->cur_fbit_pos >> 15) & 1;\n\t\t\tbreak;\n\t\tcase 0x04:\t/* disk is stepping? */\n\t\t\t/* 1 = not stepping, 0 = stepping */\n\t\t\treturn 1;\n\t\t\tbreak;\n\t\tcase 0x05:\t/* Unknown function of ROM 03? */\n\t\t\t/* 1 = or $20 into 0xe1/f24+drive, 0 = don't */\n\t\t\treturn 1;\n\t\t\tbreak;\n\t\tcase 0x06:\t/* disk is locked */\n\t\t\t/* 0 = locked, 1 = unlocked */\n\t\t\treturn (!dsk->write_prot);\n\t\t\tbreak;\n\t\tcase 0x08:\t/* motor on */\n\t\t\t/* 0 = on, 1 = off */\n\t\t\treturn !(state & IWM_STATE_MOTOR_ON35);\n\t\t\tbreak;\n\t\tcase 0x09:\t/* number of sides */\n\t\t\t/* 1 = 2 sides, 0 = 1 side */\n\t\t\treturn 1;\n\t\t\tbreak;\n\t\tcase 0x0a:\t/* at track 0 */\n\t\t\t/* 1 = not at track 0, 0 = there */\n\t\t\ttmp = (dsk->cur_frac_track != 0);\n\t\t\tiwm_printf(\"Read at track0_35: %d\\n\", tmp);\n\t\t\treturn tmp;\n\t\t\tbreak;\n\t\tcase 0x0b:\t/* disk ready??? */\n\t\t\t/* 0 = ready, 1 = not ready? */\n\t\t\ttmp = !(state & IWM_STATE_MOTOR_ON35);\n\t\t\tiwm_printf(\"Read disk ready, ret: %d\\n\", tmp);\n\t\t\treturn tmp;\n\t\t\tbreak;\n\t\tcase 0x0c:\t/* disk switched?? */\n\t\t\t/* 0 = not switched, 1 = switched? */\n\t\t\ttmp = (dsk->just_ejected != 0);\n\t\t\tiwm_printf(\"Read disk switched: %d\\n\", tmp);\n\t\t\treturn tmp;\n\t\t\tbreak;\n\t\tcase 0x0d:\t/* false read when ejecting disk */\n\t\t\treturn 1;\n\t\tcase 0x0e:\t/* tachometer */\n\t\t\thalt_printf(\"Reading tachometer!\\n\");\n\t\t\treturn (dsk->cur_fbit_pos >> 11) & 1;\n\t\t\tbreak;\n\t\tcase 0x0f:\t/* drive installed? */\n\t\t\t/* 0 = drive exists, 1 = no drive */\n\t\t\tif(drive) {\n\t\t\t\t/* pretend no drive 1 */\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\treturn 0;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thalt_printf(\"Read 3.5 status, stat35: %02x\\n\", stat35);\n\t\t\treturn 1;\n\t\t}\n\t} else {\n\t\tiwm_printf(\"Read 3.5 status with drive off!\\n\");\n\t\treturn 1;\n\t}\n}\n\nvoid\niwm_do_action35(dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tword32\tstate;\n\tint\tdrive, stat35;\n\n\t// Actions done by CONT35 routine at ff/5fae\n\t//  This code is called on the rising edge of phase[3] asserted\n\tstate = g_iwm.state;\n\tdrive = (state >> IWM_BIT_DRIVE_SEL) & 1;\n\tdsk = &(g_iwm.drive35[drive]);\n\n\tif(state & IWM_STATE_MOTOR_ON) {\n\t\t/* Perform action */\n\t\t/* stat35: ph[1], ph[0], disk35_ctrl, ph[2] */\n\t\tstat35 = (((state >> IWM_BIT_PHASES) & 3) << 2) |\n\t\t\t((state >> 6) & 2) |\n\t\t\t(((state >> IWM_BIT_PHASES) >> 2) & 1);\n\t\tdbg_log_info(dfcyc, state, stat35, 0xf00e7);\n\n\t\tswitch(stat35) {\n\t\tcase 0x00:\t/* Set step direction inward (higher tracks) */\n\t\t\t/* towards higher tracks, clear STEP_DIRECTION35 */\n\t\t\tstate &= (~IWM_STATE_STEP_DIRECTION35);\n\t\t\tiwm_printf(\"Iwm set step dir35 = 0\\n\");\n\t\t\tbreak;\n\t\tcase 0x01:\t/* Set step direction outward (lower tracks) */\n\t\t\t/* towards lower tracks */\n\t\t\tstate |= IWM_STATE_STEP_DIRECTION35;\n\t\t\tdbg_log_info(dfcyc, state, stat35, 0x300e7);\n\t\t\tiwm_printf(\"Iwm set step dir35 = 1\\n\");\n\t\t\tbreak;\n\t\tcase 0x03:\t/* reset disk-switched flag? */\n\t\t\tiwm_printf(\"Iwm reset disk switch\\n\");\n\t\t\tdsk->just_ejected = 0;\n\t\t\tbreak;\n\t\tcase 0x04:\t/* step disk */\n\t\t\tif(state & IWM_STATE_STEP_DIRECTION35) {\n\t\t\t\tiwm_move_to_ftrack(dsk, dsk->cur_frac_track,\n\t\t\t\t\t-0x20000, dfcyc);\n\t\t\t} else {\n\t\t\t\tiwm_move_to_ftrack(dsk, dsk->cur_frac_track,\n\t\t\t\t\t0x20000, dfcyc);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x08:\t/* turn motor on */\n\t\t\tiwm_printf(\"Iwm set motor_on35 = 1\\n\");\n\t\t\tstate |= IWM_STATE_MOTOR_ON35;\n\t\t\tbreak;\n\t\tcase 0x09:\t/* turn motor off */\n\t\t\tiwm_printf(\"Iwm set motor_on35 = 0\\n\");\n\t\t\tstate &= (~IWM_STATE_MOTOR_ON35);\n\t\t\tbreak;\n\t\tcase 0x0d:\t/* eject disk */\n\t\t\tprintf(\"Action 0x0d, will eject disk\\n\");\n\t\t\tiwm_eject_disk(dsk);\n\t\t\tbreak;\n\t\tcase 0x02:\n\t\tcase 0x07:\n\t\tcase 0x0b: /* hacks to allow AE 1.6MB driver to not crash me */\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thalt_printf(\"Do 3.5 action, state: %02x\\n\", state);\n\t\t\treturn;\n\t\t}\n\t} else {\n\t\thalt_printf(\"Set 3.5 status with drive off!\\n\");\n\t\treturn;\n\t}\n\tg_iwm.state = state;\n\tdbg_log_info(dfcyc, state, stat35, 0x400e7);\n}\n\nint\nread_iwm(int loc, dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tword32\tstatus, bit_diff, bit_pos, state;\n\tint\ton, q7_q6, val;\n\n\tloc = loc & 0xf;\n\ton = loc & 1;\n\n\tdsk = iwm_touch_switches(loc, dfcyc);\n\n\tstate = g_iwm.state;\n\tq7_q6 = (state >> IWM_BIT_Q6) & 3;\n\n\tif(on) {\n\t\t/* odd address, return 0 */\n\t\treturn 0;\n\t} else {\n\t\t/* even address */\n\t\tswitch(q7_q6) {\n\t\tcase 0x00:\t/* q7 = 0, q6 = 0 */\n\t\t\tif(state & IWM_STATE_ENABLE2) {\n\t\t\t\treturn iwm_read_enable2(dfcyc);\n\t\t\t} else {\n\t\t\t\tif(state & IWM_STATE_MOTOR_ON) {\n\t\t\t\t\treturn iwm_read_data(dsk, dfcyc);\n\t\t\t\t} else {\n\t\t\t\t\tiwm_printf(\"read iwm st 0, m off!\\n\");\n/* HACK!!!! */\n\t\t\t\t\treturn 0xff;\n\t\t\t\t\t//return (((int)dfcyc) & 0x7f) + 0x80;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x01:\t/* q7 = 0, q6 = 1 */\n\t\t\t/* read IWM status reg */\n\t\t\tif(state & IWM_STATE_ENABLE2) {\n\t\t\t\tiwm_printf(\"Read status under enable2: 1\\n\");\n\t\t\t\tstatus = 1;\n\t\t\t} else {\n\t\t\t\tif(state & IWM_STATE_C031_APPLE35SEL) {\n\t\t\t\t\tstatus = iwm_read_status35(dfcyc);\n\t\t\t\t} else {\n\t\t\t\t\tstatus = dsk->write_prot;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tval = ((status & 1) << 7) | (state & 0x3f);\n\t\t\t// bit 5 is motor_on, bits[4:0] are iwm_mode\n\t\t\tiwm_printf(\"Read status: %02x\\n\", val);\n\n\t\t\treturn val;\n\t\t\tbreak;\n\t\tcase 0x02:\t/* q7 = 1, q6 = 0 */\n\t\t\t/* read handshake register */\n\t\t\tif(state & IWM_STATE_ENABLE2) {\n\t\t\t\treturn iwm_read_enable2_handshake(dfcyc);\n\t\t\t} else {\n\t\t\t\tstatus = 0xc0;\n\t\t\t\tbit_pos = dsk->cur_fbit_pos >> 9;\n\t\t\t\tbit_diff = iwm_calc_bit_diff(bit_pos,\n\t\t\t\t\t\tg_iwm.wr_last_bit[0],\n\t\t\t\t\t\tdsk->cur_track_bits);\n\t\t\t\tif(bit_diff > 8) {\n\t\t\t\t\tiwm_printf(\"Write underrun!\\n\");\n\t\t\t\t\tstatus = status & 0xbf;\n\t\t\t\t}\n\t\t\t\treturn status;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x03:\t/* q7 = 1, q6 = 1 */\n\t\t\tiwm_printf(\"read iwm q7_q6=3!\\n\");\n\t\t\treturn 0;\n\t\tbreak;\n\t\t}\n\t}\n\thalt_printf(\"Got to end of read_iwm, loc: %02x!\\n\", loc);\n\n\treturn 0;\n}\n\nvoid\nwrite_iwm(int loc, int val, dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tword32\tstate;\n\tint\ton, q7_q6, drive, fast_writes;\n\n\tloc = loc & 0xf;\n\ton = loc & 1;\n\n\tdsk = iwm_touch_switches(loc, dfcyc);\n\n\tstate = g_iwm.state;\n\tq7_q6 = (state >> IWM_BIT_Q6) & 3;\n\tdrive = (state >> IWM_BIT_DRIVE_SEL) & 1;\n\tfast_writes = g_fast_disk_emul;\n\tif(state & IWM_STATE_C031_APPLE35SEL) {\n\t\tdsk = &(g_iwm.drive35[drive]);\n\t} else {\n\t\tdsk = &(g_iwm.drive525[drive]);\n\t\tfast_writes = !g_slow_525_emul_wr && fast_writes;\n\t}\n\n\tif(on) {\n\t\t/* odd address, write something */\n\t\tif(q7_q6 == 3) {\n\t\t\t/* q7, q6 = 1,1 */\n\t\t\tif(state & IWM_STATE_MOTOR_ON) {\n\t\t\t\tif(state & IWM_STATE_ENABLE2) {\n\t\t\t\t\tiwm_write_enable2(val);\n\t\t\t\t} else {\n\t\t\t\t\tiwm_write_data(dsk, val, dfcyc);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t/* write mode register */\n\t\t\t\t// bit 0: latch mode (should set if async hand)\n\t\t\t\t// bit 1: async handshake\n\t\t\t\t// bit 2: immediate motor off (no 1 sec delay)\n\t\t\t\t// bit 3: 2us bit timing\n\t\t\t\t// bit 4: Divide input clock by 8 (instead of 7)\n\t\t\t\tval = val & 0x1f;\n\t\t\t\tstate = (state & (~0x1f)) | val;\n\t\t\t\tg_iwm.state = state;\n\t\t\t\tif(val & 0x10) {\n\t\t\t\t\tiwm_printf(\"set iwm_mode:%02x!\\n\",val);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif(state & IWM_STATE_ENABLE2) {\n\t\t\t\tiwm_write_enable2(val);\n\t\t\t} else {\n#if 0\n// Flobynoid writes to 0xc0e9 causing these messages...\n\t\t\t\tprintf(\"Write iwm1, st: %02x, loc: %x: %02x\\n\",\n\t\t\t\t\tq7_q6, loc, val);\n#endif\n\t\t\t}\n\t\t}\n\t\treturn;\n\t} else {\n\t\t/* even address */\n\t\tif(state & IWM_STATE_ENABLE2) {\n\t\t\tiwm_write_enable2(val);\n\t\t} else {\n\t\t\tiwm_printf(\"Write iwm2, st: %02x, loc: %x: %02x\\n\",\n\t\t\t\tq7_q6, loc, val);\n\t\t}\n\t\treturn;\n\t}\n\n\treturn;\n}\n\n\nint\niwm_read_enable2(dword64 dfcyc)\n{\n\tiwm_printf(\"Read under enable2 %016llx!\\n\", dfcyc);\n\treturn 0xff;\n}\n\nint g_cnt_enable2_handshake = 0;\n\nint\niwm_read_enable2_handshake(dword64 dfcyc)\n{\n\tint\tval;\n\n\tiwm_printf(\"Read handshake under enable2, %016llx!\\n\", dfcyc);\n\n\tval = 0xc0;\n\tg_cnt_enable2_handshake++;\n\tif(g_cnt_enable2_handshake > 3) {\n\t\tg_cnt_enable2_handshake = 0;\n\t\tval = 0x80;\n\t}\n\n\treturn val;\n}\n\nvoid\niwm_write_enable2(int val)\n{\n\t// Smartport is selected (PH3=1, PH1=1, Sel35=0), just ignore this data\n\tiwm_printf(\"Write under enable2: %02x!\\n\", val);\n\n\treturn;\n}\n\nword32\niwm_fastemul_start_write(Disk *dsk, dword64 dfcyc)\n{\n\tdouble\tnew_fast_cycs;\n\tdword64\tdfcyc_passed;\n\tword32\tfbit_pos, fbit_diff, track_bits;\n\n\t// Nox Archaist doesn't finish reading sector header's checksum, but\n\t//  instead waits for 7 bytes to pass and then writes.  This code\n\t//  tries to allow fast_disk_emul mode to not overwrite the checksum.\n\t// Move the fbit_pos forward to try to account for a delay from the\n\t//  last read to the current write.  But accesses to I/O locations\n\t//  would still take a lot of time.  Let's assume there were\n\t//  2 slow cycles, and clamp the skip to a min of 1 byte, max 3 bytes.\n\tdfcyc_passed = dfcyc - g_iwm.dfcyc_last_fastemul_read;\n\tnew_fast_cycs = (((double)dfcyc_passed) - 0x20000) /\n\t\t\t\t\t((double)engine.fplus_ptr->dplus_1);\n\t// new_fast_cycs approximates the number of fast cycles that have\n\t//  passed, so 4.0 means 4 fast cycles have passed\n\n\tiwm_printf(\"start write, dfcyc:%016llx, new_f_cycs:%f, plus_1:%08llx\\n\",\n\t\t\tdfcyc_passed, new_fast_cycs, engine.fplus_ptr->dplus_1);\n\n\tfbit_diff = (word32)(new_fast_cycs * dsk->fbit_mult);\n\tif(new_fast_cycs < 0.0) {\n\t\tfbit_diff = 8*512;\t\t// 8 bits\n\t} else {\n\t\tif(fbit_diff < 8*512) {\t\t// 8 bits\n\t\t\tfbit_diff = 8*512;\n\t\t} else if(fbit_diff > (32*512)) {\n\t\t\tfbit_diff = 32*512;\n\t\t}\n\t}\n\ttrack_bits = dsk->cur_track_bits;\n\tfbit_pos = dsk->cur_fbit_pos;\n\n\tif(track_bits == 0) {\n\t\treturn fbit_pos;\n\t}\n\n\tfbit_pos = fbit_pos + fbit_diff;\n\tif(fbit_pos >= (track_bits * 512)) {\n\t\tfbit_pos = fbit_pos - (track_bits * 512);\n\t}\n#if 0\n\tprintf(\" adjusted fbit_pos from %07x to %07x\\n\",\n\t\t\t\t\tdsk->cur_fbit_pos, fbit_pos);\n#endif\n\tdbg_log_info(dfcyc, fbit_pos, dsk->cur_fbit_pos, 0xee);\n\tdsk->cur_fbit_pos = fbit_pos;\n\n\treturn fbit_pos;\n}\n\nword32\niwm_read_data_fast(Disk *dsk, dword64 dfcyc)\n{\n\tdword64\tdval, dsync_val_tmp, dsync_val;\n\tword32\tbit_pos, new_bit_pos, track_bits, val;\n\tint\tmsb, dec;\n\n\tif(!g_fast_disk_unnib) {\n\t\tg_iwm.dfcyc_last_fastemul_read = dfcyc;\n\t}\n\ttrack_bits = dsk->cur_track_bits;\n\tbit_pos = dsk->cur_fbit_pos >> 9;\n\n\t// fbit_pos points just after the LSB of the last nibble.  Read +8\n\t//  past to get the next nibble.  Get more to ensure a valid nibble\n\tnew_bit_pos = iwm_calc_bit_sum(bit_pos, 15, track_bits);\n\tdval = iwm_get_raw_bits(dsk, new_bit_pos, 16, &dsync_val_tmp);\n\tdsync_val = dsync_val_tmp;\n#if 0\n\tprintf(\"fast %010llx syncs:%010llx bit:%07x\\n\", dval, dsync_val,\n\t\t\t\t\t\t\tbit_pos * 2);\n#endif\n\n\tif(!g_fast_disk_unnib) {\n\t\t//dbg_log_info(dfcyc, dval, dsync_val, 0xeb);\n\t}\n\t// Find the sync val closest to 15 without going over\n\tmsb = (dsync_val >> 8) & 0xff;\n\tif((msb > 15) || (msb == 0)) {\n\t\tmsb = dsync_val & 0xff;\n\t}\n\tif(msb > 15) {\n\t\tmsb = 8;\t// Just return something\n\t}\n\tif(msb < 7) {\n\t\t// This can happen when the arm is moved from a long track to a\n\t\t//  shorter track, fbit_pos at an arbitrary position at the\n\t\t//  track start, placing us at dsync_val=0x1006.  Just ignore\n\t\tmsb = 7;\n\t}\n\tval = (word32)(dval >> (msb - 7));\n\n\t// We've moved new_fbit_pos forward 15 bits, move back 7 bits for msb=15\n\tdec = 15 - msb - 7;\n\tif(!g_iwm_motor_on && (msb == 15) && !g_fast_disk_unnib) {\n\t\t// Return this byte properly...but not the next one for the\n\t\t//  DOS3.3 RWTS motor on detect code, which reads the drive\n\t\t//  without enabling the motor, to see if the motor is on\n\t\tdec--;\n\t}\n\tif((msb != 15) && !g_fast_disk_unnib) {\n\t\t// Pull a trick to make the disk motor-on test pass ($bd34 in\n\t\t//  DOS 3.3 RWTS): if this is a sync byte, don't return whole\n\t\t//  byte, but return whole byte next time\n\t\tval = val & 0x7f;\n\t\tdec = dec - 8;\n\t}\n\tdsk->cur_fbit_pos = iwm_calc_bit_sum(new_bit_pos, dec, track_bits) << 9;\n#if 0\n\tprintf(\"  val:%02x fbit:%07x new_fbit:%07x dec:%d, msb:%d\\n\",\n\t\tval & 0xff, dsk->cur_fbit_pos, new_fbit_pos, dec, msb);\n#endif\n\tif(!g_fast_disk_unnib) {\n\t\tdbg_log_info(dfcyc, dsk->cur_fbit_pos,\n\t\t\t(dec << 24) | (bit_pos * 2), (val << 16) | 0xeb);\n\t}\n\treturn val & 0xff;\n}\n\ndword64\niwm_return_rand_data(Disk *dsk, dword64 dfcyc)\n{\n\tdword64\tdval, dval2;\n\n\tif(dsk) {\n\t\t// Use dsk\n\t}\n\tdval = dfcyc >> 16;\n\tdval2 = dval ^ (dval >> 16) ^ (dval << 10) ^ (dval << 26) ^\n\t\t\t\t\t\t\t(dval << 41);\n\tdval = dval2 & ((dval >> 6) ^ (dval >> 17));\n\treturn dval;\n}\n\ndword64\niwm_get_raw_bits(Disk *dsk, word32 bit_pos, int num_bits, dword64 *dsyncs_ptr)\n{\n\tbyte\t*bptr, *sync_ptr;\n\tdword64\tdval, dtmp_val, sync_dval;\n\tword32\ttrack_bits;\n\tint\tpos, bits, total_bits, sync_pos, sync;\n\n\ttrack_bits = dsk->cur_track_bits;\n\tif(track_bits == 0) {\n\t\thalt_printf(\"iwm_get_raw_bits track_bits 0, %08x\\n\",\n\t\t\t\t\t\tdsk->cur_frac_track);\n\t\treturn 0;\n\t}\n\ttotal_bits = 0;\n\tbits = 1 + (bit_pos & 7);\t\t// 1..8\n\tpos = bit_pos >> 3;\n\tdval = 0;\n\tsync_dval = 0;\n\tbptr = &(dsk->cur_trk_ptr->raw_bptr[0]);\n\tsync_ptr = &(dsk->cur_trk_ptr->sync_ptr[0]);\n\tsync_pos = 0;\n\tif((bptr == 0) || (sync_ptr == 0)) {\n\t\thalt_printf(\"bptr:%p, sync:%p, bit_pos:%08x, track_bits:%08x, \"\n\t\t\t\"cur_trk_ptr eff:%05lx\\n\", bptr, sync_ptr, bit_pos,\n\t\t\ttrack_bits, dsk->cur_trk_ptr - &(dsk->trks[0]));\n\t\t*dsyncs_ptr = 0xffffffffffffffULL;\n\t\treturn 0;\n\t}\n\twhile(total_bits < num_bits) {\n\t\tdtmp_val = bptr[pos];\n\t\tdtmp_val = dtmp_val >> (8 - bits);\n\t\tdval = dval | (dtmp_val << total_bits);\n\t\tsync = sync_ptr[pos];\n\t\tif((sync < 8) && (sync >= (8 - bits))) {\t// MSB is here\n\t\t\tsync = sync - (8 - bits);\n\t\t\tsync = sync + total_bits;\n\t\t\tif((sync_pos < 64) && (sync != (sync_dval & 0xff))){\n\t\t\t\tsync_dval |= ((dword64)sync & 0xff) << sync_pos;\n\t\t\t\tsync_pos += 8;\n\t\t\t}\n\t\t}\n\t\ttotal_bits += bits;\n\t\tbits = 8;\n\t\tpos--;\n\t\tif(pos < 0) {\n\t\t\tpos = (track_bits - 1) >> 3;\n\t\t\tbits = 1 + ((track_bits - 1) & 7);\n\t\t}\n\t}\n\n\tif(sync_pos == 0) {\n\t\tsync_dval = 0xff;\n\t}\n\t*dsyncs_ptr = sync_dval;\n\treturn dval;\n}\n\nword32\niwm_calc_bit_diff(word32 first, word32 last, word32 track_bits)\n{\n\tword32\tval;\n\n\tval = first - last;\n\tif(val >= track_bits) {\n\t\tval = val + track_bits;\n\t}\n\treturn val;\n}\n\nword32\niwm_calc_bit_sum(word32 bit_pos, int add_ival, word32 track_bits)\n{\n\tword32\tval;\n\n\tval = bit_pos + add_ival;\n\tif(val >= track_bits) {\n\t\tif(add_ival < 0) {\n\t\t\tval = val + track_bits;\n\t\t} else {\n\t\t\tval = val - track_bits;\n\t\t}\n\t}\n\treturn val;\n}\n\ndword64\niwm_calc_forced_sync(dword64 dval, int forced_bit)\n{\n\tdword64\tsync_dval;\n\n\t// Something like the \"E7\" protection scheme has toggled\n\t//  $c0ed in the middle of reading a nibble, causing a new sync.  This\n\t//  is used to \"move\" sync bits inside disk nibbles, to defeat\n\t//  nibble copiers (since a 36-cycle sync nibble cannot be\n\t//  differentiated from a 40-cycle sync nibble in one read pass)\n\t// Return these misaligned nibbles until we re-sync (then clear\n\t//  g_iwm.forced_sync_bit.  Toggling $c0ed can set the data latch\n\t//  to $ff is the disk is write-protected, but this gets cleared\n\t//  to $00 within 4 CPU cycles always (or less).  A phantom 1 will\n\t//  also shift in\n\n\tdval |= (1ULL << forced_bit);\n\tsync_dval = 30;\t\t\t// A default for the previous nibble\n\twhile(forced_bit >= 0) {\n\t\t// Scan until this bit is set\n\t\tif((dval >> forced_bit) & 1) {\n\t\t\t// this bit is the MSB of a nibble, note it\n\t\t\tsync_dval = (sync_dval << 8) | forced_bit;\n\t\t\tforced_bit -= 7;\n\t\t}\n\t\tforced_bit--;\n\t}\n\treturn sync_dval;\n}\n\nint\niwm_calc_forced_sync_0s(dword64 sync_dval, int bits)\n{\n\tint\tsync, forced_bits, done;\n\tint\ti;\n\n\t// Return number between 7 and bits as to the oldest valid sync bit\n\t//  in the sync_dval array.  0xff in the first position means no syncs\n\tif(bits <= 8) {\n\t\treturn bits;\n\t}\n\tforced_bits = 8;\n\tdone = 0;\n\tif((sync_dval & 0xff) <= 7) {\n\t\tsync_dval = sync_dval >> 8;\n\t}\n\tfor(i = 0; i < 8; i++) {\n\t\tsync = sync_dval & 0xff;\n\t\tif(sync == 0xff) {\n\t\t\treturn bits;\n\t\t}\n\t\tsync_dval = sync_dval >> 8;\n\t\twhile(sync > bits) {\n\t\t\tsync -= 8;\t\t// Bring it back into range\n\t\t\tdone = 1;\n\t\t}\n\t\tif(sync < forced_bits) {\n\t\t\tbreak;\n\t\t}\n\t\tif(sync < bits) {\n\t\t\tforced_bits = sync;\n\t\t}\n\t\tif(done) {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn forced_bits;\n}\n\nword32\niwm_read_data(Disk *dsk, dword64 dfcyc)\n{\n\tdword64\tdval, sync_dval, dsync_val_tmp, dval0, dval2;\n\tword32\ttrack_bits, fbit_pos, bit_pos, val, forced_sync_bit;\n\tint\tmsb_bit, bits, forced_bits, diff;\n\n\ttrack_bits = dsk->cur_track_bits;\n\n\tfbit_pos = dsk->cur_fbit_pos;\n\tbit_pos = fbit_pos >> 9;\n\tif((track_bits == 0) || (dsk->cur_trk_ptr == 0)) {\n\t\tval = ((fbit_pos * 25) >> 11) & 0xff;\n\t\tiwm_printf(\"Reading c0ec, track_len 0, returning %02x\\n\", val);\n\t\treturn val;\n\t}\n\n\tif(g_fast_disk_emul) {\n\t\treturn iwm_read_data_fast(dsk, dfcyc);\n\t}\n\n\t// First, get the last few bytes of data\n\tbits = iwm_calc_bit_diff(bit_pos, g_iwm.last_rd_bit, track_bits) + 10;\n\tif(bits < 25) {\n\t\tbits = 25;\n\t} else if(bits > 60) {\n\t\tbits = 60;\n\t}\n\tforced_sync_bit = g_iwm.forced_sync_bit;\n\tforced_bits = -1;\n\tif(forced_sync_bit < track_bits) {\n\t\tforced_bits = iwm_calc_bit_diff(bit_pos, forced_sync_bit,\n\t\t\t\t\t\t\t\ttrack_bits);\n\t\tif(forced_bits >= 64) {\n\t\t\tforced_bits = 63;\n\t\t}\n\t\tif(forced_bits > bits) {\n\t\t\tbits = forced_bits;\n\t\t}\n\t}\n\tdval = iwm_get_raw_bits(dsk, bit_pos, bits, &dsync_val_tmp);\n\tsync_dval = dsync_val_tmp;\n\n\t// See if there are runs of more than three 0 bits...introduce noise\n\tdval0 = (1ULL << bits) | dval;\n\tdval0 = dval0 | (dval0 >> 1) | (dval0 >> 2) | (dval0 >> 3);\n\tdval0 = (~dval0) & ((1ULL << bits) - 1);\n\n\tif(dval0) {\n\t\t// Each set bit of dval0 indicates the previous 3 bits are 0\n\t\tdval2 = dval0 | (dval0 << 1);\n\t\tdval2 = dval0 & iwm_return_rand_data(dsk, dfcyc);\n#if 0\n\t\tprintf(\"dval0 is %016llx, dval2:%016llx, bits:%d\\n\", dval0,\n\t\t\t\t\t\tdval2, bits);\n#endif\n\t\tdval = dval ^ dval2;\n\t\tif(forced_bits < 0) {\n\t\t\tforced_bits = iwm_calc_forced_sync_0s(sync_dval, bits);\n\t\t\tg_iwm.forced_sync_bit = iwm_calc_bit_sum(bit_pos,\n\t\t\t\t\t0 - forced_bits, track_bits);\n\t\t\tdbg_log_info(dfcyc, g_iwm.forced_sync_bit * 2,\n\t\t\t\t(forced_bits << 24) | (bit_pos * 2), 0x400ec);\n#if 0\n\t\t\tprintf(\"Forced bits are %d at %06x, %016llx \"\n\t\t\t\t\"%016llx\\n\", forced_bits, bit_pos * 2, dval,\n\t\t\t\tsync_dval);\n#endif\n\t\t}\n\t}\n\tif(forced_bits >= 0) {\n\t\tsync_dval = iwm_calc_forced_sync(dval, forced_bits);\n\t\tdval = dval & ((2ULL << forced_bits) - 1LL);\n\t\tdbg_log_info(dfcyc, (word32)dval, forced_sync_bit * 32,\n\t\t\t\t\t\t(forced_bits << 16) | 0xea);\n\t\tif(((dsync_val_tmp & 0xff) == (sync_dval & 0xff)) && (!dval0)) {\n\t\t\t// Only clear it if there are no 0's in the dval,\n\t\t\t//  otherwise we'll reenter and could calc a diff sync\n\t\t\tg_iwm.forced_sync_bit = 234567*4;\n\t\t\t//printf(\"cleared forced_sync\\n\");\n\t\t} else {\n\t\t\tg_iwm.forced_sync_bit = iwm_calc_bit_sum(bit_pos,\n\t\t\t\t\t0 - (sync_dval & 0xf), track_bits);\n\t\t}\n\n#if 0\n\t\tprintf(\"Forced_bits were %d, sync_was %016llx\\n\",\n\t\t\t\t\t\tforced_bits, dsync_val_tmp);\n#endif\n\t}\n#if 0\n\tprintf(\" Raw bits: %016llx, dsync:%016llx, fbit:%08x\\n\", dval,\n\t\t\t\t\tsync_dval, fbit_pos);\n#endif\n\n\tdbg_log_info(dfcyc, (word32)dval,\n\t\t(bits << 24) | ((word32)sync_dval & 0x00ffffffUL), 0x300ec);\n\tmsb_bit = sync_dval & 0xff;\n\tif(g_iwm.state & 1) {\t\t// Latch mode (3.5\" disk)\n\t\t// last_rd_bit points just past the last bit read (it is\n\t\t//  fbit_pos normally, which is one bit past the read bit).\n\t\t// Use latched data if that bit is before the latched LSB\n\t\tdiff = iwm_calc_bit_diff(bit_pos, g_iwm.last_rd_bit,\n\t\t\t\t\t\t\t\ttrack_bits);\n\t\tdiff = diff + 8;\t\t// Calc to the MSB\n\t\tmsb_bit = (sync_dval >> 8) & 0xff;\n\t\tif((msb_bit >= 8) && (diff > msb_bit)) {\n\t\t\t// We've not seen the LSB of this data: ret latched data\n\t\t\tdbg_log_info(dfcyc, bit_pos * 2,\n\t\t\t\t\t(msb_bit << 16) | (diff & 0xffff),\n\t\t\t\t\t\t\t\t0x2200ec);\n\t\t\tbit_pos = iwm_calc_bit_sum(bit_pos, -msb_bit + 8,\n\t\t\t\t\t\t\t\ttrack_bits);\n\t\t\tdbg_log_info(dfcyc, bit_pos * 2, g_iwm.last_rd_bit*2,\n\t\t\t\t\t\t\t\t0x200ec);\n\t\t} else {\n\t\t\tmsb_bit = sync_dval & 0xff;\n\t\t\tif(diff <= msb_bit) {\n\t\t\t\t// Handle case of 10-bit nibble which we've\n\t\t\t\t//  already returned...don't return it again!\n\t\t\t\tmsb_bit = 1;\n\t\t\t}\n\t\t}\n#if 0\n\t\tprintf(\"Latch mode diff:%d, msb_bit:%d, new_msb:%d\\n\",\n\t\t\t\t\tdiff, msb_bit, (int)(sync_dval & 0xff));\n#endif\n\t\tdbg_log_info(dfcyc, ((word32)diff << 12) | (msb_bit & 0xff),\n\t\t\t\t\t\t(word32)sync_dval, 0x100ec);\n\t} else {\n\t\tif((sync_dval & 0xff) <= 1) {\n\t\t\t// The LSbit or the next one is a sync bit--but we need\n\t\t\t//  to ignore it.  The LSbit is not valid yet, and\n\t\t\t//  the next bit being the MSB of the next nibble will\n\t\t\t//  be ignored to make the 7usec hold time of the\n\t\t\t//  previous nibble work\n\t\t\tsync_dval = sync_dval >> 8;\n\t\t\tmsb_bit = sync_dval & 0xff;\n\t\t}\n\t}\n\tval = (word32)dval;\n\tif(msb_bit < 8) {\n\t\t// We only have a partial byte\n\t\tval = val & ((2ULL << msb_bit) - 1);\n\t} else if(msb_bit < 63) {\n\t\tval = (word32)(dval >> (msb_bit - 8));\n\t}\n\n\t// val is now valid from bit 8 down to bit 1\n\t// We've allowed in to dval an extra bit which we must toss\n\tval = val >> 1;\n\n\tg_iwm.last_rd_bit = bit_pos;\n\tdbg_log_info(dfcyc, (msb_bit << 24) | (fbit_pos >> 8),\n\t\t\t(word32)(dval0 << 8) | (val & 0xff), 0xec);\n#if 0\n\tif(forced_bits >= 0) {\n\t\tprintf(\"Forced bits, msb:%d, val:%02x, dval:%016llx b_p:%06x\\n\",\n\t\t\tmsb_bit, val, dval, bit_pos * 2);\n\t}\n#endif\n\treturn val & 0xff;\n}\n\nvoid\niwm_write_data(Disk *dsk, word32 val, dword64 dfcyc)\n{\n\tTrk\t*trk;\n\tword32\ttrack_bits, bit_pos, fbit_pos, bit_last, tmp_val;\n\tint\tbits;\n\n\ttrk = dsk->cur_trk_ptr;\n\tif((trk == 0) || dsk->write_prot) {\n\t\treturn;\n\t}\n\ttrack_bits = dsk->cur_track_bits;\n\n\tbit_pos = (dsk->cur_fbit_pos + 511) >> 9;\n\tif(track_bits && (bit_pos >= track_bits)) {\n\t\tbit_pos = bit_pos - track_bits;\n\t\tif(bit_pos >= track_bits) {\n\t\t\tbit_pos = bit_pos % track_bits;\n\t\t}\n\t}\n#if 0\n\tprintf(\"iwm_write_data: %02x %016llx, bit*2:%06x\\n\", val, dfcyc,\n\t\t\t\t\t\t\t\tbit_pos *2);\n#endif\n\tif(dsk->disk_525) {\n\t\tif(!g_slow_525_emul_wr) {\n\t\t\tg_slow_525_emul_wr = 1;\n\t\t\tengine_recalc_events();\n\t\t\tif(track_bits && g_fast_disk_emul) {\n\t\t\t\tfbit_pos = iwm_fastemul_start_write(dsk, dfcyc);\n\t\t\t\tbit_pos = fbit_pos >> 9;\n\t\t\t}\n\t\t}\n\t}\n\tdsk->cur_fbit_pos = bit_pos * 512;\n\tif(g_iwm.num_active_writes == 0) {\n\t\t// No write was pending, enter write mode now\n\t\t// printf(\"Starting write of data to the track, track now:\\n\");\n\t\t// iwm_show_track(-1, -1, dfcyc);\n\t\t// printf(\"Write data to track at bit*2:%06x\\n\", bit_pos);\n\t\tiwm_start_write(dsk, bit_pos, val, 0);\n\t\treturn;\n\t}\n\tif(track_bits == 0) {\n\t\thalt_printf(\"Impossible: track_bits: 0\\n\");\n\t\treturn;\n\t}\n\tif(g_iwm.state & 2) {\t\t// async handshake mode, 3.5\"\n\t\tiwm_write_data35(dsk, val, dfcyc);\n\t\treturn;\n\t}\n\n\t// From here on, it's 5.25\" disks only\n\tbit_last = g_iwm.wr_last_bit[0];\n\tbits = iwm_calc_bit_diff(bit_pos, bit_last, track_bits);\n\tif(bits >= 500) {\n\t\thalt_printf(\"bits are %d. bit*2:%06x, bit_last*2:%06x\\n\",\n\t\t\t\tbits, bit_pos * 2, bit_last * 2);\n\t\tbits = 40;\n\t}\n\tiwm_write_one_nib(dsk, bits, dfcyc);\n\n\ttmp_val = g_iwm.write_val;\n\tdbg_log_info(dfcyc, (g_iwm.wr_last_bit[0] << 25) | (bit_last * 2),\n\t\t(bits << 16) | ((val & 0xff) << 8) | (tmp_val & 0xff), 0xed);\n\tg_iwm.write_val = val;\n}\n\nvoid\niwm_start_write(Disk *dsk, word32 bit_pos, word32 val, int no_prior)\n{\n\tint\tnum;\n\n\tg_iwm.write_val = val;\n\tnum = 0;\n\tnum = iwm_start_write_act(dsk, bit_pos, num, no_prior, 0);\n\tnum = iwm_start_write_act(dsk, bit_pos, num, no_prior, -1);\t// -0.25\n\tnum = iwm_start_write_act(dsk, bit_pos, num, no_prior, +1);\t// +0.25\n\tnum = iwm_start_write_act(dsk, bit_pos, num, no_prior, -2);\t// -0.50\n\tnum = iwm_start_write_act(dsk, bit_pos, num, no_prior, +2);\t// +0.50\n\tg_iwm.num_active_writes = num;\n\n\twoz_maybe_reparse(dsk);\n}\n\nint\niwm_start_write_act(Disk *dsk, word32 bit_pos, int num, int no_prior, int delta)\n{\n\tTrk\t*trkptr;\n\tword32\tqtr_track, track_bits, bit_diff, prior_sum, allow_diff;\n\n\tqtr_track = (dsk->cur_frac_track + 0x8000) >> 16;\n\tqtr_track += delta;\n\tif(qtr_track >= 160) {\t\t\t// Could be unsigned wrap around\n\t\tif(delta == 0) {\n\t\t\thalt_printf(\"iwm_start_write_act0, qtr_track:%04x\\n\",\n\t\t\t\t\t\t\t\tqtr_track);\n\t\t\tg_iwm.num_active_writes = 0;\n\t\t}\n\t\treturn num;\n\t}\n\tif(!dsk->disk_525 && delta) {\n\t\treturn num;\t\t// 3.5\" does not affect adjacent trks\n\t}\n\n\ttrkptr = &(dsk->trks[qtr_track]);\n\ttrack_bits = trkptr->track_bits;\n\tif(track_bits == 0) {\n\t\tif((delta == -2) || (delta == 2)) {\n\t\t\treturn num;\t\t// Nothing to do\n\t\t}\n\t\tif((delta == -1) && (qtr_track > 0)) {\n\t\t\tif(trkptr[-1].track_bits == 0) {\n\t\t\t\treturn num;\t// Nothing to do\n\t\t\t}\n\t\t}\n\t\tif((delta == 1) && (qtr_track < 159)) {\n\t\t\tif(trkptr[1].track_bits == 0) {\n\t\t\t\treturn num;\t// Nothing to do\n\t\t\t}\n\t\t}\n\t\t// Otherwise, we need to create this track and write to it\n\t\ttrack_bits = woz_add_a_track(dsk, qtr_track);\n\t}\n\tif(track_bits) {\n\t\tbit_pos = bit_pos % track_bits;\n\t}\n\tbit_diff = iwm_calc_bit_diff(bit_pos, g_iwm.wr_last_bit[num],\n\t\t\t\t\t\t\t\ttrack_bits);\n\tprior_sum = 0;\n\tallow_diff = 16 + (16 * g_fast_disk_emul);\n\tif((g_iwm.wr_qtr_track[num] == qtr_track) && (bit_diff < allow_diff)) {\n\t\t// consider this write a continuation of the previous write\n\t\tprior_sum = g_iwm.wr_prior_num_bits[num] +\n\t\t\t\t\tg_iwm.wr_num_bits[num] + bit_diff;\n#if 0\n\t\tprintf(\"prior_sum is %d, qtr_track:%04x, bit_diff:%d\\n\",\n\t\t\tprior_sum, qtr_track, bit_diff);\n#endif\n\t} else {\n#if 0\n\t\tprintf(\"No prior_sum, qtr_track:%04x vs %04x, bit_diff:%08x, \"\n\t\t\t\"bit_pos:%05x wr_last_bit:%05x\\n\",\n\t\t\tg_iwm.wr_qtr_track[num], qtr_track, bit_diff,\n\t\t\tbit_pos * 2, g_iwm.wr_last_bit[num]*2);\n#endif\n\t}\n\tif(no_prior) {\n\t\tprior_sum = 0;\n\t}\n\tif(delta == 0) {\n\t\tdsk->cur_fbit_pos = bit_pos << 9;\n\t\tiwm_move_to_qtr_track(dsk, qtr_track);\n\t}\n\tg_iwm.wr_last_bit[num] = bit_pos;\n\tg_iwm.wr_qtr_track[num] = qtr_track;\n\tg_iwm.wr_num_bits[num] = 0;\n\tg_iwm.wr_prior_num_bits[num] = prior_sum;\n\tg_iwm.wr_delta[num] = abs(delta);\t\t// 0, 1 or 2\n\treturn num + 1;\n}\n\nvoid\niwm_write_data35(Disk *dsk, word32 val, dword64 dfcyc)\n{\n\t// Just always write 8 bits to the track\n\tiwm_write_one_nib(dsk, 8, dfcyc);\n\tg_iwm.write_val = val;\n\tdsk->cur_fbit_pos = g_iwm.wr_last_bit[0] * 512;\n}\n\nvoid\niwm_write_end(Disk *dsk, int write_through_now, dword64 dfcyc)\n{\n\tTrk\t*trkptr;\n\tword32\tlast_bit, qtr_track, num_bits, bit_start, delta, track_bits;\n\tword32\tprior_sum;\n\tint\tnum_active_writes;\n\tint\ti;\n\n\t// Flush out previous write, then turn writing off\n\tnum_active_writes = g_iwm.num_active_writes;\n#if 0\n\tprintf(\"In iwm_write_end at %016llx, num:%d, %d\\n\", dfcyc,\n\t\t\t\t\tnum_active_writes, dsk->disk_dirty);\n#endif\n\tif(num_active_writes == 0) {\n\t\treturn;\t\t\t// Invalid, not in a write\n\t}\n\tif(write_through_now) {\n\t\tiwm_write_data(dsk, 0, dfcyc);\n\t}\n\n\tfor(i = 0; i < num_active_writes; i++) {\n\t\tlast_bit = g_iwm.wr_last_bit[i];\n\t\tqtr_track = g_iwm.wr_qtr_track[i];\n\t\tnum_bits = g_iwm.wr_num_bits[i];\n\t\tdelta = g_iwm.wr_delta[i];\n#if 0\n\t\tprintf(\" end %d, last_bit:%05x, qtrk:%04x, num_b:%d, \"\n\t\t\t\"delta:%d\\n\", i, last_bit * 2, qtr_track, num_bits,\n\t\t\tdelta);\n#endif\n\t\tif((num_bits == 0) || (qtr_track >= 160)) {\n\t\t\tcontinue;\n\t\t}\n\t\ttrkptr = &(dsk->trks[qtr_track]);\n\t\ttrack_bits = trkptr->track_bits;\n\t\tprior_sum = g_iwm.wr_prior_num_bits[i];\n\t\tif((num_bits + prior_sum) >= track_bits) {\n\t\t\t// Full track write.  If delta != 0, erase this track\n\t\t\tprintf(\"Full track write at qtrk:%04x %016llx\\n\",\n\t\t\t\t\t\t\tqtr_track, dfcyc);\n#if 0\n\t\t\tif(qtr_track == 4) {\n\t\t\t\thalt_printf(\"Full track at qtr_trk:4\\n\");\n\t\t\t}\n#endif\n\t\t\tif(delta != 0) {\n\t\t\t\twoz_remove_a_track(dsk, qtr_track);\n\t\t\t\tprintf(\"TRACK %04x REMOVED\\n\", qtr_track);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tg_iwm.wr_prior_num_bits[i] = 0;\n\t\t}\n\n\t\t// Otherwise, recalc sync for this track\n\t\tif(num_bits >= track_bits) {\n\t\t\tnum_bits = num_bits % track_bits;\n\t\t}\n\t\tbit_start = iwm_calc_bit_sum(last_bit, -(int)num_bits - 24,\n\t\t\t\t\t\t\t\ttrack_bits);\n\t\tiwm_recalc_sync_from(dsk, qtr_track, bit_start, dfcyc);\n#if 0\n\t\tprintf(\"Wrote %d bits to qtrk:%04x at %05x %016llx, i:%d,%d, \"\n\t\t\t\"%d\\n\", num_bits, qtr_track, bit_start*2, dfcyc, i,\n\t\t\tnum_active_writes, dsk->disk_dirty);\n#endif\n\t}\n\tg_iwm.num_active_writes = 0;\n\n\twoz_maybe_reparse(dsk);\n}\n\nvoid\niwm_write_one_nib(Disk *dsk, int bits, dword64 dfcyc)\n{\n\tword32\tqtr_track, bit_pos, delta, val, track_bits;\n\tint\tnum;\n\tint\ti;\n\n\tnum = g_iwm.num_active_writes;\n\tval = g_iwm.write_val;\n\tfor(i = 0; i < num; i++) {\n\t\tqtr_track = g_iwm.wr_qtr_track[i];\n\t\tbit_pos = g_iwm.wr_last_bit[i];\n\t\tdelta = g_iwm.wr_delta[i];\n\t\tdbg_log_info(dfcyc, val, bit_pos * 2, 0x200ed);\n\t\tif(delta == 2) {\t\t// Trk +0.50 and -0.50: corrupt\n\t\t\tval = (val & 0x7f) ^ i ^ 0x0c;\n\t\t}\n\t\tbit_pos = disk_nib_out_raw(dsk, qtr_track, val, bits, bit_pos,\n\t\t\t\t\t\t\t\t\tdfcyc);\n\t\ttrack_bits = dsk->trks[qtr_track].track_bits;\n\t\tif(bit_pos >= track_bits) {\n\t\t\tbit_pos = bit_pos - track_bits;\n\t\t}\n\t\tg_iwm.wr_last_bit[i] = bit_pos;\n\t\tg_iwm.wr_num_bits[i] += bits;\n\t\tdbg_log_info(dfcyc, (bits << 24) | (bit_pos * 2),\n\t\t\t2*iwm_calc_bit_sum(bit_pos, 0-g_iwm.wr_num_bits[i],\n\t\t\t\t\t\t\ttrack_bits), 0x300ed);\n\t}\n}\n\nvoid\niwm_recalc_sync_from(Disk *dsk, word32 qtr_track, word32 bit_pos, dword64 dfcyc)\n{\n\tTrk\t*trkptr;\n\tbyte\t*bptr, *sync_ptr;\n\tword32\ttrack_bits, val, this_sync, sync0;\n\tint\tpos, next_pos, wrap, bit, last_bit, last_byte, match, this_bits;\n\n\t// We are called with a bit_pos 3 bytes before the desired byte.\n\t//  Look at pos, pos-1, pos+1 in a safe way, and find a valid sync_num\n\tif(dfcyc) {\n\t\t//printf(\"new sync from %d, %016llx %p\\n\", bit_pos, dfcyc, dsk);\n\t}\n\tif(qtr_track >= 160) {\n\t\thalt_printf(\"iwm_recalc_sync_from bad qtr:%04x bit_pos:%06x\\n\",\n\t\t\t\t\t\t\tqtr_track, bit_pos);\n\t\treturn;\n\t}\n\ttrkptr = &(dsk->trks[qtr_track]);\n\n\ttrack_bits = trkptr->track_bits;\n\tif(track_bits == 0) {\n\t\thalt_printf(\"iwm_recalc_sync_from track_bits 0 for %04x\\n\",\n\t\t\tqtr_track);\n\t\treturn;\n\t}\n\tlast_byte = (track_bits - 1) >> 3;\n\tlast_bit = ((track_bits - 1) & 7) + 1;\t\t// 1...8\n\n\tsync_ptr = &(trkptr->sync_ptr[0]);\n\tbptr = &(trkptr->raw_bptr[0]);\n\tpos = bit_pos >> 3;\n\tbit = bit_pos & 7;\t// 0...7\n\tsync0 = sync_ptr[pos];\n\tif(sync0 >= 8) {\n\t\tif(pos > 0) {\n\t\t\tpos--;\n\t\t} else {\n\t\t\tpos++;\t\t// pos is now 1\n\t\t}\n\t\tsync0 = sync_ptr[pos];\n\t}\n\tbit = (7 - sync0) & 7;\n\tmatch = 0;\n\twrap = 0;\n\tnext_pos = pos + 1;\n\t// cnt = 0;\n\t// printf(\"recalc_sync_from pos:%04x, bit:%d\\n\", pos, bit);\n\twhile(1) {\n\t\tval = bptr[pos];\n\t\tthis_bits = 8;\n\t\tthis_sync = sync_ptr[pos];\n\t\tsync_ptr[pos] = 0x20;\n\t\tnext_pos = pos + 1;\n\t\tif(pos >= last_byte) {\n#if 0\n\t\t\tprintf(\"At last_byte, val:%02x, pos:%04x, last_bit:%d, \"\n\t\t\t\t\"bit:%d\\n\", val, pos, last_bit, bit);\n#endif\n\t\t\tthis_bits = last_bit;\n\t\t\tval = val & (0xff00 >> last_bit);\n\t\t\tnext_pos = 0;\n\t\t\twrap++;\n\t\t\tif(wrap >= 3) {\n\t\t\t\thalt_printf(\"no stable sync found\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif(bit >= this_bits) {\n\t\t\t\t// Skip over this partial byte to byte 0\n\t\t\t\tbit -= this_bits;\n\t\t\t\tpos = 0;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n#if 0\n\t\tif(wrap || (pos >= 0x2630)) {\n\t\t\tprintf(\"pos:%04x, bit:%d, val:%04x, this_bits:%d\\n\",\n\t\t\t\tpos, bit, val, this_bits);\n\t\t}\n#endif\n#if 0\n\t\tif((cnt++ < 10) && (dsk->cur_frac_track == 0)) {\n\t\t\tprintf(\"sync[%04x]=%02x, val:%02x bit:%d, this_b:%d\\n\",\n\t\t\t\tpos, this_sync, val, bit, this_bits);\n\t\t}\n#endif\n\t\t// bit is within this byte.  Find next set bit\n\t\twhile(bit < this_bits) {\n\t\t\tif(((val << bit) & 0x80) == 0) {\n\t\t\t\t// Slide to next bit\n\t\t\t\tbit++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tsync_ptr[pos] = 7 - bit;\n\t\t\tif(this_sync == (word32)(7 - bit)) {\n\t\t\t\tmatch++;\n\t\t\t\tif(match >= 7) {\n#if 0\n\t\t\t\t\tprintf(\"match %d at pos:%04x wrap:%d\\n\",\n\t\t\t\t\t\tmatch, pos, wrap);\n#endif\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tmatch = 0;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tbit = (bit + 8 - this_bits) & 7;\n\t\tpos = next_pos;\n\t}\n}\n\n/* c600 */\nvoid\nsector_to_partial_nib(byte *in, byte *nib_ptr)\n{\n\tbyte\t*aux_buf, *nib_out;\n\tword32\tval, val2;\n\tint\tx;\n\tint\ti;\n\n\t/* Convert 256(+1) data bytes to 342+1 disk nibbles */\n\n\taux_buf = nib_ptr;\n\tnib_out = nib_ptr + 0x56;\n\n\tfor(i = 0; i < 0x56; i++) {\n\t\taux_buf[i] = 0;\n\t}\n\n\tx = 0x55;\n\tfor(i = 0x101; i >= 0; i--) {\n\t\tval = in[i];\n\t\tif(i >= 0x100) {\n\t\t\tval = 0;\n\t\t}\n\t\tval2 = (aux_buf[x] << 1) + (val & 1);\n\t\tval = val >> 1;\n\t\tval2 = (val2 << 1) + (val & 1);\n\t\tval = val >> 1;\n\t\tnib_out[i] = val;\n\t\taux_buf[x] = val2;\n\t\tx--;\n\t\tif(x < 0) {\n\t\t\tx = 0x55;\n\t\t}\n\t}\n}\n\n\nint\ndisk_unnib_4x4(Disk *dsk)\n{\n\tint\tval1;\n\tint\tval2;\n\n\tval1 = iwm_read_data_fast(dsk, 0);\n\tval2 = iwm_read_data_fast(dsk, 0);\n\n\treturn ((val1 << 1) + 1) & val2;\n}\n\nint\niwm_denib_track525(Disk *dsk, word32 qtr_track, byte *outbuf)\n{\n\tbyte\taux_buf[0x80];\n\tint\tsector_done[16];\n\tbyte\t*buf;\n\tword32\tval, val2, prev_val, save_frac_track;\n\tint\ttrack_len, vol, track, phys_sec, log_sec, cksum, x, my_nib_cnt;\n\tint\tsave_fbit_pos, tmp_fbit_pos, status, ret, num_sectors_done;\n\tint\ti;\n\n\t//printf(\"iwm_denib_track525\\n\");\n\n\tsave_fbit_pos = dsk->cur_fbit_pos;\n\tsave_frac_track = dsk->cur_frac_track;\n\tiwm_move_to_qtr_track(dsk, qtr_track);\n\n\tdsk->cur_fbit_pos = 0;\n\tg_fast_disk_unnib = 1;\n\n\ttrack_len = (dsk->cur_track_bits + 7) >> 3;\n\n\tfor(i = 0; i < 16; i++) {\n\t\tsector_done[i] = 0;\n\t}\n\n\tnum_sectors_done = 0;\n\n\tval = 0;\n\tstatus = -1;\n\tmy_nib_cnt = 0;\n\twhile(my_nib_cnt++ < 2*track_len) {\n\t\t/* look for start of a sector */\n\t\tif(val != 0xd5) {\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tcontinue;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xaa) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0x96) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* It's a sector start */\n\t\tvol = disk_unnib_4x4(dsk);\n\t\ttrack = disk_unnib_4x4(dsk);\n\t\tphys_sec = disk_unnib_4x4(dsk);\n\t\tif(phys_sec < 0 || phys_sec > 15) {\n\t\t\tprintf(\"Track %02x, read sec as %02x\\n\",\n\t\t\t\t\t\tqtr_track >> 2, phys_sec);\n\t\t\tbreak;\n\t\t}\n\t\tif(dsk->image_type == DSK_TYPE_DOS33) {\n\t\t\tlog_sec = phys_to_dos_sec[phys_sec];\n\t\t} else {\n\t\t\tlog_sec = phys_to_prodos_sec[phys_sec];\n\t\t}\n\t\tcksum = disk_unnib_4x4(dsk);\n\t\tif((vol ^ track ^ phys_sec ^ cksum) != 0) {\n\t\t\t/* not correct format */\n\t\t\tprintf(\"Track %02x not DOS 3.3 since hdr cksum, %02x \"\n\t\t\t\t\"%02x %02x %02x\\n\", qtr_track >> 2,\n\t\t\t\tvol, track, phys_sec, cksum);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* see what sector it is */\n\t\tif(track != (qtr_track >> 2) || (phys_sec < 0) ||\n\t\t\t\t\t\t\t(phys_sec > 15)) {\n\t\t\tprintf(\"Track %02x bad since track: %02x, sec: %02x\\n\",\n\t\t\t\tqtr_track>>2, track, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\tif(sector_done[phys_sec]) {\n\t\t\tprintf(\"Already done sector %02x on track %02x!\\n\",\n\t\t\t\tphys_sec, qtr_track>>2);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* So far so good, let's do it! */\n\t\tval = 0;\n\t\ti = 0;\n\t\twhile(i < NIBS_FROM_ADDR_TO_DATA) {\n\t\t\ti++;\n\t\t\tif(val != 0xd5) {\n\t\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tif(val != 0xaa) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tif(val != 0xad) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t/* got it, just break */\n\t\t\tbreak;\n\t\t}\n\n\t\tif(i >= NIBS_FROM_ADDR_TO_DATA) {\n\t\t\tprintf(\"No data header, track %02x, sec %02x at %07x\\n\",\n\t\t\t\tqtr_track>>2, phys_sec, dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\tbuf = outbuf + 0x100*log_sec;\n\n\t\t/* Data start! */\n\t\tprev_val = 0;\n\t\tfor(i = 0x55; i >= 0; i--) {\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tval2 = g_from_disk_byte[val];\n\t\t\tif(val2 >= 0x100) {\n\t\t\t\tprintf(\"Bad data area1, val:%02x,val2:%03x\\n\",\n\t\t\t\t\t\t\t\tval, val2);\n\t\t\t\tprintf(\" i:%03x, fbit_pos:%08x\\n\", i,\n\t\t\t\t\t\t\tdsk->cur_fbit_pos);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tprev_val = val2 ^ prev_val;\n\t\t\taux_buf[i] = prev_val;\n\t\t}\n\n\t\t/* rest of data area */\n\t\tfor(i = 0; i < 0x100; i++) {\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tval2 = g_from_disk_byte[val];\n\t\t\tif(val2 >= 0x100) {\n\t\t\t\tprintf(\"Bad data area2, read: %02x\\n\", val);\n\t\t\t\tprintf(\"  fbit_pos: %07x\\n\", dsk->cur_fbit_pos);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tprev_val = val2 ^ prev_val;\n\t\t\tbuf[i] = prev_val;\n\t\t}\n\n\t\t/* checksum */\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tval2 = g_from_disk_byte[val];\n\t\tif(val2 >= 0x100) {\n\t\t\tprintf(\"Bad data area3, read: %02x\\n\", val);\n\t\t\tprintf(\"  fbit_pos: %07x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\t\tif(val2 != prev_val) {\n\t\t\tprintf(\"Bad data cksum, got %02x, wanted: %02x\\n\",\n\t\t\t\tval2, prev_val);\n\t\t\tprintf(\"  fbit_pos: %07x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xde) {\n\t\t\tprintf(\"No 0xde at end of sector data:%02x\\n\", val);\n\t\t\tprintf(\"  fbit_pos: %07x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xaa) {\n\t\t\tprintf(\"No 0xde,0xaa at end of sector:%02x\\n\", val);\n\t\t\tprintf(\"  fbit_pos: %07x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Got this far, data is good, merge aux_buf into buf */\n\t\tx = 0x55;\n\t\tfor(i = 0; i < 0x100; i++) {\n\t\t\tval = aux_buf[x];\n\t\t\tval2 = (buf[i] << 1) + (val & 1);\n\t\t\tval = val >> 1;\n\t\t\tval2 = (val2 << 1) + (val & 1);\n\t\t\tbuf[i] = val2;\n\t\t\tval = val >> 1;\n\t\t\taux_buf[x] = val;\n\t\t\tx--;\n\t\t\tif(x < 0) {\n\t\t\t\tx = 0x55;\n\t\t\t}\n\t\t}\n\t\tsector_done[phys_sec] = 1;\n\t\tnum_sectors_done++;\n\t\tif(num_sectors_done >= 16) {\n\t\t\tstatus = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\ttmp_fbit_pos = dsk->cur_fbit_pos;\n\tg_fast_disk_unnib = 0;\n\n\tret = 0;\n\tif(status != 0) {\n\t\tprintf(\"Nibblization not done, %02x sectors found qtrk %04x, \"\n\t\t\t\"drive:%d, slot:%d\\n\", num_sectors_done, qtr_track,\n\t\t\tdsk->drive, dsk->disk_525 + 5);\n\t\tprintf(\"my_nib_cnt: %05x, fbit_pos:%07x, trk_len:%05x\\n\",\n\t\t\tmy_nib_cnt, tmp_fbit_pos, track_len);\n\t\tret = 16;\n\t\tfor(i = 0; i < 16; i++) {\n\t\t\tprintf(\"sector_done[%d] = %d\\n\", i, sector_done[i]);\n\t\t\tif(sector_done[i]) {\n\t\t\t\tret--;\n\t\t\t}\n\t\t}\n\t\tiwm_show_a_track(dsk, dsk->cur_trk_ptr, 0);\n\t\tif(!ret) {\n\t\t\tret = -1;\n\t\t}\n\t} else {\n\t\t//printf(\"iwm_denib_track525 succeeded\\n\");\n\t}\n\n\tdsk->cur_fbit_pos = save_fbit_pos;\n\tiwm_move_to_ftrack(dsk, save_frac_track, 0, 0);\n\n\treturn ret;\n}\n\nint\niwm_denib_track35(Disk *dsk, word32 qtr_track, byte *outbuf)\n{\n\tword32\tbuf_c00[0x100];\n\tword32\tbuf_d00[0x100];\n\tword32\tbuf_e00[0x100];\n\tint\tsector_done[16];\n\tbyte\t*buf;\n\tword32\ttmp_5c, tmp_5d, tmp_5e, tmp_66, tmp_67, val, val2;\n\tword32\tsave_frac_track;\n\tint\tnum_sectors_done, track_len, phys_track, phys_sec, phys_side;\n\tint\tphys_capacity, cksum, tmp, track, side, num_sectors, x, y;\n\tint\tcarry, my_nib_cnt, save_fbit_pos, status, ret;\n\tint\ti;\n\n\tsave_fbit_pos = dsk->cur_fbit_pos;\n\tsave_frac_track = dsk->cur_frac_track;\n\tiwm_move_to_qtr_track(dsk, qtr_track);\n\n\tif(dsk->cur_trk_ptr == 0) {\n\t\treturn 0;\n\t}\n\n\tdsk->cur_fbit_pos = 0;\n\tg_fast_disk_unnib = 1;\n\n\ttrack_len = dsk->cur_track_bits >> 3;\n\n\tnum_sectors = g_track_bytes_35[qtr_track >> 5] >> 9;\n\n\tfor(i = 0; i < num_sectors; i++) {\n\t\tsector_done[i] = 0;\n\t}\n\n\tnum_sectors_done = 0;\n\n\tval = 0;\n\tstatus = -1;\n\tmy_nib_cnt = 0;\n\n\ttrack = qtr_track >> 1;\n\tside = qtr_track & 1;\n\n\twhile(my_nib_cnt++ < 2*track_len) {\n\t\t/* look for start of a sector */\n\t\tif(val != 0xd5) {\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tcontinue;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xaa) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0x96) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* It's a sector start */\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tphys_track = g_from_disk_byte[val];\n\t\tif(phys_track != (track & 0x3f)) {\n\t\t\tprintf(\"Track %02x.%d, read track %02x, %02x\\n\",\n\t\t\t\ttrack, side, phys_track, val);\n\t\t\tbreak;\n\t\t}\n\n\t\tphys_sec = g_from_disk_byte[iwm_read_data_fast(dsk, 0)];\n\t\tif((phys_sec < 0) || (phys_sec >= num_sectors)) {\n\t\t\tprintf(\"Track %02x.%d, read sector %02x??\\n\",\n\t\t\t\ttrack, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\t\tphys_side = g_from_disk_byte[iwm_read_data_fast(dsk, 0)];\n\n\t\tif(phys_side != ((side << 5) + (track >> 6))) {\n\t\t\tprintf(\"Track %02x.%d, read side %02x??\\n\",\n\t\t\t\ttrack, side, phys_side);\n\t\t\tbreak;\n\t\t}\n\t\tphys_capacity = g_from_disk_byte[iwm_read_data_fast(dsk, 0)];\n\t\tif(phys_capacity != 0x24 && phys_capacity != 0x22) {\n\t\t\tprintf(\"Track %02x.%x capacity: %02x != 0x24/22\\n\",\n\t\t\t\ttrack, side, phys_capacity);\n\t\t}\n\t\tcksum = g_from_disk_byte[iwm_read_data_fast(dsk, 0)];\n\n\t\ttmp = phys_track ^ phys_sec ^ phys_side ^ phys_capacity;\n\t\tif(cksum != tmp) {\n\t\t\tprintf(\"Track %02x.%d, sector %02x, cksum: %02x.%02x\\n\",\n\t\t\t\ttrack, side, phys_sec, cksum, tmp);\n\t\t\tbreak;\n\t\t}\n\n\n\t\tif(sector_done[phys_sec]) {\n\t\t\tprintf(\"Already done sector %02x on track %02x.%x!\\n\",\n\t\t\t\tphys_sec, track, side);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* So far so good, let's do it! */\n\t\tval = 0;\n\t\tfor(i = 0; i < 38; i++) {\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tif(val == 0xd5) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(val != 0xd5) {\n\t\t\tprintf(\"No data header, track %02x.%x, sec %02x\\n\",\n\t\t\t\ttrack, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xaa) {\n\t\t\tprintf(\"Bad data hdr1,val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tprintf(\"fbit_pos: %08x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xad) {\n\t\t\tprintf(\"Bad data hdr2,val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tprintf(\"dsk->cur_fbit_pos:%07x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\tbuf = outbuf + (phys_sec << 9);\n\n\t\t/* check sector again */\n\t\ttmp = g_from_disk_byte[iwm_read_data_fast(dsk, 0)];\n\t\tif(tmp != phys_sec) {\n\t\t\tprintf(\"Bad data hdr3,val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Data start! */\n\t\ttmp_5c = 0;\n\t\ttmp_5d = 0;\n\t\ttmp_5e = 0;\n\t\ty = 0xaf;\n\t\tcarry = 0;\n\n\t\twhile(y > 0) {\n/* 626f */\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tval2 = g_from_disk_byte[val];\n\t\t\tif(val2 >= 0x100) {\n\t\t\t\tprintf(\"Bad data area1b, read: %02x\\n\", val);\n\t\t\t\tprintf(\" i:%03x, fbit_pos:%07x\\n\", i,\n\t\t\t\t\t\t\tdsk->cur_fbit_pos);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttmp_66 = val2;\n\n\t\t\ttmp_5c = tmp_5c << 1;\n\t\t\tcarry = (tmp_5c >> 8);\n\t\t\ttmp_5c = (tmp_5c + carry) & 0xff;\n\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tval2 = g_from_disk_byte[val];\n\t\t\tif(val2 >= 0x100) {\n\t\t\t\tprintf(\"Bad data area2, read: %02x\\n\", val);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tval2 = val2 + ((tmp_66 << 2) & 0xc0);\n\n\t\t\tval2 = val2 ^ tmp_5c;\n\t\t\tbuf_c00[y] = val2;\n\n\t\t\ttmp_5e = val2 + tmp_5e + carry;\n\t\t\tcarry = (tmp_5e >> 8);\n\t\t\ttmp_5e = tmp_5e & 0xff;\n/* 62b8 */\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tval2 = g_from_disk_byte[val];\n\t\t\tval2 = val2 + ((tmp_66 << 4) & 0xc0);\n\t\t\tval2 = val2 ^ tmp_5e;\n\t\t\tbuf_d00[y] = val2;\n\t\t\ttmp_5d = val2 + tmp_5d + carry;\n\n\t\t\tcarry = (tmp_5d >> 8);\n\t\t\ttmp_5d = tmp_5d & 0xff;\n\n\t\t\ty--;\n\t\t\tif(y <= 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\n/* 6274 */\n\t\t\tval = iwm_read_data_fast(dsk, 0);\n\t\t\tval2 = g_from_disk_byte[val];\n\t\t\tval2 = val2 + ((tmp_66 << 6) & 0xc0);\n\t\t\tval2 = val2 ^ tmp_5d;\n\t\t\tbuf_e00[y+1] = val2;\n\n\t\t\ttmp_5c = val2 + tmp_5c + carry;\n\t\t\tcarry = (tmp_5c >> 8);\n\t\t\ttmp_5c = tmp_5c & 0xff;\n\t\t}\n\n/* 62d0 */\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tval2 = g_from_disk_byte[val];\n\n\t\ttmp_66 = (val2 << 6) & 0xc0;\n\t\ttmp_67 = (val2 << 4) & 0xc0;\n\t\tval2 = (val2 << 2) & 0xc0;\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tval2 = g_from_disk_byte[val] + val2;\n\t\tif(tmp_5e != (word32)val2) {\n\t\t\tprintf(\"Checksum 5e bad: %02x vs %02x\\n\", tmp_5e, val2);\n\t\t\tprintf(\"val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tval2 = g_from_disk_byte[val] + tmp_67;\n\t\tif(tmp_5d != (word32)val2) {\n\t\t\tprintf(\"Checksum 5d bad: %02x vs %02x\\n\", tmp_5e, val2);\n\t\t\tprintf(\"val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tval2 = g_from_disk_byte[val] + tmp_66;\n\t\tif(tmp_5c != (word32)val2) {\n\t\t\tprintf(\"Checksum 5c bad: %02x vs %02x\\n\", tmp_5e, val2);\n\t\t\tprintf(\"val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Whew, got it!...check for DE AA */\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xde) {\n\t\t\tprintf(\"Bad data epi1,val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tprintf(\"fbit_pos: %08x\\n\", dsk->cur_fbit_pos);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = iwm_read_data_fast(dsk, 0);\n\t\tif(val != 0xaa) {\n\t\t\tprintf(\"Bad data epi2,val:%02x trk %02x.%x, sec %02x\\n\",\n\t\t\t\tval, track, side, phys_sec);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Now, convert buf_c/d/e to output */\n/* 6459 */\n\t\ty = 0;\n\t\tfor(x = 0xab; x >= 0; x--) {\n\t\t\t*buf++ = buf_c00[x];\n\t\t\ty++;\n\t\t\tif(y >= 0x200) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t*buf++ = buf_d00[x];\n\t\t\ty++;\n\t\t\tif(y >= 0x200) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t*buf++ = buf_e00[x];\n\t\t\ty++;\n\t\t\tif(y >= 0x200) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tsector_done[phys_sec] = 1;\n\t\tnum_sectors_done++;\n\t\tif(num_sectors_done >= num_sectors) {\n\t\t\tstatus = 0;\n\t\t\tbreak;\n\t\t}\n\t\tval = 0;\n\t}\n\n\tg_fast_disk_unnib = 0;\n\n\tret = 0;\n\tif(status != 0) {\n\t\tprintf(\"dsk->fbit_pos: %07x, status: %d\\n\", dsk->cur_fbit_pos,\n\t\t\t\t\t\t\t\tstatus);\n\t\tfor(i = 0; i < num_sectors; i++) {\n\t\t\tprintf(\"sector done[%d] = %d\\n\", i, sector_done[i]);\n\t\t}\n\t\tprintf(\"Nibblization not done, %02x blocks found qtrk %04x\\n\",\n\t\t\tnum_sectors_done, qtr_track);\n\n\t\tret = -1;\n\t}\n\n\tdsk->cur_fbit_pos = save_fbit_pos;\n\tiwm_move_to_ftrack(dsk, save_frac_track, 0, 0);\n\n\treturn ret;\n\n}\n\n/* ret = 1 -> dirty data written out */\n/* ret = 0 -> not dirty, no error */\n/* ret < 0 -> error */\nint\niwm_track_to_unix(Disk *dsk, word32 qtr_track, byte *outbuf)\n{\n\tTrk\t*trk;\n\tdword64\tdunix_pos, dret, unix_len;\n\tint\tret;\n\n\ttrk = &(dsk->trks[qtr_track]);\n\tif((trk->track_bits == 0) || (trk->dirty == 0)) {\n\t\treturn 0;\n\t}\n\n\tprintf(\"iwm_track_to_unix dirty qtr:%04x, dirty:%d\\n\", qtr_track,\n\t\t\t\t\t\t\ttrk->dirty);\n#if 0\n\tif((qtr_track & 3) && disk_525) {\n\t\thalt_printf(\"You wrote to phase %02x!  Can't wr bk to unix!\\n\",\n\t\t\tqtr_track);\n\t\tdsk->write_through_to_unix = 0;\n\t\treturn -1;\n\t}\n#endif\n\n\tif(dsk->wozinfo_ptr) {\t\t\t\t// WOZ disk\n\t\toutbuf = trk->raw_bptr;\n\t\tret = 0;\n\t} else {\n\t\tif(dsk->disk_525) {\n\t\t\tif(qtr_track & 3) {\n\t\t\t\t// Not a valid track\n\t\t\t\tret = -1;\n\t\t\t} else {\n\t\t\t\tret = iwm_denib_track525(dsk, qtr_track,\n\t\t\t\t\t\t\t\t\toutbuf);\n\t\t\t}\n\t\t} else {\n\t\t\tret = iwm_denib_track35(dsk, qtr_track, outbuf);\n\t\t}\n\t}\n\n\tif(ret != 0) {\n\t\treturn -1;\n\t}\n\n\t/* Write it out */\n\tdunix_pos = trk->dunix_pos;\n\tunix_len = trk->unix_len;\n\tif(unix_len < 0x1000) {\n\t\thalt_printf(\"Disk:%s trk:%06x, dunix_pos:%08llx, len:%08llx\\n\",\n\t\t\tdsk->name_ptr, dsk->cur_frac_track, dunix_pos,\n\t\t\tunix_len);\n\t\treturn -1;\n\t}\n\n\ttrk->dirty = 0;\n\tif(dsk->dynapro_info_ptr) {\n\t\treturn dynapro_write(dsk, outbuf, dunix_pos, (word32)unix_len);\n\t}\n\n\tdret = cfg_write_to_fd(dsk->fd, outbuf, dunix_pos, unix_len);\n#if 0\n\tprintf(\"Write: qtr_trk:%04x, dunix_pos:%08llx, %s\\n\", qtr_track,\n\t\t\tdunix_pos, dsk->name_ptr);\n#endif\n\tif(dret != unix_len) {\n\t\tprintf(\"write: %08llx, errno:%d, trk: %06x, disk: %s\\n\",\n\t\t\tdret, errno, dsk->cur_frac_track, dsk->name_ptr);\n\t\treturn -1;\n\t}\n\tif(dsk->wozinfo_ptr) {\t\t\t\t// WOZ disk\n\t\tprintf(\"Wrote track %07x to fd:%d off:%08llx, len:%07llx\\n\",\n\t\t\tdsk->cur_frac_track, dsk->fd, dunix_pos, unix_len);\n\t\twoz_rewrite_crc(dsk, 0);\n\t}\n\n\treturn 1;\n}\n\nvoid\nshow_hex_data(byte *buf, int count)\n{\n\tint\ti;\n\n\tfor(i = 0; i < count; i += 16) {\n\t\tprintf(\"%04x: %02x %02x %02x %02x %02x %02x %02x %02x \"\n\t\t\t\"%02x %02x %02x %02x %02x %02x %02x %02x\\n\", i,\n\t\t\tbuf[i+0], buf[i+1], buf[i+2], buf[i+3],\n\t\t\tbuf[i+4], buf[i+5], buf[i+6], buf[i+7],\n\t\t\tbuf[i+8], buf[i+9], buf[i+10], buf[i+11],\n\t\t\tbuf[i+12], buf[i+13], buf[i+14], buf[i+15]);\n\t}\n}\n\nvoid\niwm_check_nibblization(dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tint\tslot, drive, sel35;\n\n\tdrive = (g_iwm.state >> IWM_BIT_DRIVE_SEL) & 1;\n\tslot = 6;\n\tif(g_iwm.state & IWM_STATE_MOTOR_ON) {\n\t\tsel35 = (g_iwm.state >> IWM_BIT_C031_APPLE35SEL) & 1;\n\t} else {\n\t\tsel35 = (g_iwm.state >> IWM_BIT_LAST_SEL35) & 1;\n\t}\n\tif(sel35) {\n\t\tdsk = &(g_iwm.drive35[drive]);\n\t\tslot = 5;\n\t} else {\n\t\tdsk = &(g_iwm.drive525[drive]);\n\t}\n\tprintf(\"iwm_check_nibblization, s%d d%d\\n\", slot, drive);\n\tdisk_check_nibblization(dsk, 0, 4096, dfcyc);\n}\n\nvoid\ndisk_check_nibblization(Disk *dsk, byte *in_buf, int size, dword64 dfcyc)\n{\n\tbyte\tbuffer[0x3000];\n\tword32\tqtr_track;\n\tint\tret, ret2;\n\tint\ti;\n\n\tif(size > 0x3000) {\n\t\tprintf(\"size %08x is > 0x3000, disk_check_nibblization\\n\",size);\n\t\texit(3);\n\t}\n\n\tfor(i = 0; i < size; i++) {\n\t\tbuffer[i] = 0;\n\t}\n\n\t//printf(\"About to call iwm_denib_track*, here's the track:\\n\");\n\t//iwm_show_a_track(dsk, dsk->cur_trk_ptr, dfcyc);\n\n\tqtr_track = (word32)(dsk->cur_trk_ptr - &(dsk->trks[0]));\n\tif(qtr_track >= 160) {\n\t\thalt_printf(\"cur_trk_ptr points to bad qtr_track:%08x\\n\",\n\t\t\t\t\t\t\t\tqtr_track);\n\t\treturn;\n\t}\n\tif(dsk->disk_525) {\n\t\tret = iwm_denib_track525(dsk, qtr_track, &(buffer[0]));\n\t} else {\n\t\tret = iwm_denib_track35(dsk, qtr_track, &(buffer[0]));\n\t}\n\n\tret2 = -1;\n\tif(in_buf) {\n\t\tfor(i = 0; i < size; i++) {\n\t\t\tif(buffer[i] != in_buf[i]) {\n\t\t\t\tprintf(\"buffer[%04x]: %02x != %02x\\n\", i,\n\t\t\t\t\t\t\tbuffer[i], in_buf[i]);\n\t\t\t\tret2 = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif((ret != 0) || (ret2 >= 0)) {\n\t\tprintf(\"disk_check_nib ret:%d, ret2:%d for track %06x\\n\",\n\t\t\tret, ret2, dsk->cur_frac_track);\n\t\tif(in_buf) {\n\t\t\tshow_hex_data(in_buf, 0x1000);\n\t\t}\n\t\tshow_hex_data(buffer, size);\n\t\tiwm_show_a_track(dsk, dsk->cur_trk_ptr, dfcyc);\n\t\tif(ret == 16) {\n\t\t\tprintf(\"No sectors found, ignore it\\n\");\n\t\t\treturn;\n\t\t}\n\n\t\thalt_printf(\"Stop\\n\");\n\t\texit(2);\n\t}\n}\n\n#define TRACK_BUF_LEN\t\t0x2000\n\nvoid\ndisk_unix_to_nib(Disk *dsk, int qtr_track, dword64 dunix_pos, word32 unix_len,\n\tint len_bits, dword64 dfcyc)\n{\n\tbyte\ttrack_buf[TRACK_BUF_LEN];\n\tTrk\t*trk;\n\tbyte\t*bptr;\n\tdword64\tdret, dlen;\n\tword32\tnum_bytes, must_clear_track;\n\tint\ti;\n\n\t/* Read track from dsk int track_buf */\n#if 0\n\tprintf(\"disk_unix_to_nib: qtr:%04x, unix_pos:%08llx, unix_len:%08x, \"\n\t\t\"len_bits:%06x\\n\", qtr_track, dunix_pos, unix_len, len_bits);\n#endif\n\n\tmust_clear_track = 0;\n\n\tif(unix_len > TRACK_BUF_LEN) {\n\t\tprintf(\"diks_unix_to_nib: requested len of image %s = %05x\\n\",\n\t\t\tdsk->name_ptr, unix_len);\n\t}\n\n\tbptr = dsk->raw_data;\n\tif(bptr != 0) {\n\t\t// raw_data is valid, so use it\n\t\tif((dunix_pos + unix_len) > dsk->raw_dsize) {\n\t\t\tmust_clear_track = 1;\n\t\t} else {\n\t\t\tbptr += dunix_pos;\n\t\t\tfor(i = 0; i < (int)unix_len; i++) {\n\t\t\t\ttrack_buf[i] = bptr[i];\n\t\t\t}\n\t\t}\n\t} else if(unix_len > 0) {\n\t\tdret = kegs_lseek(dsk->fd, dunix_pos, SEEK_SET);\n\t\tif(dret != dunix_pos) {\n\t\t\tprintf(\"lseek of disk %s len 0x%llx ret: %lld, errno:\"\n\t\t\t\t\"%d\\n\", dsk->name_ptr, dunix_pos, dret, errno);\n\t\t\tmust_clear_track = 1;\n\t\t}\n\n\t\tdlen = read(dsk->fd, track_buf, unix_len);\n\t\tif(dlen != unix_len) {\n\t\t\tprintf(\"read of disk %s q_trk %d ret: %lld, errno:%d\\n\",\n\t\t\t\tdsk->name_ptr, qtr_track, dlen, errno);\n\t\t\tmust_clear_track = 1;\n\t\t}\n\t}\n\n\tif(must_clear_track) {\n\t\tfor(i = 0; i < TRACK_BUF_LEN; i++) {\n\t\t\ttrack_buf[i] = 0;\n\t\t}\n\t}\n\n#if 0\n\tprintf(\"Q_track %02x dumped out\\n\", qtr_track);\n\n\tfor(i = 0; i < 4096; i += 32) {\n\t\tprintf(\"%04x: %02x%02x%02x%02x%02x%02x%02x%02x \"\n\t\t\t\"%02x%02x%02x%02x%02x%02x%02x%02x \"\n\t\t\t\"%02x%02x%02x%02x%02x%02x%02x%02x \"\n\t\t\t\"%02x%02x%02x%02x%02x%02x%02x%02x\\n\", i,\n\t\t\ttrack_buf[i+0], track_buf[i+1], track_buf[i+2],\n\t\t\ttrack_buf[i+3], track_buf[i+4], track_buf[i+5],\n\t\t\ttrack_buf[i+6], track_buf[i+7], track_buf[i+8],\n\t\t\ttrack_buf[i+9], track_buf[i+10], track_buf[i+11],\n\t\t\ttrack_buf[i+12], track_buf[i+13], track_buf[i+14],\n\t\t\ttrack_buf[i+15], track_buf[i+16], track_buf[i+17],\n\t\t\ttrack_buf[i+18], track_buf[i+19], track_buf[i+20],\n\t\t\ttrack_buf[i+21], track_buf[i+22], track_buf[i+23],\n\t\t\ttrack_buf[i+24], track_buf[i+25], track_buf[i+26],\n\t\t\ttrack_buf[i+27], track_buf[i+28], track_buf[i+29],\n\t\t\ttrack_buf[i+30], track_buf[i+31]);\n\t}\n#endif\n\n\tdsk->cur_fbit_pos = 0;\t\t/* for consistency */\n\tdsk->raw_bptr_malloc = 1;\n\n\ttrk = &(dsk->trks[qtr_track]);\n\tnum_bytes = 2 + ((len_bits + 7) >> 3) + 4;\n\ttrk->track_bits = len_bits;\n\ttrk->dunix_pos = dunix_pos;\n\ttrk->unix_len = unix_len;\n\ttrk->dirty = 0;\n\ttrk->raw_bptr = (byte *)malloc(num_bytes);\n\ttrk->sync_ptr = (byte *)malloc(num_bytes);\n\n\tiwm_move_to_ftrack(dsk, qtr_track << 16, 0, dfcyc);\n\n\t/* create nibblized image */\n\n\tif(dsk->disk_525 && (dsk->image_type == DSK_TYPE_NIB)) {\n\t\tiwm_nibblize_track_nib525(dsk, track_buf, qtr_track, unix_len);\n\t} else if(dsk->disk_525) {\n\t\tiwm_nibblize_track_525(dsk, track_buf, qtr_track, dfcyc);\n\t} else {\n\t\tiwm_nibblize_track_35(dsk, track_buf, qtr_track, unix_len,\n\t\t\t\t\t\t\t\t\tdfcyc);\n\t}\n\n\t//printf(\"For qtr_track:%04x, trk->dirty:%d\\n\", qtr_track, trk->dirty);\n\ttrk->dirty = 0;\n}\n\nvoid\niwm_nibblize_track_nib525(Disk *dsk, byte *track_buf, int qtr_track,\n\t\t\t\t\t\tword32 unix_len)\n{\n\tbyte\t*bptr, *sync_ptr;\n\tint\tlen;\n\tint\ti;\n\n\t// This is the old, dumb .nib format.  It consists of 0x1a00 bytes\n\t//  per track, but there's no sync information.  Just mark each byte\n\t//  as being sync=7\n\tlen = unix_len;\n\tbptr = &(dsk->cur_trk_ptr->raw_bptr[0]);\n\tsync_ptr = &(dsk->cur_trk_ptr->sync_ptr[0]);\n\tfor(i = 0; i < len; i++) {\n\t\tbptr[i] = track_buf[i];\n\t}\n\tfor(i = 0; i < len; i++) {\n\t\tsync_ptr[i] = 7;\n\t}\n\tif(dsk->cur_track_bits != (unix_len * 8)) {\n\t\tfatal_printf(\"Track %d.%02d of nib image should be bits:%06x \"\n\t\t\t\"but it is: %06x\\n\", qtr_track >> 2, (qtr_track & 3)*25,\n\t\t\tunix_len * 8, dsk->cur_track_bits);\n\t}\n\n\tiwm_printf(\"Nibblized q_track %02x\\n\", qtr_track);\n}\n\nvoid\niwm_nibblize_track_525(Disk *dsk, byte *track_buf, int qtr_track, dword64 dfcyc)\n{\n\tbyte\tpartial_nib_buf[0x300];\n\tword32\tval, last_val;\n\tint\tphys_sec, log_sec, num_sync;\n\tint\ti;\n\n#if 0\n\tprintf(\"nibblize track 525, qtr_track:%04x, trk:%p, trk->raw_bptr:%p, \"\n\t\t\"sync_ptr:%p\\n\", qtr_track, trk, trk->raw_bptr, trk->sync_ptr);\n#endif\n\n\tfor(phys_sec = 0; phys_sec < 16; phys_sec++) {\n\t\tif(dsk->image_type == DSK_TYPE_DOS33) {\n\t\t\tlog_sec = phys_to_dos_sec[phys_sec];\n\t\t} else {\n\t\t\tlog_sec = phys_to_prodos_sec[phys_sec];\n\t\t}\n\n\t\t/* Create sync headers */\n\t\tif(phys_sec == 0) {\n\t\t\tnum_sync = 70;\n\t\t} else {\n\t\t\tnum_sync = 22;\n\t\t}\n\n\t\tfor(i = 0; i < num_sync; i++) {\n\t\t\tdisk_nib_out(dsk, 0xff, 10);\n\t\t}\n\t\tdisk_nib_out(dsk, 0xd5, 8);\t\t// prolog: d5,aa,96\n\t\tdisk_nib_out(dsk, 0xaa, 8);\n\t\tdisk_nib_out(dsk, 0x96, 8);\n\t\tdisk_4x4_nib_out(dsk, dsk->vol_num);\n\t\tdisk_4x4_nib_out(dsk, qtr_track >> 2);\n\t\tdisk_4x4_nib_out(dsk, phys_sec);\n\t\tdisk_4x4_nib_out(dsk, dsk->vol_num ^ (qtr_track>>2) ^ phys_sec);\n\t\tdisk_nib_out(dsk, 0xde, 8);\t\t// epilog: de,aa,eb\n\t\tdisk_nib_out(dsk, 0xaa, 8);\n\t\tdisk_nib_out(dsk, 0xeb, 8);\n\n\t\t/* Inter sync */\n\t\tdisk_nib_out(dsk, 0xff, 10);\n\t\tfor(i = 0; i < 6; i++) {\n\t\t\tdisk_nib_out(dsk, 0xff, 10);\n\t\t}\n\t\tdisk_nib_out(dsk, 0xd5, 8);\t// data prolog: d5,aa,ad\n\t\tdisk_nib_out(dsk, 0xaa, 8);\n\t\tdisk_nib_out(dsk, 0xad, 8);\n\n\t\tsector_to_partial_nib( &(track_buf[log_sec*256]),\n\t\t\t&(partial_nib_buf[0]));\n\n\t\tlast_val = 0;\n\t\tfor(i = 0; i < 0x156; i++) {\n\t\t\tval = partial_nib_buf[i];\n\t\t\tdisk_nib_out(dsk, to_disk_byte[last_val ^ val], 8);\n\t\t\tlast_val = val;\n\t\t}\n\t\tdisk_nib_out(dsk, to_disk_byte[last_val], 8);\n\n\t\t/* data epilog */\n\t\tdisk_nib_out(dsk, 0xde, 8);\t// data epilog: de,aa,eb\n\t\tdisk_nib_out(dsk, 0xaa, 8);\n\t\tdisk_nib_out(dsk, 0xeb, 8);\n\t}\n\n\t/* finish nibblization */\n\tdisk_nib_end_track(dsk, dfcyc);\n\n\tiwm_printf(\"Nibblized q_track %02x\\n\", qtr_track);\n\n\tif(g_check_nibblization) {\n\t\tdisk_check_nibblization(dsk, &(track_buf[0]), 0x1000, dfcyc);\n\t}\n\n\t//printf(\"Showing track after nibblization:\\n\");\n\t//iwm_show_a_track(dsk, dsk->cur_trk_ptr, dfcyc);\n}\n\nvoid\niwm_nibblize_track_35(Disk *dsk, byte *track_buf, int qtr_track,\n\t\t\t\t\t\tword32 unix_len, dword64 dfcyc)\n{\n\tint\tphys_to_log_sec[16];\n\tword32\tbuf_c00[0x100];\n\tword32\tbuf_d00[0x100];\n\tword32\tbuf_e00[0x100];\n\tbyte\t*buf;\n\tword32\tval, phys_track, phys_side, capacity, cksum, acc_hi;\n\tword32\ttmp_5c, tmp_5d, tmp_5e, tmp_5f, tmp_63, tmp_64, tmp_65;\n\tint\tnum_sectors, log_sec, track, side, num_sync, carry;\n\tint\tinterleave, x, y;\n\tint\ti, phys_sec;\n\n\tif(dsk->cur_fbit_pos & 511) {\n\t\thalt_printf(\"fbit_pos:%07x is not bit-aligned!\\n\",\n\t\t\t\t\t\t\tdsk->cur_fbit_pos);\n\t}\n\n\tnum_sectors = (unix_len >> 9);\n\n\tfor(i = 0; i < num_sectors; i++) {\n\t\tphys_to_log_sec[i] = -1;\n\t}\n\n\tphys_sec = 0;\n\tinterleave = 2;\n\tfor(log_sec = 0; log_sec < num_sectors; log_sec++) {\n\t\twhile(phys_to_log_sec[phys_sec] >= 0) {\n\t\t\tphys_sec++;\n\t\t\tif(phys_sec >= num_sectors) {\n\t\t\t\tphys_sec = 0;\n\t\t\t}\n\t\t}\n\t\tphys_to_log_sec[phys_sec] = log_sec;\n\t\tphys_sec += interleave;\n\t\tif(phys_sec >= num_sectors) {\n\t\t\tphys_sec -= num_sectors;\n\t\t}\n\t}\n\n\ttrack = qtr_track >> 1;\n\tside = qtr_track & 1;\n\tfor(phys_sec = 0; phys_sec < num_sectors; phys_sec++) {\n\n\t\tlog_sec = phys_to_log_sec[phys_sec];\n\t\tif(log_sec < 0) {\n\t\t\tprintf(\"Track: %02x.%x phys_sec: %02x = %d!\\n\",\n\t\t\t\ttrack, side, phys_sec, log_sec);\n\t\t\texit(2);\n\t\t}\n\n\t\t/* Create sync headers */\n\t\tif(phys_sec == 0) {\n\t\t\tnum_sync = 400;\n\t\t} else {\n\t\t\tnum_sync = 54;\n\t\t}\n\n\t\tfor(i = 0; i < num_sync; i++) {\n\t\t\tdisk_nib_out(dsk, 0xff, 10);\n\t\t}\n\n\t\tdisk_nib_out(dsk, 0xd5, 8);\t\t/* prolog */\n\t\tdisk_nib_out(dsk, 0xaa, 8);\t\t/* prolog */\n\t\tdisk_nib_out(dsk, 0x96, 8);\t\t/* prolog */\n\n\t\tphys_track = track & 0x3f;\n\t\tphys_side = (side << 5) + (track >> 6);\n\t\tcapacity = 0x22;\n\t\tdisk_nib_out(dsk, to_disk_byte[phys_track], 8);\t/* trk */\n\t\tdisk_nib_out(dsk, to_disk_byte[log_sec], 8);\t/* sec */\n\t\tdisk_nib_out(dsk, to_disk_byte[phys_side], 8);\t/* sides+trk */\n\t\tdisk_nib_out(dsk, to_disk_byte[capacity], 8);\t/* capacity*/\n\n\t\tcksum = (phys_track ^ log_sec ^ phys_side ^ capacity) & 0x3f;\n\t\tdisk_nib_out(dsk, to_disk_byte[cksum], 8);\t/* cksum*/\n\n\t\tdisk_nib_out(dsk, 0xde, 8);\t\t/* epi */\n\t\tdisk_nib_out(dsk, 0xaa, 8);\t\t/* epi */\n\n\t\t/* Inter sync */\n\t\tfor(i = 0; i < 5; i++) {\n\t\t\tdisk_nib_out(dsk, 0xff, 10);\n\t\t}\n\t\tdisk_nib_out(dsk, 0xd5, 8);\t/* data prolog */\n\t\tdisk_nib_out(dsk, 0xaa, 8);\t/* data prolog */\n\t\tdisk_nib_out(dsk, 0xad, 8);\t/* data prolog */\n\t\tdisk_nib_out(dsk, to_disk_byte[log_sec], 8);\t/* sec again */\n\n\t\t/* do nibblizing! */\n\t\tbuf = track_buf + (log_sec << 9);\n\n/* 6320 */\n\t\ttmp_5e = 0;\n\t\ttmp_5d = 0;\n\t\ttmp_5c = 0;\n\t\ty = 0;\n\t\tx = 0xaf;\n\t\tbuf_c00[0] = 0;\n\t\tbuf_d00[0] = 0;\n\t\tbuf_e00[0] = 0;\n\t\tbuf_e00[1] = 0;\n\t\tfor(y = 0x4; y > 0; y--) {\n\t\t\tbuf_c00[x] = 0;\n\t\t\tbuf_d00[x] = 0;\n\t\t\tbuf_e00[x] = 0;\n\t\t\tx--;\n\t\t}\n\n\t\twhile(x >= 0) {\n/* 6338 */\n\t\t\ttmp_5c = tmp_5c << 1;\n\t\t\tcarry = (tmp_5c >> 8);\n\t\t\ttmp_5c = (tmp_5c + carry) & 0xff;\n\n\t\t\tval = buf[y];\n\t\t\ttmp_5e = val + tmp_5e + carry;\n\t\t\tcarry = (tmp_5e >> 8);\n\t\t\ttmp_5e = tmp_5e & 0xff;\n\n\t\t\tval = val ^ tmp_5c;\n\t\t\tbuf_c00[x] = val;\n\t\t\ty++;\n/* 634c */\n\t\t\tval = buf[y];\n\t\t\ttmp_5d = tmp_5d + val + carry;\n\t\t\tcarry = (tmp_5d >> 8);\n\t\t\ttmp_5d = tmp_5d & 0xff;\n\t\t\tval = val ^ tmp_5e;\n\t\t\tbuf_d00[x] = val;\n\t\t\ty++;\n\t\t\tx--;\n\t\t\tif(x <= 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\n/* 632a */\n\t\t\tval = buf[y];\n\t\t\ttmp_5c = tmp_5c + val + carry;\n\t\t\tcarry = (tmp_5c >> 8);\n\t\t\ttmp_5c = tmp_5c & 0xff;\n\n\t\t\tval = val ^ tmp_5d;\n\t\t\tbuf_e00[x+1] = val;\n\t\t\ty++;\n\t\t}\n\n/* 635f */\n\t\tval = ((tmp_5c >> 2) ^ tmp_5d) & 0x3f;\n/* 6367 */\n\t\tval = (val ^ tmp_5d) >> 2;\n/* 636b */\n\t\tval = (val ^ tmp_5e) & 0x3f;\n/* 636f */\n\t\tval = (val ^ tmp_5e) >> 2;\n/* 6373 */\n\t\ttmp_5f = val;\n/* 6375 */\n\t\ttmp_63 = 0;\n\t\ttmp_64 = 0;\n\t\ttmp_65 = 0;\n\t\tacc_hi = 0;\n\n\n\t\ty = 0xae;\n\t\twhile(y >= 0) {\n/* 63e4 */\n\t\t\t/* write out acc_hi */\n\t\t\tval = to_disk_byte[acc_hi & 0x3f];\n\t\t\tdisk_nib_out(dsk, val, 8);\n\n/* 63f2 */\n\t\t\tval = to_disk_byte[tmp_63 & 0x3f];\n\t\t\ttmp_63 = buf_c00[y];\n\t\t\tacc_hi = tmp_63 >> 6;\n\t\t\tdisk_nib_out(dsk, val, 8);\n/* 640b */\n\t\t\tval = to_disk_byte[tmp_64 & 0x3f];\n\t\t\ttmp_64 = buf_d00[y];\n\t\t\tacc_hi = (acc_hi << 2) + (tmp_64 >> 6);\n\t\t\tdisk_nib_out(dsk, val, 8);\n\t\t\ty--;\n\t\t\tif(y < 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\n/* 63cb */\n\t\t\tval = to_disk_byte[tmp_65 & 0x3f];\n\t\t\ttmp_65 = buf_e00[y+1];\n\t\t\tacc_hi = (acc_hi << 2) + (tmp_65 >> 6);\n\t\t\tdisk_nib_out(dsk, val, 8);\n\t\t}\n/* 6429 */\n\t\tval = to_disk_byte[tmp_5f & 0x3f];\n\t\tdisk_nib_out(dsk, val, 8);\n\n\t\tval = to_disk_byte[tmp_5e & 0x3f];\n\t\tdisk_nib_out(dsk, val, 8);\n\n\t\tval = to_disk_byte[tmp_5d & 0x3f];\n\t\tdisk_nib_out(dsk, val, 8);\n\n\t\tval = to_disk_byte[tmp_5c & 0x3f];\n\t\tdisk_nib_out(dsk, val, 8);\n\n/* 6440 */\n\t\t/* data epilog */\n\t\tdisk_nib_out(dsk, 0xde, 8);\t/* epi */\n\t\tdisk_nib_out(dsk, 0xaa, 8);\t/* epi */\n\t\tdisk_nib_out(dsk, 0xff, 8);\n\t}\n\n\tdisk_nib_end_track(dsk, dfcyc);\n\n\tif(g_check_nibblization) {\n\t\tdisk_check_nibblization(dsk, &(track_buf[0]), unix_len, dfcyc);\n\t}\n}\n\nvoid\ndisk_4x4_nib_out(Disk *dsk, word32 val)\n{\n\tdisk_nib_out(dsk, 0xaa | (val >> 1), 8);\n\tdisk_nib_out(dsk, 0xaa | val, 8);\n}\n\nvoid\ndisk_nib_out(Disk *dsk, word32 val, int size)\n{\n\tword32\tbit_pos;\n\n\tbit_pos = dsk->cur_fbit_pos >> 9;\n\tdsk->cur_fbit_pos = disk_nib_out_raw(dsk,\n\t\t(dsk->cur_frac_track + 0x8000) >> 16, val, size, bit_pos, 0) *\n\t\t\t\t\t\t\t\t\t512;\n}\n\nvoid\ndisk_nib_end_track(Disk *dsk, dword64 dfcyc)\n{\n\t// printf(\"disk_nib_end_track %p\\n\", dsk);\n\n\tdsk->cur_fbit_pos = 0;\n\tdsk->disk_dirty = 0;\n\tiwm_recalc_sync_from(dsk, (word32)(dsk->cur_trk_ptr - &(dsk->trks[0])),\n\t\t\t\t\t\t\t\t0, dfcyc);\n}\n\nword32\ndisk_nib_out_raw(Disk *dsk, word32 qtr_track, word32 val, int bits,\n\t\t\t\tword32 bit_pos, dword64 dfcyc)\n{\n\tTrk\t*trkptr;\n\tbyte\t*bptr, *sync_ptr;\n\tword32\ttrack_bits, tmp, mask;\n\tint\tpos, next_pos, bit, to_do, shift_left, shift_right, last_byte;\n\tint\tthis_bits;\n\tint\tdo_print;\n\n\t// write bits from val[7:x].  If bits=3 and val=0xaf, write bits 101.\n\t// If bits=10 and val=0xaf, write 0xaf then bits 00.\n\n\tif(qtr_track >= 160) {\n\t\treturn bit_pos;\n\t}\n\ttrkptr = &(dsk->trks[qtr_track]);\n\ttrack_bits = trkptr->track_bits;\n\tif(track_bits == 0) {\n\t\thalt_printf(\"disk_nib_out_raw track_bits=0, %04x\\n\", qtr_track);\n\t\treturn bit_pos;\n\t}\n\n\tlast_byte = (track_bits - 1) >> 3;\n\tbit = bit_pos & 7;\n\tpos = bit_pos >> 3;\n\tbptr = &(trkptr->raw_bptr[0]);\n\tsync_ptr = &(trkptr->sync_ptr[0]);\n\tif(dfcyc != 0) {\n\t\tdbg_log_info(dfcyc, (bits << 24) | (bit_pos << 1),\n\t\t\t\t(track_bits << 16) | (val & 0xffff), 0x100ed);\n\t}\n\tdsk->disk_dirty = 1;\n\ttrkptr->dirty = 1;\n\tdo_print = ((pos <= 0x10) || (pos >= 0x18e0)) &&\n\t\t\t\t\t(dsk->cur_frac_track == 0xb0000);\n\tdo_print = 0;\n\tif(do_print) {\n\t\tprintf(\"disk_nib_out %02x, %d, %06x\\n\", val, bits, bit_pos*2);\n\t}\n\twhile(1) {\n\t\tthis_bits = 8;\n\t\tnext_pos = pos + 1;\n\t\tif(pos >= last_byte) {\n\t\t\tthis_bits = ((track_bits - 1) & 7) + 1;\t// 1..8\n\t\t\tnext_pos = 0;\n\t\t}\n\t\tthis_bits = (this_bits - bit);\t\t// 1..8\n\t\tto_do = bits;\t\t\t\t// 1...inf\n\t\tif(to_do > this_bits) {\n\t\t\tto_do = this_bits;\t\t// 1..8\n\t\t}\n\t\tshift_left = (8 - bit - to_do) & 7;\n\t\tshift_right = 8 - to_do;\n\t\tmask = (1U << to_do) - 1;\n\t\ttmp = (val >> shift_right) & mask;\n\t\tmask = mask << shift_left;\n\t\tif(do_print) {\n\t\t\tprintf(\" pos:%04x bit:%d tmp:%02x mask:%02x bits:%d \"\n\t\t\t\t\"bptr[]=%02x new:%02x todo:%d, r:%d l:%d\\n\",\n\t\t\t\tpos, bit, tmp, mask, bits, bptr[pos],\n\t\t\t\t(bptr[pos] & (~mask)) |\n\t\t\t\t\t\t((tmp << shift_left) & mask),\n\t\t\t\tto_do, shift_right, shift_left);\n\t\t}\n\t\tbptr[pos] = (bptr[pos] & (~mask)) |\n\t\t\t\t\t\t((tmp << shift_left) & mask);\n\t\tsync_ptr[pos] = 0xff;\n\t\tbits -= to_do;\n\t\tif(bits <= 0) {\n\t\t\tpos = (pos * 8) + bit + to_do;\n\t\t\tif((bit + to_do) >= 8) {\n\t\t\t\tpos = next_pos * 8;\n\t\t\t}\n\t\t\tif((word32)pos >= track_bits) {\n\t\t\t\tpos -= track_bits;\n\t\t\t}\n\t\t\tif(do_print) {\n\t\t\t\tprintf(\" returning %05x, bits:%d orig:%05x \"\n\t\t\t\t\t\"%05x\\n\",\n\t\t\t\t\tpos*2, bits, bit_pos*2, last_byte);\n\t\t\t}\n\t\t\treturn pos;\n\t\t}\n\t\tval = (val << to_do) & 0xff;\n\t\tbit = 0;\n\t\tpos = next_pos;\n\t}\n}\n\nDisk *\niwm_get_dsk_from_slot_drive(int slot, int drive)\n{\n\tDisk\t*dsk;\n\tint\tmax_drive;\n\n\t// pass in slot=5,6,7 drive=0,1 (or more for slot 7)\n\tmax_drive = 2;\n\tswitch(slot) {\n\tcase 5:\n\t\tdsk = &(g_iwm.drive35[drive]);\n\t\tbreak;\n\tcase 6:\n\t\tdsk = &(g_iwm.drive525[drive]);\n\t\tbreak;\n\tdefault:\t// slot 7\n\t\tmax_drive = MAX_C7_DISKS;\n\t\tdsk = &(g_iwm.smartport[drive]);\n\t}\n\tif(drive >= max_drive) {\n\t\tdsk -= drive;\t\t// Move back to drive 0 effectively\n\t}\n\n\treturn dsk;\n}\n\nvoid\niwm_toggle_lock(Disk *dsk)\n{\n\tprintf(\"iwm_toggle_lock: write_prot:%d, write_through:%d\\n\",\n\t\tdsk->write_prot, dsk->write_through_to_unix);\n\tif(dsk->write_prot == 2) {\n\t\t// nothing to do\n\t\treturn;\n\t}\n\n\tif(dsk->write_prot) {\n\t\tdsk->write_prot = 0;\n\t} else {\n\t\tdsk->write_prot = 1;\n\t}\n\tprintf(\"New dsk->write_prot: %d\\n\", dsk->write_prot);\n\tif(dsk->wozinfo_ptr && dsk->write_through_to_unix) {\n\t\twoz_rewrite_lock(dsk);\n\t\tprintf(\"Called woz_rewrite_lock()\\n\");\n\t\treturn;\n\t}\n}\n\nvoid\niwm_eject_named_disk(int slot, int drive, const char *name,\n\t\t\t\t\t\tconst char *partition_name)\n{\n\tDisk\t*dsk;\n\n\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\tif(dsk->fd < 0) {\n\t\treturn;\n\t}\n\n\t/* If name matches, eject the disk! */\n\tif(!strcmp(dsk->name_ptr, name)) {\n\t\t/* It matches, eject it */\n\t\tif((partition_name != 0) && (dsk->partition_name != 0)) {\n\t\t\t/* If both have partitions, and they differ, then */\n\t\t\t/*  don't eject.  Otherwise, eject */\n\t\t\tif(strcmp(dsk->partition_name, partition_name) != 0) {\n\t\t\t\t/* Don't eject */\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tiwm_eject_disk(dsk);\n\t}\n}\n\nvoid\niwm_eject_disk_by_num(int slot, int drive)\n{\n\tDisk\t*dsk;\n\n\tdsk = iwm_get_dsk_from_slot_drive(slot, drive);\n\n\tiwm_eject_disk(dsk);\n}\n\nvoid\niwm_eject_disk(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\tword32\tstate;\n\tint\tmotor_on;\n\tint\ti;\n\n\tprintf(\"Ejecting dsk: %s, fd:%d\\n\", dsk->name_ptr, dsk->fd);\n\n\tif(dsk->fd < 0) {\n\t\treturn;\n\t}\n\n\tg_config_kegs_update_needed = 1;\n\n\tstate = g_iwm.state;\n\tmotor_on = state & IWM_STATE_MOTOR_ON;\n\tif(state & IWM_STATE_C031_APPLE35SEL) {\n\t\tmotor_on = state & IWM_STATE_MOTOR_ON35;\n\t}\n\tif(motor_on) {\n\t\thalt_printf(\"Try eject dsk:%s, but motor_on!\\n\", dsk->name_ptr);\n\t}\n\n\tdynapro_try_fix_damaged_disk(dsk);\n\tiwm_flush_disk_to_unix(dsk);\n\n\tprintf(\"Ejecting disk: %s\\n\", dsk->name_ptr);\n\n\t/* Free all memory, close file */\n\n\t/* free the tracks first */\n\tif(dsk->trks != 0) {\n\t\tfor(i = 0; i < MAX_TRACKS; i++) {\n\t\t\tif(dsk->raw_bptr_malloc) {\n\t\t\t\tfree(dsk->trks[i].raw_bptr);\n\t\t\t}\n\t\t\tfree(dsk->trks[i].sync_ptr);\n\t\t\tdsk->trks[i].raw_bptr = 0;\n\t\t\tdsk->trks[i].sync_ptr = 0;\n\t\t\tdsk->trks[i].track_bits = 0;\n\t\t}\n\t}\n\tdsk->num_tracks = 0;\n\tdsk->raw_bptr_malloc = 0;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(wozinfo_ptr) {\n\t\tif(dsk->raw_data == 0) {\n\t\t\tfree(wozinfo_ptr->wozptr);\n\t\t}\n\t\twozinfo_ptr->wozptr = 0;\n\t\tfree(wozinfo_ptr);\n\t}\n\tdsk->wozinfo_ptr = 0;\n\n\tdynapro_free_dynapro_info(dsk);\n\n\t/* close file, clean up dsk struct */\n\tif(dsk->raw_data) {\n\t\tfree(dsk->raw_data);\n\t} else {\n\t\tclose(dsk->fd);\n\t}\n\n\tdsk->fd = -1;\n\tdsk->raw_dsize = 0;\n\tdsk->raw_data = 0;\n\tdsk->dimage_start = 0;\n\tdsk->dimage_size = 0;\n\tdsk->cur_fbit_pos = 0;\n\tdsk->cur_track_bits = 0;\n\tdsk->disk_dirty = 0;\n\tdsk->write_through_to_unix = 0;\n\tdsk->write_prot = 1;\n\tdsk->just_ejected = 1;\n\n\t/* Leave name_ptr valid */\n}\n\nvoid\niwm_show_track(int slot_drive, int track, dword64 dfcyc)\n{\n\tDisk\t*dsk;\n\tTrk\t*trk;\n\tword32\tstate;\n\tint\tdrive, sel35, qtr_track;\n\n\tstate = g_iwm.state;\n\tif(slot_drive < 0) {\n\t\tdrive = (state >> IWM_BIT_DRIVE_SEL) & 1;\n\t\tif(state & IWM_STATE_MOTOR_ON) {\n\t\t\tsel35 = (state >> IWM_BIT_C031_APPLE35SEL) & 1;\n\t\t} else {\n\t\t\tsel35 = (state >> IWM_BIT_LAST_SEL35) & 1;\n\t\t}\n\t} else {\n\t\tdrive = slot_drive & 1;\n\t\tsel35 = !((slot_drive >> 1) & 1);\n\t}\n\tif(sel35) {\n\t\tdsk = &(g_iwm.drive35[drive]);\n\t} else {\n\t\tdsk = &(g_iwm.drive525[drive]);\n\t}\n\n\tif(track < 0) {\n\t\tqtr_track = dsk->cur_frac_track >> 16;\n\t} else {\n\t\tqtr_track = track;\n\t}\n\tif((dsk->trks == 0) || (qtr_track >= 160)) {\n\t\treturn;\n\t}\n\ttrk = &(dsk->trks[qtr_track]);\n\n\tif(trk->track_bits == 0) {\n\t\tdbg_printf(\"Track_bits: %d\\n\", trk->track_bits);\n\t\tdbg_printf(\"No track for type: %d, drive: %d, qtrk:0x%02x\\n\",\n\t\t\tsel35, drive, qtr_track);\n\t\treturn;\n\t}\n\n\tdbg_printf(\"Current s%dd%d, q_track:0x%02x\\n\", 6 - sel35,\n\t\t\t\t\t\t\tdrive + 1, qtr_track);\n\n\tiwm_show_a_track(dsk, trk, dfcyc);\n}\n\nvoid\niwm_show_a_track(Disk *dsk, Trk *trk, dword64 dfcyc)\n{\n\tbyte\t*bptr;\n\tbyte\t*sync_ptr;\n\tword32\tval, track_bits, len, shift, line_len, sync;\n\tint\ti, j;\n\n\ttrack_bits = trk->track_bits;\n\tdbg_printf(\"  Showtrack:track_bits*2: %06x, fbit_pos: %07x, \"\n\t\t\"trk:%06x, dfcyc:%016llx\\n\", track_bits*2, dsk->cur_fbit_pos,\n\t\tdsk->cur_frac_track, dfcyc);\n\tdbg_printf(\"  disk_525:%d, drive:%d name:%s fd:%d, dimage_start:\"\n\t\t\"%08llx, dimage_size:%08llx\\n\", dsk->disk_525, dsk->drive,\n\t\tdsk->name_ptr, dsk->fd, dsk->dimage_start, dsk->dimage_size);\n\tdbg_printf(\"  image_type:%d, vol_num:%02x, write_prot:%d, \"\n\t\t\"write_through:%d, disk_dirty:%d\\n\", dsk->image_type,\n\t\tdsk->vol_num, dsk->write_prot, dsk->write_through_to_unix,\n\t\tdsk->disk_dirty);\n\tdbg_printf(\"  just_ejected:%d, last_phases:%d, num_tracks:%d\\n\",\n\t\tdsk->just_ejected, dsk->last_phases, dsk->num_tracks);\n\n\tlen = (track_bits + 7) >> 3;\n\tif(len >= 0x3000) {\n\t\tlen = 0x3000;\n\t\tdbg_printf(\"len too big, using %04x\\n\", len);\n\t}\n\n\tbptr = trk->raw_bptr;\n\tsync_ptr = trk->sync_ptr;\n\n\tlen = len + 2;\t\t// Show an extra 2 bytes\n\tfor(i = 0; i < (int)len; i += 16) {\n\t\tline_len = 16;\n\t\tif((i + line_len) > len) {\n\t\t\tline_len = len - i;\n\t\t}\n\t\t// First, print raw bptr bytes\n\t\tdbg_printf(\"%04x:  \", i);\n\t\tfor(j = 0; j < (int)line_len; j++) {\n\t\t\tdbg_printf(\" %02x\", bptr[i + j]);\n\t\t\tif(((i + j) * 8U) >= track_bits) {\n\t\t\t\tdbg_printf(\"*\");\n\t\t\t}\n\t\t}\n\t\tdbg_printf(\"\\n\");\n\t\tdbg_printf(\"  sync:\");\n\t\tfor(j = 0; j < (int)line_len; j++) {\n\t\t\tdbg_printf(\" %2d\", sync_ptr[i + j]);\n\t\t}\n\t\tdbg_printf(\"\\n\");\n\t\tdbg_printf(\"  nibs:\");\n\t\tfor(j = 0; j < (int)line_len; j++) {\n\t\t\tsync = sync_ptr[i+j];\n\t\t\tif(sync >= 8) {\n\t\t\t\tdbg_printf(\" XX\");\n\t\t\t} else {\n\t\t\t\tshift = (7 - sync) & 7;\n\t\t\t\tval = (bptr[i + j] << 8) | bptr[i + j + 1];\n\t\t\t\tval = ((val << shift) >> 8) & 0xff;\n\t\t\t\tdbg_printf(\" %02x\", val);\n\t\t\t}\n\t\t}\n\t\tdbg_printf(\"\\n\");\n\t}\n}\n\n\nvoid\ndummy1(word32 psr)\n{\n\tprintf(\"dummy1 psr: %05x\\n\", psr);\n}\n\nvoid\ndummy2(word32 psr)\n{\n\tprintf(\"dummy2 psr: %05x\\n\", psr);\n}\n"
  },
  {
    "path": "upstream/kegs/src/iwm.h",
    "content": "#ifdef INCLUDE_RCSID_C\nconst char rcsid_iwm_h[] = \"@(#)$KmKId: iwm.h,v 1.46 2023-09-23 17:53:09+00 kentd Exp $\";\n#endif\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#define MAX_TRACKS\t(2*80)\n#define MAX_C7_DISKS\t16\n\n#define NIB_LEN_525\t\t0x18f2\t\t/* 51088 bits per track */\n// Expected bits per track: (1020484/5)/4 = 51024.  A little extra seems good\n\n#define NIBS_FROM_ADDR_TO_DATA\t28\n// Copy II+ Manual Sector Copy fails if this is 20, so make it 28\n\n// image_type settings.  0 means unknown type\n#define DSK_TYPE_PRODOS\t\t1\n#define DSK_TYPE_DOS33\t\t2\n#define DSK_TYPE_DYNAPRO\t3\n#define DSK_TYPE_NIB\t\t4\n#define DSK_TYPE_WOZ\t\t5\n\n// Note: C031_APPLE35SEL must be 6, C031_CTRL must be 7, MOTOR_ON must be 5!\n// Q7 needs to be adjacent and higher than Q6\n// Bits 4:0 are IWM mode register: 0: latch mode; 1: async handshake;\n//   2: immediate motor off (no 1 sec delay); 3: 2us bit timing;\n//   4: Divide input clock by 8 (instead of 7)\n#define IWM_BIT_MOTOR_ON\t\t5\n#define IWM_BIT_C031_APPLE35SEL\t\t6\n#define IWM_BIT_C031_CTRL\t\t7\n#define IWM_BIT_STEP_DIRECTION35\t8\n#define IWM_BIT_MOTOR_ON35\t\t9\n#define IWM_BIT_MOTOR_OFF\t\t10\n#define IWM_BIT_DRIVE_SEL\t\t11\n#define IWM_BIT_Q6\t\t\t12\n#define IWM_BIT_Q7\t\t\t13\n#define IWM_BIT_ENABLE2\t\t\t14\n#define IWM_BIT_LAST_SEL35\t\t15\n#define IWM_BIT_PHASES\t\t\t16\n#define IWM_BIT_RESET\t\t\t20\n\n#define IWM_STATE_MOTOR_ON\t\t(1 << IWM_BIT_MOTOR_ON)\n#define IWM_STATE_C031_APPLE35SEL\t(1 << IWM_BIT_C031_APPLE35SEL)\n#define IWM_STATE_C031_CTRL\t\t(1 << IWM_BIT_C031_CTRL)\n#define IWM_STATE_STEP_DIRECTION35\t(1 << IWM_BIT_STEP_DIRECTION35)\n#define IWM_STATE_MOTOR_ON35\t\t(1 << IWM_BIT_MOTOR_ON35)\n#define IWM_STATE_MOTOR_OFF\t\t(1 << IWM_BIT_MOTOR_OFF)\n#define IWM_STATE_DRIVE_SEL\t\t(1 << IWM_BIT_DRIVE_SEL)\n#define IWM_STATE_Q6\t\t\t(1 << IWM_BIT_Q6)\n#define IWM_STATE_Q7\t\t\t(1 << IWM_BIT_Q7)\n#define IWM_STATE_ENABLE2\t\t(1 << IWM_BIT_ENABLE2)\n#define IWM_STATE_LAST_SEL35\t\t(1 << IWM_BIT_LAST_SEL35)\n#define IWM_STATE_PHASES\t\t(1 << IWM_BIT_PHASES)\n#define IWM_STATE_RESET\t\t\t(1 << IWM_BIT_RESET)\n\nSTRUCT(Trk) {\n\tbyte\t*raw_bptr;\n\tbyte\t*sync_ptr;\n\tdword64\tdunix_pos;\n\tword16\tunix_len;\n\tword16\tdirty;\n\tword32\ttrack_bits;\n};\n\nSTRUCT(Woz_info) {\n\tbyte\t*wozptr;\n\tword32\twoz_size;\n\tint\tversion;\n\tint\treparse_needed;\n\tword32\tmax_trk_blocks;\n\tint\tmeta_size;\n\tint\ttrks_size;\n\tint\ttmap_offset;\n\tint\ttrks_offset;\n\tint\tinfo_offset;\n\tint\tmeta_offset;\n};\n\ntypedef struct Dynapro_map_st Dynapro_map;\n\nSTRUCT(Dynapro_file) {\n\tDynapro_file *next_ptr;\n\tDynapro_file *parent_ptr;\n\tDynapro_file *subdir_ptr;\n\tchar\t*unix_path;\n\tbyte\t*buffer_ptr;\n\tbyte\tprodos_name[17];\t// +0x00-0x0f: [0] is len, nul at end\n\tword32\tdir_byte;\t\t// Byte address of this file's dir ent\n\tword32\teof;\t\t\t// +0x15-0x17\n\tword32\tblocks_used;\t\t// +0x13-0x14\n\tword32\tcreation_time;\t\t// +0x18-0x1b\n\tword32\tlastmod_time;\t\t// +0x21-0x24\n\tword16\tupper_lower;\t\t// +0x1c-0x1d: Versions: lowercase flags\n\tword16\tkey_block;\t\t// +0x11-0x12\n\tword16\taux_type;\t\t// +0x1f-0x20\n\tword16\theader_pointer;\t\t// +0x25-0x26\n\tword16\tmap_first_block;\n\tbyte\tfile_type;\t\t// +0x10\n\tbyte\tmodified_flag;\n\tbyte\tdamaged;\n};\n\nstruct Dynapro_map_st {\n\tDynapro_file *file_ptr;\n\tword16\tnext_map_block;\n\tword16\tmodified;\n};\n\nSTRUCT(Dynapro_info) {\n\tchar\t*root_path;\n\tDynapro_file *volume_ptr;\n\tDynapro_map *block_map_ptr;\n\tint\tdamaged;\n};\n\nSTRUCT(Disk) {\n\tdword64\tdfcyc_last_read;\n\tbyte\t*raw_data;\n\tWoz_info *wozinfo_ptr;\n\tDynapro_info *dynapro_info_ptr;\n\tchar\t*name_ptr;\n\tchar\t*partition_name;\n\tint\tpartition_num;\n\tint\tfd;\n\tword32\tdynapro_blocks;\n\tdword64\traw_dsize;\n\tdword64\tdimage_start;\n\tdword64\tdimage_size;\n\tint\tsmartport;\n\tint\tdisk_525;\n\tint\tdrive;\n\tword32\tcur_frac_track;\n\tint\timage_type;\n\tint\tvol_num;\n\tint\twrite_prot;\n\tint\twrite_through_to_unix;\n\tint\tdisk_dirty;\n\tint\tjust_ejected;\n\tint\tlast_phases;\n\tdword64\tdfcyc_last_phases;\n\tword32\tcur_fbit_pos;\n\tword32\tfbit_mult;\n\tword32\tcur_track_bits;\n\tint\traw_bptr_malloc;\n\tTrk\t*cur_trk_ptr;\n\tint\tnum_tracks;\n\tTrk\t*trks;\n};\n\nSTRUCT(Iwm) {\n\tDisk\tdrive525[2];\n\tDisk\tdrive35[2];\n\tDisk\tsmartport[MAX_C7_DISKS];\n\tdword64\tdfcyc_last_fastemul_read;\n\tword32\tstate;\n\tword32\tmotor_off_vbl_count;\n\tword32\tforced_sync_bit;\n\tword32\tlast_rd_bit;\n\tword32\twrite_val;\n\tword32\twr_last_bit[5];\n\tword32\twr_qtr_track[5];\n\tword32\twr_num_bits[5];\n\tword32\twr_prior_num_bits[5];\n\tword32\twr_delta[5];\n\tint\tnum_active_writes;\n};\n\nSTRUCT(Driver_desc) {\n\tword16\tsig;\n\tword16\tblk_size;\n\tword32\tblk_count;\n\tword16\tdev_type;\n\tword16\tdev_id;\n\tword32\tdata;\n\tword16\tdrvr_count;\n};\n\nSTRUCT(Part_map) {\n\tword16\tsig;\n\tword16\tsigpad;\n\tword32\tmap_blk_cnt;\n\tword32\tphys_part_start;\n\tword32\tpart_blk_cnt;\n\tchar\tpart_name[32];\n\tchar\tpart_type[32];\n\tword32\tdata_start;\n\tword32\tdata_cnt;\n\tword32\tpart_status;\n\tword32\tlog_boot_start;\n\tword32\tboot_size;\n\tword32\tboot_load;\n\tword32\tboot_load2;\n\tword32\tboot_entry;\n\tword32\tboot_entry2;\n\tword32\tboot_cksum;\n\tchar\tprocessor[16];\n\tchar\tjunk[128];\n};\n\n"
  },
  {
    "path": "upstream/kegs/src/joystick_driver.c",
    "content": "const char rcsid_joystick_driver_c[] = \"@(#)$KmKId: joystick_driver.c,v 1.23 2023-09-26 02:59:00+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include \"defc.h\"\n\n#ifdef __linux__\n# include <linux/joystick.h>\n#endif\n\n#ifdef _WIN32\n# include <windows.h>\n# include <mmsystem.h>\n#endif\n\nextern int g_joystick_native_type1;\t\t/* in paddles.c */\nextern int g_joystick_native_type2;\t\t/* in paddles.c */\nextern int g_joystick_native_type;\t\t/* in paddles.c */\nextern int g_paddle_buttons;\nextern int g_paddle_val[];\n\n\nconst char *g_joystick_dev = \"/dev/js0\";\t/* default joystick dev file */\n#define MAX_JOY_NAME\t128\n\nint\tg_joystick_native_fd = -1;\nint\tg_joystick_num_axes = 0;\nint\tg_joystick_num_buttons = 0;\n\nint\tg_joystick_callback_buttons = 0;\nint\tg_joystick_callback_x = 32767;\nint\tg_joystick_callback_y = 32767;\n\n#ifdef __linux__\n# define JOYSTICK_DEFINED\nvoid\njoystick_init()\n{\n\tchar\tjoy_name[MAX_JOY_NAME];\n\tint\tversion, fd;\n\tint\ti;\n\n\tfd = open(g_joystick_dev, O_RDONLY | O_NONBLOCK);\n\tif(fd < 0) {\n\t\tprintf(\"Unable to open joystick dev file: %s, errno: %d\\n\",\n\t\t\tg_joystick_dev, errno);\n\t\tprintf(\"Defaulting to mouse joystick\\n\");\n\t\treturn;\n\t}\n\n\tstrcpy(&joy_name[0], \"Unknown Joystick\");\n\tversion = 0x800;\n\n\tioctl(fd, JSIOCGNAME(MAX_JOY_NAME), &joy_name[0]);\n\tioctl(fd, JSIOCGAXES, &g_joystick_num_axes);\n\tioctl(fd, JSIOCGBUTTONS, &g_joystick_num_buttons);\n\tioctl(fd, JSIOCGVERSION, &version);\n\n\tprintf(\"Detected joystick: %s [%d axes, %d buttons vers: %08x]\\n\",\n\t\tjoy_name, g_joystick_num_axes, g_joystick_num_buttons,\n\t\tversion);\n\n\tg_joystick_native_type1 = 1;\n\tg_joystick_native_type2 = -1;\n\tg_joystick_native_fd = fd;\n\tfor(i = 0; i < 4; i++) {\n\t\tg_paddle_val[i] = 32767;\n\t}\n\tg_paddle_buttons = 0xc;\n}\n\n/* joystick_update_linux() called from paddles.c.  Update g_paddle_val[] */\n/*  and g_paddle_buttons with current information */\nvoid\njoystick_update(dword64 dfcyc)\n{\n\tstruct js_event js;\t/* the linux joystick event record */\n\tint\tmask, val, num, type, ret, len;\n\tint\ti;\n\n\t/* suck up to 20 events, then give up */\n\tlen = sizeof(struct js_event);\n\tfor(i = 0; i < 20; i++) {\n\t\tret = read(g_joystick_native_fd, &js, len);\n\t\tif(ret != len) {\n\t\t\t/* just get out */\n\t\t\tbreak;\n\t\t}\n\t\ttype = js.type & ~JS_EVENT_INIT;\n\t\tval = js.value;\n\t\tnum = js.number & 3;\t\t/* clamp to 0-3 */\n\t\tswitch(type) {\n\t\tcase JS_EVENT_BUTTON:\n\t\t\tmask = 1 << num;\n\t\t\tif(val) {\n\t\t\t\tval = mask;\n\t\t\t}\n\t\t\tg_paddle_buttons = (g_paddle_buttons & ~mask) | val;\n\t\t\tbreak;\n\t\tcase JS_EVENT_AXIS:\n\t\t\t/* val is -32767 to +32767 */\n\t\t\tg_paddle_val[num] = val;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(i > 0) {\n\t\tpaddle_update_trigger_dcycs(dfcyc);\n\t}\n}\n\nvoid\njoystick_update_buttons()\n{\n}\n#endif /* LINUX */\n\n#ifdef _WIN32\n# define JOYSTICK_DEFINED\n#undef JOYSTICK_DEFINED\n\t// HACK: remove\n#if 0\nvoid\njoystick_init()\n{\n\tJOYINFO info;\n\tJOYCAPS joycap;\n\tMMRESULT ret1, ret2;\n\tint\ti;\n\n\t//\tCheck that there is a joystick device\n\tif(joyGetNumDevs() <= 0) {\n\t\tprintf(\"No joystick hardware detected\\n\");\n\t\tg_joystick_native_type1 = -1;\n\t\tg_joystick_native_type2 = -1;\n\t\treturn;\n\t}\n\n\tg_joystick_native_type1 = -1;\n\tg_joystick_native_type2 = -1;\n\n\t//\tCheck that at least joystick 1 or joystick 2 is available\n\tret1 = joyGetPos(JOYSTICKID1, &info);\n\tret2 = joyGetDevCaps(JOYSTICKID1, &joycap, sizeof(joycap));\n\tif(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {\n\t\tg_joystick_native_type1 = JOYSTICKID1;\n\t\tprintf(\"Joystick #1 = %s\\n\", joycap.szPname);\n\t\tg_joystick_native_type = JOYSTICKID1;\n\t}\n\tret1 = joyGetPos(JOYSTICKID2, &info);\n\tret2 = joyGetDevCaps(JOYSTICKID2, &joycap, sizeof(joycap));\n\tif(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {\n\t\tg_joystick_native_type2 = JOYSTICKID2;\n\t\tprintf(\"Joystick #2 = %s\\n\", joycap.szPname);\n\t\tif(g_joystick_native_type < 0) {\n\t\t\tg_joystick_native_type = JOYSTICKID2;\n\t\t}\n\t}\n\n\tfor(i = 0; i < 4; i++) {\n\t\tg_paddle_val[i] = 32767;\n\t}\n\tg_paddle_buttons = 0xc;\n}\n\nvoid\njoystick_update(dword64 dfcyc)\n{\n\tJOYCAPS joycap;\n\tJOYINFO info;\n\tUINT\tid;\n\tMMRESULT ret1, ret2;\n\n\tid = g_joystick_native_type;\n\n\tret1 = joyGetDevCaps(id, &joycap, sizeof(joycap));\n\tret2 = joyGetPos(id, &info);\n\tif(ret1 == JOYERR_NOERROR && ret2 == JOYERR_NOERROR) {\n\t\tg_paddle_val[0] = (info.wXpos - joycap.wXmin) * 32768 /\n\t\t\t\t\t\t(joycap.wXmax - joycap.wXmin);\n\t\tg_paddle_val[1] = (info.wYpos - joycap.wYmin) * 32768 /\n\t\t\t\t\t\t(joycap.wYmax - joycap.wYmin);\n\t\tif(info.wButtons & JOY_BUTTON1) {\n\t\t\tg_paddle_buttons = g_paddle_buttons | 1;\n\t\t} else {\n\t\t\tg_paddle_buttons = g_paddle_buttons & (~1);\n\t\t}\n\t\tif(info.wButtons & JOY_BUTTON2) {\n\t\t\tg_paddle_buttons = g_paddle_buttons | 2;\n\t\t} else {\n\t\t\tg_paddle_buttons = g_paddle_buttons & (~2);\n\t\t}\n\t\tpaddle_update_trigger_dcycs(dfcyc);\n\t}\n}\n\nvoid\njoystick_update_buttons()\n{\n\tJOYINFOEX info;\n\tUINT id;\n\n\tid = g_joystick_native_type;\n\n\tinfo.dwSize = sizeof(JOYINFOEX);\n\tinfo.dwFlags = JOY_RETURNBUTTONS;\n\tif(joyGetPosEx(id, &info) == JOYERR_NOERROR) {\n\t\tif(info.dwButtons & JOY_BUTTON1) {\n\t\t\tg_paddle_buttons = g_paddle_buttons | 1;\n\t\t} else {\n\t\t\tg_paddle_buttons = g_paddle_buttons & (~1);\n\t\t}\n\t\tif(info.dwButtons & JOY_BUTTON2) {\n\t\t\tg_paddle_buttons = g_paddle_buttons | 2;\n\t\t} else {\n\t\t\tg_paddle_buttons = g_paddle_buttons & (~2);\n\t\t}\n\t}\n}\n#endif\n#endif\n\n#ifdef MAC\n# define JOYSTICK_DEFINED\n\n#include <IOKit/IOKitLib.h>\n#include <IOKit/hid/IOHIDManager.h>\n#include <IOKit/hid/IOHIDKeys.h>\n#include <IOKit/usb/USBSpec.h>\n#include <CoreFoundation/CoreFoundation.h>\n// Headers are at: /Applications/Xcode.app/Contents/Developer/Platforms/\n//      MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/\n//      Frameworks/IOKit.framework/Headers\n// Thanks to VirtualC64 and hidapi library for coding example\n\nCFIndex g_joystick_min = 0;\nCFIndex g_joystick_range = 256;\nint\tg_joystick_minmax_valid = 0;\nint\tg_joystick_dummy = 0;\n\nvoid\nhid_device_callback(void *ptr, IOReturn result, void *sender,\n\t\t\t\t\t\tIOHIDValueRef value)\n{\n\tIOHIDElementRef element;\n\tword32\tmask;\n\tint\tusage_page, usage, ival, button;\n\n\t// This is a callback routine, and it's unclear to me what the state is\n\t//  For safety, do no printfs() (other than for debug).\n\tif((ptr || result || sender || 1) == 0) {\n\t\tprintf(\"Bad\\n\");\t\t// Avoid unused var warning\n\t}\n\n\telement = IOHIDValueGetElement(value);\n\tusage_page = IOHIDElementGetUsagePage(element);\n\tusage = IOHIDElementGetUsage(element);\n\tival = IOHIDValueGetIntegerValue(value);\n#if 0\n\tprintf(\" usage_page:%d, usage:%d, value:%d\\n\", usage_page, usage, ival);\n#endif\n\tif((usage_page == kHIDPage_GenericDesktop) &&\n\t\t\t\t((usage >= kHIDUsage_GD_X) &&\n\t\t\t\t(usage <= kHIDUsage_GD_Y)) &&\n\t\t\t\t!g_joystick_minmax_valid) {\n\t\tg_joystick_min = IOHIDElementGetLogicalMin(element);\n\t\tg_joystick_range = IOHIDElementGetLogicalMax(element) + 1 -\n\t\t\t\t\t\t\tg_joystick_min;\n\t\t// printf(\"min:%lld range:%lld\\n\", (dword64)g_joystick_min,\n\t\t//\t\t\t\t(dword64)g_joystick_range);\n\t\tif(g_joystick_range == 0) {\n\t\t\tg_joystick_range = 1;\n\t\t}\n\t\tg_joystick_minmax_valid = 1;\n\t}\n\tif((usage_page == kHIDPage_GenericDesktop) &&\n\t\t\t\t\t\t(usage == kHIDUsage_GD_X)) {\n\t\tg_joystick_callback_x = ((ival * 65536) / g_joystick_range) -\n\t\t\t\t\t\t\t\t\t32768;\n\t\t//printf(\"g_joystick_callback_x = %d\\n\", g_joystick_callback_x);\n\t}\n\tif((usage_page == kHIDPage_GenericDesktop) &&\n\t\t\t\t\t\t(usage == kHIDUsage_GD_Y)) {\n\t\tg_joystick_callback_y = ((ival * 65536) / g_joystick_range) -\n\t\t\t\t\t\t\t\t\t32768;\n\t\t//printf(\"g_joystick_callback_y = %d\\n\", g_joystick_callback_y);\n\t}\n\tif((usage_page == kHIDPage_Button) && (usage >= 1) && (usage <= 10)) {\n\t\t// Buttons: usage=1 is button 0, usage=2 is button 1, etc.\n\t\tbutton = (~usage) & 1;\n\t\tmask = 1 << button;\n\t\t//printf(\"Button %d (%d) pressed:%d\\n\", button, usage, ival);\n\t\tif(ival == 0) {\t\t// Button released\n\t\t\tg_joystick_callback_buttons &= (~mask);\n\t\t} else {\t\t// Button pressed\n\t\t\tg_joystick_callback_buttons |= mask;\n\t\t}\n\t}\n}\n\nint\nhid_get_int_property(IOHIDDeviceRef device, CFStringRef key_cfstr)\n{\n\tCFTypeRef ref;\n\tBoolean\tbret;\n\tint\tint_val;\n\n\tref = IOHIDDeviceGetProperty(device, key_cfstr);\n\tif(ref) {\n\t\tbret = CFNumberGetValue((CFNumberRef)ref, kCFNumberIntType,\n\t\t\t\t\t\t\t\t&int_val);\n\t\tif(bret) {\n\t\t\treturn int_val;\n\t\t}\n\t}\n\treturn 0;\n}\n\nvoid\njoystick_init()\n{\n\tIOHIDManagerRef hid_mgr;\n\tCFSetRef devices_set;\n\tCFIndex\tnum_devices;\n\tIOHIDDeviceRef *devices_array, device;\n\tint\tvendor, usage_page, usage;\n\tint\ti;\n\n\tg_joystick_native_type1 = -1;\n\tg_joystick_native_type2 = -1;\n\n\thid_mgr = IOHIDManagerCreate(kCFAllocatorDefault,\n\t\t\t\t\t\t\tkIOHIDOptionsTypeNone);\n\tIOHIDManagerSetDeviceMatching(hid_mgr, 0);\n\tIOHIDManagerOpen(hid_mgr, kIOHIDOptionsTypeNone);\n\n\tdevices_set = IOHIDManagerCopyDevices(hid_mgr);\n\tnum_devices = CFSetGetCount(devices_set);\n\n\t// Sets are hashtables, so we cannot directly access it.  The only way\n\t//  to iterate over all values is to use CFSetGetValues to get a simple\n\t//  array of the values, and iterate over that\n\tdevices_array = calloc(num_devices, sizeof(IOHIDDeviceRef));\n\tCFSetGetValues(devices_set, (const void **)devices_array);\n\n\tfor(i = 0; i < num_devices; i++) {\n\t\tdevice = devices_array[i];\n\t\tvendor = hid_get_int_property(device, CFSTR(kIOHIDVendorIDKey));\n\t\t// printf(\" vendor: %d\\n\", vendor);\n\t\tusage_page = hid_get_int_property(device,\n\t\t\t\t\tCFSTR(kIOHIDDeviceUsagePageKey));\n\t\tusage = hid_get_int_property(device,\n\t\t\t\t\tCFSTR(kIOHIDDeviceUsageKey));\n\t\t// printf(\" usage_page:%d, usage:%d\\n\", usage_page, usage);\n\t\tusage_page = hid_get_int_property(device,\n\t\t\t\t\tCFSTR(kIOHIDPrimaryUsagePageKey));\n\t\tusage = hid_get_int_property(device,\n\t\t\t\t\tCFSTR(kIOHIDPrimaryUsageKey));\n\t\t// printf(\" primary_usage_page:%d, usage:%d\\n\", usage_page,\n\t\t//\t\t\t\t\t\tusage);\n\t\tif(usage_page != kHIDPage_GenericDesktop) {\n\t\t\tcontinue;\n\t\t}\n\t\tif((usage != kHIDUsage_GD_Joystick) &&\n\t\t\t\t(usage != kHIDUsage_GD_GamePad) &&\n\t\t\t\t(usage != kHIDUsage_GD_MultiAxisController)) {\n\t\t\tcontinue;\n\t\t}\n\t\tprintf(\" JOYSTICK FOUND, vendor:%08x!\\n\", vendor);\n\t\tIOHIDDeviceOpen(device, kIOHIDOptionsTypeNone);\n\t\tIOHIDDeviceScheduleWithRunLoop(device, CFRunLoopGetCurrent(),\n\t\t\t\t\t\t\tkCFRunLoopCommonModes);\n\t\tIOHIDDeviceRegisterInputValueCallback(device,\n\t\t\t\t\thid_device_callback, 0);\n\t\tg_joystick_native_type1 = 1;\n\t\treturn;\n\t\t// Now, hid_device_callback will be called whenever a joystick\n\t\t//  value changes.  Only set global variables for joystick.\n\t}\n}\n\nvoid\njoystick_update(dword64 dfcyc)\n{\n\tint\ti;\n\n\tif(dfcyc) {\n\t\t// Avoid unused parameter warnings\n\t}\n\tfor(i = 0; i < 4; i++) {\n\t\tg_paddle_val[i] = 32767;\n\t}\n\tg_paddle_buttons = 0xc;\n\tif(g_joystick_native_type1 >= 0) {\n\t\tg_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3);\n\t\tg_paddle_val[0] = g_joystick_callback_x;\n\t\tg_paddle_val[1] = g_joystick_callback_y;\n\t\tpaddle_update_trigger_dcycs(dfcyc);\n\t}\n}\n\nvoid\njoystick_update_buttons()\n{\n\tif(g_joystick_native_type1 >= 0) {\n\t\tg_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3);\n\t}\n}\n#endif\n\n#ifndef JOYSTICK_DEFINED\n/* stubs for the routines */\nvoid\njoystick_init()\n{\n\tg_joystick_native_type1 = -1;\n\tg_joystick_native_type2 = -1;\n\tg_joystick_native_type = -1;\n}\n\nvoid\njoystick_update(dword64 dfcyc)\n{\n\tint\ti;\n\n\tif(dfcyc) {\n\t\t// Avoid unused parameter warnings\n\t}\n\tfor(i = 0; i < 4; i++) {\n\t\tg_paddle_val[i] = 32767;\n\t}\n\tg_paddle_buttons = 0xc;\n\tif(g_joystick_native_type1 >= 0) {\n\t\tg_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3);\n\t\tg_paddle_val[0] = g_joystick_callback_x;\n\t\tg_paddle_val[1] = g_joystick_callback_y;\n\t\tpaddle_update_trigger_dcycs(dfcyc);\n\t}\n}\n\nvoid\njoystick_update_buttons()\n{\n\tif(g_joystick_native_type1 >= 0) {\n\t\tg_paddle_buttons = 0xc | (g_joystick_callback_buttons & 3);\n\t}\n}\n#endif\n\nvoid\njoystick_callback_init(int native_type)\n{\n\tg_joystick_native_type1 = native_type;\n}\n\nvoid\njoystick_callback_update(word32 buttons, int paddle_x, int paddle_y)\n{\n\tg_joystick_callback_buttons = (g_paddle_buttons & (~3)) | (buttons & 3);\n\tg_joystick_callback_x = paddle_x;\n\tg_joystick_callback_y = paddle_y;\n}\n"
  },
  {
    "path": "upstream/kegs/src/kegsfont.h",
    "content": "/* $KmKId: kegsfont.h,v 1.1 2002-11-10 03:31:51-05 kadickey Exp $ */\n\n/* char 0x00 (raw 0x40)  */\n\t{ 0xc7, 0xbb, 0xab, 0xa3, 0xa7, 0xbf, 0xc3, 0xff },\n/* char 0x01 (raw 0x41)  */\n\t{ 0xef, 0xd7, 0xbb, 0xbb, 0x83, 0xbb, 0xbb, 0xff },\n/* char 0x02 (raw 0x42)  */\n\t{ 0x87, 0xbb, 0xbb, 0x87, 0xbb, 0xbb, 0x87, 0xff },\n/* char 0x03 (raw 0x43)  */\n\t{ 0xc7, 0xbb, 0xbf, 0xbf, 0xbf, 0xbb, 0xc7, 0xff },\n/* char 0x04 (raw 0x44)  */\n\t{ 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x87, 0xff },\n/* char 0x05 (raw 0x45)  */\n\t{ 0x83, 0xbf, 0xbf, 0x87, 0xbf, 0xbf, 0x83, 0xff },\n/* char 0x06 (raw 0x46)  */\n\t{ 0x83, 0xbf, 0xbf, 0x87, 0xbf, 0xbf, 0xbf, 0xff },\n/* char 0x07 (raw 0x47)  */\n\t{ 0xc3, 0xbf, 0xbf, 0xbf, 0xb3, 0xbb, 0xc3, 0xff },\n/* char 0x08 (raw 0x48)  */\n\t{ 0xbb, 0xbb, 0xbb, 0x83, 0xbb, 0xbb, 0xbb, 0xff },\n/* char 0x09 (raw 0x49)  */\n\t{ 0xc7, 0xef, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff },\n/* char 0x0a (raw 0x4a)  */\n\t{ 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xbb, 0xc7, 0xff },\n/* char 0x0b (raw 0x4b)  */\n\t{ 0xbb, 0xb7, 0xaf, 0x9f, 0xaf, 0xb7, 0xbb, 0xff },\n/* char 0x0c (raw 0x4c)  */\n\t{ 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0x83, 0xff },\n/* char 0x0d (raw 0x4d)  */\n\t{ 0xbb, 0x93, 0xab, 0xab, 0xbb, 0xbb, 0xbb, 0xff },\n/* char 0x0e (raw 0x4e)  */\n\t{ 0xbb, 0xbb, 0x9b, 0xab, 0xb3, 0xbb, 0xbb, 0xff },\n/* char 0x0f (raw 0x4f)  */\n\t{ 0xc7, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xc7, 0xff },\n/* char 0x10 (raw 0x50)  */\n\t{ 0x87, 0xbb, 0xbb, 0x87, 0xbf, 0xbf, 0xbf, 0xff },\n/* char 0x11 (raw 0x51)  */\n\t{ 0xc7, 0xbb, 0xbb, 0xbb, 0xab, 0xb7, 0xcb, 0xff },\n/* char 0x12 (raw 0x52)  */\n\t{ 0x87, 0xbb, 0xbb, 0x87, 0xaf, 0xb7, 0xbb, 0xff },\n/* char 0x13 (raw 0x53)  */\n\t{ 0xc7, 0xbb, 0xbf, 0xc7, 0xfb, 0xbb, 0xc7, 0xff },\n/* char 0x14 (raw 0x54)  */\n\t{ 0x83, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xff },\n/* char 0x15 (raw 0x55)  */\n\t{ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xc7, 0xff },\n/* char 0x16 (raw 0x56)  */\n\t{ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xd7, 0xef, 0xff },\n/* char 0x17 (raw 0x57)  */\n\t{ 0xbb, 0xbb, 0xbb, 0xab, 0xab, 0x93, 0xbb, 0xff },\n/* char 0x18 (raw 0x58)  */\n\t{ 0xbb, 0xbb, 0xd7, 0xef, 0xd7, 0xbb, 0xbb, 0xff },\n/* char 0x19 (raw 0x59)  */\n\t{ 0xbb, 0xbb, 0xd7, 0xef, 0xef, 0xef, 0xef, 0xff },\n/* char 0x1a (raw 0x5a)  */\n\t{ 0x83, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x83, 0xff },\n/* char 0x1b (raw 0x5b)  */\n\t{ 0x83, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x83, 0xff },\n/* char 0x1c (raw 0x5c)  */\n\t{ 0xff, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xff, 0xff },\n/* char 0x1d (raw 0x5d)  */\n\t{ 0x83, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0x83, 0xff },\n/* char 0x1e (raw 0x5e)  */\n\t{ 0xff, 0xff, 0xef, 0xd7, 0xbb, 0xff, 0xff, 0xff },\n/* char 0x1f (raw 0x5f)  */\n\t{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01 },\n/* char 0x20 (raw 0x20)  */\n\t{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },\n/* char 0x21 (raw 0x21)  */\n\t{ 0xef, 0xef, 0xef, 0xef, 0xef, 0xff, 0xef, 0xff },\n/* char 0x22 (raw 0x22)  */\n\t{ 0xd7, 0xd7, 0xd7, 0xff, 0xff, 0xff, 0xff, 0xff },\n/* char 0x23 (raw 0x23)  */\n\t{ 0xd7, 0xd7, 0x83, 0xd7, 0x83, 0xd7, 0xd7, 0xff },\n/* char 0x24 (raw 0x24)  */\n\t{ 0xef, 0xc3, 0xaf, 0xc7, 0xeb, 0x87, 0xef, 0xff },\n/* char 0x25 (raw 0x25)  */\n\t{ 0x9f, 0x9b, 0xf7, 0xef, 0xdf, 0xb3, 0xf3, 0xff },\n/* char 0x26 (raw 0x26)  */\n\t{ 0xdf, 0xaf, 0xaf, 0xdf, 0xab, 0xb7, 0xcb, 0xff },\n/* char 0x27 (raw 0x27)  */\n\t{ 0xef, 0xef, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff },\n/* char 0x28 (raw 0x28)  */\n\t{ 0xef, 0xdf, 0xbf, 0xbf, 0xbf, 0xdf, 0xef, 0xff },\n/* char 0x29 (raw 0x29)  */\n\t{ 0xef, 0xf7, 0xfb, 0xfb, 0xfb, 0xf7, 0xef, 0xff },\n/* char 0x2a (raw 0x2a)  */\n\t{ 0xef, 0xab, 0xc7, 0xef, 0xc7, 0xab, 0xef, 0xff },\n/* char 0x2b (raw 0x2b)  */\n\t{ 0xff, 0xef, 0xef, 0x83, 0xef, 0xef, 0xff, 0xff },\n/* char 0x2c (raw 0x2c)  */\n\t{ 0xff, 0xff, 0xff, 0xff, 0xef, 0xef, 0xdf, 0xff },\n/* char 0x2d (raw 0x2d)  */\n\t{ 0xff, 0xff, 0xff, 0x83, 0xff, 0xff, 0xff, 0xff },\n/* char 0x2e (raw 0x2e)  */\n\t{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff },\n/* char 0x2f (raw 0x2f)  */\n\t{ 0xff, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0xff, 0xff },\n/* char 0x30 (raw 0x30)  */\n\t{ 0xc7, 0xbb, 0xb3, 0xab, 0x9b, 0xbb, 0xc7, 0xff },\n/* char 0x31 (raw 0x31)  */\n\t{ 0xef, 0xcf, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff },\n/* char 0x32 (raw 0x32)  */\n\t{ 0xc7, 0xbb, 0xfb, 0xe7, 0xdf, 0xbf, 0x83, 0xff },\n/* char 0x33 (raw 0x33)  */\n\t{ 0x83, 0xfb, 0xf7, 0xe7, 0xfb, 0xbb, 0xc7, 0xff },\n/* char 0x34 (raw 0x34)  */\n\t{ 0xf7, 0xe7, 0xd7, 0xb7, 0x83, 0xf7, 0xf7, 0xff },\n/* char 0x35 (raw 0x35)  */\n\t{ 0x83, 0xbf, 0x87, 0xfb, 0xfb, 0xbb, 0xc7, 0xff },\n/* char 0x36 (raw 0x36)  */\n\t{ 0xe3, 0xdf, 0xbf, 0x87, 0xbb, 0xbb, 0xc7, 0xff },\n/* char 0x37 (raw 0x37)  */\n\t{ 0x83, 0xfb, 0xf7, 0xef, 0xdf, 0xdf, 0xdf, 0xff },\n/* char 0x38 (raw 0x38)  */\n\t{ 0xc7, 0xbb, 0xbb, 0xc7, 0xbb, 0xbb, 0xc7, 0xff },\n/* char 0x39 (raw 0x39)  */\n\t{ 0xc7, 0xbb, 0xbb, 0xc3, 0xfb, 0xf7, 0x8f, 0xff },\n/* char 0x3a (raw 0x3a)  */\n\t{ 0xff, 0xff, 0xef, 0xff, 0xef, 0xff, 0xff, 0xff },\n/* char 0x3b (raw 0x3b)  */\n\t{ 0xff, 0xff, 0xef, 0xff, 0xef, 0xef, 0xdf, 0xff },\n/* char 0x3c (raw 0x3c)  */\n\t{ 0xf7, 0xef, 0xdf, 0xbf, 0xdf, 0xef, 0xf7, 0xff },\n/* char 0x3d (raw 0x3d)  */\n\t{ 0xff, 0xff, 0x83, 0xff, 0x83, 0xff, 0xff, 0xff },\n/* char 0x3e (raw 0x3e)  */\n\t{ 0xdf, 0xef, 0xf7, 0xfb, 0xf7, 0xef, 0xdf, 0xff },\n/* char 0x3f (raw 0x3f)  */\n\t{ 0xc7, 0xbb, 0xf7, 0xef, 0xef, 0xff, 0xef, 0xff },\n/* char 0x40 (raw 0x14)  */\n\t{ 0x08, 0x10, 0x6c, 0xfe, 0xfc, 0xfc, 0x7e, 0x6c },\n/* char 0x41 (raw 0x11)  */\n\t{ 0x08, 0x10, 0x6c, 0x82, 0x84, 0x84, 0x52, 0x6c },\n/* char 0x42 (raw 0xf5)  */\n\t{ 0x00, 0x00, 0x40, 0x60, 0x70, 0x78, 0x6c, 0x42 },\n/* char 0x43 (raw 0x82)  */\n\t{ 0xfe, 0x44, 0x28, 0x10, 0x10, 0x28, 0x54, 0xfe },\n/* char 0x44 (raw 0xeb)  */\n\t{ 0x00, 0x02, 0x04, 0x88, 0x50, 0x20, 0x20, 0x00 },\n/* char 0x45 (raw 0xe4)  */\n\t{ 0xfe, 0xfc, 0xfa, 0x36, 0xae, 0xde, 0xde, 0xfe },\n/* char 0x46 (raw 0xec)  */\n\t{ 0xfc, 0xfc, 0xfc, 0xdc, 0x9c, 0x00, 0x9e, 0xde },\n/* char 0x47 (raw 0xed)  */\n\t{ 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0x00, 0xfe },\n/* char 0x48 (raw 0xee)  */\n\t{ 0x10, 0x20, 0x40, 0xfe, 0x40, 0x20, 0x10, 0x00 },\n/* char 0x49 (raw 0xe9)  */\n\t{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54 },\n/* char 0x4a (raw 0xef)  */\n\t{ 0x10, 0x10, 0x10, 0x10, 0x92, 0x54, 0x38, 0x10 },\n/* char 0x4b (raw 0xf0)  */\n\t{ 0x10, 0x38, 0x54, 0x92, 0x10, 0x10, 0x10, 0x10 },\n/* char 0x4c (raw 0xf1)  */\n\t{ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },\n/* char 0x4d (raw 0xf7)  */\n\t{ 0x02, 0x02, 0x02, 0x22, 0x62, 0xfe, 0x60, 0x20 },\n/* char 0x4e (raw 0xf6)  */\n\t{ 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc },\n/* char 0x4f (raw 0xaf)  */\n\t{ 0xc8, 0x18, 0x38, 0x7e, 0x38, 0x18, 0x08, 0xf6 },\n/* char 0x50 (raw 0xb8)  */\n\t{ 0x26, 0x30, 0x38, 0xfc, 0x38, 0x30, 0x20, 0xde },\n/* char 0x51 (raw 0xce)  */\n\t{ 0x02, 0x12, 0x10, 0xfe, 0x7c, 0x38, 0x12, 0x02 },\n/* char 0x52 (raw 0xe5)  */\n\t{ 0x02, 0x12, 0x38, 0x7c, 0xfe, 0x10, 0x12, 0x02 },\n/* char 0x53 (raw 0xea)  */\n\t{ 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00 },\n/* char 0x54 (raw 0xe6)  */\n\t{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xfe },\n/* char 0x55 (raw 0xe8)  */\n\t{ 0x10, 0x08, 0x04, 0xfe, 0x04, 0x08, 0x10, 0x00 },\n/* char 0x56 (raw 0xd7)  */\n\t{ 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa },\n/* char 0x57 (raw 0xe3)  */\n\t{ 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54, 0xaa, 0x54 },\n/* char 0x58 (raw 0xf4)  */\n\t{ 0x00, 0x7c, 0x82, 0x80, 0x80, 0x80, 0xfe, 0x00 },\n/* char 0x59 (raw 0xe7)  */\n\t{ 0x00, 0x00, 0xfc, 0x02, 0x02, 0x02, 0xfe, 0x00 },\n/* char 0x5a (raw 0xf3)  */\n\t{ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 },\n/* char 0x5b (raw 0xd2)  */\n\t{ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00 },\n/* char 0x5c (raw 0xc7)  */\n\t{ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe },\n/* char 0x5d (raw 0xd4)  */\n\t{ 0x28, 0x28, 0xee, 0x00, 0xee, 0x28, 0x28, 0x00 },\n/* char 0x5e (raw 0xdf)  */\n\t{ 0xfe, 0x02, 0x02, 0x32, 0x32, 0x02, 0x02, 0xfe },\n/* char 0x5f (raw 0xd1)  */\n\t{ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 },\n/* char 0x60 (raw 0x60)  */\n\t{ 0xdf, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff },\n/* char 0x61 (raw 0x61)  */\n\t{ 0xff, 0xff, 0xc7, 0xfb, 0xc3, 0xbb, 0xc3, 0xff },\n/* char 0x62 (raw 0x62)  */\n\t{ 0xbf, 0xbf, 0x87, 0xbb, 0xbb, 0xbb, 0x87, 0xff },\n/* char 0x63 (raw 0x63)  */\n\t{ 0xff, 0xff, 0xc3, 0xbf, 0xbf, 0xbf, 0xc3, 0xff },\n/* char 0x64 (raw 0x64)  */\n\t{ 0xfb, 0xfb, 0xc3, 0xbb, 0xbb, 0xbb, 0xc3, 0xff },\n/* char 0x65 (raw 0x65)  */\n\t{ 0xff, 0xff, 0xc7, 0xbb, 0x83, 0xbf, 0xc3, 0xff },\n/* char 0x66 (raw 0x66)  */\n\t{ 0xe7, 0xdb, 0xdf, 0x87, 0xdf, 0xdf, 0xdf, 0xff },\n/* char 0x67 (raw 0x67)  */\n\t{ 0xff, 0xff, 0xc7, 0xbb, 0xbb, 0xc3, 0xfb, 0xc7 },\n/* char 0x68 (raw 0x68)  */\n\t{ 0xbf, 0xbf, 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xff },\n/* char 0x69 (raw 0x69)  */\n\t{ 0xef, 0xff, 0xcf, 0xef, 0xef, 0xef, 0xc7, 0xff },\n/* char 0x6a (raw 0x6a)  */\n\t{ 0xf7, 0xff, 0xe7, 0xf7, 0xf7, 0xf7, 0xb7, 0xcf },\n/* char 0x6b (raw 0x6b)  */\n\t{ 0xbf, 0xbf, 0xbb, 0xb7, 0x8f, 0xb7, 0xbb, 0xff },\n/* char 0x6c (raw 0x6c)  */\n\t{ 0xcf, 0xef, 0xef, 0xef, 0xef, 0xef, 0xc7, 0xff },\n/* char 0x6d (raw 0x6d)  */\n\t{ 0xff, 0xff, 0x93, 0xab, 0xab, 0xab, 0xbb, 0xff },\n/* char 0x6e (raw 0x6e)  */\n\t{ 0xff, 0xff, 0x87, 0xbb, 0xbb, 0xbb, 0xbb, 0xff },\n/* char 0x6f (raw 0x6f)  */\n\t{ 0xff, 0xff, 0xc7, 0xbb, 0xbb, 0xbb, 0xc7, 0xff },\n/* char 0x70 (raw 0x70)  */\n\t{ 0xff, 0xff, 0x87, 0xbb, 0xbb, 0x87, 0xbf, 0xbf },\n/* char 0x71 (raw 0x71)  */\n\t{ 0xff, 0xff, 0xc3, 0xbb, 0xbb, 0xc3, 0xfb, 0xfb },\n/* char 0x72 (raw 0x72)  */\n\t{ 0xff, 0xff, 0xa3, 0x9f, 0xbf, 0xbf, 0xbf, 0xff },\n/* char 0x73 (raw 0x73)  */\n\t{ 0xff, 0xff, 0xc3, 0xbf, 0xc7, 0xfb, 0x87, 0xff },\n/* char 0x74 (raw 0x74)  */\n\t{ 0xdf, 0xdf, 0x87, 0xdf, 0xdf, 0xdb, 0xe7, 0xff },\n/* char 0x75 (raw 0x75)  */\n\t{ 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xb3, 0xcb, 0xff },\n/* char 0x76 (raw 0x76)  */\n\t{ 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xd7, 0xef, 0xff },\n/* char 0x77 (raw 0x77)  */\n\t{ 0xff, 0xff, 0xbb, 0xbb, 0xab, 0xab, 0x93, 0xff },\n/* char 0x78 (raw 0x78)  */\n\t{ 0xff, 0xff, 0xbb, 0xd7, 0xef, 0xd7, 0xbb, 0xff },\n/* char 0x79 (raw 0x79)  */\n\t{ 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xc3, 0xfb, 0xc7 },\n/* char 0x7a (raw 0x7a)  */\n\t{ 0xff, 0xff, 0x83, 0xf7, 0xef, 0xdf, 0x83, 0xff },\n/* char 0x7b (raw 0x7b)  */\n\t{ 0xe3, 0xcf, 0xcf, 0x9f, 0xcf, 0xcf, 0xe3, 0xff },\n/* char 0x7c (raw 0x7c)  */\n\t{ 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef },\n/* char 0x7d (raw 0x7d)  */\n\t{ 0x8f, 0xe7, 0xe7, 0xf3, 0xe7, 0xe7, 0x8f, 0xff },\n/* char 0x7e (raw 0x7e)  */\n\t{ 0xcb, 0xa7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },\n/* char 0x7f (raw 0x7f)  */\n\t{ 0xff, 0xab, 0xd7, 0xab, 0xd7, 0xab, 0xff, 0xff },\n/* char 0x80 (raw 0x40)  */\n\t{ 0x38, 0x44, 0x54, 0x5c, 0x58, 0x40, 0x3c, 0x00 },\n/* char 0x81 (raw 0x41)  */\n\t{ 0x10, 0x28, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x00 },\n/* char 0x82 (raw 0x42)  */\n\t{ 0x78, 0x44, 0x44, 0x78, 0x44, 0x44, 0x78, 0x00 },\n/* char 0x83 (raw 0x43)  */\n\t{ 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00 },\n/* char 0x84 (raw 0x44)  */\n\t{ 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00 },\n/* char 0x85 (raw 0x45)  */\n\t{ 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7c, 0x00 },\n/* char 0x86 (raw 0x46)  */\n\t{ 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00 },\n/* char 0x87 (raw 0x47)  */\n\t{ 0x3c, 0x40, 0x40, 0x40, 0x4c, 0x44, 0x3c, 0x00 },\n/* char 0x88 (raw 0x48)  */\n\t{ 0x44, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x00 },\n/* char 0x89 (raw 0x49)  */\n\t{ 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 },\n/* char 0x8a (raw 0x4a)  */\n\t{ 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00 },\n/* char 0x8b (raw 0x4b)  */\n\t{ 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00 },\n/* char 0x8c (raw 0x4c)  */\n\t{ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7c, 0x00 },\n/* char 0x8d (raw 0x4d)  */\n\t{ 0x44, 0x6c, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00 },\n/* char 0x8e (raw 0x4e)  */\n\t{ 0x44, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x00 },\n/* char 0x8f (raw 0x4f)  */\n\t{ 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 },\n/* char 0x90 (raw 0x50)  */\n\t{ 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00 },\n/* char 0x91 (raw 0x51)  */\n\t{ 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00 },\n/* char 0x92 (raw 0x52)  */\n\t{ 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00 },\n/* char 0x93 (raw 0x53)  */\n\t{ 0x38, 0x44, 0x40, 0x38, 0x04, 0x44, 0x38, 0x00 },\n/* char 0x94 (raw 0x54)  */\n\t{ 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00 },\n/* char 0x95 (raw 0x55)  */\n\t{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 },\n/* char 0x96 (raw 0x56)  */\n\t{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 },\n/* char 0x97 (raw 0x57)  */\n\t{ 0x44, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x44, 0x00 },\n/* char 0x98 (raw 0x58)  */\n\t{ 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00 },\n/* char 0x99 (raw 0x59)  */\n\t{ 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x00 },\n/* char 0x9a (raw 0x5a)  */\n\t{ 0x7c, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7c, 0x00 },\n/* char 0x9b (raw 0x5b)  */\n\t{ 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x00 },\n/* char 0x9c (raw 0x5c)  */\n\t{ 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00 },\n/* char 0x9d (raw 0x5d)  */\n\t{ 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00 },\n/* char 0x9e (raw 0x5e)  */\n\t{ 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00 },\n/* char 0x9f (raw 0x5f)  */\n\t{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe },\n/* char 0xa0 (raw 0x20)  */\n\t{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },\n/* char 0xa1 (raw 0x21)  */\n\t{ 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00 },\n/* char 0xa2 (raw 0x22)  */\n\t{ 0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00 },\n/* char 0xa3 (raw 0x23)  */\n\t{ 0x28, 0x28, 0x7c, 0x28, 0x7c, 0x28, 0x28, 0x00 },\n/* char 0xa4 (raw 0x24)  */\n\t{ 0x10, 0x3c, 0x50, 0x38, 0x14, 0x78, 0x10, 0x00 },\n/* char 0xa5 (raw 0x25)  */\n\t{ 0x60, 0x64, 0x08, 0x10, 0x20, 0x4c, 0x0c, 0x00 },\n/* char 0xa6 (raw 0x26)  */\n\t{ 0x20, 0x50, 0x50, 0x20, 0x54, 0x48, 0x34, 0x00 },\n/* char 0xa7 (raw 0x27)  */\n\t{ 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 },\n/* char 0xa8 (raw 0x28)  */\n\t{ 0x10, 0x20, 0x40, 0x40, 0x40, 0x20, 0x10, 0x00 },\n/* char 0xa9 (raw 0x29)  */\n\t{ 0x10, 0x08, 0x04, 0x04, 0x04, 0x08, 0x10, 0x00 },\n/* char 0xaa (raw 0x2a)  */\n\t{ 0x10, 0x54, 0x38, 0x10, 0x38, 0x54, 0x10, 0x00 },\n/* char 0xab (raw 0x2b)  */\n\t{ 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x00 },\n/* char 0xac (raw 0x2c)  */\n\t{ 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x20, 0x00 },\n/* char 0xad (raw 0x2d)  */\n\t{ 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00 },\n/* char 0xae (raw 0x2e)  */\n\t{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 },\n/* char 0xaf (raw 0x2f)  */\n\t{ 0x00, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00 },\n/* char 0xb0 (raw 0x30)  */\n\t{ 0x38, 0x44, 0x4c, 0x54, 0x64, 0x44, 0x38, 0x00 },\n/* char 0xb1 (raw 0x31)  */\n\t{ 0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 },\n/* char 0xb2 (raw 0x32)  */\n\t{ 0x38, 0x44, 0x04, 0x18, 0x20, 0x40, 0x7c, 0x00 },\n/* char 0xb3 (raw 0x33)  */\n\t{ 0x7c, 0x04, 0x08, 0x18, 0x04, 0x44, 0x38, 0x00 },\n/* char 0xb4 (raw 0x34)  */\n\t{ 0x08, 0x18, 0x28, 0x48, 0x7c, 0x08, 0x08, 0x00 },\n/* char 0xb5 (raw 0x35)  */\n\t{ 0x7c, 0x40, 0x78, 0x04, 0x04, 0x44, 0x38, 0x00 },\n/* char 0xb6 (raw 0x36)  */\n\t{ 0x1c, 0x20, 0x40, 0x78, 0x44, 0x44, 0x38, 0x00 },\n/* char 0xb7 (raw 0x37)  */\n\t{ 0x7c, 0x04, 0x08, 0x10, 0x20, 0x20, 0x20, 0x00 },\n/* char 0xb8 (raw 0x38)  */\n\t{ 0x38, 0x44, 0x44, 0x38, 0x44, 0x44, 0x38, 0x00 },\n/* char 0xb9 (raw 0x39)  */\n\t{ 0x38, 0x44, 0x44, 0x3c, 0x04, 0x08, 0x70, 0x00 },\n/* char 0xba (raw 0x3a)  */\n\t{ 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00 },\n/* char 0xbb (raw 0x3b)  */\n\t{ 0x00, 0x00, 0x10, 0x00, 0x10, 0x10, 0x20, 0x00 },\n/* char 0xbc (raw 0x3c)  */\n\t{ 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x00 },\n/* char 0xbd (raw 0x3d)  */\n\t{ 0x00, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x00, 0x00 },\n/* char 0xbe (raw 0x3e)  */\n\t{ 0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x00 },\n/* char 0xbf (raw 0x3f)  */\n\t{ 0x38, 0x44, 0x08, 0x10, 0x10, 0x00, 0x10, 0x00 },\n/* char 0xc0 (raw 0x40)  */\n\t{ 0x38, 0x44, 0x54, 0x5c, 0x58, 0x40, 0x3c, 0x00 },\n/* char 0xc1 (raw 0x41)  */\n\t{ 0x10, 0x28, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x00 },\n/* char 0xc2 (raw 0x42)  */\n\t{ 0x78, 0x44, 0x44, 0x78, 0x44, 0x44, 0x78, 0x00 },\n/* char 0xc3 (raw 0x43)  */\n\t{ 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00 },\n/* char 0xc4 (raw 0x44)  */\n\t{ 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00 },\n/* char 0xc5 (raw 0x45)  */\n\t{ 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7c, 0x00 },\n/* char 0xc6 (raw 0x46)  */\n\t{ 0x7c, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00 },\n/* char 0xc7 (raw 0x47)  */\n\t{ 0x3c, 0x40, 0x40, 0x40, 0x4c, 0x44, 0x3c, 0x00 },\n/* char 0xc8 (raw 0x48)  */\n\t{ 0x44, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x00 },\n/* char 0xc9 (raw 0x49)  */\n\t{ 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 },\n/* char 0xca (raw 0x4a)  */\n\t{ 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00 },\n/* char 0xcb (raw 0x4b)  */\n\t{ 0x44, 0x48, 0x50, 0x60, 0x50, 0x48, 0x44, 0x00 },\n/* char 0xcc (raw 0x4c)  */\n\t{ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7c, 0x00 },\n/* char 0xcd (raw 0x4d)  */\n\t{ 0x44, 0x6c, 0x54, 0x54, 0x44, 0x44, 0x44, 0x00 },\n/* char 0xce (raw 0x4e)  */\n\t{ 0x44, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x00 },\n/* char 0xcf (raw 0x4f)  */\n\t{ 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 },\n/* char 0xd0 (raw 0x50)  */\n\t{ 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x00 },\n/* char 0xd1 (raw 0x51)  */\n\t{ 0x38, 0x44, 0x44, 0x44, 0x54, 0x48, 0x34, 0x00 },\n/* char 0xd2 (raw 0x52)  */\n\t{ 0x78, 0x44, 0x44, 0x78, 0x50, 0x48, 0x44, 0x00 },\n/* char 0xd3 (raw 0x53)  */\n\t{ 0x38, 0x44, 0x40, 0x38, 0x04, 0x44, 0x38, 0x00 },\n/* char 0xd4 (raw 0x54)  */\n\t{ 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00 },\n/* char 0xd5 (raw 0x55)  */\n\t{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00 },\n/* char 0xd6 (raw 0x56)  */\n\t{ 0x44, 0x44, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 },\n/* char 0xd7 (raw 0x57)  */\n\t{ 0x44, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x44, 0x00 },\n/* char 0xd8 (raw 0x58)  */\n\t{ 0x44, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x00 },\n/* char 0xd9 (raw 0x59)  */\n\t{ 0x44, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x00 },\n/* char 0xda (raw 0x5a)  */\n\t{ 0x7c, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7c, 0x00 },\n/* char 0xdb (raw 0x5b)  */\n\t{ 0x7c, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7c, 0x00 },\n/* char 0xdc (raw 0x5c)  */\n\t{ 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00 },\n/* char 0xdd (raw 0x5d)  */\n\t{ 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00 },\n/* char 0xde (raw 0x5e)  */\n\t{ 0x00, 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00 },\n/* char 0xdf (raw 0x5f)  */\n\t{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe },\n/* char 0xe0 (raw 0x60)  */\n\t{ 0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },\n/* char 0xe1 (raw 0x61)  */\n\t{ 0x00, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00 },\n/* char 0xe2 (raw 0x62)  */\n\t{ 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x78, 0x00 },\n/* char 0xe3 (raw 0x63)  */\n\t{ 0x00, 0x00, 0x3c, 0x40, 0x40, 0x40, 0x3c, 0x00 },\n/* char 0xe4 (raw 0x64)  */\n\t{ 0x04, 0x04, 0x3c, 0x44, 0x44, 0x44, 0x3c, 0x00 },\n/* char 0xe5 (raw 0x65)  */\n\t{ 0x00, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00 },\n/* char 0xe6 (raw 0x66)  */\n\t{ 0x18, 0x24, 0x20, 0x78, 0x20, 0x20, 0x20, 0x00 },\n/* char 0xe7 (raw 0x67)  */\n\t{ 0x00, 0x00, 0x38, 0x44, 0x44, 0x3c, 0x04, 0x38 },\n/* char 0xe8 (raw 0x68)  */\n\t{ 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00 },\n/* char 0xe9 (raw 0x69)  */\n\t{ 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x38, 0x00 },\n/* char 0xea (raw 0x6a)  */\n\t{ 0x08, 0x00, 0x18, 0x08, 0x08, 0x08, 0x48, 0x30 },\n/* char 0xeb (raw 0x6b)  */\n\t{ 0x40, 0x40, 0x44, 0x48, 0x70, 0x48, 0x44, 0x00 },\n/* char 0xec (raw 0x6c)  */\n\t{ 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00 },\n/* char 0xed (raw 0x6d)  */\n\t{ 0x00, 0x00, 0x6c, 0x54, 0x54, 0x54, 0x44, 0x00 },\n/* char 0xee (raw 0x6e)  */\n\t{ 0x00, 0x00, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00 },\n/* char 0xef (raw 0x6f)  */\n\t{ 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00 },\n/* char 0xf0 (raw 0x70)  */\n\t{ 0x00, 0x00, 0x78, 0x44, 0x44, 0x78, 0x40, 0x40 },\n/* char 0xf1 (raw 0x71)  */\n\t{ 0x00, 0x00, 0x3c, 0x44, 0x44, 0x3c, 0x04, 0x04 },\n/* char 0xf2 (raw 0x72)  */\n\t{ 0x00, 0x00, 0x5c, 0x60, 0x40, 0x40, 0x40, 0x00 },\n/* char 0xf3 (raw 0x73)  */\n\t{ 0x00, 0x00, 0x3c, 0x40, 0x38, 0x04, 0x78, 0x00 },\n/* char 0xf4 (raw 0x74)  */\n\t{ 0x20, 0x20, 0x78, 0x20, 0x20, 0x24, 0x18, 0x00 },\n/* char 0xf5 (raw 0x75)  */\n\t{ 0x00, 0x00, 0x44, 0x44, 0x44, 0x4c, 0x34, 0x00 },\n/* char 0xf6 (raw 0x76)  */\n\t{ 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00 },\n/* char 0xf7 (raw 0x77)  */\n\t{ 0x00, 0x00, 0x44, 0x44, 0x54, 0x54, 0x6c, 0x00 },\n/* char 0xf8 (raw 0x78)  */\n\t{ 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00 },\n/* char 0xf9 (raw 0x79)  */\n\t{ 0x00, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x04, 0x38 },\n/* char 0xfa (raw 0x7a)  */\n\t{ 0x00, 0x00, 0x7c, 0x08, 0x10, 0x20, 0x7c, 0x00 },\n/* char 0xfb (raw 0x7b)  */\n\t{ 0x1c, 0x30, 0x30, 0x60, 0x30, 0x30, 0x1c, 0x00 },\n/* char 0xfc (raw 0x7c)  */\n\t{ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 },\n/* char 0xfd (raw 0x7d)  */\n\t{ 0x70, 0x18, 0x18, 0x0c, 0x18, 0x18, 0x70, 0x00 },\n/* char 0xfe (raw 0x7e)  */\n\t{ 0x34, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },\n/* char 0xff (raw 0x7f)  */\n\t{ 0x00, 0x54, 0x28, 0x54, 0x28, 0x54, 0x00, 0x00 },\n"
  },
  {
    "path": "upstream/kegs/src/kegswin.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio Version 17\r\nVisualStudioVersion = 17.0.32112.339\r\nMinimumVisualStudioVersion = 10.0.40219.1\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"kegswin\", \"kegswin.vcxproj\", \"{C24D318A-501E-4B8C-901A-15977CB9C00C}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|x64 = Debug|x64\r\n\t\tDebug|x86 = Debug|x86\r\n\t\tRelease|x64 = Release|x64\r\n\t\tRelease|x86 = Release|x86\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x64.ActiveCfg = Release|x64\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x64.Build.0 = Release|x64\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x86.ActiveCfg = Debug|Win32\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Debug|x86.Build.0 = Debug|Win32\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x64.ActiveCfg = Release|x64\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x64.Build.0 = Release|x64\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x86.ActiveCfg = Release|Win32\r\n\t\t{C24D318A-501E-4B8C-901A-15977CB9C00C}.Release|x86.Build.0 = Release|Win32\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\n\tGlobalSection(ExtensibilityGlobals) = postSolution\r\n\t\tSolutionGuid = {32BE13D0-68A7-486D-874C-EA158125775B}\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "upstream/kegs/src/kegswin.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <VCProjectVersion>17.0</VCProjectVersion>\r\n    <ProjectGuid>{C24D318A-501E-4B8C-901A-15977CB9C00C}</ProjectGuid>\r\n    <Keyword>Win32Proj</Keyword>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <PlatformToolset>v143</PlatformToolset>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"Shared\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n      <Optimization>Disabled</Optimization>\r\n    </ClCompile>\r\n    <Link>\r\n      <TargetMachine>MachineX86</TargetMachine>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <SubSystem>Windows</SubSystem>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n    </ClCompile>\r\n    <Link>\r\n      <TargetMachine>MachineX86</TargetMachine>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <AdditionalDependencies>wsock32.lib;dsound.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Link>\r\n      <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;wsock32.lib;dsound.lib;winmm.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n      <SubSystem>Console</SubSystem>\r\n    </Link>\r\n    <ClCompile>\r\n      <TreatWarningAsError>true</TreatWarningAsError>\r\n      <WarningLevel>Level3</WarningLevel>\r\n    </ClCompile>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"adb.c\" />\r\n    <ClCompile Include=\"applesingle.c\" />\r\n    <ClCompile Include=\"clock.c\" />\r\n    <ClCompile Include=\"compile_time.c\" />\r\n    <ClCompile Include=\"config.c\" />\r\n    <ClCompile Include=\"debugger.c\" />\r\n    <ClCompile Include=\"dynapro.c\" />\r\n    <ClCompile Include=\"dyna_filt.c\" />\r\n    <ClCompile Include=\"dyna_type.c\" />\r\n    <ClCompile Include=\"dyna_validate.c\" />\r\n    <ClCompile Include=\"engine_c.c\" />\r\n    <ClCompile Include=\"iwm.c\" />\r\n    <ClCompile Include=\"joystick_driver.c\" />\r\n    <ClCompile Include=\"mockingboard.c\" />\r\n    <ClCompile Include=\"moremem.c\" />\r\n    <ClCompile Include=\"paddles.c\" />\r\n    <ClCompile Include=\"scc.c\" />\r\n    <ClCompile Include=\"scc_unixdriver.c\" />\r\n    <ClCompile Include=\"scc_socket_driver.c\" />\r\n    <ClCompile Include=\"scc_windriver.c\" />\r\n    <ClCompile Include=\"sim65816.c\" />\r\n    <ClCompile Include=\"smartport.c\" />\r\n    <ClCompile Include=\"doc.c\" />\r\n    <ClCompile Include=\"sound.c\" />\r\n    <ClCompile Include=\"sound_driver.c\" />\r\n    <ClCompile Include=\"undeflate.c\" />\r\n    <ClCompile Include=\"unshk.c\" />\r\n    <ClCompile Include=\"video.c\" />\r\n    <ClCompile Include=\"voc.c\" />\r\n    <ClCompile Include=\"win32snd_driver.c\" />\r\n    <ClCompile Include=\"windriver.c\" />\r\n    <ClCompile Include=\"woz.c\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"config.h\" />\r\n    <ClInclude Include=\"defc.h\" />\r\n    <ClInclude Include=\"defcomm.h\" />\r\n    <ClInclude Include=\"defs_instr.h\" />\r\n    <ClInclude Include=\"disas.h\" />\r\n    <ClInclude Include=\"engine.h\" />\r\n    <ClInclude Include=\"instable.h\" />\r\n    <ClInclude Include=\"iwm.h\" />\r\n    <ClInclude Include=\"Kegs-Bridging-Header.h\" />\r\n    <ClInclude Include=\"kegsfont.h\" />\r\n    <ClInclude Include=\"op_routs.h\" />\r\n    <ClInclude Include=\"protos.h\" />\r\n    <ClInclude Include=\"protos_base.h\" />\r\n    <ClInclude Include=\"protos_windriver.h\" />\r\n    <ClInclude Include=\"protos_xdriver.h\" />\r\n    <ClInclude Include=\"scc.h\" />\r\n    <ClInclude Include=\"size_c.h\" />\r\n    <ClInclude Include=\"sound.h\" />\r\n    <ClInclude Include=\"winresource.h\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>\n"
  },
  {
    "path": "upstream/kegs/src/ldvars",
    "content": "OBJECTS = adb.o engine_c.o clock.o config.o debugger.o scc.o scc_socket_driver.o scc_windriver.o scc_unixdriver.o iwm.o joystick_driver.o moremem.o paddles.o mockingboard.o sim65816.o smartport.o doc.o sound.o sound_driver.o woz.o unshk.o undeflate.o dynapro.o dyna_type.o dyna_filt.o dyna_validate.o applesingle.o video.o voc.o\nPROJROOT = ..\n"
  },
  {
    "path": "upstream/kegs/src/macsnd_driver.c",
    "content": "const char rcsid_macsnd_driver_c[] = \"@(#)$KmKId: macsnd_driver.c,v 1.20 2022-04-03 13:38:47+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2022 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// Headers at: /Applications/Xcode.app/Contents/Developer/Platforms/\n//      MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/\n//      Frameworks/AudioToolbox.framework/Headers\n\n// Some ideas from SDL code:\n//  http://www-personal.umich.edu/~bazald/l/api/_s_d_l__coreaudio_8c_source.html\n\n#include \"defc.h\"\n#include \"sound.h\"\n\n#include <AudioToolbox/AudioToolbox.h>\n#include <CoreAudio/CoreAudio.h>\n#include <unistd.h>\n\n// Mac OS X 12.0 deprecates kAudioObjectPropertyElementMaster.  So now we\n//  need to use kAudioObjectPropertyElementMain, and set it to ...Master if it\n//  isn't already set.  This is beyond dumb\n#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED)\n# if __MAC_OS_X_VERSION_MAX_ALLOWED < 120000\n#  define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster\n# endif\n#endif\n\n#define MACSND_REBUF_SIZE\t(64*1024)\n\nword32\tg_macsnd_rebuf[MACSND_REBUF_SIZE];\nvolatile int g_macsnd_rd_pos;\nvolatile int g_macsnd_wr_pos;\nvolatile int g_macsnd_playing = 0;\nint g_macsnd_channel_warn = 0;\nextern int g_sound_min_samples;\t\t\t// About 33ms\nextern int g_sound_max_multiplier;\t\t// About 6, so 6*33 ~= 200ms.\n\nextern int Verbose;\n\nextern int g_preferred_rate;\nextern word32 *g_sound_shm_addr;\nextern int g_sound_size;\n\nint g_call_num = 0;\n\nAURenderCallbackStruct g_callback_struct = { 0 };\n\nstatic OSStatus\naudio_callback(void *in_ref_ptr, AudioUnitRenderActionFlags *aura_flags_ptr,\n\t\tconst AudioTimeStamp *a_timestamp_ptr, UInt32 bus_number,\n\t\tUInt32 in_num_frame, AudioBufferList *abuflist_ptr)\n{\n\tword32\t*wptr;\n\tint\tnum_buffers, num_channels, num_samps, sample_num, samps_avail;\n\tint\tmax_samples, min_samples;\n\tint\ti, j;\n\n\tif(in_ref_ptr || aura_flags_ptr || a_timestamp_ptr || bus_number) {\n\t\t// Avoid unused parameter warning\n\t}\n#if 0\n\tprintf(\"CB: audio_callback called, in_ref:%p, bus:%d, in_frame:%d\\n\",\n\t\tin_ref_ptr, bus_number, in_num_frame);\n#endif\n\tnum_buffers = abuflist_ptr->mNumberBuffers;\n\tmin_samples = g_sound_min_samples;\n\tmax_samples = min_samples * g_sound_max_multiplier;\n#if 0\n\tprintf(\"CB: num_buffers: %d. sample_time:%lf, host_time:%016llx \"\n\t\t\"rate_scalar:%lf, word_clock_time:%016llx, flags:%04x\\n\",\n\t\tnum_buffers, a_timestamp_ptr->mSampleTime,\n\t\ta_timestamp_ptr->mHostTime, a_timestamp_ptr->mRateScalar,\n\t\ta_timestamp_ptr->mWordClockTime, a_timestamp_ptr->mFlags);\n#endif\n\n\tsample_num = g_macsnd_rd_pos;\n\tsamps_avail = (g_macsnd_wr_pos - sample_num) & (MACSND_REBUF_SIZE - 1);\n\tif((int)in_num_frame > samps_avail) {\n\t\t// We don't have enough samples, must pause\n\t\tg_macsnd_playing = 0;\n\t\tsample_num = g_macsnd_wr_pos;\t\t// Eat remaining samps\n\t}\n\tif((g_macsnd_playing == 0) &&\n\t\t\t(samps_avail > (min_samples + (int)in_num_frame))) {\n\t\t// We can unpause\n\t\tg_macsnd_playing = 1;\n\t}\n\tif(g_macsnd_playing && (samps_avail > max_samples)) {\n\t\tprintf(\"JUMP SAMPLE_NUM by %d samples!\\n\", max_samples / 2);\n\t\tsample_num += (max_samples / 2);\n\t\tsample_num = sample_num & (MACSND_REBUF_SIZE - 1);\n\t}\n#if 0\n\tprintf(\"CB: in_frame:%d, samps_avail:%d, playing:%d\\n\", in_num_frame,\n\t\t\tsamps_avail, g_macsnd_playing);\n#endif\n\n\tfor(i = 0; i < num_buffers; i++) {\n\t\tnum_channels = abuflist_ptr->mBuffers[i].mNumberChannels;\n\t\tif((num_channels != 2) && !g_macsnd_channel_warn) {\n\t\t\tprintf(\"mNumberChannels:%d\\n\", num_channels);\n\t\t\tg_macsnd_channel_warn = 1;\n\t\t}\n\t\tnum_samps = abuflist_ptr->mBuffers[i].mDataByteSize / 4;\n\t\twptr = (word32 *)abuflist_ptr->mBuffers[i].mData;\n#if 0\n\t\tprintf(\"CB buf[%d]: num_ch:%d, num_samps:%d, wptr:%p\\n\", i,\n\t\t\tnum_channels, num_samps, wptr);\n#endif\n#if 0\n\t\tif((g_call_num & 31) == 0) {\n\t\t\tprintf(\"%d play %d samples, samps_avail:%d\\n\",\n\t\t\t\tg_call_num, num_samps, samps_avail);\n\t\t}\n#endif\n\t\tif(g_macsnd_playing) {\n\t\t\tfor(j = 0; j < num_samps; j++) {\n\t\t\t\twptr[j] = g_macsnd_rebuf[sample_num];\n\t\t\t\tsample_num++;\n\t\t\t\tif(sample_num >= MACSND_REBUF_SIZE) {\n\t\t\t\t\tsample_num = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor(j = 0; j < num_samps; j++) {\n\t\t\t\twptr[j] = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tg_call_num++;\n\tg_macsnd_rd_pos = sample_num;\n\n\treturn 0;\n}\n\nint\nmac_send_audio(byte *ptr, int in_size)\n{\n\tword32\t*wptr, *macptr;\n\tint\tsamps, sample_num;\n\tint\ti;\n\n\tsamps = in_size / 4;\n\twptr = (word32 *)ptr;\n\tsample_num = g_macsnd_wr_pos;\n\tmacptr = &(g_macsnd_rebuf[0]);\n\tfor(i = 0; i < samps; i++) {\n\t\tmacptr[sample_num] = *wptr++;\n\t\tsample_num++;\n\t\tif(sample_num >= MACSND_REBUF_SIZE) {\n\t\t\tsample_num = 0;\n\t\t}\n\t}\n\n\tg_macsnd_wr_pos = sample_num;\n\n\treturn in_size;\n}\n\nAudioObjectPropertyAddress g_aopa = { 0 };\n\nvoid\nmacsnd_init()\n{\n\tAudioComponentInstance ac_inst;\n\tAudioStreamBasicDescription *str_desc_ptr;\n\tAudioComponentDescription *ac_descr_ptr;\n\tAudioComponent\tac;\n\tAudioDeviceID\tdevice;\n\tOSStatus\tresult;\n\tUInt32\t\tsize;\n\n\tg_macsnd_rd_pos = 0;\n\tg_macsnd_wr_pos = 0;\n\tmac_printf(\"macsnd_init called\\n\");\n\n\tg_aopa.mSelector = kAudioHardwarePropertyDefaultOutputDevice;\n\tg_aopa.mScope = kAudioObjectPropertyScopeGlobal;\n\tg_aopa.mElement = kAudioObjectPropertyElementMain;\n\n\tsize = 4;\n\tresult = AudioObjectGetPropertyData(kAudioObjectSystemObject, &g_aopa,\n\t\t\t\t\t0, 0, &size, &device);\n\tif(result != 0) {\n\t\tprintf(\"AudioObjectGetPropertData on DefaultOutputDevice:%d\\n\",\n\t\t\t\t\t\t\t\t\tresult);\n\t\treturn;\n\t}\n\tmac_printf(\"Audio Device number: %d\\n\", device);\n\n\tstr_desc_ptr = calloc(1, sizeof(AudioStreamBasicDescription));\n\tstr_desc_ptr->mFormatID = kAudioFormatLinearPCM;\n\tstr_desc_ptr->mFormatFlags = kLinearPCMFormatFlagIsPacked |\n\t\t\t\t\tkLinearPCMFormatFlagIsSignedInteger;\n\tstr_desc_ptr->mChannelsPerFrame = 2;\n\tstr_desc_ptr->mSampleRate = g_preferred_rate;\n\tstr_desc_ptr->mFramesPerPacket = 1;\n\tstr_desc_ptr->mBitsPerChannel = 16;\t\t// 16-bit samples\n\tstr_desc_ptr->mBytesPerFrame = (16 * 2) / 8;\n\tstr_desc_ptr->mBytesPerPacket = str_desc_ptr->mBytesPerFrame;\n\n\tac_descr_ptr = calloc(1, sizeof(AudioComponentDescription));\n\tac_descr_ptr->componentType = kAudioUnitType_Output;\n\tac_descr_ptr->componentManufacturer = kAudioUnitManufacturer_Apple;\n\tac_descr_ptr->componentSubType = kAudioUnitSubType_DefaultOutput;\n\tac = AudioComponentFindNext(0, ac_descr_ptr);\n\tmac_printf(\"AudioComponentFindNext ret: %p\\n\", ac);\n\tif(ac == 0) {\n\t\texit(1);\n\t}\n\n\tresult = AudioComponentInstanceNew(ac, &ac_inst);\n\tmac_printf(\"AudioComponentInstanceNew ret:%d\\n\", result);\n\tif(result != 0) {\n\t\texit(1);\n\t}\n\n\tresult = AudioUnitSetProperty(ac_inst,\n\t\t\tkAudioOutputUnitProperty_CurrentDevice,\n\t\t\tkAudioUnitScope_Global, 0, &device,\n\t\t\tsizeof(device));\n\tmac_printf(\"AudioUnitSetProperty CurrentDevice ret: %d\\n\", result);\n\tif(result != 0) {\n\t\texit(1);\n\t}\n\n\tresult = AudioUnitSetProperty(ac_inst,\n\t\t\tkAudioUnitProperty_StreamFormat,\n\t\t\tkAudioUnitScope_Input, 0, str_desc_ptr,\n\t\t\tsizeof(*str_desc_ptr));\n\tmac_printf(\"AudioUnitSetProperty StreamFormat ret: %d\\n\", result);\n\tif(result != 0) {\n\t\texit(1);\n\t}\n\n\tg_callback_struct.inputProc = audio_callback;\n\tg_callback_struct.inputProcRefCon = (void *)1;\n\tresult = AudioUnitSetProperty(ac_inst,\n\t\t\tkAudioUnitProperty_SetRenderCallback,\n\t\t\tkAudioUnitScope_Input, 0, &g_callback_struct,\n\t\t\tsizeof(g_callback_struct));\n\n\tmac_printf(\"AudioUnitSetProperty SetRenderCallback ret: %d\\n\", result);\n\tif(result != 0) {\n\t\texit(1);\n\t}\n\n\tresult = AudioUnitInitialize(ac_inst);\n\tmac_printf(\"AudioUnitInitialize ret: %d\\n\", result);\n\tif(result != 0) {\n\t\texit(1);\n\t}\n\n\tresult = AudioOutputUnitStart(ac_inst);\n\tmac_printf(\"AudioOutputUnitStart ret: %d\\n\", result);\n\tif(result != 0) {\n\t\texit(1);\n\t}\n\n\tsound_set_audio_rate(g_preferred_rate);\n\n\tmac_printf(\"End of child_sound_init_mac\\n\");\n\tfflush(stdout);\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/mockingboard.c",
    "content": "const char rcsid_mockingboard_c[] = \"@(#)$KmKId: mockingboard.c,v 1.26 2023-06-13 16:54:18+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include \"defc.h\"\n#include \"sound.h\"\n\n// Mockingboard contains two pairs of a 6522/AY-8913, where the 6522 interfaces\n//  the AY-8913 (which makes the sounds) to the Apple II.  Each AY-8913\n//  contains 3 channels of sound: A,B,C.  Model each pair separately.\n// The AY-8913 has 16 registers.  The documentation numbers them using octal!\n// The AY8913 is accessed using ORB of the 6522 as control: 0 = Reset, 4=Idle\n//\t5=Reg read; 6=Write reg; 7=Latch reg address\n\nextern Mockingboard g_mockingboard;\ndword64\tg_mockingboard_last_int_dusec = 0ULL;\ndword64\tg_mockingboard_event_int_dusec = 0ULL;\t// 0 -> no event pending\n\nextern int g_irq_pending;\nextern double g_dsamps_per_dfcyc;\n\nvoid\nmock_ay8913_reset(int pair_num, dword64 dfcyc)\n{\n\tAy8913\t*ay8913ptr;\n\tint\ti;\n\n\tif(dfcyc) {\n\t\t// Avoid unused parameter warning\n\t}\n\tay8913ptr = &(g_mockingboard.pair[pair_num].ay8913);\n\tay8913ptr->reg_addr_latch = 16;\t\t// out-of-range, mb-audit1.3\n\tfor(i = 0; i < 16; i++) {\n\t\tay8913ptr->regs[i] = 0;\n\t}\n\tfor(i = 0; i < 3; i++) {\n\t\tay8913ptr->toggle_tone[i] = 0;\n\t\tay8913ptr->tone_samp[i] = 0;\n\t}\n\tay8913ptr->noise_val = 0x12345678;\n\tay8913ptr->noise_samp = 0;\n\tay8913ptr->env_dsamp = 0;\n}\n\nvoid\nmockingboard_reset(dword64 dfcyc)\n{\n\tword32\ttimer1_latch;\n\n\ttimer1_latch = g_mockingboard.pair[0].mos6522.timer1_latch;\n\tmemset(&g_mockingboard, 0, sizeof(g_mockingboard));\n\n\tg_mockingboard_last_int_dusec = (dfcyc >> 16) << 16;\n\tif(g_mockingboard_event_int_dusec != 0) {\n\t\t(void)remove_event_mockingboard();\n\t}\n\tg_mockingboard_event_int_dusec = 0;\n\tprintf(\"At reset, timer1_latch: %08x\\n\", timer1_latch);\n\tif((timer1_latch & 0xffff) == 0x234) {\t\t\t// MB-audit\n\t\tg_mockingboard.pair[0].mos6522.timer1_latch = timer1_latch;\n\t} else {\n\t\tg_mockingboard.pair[0].mos6522.timer1_latch = 0xff00;\n\t}\n\tg_mockingboard.pair[0].mos6522.timer1_counter = 0x2ff00;\n\tg_mockingboard.pair[0].mos6522.timer2_counter = 0x2ff00;\n\tg_mockingboard.pair[1].mos6522.timer1_latch = 0xff00;\n\tg_mockingboard.pair[1].mos6522.timer1_counter = 0x2ff00;\n\tg_mockingboard.pair[1].mos6522.timer2_counter = 0x2ff00;\n\n\tmock_ay8913_reset(0, dfcyc);\n\tmock_ay8913_reset(1, dfcyc);\n}\n\nvoid\nmock_show_pair(int pair_num, dword64 dfcyc, const char *str)\n{\n\tMos6522\t*mos6522ptr;\n\n\tmos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);\n\tprintf(\"Mock %d %s, t1_lat:%05x, t1_c:%05x, t2_l:%05x t2_c:%05x, ifr:\"\n\t\t\"%02x, acr:%02x, ier:%02x\\n\", pair_num, str,\n\t\tmos6522ptr->timer1_latch, mos6522ptr->timer1_counter,\n\t\tmos6522ptr->timer2_latch, mos6522ptr->timer2_counter,\n\t\tmos6522ptr->ifr, mos6522ptr->acr, mos6522ptr->ier);\n\tprintf(\"  dfcyc:%016llx, event_int:%lld\\n\", dfcyc,\n\t\t\t\t\tg_mockingboard_event_int_dusec);\n\n}\n\n// Timers work as follows: if written with '10' in cycle 0, on cycle 1 the\n//  counters loads with '10', and counts down to 9 on cycle 2...down to 1\n//  on cycle 10, then 0 on cycle 11, and then signals an interrupt on cycle 12\n// To handle this, timers are \"read value + 1\" so that timer==0 means the\n//  timer should interrupt right now.  So to write '10' to a timer, the code\n//  needs to write 10+2=12 to the variable, so that on the next cycle, it is\n//  11, which will be returned as 10 to software reading the reg.\n\nvoid\nmock_update_timers(int doit, dword64 dfcyc)\n{\n\tMos6522\t*mos6522ptr;\n\tdword64\tdusec, ddiff, dleft, timer1_int_dusec, timer2_int_dusec;\n\tdword64\tclosest_int_dusec, event_int_dusec;\n\tword32\ttimer_val, ier, timer_eff, timer_latch, log_ifr;\n\tint\ti;\n\n\tdbg_log_info(dfcyc, doit, g_mockingboard.pair[0].mos6522.ifr |\n\t\t\t(g_mockingboard.pair[0].mos6522.ier << 8), 0xc0);\n\n\tdusec = dfcyc >> 16;\n\tddiff = dusec - g_mockingboard_last_int_dusec;\n\tif(!doit && (dusec <= g_mockingboard_last_int_dusec)) {\n\t\treturn;\t\t\t\t\t// Nothing more to do\n\t}\n\n\t// printf(\"mock_update_timers at %lf %016llx, ddiff:%llx\\n\", dfcyc,\n\t//\t\t\t\t\t\tdusec, ddiff);\n\t// Update timers by ddiff integer cycles, calculate next event time\n\tg_mockingboard_last_int_dusec = dusec;\n\tclosest_int_dusec = 0;\n\tfor(i = 0; i < 2; i++) {\t\t// pair_num\n\t\tmos6522ptr = &(g_mockingboard.pair[i].mos6522);\n\t\ttimer1_int_dusec = 0;\n\t\ttimer_val = mos6522ptr->timer1_counter;\n\t\tier = mos6522ptr->ier;\n\t\ttimer_eff = (timer_val & 0x1ffff);\n\t\tdleft = ddiff;\n\t\ttimer_latch = mos6522ptr->timer1_latch + 2;\n\t\tdbg_log_info(dfcyc, (word32)dleft, timer_val, 0xcb);\n\t\tdbg_log_info(dfcyc, ier, timer_latch, 0xcb);\n\t\tif(dleft < timer_eff) {\n\t\t\t// Move ahead only a little, no triggering\n\t\t\ttimer_val = timer_val - (word32)dleft;\n\t\t\tif(ddiff) {\n\t\t\t\t// printf(\"New timer1_val:%05x, dleft:%08llx\\n\",\n\t\t\t\t//\t\ttimer_val, dleft);\n\t\t\t}\n\t\t\tif(((mos6522ptr->ifr & 0x40) == 0) && (ier & 0x40)) {\n\t\t\t\t// IFR not set yet, prepare an event\n\t\t\t\ttimer1_int_dusec = dusec +\n\t\t\t\t\t\t\t(timer_val & 0x1ffff);\n\t\t\t\tdbg_log_info(dfcyc, (word32)timer1_int_dusec,\n\t\t\t\t\t\t\ttimer_val, 0xcd);\n\t\t\t\t// printf(\"t1_int_dusec: %016llx\\n\",\n\t\t\t\t//\t\t\ttimer1_int_dusec);\n\t\t\t}\n\t\t} else {\n\t\t\t// Timer1 has triggered now (maybe rolled over more\n\t\t\t//  than once).\n\t\t\tlog_ifr = 0;\n\t\t\tif((timer_val & 0x20000) == 0) {\n\t\t\t\t// Either free-running, or not one-shot already\n\t\t\t\t//  triggered\n\t\t\t\t// Set interrupt: Ensure IFR | 0x40 is set\n\t\t\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, i,\n\t\t\t\t\t\tmos6522ptr->ifr | 0x40, ier);\n\t\t\t\tlog_ifr = 1;\n\t\t\t}\n\t\t\tdleft -= timer_eff;\n\t\t\tif(dleft >= timer_latch) {\n\t\t\t\t// It's rolled over several times, remove those\n\t\t\t\tdleft = dleft % timer_latch;\n\t\t\t\tdbg_log_info(dfcyc, (word32)dleft, timer_latch,\n\t\t\t\t\t\t\t\t\t0xcc);\n\t\t\t}\n\t\t\tif(dleft == 0) {\n\t\t\t\tdleft = timer_latch;\n\t\t\t}\n\t\t\ttimer_val = (timer_latch - dleft) & 0x1ffff;\n\t\t\tif((mos6522ptr->acr & 0x40) == 0) {\n\t\t\t\t// ACR6=0: One shot mode, mark it as triggered\n\t\t\t\ttimer_val |= 0x20000;\n\t\t\t}\n\t\t\tif(log_ifr) {\n\t\t\t\tdbg_log_info(dfcyc,\n\t\t\t\t\tmos6522ptr->ifr | (ier << 8),\n\t\t\t\t\ttimer_val, 0xc3);\n\t\t\t}\n\t\t}\n\n#if 0\n\t\tprintf(\"%016llx ch%d timer1 was %05x, now %05x\\n\", dfcyc, i,\n\t\t\t\t\tmos6522ptr->timer1_counter, timer_val);\n#endif\n\n\t\tmos6522ptr->timer1_counter = timer_val;\n\t\tdbg_log_info(dfcyc, timer_val, timer_latch, 0xce);\n\n\t\t// Handle timer2\n\t\ttimer2_int_dusec = 0;\n\t\ttimer_val = mos6522ptr->timer2_counter;\n\t\ttimer_eff = timer_val & 0x1ffff;\n\t\tdleft = ddiff;\n\t\tif(mos6522ptr->acr & 0x20) {\n\t\t\t// Count pulses mode.  Just don't count\n\t\t\tdleft = 0;\n\t\t}\n\t\tif(dleft < timer_eff) {\n\t\t\t// Move ahead only a little, no triggering\n\t\t\ttimer_val = timer_val - (word32)dleft;\n\t\t\tif(((mos6522ptr->ifr & 0x20) == 0) && (ier & 0x20)) {\n\t\t\t\t// IFR not set yet, prepare an event\n\t\t\t\ttimer2_int_dusec = dusec +\n\t\t\t\t\t\t\t(timer_val & 0x1ffff);\n\t\t\t\t//printf(\"t2_int_dusec: %016llx\\n\",\n\t\t\t\t//\t\t\ttimer1_int_dusec);\n\t\t\t}\n\t\t} else if(timer_val & 0x20000) {\n\t\t\t// And already triggered once, just update count\n\t\t\ttimer_val = ((timer_eff - dleft) & 0xffff) | 0x20000;\n\t\t} else {\n\t\t\t// Has not triggered once yet, but it will now\n\t\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, i,\n\t\t\t\t\t\tmos6522ptr->ifr | 0x20, ier);\n\t\t\ttimer_val = ((timer_val - dleft) & 0xffff) | 0x20000;\n\t\t\tdbg_log_info(dfcyc, mos6522ptr->ifr | (ier << 8),\n\t\t\t\t\ttimer_val, 0xc4);\n\t\t}\n\n\t\t// printf(\"ch%d timer2 was %05x, now %05x\\n\", i,\n\t\t//\t\t\tmos6522ptr->timer2_counter, timer_val);\n\n\t\tmos6522ptr->timer2_counter = timer_val;\n\n\t\tif(timer1_int_dusec && timer2_int_dusec) {\n\t\t\ttimer1_int_dusec = MY_MIN(timer1_int_dusec,\n\t\t\t\t\t\t\ttimer2_int_dusec);\n\t\t}\n\t\tif(timer1_int_dusec) {\n\t\t\tif(closest_int_dusec) {\n\t\t\t\tclosest_int_dusec = MY_MIN(closest_int_dusec,\n\t\t\t\t\t\t\ttimer1_int_dusec);\n\t\t\t} else {\n\t\t\t\tclosest_int_dusec = timer1_int_dusec;\n\t\t\t}\n\t\t}\n\t}\n\n\tevent_int_dusec = g_mockingboard_event_int_dusec;\n\tif(closest_int_dusec) {\n\t\t// See if this is sooner than the current pending event\n\t\t// printf(\"closest_int_dusec: %016llx, event_int:%016llx\\n\",\n\t\t//\t\t\tclosest_int_dusec, event_int_dusec);\n\t\tdoit = 0;\n\t\tif(event_int_dusec && (closest_int_dusec < event_int_dusec)) {\n\t\t\t// There was a pending event.  Discard it\n\t\t\t// printf(\"Call remove_event_mockingboard\\n\");\n\t\t\tremove_event_mockingboard();\n\t\t\tdoit = 1;\n\t\t}\n\t\tif(!event_int_dusec || doit) {\n\t\t\t//printf(\"Call add_event_mockingboard: %016llx %lld\\n\",\n\t\t\t//\tclosest_int_dusec, closest_int_dusec);\n\t\t\tadd_event_mockingboard(closest_int_dusec << 16);\n\t\t\tg_mockingboard_event_int_dusec = closest_int_dusec;\n\t\t\tdbg_log_info(dfcyc,\n\t\t\t\t(word32)(closest_int_dusec - (dfcyc >> 16)),\n\t\t\t\t\t\t\t\t0, 0xc1);\n\t\t}\n\t}\n}\n\nvoid\nmockingboard_event(dword64 dfcyc)\n{\n\t// Received an event--we believe we may need to set an IRQ now.\n\t// Event was already removed from the event queue\n\t// printf(\"Mockingboard_event received at %016llx\\n\", dfcyc);\n\tdbg_log_info(dfcyc, 0, 0, 0xc2);\n\tg_mockingboard_event_int_dusec = 0;\n\tmock_update_timers(1, dfcyc);\n}\n\nword32\nmockingboard_read(word32 loc, dword64 dfcyc)\n{\n\tint\tpair_num;\n\n\t// printf(\"mockingboard read: %04x\\n\", loc);\n\tpair_num = (loc >> 7) & 1;\t\t// 0 or 1\n\treturn mock_6522_read(pair_num, loc & 0xf, dfcyc);\n}\n\nvoid\nmockingboard_write(word32 loc, word32 val, dword64 dfcyc)\n{\n\tint\tpair_num;\n\n\t// printf(\"mockingboard write: %04x=%02x\\n\", loc, val);\n\tpair_num = (loc >> 7) & 1;\t\t// 0 or 1\n\tmock_6522_write(pair_num, loc & 0xf, val, dfcyc);\n}\n\nword32\nmock_6522_read(int pair_num, word32 loc, dword64 dfcyc)\n{\n\tMos6522\t*mos6522ptr;\n\tword32\tval;\n\n\t// Read from 6522 #pair_num loc (0-15)\n\tmos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);\n\tval = 0;\n\tswitch(loc) {\n\tcase 0x0:\t\t// ORB/IRB\n\t\t// Connected to AY8913 { RESET, BDIR, BC1 }\n\t\tval = mos6522ptr->orb;\n\t\t\t// There are no outputs from AY8913 to the 6522 B Port\n\t\tbreak;\n\tcase 0x1:\t\t// ORA\n\tcase 0xf:\t\t// ORA, no handshake\n\t\tval = mos6522ptr->ora;\n\t\tbreak;\n\tcase 0x2:\t\t// DDRB\n\t\tval = mos6522ptr->ddrb;\n\t\tbreak;\n\tcase 0x3:\t\t// DDRA\n\t\tval = mos6522ptr->ddra;\n\t\tbreak;\n\tcase 0x4:\t\t// T1C-L (timer[0])\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = (mos6522ptr->timer1_counter - 1) & 0xff;\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\t\tmos6522ptr->ifr & (~0x40), mos6522ptr->ier);\n\t\t\t// Clear Bit 6\n\t\tmock_update_timers(1, dfcyc);\t// Prepare another int (maybe)\n\t\tdbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc5);\n\t\tbreak;\n\tcase 0x5:\t\t// T1C-H\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = ((mos6522ptr->timer1_counter - 1) >> 8) & 0xff;\n\t\tbreak;\n\tcase 0x6:\t\t// T1L-L\n\t\tval = mos6522ptr->timer1_latch & 0xff;\n\t\tbreak;\n\tcase 0x7:\t\t// T1L-H\n\t\tval = (mos6522ptr->timer1_latch >> 8) & 0xff;\n\t\tbreak;\n\tcase 0x8:\t\t// T2C-L\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = (mos6522ptr->timer2_counter - 1) & 0xff;\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\tmos6522ptr->ifr & (~0x20), mos6522ptr->ier);\n\t\t\t// Clear Bit 5\n\t\tmock_update_timers(1, dfcyc);\t// Prepare another int (maybe)\n\t\tdbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc6);\n\t\tbreak;\n\tcase 0x9:\t\t// T2C-H\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = ((mos6522ptr->timer2_counter - 1) >> 8) & 0xff;\n\t\tbreak;\n\tcase 0xa:\t\t// SR\n\t\tval = mos6522ptr->sr;\n\t\t//halt_printf(\"Reading SR %d %02x\\n\", pair_num, val);\n\t\tbreak;\n\tcase 0xb:\t\t// ACR\n\t\tval = mos6522ptr->acr;\n\t\tbreak;\n\tcase 0xc:\t\t// PCR\n\t\tval = mos6522ptr->pcr;\n\t\tbreak;\n\tcase 0xd:\t\t// IFR\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = mos6522ptr->ifr;\n\t\tbreak;\n\tcase 0xe:\t\t// IER\n\t\tval = mos6522ptr->ier | 0x80;\t\t// Despite MOS6522\n\t\tbreak;\t\t\t\t\t//  datasheet, bit 7 = 1\n\t}\n\t// printf(\"6522 %d loc:%x ret:%02x\\n\", pair_num, loc, val);\n\treturn val;\n}\n\nvoid\nmock_6522_write(int pair_num, word32 loc, word32 val, dword64 dfcyc)\n{\n\tMos6522\t*mos6522ptr;\n\tword32\tora, orb, new_val, mask;\n\n\t// Write to 6522 #num6522 loc (0-15)\n\n\t// printf(\"6522 %d loc:%x write:%02x\\n\", pair_num, loc, val);\n\n\tmos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);\n\tswitch(loc) {\n\tcase 0x0:\t\t// ORB\n\t\tmask = mos6522ptr->ddrb;\n\t\torb = mos6522ptr->orb;\n\t\tnew_val = (val & mask) | (orb & (~mask));\n\t\tif(orb != new_val) {\n\t\t\tmock_ay8913_control_update(pair_num, new_val, orb,\n\t\t\t\t\t\t\t\t\tdfcyc);\n\t\t}\n\t\tmos6522ptr->orb = new_val;\n\t\tbreak;\n\tcase 0x1:\t\t// ORA\n\tcase 0xf:\t\t// ORA, no handshake\n\t\tmask = mos6522ptr->ddra;\n\t\tora = mos6522ptr->ora;\n\t\tnew_val = (val & mask) | (ora & (~mask));\n\t\tmos6522ptr->ora = new_val;\n\t\tbreak;\n\tcase 0x2:\t\t// DDRB\n\t\torb = mos6522ptr->orb;\n\t\tnew_val = (orb & val) | (orb & (~val));\n\t\tif(orb != new_val) {\n\t\t\tmock_ay8913_control_update(pair_num, new_val, orb,\n\t\t\t\t\t\t\t\t\tdfcyc);\n\t\t}\n\t\tmos6522ptr->orb = new_val;\n\t\tmos6522ptr->ddrb = val;\n\t\treturn;\n\tcase 0x3:\t\t// DDRA\n\t\tora = mos6522ptr->ora;\n\t\tmos6522ptr->ora = (ora & val) | (ora & (~val));\n\t\tmos6522ptr->ddra = val;\n\t\treturn;\n\tcase 0x4:\t\t// T1C-L\n\t\tmock_update_timers(0, dfcyc);\n\t\tmos6522ptr->timer1_latch =\n\t\t\t\t(mos6522ptr->timer1_latch & 0x1ff00) | val;\n\t\t// printf(\"Set T1C-L, timer1_latch=%05x\\n\",\n\t\t//\t\t\t\tmos6522ptr->timer1_latch);\n\t\tbreak;\n\tcase 0x5:\t\t// T1C-H\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = (mos6522ptr->timer1_latch & 0xff) | (val << 8);\n\t\tmos6522ptr->timer1_latch = val;\n\t\tmos6522ptr->timer1_counter = val + 2;\n\t\t\t// The actual timer1_counter update happens next cycle,\n\t\t\t//  so we want val+1, plus another 1\n\t\t// printf(\"Set T1C-H, timer1_latch=%05x\\n\",\n\t\t//\t\t\t\tmos6522ptr->timer1_latch);\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\tmos6522ptr->ifr & (~0x40), mos6522ptr->ier);\n\t\t\t// Clear Bit 6\n\t\tmock_update_timers(1, dfcyc);\n\t\tdbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc7);\n\t\tbreak;\n\tcase 0x6:\t\t// T1L-L\n\t\tmock_update_timers(0, dfcyc);\n\t\tmos6522ptr->timer1_latch =\n\t\t\t\t(mos6522ptr->timer1_latch & 0x1ff00) | val;\n\t\tbreak;\n\tcase 0x7:\t\t// T1L-H\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = (mos6522ptr->timer1_latch & 0xff) | (val << 8);\n\t\tmos6522ptr->timer1_latch = val;\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\tmos6522ptr->ifr & (~0x40), mos6522ptr->ier);\n\t\t\t// Clear Bit 6\n\t\tmock_update_timers(1, dfcyc);\n\t\t// mock_show_pair(pair_num, dfcyc, \"Wrote T1L-H\");\n\t\tdbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc8);\n\t\tbreak;\n\tcase 0x8:\t\t// T2C-L\n\t\tmos6522ptr->timer2_latch = (mos6522ptr->timer2_latch & 0xff00) |\n\t\t\t\t\t\t\t\t\tval;\n\t\tbreak;\n\tcase 0x9:\t\t// T2C-H\n\t\tmock_update_timers(1, dfcyc);\n\t\tval = (mos6522ptr->timer2_latch & 0xff) | (val << 8);\n\t\tmos6522ptr->timer2_latch = val;\n\t\tmos6522ptr->timer2_counter = val + 2;\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\tmos6522ptr->ifr & (~0x20), mos6522ptr->ier);\n\t\t\t// Clear bit 5\n\t\tmock_update_timers(1, dfcyc);\n\t\tbreak;\n\tcase 0xa:\t\t// SR\n\t\tmos6522ptr->sr = val;\n\t\thalt_printf(\"Wrote SR reg: %d %02x\\n\", pair_num, val);\n\t\tbreak;\n\tcase 0xb:\t\t// ACR\n\t\tmock_update_timers(0, dfcyc);\n\t\tmos6522ptr->acr = val;\n\t\tmock_update_timers(1, dfcyc);\n\t\tbreak;\n\tcase 0xc:\t\t// PCR\n\t\tmos6522ptr->pcr = val;\n\t\tbreak;\n\tcase 0xd:\t\t// IFR\n\t\tmock_update_timers(1, dfcyc);\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\t\tmos6522ptr->ifr & (~val), mos6522ptr->ier);\n\t\tmock_update_timers(1, dfcyc);\n\t\tdbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xc9);\n\t\tbreak;\n\tcase 0xe:\t\t// IER\n\t\t// Recalculate effective IFR with new IER\n\t\tmock_update_timers(1, dfcyc);\n\t\tif(val & 0x80) {\t\t\t// Set EIR bits\n\t\t\tval = mos6522ptr->ier | val;\n\t\t} else {\t\t\t\t// Clear EIR bits\n\t\t\tval = mos6522ptr->ier & (~val);\n\t\t}\n\t\tval = val & 0x7f;\n\t\tmos6522ptr->ier = val;\n\t\tmos6522ptr->ifr = mock_6522_new_ifr(dfcyc, pair_num,\n\t\t\t\t\t\t\tmos6522ptr->ifr, val);\n\t\tmock_update_timers(1, dfcyc);\n\t\t// mock_show_pair(pair_num, dfcyc, \"Wrote IER\");\n\t\tdbg_log_info(dfcyc, mos6522ptr->ifr, val, 0xca);\n\t\tbreak;\n\t}\n}\n\nword32\nmock_6522_new_ifr(dword64 dfcyc, int pair_num, word32 ifr, word32 ier)\n{\n\tword32\tirq_mask;\n\n\t// Determine if there are any interrupts pending now\n\tirq_mask = IRQ_PENDING_MOCKINGBOARDA << pair_num;\n\tif((ifr & ier & 0x7f) == 0) {\n\t\t// No IRQ pending anymore\n\t\tifr = ifr & 0x7f;\t\t// Clear bit 7\n\t\tif(g_irq_pending & irq_mask) {\n\t\t\t// printf(\"MOCK INT OFF\\n\");\n\t\t}\n\t\tremove_irq(irq_mask);\n\t\tdbg_log_info(dfcyc, (ier << 8) | ifr, g_irq_pending, 0xcf);\n\t} else {\n\t\t// IRQ is pending\n\t\tifr = ifr | 0x80;\t\t// Set bit 7\n\t\tif(!(g_irq_pending & irq_mask)) {\n\t\t\t// printf(\"MOCK INT ON\\n\");\n\t\t}\n\t\tadd_irq(irq_mask);\n\t\tdbg_log_info(dfcyc, (ier << 8) | ifr, g_irq_pending, 0xcf);\n\t}\n\treturn ifr;\n}\n\nvoid\nmock_ay8913_reg_read(int pair_num)\n{\n\tMos6522\t*mos6522ptr;\n\tAy8913\t*ay8913ptr;\n\tword32\treg_addr_latch, mask, val, ora;\n\n\tmos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);\n\tay8913ptr = &(g_mockingboard.pair[pair_num].ay8913);\n\treg_addr_latch = ay8913ptr->reg_addr_latch;\n\tval = 0;\n\tif(reg_addr_latch < 16) {\n\t\tval = ay8913ptr->regs[reg_addr_latch];\n\t}\n\t// ORA at 6522 is merge of ORA using DDRA\n\tmask = mos6522ptr->ddra;\n\tora = mos6522ptr->ora;\n\tmos6522ptr->ora = (ora & mask) | (val & (~mask));\n}\n\nword32 g_mock_channel_regs[3] = {\n\t0x39c3,\t\t// channel A: regs 0,1,6,7,8,11,12,13\n\t0x3acc,\t\t// channel B: regs 2,3,6,7,9,11,12,13\n\t0x3cf0\t\t// channel C: regs 4,5,6,7,10,11,12,13\n};\n\nvoid\nmock_ay8913_reg_write(int pair_num, dword64 dfcyc)\n{\n\tMos6522\t*mos6522ptr;\n\tAy8913\t*ay8913ptr;\n\tdouble\tdsamps;\n\tword32\treg_addr_latch, ora, mask, rmask, do_print;\n\tint\ti;\n\n\tmos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);\n\tay8913ptr = &(g_mockingboard.pair[pair_num].ay8913);\n\treg_addr_latch = ay8913ptr->reg_addr_latch;\n\tora = mos6522ptr->ora;\n\tdsamps = dfcyc * g_dsamps_per_dfcyc;\n\tif(reg_addr_latch < 16) {\n\t\tmask = (g_mockingboard.disable_mask >> (3*pair_num)) & 7;\n\t\trmask = 0;\n\t\tdo_print = 0;\n\t\tfor(i = 0; i < 3; i++) {\n\t\t\tif(((mask >> i) & 1) == 0) {\n\t\t\t\trmask |= g_mock_channel_regs[i];\n\t\t\t}\n\t\t}\n\t\tdo_print = (rmask >> reg_addr_latch) & 1;\n\t\tif((ora != ay8913ptr->regs[reg_addr_latch]) ||\n\t\t\t\t\t\t(reg_addr_latch == 13)) {\n\t\t\t// New value, or writing to Envelope control\n\t\t\tdo_print = 0;\n\t\t\tif(do_print) {\n\t\t\t\tprintf(\"%.2lf %016llx mock pair%d reg[%d]=\"\n\t\t\t\t\t\"%02x. [2,3]=%02x_%02x [67]=%02x,%02x, \"\n\t\t\t\t\t\"[9]=%02x, [12,11]=%02x_%02x [13]=\"\n\t\t\t\t\t\"%02x\\n\", dsamps, dfcyc, pair_num,\n\t\t\t\t\treg_addr_latch, ora,\n\t\t\t\t\tay8913ptr->regs[3], ay8913ptr->regs[2],\n\t\t\t\t\tay8913ptr->regs[6], ay8913ptr->regs[7],\n\t\t\t\t\tay8913ptr->regs[9], ay8913ptr->regs[12],\n\t\t\t\t\tay8913ptr->regs[11],\n\t\t\t\t\tay8913ptr->regs[13]);\n\t\t\t}\n\t\t\tsound_play(dfcyc);\n\t\t}\n\t\tay8913ptr->regs[reg_addr_latch] = ora;\n\t\tif(reg_addr_latch == 13) {\t\t// Envelope control\n\t\t\tay8913ptr->env_dsamp &= 0x1fffffffffffULL;\n\t\t\t// Clear \"hold\" in (env_val & (0x80 << 40))\n\t\t}\n\t}\n}\n\nvoid\nmock_ay8913_control_update(int pair_num, word32 new_val, word32 prev_val,\n\t\t\t\t\t\t\tdword64 dfcyc)\n{\n\tMos6522\t*mos6522ptr;\n\tAy8913\t*ay8913ptr;\n\n\tmos6522ptr = &(g_mockingboard.pair[pair_num].mos6522);\n\tay8913ptr = &(g_mockingboard.pair[pair_num].ay8913);\n\t// printf(\"ay8913 %d control now:%02x\\n\", pair_num, new_val);\n\n\t// new_val and prev_val are { reset_l, BDIR, BC1 }\n\t// 4=Idle; 5=Read; 6=Write; 7=Latch_addr\n\t// Latch new address and write data at the time the ctl changes to Idle\n\t// Do read as soon as the ctl indicates to do a read.\n\n\tif((new_val & 4) == 0) {\n\t\tmock_ay8913_reset(pair_num, dfcyc);\n\t\treturn;\n\t}\n\tnew_val = new_val & 7;\n\tprev_val = prev_val & 7;\n\tif(prev_val == 7) {\t\t// Latch new address, latch it now\n\t\tay8913ptr->reg_addr_latch = mos6522ptr->ora;\n\t} else if(prev_val == 6) {\t// Write data, do it now\n\t\tmock_ay8913_reg_write(pair_num, dfcyc);\n\t}\n\tif(new_val == 5) {\n\t\tmock_ay8913_reg_read(pair_num);\n\t}\n}\n\nvoid\nmockingboard_show(int got_num, word32 disable_mask)\n{\n\tint\ti, j;\n\n\tif(got_num) {\n\t\tg_mockingboard.disable_mask = disable_mask;\n\t}\n\tprintf(\"g_mockingboard.disable_mask:%02x\\n\",\n\t\t\t\t\t\tg_mockingboard.disable_mask);\n\tfor(i = 0; i < 2; i++) {\n\t\tfor(j = 0; j < 14; j++) {\n\t\t\tprintf(\"Mockingboard pair[%d].reg[%d]=%02x\\n\", i, j,\n\t\t\t\tg_mockingboard.pair[i].ay8913.regs[j]);\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/moremem.c",
    "content": "const char rcsid_moremem_c[] = \"@(#)$KmKId: moremem.c,v 1.306 2024-09-15 13:56:12+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2024 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include \"defc.h\"\n\nextern char g_kegs_version_str[];\n\nextern byte *g_memory_ptr;\nextern byte *g_dummy_memory1_ptr;\nextern byte *g_slow_memory_ptr;\nextern byte *g_rom_fc_ff_ptr;\nextern byte *g_rom_cards_ptr;\n\nextern word32 g_slow_mem_changed[];\n\nextern int g_num_breakpoints;\nextern Break_point g_break_pts[];\n\nextern Page_info page_info_rd_wr[];\n\nextern int Verbose;\nextern int g_rom_version;\nextern int g_user_page2_shadow;\nextern Iwm g_iwm;\nextern int g_halt_sim;\nextern int g_config_control_panel;\n\n\n/* from iwm.c */\nint\tg_num_shadow_all_banks = 0;\n\n#define IOR(val) ( (val) ? 0x80 : 0x00 )\n\n\nextern int g_cur_a2_stat;\n\nint\tg_em_emubyte_cnt = 0;\nint\tg_paddle_buttons = 0;\nint\tg_irq_pending = 0;\n\nword32\tg_c023_val = 0;\nword32\tg_c029_val_some = 0x41;\nword32\tg_c02b_val = 0x08;\nword32\tg_c02d_int_crom = 0;\nword32\tg_c033_data = 0;\nword32\tg_c034_val = 0;\nword32\tg_c035_shadow_reg = 0x08;\t// A bit set inhibits that shadowing\n\t\t// [6]=Inhibit I/O and LC, [4]=Inhibit Aux hires, [3]=Inh SHR\n\t\t// [2]=Inh hires pg 2, [1]=Inh hires pg 1, [0]=Inh text pages\nword32\tg_c036_val_speed = 0x80;\t// [7]=Fast, [4]=Shadow in all banks,\n\t\t\t\t\t// [3:0]=slot 7..4 disk motor detect\nword32\tg_c03ef_doc_ptr = 0;\nword32\tg_c041_val = 0;\t\t/* C041_EN_25SEC_INTS, C041_EN_MOVE_INTS */\nword32\tg_c046_val = 0;\nword32\tg_c05x_annuncs = 0;\nword32\tg_c068_statereg = 0;\t// [7]=ALTZP, [6]=PAGE2, [5]=RAMRD, [4]=RAMWRT\n\t\t\t// [3]=RDROM, [2]=LCBANK2, [1]=ROMBANK, [0]=INTCXROM\n\t\t\t// KEGS special: [8]=PREWRITE_LC, [9]=WRDEFRAM,\n\t\t\t//\t[10]=INTC8ROM\nword32\tg_c06d_val = 0;\nword32\tg_c06f_val = 0;\nword32\tg_zipgs_unlock = 0;\nword32\tg_zipgs_reg_c059 = 0x5f;\n\t// 7=LC cache dis, 6==5ms paddle del en, 5==5ms ext del en,\n\t// 4==5ms c02e enab, 3==CPS follow enab, 2-0: 111\nword32\tg_zipgs_reg_c05a = 0x0f;\n\t// 7:4 = current ZIP speed, 0=100%, 1=93.75%, F=6.25%\n\t// 3:0: always 1111\nword32\tg_zipgs_reg_c05b = 0x40;\n\t// 7==1ms clock, 6==cshupd: tag data at c05f updated\n\t// 5==LC cache disable, 4==bd is disabled, 3==delay in effect,\n\t// 2==rombank, 1-0==ram size (00:8K, 01=16K, 10=32K, 11=64K)\nword32\tg_zipgs_reg_c05c = 0x00;\n\t// 7:1==slot delay enable (for 52-54ms), 0==speaker 5ms delay\n\nword32\tg_c06c_latched_cyc = 0;\n\n#define SLINKY_RAM_SIZE\t0x100000\t\t/* 1MB */\nword32\tg_slinky_addr = 0;\nbyte g_slinky_ram[SLINKY_RAM_SIZE];\n\n\n#define EMUSTATE(a)\t{ #a, &a }\n\nEmustate_intlist g_emustate_intlist[] = {\n\t// EMUSTATE(g_cur_a2_stat),\n\t// EMUSTATE(g_paddle_buttons),\n\n\t// EMUSTATE(g_em_emubyte_cnt),\n\t// EMUSTATE(g_irq_pending),\n\tEMUSTATE(g_c023_val),\n\tEMUSTATE(g_c029_val_some),\n\tEMUSTATE(g_c02b_val),\n\tEMUSTATE(g_c02d_int_crom),\n\tEMUSTATE(g_c033_data),\n\tEMUSTATE(g_c034_val),\n\tEMUSTATE(g_c035_shadow_reg),\n\tEMUSTATE(g_c036_val_speed),\n\tEMUSTATE(g_c041_val),\n\tEMUSTATE(g_c046_val),\n\tEMUSTATE(g_c05x_annuncs),\n\tEMUSTATE(g_c068_statereg),\n\tEMUSTATE(g_zipgs_unlock),\n\tEMUSTATE(g_zipgs_reg_c059),\n\tEMUSTATE(g_zipgs_reg_c05a),\n\tEMUSTATE(g_zipgs_reg_c05b),\n\tEMUSTATE(g_zipgs_reg_c05c),\n\t{ 0, 0, }\n};\n\nextern dword64 g_paddle_trig_dfcyc;\nextern dword64 g_last_vbl_dfcyc;\n\nEmustate_dword64list g_emustate_dword64list[] = {\n\tEMUSTATE(g_paddle_trig_dfcyc),\n\tEMUSTATE(g_last_vbl_dfcyc),\n\t{ 0, 0, }\n};\n\nextern word32 g_mem_size_total;\n\nEmustate_word32list g_emustate_word32list[] = {\n\tEMUSTATE(g_mem_size_total),\n\tEMUSTATE(g_c03ef_doc_ptr),\n\t{ 0, 0, }\n};\n\n\n#define UNIMPL_READ\t\\\n\thalt_printf(\"UNIMP READ to addr %08x\\n\", loc);\t\\\n\treturn 0;\n\n#define UNIMPL_WRITE\t\\\n\thalt_printf(\"UNIMP WRITE to addr %08x, val: %04x\\n\", loc, val);\t\\\n\treturn;\n\nvoid\nfixup_brks()\n{\n\tword32\tpage, tmp, tmp2, end_page;\n\tPg_info\tval;\n\tint\tis_wr_only, num;\n\tint\ti;\n\n\tnum = g_num_breakpoints;\n\tfor(i = 0; i < num; i++) {\n\t\tpage = (g_break_pts[i].start_addr >> 8) & 0xffff;\n\t\tend_page = (g_break_pts[i].end_addr >> 8) & 0xffff;\n\t\tis_wr_only = (g_break_pts[i].start_addr >> 24) & 1;\n\t\twhile(page <= end_page) {\n\t\t\tif(!is_wr_only) {\n\t\t\t\tval = GET_PAGE_INFO_RD(page);\n\t\t\t\ttmp = PTR2WORD(val) & 0xff;\n\t\t\t\ttmp2 = tmp | BANK_IO_TMP | BANK_BREAK;\n\t\t\t\tSET_PAGE_INFO_RD(page, val - tmp + tmp2);\n\t\t\t}\n\t\t\tval = GET_PAGE_INFO_WR(page);\n\t\t\ttmp = PTR2WORD(val) & 0xff;\n\t\t\ttmp2 = tmp | BANK_IO_TMP | BANK_BREAK;\n\t\t\tSET_PAGE_INFO_WR(page, val - tmp + tmp2);\n\t\t\tpage++;\n\t\t}\n\t}\n}\n\nvoid\nfixup_hires_on()\n{\n\tif((g_cur_a2_stat & ALL_STAT_ST80) == 0) {\n\t\treturn;\n\t}\n\n\tfixup_bank0_2000_4000();\n\tfixup_brks();\n}\n\nvoid\nfixup_bank0_2000_4000()\n{\n\tbyte\t*mem0rd;\n\tbyte\t*mem0wr;\n\tword32\tstart_page;\n\tint\ti;\n\n\t// Do banks $00 and $E0\n\n\tfor(i = 0; i < 2; i++) {\n\t\tmem0rd = &(g_memory_ptr[0x2000]);\n\t\tstart_page = 0x20;\n\t\tif(i) {\n\t\t\tstart_page += 0xe000;\t\t\t// Bank $E0\n\t\t\tmem0rd = &(g_slow_memory_ptr[0x2000]);\n\t\t}\n\t\tmem0wr = mem0rd;\n\t\tif((g_cur_a2_stat & ALL_STAT_ST80) &&\n\t\t\t\t\t(g_cur_a2_stat & ALL_STAT_HIRES)) {\n\t\t\tif(g_cur_a2_stat & ALL_STAT_PAGE2) {\n\t\t\t\tmem0rd += 0x10000;\n\t\t\t\tmem0wr += 0x10000;\n\t\t\t\tif(((g_c035_shadow_reg & 0x12) == 0) ||\n\t\t\t\t\t((g_c035_shadow_reg & 0x8) == 0) || i) {\n\t\t\t\t\tmem0wr += BANK_SHADOW2;\n\t\t\t\t}\n\t\t\t} else if(((g_c035_shadow_reg & 0x02) == 0) || i) {\n\t\t\t\tmem0wr += BANK_SHADOW;\n\t\t\t}\n\t\t} else {\n\t\t\tif(RAMRD) {\n\t\t\t\tmem0rd += 0x10000;\n\t\t\t}\n\t\t\tif(RAMWRT) {\n\t\t\t\tmem0wr += 0x10000;\n\t\t\t\tif(((g_c035_shadow_reg & 0x12) == 0) ||\n\t\t\t\t\t((g_c035_shadow_reg & 0x8) == 0) || i) {\n\t\t\t\t\tmem0wr += BANK_SHADOW2;\n\t\t\t\t}\n\t\t\t} else if(((g_c035_shadow_reg & 0x02) == 0) || i) {\n\t\t\t\tmem0wr += BANK_SHADOW;\n\t\t\t}\n\t\t}\n\t\tfixup_any_bank_any_page(start_page, 0x20, mem0rd, mem0wr);\n\t}\n}\n\nvoid\nfixup_bank0_0400_0800()\n{\n\tbyte\t*mem0rd;\n\tbyte\t*mem0wr;\n\tword32\tstart_page, shadow;\n\tint\ti;\n\n\tfor(i = 0; i < 2; i++) {\n\t\tmem0rd = &(g_memory_ptr[0x400]);\n\t\tstart_page = 4;\n\t\tif(i) {\n\t\t\tstart_page += 0xe000;\t\t\t// Bank $E0\n\t\t\tmem0rd = &(g_slow_memory_ptr[0x400]);\n\t\t}\n\t\tmem0wr = mem0rd;\n\t\tshadow = BANK_SHADOW;\n\t\tif(g_cur_a2_stat & ALL_STAT_ST80) {\n\t\t\tif(g_cur_a2_stat & ALL_STAT_PAGE2) {\n\t\t\t\tshadow = BANK_SHADOW2;\n\t\t\t\tmem0rd += 0x10000;\n\t\t\t\tmem0wr += 0x10000;\n\t\t\t}\n\t\t} else {\n\t\t\tif(RAMWRT) {\n\t\t\t\tshadow = BANK_SHADOW2;\n\t\t\t\tmem0wr += 0x10000;\n\t\t\t}\n\t\t\tif(RAMRD) {\n\t\t\t\tmem0rd += 0x10000;\n\t\t\t}\n\t\t}\n\t\tif(((g_c035_shadow_reg & 0x01) == 0) || i) {\n\t\t\tmem0wr += shadow;\n\t\t}\n\n\t\tfixup_any_bank_any_page(start_page, 4, mem0rd, mem0wr);\n\t}\n}\n\nvoid\nfixup_any_bank_any_page(word32 start_page, int num_pages, byte *mem0rd,\n\t\tbyte *mem0wr)\n{\n\tint\ti;\n\n\tfor(i = 0; i < num_pages; i++) {\n\t\tSET_PAGE_INFO_RD(i + start_page, mem0rd);\n\t\tmem0rd += 0x100;\n\t}\n\n\tfor(i = 0; i < num_pages; i++) {\n\t\tSET_PAGE_INFO_WR(i + start_page, mem0wr);\n\t\tmem0wr += 0x100;\n\t}\n\n}\n\nvoid\nfixup_intcx()\n{\n\tbyte\t*rom10000;\n\tbyte\t*rom_inc;\n\tword32\tintcx, mask;\n\tint\tno_io_shadow, off, start_k;\n\tint\tj, k;\n\n\trom10000 = &(g_rom_fc_ff_ptr[0x30000]);\n\n\tno_io_shadow = (g_c035_shadow_reg & 0x40);\n\n\tstart_k = 0;\n\tif(no_io_shadow) {\n\t\t/* if not shadowing, banks 0 and 1 are not affected by intcx */\n\t\tstart_k = 2;\n\t}\n\n\tintcx = (g_c068_statereg & 1);\t// set means use internal rom\n\t// printf(\"fixup_intcx, intcx:%d, no_io:%d, c02d:%02x\\n\", intcx,\n\t//\t\t\t\tno_io_shadow, g_c02d_int_crom);\n\tfor(k = start_k; k < 4; k++) {\n\t\toff = k;\n\t\tif(k >= 2) {\n\t\t\toff += (0xe0 - 2);\n\t\t}\n\t\t/* step off through 0x00, 0x01, 0xe0, 0xe1 */\n\n\t\toff = off << 8;\t\t// Now 0x0000, 0x0100, 0xe000, 0xe100\n\t\tSET_PAGE_INFO_RD(0xc0 + off, SET_BANK_IO);\n\n\t\tfor(j = 0xc1; j < 0xc8; j++) {\n\t\t\tmask = 1 << (j & 0xf);\n\t\t\trom_inc = SET_BANK_IO;\n\t\t\tif(((g_c02d_int_crom & mask) == 0) || intcx) {\n\t\t\t\trom_inc = rom10000 + (j << 8);\n\t\t\t} else {\n\t\t\t\t// User-slot rom\n\t\t\t\trom_inc = &(g_rom_cards_ptr[0]) +\n\t\t\t\t\t\t\t((j - 0xc0) << 8);\n\t\t\t\tif(j == 0xc4) {\t\t// Mockingboard\n\t\t\t\t\trom_inc = SET_BANK_IO;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif((j == 0xc3) && !(g_c068_statereg & 0x400)) {\n\t\t\t\t// If INTC8ROM not set, we need to\n\t\t\t\t//  watch for reads from C3xx to set it\n\t\t\t\trom_inc = SET_BANK_IO;\n\t\t\t}\n\t\t\tSET_PAGE_INFO_RD(j + off, rom_inc);\n\t\t}\n\t\tfor(j = 0xc8; j < 0xd0; j++) {\n\t\t\t/* c800 - cfff */\n\t\t\tif(intcx || (g_c068_statereg & 0x400)) {\n\t\t\t\t// INTCXROM or INTC8ROM is set\n\t\t\t\trom_inc = rom10000 + (j << 8);\n\t\t\t\tif((j == 0xcf) && (g_c068_statereg & 0x400)) {\n\t\t\t\t\trom_inc = SET_BANK_IO;\n\t\t\t\t}\n\t\t\t} else {\t// c800 space not necessarily mapped\n\t\t\t\trom_inc = SET_BANK_IO;\n\t\t\t}\n\t\t\tSET_PAGE_INFO_RD(j + off, rom_inc);\n\t\t}\n\t\tfor(j = 0xc0; j < 0xd0; j++) {\n\t\t\tSET_PAGE_INFO_WR(j + off, SET_BANK_IO);\n\t\t}\n\t}\n\n\tfixup_brks();\n}\n\nvoid\nfixup_st80col(dword64 dfcyc)\n{\n\tint\tcur_a2_stat;\n\n\tcur_a2_stat = g_cur_a2_stat;\n\n\tfixup_bank0_0400_0800();\n\n\tif(cur_a2_stat & ALL_STAT_HIRES) {\n\t\t/* fixup no matter what PAGE2 since PAGE2 and RAMRD/WR */\n\t\t/*  can work against each other */\n\t\tfixup_bank0_2000_4000();\n\t}\n\n\tif(cur_a2_stat & ALL_STAT_PAGE2) {\n\t\tchange_display_mode(dfcyc);\n\t}\n\n\tfixup_brks();\n}\n\nvoid\nfixup_altzp()\n{\n\tbyte\t*mem0rd;\n\tword32\tstart_page, altzp;\n\tint\ti;\n\n\taltzp = ALTZP;\n\tfor(i = 0; i < 2; i++) {\n\t\tstart_page = 0;\n\t\tmem0rd = &(g_memory_ptr[0]);\n\t\tif(i) {\n\t\t\tstart_page = 0xe000;\n\t\t\tmem0rd = &(g_slow_memory_ptr[0]);\n\t\t}\n\t\tif(altzp) {\n\t\t\tmem0rd += 0x10000;\n\t\t}\n\t\tfixup_any_bank_any_page(start_page, 2, mem0rd, mem0rd);\n\t}\n\n\t/* No need for fixup_brks since called from set_statereg() */\n}\n\nvoid\nfixup_page2(dword64 dfcyc)\n{\n\tif((g_cur_a2_stat & ALL_STAT_ST80)) {\n\t\tfixup_bank0_0400_0800();\n\t\tif((g_cur_a2_stat & ALL_STAT_HIRES)) {\n\t\t\tfixup_bank0_2000_4000();\n\t\t}\n\t} else {\n\t\tchange_display_mode(dfcyc);\n\t}\n}\n\nvoid\nfixup_ramrd()\n{\n\tbyte\t*mem0rd;\n\tword32\tstart_page, cur_a2_stat;\n\tint\ti, j;\n\n\tcur_a2_stat = g_cur_a2_stat;\n\n\tif((cur_a2_stat & ALL_STAT_ST80) == 0) {\n\t\tfixup_bank0_0400_0800();\n\t}\n\tif( ((cur_a2_stat & ALL_STAT_ST80) == 0) ||\n\t\t\t\t((cur_a2_stat & ALL_STAT_HIRES) == 0) ) {\n\t\tfixup_bank0_2000_4000();\n\t}\n\n\tfor(i = 0; i < 2; i++) {\n\t\tstart_page = 0;\n\t\tmem0rd = &(g_memory_ptr[0x0000]);\n\t\tif(i) {\n\t\t\tstart_page = 0xe000;\n\t\t\tmem0rd = &(g_slow_memory_ptr[0]);\n\t\t}\n\n\t\tif(RAMRD) {\n\t\t\tmem0rd += 0x10000;\n\t\t}\n\n\t\tSET_PAGE_INFO_RD(start_page + 2, mem0rd + 0x200);\n\t\tSET_PAGE_INFO_RD(start_page + 3, mem0rd + 0x300);\n\n\t\tfor(j = 8; j < 0x20; j++) {\n\t\t\tSET_PAGE_INFO_RD(start_page + j, mem0rd + j*0x100);\n\t\t}\n\n\t\tfor(j = 0x40; j < 0xc0; j++) {\n\t\t\tSET_PAGE_INFO_RD(start_page + j, mem0rd + j*0x100);\n\t\t}\n\t}\n\n\t/* No need for fixup_brks since only called from set_statereg() */\n}\n\nvoid\nfixup_ramwrt()\n{\n\tbyte\t*mem0wr;\n\tword32\tstart_page, cur_a2_stat, shadow, ramwrt;\n\tint\ti, j;\n\n\tcur_a2_stat = g_cur_a2_stat;\n\n\tif((cur_a2_stat & ALL_STAT_ST80) == 0) {\n\t\tfixup_bank0_0400_0800();\n\t}\n\tif( ((cur_a2_stat & ALL_STAT_ST80) == 0) ||\n\t\t\t\t((cur_a2_stat & ALL_STAT_HIRES) == 0) ) {\n\t\tfixup_bank0_2000_4000();\n\t}\n\n\tfor(i = 0; i < 2; i++) {\n\t\tstart_page = 0;\n\t\tmem0wr = &(g_memory_ptr[0x0000]);\n\t\tif(i) {\n\t\t\tstart_page = 0xe000;\n\t\t\tmem0wr = &(g_slow_memory_ptr[0]);\n\t\t}\n\n\t\tramwrt = RAMWRT;\n\t\tif(ramwrt) {\n\t\t\tmem0wr += 0x10000;\n\t\t}\n\n\t\tSET_PAGE_INFO_WR(start_page + 2, mem0wr + 0x200);\n\t\tSET_PAGE_INFO_WR(start_page + 3, mem0wr + 0x300);\n\n\t\tshadow = BANK_SHADOW;\n\t\tif(ramwrt) {\n\t\t\tshadow = BANK_SHADOW2;\n\t\t}\n\t\tif( ((g_c035_shadow_reg & 0x20) != 0) ||\n\t\t\t((g_rom_version == 1) && !g_user_page2_shadow)) {\n\t\t\tif(!i) {\n\t\t\t\tshadow = 0;\n\t\t\t}\n\t\t}\n\t\tfor(j = 8; j < 0x0c; j++) {\n\t\t\tSET_PAGE_INFO_WR(start_page + j,\n\t\t\t\t\t\tmem0wr + j*0x100 + shadow);\n\t\t}\n\n\t\tfor(j = 0xc; j < 0x20; j++) {\n\t\t\tSET_PAGE_INFO_WR(start_page + j, mem0wr + j*0x100);\n\t\t}\n\n\t\tshadow = 0;\n\t\tif(ramwrt) {\n\t\t\tif(((g_c035_shadow_reg & 0x14) == 0) ||\n\t\t\t\t((g_c035_shadow_reg & 0x08) == 0) || i) {\n\t\t\t\tshadow = BANK_SHADOW2;\n\t\t\t}\n\t\t} else if(((g_c035_shadow_reg & 0x04) == 0) || i) {\n\t\t\tshadow = BANK_SHADOW;\n\t\t}\n\t\tfor(j = 0x40; j < 0x60; j++) {\n\t\t\tSET_PAGE_INFO_WR(start_page + j,\n\t\t\t\t\t\tmem0wr + j*0x100 + shadow);\n\t\t}\n\n\t\tshadow = 0;\n\t\tif(ramwrt && (((g_c035_shadow_reg & 0x08) == 0) || i)) {\n\t\t\t/* shr shadowing */\n\t\t\tshadow = BANK_SHADOW2;\n\t\t}\n\t\tfor(j = 0x60; j < 0xa0; j++) {\n\t\t\tSET_PAGE_INFO_WR(start_page + j,\n\t\t\t\t\t\tmem0wr + j*0x100 + shadow);\n\t\t}\n\n\t\tfor(j = 0xa0; j < 0xc0; j++) {\n\t\t\tSET_PAGE_INFO_WR(start_page + j, mem0wr + j*0x100);\n\t\t}\n\t}\n\n\t/* No need for fixup_brks() since only called from set_statereg() */\n}\n\nvoid\nfixup_lc()\n{\n\tbyte\t*mem0rd, *mem0wr;\n\tword32\tbank, lcbank2, c08x_wrdefram, rdrom;\n\tint\tk;\n\n\t// Fixup memory from 0xd000 - 0xffff in banks 0, 1, $e0, $e1\n\tfor(k = 0; k < 4; k++) {\n\t\tbank = k;\n\t\tmem0rd = &(g_memory_ptr[k << 16]);\n\t\tif(k >= 2) {\n\t\t\tbank += (0xe0 - 2);\t//k==2->bank=$e0, k==3->bank=$e1\n\t\t\tmem0rd = &(g_slow_memory_ptr[(k & 1) << 16]);\n\t\t}\n\t\t/* step bank through 0x00, 0x01, 0xe0, 0xe1 */\n\n\t\tif(((k & 1) == 0) && ALTZP) {\n\t\t\tmem0rd += 0x10000;\n\t\t}\n\t\tlcbank2 = LCBANK2;\n\t\tc08x_wrdefram = g_c068_statereg & 0x200;\n\t\trdrom = RDROM;\n\t\tif((k < 2) && (g_c035_shadow_reg & 0x40)) {\n\t\t\tlcbank2 = 0;\n\t\t\tc08x_wrdefram = 1;\n\t\t\trdrom = 0;\n\t\t}\n\t\tmem0wr = mem0rd;\n\t\tif((k < 2) && !c08x_wrdefram) {\n\t\t\tmem0wr += (BANK_IO_TMP | BANK_IO2_TMP);\n\t\t}\n\t\tif((k < 2) && rdrom) {\n\t\t\tmem0rd = &(g_rom_fc_ff_ptr[0x30000]);\n\t\t}\n\t\tfixup_any_bank_any_page(bank*0x100 + 0xe0, 0x20,\n\t\t\t\t\tmem0rd + 0xe000, mem0wr + 0xe000);\n\t\tif(! lcbank2) {\t\t// lcbank1, 0xd000 is ram at 0xc000-cfff\n\t\t\tif(!rdrom) {\n\t\t\t\tmem0rd -= 0x1000;\n\t\t\t}\n\t\t\tmem0wr -= 0x1000;\n\t\t}\n\t\tfixup_any_bank_any_page(bank*0x100 + 0xd0, 0x10,\n\t\t\t\t\tmem0rd + 0xd000, mem0wr + 0xd000);\n\t}\n\n\t/* No need for fixup_brks() since only called from set_statereg(), */\n\t/*  or from other routines which will handle it */\n}\n\nvoid\nset_statereg(dword64 dfcyc, word32 val)\n{\n\tword32\txor;\n\n\tdbg_log_info(dfcyc, val, g_c068_statereg, 0xc068);\n\n\txor = val ^ g_c068_statereg;\n\tg_c068_statereg = val;\n\tif(xor == 0) {\n\t\treturn;\n\t}\n\n\tif(xor & 0x28c) {\t\t// WRDEFRAM, ALTZP, RDROM, LCBANK2\n\t\tfixup_lc();\n\t\tif(xor & 0x80) {\t\t/* altzp */\n\t\t\tfixup_altzp();\n\t\t}\n\t}\n\tif(xor & 0x40) {\t\t// page2\n\t\tg_cur_a2_stat = (g_cur_a2_stat & ~ALL_STAT_PAGE2) |\n\t\t\t\t\t\t(val & ALL_STAT_PAGE2);\n\t\tfixup_page2(dfcyc);\n\t}\n\n\tif(xor & 0x20) {\t\t// RAMRD\n\t\tfixup_ramrd();\n\t}\n\n\tif(xor & 0x10) {\t\t// RAMWRT\n\t\tfixup_ramwrt();\n\t}\n\n\tif(xor & 0x02) {\t\t// ROMBANK\n\t\thalt_printf(\"Just set rombank = %d\\n\", ROMB);\n\t}\n\n\tif(xor & 0x401) {\t\t// INTC8ROM, INTCX\n\t\tfixup_intcx();\n\t}\n\n\tif(xor) {\n\t\tfixup_brks();\n\t}\n}\n\nvoid\nfixup_shadow_txt1()\n{\n\tbyte\t*mem0wr;\n\tint\tj;\n\n\tfixup_bank0_0400_0800();\n\n\tmem0wr = &(g_memory_ptr[0x10000]);\n\tif((g_c035_shadow_reg & 0x01) == 0) {\n\t\tmem0wr += BANK_SHADOW2;\n\t}\n\tfor(j = 4; j < 8; j++) {\n\t\tSET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100);\n\t}\n}\n\nvoid\nfixup_shadow_txt2()\n{\n\tbyte\t*mem0wr;\n\tint\tshadow;\n\tint\tj;\n\n\t/* bank 0 */\n\tmem0wr = &(g_memory_ptr[0x00000]);\n\tshadow = BANK_SHADOW;\n\tif(RAMWRT) {\n\t\tmem0wr += 0x10000;\n\t\tshadow = BANK_SHADOW2;\n\t}\n\tif(((g_c035_shadow_reg & 0x20) == 0) &&\n\t\t\t((g_rom_version != 1) || g_user_page2_shadow)) {\n\t\tmem0wr += shadow;\n\t}\n\tfor(j = 8; j < 0xc; j++) {\n\t\tSET_PAGE_INFO_WR(j, mem0wr + j*0x100);\n\t}\n\n\t/* and bank 1 */\n\tmem0wr = &(g_memory_ptr[0x10000]);\n\tif(((g_c035_shadow_reg & 0x20) == 0) &&\n\t\t\t((g_rom_version != 1) || g_user_page2_shadow)) {\n\t\tmem0wr += BANK_SHADOW2;\n\t}\n\tfor(j = 8; j < 0xc; j++) {\n\t\tSET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100);\n\t}\n}\n\nvoid\nfixup_shadow_hires1()\n{\n\tbyte\t*mem0wr;\n\tint\tj;\n\n\tfixup_bank0_2000_4000();\n\n\t/* and bank 1 */\n\tmem0wr = &(g_memory_ptr[0x10000]);\n\tif((g_c035_shadow_reg & 0x12) == 0 || (g_c035_shadow_reg & 0x8) == 0) {\n\t\tmem0wr += BANK_SHADOW2;\n\t}\n\tfor(j = 0x20; j < 0x40; j++) {\n\t\tSET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100);\n\t}\n}\n\nvoid\nfixup_shadow_hires2()\n{\n\tbyte\t*mem0wr;\n\tint\tj;\n\n\t/* bank 0 */\n\tmem0wr = &(g_memory_ptr[0x00000]);\n\tif(RAMWRT) {\n\t\tmem0wr += 0x10000;\n\t\tif((g_c035_shadow_reg & 0x14) == 0 ||\n\t\t\t\t\t(g_c035_shadow_reg & 0x8) == 0) {\n\t\t\tmem0wr += BANK_SHADOW2;\n\t\t}\n\t} else if((g_c035_shadow_reg & 0x04) == 0) {\n\t\tmem0wr += BANK_SHADOW;\n\t}\n\tfor(j = 0x40; j < 0x60; j++) {\n\t\tSET_PAGE_INFO_WR(j, mem0wr + j*0x100);\n\t}\n\n\t/* and bank 1 */\n\tmem0wr = &(g_memory_ptr[0x10000]);\n\tif((g_c035_shadow_reg & 0x14) == 0 || (g_c035_shadow_reg & 0x8) == 0) {\n\t\tmem0wr += BANK_SHADOW2;\n\t}\n\tfor(j = 0x40; j < 0x60; j++) {\n\t\tSET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100);\n\t}\n}\n\nvoid\nfixup_shadow_shr()\n{\n\tbyte\t*mem0wr;\n\tint\tj;\n\n\t/* bank 0, only pages 0x60 - 0xa0 */\n\tmem0wr = &(g_memory_ptr[0x00000]);\n\tif(RAMWRT) {\n\t\tmem0wr += 0x10000;\n\t\tif((g_c035_shadow_reg & 0x8) == 0) {\n\t\t\tmem0wr += BANK_SHADOW2;\n\t\t}\n\t}\n\tfor(j = 0x60; j < 0xa0; j++) {\n\t\tSET_PAGE_INFO_WR(j, mem0wr + j*0x100);\n\t}\n\n\t/* and bank 1, only pages 0x60 - 0xa0 */\n\tmem0wr = &(g_memory_ptr[0x10000]);\n\tif((g_c035_shadow_reg & 0x8) == 0) {\n\t\tmem0wr += BANK_SHADOW2;\n\t}\n\tfor(j = 0x60; j < 0xa0; j++) {\n\t\tSET_PAGE_INFO_WR(0x100 + j, mem0wr + j*0x100);\n\t}\n}\n\nvoid\nfixup_shadow_iolc()\n{\n\tbyte\t*mem0rd;\n\tint\tk;\n\n\tif(g_c035_shadow_reg & 0x40) {\n\t\t/* Disable language card area */\n\t\tfor(k = 0; k < 2; k++) {\n\t\t\tmem0rd = &(g_memory_ptr[k << 16]);\n\t\t\tfixup_any_bank_any_page((k << 8) + 0xc0, 0x10,\n\t\t\t\tmem0rd + 0xd000, mem0rd + 0xd000);\n\t\t\tif(k == 0 && ALTZP) {\n\t\t\t\tmem0rd += 0x10000;\n\t\t\t}\n\t\t\tfixup_any_bank_any_page((k << 8) + 0xd0, 0x10,\n\t\t\t\tmem0rd + 0xc000, mem0rd + 0xc000);\n\t\t\tfixup_any_bank_any_page((k << 8) + 0xe0, 0x20,\n\t\t\t\tmem0rd + 0xe000, mem0rd + 0xe000);\n\t\t}\n\t} else {\n\t\t/* 0xc000 area */\n\t\tfixup_intcx();\n\n\t\t// Fix 0xd000-0xffff banks 0, 1, 0xe0, 0xe1\n\t\tfixup_lc();\n\t}\n}\n\nvoid\nupdate_shadow_reg(dword64 dfcyc, word32 val)\n{\n\tint\txor;\n\n\tdbg_log_info(dfcyc, val, g_c035_shadow_reg, 0xc035);\n\n\tif(g_c035_shadow_reg == val) {\n\t\treturn;\n\t}\n\n\txor = g_c035_shadow_reg ^ val;\n\tg_c035_shadow_reg = val;\n\n\tif(xor & 8) {\n\t\tfixup_shadow_hires1();\n\t\tfixup_shadow_hires2();\n\t\tfixup_shadow_shr();\n\t\txor = xor & (~0x16);\n\t}\n\tif(xor & 0x10) {\n\t\tfixup_shadow_hires1();\n\t\tfixup_shadow_hires2();\n\t\txor = xor & (~0x6);\n\t}\n\tif(xor & 2) {\n\t\tfixup_shadow_hires1();\n\t}\n\tif(xor & 4) {\n\t\tfixup_shadow_hires2();\n\t}\n\tif(xor & 1) {\n\t\tfixup_shadow_txt1();\n\t}\n\tif((xor & 0x20) && ((g_rom_version != 1) || g_user_page2_shadow)) {\n\t\tfixup_shadow_txt2();\n\t}\n\tif(xor & 0x40) {\n\t\tfixup_shadow_iolc();\n\t}\n\tif(xor) {\n\t\tfixup_brks();\n\t}\n}\n\nvoid\nfixup_shadow_all_banks()\n{\n\tbyte\t*mem0rd;\n\tint\tshadow;\n\tint\tnum_banks;\n\tint\tj, k;\n\n\t/* Assume Ninja Force Megademo */\n\t/* only do banks 3 - num_banks by 2, shadowing into e1 */\n\n\tshadow = 0;\n\tif((g_c036_val_speed & 0x10) && ((g_c035_shadow_reg & 0x08) == 0)) {\n\t\tshadow = BANK_SHADOW2;\n\t}\n\tnum_banks = g_mem_size_total >> 16;\n\tfor(k = 3; k < num_banks; k += 2) {\n\t\tmem0rd = &(g_memory_ptr[k*0x10000 + 0x2000]) + shadow;\n\t\tfor(j = 0x20; j < 0xa0; j++) {\n\t\t\tSET_PAGE_INFO_WR(k*0x100 + j, mem0rd);\n\t\t\tmem0rd += 0x100;\n\t\t}\n\t}\n\n\tfixup_brks();\n}\n\nvoid\nsetup_pageinfo()\n{\n\tbyte\t*mem0rd;\n\tword32\tmem_size_pages;\n\n\t/* first, set all of memory to point to itself */\n\tmem_size_pages = g_mem_size_total >> 8;\n\tmem0rd = &(g_memory_ptr[0]);\n\tfixup_any_bank_any_page(0, mem_size_pages, mem0rd, mem0rd);\n\n\t/* mark unused memory as BAD_MEM */\n\tfixup_any_bank_any_page(mem_size_pages, 0xfc00-mem_size_pages,\n\t\t\tBANK_BAD_MEM, BANK_BAD_MEM);\n\n\tfixup_shadow_all_banks();\n\n\t/* ROM */\n\tmem0rd = &(g_rom_fc_ff_ptr[0]);\n\tfixup_any_bank_any_page(0xfc00, 0x400, mem0rd,\n\t\t\t\tmem0rd + (BANK_IO_TMP | BANK_IO2_TMP));\n\n\t/* banks e0, e1 */\n\tmem0rd = &(g_slow_memory_ptr[0]);\n\tfixup_any_bank_any_page(0xe000, 0x200, mem0rd, mem0rd);\n\t\t\t\t// First, did all 128KB\n\n\tfixup_any_bank_any_page(0xe004, 0x08, mem0rd + 0x0400,\n\t\t\t\t\t\tmem0rd + 0x0400 + BANK_SHADOW);\n\tfixup_any_bank_any_page(0xe020, 0x80, mem0rd + 0x2000,\n\t\t\t\t\t\tmem0rd + 0x2000 + BANK_SHADOW);\n\n\tmem0rd = &(g_slow_memory_ptr[0x10000]);\n\tfixup_any_bank_any_page(0xe104, 0x08, mem0rd + 0x0400,\n\t\t\t\t\t\tmem0rd + 0x0400 + BANK_SHADOW2);\n\tfixup_any_bank_any_page(0xe120, 0x80, mem0rd + 0x2000,\n\t\t\t\t\t\tmem0rd + 0x2000 + BANK_SHADOW2);\n\n\tfixup_intcx();\t/* correct banks 0xe0,0xe1, 0xc000-0xcfff area */\n\tfixup_lc();\t/* correct 0xd000-0xdfff area */\n\n\tfixup_bank0_2000_4000();\n\tfixup_bank0_0400_0800();\n\tfixup_altzp();\n\tfixup_ramrd();\n\tfixup_ramwrt();\n\tfixup_shadow_txt1();\n\tfixup_shadow_txt2();\n\tfixup_shadow_hires1();\n\tfixup_shadow_hires2();\n\tfixup_shadow_shr();\n\tfixup_shadow_iolc();\n\tfixup_brks();\n}\n\nvoid\nshow_bankptrs_bank0rdwr()\n{\n\tshow_bankptrs(0);\n\tshow_bankptrs(1);\n\tshow_bankptrs(0xe0);\n\tshow_bankptrs(0xe1);\n\tprintf(\"statereg: %02x\\n\", g_c068_statereg);\n}\n\nvoid\nshow_bankptrs(int bnk)\n{\n\tint i;\n\tPg_info rd, wr;\n\tbyte *ptr_rd, *ptr_wr;\n\n\tprintf(\"g_memory_ptr: %p, dummy_mem: %p, slow_mem_ptr: %p\\n\",\n\t\tg_memory_ptr, g_dummy_memory1_ptr, g_slow_memory_ptr);\n\tprintf(\"g_rom_fc_ff_ptr: %p\\n\", g_rom_fc_ff_ptr);\n\n\tprintf(\"Showing bank_info array for %02x\\n\", bnk);\n\tfor(i = 0; i < 256; i++) {\n\t\trd = GET_PAGE_INFO_RD(bnk*0x100 + i);\n\t\twr = GET_PAGE_INFO_WR(bnk*0x100 + i);\n\t\tptr_rd = (byte *)rd;\n\t\tptr_wr = (byte *)wr;\n\t\tprintf(\"%04x rd: \", bnk*256 + i);\n\t\tshow_addr(ptr_rd);\n\t\tprintf(\" wr: \");\n\t\tshow_addr(ptr_wr);\n\t\tprintf(\"\\n\");\n\t}\n}\n\nvoid\nshow_addr(byte *ptr)\n{\n\tword32\tmem_size;\n\n\tmem_size = g_mem_size_total;\n\tif(ptr >= g_memory_ptr && ptr < &g_memory_ptr[mem_size]) {\n\t\tprintf(\"%p--memory[%06x]\", ptr,\n\t\t\t\t\t(word32)(ptr - g_memory_ptr));\n\t} else if(ptr >= g_rom_fc_ff_ptr && ptr < &g_rom_fc_ff_ptr[256*1024]) {\n\t\tprintf(\"%p--rom_fc_ff[%06x]\", ptr,\n\t\t\t\t\t(word32)(ptr - g_rom_fc_ff_ptr));\n\t} else if(ptr >= g_slow_memory_ptr && ptr<&g_slow_memory_ptr[128*1024]){\n\t\tprintf(\"%p--slow_memory[%06x]\", ptr,\n\t\t\t\t\t(word32)(ptr - g_slow_memory_ptr));\n\t} else if(ptr >=g_dummy_memory1_ptr && ptr < &g_dummy_memory1_ptr[256]){\n\t\tprintf(\"%p--dummy_memory[%06x]\", ptr,\n\t\t\t\t\t(word32)(ptr - g_dummy_memory1_ptr));\n\t} else {\n\t\tprintf(\"%p--unknown\", ptr);\n\t}\n}\n\nword32\nmoremem_fix_vector_pull(word32 addr)\n{\n\t// Default vector for BRK will come from 0xfffffe.  But if\n\t//  I/O shadowing is off, or we're a //e, then get from bank0\n\tif((g_c035_shadow_reg & 0x40) || (g_rom_version == 0)) {\n\t\t// I/O shadowing off, or Apple //e: use RAM loc\n\t\taddr = addr & 0xffff;\n\t}\n\treturn addr;\n}\n\nword32\nio_read(word32 loc, dword64 *cyc_ptr)\n{\n\tdword64\tdfcyc;\n\tword32\tval;\n\tword32\tnew_lcbank2, new_wrdefram, new_prewrite, new_rdrom, tmp;\n\tint\ti;\n\n\tdfcyc = *cyc_ptr;\n\n/* IO space */\n\tswitch((loc >> 8) & 0xf) {\n\tcase 0: /* 0xc000 - 0xc0ff */\n\t\tswitch(loc & 0xff) {\n\t\t/* 0xc000 - 0xc00f */\n\t\tcase 0x00: case 0x01: case 0x02: case 0x03:\n\t\tcase 0x04: case 0x05: case 0x06: case 0x07:\n\t\tcase 0x08: case 0x09: case 0x0a: case 0x0b:\n\t\tcase 0x0c: case 0x0d: case 0x0e: case 0x0f:\n\t\t\treturn(adb_read_c000());\n\n\t\t/* 0xc010 - 0xc01f */\n\t\tcase 0x10: /* c010 */\n\t\t\treturn(adb_access_c010());\n\t\tcase 0x11: /* c011 = RDLCBANK2 */\n\t\t\treturn IOR(LCBANK2);\n\t\tcase 0x12: /* c012= RDLCRAM */\n\t\t\treturn IOR(!RDROM);\n\t\tcase 0x13: /* c013=rdramd */\n\t\t\treturn IOR(RAMRD);\n\t\tcase 0x14: /* c014=rdramwrt */\n\t\t\treturn IOR(RAMWRT);\n\t\tcase 0x15: /* c015 = RDCXROM = INTCX */\n\t\t\treturn IOR(g_c068_statereg & 1);\n\t\tcase 0x16: /* c016: Read ALTZP, 0 = Read Main ZP; 1 = Alt ZP */\n\t\t\treturn IOR(ALTZP);\n\t\tcase 0x17: /* c017: rdc3rom */\n\t\t\treturn IOR(g_c02d_int_crom & (1 << 3));\n\t\tcase 0x18: /* c018: rd80c0l */\n\t\t\treturn IOR((g_cur_a2_stat & ALL_STAT_ST80));\n\t\tcase 0x19: /* c019: rdvblbar: MSB set if in VBL */\n\t\t\ttmp = in_vblank(dfcyc);\n\t\t\tif(g_rom_version == 0) {\t// Apple //e\n\t\t\t\ttmp = tmp ^ 1;\t\t// 1=not in VBL on //e\n\t\t\t}\n\t\t\treturn IOR(tmp);\n\t\tcase 0x1a: /* c01a: rdtext */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_TEXT);\n\t\tcase 0x1b: /* c01b: rdmix */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_MIX_T_GR);\n\t\tcase 0x1c: /* c01c: rdpage2 */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_PAGE2);\n\t\tcase 0x1d: /* c01d: rdhires */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_HIRES);\n\t\tcase 0x1e: /* c01e: altcharset on? */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_ALTCHARSET);\n\t\tcase 0x1f: /* c01f: rd80vid */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_VID80);\n\n\t\t/* 0xc020 - 0xc02f */\n\t\tcase 0x20: /* 0xc020 */\n\t\t\t/* Click cassette port */\n\t\t\treturn float_bus(dfcyc);\n\t\tcase 0x21: /* 0xc021 */\n\t\t\t/* Not documented, but let's return COLOR_C021 */\n\t\t\treturn IOR(g_cur_a2_stat & ALL_STAT_COLOR_C021);\n\t\tcase 0x22: /* 0xc022 */\n\t\t\treturn (g_cur_a2_stat >> BIT_ALL_STAT_BG_COLOR) & 0xff;\n\t\tcase 0x23: /* 0xc023 */\n\t\t\treturn g_c023_val;\n\t\tcase 0x24: /* 0xc024 */\n\t\t\treturn mouse_read_c024(dfcyc);\n\t\tcase 0x25: /* 0xc025 */\n\t\t\treturn adb_read_c025();\n\t\tcase 0x26: /* 0xc026 */\n\t\t\treturn adb_read_c026();\n\t\tcase 0x27: /* 0xc027 */\n\t\t\treturn adb_read_c027();\n\t\tcase 0x28: /* 0xc028 */\n\t\t\treturn 0;\n\t\tcase 0x29: /* 0xc029 */\n\t\t\treturn((g_cur_a2_stat & 0xa0) | g_c029_val_some);\n\t\tcase 0x2a: /* 0xc02a */\n#if 0\n\t\t\tprintf(\"Reading c02a...returning 0\\n\");\n#endif\n\t\t\treturn 0;\n\t\tcase 0x2b: /* 0xc02b */\n\t\t\treturn g_c02b_val;\n\t\tcase 0x2c: /* 0xc02c */\n\t\t\t/* printf(\"reading c02c, returning 0\\n\"); */\n\t\t\tif(g_c06d_val == 0x40) {\t// Test mode $40\n\t\t\t\treturn read_video_data(dfcyc);\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x2d: /* 0xc02d */\n\t\t\treturn g_c02d_int_crom;\n\t\tcase 0x2e: /* 0xc02e */\n\t\tcase 0x2f: /* 0xc02f */\n\t\t\treturn read_vid_counters(loc, dfcyc);\n\n\t\t/* 0xc030 - 0xc03f */\n\t\tcase 0x30: /* 0xc030 */\n\t\t\t/* click speaker */\n\t\t\treturn sound_read_c030(dfcyc);\n\t\tcase 0x31: /* 0xc031 */\n\t\t\t/* 3.5\" control */\n\t\t\treturn g_iwm.state & 0xc0;\n\t\tcase 0x32: /* 0xc032 */\n\t\t\t/* scan int */\n\t\t\treturn 0;\n\t\tcase 0x33: /* 0xc033 = CLOCKDATA*/\n\t\t\treturn g_c033_data;\n\t\tcase 0x34: /* 0xc034 = CLOCKCTL */\n\t\t\treturn g_c034_val;\n\t\tcase 0x35: /* 0xc035 */\n\t\t\treturn g_c035_shadow_reg;\n\t\tcase 0x36: /* 0xc036 = CYAREG */\n\t\t\treturn g_c036_val_speed;\n\t\tcase 0x37: /* 0xc037 */\n\t\t\treturn 0;\n\t\tcase 0x38: /* 0xc038 */\n\t\t\treturn scc_read_reg(dfcyc, 1);\n\t\tcase 0x39: /* 0xc039 */\n\t\t\treturn scc_read_reg(dfcyc, 0);\n\t\tcase 0x3a: /* 0xc03a */\n\t\t\treturn scc_read_data(dfcyc, 1);\n\t\tcase 0x3b: /* 0xc03b */\n\t\t\treturn scc_read_data(dfcyc, 0);\n\t\tcase 0x3c: /* 0xc03c */\n\t\t\t/* doc control */\n\t\t\treturn doc_read_c03c();\n\t\tcase 0x3d: /* 0xc03d */\n\t\t\treturn doc_read_c03d(dfcyc);\n\t\tcase 0x3e: /* 0xc03e */\n\t\t\treturn (g_c03ef_doc_ptr & 0xff);\n\t\tcase 0x3f: /* 0xc03f */\n\t\t\treturn (g_c03ef_doc_ptr >> 8);\n\n\t\t/* 0xc040 - 0xc04f */\n\t\tcase 0x40: /* 0xc040 */\n\t\t\t/* cassette */\n\t\t\treturn 0;\n\t\tcase 0x41: /* 0xc041 */\n\t\t\treturn g_c041_val;\n\t\tcase 0x45: /* 0xc045 */\n\t\t\t//halt_printf(\"Mega II mouse read: c045\\n\");\n\t\t\treturn 0;\n\t\tcase 0x46: /* 0xc046 */\n\t\t\ttmp = g_c046_val;\n\t\t\tg_c046_val = (tmp & 0xbf) + ((tmp & 0x80) >> 1);\n\t\t\treturn tmp;\n\t\tcase 0x47: /* 0xc047 */\n\t\t\tremove_irq(IRQ_PENDING_C046_25SEC |\n\t\t\t\t\t\t\tIRQ_PENDING_C046_VBL);\n\t\t\tg_c046_val &= 0xe7;\t/* clear vbl_int, 1/4sec int*/\n\t\t\treturn 0;\n\t\tcase 0x42: /* 0xc042 */\n\t\tcase 0x43: /* 0xc043 */\n\t\t\treturn 0;\n\t\tcase 0x4f: /* 0xc04f */\n\t\t\t/* for information on c04f, see: */\n\t\t\t/* www.sheppyware.net/tech/hardware/softswitches.html */\n\t\t\t/* write to $c04f to start.  Then read $c04f to get */\n\t\t\t/* emulator ($16=sweet16, $fe=bernie II). */\n\t\t\t/* Then read again to get version: $21 == 2.1 */\n\t\t\tswitch(g_em_emubyte_cnt) {\n\t\t\tcase 1:\n\t\t\t\tg_em_emubyte_cnt = 2;\n\t\t\t\treturn 'K';\n\t\t\tcase 2:\n\t\t\t\tg_em_emubyte_cnt = 0;\n\t\t\t\ttmp = g_kegs_version_str[0] - '0';\n\t\t\t\ti = g_kegs_version_str[2] - '0';\n\t\t\t\treturn ((tmp & 0xf) << 4) + (i & 0xf);\n\t\t\tdefault:\n\t\t\t\tg_em_emubyte_cnt = 0;\n\t\t\t\treturn 0;\n\t\t\t}\n\t\tcase 0x44: /* 0xc044 */\n\t\tcase 0x48: /* 0xc048 */\n\t\tcase 0x49: /* 0xc049 */\n\t\tcase 0x4a: /* 0xc04a */\n\t\tcase 0x4b: /* 0xc04b */\n\t\tcase 0x4c: /* 0xc04c */\n\t\tcase 0x4d: /* 0xc04d */\n\t\tcase 0x4e: /* 0xc04e */\n\t\t\tUNIMPL_READ;\n\n\t\t/* 0xc050 - 0xc05f */\n\t\tcase 0x50: /* 0xc050 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tif(g_cur_a2_stat & ALL_STAT_TEXT) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_TEXT);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn val;\n\t\tcase 0x51: /* 0xc051 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tif((g_cur_a2_stat & ALL_STAT_TEXT) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_TEXT);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn val;\n\t\tcase 0x52: /* 0xc052 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tif(g_cur_a2_stat & ALL_STAT_MIX_T_GR) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_MIX_T_GR);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn val;\n\t\tcase 0x53: /* 0xc053 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tif((g_cur_a2_stat & ALL_STAT_MIX_T_GR) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_MIX_T_GR);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn val;\n\t\tcase 0x54: /* 0xc054 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tset_statereg(dfcyc, g_c068_statereg & (~0x40));\n\t\t\treturn val;\n\t\t\treturn float_bus(dfcyc);\n\t\tcase 0x55: /* 0xc055 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x40);\n\t\t\treturn val;\n\t\tcase 0x56: /* 0xc056 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tif(g_cur_a2_stat & ALL_STAT_HIRES) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_HIRES);\n\t\t\t\tfixup_hires_on();\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn val;\n\t\tcase 0x57: /* 0xc057 */\n\t\t\tval = float_bus(dfcyc);\n\t\t\tif((g_cur_a2_stat & ALL_STAT_HIRES) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_HIRES);\n\t\t\t\tfixup_hires_on();\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn val;\n\t\tcase 0x58: /* 0xc058 */\n\t\t\tif(g_zipgs_unlock < 4) {\n\t\t\t\tg_c05x_annuncs &= (~1);\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x59: /* 0xc059 */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\treturn g_zipgs_reg_c059;\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs |= 1;\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x5a: /* 0xc05a */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\treturn g_zipgs_reg_c05a;\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs &= (~2);\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x5b: /* 0xc05b */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\t// Bit 7 is 1msclk, clock with 1ms period.\n\t\t\t\ttmp = (dfcyc >> 25) & 1;\n\t\t\t\treturn (tmp << 7) + (g_zipgs_reg_c05b & 0x7f);\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs |= 2;\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x5c: /* 0xc05c */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\treturn g_zipgs_reg_c05c;\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs &= (~4);\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x5d: /* 0xc05d */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\thalt_printf(\"Reading ZipGS $c05d!\\n\");\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs |= 4;\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x5e: /* 0xc05e */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\thalt_printf(\"Reading ZipGS $c05e!\\n\");\n\t\t\t} else if(g_cur_a2_stat & ALL_STAT_ANNUNC3) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_ANNUNC3);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn 0;\n\t\tcase 0x5f: /* 0xc05f */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\thalt_printf(\"Reading ZipGS $c05f!\\n\");\n\t\t\t} else if((g_cur_a2_stat & ALL_STAT_ANNUNC3) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_ANNUNC3);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn 0;\n\n\n\t\t/* 0xc060 - 0xc06f */\n\t\tcase 0x60: /* 0xc060 */\n\t\t\treturn IOR(g_paddle_buttons & 8);\n\t\tcase 0x61: /* 0xc061 */\n\t\t\treturn IOR(adb_is_cmd_key_down() ||\n\t\t\t\t\t\t\tg_paddle_buttons & 1);\n\t\tcase 0x62: /* 0xc062 */\n\t\t\treturn IOR(adb_is_option_key_down() ||\n\t\t\t\t\t\t\tg_paddle_buttons & 2);\n\t\tcase 0x63: /* 0xc063 */\n\t\t\treturn IOR(g_paddle_buttons & 4);\n\t\tcase 0x64: /* 0xc064 */\n\t\t\treturn read_paddles(dfcyc, 0);\n\t\tcase 0x65: /* 0xc065 */\n\t\t\treturn read_paddles(dfcyc, 1);\n\t\tcase 0x66: /* 0xc066 */\n\t\t\treturn read_paddles(dfcyc, 2);\n\t\tcase 0x67: /* 0xc067 */\n\t\t\treturn read_paddles(dfcyc, 3);\n\t\tcase 0x68: /* 0xc068 = STATEREG */\n\t\t\treturn g_c068_statereg & 0xff;\n\t\tcase 0x69: /* 0xc069 */\n\t\t\t/* Reserved reg, return 0 */\n\t\t\treturn 0;\n\t\tcase 0x6a: /* 0xc06a */\n\t\tcase 0x6b: /* 0xc06b */\n\t\t\tUNIMPL_READ;\n\t\tcase 0x6c: /* 0xc06c */\n\t\tcase 0x6d: /* 0xc06d */\n\t\tcase 0x6e: /* 0xc06e */\n\t\tcase 0x6f: /* 0xc06f */\n\t\t\treturn g_c06c_latched_cyc >> (8 * (loc & 3)) & 0xff;\n\n\t\t/* 0xc070 - 0xc07f */\n\t\tcase 0x70: /* c070 */\n\t\t\tpaddle_trigger(dfcyc);\n\t\t\treturn float_bus(dfcyc);\n\t\tcase 0x71:\t/* 0xc071 */\n\t\tcase 0x72: case 0x73:\n\t\tcase 0x74: case 0x75: case 0x76: case 0x77:\n\t\tcase 0x78: case 0x79: case 0x7a: case 0x7b:\n\t\tcase 0x7c: case 0x7d: case 0x7e: case 0x7f:\n\t\t\treturn g_rom_fc_ff_ptr[3*65536 + 0xc000 + (loc & 0xff)];\n\n\t\t/* 0xc080 - 0xc08f */\n\t\tcase 0x80: case 0x81: case 0x82: case 0x83:\n\t\tcase 0x84: case 0x85: case 0x86: case 0x87:\n\t\tcase 0x88: case 0x89: case 0x8a: case 0x8b:\n\t\tcase 0x8c: case 0x8d: case 0x8e: case 0x8f:\n\t\t\tnew_lcbank2 = ((loc >> 1) & 0x4) ^ 0x4;\n\t\t\tnew_rdrom = ((loc << 3) ^ (loc << 2)) & 8;\n\t\t\t\t// new_rdrom is set if loc[0] ^ loc[1] is true\n\t\t\t\t// 8=RDROM, 0=read from LC RAM\n\t\t\tnew_prewrite = (loc & 1) << 8;\t// Odd read set PREWRITE\n\t\t\tnew_wrdefram = g_c068_statereg & 0x200;\n\t\t\tif((loc & 1) == 0) {\n\t\t\t\tnew_wrdefram = 0;\t// Any even access clrs\n\t\t\t} else {\n\t\t\t\tnew_wrdefram |= (g_c068_statereg << 1) & 0x200;\n\t\t\t\t// Odd read also makes Ram wr if PREWR was set\n\t\t\t}\n\t\t\tset_statereg(dfcyc, (g_c068_statereg & ~(0x30c)) |\n\t\t\t\t\tnew_lcbank2 | new_rdrom |\n\t\t\t\t\tnew_prewrite | new_wrdefram);\n\t\t\t// access 0-7: lcbank1, 8-f: lcbank0\n\t\t\t// a0!=a1: set rdrom (c081,c082,c085,c086, etc.)\n\t\t\t// a0=1 && rd set prewrite.  a0=0, or wr: clear prewrite\n\t\t\t// wrdefram is clr if a0=0, set if prewrite and\n\t\t\t//  old_prewrite are set, otherwise stays same.\n\t\t\t// From Apple language card schematics:\n\t\t\t//  wr_def = (wr_def_ff || (prewr && prewr_ff)) && a0;\n\t\t\t//  prewr = read && a0\n\t\t\treturn float_bus(dfcyc);\n\t\t/* 0xc090 - 0xc09f */\n\t\tcase 0x90: case 0x91: case 0x92: case 0x93:\n\t\tcase 0x94: case 0x95: case 0x96: case 0x97:\n\t\tcase 0x98: case 0x99: case 0x9a: case 0x9b:\n\t\tcase 0x9c: case 0x9d: case 0x9e: case 0x9f:\n\t\t\t/* UNIMPL_READ; */\n\t\t\treturn 0;\n\t\t/* 0xc0a0 - 0xc0af */\n\t\tcase 0xa0: case 0xa1: case 0xa2: case 0xa3:\n\t\tcase 0xa4: case 0xa5: case 0xa6: case 0xa7:\n\t\tcase 0xa8: case 0xa9: case 0xaa: case 0xab:\n\t\tcase 0xac: case 0xad: case 0xae: case 0xaf:\n\t\t\treturn 0;\n\t\t\t/* UNIMPL_READ; */\n\n\t\t/* 0xc0b0 - 0xc0bf */\n\t\tcase 0xb0: case 0xb1: case 0xb2: case 0xb3:\n\t\tcase 0xb4: case 0xb5: case 0xb6: case 0xb7:\n\t\tcase 0xb8: case 0xb9: case 0xba: case 0xbb:\n\t\tcase 0xbc: case 0xbd: case 0xbe: case 0xbf:\n\t\t\treturn voc_devsel_read(loc, dfcyc);\n\n\t\t/* 0xc0c0 - 0xc0cf */\n\t\tcase 0xc0: case 0xc1: case 0xc2: case 0xc3:\n\t\t\t// Slot 4 has a Slinky RAM card\n\t\t\treturn slinky_devsel_read(dfcyc, loc);\n\t\tcase 0xc4: case 0xc5: case 0xc6: case 0xc7:\n\t\tcase 0xc8: case 0xc9: case 0xca: case 0xcb:\n\t\tcase 0xcc: case 0xcd: case 0xce: case 0xcf:\n\t\t\treturn 0;\n\t\t/* 0xc0d0 - 0xc0df */\n\t\tcase 0xd0: case 0xd1: case 0xd2: case 0xd3:\n\t\tcase 0xd4: case 0xd5: case 0xd6: case 0xd7:\n\t\tcase 0xd8: case 0xd9: case 0xda: case 0xdb:\n\t\tcase 0xdc: case 0xdd: case 0xde: case 0xdf:\n\t\t\treturn 0;\n\t\t/* 0xc0e0 - 0xc0ef */\n\t\tcase 0xe0: case 0xe1: case 0xe2: case 0xe3:\n\t\tcase 0xe4: case 0xe5: case 0xe6: case 0xe7:\n\t\tcase 0xe8: case 0xe9: case 0xea: case 0xeb:\n\t\tcase 0xec: case 0xed: case 0xee: case 0xef:\n\t\t\treturn read_iwm(loc, dfcyc);\n\t\t/* 0xc0f0 - 0xc0ff */\n\t\tcase 0xf0: case 0xf1: case 0xf2: case 0xf3:\n\t\tcase 0xf4: case 0xf5: case 0xf6: case 0xf7:\n\t\tcase 0xf8: case 0xf9: case 0xfa: case 0xfb:\n\t\tcase 0xfc: case 0xfd: case 0xfe: case 0xff:\n\t\t\treturn 0;\n\n\t\tdefault:\n\t\t\tprintf(\"loc: %04x bad\\n\", loc);\n\t\t\tUNIMPL_READ;\n\t\t}\n\tcase 1: case 2: case 5: case 6: case 7:\n\t\t/* c100 - c7ff, (except c3xx, c4xx) */\n\t\treturn float_bus(dfcyc);\n\tcase 3:\t// c300\n\t\treturn c3xx_read(dfcyc, loc);\n\tcase 4:\n\t\treturn mockingboard_read(loc, dfcyc);\n\tcase 8: case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0xe:\n\t\treturn float_bus(dfcyc);\n\t\t// UNIMPL_READ;\n\tcase 0xf:\t// $CFxx\n\t\tif(g_c068_statereg & 0x401) {\t\t// INTC8ROM or INTCX\n\t\t\tval = g_rom_fc_ff_ptr[0x3cf00 + (loc & 0xff)];\n\t\t} else {\n\t\t\tval = float_bus(dfcyc);\n\t\t}\n\t\tif((loc & 0xfff) == 0xfff) {\n\t\t\tif(g_c068_statereg & 0x400) {\n\t\t\t\tset_statereg(dfcyc, g_c068_statereg & (~0x400));\n\t\t\t}\n\t\t}\n\t\treturn val;\n\t\t// UNIMPL_READ;\n\t}\n\n\thalt_printf(\"io_read: hit end, loc: %06x\\n\", loc);\n\n\treturn 0xff;\n}\n\nvoid\nio_write(word32 loc, word32 val, dword64 *cyc_ptr)\n{\n\tdword64\tdfcyc;\n\tword32\tnew_lcbank2, new_wrdefram, new_rdrom, new_prewrite, tmp;\n\tword32\tnew_tmp;\n\tint\tfixup;\n\n\tdfcyc = *cyc_ptr;\n\n\tval = val & 0xff;\n\tswitch((loc >> 8) & 0xf) {\n\tcase 0: /* 0xc000 - 0xc0ff */\n\t\tswitch(loc & 0xff) {\n\t\t/* 0xc000 - 0xc00f */\n\t\tcase 0x00: /* 0xc000: CLR80COL */\n\t\t\tif(g_cur_a2_stat & ALL_STAT_ST80) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_ST80);\n\t\t\t\tfixup_st80col(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x01: /* 0xc001: SET80COL, enable 80-column store */\n\t\t\tif((g_cur_a2_stat & ALL_STAT_ST80) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_ST80);\n\t\t\t\tfixup_st80col(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x02: /* 0xc002: Set RDMAINRAM */\n\t\t\tset_statereg(dfcyc, g_c068_statereg & ~0x20);\n\t\t\treturn;\n\t\tcase 0x03: /* 0xc003: Set RDCARDRAM (alt) */\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x20);\n\t\t\treturn;\n\t\tcase 0x04: /* 0xc004: Set WRMAINRAM */\n\t\t\tset_statereg(dfcyc, g_c068_statereg & ~0x10);\n\t\t\treturn;\n\t\tcase 0x05: /* 0xc005: Set WRCARDRAM (alt) */\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x10);\n\t\t\treturn;\n\t\tcase 0x06: /* 0xc006 = SETSLOTCXROM, use slot rom c800 */\n\t\t\tset_statereg(dfcyc, g_c068_statereg & ~0x01);\n\t\t\treturn;\n\t\tcase 0x07: /* 0xc007 = SETINTXROM, use int rom c800 */\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x01);\n\t\t\treturn;\n\t\tcase 0x08: /* 0xc008: SETSTDZP (main ZP/LC) */\n\t\t\tset_statereg(dfcyc, g_c068_statereg & ~0x80);\n\t\t\treturn;\n\t\tcase 0x09: /* 0xc009: SETALTZP (alt ZP/LC) */\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x80);\n\t\t\treturn;\n\t\tcase 0x0a: /* 0xc00a = SETINTC3ROM, use internal ROM slot 3*/\n\t\t\ttmp = 1 << 3;\n\t\t\tif((g_c02d_int_crom & tmp) != 0) {\n\t\t\t\tg_c02d_int_crom &= ~tmp;\n\t\t\t\tfixup_intcx();\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x0b: /* 0xc00b = SETSLOTC3ROM, use slot rom slot 3 */\n\t\t\ttmp = 1 << 3;\n\t\t\tif((g_c02d_int_crom & tmp) == 0) {\n\t\t\t\tg_c02d_int_crom |= tmp;\n\t\t\t\tfixup_intcx();\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x0c: /* 0xc00c = CLR80VID, disable 80 col hardware */\n\t\t\tif(g_cur_a2_stat & ALL_STAT_VID80) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_VID80);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x0d: /* 0xc00d: SET80VID, enable 80 col hardware */\n\t\t\tif((g_cur_a2_stat & ALL_STAT_VID80) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_VID80);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x0e: /* 0xc00e: CLRALTCHAR */\n\t\t\tif(g_cur_a2_stat & ALL_STAT_ALTCHARSET) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_ALTCHARSET);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x0f: /* 0xc00f: SETALTCHAR */\n\t\t\tif((g_cur_a2_stat & ALL_STAT_ALTCHARSET) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_ALTCHARSET);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\t/* 0xc010 - 0xc01f */\n\t\tcase 0x10: case 0x11: case 0x12: case 0x13:\n\t\tcase 0x14: case 0x15: case 0x16: case 0x17:\n\t\tcase 0x18: case 0x19: case 0x1a: case 0x1b:\n\t\tcase 0x1c: case 0x1d: case 0x1e: case 0x1f:\n\t\t\tadb_access_c010();\n\t\t\treturn;\n\t\t/* 0xc020 - 0xc02f */\n\t\tcase 0x20: /* 0xc020 */\n\t\t\t/* WRITE CASSETTE?? */\n\t\t\treturn;\n\t\tcase 0x21: /* 0xc021 */\n\t\t\tnew_tmp = ((val >> 7) & 1) <<\n\t\t\t\t\t\t(31 - BIT_ALL_STAT_COLOR_C021);\n\t\t\tif((g_cur_a2_stat & ALL_STAT_COLOR_C021) != new_tmp) {\n\t\t\t\tg_cur_a2_stat ^= new_tmp;\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x22: /* 0xc022 */\n\t\t\t/* change text color */\n\t\t\ttmp = (g_cur_a2_stat >> BIT_ALL_STAT_BG_COLOR) & 0xff;\n\t\t\tif(val != tmp) {\n\t\t\t\t/* change text/bg color! */\n\t\t\t\tg_cur_a2_stat &= ~(ALL_STAT_TEXT_COLOR |\n\t\t\t\t\t\t\tALL_STAT_BG_COLOR);\n\t\t\t\tg_cur_a2_stat += (val << BIT_ALL_STAT_BG_COLOR);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x23: /* 0xc023 */\n\t\t\tif((val & 0x19) != 0) {\n\t\t\t\thalt_printf(\"c023 write of %02x!!!\\n\", val);\n\t\t\t}\n\t\t\ttmp = (g_c023_val & 0x70) | (val & 0x0f);\n\t\t\tif((tmp & 0x22) == 0x22) {\n\t\t\t\tadd_irq(IRQ_PENDING_C023_SCAN);\n\t\t\t}\n\t\t\tif(!(tmp & 2)) {\n\t\t\t\tremove_irq(IRQ_PENDING_C023_SCAN);\n\t\t\t}\n\t\t\tif((tmp & 0x44) == 0x44) {\n\t\t\t\tadd_irq(IRQ_PENDING_C023_1SEC);\n\t\t\t}\n\t\t\tif(!(tmp & 0x4)) {\n\t\t\t\tremove_irq(IRQ_PENDING_C023_1SEC);\n\t\t\t}\n\n\t\t\tif(g_irq_pending & (IRQ_PENDING_C023_SCAN |\n\t\t\t\t\t\tIRQ_PENDING_C023_1SEC)) {\n\t\t\t\ttmp |= 0x80;\n\t\t\t}\n\t\t\tg_c023_val = tmp;\n\t\t\treturn;\n\t\tcase 0x24: /* 0xc024 */\n\t\t\t/* Write to mouse reg: Throw it away */\n\t\t\treturn;\n\t\tcase 0x26: /* 0xc026 */\n\t\t\tadb_write_c026(val);\n\t\t\treturn;\n\t\tcase 0x27: /* 0xc027 */\n\t\t\tadb_write_c027(val);\n\t\t\treturn;\n\t\tcase 0x29: /* 0xc029 */\n\t\t\tg_c029_val_some = val & 0x41;\n\t\t\tif((val & 1) == 0) {\n\t\t\t\thalt_printf(\"c029: %02x\\n\", val);\n\t\t\t}\n\t\t\tnew_tmp = val & 0xa0;\n\t\t\tif(new_tmp != (g_cur_a2_stat & 0xa0)) {\n\t\t\t\tg_cur_a2_stat = (g_cur_a2_stat & (~0xa0)) +\n\t\t\t\t\tnew_tmp;\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x2a: /* 0xc02a */\n#if 0\n\t\t\tprintf(\"Writing c02a with %02x\\n\", val);\n#endif\n\t\t\treturn;\n\t\tcase 0x2b: /* 0xc02b */\n\t\t\tg_c02b_val = val;\n\t\t\tif((val != 0x08) && (val != 0x00)) {\n\t\t\t\tprintf(\"Writing c02b with %02x\\n\", val);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x2d: /* 0xc02d = Slot Reg. Bit set means use slot rom */\n\t\t\tif((val & 0x9) != 0) {\n\t\t\t\thalt_printf(\"Illegal c02d write: %02x!\\n\", val);\n\t\t\t}\n\t\t\tfixup = (val != g_c02d_int_crom);\n\t\t\tg_c02d_int_crom = val;\n\t\t\tif(fixup) {\n\t\t\t\tvid_printf(\"Write c02d of %02x\\n\", val);\n\t\t\t\tfixup_intcx();\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x28: /* 0xc028 */\n\t\tcase 0x2c: /* 0xc02c */\n\t\t\tUNIMPL_WRITE;\n\t\tcase 0x25: /* 0xc025 */\n\t\t\t/* Space Shark writes to c025--ignore */\n\t\tcase 0x2e: /* 0xc02e */\n\t\tcase 0x2f: /* 0xc02f */\n\t\t\t/* Modulae writes to this--just ignore them */\n\t\t\treturn;\n\t\t\tbreak;\n\n\t\t/* 0xc030 - 0xc03f */\n\t\tcase 0x30: /* 0xc030 */\n#if 0\n\t\t\tprintf(\"Write speaker?\\n\");\n#endif\n\t\t\tsound_write_c030(dfcyc);\n\t\t\treturn;\n\t\tcase 0x31: /* 0xc031 */\n\t\t\ttmp = val ^ g_iwm.state;\n\t\t\tiwm_flush_cur_disk();\t// In case APPLE35SEL changes\n\t\t\tg_iwm.state = (g_iwm.state & (~0xc0)) | (val & 0xc0);\n\t\t\tif(tmp & IWM_STATE_C031_APPLE35SEL) {\n\t\t\t\t/* apple35_sel changed, maybe speed change */\n\t\t\t\tengine_recalc_events();\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x32: /* 0xc032 */\n\t\t\ttmp = g_c023_val & 0x7f;\n\t\t\tif(((val & 0x40) == 0) && (tmp & 0x40)) {\n\t\t\t\t/* clear 1 sec int */\n\t\t\t\tremove_irq(IRQ_PENDING_C023_1SEC);\n\t\t\t\ttmp &= 0xbf;\n\t\t\t\tg_c023_val = tmp;\n\t\t\t}\n\t\t\tif(((val & 0x20) == 0) && (tmp & 0x20)) {\n\t\t\t\t/* clear scan line int */\n\t\t\t\tremove_irq(IRQ_PENDING_C023_SCAN);\n\t\t\t\tg_c023_val = tmp & 0xdf;\n\t\t\t\tcheck_for_new_scan_int(dfcyc);\n\t\t\t}\n\t\t\tif(g_irq_pending & (IRQ_PENDING_C023_1SEC |\n\t\t\t\t\t\tIRQ_PENDING_C023_SCAN)) {\n\t\t\t\tg_c023_val |= 0x80;\n\t\t\t}\n\t\t\tif((val & 0x9f) != 0x9f) {\n\t\t\t\tirq_printf(\"c032: wrote %02x!\\n\", val);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x33: /* 0xc033 = CLOCKDATA*/\n\t\t\tg_c033_data = val;\n\t\t\treturn;\n\t\tcase 0x34: /* 0xc034 = CLOCKCTL */\n\t\t\ttmp = val ^ g_c034_val;\n\t\t\tclock_write_c034(val);\n\t\t\tif(tmp & 0xf) {\n\t\t\t\tchange_border_color(dfcyc, val & 0xf);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x35: /* 0xc035 */\n\t\t\tupdate_shadow_reg(dfcyc, val);\n\t\t\treturn;\n\t\tcase 0x36: /* 0xc036 = CYAREG */\n\t\t\ttmp = val ^ g_c036_val_speed;\n\t\t\tg_c036_val_speed = (val & ~0x20);\t/* clr bit 5 */\n\t\t\tif(tmp & 0x80) {\n\t\t\t\t/* to recalculate times since speed changing */\n\t\t\t\tengine_recalc_events();\n\t\t\t}\n\t\t\tif(tmp & 0xf) {\n\t\t\t\t/* slot_motor_detect changed */\n\t\t\t\tengine_recalc_events();\n\t\t\t}\n\n\t\t\tif((val & 0x60) != 0) {\n\t\t\t\t/* for ROM 03, 0x40 is the power-on status */\n\t\t\t\t/*  and can be read/write */\n\t\t\t\tif(((val & 0x60) != 0x40) ||\n\t\t\t\t\t\t\t(g_rom_version < 3)) {\n\t\t\t\t\tg_c036_val_speed &= (~0x60);\n\t\t\t\t\thalt_printf(\"c036: %2x\\n\", val);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(tmp & 0x10) {\t/* shadow in all banks! */\n\t\t\t\tif(g_num_shadow_all_banks++ == 0) {\n\t\t\t\t\tprintf(\"Shadowing all banks...This \"\n\t\t\t\t\t\t\"must be the NFC Megademo\\n\");\n\t\t\t\t}\n\t\t\t\tfixup_shadow_all_banks();\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x37: /* 0xc037 */\n\t\t\t/* just ignore, probably someone writing c036 m=0 */\n\t\t\treturn;\n\t\tcase 0x38: /* 0xc038 */\n\t\t\tscc_write_reg(dfcyc, 1, val);\n\t\t\treturn;\n\t\tcase 0x39: /* 0xc039 */\n\t\t\tscc_write_reg(dfcyc, 0, val);\n\t\t\treturn;\n\t\tcase 0x3a: /* 0xc03a */\n\t\t\tscc_write_data(dfcyc, 1, val);\n\t\t\treturn;\n\t\tcase 0x3b: /* 0xc03b */\n\t\t\tscc_write_data(dfcyc, 0, val);\n\t\t\treturn;\n\t\tcase 0x3c: /* 0xc03c */\n\t\t\t/* doc ctl */\n\t\t\tdoc_write_c03c(dfcyc, val);\n\t\t\treturn;\n\t\tcase 0x3d: /* 0xc03d */\n\t\t\t/* doc data reg */\n\t\t\tdoc_write_c03d(dfcyc, val);\n\t\t\treturn;\n\t\tcase 0x3e: /* 0xc03e */\n\t\t\tg_c03ef_doc_ptr = (g_c03ef_doc_ptr & 0xff00) + val;\n\t\t\treturn;\n\t\tcase 0x3f: /* 0xc03f */\n\t\t\tg_c03ef_doc_ptr = (g_c03ef_doc_ptr & 0xff) + (val << 8);\n\t\t\treturn;\n\n\t\t/* 0xc040 - 0xc04f */\n\t\tcase 0x41: /* c041 */\n\t\t\tg_c041_val = val & 0x1f;\n\t\t\tif((val & 0xe7) != 0) {\n\t\t\t\thalt_printf(\"write c041: %02x\\n\", val);\n\t\t\t}\n\n\t\t\tif(!(val & C041_EN_VBL_INTS)) {\n\t\t\t\t/* no more vbl interrupt */\n\t\t\t\tremove_irq(IRQ_PENDING_C046_VBL);\n\t\t\t}\n\t\t\tif(!(val & C041_EN_25SEC_INTS)) {\n\t\t\t\tremove_irq(IRQ_PENDING_C046_25SEC);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x46: /* c046 */\n\t\t\t/* ignore writes to c046 */\n\t\t\treturn;\n\t\tcase 0x47: /* c047 */\n\t\t\tremove_irq(IRQ_PENDING_C046_VBL |\n\t\t\t\t\t\tIRQ_PENDING_C046_25SEC);\n\t\t\tg_c046_val &= 0xe7;\t/* clear vblint, 1/4sec int*/\n\t\t\treturn;\n\t\tcase 0x48: /* c048 */\n\t\t\t/* diversitune writes this--ignore it */\n\t\t\treturn;\n\t\tcase 0x42: /* c042 */\n\t\tcase 0x43: /* c043 */\n\t\t\treturn;\n\t\tcase 0x4f: /* c04f */\n\t\t\tg_em_emubyte_cnt = 1;\n\t\t\treturn;\n\t\tcase 0x45: /* c045 */\n\t\t\treturn;\n\t\tcase 0x40: /* c040 */\n\t\tcase 0x44: /* c044 */\n\t\tcase 0x49: /* c049 */\n\t\tcase 0x4a: /* c04a */\n\t\tcase 0x4b: /* c04b */\n\t\tcase 0x4c: /* c04c */\n\t\tcase 0x4d: /* c04d */\n\t\tcase 0x4e: /* c04e */\n\t\t\tUNIMPL_WRITE;\n\n\t\t/* 0xc050 - 0xc05f */\n\t\tcase 0x50: /* 0xc050 */\n\t\t\tif(g_cur_a2_stat & ALL_STAT_TEXT) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_TEXT);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x51: /* 0xc051 */\n\t\t\tif((g_cur_a2_stat & ALL_STAT_TEXT) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_TEXT);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x52: /* 0xc052 */\n\t\t\tif(g_cur_a2_stat & ALL_STAT_MIX_T_GR) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_MIX_T_GR);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x53: /* 0xc053 */\n\t\t\tif((g_cur_a2_stat & ALL_STAT_MIX_T_GR) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_MIX_T_GR);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x54: /* 0xc054 */\n\t\t\tset_statereg(dfcyc, g_c068_statereg & (~0x40));\n\t\t\treturn;\n\t\tcase 0x55: /* 0xc055 */\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x40);\n\t\t\treturn;\n\t\tcase 0x56: /* 0xc056 */\n\t\t\tif(g_cur_a2_stat & ALL_STAT_HIRES) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_HIRES);\n\t\t\t\tfixup_hires_on();\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x57: /* 0xc057 */\n\t\t\tif((g_cur_a2_stat & ALL_STAT_HIRES) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_HIRES);\n\t\t\t\tfixup_hires_on();\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x58: /* 0xc058 */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\tg_zipgs_reg_c059 &= 0x4;  /* last reset cold */\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs &= (~1);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x59: /* 0xc059 */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\tg_zipgs_reg_c059 = (val & 0xf8) |\n\t\t\t\t\t\t(g_zipgs_reg_c059 & 0x7);\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs |= 1;\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x5a: /* 0xc05a */\n\t\t\tg_c05x_annuncs &= (~2);\n\t\t\tif((val & 0xf0) == 0x50) {\n\t\t\t\tg_zipgs_unlock++;\n\t\t\t} else if((val & 0xf0) == 0xa0) {\n\t\t\t\tg_zipgs_unlock = 0;\n\t\t\t} else if(g_zipgs_unlock >= 4) {\n\t\t\t\tif((g_zipgs_reg_c05b & 0x10) == 0) {\n\t\t\t\t\t/* to recalculate times */\n\t\t\t\t\tengine_recalc_events();\n\t\t\t\t}\n\t\t\t\tg_zipgs_reg_c05b |= 0x10;\t// disable\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x5b: /* 0xc05b */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\tif((g_zipgs_reg_c05b & 0x10) != 0) {\n\t\t\t\t\t/* to recalculate times */\n\t\t\t\t\tengine_recalc_events();\n\t\t\t\t}\n\t\t\t\tg_zipgs_reg_c05b &= (~0x10);\t// enable\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs |= 2;\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x5c: /* 0xc05c */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\tg_zipgs_reg_c05c = val;\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs &= (~4);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x5d: /* 0xc05d */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\tif(((g_zipgs_reg_c05a ^ val) >= 0x10) &&\n\t\t\t\t\t((g_zipgs_reg_c05b & 0x10) == 0)) {\n\t\t\t\t\tengine_recalc_events();\n\t\t\t\t}\n\t\t\t\tg_zipgs_reg_c05a = val | 0xf;\n\t\t\t} else {\n\t\t\t\tg_c05x_annuncs |= 4;\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x5e: /* 0xc05e: SETAN3, clear AN3, double-hires on */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\t/* Zippy writes 0x80 and 0x00 here... */\n\t\t\t} else if(g_cur_a2_stat & ALL_STAT_ANNUNC3) {\n\t\t\t\tg_cur_a2_stat &= (~ALL_STAT_ANNUNC3);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\t\tcase 0x5f: /* 0xc05f: CLRAN3, set AN3, double-hires off */\n\t\t\tif(g_zipgs_unlock >= 4) {\n\t\t\t\thalt_printf(\"Wrote ZipGS $c05f: %02x\\n\", val);\n\t\t\t} else if((g_cur_a2_stat & ALL_STAT_ANNUNC3) == 0) {\n\t\t\t\tg_cur_a2_stat |= (ALL_STAT_ANNUNC3);\n\t\t\t\tchange_display_mode(dfcyc);\n\t\t\t}\n\t\t\treturn;\n\n\n\t\t/* 0xc060 - 0xc06f */\n\t\tcase 0x60: /* 0xc060 */\n\t\tcase 0x61: /* 0xc061 */\n\t\tcase 0x62: /* 0xc062 */\n\t\tcase 0x63: /* 0xc063 */\n\t\tcase 0x64: /* 0xc064 */\n\t\tcase 0x65: /* 0xc065 */\n\t\tcase 0x66: /* 0xc066 */\n\t\tcase 0x67: /* 0xc067 */\n\t\t\t/* all the above do nothing--return */\n\t\t\treturn;\n\t\tcase 0x68: /* 0xc068 = STATEREG */\n\t\t\tset_statereg(dfcyc, (g_c068_statereg & ~0xff) | val);\n\t\t\treturn;\n\t\tcase 0x69: /* 0xc069 */\n\t\t\t/* just ignore, someone writing c068 with m=0 */\n\t\t\treturn;\n\t\tcase 0x6a: /* 0xc06a */\n\t\tcase 0x6b: /* 0xc06b */\n\t\t\tUNIMPL_WRITE;\n\t\tcase 0x6c: /* 0xc06c */\n\t\t\tg_c06c_latched_cyc = (word32)(dfcyc >> 16);\n\t\t\treturn;\n\t\tcase 0x6d: /* 0xc06d */\n\t\t\t// Affect what reads to $C02C can see, only $40 now\n\t\t\tif((g_c06f_val & 0x100) == 0) {\t\t// Locked\n\t\t\t\tval = 0;\n\t\t\t}\n\t\t\tg_c06d_val = val;\t\t\t// Test mode\n\t\t\treturn;\n\t\tcase 0x6e: /* 0xc06e */\n\t\t\tg_c06f_val = 0;\n\t\t\treturn;\n\t\tcase 0x6f: /* 0xc06f */\n\t\t\tif((g_c06f_val == 0xda) && (val == 0x61)) {\n\t\t\t\tval |= 0x100;\t// Unlock\n\t\t\t}\n\t\t\tg_c06f_val = val;\n\t\t\treturn;\n\n\t\t/* 0xc070 - 0xc07f */\n\t\tcase 0x70: /* 0xc070 = Trigger paddles */\n\t\t\tpaddle_trigger(dfcyc);\n\t\t\treturn;\n\t\tcase 0x73: /* 0xc073 = multibank ram card bank addr? */\n\t\t\treturn;\n\t\tcase 0x71: /* 0xc071 = another multibank ram card enable? */\n\t\tcase 0x7e: /* 0xc07e */\n\t\tcase 0x7f: /* 0xc07f */\n\t\t\treturn;\n\t\tcase 0x72: /* 0xc072 */\n\t\tcase 0x74: /* 0xc074 */\n\t\tcase 0x75: /* 0xc075 */\n\t\tcase 0x76: /* 0xc076 */\n\t\tcase 0x77: /* 0xc077 */\n\t\tcase 0x78: /* 0xc078 */\n\t\tcase 0x79: /* 0xc079 */\n\t\tcase 0x7a: /* 0xc07a */\n\t\tcase 0x7b: /* 0xc07b */\n\t\tcase 0x7c: /* 0xc07c */\n\t\tcase 0x7d: /* 0xc07d */\n\t\t\treturn;\n\n\t\t/* 0xc080 - 0xc08f */\n\t\tcase 0x80: case 0x81: case 0x82: case 0x83:\n\t\tcase 0x84: case 0x85: case 0x86: case 0x87:\n\t\tcase 0x88: case 0x89: case 0x8a: case 0x8b:\n\t\tcase 0x8c: case 0x8d: case 0x8e: case 0x8f:\n\t\t\tnew_lcbank2 = ((loc >> 1) & 0x4) ^ 0x4;\n\t\t\tnew_rdrom = ((loc << 3) ^ (loc << 2)) & 8;\n\t\t\t\t// new_rdrom is set if loc0 ^ loc1 is set\n\t\t\t\t// 8=RDROM, 0=read from LC RAM\n\t\t\tnew_prewrite = 0;\t\t// Writes clear PREWRITE\n\t\t\tnew_wrdefram = g_c068_statereg & 0x200;\n\t\t\tif((loc & 1) == 0) {\n\t\t\t\tnew_wrdefram = 0;\t// Any even access clrs\n\t\t\t}\n\t\t\tset_statereg(dfcyc, (g_c068_statereg & ~(0x30c)) |\n\t\t\t\t\tnew_lcbank2 | new_rdrom |\n\t\t\t\t\tnew_prewrite | new_wrdefram);\n\t\t\treturn;\n\n\t\t/* 0xc090 - 0xc09f */\n\t\tcase 0x90: case 0x91: case 0x92: case 0x93:\n\t\tcase 0x94: case 0x95: case 0x96: case 0x97:\n\t\tcase 0x98: case 0x99: case 0x9a: case 0x9b:\n\t\tcase 0x9c: case 0x9d: case 0x9e: case 0x9f:\n\t\t\tUNIMPL_WRITE;\n\n\t\t/* 0xc0a0 - 0xc0af */\n\t\tcase 0xa0: case 0xa1: case 0xa3:\n\t\tcase 0xa4: case 0xa5: case 0xa6: case 0xa7:\n\t\tcase 0xa9: case 0xaa: case 0xab:\n\t\tcase 0xac: case 0xad: case 0xae: case 0xaf:\n\t\t\tUNIMPL_WRITE;\n\t\tcase 0xa2:\t/* Burger Times writes here on error */\n\t\tcase 0xa8:\n\t\t\t/* Kurzweil SMP writes to 0xc0a8, ignore it */\n\t\t\treturn;\n\n\t\t/* 0xc0b0 - 0xc0bf */\n\t\tcase 0xb0: case 0xb1: case 0xb2: case 0xb3:\n\t\tcase 0xb4: case 0xb5: case 0xb6: case 0xb7:\n\t\tcase 0xb8: case 0xb9: case 0xba: case 0xbb:\n\t\tcase 0xbc: case 0xbd: case 0xbe: case 0xbf:\n\t\t\tvoc_devsel_write(loc, val, dfcyc);\n\t\t\treturn;\n\n\t\t/* 0xc0c0 - 0xc0cf */\n\t\tcase 0xc0: case 0xc1: case 0xc2: case 0xc3:\n\t\t\t// Slot 4 has a Slinky RAM card\n\t\t\tslinky_devsel_write(dfcyc, loc, val);\n\t\t\treturn;\n\t\tcase 0xc4: case 0xc5: case 0xc6: case 0xc7:\n\t\tcase 0xc8: case 0xc9: case 0xca: case 0xcb:\n\t\tcase 0xcc: case 0xcd: case 0xce: case 0xcf:\n\t\t\tUNIMPL_WRITE;\n\n\t\t/* 0xc0d0 - 0xc0df */\n\t\tcase 0xd0: case 0xd1: case 0xd2: case 0xd3:\n\t\tcase 0xd4: case 0xd5: case 0xd6: case 0xd7:\n\t\tcase 0xd8: case 0xd9: case 0xda: case 0xdb:\n\t\tcase 0xdc: case 0xdd: case 0xde: case 0xdf:\n\t\t\tUNIMPL_WRITE;\n\n\t\t/* 0xc0e0 - 0xc0ef */\n\t\tcase 0xe0: case 0xe1: case 0xe2: case 0xe3:\n\t\tcase 0xe4: case 0xe5: case 0xe6: case 0xe7:\n\t\tcase 0xe8: case 0xe9: case 0xea: case 0xeb:\n\t\tcase 0xec: case 0xed: case 0xee: case 0xef:\n\t\t\twrite_iwm(loc, val, dfcyc);\n\t\t\treturn;\n\n\t\t/* 0xc0f0 - 0xc0ff */\n\t\tcase 0xf0: case 0xf1: case 0xf2: case 0xf3:\n\t\tcase 0xf4: case 0xf5: case 0xf6: case 0xf7:\n\t\tcase 0xf8: case 0xf9: case 0xfa: case 0xfb:\n\t\tcase 0xfc: case 0xfd: case 0xfe: case 0xff:\n\t\t\tUNIMPL_WRITE;\n\t\tdefault:\n\t\t\tprintf(\"Write loc: %x\\n\",loc);\n\t\t\texit(-300);\n\t\t}\n\t\tbreak;\n\tcase 1: case 2: case 5: case 6: case 7:\n\t\t/* c1000 - c7ff (but not c3xx,c4xx) */\n\t\tif((loc & 0xff) == 0) {\t\t// Frogger writes $ff to $Cx00\n\t\t\treturn;\n\t\t}\n\t\tUNIMPL_WRITE;\n\tcase 3:\n\t\tif(((g_c02d_int_crom & 8) == 0) &&\n\t\t\t\t\t((g_c068_statereg & 0x400) == 0)) {\n\t\t\t// SLOTC3ROM clear, INTC8rom clear: Set INTC8ROM\n\t\t\tset_statereg(dfcyc, g_c068_statereg | 0x400);\n\t\t}\n\t\treturn;\n\tcase 4:\n\t\tif((g_c02d_int_crom & 0x10) && !(g_c068_statereg & 1)) {\n\t\t\t// Slot 4 is set to Your Card and not INTCX\n\t\t\tmockingboard_write(loc, val, dfcyc);\n\t\t\treturn;\n\t\t}\n\t\treturn;\n\tcase 8: case 9: case 0xa: case 0xb: case 0xc: case 0xd: case 0xe:\n\t\t// UNIMPL_WRITE;\n\t\treturn;\n\tcase 0xf:\n\t\tif((loc & 0xfff) == 0xfff) {\n\t\t\t/* cfff */\n\t\t\tif(g_c068_statereg & 0x400) {\n\t\t\t\tset_statereg(dfcyc, g_c068_statereg & (~0x400));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// UNIMPL_WRITE;\n\t\t// Wings of Fury writes to $cf00-$cfff when reading a 0x300\n\t\t//  sector where it wants to load 0x200 to 0xd000-0xd1ff\n\t\treturn;\n\t}\n\tprintf(\"Huh2? Write loc: %x\\n\", loc);\n\texit(-290);\n}\n\nword32\nslinky_devsel_read(dword64 dfcyc, word32 loc)\n{\n\tword32\tval;\n\n\tloc = loc & 0xf;\n\tval = 0;\n\tif(loc <= 2) {\n\t\tval = (g_slinky_addr >> (8*loc)) & 0xff;\n\t}\n\tif(loc == 3) {\n\t\tval = g_slinky_ram[g_slinky_addr & (SLINKY_RAM_SIZE - 1)];\n\t\tdbg_log_info(dfcyc, g_slinky_addr, val, 0xc0c3);\n\t\tg_slinky_addr++;\n\t}\n\treturn val;\n}\n\nvoid\nslinky_devsel_write(dword64 dfcyc, word32 loc, word32 val)\n{\n\tword32\tmask;\n\n\tloc = loc & 0xf;\n\tdbg_log_info(dfcyc, g_slinky_addr, val, 0xc0c0 + loc);\n\tif(loc <= 2) {\n\t\tmask = 0xff << (8 * loc);\n\t\tval = val * 0x010101;\n\t\tg_slinky_addr = (g_slinky_addr & (~mask)) | (val & mask);\n\t}\n\tif(loc == 3) {\n\t\tg_slinky_ram[g_slinky_addr & (SLINKY_RAM_SIZE - 1)] = val;\n\t\tg_slinky_addr++;\n\t}\n}\n\nword32\nc3xx_read(dword64 dfcyc, word32 loc)\n{\n\t// We may have been marked IO so that we could set INTC8ROM,\n\t//  but we still need to return ROM\n\tif(((g_c02d_int_crom & 8) == 0) && ((g_c068_statereg & 0x400) == 0)) {\n\t\t// SLOTC3ROM is not set:  Set INTC8ROM\n\t\tset_statereg(dfcyc, g_c068_statereg | 0x400);\n\t}\n\tif(((g_c02d_int_crom & 8) == 0) || (g_c068_statereg & 1) ||\n\t\t\t\t\t\t\t(g_rom_version == 0)) {\n\t\t// Access ROM for slot 3\n\t\treturn g_rom_fc_ff_ptr[0x3c300 + (loc & 0xff)];\n\t}\n\treturn float_bus(dfcyc);\n}\n\n// IIgs vertical line counters\n// 0x7d - 0x7f: in vbl, top of screen\n// 0x80 - 0xdf: not in vbl, drawing screen\n// 0xe0 - 0xff: in vbl, bottom of screen\n// Note: lines are then 0-0x60 effectively, for 192 lines, from 0x80-0xdf\n// vertical blanking engages on line 192, even if in super hires mode\n// (Last 8 lines in SHR are drawn with vbl_active set\n\nword32\nget_lines_since_vbl(dword64 dfcyc)\n{\n\tdouble\tdusecs_since_last_vbl, dlines_since_vbl, dcyc_line_start;\n\tword32\tlines_since_vbl;\n\tint\toffset;\n\n\tdusecs_since_last_vbl = (double)((dfcyc >> 16) -\n\t\t\t\t\t\t(g_last_vbl_dfcyc >> 16));\n\n\tdlines_since_vbl = dusecs_since_last_vbl * (1.0 / 65.0);\n\tlines_since_vbl = (int)dlines_since_vbl;\n\tdcyc_line_start = (double)lines_since_vbl * 65.0;\n\n\toffset = ((int)(dusecs_since_last_vbl - dcyc_line_start)) & 0xff;\n\n\tlines_since_vbl = (lines_since_vbl << 8) + offset;\n\n\tif(!g_halt_sim && !g_config_control_panel) {\n\t\tdbg_log_info(dfcyc, (word32)dusecs_since_last_vbl,\n\t\t\t\t\t\tlines_since_vbl, 0xc02e);\n\t}\n\tif(lines_since_vbl < 0x10541) {\n\t\treturn lines_since_vbl;\n\t} else {\n\t\t// We've entered the next frame, but update_60hz() hasn't been\n\t\t//  called yet.  Produce the proper c02e/c02f counter values\n\t\t//  for the first line of the display\n\t\tlines_since_vbl = lines_since_vbl - 0x10600;\n#if 0\n\t\tprintf(\"lines_since_vbl was:%08x, now:%08x\\n\",\n\t\t\t\tlines_since_vbl + 0x10600, lines_since_vbl);\n\t\thalt_printf(\"lines_since_vbl: %08x!\\n\", lines_since_vbl);\n\t\tprintf(\"du_s_l_v: %f, dfcyc: %016llx, last_vbl_cycs: %016llx\\n\",\n\t\t\tdusecs_since_last_vbl, dfcyc, g_last_vbl_dfcyc);\n\t\tshow_dtime_array();\n\t\tshow_all_events();\n\t\t/* U_STACK_TRACE(); */\n#endif\n\t}\n\n\treturn lines_since_vbl;\n}\n\nint\nin_vblank(dword64 dfcyc)\n{\n\tword32\tlines_since_vbl;\n\n\tlines_since_vbl = get_lines_since_vbl(dfcyc);\n\n\t// Testing indicates $c019 is a cycle delayed from $C02F counter\n\tif(lines_since_vbl > 0xc000) {\t// Exclude line 192 first cycle!\n\t\treturn 1;\t\t// 1=in VBL\n\t}\n\tif(lines_since_vbl == 0) {\n\t\treturn 1;\t\t// Handle 1-cycle delay in reading c019\n\t}\n\n\treturn 0;\n}\n\n// horizontal video counter goes from 0x00,0x40 - 0x7f, then 0x80,0xc0-0xff\n// over 2*65 cycles.  The last visible screen pos is 0x7f and 0xff\n// KEGS starts it's \"line 0\" at the start of the right border for line -1\n// See get_lines_since_vbl comment for the format of the vertical counter\nint\nread_vid_counters(int loc, dword64 dfcyc)\n{\n\tword32\tmask;\n\tint\tlines_since_vbl;\n\n\tloc = loc & 0xf;\n\n\tlines_since_vbl = get_lines_since_vbl(dfcyc);\n\n\tlines_since_vbl += 0x10000;\n\tif(lines_since_vbl >= 0x20000) {\n\t\tlines_since_vbl = lines_since_vbl - 0x20000 + 0xfa00;\n\t}\n\n\tif(lines_since_vbl > 0x1ffff) {\n\t\thalt_printf(\"lines_since_vbl: %04x, dfcyc: %016llx, last_vbl:\"\n\t\t\t\"%016llx\\n\", lines_since_vbl, dfcyc, g_last_vbl_dfcyc);\n\t}\n\n\tif(loc == 0xe) {\t\t// c02e:  Vertical count\n\t\treturn (lines_since_vbl >> 9) & 0xff;\n\t}\n\n\tmask = (lines_since_vbl >> 1) & 0x80;\n\n\tlines_since_vbl = (lines_since_vbl & 0xff);\n\tif(lines_since_vbl >= 0x01) {\n\t\tlines_since_vbl = (lines_since_vbl + 0x3f) & 0x7f;\n\t}\n\treturn (mask | (lines_since_vbl & 0xff));\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/op_routs.h",
    "content": "// $KmKId: op_routs.h,v 1.47 2023-11-05 16:21:51+00 kentd Exp $\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2021 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n\n#define GET_DLOC_X_IND_WR()\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\t\\\n\tif(direct & 0xff) {\t\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\t\\\n\t}\t\t\t\t\t\\\n\targ = arg + xreg + direct;\t\t\\\n\tGET_MEMORY_DIRECT_PAGE16(arg & 0xffff, arg, 1);\t\\\n\targ = (dbank << 16) + arg;\n\n\n#define GET_DLOC_X_IND_ADDR()\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DLOC_X_IND_WR()\n\n#define GET_DISP8_S_WR()\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\\\n\targ = (arg + stack) & 0xffff;\t\\\n\tINC_KPC_2;\n\n\n#define GET_DISP8_S_ADDR()\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DISP8_S_WR()\n\n#define GET_DLOC_WR()\t\t\t\\\n\targ = (arg + direct) & 0xffff;\t\\\n\tif(direct & 0xff) {\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\\\n\t}\t\t\t\t\\\n\tINC_KPC_2;\n\n#define GET_DLOC_ADDR()\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DLOC_WR()\n\n#define GET_DLOC_L_IND_WR()\t\t\\\n\targ = (arg + direct) & 0xffff;\t\\\n\tif(direct & 0xff) {\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\\\n\t}\t\t\t\t\\\n\tINC_KPC_2;\t\t\t\\\n\tGET_MEMORY24(arg, arg, 1);\n\n#define GET_DLOC_L_IND_ADDR()\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DLOC_L_IND_WR()\n\n\n#define GET_DLOC_IND_WR()\t\t\\\n\tINC_KPC_2;\t\t\t\\\n\tif(direct & 0xff) {\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\\\n\t}\t\t\t\t\\\n\tGET_MEMORY_DIRECT_PAGE16((direct + arg) & 0xffff, arg, 0);\t\\\n\targ = (dbank << 16) + arg;\n\n\n#define GET_DLOC_IND_ADDR()\t\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DLOC_IND_WR();\n\n#define GET_DLOC_INDEX_WR(index_reg)\t\\\n\tCYCLES_PLUS_1;\t\t\t\\\n\targ = (arg & 0xff) + index_reg;\t\\\n\tINC_KPC_2;\t\t\t\\\n\tif(direct & 0xff) {\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\\\n\t}\t\t\t\t\\\n\tif((psr & 0x100) && ((direct & 0xff) == 0)) {\t\\\n\t\targ = (arg & 0xff);\t\\\n\t}\t\t\t\t\\\n\targ = (arg + direct) & 0xffff;\n\n#define GET_DLOC_X_WR()\t\\\n\tGET_DLOC_INDEX_WR(xreg)\n#define GET_DLOC_Y_WR()\t\\\n\tGET_DLOC_INDEX_WR(yreg)\n\n#define GET_DLOC_X_ADDR()\t\\\n\tGET_1BYTE_ARG;\t\t\\\n\tGET_DLOC_INDEX_WR(xreg)\n\n#define GET_DLOC_Y_ADDR()\t\\\n\tGET_1BYTE_ARG;\t\t\\\n\tGET_DLOC_INDEX_WR(yreg)\n\n#define GET_DISP8_S_IND_Y_WR()\t\t\\\n\targ = (stack + arg) & 0xffff;\t\\\n\tGET_MEMORY16(arg,arg,1);\t\\\n\tCYCLES_PLUS_2;\t\t\t\\\n\targ += (dbank << 16);\t\t\\\n\tINC_KPC_2;\t\t\t\\\n\targ = (arg + yreg) & 0xffffff;\n\n#define GET_DISP8_S_IND_Y_ADDR()\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DISP8_S_IND_Y_WR()\n\n#define GET_DLOC_L_IND_Y_WR()\t\t\\\n\targ = (direct + arg) & 0xffff;\t\\\n\tif(direct & 0xff) {\t\t\\\n\t\tCYCLES_PLUS_1;\t\t\\\n\t}\t\t\t\t\\\n\tGET_MEMORY24(arg,arg,1);\t\\\n\tINC_KPC_2;\t\t\t\\\n\targ = (arg + yreg) & 0xffffff;\n\n#define GET_DLOC_L_IND_Y_ADDR()\t\\\n\tGET_1BYTE_ARG;\t\t\t\\\n\tGET_DLOC_L_IND_Y_WR()\n\n\n#define GET_ABS_ADDR()\t\t\t\\\n\tGET_2BYTE_ARG;\t\t\t\\\n\tCYCLES_PLUS_1;\t\t\t\\\n\targ = arg + (dbank << 16);\t\\\n\tINC_KPC_3;\n\n#define GET_LONG_ADDR()\t\t\\\n\tGET_3BYTE_ARG;\t\t\t\\\n\tCYCLES_PLUS_2;\t\t\t\\\n\tINC_KPC_4;\n\n#define GET_LONG_X_ADDR_FOR_WR()\t\t\\\n\tGET_3BYTE_ARG;\t\t\t\\\n\tINC_KPC_4;\t\t\t\\\n\targ = (arg + xreg) & 0xffffff;\t\\\n\tCYCLES_PLUS_2;\n\n"
  },
  {
    "path": "upstream/kegs/src/paddles.c",
    "content": "const char rcsid_paddles_c[] = \"@(#)$KmKId: paddles.c,v 1.21 2023-05-19 13:52:54+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include \"defc.h\"\n\nextern int g_mouse_raw_x;\t/* from adb.c */\nextern int g_mouse_raw_y;\n\ndword64\tg_paddle_trig_dfcyc = 0;\nint\tg_swap_paddles = 0;\nint\tg_invert_paddles = 0;\nint\tg_joystick_scale_factor_x = 0x100;\nint\tg_joystick_scale_factor_y = 0x100;\nint\tg_joystick_trim_amount_x = 0;\nint\tg_joystick_trim_amount_y = 0;\n\nint\tg_joystick_type = 0;\t/* 0 = Keypad Joystick */\nint\tg_joystick_native_type1 = -1;\nint\tg_joystick_native_type2 = -1;\nint\tg_joystick_native_type = -1;\n\nextern int g_paddle_buttons;\n\nint\tg_paddle_val[4] = { 0, 0, 0, 0 };\n\t\t/* g_paddle_val[0]: Joystick X coord, [1]:Y coord */\n\ndword64\tg_paddle_dfcyc[4] = { 0, 0, 0, 0 };\n\t\t/* g_paddle_dfcyc are the dfcyc the paddle goes to 0 */\n\n\nvoid\npaddle_fixup_joystick_type()\n{\n\t/* If g_joystick_type points to an illegal value, change it */\n\tif(g_joystick_type == 2) {\n\t\tg_joystick_native_type = g_joystick_native_type1;\n\t\tif(g_joystick_native_type1 < 0) {\n\t\t\tg_joystick_type = 0;\n\t\t}\n\t}\n\tif(g_joystick_type == 3) {\n\t\tg_joystick_native_type = g_joystick_native_type2;\n\t\tif(g_joystick_native_type2 < 0) {\n\t\t\tg_joystick_type = 0;\n\t\t}\n\t}\n}\n\nvoid\npaddle_trigger(dword64 dfcyc)\n{\n\t/* Called by read/write to $c070 */\n\tg_paddle_trig_dfcyc = dfcyc;\n\n\t/* Determine what all the paddle values are right now */\n\tpaddle_fixup_joystick_type();\n\n\tswitch(g_joystick_type) {\n\tcase 0:\t\t/* Keypad Joystick */\n\t\tpaddle_trigger_keypad(dfcyc);\n\t\tbreak;\n\tcase 1:\t\t/* Mouse Joystick */\n\t\tpaddle_trigger_mouse(dfcyc);\n\t\tbreak;\n\tdefault:\n\t\tjoystick_update(dfcyc);\n\t}\n}\n\nvoid\npaddle_trigger_mouse(dword64 dfcyc)\n{\n\tint\tval_x, val_y;\n\tint\tmouse_x, mouse_y;\n\n\tval_x = 0;\n\n\tmouse_x = g_mouse_raw_x;\n\tmouse_y = g_mouse_raw_y;\n\t/* mous_phys_x is 0->560, convert that to -32768 to + 32767 cyc */\n\t/*  So subtract 280 then mult by 117 */\n\tval_x = (mouse_x - 280) * 117;\n\n\t/* mous_phys_y is 0->384, convert that to -32768 to + 32767 cyc */\n\t/*  so subtract 192 then mult by 180 to overscale it a bit */\n\tval_y = (mouse_y - 192) * 180;\n\n\tg_paddle_val[0] = val_x;\n\tg_paddle_val[1] = val_y;\n\tg_paddle_val[2] = 32767;\n\tg_paddle_val[3] = 32767;\n\tg_paddle_buttons |= 0xc;\n\tpaddle_update_trigger_dcycs(dfcyc);\n}\n\nvoid\npaddle_trigger_keypad(dword64 dfcyc)\n{\n\tint\tget_y, val_x, val_y;\n\n\tval_x = adb_get_keypad_xy(get_y=0);\n\tval_y = adb_get_keypad_xy(get_y=1);\n\t/* val_x and val_y are already scale -32768 to +32768 */\n\n\tg_paddle_val[0] = val_x;\n\tg_paddle_val[1] = val_y;\n\tg_paddle_val[2] = 32767;\n\tg_paddle_val[3] = 32767;\n\tg_paddle_buttons |= 0xc;\n\tpaddle_update_trigger_dcycs(dfcyc);\n}\n\nvoid\npaddle_update_trigger_dcycs(dword64 dfcyc)\n{\n\tdword64\ttrig_dfcyc;\n\tint\tval, paddle_num, scale, trim;\n\tint\ti;\n\n\tfor(i = 0; i < 4; i++) {\n\t\tpaddle_num = i;\n\t\tif(g_swap_paddles) {\n\t\t\tpaddle_num = i ^ 1;\n\t\t}\n\t\tval = g_paddle_val[paddle_num];\n\t\tif(g_invert_paddles) {\n\t\t\tval = -val;\n\t\t}\n\t\t/* convert -32768 to +32768 into 0->2816.0 cycles (the */\n\t\t/* paddle delay const) */\n\t\t/* First multiply by the scale factor to adjust range */\n\t\tif(paddle_num & 1) {\n\t\t\tscale = g_joystick_scale_factor_y;\n\t\t\ttrim = g_joystick_trim_amount_y;\n\t\t} else {\n\t\t\tscale = g_joystick_scale_factor_x;\n\t\t\ttrim = g_joystick_trim_amount_x;\n\t\t}\n#if 0\n\t\tif(i == 0) {\n\t\t\tprintf(\"val was %04x(%d) * scale %03x = %d\\n\",\n\t\t\t\tval, val, scale, (val*scale)>>16);\n\t\t}\n#endif\n\t\tval = (val * scale) >> 16;\n\t\t/* Val is now from -128 to + 128 since scale is */\n\t\t/*  256=1.0, 128 = 0.5 */\n\t\tval = val + 128 + trim;\n\t\tif(val >= 255) {\n\t\t\tval = 280;\t/* increase range */\n\t\t}\n\t\ttrig_dfcyc = dfcyc + (dword64)((val * (2816/255.0)) * 65536);\n\t\tg_paddle_dfcyc[i] = trig_dfcyc;\n\t\tif(i < 2) {\n\t\t\tdbg_log_info(dfcyc, (scale << 16) | (val & 0xffff),\n\t\t\t\t\t(trim << 16) | i, 0x70);\n\t\t}\n\t}\n}\n\nint\nread_paddles(dword64 dfcyc, int paddle)\n{\n\tdword64\ttrig_dfcyc;\n\n\ttrig_dfcyc = g_paddle_dfcyc[paddle & 3];\n\n\tif(dfcyc < trig_dfcyc) {\n\t\treturn 0x80;\n\t} else {\n\t\treturn 0x00;\n\t}\n}\n\nvoid\npaddle_update_buttons()\n{\n\tpaddle_fixup_joystick_type();\n\tjoystick_update_buttons();\n}\n"
  },
  {
    "path": "upstream/kegs/src/protos.h",
    "content": "/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2019 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#ifdef INCLUDE_RCSID_C\nconst char rcsid_protos_h[] = \"@(#)$KmKId: protos.h,v 1.190 2019-12-16 02:02:53+00 kentd Exp $\";\n#endif\n\n#include \"protos_base.h\"\n"
  },
  {
    "path": "upstream/kegs/src/protos_base.h",
    "content": "/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2024 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#ifdef INCLUDE_RCSID_C\nconst char rcsid_protos_base_h[] = \"@(#)$KmKId: protos_base.h,v 1.161 2024-09-15 13:56:41+00 kentd Exp $\";\n#endif\n\n#ifdef __GNUC__\nvoid halt_printf(const char *fmt, ...) __attribute__ ((\n\t\t\t\t\t\t__format__(printf, 1, 2)));\nvoid cfg_err_printf(const char *pre_str, const char *fmt, ...) __attribute__ ((\n\t\t\t\t\t\t__format__(printf, 2, 3)));\nvoid dynapro_error(Disk *dsk, const char *fmt, ...) __attribute__ ((\n\t\t\t\t\t\t__format__(printf, 2, 3)));\n#endif\n\n/* xdriver.c and macdriver.c and windriver.c */\nint win_nonblock_read_stdin(int fd, char *bufptr, int len);\n\n/* special scc_unixdriver.c prototypes */\nvoid scc_serial_unix_open(int port);\nvoid scc_serial_unix_close(int port);\nvoid scc_serial_unix_change_params(int port);\nvoid scc_serial_unix_fill_readbuf(dword64 dfcyc, int port, int space_left);\nvoid scc_serial_unix_empty_writebuf(int port);\n\n/* special scc_windriver.c prototypes */\nvoid scc_serial_win_open(int port);\nvoid scc_serial_win_close(int port);\nvoid scc_serial_win_change_params(int port);\nvoid scc_serial_win_fill_readbuf(dword64 dfcyc, int port, int space_left);\nvoid scc_serial_win_empty_writebuf(int port);\n\n/* special joystick_driver.c prototypes */\nvoid joystick_init(void);\nvoid joystick_update(dword64 dfcyc);\nvoid joystick_update_buttons(void);\n\n/* END_HDR */\n\n/* adb.c */\nint adb_get_hide_warp_info(Kimage *kimage_ptr, int *warpptr);\nint adb_get_copy_requested(void);\nvoid adb_nonmain_check(void);\nvoid adb_init(void);\nvoid adb_reset(void);\nvoid adb_log(word32 addr, int val);\nvoid show_adb_log(void);\nvoid adb_error(void);\nvoid adb_add_kbd_srq(void);\nvoid adb_clear_kbd_srq(void);\nvoid adb_add_data_int(void);\nvoid adb_add_mouse_int(void);\nvoid adb_clear_data_int(void);\nvoid adb_clear_mouse_int(void);\nvoid adb_send_bytes(int num_bytes, word32 val0, word32 val1, word32 val2);\nvoid adb_send_1byte(word32 val);\nvoid adb_response_packet(int num_bytes, word32 val);\nvoid adb_kbd_reg0_data(int a2code, int is_up);\nvoid adb_kbd_talk_reg0(void);\nvoid adb_set_config(word32 val0, word32 val1, word32 val2);\nvoid adb_set_new_mode(word32 val);\nint adb_read_c026(void);\nvoid adb_write_c026(int val);\nvoid do_adb_cmd(void);\nint adb_read_c027(void);\nvoid adb_write_c027(int val);\nint read_adb_ram(word32 addr);\nvoid write_adb_ram(word32 addr, int val);\nint adb_get_keypad_xy(int get_y);\nint adb_update_mouse(Kimage *kimage_ptr, int x, int y, int button_states, int buttons_valid);\nint mouse_read_c024(dword64 dfcyc);\nvoid mouse_compress_fifo(dword64 dfcyc);\nvoid adb_paste_update_state(void);\nint adb_paste_add_buf(word32 key);\nvoid adb_key_event(int a2code, int is_up);\nword32 adb_read_c000(void);\nword32 adb_access_c010(void);\nword32 adb_read_c025(void);\nint adb_is_cmd_key_down(void);\nint adb_is_option_key_down(void);\nvoid adb_increment_speed(void);\nvoid adb_update_c025_mask(Kimage *kimage_ptr, word32 new_c025_val, word32 mask);\nint adb_ascii_to_a2code(int unicode_c, int a2code, int *shift_down_ptr);\nvoid adb_physical_key_update(Kimage *kimage_ptr, int raw_a2code, word32 unicode_c, int is_up);\nvoid adb_maybe_virtual_key_update(int a2code, int is_up);\nvoid adb_virtual_key_update(int a2code, int is_up);\nvoid adb_kbd_repeat_off(void);\nvoid adb_mainwin_focus(int has_focus);\n\n\n\n/* engine_c.c */\nword32 get_memory8_io_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, dword64 dplus_x_m1);\nword32 get_memory16_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, Fplus *fplus_ptr, int in_bank);\nword32 get_memory24_pieces_stub(word32 addr, byte *stat, dword64 *dcycs_ptr, Fplus *fplus_ptr, int in_bank);\nvoid set_memory8_io_stub(word32 addr, word32 val, byte *stat, dword64 *dcycs_ptr, dword64 dplus_x_m1);\nvoid set_memory16_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr, dword64 dplus_1, dword64 dplus_x_m1, int in_bank);\nvoid set_memory24_pieces_stub(word32 addr, word32 val, dword64 *dcycs_ptr, Fplus *fplus_ptr, int in_bank);\nword32 get_memory_c(word32 addr);\nword32 get_memory16_c(word32 addr);\nword32 get_memory24_c(word32 addr);\nvoid set_memory_c(word32 addr, word32 val, int do_log);\nvoid set_memory16_c(word32 addr, word32 val, int do_log);\nvoid set_memory24_c(word32 addr, word32 val);\nword32 do_adc_sbc8(word32 in1, word32 in2, word32 psr, int sub);\nword32 do_adc_sbc16(word32 in1, word32 in2, word32 psr, int sub);\nvoid fixed_memory_ptrs_init(void);\nword32 get_itimer(void);\nvoid engine_recalc_events(void);\nvoid set_halt_act(int val);\nvoid clr_halt_act(void);\nword32 get_remaining_operands(word32 addr, word32 opcode, word32 psr, dword64 *dcyc_ptr, Fplus *fplus_ptr);\nint enter_engine(Engine_reg *engine_ptr);\n\n\n\n/* clock.c */\ndouble get_dtime(void);\nint micro_sleep(double dtime);\nvoid clk_bram_zero(void);\nvoid clk_bram_set(int bram_num, int offset, int val);\nvoid clk_setup_bram_version(void);\nvoid clk_write_bram(FILE *fconf);\nvoid update_cur_time(void);\nvoid clock_update(void);\nvoid clock_update_if_needed(void);\nvoid clock_write_c034(word32 val);\nvoid do_clock_data(void);\n\n\n\n/* compile_time.c */\n\n\n\n/* config.c */\nint config_add_argv_override(const char *str1, const char *str2);\nvoid config_set_config_kegs_name(const char *str1);\nvoid config_init_menus(Cfg_menu *menuptr);\nvoid config_init(void);\nvoid cfg_find_config_kegs_file(void);\nint config_setup_kegs_file(char *outname, int maxlen, const char **name_ptr);\nint config_expand_path(char *out_ptr, const char *in_ptr, int maxlen);\nchar *cfg_exit(int get_status);\nvoid cfg_err_vprintf(const char *pre_str, const char *fmt, va_list ap);\nvoid cfg_err_printf(const char *pre_str, const char *fmt, ...);\nvoid cfg_toggle_config_panel(void);\nvoid cfg_set_config_panel(int panel);\nchar *cfg_text_screen_dump(int get_status);\nchar *cfg_text_screen_str(void);\nchar *cfg_get_serial0_status(int get_status);\nchar *cfg_get_serial1_status(int get_status);\nchar *cfg_get_current_copy_selection(void);\nvoid config_vbl_update(int doit_3_persec);\nvoid cfg_file_update_rom(const char *str);\nvoid cfg_file_update_ptr(char **strptr, const char *str, int need_update);\nvoid cfg_int_update(int *iptr, int new_val);\nvoid cfg_load_charrom(void);\nvoid config_load_roms(void);\nvoid config_parse_config_kegs_file(void);\nvoid cfg_parse_one_line(char *buf, int line);\nvoid cfg_parse_bram(char *buf, int pos, int len);\nvoid cfg_parse_sxdx(char *buf, int pos, int len);\nvoid config_generate_config_kegs_name(char *outstr, int maxlen, Disk *dsk, int with_extras);\nchar *config_write_config_kegs_file(int get_status);\nvoid insert_disk(int slot, int drive, const char *name, int ejected, const char *partition_name, int part_num, word32 dynamic_blocks);\ndword64 cfg_detect_dc42(byte *bptr, dword64 dsize, dword64 exp_dsize, int slot);\ndword64 cfg_get_fd_size(int fd);\ndword64 cfg_read_from_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize);\ndword64 cfg_write_to_fd(int fd, byte *bufptr, dword64 dpos, dword64 dsize);\nint cfg_partition_maybe_add_dotdot(void);\nint cfg_partition_name_check(const byte *name_ptr, int name_len);\nint cfg_partition_read_block(int fd, void *buf, dword64 blk, int blk_size);\nint cfg_partition_find_by_name_or_num(Disk *dsk, const char *in_partnamestr, int part_num);\nint cfg_partition_make_list_from_name(const char *namestr);\nint cfg_partition_make_list(int fd);\nint cfg_maybe_insert_disk(int slot, int drive, const char *namestr);\nvoid cfg_insert_disk_dynapro(int slot, int drive, const char *name);\nint cfg_stat(char *path, struct stat *sb, int do_lstat);\nword32 cfg_get_le16(byte *bptr);\nword32 cfg_get_le32(byte *bptr);\ndword64 cfg_get_le64(byte *bptr);\nword32 cfg_get_be_word16(word16 *ptr);\nword32 cfg_get_be_word32(word32 *ptr);\nvoid cfg_set_le32(byte *bptr, word32 val);\nvoid config_file_to_pipe(Disk *dsk, const char *cmd_ptr, const char *name_ptr);\nvoid cfg_htab_vtab(int x, int y);\nvoid cfg_home(void);\nvoid cfg_cleol(void);\nvoid cfg_putchar(int c);\nvoid cfg_printf(const char *fmt, ...);\nvoid cfg_print_dnum(dword64 dnum, int max_len);\nint cfg_get_disk_name(char *outstr, int maxlen, int type_ext, int with_extras);\nint cfg_get_disk_locked(int type_ext);\nvoid cfg_parse_menu(Cfg_menu *menuptr, int menu_pos, int highlight_pos, int change);\nvoid cfg_get_base_path(char *pathptr, const char *inptr, int go_up);\nchar *cfg_name_new_image(int get_status);\nvoid cfg_dup_existing_image(word32 slotdrive);\nvoid cfg_dup_image_selected(void);\nvoid cfg_validate_image(word32 slotdrive);\nvoid cfg_toggle_lock_disk(word32 slotdrive);\nint cfg_create_new_image_act(const char *str, int type, int size_blocks);\nvoid cfg_create_new_image(void);\nvoid cfg_file_init(void);\nvoid cfg_free_alldirents(Cfg_listhdr *listhdrptr);\nvoid cfg_file_add_dirent_unique(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, dword64 dsize, dword64 dimage_start, dword64 compr_dsize, int part_num);\nvoid cfg_file_add_dirent(Cfg_listhdr *listhdrptr, const char *nameptr, int is_dir, dword64 dsize, dword64 dimage_start, dword64 compr_dsize, int part_num);\nint cfg_dirent_sortfn(const void *obj1, const void *obj2);\nint cfg_str_match(const char *str1, const char *str2, int len);\nint cfg_str_match_maybecase(const char *str1, const char *str2, int len, int ignorecase);\nint cfgcasecmp(const char *str1, const char *str2);\nint cfg_strlcat(char *dstptr, const char *srcptr, int dstsize);\nchar *cfg_strncpy(char *dstptr, const char *srcptr, int dstsize);\nconst char *cfg_str_basename(const char *str);\nchar *cfg_strncpy_dirname(char *dstptr, const char *srcptr, int dstsize);\nvoid cfg_file_readdir(const char *pathptr);\nchar *cfg_shorten_filename(const char *in_ptr, int maxlen);\nvoid cfg_fix_topent(Cfg_listhdr *listhdrptr);\nvoid cfg_file_draw(void);\nvoid cfg_partition_select_all(void);\nvoid cfg_partition_selected(void);\nvoid cfg_file_selected(void);\nvoid cfg_file_handle_key(int key);\nvoid cfg_draw_menu(void);\nvoid cfg_newdisk_pick_menu(word32 slotdrive);\nint cfg_control_panel_update(void);\nvoid cfg_edit_mode_key(int key);\nint cfg_control_panel_update1(void);\n\n\n\n/* debugger.c */\nvoid debugger_init(void);\nvoid check_breakpoints(word32 addr, dword64 dfcyc, word32 maybe_stack, word32 type);\nvoid debug_hit_bp(word32 addr, dword64 dfcyc, word32 maybe_stack, word32 type, int pos);\nint debugger_run_16ms(void);\nvoid dbg_log_info(dword64 dfcyc, word32 info1, word32 info2, word32 type);\nvoid debugger_update_list_kpc(void);\nvoid debugger_key_event(Kimage *kimage_ptr, int a2code, int is_up);\nvoid debugger_page_updown(int isup);\nvoid debugger_redraw_screen(Kimage *kimage_ptr);\nvoid debug_draw_debug_line(Kimage *kimage_ptr, int line, int vid_line);\nvoid debugger_help(void);\nvoid dbg_help_show_strs(int help_depth, const char *str, const char *help_str);\nconst char *debug_find_cmd_in_table(const char *line_ptr, Dbg_longcmd *longptr, int help_depth);\nvoid do_debug_cmd(const char *in_str);\nword32 dis_get_memory_ptr(word32 addr);\nvoid show_one_toolset(FILE *toolfile, int toolnum, word32 addr);\nvoid show_toolset_tables(word32 a2bank, word32 addr);\nword32 debug_getnum(const char **str_ptr);\nchar *debug_get_filename(const char **str_ptr);\nvoid debug_help(const char *str);\nvoid debug_bp(const char *str);\nvoid debug_bp_set(const char *str);\nvoid debug_bp_clear(const char *str);\nvoid debug_bp_clear_all(const char *str);\nvoid debug_bp_setclr(const char *str, int is_set_clear);\nvoid debug_soundfile(const char *cmd_str);\nvoid debug_logpc(const char *str);\nvoid debug_logpc_on(const char *str);\nvoid debug_logpc_off(const char *str);\nvoid debug_logpc_out_data(FILE *pcfile, Data_log *log_data_ptr, dword64 start_dcyc);\nData_log *debug_show_data_info(FILE *pcfile, Data_log *log_data_ptr, dword64 base_dcyc, dword64 dfcyc, dword64 start_dcyc, int *data_wrap_ptr, int *count_ptr);\nvoid debug_logpc_save(const char *cmd_str);\nvoid set_bp(word32 addr, word32 end_addr, word32 acc_type);\nvoid show_bp(void);\nvoid delete_bp(word32 addr, word32 end_addr);\nvoid debug_iwm(const char *str);\nvoid debug_iwm_check(const char *str);\nint do_blank(int mode, int old_mode);\nvoid do_go(void);\nvoid do_step(void);\nvoid xam_mem(int count);\nvoid show_hex_mem(word32 startbank, word32 start, word32 end, int count);\nvoid do_debug_list(void);\nvoid dis_do_memmove(void);\nvoid dis_do_pattern_search(void);\nvoid dis_do_compare(void);\nconst char *do_debug_unix(const char *str, int old_mode);\nvoid do_debug_load(void);\nchar *do_dis(word32 kpc, int accsize, int xsize, int op_provided, word32 instr, int *size_ptr);\nint debug_get_view_line(int back);\nint debug_add_output_line(char *in_str);\nvoid debug_add_output_string(char *in_str, int len);\nvoid debug_add_output_chars(char *str);\nint dbg_printf(const char *fmt, ...);\nint dbg_vprintf(const char *fmt, va_list args);\nvoid halt_printf(const char *fmt, ...);\nvoid halt2_printf(const char *fmt, ...);\n\n\n\n/* scc.c */\nvoid scc_init(void);\nvoid scc_reset(void);\nvoid scc_hard_reset_port(int port);\nvoid scc_reset_port(int port);\nvoid scc_regen_clocks(int port);\nvoid scc_port_close(int port);\nvoid scc_port_open(dword64 dfcyc, int port);\nint scc_is_port_closed(dword64 dfcyc, int port);\nchar *scc_get_serial_status(int get_status, int port);\nvoid scc_config_changed(int port, int cfg_changed, int remote_changed, int serial_dev_changed);\nvoid scc_update(dword64 dfcyc);\nvoid scc_try_to_empty_writebuf(dword64 dfcyc, int port);\nvoid scc_try_fill_readbuf(dword64 dfcyc, int port);\nvoid scc_do_event(dword64 dfcyc, int type);\nvoid show_scc_state(void);\nword32 scc_read_reg(dword64 dfcyc, int port);\nvoid scc_write_reg(dword64 dfcyc, int port, word32 val);\nword32 scc_read_data(dword64 dfcyc, int port);\nvoid scc_write_data(dword64 dfcyc, int port, word32 val);\nword32 scc_do_read_rr2b(void);\nvoid scc_maybe_br_event(dword64 dfcyc, int port);\nvoid scc_evaluate_ints(int port);\nvoid scc_maybe_rx_event(dword64 dfcyc, int port);\nvoid scc_maybe_rx_int(int port);\nvoid scc_clr_rx_int(int port);\nvoid scc_handle_tx_event(int port);\nvoid scc_maybe_tx_event(dword64 dfcyc, int port);\nvoid scc_clr_tx_int(int port);\nvoid scc_set_zerocnt_int(int port);\nvoid scc_clr_zerocnt_int(int port);\nvoid scc_add_to_readbuf(dword64 dfcyc, int port, word32 val);\nvoid scc_add_to_readbufv(dword64 dfcyc, int port, const char *fmt, ...);\nvoid scc_transmit(dword64 dfcyc, int port, word32 val);\nvoid scc_add_to_writebuf(dword64 dfcyc, int port, word32 val);\n\n\n\n/* scc_socket_driver.c */\nvoid scc_socket_open(dword64 dfcyc, int port, int cfg);\nvoid scc_socket_close(int port);\nvoid scc_socket_close_extended(dword64 dfcyc, int port, int allow_retry);\nvoid scc_socket_maybe_open(dword64 dfcyc, int port, int must);\nvoid scc_socket_open_incoming(dword64 dfcyc, int port);\nvoid scc_socket_open_outgoing(dword64 dfcyc, int port, const char *remote_ip_str, int remote_port);\nvoid scc_socket_make_nonblock(dword64 dfcyc, int port);\nvoid scc_accept_socket(dword64 dfcyc, int port);\nvoid scc_socket_telnet_reqs(dword64 dfcyc, int port);\nvoid scc_socket_fill_readbuf(dword64 dfcyc, int port, int space_left);\nvoid scc_socket_recvd_char(dword64 dfcyc, int port, int c);\nvoid scc_socket_empty_writebuf(dword64 dfcyc, int port);\nvoid scc_socket_modem_write(dword64 dfcyc, int port, int c);\nvoid scc_socket_do_cmd_str(dword64 dfcyc, int port);\nvoid scc_socket_send_modem_code(dword64 dfcyc, int port, int code);\nvoid scc_socket_modem_connect(dword64 dfcyc, int port);\nvoid scc_socket_modem_do_ring(dword64 dfcyc, int port);\nvoid scc_socket_do_answer(dword64 dfcyc, int port);\n\n\n\n/* scc_windriver.c */\n\n\n\n/* scc_unixdriver.c */\n\n\n\n/* iwm.c */\nvoid iwm_init_drive(Disk *dsk, int smartport, int drive, int disk_525);\nvoid iwm_init(void);\nvoid iwm_reset(void);\nvoid disk_set_num_tracks(Disk *dsk, int num_tracks);\nword32 iwm_get_default_track_bits(Disk *dsk, word32 qtr_trk);\nvoid draw_iwm_status(int line, char *buf);\nvoid iwm_flush_cur_disk(void);\nvoid iwm_flush_disk_to_unix(Disk *dsk);\nvoid iwm_vbl_update(void);\nvoid iwm_update_fast_disk_emul(int fast_disk_emul_en);\nvoid iwm_show_stats(int slot_drive);\nDisk *iwm_get_dsk(word32 state);\nDisk *iwm_touch_switches(int loc, dword64 dfcyc);\nvoid iwm_move_to_ftrack(Disk *dsk, word32 new_frac_track, int delta, dword64 dfcyc);\nvoid iwm_move_to_qtr_track(Disk *dsk, word32 qtr_track);\nvoid iwm525_update_phases(Disk *dsk, dword64 dfcyc);\nvoid iwm525_update_head(Disk *dsk, dword64 dfcyc);\nint iwm_read_status35(dword64 dfcyc);\nvoid iwm_do_action35(dword64 dfcyc);\nint read_iwm(int loc, dword64 dfcyc);\nvoid write_iwm(int loc, int val, dword64 dfcyc);\nint iwm_read_enable2(dword64 dfcyc);\nint iwm_read_enable2_handshake(dword64 dfcyc);\nvoid iwm_write_enable2(int val);\nword32 iwm_fastemul_start_write(Disk *dsk, dword64 dfcyc);\nword32 iwm_read_data_fast(Disk *dsk, dword64 dfcyc);\ndword64 iwm_return_rand_data(Disk *dsk, dword64 dfcyc);\ndword64 iwm_get_raw_bits(Disk *dsk, word32 bit_pos, int num_bits, dword64 *dsyncs_ptr);\nword32 iwm_calc_bit_diff(word32 first, word32 last, word32 track_bits);\nword32 iwm_calc_bit_sum(word32 bit_pos, int add_ival, word32 track_bits);\ndword64 iwm_calc_forced_sync(dword64 dval, int forced_bit);\nint iwm_calc_forced_sync_0s(dword64 sync_dval, int bits);\nword32 iwm_read_data(Disk *dsk, dword64 dfcyc);\nvoid iwm_write_data(Disk *dsk, word32 val, dword64 dfcyc);\nvoid iwm_start_write(Disk *dsk, word32 bit_pos, word32 val, int no_prior);\nint iwm_start_write_act(Disk *dsk, word32 bit_pos, int num, int no_prior, int delta);\nvoid iwm_write_data35(Disk *dsk, word32 val, dword64 dfcyc);\nvoid iwm_write_end(Disk *dsk, int write_through_now, dword64 dfcyc);\nvoid iwm_write_one_nib(Disk *dsk, int bits, dword64 dfcyc);\nvoid iwm_recalc_sync_from(Disk *dsk, word32 qtr_track, word32 bit_pos, dword64 dfcyc);\nvoid sector_to_partial_nib(byte *in, byte *nib_ptr);\nint disk_unnib_4x4(Disk *dsk);\nint iwm_denib_track525(Disk *dsk, word32 qtr_track, byte *outbuf);\nint iwm_denib_track35(Disk *dsk, word32 qtr_track, byte *outbuf);\nint iwm_track_to_unix(Disk *dsk, word32 qtr_track, byte *outbuf);\nvoid show_hex_data(byte *buf, int count);\nvoid iwm_check_nibblization(dword64 dfcyc);\nvoid disk_check_nibblization(Disk *dsk, byte *in_buf, int size, dword64 dfcyc);\nvoid disk_unix_to_nib(Disk *dsk, int qtr_track, dword64 dunix_pos, word32 unix_len, int len_bits, dword64 dfcyc);\nvoid iwm_nibblize_track_nib525(Disk *dsk, byte *track_buf, int qtr_track, word32 unix_len);\nvoid iwm_nibblize_track_525(Disk *dsk, byte *track_buf, int qtr_track, dword64 dfcyc);\nvoid iwm_nibblize_track_35(Disk *dsk, byte *track_buf, int qtr_track, word32 unix_len, dword64 dfcyc);\nvoid disk_4x4_nib_out(Disk *dsk, word32 val);\nvoid disk_nib_out(Disk *dsk, word32 val, int size);\nvoid disk_nib_end_track(Disk *dsk, dword64 dfcyc);\nword32 disk_nib_out_raw(Disk *dsk, word32 qtr_track, word32 val, int bits, word32 bit_pos, dword64 dfcyc);\nDisk *iwm_get_dsk_from_slot_drive(int slot, int drive);\nvoid iwm_toggle_lock(Disk *dsk);\nvoid iwm_eject_named_disk(int slot, int drive, const char *name, const char *partition_name);\nvoid iwm_eject_disk_by_num(int slot, int drive);\nvoid iwm_eject_disk(Disk *dsk);\nvoid iwm_show_track(int slot_drive, int track, dword64 dfcyc);\nvoid iwm_show_a_track(Disk *dsk, Trk *trk, dword64 dfcyc);\nvoid dummy1(word32 psr);\nvoid dummy2(word32 psr);\n\n\n\n/* joystick_driver.c */\nvoid joystick_callback_init(int native_type);\nvoid joystick_callback_update(word32 buttons, int paddle_x, int paddle_y);\n\n\n\n/* moremem.c */\nvoid fixup_brks(void);\nvoid fixup_hires_on(void);\nvoid fixup_bank0_2000_4000(void);\nvoid fixup_bank0_0400_0800(void);\nvoid fixup_any_bank_any_page(word32 start_page, int num_pages, byte *mem0rd, byte *mem0wr);\nvoid fixup_intcx(void);\nvoid fixup_st80col(dword64 dfcyc);\nvoid fixup_altzp(void);\nvoid fixup_page2(dword64 dfcyc);\nvoid fixup_ramrd(void);\nvoid fixup_ramwrt(void);\nvoid fixup_lc(void);\nvoid set_statereg(dword64 dfcyc, word32 val);\nvoid fixup_shadow_txt1(void);\nvoid fixup_shadow_txt2(void);\nvoid fixup_shadow_hires1(void);\nvoid fixup_shadow_hires2(void);\nvoid fixup_shadow_shr(void);\nvoid fixup_shadow_iolc(void);\nvoid update_shadow_reg(dword64 dfcyc, word32 val);\nvoid fixup_shadow_all_banks(void);\nvoid setup_pageinfo(void);\nvoid show_bankptrs_bank0rdwr(void);\nvoid show_bankptrs(int bnk);\nvoid show_addr(byte *ptr);\nword32 moremem_fix_vector_pull(word32 addr);\nword32 io_read(word32 loc, dword64 *cyc_ptr);\nvoid io_write(word32 loc, word32 val, dword64 *cyc_ptr);\nword32 slinky_devsel_read(dword64 dfcyc, word32 loc);\nvoid slinky_devsel_write(dword64 dfcyc, word32 loc, word32 val);\nword32 c3xx_read(dword64 dfcyc, word32 loc);\nword32 get_lines_since_vbl(dword64 dfcyc);\nint in_vblank(dword64 dfcyc);\nint read_vid_counters(int loc, dword64 dfcyc);\n\n\n\n/* paddles.c */\nvoid paddle_fixup_joystick_type(void);\nvoid paddle_trigger(dword64 dfcyc);\nvoid paddle_trigger_mouse(dword64 dfcyc);\nvoid paddle_trigger_keypad(dword64 dfcyc);\nvoid paddle_update_trigger_dcycs(dword64 dfcyc);\nint read_paddles(dword64 dfcyc, int paddle);\nvoid paddle_update_buttons(void);\n\n\n\n/* mockingboard.c */\nvoid mock_ay8913_reset(int pair_num, dword64 dfcyc);\nvoid mockingboard_reset(dword64 dfcyc);\nvoid mock_show_pair(int pair_num, dword64 dfcyc, const char *str);\nvoid mock_update_timers(int doit, dword64 dfcyc);\nvoid mockingboard_event(dword64 dfcyc);\nword32 mockingboard_read(word32 loc, dword64 dfcyc);\nvoid mockingboard_write(word32 loc, word32 val, dword64 dfcyc);\nword32 mock_6522_read(int pair_num, word32 loc, dword64 dfcyc);\nvoid mock_6522_write(int pair_num, word32 loc, word32 val, dword64 dfcyc);\nword32 mock_6522_new_ifr(dword64 dfcyc, int pair_num, word32 ifr, word32 ier);\nvoid mock_ay8913_reg_read(int pair_num);\nvoid mock_ay8913_reg_write(int pair_num, dword64 dfcyc);\nvoid mock_ay8913_control_update(int pair_num, word32 new_val, word32 prev_val, dword64 dfcyc);\nvoid mockingboard_show(int got_num, word32 disable_mask);\n\n\n\n/* sim65816.c */\nint sim_get_force_depth(void);\nint sim_get_use_shmem(void);\nvoid sim_set_use_shmem(int use_shmem);\nword32 toolbox_debug_4byte(word32 addr);\nvoid toolbox_debug_c(word32 xreg, word32 stack, dword64 *dcyc_ptr);\nvoid show_toolbox_log(void);\nword32 get_memory_io(word32 loc, dword64 *dcyc_ptr);\nvoid set_memory_io(word32 loc, int val, dword64 *dcyc_ptr);\nvoid show_regs_act(Engine_reg *eptr);\nvoid show_regs(void);\nvoid my_exit(int ret);\nvoid do_reset(void);\nbyte *memalloc_align(int size, int skip_amt, void **alloc_ptr);\nvoid memory_ptr_init(void);\nint parse_argv(int argc, char **argv, int slashes_to_find);\nint kegs_init(int mdepth, int screen_width, int screen_height, int no_scale_window);\nvoid load_roms_init_memory(void);\nvoid initialize_events(void);\nvoid check_for_one_event_type(int type, word32 mask);\nvoid add_event_entry(dword64 dfcyc, int type);\ndword64 remove_event_entry(int type, word32 mask);\nvoid add_event_stop(dword64 dfcyc);\nvoid add_event_doc(dword64 dfcyc, int osc);\nvoid add_event_scc(dword64 dfcyc, int type);\nvoid add_event_vbl(void);\nvoid add_event_vid_upd(int line);\nvoid add_event_mockingboard(dword64 dfcyc);\nvoid add_event_scan_int(dword64 dfcyc, int line);\ndword64 remove_event_doc(int osc);\ndword64 remove_event_scc(int type);\nvoid remove_event_mockingboard(void);\nvoid show_all_events(void);\nvoid show_pmhz(void);\nvoid setup_zip_speeds(void);\nint run_16ms(void);\nint run_a2_one_vbl(void);\nvoid add_irq(word32 irq_mask);\nvoid remove_irq(word32 irq_mask);\nvoid take_irq(void);\nvoid show_dtime_array(void);\nvoid update_60hz(dword64 dfcyc, double dtime_now);\nvoid do_vbl_int(void);\nvoid do_scan_int(dword64 dfcyc, int line);\nvoid check_scan_line_int(int cur_video_line);\nvoid check_for_new_scan_int(dword64 dfcyc);\nvoid scb_changed(dword64 dfcyc, word32 addr, word32 new_val, word32 old_val);\nvoid init_reg(void);\nvoid handle_action(word32 ret);\nvoid do_break(word32 ret);\nvoid do_cop(word32 ret);\nvoid do_wdm(word32 arg);\nvoid do_wai(void);\nvoid do_stp(void);\nvoid do_wdm_emulator_id(void);\nvoid size_fail(int val, word32 v1, word32 v2);\nint fatal_printf(const char *fmt, ...);\nint kegs_vprintf(const char *fmt, va_list ap);\ndword64 must_write(int fd, byte *bufptr, dword64 dsize);\nvoid clear_fatal_logs(void);\nchar *kegs_malloc_str(const char *in_str);\ndword64 kegs_lseek(int fd, dword64 offs, int whence);\n\n\n\n/* smartport.c */\nvoid smartport_error(void);\nvoid smartport_log(word32 start_addr, word32 cmd, word32 rts_addr, word32 cmd_list);\nvoid do_c70d(word32 arg0);\nvoid do_c70a(word32 arg0);\nint do_read_c7(int unit_num, word32 buf, word32 blk);\nint do_write_c7(int unit_num, word32 buf, word32 blk);\nint smartport_memory_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size);\nint do_format_c7(int unit_num);\nvoid do_c700(word32 ret);\n\n\n\n/* doc.c */\nvoid doc_init(void);\nvoid doc_reset(dword64 dfcyc);\nint doc_play(dword64 dfcyc, double last_dsamp, double dsamp_now, int num_samps, int snd_buf_init, int *outptr_start);\nvoid doc_handle_event(int osc, dword64 dfcyc);\nvoid doc_sound_end(int osc, int can_repeat, double eff_dsamps, double dsamps);\nvoid doc_add_sound_irq(int osc);\nvoid doc_remove_sound_irq(int osc, int must);\nvoid doc_start_sound2(int osc, dword64 dfcyc);\nvoid doc_start_sound(int osc, double eff_dsamps, double dsamps);\nvoid doc_wave_end_estimate2(int osc, dword64 dfcyc);\nvoid doc_wave_end_estimate(int osc, double eff_dsamps, double dsamps);\nvoid doc_remove_sound_event(int osc);\nvoid doc_write_ctl_reg(dword64 dfcyc, int osc, int val);\nvoid doc_recalc_sound_parms(dword64 dfcyc, int osc);\nint doc_read_c03c(void);\nint doc_read_c03d(dword64 dfcyc);\nvoid doc_write_c03c(dword64 dfcyc, word32 val);\nvoid doc_write_c03d(dword64 dfcyc, word32 val);\nvoid doc_show_ensoniq_state(void);\n\n\n\n/* sound.c */\nvoid sound_init(void);\nvoid sound_set_audio_rate(int rate);\nvoid sound_reset(dword64 dfcyc);\nvoid sound_shutdown(void);\nvoid sound_update(dword64 dfcyc);\nvoid sound_file_start(char *filename);\nvoid sound_file_open(void);\nvoid sound_file_close(void);\nvoid send_sound_to_file(word32 *wptr, int shm_pos, int num_samps, int real_samps);\nvoid show_c030_state(dword64 dfcyc);\nvoid show_c030_samps(dword64 dfcyc, int *outptr, int num);\nint sound_play_c030(dword64 dfcyc, dword64 dsamp, int *outptr_start, int num_samps);\nvoid sound_play(dword64 dfcyc);\nvoid sound_mock_envelope(int pair, int *env_ptr, int num_samps, int *vol_ptr);\nvoid sound_mock_noise(int pair, byte *noise_ptr, int num_samps);\nvoid sound_mock_play(int pair, int channel, int *outptr, int *env_ptr, byte *noise_ptr, int *vol_ptr, int num_samps);\nword32 sound_read_c030(dword64 dfcyc);\nvoid sound_write_c030(dword64 dfcyc);\n\n\n\n/* sound_driver.c */\nvoid snddrv_init(void);\nvoid sound_child_fork(int size);\nvoid parent_sound_get_sample_rate(int read_fd);\nvoid snddrv_shutdown(void);\nvoid snddrv_send_sound(int real_samps, int size);\nvoid child_sound_playit(word32 tmp);\nvoid reliable_buf_write(word32 *shm_addr, int pos, int size);\nvoid reliable_zero_write(int amt);\nint child_send_samples(byte *ptr, int size);\nvoid child_sound_loop(int read_fd, int write_fd, word32 *shm_addr);\n\n\n\n/* woz.c */\nvoid woz_crc_init(void);\nword32 woz_calc_crc32(byte *bptr, dword64 dlen, word32 bytes_to_skip);\nvoid woz_rewrite_crc(Disk *dsk, int min_write_size);\nvoid woz_rewrite_lock(Disk *dsk);\nvoid woz_check_file(Disk *dsk);\nvoid woz_parse_meta(Disk *dsk, int offset, int size);\nvoid woz_parse_info(Disk *dsk, int offset, int size);\nvoid woz_parse_tmap(Disk *dsk, int offset, int size);\nvoid woz_parse_trks(Disk *dsk, int offset, int size);\nint woz_add_track(Disk *dsk, int qtr_track, word32 tmap, dword64 dfcyc);\nint woz_parse_header(Disk *dsk);\nWoz_info *woz_malloc(byte *wozptr, word32 woz_size);\nint woz_reopen(Disk *dsk, dword64 dfcyc);\nint woz_open(Disk *dsk, dword64 dfcyc);\nbyte *woz_append_bytes(byte *wozptr, byte *in_bptr, int len);\nbyte *woz_append_word32(byte *wozptr, word32 val);\nint woz_append_chunk(Woz_info *wozinfo_ptr, word32 chunk_id, word32 length, byte *bptr);\nbyte *woz_append_a_trk(Woz_info *wozinfo_ptr, Disk *dsk, int trk_num, byte *bptr, word32 *num_blocks_ptr, dword64 *tmap_dptr);\nWoz_info *woz_new_from_woz(Disk *dsk, int disk_525);\nint woz_new(int fd, const char *str, int size_kb);\nvoid woz_maybe_reparse(Disk *dsk);\nvoid woz_set_reparse(Disk *dsk);\nvoid woz_reparse_woz(Disk *dsk);\nvoid woz_remove_a_track(Disk *dsk, word32 qtr_track);\nword32 woz_add_a_track(Disk *dsk, word32 qtr_track);\n\n\n\n/* unshk.c */\nword32 unshk_get_long4(byte *bptr);\nword32 unshk_get_word2(byte *bptr);\nword32 unshk_calc_crc(byte *bptr, int size, word32 start_crc);\nint unshk_unrle(byte *cptr, int len, word32 rle_delim, byte *ucptr);\nvoid unshk_lzw_clear(Lzw_state *lzw_ptr);\nbyte *unshk_unlzw(byte *cptr, Lzw_state *lzw_ptr, byte *ucptr, word32 uclen);\nvoid unshk_data(Disk *dsk, byte *cptr, word32 compr_size, byte *ucptr, word32 uncompr_size, word32 thread_format, byte *base_cptr);\nvoid unshk_parse_header(Disk *dsk, byte *cptr, int compr_size, byte *base_cptr);\nvoid unshk(Disk *dsk, const char *name_str);\nvoid unshk_dsk_raw_data(Disk *dsk);\n\n\n\n/* undeflate.c */\nvoid *undeflate_realloc(void *ptr, dword64 dsize);\nvoid *undeflate_malloc(dword64 dsize);\nvoid show_bits(unsigned *llptr, int nl);\nvoid show_huftb(unsigned *tabptr, int bits);\nvoid undeflate_init_len_dist_tab(word32 *tabptr, dword64 drepeats, word32 start);\nvoid undeflate_init_bit_rev_tab(word32 *tabptr, int num);\nword32 undeflate_bit_reverse(word32 val, word32 bits);\nword32 undeflate_calc_crc32(byte *bptr, word32 len);\nbyte *undeflate_ensure_dest_len(Disk *dsk, byte *ucptr, word32 len);\nvoid undeflate_add_tab_code(word32 *tabptr, word32 tabsz_lg2, word32 code, word32 entry);\nword32 *undeflate_init_fixed_tabs(void);\nword32 *undeflate_init_tables(void);\nvoid undeflate_free_tables(void);\nvoid undeflate_check_bit_reverse(void);\nword32 *undeflate_build_huff_tab(word32 *tabptr, word32 *entry_ptr, word32 len_size, word32 *bl_count_ptr, int max_bits);\nword32 *undeflate_dynamic_table(byte *cptr, word32 *bit_pos_ptr, byte *cptr_base);\nbyte *undeflate_block(Disk *dsk, byte *cptr, word32 *bit_pos_ptr, byte *cptr_base, byte *cptr_end);\nbyte *undeflate_gzip_header(Disk *dsk, byte *cptr, word32 compr_size);\nvoid undeflate_gzip(Disk *dsk, const char *name_str);\nbyte *undeflate_zipfile_blocks(Disk *dsk, byte *cptr, dword64 dcompr_size);\nint undeflate_zipfile(Disk *dsk, int fd, dword64 dlocal_header_off, dword64 uncompr_dsize, dword64 compr_dsize);\nint undeflate_zipfile_search(byte *bptr, byte *cmp_ptr, int size, int cmp_len, int min_size);\nint undeflate_zipfile_make_list(int fd);\n\n\n\n/* dynapro.c */\nword32 dynapro_get_word32(byte *bptr);\nword32 dynapro_get_word24(byte *bptr);\nword32 dynapro_get_word16(byte *bptr);\nvoid dynapro_set_word24(byte *bptr, word32 val);\nvoid dynapro_set_word32(byte *bptr, word32 val);\nvoid dynapro_set_word16(byte *bptr, word32 val);\nvoid dynapro_error(Disk *dsk, const char *fmt, ...);\nDynapro_file *dynapro_alloc_file(void);\nvoid dynapro_free_file(Dynapro_file *fileptr, int check_map);\nvoid dynapro_free_recursive_file(Dynapro_file *fileptr, int check_map);\nvoid dynapro_free_dynapro_info(Disk *dsk);\nword32 dynapro_find_free_block_internal(Disk *dsk);\nword32 dynapro_find_free_block(Disk *dsk);\nbyte *dynapro_malloc_file(char *path_ptr, dword64 *dsize_ptr, int extra_size);\nvoid dynapro_join_path_and_file(char *outstr, const char *unix_path, const char *str, int path_max);\nword32 dynapro_fill_fileptr_from_prodos(Disk *dsk, Dynapro_file *fileptr, char *buf32_ptr, word32 dir_byte);\nword32 dynapro_diff_fileptrs(Dynapro_file *oldfileptr, Dynapro_file *newfileptr);\nword32 dynapro_do_one_dir_entry(Disk *dsk, Dynapro_file *fileptr, Dynapro_file *localfile_ptr, char *buf32_ptr, word32 dir_byte);\nvoid dynapro_fix_damaged_entry(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_try_fix_damage(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_try_fix_damaged_disk(Disk *dsk);\nvoid dynapro_new_unix_path(Dynapro_file *fileptr, const char *path_str, const char *name_str);\nDynapro_file *dynapro_process_write_dir(Disk *dsk, Dynapro_file *parent_ptr, Dynapro_file **head_ptr_ptr, word32 dir_byte);\nvoid dynapro_handle_write_dir(Disk *dsk, Dynapro_file *parent_ptr, Dynapro_file *head_ptr, word32 dir_byte);\nword32 dynapro_process_write_file(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_handle_write_file(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_handle_changed_entry(Disk *dsk, Dynapro_file *fileptr);\nword32 dynapro_write_to_unix_file(const char *unix_path, byte *data_ptr, word32 size);\nvoid dynapro_unmap_file(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_unlink_file(Dynapro_file *fileptr);\nvoid dynapro_erase_free_entry(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_erase_free_dir(Disk *dsk, Dynapro_file *fileptr);\nvoid dynapro_mark_damaged(Disk *dsk, Dynapro_file *fileptr);\nint dynapro_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size);\nvoid dynapro_debug_update(Disk *dsk);\nvoid dynapro_debug_map(Disk *dsk, const char *str);\nvoid dynapro_debug_recursive_file_map(Dynapro_file *fileptr, int start);\nword32 dynapro_unix_to_prodos_time(const time_t *time_ptr);\nint dynapro_create_prodos_name(Dynapro_file *newfileptr, Dynapro_file *matchptr, word32 storage_type);\nDynapro_file *dynapro_new_unix_file(const char *path, Dynapro_file *parent_ptr, Dynapro_file *match_ptr, word32 storage_type);\nint dynapro_create_dir(Disk *dsk, char *unix_path, Dynapro_file *parent_ptr, word32 dir_byte);\nword32 dynapro_add_file_entry(Disk *dsk, Dynapro_file *fileptr, Dynapro_file *head_ptr, word32 dir_byte, word32 inc);\nword32 dynapro_fork_from_unix(Disk *dsk, byte *fptr, word32 *storage_type_ptr, word32 key_block, dword64 dsize);\nword32 dynapro_file_from_unix(Disk *dsk, Dynapro_file *fileptr);\nword32 dynapro_prep_image(Disk *dsk, const char *dir_path, word32 num_blocks);\nword32 dynapro_map_one_file_block(Disk *dsk, Dynapro_file *fileptr, word32 block_num, word32 file_offset, word32 eof);\nword32 dynapro_map_file_blocks(Disk *dsk, Dynapro_file *fileptr, word32 block_num, int level, word32 file_offset, word32 eof);\nword32 dynapro_map_file(Disk *dsk, Dynapro_file *fileptr, int do_file_data);\nword32 dynapro_map_dir_blocks(Disk *dsk, Dynapro_file *fileptr);\nword32 dynapro_build_map(Disk *dsk, Dynapro_file *fileptr);\nint dynapro_mount(Disk *dsk, char *dir_path, word32 num_blocks);\n\n\n\n/* dyna_type.c */\nword32 dynatype_scan_extensions(const char *str);\nword32 dynatype_find_prodos_type(const char *str);\nconst char *dynatype_find_file_type(word32 file_type);\nword32 dynatype_detect_file_type(Dynapro_file *fileptr, const char *path_ptr, word32 storage_type);\nint dynatype_get_extension(const char *str, char *out_ptr, int buf_len);\nint dynatype_comma_arg(const char *str, word32 *type_or_aux_ptr);\nvoid dynatype_fix_unix_name(Dynapro_file *fileptr, char *outbuf_ptr, int path_max);\n\n\n\n/* dyna_filt.c */\n\n\n\n/* dyna_validate.c */\nword32 dynapro_validate_header(Disk *dsk, Dynapro_file *fileptr, word32 dir_byte, word32 parent_dir_byte);\nvoid dynapro_validate_init_freeblks(byte *freeblks_ptr, word32 num_blocks);\nword32 dynapro_validate_freeblk(Disk *dsk, byte *freeblks_ptr, word32 block);\nword32 dynapro_validate_file(Disk *dsk, byte *freeblks_ptr, word32 block_num, word32 eof, int level_first);\nword32 dynapro_validate_forked_file(Disk *dsk, byte *freeblks_ptr, word32 block_num, word32 eof);\nword32 dynapro_validate_dir(Disk *dsk, byte *freeblks_ptr, word32 dir_byte, word32 parent_dir_byte, word32 exp_blocks_used);\nint dynapro_validate_disk(Disk *dsk);\nvoid dynapro_validate_any_image(Disk *dsk);\n\n\n\n/* applesingle.c */\nword32 applesingle_get_be32(const byte *bptr);\nword32 applesingle_get_be16(const byte *bptr);\nvoid applesingle_set_be32(byte *bptr, word32 val);\nvoid applesingle_set_be16(byte *bptr, word32 val);\nword32 applesingle_map_from_prodos(Disk *dsk, Dynapro_file *fileptr, int do_file_data);\nword32 applesingle_from_unix(Disk *dsk, Dynapro_file *fileptr, byte *fptr, dword64 dsize);\nword32 applesingle_make_prodos_fork(Disk *dsk, byte *fptr, byte *tptr, word32 length);\n\n\n\n/* video.c */\nvoid video_set_red_mask(word32 red_mask);\nvoid video_set_green_mask(word32 green_mask);\nvoid video_set_blue_mask(word32 blue_mask);\nvoid video_set_alpha_mask(word32 alpha_mask);\nvoid video_set_mask_and_shift(word32 x_mask, word32 *mask_ptr, int *shift_left_ptr, int *shift_right_ptr);\nvoid video_set_palette(void);\nvoid video_set_redraw_skip_amt(int amt);\nKimage *video_get_kimage(int win_id);\nchar *video_get_status_ptr(int line);\nvoid video_set_x_refresh_needed(Kimage *kimage_ptr, int do_refresh);\nint video_get_active(Kimage *kimage_ptr);\nvoid video_set_active(Kimage *kimage_ptr, int active);\nvoid video_init(int mdepth, int screen_width, int screen_height, int no_scale_window);\nvoid video_init_kimage(Kimage *kimage_ptr, int width, int height, int screen_width, int screen_height);\nvoid show_a2_line_stuff(void);\nvoid video_reset(void);\nvoid video_update(void);\nword32 video_all_stat_to_filt_stat(int line, word32 new_all_stat);\nvoid change_display_mode(dword64 dfcyc);\nvoid video_add_new_all_stat(dword64 dfcyc, word32 lines_since_vbl);\nvoid change_border_color(dword64 dfcyc, int val);\nvoid update_border_info(void);\nvoid update_border_line(int st_line_offset, int end_line_offset, int color);\nvoid video_border_pixel_write(Kimage *kimage_ptr, int starty, int num_lines, int color, int st_off, int end_off);\nword32 video_get_ch_mask(word32 mem_ptr, word32 filt_stat, int reparse);\nvoid video_update_edges(int line, int left, int right, const char *str);\nvoid redraw_changed_text(word32 line_bytes, int reparse, word32 *in_wptr, int pixels_per_line, word32 filt_stat);\nvoid redraw_changed_string(const byte *bptr, word32 line_bytes, word32 ch_mask, word32 *in_wptr, word32 bg_pixel, word32 fg_pixel, int pixels_per_line, int dbl);\nvoid redraw_changed_gr(word32 line_bytes, int reparse, word32 *in_wptr, int pixels_per_line, word32 filt_stat);\nvoid video_hgr_line_segment(byte *slow_mem_ptr, word32 *wptr, int start_byte, int end_byte, int pixels_per_line, word32 filt_stat);\nvoid redraw_changed_hgr(word32 line_bytes, int reparse, word32 *in_wptr, int pixels_per_line, word32 filt_stat);\nint video_rebuild_super_hires_palette(int bank, word32 scan_info, int line, int reparse);\nword32 redraw_changed_super_hires_oneline(int bank, word32 *in_wptr, int pixels_per_line, int y, int scan, word32 ch_mask);\nvoid redraw_changed_super_hires_bank(int bank, int start_line, int reparse, word32 *wptr, int pixels_per_line);\nvoid redraw_changed_super_hires(word32 line_bytes, int reparse, word32 *wptr, int pixels_per_line, word32 filt_stat);\nvoid video_copy_changed2(void);\nvoid video_update_event_line(int line);\nvoid video_force_reparse(void);\nvoid video_update_through_line(int line);\nvoid video_do_partial_line(word32 lines_since_vbl, int end, word32 cur_all_stat);\nvoid video_refresh_line(word32 line_bytes, int must_reparse, word32 filt_stat);\nvoid prepare_a2_font(void);\nvoid prepare_a2_romx_font(byte *font_ptr);\nvoid video_add_rect(Kimage *kimage_ptr, int x, int y, int width, int height);\nvoid video_add_a2_rect(int start_line, int end_line, int left_pix, int right_pix);\nvoid video_form_change_rects(void);\nint video_get_a2_width(Kimage *kimage_ptr);\nint video_get_x_width(Kimage *kimage_ptr);\nint video_get_a2_height(Kimage *kimage_ptr);\nint video_get_x_height(Kimage *kimage_ptr);\nint video_get_x_xpos(Kimage *kimage_ptr);\nint video_get_x_ypos(Kimage *kimage_ptr);\nvoid video_update_xpos_ypos(Kimage *kimage_ptr, int x_xpos, int x_ypos);\nint video_change_aspect_needed(Kimage *kimage_ptr, int x_width, int x_height);\nvoid video_update_status_enable(Kimage *kimage_ptr);\nint video_out_query(Kimage *kimage_ptr);\nvoid video_out_done(Kimage *kimage_ptr);\nint video_out_data(void *vptr, Kimage *kimage_ptr, int out_width_act, Change_rect *rectptr, int pos);\nint video_out_data_intscaled(void *vptr, Kimage *kimage_ptr, int out_width_act, Change_rect *rectptr);\nint video_out_data_scaled(void *vptr, Kimage *kimage_ptr, int out_width_act, Change_rect *rectptr);\nword32 video_scale_calc_frac(int pos, word32 max, word32 frac_inc, word32 frac_inc_inv);\nvoid video_update_scale(Kimage *kimage_ptr, int out_width, int out_height, int must_update);\nint video_scale_mouse_x(Kimage *kimage_ptr, int raw_x, int x_width);\nint video_scale_mouse_y(Kimage *kimage_ptr, int raw_y, int y_height);\nint video_unscale_mouse_x(Kimage *kimage_ptr, int a2_x, int x_width);\nint video_unscale_mouse_y(Kimage *kimage_ptr, int a2_y, int y_height);\nvoid video_update_color_raw(int bank, int col_num, int a2_color);\nvoid video_update_status_line(int line, const char *string);\nvoid video_draw_a2_string(int line, const byte *bptr);\nvoid video_show_debug_info(void);\nword32 read_video_data(dword64 dfcyc);\nword32 float_bus(dword64 dfcyc);\nword32 float_bus_lines(dword64 dfcyc, word32 lines_since_vbl);\n\n\n\n/* voc.c */\nword32 voc_devsel_read(word32 loc, dword64 dfcyc);\nvoid voc_devsel_write(word32 loc, word32 val, dword64 dfcyc);\nvoid voc_iosel_c300_write(word32 loc, word32 val, dword64 dfcyc);\nvoid voc_reset(void);\nword32 voc_read_reg0(dword64 dfcyc);\nvoid voc_update_interlace(dword64 dfcyc);\n\n\n"
  },
  {
    "path": "upstream/kegs/src/protos_macdriver.h",
    "content": "/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2019 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\nconst char rcsid_protos_mac_h[] = \"@(#)$KmKId: protos_macdriver.h,v 1.12 2019-12-16 02:02:53+00 kentd Exp $\";\n\n/* END_HDR */\n\n/* macdriver.c */\npascal OSStatus quit_event_handler(EventHandlerCallRef call_ref, EventRef event, void *ignore);\nvoid show_simple_alert(char *str1, char *str2, char *str3, int num);\nvoid x_dialog_create_kegs_conf(const char *str);\nint x_show_alert(int is_fatal, const char *str);\npascal OSStatus my_cmd_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata);\nvoid update_window(void);\nvoid show_event(UInt32 event_class, UInt32 event_kind, int handled);\npascal OSStatus my_win_handler(EventHandlerCallRef handlerRef, EventRef event, void *userdata);\npascal OSStatus dummy_event_handler(EventHandlerCallRef call_ref, EventRef in_event, void *ignore);\nvoid mac_update_modifiers(word32 state);\nvoid mac_warp_mouse(void);\nvoid check_input_events(void);\nvoid temp_run_application_event_loop(void);\nint main(int argc, char *argv[]);\nvoid x_update_color(int col_num, int red, int green, int blue, word32 rgb);\nvoid x_update_physical_colormap(void);\nvoid show_xcolor_array(void);\nvoid xdriver_end(void);\nvoid x_get_kimage(Kimage *kimage_ptr);\nvoid dev_video_init(void);\nvoid x_redraw_status_lines(void);\nvoid x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, int width, int height);\nvoid x_push_done(void);\nvoid x_auto_repeat_on(int must);\nvoid x_auto_repeat_off(int must);\nvoid x_hide_pointer(int do_hide);\nvoid x_full_screen(int do_full);\nvoid update_main_window_size(void);\n\n"
  },
  {
    "path": "upstream/kegs/src/protos_macsnd_driver.h",
    "content": "/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\nconst char rcsid_protos_macsnd_driver_h[] = \"@(#)$KmKId: protos_macsnd_driver.h,v 1.9 2023-05-04 19:35:29+00 kentd Exp $\";\n\n/* END_HDR */\n\n/* macsnd_driver.c */\nint mac_send_audio(byte *ptr, int in_size);\nvoid macsnd_init(void);\n\n\n"
  },
  {
    "path": "upstream/kegs/src/protos_pulseaudio_driver.h",
    "content": "/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2020 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\nconst char rcsid_protos_pulseaudio_driver_h[] = \"@(#)$KmKId: protos_pulseaudio_driver.h,v 1.4 2020-12-02 22:35:39+00 kentd Exp $\";\n\n/* END_HDR */\n\n/* pulseaudio_driver.c */\nint pulse_audio_send_audio(byte *ptr, int in_size);\nvoid pulse_audio_main_events(void);\nvoid pulse_audio_write_to_stream(int dbg_count, int in_sz);\nint pulse_audio_start_stream(void);\nint pulse_audio_do_init(void);\nint pulse_audio_init(void);\nvoid pulse_audio_shutdown(void);\n\n"
  },
  {
    "path": "upstream/kegs/src/protos_windriver.h",
    "content": "/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2022 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// $KmKId: protos_windriver.h,v 1.15 2023-05-17 22:37:57+00 kentd Exp $\n\n/* END_HDR */\n\n/* windriver.c */\nWindow_info *win_find_win_info_ptr(HWND hwnd);\nvoid win_hide_pointer(Window_info *win_info_ptr, int do_hide);\nint win_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y, int button_states, int buttons_valid);\nvoid win_event_mouse(HWND hwnd, WPARAM wParam, LPARAM lParam);\nvoid win_event_key(HWND hwnd, WPARAM wParam, LPARAM lParam, int down);\nvoid win_event_redraw(HWND hwnd);\nvoid win_event_destroy(HWND hwnd);\nvoid win_event_size(HWND hwnd, WPARAM wParam, LPARAM lParam);\nvoid win_event_minmaxinfo(HWND hwnd, LPARAM lParam);\nvoid win_event_focus(HWND hwnd, int gain_focus);\nLRESULT CALLBACK win_event_handler(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam);\nint main(int argc, char **argv);\nvoid check_input_events(void);\nvoid win_video_init(int mdepth);\nvoid win_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str, int mdepth);\nvoid win_create_window(Window_info *win_info_ptr);\nvoid xdriver_end(void);\nvoid win_resize_window(Window_info *win_info_ptr);\nvoid x_update_display(Window_info *win_info_ptr);\nvoid x_hide_pointer(int do_hide);\nint opendir_int(DIR *dirp, const char *in_filename);\nDIR *opendir(const char *in_filename);\nstruct dirent *readdir(DIR *dirp);\nint closedir(DIR *dirp);\nint lstat(const char *path, struct stat *bufptr);\nint ftruncate(int fd, word32 length);\n\n\n\n/* win32snd_driver.c */\nvoid win32snd_init(word32 *shmaddr);\nvoid win32snd_shutdown(void);\nvoid CALLBACK handle_wav_snd(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);\nvoid check_wave_error(int res, char *str);\nvoid child_sound_init_win32(void);\nvoid win32snd_set_playing(int snd_playing);\nvoid win32_send_audio2(byte *ptr, int size);\nint win32_send_audio(byte *ptr, int in_size);\n\n\n"
  },
  {
    "path": "upstream/kegs/src/protos_xdriver.h",
    "content": "/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\nconst char rcsid_protos_x_h[] = \"@(#)$KmKId: protos_xdriver.h,v 1.40 2023-06-16 19:32:26+00 kentd Exp $\";\n\n/* END_HDR */\n\n/* xdriver.c */\nint main(int argc, char **argv);\nint my_error_handler(Display *display, XErrorEvent *ev);\nvoid xdriver_end(void);\nvoid x_try_xset_r(void);\nvoid x_badpipe(int signum);\nint kegs_x_io_error_handler(Display *display);\nint x_video_get_mdepth(void);\nint x_try_find_visual(int depth, int screen_num);\nvoid x_video_init(void);\nvoid x_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str);\nvoid x_create_window(Window_info *win_info_ptr);\nint xhandle_shm_error(Display *display, XErrorEvent *event);\nvoid x_allocate_window_data(Window_info *win_info_ptr);\nvoid get_shm(Window_info *win_info_ptr, int width, int height);\nvoid get_ximage(Window_info *win_info_ptr, int width, int height);\nvoid x_set_size_hints(Window_info *win_info_ptr);\nvoid x_resize_window(Window_info *win_info_ptr);\nvoid x_update_display(Window_info *win_info_ptr);\nWindow_info *x_find_xwin(Window in_win);\nvoid x_send_copy_data(Window_info *win_info_ptr);\nvoid x_handle_copy(XSelectionRequestEvent *req_ev_ptr);\nvoid x_handle_targets(XSelectionRequestEvent *req_ev_ptr);\nvoid x_request_paste_data(Window_info *win_info_ptr);\nvoid x_handle_paste(Window w, Atom property);\nint x_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y, int button_states, int buttons_valid);\nvoid x_input_events(void);\nvoid x_hide_pointer(Window_info *win_info_ptr, int do_hide);\nvoid x_handle_keysym(XEvent *xev_in);\nint x_keysym_to_a2code(Window_info *win_info_ptr, int keysym, int is_up);\nvoid x_update_modifier_state(Window_info *win_info_ptr, int state);\nvoid x_auto_repeat_on(int must);\nvoid x_auto_repeat_off(int must);\nvoid x_full_screen(int do_full);\n\n\n"
  },
  {
    "path": "upstream/kegs/src/pulseaudio_driver.c",
    "content": "const char rcsid_pulseaudio_driver_c[] = \"@(#)$KmKId: pulseaudio_driver.c,v 1.10 2021-12-16 22:41:59+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2020 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#ifdef PULSE_AUDIO\n// Ignore entire file if PULSE_AUDIO is not defined!\n\n// Some ideas from Sample code:\n//  https://github.com/gavv/snippets/blob/master/pa/pa_play_async_poll.c\n\n#include \"defc.h\"\n#include \"sound.h\"\n#include \"protos_pulseaudio_driver.h\"\n\n#include <pulse/pulseaudio.h>\n#include <unistd.h>\n\n#define PULSE_REBUF_SIZE\t(64*1024)\n\nword32\tg_pulseaudio_rebuf[PULSE_REBUF_SIZE];\nvolatile int g_pulseaudio_rd_pos;\nvolatile int g_pulseaudio_wr_pos;\nvolatile int g_pulseaudio_playing = 0;\n\nextern int Verbose;\n\nextern int g_preferred_rate;\nextern int g_audio_enable;\nextern word32 *g_sound_shm_addr;\nextern int g_sound_min_samples;\nextern int g_sound_max_multiplier;\nextern int g_sound_size;\nextern word32 g_vbl_count;\n\nint g_call_num = 0;\n\npa_mainloop *g_pa_mainloop_ptr = 0;\npa_context *g_pa_context_ptr = 0;\npa_stream *g_pa_stream_ptr = 0;\npa_sample_spec g_pa_sample_spec = { 0 };\npa_buffer_attr g_pa_buffer_attr = { 0 };\n\n\nint\npulse_audio_send_audio(byte *ptr, int in_size)\n{\n\tword32\t*wptr, *pa_wptr;\n\tint\tsamps, sample_num;\n\tint\ti;\n\n\tsamps = in_size / 4;\n\twptr = (word32 *)ptr;\n\tsample_num = g_pulseaudio_wr_pos;\n\tpa_wptr = &(g_pulseaudio_rebuf[0]);\n\tfor(i = 0; i < samps; i++) {\n\t\tpa_wptr[sample_num] = *wptr++;\n\t\tsample_num++;\n\t\tif(sample_num >= PULSE_REBUF_SIZE) {\n\t\t\tsample_num = 0;\n\t\t}\n\t}\n\n\tg_pulseaudio_wr_pos = sample_num;\n\n\tpulse_audio_main_events();\n\n\treturn in_size;\n}\n\nvoid\npulse_audio_main_events()\n{\n\tpa_stream_state_t stream_state;\n\tpa_context_state_t context_state;\n\tint\tcount, num, sz, do_write;\n\n\tcount = 0;\n\tdo_write = 1;\n\twhile(1) {\n\t\t// Do a few mainloop cycles to see if samples are needed\n\t\tnum = pa_mainloop_iterate(g_pa_mainloop_ptr, 0, 0);\n\t\t//printf(\"pa_mainloop_iterate ret:%d count:%d\\n\", num, count);\n\t\tif(num < 0) {\n\t\t\treturn;\n\t\t}\n\t\tcontext_state = pa_context_get_state(g_pa_context_ptr);\n\t\tif((context_state == PA_CONTEXT_FAILED) ||\n\t\t\t\t(context_state == PA_CONTEXT_TERMINATED)) {\n\t\t\tprintf(\"context_state is bad: %d\\n\", context_state);\n\t\t\tg_audio_enable = 0;\n\t\t\treturn;\n\t\t}\n\t\tstream_state = pa_stream_get_state(g_pa_stream_ptr);\n\t\tif((stream_state == PA_STREAM_FAILED) ||\n\t\t\t\t(stream_state == PA_STREAM_TERMINATED)) {\n\t\t\tprintf(\"stream state bad: %d\\n\", stream_state);\n\t\t\tg_audio_enable = 0;\n\t\t\treturn;\n\t\t}\n\t\tif(do_write) {\n\t\t\tsz = pa_stream_writable_size(g_pa_stream_ptr);\n\t\t\tif(sz > 0) {\n\t\t\t\tpulse_audio_write_to_stream(count, sz);\n\t\t\t\tdo_write = 0;\n\t\t\t}\n\t\t}\n\t\tcount++;\n\t\tif(count > 50) {\n\t\t\tprintf(\"pulse_audio_main_events() looped %d times\\n\",\n\t\t\t\t\t\t\t\tcount);\n\t\t\treturn;\n\t\t}\n\t\tif(num == 0) {\t\t\t// Nothing else to do\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nvoid\npulse_audio_write_to_stream(int dbg_count, int in_sz)\n{\n\tword32\t*wptr;\n\tvoid\t*vptr;\n\t// const pa_timing_info *pa_timing_info_ptr;\n\t// pa_usec_t\tpa_latency;\n\tsize_t\tsz;\n\tint\tnum_samps, sample_num, samps_avail, err, samps_needed;\n\tint\tmin_samples, max_samples;\n\tint\ti;\n\n\tsamps_needed = in_sz / 4;\n\tsample_num = g_pulseaudio_rd_pos;\n\tsamps_avail = (g_pulseaudio_wr_pos - sample_num) &\n\t\t\t\t\t\t\t(PULSE_REBUF_SIZE - 1);\n\tmin_samples = g_sound_min_samples;\n\tmax_samples = min_samples * g_sound_max_multiplier;\n\tif(samps_needed > min_samples) {\n\t\tmin_samples = samps_needed;\n\t}\n#if 0\n\tif(samps_needed > samps_avail) {\n\t\t// We don't have enough samples, must pause\n\t\tg_pulseaudio_playing = 0;\n\t\tsample_num = g_pulseaudio_wr_pos;\t// Eat remaining samps\n\t}\n#endif\n\tif((g_pulseaudio_playing == 0) && (samps_avail > min_samples)) {\n\t\t// We can unpause\n\t\tg_pulseaudio_playing = 1;\n\t}\n\tif(g_pulseaudio_playing && (samps_avail > max_samples)) {\n\t\tprintf(\"JUMP SAMPLE_NUM by %d samples!\\n\", max_samples / 2);\n\t\tsample_num += (max_samples / 2);\n\t\tsample_num = sample_num & (PULSE_REBUF_SIZE - 1);\n\t}\n\n#if 0\n\tif(g_call_num < 100) {\n\t\tprintf(\"call_num:%d playing:%d g_vbl_count:%d samps_needed:%d, \"\n\t\t\t\"samps_avail:%d\\n\", g_call_num, g_pulseaudio_playing,\n\t\t\tg_vbl_count, samps_needed, samps_avail);\n\t}\n#endif\n\tg_call_num++;\n\tnum_samps = MIN(samps_avail, samps_needed);\n\tif(g_pulseaudio_playing) {\n\t\tvptr = 0;\t\t\t// Let it allocate for us\n\t\tsz = num_samps * 4;\n\t\terr = pa_stream_begin_write(g_pa_stream_ptr, &vptr, &sz);\n\t\twptr = vptr;\n\t\tif(err) {\n\t\t\tg_audio_enable = 0;\n\t\t\tprintf(\"pa_stream_begin_write failed: %s\\n\",\n\t\t\t\t\t\t\tpa_strerror(err));\n\t\t\treturn;\n\t\t}\n\t\tnum_samps = sz / 4;\n\t\tfor(i = 0; i < num_samps; i++) {\n\t\t\twptr[i] = g_pulseaudio_rebuf[sample_num];\n\t\t\tsample_num++;\n\t\t\tif(sample_num >= PULSE_REBUF_SIZE) {\n\t\t\t\tsample_num = 0;\n\t\t\t}\n\t\t}\n\t} else {\n\t\terr = pa_stream_cancel_write(g_pa_stream_ptr);\n\t\t// Just get out...don't let us get further behind by sending\n\t\t//  silence frames.\n\t\treturn;\n\t}\n\n\tg_pulseaudio_rd_pos = sample_num;\n\n#if 0\n\tpa_timing_info_ptr = pa_stream_get_timing_info(g_pa_stream_ptr);\n\terr = pa_stream_get_latency(g_pa_stream_ptr, &pa_latency, 0);\n\tprintf(\" will send %d samples to the stream, write_index:%lld, \"\n\t\t\"latency:%lld\\n\", num_samps * 4,\n\t\t(word64)pa_timing_info_ptr->write_index, (word64)pa_latency);\n#endif\n\terr = pa_stream_write(g_pa_stream_ptr, wptr, num_samps * 4, 0, 0,\n\t\t\t\t\t\t\tPA_SEEK_RELATIVE);\n\tif(err) {\n\t\tprintf(\"pa_stream_write: %s\\n\", pa_strerror(err));\n\t\tg_audio_enable = 0;\n\t}\n}\n\nint\npulse_audio_start_stream()\n{\n\tint\tflags, ret;\n\n\tg_pa_sample_spec.format = PA_SAMPLE_S16LE;\n\tg_pa_sample_spec.rate = g_preferred_rate;\n\tg_pa_sample_spec.channels = 2;\n\tprintf(\"Set requested rate=%d\\n\", g_pa_sample_spec.rate);\n\tg_pa_stream_ptr = pa_stream_new(g_pa_context_ptr, \"KEGS\",\n\t\t\t\t\t\t&g_pa_sample_spec, 0);\n\tif(!g_pa_stream_ptr) {\n\t\tprintf(\"pa_stream_new failed\\n\");\n\t\treturn 1;\n\t}\n\n\tg_pa_buffer_attr.maxlength = -1;\t\t// Maximum server buffer\n\tg_pa_buffer_attr.tlength = 4*g_preferred_rate/10;\t// 1/10th sec\n\t//g_pa_buffer_attr.prebuf = 4*g_preferred_rate/100;\t// 1/100th sec\n\tg_pa_buffer_attr.prebuf = -1;\n\tg_pa_buffer_attr.minreq = 4*g_preferred_rate/60;\t// 1/60th sec\n\n\tflags = PA_STREAM_ADJUST_LATENCY;\n\t//flags = PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |\n\t//\t\tPA_STREAM_ADJUST_LATENCY;\n\t// PA_STREAM_AUTO_TIMING_UPDATE and PA_STREAM_INTERPOLATE_TIMING are\n\t//  to get latency info from the server.  PA_STREAM_ADJUST_LATENCY\n\t//  means the total latency including the server output sink buffer\n\t//  tries to be tlength\n\n\tret = pa_stream_connect_playback(g_pa_stream_ptr, 0, &g_pa_buffer_attr,\n\t\t\t\t\t\t\tflags, 0, 0);\n\tif(ret) {\n\t\tprintf(\"pa_stream_connect_playback failed: %d\\n\", ret);\n\t\treturn ret;\n\t}\n\n\treturn 0;\t\t// Success!\n}\n\nint\npulse_audio_do_init()\n{\n\tpa_stream_state_t stream_state;\n\tpa_context_state_t context_state;\n\tint\tret, count, num;\n\n\tg_pa_mainloop_ptr = pa_mainloop_new();\n\tg_pa_context_ptr = pa_context_new(\n\t\t\tpa_mainloop_get_api(g_pa_mainloop_ptr), \"KEGS\");\n\tif(!g_pa_context_ptr) {\n\t\tprintf(\"Pulse Audio pa_context_new() failed\\n\");\n\t\treturn 1;\n\t}\n\n\tret = pa_context_connect(g_pa_context_ptr, 0, 0, 0);\n\tif(ret != 0) {\n\t\tprintf(\"pa_context_connect failed: %d\\n\", ret);\n\t\treturn 1;\n\t}\n\n\tcount = 0;\n\twhile(1) {\n\t\t// Do a few mainloop cycles to get stream initialized\n\t\tnum = pa_mainloop_iterate(g_pa_mainloop_ptr, 0, 0);\n#if 0\n\t\tprintf(\"pa_mainloop_iterate ret: %d, count:%d g_pa_stream_ptr:\"\n\t\t\t\t\"%p\\n\", num, count, g_pa_stream_ptr);\n#endif\n\t\tif(num < 0) {\n\t\t\treturn 1;\n\t\t}\n\t\tif(num == 0) {\n\t\t\tusleep(10*1000);\n\t\t\tif(count++ > 50) {\n\t\t\t\t// Waited more than 500ms, just give up\n\t\t\t\tprintf(\"Timed out waiting for Pulse Audio to \"\n\t\t\t\t\t\t\t\t\"start\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t\t// See if context is ready\n\t\tcontext_state = pa_context_get_state(g_pa_context_ptr);\n\t\tif((context_state == PA_CONTEXT_FAILED) ||\n\t\t\t\t(context_state == PA_CONTEXT_TERMINATED)) {\n\t\t\tprintf(\"context_state is bad: %d\\n\", context_state);\n\t\t\treturn 1;\n\t\t}\n\t\tif((context_state == PA_CONTEXT_READY) && !g_pa_stream_ptr) {\n\t\t\tret = pulse_audio_start_stream();\n\t\t\tif(ret) {\n\t\t\t\treturn ret;\n\t\t\t}\n\t\t}\n\t\tif(g_pa_stream_ptr) {\n\t\t\tstream_state = pa_stream_get_state(g_pa_stream_ptr);\n\t\t\tif((stream_state == PA_STREAM_FAILED) ||\n\t\t\t\t\t(stream_state == PA_STREAM_TERMINATED)){\n\t\t\t\tprintf(\"stream state bad: %d\\n\", stream_state);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tif(stream_state == PA_STREAM_READY) {\n\t\t\t\tprintf(\"Pulse Audio stream is now ready!\\n\");\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\nint\npulse_audio_init()\n{\n\tint\tret;\n\n\tg_pulseaudio_rd_pos = 0;\n\tg_pulseaudio_wr_pos = 0;\n\n\tret = pulse_audio_do_init();\n\t// printf(\"pulse_audio_init ret:%d\\n\", ret);\n\tif(ret != 0) {\n\t\t// Free structures, disable sound\n\t\tif(g_pa_stream_ptr) {\n\t\t\tpa_stream_disconnect(g_pa_stream_ptr);\n\t\t\tpa_stream_unref(g_pa_stream_ptr);\n\t\t\tg_pa_stream_ptr = 0;\n\t\t}\n\t\tif(g_pa_context_ptr) {\n\t\t\tpa_context_disconnect(g_pa_context_ptr);\n\t\t\tpa_context_unref(g_pa_context_ptr);\n\t\t\tg_pa_context_ptr = 0;\n\t\t}\n\t\tif(g_pa_mainloop_ptr) {\n\t\t\tpa_mainloop_free(g_pa_mainloop_ptr);\n\t\t\tg_pa_mainloop_ptr = 0;\n\t\t}\n\t\treturn ret;\n\t}\n\n\tsound_set_audio_rate(g_preferred_rate);\n\n\treturn 0;\n}\n\nvoid\npulse_audio_shutdown()\n{\n\tprintf(\"pulse_audio_shutdown\\n\");\n}\n\n#endif\t\t/* PULSE_AUDIO */\n"
  },
  {
    "path": "upstream/kegs/src/scc.c",
    "content": "const char rcsid_scc_c[] = \"@(#)$KmKId: scc.c,v 1.74 2025-04-29 22:17:05+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2025 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// Driver for the Zilog SCC Z8530, which implements two channels (A,B) of\n//  serial ports, controlled by $C038-$C03B\n\n#include \"defc.h\"\n\nextern int Verbose;\nextern int g_code_yellow;\nextern dword64 g_cur_dfcyc;\nextern int g_serial_cfg[2];\nextern int g_serial_mask[2];\nextern char *g_serial_remote_ip[2];\nextern int g_serial_remote_port[2];\nextern char *g_serial_device[2];\nextern int g_serial_win_device[2];\nextern int g_irq_pending;\n\n/* scc\tport 0 == channel A = slot 1 = c039/c03b */\n/*\tport 1 == channel B = slot 2 = c038/c03a */\n\n#include \"scc.h\"\n#define SCC_R14_DPLL_SOURCE_BRG\t\t0x100\n#define SCC_R14_DPLL_SOURCE_RTXC\t0x200\n\n#define SCC_DCYCS_PER_PCLK\t((DCYCS_1_MHZ) / ((DCYCS_28_MHZ) /8))\n#define SCC_DCYCS_PER_XTAL\t((DCYCS_1_MHZ) / 3686400.0)\n\n// PCLK is 3.5795MHz\n\n#define SCC_BR_EVENT\t\t\t1\n#define SCC_TX_EVENT\t\t\t2\n#define SCC_RX_EVENT\t\t\t3\n#define SCC_MAKE_EVENT(port, a)\t\t(((a) << 1) + (port))\n\nScc\tg_scc[2];\n\nint g_baud_table[] = {\n\t110, 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200\n};\n\nint g_scc_overflow = 0;\nint\tg_scc_init = 0;\n\n// cur_state >= 0 and matches g_serial_cfg[port]: port is in that mode\n// cur_state = -1: port is in no particular mode and should go to g_serial_cfg[]\n// cur_state = -2: port failed to enter g_serial_cfg[], do not try again until\n//\t\tsomething changes\n\nvoid\nscc_init()\n{\n\tScc\t*scc_ptr;\n\tint\ti, j;\n\n\tfor(i = 0; i < 2; i++) {\n\t\tscc_ptr = &(g_scc[i]);\n\t\tmemset(scc_ptr, 0, sizeof(*scc_ptr));\n\t\tscc_ptr->cur_state = -1;\n\t\tscc_ptr->modem_state = 0;\n\t\tscc_ptr->sockfd = INVALID_SOCKET;\n\t\tscc_ptr->rdwrfd = INVALID_SOCKET;\n\t\tscc_ptr->sockaddr_ptr = 0;\n\t\tscc_ptr->sockaddr_size = 0;\n\t\tscc_ptr->unix_dev_fd = -1;\n\t\tscc_ptr->win_com_handle = 0;\n\t\tscc_ptr->win_dcb_ptr = 0;\n\t\tscc_ptr->br_event_pending = 0;\n\t\tscc_ptr->rx_event_pending = 0;\n\t\tscc_ptr->tx_event_pending = 0;\n\t\tscc_ptr->char_size = 8;\n\t\tscc_ptr->baud_rate = 9600;\n\t\tscc_ptr->telnet_mode = 0;\n\t\tscc_ptr->telnet_iac = 0;\n\t\tscc_ptr->out_char_dfcyc = 0;\n\t\tscc_ptr->socket_error = 0;\n\t\tscc_ptr->socket_num_rings = 0;\n\t\tscc_ptr->socket_last_ring_dfcyc = 0;\n\t\tscc_ptr->modem_mode = 0;\n\t\tscc_ptr->modem_plus_mode = 0;\n\t\tscc_ptr->modem_s0_val = 0;\n\t\tscc_ptr->modem_s2_val = '+';\n\t\tscc_ptr->modem_cmd_len = 0;\n\t\tscc_ptr->modem_out_portnum = 23;\n\t\tscc_ptr->modem_cmd_str[0] = 0;\n\t\tfor(j = 0; j < 2; j++) {\n\t\t\tscc_ptr->telnet_local_mode[j] = 0;\n\t\t\tscc_ptr->telnet_remote_mode[j] = 0;\n\t\t\tscc_ptr->telnet_reqwill_mode[j] = 0;\n\t\t\tscc_ptr->telnet_reqdo_mode[j] = 0;\n\t\t}\n\t}\n\n\tg_scc_init = 1;\n}\n\nvoid\nscc_reset()\n{\n\tScc\t*scc_ptr;\n\tint\ti;\n\n\tif(!g_scc_init) {\n\t\thalt_printf(\"scc_reset called before init\\n\");\n\t\treturn;\n\t}\n\tfor(i = 0; i < 2; i++) {\n\t\tscc_ptr = &(g_scc[i]);\n\n\t\tscc_ptr->mode = 0;\n\t\tscc_ptr->reg_ptr = 0;\n\t\tscc_ptr->in_rdptr = 0;\n\t\tscc_ptr->in_wrptr = 0;\n\t\tscc_ptr->out_rdptr = 0;\n\t\tscc_ptr->out_wrptr = 0;\n\t\tscc_ptr->dcd = 1 - i;\t\t// 1 for slot 1, 0 for slot 2\n\t\tscc_ptr->wantint_rx = 0;\n\t\tscc_ptr->wantint_tx = 0;\n\t\tscc_ptr->wantint_zerocnt = 0;\n\t\tscc_ptr->read_called_this_vbl = 0;\n\t\tscc_ptr->write_called_this_vbl = 0;\n\t\tscc_evaluate_ints(i);\n\t\tscc_hard_reset_port(i);\n\t}\n}\n\nvoid\nscc_hard_reset_port(int port)\n{\n\tScc\t*scc_ptr;\n\n\tscc_reset_port(port);\n\n\tscc_ptr = &(g_scc[port]);\n\tscc_ptr->reg[14] = 0;\t\t/* zero bottom two bits */\n\tscc_ptr->reg[13] = 0;\n\tscc_ptr->reg[12] = 0;\n\tscc_ptr->reg[11] = 0x08;\n\tscc_ptr->reg[10] = 0;\n\tscc_ptr->reg[7] = 0;\n\tscc_ptr->reg[6] = 0;\n\tscc_ptr->reg[5] = 0;\n\tscc_ptr->reg[4] = 0x04;\n\tscc_ptr->reg[3] = 0;\n\tscc_ptr->reg[2] = 0;\n\tscc_ptr->reg[1] = 0;\n\n\t/* HACK HACK: */\n\tg_scc[0].reg[9] = 0;\t\t/* Clear all interrupts */\n\n\tscc_evaluate_ints(port);\n\n\tscc_regen_clocks(port);\n}\n\nvoid\nscc_reset_port(int port)\n{\n\tScc\t*scc_ptr;\n\n\tscc_ptr = &(g_scc[port]);\n\tscc_ptr->reg[15] = 0xf8;\n\tscc_ptr->reg[14] &= 0x03;\t/* 0 most (including >= 0x100) bits */\n\tscc_ptr->reg[10] = 0;\n\tscc_ptr->reg[5] &= 0x65;\t/* leave tx bits and sdlc/crc bits */\n\tscc_ptr->reg[4] |= 0x04;\t/* Set async mode */\n\tscc_ptr->reg[3] &= 0xfe;\t/* clear receiver enable */\n\tscc_ptr->reg[1] &= 0xfe;\t/* clear ext int enable */\n\n\tscc_ptr->br_is_zero = 0;\n\tscc_ptr->tx_buf_empty = 1;\n\n\tscc_ptr->wantint_rx = 0;\n\tscc_ptr->wantint_tx = 0;\n\tscc_ptr->wantint_zerocnt = 0;\n\n\tscc_ptr->rx_queue_depth = 0;\n\n\tscc_evaluate_ints(port);\n\n\tscc_regen_clocks(port);\n\n\tscc_clr_tx_int(port);\n\tscc_clr_rx_int(port);\n}\n\nvoid\nscc_regen_clocks(int port)\n{\n\tScc\t*scc_ptr;\n\tdouble\tbr_dcycs, tx_dcycs, rx_dcycs, rx_char_size, tx_char_size;\n\tdouble\tclock_mult, dpll_dcycs;\n\tword32\treg4, reg11, reg14, br_const, max_diff, diff;\n\tint\tbaud, cur_state, baud_entries, pos;\n\tint\ti;\n\n\t/*\tAlways do baud rate generator */\n\tscc_ptr = &(g_scc[port]);\n\tbr_const = (scc_ptr->reg[13] << 8) + scc_ptr->reg[12];\n\tbr_const += 2;\t/* counts down past 0 */\n\n\treg4 = scc_ptr->reg[4];\t\t// Transmit/Receive misc params\n\tclock_mult = 1.0;\n\tswitch((reg4 >> 6) & 3) {\n\tcase 0:\t\t/* x1 */\n\t\tclock_mult = 1.0;\n\t\tbreak;\n\tcase 1:\t\t/* x16 */\n\t\tclock_mult = 16.0;\n\t\tbreak;\n\tcase 2:\t\t/* x32 */\n\t\tclock_mult = 32.0;\n\t\tbreak;\n\tcase 3:\t\t/* x64 */\n\t\tclock_mult = 64.0;\n\t\tbreak;\n\t}\n\n\tbr_dcycs = 0.01;\n\treg14 = scc_ptr->reg[14];\n\tif(reg14 & 0x1) {\n\t\tbr_dcycs = SCC_DCYCS_PER_XTAL;\n\t\tif(reg14 & 0x2) {\n\t\t\tbr_dcycs = SCC_DCYCS_PER_PCLK;\n\t\t}\n\t}\n\n\tbr_dcycs = br_dcycs * (double)br_const * 2.0;\n\n\tdpll_dcycs = 0.1;\n\tif(reg14 & SCC_R14_DPLL_SOURCE_BRG) {\n\t\tdpll_dcycs = br_dcycs;\n\t} else if(reg14 & SCC_R14_DPLL_SOURCE_RTXC) {\n\t\tdpll_dcycs = SCC_DCYCS_PER_XTAL;\n\t}\n\n\ttx_dcycs = 1;\n\treg11 = scc_ptr->reg[11];\n\tswitch((reg11 >> 3) & 3) {\n\tcase 0:\t\t// /RTxC pin\n\t\ttx_dcycs = SCC_DCYCS_PER_XTAL;\n\t\tbreak;\n\tcase 2:\t\t// BR generator output\n\t\ttx_dcycs = br_dcycs;\n\t\tbreak;\n\tcase 3:\t\t// DPLL output\n\t\ttx_dcycs = dpll_dcycs;\n\t\tbreak;\n\t}\n\n\ttx_dcycs = tx_dcycs * clock_mult;\n\n\trx_dcycs = 1;\n\tswitch((reg11 >> 5) & 3) {\n\tcase 0:\t\t// /RTxC pin\n\t\trx_dcycs = SCC_DCYCS_PER_XTAL;\n\t\tbreak;\n\tcase 2:\t\t// BR generator output\n\t\trx_dcycs = br_dcycs;\n\t\tbreak;\n\tcase 3:\t\t// DPLL output\n\t\trx_dcycs = dpll_dcycs;\n\t\tbreak;\n\t}\n\trx_dcycs = rx_dcycs * clock_mult;\n\n\ttx_char_size = 8.0;\n\tswitch((scc_ptr->reg[5] >> 5) & 0x3) {\n\tcase 0:\t// 5 bits\n\t\ttx_char_size = 5.0;\n\t\tbreak;\n\tcase 1:\t// 7 bits\n\t\ttx_char_size = 7.0;\n\t\tbreak;\n\tcase 2:\t// 6 bits\n\t\ttx_char_size = 6.0;\n\t\tbreak;\n\t}\n\n\tscc_ptr->char_size = (int)tx_char_size;\n\n\tswitch((scc_ptr->reg[4] >> 2) & 0x3) {\n\tcase 1:\t// 1 stop bit\n\t\ttx_char_size += 2.0;\t// 1 stop + 1 start bit\n\t\tbreak;\n\tcase 2:\t// 1.5 stop bit\n\t\ttx_char_size += 2.5;\t// 1.5 stop + 1 start bit\n\t\tbreak;\n\tcase 3:\t// 2 stop bits\n\t\ttx_char_size += 3.0;\t// 2.0 stop + 1 start bit\n\t\tbreak;\n\t}\n\n\tif(scc_ptr->reg[4] & 1) {\n\t\t// parity enabled\n\t\ttx_char_size += 1.0;\n\t}\n\n\tif(scc_ptr->reg[14] & 0x10) {\n\t\t/* loopback mode, make it go faster...*/\n\t\trx_char_size = 1.0;\n\t\ttx_char_size = 1.0;\n\t}\n\n\trx_char_size = tx_char_size;\t/* HACK */\n\n\tbaud = (int)(DCYCS_1_MHZ / tx_dcycs);\n\tmax_diff = 5000000;\n\tpos = 0;\n\tbaud_entries = sizeof(g_baud_table)/sizeof(g_baud_table[0]);\n\tfor(i = 0; i < baud_entries; i++) {\n\t\tdiff = abs(g_baud_table[i] - baud);\n\t\tif(diff < max_diff) {\n\t\t\tpos = i;\n\t\t\tmax_diff = diff;\n\t\t}\n\t}\n\n\tscc_ptr->baud_rate = g_baud_table[pos];\n\n\tscc_ptr->br_dcycs = br_dcycs;\n\tscc_ptr->tx_dcycs = tx_dcycs * tx_char_size;\n\tscc_ptr->rx_dcycs = rx_dcycs * rx_char_size;\n\n\tcur_state = scc_ptr->cur_state;\n\tif(cur_state == 0) {\t\t// real serial ports\n#ifdef _WIN32\n\t\tscc_serial_win_change_params(port);\n#else\n\t\tscc_serial_unix_change_params(port);\n#endif\n\t}\n}\n\nvoid\nscc_port_close(int port)\n{\n\tScc\t*scc_ptr;\n\n\tscc_ptr = &(g_scc[port]);\n\n#ifdef _WIN32\n\tscc_serial_win_close(port);\n#else\n\tscc_serial_unix_close(port);\n#endif\n\tscc_socket_close(port);\n\n\tscc_ptr->cur_state = -1;\t\t// Nothing open\n}\n\nvoid\nscc_port_open(dword64 dfcyc, int port)\n{\n\tint\tcfg;\n\n\tcfg = g_serial_cfg[port];\n\tprintf(\"scc_port_open port:%d cfg:%d\\n\", port, cfg);\n\n\tif(cfg == 0) {\t\t// Real host serial port\n#ifdef _WIN32\n\t\tscc_serial_win_open(port);\n#else\n\t\tscc_serial_unix_open(port);\n#endif\n\t} else if(cfg >= 1) {\n\t\tscc_socket_open(dfcyc, port, cfg);\n\t}\n\tprintf(\" open socketfd:%ld\\n\", (long)g_scc[port].sockfd);\n}\n\nint\nscc_is_port_closed(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\n\t// Returns 1 is the port is closed (not working).  Returns 0\n\t//  if the port is open.  Tries to open the port if it is not in error\n\tscc_ptr = &(g_scc[port]);\n\tif(scc_ptr->cur_state == -1) {\n\t\tscc_port_open(dfcyc, port);\n\t}\n\tif(scc_ptr->cur_state < 0) {\n\t\tscc_ptr->cur_state = -2;\n\t\t//printf(\"scc_is_port_closed p:%d returning 0\\n\", port);\n\t\treturn 1;\t\t// Not open\n\t}\n\treturn 0;\t\t\t// Port is open!\n}\n\nchar *\nscc_get_serial_status(int get_status, int port)\n{\n\tchar\tbuf[80];\n\tchar\t*str;\n\tint\tcur_state;\n\n\tif(get_status == 0) {\n\t\treturn 0;\n\t}\n\tcur_state = g_scc[port].cur_state;\n\tstr = \"\";\n\tswitch(cur_state) {\n\tcase -1:\n\t\tstr = \"Not initialized yet\";\n\t\tbreak;\n\tcase 0:\n\t\tsnprintf(buf, 80, \"Opened %s OK\", g_serial_device[port]);\n\t\tstr = buf;\n\t\tbreak;\n\tcase 1:\n\t\tsnprintf(buf, 80, \"Virtual modem, sockfd:%d\",\n\t\t\t(int)g_scc[port].sockfd);\n\t\tstr = buf;\n\t\tbreak;\n\tcase 2:\n\t\tsnprintf(buf, 80, \"Outgoing to %s:%d\",\n\t\t\tg_serial_remote_ip[port], g_serial_remote_port[port]);\n\t\tstr = buf;\n\t\tbreak;\n\tcase 3:\n\t\tsnprintf(buf, 80, \"Opened %d, sockfd:%d\", 6501 + port,\n\t\t\t\t\t(int)g_scc[port].sockfd);\n\t\tstr = buf;\n\t\tbreak;\n\tdefault:\n\t\tstr = \"Open failed, port is closed\";\n\t}\n\treturn kegs_malloc_str(str);\n}\n\n\nvoid\nscc_config_changed(int port, int cfg_changed, int remote_changed,\n\t\t\t\t\t\tint serial_dev_changed)\n{\n\tScc\t*scc_ptr;\n\tint\tmust_change;\n\n\t// Check if scc_init() was called, if not get out\n\tif(!g_scc_init) {\n\t\treturn;\n\t}\n\n\t// F4 may have changed the serial port config.  If so, close old\n\t//  port, open new one.\n\n\tscc_ptr = &(g_scc[port]);\n\tmust_change = cfg_changed;\n\tswitch(scc_ptr->cur_state) {\n\tcase 0:\t\t// Using serial port\n\t\tmust_change |= serial_dev_changed;\n\t\tbreak;\n\tcase 2:\t\t// Using remote connection\n\t\tmust_change |= remote_changed;\n\t\tbreak;\n\t}\n\tif(must_change) {\n\t\tscc_port_close(port);\n\t}\n}\n\nvoid\nscc_update(dword64 dfcyc)\n{\n\tint\ti;\n\n\t// called each VBL update\n\tfor(i = 0; i < 2; i++) {\n\t\tg_scc[i].write_called_this_vbl = 0;\n\t\tg_scc[i].read_called_this_vbl = 0;\n\n\t\t// These calls will try to open the port if it's closed\n\t\tscc_try_to_empty_writebuf(dfcyc, i);\n\t\tscc_try_fill_readbuf(dfcyc, i);\n\n\t\tg_scc[i].write_called_this_vbl = 0;\n\t\tg_scc[i].read_called_this_vbl = 0;\n\t}\n}\n\nvoid\nscc_try_to_empty_writebuf(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tint\tcur_state;\n\n\tscc_ptr = &(g_scc[port]);\n\tcur_state = scc_ptr->cur_state;\n\tif(scc_ptr->write_called_this_vbl) {\n\t\treturn;\n\t}\n\tif(scc_is_port_closed(dfcyc, port)) {\n\t\treturn;\t\t\t\t// Port is not open\n\t}\n\n\tscc_ptr->write_called_this_vbl = 1;\n\n\tif(cur_state == 0) {\n#if defined(_WIN32)\n\t\tscc_serial_win_empty_writebuf(port);\n#else\n\t\tscc_serial_unix_empty_writebuf(port);\n#endif\n\t} else if(cur_state >= 1) {\n\t\tscc_socket_empty_writebuf(dfcyc, port);\n\t}\n}\n\nvoid\nscc_try_fill_readbuf(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tint\tspace_used, space_left, cur_state;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tspace_used = scc_ptr->in_wrptr - scc_ptr->in_rdptr;\n\tif(space_used < 0) {\n\t\tspace_used += SCC_INBUF_SIZE;\n\t}\n\tspace_left = (7*SCC_INBUF_SIZE/8) - space_used;\n\tif(space_left < 1) {\n\t\t/* Buffer is pretty full, don't try to get more */\n\t\treturn;\n\t}\n\n\tif(scc_is_port_closed(dfcyc, port)) {\n\t\treturn;\t\t\t\t// Port is not open\n\t}\n\n#if 0\n\tif(scc_ptr->read_called_this_vbl) {\n\t\treturn;\n\t}\n#endif\n\n\tscc_ptr->read_called_this_vbl = 1;\n\n\tcur_state = scc_ptr->cur_state;\n\tif(cur_state == 0) {\n#if defined(_WIN32)\n\t\tscc_serial_win_fill_readbuf(dfcyc, port, space_left);\n#else\n\t\tscc_serial_unix_fill_readbuf(dfcyc, port, space_left);\n#endif\n\t} else if(cur_state >= 1) {\n\t\tscc_socket_fill_readbuf(dfcyc, port, space_left);\n\t}\n}\n\nvoid\nscc_do_event(dword64 dfcyc, int type)\n{\n\tScc\t*scc_ptr;\n\tint\tport;\n\n\tport = type & 1;\n\ttype = (type >> 1);\n\n\tscc_ptr = &(g_scc[port]);\n\tif(type == SCC_BR_EVENT) {\n\t\t/* baud rate generator counted down to 0 */\n\t\tscc_ptr->br_event_pending = 0;\n\t\tscc_set_zerocnt_int(port);\n\t\tscc_maybe_br_event(dfcyc, port);\n\t} else if(type == SCC_TX_EVENT) {\n\t\tscc_ptr->tx_event_pending = 0;\n\t\tscc_ptr->tx_buf_empty = 1;\n\t\tscc_handle_tx_event(port);\n\t} else if(type == SCC_RX_EVENT) {\n\t\tscc_ptr->rx_event_pending = 0;\n\t\tscc_maybe_rx_event(dfcyc, port);\n\t\tscc_maybe_rx_int(port);\n\t} else {\n\t\thalt_printf(\"scc_do_event: %08x!\\n\", type);\n\t}\n\treturn;\n}\n\nvoid\nshow_scc_state()\n{\n\tScc\t*scc_ptr;\n\tint\ti, j;\n\n\tfor(i = 0; i < 2; i++) {\n\t\tscc_ptr = &(g_scc[i]);\n\t\tprintf(\"SCC port: %d\\n\", i);\n\t\tfor(j = 0; j < 16; j += 4) {\n\t\t\tprintf(\"Reg %2d-%2d: %02x %02x %02x %02x\\n\", j, j+3,\n\t\t\t\tscc_ptr->reg[j], scc_ptr->reg[j+1],\n\t\t\t\tscc_ptr->reg[j+2], scc_ptr->reg[j+3]);\n\t\t}\n\t\tprintf(\"state: %d, sockfd:%llx rdwrfd:%llx, win_com:%p, \"\n\t\t\t\"win_dcb:%p\\n\", scc_ptr->cur_state,\n\t\t\t(dword64)scc_ptr->sockfd, (dword64)scc_ptr->rdwrfd,\n\t\t\tscc_ptr->win_com_handle, scc_ptr->win_dcb_ptr);\n\t\tprintf(\"in_rdptr: %04x, in_wr:%04x, out_rd:%04x, out_wr:%04x\\n\",\n\t\t\tscc_ptr->in_rdptr, scc_ptr->in_wrptr,\n\t\t\tscc_ptr->out_rdptr, scc_ptr->out_wrptr);\n\t\tprintf(\"rx_queue_depth: %d, queue: %02x, %02x, %02x, %02x\\n\",\n\t\t\tscc_ptr->rx_queue_depth, scc_ptr->rx_queue[0],\n\t\t\tscc_ptr->rx_queue[1], scc_ptr->rx_queue[2],\n\t\t\tscc_ptr->rx_queue[3]);\n\t\tprintf(\"want_ints: rx:%d, tx:%d, zc:%d\\n\",\n\t\t\tscc_ptr->wantint_rx, scc_ptr->wantint_tx,\n\t\t\tscc_ptr->wantint_zerocnt);\n\t\tprintf(\"ev_pendings: rx:%d, tx:%d, br:%d\\n\",\n\t\t\tscc_ptr->rx_event_pending,\n\t\t\tscc_ptr->tx_event_pending,\n\t\t\tscc_ptr->br_event_pending);\n\t\tprintf(\"br_dcycs: %f, tx_dcycs: %f, rx_dcycs: %f\\n\",\n\t\t\tscc_ptr->br_dcycs, scc_ptr->tx_dcycs,\n\t\t\tscc_ptr->rx_dcycs);\n\t\tprintf(\"char_size: %d, baud_rate: %d, mode: %d\\n\",\n\t\t\tscc_ptr->char_size, scc_ptr->baud_rate,\n\t\t\tscc_ptr->mode);\n\t\tprintf(\"modem_state: %dtelnet_mode:%d iac:%d, \"\n\t\t\t\"modem_cmd_len:%d\\n\", scc_ptr->modem_state,\n\t\t\tscc_ptr->telnet_mode, scc_ptr->telnet_iac,\n\t\t\tscc_ptr->modem_cmd_len);\n\t\tprintf(\"telnet_loc_modes:%08x %08x, telnet_rem_motes:\"\n\t\t\t\"%08x %08x\\n\", scc_ptr->telnet_local_mode[0],\n\t\t\tscc_ptr->telnet_local_mode[1],\n\t\t\tscc_ptr->telnet_remote_mode[0],\n\t\t\tscc_ptr->telnet_remote_mode[1]);\n\t\tprintf(\"modem_mode:%08x plus_mode:%d, out_char_dfcyc:%016llx\\n\",\n\t\t\tscc_ptr->modem_mode, scc_ptr->modem_plus_mode,\n\t\t\tscc_ptr->out_char_dfcyc);\n\t}\n\n}\n\nword32\nscc_read_reg(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tword32\tret;\n\tint\tregnum;\n\n\tscc_ptr = &(g_scc[port]);\n\tscc_ptr->mode = 0;\n\tregnum = scc_ptr->reg_ptr;\n\n\t/* port 0 is channel A, port 1 is channel B */\n\tswitch(regnum) {\n\tcase 0:\n\tcase 4:\n\t\tret = 0x60;\t/* 0x44 = no dcd, no cts,0x6c = dcd ok, cts ok*/\n\t\tif(scc_ptr->dcd) {\n\t\t\tret |= 0x08;\n\t\t}\n\t\t//ret |= 0x8;\t/* HACK HACK */\n\t\tif(scc_ptr->rx_queue_depth) {\n\t\t\tret |= 0x01;\n\t\t}\n\t\tif(scc_ptr->tx_buf_empty) {\n\t\t\tret |= 0x04;\n\t\t}\n\t\tif(scc_ptr->br_is_zero) {\n\t\t\tret |= 0x02;\n\t\t}\n\t\t//printf(\"Read scc[%d] stat: %016llx : %02x dcd:%d\\n\", port,\n\t\t//\tdfcyc, ret, scc_ptr->dcd);\n\t\tbreak;\n\tcase 1:\n\tcase 5:\n\t\t/* HACK: residue codes not right */\n\t\tret = 0x07;\t/* all sent */\n\t\tbreak;\n\tcase 2:\n\tcase 6:\n\t\tif(port == 0) {\n\t\t\tret = scc_ptr->reg[2];\n\t\t} else {\t\t\t// Port B, read RR2B int stat\n\t\t\t// The TELNET.SYSTEM by Colin Leroy-Mira uses RR2B\n\t\t\tret = scc_do_read_rr2b() << 1;\n\t\t\tif(g_scc[0].reg[9] & 0x10) {\t// wr9 status high\n\t\t\t\t// Map bit 3->4, 2->5, 1->6\n\t\t\t\tret = ((ret << 1) & 0x10) |\n\t\t\t\t\t((ret << 3) & 0x20) |\n\t\t\t\t\t((ret << 5) & 0x40);\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase 3:\n\tcase 7:\n\t\tif(port == 0) {\n\t\t\tret = (g_irq_pending & 0x3f);\n\t\t} else {\n\t\t\tret = 0;\n\t\t}\n\t\tbreak;\n\tcase 8:\n\t\tret = scc_read_data(dfcyc, port);\n\t\tbreak;\n\tcase 9:\n\tcase 13:\n\t\tret = scc_ptr->reg[13];\n\t\tbreak;\n\tcase 10:\n\tcase 14:\n\t\tret = 0;\n\t\tbreak;\n\tcase 11:\n\tcase 15:\n\t\tret = scc_ptr->reg[15];\n\t\tbreak;\n\tcase 12:\n\t\tret = scc_ptr->reg[12];\n\t\tbreak;\n\tdefault:\n\t\thalt_printf(\"Tried reading c03%x with regnum: %d!\\n\", 8+port,\n\t\t\tregnum);\n\t\tret = 0;\n\t}\n\n\tscc_ptr->reg_ptr = 0;\n\tscc_printf(\"Read c03%x, rr%d, ret: %02x\\n\", 8+port, regnum, ret);\n\tdbg_log_info(dfcyc, 0, ret, (regnum << 20) | (0xc039 - port));\n\n\treturn ret;\n}\n\nvoid\nscc_write_reg(dword64 dfcyc, int port, word32 val)\n{\n\tScc\t*scc_ptr;\n\tword32\told_val, changed_bits, irq_mask;\n\tint\tregnum, mode, tmp1;\n\n\tscc_ptr = &(g_scc[port]);\n\tregnum = scc_ptr->reg_ptr & 0xf;\n\tmode = scc_ptr->mode;\n\n\tif(mode == 0) {\n\t\tif((val & 0xf0) == 0) {\n\t\t\t/* Set reg_ptr */\n\t\t\tscc_ptr->reg_ptr = val & 0xf;\n\t\t\tregnum = 0;\n\t\t\tscc_ptr->mode = 1;\n\t\t}\n\t} else {\n\t\tscc_ptr->reg_ptr = 0;\n\t\tscc_ptr->mode = 0;\n\t}\n\told_val = scc_ptr->reg[regnum];\n\tchanged_bits = (old_val ^ val) & 0xff;\n\n\tdbg_log_info(dfcyc, (mode << 16) | scc_ptr->reg_ptr,\n\t\t\t(changed_bits << 16) | val,\n\t\t\t(regnum << 20) | (0x1c039 - port));\n\n\t/* Set reg reg */\n\tswitch(regnum) {\n\tcase 0: /* wr0 */\n\t\ttmp1 = (val >> 3) & 0x7;\n\t\tswitch(tmp1) {\n\t\tcase 0x0:\n\t\tcase 0x1:\n\t\t\tbreak;\n\t\tcase 0x2:\t/* reset ext/status ints */\n\t\t\t/* should clear other ext ints */\n\t\t\tscc_clr_zerocnt_int(port);\n\t\t\tbreak;\n\t\tcase 0x5:\t/* reset tx int pending */\n\t\t\tscc_clr_tx_int(port);\n\t\t\tbreak;\n\t\tcase 0x6:\t/* reset rr1 bits */\n\t\t\tbreak;\n\t\tcase 0x7:\t/* reset highest pri int pending */\n\t\t\tirq_mask = g_irq_pending;\n\t\t\tif(port == 0) {\n\t\t\t\t/* Move SCC0 ints into SCC1 positions */\n\t\t\t\tirq_mask = irq_mask >> 3;\n\t\t\t}\n\t\t\tif(irq_mask & IRQ_PENDING_SCC1_RX) {\n\t\t\t\tscc_clr_rx_int(port);\n\t\t\t} else if(irq_mask & IRQ_PENDING_SCC1_TX) {\n\t\t\t\tscc_clr_tx_int(port);\n\t\t\t} else if(irq_mask & IRQ_PENDING_SCC1_ZEROCNT) {\n\t\t\t\tscc_clr_zerocnt_int(port);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 0x4:\t/* enable int on next rx char */\n\t\tdefault:\n\t\t\thalt_printf(\"Wr c03%x to wr0 of %02x, bad cmd cd:%x!\\n\",\n\t\t\t\t9-port, val, tmp1);\n\t\t}\n\t\ttmp1 = (val >> 6) & 0x3;\n\t\tswitch(tmp1) {\n\t\tcase 0x0:\t/* null code */\n\t\t\tbreak;\n\t\tcase 0x1:\t/* reset rx crc */\n\t\tcase 0x2:\t/* reset tx crc */\n\t\t\tprintf(\"Wr c03%x to wr0 of %02x!\\n\", 9-port, val);\n\t\t\tbreak;\n\t\tcase 0x3:\t/* reset tx underrun/eom latch */\n\t\t\t/* if no extern status pending, or being reset now */\n\t\t\t/*  and tx disabled, ext int with tx underrun */\n\t\t\t/* ah, just do nothing */\n\t\t\tbreak;\n\t\t}\n\t\treturn;\n\tcase 1: /* wr1 */\n\t\t/* proterm sets this == 0x10, which is int on all rx */\n\t\tscc_ptr->reg[regnum] = val;\n\t\treturn;\n\tcase 2: /* wr2 */\n\t\t/* All values do nothing, let 'em all through! */\n\t\tscc_ptr->reg[regnum] = val;\n\t\treturn;\n\tcase 3: /* wr3 */\n\t\tif((val & 0x1e) != 0x0) {\n\t\t\thalt_printf(\"Wr c03%x to wr3 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\treturn;\n\tcase 4: /* wr4 */\n\t\tif((val & 0x30) != 0x00 || (val & 0x0c) == 0) {\n\t\t\thalt_printf(\"Wr c03%x to wr4 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\tif(changed_bits) {\n\t\t\tscc_regen_clocks(port);\n\t\t}\n\t\treturn;\n\tcase 5: /* wr5 */\n\t\tif((val & 0x15) != 0x0) {\n\t\t\thalt_printf(\"Wr c03%x to wr5 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\tif(changed_bits & 0x60) {\n\t\t\tscc_regen_clocks(port);\n\t\t}\n\t\tif(changed_bits & 0x80) {\n\t\t\tscc_printf(\"SCC port %d DTR:%d\\n\", port, val & 0x80);\n\t\t}\n\t\treturn;\n\tcase 6: /* wr6 */\n\t\tif(val != 0) {\n\t\t\thalt_printf(\"Wr c03%x to wr6 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\treturn;\n\tcase 7: /* wr7 */\n\t\tif(val != 0) {\n\t\t\thalt_printf(\"Wr c03%x to wr7 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\treturn;\n\tcase 8: /* wr8 */\n\t\tscc_write_data(dfcyc, port, val);\n\t\treturn;\n\tcase 9: /* wr9 */\n\t\tif((val & 0xc0)) {\n\t\t\tif(val & 0x80) {\n\t\t\t\tscc_reset_port(0);\n\t\t\t}\n\t\t\tif(val & 0x40) {\n\t\t\t\tscc_reset_port(1);\n\t\t\t}\n\t\t\tif((val & 0xc0) == 0xc0) {\n\t\t\t\tscc_hard_reset_port(0);\n\t\t\t\tscc_hard_reset_port(1);\n\t\t\t}\n\t\t}\n\t\t// Bit 5 is software interrupt ack, which does not exist on NMOS\n\t\t// Bit 2 sets IEO pin low, which doesn't exist either\n\t\told_val = g_scc[0].reg[9];\n\t\tg_scc[0].reg[9] = val;\n\t\tscc_evaluate_ints(0);\n\t\tscc_evaluate_ints(1);\n\t\treturn;\n\tcase 10: /* wr10 */\n\t\tif((val & 0xff) != 0x00) {\n\t\t\tprintf(\"Wr c03%x to wr10 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\treturn;\n\tcase 11: /* wr11 */\n\t\tscc_ptr->reg[regnum] = val;\n\t\tif(changed_bits) {\n\t\t\tscc_regen_clocks(port);\n\t\t}\n\t\treturn;\n\tcase 12: /* wr12 */\n\t\tscc_ptr->reg[regnum] = val;\n\t\tif(changed_bits) {\n\t\t\tscc_regen_clocks(port);\n\t\t}\n\t\treturn;\n\tcase 13: /* wr13 */\n\t\tscc_ptr->reg[regnum] = val;\n\t\tif(changed_bits) {\n\t\t\tscc_regen_clocks(port);\n\t\t}\n\t\treturn;\n\tcase 14: /* wr14 */\n\t\tval = val | (old_val & (~0xffU));\n\t\tswitch((val >> 5) & 0x7) {\n\t\tcase 0x0:\t// Null command (change nothing)\n\t\t\tbreak;\n\t\tcase 0x1:\t// Enter search mode\n\t\tcase 0x2:\t// Reset missing clock\n\t\tcase 0x3:\t// Disable PLL\n\t\tcase 0x6:\t// Set FM mode\n\t\tcase 0x7:\t// Set NRZI mode\n\t\t\t// Disable the PLL effectively\n\t\t\tval = val & 0xff;\t// Clear all upper bits\n\t\t\tbreak;\n\t\tcase 0x4:\t// DPLL source is BR gen\n\t\t\tval = (val & 0xff) | SCC_R14_DPLL_SOURCE_BRG;\n\t\t\tbreak;\n\t\tcase 0x5:\t// DPLL source is RTxC\n\t\t\tval = (val & 0xff) | SCC_R14_DPLL_SOURCE_RTXC;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thalt_printf(\"Wr c03%x to wr14 of %02x, bad dpll cd!\\n\",\n\t\t\t\t8+port, val);\n\t\t}\n\t\tif((val & 0x0c) != 0x0) {\n\t\t\thalt_printf(\"Wr c03%x to wr14 of %02x!\\n\", 8+port, val);\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\tif(changed_bits || (val != old_val)) {\n\t\t\tscc_regen_clocks(port);\n\t\t}\n\t\tscc_maybe_br_event(dfcyc, port);\n\t\treturn;\n\tcase 15: /* wr15 */\n\t\t/* ignore all accesses since IIgs self test messes with it */\n\t\tif((val & 0xff) != 0x0) {\n\t\t\tscc_printf(\"Write c03%x to wr15 of %02x!\\n\", 8+port,\n\t\t\t\tval);\n\t\t}\n\t\tif((g_scc[0].reg[9] & 0x8) && (val != 0)) {\n\t\t\tprintf(\"Write wr15:%02x and master int en = 1!\\n\",val);\n\t\t\t/* set_halt(1); */\n\t\t}\n\t\tscc_ptr->reg[regnum] = val;\n\t\tscc_maybe_br_event(dfcyc, port);\n\t\tscc_evaluate_ints(port);\n\t\treturn;\n\tdefault:\n\t\thalt_printf(\"Wr c03%x to wr%d of %02x!\\n\", 8+port, regnum, val);\n\t\treturn;\n\t}\n}\n\n// scc_read_data: Read from 0xc03b or 0xc03a\nword32\nscc_read_data(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tword32\tret;\n\tint\tdepth;\n\tint\ti;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tscc_try_fill_readbuf(dfcyc, port);\n\n\tdepth = scc_ptr->rx_queue_depth;\n\n\tret = 0;\n\tif(depth != 0) {\n\t\tret = scc_ptr->rx_queue[0];\n\t\tfor(i = 1; i < depth; i++) {\n\t\t\tscc_ptr->rx_queue[i-1] = scc_ptr->rx_queue[i];\n\t\t}\n\t\tscc_ptr->rx_queue_depth = depth - 1;\n\t\tscc_maybe_rx_event(dfcyc, port);\n\t\tscc_maybe_rx_int(port);\n\t}\n\n\tscc_printf(\"SCC read %04x: ret %02x, depth:%d\\n\", 0xc03b-port, ret,\n\t\t\tdepth);\n\n\tdbg_log_info(dfcyc, 0, ret, 0xc03b - port);\n\n\treturn ret;\n}\n\nvoid\nscc_write_data(dword64 dfcyc, int port, word32 val)\n{\n\tScc\t*scc_ptr;\n\n\tscc_printf(\"SCC write %04x: %02x\\n\", 0xc03b-port, val);\n\tdbg_log_info(dfcyc, val, 0, 0x1c03b - port);\n\n\tscc_ptr = &(g_scc[port]);\n\tif(scc_ptr->reg[14] & 0x10) {\n\t\t/* local loopback! */\n\t\tscc_add_to_readbuf(dfcyc, port, val);\n\t} else {\n\t\tscc_transmit(dfcyc, port, val);\n\t}\n\tscc_try_to_empty_writebuf(dfcyc, port);\n\n\tscc_maybe_tx_event(dfcyc, port);\n}\n\nword32\nscc_do_read_rr2b()\n{\n\tword32\tval;\n\n\tval = g_irq_pending & 0x3f;\n\tif(val == 0) {\n\t\treturn 3;\t// 011 if no interrupts pending\n\t}\n\t// Do Channel A first.  Priority order from SCC documentation\n\tif(val & IRQ_PENDING_SCC0_RX) {\n\t\treturn 6;\t// 110 Ch A Rx char available\n\t}\n\tif(val & IRQ_PENDING_SCC0_TX) {\n\t\treturn 4;\t// 100 Ch A Tx buffer empty\n\t}\n\tif(val & IRQ_PENDING_SCC0_ZEROCNT) {\n\t\treturn 5;\t// 101 Ch A External/Status change\n\t}\n\tif(val & IRQ_PENDING_SCC1_RX) {\n\t\treturn 2;\t// 010 Ch B Rx char available\n\t}\n\tif(val & IRQ_PENDING_SCC1_TX) {\n\t\treturn 0;\t// 000 Ch B Tx buffer empty\n\t}\n\tif(val & IRQ_PENDING_SCC1_ZEROCNT) {\n\t\treturn 1;\t// 001 Ch B External/Status change\n\t}\n\n\treturn 3;\n}\n\nvoid\nscc_maybe_br_event(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tdouble\tbr_dcycs;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(((scc_ptr->reg[14] & 0x01) == 0) || scc_ptr->br_event_pending) {\n\t\treturn;\n\t}\n\t/* also, if ext ints not enabled, don't do baud rate ints */\n\tif(((scc_ptr->reg[15] & 0x02) == 0) || ((scc_ptr->reg[9] & 8) == 0)) {\n\t\treturn;\n\t}\n\n\tbr_dcycs = scc_ptr->br_dcycs;\n\tif(br_dcycs < 1.0) {\n\t\thalt_printf(\"br_dcycs: %f!\\n\", br_dcycs);\n#if 0\n\t\tprintf(\"br_dcycs: %f!\\n\", br_dcycs);\n\t\tdbg_log_info(dfcyc, (word32)(br_dcycs * 65536),\n\t\t\t(scc_ptr->reg[15] << 24) | (scc_ptr->reg[14] << 16) |\n\t\t\t(scc_ptr->reg[13] << 8) | scc_ptr->reg[12], 0xdc1c);\n\t\tdbg_log_info(dfcyc,\n\t\t\t(scc_ptr->reg[11] << 24) | (scc_ptr->reg[10] << 16) |\n\t\t\t(scc_ptr->reg[9] << 8) | scc_ptr->reg[5],\n\t\t\t(scc_ptr->reg[4] << 24) | (scc_ptr->reg[3] << 16) |\n\t\t\t(scc_ptr->reg[1] << 8) | scc_ptr->reg[0], 0xdc1b);\n#endif\n\t}\n\n\tscc_ptr->br_event_pending = 1;\n\tadd_event_scc(dfcyc + (dword64)(br_dcycs * 65536.0),\n\t\t\t\t\tSCC_MAKE_EVENT(port, SCC_BR_EVENT));\n}\n\nvoid\nscc_evaluate_ints(int port)\n{\n\tScc\t*scc_ptr;\n\tword32\tirq_add_mask, irq_remove_mask;\n\tint\tmie;\n\n\tscc_ptr = &(g_scc[port]);\n\tmie = g_scc[0].reg[9] & 0x8;\t\t\t/* Master int en */\n\n\tif(!mie) {\n\t\t/* There can be no interrupts if MIE=0 */\n\t\tremove_irq(IRQ_PENDING_SCC1_RX | IRQ_PENDING_SCC1_TX |\n\t\t\t\t\t\tIRQ_PENDING_SCC1_ZEROCNT |\n\t\t\tIRQ_PENDING_SCC0_RX | IRQ_PENDING_SCC0_TX |\n\t\t\t\t\t\tIRQ_PENDING_SCC0_ZEROCNT);\n\t\treturn;\n\t}\n\n\tirq_add_mask = 0;\n\tirq_remove_mask = 0;\n\tif(scc_ptr->wantint_rx) {\n\t\tirq_add_mask |= IRQ_PENDING_SCC1_RX;\n\t} else {\n\t\tirq_remove_mask |= IRQ_PENDING_SCC1_RX;\n\t}\n\tif(scc_ptr->wantint_tx) {\n\t\tirq_add_mask |= IRQ_PENDING_SCC1_TX;\n\t} else {\n\t\tirq_remove_mask |= IRQ_PENDING_SCC1_TX;\n\t}\n\tif(scc_ptr->wantint_zerocnt) {\n\t\tirq_add_mask |= IRQ_PENDING_SCC1_ZEROCNT;\n\t} else {\n\t\tirq_remove_mask |= IRQ_PENDING_SCC1_ZEROCNT;\n\t}\n\tif(port == 0) {\n\t\t/* Port 1 is in bits 0-2 and port 0 is in bits 3-5 */\n\t\tirq_add_mask = irq_add_mask << 3;\n\t\tirq_remove_mask = irq_remove_mask << 3;\n\t}\n\tif(irq_add_mask) {\n\t\tadd_irq(irq_add_mask);\n\t}\n\tif(irq_remove_mask) {\n\t\tremove_irq(irq_remove_mask);\n\t}\n}\n\nvoid\nscc_maybe_rx_event(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tdouble\trx_dcycs;\n\tint\tin_rdptr, in_wrptr, depth;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->rx_event_pending) {\n\t\t/* one pending already, wait for the event to arrive */\n\t\treturn;\n\t}\n\n\tin_rdptr = scc_ptr->in_rdptr;\n\tin_wrptr = scc_ptr->in_wrptr;\n\tdepth = scc_ptr->rx_queue_depth;\n\tif((in_rdptr == in_wrptr) || (depth >= 3)) {\n\t\t/* no more chars or no more space, just get out */\n\t\treturn;\n\t}\n\n\tif(depth < 0) {\n\t\tdepth = 0;\n\t}\n\n\t/* pull char from in_rdptr into queue */\n\tscc_ptr->rx_queue[depth] = scc_ptr->in_buf[in_rdptr];\n\tscc_ptr->in_rdptr = (in_rdptr + 1) & (SCC_INBUF_SIZE - 1);\n\tscc_ptr->rx_queue_depth = depth + 1;\n\tscc_maybe_rx_int(port);\n\trx_dcycs = scc_ptr->rx_dcycs;\n\tscc_ptr->rx_event_pending = 1;\n\tadd_event_scc(dfcyc + (dword64)(rx_dcycs*65536.0),\n\t\t\t\t\tSCC_MAKE_EVENT(port, SCC_RX_EVENT));\n}\n\nvoid\nscc_maybe_rx_int(int port)\n{\n\tScc\t*scc_ptr;\n\tint\tdepth;\n\tint\trx_int_mode;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tdepth = scc_ptr->rx_queue_depth;\n\tif(depth <= 0) {\n\t\t/* no more chars, just get out */\n\t\tscc_clr_rx_int(port);\n\t\treturn;\n\t}\n\trx_int_mode = (scc_ptr->reg[1] >> 3) & 0x3;\n\tif((rx_int_mode == 1) || (rx_int_mode == 2)) {\n\t\tscc_ptr->wantint_rx = 1;\n\t}\n\tscc_evaluate_ints(port);\n}\n\nvoid\nscc_clr_rx_int(int port)\n{\n\tg_scc[port].wantint_rx = 0;\n\tscc_evaluate_ints(port);\n}\n\nvoid\nscc_handle_tx_event(int port)\n{\n\tScc\t*scc_ptr;\n\tint\ttx_int_mode;\n\n\tscc_ptr = &(g_scc[port]);\n\n\t/* nothing pending, see if ints on */\n\ttx_int_mode = (scc_ptr->reg[1] & 0x2);\n\tif(tx_int_mode) {\n\t\tscc_ptr->wantint_tx = 1;\n\t}\n\tscc_evaluate_ints(port);\n}\n\nvoid\nscc_maybe_tx_event(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tdouble\ttx_dcycs;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->tx_event_pending) {\n\t\t/* one pending already, tx_buf is full */\n\t\tscc_ptr->tx_buf_empty = 0;\n\t} else {\n\t\t/* nothing pending, see ints on */\n\t\tscc_ptr->tx_buf_empty = 0;\n\t\tscc_evaluate_ints(port);\n\t\ttx_dcycs = scc_ptr->tx_dcycs;\n\t\tscc_ptr->tx_event_pending = 1;\n\t\tadd_event_scc(dfcyc + (dword64)(tx_dcycs * 65536.0),\n\t\t\t\tSCC_MAKE_EVENT(port, SCC_TX_EVENT));\n\t}\n}\n\nvoid\nscc_clr_tx_int(int port)\n{\n\tg_scc[port].wantint_tx = 0;\n\tscc_evaluate_ints(port);\n}\n\nvoid\nscc_set_zerocnt_int(int port)\n{\n\tScc\t*scc_ptr;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->reg[15] & 0x2) {\n\t\tscc_ptr->wantint_zerocnt = 1;\n\t}\n\tscc_evaluate_ints(port);\n}\n\nvoid\nscc_clr_zerocnt_int(int port)\n{\n\tg_scc[port].wantint_zerocnt = 0;\n\tscc_evaluate_ints(port);\n}\n\nvoid\nscc_add_to_readbuf(dword64 dfcyc, int port, word32 val)\n{\n\tScc\t*scc_ptr;\n\tint\tin_wrptr, in_wrptr_next, in_rdptr, safe_val;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif((scc_ptr->reg[5] & 0x60) != 0x60) {\t// HACK: this is tx char size!\n\t\tval = val & 0x7f;\n\t}\n\tin_wrptr = scc_ptr->in_wrptr;\n\tin_rdptr = scc_ptr->in_rdptr;\n\tin_wrptr_next = (in_wrptr + 1) & (SCC_INBUF_SIZE - 1);\n\tif(in_wrptr_next != in_rdptr) {\n\t\tscc_ptr->in_buf[in_wrptr] = val;\n\t\tscc_ptr->in_wrptr = in_wrptr_next;\n\t\tsafe_val = val & 0x7f;\n\t\tif((safe_val < 0x20) || (safe_val >= 0x7f)) {\n\t\t\tsafe_val = '.';\n\t\t}\n\t\tscc_printf(\"scc in port[%d] add 0x%02x (%c), %d,%d != %d\\n\",\n\t\t\tport, val, safe_val, in_wrptr, in_wrptr_next, in_rdptr);\n\t\tg_scc_overflow = 0;\n\t} else {\n\t\tif(g_scc_overflow == 0) {\n\t\t\tg_code_yellow++;\n\t\t\tprintf(\"scc inbuf overflow port %d\\n\", port);\n\t\t}\n\t\tg_scc_overflow = 1;\n\t}\n\n\tscc_maybe_rx_event(dfcyc, port);\n\tscc_maybe_rx_int(port);\n}\n\nvoid\nscc_add_to_readbufv(dword64 dfcyc, int port, const char *fmt, ...)\n{\n\tva_list\tap;\n\tchar\t*bufptr;\n\tint\tlen, c;\n\tint\ti;\n\n\tva_start(ap, fmt);\n\tbufptr = malloc(4096);\n\tbufptr[0] = 0;\n\tvsnprintf(bufptr, 4090, fmt, ap);\n\tlen = (int)strlen(bufptr);\n\tfor(i = 0; i < len; i++) {\n\t\tc = bufptr[i];\n\t\tif(c == 0x0a) {\n\t\t\tscc_add_to_readbuf(dfcyc, port, 0x0d);\n\t\t}\n\t\tscc_add_to_readbuf(dfcyc, port, c);\n\t}\n\tva_end(ap);\n}\n\nvoid\nscc_transmit(dword64 dfcyc, int port, word32 val)\n{\n\tScc\t*scc_ptr;\n\tint\tout_wrptr, out_rdptr;\n\n\tscc_ptr = &(g_scc[port]);\n\n\t// printf(\"scc_transmit port:%d val:%02x \\n\", port, val);\n\t/* See if port initialized, if not, do so now */\n\tif(scc_is_port_closed(dfcyc, port)) {\n\t\tprintf(\"  port %d is closed, cur_state:%d\\n\", port,\n\t\t\t\tscc_ptr->cur_state);\n\t\treturn;\t\t// No working serial port, just toss it and go\n\t}\n\tif(!scc_ptr->tx_buf_empty) {\n\t\t/* toss character! */\n\t\tprintf(\"Tossing char\\n\");\n\t\treturn;\n\t}\n\n\tout_wrptr = scc_ptr->out_wrptr;\n\tout_rdptr = scc_ptr->out_rdptr;\n\tif(scc_ptr->tx_dcycs < 1.0) {\n\t\tif(out_wrptr != out_rdptr) {\n\t\t\t/* do just one char, then get out */\n\t\t\tprintf(\"tx_dcycs < 1\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\tif(g_serial_mask[port] || (scc_ptr->reg[5] & 0x60) != 0x60) {\n\t\tval = val & 0x7f;\n\t}\n\n\tscc_add_to_writebuf(dfcyc, port, val);\n}\n\nvoid\nscc_add_to_writebuf(dword64 dfcyc, int port, word32 val)\n{\n\tScc\t*scc_ptr;\n\tint\tout_wrptr, out_wrptr_next, out_rdptr;\n\n\t// printf(\"scc_add_to_writebuf p:%d, val:%02x\\n\", port, val);\n\tif(scc_is_port_closed(dfcyc, port)) {\n\t\treturn;\t\t\t// Port is closed\n\t}\n\tscc_ptr = &(g_scc[port]);\n\n\tout_wrptr = scc_ptr->out_wrptr;\n\tout_rdptr = scc_ptr->out_rdptr;\n\n\tout_wrptr_next = (out_wrptr + 1) & (SCC_OUTBUF_SIZE - 1);\n\tif(out_wrptr_next != out_rdptr) {\n\t\tscc_ptr->out_buf[out_wrptr] = val;\n\t\tscc_ptr->out_wrptr = out_wrptr_next;\n\t\tscc_printf(\"scc wrbuf port %d had char 0x%02x added\\n\",\n\t\t\tport, val);\n\t\tg_scc_overflow = 0;\n\t} else {\n\t\tif(g_scc_overflow == 0) {\n\t\t\tg_code_yellow++;\n\t\t\tprintf(\"scc outbuf overflow port %d\\n\", port);\n\t\t}\n\t\tg_scc_overflow = 1;\n\t}\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/scc.h",
    "content": "#ifdef INCLUDE_RCSID_C\nconst char rcsid_scc_h[] = \"@(#)$KmKId: scc.h,v 1.27 2025-01-11 18:45:06+00 kentd Exp $\";\n#endif\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2025 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include <ctype.h>\n\n#ifdef _WIN32\n# include <winsock2.h>\n#else\n# include <sys/socket.h>\n# include <netinet/in.h>\n# include <netdb.h>\n#endif\n\n#if defined(HPUX) || defined(__linux__) || defined(SOLARIS)\n# define SCC_SOCKETS\n#endif\n#if defined(MAC) || defined(__MACH__) || defined(_WIN32)\n# define SCC_SOCKETS\n#endif\n\n\n/* my scc port 0 == channel A, port 1 = channel B */\n\n#define\tSCC_INBUF_SIZE\t\t512\t\t/* must be a power of 2 */\n#define\tSCC_OUTBUF_SIZE\t\t512\t\t/* must be a power of 2 */\n\n#define SCC_MODEM_MAX_CMD_STR\t128\n\n#ifndef _WIN32\n# define SOCKET\t\t\tint\t\t/* For non-Windows */\n# define INVALID_SOCKET\t\t(-1)\t\t/* For non-Windows */\n#endif\n\nSTRUCT(Scc) {\n\tint\tcur_state;\n\tint\tmodem_state;\n\tSOCKET\tsockfd;\n\tSOCKET\trdwrfd;\n\tvoid\t*sockaddr_ptr;\t\t// Socket: pointer to sockaddr struct\n\tint\tsockaddr_size;\t\t// Socket: sizeof(sockaddr_in)\n\tint\tunix_dev_fd;\t\t// Unix fd to real serial device\n\tvoid\t*win_com_handle;\t// Win32 handle to COMx port\n\tvoid\t*win_dcb_ptr;\t\t// Win32 ptr to COMx DCB\n\tint\tread_called_this_vbl;\n\tint\twrite_called_this_vbl;\n\n\tbyte\tdcd;\n\tbyte\treg_ptr;\n\tbyte\tbr_is_zero;\n\tbyte\ttx_buf_empty;\n\tword32\tmode;\n\tbyte\treg[16];\n\n\tint\trx_queue_depth;\n\tbyte\trx_queue[4];\n\n\tint\tin_rdptr;\n\tint\tin_wrptr;\n\tbyte\tin_buf[SCC_INBUF_SIZE];\n\n\tint\tout_rdptr;\n\tint\tout_wrptr;\n\tbyte\tout_buf[SCC_OUTBUF_SIZE];\n\n\tint\twantint_rx;\n\tint\twantint_tx;\n\tint\twantint_zerocnt;\n\n\tdouble\tbr_dcycs;\n\tdouble\ttx_dcycs;\n\tdouble\trx_dcycs;\n\n\tint\tbr_event_pending;\n\tint\trx_event_pending;\n\tint\ttx_event_pending;\n\n\tint\tchar_size;\n\tint\tbaud_rate;\n\tdword64\tout_char_dfcyc;\n\n\tint\tsocket_error;\n\tint\tsocket_num_rings;\n\tdword64\tsocket_last_ring_dfcyc;\n\tword32\tmodem_mode;\n\tint\tmodem_plus_mode;\n\tint\tmodem_s0_val;\n\tint\tmodem_s2_val;\n\tint\ttelnet_mode;\n\tint\ttelnet_iac;\n\tword32\ttelnet_local_mode[2];\n\tword32\ttelnet_remote_mode[2];\n\tword32\ttelnet_reqwill_mode[2];\n\tword32\ttelnet_reqdo_mode[2];\n\tword32\tmodem_out_portnum;\n\tint\tmodem_cmd_len;\n\tbyte\tmodem_cmd_str[SCC_MODEM_MAX_CMD_STR + 5];\n};\n\n#define SCCMODEM_NOECHO\t\t0x0001\n#define SCCMODEM_NOVERBOSE\t0x0002\n\n"
  },
  {
    "path": "upstream/kegs/src/scc_socket_driver.c",
    "content": "const char rcsid_scc_socket_driver_c[] = \"@(#)$KmKId: scc_socket_driver.c,v 1.37 2025-04-29 22:17:38+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2025 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// This file contains the socket calls for Win32 and Mac/Linux\n// Win32: see: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/\n\n// Modem init string GBBS GSPORT.HST: ats0=1s2=128v0\n// Modem init string Warp6: atm0e0v0s0=0s7=25   atx4h0  at&c1&d2\n\n#include \"defc.h\"\n\n#include \"scc.h\"\n#include <signal.h>\n\nextern int Verbose;\nextern Scc g_scc[2];\nextern int g_serial_cfg[2];\nextern char *g_serial_remote_ip[2];\nextern int g_serial_remote_port[2];\nextern int g_serial_modem_response_code;\nextern int g_serial_modem_allow_incoming;\nextern int g_serial_modem_init_telnet;\n\n#ifdef _WIN32\n#include <winsock2.h>\ntypedef int socklen_t;\n#endif\n\n#ifndef _WIN32\nextern int h_errno;\n#endif\n\nint g_wsastartup_called = 0;\n\n#ifndef _WIN32\n// SOCKET is defined in scc.h, and is an \"int\" for non-Windows systems\n// INVALID_SOCKET is -1 for non-WINDOWS\n# define closesocket(s)\t\tclose(s)\n#endif\n\n/* Usage: scc_socket_open() called to init socket mode */\n/*  At all times, we try to have a listen running on the incoming socket */\n/* If we want to dial out, we close the incoming socket and create a new */\n/*  outgoing socket.  Any hang-up causes the socket to be closed and it will */\n/*  then re-open on a subsequent call to scc_socket_open */\n\nvoid\nscc_socket_open(dword64 dfcyc, int port, int cfg)\n{\n\tScc\t*scc_ptr;\n\n\t// printf(\"scc_socket_open p:%d cfg:%d\\n\", port, cfg);\n#if defined(_WIN32) && defined(SCC_SOCKETS)\n\tWSADATA\twsadata;\n\tint\tret;\n\n\tif(g_wsastartup_called == 0) {\n\t\tret = WSAStartup(MAKEWORD(2,0), &wsadata);\n\t\tprintf(\"WSAStartup ret: %d\\n\", ret);\n\t\tg_wsastartup_called = 1;\n\t}\n#endif\n\tscc_ptr = &(g_scc[port]);\n\tscc_ptr->cur_state = cfg;\t\t// successful socket\n\tscc_ptr->sockfd = INVALID_SOCKET; /* Indicate no socket open yet */\n\tscc_ptr->rdwrfd = INVALID_SOCKET; /* Indicate no socket open yet */\n\tscc_ptr->modem_state = 0;\t/* 0 means talk to socket */\n\t\t\t\t\t/* 1 means talk to modem */\n\tscc_ptr->socket_error = 0;\n\tscc_ptr->socket_num_rings = 0;\n\tscc_ptr->socket_last_ring_dfcyc = 0;\n\tscc_ptr->dcd = 1;\t\t/* 1 means carrier */\n\tscc_ptr->modem_s0_val = 0;\t\t// Number of rings before answer\n\tscc_ptr->sockaddr_size = sizeof(struct sockaddr_in);\n\tfree(scc_ptr->sockaddr_ptr);\n\tscc_ptr->sockaddr_ptr = malloc(scc_ptr->sockaddr_size);\n\n\t// cfg==1: Virtual Modem.  All set up, wait for AT commands now\n\t// cfg==2: Outgoing connection to g_serial_remote_ip[], do it now\n\t// cfg==3: Incoming connection on 6501/6502.  Do it now\n\tif(cfg == 2) {\n\t\tscc_ptr->modem_state = 0;\n\t\t// Do not open it now, it will be opened when output is\n\t\t//  sent to the port\n\t}\n\tif(cfg == 1) {\n\t\tscc_ptr->modem_state = 1;\n\t\tscc_ptr->dcd = 0;\n\t\tscc_socket_open_incoming(dfcyc, port);\n\t} else if(cfg == 3) {\n\t\tscc_ptr->modem_state = 0;\n\t\tscc_socket_open_incoming(dfcyc, port);\n\t}\n}\n\nvoid\nscc_socket_close(int port)\n{\n#ifdef SCC_SOCKETS\n\tScc\t*scc_ptr;\n\tSOCKET\trdwrfd;\n\tSOCKET\tsockfd;\n\tint\tdo_init;\n\tint\ti;\n\n\tscc_ptr = &(g_scc[port]);\n\n\t// printf(\"In scc_socket_close, %d\\n\", port);\n\n\trdwrfd = scc_ptr->rdwrfd;\n\tdo_init = 0;\n\tif(rdwrfd != INVALID_SOCKET) {\n\t\tprintf(\"socket_close: rdwrfd=%llx, closing\\n\", (dword64)rdwrfd);\n\t\tclosesocket(rdwrfd);\n\t\tdo_init = 1;\n\t}\n\tsockfd = scc_ptr->sockfd;\n\tif((sockfd != INVALID_SOCKET) && (rdwrfd != sockfd)) {\n\t\tprintf(\"socket_close: sockfd=%llx, closing\\n\", (dword64)sockfd);\n\t\tclosesocket(sockfd);\n\t\tdo_init = 1;\n\t}\n\n\tscc_ptr->modem_state = 0;\n\tscc_ptr->socket_num_rings = 0;\n\tif(scc_ptr->cur_state == 1) {\t\t\t// Virtual modem\n\t\tscc_ptr->modem_state = 1;\n\t\tscc_ptr->dcd = 0;\n\t}\n\tif(do_init) {\n\t\tscc_ptr->modem_cmd_len = 0;\n\t\tscc_ptr->telnet_iac = 0;\n\t\tfor(i = 0; i < 2; i++) {\n\t\t\tscc_ptr->telnet_local_mode[i] = 0;\n\t\t\tscc_ptr->telnet_remote_mode[i] = 0;\n\t\t\tscc_ptr->telnet_reqwill_mode[i] = 0;\n\t\t\tscc_ptr->telnet_reqdo_mode[i] = 0;\n\t\t}\n\t\tscc_ptr->rdwrfd = INVALID_SOCKET;\n\t\tscc_ptr->sockfd = INVALID_SOCKET;\n\t}\n#endif\n}\n\nvoid\nscc_socket_close_extended(dword64 dfcyc, int port, int allow_retry)\n{\n#ifdef SCC_SOCKETS\n\tScc\t*scc_ptr;\n\n\t// printf(\"In scc_socket_close_extended, %d, %016llx\\n\", port, dfcyc);\n\tscc_socket_close(port);\n\n\tscc_ptr = &(g_scc[port]);\n\tif(scc_ptr->cur_state == 1) {\t\t// Virtual modem mode\n\t\tscc_socket_send_modem_code(dfcyc, port, 3);\n\t\tscc_ptr->modem_state = 1;\t// and go back to modem mode\n\t} else if((scc_ptr->cur_state == 2) && !allow_retry) {\t// Remote IP\n\t\tscc_ptr->cur_state = -2;\t// Error, give up\n\t}\n#endif\n}\n\nvoid\nscc_socket_maybe_open(dword64 dfcyc, int port, int must)\n{\n\tScc\t*scc_ptr;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->sockfd != INVALID_SOCKET) {\n\t\t// Valid socket.  See if we should hang up\n\t\tif((scc_ptr->reg[5] & 0x80) && (scc_ptr->cur_state == 1)) {\n\t\t\t// Handle DTR forcing modem hang-up now\n\t\t\tprintf(\"DTR is deasserted, hanging up\\n\");\n\t\t\tscc_socket_close(port);\n\t\t}\n\t\treturn;\n\t}\n\tif(scc_ptr->cur_state == 2) {\n\t\tif(must) {\n\t\t\tscc_socket_open_outgoing(dfcyc, port,\n\t\t\t\t\tg_serial_remote_ip[port],\n\t\t\t\t\tg_serial_remote_port[port]);\n\t\t}\n\t} else {\n\t\tscc_socket_open_incoming(dfcyc, port);\n\t}\n}\n\nvoid\nscc_socket_open_incoming(dword64 dfcyc, int port)\n{\n#ifdef SCC_SOCKETS\n\tSOCKET\tsockfd;\n\tstruct sockaddr_in sa_in;\n\tScc\t*scc_ptr;\n\tint\ton, ret, inc;\n\n\tinc = 0;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->sockfd != INVALID_SOCKET) {\n\t\t/* it's already open, get out */\n\t\treturn;\n\t}\n\n\t//printf(\"scc_socket_open_incoming: scc socket close being called\\n\");\n\tscc_socket_close(port);\n\n\tscc_ptr->socket_num_rings = 0;\n\n\tmemset(scc_ptr->sockaddr_ptr, 0, scc_ptr->sockaddr_size);\n\n\tif(scc_ptr->cur_state == 2) {\n\t\t// Outgoing connection only, never accept an incoming connect\n\t\treturn;\n\t}\n\tif(scc_ptr->cur_state == 1) {\n\t\tif(!g_serial_modem_allow_incoming) {\n\t\t\t// Virtual modem with incoming connections disallowed\n\t\t\treturn;\n\t\t}\n\t\tif(scc_ptr->reg[5] & 0x80) {\n\t\t\t// DTR is forcing hang-up.  Don't open incoming socket\n\t\t\treturn;\n\t\t}\n\t}\n\n\twhile(1) {\n\t\tsockfd = socket(AF_INET, SOCK_STREAM, 0);\n\t\t//printf(\"sockfd ret: %llx\\n\", (dword64)sockfd);\n\t\tif(sockfd == INVALID_SOCKET) {\n\t\t\tprintf(\"socket ret: -1, errno: %d\\n\", errno);\n\t\t\tscc_socket_close(port);\n\t\t\tscc_ptr->socket_error = -1;\n\t\t\treturn;\n\t\t}\n\t\t/* printf(\"socket ret: %d\\n\", sockfd); */\n\n\t\ton = 1;\n\t\tret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,\n\t\t\t\t\t(char *)&on, sizeof(on));\n\t\tif(ret < 0) {\n\t\t\tprintf(\"setsockopt REUSEADDR ret: %d, err:%d\\n\",\n\t\t\t\tret, errno);\n\t\t\tscc_socket_close(port);\n\t\t\treturn;\n\t\t}\n\n\t\tmemset(&sa_in, 0, sizeof(sa_in));\n\t\tsa_in.sin_family = AF_INET;\n\t\tsa_in.sin_port = htons(6501 + port + inc);\n\t\tsa_in.sin_addr.s_addr = htonl(INADDR_ANY);\n\n\t\tret = bind(sockfd, (struct sockaddr *)&sa_in, sizeof(sa_in));\n\t\tprintf(\"bind ret:%d\\n\", ret);\n\n\t\tif(ret >= 0) {\n\t\t\tret = listen(sockfd, 1);\n\t\t\tbreak;\n\t\t}\n\t\t/* else ret to bind was < 0 */\n\t\tprintf(\"bind or listen ret: %d, errno: %d\\n\", ret, errno);\n\t\tinc++;\n\t\tclosesocket(sockfd);\n\t\tprintf(\"Trying next port: %d\\n\", 6501 + port + inc);\n\t\tif(inc >= 10) {\n\t\t\tprintf(\"Too many retries, quitting\\n\");\n\t\t\tscc_socket_close(port);\n\t\t\tscc_ptr->socket_error = -1;\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprintf(\"SCC port %d is at unix port %d\\n\", port, 6501 + port + inc);\n\n\tscc_ptr->sockfd = sockfd;\n\tscc_ptr->socket_error = 0;\n\n\tscc_socket_make_nonblock(dfcyc, port);\n#endif\n}\n\nvoid\nscc_socket_open_outgoing(dword64 dfcyc, int port, const char *remote_ip_str,\n\t\t\t\t\t\tint remote_port)\n{\n#ifdef SCC_SOCKETS\n\tScc\t*scc_ptr;\n\tstruct sockaddr_in sa_in;\n\tstruct hostent *hostentptr;\n\tint\ton;\n\tint\tret;\n\tSOCKET\tsockfd;\n\n\tscc_ptr = &(g_scc[port]);\n\n\t// printf(\"scc socket close being called from socket_open_out\\n\");\n\tscc_socket_close(port);\n\n\tmemset(scc_ptr->sockaddr_ptr, 0, scc_ptr->sockaddr_size);\n\n\tsockfd = socket(AF_INET, SOCK_STREAM, 0);\n\t// printf(\"outgoing sockfd ret: %llx\\n\", (dword64)sockfd);\n\tif(sockfd == INVALID_SOCKET) {\n\t\tprintf(\"socket ret: %llx, errno: %d\\n\", (dword64)sockfd, errno);\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\treturn;\n\t}\n\t/* printf(\"socket ret: %d\\n\", sockfd); */\n\n\ton = 1;\n\tret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,\n\t\t\t\t\t(char *)&on, sizeof(on));\n\tif(ret < 0) {\n\t\tprintf(\"setsockopt REUSEADDR ret: %d, err:%d\\n\",\n\t\t\tret, errno);\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\treturn;\n\t}\n\n\tmemset(&sa_in, 0, sizeof(sa_in));\n\tsa_in.sin_family = AF_INET;\n\tsa_in.sin_port = htons(remote_port);\n\twhile(*remote_ip_str == ' ') {\n\t\tremote_ip_str++;\t\t// Skip leading blanks\n\t}\n\thostentptr = gethostbyname(remote_ip_str);\n\tprintf(\"Connecting to %s, port:%d\\n\", remote_ip_str, remote_port);\n\tif(hostentptr == 0) {\n#ifdef _WIN32\n\t\tfatal_printf(\"Lookup host %s failed\\n\",\n\t\t\t\t\t\t&scc_ptr->modem_cmd_str[0]);\n#else\n\t\tfatal_printf(\"Lookup host %s failed, herrno: %d\\n\",\n\t\t\t\t\t&scc_ptr->modem_cmd_str[0], h_errno);\n#endif\n\t\tclosesocket(sockfd);\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\treturn;\n\t}\n\tmemcpy(&sa_in.sin_addr.s_addr, hostentptr->h_addr,\n\t\t\t\t\t\t\thostentptr->h_length);\n\t/* The above copies the 32-bit internet address into */\n\t/*  sin_addr.s_addr.  It's in correct network format */\n\n\tret = connect(sockfd, (struct sockaddr *)&sa_in, sizeof(sa_in));\n\tif(ret < 0) {\n\t\tprintf(\"connect ret: %d, errno: %d\\n\", ret, errno);\n\t\tclosesocket(sockfd);\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\treturn;\n\t}\n\tscc_socket_modem_connect(dfcyc, port);\n\tscc_ptr->dcd = 1;\t\t/* carrier on */\n\tscc_ptr->modem_state = 0;\t/* talk to socket */\n\tscc_ptr->socket_num_rings = 0;\n\n\tprintf(\"SCC port %d is now outgoing to %s:%d\\n\", port, remote_ip_str,\n\t\t\t\t\t\t\tremote_port);\n\n\tscc_ptr->sockfd = sockfd;\n\n\tscc_socket_make_nonblock(dfcyc, port);\n\tscc_ptr->rdwrfd = scc_ptr->sockfd;\n#endif\n}\n\nvoid\nscc_socket_make_nonblock(dword64 dfcyc, int port)\n{\n#ifdef SCC_SOCKETS\n\tScc\t*scc_ptr;\n\tSOCKET\tsockfd;\n\tint\tret;\n# ifdef _WIN32\n\tu_long\tflags;\n# else\n\tint\tflags;\n# endif\n\n\tscc_ptr = &(g_scc[port]);\n\tsockfd = scc_ptr->sockfd;\n\n# ifdef _WIN32\n\tflags = 1;\n\tret = ioctlsocket(sockfd, FIONBIO, &flags);\n\tif(ret != 0) {\n\t\tprintf(\"ioctlsocket ret: %d\\n\", ret);\n\t}\n# else\n\tflags = fcntl(sockfd, F_GETFL, 0);\n\tif(flags == -1) {\n\t\tprintf(\"fcntl GETFL ret: %d, errno: %d\\n\", flags, errno);\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\treturn;\n\t}\n\tret = fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);\n\tif(ret == -1) {\n\t\tprintf(\"fcntl SETFL ret: %d, errno: %d\\n\", ret, errno);\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\treturn;\n\t}\n# endif\n#endif\n}\n\nvoid\nscc_accept_socket(dword64 dfcyc, int port)\n{\n#ifdef SCC_SOCKETS\n\tScc\t*scc_ptr;\n\tSOCKET\trdwrfd;\n\tsocklen_t address_len;\n\tint\tflags, num_rings, ret;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->sockfd == INVALID_SOCKET) {\n\t\t// printf(\"in accept_socket, call socket_open\\n\");\n\t\tscc_socket_open_incoming(dfcyc, port);\n\t}\n\tif(scc_ptr->sockfd == INVALID_SOCKET) {\n\t\treturn;\t\t/* just give up */\n\t}\n\tif(scc_ptr->rdwrfd == INVALID_SOCKET) {\n\t\taddress_len = scc_ptr->sockaddr_size;\n\t\trdwrfd = accept(scc_ptr->sockfd, scc_ptr->sockaddr_ptr,\n\t\t\t\t\t\t\t\t&address_len);\n\t\tscc_ptr->sockaddr_size = (int)address_len;\n\t\tif(rdwrfd == INVALID_SOCKET) {\n\t\t\treturn;\n\t\t}\n\n\t\tflags = 0;\n\t\tret = 0;\n#ifndef _WIN32\n\t\t/* For Linux, we need to set O_NONBLOCK on the rdwrfd */\n\t\tflags = fcntl(rdwrfd, F_GETFL, 0);\n\t\tif(flags == -1) {\n\t\t\tprintf(\"fcntl GETFL ret: %d, errno: %d\\n\", flags,errno);\n\t\t\treturn;\n\t\t}\n\t\tret = fcntl(rdwrfd, F_SETFL, flags | O_NONBLOCK);\n\t\tif(ret == -1) {\n\t\t\tprintf(\"fcntl SETFL ret: %d, errno: %d\\n\", ret, errno);\n\t\t\treturn;\n\t\t}\n#endif\n\t\tscc_ptr->rdwrfd = rdwrfd;\n\t\tprintf(\"Set port[%d].rdwrfd = %llx\\n\", port, (dword64)rdwrfd);\n\n\t\tnum_rings = 4;\n\t\tif(scc_ptr->modem_s0_val > 0) {\n\t\t\tnum_rings = scc_ptr->modem_s0_val;\n\t\t}\n\t\tscc_ptr->socket_num_rings = num_rings;\n\t\tscc_ptr->socket_last_ring_dfcyc = 0;\t/* do ring now*/\n\n\t\t/* and send some telnet codes */\n\t\tif(g_serial_modem_init_telnet) {\n\t\t\tscc_ptr->telnet_reqwill_mode[0] = 0xa;\n\t\t\tscc_ptr->telnet_reqdo_mode[0] = 0xa;\n\t\t\t\t/* 3=GO_AH and 1=ECHO */\n\t\t\tscc_ptr->telnet_reqdo_mode[1] = 0x4;\t// 34=LINEMODE\n\t\t}\n\t\tprintf(\"Telnet reqwill and reqdo's initialized: %08x_%08x,\"\n\t\t\t\t\"%08x_%08x\\n\",\n\t\t\t\tscc_ptr->telnet_reqwill_mode[1],\n\t\t\t\tscc_ptr->telnet_reqwill_mode[0],\n\t\t\t\tscc_ptr->telnet_reqdo_mode[1],\n\t\t\t\tscc_ptr->telnet_reqdo_mode[0]);\n\n\t\tscc_socket_modem_do_ring(dfcyc, port);\n\t}\n#endif\n}\n\nvoid\nscc_socket_telnet_reqs(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tword32\tmask, willmask, domask;\n\tint\ti, j;\n\n\tscc_ptr = &(g_scc[port]);\n\tfor(i = 0; i < 64; i++) {\n\t\tj = i >> 5;\n\t\tmask = 1 << (i & 31);\n\t\twillmask = scc_ptr->telnet_reqwill_mode[j];\n\t\tif(willmask & mask) {\n\t\t\tscc_printf(\"Telnet reqwill %d\\n\", i);\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xff);\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xfb);\t/* WILL */\n\t\t\tscc_add_to_writebuf(dfcyc, port, i);\n\t\t}\n\t\tdomask = scc_ptr->telnet_reqdo_mode[j];\n\t\tif(domask & mask) {\n\t\t\tscc_printf(\"Telnet reqdo %d\\n\", i);\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xff);\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xfd);\t/* DO */\n\t\t\tscc_add_to_writebuf(dfcyc, port, i);\n\t\t}\n\t}\n}\n\nvoid\nscc_socket_fill_readbuf(dword64 dfcyc, int port, int space_left)\n{\n#ifdef SCC_SOCKETS\n\tbyte\ttmp_buf[256];\n\tScc\t*scc_ptr;\n\tSOCKET\trdwrfd;\n\tint\tret;\n\tint\ti;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tscc_accept_socket(dfcyc, port);\n\tscc_socket_modem_do_ring(dfcyc, port);\n\n\tif(scc_ptr->modem_state) {\n\t\t/* Just get out, this is modem mode */\n\t\t/*  The transmit function stuffs any bytes in receive buf */\n\t\treturn;\n\t}\n\trdwrfd = scc_ptr->rdwrfd;\n\tif(rdwrfd == INVALID_SOCKET) {\n\t\treturn;\t\t/* just get out */\n\t}\n\n\t/* Try reading some bytes */\n\tspace_left = MY_MIN(space_left, 256);\n\tret = (int)recv(rdwrfd, tmp_buf, space_left, 0);\n\tif(ret > 0) {\n\t\tfor(i = 0; i < ret; i++) {\n\t\t\tif(tmp_buf[i] == 0) {\n\t\t\t\t/* Skip null chars */\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tscc_socket_recvd_char(dfcyc, port, tmp_buf[i]);\n\t\t}\n\t} else if(ret == 0) {\n\t\t/* assume socket close */\n\t\tprintf(\"recv got 0 from rdwrfd=%llx, closing\\n\",\n\t\t\t\t\t\t\t(dword64)rdwrfd);\n\t\tscc_socket_close_extended(dfcyc, port, 1);\n\t}\n#endif\n}\n\nvoid\nscc_socket_recvd_char(dword64 dfcyc, int port, int c)\n{\n\tScc\t*scc_ptr;\n\tword32\tlocmask, remmask, mask, reqwillmask, reqdomask;\n\tint\ttelnet_mode, telnet_iac, eff_c, cpos, reply;\n\n\tscc_ptr = &(g_scc[port]);\n\n\ttelnet_mode = scc_ptr->telnet_mode;\n\ttelnet_iac = scc_ptr->telnet_iac;\n\n\teff_c = c;\n\tif(scc_ptr->cur_state == 2) {\n\t\t// Outgoing only connection, support 8-bit transfers with\n\t\t//  no telnet command support\n\t\ttelnet_mode = 0;\n\t\ttelnet_iac = 0;\n\t} else if(telnet_iac == 0) {\n\t\tif(c == 0xff) {\n\t\t\tscc_ptr->telnet_iac = 0xff;\n\t\t\treturn;\t\t\t/* and just get out */\n\t\t}\n\t} else {\n\t\t/* last char was 0xff, see if this is 0xff */\n\t\tif(c != 0xff) {\n\t\t\t/* this is some kind of command */\n\t\t\teff_c = eff_c | 0x100;\t/* indicate prev char IAC */\n\t\t}\n\t}\n\tscc_ptr->telnet_iac = 0;\n\n\tmask = 1 << (c & 31);\n\tcpos = (c >> 5) & 1;\n\tlocmask = scc_ptr->telnet_local_mode[cpos] & mask;\n\tremmask = scc_ptr->telnet_remote_mode[cpos] & mask;\n\treqwillmask = scc_ptr->telnet_reqwill_mode[cpos] & mask;\n\treqdomask = scc_ptr->telnet_reqdo_mode[cpos] & mask;\n\tswitch(telnet_mode) {\n\tcase 0:\t/* just passing through bytes */\n\t\tswitch(eff_c) {\n\t\tcase 0x1fe:\t/* DON'T */\n\t\tcase 0x1fd:\t/* DO */\n\t\tcase 0x1fc:\t/* WON'T */\n\t\tcase 0x1fb:\t/* WILL */\n\t\tcase 0x1fa:\t/* SB */\n\t\t\ttelnet_mode = c;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif(eff_c < 0x100) {\n\t\t\t\tscc_add_to_readbuf(dfcyc, port, c);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase 3: /* LINEMODE SB SLC, octet 0 */\n\t\tif(eff_c == 0x1f0) {\n\t\t\t/* SE, the end */\n\t\t\ttelnet_mode = 0;\n\t\t}\n\t\tscc_printf(\"LINEMODE SLC octet 0: %02x\\n\", c);\n\t\ttelnet_mode = 4;\n\t\tbreak;\n\tcase 4: /* LINEMODE SB SLC, octet 1 */\n\t\tscc_printf(\"LINEMODE SLC octet 1: %02x\\n\", c);\n\t\ttelnet_mode = 5;\n\t\tif(eff_c == 0x1f0) {\n\t\t\t/* SE, the end */\n\t\t\tscc_printf(\"Got SE at octet 1...\\n\");\n\t\t\ttelnet_mode = 0;\n\t\t}\n\t\tbreak;\n\tcase 5: /* LINEMODE SB SLC, octet 2 */\n\t\tscc_printf(\"LINEMODE SLC octet 2: %02x\\n\", c);\n\t\ttelnet_mode = 3;\n\t\tif(eff_c == 0x1f0) {\n\t\t\t/* SE, the end */\n\t\t\tprintf(\"Got Telnet SE at octet 2...\\n\");\n\t\t\ttelnet_mode = 0;\n\t\t}\n\t\tbreak;\n\tcase 34: /* LINEMODE SB beginning */\n\t\tswitch(c) {\n\t\tcase 3:\t/* SLC */\n\t\t\ttelnet_mode = 3;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttelnet_mode = 0xee;\t/* go to SB eat */\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase 0xfa: /* in 0xfa = SB mode, eat up subcommands */\n\t\tswitch(c) {\n\t\tcase 34:\t/* LINEMODE */\n\t\t\ttelnet_mode = 34;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttelnet_mode = 0xee;\t/* SB eat mode */\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase 0xee: /* in SB eat mode */\n\t\tif(eff_c == 0x1f0) {\t\t/* SE, end of sub-command */\n\t\t\ttelnet_mode = 0;\n\t\t} else {\n\t\t\t/* just stay in eat mode */\n\t\t}\n\t\tbreak;\n\tcase 0xfe:\t/* previous char was \"DON'T\" */\n\t\tif(locmask && (reqwillmask == 0)) {\n\t\t\t/* it's a mode change */\n\t\t\t/* always OK to turn off a mode that we had on */\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xff);\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xfc);\t/* WON'T */\n\t\t\tscc_add_to_writebuf(dfcyc, port, c);\n\t\t}\n\t\tscc_ptr->telnet_local_mode[cpos] &= ~mask;\n\t\tscc_ptr->telnet_reqwill_mode[cpos] &= ~mask;\n\t\ttelnet_mode = 0;\n\t\tbreak;\n\tcase 0xfd:\t/* previous char was \"DO\" */\n\t\treply = 0xfc;\n\t\tif(locmask == 0 && (reqwillmask == 0)) {\n\t\t\t/* it's a mode change, send some response  */\n\t\t\treply = 0xfc;\t/* nack it with WON'T */\n\t\t\tif(c == 0x03 || c == 0x01) {\n\t\t\t\treply = 0xfb;\t/* Ack with WILL */\n\t\t\t}\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xff);\n\t\t\tscc_add_to_writebuf(dfcyc, port, reply);\n\t\t\tscc_add_to_writebuf(dfcyc, port, c);\n\t\t}\n\t\tif(reqwillmask || (reply == 0xfb)) {\n\t\t\tscc_ptr->telnet_local_mode[cpos] |= mask;\n\t\t}\n\t\tscc_ptr->telnet_reqwill_mode[cpos] &= ~mask;\n\t\ttelnet_mode = 0;\n\t\tbreak;\n\tcase 0xfc:\t/* previous char was \"WON'T\" */\n\t\tif(remmask && (reqdomask == 0)) {\n\t\t\t/* it's a mode change, ack with DON'T */\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xff);\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xfe);\t/* DON'T */\n\t\t\tscc_add_to_writebuf(dfcyc, port, c);\n\t\t}\n\t\tscc_ptr->telnet_remote_mode[cpos] &= ~mask;\n\t\tscc_ptr->telnet_reqdo_mode[cpos] &= ~mask;\n\t\ttelnet_mode = 0;\n\t\tbreak;\n\tcase 0xfb:\t/* previous char was \"WILL\" */\n\t\treply = 0xfe;\t/* nack it with DON'T */\n\t\tif(remmask == 0 && (reqdomask == 0)) {\n\t\t\t/* it's a mode change, send some response  */\n\t\t\tif(c == 0x03 || c == 0x01) {\n\t\t\t\treply = 0xfd;\t/* Ack with DO */\n\t\t\t}\n\t\t\tscc_add_to_writebuf(dfcyc, port, 0xff);\n\t\t\tscc_add_to_writebuf(dfcyc, port, reply);\n\t\t\tscc_add_to_writebuf(dfcyc, port, c);\n\t\t}\n\t\tif(reqdomask || (reply == 0xfd)) {\n\t\t\tscc_ptr->telnet_remote_mode[cpos] |= mask;\n\t\t}\n\t\tscc_ptr->telnet_reqdo_mode[cpos] &= ~mask;\n\t\ttelnet_mode = 0;\n\t\tbreak;\n\tdefault:\n\t\ttelnet_mode = 0;\n\t\tbreak;\n\t}\n\tscc_ptr->telnet_mode = telnet_mode;\n}\n\nvoid\nscc_socket_empty_writebuf(dword64 dfcyc, int port)\n{\n#ifdef SCC_SOCKETS\n# ifndef _WIN32\n\tstruct sigaction newact, oldact;\n# endif\n\tScc\t*scc_ptr;\n\tdword64\tdiff_dusec;\n\tSOCKET\trdwrfd;\n\tint\tplus_mode, plus_char, rdptr, wrptr, done, ret, len, c;\n\tint\ti;\n\n\tscc_socket_maybe_open(dfcyc, port, 0);\n\n\tscc_ptr = &(g_scc[port]);\n\n\t/* See if +++ done and we should go to command mode */\n\tdiff_dusec = (dfcyc - scc_ptr->out_char_dfcyc) >> 16;\n\tif((diff_dusec > 900LL*1000) && (scc_ptr->modem_plus_mode == 3) &&\n\t\t\t\t(scc_ptr->modem_state == 0) &&\n\t\t\t\t(scc_ptr->cur_state == 1)) {\n\t\tscc_ptr->modem_state = 1;\t/* go modem mode, stay connect*/\n\t\tscc_ptr->modem_plus_mode = 0;\n\t\tscc_socket_send_modem_code(dfcyc, port, 0);\t// \"OK\"\n\t}\n\n\t/* Try writing some bytes */\n\tdone = 0;\n\twhile(!done) {\n\t\trdptr = scc_ptr->out_rdptr;\n\t\twrptr = scc_ptr->out_wrptr;\n\t\tif(rdptr == wrptr) {\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t}\n\t\trdwrfd = scc_ptr->rdwrfd;\n\t\tlen = wrptr - rdptr;\n\t\tif(len < 0) {\n\t\t\tlen = SCC_OUTBUF_SIZE - rdptr;\n\t\t}\n\t\tif(len > 32) {\n\t\t\tlen = 32;\n\t\t}\n\t\t//printf(\"Writing data, %d bytes, modem_state:%d\\n\", len,\n\t\t//\t\t\t\t\tscc_ptr->modem_state);\n\t\tif(len <= 0) {\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t}\n\t\tscc_socket_maybe_open(dfcyc, port, 1);\n\n\t\tif(scc_ptr->modem_state) {\n\t\t\tlen = 1;\n\t\t\tscc_socket_modem_write(dfcyc, port,\n\t\t\t\t\t\tscc_ptr->out_buf[rdptr]);\n\t\t\tscc_ptr->write_called_this_vbl = 0;\n\t\t\tret = 1;\n\t\t} else {\n\t\t\tif(rdwrfd == INVALID_SOCKET) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tplus_char = scc_ptr->modem_s2_val;\n\t\t\tif((plus_char == 0) || (plus_char >= 128)) {\n\t\t\t\tplus_char = 0xfff;\t\t// Invalid\n\t\t\t\tscc_ptr->modem_plus_mode = 0;\n\t\t\t}\n\t\t\t// Look for '+++' within .8 seconds\n\t\t\tfor(i = 0; i < len; i++) {\n\t\t\t\tc = scc_ptr->out_buf[rdptr + i];\n\t\t\t\tplus_mode = scc_ptr->modem_plus_mode;\n\t\t\t\tdiff_dusec =\n\t\t\t\t\t(dfcyc - scc_ptr->out_char_dfcyc) >> 16;\n\t\t\t\tif(c == plus_char && plus_mode == 0) {\n\t\t\t\t\tif(diff_dusec > 500LL*1000) {\n\t\t\t\t\t\tscc_ptr->modem_plus_mode = 1;\n\t\t\t\t\t}\n\t\t\t\t} else if(c == plus_char) {\n\t\t\t\t\tif(diff_dusec < 800LL*1000) {\n\t\t\t\t\t\tplus_mode++;\n\t\t\t\t\t\tscc_ptr->modem_plus_mode =\n\t\t\t\t\t\t\t\tplus_mode;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tscc_ptr->modem_plus_mode = 0;\n\t\t\t\t}\n\t\t\t\tscc_ptr->out_char_dfcyc = dfcyc;\n\t\t\t}\n# ifndef _WIN32\n\t\t\t// ignore SIGPIPE around writes to the socket, so we\n\t\t\t//  can catch a closed socket and prepare to accept\n\t\t\t//  a new connection.  Otherwise, SIGPIPE kills KEGS\n\t\t\tsigemptyset(&newact.sa_mask);\n\t\t\tnewact.sa_handler = SIG_IGN;\n\t\t\tnewact.sa_flags = 0;\n\t\t\tsigaction(SIGPIPE, &newact, &oldact);\n# endif\n\n\t\t\tret = (int)send(rdwrfd, &(scc_ptr->out_buf[rdptr]),\n\t\t\t\t\t\t\t\t\tlen, 0);\n\n# ifndef _WIN32\n\t\t\tsigaction(SIGPIPE, &oldact, 0);\n\t\t\t/* restore previous SIGPIPE behavior */\n# endif\n\n#if 0\n\t\t\tprintf(\"sock output: %02x, len:%d\\n\",\n\t\t\t\t\tscc_ptr->out_buf[rdptr], len);\n#endif\n\n\t\t}\n\n\t\tif(ret == 0) {\n\t\t\tdone = 1;\t/* give up for now */\n\t\t\tbreak;\n\t\t} else if(ret < 0) {\n\t\t\t/* assume socket is dead */\n\t\t\tprintf(\"socket write failed, resuming modem mode\\n\");\n\t\t\tscc_socket_close_extended(dfcyc, port, 1);\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t} else {\n\t\t\trdptr = rdptr + ret;\n\t\t\tif(rdptr >= SCC_OUTBUF_SIZE) {\n\t\t\t\trdptr = rdptr - SCC_OUTBUF_SIZE;\n\t\t\t}\n\t\t\tscc_ptr->out_rdptr = rdptr;\n\t\t}\n\t}\n#endif\n}\n\nvoid\nscc_socket_modem_write(dword64 dfcyc, int port, int c)\n{\n\tScc\t*scc_ptr;\n\tchar\t*str;\n\tword32\tmodem_mode;\n\tint\tdo_echo, got_at, len;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tif(scc_ptr->sockfd == INVALID_SOCKET) {\n\t\tscc_socket_open_incoming(dfcyc, port);\n\t}\n\n\tmodem_mode = scc_ptr->modem_mode;\n\tstr = (char *)&(scc_ptr->modem_cmd_str[0]);\n\n\tdo_echo = ((modem_mode & SCCMODEM_NOECHO) == 0);\n\tlen = scc_ptr->modem_cmd_len;\n\tgot_at = 0;\n#if 0\n\tprintf(\"T:%016llx M[%d][%d]: %02x\\n\", dfcyc, port, len, c);\n#endif\n\tif(len >= 2 && str[0] == 'a' && str[1] == 't') {\n\t\t/* we've got an 'at', do not back up past it */\n\t\tgot_at = 1;\n\t}\n\tif(c == 0x0d) {\n\t\tif(do_echo) {\n\t\t\tscc_add_to_readbuf(dfcyc, port, c);\t/* echo cr */\n\t\t\tscc_add_to_readbuf(dfcyc, port, 0x0a);\t/* echo lf */\n\t\t}\n\t\tdo_echo = 0;\t/* already did the echo */\n\t\tscc_socket_do_cmd_str(dfcyc, port);\n\t\tscc_ptr->modem_cmd_len = 0;\n\t\tlen = 0;\n\t\tstr[0] = 0;\n\t} else if(c == 0x08) {\n\t\tif(len <= 0) {\n\t\t\tdo_echo = 0;\t/* do not go past left margin */\n\t\t} else if(len == 2 && got_at) {\n\t\t\tdo_echo = 0;\t/* do not erase \"AT\" */\n\t\t} else {\n\t\t\t/* erase a character */\n\t\t\tlen--;\n\t\t\tstr[len] = 0;\n\t\t}\n\t} else if(c < 0x20) {\n\t\t/* ignore all control characters, don't echo */\n\t\t/* includes line feeds and nulls */\n\t\tdo_echo = 0;\n\t} else {\n\t\t/* other characters */\n\t\tif(len < SCC_MODEM_MAX_CMD_STR) {\n\t\t\tstr[len] = tolower(c);\n\t\t\tstr[len+1] = 0;\n\t\t\tlen++;\n\t\t}\n\t}\n\tscc_ptr->modem_cmd_len = len;\n\tif(do_echo) {\n\t\tscc_add_to_readbuf(dfcyc, port, c);\t/* echo */\n\t}\n}\n\nvoid\nscc_socket_do_cmd_str(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tchar\t*str;\n\tint\tpos, len, ret_val, reg, reg_val, was_amp, out_port, c;\n\tint\ti;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tstr = (char *)&(scc_ptr->modem_cmd_str[0]);\n\tprintf(\"Got modem string :%s:=%02x %02x %02x\\n\", str, str[0], str[1],\n\t\t\t\t\t\tstr[2]);\n\n\tlen = scc_ptr->modem_cmd_len;\n\tstr[len] = 0;\n\tstr[len+1] = 0;\n\tstr[len+2] = 0;\n\tpos = -1;\n\tif(len < 2) {\n\t\t/* just ignore it */\n\t\treturn;\n\t}\n\tif(str[0] != 'a' || str[1] != 't') {\n\t\treturn;\n\t}\n\n\t/* Some AT command received--make sure socket 6501/6502 is open */\n\tprintf(\"Some AT command received, sockfd=%llx\\n\",\n\t\t\t\t\t\t(dword64)scc_ptr->sockfd);\n\n\tpos = 2 - 1;\n\tret_val = 0;\t\t/* \"OK\" */\n\twas_amp = 0;\n\twhile(++pos < len) {\n\t\tc = str[pos] + was_amp;\n\t\twas_amp = 0;\n\t\tswitch(c) {\n\t\tcase '&':\t/* at& */\n\t\t\twas_amp = 0x100;\n\t\t\tbreak;\n\t\tcase 'z':\t/* atz */\n\t\t\tscc_ptr->modem_mode = 0;\n\t\t\tscc_ptr->modem_s0_val = 0;\n\t\t\tpos = len;\t\t/* ignore any other commands */\n\t\t\tbreak;\n\t\tcase 'e':\t/* ate = echo */\n\t\t\tc = str[pos+1];\n\t\t\tif(c == '1') {\n\t\t\t\tscc_ptr->modem_mode &= ~SCCMODEM_NOECHO;\n\t\t\t\tpos++;\n\t\t\t} else {\n\t\t\t\tscc_ptr->modem_mode |= SCCMODEM_NOECHO;\n\t\t\t\tpos++;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'v':\t/* atv = verbose */\n\t\t\tc = str[pos+1];\n\t\t\tif(c == '1') {\n\t\t\t\tscc_ptr->modem_mode &= ~SCCMODEM_NOVERBOSE;\n\t\t\t\tpos++;\n\t\t\t} else {\n\t\t\t\tscc_ptr->modem_mode |= SCCMODEM_NOVERBOSE;\n\t\t\t\tpos++;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'o':\t/* ato = go online */\n\t\t\tprintf(\"ato\\n\");\n\t\t\tif(scc_ptr->dcd && scc_ptr->modem_state &&\n\t\t\t\t\t(scc_ptr->rdwrfd != INVALID_SOCKET)) {\n\t\t\t\tprintf(\"Going back online\\n\");\n\t\t\t\tscc_socket_modem_connect(dfcyc, port);\n\t\t\t\tscc_ptr->modem_state = 0;\n\t\t\t\t\t\t// talk to socket\n\t\t\t\tret_val = -1;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'h':\t/* ath = hang up */\n\t\t\tprintf(\"ath, hanging up\\n\");\n\t\t\tscc_socket_close(port);\n\t\t\tif(scc_ptr->rdwrfd != INVALID_SOCKET) {\n\t\t\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\t\t}\n\t\t\t/* scc_socket_maybe_open_incoming(dfcyc, port); */\n\t\t\t\t\t\t\t/* reopen listen */\n\t\t\tbreak;\n\t\tcase 'a':\t/* ata */\n\t\t\tprintf(\"Doing ATA\\n\");\n\t\t\tscc_socket_do_answer(dfcyc, port);\n\t\t\tret_val = -1;\n\t\t\tbreak;\n\t\tcase 'd':\t/* atd */\n\t\t\tscc_ptr->modem_out_portnum = 23;\n\t\t\tpos++;\n\t\t\tc = str[pos];\n\t\t\tif(c == 't' || c == 'p') {\n\t\t\t\t/* skip tone or pulse */\n\t\t\t\tpos++;\n\t\t\t}\n\t\t\t/* see if it is 111 */\n\t\t\tif(strcmp(&str[pos], \"111\") == 0) {\n\t\t\t\t/* Do PPP! */\n\t\t\t} else {\n\t\t\t\t/* get string to connect to */\n\t\t\t\t/* Shift string so hostname moves to str[0] */\n\t\t\t\tfor(i = 0; i < len; i++) {\n\t\t\t\t\tc = str[pos];\n\t\t\t\t\tif(c == ':') {\n\t\t\t\t\t\t/* get port number now */\n\t\t\t\t\t\tout_port = (int)strtol(\n\t\t\t\t\t\t\t&str[pos+1], 0, 10);\n\t\t\t\t\t\tif(out_port <= 1) {\n\t\t\t\t\t\t\tout_port = 23;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tscc_ptr->modem_out_portnum =\n\t\t\t\t\t\t\t\tout_port;\n\t\t\t\t\t\tc = 0;\n\t\t\t\t\t}\n\t\t\t\t\tstr[i] = c;\n\t\t\t\t\tif((pos >= len) || (c == 0)) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tpos++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tscc_socket_open_outgoing(dfcyc, port,\n\t\t\t\t(char *)&scc_ptr->modem_cmd_str[0],\n\t\t\t\tscc_ptr->modem_out_portnum);\n\t\t\tret_val = -1;\n\t\t\tpos = len;\t/* always eat rest of the line */\n\t\t\tbreak;\n\t\tcase 's':\t/* atsnn=yy */\n\t\t\tpos++;\n\t\t\treg = 0;\n\t\t\twhile(1) {\n\t\t\t\tc = str[pos];\n\t\t\t\tif(c < '0' || c > '9') {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treg = (reg * 10) + c - '0';\n\t\t\t\tpos++;\n\t\t\t}\n\t\t\tif(c == '?') {\n\t\t\t\t/* display S-register */\n\t\t\t\tif(reg == 0) {\n\t\t\t\t\tscc_add_to_readbufv(dfcyc, port,\n\t\t\t\t\t\t\"S0=%d\\n\",\n\t\t\t\t\t\tscc_ptr->modem_s0_val);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif(c != '=') {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tpos++;\n\t\t\treg_val = 0;\n\t\t\twhile(1) {\n\t\t\t\tc = str[pos];\n\t\t\t\tif(c < '0' || c >'9') {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\treg_val = (reg_val * 10) + c - '0';\n\t\t\t\tpos++;\n\t\t\t}\n\t\t\tprintf(\"ats%d = %d\\n\", reg, reg_val);\n\t\t\tif(reg == 0) {\n\t\t\t\tscc_ptr->modem_s0_val = reg_val;\n\t\t\t}\n\t\t\tif(reg == 2) {\n\t\t\t\tscc_ptr->modem_s2_val = reg_val;\n\t\t\t}\n\t\t\tpos--;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t/* some command--peek into next chars to finish it */\n\t\t\twhile(1) {\n\t\t\t\tc = str[pos+1];\n\t\t\t\tif(c >= '0' && c <= '9') {\n\t\t\t\t\t/* eat numbers */\n\t\t\t\t\tpos++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(c == '=') {\n\t\t\t\t\t/* eat this as well */\n\t\t\t\t\tpos++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t/* else get out */\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif(ret_val >= 0) {\n\t\tscc_socket_send_modem_code(dfcyc, port, ret_val);\n\t}\n}\n\nvoid\nscc_socket_send_modem_code(dword64 dfcyc, int port, int code)\n{\n\tScc\t*scc_ptr;\n\tchar\t*str;\n\tword32\tmodem_mode;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tswitch(code) {\n\tcase 0: str = \"OK\"; break;\n\tcase 1: str = \"CONNECT\"; break;\n\tcase 2: str = \"RING\"; break;\n\tcase 3: str = \"NO CARRIER\"; break;\n\tcase 4: str = \"ERROR\"; break;\n\tcase 5: str = \"CONNECT 1200\"; break;\n\tcase 10: str = \"CONNECT 2400\"; break;\n\tcase 12: str = \"CONNECT 9600\"; break;\t// Generic AT docs/Warp6 BBS\n\tcase 13: str = \"CONNECT 9600\"; break;\t// US Robotics Sportster/HST\n\tcase 14: str = \"CONNECT 19200\"; break;\t// Warp6 BBS\n\tcase 16: str = \"CONNECT 19200\"; break;\t// Generic AT docs\n\tcase 25: str = \"CONNECT 14400\"; break;\t// US Robotics Sportster\n\tcase 85: str = \"CONNECT 19200\"; break;\t// US Robotics Sportster\n\tcase 18: str = \"CONNECT 57600\"; break;\t// Generic AT docs/Warp6 BBS\n\tcase 28: str = \"CONNECT 38400\"; break;\t// Warp6 BBS/Hayes\n\tdefault:\n\t\tstr = \"ERROR\";\n\t}\n\n\tprintf(\"Sending modem code %d = %s\\n\", code, str);\n\n\tmodem_mode = scc_ptr->modem_mode;\n\tif(modem_mode & SCCMODEM_NOVERBOSE) {\n\t\t/* just the number */\n\t\tscc_add_to_readbufv(dfcyc, port, \"%d\", code);\n\t\tscc_add_to_readbuf(dfcyc, port, 0x0d);\n\t} else {\n\t\tscc_add_to_readbufv(dfcyc, port, \"%s\\n\", str);\n\t}\n}\n\nvoid\nscc_socket_modem_connect(dword64 dfcyc, int port)\n{\n\t// Send code telling Term program the connect speed\n\tif(g_scc[port].cur_state == 1) {\t\t// Virtual modem\n\t\tscc_socket_send_modem_code(dfcyc, port,\n\t\t\tg_serial_modem_response_code);\t/*28=38400*/\n\t}\n}\n\nvoid\nscc_socket_modem_do_ring(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\tdword64\tdiff_dusecs;\n\tint\tnum_rings;\n\n\tscc_ptr = &(g_scc[port]);\n\tnum_rings = scc_ptr->socket_num_rings;\n\tif((num_rings > 0) && scc_ptr->modem_state) {\n\t\tnum_rings--;\n\t\tdiff_dusecs = (dfcyc - scc_ptr->socket_last_ring_dfcyc) >> 16;\n\t\tif(diff_dusecs < 2LL*1000*1000) {\n\t\t\treturn;\t\t/* nothing more to do */\n\t\t}\n\n\t\tscc_ptr->socket_num_rings = num_rings;\n\t\tscc_ptr->socket_last_ring_dfcyc = dfcyc;\n\t\tif(num_rings <= 0) {\n\t\t\t/* decide on answering */\n\t\t\tif(scc_ptr->modem_s0_val) {\n\t\t\t\tscc_socket_do_answer(dfcyc, port);\n\t\t\t} else {\n\t\t\t\tprintf(\"No answer, closing socket\\n\");\n\t\t\t\tscc_socket_close(port);\n\t\t\t}\n\t\t} else {\n\t\t\tscc_socket_send_modem_code(dfcyc, port, 2); /* RING */\n\t\t}\n\t}\n}\n\nvoid\nscc_socket_do_answer(dword64 dfcyc, int port)\n{\n\tScc\t*scc_ptr;\n\n\tscc_ptr = &(g_scc[port]);\n\tscc_accept_socket(dfcyc, port);\n\tif(scc_ptr->rdwrfd == INVALID_SOCKET) {\n\t\tprintf(\"Answer when rdwrfd=-1, closing\\n\");\n\t\tscc_socket_close_extended(dfcyc, port, 0);\n\t\t/* send NO CARRIER message */\n\t} else {\n\t\tscc_socket_telnet_reqs(dfcyc, port);\n\t\tprintf(\"Send telnet reqs\\n\");\n\t\tif(scc_ptr->modem_state) {\n\t\t\tscc_socket_modem_connect(dfcyc, port);\n\t\t}\n\t\tscc_ptr->modem_state = 0;\t\t// Talk to socket\n\t\tscc_ptr->dcd = 1;\t\t\t// carrier on\n\t\tscc_ptr->socket_num_rings = 0;\n\t}\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/scc_unixdriver.c",
    "content": "const char rcsid_scc_macdriver_c[] = \"@(#)$KmKId: scc_unixdriver.c,v 1.4 2025-01-07 16:45:35+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2025 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n/* This file contains the Mac/Linux calls to a real serial port */\n\n#include \"defc.h\"\n#include \"scc.h\"\n\n#ifndef _WIN32\n# include <termios.h>\n#endif\n\nextern Scc g_scc[2];\nextern word32 g_c025_val;\nextern char *g_serial_device[2];\n\n#ifndef _WIN32\nvoid\nscc_serial_unix_open(int port)\n{\n\tScc\t*scc_ptr;\n\tint\tfd;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tfd = open(&g_serial_device[port][0], O_RDWR | O_NONBLOCK);\n\tscc_ptr->unix_dev_fd = fd;\n\n\tprintf(\"scc_serial_unix_init %d called, fd: %d\\n\", port, fd);\n\n\tif(fd < 0) {\n\t\tscc_ptr->unix_dev_fd = -1;\n\t\tscc_ptr->cur_state = -1;\t// Failed to open\n\t\treturn;\n\t}\n\n\tscc_serial_unix_change_params(port);\n\n\tscc_ptr->cur_state = 0;\t\t// Actual Serial device\n}\n\nvoid\nscc_serial_unix_close(int port)\n{\n\tScc\t*scc_ptr;\n\tint\tfd;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tfd = scc_ptr->unix_dev_fd;\n\tif(fd >= 0) {\n\t\tclose(fd);\n\t}\n\tscc_ptr->unix_dev_fd = -1;\n}\n\nvoid\nscc_serial_unix_change_params(int port)\n{\n\tstruct termios termios_buf;\n\tScc\t*scc_ptr;\n\tint\tfd, csz, ret;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tfd = scc_ptr->unix_dev_fd;\n\tprintf(\"scc_serial_unix_change_parms port: %d, fd: %d\\n\", port, fd);\n\tif(fd <= 0) {\n\t\treturn;\n\t}\n\n\tret = tcgetattr(fd, &termios_buf);\n\tif(ret != 0) {\n\t\tprintf(\"tcgetattr port%d ret: %d\\n\", port, ret);\n\t}\n\n#if 1\n\tprintf(\"baudrate: %d, iflag:%x, oflag:%x, cflag:%x, lflag:%x\\n\",\n\t\t(int)termios_buf.c_ispeed, (int)termios_buf.c_iflag,\n\t\t(int)termios_buf.c_oflag, (int)termios_buf.c_cflag,\n\t\t(int)termios_buf.c_lflag);\n#endif\n\n\tmemset(&termios_buf, 0, sizeof(struct termios));\n\tcfmakeraw(&termios_buf);\n\tcfsetspeed(&termios_buf, scc_ptr->baud_rate);\n\n\tcsz = scc_ptr->char_size;\n\ttermios_buf.c_cflag = CREAD | CLOCAL;\n\ttermios_buf.c_cflag |= (csz == 5) ? CS5 :\n\t\t\t\t(csz == 6) ? CS6 :\n\t\t\t\t(csz == 7) ? CS7 :\n\t\t\t\t\tCS8;\n\tswitch((scc_ptr->reg[4] >> 2) & 0x3) {\n\tcase 2: // 1.5 stop bits\n\t\ttermios_buf.c_cflag |= CSTOPB;\t/* no 1.5 stop bit setting.*/\n\t\tbreak;\n\tcase 3: // 2 stop bits\n\t\ttermios_buf.c_cflag |= CSTOPB;\n\t\tbreak;\n\t}\n\n\tswitch((scc_ptr->reg[4]) & 0x3) {\n\tcase 1:\t// Odd parity\n\t\ttermios_buf.c_cflag |= (PARENB | PARODD);\n\t\tbreak;\n\tcase 3:\t// Even parity\n\t\ttermios_buf.c_cflag |= PARENB;\n\t\tbreak;\n\t}\n\n\t/* always enabled DTR and RTS control */\n#ifdef CRTSCTS\n\ttermios_buf.c_cflag |= CRTSCTS;\t\t\t// Linux: CTS/RTS\n#endif\n#ifdef CRTS_IFLOW\n\ttermios_buf.c_cflag |= CDTR_IFLOW | CRTS_IFLOW;\t\t// Mac: CTS/RTS\n#endif\n\n\tprintf(\"fd: %d, baudrate: %d, iflag:%x, oflag:%x, cflag:%x, lflag:%x\\n\",\n\t\tfd, (int)termios_buf.c_ispeed, (int)termios_buf.c_iflag,\n\t\t(int)termios_buf.c_oflag, (int)termios_buf.c_cflag,\n\t\t(int)termios_buf.c_lflag);\n\tret = tcsetattr(fd, TCSANOW, &termios_buf);\n\tif(ret != 0) {\n\t\tprintf(\"tcsetattr ret: %d\\n\", ret);\n\t}\n}\n\nvoid\nscc_serial_unix_fill_readbuf(dword64 dfcyc, int port, int space_left)\n{\n\tbyte\ttmp_buf[256];\n\tScc\t*scc_ptr;\n\tint\tfd, ret, flags, dcd;\n\tint\ti;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tfd = scc_ptr->unix_dev_fd;\n\tif(fd <= 0) {\n\t\treturn;\n\t}\n\n\t/* Try reading some bytes */\n\tspace_left = MY_MIN(space_left, 256);\n\tret = read(fd, tmp_buf, space_left);\n\n\tif(ret > 0) {\n\t\tfor(i = 0; i < ret; i++) {\n\t\t\tscc_add_to_readbuf(dfcyc, port, tmp_buf[i]);\n\t\t}\n\t}\n\tflags = 0;\n\tdcd = 0;\n\n#if defined(TIOCMGET) && defined(TIOCM_CAR)\n\tret = ioctl(fd, TIOCMGET, &flags);\n\tif(ret == 0) {\n\t\tdcd = 0;\n\t\tif(flags & TIOCM_CAR) {\t\t// DCD\n\t\t\tdcd = 1;\n\t\t}\n\t\tscc_ptr->dcd = dcd;\n\t}\n#endif\n}\n\nvoid\nscc_serial_unix_empty_writebuf(int port)\n{\n\tScc\t*scc_ptr;\n\tint\tfd, rdptr, wrptr, done, ret, len;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tfd = scc_ptr->unix_dev_fd;\n\tif(fd <= 0) {\n\t\treturn;\n\t}\n\n\t/* Try writing some bytes */\n\tdone = 0;\n\twhile(!done) {\n\t\trdptr = scc_ptr->out_rdptr;\n\t\twrptr = scc_ptr->out_wrptr;\n\t\tif(rdptr == wrptr) {\n\t\t\t//printf(\"...rdptr == wrptr\\n\");\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t}\n\t\tlen = wrptr - rdptr;\n\t\tif(len < 0) {\n\t\t\tlen = SCC_OUTBUF_SIZE - rdptr;\n\t\t}\n\t\tif(len > 32) {\n\t\t\tlen = 32;\n\t\t}\n\t\tif(len <= 0) {\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t}\n\t\tret = write(fd, &(scc_ptr->out_buf[rdptr]), len);\n\n\t\tif(ret <= 0) {\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t} else {\n\t\t\trdptr = rdptr + ret;\n\t\t\tif(rdptr >= SCC_OUTBUF_SIZE) {\n\t\t\t\trdptr = rdptr - SCC_OUTBUF_SIZE;\n\t\t\t}\n\t\t\tscc_ptr->out_rdptr = rdptr;\n\t\t}\n\t}\n}\n#endif\t/* !_WIN32 */\n"
  },
  {
    "path": "upstream/kegs/src/scc_windriver.c",
    "content": "const char rcsid_scc_windriver_c[] = \"@(#)$KmKId: scc_windriver.c,v 1.13 2023-08-28 18:11:05+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n/* This file contains the Win32 COM1/COM2 calls */\n\n#include \"defc.h\"\n#include \"scc.h\"\n\nextern Scc g_scc[2];\nextern word32 g_c025_val;\nextern int g_serial_win_device[2];\n\n#ifdef _WIN32\nvoid\nscc_serial_win_open(int port)\n{\n\tCOMMTIMEOUTS commtimeouts;\n\tchar\tstr_buf[32];\n\tScc\t*scc_ptr;\n\tHANDLE\tcom_handle;\n\tint\tret;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tsnprintf(&str_buf[0], sizeof(str_buf), \"COM%d\",\n\t\t\t\t\t\tg_serial_win_device[port]);\n\n\tcom_handle = CreateFile(&str_buf[0], GENERIC_READ | GENERIC_WRITE,\n\t\t\t0, NULL, OPEN_EXISTING, 0, NULL);\n\n\tscc_ptr->win_com_handle = com_handle;\n\n\tprintf(\"scc_serial_win_init %d called, com_handle: %p\\n\", port,\n\t\t\t\t\t\t\t\tcom_handle);\n\n\tif(com_handle == INVALID_HANDLE_VALUE) {\n\t\tscc_ptr->cur_state = -1;\t\t// Failed to open\n\t\treturn;\n\t}\n\tscc_ptr->win_dcb_ptr = malloc(sizeof(DCB));\n\n\tscc_serial_win_change_params(port);\n\n\tcommtimeouts.ReadIntervalTimeout = MAXDWORD;\n\tcommtimeouts.ReadTotalTimeoutMultiplier = 0;\n\tcommtimeouts.ReadTotalTimeoutConstant = 0;\n\tcommtimeouts.WriteTotalTimeoutMultiplier = 0;\n\tcommtimeouts.WriteTotalTimeoutConstant = 10;\n\tret = SetCommTimeouts(com_handle, &commtimeouts);\n\tif(ret == 0) {\n\t\tprintf(\"setcommtimeout ret: %d\\n\", ret);\n\t}\n\tscc_ptr->cur_state = 0;\t\t// COM* is open\n}\n\nvoid\nscc_serial_win_close(int port)\n{\n\tScc\t*scc_ptr;\n\tHANDLE\tcom_handle;\n\n\tscc_ptr = &(g_scc[port]);\n\tcom_handle = scc_ptr->win_com_handle;\n\tscc_ptr->win_com_handle = INVALID_HANDLE_VALUE;\n\tif(com_handle == INVALID_HANDLE_VALUE) {\n\t\treturn;\n\t}\n\tCloseHandle(com_handle);\n\tfree(scc_ptr->win_dcb_ptr);\n\tscc_ptr->win_dcb_ptr = 0;\n}\n\nvoid\nscc_serial_win_change_params(int port)\n{\n\tDCB\t*dcbptr;\n\tHANDLE\tcom_handle;\n\tScc\t*scc_ptr;\n\tint\tret;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tcom_handle = scc_ptr->win_com_handle;\n\tdcbptr = scc_ptr->win_dcb_ptr;\n\tif((com_handle == INVALID_HANDLE_VALUE) || (scc_ptr->cur_state != 0)) {\n\t\treturn;\n\t}\n\n\tret = GetCommState(com_handle, dcbptr);\n\tif(ret == 0) {\n\t\tprintf(\"getcomm port%d ret: %d\\n\", port, ret);\n\t}\n\n#if 1\n\tprintf(\"dcb.baudrate: %d, bytesize:%d, stops:%d, parity:%d\\n\",\n\t\t(int)dcbptr->BaudRate, (int)dcbptr->ByteSize,\n\t\t(int)dcbptr->StopBits, (int)dcbptr->Parity);\n\tprintf(\"dcb.binary: %d, ctsflow: %d, dsrflow: %d, dtr: %d, dsr: %d\\n\",\n\t\t(int)dcbptr->fBinary,\n\t\t(int)dcbptr->fOutxCtsFlow,\n\t\t(int)dcbptr->fOutxDsrFlow,\n\t\t(int)dcbptr->fDtrControl,\n\t\t(int)dcbptr->fDsrSensitivity);\n\tprintf(\"dcb.txonxoff:%d, outx:%d, inx: %d, null: %d, rts: %d\\n\",\n\t\t(int)dcbptr->fTXContinueOnXoff,\n\t\t(int)dcbptr->fOutX,\n\t\t(int)dcbptr->fInX,\n\t\t(int)dcbptr->fNull,\n\t\t(int)dcbptr->fRtsControl);\n\tprintf(\"dcb.fAbortOnErr:%d, fParity:%d\\n\", (int)dcbptr->fAbortOnError,\n\t\t(int)dcbptr->fParity);\n#endif\n\n\tdcbptr->fAbortOnError = 0;\n\n\tdcbptr->BaudRate = scc_ptr->baud_rate;\n\tdcbptr->ByteSize = scc_ptr->char_size;\n\tdcbptr->StopBits = ONESTOPBIT;\n\tswitch((scc_ptr->reg[4] >> 2) & 0x3) {\n\tcase 2: // 1.5 stop bits\n\t\tdcbptr->StopBits = ONE5STOPBITS;\n\t\tbreak;\n\tcase 3: // 2 stop bits\n\t\tdcbptr->StopBits = TWOSTOPBITS;\n\t\tbreak;\n\t}\n\n\tdcbptr->Parity = NOPARITY;\n\tswitch((scc_ptr->reg[4]) & 0x3) {\n\tcase 1:\t// Odd parity\n\t\tdcbptr->Parity = ODDPARITY;\n\t\tbreak;\n\tcase 3:\t// Even parity\n\t\tdcbptr->Parity = EVENPARITY;\n\t\tbreak;\n\t}\n\n\tdcbptr->fNull = 0;\n\tdcbptr->fDtrControl = DTR_CONTROL_ENABLE;\n\tdcbptr->fDsrSensitivity = 0;\n\tdcbptr->fOutxCtsFlow = 0;\n\tdcbptr->fOutxDsrFlow = 0;\n\tdcbptr->fParity = 0;\n\tdcbptr->fInX = 0;\n\tdcbptr->fOutX = 0;\n\tdcbptr->fRtsControl = RTS_CONTROL_ENABLE;\n\n\tret = SetCommState(com_handle, dcbptr);\n\tif(ret == 0) {\n\t\tprintf(\"SetCommState ret: %d, new baud: %d\\n\", ret,\n\t\t\t(int)dcbptr->BaudRate);\n\t}\n}\n\nvoid\nscc_serial_win_fill_readbuf(dword64 dfcyc, int port, int space_left)\n{\n\tbyte\ttmp_buf[256];\n\tScc\t*scc_ptr;\n\tHANDLE\tcom_handle;\n\tDWORD\tbytes_read;\n\tint\tret;\n\tint\ti;\n\n\tscc_ptr = &(g_scc[port]);\n\n\tcom_handle = scc_ptr->win_com_handle;\n\tif(com_handle == INVALID_HANDLE_VALUE) {\n\t\treturn;\n\t}\n\n\t/* Try reading some bytes */\n\tspace_left = MY_MIN(256, space_left);\n\tret = ReadFile(com_handle, tmp_buf, space_left, &bytes_read, NULL);\n\n\tif(ret == 0) {\n\t\tprintf(\"ReadFile ret 0\\n\");\n\t}\n\n\tif(ret && (bytes_read > 0)) {\n\t\tfor(i = 0; i < (int)bytes_read; i++) {\n\t\t\tscc_add_to_readbuf(dfcyc, port, tmp_buf[i]);\n\t\t}\n\t}\n}\n\nvoid\nscc_serial_win_empty_writebuf(int port)\n{\n\tScc\t*scc_ptr;\n\tHANDLE\tcom_handle;\n\tDWORD\tbytes_written;\n\tword32\terr_code;\n\tint\trdptr, wrptr, done, ret, len;\n\n\tscc_ptr = &(g_scc[port]);\n\n\t//printf(\"win_empty_writebuf, com_h: %d\\n\", scc_ptr->win_com_handle);\n\tcom_handle = scc_ptr->win_com_handle;\n\tif(com_handle == INVALID_HANDLE_VALUE) {\n\t\treturn;\n\t}\n\n\t/* Try writing some bytes */\n\tdone = 0;\n\twhile(!done) {\n\t\trdptr = scc_ptr->out_rdptr;\n\t\twrptr = scc_ptr->out_wrptr;\n\t\tif(rdptr == wrptr) {\n\t\t\t//printf(\"...rdptr == wrptr\\n\");\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t}\n\t\tlen = wrptr - rdptr;\n\t\tif(len < 0) {\n\t\t\tlen = SCC_OUTBUF_SIZE - rdptr;\n\t\t}\n\t\tif(len > 32) {\n\t\t\tlen = 32;\n\t\t}\n\t\tif(len <= 0) {\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t}\n\t\tbytes_written = 1;\n\t\tret = WriteFile(com_handle, &(scc_ptr->out_buf[rdptr]), len,\n\t\t\t\t&bytes_written, NULL);\n\t\tprintf(\"WriteFile ret: %d, bytes_written:%d, len:%d\\n\", ret,\n\t\t\t(int)bytes_written, len);\n\n\t\terr_code = (word32)-1;\n\t\tif(ret == 0) {\n\t\t\terr_code = (word32)GetLastError();\n\t\t\tprintf(\"WriteFile ret:0, err_code: %08x\\n\", err_code);\n\t\t}\n\n\t\tif(ret == 0 || (bytes_written == 0)) {\n\t\t\tdone = 1;\n\t\t\tbreak;\n\t\t} else {\n\t\t\trdptr = rdptr + bytes_written;\n\t\t\tif(rdptr >= SCC_OUTBUF_SIZE) {\n\t\t\t\trdptr = rdptr - SCC_OUTBUF_SIZE;\n\t\t\t}\n\t\t\tscc_ptr->out_rdptr = rdptr;\n\t\t}\n\t}\n}\n\n#endif\n"
  },
  {
    "path": "upstream/kegs/src/sim65816.c",
    "content": "const char rcsid_sim65816_c[] = \"@(#)$KmKId: sim65816.c,v 1.491 2025-04-27 18:03:43+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2025 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include <math.h>\n\n#define INCLUDE_RCSID_C\n#include \"defc.h\"\n#undef INCLUDE_RCSID_C\n\ndouble g_dtime_sleep = 0;\ndouble g_dtime_in_sleep = 0;\nextern char *g_argv0_path;\n\n#define MAX_EVENTS\t64\n\n/* All EV_* must be less than 256, since upper bits reserved for other use */\n/*  e.g., DOC_INT uses upper bits to encode oscillator */\n#define EV_60HZ\t\t\t1\n#define EV_STOP\t\t\t2\n#define EV_SCAN_INT\t\t3\n#define EV_DOC_INT\t\t4\n#define EV_VBL_INT\t\t5\n#define EV_SCC\t\t\t6\n#define EV_VID_UPD\t\t7\n#define EV_MOCKINGBOARD\t\t8\n\nextern int g_stepping;\n\nextern word32 g_c068_statereg;\nextern int g_cur_a2_stat;\n\nextern word32 g_c02d_int_crom;\n\nextern word32 g_c035_shadow_reg;\nextern word32 g_c036_val_speed;\n\nextern word32 g_c023_val;\nextern word32 g_c041_val;\nextern word32 g_c046_val;\nextern word32 g_zipgs_reg_c059;\nextern word32 g_zipgs_reg_c05a;\nextern word32 g_zipgs_reg_c05b;\nextern word32 g_zipgs_unlock;\nextern Iwm g_iwm;\n\nEngine_reg engine;\nextern word32 table8[];\nextern word32 table16[];\n\nextern byte doc_ram[];\n\nextern int g_iwm_motor_on;\nextern int g_fast_disk_emul;\nextern int g_slow_525_emul_wr;\nextern int g_config_control_panel;\n\nextern int g_audio_enable;\nextern int g_preferred_rate;\n\nint\tg_a2_fatal_err = 0;\ndword64\tg_dcycles_end = 0;\nint\tg_halt_sim = 0;\nint\tg_rom_version = -1;\nint\tg_user_halt_bad = 0;\nint\tg_halt_on_bad_read = 0;\nint\tg_ignore_bad_acc = 1;\nint\tg_ignore_halts = 1;\nint\tg_code_red = 0;\nint\tg_code_yellow = 0;\nint\tg_emul_6502_ind_page_cross_bug = 0;\n\nint\tg_config_iwm_vbl_count = 0;\nconst char g_kegs_version_str[] = \"1.38\";\n\ndword64\tg_last_vbl_dfcyc = 0;\ndword64\tg_cur_dfcyc = 1;\n\ndouble\tg_last_vbl_dadjcycs = 0;\ndouble\tg_dadjcycs = 0;\n\n\nint\tg_wait_pending = 0;\nint\tg_stp_pending = 0;\nextern int g_irq_pending;\n\nint\tg_num_irq = 0;\nint\tg_num_brk = 0;\nint\tg_num_cop = 0;\nint\tg_num_enter_engine = 0;\nint\tg_io_amt = 0;\nint\tg_engine_action = 0;\nint\tg_engine_recalc_event = 0;\nint\tg_engine_scan_int = 0;\nint\tg_engine_doc_int = 0;\n\n#define MAX_FATAL_LOGS\t\t20\n\nint\tg_debug_file_fd = -1;\nint\tg_fatal_log = -1;\nchar *g_fatal_log_strs[MAX_FATAL_LOGS];\n\nword32 stop_run_at;\n\nint g_25sec_cntr = 0;\nint g_1sec_cntr = 0;\n\ndouble g_dnatcycs_1sec = 0.0;\nword32 g_natcycs_lastvbl = 0;\n\nint Verbose = 0;\nint Halt_on = 0;\n\nword32 g_mem_size_base = 128*1024;\t/* size of motherboard memory */\nword32 g_mem_size_exp = 8*1024*1024;\t/* size of expansion RAM card */\nword32 g_mem_size_total = 128*1024;\t/* Total contiguous RAM from 0 */\n\nextern word32 g_slow_mem_changed[];\n\nbyte *g_slow_memory_ptr = 0;\nbyte *g_memory_ptr = 0;\nbyte *g_dummy_memory1_ptr = 0;\nbyte *g_rom_fc_ff_ptr = 0;\nbyte *g_rom_cards_ptr = 0;\n\nvoid *g_memory_alloc_ptr = 0;\t\t/* for freeing memory area */\n\nPage_info page_info_rd_wr[2*65536 + PAGE_INFO_PAD_SIZE];\n\nword32\tg_word32_tmp = 0;\nint\tg_force_depth = -1;\nint\tg_use_shmem = 1;\n\nextern word32 g_cycs_in_40col;\nextern word32 g_cycs_in_xredraw;\nextern word32 g_cycs_in_refresh_line;\nextern word32 g_cycs_outside_run_16ms;\nextern word32 g_refresh_bytes_xfer;\n\nextern int g_num_snd_plays;\nextern int g_num_recalc_snd_parms;\n\nextern int g_doc_vol;\n\nextern int g_status_refresh_needed;\n\nint\nsim_get_force_depth()\n{\n\treturn g_force_depth;\n}\n\nint\nsim_get_use_shmem()\n{\n\treturn g_use_shmem;\n}\n\nvoid\nsim_set_use_shmem(int use_shmem)\n{\n\tg_use_shmem = use_shmem;\n}\n\n#define TOOLBOX_LOG_LEN\t\t64\n\nint g_toolbox_log_pos = 0;\nword32 g_toolbox_log_array[TOOLBOX_LOG_LEN][8];\n\nword32\ntoolbox_debug_4byte(word32 addr)\n{\n\tword32\tpart1, part2;\n\n\t/* If addr looks safe, use it */\n\tif(addr > 0xbffc) {\n\t\treturn (word32)-1;\n\t}\n\n\tpart1 = get_memory16_c(addr);\n\tpart1 = (part1 >> 8) + ((part1 & 0xff) << 8);\n\tpart2 = get_memory16_c(addr+2);\n\tpart2 = (part2 >> 8) + ((part2 & 0xff) << 8);\n\n\treturn (part1 << 16) + part2;\n}\n\nvoid\ntoolbox_debug_c(word32 xreg, word32 stack, dword64 *dcyc_ptr)\n{\n\tint\tpos;\n\n\tpos = g_toolbox_log_pos;\n\n\tstack += 9;\n\tg_toolbox_log_array[pos][0] = (word32)\n\t\t\t\t\t((g_last_vbl_dfcyc + *dcyc_ptr) >> 16);\n\tg_toolbox_log_array[pos][1] = stack+1;\n\tg_toolbox_log_array[pos][2] = xreg;\n\tg_toolbox_log_array[pos][3] = toolbox_debug_4byte(stack+1);\n\tg_toolbox_log_array[pos][4] = toolbox_debug_4byte(stack+5);\n\tg_toolbox_log_array[pos][5] = toolbox_debug_4byte(stack+9);\n\tg_toolbox_log_array[pos][6] = toolbox_debug_4byte(stack+13);\n\tg_toolbox_log_array[pos][7] = toolbox_debug_4byte(stack+17);\n\n\tpos++;\n\tif(pos >= TOOLBOX_LOG_LEN) {\n\t\tpos = 0;\n\t}\n\n\tg_toolbox_log_pos = pos;\n}\n\nvoid\nshow_toolbox_log()\n{\n\tint\tpos;\n\tint\ti;\n\n\tpos = g_toolbox_log_pos;\n\n\tfor(i = TOOLBOX_LOG_LEN - 1; i >= 0; i--) {\n\t\tprintf(\"%2d:%2d: %08x %06x  %04x: %08x %08x %08x %08x %08x\\n\",\n\t\t\ti, pos,\n\t\t\tg_toolbox_log_array[pos][0],\n\t\t\tg_toolbox_log_array[pos][1],\n\t\t\tg_toolbox_log_array[pos][2],\n\t\t\tg_toolbox_log_array[pos][3],\n\t\t\tg_toolbox_log_array[pos][4],\n\t\t\tg_toolbox_log_array[pos][5],\n\t\t\tg_toolbox_log_array[pos][6],\n\t\t\tg_toolbox_log_array[pos][7]);\n\t\tpos++;\n\t\tif(pos >= TOOLBOX_LOG_LEN) {\n\t\t\tpos = 0;\n\t\t}\n\t}\n}\n\nword32\nget_memory_io(word32 loc, dword64 *dcyc_ptr)\n{\n\tint\ttmp;\n\n\tif(loc > 0xffffff) {\n\t\thalt_printf(\"get_memory_io:%08x out of range==halt!\\n\", loc);\n\t\treturn 0;\n\t}\n\n\ttmp = loc & 0xfef000;\n\tif(tmp == 0xc000 || tmp == 0xe0c000) {\n\t\treturn(io_read(loc & 0xfff, dcyc_ptr));\n\t}\n\n\t/* Else it's an illegal addr...skip if memory sizing */\n\tif(loc >= g_mem_size_total) {\n\t\tif((loc & 0xfffe) == 0) {\n\t\t\t//printf(\"get_io assuming mem sizing, not halting\\n\");\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/* Skip reads to f80000 and f00000, just return 0 */\n\tif((loc & 0xf70000) == 0xf00000) {\n\t\treturn 0;\n\t}\n\n\tif((loc & 0xff0000) == 0xef0000) {\n\t\t/* DOC RAM */\n\t\treturn (doc_ram[loc & 0xffff]);\n\t}\n\tif((loc & 0xffff00) == 0xbcff00) {\n\t\t/* TWGS mapped some ROM here, we'll force in all 0's */\n\t\t/* If user has selected >= 12MB of memory, then mem will be */\n\t\t/*  returned and we won't ever get here */\n\t\treturn 0;\n\t}\n\n\tg_code_yellow++;\n\tif(g_ignore_bad_acc && !g_user_halt_bad) {\n\t\t/* print no message, just get out.  User doesn't want */\n\t\t/*  to be bothered by buggy programs */\n\t\treturn 0;\n\t}\n\n\tprintf(\"get_memory_io for addr: %06x\\n\", loc);\n\tprintf(\"stat for addr: %06x = %p\\n\", loc,\n\t\t\t\tGET_PAGE_INFO_RD((loc >> 8) & 0xffff));\n\tset_halt(g_halt_on_bad_read | g_user_halt_bad);\n\n\treturn 0;\n}\n\nvoid\nset_memory_io(word32 loc, int val, dword64 *dcyc_ptr)\n{\n\tword32\ttmp;\n\n\ttmp = loc & 0xfef000;\n\tif(tmp == 0xc000 || tmp == 0xe0c000) {\n\t\tio_write(loc, val, dcyc_ptr);\n\t\treturn;\n\t}\n\n\t/* Else it's an illegal addr */\n\tif(loc >= g_mem_size_total) {\n\t\tif((loc & 0xfffe) == 0) {\n\t\t\t//printf(\"set_io assuming mem sizing, not halting\\n\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/* ignore writes to ROM */\n\tif((loc & 0xfc0000) == 0xfc0000) {\n\t\treturn;\n\t}\n\n\tif((loc & 0xff0000) == 0xef0000) {\n\t\t/* DOC RAM */\n\t\tdoc_ram[loc & 0xffff] = val;\n\t\treturn;\n\t}\n\n\tif(g_ignore_bad_acc && !g_user_halt_bad) {\n\t\t/* print no message, just get out.  User doesn't want */\n\t\t/*  to be bothered by buggy programs */\n\t\treturn;\n\t}\n\n\tif((loc & 0xffc000) == 0x00c000) {\n\t\tprintf(\"set_memory %06x = %02x, warning\\n\", loc, val);\n\t\treturn;\n\t}\n\n\thalt_printf(\"set_memory %06x = %02x, stopping\\n\", loc, val);\n\n\treturn;\n}\n\nvoid\nshow_regs_act(Engine_reg *eptr)\n{\n\tint\ttmp_acc, tmp_x, tmp_y, tmp_psw, kpc, direct_page, dbank, stack;\n\n\tkpc = eptr->kpc;\n\ttmp_acc = eptr->acc;\n\tdirect_page = eptr->direct;\n\tdbank = eptr->dbank;\n\tstack = eptr->stack;\n\n\ttmp_x = eptr->xreg;\n\ttmp_y = eptr->yreg;\n\n\ttmp_psw = eptr->psr;\n\n\tdbg_printf(\"  PC=%02x.%04x A=%04x X=%04x Y=%04x P=%03x\",\n\t\tkpc>>16, kpc & 0xffff ,tmp_acc,tmp_x,tmp_y,tmp_psw);\n\tdbg_printf(\" S=%04x D=%04x B=%02x,cyc:%016llx\\n\", stack, direct_page,\n\t\tdbank, g_cur_dfcyc);\n}\n\nvoid\nshow_regs()\n{\n\tshow_regs_act(&engine);\n}\n\nvoid\nmy_exit(int ret)\n{\n\tg_a2_fatal_err = 0x10 + ret;\n\tprintf(\"exiting\\n\");\n}\n\n\nvoid\ndo_reset()\n{\n\tg_c035_shadow_reg = 0;\n\n\tg_c068_statereg = 0x200 | 0x08 | 0x04;\t// Set wrdefram, rdrom, lcbank2\n\tg_c02d_int_crom = 0xff;\n\tif(g_rom_version != 0) {\t\t// IIgs ROM01 or ROM03\n\t\tg_c068_statereg |= 0x01;\t// also set intcx\n\t\tg_c02d_int_crom = 0;\n\t}\n\tg_c023_val = 0;\n\tg_c041_val = 0;\n\n\tengine.psr = (engine.psr | 0x134) & ~(0x08);\n\tengine.stack = 0x100 + (engine.stack & 0xff);\n\tengine.dbank = 0;\n\tengine.direct = 0;\n\tengine.xreg &= 0xff;\n\tengine.yreg &= 0xff;\n\tg_wait_pending = 0;\n\tg_stp_pending = 0;\n\n\tvideo_reset();\n\tadb_reset();\n\tiwm_reset();\n\tscc_reset();\n\tsound_reset(g_cur_dfcyc);\n\tsetup_pageinfo();\n\tchange_display_mode(g_cur_dfcyc);\n\n\tg_irq_pending = 0;\n\tg_code_yellow = 0;\n\tg_code_red = 0;\n\n\tengine.kpc = get_memory16_c(0x00fffc);\n\n\tg_stepping = 0;\n}\n\n#define CHECK(start, var, value, var1, var2)\t\t\t\t\\\n\tvar2 = PTR2WORD(&(var));\t\t\t\t\t\\\n\tvar1 = PTR2WORD((start));\t\t\t\t\t\\\n\tif((var2 - var1) != value) {\t\t\t\t\t\\\n\t\tprintf(\"CHECK: \" #var \" is 0x%x, but \" #value \" is 0x%x\\n\", \\\n\t\t\t(var2 - var1), value);\t\t\t\t\\\n\t\texit(5);\t\t\t\t\t\t\\\n\t}\n\nbyte *\nmemalloc_align(int size, int skip_amt, void **alloc_ptr)\n{\n\tbyte\t*bptr;\n\tword32\taddr;\n\tword32\toffset;\n\n\tskip_amt = MY_MAX(256, skip_amt);\n\tbptr = calloc(size + skip_amt, 1);\n\tif(alloc_ptr) {\n\t\t/* Save allocation address */\n\t\t*alloc_ptr = bptr;\n\t}\n\n\taddr = PTR2WORD(bptr) & 0xff;\n\n\t/* must align bptr to be 256-byte aligned */\n\t/* this code should work even if ptrs are > 32 bits */\n\n\toffset = ((addr + skip_amt - 1) & (~0xff)) - addr;\n\n\treturn (bptr + offset);\n}\n\nvoid\nmemory_ptr_init()\n{\n\tword32\tmem_size;\n\n\t/* This routine may be called several times--each time the ROM file */\n\t/*  changes this will be called */\n\tmem_size = MY_MIN(0xdf0000, g_mem_size_base + g_mem_size_exp);\n\tif(g_rom_version == 0) {\t\t\t// Apple //e\n\t\tmem_size = g_mem_size_base;\n\t}\n\tg_mem_size_total = mem_size;\n\tif(g_memory_alloc_ptr) {\n\t\tfree(g_memory_alloc_ptr);\n\t\tg_memory_alloc_ptr = 0;\n\t}\n\tg_memory_ptr = memalloc_align(mem_size, 256, &g_memory_alloc_ptr);\n\n\tprintf(\"RAM size is 0 - %06x (%.2fMB)\\n\", mem_size,\n\t\t(double)mem_size/(1024.0*1024.0));\n}\n\nextern int g_screen_redraw_skip_amt;\nextern int g_use_dhr140;\nextern int g_use_bw_hires;\n\nchar g_display_env[512];\nint\tg_screen_depth = 8;\n\nint\nparse_argv(int argc, char **argv, int slashes_to_find)\n{\n\tbyte\t*bptr;\n\tchar\t*str, *arg2_str;\n\tint\tskip_amt, tmp1, len;\n\tint\ti;\n\n#if 0\n\t// If launched from Finder, no stdout, so send it to /tmp/out1\n\tfflush(stdout);\n\tsetvbuf(stdout, 0, _IONBF, 0);\n\tclose(1);\n\t(void)open(\"/tmp/out1\", O_WRONLY | O_CREAT | O_TRUNC, 0x1b6);\n#endif\n\n\tprintf(\"Starting KEGS v%s\\n\", &g_kegs_version_str[0]);\n\n\t// parse arguments\n\t// First, Check if KEGS_BIG_ENDIAN is set correctly\n\tg_word32_tmp = 0x01020304;\n\tbptr = (byte *)&(g_word32_tmp);\n#ifdef KEGS_BIG_ENDIAN\n\tbptr[0] = 6;\n\tbptr[3] = 5;\n#else\n\tbptr[0] = 5;\n\tbptr[3] = 6;\n#endif\n\tif(g_word32_tmp != 0x06020305) {\n\t\tfatal_printf(\"KEGS_BIG_ENDIAN is not properly set\\n\");\n\t\treturn 1;\n\t}\n\n\tstr = kegs_malloc_str(argv[0]);\n\tlen = (int)strlen(str);\n\tfor(i = len; i > 0; i--) {\n\t\tif(str[i] == '/') {\n\t\t\tif(--slashes_to_find <= 0) {\n\t\t\t\tstr[i] = 0;\n\t\t\t\tg_argv0_path = str;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tprintf(\"g_argv0_path: %s\\n\", g_argv0_path);\n\n\ti = 0;\n\twhile(++i < argc) {\n\t\tprintf(\"argv[%d] = %s\\n\", i, argv[i]);\n\t\tif(!strcmp(\"-badrd\", argv[i])) {\n\t\t\tprintf(\"Halting on bad reads\\n\");\n\t\t\tg_halt_on_bad_read = 2;\n\t\t} else if(!strcmp(\"-noignbadacc\", argv[i])) {\n\t\t\tprintf(\"Not ignoring bad memory accesses\\n\");\n\t\t\tg_ignore_bad_acc = 0;\n\t\t} else if(!strcmp(\"-noignhalt\", argv[i])) {\n\t\t\tprintf(\"Not ignoring code red halts\\n\");\n\t\t\tg_ignore_halts = 0;\n\t\t} else if(!strcmp(\"-24\", argv[i])) {\n\t\t\tprintf(\"Using 24-bit visual\\n\");\n\t\t\tg_force_depth = 24;\n\t\t} else if(!strcmp(\"-16\", argv[i])) {\n\t\t\tprintf(\"Using 16-bit visual\\n\");\n\t\t\tg_force_depth = 16;\n\t\t} else if(!strcmp(\"-15\", argv[i])) {\n\t\t\tprintf(\"Using 15-bit visual\\n\");\n\t\t\tg_force_depth = 15;\n\t\t} else if(!strcmp(\"-mem\", argv[i])) {\n\t\t\tif((i+1) >= argc) {\n\t\t\t\tprintf(\"Missing argument\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tg_mem_size_exp = strtol(argv[i+1], 0, 0) & 0x00ff0000;\n\t\t\tprintf(\"Using %d as memory size\\n\", g_mem_size_exp);\n\t\t\ti++;\n\t\t} else if(!strcmp(\"-skip\", argv[i])) {\n\t\t\tif((i+1) >= argc) {\n\t\t\t\tprintf(\"Missing to skip argument\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tskip_amt = (int)strtol(argv[i+1], 0, 0);\n\t\t\tprintf(\"Using %d as skip_amt\\n\", skip_amt);\n\t\t\tg_screen_redraw_skip_amt = skip_amt;\n\t\t\ti++;\n\t\t} else if(!strcmp(\"-audio\", argv[i])) {\n\t\t\tif((i+1) >= argc) {\n\t\t\t\tprintf(\"Missing argument to -audio\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\ttmp1 = (int)strtol(argv[i+1], 0, 0);\n\t\t\tprintf(\"Using %d as audio enable val\\n\", tmp1);\n\t\t\tg_audio_enable = tmp1;\n\t\t\ti++;\n\t\t} else if(!strcmp(\"-arate\", argv[i])) {\n\t\t\tif((i+1) >= argc) {\n\t\t\t\tprintf(\"Missing argument to -arate\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\ttmp1 = (int)strtol(argv[i+1], 0, 0);\n\t\t\tprintf(\"Using %d as preferred audio rate\\n\", tmp1);\n\t\t\tg_preferred_rate = tmp1;\n\t\t\ti++;\n\t\t} else if(!strcmp(\"-v\", argv[i])) {\n\t\t\tif((i+1) >= argc) {\n\t\t\t\tprintf(\"Missing argument to -v\\n\");\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\ttmp1 = (int)strtol(argv[i+1], 0, 0);\n\t\t\tprintf(\"Setting Verbose = 0x%03x\\n\", tmp1);\n\t\t\tVerbose = tmp1;\n\t\t\ti++;\n\t\t} else if(!strcmp(\"-display\", argv[i])) {\n\t\t\tif((i+1) >= argc) {\n\t\t\t\tprintf(\"Missing argument\\n\");\n\t\t\t\texit(1);\n\t\t\t}\n\t\t\tprintf(\"Using %s as display\\n\", argv[i+1]);\n\t\t\tsnprintf(g_display_env, sizeof(g_display_env),\n\t\t\t\t\t\t\"DISPLAY=%s\", argv[i+1]);\n\t\t\tputenv(&g_display_env[0]);\n\t\t\ti++;\n\t\t} else if(!strcmp(\"-noshm\", argv[i])) {\n\t\t\tprintf(\"Not using X shared memory\\n\");\n\t\t\tg_use_shmem = 0;\n\t\t} else if(!strcmp(\"-joystick\", argv[i])) {\n\t\t\tprintf(\"Ignoring -joystick option\\n\");\n\t\t} else if(!strcmp(\"-dhr140\", argv[i])) {\n\t\t\tprintf(\"Using simple dhires color map\\n\");\n\t\t\tg_use_dhr140 = 1;\n\t\t} else if(!strcmp(\"-bw\", argv[i])) {\n\t\t\tprintf(\"Forcing black-and-white hires modes\\n\");\n\t\t\tg_cur_a2_stat |= ALL_STAT_COLOR_C021;\n\t\t\tg_use_bw_hires = 1;\n\t\t} else if(!strncmp(\"-NS\", argv[i], 3)) {\n\t\t\t// Some Mac argument, just ignore it\n\t\t\tif((i + 1) < argc) {\n\t\t\t\t// If the next argument doesn't start with '-',\n\t\t\t\t//  then ignore it, too\n\t\t\t\tif(argv[i+1][0] != '-') {\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if(!strcmp(\"-logpc\", argv[i])) {\n\t\t\tprintf(\"Force logpc enable\\n\");\n\t\t\tdebug_logpc_on(\"on\");\n\t\t} else if(!strncmp(\"-cfg\", argv[i], 4)) {\n\t\t\tif((i + 1) < argc) {\n\t\t\t\tconfig_set_config_kegs_name(argv[i+1]);\n\t\t\t}\n\t\t\ti++;\n\t\t} else if(argv[i][0] == '-') {\n\t\t\targ2_str = 0;\n\t\t\tif((i + 1) < argc) {\n\t\t\t\targ2_str = argv[i+1];\n\t\t\t}\n\t\t\ti += config_add_argv_override(&argv[i][1], arg2_str);\n\t\t} else {\n\t\t\tprintf(\"Bad option: %s\\n\", argv[i]);\n\t\t\treturn 3;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nint\nkegs_init(int mdepth, int screen_width, int screen_height, int no_scale_window)\n{\n\tg_config_control_panel = 0;\n\n\twoz_crc_init();\n\tfixed_memory_ptrs_init();\n\n\tif(sizeof(word32) != 4) {\n\t\tprintf(\"sizeof(word32) = %d, must be 4!\\n\",\n\t\t\t\t\t\t\t(int)sizeof(word32));\n\t\treturn 1;\n\t}\n\tprepare_a2_font();\t\t// Prepare default built-in font\n\tscc_init();\n\tiwm_init();\n\tinit_reg();\n\tadb_init();\n\tinitialize_events();\n\tdebugger_init();\n\tsetup_pageinfo();\n\n\tconfig_init();\n\tload_roms_init_memory();\n\n\tclear_halt();\n\n\tvideo_init(mdepth, screen_width, screen_height, no_scale_window);\n\n\tsound_init();\n\tjoystick_init();\n\n\tif(g_rom_version >= 3) {\n\t\tg_c036_val_speed |= 0x40;\t/* set power-on bit */\n\t}\n\n\tdo_reset();\n\n\tg_stepping = 0;\n\tclear_halt();\n\tcfg_set_config_panel(g_config_control_panel);\n\n\treturn 0;\n}\n\nvoid\nload_roms_init_memory()\n{\n\tconfig_load_roms();\n\tmemory_ptr_init();\n\tclk_setup_bram_version();\t/* Must be after config_load_roms */\n\tif(g_rom_version >= 3) {\n\t\tg_c036_val_speed |= 0x40;\t/* set power-on bit */\n\t} else {\n\t\tg_c036_val_speed &= (~0x40);\t/* clear the bit */\n\t}\n\t// Do not call do_reset(), caller is responsible for that\n\n\t/* if user booted ROM 01, switches to ROM 03, then switches back */\n\t/*  to ROM 01, then the reset routines call to Tool $0102 looks */\n\t/*  at uninitialized $e1/15fe and if it is negative it will JMP */\n\t/*  through $e1/1688 which ROM 03 left pointing to fc/0199 */\n\t/* So set e1/15fe = 0 */\n\tset_memory16_c(0xe115fe, 0, 1);\n}\n\nEvent g_event_list[MAX_EVENTS];\nEvent g_event_free;\nEvent g_event_start;\n\nvoid\ninitialize_events()\n{\n\tint\ti;\n\n\tfor(i = 1; i < MAX_EVENTS; i++) {\n\t\tg_event_list[i-1].next = &g_event_list[i];\n\t}\n\tg_event_free.next = &g_event_list[0];\n\tg_event_list[MAX_EVENTS-1].next = 0;\n\n\tg_event_start.next = 0;\n\tg_event_start.dfcyc = 0;\n\n\tadd_event_entry(CYCLES_IN_16MS_RAW << 16, EV_60HZ);\n}\n\nvoid\ncheck_for_one_event_type(int type, word32 mask)\n{\n\tEvent\t*ptr;\n\tint\tcount, depth;\n\n\ttype = type & 0xff;\n\tcount = 0;\n\tdepth = 0;\n\tptr = g_event_start.next;\n\twhile(ptr != 0) {\n\t\tdepth++;\n\t\tif((ptr->type & mask) == (word32)type) {\n\t\t\tcount++;\n\t\t\tif(count != 1) {\n\t\t\t\thalt_printf(\"in check_for_1, type %04x found \"\n\t\t\t\t\t\"at depth: %d, count: %d, at %016llx\\n\",\n\t\t\t\t\tptr->type, depth, count, ptr->dfcyc);\n\t\t\t}\n\t\t}\n\t\tptr = ptr->next;\n\t}\n}\n\nvoid\nadd_event_entry(dword64 dfcyc, int type)\n{\n\tEvent\t*this_event;\n\tEvent\t*ptr, *prev_ptr;\n\tint\tdone;\n\n\tthis_event = g_event_free.next;\n\tif(this_event == 0) {\n\t\thalt_printf(\"Out of queue entries!\\n\");\n\t\tshow_all_events();\n\t\treturn;\n\t}\n\tg_event_free.next = this_event->next;\n\n\tthis_event->type = type;\n\n\tif((dfcyc > (g_cur_dfcyc + (50LL*1000*1000 << 16))) ||\n\t\t\t\t\t\t(dfcyc < g_cur_dfcyc)) {\n\t\thalt_printf(\"add_event bad dfcyc:%016llx, type:%05x, \"\n\t\t\t\"cur_dfcyc: %016llx!\\n\", dfcyc, type, g_cur_dfcyc);\n\t\tdfcyc = g_cur_dfcyc + (1000LL << 16);\n\t}\n\n\tptr = g_event_start.next;\n\tif(ptr && (dfcyc < ptr->dfcyc)) {\n\t\t/* create event before next expected event */\n\t\t/* do this by calling engine_recalc_events */\n\t\tengine_recalc_events();\n\t}\n\n\tprev_ptr = &g_event_start;\n\tptr = g_event_start.next;\n\n\tdone = 0;\n\twhile(!done) {\n\t\tif(ptr == 0) {\n\t\t\tthis_event->next = ptr;\n\t\t\tthis_event->dfcyc = dfcyc;\n\t\t\tprev_ptr->next = this_event;\n\t\t\tbreak;\n\t\t} else {\n\t\t\tif(ptr->dfcyc < dfcyc) {\t// step across this guy\n\t\t\t\tprev_ptr = ptr;\n\t\t\t\tptr = ptr->next;\n\t\t\t} else {\t\t\t// go before this guy */\n\t\t\t\tthis_event->dfcyc = dfcyc;\n\t\t\t\tthis_event->next = ptr;\n\t\t\t\tprev_ptr->next = this_event;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tcheck_for_one_event_type(type, 0xffff);\n}\n\nextern int g_doc_saved_ctl;\n\ndword64\nremove_event_entry(int type, word32 mask)\n{\n\tEvent\t*ptr, *prev_ptr;\n\tEvent\t*next_ptr;\n\n\tptr = g_event_start.next;\n\tprev_ptr = &g_event_start;\n\n\twhile(ptr != 0) {\n\t\tif((ptr->type & mask) == (word32)type) {\t// got it\n\t\t\tnext_ptr = ptr->next;\t\t\t// remove it\n\t\t\tprev_ptr->next = next_ptr;\n\n\t\t\t/* Add ptr to free list */\n\t\t\tptr->next = g_event_free.next;\n\t\t\tg_event_free.next = ptr;\n\n\t\t\treturn ptr->dfcyc;\n\t\t}\n\t\tprev_ptr = ptr;\n\t\tptr = ptr->next;\n\t}\n\n\thalt_printf(\"remove event_entry: %08x, but not found!\\n\", type);\n\tif((type & 0xff) == EV_DOC_INT) {\n\t\tprintf(\"DOC, g_doc_saved_ctl = %02x\\n\", g_doc_saved_ctl);\n\t}\n\tshow_all_events();\n\n\treturn 0;\n}\n\nvoid\nadd_event_stop(dword64 dfcyc)\n{\n\tadd_event_entry(dfcyc, EV_STOP);\n}\n\nvoid\nadd_event_doc(dword64 dfcyc, int osc)\n{\n\tif(dfcyc < g_cur_dfcyc) {\n\t\tdfcyc = g_cur_dfcyc;\n\t\t//halt_printf(\"add_event_doc: dfcyc:%016llx, cur_dfcyc:\"\n\t\t//\t\"%016llx\\n\", dfcyc, g_cur_dfcyc);\n\t}\n\n\tadd_event_entry(dfcyc, EV_DOC_INT + (osc << 8));\n}\n\nvoid\nadd_event_scc(dword64 dfcyc, int type)\n{\n\tif(dfcyc < g_cur_dfcyc) {\n\t\tdfcyc = g_cur_dfcyc;\n\t}\n\n\tadd_event_entry(dfcyc, EV_SCC + (type << 8));\n}\n\nvoid\nadd_event_vbl()\n{\n\tdword64\tdfcyc;\n\n\tdfcyc = g_last_vbl_dfcyc + ((192*65LL) << 16);\n\tadd_event_entry(dfcyc, EV_VBL_INT);\n}\n\nvoid\nadd_event_vid_upd(int line)\n{\n\tdword64\tdfcyc;\n\n\tdfcyc = g_last_vbl_dfcyc + (((line + 1) * 65LL) << 16);\n\tadd_event_entry(dfcyc, EV_VID_UPD + (line << 8));\n\t\t// Redraw line when video counters first read video data\n}\n\nvoid\nadd_event_mockingboard(dword64 dfcyc)\n{\n\tif(dfcyc < g_cur_dfcyc) {\n\t\tdfcyc = g_cur_dfcyc;\n\t}\n\tadd_event_entry(dfcyc, EV_MOCKINGBOARD);\n}\n\ndword64 g_dfcyc_scan_int = 0;\n\nvoid\nadd_event_scan_int(dword64 dfcyc, int line)\n{\n\tdword64\tdfcyc_scan_int;\n\n\tdfcyc_scan_int = g_dfcyc_scan_int;\n\tif(dfcyc_scan_int) {\t\t\t\t// Event is pending\n\t\tif(dfcyc >= g_dfcyc_scan_int) {\n\t\t\t// We are after (or the same) as current, do nothing\n\t\t\treturn;\n\t\t}\n\t\tremove_event_entry(EV_SCAN_INT, 0xff);\n\t}\n\tif(dfcyc < g_cur_dfcyc) {\n\t\t// scan_int for line 0 is found during EV_60HZ, and some\n\t\t//  cycles may have passed before the EV_60HZ was handled.\n\t\t// We need it to happen now, so just adjust dfcyc\n\t\tdfcyc = g_cur_dfcyc;\n\t}\n\tadd_event_entry(dfcyc, EV_SCAN_INT + (line << 8));\n\tg_dfcyc_scan_int = dfcyc;\n\n\tcheck_for_one_event_type(EV_SCAN_INT, 0xff);\n}\n\n\ndword64\nremove_event_doc(int osc)\n{\n\treturn remove_event_entry(EV_DOC_INT + (osc << 8), 0xffff);\n}\n\ndword64\nremove_event_scc(int type)\n{\n\treturn remove_event_entry(EV_SCC + (type << 8), 0xffff);\n}\n\nvoid\nremove_event_mockingboard()\n{\n\t(void)remove_event_entry(EV_MOCKINGBOARD, 0xff);\n}\n\nvoid\nshow_all_events()\n{\n\tEvent\t*ptr;\n\tdword64\tdfcyc;\n\tint\tcount;\n\n\tcount = 0;\n\tptr = g_event_start.next;\n\twhile(ptr != 0) {\n\t\tdfcyc = ptr->dfcyc;\n\t\tprintf(\"Event: %02x: type: %05x, dfcyc: %016llx (%08llx)\\n\",\n\t\t\tcount, ptr->type, dfcyc, dfcyc - g_cur_dfcyc);\n\t\tptr = ptr->next;\n\t\tcount++;\n\t}\n\n}\n\nword32\tg_vbl_count = 0;\nint\tg_vbl_index_count = 0;\ndouble\tdtime_array[60];\ndouble\tg_dadjcycs_array[60];\ndouble\tg_dtime_diff3_array[60];\ndouble\tg_dtime_this_vbl_array[60];\ndouble\tg_dtime_exp_array[60];\ndouble\tg_dtime_pmhz_array[60];\ndouble\tg_dtime_eff_pmhz_array[60];\ndouble\tg_dtime_in_run_16ms = 0;\ndouble\tg_dtime_outside_run_16ms = 0;\ndouble\tg_dtime_end_16ms = 0;\nint\tg_limit_speed = 3;\nint\tg_zip_speed_mhz = 16;\t\t// 16MHz default\ndouble\tsim_time[60];\ndouble\tg_sim_sum = 0.0;\n\ndouble\tg_cur_sim_dtime = 0.0;\ndouble\tg_projected_pmhz = 1.0;\ndouble\tg_zip_pmhz = 8.0;\ndouble\tg_sim_mhz = 100.0;\nint\tg_line_ref_amt = 1;\nint\tg_video_line_update_interval = 0;\ndword64\tg_video_pixel_dcount = 0;\n\nFplus\tg_recip_projected_pmhz_slow;\nFplus\tg_recip_projected_pmhz_fast;\nFplus\tg_recip_projected_pmhz_zip;\nFplus\tg_recip_projected_pmhz_unl;\n\nvoid\nshow_pmhz()\n{\n\tprintf(\"Pmhz: %f, c036:%02x, limit: %d\\n\",\n\t\tg_projected_pmhz, g_c036_val_speed, g_limit_speed);\n\n}\n\nvoid\nsetup_zip_speeds()\n{\n\tdword64\tdrecip;\n\tdouble\tfmhz;\n\tint\tmult;\n\n\tmult = 16 - ((g_zipgs_reg_c05a >> 4) & 0xf);\n\t\t// 16 = full speed, 1 = 1/16th speed\n\tfmhz = (g_zip_speed_mhz * mult) / 16.0;\n#if 0\n\tif(mult == 16) {\n\t\t/* increase full speed by 19% to make zipgs freq measuring */\n\t\t/* programs work correctly */\n\t\tfmhz = fmhz * 1.19;\n\t}\n#endif\n\tdrecip = (dword64)(65536 / fmhz);\n\tg_zip_pmhz = fmhz;\n\tg_recip_projected_pmhz_zip.dplus_1 = drecip;\n\tif(fmhz <= 2.0) {\n\t\tg_recip_projected_pmhz_zip.dplus_x_minus_1 =\n\t\t\t\t\t\t(dword64)(1.01 * 65536);\n\t} else {\n\t\tg_recip_projected_pmhz_zip.dplus_x_minus_1 =\n\t\t\t\t\t(dword64)(1.01 * 65536 - drecip);\n\t}\n}\n\nword32 g_cycs_end_16ms = 0;\n\nint\nrun_16ms()\n{\n\tdouble\tdtime_start, dtime_end, dtime_end2, dtime_outside;\n\tint\tret;\n\n\tdtime_start = get_dtime();\n\tret = 0;\n\tfflush(stdout);\n\tg_dtime_sleep = 1.0/61.0;\t\t// For control_panel/debugger\n\tif(g_config_control_panel) {\n\t\tret = cfg_control_panel_update();\n\t\tif(!g_config_control_panel) {\n\t\t\treturn 0;\t// Was just switched off, get out\n\t\t}\n\t} else {\n\t\tif(g_halt_sim) {\n\t\t\tret = debugger_run_16ms();\n\t\t} else {\n\t\t\tret = run_a2_one_vbl();\n\t\t}\n\t}\n\tvideo_update();\n\tg_vbl_count++;\n\tdtime_end = get_dtime();\n\tg_dtime_in_run_16ms += (dtime_end - dtime_start);\n\n\t// If we are ahead, then do the sleep now\n\tmicro_sleep(g_dtime_sleep);\n\tdtime_end2 = get_dtime();\n\t//printf(\"Did sleep for %f, dtime passed:%f\\n\", g_dtime_sleep,\n\t//\t\t\t\t\tdtime_end2 - dtime_end);\n\tg_dtime_sleep = 0.0;\n\n\tg_dtime_in_sleep += (dtime_end2 - dtime_end);\n\n\tdtime_outside = 0.0;\n\tif(g_vbl_count > 1) {\n\t\tdtime_outside += (dtime_start - g_dtime_end_16ms);\n\t}\n\tg_dtime_outside_run_16ms += dtime_outside;\n\tg_dtime_end_16ms = dtime_end2;\n#if 0\n\tif((g_vbl_count & 0xf) == 0) {\n\t\tprintf(\"run_16ms end at %.3lf, dtime_16ms:%1.5lf out:%1.5lf\\n\",\n\t\t\tdtime_end, dtime_end - dtime_start, dtime_outside);\n\t}\n#endif\n\treturn ret | g_a2_fatal_err;\n}\n\nint\nrun_a2_one_vbl()\n{\n\tFplus\t*fplus_ptr;\n\tEvent\t*this_event, *db1;\n\tdouble\tnow_dtime, prev_dtime, fspeed_mult;\n\tdword64\tdfcyc, dfcyc_stop, prerun_dfcyc;\n\tword32\tret, zip_speed_0tof, zip_speed_0tof_new;\n\tint\tzip_en, zip_follow_cps, type, motor_on, iwm_1, iwm_25, fast;\n\tint\tlimit_speed, apple35_sel, zip_speed, faster_than_28, unl_speed;\n\n\tfflush(stdout);\n\n\tg_cur_sim_dtime = 0.0;\n\n\tg_recip_projected_pmhz_slow.dplus_1 = 0x10000;\n\tg_recip_projected_pmhz_slow.dplus_x_minus_1 = (dword64)(0.9 * 0x10000);\n\n\tg_recip_projected_pmhz_fast.dplus_1 = (dword64)(0x10000 / 2.8);\n\tg_recip_projected_pmhz_fast.dplus_x_minus_1 = (dword64)\n\t\t\t\t((1.98 - (1.0/2.8)) * 0x10000);\n\n\tzip_speed_0tof = g_zipgs_reg_c05a & 0xf0;\n\tsetup_zip_speeds();\n\n\tif(engine.fplus_ptr == 0) {\n\t\tg_recip_projected_pmhz_unl = g_recip_projected_pmhz_slow;\n\t}\n\n\twhile(1) {\n\t\tfflush(stdout);\n\n\t\tif(g_irq_pending && !(engine.psr & 0x4)) {\n\t\t\tirq_printf(\"taking an irq!\\n\");\n\t\t\ttake_irq();\n\t\t\t/* Interrupt! */\n\t\t}\n\n\t\tmotor_on = g_iwm_motor_on;\n\t\tlimit_speed = g_limit_speed;\n\t\tapple35_sel = g_iwm.state & IWM_STATE_C031_APPLE35SEL;\n\t\tzip_en = ((g_zipgs_reg_c05b & 0x10) == 0);\n\t\tzip_follow_cps = ((g_zipgs_reg_c059 & 0x8) != 0);\n\t\tzip_speed_0tof_new = g_zipgs_reg_c05a & 0xf0;\n\t\tfast = (g_c036_val_speed & 0x80) || (zip_en && !zip_follow_cps);\n\n\t\tif(zip_speed_0tof_new != zip_speed_0tof) {\n\t\t\tzip_speed_0tof = zip_speed_0tof_new;\n\t\t\tsetup_zip_speeds();\n\t\t}\n\n\t\tiwm_1 = motor_on && !apple35_sel &&\n\t\t\t\t(g_c036_val_speed & 0x4) &&\n\t\t\t\t(g_slow_525_emul_wr || !g_fast_disk_emul);\n\t\tiwm_25 = (motor_on && apple35_sel) && !g_fast_disk_emul;\n\t\tfaster_than_28 = fast && (!iwm_1 && !iwm_25) && zip_en &&\n\t\t\t((limit_speed == 0) || (limit_speed == 3));\n\t\tzip_speed = faster_than_28 &&\n\t\t\t((zip_speed_0tof != 0) || (limit_speed == 3) ||\n\t\t\t\t\t\t\t(g_zipgs_unlock >= 4) );\n\t\tunl_speed = faster_than_28 && !zip_speed;\n\t\tif(unl_speed) {\n\t\t\t/* use unlimited speed */\n\t\t\tfspeed_mult = g_projected_pmhz;\n\t\t\tfplus_ptr = &g_recip_projected_pmhz_unl;\n\t\t} else if(zip_speed) {\n\t\t\tfspeed_mult = g_zip_pmhz;\n\t\t\tfplus_ptr = &g_recip_projected_pmhz_zip;\n\t\t} else if(fast && !iwm_1 && (iwm_25 || (limit_speed != 1))) {\n\t\t\tfspeed_mult = 2.5;\n\t\t\tfplus_ptr = &g_recip_projected_pmhz_fast;\n\t\t} else {\n\t\t\t/* else run slow */\n\t\t\tfspeed_mult = 1.0;\n\t\t\tfplus_ptr = &g_recip_projected_pmhz_slow;\n\t\t}\n\n\t\tengine.fplus_ptr = fplus_ptr;\n\n\t\tprerun_dfcyc = g_cur_dfcyc;\n\t\tengine.dfcyc = prerun_dfcyc;\n\t\tdfcyc_stop = g_event_start.next->dfcyc + 1;\n\t\tif(g_stepping) {\n\t\t\tdfcyc_stop = prerun_dfcyc + 1;\n\t\t}\n\t\tg_dcycles_end = dfcyc_stop;\n\n#if 0\n\t\tprintf(\"Enter engine, fcycs: %f, stop: %f\\n\",\n\t\t\tprerun_fcycles, fcycles_stop);\n\t\tprintf(\"g_cur_dfcyc:%016llx, last_vbl_dfcyc:%016llx\\n\",\n\t\t\tg_cur_dfcyc, g_last_vbl_dfcyc);\n#endif\n\n\t\tg_num_enter_engine++;\n\t\tprev_dtime = get_dtime();\n\n\t\tret = enter_engine(&engine);\n\n\t\tnow_dtime = get_dtime();\n\n\t\tg_cur_sim_dtime += (now_dtime - prev_dtime);\n\n\t\tdfcyc = engine.dfcyc;\n\t\tg_cur_dfcyc = dfcyc;\n\n\t\tg_dadjcycs += (engine.dfcyc - prerun_dfcyc) * (1/65536.0) *\n\t\t\t\t\tfspeed_mult;\n\n#if 0\n\t\tprintf(\"...back, engine.dfcyc: %016llx, dfcyc: %016llx\\n\",\n\t\t\t(double)engine.dfcyc, dfcyc);\n#endif\n\n\t\tif(ret != 0) {\n\t\t\tg_engine_action++;\n\t\t\thandle_action(ret);\n\t\t}\n\n\t\tthis_event = g_event_start.next;\n\t\twhile(dfcyc >= this_event->dfcyc) {\n\t\t\t/* Pop this guy off of the queue */\n\t\t\tg_event_start.next = this_event->next;\n\n\t\t\ttype = this_event->type;\n\t\t\tthis_event->next = g_event_free.next;\n\t\t\tg_event_free.next = this_event;\n\t\t\tdbg_log_info(dfcyc, type, 0, 0x101);\n\t\t\tswitch(type & 0xff) {\n\t\t\tcase EV_60HZ:\n\t\t\t\tupdate_60hz(dfcyc, now_dtime);\n\t\t\t\treturn 0;\n\t\t\t\tbreak;\n\t\t\tcase EV_STOP:\n\t\t\t\tprintf(\"type: EV_STOP\\n\");\n\t\t\t\tprintf(\"next: %p, dfcyc: %016llx\\n\",\n\t\t\t\t\t\tg_event_start.next, dfcyc);\n\t\t\t\tdb1 = g_event_start.next;\n\t\t\t\thalt_printf(\"next.dfcyc:%016llx\\n\", db1->dfcyc);\n\t\t\t\tbreak;\n\t\t\tcase EV_SCAN_INT:\n\t\t\t\tg_engine_scan_int++;\n\t\t\t\tirq_printf(\"type: scan int\\n\");\n\t\t\t\tdo_scan_int(dfcyc, type >> 8);\n\t\t\t\tbreak;\n\t\t\tcase EV_DOC_INT:\n\t\t\t\tg_engine_doc_int++;\n\t\t\t\tdoc_handle_event(type >> 8, dfcyc);\n\t\t\t\tbreak;\n\t\t\tcase EV_VBL_INT:\n\t\t\t\tdo_vbl_int();\n\t\t\t\tbreak;\n\t\t\tcase EV_SCC:\n\t\t\t\tscc_do_event(dfcyc, type >> 8);\n\t\t\t\tbreak;\n\t\t\tcase EV_VID_UPD:\n\t\t\t\tvideo_update_event_line(type >> 8);\n\t\t\t\tbreak;\n\t\t\tcase EV_MOCKINGBOARD:\n\t\t\t\tmockingboard_event(dfcyc);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tprintf(\"Unknown event: %d!\\n\", type);\n\t\t\t\texit(3);\n\t\t\t}\n\n\t\t\tthis_event = g_event_start.next;\n\n\t\t}\n\n\t\tif(g_event_start.next == 0) {\n\t\t\thalt_printf(\"ERROR...run_prog, event_start.n=0!\\n\");\n\t\t}\n\n\t\tif(g_halt_sim) {\n\t\t\tbreak;\n\t\t}\n\t\tif(g_stepping) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tprintf(\"leaving run_prog, g_halt_sim:%d\\n\", g_halt_sim);\n\n\treturn 0;\n}\n\nvoid\nadd_irq(word32 irq_mask)\n{\n\tif(g_irq_pending & irq_mask) {\n\t\t/* Already requested, just get out */\n\t\treturn;\n\t}\n\tg_irq_pending |= irq_mask;\n\tengine_recalc_events();\n}\n\nvoid\nremove_irq(word32 irq_mask)\n{\n\tg_irq_pending = g_irq_pending & (~irq_mask);\n}\n\nvoid\ntake_irq()\n{\n\tword32\tnew_kpc, va;\n\n\tirq_printf(\"Taking irq, at: %02x/%04x, psw: %02x, dfcyc:%016llx\\n\",\n\t\t\tengine.kpc >> 16, engine.kpc & 0xffff, engine.psr,\n\t\t\tg_cur_dfcyc);\n\n\tg_num_irq++;\n\tif(g_wait_pending) {\n\t\t/* step over WAI instruction */\n\t\tengine.kpc = (engine.kpc & 0xff0000) |\n\t\t\t\t\t\t((engine.kpc + 1) & 0xffff);\n\t\tg_wait_pending = 0;\n\t}\n\n\tif(engine.psr & 0x100) {\n\t\t/* Emulation */\n\t\tset_memory_c(engine.stack, (engine.kpc >> 8) & 0xff, 1);\n\t\tengine.stack = ((engine.stack -1) & 0xff) + 0x100;\n\n\t\tset_memory_c(engine.stack, engine.kpc & 0xff, 1);\n\t\tengine.stack = ((engine.stack -1) & 0xff) + 0x100;\n\n\t\tset_memory_c(engine.stack, (engine.psr & 0xef), 1);\n\t\t\t/* Clear B bit in psr on stack */\n\t\tengine.stack = ((engine.stack -1) & 0xff) + 0x100;\n\n\t\tva = 0xfffffe;\n\t} else {\n\t\t/* native */\n\t\tset_memory_c(engine.stack, (engine.kpc >> 16) & 0xff, 1);\n\t\tengine.stack = ((engine.stack -1) & 0xffff);\n\n\t\tset_memory_c(engine.stack, (engine.kpc >> 8) & 0xff, 1);\n\t\tengine.stack = ((engine.stack -1) & 0xffff);\n\n\t\tset_memory_c(engine.stack, engine.kpc & 0xff, 1);\n\t\tengine.stack = ((engine.stack -1) & 0xffff);\n\n\t\tset_memory_c(engine.stack, engine.psr & 0xff, 1);\n\t\tengine.stack = ((engine.stack -1) & 0xffff);\n\n\t\tva = 0xffffee;\n\t}\n\n\tva = moremem_fix_vector_pull(va);\n\tnew_kpc = get_memory_c(va);\n\tnew_kpc = new_kpc + (get_memory_c(va + 1) << 8);\n\n\tengine.psr = ((engine.psr & 0x1f3) | 0x4);\n\n\tengine.kpc = new_kpc;\n\tHALT_ON(HALT_ON_IRQ, \"Halting on IRQ\\n\");\n\n}\n\ndouble\tg_dtime_last_vbl = 0.0;\ndouble\tg_dtime_expected = (1.0/VBL_RATE);\t// Approximately 1.0/60.0\n\nint g_scan_int_events = 0;\n\nvoid\nshow_dtime_array()\n{\n\tdouble\tdfirst_time;\n\tdouble\tfirst_total_cycs;\n\tint\ti;\n\tint\tpos;\n\n\tdfirst_time = 0.0;\n\tfirst_total_cycs = 0.0;\n\n\n\tfor(i = 0; i < 60; i++) {\n\t\tpos = (g_vbl_index_count + i) % 60;\n\t\tprintf(\"%2d:%2d dt:%.5f adjc:%9.1f this_vbl:%.6f \"\n\t\t\t\"exp:%.5f p:%2.2f ep:%2.2f\\n\",\n\t\t\ti, pos,\n\t\t\tdtime_array[pos] - dfirst_time,\n\t\t\tg_dadjcycs_array[pos] - first_total_cycs,\n\t\t\tg_dtime_this_vbl_array[pos],\n\t\t\tg_dtime_exp_array[pos] - dfirst_time,\n\t\t\tg_dtime_pmhz_array[pos],\n\t\t\tg_dtime_eff_pmhz_array[pos]);\n\t\tdfirst_time = dtime_array[pos];\n\t\tfirst_total_cycs = g_dadjcycs_array[pos];\n\t}\n}\n\n\nvoid\nupdate_60hz(dword64 dfcyc, double dtime_now)\n{\n\tregister word32 end_time;\n\tchar\tstatus_buf[1024];\n\tchar\tsim_mhz_buf[128];\n\tchar\ttotal_mhz_buf[128];\n\tchar\tsp_buf[128];\n\tchar\t*sim_mhz_ptr, *total_mhz_ptr, *code_str1, *code_str2, *sp_str;\n\tdword64\tplanned_dcyc;\n\tdouble\teff_pmhz, predicted_pmhz, recip_predicted_pmhz;\n\tdouble\tdtime_this_vbl_sim, dtime_diff_1sec, dratio, dtime_diff;\n\tdouble\tdtime_till_expected, dtime_this_vbl, dadjcycs_this_vbl;\n\tdouble\tdadj_cycles_1sec, dnatcycs_1sec;\n\tint\ttmp, doit_3_persec, cur_vbl_index, prev_vbl_index;\n\n\t/* NOTE: this event is defined to occur before line 0 */\n\t/* It's actually happening at the start of the border for line (-1) */\n\t/* All other timings should be adjusted for this */\n\n\tirq_printf(\"vbl_60hz: vbl: %d, dfcyc:%016llx, last_vbl_dfcyc:%016llx\\n\",\n\t\tg_vbl_count, dfcyc, g_last_vbl_dfcyc);\n\n\tplanned_dcyc = CYCLES_IN_16MS_RAW << 16;\n\n\tg_last_vbl_dfcyc = g_last_vbl_dfcyc + planned_dcyc;\n\n\tadd_event_entry(g_last_vbl_dfcyc + planned_dcyc, EV_60HZ);\n\tcheck_for_one_event_type(EV_60HZ, 0xff);\n\n\tcur_vbl_index = g_vbl_index_count;\n\n\t/* figure out dtime spent running SIM, not all the overhead */\n\tdtime_this_vbl_sim = g_cur_sim_dtime;\n\tg_cur_sim_dtime = 0.0;\n\tg_sim_sum = g_sim_sum - sim_time[cur_vbl_index] + dtime_this_vbl_sim;\n\tsim_time[cur_vbl_index] = dtime_this_vbl_sim;\n\n\tdadj_cycles_1sec = g_dadjcycs - g_dadjcycs_array[cur_vbl_index];\n\n\t/* dtime_diff_1sec is dtime total spent over the last 60 ticks */\n\tdtime_diff_1sec = dtime_now - dtime_array[cur_vbl_index];\n\n\tdtime_array[cur_vbl_index] = dtime_now;\n\tg_dadjcycs_array[cur_vbl_index] = g_dadjcycs;\n\n\tprev_vbl_index = cur_vbl_index;\n\tcur_vbl_index = prev_vbl_index + 1;\n\tif(cur_vbl_index >= 60) {\n\t\tcur_vbl_index = 0;\n\t}\n\tg_vbl_index_count = cur_vbl_index;\n\n\tGET_ITIMER(end_time);\n\tg_dnatcycs_1sec += (double)(end_time - g_natcycs_lastvbl);\n\tg_natcycs_lastvbl = end_time;\n\n\tif(prev_vbl_index == 0) {\n\t\tif(g_sim_sum < (1.0/250.0)) {\n\t\t\tsim_mhz_ptr = \"???\";\n\t\t\tg_sim_mhz = 250.0;\n\t\t} else {\n\t\t\tg_sim_mhz = (dadj_cycles_1sec / g_sim_sum) /\n\t\t\t\t\t\t\t(1000.0*1000.0);\n\t\t\tif(g_sim_mhz > 8000.0) {\n\t\t\t\tg_sim_mhz = 8000.0;\n\t\t\t}\n\t\t\tsnprintf(sim_mhz_buf, sizeof(sim_mhz_buf), \"%6.2f\",\n\t\t\t\t\t\t\t\tg_sim_mhz);\n\t\t\tsim_mhz_ptr = sim_mhz_buf;\n\t\t}\n\t\tif(dtime_diff_1sec < (1.0/250.0)) {\n\t\t\ttotal_mhz_ptr = \"???\";\n\t\t} else {\n\t\t\tsnprintf(total_mhz_buf, sizeof(total_mhz_buf), \"%6.2f\",\n\t\t\t\t(dadj_cycles_1sec / dtime_diff_1sec) /\n\t\t\t\t\t\t\t\t(1000000.0));\n\t\t\ttotal_mhz_ptr = total_mhz_buf;\n\t\t}\n\n\t\tswitch(g_limit_speed) {\n\t\tcase 1:\tsp_str = \"1Mhz\"; break;\n\t\tcase 2:\tsp_str = \"2.8Mhz\"; break;\n\t\tcase 3:\tsp_str = \"8.0Mhz\"; break;\n\t\tdefault: sp_str = \"Unlimited\"; break;\n\t\t}\n\t\tif(g_limit_speed == 3) {\t\t// ZipGS\n\t\t\tsnprintf(sp_buf, sizeof(sp_buf), \"%1.1fMHz\",\n\t\t\t\t\t\t\tg_zip_pmhz);\n\t\t\tsp_str = sp_buf;\n\t\t}\n\n\t\tsnprintf(status_buf, sizeof(status_buf), \"dfcyc:%7.1f sim \"\n\t\t\t\"MHz:%s Eff MHz:%s, sec:%1.3f vol:%02x Limit:%s\",\n\t\t\t(double)(dfcyc >> 20)/65536.0, sim_mhz_ptr,\n\t\t\ttotal_mhz_ptr, dtime_diff_1sec, g_doc_vol, sp_str);\n\t\tvideo_update_status_line(0, status_buf);\n\n\t\tif(g_video_line_update_interval == 0) {\n\t\t\tif(g_sim_mhz > 12.0) {\n\t\t\t\t/* just set video line_ref_amt to 1 */\n\t\t\t\tg_line_ref_amt = 1;\n\t\t\t} else if(g_line_ref_amt == 1 && g_sim_mhz < 4.0) {\n\t\t\t\tg_line_ref_amt = 8;\n\t\t\t}\n\t\t} else {\n\t\t\tg_line_ref_amt = g_video_line_update_interval;\n\t\t}\n\n\t\tdnatcycs_1sec = g_dnatcycs_1sec;\n\t\tif(g_dnatcycs_1sec < (1000.0*1000.0)) {\n\t\t\t/* make it so large that all %'s become 0 */\n\t\t\tdnatcycs_1sec = 800.0*1000.0*1000.0*1000.0;\n\t\t}\n\t\tdnatcycs_1sec = dnatcycs_1sec / 100.0; /* eff mult by 100 */\n\n\t\tg_video_pixel_dcount = 0;\n\n\t\tcode_str1 = \"\";\n\t\tcode_str2 = \"\";\n\t\tif(g_code_yellow) {\n\t\t\tcode_str1 = \"Code: Yellow\";\n\t\t\tcode_str2 = \"Emulated state suspect\";\n\t\t}\n\t\tif(g_code_red) {\n\t\t\tcode_str1 = \"Code: RED\";\n\t\t\tcode_str2 = \"Emulated state corrupt?\";\n\t\t}\n\t\tsnprintf(status_buf, sizeof(status_buf), \"sleep_dtime:%8.6f, \"\n\t\t\t\"out_16ms:%8.6f, in_16ms:%8.6f, snd_pl:%d\",\n\t\t\tg_dtime_in_sleep, g_dtime_outside_run_16ms,\n\t\t\tg_dtime_in_run_16ms, g_num_snd_plays);\n\t\tvideo_update_status_line(1, status_buf);\n\n\t\tdraw_iwm_status(2, status_buf);\n\n\t\tsnprintf(status_buf, sizeof(status_buf), \" KEGS v%-6s       \"\n\t\t\t\"Press F4 for Config Menu    %s %s\",\n\t\t\tg_kegs_version_str, code_str1, code_str2);\n\t\tvideo_update_status_line(3, status_buf);\n\n\t\tg_status_refresh_needed = 1;\n\n\t\tg_num_irq = 0;\n\t\tg_num_brk = 0;\n\t\tg_num_cop = 0;\n\t\tg_num_enter_engine = 0;\n\t\tg_io_amt = 0;\n\t\tg_engine_action = 0;\n\t\tg_engine_recalc_event = 0;\n\t\tg_engine_scan_int = 0;\n\t\tg_engine_doc_int = 0;\n\n\t\tg_cycs_in_40col = 0;\n\t\tg_cycs_in_xredraw = 0;\n\t\tg_cycs_in_refresh_line = 0;\n\t\tg_dnatcycs_1sec = 0.0;\n\t\tg_dtime_outside_run_16ms = 0.0;\n\t\tg_dtime_in_run_16ms = 0.0;\n\t\tg_refresh_bytes_xfer = 0;\n\n\t\tg_dtime_in_sleep = 0;\n\t\tg_num_snd_plays = 0;\n\t\tg_num_recalc_snd_parms = 0;\n\t}\n\n\tdtime_this_vbl = dtime_now - g_dtime_last_vbl;\n\tif(dtime_this_vbl < 0.001) {\n\t\tdtime_this_vbl = 0.001;\n\t}\n\n\tg_dtime_last_vbl = dtime_now;\n\n\tdadjcycs_this_vbl = g_dadjcycs - g_last_vbl_dadjcycs;\n\tg_last_vbl_dadjcycs = g_dadjcycs;\n\n\tg_dtime_expected += (1.0/VBL_RATE);\t// Approx. 1/60\n\n\teff_pmhz = (dadjcycs_this_vbl / dtime_this_vbl) / DCYCS_1_MHZ;\n\n\t/* using eff_pmhz, predict how many cycles can be run by */\n\t/*  g_dtime_expected */\n\n\tdtime_till_expected = g_dtime_expected - dtime_now;\n\n\tdratio = VBL_RATE * dtime_till_expected;\t// Approx. 60*dtime_exp\n\n\tpredicted_pmhz = eff_pmhz * dratio;\n\n\tif(! (predicted_pmhz < (1.4 * g_projected_pmhz))) {\n\t\tpredicted_pmhz = 1.4 * g_projected_pmhz;\n\t}\n\n\tif(! (predicted_pmhz > (0.7 * g_projected_pmhz))) {\n\t\tpredicted_pmhz = 0.7 * g_projected_pmhz;\n\t}\n\n\tif(!(predicted_pmhz >= 1.0)) {\n\t\tirq_printf(\"predicted: %f, setting to 1.0\\n\", predicted_pmhz);\n\t\tpredicted_pmhz = 1.0;\n\t}\n\n\tif(!(predicted_pmhz < 4500.0)) {\n\t\tirq_printf(\"predicted: %f, set to 1900.0\\n\", predicted_pmhz);\n\t\tpredicted_pmhz = 4500.0;\n\t}\n\n\trecip_predicted_pmhz = 1.0/predicted_pmhz;\n\tg_projected_pmhz = predicted_pmhz;\n\n\tg_recip_projected_pmhz_unl.dplus_1 = (dword64)\n\t\t\t\t\t\t(65536 * recip_predicted_pmhz);\n\tg_recip_projected_pmhz_unl.dplus_x_minus_1 =\n\t\t\t(dword64)(65536 * (1.01 - recip_predicted_pmhz));\n\n\tif(dtime_till_expected < -0.125) {\n\t\t/* If we were way off, get back on track */\n\t\t/* this happens because our sim took much longer than */\n\t\t/* expected, so we're going to skip some VBL */\n\t\tirq_printf(\"adj1: dtexp:%f, dt_new:%f\\n\",\n\t\t\tg_dtime_expected, dtime_now);\n\n\t\tdtime_diff = -dtime_till_expected;\n\n\t\tirq_printf(\"dtime_till_exp:%f, dtime_diff:%f, dfcyc:%016llx\\n\",\n\t\t\tdtime_till_expected, dtime_diff, dfcyc);\n\n\t\tg_dtime_expected += dtime_diff;\n\t}\n\n\tg_dtime_sleep = 0.0;\n\tif(dtime_till_expected > (1.0/VBL_RATE)) {\n\t\t/* we're running fast, usleep */\n\t\tg_dtime_sleep = dtime_till_expected - (1.0/VBL_RATE);\n\t}\n#if 0\n\tprintf(\"Sleep %f, till_exp:%f, dtime_now:%f, exp:%f\\n\",\n\t\tg_dtime_sleep, dtime_till_expected, dtime_now,\n\t\tg_dtime_expected);\n#endif\n\n\tg_dtime_this_vbl_array[prev_vbl_index] = dtime_this_vbl;\n\tg_dtime_exp_array[prev_vbl_index] = g_dtime_expected;\n\tg_dtime_pmhz_array[prev_vbl_index] = predicted_pmhz;\n\tg_dtime_eff_pmhz_array[prev_vbl_index] = eff_pmhz;\n\n\n\tif(g_c041_val & C041_EN_VBL_INTS) {\n\t\tadd_event_vbl();\n\t}\n\n\tg_25sec_cntr++;\n\tif(g_25sec_cntr >= 16) {\n\t\tg_25sec_cntr = 0;\n\t\tif(g_c041_val & C041_EN_25SEC_INTS) {\n\t\t\tadd_irq(IRQ_PENDING_C046_25SEC);\n\t\t\tg_c046_val |= 0x10;\n\t\t\tirq_printf(\"Setting c046 .25 sec int, g_irq_pend:%d\\n\",\n\t\t\t\t\t\tg_irq_pending);\n\t\t}\n\t}\n\n\tg_1sec_cntr++;\n\tif(g_1sec_cntr >= 60) {\n\t\tg_1sec_cntr = 0;\n\t\ttmp = g_c023_val;\n\t\ttmp |= 0x40;\t/* set 1sec int */\n\t\tif(tmp & 0x04) {\n\t\t\ttmp |= 0x80;\n\t\t\tadd_irq(IRQ_PENDING_C023_1SEC);\n\t\t\tirq_printf(\"Setting c023 to %02x irq_pend: %d\\n\",\n\t\t\t\ttmp, g_irq_pending);\n\t\t}\n\t\tg_c023_val = tmp;\n\t}\n\n\tif(!g_scan_int_events) {\n\t\tcheck_scan_line_int(0);\n\t}\n\n\tdoit_3_persec = 0;\n\tif(g_config_iwm_vbl_count > 0) {\n\t\tg_config_iwm_vbl_count--;\n\t} else {\n\t\tg_config_iwm_vbl_count = 20;\n\t\tdoit_3_persec = 1;\n\t}\n\n\tiwm_vbl_update();\n\tconfig_vbl_update(doit_3_persec);\n\n\tsound_update(dfcyc);\n\tclock_update();\n\tscc_update(dfcyc);\n\tpaddle_update_buttons();\n}\n\nvoid\ndo_vbl_int()\n{\n\tif(g_c041_val & C041_EN_VBL_INTS) {\n\t\tg_c046_val |= 0x08;\n\t\tadd_irq(IRQ_PENDING_C046_VBL);\n\t\tirq_printf(\"Setting c046 vbl_int_status to 1, irq_pend: %d\\n\",\n\t\t\tg_irq_pending);\n\t}\n}\n\nvoid\ndo_scan_int(dword64 dfcyc, int line)\n{\n\tint\tc023_val;\n\n\tif(dfcyc) {\n\t\t// Avoid unused param warning\n\t}\n\n\tg_scan_int_events = 0;\n\tg_dfcyc_scan_int = 0;\n\n\tc023_val = g_c023_val;\n\tif(c023_val & 0x20) {\n\t\thalt_printf(\"c023 scan_int and another on line %03x\\n\", line);\n\t}\n\n#if 0\n\tdvbl = (dfcyc >> 16) / 17030;\n\tdline = ((dfcyc >> 16) - (dvbl * 17030)) / 65;\n\tprintf(\"do_scan_int at time %lld (%lld,line %lld), line:%d, SCB:%02x, \"\n\t\t\"a2_stat_ok:%d, c023:%02x\\n\", dfcyc >> 16, dvbl, dline, line,\n\t\t(g_slow_memory_ptr[0x19d00 + line] & 0x40),\n\t\t(g_cur_a2_stat & ALL_STAT_SUPER_HIRES) != 0, c023_val);\n\tfor(i = 0; i < 200; i++) {\n\t\tif(g_slow_memory_ptr[0x19d00 + i] & 0x40) {\n\t\t\tprintf(\"  Line %d has SCB:%02x\\n\", i,\n\t\t\t\tg_slow_memory_ptr[0x19d00 + i]);\n\t\t}\n\t}\n#endif\n\n\t/* make sure scan int is still enabled for this line */\n\tif((g_slow_memory_ptr[0x19d00 + line] & 0x40) &&\n\t\t\t\t(g_cur_a2_stat & ALL_STAT_SUPER_HIRES)) {\n\t\t/* valid interrupt, do it */\n\t\tc023_val |= 0xa0;\t/* vgc_int and scan_int */\n\t\tif(c023_val & 0x02) {\n\t\t\tadd_irq(IRQ_PENDING_C023_SCAN);\n\t\t\tirq_printf(\"Setting c023 to %02x, irq_pend: %d\\n\",\n\t\t\t\tc023_val, g_irq_pending);\n\t\t}\n\t\tg_c023_val = c023_val;\n\t\tHALT_ON(HALT_ON_SCAN_INT, \"In do_scan_int\\n\");\n\t} else {\n\t\t/* scan int bit cleared on scan line control byte */\n\t\t/* look for next line, if any */\n\t\tcheck_scan_line_int(line+1);\n\t}\n}\n\nvoid\ncheck_scan_line_int(int cur_video_line)\n{\n\tdword64\tddelay;\n\tint\tstart, line;\n\tint\ti;\n\n\t/* Called during VBL interrupt phase */\n\n\tif(!(g_cur_a2_stat & ALL_STAT_SUPER_HIRES)) {\n\t\treturn;\n\t}\n\n\tif(g_c023_val & 0x20) {\n\t\t/* don't check for any more */\n\t\treturn;\n\t}\n\n\tstart = cur_video_line;\n\tif(start < 0) {\n\t\thalt_printf(\"check_scan_line_int: cur_video_line: %d\\n\",\n\t\t\tcur_video_line);\n\t\tstart = 0;\n\t}\n\n\tfor(line = start; line < 200; line++) {\n\t\ti = line;\n\n\t\tif(i < 0 || i >= 200) {\n\t\t\thalt_printf(\"check_new_scan_int:i:%d, line:%d, st:%d\\n\",\n\t\t\t\ti, line, start);\n\t\t\ti = 0;\n\t\t}\n\t\tif(g_slow_memory_ptr[0x19d00 + i] & 0x40) {\n\t\t\tirq_printf(\"Adding scan_int for line %d\\n\", i);\n\t\t\tddelay = (65ULL * line) << 16;\n\t\t\tadd_event_scan_int(g_last_vbl_dfcyc + ddelay, line);\n\t\t\tg_scan_int_events = 1;\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid\ncheck_for_new_scan_int(dword64 dfcyc)\n{\n\tint\tcur_video_line;\n\n\tcur_video_line = get_lines_since_vbl(dfcyc) >> 8;\n\tcheck_scan_line_int(cur_video_line);\n}\n\nvoid\nscb_changed(dword64 dfcyc, word32 addr, word32 new_val, word32 old_val)\n{\n\tif(new_val & (~old_val) & 0x40) {\n\t\tcheck_for_new_scan_int(dfcyc);\n\t}\n\tif(addr) {\n\t}\n}\n\nvoid\ninit_reg()\n{\n\tmemset(&engine, 0, sizeof(engine));\n\n\tengine.acc = 0;\n\tengine.xreg = 0;\n\tengine.yreg = 0;\n\tengine.stack = 0x1ff;\n\tengine.direct = 0;\n\tengine.psr = 0x134;\n\tengine.fplus_ptr = 0;\n}\n\nvoid\nhandle_action(word32 ret)\n{\n\tint\ttype, arg;\n\n\ttype = ret & 0xff;\n\targ = ret >> 8;\n\tswitch(type) {\n\tcase RET_BREAK:\n\t\tdo_break(arg);\n\t\tbreak;\n\tcase RET_COP:\n\t\tdo_cop(arg);\n\t\tbreak;\n\tcase RET_IRQ:\n\t\tirq_printf(\"Special fast IRQ response.  irq_pending: %x\\n\",\n\t\t\tg_irq_pending);\n\t\tbreak;\n\tcase RET_WDM:\n\t\tdo_wdm(arg);\n\t\tbreak;\n\tcase RET_STP:\n\t\tdo_stp();\n\t\tbreak;\n\tcase RET_TOOLTRACE:\n\t\tdbg_log_info(g_cur_dfcyc, engine.kpc, engine.xreg,\n\t\t\t\t\t\t(engine.stack << 16) | 0xe100);\n\t\tbreak;\n\tdefault:\n\t\thalt_printf(\"Unknown special action: %08x!\\n\", ret);\n\t}\n}\n\n\nvoid\ndo_break(word32 ret)\n{\n\tprintf(\"I think I got a break, second byte: %02x!\\n\", ret);\n\tprintf(\"kpc: %06x\\n\", engine.kpc);\n\n\thalt_printf(\"do_break, kpc: %06x\\n\", engine.kpc);\n}\n\nvoid\ndo_cop(word32 ret)\n{\n\thalt_printf(\"COP instr %02x!\\n\", ret);\n\tfflush(stdout);\n}\n\nvoid\ndo_wdm(word32 arg)\n{\n\tif(arg == 0x00c7) {\n\t\t// WDM, 0xc7, 0x00: WDM in Slot 7\n\t\tif(engine.psr & 0x40) {\n\t\t\t// Overflow set: $C700 called\n\t\t\tdo_c700(arg);\n\t\t} else if(engine.psr & 1) {\t// V=0, C=1: $C70D called\n\t\t\tdo_c70d(arg);\n\t\t} else {\t\t\t// V=0, C=0, $C70A called\n\t\t\tdo_c70a(arg);\n\t\t}\n\t\treturn;\n\t}\n\tif(arg == 0x00ea) {\n\t\t// WDM, 0xea, 0x00: WDM emulator ID\n\t\tdo_wdm_emulator_id();\n\t\treturn;\n\t}\n\tif((arg == 0xeaea) && ((engine.psr & 0x171) == 0x41) &&\n\t\t\t\t\t\t(engine.acc == 0x4d44)) {\n\t\t// WDM $EA,$EA with V=1,C=1 and ACC=0x4d44 (\"EM\")\n\t\tengine.psr = engine.psr & 0x1bf;\t// V=0\n\t\t// printf(\"WDM $EA,$EA, cleared V=0, psr:%04x\\n\", engine.psr);\n\t\treturn;\n\t}\n\n\tswitch(arg & 0xff) {\n\tcase 0x8d: /* Bouncin Ferno does WDM 8d */\n\t\tbreak;\n\tcase 0xea:\t// Detectiong feature, don't flag an error\n\t\tbreak;\n\tcase 0xfc:\t// HOST.FST \"head_call\" for ATINIT for ProDOS 8\n\tcase 0xfd:\t// HOST.FST \"tail_call\" for ATINIT for ProDOS 8\n\tcase 0xff:\t// HOST.FST \"call_host\" for GS/OS driver\n\t\tbreak;\n\tdefault:\n\t\thalt_printf(\"do_wdm: %04x!\\n\", arg);\n\t}\n}\n\nvoid\ndo_wai()\n{\n\thalt_printf(\"do_wai!\\n\");\n}\n\nvoid\ndo_stp()\n{\n\tif(!g_stp_pending) {\n\t\tg_stp_pending = 1;\n\t\thalt_printf(\"Hit STP instruction at: %06x, press RESET to \"\n\t\t\t\t\"continue\\n\", engine.kpc);\n\t}\n}\n\nchar g_emulator_name[64];\n\nvoid\ndo_wdm_emulator_id()\n{\n\tword32\taddr, version, subvers;\n\tint\tmaxlen, len, c, got_dot;\n\tint\ti;\n\n\t// WDM, $EA, $00: WDM emulator ID\n\t// dbank.acc = address to write emulator description string\n\t// X = size of buffer to hold string\n\t// Y = 0 (not checked)\n\t// Returns: X: actual length of emulator string (always <=\n\t//\tvalue in X at call)\n\t// ACC: emulator version as: $VVMN as BCD, so 1.32 is $0132\n\t// Y: Emulation feature flags.  bit 0: $c06c-$c06f timer available\n\t// Works in emulation mode\n\n\tprintf(\"WDM EA at %06x.  acc:%04x, dbank:%02x xreg:%04x\\n\", engine.kpc,\n\t\t\t\tengine.acc, engine.dbank, engine.xreg);\n\tmaxlen = engine.xreg;\n\tcfg_strncpy(&g_emulator_name[0], \"KEGS v\", 64);\n\tcfg_strlcat(&g_emulator_name[0], &g_kegs_version_str[0], 64);\n\tlen = (int)strlen(&g_emulator_name[0]);\n\taddr = engine.acc;\n\tengine.xreg = 0;\n\tfor(i = 0; i < len; i++) {\n\t\tif(i >= maxlen) {\n\t\t\tbreak;\n\t\t}\n\t\taddr = (engine.dbank << 8) | (addr & 0xffff);\n\t\tset_memory_c(addr, 0x80 | g_emulator_name[i], 1);\n\t\taddr++;\n\t\tengine.xreg = i + 1;\n\t}\n\n\tversion = 0;\n\tsubvers = 0;\n\tlen = (int)strlen(&g_kegs_version_str[0]);\n\tgot_dot = 0;\n\tfor(i = 0; i < len; i++) {\n\t\tc = g_kegs_version_str[i];\n\t\tif(c == '.') {\n\t\t\tgot_dot++;\n\t\t}\n\t\tif(got_dot >= 3) {\n\t\t\tbreak;\n\t\t}\n\t\tif((c >= '0') && (c <= '9')) {\n\t\t\tc = c - '0';\n\t\t\tif(got_dot) {\n\t\t\t\tsubvers = (subvers << 4) | c;\n\t\t\t\tgot_dot++;\n\t\t\t} else {\n\t\t\t\tversion = (version << 4) | c;\n\t\t\t}\n\t\t}\n\t}\n\n\tengine.acc = ((version & 0xff) << 8) | (subvers & 0xff);\n\tengine.yreg = 0x01;\t\t// $C06C timer available\n}\n\nvoid\nsize_fail(int val, word32 v1, word32 v2)\n{\n\thalt_printf(\"Size failure, val: %08x, %08x %08x\\n\", val, v1, v2);\n}\n\nint\nfatal_printf(const char *fmt, ...)\n{\n\tva_list\tap;\n\tint\tret;\n\n\tva_start(ap, fmt);\n\n\tif(g_fatal_log < 0) {\n\t\tg_fatal_log = 0;\n\t}\n\tret = kegs_vprintf(fmt, ap);\n\tva_end(ap);\n\n\treturn ret;\n}\n\nint\nkegs_vprintf(const char *fmt, va_list ap)\n{\n\tchar\t*bufptr, *buf2ptr;\n\tint\tlen, ret;\n\n\tbufptr = malloc(4096);\n\tret = vsnprintf(bufptr, 4090, fmt, ap);\n\n\tlen = (int)strlen(bufptr);\n\tif(g_fatal_log >= 0 && g_fatal_log < MAX_FATAL_LOGS) {\n\t\tbuf2ptr = malloc(len+1);\n\t\tmemcpy(buf2ptr, bufptr, len+1);\n\t\tg_fatal_log_strs[g_fatal_log++] = buf2ptr;\n\t}\n\t(void)must_write(1, (byte *)bufptr, len);\n\tif(g_debug_file_fd >= 0) {\n\t\t(void)must_write(g_debug_file_fd, (byte *)bufptr, len);\n\t}\n\tfree(bufptr);\n\n\treturn ret;\n}\n\ndword64\nmust_write(int fd, byte *bufptr, dword64 dsize)\n{\n\tdword64\tdlen;\n\tlong long ret;\n\tword32\tthis_len;\n\n\tdlen = dsize;\n\twhile(dlen != 0) {\n\t\t// Support Windows64, which can only rd/wr 2GB max per call\n\t\tthis_len = (1UL << 30);\n\t\tif(dlen < this_len) {\n\t\t\tthis_len = (word32)dlen;\n\t\t}\n\t\tret = write(fd, bufptr, this_len);\n\t\tif(ret >= 0) {\n\t\t\tdlen -= ret;\n\t\t\tbufptr += ret;\n\t\t} else if((errno != EAGAIN) && (errno != EINTR)) {\n\t\t\treturn 0;\t\t// just get out\n\t\t}\n\t}\n\treturn dsize;\n}\n\nvoid\nclear_fatal_logs()\n{\n\tint\ti;\n\n\tfor(i = 0; i < g_fatal_log; i++) {\n\t\tfree(g_fatal_log_strs[i]);\n\t\tg_fatal_log_strs[i] = 0;\n\t}\n\tg_fatal_log = -1;\n}\n\nchar *\nkegs_malloc_str(const char *in_str)\n{\n\tchar\t*str;\n\tint\tlen;\n\n\tlen = (int)strlen(in_str) + 1;\n\tstr = malloc(len);\n\tmemcpy(str, in_str, len);\n\n\treturn str;\n}\n\ndword64\nkegs_lseek(int fd, dword64 offs, int whence)\n{\n#ifdef _WIN32\n\treturn _lseeki64(fd, offs, whence);\n#else\n\treturn lseek(fd, offs, whence);\n#endif\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/size_c.h",
    "content": "// \"@(#)$KmKId: size_c.h,v 1.2 2023-11-12 15:32:17+00 kentd Exp $\"\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2020 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n\n\t0x1,\t/* 00 */\t/* brk */\n\t0x1,\t/* 01 */\t/* ORA (Dloc,X) */\n\t0x1,\t/* 02 */\t/* COP */\n\t0x1,\t/* 03 */\t/* ORA Disp8,S */\n\t0x1,\t/* 04 */\t/* TSB Dloc */\n\t0x1,\t/* 05 */\t/* ORA Dloc */\n\t0x1,\t/* 06 */\t/* ASL Dloc */\n\t0x1,\t/* 07 */\t/* ORA [Dloc] */\n\t0x0,\t/* 08 */\t/* PHP */\n\t0x4,\t/* 09 */\t/* ORA #imm */\n\t0x0,\t/* 0a */\t/* ASL a */\n\t0x0,\t/* 0b */\t/* PHD */\n\t0x2,\t/* 0c */\t/* TSB abs */\n\t0x2,\t/* 0d */\t/* ORA abs */\n\t0x2,\t/* 0e */\t/* ASL abs */\n\t0x3,\t/* 0f */\t/* ORA long */\n\t0x1,\t/* 10 */\t/* BPL disp8 */\n\t0x1,\t/* 11 */\t/* ORA (),y */\n\t0x1,\t/* 12 */\t/* ORA () */\n\t0x1,\t/* 13 */\t/* ORA (disp8,s),y */\n\t0x1,\t/* 14 */\t/* TRB Dloc */\n\t0x1,\t/* 15 */\t/* ORA Dloc,x */\n\t0x1,\t/* 16 */\t/* ASL Dloc,x */\n\t0x1,\t/* 17 */\t/* ORA [],y */\n\t0x0,\t/* 18 */\t/* clc */\n\t0x2,\t/* 19 */\t/* ORA abs,y */\n\t0x0,\t/* 1a */\t/* INC a */\n\t0x0,\t/* 1b */\t/* TCS */\n\t0x2,\t/* 1c */\t/* TRB Abs */\n\t0x2,\t/* 1d */\t/* ORA Abs,X */\n\t0x2,\t/* 1e */\t/* ASL abs,x */\n\t0x3,\t/* 1f */\t/* ORA Long,x */\n\t0x2,\t/* 20 */\t/* JSR abs */\n\t0x1,\t/* 21 */\t/* AND (Dloc,X) */\n\t0x3,\t/* 22 */\t/* JSL Abslong */\n\t0x1,\t/* 23 */\t/* AND Disp8,S */\n\t0x1,\t/* 24 */\t/* BIT Dloc */\n\t0x1,\t/* 25 */\t/* AND Dloc */\n\t0x1,\t/* 26 */\t/* ROL Dloc */\n\t0x1,\t/* 27 */\t/* AND [Dloc] */\n\t0x0,\t/* 28 */\t/* PLP */\n\t0x4,\t/* 29 */\t/* AND #imm */\n\t0x0,\t/* 2a */\t/* ROL a */\n\t0x0,\t/* 2b */\t/* PLD */\n\t0x2,\t/* 2c */\t/* BIT abs */\n\t0x2,\t/* 2d */\t/* AND abs */\n\t0x2,\t/* 2e */\t/* ROL abs */\n\t0x3,\t/* 2f */\t/* AND long */\n\t0x1,\t/* 30 */\t/* BMI disp8 */\n\t0x1,\t/* 31 */\t/* AND (),y */\n\t0x1,\t/* 32 */\t/* AND () */\n\t0x1,\t/* 33 */\t/* AND (disp8,s),y */\n\t0x1,\t/* 34 */\t/* BIT Dloc,X */\n\t0x1,\t/* 35 */\t/* AND Dloc,x */\n\t0x1,\t/* 36 */\t/* ROL Dloc,x */\n\t0x1,\t/* 37 */\t/* AND [],y */\n\t0x0,\t/* 38 */\t/* SEC */\n\t0x2,\t/* 39 */\t/* AND abs,y */\n\t0x0,\t/* 3a */\t/* DEC a */\n\t0x0,\t/* 3b */\t/* TSC */\n\t0x2,\t/* 3c */\t/* BIT Abs,X */\n\t0x2,\t/* 3d */\t/* AND Abs,X */\n\t0x2,\t/* 3e */\t/* ROL abs,x */\n\t0x3,\t/* 3f */\t/* AND Long,x */\n\t0x0,\t/* 40 */\t/* RTI */\n\t0x1,\t/* 41 */\t/* EOR (Dloc,X) */\n\t0x2,\t/* 42 */\t/* WDM HACK: uses 2 args */\n\t0x1,\t/* 43 */\t/* EOR Disp8,S */\n\t0x2,\t/* 44 */\t/* MVP I,J */\n\t0x1,\t/* 45 */\t/* EOR Dloc */\n\t0x1,\t/* 46 */\t/* LSR Dloc */\n\t0x1,\t/* 47 */\t/* EOR [Dloc] */\n\t0x0,\t/* 48 */\t/* PHA */\n\t0x4,\t/* 49 */\t/* EOR #imm */\n\t0x0,\t/* 4a */\t/* LSR a */\n\t0x0,\t/* 4b */\t/* PHK */\n\t0x2,\t/* 4c */\t/* JMP abs */\n\t0x2,\t/* 4d */\t/* EOR abs */\n\t0x2,\t/* 4e */\t/* LSR abs */\n\t0x3,\t/* 4f */\t/* EOR long */\n\t0x1,\t/* 50 */\t/* BVC disp8 */\n\t0x1,\t/* 51 */\t/* EOR (),y */\n\t0x1,\t/* 52 */\t/* EOR () */\n\t0x1,\t/* 53 */\t/* EOR (disp8,s),y */\n\t0x2,\t/* 54 */\t/* MVN I,J */\n\t0x1,\t/* 55 */\t/* EOR Dloc,x */\n\t0x1,\t/* 56 */\t/* LSR Dloc,x */\n\t0x1,\t/* 57 */\t/* EOR [],y */\n\t0x0,\t/* 58 */\t/* CLI */\n\t0x2,\t/* 59 */\t/* EOR abs,y */\n\t0x0,\t/* 5a */\t/* PHY */\n\t0x0,\t/* 5b */\t/* TCD */\n\t0x3,\t/* 5c */\t/* JMP Long */\n\t0x2,\t/* 5d */\t/* EOR Abs,X */\n\t0x2,\t/* 5e */\t/* LSR abs,x */\n\t0x3,\t/* 5f */\t/* EOR Long,x */\n\t0x0,\t/* 60 */\t/* RTS */\n\t0x1,\t/* 61 */\t/* ADC (Dloc,X) */\n\t0x2,\t/* 62 */\t/* PER DISP16 */\n\t0x1,\t/* 63 */\t/* ADC Disp8,S */\n\t0x1,\t/* 64 */\t/* STZ Dloc */\n\t0x1,\t/* 65 */\t/* ADC Dloc */\n\t0x1,\t/* 66 */\t/* ROR Dloc */\n\t0x1,\t/* 67 */\t/* ADC [Dloc] */\n\t0x0,\t/* 68 */\t/* PLA */\n\t0x4,\t/* 69 */\t/* ADC #imm */\n\t0x0,\t/* 6a */\t/* ROR a */\n\t0x0,\t/* 6b */\t/* RTL */\n\t0x2,\t/* 6c */\t/* JMP (abs) */\n\t0x2,\t/* 6d */\t/* ADC abs */\n\t0x2,\t/* 6e */\t/* ROR abs */\n\t0x3,\t/* 6f */\t/* ADC long */\n\t0x1,\t/* 70 */\t/* BVS disp8 */\n\t0x1,\t/* 71 */\t/* ADC (),y */\n\t0x1,\t/* 72 */\t/* ADC () */\n\t0x1,\t/* 73 */\t/* ADC (disp8,s),y */\n\t0x1,\t/* 74 */\t/* STZ Dloc,X */\n\t0x1,\t/* 75 */\t/* ADC Dloc,x */\n\t0x1,\t/* 76 */\t/* ROR Dloc,x */\n\t0x1,\t/* 77 */\t/* ADC [],y */\n\t0x0,\t/* 78 */\t/* SEI */\n\t0x2,\t/* 79 */\t/* ADC abs,y */\n\t0x0,\t/* 7a */\t/* PLY */\n\t0x0,\t/* 7b */\t/* TDC */\n\t0x2,\t/* 7c */\t/* JMP (abs,x) */\n\t0x2,\t/* 7d */\t/* ADC Abs,X */\n\t0x2,\t/* 7e */\t/* ROR abs,x */\n\t0x3,\t/* 7f */\t/* ADC Long,x */\n\t0x1,\t/* 80 */\t/* BRA Disp8 */\n\t0x1,\t/* 81 */\t/* STA (Dloc,X) */\n\t0x2,\t/* 82 */\t/* BRL DISP16 */\n\t0x1,\t/* 83 */\t/* STA Disp8,S */\n\t0x1,\t/* 84 */\t/* STY Dloc */\n\t0x1,\t/* 85 */\t/* STA Dloc */\n\t0x1,\t/* 86 */\t/* STX Dloc */\n\t0x1,\t/* 87 */\t/* STA [Dloc] */\n\t0x0,\t/* 88 */\t/* DEY */\n\t0x4,\t/* 89 */\t/* BIT #imm */\n\t0x0,\t/* 8a */\t/* TXA */\n\t0x0,\t/* 8b */\t/* PHB */\n\t0x2,\t/* 8c */\t/* STY abs */\n\t0x2,\t/* 8d */\t/* STA abs */\n\t0x2,\t/* 8e */\t/* STX abs */\n\t0x3,\t/* 8f */\t/* STA long */\n\t0x1,\t/* 90 */\t/* BCC disp8 */\n\t0x1,\t/* 91 */\t/* STA (),y */\n\t0x1,\t/* 92 */\t/* STA () */\n\t0x1,\t/* 93 */\t/* STA (disp8,s),y */\n\t0x1,\t/* 94 */\t/* STY Dloc,X */\n\t0x1,\t/* 95 */\t/* STA Dloc,x */\n\t0x1,\t/* 96 */\t/* STX Dloc,y */\n\t0x1,\t/* 97 */\t/* STA [],y */\n\t0x0,\t/* 98 */\t/* TYA */\n\t0x2,\t/* 99 */\t/* STA abs,y */\n\t0x0,\t/* 9a */\t/* TXS */\n\t0x0,\t/* 9b */\t/* TXY */\n\t0x2,\t/* 9c */\t/* STX abs */\n\t0x2,\t/* 9d */\t/* STA Abs,X */\n\t0x2,\t/* 9e */\t/* STZ abs,x */\n\t0x3,\t/* 9f */\t/* STA Long,x */\n\t0x5,\t/* a0 */\t/* LDY #imm */\n\t0x1,\t/* a1 */\t/* LDA (Dloc,X) */\n\t0x5,\t/* a2 */\t/* LDX #imm */\n\t0x1,\t/* a3 */\t/* LDA Disp8,S */\n\t0x1,\t/* a4 */\t/* LDY Dloc */\n\t0x1,\t/* a5 */\t/* LDA Dloc */\n\t0x1,\t/* a6 */\t/* LDX Dloc */\n\t0x1,\t/* a7 */\t/* LDA [Dloc] */\n\t0x0,\t/* a8 */\t/* TAY */\n\t0x4,\t/* a9 */\t/* LDA #imm */\n\t0x0,\t/* aa */\t/* TAX */\n\t0x0,\t/* ab */\t/* PLB */\n\t0x2,\t/* ac */\t/* LDY abs */\n\t0x2,\t/* ad */\t/* LDA abs */\n\t0x2,\t/* ae */\t/* LDX abs */\n\t0x3,\t/* af */\t/* LDA long */\n\t0x1,\t/* b0 */\t/* BCS disp8 */\n\t0x1,\t/* b1 */\t/* LDA (),y */\n\t0x1,\t/* b2 */\t/* LDA () */\n\t0x1,\t/* b3 */\t/* LDA (disp8,s),y */\n\t0x1,\t/* b4 */\t/* LDY Dloc,X */\n\t0x1,\t/* b5 */\t/* LDA Dloc,x */\n\t0x1,\t/* b6 */\t/* LDX Dloc,y */\n\t0x1,\t/* b7 */\t/* LDA [],y */\n\t0x0,\t/* b8 */\t/* CLV */\n\t0x2,\t/* b9 */\t/* LDA abs,y */\n\t0x0,\t/* ba */\t/* TSX */\n\t0x0,\t/* bb */\t/* TYX */\n\t0x2,\t/* bc */\t/* LDY abs,x */\n\t0x2,\t/* bd */\t/* LDA Abs,X */\n\t0x2,\t/* be */\t/* LDX abs,y */\n\t0x3,\t/* bf */\t/* LDA Long,x */\n\t0x5,\t/* c0 */\t/* CPY #Imm */\n\t0x1,\t/* c1 */\t/* CMP (Dloc,X) */\n\t0x1,\t/* c2 */\t/* REP #8bit */\n\t0x1,\t/* c3 */\t/* CMP Disp8,S */\n\t0x1,\t/* c4 */\t/* CPY Dloc */\n\t0x1,\t/* c5 */\t/* CMP Dloc */\n\t0x1,\t/* c6 */\t/* DEC Dloc */\n\t0x1,\t/* c7 */\t/* CMP [Dloc] */\n\t0x0,\t/* c8 */\t/* INY */\n\t0x4,\t/* c9 */\t/* CMP #imm */\n\t0x0,\t/* ca */\t/* DEX */\n\t0x0,\t/* cb */\t/* WAI */\n\t0x2,\t/* cc */\t/* CPY abs */\n\t0x2,\t/* cd */\t/* CMP abs */\n\t0x2,\t/* ce */\t/* DEC abs */\n\t0x3,\t/* cf */\t/* CMP long */\n\t0x1,\t/* d0 */\t/* BNE disp8 */\n\t0x1,\t/* d1 */\t/* CMP (),y */\n\t0x1,\t/* d2 */\t/* CMP () */\n\t0x1,\t/* d3 */\t/* CMP (disp8,s),y */\n\t0x1,\t/* d4 */\t/* PEI Dloc */\n\t0x1,\t/* d5 */\t/* CMP Dloc,x */\n\t0x1,\t/* d6 */\t/* DEC Dloc,x */\n\t0x1,\t/* d7 */\t/* CMP [],y */\n\t0x0,\t/* d8 */\t/* CLD */\n\t0x2,\t/* d9 */\t/* CMP abs,y */\n\t0x0,\t/* da */\t/* PHX */\n\t0x0,\t/* db */\t/* STP */\n\t0x2,\t/* dc */\t/* JML (Abs) */\n\t0x2,\t/* dd */\t/* CMP Abs,X */\n\t0x2,\t/* de */\t/* DEC abs,x */\n\t0x3,\t/* df */\t/* CMP Long,x */\n\t0x5,\t/* e0 */\t/* CPX #Imm */\n\t0x1,\t/* e1 */\t/* SBC (Dloc,X) */\n\t0x1,\t/* e2 */\t/* SEP #8bit */\n\t0x1,\t/* e3 */\t/* SBC Disp8,S */\n\t0x1,\t/* e4 */\t/* CPX Dloc */\n\t0x1,\t/* e5 */\t/* SBC Dloc */\n\t0x1,\t/* e6 */\t/* INC Dloc */\n\t0x1,\t/* e7 */\t/* SBC [Dloc] */\n\t0x0,\t/* e8 */\t/* INX */\n\t0x4,\t/* e9 */\t/* SBC #imm */\n\t0x0,\t/* ea */\t/* NOP */\n\t0x0,\t/* eb */\t/* XBA */\n\t0x2,\t/* ec */\t/* CPX abs */\n\t0x2,\t/* ed */\t/* SBC abs */\n\t0x2,\t/* ee */\t/* INC abs */\n\t0x3,\t/* ef */\t/* SBC long */\n\t0x1,\t/* f0 */\t/* BEQ disp8 */\n\t0x1,\t/* f1 */\t/* SBC (),y */\n\t0x1,\t/* f2 */\t/* SBC () */\n\t0x1,\t/* f3 */\t/* SBC (disp8,s),y */\n\t0x2,\t/* f4 */\t/* PEA Imm */\n\t0x1,\t/* f5 */\t/* SBC Dloc,x */\n\t0x1,\t/* f6 */\t/* INC Dloc,x */\n\t0x1,\t/* f7 */\t/* SBC [],y */\n\t0x0,\t/* f8 */\t/* SED */\n\t0x2,\t/* f9 */\t/* SBC abs,y */\n\t0x0,\t/* fa */\t/* PLX */\n\t0x0,\t/* fb */\t/* XCE */\n\t0x2,\t/* fc */\t/* JSR (Abs,x) */\n\t0x2,\t/* fd */\t/* SBC Abs,X */\n\t0x2,\t/* fe */\t/* INC abs,x */\n\t0x3,\t/* ff */\t/* SBC Long,x */\n\n"
  },
  {
    "path": "upstream/kegs/src/smartport.c",
    "content": "const char rcsid_smartport_c[] = \"@(#)$KmKId: smartport.c,v 1.61 2024-09-15 13:53:46+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2024 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include \"defc.h\"\n\nextern int Verbose;\nextern int Halt_on;\nextern int g_rom_version;\nextern int g_io_amt;\nextern int g_highest_smartport_unit;\nextern dword64 g_cur_dfcyc;\n\nextern Engine_reg engine;\n\nextern Iwm g_iwm;\n\n#define LEN_SMPT_LOG\t16\nSTRUCT(Smpt_log) {\n\tword32\tstart_addr;\n\tint\tcmd;\n\tint\trts_addr;\n\tint\tcmd_list;\n\tint\textras;\n\tint\tunit;\n\tint\tbuf;\n\tint\tblk;\n};\n\nSmpt_log g_smpt_log[LEN_SMPT_LOG];\nint\tg_smpt_log_pos = 0;\n\nvoid\nsmartport_error(void)\n{\n\tint\tpos;\n\tint\ti;\n\n\tpos = g_smpt_log_pos;\n\tprintf(\"Smartport log pos: %d\\n\", pos);\n\tfor(i = 0; i < LEN_SMPT_LOG; i++) {\n\t\tpos--;\n\t\tif(pos < 0) {\n\t\t\tpos = LEN_SMPT_LOG - 1;\n\t\t}\n\t\tprintf(\"%d:%d: t:%04x, cmd:%02x, rts:%04x, \"\n\t\t\t\"cmd_l:%04x, x:%d, unit:%d, buf:%04x, blk:%04x\\n\",\n\t\t\ti, pos,\n\t\t\tg_smpt_log[pos].start_addr,\n\t\t\tg_smpt_log[pos].cmd,\n\t\t\tg_smpt_log[pos].rts_addr,\n\t\t\tg_smpt_log[pos].cmd_list,\n\t\t\tg_smpt_log[pos].extras,\n\t\t\tg_smpt_log[pos].unit,\n\t\t\tg_smpt_log[pos].buf,\n\t\t\tg_smpt_log[pos].blk);\n\t}\n}\nvoid\nsmartport_log(word32 start_addr, word32 cmd, word32 rts_addr, word32 cmd_list)\n{\n\tint\tpos;\n\n\tpos = g_smpt_log_pos;\n\tif(start_addr != 0) {\n\t\tg_smpt_log[pos].start_addr = start_addr;\n\t\tg_smpt_log[pos].cmd = cmd;\n\t\tg_smpt_log[pos].rts_addr = rts_addr;\n\t\tg_smpt_log[pos].cmd_list = cmd_list;\n\t\tg_smpt_log[pos].extras = 0;\n\t\tg_smpt_log[pos].unit = 0;\n\t\tg_smpt_log[pos].buf = 0;\n\t\tg_smpt_log[pos].blk = 0;\n\t} else {\n\t\tpos--;\n\t\tif(pos < 0) {\n\t\t\tpos = LEN_SMPT_LOG - 1;\n\t\t}\n\t\tg_smpt_log[pos].extras = 1;\n\t\tg_smpt_log[pos].unit = cmd;\n\t\tg_smpt_log[pos].buf = rts_addr;\n\t\tg_smpt_log[pos].blk = cmd_list;\n\t}\n\tpos++;\n\tif(pos >= LEN_SMPT_LOG) {\n\t\tpos = 0;\n\t}\n\tg_smpt_log_pos = pos;\n}\n\nvoid\ndo_c70d(word32 arg0)\n{\n\tdword64\tdsize;\n\tword32\tstatus_ptr, rts_addr, cmd_list,\tcmd_list_lo, cmd_list_mid;\n\tword32\tcmd_list_hi, status_ptr_lo, status_ptr_mid, status_ptr_hi;\n\tword32\trts_lo, rts_hi, buf_ptr_lo, buf_ptr_hi, buf_ptr, mask, cmd;\n\tword32\tblock_lo, block_mid, block_hi, block_hi2, unit, ctl_code;\n\tword32\tctl_ptr_lo, ctl_ptr_hi, ctl_ptr, block, stat_val;\n\tint\tparam_cnt, ret, ext, slot;\n\tint\ti;\n\n\tslot = (engine.kpc >> 8) & 7;\n\tset_memory_c(0x7f8, 0xc0 | slot, 1);\n\n\tif((engine.psr & 0x100) == 0) {\n\t\tdisk_printf(\"c70d %02x called in native mode!\\n\", arg0);\n\t\tif((engine.psr & 0x30) != 0x30) {\n\t\t\thalt_printf(\"c70d called native, psr: %03x!\\n\",\n\t\t\t\t\t\t\tengine.psr);\n\t\t}\n\t}\n\n\tengine.stack = ((engine.stack + 1) & 0xff) + 0x100;\n\trts_lo = get_memory_c(engine.stack);\n\tengine.stack = ((engine.stack + 1) & 0xff) + 0x100;\n\trts_hi = get_memory_c(engine.stack);\n\trts_addr = (rts_lo + (256*rts_hi) + 1) & 0xffff;\n\tdisk_printf(\"rts_addr: %04x\\n\", rts_addr);\n\n\tcmd = get_memory_c(rts_addr);\n\tcmd_list_lo = get_memory_c((rts_addr + 1) & 0xffff);\n\tcmd_list_mid = get_memory_c((rts_addr + 2) & 0xffff);\n\tcmd_list_hi = 0;\n\tmask = 0xffff;\n\text = 0;\n\tif(cmd & 0x40) {\n\t\text = 2;\n\t\tmask = 0xffffff;\n\t\tcmd_list_hi = get_memory_c((rts_addr + 3) & 0xffff);\n\t}\n\n\tcmd_list = cmd_list_lo + (256*cmd_list_mid) + (65536*cmd_list_hi);\n\n\tdisk_printf(\"cmd: %02x, cmd_list: %06x\\n\", cmd, cmd_list);\n\tparam_cnt = get_memory_c(cmd_list);\n\tunit = get_memory_c((cmd_list + 1) & mask);\n\tctl_code = get_memory_c((cmd_list + 4 + ext) & mask);\n\n\tsmartport_log(0xc70d, cmd, rts_addr, cmd_list);\n\tdbg_log_info(g_cur_dfcyc, (rts_addr << 16) | (unit << 8) | cmd,\n\t\t\t\t\t\t\tcmd_list, 0xc70d);\n#if 0\n\tif(cmd != 0x41) {\n\t\tprintf(\"SMTPT: c70d %08x, %08x at %016llx\\n\",\n\t\t\t(rts_addr << 16) | (unit << 8) | cmd, cmd_list,\n\t\t\tg_cur_dfcyc);\n\t}\n#endif\n\tret = 0;\n\tif((unit >= 1) && (unit <= MAX_C7_DISKS) && ext) {\n\t\tif(g_iwm.smartport[unit-1].just_ejected) {\n\t\t\tret = 0x2e;\t\t// DISKSW error\n\t\t}\n\t\tg_iwm.smartport[unit-1].just_ejected = 0;\n\t}\n\n\tswitch(cmd & 0x3f) {\n\tcase 0x00:\t/* Status == 0x00 and 0x40 */\n\t\tif(param_cnt != 3) {\n\t\t\tdisk_printf(\"param_cnt %d is != 3!\\n\", param_cnt);\n\t\t\tret = 0x04;\t\t// BADPCNT\n\t\t\tbreak;\n\t\t}\n\t\tstatus_ptr_lo = get_memory_c((cmd_list+2) & mask);\n\t\tstatus_ptr_mid = get_memory_c((cmd_list+3) & mask);\n\t\tstatus_ptr_hi = 0;\n\t\tif(cmd & 0x40) {\n\t\t\tstatus_ptr_hi = get_memory_c((cmd_list+4) & mask);\n\t\t}\n\n\t\tstatus_ptr = status_ptr_lo + (256*status_ptr_mid) +\n\t\t\t\t\t\t\t(65536*status_ptr_hi);\n\n\t\tsmartport_log(0, unit, status_ptr, ctl_code);\n\t\tdbg_log_info(g_cur_dfcyc, (ctl_code << 16) | unit,\n\t\t\t\t\t\t\tcmd_list, 0xc700);\n\n\t\tdisk_printf(\"unit: %02x, status_ptr: %06x, code: %02x\\n\",\n\t\t\tunit, status_ptr, ctl_code);\n\t\tif((unit == 0) && (ctl_code == 0)) {\n\t\t\t/* Smartport driver status */\n\t\t\t/* see technotes/smpt/tn-smpt-002 */\n\t\t\tset_memory_c(status_ptr, MAX_C7_DISKS, 1);\n\t\t\tset_memory_c(status_ptr+1, 0xff, 1);\t// intrpt stat\n\t\t\tset_memory16_c(status_ptr+2, 0x004b, 1); // vendor id\n\t\t\tset_memory16_c(status_ptr+4, 0x1000, 1); // version\n\t\t\tset_memory16_c(status_ptr+6, 0x0000, 1);\n\t\t\t//printf(\" driver status, highest_unit:%02x\\n\",\n\t\t\t//\t\tg_highest_smartport_unit+1);\n\n\t\t\tengine.xreg = 8;\n\t\t\tengine.yreg = 0;\n\t\t} else if((unit > 0) && (ctl_code == 0)) {\n\t\t\t/* status for unit x */\n\t\t\tif((unit > MAX_C7_DISKS) ||\n\t\t\t\t\t(g_iwm.smartport[unit-1].fd < 0)) {\n\t\t\t\tstat_val = 0x80;\n\t\t\t\tdsize = 0;\n\t\t\t\tret = 0;\t// Not DISK_SWITCHed error\n\t\t\t} else {\n\t\t\t\tstat_val = 0xf8;\n\t\t\t\tdsize = g_iwm.smartport[unit-1].dimage_size;\n\t\t\t\tdsize = (dsize+511) / 512;\n\t\t\t\tif(g_iwm.smartport[unit-1].write_prot) {\n\t\t\t\t\tstat_val |= 4;\t\t// Write prot\n\t\t\t\t}\n\t\t\t}\n#if 0\n\t\t\tprintf(\"  status unit:%02x just_ejected:%d, \"\n\t\t\t\t\t\"stat_val:%02x\\n\", unit,\n\t\t\t\t\tg_iwm.smartport[unit-1].just_ejected,\n\t\t\t\t\tstat_val);\n#endif\n\t\t\tset_memory_c(status_ptr, stat_val, 1);\n\t\t\tset_memory24_c(status_ptr + 1, (word32)dsize);\n\t\t\tengine.xreg = 4;\n\t\t\tif(cmd & 0x40) {\n\t\t\t\tset_memory_c(status_ptr + 4,\n\t\t\t\t\t\t(dsize >> 24) & 0xff, 1);\n\t\t\t\tengine.xreg = 5;\n\t\t\t}\n\t\t\tengine.yreg = 0;\n\t\t\tdisk_printf(\"just finished unit %d, stat 0\\n\", unit);\n\t\t} else if(ctl_code == 3) {\n\t\t\tif((unit > MAX_C7_DISKS) ||\n\t\t\t\t\t(g_iwm.smartport[unit-1].fd < 0)) {\n\t\t\t\tstat_val = 0x80;\n\t\t\t\tdsize = 0;\n\t\t\t\tret = 0;\t// Not a disk-switched error\n\t\t\t} else {\n\t\t\t\tstat_val = 0xf8;\n\t\t\t\tdsize = g_iwm.smartport[unit-1].dimage_size;\n\t\t\t\tdsize = (dsize + 511) / 512;\n\t\t\t}\n\t\t\tif(cmd & 0x40) {\n\t\t\t\tdisk_printf(\"extended for stat_code 3!\\n\");\n\t\t\t}\n\t\t\t/* DIB for unit 1 */\n\t\t\tset_memory_c(status_ptr, stat_val, 1);\n\t\t\tset_memory24_c(status_ptr + 1, (word32)dsize);\n\t\t\tif(cmd & 0x40) {\n\t\t\t\tset_memory_c(status_ptr + 4,\n\t\t\t\t\t\t(dsize >> 24) & 0xff, 1);\n\t\t\t\tstatus_ptr++;\n\t\t\t}\n\t\t\tset_memory_c(status_ptr + 4, 4, 1);\n\t\t\tfor(i = 5; i < 21; i++) {\n\t\t\t\tset_memory_c(status_ptr + i, 0x20, 1);\n\t\t\t}\n\t\t\tset_memory_c(status_ptr + 5, 'K', 1);\n\t\t\tset_memory_c(status_ptr + 6, 'E', 1);\n\t\t\tset_memory_c(status_ptr + 7, 'G', 1);\n\t\t\tset_memory_c(status_ptr + 8, 'S', 1);\n\n\t\t\t// Profile hard disk supporting extended calls+disk_sw\n\t\t\tset_memory16_c(status_ptr + 21, 0xc002, 1);\n\t\t\tset_memory16_c(status_ptr + 23, 0x0000, 1);\n\n\t\t\tif(cmd & 0x40) {\n\t\t\t\tengine.xreg = 26;\n\t\t\t} else {\n\t\t\t\tengine.xreg = 25;\n\t\t\t}\n#if 0\n\t\t\tprintf(\"  DIB unit:%02x just_ejected:%d, \"\n\t\t\t\t\t\"stat_val:%02x\\n\", unit,\n\t\t\t\t\tg_iwm.smartport[unit-1].just_ejected,\n\t\t\t\t\tstat_val);\n#endif\n\t\t\tengine.yreg = 0;\n\n\t\t\tdisk_printf(\"Just finished unit %d, stat 3\\n\", unit);\n\t\t\tif(unit == 0 || unit > MAX_C7_DISKS) {\n\t\t\t\tret = 0x28;\t\t// NODRIVE error\n\t\t\t}\n\t\t} else {\n\t\t\tprintf(\"cmd: 00, unknown unit/status code %02x!\\n\",\n\t\t\t\t\t\t\t\tctl_code);\n\t\t\tret = 0x21;\t\t\t// BADCTL\n\t\t}\n\t\tbreak;\n\tcase 0x01:\t/* Read Block == 0x01 and 0x41 */\n\t\tif(param_cnt != 3) {\n\t\t\thalt_printf(\"param_cnt %d is != 3!\\n\", param_cnt);\n\t\t\tret = 0x04;\t\t// BADPCNT\n\t\t\tbreak;\n\t\t}\n\t\tbuf_ptr_lo = get_memory_c((cmd_list+2) & mask);\n\t\tbuf_ptr_hi = get_memory_c((cmd_list+3) & mask);\n\n\t\tbuf_ptr = buf_ptr_lo + (256*buf_ptr_hi);\n\t\tif(cmd & 0x40) {\n\t\t\tbuf_ptr_lo = get_memory_c((cmd_list+4) & mask);\n\t\t\tbuf_ptr_hi = get_memory_c((cmd_list+5) & mask);\n\t\t\tbuf_ptr += ((buf_ptr_hi*256) + buf_ptr_lo)*65536;\n\t\t\tcmd_list += 2;\n\t\t}\n\t\tblock_lo = get_memory_c((cmd_list+4) & mask);\n\t\tblock_mid = get_memory_c((cmd_list+5) & mask);\n\t\tblock_hi = get_memory_c((cmd_list+6) & mask);\n\t\tblock_hi2 = 0;\n\t\tif(cmd & 0x40) {\n\t\t\tblock_hi2 = get_memory_c((cmd_list+7) & mask);\n\t\t}\n\t\tblock = (block_hi2 << 24) | (block_hi << 16) |\n\t\t\t\t\t(block_mid << 8) | block_lo;\n\t\tdisk_printf(\"smartport read unit %d of block %06x to %06x\\n\",\n\t\t\tunit, block, buf_ptr);\n\t\tif(unit < 1 || unit > MAX_C7_DISKS) {\n\t\t\thalt_printf(\"Unknown unit #: %d\\n\", unit);\n\t\t}\n\n\t\tsmartport_log(0, unit - 1, buf_ptr, block);\n\n\t\tif(ret == 0) {\n\t\t\tret = do_read_c7(unit - 1, buf_ptr, block);\n\t\t}\n\t\tengine.xreg = 0;\n\t\tengine.yreg = 2;\n\t\tbreak;\n\tcase 0x02:\t/* Write Block == 0x02 and 0x42 */\n\t\tif(param_cnt != 3) {\n\t\t\thalt_printf(\"param_cnt %d is != 3!\\n\", param_cnt);\n\t\t\tret = 0x04;\t\t// BADPCNT\n\t\t\tbreak;\n\t\t}\n\t\tbuf_ptr_lo = get_memory_c((cmd_list+2) & mask);\n\t\tbuf_ptr_hi = get_memory_c((cmd_list+3) & mask);\n\n\t\tbuf_ptr = buf_ptr_lo + (256*buf_ptr_hi);\n\t\tif(cmd & 0x40) {\n\t\t\tbuf_ptr_lo = get_memory_c((cmd_list+4) & mask);\n\t\t\tbuf_ptr_hi = get_memory_c((cmd_list+5) & mask);\n\t\t\tbuf_ptr += ((buf_ptr_hi*256) + buf_ptr_lo)*65536;\n\t\t\tcmd_list += 2;\n\t\t}\n\t\tblock_lo = get_memory_c((cmd_list+4) & mask);\n\t\tblock_mid = get_memory_c((cmd_list+5) & mask);\n\t\tblock_hi = get_memory_c((cmd_list+6) & mask);\n\t\tblock_hi2 = 0;\n\t\tif(cmd & 0x40) {\n\t\t\tblock_hi2 = get_memory_c((cmd_list+7) & mask);\n\t\t}\n\t\tblock = (block_hi2 << 24) | (block_hi << 16) |\n\t\t\t\t\t(block_mid << 8) | block_lo;\n\t\tdisk_printf(\"smartport write unit %d of block %04x from %04x\\n\",\n\t\t\tunit, block, buf_ptr);\n\t\tif(unit < 1 || unit > MAX_C7_DISKS) {\n\t\t\thalt_printf(\"Unknown unit #: %d\\n\", unit);\n\t\t}\n\n\t\tsmartport_log(0, unit - 1, buf_ptr, block);\n\n\t\tif(ret == 0) {\n\t\t\tret = do_write_c7(unit - 1, buf_ptr, block);\n\t\t}\n\t\tengine.xreg = 0;\n\t\tengine.yreg = 2;\n\n\t\tHALT_ON(HALT_ON_C70D_WRITES, \"c70d Write done\\n\");\n\t\tbreak;\n\tcase 0x03:\t/* Format == 0x03 and 0x43 */\n\t\tif(param_cnt != 1) {\n\t\t\thalt_printf(\"param_cnt %d is != 1!\\n\", param_cnt);\n\t\t\tret = 0x04;\t\t// BADPCNT\n\t\t\tbreak;\n\t\t}\n\t\tif((unit < 1) || (unit > MAX_C7_DISKS)) {\n\t\t\thalt_printf(\"Unknown unit #: %d\\n\", unit);\n\t\t\tret = 0x11;\t\t// BADUNIT\n\t\t}\n\n\t\tsmartport_log(0, unit - 1, 0, 0);\n\n\t\tif(ret == 0) {\n\t\t\tret = do_format_c7(unit - 1);\n\t\t}\n\t\tengine.xreg = 0;\n\t\tengine.yreg = 2;\n\n\t\tHALT_ON(HALT_ON_C70D_WRITES, \"c70d Format done\\n\");\n\t\tbreak;\n\tcase 0x04:\t/* Control == 0x04 and 0x44 */\n\t\tif(cmd == 0x44) {\n\t\t\thalt_printf(\"smartport code 0x44 not supported\\n\");\n\t\t}\n\t\tif(param_cnt != 3) {\n\t\t\thalt_printf(\"param_cnt %d is != 3!\\n\", param_cnt);\n\t\t\tbreak;\n\t\t}\n\t\tctl_ptr_lo = get_memory_c((cmd_list+2) & mask);\n\t\tctl_ptr_hi = get_memory_c((cmd_list+3) & mask);\n\t\tctl_ptr = (ctl_ptr_hi << 8) + ctl_ptr_lo;\n\t\tif(cmd & 0x40) {\n\t\t\tctl_ptr_lo = get_memory_c((cmd_list+4) & mask);\n\t\t\tctl_ptr_hi = get_memory_c((cmd_list+5) & mask);\n\t\t\tctl_ptr += ((ctl_ptr_hi << 8) + ctl_ptr_lo) << 16;\n\t\t\tcmd_list += 2;\n\t\t}\n\n\t\tswitch(ctl_code) {\n\t\tcase 0x00:\n\t\t\tprintf(\"Performing a reset on unit %d\\n\", unit);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thalt_printf(\"control code: %02x ptr:%06x unknown!\\n\",\n\t\t\t\t\t\t\tctl_code, ctl_ptr);\n\t\t}\n\t\t// printf(\"CONTROL, ctl_code:%02x\\n\", ctl_code);\n\n\t\tengine.xreg = 0;\n\t\tengine.yreg = 2;\n\t\tbreak;\n\tdefault:\t/* Unknown command! */\n\t\t/* set acc = 1, and set carry, and set kpc */\n\t\tengine.xreg = (rts_addr) & 0xff;\n\t\tengine.yreg = (rts_addr >> 8) & 0xff;\n\t\tret = 0x01;\t\t// BADCMD error\n\t\tif((cmd != 0x4b) && (cmd != 0x48) && (cmd != 0x4a)) {\n\t\t\t// Finder does 0x4a before dialog for formatting disk\n\t\t\t// Finder does 0x4b call before formatting disk\n\t\t\t// Many things do 0x48 call to see online drives\n\t\t\t// So: ignore those, just return BADCMD\n\t\t\thalt_printf(\"Just did smtport cmd:%02x rts_addr:%04x, \"\n\t\t\t\t\"cmdlst:%06x\\n\", cmd, rts_addr, cmd_list);\n\t\t}\n\t}\n\n\tengine.acc = (engine.acc & 0xff00) | (ret & 0xff);\n\tengine.psr &= ~1;\n\tif(ret) {\n\t\tengine.psr |= 1;\n\t\tprintf(\"Smtport cmd:%02x unit:%02x ctl_code:%02x ret:%02x\\n\",\n\t\t\tcmd, unit, ctl_code, ret);\n\t}\n\tengine.kpc = (rts_addr + 3 + ext) & 0xffff;\n\t// printf(\"   ret:%02x psr_c:%d\\n\", ret & 0xff, engine.psr & 1);\n}\n\n// $C70A is the ProDOS entry point, documented in ProDOS 8 Technical Ref\n//  Manual, section 6.3.\nvoid\ndo_c70a(word32 arg0)\n{\n\tdword64\tdsize;\n\tword32\tcmd, unit, buf_lo, buf_hi, blk_lo, blk_hi, blk, buf;\n\tword32\tprodos_unit;\n\tint\tret, slot;\n\n\tslot = (engine.kpc >> 8) & 7;\n\tset_memory_c(0x7f8, 0xc0 | slot, 1);\n\n\tcmd = get_memory_c((engine.direct + 0x42) & 0xffff);\n\tprodos_unit = get_memory_c((engine.direct + 0x43) & 0xffff);\n\tbuf_lo = get_memory_c((engine.direct + 0x44) & 0xffff);\n\tbuf_hi = get_memory_c((engine.direct + 0x45) & 0xffff);\n\tblk_lo = get_memory_c((engine.direct + 0x46) & 0xffff);\n\tblk_hi = get_memory_c((engine.direct + 0x47) & 0xffff);\n\n\tblk = (blk_hi << 8) + blk_lo;\n\tbuf = (buf_hi << 8) + buf_lo;\n\tdisk_printf(\"c70a %02x cmd:%02x, pro_unit:%02x, buf:%04x, blk:%04x\\n\",\n\t\targ0, cmd, prodos_unit, buf, blk);\n\n\tunit = 0 + (prodos_unit >> 7);\t\t// units 0,1\n\tif((prodos_unit & 0x7f) != (slot << 4)) {\n\t\tunit += 2;\t\t\t// units 2,3\n\t}\n\n\tsmartport_log(0xc70a, cmd, blk, buf);\n\tdbg_log_info(g_cur_dfcyc,\n\t\t(buf << 16) | ((unit & 0xff) << 8) | (cmd & 0xff), blk, 0xc70a);\n\n#if 0\n\tif(cmd != 0x1ff) {\n\t\tprintf(\"SMTPT: c70a %08x %08x\\n\",\n\t\t\t(buf << 16) | ((unit & 0xff) << 8) | (cmd & 0xff), blk);\n\t}\n#endif\n\tengine.psr &= ~1;\t/* clear carry */\n\n\tret = 0x27;\t/* I/O error */\n\tif(cmd == 0x00) {\n\t\tdsize = g_iwm.smartport[unit].dimage_size;\n\t\tdsize = (dsize + 511) / 512;\n\n\t\tsmartport_log(0, unit, (word32)dsize, 0);\n\t\tdbg_log_info(g_cur_dfcyc, ((unit & 0xff) << 8) | (cmd & 0xff),\n\t\t\t\t\t\t\t(word32)dsize, 0x1c700);\n\n\t\tret = 0;\n\t\tengine.xreg = dsize & 0xff;\n\t\tengine.yreg = (word32)(dsize >> 8);\n\t} else if(cmd == 0x01) {\n\t\tsmartport_log(0, unit, buf, blk);\n\t\tret = do_read_c7(unit, buf, blk);\n\t} else if(cmd == 0x02) {\n\t\tsmartport_log(0, unit, buf, blk);\n\t\tret = do_write_c7(unit, buf, blk);\n\t} else if(cmd == 0x03) {\t/* format */\n\t\tsmartport_log(0, unit, buf, blk);\n\t\tret = do_format_c7(unit);\n\t}\n\n\tengine.acc = (engine.acc & 0xff00) | (ret & 0xff);\n\tif(ret != 0) {\n\t\tengine.psr |= 1;\t\t\t// Set carry\n\t}\n\treturn;\n}\n\nint\ndo_read_c7(int unit_num, word32 buf, word32 blk)\n{\n\tbyte\tlocal_buf[0x200];\n\tDisk\t*dsk;\n\tbyte\t*bptr;\n\tdword64\tdimage_start, dimage_size, dret;\n\tword32\tval;\n\tint\tlen, fd;\n\tint\ti;\n\n\tdbg_log_info(g_cur_dfcyc, (buf << 8) | (unit_num & 0xff), blk, 0xc701);\n\tif((unit_num < 0) || (unit_num > MAX_C7_DISKS)) {\n\t\thalt_printf(\"do_read_c7: unit_num: %d\\n\", unit_num);\n\t\tsmartport_error();\n\t\treturn 0x28;\n\t}\n\n\tdsk = &(g_iwm.smartport[unit_num]);\n\tfd = dsk->fd;\n\tdimage_start = dsk->dimage_start;\n\tdimage_size = dsk->dimage_size;\n\tif(fd < 0) {\n\t\tprintf(\"c7_fd == %d!\\n\", fd);\n#if 0\n\t\tif(blk != 2 && blk != 0) {\n\t\t\t/* don't print error if only reading directory */\n\t\t\tsmartport_error();\n\t\t\thalt_printf(\"Read unit:%02x blk:%04x\\n\", unit_num, blk);\n\t\t}\n#endif\n\t\treturn 0x2f;\n\t}\n\tif(((blk + 1) * 0x200ULL) > (dimage_start + dimage_size)) {\n\t\thalt_printf(\"Tried to read past %08llx on disk (blk:%04x)\\n\",\n\t\t\tdimage_start + dimage_size, blk);\n\t\tsmartport_error();\n\t\treturn 0x27;\n\t}\n\n\tif(dsk->raw_data) {\n\t\t// image was compressed and is in dsk->raw_data\n\t\tbptr = dsk->raw_data + dimage_start + (blk*0x200ULL);\n\t\tfor(i = 0; i < 0x200; i++) {\n\t\t\tlocal_buf[i] = bptr[i];\n\t\t}\n\t} else {\n\t\tdret = kegs_lseek(fd, dimage_start + blk*0x200ULL, SEEK_SET);\n\t\tif(dret != (dimage_start + blk*0x200ULL)) {\n\t\t\thalt_printf(\"lseek ret %08llx, errno:%d\\n\", dret,\n\t\t\t\t\t\t\t\t\terrno);\n\t\t\tsmartport_error();\n\t\t\treturn 0x27;\n\t\t}\n\n\t\tlen = (int)read(fd, &local_buf[0], 0x200);\n\t\tif(len != 0x200) {\n\t\t\tprintf(\"read returned %08x, errno:%d, blk:%04x, unit:\"\n\t\t\t\t\"%02x\\n\", len, errno, blk, unit_num);\n\t\t\thalt_printf(\"name: %s\\n\", dsk->name_ptr);\n\t\t\tsmartport_error();\n\t\t\treturn 0x27;\n\t\t}\n\t}\n\n\tg_io_amt += 0x200;\n\n\tif(buf >= 0xfc0000) {\n\t\tdisk_printf(\"reading into ROM, just returning\\n\");\n\t\treturn 0;\n\t}\n\n\tfor(i = 0; i < 0x200; i += 2) {\n\t\tval = (local_buf[i+1] << 8) + local_buf[i];\n\t\tset_memory16_c(buf + i, val, 0);\n\t}\n\n\treturn 0;\n}\n\nint\ndo_write_c7(int unit_num, word32 buf, word32 blk)\n{\n\tbyte\tlocal_buf[0x200];\n\tDisk\t*dsk;\n\tdword64\tdret, dimage_start, dimage_size;\n\tint\tlen, fd, ret;\n\tint\ti;\n\n\tdbg_log_info(g_cur_dfcyc, (buf << 16) | (unit_num & 0xff), blk, 0xc702);\n\n\tif(unit_num < 0 || unit_num > MAX_C7_DISKS) {\n\t\thalt_printf(\"do_write_c7: unit_num: %d\\n\", unit_num);\n\t\tsmartport_error();\n\t\treturn 0x28;\n\t}\n\n\tdsk = &(g_iwm.smartport[unit_num]);\n\tfd = dsk->fd;\n\tdimage_start = dsk->dimage_start;\n\tdimage_size = dsk->dimage_size;\n\tif(fd < 0) {\n\t\thalt_printf(\"c7_fd == %d!\\n\", fd);\n\t\tsmartport_error();\n\t\treturn 0x28;\n\t}\n\n\tfor(i = 0; i < 0x200; i++) {\n\t\tlocal_buf[i] = get_memory_c(buf + i);\n\t}\n\n\tif(dsk->write_prot) {\n\t\tprintf(\"Write, but s7d%d %s is write protected!\\n\",\n\t\t\t\t\t\tunit_num + 1, dsk->name_ptr);\n\t\treturn 0x2b;\n\t}\n\n\tif(dsk->write_through_to_unix == 0) {\n\t\t//halt_printf(\"Write to %s, but not wr_thru!\\n\", dsk->name_ptr);\n\t\tif(dsk->raw_data) {\n\t\t\t// Update the memory copy\n\t\t\tret = smartport_memory_write(dsk, &local_buf[0],\n\t\t\t\t\t\tblk * 0x200ULL, 0x200);\n\t\t\tif(ret) {\n\t\t\t\treturn 0x27;\t\t// I/O Error\n\t\t\t}\n\t\t}\n\t\treturn 0x00;\n\t}\n\n\tif(dsk->dynapro_info_ptr) {\n\t\tdynapro_write(dsk, &local_buf[0], blk*0x200UL, 0x200);\n\t} else {\n\t\tdret = kegs_lseek(fd, dimage_start + blk*0x200ULL, SEEK_SET);\n\t\tif(dret != (dimage_start + blk*0x200ULL)) {\n\t\t\thalt_printf(\"lseek returned %08llx, errno: %d\\n\", dret,\n\t\t\t\t\t\t\t\terrno);\n\t\t\tsmartport_error();\n\t\t\treturn 0x27;\n\t\t}\n\n\t\tif(dret >= (dimage_start + dimage_size)) {\n\t\t\thalt_printf(\"Tried to write to %08llx\\n\", dret);\n\t\t\tsmartport_error();\n\t\t\treturn 0x27;\n\t\t}\n\n\t\tlen = (int)write(fd, &local_buf[0], 0x200);\n\t\tif(len != 0x200) {\n\t\t\thalt_printf(\"write ret %08x bytes, errno: %d\\n\", len,\n\t\t\t\t\t\t\t\t\terrno);\n\t\t\tsmartport_error();\n\t\t\tdsk->write_prot = 1;\n\t\t\treturn 0x2b;\t\t// Write protected\n\t\t}\n\t}\n\n\tg_io_amt += 0x200;\n\n\treturn 0;\n}\n\nint\nsmartport_memory_write(Disk *dsk, byte *bufptr, dword64 doffset, word32 size)\n{\n\tbyte\t*bptr;\n\tword32\tui;\n\n\tbptr = dsk->raw_data;\n\tif((bptr == 0) || ((doffset + size) > dsk->dimage_size)) {\n\t\tprintf(\"Write to %s failed, %08llx past end %08llx\\n\",\n\t\t\tdsk->name_ptr, doffset, dsk->dimage_size);\n\t\treturn -1;\n\t}\n\tfor(ui = 0; ui < size; ui++) {\n\t\tbptr[doffset + ui] = bufptr[ui];\n\t}\n\n\treturn 0;\n}\n\nint\ndo_format_c7(int unit_num)\n{\n\tbyte\tlocal_buf[0x1000];\n\tDisk\t*dsk;\n\tdword64\tdimage_start, dimage_size, dret, dtotal, dsum;\n\tint\tlen, max, fd, ret;\n\tint\ti;\n\n\tdbg_log_info(g_cur_dfcyc, (unit_num & 0xff), 0, 0xc703);\n\n\tif(unit_num < 0 || unit_num > MAX_C7_DISKS) {\n\t\thalt_printf(\"do_format_c7: unit_num: %d\\n\", unit_num);\n\t\tsmartport_error();\n\t\treturn 0x28;\n\t}\n\n\tdsk = &(g_iwm.smartport[unit_num]);\n\tfd = dsk->fd;\n\tdimage_start = dsk->dimage_start;\n\tdimage_size = dsk->dimage_size;\n\tif(fd < 0) {\n\t\thalt_printf(\"c7_fd == %d!\\n\", fd);\n\t\tsmartport_error();\n\t\treturn 0x28;\n\t}\n\n\tif(dsk->write_prot || (dsk->raw_data && !dsk->dynapro_info_ptr)) {\n\t\tprintf(\"Format, but %s is write protected!\\n\", dsk->name_ptr);\n\t\treturn 0x2b;\n\t}\n\n\tif(dsk->write_through_to_unix == 0) {\n\t\tif(!dsk->raw_data) {\n\t\t\tprintf(\"Format of %s ignored\\n\", dsk->name_ptr);\n\t\t\treturn 0x00;\n\t\t}\n\t}\n\n\tfor(i = 0; i < 0x1000; i++) {\n\t\tlocal_buf[i] = 0;\n\t}\n\n\tif(!dsk->dynapro_info_ptr) {\n\t\tdret = kegs_lseek(fd, dimage_start, SEEK_SET);\n\t\tif(dret != dimage_start) {\n\t\t\thalt_printf(\"lseek returned %08llx, errno: %d\\n\", dret,\n\t\t\t\t\t\t\t\t\terrno);\n\t\t\tsmartport_error();\n\t\t\treturn 0x27;\n\t\t}\n\t}\n\n\tdsum = 0;\n\tdtotal = dimage_size;\n\n\twhile(dsum < dtotal) {\n\t\tmax = (int)MY_MIN(0x1000, dtotal - dsum);\n\t\tlen = max;\n\t\tif(dsk->dynapro_info_ptr) {\n\t\t\tdynapro_write(dsk, &local_buf[0], dsum, max);\n\t\t} else if(dsk->raw_data) {\n\t\t\tret = smartport_memory_write(dsk, &local_buf[0],\n\t\t\t\t\t\t\t\tdsum, max);\n\t\t\tif(ret) {\n\t\t\t\treturn 0x27;\t\t// I/O Error\n\t\t\t}\n\t\t} else {\n\t\t\tlen = (int)write(fd, &local_buf[0], max);\n\t\t}\n\t\tif(len != max) {\n\t\t\thalt_printf(\"write ret %08x, errno:%d\\n\", len, errno);\n\t\t\tsmartport_error();\n\t\t\tdsk->write_prot = 1;\n\t\t\treturn 0x2b;\t\t// Write-protected\n\t\t}\n\t\tdsum += len;\n\t}\n\n\treturn 0;\n}\n\nvoid\ndo_c700(word32 ret)\n{\n\tint\tslot;\n\n\tdisk_printf(\"do_c700 called, ret: %08x\\n\", ret);\n\tdbg_log_info(g_cur_dfcyc, 0, 0, 0xc700);\n\n\tslot = (engine.kpc >> 8) & 7;\n\tret = do_read_c7(0, 0x800, 0);\t\t// Always read unit 0, block 0\n\n\tset_memory_c(0x7f8, slot, 1);\n\tset_memory16_c(0x42, (slot << 12) | 1, 1);\n\tset_memory16_c(0x44, 0x0800, 1);\n\tset_memory16_c(0x46, 0x0000, 1);\n\tengine.xreg = slot << 4;\t\t// 0x70 for slot 7\n\tengine.kpc = 0x801;\n\n\tif(ret != 0) {\n\t\tprintf(\"Failure reading boot disk in s7d1, trying slot 5!\\n\");\n\t\tengine.kpc = 0xc500;\t\t// Try to boot slot 5\n\t\tif((slot == 5) || (g_rom_version == 0)) {\n\t\t\tengine.kpc = 0xc600;\t// Try to boot slot 6\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/sound.c",
    "content": "const char rcsid_sound_c[] = \"@(#)$KmKId: sound.c,v 1.155 2023-08-19 17:45:33+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include \"defc.h\"\n\n#define INCLUDE_RCSID_C\n#include \"sound.h\"\n#undef INCLUDE_RCSID_C\n\n#define DOC_LOG(a,b,c,d)\n\nextern int Verbose;\nextern int g_use_shmem;\nextern word32 g_vbl_count;\nextern int g_preferred_rate;\n\nextern word32 g_c03ef_doc_ptr;\n\nextern int g_doc_vol;\n\nextern dword64 g_last_vbl_dfcyc;\n\nint\tg_queued_samps = 0;\nint\tg_queued_nonsamps = 0;\n\n#if defined(HPUX) || defined(__linux__) || defined(_WIN32) || defined(MAC)\nint\tg_audio_enable = -1;\n#else\nint\tg_audio_enable = 0;\t\t/* Not supported: default to off */\n#endif\nint\tg_sound_min_msecs = 32;\t\t\t// 32 msecs\nint\tg_sound_min_msecs_pulse = 150;\t\t// 150 msecs\nint\tg_sound_max_multiplier = 6;\t\t// 6*32 = ~200 msecs\nint\tg_sound_min_samples = 48000 * 32/1000;\t// 32 msecs\n\nMockingboard g_mockingboard;\n\n// The AY8913 chip has non-linear amplitudes (it has 16 levels) and the\n//  documentation does not match measured results.  But all the measurements\n//  should really be done at the final speaker/jack since all the stuff in\n//  the path affects it.  But: no one's done this for Mockingboard that I\n//  have found, so I'm taking measurements from the AY8913 chip itself.\n// AY8913 amplitudes from https://groups.google.com/forum/#!original/\n//\t\t\t\tcomp.sys.sinclair/-zCR2kxMryY/XgvaDICaldUJ\n// by Matthew Westcott on December 21, 2001.\ndouble g_ay8913_ampl_factor_westcott[16] = {\t\t// NOT USED\n\t0.000,\t// level[0]\n\t0.010,\t// level[1]\n\t0.015,\t// level[2]\n\t0.022,\t// level[3]\n\t0.031,\t// level[4]\n\t0.046,\t// level[5]\n\t0.064,\t// level[6]\n\t0.106,\t// level[7]\n\t0.132,\t// level[8]\n\t0.216,\t// level[9]\n\t0.297,\t// level[10]\n\t0.391,\t// level[11]\n\t0.513,\t// level[12]\n\t0.637,\t// level[13]\n\t0.819,\t// level[14]\n\t1.000,\t// level[15]\n};\n// https://sourceforge.net/p/fuse-emulator/mailman/message/34065660/\n//  refers to some Russian-language measurements at:\n//  http://forum.tslabs.info/viewtopic.php?f=6&t=539 (translate from\n//  Russian), they give:\n// 0000,028F,03B3,0564, 07DC,0BA9,1083,1B7C,\n// 2068,347A,4ACE,5F72, 7E16,A2A4,CE3A,FFFF\ndouble g_ay8913_ampl_factor[16] = {\n\t0.000,\t// level[0]\n\t0.010,\t// level[1]\n\t0.014,\t// level[2]\n\t0.021,\t// level[3]\n\t0.031,\t// level[4]\n\t0.046,\t// level[5]\n\t0.064,\t// level[6]\n\t0.107,\t// level[7]\n\t0.127,\t// level[8]\n\t0.205,\t// level[9]\n\t0.292,\t// level[10]\n\t0.373,\t// level[11]\n\t0.493,\t// level[12]\n\t0.635,\t// level[13]\n\t0.806,\t// level[14]\n\t1.000,\t// level[15]\n};\n// MAME also appears to try to figure out how the channels get \"summed\"\n//  together.  KEGS code adds them in a completely independent way, and due\n//  to the circuit used on the AY8913, this is certainly incorrect.\n#define MAX_MOCK_ENV_SAMPLES\t2000\nint\tg_mock_env_vol[MAX_MOCK_ENV_SAMPLES];\nbyte\tg_mock_noise_bytes[MAX_MOCK_ENV_SAMPLES];\nint\tg_mock_volume[16];\t\t// Sample for each of the 16 amplitudes\nword32\tg_last_mock_vbl_count = 0;\n\n#define VAL_MOCK_RANGE\t\t(39000)\n\nint\tg_audio_rate = 0;\ndouble\tg_daudio_rate = 0.0;\ndouble\tg_drecip_audio_rate = 0.0;\ndouble\tg_dsamps_per_dfcyc = 0.0;\ndouble\tg_fcyc_per_samp = 0.0;\n\ndouble g_last_sound_play_dsamp = 0.0;\n\n#define VAL_C030_POS_VAL\t(20400*16/15)\n\t\t// C030_POS_VAL is multiplied by g_doc_vol (0-15) and then\n\t\t//  divided by 16.  So scale this value up by 16/15 so that\n\t\t//  g_doc_vol==15 gives the intended value (+/-20400)\n\n#define MAX_C030_TIMES\t\t18000\nfloat\tg_c030_fsamps[MAX_C030_TIMES + 2];\nint\tg_num_c030_fsamps = 0;\nint\tg_c030_state = 0;\nint\tg_c030_val = (VAL_C030_POS_VAL / 2);\ndword64\tg_c030_dsamp_last_toggle = 0;\n\nword32\t*g_sound_shm_addr = 0;\nint\tg_sound_shm_pos = 0;\n\nextern dword64 g_cur_dfcyc;\n\n#define MAX_SND_BUF\t65536\n\nint\tg_samp_buf[2*MAX_SND_BUF];\nbyte\tg_zero_buf[4096];\n\ndouble g_doc_dsamps_extra = 0.0;\n\nint\tg_num_snd_plays = 0;\nint\tg_num_recalc_snd_parms = 0;\n\nchar\t*g_sound_file_str = 0;\nint\tg_sound_file_fd = -1;\nint\tg_sound_file_bytes = 0;\n\n// WAV file information:\n// From https://docs.fileformat.com/audio/wav/\n// left channel is first: https://web.archive.org/web/20080113195252/\n//\t\t\thttp://www.borg.com/~jglatt/tech/wave.htm\n\nbyte g_wav_hdr[44] = {\n\t'R', 'I', 'F', 'F', 0xff, 0xff, 0xff, 0xff,\t\t// 0x00-0x07\n\t'W', 'A', 'V', 'E', 'f', 'm', 't', ' ',\t\t\t// 0x08-0x0f\n\t16, 0, 0, 0, 1, 0, 2, 0,\t\t\t\t// 0x10-0x17\n\t\t// 16=length of 'fmt ' chunk, 1=PCM, 2=stereo.\n\t0xff, 0xff, 0xff, 0xff,\t0xff, 0xff, 0xff, 0xff,\t\t// 0x18-0x1f\n\t4, 0, 16, 0, 'd', 'a', 't', 'a',\t\t\t// 0x20-0x27\n\t0xff, 0xff, 0xff, 0xff\t\t\t\t\t// 0x28-0x2b\n};\n\t// Bytes 4-7 are the total file size-8, so [0x28:0x2b]+0x24\n\t// Bytes [0x18-0x1b]is the sample rate (so 44100 or so)\n\t// [0x1c-0x1f] is bytes-per-second: [0x18-0x1b]*[0x10]*[0x16]/8\n\n\nvoid\nsound_init()\n{\n\tdoc_init();\n\tsnddrv_init();\n}\n\nvoid\nsound_set_audio_rate(int rate)\n{\n\tg_audio_rate = rate;\n\tg_daudio_rate = (rate)*1.0;\n\tg_drecip_audio_rate = 1.0/(rate);\n\tg_dsamps_per_dfcyc = ((rate*1.0) / (DCYCS_1_MHZ * 65536.0));\n\tg_fcyc_per_samp = (DCYCS_1_MHZ * 65536.0 / (rate*1.0));\n\tg_sound_min_samples = rate * g_sound_min_msecs / 1000;\n\n\tprintf(\"Set g_audio_rate = %d in main KEGS process, min_samples:%d\\n\",\n\t\trate, g_sound_min_samples);\n}\n\nvoid\nsound_reset(dword64 dfcyc)\n{\n\tdoc_reset(dfcyc);\n\tmockingboard_reset(dfcyc);\n}\n\nvoid\nsound_shutdown()\n{\n\tsnddrv_shutdown();\n}\n\nvoid\nsound_update(dword64 dfcyc)\n{\n\t/* Called every VBL time to update sound status */\n\n\t/* \"play\" sounds for this vbl */\n\n\t//DOC_LOG(\"do_snd_pl\", -1, dsamps, 0);\n\tsound_play(dfcyc);\n}\n\nvoid\nsound_file_start(char *filename)\n{\n\tsound_file_close();\n\n\tg_sound_file_str = filename;\t// Can be NULL, if so, do not start\n\tif(filename) {\n\t\tprintf(\"Set audio save file to: %s\\n\", filename);\n\t}\n}\n\nvoid\nsound_file_open()\n{\n\tchar\t*filename;\n\tword32\texp_size;\n\tint\tfd;\n\n\tfilename = g_sound_file_str;\n\tif(!filename) {\n\t\treturn;\n\t}\n\n\tfd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\tprintf(\"open_sound_file open ret: %d, errno: %d\\n\", fd, errno);\n\t\tsound_file_close();\n\t\treturn;\n\t}\n\n\texp_size = 1024*1024;\t\t// Default to 1MB, changed at close\n\tdynapro_set_word32(&g_wav_hdr[4], exp_size + 44 - 8);\t// File size\n\tdynapro_set_word32(&g_wav_hdr[0x28], exp_size);\t\t// data size\n\tdynapro_set_word32(&g_wav_hdr[0x18], g_audio_rate);\t// Sample rate\n\tdynapro_set_word32(&g_wav_hdr[0x1c], g_audio_rate * 2 * 2);\n\t\t\t\t\t\t\t\t// bytes-per-sec\n\n\t(void)cfg_write_to_fd(fd, &g_wav_hdr[0], 0, 44);\n\tg_sound_file_fd = fd;\n\tg_sound_file_bytes = 0;\n\tprintf(\"Opened file %s for sound\\n\", filename);\n}\n\nvoid\nsound_file_close()\n{\n\tint\tfd;\n\n\tfd = g_sound_file_fd;\n\tif(fd >= 0) {\n\t\tdynapro_set_word32(&g_wav_hdr[0x28], g_sound_file_bytes);\n\t\tdynapro_set_word32(&g_wav_hdr[4], g_sound_file_bytes + 44 - 8);\n\t\tcfg_write_to_fd(fd, &g_wav_hdr[0], 0, 44);\n\t\t\t// Rewrite first 44 bytes with WAV header\n\t\tprintf(\"Close sound file %s, fd:%d\\n\", g_sound_file_str, fd);\n\t\tclose(fd);\n\t}\n\n\tfree(g_sound_file_str);\n\tg_sound_file_fd = -1;\n\tg_sound_file_str = 0;\n}\n\nvoid\nsend_sound_to_file(word32 *wptr, int shm_pos, int num_samps, int real_samps)\n{\n\tint\tsize, this_size;\n\n\tif(!real_samps && g_sound_file_bytes) {\n\t\t// Don't do anything\n\t\treturn;\n\t}\n\tif(g_sound_file_fd < 0) {\n\t\tsound_file_open();\n\t}\n\n\tif(!wptr) {\n\t\t// No real samps\n\t\tsize = real_samps * 4;\n\t\twhile(size) {\n\t\t\tthis_size = size;\n\t\t\tif(this_size > 4096) {\n\t\t\t\tthis_size = 4096;\n\t\t\t}\n\t\t\tmust_write(g_sound_file_fd, &g_zero_buf[0], this_size);\n\t\t\tsize -= this_size;\n\t\t}\n\t\treturn;\n\t}\n\n\tsize = 0;\n\tif((num_samps + shm_pos) > SOUND_SHM_SAMP_SIZE) {\n\t\tsize = SOUND_SHM_SAMP_SIZE - shm_pos;\n\t\tg_sound_file_bytes += (size * 4);\n\n\t\tmust_write(g_sound_file_fd, (byte *)&(wptr[shm_pos]), 4*size);\n\t\tshm_pos = 0;\n\t\tnum_samps -= size;\n\t}\n\n\tg_sound_file_bytes += (num_samps * 4);\n\n\tmust_write(g_sound_file_fd, (byte *)&(wptr[shm_pos]), 4*num_samps);\n}\n\nvoid\nshow_c030_state(dword64 dfcyc)\n{\n\tshow_c030_samps(dfcyc, &(g_samp_buf[0]), 100);\n}\n\nvoid\nshow_c030_samps(dword64 dfcyc, int *outptr, int num)\n{\n\tint\tlast;\n\tint\ti;\n\n\tif(!g_num_c030_fsamps) {\n\t\treturn;\n\n\t}\n\tprintf(\"c030_fsamps[]: %d, dfcyc:%015llx\\n\", g_num_c030_fsamps, dfcyc);\n\n\tfor(i = 0; i < g_num_c030_fsamps+2; i++) {\n\t\tprintf(\"%3d: %5.3f\\n\", i, g_c030_fsamps[i]);\n\t}\n\n\tprintf(\"Samples[] = %d\\n\", num);\n\n\tlast = 0x0dadbeef;\n\tfor(i = 0; i < num; i++) {\n\t\tif((last != outptr[0]) || (i == (num - 1))) {\n\t\t\tprintf(\"Samp[%4d]: %d\\n\", i, outptr[0]);\n\t\t\tlast = outptr[0];\n\t\t}\n\t\toutptr += 2;\n\t}\n}\n\nint\nsound_play_c030(dword64 dfcyc, dword64 dsamp, int *outptr_start, int num_samps)\n{\n\tint\t*outptr;\n\tdword64\tdsamp_min;\n\tfloat\tftmp, fsampnum, next_fsampnum, fpercent;\n\tint\tval, num, c030_state, c030_val, pos, sampnum, next_sampnum;\n\tint\tdoc_vol, min_i, mul;\n\tint\ti, j;\n\n\t// Handle $C030 speaker clicks.  Clicks for the past num_samps are\n\t//  in g_c030_fsamps[] giving the sample position when the click\n\t//  occurred.  Turn this into samples, tracking multiple clicks per\n\t//  sample into an intermediate value.  After 500ms of no clicks,\n\t//  transition the speakers from +/-20400 to 0, so it's idle.\n\t// The speaker is affected by the DOC volume in g_doc_vol, like a real\n\t//  IIgs (this is used during the system beep).  This code reacts\n\t//  to DOC volume changes when they happen by causing sound_play()\n\t//  to be called, so all samples with the old volume are played then\n\t//  new clicks are collected.\n\n\tnum = g_num_c030_fsamps;\t\t// Number of clicks\n\tif(!num) {\n\t\tif(g_c030_val == 0) {\n\t\t\treturn 0;\t\t// Speaker is at rest\n\t\t}\n\t}\n\n\tpos = 0;\n\toutptr = outptr_start;\n\tc030_state = g_c030_state;\n\tc030_val = g_c030_val;\n\t\t// c030_val may be less than max due decay after 500ms.\n\t\t// Always use it first, until speaker toggles, which should\n\t\t//  restore the full speaker range\n\tdoc_vol = g_doc_vol;\n\n\tif(!num) {\n\t\t// No clicks.  See if we should begin transitioning the\n\t\t//  speaker output to 0.  I tried multiplying by .9999 but\n\t\t//  that seemed to take too long at the end, so just use a\n\t\t//  linear ramp down.  Do this ramp based on the last click\n\t\t//  time, not VBL, since this is more consistent\n\n\t\tdsamp_min = g_c030_dsamp_last_toggle + (g_audio_rate >> 4);\n\t\tif(dsamp >= dsamp_min) {\n\t\t\tmin_i = 0;\n\t\t} else {\n\t\t\tmin_i = (int)(dsamp_min - dsamp);\n\t\t}\n\t\tmul = (2 * c030_state - 1) * doc_vol;\n\t\tval = (c030_val * mul) >> 4;\n\t\tfor(i = 0; i < num_samps; i++) {\n\t\t\tif(i >= min_i) {\n\t\t\t\tif(c030_val > 4) {\n\t\t\t\t\tc030_val -= 4;\n\t\t\t\t} else {\n\t\t\t\t\tc030_val = 0;\n\t\t\t\t}\n\t\t\t\tval = (c030_val * mul) >> 4;\n\t\t\t}\n\t\t\toutptr[0] = val;\n\t\t\toutptr[1] = val;\n\t\t\toutptr += 2;\n\t\t}\n#if 0\n\t\tprintf(\"at %015llx, num_samps:%d val at start:%d, at end:%d, \"\n\t\t\t\"min_i:%d\\n\", dfcyc, num_samps, g_c030_val, c030_val,\n\t\t\tmin_i);\n#endif\n\t\tg_c030_val = c030_val;\n\t\tif(c030_val == 0) {\n\t\t\t//printf(\"Speaker at rest\\n\");\n\t\t}\n\n\t\treturn 1;\n\t}\n\n\tg_c030_fsamps[num] = (float)(num_samps);\n\n\tnum++;\n\tfsampnum = g_c030_fsamps[0];\n\tsampnum = (int)fsampnum;\n\tfpercent = (float)0.0;\n\ti = 0;\n\n\twhile(i < num) {\n\t\tif(sampnum < 0 || sampnum > num_samps) {\n\t\t\thalt_printf(\"play c030: [%d]:%f is %d, > %d\\n\",\n\t\t\t\t\ti, fsampnum, sampnum, num_samps);\n\t\t\tbreak;\n\t\t}\n\n\t\t/* write in samples to all samps < me */\n\t\tval = ((2 * c030_state) - 1) * ((c030_val * doc_vol) >> 4);\n\t\tif(num <= 1) {\n\t\t\tprintf(\"num:%d i:%d pos:%d, sampnum:%d c030_state:%d \"\n\t\t\t\t\" at %015llx\\n\", num, i, pos, sampnum,\n\t\t\t\tc030_state, dfcyc);\n\t\t}\n\t\tfor(j = pos; j < sampnum; j++) {\n\t\t\toutptr[0] = val;\n\t\t\toutptr[1] = val;\n\t\t\toutptr += 2;\n\t\t\tpos++;\n\t\t}\n\n\t\tif((sampnum >= num_samps) || ((i + 1) >= num)) {\n\t\t\tbreak;\t\t// All done\n\t\t}\n\n\t\t/* now, calculate me */\n\t\tfpercent = (float)0.0;\n\t\tif(c030_state) {\n\t\t\tfpercent = (fsampnum - (float)sampnum);\n\t\t}\n\n\t\tc030_state = !c030_state;\n\t\tc030_val = VAL_C030_POS_VAL;\n\t\tg_c030_dsamp_last_toggle = dsamp + i;\n\n\t\tnext_fsampnum = g_c030_fsamps[i+1];\n\t\tnext_sampnum = (int)next_fsampnum;\n\t\t// Handle all the changes during this one sample\n\t\twhile(next_sampnum == sampnum) {\n\t\t\tif(c030_state) {\n\t\t\t\tfpercent += (next_fsampnum - fsampnum);\n\t\t\t}\n\t\t\ti++;\n\t\t\tfsampnum = next_fsampnum;\n\n\t\t\tif(i > num) {\n\t\t\t\tbreak;\t\t// This should not happen!\n\t\t\t}\n\t\t\tnext_fsampnum = g_c030_fsamps[i+1];\n\t\t\tnext_sampnum = (int)next_fsampnum;\n\t\t\tc030_state = !c030_state;\n\t\t}\n\n\t\tif(c030_state) {\n\t\t\t// add in time until next sample\n\t\t\tftmp = (float)(int)(fsampnum + (float)1.0);\n\t\t\tfpercent += (ftmp - fsampnum);\n\t\t}\n\n\t\tif((fpercent < (float)0.0) || (fpercent > (float)1.0)) {\n\t\t\thalt_printf(\"fpercent: %d = %f\\n\", i, fpercent);\n\t\t\tshow_c030_samps(dfcyc, outptr_start, num_samps);\n\t\t\tbreak;\n\t\t}\n\n\t\tval = (int)((2*fpercent - 1) * ((c030_val * doc_vol) >> 4));\n\t\toutptr[0] = val;\n\t\toutptr[1] = val;\n\t\toutptr += 2;\n\t\tpos++;\n\t\ti++;\n\n\t\tsampnum = next_sampnum;\n\t\tfsampnum = next_fsampnum;\n\t}\n\n\tg_c030_state = c030_state;\n\tg_c030_val = c030_val;\n\n#if 0\n\tif(g_sound_file_str) {\n\t\tshow_c030_samps(dfcyc, outptr_start, num_samps);\n\t}\n#endif\n\n\t// See if there are any entries >= fsampnum, copy them back down\n\t//  to the beginning of the array\n\tpos = 0;\n\tnum--;\n\tfsampnum = (float)num_samps;\n\twhile(i < num) {\n\t\tg_c030_fsamps[pos] = g_c030_fsamps[i] - fsampnum;\n#if 0\n\t\tprintf(\"Copied [%d] %f to [%d] as %f\\n\", i, g_c030_fsamps[i],\n\t\t\tpos, g_c030_fsamps[pos]);\n#endif\n\t\ti++;\n\t\tpos++;\n\t}\n\tg_num_c030_fsamps = pos;\n\n\treturn 1;\n}\n\n\nint g_sound_play_depth = 0;\n\n// sound_play(): forms the samples from the last sample time to the current\n//  time.  Can be called anytime from anywhere.  This is how KEGS handles\n//  dynamic sound changes (say, disabling an Ensoniq oscillator manually):\n//  when it's turned off, call sound_play() to play up to this moment, then\n//  the next time sound_play() is called, it will just know this osc is off\n// So, on any sound-related state change, call sound_play().\n\nvoid\nsound_play(dword64 dfcyc)\n{\n\tAy8913\t*ay8913ptr;\n\tint\t*outptr, *outptr_start;\n\tword32\t*sndptr;\n\tdouble\tlast_dsamp, dsamp_now, dvolume, dsamps;\n\tword32\tuval1, uval0;\n\tint\tval, val0, val1, pos, snd_buf_init, num_samps, num_pairs;\n\tint\tsound_mask, ivol;\n\tint\ti, j;\n\n\tg_num_snd_plays++;\n\tif(g_sound_play_depth) {\n\t\thalt_printf(\"Nested sound_play!\\n\");\n\t}\n\n\tg_sound_play_depth++;\n\n\t/* calc sample num */\n\n\tdsamps = dfcyc * g_dsamps_per_dfcyc;\n\tlast_dsamp = g_last_sound_play_dsamp;\n\tnum_samps = (int)(dsamps - g_last_sound_play_dsamp);\n\n\tdsamp_now = last_dsamp + (double)num_samps;\n\n\tif(num_samps < 1) {\n\t\t/* just say no */\n\t\tg_sound_play_depth--;\n\t\treturn;\n\t}\n\n\tdbg_log_info(dfcyc, (word32)(dword64)dsamp_now, num_samps, 0x200);\n\n\tif(num_samps > MAX_SND_BUF) {\n\t\tprintf(\"num_samps: %d, too big!\\n\", num_samps);\n\t\tg_sound_play_depth--;\n\t\treturn;\n\t}\n\n\toutptr_start = &(g_samp_buf[0]);\n\toutptr = outptr_start;\n\n\tsnd_buf_init = sound_play_c030(dfcyc, (dword64)dsamp_now, outptr_start,\n\t\t\t\t\t\t\t\tnum_samps);\n\n\tsnd_buf_init = doc_play(dfcyc, last_dsamp, dsamp_now, num_samps,\n\t\t\t\t\t\tsnd_buf_init, outptr_start);\n\n\tnum_pairs = 0;\n\t// Do Mockinboard channels\n\tfor(i = 0; i < 2; i++) {\t\t\t// Pair: 0 or 1\n\t\tay8913ptr = &(g_mockingboard.pair[i].ay8913);\n\t\tfor(j = 0; j < 3; j++) {\t\t// Channels: A, B, or C\n\t\t\tif((ay8913ptr->regs[8 + j] & 0x1f) == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tnum_pairs = 2;\n\t\t\tg_last_mock_vbl_count = g_vbl_count;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif((g_vbl_count - g_last_mock_vbl_count) < 120) {\n\t\t// Keep playing for 2 seconds, to avoid some static issues\n\t\tnum_pairs = 2;\n\t}\n\tif(num_pairs) {\n\t\tsound_mask = -1;\n\t\tif(snd_buf_init == 0) {\n\t\t\tsound_mask = 0;\n\t\t\tsnd_buf_init++;\n\t\t}\n\t\toutptr = outptr_start;\n\t\tivol = -((VAL_MOCK_RANGE * 3 / (8 * 15)) * g_doc_vol);\n\t\t\t// Do 3/8 of range below 0, leaving 5/8 above 0\n\t\tfor(i = 0; i < num_samps; i++) {\n\t\t\toutptr[0] = (outptr[0] & sound_mask) + ivol;\n\t\t\toutptr[1] = (outptr[1] & sound_mask) + ivol;\n\t\t\toutptr += 2;\n\t\t}\n\t\tfor(i = 0; i < 16; i++) {\n\t\t\tdvolume = (g_doc_vol * VAL_MOCK_RANGE) / (15.0 * 3.0);\n\t\t\tivol = (int)(g_ay8913_ampl_factor[i] * dvolume);\n\t\t\tg_mock_volume[i] = ivol;\n\t\t}\n\t}\n\tfor(i = 0; i < num_pairs; i++) {\n\t\tif(g_mockingboard.disable_mask) {\n\t\t\tprintf(\"dsamp:%lf\\n\", dsamps);\n\t\t}\n\n\t\tsound_mock_envelope(i, &(g_mock_env_vol[0]), num_samps,\n\t\t\t\t\t\t\t&(g_mock_volume[0]));\n\t\tsound_mock_noise(i, &(g_mock_noise_bytes[0]), num_samps);\n\t\tfor(j = 0; j < 3; j++) {\n\t\t\tsound_mock_play(i, j, outptr_start,\n\t\t\t\t&(g_mock_env_vol[0]), &(g_mock_noise_bytes[0]),\n\t\t\t\t&(g_mock_volume[0]), num_samps);\n\t\t}\n\t}\n\n\tg_last_sound_play_dsamp = dsamp_now;\n\n\toutptr = outptr_start;\n\tpos = g_sound_shm_pos;\n\tsndptr = g_sound_shm_addr;\n\n#if 0\n\tprintf(\"samps_left: %d, num_samps: %d\\n\", samps_left, num_samps);\n#endif\n\n\tif(g_audio_enable != 0) {\n\t\tif(snd_buf_init) {\n\t\t\t/* convert sound buf */\n\t\t\tfor(i = 0; i < num_samps; i++) {\n\t\t\t\tval0 = outptr[0];\n\t\t\t\tval1 = outptr[1];\n\t\t\t\tval = val0;\n\t\t\t\tif(val0 > 32767) {\n\t\t\t\t\tval = 32767;\n\t\t\t\t}\n\t\t\t\tif(val0 < -32768) {\n\t\t\t\t\tval = -32768;\n\t\t\t\t}\n\t\t\t\tuval0 = val & 0xffffU;\n\t\t\t\tval = val1;\n\t\t\t\tif(val1 > 32767) {\n\t\t\t\t\tval = 32767;\n\t\t\t\t}\n\t\t\t\tif(val1 < -32768) {\n\t\t\t\t\tval = -32768;\n\t\t\t\t}\n\t\t\t\tuval1 = val & 0xffffU;\n\t\t\t\toutptr += 2;\n\n#if defined(__linux__) || defined(OSS)\n\t\t\t\t/* Linux seems to expect little-endian */\n\t\t\t\t/*  samples always, even on PowerPC */\n# ifdef KEGS_BIG_ENDIAN\n\t\t\t\tsndptr[pos] = ((uval1 & 0xff) << 24) +\n\t\t\t\t\t\t((uval1 & 0xff00) << 8) +\n\t\t\t\t\t\t((uval0 & 0xff) << 8) +\n\t\t\t\t\t\t((uval0 >> 8) & 0xff);\n# else\n\t\t\t\tsndptr[pos] = (uval1 << 16) + (uval0 & 0xffff);\n# endif\n#else\n# ifdef KEGS_BIG_ENDIAN\n\t\t\t\tsndptr[pos] = (uval0 << 16) + uval1;\n# else\n\t\t\t\tsndptr[pos] = (uval1 << 16) + uval0;\n# endif\n#endif\n\t\t\t\tpos++;\n\t\t\t\tif(pos >= SOUND_SHM_SAMP_SIZE) {\n\t\t\t\t\tpos = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(g_queued_nonsamps) {\n\t\t\t\t/* force out old 0 samps */\n\t\t\t\tsnddrv_send_sound(0, g_queued_nonsamps);\n\t\t\t\tg_queued_nonsamps = 0;\n\t\t\t}\n\t\t\tif(g_sound_file_str) {\n\t\t\t\tsend_sound_to_file(g_sound_shm_addr,\n\t\t\t\t\t\tg_sound_shm_pos, num_samps, 1);\n\t\t\t}\n\t\t\tg_queued_samps += num_samps;\n\t\t} else {\n\t\t\t/* move pos */\n\t\t\tpos += num_samps;\n\t\t\twhile(pos >= SOUND_SHM_SAMP_SIZE) {\n\t\t\t\tpos -= SOUND_SHM_SAMP_SIZE;\n\t\t\t}\n\t\t\tif(g_sound_file_str) {\n\t\t\t\tsend_sound_to_file(0, g_sound_shm_pos,\n\t\t\t\t\t\t\t\tnum_samps, 0);\n\t\t\t}\n\t\t\tif(g_queued_samps) {\n\t\t\t\t/* force out old non-0 samps */\n\t\t\t\tsnddrv_send_sound(1, g_queued_samps);\n\t\t\t\tg_queued_samps = 0;\n\t\t\t}\n\t\t\tg_queued_nonsamps += num_samps;\n\t\t}\n\t}\n\n\tg_sound_shm_pos = pos;\n\n\tif(g_audio_enable != 0) {\n\t\tif(g_queued_samps >= (g_audio_rate/60)) {\n\t\t\tsnddrv_send_sound(1, g_queued_samps);\n\t\t\tg_queued_samps = 0;\n\t\t}\n\n\t\tif(g_queued_nonsamps >= (g_audio_rate/60)) {\n\t\t\tsnddrv_send_sound(0, g_queued_nonsamps);\n\t\t\tg_queued_nonsamps = 0;\n\t\t}\n\t}\n\n\tg_last_sound_play_dsamp = dsamp_now;\n\n\tg_sound_play_depth--;\n}\n\nvoid\nsound_mock_envelope(int pair, int *env_ptr, int num_samps, int *vol_ptr)\n{\n\tAy8913\t*ay8913ptr;\n\tdouble\tdmul, denv_period, dusecs_per_samp;\n\tdword64\tenv_dsamp, dsamp_inc;\n\tword32\tampl, eff_ampl, reg13, env_val, env_period;\n\tint\ti;\n\n\t// This routine calculates a fixed-point increment to apply\n\t//  to env_dsamp, where the envelope value is in bits 44:40 (bit\n\t//  44 is to track the alternating waveform, 43:40 is the env_ampl).\n\t// This algorithm does not properly handle dynamically changing the\n\t//  envelope period in the middle of a step.  In the AY8913, the\n\t//  part counts up to the env_period, and if the period is changed\n\t//  to a value smaller than the current count, it steps immediately\n\t//  to the next step.  This routine will wait for enough fraction\n\t//  to accumulate before stepping.  At most, this can delay the step\n\t//  by almost the new count time (if the new period is smaller), but\n\t//  no more.  I suspect this is not noticeable.\n\tif(num_samps > MAX_MOCK_ENV_SAMPLES) {\n\t\thalt_printf(\"envelope overflow!: %d\\n\", num_samps);\n\t\treturn;\n\t}\n\n\tay8913ptr = &(g_mockingboard.pair[pair].ay8913);\n\tampl = ay8913ptr->regs[8] | ay8913ptr->regs[9] | ay8913ptr->regs[10];\n\tif((ampl & 0x10) == 0) {\n\t\t// No one uses the envelope\n\t\treturn;\n\t}\n\n\tenv_dsamp = ay8913ptr->env_dsamp;\n\tenv_period = ay8913ptr->regs[11] + (256 * ay8913ptr->regs[12]);\n\tif(env_period == 0) {\n\t\tdenv_period = 0.5;\t\t// To match MAME\n\t} else {\n\t\tdenv_period = (double)env_period;\n\t}\n\tdmul = (1.0 / 16.0) * (1 << 20) * (1 << 20);\t// (1ULL << 40) / 16.0\n\t// Calculate amount counter will count in one sample.\n\t// inc_per_tick 62.5KHz tick: (1/env_period)\n\t// inc_per_dfcyc: (1/(16*env_period))\n\t// inc_per_samp = inc_per_dfcyc * g_fcyc_per_samp\n\tdusecs_per_samp = g_fcyc_per_samp / 65536.0;\n\tdsamp_inc = (dword64)((dmul * dusecs_per_samp / denv_period));\n\t\t\t// Amount to inc per sample, fixed point, 40 bit frac\n\n\treg13 = ay8913ptr->regs[13];\t\t\t// \"reg15\", env ctrl\n\teff_ampl = 0;\n\tfor(i = 0; i < num_samps; i++) {\n\t\tenv_dsamp = (env_dsamp + dsamp_inc) & 0x9fffffffffffULL;\n\t\tenv_val = (env_dsamp >> 40) & 0xff;\n\t\teff_ampl = env_val & 0xf;\n\t\tif((reg13 & 4) == 0) {\n\t\t\teff_ampl = 15 - eff_ampl;\t// not attack\n\t\t}\n\t\tif((reg13 & 8) && (reg13 & 2)) {\n\t\t\t// continue and alternate\n\t\t\tif(env_val & 0x10) {\n\t\t\t\teff_ampl = 15 - eff_ampl;\n\t\t\t}\n\t\t}\n\t\tif(((reg13 & 8) == 0) && (env_val >= 0x10)) {\n\t\t\teff_ampl = 0;\n\t\t\tampl = 0;\t\t// Turn off envelope\n\t\t\tenv_dsamp |= (0x80ULL << 40);\n\t\t} else if((reg13 & 1) && (env_val >= 0x10)) {\n\t\t\teff_ampl = ((reg13 >> 1) ^ (reg13 >> 2)) & 1;\n\t\t\teff_ampl = eff_ampl * 15;\n\t\t\tampl = eff_ampl;\t// Turn off envelope\n\t\t\tenv_dsamp |= (0x80ULL << 40);\n\t\t}\n\t\t*env_ptr++ = vol_ptr[eff_ampl & 0xf];\n\t}\n\tay8913ptr->env_dsamp = env_dsamp;\n}\n\nvoid\nsound_mock_noise(int pair, byte *noise_ptr, int num_samps)\n{\n\tAy8913\t*ay8913ptr;\n\tword32\tampl, mix, noise_val, noise_samp, noise_period, xor, samp_inc;\n\tint\tdoit;\n\tint\ti;\n\n\tif(num_samps > MAX_MOCK_ENV_SAMPLES) {\n\t\thalt_printf(\"noise overflow!: %d\\n\", num_samps);\n\t\treturn;\n\t}\n\n\tay8913ptr = &(g_mockingboard.pair[pair].ay8913);\n\tdoit = 0;\n\tfor(i = 0; i < 3; i++) {\n\t\tampl = ay8913ptr->regs[8 + i];\n\t\tmix = ay8913ptr->regs[7] >> i;\n\t\tif((ampl != 0) && ((mix & 8) == 0)) {\n\t\t\tdoit = 1;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif(!doit) {\n\t\t// No channel looks at noise, don't bother\n\t\treturn;\n\t}\n\n\tnoise_val = ay8913ptr->noise_val;\n\tnoise_samp = ay8913ptr->noise_samp;\n\tnoise_period = (ay8913ptr->regs[6] & 0x1f);\n\tnoise_period = noise_period << 16;\n\tsamp_inc = (word32)(g_fcyc_per_samp / 16.0);\n\t\t\t// Amount to inc per sample\n\tif(noise_samp >= noise_period) {\n\t\t// Period changed during sound, reset\n\t\tnoise_samp = noise_period;\n\t}\n\tfor(i = 0; i < num_samps; i++) {\n\t\tnoise_samp += samp_inc;\n\t\tif(noise_samp >= noise_period) {\n\t\t\t// HACK: handle fraction\n\t\t\t// 17-bit LFSR, algorithm from MAME:sound/ay8910.cpp\n\t\t\t// val = val ^ (((val & 1) ^ ((val >> 3) & 1)) << 17)\n\t\t\txor = 0;\n\t\t\txor = (noise_val ^ (noise_val >> 3)) & 1;\n\t\t\tnoise_val = (noise_val ^ (xor << 17)) >> 1;\n\t\t\tnoise_samp -= noise_period;\n\t\t}\n\t\tnoise_ptr[i] = noise_val & 1;\n\t}\n\tay8913ptr->noise_samp = noise_samp;\n\tay8913ptr->noise_val = noise_val;\n}\n\nint g_did_mock_print = 100;\n\nvoid\nsound_mock_play(int pair, int channel, int *outptr, int *env_ptr,\n\t\t\t\tbyte *noise_ptr, int *vol_ptr, int num_samps)\n{\n\tAy8913\t*ay8913ptr;\n\tword32\tampl, mix, tone_samp, tone_period, toggle_tone;\n\tword32\tsamp_inc, noise_val;\n\tint\tout, ival, do_print;\n\tint\ti;\n\n\tif((g_mockingboard.disable_mask >> ((pair * 3) + channel)) & 1) {\n\t\t// This channel is disabled\n\t\treturn;\n\t}\n\n\tay8913ptr = &(g_mockingboard.pair[pair].ay8913);\n\tampl = ay8913ptr->regs[8 + channel] & 0x1f;\n\tif(ampl == 0) {\n\t\treturn;\n\t}\n\ttoggle_tone = ay8913ptr->toggle_tone[channel];\t\t// 0 or 1\n\tmix = (ay8913ptr->regs[7] >> channel) & 9;\n\tif(mix == 9) {\n\t\t// constant tone: output will be ampl for this channel.\n\t\tif(ampl & 0x10) {\t\t// Envelope!\n\t\t\t// The envelope can make the tone, so must calculate it\n\t\t} else {\n\t\t\t// HACK: do nothing for now\n\t\t\treturn;\n\t\t}\n\t}\n\toutptr += pair;\t\t\t// pair[1] is right\n\ttone_samp = ay8913ptr->tone_samp[channel];\n\ttone_period = ay8913ptr->regs[2*channel] +\n\t\t\t\t\t(256 * ay8913ptr->regs[2*channel + 1]);\n\ttone_period = tone_period << 16;\n\tsamp_inc = (word32)(g_fcyc_per_samp / 8.0);\n\t\t\t// Amount to inc per sample\n\tdo_print = 0;\n\tif(g_mockingboard.disable_mask) {\n\t\tprintf(\"Doing %d samps, mix:%d, ampl:%02x\\n\", num_samps, mix,\n\t\t\t\t\t\t\t\t\tampl);\n\t\tdo_print = 1;\n\t\tg_did_mock_print = 0;\n\t}\n\tif((num_samps > 500) && (g_did_mock_print == 0)) {\n\t\tdo_print = 1;\n\t\tg_did_mock_print = 1;\n\t\tprintf(\"Start of %d sample, channel %d mix:%02x ampl:%02x \"\n\t\t\t\"toggle_tone:%02x\\n\", num_samps, channel, mix, ampl,\n\t\t\ttoggle_tone);\n\t\tprintf(\" tone_period:%08x, tone_samp:%08x, samp_inc:%08x\\n\",\n\t\t\ttone_period, tone_samp, samp_inc);\n\t}\n\tif(tone_samp >= tone_period) {\n\t\t// Period changed during sound, reset it\n\t\ttone_samp = tone_period;\n\t}\n\tfor(i = 0; i < num_samps; i++) {\n\t\ttone_samp += samp_inc;\n\t\tif(tone_samp >= tone_period) {\n\t\t\t// HACK: handle toggling mid-sample...\n\t\t\ttoggle_tone ^= 1;\n\t\t\tif(do_print) {\n\t\t\t\tprintf(\"i:%d tone_toggled to %d, tone_period:\"\n\t\t\t\t\t\"%04x, pre tone_samp:%08x\\n\", i,\n\t\t\t\t\ttoggle_tone, tone_period, tone_samp);\n\t\t\t}\n\t\t\ttone_samp -= tone_period;\n\t\t\tif(do_print) {\n\t\t\t\tprintf(\"post tone_samp:%08x\\n\", tone_samp);\n\t\t\t}\n\t\t}\n\t\tnoise_val = noise_ptr[i] & 1;\n\t\tout = (toggle_tone || (mix & 1)) &\n\t\t\t\t\t\t((noise_val & 1) || (mix & 8));\n\t\t\t// Careful mix of || and & above...\n\t\tival = vol_ptr[ampl & 0xf];\n\t\tif(ampl & 0x10) {\t\t\t// Envelope\n\t\t\tival = env_ptr[i];\n\t\t}\n\t\t*outptr += ival*out;\n\t\toutptr += 2;\n\t}\n\tay8913ptr->tone_samp[channel] = tone_samp;\n\tay8913ptr->toggle_tone[channel] = toggle_tone;\n}\n\nword32\nsound_read_c030(dword64 dfcyc)\n{\n\tsound_write_c030(dfcyc);\n\treturn float_bus(dfcyc);\n}\n\nvoid\nsound_write_c030(dword64 dfcyc)\n{\n\tint\tnum;\n\n\tnum = g_num_c030_fsamps;\n\tif(num >= MAX_C030_TIMES) {\n\t\thalt_printf(\"Too many clicks per vbl: %d\\n\", num);\n\t\treturn;\n\t}\n\n\tg_c030_fsamps[num] = (float)(dfcyc * g_dsamps_per_dfcyc -\n\t\t\t\t\t\tg_last_sound_play_dsamp);\n\tg_num_c030_fsamps = num + 1;\n\tdbg_log_info(dfcyc, num, 0, 0xc030);\n\n\tdoc_printf(\"touch c030, num this vbl: %04x\\n\", num);\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/sound.h",
    "content": "#ifdef INCLUDE_RCSID_C\nconst char rcsid_sound_h[] = \"@(#)$KmKId: sound.h,v 1.31 2023-05-04 19:35:29+00 kentd Exp $\";\n#endif\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#if !defined(_WIN32) && !defined(__CYGWIN__)\n# include <sys/ipc.h>\n# include <sys/shm.h>\n#endif\n\n#define SOUND_SHM_SAMP_SIZE\t\t(32*1024)\n\n#define SAMPLE_SIZE\t\t2\n#define NUM_CHANNELS\t\t2\n#define SAMPLE_CHAN_SIZE\t(SAMPLE_SIZE * NUM_CHANNELS)\n\nSTRUCT(Doc_reg) {\n\tdouble\tdsamp_ev;\n\tdouble\tdsamp_ev2;\n\tdouble\tcomplete_dsamp;\n\tint\tsamps_left;\n\tword32\tcur_acc;\n\tword32\tcur_inc;\n\tword32\tcur_start;\n\tword32\tcur_end;\n\tword32\tcur_mask;\n\tint\tsize_bytes;\n\tint\tevent;\n\tint\trunning;\n\tint\thas_irq_pending;\n\tword32\tfreq;\n\tword32\tvol;\n\tword32\twaveptr;\n\tword32\tctl;\n\tword32\twavesize;\n\tword32\tlast_samp_val;\n};\n\n// Mockingboard contains two pairs.  Each pair is a 6522 interfacing\n//  to an AY-8913 to generate sounds.  Eacho AY-8913 contains 3 channels of\n//  sound.  Model each pair separately.\n\nSTRUCT(Mos6522) {\n\tbyte\torb;\n\tbyte\tora;\n\tbyte\tddrb;\n\tbyte\tddra;\n\tword32\ttimer1_latch;\n\tword32\ttimer1_counter;\n\tword32\ttimer2_latch;\n\tword32\ttimer2_counter;\n\tbyte\tsr;\n\tbyte\tacr;\n\tbyte\tpcr;\n\tbyte\tifr;\n\tbyte\tier;\n};\n\nSTRUCT(Ay8913) {\n\tbyte\tregs[16];\n\tbyte\treg_addr_latch;\n\tbyte\ttoggle_tone[3];\t\t// Channel A,B,C: 0 = low, 1 = high\n\tword32\ttone_samp[3];\n\tword32\tnoise_val;\n\tword32\tnoise_samp;\n\tdword64\tenv_dsamp;\n};\n\nSTRUCT(Mock_pair) {\n\tMos6522\tmos6522;\n\tAy8913\tay8913;\n};\n\nSTRUCT(Mockingboard) {\n\tMock_pair pair[2];\n\tword32\tdisable_mask;\n};\n\n/* prototypes for win32snd_driver.c functions */\nvoid win32snd_init(word32 *);\nvoid win32snd_shutdown(void);\nvoid child_sound_init_win32(void);\nint win32_send_audio(byte *ptr, int size);\n\n/* Prototypes for macsnd_driver.c functions */\nint mac_send_audio(byte *ptr, int in_size);\nvoid macsnd_init();\n\n/* Prototypes for pulseaudio_driver.c functions */\nint pulse_audio_init();\nint pulse_audio_send_audio(byte *ptr, int in_size);\nvoid pulse_audio_shutdown(void);\n\n"
  },
  {
    "path": "upstream/kegs/src/sound_driver.c",
    "content": "const char rcsid_sound_driver_c[] = \"@(#)$KmKId: sound_driver.c,v 1.32 2023-06-05 18:41:09+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// Routines for managing sending sound samples to the hardware.  The\n//  primary routines are snddrv_init() for initializing the sound hardware,\n//  and snddrv_send_sound() which calls the driver for the sound hardware\n//  in use to play samples.\n// Linux forks a child process to manage /dev/dsp (so KEGS will not block)\n//  so the lowerlevel routines for all sound hardware start with child_().\n\n#include \"defc.h\"\n#include \"sound.h\"\n\n#if defined(__linux__) || defined(OSS)\n# include <sys/soundcard.h>\n#endif\n\n#ifndef _WIN32\n# include <sys/socket.h>\n# include <netinet/in.h>\n#endif\n#include <errno.h>\n\n#if defined(_WIN32) || defined(__CYGWIN__) || defined(MAC)\n# define KEGS_CAN_FORK\t0\n#else\n\t// Linux, or other Unix, we may fork and run sound in the child\n# define KEGS_CAN_FORK\t1\n#endif\n\nextern int Verbose;\n\nextern int g_audio_rate;\nextern int g_audio_enable;\nextern double g_last_sound_play_dsamp;\nextern word32 *g_sound_shm_addr;\n\nint\tg_preferred_rate = 48000;\nint\tg_audio_socket = -1;\nint\tg_bytes_written = 0;\nint\tg_pulse_audio = 0;\nint\tg_pipe_fd[2] = { -1, -1 };\nint\tg_pipe2_fd[2] = { -1, -1 };\n\n#define ZERO_BUF_SIZE\t\t2048\n\nword32 g_snd_zero_buf[ZERO_BUF_SIZE];\n\n#define ZERO_PAUSE_SAFETY_SAMPS\t\t(g_audio_rate >> 5)\n#define ZERO_PAUSE_NUM_SAMPS\t\t(4*g_audio_rate)\n\nint\tg_zeroes_buffered = 0;\nint\tg_zeroes_seen = 0;\nint\tg_sound_paused = 0;\nint\tg_childsnd_vbl = 0;\nint\tg_childsnd_pos = 0;\n\nint child_sound_init_linux(void);\n\nvoid\nsnddrv_init()\n{\n\tword32\t*shmaddr;\n\tint\tsize, ret, use_shm;\n\n\tret = 0;\n\tif(ret) {\t\t\t// Avoid unused var warning\n\t}\n\n\tg_zeroes_buffered = 0;\n\tg_zeroes_seen = 0;\n\tg_sound_paused = 0;\n\n\tg_childsnd_pos = 0;\n\tg_childsnd_vbl = 0;\n\n\tif(g_audio_enable == 0) {\n\t\tsound_set_audio_rate(g_preferred_rate);\n\t\treturn;\n\t}\n\tprintf(\"snddrv_init, g_audio_enable:%d\\n\", g_audio_enable);\n\n\tsize = SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE;\n\n\tuse_shm = KEGS_CAN_FORK;\n#ifdef PULSE_AUDIO\n\tuse_shm = 0;\n#endif\n\tif(!use_shm) {\n\t\t/* windows and mac, and Linux Pulse Audio */\n\t\tshmaddr = malloc(size);\n\t\tmemset(shmaddr, 0, size);\n\t\tg_sound_shm_addr = shmaddr;\n#ifdef MAC\n\t\tmacsnd_init();\n\t\treturn;\n#endif\n#ifdef _WIN32\n\t\twin32snd_init(shmaddr);\n\t\treturn;\n#endif\n#ifdef PULSE_AUDIO\n\t\tret = pulse_audio_init(shmaddr);\n\t\tif(ret == 0) {\n\t\t\tg_pulse_audio = 1;\n\t\t\treturn;\t\t// Success!\n\t\t}\n\t\tfree(shmaddr);\n\t\tg_sound_shm_addr = 0;\n\t\tuse_shm = 1;\n\t\t// Otherwise, fall back on /dev/dsp\n#endif\n\t}\n\n\tif(use_shm) {\n\t\tsound_child_fork(size);\n\t}\n}\n\nvoid\nsound_child_fork(int size)\n{\n#if KEGS_CAN_FORK\n\tword32\t*shmaddr;\n\tint\tshmid, pid, tmp, ret;\n\tint\ti;\n\n\tdoc_printf(\"In sound_child_fork, size:%d\\n\", size);\n\tshmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);\n\tif(shmid < 0) {\n\t\tprintf(\"sound_init: shmget ret: %d, errno: %d\\n\", shmid,\n\t\t\terrno);\n\t\texit(2);\n\t}\n\n\tshmaddr = shmat(shmid, 0, 0);\n\ttmp = (int)PTR2WORD(shmaddr);\n\tif(tmp == -1) {\n\t\tprintf(\"sound_init: shmat ret: %p, errno: %d\\n\", shmaddr,\n\t\t\terrno);\n\t\texit(3);\n\t}\n\n\tret = shmctl(shmid, IPC_RMID, 0);\n\tif(ret < 0) {\n\t\tprintf(\"sound_init: shmctl ret: %d, errno: %d\\n\", ret, errno);\n\t\texit(4);\n\t}\n\n\tg_sound_shm_addr = shmaddr;\n\tprintf(\"shmaddr: %p\\n\", shmaddr);\n\n\tfflush(stdout);\n\n\t/* prepare pipe so parent can signal child each other */\n\t/*  pipe[0] = read side, pipe[1] = write end */\n\tret = pipe(&g_pipe_fd[0]);\n\tif(ret < 0) {\n\t\tprintf(\"sound_init: pipe ret: %d, errno: %d\\n\", ret, errno);\n\t\texit(5);\n\t}\n\tret = pipe(&g_pipe2_fd[0]);\n\tif(ret < 0) {\n\t\tprintf(\"sound_init: pipe ret: %d, errno: %d\\n\", ret, errno);\n\t\texit(5);\n\t}\n\n\n\tdoc_printf(\"pipes: pipe_fd = %d, %d  pipe2_fd: %d,%d\\n\",\n\t\tg_pipe_fd[0], g_pipe_fd[1], g_pipe2_fd[0], g_pipe2_fd[1]);\n\tfflush(stdout);\n\n\tpid = fork();\n\tswitch(pid) {\n\tcase 0:\n\t\t/* child */\n\t\t/* close stdin and write-side of pipe */\n\t\tclose(0);\n\t\t/* Close other fds to make sure X window fd is closed */\n\t\tfor(i = 3; i < 100; i++) {\n\t\t\tif((i != g_pipe_fd[0]) && (i != g_pipe2_fd[1])) {\n\t\t\t\tclose(i);\n\t\t\t}\n\t\t}\n\t\tclose(g_pipe_fd[1]);\t\t/*make sure write pipe closed*/\n\t\tclose(g_pipe2_fd[0]);\t\t/*make sure read pipe closed*/\n\t\tchild_sound_loop(g_pipe_fd[0], g_pipe2_fd[1], g_sound_shm_addr);\n\t\tprintf(\"Child sound loop returned\\n\");\n\t\texit(0);\n\tcase -1:\n\t\t/* error */\n\t\tprintf(\"sound_init: fork ret: -1, errno: %d\\n\", errno);\n\t\texit(6);\n\tdefault:\n\t\t/* parent */\n\t\t/* close read-side of pipe1, and the write side of pipe2 */\n\t\tclose(g_pipe_fd[0]);\n\t\tclose(g_pipe2_fd[1]);\n\t\tdoc_printf(\"Child is pid: %d\\n\", pid);\n\t}\n\n\tparent_sound_get_sample_rate(g_pipe2_fd[0]);\n#endif\n\tif(size) {\n\t\t// Avoid unused param warning\n\t}\n}\n\nvoid\nparent_sound_get_sample_rate(int read_fd)\n{\n\tword32\taudio_rate, tmp;\n\tint\tret;\n\n\tret = (int)read(read_fd, &audio_rate, 4);\n\tif(ret != 4) {\n\t\tprintf(\"parent dying, could not get sample rate from child\\n\");\n\t\tprintf(\"ret: %d, fd: %d errno:%d\\n\", ret, read_fd, errno);\n\t\texit(1);\n\t}\n\tret = (int)read(read_fd, &tmp, 4);\n\tif(ret != 4) {\n\t\tprintf(\"parent dying, could not get audio status from child\\n\");\n\t\tprintf(\"ret: %d, fd: %d errno:%d\\n\", ret, read_fd, errno);\n\t\texit(1);\n\t}\n\tif(tmp == 0) {\n\t\tg_audio_enable = 0;\n\t\tprintf(\"Failed to init Sound, turning off audio\\n\");\n\t}\n\tclose(read_fd);\n\n\tsound_set_audio_rate(audio_rate);\n}\n\nvoid\nsnddrv_shutdown()\n{\n#ifdef _WIN32\n\twin32snd_shutdown();\n#else\n\tif((g_audio_enable != 0) && (g_pipe_fd[1] >= 0)) {\n\t\tclose(g_pipe_fd[1]);\n\t}\n#endif\n#ifdef PULSE_AUDIO\n\tif(g_pulse_audio) {\n\t\tpulse_audio_shutdown();\n\t}\n#endif\n}\n\nvoid\nsnddrv_send_sound(int real_samps, int size)\n{\n\tword32\ttmp;\n\tint\tret, call_playit;\n\n\tif(g_audio_enable == 0) {\n\t\tprintf(\"Entered send_sound but audio off!\\n\");\n\t\texit(2);\n\t}\n\n\tif(real_samps) {\n\t\ttmp = size + 0xa2000000;\n\t} else {\n\t\ttmp = size + 0xa1000000;\n\t}\n\t//doc_log_rout(\"send_sound\", -1, g_last_sound_play_dsamp,\n\t//\t\t\t\t\t(real_samps << 30) + size);\n\n\tcall_playit = 0;\n#if defined(MAC) || defined(_WIN32)\n\tcall_playit = 1;\t\t\t// Never fork child mac/windows\n#endif\n\tif(call_playit || g_pulse_audio) {\n\t\tchild_sound_playit(tmp);\n\t\treturn;\n\t}\n\n\t/* Although this looks like a big/little-endian issue, since the */\n\t/*  child is also reading an int, it just works with no byte swap */\n\tret = (int)write(g_pipe_fd[1], &tmp, 4);\n\tif(ret != 4) {\n\t\thalt_printf(\"send_sound, wr ret: %d, errno: %d\\n\", ret, errno);\n\t}\n}\n\nvoid\nchild_sound_playit(word32 tmp)\n{\n\tint\tsize;\n\n\tsize = tmp & 0xffffff;\n\n\t//printf(\"child_sound_playit: %08x\\n\", tmp);\n\n\tif((tmp >> 24) == 0xa2) {\t\t\t// play sound\n\t\tif(g_zeroes_buffered) {\n\t\t\treliable_zero_write(g_zeroes_buffered);\n\t\t}\n\n\t\tg_zeroes_buffered = 0;\n\t\tg_zeroes_seen = 0;\n\n\t\tif((size + g_childsnd_pos) > SOUND_SHM_SAMP_SIZE) {\n\t\t\treliable_buf_write(g_sound_shm_addr, g_childsnd_pos,\n\t\t\t\t\tSOUND_SHM_SAMP_SIZE - g_childsnd_pos);\n\t\t\tsize = (g_childsnd_pos + size) - SOUND_SHM_SAMP_SIZE;\n\t\t\tg_childsnd_pos = 0;\n\t\t}\n\n\t\treliable_buf_write(g_sound_shm_addr, g_childsnd_pos, size);\n\n\t\tif(g_sound_paused) {\n\t\t\tprintf(\"Unpausing sound, zb: %d\\n\", g_zeroes_buffered);\n\t\t\tg_sound_paused = 0;\n\t\t}\n\n\t} else if((tmp >> 24) == 0xa1) {\t\t// play zeroes\n\t\tif(g_sound_paused) {\n\t\t\tif(g_zeroes_buffered < ZERO_PAUSE_SAFETY_SAMPS) {\n\t\t\t\tg_zeroes_buffered += size;\n\t\t\t}\n\t\t} else {\n\t\t\t/* not paused, send it through */\n\t\t\tg_zeroes_seen += size;\n\n\t\t\treliable_zero_write(size);\n\n\t\t\tif(g_zeroes_seen >= ZERO_PAUSE_NUM_SAMPS) {\n\t\t\t\tprintf(\"Pausing sound\\n\");\n\t\t\t\tg_sound_paused = 1;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tprintf(\"tmp received bad: %08x\\n\", tmp);\n\t\texit(3);\n\t}\n\n\tg_childsnd_pos += size;\n\twhile(g_childsnd_pos >= SOUND_SHM_SAMP_SIZE) {\n\t\tg_childsnd_pos -= SOUND_SHM_SAMP_SIZE;\n\t}\n\n\tg_childsnd_vbl++;\n\tif(g_childsnd_vbl >= 60) {\n\t\tg_childsnd_vbl = 0;\n#if 0\n\t\tprintf(\"sound bytes written: %06x\\n\", g_bytes_written);\n#endif\n\t\tg_bytes_written = 0;\n\t}\n}\n\nvoid\nreliable_buf_write(word32 *shm_addr, int pos, int size)\n{\n\tbyte\t*ptr;\n\tint\tret;\n\n\tif(size < 1 || pos < 0 || pos > SOUND_SHM_SAMP_SIZE ||\n\t\t\t\tsize > SOUND_SHM_SAMP_SIZE ||\n\t\t\t\t(pos + size) > SOUND_SHM_SAMP_SIZE) {\n\t\tprintf(\"reliable_buf_write: pos: %04x, size: %04x\\n\",\n\t\t\tpos, size);\n\t\texit(1);\n\t}\n\n\tptr = (byte *)&(shm_addr[pos]);\n\tsize = size * 4;\n\n\twhile(size > 0) {\n\t\tret = child_send_samples(ptr, size);\n\n\t\tif(ret < 0) {\n\t\t\tprintf(\"audio write, errno: %d %s\\n\", errno,\n\t\t\t\t\t\t\tstrerror(errno));\n\t\t\texit(1);\n\t\t}\n\t\tsize = size - ret;\n\t\tptr += ret;\n\t\tg_bytes_written += ret;\n\t}\n\n}\n\nvoid\nreliable_zero_write(int amt)\n{\n\tint\tlen;\n\n\twhile(amt > 0) {\n\t\tlen = MY_MIN(amt, ZERO_BUF_SIZE);\n\t\treliable_buf_write(g_snd_zero_buf, 0, len);\n\t\tamt -= len;\n\t}\n}\n\n\nint\nchild_send_samples(byte *ptr, int size)\n{\n#ifdef _WIN32\n\treturn win32_send_audio(ptr, size);\n#else\n# ifdef MAC\n\treturn mac_send_audio(ptr, size);\n# else\n#  ifdef PULSE_AUDIO\n\tif(g_pulse_audio) {\n\t\treturn pulse_audio_send_audio(ptr, size);\n\t}\n#  endif\n\treturn (int)write(g_audio_socket, ptr, size);\n# endif\n#endif\n}\n\n// child_sound_loop(): used by Linux child process as the main loop, to read\n//  from pipe to get sample info every VBL, and use shm_addr to get samples\nvoid\nchild_sound_loop(int read_fd, int write_fd, word32 *shm_addr)\n{\n\tword32\ttmp, did_init;\n\tint\tret, ret1, ret2;\n\n\tdoc_printf(\"Child pipe fd: %d, shm_addr:%p\\n\", read_fd, shm_addr);\n\n\tg_audio_rate = g_preferred_rate;\n\n\tdid_init = 0;\n#if defined(__linux__) || defined(OSS)\n\tdid_init = child_sound_init_linux();\n#endif\n\n\ttmp = g_audio_rate;\n\tret1 = (int)write(write_fd, &tmp, 4);\n\ttmp = did_init;\n\tret2 = (int)write(write_fd, &tmp, 4);\n\tif((ret1) != 4 || (ret2 != 4)) {\n\t\tprintf(\"Unable to send back audio rate to parent\\n\");\n\t\tprintf(\"ret1: %d,%d fd: %d, errno:%d\\n\", ret1, ret2, write_fd,\n\t\t\t\t\t\t\t\terrno);\n\t\texit(1);\n\t}\n\tdoc_printf(\"Wrote to fd %d the audio rate\\n\", write_fd);\n\n\tclose(write_fd);\n\n\twhile(1) {\n\t\terrno = 0;\n\t\tret = (int)read(read_fd, &tmp, 4);\n\t\tif(ret <= 0) {\n\t\t\tprintf(\"child dying from ret: %d, errno: %d\\n\",\n\t\t\t\tret, errno);\n\t\t\tbreak;\n\t\t}\n\n\t\tchild_sound_playit(tmp);\n\t}\n\n\tclose(g_audio_socket);\n\n\texit(0);\n}\n\n\n#if defined(__linux__) || defined(OSS)\nint\nchild_sound_init_linux()\n{\n\tint\tstereo, sample_size, rate, fmt, ret;\n\n\tg_audio_socket = open(\"/dev/dsp\", O_WRONLY, 0);\n\tif(g_audio_socket < 0) {\n\t\tprintf(\"open /dev/dsp failed, ret: %d, errno:%d\\n\",\n\t\t\tg_audio_socket, errno);\n\t\treturn 0;\n\t}\n\n#if 0\n\tfragment = 0x00200009;\n\tret = ioctl(g_audio_socket, SNDCTL_DSP_SETFRAGMENT, &fragment);\n\tif(ret < 0) {\n\t\tprintf(\"ioctl SETFRAGEMNT failed, ret:%d, errno:%d\\n\",\n\t\t\tret, errno);\n\t\treturn 0;\n\t}\n#endif\n\n\tsample_size = 16;\n\tret = ioctl(g_audio_socket, SNDCTL_DSP_SAMPLESIZE, &sample_size);\n\tif(ret < 0) {\n\t\tprintf(\"ioctl SNDCTL_DSP_SAMPLESIZE failed, ret:%d, errno:%d\\n\",\n\t\t\tret, errno);\n\t\treturn 0;\n\t}\n\n#ifdef KEGS_BIG_ENDIAN\n\tfmt = AFMT_S16_BE;\n#else\n\tfmt = AFMT_S16_LE;\n#endif\n\tret = ioctl(g_audio_socket, SNDCTL_DSP_SETFMT, &fmt);\n\tif(ret < 0) {\n\t\tprintf(\"ioctl SNDCTL_DSP_SETFMT failed, ret:%d, errno:%d\\n\",\n\t\t\tret, errno);\n\t\treturn 0;\n\t}\n\n\tstereo = 1;\n\tret = ioctl(g_audio_socket, SNDCTL_DSP_STEREO, &stereo);\n\tif(ret < 0) {\n\t\tprintf(\"ioctl SNDCTL_DSP_STEREO failed, ret:%d, errno:%d\\n\",\n\t\t\tret, errno);\n\t\treturn 0;\n\t}\n\n\trate = g_audio_rate;\n\tret = ioctl(g_audio_socket, SNDCTL_DSP_SPEED, &rate);\n\tif(ret < 0) {\n\t\tprintf(\"ioctl SNDCTL_DSP_SPEED failed, ret:%d, errno:%d\\n\",\n\t\t\tret, errno);\n\t\treturn 0;\n\t}\n\tif(ret > 0) {\n\t\trate = ret;\t/* rate is returned value */\n\t}\n\tif(rate < 8000) {\n\t\tprintf(\"Audio rate of %d which is < 8000!\\n\", rate);\n\t\treturn 0;\n\t}\n\tg_audio_rate = rate;\n\n\tprintf(\"Sound initialized\\n\");\n\treturn 1;\n}\n#endif\n"
  },
  {
    "path": "upstream/kegs/src/style_check",
    "content": "#!/usr/bin/perl -w\n# $KmKId: style_check,v 1.1 2020-06-14 02:52:13+00 kentd Exp $\n\n# Perl script to check for coding conventions\nuse English;\n\n$some_bad = 0;\n\nwhile($#ARGV >= 0) {\n\t$file = shift;\n\t$check_spaces = 0;\n\tif($file =~ /\\.c$/) {\n\t\t$check_spaces = 1;\n\t} elsif($file =~ /\\.s$/) {\n\t\t$check_spaces = 1;\n\t} elsif($file =~ /\\.k$/) {\n\t\t$check_spaces = 1;\n\t} elsif($file =~ /\\.h$/) {\n\t\t$check_spaces = 1;\n\t\tif($file =~ /^protos.*.h$/) {\n\t\t\tnext;\t\t\t# skip global_names.h\n\t\t}\n\t\tif($file =~ /^global_names.h$/) {\n\t\t\tnext;\t\t\t# skip global_names.h\n\t\t}\n\t\tif($file =~ /^knobs.h$/) {\n\t\t\tnext;\t\t\t# skip global_names.h\n\t\t}\n\t} else {\n\t\tnext;\t\t# skip this file\n\t}\n\tprint \"Style check: $file\\n\";\n\tif(-x $file) {\n\t\tprint \"File mode is executable\\n\";\n\t\t$some_bad++;\n\t}\n\topen(FILE, \"<$file\") || die \"Open: $file: $1\\n\";\n\t$line_num = 1;\n\tforeach $line (<FILE>) {\n\t\tchomp($line);\n\t\t$ign_tab_space = 0;\n\t\tif($line =~ m:^[/\\t]+{.*}:) {\n\t\t\t$ign_tab_space = 1;\n\t\t}\n\t\t$len = 0;\n\t\t$prev = 0;\n\t\t$pprev = 0;\n\t\t$bad = 0;\n\t\t$last_tab = -10;\n\t\tif($check_spaces) {\n\t\t\tif($line =~ / $/ || $line =~ /\t$/) {\n\t\t\t\tprint \"Line ends in tab or space\\n\";\n\t\t\t\t$bad++;\n\t\t\t}\n\t\t}\n\t\tif($line =~ /\\r$/) {\t\t# Line ends with Ctrl-M\n\t\t\tprint \"Windows linebreak detected\\n\";\n\t\t\t$bad = 101;\n\t\t}\n\t\twhile($line =~ m/^([^\"]*)(\"[^\"]*\")(.*)$/) {\n\t\t\t# Convert text in strings to '-' to avoid space checks\n\t\t\t$prev = $1;\n\t\t\t$post = $3;\n\t\t\t$quot = &dotify($2);\n\t\t\t$line = $prev . $quot . $post;\n\t\t}\n\t\twhile($line =~ m:^(.*)//(.*)$:) {\n\t\t\t# Convert text in comments to '-' to avoid space checks\n\t\t\t$prev = $1;\n\t\t\t$quot = &dotify($2);\n\t\t\t$line = $prev . \"::\" . $quot;\n\t\t}\n\t\t@chars = split(//, $line);\n\t\tforeach $char (@chars) {\n\t\t\t$len++;\n\t\t\tif(!$check_spaces) {\n\t\t\t\t# do nothing\n\t\t\t} elsif($char eq \"\\t\") {\n\t\t\t\t$len = (($len + 7) >> 3) * 8;\n\t\t\t\tif($prev eq ' ') {\n\t\t\t\t\tprint \"Space followed by tab\\n\";\n\t\t\t\t\t$bad++;\n\t\t\t\t}\n\t\t\t\t$last_tab = $len;\n\t\t\t} elsif($char eq \" \") {\n\t\t\t\tif($prev eq \"\\t\" && !$ign_tab_space) {\n\t\t\t\t\tprint \"Tab followed by space\\n\";\n\t\t\t\t\t$bad++;\n\t\t\t\t}\n\t\t\t\tif($prev eq \" \" && $pprev eq \" \" &&\n\t\t\t\t\t\t(($len - $last_tab) > 4)) {\n\t\t\t\t\tprint \"Too many spaces\\n\";\n\t\t\t\t\t$bad++;\n\t\t\t\t\tlast;\n\t\t\t\t}\n\t\t\t}\n\t\t\t$pprev = $prev;\n\t\t\t$prev = $char\n\t\t}\n\t\tif($check_spaces) {\n\t\t\tif(($len > 80) && ($line_num > 2)) {\n\t\t\t\tprint \"Line more than 80 columns\\n\";\n\t\t\t\t$bad++;\n\t\t\t}\n\t\t\t#print \"line $line has len $len\\n\";\n\t\t}\n\t\tif($bad) {\n\t\t\t$some_bad++;\n\t\t\tprint \"...at line $line_num in file $file\\n\";\n\t\t\tif($some_bad > 20) {\n\t\t\t\tdie \"Too many style errors\\n\";\n\t\t\t}\n\t\t\tif($bad >= 100) {\n\t\t\t\tnext;\t\t# Skip to next file\n\t\t\t}\n\t\t}\n\t\t$line_num++;\n\t}\n}\n\nif($some_bad) {\n\tdie \"Style errors\\n\";\n}\n\nexit 0;\n\nsub\ndotify\n{\n\tmy ($str) = @_;\n\tmy @chars = ();\n\tmy @outchars = ();\n\tmy ($char, $result);\n\n\t@chars = split(//, $str);\n\t@outchars = ();\n\tforeach $char (@chars) {\n\t\tif($char ne \"\\t\") {\n\t\t\t$char = \"-\";\n\t\t}\n\t\tpush(@outchars, $char);\n\t}\n\t$result = join('', @outchars);\n\t#print \"Old quote :$str:\\n\";\n\t#print \"New quote :$result:\\n\";\n\treturn $result;\n}\n"
  },
  {
    "path": "upstream/kegs/src/undeflate.c",
    "content": "const char rcsid_undeflate_c[] = \"@(#)$KmKId: undeflate.c,v 1.20 2023-09-26 00:20:53+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// This file has routines for the undeflate uncompression algorithm for\n//  gzip/zip, and routines for reading .zip files.\n\n// Based on https://www.ietf.org/rfc/rfc1951.txt for Deflate algorithm,\n//  and https://www.ietf.org/rfc/rfc1952.txt for gzip file format.\n\n// .zip file format from:\n// https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.3.TXT\n\n#include \"defc.h\"\n\n// FILE *g_outf = 0;\n\n#define LENGTH_ENCODED\t0xffffff444449ULL\n\t// LENGTH_ENCODED encodes the first table of section 3.2.5 for\n\t//  fixed Huffman: 257-264 = 0 extra bits, length=3-10 (so 8 entries)\n\t//  then 265-268 = 1 extra bit, length=11... (so 4 entries), etc.\n\t//  which is encoded in each nibble of this word.  Code 285 (entry 29)\n\t//  has extra_bits=0, indicated by the encoded nibble being 0xf.\n#define DIST_ENCODED\t0xff22222222222224ULL\n\t// DIST_ENCODED encodes the second table of section 3.2.5 for the\n\t//  fixed Huffman table:  Codes 0-3 have 0 extra bits, dist=1,2,3,4\n\t//  (so 4 entries), codes 4,5 have 1 extra bit, dest=5... (so 2\n\t//  entries), etc.  0xf indicates an invalid entry\n\nword32\tg_undeflate_fixed_len_tab[512+1];\n\t\t\t// extrabits << 20 | bits << 16 | len/literal\nword32\tg_undeflate_fixed_dist_tab[32+1];\n\t\t\t// extrabits << 20 | bits << 16 | distance\nword32\tg_undeflate_length_tab[32+1];\t// extra_bits << 20 | len\nword32\tg_undeflate_dist_tab[32+1];\t// extra_bits << 20 | dist\nword32\tg_undeflate_bit_rev[512];\nword32 g_undeflate_lencode_positions[19] =\n{\t\t0x200310, 0x300311, 0x700b12,\n\t\t0x100, 0x108, 0x107, 0x109, 0x106, 0x10a, 0x105, 0x10b,\n\t\t0x104, 0x10c, 0x103, 0x10d, 0x102, 0x10e, 0x101, 0x10f };\nword32\tg_undeflate_lencode_tab[128 + 1];\nword32\t*g_undeflate_dynamic_tabptr = 0;\nword32\tg_undeflate_dynamic_bits = 0;\nword32\t*g_undeflate_dynamic_dist_tabptr = 0;\nword32\tg_undeflate_dynamic_dist_bits = 0;\n\nvoid *\nundeflate_realloc(void *ptr, dword64 dsize)\n{\n\tif((size_t)dsize != dsize) {\n\t\treturn 0;\n\t}\n\treturn realloc(ptr, (size_t)dsize);\n}\n\nvoid *\nundeflate_malloc(dword64 dsize)\n{\n\tif((size_t)dsize != dsize) {\n\t\treturn 0;\n\t}\n\treturn malloc((size_t)dsize);\n}\n\nvoid\nshow_bits(unsigned *llptr, int nl)\n{\n\tint\ti;\n\n\tfprintf(stdout, \"Showing %03x bits entries\\n\", nl);\n\tfor(i = 0; i < nl; i++) {\n\t\tfprintf(stdout, \"%03x: %03x\\n\", i, (llptr[i] >> 16) & 0xf);\n\t}\n}\n\nvoid\nshow_huftb(unsigned *tabptr, int bits)\n{\n\tunsigned char seen[512];\n\tword32\tentry, code, val;\n\tint\ti, j;\n\n\tprintf(\"Showing hufftab of %d bits\\n\", bits);\n\n\tfor(i = 0; i < 256+32; i++) {\n\t\tseen[i] = 0;\n\t}\n\tfor(i = 0; i < (1 << bits); i++) {\n\t\tentry = tabptr[i];\n\t\tcode = entry & 0xff;\n\t\tif((entry >> 24) & 1) {\n\t\t\tval = entry & 0xfff0ffffUL;\n\t\t\tfor(j = 0; j < 32; j++) {\n\t\t\t\tif(val == g_undeflate_length_tab[j]) {\n\t\t\t\t\tcode = 256 + j;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(code < 256) {\n\t\t\t\tprintf(\"entry %08x (%08x) not found, [0]=%08x \"\n\t\t\t\t\t\"[1]=%08x\\n\", entry, val,\n\t\t\t\t\tg_undeflate_length_tab[0],\n\t\t\t\t\tg_undeflate_length_tab[1]);\n\t\t\t\tcode = 256 + 31;\n\t\t\t}\n\t\t}\n\t\tif(!seen[code]) {\n\t\t\tprintf(\"code %03x has bits:%d huffcode:%04x\\n\", code,\n\t\t\t\t(entry >> 16) & 0xf, i);\n\t\t\tseen[code] = 1;\n\t\t}\n\t}\n}\n\nvoid\nundeflate_init_len_dist_tab(word32 *tabptr, dword64 drepeats, word32 start)\n{\n\tword32\tpos, repeats, extra_bits;\n\tint\ti;\n\n\t// Initializes g_undeflate_length_tab[] and g_underflate_dist_tab[]\n\t// printf(\"undeflate len_dist_tab repeats:%016llx\\n\", drepeats);\n\tpos = 0;\n\textra_bits = 0;\n\twhile(pos < 30) {\n\t\trepeats = drepeats & 0xf;\n\t\tdrepeats = drepeats >> 4;\n\t\tif(repeats == 0xf) {\n\t\t\t// Special handling for code=285 (pos=29)\n\t\t\textra_bits = 0;\n\t\t\tstart--;\t\t// 258, not 259\n\t\t\trepeats = 1;\n\t\t}\n\t\tfor(i = 0; i < (int)repeats; i++) {\n\t\t\ttabptr[pos] = start | (extra_bits << 20) | (1 << 24);\n\t\t\t//printf(\"Set table[%d]=%08x (%d) i:%d out of %d\\n\",\n\t\t\t//\tpos, tabptr[pos], start, i, repeats);\n\t\t\tpos++;\n\t\t\tstart += (1 << extra_bits);\n\t\t}\n\t\textra_bits++;\n\t}\n}\n\nvoid\nundeflate_init_bit_rev_tab(word32 *tabptr, int num)\n{\n\tword32\tpos, val;\n\tint\ti, j;\n\n\t// Initializes g_undeflate_bit_rev[]\n\tfor(i = 0; i < num; i++) {\n\t\tpos = i;\n\t\tval = 0;\n\t\tfor(j = 0; j < 9; j++) {\n\t\t\tval = (val << 1) | (pos & 1);\n\t\t\tpos = pos >> 1;\n\t\t}\n\t\ttabptr[i] = val;\n\t\t// printf(\"Bit reverse[%03x]=%03x\\n\", i, val);\n\t}\n}\n\nword32\nundeflate_bit_reverse(word32 val, word32 bits)\n{\n\tword32\tnew_val, val2, shift;\n\n\tnew_val = g_undeflate_bit_rev[val & 0x1ff];\t// at most 9 bits\n\tshift = 9 - bits;\n\tif(bits <= 9) {\n\t\treturn new_val >> shift;\n\t} else if(bits <= 18) {\n\t\tshift += 9;\t\t// bits:10->shift=8, bits:11->shift=7,..\n\t\tval2 = g_undeflate_bit_rev[(val >> 9) & 0x1ff] >> shift;\n\t\tshift = bits - 9;\n\t\treturn (new_val << shift) | val2;\n\t}\n\tprintf(\"Cannot reverse %08x bits:%d!\\n\", val, bits);\n\treturn 0;\n}\n\nword32\nundeflate_calc_crc32(byte *bptr, word32 len)\n{\n\tword32\tcrc, c, xor;\n\tint\ti;\n\n\t// Old version, don't use other than for testing purposes.\n\t//  Use woz_calc_crc32() instead.\n\n\t// Generate CCITT-32 CRC, with remainder initialized to -1 and return\n\t//  the complement of the CRC value\n\t// This is slow--but it doesn't matter for KEGS, where the images\n\t//  are generally only 800KB max.\n\tcrc = (word32)-1;\n\twhile(len != 0) {\n\t\tc = *bptr++;\n\t\tlen--;\n\t\tfor(i = 0; i < 8; i++) {\n\t\t\txor = 0;\n\t\t\tif((crc ^ c) & 1) {\n\t\t\t\txor = 0xedb88320UL;\n\t\t\t}\n\t\t\tcrc = (crc >> 1) ^ xor;\n\t\t\tc = c >> 1;\n\t\t}\n\t}\n\treturn (~crc);\n}\n\nbyte *\nundeflate_ensure_dest_len(Disk *dsk, byte *ucptr, word32 len)\n{\n\tbyte\t*raw_ptr;\n\tdword64\traw_dsize, dimage_size;\n\tword32\tnew_image_size;\n\n\traw_dsize = dsk->raw_dsize;\n\tdimage_size = dsk->dimage_size;\n\tif(ucptr) {\n\t\tnew_image_size = (word32)(ucptr - dsk->raw_data);\n\t\tif(new_image_size < dimage_size) {\n\t\t\tprintf(\"ucptr moved backwards!\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\tif((new_image_size >> 31) != 0) {\n\t\t\tprintf(\"Output file > 2GB, failing\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\tdimage_size = new_image_size;\n\t\tdsk->dimage_size = dimage_size;\n\t}\n\tif(dimage_size > raw_dsize) {\n\t\tprintf(\"dimage_size %08llx overflowed raw_dsize %08llx\\n\",\n\t\t\tdimage_size, raw_dsize);\n\t\treturn 0;\n\t}\n\tif((dimage_size + len) > raw_dsize) {\n\t\traw_dsize = ((dimage_size + len) * 3ULL) / 2;\n\t\traw_ptr = undeflate_realloc(dsk->raw_data, raw_dsize);\n\t\t//printf(\"Did realloc to %08x, new new_data:%p, was %p\\n\",\n\t\t//\t\t\traw_size, raw_ptr, dsk->raw_data);\n\t\tif(raw_ptr == 0) {\n\t\t\tprintf(\"undeflate realloc failed\\n\");\n\t\t\tfree(dsk->raw_data);\n\t\t\tdsk->raw_data = 0;\n\t\t\treturn 0;\n\t\t}\n\t\tdsk->raw_data = raw_ptr;\n\t\tdsk->raw_dsize = raw_dsize;\n\t}\n#if 0\n\tprintf(\"undeflate_ensure_dest_len will ret %p, dsk->raw_data:%p, \"\n\t\t\"image_size:%08llx, raw_dsize:%08llx\\n\",\n\t\tdsk->raw_data + dimage_size, dsk->raw_data, dimage_size,\n\t\traw_dsize);\n#endif\n\treturn dsk->raw_data + dimage_size;\n}\n\nvoid\nundeflate_add_tab_code(word32 *tabptr, word32 tabsz_lg2, word32 code,\n\t\t\t\t\t\t\tword32 entry)\n{\n\tword32\trev_code, bits, pos, tab_size;\n\tint\tnum;\n\tint\ti;\n\n\tif(tabsz_lg2 > 15) {\n\t\tprintf(\"tabsz_lg2: %04x is not supported\\n\", tabsz_lg2);\n\t\treturn;\n\t}\n\ttab_size = 1 << tabsz_lg2;\n\trev_code = 0;\n\tbits = (entry >> 16) & 0xf;\n\trev_code = undeflate_bit_reverse(code, bits);\n\tif(rev_code >= tab_size) {\n\t\tprintf(\"rev_code:%04x out of range for entry %08x\\n\", rev_code,\n\t\t\t\t\t\t\t\t\tentry);\n\t\ttabptr[tab_size] = 1;\n\t\treturn;\n\t}\n\tnum = 1 << (tabsz_lg2 - bits);\n\tif(num < 0) {\n\t\tprintf(\"num %d out of range for entry %08x\\n\", num, entry);\n\t\ttabptr[tab_size] = 1;\n\t\treturn;\n\t}\n\tfor(i = 0; i < num; i++) {\n\t\tpos = rev_code | (i << bits);\n\t\tif(tabptr[pos] != 0) {\n\t\t\tprintf(\"Overwriting old [%04x]=%08x with %08x\\n\", pos,\n\t\t\t\t\t\ttabptr[pos], entry);\n\t\t\ttabptr[tab_size] = 1;\n\t\t}\n#if 0\n\t\tif(i >= 0) {\n\t\t\tprintf(\"Set code tab[%04x]=%08x (code:%04x)\\n\", pos,\n\t\t\t\t\t\t\tentry, save_code);\n\t\t}\n#endif\n\t\ttabptr[pos] = entry;\n\t}\n}\n\nword32 *\nundeflate_init_fixed_tabs()\n{\n\tword32\t*tabptr;\n\tint\ti;\n\n\ttabptr = &(g_undeflate_fixed_len_tab[0]);\n\t// Init g_undeflate_fixed_len_tab[] for the fixed Huffman code\n\tfor(i = 0; i < 513; i++) {\n\t\ttabptr[i] = 0;\n\t}\n\t// printf(\"Add fixed_len_tab for literals 0 - 143\\n\");\n\tfor(i = 0; i < 144; i++) {\n\t\tundeflate_add_tab_code(tabptr, 9, 0x30 + i, (8 << 16) | i);\n\t}\n\t// printf(\"Add fixed_len_tab for literals 144 - 255\\n\");\n\tfor(i = 144; i < 256; i++) {\n\t\tundeflate_add_tab_code(tabptr, 9, 0x190 + i - 144,\n\t\t\t\t\t\t\t\t(9 << 16) | i);\n\t}\n\t// printf(\"Add fixed_len_tab for length codes 256 - 279\\n\");\n\tfor(i = 256; i < 280; i++) {\n\t\t// printf(\"code: %03x fixed_len_tab[%03x]=%08x\\n\", i, i - 256,\n\t\t//\t\t\tg_undeflate_length_tab[i - 256]);\n\t\tundeflate_add_tab_code(tabptr, 9, 0 + i - 256,\n\t\t\t\t(7 << 16) | g_undeflate_length_tab[i - 256]);\n\t}\n\t// printf(\"Add fixed_len_tab for length codes 280 - 287\\n\");\n\tfor(i = 280; i < 288; i++) {\n\t\tundeflate_add_tab_code(tabptr, 9, 0xc0 + i - 280,\n\t\t\t\t(8 << 16) | g_undeflate_length_tab[i - 256]);\n\t}\n\tif(tabptr[512]) {\n\t\treturn 0;\n\t}\n\n\t// And init g_undeflate_fixed_dist_tab[]\n\ttabptr = &(g_undeflate_fixed_dist_tab[0]);\n\tfor(i = 0; i < 33; i++) {\n\t\ttabptr[i] = 0;\n\t}\n\t// printf(\"Add fixed_dist_tab for codes 0 - 29\\n\");\n\tfor(i = 0; i < 30; i++) {\n\t\tundeflate_add_tab_code(tabptr, 5, i,\n\t\t\t\t\t(5 << 16) | g_undeflate_dist_tab[i]);\n\t}\n\n\tif(tabptr[32]) {\n\t\treturn 0;\n\t}\n\treturn tabptr;\n}\n\nword32 *\nundeflate_init_tables()\n{\n\tundeflate_init_len_dist_tab(&(g_undeflate_length_tab[0]),\n\t\t\t\t\t\t\tLENGTH_ENCODED, 2);\n\t\t// code=257 has length 3, but the first entry is really code=256\n\t\t//  so set 256 to length=2\n\tundeflate_init_len_dist_tab(&(g_undeflate_dist_tab[0]), DIST_ENCODED,\n\t\t\t\t\t\t\t\t\t1);\n\tundeflate_init_bit_rev_tab(&(g_undeflate_bit_rev[0]), 512);\n\t// undeflate_check_bit_reverse();\n\treturn undeflate_init_fixed_tabs();\n}\n\nvoid\nundeflate_free_tables()\n{\n\tfree(g_undeflate_dynamic_tabptr);\n\tg_undeflate_dynamic_tabptr = 0;\n\tfree(g_undeflate_dynamic_dist_tabptr);\n\tg_undeflate_dynamic_dist_tabptr = 0;\n}\n\nvoid\nundeflate_check_bit_reverse()\n{\n\tword32\trev, tmp, checked;\n\tint\ti, j, bits;\n\n\t// Check bit-reverse function.  Reverse all values from 0-32767\n\tchecked = 0;\n\tfor(bits = 1; bits <= 16; bits++) {\n\t\t// printf(\"Checking bit reverse bits=%d\\n\", bits);\n\t\tfor(i = 0; i < 65536; i++) {\n\t\t\tif(i >= (1 << bits)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\ttmp = i;\n\t\t\trev = 0;\n\t\t\tfor(j = 0; j < bits; j++) {\n\t\t\t\trev = (rev << 1) | (tmp & 1);\n\t\t\t\ttmp = tmp >> 1;\n\t\t\t}\n\t\t\ttmp = undeflate_bit_reverse(i, bits);\n\t\t\tif(tmp != rev) {\n\t\t\t\tprintf(\"Reverse %04x bits:%d ret:%04x, \"\n\t\t\t\t\t\"exp:%04x\\n\", i, bits, tmp, rev);\n\t\t\t\texit(2);\n\t\t\t}\n\t\t\tchecked++;\n\t\t}\n\t}\n\tprintf(\"Checked %08x values\\n\", checked);\n}\n\nword32 *\nundeflate_build_huff_tab(word32 *tabptr, word32 *entry_ptr, word32 len_size,\n\t\t\t\t\tword32 *bl_count_ptr, int max_bits)\n{\n\tword32\tnext_code[16];\n\tword32\tcode, tab_size, bits, entry;\n\tint\ti;\n\n\ttab_size = (1 << max_bits);\n\tif(max_bits > 15) {\n\t\tprintf(\"max_bits: %d out of range\\n\", max_bits);\n\t\treturn 0;\n\t}\n\tnext_code[0] = 0;\n\tbl_count_ptr[0] = 0;\t\t// Force number of 0-bit lengths to 0\n\tcode = 0;\n\t// printf(\"build_huff_tab, max_bits:%d, tab_size:%08x\\n\", max_bits,\n\t//\t\t\t\t\t\t\ttab_size);\n\tfor(i = 1; i <= max_bits; i++) {\n\t\t// printf(\"bl_count[%d] = %03x\\n\", i - 1, bl_count_ptr[i-1]);\n\t\tcode = (code + bl_count_ptr[i - 1]) << 1;\n\t\tnext_code[i] = code;\n\t\t// printf(\"Set next_code[%d] = %03x\\n\", i, code);\n\t}\n\tfor(i = 0; i < (int)tab_size; i++) {\n\t\ttabptr[i] = 0;\n\t}\n\ttabptr[tab_size] = 0;\n\n\tfor(i = 0; i < (int)len_size; i++) {\n\t\tentry = entry_ptr[i];\n\t\tbits = (entry >> 16) & 0xf;\n\t\t//printf(\"i:%03x, bits:%d, entry:%08x\\n\", i, bits, entry);\n\t\tif(!bits) {\n\t\t\tcontinue;\n\t\t}\n\t\tcode = next_code[bits]++;\n\t\t//printf(\"Set tab code:%03x = %08x\\n\", code, entry);\n\t\tundeflate_add_tab_code(tabptr, max_bits, code, entry);\n\t}\n\n\t// printf(\"All done, returning tabptr\\n\");\n\treturn tabptr;\n}\n\nword32 *\nundeflate_dynamic_table(byte *cptr, word32 *bit_pos_ptr, byte *cptr_base)\n{\n\tword32\tcode_list[256+32+32+1];\n\tword32\tlen_codes[19];\n\tword32\tbl_count[19], bl_count_dist[16];\n\tword32\t*tabptr, *tabptr_dist;\n\tbyte\t*cptr_start;\n\tword32\tbit_pos, val, hlit, hdist, hclen, pos, max_bits, code_pos;\n\tword32\ttotal_codes_needed, repeat, mask, entry, bits;\n\tword32\tmax_length_bits, max_distance_bits, extra_bits;\n\tint\ti;\n\n\t// This is compressed compressed huffman lengths.  First\n\t//  get the length codes, then get the actual lengths\n\t// Get 14 bits, which always fits in 3 bytes\n\tbit_pos = *bit_pos_ptr;\n\tcptr_start = cptr;\n\tval = (cptr[0] + (cptr[1] << 8) + (cptr[2] << 16)) >> bit_pos;\n\thlit = (val & 0x1f) + 257;\t\t// 257 - 288\n\thdist = ((val >> 5) & 0x1f) + 1;\n\thclen = (val >> 10) & 0xf;\n#if 0\n\tprintf(\"At +%06x, bit:%d, hlit:%02x hdist:%02x, hclen:%02x\\n\",\n\t\t(word32)(cptr - cptr_base), bit_pos, hlit, hdist, hclen);\n#endif\n\tif(cptr_base) {\n\t\t// Avoid unused parameter warning\n\t}\n\tbit_pos += 14;\n\tcptr += (bit_pos >> 3);\n\tbit_pos = bit_pos & 7;\n\tfor(i = 0; i < 19; i++) {\n\t\tlen_codes[i] = 0;\n\t\tbl_count[i] = 0;\n\t}\n\thclen += 4;\t\t\t// 19*3 = 57 bits, at most\n\tmax_bits = 0;\n\tfor(i = 0; i < (int)hclen; i++) {\n\t\tval = ((cptr[0] + (cptr[1] << 8)) >> bit_pos) & 7;\n\t\tentry = g_undeflate_lencode_positions[i];\n\t\tentry = entry & (~0xf0000);\t\t// clear bits from entry\n\t\tpos = entry & 0x1f;\n\t\tlen_codes[pos] = entry | (val << 16);\n\t\t// printf(\"len_codes[%d]=%08x\\n\", pos, len_codes[pos]);\n\t\tbl_count[val]++;\n\t\tif(val > max_bits) {\n\t\t\tmax_bits = val;\n\t\t}\n\t\t// printf(\"Num bits for len code %02x = %d\\n\", pos, val);\n\t\tbit_pos += 3;\n\t\tcptr += (bit_pos >> 3);\n\t\tbit_pos = bit_pos & 7;\n\t}\n\t// Build huffman table\n\ttabptr = undeflate_build_huff_tab(&(g_undeflate_lencode_tab[0]),\n\t\t\t&(len_codes[0]), 19, &(bl_count[0]), max_bits);\n\tif(tabptr == 0) {\n\t\tprintf(\"Bad table\\n\");\n\t\treturn 0;\n\t}\n\n\t// Now we've made the table in tabptr.  Read the length codes now\n\ttotal_codes_needed = hlit + hdist;\n\t// printf(\"Getting %04x total codes\\n\", total_codes_needed);\n\tcode_pos = 0;\n\tmask = (1 << max_bits) - 1;\n\tif(total_codes_needed > (256+32+32)) {\n\t\tprintf(\"total_codes_needed high: %04x\\n\", total_codes_needed);\n\t\treturn 0;\n\t}\n\tfor(i = 0; i < 16; i++) {\n\t\tbl_count[i] = 0;\n\t\tbl_count_dist[i] = 0;\n\t}\n\twhile(code_pos < total_codes_needed) {\n\t\tpos = (cptr[0] | (cptr[1] << 8)) >> bit_pos;\n\t\tpos = pos & mask;\n\t\tentry = tabptr[pos & mask];\n#if 0\n\t\tprintf(\"At +%06x, bit:%d: Raw code: %02x, entry:%08x\\n\",\n\t\t\t(word32)(cptr - cptr_base), bit_pos, pos, entry);\n#endif\n\t\tval = entry & 0x1f;\n\n\t\tbits = (entry >> 16) & 7;\n\t\textra_bits = (entry >> 20) & 7;\n\t\trepeat = (entry >> 8) & 0xf;\n\t\tentry = (val << 16);\t\t\t// Set bits\n\t\tbit_pos += bits;\n\t\tcptr += (bit_pos >> 3);\n\t\tbit_pos = bit_pos & 7;\n\t\tpos = (cptr[0] | (cptr[1] << 8)) >> bit_pos;\n#if 0\n\t\tprintf(\"At +%06x, bit:%d: Raw pos:%04x\\n\",\n\t\t\t(word32)(cptr - cptr_base), bit_pos, pos);\n#endif\n\t\tpos = pos & ((1 << extra_bits) - 1);\n\t\trepeat = repeat + pos;\n\t\tbit_pos += extra_bits;\n\t\tcptr += (bit_pos >> 3);\n\t\tbit_pos = bit_pos & 7;\n\t\tif(!repeat) {\n\t\t\tprintf(\"Bad repeat value\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\tif(val >= 0x10) {\n\t\t\tentry = 0;\n\t\t\tif(val == 0x10) {\t\t// Repeat prev entry\n\t\t\t\tentry = code_list[code_pos - 1];\n\t\t\t\tif(!code_pos) {\n\t\t\t\t\tprintf(\"Got repeat code 0x10 at 0!\\n\");\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor(i = 0; i < (int)repeat; i++) {\n\t\t\tcode_list[code_pos] = entry;\n\t\t\t// printf(\"Added code_list[%03x] = %08x\\n\", code_pos,\n\t\t\t//\t\t\t\t\t\tentry);\n\t\t\tcode_pos++;\n\t\t}\n\t}\n\n\t// Fix lengths and literals\n\tmax_length_bits = 0;\n\tfor(i = 0; i < (int)hlit; i++) {\n\t\tentry = code_list[i];\n\t\tbits = (entry >> 16) & 0xf;\n\t\tbl_count[bits]++;\n\t\tif(i >= 256) {\n\t\t\tentry |= g_undeflate_length_tab[i - 256];\n\t\t} else {\n\t\t\tentry |= i;\n\t\t}\n\t\tcode_list[i] = entry;\n\t\tif(bits > max_length_bits) {\n\t\t\tmax_length_bits = bits;\n\t\t}\n\t}\n\n\t// Fix distances\n\tmax_distance_bits = 0;\n\tfor(i = 0; i < (int)hdist; i++) {\n\t\tentry = code_list[i + hlit];\n\t\tbits = (entry >> 16) & 0xf;\n\t\tbl_count_dist[bits]++;\n\t\tentry |= g_undeflate_dist_tab[i];\n\t\tcode_list[i + hlit] = entry;\n\t\tif(bits > max_distance_bits) {\n\t\t\tmax_distance_bits = bits;\n\t\t}\n\t}\n\tif(code_pos != total_codes_needed) {\n\t\tprintf(\"Got %03x codes, needed %03x codes\\n\", code_pos,\n\t\t\t\t\t\t\ttotal_codes_needed);\n\t\treturn 0;\n\t}\n\t// printf(\"max_length_bits: %d, max_distance_bits: %d\\n\",\n\t//\t\t\t\tmax_length_bits, max_distance_bits);\n\ttabptr = g_undeflate_dynamic_tabptr;\n\tif(!tabptr) {\n\t\ttabptr = malloc(sizeof(word32)*((1 << 15) + 1));\n\t\tg_undeflate_dynamic_tabptr = tabptr;\n\t\t// printf(\"malloc literal table\\n\");\n\t}\n\tg_undeflate_dynamic_bits = max_length_bits;\n\t//printf(\"Building literal/length table, %d entries, %d bits\\n\", hlit,\n\t//\t\t\t\t\t\tmax_length_bits);\n\t//show_bits(&(code_list[0]), hlit);\n\n\ttabptr = undeflate_build_huff_tab(tabptr, &(code_list[0]),\n\t\t\thlit, &(bl_count[0]), max_length_bits);\n\tif(tabptr == 0) {\n\t\tprintf(\"Building literal table failed\\n\");\n\t\treturn 0;\n\t}\n\t//show_huftb(tabptr, max_length_bits);\n\n\ttabptr_dist = g_undeflate_dynamic_dist_tabptr;\n\tif(!tabptr_dist) {\n\t\ttabptr_dist = malloc(sizeof(word32) * ((1 << 15) + 1));\n\t\tg_undeflate_dynamic_dist_tabptr = tabptr_dist;\n\t\t// printf(\"malloc dist table\\n\");\n\t}\n\tg_undeflate_dynamic_dist_bits = max_distance_bits;\n\ttabptr_dist = undeflate_build_huff_tab(tabptr_dist, &(code_list[hlit]),\n\t\t\thdist, &(bl_count_dist[0]), max_distance_bits);\n\tif(tabptr_dist == 0) {\n\t\tprintf(\"Building dist table failed\\n\");\n\t\treturn 0;\n\t}\n\n\t// Update *bit_pos_ptr to skip over the table\n\t*bit_pos_ptr = bit_pos + (int)(8*(cptr - cptr_start));\n\treturn tabptr;\n}\n\nbyte *\nundeflate_block(Disk *dsk, byte *cptr, word32 *bit_pos_ptr, byte *cptr_base,\n\t\t\t\t\t\t\tbyte *cptr_end)\n{\n\tword32\t*lit_tabptr, *dist_tabptr;\n\tbyte\t*ucptr, *ucptr_end;\n\tword32\tbfinal, btype, bit_pos, len, pos, extra_bits, entry, dist_entry;\n\tword32\tbits, is_len, dist, lit_mask, dist_mask, tmp;\n\tint\ti;\n\n\tbit_pos = *bit_pos_ptr;\n\n\t// printf(\"At file offset %08x,bit %d cptr[0]:%02x %02x\\n\",\n\t//\t\t(word32)(cptr - cptr_base), bit_pos, cptr[0], cptr[1]);\n\tbfinal = (cptr[0] >> bit_pos) & 1;\n\tbit_pos++;\n\tbtype = (((cptr[1] << 8) | cptr[0]) >> bit_pos) & 3;\n\tbit_pos += 2;\n\tcptr += (bit_pos >> 3);\n\tbit_pos = bit_pos & 7;\n\t// printf(\"bfinal:%d, btype:%d\\n\", bfinal, btype);\n\n\tif(bfinal) {\n\t\tdsk->fd = 0;\t\t\t// Last block\n\t}\n\tif(btype == 3) {\t\t\t// Reserved: error\n\t\treturn 0;\n\t} else if(btype == 0) {\t\t\t// uncompressed\n\t\t// Align cptr to next byte\n\t\tbit_pos += 7;\n\t\tcptr += (bit_pos >> 3);\n\t\t*bit_pos_ptr = 0;\n\t\tlen = cptr[0] + (cptr[1] << 8);\n\t\tucptr = undeflate_ensure_dest_len(dsk, 0, len);\n\t\tif(!ucptr) {\n\t\t\treturn 0;\n\t\t}\n\t\tcptr += 4;\n\t\tfor(i = 0; i < (int)len; i++) {\n\t\t\t*ucptr++ = *cptr++;\n\t\t}\n\t\tdsk->dimage_size += len;\n\t\treturn cptr;\n\t}\n\n\tif(btype == 1) {\t\t\t// Fixed Huffman codes\n\t\tlit_tabptr = &(g_undeflate_fixed_len_tab[0]);\n\t\tdist_tabptr = &(g_undeflate_fixed_dist_tab[0]);\n\t\tlit_mask = 0x1ff;\n\t\tdist_mask = 0x1f;\n\t} else {\t\t\t\t// Dynamic Huffman codes\n\t\t*bit_pos_ptr = bit_pos;\n\t\tlit_tabptr = undeflate_dynamic_table(cptr, bit_pos_ptr,\n\t\t\t\t\t\t\t\tcptr_base);\n\t\tdist_tabptr = g_undeflate_dynamic_dist_tabptr;\n\t\t// printf(\"dynamic table used %d bits\\n\",\n\t\t//\t\t\t\t*bit_pos_ptr - bit_pos);\n\t\tlit_mask = (1 << g_undeflate_dynamic_bits) - 1;\n\t\tdist_mask = (1 << g_undeflate_dynamic_dist_bits) - 1;\n\t\tbit_pos = *bit_pos_ptr;\n\t\tcptr += (bit_pos >> 3);\n\t\tbit_pos = bit_pos & 7;\n\t}\n\tif(!lit_tabptr || !dist_tabptr) {\n\t\tprintf(\"Code table failure\\n\");\n\t\treturn 0;\n\t}\n\n\tucptr = undeflate_ensure_dest_len(dsk, 0, 65536);\t// Just a guess\n\tif(!ucptr) {\n\t\treturn 0;\n\t}\n\tucptr_end = dsk->raw_data + dsk->raw_dsize - 500;\n\n\twhile(cptr < cptr_end) {\n#if 0\n\t\tprintf(\"Top of loop, cptr:%p, lit_tabptr:%p, dsk->raw:%p\\n\",\n\t\t\t\tcptr, lit_tabptr, dsk->raw_data);\n#endif\n\n\t\tif(ucptr > ucptr_end) {\n\t\t\tucptr = undeflate_ensure_dest_len(dsk, ucptr, 65536);\n\t\t\tucptr_end = dsk->raw_data + dsk->raw_dsize - 500;\n\t\t\t// printf(\"Update ucptr to %p\\n\", ucptr);\n\t\t\tif(!ucptr) {\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t\tpos = cptr[0] | (cptr[1] << 8) | (cptr[2] << 16);\n\t\tpos = pos >> bit_pos;\n\t\tentry = lit_tabptr[pos & lit_mask];\n\t\tbits = (entry >> 16) & 0xf;\n\t\tis_len = (entry >> 24) & 1;\n\t\tlen = entry & 0xffff;\n#if 0\n\t\tprintf(\"At offset +%08x bit:%d, huffcode=%04x, is %d bits, \"\n\t\t\t\"entry=%08x\\n\", (int)(cptr - cptr_base), bit_pos,\n\t\t\tpos & lit_mask, bits, entry);\n#endif\n\t\tif(bits == 0) {\n\t\t\tprintf(\"bits=0, %08x bad table\\n\", lit_mask);\n\t\t\treturn 0;\n\t\t}\n\t\tbit_pos += bits;\n\t\tcptr += (bit_pos >> 3);\n\t\tbit_pos = bit_pos & 7;\n\t\tif(!is_len) {\t\t\t// Literal\n\t\t\t// literal byte\n\t\t\t// printf(\" Out +%06x: %02x\\n\",\n\t\t\t//\t(int)(ucptr - dsk->raw_data), len & 0xff);\n\t\t\t//putc(len, g_outf);\n\t\t\t*ucptr++ = len;\n\t\t} else {\n\t\t\tif(len == 2) {\t\t\t// Code=0x100, end block\n\t\t\t\t// All done\n\t\t\t\t// printf(\"Got the 0x100 code!  All done!\\n\");\n\t\t\t\t*bit_pos_ptr = bit_pos;\n\t\t\t\tdsk->dimage_size = ucptr - dsk->raw_data;\n\t\t\t\t// printf(\"Set dsk->image_size = %08x\\n\",\n\t\t\t\t//\t\t\tdsk->image_size);\n\t\t\t\treturn cptr;\n\t\t\t}\n\t\t\textra_bits = (entry >> 20) & 7;\n\t\t\tif(extra_bits) {\n\t\t\t\tpos = cptr[0] | (cptr[1] << 8);\n\t\t\t\tpos = pos >> bit_pos;\n\t\t\t\tpos = pos & ((1 << extra_bits) - 1);\n\t\t\t\tlen += pos;\n\t\t\t}\n#if 0\n\t\t\tprintf(\"At offset +%08x, bit:%d got extra_bits=%d, \"\n\t\t\t\t\"len=%08x\\n\", (int)(cptr - cptr_base), bit_pos,\n\t\t\t\t\t\t\textra_bits, len);\n#endif\n\t\t\tbit_pos += extra_bits;\n\t\t\tcptr += (bit_pos >> 3);\n\t\t\tbit_pos = bit_pos & 7;\n\n\t\t\t// Get distance code\n\t\t\tpos = cptr[0] | (cptr[1] << 8) | (cptr[2] << 16);\n\t\t\tpos = pos >> bit_pos;\n#if 0\n\t\t\tprintf(\"At offset +%08x, bit:%d raw distance code: \"\n\t\t\t\t\"%02x\\n\", (int)(cptr - cptr_base), bit_pos,\n\t\t\t\tpos & dist_mask);\n#endif\n\t\t\tdist_entry = dist_tabptr[pos & dist_mask];\n\t\t\tbits = (dist_entry >> 16) & 0xf;\n\t\t\tif(bits == 0) {\n\t\t\t\tprintf(\"bits=0 for dist_entry:%08x %08x\\n\",\n\t\t\t\t\tdist_entry, pos);\n\t\t\t}\n\t\t\textra_bits = (dist_entry >> 20) & 0xf;\n\t\t\tdist = dist_entry & 0xffff;\n\t\t\t//printf(\"dist_entry:%08x, extra_bits:%d, dist:%05x\\n\",\n\t\t\t//\tdist_entry, extra_bits, dist);\n\t\t\tbit_pos += bits;\n\t\t\tcptr += (bit_pos >> 3);\n\t\t\tbit_pos = bit_pos & 7;\n\t\t\tif(extra_bits) {\n\t\t\t\tpos = (cptr[0] | (cptr[1] << 8) |\n\t\t\t\t\t\t(cptr[2] << 16)) >> bit_pos;\n#if 0\n\t\t\t\tprintf(\" At offset +%08x, bit:%d, raw ex:\"\n\t\t\t\t\t\"%08x\\n\", (int)(cptr - cptr_base),\n\t\t\t\t\tbit_pos, pos);\n#endif\n\t\t\t\ttmp = pos & ((1 << extra_bits) - 1);\n\t\t\t\tdist += tmp;\n#if 0\n\t\t\t\tprintf(\"at offset +%08x, got %d extra dist \"\n\t\t\t\t\t\"for total dist=%d (%05x)\\n\",\n\t\t\t\t\t(int)(cptr - cptr_base), extra_bits,\n\t\t\t\t\tdist, pos);\n#endif\n\t\t\t\tbit_pos += extra_bits;\n\t\t\t\tcptr += (bit_pos >> 3);\n\t\t\t\tbit_pos = bit_pos & 7;\n\t\t\t}\n\t\t\t//printf(\"Repeating %d bytes from dist:%05x\\n\", len,\n\t\t\t//\t\t\t\t\t\tdist);\n\t\t\tif(ucptr < (dsk->raw_data + dist)) {\n\t\t\t\tprintf(\"Dist out of bounds:%04x %p %p\\n\",\n\t\t\t\t\t\tdist, ucptr, dsk->raw_data);\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tfor(i = 0; i < (int)len; i++) {\n\t\t\t\tucptr[0] = ucptr[0-(int)dist];\n#if 0\n\t\t\t\tputc(ucptr[0], g_outf);\n\t\t\t\tprintf(\" Out +%06x: %02x\\n\",\n\t\t\t\t\t(int)(ucptr - dsk->raw_data),\n\t\t\t\t\tucptr[0]);\n#endif\n\t\t\t\tucptr++;\n\t\t\t}\n\t\t}\n\t}\n\n\tprintf(\"Ran out of compressed data, bad gzip file\\n\");\n\n\treturn 0;\n}\n\nbyte *\nundeflate_gzip_header(Disk *dsk, byte *cptr, word32 compr_size)\n{\n\tword32\t*wptr;\n\tbyte\t*cptr_base, *cptr_end;\n\tword32\tflg, xfl, xlen, bit_offset, exp_crc, len, crc;\n\n\tcptr_base = cptr;\n\tcptr_end = cptr + compr_size;\n\n\tif((cptr[0] != 0x1f) || (cptr[1] != 0x8b) || (cptr[2] != 0x08)) {\n\t\tprintf(\"Not gzip file, exiting\\n\");\n\t\treturn 0;\n\t}\n\n\tflg = cptr[3];\n\txfl = cptr[8];\n\tprintf(\"flg:%02x and xflags:%02x\\n\", flg, xfl);\n\tcptr += 10;\n\n\tif(flg & 4) {\t\t// FEXTRA set\n\t\txlen = cptr[0] + (cptr[1] * 256);\n\t\tprintf(\"FEXTRA XLEN is %d, skipping that many bytes\\n\", xlen);\n\t\tcptr += 2 + xlen;\n\t}\n\n\tif(flg & 8) {\t\t// FNAME set\n\t\tcptr += strlen((char *)cptr) + 1;\n\t}\n\tif(flg & 0x10) {\t// FCOMMENT set\n\t\tcptr += strlen((char *)cptr) + 1;\n\t}\n\tif(flg & 2) {\t\t// FHCRC set\n\t\tcptr += 2;\n\t}\n\tprintf(\"gzip header was %02x bytes long\\n\", (int)(cptr - cptr_base));\n\n\tdsk->raw_dsize = 140*1024;\t\t// Just a guess, alloc size\n\tdsk->raw_data = undeflate_malloc(dsk->raw_dsize);\n\tif(dsk->raw_data == 0) {\n\t\treturn 0;\n\t}\n\tprintf(\"Initial malloc (not realloc) set raw_data=%p\\n\", dsk->raw_data);\n\n\tdsk->dimage_size = 0;\t\t\t// Used size\n\n\twptr = undeflate_init_tables();\n\tif(wptr == 0) {\n\t\treturn 0;\t\t\t// Some sort of error, get out\n\t}\n\n\tbit_offset = 0;\n\twhile(cptr < cptr_end) {\n\t\tcptr = undeflate_block(dsk, cptr, &bit_offset, cptr_base,\n\t\t\t\t\t\t\t\tcptr_end);\n\t\tif(cptr == 0) {\n\t\t\t// Failed\n\t\t\tbreak;\n\t\t}\n\t\tif(dsk->fd == 0) {\n\t\t\tprintf(\"undeflate_block set fd=0, success\\n\");\n\t\t\t// Done, success!\n\t\t\t// Check crc\n\t\t\tif(bit_offset) {\n\t\t\t\tcptr++;\n\t\t\t}\n\t\t\tif((cptr + 8) > cptr_end) {\n\t\t\t\tprintf(\"No CRC or LEN fields at end\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\texp_crc = cptr[0] | (cptr[1] << 8) | (cptr[2] << 16) |\n\t\t\t\t\t\t(cptr[3] << 24);\n\t\t\tlen = cptr[4] | (cptr[5] << 8) | (cptr[6] << 16) |\n\t\t\t\t\t\t(cptr[7] << 24);\n\t\t\tif(len != dsk->dimage_size) {\n\t\t\t\tprintf(\"Len mismatch: exp %08x != %08llx\\n\",\n\t\t\t\t\t\t\tlen, dsk->dimage_size);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcrc = woz_calc_crc32(dsk->raw_data, len, 0);\n\t\t\tif(crc != exp_crc) {\n\t\t\t\tprintf(\"CRC mismatch: %08x != exp %08x\\n\",\n\t\t\t\t\t\tcrc, exp_crc);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t// Real success, set raw_dsize\n\t\t\tdsk->raw_data = undeflate_realloc(dsk->raw_data,\n\t\t\t\t\t\t\tdsk->dimage_size);\n\t\t\tdsk->raw_dsize = dsk->dimage_size;\n\t\t\treturn cptr;\n\t\t}\n\t}\n\n\tprintf(\"Failed\\n\");\n\t// Disk image thread not found, get out\n\tfree(dsk->raw_data);\n\tdsk->fd = -1;\n\tdsk->dimage_size = 0;\n\tdsk->raw_data = 0;\n\tdsk->raw_dsize = 0;\n\treturn 0;\n}\n\nvoid\nundeflate_gzip(Disk *dsk, const char *name_str)\n{\n\tbyte\t*cptr;\n\tdword64\tcompr_dsize, dret;\n\tword32\tcompr_size;\n\tint\tfd;\n\tint\ti;\n\n\t// On success, set dsk->fd=0 and dsk->raw_data,raw_dsize properly.\n\tprintf(\"undeflate_gzip on file %s\\n\", name_str);\n\tfd = open(name_str, O_RDONLY | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\treturn;\n\t}\n\tcompr_dsize = cfg_get_fd_size(fd);\n\tprintf(\"size: %lld\\n\", compr_dsize);\n\tif((compr_dsize >> 31) != 0) {\n\t\t// > 2GB...too big for this code\n\t\tprintf(\"gzip file is too large\\n\");\n\t\tdsk->fd = -1;\n\t\treturn;\n\t}\n\tcompr_size = (word32)compr_dsize;\n\n\tcptr = malloc(compr_size + 0x1000);\n\tfor(i = 0; i < 0x1000; i++) {\n\t\tcptr[compr_size + i] = 0;\n\t}\n\tdret = cfg_read_from_fd(fd, cptr, 0, compr_size);\n\tif(dret != compr_size) {\n\t\tcompr_size = 0;\t\t// Make header searching fail\n\t}\n\t//g_outf = fopen(\"out.dbg\", \"w\");\n\tundeflate_gzip_header(dsk, cptr, compr_size);\n\n\tfree(cptr);\n\tundeflate_free_tables();\n}\n\nbyte *\nundeflate_zipfile_blocks(Disk *dsk, byte *cptr, dword64 dcompr_size)\n{\n\tword32\t*wptr;\n\tbyte\t*cptr_base, *cptr_end;\n\tword32\tbit_offset;\n\n\tcptr_base = cptr;\n\tcptr_end = cptr + dcompr_size;\n\n\tdsk->raw_data = undeflate_malloc(dsk->raw_dsize);\n\tif(dsk->raw_data == 0) {\n\t\treturn 0;\n\t}\n\tprintf(\"Initial malloc (not realloc) set raw_data=%p\\n\", dsk->raw_data);\n\n\tdsk->dimage_size = 0;\t\t\t// Used size\n\n\twptr = undeflate_init_tables();\n\tif(wptr == 0) {\n\t\treturn 0;\t\t\t// Some sort of error, get out\n\t}\n\n\tbit_offset = 0;\n\twhile(cptr < cptr_end) {\n\t\tcptr = undeflate_block(dsk, cptr, &bit_offset, cptr_base,\n\t\t\t\t\t\t\t\tcptr_end);\n\t\tif(cptr == 0) {\n\t\t\t// Failed\n\t\t\tbreak;\n\t\t}\n\t\tif(dsk->fd == 0) {\n\t\t\tprintf(\"undeflate_block set fd=0, success\\n\");\n\t\t\t// Done, success!\n\t\t\t// Check crc\n\t\t\tif(bit_offset) {\n\t\t\t\tcptr++;\n\t\t\t}\n\t\t\t// Real success, set raw_dsize\n\t\t\tdsk->raw_data = undeflate_realloc(dsk->raw_data,\n\t\t\t\t\t\t\tdsk->dimage_size);\n\t\t\tdsk->raw_dsize = dsk->dimage_size;\n\t\t\treturn cptr;\n\t\t}\n\t}\n\n\tprintf(\"Failed\\n\");\n\t// Disk image thread not found, get out\n\tfree(dsk->raw_data);\n\tdsk->fd = -1;\n\tdsk->dimage_size = 0;\n\tdsk->raw_data = 0;\n\tdsk->raw_dsize = 0;\n\treturn 0;\n}\n\nbyte g_zip_local_file_header[] = { 0x50, 0x4b, 0x03, 0x04 };\nbyte g_zip_central_file_header[] = { 0x50, 0x4b, 0x01, 0x02 };\nbyte g_zip_end_central_dir_header[] = { 0x50, 0x4b, 0x05, 0x06, 0, 0, 0, 0 };\nbyte g_zip64_end_central_dir_locator[] = { 0x50, 0x4b, 0x06, 0x07, 0, 0, 0, 0 };\nbyte g_zip64_end_central_dir_header[] = { 0x50, 0x4b, 0x06, 0x06 };\n\nextern Cfg_listhdr g_cfg_partitionlist;\n\n\nint\nundeflate_zipfile(Disk *dsk, int fd, dword64 dlocal_header_off,\n\t\t\tdword64 uncompr_dsize, dword64 compr_dsize)\n{\n\tbyte\tbuf[64];\n\tbyte\t*cptr, *cptr2;\n\tdword64\tdret, compr_doffset;\n\tword32\tcompr_method, name_len, extra_len;\n\tword32\tbit_flags;\n\tint\tret;\n\tint\ti;\n\n\t// return -1 on failure, >= 0 on success\n\n\tprintf(\"undeflate_zipfile called, fd:%d, offset:%08llx, unc:%lld \"\n\t\t\"compr:%lld\\n\", fd, dlocal_header_off, uncompr_dsize,\n\t\tcompr_dsize);\n\n\tdret = cfg_read_from_fd(fd, &buf[0], dlocal_header_off, 64);\n\tif(dret != 64) {\n\t\tprintf(\"read dret:%08llx != 64\\n\", dret);\n\t\treturn -1;\n\t}\n\n\tfor(i = 0; i < 4; i++) {\n\t\tif(buf[i] != g_zip_local_file_header[i]) {\n\t\t\tprintf(\"hdr[%d]=%02x\\n\", i, buf[i]);\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\tif(((uncompr_dsize | compr_dsize) >> 31) != 0) {\n\t\tprintf(\"Size >2GB, not supported\\n\");\n\t\treturn -1;\n\t}\n\tbit_flags = cfg_get_le16(&(buf[6]));\n\tcompr_method = cfg_get_le16(&(buf[8]));\n\t// compr_size = cfg_get_le32(&(buf[18]));\t// Probably 0\n\t// uncompr_size = cfg_get_le32(&(buf[22]));\t// Probably 0\n\tname_len = cfg_get_le16(&(buf[26]));\n\textra_len = cfg_get_le16(&(buf[28]));\n\n\t// The ZIP file format is annoying, the local header doesn't have\n\t//  compr_size and uncompr_size generally (if bit_flags bit 3 is set).\n\t// Even if it does, it's fine to always use the central directory\n\tprintf(\"bit_flags: %04x\\n\", bit_flags);\n\n\tcompr_doffset = dlocal_header_off + 30 + name_len + extra_len;\n\n\tcptr = undeflate_malloc(compr_dsize + 0x1000);\n\tfor(i = 0; i < 0x1000; i++) {\n\t\tcptr[compr_dsize + i] = 0;\n\t}\n\n\tdret = cfg_read_from_fd(fd, cptr, compr_doffset, compr_dsize);\n\tif(dret != compr_dsize) {\n\t\treturn -1;\n\t}\n\tdsk->raw_dsize = uncompr_dsize;\n\tdsk->dimage_size = uncompr_dsize;\n\n\tret = -1;\n\tif(compr_method == 0) {\t\t\t// Stored, just use cptr\n\t\tdsk->raw_data = cptr;\n\t\tdsk->raw_dsize = uncompr_dsize;\n\t\tdsk->dimage_start = 0;\n\t\tclose(fd);\n\t\tdsk->fd = 0;\n\t\tcptr = 0;\t\t\t// So free(cptr) does nothing\n\t\tret = 0;\n\t} else if(compr_method == 8) {\t\t// Deflate\n\t\tcptr2 = undeflate_zipfile_blocks(dsk, cptr, compr_dsize);\n\t\tprintf(\"undeflate_zipfile_blocks ret:%p\\n\", cptr2);\n\t\tif(cptr2 != 0) {\n\t\t\tret = 0;\n\t\t}\n\t} else {\n\t\tprintf(\"Unknown compr_method:%04x\\n\", compr_method);\n\t}\n\tfree(cptr);\n\tundeflate_free_tables();\n\n\treturn ret;\n}\n\nint\nundeflate_zipfile_search(byte *bptr, byte *cmp_ptr, int size, int cmp_len,\n\t\t\t\tint min_size)\n{\n\tint\tpos, good;\n\tint\ti;\n\n\t// Search for cmp_ptr in the bptr buffer (basically, look for \"PKxx\"\n\t//  header strings).\n\tpos = size - min_size;\n\tgood = 0;\n\twhile(pos >= 0) {\n\t\tgood = 1;\n\t\tfor(i = 0; i < cmp_len; i++) {\n\t\t\tif(bptr[pos + i] != cmp_ptr[i]) {\n\t\t\t\tgood = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(good) {\n\t\t\tbreak;\n\t\t}\n\t\tpos--;\n\t}\n\n\tif(!good) {\n\t\treturn -1;\n\t}\n\treturn pos;\n}\n\nint\nundeflate_zipfile_make_list(int fd)\n{\n\tbyte\tbuf[1024];\n\tdword64\tdret, dsize, dir_doff, dir_dsize, unc_dsize, compr_dsize;\n\tdword64\tlocal_dheader, dneg1, dval, doff, dpos, dlen;\n\tbyte\t*dirptr, *name_ptr, *bptr, *bptr2;\n\tchar\t*str;\n\tword32\textra_len, comment_len, ent, entries, part_len, inc;\n\tword32\ttmp_off, ex_off, this_size, this_id;\n\tint\tpos, good, add_it, need_compr, need_unc, need_dheader;\n\tint\tname_len;\n\tint\ti;\n\n\tdret = cfg_read_from_fd(fd, &buf[0], 0, 64);\n\tif(dret != 64) {\n\t\treturn 0;\t\t// Not a ZIP file\n\t}\n\n\t// See if it's a PKZIP file, starting 0x50, 0x4b, 0x03, 0x04\n\tfor(i = 0; i < 4; i++) {\n\t\tif(buf[i] != g_zip_local_file_header[i]) {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprintf(\"This looks like a .zip file\\n\");\n\n\t// Find end of central directory record in last 1024 bytes.  If it's\n\t//  not there, this is too complex of a ZIP file for us, give up\n\tdsize = cfg_get_fd_size(fd);\n\n\tfor(i = 0; i < 1024; i++) {\n\t\tbuf[i] = 0;\n\t}\n\tdpos = 0;\n\tdlen = dsize;\n\tif(dsize > 1024) {\n\t\tdpos = dsize - 1024;\n\t\tdlen = 1024;\n\t}\n\tdret = cfg_read_from_fd(fd, &buf[0], dpos, dlen);\n\tif(dret != dlen) {\n\t\treturn 0;\t\t// Unknown problem\n\t}\n\n\tpos = undeflate_zipfile_search(&buf[0],\n\t\t\t\t&g_zip_end_central_dir_header[0], 1024, 8, 22);\n\t\t\t// End of Central Directory is at least 22 bytes\n\tif(pos < 0) {\n\t\tprintf(\"Cannot parse this .zip file\\n\");\n\t\treturn 0;\n\t}\n\n\tentries = cfg_get_le16(&(buf[pos + 8]));\n\tdir_dsize = cfg_get_le32(&(buf[pos + 12]));\n\tdir_doff = cfg_get_le32(&(buf[pos + 16]));\n\n#if 0\n\tprintf(\".zip entries:%04x, dir_dsize:%06llx, dir_doff:%08llx\\n\",\n\t\t\t\t\tentries, dir_dsize, dir_doff);\n#endif\n\tdneg1 = 0xffffffffULL;\n\tif(dir_doff == dneg1) {\n\t\tprintf(\"We must look for the ZIP64 end dir locator\\n\");\n\t\tpos = undeflate_zipfile_search(&buf[0],\n\t\t\t&g_zip64_end_central_dir_locator[0], 1024, 8, 20);\n\t\tif(pos < 0) {\n\t\t\tprintf(\"Cannot parse this ZIP64 file\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\tdoff = cfg_get_le64(&(buf[pos + 8]));\n\t\tprintf(\"ZIP64 end of central dir record at 0x%08llx\\n\", doff);\n\t\tif((doff + 64) > dsize) {\n\t\t\tprintf(\"End Central Dir record out of bounds\\n\");\n\t\t\treturn 0;\n\t\t}\n\t\t// Now read end of central directory record.  Just read 64 bytes\n\t\t//  It has to be at least 56 bytes, and the locator had to be\n\t\t//  after, so it must fit\n\t\tdret = cfg_read_from_fd(fd, &buf[0], doff, 64);\n\t\tif(dret != 64) {\n\t\t\treturn 0;\t\t// Unknown problem\n\t\t}\n\t\tpos = undeflate_zipfile_search(&buf[0],\n\t\t\t&g_zip64_end_central_dir_header[0], 64, 4, 64);\n\t\tif(pos != 0) {\n\t\t\tprintf(\"ZIP64 end of central dir record not found\\n\");\n\t\t\treturn 0;\n\t\t}\n\n\t\tentries = cfg_get_le32(&(buf[32]));\n\t\tdir_dsize = cfg_get_le64(&(buf[40]));\n\t\tdir_doff = cfg_get_le64(&(buf[48]));\n\t}\n\n\tif((entries < 1) || (dir_dsize > dsize) || (dir_dsize > (1L << 20)) ||\n\t\t\t\t\t((dir_doff + dir_dsize) > dsize)) {\n\t\tprintf(\"Malformed zip file\\n\");\n\t\treturn 0;\n\t}\n\n\tdirptr = undeflate_malloc(dir_dsize);\n\tdret = cfg_read_from_fd(fd, dirptr, dir_doff, dir_dsize);\n\tif(dret != dir_dsize) {\n\t\tprintf(\"Couldn't read central dir\\n\");\n\t\treturn 0;\n\t}\n\n\tpart_len = cfg_partition_maybe_add_dotdot();\n\t\t// part_len is strlen(g_cfg_part_path[]);\n\n\tpos = 0;\n\tent = 0;\n\twhile(pos < (int)dir_dsize) {\n#if 0\n\t\tprintf(\"Working on ent %d at pos %d\\n\", ent, pos);\n#endif\n\t\tif(ent >= entries) {\n\t\t\tbreak;\t\t// all done\n\t\t}\n\t\tgood = 1;\n\t\tfor(i = 0; i < 4; i++) {\n\t\t\tif(dirptr[pos + i] != g_zip_central_file_header[i]) {\n\t\t\t\t// corrupt index, get out\n\t\t\t\tprintf(\"At pos %04x, i:%d bad hdr\\n\", pos, i);\n\t\t\t\tgood = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(!good) {\n\t\t\tbreak;\n\t\t}\n\t\tcompr_dsize = cfg_get_le32(&dirptr[pos + 20]);\n\t\tunc_dsize = cfg_get_le32(&dirptr[pos + 24]);\n\t\tname_len = cfg_get_le16(&dirptr[pos + 28]);\n\t\textra_len = cfg_get_le16(&dirptr[pos + 30]);\n\t\tcomment_len = cfg_get_le16(&dirptr[pos + 32]);\n\t\tlocal_dheader = cfg_get_le32(&dirptr[pos + 42]);\n\t\tif((pos + 46UL + name_len) > dir_dsize) {\n\t\t\tprintf(\"Corrupt entry: pos:%04x, name_len:%04x, \"\n\t\t\t\t\"dir_dsize:%05llx\\n\", pos, name_len, dir_dsize);\n\t\t\tbreak;\n\t\t}\n\n\t\tneed_unc = (unc_dsize == dneg1);\n\t\tneed_compr = (compr_dsize == dneg1);\n\t\tneed_dheader = (local_dheader == dneg1);\n\n\t\t// Walk extras to update unc/compr size and file offset, if\n\t\t//  the standard fields are 0xffffffff.\n\t\tbptr = &(dirptr[pos + 46 + name_len]);\n\t\tex_off = 0;\n\t\tadd_it = 1;\n\t\twhile(ex_off < extra_len) {\n#if 0\n\t\t\tprintf(\"Working on ex_off:%d out of %d\\n\", ex_off,\n\t\t\t\t\t\t\t\textra_len);\n#endif\n\t\t\tthis_id = cfg_get_le16(&bptr[ex_off]);\n\t\t\tthis_size = cfg_get_le16(&bptr[ex_off + 2]);\n\t\t\tif((this_size + ex_off + pos + 46UL + name_len) >\n\t\t\t\t\t\t\t\tdir_dsize) {\n\t\t\t\tprintf(\"Corrupt ZIP64 extra info entry\\n\");\n\t\t\t\tadd_it = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tex_off += 4;\n\t\t\tif(this_id == 0x0001) {\n\t\t\t\ttmp_off = 0;\n\t\t\t\tbptr2 = &(bptr[ex_off]);\n\t\t\t\twhile(tmp_off < this_size) {\n\t\t\t\t\tdval = cfg_get_le64(bptr2);\n#if 0\n\t\t\t\t\tprintf(\"tmp_off %d of %d, dval:\"\n\t\t\t\t\t\t\"%016llx\\n\", tmp_off,\n\t\t\t\t\t\tthis_size, dval);\n#endif\n\t\t\t\t\tif(need_compr) {\n\t\t\t\t\t\tcompr_dsize = dval;\n\t\t\t\t\t\tneed_compr = 0;\n\t\t\t\t\t} else if(need_unc) {\n\t\t\t\t\t\tunc_dsize = dval;\n\t\t\t\t\t\tneed_unc = 0;\n\t\t\t\t\t} else if(need_dheader) {\n\t\t\t\t\t\tlocal_dheader = dval;\n\t\t\t\t\t\tneed_dheader = 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tprintf(\"Corrupt ZIP64\\n\");\n\t\t\t\t\t\tadd_it = 0;\n\t\t\t\t\t}\n\t\t\t\t\ttmp_off += 8;\n\t\t\t\t\tbptr += 8;\n\t\t\t\t}\n\t\t\t}\n\t\t\tex_off += this_size;\n\t\t}\n\n\t\tif(need_unc || need_compr || need_dheader) {\n\t\t\tprintf(\"Bad ZIP64 overrides\\n\");\n\t\t\tadd_it = 0;\n\t\t}\n\n\t\t// See if filename is at the proper depth\n\t\tname_ptr = &(dirptr[pos + 46]);\n\t\tif(add_it) {\n\t\t\tadd_it = cfg_partition_name_check(name_ptr, name_len);\n\t\t}\n\n\t\t//printf(\"ent:%d name:%s len:%d had add_it:%d, part_len:%d\\n\",\n\t\t//\t\tent, name_ptr, name_len, add_it, part_len);\n\n\t\tinc = 46 + name_len + extra_len + comment_len;\n\t\tif(add_it) {\n\t\t\t// Handle directories either explicitly listed, as\n\t\t\t//  foo/, foo/bar/, foo/bar/1, foo/bar/2 ; or\n\t\t\t//  implied:  foo/bar/1 and foo/bar/2 as entries\n\t\t\t//  implies foo/ and foo/bar/ are directories.\n\t\t\t// Add any name at the current part_len level, but\n\t\t\t//  make sure it's unique (don't add lots of \"foo\"s).\n\t\t\tname_ptr += part_len;\n\t\t\tname_len -= part_len;\n\t\t\tif(name_len <= 0) {\n\t\t\t\tadd_it = 0;\n\t\t\t}\n\t\t\tfor(i = 0; i < name_len; i++) {\n\t\t\t\tif(name_ptr[i] == '/') {\n\t\t\t\t\t// This ends this name at this level\n\t\t\t\t\tif(i > 0) {\n\t\t\t\t\t\tadd_it = 2;\n\t\t\t\t\t\tname_len = i + 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tadd_it = 0;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif((add_it < 2) && (unc_dsize < 140*1024)) {\n\t\t\tadd_it = 0;\n\t\t}\n\t\tif(add_it) {\n\t\t\tstr = malloc(name_len + 1);\n\t\t\tcfg_strncpy(str, (char *)&name_ptr[0], name_len + 1);\n\t\t\tcfg_file_add_dirent_unique(&g_cfg_partitionlist, str,\n\t\t\t\tadd_it - 1, unc_dsize, local_dheader,\n\t\t\t\tcompr_dsize, ent);\n\t\t\tfree(str);\n\t\t}\n\t\tpos += inc;\n\t\tent++;\n\t}\n\tfree(dirptr);\n\n\tprintf(\"Returning %d, pos:%05x, dir_dsize:%05llx\\n\", ent, pos,\n\t\t\t\t\t\t\t\tdir_dsize);\n\treturn g_cfg_partitionlist.last;\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/unshk.c",
    "content": "const char rcsid_unshk_c[] = \"@(#)$KmKId: unshk.c,v 1.14 2023-05-19 13:57:52+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2021 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// This code is based on the official NuFX documentation in Apple II FTN.e08002\n//  available at: http://nulib.com/library/FTN.e08002.htm.  Andy McFadden has\n//  reverse-engineered GSHK (and its quirks) in Nulib, at: http://nulib.com/,\n//  and that code was very helpful in getting the basic algorithms correct.\n\n#include \"defc.h\"\n\nword32\nunshk_get_long4(byte *bptr)\n{\n\tword32\tval;\n\tint\ti;\n\n\t// Get 4 bytes in little-endian form\n\tval = 0;\n\tfor(i = 3; i >=0 ; i--) {\n\t\tval = (val << 8) | bptr[i];\n\t}\n\treturn val;\n}\n\nword32\nunshk_get_word2(byte *bptr)\n{\n\t// Get 2 bytes in little-endian form\n\treturn (bptr[1] << 8) | bptr[0];\n}\n\nword32\nunshk_calc_crc(byte *bptr, int size, word32 start_crc)\n{\n\tword32\tcrc;\n\tint\ti, j;\n\n\t// No table used: do basic CRC operation on size bytes.  For CCITT-16\n\t//  (the one used in ShrinkIt), xor the byte into the upper 8 bits of\n\t//  the current crc, then xor in 0x1021 for each '1' bit shifted out\n\t//  the top.  Use a 32-bit crc variable to get the bit shifted out.\n\tcrc = start_crc & 0xffff;\n\tfor(i = 0; i < size; i++) {\n\t\tcrc = crc ^ (bptr[i] << 8);\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\tcrc = crc << 1;\n\t\t\tif(crc & 0x10000) {\n\t\t\t\tcrc = crc ^ 0x11021;\n\t\t\t\t// XOR in 0x1021, and clear bit 16 as well.\n\t\t\t}\n\t\t}\n\t\t// printf(\"CRC after [%04x]=%02x is %04x\\n\", i, bptr[i], crc);\n\t}\n\treturn crc & 0xffff;\n}\n\nint\nunshk_unrle(byte *cptr, int len, word32 rle_delim, byte *ucptr)\n{\n\tbyte\t*start_ucptr;\n\tword32\tc;\n\tint\toutlen, count;\n\tint\ti;\n\n\t// RLE is 3 bytes: { 0xdb, char, count}, where count==0 means output\n\t//  one char.\n\tstart_ucptr = ucptr;\n\twhile(len > 0) {\n\t\tc = *cptr++;\n\t\tlen--;\n\t\tif(c == rle_delim) {\n\t\t\tc = *cptr++;\n\t\t\tcount = *cptr++;\n\t\t\tlen -= 2;\n\t\t\tfor(i = 0; i <= count; i++) {\n\t\t\t\t*ucptr++ = c;\n\t\t\t}\n\t\t} else {\n\t\t\t*ucptr++ = c;\n\t\t}\n\t}\n\toutlen = (int)(ucptr - start_ucptr);\n\tif(outlen != 0x1000) {\n\t\tprintf(\"RLE failed, output %d bytes\\n\", outlen);\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nvoid\nunshk_lzw_clear(Lzw_state *lzw_ptr)\n{\n\tint\ti;\n\n\tlzw_ptr->entry = 0x100;\t\t\t// First expected table pos\n\tlzw_ptr->bits = 9;\n\tfor(i = 0; i < 256; i++) {\n\t\tlzw_ptr->table[i] = i << 12;\t// Encodes depth==0 as well\n\t}\n\tlzw_ptr->table[0x100] = 0;\n}\n\n// LZW Table format in 32-bit word: { depth[11:0], finalc[7:0], code[11:0] }\n\nbyte *\nunshk_unlzw(byte *cptr, Lzw_state *lzw_ptr, byte *ucptr, word32 uclen)\n{\n\tbyte\t*end_ucptr, *bptr;\n\tword32\tmask, val, entry, newcode, finalc_code;\n\tint\tbit_pos, depth, bits;\n\n\t// This routine handles ShrinkIt LZW/1 and LZW/2 streams.  It expects\n\t//  the caller has set entry=0x100 and bits=9 at the start of each\n\t//  LZW/1 chunk\n\n\tentry = lzw_ptr->entry;\n\tbits = lzw_ptr->bits;\n\tend_ucptr = ucptr + uclen;\n\t//printf(\"unlzw block: format:%d, uclen:%04x\\n\", thread_format, uclen);\n\tmask = (1 << bits) - 1;\n\tbit_pos = 0;\n\n\twhile(ucptr < end_ucptr) {\n\t\tnewcode = (cptr[2] << 16) | (cptr[1] << 8) | cptr[0];\n\t\tnewcode = (newcode >> bit_pos) & mask;\n\t\tbit_pos += bits;\n\t\tcptr += (bit_pos >> 3);\n\t\tbit_pos = bit_pos & 7;\n\t\t// printf(\"At entry:%04x, bits:%d newcode:%04x\\n\", entry, bits,\n\t\t//\t\t\t\t\t\tnewcode);\n\t\tif((entry + 1) >= mask) {\n\t\t\tbits++;\n\t\t\tmask = (mask << 1) | 1;\n\t\t\t// Note this is one too early, but this is needed to\n\t\t\t//  match ShrinkIt\n\t\t}\n\n\t\t// Newcode is up to 12-bits, where <= 0xff means just this\n\t\t//  char, and >= 0x101 means chase down that code, output the\n\t\t//  character in that table entry, and repeat\n\t\tif(newcode == 0x100) {\n\t\t\t// printf(\"Got clear code\\n\");\n\t\t\tentry = 0x100;\n\t\t\tbits = 9;\n\t\t\tmask = 0x1ff;\n\t\t\tcontinue;\n\t\t}\n\t\tif(newcode > entry) {\n\t\t\tprintf(\"Bad code: %04x, entry:%04x\\n\", newcode, entry);\n\t\t\treturn 0;\n\t\t}\n#if 0\n\t\tif(newcode == entry) {\n\t\t\t// KwKwK case: operate on oldcode\n\t\t\tprintf(\"KwKwK case!\\n\");\n\t\t}\n#endif\n\t\tfinalc_code = newcode;\n\t\tdepth = lzw_ptr->table[newcode & 0xfff] >> 20;\n\t\t// depth will be 0 for 1 character, 1 for 2 characters, etc.\n\t\tbptr = ucptr + depth;\n\t\twhile(bptr >= ucptr) {\n\t\t\tfinalc_code = lzw_ptr->table[finalc_code & 0xfff];\n\t\t\t*bptr-- = (finalc_code >> 12) & 0xff;\n\t\t}\n\t\tval = lzw_ptr->table[entry];\n\t\tlzw_ptr->table[entry] = (val & (~0xff000)) |\n\t\t\t\t\t\t(finalc_code & 0xff000);\n\t\t\t// [entry] has code from last iteration (which stuck in\n\t\t\t//  the last finalc char, which we need to toss now),\n\t\t\t//  and update it with the correct finalc character.\n\t\t// printf(\"Table[%04x]=%08x\\n\", entry, lzw_ptr->table[entry]);\n\n\t\tdepth++;\n\t\tucptr += depth;\n#if 0\n\t\tbptr = ucptr - depth;\n\t\tprintf(\"src:%04x, out+%06x: \", (int)(cptr - cptr_start),\n\t\t\t\t(int)(ucptr - depth - (end_ucptr - uclen)));\n\t\tfor(i = 0; i < depth; i++) {\n\t\t\tprintf(\" %02x\", *bptr++);\n\t\t}\n\t\tprintf(\"\\n\");\n#endif\n\t\tlzw_ptr->table[entry + 1] = (depth << 20) |\n\t\t\t\t\t(finalc_code & 0xff000) | newcode;\n\t\t\t// Set tab[entry+1] for KwKwK case, with this newcode,\n\t\t\t//  and this finalc character.  This also saves this\n\t\t\t//  newcode when the next code is received.\n\t\tentry++;\n\t}\n\tlzw_ptr->entry = entry;\n\tlzw_ptr->bits = bits;\n\tif(bit_pos) {\t\t\t// We used part of this byte, use it\n\t\tcptr++;\n\t}\n\treturn cptr;\n}\n\nvoid\nunshk_data(Disk *dsk, byte *cptr, word32 compr_size, byte *ucptr,\n\t\tword32 uncompr_size, word32 thread_format, byte *base_cptr)\n{\n\tLzw_state lzw_state;\n\tbyte\t*end_cptr, *end_ucptr, *rle_inptr;\n\tword32\trle_delim, len, use_lzw, lzw_len, crc, chunk_crc;\n\tint\tret;\n\tint\ti;\n\n\tprintf(\"Uncompress %d compress bytes into %d bytes, source offset:\"\n\t\t\"%08x\\n\", compr_size, uncompr_size, (word32)(cptr - base_cptr));\n\n\t// LZW/1 format: crc_lo, crc_hi, vol, rle_delim then start the chunk\n\t//\teach chunk: rle_len_lo, rle_len_hi, lzw_used\n\t// LZW/2 format: vol, rle_delim then start the chunk\n\t//\teach chunk: rle_len_lo, rle_len_hi, lzw_len_lo, lzw_len_hi\n\t//\twhere rle_len_hi[7]==1 means LZW was used\n\tend_cptr = cptr + compr_size;\n\tend_ucptr = ucptr + uncompr_size;\n\n\tchunk_crc = 0;\n\tif(thread_format != 3) {\t\t// LZW/1\n\t\tchunk_crc = (cptr[1] << 8) | cptr[0];\n\t\tcptr += 2;\t\t\t// Skip over CRC bytes\n\t}\n\tdsk->vol_num = cptr[0];\t\t// LZW/1\n\trle_delim = cptr[1];\n\tcptr += 2;\n\tunshk_lzw_clear(&lzw_state);\n\n\t// printf(\"vol_num:%02x, rle_delim:%02x\\n\", dsk->vol_num, rle_delim);\n\n\t// LZW/1 format for each chunk: len_lo, len_hi, use_lzw\n\t// LZW/2 format for each chunk: len_lo, len_hi.  If len_hi[7]=1, then\n\t//\ttwo more bytes: lzw_len_lo, lzw_len_hi\n\twhile(cptr < (end_cptr - 4)) {\n\t\tif(ucptr >= end_ucptr) {\n\t\t\tbreak;\n\t\t}\n\t\tlen = (cptr[1] << 8) | cptr[0];\n#if 0\n\t\tprintf(\"chunk at +%08x, len:%04x, dest offset:%08x\\n\",\n\t\t\t\t(word32)(cptr - base_cptr), len,\n\t\t\t\t(word32)(ucptr - (end_ucptr - uncompr_size)));\n#endif\n\t\tcptr += 2;\n\t\tuse_lzw = (len >> 15) & 1;\n\t\tif(len & 0x6000) {\n\t\t\tprintf(\"Illegal length: %04x\\n\", len);\n\t\t\treturn;\t\t\t\t// Ilegal length\n\t\t}\n\t\tlen = len & 0x1fff;\n\t\tlzw_len = 0;\n\t\tif(thread_format == 3) {\t\t// LZW/2\n\t\t\tif(use_lzw) {\n\t\t\t\tlzw_len = (cptr[1] << 8) | cptr[0];\n\t\t\t\tif(lzw_len > 0x1004) {\n\t\t\t\t\tprintf(\"Bad lzw_len: %04x\\n\", lzw_len);\n\t\t\t\t\treturn;\t\t// Illegal\n\t\t\t\t}\n\t\t\t\tcptr += 2;\n\t\t\t\tlzw_len -= 4;\t\t// Counts from [-4]\n\t\t\t}\n\t\t} else {\t\t\t\t// LZW/1\n\t\t\tuse_lzw = *cptr++;\n\t\t\tif(use_lzw >= 2) {\n\t\t\t\tprintf(\"Bad use_lzw:%02x\\n\", use_lzw);\n\t\t\t\treturn;\t\t\t// Bad format\n\t\t\t}\n\t\t}\n\t\trle_inptr = cptr;\n\t\tif(use_lzw) {\n\t\t\t//printf(\"lzw on %02x.%02x.%02x.., %d bytes (rle:%d)\\n\",\n\t\t\t//\tcptr[0], cptr[1], cptr[2], lzw_len, len);\n\t\t\trle_inptr = ucptr;\n\t\t\tif(len != 0x1000) {\n\t\t\t\t// RLE pass is needed: Write to ucptr+0x1000,\n\t\t\t\t//  and then UnRLE down to ucptr;\n\t\t\t\trle_inptr = ucptr + 0x1000;\n\t\t\t}\n\t\t\tcptr = unshk_unlzw(cptr, &lzw_state, rle_inptr, len);\n\t\t\tif(cptr == 0) {\n\t\t\t\tprintf(\"Bad LZW stream\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif(thread_format != 3) {\t\t// LZW/1\n\t\t\t\tlzw_state.entry = 0x100;\t// Reset table\n\t\t\t\tlzw_state.bits = 9;\n\t\t\t}\n\t\t} else {\n\t\t\tlzw_state.entry = 0x100;\t// Reset table\n\t\t\tlzw_state.bits = 9;\n\t\t}\n\t\tif(len != 0x1000) {\n\t\t\t// printf(\"RLE on %02x.%02x.%02x... %d bytes\\n\",\n\t\t\t//\tcptr[0], cptr[1], cptr[2], len);\n\t\t\tret = unshk_unrle(rle_inptr, len, rle_delim, ucptr);\n\t\t\tif(ret) {\n\t\t\t\tprintf(\"unRLE failed\\n\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif(!use_lzw) {\n\t\t\t\tcptr += len;\n\t\t\t}\n\t\t} else if(!use_lzw) {\n\t\t\t// Uncompressed\n\t\t\t// printf(\"Uncompressed %02x.%02x.%02x....%d bytes\\n\",\n\t\t\t//\tcptr[0], cptr[1], cptr[2], len);\n\t\t\tfor(i = 0; i < 0x1000; i++) {\n\t\t\t\tucptr[i] = *cptr++;\n\t\t\t}\n\t\t}\n\t\t// write(g_out_fd, ucptr, 0x1000);\n\t\tucptr += 0x1000;\n\t}\n\n\tprintf(\"cptr:%p, end_cptr:%p, uncompr_size:%08x\\n\", cptr, end_cptr,\n\t\t\t\t\t\t\t\tuncompr_size);\n\tif(thread_format != 3) {\t\t// LZW/1\n\t\tcrc = unshk_calc_crc(ucptr - uncompr_size, uncompr_size, 0);\n\t\t//printf(\"LZW/1 calc CRC %04x vs CRC %04x\\n\", crc, chunk_crc);\n\t\tif(crc != chunk_crc) {\n\t\t\tprintf(\"Bad LZW/1 CRC: %04x != %04x\\n\", crc, chunk_crc);\n\t\t\treturn;\n\t\t}\n\t}\n\tdsk->fd = 0;\n}\n\nvoid\nunshk_parse_header(Disk *dsk, byte *cptr, int compr_size, byte *base_cptr)\n{\n\tbyte\t*cptr_end, *dptr, *ucptr;\n\tword32\ttotal_records, attrib_count, total_threads, thread_class;\n\tword32\tthread_format, thread_kind, thread_eof, comp_thread_eof;\n\tword32\tthread_crc, crc, version, disk_size, block_size, num_blocks;\n\tword32\tfilename_length;\n\tint\ti;\n\n\tcptr_end = cptr + compr_size;\n\tif(compr_size < 0xa0) {\n\t\tprintf(\"Didn't read everything\\n\");\n\t\treturn;\n\t}\n\n\t// Parse NuFX format: \"NuFile\" with alternating high bits\n\tif((cptr[0] != 0x4e) || (cptr[1] != 0xf5) || (cptr[2] != 0x46) ||\n\t\t(cptr[3] != 0xe9) || (cptr[4] != 0x6c) || (cptr[5] != 0xe5)) {\n\t\tprintf(\"Not NuFile, exiting\\n\");\n\t\treturn;\n\t}\n\ttotal_records = unshk_get_long4(&cptr[8]);\n\tif(total_records < 1) {\n\t\treturn;\n\t}\n\n\t// Master Header to NuFile is apparently 48 bytes.  Look for \"NuFX\"\n\t//  Header to describe threads\n\tcptr += 0x30;\n\tif((cptr[0] != 0x4e) || (cptr[1] != 0xf5) || (cptr[2] != 0x46) ||\n\t\t\t\t\t\t\t(cptr[3] != 0xd8)) {\n\t\treturn;\n\t}\n\tattrib_count = unshk_get_word2(&cptr[6]);\n\tversion = unshk_get_word2(&cptr[8]);\t\t// >= 3 means File CRC\n\ttotal_threads = unshk_get_long4(&cptr[10]);\n\tnum_blocks = unshk_get_long4(&cptr[26]);\t// extra_type\n\tblock_size = unshk_get_word2(&cptr[30]);\t// storage_type\n\t// P8 ShrinkIt is riddled with bugs.  Disk archives have incorrect\n\t//  thread_eof for the uncompressed total size.  So we need to do\n\t//  num_blocks * block_size to get the real size.  But this can be\n\t//  buggy too!  These fixes are from NuFxLib::Thread.c actualThreadEOF\n\t//  comments\n\t// First, fix block_size.  SHK v3.0.1 stored it as a small value\n\tif(block_size < 256) {\n\t\tblock_size = 512;\n\t}\n\tdisk_size = block_size * num_blocks;\n\tif(disk_size == (70*1024)) {\n\t\t// Old GSHK apparently set block_size==256 but blocks=280 for\n\t\t//  5.25\" DOS 3.3 disks...block size must be 512 to equal 140K.\n\t\tdisk_size = 140*1024;\n\t}\n\tif(disk_size < 140*1024) {\n\t\tprintf(\"disk_size %dK is invalid\\n\", disk_size >> 10);\n\t\treturn;\n\t}\n\tcptr += attrib_count;\n\tfilename_length = unshk_get_word2(&cptr[-2]);\t// filename_length\n\tcptr += filename_length;\n\tdptr = cptr + 16*total_threads;\n\t\t// Each thread is 16 bytes, so the data is at +16*total_threads\n\t\t// The data is in the same order as the header for the threads\n\t\t// We ignore anything other than a data thread for SDK\n\tfor(i = 0; i < (int)total_threads; i++) {\n\t\tif((dptr >= cptr_end) || (cptr >= cptr_end)) {\n\t\t\treturn;\n\t\t}\n\t\tthread_class = unshk_get_word2(&cptr[0]);\n\t\tthread_format = unshk_get_word2(&cptr[2]);\n\t\tthread_kind = unshk_get_word2(&cptr[4]);\n\t\tthread_crc = unshk_get_word2(&cptr[6]);\n\t\t//thread_eof = unshk_get_long4(&cptr[8]);\n\t\t// thread_eof is wrong in P8 ShrinkIt, so just use disk_size\n\t\tthread_eof = disk_size;\n\t\tcomp_thread_eof = unshk_get_long4(&cptr[12]);\n\t\tif((dptr + comp_thread_eof) > cptr_end) {\n\t\t\treturn;\t\t\t// Corrupt\n\t\t}\n\t\tif((thread_class == 2) && (thread_kind == 1)) {\n\t\t\t// Disk image!\n\t\t\tucptr = malloc(thread_eof + 0x1000);\n\t\t\tunshk_data(dsk, dptr, comp_thread_eof, ucptr,\n\t\t\t\t\tthread_eof, thread_format, base_cptr);\n\t\t\tif(dsk->fd == 0) {\n\t\t\t\t// Success, so far.  Check CRC\n\t\t\t\tprintf(\"Version:%d, thread_crc:%04x\\n\",\n\t\t\t\t\tversion, thread_crc);\n\t\t\t\tif(version >= 3) {\t\t// CRC is valid\n\t\t\t\t\tcrc = unshk_calc_crc(ucptr, thread_eof,\n\t\t\t\t\t\t\t\t0xffff);\n#if 0\n\t\t\t\t\tprintf(\"Thread CRC:%04x, exp:%04x\\n\",\n\t\t\t\t\t\tcrc, thread_crc);\n#endif\n\t\t\t\t\tif(crc != thread_crc) {\n\t\t\t\t\t\tprintf(\"Bad CRC: %04x != exp \"\n\t\t\t\t\t\t\t\"%04x\\n\", crc,\n\t\t\t\t\t\t\tthread_crc);\n\t\t\t\t\t\tdsk->fd = -1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(dsk->fd < 0) {\n\t\t\t\tfree(ucptr);\n\t\t\t} else {\n\t\t\t\t// Real success, set raw_size\n\t\t\t\tdsk->raw_dsize = thread_eof;\n\t\t\t\tdsk->raw_data = ucptr;\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\tdptr += comp_thread_eof;\n\t\t\tcptr += 16;\n\t\t}\n\t}\n\n\t// Disk image thread not found, get out\n}\n\nvoid\nunshk(Disk *dsk, const char *name_str)\n{\n\tbyte\t*cptr;\n\tint\tcompr_size, fd, pos, ret;\n\tint\ti;\n\n\tprintf(\"unshk %s\\n\", name_str);\n\t// Handle .sdk inside a .zip\n\tif(dsk->raw_data) {\n\t\tunshk_dsk_raw_data(dsk);\n\t\treturn;\n\t}\n\n\t// File is not opened yet, try to open it\n\tfd = open(name_str, O_RDONLY | O_BINARY, 0x1b6);\n\tif(fd < 0) {\n\t\treturn;\n\t}\n\tcompr_size = (int)cfg_get_fd_size(fd);\n\tprintf(\"size: %d\\n\", compr_size);\n\n\tcptr = malloc(compr_size + 0x1000);\n\tpos = 0;\n\tfor(i = 0; i < 0x1000; i++) {\n\t\tcptr[compr_size + i] = 0;\n\t}\n\twhile(1) {\n\t\tif(pos >= compr_size) {\n\t\t\tbreak;\n\t\t}\n\t\tret = read(fd, cptr + pos, compr_size - pos);\n\t\tif(ret <= 0) {\n\t\t\tbreak;\n\t\t}\n\t\tpos += ret;\n\t}\n\tclose(fd);\n\n\tif(pos != compr_size) {\n\t\tcompr_size = 0;\t\t// Make header searching fail\n\t}\n\tunshk_parse_header(dsk, cptr, compr_size, cptr);\n\n\tfree(cptr);\n}\n\nvoid\nunshk_dsk_raw_data(Disk *dsk)\n{\n\tbyte\t*save_raw_data, *cptr;\n\tdword64\tsave_raw_dsize;\n\tint\tsave_fd, compr_size;\n\tint\ti;\n\t// This code handles the case of .sdk inside a .zip (for example).\n\t// Since unshk() code uses dsk->fd, dsk->raw_data, and dsk->raw_dsize\n\t//  to communicate success in unshk'ing the disk, we need to copy\n\t//  those, and restore them, if the unshk fails\n\tsave_fd = dsk->fd;\n\tsave_raw_data = dsk->raw_data;\n\tsave_raw_dsize = dsk->raw_dsize;\n\tif(save_raw_dsize >= (1ULL << 30)) {\n\t\treturn;\t\t\t// Too large\n\t}\n\n\tdsk->fd = -1;\n\tdsk->raw_data = 0;\n\tdsk->raw_dsize = 0;\n\n\tcompr_size = (int)save_raw_dsize;\n\tcptr = malloc(compr_size + 0x1000);\n\tfor(i = 0; i < 0x1000; i++) {\n\t\tcptr[compr_size + i] = 0;\n\t}\n\tfor(i = 0; i < compr_size; i++) {\n\t\tcptr[i] = save_raw_data[i];\n\t}\n\n\tunshk_parse_header(dsk, cptr, compr_size, cptr);\n\tfree(cptr);\n\n\tif(dsk->raw_data) {\n\t\t// Success, free the old raw data\n\t\tfree(save_raw_data);\n\t\treturn;\n\t}\n\tdsk->fd = save_fd;\n\tdsk->raw_data = save_raw_data;\n\tdsk->raw_dsize = save_raw_dsize;\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/vars",
    "content": "\nTARGET = kegsmac\nOBJECTS1 = macsnd_driver.o\nCCOPTS = -Wall -O2 -DMAC\nSUFFIX =\nNAME = kegsmac\n\nXOPTS =\n\n"
  },
  {
    "path": "upstream/kegs/src/vars_mac",
    "content": "\nTARGET = kegsmac\nOBJECTS1 = macsnd_driver.o\nCCOPTS = -Wall -O2 -DMAC\nSUFFIX =\nNAME = kegsmac\n\nXOPTS =\n\n"
  },
  {
    "path": "upstream/kegs/src/vars_mac_x",
    "content": "\nTARGET = xkegs\nOBJECTS1 = macsnd_driver.o xdriver.o\nCCOPTS = -O2 -DMAC -Wall -I/usr/X11/include\nSUFFIX =\nNAME = xkegs\n\nXOPTS =\nLDOPTS = -Wl,-framework,CoreAudio -Wl,-framework,CoreFoundation -Wl,-framework,AudioToolbox\n\n"
  },
  {
    "path": "upstream/kegs/src/vars_x86linux",
    "content": "\nTARGET = xkegs\nOBJECTS1 = pulseaudio_driver.o xdriver.o\nCCOPTS = -O2 -Wall -fomit-frame-pointer -DPULSE_AUDIO\nNAME = xkegs\nLD = $(CC)\nEXTRA_LIBS = -lXext -lpulse\nEXTRA_SPECIALS =\n\nXOPTS = -I/usr/X11R6/include\n\n"
  },
  {
    "path": "upstream/kegs/src/video.c",
    "content": "const char rcsid_video_c[] = \"@(#)$KmKId: video.c,v 1.213 2025-04-27 18:03:43+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2025 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n#include <time.h>\n\n#include \"defc.h\"\n\nextern int Verbose;\n\nword32 g_a2_filt_stat[200];\nint g_a2_line_left_edge[200];\nint g_a2_line_right_edge[200];\n\nbyte g_cur_border_colors[270];\n\nword32\tg_a2_screen_buffer_changed = (word32)-1;\nword32\tg_full_refresh_needed = (word32)-1;\n\nword32 g_cycs_in_40col = 0;\nword32 g_cycs_in_xredraw = 0;\nword32 g_refresh_bytes_xfer = 0;\n\nextern byte *g_slow_memory_ptr;\nextern int g_fatal_log;\n\nextern dword64 g_cur_dfcyc;\n\nextern int g_line_ref_amt;\n\nextern word32 g_c034_val;\nextern int g_config_control_panel;\nextern int g_halt_sim;\n\nword32 g_slow_mem_changed[SLOW_MEM_CH_SIZE];\nword32 g_slow_mem_ch2[SLOW_MEM_CH_SIZE];\n\nword32 g_a2font_bits[0x100][8];\n\nword32 g_superhires_scan_save[2][256];\n\nKimage g_mainwin_kimage = { 0 };\nKimage g_debugwin_kimage = { 0 };\nint g_debugwin_last_total = 0;\n\nextern int g_debug_lines_total;\n\nextern dword64 g_last_vbl_dfcyc;\nextern dword64 g_video_pixel_dcount;\n\ndword64\tg_video_dfcyc_check_input = 0;\nint\tg_video_act_margin_left = BASE_MARGIN_LEFT;\nint\tg_video_act_margin_right = BASE_MARGIN_RIGHT;\nint\tg_video_act_margin_top = BASE_MARGIN_TOP;\nint\tg_video_act_margin_bottom = BASE_MARGIN_BOTTOM;\nint\tg_video_act_width = X_A2_WINDOW_WIDTH;\nint\tg_video_act_height = X_A2_WINDOW_HEIGHT;\nint\tg_mainwin_width = X_A2_WINDOW_WIDTH;\nint\tg_mainwin_height = X_A2_WINDOW_HEIGHT + MAX_STATUS_LINES*16 + 2;\nint\tg_mainwin_xpos = 100;\nint\tg_mainwin_ypos = 300;\nint\tg_video_no_scale_window = 0;\n\nword32\tg_palette_change_cnt[2][16];\nint\tg_border_sides_refresh_needed = 1;\nint\tg_border_special_refresh_needed = 1;\nint\tg_border_line24_refresh_needed = 1;\nint\tg_status_refresh_needed = 1;\n\nint\tg_vbl_border_color = 0;\nint\tg_border_last_vbl_changes = 0;\nint\tg_border_reparse = 0;\n\nint\tg_use_dhr140 = 0;\nint\tg_use_bw_hires = 0;\n\nint\tg_vid_update_last_line = 0;\nint\tg_video_save_all_stat_pos = 0;\n\nint g_cur_a2_stat = ALL_STAT_TEXT | ALL_STAT_ANNUNC3 |\n\t\t\t\t\t(0xf << BIT_ALL_STAT_TEXT_COLOR);\n\nword32 g_palette_8to1624[2][256];\nword32 g_a2palette_1624[16];\n\nword32\tg_saved_line_palettes[2][200][8];\n\nword32 g_cycs_in_refresh_line = 0;\nword32 g_cycs_in_refresh_ximage = 0;\nword32 g_cycs_in_run_16ms = 0;\n\nint\tg_num_lines_superhires = 0;\nint\tg_num_lines_superhires640 = 0;\nint\tg_num_lines_prev_superhires = 0;\nint\tg_num_lines_prev_superhires640 = 0;\n\nint\tg_screen_redraw_skip_count = 0;\nint\tg_screen_redraw_skip_amt = -1;\n\nword32\tg_alpha_mask = 0;\nword32\tg_red_mask = 0xff;\nword32\tg_green_mask = 0xff;\nword32\tg_blue_mask = 0xff;\nint\tg_red_left_shift = 16;\nint\tg_green_left_shift = 8;\nint\tg_blue_left_shift = 0;\nint\tg_red_right_shift = 0;\nint\tg_green_right_shift = 0;\nint\tg_blue_right_shift = 0;\n\nint\tg_status_enable = 1;\nint\tg_status_enable_previous = 1;\nchar\tg_status_buf[MAX_STATUS_LINES][STATUS_LINE_LENGTH + 1];\nchar\t*g_status_ptrs[MAX_STATUS_LINES] = { 0 };\nword16\tg_pixels_widened[128];\n\nint\tg_video_scale_algorithm = 0;\n\nSTRUCT(Video_all_stat) {\n\tword32\tlines_since_vbl;\n\tword32\tcur_all_stat;\n};\n\n#define MAX_VIDEO_ALL_STAT\t((200*42) + 40)\nint g_video_all_stat_pos = 0;\nVideo_all_stat g_video_all_stat[MAX_VIDEO_ALL_STAT];\n\nSTRUCT(Video_filt_stat) {\n\tword32\tline_bytes;\n\tword32\tfilt_stat;\n};\n\n#define MAX_VIDEO_FILT_STAT\t10000\nint g_video_filt_stat_pos = 0;\nVideo_filt_stat g_video_filt_stat[MAX_VIDEO_FILT_STAT];\n\nint g_video_stat_old_pos = 0;\nVideo_filt_stat g_video_filt_stat_old[MAX_VIDEO_FILT_STAT];\n\n\nword16 g_dhires_convert[4096];\t/* look up { next4, this4, prev 4 } */\n\nconst byte g_dhires_colors_16[] = {\t\t// Convert dhires to lores color\n\t\t0x00,\t/* 0x0 black */\n\t\t0x02,\t/* 0x1 dark blue */\n\t\t0x04,\t/* 0x2 dark green */\n\t\t0x06,\t/* 0x3 medium blue */\n\t\t0x08,\t/* 0x4 brown */\n\t\t0x0a,\t/* 0x5 light gray */\n\t\t0x0c,\t/* 0x6 green */\n\t\t0x0e,\t/* 0x7 aquamarine */\n\t\t0x01,\t/* 0x8 deep red */\n\t\t0x03,\t/* 0x9 purple */\n\t\t0x05,\t/* 0xa dark gray */\n\t\t0x07,\t/* 0xb light blue */\n\t\t0x09,\t/* 0xc orange */\n\t\t0x0b,\t/* 0xd pink */\n\t\t0x0d,\t/* 0xe yellow */\n\t\t0x0f\t/* 0xf white */\n};\n\nconst int g_lores_colors[] = {\t\t// From IIgs Technote #63\n\t\t/* rgb */\n\t\t0x000,\t\t/* 0x0 black */\n\t\t0xd03,\t\t/* 0x1 deep red */\n\t\t0x009,\t\t/* 0x2 dark blue */\n\t\t0xd2d,\t\t/* 0x3 purple */\n\t\t0x072,\t\t/* 0x4 dark green */\n\t\t0x555,\t\t/* 0x5 dark gray */\n\t\t0x22f,\t\t/* 0x6 medium blue */\n\t\t0x6af,\t\t/* 0x7 light blue */\n\t\t0x850,\t\t/* 0x8 brown */\n\t\t0xf60,\t\t/* 0x9 orange */\n\t\t0xaaa,\t\t/* 0xa light gray */\n\t\t0xf98,\t\t/* 0xb pink */\n\t\t0x1d0,\t\t/* 0xc green */\n\t\t0xff0,\t\t/* 0xd yellow */\n\t\t0x4f9,\t\t/* 0xe aquamarine */\n\t\t0xfff\t\t/* 0xf white */\n};\n\nconst byte g_hires_lookup[64] = {\n// Indexed by { next_bit, this_bit, prev_bit, hibit, odd_byte, odd_col }.\n//  Return lores colors: 0, 3, 6, 9, 0xc, 0xf\n\t0x00,\t\t\t// 00,0000\t// black: this and next are 0\n\t0x00,\t\t\t// 00,0001\n\t0x00,\t\t\t// 00,0010\n\t0x00,\t\t\t// 00,0011\n\t0x00,\t\t\t// 00,0100\n\t0x00,\t\t\t// 00,0101\n\t0x00,\t\t\t// 00,0110\n\t0x00,\t\t\t// 00,0111\n\t0x00,\t\t\t// 00,1000\n\t0x00,\t\t\t// 00,1001\n\t0x00,\t\t\t// 00,1010\n\t0x00,\t\t\t// 00,1011\n\t0x00,\t\t\t// 00,1100\n\t0x00,\t\t\t// 00,1101\n\t0x00,\t\t\t// 00,1110\n\t0x00,\t\t\t// 00,1111\n\n\t0x03,\t\t\t// 01,0000\t// purple\n\t0x03,\t\t\t// 01,0001\t// purple\n\t0x0c,\t\t\t// 01,0010\t// green (odd column)\n\t0x0c,\t\t\t// 01,0011\t// green\n\t0x06,\t\t\t// 01,0100\t// blue\n\t0x06,\t\t\t// 01,0101\t// blue\n\t0x09,\t\t\t// 01,0110\t// orange\n\t0x09,\t\t\t// 01,0111\t// orange\n\t0x0f,\t\t\t// 01,1000\t// white: this and prev are 1\n\t0x0f,\t\t\t// 01,1001\n\t0x0f,\t\t\t// 01,1010\n\t0x0f,\t\t\t// 01,1011\n\t0x0f,\t\t\t// 01,1100\n\t0x0f,\t\t\t// 01,1101\n\t0x0f,\t\t\t// 01,1110\n\t0x0f,\t\t\t// 01,1111\n\n\t0x00,\t\t\t// 10,0000\t// black\n\t0x00,\t\t\t// 10,0001\t// black\n\t0x00,\t\t\t// 10,0010\t// black\n\t0x00,\t\t\t// 10,0011\t// black\n\t0x00,\t\t\t// 10,0100\t// black\n\t0x00,\t\t\t// 10,0101\t// black\n\t0x00,\t\t\t// 10,0110\t// black\n\t0x00,\t\t\t// 10,0111\t// black\n\t0x0c,\t\t\t// 10,1000\t// green\n\t0x0c,\t\t\t// 10,1001\t// green\n\t0x03,\t\t\t// 10,1010\t// purple\n\t0x03,\t\t\t// 10,1011\t// purple\n\t0x09,\t\t\t// 10,1100\t// orange\n\t0x09,\t\t\t// 10,1101\t// orange\n\t0x06,\t\t\t// 10,1110\t// blue\n\t0x06,\t\t\t// 10,1111\t// blue\n\n\t0x0f,\t\t\t// 11,0000\t// white\n\t0x0f,\t\t\t// 11,0001\n\t0x0f,\t\t\t// 11,0010\n\t0x0f,\t\t\t// 11,0011\n\t0x0f,\t\t\t// 11,0100\n\t0x0f,\t\t\t// 11,0101\n\t0x0f,\t\t\t// 11,0110\n\t0x0f,\t\t\t// 11,0111\n\t0x0f,\t\t\t// 11,1000\n\t0x0f,\t\t\t// 11,1001\n\t0x0f,\t\t\t// 11,1010\n\t0x0f,\t\t\t// 11,1011\n\t0x0f,\t\t\t// 11,1100\n\t0x0f,\t\t\t// 11,1101\n\t0x0f,\t\t\t// 11,1110\n\t0x0f\t\t\t// 11,1111\n};\n\nconst int g_screen_index[] = {\n\t\t0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380,\n\t\t0x028, 0x0a8, 0x128, 0x1a8, 0x228, 0x2a8, 0x328, 0x3a8,\n\t\t0x050, 0x0d0, 0x150, 0x1d0, 0x250, 0x2d0, 0x350, 0x3d0,\n\t\t0x078, 0x0f8, 0x178, 0x1f8, 0x278, 0x2f8, 0x378, 0x3f8\n\t\t\t// Last row is for float_bus() during VBL\n};\n\nbyte g_font_array[256][8] = {\n#include \"kegsfont.h\"\n};\n\nvoid\nvideo_set_red_mask(word32 red_mask)\n{\n\tvideo_set_mask_and_shift(red_mask, &g_red_mask, &g_red_left_shift,\n\t\t\t\t\t\t\t&g_red_right_shift);\n}\n\nvoid\nvideo_set_green_mask(word32 green_mask)\n{\n\tvideo_set_mask_and_shift(green_mask, &g_green_mask, &g_green_left_shift,\n\t\t\t\t\t\t\t&g_green_right_shift);\n}\n\nvoid\nvideo_set_blue_mask(word32 blue_mask)\n{\n\tvideo_set_mask_and_shift(blue_mask, &g_blue_mask, &g_blue_left_shift,\n\t\t\t\t\t\t\t&g_blue_right_shift);\n}\n\nvoid\nvideo_set_alpha_mask(word32 alpha_mask)\n{\n\tg_alpha_mask = alpha_mask;\n\tprintf(\"Set g_alpha_mask=%08x\\n\", alpha_mask);\n}\n\nvoid\nvideo_set_mask_and_shift(word32 x_mask, word32 *mask_ptr, int *shift_left_ptr,\n\t\t\t\t\t\tint *shift_right_ptr)\n{\n\tint\tshift;\n\tint\ti;\n\n\t/* Shift until we find first set bit in mask, then remember mask,shift*/\n\n\tshift = 0;\n\tfor(i = 0; i < 32; i++) {\n\t\tif(x_mask & 1) {\n\t\t\t/* we're done! */\n\t\t\tbreak;\n\t\t}\n\t\tx_mask = x_mask >> 1;\n\t\tshift++;\n\t}\n\t*mask_ptr = x_mask;\n\t*shift_left_ptr = shift;\n\t/* Now, calculate shift_right_ptr */\n\tshift = 0;\n\tx_mask |= 1;\t\t// make sure at least one bit is set\n\tfor(i = 0; i < 32; i++) {\n\t\tif(x_mask >= 0x80) {\n\t\t\tbreak;\n\t\t}\n\t\tshift++;\n\t\tx_mask = x_mask << 1;\n\t}\n\n\t*shift_right_ptr = shift;\n}\n\nvoid\nvideo_set_palette()\n{\n\tint\ti;\n\n\tfor(i = 0; i < 16; i++) {\n\t\tvideo_update_color_raw(0, i, g_lores_colors[i]);\n\t\tg_a2palette_1624[i] = g_palette_8to1624[0][i];\n\t}\n}\n\nvoid\nvideo_set_redraw_skip_amt(int amt)\n{\n\tif(g_screen_redraw_skip_amt < amt) {\n\t\tg_screen_redraw_skip_amt = amt;\n\t\tprintf(\"Set g_screen_redraw_skip_amt = %d\\n\", amt);\n\t}\n}\n\nKimage *\nvideo_get_kimage(int win_id)\n{\n\tif(win_id == 0) {\n\t\treturn &g_mainwin_kimage;\n\t}\n\tif(win_id == 1) {\n\t\treturn &g_debugwin_kimage;\n\t}\n\tprintf(\"win_id: %d not supported\\n\", win_id);\n\texit(1);\n}\n\nchar *\nvideo_get_status_ptr(int line)\n{\n\tif(line < MAX_STATUS_LINES) {\n\t\treturn g_status_ptrs[line];\n\t}\n\treturn 0;\n}\n\n#if 0\nint\nvideo_get_x_refresh_needed(Kimage *kimage_ptr)\n{\n\tint\tret;\n\n\tret = kimage_ptr->x_refresh_needed;\n\tkimage_ptr->x_refresh_needed = 0;\n\n\treturn ret;\n}\n#endif\n\nvoid\nvideo_set_x_refresh_needed(Kimage *kimage_ptr, int do_refresh)\n{\n\tkimage_ptr->x_refresh_needed = do_refresh;\n}\n\nint\nvideo_get_active(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->active;\n}\n\nvoid\nvideo_set_active(Kimage *kimage_ptr, int active)\n{\n\tkimage_ptr->active = active;\n\tif(kimage_ptr != &g_mainwin_kimage) {\n\t\tadb_nonmain_check();\n\t}\n}\n\nvoid\nvideo_init(int mdepth, int screen_width, int screen_height, int no_scale_window)\n{\n\tword32\tcol[4];\n\tword32\tval0, val1, val2, val3, next_col, next2_col;\n\tword32\tval, cur_col;\n\tint\ti, j;\n\n// Initialize video system (called one-time only)\n\n\tg_video_no_scale_window = no_scale_window;\n\n\tfor(i = 0; i < 200; i++) {\n\t\tg_a2_line_left_edge[i] = 0;\n\t\tg_a2_line_right_edge[i] = 0;\n\t}\n\tfor(i = 0; i < 200; i++) {\n\t\tg_a2_filt_stat[i] = -1;\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\tg_saved_line_palettes[0][i][j] = (word32)-1;\n\t\t\tg_saved_line_palettes[1][i][j] = (word32)-1;\n\t\t}\n\t}\n\tfor(i = 0; i < 262; i++) {\n\t\tg_cur_border_colors[i] = -1;\n\t}\n\tfor(i = 0; i < 128; i++) {\n\t\tval0 = i;\n\t\tval1 = 0;\n\t\tfor(j = 0; j < 7; j++) {\n\t\t\tval1 = val1 << 2;\n\t\t\tif(val0 & 0x40) {\n\t\t\t\tval1 |= 3;\n\t\t\t}\n\t\t\tval0 = val0 << 1;\n\t\t}\n\t\tg_pixels_widened[i] = val1;\n\t}\n\n\tvid_printf(\"Zeroing out video memory, mdepth:%d\\n\", mdepth);\n\n\tfor(i = 0; i < SLOW_MEM_CH_SIZE; i++) {\n\t\tg_slow_mem_changed[i] = (word32)-1;\n\t\tg_slow_mem_ch2[i] = 0;\n\t}\n\n\t// create g_dhires_convert[] array\n\t// TODO: Look at patent #4786893 for details on VGC and dhr\n\tfor(i = 0; i < 4096; i++) {\n\t\t/* Convert index bits 11:0 where 3:0 is the previous color */\n\t\t/*  and 7:4 is the current color to translate */\n\t\t/*  Bit 4 will be the first pixel displayed on the screen */\n\t\tfor(j = 0; j < 4; j++) {\n\t\t\tcur_col = (i >> (1 + j)) & 0xf;\n\t\t\tnext_col = (i >> (2 + j)) & 0xf;\n\t\t\tnext2_col = (i >> (3 + j)) & 0xf;\n\t\t\tcur_col = (((cur_col << 4) + cur_col) >> (3 - j)) & 0xf;\n\n\t\t\tif((cur_col == 0xf) || (next_col == 0xf) ||\n\t\t\t\t\t\t\t(next2_col == 0xf)) {\n\t\t\t\tcur_col = 0xf;\n\t\t\t\tcol[j] = cur_col;\n\t\t\t} else if((cur_col == 0) || (next_col == 0) ||\n\t\t\t\t\t\t(next2_col == 0)) {\n\t\t\t\tcur_col = 0;\n\t\t\t\tcol[j] = cur_col;\n\t\t\t} else {\n\t\t\t\tcol[j] = cur_col;\n\t\t\t}\n\t\t}\n\t\tif(g_use_dhr140) {\n\t\t\tfor(j = 0; j < 4; j++) {\n\t\t\t\tcol[j] = (i >> 4) & 0xf;\n\t\t\t}\n\t\t}\n\t\tval0 = g_dhires_colors_16[col[0] & 0xf];\n\t\tval1 = g_dhires_colors_16[col[1] & 0xf];\n\t\tval2 = g_dhires_colors_16[col[2] & 0xf];\n\t\tval3 = g_dhires_colors_16[col[3] & 0xf];\n\t\tval = val0 | (val1 << 4) | (val2 << 8) | (val3 << 12);\n\t\tg_dhires_convert[i] = val;\n\t\tif((i == 0x7bc) || (i == 0xfff)) {\n\t\t\t//printf(\"g_dhires_convert[%03x] = %04x\\n\", i, val);\n\t\t}\n\t}\n\n\tvideo_init_kimage(&g_mainwin_kimage, X_A2_WINDOW_WIDTH,\n\t\t\t\tX_A2_WINDOW_HEIGHT + MAX_STATUS_LINES*16 + 2,\n\t\t\t\tscreen_width, screen_height);\n\n\tvideo_init_kimage(&g_debugwin_kimage, 80*8 + 8 + 8, 25*16 + 8 + 8,\n\t\t\t\tscreen_width, screen_height);\n\n\tchange_display_mode(g_cur_dfcyc);\n\tvideo_reset();\n\tg_vid_update_last_line = 0;\n\tg_video_all_stat_pos = 1;\n\tg_video_all_stat[0].cur_all_stat = 0;\n\tg_video_all_stat[0].lines_since_vbl = 0;\n\tg_video_save_all_stat_pos = 0;\n\tg_video_filt_stat_pos = 0;\n\tvideo_update_status_enable(&g_mainwin_kimage);\n\tvideo_update_through_line(262);\n\n\tprintf(\"g_mainwin_kimage created and init'ed\\n\");\n\tfflush(stdout);\n}\n\nint\nvideo_clamp(int value, int min, int max)\n{\n\t// Ensure value is >= min and <= max.  If max <= min, return min\n\tif(value > max) {\n\t\tvalue = max;\n\t}\n\tif(value <= min) {\n\t\tvalue = min;\n\t}\n\treturn value;\n}\n\nvoid\nvideo_init_kimage(Kimage *kimage_ptr, int width, int height,\n\t\t\tint screen_width, int screen_height)\n{\n\tint\tx_width, x_height, a2_height, x_xpos, x_ypos;\n\tint\ti;\n\n\tif(screen_width < width) {\n\t\tscreen_width = width;\n\t}\n\tif(screen_height < height) {\n\t\tscreen_width = height;\n\t}\n\tx_width = width;\n\tx_height = height;\n\ta2_height = height;\n\tx_xpos = 100;\n\tx_ypos = 300;\n\tif(kimage_ptr == &g_mainwin_kimage) {\n\t\tx_width = g_mainwin_width;\n\t\tx_height = g_mainwin_height;\n\t\tx_xpos = g_mainwin_xpos;\n\t\tx_ypos = g_mainwin_ypos;\n\n\t\t// Handle status lines now\n\t\tif(!g_status_enable) {\n\t\t\ta2_height = g_video_act_margin_top + A2_WINDOW_HEIGHT +\n\t\t\t\t\t\tg_video_act_margin_bottom;\n\t\t}\n\t}\n\n\tx_width = video_clamp(x_width, width, screen_width);\n\tx_height = video_clamp(x_height, height, screen_height);\n\tx_xpos = video_clamp(x_xpos, 0, screen_width - 640);\n\tx_ypos = video_clamp(x_ypos, 0, screen_height - 420);\n\n\tkimage_ptr->wptr = (word32 *)calloc(1, width * (height + 2) * 4);\n\t\t// Scaling routines read from line+1, expect it to be 0\n\tkimage_ptr->a2_width_full = width;\n\tkimage_ptr->a2_height_full = height;\n\tkimage_ptr->a2_width = width;\n\tkimage_ptr->a2_height = a2_height;\n\tkimage_ptr->x_width = x_width;\n\tkimage_ptr->x_height = x_height;\n\tkimage_ptr->x_refresh_needed = 1;\n\tkimage_ptr->x_max_width = screen_width;\n\tkimage_ptr->x_max_height = screen_height;\n\tkimage_ptr->x_xpos = x_xpos;\n\tkimage_ptr->x_ypos = x_ypos;\n\tkimage_ptr->active = 0;\n\tkimage_ptr->vbl_of_last_resize = 0;\n\tkimage_ptr->c025_val = 0;\n\t//printf(\"Created window, width:%d x_width:%d height:%d x_height:%d\\n\",\n\t//\twidth, x_width, height, x_height);\n\n\tkimage_ptr->scale_width_to_a2 = 0x10000;\n\tkimage_ptr->scale_width_a2_to_x = 0x10000;\n\tkimage_ptr->scale_height_to_a2 = 0x10000;\n\tkimage_ptr->scale_height_a2_to_x = 0x10000;\n\tkimage_ptr->num_change_rects = 0;\n\tfor(i = 0; i <= MAX_SCALE_SIZE; i++) {\n\t\tkimage_ptr->scale_width[i] = i;\n\t\tkimage_ptr->scale_height[i] = i;\n\t}\n\n\tvideo_update_scale(kimage_ptr, x_width, x_height, 1);\n}\n\nvoid\nshow_a2_line_stuff()\n{\n\tint\tnum, num_filt;\n\tint\ti;\n\n\tfor(i = 0; i < 200; i++) {\n\t\tprintf(\"line: %d: stat: %07x, \"\n\t\t\t\"left_edge:%d, right_edge:%d\\n\",\n\t\t\ti, g_a2_filt_stat[i],\n\t\t\tg_a2_line_left_edge[i],\n\t\t\tg_a2_line_right_edge[i]);\n\t}\n\n\tnum = g_video_all_stat_pos;\n\tnum_filt = g_video_stat_old_pos;\n\tprintf(\"cur_a2_stat:%04x, all_stat_pos:%d, num_filt:%d\\n\",\n\t\t\tg_cur_a2_stat, num, num_filt);\n\tfor(i = 0; i < num; i++) {\n\t\tprintf(\"all_stat[%3d]=%08x stat:%08x\\n\", i,\n\t\t\tg_video_all_stat[i].lines_since_vbl,\n\t\t\tg_video_all_stat[i].cur_all_stat);\n\t}\n\tfor(i = 0; i < num_filt; i++) {\n\t\tprintf(\"filt[%3d]=%08x filt_stat:%08x\\n\", i,\n\t\t\tg_video_filt_stat_old[i].line_bytes,\n\t\t\tg_video_filt_stat_old[i].filt_stat);\n\t}\n}\n\nint\tg_flash_count = 0;\n\nvoid\nvideo_reset()\n{\n\tint\tstat;\n\tint\ti;\n\n\tvoc_reset();\n\n\tstat = ALL_STAT_TEXT | ALL_STAT_ANNUNC3 |\n\t\t\t\t\t(0xf << BIT_ALL_STAT_TEXT_COLOR);\n\tif(g_use_bw_hires) {\n\t\tstat |= ALL_STAT_COLOR_C021;\n\t}\n\tif(g_config_control_panel) {\n\t\t/* Don't update cur_a2_stat when in configuration panel */\n\t\t//g_save_cur_a2_stat = stat;\n\t} else {\n\t\tg_cur_a2_stat = stat;\n\t}\n\n\tfor(i = 0; i < 16; i++) {\n\t\tg_palette_change_cnt[0][i] = 0;\n\t\tg_palette_change_cnt[1][i] = 0;\n\t}\n}\n\nword32\tg_cycs_in_check_input = 0;\n\nvoid\nvideo_update()\n{\n\tint\tdid_video;\n\n\tif(g_fatal_log > 0) {\n\t\t// NOT IMPLEMENTED YET\n\t\t//adb_all_keys_up();\n\t\tclear_fatal_logs();\n\t}\n\tif(g_status_enable != g_status_enable_previous) {\n\t\tg_status_enable_previous = g_status_enable;\n\t\tvideo_update_status_enable(&g_mainwin_kimage);\n\t}\n\n\tdebugger_redraw_screen(&g_debugwin_kimage);\n\tif(g_config_control_panel) {\n\t\treturn;\t\t// Nothing else to do\n\t}\n\n\tg_screen_redraw_skip_count--;\n\tdid_video = 0;\n\tif(g_screen_redraw_skip_count < 0) {\n\t\tdid_video = 1;\n\t\tvideo_copy_changed2();\n\t\tvideo_update_event_line(262);\n\t\tupdate_border_info();\n\t\tg_screen_redraw_skip_count = g_screen_redraw_skip_amt;\n\t}\n\n\t/* update flash */\n\tg_flash_count++;\n\tif(g_flash_count >= 16) {\n\t\tg_flash_count = 0;\n\t\tg_cur_a2_stat ^= ALL_STAT_FLASH_STATE;\n\t\tchange_display_mode(g_cur_dfcyc);\n\t}\n\n\tif(did_video) {\n\t\tg_vid_update_last_line = 0;\n\t\tg_video_all_stat_pos = 1;\n\t\tg_video_all_stat[0].cur_all_stat = g_cur_a2_stat;\n\t\tg_video_all_stat[0].lines_since_vbl = 0;\n\t\tg_video_save_all_stat_pos = 0;\n\t\tg_video_filt_stat_pos = 0;\n\t}\n}\n\nword32\nvideo_all_stat_to_filt_stat(int line, word32 new_all_stat)\n{\n\tword32\tfilt_stat, merge_mask, mix_t_gr;\n\n\tfilt_stat = new_all_stat & ALL_STAT_TEXT;\n\tmerge_mask = 0;\n\tif((new_all_stat & ALL_STAT_ST80) == 0) {\n\t\tmerge_mask = ALL_STAT_PAGE2;\n\t}\n\tmix_t_gr = new_all_stat & ALL_STAT_MIX_T_GR;\n\tif(new_all_stat & ALL_STAT_SUPER_HIRES) {\n\t\tfilt_stat = ALL_STAT_SUPER_HIRES;\n\t\tmerge_mask = ALL_STAT_VOC_INTERLACE | ALL_STAT_VOC_MAIN;\n\t} else if(line >= 192) {\n\t\tfilt_stat = ALL_STAT_BORDER;\n\t} else if(filt_stat || (line >= 160 && mix_t_gr)) {\n\t\t// text mode\n\t\tfilt_stat |= ALL_STAT_TEXT;\n\t\tmerge_mask |= ALL_STAT_ALTCHARSET | ALL_STAT_BG_COLOR |\n\t\t\t\t\tALL_STAT_TEXT_COLOR | ALL_STAT_VID80;\n\t\tif((new_all_stat & ALL_STAT_ALTCHARSET) == 0) {\n\t\t\tmerge_mask |= ALL_STAT_FLASH_STATE;\n\t\t}\n\t} else {\n\t\t// GR or Hires\n\t\tmerge_mask |= ALL_STAT_ANNUNC3 | ALL_STAT_HIRES;\n\t\tif((new_all_stat & ALL_STAT_ANNUNC3) == 0) {\n\t\t\t// AN3 must be 0 to enable dbl-lores or dbl-hires\n\t\t\tmerge_mask |= ALL_STAT_VID80;\n\t\t}\n\t\tif(new_all_stat & ALL_STAT_HIRES) {\n\t\t\tmerge_mask |= ALL_STAT_COLOR_C021 |\n\t\t\t\t\t\tALL_STAT_DIS_COLOR_DHIRES;\n\t\t}\n\t}\n\tfilt_stat = filt_stat | (new_all_stat & merge_mask);\n\treturn filt_stat;\n}\n\nvoid\nchange_display_mode(dword64 dfcyc)\n{\n\tword32\tlines_since_vbl;\n\n\tlines_since_vbl = get_lines_since_vbl(dfcyc);\n\tvideo_add_new_all_stat(dfcyc, lines_since_vbl);\n\n}\n\nvoid\nvideo_add_new_all_stat(dword64 dfcyc, word32 lines_since_vbl)\n{\n\tword32\tmy_start, first_start, prev_lines_since_vbl;\n\tint\tpos, prev;\n\n\tpos = g_video_all_stat_pos;\n\tmy_start = lines_since_vbl & 0x1ff00;\n\tfirst_start = my_start + 24;\n\tif(lines_since_vbl >= (200 << 8)) {\n\t\treturn;\t\t\t// In VBL, don't log this\n\t}\n\tif(pos && (lines_since_vbl < first_start)) {\n\t\tprev = pos - 1;\n\t\tprev_lines_since_vbl = g_video_all_stat[prev].lines_since_vbl;\n\t\t// If the previous toggle has the same line, and it is before\n\t\t//  offset 24, then ignore it and overwrite it\n\t\tif((my_start <= prev_lines_since_vbl) &&\n\t\t\t\t\t(prev_lines_since_vbl < first_start)) {\n\t\t\t// needless toggling during HBL, just toss earlier\n\t\t\tpos = prev;\n\t\t}\n\t}\n\tg_video_all_stat[pos].lines_since_vbl = lines_since_vbl;\n\tg_video_all_stat[pos].cur_all_stat = g_cur_a2_stat;\n\tif(!g_halt_sim || g_config_control_panel) {\n\t\tdbg_log_info(dfcyc, g_cur_a2_stat, lines_since_vbl,\n\t\t\t\t\t\t\t(pos << 16) | 0x102);\n\t}\n\tpos++;\n\tif(pos >= MAX_VIDEO_ALL_STAT) {\n\t\tpos--;\n\t}\n\tg_video_all_stat_pos = pos;\n}\n\n#define MAX_BORDER_CHANGES\t16384\n\nSTRUCT(Border_changes) {\n\tword32\tusec;\n\tint\tval;\n};\n\nBorder_changes g_border_changes[MAX_BORDER_CHANGES];\nint\tg_num_border_changes = 0;\n\nvoid\nchange_border_color(dword64 dfcyc, int val)\n{\n\tint\tpos;\n\n\tpos = g_num_border_changes;\n\tg_border_changes[pos].usec = (word32)((dfcyc - g_last_vbl_dfcyc) >> 16);\n\tg_border_changes[pos].val = val;\n\n\tpos++;\n\tg_num_border_changes = pos;\n\n\tif(pos >= MAX_BORDER_CHANGES) {\n\t\thalt_printf(\"num border changes: %d\\n\", pos);\n\t\tg_num_border_changes = 0;\n\t}\n}\n\nvoid\nupdate_border_info()\n{\n\tdword64\tdrecip_usec, dline;\n\tword32\tusec;\n\tint\toffset, new_line_offset, last_line_offset, new_line, new_val;\n\tint\tlimit, color_now;\n\tint\ti;\n\n\t/* to get this routine to redraw the border, change */\n\t/*  g_vbl_border_color, set g_border_last_vbl_changes = 1 */\n\t/*  and change the cur_border_colors[] array */\n\n\tcolor_now = g_vbl_border_color;\n\n\tdrecip_usec = (65536LL * 65536LL) / 65;\n\tlimit = g_num_border_changes;\n\tif(g_border_last_vbl_changes || limit || g_border_reparse) {\n\t\t/* add a dummy entry */\n\t\tg_border_changes[limit].usec = CYCLES_IN_16MS_RAW + 21;\n\t\tg_border_changes[limit].val = (g_c034_val & 0xf);\n\t\tlimit++;\n\t}\n\tlast_line_offset = (((word32)-1L) << 8) + 44;\n\tfor(i = 0; i < limit; i++) {\n\t\tusec = g_border_changes[i].usec;\n\t\tdline = usec * drecip_usec;\n\t\tnew_line = dline >> 32;\n\t\toffset = ((dword64)(word32)dline * 65ULL) >> 32;\n\n\t\t/* here comes the tricky part */\n\t\t/* offset is from 0 to 65, where 0-3 is the right border of */\n\t\t/*  the previous line, 4-20 is horiz blanking, 21-24 is the */\n\t\t/*  left border and 25-64 is the main window */\n\t\t/* Convert this to a new notation which is 0-3 is the left */\n\t\t/*  border, 4-43 is the main window, and 44-47 is the right */\n\t\t/* basically, add -21 to offset, and wrap < 0 to previous ln */\n\t\t/* note this makes line -1 offset 44-47 the left hand border */\n\t\t/* for true line 261 on the screen */\n\t\toffset -= 21;\n\t\tif(offset < 0) {\n\t\t\tnew_line--;\n\t\t\toffset += 64;\n\t\t}\n\t\tnew_val = g_border_changes[i].val;\n\t\tnew_line_offset = (new_line << 8) + offset;\n\n\t\tif((new_line_offset < -256) ||\n\t\t\t\t\t(new_line_offset > (262*256 + 0x80))) {\n\t\t\tprintf(\"new_line_offset: %05x\\n\", new_line_offset);\n\t\t\tnew_line_offset = last_line_offset;\n\t\t}\n\t\twhile(last_line_offset < new_line_offset) {\n\t\t\t/* see if this will finish it */\n\t\t\tif((last_line_offset & -256)==(new_line_offset & -256)){\n\t\t\t\tupdate_border_line(last_line_offset,\n\t\t\t\t\t\tnew_line_offset, color_now);\n\t\t\t\tlast_line_offset = new_line_offset;\n\t\t\t} else {\n\t\t\t\tupdate_border_line(last_line_offset,\n\t\t\t\t\t\t(last_line_offset & -256) + 65,\n\t\t\t\t\t\tcolor_now);\n\t\t\t\tlast_line_offset =(last_line_offset & -256)+256;\n\t\t\t}\n\t\t}\n\n\t\tcolor_now = new_val;\n\t}\n\n#if 0\n\tif(g_num_border_changes) {\n\t\tprintf(\"Border changes: %d\\n\", g_num_border_changes);\n\t}\n#endif\n\n\tif(limit > 1) {\n\t\tg_border_last_vbl_changes = 1;\n\t} else {\n\t\tg_border_last_vbl_changes = 0;\n\t}\n\n\tg_num_border_changes = 0;\n\tg_border_reparse = 0;\n\tg_vbl_border_color = (g_c034_val & 0xf);\n}\n\nvoid\nupdate_border_line(int st_line_offset, int end_line_offset, int color)\n{\n\tword32\tfilt_stat;\n\tint\tst_offset, end_offset, left, right, width, line;\n\n\tline = st_line_offset >> 8;\n\tif(line != (end_line_offset >> 8)) {\n\t\thalt_printf(\"ubl, %04x %04x %02x!\\n\", st_line_offset,\n\t\t\t\t\tend_line_offset, color);\n\t}\n\tif(line < -1 || line >= 262) {\n\t\thalt_printf(\"ubl-b, mod line is %d\\n\", line);\n\t\tline = 0;\n\t}\n\tif(line < 0 || line >= 262) {\n\t\tline = 0;\n\t}\n\n\tst_offset = st_line_offset & 0xff;\n\tend_offset = end_line_offset & 0xff;\n\n\tif((st_offset == 0) && (end_offset >= 0x41) && !g_border_reparse) {\n\t\t/* might be the same as last time, save some work */\n\t\tif(g_cur_border_colors[line] == color) {\n\t\t\treturn;\n\t\t}\n\t\tg_cur_border_colors[line] = color;\n\t} else {\n\t\tg_cur_border_colors[line] = -1;\n\t}\n\n\t/* 0-3: left border, 4-43: main window, 44-47: right border */\n\t/* 48-65: horiz blanking */\n\t/* first, do the sides from line 0 to line 199 */\n\tif((line < 200) || (line >= 262)) {\n\t\tif(line >= 262) {\n\t\t\tline = 0;\n\t\t}\n\t\tif(st_offset < 4) {\n\t\t\t/* left side */\n\t\t\tleft = st_offset;\n\t\t\tright = MY_MIN(4, end_offset);\n\t\t\tvideo_border_pixel_write(&g_mainwin_kimage,\n\t\t\t\tg_video_act_margin_top + 2*line, 2, color,\n\t\t\t\t(left * BORDER_WIDTH)/4,\n\t\t\t\t(right * BORDER_WIDTH) / 4);\n\n\t\t\tg_border_sides_refresh_needed = 1;\n\t\t}\n\t\tif((st_offset < 48) && (end_offset >= 44)) {\n\t\t\t/* right side */\n\t\t\tfilt_stat = g_a2_filt_stat[line];\n\t\t\twidth = BORDER_WIDTH;\n\t\t\tif((filt_stat & ALL_STAT_SUPER_HIRES) == 0) {\n\t\t\t\twidth += 80;\n\t\t\t}\n\t\t\tleft = MY_MAX(0, st_offset - 44);\n\t\t\tright = MY_MIN(4, end_offset - 44);\n\t\t\tvideo_border_pixel_write(&g_mainwin_kimage,\n\t\t\t\tg_video_act_margin_top + 2*line, 2, color,\n\t\t\t\tX_A2_WINDOW_WIDTH - width +\n\t\t\t\t\t\t(left * width/4),\n\t\t\t\tX_A2_WINDOW_WIDTH - width +\n\t\t\t\t\t\t(right * width/4));\n\t\t\tg_border_sides_refresh_needed = 1;\n\t\t}\n\t}\n\n\tif((line >= 192) && (line < 200)) {\n\t\tfilt_stat = g_a2_filt_stat[line];\n\t\tif((filt_stat & ALL_STAT_BORDER) && (st_offset < 44) &&\n\t\t\t\t\t\t\t(end_offset > 4)) {\n\t\t\tleft = MY_MAX(0, st_offset - 4);\n\t\t\tright = MY_MIN(40, end_offset - 4);\n\t\t\tvideo_border_pixel_write(&g_mainwin_kimage,\n\t\t\t\tg_video_act_margin_top + 2*line, 2, color,\n\t\t\t\tg_video_act_margin_left + (left * 640 / 40),\n\t\t\t\tg_video_act_margin_left + (right * 640 / 40));\n\t\t\tg_border_line24_refresh_needed = 1;\n\t\t}\n\t}\n\n\t/* now do the bottom, lines 200 to 215 */\n\tif((line >= 200) && (line < (200 + BASE_MARGIN_BOTTOM/2)) ) {\n\t\tline -= 200;\n\t\tleft = st_offset;\n\t\tright = MY_MIN(48, end_offset);\n\t\tvideo_border_pixel_write(&g_mainwin_kimage,\n\t\t\tg_video_act_margin_top + 200*2 + 2*line, 2,\n\t\t\tcolor,\n\t\t\t(left * X_A2_WINDOW_WIDTH / 48),\n\t\t\t(right * X_A2_WINDOW_WIDTH / 48));\n\t\tg_border_special_refresh_needed = 1;\n\t}\n\n\t/* and top, lines 236 to 262 */\n\tif((line >= (262 - BASE_MARGIN_TOP/2)) && (line < 262)) {\n\t\tline -= (262 - BASE_MARGIN_TOP/2);\n\t\tleft = st_offset;\n\t\tright = MY_MIN(48, end_offset);\n\t\tvideo_border_pixel_write(&g_mainwin_kimage, 2*line, 2, color,\n\t\t\t(left * X_A2_WINDOW_WIDTH / 48),\n\t\t\t(right * X_A2_WINDOW_WIDTH / 48));\n\t\tg_border_special_refresh_needed = 1;\n\t}\n}\n\nvoid\nvideo_border_pixel_write(Kimage *kimage_ptr, int starty, int num_lines,\n\t\t\tint color, int st_off, int end_off)\n{\n\tword32\t*wptr, *wptr0;\n\tword32\tpixel;\n\tint\twidth, width_full, offset;\n\tint\ti, j;\n\n\tif(end_off <= st_off) {\n\t\treturn;\n\t}\n\n\twidth = end_off - st_off;\n\twidth_full = kimage_ptr->a2_width_full;\n\n\tif(width > width_full) {\n\t\thalt_printf(\"border write but width %d > act %d\\n\", width,\n\t\t\t\twidth_full);\n\t\treturn;\n\t}\n\tif((starty + num_lines) > kimage_ptr->a2_height) {\n\t\thalt_printf(\"border write line %d, > act %d\\n\",\n\t\t\t\tstarty+num_lines, kimage_ptr->a2_height);\n\t\treturn;\n\t}\n\tpixel = g_a2palette_1624[color & 0xf];\n\n\toffset = starty * width_full;\n\twptr0 = kimage_ptr->wptr;\n\twptr0 += offset;\n\tfor(i = 0; i < num_lines; i++) {\n\t\twptr = wptr0 + st_off;\n\t\tfor(j = 0; j < width; j++) {\n\t\t\t*wptr++ = pixel;\n\t\t}\n\t\twptr0 += width_full;\n\t}\n}\n\nword32\nvideo_get_ch_mask(word32 mem_ptr, word32 filt_stat, int reparse)\n{\n\tword32\tch_mask, mask;\n\tint\tshift;\n\n\tif(reparse) {\n\t\treturn (word32)-1;\n\t}\n\tshift = (mem_ptr >> SHIFT_PER_CHANGE) & 0x1f;\n\tmask = (1 << (40 >> SHIFT_PER_CHANGE)) - 1;\n\tch_mask = g_slow_mem_changed[mem_ptr >> CHANGE_SHIFT] |\n\t\t\tg_slow_mem_ch2[mem_ptr >> CHANGE_SHIFT];\n\tif(filt_stat & ALL_STAT_VID80) {\n\t\tmem_ptr += 0x10000;\n\t\tch_mask |= (g_slow_mem_changed[mem_ptr >> CHANGE_SHIFT]);\n\t\tch_mask |= (g_slow_mem_ch2[mem_ptr >> CHANGE_SHIFT]);\n\t}\n\tch_mask = (ch_mask >> shift) & mask;\n\n\treturn ch_mask;\n}\n\nvoid\nvideo_update_edges(int line, int left, int right, const char *str)\n{\n\tg_a2_line_left_edge[line] = MY_MIN(left, g_a2_line_left_edge[line]);\n\tg_a2_line_right_edge[line] = MY_MAX(right, g_a2_line_right_edge[line]);\n\n\tif((left < 0) || (right < 0) || (left > 640) || (right > 640)) {\n\t\tprintf(\"video_update_edges: %s: line %d: %d (left) >= %d \"\n\t\t\t\"(right)\\n\", str, line, left, right);\n\t}\n}\n\nvoid\nredraw_changed_text(word32 line_bytes, int reparse, word32 *in_wptr,\n\t\t\t\tint pixels_per_line, word32 filt_stat)\n{\n\tbyte\tstr_buf[81];\n\tbyte\t*slow_mem_ptr;\n\tword32\tch_mask, line_mask, mem_ptr, val0, val1, bg_pixel, fg_pixel;\n\tint\tflash_state, y, bg_color, fg_color, start_line;\n\tint\tx1, x2;\n\n\t// Redraws a single line, will be called over 8 lines to finish a byte.\n\n\tstart_line = line_bytes >> 16;\n\tbg_color = (filt_stat >> BIT_ALL_STAT_BG_COLOR) & 0xf;\n\tfg_color = (filt_stat >> BIT_ALL_STAT_TEXT_COLOR) & 0xf;\n\tbg_pixel = g_a2palette_1624[bg_color];\n\tfg_pixel = g_a2palette_1624[fg_color];\n\n\ty = start_line >> 3;\n\tline_mask = 1 << y;\n\tmem_ptr = 0x400 + g_screen_index[y];\n\tif(filt_stat & ALL_STAT_PAGE2) {\n\t\tmem_ptr += 0x400;\n\t}\n\tif((mem_ptr < 0x400) || (mem_ptr >= 0xc00)) {\n\t\thalt_printf(\"redraw_changed_text: mem_ptr: %08x, y:%d\\n\",\n\t\t\t\t\t\t\t\tmem_ptr, y);\n\t\treturn;\n\t}\n\n\tch_mask = video_get_ch_mask(mem_ptr, filt_stat, reparse);\n\tif(!ch_mask) {\n\t\treturn;\n\t}\n\n\tg_a2_screen_buffer_changed |= line_mask;\n\tx2 = 0;\n\tslow_mem_ptr = &(g_slow_memory_ptr[mem_ptr]);\n\tflash_state = -0x40;\n\tif(g_cur_a2_stat & ALL_STAT_FLASH_STATE) {\n\t\tflash_state = 0x40;\n\t}\n\n\tfor(x1 = 0; x1 < 40; x1++) {\n\t\tval0 = slow_mem_ptr[0x10000];\n\t\tval1 = *slow_mem_ptr++;\n\t\tif(!(filt_stat & ALL_STAT_ALTCHARSET)) {\n\t\t\tif((val0 >= 0x40) && (val0 < 0x80)) {\n\t\t\t\tval0 += flash_state;\n\t\t\t}\n\t\t\tif((val1 >= 0x40) && (val1 < 0x80)) {\n\t\t\t\tval1 += flash_state;\n\t\t\t}\n\t\t}\n\t\tif(filt_stat & ALL_STAT_VID80) {\n\t\t\tstr_buf[x2++] = val0;\t\t// aux mem\n\t\t}\n\t\tstr_buf[x2++] = val1;\t\t\t// main mem\n\t}\n\tstr_buf[x2] = 0;\t\t\t\t// null terminate\n\n\tredraw_changed_string(&str_buf[0], line_bytes, ch_mask, in_wptr,\n\t\tbg_pixel, fg_pixel, pixels_per_line,\n\t\t(filt_stat & ALL_STAT_VID80));\n}\n\nvoid\nredraw_changed_string(const byte *bptr, word32 line_bytes, word32 ch_mask,\n\tword32 *in_wptr, word32 bg_pixel,\n\tword32 fg_pixel, int pixels_per_line, int dbl)\n{\n\tregister word32 start_time, end_time;\n\tword32\t*wptr;\n\tword32\tval0, val1, val2, val3, pixel;\n\tint\tleft, right, st_line_mod8, offset, pos, shift, start_line;\n\tint\tstart_byte, end_byte;\n\tint\tx1, j;\n\n\tleft = 40;\n\tright = 0;\n\n\tGET_ITIMER(start_time);\n\n\tstart_line = line_bytes >> 16;\n\tstart_byte = line_bytes & 0x3f;\n\tend_byte = (line_bytes >> 8) & 0x3f;\n\tst_line_mod8 = start_line & 7;\n\n\tfor(x1 = start_byte; x1 < end_byte; x1++) {\n\t\tshift = x1 >> SHIFT_PER_CHANGE;\n\t\tif(((ch_mask >> shift) & 1) == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tleft = MY_MIN(x1, left);\n\t\tright = MY_MAX(x1 + 1, right);\n\t\toffset = (start_line * 2 * pixels_per_line) + x1*14;\n\t\tpos = x1;\n\t\tif(dbl) {\n\t\t\tpos = pos * 2;\n\t\t}\n\n\t\twptr = in_wptr + offset;\n\n\t\tval0 = bptr[pos];\n\t\tif(dbl) {\n\t\t\tpos++;\n\t\t}\n\t\tval1 = bptr[pos++];\n\t\tval2 = g_a2font_bits[val0][st_line_mod8];\n\t\tval3 = g_a2font_bits[val1][st_line_mod8];\n\t\t\t// val2, [6:0] is 80-column character bits, and\n\t\t\t//  [21:8] are the 40-column char bits (double-wide)\n\t\tif(dbl) {\n\t\t\tval2 = (val3 << 7) | (val2 & 0x7f);\n\t\t} else {\n\t\t\tval2 = val3 >> 8;\t// 40-column format\n\t\t}\n\t\tfor(j = 0; j < 14; j++) {\n\t\t\tpixel = bg_pixel;\n\t\t\tif(val2 & 1) {\t\t// LSB is first pixel\n\t\t\t\tpixel = fg_pixel;\n\t\t\t}\n\t\t\twptr[pixels_per_line] = pixel;\n\t\t\t*wptr++ = pixel;\n\t\t\tval2 = val2 >> 1;\n\t\t}\n\t}\n\tGET_ITIMER(end_time);\n\tif(start_line < 200) {\n\t\tvideo_update_edges(start_line, left * 14, right * 14, \"text\");\n\t}\n\n\tif((left >= right) || (left < 0) || (right < 0)) {\n\t\tprintf(\"str line %d, 40: left >= right: %d >= %d\\n\",\n\t\t\tstart_line, left, right);\n\t\tprintf(\" line_bytes:%08x ch_mask:%08x\\n\", line_bytes, ch_mask);\n\t}\n\n\tg_cycs_in_40col += (end_time - start_time);\n}\n\n// gr with an3=0:\n// 0=0\n// 1,0=3 (purple). 1,1=0\n// 2,0=c (green). 2,1=0\n// 3,0=f (white). 3,1=0\n// 4,0=0. 4,1=c (green)\n// 5,0=3 (purple). 5,1=c (green)\n// 6,0=c (green). 6,1=c (green)\n// 7,0=f (white). 7,1=c (green)\n// 8,0=0 (black). 7,1=3 (purple)\n// 9,0=3 (purple). 9,1=3 (purple)\n// a,0=c (green). a,1=3 (purple)\n// b,0=f (white). b,1=3 (purple)\n// c,0=0 (black). c,1=f (white)\n// d,0=3 (purple). d,1=f (white)\n// e,0=c (green). e,1=f (white)\n// f,0=f (white). e,1=f (white)\n\nvoid\nredraw_changed_gr(word32 line_bytes, int reparse, word32 *in_wptr,\n\t\t\t\tint pixels_per_line, word32 filt_stat)\n{\n\tword32\t*wptr;\n\tbyte\t*slow_mem_ptr;\n\tword32\tline_mask, mem_ptr, val0, val1, pixel0, pixel1, ch_mask;\n\tint\ty, shift, left, right, st_line_mod8, start_line, offset;\n\tint\tstart_byte, end_byte;\n\tint\tx1, i;\n\n\tstart_line = line_bytes >> 16;\n\tst_line_mod8 = start_line & 7;\n\n\ty = start_line >> 3;\n\tline_mask = 1 << (y);\n\tmem_ptr = 0x400 + g_screen_index[y];\n\tif(filt_stat & ALL_STAT_PAGE2) {\n\t\tmem_ptr += 0x400;\n\t}\n\tif((mem_ptr < 0x400) || (mem_ptr >= 0xc00)) {\n\t\thalt_printf(\"redraw_changed_gr: mem_ptr: %08x, y:%d\\n\",\n\t\t\t\t\t\t\tmem_ptr, y);\n\t\treturn;\n\t}\n\n\tch_mask = video_get_ch_mask(mem_ptr, filt_stat, reparse);\n\tif(!ch_mask) {\n\t\treturn;\n\t}\n\n\tg_a2_screen_buffer_changed |= line_mask;\n\n\tleft = 40;\n\tright = 0;\n\n\tslow_mem_ptr = &(g_slow_memory_ptr[mem_ptr]);\n\toffset = (start_line * 2 * pixels_per_line);\n\tstart_byte = line_bytes & 0x3f;\n\tend_byte = (line_bytes >> 8) & 0x3f;\n\tfor(x1 = start_byte; x1 < end_byte; x1++) {\n\t\tshift = x1 >> SHIFT_PER_CHANGE;\n\t\tif(((ch_mask >> shift) & 1) == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tleft = MY_MIN(x1, left);\n\t\tright = MY_MAX(x1 + 1, right);\n\n\t\twptr = in_wptr + offset + x1*14;\n\n\t\tval0 = slow_mem_ptr[0x10000 + x1];\n\t\tval1 = slow_mem_ptr[x1];\n\n\t\tif(st_line_mod8 >= 4) {\n\t\t\tval0 = val0 >> 4;\n\t\t\tval1 = val1 >> 4;\n\t\t}\n\t\tif(filt_stat & ALL_STAT_VID80) {\n\t\t\t// aux pixel is { [2:0],[3] }\n\t\t\tval0 = (val0 << 1) | ((val0 >> 3) & 1);\n\t\t} else if((filt_stat & ALL_STAT_ANNUNC3) == 0) {\n\t\t\tif(x1 & 1) {\t\t\t// odd cols\n\t\t\t\tval0 = ((val1 >> 1) & 2) | ((val1 >> 3) & 1);\n\t\t\t} else {\n\t\t\t\tval0 = val1 & 3;\t// even cols\n\t\t\t}\n\t\t\t// map val0: 0->0, 1->3, 2->c, 3->f\n\t\t\tval1 = 0;\n\t\t\tif(val0 & 1) {\n\t\t\t\tval1 |= 3;\n\t\t\t}\n\t\t\tif(val0 & 2) {\n\t\t\t\tval1 |= 0xc;\n\t\t\t}\n\t\t\tval0 = val1;\n\t\t} else {\n\t\t\tval0 = val1;\n\t\t}\n\t\tpixel0 = g_a2palette_1624[val0 & 0xf];\n\t\tpixel1 = g_a2palette_1624[val1 & 0xf];\n\t\tfor(i = 0; i < 7; i++) {\n\t\t\twptr[pixels_per_line] = pixel0;\n\t\t\twptr[pixels_per_line + 7] = pixel1;\n\t\t\twptr[0] = pixel0;\n\t\t\twptr[7] = pixel1;\n\t\t\twptr++;\n\t\t}\n\t}\n\n\tvideo_update_edges(start_line, left * 14, right * 14, \"gr\");\n}\n\nvoid\nvideo_hgr_line_segment(byte *slow_mem_ptr, word32 *wptr, int start_byte,\n\t\tint end_byte, int pixels_per_line, word32 filt_stat)\n{\n\tword32\tval0, val1, val2, prev_bits, val1_hi, dbl_step, pixel, color;\n\tword32\tmonochrome;\n\tint\tshift;\n\tint\tx2, i;\n\n\tmonochrome = filt_stat & (ALL_STAT_COLOR_C021 |\n\t\t\t\t\t\tALL_STAT_DIS_COLOR_DHIRES);\n\n\tprev_bits = 0;\n\tif(start_byte) {\n\t\tprev_bits = (slow_mem_ptr[-1] >> 3) & 0xf;\n\t\tif(!(filt_stat & ALL_STAT_VID80)) {\n\t\t\t// prev_bits is 4 bits, widen to 8 for std HGR\n\t\t\tprev_bits = g_pixels_widened[prev_bits] >> 4;\n\t\t}\n\t\tprev_bits = prev_bits & 0xf;\n\t}\n\tfor(x2 = start_byte; x2 < end_byte; x2++) {\n\t\tval0 = slow_mem_ptr[0x10000];\n\t\tval1 = *slow_mem_ptr++;\n\t\tval2 = slow_mem_ptr[0x10000];\t// next pixel, aux mem\n\t\tif(x2 >= 39) {\n\t\t\tval2 = 0;\n\t\t}\n\t\tval1_hi = ((val1 >> 5) & 4) | ((x2 & 1) << 1);\n\t\t\t// Hi-order bit in bit 2, odd pixel is in bit 0\n\n\t\tdbl_step = 3;\n\t\tif(filt_stat & ALL_STAT_VID80) {\n\t\t\t// aux+1[6:0], main[6:0], aux[6:0], prev[3:0]\n\t\t\tval0 = (val2 << 18) | ((val1 & 0x7f) << 11) |\n\t\t\t\t\t\t\t((val0 & 0x7f) << 4);\n\t\t\tif(!monochrome && (x2 & 1)) {\t// Get 6 bits from prev\n\t\t\t\tval0 = (val0 << 2);\n\t\t\t\tdbl_step = 1;\n\t\t\t}\n\t\t\tval0 = val0 | prev_bits;\n\t\t} else if(monochrome) {\n\t\t\tval0 = g_pixels_widened[val1 & 0x7f] << 4;\n\t\t} else {\t\t\t// color, normal hgr\n\t\t\tval2 = g_pixels_widened[*slow_mem_ptr & 0x7f];\n\t\t\tif(x2 >= 39) {\n\t\t\t\tval2 = 0;\n\t\t\t}\n\t\t\tval0 = ((val1 & 0x7f) << 4) | prev_bits | (val2 << 11);\n\t\t\tif((filt_stat & ALL_STAT_ANNUNC3) == 0) {\n\t\t\t\tval1_hi = val1_hi & 3;\n\t\t\t}\n\t\t}\n#if 0\n\t\tif(st_line < 8) {\n\t\t\tprintf(\"hgrl %d c:%d,d:%d, off:%03x val0:%02x 1:%02x\\n\",\n\t\t\t\tst_line, monochrome, dbl, x1 + x2, val0, val1);\n\t\t}\n#endif\n\t\tfor(i = 0; i < 14; i++) {\n\t\t\tcolor = 0;\t\t\t// black\n\t\t\tif(monochrome) {\n\t\t\t\tif(val0 & 0x10) {\n\t\t\t\t\tcolor = 0xf;\t// white\n\t\t\t\t}\n\t\t\t\tval0 = val0 >> 1;\n\t\t\t} else {\t\t\t// color\n\t\t\t\tif(filt_stat & ALL_STAT_VID80) {\n\t\t\t\t\tcolor = g_dhires_convert[val0 & 0xfff];\n\t\t\t\t\tshift = (x2 + x2 + i) & 3;\n\t\t\t\t\tcolor = color >> (4 * shift);\n\t\t\t\t\tif((i & 3) == dbl_step) {\n\t\t\t\t\t\tval0 = val0 >> 4;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tval2 = (val0 & 0x38) ^ val1_hi ^(i & 3);\n\t\t\t\t\tcolor = g_hires_lookup[val2 & 0x7f];\n\t\t\t\t\tif(i & 1) {\n\t\t\t\t\t\tval0 = val0 >> 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tpixel = g_a2palette_1624[color & 0xf];\n\t\t\twptr[pixels_per_line] = pixel;\n\t\t\t*wptr++ = pixel;\n\t\t}\n\t\tif((filt_stat & ALL_STAT_VID80) && ((x2 & 1) == 0)) {\n\t\t\tprev_bits = val0 & 0x3f;\n\t\t} else {\n\t\t\tprev_bits = val0 & 0xf;\n\t\t}\n\t}\n}\n\nvoid\nredraw_changed_hgr(word32 line_bytes, int reparse,\n\tword32 *in_wptr, int pixels_per_line, word32 filt_stat)\n{\n\tword32\t*wptr;\n\tbyte\t*slow_mem_ptr;\n\tword32\tch_mask, line_mask, mem_ptr;\n\tint\ty, shift, st_line_mod8, start_line, offset, start_byte;\n\tint\tend_byte;\n\tint\tx1;\n\n\tstart_line = line_bytes >> 16;\n\tstart_byte = line_bytes & 0x3f;\n\tend_byte = (line_bytes >> 8) & 0x3f;\t// Usually '40'\n\n\ty = start_line >> 3;\n\tst_line_mod8 = start_line & 7;\n\tline_mask = 1 << y;\n\tmem_ptr = 0x2000 + g_screen_index[y] + (st_line_mod8 * 0x400);\n\tif(filt_stat & ALL_STAT_PAGE2) {\n\t\tmem_ptr += 0x2000;\n\t}\n\tif((mem_ptr < 0x2000) || (mem_ptr >= 0x6000)) {\n\t\thalt_printf(\"redraw_changed_hgr: mem_ptr: %08x, y:%d\\n\",\n\t\t\t\t\t\t\t\tmem_ptr, y);\n\t\treturn;\n\t}\n\n\tch_mask = video_get_ch_mask(mem_ptr, filt_stat, reparse);\n\tif(ch_mask == 0) {\n\t\treturn;\n\t}\n\n\t// Hires depends on adjacent bits, so also reparse adjacent regions\n\t//  to handle redrawing of pixels on the boundaries\n\tch_mask = ch_mask | (ch_mask >> 1) | (ch_mask << 1);\n\n\tg_a2_screen_buffer_changed |= line_mask;\n\n\tfor(x1 = start_byte; x1 < end_byte; x1++) {\n\t\tshift = x1 >> SHIFT_PER_CHANGE;\n\t\tif(((ch_mask >> shift) & 1) == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tslow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]);\n\t\toffset = (start_line * 2 * pixels_per_line) + x1*14;\n\n\t\twptr = in_wptr + offset;\n\t\tvideo_hgr_line_segment(slow_mem_ptr, wptr, x1, end_byte,\n\t\t\t\tpixels_per_line, filt_stat);\n\n\t\tvideo_update_edges(start_line, x1 * 14, end_byte * 14, \"hgr\");\n\t\tbreak;\n\t}\n}\n\nint\nvideo_rebuild_super_hires_palette(int bank, word32 scan_info, int line,\n\t\t\t\t\t\t\t\tint reparse)\n{\n\tword32\t*word_ptr;\n\tbyte\t*byte_ptr;\n\tword32\tch_mask, mem_ptr, scan, old_scan, val0, val1;\n\tint\tdiffs, palette;\n\tint\tj;\n\n\tpalette = scan_info & 0xf;\n\n\tmem_ptr = (bank << 16) + 0x9e00 + (palette * 0x20);\n\tch_mask = video_get_ch_mask(mem_ptr, 0, 0);\n\n\told_scan = g_superhires_scan_save[bank][line];\n\tscan = (scan_info & 0xfaf) +\n\t\t\t\t(g_palette_change_cnt[bank][palette] << 12);\n\tg_superhires_scan_save[bank][line] = scan;\n\n#if 0\n\tif(line == 1) {\n\t\tword_ptr = (word32 *)&(g_slow_memory_ptr[0x19e00+palette*0x20]);\n\t\tprintf(\"y1vrshp, ch:%08x, s:%08x,os:%08x %d = %08x %08x %08x \"\n\t\t\t\"%08x %08x %08x %08x %08x\\n\",\n\t\t\tch_mask, scan, old_scan, reparse,\n\t\t\tword_ptr[0], word_ptr[1], word_ptr[2], word_ptr[3],\n\t\t\tword_ptr[4], word_ptr[5], word_ptr[6], word_ptr[7]);\n\t}\n#endif\n\n\tdiffs = reparse | ((scan ^ old_scan) & 0xf0f);\n\t\t/* we must do full reparse if palette changed for this line */\n\n\tif(!diffs && (ch_mask == 0) && (((scan ^ old_scan) & (~0xf0)) == 0)) {\n\t\t/* nothing changed, get out fast */\n\t\treturn 0;\n\t}\n\n\tif(ch_mask) {\n\t\t/* indicates the palette has changed, and other scan lines */\n\t\t/*  using this palette need to do a full 32-byte compare to */\n\t\t/*  decide if they need to update or not */\n\t\tg_palette_change_cnt[bank][palette]++;\n\t}\n\n\tword_ptr = (word32 *)&(g_slow_memory_ptr[(bank << 16) + 0x9e00 +\n\t\t\t\t\t\t\tpalette*0x20]);\n\tfor(j = 0; j < 8; j++) {\n\t\tif(word_ptr[j] != g_saved_line_palettes[bank][line][j]) {\n\t\t\tdiffs = 1;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(diffs == 0) {\n\t\treturn 0;\n\t}\n\n\t/* first, save this word_ptr into saved_line_palettes */\n\tbyte_ptr = (byte *)word_ptr;\n\tfor(j = 0; j < 8; j++) {\n\t\tg_saved_line_palettes[bank][line][j] = word_ptr[j];\n\t}\n\n\tbyte_ptr = (byte *)word_ptr;\n\t/* this palette has changed */\n\tfor(j = 0; j < 16; j++) {\n\t\tval0 = *byte_ptr++;\n\t\tval1 = *byte_ptr++;\n\t\tvideo_update_color_raw(bank, palette*16 + j, (val1<<8) + val0);\n\t}\n\n\treturn 1;\n}\n\nword32\nredraw_changed_super_hires_oneline(int bank, word32 *in_wptr,\n\t\tint pixels_per_line, int y, int scan, word32 ch_mask)\n{\n\tword32\t*palptr, *wptr;\n\tbyte\t*slow_mem_ptr;\n\tword32\tmem_ptr, val0, pal, pix0, pix1, pix2, pix3, save_pix;\n\tint\toffset, shift_per, left, right, shift;\n\tint\tx1, x2;\n\n\tmem_ptr = (bank << 16) + 0x2000 + (0xa0 * y);\n\n\tshift_per = (1 << SHIFT_PER_CHANGE);\n\tpal = (scan & 0xf);\n\n\tsave_pix = 0;\n\tif(scan & 0x20) {\t\t\t// Fill mode\n\t\tch_mask = (word32)-1;\n\t}\n\n\tpalptr = &(g_palette_8to1624[bank][pal * 16]);\n\tleft = 160;\n\tright = 0;\n\n\tfor(x1 = 0; x1 < 0xa0; x1 += shift_per) {\n\t\tshift = x1 >> SHIFT_PER_CHANGE;\n\t\tif(((ch_mask >> shift) & 1) == 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tleft = MY_MIN(x1, left);\n\t\tright = MY_MAX(x1 + shift_per, right);\n\n\t\tslow_mem_ptr = &(g_slow_memory_ptr[mem_ptr + x1]);\n\t\toffset = x1*4;\n\n\t\twptr = in_wptr + offset;\n\n\t\tfor(x2 = 0; x2 < shift_per; x2++) {\n\t\t\tval0 = *slow_mem_ptr++;\n\n\t\t\tif(scan & 0x80) {\t\t// 640 mode\n\t\t\t\tpix0 = (val0 >> 6) & 3;\n\t\t\t\tpix1 = (val0 >> 4) & 3;\n\t\t\t\tpix2 = (val0 >> 2) & 3;\n\t\t\t\tpix3 = val0 & 3;\n\t\t\t\tpix0 = palptr[pix0 + 8];\n\t\t\t\tpix1 = palptr[pix1 + 12];\n\t\t\t\tpix2 = palptr[pix2 + 0];\n\t\t\t\tpix3 = palptr[pix3 + 4];\n\t\t\t} else {\t/* 320 mode */\n\t\t\t\tpix0 = (val0 >> 4);\n\t\t\t\tpix2 = (val0 & 0xf);\n\t\t\t\tif(scan & 0x20) {\t// Fill mode\n\t\t\t\t\tif(!pix0) {\t// 0 = repeat last color\n\t\t\t\t\t\tpix0 = save_pix;\n\t\t\t\t\t}\n\t\t\t\t\tif(!pix2) {\n\t\t\t\t\t\tpix2 = pix0;\n\t\t\t\t\t}\n\t\t\t\t\tsave_pix = pix2;\n\t\t\t\t}\n\t\t\t\tpix0 = palptr[pix0];\n\t\t\t\tpix1 = pix0;\n\t\t\t\tpix2 = palptr[pix2];\n\t\t\t\tpix3 = pix2;\n\t\t\t}\n\t\t\twptr[pixels_per_line] = pix0;\n\t\t\t*wptr++ = pix0;\n\t\t\twptr[pixels_per_line] = pix1;\n\t\t\t*wptr++ = pix1;\n\t\t\twptr[pixels_per_line] = pix2;\n\t\t\t*wptr++ = pix2;\n\t\t\twptr[pixels_per_line] = pix3;\n\t\t\t*wptr++ = pix3;\n\t\t}\n\t}\n\n\treturn (left << 16) | (right & 0xffff);\n}\n\nvoid\nredraw_changed_super_hires_bank(int bank, int start_line, int reparse,\n\t\t\t\t\tword32 *wptr, int pixels_per_line)\n{\n\tdword64\tdval, dval1;\n\tword32\tthis_check, mask, tmp, scan, old_scan, mem_ptr;\n\tint\tleft, right, ret, shift;\n\n\tmem_ptr = (bank << 16) + 0x2000 + (160 * start_line);\n\tdval1 = g_slow_mem_changed[(mem_ptr >> CHANGE_SHIFT) + 1] |\n\t\t\tg_slow_mem_ch2[(mem_ptr >> CHANGE_SHIFT) + 1];\n\tdval = g_slow_mem_changed[mem_ptr >> CHANGE_SHIFT] |\n\t\tg_slow_mem_ch2[mem_ptr >> CHANGE_SHIFT] | (dval1 << 32);\n\tshift = (mem_ptr >> SHIFT_PER_CHANGE) & 0x1f;\n\tmask = (1 << (160 >> SHIFT_PER_CHANGE)) - 1;\n\tthis_check = (dval >> shift) & mask;\n\n\tscan = g_slow_memory_ptr[(bank << 16) + 0x9d00 + start_line];\n\n\told_scan = g_superhires_scan_save[bank][start_line];\n\n\tret = video_rebuild_super_hires_palette(bank, scan, start_line,\n\t\t\t\t\t\t\t\treparse);\n\tif(ret || reparse || ((scan ^ old_scan) & 0xa0)) {\n\t\t\t\t\t/* 0x80 == mode640, 0x20 = fill */\n\t\tthis_check = (word32)-1;\n\t}\n\n\tif(!this_check) {\n\t\treturn;\t\t\t// Nothing to do, get out\n\t}\n\n\tif(scan & 0x80) {\t\t\t// 640 mode\n\t\tg_num_lines_superhires640++;\n\t}\n\n\tif((scan >> 5) & 1) {\t\t// fill mode--redraw whole line\n\t\tthis_check = (word32)-1;\n\t}\n\n\tg_a2_screen_buffer_changed |= (1 << (start_line >> 3));\n\ttmp = redraw_changed_super_hires_oneline(bank, wptr, pixels_per_line,\n\t\t\t\t\t\tstart_line, scan, this_check);\n\tleft = tmp >> 16;\n\tright = tmp & 0xffff;\n\n\tvideo_update_edges(start_line, left * 4, right * 4, \"shr\");\n}\n\nvoid\nredraw_changed_super_hires(word32 line_bytes, int reparse, word32 *wptr,\n\t\t\t\tint pixels_per_line, word32 filt_stat)\n{\n\tint\tbank, start_line;\n\n\tstart_line = line_bytes >> 16;\n\twptr += start_line * 2 * pixels_per_line;\n\n\tif(filt_stat & ALL_STAT_VOC_INTERLACE) {\n\t\t// Do 400 interlaced lines.  Do aux first, then main mem\n\t\tredraw_changed_super_hires_bank(1, start_line, reparse, wptr,\n\t\t\t\t\t\t\t\t\t0);\n\t\tredraw_changed_super_hires_bank(0, start_line, reparse,\n\t\t\t\t\t\twptr + pixels_per_line, 0);\n\t} else {\n\t\tbank = 1;\n\t\tif(filt_stat & ALL_STAT_VOC_MAIN) {\n\t\t\tbank = 0;\t\t// VOC SHR in main memory\n\t\t}\n\t\tredraw_changed_super_hires_bank(bank, start_line, reparse, wptr,\n\t\t\t\t\t\t\tpixels_per_line);\n\t}\n}\n\nvoid\nvideo_copy_changed2()\n{\n\tword32\t*ch_ptr, *ch2_ptr;\n\tint\tbank1_off;\n\tint\ti;\n\n\t// Copy entries from g_slow_mem_changed[] to g_slow_mem_ch2[] and\n\t//  clear g_slow_mem_changed[]\n\tch_ptr = &g_slow_mem_changed[0];\n\tch2_ptr = &g_slow_mem_ch2[0];\n\tbank1_off = 0x10000 >> CHANGE_SHIFT;\n\tfor(i = 4; i < 0xa0; i++) {\t\t// Pages 0x0400 through 0x9fff\n\t\tch2_ptr[i] = ch_ptr[i];\n\t\tch2_ptr[i + bank1_off] = ch_ptr[i + bank1_off];\n\t\tch_ptr[i] = 0;\n\t\tch_ptr[i + bank1_off] = 0;\n\t}\n}\n\nvoid\nvideo_update_event_line(int line)\n{\n\tint\tnew_line;\n\n\tvideo_update_through_line(line);\n\n\tnew_line = line + g_line_ref_amt;\n\tif(new_line < 200) {\n\t\tif(!g_config_control_panel && !g_halt_sim) {\n\t\t\tadd_event_vid_upd(new_line);\n\t\t}\n\t} else if(line >= 262) {\n\t\tif(!g_config_control_panel && !g_halt_sim) {\n\t\t\tadd_event_vid_upd(0);\t/* add event for new screen */\n\t\t}\n\t}\n}\n\nvoid\nvideo_force_reparse()\n{\n\tword32\t*wptr;\n\tint\theight, width_full;\n\tint\ti, j;\n\n\tg_video_stat_old_pos = 1;\n\tg_video_filt_stat_old[0].filt_stat = (word32)-1;\n\theight = g_video_act_margin_top + A2_WINDOW_HEIGHT +\n\t\t\t\t\t\tg_video_act_margin_bottom;\n\theight = MY_MIN(height, g_mainwin_kimage.a2_height);\n\twidth_full = g_mainwin_kimage.a2_width_full;\n\twptr = g_mainwin_kimage.wptr;\n\tfor(i = 0; i < height; i++) {\n\t\tfor(j = 0; j < width_full; j++) {\n\t\t\t*wptr++ = 0;\n\t\t}\n\t}\n\tg_border_reparse = 1;\n}\n\nvoid\nvideo_update_through_line(int line)\n{\n\tregister word32 start_time;\n\tregister word32 end_time;\n\tword32\tmy_start_lines, my_end_lines, prev_all_stat, next_all_stat;\n\tword32\tprev_lines_since_vbl, next_lines_since_vbl;\n\tint\tlast_line, pos, last_pos, end, num;\n\tint\ti;\n\n#if 0\n\tvid_printf(\"\\nvideo_upd for line %d, lines: %06x\\n\", line,\n\t\t\t\tget_lines_since_vbl(g_cur_dfcyc));\n#endif\n\n\tGET_ITIMER(start_time);\n\n\tlast_line = MY_MIN(200, line+1); /* go through line, but not past 200 */\n\n\tpos = g_video_save_all_stat_pos;\n\tlast_pos = g_video_all_stat_pos;\n\tprev_all_stat = g_video_all_stat[pos].cur_all_stat;\n\tprev_lines_since_vbl = g_video_all_stat[pos].lines_since_vbl;\n\tg_video_all_stat[last_pos].cur_all_stat = g_cur_a2_stat;\n\tg_video_all_stat[last_pos].lines_since_vbl = (line + 1) << 8;\n\tnext_all_stat = g_video_all_stat[pos+1].cur_all_stat;\n\tnext_lines_since_vbl = g_video_all_stat[pos+1].lines_since_vbl;\n\tfor(i = g_vid_update_last_line; i < last_line; i++) {\n\t\t// We need to step through pos in g_video_all_stat[] and find\n\t\t//  the start/end pairs for each line\n\t\tg_a2_line_left_edge[i] = 640;\n\t\tg_a2_line_right_edge[i] = 0;\n\t\tmy_start_lines = (i << 8) + 25;\n\t\tmy_end_lines = (i << 8) + 65;\n\t\tif(prev_lines_since_vbl > my_start_lines) {\n\t\t\tprintf(\"prev:%08x > %08x start at i:%d\\n\",\n\t\t\t\tprev_lines_since_vbl, my_start_lines, i);\n\t\t}\n\t\twhile(my_start_lines < my_end_lines) {\n\t\t\twhile(next_lines_since_vbl <= my_start_lines) {\n\t\t\t\t// Step into next entry\n\t\t\t\tprev_all_stat = next_all_stat;\n\t\t\t\tprev_lines_since_vbl = next_lines_since_vbl;\n\t\t\t\tpos++;\n\t\t\t\tg_video_save_all_stat_pos = pos;\n\t\t\t\tnext_all_stat =\n\t\t\t\t\tg_video_all_stat[pos+1].cur_all_stat;\n\t\t\t\tnext_lines_since_vbl =\n\t\t\t\t\tg_video_all_stat[pos+1].lines_since_vbl;\n\t\t\t\tif(pos >= last_pos) {\n\t\t\t\t\tprintf(\"FELL OFF %d %d!\\n\", pos,\n\t\t\t\t\t\t\t\tlast_pos);\n\t\t\t\t\tpos--;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tend = 65;\n\t\t\tif(next_lines_since_vbl < my_end_lines) {\n\t\t\t\tend = (next_lines_since_vbl & 0xff);\n\t\t\t\tif(end < 25) {\n\t\t\t\t\tprintf(\"i:%d next_lines_since_vbl:\"\n\t\t\t\t\t\t\"%08x!\\n\", i,\n\t\t\t\t\t\tnext_lines_since_vbl);\n\t\t\t\t\tend = 25;\n\t\t\t\t}\n\t\t\t}\n\t\t\tvideo_do_partial_line(my_start_lines, end,\n\t\t\t\t\t\t\tprev_all_stat);\n\t\t\tmy_start_lines = (i << 8) + end;\n\t\t}\n\t}\n\n\n\tg_vid_update_last_line = last_line;\n\tg_video_save_all_stat_pos = pos;\n\n\t/* deal with border and forming rects for xdriver.c to use */\n\tif(line >= 262) {\n\t\tif(g_num_lines_prev_superhires != g_num_lines_superhires) {\n\t\t\t/* switched in/out from superhires--refresh borders */\n\t\t\tg_border_sides_refresh_needed = 1;\n\t\t}\n\n\t\tvideo_form_change_rects();\n\t\tg_num_lines_prev_superhires = g_num_lines_superhires;\n\t\tg_num_lines_prev_superhires640 = g_num_lines_superhires640;\n\t\tg_num_lines_superhires = 0;\n\t\tg_num_lines_superhires640 = 0;\n\n\t\tnum = g_video_filt_stat_pos;\n\t\tg_video_stat_old_pos = num;\n\t\tfor(i = 0; i < num; i++) {\n\t\t\tg_video_filt_stat_old[i] = g_video_filt_stat[i];\n\t\t}\n\t\tg_video_filt_stat_pos = 0;\n\t}\n\tGET_ITIMER(end_time);\n\tg_cycs_in_refresh_line += (end_time - start_time);\n}\n\nextern word32 g_vbl_count;\n\nvoid\nvideo_do_partial_line(word32 lines_since_vbl, int end, word32 cur_all_stat)\n{\n\tword32\tfilt_stat, old_filt_stat, line_bytes, old_line_bytes;\n\tint\tpos, old_pos, reparse, line;\n\n\tpos = g_video_filt_stat_pos;\n\told_pos = g_video_stat_old_pos;\n\tfilt_stat = video_all_stat_to_filt_stat(lines_since_vbl >> 8,\n\t\t\t\t\t\t\t\tcur_all_stat);\n\tline_bytes = ((lines_since_vbl & 0x1ff00) << 8) |\n\t\t\t((end - 25) << 8) | ((lines_since_vbl - 25) & 0x3f);\n\tg_video_filt_stat[pos].line_bytes = line_bytes;\n\tg_video_filt_stat[pos].filt_stat = filt_stat;\n\treparse = 1;\n\told_filt_stat = (word32)-1;\n\told_line_bytes = (word32)-1;\n\tif(pos < old_pos) {\n\t\told_filt_stat = g_video_filt_stat_old[pos].filt_stat;\n\t\told_line_bytes = g_video_filt_stat_old[pos].line_bytes;\n\t}\n\tif((old_filt_stat == filt_stat) && (line_bytes == old_line_bytes)) {\n\t\treparse = 0;\n\t} else if((old_filt_stat ^ filt_stat) & ALL_STAT_SUPER_HIRES) {\n\t\tg_border_reparse = 1;\n\t}\n\tvideo_refresh_line(line_bytes, reparse, filt_stat);\n\tline = lines_since_vbl >> 8;\n\tif(line < 200) {\n\t\tg_a2_filt_stat[line] = filt_stat;\n\t} else {\n\t\tprintf(\"partial_line %08x %d %08x out of range!\\n\",\n\t\t\t\t\tlines_since_vbl, end, cur_all_stat);\n\t}\n\tif((end <= 25) || (end < (int)(lines_since_vbl & 0xff))) {\n\t\tprintf(\"Bad lsv:%08x, end:%d, stat:%08x\\n\", lines_since_vbl,\n\t\t\t\t\t\tend, filt_stat);\n\t}\n\tpos++;\n\tif(pos >= MAX_VIDEO_FILT_STAT) {\n\t\tpos--;\n\t}\n\tg_video_filt_stat_pos = pos;\n}\n\nvoid\nvideo_refresh_line(word32 line_bytes, int must_reparse, word32 filt_stat)\n{\n\tword32\t*wptr;\n\tint\tpixels_per_line, offset, line;\n\n\tline = line_bytes >> 16;\n\tif((word32)line >= 200) {\n\t\tprintf(\"video_refresh %08x %d %08x!\\n\", line_bytes,\n\t\t\t\t\t\tmust_reparse, filt_stat);\n\t\treturn;\n\t}\n\twptr = g_mainwin_kimage.wptr;\n\tpixels_per_line = g_mainwin_kimage.a2_width_full;\n\toffset = (pixels_per_line * g_video_act_margin_top) +\n\t\t\t\t\t\tg_video_act_margin_left;\n\twptr = wptr + offset;\n\n\tif(filt_stat & ALL_STAT_SUPER_HIRES) {\n\t\tg_num_lines_superhires++;\n\t\tredraw_changed_super_hires(line_bytes, must_reparse, wptr,\n\t\t\t\t\t\tpixels_per_line, filt_stat);\n\t} else if(filt_stat & ALL_STAT_BORDER) {\n\t\tif(line < 192) {\n\t\t\thalt_printf(\"Border line not 192: %d\\n\", line);\n\t\t}\n\t\tg_a2_line_left_edge[line] = 0;\n\t\tg_a2_line_right_edge[line] = 560;\n\t\tif(g_border_line24_refresh_needed) {\n\t\t\tg_border_line24_refresh_needed = 0;\n\t\t\tg_a2_screen_buffer_changed |= (1 << 24);\n\t\t}\n\t} else if(filt_stat & ALL_STAT_TEXT) {\n\t\tredraw_changed_text(line_bytes, must_reparse, wptr,\n\t\t\t\t\t\tpixels_per_line, filt_stat);\n\t} else if(filt_stat & ALL_STAT_HIRES) {\n\t\tredraw_changed_hgr(line_bytes, must_reparse, wptr,\n\t\t\t\t\t\tpixels_per_line, filt_stat);\n\t} else {\n\t\tredraw_changed_gr(line_bytes, must_reparse, wptr,\n\t\t\t\t\t\tpixels_per_line, filt_stat);\n\t}\n}\n\nvoid\nprepare_a2_font()\n{\n\tword32\tval0, val1, val2;\n\tint\ti, j, k;\n\n\t// Prepare g_a2font_bits[char][line] where each entry indicates the\n\t//  set pixels in this line of the character.  Bits 6:0 are an\n\t//  80-column character, and bits 21:8 are  the 14 expanded bits of a\n\t//  40-columns character, both with the first visible bit at the\n\t//  rightmost bit address.  But g_font_array[] is in big-endian bit\n\t//  order, which is less useful\n\tfor(i = 0; i < 256; i++) {\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\tval0 = g_font_array[i][j] >> 1;\n\t\t\tval1 = 0;\t\t// 80-column bits\n\t\t\tval2 = 0;\t\t// 40-column bits (doubled)\n\t\t\tfor(k = 0; k < 7; k++) {\n\t\t\t\tval1 = val1 << 1;\n\t\t\t\tval2 = val2 << 2;\n\t\t\t\tif(val0 & 1) {\n\t\t\t\t\tval1 |= 1;\n\t\t\t\t\tval2 |= 3;\n\t\t\t\t}\n\t\t\t\tval0 = val0 >> 1;\n\t\t\t}\n\t\t\tg_a2font_bits[i][j] = (val2 << 8) | val1;\n\t\t}\n\t}\n}\n\nvoid\nprepare_a2_romx_font(byte *font_ptr)\n{\n\tword32\tval0, val1, val2;\n\tint\ti, j, k;\n\n\t// ROMX file\n\tfor(i = 0; i < 256; i++) {\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\tval0 = font_ptr[i*8 + j];\n\t\t\tval1 = 0;\t\t// 80-column bits\n\t\t\tval2 = 0;\t\t// 40-column bits (doubled)\n\t\t\tfor(k = 0; k < 7; k++) {\n\t\t\t\tval1 = val1 << 1;\n\t\t\t\tval2 = val2 << 2;\n\t\t\t\tif((val0 & 0x40) == 0) {\n\t\t\t\t\tval1 |= 1;\n\t\t\t\t\tval2 |= 3;\n\t\t\t\t}\n\t\t\t\tval0 = val0 << 1;\n\t\t\t}\n\t\t\tg_a2font_bits[i][j] = (val2 << 8) | val1;\n\t\t}\n\t}\n}\n\nvoid\nvideo_add_rect(Kimage *kimage_ptr, int x, int y, int width, int height)\n{\n\tint\tpos;\n\n\tpos = kimage_ptr->num_change_rects++;\n\tif(pos >= MAX_CHANGE_RECTS) {\n\t\treturn;\t\t\t// This will be handled later\n\t}\n\tkimage_ptr->change_rect[pos].x = x;\n\tkimage_ptr->change_rect[pos].y = y;\n\tkimage_ptr->change_rect[pos].width = width;\n\tkimage_ptr->change_rect[pos].height = height;\n\n\tg_video_pixel_dcount += (width * height);\n#if 0\n\tprintf(\"Add rect %d, x:%d y:%d, w:%d h:%d\\n\", pos, x, y, width, height);\n#endif\n}\n\nvoid\nvideo_add_a2_rect(int start_line, int end_line, int left_pix, int right_pix)\n{\n\tint\tsrcy;\n\n\tif((left_pix >= right_pix) || (left_pix < 0) || (right_pix <= 0)) {\n\t\thalt_printf(\"video_push_lines: lines %d to %d, pix %d to %d\\n\",\n\t\t\tstart_line, end_line, left_pix, right_pix);\n\t\tprintf(\"a2_screen_buf_ch:%08x, g_full_refr:%08x\\n\",\n\t\t\tg_a2_screen_buffer_changed, g_full_refresh_needed);\n\t\treturn;\n\t}\n\n\tsrcy = 2*start_line;\n\n\tvideo_add_rect(&g_mainwin_kimage, g_video_act_margin_left + left_pix,\n\t\t\tg_video_act_margin_top + srcy,\n\t\t\t(right_pix - left_pix), 2*(end_line - start_line));\n}\n\nvoid\nvideo_form_change_rects()\n{\n\tKimage\t*kimage_ptr;\n\tregister word32 start_time;\n\tregister word32 end_time;\n\tdword64\tsave_pixel_dcount;\n\tword32\tmask;\n\tint\tstart, line, left_pix, right_pix, left, right, line_div8;\n\tint\tx, y, width, height;\n\n\tkimage_ptr = &g_mainwin_kimage;\n\tif(g_border_sides_refresh_needed) {\n\t\tg_border_sides_refresh_needed = 0;\n\t\t// Add left side border\n\t\tvideo_add_rect(kimage_ptr, 0, g_video_act_margin_top,\n\t\t\t\t\t\tBORDER_WIDTH, A2_WINDOW_HEIGHT);\n\n\t\t// Add right-side border.  Resend x from 560 through\n\t\t//  X_A2_WINDOW_WIDTH\n\t\tx = g_video_act_margin_left + 560;\n\t\twidth = X_A2_WINDOW_WIDTH - x;\n\t\tvideo_add_rect(kimage_ptr, x, g_video_act_margin_top, width,\n\t\t\t\t\t\t\tA2_WINDOW_HEIGHT);\n\t}\n\tif(g_border_special_refresh_needed) {\n\t\tg_border_special_refresh_needed = 0;\n\n\t\t// Do top border\n\t\twidth = g_video_act_width;\n\t\theight = g_video_act_margin_top;\n\t\tvideo_add_rect(kimage_ptr, 0, 0, width, height);\n\n\t\t// Do bottom border\n\t\theight = g_video_act_margin_bottom;\n\t\ty = g_video_act_margin_top + A2_WINDOW_HEIGHT;\n\t\tvideo_add_rect(kimage_ptr, 0, y, width, height);\n\t}\n\tif(g_status_refresh_needed) {\n\t\tg_status_refresh_needed = 0;\n\t\twidth = g_mainwin_kimage.a2_width;\n\t\ty = g_video_act_margin_top + A2_WINDOW_HEIGHT +\n\t\t\t\t\t\tg_video_act_margin_bottom;\n\t\theight = kimage_ptr->a2_height - y;\n\t\tif(height > 0) {\n\t\t\tsave_pixel_dcount = g_video_pixel_dcount;\n\t\t\tvideo_add_rect(kimage_ptr, 0, y, width, height);\n\t\t\tg_video_pixel_dcount = save_pixel_dcount;\n\t\t}\n\t}\n\n\tif(g_a2_screen_buffer_changed == 0) {\n\t\treturn;\n\t}\n\n\tGET_ITIMER(start_time);\n\n\tstart = -1;\n\n\tleft_pix = 640;\n\tright_pix = 0;\n\n\tfor(line = 0; line < 200; line++) {\n\t\tline_div8 = line >> 3;\n\t\tmask = 1 << (line_div8);\n\t\tif((g_full_refresh_needed & mask) != 0) {\n\t\t\tleft = 0;\n\t\t\tright = 640;\n\t\t} else {\n\t\t\tleft = g_a2_line_left_edge[line];\n\t\t\tright = g_a2_line_right_edge[line];\n\t\t}\n\n\t\tif(!(g_a2_screen_buffer_changed & mask) || (left >= right)) {\n\t\t\t/* No need to update this line */\n\t\t\t/* Refresh previous chunks of lines, if any */\n\t\t\tif(start >= 0) {\n\t\t\t\tvideo_add_a2_rect(start, line, left_pix,\n\t\t\t\t\t\t\t\tright_pix);\n\t\t\t\tstart = -1;\n\t\t\t\tleft_pix = 640;\n\t\t\t\tright_pix = 0;\n\t\t\t}\n\t\t} else {\n\t\t\t/* Need to update this line */\n\t\t\tif(start < 0) {\n\t\t\t\tstart = line;\n\t\t\t}\n\t\t\tleft_pix = MY_MIN(left, left_pix);\n\t\t\tright_pix = MY_MAX(right, right_pix);\n\t\t}\n\t}\n\n\tif(start >= 0) {\n\t\tvideo_add_a2_rect(start, 200, left_pix, right_pix);\n\t}\n\n\tg_a2_screen_buffer_changed = 0;\n\tg_full_refresh_needed = 0;\n\n\tGET_ITIMER(end_time);\n\n\tg_cycs_in_xredraw += (end_time - start_time);\n}\n\nint\nvideo_get_a2_width(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->a2_width;\n}\n\nint\nvideo_get_x_width(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->x_width;\n}\n\nint\nvideo_get_a2_height(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->a2_height;\n}\n\nint\nvideo_get_x_height(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->x_height;\n}\n\nint\nvideo_get_x_xpos(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->x_xpos;\n}\n\nint\nvideo_get_x_ypos(Kimage *kimage_ptr)\n{\n\treturn kimage_ptr->x_ypos;\n}\n\nvoid\nvideo_update_xpos_ypos(Kimage *kimage_ptr, int x_xpos, int x_ypos)\n{\n\tx_xpos = video_clamp(x_xpos, 0, kimage_ptr->x_max_width - 640);\n\tx_ypos = video_clamp(x_ypos, 0, kimage_ptr->x_max_height - 420);\n\tkimage_ptr->x_xpos = x_xpos;\n\tkimage_ptr->x_ypos = x_ypos;\n\tif(kimage_ptr == &g_mainwin_kimage) {\n\t\tg_mainwin_xpos = x_xpos;\n\t\tg_mainwin_ypos = x_ypos;\n\t\t// printf(\"Set g_mainwin_xpos:%d, ypos:%d\\n\", x_xpos, x_ypos);\n\t}\n}\n\nint\nvideo_change_aspect_needed(Kimage *kimage_ptr, int x_width, int x_height)\n{\n\t// Return 1 if the passed in height, width do not match the kimage\n\t//  aspect-corrected version, and at least 2 VBL periods have passed\n\n\tif((kimage_ptr->vbl_of_last_resize + 6) > g_vbl_count) {\n\t\treturn 0;\n\t}\n\tif((kimage_ptr->x_height != x_height) ||\n\t\t\t\t\t(kimage_ptr->x_width != x_width)) {\n#if 0\n\t\tprintf(\"change_aspect_needed, vbl:%d kimage width:%d height:%d \"\n\t\t\t\"but x width:%d height:%d\\n\", g_vbl_count,\n\t\t\tkimage_ptr->x_width, kimage_ptr->x_height,\n\t\t\tx_width, x_height);\n#endif\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nvoid\nvideo_update_status_enable(Kimage *kimage_ptr)\n{\n\tint\theight, a2_height;\n\n\theight = g_video_act_margin_top + A2_WINDOW_HEIGHT +\n\t\t\t\t\t\tg_video_act_margin_bottom;\n\ta2_height = height;\n\tif(g_status_enable) {\n\t\ta2_height = kimage_ptr->a2_height_full;\n\t}\n\tkimage_ptr->a2_height = a2_height;\n\theight = (a2_height * kimage_ptr->scale_width_a2_to_x) >> 16;\n\tif(height > kimage_ptr->x_max_height) {\n\t\theight = kimage_ptr->x_max_height;\n\t}\n\tkimage_ptr->x_height = height;\n#if 0\n\tprintf(\"new a2_height:%d, x_height:%d\\n\", kimage_ptr->a2_height,\n\t\t\t\t\t\t\tkimage_ptr->x_height);\n#endif\n\t//printf(\"Calling video_update_scale from video_update_status_en\\n\");\n\tvideo_update_scale(kimage_ptr, kimage_ptr->x_width, height, 0);\n}\n\n// video_out_query: return 0 if no screen drawing at all is needed.\n//  returns 1 or the number of change_rects if any drawing is needed\nint\nvideo_out_query(Kimage *kimage_ptr)\n{\n\tint\tnum_change_rects, x_refresh_needed;\n\n\tnum_change_rects = kimage_ptr->num_change_rects;\n\tx_refresh_needed = kimage_ptr->x_refresh_needed;\n\tif(x_refresh_needed) {\n\t\treturn 1;\n\t}\n\treturn num_change_rects;\n}\n\n// video_out_done: used by specialize xdriver platform code which needs to\n//  clear the num_change_rects=0.\nvoid\nvideo_out_done(Kimage *kimage_ptr)\n{\n\tkimage_ptr->num_change_rects = 0;\n\tkimage_ptr->x_refresh_needed = 0;\n}\n\n// Called by xdriver.c to copy KEGS's kimage data to the vptr buffer\nint\nvideo_out_data(void *vptr, Kimage *kimage_ptr, int out_width_act,\n\t\tChange_rect *rectptr, int pos)\n{\n\tword32\t*out_wptr, *wptr;\n\tint\ta2_width, a2_width_full, width, a2_height, height, x, eff_y;\n\tint\tx_width, x_height, num_change_rects, x_refresh_needed;\n\tint\ti, j;\n\n\t// Copy from kimage_ptr->wptr to vptr\n\tnum_change_rects = kimage_ptr->num_change_rects;\n\tx_refresh_needed = kimage_ptr->x_refresh_needed;\n\tif(((pos >= num_change_rects) || (pos >= MAX_CHANGE_RECTS)) &&\n\t\t\t\t\t\t\t!x_refresh_needed) {\n\t\tkimage_ptr->num_change_rects = 0;\n\t\treturn 0;\n\t}\n\ta2_width = kimage_ptr->a2_width;\n\ta2_width_full = kimage_ptr->a2_width_full;\n\ta2_height = kimage_ptr->a2_height;\n\tif((num_change_rects >= MAX_CHANGE_RECTS) || x_refresh_needed) {\n\t\t// Table overflow, just copy everything in one go\n\t\tkimage_ptr->x_refresh_needed = 0;\n\t\tif(pos >= 1) {\n\t\t\tkimage_ptr->num_change_rects = 0;\n\t\t\treturn 0;\t\t// No more to do\n\t\t}\n\t\t// Force full update\n\t\trectptr->x = 0;\n\t\trectptr->y = 0;\n\t\trectptr->width = a2_width;\n\t\trectptr->height = a2_height;\n\t} else {\n\t\t*rectptr = kimage_ptr->change_rect[pos];\t// Struct copy\n\t}\n#if 0\n\tprintf(\"video_out_data, %p rectptr:%p, pos:%d, x:%d y:%d w:%d h:%d, \"\n\t\t\"wptr:%p\\n\", vptr, rectptr, pos, rectptr->x,\n\t\trectptr->y, rectptr->width, rectptr->height,\n\t\tkimage_ptr->wptr);\n#endif\n\n\twidth = rectptr->width;\n\theight = rectptr->height;\n\tx = rectptr->x;\n\tx_width = kimage_ptr->x_width;\n\tx_height = kimage_ptr->x_height;\n\tif(!g_video_no_scale_window &&\n\t\t\t((a2_width != x_width) || (a2_height != x_height))) {\n#if 0\n\t\tprintf(\"a2_width:%d, x_width:%d, a2_height:%d, x_height:\"\n\t\t\t\"%d\\n\", a2_width, x_width, a2_height, x_height);\n#endif\n\t\treturn video_out_data_scaled(vptr, kimage_ptr, out_width_act,\n\t\t\t\t\t\t\t\trectptr);\n\t} else {\n\t\tout_wptr = (word32 *)vptr;\n\t\tfor(i = 0; i < height; i++) {\n\t\t\teff_y = rectptr->y + i;\n\t\t\twptr = kimage_ptr->wptr + (eff_y * a2_width_full) + x;\n\t\t\tout_wptr = ((word32 *)vptr) +\n\t\t\t\t\t\t(eff_y * out_width_act) + x;\n\t\t\tfor(j = 0; j < width; j++) {\n\t\t\t\t*out_wptr++ = *wptr++;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn 1;\n}\n\n\nint\nvideo_out_data_intscaled(void *vptr, Kimage *kimage_ptr, int out_width_act,\n\t\t\t\t\tChange_rect *rectptr)\n{\n\tword32\t*out_wptr, *wptr;\n\tword32\tpos_scale, alpha_mask;\n\tint\ta2_width_full, eff_y, src_y, x, y, new_x, out_x, out_y;\n\tint\tout_width, out_height, max_x, max_y, out_max_x, out_max_y, pos;\n\tint\ti, j;\n\n\t// Faster scaling routine which does simple pixel replication rather\n\t//  than blending.  Intended for scales >= 3.0 (or so) since at\n\t//  these scales, replication looks fine.\n\tx = rectptr->x;\n\ty = rectptr->y;\n\tmax_x = rectptr->width + x;\n\tmax_y = rectptr->height + y;\n\tmax_x = MY_MIN(kimage_ptr->a2_width_full, max_x + 1);\n\tmax_y = MY_MIN(kimage_ptr->a2_height, max_y + 1);\n\tx = MY_MAX(0, x - 1);\n\ty = MY_MAX(0, y - 1);\n\ta2_width_full = kimage_ptr->a2_width_full;\n\n\tout_x = (x * kimage_ptr->scale_width_a2_to_x) >> 16;\n\tout_y = (y * kimage_ptr->scale_height_a2_to_x) >> 16;\n\tout_max_x = (max_x * kimage_ptr->scale_width_a2_to_x + 65535) >> 16;\n\tout_max_y = (max_y * kimage_ptr->scale_height_a2_to_x + 65535) >> 16;\n\tout_max_x = MY_MIN(out_max_x, out_width_act);\n\tout_max_y = MY_MIN(out_max_y, kimage_ptr->x_height);\n\tout_width = out_max_x - out_x;\n\tout_height = out_max_y - out_y;\n\tout_wptr = (word32 *)vptr;\n\trectptr->x = out_x;\n\trectptr->y = out_y;\n\trectptr->width = out_width;\n\trectptr->height = out_height;\n\talpha_mask = g_alpha_mask;\n\tfor(i = 0; i < out_height; i++) {\n\t\teff_y = out_y + i;\n\t\tpos_scale = kimage_ptr->scale_height[eff_y];\n\t\tsrc_y = pos_scale >> 16;\n\t\twptr = kimage_ptr->wptr + (src_y * a2_width_full);\n\t\tout_wptr = ((word32 *)vptr) + (eff_y * out_width_act) + out_x;\n\t\tfor(j = 0; j < out_width; j++) {\n\t\t\tnew_x = j + out_x;\n\t\t\tpos_scale = kimage_ptr->scale_width[new_x];\n\t\t\tpos = pos_scale >> 16;\n\t\t\t*out_wptr++ = wptr[pos] | alpha_mask;\n\t\t}\n\t}\n\trectptr->width = kimage_ptr->x_width - rectptr->x;\n\n\treturn 1;\n}\n\nint\nvideo_out_data_scaled(void *vptr, Kimage *kimage_ptr, int out_width_act,\n\t\t\t\t\tChange_rect *rectptr)\n{\n\tword32\t*out_wptr, *wptr;\n\tdword64\tdval0a, dval0b, dval1a, dval1b, dscale, dscale_y, dval;\n\tword32\tnew_val, pos_scale, alpha_mask;\n\tint\ta2_width_full, eff_y, src_y, x, y, new_x, out_x, out_y;\n\tint\tout_width, out_height, max_x, max_y, out_max_x, out_max_y, pos;\n\tint\ti, j;\n\n\tif((kimage_ptr->scale_width_a2_to_x >= 0x34000) ||\n\t\t\t(kimage_ptr->scale_height_a2_to_x >= 0x34000)) {\n\t\treturn video_out_data_intscaled(vptr, kimage_ptr,\n\t\t\t\t\t\tout_width_act, rectptr);\n\t}\n\tx = rectptr->x;\n\ty = rectptr->y;\n\tmax_x = rectptr->width + x;\n\tmax_y = rectptr->height + y;\n\tmax_x = MY_MIN(kimage_ptr->a2_width_full, max_x + 1);\n\tmax_y = MY_MIN(kimage_ptr->a2_height, max_y + 1);\n\tx = MY_MAX(0, x - 1);\n\ty = MY_MAX(0, y - 1);\n\ta2_width_full = kimage_ptr->a2_width_full;\n\n\tout_x = (x * kimage_ptr->scale_width_a2_to_x) >> 16;\n\tout_y = (y * kimage_ptr->scale_height_a2_to_x) >> 16;\n\tout_max_x = (max_x * kimage_ptr->scale_width_a2_to_x + 65535) >> 16;\n\tout_max_y = (max_y * kimage_ptr->scale_height_a2_to_x + 65535) >> 16;\n\tout_max_x = MY_MIN(out_max_x, out_width_act);\n\tout_max_y = MY_MIN(out_max_y, kimage_ptr->x_height);\n\tout_width = out_max_x - out_x;\n\tout_height = out_max_y - out_y;\n#if 0\n\tprintf(\"scaled: in %d,%d %d,%d becomes %d,%d %d,%d\\n\", x, y, width,\n\t\theight, out_x, out_y, out_width, out_height);\n#endif\n\tout_wptr = (word32 *)vptr;\n\trectptr->x = out_x;\n\trectptr->y = out_y;\n\trectptr->width = out_width;\n\trectptr->height = out_height;\n\talpha_mask = g_alpha_mask;\n\tfor(i = 0; i < out_height; i++) {\n\t\teff_y = out_y + i;\n\t\tpos_scale = kimage_ptr->scale_height[eff_y];\n\t\tsrc_y = pos_scale >> 16;\n\t\tdscale_y = (pos_scale & 0xffff) >> 8;\n\t\twptr = kimage_ptr->wptr + (src_y * a2_width_full);\n\t\tout_wptr = ((word32 *)vptr) + (eff_y * out_width_act) + out_x;\n\t\tfor(j = 0; j < out_width; j++) {\n\t\t\tnew_x = j + out_x;\n\t\t\tpos_scale = kimage_ptr->scale_width[new_x];\n\t\t\tpos = pos_scale >> 16;\n\t\t\tdscale = (pos_scale & 0xffff) >> 8;\n\t\t\tdval0a = wptr[pos];\n\t\t\tdval0a = (dval0a & 0x00ff00ffULL) |\n\t\t\t\t\t((dval0a & 0xff00ff00ULL) << 24);\n\t\t\tdval0b = wptr[pos + 1];\n\t\t\tdval0b = (dval0b & 0x00ff00ffULL) |\n\t\t\t\t\t((dval0b & 0xff00ff00ULL) << 24);\n\t\t\tdval1a = wptr[pos + a2_width_full];\n\t\t\tdval1a = (dval1a & 0x00ff00ffULL) |\n\t\t\t\t\t((dval1a & 0xff00ff00ULL) << 24);\n\t\t\tdval1b = wptr[pos + 1 + a2_width_full];\n\t\t\tdval1b = (dval1b & 0x00ff00ffULL) |\n\t\t\t\t\t((dval1b & 0xff00ff00ULL) << 24);\n\t\t\tdval0a = ((0x100 - dscale) * dval0a) +\n\t\t\t\t\t(dscale * dval0b);\n\t\t\tdval1a = ((0x100 - dscale) * dval1a) +\n\t\t\t\t\t(dscale * dval1b);\n\t\t\tdval0a = (dval0a >> 8) & 0x00ff00ff00ff00ffULL;\n\t\t\tdval1a = (dval1a >> 8) & 0x00ff00ff00ff00ffULL;\n\t\t\tdval = ((0x100 - dscale_y) * dval0a) +\n\t\t\t\t\t(dscale_y * dval1a);\n\t\t\tnew_val = ((dval >> 8) & 0x00ff00ffULL) |\n\t\t\t\t((dval >> 32) & 0xff00ff00ULL);\n\t\t\t*out_wptr++ = new_val | alpha_mask;\n#if 0\n\t\t\tif((pos == 300) && (eff_y == 100)) {\n\t\t\t\tprintf(\"x:%d pos:%d %08x.  %016llx,%016llx \"\n\t\t\t\t\t\"pos_sc:%08x, %08x\\n\", new_x, pos,\n\t\t\t\t\tnew_val, dval0a, dval0b, pos_scale,\n\t\t\t\t\twptr[pos]);\n\t\t\t}\n#endif\n\t\t}\n\t}\n\trectptr->width = kimage_ptr->x_width - rectptr->x;\n#if 0\n\tfor(i = 0; i < kimage_ptr->x_height; i++) {\n\t\tout_wptr = ((word32 *)vptr) + (i * out_width_act) +\n\t\t\t\t\t\tkimage_ptr->x_width - 1;\n\t\t*out_wptr = 0x00ff00ff;\n# if 0\n\t\tfor(j = 0; j < 10; j++) {\n\t\t\tif(*out_wptr != 0) {\n\t\t\t\tprintf(\"out_wptr:%p is %08x at %d,%d\\n\",\n\t\t\t\t\tout_wptr, *out_wptr,\n\t\t\t\t\tout_width_act - 1 - j, i);\n\t\t\t}\n\t\t\tout_wptr--;\n\t\t}\n# endif\n\t}\n#endif\n\n\treturn 1;\n}\n\nword32\nvideo_scale_calc_frac(int pos, word32 max, word32 frac_inc, word32 frac_inc_inv)\n{\n\tword32\tfrac, frac_to_next, new_frac;\n\n\tfrac = pos * frac_inc;\n\tif(frac >= max) {\n\t\treturn max;\t\t\t// Clear frac bits\n\t}\n\tif(g_video_scale_algorithm == 2) {\n\t\treturn frac & -65536;\t\t\t// nearest neighbor\n\t}\n\tif(g_video_scale_algorithm == 1) {\n\t\treturn frac;\t\t\t\t// bilinear interp\n\t}\n\t// Do proper scaling.  fraction=0 means 100% this pixel, fraction=ffff\n\t//  means 99.99% the next pixel\n\tfrac_to_next = frac_inc + (frac & 0xffff);\n\tif(frac_to_next < 65536) {\n\t\tfrac_to_next = 0;\n\t}\n\tfrac_to_next = (frac_to_next & 0xffff) * frac_inc_inv;\n\tfrac_to_next = frac_to_next >> 16;\n\tnew_frac = (frac & -65536) | (frac_to_next & 0xffff);\n#if 0\n\tif((frac >= (30 << 16)) && (frac < (38 << 16))) {\n\t\tprintf(\"scale %d (%02x) -> %08x (was %08x) %08x %08x\\n\",\n\t\t\tpos, pos, new_frac, frac, frac_inc, frac_inc_inv);\n\t}\n#endif\n\treturn new_frac;\n}\n\nvoid\nvideo_update_scale(Kimage *kimage_ptr, int out_width, int out_height,\n\t\t\t\t\t\t\tint must_update)\n{\n\tword32\tfrac_inc, frac_inc_inv, new_frac, max;\n\tint\ta2_width, a2_height, exp_width, exp_height;\n\tint\ti;\n\n\tout_width = video_clamp(out_width, 1, kimage_ptr->x_max_width);\n\tout_width = video_clamp(out_width, 1, MAX_SCALE_SIZE);\n\n\tout_height = video_clamp(out_height, 1, kimage_ptr->x_max_height);\n\tout_height = video_clamp(out_height, 1, MAX_SCALE_SIZE);\n\n\ta2_width = kimage_ptr->a2_width;\n\ta2_height = kimage_ptr->a2_height;\n\tkimage_ptr->vbl_of_last_resize = g_vbl_count;\n\n\t// Handle aspect ratio.  Calculate height/width based on the other's\n\t//  aspect ratio, and pick the smaller value\n\texp_width = (a2_width * out_height) / a2_height;\n\texp_height = (a2_height * out_width) / a2_width;\n\n\tif(exp_width < a2_width) {\n\t\texp_width = a2_width;\n\t}\n\tif(exp_height < a2_height) {\n\t\texp_height = a2_height;\n\t}\n\tif(exp_width < out_width) {\n\t\t// Allow off-by-one to be OK, so window doesn't keep resizing\n\t\tif((exp_width + 1) != out_width) {\n\t\t\tout_width = exp_width;\n\t\t}\n\t}\n\tif(exp_height < out_height) {\n\t\tif((exp_height + 1) != out_height) {\n\t\t\tout_height = exp_height;\n\t\t}\n\t}\n\tif(out_width <= 0) {\n\t\tout_width = 1;\n\t}\n\tif(out_height <= 0) {\n\t\tout_height = 1;\n\t}\n\n\t// See if anything changed.  If it's unchanged, don't do anything\n\tif((kimage_ptr->x_width == out_width) && !must_update &&\n\t\t\t(kimage_ptr->x_height == out_height)) {\n\t\treturn;\n\t}\n\tkimage_ptr->x_width = out_width;\n\tkimage_ptr->x_height = out_height;\n\tkimage_ptr->x_refresh_needed = 1;\n\tif(kimage_ptr == &g_mainwin_kimage) {\n\t\tg_mainwin_width = out_width;\n\t\tg_mainwin_height = out_height;\n\t\t//printf(\"Set g_mainwin_width=%d, g_mainwin_height=%d\\n\",\n\t\t//\t\t\t\tout_width, out_height);\n\t}\n\n\t// the per-pixel inc = a2_width / out_width.  Scale by 65536\n\tfrac_inc = (a2_width * 65536UL) / out_width;\n\tkimage_ptr->scale_width_to_a2 = frac_inc;\n\tfrac_inc_inv = (out_width * 65536UL) / a2_width;\n\tkimage_ptr->scale_width_a2_to_x = frac_inc_inv;\n#if 0\n\tprintf(\"scale_width_to_a2: %08x, a2_to_x:%08x, is_debugwin:%d\\n\",\n\t\tkimage_ptr->scale_width_to_a2, kimage_ptr->scale_width_a2_to_x,\n\t\t(kimage_ptr == &g_debugwin_kimage));\n#endif\n\tmax = (a2_width - 1) << 16;\n\tfor(i = 0; i < out_width + 1; i++) {\n\t\tnew_frac = video_scale_calc_frac(i, max, frac_inc,\n\t\t\t\t\t\t\t\tfrac_inc_inv);\n\t\tkimage_ptr->scale_width[i] = new_frac;\n\t}\n\n\tfrac_inc = (a2_height * 65536UL) / out_height;\n\tkimage_ptr->scale_height_to_a2 = frac_inc;\n\tfrac_inc_inv = (out_height * 65536UL) / a2_height;\n\tkimage_ptr->scale_height_a2_to_x = frac_inc_inv;\n#if 0\n\tprintf(\"scale_height_to_a2: %08x, a2_to_x:%08x. w:%d h:%d\\n\",\n\t\tkimage_ptr->scale_height_to_a2,\n\t\tkimage_ptr->scale_height_a2_to_x, out_width, out_height);\n#endif\n\tmax = (a2_height - 1) << 16;\n\tfor(i = 0; i < out_height + 1; i++) {\n\t\tnew_frac = video_scale_calc_frac(i, max, frac_inc,\n\t\t\t\t\t\t\t\tfrac_inc_inv);\n\t\tkimage_ptr->scale_height[i] = new_frac;\n\t}\n}\n\nint\nvideo_scale_mouse_x(Kimage *kimage_ptr, int raw_x, int x_width)\n{\n\tint\tx;\n\n\t// raw_x is in output coordinates.  Scale down to a2 coordinates\n\tif(x_width == 0) {\n\t\tx = (kimage_ptr->scale_width_to_a2 * raw_x) / 65536;\n\t} else {\n\t\t// Scale raw_x using x_width\n\t\tx = (raw_x * kimage_ptr->a2_width_full) / x_width;\n\t}\n\tx = x - BASE_MARGIN_LEFT;\n\treturn x;\n}\n\nint\nvideo_scale_mouse_y(Kimage *kimage_ptr, int raw_y, int y_height)\n{\n\tint\ty;\n\n\t// raw_y is in output coordinates.  Scale down to a2 coordinates\n\tif(y_height == 0) {\n\t\ty = (kimage_ptr->scale_height_to_a2 * raw_y) / 65536;\n\t} else {\n\t\t// Scale raw_y using y_height\n\t\ty = (raw_y * kimage_ptr->a2_height) / y_height;\n\t}\n\ty = y - BASE_MARGIN_TOP;\n\treturn y;\n}\n\nint\nvideo_unscale_mouse_x(Kimage *kimage_ptr, int a2_x, int x_width)\n{\n\tint\tx;\n\n\t// Convert a2_x to output coordinates\n\tx = a2_x + BASE_MARGIN_LEFT;\n\tif(x_width == 0) {\n\t\tx = (kimage_ptr->scale_width_a2_to_x * x) / 65536;\n\t} else {\n\t\t// Scale a2_x using x_width\n\t\tx = (x * x_width) / kimage_ptr->a2_width_full;\n\t}\n\treturn x;\n}\n\nint\nvideo_unscale_mouse_y(Kimage *kimage_ptr, int a2_y, int y_height)\n{\n\tint\ty;\n\n\t// Convert a2_y to output coordinates\n\ty = a2_y + BASE_MARGIN_TOP;\n\tif(y_height == 0) {\n\t\ty = (kimage_ptr->scale_height_a2_to_x * y) / 65536;\n\t} else {\n\t\t// Scale a2_y using y_height\n\t\ty = (y * y_height) / kimage_ptr->a2_height;\n\t}\n\treturn y;\n}\n\nvoid\nvideo_update_color_raw(int bank, int col_num, int a2_color)\n{\n\tword32\ttmp;\n\tint\tred, green, blue, newred, newgreen, newblue;\n\n\tif(col_num >= 256 || col_num < 0) {\n\t\thalt_printf(\"video_update_color_raw: col: %03x\\n\", col_num);\n\t\treturn;\n\t}\n\n\tred = (a2_color >> 8) & 0xf;\n\tgreen = (a2_color >> 4) & 0xf;\n\tblue = (a2_color) & 0xf;\n\tred = ((red << 4) + red);\n\tgreen = ((green << 4) + green);\n\tblue = ((blue << 4) + blue);\n\n\tnewred = red >> g_red_right_shift;\n\tnewgreen = green >> g_green_right_shift;\n\tnewblue = blue >> g_blue_right_shift;\n\n\ttmp = ((newred & g_red_mask) << g_red_left_shift) +\n\t\t\t((newgreen & g_green_mask) << g_green_left_shift) +\n\t\t\t((newblue & g_blue_mask) << g_blue_left_shift);\n\tg_palette_8to1624[bank][col_num] = tmp;\n}\n\nvoid\nvideo_update_status_line(int line, const char *string)\n{\n\tbyte\ta2_str_buf[STATUS_LINE_LENGTH+1];\n\tword32\t*wptr;\n\tchar\t*buf;\n\tconst char *ptr;\n\tword32\tline_bytes;\n\tint\tstart_line, c, pixels_per_line, offset;\n\tint\ti;\n\n\tif(line >= MAX_STATUS_LINES || line < 0) {\n\t\tprintf(\"update_status_line: line: %d!\\n\", line);\n\t\texit(1);\n\t}\n\n\tptr = string;\n\tbuf = &(g_status_buf[line][0]);\n\tg_status_ptrs[line] = buf;\n\tfor(i = 0; i < STATUS_LINE_LENGTH; i++) {\n\t\tif(*ptr) {\n\t\t\tc = *ptr++;\n\t\t} else {\n\t\t\tc = ' ';\n\t\t}\n\t\tbuf[i] = c;\n\t\ta2_str_buf[i] = c | 0x80;\n\t}\n\n\tbuf[STATUS_LINE_LENGTH] = 0;\n\ta2_str_buf[STATUS_LINE_LENGTH] = 0;\n\tstart_line = (200 + 2*8) + line*8;\n\tpixels_per_line = g_mainwin_kimage.a2_width_full;\n\toffset = (pixels_per_line * g_video_act_margin_top);\n\twptr = g_mainwin_kimage.wptr;\n\twptr += offset;\n\tfor(i = 0; i < 8; i++) {\n\t\tline_bytes = ((start_line + i) << 16) | (40 << 8) | 0;\n\t\tredraw_changed_string(&(a2_str_buf[0]), line_bytes, -1L,\n\t\t\twptr, 0, 0x00ffffff, pixels_per_line, 1);\n\t}\n\n\t// Don't add rectangle here, video_form_change_rects will do it\n\t//video_add_a2_rect(start_line, start_line + 8, 0, 640);\n}\n\nvoid\nvideo_draw_a2_string(int line, const byte *bptr)\n{\n\tword32\t*wptr;\n\tword32\tline_bytes;\n\tint\tstart_line, pixels_per_line, offset;\n\tint\ti;\n\n\tstart_line = line*8;\n\tpixels_per_line = g_mainwin_kimage.a2_width_full;\n\toffset = (pixels_per_line * g_video_act_margin_top) +\n\t\t\t\t\tg_video_act_margin_left;\n\twptr = g_mainwin_kimage.wptr;\n\twptr += offset;\n\tfor(i = 0; i < 8; i++) {\n\t\tline_bytes = ((start_line + i) << 16) | (40 << 8) | 0;\n\t\tredraw_changed_string(bptr, line_bytes, -1L,\n\t\t\twptr, 0, 0x00ffffff, pixels_per_line, 1);\n\t}\n\tg_mainwin_kimage.x_refresh_needed = 1;\n}\n\nvoid\nvideo_show_debug_info()\n{\n\tword32\ttmp1;\n\n\tprintf(\"g_cur_dfcyc: %016llx, last_vbl: %016llx\\n\", g_cur_dfcyc,\n\t\t\t\t\t\t\tg_last_vbl_dfcyc);\n\ttmp1 = get_lines_since_vbl(g_cur_dfcyc);\n\tprintf(\"lines since vbl: %06x\\n\", tmp1);\n\tprintf(\"Last line updated: %d\\n\", g_vid_update_last_line);\n}\n\nword32\nread_video_data(dword64 dfcyc)\n{\n\tword32\tval, val2;\n\tint\tlines_since_vbl, line;\n\n\t// Return Charrom data at $C02C for SuperConvert 4 TDM mode\n\tlines_since_vbl = get_lines_since_vbl(dfcyc);\n\tval = float_bus_lines(dfcyc, lines_since_vbl);\n\tline = lines_since_vbl >> 8;\n\tif(line < 192) {\n\t\t// Always do the character ROM\n\t\tval2 = g_a2font_bits[val & 0xff][line & 7];\n\t\tdbg_log_info(dfcyc, val,\n\t\t\t(lines_since_vbl << 8) | (val2 & 0xff), 0xc02c);\n\t\tval = ~val2;\t\t// Invert it, maybe\n\t}\n\treturn val & 0xff;\n}\n\nword32\nfloat_bus(dword64 dfcyc)\n{\n\tword32\tlines_since_vbl;\n\n\tlines_since_vbl = get_lines_since_vbl(dfcyc);\n\treturn float_bus_lines(dfcyc, lines_since_vbl);\n}\n\nword32\nfloat_bus_lines(dword64 dfcyc, word32 lines_since_vbl)\n{\n\tword32\tval;\n\tint\tline, eff_line, line24, all_stat, byte_offset;\n\tint\thires, page2, addr;\n\n/* For floating bus, model hires style: Visible lines 0-191 are simply the */\n/* data being displayed at that time.  Lines 192-255 are lines 0 - 63 again */\n/*  and lines 256-261 are lines 58-63 again */\n/* For each line, figure out starting byte at -25 mod 128 bytes from this */\n/*  line's start */\n/* This emulates an Apple II style floating bus.  A real IIgs does not */\n/*  drive anything meaningful during the 25 horizontal blanking cycles, */\n/*  nor during veritical blanking.  The data seems to be 0 or related to */\n/*  the instruction fetches on a real IIgs during blankings */\n\n\tline = lines_since_vbl >> 8;\n\tbyte_offset = lines_since_vbl & 0xff;\n\t// byte offset is from 0 through 64, where the visible screen is drawn\n\t//  from 25 through 64\n\n\teff_line = line;\n\tif(eff_line >= 0x100) {\n\t\teff_line = (eff_line - 6) & 0xff;\n\t}\n\tif(byte_offset == 0) {\n\t\tbyte_offset = 1;\n\t}\n\tall_stat = g_cur_a2_stat;\n\thires = (all_stat & ALL_STAT_HIRES) && !(all_stat & ALL_STAT_TEXT);\n\tif((all_stat & ALL_STAT_MIX_T_GR) && (line >= 160)) {\n\t\thires = 0;\n\t}\n\tpage2 = EXTRU(all_stat, 31 - BIT_ALL_STAT_PAGE2, 1);\n\tif(all_stat & ALL_STAT_ST80) {\n\t\tpage2 = 0;\n\t}\n\n\tline24 = (eff_line >> 3) & 0x1f;\n\taddr = g_screen_index[line24] & 0x3ff;\n\taddr = (addr & 0x380) + (((addr & 0x7f) - 25 + byte_offset) & 0x7f);\n\tif(hires) {\n\t\taddr = 0x2000 + addr + ((eff_line & 7) << 10) + (page2 << 13);\n\t} else {\n\t\taddr = 0x400 + addr + (page2 << 10);\n\t}\n\n\tval = g_slow_memory_ptr[addr];\n#if 0\n\tprintf(\"For %04x (%d) addr=%04x, val=%02x, dfcyc:%016llx\\n\",\n\t\tlines_since_vbl, eff_line, addr, val, dfcyc - g_last_vbl_dfcyc);\n#endif\n\tdbg_log_info(dfcyc, ((lines_since_vbl >> 11) << 24) |\n\t\t\t(lines_since_vbl - 25), (addr << 8) | val, 0xff);\n\n\treturn val;\n}\n"
  },
  {
    "path": "upstream/kegs/src/voc.c",
    "content": "const char rcsid_voc_c[] = \"@(#)$KmKId: voc.c,v 1.12 2023-09-23 17:52:44+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// This file provides emulation of the Apple Video Overlay Card, which\n//  will appear to be in slot 3 if g_voc_enable=1 (there's a config.c\n//  setting to control enabling VOC).  The only currently supported VOC\n//  feature is the SHR interlaced display using both Main and Aux memory\n//  to provide a 640x400 (or 320x400) pixel display.\n\n#include \"defc.h\"\n\nextern word32 g_c02b_val;\nextern int g_cur_a2_stat;\nextern word32 g_vbl_count;\n\nint\tg_voc_enable = 0;\t\t// Default to disabled for now\nword32\tg_voc_reg1 = 0x09;\nword32\tg_voc_reg3 = 0;\nword32\tg_voc_reg4 = 0;\nword32\tg_voc_reg5 = 0;\nword32\tg_voc_reg6 = 0;\n\nword32\nvoc_devsel_read(word32 loc, dword64 dfcyc)\n{\n\t// Reads to $c0b0-$c0bf.\n\tloc = loc & 0xf;\n\tswitch(loc) {\n\tcase 0:\t\t// 0xc0b0\n\t\treturn voc_read_reg0(dfcyc);\n\t\tbreak;\n\tcase 1:\t\t// 0xc0b1\n\t\treturn g_voc_reg1;\n\t\tbreak;\n\tcase 3:\t\t// 0xc0b3\n\t\treturn g_voc_reg3;\n\t\tbreak;\n\tcase 4:\t\t// 0xc0b4\n\t\treturn g_voc_reg4;\n\t\tbreak;\n\tcase 5:\t\t// 0xc0b5\n\t\treturn g_voc_reg5;\n\t\tbreak;\n\tcase 6:\t\t// 0xc0b6\n\t\treturn g_voc_reg6;\n\t\tbreak;\n\tcase 7:\t\t// 0xc0b7, possible Uthernet 2 detection\n\t\treturn 0x00;\n\t\tbreak;\n\tcase 8:\t\t// 0xc0b8, Second Sight detection by jpeGS program\n\t\treturn 0x00;\t\t// Second Sight returns 0x01\n\t\tbreak;\n\tcase 0xd:\t// 0xc0bd, A2OSX Uthernet 1 detection code\n\t\treturn 0x00;\n\t\tbreak;\n\t}\n\n\thalt_printf(\"Tried to read: %04x\\n\", 0xc0b0 + loc);\n\n\treturn 0;\n}\n\nvoid\nvoc_devsel_write(word32 loc, word32 val, dword64 dfcyc)\n{\n\t// Writes to $c0b0-$c0bf.\n\tloc = loc & 0xf;\n\tswitch(loc) {\n\tcase 0:\t\t// 0xc0b0\n\t\t// Write 0 to clear VBL interrupts\n\t\tif(val != 0) {\n\t\t\thalt_printf(\"VOC write %04x = %02x\\n\", loc, val);\n\t\t}\n\t\treturn;\n\t\tbreak;\n\tcase 1:\t\t// 0xc0b1\n\t\t// bit 0: R/W: 1=GG Bus Enable\n\t\t//\tWhen 0, I think VOC ignores writes to $c023,etc.\n\t\t// bit 2: R/W: 0=OutChromaFilter enabled, 1=ChromaFilter disab\n\t\t//   bit 2 is also TextMonoOver somehow using bit[5]==1\n\t\t// bit 3: R/W: 1=MainPageLin\n\t\t// bits 5:4: R/W: 00=Aux mem; 01=Main Memory; 11=Interlaced\n\t\t// bit 6: R/W: 1=Enable VBL Interrupt\n\t\t// bit 7: R/W: 1=Enable Line interrupts\n\t\tif(!g_voc_enable) {\n\t\t\tval = 0;\n\t\t}\n\t\tif(val & 0xc0) {\n\t\t\thalt_printf(\"VOC write %04x = %02x\\n\", loc, val);\n\t\t}\n#if 0\n\t\tif(val != g_voc_reg1) {\n\t\t\tprintf(\"$c0b1:%02x (was %02x)\\n\", val, g_voc_reg1);\n\t\t}\n#endif\n\t\tg_voc_reg1 = val;\n\t\tvoc_update_interlace(dfcyc);\n\t\treturn;\n\t\tbreak;\n\tcase 3:\t\t// 0xc0b3\n\t\t// bits 2:0: R/W: Key Dissolve, 0=100% graphics, 7=100% video\n\t\t// bit 3: R/W: 1=Enhanced Dissolve enabled\n\t\t// bits 6:4: R/W: Non-Key Dissolve, 0=100% graphics, 7=100% vid\n\t\t// bit 7: R/W: 0=Output Setup Enabled, 1=Output Setup Disabled\n\t\tg_voc_reg3 = val;\n\t\treturn;\n\t\tbreak;\n\tcase 4:\t\t// 0xc0b4\n\t\t// bits 3:0: R/W: KeyColor Blue\n\t\t// bits 7:4: R/W: KeyColor Green\n\t\tg_voc_reg4 = val;\n\t\treturn;\n\t\tbreak;\n\tcase 5:\t\t// 0xc0b5\n\t\t// bits 3:0: R/W: KeyColor Red\n\t\t// bit 4: R/W: OutExtBlank: 0=Graphics, 1=External\n\t\t// bit 5: R/W: 0=GenLock enabled, 1=GenLock disabled\n\t\t// bit 6: R/W: 0=KeyColor enabled, 1=KeyColor disabled\n\t\t// bit 7: R/W: 1=Interlace mode enabled\n\t\tg_voc_reg5 = val;\n\t\tvoc_update_interlace(dfcyc);\n\t\treturn;\n\t\tbreak;\n\tcase 6:\t\t// 0xc0b6\n\t\t// Write 0 to cause AdjSave to occur\n\t\t// Write 8, then 9, then 8 again to cause AdjInc for Hue\n\t\t// Write a, then b, then a again to cause AdjDec for Hue\n\t\t// Write 4, then 5, then 4 again to cause AdjInc for Saturation\n\t\t// Write 6, then 7, then 6 again to cause AdjDec for Saturation\n\t\t// bit 3: hue, bit 2: saturation\n\t\tg_voc_reg6 = val;\n\t\treturn;\n\t\tbreak;\n\tcase 7:\t\t// 0xc0b7\n\t\t// Written by System Disk 1.1 Desktop.sys to 0xfd, ignore\n\t\tif(val == 0xfd) {\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\tcase 0xa:\n\tcase 0xb:\t// 0xc0ba,0xc0bb written to 0 by A2OSX Uthernet1 detect\n\t\tif(val == 0) {\n\t\t\treturn;\n\t\t}\n\t\tbreak;\n\t}\n\thalt_printf(\"Unknown Write %04x = %02x %016llx\\n\", 0xc0b0 + loc, val,\n\t\t\t\t\t\t\t\tdfcyc);\n}\n\nvoid\nvoc_iosel_c300_write(word32 loc, word32 val, dword64 dfcyc)\n{\n\t// Writes to $c300-$c3ff\n\thalt_printf(\"Wrote VOC %04x = %02x %016llx\\n\", 0xc300 + (loc & 0xff),\n\t\t\t\t\t\t\t\tval, dfcyc);\n}\n\nvoid\nvoc_reset()\n{\n\tg_voc_reg1 = 0x0d;\t\t// [0]: GG Bus enable, [3]:MainPageLin\n\tg_voc_reg3 = 0x07;\n\tg_voc_reg4 = 0;\n\tg_voc_reg5 = 0x40;\n\tg_voc_reg6 = 0;\n}\n\ndouble g_voc_last_pal_vbl = 0;\n\nword32\nvoc_read_reg0(dword64 dfcyc)\n{\n\tword32\tframe, in_vbl;\n\n\tif(!g_voc_enable) {\n\t\treturn 0;\n\t}\n\t// Reading $c0b0.\n\t// c0b0: bit 2: R/O: 1=In VBL\n\t// c0b0: bit 3: R/O: 0=No Video Detected, 1=Video Detected\n\t// c0b0: bit 4: R/O: 1=Video Genlocked\n\t// c0b0: bit 5: R/O: 0=showing Field 0, 1=showing Field 1\n\t// c0b0: bit 6: R/O: 1=VBL Int Request pending\n\t// c0b0: bit 7: R/O: 1=Line Int Request pending\n\tin_vbl = in_vblank(dfcyc);\n\tdbg_log_info(dfcyc, 0, in_vbl, 0x1c0b0);\n\tframe = g_vbl_count & 1;\n\treturn (frame << 5) | (in_vbl << 2);\n}\n\nvoid\nvoc_update_interlace(dword64 dfcyc)\n{\n\tword32\tnew_stat, mask;\n\n\tnew_stat = 0;\n\tif(((g_voc_reg1 & 0x30) == 0x30) && (g_voc_reg5 & 0x80)) {\n\t\tnew_stat = ALL_STAT_VOC_INTERLACE;\n\t}\n\tif((g_voc_reg1 & 0x30) == 0x10) {\t\t// Draw SHR from mainmem\n\t\tnew_stat = ALL_STAT_VOC_MAIN;\n\t}\n\tmask = ALL_STAT_VOC_INTERLACE | ALL_STAT_VOC_MAIN;\n\tif((g_cur_a2_stat ^ new_stat) & mask) {\n\t\t// Interlace mode has changed\n\t\tg_cur_a2_stat &= (~mask);\n\t\tg_cur_a2_stat |= new_stat;\n\t\tprintf(\"Change VOC interlace mode: %08x\\n\", new_stat);\n\t\tchange_display_mode(dfcyc);\n\t}\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/win32snd_driver.c",
    "content": "const char rcsid_win32snd_driver_c[] = \"@(#)$KmKId: win32snd_driver.c,v 1.12 2023-05-19 14:01:33+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2023 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// Audio is sent every 1/60th of a second to win32_send_audio().  Play it\n//  using waveOutWrite() as long as we've got at least 2/60th of a second\n//  buffered--otherwise waveOutPause().  If we get more than 10 buffers\n//  queued, drop buffers until we get down to 6 buffers queued again.\n\n// Track headers completed in g_wavehdr_rd_pos using callback function.\n\n#include \"defc.h\"\n#include \"sound.h\"\n\n#include <windows.h>\n#include <mmsystem.h>\n\nextern int Verbose;\n\nextern int g_audio_rate;\n\nunsigned int __stdcall child_sound_loop_win32(void *param);\nvoid check_wave_error(int res, char *str);\n\n#define NUM_WAVE_HEADERS\t32\n\nHWAVEOUT g_wave_handle;\nWAVEHDR g_wavehdr[NUM_WAVE_HEADERS];\n// Each header is for 1/60th of a second of sound (generally).  Pause\n//  until 2 headers are available, then unpause.  Experimentally it appears\n//  we keep about 5 headers (5/60th of a second = 80msec) ahead, which is\n//  excellent latency\n\nextern int g_audio_enable;\nextern word32 *g_sound_shm_addr;\nextern int g_preferred_rate;\n\nint\tg_win32snd_buflen = 0x1000;\nint\tg_win32_snd_playing = 0;\nint\tg_win32_snd_to_drop = 0;\nword32\tg_win32_snd_dropped = 0;\nvolatile int g_wavehdr_rd_pos = 0;\nvolatile int g_wavehdr_wr_pos = 0;\n\nvoid\nwin32snd_init(word32 *shmaddr)\n{\n\tprintf(\"win32snd_init\\n\");\n\tchild_sound_init_win32();\n}\n\nvoid\nwin32snd_shutdown()\n{\n\t/* hmm */\n\n}\n\nvoid CALLBACK\nhandle_wav_snd(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,\n\t\t\t\t\tDWORD_PTR dwParam1, DWORD_PTR dwParam2)\n{\n\tLPWAVEHDR lpwavehdr;\n\tint\tpos;\n\n\t/* Only service \"buffer done playing messages */\n\tif(uMsg == WOM_DONE) {\n\t\tlpwavehdr = (LPWAVEHDR)dwParam1;\n\t\tif(lpwavehdr->dwFlags == (WHDR_DONE | WHDR_PREPARED)) {\n\t\t\tlpwavehdr->dwUser = FALSE;\n\t\t}\n\t\tpos = (int)(lpwavehdr - &g_wavehdr[0]);\n\t\t// printf(\"At %.3f, pos %d is done\\n\", get_dtime(), pos);\n\t\tif(pos == g_wavehdr_rd_pos) {\n\t\t\tpos = (pos + 1) % NUM_WAVE_HEADERS;\n\t\t\tg_wavehdr_rd_pos = pos;\n\t\t} else {\n\t\t\tprintf(\"wavehdr %d finished, exp %d\\n\", pos,\n\t\t\t\t\t\t\tg_wavehdr_rd_pos);\n\t\t}\n\t}\n\n\treturn;\n}\n\n\nvoid\ncheck_wave_error(int res, char *str)\n{\n\tchar\tbuf[256];\n\n\tif(res == MMSYSERR_NOERROR) {\n\t\treturn;\n\t}\n\n\twaveOutGetErrorText(res, &buf[0], sizeof(buf));\n\tprintf(\"%s: %s\\n\", str, buf);\n\texit(1);\n}\n\nvoid\nchild_sound_init_win32()\n{\n\tWAVEFORMATEX wavefmt;\n\tWAVEOUTCAPS caps;\n\tbyte\t*bptr;\n\tUINT\twave_id;\n\tint\tbits_per_sample, channels, block_align, blen, res;\n\tint\ti;\n\n\tmemset(&wavefmt, 0, sizeof(WAVEFORMATEX));\n\n\twavefmt.wFormatTag = WAVE_FORMAT_PCM;\n\tbits_per_sample = 16;\n\tchannels = 2;\n\twavefmt.wBitsPerSample = bits_per_sample;\n\twavefmt.nChannels = channels;\n\twavefmt.nSamplesPerSec = g_preferred_rate;\n\tblock_align = channels * (bits_per_sample / 8);\n\twavefmt.nBlockAlign = block_align;\n\twavefmt.nAvgBytesPerSec = block_align * g_audio_rate;\n\n\tres = waveOutOpen(&g_wave_handle, WAVE_MAPPER, &wavefmt, 0, 0,\n\t\t\t\tWAVE_FORMAT_QUERY);\n\n\tif(res != MMSYSERR_NOERROR) {\n\t\tprintf(\"Cannot open audio device, res:%d, g_audio_rate:%d\\n\",\n\t\t\t\t\t\tres, g_preferred_rate);\n\t\tg_audio_enable = 0;\n\t\treturn;\n\t}\n\n\tres = waveOutOpen(&g_wave_handle, WAVE_MAPPER, &wavefmt,\n\t\t(DWORD_PTR)handle_wav_snd, 0,\n\t\tCALLBACK_FUNCTION | WAVE_ALLOWSYNC);\n\n\tif(res != MMSYSERR_NOERROR) {\n\t\tprintf(\"Cannot register audio\\n\");\n\t\tg_audio_enable = 0;\n\t\treturn;\n\t}\n\n\tg_audio_rate = wavefmt.nSamplesPerSec;\n\tblen = (((g_audio_rate * block_align) / 60) * 5) / 4;\n\t\t// Size buffer 25% larger than expected, to add some margin\n\tblen = (blen + 15) & -16L;\n\n\tg_win32snd_buflen = blen;\n\tbptr = malloc(blen * NUM_WAVE_HEADERS);\n\tif(bptr == NULL) {\n\t\tprintf(\"Unabled to allocate sound buffer\\n\");\n\t\texit(1);\n\t}\n\n\tfor(i = 0; i < NUM_WAVE_HEADERS; i++) {\n\t\tmemset(&g_wavehdr[i], 0, sizeof(WAVEHDR));\n\t\tg_wavehdr[i].dwUser = FALSE;\n\t\tg_wavehdr[i].lpData = (char *)&(bptr[i * blen]);\n\t\tg_wavehdr[i].dwBufferLength = blen;\n\t\tg_wavehdr[i].dwFlags = 0;\n\t\tg_wavehdr[i].dwLoops = 0;\n\t\tres = waveOutPrepareHeader(g_wave_handle, &g_wavehdr[i],\n\t\t\t\t\t\tsizeof(WAVEHDR));\n\t\tcheck_wave_error(res, \"waveOutPrepareHeader\");\n\t}\n\n\tres = waveOutGetID(g_wave_handle, &wave_id);\n\tres = waveOutGetDevCaps(wave_id, &caps, sizeof(caps));\n\tcheck_wave_error(res, \"waveOutGetDevCaps\");\n\tprintf(\"Using %s, buflen:%d\\n\", caps.szPname, g_win32snd_buflen);\n\tprintf(\" Bits per Sample = %d.  Channels = %d\\n\",\n\t\twavefmt.wBitsPerSample, wavefmt.nChannels);\n\tprintf(\" Sampling rate = %d, avg_bytes_per_sec = %d\\n\",\n\t\t(int)wavefmt.nSamplesPerSec, (int)wavefmt.nAvgBytesPerSec);\n\n\tsound_set_audio_rate(g_audio_rate);\n}\n\nvoid\nwin32snd_set_playing(int snd_playing)\n{\n\tg_win32_snd_playing = snd_playing;\n\tif(snd_playing) {\n\t\twaveOutRestart(g_wave_handle);\n#if 0\n\t\tprintf(\"win32 restarted sound wr:%d rd:%d\\n\", g_wavehdr_wr_pos,\n\t\t\t\t\t\t\tg_wavehdr_rd_pos);\n#endif\n\t} else {\n\t\twaveOutPause(g_wave_handle);\n#if 0\n\t\tprintf(\"win32 paused sound wr:%d rd:%d\\n\", g_wavehdr_wr_pos,\n\t\t\t\t\t\t\tg_wavehdr_rd_pos);\n#endif\n\t}\n}\n\nvoid\nwin32_send_audio2(byte *ptr, int size)\n{\n\tint\tres, wr_pos, rd_pos, new_pos, bufs_in_use;\n\n\twr_pos = g_wavehdr_wr_pos;\n\trd_pos = g_wavehdr_rd_pos;\n#if 0\n\tif(wr_pos == 0) {\n\t\tprintf(\"send_audio2 wr:%d rd:%d sz:%d at %.3f\\n\", wr_pos,\n\t\t\t\trd_pos, size, get_dtime());\n\t}\n#endif\n\tif(g_wavehdr[wr_pos].dwUser != FALSE) {\n\t\t// Audio buffer busy...should not happen!\n\t\tprintf(\"Audio buffer %d is busy!\\n\", wr_pos);\n\t\treturn;\n\t}\n\n\tbufs_in_use = (NUM_WAVE_HEADERS + wr_pos - rd_pos) % NUM_WAVE_HEADERS;\n\tif(g_win32_snd_to_drop) {\n\t\tg_win32_snd_to_drop--;\n\t\tg_win32_snd_dropped += size;\n\t\tif((bufs_in_use < 4) && (g_win32_snd_to_drop != 0)) {\n#if 0\n\t\t\tprintf(\"bufs_in_use:%d, snd_to_drop:%d\\n\", bufs_in_use,\n\t\t\t\t\t\t\tg_win32_snd_to_drop);\n#endif\n\t\t\tg_win32_snd_to_drop = 0;\n\t\t}\n\t\tif(g_win32_snd_to_drop == 0) {\n\t\t\tprintf(\"Dropped %d bytes of sound\\n\",\n\t\t\t\t\t\tg_win32_snd_dropped);\n\t\t}\n\t\treturn;\n\t}\n#if 0\n\tif(g_win32_snd_playing && (bufs_in_use <= 2)) {\n\t\tprintf(\"bufs_in_use:%d, wr:%d, rd:%d\\n\", bufs_in_use, wr_pos,\n\t\t\t\t\t\t\t\trd_pos);\n\t}\n#endif\n\tif(bufs_in_use == 0) {\n#if 0\n\t\tprintf(\"bufs_in_use:%d, wr_pos:%d rd_pos:%d\\n\", bufs_in_use,\n\t\t\twr_pos, rd_pos);\n#endif\n\t\t// We've underflowed, so pause sound until we get some buffered\n\t\twin32snd_set_playing(0);\n\t} else if(g_win32_snd_playing == 0) {\n\t\tif(bufs_in_use >= 2) {\n\t\t\t//printf(\"bufs_in_use:%d, will start\\n\", bufs_in_use);\n\t\t\twin32snd_set_playing(1);\n\t\t}\n\t} else {\n\t\tif(bufs_in_use >= 14) {\t\t// About 230msec\n\t\t\t// Drop 6 buffers to get us back down to 100msec delay\n\t\t\tprintf(\"bufs_in_use:%d, wr:%d will drop 6\\n\",\n\t\t\t\t\t\t\tbufs_in_use, wr_pos);\n\t\t\tg_win32_snd_to_drop = 6;\n\t\t\tg_win32_snd_dropped = 0;\n\t\t}\n\t}\n\tmemcpy(g_wavehdr[wr_pos].lpData, ptr, size);\n\tg_wavehdr[wr_pos].dwBufferLength = size;\n\tg_wavehdr[wr_pos].dwUser = TRUE;\n\n\tnew_pos = (wr_pos + 1) % NUM_WAVE_HEADERS;\n\tg_wavehdr_wr_pos = new_pos;\n\tres = waveOutWrite(g_wave_handle, &g_wavehdr[wr_pos],\n\t\t\t\t\t\t\tsizeof(g_wavehdr));\n\tcheck_wave_error(res, \"waveOutWrite\");\n\n\treturn;\n}\n\nint\nwin32_send_audio(byte *ptr, int in_size)\n{\n\tint\tsize;\n\tint\ttmpsize;\n\n\t// printf(\"send_audio %d bytes at %.3f\\n\", in_size, get_dtime());\n\tsize = in_size;\n\twhile(size > 0) {\n\t\ttmpsize = size;\n\t\tif(size > g_win32snd_buflen) {\n\t\t\ttmpsize = g_win32snd_buflen;\n\t\t}\n\t\twin32_send_audio2(ptr, tmpsize);\n\t\tptr += tmpsize;\n\t\tif(size != tmpsize) {\n#if 0\n\t\t\tprintf(\"Orig size:%d, reduced to %d\\n\", in_size,\n\t\t\t\t\t\t\t\ttmpsize);\n#endif\n\t\t}\n\t\tsize = size - tmpsize;\n\t}\n\n\treturn in_size;\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/win_dirent.h",
    "content": "#ifdef INCLUDE_RCSID_C\nconst char rcsid_kegswin_dirent_h[] = \"@(#)$KmKId: win_dirent.h,v 1.2 2022-02-11 04:13:45+00 kentd Exp $\";\n#endif\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2022 by Kent Dickey\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// Hacky defines to get something to compile for now\ntypedef unsigned short mode_t;\n\nstruct dirent {\n\tchar\td_name[1024];\n};\n\nstruct DIR_t {\n\tint\tfind_data_valid;\n\tvoid\t*win_handle;\n\tvoid\t*find_data_ptr;\n\tstruct dirent dirent;\n};\ntypedef struct DIR_t DIR;\n\nDIR *opendir(const char *filename);\nstruct dirent *readdir(DIR *dirp);\nint closedir(DIR *dirp);\n\n"
  },
  {
    "path": "upstream/kegs/src/windriver.c",
    "content": "const char rcsid_windriver_c[] = \"@(#)$KmKId: windriver.c,v 1.26 2024-09-15 13:55:35+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2024 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// Based on code from Chea Chee Keong from KEGS32, which was available at\n//  http://www.geocities.com/akilgard/kegs32 (geocities is gone now)\n\n#define WIN32_LEAN_AND_MEAN\t/* Tell windows we want less header gunk */\n#define STRICT\t\t\t/* Tell Windows we want compile type checks */\n\n#include <windows.h>\n#include <windowsx.h>\n#include <mmsystem.h>\n#include <winsock.h>\n#include <commctrl.h>\n#include <io.h>\t\t\t/* For _get_osfhandle */\n\n#include \"defc.h\"\n#include \"win_dirent.h\"\n\nextern int Verbose;\n\ntypedef struct windowinfo {\n\tHWND\twin_hwnd;\n\tHDC\twin_dc;\n\tHDC\twin_cdc;\n\tBITMAPINFO *win_bmapinfo_ptr;\n\tBITMAPINFOHEADER *win_bmaphdr_ptr;\n\tHBITMAP\twin_dev_handle;\n\n\tKimage\t*kimage_ptr;\t// KEGS Image pointer for window content\n\tchar\t*name_str;\n\tbyte\t*data_ptr;\n\tint\tmotion;\n\tint\tmdepth;\n\tint\tactive;\n\tint\tpixels_per_line;\n\tint\tx_xpos;\n\tint\tx_ypos;\n\tint\twidth;\n\tint\theight;\n\tint\textra_width;\n\tint\textra_height;\n} Window_info;\n\n#include \"protos_windriver.h\"\n\nWindow_info g_mainwin_info = { 0 };\nWindow_info g_debugwin_info = { 0 };\n\nint\tg_win_max_width = 0;\nint\tg_win_max_height = 0;\nint\tg_num_a2_keycodes = 0;\n\nint\tg_win_button_states = 0;\nint\tg_win_hide_pointer = 0;\nint\tg_win_warp_pointer = 0;\nint\tg_win_warp_x = 0;\nint\tg_win_warp_y = 0;\n\n\n/* this table is used to search for the Windows VK_* in col 1 or 2 */\n/* flags bit 8 is or'ed into the VK, so we can distinguish keypad keys */\n/* regardless of numlock */\nint g_a2_key_to_wsym[][3] = {\n\t{ 0x35,\tVK_ESCAPE,\t0 },\n\t{ 0x7a,\tVK_F1,\t0 },\n\t{ 0x78,\tVK_F2,\t0 },\n\t{ 0x63,\tVK_F3,\t0 },\n\t{ 0x76,\tVK_F4,\t0 },\n\t{ 0x60,\tVK_F5,\t0 },\n\t{ 0x61,\tVK_F6,\t0 },\n\t{ 0x62,\tVK_F7,\t0 },\n\t{ 0x64,\tVK_F8,\t0 },\n\t{ 0x65,\tVK_F9,\t0 },\n\t{ 0x6d,\tVK_F10,\t0 },\n\t{ 0x67,\tVK_F11,\t0 },\n\t{ 0x6f,\tVK_F12,\t0 },\n\t{ 0x69,\tVK_F13,\t0 },\n\t{ 0x6b,\tVK_F14,\t0 },\n\t{ 0x71,\tVK_F15,\t0 },\n\t{ 0x7f, VK_PAUSE, VK_CANCEL+0x100 },\t// Reset\n\n\t{ 0x12,\t'1', 0 },\n\t{ 0x13,\t'2', 0 },\n\t{ 0x14,\t'3', 0 },\n\t{ 0x15,\t'4', 0 },\n\t{ 0x17,\t'5', 0 },\n\t{ 0x16,\t'6', 0 },\n\t{ 0x1a,\t'7', 0 },\n\t{ 0x1c,\t'8', 0 },\n\t{ 0x19,\t'9', 0 },\n\t{ 0x1d,\t'0', 0 },\n\t{ 0x1b,\t0xbd, 0 },\t\t/* '-' */\n\t{ 0x18,\t0xbb, 0 },\t\t/* '=' */\n\t{ 0x33,\tVK_BACK, 0 },\t\t/* backspace */\n\t{ 0x72,\tVK_INSERT+0x100, 0 },\t/* Insert key */\n\t{ 0x74,\tVK_PRIOR+0x100, 0 },\t/* pageup */\n\t{ 0x47,\tVK_NUMLOCK, VK_NUMLOCK+0x100 },\t/* clear */\n\t{ 0x51,\tVK_HOME+0x100, 0 },\t\t/* KP_equal is HOME key */\n\t{ 0x4b,\tVK_DIVIDE, VK_DIVIDE+0x100 },\t\t// KP /\n\t{ 0x43,\tVK_MULTIPLY, VK_MULTIPLY+0x100 },\t// KP *\n\n\t{ 0x30,\tVK_TAB, 0 },\n\t{ 0x32,\t0xc0, 0 },\t\t/* '`' */\n\t{ 0x0c,\t'Q', 0 },\n\t{ 0x0d,\t'W', 0 },\n\t{ 0x0e,\t'E', 0 },\n\t{ 0x0f,\t'R', 0 },\n\t{ 0x11,\t'T', 0 },\n\t{ 0x10,\t'Y', 0 },\n\t{ 0x20,\t'U', 0 },\n\t{ 0x22,\t'I', 0 },\n\t{ 0x1f,\t'O', 0 },\n\t{ 0x23,\t'P', 0 },\n\t{ 0x21,\t0xdb, 0 },\t\t/* [ */\n\t{ 0x1e,\t0xdd, 0 },\t\t/* ] */\n\t{ 0x2a,\t0xdc, 0 },\t\t/* backslash, bar */\n\t{ 0x75,\tVK_DELETE+0x100, 0 },\n\t{ 0x77,\tVK_END+0x100, VK_END },\n\t{ 0x79,\tVK_NEXT+0x100, 0 },\n\t{ 0x59,\tVK_NUMPAD7, VK_HOME },\n\t{ 0x5b,\tVK_NUMPAD8, VK_UP },\n\t{ 0x5c,\tVK_NUMPAD9, VK_PRIOR },\n\t{ 0x4e,\tVK_SUBTRACT, VK_SUBTRACT+0x100 },\n\n\t{ 0x39,\tVK_CAPITAL, 0 },  // Capslock\n\t{ 0x00,\t'A', 0 },\n\t{ 0x01,\t'S', 0 },\n\t{ 0x02,\t'D', 0 },\n\t{ 0x03,\t'F', 0 },\n\t{ 0x05,\t'G', 0 },\n\t{ 0x04,\t'H', 0 },\n\t{ 0x26,\t'J', 0 },\n\t{ 0x28,\t'K', 0 },\n\t{ 0x25,\t'L', 0 },\n\t{ 0x29,\t0xba, 0 },\t/* ; */\n\t{ 0x27,\t0xde, 0 },\t/* single quote */\n\t{ 0x24,\tVK_RETURN, 0 },\n\t{ 0x56,\tVK_NUMPAD4, VK_LEFT },\n\t{ 0x57,\tVK_NUMPAD5, VK_CLEAR },\n\t{ 0x58,\tVK_NUMPAD6, VK_RIGHT },\n\t{ 0x45,\tVK_ADD, 0 },\n\n\t{ 0x38,\tVK_SHIFT, 0 },\n\t{ 0x06,\t'Z', 0 },\n\t{ 0x07,\t'X', 0 },\n\t{ 0x08,\t'C', 0 },\n\t{ 0x09,\t'V', 0 },\n\t{ 0x0b,\t'B', 0 },\n\t{ 0x2d,\t'N', 0 },\n\t{ 0x2e,\t'M', 0 },\n\t{ 0x2b,\t0xbc, 0 },\t\t/* , */\n\t{ 0x2f,\t0xbe, 0 },\t\t/* . */\n\t{ 0x2c,\t0xbf, 0 },\t\t/* / */\n\t{ 0x3e,\tVK_UP+0x100, 0 },\n\t{ 0x53,\tVK_NUMPAD1, VK_END },\n\t{ 0x54,\tVK_NUMPAD2, VK_DOWN },\n\t{ 0x55,\tVK_NUMPAD3, VK_NEXT },\n\n\t{ 0x36,\tVK_CONTROL, VK_CONTROL+0x100 },\n\t{ 0x37,\tVK_SCROLL, VK_MENU+0x100 },\t// Command=scr_lock or alt-r\n\t{ 0x3a,\tVK_SNAPSHOT+0x100, VK_MENU },\t// Opt=prntscrn or alt-l\n\t{ 0x31,\t' ', 0 },\n\t{ 0x3b,\tVK_LEFT+0x100, 0 },\n\t{ 0x3d,\tVK_DOWN+0x100, 0 },\n\t{ 0x3c,\tVK_RIGHT+0x100, 0 },\n\t{ 0x52,\tVK_NUMPAD0, VK_INSERT },\n\t{ 0x41,\tVK_DECIMAL, VK_DELETE },\n\t{ 0x4c,\tVK_RETURN+0x100, 0 },\n\t{ -1, -1, -1 }\n};\n\n#if 0\nint\nwin_nonblock_read_stdin(int fd, char *bufptr, int len)\n{\n\tHANDLE\toshandle;\n\tDWORD\tdwret;\n\tint\tret;\n\n\terrno = EAGAIN;\n\toshandle = (HANDLE)_get_osfhandle(fd);\t// get stdin handle\n\tdwret = WaitForSingleObject(oshandle, 1);\t// wait 1msec for data\n\tret = -1;\n\tif(dwret == WAIT_OBJECT_0) {\n\t\tret = read(fd, bufptr, len);\n\t}\n\treturn ret;\n}\n#endif\n\nWindow_info *\nwin_find_win_info_ptr(HWND hwnd)\n{\n\tif(hwnd == g_mainwin_info.win_hwnd) {\n\t\treturn &g_mainwin_info;\n\t}\n\tif(hwnd == g_debugwin_info.win_hwnd) {\n\t\treturn &g_debugwin_info;\n\t}\n\treturn 0;\n}\n\nvoid\nwin_hide_pointer(Window_info *win_info_ptr, int do_hide)\n{\n\tShowCursor(!do_hide);\n\t// printf(\"Doing ShowCursor(%d)\\n\", !do_hide);\n}\n\nint\nwin_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y,\n\t\t\t\tint button_states, int buttons_valid)\n{\n\tKimage\t*kimage_ptr;\n\tint\tbuttons_changed, x, y;\n\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\tx = video_scale_mouse_x(kimage_ptr, raw_x, 0);\n\ty = video_scale_mouse_y(kimage_ptr, raw_y, 0);\n\n\t// printf(\"wum: %d,%d -> %d,%d\\n\", raw_x, raw_y, x, y);\n\n\tbuttons_changed = ((g_win_button_states & buttons_valid) !=\n\t\t\t\t\t\t\t\tbutton_states);\n\tg_win_button_states = (g_win_button_states & ~buttons_valid) |\n\t\t\t\t\t(button_states & buttons_valid);\n\tif(g_win_warp_pointer && (raw_x == g_win_warp_x) &&\n\t\t\t(raw_y == g_win_warp_y) && (!buttons_changed) ) {\n\t\t/* tell adb routs to recenter but ignore this motion */\n\t\tadb_update_mouse(kimage_ptr, x, y, 0, -1);\n\t\treturn 0;\n\t}\n\treturn adb_update_mouse(kimage_ptr, x, y, button_states,\n\t\t\t\t\t\t\tbuttons_valid & 7);\n}\n\nvoid\nwin_event_mouse(HWND hwnd, WPARAM wParam, LPARAM lParam)\n{\n\tWindow_info *win_info_ptr;\n\tword32\tflags;\n\tint\tbuttons, x, y, hide, warp;\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(!win_info_ptr) {\n\t\treturn;\n\t}\n\n\tflags = (word32)wParam;\n\tx = LOWORD(lParam);\n\ty = HIWORD(lParam);\n\n\tbuttons = (flags & 1) | (((flags >> 1) & 1) << 2) |\n\t\t\t\t\t\t(((flags >> 4) & 1) << 1);\n#if 0\n\tprintf(\"Mouse at %d, %d fl: %08x, but: %d\\n\", x, y, flags, buttons);\n#endif\n\twin_info_ptr->motion |= win_update_mouse(win_info_ptr, x, y, buttons,\n\t\t\t\t\t\t\t\t\t7);\n\n\thide = 0;\n\twarp = 0;\n\thide = adb_get_hide_warp_info(win_info_ptr->kimage_ptr, &warp);\n\tif(warp != g_win_warp_pointer) {\n\t\twin_info_ptr->motion = 1;\n\t}\n\tg_win_warp_pointer = warp;\n\tif(g_win_hide_pointer != hide) {\n\t\twin_hide_pointer(win_info_ptr, hide);\n\t}\n\tg_win_hide_pointer = hide;\n}\n\nvoid\nwin_event_key(HWND hwnd, WPARAM wParam, LPARAM lParam, int down)\n{\n\tWindow_info *win_info_ptr;\n\tKimage\t*kimage_ptr;\n\tword32\tvk, raw_vk, flags, capslock_state;\n\tint\ta2code, is_up;\n\tint\ti;\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(!win_info_ptr) {\n\t\treturn;\n\t}\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\traw_vk = (word32)wParam;\n\tflags = HIWORD(lParam);\n#if 0\n\tprintf(\"win_event_key: raw:%04x lParam:%08x d:%d flags:%08x\\n\",\n\t\traw_vk, (word32)lParam, down, flags);\n#endif\n\n\tif((flags & 0x4000) && down) {\n\t\t/* auto-repeating, just ignore it */\n\t\treturn;\n\t}\n\n\tvk = raw_vk + (flags & 0x100);\n#if 0\n\tprintf(\"Key event, vk=%04x, down:%d, repeat: %d, flags: %08x\\n\",\n\t\t\tvk, down, repeat, flags);\n#endif\n\n\t/* remap a few keys here.. sigh */\n\tif((vk & 0xff) == VK_APPS) {\n\t\t/* remap to command */\n\t\tvk = VK_MENU;\n\t}\n\n\tif((vk & 0xff) == VK_CAPITAL) {\n\t\t// Fix up capslock info: Windows gives us a down, then up event\n\t\t//  when the capslock key itself is pressed and released.  We\n\t\t//  need to ask for the true toggle state instead\n\t\tcapslock_state = (GetKeyState(VK_CAPITAL) & 1);\n\t\tdown = capslock_state;\n\t}\n\n\t/* search a2key_to_wsym to find wsym in col 1 or 2 */\n\ti = 0;\n\tis_up = !down;\n\tfor(i = g_num_a2_keycodes-1; i >= 0; i--) {\n\t\ta2code = g_a2_key_to_wsym[i][0];\n\t\tif((vk == g_a2_key_to_wsym[i][1]) ||\n\t\t\t\t\t(vk == g_a2_key_to_wsym[i][2])) {\n\t\t\tvid_printf(\"Found vk:%04x = %02x\\n\", vk, a2code);\n\t\t\tadb_physical_key_update(kimage_ptr, a2code, 0, is_up);\n\t\t\treturn;\n\t\t}\n\t}\n\tprintf(\"VK: %04x unknown\\n\", vk);\n}\n\nvoid\nwin_event_redraw(HWND hwnd)\n{\n\tWindow_info *win_info_ptr;\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\n\tif(win_info_ptr) {\n\t\tvideo_set_x_refresh_needed(win_info_ptr->kimage_ptr, 1);\n\t}\n}\n\nvoid\nwin_event_destroy(HWND hwnd)\n{\n\tWindow_info *win_info_ptr;\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(win_info_ptr == 0) {\n\t\treturn;\n\t}\n\tvideo_set_active(win_info_ptr->kimage_ptr, 0);\n\twin_info_ptr->active = 0;\n\tif(win_info_ptr == &g_mainwin_info) {\n\t\tmy_exit(0);\n\t} else {\n\t\tShowWindow(win_info_ptr->win_hwnd, SW_HIDE);\n\t\tReleaseDC(hwnd, win_info_ptr->win_dc);\n\t\tDeleteDC(win_info_ptr->win_cdc);\n\t\tDeleteObject(win_info_ptr->win_dev_handle);\n\t\tGlobalFree(win_info_ptr->win_bmapinfo_ptr);\n\t\twin_info_ptr->win_hwnd = 0;\n\t\twin_info_ptr->data_ptr = 0;\n\t}\n}\n\nvoid\nwin_event_move(HWND hwnd, WPARAM wParam, LPARAM lParam)\n{\n\tWindow_info *win_info_ptr;\n\tint\tx_xpos, x_ypos;\n\n\t// These WM_MOVE events indicate the window is being moved\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(!win_info_ptr) {\n\t\treturn;\n\t}\n\t// printf(\"WM_MOVE: %04x %08x\\n\", (word32)wParam, (word32)lParam);\n\tx_xpos = lParam & 0xffff;\n\tx_ypos = (lParam >> 16) & 0xffff;\n\tvideo_update_xpos_ypos(win_info_ptr->kimage_ptr, x_xpos, x_ypos);\n}\n\nvoid\nwin_event_size(HWND hwnd, WPARAM wParam, LPARAM lParam)\n{\n\tWindow_info *win_info_ptr;\n\tint\twidth, height;\n\n\t// These WM_SIZE events indicate the window is being resized\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(!win_info_ptr) {\n\t\treturn;\n\t}\n\t// printf(\"WM_SIZE: %04x %08x\\n\", (word32)wParam, (word32)lParam);\n\twidth = lParam & 0xffff;\n\theight = (lParam >> 16) & 0xffff;\n\tvideo_update_scale(win_info_ptr->kimage_ptr, width, height, 0);\n#if 0\n\tprintf(\"Frac width: %f\\n\",\n\t\twin_info_ptr->kimage_ptr->scale_width_a2_to_x / 65536.0);\n#endif\n\n\t// The following try to do \"live updating\" of the resize\n\twin_info_ptr->kimage_ptr->x_refresh_needed = 1;\n\tx_update_display(win_info_ptr);\n}\n\nvoid\nwin_event_minmaxinfo(HWND hwnd, LPARAM lParam)\n{\n\tWindow_info *win_info_ptr;\n\tMINMAXINFO *minmax_ptr;\n\tint\ta2_width, a2_height;\n\n\t// Windows sends WM_GETMINMAXINFO events when resizing is occurring,\n\t//  and we can modify the *lParam MINMAXINFO structure to set the\n\t//  minimum and maximum Track size (the size of the window)\n\t// This code forces the minimum to be the A2 window size, and the\n\t//  maximum to be the screen size\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(!win_info_ptr) {\n\t\treturn;\n\t}\n\tminmax_ptr = (MINMAXINFO *)lParam;\n#if 0\n\tprintf(\"MinMax: mintrack.x:%d, mintrack.y:%d\\n\",\n\t\tminmax_ptr->ptMinTrackSize.x,\n\t\tminmax_ptr->ptMinTrackSize.y);\n#endif\n\ta2_width = video_get_a2_width(win_info_ptr->kimage_ptr);\n\ta2_height = video_get_a2_height(win_info_ptr->kimage_ptr);\n\tminmax_ptr->ptMinTrackSize.x = a2_width + win_info_ptr->extra_width;\n\tminmax_ptr->ptMinTrackSize.y = a2_height + win_info_ptr->extra_height;\n\tminmax_ptr->ptMaxTrackSize.x = g_win_max_width +\n\t\t\t\t\t\twin_info_ptr->extra_width;\n\tminmax_ptr->ptMaxTrackSize.y = g_win_max_height +\n\t\t\t\t\t\twin_info_ptr->extra_height;\n}\n\nvoid\nwin_event_focus(HWND hwnd, int gain_focus)\n{\n\tWindow_info *win_info_ptr;\n\tword32\tc025_val, info;\n\n\twin_info_ptr = win_find_win_info_ptr(hwnd);\n\tif(!win_info_ptr) {\n\t\treturn;\n\t}\n\n\tif(gain_focus) {\n\t\t// printf(\"Got focus on %p\\n\", hwnd);\n\t\t// Get shift, ctrl, capslock state\n\t\tc025_val = 0;\n\t\tinfo = GetKeyState(VK_SHIFT);\t\t// left or right\n\t\tif(info & 0x8000) {\n\t\t\tc025_val |= 1;\t\t\t// Shift key is down\n\t\t}\n\t\tinfo = GetKeyState(VK_CONTROL);\t\t// left or right\n\t\tif(info & 0x8000) {\n\t\t\tc025_val |= 2;\n\t\t}\n\t\tinfo = GetKeyState(VK_CAPITAL);\t\t// Capslock?\n\t\tif(info & 1) {\n\t\t\tc025_val |= 4;\t\t\t// Capslock key is down\n\t\t}\n\t\t//printf(\"Calling update_c025 with %03x\\n\", c025_val);\n\t\tadb_update_c025_mask(win_info_ptr->kimage_ptr, c025_val, 7);\n\t} else {\n\t\t// printf(\"Lost focus on %p\\n\", hwnd);\n\t}\n\tif(win_info_ptr == &g_mainwin_info) {\n\t\tadb_kbd_repeat_off();\n\t\tadb_mainwin_focus(gain_focus);\n\t}\n}\n\nLRESULT CALLBACK\nwin_event_handler(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam)\n{\n\n#if 0\n\tprintf(\"Message: umsg: %04x, wparam:%04x lParam:%08x\\n\", umsg,\n\t\t\t(word32)wParam, (word32)lParam);\n#endif\n\n\tswitch(umsg) {\n\tcase WM_MOUSEMOVE:\n\tcase WM_LBUTTONDOWN:\n\tcase WM_LBUTTONUP:\n\tcase WM_MBUTTONDOWN:\n\tcase WM_MBUTTONUP:\n\tcase WM_RBUTTONDOWN:\n\tcase WM_RBUTTONUP:\n\t\twin_event_mouse(hwnd, wParam, lParam);\n\t\treturn 0;\n\tcase WM_KEYUP:\n\tcase WM_SYSKEYUP:\n\t\twin_event_key(hwnd, wParam, lParam, 0);\n\t\treturn 0;\n\tcase WM_KEYDOWN:\n\tcase WM_SYSKEYDOWN:\n\t\twin_event_key(hwnd, wParam, lParam, 1);\n\t\treturn 0;\n\tcase WM_SYSCOMMAND:\n\t\t// Alt key press can cause this.  Return 0 for SC_KEYMENU\n\t\tif(wParam == SC_KEYMENU) {\n\t\t\treturn 0;\n\t\t}\n\t\tbreak;\n\tcase WM_KILLFOCUS:\n\t\twin_event_focus(hwnd, 0);\n\t\tbreak;\n\tcase WM_SETFOCUS:\n\t\twin_event_focus(hwnd, 1);\n\t\tbreak;\n\tcase WM_DESTROY:\n\t\twin_event_destroy(hwnd);\n\t\treturn 0;\n\tcase WM_PAINT:\n\t\twin_event_redraw(hwnd);\n\t\tbreak;\n\tcase WM_MOVE:\n\t\twin_event_move(hwnd, wParam, lParam);\n\t\tbreak;\n\tcase WM_SIZE:\n\t\twin_event_size(hwnd, wParam, lParam);\n\t\tbreak;\n\tcase WM_GETMINMAXINFO:\n\t\twin_event_minmaxinfo(hwnd, lParam);\n\t\tbreak;\n\t}\n#if 0\n\tswitch(umsg) {\n\t\tHANDLE_MSG(hwnd, WM_KEYUP, win_event_key);\n\t\tHANDLE_MSG(hwnd, WM_KEYDOWN, win_event_key);\n\t\tHANDLE_MSG(hwnd, WM_SYSKEYUP, win_event_key);\n\t\tHANDLE_MSG(hwnd, WM_SYSKEYDOWN, win_event_key);\n\t\tHANDLE_MSG(hwnd, WM_DESTROY, win_event_destroy);\n\t}\n#endif\n\n#if 0\n\tswitch(umsg) {\n\tcase WM_NCACTIVATE:\n\tcase WM_NCHITTEST:\n\tcase WM_NCMOUSEMOVE:\n\tcase WM_SETCURSOR:\n\tcase WM_LBUTTONDOWN:\n\tcase WM_LBUTTONUP:\n\tcase WM_RBUTTONDOWN:\n\tcase WM_CONTEXTMENU:\n\tcase WM_RBUTTONUP:\n\tcase WM_MBUTTONDOWN:\n\tcase WM_MBUTTONUP:\n\tcase WM_PAINT:\n\n\t\tbreak;\n\tdefault:\n\t\tprintf(\"Got umsg2: %d\\n\", umsg);\n\t}\n#endif\n\n\treturn DefWindowProc(hwnd, umsg, wParam, lParam);\n}\n\n\nint\nmain(int argc, char **argv)\n{\n\tint\tret, mdepth;\n\n\tret = parse_argv(argc, argv, 1);\n\tif(ret) {\n\t\tprintf(\"parse_argv ret: %d, stopping\\n\", ret);\n\t\texit(1);\n\t}\n\n\tmdepth = 32;\n\n\tvideo_set_blue_mask(0x0000ff);\n\tvideo_set_green_mask(0x00ff00);\n\tvideo_set_red_mask(0xff0000);\n\n\tg_win_max_width = GetSystemMetrics(SM_CXSCREEN);\n\tg_win_max_height = GetSystemMetrics(SM_CYSCREEN);\n\tvid_printf(\"g_win_max_width:%d, g_win_max_height:%d\\n\",\n\t\t\t\t\tg_win_max_width, g_win_max_height);\n\n\tret = kegs_init(mdepth, g_win_max_width, g_win_max_height, 0);\n\tprintf(\"kegs_init done\\n\");\n\tif(ret) {\n\t\tprintf(\"kegs_init ret: %d, stopping\\n\", ret);\n\t\texit(1);\n\t}\n\n\twin_video_init(mdepth);\n\n\tprintf(\"Entering main loop!\\n\");\n\tfflush(stdout);\n\twhile(1) {\n\t\tret = run_16ms();\n\t\tif(ret != 0) {\n\t\t\tprintf(\"run_16ms returned: %d\\n\", ret);\n\t\t\tbreak;\n\t\t}\n\t\tx_update_display(&g_mainwin_info);\n\t\tx_update_display(&g_debugwin_info);\n\t\tcheck_input_events();\n\t}\n\txdriver_end();\n\texit(0);\n}\n\nvoid\ncheck_input_events()\n{\n\tMSG\tmsg;\n\tPOINT\tpt;\n\tBOOL\tret;\n\tWindow_info *win_info_ptr;\n\n\twhile(PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) {\n\t\tif(GetMessage(&msg, 0, 0, 0) > 0) {\n\t\t\t//TranslateMessage(&msg);\n\t\t\tDispatchMessage(&msg);\n\t\t} else {\n\t\t\tprintf(\"GetMessage returned <= 0\\n\");\n\t\t\tmy_exit(2);\n\t\t}\n\t}\n\n\twin_info_ptr = &g_mainwin_info;\n\tif(win_info_ptr->motion == 0) {\n\t\treturn;\n\t}\n\n\t// ONLY look at g_mainwin_info!\n\twin_info_ptr->motion = 0;\n\n\tif(g_win_warp_pointer) {\n\t\t/* move mouse to center of screen */\n\t\tg_win_warp_x = video_unscale_mouse_x(win_info_ptr->kimage_ptr,\n\t\t\tBASE_MARGIN_LEFT + (A2_WINDOW_WIDTH/2), 0);\n\t\tg_win_warp_y = video_unscale_mouse_y(win_info_ptr->kimage_ptr,\n\t\t\tBASE_MARGIN_TOP + (A2_WINDOW_HEIGHT/2), 0);\n\t\tpt.x = g_win_warp_x;\n\t\tpt.y = g_win_warp_y;\n\t\tClientToScreen(win_info_ptr->win_hwnd, &pt);\n\t\tret = SetCursorPos(pt.x, pt.y);\n#if 0\n\t\tprintf(\"Did SetCursorPos(%d, %d) warp_x:%d,y:%d, ret:%d\\n\",\n\t\t\tpt.x, pt.y, g_win_warp_x, g_win_warp_y, ret);\n#endif\n\t}\n\n\treturn;\n}\n\nvoid\nwin_video_init(int mdepth)\n{\n\tWNDCLASS wndclass;\n\tint\ta2code;\n\tint\ti;\n\n\tvideo_set_palette();\n\n\tg_num_a2_keycodes = 0;\n\tfor(i = 0; i < 0x7f; i++) {\n\t\ta2code = g_a2_key_to_wsym[i][0];\n\t\tif(a2code < 0) {\n\t\t\tg_num_a2_keycodes = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\twndclass.style = 0;\n\twndclass.lpfnWndProc = (WNDPROC)win_event_handler;\n\twndclass.cbClsExtra = 0;\n\twndclass.cbWndExtra = 0;\n\twndclass.hInstance = GetModuleHandle(NULL);\n\twndclass.hIcon = LoadIcon((HINSTANCE)NULL, IDI_APPLICATION);\n\twndclass.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);\n\twndclass.hbrBackground = GetStockObject(WHITE_BRUSH);\n\twndclass.lpszMenuName = NULL;\n\twndclass.lpszClassName = \"kegswin\";\n\n\t// Register the window\n\tif(!RegisterClass(&wndclass)) {\n\t\tprintf(\"Registering window failed\\n\");\n\t\texit(1);\n\t}\n\n\twin_init_window(&g_mainwin_info, video_get_kimage(0), \"KEGS\", mdepth);\n\twin_init_window(&g_debugwin_info, video_get_kimage(1),\n\t\t\t\t\t\t\"KEGS Debugger\", mdepth);\n\n\twin_create_window(&g_mainwin_info);\n}\n\nvoid\nwin_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str,\n\t\t\t\t\t\t\tint mdepth)\n{\n\tint\theight, width, x_xpos, x_ypos;\n\n\theight = video_get_x_height(kimage_ptr);\n\twidth = video_get_x_width(kimage_ptr);\n\n\tx_xpos = video_get_x_xpos(kimage_ptr);\n\tx_ypos = video_get_x_ypos(kimage_ptr);\n\n\twin_info_ptr->win_hwnd = 0;\n\twin_info_ptr->win_dc = 0;\n\twin_info_ptr->win_cdc = 0;\n\twin_info_ptr->win_bmapinfo_ptr = 0;\n\twin_info_ptr->win_bmaphdr_ptr = 0;\n\twin_info_ptr->win_dev_handle = 0;\n\twin_info_ptr->kimage_ptr = kimage_ptr;\n\twin_info_ptr->name_str = name_str;\n\twin_info_ptr->data_ptr = 0;\n\twin_info_ptr->motion = 0;\n\twin_info_ptr->mdepth = mdepth;\n\twin_info_ptr->active = 0;\n\twin_info_ptr->pixels_per_line = width;\n\twin_info_ptr->x_xpos = x_xpos;\n\twin_info_ptr->x_ypos = x_ypos;\n\twin_info_ptr->width = width;\n\twin_info_ptr->height = height;\n}\n\nvoid\nwin_create_window(Window_info *win_info_ptr)\n{\n\tHWND\twin_hwnd;\n\tRECT\trect;\n\tBITMAPINFO *bmapinfo_ptr;\n\tBITMAPINFOHEADER *bmaphdr_ptr;\n\tHBITMAP\twin_dev_handle;\n\tKimage\t*kimage_ptr;\n\tint\theight, width, extra_width, extra_height;\n\tint\textra_size, w_flags;\n\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\n\theight = win_info_ptr->height;\n\twidth = win_info_ptr->width;\n\n\tprintf(\"Got height: %d, width:%d\\n\", height, width);\n\n\t// We must call CreateWindow with a width,height that accounts for\n\t//  the title bar and any other stuff.  Use AdjustWindowRect() to\n\t//  calculate this info for us\n\tw_flags = WS_TILED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |\n\t\t\t\t\t\t\t\tWS_SIZEBOX;\n\trect.left = 0;\n\trect.top = 0;\n\trect.right = width;\n\trect.bottom = height;\n\t(void)AdjustWindowRect(&rect, w_flags, 0);\n\textra_width = rect.right - rect.left - width;\n\textra_height = rect.bottom - rect.top - height;\n\twin_info_ptr->extra_width = extra_width;\n\twin_info_ptr->extra_height = extra_height;\n\twin_hwnd = CreateWindow(\"kegswin\", win_info_ptr->name_str, w_flags,\n\t\twin_info_ptr->x_xpos, win_info_ptr->x_ypos, width + extra_width,\n\t\theight + extra_height, NULL, NULL, GetModuleHandle(NULL), NULL);\n\twin_info_ptr->win_hwnd = win_hwnd;\n\twin_info_ptr->active = 0;\n\n\tvideo_set_active(kimage_ptr, 1);\n\tvideo_update_scale(kimage_ptr, win_info_ptr->width,\n\t\t\t\t\t\twin_info_ptr->height, 1);\n\n\tprintf(\"win_hwnd = %p, height = %d\\n\", win_hwnd, height);\n\tGetWindowRect(win_hwnd, &rect);\n\tprintf(\"...rect is: %ld, %ld, %ld, %ld\\n\", rect.left, rect.top,\n\t\trect.right, rect.bottom);\n\n\twin_info_ptr->win_dc = GetDC(win_hwnd);\n\n\tSetTextColor(win_info_ptr->win_dc, 0);\n\tSetBkColor(win_info_ptr->win_dc, 0xffffff);\n\n\twin_info_ptr->win_cdc = CreateCompatibleDC(win_info_ptr->win_dc);\n\tprintf(\"win_cdc: %p, win_dc:%p\\n\", win_info_ptr->win_cdc,\n\t\t\t\t\t\twin_info_ptr->win_dc);\n\n\n\tprintf(\"Getting height, kimage_ptr:%p\\n\", kimage_ptr);\n\tfflush(stdout);\n\n\twin_info_ptr->data_ptr = 0;\n\n\textra_size = sizeof(RGBQUAD);\n\tbmapinfo_ptr = (BITMAPINFO *)GlobalAlloc(GPTR,\n\t\t\tsizeof(BITMAPINFOHEADER) + extra_size);\n\twin_info_ptr->win_bmapinfo_ptr = bmapinfo_ptr;\n\n\tbmaphdr_ptr = (BITMAPINFOHEADER *)bmapinfo_ptr;\n\twin_info_ptr->win_bmaphdr_ptr = bmaphdr_ptr;\n\tbmaphdr_ptr->biSize = sizeof(BITMAPINFOHEADER);\n\tbmaphdr_ptr->biWidth = g_win_max_width;\n\tbmaphdr_ptr->biHeight = -g_win_max_height;\n\tbmaphdr_ptr->biPlanes = 1;\n\tbmaphdr_ptr->biBitCount = win_info_ptr->mdepth;\n\tbmaphdr_ptr->biCompression = BI_RGB;\n\tbmaphdr_ptr->biClrUsed = 0;\n\n\t/* Use g_bmapinfo_ptr, adjusting width, height */\n\tprintf(\"bmaphdr_ptr:%p\\n\", bmaphdr_ptr);\n\n\tprintf(\"About to call CreateDIBSection, win_dc:%p\\n\",\n\t\t\t\t\t\twin_info_ptr->win_dc);\n\tfflush(stdout);\n\twin_dev_handle = CreateDIBSection(win_info_ptr->win_dc,\n\t\t\twin_info_ptr->win_bmapinfo_ptr, DIB_RGB_COLORS,\n\t\t\t(VOID **)&(win_info_ptr->data_ptr), NULL, 0);\n\twin_info_ptr->win_dev_handle = win_dev_handle;\n\n\twin_info_ptr->pixels_per_line = g_win_max_width;\n\tprintf(\"kim: %p, dev:%p data: %p\\n\", kimage_ptr,\n\t\t\twin_dev_handle, win_info_ptr->data_ptr);\n\tfflush(stdout);\n}\n\nvoid\nxdriver_end()\n{\n\tprintf(\"xdriver_end\\n\");\n}\n\nvoid\nwin_resize_window(Window_info *win_info_ptr)\n{\n\tRECT\trect1, rect2;\n\tBOOL\tret;\n\tKimage\t*kimage_ptr;\n\tint\tx_width, x_height;\n\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\tx_width = video_get_x_width(kimage_ptr);\n\tx_height = video_get_x_height(kimage_ptr);\n\t// printf(\"win_resize_window, x_w:%d, x_h:%d\\n\", x_width, x_height);\n\n\tret = GetWindowRect(win_info_ptr->win_hwnd, &rect1);\n\tret = GetClientRect(win_info_ptr->win_hwnd, &rect2);\n#if 0\n\tprintf(\"window_rect: l:%d, t:%d, r:%d, b:%d\\n\",\n\t\t\trect1.left, rect1.top, rect1.right, rect1.bottom);\n\tprintf(\"client_rect: l:%d, t:%d, r:%d, b:%d\\n\",\n\t\t\trect2.left, rect2.top, rect2.right, rect2.bottom);\n#endif\n\tret = MoveWindow(win_info_ptr->win_hwnd, rect1.left, rect1.top,\n\t\tx_width + win_info_ptr->extra_width,\n\t\tx_height + win_info_ptr->extra_height, TRUE);\n\t// printf(\"MoveWindow ret:%d\\n\", ret);\n\twin_info_ptr->width = x_width;\n\twin_info_ptr->height = x_height;\n}\n\nvoid\nx_update_display(Window_info *win_info_ptr)\n{\n\tChange_rect rect;\n\tvoid\t*bitm_old;\n\t//POINT\tpoint;\n\tint\tvalid, a2_active, x_active;\n\tint\ti;\n\n\ta2_active = video_get_active(win_info_ptr->kimage_ptr);\n\tx_active = win_info_ptr->active;\n\n\tif(x_active && !a2_active) {\n\t\t// We need to SW_HIDE this window\n\t\tShowWindow(win_info_ptr->win_hwnd, SW_HIDE);\n\t\tx_active = 0;\n\t\twin_info_ptr->active = x_active;\n\t}\n\tif(!x_active && a2_active) {\n\t\t// We need to SW_SHOWDEFAULT this window (and maybe create it)\n\t\tif(win_info_ptr->win_hwnd == 0) {\n\t\t\twin_create_window(win_info_ptr);\n\t\t}\n\t\tShowWindow(win_info_ptr->win_hwnd, SW_SHOWDEFAULT);\n\t\tUpdateWindow(win_info_ptr->win_hwnd);\n\t\tx_active = 1;\n\t\twin_info_ptr->active = x_active;\n\t}\n\tif(x_active == 0) {\n\t\treturn;\n\t}\n\n\tif(video_change_aspect_needed(win_info_ptr->kimage_ptr,\n\t\t\t\twin_info_ptr->width, win_info_ptr->height)) {\n\t\twin_resize_window(win_info_ptr);\n\t}\n\tfor(i = 0; i < MAX_CHANGE_RECTS; i++) {\n\t\tvalid = video_out_data(win_info_ptr->data_ptr,\n\t\t\twin_info_ptr->kimage_ptr, win_info_ptr->pixels_per_line,\n\t\t\t&rect, i);\n\t\tif(!valid) {\n\t\t\tbreak;\n\t\t}\n#if 0\n\t\tpoint.x = 0;\n\t\tpoint.y = 0;\n\t\tClientToScreen(win_info_ptr->win_hwnd, &point);\n#endif\n\t\tbitm_old = SelectObject(win_info_ptr->win_cdc,\n\t\t\t\t\twin_info_ptr->win_dev_handle);\n\n\t\tBitBlt(win_info_ptr->win_dc, rect.x, rect.y, rect.width,\n\t\t\trect.height, win_info_ptr->win_cdc, rect.x, rect.y,\n\t\t\tSRCCOPY);\n\n\t\tSelectObject(win_info_ptr->win_cdc, bitm_old);\n\t}\n}\n\nvoid\nx_hide_pointer(int do_hide)\n{\n\tif(do_hide) {\n\t\tShowCursor(0);\n\t} else {\n\t\tShowCursor(1);\n\t}\n}\n\nint\nopendir_int(DIR *dirp, const char *in_filename)\n{\n\tHANDLE\thandle1;\n\twchar_t\t*wcstr;\n\tchar\t*filename;\n\tsize_t\tret_val;\n\tint\tbuflen, len;\n\tint\ti;\n\n\tprintf(\"opendir on %s\\n\", in_filename);\n\tlen = (int)strlen(in_filename);\n\tbuflen = len + 8;\n\tif(buflen >= sizeof(dirp->dirent.d_name)) {\n\t\tprintf(\"buflen %d >= d_name %d\\n\", buflen,\n\t\t\t\t\t(int)sizeof(dirp->dirent.d_name));\n\t\treturn 1;\n\t}\n\tfilename = &dirp->dirent.d_name[0];\n\tmemcpy(filename, in_filename, len + 1);\n\twhile(len && (filename[len-1] == '/')) {\n\t\tfilename[len - 1] = 0;\n\t\tlen--;\n\t}\n\tcfg_strlcat(filename, \"/*.*\", buflen);\n\tfor(i = 0; i < len; i++) {\n\t\tif(filename[i] == '/') {\n\t\t\tfilename[i] = '\\\\';\n\t\t}\n\t}\n\tlen = (int)strlen(filename);\n\twcstr = malloc(buflen * 2);\n\t(void)mbstowcs_s(&ret_val, wcstr, buflen, filename, _TRUNCATE);\n\thandle1 = FindFirstFileW(wcstr, dirp->find_data_ptr);\n\tdirp->win_handle = handle1;\n\tfree(wcstr);\n\tif(handle1) {\n\t\tdirp->find_data_valid = 1;\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n\nDIR *\nopendir(const char *in_filename)\n{\n\tDIR\t*dirp;\n\tint\tret;\n\n\tdirp = calloc(1, sizeof(DIR));\n\tif(!dirp) {\n\t\treturn 0;\n\t}\n\tdirp->find_data_valid = 1;\n\tdirp->find_data_ptr = calloc(1, sizeof(WIN32_FIND_DATAW));\n\tret = 1;\n\tif(dirp->find_data_ptr) {\n\t\tret = opendir_int(dirp, in_filename);\n\t}\n\tif(ret) {\t\t// Bad\n\t\tfree(dirp->find_data_ptr);\t\t// free(0) is OK\n\t\tfree(dirp);\n\t\treturn 0;\n\t}\n\treturn dirp;\n}\n\nstruct dirent *\nreaddir(DIR *dirp)\n{\n\tWIN32_FIND_DATAW *find_data_ptr;\n\tHANDLE\thandle1;\n\tsize_t\tret_val;\n\tBOOL\tret;\n\n\thandle1 = dirp->win_handle;\n\tfind_data_ptr = dirp->find_data_ptr;\n\tif(!handle1 || !find_data_ptr) {\n\t\treturn 0;\n\t}\n\tret = 1;\n\tif(!dirp->find_data_valid) {\n\t\tif(handle1) {\n\t\t\tfind_data_ptr->cFileName[MAX_PATH-1] = 0;\n\t\t\tret = FindNextFileW(handle1, find_data_ptr);\n\t\t}\n\t}\n\tdirp->find_data_valid = 0;\n\tif(!ret) {\n\t\treturn 0;\n\t}\n\t(void)wcstombs_s(&ret_val, &(dirp->dirent.d_name[0]),\n\t\t\t\t(int)sizeof(dirp->dirent.d_name),\n\t\t\t\t&(find_data_ptr->cFileName[0]), _TRUNCATE);\n\tprintf(\"Returning file %s\\n\", &(dirp->dirent.d_name[0]));\n\n\treturn &(dirp->dirent);;\n}\n\nint\nclosedir(DIR *dirp)\n{\n\tFindClose(dirp->win_handle);\n\tfree(dirp->find_data_ptr);\n\tfree(dirp);\n\n\treturn 0;\n}\n\nint\nlstat(const char *path, struct stat *bufptr)\n{\n\treturn stat(path, bufptr);\n}\n\nint\nftruncate(int fd, word32 length)\n{\n\tHANDLE\thandle1;\n\n\thandle1 = (HANDLE)_get_osfhandle(fd);\n\tSetFilePointer(handle1, length, 0, FILE_BEGIN);\n\tSetEndOfFile(handle1);\n\n\treturn 0;\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/woz.c",
    "content": "const char rcsid_woz_c[] = \"@(#)$KmKId: woz.c,v 1.34 2025-01-07 16:45:35+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2025 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n// Based on WOZ 2.0/1.0 spec at https://applesaucefdc.com/woz/reference2/\n\n#include \"defc.h\"\n\nextern const char g_kegs_version_str[];\n\nbyte g_woz_hdr_bytes[12] = { 0x57, 0x4f, 0x5a, 0x32,\t\t// \"WOZ2\"\n\t\t\t\t0xff, 0x0a, 0x0d, 0x0a, 0, 0, 0, 0 };\n\nword32 g_woz_crc32_tab[256];\n\nvoid\nwoz_crc_init()\n{\n\tword32\tcrc, val, xor;\n\tint\ti, j;\n\n\tfor(i = 0; i < 256; i++) {\n\t\tcrc = 0;\n\t\tval = i;\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\txor = 0;\n\t\t\tif((val ^ crc) & 1) {\n\t\t\t\txor = 0xedb88320UL;\n\t\t\t}\n\t\t\tcrc = (crc >> 1) ^ xor;\n\t\t\tval = val >> 1;\n\t\t}\n\t\tg_woz_crc32_tab[i] = crc;\n\t\t// printf(\"crc32_tab[%d] = %08x\\n\", i, crc);\n\t}\n}\n\nword32\nwoz_calc_crc32(byte *bptr, dword64 dlen, word32 bytes_to_skip)\n{\n\tword32\tcrc, c;\n\n\tcrc = (~0U);\n\tif(bytes_to_skip > dlen) {\n\t\tdlen = 0;\n\t} else {\n\t\tbptr += bytes_to_skip;\n\t\tdlen -= bytes_to_skip;\n\t}\n\twhile(dlen != 0) {\n\t\tc = *bptr++;\n\t\tdlen--;\n\t\tcrc = g_woz_crc32_tab[(crc ^ c) & 0xff] ^ (crc >> 8);\n\t}\n\treturn (~crc);\n}\n\nvoid\nwoz_rewrite_crc(Disk *dsk, int min_write_size)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr;\n\tword32\tcrc;\n\n\t// Recalculate WOZ image CRC and write it to disk\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(!wozinfo_ptr || (dsk->fd < 0)) {\n\t\treturn;\n\t}\n\twozptr = wozinfo_ptr->wozptr;\n\tcrc = woz_calc_crc32(&wozptr[0], wozinfo_ptr->woz_size, 12);\n\tcfg_set_le32(&wozptr[8], crc);\n\tif(min_write_size < 12) {\n\t\tmin_write_size = 12;\n\t}\n\tcfg_write_to_fd(dsk->fd, wozptr, 0, min_write_size);\n}\n\nvoid\nwoz_rewrite_lock(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr;\n\tint\toffset;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(!wozinfo_ptr || (dsk->fd < 0)) {\n\t\treturn;\n\t}\n\twozptr = wozinfo_ptr->wozptr;\n\toffset = wozinfo_ptr->info_offset;\n\n\twozptr[offset + 2] = (dsk->write_prot != 0);\t// Update locked\n\twoz_rewrite_crc(dsk, offset + 2 + 1);\n}\n\nvoid\nwoz_check_file(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr, *newwozptr, *bptr;\n\tword32\tfdval, memval, crc, mem_crc, crcnew, file_size;\n\tint\twoz_size;\n\tint\ti;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(!wozinfo_ptr) {\n\t\treturn;\n\t}\n\twoz_size = wozinfo_ptr->woz_size;\n\twozptr = wozinfo_ptr->wozptr;\n\tcrc = woz_calc_crc32(wozptr, woz_size, 12);\n\tmem_crc = wozptr[8] | (wozptr[9] << 8) | (wozptr[10] << 16) |\n\t\t\t\t\t\t\t(wozptr[11] << 24);\n\tif(crc != mem_crc) {\n\t\thalt_printf(\"WOZ CRC calc:%08x, from mem:%08x\\n\", crc, mem_crc);\n\t}\n\tif((dsk->fd < 0) || dsk->raw_data || !dsk->write_through_to_unix) {\n\t\tprintf(\"woz_check_file: CRC check done, cannot check fd\\n\");\n\t\treturn;\n\t}\n\n\tfile_size = (word32)cfg_get_fd_size(dsk->fd);\n\tif(file_size != (word32)woz_size) {\n\t\thalt_printf(\"woz_size:%08x != file_size %08x\\n\", woz_size,\n\t\t\t\t\t\t\t\tfile_size);\n\t\tif((word32)woz_size > file_size) {\n\t\t\twoz_size = file_size;\n\t\t}\n\t}\n\tnewwozptr = malloc(woz_size);\n\tcfg_read_from_fd(dsk->fd, newwozptr, 0, woz_size);\n\n\tfor(i = 0; i < woz_size; i++) {\n\t\tfdval = newwozptr[i];\n\t\tmemval = wozptr[i];\n\t\tif(fdval == memval) {\n\t\t\tcontinue;\n\t\t}\n\t\thalt_printf(\"byte %07x of %07x: mem %02x != %02x fd\\n\", i,\n\t\t\t\t\t\twoz_size, memval, fdval);\n\t}\n\n\tcrcnew = woz_calc_crc32(newwozptr, woz_size, 12);\n\tfree(newwozptr);\n\tprintf(\"Woz check file complete.  mem %08x vs fd %08x, freed %p\\n\",\n\t\tcrc, crcnew, newwozptr);\n\n\tbptr = dsk->cur_trk_ptr->raw_bptr;\n\tprintf(\"dsk->cur_trk_ptr->raw_bptr = %p.  offset:%d\\n\", bptr,\n\t\t\t(int)(bptr - wozptr));\n}\n\nvoid\nwoz_parse_meta(Disk *dsk, int offset, int size)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr, *bptr;\n\tint\tc;\n\tint\ti;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\twozptr = wozinfo_ptr->wozptr;\n\tif(wozinfo_ptr->meta_offset) {\n\t\tprintf(\"Bad WOZ file, 2 META chunks\\n\");\n\t\twozinfo_ptr->woz_size = 0;\n\t\treturn;\n\t}\n\twozinfo_ptr->meta_offset = offset;\n\twozinfo_ptr->meta_size = size;\n\tprintf(\"META field, %d bytes:\\n\", size);\n\tbptr = &(wozptr[offset]);\n\tfor(i = 0; i < size; i++) {\n\t\tc = bptr[i];\n\t\tif(c == 0) {\n\t\t\tbreak;\n\t\t}\n\t\tputchar(c);\n\t}\n\tputchar('\\n');\n}\n\nvoid\nwoz_parse_info(Disk *dsk, int offset, int size)\n{\n\tbyte\tnew_buf[36];\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr, *bptr;\n\tint\tinfo_version, disk_type, write_protect, synchronized;\n\tint\tcleaned, ram, largest_track;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\twozptr = wozinfo_ptr->wozptr;\n\tif(wozinfo_ptr->info_offset) {\n\t\tprintf(\"Two INFO chunks, bad WOZ file\\n\");\n\t\twozinfo_ptr->woz_size = 0;\n\t\treturn;\n\t}\n\twozinfo_ptr->info_offset = offset;\n\tbptr = &(wozptr[offset]);\n\tif(size < 60) {\n\t\tprintf(\"INFO field is %d, too short\\n\", size);\n\t\twozinfo_ptr->woz_size = 0;\n\t\treturn;\n\t}\n\tinfo_version = bptr[0];\t\t// Only \"1\" or \"2\" is supported\n\tdisk_type = bptr[1];\t\t// 1==5.25\", 2=3.5\"\n\twrite_protect = bptr[2];\t// 1==write protected\n\tsynchronized = bptr[3];\t\t// 1==cross track sync during imaging\n\tcleaned = bptr[4];\t\t// 1==MC3470 fake bits have been removed\n\tmemcpy(&new_buf[0], &(bptr[5]), 32);\n\tnew_buf[32] = 0;\t\t// Null terminate\n\tprintf(\"INFO, %d bytes.  info_version:%d, disk_type:%d, wp:%d, sync:\"\n\t\t\"%d, cleaned:%d\\n\", size, info_version, disk_type,\n\t\twrite_protect, synchronized, cleaned);\n\tprintf(\"Creator: %s\\n\", (char *)&new_buf[0]);\n\tif(info_version >= 2) {\n\t\tram = bptr[42] + (bptr[43] << 8);\n\t\tlargest_track = (bptr[44] + (bptr[45] << 8)) * 512;\n\t\tprintf(\"Disk sides:%d, boot_format:%d bit_timing:%d, hw:\"\n\t\t\t\"%02x%02x, ram:%d, largest_track:0x%07x\\n\", bptr[37],\n\t\t\tbptr[38], bptr[39], bptr[41], bptr[40], ram,\n\t\t\tlargest_track);\n\t}\n\n\tif(write_protect) {\n\t\tprintf(\"Write protected\\n\");\n\t\tdsk->write_prot = 1;\n\t}\n}\n\nvoid\nwoz_parse_tmap(Disk *dsk, int offset, int size)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*bptr, *wozptr;\n\tint\ti;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\twozptr = wozinfo_ptr->wozptr;\n\tif(wozinfo_ptr->tmap_offset) {\n\t\tprintf(\"Second TMAP chunk, bad WOZ file!\\n\");\n\t\twozinfo_ptr->woz_size = 0;\n\t\treturn;\n\t}\n\twozinfo_ptr->tmap_offset = offset;\n\tprintf(\"TMAP field, %d bytes\\n\", size);\n\tbptr = &(wozptr[offset]);\n\tfor(i = 0; i < 40; i++) {\n\t\tprintf(\"Track %2d.00: %02x, %2d.25:%02x %2d.50:%02x %2d.75:\"\n\t\t\t\"%02x\\n\", i, bptr[0], i, bptr[1],\n\t\t\ti, bptr[2], i, bptr[3]);\n\t\tbptr += 4;\n\t}\n}\n\nvoid\nwoz_parse_trks(Disk *dsk, int offset, int size)\n{\n\tWoz_info *wozinfo_ptr;\n\n\tprintf(\"TRKS field, %d bytes, offset: %d\\n\", size, offset);\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(wozinfo_ptr->trks_offset) {\n\t\tprintf(\"Second TRKS chunk, illegal Woz file\\n\");\n\t\twozinfo_ptr->woz_size = 0;\n\t\treturn;\n\t}\n\twozinfo_ptr->trks_offset = offset;\n\twozinfo_ptr->trks_size = size;\n}\n\nint\nwoz_add_track(Disk *dsk, int qtr_track, word32 tmap, dword64 dfcyc)\n{\n\tWoz_info *wozinfo_ptr;\n\tTrk\t*trk;\n\tbyte\t*wozptr, *sync_ptr, *bptr;\n\tword32\traw_bytes, num_bytes, trks_size, len_bits, offset, num_blocks;\n\tword32\tblock;\n\tint\ttrks_offset;\n\tint\ti;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\twozptr = wozinfo_ptr->wozptr;\n\ttrks_offset = wozinfo_ptr->trks_offset;\n\ttrks_size = wozinfo_ptr->trks_size;\n\tif(wozinfo_ptr->version == 1) {\n\t\toffset = tmap * 6656;\n\t\tif((offset + 6656) > trks_size) {\n\t\t\tprintf(\"Trk %d is out of range!\\n\", tmap);\n\t\t\treturn 0;\n\t\t}\n\n\t\toffset = trks_offset + offset;\n\t\tbptr = &(wozptr[offset]);\n\t\tlen_bits = bptr[6648] | (bptr[6649] << 8);\n\t\tif(len_bits > (6656*8)) {\n\t\t\tprintf(\"Trk bits: %d too big\\n\", len_bits);\n\t\t\treturn 0;\n\t\t}\n\t} else {\n\t\tbptr = &(wozptr[trks_offset + (tmap * 8)]);\n\t\t// This is a TRK 8-byte structure\n\t\tblock = cfg_get_le16(&bptr[0]);\t\t// Starting Block\n\t\tnum_blocks = cfg_get_le16(&bptr[2]);\t// Block Count\n\t\tlen_bits = cfg_get_le32(&bptr[4]);\t// Bits Count\n#if 0\n\t\tprintf(\"qtr_track:%02x, block:%04x, num_blocks:%04x, \"\n\t\t\t\"len_bits:%06x\\n\", qtr_track, block, num_blocks,\n\t\t\tlen_bits);\n\t\tprintf(\"File offset: %05lx\\n\", bptr - wozinfo_ptr->wozptr);\n#endif\n\n\t\tif(block < 3) {\n\t\t\tprintf(\"block %04x is < 3\\n\", block);\n\t\t\treturn 0;\n\t\t}\n\t\toffset = (block * 512);\t\t// Offset from wozptr\n\t\tif((offset + (num_blocks * 512)) > (trks_size + trks_offset)) {\n\t\t\tprintf(\"Trk %d is out of range!\\n\", tmap);\n\t\t\treturn 0;\n\t\t}\n\t\tbptr = &(wozptr[offset]);\n#if 0\n\t\tprintf(\"Qtr_track %03x offset:%06x, bptr:%p, trks_bptr:%p\\n\",\n\t\t\tqtr_track, offset, bptr, trks_bptr);\n\t\tprintf(\" len_bits:%d %06x bptr-wozptr: %07lx\\n\", len_bits,\n\t\t\t\tlen_bits, bptr - wozinfo_ptr->wozptr);\n#endif\n\t\tif(len_bits > (num_blocks * 512 * 8)) {\n\t\t\tprintf(\"Trk bits: %d too big\\n\", len_bits);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tdsk->raw_bptr_malloc = 0;\n\n\traw_bytes = (len_bits + 7) >> 3;\n\tnum_bytes = raw_bytes + 8;\n\ttrk = &(dsk->trks[qtr_track]);\n\ttrk->raw_bptr = bptr;\n\ttrk->dunix_pos = offset;\n\ttrk->unix_len = raw_bytes;\n\ttrk->dirty = 0;\n\ttrk->track_bits = len_bits;\n#if 0\n\tprintf(\"track %d.%d dunix_pos:%08llx\\n\", qtr_track >> 2,\n\t\t\t\t(qtr_track & 3) * 25, trk->dunix_pos);\n#endif\n\ttrk->sync_ptr = (byte *)malloc(num_bytes);\n\n\tdsk->cur_trk_ptr = 0;\n\tiwm_move_to_ftrack(dsk, qtr_track << 16, 0, dfcyc);\n\tsync_ptr = &(trk->sync_ptr[0]);\n\tfor(i = 0; i < (int)raw_bytes; i++) {\n\t\tsync_ptr[i] = 0xff;\n\t}\n\n\tiwm_recalc_sync_from(dsk, qtr_track, 0, dfcyc);\n\n\tif(qtr_track == 0) {\n\t\tprintf(\"Track 0 data begins: %02x %02x %02x, offset:%d\\n\",\n\t\t\tbptr[0], bptr[1], bptr[2], offset);\n\t}\n\tfor(i = 0; i < qtr_track; i++) {\n\t\ttrk = &(dsk->trks[i]);\n\t\tif(trk->track_bits && (trk->raw_bptr == bptr)) {\n\t\t\t// Multiple tracks point to the same woz track\n\t\t\t// This is not allowed, reparse\n\t\t\twozinfo_ptr->reparse_needed = 1;\n\t\t\tprintf(\"Track %04x matchs track %04x, reparse needed\\n\",\n\t\t\t\t\t\t\tqtr_track, i);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn 1;\n}\n\nint\nwoz_parse_header(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr, *bptr;\n\tword32\tchunk_id, size, woz_size;\n\tint\tpos, version;\n\tint\ti;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\twozptr = wozinfo_ptr->wozptr;\n\twoz_size = wozinfo_ptr->woz_size;\n\tversion = 2;\n\tif(woz_size < 8) {\n\t\treturn 0;\n\t}\n\tfor(i = 0; i < 8; i++) {\n\t\tif(wozptr[i] != g_woz_hdr_bytes[i]) {\n\t\t\tif(i == 3) {\t\t// Check for WOZ1\n\t\t\t\tif(wozptr[i] == 0x31) {\t\t// WOZ1\n\t\t\t\t\tversion = 1;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tprintf(\"WOZ header[%d]=%02x, invalid\\n\", i, wozptr[i]);\n\t\t\treturn 0;\n\t\t}\n\t}\n\twozinfo_ptr->version = version;\n\n\tprintf(\"WOZ version: %d\\n\", version);\n\tpos = 12;\n\twhile(pos < (int)woz_size) {\n\t\tbptr = &(wozptr[pos]);\n\t\tchunk_id = bptr[0] | (bptr[1] << 8) | (bptr[2] << 16) |\n\t\t\t\t(bptr[3] << 24);\n\t\tsize = bptr[4] | (bptr[5] << 8) | (bptr[6] << 16) |\n\t\t\t\t\t\t\t(bptr[7] << 24);\n\t\tpos += 8;\n\t\tprintf(\"chunk_id: %08x, size:%08x\\n\", chunk_id, size);\n\t\tif(((pos + size) > woz_size) || (size < 8) ||\n\t\t\t\t\t\t\t((size >> 30) != 0)) {\n\t\t\treturn 0;\n\t\t}\n\t\tbptr = &(wozptr[pos]);\n\t\tif(chunk_id == 0x4f464e49) {\t\t// \"INFO\"\n\t\t\twoz_parse_info(dsk, pos, size);\n\t\t} else if(chunk_id == 0x50414d54) {\t// \"TMAP\"\n\t\t\twoz_parse_tmap(dsk, pos, size);\n\t\t} else if(chunk_id == 0x534b5254) {\t// \"TRKS\"\n\t\t\twoz_parse_trks(dsk, pos, size);\n\t\t} else if(chunk_id == 0x4154454d) {\t// \"META\"\n\t\t\twoz_parse_meta(dsk, pos, size);\n\t\t} else {\n\t\t\tprintf(\"Chunk header %08x is unknown\\n\", chunk_id);\n\t\t}\n\t\tpos += size;\n\t}\n\n\treturn 1;\t\t// Good so far\n}\n\nWoz_info *\nwoz_malloc(byte *wozptr, word32 woz_size)\n{\n\tWoz_info *wozinfo_ptr;\n\n\twozinfo_ptr = malloc(sizeof(Woz_info));\n\tprintf(\"malloc wozinfo_ptr:%p\\n\", wozinfo_ptr);\n\tif(!wozinfo_ptr) {\n\t\tfprintf(stderr, \"Out of memory\\n\");\n\t\texit(1);\n\t}\n\n\twozinfo_ptr->wozptr = wozptr;\n\twozinfo_ptr->woz_size = woz_size;\n\twozinfo_ptr->version = 0;\n\twozinfo_ptr->reparse_needed = 0;\n\twozinfo_ptr->max_trk_blocks = 0;\n\twozinfo_ptr->meta_size = 0;\n\twozinfo_ptr->trks_size = 0;\n\twozinfo_ptr->tmap_offset = 0;\n\twozinfo_ptr->trks_offset = 0;\n\twozinfo_ptr->info_offset = 0;\n\twozinfo_ptr->meta_offset = 0;\n\n\tif(woz_size < 12) {\n\t\tif(wozptr) {\n\t\t\tfree(wozptr);\n\t\t}\n\t\twozptr = 0;\n\t}\n\tif(!wozptr) {\n\t\twozptr = malloc(12);\n\t\twoz_append_bytes(wozptr, &g_woz_hdr_bytes[0], 12);\n\t\twozinfo_ptr->wozptr = wozptr;\n\t\twozinfo_ptr->woz_size = 12;\n\t}\n\n\treturn wozinfo_ptr;\n}\n\nint\nwoz_reopen(Disk *dsk, dword64 dfcyc)\n{\n\tbyte\tact_tmap[160];\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr, *tmap_bptr;\n\tword32\ttmap, prev_tmap, last_act;\n\tint\tret, num_tracks, num_match;\n\tint\ti, j;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\twozptr = wozinfo_ptr->wozptr;\n\n\tif(!wozinfo_ptr->tmap_offset) {\n\t\tprintf(\"No TMAP found\\n\");\n\t\treturn 0;\n\t}\n\tif(!wozinfo_ptr->trks_offset) {\n\t\tprintf(\"No TRKS found\\n\");\n\t\treturn 0;\n\t}\n\tif(!wozinfo_ptr->info_offset) {\n\t\tprintf(\"No INFO found\\n\");\n\t\treturn 0;\n\t}\n\tif(wozinfo_ptr->woz_size == 0) {\n\t\tprintf(\"woz_size is 0!\\n\");\n\t\treturn 0;\n\t}\n\n\ttmap_bptr = wozptr + wozinfo_ptr->tmap_offset;\n\tdsk->cur_fbit_pos = 0;\n\tnum_tracks = 4*35;\n\tfor(i = num_tracks; i < 160; i++) {\n\t\t// See what the largest track is, go to track 39.50\n\t\tif(tmap_bptr[i] != 0xff) {\n\t\t\tnum_tracks = i + 1;\n\t\t\t//printf(\"Will set num_tracks=%d\\n\", num_tracks);\n\t\t}\n\t}\n\tdsk->fbit_mult = 128;\t\t// 5.25\" multipler value\n\tif(!dsk->disk_525) {\n\t\tnum_tracks = 160;\n\t\tdsk->fbit_mult = 256;\t// 3.5\" multipler value\n\t}\n\tdisk_set_num_tracks(dsk, num_tracks);\n\tfor(i = 0; i < 160; i++) {\n\t\tact_tmap[i] = 0xff;\n\t}\n\tfor(i = 0; i < 160; i++) {\n\t\ttmap = tmap_bptr[i];\n\t\tif(tmap >= 0xff) {\n\t\t\tcontinue;\t\t// Skip\n\t\t}\n\t\t// WOZ format adds dup entries for adjacent qtr tracks, so\n\t\t//  track 2.0 has entries at 1.75 and 2.25 pointing to 2.0.\n\t\t// KEGS doesn't want these, it handles this itself, so remove\n\t\t//  them.\n\t\tif(dsk->disk_525) {\n\t\t\tnum_match = 1;\n\t\t\tfor(j = i + 1; j < 160; j++) {\n\t\t\t\tif(tmap_bptr[j] != tmap) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tnum_match++;\n\t\t\t}\n\t\t\t// From i, num_match is the number of time tmap repeats\n\t\t\tprev_tmap = 0xff;\n\t\t\tlast_act = 0xff;\n\t\t\tif(i) {\n\t\t\t\tprev_tmap = tmap_bptr[i - 1];\n\t\t\t\tlast_act = act_tmap[i - 1];\n\t\t\t} else if(num_match == 3) {\n\t\t\t\t// Weird case where WOZ starts with 0,0,0, we\n\t\t\t\t//  should treat track 0.25 as the real track\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(num_match >= 3) {\n\t\t\t\t// long run of repeats, add a track every other\n\t\t\t\t//  one.\n\t\t\t\tif(last_act == tmap) {\t\t// Just did trk\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(prev_tmap != tmap) {\t\t// Start of run\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t} else if(num_match == 2) {\n\t\t\t\t// Handle A, B, [B], B, C and A, B, B, [B], B, C\n\t\t\t\t// ALWAYS add this track\n\t\t\t} else {\t\t\t// num_match==1\n\t\t\t\tif(prev_tmap == tmap) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Otherwise, a lone track, always add it\n\t\t\t}\n\t\t}\n\t\tret = woz_add_track(dsk, i, tmap, dfcyc);\n\t\tif(ret == 0) {\n\t\t\tprintf(\"woz_add_track i:%04x tmap:%04x ret 0\\n\", i,\n\t\t\t\t\t\t\t\ttmap);\n\t\t\treturn ret;\n\t\t}\n\t}\n\n\treturn 1;\t\t// WOZ file is good!\n}\n\nint\nwoz_open(Disk *dsk, dword64 dfcyc)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr;\n\tdword64\tdoff;\n\tword32\twoz_size, crc, file_crc;\n\tint\tfd, ret;\n\n\t// return 0 for bad WOZ file, 1 for success\n\t// We set dsk->wozinfo_ptr, and caller will free it if we return 0\n\tprintf(\"woz_open on file %s, write_prot:%d\\n\", dsk->name_ptr,\n\t\t\t\t\t\tdsk->write_prot);\n\tif(dsk->trks == 0) {\n\t\treturn 0;\t\t// Smartport?\n\t}\n\tif(dsk->raw_data) {\n\t\twozptr = dsk->raw_data;\n\t\twoz_size = (word32)dsk->raw_dsize;\n\t\tdsk->write_prot = 1;\n\t\tdsk->write_through_to_unix = 0;\n\t} else {\n\t\tfd = dsk->fd;\n\t\tif(fd < 0) {\n\t\t\treturn 0;\n\t\t}\n\t\twoz_size = (word32)cfg_get_fd_size(fd);\n\t\tprintf(\"size: %d\\n\", woz_size);\n\n\t\twozptr = malloc(woz_size);\n\t\tdoff = cfg_read_from_fd(fd, wozptr, 0, woz_size);\n\t\tif(doff != woz_size) {\n\t\t\tclose(fd);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\twozinfo_ptr = woz_malloc(wozptr, woz_size);\n\tdsk->wozinfo_ptr = wozinfo_ptr;\n\n\tif(woz_size < 16) {\n\t\treturn 0;\n\t}\n\tcrc = woz_calc_crc32(wozptr, woz_size, 12);\n\tfile_crc = wozptr[8] | (wozptr[9] << 8) | (wozptr[10] << 16) |\n\t\t\t\t\t\t\t(wozptr[11] << 24);\n\tif((crc != file_crc) && (file_crc != 0)) {\n\t\tprintf(\"Bad Woz CRC:%08x in file, calc:%08x\\n\", file_crc, crc);\n\t\treturn 0;\n\t}\n\n\tret = woz_parse_header(dsk);\n\tprintf(\"woz_parse_header ret:%d, write_prot:%d\\n\", ret,\n\t\t\t\t\t\t\tdsk->write_prot);\n\tif(ret == 0) {\n\t\treturn ret;\n\t}\n\n\tret = woz_reopen(dsk, dfcyc);\n\tprintf(\"woz_reopen ret:%d\\n\", ret);\n\n\twoz_maybe_reparse(dsk);\n\treturn ret;\n}\n\nbyte *\nwoz_append_bytes(byte *wozptr, byte *in_bptr, int len)\n{\n\tint\ti;\n\n\tfor(i = 0; i < len; i++) {\n\t\t*wozptr++ = *in_bptr++;\n\t}\n\n\treturn wozptr;\n}\n\nbyte *\nwoz_append_word32(byte *wozptr, word32 val)\n{\n\tint\ti;\n\n\tfor(i = 0; i < 4; i++) {\n\t\t*wozptr++ = val & 0xff;\n\t\tval = val >> 8;\n\t}\n\n\treturn wozptr;\n}\n\nint\nwoz_append_chunk(Woz_info *wozinfo_ptr, word32 chunk_id, word32 length,\n\t\t\t\t\t\t\tbyte *bptr)\n{\n\tbyte\t*wozptr, *new_wozptr, *save_wozptr;\n\tword32\twoz_size, new_size;\n\tint\toffset;\n\n\twozptr = wozinfo_ptr->wozptr;\n\twoz_size = wozinfo_ptr->woz_size;\n\n\tnew_size = woz_size + 4 + 4 + length;\n\tnew_wozptr = realloc(wozptr, new_size);\n\tif(new_wozptr == 0) {\n\t\treturn 0;\n\t}\n\twozptr = new_wozptr + woz_size;\n\twozptr = woz_append_word32(wozptr, chunk_id);\n\tsave_wozptr = woz_append_word32(wozptr, length);\n\twozptr = woz_append_bytes(save_wozptr, bptr, length);\n\n\twozinfo_ptr->wozptr = new_wozptr;\n\twozinfo_ptr->woz_size = new_size;\n\toffset = (int)(save_wozptr - new_wozptr);\n\tswitch(chunk_id) {\n\tcase 0x4f464e49:\t\t// \"INFO\"\n\t\twozinfo_ptr->info_offset = offset;\n\t\tbreak;\n\tcase 0x50414d54:\t\t// \"TMAP\"\n\t\twozinfo_ptr->tmap_offset = offset;\n\t\tbreak;\n\tcase 0x534b5254:\t\t// \"TRKS\"\n\t\twozinfo_ptr->trks_offset = offset;\n\t\twozinfo_ptr->trks_size = new_size;\n\t\tbreak;\n\t}\n\tif(wozptr != (new_wozptr + new_size)) {\n\t\thalt_printf(\"wozptr:%p != %p + %08x\\n\", wozptr, new_wozptr,\n\t\t\t\t\t\t\t\tnew_size);\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\nbyte *\nwoz_append_a_trk(Woz_info *wozinfo_ptr, Disk *dsk, int trk_num, byte *bptr,\n\t\tword32 *num_blocks_ptr, dword64 *tmap_dptr)\n{\n\tbyte\t*new_bptr;\n\tword32\tnum_blocks, blocks_start, this_blocks, size_bytes, track_bits;\n\tint\ti;\n\n\t// Align trks_buf_size to 512 bytes\n\tblocks_start = *num_blocks_ptr;\n\n\ttrack_bits = dsk->trks[trk_num].track_bits;\n\tsize_bytes = (dsk->trks[trk_num].track_bits + 7) >> 3;\n\tthis_blocks = (size_bytes + 511) >> 9;\n\tif(wozinfo_ptr->max_trk_blocks < this_blocks) {\n\t\twozinfo_ptr->max_trk_blocks = this_blocks;\n\t\tprintf(\"max_trk_blocks=%d from trk %03x\\n\", this_blocks,\n\t\t\t\t\t\t\t\ttrk_num);\n\t}\n\n\tnum_blocks = blocks_start + this_blocks;\n\t*num_blocks_ptr = num_blocks;\n\n\t*tmap_dptr = (((dword64)track_bits) << 32) | (this_blocks << 16) |\n\t\t\t\t\t\t\t\tblocks_start;\n\n\tnew_bptr = realloc(bptr, num_blocks << 9);\n\tif(new_bptr == 0) {\n\t\treturn 0;\n\t}\n\t// Zero out last 512 byte block, to ensure a partial track has all 0's\n\t//  in the last block\n\tfor(i = 0; i < 512; i++) {\n\t\tnew_bptr[(num_blocks << 9) - 512 + i] = 0;\n\t}\n\twoz_append_bytes(new_bptr + (blocks_start << 9),\n\t\t\tdsk->trks[trk_num].raw_bptr, size_bytes);\n\n\treturn new_bptr;\n}\n\nWoz_info *\nwoz_new_from_woz(Disk *dsk, int disk_525)\n{\n\tdword64\ttmap_dvals[160];\n\tint\ttmap_qtrk[160];\n\tbyte\tbuf[160];\n\tWoz_info *wozinfo_ptr, *in_wozinfo_ptr;\n\tTrk\t*trkptr;\n\tbyte\t*in_wozptr, *wozptr, *trks_bufptr;\n\tdword64\tdval;\n\tword32\ttype, woz_size, num_blocks, crc, track_bits;\n\tint\tc, num_valid_tmap, pos, offset, raw_bytes;\n\tint\ti, j;\n\n\twozinfo_ptr = woz_malloc(0, 0);\t\t// New wozinfo\n\tin_wozptr = 0;\n\tin_wozinfo_ptr = 0;\n\tif(dsk) {\n\t\tin_wozinfo_ptr = dsk->wozinfo_ptr;\n\t\tif(!in_wozinfo_ptr) {\n\t\t\thalt_printf(\"Changing to WOZ format!\\n\");\n\t\t}\n\t}\n\tif(in_wozinfo_ptr) {\n\t\tin_wozptr = in_wozinfo_ptr->wozptr;\n\t}\n\tprintf(\" START woz_new_from_woz, in_wozinfo_ptr:%p, in_wozptr:%p\\n\",\n\t\t\t\t\tin_wozinfo_ptr, in_wozptr);\n\tfor(i = 0; i < 60; i++) {\n\t\tbuf[i] = 0;\n\t}\n\tif(in_wozptr) {\n\t\t// Output the INFO chunk\n\t\tmemcpy(&buf[0], &in_wozptr[20], 60);\n\t} else {\n\t\t// Output an INFO chunk for KEGS\n\t\tbuf[3] = 1;\t\t\t// 1=synchronized tracks\n\t\tbuf[4] = 1;\t\t\t// 1=MC3470 fake bits removed\n\t\tfor(i = 0; i < 32; i++) {\n\t\t\tbuf[5 + i] = ' ';\t\t\t// Creator field\n\t\t}\n\t\tmemcpy(&buf[5], \"KEGS\", 4);\n\t\t// And put the revision number after it\n\t\tfor(i = 0; i < 20; i++) {\n\t\t\tc = g_kegs_version_str[i];\n\t\t\tif(c == 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbuf[5 + 5 + i] = c;\n\t\t}\n\t}\n\tbuf[0] = 2;\t\t\t// WOZ2 version\n\ttype = 2 - disk_525;\t\t// 1=5.25, 2=3.5\n\tbuf[1] = type;\n\tbuf[2] = 0;\t\t\t\t\t// write_prot=0\n\tif(buf[37] == 0) {\n\t\tbuf[37] = type;\t\t\t\t// sides, re-use type\n\t}\n\tif(buf[39] == 0) {\n\t\tbuf[39] = 16 + 16 * (type & 1);\t\t// 5.25=32, 3.5=16\n\t}\n\twoz_append_chunk(wozinfo_ptr, 0x4f464e49, 60, &buf[0]);\t\t// INFO\n\n\t// TMAP\n\tfor(i = 0; i < 160; i++) {\n\t\tbuf[i] = 0xff;\n\t}\n\tnum_blocks = 6;\t\t\t// 1280 + 256 = 6x512\n\ttrks_bufptr = malloc(num_blocks << 9);\n\tprintf(\"trk_bufptr = %p\\n\", trks_bufptr);\n\tfor(i = 0; i < (int)(num_blocks << 9); i++) {\n\t\ttrks_bufptr[i] = 0;\n\t}\n\tnum_valid_tmap = 0;\n\tif((dsk != 0) && (dsk->trks != 0)) {\n\t\t// Output all valid tracks, and set the TMAP for adjacent .25\n\t\tfor(i = 0; i < 160; i++) {\n\t\t\ttrkptr = &(dsk->trks[i]);\n\t\t\tif(trkptr->track_bits >= 10000) {\n\t\t\t\tbuf[i] = num_valid_tmap;\n\t\t\t\tif(i && (buf[i-1] == 0xff)) {\n\t\t\t\t\tbuf[i-1] = num_valid_tmap;\n\t\t\t\t}\n\t\t\t\tif((i < 159) && (trkptr[1].track_bits < 10000)){\n\t\t\t\t\tbuf[i+1] = num_valid_tmap;\n\t\t\t\t}\n\t\t\t\ttrks_bufptr = woz_append_a_trk(wozinfo_ptr, dsk,\n\t\t\t\t\ti, trks_bufptr, &num_blocks,\n\t\t\t\t\t&tmap_dvals[num_valid_tmap]);\n\t\t\t\ttmap_qtrk[num_valid_tmap] = i;\n\t\t\t\tprintf(\"Did append, tmap:%04x qtrk:%04x dval:\"\n\t\t\t\t\t\"%016llx\\n\", num_valid_tmap, i,\n\t\t\t\t\ttmap_dvals[num_valid_tmap]);\n\t\t\t\tnum_valid_tmap++;\n\t\t\t}\n\t\t}\n\t}\n\twoz_append_chunk(wozinfo_ptr, 0x50414d54, 160, &buf[0]);\t// TMAP\n\n\t// TRKS.  Already all 0 if there are no tracks.  Fill in tmap_dvals[]\n\tfor(i = 0; i < num_valid_tmap; i++) {\n\t\tpos = 256 + i*8;\n\t\tdval = tmap_dvals[i];\n\t\tfor(j = 0; j < 8; j++) {\n\t\t\ttrks_bufptr[pos + j] = dval & 0xff;\n\t\t\tdval = dval >> 8;\n\t\t}\n\t}\n\twoz_append_chunk(wozinfo_ptr, 0x534b5254, (num_blocks << 9) - 256,\n\t\t\t\t\t\ttrks_bufptr + 256);\n\t\t\t\t\t\t// TRKS, 1280 minimum size\n\tfree(trks_bufptr);\n\n\t// Final META chunk...just remove, we've added or removed tracks from\n\t//  an original WOZ image, so the Meta data is no longer accurate\n\n\twozptr = wozinfo_ptr->wozptr;\n\twoz_size = wozinfo_ptr->woz_size;\n\n\tprintf(\" new wozptr:%p, woz_size:%08x\\n\", wozptr, woz_size);\n\twozptr[64] = wozinfo_ptr->max_trk_blocks;\t// largest track\n\n\tcrc = woz_calc_crc32(wozptr, woz_size, 12);\n\tcfg_set_le32(&wozptr[8], crc);\n\tif(dsk) {\n\t\tpos = 0;\n\t\tfor(i = 0; i < 160; i++) {\n\t\t\t// Go through and fix up trks structure to match new WOZ\n\t\t\ttrkptr = &(dsk->trks[i]);\n\t\t\tif((pos < num_valid_tmap) && (tmap_qtrk[pos] == i)) {\n\t\t\t\t// This is a valid track\n\t\t\t\tdval = tmap_dvals[pos];\n\t\t\t\ttrack_bits = dval >> 32;\n\t\t\t\toffset = (dval & 0xffff) << 9;\n\n\t\t\t\traw_bytes = (track_bits + 7) >> 3;\n\t\t\t\tif(trkptr->unix_len == 0) {\n\t\t\t\t\tfree(trkptr->raw_bptr);\n\t\t\t\t}\n\t\t\t\ttrkptr->raw_bptr = &wozptr[offset];\n\t\t\t\ttrkptr->dunix_pos = offset;\n\t\t\t\ttrkptr->unix_len = raw_bytes;\n\t\t\t\ttrkptr->dirty = 0;\n\t\t\t\ttrkptr->track_bits = track_bits;\n\t\t\t\tif(trkptr->sync_ptr == 0) {\n\t\t\t\t\thalt_printf(\"sync_ptr 0 qtrk:%04x\\n\",\n\t\t\t\t\t\t\t\t\ti);\n\t\t\t\t}\n\t\t\t\tpos++;\n\t\t\t} else {\n\t\t\t\t// No longer a valid track, free any ptrs\n\t\t\t\tif(dsk->raw_bptr_malloc) {\n\t\t\t\t\tfree(trkptr->raw_bptr);\n\t\t\t\t}\n\t\t\t\tfree(trkptr->sync_ptr);\n\t\t\t\ttrkptr->raw_bptr = 0;\n\t\t\t\ttrkptr->sync_ptr = 0;\n\t\t\t\ttrkptr->dunix_pos = 0;\n\t\t\t\ttrkptr->unix_len = 0;\n\t\t\t\ttrkptr->dirty = 0;\n\t\t\t\ttrkptr->track_bits = 0;\n\t\t\t}\n\t\t}\n\t\tdsk->raw_bptr_malloc = 0;\n\n\t\tif(dsk->raw_data) {\n\t\t\tfree(dsk->raw_data);\n\t\t\tdsk->raw_data = wozptr;\n\t\t\tdsk->raw_dsize = woz_size;\n\t\t\tdsk->dimage_start = 0;\n\t\t\tdsk->dimage_size = woz_size;\n\t\t\tin_wozptr = 0;\n\t\t}\n\t\tfree(in_wozptr);\n\t\tfree(in_wozinfo_ptr);\n\t\tif(in_wozinfo_ptr == 0) {\n\t\t\tdsk->write_through_to_unix = 0;\n\t\t\thalt_printf(\"Force write_through_to_unix since image \"\n\t\t\t\t\"changed to WOZ format\\n\");\n\t\t}\n\t}\n\n\tprintf(\" END woz_new_from_woz, wozinfo_ptr:%p wozptr:%p\\n\",\n\t\t\t\t\twozinfo_ptr, wozinfo_ptr->wozptr);\n\treturn wozinfo_ptr;\n}\n\nint\nwoz_new(int fd, const char *str, int size_kb)\n{\n\tWoz_info *wozinfo_ptr;\n\tbyte\t*wozptr;\n\tword32\tsize, woz_size;\n\tint\tdisk_525;\n\n\tdisk_525 = 0;\n\tif(size_kb <= 140) {\n\t\tdisk_525 = 1;\n\t}\n\twozinfo_ptr = woz_new_from_woz(0, disk_525);\n\n\twozptr = wozinfo_ptr->wozptr;\n\twoz_size = wozinfo_ptr->woz_size;\n\n\n\tsize = (word32)must_write(fd, wozptr, woz_size);\n\tfree(wozptr);\n\tfree(wozinfo_ptr);\n\tif(size != woz_size) {\n\t\treturn -1;\n\t}\n\tif(str) {\n\t\t// Avoid unused var warning\n\t}\n\n\treturn 0;\n}\n\nvoid\nwoz_maybe_reparse(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(wozinfo_ptr) {\n\t\tif(wozinfo_ptr->reparse_needed) {\n\t\t\twoz_reparse_woz(dsk);\n\t\t}\n\t}\n}\n\nvoid\nwoz_set_reparse(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\n\twozinfo_ptr = dsk->wozinfo_ptr;\n\tif(wozinfo_ptr) {\n\t\twozinfo_ptr->reparse_needed = 1;\n\t} else {\n\t\twoz_reparse_woz(dsk);\n\t}\n}\n\nvoid\nwoz_reparse_woz(Disk *dsk)\n{\n\tWoz_info *wozinfo_ptr;\n\n#if 0\n\tprintf(\"In woz_reparse_woz, showing track 0\\n\");\n\n\tiwm_show_a_track(dsk, &(dsk->trks[0]), 0.0);\n\tiwm_show_stats(3);\n#endif\n\n\twozinfo_ptr = woz_new_from_woz(dsk, dsk->disk_525);\n\t\t// This wozinfo_ptr has reparse_needed==0\n\n\tdsk->wozinfo_ptr = wozinfo_ptr;\n\tif(!dsk->raw_data && dsk->write_through_to_unix) {\n\t\t(void)!ftruncate(dsk->fd, wozinfo_ptr->woz_size);\n\t\t(void)cfg_write_to_fd(dsk->fd, wozinfo_ptr->wozptr, 0,\n\t\t\t\t\t\t\twozinfo_ptr->woz_size);\n\t\tprintf(\"did ftruncate and write of WOZ to %s\\n\", dsk->name_ptr);\n\t}\n\n\t// Need to recalculate dsk->cur_track_bits, cur_trk_ptr\n\tdsk->cur_trk_ptr = 0;\n\tiwm_move_to_ftrack(dsk, dsk->cur_frac_track, 0, 0);\n\n#if 0\n\tprintf(\"End of woz_reparse_woz, showing track 0\\n\");\n\tiwm_show_a_track(dsk, &(dsk->trks[0]), 0.0);\n\tiwm_show_stats(3);\n#endif\n\n\twoz_check_file(dsk);\n\tprintf(\"woz_reparse_woz complete!\\n\");\n}\n\nvoid\nwoz_remove_a_track(Disk *dsk, word32 qtr_track)\n{\n\tTrk\t*trkptr;\n\n\tprintf(\"woz_remove_track: %s qtr_track:%03x\\n\", dsk->name_ptr,\n\t\t\t\t\t\t\t\tqtr_track);\n\ttrkptr = &(dsk->trks[qtr_track]);\n\ttrkptr->track_bits = 0;\t\t// Track invalid\n\n\twoz_set_reparse(dsk);\n}\n\nword32\nwoz_add_a_track(Disk *dsk, word32 qtr_track)\n{\n\tTrk\t*trkptr, *other_trkptr;\n\tbyte\t*bptr, *other_bptr, *sync_ptr, *other_sync_ptr;\n\tword32\ttrack_bits, val;\n\tint\traw_bytes;\n\tint\ti;\n\n\t// Return track_bits for the new track\n\n\ttrkptr = &(dsk->trks[qtr_track]);\n\n\tother_trkptr = 0;\n\tif((qtr_track > 0) && (trkptr[-1].track_bits > 0)) {\n\t\t// Copy this track\n\t\tother_trkptr = trkptr - 1;\n\t} else if((qtr_track < 159) && (trkptr[1].track_bits > 0)) {\n\t\tother_trkptr = trkptr + 1;\n\t}\n\tother_trkptr = 0;\t\t// HACK\n\tif(dsk->disk_525 && other_trkptr) {\n\t\t// We're .25 tracks away from a valid track, copy it's data\n\t\ttrack_bits = other_trkptr->track_bits;\n\t\traw_bytes = (track_bits + 7) >> 3;\n\t\ttrkptr->track_bits = track_bits;\n\t\ttrkptr->raw_bptr = malloc(raw_bytes + 8);\n\t\ttrkptr->sync_ptr = malloc(raw_bytes + 8);\n\t\tprintf(\" add a track, copy bptr:%p sync_ptr:%p size:%08x\\n\",\n\t\t\ttrkptr->raw_bptr, trkptr->sync_ptr, raw_bytes + 8);\n\t\tbptr = trkptr->raw_bptr;\n\t\tsync_ptr = trkptr->sync_ptr;\n\t\tother_bptr = other_trkptr->raw_bptr;\n\t\tother_sync_ptr = other_trkptr->sync_ptr;\n\t\tfor(i = 0; i < raw_bytes; i++) {\n\t\t\tbptr[i] = other_bptr[i];\n\t\t\tsync_ptr[i] = other_sync_ptr[i];\n\t\t}\n\t} else {\n\t\ttrack_bits = iwm_get_default_track_bits(dsk, qtr_track);\n\t\traw_bytes = (track_bits + 7) >> 3;\n\t\ttrkptr->track_bits = track_bits;\n\t\ttrkptr->raw_bptr = malloc(raw_bytes + 8);\n\t\ttrkptr->sync_ptr = malloc(raw_bytes + 8);\n\t\tprintf(\" add a track, raw_bptr:%p sync_ptr:%p size:%08x\\n\",\n\t\t\ttrkptr->raw_bptr, trkptr->sync_ptr, raw_bytes + 8);\n\n\t\tbptr = trkptr->raw_bptr;\n\t\tsync_ptr = trkptr->sync_ptr;\n\t\tfor(i = 0; i < raw_bytes; i++) {\n\t\t\tval = ((i >> 6) ^ i) & 0x7f;\n\t\t\tif(((val & 0xf0) == 0) || ((val & 0x0f) == 0)) {\n\t\t\t\tval |= 0x21;\n\t\t\t}\n\t\t\tbptr[i] = val;\n\t\t\tsync_ptr[i] = 0xff;\n\t\t}\n\t\tbptr[raw_bytes - 1] = 0;\n\n\t\tiwm_recalc_sync_from(dsk, qtr_track, 0, 0);\n\t}\n\ttrkptr->dunix_pos = 0;\n\ttrkptr->unix_len = 0;\t\t\t// Mark as a newly created trk\n\ttrkptr->dirty = 0;\n\n\tprintf(\"woz_add_new_track: %s qtr_track:%03x\\n\", dsk->name_ptr,\n\t\t\t\t\t\t\t\tqtr_track);\n\twoz_set_reparse(dsk);\n\n\treturn track_bits;\n}\n\n"
  },
  {
    "path": "upstream/kegs/src/xdriver.c",
    "content": "const char rcsid_xdriver_c[] = \"@(#)$KmKId: xdriver.c,v 1.244 2025-01-07 16:40:09+00 kentd Exp $\";\n\n/************************************************************************/\n/*\t\t\tKEGS: Apple //gs Emulator\t\t\t*/\n/*\t\t\tCopyright 2002-2025 by Kent Dickey\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThis code is covered by the GNU GPL v3\t\t\t\t*/\n/*\tSee the file COPYING.txt or https://www.gnu.org/licenses/\t*/\n/*\tThis program is provided with no warranty\t\t\t*/\n/*\t\t\t\t\t\t\t\t\t*/\n/*\tThe KEGS web page is kegs.sourceforge.net\t\t\t*/\n/*\tYou may contact the author at: kadickey@alumni.princeton.edu\t*/\n/************************************************************************/\n\n# if !defined(__CYGWIN__) && !defined(__POWERPC__)\n/* No shared memory on Cygwin */\n# define X_SHARED_MEM\n#endif /* CYGWIN */\n\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n#include <X11/keysym.h>\n#include <X11/Xatom.h>\n#include <time.h>\n#include <stdlib.h>\n#include <signal.h>\n\n#ifdef X_SHARED_MEM\n# include <sys/ipc.h>\n# include <sys/shm.h>\n# include <X11/extensions/XShm.h>\n#endif\n\nint XShmQueryExtension(Display *display);\nvoid _XInitImageFuncPtrs(XImage *xim);\n\n#include \"defc.h\"\n\nextern int g_video_scale_algorithm;\nextern int g_audio_enable;\n\ntypedef struct windowinfo {\n\tXShmSegmentInfo *seginfo;\n\tXImage *xim;\n\tKimage *kimage_ptr;\t\t// KEGS Image pointer for window content\n\tchar\t*name_str;\n\tWindow\tx_win;\n\tGC\tx_winGC;\n\tAtom\tdelete_atom;\n\tint\tx_use_shmem;\n\tint\tactive;\n\tint\twidth_req;\n\tint\tpixels_per_line;\n\tint\tmain_height;\n\tint\tfull_min_width;\n\tint\tfull_min_height;\n} Window_info;\n\n#include \"protos_xdriver.h\"\n\nint\tg_x_warp_x = 0;\nint\tg_x_warp_y = 0;\nint\tg_x_warp_pointer = 0;\nint\tg_x_hide_pointer = 0;\n\nextern int Verbose;\nint\tg_x_screen_depth = 24;\nint\tg_x_screen_mdepth = 32;\nint\tg_x_max_width = 0;\nint\tg_x_max_height = 0;\nint\tg_screen_num = 0;\n\nextern int _Xdebug;\n\nint\tg_auto_repeat_on = -1;\n\nDisplay *g_display = 0;\nVisual\t*g_vis = 0;\nColormap g_default_colormap = 0;\n\nWindow_info g_mainwin_info = { 0 };\nWindow_info g_debugwin_info = { 0 };\n\nCursor\tg_cursor;\nPixmap\tg_cursor_shape;\nPixmap\tg_cursor_mask;\n\nXColor\tg_xcolor_black = { 0, 0x0000, 0x0000, 0x0000, DoRed|DoGreen|DoBlue, 0 };\nXColor\tg_xcolor_white = { 0, 0xffff, 0xffff, 0xffff, DoRed|DoGreen|DoBlue, 0 };\n\nconst char *g_x_selection_strings[3] = {\n\t// If we get SelectionRequests with target=Atom(\"TARGETS\"), then\n\t//  send XA_STRING plus this list to say what we can provide for copy\n\t\"UTF8_STRING\", \"text/plain\", \"text/plain;charset=utf-8\"\n};\n\nint\tg_x_num_targets = 0;\nAtom g_x_targets_array[5] = { 0 };\n\nAtom g_x_atom_targets = None;\t\t// Will be set to \"TARGETS\"\n\nint g_depth_attempt_list[] = { 24, 16, 15 };\n\n#define X_EVENT_LIST_ALL_WIN\t\t\t\t\t\t\\\n\t(ExposureMask | ButtonPressMask | ButtonReleaseMask |\t\t\\\n\tOwnerGrabButtonMask | KeyPressMask | KeyReleaseMask |\t\t\\\n\tKeymapStateMask | FocusChangeMask)\n\n#define X_BASE_WIN_EVENT_LIST\t\t\t\t\t\t\\\n\t(X_EVENT_LIST_ALL_WIN | PointerMotionMask | ButtonMotionMask |\t\\\n\tStructureNotifyMask)\n\n#define X_A2_WIN_EVENT_LIST\t\t\t\t\t\t\\\n\t(X_BASE_WIN_EVENT_LIST)\n\nint\tg_num_a2_keycodes = 0;\n\nint\tg_x_a2_key_to_xsym[][3] = {\n\t{ 0x35,\tXK_Escape,\t0 },\n\t{ 0x7a,\tXK_F1,\t0 },\n\t{ 0x78,\tXK_F2,\t0 },\n\t{ 0x63,\tXK_F3,\t0 },\n\t{ 0x76,\tXK_F4,\t0 },\n\t{ 0x60,\tXK_F5,\t0 },\n\t{ 0x61,\tXK_F6,\t0 },\n\t{ 0x62,\tXK_F7,\t0 },\n\t{ 0x64,\tXK_F8,\t0 },\n\t{ 0x65,\tXK_F9,\t0 },\n\t{ 0x6d,\tXK_F10,\t0 },\n\t{ 0x67,\tXK_F11,\t0 },\n\t{ 0x6f,\tXK_F12,\t0 },\n\t{ 0x69,\tXK_F13,\t0 },\n\t{ 0x6b,\tXK_F14,\t0 },\n\t{ 0x71,\tXK_F15,\t0 },\n\t{ 0x7f, XK_Pause, XK_Break },\n\t{ 0x32,\t'`', '~' },\t\t/* Key number 18? */\n\t{ 0x12,\t'1', '!' },\n\t{ 0x13,\t'2', '@' },\n\t{ 0x14,\t'3', '#' },\n\t{ 0x15,\t'4', '$' },\n\t{ 0x17,\t'5', '%' },\n\t{ 0x16,\t'6', '^' },\n\t{ 0x1a,\t'7', '&' },\n\t{ 0x1c,\t'8', '*' },\n\t{ 0x19,\t'9', '(' },\n\t{ 0x1d,\t'0', ')' },\n\t{ 0x1b,\t'-', '_' },\n\t{ 0x18,\t'=', '+' },\n\t{ 0x33,\tXK_BackSpace, 0 },\n\t{ 0x72,\tXK_Insert, XK_Help },\t/* Help? */\n/*\t{ 0x73,\tXK_Home, 0 },\t\talias XK_Home to be XK_KP_Equal! */\n\t{ 0x74,\tXK_Page_Up, 0 },\n\t{ 0x47,\tXK_Num_Lock, XK_Clear },\t/* Clear */\n\t{ 0x51,\tXK_KP_Equal, XK_Home },\t\t/* Note XK_Home alias! */\n\t{ 0x4b,\tXK_KP_Divide, 0 },\n\t{ 0x43,\tXK_KP_Multiply, 0 },\n\n\t{ 0x30,\tXK_Tab, 0 },\n\t{ 0x0c,\t'q', 'Q' },\n\t{ 0x0d,\t'w', 'W' },\n\t{ 0x0e,\t'e', 'E' },\n\t{ 0x0f,\t'r', 'R' },\n\t{ 0x11,\t't', 'T' },\n\t{ 0x10,\t'y', 'Y' },\n\t{ 0x20,\t'u', 'U' },\n\t{ 0x22,\t'i', 'I' },\n\t{ 0x1f,\t'o', 'O' },\n\t{ 0x23,\t'p', 'P' },\n\t{ 0x21,\t'[', '{' },\n\t{ 0x1e,\t']', '}' },\n\t{ 0x2a,\t0x5c, '|' },\t/* backslash, bar */\n\t{ 0x75,\tXK_Delete, 0 },\n\t{ 0x77,\tXK_End, 0 },\n\t{ 0x79,\tXK_Page_Down, 0 },\n\t{ 0x59,\tXK_KP_7, XK_KP_Home },\n\t{ 0x5b,\tXK_KP_8, XK_KP_Up },\n\t{ 0x5c,\tXK_KP_9, XK_KP_Page_Up },\n\t{ 0x4e,\tXK_KP_Subtract, 0 },\n\n\t{ 0x39,\tXK_Caps_Lock, 0 },\n\t{ 0x00,\t'a', 'A' },\n\t{ 0x01,\t's', 'S' },\n\t{ 0x02,\t'd', 'D' },\n\t{ 0x03,\t'f', 'F' },\n\t{ 0x05,\t'g', 'G' },\n\t{ 0x04,\t'h', 'H' },\n\t{ 0x26,\t'j', 'J' },\n\t{ 0x28,\t'k', 'K' },\n\t{ 0x25,\t'l', 'L' },\n\t{ 0x29,\t';', ':' },\n\t{ 0x27,\t0x27, '\"' },\t/* single quote */\n\t{ 0x24,\tXK_Return, 0 },\n\t{ 0x56,\tXK_KP_4, XK_KP_Left },\n\t{ 0x57,\tXK_KP_5, XK_KP_Begin },\n\t{ 0x58,\tXK_KP_6, XK_KP_Right },\n\t{ 0x45,\tXK_KP_Add, 0 },\n\n\t{ 0x38,\tXK_Shift_L, XK_Shift_R },\n\t{ 0x06,\t'z', 'Z' },\n\t{ 0x07,\t'x', 'X' },\n\t{ 0x08,\t'c', 'C' },\n\t{ 0x09,\t'v', 'V' },\n\t{ 0x0b,\t'b', 'B' },\n\t{ 0x2d,\t'n', 'N' },\n\t{ 0x2e,\t'm', 'M' },\n\t{ 0x2b,\t',', '<' },\n\t{ 0x2f,\t'.', '>' },\n\t{ 0x2c,\t'/', '?' },\n\t{ 0x3e,\tXK_Up, 0 },\n\t{ 0x53,\tXK_KP_1, XK_KP_End },\n\t{ 0x54,\tXK_KP_2, XK_KP_Down },\n\t{ 0x55,\tXK_KP_3, XK_KP_Page_Down },\n\n\t{ 0x36,\tXK_Control_L, XK_Control_R },\n\t{ 0x3a,\tXK_Print, XK_Sys_Req },\t\t/* Option */\n\t{ 0x37,\tXK_Scroll_Lock, 0 },\t\t/* Command */\n\t{ 0x31,\t' ', 0 },\n\t{ 0x3b,\tXK_Left, 0 },\n\t{ 0x3d,\tXK_Down, 0 },\n\t{ 0x3c,\tXK_Right, 0 },\n\t{ 0x52,\tXK_KP_0, XK_KP_Insert },\n\t{ 0x41,\tXK_KP_Decimal, XK_KP_Separator },\n\t{ 0x4c,\tXK_KP_Enter, 0 },\n\t{ -1, -1, -1 }\n};\n\nint\nmain(int argc, char **argv)\n{\n\tint\tret, mdepth;\n\n\tret = parse_argv(argc, argv, 1);\n\tif(ret) {\n\t\tprintf(\"kegsmain ret: %d, stopping\\n\", ret);\n\t\texit(1);\n\t}\n\n\tmdepth = x_video_get_mdepth();\n\n\tret = kegs_init(mdepth, g_x_max_width, g_x_max_height, 0);\n\tprintf(\"kegs_init done\\n\");\n\tif(ret) {\n\t\tprintf(\"kegs_init ret: %d, stopping\\n\", ret);\n\t\texit(1);\n\t}\n\tx_video_init();\n\n\t// This is the main loop of KEGS, when this exits, KEGS exits\n\t//  run_16ms() does one video frame worth of instructions and video\n\t//  updates: 17030 1MHz clock cycles.\n\twhile(1) {\n\t\tret = run_16ms();\n\t\tif(ret != 0) {\n\t\t\tprintf(\"run_16ms returned: %d\\n\", ret);\n\t\t\tbreak;\n\t\t}\n\t\tx_input_events();\n\t\tx_update_display(&g_mainwin_info);\n\t\tx_update_display(&g_debugwin_info);\n\t}\n\txdriver_end();\n\texit(0);\n}\n\nint\nmy_error_handler(Display *display, XErrorEvent *ev)\n{\n\tchar msg[1024];\n\tXGetErrorText(display, ev->error_code, msg, 1000);\n\tprintf(\"X Error code %s\\n\", msg);\n\tfflush(stdout);\n\n\treturn 0;\n}\n\nvoid\nxdriver_end()\n{\n\tprintf(\"xdriver_end\\n\");\n\tif(g_display) {\n\t\tx_auto_repeat_on(1);\n\t\tXFlush(g_display);\n\t}\n}\n\nvoid\nx_try_xset_r()\n{\n\t/* attempt \"xset r\" */\n\t(void)!system(\"xset r\");\n\txdriver_end();\n\texit(5);\n}\n\nvoid\nx_badpipe(int signum)\n{\n\t/* restore normal sigpipe handling */\n\tsignal(SIGPIPE, SIG_DFL);\n\n\tx_try_xset_r();\n}\n\nint\nkegs_x_io_error_handler(Display *display)\n{\n\tprintf(\"kegs_x_io_error_handler called (likely window closed)\\n\");\n\tg_display = 0;\n\tx_try_xset_r();\n\treturn 0;\n}\n\nint\nx_video_get_mdepth()\n{\n\tXWindowAttributes get_attr;\n\tint\tdepth, len, ret, force_depth;\n\tint\ti;\n\n\tprintf(\"Preparing X Windows graphics system\\n\");\n\tret = 0;\n\n\tsignal(SIGPIPE, x_badpipe);\n\tsignal(SIGPIPE, x_badpipe);\n\n#if 0\n\tprintf(\"Setting _Xdebug = 1, makes X synchronous\\n\");\n\t_Xdebug = 1;\n#endif\n\n\tg_display = XOpenDisplay(NULL);\n\tif(g_display == NULL) {\n\t\tfprintf(stderr, \"Can't open display\\n\");\n\t\texit(1);\n\t}\n\n\tvid_printf(\"Just opened display = %p\\n\", g_display);\n\tfflush(stdout);\n\n\tg_screen_num = DefaultScreen(g_display);\n\n\tget_attr.width = 0;\n\tget_attr.height = 0;\n\tret = XGetWindowAttributes(g_display, DefaultRootWindow(g_display),\n\t\t\t&get_attr);\n\tprintf(\"XGetWindowAttributes ret: %d\\n\", ret);\n\tg_x_max_width = get_attr.width;\n\tg_x_max_height = get_attr.height;\n\tprintf(\"get_attr.width:%d, height:%d\\n\", g_x_max_width, g_x_max_height);\n\n\tlen = sizeof(g_depth_attempt_list)/sizeof(int);\n\tforce_depth = sim_get_force_depth();\n\tif(force_depth > 0) {\n\t\t/* Only use the requested user depth */\n\t\tlen = 1;\n\t\tg_depth_attempt_list[0] = force_depth;\n\t}\n\n\tfor(i = 0; i < len; i++) {\n\t\tdepth = g_depth_attempt_list[i];\n\n\t\tg_x_screen_mdepth = x_try_find_visual(depth, g_screen_num);\n\t\tif(g_x_screen_mdepth != 0) {\n\t\t\tbreak;\n\t\t}\n\t}\n\tif(g_x_screen_mdepth == 0) {\n\t\tfprintf(stderr, \"Couldn't find any visuals at any depth!\\n\");\n\t\texit(2);\n\t}\n\n\treturn g_x_screen_mdepth;\n}\n\nint\nx_try_find_visual(int depth, int screen_num)\n{\n\tXVisualInfo *visualList;\n\tXVisualInfo *v_chosen;\n\tXVisualInfo vTemplate;\n\tint\tvisualsMatched, visual_chosen, mdepth;\n\tint\ti;\n\n\tvTemplate.screen = screen_num;\n\tvTemplate.depth = depth;\n\n\tvisualList = XGetVisualInfo(g_display,\n\t\t(VisualScreenMask | VisualDepthMask),\n\t\t&vTemplate, &visualsMatched);\n\n\tvid_printf(\"visuals matched: %d\\n\", visualsMatched);\n\tif(visualsMatched == 0) {\n\t\treturn 0;\n\t}\n\n\tvisual_chosen = -1;\n\tfor(i = 0; i < visualsMatched; i++) {\n\t\tprintf(\"Visual %d\\n\", i);\n\t\tprintf(\"\tid: %08x, screen: %d, depth: %d, class: %d\\n\",\n\t\t\t(word32)visualList[i].visualid,\n\t\t\tvisualList[i].screen,\n\t\t\tvisualList[i].depth,\n\t\t\tvisualList[i].class);\n\t\tprintf(\"\tred: %08lx, green: %08lx, blue: %08lx\\n\",\n\t\t\tvisualList[i].red_mask,\n\t\t\tvisualList[i].green_mask,\n\t\t\tvisualList[i].blue_mask);\n\t\tprintf(\"\tcmap size: %d, bits_per_rgb: %d\\n\",\n\t\t\tvisualList[i].colormap_size,\n\t\t\tvisualList[i].bits_per_rgb);\n\t\tif((depth != 8) && (visualList[i].class == TrueColor)) {\n\t\t\tvisual_chosen = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(visual_chosen < 0) {\n\t\tprintf(\"Couldn't find any good visuals at depth %d!\\n\",\n\t\t\tdepth);\n\t\treturn 0;\n\t}\n\n\tprintf(\"Chose visual: %d\\n\", visual_chosen);\n\n\tv_chosen = &(visualList[visual_chosen]);\n\tvideo_set_red_mask((word32)v_chosen->red_mask);\n\tvideo_set_green_mask((word32)v_chosen->green_mask);\n\tvideo_set_blue_mask((word32)v_chosen->blue_mask);\n\n\tvideo_set_palette();\t// Uses above masks to initialize palettes\n\n\tg_x_screen_depth = depth;\n\tmdepth = depth;\n\tif(depth > 8) {\n\t\tmdepth = 16;\n\t}\n\tif(depth > 16) {\n\t\tmdepth = 32;\n\t}\n\n\t// XFree(visualList); -- Cannot free, still using g_vis...\n\n\tg_vis = v_chosen->visual;\n\n\treturn mdepth;\n}\n\nvoid\nx_video_init()\n{\n\tint\ttmp_array[0x80];\n\tAtom\ttarget;\n\tchar\tcursor_data;\n\tint\tkeycode, num_targets, max_targets, num;\n\tint\ti;\n\n\tprintf(\"Opening X Window now\\n\");\n\n\tg_num_a2_keycodes = 0;\n\tfor(i = 0; i <= 0x7f; i++) {\n\t\ttmp_array[i] = 0;\n\t}\n\tfor(i = 0; i < 0x7f; i++) {\n\t\tkeycode = g_x_a2_key_to_xsym[i][0];\n\t\tif(keycode < 0) {\n\t\t\tg_num_a2_keycodes = i;\n\t\t\tbreak;\n\t\t} else if(keycode > 0x7f) {\n\t\t\tprintf(\"a2_key_to_xsym[%d] = %02x!\\n\", i, keycode);\n\t\t\t\texit(2);\n\t\t} else {\n\t\t\tif(tmp_array[keycode]) {\n\t\t\t\tprintf(\"a2_key_to_x[%d] = %02x used by %d\\n\",\n\t\t\t\t\ti, keycode, tmp_array[keycode] - 1);\n\t\t\t}\n\t\t\ttmp_array[keycode] = i + 1;\n\t\t}\n\t}\n\n\n\tg_default_colormap = XDefaultColormap(g_display, g_screen_num);\n\tif(!g_default_colormap) {\n\t\tprintf(\"g_default_colormap == 0!\\n\");\n\t\texit(4);\n\t}\n\n\t/* and define cursor */\n\tcursor_data = 0;\n\tg_cursor_shape = XCreatePixmapFromBitmapData(g_display,\n\t\tRootWindow(g_display,g_screen_num), &cursor_data, 1, 1, 1, 0,1);\n\tg_cursor_mask = XCreatePixmapFromBitmapData(g_display,\n\t\tRootWindow(g_display,g_screen_num), &cursor_data, 1, 1, 1, 0,1);\n\n\tg_cursor = XCreatePixmapCursor(g_display, g_cursor_shape,\n\t\t\tg_cursor_mask, &g_xcolor_black, &g_xcolor_white, 0, 0);\n\n\tXFreePixmap(g_display, g_cursor_shape);\n\tXFreePixmap(g_display, g_cursor_mask);\n\n\tXFlush(g_display);\n\tg_x_atom_targets = XInternAtom(g_display, \"TARGETS\", 0);\n\tnum = sizeof(g_x_selection_strings)/sizeof(g_x_selection_strings[0]);\n\tg_x_targets_array[0] = XA_STRING;\n\tnum_targets = 1;\n\tfor(i = 0; i < num; i++) {\n\t\ttarget = XInternAtom(g_display, g_x_selection_strings[i], 0);\n\t\tif(target != None) {\n\t\t\tg_x_targets_array[num_targets++] = target;\n\t\t}\n\t}\n\tg_x_num_targets = num_targets;\n\tmax_targets = sizeof(g_x_targets_array)/sizeof(g_x_targets_array[0]);\n\tif(num_targets > max_targets) {\n\t\tprintf(\"Overflowed g_x_targets_array: %d out of %d\\n\",\n\t\t\t\t\t\tnum_targets, max_targets);\n\t\texit(-1);\n\t}\n\n\tx_init_window(&g_mainwin_info, video_get_kimage(0), \"KEGS\");\n\tx_init_window(&g_debugwin_info, video_get_kimage(1), \"KEGS Debugger\");\n\n\tx_create_window(&g_mainwin_info);\n\n\tvid_printf(\"Set error handler to my_x_handler\\n\");\n\tXSetIOErrorHandler(kegs_x_io_error_handler);\n}\n\nvoid\nx_init_window(Window_info *win_info_ptr, Kimage *kimage_ptr, char *name_str)\n{\n\tint\twidth, height, x_use_shmem, ret;\n\n\theight = video_get_x_height(kimage_ptr);\n\twidth = video_get_x_width(kimage_ptr);\n\n\twin_info_ptr->seginfo = 0;\n\twin_info_ptr->xim = 0;\n\twin_info_ptr->kimage_ptr = kimage_ptr;\n\twin_info_ptr->name_str = name_str;\n\twin_info_ptr->x_win = 0;\n\twin_info_ptr->x_winGC = 0;\n\twin_info_ptr->delete_atom = 0;\n\twin_info_ptr->active = 0;\n\twin_info_ptr->x_use_shmem = 0;\n\twin_info_ptr->width_req = width;\n\twin_info_ptr->pixels_per_line = width;\n\twin_info_ptr->main_height = height;\n\twin_info_ptr->full_min_width = width;\n\twin_info_ptr->full_min_height = height;\n\tvid_printf(\"init win %p (main:%p) width:%d, height:%d\\n\", win_info_ptr,\n\t\t&g_mainwin_info, width, height);\n\n\tx_use_shmem = 0;\n\t\t// Allow cmd-line args to force shmem off\n/* Check for XShm */\n#ifdef X_SHARED_MEM\n\tif(sim_get_use_shmem()) {\n\t\tret = XShmQueryExtension(g_display);\n\t\tif(ret == 0) {\n\t\t\tprintf(\"XShmQueryExt ret: %d, not using shared mem\\n\",\n\t\t\t\t\t\t\t\t\tret);\n\t\t} else {\n\t\t\tvid_printf(\"Will use shared memory for X\\n\");\n\t\t\tx_use_shmem = 1;\n\t\t}\n\t}\n#endif\n\twin_info_ptr->x_use_shmem = x_use_shmem;\n\n\tvideo_update_scale(kimage_ptr, win_info_ptr->width_req,\n\t\t\t\t\t\twin_info_ptr->main_height, 1);\n}\n\nvoid\nx_create_window(Window_info *win_info_ptr)\n{\n\tXGCValues new_gc;\n\tXSetWindowAttributes win_attr;\n\tWindow\tx_win;\n\tGC\tx_winGC;\n\tword32\tcreate_win_list;\n\tint\twidth, height, x_xpos, x_ypos;\n\n\twin_attr.event_mask = X_A2_WIN_EVENT_LIST;\n\twin_attr.colormap = g_default_colormap;\n\twin_attr.backing_store = WhenMapped;\n\twin_attr.border_pixel = 1;\n\twin_attr.background_pixel = 0;\n\tif(g_x_warp_pointer) {\n\t\twin_attr.cursor = g_cursor;\n\t} else {\n\t\twin_attr.cursor = None;\n\t}\n\n\tvid_printf(\"About to win, depth: %d\\n\", g_x_screen_depth);\n\tfflush(stdout);\n\n\tcreate_win_list = CWEventMask | CWBackingStore | CWCursor;\n\tcreate_win_list |= CWColormap | CWBorderPixel | CWBackPixel;\n\n\tx_xpos = video_get_x_xpos(win_info_ptr->kimage_ptr);\n\tx_ypos = video_get_x_ypos(win_info_ptr->kimage_ptr);\n\twidth = win_info_ptr->width_req;\n\theight = win_info_ptr->main_height;\n\n\tx_win = XCreateWindow(g_display, RootWindow(g_display, g_screen_num),\n\t\tx_xpos, x_ypos, width, height, 0, g_x_screen_depth,\n\t\tInputOutput, g_vis, create_win_list, &win_attr);\n\tvid_printf(\"x_win = %d, width:%d, height:%d\\n\", (int)x_win, width,\n\t\t\t\t\t\t\t\t\theight);\n\n\tXFlush(g_display);\n\n\twin_info_ptr->x_win = x_win;\n\twin_info_ptr->active = 0;\n\tvideo_set_active(win_info_ptr->kimage_ptr, 1);\n\n\tx_allocate_window_data(win_info_ptr);\n\n\tif(!win_info_ptr->x_use_shmem) {\n\t\tprintf(\"Not using shared memory, setting skip_amt = 2, \"\n\t\t\t\t\t\t\t\"g_audio_enable=0\\n\");\n\t\tvideo_set_redraw_skip_amt(2);\n\t\tg_audio_enable = 0;\n\t}\n\n\tx_set_size_hints(win_info_ptr);\n\n\tXMapRaised(g_display, x_win);\n\n\tif(win_info_ptr != &g_mainwin_info) {\n\t\t// Debugger window\n\t\twin_info_ptr->delete_atom = XInternAtom(g_display,\n\t\t\t\t\t\t\"WM_DELETE_WINDOW\", False);\n\t\tXSetWMProtocols(g_display, x_win, &(win_info_ptr->delete_atom),\n\t\t\t\t\t\t\t\t\t1);\n\t}\n\n\tXSync(g_display, False);\n\n\tx_winGC = XCreateGC(g_display, x_win, 0, (XGCValues *) 0);\n\twin_info_ptr->x_winGC = x_winGC;\n\twin_info_ptr->active = 1;\n\n\tnew_gc.fill_style = FillSolid;\n\tXChangeGC(g_display, x_winGC, GCFillStyle, &new_gc);\n\n\t/* XSync(g_display, False); */\n\n\tXFlush(g_display);\n\tfflush(stdout);\n}\n\nint g_xshm_error = 0;\n\nint\nxhandle_shm_error(Display *display, XErrorEvent *event)\n{\n\tg_xshm_error = 1;\n\treturn 0;\n}\n\nvoid\nx_allocate_window_data(Window_info *win_info_ptr)\n{\n\tint\twidth, height;\n\n\twidth = g_x_max_width;\n\theight = g_x_max_height;\n\tif(win_info_ptr->x_use_shmem) {\n\t\twin_info_ptr->x_use_shmem = 0;\t\t// Default to no shmem\n\t\tget_shm(win_info_ptr, width, height);\n\t}\n\tif(!win_info_ptr->x_use_shmem) {\n\t\tget_ximage(win_info_ptr, width, height);\n\t}\n}\n\nvoid\nget_shm(Window_info *win_info_ptr, int width, int height)\n{\n#ifdef X_SHARED_MEM\n\tXShmSegmentInfo *seginfo;\n\tXImage *xim;\n\tint\t(*old_x_handler)(Display *, XErrorEvent *);\n\tint\tdepth, size;\n\n\tdepth = g_x_screen_depth;\t\t// 24, actual bits per pixel\n\n\tseginfo = (XShmSegmentInfo *)malloc(sizeof(XShmSegmentInfo));\n\txim = XShmCreateImage(g_display, g_vis, depth, ZPixmap,\n\t\t(char *)0, seginfo, width, height);\n\n\t/* check mdepth, which should be 32 */\n\tif(xim->bits_per_pixel != g_x_screen_mdepth) {\n\t\tprintf(\"get_shm bits_per_pix: %d != %d\\n\",\n\t\t\t\txim->bits_per_pixel, g_x_screen_mdepth);\n\t}\n\tvid_printf(\"xim: %p, DO_VERBOSE:%d, Verbose:%d, VERBOSE_VIDEO:%d\\n\",\n\t\txim, DO_VERBOSE, Verbose, VERBOSE_VIDEO);\n\n\tvid_printf(\"xim: %p\\n\", xim);\n\twin_info_ptr->seginfo = seginfo;\n\tif(xim == 0) {\n\t\treturn;\n\t}\n\tvid_printf(\"bytes_per_line:%d, height:%d\\n\", xim->bytes_per_line,\n\t\t\t\t\t\t\t\txim->height);\n\tsize = xim->bytes_per_line * xim->height;\n\tvid_printf(\"size: %d\\n\", size);\n\n\t/* It worked, we got it */\n\tseginfo->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);\n\tvid_printf(\"seginfo->shmid = %d, errno:%d, %s\\n\", seginfo->shmid,\n\t\terrno, strerror(errno));\n\tif(seginfo->shmid < 0) {\n\t\tXDestroyImage(xim);\n\t\treturn;\n\t}\n\n\t/* Still working */\n\tseginfo->shmaddr = (char *)shmat(seginfo->shmid, 0, 0);\n\tvid_printf(\"seginfo->shmaddr: %p\\n\", seginfo->shmaddr);\n\tif(seginfo->shmaddr == ((char *) -1)) {\n\t\tXDestroyImage(xim);\n\t\treturn;\n\t}\n\n\t/* Still working */\n\txim->data = seginfo->shmaddr;\n\tseginfo->readOnly = False;\n\tvid_printf(\"xim->data is %p, size:%08x\\n\", xim->data, size);\n\n\t/* XShmAttach will trigger X error if server is remote, so catch it */\n\tg_xshm_error = 0;\n\told_x_handler = XSetErrorHandler(xhandle_shm_error);\n\n\tXShmAttach(g_display, seginfo);\n\tXSync(g_display, False);\n\n\n\tvid_printf(\"about to RMID the shmid\\n\");\n\tshmctl(seginfo->shmid, IPC_RMID, 0);\n\n\tXFlush(g_display);\n\tXSetErrorHandler(old_x_handler);\n\n\tif(g_xshm_error) {\n\t\tXDestroyImage(xim);\n\t\t/* We could release the shared mem segment, but by doing the */\n\t\t/* RMID, it will go away when we die now, so just leave it */\n\t\tprintf(\"Not using shared memory\\n\");\n\t\treturn;\n\t}\n\n\twidth = xim->bytes_per_line;\n\twin_info_ptr->pixels_per_line = width / 4;\n\twin_info_ptr->xim = xim;\n\twin_info_ptr->x_use_shmem = 1;\n\n\tvid_printf(\"Sharing memory. xim: %p, xim->data: %p, width:%d\\n\", xim,\n\t\txim->data, win_info_ptr->pixels_per_line);\n#endif\t/* X_SHARED_MEM */\n}\n\nvoid\nget_ximage(Window_info *win_info_ptr, int width, int height)\n{\n\tXImage\t*xim;\n\tbyte\t*ptr;\n\tint\tdepth, mdepth, size;\n\n\tdepth = g_x_screen_depth;\n\tmdepth = g_x_screen_mdepth;\n\n\tsize = (width * height * mdepth) >> 3;\n\tprintf(\"Get_ximage, w:%d, h:%d, mdepth:%d, size:%08x\\n\", width,\n\t\t\t\t\t\theight, mdepth, size);\n\tptr = (byte *)malloc(size);\n\n\tvid_printf(\"ptr: %p\\n\", ptr);\n\n\tif(ptr == 0) {\n\t\tprintf(\"malloc for data failed, mdepth: %d\\n\", mdepth);\n\t\texit(2);\n\t}\n\n\twin_info_ptr->pixels_per_line = width;\n\n\txim = XCreateImage(g_display, g_vis, depth, ZPixmap, 0,\n\t\t(char *)ptr, width, height, 8, 0);\n\n#ifdef KEGS_BIG_ENDIAN\n\txim->byte_order = MSBFirst;\n#else\n\txim->byte_order = LSBFirst;\n#endif\n\t_XInitImageFuncPtrs(xim);\t/* adjust to new byte order */\n\n\t/* check mdepth! */\n\tif(xim->bits_per_pixel != mdepth) {\n\t\tprintf(\"shm_ximage bits_per_pix: %d != %d\\n\",\n\t\t\t\t\txim->bits_per_pixel, mdepth);\n\t}\n\n\tvid_printf(\"xim: %p\\n\", xim);\n\n\twin_info_ptr->xim = xim;\n\twin_info_ptr->x_use_shmem = 0;\n\n\treturn;\n}\n\nvoid\nx_set_size_hints(Window_info *win_info_ptr)\n{\n\tXSizeHints my_winSizeHints;\n\tXClassHint my_winClassHint;\n\tXTextProperty my_winText;\n\tKimage\t*kimage_ptr;\n\tint\twidth, height, a2_width, a2_height;\n\n\twidth = win_info_ptr->width_req;\n\theight = win_info_ptr->main_height;\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\tvideo_update_scale(kimage_ptr, width, height, 0);\n\n\ta2_width = video_get_a2_width(kimage_ptr);\n\ta2_height = video_get_a2_height(kimage_ptr);\n\n\tXStringListToTextProperty(&(win_info_ptr->name_str), 1, &my_winText);\n\n\tmy_winSizeHints.flags = PSize | PMinSize | PMaxSize | PAspect;\n\tmy_winSizeHints.width = width;\n\tmy_winSizeHints.height = height;\n\tmy_winSizeHints.min_width = a2_width;\n\tmy_winSizeHints.min_height = a2_height;\n\tmy_winSizeHints.max_width = g_x_max_width;\n\tmy_winSizeHints.max_height = g_x_max_height;\n\tmy_winSizeHints.min_aspect.x = a2_width - 1;\n\tmy_winSizeHints.min_aspect.y = a2_height;\n\tmy_winSizeHints.max_aspect.x = a2_width + 1;\n\tmy_winSizeHints.max_aspect.y = a2_height;\n\tmy_winClassHint.res_name = win_info_ptr->name_str;\n\tmy_winClassHint.res_class = win_info_ptr->name_str;\n\n\tXSetWMProperties(g_display, win_info_ptr->x_win, &my_winText,\n\t\t&my_winText, 0, 0, &my_winSizeHints, 0, &my_winClassHint);\n\t// printf(\"Did XSetWMProperties w:%d h:%d\\n\", width, height);\n}\n\nvoid\nx_resize_window(Window_info *win_info_ptr)\n{\n\tKimage\t*kimage_ptr;\n\tint\tx_width, x_height, ret;\n\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\tx_width = video_get_x_width(kimage_ptr);\n\tx_height = video_get_x_height(kimage_ptr);\n\n\twin_info_ptr->main_height = MY_MIN(x_height, g_x_max_height);\n\twin_info_ptr->width_req = MY_MIN(x_width, g_x_max_width);\n\n\tret = XResizeWindow(g_display, win_info_ptr->x_win, x_width, x_height);\n\tif(0) {\n\t\tprintf(\"XResizeWindow ret:%d, w:%d, h:%d\\n\", ret, x_width,\n\t\t\t\t\t\t\t\tx_height);\n\t}\n}\n\nvoid\nx_update_display(Window_info *win_info_ptr)\n{\n\tChange_rect rect;\n\tint\tdid_copy, valid, x_active, a2_active;\n\tint\ti;\n\n\t// Update active state\n\ta2_active = video_get_active(win_info_ptr->kimage_ptr);\n\tx_active = win_info_ptr->active;\n\tif(x_active && !a2_active) {\n\t\t// We need to unmap this window\n\t\tXUnmapWindow(g_display, win_info_ptr->x_win);\n\t\tx_active = 0;\n\t\twin_info_ptr->active = x_active;\n\t}\n\tif(!x_active && a2_active) {\n\t\t// We need to map this window (and maybe create it)\n\t\tif(win_info_ptr->xim == 0) {\n\t\t\tx_create_window(win_info_ptr);\n\t\t}\n\t\tXMapWindow(g_display, win_info_ptr->x_win);\n\t\tx_active = 1;\n\t\twin_info_ptr->active = x_active;\n\t}\n\tif(x_active == 0) {\n\t\treturn;\n\t}\n\n\tif(video_change_aspect_needed(win_info_ptr->kimage_ptr,\n\t\t\twin_info_ptr->width_req, win_info_ptr->main_height)) {\n\t\tx_resize_window(win_info_ptr);\n\t}\n\tdid_copy = 0;\n\tfor(i = 0; i < MAX_CHANGE_RECTS; i++) {\n\t\tvalid = video_out_data(win_info_ptr->xim->data,\n\t\t\t\twin_info_ptr->kimage_ptr,\n\t\t\t\twin_info_ptr->pixels_per_line, &rect, i);\n\t\tif(!valid) {\n\t\t\tbreak;\n\t\t}\n#if 0\n\t\tif(win_info_ptr == &g_debugwin_info) {\n\t\t\tprintf(\"  i:%d valid:%d, w:%d h:%d\\n\", i, valid,\n\t\t\t\t\trect.width, rect.height);\n\t\t}\n#endif\n\t\tdid_copy = 1;\n#ifdef X_SHARED_MEM\n\t\tif(win_info_ptr->x_use_shmem) {\n\t\t\tXShmPutImage(g_display, win_info_ptr->x_win,\n\t\t\t\twin_info_ptr->x_winGC,\n\t\t\t\twin_info_ptr->xim, rect.x, rect.y,\n\t\t\t\trect.x, rect.y,\n\t\t\t\trect.width, rect.height, False);\n\t\t}\n#endif\n\t\tif(!win_info_ptr->x_use_shmem) {\n\t\t\tXPutImage(g_display, win_info_ptr->x_win,\n\t\t\t\twin_info_ptr->x_winGC,\n\t\t\t\twin_info_ptr->xim, rect.x, rect.y,\n\t\t\t\trect.x, rect.y,\n\t\t\t\trect.width, rect.height);\n\t\t}\n\t}\n\n\tif(did_copy) {\n\t\tXFlush(g_display);\n\t}\n}\n\nWindow_info *\nx_find_xwin(Window in_win)\n{\n\tif(g_mainwin_info.kimage_ptr) {\n\t\tif(g_mainwin_info.x_win == in_win) {\n\t\t\treturn &g_mainwin_info;\n\t\t}\n\t}\n\tif(g_debugwin_info.kimage_ptr) {\n\t\tif(g_debugwin_info.x_win == in_win) {\n\t\t\treturn &g_debugwin_info;\n\t\t}\n\t}\n\tprintf(\"in_win:%d not found\\n\", (int)in_win);\n\texit(1);\n}\n\n#define KEYBUFLEN\t128\n\nint g_num_check_input_calls = 0;\nint g_check_input_flush_rate = 2;\n\n// https://stackoverflow.com/questions/72236711/trouble-with-xsetselectionowner\n//  See answer from \"n.m\" on May 17th.\nvoid\nx_send_copy_data(Window_info *win_info_ptr)\n{\n\tprintf(\"x_send_copy_data!\\n\");\n\tXSetSelectionOwner(g_display, XA_PRIMARY, win_info_ptr->x_win,\n\t\t\t\t\t\t\tCurrentTime);\n\t(void)cfg_text_screen_str();\n}\n\nvoid\nx_handle_copy(XSelectionRequestEvent *req_ev_ptr)\n{\n\tbyte\t*bptr;\n\tint\tret;\n\n\tbptr = (byte *)cfg_get_current_copy_selection();\n\tret = XChangeProperty(g_display, req_ev_ptr->requestor,\n\t\treq_ev_ptr->property, req_ev_ptr->target, 8,\n\t\tPropModeReplace, bptr, strlen((char *)bptr));\n\t\t// req_ev_ptr->target is either XA_STRING, or equivalent\n\tif(0) {\n\t\t// Seems to return 1, BadRequest always, but it works\n\t\tprintf(\"XChangeProperty ret: %d\\n\", ret);\n\t}\n}\n\nvoid\nx_handle_targets(XSelectionRequestEvent *req_ev_ptr)\n{\n\tint\tret;\n\n\t// Tell the other client what targets we can supply from\n\t//  g_x_targets_array[]\n\tret = XChangeProperty(g_display, req_ev_ptr->requestor,\n\t\treq_ev_ptr->property, XA_ATOM, 32,\n\t\tPropModeReplace, (byte *)&g_x_targets_array[0],\n\t\tg_x_num_targets);\n\tif(0) {\n\t\t// Seems to return 1, BadRequest always, but it works\n\t\tprintf(\"XChangeProperty TARGETS ret: %d\\n\", ret);\n\t}\n}\n\nvoid\nx_request_paste_data(Window_info *win_info_ptr)\n{\n\tprintf(\"Pasting selection\\n\");\n\t// printf(\"Calling XConvertSelection\\n\");\n\tXConvertSelection(g_display, XA_PRIMARY, XA_STRING, XA_STRING,\n\t\twin_info_ptr->x_win, CurrentTime);\n\t// This will cause a SelectionNotify event, and we get the data\n\t//  by using XGetWindowProperty on our own window.  This will eventually\n\t//  call x_handle_paste().\n}\n\nvoid\nx_handle_paste(Window w, Atom property)\n{\n\tbyte\t*bptr;\n\tAtom\tsel_type;\n\tunsigned long sel_nitems, sel_bytes_after;\n\tlong\tsel_length;\n\tint\tsel_format, ret, ret2, c;\n\tint\ti;\n\n\tsel_length = 16384;\n\tsel_type = 0;\n\tsel_format = 0;\n\tsel_nitems = 0;\n\tsel_bytes_after = 0;\n\tbptr = 0;\n\tret = XGetWindowProperty(g_display, w, property, 0, sel_length, 1,\n\t\tAnyPropertyType, &sel_type, &sel_format, &sel_nitems,\n\t\t&sel_bytes_after, &bptr);\n#if 0\n\tprintf(\"XGetWindowProperty ret:%d, sel_type:%ld, sel_format:%d, \"\n\t\t\"sel_nitems:%ld, sel_bytes_after:%ld, bptr:%p\\n\",\n\t\tret, sel_type, sel_format, sel_nitems, sel_bytes_after, bptr);\n#endif\n\tif(bptr && (sel_type == property) && sel_nitems && (sel_format == 8)) {\n\t\t//printf(\"bptr: %s\\n\", (char *)bptr);\n\t\tfor(i = 0; i < sel_nitems; i++) {\n\t\t\tc = bptr[i];\n\t\t\tret2 = adb_paste_add_buf(c);\n\t\t\tif(ret2) {\n\t\t\t\tprintf(\"Paste buffer full!\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif(ret == 0) {\n\t\tXFree(bptr);\n\t}\n}\n\nint\nx_update_mouse(Window_info *win_info_ptr, int raw_x, int raw_y,\n\t\t\t\tint button_states, int buttons_valid)\n{\n\tKimage\t*kimage_ptr;\n\tint\tx, y, ret;\n\n\tif((button_states & buttons_valid & 2) == 2) {\n\t\t// Middle button: Paste request\n\t\tx_request_paste_data(win_info_ptr);\n\t\tbutton_states = button_states & (~2);\n\t}\n\tkimage_ptr = win_info_ptr->kimage_ptr;\n\tx = video_scale_mouse_x(kimage_ptr, raw_x, 0);\n\ty = video_scale_mouse_y(kimage_ptr, raw_y, 0);\n\n\tif(g_x_warp_pointer && (raw_x == g_x_warp_x) &&\n\t\t\t(raw_y == g_x_warp_y) && (buttons_valid == 0) ) {\n\t\t/* tell adb routs to recenter but ignore this motion */\n\t\tadb_update_mouse(kimage_ptr, x, y, 0, -1);\n\t\treturn 0;\n\t}\n\tret = adb_update_mouse(kimage_ptr, x, y, button_states,\n\t\t\t\t\t\t\tbuttons_valid & 7);\n\treturn ret;\n}\n\nvoid\nx_input_events()\n{\n\tXEvent\tev, response;\n\tXSelectionRequestEvent *req_ev_ptr;\n\tWindow_info *win_info_ptr;\n\tchar\t*str;\n\tint\tlen, motion, key_or_mouse, refresh_needed, buttons, hide, warp;\n\tint\twidth, height, resp_property, match, x_xpos, x_ypos;\n\tint\ti;\n\n\tstr = 0;\n\tif(str) {\n\t\t// Use str\n\t}\n\tif(adb_get_copy_requested()) {\n\t\tx_send_copy_data(&g_mainwin_info);\n\t}\n\tg_num_check_input_calls--;\n\tif(g_num_check_input_calls < 0) {\n\t\tlen = XPending(g_display);\n\t\tg_num_check_input_calls = g_check_input_flush_rate;\n\t} else {\n\t\tlen = QLength(g_display);\n\t}\n\n\tmotion = 0;\n\twin_info_ptr = 0;\n\trefresh_needed = 0;\n\tkey_or_mouse = 0;\n\twhile(len > 0) {\n\t\tXNextEvent(g_display, &ev);\n\t\tlen--;\n\t\tif(len == 0) {\n\t\t\t/* Xaccel on linux only buffers one X event */\n\t\t\t/*  must look for more now */\n\t\t\tlen = XPending(g_display);\n\t\t}\n\t\tswitch(ev.type) {\n\t\tcase FocusIn:\n\t\tcase FocusOut:\n\t\t\twin_info_ptr = x_find_xwin(ev.xfocus.window);\n\t\t\tif(ev.xfocus.type == FocusOut) {\n\t\t\t\t/* Allow keyrepeat again! */\n\t\t\t\tvid_printf(\"Left window, auto repeat on\\n\");\n\t\t\t\tx_auto_repeat_on(0);\n\t\t\t} else if(ev.xfocus.type == FocusIn &&\n\t\t\t\t\t(win_info_ptr == &g_mainwin_info)) {\n\t\t\t\t/* Allow keyrepeat again! */\n\t\t\t\tvid_printf(\"Enter window, auto repeat off\\n\");\n\t\t\t\tx_auto_repeat_off(0);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase EnterNotify:\n\t\tcase LeaveNotify:\n\t\t\t/* These events are disabled now */\n\t\t\tprintf(\"Enter/Leave event for winow %08x, sub: %08x\\n\",\n\t\t\t\t(word32)ev.xcrossing.window,\n\t\t\t\t(word32)ev.xcrossing.subwindow);\n\t\t\tprintf(\"Enter/L mode: %08x, detail: %08x, type:%02x\\n\",\n\t\t\t\tev.xcrossing.mode, ev.xcrossing.detail,\n\t\t\t\tev.xcrossing.type);\n\t\t\tbreak;\n\t\tcase ButtonPress:\n\t\t\twin_info_ptr = x_find_xwin(ev.xbutton.window);\n\t\t\tvid_printf(\"Got button press of button %d!\\n\",\n\t\t\t\t\t\tev.xbutton.button);\n\t\t\tbuttons = (1 << ev.xbutton.button) >> 1;\n\t\t\tmotion |= x_update_mouse(win_info_ptr, ev.xbutton.x,\n\t\t\t\tev.xbutton.y, buttons, buttons & 7);\n\t\t\tkey_or_mouse = 1;\n\t\t\tbreak;\n\t\tcase ButtonRelease:\n\t\t\twin_info_ptr = x_find_xwin(ev.xbutton.window);\n\t\t\tbuttons = (1 << ev.xbutton.button) >> 1;\n\t\t\tmotion |= x_update_mouse(win_info_ptr, ev.xbutton.x,\n\t\t\t\t\t\tev.xbutton.y, 0, buttons & 7);\n\t\t\tkey_or_mouse = 1;\n\t\t\tbreak;\n\t\tcase MotionNotify:\n\t\t\twin_info_ptr = x_find_xwin(ev.xmotion.window);\n\t\t\tmotion |= x_update_mouse(win_info_ptr, ev.xmotion.x,\n\t\t\t\t\t\tev.xmotion.y, 0, 0);\n\t\t\tkey_or_mouse = 1;\n\t\t\tbreak;\n\t\tcase Expose:\n\t\t\twin_info_ptr = x_find_xwin(ev.xexpose.window);\n\t\t\trefresh_needed = -1;\n\t\t\t//printf(\"Got an Expose event\\n\");\n\t\t\tbreak;\n\t\tcase NoExpose:\n\t\t\t/* do nothing */\n\t\t\tbreak;\n\t\tcase KeyPress:\n\t\tcase KeyRelease:\n\t\t\tx_handle_keysym(&ev);\n\t\t\tkey_or_mouse = 1;\n\t\t\tbreak;\n\t\tcase KeymapNotify:\n\t\t\tbreak;\n\t\tcase DestroyNotify:\n\t\t\twin_info_ptr = x_find_xwin(ev.xdestroywindow.window);\n\t\t\tvideo_set_active(win_info_ptr->kimage_ptr, 0);\n\t\t\twin_info_ptr->active = 0;\n\t\t\tprintf(\"Destroy %s\\n\", win_info_ptr->name_str);\n\t\t\tif(win_info_ptr == &g_mainwin_info) {\n\t\t\t\tx_try_xset_r();\t\t// Mainwin: quit BURST\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ReparentNotify:\n\t\tcase UnmapNotify:\n\t\tcase MapNotify:\n\t\t\tbreak;\n\t\tcase ClientMessage:\n\t\t\twin_info_ptr = x_find_xwin(ev.xclient.window);\n\t\t\tif(ev.xclient.data.l[0] == win_info_ptr->delete_atom) {\n\t\t\t\t// This is a WM_DELETE_WINDOW event\n\t\t\t\t// Just unmap the window\n\t\t\t\twin_info_ptr->kimage_ptr->active = 0;\n\t\t\t} else {\n\t\t\t\tprintf(\"unknown ClientMessage\\n\");\n\t\t\t}\n\t\t\tbreak;\n\t\tcase ConfigureNotify:\n\t\t\twin_info_ptr = x_find_xwin(ev.xconfigure.window);\n\t\t\twidth = ev.xconfigure.width;\n\t\t\theight = ev.xconfigure.height;\n#if 0\n\t\t\tprintf(\"ConfigureNotify, width:%d, height:%d\\n\",\n\t\t\t\twidth, height);\n#endif\n\t\t\tvideo_update_scale(win_info_ptr->kimage_ptr, width,\n\t\t\t\t\t\t\t\theight, 0);\n\t\t\tx_xpos = ev.xconfigure.x;\n\t\t\tx_ypos = ev.xconfigure.y;\n\t\t\tvideo_update_xpos_ypos(win_info_ptr->kimage_ptr,\n\t\t\t\t\t\t\tx_xpos, x_ypos);\n\t\t\tbreak;\n\t\tcase SelectionRequest:\n\t\t\t//printf(\"SelectionRequest received\\n\");\n\t\t\treq_ev_ptr = &(ev.xselectionrequest);\n\t\t\t// This is part of the dance for copy: Another client\n\t\t\t//  is asking us what format we can supply (TARGETS),\n\t\t\t//  or is doing to tell us one at a time what types\n\t\t\t//  it would like.\n#if 0\n\t\t\tprintf(\"req:%ld, property:%ld, target:%ld, \"\n\t\t\t\t\"selection:%ld\\n\", req_ev_ptr->requestor,\n\t\t\t\treq_ev_ptr->property, req_ev_ptr->target,\n\t\t\t\treq_ev_ptr->selection);\n\t\t\tstr = XGetAtomName(g_display, req_ev_ptr->target);\n\t\t\tprintf(\"XAtom target str: %s\\n\", str);\n\t\t\tXFree(str);\n\t\t\tstr = XGetAtomName(g_display, req_ev_ptr->property);\n\t\t\tprintf(\"XAtom property str: %s\\n\", str);\n\t\t\tXFree(str);\n#endif\n\t\t\tresp_property = None;\n\t\t\tmatch = 0;\n\t\t\tfor(i = 0; i < g_x_num_targets; i++) {\n\t\t\t\tif(req_ev_ptr->target == g_x_targets_array[i]) {\n\t\t\t\t\tmatch = 1;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(match) {\n\t\t\t\tx_handle_copy(req_ev_ptr);\n\t\t\t\tresp_property = req_ev_ptr->property;\n\t\t\t} else if(req_ev_ptr->target == g_x_atom_targets) {\n\t\t\t\t// Some other agent is asking us \"TARGETS\",\n\t\t\t\t//  so send our list of targets\n\t\t\t\tx_handle_targets(req_ev_ptr);\n\t\t\t}\n\t\t\t// But no matter what the request target was, respond\n\t\t\t//  so it will send an eventual request for XA_STRING\n\t\t\tresponse.xselection.type = SelectionNotify;\n\t\t\tresponse.xselection.display = req_ev_ptr->display;\n\t\t\tresponse.xselection.requestor = req_ev_ptr->requestor;\n\t\t\tresponse.xselection.selection = req_ev_ptr->selection;\n\t\t\tresponse.xselection.target = req_ev_ptr->target;\n\t\t\tresponse.xselection.property = resp_property;\n\t\t\tresponse.xselection.time = req_ev_ptr->time;\n\t\t\tXSendEvent(g_display, req_ev_ptr->requestor, 0, 0,\n\t\t\t\t\t\t\t\t&response);\n\t\t\tXFlush(g_display);\t// Speed up getting more resp\n\t\t\tbreak;\n\t\tcase SelectionNotify:\n\t\t\t// We get this event after we requested the PRIMARY\n\t\t\t//  selection, so paste this to adb().\n\t\t\tvid_printf(\"SelectionNotify received\\n\");\n\t\t\tvid_printf(\"req:%ld, selection:%ld, target:%ld, \"\n\t\t\t\t\"property:%ld\\n\", ev.xselection.requestor,\n\t\t\t\tev.xselection.selection,\n\t\t\t\tev.xselection.target,\n\t\t\t\tev.xselection.property);\n\t\t\tif(ev.xselection.property == None) {\n\t\t\t\tprintf(\"No selection\\n\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tx_handle_paste(ev.xselection.requestor,\n\t\t\t\t\t\t\tev.xselection.property);\n\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tprintf(\"X event 0x%08x is unknown!\\n\",\n\t\t\t\tev.type);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif(key_or_mouse && (win_info_ptr == &g_mainwin_info)) {\n\t\thide = adb_get_hide_warp_info(win_info_ptr->kimage_ptr, &warp);\n\t\tif(warp != g_x_warp_pointer) {\n\t\t\tmotion = 1;\n\t\t}\n\t\tg_x_warp_pointer = warp;\n\t\tif(g_x_hide_pointer != hide) {\n\t\t\tx_hide_pointer(&g_mainwin_info, hide);\n\t\t}\n\t\tg_x_hide_pointer = hide;\n\t}\n\n\tif(motion && g_x_warp_pointer && (win_info_ptr == &g_mainwin_info)) {\n\t\t// Calculate where to warp to\n\t\tg_x_warp_x = video_unscale_mouse_x(win_info_ptr->kimage_ptr,\n\t\t\tBASE_MARGIN_LEFT + (BASE_WINDOW_WIDTH/2), 0);\n\t\tg_x_warp_y = video_unscale_mouse_y(win_info_ptr->kimage_ptr,\n\t\t\tBASE_MARGIN_TOP + (A2_WINDOW_HEIGHT/2), 0);\n\n\t\tXWarpPointer(g_display, None, win_info_ptr->x_win, 0, 0, 0, 0,\n\t\t\tg_x_warp_x, g_x_warp_y);\n\t}\n\n\tif(refresh_needed && win_info_ptr) {\n\t\t//printf(\"...at end, refresh_needed:%d\\n\", refresh_needed);\n\t\tvideo_set_x_refresh_needed(win_info_ptr->kimage_ptr, 1);\n\t}\n\n}\n\nvoid\nx_hide_pointer(Window_info *win_info_ptr, int do_hide)\n{\n\tif(do_hide) {\t\t\t\t// invisible\n\t\tXDefineCursor(g_display, win_info_ptr->x_win, g_cursor);\n\t} else {\t\t\t\t// Default cursor\n\t\tXDefineCursor(g_display, win_info_ptr->x_win, None);\n\t}\n}\n\nvoid\nx_handle_keysym(XEvent *xev_in)\n{\n\tWindow_info *win_info_ptr;\n\tKeySym\tkeysym;\n\tword32\tstate;\n\tint\tkeycode, a2code, type, is_up;\n\n\twin_info_ptr = x_find_xwin(xev_in->xkey.window);\n\n\tkeycode = xev_in->xkey.keycode;\n\ttype = xev_in->xkey.type;\n\n\tkeysym = XLookupKeysym(&(xev_in->xkey), 0);\n\n\tstate = xev_in->xkey.state;\n\n\tvid_printf(\"keycode: %d, type: %d, state:%d, sym: %08x\\n\",\n\t\tkeycode, type, state, (word32)keysym);\n\n\tx_update_modifier_state(win_info_ptr, state);\n\n\tis_up = 0;\n\tif(type == KeyRelease) {\n\t\tis_up = 1;\n\t}\n\n\t/* first, do conversions */\n\tswitch(keysym) {\n\tcase XK_Alt_L:\n\tcase XK_Meta_R:\t\t\t\t// Windows key on right side\n\tcase XK_Super_R:\n\tcase XK_Mode_switch:\n\tcase XK_Cancel:\n\t\tkeysym = XK_Print;\t\t/* option */\n\t\tbreak;\n\tcase XK_Meta_L:\t\t\t\t// Windows key on left side\n\tcase XK_Alt_R:\n\tcase XK_Super_L:\n\tcase XK_Menu:\n\t\tkeysym = XK_Scroll_Lock;\t/* cmd */\n\t\tbreak;\n\tcase XK_F5:\n\t\tbreak;\n\tcase XK_F10:\n\t\tif(!is_up) {\n\t\t\tg_video_scale_algorithm++;\n\t\t\tif(g_video_scale_algorithm >= 3) {\n\t\t\t\tg_video_scale_algorithm = 0;\n\t\t\t}\n\t\t\tprintf(\"g_video_scale_algorithm = %d\\n\",\n\t\t\t\t\t\tg_video_scale_algorithm);\n\t\t\tvideo_update_scale(win_info_ptr->kimage_ptr,\n\t\t\t\t\twin_info_ptr->width_req,\n\t\t\t\t\twin_info_ptr->main_height, 1);\n\t\t}\n\t\tbreak;\n\tcase 0x1000003:\n\t\tif(keycode == 0x3c) {\n\t\t\t/* enter key on Mac OS X laptop--make it option */\n\t\t\tkeysym = XK_Print;\n\t\t}\n\t\tbreak;\n\tcase NoSymbol:\n\t\tswitch(keycode) {\n\t\t/* 94-95 are for my PC101 kbd + windows keys on HPUX */\n\t\tcase 0x0095:\n\t\t\t/* left windows key = option */\n\t\t\tkeysym = XK_Print;\n\t\t\tbreak;\n\t\tcase 0x0096:\n\t\tcase 0x0094:\n\t\t\t/* right windows key = cmd */\n\t\t\tkeysym = XK_Scroll_Lock;\n\t\t\tbreak;\n\t\t/* 0072 is for cra@WPI.EDU who says it's Break under XFree86 */\n\t\tcase 0x0072:\n\t\t/* 006e is break according to mic@research.nj.nec.com */\n\t\tcase 0x006e:\n\t\t\tkeysym = XK_Break;\n\t\t\tbreak;\n\n\t\t/* 0x0042, 0x0046, and 0x0048 are the windows keys according */\n\t\t/*  to Geoff Weiss on Solaris x86 */\n\t\tcase 0x0042:\n\t\tcase 0x0046:\n\t\t\t/* flying windows == open apple */\n\t\t\tkeysym = XK_Scroll_Lock;\n\t\t\tbreak;\n\t\tcase 0x0048:\n\t\tcase 0x0076:\t\t/* Windows menu key on Mac OS X */\n\t\t\t/* menu windows == option */\n\t\t\tkeysym = XK_Print;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\ta2code = x_keysym_to_a2code(win_info_ptr, (int)keysym, is_up);\n\tif(a2code >= 0) {\n\t\tadb_physical_key_update(win_info_ptr->kimage_ptr, a2code,\n\t\t\t0, is_up);\n\t} else if(a2code != -2) {\n\t\tprintf(\"Keysym: %04x of keycode: %02x unknown\\n\",\n\t\t\t(word32)keysym, keycode);\n\t}\n}\n\nint\nx_keysym_to_a2code(Window_info *win_info_ptr, int keysym, int is_up)\n{\n\tint\ti;\n\n\tif(keysym == 0) {\n\t\treturn -1;\n\t}\n\n\t/* Look up Apple 2 keycode */\n\tfor(i = g_num_a2_keycodes - 1; i >= 0; i--) {\n\t\tif((keysym == g_x_a2_key_to_xsym[i][1]) ||\n\t\t\t\t\t(keysym == g_x_a2_key_to_xsym[i][2])) {\n\n\t\t\tvid_printf(\"Found keysym:%04x = a[%d] = %04x or %04x\\n\",\n\t\t\t\t(int)keysym, i, g_x_a2_key_to_xsym[i][1],\n\t\t\t\tg_x_a2_key_to_xsym[i][2]);\n\n\t\t\treturn g_x_a2_key_to_xsym[i][0];\n\t\t}\n\t}\n\n\treturn -1;\n}\n\nvoid\nx_update_modifier_state(Window_info *win_info_ptr, int state)\n{\n\tword32\tc025_val;\n\n\tc025_val = 0;\n\tif(state & ShiftMask) {\n\t\tc025_val |= 1;\n\t}\n\tif(state & ControlMask) {\n\t\tc025_val |= 2;\n\t}\n\tif(state & LockMask) {\n\t\tc025_val |= 4;\n\t}\n\tadb_update_c025_mask(win_info_ptr->kimage_ptr, c025_val, 7);\n}\n\nvoid\nx_auto_repeat_on(int must)\n{\n\tif((g_auto_repeat_on <= 0) || must) {\n\t\tg_auto_repeat_on = 1;\n\t\tXAutoRepeatOn(g_display);\n\t\tXFlush(g_display);\n\t\tadb_kbd_repeat_off();\n\t}\n}\n\nvoid\nx_auto_repeat_off(int must)\n{\n\tif((g_auto_repeat_on != 0) || must) {\n\t\tXAutoRepeatOff(g_display);\n\t\tXFlush(g_display);\n\t\tg_auto_repeat_on = 0;\n\t\tadb_kbd_repeat_off();\n\t}\n}\n\nvoid\nx_full_screen(int do_full)\n{\n\treturn;\n}\n"
  }
]