From 2c0b818abad30349774e935fd3c43b63018b6d9a Mon Sep 17 00:00:00 2001 From: Wayne Warthen Date: Mon, 14 Feb 2022 13:36:06 -0800 Subject: [PATCH] ZXCC Cleanup - I know I said I was done, but I found some more stuff to clean up. I think I am really done now. --- Binary/Apps/Makefile | 2 +- Tools/unix/zxcc/Build-VC.cmd | 6 +- Tools/unix/zxcc/COPYING | 678 +++++----- Tools/unix/zxcc/cbops.h | 242 ++-- Tools/unix/zxcc/config.h.windows | 5 + Tools/unix/zxcc/cpmdrv.c | 368 +++--- Tools/unix/zxcc/cpmglob.c | 1188 +++++++++--------- Tools/unix/zxcc/cpmint.h | 523 ++++---- Tools/unix/zxcc/cpmparse.c | 206 ++-- Tools/unix/zxcc/cpmredir.c | 1971 +++++++++++++++--------------- Tools/unix/zxcc/cpmredir.h | 301 +++-- Tools/unix/zxcc/dirent.c | 221 ++-- Tools/unix/zxcc/dirent.h | 48 +- Tools/unix/zxcc/drdos.c | 96 +- Tools/unix/zxcc/track.c | 48 +- Tools/unix/zxcc/util.c | 892 +++++++------- Tools/unix/zxcc/xlt.c | 312 +++-- Tools/unix/zxcc/z80.c | 2 +- Tools/unix/zxcc/z80.h | 34 +- Tools/unix/zxcc/zxbdos.c | 1112 +++++++++-------- Tools/unix/zxcc/zxbdos.h | 99 +- Tools/unix/zxcc/zxcbdos.c | 299 +++-- Tools/unix/zxcc/zxcbdos.h | 10 +- Tools/unix/zxcc/zxcc.c | 1096 +++++++++-------- Tools/unix/zxcc/zxcc.h | 267 ++-- Tools/unix/zxcc/zxdbdos.c | 62 +- Tools/unix/zxcc/zxdbdos.h | 12 +- Tools/zxcc/COPYING | 678 +++++----- Tools/zxcc/zxcc-src.zip | Bin 69571 -> 70311 bytes Tools/zxcc/zxcc.exe | Bin 95744 -> 96256 bytes Tools/zxcc/zxccdbg.exe | Bin 104448 -> 120320 bytes 31 files changed, 5449 insertions(+), 5329 deletions(-) diff --git a/Binary/Apps/Makefile b/Binary/Apps/Makefile index 20ebfc63..0b913750 100644 --- a/Binary/Apps/Makefile +++ b/Binary/Apps/Makefile @@ -8,4 +8,4 @@ all:: mkdir -p Tunes clobber:: - @rm -f *.bin *.com *.img *.rom *.pdf *.log *.eeprom *.ovr *.hlp *.doc *.COM *.BIN Tunes/*.mym Tunes/*.pt? + @rm -f *.bin *.com *.img *.rom *.pdf *.log *.eeprom *.ovr *.hlp *.doc *.COM *.BIN Tunes/*.mym Tunes/*.pt? Tunes/*.vgm diff --git a/Tools/unix/zxcc/Build-VC.cmd b/Tools/unix/zxcc/Build-VC.cmd index 2d199c6d..b9383633 100644 --- a/Tools/unix/zxcc/Build-VC.cmd +++ b/Tools/unix/zxcc/Build-VC.cmd @@ -5,7 +5,7 @@ setlocal :: Visual Studio x86 Native Tools Command Prompt is assumed :: -:: Below configures VS2012 to target Windows XP and beyond +:: Below configures VS2012 to target Windows XP. :: Not sure if it will work in later versions of VS, but seems :: to do no harm. set INCLUDE=%ProgramFiles(x86)%\Microsoft SDKs\Windows\7.1A\Include;%INCLUDE% @@ -16,10 +16,10 @@ set LINK=/SUBSYSTEM:CONSOLE,5.01 %LINK% copy config.h.windows config.h -cl zxcc.c cpmdrv.c cpmglob.c cpmparse.c cpmredir.c drdos.c util.c xlt.c zxbdos.c zxcbdos.c zxdbdos.c z80.c dirent.c track.c +cl -I. zxcc.c cpmdrv.c cpmglob.c cpmparse.c cpmredir.c drdos.c util.c xlt.c zxbdos.c zxcbdos.c zxdbdos.c z80.c dirent.c track.c if errorlevel 1 exit /b 255 -cl /DDEBUG /Fe"zxccdbg.exe" zxcc.c cpmdrv.c cpmglob.c cpmparse.c cpmredir.c drdos.c util.c xlt.c zxbdos.c zxcbdos.c zxdbdos.c z80.c dirent.c track.c +cl -I. /DDEBUG /Fe"zxccdbg.exe" zxcc.c cpmdrv.c cpmglob.c cpmparse.c cpmredir.c drdos.c util.c xlt.c zxbdos.c zxcbdos.c zxdbdos.c z80.c dirent.c track.c if errorlevel 1 exit /b 255 copy cpm\bios.bin . \ No newline at end of file diff --git a/Tools/unix/zxcc/COPYING b/Tools/unix/zxcc/COPYING index a43ea212..92851102 100644 --- a/Tools/unix/zxcc/COPYING +++ b/Tools/unix/zxcc/COPYING @@ -1,339 +1,339 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 675 Mass Ave, Cambridge, MA 02139, USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Tools/unix/zxcc/cbops.h b/Tools/unix/zxcc/cbops.h index f583e3a8..6772b004 100644 --- a/Tools/unix/zxcc/cbops.h +++ b/Tools/unix/zxcc/cbops.h @@ -33,128 +33,128 @@ #define res(n,x) (x&=~(1<>3)&7; - switch(op&0xc7){ - case 0x40: bit(n,b); break; - case 0x41: bit(n,c); break; - case 0x42: bit(n,d); break; - case 0x43: bit(n,e); break; - case 0x44: bit(n,h); break; - case 0x45: bit(n,l); break; - case 0x46: tstates+=4;val=fetch(addr);bit(n,val);store(addr,val);break; - case 0x47: bit(n,a); break; - case 0x80: res(n,b); break; - case 0x81: res(n,c); break; - case 0x82: res(n,d); break; - case 0x83: res(n,e); break; - case 0x84: res(n,h); break; - case 0x85: res(n,l); break; - case 0x86: tstates+=4;val=fetch(addr);res(n,val);store(addr,val);break; - case 0x87: res(n,a); break; - case 0xc0: set(n,b); break; - case 0xc1: set(n,c); break; - case 0xc2: set(n,d); break; - case 0xc3: set(n,e); break; - case 0xc4: set(n,h); break; - case 0xc5: set(n,l); break; - case 0xc6: tstates+=4;val=fetch(addr);set(n,val);store(addr,val);break; - case 0xc7: set(n,a); break; - } - } - if(ixoriy)switch(reg){ - case 0:b=val; break; - case 1:c=val; break; - case 2:d=val; break; - case 3:e=val; break; - case 4:h=val; break; - case 5:l=val; break; - case 7:a=val; break; - } +if (op < 64)switch (op) { +case 0: rlc(b); break; +case 1: rlc(c); break; +case 2: rlc(d); break; +case 3: rlc(e); break; +case 4: rlc(h); break; +case 5: rlc(l); break; +case 6: tstates += 7; val = fetch(addr); rlc(val); store(addr, val); break; +case 7: rlc(a); break; +case 8: rrc(b); break; +case 9: rrc(c); break; +case 10: rrc(d); break; +case 11: rrc(e); break; +case 12: rrc(h); break; +case 13: rrc(l); break; +case 14: tstates += 7; val = fetch(addr); rrc(val); store(addr, val); break; +case 15: rrc(a); break; +case 0x10: rl(b); break; +case 0x11: rl(c); break; +case 0x12: rl(d); break; +case 0x13: rl(e); break; +case 0x14: rl(h); break; +case 0x15: rl(l); break; +case 0x16: tstates += 7; val = fetch(addr); rl(val); store(addr, val); break; +case 0x17: rl(a); break; +case 0x18: rr(b); break; +case 0x19: rr(c); break; +case 0x1a: rr(d); break; +case 0x1b: rr(e); break; +case 0x1c: rr(h); break; +case 0x1d: rr(l); break; +case 0x1e: tstates += 7; val = fetch(addr); rr(val); store(addr, val); break; +case 0x1f: rr(a); break; +case 0x20: sla(b); break; +case 0x21: sla(c); break; +case 0x22: sla(d); break; +case 0x23: sla(e); break; +case 0x24: sla(h); break; +case 0x25: sla(l); break; +case 0x26: tstates += 7; val = fetch(addr); sla(val); store(addr, val); break; +case 0x27: sla(a); break; +case 0x28: sra(b); break; +case 0x29: sra(c); break; +case 0x2a: sra(d); break; +case 0x2b: sra(e); break; +case 0x2c: sra(h); break; +case 0x2d: sra(l); break; +case 0x2e: tstates += 7; val = fetch(addr); sra(val); store(addr, val); break; +case 0x2f: sra(a); break; +case 0x30: sll(b); break; +case 0x31: sll(c); break; +case 0x32: sll(d); break; +case 0x33: sll(e); break; +case 0x34: sll(h); break; +case 0x35: sll(l); break; +case 0x36: tstates += 7; val = fetch(addr); sll(val); store(addr, val); break; +case 0x37: sll(a); break; +case 0x38: srl(b); break; +case 0x39: srl(c); break; +case 0x3a: srl(d); break; +case 0x3b: srl(e); break; +case 0x3c: srl(h); break; +case 0x3d: srl(l); break; +case 0x3e: tstates += 7; val = fetch(addr); srl(val); store(addr, val); break; +case 0x3f: srl(a); break; +} +else { + unsigned char n = (op >> 3) & 7; + switch (op & 0xc7) { + case 0x40: bit(n, b); break; + case 0x41: bit(n, c); break; + case 0x42: bit(n, d); break; + case 0x43: bit(n, e); break; + case 0x44: bit(n, h); break; + case 0x45: bit(n, l); break; + case 0x46: tstates += 4; val = fetch(addr); bit(n, val); store(addr, val); break; + case 0x47: bit(n, a); break; + case 0x80: res(n, b); break; + case 0x81: res(n, c); break; + case 0x82: res(n, d); break; + case 0x83: res(n, e); break; + case 0x84: res(n, h); break; + case 0x85: res(n, l); break; + case 0x86: tstates += 4; val = fetch(addr); res(n, val); store(addr, val); break; + case 0x87: res(n, a); break; + case 0xc0: set(n, b); break; + case 0xc1: set(n, c); break; + case 0xc2: set(n, d); break; + case 0xc3: set(n, e); break; + case 0xc4: set(n, h); break; + case 0xc5: set(n, l); break; + case 0xc6: tstates += 4; val = fetch(addr); set(n, val); store(addr, val); break; + case 0xc7: set(n, a); break; + } +} +if (ixoriy)switch (reg) { +case 0:b = val; break; +case 1:c = val; break; +case 2:d = val; break; +case 3:e = val; break; +case 4:h = val; break; +case 5:l = val; break; +case 7:a = val; break; +} } #undef var_t diff --git a/Tools/unix/zxcc/config.h.windows b/Tools/unix/zxcc/config.h.windows index 1b838310..0f256eac 100644 --- a/Tools/unix/zxcc/config.h.windows +++ b/Tools/unix/zxcc/config.h.windows @@ -1,5 +1,10 @@ #define HAVE_WINDOWS_H #define HAVE_FCNTL_H +#ifdef _MSC_VER + #define HAVE_DIRENT_H +#endif +#define HAVE_DIRECT_H +#define HAVE_IO_H #define WINVER _WIN32_WINNT_WINXP // target Windows XP #define _WIN32_WINNT _WIN32_WINNT_WINXP // target Windows XP //#define FILETRACKER 1 diff --git a/Tools/unix/zxcc/cpmdrv.c b/Tools/unix/zxcc/cpmdrv.c index 8dcd8889..691e774f 100644 --- a/Tools/unix/zxcc/cpmdrv.c +++ b/Tools/unix/zxcc/cpmdrv.c @@ -1,186 +1,182 @@ -/* - - CPMREDIR: CP/M filesystem redirector - Copyright (C) 1998,2003 John Elliott - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - This file deals with drive-based functions. -*/ - -#include "cpmint.h" - -#ifdef _WIN32 -static char *drive_to_hostdrive(int cpm_drive) -{ - static char prefix[CPM_MAXPATH]; - char *lpfp; - dword dw; - - if (!redir_drive_prefix[cpm_drive]) return NULL; - dw = GetFullPathName(redir_drive_prefix[cpm_drive], sizeof(prefix), - prefix, &lpfp); - - if (!dw) return NULL; - if (prefix[1] == ':') /* If path starts with a drive, limit it */ - { /* to just that drive */ - prefix[2] = '\\'; /* GetDiskFreeSpace should have trailing backslash */ - prefix[3] = 0; - } - return prefix; -} -#endif - - -cpm_byte fcb_reset(void) -{ -#ifdef __MSDOS__ - bdos(0x0D, 0, 0); -#endif - - redir_l_drives = 0; - redir_cpmdrive = 0; /* A reset forces current drive to A: */ -/* redir_ro_drives = 0; Software write protect not revoked by func 0Dh. - * - * This does not follow true CP/M, but does match many 3rd-party replacements. - */ - return 0; -} - - -cpm_word fcb_drive (cpm_byte drv) -{ - if (redir_drive_prefix[drv][0]) - { - redir_cpmdrive = drv; - redir_log_drv(drv); - return 0; - } - else return 0x04FF; /* Drive doesn't exist */ -} - -cpm_byte fcb_getdrv(void) -{ - return redir_cpmdrive; -} - - -cpm_byte fcb_user (cpm_byte usr) -{ - if (usr != 0xFF) redir_cpmuser = usr % 16; - - redir_Msg("User: parameter %d returns %d\r\n", usr, redir_cpmuser); - - return redir_cpmuser; -} - - - -cpm_word fcb_logvec(void) -{ - return redir_l_drives; -} - - -cpm_word fcb_rovec(void) -{ - return redir_ro_drives; -} - - -cpm_word fcb_rodisk(void) -{ - cpm_word mask = 1; - - if (redir_cpmdrive) mask = mask << redir_cpmdrive; - - redir_ro_drives |= mask; - return 0; -} - - -cpm_word fcb_resro(cpm_word bitmap) -{ - redir_ro_drives &= ~bitmap; - - return 0; -} - - -cpm_word fcb_sync(cpm_byte flag) -{ -#ifdef _WIN32 - return 0; -#else - sync(); return 0; /* Apparently some sync()s are void not int */ -#endif -} - - -cpm_word fcb_purge() -{ -#ifdef _WIN32 - return 0; -#else - sync(); return 0; /* Apparently some sync()s are void not int */ -#endif -} - -/* Generic 8MB disk definition */ - -static cpm_byte exdpb[0x11] = { - 0x80, 0, /* 128 records/track */ - 0x04, 0x0F, /* 2k blocks */ - 0x00, /* 16k / extent */ - 0xFF, 0x0F, /* 4095 blocks */ - 0xFF, 0x03, /* 1024 dir entries */ - 0xFF, 0xFF, /* 16 directory blocks */ - 0x00, 0x80, /* Non-removable media */ - 0x00, 0x00, /* No system tracks */ - 0x02, 0x03 /* 512-byte sectors */ -}; - -cpm_word fcb_getdpb(cpm_byte *dpb) -{ - /* Return the example dpb */ - memcpy(dpb, &exdpb, 0x11); - return 0x11; -} - - -/* Create an entirely bogus ALV - * TODO: Make it a bit better */ - -cpm_word fcb_getalv(cpm_byte *alv, cpm_word max) -{ - if (max > 1024) max = 1024; - - memset(alv, 0xFF, max / 2); - memset(alv + (max / 2), 0, max / 2); - - return max; -} - -/* Get disk free space */ - -cpm_word fcb_dfree (cpm_byte drive, cpm_byte *dma) -{ - /* Return half of disk capacity */ - redir_wr24(dma, 0x8000L); /* 8MB / 128 / 2 */ - return 0; -} - - - +/* + + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998,2003 John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file deals with drive-based functions. +*/ + +#include "cpmint.h" + +#ifdef _WIN32 +static char* drive_to_hostdrive(int cpm_drive) +{ + static char prefix[CPM_MAXPATH]; + char* lpfp; + dword dw; + + if (!redir_drive_prefix[cpm_drive]) return NULL; + dw = GetFullPathName(redir_drive_prefix[cpm_drive], sizeof(prefix), + prefix, &lpfp); + + if (!dw) return NULL; + if (prefix[1] == ':') /* If path starts with a drive, limit it */ + { /* to just that drive */ + prefix[2] = '\\'; /* GetDiskFreeSpace should have trailing backslash */ + prefix[3] = 0; + } + return prefix; +} +#endif + + +cpm_byte fcb_reset(void) +{ +#ifdef __MSDOS__ + bdos(0x0D, 0, 0); +#endif + + redir_l_drives = 0; + redir_cpmdrive = 0; /* A reset forces current drive to A: */ +/* redir_ro_drives = 0; Software write protect not revoked by func 0Dh. + * + * This does not follow true CP/M, but does match many 3rd-party replacements. + */ + return 0; +} + + +cpm_word fcb_drive(cpm_byte drv) +{ + if (redir_drive_prefix[drv][0]) + { + redir_cpmdrive = drv; + redir_log_drv(drv); + return 0; + } + else return 0x04FF; /* Drive doesn't exist */ +} + +cpm_byte fcb_getdrv(void) +{ + return redir_cpmdrive; +} + + +cpm_byte fcb_user(cpm_byte usr) +{ + if (usr != 0xFF) redir_cpmuser = usr % 16; + + DBGMSGV("User: parameter %d returns %d\n", usr, redir_cpmuser); + + return redir_cpmuser; +} + + + +cpm_word fcb_logvec(void) +{ + return redir_l_drives; +} + + +cpm_word fcb_rovec(void) +{ + return redir_ro_drives; +} + + +cpm_word fcb_rodisk(void) +{ + cpm_word mask = 1; + + if (redir_cpmdrive) mask = mask << redir_cpmdrive; + + redir_ro_drives |= mask; + return 0; +} + + +cpm_word fcb_resro(cpm_word bitmap) +{ + redir_ro_drives &= ~bitmap; + + return 0; +} + + +cpm_word fcb_sync(cpm_byte flag) +{ +#ifdef _WIN32 + return 0; +#else + sync(); return 0; /* Apparently some sync()s are void not int */ +#endif +} + + +cpm_word fcb_purge() +{ +#ifdef _WIN32 + return 0; +#else + sync(); return 0; /* Apparently some sync()s are void not int */ +#endif +} + +/* Generic 8MB disk definition */ + +static cpm_byte exdpb[0x11] = { + 0x80, 0, /* 128 records/track */ + 0x04, 0x0F, /* 2k blocks */ + 0x00, /* 16k / extent */ + 0xFF, 0x0F, /* 4095 blocks */ + 0xFF, 0x03, /* 1024 dir entries */ + 0xFF, 0xFF, /* 16 directory blocks */ + 0x00, 0x80, /* Non-removable media */ + 0x00, 0x00, /* No system tracks */ + 0x02, 0x03 /* 512-byte sectors */ +}; + +cpm_word fcb_getdpb(cpm_byte* dpb) +{ + /* Return the example dpb */ + memcpy(dpb, &exdpb, 0x11); + return 0x11; +} + +/* Create an entirely bogus ALV + * TODO: Make it a bit better */ + +cpm_word fcb_getalv(cpm_byte* alv, cpm_word max) +{ + if (max > 1024) max = 1024; + + memset(alv, 0xFF, max / 2); + memset(alv + (max / 2), 0, max / 2); + + return max; +} + +/* Get disk free space */ + +cpm_word fcb_dfree(cpm_byte drive, cpm_byte* dma) +{ + /* Return half of disk capacity */ + redir_wr24(dma, 0x8000L); /* 8MB / 128 / 2 */ + return 0; +} diff --git a/Tools/unix/zxcc/cpmglob.c b/Tools/unix/zxcc/cpmglob.c index dadae255..3705b05e 100644 --- a/Tools/unix/zxcc/cpmglob.c +++ b/Tools/unix/zxcc/cpmglob.c @@ -1,590 +1,598 @@ -/* - - CPMREDIR: CP/M filesystem redirector - Copyright (C) 1998, John Elliott - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - This file implements those BDOS functions that use wildcard expansion. -*/ - -#include "cpmint.h" -#ifdef _MSC_VER -#define S_ISDIR(mode) (((mode) & _S_IFDIR) != 0) -#endif - - -static cpm_byte *find_fcb; -static int find_n; -static int find_ext = 0; -static int find_xfcb = 0; -static int entryno; -static cpm_byte lastdma[0x80]; -static long lastsize; -static char target_name[CPM_MAXPATH]; - -static char upper(char c) -{ - if (islower(c)) return toupper(c); - return c; -} - -/* - * we need to handle case sensitive filesystems correctly. - * underlying files need to just work, irrespective if the files - * in the native filesystem are mixed, upper or lower. - * the naive code in the distributed zx will not work everywhere. - */ - -/* Does the string "s" match the CP/M FCB? */ -/* pattern[0-10] will become a CP/M name parsed from "s" if it matches. */ -/* If 1st byte of FCB is '?' then anything matches. */ - -static int cpm_match(char *s, cpm_byte *fcb, cpm_byte *pattern) -{ - int n; - size_t m; - char *dotpos; - - m = strlen(s); - - /* - * let's cook the filename into something that we can match against - * the fcb. we reject any that can't be valid cp/m filenames, - * normalizing case as we go. all this goes into 'pattern' - */ - for (n = 0; n < 11; n++) pattern[n] = ' '; - - /* The name must have 1 or 0 dots */ - dotpos = strchr(s, '.'); - if (!dotpos) { /* No dot. The name must be at most 8 characters */ - if (m > 8) return 0; - for (n = 0; n < m; n++) { - pattern[n] = upper(s[n]) & 0x7F; - } - } else { /* at least one dot */ - if (strchr(dotpos + 1, '.')) { /* More than 1 dot */ - return 0; - } - if (dotpos == s) { /* Dot right at the beginning */ - return 0; - } - if ((dotpos - s) > 8) { /* "name" > 8 characters */ - return 0; - } - if (strlen(dotpos + 1) > 3) { /* "type" > 3 characters */ - return 0; - } - for (n = 0; n < (dotpos - s); n++) { /* copy filename portion */ - pattern[n] = upper(s[n]) & 0x7F; - } - m = strlen(dotpos + 1); - for (n = 0; n < m; n++) { /* copy extention portion */ - pattern[n + 8] = upper(dotpos[n + 1]) & 0x7F; - } - } - - /* - * handle special case where fcb[0] == '?' or fcb[0] & 0x80 - * this is used to return a full directory list on bdos's - */ - if (((fcb[0] & 0x7F) == '?') || (fcb[0] & 0x80)) { - return 1; - } - for (n = 0; n < 11; n++) - { - if (fcb[n+1] == '?') continue; - if ((pattern[n] & 0x7F) != (fcb[n+1] & 0x7F)) - { - return 0; - } - } - return 1; /* Success! */ -} - - -/* Get the next entry from the host's directory matching "fcb" */ - -static struct dirent * next_entry(DIR *dir, cpm_byte *fcb, cpm_byte *pattern, - struct stat *st) -{ - struct dirent *en; - int unsatisfied; - int drv = fcb[0] & 0x7F; - - if (drv == '?') drv = 0; - if (!drv) drv = redir_cpmdrive; - else drv--; - - for (unsatisfied = 1; unsatisfied; ) - { - /* 1. Get the next entry */ - - en = readdir(dir); - if (!en) return NULL; /* No next entry */ - ++entryno; /* 0 for 1st, 1 for 2nd, etc. */ - - /* 2. See if it matches. We do this first (in preference to - seeing if it's a subdirectory first) because it doesn't - require disc access */ - if (!cpm_match(en->d_name, fcb, pattern)) - { - continue; - } - /* 3. Stat it, & reject it if it's a directory */ - - strcpy(target_name, redir_drive_prefix[drv]); - strcat(target_name, en->d_name); - - if (stat(target_name, st)) - { - redir_Msg("Can't stat %s so omitting it.\n", target_name); - continue; /* Can't stat */ - } - if (S_ISDIR(st->st_mode)) - { - /* Searching for files only */ - if (fcb[0] != '?' && fcb[0] < 0x80) - { - continue; - } - } - unsatisfied = 0; - } - return en; -} - - - -void volume_label(int drv, cpm_byte *dma) -{ - struct stat st; - - memset(dma, 0x20, 12); /* Volume label */ - - /* Get label name */ - redir_get_label(drv, (char *)(dma + 1)); - /* [0x0c] = label byte - * [0x0d] = password byte (=0) - * [0x10-0x17] = password - * [0x18] = label create date - * [0x1c] = label update date - */ -#ifdef __MSDOS__ - dma[0x0c] = 0x21; /* Under DOS, only "update" */ - if (redir_drdos) dma[0x0c] |= 0x80; /* Under DRDOS, passwords allowed */ -#else - dma[0x0c] = 0x61; /* Label exists and time stamps allowed */ -#endif /* (update & access) */ - dma[0x0d] = 0; /* Label not passworded */ - dma[0x0f] = 0x80; /* Non-CP/M media */ - - if (stat(redir_drive_prefix[drv], &st)) - { - redir_Msg("stat() fails on '%s'\n", redir_drive_prefix[drv]); - return; - } - - redir_wr32(dma + 0x18, redir_cpmtime(st.st_atime)); - redir_wr32(dma + 0x1C, redir_cpmtime(st.st_mtime)); -} - - - -cpm_word redir_find(int n, cpm_byte *fcb, cpm_byte *dma) -{ - DIR *hostdir; - int drv, attrib; - long recs; - struct stat st; - struct dirent *de; - cpm_word rights; - - drv = (fcb[0] & 0x7F); - if (!drv || drv == '?') drv = redir_cpmdrive; - else drv--; - - if (find_xfcb) /* Return another extent */ - { - memcpy(dma, lastdma, 0x80); - dma[0] |= 0x10; /* XFCB */ - dma[0x0c] = dma[0x69]; /* Password mode */ - dma[0x0d] = 0x0A; /* Password decode byte */ - memset(dma + 0x10, '*', 7); - dma[0x17] = ' '; /* Encoded password */ - memcpy(lastdma, dma, 0x80); - - find_xfcb = 0; - return 0; - } - - if (find_ext) /* Return another extent */ - { - memcpy(dma, lastdma, 0x80); - dma[0x0c]++; - if (dma[0x0c] == 0x20) - { - dma[0x0c] = 0; - dma[0x0e]++; - } - lastsize -= 0x4000; - recs = (lastsize + 127) / 128; - dma[0x0f] = (recs > 127) ? 0x80 : (recs & 0x7F); - - if (lastsize <= 0x4000) find_ext = 0; - memcpy(lastdma, dma, 0x80); - - return 0; - } - - - memset(dma, 0, 128); /* Zap the buffer */ - - /* - If returning all entries, return a volume label. - */ - if ((fcb[0] & 0x7F) == '?') - { - if (!n) - { - volume_label(drv, dma); - return 0; - } - else --n; - } - - /* Note: This implies that opendir() works on a filename with a - trailing slash. It does under Linux, but that's the only assurance - I can give. - */ - - entryno = -1; - hostdir = opendir(redir_drive_prefix[drv]); - - if (!hostdir) - { - redir_Msg("opendir() fails on '%s'\n", redir_drive_prefix[drv]); - return 0xFF; - } - /* We have a handle to the directory. */ - while (n >= 0) - { - de = next_entry(hostdir, fcb, dma + 1, &st); - if (!de) - { - closedir(hostdir); - return 0xFF; - } - --n; - } - /* Valid entry found & statted. dma+1 holds filename. */ - - dma[0] = redir_cpmuser; /* Uid always matches */ - dma[0x0c] = 0; /* Extent counter, low */ - dma[0x0d] = st.st_size & 0x7F; /* Last record byte count */ - dma[0x0e] = 0; /* Extent counter, high */ - -#ifdef _WIN32 - attrib = GetFileAttributesA(target_name); - rights = redir_drdos_get_rights(target_name); - if (rights && ((fcb[0] & 0x7F) == '?')) find_xfcb = 1; -#else - attrib = 0; - rights = 0; -#endif - if (attrib & 1) dma[9] |= 0x80; /* read only */ - if (attrib & 4) dma[10] |= 0x80; /* system */ - if (!(attrib & 0x20)) dma[11] |= 0x80; /* archive */ - - - -/* TODO: Under Unix, work out correct RO setting */ - - recs = (st.st_size + 127) / 128; - dma[0x0f] = (recs > 127) ? 0x80 : (recs & 0x7F); - dma[0x10] = 0x80; - if (S_ISDIR(st.st_mode)) dma[0x10] |= 0x40; - if (attrib & 2) dma[0x10] |= 0x20; - dma[0x10] |= ((entryno & 0x1FFF) >> 8); - dma[0x11] = dma[0x10]; - dma[0x12] = entryno & 0xFF; - - redir_wr32(dma + 0x16, (dword)st.st_mtime); /* Modification time. */ - /* TODO: It should be in DOS */ - /* format */ - /* TODO: At 0x1A, 1st cluster */ - redir_wr32(dma + 0x1C, st.st_size); /* True size */ - - if (rights) /* Store password mode. Don't return an XFCB. */ - { - dma[0x69] = redir_cpm_pwmode(rights); - memcpy(lastdma, dma, 0x80); - } - - dma[0x60] = 0x21; /* XFCB */ - redir_wr32(dma + 0x61, redir_cpmtime(st.st_atime)); - redir_wr32(dma + 0x65, redir_cpmtime(st.st_mtime)); - - closedir(hostdir); - - if (st.st_size > 0x4000 && (fcb[0x0C] == '?')) /* All extents? */ - { - lastsize = st.st_size; - find_ext = 1; - memcpy(lastdma, dma, 0x80); - } - return 0; -} - - -#ifdef DEBUG -#define SHOWNAME(func) \ - { \ - char fname[CPM_MAXPATH]; \ - redir_fcb2unix(fcb, fname); \ - redir_Msg(func "(\"%s\")\n", fname); \ - } - -#else - #define SHOWNAME(func) -#endif - -cpm_word fcb_find1 (cpm_byte *fcb, cpm_byte *dma) /* 0x11 */ -{ -#ifdef DEBUG - int rv; -#endif - SHOWNAME("fcb_find1") - - redir_log_fcb(fcb); - - find_n = 0; - find_fcb = fcb; - find_ext = 0; - find_xfcb = 0; -#ifdef DEBUG - rv = redir_find(find_n, fcb, dma); - - if (rv < 4) - { - redir_Msg("Ret: %-11.11s\n", dma + 1); - } - else redir_Msg("Ret: Fail\n"); - return rv; -#else - return redir_find(find_n, find_fcb, dma); -#endif -} - -/* We don't bother with the FCB parameter - it's undocmented, and - * programs that do know about it will just pass in the same parameter - * that they did to function 0x11 */ - -cpm_word fcb_find2 (cpm_byte *fcb, cpm_byte *dma) /* 0x12 */ -{ -#ifdef DEBUG - int rv; - - char fname[CPM_MAXPATH]; - redir_fcb2unix(find_fcb, fname); - redir_Msg("fcb_find2(\"%s\") no. %d\n", fname, find_n); -#endif - ++find_n; -#ifdef DEBUG - rv = redir_find(find_n, find_fcb, dma); - - if (rv < 4) - { - redir_Msg("Ret: %-11.11s\n", dma + 1); - } - else redir_Msg("Ret: Fail\n"); - return rv; -#else - return redir_find(find_n, find_fcb, dma); -#endif -} - -/* Under CP/M, unlinking works with wildcards */ - -cpm_word fcb_unlink(cpm_byte *fcb, cpm_byte *dma) -{ - DIR *hostdir; - int drv; - struct dirent *de; - struct stat st; - int handle = 0; - int unpasswd = 0; - char fname[CPM_MAXPATH]; - - SHOWNAME("fcb_unlink") - - if (fcb[5] & 0x80) unpasswd = 1; /* Remove password rather than file */ - - redir_log_fcb(fcb); - - drv = (fcb[0] & 0x7F); - if (!drv || drv == '?') drv = redir_cpmdrive; - else drv--; - - if (redir_ro_drv(drv)) return 0x02FF; /* Error: R/O drive */ - -#ifdef DEBUG - redir_fcb2unix(fcb, fname); - redir_Msg("fcb_unlink(\"%s\")\n", fname); -#endif - - /* Note: This implies that opendir() works on a filename with a - trailing slash. It does under Linux, but that's the only assurance - I can give. - */ - - hostdir = opendir(redir_drive_prefix[drv]); - - if (!hostdir) - { - redir_Msg("opendir() fails on '%s'\n", redir_drive_prefix[drv]); - return 0xFF; - } - /* We have a handle to the directory. */ - do - { - de = next_entry(hostdir, fcb, (cpm_byte *)fname, &st); - if (de) - { - strcpy(target_name, redir_drive_prefix[drv]); - strcat(target_name, de->d_name); - redir_Msg("Deleting %s\n", de->d_name); - if (unpasswd) - { -#ifdef __MSDOS__ - if (redir_drdos) - { - handle = redir_drdos_put_rights (target_name, dma, 0); - } - else handle = 0; -#endif - } - else if (fcb[0] & 0x80) - { - handle = rmdir(target_name); - if (handle && redir_password_error()) - { - redir_password_append(target_name, dma); - handle = rmdir(target_name); - } - } - else - { - releaseFile(target_name); - handle = unlink(target_name); - if (handle && redir_password_error()) - { - redir_password_append(target_name, dma); - releaseFile(target_name); - handle = unlink(target_name); - } - } - - if (handle) de = NULL; /* Delete failed */ - } - } - while (de != NULL); - if (handle) - { - redir_Msg("Ret: -1\n"); - closedir(hostdir); - return 0xFF; - } - redir_Msg("Ret: 0\n"); - closedir(hostdir); - return 0; -} - - - - - -#ifdef __MSDOS__ -cpm_word redir_get_label(cpm_byte drv, char *pattern) -{ - char strs[10]; - struct ffblk fblk; - int done; - char *s; - int n; - - /* We need the drive prefix to be of the form "C:\etc..." */ - - memset(pattern, ' ', 11); - if (!redir_drive_prefix[drv][0] || redir_drive_prefix[drv][1] != ':') - return 0; - - sprintf(strs,"%c:/*.*", redir_drive_prefix[drv][0]); - - done = findfirst(strs, &fblk, FA_LABEL); - while (!done) - { - if ((fblk.ff_attrib & FA_LABEL) && - !(fblk.ff_attrib & (FA_SYSTEM | FA_HIDDEN))) - { - s = strchr(fblk.ff_name, '/'); - if (!s) s = strchr(fblk.ff_name, '\\'); - if (!s) s = strchr(fblk.ff_name, ':'); - if (!s) s = fblk.ff_name; - for (n = 0; n < 11; n++) - { - if (!(*s)) break; - if (*s == '.') - { - n = 7; - ++s; - continue; - } - pattern[n] = upper(*s); - ++s; - } - return 1; - } - done = findnext(&fblk); - } - return 0; -} -#else -cpm_word redir_get_label(cpm_byte drv, char *pattern) -{ - char *dname; - size_t l; - int n; - - memset(pattern, ' ', 11); - - dname = strrchr(redir_drive_prefix[drv], '/'); - if (dname) - { - ++dname; - l = strlen(dname); - if (l > 11) l = 11; - for (n = 0; n < l; n++) pattern[n] = upper(dname[l]); - } - else - { - pattern[0] = '.'; - } - return 0; -} - - - -#endif +/* + + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file implements those BDOS functions that use wildcard expansion. +*/ + +#include "cpmint.h" +#ifdef _MSC_VER + #define S_ISDIR(mode) (((mode) & _S_IFDIR) != 0) +#endif + +static cpm_byte* find_fcb; +static int find_n; +static int find_ext = 0; +static int find_xfcb = 0; +static int entryno; +static cpm_byte lastdma[0x80]; +static long lastsize; +static char target_name[CPM_MAXPATH]; + +static char upper(char c) +{ + if (islower(c)) return toupper(c); + return c; +} + +/* + * we need to handle case sensitive filesystems correctly. + * underlying files need to just work, irrespective if the files + * in the native filesystem are mixed, upper or lower. + * the naive code in the distributed zx will not work everywhere. + */ + + /* + * Does the string "s" match the CP/M FCB? + * pattern[0-10] will become a CP/M name parsed from "s" if it matches. + * If 1st byte of FCB is '?' then anything matches. + */ + +static int cpm_match(char* s, cpm_byte* fcb, cpm_byte* pattern) +{ + int n; + size_t m; + char* dotpos; + + m = strlen(s); + + /* + * let's cook the filename into something that we can match against + * the fcb. we reject any that can't be valid cp/m filenames, + * normalizing case as we go. all this goes into 'pattern' + */ + + for (n = 0; n < 11; n++) pattern[n] = ' '; + + /* The name must have 1 or 0 dots */ + dotpos = strchr(s, '.'); + if (!dotpos) { /* No dot. The name must be at most 8 characters */ + if (m > 8) return 0; + for (n = 0; n < m; n++) { + pattern[n] = upper(s[n]) & 0x7F; + } + } + else { /* at least one dot */ + if (strchr(dotpos + 1, '.')) { /* More than 1 dot */ + return 0; + } + if (dotpos == s) { /* Dot right at the beginning */ + return 0; + } + if ((dotpos - s) > 8) { /* "name" > 8 characters */ + return 0; + } + if (strlen(dotpos + 1) > 3) { /* "type" > 3 characters */ + return 0; + } + for (n = 0; n < (dotpos - s); n++) { /* copy filename portion */ + pattern[n] = upper(s[n]) & 0x7F; + } + m = strlen(dotpos + 1); + for (n = 0; n < m; n++) { /* copy extention portion */ + pattern[n + 8] = upper(dotpos[n + 1]) & 0x7F; + } + } + + /* + * handle special case where fcb[0] == '?' or fcb[0] & 0x80 + * this is used to return a full directory list on bdos's + */ + + if (((fcb[0] & 0x7F) == '?') || (fcb[0] & 0x80)) { + return 1; + } + for (n = 0; n < 11; n++) + { + if (fcb[n + 1] == '?') continue; + if ((pattern[n] & 0x7F) != (fcb[n + 1] & 0x7F)) + { + return 0; + } + } + return 1; /* Success! */ +} + +/* Get the next entry from the host's directory matching "fcb" */ + +static struct dirent* next_entry(DIR* dir, cpm_byte* fcb, cpm_byte* pattern, + struct stat* st) +{ + struct dirent* en; + int unsatisfied; + int drv = fcb[0] & 0x7F; + + if (drv == '?') drv = 0; + if (!drv) drv = redir_cpmdrive; + else drv--; + + for (unsatisfied = 1; unsatisfied; ) + { + /* 1. Get the next entry */ + en = readdir(dir); + if (!en) return NULL; /* No next entry */ + ++entryno; /* 0 for 1st, 1 for 2nd, etc. */ + + /* 2. See if it matches. We do this first (in preference to + * seeing if it's a subdirectory first) because it doesn't + * require disc access */ + if (!cpm_match(en->d_name, fcb, pattern)) + { + continue; + } + + /* 3. Stat it, & reject it if it's a directory */ + strcpy(target_name, redir_drive_prefix[drv]); + strcat(target_name, en->d_name); + + if (stat(target_name, st)) + { + DBGMSGV("Can't stat %s so omitting it.\n", target_name); + continue; /* Can't stat */ + } + if (S_ISDIR(st->st_mode)) + { + /* Searching for files only */ + if (fcb[0] != '?' && fcb[0] < 0x80) + { + continue; + } + } + unsatisfied = 0; + } + return en; +} + +void volume_label(int drv, cpm_byte* dma) +{ + struct stat st; + + memset(dma, 0x20, 12); /* Volume label */ + + /* Get label name */ + redir_get_label(drv, (char*)(dma + 1)); + + /* [0x0c] = label byte + * [0x0d] = password byte (=0) + * [0x10-0x17] = password + * [0x18] = label create date + * [0x1c] = label update date + */ +#ifdef __MSDOS__ + dma[0x0c] = 0x21; /* Under DOS, only "update" */ + if (redir_drdos) dma[0x0c] |= 0x80; /* Under DRDOS, passwords allowed */ +#else + dma[0x0c] = 0x61; /* Label exists and time stamps allowed */ +#endif /* (update & access) */ + dma[0x0d] = 0; /* Label not passworded */ + dma[0x0f] = 0x80; /* Non-CP/M media */ + + if (stat(redir_drive_prefix[drv], &st)) + { + DBGMSGV("stat() fails on '%s'\n", redir_drive_prefix[drv]); + return; + } + + redir_wr32(dma + 0x18, redir_cpmtime(st.st_atime)); + redir_wr32(dma + 0x1C, redir_cpmtime(st.st_mtime)); +} + +cpm_word redir_find(int n, cpm_byte* fcb, cpm_byte* dma) +{ + DIR* hostdir; + int drv, attrib; + long recs; + struct stat st; + struct dirent* de; + cpm_word rights; + + drv = (fcb[0] & 0x7F); + if (!drv || drv == '?') drv = redir_cpmdrive; + else drv--; + + if (find_xfcb) /* Return another extent */ + { + memcpy(dma, lastdma, 0x80); + dma[0] |= 0x10; /* XFCB */ + dma[0x0c] = dma[0x69]; /* Password mode */ + dma[0x0d] = 0x0A; /* Password decode byte */ + memset(dma + 0x10, '*', 7); + dma[0x17] = ' '; /* Encoded password */ + memcpy(lastdma, dma, 0x80); + + find_xfcb = 0; + return 0; + } + + if (find_ext) /* Return another extent */ + { + memcpy(dma, lastdma, 0x80); + dma[0x0c]++; + if (dma[0x0c] == 0x20) + { + dma[0x0c] = 0; + dma[0x0e]++; + } + lastsize -= 0x4000; + recs = (lastsize + 127) / 128; + dma[0x0f] = (recs > 127) ? 0x80 : (recs & 0x7F); + + if (lastsize <= 0x4000) find_ext = 0; + memcpy(lastdma, dma, 0x80); + + return 0; + } + + memset(dma, 0, 128); /* Zap the buffer */ + + /* If returning all entries, return a volume label. */ + + if ((fcb[0] & 0x7F) == '?') + { + if (!n) + { + volume_label(drv, dma); + return 0; + } + else --n; + } + + /* Note: This implies that opendir() works on a filename with a + * trailing slash. It does under Linux, but that's the only assurance + * I can give. */ + + entryno = -1; + hostdir = opendir(redir_drive_prefix[drv]); + + if (!hostdir) + { + DBGMSGV("opendir() fails on '%s'\n", redir_drive_prefix[drv]); + return 0xFF; + } + + /* We have a handle to the directory. */ + while (n >= 0) + { + de = next_entry(hostdir, fcb, dma + 1, &st); + if (!de) + { + closedir(hostdir); + return 0xFF; + } + --n; + } + /* Valid entry found & statted. dma+1 holds filename. */ + + dma[0] = redir_cpmuser; /* Uid always matches */ + dma[0x0c] = 0; /* Extent counter, low */ + dma[0x0d] = st.st_size & 0x7F; /* Last record byte count */ + dma[0x0e] = 0; /* Extent counter, high */ + +#ifdef _WIN32 + attrib = GetFileAttributesA(target_name); + rights = redir_drdos_get_rights(target_name); + if (rights && ((fcb[0] & 0x7F) == '?')) find_xfcb = 1; +#else + attrib = 0; + rights = 0; +#endif + if (attrib & 1) dma[9] |= 0x80; /* read only */ + if (attrib & 4) dma[10] |= 0x80; /* system */ + if (!(attrib & 0x20)) dma[11] |= 0x80; /* archive */ + +/* TODO: Under Unix, work out correct RO setting */ + + recs = (st.st_size + 127) / 128; + dma[0x0f] = (recs > 127) ? 0x80 : (recs & 0x7F); + dma[0x10] = 0x80; + if (S_ISDIR(st.st_mode)) dma[0x10] |= 0x40; + if (attrib & 2) dma[0x10] |= 0x20; + dma[0x10] |= ((entryno & 0x1FFF) >> 8); + dma[0x11] = dma[0x10]; + dma[0x12] = entryno & 0xFF; + + redir_wr32(dma + 0x16, (dword)st.st_mtime); /* Modification time. */ + /* TODO: It should be in DOS format */ + /* TODO: At 0x1A, 1st cluster */ + redir_wr32(dma + 0x1C, st.st_size); /* True size */ + + if (rights) /* Store password mode. Don't return an XFCB. */ + { + dma[0x69] = redir_cpm_pwmode(rights); + memcpy(lastdma, dma, 0x80); + } + + dma[0x60] = 0x21; /* XFCB */ + redir_wr32(dma + 0x61, redir_cpmtime(st.st_atime)); + redir_wr32(dma + 0x65, redir_cpmtime(st.st_mtime)); + + closedir(hostdir); + + if (st.st_size > 0x4000 && (fcb[0x0C] == '?')) /* All extents? */ + { + lastsize = st.st_size; + find_ext = 1; + memcpy(lastdma, dma, 0x80); + } + return 0; +} + +cpm_word fcb_find1(cpm_byte* fcb, cpm_byte* dma) /* 0x11 */ +{ +#ifdef DEBUG + int rv; +#endif + + FCBENT(fcb); + + redir_log_fcb(fcb); + + find_n = 0; + find_fcb = fcb; + find_ext = 0; + find_xfcb = 0; + +#ifdef DEBUG + rv = redir_find(find_n, fcb, dma); + + if (rv < 4) + { + DBGMSGV("Ret: %-11.11s\n", dma + 1); + } + else DBGMSG("Ret: Fail\n"); + FCBRET(rv); +#else + FCBRET(redir_find(find_n, find_fcb, dma)); +#endif +} + +/* We don't bother with the FCB parameter - it's undocmented, and + * programs that do know about it will just pass in the same parameter + * that they did to function 0x11 */ + +cpm_word fcb_find2(cpm_byte* fcb, cpm_byte* dma) /* 0x12 */ +{ +#ifdef DEBUG + int rv; + char fname[CPM_MAXPATH]; +#endif + + FCBENT(find_fcb); + +#ifdef DEBUG + redir_fcb2unix(find_fcb, fname); + DBGMSGV("file number %d, '%s'\n", find_n, fname); +#endif + + ++find_n; + +#ifdef DEBUG + rv = redir_find(find_n, find_fcb, dma); + + if (rv < 4) + { + DBGMSGV("Ret: %-11.11s\n", dma + 1); + } + else DBGMSG("Ret: Fail\n"); + FCBRET(rv); +#else + FCBRET(redir_find(find_n, find_fcb, dma)); +#endif +} + +/* Under CP/M, unlinking works with wildcards */ + +cpm_word fcb_unlink(cpm_byte* fcb, cpm_byte* dma) +{ + DIR* hostdir; + int drv; + struct dirent* de; + struct stat st; + int handle = 0; + int unpasswd = 0; + char fname[CPM_MAXPATH]; + int del_cnt = 0; + + FCBENT(fcb); + + if (fcb[5] & 0x80) unpasswd = 1; /* Remove password rather than file */ + + redir_log_fcb(fcb); + + drv = (fcb[0] & 0x7F); + if (!drv || drv == '?') drv = redir_cpmdrive; + else drv--; + + if (redir_ro_drv(drv)) + { + /* Error: R/O drive */ + DBGMSG("delete failed - R/O drive\n"); + FCBRET(0x02FF); + } + +#ifdef DEBUG + redir_fcb2unix(fcb, fname); + DBGMSGV("fcb_unlink('%s')\n", fname); +#endif + + /* Note: This implies that opendir() works on a filename with a + * trailing slash. It does under Linux, but that's the only assurance + * I can give.*/ + + hostdir = opendir(redir_drive_prefix[drv]); + + if (!hostdir) + { + DBGMSGV("opendir failed on '%s'\n", redir_drive_prefix[drv]); + FCBRET(0xFF); + } + + /* We have a handle to the directory. */ + do + { + de = next_entry(hostdir, fcb, (cpm_byte*)fname, &st); + if (de) + { + strcpy(target_name, redir_drive_prefix[drv]); + strcat(target_name, de->d_name); + DBGMSGV("deleting '%s'\n", de->d_name); + if (unpasswd) + { +#ifdef __MSDOS__ + if (redir_drdos) + { + handle = redir_drdos_put_rights(target_name, dma, 0); + } + else handle = 0; +#endif + } + else if (fcb[0] & 0x80) + { + DBGMSGV("rmdir '%s'\n", target_name); + handle = rmdir(target_name); + if (handle && redir_password_error()) + { + DBGMSGV("rmdir failed (errno=%lu): %s\n", errno, strerror(errno)); + redir_password_append(target_name, dma); + DBGMSGV("rmdir '%s'\n", target_name); + handle = rmdir(target_name); + } + if (handle) + DBGMSGV("rmdir failed (errno=%lu): %s\n", errno, strerror(errno)); + } + else + { + releaseFile(target_name); + DBGMSGV("unlink '%s'\n", target_name); + handle = unlink(target_name); + if (handle && redir_password_error()) + { + DBGMSGV("unlink failed (errno=%lu): %s\n", errno, strerror(errno)); + redir_password_append(target_name, dma); + releaseFile(target_name); + DBGMSGV("unlink '%s'\n", target_name); + handle = unlink(target_name); + } + if (handle) + DBGMSGV("unlink failed (errno=%lu): %s\n", errno, strerror(errno)); + } + + if (handle) + de = NULL; /* Delete failed */ + else + del_cnt++; + } + } while (de != NULL); + + if (!handle && !del_cnt) + DBGMSG("no matching directory entries\n"); + else + DBGMSGV("deleted %i file(s)\n", del_cnt); + + if (handle || !del_cnt) + { + DBGMSG("delete processing failed\n"); + closedir(hostdir); + FCBRET(0xFF); + } + + DBGMSG("delete processing succeeded\n"); + closedir(hostdir); + FCBRET(0); +} + +#ifdef __MSDOS__ +cpm_word redir_get_label(cpm_byte drv, char* pattern) +{ + char strs[10]; + struct ffblk fblk; + int done; + char* s; + int n; + + /* We need the drive prefix to be of the form "C:\etc..." */ + + memset(pattern, ' ', 11); + if (!redir_drive_prefix[drv][0] || redir_drive_prefix[drv][1] != ':') + return 0; + + sprintf(strs, "%c:/*.*", redir_drive_prefix[drv][0]); + + done = findfirst(strs, &fblk, FA_LABEL); + while (!done) + { + if ((fblk.ff_attrib & FA_LABEL) && + !(fblk.ff_attrib & (FA_SYSTEM | FA_HIDDEN))) + { + s = strchr(fblk.ff_name, '/'); + if (!s) s = strchr(fblk.ff_name, '\\'); + if (!s) s = strchr(fblk.ff_name, ':'); + if (!s) s = fblk.ff_name; + for (n = 0; n < 11; n++) + { + if (!(*s)) break; + if (*s == '.') + { + n = 7; + ++s; + continue; + } + pattern[n] = upper(*s); + ++s; + } + return 1; + } + done = findnext(&fblk); + } + return 0; +} +#else +cpm_word redir_get_label(cpm_byte drv, char* pattern) +{ + char* dname; + size_t l; + int n; + + memset(pattern, ' ', 11); + + dname = strrchr(redir_drive_prefix[drv], '/'); + if (dname) + { + ++dname; + l = strlen(dname); + if (l > 11) l = 11; + for (n = 0; n < l; n++) pattern[n] = upper(dname[l]); + } + else + { + pattern[0] = '.'; + } + return 0; +} + +#endif diff --git a/Tools/unix/zxcc/cpmint.h b/Tools/unix/zxcc/cpmint.h index 65879654..9520e05e 100644 --- a/Tools/unix/zxcc/cpmint.h +++ b/Tools/unix/zxcc/cpmint.h @@ -1,249 +1,274 @@ -/* - - CPMREDIR: CP/M filesystem redirector - Copyright (C) 1998, John Elliott - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - This file holds internal declarations for the library. -*/ - -#ifndef _WIN32 - #include "config.h" - #define DIRSEP "/" -#else - #include "config.h" - #define DIRSEP "/\\:" -#endif -#include -#ifdef HAVE_STDLIB_H - #include -#endif -#include -#ifdef HAVE_STRING_H - #include -#endif -#include -#include -#ifdef _WIN32 - #include -#endif -#ifdef HAVE_SYS_TYPES_H - #include -#endif -#include -#include -#ifdef HAVE_DIRENT_H - #include - #ifdef HAVE_DIRECT_H - #include - #endif -#else - #ifdef __WATCOMC__ - #include - #include - #else - #include "dirent.h" - #endif -#endif -#ifdef HAVE_NDIR_H - #include -#endif -#ifdef HAVE_SYS_DIR_H - #include -#endif -#ifdef HAVE_SYS_NDIR_H - #include -#endif -#ifdef HAVE_WINDOWS_H - #include -#endif -#ifdef HAVE_WINNT_H - #include -#endif -#ifdef HAVE_SYS_VFS_H - #include -#endif -#ifdef HAVE_UTIME_H - #include -#endif -#ifdef HAVE_FCNTL_H - #include -#endif -#ifdef HAVE_UNISTD_H - #include -#endif -#ifndef _WIN32 - #include - #include - #define _S_IFDIR S_IFDIR -#endif - -/* MSDOS includes removed */ - -#ifdef _WIN32 - #define mkdir(dir, mode) _mkdir(dir) - #define strcasecmp _stricmp - int truncate(const char* path, off_t length); /* see util.c */ - #define ftruncate _chsize - /* note Windows build assumes Windows is configured as a non case sensitive filesystem */ -#else - #define CASE_SENSITIVE_FILESYSTEM 1 -#endif - -#include "cpmredir.h" - -typedef unsigned long dword; /* Must be at least 32 bits, and - >= sizeof(int) */ - -#ifdef CPMDEF - #define EXT - #define INIT(x) =x -#else - #define EXT extern - #define INIT(x) -#endif - -/* The 16 directories to which the 16 CP/M drives are mapped */ - -EXT char redir_drive_prefix[16][CPM_MAXPATH]; - -/* Current drive and user */ - -EXT int redir_cpmdrive; -EXT int redir_cpmuser; - -/* Length of 1 read/write operation, bytes */ - -EXT int redir_rec_len INIT(128); - -/* Same, but in 128-byte records */ -EXT int redir_rec_multi INIT(1); - -/* Using a DRDOS system? */ -EXT int redir_drdos INIT(0); - -/* Default password */ -#ifdef __MSDOS__ -EXT char redir_passwd[8] INIT(""); -#endif - -EXT cpm_word redir_l_drives INIT(0); -EXT cpm_word redir_ro_drives INIT(0); - -#undef EXT -#undef INIT - - - -/* Convert FCB to a Unix filename, returning 1 if it's ambiguous */ -int redir_fcb2unix(cpm_byte *fcb, char *fname); - -/* Open FCB, set file attributes */ -int redir_ofile(cpm_byte * fcb, char *s); - -/* Check that the FCB we have is valid */ -int redir_verify_fcb(cpm_byte *fcb); - -#ifndef O_BINARY /* Necessary in DOS, not present in Linux */ -#define O_BINARY 0 -#endif - -/* Facilities for debug tracing */ - - -long zxlseek(int fd, long offset, int wh); - -#ifdef DEBUG - // long zxlseek(int fd, long offset, int wh); - void redir_Msg(char *s, ...); - void redir_showfcb(cpm_byte *fcb); -#else - // #define zxlseek lseek - /* Warning: This is a GCC extension */ - #define redir_Msg(x, ...) - #define redir_showfcb(x) -#endif - - - -/* Get the "sequential access" file pointer out of an FCB */ - -long redir_get_fcb_pos(cpm_byte *fcb); - -/* Write "sequential access" pointer to FCB */ - -void redir_put_fcb_pos(cpm_byte *fcb, long npos); - -/* Convert time_t to CP/M day count/hours/minutes */ -dword redir_cpmtime(time_t t); -/* And back */ -time_t redir_unixtime(cpm_byte *c); - - -/* Functions to access 24-bit & 32-bit words in memory. These are always - little-endian. */ - -void redir_wr24(cpm_byte *addr, dword v); -void redir_wr32(cpm_byte *addr, dword v); -dword redir_rd24(cpm_byte *addr); -dword redir_rd32(cpm_byte *addr); - -/* If you have 64-bit file handles, you'll need to write separate wrhandle() - and rdhandle() routines */ -#define redir_wrhandle redir_wr32 -#define redir_rdhandle redir_rd32 - -/* Mark a drive as logged in */ - -void redir_log_drv(cpm_byte drv); -void redir_log_fcb(cpm_byte *fcb); - -/* Check if a drive is software read-only */ - -int redir_ro_drv(cpm_byte drv); -int redir_ro_fcb(cpm_byte *fcb); - -/* Translate errno to a CP/M error */ - -cpm_word redir_xlt_err(void); - -/* Get disc label */ -cpm_word redir_get_label(cpm_byte drv, char *pattern); - - -/* DRDOS set/get access rights - no-ops under MSDOS and Unix: - * - * CP/M password mode -> DRDOS password mode */ -cpm_word redir_drdos_pwmode(cpm_byte b); - -/* DRDOS password mode to CP/M password mode */ -cpm_byte redir_cpm_pwmode(cpm_word w); - -/* Get DRDOS access rights for a file */ -cpm_word redir_drdos_get_rights(char *path); - -/* Set DRDOS access rights and/or password */ -cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights); - -/* Was the last error caused by invalid password? */ -cpm_word redir_password_error(void); - -/* Append password to filename (FILE.TYP -> FILE.TYP;PASSWORD) */ -void redir_password_append(char *s, cpm_byte *dma); - -void releaseFile(char *fname); -int trackFile(char *fname, void *fcb, int fd); -#define releaseFCB(fcb) trackFile(NULL, fcb, -1) +/* + + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file holds internal declarations for the library. +*/ + +#include "config.h" +#include +#ifdef HAVE_STDLIB_H + #include +#endif +#include +#ifdef HAVE_STRING_H + #include +#endif +#include +#include +#ifdef _WIN32 + #include +#endif +#ifdef HAVE_SYS_TYPES_H + #include +#endif +#include +#include +#ifdef HAVE_DIRENT_H + #include +#endif +#ifdef HAVE_DIRECT_H + #include +#endif +#ifdef HAVE_IO_H + #include +#endif +#ifdef HAVE_NDIR_H + #include +#endif +#ifdef HAVE_SYS_DIR_H + #include +#endif +#ifdef HAVE_SYS_NDIR_H + #include +#endif +#ifdef HAVE_WINDOWS_H + #include +#endif +#ifdef HAVE_WINNT_H + #include +#endif +#ifdef HAVE_SYS_VFS_H + #include +#endif +#ifdef HAVE_UTIME_H + #include +#endif +#ifdef HAVE_FCNTL_H + #include +#endif +#ifdef HAVE_UNISTD_H + #include +#endif +#ifndef _WIN32 + #include + #include + #define _S_IFDIR S_IFDIR +#endif + +/* MSDOS includes removed */ + +#ifdef _WIN32 + #define DIRSEP "/" + #define mkdir(dir, mode) _mkdir(dir) + #define strcasecmp _stricmp + #define ftruncate _chsize +/* note Windows build assumes Windows is configured as a non case sensitive filesystem */ + #ifndef STDIN_FILENO + #define STDIN_FILENO _fileno(stdin) + #define STDOUT_FILENO _fileno(stdout) + #define STDERR_FILENO _fileno(stderr) + #endif +#else + #define DIRSEP "/\\:" + #define CASE_SENSITIVE_FILESYSTEM 1 +#endif + +#ifdef _WIN32 +int truncate(const char* path, off_t length); /* see util.c */ +#endif + +typedef unsigned char byte; /* Must be exactly 8 bits */ +typedef unsigned short word; /* Must be exactly 16 bits */ +typedef unsigned long dword; /* Must be at least 32 bits, and + >= sizeof(int) */ + +#include "cpmredir.h" + +#ifdef CPMDEF + #define EXT + #define INIT(x) =x +#else + #define EXT extern + #define INIT(x) +#endif + +/* The 16 directories to which the 16 CP/M drives are mapped */ + +EXT char redir_drive_prefix[16][CPM_MAXPATH]; + +/* Current drive and user */ + +EXT int redir_cpmdrive; +EXT int redir_cpmuser; + +/* Length of 1 read/write operation, bytes */ + +EXT int redir_rec_len INIT(128); + +/* Same, but in 128-byte records */ +EXT int redir_rec_multi INIT(1); + +/* Using a DRDOS system? */ +EXT int redir_drdos INIT(0); + +/* Default password */ +#ifdef __MSDOS__ +EXT char redir_passwd[8] INIT(""); +#endif + +EXT cpm_word redir_l_drives INIT(0); +EXT cpm_word redir_ro_drives INIT(0); + +#undef EXT +#undef INIT + +/* Convert FCB to a Unix filename, returning 1 if it's ambiguous */ +int redir_fcb2unix(cpm_byte* fcb, char* fname); + +/* Open FCB, set file attributes */ +int redir_ofile(cpm_byte* fcb, char* s); + +/* Check that the FCB we have is valid */ +int redir_verify_fcb(cpm_byte* fcb); + +#ifndef O_BINARY /* Necessary in DOS, not present in Linux */ +#define O_BINARY 0 +#endif + +/* Facilities for debug tracing */ + +long zxlseek(int fd, long offset, int wh); + +#ifdef _WIN32 +char* GetErrorStr(DWORD); +#endif + +#ifdef DEBUG +// long zxlseek(int fd, long offset, int wh); +// void redir_Msg(char *s, ...); +void DbgMsg(const char* file, int line, const char* func, char* s, ...); +void redir_showfcb(cpm_byte* fcb); +#else +// #define zxlseek lseek +/* Warning: This is a GCC extension */ +// #define redir_Msg(x, ...) +#define redir_showfcb(x) +#endif + +#ifdef DEBUG +#define FCBENT(fcb) \ + { \ + char fname[CPM_MAXPATH] = ""; \ + redir_fcb2unix(fcb, fname); \ + DBGMSGV("entry w/ FCB @ 0x%04X, filename:'%s'\n", fcb-RAM, fname); \ + } + +#define FCBRET(rc) \ + { \ + DBGMSGV("returning 0x%04X\n", rc); \ + return rc; \ + } + +#define DBGMSGV(s, ...) DbgMsg(__FILE__, __LINE__, __func__, s, __VA_ARGS__) +#define DBGMSG(s) DbgMsg(__FILE__, __LINE__, __func__, s) +#else +#define FCBENT(fcb) +#define FCBRET(rc) return rc; +#define DBGMSGV(s, ...) +#define DBGMSG(s) +#endif + +/* Get the "sequential access" file pointer out of an FCB */ + +long redir_get_fcb_pos(cpm_byte* fcb); + +/* Write "sequential access" pointer to FCB */ + +void redir_put_fcb_pos(cpm_byte* fcb, long npos); + +/* Convert time_t to CP/M day count/hours/minutes */ +dword redir_cpmtime(time_t t); +/* And back */ +time_t redir_unixtime(cpm_byte* c); + +/* Functions to access 24-bit & 32-bit words in memory. These are always + little-endian. */ + +void redir_wr24(cpm_byte* addr, dword v); +void redir_wr32(cpm_byte* addr, dword v); +dword redir_rd24(cpm_byte* addr); +dword redir_rd32(cpm_byte* addr); + +/* If you have 64-bit file handles, you'll need to write separate wrhandle() + and rdhandle() routines */ +#define redir_wrhandle redir_wr32 +#define redir_rdhandle redir_rd32 + +/* Mark a drive as logged in */ + +void redir_log_drv(cpm_byte drv); +void redir_log_fcb(cpm_byte* fcb); + +/* Check if a drive is software read-only */ + +int redir_ro_drv(cpm_byte drv); +int redir_ro_fcb(cpm_byte* fcb); + +/* Translate errno to a CP/M error */ + +cpm_word redir_xlt_err(void); + +/* Get disc label */ +cpm_word redir_get_label(cpm_byte drv, char* pattern); + +/* DRDOS set/get access rights - no-ops under MSDOS and Unix: + * + * CP/M password mode -> DRDOS password mode */ +cpm_word redir_drdos_pwmode(cpm_byte b); + +/* DRDOS password mode to CP/M password mode */ +cpm_byte redir_cpm_pwmode(cpm_word w); + +/* Get DRDOS access rights for a file */ +cpm_word redir_drdos_get_rights(char* path); + +/* Set DRDOS access rights and/or password */ +cpm_word redir_drdos_put_rights(char* path, cpm_byte* dma, cpm_word rights); + +/* Was the last error caused by invalid password? */ +cpm_word redir_password_error(void); + +/* Append password to filename (FILE.TYP -> FILE.TYP;PASSWORD) */ +void redir_password_append(char* s, cpm_byte* dma); + +void releaseFile(char* fname); +int trackFile(char* fname, void* fcb, int fd); +#define releaseFCB(fcb) trackFile(NULL, fcb, -1) + +extern byte RAM[65536]; /* The Z80's address space */ diff --git a/Tools/unix/zxcc/cpmparse.c b/Tools/unix/zxcc/cpmparse.c index ea0949ed..73919c68 100644 --- a/Tools/unix/zxcc/cpmparse.c +++ b/Tools/unix/zxcc/cpmparse.c @@ -1,126 +1,124 @@ /* - CPMREDIR: CP/M filesystem redirector - Copyright (C) 1998, John Elliott + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - This file parses filenames to FCBs. + This file parses filenames to FCBs. */ #include "cpmint.h" #define is_num(c) ((c >= '0') && (c <= '9')) -static int parse_drive_user(char *txt, cpm_byte *fcb) +static int parse_drive_user(char* txt, cpm_byte* fcb) { - char uid[4], drvid[4]; - int up, dp; + char uid[4], drvid[4]; + int up, dp; - for (up = dp = 0; *txt != ':'; ++txt) - { - if (is_num (*txt)) uid [up++] = *txt; - if (isalpha(*txt)) drvid[dp++] = *txt; - if (!is_num(*txt) && !isalpha(*txt)) return -1; - } - uid[up] = 0; drvid[dp] = 0; + for (up = dp = 0; *txt != ':'; ++txt) + { + if (is_num(*txt)) uid[up++] = *txt; + if (isalpha(*txt)) drvid[dp++] = *txt; + if (!is_num(*txt) && !isalpha(*txt)) return -1; + } + uid[up] = 0; drvid[dp] = 0; - if (dp > 1) return -1; /* Invalid driveletter */ - if (up > 2) return -1; /* Invalid uid */ - - fcb[0x0d] = atoi(uid) + 1; if (fcb[0x0d] > 16) return -1; + if (dp > 1) return -1; /* Invalid driveletter */ + if (up > 2) return -1; /* Invalid uid */ - if (islower(drvid[0])) drvid[0] = toupper(drvid[0]); + fcb[0x0d] = atoi(uid) + 1; if (fcb[0x0d] > 16) return -1; - if (drvid[0] < 'A' || drvid[0] > 'P') return -1; - - fcb[0] = drvid[0] - '@'; - return 0; -} + if (islower(drvid[0])) drvid[0] = toupper(drvid[0]); + if (drvid[0] < 'A' || drvid[0] > 'P') return -1; + fcb[0] = drvid[0] - '@'; + return 0; +} -cpm_word fcb_parse(char *txt, cpm_byte *fcb) +cpm_word fcb_parse(char* txt, cpm_byte* fcb) { - int nl = 0, tl = 0, pl = 0, phase = 0; - char *ntxt, ch; - - memset(fcb, 0, 0x24); - - if (txt[1] == ':' || txt[2] == ':' || txt[3] == ':') - { - if (parse_drive_user(txt, fcb)) return 0xFFFF; - /* Move past the colon */ - ntxt = strchr(txt, ':') + 1; - } - else ntxt = txt; - while (phase < 3) - { - ch = *ntxt; - if (islower(ch)) ch = toupper(ch); - - switch(ch) - { - case 0: - case '\r': /* EOL */ - phase = 4; - break; - - case '.': /* file.typ */ - if (!phase) ++phase; - else phase = 3; - break; - - case ';': /* Password */ - if (phase < 2) phase = 2; - else phase = 3; - break; - - case '[': case ']': case '=': case 9: case ' ': - case '>': case '<': case ':': case ',': case '/': - case '|': /* Terminator */ - phase = 3; - - default: - switch(phase) - { - case 0: - if (nl >= 8) return 0xFFFF; - fcb[++nl] = ch; - break; - - case 1: - if (tl >= 3) return 0xFFFF; - fcb[tl + 9] = ch; - ++tl; - break; - - case 2: - if (pl >= 8) return 0xFFFF; - fcb[pl + 0x10] = ch; - ++pl; - break; - } - break; - } - } - if (!nl) return 0xFFFF; - - fcb[0x1A] = pl; - - if (phase == 4) return 0; - - return (cpm_word)(ntxt - txt); + int nl = 0, tl = 0, pl = 0, phase = 0; + char* ntxt, ch; + + memset(fcb, 0, 0x24); + + if (txt[1] == ':' || txt[2] == ':' || txt[3] == ':') + { + if (parse_drive_user(txt, fcb)) return 0xFFFF; + /* Move past the colon */ + ntxt = strchr(txt, ':') + 1; + } + else ntxt = txt; + while (phase < 3) + { + ch = *ntxt; + if (islower(ch)) ch = toupper(ch); + + switch (ch) + { + case 0: + case '\r': /* EOL */ + phase = 4; + break; + + case '.': /* file.typ */ + if (!phase) ++phase; + else phase = 3; + break; + + case ';': /* Password */ + if (phase < 2) phase = 2; + else phase = 3; + break; + + case '[': case ']': case '=': case 9: case ' ': + case '>': case '<': case ':': case ',': case '/': + case '|': /* Terminator */ + phase = 3; + + default: + switch (phase) + { + case 0: + if (nl >= 8) return 0xFFFF; + fcb[++nl] = ch; + break; + + case 1: + if (tl >= 3) return 0xFFFF; + fcb[tl + 9] = ch; + ++tl; + break; + + case 2: + if (pl >= 8) return 0xFFFF; + fcb[pl + 0x10] = ch; + ++pl; + break; + } + break; + } + } + if (!nl) return 0xFFFF; + + fcb[0x1A] = pl; + + if (phase == 4) return 0; + + return (cpm_word)(ntxt - txt); } diff --git a/Tools/unix/zxcc/cpmredir.c b/Tools/unix/zxcc/cpmredir.c index 6484608e..cb47cc1c 100644 --- a/Tools/unix/zxcc/cpmredir.c +++ b/Tools/unix/zxcc/cpmredir.c @@ -1,967 +1,1004 @@ -/* - - CPMREDIR: CP/M filesystem redirector - Copyright (C) 1998, John Elliott - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* This file handles actual reading and writing */ - -#define CPMDEF -#include "cpmint.h" - -#ifdef DEBUG -#define SHOWNAME(func) \ - { \ - char fname[CPM_MAXPATH]; \ - redir_fcb2unix(fcb, fname); \ - redir_Msg(func "(\"%s\")\n", fname); \ - } - -#else - #define SHOWNAME(func) -#endif - - -/* DISK BDOS FUNCTIONS */ - -/* General treatment: - * - * We use the "disk block number" fields in the FCB to store our file handle; - * this is a similar trick to that used by DOSPLUS, which stores its cluster - * number in there. It works if: - * - * a) sizeof(int) <= 8 bytes (64 bits). If it's more, this needs rewriting - * to use a hash table; - * b) the program never touches these bytes. Practically no CP/M program does. - * - * We store a "magic number" (0x00FD) in the first two bytes of this field, and - * if the number has been changed then we abort. - * - * nb: Since I wrote ZXCC, I have found that DOSPLUS uses 0x8080 as a magic - * number [well, actually this is an oversimplification, but a hypothetical - * program written against DOSPLUS would work with 0x8080]. Perhaps 0x8080 - * should be used instead. - * - * Format of the field: - * - * [--2 bytes--] magic number - * [--8 bytes--] file handle. 8 bytes reserved but only 4 currently used. - * [--2 bytes--] reserved. - * [--4 bytes--] file length. - */ -#define MAGIC_OFFSET 0x10 -#define HANDLE_OFFSET 0x12 -#define LENGTH_OFFSET 0x1C - -cpm_word fcb_open(cpm_byte *fcb, cpm_byte *dma) -{ - char fname[CPM_MAXPATH]; - int handle; - int drv; - size_t l; - char *s; - DIR *dir; - - /* Don't support ambiguous filenames */ - if (redir_fcb2unix(fcb, fname)) return 0x09FF; - - redir_log_fcb(fcb); - - drv = fcb[0] & 0x7F; - if (!drv) drv = redir_cpmdrive; else --drv; - - if (fcb[0] & 0x80) /* Open directory */ - { - if (fcb[0x0C]) return 0x0BFF; /* Can't assign "floating" dir */ - - if (!memcmp(fcb + 1, ". ", 11)) - { - return 0; /* Opening "." */ - } - if (!memcmp(fcb + 1, ".. ", 11)) - { - l = strlen(redir_drive_prefix[drv]) - 1; - s = redir_drive_prefix[drv]; - while (--l > 0 && !strchr(DIRSEP, s[l])) - --l; - if (l == 0) return 0; /* "/" or "\" */ -#ifdef _WIN32 - if (s[l] == ':' && l < 2) return 0; /* "C:" */ -#endif - s[l + 1] = 0; - return 0; - } -/* Opening some other directory */ - - dir = opendir(fname); - if (!dir) return 0xFF; /* Not a directory */ - closedir(dir); - strcpy(redir_drive_prefix[drv], fname); - strcat(redir_drive_prefix[drv], "/"); - return 0; - } - - /* Note: Some programs (MAC is an example) don't close a file - * if they opened it just to do reading. MAC then reopens the - * file (which rewinds it); this causes FCB leaks under some - * DOS-based emulators */ - - handle = redir_ofile(fcb, fname); - redir_Msg("fcb_open(\"%s\")\r\n", fname); - if (handle < 0 && redir_password_error()) - { - redir_Msg("1st chance open failed on %s\r\n", fname); - redir_password_append(fname, dma); - redir_Msg("Trying with %s\r\n", fname); - handle = redir_ofile(fcb, fname); - } - - - if (handle == -1) - { - redir_Msg("Ret: -1\n"); - if (redir_password_error()) return 0x7FF; - return 0xFF; - } - fcb[MAGIC_OFFSET ] = 0xFD; /* "Magic number" */ - fcb[MAGIC_OFFSET + 1] = 0x00; - -/* TODO: Should the magic number perhaps be 0x8080, as in DOSPLUS? */ - - redir_wrhandle(fcb + HANDLE_OFFSET, handle); - - redir_put_fcb_pos(fcb, fcb[0x0C] * 16384); - /* (v1.01) "seek" to beginning of extent, not file. - * This is necessary for the awful I/O code - * in LINK-80 to work - */ - - /* Get the file length */ - redir_wr32(fcb + LENGTH_OFFSET, zxlseek(handle, 0, SEEK_CUR)); - zxlseek(handle, 0, SEEK_SET); - - /* Set the last record byte count */ - if (fcb[0x20] == 0xFF) fcb[0x20] = fcb[LENGTH_OFFSET] & 0x7F; - - redir_Msg("Ret: 0\n"); - - return 0; -} - - -cpm_word fcb_close(cpm_byte *fcb) -{ - int handle, drv; - - SHOWNAME("fcb_close") - - if ((handle = redir_verify_fcb(fcb)) < 0) return -1; - redir_Msg(" (at %lx)\n", zxlseek(handle, 0, SEEK_END)); - - if (fcb[0] & 0x80) /* Close directory */ - { - drv = fcb[0] & 0x7F; - if (!drv) drv = redir_cpmdrive; else drv--; -#ifdef __MSDOS__ - strcpy(redir_drive_prefix[drv] + 1, ":/"); -#else - strcpy(redir_drive_prefix[drv], "/"); -#endif - return 0; - } - - if (fcb[5] & 0x80) /* CP/M 3: Flush rather than close */ - { -#ifndef _WIN32 - sync(); -#endif - return 0; - } - -#ifdef _WIN32 - { - BOOL b; - redir_Msg(">CloseHandle() Handle=%lu\n", handle); - b = CloseHandle((HANDLE)handle); - redir_Msg("80h, let it be 80h - -*/ - - -cpm_word fcb_read(cpm_byte *fcb, cpm_byte *dma) -{ - int handle; - int rv, n, rd_len; - long npos; - - SHOWNAME("fcb_read") - - if ((handle = redir_verify_fcb(fcb)) < 0) return 9; /* Invalid FCB */ - - /* The program may have mucked about with the counters, so - * do an lseek() to where it should be. */ - - npos = redir_get_fcb_pos(fcb); - zxlseek(handle, npos, SEEK_SET); - redir_Msg(" (from %lx)\n", zxlseek(handle, 0, SEEK_CUR)); - - /* Read in the required amount */ - - memset(dma, 0x00, redir_rec_len); - -#ifdef _WIN32 - { - BOOL b; - redir_Msg(">ReadFile() Handle=%lu, DMA=%lu, Len=%lu\n", handle, dma, redir_rec_len); - b = ReadFile((HANDLE)handle, dma, redir_rec_len, (unsigned long *)(&rv), NULL); - redir_Msg("= 0) && (rv < redir_rec_len)) - memset(dma + rv, 0x00, redir_rec_len - rv); - - /* rd_len = length supposedly read, bytes. Round to nearest 128 bytes. - */ - rd_len = ((rv + 127) / 128) * 128; - - npos += rd_len; - - /* Write new file pointer into FCB */ - - redir_put_fcb_pos(fcb, npos); - - if (rv < 0) - { - redir_Msg("Ret: -1\n"); - return redir_xlt_err(); /* unwritten extent */ - } - - /* if not multiple of 128 bytes, pad sector with 0x1A */ - for (n = rv; n < rd_len; n++) dma[n] = 0x1A; - - /* Less was read in than asked for. Report the number of 128-byte - * records that _were_ read in. - */ - - if (rd_len < redir_rec_len) /* eof */ - { - /* Pack from the size actually read up to the size we claim - * to have read */ - rd_len = rd_len * 2; /* rd_len already sector * 128, so * 2 to move to High byte */ - redir_Msg("Ret: 0x%x\n", rd_len | 1); - return rd_len | 1; /* eof */ - } - - redir_Msg("Ret: 0 (bytes read=%d)\n", rv); - return 0; -} - - -cpm_word fcb_write(cpm_byte *fcb, cpm_byte *dma) -{ - int handle; - int rv; - long npos, len; - - SHOWNAME("fcb_write") - - if ((handle = redir_verify_fcb(fcb)) < 0) return 9; /* Invalid FCB */ - - /* Software write-protection */ - if (redir_ro_fcb(fcb)) return 0x02FF; - - /* Check for a seek */ - npos = redir_get_fcb_pos(fcb); - zxlseek(handle, npos, SEEK_SET); - - redir_Msg(" (to %lx)\n", zxlseek(handle, 0, SEEK_CUR)); - -#ifdef _WIN32 - { - BOOL b; - redir_Msg(">WriteFile() Handle=%lu, DMA=%lu, Len=%lu\n", handle, dma, redir_rec_len); - b = WriteFile((HANDLE)handle, dma, redir_rec_len, (unsigned long *)(&rv), NULL); - redir_Msg("CreateFile([CREATE_ALWAYS]) Name='%s'\n", fname); - handle = (int)CreateFile(fname, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - redir_Msg("ReadFile() Handle=%lu, DMA=%lu, Len=%lu\n", handle, dma, redir_rec_len); - b = ReadFile((HANDLE)handle, dma, redir_rec_len, (unsigned long *)(&rv), NULL); - redir_Msg("= 0) && (rv < redir_rec_len)) - memset(dma + rv, 0x00, redir_rec_len - rv); - - zxlseek(handle, offs, SEEK_SET); - - redir_put_fcb_pos(fcb, offs); - - if (rv < 0) return redir_xlt_err(); /* Error */ - - rd_len = ((rv + 127) / 128) * 128; - - /* PMO: pad partial sector to 128 bytes, even if EOF reached in multi sector read */ - for (n = rv; n < rd_len; n++) dma[n] = 0x1A; /* pad last read to 128 boundary with 0x1A*/ - - if (rd_len < redir_rec_len) /* eof */ - { - rd_len = rd_len * 2; /* rd_len already sector * 128, so * 2 to move to High byte */ - redir_Msg("Ret: 0x%x\n", rd_len | 1); - return rd_len | 1; /* eof */ - } - - return 0; -} - - - -cpm_word fcb_randwr(cpm_byte *fcb, cpm_byte *dma) -{ - int handle; - int rv; - dword offs = redir_rd24(fcb + 0x21) * 128; - dword len; - - SHOWNAME("fcb_randwr") - - if ((handle = redir_verify_fcb(fcb)) < 0) return 9; /* Invalid FCB */ - /* Software write-protection */ - if (redir_ro_fcb(fcb)) return 0x02FF; - - if (zxlseek(handle, offs, SEEK_SET) < 0) return 6; /* bad record no. */ - -#ifdef _WIN32 - { - BOOL b; - redir_Msg(">WriteFile() Handle=%lu, DMA=%lu, Len=%lu\n", handle, dma, redir_rec_len); - b = WriteFile((HANDLE)handle, dma, redir_rec_len, (unsigned long *)(&rv), NULL); - redir_Msg("WriteFile() Handle=%lu, DMA=%lu, Len=%lu\n", handle, zerorec, rl); - b = WriteFile((HANDLE)handle, zerorec, rl, (unsigned long *)(&rv), NULL); - redir_Msg("= 0) len += rv; - - if (rv < rl) - { - redir_wr32(fcb + LENGTH_OFFSET, len); - return redir_xlt_err(); - } - } - redir_wr32(fcb + LENGTH_OFFSET, offs); - - return fcb_randwr(fcb, dma); -} -#endif - - -cpm_word fcb_tell(cpm_byte *fcb) -{ - int handle; - off_t rv; - - SHOWNAME("fcb_tell") - - if ((handle = redir_verify_fcb(fcb)) < 0) return 9; /* Invalid FCB */ - - rv = zxlseek(handle, 0, SEEK_CUR); - - if (rv < 0) return 0xFF; - - rv = rv >> 7; - fcb[0x21] = rv & 0xFF; - fcb[0x22] = (rv >> 8) & 0xFF; - fcb[0x23] = (rv >> 16) & 0xFF; - return 0; -} - - -cpm_word fcb_stat(cpm_byte *fcb) -{ - char fname[CPM_MAXPATH]; - struct stat st; - int rv; - - /* Don't support ambiguous filenames */ - if (redir_fcb2unix(fcb, fname)) return 0x09FF; - - rv = stat(fname, &st); - - redir_Msg("fcb_stat(\"%s\") fcb=%p\n", fname, fcb); - if (rv < 0) - { - redir_Msg("ret: -1\n"); - return 0xFF; - } - - redir_wr24(fcb + 0x21, (st.st_size + 127) / 128); - - redir_Msg("ret: 0"); - return 0; -} - - -cpm_word fcb_multirec(cpm_byte rc) -{ - if (rc < 1 || rc > 128) return 0xFF; - - redir_rec_multi = rc; - redir_rec_len = 128 * rc; - redir_Msg("Set read/write to %d bytes\n", redir_rec_len); - return 0; -} - - -cpm_word fcb_date(cpm_byte *fcb) -{ - char fname[CPM_MAXPATH]; - struct stat st; - int rv; - - /* as this function will overwrite the fcb info used by ZXCC - * release any file associated with it - */ - releaseFCB(fcb); - /* Don't support ambiguous filenames */ - if (redir_fcb2unix(fcb, fname)) return 0x09FF; - - rv = stat(fname, &st); - - redir_Msg("fcb_stat(\"%s\")\n", fname); - if (rv < 0) return 0xFF; - - redir_wr32(fcb + 0x18, redir_cpmtime(st.st_atime)); - redir_wr32(fcb + 0x1C, redir_cpmtime(st.st_ctime)); - - fcb[0x0C] = redir_cpm_pwmode(redir_drdos_get_rights(fname)); - return 0; -} - - -cpm_word fcb_trunc(cpm_byte *fcb, cpm_byte *dma) -{ - char fname[CPM_MAXPATH]; - dword offs = redir_rd24(fcb + 0x21) * 128; - - releaseFCB(fcb); /* CP/M requires truncated files be closed */ - /* Don't support ambiguous filenames */ - if (redir_fcb2unix(fcb, fname)) return 0x09FF; - - /* Software write-protection */ - if (redir_ro_fcb(fcb)) return 0x02FF; - - releaseFile(fname); /* after truncate open files are invalid */ - redir_log_fcb(fcb); - if (truncate(fname, offs)) - { - if (redir_password_error()) - { - redir_password_append(fname, dma); - if (!truncate(fname, offs)) return 0; - } - return redir_xlt_err(); - } - return 0; -} - - -cpm_word fcb_sdate(cpm_byte *fcb, cpm_byte *dma) -{ - char fname[CPM_MAXPATH]; - - struct utimbuf buf; - - buf.actime = redir_unixtime(dma); - buf.modtime = redir_unixtime(dma + 4); - - /* Don't support ambiguous filenames */ - if (redir_fcb2unix(fcb, fname)) return 0x09FF; - - /* Software write-protection */ - if (redir_ro_fcb(fcb)) return 0x02FF; - - redir_log_fcb(fcb); - - if (utime(fname, &buf)) - { - if (redir_password_error()) - { - redir_password_append(fname, dma); - if (!utime(fname, &buf)) return 0; - } - return redir_xlt_err(); - } - return 0; -} - - - -cpm_word fcb_chmod(cpm_byte *fcb, cpm_byte *dma) -{ - char fname[CPM_MAXPATH]; - struct stat st; - int handle, wlen, omode; - long offs, newoffs; - cpm_byte zero[128]; - - /* Don't support ambiguous filenames */ - if (redir_fcb2unix(fcb, fname)) return 0x09FF; - - /* Software write-protection */ - if (redir_ro_fcb(fcb)) return 0x02FF; - - redir_log_fcb(fcb); - - if (stat(fname, &st)) return redir_xlt_err(); - -#ifdef __MSDOS__ - omode = 0; - if (fcb[9] & 0x80) omode |= 1; - if (fcb[10] & 0x80) omode |= 4; - if (!(fcb[11] & 0x80)) omode |= 0x20; - - if (_chmod(fname, 1, omode) < 0) - { - if (redir_password_error()) - { - redir_password_append(fname, dma); - if (_chmod(fname, 1, omode) >= 0) return 0; - } - return redir_xlt_err(); - } -#elif defined (_WIN32) - omode = 0; - - if (fcb[9] & 0x80) omode |= FILE_ATTRIBUTE_READONLY; - if (fcb[10] & 0x80) omode |= FILE_ATTRIBUTE_SYSTEM; - if (!(fcb[11] & 0x80)) omode |= FILE_ATTRIBUTE_ARCHIVE; - - if (!omode) omode = FILE_ATTRIBUTE_NORMAL; - - SetFileAttributes(fname, omode); -#else - omode = st.st_mode; - if (fcb[9] & 0x80) /* Read-only */ - { - st.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); - } - else st.st_mode |= S_IWUSR; - - if (omode != st.st_mode) - { - if (chmod(fname, st.st_mode)) return redir_xlt_err(); - } - -#endif - - if (fcb[6] & 0x80) /* Set exact size */ - { - if (stat(fname, &st)) return redir_xlt_err(); - - releaseFCB(fcb); /* cpm required file to be closed so release FCB */ - releaseFile(fname); /* also make sure no other handles open to file */ - handle = open(fname, O_RDWR | O_BINARY); - if (handle < 0) return redir_xlt_err(); - - newoffs = offs = ((st.st_size + 127) / 128) * 128; - if (fcb[0x20] & 0x7F) - { - newoffs -= fcb[0x20] & 0x7f; - //newoffs -= (0x80 - (fcb[0x20] & 0x7F)); - } - if (newoffs == st.st_size) - { - ; /* Nothing to do! */ - } - else if (newoffs < st.st_size) - { - if (ftruncate(handle, newoffs)) - { - close(handle); - return redir_xlt_err(); - } - } - else while (newoffs > st.st_size) - { - wlen = newoffs - st.st_size; - if (wlen > 0x80) wlen = 0x80; - memset(zero, 0x1A, sizeof(zero)); - if (write(handle, zero, wlen) < wlen) - { - close(handle); - return redir_xlt_err(); - } - st.st_size += wlen; - } - close(handle); - } - return 0; -} - - - - -cpm_word fcb_setpwd(cpm_byte *fcb, cpm_byte *dma) -{ -#ifdef __MSDOS__ - char fname[CPM_MAXPATH]; - cpm_word rv; - - /* Don't support ambiguous filenames */ - if (redir_fcb2unix(fcb, fname)) return 0x09FF; - - /* Software write-protection */ - if (redir_ro_fcb(fcb)) return 0x02FF; - - redir_log_fcb(fcb); - - rv = redir_drdos_put_rights(fname, dma, redir_drdos_pwmode(fcb[0x0c])); - if (rv || !(fcb[0x0c] & 1)) return rv; - return redir_drdos_put_rights(fname, dma, redir_drdos_pwmode(fcb[0x0c]) | 0x8000); -#else - return 0xFF; /* Unix doesn't do this */ -#endif -} - - -cpm_word fcb_getlbl(cpm_byte drv) -{ - redir_Msg("fcb_getlbl()\r\n"); -#ifdef __MSDOS__ - if (redir_drdos) return 0xA1; /* Supports passwords & Update stamps */ - return 0x21; /* Update stamps only */ -#else - return 0x61; /* Update & Access stamps */ -#endif -} - -cpm_word fcb_setlbl(cpm_byte *fcb, cpm_byte *dma) -{ -/* I am not letting CP/M fiddle with the host's FS settings - even if they - * could be altered, which they mostly can't. */ - - return 0x03FF; -} - - - -cpm_word fcb_defpwd(cpm_byte *pwd) -{ -#ifdef __MSDOS__ - union REGS r; - struct SREGS s; - - if (pwd[0] == 0 || pwd[0] == ' ') - { - redir_passwd[0] = 0; - } - else memcpy(redir_passwd, pwd, 8); - if (redir_drdos) - { - dosmemput(pwd, 8, __tb); - r.w.ax = 0x4454; - r.w.dx = __tb & 0x0F; - s.ds = __tb >> 4; - intdosx(&r, &r, &s); - } - -#endif - return 0; -} - - +/* + + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* This file handles actual reading and writing */ + +#define CPMDEF +#include "cpmint.h" + +/* DISK BDOS FUNCTIONS */ + +/* General treatment: + * + * We use the "disk block number" fields in the FCB to store our file handle; + * this is a similar trick to that used by DOSPLUS, which stores its cluster + * number in there. It works if: + * + * a) sizeof(int) <= 8 bytes (64 bits). If it's more, this needs rewriting + * to use a hash table; + * b) the program never touches these bytes. Practically no CP/M program does. + * + * We store a "magic number" (0x00FD) in the first two bytes of this field, and + * if the number has been changed then we abort. + * + * nb: Since I wrote ZXCC, I have found that DOSPLUS uses 0x8080 as a magic + * number [well, actually this is an oversimplification, but a hypothetical + * program written against DOSPLUS would work with 0x8080]. Perhaps 0x8080 + * should be used instead. + * + * Format of the field: + * + * [--2 bytes--] magic number + * [--8 bytes--] file handle. 8 bytes reserved but only 4 currently used. + * [--2 bytes--] reserved. + * [--4 bytes--] file length. + */ + +#define MAGIC_OFFSET 0x10 +#define HANDLE_OFFSET 0x12 +#define LENGTH_OFFSET 0x1C + +cpm_word fcb_open(cpm_byte* fcb, cpm_byte* dma) +{ + char fname[CPM_MAXPATH]; + int handle; + int drv; + size_t l; + char* s; + DIR* dir; + + FCBENT(fcb); + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) FCBRET(0x09FF); + + redir_log_fcb(fcb); + + drv = fcb[0] & 0x7F; + if (!drv) drv = redir_cpmdrive; else --drv; + + if (fcb[0] & 0x80) /* Open directory */ + { + if (fcb[0x0C]) FCBRET(0x0BFF); /* Can't assign "floating" dir */ + + if (!memcmp(fcb + 1, ". ", 11)) + { + FCBRET(0); /* Opening "." */ + } + if (!memcmp(fcb + 1, ".. ", 11)) + { + l = strlen(redir_drive_prefix[drv]) - 1; + s = redir_drive_prefix[drv]; + while (--l > 0 && !strchr(DIRSEP, s[l])) + --l; + if (l == 0) FCBRET(0); /* "/" or "\" */ +#ifdef _WIN32 + if (s[l] == ':' && l < 2) FCBRET(0); /* "C:" */ +#endif + s[l + 1] = 0; + FCBRET(0); + } + /* Opening some other directory */ + + dir = opendir(fname); + if (!dir) FCBRET(0xFF); /* Not a directory */ + closedir(dir); + strcpy(redir_drive_prefix[drv], fname); + strcat(redir_drive_prefix[drv], "/"); + FCBRET(0); + } + + /* Note: Some programs (MAC is an example) don't close a file + * if they opened it just to do reading. MAC then reopens the + * file (which rewinds it); this causes FCB leaks under some + * DOS-based emulators */ + + handle = redir_ofile(fcb, fname); + //DBGMSGV("fcb_open('%s')\n", fname); + if (handle < 0 && redir_password_error()) + { + DBGMSGV("1st chance open failed on %s\n", fname); + redir_password_append(fname, dma); + DBGMSGV("Trying with %s\n", fname); + handle = redir_ofile(fcb, fname); + } + + if (handle == -1) + { + if (redir_password_error()) FCBRET(0x7FF); + FCBRET(0xFF); + } + fcb[MAGIC_OFFSET] = 0xFD; /* "Magic number" */ + fcb[MAGIC_OFFSET + 1] = 0x00; + + /* TODO: Should the magic number perhaps be 0x8080, as in DOSPLUS? */ + + redir_wrhandle(fcb + HANDLE_OFFSET, handle); + + redir_put_fcb_pos(fcb, fcb[0x0C] * 16384); + /* (v1.01) "seek" to beginning of extent, not file. + * This is necessary for the awful I/O code + * in LINK-80 to work + */ + + /* Get the file length */ + redir_wr32(fcb + LENGTH_OFFSET, zxlseek(handle, 0, SEEK_END)); + zxlseek(handle, 0, SEEK_SET); + + /* Set the last record byte count */ + if (fcb[0x20] == 0xFF) fcb[0x20] = fcb[LENGTH_OFFSET] & 0x7F; + + FCBRET(0); +} + +cpm_word fcb_close(cpm_byte* fcb) +{ + int handle, drv; + + FCBENT(fcb); + + if ((handle = redir_verify_fcb(fcb)) < 0) FCBRET(-1); + DBGMSGV(" (at 0x%lX)\n", zxlseek(handle, 0, SEEK_CUR)); + + if (fcb[0] & 0x80) /* Close directory */ + { + drv = fcb[0] & 0x7F; + if (!drv) drv = redir_cpmdrive; else drv--; +#ifdef __MSDOS__ + strcpy(redir_drive_prefix[drv] + 1, ":/"); +#else + strcpy(redir_drive_prefix[drv], "/"); +#endif + FCBRET(0); + } + + if (fcb[5] & 0x80) /* CP/M 3: Flush rather than close */ + { +#ifdef _WIN32 + BOOL b; + DBGMSGV("flush file #%i\n", handle); + b = FlushFileBuffers((HANDLE)handle); + if (!b) + DBGMSGV("failed to flush file #%i (Error=%lu): %s\n", handle, GetLastError(), GetErrorStr(GetLastError())); +#else + DBGMSGV("flush file #%i\n", handle); + sync(); +#endif + FCBRET(0); + } + +#ifdef _WIN32 + { + BOOL b; + DBGMSGV("close file #%i\n", handle); + b = CloseHandle((HANDLE)handle); + if (!b) + { + DBGMSGV("failed to close file #%i (Error=%lu): %s\n", handle, GetLastError(), GetErrorStr(GetLastError())); + FCBRET(0xFF); + } + } +#else + DBGMSGV("close file #%i\n", handle); + if (close(handle)) + { + DBGMSGV("failed to close file #%i (Error=%lu): %s\n", handle, errno, strerror(errno)); + FCBRET(0xFF); + } +#endif + + FCBRET(0); +} + +/* In theory, fcb_read() is supposed to be sequential access - the program + just reads one record after another and lets the OS worry about file + pointers. + + In practice, it isn't so easy. For example, DR's LINK-80 does seeks when + the file size gets above 8k, and SAVE rewinds the file by setting the + counter fields to 0. + + Seeking is done by relying on the following fields: + + ex (FCB+12) = (position / 16k) % 32 + s2 (FCB+14) = position / 512k + cr (FCB+32) = (position % 16k) / 128 + + TODO: Set rc to number of 80h-byte records in last extent: ie: + + length of file - (file ptr - (file ptr % 16384)) / 128 + + if >80h, let it be 80h + +*/ + +cpm_word fcb_read(cpm_byte* fcb, cpm_byte* dma) +{ + int handle; + int rv, n, rd_len; + long npos; + + FCBENT(fcb); + + if ((handle = redir_verify_fcb(fcb)) < 0) FCBRET(9); /* Invalid FCB */ + + /* The program may have mucked about with the counters, so + * do an lseek() to where it should be. */ + + npos = redir_get_fcb_pos(fcb); + zxlseek(handle, npos, SEEK_SET); + DBGMSGV(" (from 0x%lX)\n", zxlseek(handle, 0, SEEK_CUR)); + + /* Read in the required amount */ + +#ifdef _WIN32 + { + BOOL b; + DBGMSGV("read file #%i @ 0x%X, %i bytes\n", handle, dma - RAM, redir_rec_len); + b = ReadFile((HANDLE)handle, dma, redir_rec_len, (unsigned long*)(&rv), NULL); + if (!b) + { + DBGMSGV("failed to read file #%i (Error=%lu): %s\n", handle, GetLastError(), GetErrorStr(GetLastError())); + rv = -1; + } + } +#else + DBGMSGV("read file #%i @ 0x%X, %i bytes\n", handle, dma - RAM, redir_rec_len); + rv = read(handle, dma, redir_rec_len); + if (rv == -1) + DBGMSGV("failed to read file #%i (errno=%lu): %s\n", handle, errno, strerror(errno)); +#endif + + /* read() can corrupt buffer area following data read if length + * of data read is less than buffer. Clean it up. */ + if (rv == -1) + memset(dma, 0x00, redir_rec_len); + else + memset(dma + rv, 0x00, redir_rec_len - rv); + + /* rd_len = length supposedly read, bytes. Round to nearest 128 + * bytes. */ + rd_len = ((rv + 127) / 128) * 128; + + npos += rd_len; + + /* Write new file pointer into FCB */ + + redir_put_fcb_pos(fcb, npos); + + if (rv < 0) + { + DBGMSG("Ret: -1\n"); + FCBRET(redir_xlt_err()); /* unwritten extent */ + } + + /* if not multiple of 128 bytes, pad sector with 0x1A */ + for (n = rv; n < rd_len; n++) dma[n] = 0x1A; + + /* Less was read in than asked for. Report the number of 128-byte + * records that _were_ read in. + */ + + if (rd_len < redir_rec_len) /* eof */ + { + /* Pack from the size actually read up to the size we claim + * to have read */ + rd_len = rd_len * 2; /* rd_len already sector * 128, so * 2 to move to High byte */ + FCBRET(rd_len | 1); /* eof */ + } + + DBGMSGV("Ret: 0 (bytes read=%d)\n", rv); + FCBRET(0); +} + +cpm_word fcb_write(cpm_byte* fcb, cpm_byte* dma) +{ + int handle; + int rv; + long npos, len; + + FCBENT(fcb); + + if ((handle = redir_verify_fcb(fcb)) < 0) FCBRET(9); /* Invalid FCB */ + + /* Software write-protection */ + if (redir_ro_fcb(fcb)) FCBRET(0x02FF); + + /* Check for a seek */ + npos = redir_get_fcb_pos(fcb); + zxlseek(handle, npos, SEEK_SET); + + DBGMSGV(" (to %lX)\n", zxlseek(handle, 0, SEEK_CUR)); + +#ifdef _WIN32 + { + BOOL b; + DBGMSGV("write file #%i @ 0x%X, %i bytes\n", handle, dma - RAM, redir_rec_len); + b = WriteFile((HANDLE)handle, dma, redir_rec_len, (unsigned long*)(&rv), NULL); + if (!b) + { + DBGMSGV("failed to write file #%i (Error=%lu): %s\n", handle, GetLastError(), GetErrorStr(GetLastError())); + rv = -1; + } + } +#else + DBGMSGV("write file #%i @ 0x%X, %i bytes\n", handle, dma - RAM, redir_rec_len); + rv = write(handle, dma, redir_rec_len); + if (rv == -1) + DBGMSGV("failed to write file #%i (errno=%lu): %s\n", handle, errno, strerror(errno)); +#endif + npos += redir_rec_len; + + redir_put_fcb_pos(fcb, npos); + + /* Update the file length */ + len = redir_rd32(fcb + LENGTH_OFFSET); + if (len < npos) redir_wr32(fcb + LENGTH_OFFSET, npos); + + if (rv < 0) FCBRET(redir_xlt_err()); /* error */ + if (rv < redir_rec_len) FCBRET(1); /* disk full */ + FCBRET(0); +} + +cpm_word fcb_creat(cpm_byte* fcb, cpm_byte* dma) +{ + char fname[CPM_MAXPATH]; + int handle; + + FCBENT(fcb); + + releaseFCB(fcb); /* release existing fcb usage */ + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) FCBRET(0x09FF); + DBGMSGV("fcb_creat('%s')\n", fname); + + /* Software write-protection */ + if (redir_ro_fcb(fcb)) FCBRET(0x02FF); + + redir_log_fcb(fcb); + + if (fcb[0] & 0x80) + { + handle = mkdir(fname, 0x777); + if (handle) FCBRET(redir_xlt_err()); + FCBRET(0); + } + releaseFile(fname); /* purge any open handles for this file */ + +#ifdef _WIN32 + DBGMSGV("create file '%s'\n", fname); + handle = (int)CreateFile(fname, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == HFILE_ERROR) + { + DBGMSGV("failed to create file '%s' (Error=%lu): %s\n", fname, GetLastError(), GetErrorStr(GetLastError())); + FCBRET(0xFF); + } +#else + DBGMSGV("create file '%s'\n", fname); + handle = open(fname, O_RDWR | O_CREAT | O_EXCL | O_BINARY, + S_IREAD | S_IWRITE); + if (handle < 0) + { + DBGMSGV("failed to create file '%s' (Error=%lu): %s\n", fname, errno, strerror(errno)); + FCBRET(0xFF); + } +#endif + + trackFile(fname, fcb, handle); /* track new file */ + + fcb[MAGIC_OFFSET] = 0xFD; /* "Magic number" */ + fcb[MAGIC_OFFSET + 1] = 0; + redir_wrhandle(fcb + HANDLE_OFFSET, handle); + redir_wr32(fcb + LENGTH_OFFSET, 0); + redir_put_fcb_pos(fcb, 0); /* Seek to 0 */ + +#ifdef __MSDOS__ + if (redir_drdos && (fcb[6] & 0x80)) + { + cpm_word rights = redir_drdos_pwmode(dma[9]); + redir_drdos_put_rights(fname, dma, rights | 0x8000); + } +#endif + + FCBRET(0); +} + +cpm_word fcb_rename(cpm_byte* fcb, cpm_byte* dma) +{ + char ofname[CPM_MAXPATH], nfname[CPM_MAXPATH]; + cpm_byte sdrv, ddrv; + + FCBENT(fcb); + + releaseFCB(fcb); /* release any file associated with the fcb */ + redir_log_fcb(fcb); + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, ofname)) FCBRET(0x09FF); + if (redir_fcb2unix(fcb + 0x10, nfname)) FCBRET(0x09FF); + + /* Software write-protection */ + if (redir_ro_fcb(fcb)) FCBRET(0x02FF); + + if (fcb[0] & 0x80) FCBRET(0xFF); /* Can't rename directories */ + + /* Check we're not trying to rename across drives. Otherwise, it + * might let you do it if the two "drives" are on the same disk. */ + + sdrv = fcb[0] & 0x7F; if (!sdrv) sdrv = redir_cpmdrive + 1; + ddrv = fcb[0x10] & 0x7F; if (!ddrv) ddrv = redir_cpmdrive + 1; + + if (sdrv != ddrv) FCBRET(0xFF); + + DBGMSGV("rename '%s' to '%s'\n", ofname, nfname); + + releaseFile(ofname); /* need ofname and nfname to be closed */ + releaseFile(nfname); + if (rename(ofname, nfname)) + { + if (redir_password_error()) + { + redir_password_append(ofname, dma); + if (!rename(ofname, nfname)) FCBRET(0); + if (redir_password_error()) FCBRET(0x7FF); + } + FCBRET(0xFF); + } + + FCBRET(0); +} + +cpm_word fcb_randrd(cpm_byte* fcb, cpm_byte* dma) +{ + int handle; + int rv, n, rd_len; + dword offs = redir_rd24(fcb + 0x21) * 128; + + FCBENT(fcb); + + if ((handle = redir_verify_fcb(fcb)) < 0) FCBRET(9); /* Invalid FCB */ + + if (zxlseek(handle, offs, SEEK_SET) < 0) FCBRET(6); /* bad record no. */ + +#ifdef _WIN32 + { + BOOL b; + DBGMSGV("read file #%i @ 0x%X, %i bytes\n", handle, dma - RAM, redir_rec_len); + b = ReadFile((HANDLE)handle, dma, redir_rec_len, (unsigned long*)(&rv), NULL); + if (!b) + { + DBGMSGV("failed to read file #%i (Error=%lu): %s\n", handle, GetLastError(), GetErrorStr(GetLastError())); + rv = -1; + } + } +#else + DBGMSGV("read file #%i @ 0x%X, %i bytes\n", handle, dma - RAM, redir_rec_len); + rv = read(handle, dma, redir_rec_len); + if (rv == -1) + DBGMSGV("failed to read file #%i (errno=%lu): %s\n", handle, errno, strerror(errno)); +#endif + + // read() can corrupt buffer area following data read if length + // of data read is less than buffer. Clean it up. + if (rv == -1) + memset(dma, 0x00, redir_rec_len); + else + memset(dma + rv, 0x00, redir_rec_len - rv); + + zxlseek(handle, offs, SEEK_SET); + + redir_put_fcb_pos(fcb, offs); + + if (rv < 0) FCBRET(redir_xlt_err()); /* Error */ + + rd_len = ((rv + 127) / 128) * 128; + + /* PMO: pad partial sector to 128 bytes, even if EOF reached in multi sector read */ + for (n = rv; n < rd_len; n++) dma[n] = 0x1A; /* pad last read to 128 boundary with 0x1A*/ + + if (rd_len < redir_rec_len) /* eof */ + { + rd_len = rd_len * 2; /* rd_len already sector * 128, so * 2 to move to High byte */ + DBGMSGV("Ret: 0x%x\n", rd_len | 1); + FCBRET(rd_len | 1); /* eof */ + } + + FCBRET(0); +} + +cpm_word fcb_randwr(cpm_byte* fcb, cpm_byte* dma) +{ + int handle; + int rv; + dword offs = redir_rd24(fcb + 0x21) * 128; + dword len; + + FCBENT(fcb); + + if ((handle = redir_verify_fcb(fcb)) < 0) FCBRET(9); /* Invalid FCB */ + /* Software write-protection */ + if (redir_ro_fcb(fcb)) FCBRET(0x02FF); + + if (zxlseek(handle, offs, SEEK_SET) < 0) FCBRET(6); /* bad record no. */ + +#ifdef _WIN32 + { + BOOL b; + DBGMSGV("write file #%i @ 0x%X, %i bytes\n", handle, dma - RAM, redir_rec_len); + b = WriteFile((HANDLE)handle, dma, redir_rec_len, (unsigned long*)(&rv), NULL); + if (!b) + { + DBGMSGV("failed to write file #%i (Error=%lu): %s\n", handle, GetLastError(), GetErrorStr(GetLastError())); + rv = -1; + } + } +#else + DBGMSGV("write file #%i @ 0x%X, %i bytes\n", handle, dma - RAM, redir_rec_len); + rv = write(handle, dma, redir_rec_len); + if (rv == -1) + DBGMSGV("failed to write file #%i (errno=%lu): %s\n", handle, errno, strerror(errno)); +#endif + zxlseek(handle, offs, SEEK_SET); + redir_put_fcb_pos(fcb, offs); + + if (rv < 0) FCBRET(redir_xlt_err()); /* Error */ + /* Update the file length */ + len = redir_rd32(fcb + LENGTH_OFFSET); + /* PMO: Bug fix, account for the data just written */ + if (len < offs + rv) { + redir_wr32(fcb + LENGTH_OFFSET, offs + rv); + /* WBW: Not actually a bug. Causes problems w/ GENCPM */ + // fcb[0x20] = (offs + rv) % 256; + } + + if (rv < redir_rec_len) FCBRET(1); /* disk full */ + FCBRET(0); +} + +#ifndef OLD_RANDWZ +/* PMO: + * Under CP/M for random write with zero fill, the zero fill is only done for a newly allocated + * block and not fill from previous end of file + * to implement this fully would require tracking sparse files and filling to block + * boundaries. + * As the default for POSIX/Windows lseek is to effectively zero fill and for modern hard disks + * the additional space used is small compared to capacity, fcb_randwz is the same as fcb_randwr + * Note zero padding to the end of the block will be done automatically as required when data is + * written to later offsets + */ + /* Write random with 0 fill */ +cpm_word fcb_randwz(cpm_byte* fcb, cpm_byte* dma) +{ + FCBENT(fcb); + FCBRET(fcb_randwr(fcb, dma)); +} +#else +/* Write random with 0 fill */ +cpm_word fcb_randwz(cpm_byte* fcb, cpm_byte* dma) +{ + dword offs, len; + int handle, rl, rv; + cpm_byte zerorec[128]; + + FCBENT(fcb); + + if ((handle = redir_verify_fcb(fcb)) < 0) FCBRET(9); /* Invalid FCB */ + /* Software write-protection */ + if (redir_ro_fcb(fcb)) FCBRET(0x02FF); + + offs = redir_rd24(fcb + 0x21) * 128; + len = redir_rd32(fcb + LENGTH_OFFSET); + + redir_wr32(fcb + LENGTH_OFFSET, offs); + + memset(zerorec, 0, sizeof(zerorec)); + + while (len < offs) + { + memset(zerorec, 0, sizeof(zerorec)); + + rl = sizeof(zerorec); + if ((offs - len) < sizeof(zerorec)) rl = offs - len; +#ifdef _WIN32 + { + BOOL b; + DBGMSGV("write file #%i (zeroes), %i bytes\n", handle, redir_rec_len); + b = WriteFile((HANDLE)handle, zerorec, rl, (unsigned long*)(&rv), NULL); + if (!b) + { + DBGMSGV("failed to write file #%i (Error=%lu): %s\n", handle, GetLastError(), GetErrorStr(GetLastError())); + rv = -1; + } + } +#else + DBGMSGV("write file #%i (zeroes), %i bytes\n", handle, redir_rec_len); + rv = write(handle, zerorec, rl); + if (rv == -1) + DBGMSGV("failed to write file #%i (errno=%lu): %s\n", handle, errno, strerror(errno)); +#endif + if (rv >= 0) len += rv; + + if (rv < rl) + { + redir_wr32(fcb + LENGTH_OFFSET, len); + FCBRET(redir_xlt_err()); + } + } + redir_wr32(fcb + LENGTH_OFFSET, offs); + + FCBRET(fcb_randwr(fcb, dma)); +} +#endif + +cpm_word fcb_tell(cpm_byte* fcb) +{ + int handle; + off_t rv; + + FCBENT(fcb); + + if ((handle = redir_verify_fcb(fcb)) < 0) FCBRET(9); /* Invalid FCB */ + + rv = zxlseek(handle, 0, SEEK_CUR); + + if (rv < 0) FCBRET(0xFF); + + rv = rv >> 7; + fcb[0x21] = rv & 0xFF; + fcb[0x22] = (rv >> 8) & 0xFF; + fcb[0x23] = (rv >> 16) & 0xFF; + FCBRET(0); +} + +cpm_word fcb_stat(cpm_byte* fcb) +{ + char fname[CPM_MAXPATH]; + struct stat st; + int rv; + + FCBENT(fcb); + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) FCBRET(0x09FF); + + DBGMSGV("stat '%s', FCB=%0.4X\n", fname, fcb - RAM); + rv = stat(fname, &st); + if (rv < 0) + { + DBGMSGV("failed to stat file '%s' (errno=%lu): %s\n", fname, errno, strerror(errno)); + FCBRET(0xFF); + } + + redir_wr24(fcb + 0x21, (st.st_size + 127) / 128); + + FCBRET(0); +} + +cpm_word fcb_multirec(cpm_byte rc) +{ + if (rc < 1 || rc > 128) return 0xFF; + + redir_rec_multi = rc; + redir_rec_len = 128 * rc; + DBGMSGV("Set read/write to %d bytes\n", redir_rec_len); + return 0; +} + +cpm_word fcb_date(cpm_byte* fcb) +{ + char fname[CPM_MAXPATH]; + struct stat st; + int rv; + + FCBENT(fcb); + + /* as this function will overwrite the fcb info used by ZXCC + * release any file associated with it + */ + releaseFCB(fcb); + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) FCBRET(0x09FF); + + DBGMSGV("stat '%s', FCB=%0.4X\n", fname, fcb - RAM); + rv = stat(fname, &st); + if (rv < 0) + { + DBGMSGV("failed to stat file '%s' (errno=%lu): %s\n", fname, errno, strerror(errno)); + FCBRET(0xFF); + } + + redir_wr32(fcb + 0x18, redir_cpmtime(st.st_atime)); + redir_wr32(fcb + 0x1C, redir_cpmtime(st.st_ctime)); + + fcb[0x0C] = redir_cpm_pwmode(redir_drdos_get_rights(fname)); + FCBRET(0); +} + +cpm_word fcb_trunc(cpm_byte* fcb, cpm_byte* dma) +{ + char fname[CPM_MAXPATH]; + dword offs = redir_rd24(fcb + 0x21) * 128; + + FCBENT(fcb); + + releaseFCB(fcb); /* CP/M requires truncated files be closed */ + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) FCBRET(0x09FF); + + /* Software write-protection */ + if (redir_ro_fcb(fcb)) FCBRET(0x02FF); + + releaseFile(fname); /* after truncate open files are invalid */ + redir_log_fcb(fcb); + + DBGMSGV("truncate file '%s' at %lu\n", fname, offs); + if (truncate(fname, offs)) + { + DBGMSGV("failed to truncate file '%s' (errno=%lu): %s\n", fname, errno, strerror(errno)); + if (redir_password_error()) + { + redir_password_append(fname, dma); + DBGMSGV("truncate file '%s' w/ password at %lu\n", fname, offs); + if (!truncate(fname, offs)) FCBRET(0); + DBGMSGV("failed to truncate file '%s' (errno=%lu): %s\n", fname, errno, strerror(errno)); + } + FCBRET(redir_xlt_err()); + } + FCBRET(0); +} + +cpm_word fcb_sdate(cpm_byte* fcb, cpm_byte* dma) +{ + char fname[CPM_MAXPATH]; + struct utimbuf buf; + + FCBENT(fcb); + + buf.actime = redir_unixtime(dma); + buf.modtime = redir_unixtime(dma + 4); + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) FCBRET(0x09FF); + + /* Software write-protection */ + if (redir_ro_fcb(fcb)) FCBRET(0x02FF); + + redir_log_fcb(fcb); + + DBGMSGV("utime file '%s'\n", fname); + if (utime(fname, &buf)) + { + DBGMSGV("failed to utime file '%s' (Error=%lu): %s\n", fname, errno, strerror(errno)); + if (redir_password_error()) + { + redir_password_append(fname, dma); + DBGMSGV("utime file '%s' w/ password\n", fname); + if (!utime(fname, &buf)) FCBRET(0); + DBGMSGV("failed to utime file '%s' (Error=%lu): %s\n", fname, errno, strerror(errno)); + } + FCBRET(redir_xlt_err()); + } + FCBRET(0); +} + +cpm_word fcb_chmod(cpm_byte* fcb, cpm_byte* dma) +{ + char fname[CPM_MAXPATH]; + struct stat st; + int handle, wlen, omode; + long offs, newoffs; + cpm_byte zero[128]; + + FCBENT(fcb); + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) FCBRET(0x09FF); + + /* Software write-protection */ + if (redir_ro_fcb(fcb)) FCBRET(0x02FF); + + redir_log_fcb(fcb); + + DBGMSGV("stat '%s', FCB=%0.4X\n", fname, fcb - RAM); + if (stat(fname, &st)) + { + DBGMSGV("failed to stat file '%s' (errno=%lu): %s\n", fname, errno, strerror(errno)); + FCBRET(redir_xlt_err()); + } + +#ifdef __MSDOS__ + omode = 0; + if (fcb[9] & 0x80) omode |= 1; + if (fcb[10] & 0x80) omode |= 4; + if (!(fcb[11] & 0x80)) omode |= 0x20; + + if (_chmod(fname, 1, omode) < 0) + { + if (redir_password_error()) + { + redir_password_append(fname, dma); + if (_chmod(fname, 1, omode) >= 0) FCBRET(0); + } + FCBRET(redir_xlt_err()); + } +#elif defined (_WIN32) + omode = 0; + + if (fcb[9] & 0x80) omode |= FILE_ATTRIBUTE_READONLY; + if (fcb[10] & 0x80) omode |= FILE_ATTRIBUTE_SYSTEM; + if (!(fcb[11] & 0x80)) omode |= FILE_ATTRIBUTE_ARCHIVE; + + if (!omode) omode = FILE_ATTRIBUTE_NORMAL; + + { + BOOL b; + DBGMSGV("set attributes file '%s', FCB=%0.4X\n", fname, fcb - RAM); + b = SetFileAttributes(fname, omode); + if (!b) + { + DBGMSGV("failed to set attributes file '%s' (Error=%lu): %s\n", fname, GetLastError(), GetErrorStr(GetLastError())); + FCBRET(redir_xlt_err()); + } + } +#else + omode = st.st_mode; + if (fcb[9] & 0x80) /* Read-only */ + { + st.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + } + else st.st_mode |= S_IWUSR; + + if (omode != st.st_mode) + { + DBGMSGV("chmod '%s', FCB=%0.4X\n", fname, fcb - RAM); + if (chmod(fname, st.st_mode)) + { + DBGMSGV("failed to chmod file '%s' (errno=%lu): %s\n", fname, errno, strerror(errno)); + FCBRET(redir_xlt_err()); + } + } +#endif + + if (fcb[6] & 0x80) /* Set exact size */ + { + DBGMSGV("stat '%s', FCB=%0.4X\n", fname, fcb - RAM); + if (stat(fname, &st)) + { + DBGMSGV("failed to stat file '%s' (errno=%lu): %s\n", fname, errno, strerror(errno)); + FCBRET(redir_xlt_err()); + } + + releaseFCB(fcb); /* cpm required file to be closed so release FCB */ + releaseFile(fname); /* also make sure no other handles open to file */ + DBGMSGV("open '%s', FCB=%0.4X\n", fname, fcb - RAM); + handle = open(fname, O_RDWR | O_BINARY); + if (handle < 0) + { + DBGMSGV("failed to open file '%s' (errno=%lu): %s\n", fname, errno, strerror(errno)); + FCBRET(redir_xlt_err()); + } + DBGMSGV("file '%s' opened at #%i\n", fname, handle); + + newoffs = offs = ((st.st_size + 127) / 128) * 128; + if (fcb[0x20] & 0x7F) + { + newoffs -= fcb[0x20] & 0x7f; + //newoffs -= (0x80 - (fcb[0x20] & 0x7F)); + } + if (newoffs == st.st_size) + { + ; /* Nothing to do! */ + } + else if (newoffs < st.st_size) + { + DBGMSGV("ftruncate file #%i at %lu\n", handle, newoffs); + if (ftruncate(handle, newoffs)) + { + DBGMSGV("failed to ftruncate file #%i (errno=%lu): %s\n", handle, errno, strerror(errno)); + close(handle); + FCBRET(redir_xlt_err()); + } + } + else while (newoffs > st.st_size) + { + wlen = newoffs - st.st_size; + if (wlen > 0x80) wlen = 0x80; + memset(zero, 0x1A, sizeof(zero)); + DBGMSGV("write file #%i (zeroes), %lu bytes\n", handle, wlen); + if (write(handle, zero, wlen) < wlen) + { + DBGMSGV("failed to write file #%i (errno=%lu): %s\n", handle, errno, strerror(errno)); + close(handle); + FCBRET(redir_xlt_err()); + } + st.st_size += wlen; + } + close(handle); + } + FCBRET(0); +} + +cpm_word fcb_setpwd(cpm_byte* fcb, cpm_byte* dma) +{ +#ifdef __MSDOS__ + char fname[CPM_MAXPATH]; + cpm_word rv; + + FCBENT(fcb); + + /* Don't support ambiguous filenames */ + if (redir_fcb2unix(fcb, fname)) FCBRET(0x09FF); + + /* Software write-protection */ + if (redir_ro_fcb(fcb)) FCBRET(0x02FF); + + redir_log_fcb(fcb); + + rv = redir_drdos_put_rights(fname, dma, redir_drdos_pwmode(fcb[0x0c])); + if (rv || !(fcb[0x0c] & 1)) FCBRET(rv); + FCBRET(redir_drdos_put_rights(fname, dma, redir_drdos_pwmode(fcb[0x0c]) | 0x8000)); +#else + FCBRET(0xFF); /* Unix doesn't do this */ +#endif +} + +cpm_word fcb_getlbl(cpm_byte drv) +{ + DBGMSG("fcb_getlbl()\n"); +#ifdef __MSDOS__ + if (redir_drdos) return 0xA1; /* Supports passwords & Update stamps */ + return 0x21; /* Update stamps only */ +#else + return 0x61; /* Update & Access stamps */ +#endif +} + +cpm_word fcb_setlbl(cpm_byte* fcb, cpm_byte* dma) +{ + /* I am not letting CP/M fiddle with the host's FS settings - even if they + * could be altered, which they mostly can't. */ + + return 0x03FF; +} + +cpm_word fcb_defpwd(cpm_byte* pwd) +{ +#ifdef __MSDOS__ + union REGS r; + struct SREGS s; + + if (pwd[0] == 0 || pwd[0] == ' ') + { + redir_passwd[0] = 0; + } + else memcpy(redir_passwd, pwd, 8); + if (redir_drdos) + { + dosmemput(pwd, 8, __tb); + r.w.ax = 0x4454; + r.w.dx = __tb & 0x0F; + s.ds = __tb >> 4; + intdosx(&r, &r, &s); + } + +#endif + return 0; +} diff --git a/Tools/unix/zxcc/cpmredir.h b/Tools/unix/zxcc/cpmredir.h index 2584e239..40810159 100644 --- a/Tools/unix/zxcc/cpmredir.h +++ b/Tools/unix/zxcc/cpmredir.h @@ -1,151 +1,150 @@ -/* - CPMREDIR: CP/M filesystem redirector - Copyright (C) 1998, John Elliott - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - This file holds the public interface to CPMREDIR. -*/ - -#ifndef CPMREDIR_H_INCLUDED - -#define CPMREDIR_H_INCLUDED 16-11-1998 - -/* The "cpm_byte" must be exactly 8 bits. - The "cpm_word" must be exactly 16 bits. */ - -typedef unsigned char cpm_byte; -typedef unsigned short cpm_word; - -/* Maximum length of a directory path */ -#ifdef _POSIX_PATH_MAX - #define CPM_MAXPATH _POSIX_PATH_MAX -#else - #ifdef _MAX_PATH - #define CPM_MAXPATH _MAX_PATH - #else - #define CPM_MAXPATH 260 - #endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* Initialise this library. Call this function first. - * - * Returns 0 if failed to initialise. - */ -int fcb_init(void); - -/* Deinitialise the library. */ - -void fcb_deinit(void); - -/* Translate a name from the host FS to a CP/M name. This will (if necessary) - * create a mapping between a CP/M drive and a host directory path. - * - * CP/M drives A: to O: can be mapped in this way. P: is always the current - * drive. - * - */ - -void xlt_name(char *localname, char *cpmname); - -/* It is sometimes convenient to set some fixed mappings. This will create - * a mapping for a given directory. - * Pass drive = -1 for "first available", or 0-15 for A: to P: - * Returns 1 if OK, 0 if requested drive not available. - * - * NB: It is important that the localname should have a trailing - * directory separator! - */ - -int xlt_map(int drive, char *localdir); - -/* - * This revokes a mapping. No check is made whether CP/M has files open - * on the drive or not. - */ - -int xlt_umap(int drive); - -/* Find out if a drive is mapped, and if so to what directory */ - -char *xlt_getcwd(int drive); - - -/* BDOS functions. Eventually this should handle all disc-related BDOS - * functions. - * - * I am assuming that your emulator has the CP/M RAM in its normal address - * space, accessible as a range 0-64k. If this is not the case - * (eg: you are emulating banked memory, or using a segmented architecture) - * you will have to use "copy in and copy out" techniques. The "fcb" area - * must be 36 bytes long; the "dma" area should be 128 * the value set - * in fcb_multirec() [default is 1, so 128 bytes]. - * - */ - -cpm_byte fcb_reset (void); /* 0x0D */ -cpm_word fcb_drive (cpm_byte drv); /* 0x0E */ -cpm_word fcb_open (cpm_byte *fcb, cpm_byte *dma); /* 0x0F */ -cpm_word fcb_close (cpm_byte *fcb); /* 0x10 */ -cpm_word fcb_find1 (cpm_byte *fcb, cpm_byte *dma); /* 0x11 */ -cpm_word fcb_find2 (cpm_byte *fcb, cpm_byte *dma); /* 0x12 */ -cpm_word fcb_unlink(cpm_byte *fcb, cpm_byte *dma); /* 0x13 */ -cpm_word fcb_read (cpm_byte *fcb, cpm_byte *dma); /* 0x14 */ -cpm_word fcb_write (cpm_byte *fcb, cpm_byte *dma); /* 0x15 */ -cpm_word fcb_creat (cpm_byte *fcb, cpm_byte *dma); /* 0x16 */ -cpm_word fcb_rename(cpm_byte *fcb, cpm_byte *dma); /* 0x17 */ -cpm_word fcb_logvec(void); /* 0x18 */ -cpm_byte fcb_getdrv(void); /* 0x19 */ -/* DMA is a parameter to routines, not a separate call */ -cpm_word fcb_getalv(cpm_byte *alv, cpm_word max); /* 0x1B */ -/* Get alloc vector: caller must provide space and say how big it is. */ -cpm_word fcb_rodisk(void); /* 0x1C */ -cpm_word fcb_rovec (void); /* 0x1D */ -cpm_word fcb_chmod (cpm_byte *fcb, cpm_byte *dma); /* 0x1E */ -cpm_word fcb_getdpb(cpm_byte *dpb); /* 0x1F */ -cpm_byte fcb_user (cpm_byte usr); /* 0x20 */ -cpm_word fcb_randrd(cpm_byte *fcb, cpm_byte *dma); /* 0x21 */ -cpm_word fcb_randwr(cpm_byte *fcb, cpm_byte *dma); /* 0x22 */ -cpm_word fcb_stat (cpm_byte *fcb); /* 0x23 */ -cpm_word fcb_tell (cpm_byte *fcb); /* 0x24 */ -cpm_word fcb_resro (cpm_word bitmap); /* 0x25 */ -/* Access Drives and Free Drives are not supported. */ -cpm_word fcb_randwz(cpm_byte *fcb, cpm_byte *dma); /* 0x28 */ -/* Record locking calls not supported (though they could be) */ -cpm_word fcb_multirec(cpm_byte rc); /* 0x2C */ -/* Set hardware error action must be done by caller */ -cpm_word fcb_dfree (cpm_byte drive, cpm_byte *dma);/* 0x2E */ -cpm_word fcb_sync (cpm_byte flag); /* 0x30 */ -cpm_word fcb_purge (void); /* 0x62 */ -cpm_word fcb_trunc (cpm_byte *fcb, cpm_byte *dma); /* 0x63 */ -cpm_word fcb_setlbl(cpm_byte *fcb, cpm_byte *dma); /* 0x64 */ -cpm_word fcb_getlbl(cpm_byte drive); /* 0x65 */ -cpm_word fcb_date (cpm_byte *fcb); /* 0x66 */ -cpm_word fcb_setpwd(cpm_byte *fcb, cpm_byte *dma); /* 0x67 */ -cpm_word fcb_defpwd(cpm_byte *pwd); /* 0x6A */ -cpm_word fcb_sdate (cpm_byte *fcb, cpm_byte *dma); /* 0x74 */ -cpm_word fcb_parse (char *txt, cpm_byte *fcb); /* 0x98 */ - -/* fcb_parse returns length of filename parsed, 0 if EOL, 0xFFFF if error */ -#ifdef __cplusplus -} -#endif - - -#endif /* def CPMREDIR_H_INCLUDED */ +/* + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file holds the public interface to CPMREDIR. +*/ + +#ifndef CPMREDIR_H_INCLUDED + +#define CPMREDIR_H_INCLUDED 16-11-1998 + +/* The "cpm_byte" must be exactly 8 bits. + The "cpm_word" must be exactly 16 bits. */ + +typedef unsigned char cpm_byte; +typedef unsigned short cpm_word; + +/* Maximum length of a directory path */ +#ifdef _POSIX_PATH_MAX + #define CPM_MAXPATH _POSIX_PATH_MAX +#else + #ifdef _MAX_PATH + #define CPM_MAXPATH _MAX_PATH + #else + #define CPM_MAXPATH 260 + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + /* Initialise this library. Call this function first. + * + * Returns 0 if failed to initialise. + */ + int fcb_init(void); + + /* Deinitialise the library. */ + + void fcb_deinit(void); + + /* Translate a name from the host FS to a CP/M name. This will (if necessary) + * create a mapping between a CP/M drive and a host directory path. + * + * CP/M drives A: to O: can be mapped in this way. P: is always the current + * drive. + * + */ + + void xlt_name(char* localname, char* cpmname); + + /* It is sometimes convenient to set some fixed mappings. This will create + * a mapping for a given directory. + * Pass drive = -1 for "first available", or 0-15 for A: to P: + * Returns 1 if OK, 0 if requested drive not available. + * + * NB: It is important that the localname should have a trailing + * directory separator! + */ + + int xlt_map(int drive, char* localdir); + + /* + * This revokes a mapping. No check is made whether CP/M has files open + * on the drive or not. + */ + + int xlt_umap(int drive); + + /* Find out if a drive is mapped, and if so to what directory */ + + char* xlt_getcwd(int drive); + + /* BDOS functions. Eventually this should handle all disc-related BDOS + * functions. + * + * I am assuming that your emulator has the CP/M RAM in its normal address + * space, accessible as a range 0-64k. If this is not the case + * (eg: you are emulating banked memory, or using a segmented architecture) + * you will have to use "copy in and copy out" techniques. The "fcb" area + * must be 36 bytes long; the "dma" area should be 128 * the value set + * in fcb_multirec() [default is 1, so 128 bytes]. + * + */ + + cpm_byte fcb_reset(void); /* 0x0D */ + cpm_word fcb_drive(cpm_byte drv); /* 0x0E */ + cpm_word fcb_open(cpm_byte* fcb, cpm_byte* dma); /* 0x0F */ + cpm_word fcb_close(cpm_byte* fcb); /* 0x10 */ + cpm_word fcb_find1(cpm_byte* fcb, cpm_byte* dma); /* 0x11 */ + cpm_word fcb_find2(cpm_byte* fcb, cpm_byte* dma); /* 0x12 */ + cpm_word fcb_unlink(cpm_byte* fcb, cpm_byte* dma); /* 0x13 */ + cpm_word fcb_read(cpm_byte* fcb, cpm_byte* dma); /* 0x14 */ + cpm_word fcb_write(cpm_byte* fcb, cpm_byte* dma); /* 0x15 */ + cpm_word fcb_creat(cpm_byte* fcb, cpm_byte* dma); /* 0x16 */ + cpm_word fcb_rename(cpm_byte* fcb, cpm_byte* dma); /* 0x17 */ + cpm_word fcb_logvec(void); /* 0x18 */ + cpm_byte fcb_getdrv(void); /* 0x19 */ + /* DMA is a parameter to routines, not a separate call */ + cpm_word fcb_getalv(cpm_byte* alv, cpm_word max); /* 0x1B */ + /* Get alloc vector: caller must provide space and say how big it is. */ + cpm_word fcb_rodisk(void); /* 0x1C */ + cpm_word fcb_rovec(void); /* 0x1D */ + cpm_word fcb_chmod(cpm_byte* fcb, cpm_byte* dma); /* 0x1E */ + cpm_word fcb_getdpb(cpm_byte* dpb); /* 0x1F */ + cpm_byte fcb_user(cpm_byte usr); /* 0x20 */ + cpm_word fcb_randrd(cpm_byte* fcb, cpm_byte* dma); /* 0x21 */ + cpm_word fcb_randwr(cpm_byte* fcb, cpm_byte* dma); /* 0x22 */ + cpm_word fcb_stat(cpm_byte* fcb); /* 0x23 */ + cpm_word fcb_tell(cpm_byte* fcb); /* 0x24 */ + cpm_word fcb_resro(cpm_word bitmap); /* 0x25 */ + /* Access Drives and Free Drives are not supported. */ + cpm_word fcb_randwz(cpm_byte* fcb, cpm_byte* dma); /* 0x28 */ + /* Record locking calls not supported (though they could be) */ + cpm_word fcb_multirec(cpm_byte rc); /* 0x2C */ + /* Set hardware error action must be done by caller */ + cpm_word fcb_dfree(cpm_byte drive, cpm_byte* dma); /* 0x2E */ + cpm_word fcb_sync(cpm_byte flag); /* 0x30 */ + cpm_word fcb_purge(void); /* 0x62 */ + cpm_word fcb_trunc(cpm_byte* fcb, cpm_byte* dma); /* 0x63 */ + cpm_word fcb_setlbl(cpm_byte* fcb, cpm_byte* dma); /* 0x64 */ + cpm_word fcb_getlbl(cpm_byte drive); /* 0x65 */ + cpm_word fcb_date(cpm_byte* fcb); /* 0x66 */ + cpm_word fcb_setpwd(cpm_byte* fcb, cpm_byte* dma); /* 0x67 */ + cpm_word fcb_defpwd(cpm_byte* pwd); /* 0x6A */ + cpm_word fcb_sdate(cpm_byte* fcb, cpm_byte* dma); /* 0x74 */ + cpm_word fcb_parse(char* txt, cpm_byte* fcb); /* 0x98 */ + + /* fcb_parse returns length of filename parsed, 0 if EOL, 0xFFFF if error */ + +#ifdef __cplusplus +} +#endif + +#endif /* def CPMREDIR_H_INCLUDED */ diff --git a/Tools/unix/zxcc/dirent.c b/Tools/unix/zxcc/dirent.c index 87cf85a9..1cdce1cb 100644 --- a/Tools/unix/zxcc/dirent.c +++ b/Tools/unix/zxcc/dirent.c @@ -1,16 +1,15 @@ /* - Implementation of POSIX directory browsing functions and types for Win32. + Implementation of POSIX directory browsing functions and types for Win32. - Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) - History: Created March 1997. Updated June 2003 and July 2012. - Rights: See end of file. + Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) + History: Created March 1997. Updated June 2003 and July 2012. + Rights: See end of file. */ - +#pragma warning(disable : 4996) #include "dirent.h" #include -#include /* _findfirst and _findnext set errno iff they return -1 */ #include #include @@ -19,115 +18,89 @@ extern "C" { #endif -//typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */ -typedef long handle_type; /* C99's intptr_t not sufficiently portable */ - -struct DIR -{ - handle_type handle; /* -1 for failed rewind */ - struct _finddata_t info; - struct dirent result; /* d_name null iff first time */ - char *name; /* null-terminated char string */ -}; - -DIR *opendir(const char *name) -{ - DIR *dir = 0; - - if(name && name[0]) - { - size_t base_length = strlen(name); - const char *all = /* search pattern must end with suitable wildcard */ - strchr("/\\", name[base_length - 1]) ? "*" : "/*"; - - if((dir = (DIR *) malloc(sizeof *dir)) != 0 && - (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0) - { - strcat(strcpy(dir->name, name), all); - - if((dir->handle = - (handle_type) _findfirst(dir->name, &dir->info)) != -1) - { - dir->result.d_name = 0; - } - else /* rollback */ - { - free(dir->name); - free(dir); - dir = 0; - } - } - else /* rollback */ - { - free(dir); - dir = 0; - errno = ENOMEM; - } - } - else - { - errno = EINVAL; - } - - return dir; -} - -int closedir(DIR *dir) -{ - int result = -1; - - if(dir) - { - if(dir->handle != -1) - { - result = _findclose(dir->handle); - } - - free(dir->name); - free(dir); - } - - if(result == -1) /* map all errors to EBADF */ - { - errno = EBADF; - } - - return result; -} - -struct dirent *readdir(DIR *dir) -{ - struct dirent *result = 0; - - if(dir && dir->handle != -1) - { - if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) - { - result = &dir->result; - result->d_name = dir->info.name; - } - } - else - { - errno = EBADF; - } - - return result; -} - -void rewinddir(DIR *dir) -{ - if(dir && dir->handle != -1) - { - _findclose(dir->handle); - dir->handle = (handle_type) _findfirst(dir->name, &dir->info); - dir->result.d_name = 0; - } - else - { - errno = EBADF; - } -} + DIR* opendir(const char* name) { + DIR* dir = 0; + + if (name && name[0]) { + size_t base_length = strlen(name); + const char* all = /* search pattern must end with suitable wildcard */ + strchr("/\\", name[base_length - 1]) ? "*" : "/*"; + + if ((dir = (DIR*)malloc(sizeof * dir)) != 0 && + (dir->name = (char*)malloc(base_length + strlen(all) + 1)) != 0) { + strcat(strcpy(dir->name, name), all); + + if ((dir->handle = + (handle_type)_findfirst(dir->name, &dir->info)) != -1) { + dir->result.d_name = 0; + } + else /* rollback */ + { + free(dir->name); + free(dir); + dir = 0; + } + } + else /* rollback */ + { + free(dir); + dir = 0; + errno = ENOMEM; + } + } + else { + errno = EINVAL; + } + + return dir; + } + + int closedir(DIR* dir) { + int result = -1; + + if (dir) { + if (dir->handle != -1) { + result = _findclose(dir->handle); + } + + free(dir->name); + free(dir); + } + + if (result == -1) /* map all errors to EBADF */ + { + errno = EBADF; + } + + return result; + } + + struct dirent* readdir(DIR* dir) { + struct dirent* result = 0; + + if (dir && dir->handle != -1) { + if (!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1) { + result = &dir->result; + result->d_name = dir->info.name; + } + } + else { + errno = EBADF; + } + + return result; + } + + void rewinddir(DIR* dir) { + if (dir && dir->handle != -1) { + _findclose(dir->handle); + dir->handle = (handle_type)_findfirst(dir->name, &dir->info); + dir->result.d_name = 0; + } + else { + errno = EBADF; + } + } #ifdef __cplusplus } @@ -135,15 +108,15 @@ void rewinddir(DIR *dir) /* - Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved. + Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved. - Permission to use, copy, modify, and distribute this software and its - documentation for any purpose is hereby granted without fee, provided - that this copyright and permissions notice appear in all copies and - derivatives. - - This software is supplied "as is" without express or implied warranty. + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose is hereby granted without fee, provided + that this copyright and permissions notice appear in all copies and + derivatives. - But that said, if there are any problems please get in touch. + This software is supplied "as is" without express or implied warranty. -*/ + But that said, if there are any problems please get in touch. + +*/ \ No newline at end of file diff --git a/Tools/unix/zxcc/dirent.h b/Tools/unix/zxcc/dirent.h index bbbfce52..7db1a3ee 100644 --- a/Tools/unix/zxcc/dirent.h +++ b/Tools/unix/zxcc/dirent.h @@ -8,40 +8,48 @@ Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com) History: Created March 1997. Updated June 2003. Rights: See end of file. - + */ +#include /* _findfirst and _findnext set errno iff they return -1 */ + #ifdef __cplusplus extern "C" { #endif -typedef struct DIR DIR; + struct dirent { + char *d_name; + }; -struct dirent -{ - char *d_name; -}; + typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */ -DIR *opendir(const char *); -int closedir(DIR *); -struct dirent *readdir(DIR *); -void rewinddir(DIR *); + typedef struct { + handle_type handle; /* -1 for failed rewind */ + struct _finddata_t info; + struct dirent result; /* d_name null iff first time */ + char *name; /* null-terminated char string */ + } DIR; -/* + DIR *opendir(const char *); + int closedir(DIR *); + struct dirent *readdir(DIR *); + void rewinddir(DIR *); - Copyright Kevlin Henney, 1997, 2003. All rights reserved. + /* - Permission to use, copy, modify, and distribute this software and its - documentation for any purpose is hereby granted without fee, provided - that this copyright and permissions notice appear in all copies and - derivatives. - - This software is supplied "as is" without express or implied warranty. + Copyright Kevlin Henney, 1997, 2003. All rights reserved. - But that said, if there are any problems please get in touch. + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose is hereby granted without fee, provided + that this copyright and permissions notice appear in all copies and + derivatives. -*/ + This software is supplied "as is" without express or implied warranty. + + But that said, if there are any problems please get in touch. + + */ #ifdef __cplusplus } diff --git a/Tools/unix/zxcc/drdos.c b/Tools/unix/zxcc/drdos.c index baf7a00b..f62a3a5c 100644 --- a/Tools/unix/zxcc/drdos.c +++ b/Tools/unix/zxcc/drdos.c @@ -1,23 +1,23 @@ /* - CPMREDIR: CP/M filesystem redirector - Copyright (C) 1998, John Elliott + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - This file holds DRDOS-specific password code. + This file holds DRDOS-specific password code. */ #include "cpmint.h" @@ -46,24 +46,24 @@ cpm_byte redir_cpm_pwmode(cpm_word w) #ifdef __MSDOS__ #ifdef __GO32__ /* The GO32 extender doesn't understand DRDOS password - * functions, so these are done with __dpmi_int() rather - * than intdos() */ + * functions, so these are done with __dpmi_int() rather + * than intdos() */ -cpm_word redir_drdos_get_rights(char *path) +cpm_word redir_drdos_get_rights(char* path) { __dpmi_regs r; - if (!redir_drdos) return 0; + if (!redir_drdos) return 0; redir_Msg("Rights for file %s: \n\r", path); dosmemput(path, strlen(path) + 1, __tb); r.x.ax = 0x4302; r.x.dx = __tb & 0x0F; - r.x.ds = (__tb) >> 4; + r.x.ds = (__tb) >> 4; __dpmi_int(0x21, &r); - + redir_Msg(" %04x \n\r", r.x.cx); if (r.x.flags & 1) return 0; @@ -71,15 +71,15 @@ cpm_word redir_drdos_get_rights(char *path) } -cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights) +cpm_word redir_drdos_put_rights(char* path, cpm_byte* dma, cpm_word rights) { __dpmi_regs r; - if (!redir_drdos) return 0; + if (!redir_drdos) return 0; redir_Msg("Put rights for file %s: %04x %-8.8s %-8.8s\n\r", path, rights, dma, dma + 8); - dosmemput(dma+8, 8, __tb); /* Point DTA at password */ + dosmemput(dma + 8, 8, __tb); /* Point DTA at password */ r.x.ax = 0x1A00; r.x.dx = (__tb & 0x0F); r.x.ds = (__tb) >> 4; @@ -89,11 +89,11 @@ cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights) r.x.ax = 0x4303; /* Set rights */ r.x.cx = rights; r.x.dx = (__tb & 0x0F) + 0x10; - r.x.ds = (__tb) >> 4; + r.x.ds = (__tb) >> 4; __dpmi_int(0x21, &r); - - if (r.x.flags & 1) + + if (r.x.flags & 1) { redir_Msg(" Try 1 failed. Error %04x\n\r", r.x.ax); if (redir_password_error()) @@ -104,7 +104,7 @@ cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights) r.x.ax = 0x4303; /* Set rights */ r.x.cx = rights; r.x.dx = (__tb & 0x0F) + 0x10; - r.x.ds = (__tb) >> 4; + r.x.ds = (__tb) >> 4; __dpmi_int(0x21, &r); if (!r.x.flags & 1) return 0; @@ -117,22 +117,22 @@ cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights) #else /* __GO32__ */ -cpm_word redir_drdos_get_rights(char *path) +cpm_word redir_drdos_get_rights(char* path) { union REGS r; struct SREGS s; - if (!redir_drdos) return 0; + if (!redir_drdos) return 0; redir_Msg("Rights for file %s: \n\r", path); dosmemput(path, strlen(path) + 1, __tb); r.w.ax = 0x4302; r.w.dx = __tb & 0x0F; - s.ds = (__tb) >> 4; + s.ds = (__tb) >> 4; intdosx(&r, &r, &s); - + redir_Msg(" %04x \n\r", r.w.cx); if (r.w.cflag) return 0; @@ -140,30 +140,30 @@ cpm_word redir_drdos_get_rights(char *path) } -cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights) +cpm_word redir_drdos_put_rights(char* path, cpm_byte* dma, cpm_word rights) { union REGS r; struct SREGS s; - if (!redir_drdos) return 0; + if (!redir_drdos) return 0; redir_Msg("Put rights for file %s: %04x\n\r", path, rights); - dosmemput(dma, 8, __tb); /* Point DTA at password */ + dosmemput(dma, 8, __tb); /* Point DTA at password */ r.w.ax = 0x1A00; r.w.dx = (__tb & 0x0F); - s.ds = (__tb) >> 4; + s.ds = (__tb) >> 4; intdosx(&r, &r, &s); dosmemput(path, strlen(path) + 1, __tb + 0x10); r.w.ax = 0x4303; /* Set rights */ r.w.cx = rights; r.w.dx = (__tb & 0x0F) + 0x10; - s.ds = (__tb) >> 4; + s.ds = (__tb) >> 4; intdosx(&r, &r, &s); - - if (r.w.cflag) + + if (r.w.cflag) { redir_Msg(" Try 1 failed. Error %04x \n\r", r.w.ax); if (redir_password_error()) @@ -174,7 +174,7 @@ cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights) r.w.ax = 0x4303; /* Set rights */ r.w.cx = rights; r.w.dx = (__tb & 0x0F) + 0x10; - s.ds = (__tb) >> 4; + s.ds = (__tb) >> 4; intdosx(&r, &r, &s); if (!r.w.cflag) return 0; @@ -198,14 +198,14 @@ cpm_word redir_password_error(void) intdos(&r, &r); - redir_Msg("Last error was: %04x\r\n", r.w.ax); + redir_Msg("Last error was: %04x\n", r.w.ax); if (r.w.ax == 0x56) return 1; /* Bad password */ return 0; } -void redir_password_append(char *s, cpm_byte *dma) +void redir_password_append(char* s, cpm_byte* dma) { int n, m; @@ -223,14 +223,14 @@ void redir_password_append(char *s, cpm_byte *dma) ++m; } s[m] = 0; - + } #else /* __MSDOS__ */ -void redir_password_append(char *s, cpm_byte *dma) {} +void redir_password_append(char* s, cpm_byte* dma) {} cpm_word redir_password_error(void) { return 0; } -cpm_word redir_drdos_put_rights(char *path, cpm_byte *dma, cpm_word rights) -{ return 0; } -cpm_word redir_drdos_get_rights(char *path) { return 0; } +cpm_word redir_drdos_put_rights(char* path, cpm_byte* dma, cpm_word rights) +{ + return 0; +} +cpm_word redir_drdos_get_rights(char* path) { return 0; } #endif /* __MSDOS__ */ - - diff --git a/Tools/unix/zxcc/track.c b/Tools/unix/zxcc/track.c index 20770a8a..7c944b79 100644 --- a/Tools/unix/zxcc/track.c +++ b/Tools/unix/zxcc/track.c @@ -1,7 +1,7 @@ /* CPMREDIR: CP/M filesystem redirector - Optional Open file tracker + Optional Open file tracker Copyright (C) 2021, Mark Ogden This is an addition to the CPMREDIR @@ -22,8 +22,8 @@ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -//#include "cpmint.h" -#include "zxcc.h" + +#include "cpmint.h" /* CP/M does not require that files opened for reading need to be closed, * this has two impacts @@ -37,52 +37,66 @@ * As an example the build of cgen.com using my decompiled sources * the linq phase without tracking left 42 open files * with tracking this was reduced to 2 - * + * * This code keeps track of files that are opened until they are explicitly * closed or the FCB used to open the file is reused, or the file needs to be * renamed or deleted. * To do this it keeps track of the expanded filename, fcb location and allocated * file handle - * + * * Two public functions are used to manage the file list, and are called from * within the bdos emulation - * + * * trackFile(char *fname, void *fcb, int fd) * removes existing tracking with matchin fcb or fd and * if (fname != NULL) - add the info to the head of the open files list * it returns fd - * + * * the function is called in the following circumstances * 1) before closing a file (fname is NULL) - * 2) just after the file has been opened/created. + * 2) just after the file has been opened/created. * 3) to remove association with a given fcb trackFile(NULL, fcb, -1) * * note a helper macro releaseFCB(fcb) can be used for (3) above - * + * * releaseFile(char *fname) * this scans through the list of open files and for each open file * with a matching fname, the file is closed - * + * * the function is called before deleting a file or renaming a file - * - * + * + * * there is a helper function that removes the info from the list - * + * * Notes: * For most applications the tracker could in principle automatically * close existing open files at the start of a new executable invocation. * Unfortunately this does not support the case where there is a scripting * engine intercepting the warm reboots, as it may need to keep the script * source file open. - * + * * Note in theory it would be possible for a CP/M program to open a file * with a given fcb, move the fcb internally and then open another file * with the original fcb. If this happens the FCB tracking could cause * a problem. I am not aware of any real programs that do this. * Please let me know if the situation arises. */ -/* windows needs to use file tracking, for unix/linux it is optional */ + +/* + * The FILETRACKER functionality was implemented primarily because + * MSDOS file interface does not allow opening files in shared mode. + * This port of zxcc deprecates MSDOS and uses WIN32 API calls to handle + * all file I/O. So, this means that FILETRACKER is now optional for + * for Windows as well as Unix. I have found some edge cases where + * FILETRACKER caused a CP/M program to misbehave. Specifically, ZSM4 + * reuses FCBs if files are included and does it such a way that the + * FILETRACKER is unable to solve the problem. For maximum + * compatibility, FILETRACKER may now be left off with the implication + * that a lot of file handles will be left open. + */ + #ifdef FILETRACKER + typedef struct _track { struct _track* next; int handle; @@ -111,10 +125,9 @@ void releaseFile(char* fname) { s = s->next; } - int trackFile(char* fname, void* fcb, int fd) { track_t* s = (track_t*)&openFiles; - Msg("trackFile: \"%s\", FCB=0x%X, Handle=%i\n", fname, (byte *)fcb - RAM, fd); + Msg("trackFile: \"%s\", FCB=0x%X, Handle=%i\n", fname, (byte*)fcb - RAM, fd); while (s->next) { /* find any existing fcb or fd */ if (s->next->fcb == fcb || s->next->handle == fd) { if (s->next->handle != fd) { @@ -147,4 +160,3 @@ void releaseFile(char* fname) {} int trackFile(char* fname, void* fcb, int fd) { return fd; } #endif - diff --git a/Tools/unix/zxcc/util.c b/Tools/unix/zxcc/util.c index bc3dbb6e..e26fa0db 100644 --- a/Tools/unix/zxcc/util.c +++ b/Tools/unix/zxcc/util.c @@ -1,408 +1,484 @@ -/* - - CPMREDIR: CP/M filesystem redirector - Copyright (C) 1998, John Elliott - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - This file holds miscellaneous utility functions. -*/ - -#include "cpmint.h" - - - - - - -/* In debug mode, lseek()s can be traced. */ - -#ifdef DEBUG - -long zxlseek(int fd, long offset, int wh) -{ -#ifdef _WIN32 - long v; - redir_Msg(">SetFilePointer() Handle=%lu, Offset=%lu, Method=%lu\n", fd, offset, wh); - v = SetFilePointer((HANDLE)fd, offset, NULL, wh); - redir_Msg("= 0) return v; - - redir_Msg("lseek fails with errno = %d\n", errno); - if (errno == EBADF) redir_Msg(" (bad file descriptor %d)\n", fd); - if (errno == ESPIPE) redir_Msg(" (file %d is a pipe)\n", fd); - if (errno == EINVAL) redir_Msg(" (bad parameter %d)\n", wh); - - return -1; -#endif -} - -#else - -long zxlseek(int fd, long offset, int wh) -{ - #ifdef _WIN32 - return SetFilePointer((HANDLE)fd, offset, NULL, wh); - #else - return lseek(fd, offset, wh); - #endif -} - - -#endif - -#ifdef DEBUG - -void redir_showfcb(cpm_byte *fd) -{ - int n; - - for (n = 0; n < 32; n++) - { - if (!n || n>= 12) printf("%02x ", fd[n]); - else printf("%c", fd[n] & 0x7F); - } - printf("\r\n"); -} - -#endif - -/* Get the "sequential access" file pointer out of an FCB */ - -long redir_get_fcb_pos(cpm_byte *fcb) -{ - long npos; - - npos = 524288L * fcb[0x0E]; /* S2 */ - npos += 16384L * fcb[0x0C]; /* Extent */ - npos += 128L * fcb[0x20]; /* Record */ - - return npos; -} - -void redir_put_fcb_pos(cpm_byte *fcb, long npos) -{ - fcb[0x20] = (npos / 128) % 128; /* Record */ - fcb[0x0C] = (npos / 16384) % 32; /* Extent */ - fcb[0x0E] = (npos / 524288L) % 64; /* S2 */ -} - - -/* - * find a filename that works. - * note that this is where we handle the case sensitivity/non-case sensitivity - * horror. - * the name that is passed in should be in lower case. - * we'll modify it to the first one that matches - */ -void -swizzle(char *fullpath) -{ - struct stat ss; - char *slash; - DIR *dirp; - struct dirent *dentry; - - /* short circuit if ok */ - if (stat(fullpath, &ss) == 0) { - return; - } - - slash = strrchr(fullpath, '/'); - if (!slash) { - return; - } - *slash = '\0'; - dirp = opendir(fullpath); - *slash = '/'; - while ((dentry = readdir(dirp)) != NULL) { - if (strcasecmp(dentry->d_name, slash + 1) == 0) { - strcpy(slash + 1, dentry->d_name); - break; - } - } - closedir(dirp); -} - -/* Passed a CP/M FCB, convert it to a unix filename. Turn its drive back into - * a path. */ - -int redir_fcb2unix(cpm_byte *fcb, char *fname) -{ - int n, q, drv, ddrv; - char s[2]; - - s[1] = 0; - q = 0; - drv = fcb[0] & 0x7F; - if (drv == '?') drv = 0; - - ddrv = fcb[0] & 0x7F; - if (ddrv < 0x1F) ddrv += '@'; - - redir_Msg("%c:%-8.8s.%-3.3s\n", - ddrv, - fcb + 1, - fcb + 9); - - if (!drv) strcpy(fname, redir_drive_prefix[redir_cpmdrive]); - else strcpy(fname, redir_drive_prefix[drv - 1]); - - for (n = 1; n < 12; n++) - { - s[0] = (fcb[n] & 0x7F); - if (s[0] == '?') q = 1; - if (isupper(s[0])) s[0] = tolower(s[0]); - if (s[0] != ' ') - { - if (n == 9) strcat(fname, "."); - strcat(fname, s); - } - } - return q; -} - -#ifndef EROFS /* Open fails because of read-only FS */ -#define EROFS EACCES -#endif - -int redir_ofile(cpm_byte *fcb, char *s) -{ - int h; - - /* Software write-protection */ -#ifdef _WIN32 - redir_Msg(">CreateFile([OPEN_EXISTING]) Name='%s'\n", s); - h = (int)CreateFile(s, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - redir_Msg("= 0 || (errno != EACCES && errno != EROFS)) - return trackFile(s, fcb, h); - } - h = open(s, O_RDONLY | O_BINARY); - if (h < 0) return -1; - fcb[9] |= 0x80; - #endif -#endif - return trackFile(s, fcb, h); -} - - -/* Extract a file handle from where it was stored in an FCB by fcb_open() - or fcb_creat(). Aborts if the FCB has been tampered with. - - Note: Some programs (like GENCOM) close FCBs they never opened. This causes - the Corrupt FCB message, but no harm seems to ensue. */ - -int redir_verify_fcb(cpm_byte *fcb) -{ - if (fcb[16] != 0xFD || fcb[17] != 0x00) - { - fprintf(stderr,"cpmredir: Corrupt FCB\n"); - return -1; - } - return (int)(redir_rd32(fcb + 18)); - -} - -/* Print a trace message */ - -#ifdef DEBUG - -void redir_Msg(char *s, ...) -{ - va_list ap; - - va_start(ap, s); - fprintf(stderr, "cpmredir trace: "); - vfprintf(stderr, s, ap); - va_end(ap); - fflush(stderr); -} - -#endif - -#define BCD(x) (((x % 10)+16*(x/10)) & 0xFF) - -/* Convert time_t to CP/M day count/hours/minutes */ -dword redir_cpmtime(time_t t) -{ -/* Microsoft compiler warned around the conversion from time_t to long - * as to support dates beyond 2038 time_t is set as a long long - * and for the Microsoft compiler sizeof(long) == 4 and sizeof(long long) == 8 - * for other compilers both have size 8 - * As the result is a dword (unsigned long), the code below is modified to reflect this - */ - - dword d = (dword)((t / 86400) - 2921); /* CP/M day 0 is unix day 2921 */ - dword h = (t % 86400) / 3600; /* Hour, 0-23 */ - dword m = (t % 3600) / 60; /* Minute, 0-59 */ - - return (d | (BCD(h) << 16) | (BCD(m) << 24)); -} - -#undef BCD - -#define UNBCD(x) (((x % 16) + 10 * (x / 16)) & 0xFF) - -time_t redir_unixtime(cpm_byte *c) -{ - time_t t; - cpm_word days; - - days = (c[0] + 256 * c[1]) + 2921; - - t = 60L * UNBCD(c[3]); - t += 3600L * UNBCD(c[2]); - t += 86400L * days; - - return t; -} - -#undef UNBCD - - -/* Functions to access 24-bit & 32-bit words in memory. These are always - little-endian. */ - -void redir_wr24(cpm_byte *addr, dword v) -{ - addr[0] = v & 0xFF; - addr[1] = (v >> 8) & 0xFF; - addr[2] = (v >> 16) & 0xFF; -} - -void redir_wr32(cpm_byte *addr, dword v) -{ - addr[0] = v & 0xFF; - addr[1] = (v >> 8) & 0xFF; - addr[2] = (v >> 16) & 0xFF; - addr[3] = (v >> 24) & 0xFF; -} - -dword redir_rd24(cpm_byte *addr) -{ - register dword rv = addr[2]; - - rv = (rv << 8) | addr[1]; - rv = (rv << 8) | addr[0]; - return rv; -} - - -dword redir_rd32(cpm_byte *addr) -{ - register dword rv = addr[3]; - - rv = (rv << 8) | addr[2]; - rv = (rv << 8) | addr[1]; - rv = (rv << 8) | addr[0]; - return rv; -} - - -void redir_log_drv(cpm_byte drv) -{ - if (!drv) redir_l_drives |= 1; - else redir_l_drives |= (1L << drv); -} - -void redir_log_fcb(cpm_byte *fcb) -{ - int drv = fcb[0] & 0x7F; - - if (drv && drv != '?') redir_log_drv(drv - 1); - else redir_log_drv(redir_cpmdrive); -} - - -int redir_ro_drv(cpm_byte drv) -{ - if (!drv) return redir_ro_drives & 1; - else return redir_ro_drives & (1L << drv); -} - -int redir_ro_fcb(cpm_byte *fcb) -{ - int drv = fcb[0] & 0x7F; - - if (drv && drv != '?') return redir_ro_drv(drv - 1); - else return redir_ro_drv(redir_cpmdrive); -} - - - -cpm_word redir_xlt_err(void) -{ - if (redir_password_error()) return 0x7FF; /* DRDOS pwd error */ - switch(errno) - { - case EISDIR: - case EBADF: return 9; /* Bad FCB */ - case EINVAL: return 0x03FF; /* Readonly file */ - case EPIPE: return 0x01FF; /* Broken pipe */ - case ENOSPC: return 1; /* No space */ - default: return 0xFF; /* Software error */ - } -} - - -#ifdef _WIN32 -/* minimal implementation of truncate */ -int truncate(const char* path, off_t length) -{ - int result; - int fd = open(path, O_BINARY | O_RDWR); - - if (fd < 0) - return -1; - result = ftruncate(fd, length); - return close(fd) == 0 && result == 0 ? 0 : -1; - -} - - -#endif +/* + + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file holds miscellaneous utility functions. +*/ + +#include "cpmint.h" + +#ifdef _WIN32 + +char* GetErrorStr(dword dwErr) +{ + LPVOID lpMsgBuf; + static char ErrStr[256] = ""; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, + dwErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf, + sizeof(ErrStr), NULL); + + strncpy(ErrStr, lpMsgBuf, sizeof(ErrStr)); + + LocalFree(lpMsgBuf); + + return ErrStr; +} + +#endif + +char* whence(int wh) +{ + switch (wh) + { + case SEEK_SET: return("SEEK_SET"); + case SEEK_CUR: return("SEEK_CUR"); + case SEEK_END: return("SEEK_END"); + default: return("SEEK_???"); + } +} + +/* In debug mode, lseek()s can be traced. */ + +long zxlseek(int fd, long offset, int wh) +{ +#ifdef _WIN32 + + long v; + DBGMSGV("seek on file #%i to 0x%lX using %s\n", fd, offset, whence(wh)); + v = SetFilePointer((HANDLE)fd, offset, NULL, wh); + if (v != INVALID_SET_FILE_POINTER) return v; + DBGMSGV("seek failed (Error=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError())); + return -1; + +#else + + DBGMSGV("seek on #%i to 0x%lX using %s\n", fd, offset, whence(wh)); + long v = lseek(fd, offset, wh); + if (v >= 0) return v; + DBGMSGV("seek failed (errno=%lu): %s\n", errno, strerror(errno)); + return -1; + +#endif +} + +#ifdef DEBUG + +void redir_showfcb(cpm_byte* fd) +{ + int n; + + for (n = 0; n < 32; n++) + { + if (!n || n >= 12) printf("%02x ", fd[n]); + else printf("%c", fd[n] & 0x7F); + } + printf("\n"); +} + +#endif + +/* Get the "sequential access" file pointer out of an FCB */ + +long redir_get_fcb_pos(cpm_byte* fcb) +{ + long npos; + + npos = 524288L * fcb[0x0E]; /* S2 */ + npos += 16384L * fcb[0x0C]; /* Extent */ + npos += 128L * fcb[0x20]; /* Record */ + + return npos; +} + +void redir_put_fcb_pos(cpm_byte* fcb, long npos) +{ + fcb[0x20] = (npos / 128) % 128; /* Record */ + fcb[0x0C] = (npos / 16384) % 32; /* Extent */ + fcb[0x0E] = (npos / 524288L) % 64; /* S2 */ +} + +/* + * find a filename that works. + * note that this is where we handle the case sensitivity/non-case sensitivity + * horror. + * the name that is passed in should be in lower case. + * we'll modify it to the first one that matches + */ +void swizzle(char* fullpath) +{ + struct stat ss; + char* slash; + DIR* dirp; + struct dirent* dentry; + + /* short circuit if ok */ + if (stat(fullpath, &ss) == 0) { + return; + } + + slash = strrchr(fullpath, '/'); + if (!slash) { + return; + } + *slash = '\0'; + dirp = opendir(fullpath); + *slash = '/'; + while ((dentry = readdir(dirp)) != NULL) { + if (strcasecmp(dentry->d_name, slash + 1) == 0) { + strcpy(slash + 1, dentry->d_name); + break; + } + } + closedir(dirp); +} + +/* Passed a CP/M FCB, convert it to a unix filename. Turn its drive back into + * a path. */ + +int redir_fcb2unix(cpm_byte* fcb, char* fname) +{ + int n, q, drv, ddrv; + char s[2]; + char buf[256]; + + s[1] = 0; + q = 0; + drv = fcb[0] & 0x7F; + if (drv == '?') drv = 0; + + ddrv = fcb[0] & 0x7F; + if (ddrv < 0x1F) ddrv += '@'; + + if (!drv) strcpy(fname, redir_drive_prefix[redir_cpmdrive]); + else strcpy(fname, redir_drive_prefix[drv - 1]); + + for (n = 1; n < 12; n++) + { + s[0] = (fcb[n] & 0x7F); + if (s[0] == '?') q = 1; + if (isupper(s[0])) s[0] = tolower(s[0]); + if (s[0] != ' ') + { + if (n == 9) strcat(fname, "."); + strcat(fname, s); + } + } + + sprintf(buf, "'%c:%-8.8s.%-3.3s' --> '%s'", ddrv, fcb + 1, fcb + 9, fname); + for (n = 0; buf[n] != '\0'; n++) + { + buf[n] &= 0x7F; + if (buf[n] < ' ') buf[n] = 'x'; + } + + DBGMSGV("%s\n", buf); + + return q; +} + +#ifndef EROFS /* Open fails because of read-only FS */ +#define EROFS EACCES +#endif + +int redir_ofile(cpm_byte* fcb, char* s) +{ + int h; + + /* Software write-protection */ + +#ifdef _WIN32 + + releaseFCB(fcb); + + if (!redir_ro_fcb(fcb)) + { + // Attempt to open existing file with read/write access + DBGMSGV("open existing file '%s' with read/write access\n", s); + h = (int)CreateFile(s, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (h != HFILE_ERROR) + { + DBGMSGV("file '%s' opened R/W as #%i\n", s, h); + return trackFile(s, fcb, h); + } + DBGMSGV("open R/W failed (errno=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError())); + } + + // Attempt to open existing file with read-only access + DBGMSGV("open existing file '%s' with read-only access\n", s); + h = (int)CreateFile(s, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (h == HFILE_ERROR) + { + DBGMSGV("open R/O failed (errno=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError())); + return -1; + } + DBGMSGV("file '%s' opened R/O as #%i\n", s, h); + fcb[9] |= 0x80; + +#elif defined(__MSDOS__) + + int rv; + if (!redir_ro_fcb(fcb)) + { + rv = _dos_open(s, O_RDWR, &h); + if (!rv) return h; + DBGMSGV("Open of %s fails: error %x\n", s, rv); + } + rv = _dos_open(s, O_RDONLY, &h); + if (rv) return -1; + fcb[9] |= 0x80; + +#else + + releaseFCB(fcb); + + swizzle(s); + + if (!redir_ro_fcb(fcb)) + { + // Attempt to open existing file with read/write access + DBGMSGV("open existing file '%s' with read/write access\n", s); + h = open(s, O_RDWR | O_BINARY); + if (h >= 0 || (errno != EACCES && errno != EROFS)) + { + DBGMSGV("file '%s' opened R/W as #%i\n", s, h); + return trackFile(s, fcb, h); + } + DBGMSGV("failed to open R/W (errno=%lu): %s\n", errno, strerror(errno)); + } + + // Attempt to open existing file with read-only access + DBGMSGV("open existing file '%s' with read-only access\n", s); + h = open(s, O_RDONLY | O_BINARY); + if (h < 0) + { + DBGMSGV("failed to open R/O (errno=%lu): %s\n", errno, strerror(errno)); + return -1; + } + DBGMSGV("file '%s' opened R/O as #%i\n", s, h); + fcb[9] |= 0x80; + +#endif + + return trackFile(s, fcb, h); +} + +/* Extract a file handle from where it was stored in an FCB by fcb_open() + or fcb_creat(). Aborts if the FCB has been tampered with. + + Note: Some programs (like GENCOM) close FCBs they never opened. This causes + the Corrupt FCB message, but no harm seems to ensue. */ + +int redir_verify_fcb(cpm_byte* fcb) +{ + if (fcb[16] != 0xFD || fcb[17] != 0x00) + { + fprintf(stderr, "cpmredir: Corrupt FCB\n"); + return -1; + } + return (int)(redir_rd32(fcb + 18)); +} + +/* Print a trace message */ + +#ifdef DEBUG + +void DbgMsg(const char* file, int line, const char* func, char* s, ...) +{ + va_list ap; + + va_start(ap, s); + fprintf(stderr, "%s(%s@%i): ", func, file, line); + vfprintf(stderr, s, ap); + va_end(ap); + fflush(stderr); +} + +#endif + +#define BCD(x) (((x % 10)+16*(x/10)) & 0xFF) + +/* Convert time_t to CP/M day count/hours/minutes */ +dword redir_cpmtime(time_t t) +{ + /* Microsoft compiler warned around the conversion from time_t to long + * as to support dates beyond 2038 time_t is set as a long long + * and for the Microsoft compiler sizeof(long) == 4 and sizeof(long long) == 8 + * for other compilers both have size 8 + * As the result is a dword (unsigned long), the code below is modified to reflect this + */ + + dword d = (dword)((t / 86400) - 2921); /* CP/M day 0 is unix day 2921 */ + dword h = (t % 86400) / 3600; /* Hour, 0-23 */ + dword m = (t % 3600) / 60; /* Minute, 0-59 */ + + return (d | (BCD(h) << 16) | (BCD(m) << 24)); +} + +#undef BCD + +#define UNBCD(x) (((x % 16) + 10 * (x / 16)) & 0xFF) + +time_t redir_unixtime(cpm_byte* c) +{ + time_t t; + cpm_word days; + + days = (c[0] + 256 * c[1]) + 2921; + + t = 60L * UNBCD(c[3]); + t += 3600L * UNBCD(c[2]); + t += 86400L * days; + + return t; +} + +#undef UNBCD + +/* Functions to access 24-bit & 32-bit words in memory. These are always + little-endian. */ + +void redir_wr24(cpm_byte* addr, dword v) +{ + addr[0] = v & 0xFF; + addr[1] = (v >> 8) & 0xFF; + addr[2] = (v >> 16) & 0xFF; +} + +void redir_wr32(cpm_byte* addr, dword v) +{ + addr[0] = v & 0xFF; + addr[1] = (v >> 8) & 0xFF; + addr[2] = (v >> 16) & 0xFF; + addr[3] = (v >> 24) & 0xFF; +} + +dword redir_rd24(cpm_byte* addr) +{ + register dword rv = addr[2]; + + rv = (rv << 8) | addr[1]; + rv = (rv << 8) | addr[0]; + return rv; +} + +dword redir_rd32(cpm_byte* addr) +{ + register dword rv = addr[3]; + + rv = (rv << 8) | addr[2]; + rv = (rv << 8) | addr[1]; + rv = (rv << 8) | addr[0]; + return rv; +} + +void redir_log_drv(cpm_byte drv) +{ + if (!drv) redir_l_drives |= 1; + else redir_l_drives |= (1L << drv); +} + +void redir_log_fcb(cpm_byte* fcb) +{ + int drv = fcb[0] & 0x7F; + + if (drv && drv != '?') redir_log_drv(drv - 1); + else redir_log_drv(redir_cpmdrive); +} + +int redir_ro_drv(cpm_byte drv) +{ + if (!drv) return redir_ro_drives & 1; + else return redir_ro_drives & (1L << drv); +} + +int redir_ro_fcb(cpm_byte* fcb) +{ + int drv = fcb[0] & 0x7F; + + if (drv && drv != '?') return redir_ro_drv(drv - 1); + else return redir_ro_drv(redir_cpmdrive); +} + +cpm_word redir_xlt_err(void) +{ + if (redir_password_error()) return 0x7FF; /* DRDOS pwd error */ + + switch (errno) + { + case EISDIR: + case EBADF: return 9; /* Bad FCB */ + case EINVAL: return 0x03FF; /* Readonly file */ + case EPIPE: return 0x01FF; /* Broken pipe */ + case ENOSPC: return 1; /* No space */ + default: return 0xFF; /* Software error */ + } +} + +#ifdef _WIN32 + +int truncate(const char* path, off_t length) +{ + BOOL bResult; + HANDLE hFile; + DWORD dwOffset; + + DBGMSGV("truncate file %s to %lu\n", path, length); + + hFile = CreateFile(path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + DBGMSGV("truncate failed to open file (Error=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError())); + return -1; + } + + dwOffset = SetFilePointer(hFile, length, NULL, FILE_BEGIN); + if (dwOffset == INVALID_SET_FILE_POINTER) + { + DBGMSGV("truncate failed to open file (Error=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError())); + CloseHandle(hFile); + return -1; + } + + bResult = SetEndOfFile(hFile); + if (!bResult) + { + DBGMSGV("truncate failed to set end of file (Error=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError())); + CloseHandle(hFile); + return -1; + } + + bResult = CloseHandle(hFile); + if (!bResult) + { + DBGMSGV("truncate failed to close file (Error=%lu): %s\n", GetLastError(), GetErrorStr(GetLastError())); + return -1; + } + + DBGMSGV("truncate set file length to %lu\n", dwOffset); + return 0; +} + +#endif diff --git a/Tools/unix/zxcc/xlt.c b/Tools/unix/zxcc/xlt.c index 84b76952..a36f7f50 100644 --- a/Tools/unix/zxcc/xlt.c +++ b/Tools/unix/zxcc/xlt.c @@ -1,24 +1,24 @@ /* - CPMREDIR: CP/M filesystem redirector - Copyright (C) 1998, John Elliott + CPMREDIR: CP/M filesystem redirector + Copyright (C) 1998, John Elliott - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - This file holds functions dealing with name translation; also the - initialisation code. + This file holds functions dealing with name translation; also the + initialisation code. */ #include "cpmint.h" @@ -29,213 +29,209 @@ static char* skipUser(char* localname); static void drdos_init(void) { -/* The DJGPP DOS extender won't detect DRDOS using intdos(), so we have - to use __dpmi_int() instead. */ + /* The DJGPP DOS extender won't detect DRDOS using intdos(), so we have + to use __dpmi_int() instead. */ -#ifdef __GO32__ - __dpmi_regs ir; + #ifdef __GO32__ + __dpmi_regs ir; - ir.x.ax = 0x4452; /* "DR" */ + ir.x.ax = 0x4452; /* "DR" */ - __dpmi_int(0x21, &ir); - if (ir.x.flags & 1) return; /* Not DRDOS */ + __dpmi_int(0x21, &ir); + if (ir.x.flags & 1) return; /* Not DRDOS */ - redir_Msg("DRDOS detected.\r\n"); + redir_Msg("DRDOS detected.\n"); - redir_drdos = 1; + redir_drdos = 1; -#else /* __GO32__ */ + #else /* __GO32__ */ - union REGS ir, or; + union REGS ir, or ; - ir.w.ax = 0x4452; /* "DR" */ + ir.w.ax = 0x4452; /* "DR" */ - intdos(&ir, &or); - if (or.w.cflag) return; /* Not DRDOS */ + intdos(&ir, &or ); + if (or .w.cflag) return; /* Not DRDOS */ - redir_Msg("DRDOS detected.\r\n"); + redir_Msg("DRDOS detected.\n"); - redir_drdos = 1; -#endif /* __GO32__ */ + redir_drdos = 1; + #endif /* __GO32__ */ } -#endif /* __MSDOS__ */ - - +#endif /* __MSDOS__ */ int fcb_init(void) { - int n; + int n; - /* A: to O: free */ - for (n = 0; n < 15; n++) redir_drive_prefix[n][0] = 0; + /* A: to O: free */ + for (n = 0; n < 15; n++) redir_drive_prefix[n][0] = 0; - strcpy(redir_drive_prefix[15], "./"); /* P: is current directory */ + strcpy(redir_drive_prefix[15], "./"); /* P: is current directory */ - /* Log on to P:. It is the only drive at this point which we - * know works. */ - redir_cpmdrive = 15; + /* Log on to P:. It is the only drive at this point which we + * know works. */ + redir_cpmdrive = 15; #ifdef __MSDOS__ - drdos_init(); + drdos_init(); #endif - return 1; + return 1; } /* Deinitialise the library. */ void fcb_deinit(void) { - /* Nothing */ + /* Nothing */ } /* Translate a name from the host FS to a CP/M name. This will (if necessary) - * create a mapping between a CP/M drive and a host directory path. + * create a mapping between a CP/M drive and a host directory path. * * CP/M drives A: to O: can be mapped in this way. P: is always the current - * drive. + * drive. * */ -void xlt_name(char *localname, char *cpmname) +void xlt_name(char* localname, char* cpmname) { - char ibuf[CPM_MAXPATH + 1]; - char nbuf[CPM_MAXPATH + 1]; - char *pname = ibuf; - char *s; - int n; + char ibuf[CPM_MAXPATH + 1]; + char nbuf[CPM_MAXPATH + 1]; + char* pname = ibuf; + char* s; + int n; - sprintf(ibuf, "%-.*s", CPM_MAXPATH, skipUser(localname)); + sprintf(ibuf, "%-.*s", CPM_MAXPATH, skipUser(localname)); - while ((s = strpbrk(pname, DIRSEP))) { /* find the last directory separator allows mixed \ and / in windows */ + while ((s = strpbrk(pname, DIRSEP))) { /* find the last directory separator allows mixed \ and / in windows */ #ifdef _WIN32 - if (*s == '\\') /* convert separators to common format so directory tracking works more efficiently */ - *s = '/'; + if (*s == '\\') /* convert separators to common format so directory tracking works more efficiently */ + *s = '/'; #endif - pname = s + 1; - } - - if (pname == ibuf) { /* No path separators in the name. It is therefore a - local filename, so map it to drive P: */ - strcpy(cpmname, "p:"); - strcat(cpmname, ibuf); - return; - } - - /* catch user specified current drive a,b,c,p or A,B,C,P only, which map to predefined directories */ - if (pname == ibuf + 2 && ibuf[1] == ':' && (s = strchr("aAbBcCpP", ibuf[0]))) { - cpmname[0] = tolower(*s); /* make sure it's lower case */ - strcpy(cpmname + 1, ibuf + 1); - return; - } - - strcpy(nbuf, pname); /* nbuf holds filename component */ - *pname = 0; /* ibuf holds path component */ - - /* See if the path is one of those already mapped to drives */ - - for (n = 0; n < 15; n++) - { - if (redir_drive_prefix[n][0] && !strcmp(ibuf, redir_drive_prefix[n])) - { - sprintf(cpmname,"%c:%s", n + 'a', nbuf); - return; - } - } - - /* It is not, see if another drive can be allocated */ - - for (n = 0; n < 15; n++) if (!redir_drive_prefix[n][0]) - { - strcpy(redir_drive_prefix[n], ibuf); - sprintf(cpmname,"%c:%s", n + 'a', nbuf); - return; - } - - /* No other drive can be allocated */ - - strcpy(cpmname,"p:"); - strcat(cpmname, nbuf); + pname = s + 1; + } + + if (pname == ibuf) { /* No path separators in the name. It is therefore a + local filename, so map it to drive P: */ + strcpy(cpmname, "p:"); + strcat(cpmname, ibuf); + return; + } + + /* catch user specified current drive a,b,c,p or A,B,C,P only, which map to predefined directories */ + if (pname == ibuf + 2 && ibuf[1] == ':' && (s = strchr("aAbBcCpP", ibuf[0]))) { + cpmname[0] = tolower(*s); /* make sure it's lower case */ + strcpy(cpmname + 1, ibuf + 1); + return; + } + + strcpy(nbuf, pname); /* nbuf holds filename component */ + *pname = 0; /* ibuf holds path component */ + + /* See if the path is one of those already mapped to drives */ + + for (n = 0; n < 15; n++) + { + if (redir_drive_prefix[n][0] && !strcmp(ibuf, redir_drive_prefix[n])) + { + sprintf(cpmname, "%c:%s", n + 'a', nbuf); + return; + } + } + + /* It is not, see if another drive can be allocated */ + + for (n = 0; n < 15; n++) if (!redir_drive_prefix[n][0]) + { + strcpy(redir_drive_prefix[n], ibuf); + sprintf(cpmname, "%c:%s", n + 'a', nbuf); + return; + } + + /* No other drive can be allocated */ + + strcpy(cpmname, "p:"); + strcat(cpmname, nbuf); } /* It is sometimes convenient to set some fixed mappings. This will create - * a mapping for a given directory. + * a mapping for a given directory. * Pass drive = -1 for "first available", or 0-15 for A: to P: */ -int xlt_map(int drive, char *localdir) +int xlt_map(int drive, char* localdir) { - int n; - - if (drive == -1) - { - for (n = 0; n < 15; n++) if (!redir_drive_prefix[n][0]) - { - drive = n; - break; - } - if (drive == -1) return 0; /* No space for mappings */ - } - if (redir_drive_prefix[drive][0]) return 0; /* Drive taken */ - - sprintf(redir_drive_prefix[drive], "%-.*s", CPM_MAXPATH, localdir); - return 1; + int n; + + if (drive == -1) + { + for (n = 0; n < 15; n++) if (!redir_drive_prefix[n][0]) + { + drive = n; + break; + } + if (drive == -1) return 0; /* No space for mappings */ + } + if (redir_drive_prefix[drive][0]) return 0; /* Drive taken */ + + sprintf(redir_drive_prefix[drive], "%-.*s", CPM_MAXPATH, localdir); + return 1; } - /* Unmap a drive */ int xlt_umap(int drive) { - if (!redir_drive_prefix[drive][0]) return 0; /* Drive not taken */ - redir_drive_prefix[drive][0] = 0; - return 1; + if (!redir_drive_prefix[drive][0]) return 0; /* Drive not taken */ + redir_drive_prefix[drive][0] = 0; + return 1; } - -char *xlt_getcwd(int drive) +char* xlt_getcwd(int drive) { - if (drive < 0 || drive > 16) return ""; + if (drive < 0 || drive > 16) return ""; - return redir_drive_prefix[drive]; + return redir_drive_prefix[drive]; } /* as zxcc doesn't really support user spaces, remove any user specification - *hitech c supports + * hitech c supports * [[0-9]+[:]][[a-pA-P]:]name[.ext] | [[a-pA-p][[0-9]+]:]name[.ext] * this function also checks that user is no more than 2 digits and user # <= 31 * the hitech fcb checks for : as char 2, 3, or 4 which aligns to this */ static char* skipUser(char* localname) { - char* s; - int user; - int drive; - - if (!localname || !(s = strchr(localname, ':')) || s > localname + 3) - return localname; - s = localname; - if (isdigit(*s)) { - user = *s++ - '0'; - if (isdigit(*s)) { - user = user * 10 + *s++ - '0'; - if (user > 31) /* check sensible user id */ - return localname; - } - if (*s == ':') /* just strip the user id assume rest is a filename */ - return s + 1; - if ('a' <= (drive = tolower(*s)) && drive <= 'p' && s[1] == ':') - return s; /* was form [0-9]+[a-pA-P] so strip user id */ - else - return localname; /* not vaild so don't change */ - } - if ((drive = tolower(*s++)) < 'a' || 'p' < drive || !isdigit(*s)) - return localname; /* not a valid drive prefix or simple drive spec */ - - user = *s++ - '0'; - if (isdigit(*s)) - user = user * 10 + *s++ - '0'; - if (*s != ':' || user > 31) - return localname; - *--s = drive; /* reinsert the drive just before the : */ - return s; + char* s; + int user; + int drive; + + if (!localname || !(s = strchr(localname, ':')) || s > localname + 3) + return localname; + s = localname; + if (isdigit(*s)) { + user = *s++ - '0'; + if (isdigit(*s)) { + user = user * 10 + *s++ - '0'; + if (user > 31) /* check sensible user id */ + return localname; + } + if (*s == ':') /* just strip the user id assume rest is a filename */ + return s + 1; + if ('a' <= (drive = tolower(*s)) && drive <= 'p' && s[1] == ':') + return s; /* was form [0-9]+[a-pA-P] so strip user id */ + else + return localname; /* not vaild so don't change */ + } + if ((drive = tolower(*s++)) < 'a' || 'p' < drive || !isdigit(*s)) + return localname; /* not a valid drive prefix or simple drive spec */ + + user = *s++ - '0'; + if (isdigit(*s)) + user = user * 10 + *s++ - '0'; + if (*s != ':' || user > 31) + return localname; + *--s = drive; /* reinsert the drive just before the : */ + return s; } diff --git a/Tools/unix/zxcc/z80.c b/Tools/unix/zxcc/z80.c index 1b62994c..59af1ed3 100644 --- a/Tools/unix/zxcc/z80.c +++ b/Tools/unix/zxcc/z80.c @@ -123,7 +123,7 @@ void mainloop(word spc, word ssp){ // if (pc == 0x1177) tr = 1; // if (pc == 0x1185) tr = 0; if (tr >= 1) ++id; - if (tr >= 1) printf("%d: PC=%04x %02x AF=%02x:%02x BC=%04x DE=%04x HL=%04x IX=%04x IY=%04x\r\n", + if (tr >= 1) printf("%d: PC=%04x %02x AF=%02x:%02x BC=%04x DE=%04x HL=%04x IX=%04x IY=%04x\n", id, pc, fetch(pc), a,f, bc, de, hl, ix, iy); } */ diff --git a/Tools/unix/zxcc/z80.h b/Tools/unix/zxcc/z80.h index d9e3a547..7634389f 100644 --- a/Tools/unix/zxcc/z80.h +++ b/Tools/unix/zxcc/z80.h @@ -15,16 +15,16 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* [John Elliott, 15 July 2001] - * Copied this file into ZXCC, a CP/M emulator. - * Since ZXCC's memory is a flat 64k space and will never be bank-switched, - * the bank-switching code is removed. - * Since ZXCC has no memory-mapped screen, all the screen management code - * goes as well. - * Since ZXCC doesn't need its speed regulated, all the speed regulation - * code goes as well. - * Since ZXCC doesn't save or load snapshots... OK, you get the idea. - */ + /* [John Elliott, 15 July 2001] + * Copied this file into ZXCC, a CP/M emulator. + * Since ZXCC's memory is a flat 64k space and will never be bank-switched, + * the bank-switching code is removed. + * Since ZXCC has no memory-mapped screen, all the screen management code + * goes as well. + * Since ZXCC doesn't need its speed regulated, all the speed regulation + * code goes as well. + * Since ZXCC doesn't save or load snapshots... OK, you get the idea. + */ #ifdef HAVE_SYS_PARAM_H #include @@ -61,21 +61,21 @@ void drawborder(); #define store(x,y) do { RAM[(x)] = (y); } while(0) -#define store2b(x,hi,lo) do {\ - RAM[(x)]=(lo); \ - RAM[((x+1) & 0xFFFF)]=(hi); } while(0) +#define store2b(x,hi,lo) do { \ + RAM[(x)]=(lo); \ + RAM[((x+1) & 0xFFFF)]=(hi); } while(0) #define store2(x,y) store2b(x,(y)>>8,y) #ifdef __GNUC__ -static void inline storefunc(unsigned short ad,unsigned char b){ - store(ad,b); +static void inline storefunc(unsigned short ad, unsigned char b) { + store(ad, b); } #undef store #define store(x,y) storefunc(x,y) -static void inline store2func(unsigned short ad,unsigned char b1,unsigned char b2){ - store2b(ad,b1,b2); +static void inline store2func(unsigned short ad, unsigned char b1, unsigned char b2) { + store2b(ad, b1, b2); } #undef store2b #define store2b(x,hi,lo) store2func(x,hi,lo) diff --git a/Tools/unix/zxcc/zxbdos.c b/Tools/unix/zxcc/zxbdos.c index 811dcd6d..dab19e58 100644 --- a/Tools/unix/zxcc/zxbdos.c +++ b/Tools/unix/zxcc/zxbdos.c @@ -1,557 +1,555 @@ -#include "zxcc.h" - -#define BDOS_DEF -#include "zxbdos.h" -#include "zxcbdos.h" -#include "zxdbdos.h" - -#ifdef __MSDOS__ -#include -#endif - -#define BCD(x) (((x % 10)+16*(x/10)) & 0xFF) - -/* Convert time_t to CP/M day count/hours/minutes */ -/* there is a duplicate of this code in util.c. -* same modification applied here -*/ -dword cpmtime(time_t t) -{ - dword d = (dword)((t / 86400) - 2921); /* CP/M day 0 is unix day 2921 */ - dword h = (t % 86400) / 3600; /* Hour, 0-23 */ - dword m = (t % 3600) / 60; /* Minute, 0-59 */ - - return (d | (BCD(h) << 16) | (BCD(m) << 24)); -} - - -byte get_time(cpm_word b) -{ - time_t t; - - time(&t); - wr32(b, cpmtime(t)); - - return (BCD(t % 60)); -} - - -/* Functions to access 24-bit & 32-bit words in memory. These are always - little-endian. */ - -void wr24(word addr, dword v) -{ - RAM[addr ] = v & 0xFF; - RAM[addr + 1] = (v >> 8) & 0xFF; - RAM[addr + 2] = (v >> 16) & 0xFF; -} - -void wr32(word addr, dword v) -{ - RAM[addr ] = v & 0xFF; - RAM[addr + 1] = (v >> 8) & 0xFF; - RAM[addr + 2] = (v >> 16) & 0xFF; - RAM[addr + 3] = (v >> 24) & 0xFF; -} - -dword rd24(word addr) -{ - register dword rv = RAM[addr + 2]; - - rv = (rv << 8) | RAM[addr + 1]; - rv = (rv << 8) | RAM[addr]; - return rv; -} - - -dword rd32(word addr) -{ - register dword rv = RAM[addr + 3]; - - rv = (rv << 8) | RAM[addr + 2]; - rv = (rv << 8) | RAM[addr + 1]; - rv = (rv << 8) | RAM[addr]; - return rv; -} - -#define peekw(addr) ( (((word)(RAM[addr + 1])) << 8) | RAM[addr]) - - -/* Get / set the program return code. We store this in 'C' form: 0 for - success, 1-255 for failure. Translate to/from the CP/M form of: - - 0x0000-0xFEFF for success - 0xFF00-0xFFFE for failure - - We also store the actual value so it can be returned - - */ - -word cpm_errcde(word DE) -{ - static word real_err = 0; - - if (DE == 0xFFFF) return real_err; - real_err = DE; - - if (DE == 0xFF00) cpm_error = 1; - else if (DE > 0xFF00) cpm_error = (DE & 0xFF); - else cpm_error = 0; - return 0; -} - - -#ifdef USE_CPMGSX -gsx_byte gsxrd(gsx_word addr) -{ - return RdZ80(addr); -} - -void gsxwr(gsx_word addr, gsx_byte value) -{ - WrZ80(addr, value); -} - -#endif - -#undef bc -#undef de -#undef hl - -void setw(byte *l, byte *h, word w) -{ - *l = (w & 0xFF); - *h = (w >> 8) & 0xFF; -} - -void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, - byte *h, byte *l, word *pc, word *ix, word *iy) -{ - word de = ((*d) << 8) | *e; - word hl = ((*h) << 8) | *l; - byte *pde = &RAM[de]; - byte *pdma = &RAM[cpm_dma]; - word temp; - int retv; - - Msg("BDOS service invoked: C=%02x DE=%04x\n", *c, de); - - switch(*c) - { - case 0: - *pc = 0; - break; - - case 1: /* Get a character */ -#ifdef USE_CPMIO - retv = cpm_bdos_1(); -#else - retv = cin(); -#endif - if (retv < 0) *pc = 0; - setw(l, h, retv); - break; - - case 2: /* Print a character */ -#ifdef USE_CPMIO - if (cpm_bdos_2(*e)) *pc = 0; -#else - cout(*e); -#endif - break; - - case 3: /* No auxin */ - setw(l, h, 0x1A); - break; - - case 4: /* No auxout */ - break; - - case 5: /* No printer */ - break; - - case 6: /* Direct console I/O */ - retv = cpm_bdos_6(*e); - if (retv < 0) *pc = 0; - setw(l, h, retv); - break; - - case 7: /* No auxist */ - case 8: /* No auxost */ - break; - - case 9: /* Print a $-terminated string */ -#ifdef USE_CPMIO - if (cpm_bdos_9((char *)pde)) *pc = 0; -# else - for (temp = 0; RAM[de + temp] != '$'; ++temp) - { - cout(RAM[de + temp]); - } -#endif - break; - - case 0x0A: - bdos_rdline(de, &(*pc)); - break; - - case 0x0B: /* Console status */ - //*l = *h = 0; /* No keys pressed */ - *l = cstat(); - *h = 0; - break; - - case 0x0C: /* Get CP/M version */ - -/* For GENCOM's benefit, claim to be v3.1 */ - - *l = 0x31; /* v3.1 */ - /* *l = 0x22; * v2.2 */ - *h = 0; /* CP/M, no network */ - break; - - case 0x0D: /* Re-log discs */ - fcb_reset(); - break; - - case 0x0E: /* Set default drive */ - setw(l, h, fcb_drive(*e)); - break; - - case 0x0F: /* Open using FCB */ - setw(l, h, x_fcb_open(pde, pdma)); - break; - - case 0x10: /* Close using FCB */ - setw(l, h, fcb_close(pde)); - break; - - case 0x11: /* Find first */ - setw(l, h, fcb_find1(pde, pdma)); - break; - - case 0x12: - setw(l, h, fcb_find2(pde, pdma)); - break; - - case 0x13: /* Delete using FCB */ - setw(l, h, fcb_unlink(pde, pdma)); - break; - - case 0x14: /* Sequential read using FCB */ - setw(l, h, fcb_read(pde, pdma)); - break; - - case 0x15: /* Sequential write using FCB */ - setw(l, h, fcb_write(pde, pdma)); - break; - - case 0x16: /* Create using FCB */ - setw(l, h, fcb_creat(pde, pdma)); - break; - - case 0x17: /* Rename using FCB */ - setw(l, h, fcb_rename(pde, pdma)); - break; - - case 0x18: /* Get login vector */ - setw(l, h, fcb_logvec()); - break; - - case 0x19: /* Get default drive */ - setw(l, h, cpm_drive); - break; - - case 0x1A: /* Set DMA */ - Msg("Set DMA to %04x\n", de); - cpm_dma = de; - break; - - case 0x1B: /* Get alloc vector */ - fcb_getalv(RAM + 0xFF80, 0x40); - setw(l, h, 0xFF80); - break; - - case 0x1C: /* Make disc R/O */ - setw(l, h, fcb_rodisk()); - break; - - case 0x1D: /* Get R/O vector */ - setw(l, h, fcb_rovec()); - break; - - case 0x1E: /* Set attributes */ - setw(l, h, fcb_chmod(pde, pdma)); - break; - - case 0x1F: /* Get DPB */ - fcb_getdpb(RAM + 0xFFC0); - setw(l, h, 0xFFC0); - break; /* Whoops. Missed that 'break'. */ - - case 0x20: /* Get/set uid */ - setw(l, h, fcb_user(*e)); - break; - - case 0x21: /* Read a record */ - setw(l, h, fcb_randrd(pde, pdma)); - break; - - case 0x22: /* Write a record */ - setw(l, h, fcb_randwr(pde, pdma)); - break; - - case 0x23: /* Get file size */ - setw(l, h, x_fcb_stat(pde)); - break; - - case 0x24: /* Get file pointer */ - setw(l, h, fcb_tell(pde)); - break; - - case 0x25: - setw(l, h, fcb_resro(de)); - break; - - /* MP/M drive access functions, not implemented */ - - case 0x28: /* Write with 0 fill */ - setw(l, h, fcb_randwz(pde, pdma)); - break; - - /* MP/M record locking functions, not implemented */ - - case 0x2C: /* Set no. of records to read/write */ - setw(l, h, fcb_multirec(*e)); - break; - - case 0x2D: /* Set error mode */ - err_mode = *e; - break; - - case 0x2E: - setw(l, h, fcb_dfree(*e, pdma)); - break; /* Whoops. Missed that 'break'. */ - - /* 0x2F: Chain */ - - case 0x30: - setw(l, h, fcb_sync(*e)); - break; - - case 0x31: - if (pde[1] == 0xFE) - { - RAM[0xFE9C + *pde] = pde[2]; - RAM[0xFE9D + *pde] = pde[3]; - } - else if (RAM[hl + 1] == 0xFF) - { - RAM[0xFE9C + *pde] = pde[2]; - } - else - { - *l = RAM[0xFE9C + *pde]; - *h = RAM[0xFE9D + *pde]; - } - break; - - case 0x32: - temp = *ix; - *ix = 3 * (pde[0] + 1); - *a = pde[1]; - *c = pde[2]; - *b = pde[3]; - *e = pde[4]; - *d = pde[5]; - *l = pde[6]; - *h = pde[7]; - cpmbios(a,b,c,d,e,f,h,l,pc,ix,iy); - *ix = temp; - break; - - case 0x3C: /* Communicate with RSX */ - *h = 0; *l = 0xFF; /* return error */ - break; - - case 0x62: /* Purge */ - setw(l, h, fcb_purge()); - break; - - case 0x63: /* Truncate file */ - setw(l, h, fcb_trunc(pde, pdma)); - break; - - case 0x64: /* Set label */ - setw(l, h, fcb_setlbl(pde, pdma)); - break; - - case 0x65: /* Get label byte */ - setw(l, h, fcb_getlbl(*e)); - break; - - case 0x66: /* Get file date */ - setw(l, h, fcb_date(pde)); - break; - - case 0x67: /* Set password */ - setw(l, h, fcb_setpwd(pde, pdma)); - break; - - case 0x68: /* Set time of day */ - /* Not advisable to let an emulator play with the clock */ - break; - - case 0x69: /* Get time of day */ - setw(l, h, get_time(de)); - break; - - case 0x6A: /* Set default password */ - setw(l, h, fcb_defpwd(pde)); - break; - - case 0x6B: /* Get serial number */ - memcpy(pde, SERIAL, 6); - break; - - case 0x6C: /* 0.03 set error code */ - setw(l, h, cpm_errcde(de)); - break; - -#ifdef USE_CPMIO - case 0x6D: /* Set/get console mode */ - setw(l, h, cpm_bdos_109(de)); - break; - - case 0x6E: /* Set/get string delimiter */ - setw(l, h, cpm_bdos_110(*e)); - break; - - case 0x6F: /* Send fixed length string to screen */ - if (cpm_bdos_111((char *)RAM + peekw(de), - peekw(de + 2))) - *pc = 0; - break; - - case 0x70: /* Send fixed length string to printer */ - break; - - /* 0x71: Strange PCP/M function */ -#else - case 0x6D: /* Set/get console mode */ - setw(l, h, 0); - break; - -#endif - -#ifdef USE_CPMGSX - case 0x73: /* GSX */ - setw(l, h, gsx80(gsxrd, gsxwr, de)); - break; -#endif - - case 0x74: /* Set date stamp */ - setw(l, h, fcb_sdate(pde, pdma)); - break; - - case 0x98: /* Parse filename */ - setw(l, h, fcb_parse((char *)RAM + peekw(de), - (byte *)RAM + peekw(de + 2))); - break; - - default: -#ifdef USE_CPMIO - cpm_scr_unit(); -#endif -#ifdef USE_CPMGSX - gsx_deinit(); -#endif - - fprintf(stderr,"%s: Unsupported BDOS call %d\n", progname, - (int)(*c)); - dump_regs(stderr,*a,*b,*c,*d,*e,*f,*h,*l,*pc,*ix,*iy); - zxcc_exit(1); - break; - } - - *a = *l; - *b = *h; -} - - - -void cpmbios(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, - byte *h, byte *l, word *pc, word *ix, word *iy) -{ - int func = (((*ix) & 0xFF) / 3) - 1; - - Msg("BIOS service invoked: func=%02x\n", func); - - switch(func) /* BIOS function */ - { - case 1: - zxcc_exit(zxcc_term()); /* Program termination */ - break; - - case 2: /* CONST */ -#ifdef USE_CPMIO - *a = cpm_const(); -#else - *a = cpm_bdos_6(0xFE); -#endif - break; - - case 3: /* CONIN */ -#ifdef USE_CPMIO - *a = cpm_conin(); -#else - *a = cpm_bdos_6(0xFD); -#endif - break; - - case 4: /* CONOUT */ -#ifdef USE_CPMIO - cpm_conout(*c); -#else - cpm_bdos_6(*c); -#endif - break; - - case 20: /* DEVTBL */ - setw(l, h, 0xFFFF); - break; - - case 22: /* DRVTBL */ - setw(l, h, 0xFFFF); - break; - - case 26: /* TIME */ - RAM[0xFEF8] = get_time(0xFEF4); - break; - - case 30: /* USERF!!! */ -#ifdef USE_CPMIO - cpm_bdos_110('$'); - cpm_bdos_9("This program has attempted to call USERF, " - "which is not implemented\r\n$"); -#else - printf("This program has attempted to call USERF, which " - "is not implemented.\n"); -#endif - zxcc_term(); - zxcc_exit(1); - break; - - default: -#ifdef USE_CPMIO - cpm_scr_unit(); -#endif -#ifdef USE_CPMGSX - gsx_deinit(); -#endif - - fprintf(stderr,"%s: Unsupported BIOS call %d\n", progname, func); - dump_regs(stderr,*a,*b,*c,*d,*e,*f,*h,*l,*pc,*ix,*iy); - zxcc_exit(1); - } -} +#include "zxcc.h" + +#define BDOS_DEF +#include "zxbdos.h" +#include "zxcbdos.h" +#include "zxdbdos.h" + +#ifdef __MSDOS__ +#include +#endif + +#define BCD(x) (((x % 10)+16*(x/10)) & 0xFF) + +/* Convert time_t to CP/M day count/hours/minutes */ +/* there is a duplicate of this code in util.c. +* same modification applied here +*/ +dword cpmtime(time_t t) +{ + dword d = (dword)((t / 86400) - 2921); /* CP/M day 0 is unix day 2921 */ + dword h = (t % 86400) / 3600; /* Hour, 0-23 */ + dword m = (t % 3600) / 60; /* Minute, 0-59 */ + + return (d | (BCD(h) << 16) | (BCD(m) << 24)); +} + + +byte get_time(cpm_word b) +{ + time_t t; + + time(&t); + wr32(b, cpmtime(t)); + + return (BCD(t % 60)); +} + + +/* Functions to access 24-bit & 32-bit words in memory. These are always + little-endian. */ + +void wr24(word addr, dword v) +{ + RAM[addr] = v & 0xFF; + RAM[addr + 1] = (v >> 8) & 0xFF; + RAM[addr + 2] = (v >> 16) & 0xFF; +} + +void wr32(word addr, dword v) +{ + RAM[addr] = v & 0xFF; + RAM[addr + 1] = (v >> 8) & 0xFF; + RAM[addr + 2] = (v >> 16) & 0xFF; + RAM[addr + 3] = (v >> 24) & 0xFF; +} + +dword rd24(word addr) +{ + register dword rv = RAM[addr + 2]; + + rv = (rv << 8) | RAM[addr + 1]; + rv = (rv << 8) | RAM[addr]; + return rv; +} + + +dword rd32(word addr) +{ + register dword rv = RAM[addr + 3]; + + rv = (rv << 8) | RAM[addr + 2]; + rv = (rv << 8) | RAM[addr + 1]; + rv = (rv << 8) | RAM[addr]; + return rv; +} + +#define peekw(addr) ( (((word)(RAM[addr + 1])) << 8) | RAM[addr]) + + +/* Get / set the program return code. We store this in 'C' form: 0 for + success, 1-255 for failure. Translate to/from the CP/M form of: + + 0x0000-0xFEFF for success + 0xFF00-0xFFFE for failure + + We also store the actual value so it can be returned + + */ + +word cpm_errcde(word DE) +{ + static word real_err = 0; + + if (DE == 0xFFFF) return real_err; + real_err = DE; + + if (DE == 0xFF00) cpm_error = 1; + else if (DE > 0xFF00) cpm_error = (DE & 0xFF); + else cpm_error = 0; + return 0; +} + + +#ifdef USE_CPMGSX +gsx_byte gsxrd(gsx_word addr) +{ + return RdZ80(addr); +} + +void gsxwr(gsx_word addr, gsx_byte value) +{ + WrZ80(addr, value); +} + +#endif + +#undef bc +#undef de +#undef hl + +void setw(byte* l, byte* h, word w) +{ + *l = (w & 0xFF); + *h = (w >> 8) & 0xFF; +} + +void cpmbdos(byte* a, byte* b, byte* c, byte* d, byte* e, byte* f, + byte* h, byte* l, word* pc, word* ix, word* iy) +{ + word de = ((*d) << 8) | *e; + word hl = ((*h) << 8) | *l; + byte* pde = &RAM[de]; + byte* pdma = &RAM[cpm_dma]; + word temp; + int retv; + + DBGMSGV("BDOS service invoked: C=0x%02X DE=0x%04X\n", *c, de); + + switch (*c) + { + case 0: + *pc = 0; + break; + + case 1: /* Get a character */ +#ifdef USE_CPMIO + retv = cpm_bdos_1(); +#else + retv = cin(); +#endif + if (retv < 0) *pc = 0; + setw(l, h, retv); + break; + + case 2: /* Print a character */ +#ifdef USE_CPMIO + if (cpm_bdos_2(*e)) *pc = 0; +#else + cout(*e); +#endif + break; + + case 3: /* No auxin */ + setw(l, h, 0x1A); + break; + + case 4: /* No auxout */ + break; + + case 5: /* No printer */ + break; + + case 6: /* Direct console I/O */ + retv = cpm_bdos_6(*e); + if (retv < 0) *pc = 0; + setw(l, h, retv); + break; + + case 7: /* No auxist */ + case 8: /* No auxost */ + break; + + case 9: /* Print a $-terminated string */ +#ifdef USE_CPMIO + if (cpm_bdos_9((char*)pde)) *pc = 0; +# else + for (temp = 0; RAM[de + temp] != '$'; ++temp) + { + cout(RAM[de + temp]); + } +#endif + break; + + case 0x0A: + bdos_rdline(de, &(*pc)); + break; + + case 0x0B: /* Console status */ + // *l = *h = 0; /* No keys pressed */ + *l = cstat(); + *h = 0; + break; + + case 0x0C: /* Get CP/M version */ + + /* For GENCOM's benefit, claim to be v3.1 */ + + *l = 0x31; /* v3.1 */ + /* *l = 0x22; * v2.2 */ + *h = 0; /* CP/M, no network */ + break; + + case 0x0D: /* Re-log discs */ + fcb_reset(); + break; + + case 0x0E: /* Set default drive */ + setw(l, h, fcb_drive(*e)); + break; + + case 0x0F: /* Open using FCB */ + setw(l, h, x_fcb_open(pde, pdma)); + break; + + case 0x10: /* Close using FCB */ + setw(l, h, fcb_close(pde)); + break; + + case 0x11: /* Find first */ + setw(l, h, fcb_find1(pde, pdma)); + break; + + case 0x12: + setw(l, h, fcb_find2(pde, pdma)); + break; + + case 0x13: /* Delete using FCB */ + setw(l, h, fcb_unlink(pde, pdma)); + break; + + case 0x14: /* Sequential read using FCB */ + setw(l, h, fcb_read(pde, pdma)); + break; + + case 0x15: /* Sequential write using FCB */ + setw(l, h, fcb_write(pde, pdma)); + break; + + case 0x16: /* Create using FCB */ + setw(l, h, fcb_creat(pde, pdma)); + break; + + case 0x17: /* Rename using FCB */ + setw(l, h, fcb_rename(pde, pdma)); + break; + + case 0x18: /* Get login vector */ + setw(l, h, fcb_logvec()); + break; + + case 0x19: /* Get default drive */ + setw(l, h, cpm_drive); + break; + + case 0x1A: /* Set DMA */ + DBGMSGV("Set DMA to 0x%04X\n", de); + cpm_dma = de; + break; + + case 0x1B: /* Get alloc vector */ + fcb_getalv(RAM + 0xFF80, 0x40); + setw(l, h, 0xFF80); + break; + + case 0x1C: /* Make disc R/O */ + setw(l, h, fcb_rodisk()); + break; + + case 0x1D: /* Get R/O vector */ + setw(l, h, fcb_rovec()); + break; + + case 0x1E: /* Set attributes */ + setw(l, h, fcb_chmod(pde, pdma)); + break; + + case 0x1F: /* Get DPB */ + fcb_getdpb(RAM + 0xFFC0); + setw(l, h, 0xFFC0); + break; + + case 0x20: /* Get/set uid */ + setw(l, h, fcb_user(*e)); + break; + + case 0x21: /* Read a record */ + setw(l, h, fcb_randrd(pde, pdma)); + break; + + case 0x22: /* Write a record */ + setw(l, h, fcb_randwr(pde, pdma)); + break; + + case 0x23: /* Get file size */ + setw(l, h, x_fcb_stat(pde)); + break; + + case 0x24: /* Get file pointer */ + setw(l, h, fcb_tell(pde)); + break; + + case 0x25: + setw(l, h, fcb_resro(de)); + break; + + /* MP/M drive access functions, not implemented */ + + case 0x28: /* Write with 0 fill */ + setw(l, h, fcb_randwz(pde, pdma)); + break; + + /* MP/M record locking functions, not implemented */ + + case 0x2C: /* Set no. of records to read/write */ + setw(l, h, fcb_multirec(*e)); + break; + + case 0x2D: /* Set error mode */ + err_mode = *e; + break; + + case 0x2E: + setw(l, h, fcb_dfree(*e, pdma)); + break; + + /* 0x2F: Chain */ + + case 0x30: + setw(l, h, fcb_sync(*e)); + break; + + case 0x31: + if (pde[1] == 0xFE) + { + RAM[0xFE9C + *pde] = pde[2]; + RAM[0xFE9D + *pde] = pde[3]; + } + else if (RAM[hl + 1] == 0xFF) + { + RAM[0xFE9C + *pde] = pde[2]; + } + else + { + *l = RAM[0xFE9C + *pde]; + *h = RAM[0xFE9D + *pde]; + } + break; + + case 0x32: + temp = *ix; + *ix = 3 * (pde[0] + 1); + *a = pde[1]; + *c = pde[2]; + *b = pde[3]; + *e = pde[4]; + *d = pde[5]; + *l = pde[6]; + *h = pde[7]; + cpmbios(a, b, c, d, e, f, h, l, pc, ix, iy); + *ix = temp; + break; + + case 0x3C: /* Communicate with RSX */ + *h = 0; *l = 0xFF; /* return error */ + break; + + case 0x62: /* Purge */ + setw(l, h, fcb_purge()); + break; + + case 0x63: /* Truncate file */ + setw(l, h, fcb_trunc(pde, pdma)); + break; + + case 0x64: /* Set label */ + setw(l, h, fcb_setlbl(pde, pdma)); + break; + + case 0x65: /* Get label byte */ + setw(l, h, fcb_getlbl(*e)); + break; + + case 0x66: /* Get file date */ + setw(l, h, fcb_date(pde)); + break; + + case 0x67: /* Set password */ + setw(l, h, fcb_setpwd(pde, pdma)); + break; + + case 0x68: /* Set time of day */ + /* Not advisable to let an emulator play with the clock */ + break; + + case 0x69: /* Get time of day */ + setw(l, h, get_time(de)); + break; + + case 0x6A: /* Set default password */ + setw(l, h, fcb_defpwd(pde)); + break; + + case 0x6B: /* Get serial number */ + memcpy(pde, SERIAL, 6); + break; + + case 0x6C: /* 0.03 set error code */ + setw(l, h, cpm_errcde(de)); + break; + +#ifdef USE_CPMIO + case 0x6D: /* Set/get console mode */ + setw(l, h, cpm_bdos_109(de)); + break; + + case 0x6E: /* Set/get string delimiter */ + setw(l, h, cpm_bdos_110(*e)); + break; + + case 0x6F: /* Send fixed length string to screen */ + if (cpm_bdos_111((char*)RAM + peekw(de), + peekw(de + 2))) + *pc = 0; + break; + + case 0x70: /* Send fixed length string to printer */ + break; + + /* 0x71: Strange PCP/M function */ +#else + case 0x6D: /* Set/get console mode */ + setw(l, h, 0); + break; + +#endif + +#ifdef USE_CPMGSX + case 0x73: /* GSX */ + setw(l, h, gsx80(gsxrd, gsxwr, de)); + break; +#endif + + case 0x74: /* Set date stamp */ + setw(l, h, fcb_sdate(pde, pdma)); + break; + + case 0x98: /* Parse filename */ + setw(l, h, fcb_parse((char*)RAM + peekw(de), + (byte*)RAM + peekw(de + 2))); + break; + + default: +#ifdef USE_CPMIO + cpm_scr_unit(); +#endif +#ifdef USE_CPMGSX + gsx_deinit(); +#endif + + fprintf(stderr, "%s: Unsupported BDOS call %d\n", progname, + (int)(*c)); + dump_regs(stderr, *a, *b, *c, *d, *e, *f, *h, *l, *pc, *ix, *iy); + zxcc_exit(1); + break; + } + + *a = *l; + *b = *h; +} + +void cpmbios(byte* a, byte* b, byte* c, byte* d, byte* e, byte* f, + byte* h, byte* l, word* pc, word* ix, word* iy) +{ + int func = (((*ix) & 0xFF) / 3) - 1; + + DBGMSGV("BIOS service invoked: func=0x%02X\n", func); + + switch (func) /* BIOS function */ + { + case 1: + zxcc_exit(zxcc_term()); /* Program termination */ + break; + + case 2: /* CONST */ +#ifdef USE_CPMIO + * a = cpm_const(); +#else + * a = cpm_bdos_6(0xFE); +#endif + break; + + case 3: /* CONIN */ +#ifdef USE_CPMIO + * a = cpm_conin(); +#else + * a = cpm_bdos_6(0xFD); +#endif + break; + + case 4: /* CONOUT */ +#ifdef USE_CPMIO + cpm_conout(*c); +#else + cpm_bdos_6(*c); +#endif + break; + + case 20: /* DEVTBL */ + setw(l, h, 0xFFFF); + break; + + case 22: /* DRVTBL */ + setw(l, h, 0xFFFF); + break; + + case 26: /* TIME */ + RAM[0xFEF8] = get_time(0xFEF4); + break; + + case 30: /* USERF!!! */ +#ifdef USE_CPMIO + cpm_bdos_110('$'); + cpm_bdos_9("This program has attempted to call USERF, " + "which is not implemented\n$"); +#else + printf("This program has attempted to call USERF, which " + "is not implemented.\n"); +#endif + zxcc_term(); + zxcc_exit(1); + break; + + default: +#ifdef USE_CPMIO + cpm_scr_unit(); +#endif +#ifdef USE_CPMGSX + gsx_deinit(); +#endif + + fprintf(stderr, "%s: Unsupported BIOS call %d\n", progname, func); + dump_regs(stderr, *a, *b, *c, *d, *e, *f, *h, *l, *pc, *ix, *iy); + zxcc_exit(1); + } +} diff --git a/Tools/unix/zxcc/zxbdos.h b/Tools/unix/zxcc/zxbdos.h index 4111c78e..fd7bebc2 100644 --- a/Tools/unix/zxcc/zxbdos.h +++ b/Tools/unix/zxcc/zxbdos.h @@ -1,50 +1,49 @@ -extern char *progname; -extern char **argv; -extern int argc; - -extern byte cpm_drive; -extern byte cpm_user; - -extern byte RAM[65536]; /* The Z80's address space */ - -extern void Msg(char *s, ...); - -#ifdef BDOS_DEF - -word cpm_dma = 0x80; /* DMA address */ -byte err_mode = 0xFF; -byte rec_multi = 1; -word rec_len = 128; -word ffirst_fcb = 0xFFFF; -byte cpm_error = 0; /* Error code returned by CP/M */ - -#else /* BDOS_DEF */ - -extern word cpm_dma, rec_len, ffirst_fcb; -extern byte err_mode, rec_multi, cpm_error; - -#endif /* BDOS_DEF */ - -#ifndef O_BINARY /* Necessary in DOS, not present in Linux */ -#define O_BINARY 0 -#endif - -typedef unsigned long dword; - -/* Functions in zxbdos.c */ - -void wr24(word addr, dword v); -void wr32(word addr, dword v); -dword rd24(word addr); -dword rd32(word addr); -dword cpmtime(time_t t); -word cpm_errcde(word DE); - -#ifdef USE_CPMGSX -gsx_byte gsxrd(gsx_word addr); -void gsxwr(gsx_word addr, gsx_byte value); -#endif - -void cpmbdos(); -void cpmbios(); - +extern char* progname; +extern char** argv; +extern int argc; + +extern byte cpm_drive; +extern byte cpm_user; + +extern byte RAM[65536]; /* The Z80's address space */ + +extern void Msg(char* s, ...); + +#ifdef BDOS_DEF + +word cpm_dma = 0x80; /* DMA address */ +byte err_mode = 0xFF; +byte rec_multi = 1; +word rec_len = 128; +word ffirst_fcb = 0xFFFF; +byte cpm_error = 0; /* Error code returned by CP/M */ + +#else /* BDOS_DEF */ + +extern word cpm_dma, rec_len, ffirst_fcb; +extern byte err_mode, rec_multi, cpm_error; + +#endif /* BDOS_DEF */ + +#ifndef O_BINARY /* Necessary in DOS, not present in Linux */ + #define O_BINARY 0 +#endif + +typedef unsigned long dword; + +/* Functions in zxbdos.c */ + +void wr24(word addr, dword v); +void wr32(word addr, dword v); +dword rd24(word addr); +dword rd32(word addr); +dword cpmtime(time_t t); +word cpm_errcde(word DE); + +#ifdef USE_CPMGSX +gsx_byte gsxrd(gsx_word addr); +void gsxwr(gsx_word addr, gsx_byte value); +#endif + +void cpmbdos(); +void cpmbios(); diff --git a/Tools/unix/zxcc/zxcbdos.c b/Tools/unix/zxcc/zxcbdos.c index 46cc8d0b..17466e47 100644 --- a/Tools/unix/zxcc/zxcbdos.c +++ b/Tools/unix/zxcc/zxcbdos.c @@ -1,154 +1,145 @@ -#include "zxcc.h" -#include "zxbdos.h" -#include "zxcbdos.h" - -#ifndef _WIN32 -#include -#endif - -#ifdef _WIN32 -#include -#endif - -/* Line input */ -#ifdef USE_CPMIO - - -void bdos_rdline(word line, word *PC) -{ - unsigned char *buf; - - if (!line) line = cpm_dma; - else RAM[line + 1] = 0; - - buf = (unsigned char *)&RAM[line]; - - if (cpm_bdos_10(buf)) *PC = 0; -} - -#else /* def USE_CPMIO */ - -void bdos_rdline(word line, word *PC) -{ - unsigned char c; - unsigned char *p; - int n; - int maxlen; - - if (!line) line = cpm_dma; - maxlen = RAM[line]; - - // fgets causes extra linefeeds, so we invent our own - //fgets((char *)(RAM + line + 2), maxlen, stdin); - - p = (RAM + line + 2); - n = 0; - - while (1) { - c = cin(); - if (c == '\r') - break; - if (c == '\b') { - if (n > 0) { - cout('\b'); - cout(' '); - cout('\b'); - n--; - p--; - } - } - else { - if (n < maxlen) { - cout(c); - *p++ = c; - n++; - } - } - } - - cout('\r'); - *p = '\0'; - - //RAM[line + 1] = strlen((char *)(RAM + line + 2)) - 1; - RAM[line + 1] = (unsigned char)n; - - Msg("Input: [%d] %-*.*s\n", RAM[line + 1], RAM[line + 1], RAM[line +1], (char *)(RAM+line+2)); -} -#endif /* ndef USE_CPMIO */ - -#ifndef USE_CPMIO - -int cpm_bdos_6(byte e) -{ - int c; - - switch(e) { - case 0xFF: - if (cstat()) return cin(); - return 0; - - case 0xFE: - return cstat(); - - case 0xFD: - return cin(); - - default: - cout(e); - break; - } - return 0; -} -#endif - -#ifdef _WIN32 -byte cin() -{ - if (_isatty(STDIN_FILENO)) - return getch(); - else - return getchar(); -} - -void cout(byte c) -{ - if (_isatty(STDOUT_FILENO)) - putch(c); - else - putchar(c); -} - -int cstat() -{ - if (_isatty(STDIN_FILENO)) - return _kbhit() ? 0xFF : 0; - else - return 0xFF; -} - -#else /* def _WIN32 */ - -byte cin() -{ - char c = 0; - - read(STDIN_FILENO, &c, 1); - return c; -} - -void cout(byte c) -{ - write(STDOUT_FILENO, &c, 1); - return; -} - -int cstat() -{ - int i; - - ioctl(STDIN_FILENO, FIONREAD, &i); - if (i > 0) return 0xFF; - return 0; -} - -#endif +#include "zxcc.h" +#include "zxbdos.h" +#include "zxcbdos.h" + +/* Line input */ +#ifdef USE_CPMIO + +void bdos_rdline(word line, word* PC) +{ + unsigned char* buf; + + if (!line) line = cpm_dma; + else RAM[line + 1] = 0; + + buf = (unsigned char*)&RAM[line]; + + if (cpm_bdos_10(buf)) *PC = 0; +} + +#else /* def USE_CPMIO */ + +void bdos_rdline(word line, word* PC) +{ + unsigned char c; + unsigned char* p; + int n; + int maxlen; + + if (!line) line = cpm_dma; + maxlen = RAM[line]; + + // fgets causes extra linefeeds, so we invent our own + // fgets((char *)(RAM + line + 2), maxlen, stdin); + + p = (RAM + line + 2); + n = 0; + + while (1) { + c = cin(); + if (c == '\r') + break; + if (c == '\b') { + if (n > 0) { + cout('\b'); + cout(' '); + cout('\b'); + n--; + p--; + } + } + else { + if (n < maxlen) { + cout(c); + *p++ = c; + n++; + } + } + } + + cout('\r'); + *p = '\0'; + + //RAM[line + 1] = strlen((char *)(RAM + line + 2)) - 1; + RAM[line + 1] = (unsigned char)n; + + DBGMSGV("Input: [%d] %-*.*s\n", RAM[line + 1], RAM[line + 1], RAM[line + 1], (char*)(RAM + line + 2)); +} +#endif /* ndef USE_CPMIO */ + +#ifndef USE_CPMIO + +int cpm_bdos_6(byte e) +{ + int c; + + switch (e) { + case 0xFF: + if (cstat()) return cin(); + return 0; + + case 0xFE: + return cstat(); + + case 0xFD: + return cin(); + + default: + cout(e); + break; + } + return 0; +} +#endif + +#ifdef _WIN32 +byte cin() +{ + if (_isatty(STDIN_FILENO)) + return getch(); + else + return getchar(); +} + +void cout(byte c) +{ + if (_isatty(STDOUT_FILENO)) + putch(c); + else + putchar(c); +} + +int cstat() +{ + if (_isatty(STDIN_FILENO)) + return _kbhit() ? 0xFF : 0; + else + return 0xFF; +} + +#else /* def _WIN32 */ + +byte cin() +{ + char c = 0; + + read(STDIN_FILENO, &c, 1); + return c; +} + +void cout(byte c) +{ + write(STDOUT_FILENO, &c, 1); + return; +} + +int cstat() +{ + int i; + + ioctl(STDIN_FILENO, FIONREAD, &i); + if (i > 0) return 0xFF; + return 0; +} + +#endif diff --git a/Tools/unix/zxcc/zxcbdos.h b/Tools/unix/zxcc/zxcbdos.h index 7f724441..dde4c08d 100644 --- a/Tools/unix/zxcc/zxcbdos.h +++ b/Tools/unix/zxcc/zxcbdos.h @@ -1,4 +1,6 @@ -void bdos_rdline(word line, word *PC); -int cpm_bdos_6(byte e); - - +void bdos_rdline(word line, word* PC); +int cpm_bdos_6(byte e); + +byte cin(void); +void cout(byte); +int cstat(void); diff --git a/Tools/unix/zxcc/zxcc.c b/Tools/unix/zxcc/zxcc.c index 9668243b..1dacc9bd 100644 --- a/Tools/unix/zxcc/zxcc.c +++ b/Tools/unix/zxcc/zxcc.c @@ -1,552 +1,544 @@ -#include "zxcc.h" - -/* Global variables */ - -char *progname; -char **argv; -int argc; - -byte cpm_drive; -byte cpm_user; -extern byte cpm_error; - -char bindir80[CPM_MAXPATH] = ""; -char libdir80[CPM_MAXPATH] = ""; -char incdir80[CPM_MAXPATH] = ""; - -byte RAM[65536]; /* The Z80's address space */ - -void load_comfile(void); /* Forward declaration */ - -static int deinit_term, deinit_gsx; -static void mkpath(char* fullpath, char* path, char* subdir); - -#ifndef _WIN32 -struct termios tc_orig; - -void raw_init(void); -void deinit_raw(void); -#endif - -void dump_regs(FILE *fp, byte a, byte b, byte c, byte d, byte e, byte f, - byte h, byte l, word pc, word ix, word iy) -{ - fprintf(fp, "\tAF=%02x%02x BC=%02x%02x DE=%02x%02x HL=%02x%02x\n" - "\tIX=%04x IY=%04x PC=%04x\n", - a,f,b,c,d,e,h,l,pc,ix,iy); -} - - - -char *parse_to_fcb(char *s, int afcb) -{ - byte *fcb = &RAM[afcb+1]; - - RAM[afcb] = 0; - memset(fcb, ' ', 11); - - while (s[0]==' ') /* skip leading spaces */ - { - s++; - } - while (1) - { - if (s[0] == 0) break; - if (s[0] == ' ') break; - if (s[1] == ':') - { - RAM[afcb] = s[0] - '@'; - if (RAM[afcb] > 16) RAM[afcb] -= 0x20; - s+=2; - continue; - } - if (s[0] == '.') - { - ++s; - fcb = &RAM[afcb+9]; - continue; - } - *fcb = *s; if (islower(*fcb)) *fcb = toupper(*fcb); - ++s; - ++fcb; - if (fcb >= &RAM[afcb+12]) break; - } - return s; -} - - -void Msg(char *s, ...) -{ -#ifdef DEBUG - va_list ap; - - va_start(ap, s); - fprintf(stderr, "%s trace: ", progname); - vfprintf(stderr, s, ap); - fflush(stderr); - va_end(ap); -#endif -} - - -void ed_fe(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, - byte *h, byte *l, word *pc, word *ix, word *iy) -{ - switch(*a) - { - case 0xC0: - cpmbdos(a,b,c,d,e,f,h,l,pc,ix,iy); - break; - - case 0xC1: - load_comfile(); - break; - - case 0xC2: - fprintf(stderr,"%s: Incompatible BIOS.BIN\n", progname); - zxcc_term(); - zxcc_exit(1); - - case 0xC3: - cpmbios(a,b,c,d,e,f,h,l,pc,ix,iy); - break; - - default: - fprintf(stderr, "%s: Z80 encountered invalid trap\n", progname); - dump_regs(stderr,*a,*b,*c,*d,*e,*f,*h,*l,*pc,*ix,*iy); - zxcc_term(); - zxcc_exit(1); - - } -} - - -/* - * load_bios() loads the minimal CP/M BIOS and BDOS. - * - */ - -void load_bios(void) -{ - char dir[CPM_MAXPATH + 1], fname[CPM_MAXPATH + 1]; - char* q; - size_t bios_len; - - FILE* fp = fopen("bios.bin", "rb"); - if (!fp) - { - strcpy(fname, bindir80); - strcat(fname, "bios.bin"); - fp = fopen(fname, "rb"); - } - if (!fp) - { -#ifdef _WIN32 - dir[0] = 0; /* use strncat in case the path is very long */ - strncat(dir, _pgmptr, CPM_MAXPATH - 8); /* copy the executable path */ -#elif defined(__APPLE__) - uint32_t size = CPM_MAXPATH - 8; - _NSGetExecutablePath(dir, &size); -#else - readlink("/proc/self/exe", dir, CPM_MAXPATH - 8); /* allow room for bios.bin */ -#endif - q = strrchr(dir, DIRSEPCH); - *++q = 0; - strcpy(fname, dir); - strcat(fname, "bios.bin"); - fp = fopen(fname, "rb"); - } - if (!fp) - { - fprintf(stderr,"%s: Cannot locate bios.bin\n", progname); - zxcc_term(); - zxcc_exit(1); - } - bios_len = fread(RAM + 0xFE00, 1, 512, fp); - if (bios_len < 1 || ferror(fp)) - { - fclose(fp); - fprintf(stderr,"%s: Cannot load bios.bin\n", progname); - zxcc_term(); - zxcc_exit(1); - } - fclose(fp); - - Msg("Loaded %d bytes of BIOS\n", bios_len); -} -/* - * try_com() attempts to open file, file.com, file.COM, file.cpm and file.CPM - * - */ - -FILE *try_com(char *s) -{ - char fname[CPM_MAXPATH + 1]; - FILE *fp; - - strcpy(fname, s); - fp = fopen(s, "rb"); if (fp) return fp; - sprintf(s,"%s.com", fname); fp = fopen(s, "rb"); if (fp) return fp; - sprintf(s,"%s.COM", fname); fp = fopen(s, "rb"); if (fp) return fp; - sprintf(s,"%s.cpm", fname); fp = fopen(s, "rb"); if (fp) return fp; - sprintf(s,"%s.CPM", fname); fp = fopen(s, "rb"); if (fp) return fp; - - strcpy(s, fname); - return NULL; -} - -/* - * load_comfile() loads the COM file whose name was passed as a parameter. - * - */ - - -void load_comfile(void) -{ - size_t com_len; - char fname[CPM_MAXPATH + 1]; - FILE *fp; - - /* Look in current directory first */ - strcpy(fname, argv[1]); - fp = try_com(fname); - if (!fp) - { - strcpy(fname, bindir80); - strcat(fname, argv[1]); - fp = try_com(fname); - } - if (!fp) - { - fprintf(stderr,"%s: Cannot locate %s, %s.com, %s.COM, %s.cpm _or_ %s.CPM\r\n", - progname, argv[1], argv[1], argv[1], argv[1], argv[1]); - zxcc_term(); - zxcc_exit(1); - } - com_len = fread(RAM + 0x0100, 1, 0xFD00, fp); - if (com_len < 1 || ferror(fp)) - { - fclose(fp); - fprintf(stderr,"%s: Cannot load %s\n", progname, fname); - zxcc_term(); - zxcc_exit(1); - } - fclose(fp); - - memset(RAM + 0x0100 + com_len, 0, 0xFD00 - com_len); - - Msg("Loaded %d bytes from %s\n", com_len, fname); -} - -unsigned int in() { return 0; } -unsigned int out() { return 0; } - - - -/* - * xltname: Convert a unix filepath into a CP/M compatible drive:name form. - * The unix filename must be 8.3 or the CP/M code will reject it. - * - * This uses the library xlt_name to do the work, and then just strcat()s - * the result to the command line. - */ - -void zxcc_xltname(char *name, char *pcmd) -{ - char nbuf[CPM_MAXPATH + 1]; - - xlt_name(pcmd, nbuf); - - strcat(name, nbuf); -} - -/* main() parses the arguments to CP/M form. argv[1] is the name of the CP/M - program to load; the remaining arguments are arguments for the CP/M program. - - main() also loads the vestigial CP/M BIOS and does some sanity checks - on the endianness of the host CPU and the sizes of data types. - */ - -int main(int ac, char **av) -{ - int n; - char *pCmd, *str; - char* tmpenv; - - - argc = ac; - argv = av; -#ifdef __PACIFIC__ /* Pacific C doesn't support argv[0] */ - progname="ZXCC"; -#endif - progname = argv[0]; - - /* DJGPP includes the whole path in the program name, which looks - * untidy... - */ - while ((str = strpbrk(progname, DIRSEP))) - progname = str + 1; - -#ifdef DEBUG - fprintf(stderr, "\n\n"); - Msg("Start of execution: "); - for (n = 0; n < argc; n++) - fprintf(stderr, " %s", argv[n]); - fprintf(stderr, "\n"); -#endif - - if (_isatty(STDIN_FILENO)) - Msg("Using interactive console mode\n"); - else - Msg("Using standard input/ouput mode\n"); - - if (sizeof(int) > 8 || sizeof(byte) != 1 || sizeof(word) != 2) - { - fprintf(stderr,"%s: type lengths incorrect; edit typedefs " - "and recompile.\n", progname); - zxcc_exit(1); - } - - if (argc < 2) - { - fprintf(stderr,"%s: No CP/M program name provided.\n",progname); - zxcc_exit(1); - } - -#ifdef _WIN32 - setmode(STDIN_FILENO, O_BINARY ); - setmode(STDOUT_FILENO, O_BINARY ); -#else - if (_isatty(STDIN_FILENO)) - raw_init(); -#endif - - /* Parse arguments. An argument can be either: - - * preceded by a '-', in which case it is copied in as-is, less the - dash; - * preceded by a '+', in which case it is parsed as a filename and - then concatenated to the previous argument; - * preceded by a '+-', in which case it is concatenated without - parsing; - * not preceded by either, in which case it is parsed as a filename. - - So, the argument string "--a -b c +-=q --x +/dev/null" would be rendered - into CP/M form as "-a b p:c=q -xd:null" */ - - if (!fcb_init()) - { - fprintf(stderr, "Could not initialise CPMREDIR library\n"); - zxcc_exit(1); - } - - /* allow environment variables to override default locations */ - /* two options are supported, explicit overrides for each directory - * (BINDIR80, LIBDIR80, INCDIR80) - * or a common directory prefix override (CPMDIR80) - * the explict override takes precedence - */ - if ((tmpenv = getenv("CPMDIR80"))) { - mkpath(bindir80, tmpenv, BIN80); /* use CPMDIR80 & std subdirs */ - mkpath(libdir80, tmpenv, LIB80); - mkpath(incdir80, tmpenv, INC80); - } - if ((tmpenv = getenv("BINDIR80"))) - mkpath(bindir80, tmpenv, ""); - - if ((tmpenv = getenv("LIBDIR80"))) - mkpath(libdir80, tmpenv, ""); - - if ((tmpenv = getenv("INCDIR80"))) - mkpath(incdir80, tmpenv, ""); - - Msg("BINDIR80=\"%s\"\n", bindir80); - Msg("LIBDIR80=\"%s\"\n", libdir80); - Msg("INCDIR80=\"%s\"\n", incdir80); - - xlt_map(0, bindir80); /* Establish the 3 fixed mappings */ - xlt_map(1, libdir80); - xlt_map(2, incdir80); - - pCmd = (char *)RAM + 0x81; - - for (n = 2; n < argc; n++) - { - if (argv[n][0] == '+' && argv[n][1] == '-') - { - /* Append, no parsing */ - strcat(pCmd, argv[n] + 2); - } - else if (!argv[n][0] || argv[n][0] == '-') - { - /* Append with space; no parsing. */ - strcat(pCmd, " "); - strcat(pCmd, argv[n] + 1); - } - else if (argv[n][0] == '+') - { - zxcc_xltname(pCmd, argv[n]+1); - } - else /* Translate a filename */ - { - strcat(pCmd, " "); - zxcc_xltname(pCmd, argv[n]); - } - - } - pCmd[0x7F] = 0; /* Truncate to fit the buffer */ - RAM[0x80] = (byte)strlen(pCmd); - - str = parse_to_fcb(pCmd, 0x5C); - parse_to_fcb(str, 0x6C); - - // This statement is very useful when creating a client like zxc or zxas - Msg("Command tail is \"%s\"\n", pCmd); - - load_bios(); - - memset(RAM + 0xFE9C, 0, 0x64); /* Zap the SCB */ - RAM[0xFE98] = 0x06; - RAM[0xFE99] = 0xFE; /* FE06, BDOS entry */ - RAM[0xFEA1] = 0x31; /* BDOS 3.1 */ - RAM[0xFEA8] = 0x01; /* UK date format */ - RAM[0xFEAF] = 0x0F; /* CCP drive */ - -#ifdef USE_CPMIO - RAM[0xFEB6] = cpm_term_direct(CPM_TERM_WIDTH, -1); - RAM[0xFEB8] = cpm_term_direct(CPM_TERM_HEIGHT, -1); -#else - RAM[0xFEB6] = 79; - RAM[0xFEB8] = 23; -#endif - RAM[0xFED1] = 0x80; /* Buffer area */ - RAM[0xFED2] = 0xFF; - RAM[0xFED3] = '$'; - RAM[0xFED6] = 0x9C; - RAM[0xFED7] = 0xFE; /* SCB address */ - RAM[0xFED8] = 0x80; /* DMA address */ - RAM[0xFED9] = 0x00; - RAM[0xFEDA] = 0x0F; /* P: */ - RAM[0xFEE6] = 0x01; /* Multi sector count */ - RAM[0xFEFE] = 0x06; - RAM[0xFEFF] = 0xFE; /* BDOS */ - - cpm_drive = 0x0F; /* Start logged into P: */ - cpm_user = 0; /* and user 0 */ - -#ifdef USE_CPMIO - cpm_scr_init(); deinit_term = 1; -#endif -#ifdef USE_CPMGSX - gsx_init(); deinit_gsx = 1; -#endif - - /* Start the Z80 at 0xFF00, with stack at 0xFE00 */ - mainloop(0xFF00, 0xFE00); - - return zxcc_term(); -} - -void zxcc_exit(int code) -{ -#ifdef USE_CPMIO - if (deinit_term) cpm_scr_unit(); -#endif -#ifdef USE_CPMGSX - if (deinit_gsx) gsx_deinit(); -#endif - exit(code); -} - -int zxcc_term(void) -{ - word n; - -#ifndef _WIN32 - deinit_raw(); -#endif - - //n = RAM[0x81]; /* Get the return code. This is Hi-Tech C */ - //n = (n << 8) | RAM[0x80]; /* specific and fails with other COM files */ - n = 0; - - putchar('\n'); - - if (cpm_error != 0) /* The CP/M "set return code" call was used */ - { /* (my modified Hi-Tech C library uses this */ - n = cpm_error; /* call) */ - } - if (n < 256 || n == 0xFFFF) - { - Msg("Return code %d\n", n); - return n; - } - else return 0; -} - -/* helper function to build full path */ -/* make sure that a / or \ is present at the end of path - * before appending the subdir - */ -static void mkpath(char* fullpath, char* path, char* subdir) { - char* s; - strcpy(fullpath, path); - s = strchr(fullpath, '\0'); - if (*fullpath && !ISDIRSEP(s[-1])) /* make sure we have dir sep */ - *s++ = '/'; - strcpy(s, subdir); -} - -#ifndef _WIN32 - -void raw_init(void) -{ - struct termios tc_raw; - - Msg("Enabling RAW Terminal IO\n"); - - if (tcgetattr(STDIN_FILENO, &tc_orig) == -1) - { - Msg("Failed to enable RAW Terminal IO - tcgetattr() failed\n"); - zxcc_exit(1);; - } - - //tc_raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); - //tc_raw.c_oflag &= ~(OPOST); - //tc_raw.c_cflag |= (CS8); - //tc_raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); - //tc_raw.c_cc[VMIN] = 1; - //tc_raw.c_cc[VTIME] = 0; - - tc_raw.c_iflag &= ~(ICRNL | INPCK | ISTRIP | IXON); - tc_raw.c_oflag &= ~(OPOST); - tc_raw.c_cflag |= (CS8); - tc_raw.c_lflag &= ~(ECHO | ICANON | IEXTEN); - tc_raw.c_cc[VMIN] = 1; - tc_raw.c_cc[VTIME] = 0; - - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tc_raw) == -1) - { - Msg("Failed to enable RAW Terminal IO - tcsetattr() failed\n"); - zxcc_exit(1); - } - - Msg("Enabled RAW Terminal IO\n"); - - return; -} - -void deinit_raw(void) -{ - if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tc_orig) == -1) - { - Msg("Failed to disable RAW Terminal IO - tcsetattr() failed\n"); - return; - } - - Msg("Disabled RAW Terminal IO\n"); - - return; -} - -#endif +#include "zxcc.h" + +/* Global variables */ + +char* progname; +char** argv; +int argc; + +byte cpm_drive; +byte cpm_user; +extern byte cpm_error; + +char bindir80[CPM_MAXPATH] = ""; +char libdir80[CPM_MAXPATH] = ""; +char incdir80[CPM_MAXPATH] = ""; + +#ifndef _WIN32 +struct termios tc_orig; +#endif + +byte RAM[65536]; /* The Z80's address space */ + +void load_comfile(void); /* Forward declaration */ + +static int deinit_term, deinit_gsx; +static void mkpath(char* fullpath, char* path, char* subdir); + +void dump_regs(FILE* fp, byte a, byte b, byte c, byte d, byte e, byte f, + byte h, byte l, word pc, word ix, word iy) +{ + fprintf(fp, "\tAF=%02x%02x BC=%02x%02x DE=%02x%02x HL=%02x%02x\n" + "\tIX=%04x IY=%04x PC=%04x\n", + a, f, b, c, d, e, h, l, pc, ix, iy); +} + +char* parse_to_fcb(char* s, int afcb) +{ + byte* fcb = &RAM[afcb + 1]; + + RAM[afcb] = 0; + memset(fcb, ' ', 11); + + while (s[0] == ' ') /* skip leading spaces */ + { + s++; + } + while (1) + { + if (s[0] == 0) break; + if (s[0] == ' ') break; + if (s[1] == ':') + { + RAM[afcb] = s[0] - '@'; + if (RAM[afcb] > 16) RAM[afcb] -= 0x20; + s += 2; + continue; + } + if (s[0] == '.') + { + ++s; + fcb = &RAM[afcb + 9]; + continue; + } + *fcb = *s; if (islower(*fcb)) *fcb = toupper(*fcb); + ++s; + ++fcb; + if (fcb >= &RAM[afcb + 12]) break; + } + return s; +} + +void ed_fe(byte* a, byte* b, byte* c, byte* d, byte* e, byte* f, + byte* h, byte* l, word* pc, word* ix, word* iy) +{ + switch (*a) + { + case 0xC0: + cpmbdos(a, b, c, d, e, f, h, l, pc, ix, iy); + break; + + case 0xC1: + load_comfile(); + break; + + case 0xC2: + fprintf(stderr, "%s: Incompatible BIOS.BIN\n", progname); + zxcc_term(); + zxcc_exit(1); + + case 0xC3: + cpmbios(a, b, c, d, e, f, h, l, pc, ix, iy); + break; + + default: + fprintf(stderr, "%s: Z80 encountered invalid trap\n", progname); + dump_regs(stderr, *a, *b, *c, *d, *e, *f, *h, *l, *pc, *ix, *iy); + zxcc_term(); + zxcc_exit(1); + + } +} + +/* + * load_bios() loads the minimal CP/M BIOS and BDOS. + * + */ + +void load_bios(void) +{ + char dir[CPM_MAXPATH + 1], fname[CPM_MAXPATH + 1]; + char* q; + size_t bios_len; + + FILE* fp = fopen("bios.bin", "rb"); + if (!fp) + { + strcpy(fname, bindir80); + strcat(fname, "bios.bin"); + fp = fopen(fname, "rb"); + } + if (!fp) + { +#ifdef _WIN32 + dir[0] = 0; /* use strncat in case the path is very long */ + strncat(dir, _pgmptr, CPM_MAXPATH - 8); /* copy the executable path */ +#elif defined(__APPLE__) + uint32_t size = CPM_MAXPATH - 8; + _NSGetExecutablePath(dir, &size); +#else + readlink("/proc/self/exe", dir, CPM_MAXPATH - 8); /* allow room for bios.bin */ +#endif + q = strrchr(dir, DIRSEPCH); + *++q = 0; + strcpy(fname, dir); + strcat(fname, "bios.bin"); + fp = fopen(fname, "rb"); + } + if (!fp) + { + fprintf(stderr, "%s: Cannot locate bios.bin\n", progname); + zxcc_term(); + zxcc_exit(1); + } + bios_len = fread(RAM + 0xFE00, 1, 512, fp); + if (bios_len < 1 || ferror(fp)) + { + fclose(fp); + fprintf(stderr, "%s: Cannot load bios.bin\n", progname); + zxcc_term(); + zxcc_exit(1); + } + fclose(fp); + + DBGMSGV("Loaded %d bytes of BIOS\n", bios_len); +} +/* + * try_com() attempts to open file, file.com, file.COM, file.cpm and file.CPM + * + */ + +FILE* try_com(char* s) +{ + char fname[CPM_MAXPATH + 1]; + FILE* fp; + + strcpy(fname, s); + fp = fopen(s, "rb"); if (fp) return fp; + sprintf(s, "%s.com", fname); fp = fopen(s, "rb"); if (fp) return fp; + sprintf(s, "%s.COM", fname); fp = fopen(s, "rb"); if (fp) return fp; + sprintf(s, "%s.cpm", fname); fp = fopen(s, "rb"); if (fp) return fp; + sprintf(s, "%s.CPM", fname); fp = fopen(s, "rb"); if (fp) return fp; + + strcpy(s, fname); + return NULL; +} + +/* + * load_comfile() loads the COM file whose name was passed as a parameter. + * + */ + + +void load_comfile(void) +{ + size_t com_len; + char fname[CPM_MAXPATH + 1]; + FILE* fp; + + /* Look in current directory first */ + strcpy(fname, argv[1]); + fp = try_com(fname); + if (!fp) + { + strcpy(fname, bindir80); + strcat(fname, argv[1]); + fp = try_com(fname); + } + if (!fp) + { + fprintf(stderr, "%s: Cannot locate %s, %s.com, %s.COM, %s.cpm _or_ %s.CPM\n", + progname, argv[1], argv[1], argv[1], argv[1], argv[1]); + zxcc_term(); + zxcc_exit(1); + } + com_len = fread(RAM + 0x0100, 1, 0xFD00, fp); + if (com_len < 1 || ferror(fp)) + { + fclose(fp); + fprintf(stderr, "%s: Cannot load %s\n", progname, fname); + zxcc_term(); + zxcc_exit(1); + } + fclose(fp); + + /* read() can corrupt buffer area following data read if length + * of data read is less than buffer. Clean it up. */ + memset(RAM + 0x0100 + com_len, 0, 0xFD00 - com_len); + + DBGMSGV("Loaded %d bytes from %s\n", com_len, fname); +} + +unsigned int in() { return 0; } +unsigned int out() { return 0; } + +/* + * xltname: Convert a unix filepath into a CP/M compatible drive:name form. + * The unix filename must be 8.3 or the CP/M code will reject it. + * + * This uses the library xlt_name to do the work, and then just strcat()s + * the result to the command line. + */ + +void zxcc_xltname(char* name, char* pcmd) +{ + char nbuf[CPM_MAXPATH + 1]; + + xlt_name(pcmd, nbuf); + + strcat(name, nbuf); +} + +/* main() parses the arguments to CP/M form. argv[1] is the name of the CP/M + program to load; the remaining arguments are arguments for the CP/M program. + + main() also loads the vestigial CP/M BIOS and does some sanity checks + on the endianness of the host CPU and the sizes of data types. + */ + +int main(int ac, char** av) +{ + int n; + char* pCmd, * str; + char* tmpenv; + + argc = ac; + argv = av; +#ifdef __PACIFIC__ /* Pacific C doesn't support argv[0] */ + progname = "ZXCC"; +#endif + progname = argv[0]; + + /* DJGPP includes the whole path in the program name, which looks + * untidy... + */ + while ((str = strpbrk(progname, DIRSEP))) + progname = str + 1; + +#ifdef DEBUG + fprintf(stderr, "\n\n"); + DBGMSG("Start of execution: "); + for (n = 0; n < argc; n++) + fprintf(stderr, " %s", argv[n]); + fprintf(stderr, "\n"); +#endif + + term_init(); + + if (sizeof(int) > 8 || sizeof(byte) != 1 || sizeof(word) != 2) + { + fprintf(stderr, "%s: type lengths incorrect; edit typedefs " + "and recompile.\n", progname); + zxcc_exit(1); + } + + if (argc < 2) + { + fprintf(stderr, "%s: No CP/M program name provided.\n", progname); + zxcc_exit(1); + } + + /* Parse arguments. An argument can be either: + + * preceded by a '-', in which case it is copied in as-is, less the + dash; + * preceded by a '+', in which case it is parsed as a filename and + then concatenated to the previous argument; + * preceded by a '+-', in which case it is concatenated without + parsing; + * not preceded by either, in which case it is parsed as a filename. + + So, the argument string "--a -b c +-=q --x +/dev/null" would be rendered + into CP/M form as "-a b p:c=q -xd:null" */ + + if (!fcb_init()) + { + fprintf(stderr, "Could not initialise CPMREDIR library\n"); + zxcc_exit(1); + } + + /* allow environment variables to override default locations */ + /* two options are supported, explicit overrides for each directory + * (BINDIR80, LIBDIR80, INCDIR80) + * or a common directory prefix override (CPMDIR80) + * the explict override takes precedence + */ + if ((tmpenv = getenv("CPMDIR80"))) { + mkpath(bindir80, tmpenv, BIN80); /* use CPMDIR80 & std subdirs */ + mkpath(libdir80, tmpenv, LIB80); + mkpath(incdir80, tmpenv, INC80); + } + if ((tmpenv = getenv("BINDIR80"))) + mkpath(bindir80, tmpenv, ""); + + if ((tmpenv = getenv("LIBDIR80"))) + mkpath(libdir80, tmpenv, ""); + + if ((tmpenv = getenv("INCDIR80"))) + mkpath(incdir80, tmpenv, ""); + + DBGMSGV("BINDIR80=\"%s\"\n", bindir80); + DBGMSGV("LIBDIR80=\"%s\"\n", libdir80); + DBGMSGV("INCDIR80=\"%s\"\n", incdir80); + + xlt_map(0, bindir80); /* Establish the 3 fixed mappings */ + xlt_map(1, libdir80); + xlt_map(2, incdir80); + + pCmd = (char*)RAM + 0x81; + + for (n = 2; n < argc; n++) + { + if (argv[n][0] == '+' && argv[n][1] == '-') + { + /* Append, no parsing */ + strcat(pCmd, argv[n] + 2); + } + else if (!argv[n][0] || argv[n][0] == '-') + { + /* Append with space; no parsing. */ + strcat(pCmd, " "); + strcat(pCmd, argv[n] + 1); + } + else if (argv[n][0] == '+') + { + zxcc_xltname(pCmd, argv[n] + 1); + } + else /* Translate a filename */ + { + strcat(pCmd, " "); + zxcc_xltname(pCmd, argv[n]); + } + + } + pCmd[0x7F] = 0; /* Truncate to fit the buffer */ + RAM[0x80] = (byte)strlen(pCmd); + + str = parse_to_fcb(pCmd, 0x5C); + parse_to_fcb(str, 0x6C); + + // This statement is very useful when creating a client like zxc or zxas + DBGMSGV("Command tail is \"%s\"\n", pCmd); + + load_bios(); + + memset(RAM + 0xFE9C, 0, 0x64); /* Zap the SCB */ + RAM[0xFE98] = 0x06; + RAM[0xFE99] = 0xFE; /* FE06, BDOS entry */ + RAM[0xFEA1] = 0x31; /* BDOS 3.1 */ + RAM[0xFEA8] = 0x01; /* UK date format */ + RAM[0xFEAF] = 0x0F; /* CCP drive */ + +#ifdef USE_CPMIO + RAM[0xFEB6] = cpm_term_direct(CPM_TERM_WIDTH, -1); + RAM[0xFEB8] = cpm_term_direct(CPM_TERM_HEIGHT, -1); +#else + RAM[0xFEB6] = 79; + RAM[0xFEB8] = 23; +#endif + RAM[0xFED1] = 0x80; /* Buffer area */ + RAM[0xFED2] = 0xFF; + RAM[0xFED3] = '$'; + RAM[0xFED6] = 0x9C; + RAM[0xFED7] = 0xFE; /* SCB address */ + RAM[0xFED8] = 0x80; /* DMA address */ + RAM[0xFED9] = 0x00; + RAM[0xFEDA] = 0x0F; /* P: */ + RAM[0xFEE6] = 0x01; /* Multi sector count */ + RAM[0xFEFE] = 0x06; + RAM[0xFEFF] = 0xFE; /* BDOS */ + + cpm_drive = 0x0F; /* Start logged into P: */ + cpm_user = 0; /* and user 0 */ + +#ifdef USE_CPMIO + cpm_scr_init(); deinit_term = 1; +#endif +#ifdef USE_CPMGSX + gsx_init(); deinit_gsx = 1; +#endif + + /* Start the Z80 at 0xFF00, with stack at 0xFE00 */ + mainloop(0xFF00, 0xFE00); + + return zxcc_term(); +} + +void zxcc_exit(int code) +{ +#ifdef USE_CPMIO + if (deinit_term) cpm_scr_unit(); +#endif +#ifdef USE_CPMGSX + if (deinit_gsx) gsx_deinit(); +#endif + exit(code); +} + +int zxcc_term(void) +{ + word n; + + //n = RAM[0x81]; /* Get the return code. This is Hi-Tech C */ + //n = (n << 8) | RAM[0x80]; /* specific and fails with other COM files */ + n = 0; + + putchar('\n'); + + if (cpm_error != 0) /* The CP/M "set return code" call was used */ + { /* (my modified Hi-Tech C library uses this */ + n = cpm_error; /* call) */ + } + + if (n < 256 || n == 0xFFFF) + DBGMSGV("Return code %d\n", n); + else + n = 0; + + term_reset(); + + return n; +} + +/* helper function to build full path */ +/* make sure that a / or \ is present at the end of path + * before appending the subdir + */ +static void mkpath(char* fullpath, char* path, char* subdir) { + char* s; + strcpy(fullpath, path); + s = strchr(fullpath, '\0'); + if (*fullpath && !ISDIRSEP(s[-1])) /* make sure we have dir sep */ + *s++ = '/'; + strcpy(s, subdir); +} + +#ifndef _WIN32 + +void raw_init(void) +{ + struct termios tc_raw; + + DBGMSG("Enabling RAW Terminal IO\n"); + + if (tcgetattr(STDIN_FILENO, &tc_orig) == -1) + { + DBGMSG("Failed to enable RAW Terminal IO - tcgetattr() failed\n"); + zxcc_exit(1);; + } + + //tc_raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + //tc_raw.c_oflag &= ~(OPOST); + //tc_raw.c_cflag |= (CS8); + //tc_raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + //tc_raw.c_cc[VMIN] = 1; + //tc_raw.c_cc[VTIME] = 0; + + tc_raw.c_iflag &= ~(ICRNL | INPCK | ISTRIP | IXON); + tc_raw.c_oflag &= ~(OPOST); + tc_raw.c_cflag |= (CS8); + tc_raw.c_lflag &= ~(ECHO | ICANON | IEXTEN); + tc_raw.c_cc[VMIN] = 1; + tc_raw.c_cc[VTIME] = 0; + + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tc_raw) == -1) + { + DBGMSG("Failed to enable RAW Terminal IO - tcsetattr() failed\n"); + zxcc_exit(1); + } + + DBGMSG("Enabled RAW Terminal IO\n"); + + return; +} + +void deinit_raw(void) +{ + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tc_orig) == -1) + { + DBGMSG("Failed to disable RAW Terminal IO - tcsetattr() failed\n"); + return; + } + + DBGMSG("Disabled RAW Terminal IO\n"); + + return; +} + +#endif + +void term_init(void) +{ +#ifdef _WIN32 + setmode(STDIN_FILENO, O_BINARY); + setmode(STDOUT_FILENO, O_BINARY); +#else + if (_isatty(STDIN_FILENO)) + raw_init(); +#endif + + if (_isatty(STDIN_FILENO)) + DBGMSG("Using interactive console mode\n"); + else + DBGMSG("Using standard input/output mode\n"); + + return; +} + +void term_reset(void) +{ +#ifndef _WIN32 + if (_isatty(STDIN_FILENO)) + deinit_raw(); +#endif +} diff --git a/Tools/unix/zxcc/zxcc.h b/Tools/unix/zxcc/zxcc.h index 4d08949a..d60cd1ba 100644 --- a/Tools/unix/zxcc/zxcc.h +++ b/Tools/unix/zxcc/zxcc.h @@ -1,126 +1,141 @@ -/* - * Change the directories in these #defines if necessary. Note trailing slash. - */ -#ifndef _WIN32 - #include "config.h" - #define ISDIRSEP(c) ((c) == '/') - #define DIRSEPCH '/' - #define DIRSEP "/" -#else - #include "config.h" - #define ISDIRSEP(c) ((c) == '/' || (c) == '\\') - #define DIRSEPCH '\\' - #define DIRSEP "/\\:" -#endif - -#ifndef CPMDIR80 - #ifdef _WIN32 - #define CPMDIR80 "d:/local/lib/cpm/" - #else - #define CPMDIR80 "/usr/local/lib/cpm/" - #endif -#endif - -/* the default sub directories trailing / is required */ -#define BIN80 "bin80/" -#define LIB80 "lib80/" -#define INC80 "include80/" - -#ifndef BINDIR80 - #define BINDIR80 CPMDIR80 BIN80 -#endif -#ifndef LIBDIR80 - #define LIBDIR80 CPMDIR80 LIB80 -#endif -#ifndef INCDIR80 - #define INCDIR80 CPMDIR80 INC80 -#endif - -extern char bindir80[]; -extern char libdir80[]; -extern char incdir80[]; - -#define SERIAL "ZXCC05" - -/* System include files */ - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H - #include -#endif -#ifdef _WIN32 - #include - #include - #define strcasecmp _stricmp - #ifndef STDIN_FILENO - #define STDIN_FILENO _fileno(stdin) - #define STDOUT_FILENO _fileno(stdout) - #define STDERR_FILENO _fileno(stderr) - #endif -#else - #include - #define _isatty(a) isatty(a) - #define _fileno(a) fileno(a) -#endif -#include -#include -#ifdef __MSDOS - #include -#endif -#ifndef _WIN32 - #include - #include - #define _S_IFDIR S_IFDIR -#endif - -/* Library includes */ - -#ifdef USE_CPMIO - #include "cpmio.h" -#endif - -#ifdef USE_CPMGSX - #include "cpmgsx.h" -#endif - -#include "cpmredir.h" /* BDOS disc simulation */ - -typedef unsigned char byte; /* Must be exactly 8 bits */ -typedef unsigned short word; /* Must be exactly 16 bits */ - -/* Prototypes */ - -void ed_fe (byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, - byte *h, byte *l, word *pc, word *ix, word *iy); -void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, - byte *h, byte *l, word *pc, word *ix, word *iy); -void cpmbios(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, - byte *h, byte *l, word *pc, word *ix, word *iy); -void dump_regs(FILE *fp, byte a, byte b, byte c, byte d, byte e, byte f, - byte h, byte l, word pc, word ix, word iy); -void Msg(char *s, ...); -int zxcc_term(void); -void zxcc_exit(int code); - -byte cin(void); -void cout(byte); -int cstat(void); - -/* Global variables */ - -extern char *progname; -extern char **argv; -extern int argc; -extern byte RAM[65536]; /* The Z80's address space */ - -/* Z80 CPU emulation */ - -#include "z80.h" - +/* + * Change the directories in these #defines if necessary. Note trailing slash. + */ +#ifndef _WIN32 + #include "config.h" + #define ISDIRSEP(c) ((c) == '/') + #define DIRSEPCH '/' + #define DIRSEP "/" +#else + #include "config.h" + #define ISDIRSEP(c) ((c) == '/' || (c) == '\\') + #define DIRSEPCH '\\' + #define DIRSEP "/\\:" +#endif + +#ifndef CPMDIR80 + #ifdef _WIN32 + #define CPMDIR80 "d:/local/lib/cpm/" + #else + #define CPMDIR80 "/usr/local/lib/cpm/" + #endif +#endif + +/* the default sub directories trailing / is required */ +#ifdef _WIN32 + #define BIN80 "bin80\\" + #define LIB80 "lib80\\" + #define INC80 "include80\\" +#else + #define BIN80 "bin80/" + #define LIB80 "lib80/" + #define INC80 "include80/" +#endif + +#ifndef BINDIR80 + #define BINDIR80 CPMDIR80 BIN80 +#endif +#ifndef LIBDIR80 + #define LIBDIR80 CPMDIR80 LIB80 +#endif +#ifndef INCDIR80 + #define INCDIR80 CPMDIR80 INC80 +#endif + +extern char bindir80[]; +extern char libdir80[]; +extern char incdir80[]; + +#define SERIAL "ZXCC05" + +/* System include files */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H + #include +#endif +#ifdef _WIN32 + #include + #include + #include + #define strcasecmp _stricmp + #ifndef STDIN_FILENO + #define STDIN_FILENO _fileno(stdin) + #define STDOUT_FILENO _fileno(stdout) + #define STDERR_FILENO _fileno(stderr) + #endif +#else + #include + #define _isatty(a) isatty(a) + #define _fileno(a) fileno(a) +#endif +#include +#include +#ifdef __MSDOS + #include +#endif +#ifndef _WIN32 + #include + #include + #define _S_IFDIR S_IFDIR +#endif + +/* Library includes */ + +#ifdef USE_CPMIO + #include "cpmio.h" +#endif + +#ifdef USE_CPMGSX + #include "cpmgsx.h" +#endif + +typedef unsigned char byte; /* Must be exactly 8 bits */ +typedef unsigned short word; /* Must be exactly 16 bits */ + +#include "cpmredir.h" /* BDOS disc simulation */ + +/* Prototypes */ + +void ed_fe (byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, + byte *h, byte *l, word *pc, word *ix, word *iy); +void cpmbdos(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, + byte *h, byte *l, word *pc, word *ix, word *iy); +void cpmbios(byte *a, byte *b, byte *c, byte *d, byte *e, byte *f, + byte *h, byte *l, word *pc, word *ix, word *iy); +void dump_regs(FILE *fp, byte a, byte b, byte c, byte d, byte e, byte f, + byte h, byte l, word pc, word ix, word iy); +void Msg(char *s, ...); +void DbgMsg(const char *file, int line, const char *func, char *s, ...); +int zxcc_term(void); +void zxcc_exit(int code); + +void term_init(void); +void term_reset(void); + +#ifdef DEBUG + #define DBGMSGV(s, ...) DbgMsg(__FILE__, __LINE__, __func__, s, __VA_ARGS__) + #define DBGMSG(s) DbgMsg(__FILE__, __LINE__, __func__, s) + +#else + #define DBGMSGV(s, ...) + #define DBGMSG(s) +#endif + +/* Global variables */ + +extern char *progname; +extern char **argv; +extern int argc; +extern byte RAM[65536]; /* The Z80's address space */ + +/* Z80 CPU emulation */ + +#include "z80.h" diff --git a/Tools/unix/zxcc/zxdbdos.c b/Tools/unix/zxcc/zxdbdos.c index 36cbe532..9a839e5e 100644 --- a/Tools/unix/zxcc/zxdbdos.c +++ b/Tools/unix/zxcc/zxdbdos.c @@ -2,43 +2,42 @@ #include "zxbdos.h" #include "zxdbdos.h" -/* This file used to deal with all disc-based BDOS calls. +/* This file used to deal with all disc-based BDOS calls. Now the calls have been moved into libcpmredir, it's a bit empty round - here. + here. ZXCC does a few odd things when searching, to make Hi-Tech C behave properly. */ - /* If a file could not be found on the default drive, try again on a "search" drive (A: for .COM files, B: for .LIB and .OBJ files) */ - -int fcbforce(byte *fcb, byte *odrv) + +int fcbforce(byte* fcb, byte* odrv) { byte drive; char nam[9]; - char typ[4]; + char typ[4]; int n; - for (n = 0; n < 8; n++) nam[n] = fcb[n+1] & 0x7F; + for (n = 0; n < 8; n++) nam[n] = fcb[n + 1] & 0x7F; nam[8] = 0; - for (n = 0; n < 3; n++) typ[n] = fcb[n+9] & 0x7F; + for (n = 0; n < 3; n++) typ[n] = fcb[n + 9] & 0x7F; typ[3] = 0; drive = 0; if (*fcb) return 0; /* not using default drive */ /* Microsoft BASIC compiler run-time */ - if (!strcmp(nam,"BCLOAD ") && !strcmp(typ, " ")) drive = 2; + if (!strcmp(nam, "BCLOAD ") && !strcmp(typ, " ")) drive = 2; /* HI-TECH C options help file */ - if (!strcmp(nam,"OPTIONS ") && !strcmp(typ, " ")) drive = 1; + if (!strcmp(nam, "OPTIONS ") && !strcmp(typ, " ")) drive = 1; /* binaries, libraries and object files */ if (!strcmp(typ, "COM")) drive = 1; - if (!strcmp(typ, "LIB")) drive = 2; - if (!strcmp(typ, "OBJ")) drive = 2; + if (!strcmp(typ, "LIB")) drive = 2; + if (!strcmp(typ, "OBJ")) drive = 2; /* some extras for messages, overlays, includes */ if (!strcmp(typ, "HLP")) drive = 1; @@ -48,18 +47,18 @@ int fcbforce(byte *fcb, byte *odrv) if (!strcmp(typ, "H ")) drive = 3; if (!drive) return 0; - + *odrv = *fcb; *fcb = drive; return 1; } /* zxcc has a trick with some filenames: If it can't find them where they - should be, and a drive wasn't specified, it searches BINDIR80, - LIBDIR80 or INCDIR80 (depending on the type of the file). + should be, and a drive wasn't specified, it searches BINDIR80, + LIBDIR80 or INCDIR80 (depending on the type of the file). */ -word x_fcb_open(byte *fcb, byte *dma) +word x_fcb_open(byte* fcb, byte* dma) { word rv = fcb_open(fcb, dma); byte odrv; @@ -75,23 +74,18 @@ word x_fcb_open(byte *fcb, byte *dma) return rv; } - - -word x_fcb_stat(byte *fcb) +word x_fcb_stat(byte* fcb) { - word rv = fcb_stat(fcb); - byte odrv; - - if (rv == 0xFF) - { - if (fcbforce(fcb, &odrv)) - { - rv = fcb_stat(fcb); - *fcb = odrv; - } - } - return rv; -} - - + word rv = fcb_stat(fcb); + byte odrv; + if (rv == 0xFF) + { + if (fcbforce(fcb, &odrv)) + { + rv = fcb_stat(fcb); + *fcb = odrv; + } + } + return rv; +} diff --git a/Tools/unix/zxcc/zxdbdos.h b/Tools/unix/zxcc/zxdbdos.h index 7a47b801..16bcde18 100644 --- a/Tools/unix/zxcc/zxdbdos.h +++ b/Tools/unix/zxcc/zxdbdos.h @@ -1,8 +1,4 @@ - - - -int fcbforce(byte *fcb, byte *odrv); - -word x_fcb_open(byte *fcb, byte *dma); -word x_fcb_stat(byte *fcb); - +int fcbforce(byte* fcb, byte* odrv); + +word x_fcb_open(byte* fcb, byte* dma); +word x_fcb_stat(byte* fcb); diff --git a/Tools/zxcc/COPYING b/Tools/zxcc/COPYING index a43ea212..92851102 100644 --- a/Tools/zxcc/COPYING +++ b/Tools/zxcc/COPYING @@ -1,339 +1,339 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 675 Mass Ave, Cambridge, MA 02139, USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Tools/zxcc/zxcc-src.zip b/Tools/zxcc/zxcc-src.zip index 1ae919e4f1fcd045b48e6ffa8caeda9192de8000..25238d8af7609c967953ff343d0d209deb1f4a31 100644 GIT binary patch delta 50899 zcmYhiQ*@wRu&x{1wr$&X(y?vZ$rsyBI=0cVt&VNGqfW;@{~BkHz4q0-m{)UDje4r) z`_^y)L`4|{qKZ5?1O^BQ2n-0kqe2s+Iw%0`sG!b~yb>`33IdW24g!Mk->sO3jlDUe zrZ|(CgZWH~k=+&x#^@gSD^g~NNvKjZxQ@J-@l1hdQ9ZtMiUSJFFZa}_-+iJf=nVyd zu2uCKD4Hem)hfj#kNtUr_oeX3OqSqOVHV@S#|%vJLo81E@TXPv`rQzbW$= zKlB@8$*G`MogiDmPxfnDBG|LJRd}s$<_BQoHPZA7wvHT08GFE4o74Nrb$XVb(-I5DctLi{tuxAEnh2!g z&w2x9af?H?=%1dbA=t-!vuJ-)e#^G2BM9-2d;V;hqJ>xHIXiQEf2Zj1yTdRvVy2in z;+P~sBu#|K?Fn&bT5h}BDnxWiIk6pVC1gt$-rEbUa83bcGwJ$bT%DEl|0X2(^H{*< zl*NU!`Azr5(##syFVc_JgJu??f&Jo`cFIO0*Di{f@QjnQHKI7D_dmjB)UKGM^e5*!g?%aJW-31)ml80cTWhWwn%uZ6w{npQu`TD7CJA z_ozRHgXgElFqyd?0(*DxxG~B9kln`@0QlZv@&^Ol71+c^1qH)Dm91F9=a!mdmr|#m zm)KwYeV$&IUN6r;&cx0u`G)$_kY;R(vVtdc7@Di!8@xwwHEF=3*5e0CPRaTP-*B7> ziX)X5Buv;8>lGS0x>MXBTwr8#+V?d_c{OuZXV>1ethPgz!i1d$P5PmUwKE1Epb{U$ z4CVlJ0{w%3!x)={4SvfZQ}9*5mmRmAWCLfMwIK&i8x9Yt)j9w z7)(KG3c02NPuu(zJVH0!jF@ts>AaFPJb(4QhDCKENj@z?l#>$YaeG_bgovJZeq7{s zm+ZCwUH`G%-G%u`H~-aU*_(QieyMrCkw5%YG2 z1cd^5QkHG6Fpf+B%FA@)xE(?Dz7wdM5OgJi`o!--gGNxM-0^UJBH}JDw}_*z5emJ& zW*)B;O%sC0=pyJhzFqI!j+-gZgVxCF;6^U$%YVltVG!=yK%sqF9-sP+ftqu_Bu zcK;!?&9Y$dCg$>bNuTqb&E;PJ_YC6=R#0BOlgc@1PG;gb6;5*arjQ z{5%rxx1WNfcd-vOSjUEVdk_|biGXB=8KMS$a*K&(*@dTKJ}beSqbI%@&!@fa8G@Rq zpY`rrHh)Mq+SQpgq5!-%qB{eOZJ}LoCFovAC6q7L1Rf88*b0+vIda3Y_;U==Ci+ar zZ`Tih{AQsO-=_3*g=ls(X9VDIg3YO7>Z+@*s!y0nWQS!l1<%b%#(#b0!RmO{hL=0jfg8gI3z#bV=AZ)pCr`I}ij*riKta%aoUD7!5_c+}G1~m|YejQ!>Vr74z zp5&%!w`pBO4jiK)!#~laF>cbeRlWUg{N?_%euLX;vA$c#N$m%JA?ulUkKlPzo?_gj z@f13@kAw^bMw7!m%Q4VyOE5fq6E{Hxq`GEm>jRx5vy+F0(Y)dOlt?t&j7^1&Mu$;D zy=d}q+^F>E-<&$uJx;2tBX9Ay_p6D;kC!pRI@Lz7*9QpqX24__c_t#8-QuE~ zpC%HUe&Vv5@8X_&_4us9GLHqDV(%3RkEwm1T6V2X8ThA;4yGkA!G2$0%_6WZpg{T$d3Bto+oH7pNxyJZ_1WrU!NljxT7s|ft) zExf>8925x1Cer`3GXFin*}>e^lgVsU%i%w4p?v96d<}zwZKAHW968X7r#TQxUUIeK zvkU_2ozdM{lrHA{2Jmrvvw3-a3!sPSo10^O-DkUVN*>owbzALn{Ns5yyN=!6ko@B- zNC=YCj}rL#L5D>u0xYViKc}hW^d+Ev^J7*zIQISh)7KArYo0b_NC0nx(DQ-zcP)pV z@z-M#HJbtQv2Wn(Z#ur{un27g3^nwD@uEY3poJiZzT6v}iY+pzOtXed4#9O;6vx;` zyA_Lq4PzMA;lySFGb<|efNp^#(z6NhhJ%Lu5>oCoWhFQZ)rO!6AOXv_MN$x#^5Z)s zMWE7bl+1X@rR+$Fnu|-a0q+puqa}7JbAkS&W&xQGtty(0%VrcjEmIr9rA%T870&=1 zuR6sIO#k4JEXCqV!5xQ&kPvQ=AQ%o3uYYAp6qFD#e3=*W@pJbDn;B+*nZNS<8A)x0 zc+8nBK}YNoNs+>U1rsMkn~?*HmjII2do!z-X5D8)+@d@?@uvBH?A)*xy7~h<$9elI zzr{-lImduU=$eT5@o5#*H&>ZF4u=~U$0Vdc^J~0n5KtrQu9@07Qqm3Tjs3^fp6F{G?5<+(l1oQoRKMVyzBFxuC|3Ims!fXa=RBc?+*aeAo7zCNgXUPZXLT!<-6bN*T zDIcVj98lft9NEw`(q1GUa%SONAR^B$)c*}pQSI}K6objehMLmsQmdQ{NyG0 z_pX0YJB+?FY=jAXGlz9cQqgnkMpY%p`0>Xc)C9q?++7O{vSe+i#b7^Vnn_W-&jQzU_7E_vd@(7;m8+kTb=&GUzV*MutS zFTT~E2smP^tSeYCD^@BBQTf)<+F4Wkyx#oCbh+J#mB$5+#q<5Bp-Z}}ay+Ssd*v@5 zNZu$r&cKQJEOb+!h4DPLgGEo1*&Yb*xMsU9-lu{VU9PY0FiQhKk@)a~2>v?e$+wJ& zse=^qekiM2yKTC&pDey2D#x$Du619qet(urcg0rY)S4pL5$#TBIlq%@C!l%n;<}%C zPf+p?mNB0*#QYC0VTRnLmlxjN%jClLIL(%>BRwkK8m%QM7s9)JZo|2n4{lklf5<~J zpFiEBKKp5e$f*?oBU5MF`-9fpaJj*QK*PQdN?A?&vXVajr?I_QTtCgpfA)4H3PWsm zM|6Ke9bOJ_P_Az0eN&)Ld@e?=LYx8K#tf629}Y(um~|X6Vp&L83}zgvhp>jvXh;dN z;E0GZwS*vKWz_{rO+OK*Ts;P-zNc_7+PKjKoQ>RfK`^0!LRd0P_ieqe4O*dCBI3x6 z{Z!%lm2-~59?4nJiJ1L~QKUx9PPn z&GMDSNTEoIHg&5Z$LPSl4e2|4!0@77;3uqaF$iedzP!TImQCu~R2QduKO5L@T`IlmX%0mB$Gc56%`J{@?NvPX#da1vJ$MDacr}Dv2)Cdle zPx4Xk-F`3sQi5k<+h01Li(UOn`nxYjXFlD$nzm-ZyTzElUO*d%#hQSqK<{+A%q78Z1f0>wsp4M0^&R?DmpP$|M{+tDgpCEar3dj-J$559o^ zXf>a&s16U@)g7XOfEaOufWZF${IarlGG#KG(eqK;lEV1@Is3rR;2y%rGFGk?17A11 zun3xRaGuUaq@!!b`RiDYZJ+A(=Cijw(;Q!JG1X?ku=n|CXS}|v^XUMZIIywh`SAG4 z`1{;m@2YoQ05y~*s-B8~@B1u&}z9{T?WZyttawh{5 zJ|Mg&i+xKKi>X8jM05OiQ^FAkrKX!hQ8X>VnpT{&L4$n})bH=+OPc_2ema?xu?u4{ zQ+@Kl1qBd5vQ3u#bz?1W^`{YEC}2Azb#bld8EnUR_#~4{O-Mq`UpnPxn`$C!o3_j` z6fe!Le1~i*h;~=7Ci2dYS!W(79kXLv4#LE!m4HnpEH{frCV}B~D&fHjprz_0P&kC5 z0nz+$cE7v6pMqZy{FDVg<^}mL^9?_iLG4);?$mc>Bx&*Dk6^+`)lR8LG2%i8CI>fm z+Xv2t`1ypoUZOjM1-f_yd4#be=jJ4|dd~9=2(Ar?yyiw?r<$?n=8$v5a^-C}5b$C> z7~&$J3wh;=CRDHjG0xUM#d2$vlF=8*vxy6Y%w zl9U$fs4WJ%0T$)rI=$~_IC+Gg1d+CVbzX33=!rd8?%y-iND1WKnmcAdw z-i)?~89?U7jkDFG|&aICq++P&&gSUV4C(VuSk!%5%_?$8fsP zOOjd02ysY2g>-@wmrqk(X$?5Z^Rh*PTcwd7*`zva_{LQHaIGt3C^Mqw4dI(H5YqxQ$!Ev>?tPz>4iK5d^zJ7BdjE^RhcmK0cMVDxt!NeC5v#W`7LFOSqhP$5uHh`kqPDyQpRdsmyYKvdPspMc=MiP3%in?F1Aj3 z8R;l3NI5A8^^DL&(Ts#Bf=MFGn9Av&F^dZ#EkF_NQ?|Q^o=RJ7fJTV}`tSNtl&iER zjHJ^jT2>+%*DuLVI6UddsR!DMn0fTmFD}| zMnAMcfST$oPcSZOtu`_U&{U8`%4GrRq>huVXmy(3n~5ns#<Fab(}Ss(l}ryJElC z@qmM6I65TmK!Wfj3pexUN&Fa*6iae}PeMP?{8-}TE+x^$M6+~(1{1SUZN-|RvGtz} z3qim2hD4dr?#is&@4Nzbnpc(T|29(DN`W4N={Gl$dw2hSbZ}k5qGh(~l9!8@0|r@Y zhPhfTXD-hR4K(Kj?{Z-B%fmKuNw*_8SOCSCL}gy^7toi5hwR@nS1}b_fAl2$m2#6a zPB{hj>8482T6Ss@$cnaOv5%2_sCS!oC`>*=a$iR zx{kX&3awFTyG`lHka=!{PmCG#ZlL}=(#CMg;GCYjivD-b4`ru|Xl^Ho-d|M}p)yWO zH%UCm6et3|bsv$iHu2hTqHuLo=o?K)c9?&%barVEHZ90zTF!c7zj%=*qs~%El6?9Q ziX6a$#?(et$!^{Ghacs*&v{+DmkQ*z^$7-l%atJ|TPcQw1c_S2Up!_R5(0Ibcy-@C zKZ^D1FHlxEKUO`ebK48hy%>BhWzD=i0! za1hvAa0pW&E+R>|xcKVL)3iY>yVVY4aV}FKH)kN<{lloPQHsGzvgqw$9=9L{mRli* zsBamtTP@aBC(O;bk7qju>&*zt2|&_WeC;4VaKNwx%(zCXZ5hwyqXCPlhTWfZ8{WFi ztw)I0$X5u0i{kFjXpYV&9ta^u>ZkC)@jM_iNukybZ=a)iui>g7;wop}6JK@UD=>Gm{`iU)=Urhj?OH*3llAF49-Y2AJ zT1PA?8M*dLZmOn+Y8U8J$3=6F*^e{HLjJ75I%8Y=}@ux(U2UHzW*D#(0vBd zt&q<#QO1q;(cUd;p#f7L_G@EdBua~p|BsDzK>};CnFQ<#feNr^NL(R@(YXEaNkIrY2%bgRS#p zS;NIxnY9RAHS@BvZ^K0SA}uNx6_=h;jJ@24S)D-jE6?nbU1K9bhsh`MA5txZpa@6G zF=&ZPb__4=ydt1zKyRv1L4-`=7@6dC&Vc(SAgz0vl@Cq?k~O-}$-A*n>kP3FaIL}g zbvsEZqfCeEyxpNbz>|+N|BA-R+)C|(h`WD~TUsJBp60Q;b#=seU41NwsC6b|X50`R zx4&e?^G{IQFSbbvE0()-fi%mqs8hw$_zrX1daL6y_JzZZ9||*3Q{@<{7o(~ z&>d786al0t>lri07pc{_As?@9>ei78c_FBNEtWKwdnKV{&qtsyxDM&^(hs2xT>a2* zy$;Y@VRe6Pugc=+=yTNjm7?j@QP=^dJEM~KFNFfF?IT-Fq)C)Q$fgy8rIX{eB%mij z_py_j>+XzJRNYl6Z_o>KLTkEpeYIvbopUSUG!w9tZ}7c&r6E^W7cw%$AE~6P7k)S6 ztW<_NCvfmNQDZI_8|J69b~Ga=IPF-tE?3X`Sw`SHm!*mOD zWOv;;TaEHT%n_asX-y09Z3G`12e$Q+sDRnk^XWRb9_czju*gzokHQ|Q7wPArOzY5_ z?-_t|yf;ST!DJ-8#n@Oy*9z4N!@i8`N`#twqk?6hvWG$sb=mqNedOckmUoJqB=pv$izv(nhV(cX3|bF0EVJqS0~5!bfhT zTGZ*%=U_pyARx;mxfpN%;^1DwCZ91!Em|sMo88r`5T@t zvwTurf^J>)y4Aael#WWg+wv~5WGS&dsNR;%d4Ypi?7^`pasS>iz*Fu=ICf)MeG;H& z`)G`ne_i3CsdXD4(BLY)bhGZcaJTB(eb2oF|3{Exn=bFHidgkGcD-VmW#+gWJM0Wu zpsJL8yCus?UWB+_TU52bH(Z@y=$PAj{BleM`q|n0T)Rep+}?tL_>TQP*YfssRP$NZ zZy(KN>$Xyyxh!V7+aGEiaI)1rW1GOV|3=GEne%{?`o+ccA16B`>)2qr*vGC}&uN+W zzbHbD^3q+DGJHC2rE$(v8z~D@CAKlV>`tRDXmTUFR(P_`=%`^^2k7R+qLbGR= z&IP+e0zzr;MlSi!BpY;k*c$FWN_+8-Iey6WJH)08LUKjGw;{KnbKQ4~PBVzLTmTpQ`?BdsT%qdsxa`o7a}zc_qS4*FlSw zg!3375yBE`;Mzfr4_46Fg|{1EhgfGiNBtSXbGD25bv0`0eOb+9-4J}yPzCC5?r#jp|+cO;Q%za)M{8 z?;X3}IX;n)22L4w*=Eiw#?hacp9W42_xTuadj$NnBMjFjy$Jwd_-guxWNOh~FV{f` zTVgHu8V5UUEJ@ZIY@}!=B9xuUW*a%(^-?AJ?g{n4&*+%1p}6p|aZ11Ct3ldraaWi# z+Kyfz5T(65*ksoZ4S8u?agPbo+rBoNwTC0Tf1Ge@NWM*_OTO*jXZDje&dRwplIERx zvF@H0V-L*W+!eqHRYxa#E^XsvSx%^oHPcc)XNEOxktO7=YHk^!25BZDhwW73_H3{8 zVh8_wX(T1H|_Y9B>*VJBYY*e3XA=1Z~o^8G=DrPbi+7U@YU#Ziy8gKi zm{)ra`jU^mqkz?=P-(W8#*CT!{n^uZEZ+VHe}Cxj`Wwi^Pq`U`t=g`c6h~7zY&fTI zAvVZ73&~1-t=!&B&U!7TJ6YSk8yCm{f~iZkhV}x*X5(Agk=EAy8eIo2=dH_~y`HM8 zFW=Gho2wkpc1GiUK4TouopDw?1I`6GaRJOId!N0fHeXir?$eoV?BvQL1T=&{ z6oKNk0R5q3k~t|O~jXRd_$91db>eCeSmC^cxF?9mn5dZjt61pDy#YS zt;3w}JBIEW3MaBIn4Vdqy6o3Z)pKq0AU09zh06=`I2Dx2YM~#qkZ1C<@qT4RTph!B zdG~m0om*oy>?H5yTbG3iEmLntja+)~Jb>BVQUe0<#pIvPz1r|^c1r}+57$UzmJk6P zFP|)r(7373pyCbsf5i^{0!8(K_0LdkI1msU{QoO<*f_fXr_t;9xot|JeDmi`dab)u zAbPW`D_xcsDaQKnxP$nCjC-WUohBi5=;og9z$);6-s}kW0wO6ptLW4zj?e9W?|t$| zFDKZ{{oV_GxCz~PUoVU;PI%&3@CBE7T>g+Teqt_b5HS-Wk4n*_iy~zP;mx=GfLf?g zNLg6@ZR`Nf%Yj$scP;OdsloFLU6KcPWhC-#ZPJCR_1{qi)>d%;T>4p2Xt!pI7;Y2n zuzfZgO52hVfPsS`43w%jBE1hzfOT)7Ld91{=_3AVcdSK zxNUH%n8({0Hn0%bLwlo{f1PPigQhTKNZfqVb{2-4ptU zdw36JdJTi4UVB4HHi&hId@VDY?36u_LP&bfzJX6~?|9ovy4lFIq7xc@6$?)1mXrS5 zb(icGF3BW~ciGkvwd07U->Nce$bv}HjwEM*=?^2K>wphYd}WBo6aF`@=NE$Ab|Lq? z$rko=BfeXsKu~9q*mG3Y%q%=!l5#9cnWL}s>ZsB%eSzTPoSmGl)~lbW^nHof+mF%> zb8dzFJ1~qU5kJ*V0}mB$ow`yL3gXgB|A4O!dpKlG+Ki^BNFJGJTqczJpsFvIZVWR6 zY_0Oy+ZEYwYd%n@0Ps6MWxs&l$i|1caLo$O1fU-DC>nXdKsF#Ve`7vbC9c-t0Q z!en!-G(P6(<3naXY-HP)z8Zg8`3fESUN5*OS!H)GzW6aDp|k%n`@%ey38u}!rYMk) z;KYi=bj8rp)J{_P6@nt>>P)579~l@5yzD8=PBG(+v*fzhQ=^nvtE}5qWvC|8np;4{ z@TA@#4*WQ>{=WYM3SJb_&pALeRTCtv^w!j+98)%Ak<(qn$)nEo*97o!RHrepQ~|uI zMhmv-=)pgnu_2MB#ex|@VjVgT`CjE_Sm>gP4qIXjL`~Zzb!gD-`dHTKpH9($ZQ4mi z`vjstiMfWrJfVSEe8LLv3j&hMRG}Crmfiy??$Z()-&KXr(2cjg*o208IR{opgU`Z0 z>lfvMU842Aog8B*cj?rxmhU3FB39z^9n#z~Cgv`TpGXwt^@Rs5pk9;9thL!XtBskn zgy5CiLku;FBG}gU`&sybJFOYOR8Z9{nrjr7q0EgAL2fL9Q(qwuJSrYF%GZSc(d|Cs zbl5`FkK@vtdS^@6G_Bvho_7ch4(Eo!nF~xfLj6MR)0{a_3#50*V;9}T!G{c4T454a zm|a)C7S!5*nzM0Twy)j&-W|`s_;!Hn#J8p2W|)EgaRJ#Sql!P)m`!8>`*$*><%^W1 z*;G=lIi9OudFBeJPV<`7160`O{j=-~J!oo_DY0j-$A3 zfxR}0dw}B@+g4HW4VFpDsCAIKUl4{6D+%623g?t&6eB3%x^*$@H3DqbT9hRNftCh` z{6^tIq^c>+c6aCk&c-=#^`XcKn%9UaJvKl85(`fqWg;z(5C&5X(*hSjZd!?Pu6YCz zDp|r_-i5OgRqzC=`tB2)kfp44id4W3F?zg^BZl(&E6&Zn$|E;~o}u;mZsrW;<{aj3 zaU`Z{jXn~@3~4XAH8dJ4SrAS(dqeWD!24%MS28pNVu zV{$Jg|Bc(^4s@>Y{#4KV=?I z6G!|XjnxsmA`y4tiK|K;SX<_HKqEAX9CCL7V>>m}A5{EPcxLF>_c1e4n}8_ABpt0j zPp0>h)tCJ0DVRinNOy}p3&Pzoo)5!V)uf^+nFocE}?eqDtWp9^8$LuE;&eBQ}r@8 zFTor0rnd2=3&UN*B2B~>VX|a@^eBp#2;Uz~qJeRvZH5g1dbq*OG?pi$9oOm(Y6>%j z5Mix}n_TZtQj3sAJ$re7tTnwFP5TdXa^vqd&3-ekti2Gy_yb}nt?RbB--|>`iTKbk z8@oj02C8$XEk3nE7HeU{V-)!`bfq`3S7+#LW&MloS6lGEMAJB-NAUu~N=iw=05U9!`e-&@Y<6 z;-q3~MP_1MB|HCckIo$Fn)B}%wfNAwuAs*IJTt&%OGUn=8P6sqT zEa*-HA{U~>PaJM)7QW{Oe$b)5BU|D3PD;+cVGS;EEyxu5YDGo|ov=<;)^a6wl;Qnf z8P^<$p4uz%bIKEL3aihG7&T2AXM0yw0|Lm%Dhp#=E0+0BkFQHDkxwdypzSiMLGp<6 z1xp6z%WTY7V@H>C1Q2&G>Ln{$-VHRo&l6I>0zn0Do-*S2T2!MLG##0PF12jX6IU{URkklk20|^KnSE2ESMlh?2qi%x*TqdTZL_D8sx@OLtoXE= zk61Rfyd`SX5B3TK@^{w{DMF=<%? zZc%Bg$X&dicM(2g0jAUIzkVBIBe&F9SV{2pP`3zoZOKFM_+Wz)2hQ63oH?HLmA}j$syg{?yojd;p$zitFh!)o zwEew2ZXdagFy1w8@zf@~E}8tFoafUlpu}}DarbRm>MLxP*a*=FrQ_^FAJG77Cy}EO zSYGmOB_J>kdiqO6`ul{mG_ORBGLUYGOvgQnA3L~?GvO0g*q5A;uTYG!J>j_v19Z~u zzMM0Ay>lf-tT6<~lJ%(f&$g7T>u)J;upViv=umW7SNae}@_y}RbjR)fVwa8_z~e8* zuK*i<;j7>{N9a)95ieIwO3LC?;dPYZ>_11N?R$NFFWX@+wclGmm$Wvq%w4+-*}370 zfd9vTQ}myHnQx}!bpinb0tF8O@?U5AKmHqM6IVA2CbM*PzyERIe0R(EM}ku?xZr+9 zVqbJe4CprWhw|wyVk^J<`&vXTnX_z?9lB&XiPSYG@<2qLu9aC~ksaPLJ z9vEA1xWqGiXL;(M zQiX`64R3c}p4_2hcRpUB4JXiC=e{Zy(``eub#-qbI7>b}Jg&n;%#!Ju-QFnz!2*I+ z&&A$tVifL`6~?9Zt07XAvcO^NTrI;L#1u4|O|fHfh{R089XrqI!U=s$cHMSp$x3-Y z$^sWbYkeM7Z?-i`sKlEiOsS!}c`Ixw)nr5bU4+Df??_2$9gQ;1i^W&*b#=@9UF1JC zm>das9(JciTZ*~W^BBP<8-%jRlKwTPqZ*5E>M7P2%8!i^KL;v?*b6A3oAyK{^lNh`F6i2ZjO5|S0_+93^E?MT##}ZjAD{M*H~k3$JUjGR9QX4-#ErTn%N73Ex?+Ns-1S6Yjo0>BTol zQx4}vYLyO_M3P-8^`nF;^PQ`)y^lxc`&%r9j)VK<+RgSAYrnPB#_m`!HY)*n1wiI1 zF1v&FAX$e#cO^d{M#!@aTd$c@f!4|(+n_n{@CE%Qa2}e<1a~niwRsKhP3T$W?yHk^ zgQ?n|loP(Z>-lz&lI0T5Phiy9r?X6`jIhQxRYtn@ORe6!(sgIkF|$E}%;r(J-+_$& zX@v_fdgRXYw(OTiJX%9{xD~*#J6mu!UkKX0hT_sYB^>hHHn0RFONK((cIV;0TyXkR zfA_5F`IR^P?2`DxD%J1YAsy6HaJu~&Om5iKsRvX$w&~W{?wynSmTx4*&T&P=GH5t2bgMcbThYF0LZj@T65x{-P5H zd+VHXT&Z=o%83;ENu1~F_UDaW{yd5;ZH|a1adt@iot@pA-M*chyso#0`#GU+dz-VD z!j<(MJBOIOqZRN4H+_DB$mAXD;2jUJZuD1Bk9+8&fzFON36lyMTO*+N2io3iX1;Yt zJ#ZwQ?ENY~{>9Z&H;=2{<+=AvN7&ES<2#y~tIt>DoOeF}HwH%R7wix_e=A8&-`GB~ zP8^ePau;WU!E-h|fu%&j(9r$6`T4Vc34f`iXss0-)nJqCS7;#32NB@o=1up6`N7?0 z1)V{k5P4R=GTwv9mS`46;{jX$@AT^Ht2^|yth zD39diD-6=Z3Zp>MlRbbRAX{sr@+5}aNs6e(*C?EbDA1_52tfH=Alv(ch|Kf4{`dQ} zWs0-eTcigD@ql^zq_j(vmml?Z;CENQYrBH1yz5ltLlRHZSI-*h#Q}Kj+S{im}T>~xgmZqECg%sI3ODAbsNX9 zjy1Z=^9UZi{*a9WqXrub?CHW004xIJP3JDvp#A$OYI&VrYE2gXCY zD(hGB0}RkAuKCWryX0DGg)m!EYN<%ZF2(f1R-z*^;D6QDgCGA*Ybu;`UB))(KJ#u1 zs=wySdAac>^jdEnb+wxJ>Zx??tPQ%oY3;{u@Jei?sEFjJ_LGiuD6&e(2GC5(E1mon zPR|OWD;~&qn@U+`rcmnWU2fj;=noLz&paCu+#TBSZAI;FdZ?hnQR^_wxn*atf>Ucp zfee_uz9U`GQ+E*L5*ZQJ%w-Z1-yUESYK=K>wk#|v_y>)M5ey}XH5@#rpRv{m(eV~? z{d}{POk+&e75COH=0I{W;N8i{#Ucse9e#FsNFWl6vjP4B?E6i>KZ{A zM9V$`rb8+*%C}aUhn)=_I?xYODbsME`;w$;`ha|>~s=KKq21xtF@5owylSPys3m11a5Bxm&&(ZCjY&EyYK*y4HAE} zEb01ziN(lm)}UQk@y+tfpuMzon-KrtZ%Ix zim`!q0ydt#lsTC;z@|NjX-eFL=ILyM$WacL%SC(Eh9M0GiO6wf{U*8>ljf03z_s{P zq;HsNr@{naOiC`z;Jdh~3ez>IL!qRDXvV=l39?ukxJQVB^p04GRk87=5?O4nvM+VC zd7&B<^P2G^{c(EooK47?@jQ|ULT|0M3Dwx!ajKNE)nj)6-s6Y4sh!Xr_#rSo&k*8n zVhqnd5CX^P=h26bX$9$E?_0OpV+w#L{%M*KGpxsyc z2%kkQJz-+N#D`8uIR=jk@9=qR(b+aiR*FQkHih1rZEg}?;Ep}r`LF&m|Xk<4OEv?)x~ zZ#|}H-8yXvBi`$jd>A*H2M`SL3`$3NU>B^74Zjv1iL_;qtjfcLDGw1W>SzX<1(qX2 z(jk^V@?4-K^NOX5B9A6*s@|Kj`tZ)|4rYKqQxp_!*^wYq;$`r2;{J%ZYk?)23uOaq zzqbf*i$1pX@yI4CI4PjaGvX1XiQ(~U_j<8sB$5}gc}bn2=BS`IjA^ByfNE*35?LoI zRwIga86k_qT&Y9IG|Q*zAbbn(HvSibyHFVD4aQTN`=Xu%){Dsumrog1wxNDZ_?DDb zo6G2-FWdqP7f69{cxWtO7oGN%RjOz#4u@mX4brbemYZ2XlE6!z@;dk+0bcp`R% z5C1hB;o3CBUOr^XIsUSaosf-ceoB5fmE>QUh{|_b4Fh7{9!0&Jtlx>ZX9jx+5?%~3 zeyJL!x}6eOYu1De@u#t8RfC2br-j9phqvNirzFzY4YC2e0^%c_Y$$eYqcVGP21O^( z-qrQ~HY55zgB*UczAU5zTYv#PWuSk0D`NI!GMvMmgV-uZyiFs43DaQ2ZrYC++nJ06 zi;i1jMH7KJ8OI_{$z^4m(}FMIB{|D3&@~n9$ps6+jwQsjQ3s!p8Oa5cCZtvC(^EO@ zA@If9_gizFgu&XZ4P;KZQW=m}5%B?6bQZ!gl4dQ=sp2hTs+CyX1*s)F%_|U;-CTBZw!2p!oIl zu`$Ni#hxF2savK?A9FI_){s$zIdCy_x8C(4Xss3IT`HkRt{E1CVf%S25$*~Qd77KZ z;5Xzmd#$a)Y$-Lh`-U;JYMQh9Nm{_S%J`I&hI{5|{_46=BON#H|_B z{Hn65LW`FE;0-T8g7Yp;YkddY$jVLmGmxs)F)YN&QV>$8$VtMDNJ~s$+L9roL$R)s z(Z1^ROOmC$9!*^$Lcqo_pQYLOnzMV{HtDde!dXVv2#an0Td|>c{f*66RHeVmR{wHU zh{(yu|MRw7o#z)V;XF2k!xzbQW87Z8yM6??5hX9lbI=@@Bzm`=H+n1}=mjAq*I&lV zhO5yd#J7okW^=hoPNFK^r|r_n(L}o+cxI|MyCjjgHf)HjS0^UAnNa-4I!j@(H4TKR z7v#dq7w?C4Qt*zUOXR4c#d0f8%I=N072YDmlGke|A!N9_9;@YxfehTZVTCjQCAH7< zJHr?2l+#H;+IstkGtngwSTHJ_e`@&B&T+B0Uh#?B%*EU!5!u4?r|4f}fgDP1(^)1> zex_q~Kem;(S#Rx99eImYq8mvg0VP*5MmJ`Rv#@I>Hk;?Ms`&dFX-|e(rwm_`8QO{Y zER>!ZshLO!3fW>)YkIqHns=)Ju4*WZcLB}uptQq3^}(zZD)9>d0e61DO8}wR9w~sD z0-Eg({4EdW{6C91wv0m}eVV!oDSeKYp0e0A94{rsL}P?$m`GX>F15^-u?wiawePVh zSG`=EVfUA~xO7?h^D9me+2-*Adx%7R?7VumfT_BM+4flOIh+D*MEOF{c>*t(iVpRj z$0sUL<-t!^3K&!Xl3_WswUsQbEQnJC4$({#)l46AG{(Da>~n~7#VPMCTB5(f)=mqp zU&PZV1GIIh5aw_bYOb1`9)D^Ll#s5p*w~`PS0|!2Jy;HFPR>=I3pUldUy2~?Af5cy()a{1Yk+Ko@`=xwR%6@>DPue>tOkb zxUX(a2`HFYd201j8{}f~oDA_{fDCmaK(kO2RKdanim=_W#i^8M(8Y_svKse(lWiyt zCKIS-VCtwaruP12n`a`nI}}Y5^MfWBQQST8d1Iuy#qLITl(oytpv7?++6`%_=v#32 z>qgyXW8J0nRE*Uujf(A}08a9P1)BwQcmP73h=hN%5_4KGQ!(|IJ?`+oleQdzu@l0T z=l}MfqO?@Ior3rJ*n-=yOJHdhFxitCY^k*A7}~F_(Q7|9zrt!IpDI|+%&@051 z)7jH9$siP-QM3F4_+16 zIAeBBC1zKw-dRYkM$uLtQhG*uJKGoe%199gS#V&irBb{cfVi6BNjuBepFFSy4o`fFVZq_u!H@rst8I=1a}Y+D_pW83W5PQ@MDwr!_l+qP|Y zpWJcJ7<-TNRP|DitJYj|e*d@5f!ywz$q&(_OL{lEMlFe|^>}|xrG^keiqD*tPv`KN z)K8$#N=tTfr=Vf!sw4RYj3`y&qwIEnN&s;0a?`NuVnhMGY~UX>_BPs*qi$vh=l{p2 zi3kwQoai-EJv?$vKPbC%2>F&g$1lM<4c~*P+pcfeRDOE^s>!`eHfzD~Z2mnf?vt#R zn;ChO6f>^ow`SI#VUFyv9!^^ys}5U#QFyN z;QR$cG*01!{ehKvUXjE&Xw+B`RDEDH)9arv6qg;FU2ZY^K#JO)8m#{((V`+xRvOQA z8)g?wh70!abmov^M+**GxQdSRG8j7`=OXJNgZqL>SI#tNTI*YbMbXS?bzYZUv#6qX z^S1#HoF)O?U_b`=MS^hJ_Aqseh??1@_I9B=mQ9flqOvWw9kpb07FJRs>DqGS}pTvfzXxQssFyz?OFW<-RAiAlNk4 z0rTNyQ$7bM<{EK3kekK58Pz@_(sb)#B$sP%-@f=$vgr78K<6HV8F71jU)iC4KoW<| z2*Ric(UjW))I}B3;E~!xU9H@~>}4&F4)Q>Wh#g31G4|;;w3)}yk)vVtF!lfxiTL8F zdAkn+C4~rD;Vp5qg>BysH^5ao>?Ag6FCP8cPy5;P&yOZ)=xmOAF_QnfFMO=C%UE}PjrZwy%t5# zL+QL{FGc`5qQe5M^P!XBoTr6edJYxD1Q20iH$%U1(mQKfU&OOAja$_?k<~}(&#vPR z3h}V!R#%-%oE`Xew_CusPaz&c)uHd88W&H3)6TJ^SdP}Aq7j25ariV-fU%2H;*?KD zFJe~{V44r$CK&d2V#_Kcu{nX8hn>h6eKHQ1kAlb^PDTD=jCUGcv5wc?pd?5-1Q65( z>FMU3y7#P_{v;!^oCJt`$F|?c_VnG!x6Jk!niB1IQ=aGJ(40$R?m*FVWjfwkN^TTt zUFvQ<*Pio~o0gvA*>+W<#=AB9i3SN>wMBay0Ik5RcUj>5k;?do9CwN9B>{{hJmGR0 z^|I->z5fHv&M=X`j`?3GWTE3q7yuqJ$Jn6$PkoCJ`5Y&&Uo{l`u8u%ls<8s)& zvqaCEf4}Zy7LQibsio`^WvW;m-K`wHRb&7sL#7;y@8B%50FjIvdPajY)K2*|Xj1u`v$W zC|$zqNy%mq^QWZKheG638)uDo=bZCRcoX~_j<|*N8BnFm+34ziCRO%LoG(T~gAtgz z`lQt^{JmJw0g=e&1aM?~*Frm%+fQ;mj{T%ZI!d4at)DUfTiEpl>^;XMM$1Jj#bxs_ z?m~rC!d#Y;<~3C%Al_0;M|PCZoF3C*ir$LXHtSyXOxCt73A2s+M-rp(1t`xke+16f z)UNH@N*7-P;N1GpS74eIZ2fwjGAfYoJQhb|*vjkK*8*FG2%xT}qAAvg|M9#oeJlBc z^sizKak6xd{%@|P2n3G7nn&zVEe`Q>#A|ga+ZGMjo(ymh0w)84TG;#f6t6bmx zA88ShN8nL7l!SH>J+RJ?JB^!4Dac!m(TgPo2`zPQd1Z@1TCfXD4Hu|25>f*c2w1c; z1Zwdhlt~OQAHb8#zG99o&V5y++#=}q#VYVQ#P|;BK&dV?q4l9m$vpp)X~F9O?Y`At zP?>Yg{=+k8!YW>1O6Is>fl4_cqZ#IqzBFRX=N$Hq>p3oHYP?!lF8 zHd0)S^9B^l%uOB?0_j=p)EeKk%vuOzXieTHXz|M9a`b3PUq+wpZyUJd8z0QmRdqNy zHayw00BzE=x9DZo*>U?I-YzBoF#KNqQi*bS-M#z#Q5Lq7QC(!$-w7_V*$j1D35PO^ zVx5V=wZ^K?&Y`CCuVW75$|N!bmSbO(NTSF)YwQNR!1b0`&uE~6lGmnb3(7gRNma8T9+!V0qBL7Pv&W$6VKFg*JVLwg>P>$UdH!N-IJOch5ue|d)g zP+Z#PkAYkMP?Dwoar@H-wJZF?EQ8OD9!<5)Q+8`P(GhLv6@Oz`Wtf5*@(d~QWnh2l z7fValX<@)oDxBFLCwQjMZ(UQ2-x9%&Z%IsGov1L0>Ovmw^90qqZRGzk+P5p1IDNaD z1CUMu?pf>el)K)IGqAr4hIAEn1fKwD?ZyZs+~Pm zR{uq*3>fbWORNw=OU;VgV)_74fwDNVn zqX>pYc;&;+;Qn69eeh@-J1+`SH}a*Ib;)UA!q?mXnKT__$*Sl!E17)%GGClv{GUma zMUuw3?U%XfF9PbdJvIPK6O2gZCc4T)#Z=n<{RaRoYrEHM)`q@rsg8U-<#s_~*(+ii zK;Jy=c7;XRVyn~UeQIFOcR}#-h8xNsG}^Rp{B1aV7}sV(wQ%+LDdEfH3bls!UxmPI9Z#&>g;`kH#}A%hlwJ` z5SI%tgh9+Lius(acqS5G@Oe%XaQ)qX)iHZKoZHlZU*br@Pnk4@1~F7C6){nr%Y}{q z90FD2chY^*a~UJh@Fej$+p#qYL(oN}lmfZATMFFl+PFyuBK7yaC+J0-h>p$m@1Our z%t?gVe&*@@adnNt*BpQBFVWX`A4SaaiGX9_CU`gC|h# zaBA4>9(^22Y_Xkz7Jkn6@Z{0cY?H7wq&p7d!-|ttd+-|?tKSa$HD_AA-y_+(HXpA( zSA7(pM8{H4jFXQx8C0w^x8DQ9RlKG|y>qM8YwS=x_q|BeOqq9o2bKnJH2eqv^+FyI zFf=QXZ1`I;FXZ-c_R#ts_PAvaWp_KfZL(})M5bKxYOqXnuNOPa5!SCS)3M!lSyr!+ z=Q`HYv{o|9M-Bh4$5Atz^abaV;9_=KcJ1elxS+Bc%maQ~QVo5fR{TqqRGe65yd=Gi zH5^y_uJ5eEny`6@t&^)fI13|i^r3_yFYgW46X(YkHdNxhq;Wvg{kK(RS3wO#eeA1p z-eF{J0~=-mA<7HJzOo24rcgp}o0@m&7ZxVSs+I1OMRMEvUX8Z>2UjPl)YX{yx}B3| zZjSZmpm%jWwbC3z(S$T%ow(i3QVc@!jl`YxTS>9Rr+^Xvmf)TcXmT|0-rmCF0)9cj zx3@%#O6UD39=ycT5S?MJQn&3mHD6zh}bY$)IulRIcZu~l!4O`i2OS8Mzjurb_kMGXx z{zb_6&yK@3i5+7Ap+a^mI+I2mST_A79FTxDPs{~1yG_}GT671wsb{1U6w~eQpegaQ*L~QtYz3h}~W%79knJL76Gttjwh9 z-U8eORJI+jOVQ#>4JZaze05q_uS` zKBZLWR%?wQ8*SDrB8#KrxacdF!jPhwqHWMq$=3&@;dmy9v|2+xM;ubtTKwCluxOII zH`@e^24LMX$gj4QeOHw&zLQ`bpG=sO6d(mGV7=Tkp?_$mU#EI@)h6t{x`V|ms#Os} z7=_xF(?WYzY>8fMBc_4tpKdN}*y;P}pKadcy>UEE!>%fEtIVlUj0zIF1tys|Z&9_p zWWfWRpt&?NT{P3}wn}q(J$HRP&mr1XO+DFZMpIZ!I4ul!qcNM_ce$v1i72c;LUIga z+b=lQ>q~oyNQzmr4Qa!doG#z=aTf|IryvMyxKDFYy#m_%k|_1sU#r>mfz7PK^csh3 zsflivuHYeTz^YuVt;{v9t4jf~vK~Q}G7LzyJ@ceYY`zzkH_b)mea{P;Z8f_@kCvm_ zW|~QGsK@(jTN>YTduUp}#LdQY19M0e*gg+)&aHo?HkLKh`X|dAFJ>`$##YjT&NVak zS){u+m+6o;hkv-GNKp1^<#)G?&d?Iq++UaSzleK!lpDOUq@l}<8ePwJx6ZynM06NF z$$`6KM0mc7`q*QX$zHw3x*A8 z{fe6yC#kJgf02DY45noWFqw)a!CMtPV1KE8j4g(;#y-;q6L1+)k^TcP==& zo1q{in$C2x9wvO6%I5YecG6LUNG{E^SE8N4>GXYkdO<6kGuUg??3*}2qg}+W*CK}p z>WNigZ8h_PW$<387L&%oaxP0<+c-GCW$)(4!e-|xwFX^rZg*0~JLXPyBFXZWO-_Ug zmkQ;Ne{o51$yQrIP>{j>=z7oz2Ta`#`)?py>SZzfRTzDTvnQ~(J8E@>OQ|@{TO%G< zbc;X)v}9KyWdek!YuD~7>oG0qQIQ{jW{7TiRy-H3>2Hr#z18YKPvNH`rC0)u!kfE6 z9K#XNGJ{S9S^?jJ!_q8IB`UBBi>J446;vxcwFvdhomZM+RM&l*W0x zdz(Rc5)M|J5xb2TuainhrDy~%2!qPBfYf5hc3Z&1639Th(+3gr!S}Z2$EybX{Zkg0 zR?9`NFsUoIA4<~oPI5cY&tVmoPdrfzh53oD(&y1_DvRv;B&vj|{e|%;R!E{5Dj6j3 z>dehj%}0gkcZ+`hF>m-F$42YuKz{swCxH8upZk5}1kzMj4JSoZj8b>$N5iS=Fmop9 zZXp}G@kbG_PLM>MpX-X*Yy>SZxqzW;wQ)d_u~Zm*epjGXodEgQfyiBFXB8PCHFL>O zsEGP%HRvhPO_Fc2P@2`Ua(%+Bx94Zl>5tvH*29@wT}T_a9A6~B)L6z?+E0c{mBX(%N1U+BR1`Du&1XZnn%Y{!K zOMmuLh@!LQaiIm_+t;YFZNfQ($}BuE*S=pzE@DDYy>{>5*5=@4(g&F96qNH(6lg)bSLNEWkll73Jocx{bAC8*Tz6#3ral-AY672Ln z9}s$Rd@R*2e3!e-*IFz9Y^Gm$9sgALaX+Udy`8Hw$jgVGcC4Nah7*%AG0^0*HQMij>$` z`ZL%BG)~VlOO%|G*@R9OeY2@dZq_Fb%L;}5 z(bvV(!O4RUqmTWkg9j5Q;|#aaz4O4ys4SsZ&=|BtaqzeiO?zAhKsV>#H?eP~j)vdn zcdov#;+$BUGKUja1~lGa3w40>j9y(wC0h)0voqj>dJngez?oanGwfUf+wC9UO?Vv* zIf8uYol+qUg9xOSyU&TIsFFv1Ga*u;(!5XySQ|X=?Ce9^c9nZ=vm7@)@Am&Kb!yMo z{L=AsiGw$m7v_%|kaMh1Z`;(*nFQa2ZIr&k^Ma+;@<}U)mXR&e({E9Rrh;5UF= z3!^FrPP{{$#LuCn9PiXNs~bB!H63ZyHinb88TVql9d47lN;?(-;Alp_)*d~&mhuqA z!&eJ&^XI==a}@kC_ z^4VaeNdUh=e31$hfMA|bjLHtw!Q?lhQ|IL#LHSLbWgffaz5FVj`fn2-0mJMo2C<4(xsBs8FG5 zhbvuDSF%l}dFX+5sm_^Is(6pz(NY7_P==YZ@_L5V7TaP97&yXSCTrFFSvJw|9Fn@3 z9NM|1RX5-@{axH1zvNP8rKGT4L*!$uImr{fFyzOhkHLjpy_i&@oi<)C9(v_?n}-~GL%&v}BL5Wo9}KxD=#tOvOq{80PzMUM$F*7*Rjs@H%(M~&d6@e~^j z0WUZ#&t@Bs%>HvC9*^PBA-W!00D0Kyyj>m1w>wFU0xrVeX0lXV-Oo-|8k%YPAWvKh zN=*->{b(#aQrQaA9uyO5k{!3ceIF;e4Yl|WJ2XT$@gEo$Z1-XvZ$9q8eXf5Jd#L5B zOz>vSMaB}gH9K^|3mId_~BS1+%)A2B-$Rg$au4%II+P1yfp^v zj^4{$jyDf~#*gO%{BCE7f~(Pk&Ooo%J`u(?VnvGaTN^mE{j}1fI}DC!7|5>KUCPLl z?rWlF%H#a^g1-laoP_#YRrfoG=P$C$+8C5sv4QHQTJ+=1jnytGCY`pbEeDy4W!!tU~8H*#&?+DfK{U?=e&&wN7 zP*CSrUF>cZi{W$1Kp)tdG!iaDF#Rdr6>f^uv}J_hhEsdfiWBx@CgYm8+g4kS+m*gU zvr>t*uW<_!{X8*+Oi3U!Dtp!u9g%#_rEE!(I06M1g%O0wXc#(HKk0-I>Q97yDq)m) zoL&t?eFZR9dlzO|83+nWIhj&yV4B=fvM$lTQ9jPS2v!9Rh>sWD_g&z7AbvclWapR~ z%)BW>*DpSSi(2|KEXq9P@J;#%-Xi2SP=^mQd4~*NwE9~HEiY-B>c+Z7Em58N&E%)J z+d`>0x*s?G-pe=jAe8B`GFhFr(msYp6+VibU9$;Y{iP;y$+c|uXVe6LiZ&_K6+Y=p z_{;FfkQO^bK(IUo3;|~7A;z+4hUZJI{g>rbjch(;5gx>l$;M`?)`<4^4;~r^NjAES z8WVenH)wy8b}IueT!w@hi*x-vv(Rw4hzHRU--kwByM`C}XeM?b@C~f!+Q>INa%wt|g6=eY7$oKI zPqeCWJY%|T^+NYP=u9x_CIYtAj!@3DE&VM@A@+x6k+jRXI@H6O?)mFZlY`xeE!M|( zgR^lPfDg5Ak(woX^FpG4MCc@RwxEyghH3?vx_5Mt;`P1;tT03xv@@cyrJ?rE0YQc9 z+R`ngL77Filkebh8^jGp<-!& z{CTT?zY=5%ZlOyOqMaJbhKkCmeM7*8<=fH`@Qc@DYV!17;dILnHCx(~`^ghEjy-K^ z2`;qby~RzXU%FsuRaQ2)*;o!eH*^K=fZb;ut;qe3sjHP)Tv=Gnhx?#85Z8$B zO@*6hf5Yr(x)khK`1HK1)}3e1a^y!9!ymH0VfVtPeuDwS)up=d(a4A&cjr1WKaU1S zfSSqcdc~JdygAc{Cgyd*(ZXs62)Ju!*8>Hv8w#AG1438`6`fZfWF2<|n~#Q$44sk2 zLRu?6e@ayYeFB{UC$j8;+9&A`#l(%qj&rj6BslStdI^uGBIIw~aOpSacWR_CJ@tx# zco+E<>9IQ_vfz+4)d6zlOIevy7sQ!5RiG;pj6tYJ={Vs@;m3nw9L7 znQzrEpEQotm}1i%yFDT_qk2SlEno>;9jzX=hrQBKX0i8KeE1!_%jR13*v3 zPu;-klF9F!Fmi%yVGLOmhoyV$EpycL9F8}LUiElY)FoQR zD0VW<#r}5}zOridY(dj_V@bagZZ{56;GO;ZFqC00#?){HL69A)WwYmnmG_;O4vdKn z7`s+HT(3v(&Dp#@!u2MO?pOECYXAo08hy>qMIx6UEMiG`Af&}wNxopi-)>HxK92Y^ zQ#V&UTJJ9~ya4m#)&2F34(H?1OtS}P4xriT{(5K$ZOSrMZWt@r0capgY z-uw?8U8qcxI)xj_le%P0;BOCU+DApw$uJJ0-JE>z`nMMRqh=lH0j_yvPasW3yy($b zYk8>Oc-<81HxReYe3>|EB6B>IBi6+w(GVkfBCAUGD}mv5zW^sMRZbs&{(duUfI0oH z=eKOyfo%Dyf)Yk)@#q;cqr603sB#4XZve3%TQMRGLJ>k3ae1OtpLP;1xY92=_wh72 z$8+a!nR%+9<^a9VHAfEBJs_)-iyPdMd&dTorx%tT^g=@*n8lP0ZJ|~VLMmu7L>CSJ z!|;3F9Hcm(DUq z3&N>9Lm7+VhTSCH{sP?e0X<2rsT^EbJS*j5R|IXny^$s(CHpbvhK-&4lJWgYoyL^ywea$)UmlSp+>c(77u9(5uA3!!pu~|UYfAx zAK|>eTIiZk?#b+#C06E(ZBV_NC56x{w)Z)c2kBK{y8jD;MPUSt~!I47!-x{ak9YUP^Inj^X1`n}ge#BMLpONY8?szcYyom0!73Fgl= z7(FT^=}SE0d;`$%`~JPzjg7u1L$-})X|Q3{&7;$n_6n0q{T7;$ZTD*?9>$q=Et=7! z{g5OiTa!WRiG|r^4t=VkKIwgwgeB$)S&Q^(Kq=-5l&*E%HQp^Mkc+j(3&8;i0@^z& zJd5I_Gs{s9iM#m^{w`{5SR6XEX>IcWJqFdeQ!m9Z*#kf%$Zg$!@B|8NpQv7|Us`Ck zqf?+j^6~8nsjVfLmR}DPY(*Vz#a3|*%`1UW7#`K_$F6XtO@q#0^3H+4iBrG^B%Bs` zg|DuHk#!YuGP+5VZd9KAztq0iOoa&TRe#O01z&2AM+0`e(~+}W5w|d&s|8Ao`N7p* zKFIAW)PDf>HN%w8;H3o1$Z-cV4nozan4@ac`no-oT2u=}^Y`RS+r^}5Bx_HhKn^Ps ztvvRsI%Ucz^@z;R4{R@~nMJ1;-S^_4xx1*ssvI~SWmR|T(6B}alEM-QoNC!Y0qA6g zl;Q!r>~%Ey^UgGL-oJ7h8s`MpKO38i`hW{CpAYHpFCme`{5yraAGp|3kuZ(uD2UD z{_cG$MZGIex&1_w`p@Mnqk6Q(R=>`*A(%LU01IJ`fLKEbuhNMl*fXF`q7A=wA(L}+>`(C98(k~Ozw0vA^Z*@6 zWP|Q|2nuso=sk|RhSi@vAiL799c`#Aa@eI5v+>*+=WH-Iz%bTEgk<-W5nKzqWd?qD zOO1zFlj@DRYs<%(qFEO)(-iz^P4hPfs<}@a9oT6IEtg?%&7ydp4b)JVAt(7Wq&wgT za;NaE3sq!ZDMw~D$iWXvK+@7s19#tsuKL%meJ}0=S6~{t#J{o>=fFVshjIBJCdd|I z-Pf;WrCHu!Q_*?GnRH|t*&w@jYJ*+gy%Tu$I|;>eiW0yXN497jlIJUpY_)jxw`)*r zwYqtsPdwMo49I%9m&|PMKNzpS-vDyTytJ{N!M$dxTJzk|asr8*lQwRxsw6%lLcV49 zv#-^R#29K>cKsXTSUo1P+uge6AHt_|sfLfSeebsGisvThVHpUI)Xb%)E_dbjWVfBI z(JMmh9qRUR#CgOER(WB(CTu(0QMfjKKg5pzJeomyKAOq6n@RO$nkS5oa0Z%6SQ&y5 zwqJ;{cnMrvte@aXhP^1}6?ugRf}Rsj1(_&>Tv}6%@QtfSD<}mBJh+Q@E*D8d0JC zU2fXfZr}Wl3^_T)3fsVY(gRLEVCCai{2|%@VXbUiLOnDm`(k&A&kL4G>pc;XNW?gD zRJ$}0*Fbs~#@aRdv*t8SY7rp(rpmn8oKwoWO+02*!Sl^gj5&?9Da65S|6?8KZp?-3 zPn3CK(nFxnIN3_*-7!|hWK-U&Cf1gV7AK`~f)OBTQWj_o4%2onuma4fEg4drFs9G6 z01U!sHi~Sj#gIK{ABg{%&Oc|$s`OreJ6%8m0STe|ipYPp&|RD@tr?6LwY;3xMZbJa z6E_6WOj3mlx3uJB%nq6;pq^7`5+>TqkC`W5Rg71t5dGQ!UI`i*|#+-?A_(9;96GS4hCok8Hewx|a(m311v3UdQ9tx=aY)EttEA_35-xgER3yCy6`)0a!jc zVQMjSnLRJwM2uT(x*+5C3FdGSx5m&Sdt<}*eNezD2g2CxPErI8pc6rnFA6o~iV$Vm zO&OnRvaozhnM=vL467I%aUw8i(UEwILw6-RV#Aa@)QBX+sS}ESNFsBG`>`Fp`KJc@ z)F?Z3dz7g!l!|hKtt%5+*l>k8j40ed&Vs_k7d@LmL^3L6`eG3f9q79aJw7(wolJnkcKQz0^0mld$p` zO^pm^R&0&qG~7Yc8$d$v$5&jtBv1e1jsti?!gh4UPgw&jymMWF-p%$qYS+yndOO>- zLw`nPv{0@@UxYHzp7ZcT_K02_U<=?@W;v{fgQk{ zY_yysBMTH^vT6G^en`Mx$gy^O5}UPy1ILo zVcWpa>A~CHrXfRv9d2ZC0NsZP0=|`TM@hLIfo0^@(igxPU~e{TgCq77#l}<{2w|rV z)z5+1<7tUdQKiiR`X`L>M3r_h4Cc)s+< z#G=53{RGML$kzy!Flr;mgo!zkdm@vVqnHJ9Z_4c`#*=|kiLWlauvgpP*b6w25C<)# z6vkl`*4M@tvGb5EipiqUo_H^NAh0x;y)8{)3V7yGHoW0A#oF|ra&Gt2YqNk#0Q)pfJibjsNMA}rgp*p7~fLK=E7y9X)6pUk96 zc35R7-l-XbY?^mD;-E*x2^^1nocjA~?2lqov0=+l#EDv)nXT-7^1^#cpe`^ntbdN% zMe2Sh2i1qz9QI-ubh$O0e#d;zhdb#y;RQG)B2fv$275!2+C;D)OgBLMD?mO>EwER> z&*{e}Y7+ruhJ6EC=Boh-M$v|Q*`~$Hd0cqB$z=AEKgO|5q4hhzryJM zLikBcF!CLqeuhsa>@pqyN;1x#Vd zQzf945o*;W!Zk8$1c1UAg!8{O^m@8j~&lbTGjjV1E^ zan>QAgrg_24P>99BSJmfB$jwc|6Ir&)3_1*jxsvJe+O;SnmtzIejFXU-&*JOn=gL{ z>hJ6u@q7j(x{OQ)vvRShXS7a;@)+4Zu9W9(fENx6(h%4+*g=Y=R@FWE39`7ofc|{O zDp#qAC^kfwHCLza+h{@v&sJ=@rxUN7d^4907xUic5P(Gg91R-5$D2M!dlo6;?~jBN5Gp-*DXuqrH=9v0C$%2a5~37ZS=Gx<4YIk%drA*Q0eIt5*6 z_j+5kG;tdsS=^^mU+_|Eeiy2g7%l-7ig%x{YTv8 z##|Ut>^n&MMM5Dgwi+G?1dw)8Dbe%9nBUpi3mgx`aL@GgBl)5sq0MI8J)e$SFzhT|N$}ucm0Kx3y0}Ercd__C% z%cXu`sng=N`PRH-PdFC#ej|?1i^(@eiA?l^ckkID>@tq(o8==qU72!iqbsbq-GUly zFt8NOikVuhEb<{~GDpp+HF3;hg8WiLfey7|@^FHi7V|?KAKBcj%FH|8x7gZfI83kO zRrj)@Vm}VoCu6Wtz`kzn56_Cz)N{1leTky)^$8b%PY_yAFg@6-=UiLiO)fzzkr&68 zxhoVrq%{T-ZW0@#(pZBlJAK(>U{$1b2JB92Oa_765FyUVQjMoOJF;ojkoi`cz{JYq z#4et5yftFE)=k=HV;J(fSwDcs65nt*qQ7hvSUhl73qZ!7{|u(A{8eT4m_TO2eg+!8 zc1eOoAH2@C8I=I5m9#8zk8F;s-e%CLIR2}MFN_}^56UC84IM^0i0SLn!?uO?U?8A$ z$bx^;BM?T?>eWvihP;uCdHJ)_&Hr%b6-|Nl+}5ZL%UYr+Nzx~x*}7bN0{4&6DlwYa z$Xr$QqSaCee}xuSPhm}s=cIoUnhxfJzM7XBXlxW!YGLb%o@Q3wqfLXLg)0NQkqgra zJ7cw?yIZM}E%geWXYoNWBhJFvBhcPO7sL|GiXr7^X$5;fke zT}4Rm1AF7zA1`Xn-v7^v9O3<$x03bUt$pIUb}X{BZUiui;#_)=XUNs+!NNO#zULry z+b)S~*ptrq;LIxOIHlvk6L{DA5XRfIWTyU+m#}6$G-SVWwcPQ!i_CL+34lV~T@P~+ z<@Y-vBS?X=_`0oB^qi4IC}*z7=G{xiy|jEJeSKI;CL5alXIi1n-tz*1gf%JG^z3Q*zPm>hT<`kxLi|kw<^BCT zj?!_|Ys{4_jTY)W%Bq{q58wmKHQyyiM;p8eGEu#L{iFF?`}Hrs9-9=&xu|WOxyqSZ zLTF+&3}-D=`96cacX^I_`tuQt^j5yg|w5RT+F2H(mu2C3_nCPwI z3+iKRv!dS3BZ#TF4m0aCuJq#|pV=1cqHoCnQm_n&W*^QkFBk?5v4t17Hcdi*0UGBu z0S+(zH6aTGY0AtROd?#Da5cg=6(kJS>$Hc;Y@hjRoP;uLkdaVlh8UCPMF}GNd4L~f!g!r{|3Y)+=@_r8 zxuc|ZQu)~M6RAx7&3+%cdaUBNl%K5%kt-u-XH->`F!Pc#O?f|BFL?}qVAbWaW{B*@ zG9XYNT*h9FjYN!|y)h*8vm^v~ITMRM@_R93goJWKiyK8A7=xf5?%i4j1VRh_PRq3s z*DD55NHm;qPzEXw97A^69&a;|lO@D>T*koDG=8|XQlo?hed`V~nE zM#-RB9XCIW*F?#;Po=j)X>98iwuArni<+K??}xGcJP1&?pJ1dI=Z-_SlQ~#$cgr#) zV+^7|l8MYk+PG>3eVEX&SWSO`!Sz0>IE(du!TKOSX<{%0c6e-dvz2<>NSr}TNITvqTw7&HqO2d01=tuM>1loE6 z;m@0)><85mSnDo8+&@^Nx54+*1+P(*epQO|v}Iu$K9!0G*&d zOGXr-q@34d76(aG+16y1o_{#uVK$EbUvCDv$wy$+kwcS5Gu@|yq|f1%2vca1(7fUB z_#BkxTl~CLzob^J!qyIaTwZd@IrO9^Jx2`oix_&Ql8Qwh?fOw;`syGSv~9|9>qqn* zz95M_&E5>}FX~R+j#1s6 z*4BU146Y7P>#nR#_j&t6@6%Pfgm?U%#5)A25c%$981oox1v~Qm?>G&7a9^CX&7yHw zW!WLVHPUghv;GIK2sJqsmZ5#bnDNz=Ix^4ZUsd`;#koG`*q@U&5<+G~);m3sjo&jL z&6J%^?6^4I>ci#-NaRRNEXK# zuD|8$6~@|Ucf(*wi+XN-(yNv+G5qMIDq=LzFpR;&qPrD8H@QQ!&fB|d2x;50%+h== z@*eQ^4%ehNEFy#}Ah^z-^#tBlO51NTu-*2xG0A<5M2phY-uv3EU6trrVda6vJ3A`v zg77!;ln?0?N}F6hg^WLU!<#UFlnYIOoJ9pd5`zV5j`HQBr&1yOe)(bdYQRgyRQX8u z$wbtwOxK>f8!bos?^a2x9^yaP|MUeczxo1MFE$0kU&9vDXQHZAE`_WEnx(jv>d8O4u#rjV94x{#`baia37naQG=IicjkH&)uL zT(T4h^^EtxLn!vZ2k(+;`fw9blZ}0dlf?$c9$ipO#7RjRBR0}2i3j^g{5XfR>ySz# zMnPz%%&2OUK8SV)bV-t>(=7%{6-$~Qi1mg@5)JzkPPtp6LeLah7g8yot|;&&rli^6{ns-kd@=-nQ7#&43cylA*l zPQtC6QDd06wNl7bnjF{oOxz2KSEn4@EkSVR>Iv1sd%f-Kx!|*X-n|ZNk*%rI zJTtk|Ju@EygoFXdTp^@jNgh0GOHcC9HU1%nKS-}tNG7`HN@k`>iE7_EutOZ2=`iFX%;(N30fS8`v8g%+27qesui5j&2mYixS7y-2FN0TAmRl6m15SW(Yew|6(E_T&A#P*jt5a1WXeYmSlWeLlJrvl9#-|8tZUVc_nXz>dXheYHwX33YKtz+PY@G zum)Uq8$I{9)WElEexMD>tI!o)To$lQyuXkYa6ub}4ZMjg5Xg8CtTfdAzOV^5^wH{B zv_c(s7$l^P`xbbgTYlrYFg3flYNpecz9)SFQ)CuddoO~ zAO2V`+=PF~Y}0@r|#;cZ^v56^CmK4Qt&p^;DEBbJ=lemBc6w!Y+-;(=ihLq^{yn|efbkr`@&CCMg*<40^Hp``m^ zC_s|l)+;8Y-TcFEqE_hd2gQc9TNV_wnNJe?L*@Ysf%63ua$_O{H-Z(lz(Dn>39uFcNN}o9T04u0@zK27KWtb z_&d|R*6hJodIH>LCv8@#xYRqNAbg$L59e{vrZN7ftg{S@D_I&a?(XjHBtUQr5Zv7f z?(Q-KcZUh??j9hxySuvt4+JOQWcPk|vp4YY=M3|n>YD1FKGh{qNx160kgQK1OpGW2 zkPw*8lqNwR(*k`_F4t|#ZT0JjYS7kA2ss%xuj)R2XhrBB>V>QF+=uIhdi3Az}@HZoh#e3RJm#!NJhP`KKb<)SQS10tHZp-?P{ zu(}o|4hwIV5s-IbsN9yKO%ue!k<6;Xu+eIVZzBD|D(yYJ^cDzsFcz0~9CykF0m>u1 zZ|m10KB`b$hjjV|ev^YLuJT~?7>ehK3H(y~!`nloRdZK&JH-p)sBFu9 z;7#aCv z><^0}SMG}nvntHCCdT$En|2454L}U1X0lo%y_jCd<6i!-myBfj`%KVsSp22iGf+{@ zRC8U|etp|^F{o{o+d$T5Mwq;+NN@R*t#c)oD69M@A}!if@x$6@SUPU1!vis#GRQA` z1;Qshwf&r6GrInAI}c8Y%tTCX*T399%MRT{gKu&ta8}A$ z@7#tn;|Hh_8ij=HO>Qq8oB=N)fHQ}-O(Fi9g~SQ3=FWAX{$d|2H}L)<&hVTOLpR?>B?CmKBZfvlp8$MvPnh4Uh_PTLaC*7QqaID45MKC zzB!1sS=vumWM2r&5e3AghdKW^@)eF8ZS9Y*n3xS3m9U5#$}k_Pt?uY*%q)L*3uRzj zM>gpUPCUloJK^_g)6hJ+!&yLI>*Qe`MlUe-`5yrv9u;=~xxqr;jPsOoX z#q`JvCp0zb2NDiA*Dd)~hTeyd_krbgqsnyGRseK$`El`50GK#E>?i9}$J%`1#lk5A zJp))X-6HJfYAjMHhonv;?r7#X7^mELHSZ7f6jq2w3jgf>G*abMFB;*c3qaM98xucd z6Jw-l>XTYx<2bCFu*bQHkm$5kCAwv^fmbIMXR|?`E>kA1WJ^L1z5`z7C(`Cv0*Y`4 zh4l2h?#KD=O?}+B{G6{Qre{;g7zD!;fEk7HVheQXJB}T~=p2!Yq2`IkHgElS1XA_C zzL(mv@VYv&lggn?fLv(%J`pvSkt4Ay4}f6*8(=9DxG zRK{9x!RgfZ?#zADkJXr!!N)OfzeCoo7dO!^mzYf8CmUG;#l)Uk6vL%xRgTDq*lZue z?ArIf0(FKZfY_15@?0*!{PyT5Y3GxhJ*vQa3g55?*_`qS$Qvooq<`0 zZ(}$`(7pHc(r&BCk$|LEQe*WQhexv~g$gW6`&$g%KP54vnr)73FpPYq5-h)vcR8Zt zslD6;&gCg-XL|&p99Vcc!L5o!FgFA)(;yivJW715n0u9Qry|zlK~fI32fsR9A6;tS z&TpQwUe*u`Kan9y{%n?NDw;|ts`a}++N-;hw5-oGlP&BXs zBE8j9j2Fsata6?cW;JPnpG!Q>jFa#o1BPm?wXo zm#ufp)A4U)Egh^|VXIFvO~zh07+^>*8?JtjAKszI{6+=9Dj&_{{?(H9kpj8yHKL;# z_D%*6=M@+-KMLTbKUH zZAFOdEQ?IDB67ZnTymj69dm_FaZ)7noW2Y_u0Wl5#cECl<-Sk2(+1^gf2->V+lpwa z_6XZae5JAOI&UFslOim0A*)vdSB+if%x#mRIdfs8(=OjarOFR$ag-*!supGIvtYII zGCS4`ZdSdUTkU4A!_FS|=V!ASo#Pw?s46PVo~LfXb#^qvNu8H1zfD2zBO%|g#GL|5 z!kzvONN^k?xaF&Gzv!AR|4Dt~6OB*Mu)05-63q%Z0KUAMCsvuL6&M#nG&>R5W zHD_RMZUvnjOV|}@8yf;Yc{1dOqz{tJ$H4Vq1hT&Yf!Nn4H9jQSNt4H`ClB&M!wg)> zGdGd=P=zGXy>)DzIX3|bh|YdGe}l5&D(Xf_ zwll3|fb}_QB1itZnRTuA>gvAT(_(WX{C)V)4lIwLB@>=+1jpnMgJ)3w1cq12s>QSC zuno;e-pCx4AnM)fygtMbenBrpRt)?qfAv;ub7gcPT?+lCFTzHzl8A+sl$fbo=>4L1 zPd{=o(2JG!_)h(2}~QT3>oEI3hEqW8-U<;5&Bvv7p-R=i@{X zTdH#41ni|5A$c3p@%;SBNgnUHOt7f!#1swN>JQNsi6c9a$&7^?<1ci@b6p>@C2|)q z0kZBlo``~xbc3^bmNDP&Ldgb1a&MH<1il7idJb(yYH5?MqeEVr(viSMn5hG~a? zSE>VK`=_{Ur4F^YwBndmc|-9Xf=Fp4Y3ff~plD6Z1+0 z!DSP-OSl13Qptgn>snHTg{#cZ4f4ymu_rBupf%%G-LEOd1z`H9t6=Nw^(9qP(2b5~ zpTsUknMCI07EkDE=K%IC{@OUz+svvRT=AG;hmgLs>47X`b?9AWp*KGA#IgpxXxU~M z>5@nJ?mO7~D3ysWs?PP+3FETq363I!9g+v0V%Y%cqt{Sr`Hdi+^0Qt%&a;Sc^I7R)@<{a z2;413Xi7`*YizseRkYis*dMXwR&;ItCvf#w+-e-?D{gFoj=~p|JJmLJaI49*9R}!{< z?0FuHr}riHN1LNujfb}5>n!p=C9<)XFlE8Pb<<9 z95E%@qybrGIDh6_GBEE;utotiF-S0bWz}izsFCh0@)0Ap%(+t*@OfB?5?!cN*F-A{ zV@&I=#7LM_$3k>R_Li8KkEy=$^Ks{yS1g4^p+tJpP?Nt~Ystmr!nv3eKz15fBC~~%k>h6Fh?{6PCXRTQ1g`;jcx{1MRV9}$ z6V5)#xB^q&Ye~?Oa4za`QZ|Y10=M?!%Mb+kaumLHv$vnc4!#5)JEAVI%yaK!vb5~I znWQR-Pe&iIjT6nm6TnB!>Mj21ac{*fAVG8E!Y$zOCUt6d(fDN62*6K_5~F`R=;Fa4 z=p*1cLzTwntv-3#EKLi@)uA&RlOGyWUnb4($3dw7tnAoh;o5f%^(cmy2XJSv36m`s zAHrTx00}Tt*$I&3 zE;f;2kg52&ZKA_~&@kfZX2i|a61c@R7eDDUySsX@0YAtOK+@p_b( z$?XdOR_Jaqx8MNMDm$$iZKA*5u*=V@!LIvlWQq%V1Y`tPLndjkmjzCh31ja(o5Mwd ze|OHb{DhP=#O^V{l`iM?Qiq;09-!wb^${oQu%QKL8X)jt^2t7HjAk$qze*Iq;T;1B zH5~DobSc_)AA)jGmK5A9cpQtr*Dol>`)nBq(G28$+Uf>aNQ$ldPSpg*r9T-e$~FJ! zj>A}gop%DC;1l%At>USx&j6r0I-~CBM6v7}pO`ytIpIjE@o8=PUN5Z+!6~z*42qIP%3VevEQ#k{?N~;jJD! zWDF$!7@tELxIAPA_$&NBn{j#2N>yK2b&m=-PM1?TCh}pF5oFelAx>@b-X&ULD8Gb8_?SIa{fdt)owHD zc-NKE6)v{%a_;e!k30N}WlR^C2#4)~myxslU{R+g%ULr9EzNEJ)0RU`bGBGwb z54mJamv%OdGwN2^U@6fOi|$Z-BT9b8k6CA=3Z3GFcTC>Oig}BRDcD7pD+vO|$nDp8 z)1_9UyjcvV)d44ejD7a~#V$b#&i_cQ1n-^y`RcS1tTW4_+COZ$D z%*z5Yv7!XLrR)PBPKq^E48eC(d%!fy^S@#2@I|!@+~k`vPHr=x0MMjEP~s6otc^%M z^6+3X86tRK!IIpV0@$L>Otg(QeR>HprDwJxLd@Efd4!gJLeV_OkQxnplSBt$8%D>+ z38=eCjzPfO7WopRKW$K0a5g$Jza`5x^MVU65NMd#3m+yL$dYo#m^mdZ8tz4tCXPOX z>zJ{MfK{d_kaBJL27rwnJY7!;&Awie)$@1$#R>fym|IKdA7{;{4Rxsnsqq}VJZTE0I3{^N zr6c*}81pTgG&qx`q187GrF;E1SB?50i!2WO>BCYmSyniog8|N&3H8_#4js`D?cu#( zf=cu%W%JLvh1z^{(EUG=8xw7~cb|=N;geG(q?BmlAbbkzP_(5e_c)79Fn8=Zph0nG zupy%b!tv)Z%ia?`L#W(^!sv99-jmCHsB0{<$3p$Eh{ou8oD1AmSs99N)A5=xVZl6b z@$`=rNur-0sQ{OH3DgOx{vvK?B&Ud2zNNc|aPn{QP)gr+tF`y$o)V+LdZI@>35eSq z6|d~fM}6!ya_$Z1d5_w&&cV-hU=_56QNfUv$Rw~MVyLbA_#Tk+l{_kLLoS}B%a&U# zSZcNqa|yHP7WQ5(t_6MNw4~?&bxB;#QY-9~z1QaYd7g zS}662AP6r~Dmc#NDu0Agzti{#?GY`CoPm-O?0-mf9JR<>P&217hB7y;nvGitr>-gL!ey;@B$i0@|o%riLe;N261sdPvg|thWm!7a^I+M|ZK&Q)+xGKlgTh ze_Fs%V`0c)pC2ND;3I_|f6*k+VD8sM+oR~>*Ndj(860HJ!F&q+jWd2m6Zj3+y2K=# z5OkVd?c#5etsTS;n5j6fzzYXAwNCV#Z}qe2#!s-PKkEzw;s)YfGI2uiQjo7IxC0G{ zjgpq|4X5jBvT|;8rN@*{JJ{Z@^WU9+ z>_E4xG=w=QbtqD$4BR*L^H_+tqlwToATq(42AYve`BsX&OB$t?1?s)|pdd$H>FZb` zt5N+nUbN{NW zW6rO9fIr*QK~%^COe*}`H}{EI$m!YJY&ka-&-!D`2Q~y+%%n>cs0-o&{=h=ZYm{zIID4Q0mL*Cp{JoT*uuD@jf&Eny$5b=-FvJ`dE4&1XB}hYrlmKZb`ZxKE<03Dan(v55Z!{i zOeZ4pQ2<(jqs>3NB7ha*$#Nfhn&pz1XZgs3V2-LxPuQRg>l=!RJ?lkBxP-Fw#9iwKVG}IjrxJEUH&PzTVjp2d0Hxm_~2BKSC{&i~fafdP*4v zkZNVc9b=CZDrekJAGtkd*=WcrrsQ3=c-dL0p}wFxuf>a&nphnT%Thq zoWQ55SsK2k%7nRqIxih~AqF(zuI zqv9pR9#zq+Kuf=Mjz=ji+dRp8QT6Bd5Wb{#u)qsn)L80CxkBZVi{pgnM#!>tBi|tZ zk*m`CLmg&)=ABVU?8ZWT0|7u!ZD}^@dF{Sr!W>46hXqBT^td8)P7xYE(+7hy<~aiD z1v?;x>OM$lc^O4XfmpGqCnTGksb5-C^{Q=vc1SJ0tth95p*EbUey*d?iWQpubr;zk zen$E61HmVTAC}082ZRne-zwQk2zNOXF5Z(_z_mZUM)4bn=i!@8(GuyPe68H>2!|&HG#O7n$=E7u-Vj}4p z3!Us?dFn>oT^>U%>B7E4FZz`Cm@1xb;RujYk29X>(wi&c50`f!7wxgC6hiOW-IXzS@ov+nv7hyEl(xP-BR~r}0 znex*KJK+f%WwIY-VPy|v2|qtf*W3#V7XO%@TQ`rR!Ggi;9`AgQ zvgt0_iNVe(n(=Ph?b92Dy~wYWg=B9(VGETgpyH|!egYdvUcYDE&gWF479}}=N;EmvhA>05OpNMYOEC-dWs9QJYlG0?N;u_yx=q|e z=FfjD`yuB)GTO(#Mgi!C%~`S)gq636I)`~ zi0R-#t@QlfV>w9Z!p`3Q+z-0ub2m^Ui1gdO4m^7yL?M*c0F+S^9{RTcB{V)g^TogO+5xrB`@?$WQCm<_p4HKD`z6` z^RxEY?ZBI1xU%(%0i~kMzk9!b9SZUeiM;Z87k}XcZTmr9>sn)!Mcb~DVQ0wDnWu@&{JqT-2cJY%4**;lIyur9HB{6P`06yJB0)f3d9&QUTcX=HT) z#lqu33&8WHYHp>9tr@zXb}QKF#OCmphpEf{;v?=ED;IvSH*8#W7NoM+r zR0F3uz;MA)J-36e&r)k47=Cz`8ny=I9`YY+zyn!B@Sb$QHK@YQ7AzPT$kztue36A>5K}2P3T)If3Fn>KO40X0K>jA9fTk2XGE9nd#=-nQv7UYB#lO|F$Cm^clfUm zb5d?AQ0+{Jr=TBW_8a_h?#xAj&EI_#e{4U*gDi2J`E+-dv$JvW3u%vfDHo z`c0aE&A~(BfUQbyPQN-2tmS}zg0-F>q7B*pIt6Rbtn3$bWMdmi#V7UD>ALjHP z>e$IB)q!!0%)WF6Q_6e+vBN}|7pEjIsbr@cdfF_FQW_=K?b{*~HT}5H0smfdeVXGt zQSi+iVtBM^MCxgp_6D$>pLw4wxbhbn{EQCsV^92O78b)ixyYisPu1G1xcirGy-<`; z)iQ#r6NPCN8&(>>SU=RF019HsH%~*L<&JKokJ22OD*Um>m46NnFYga4C=-_=pZAPc zdBe8Jd2gcFi8sctm)-ffDp&8B;8=vh11INS!*(E!d*hQCa|vF~^!>=|&4>NPtqWX3 z#A^-`Sp3;$5l#$?SJJm@+A>pwc@qp>eQ4er6u^wbjmgqv1=>mD0(i`sA6FPkZ<&H8Bou7)cki|D>e{tQUvX0fO^0u;fuyYrPIuXzpMMcg)^cc9GZ$e^6TD`jM$z>C2*}TJTP3 zgDD*2@~FsLmBo1(=|kdG|5izC7}2&Yd<&1`u{0J81Ab}eKhxtnO->b56{u1BSAZSp z@Av6pArRGu?9YZI_9VI7Z7K|y<49uafVi*!+Nzc?qCMZ@4UnEgcE z2DF06g@^jEv|v;VgZTcdeD%p`&mevs)&kuJCixDknMuia|l)3;~QOi?dipSLo&E*9Af!JNb`6e_OL>16hOo*pEv zRO>gwL(NcJwG%H1DVLos03yZmda~?3${7g%X-feCrE0yqz}KjmRk5?upOc zUh{Z$$I8-IJ9xNn*9*9vcAP1N4*aZFg;-9fUjQ8`%f3V&6j8+07X$f5Xpg2Rr41=; z-^t`jGWsyHE<)sY3Pgymm?)s_S@0y;@x(+{Xv?e)_9f(X@X@N-S_%1%ZMDv(956y| zv@uTvHd8PYva1ff z^a0)@bqrM5L8;7oRQNnk8~BsPV6weqnzbq{ceyh>{66rQ3XC(r!HNu($lqfMIYESSh;k&1Zt8u4#|V9MqGV_Gz)LP4 zopm^P6bGSz*jKYAhD#K^$AE_J+#eAZ`t<-!pe*jOc7AY7yikIlwuFWpQ{;Y{1s5a2 z%{C4L`N(xMjK8}@jc6MAML;cRi-ed*s7~t(1GIRcZmAR1%szcK>yLtdA6cn{9@yu8 zuD$H9XXaN@8qx%W2a2k11<_VV>|X#a6VglvJ2A({BWfgb9|K+o-J=UYVoS#R^wAGbTeAP=#rvpY)DXL=~1-(EreIG%(MJy zEWXUlyey5ltV}@dnp|>SN2!Wef)oHs5?u;&Gfu|~$RzftW#a39%kFcoiT2g#O=!t* z7y^tf`6+L=*$2HM5*z-Qr$Z+l7s@e`UO(iV4)68)$VdIVBzfBdeJu5B8Jnk(7Iy$a zf#So?M?(D2Zon>DRaa`zv1r=x5F)9I0tMQhcMZawd|0$Rruj3{ajMaHjVgfn8Rov_ z-t+$99{H-RpS90k7P33(mA`}Wr-Jt3LK!K5s%&Jw299AZgftL9&}|#FHND#2h>6qe zK%sDY_WqnB(r-aW3+tv#Es~gl|HB5bZaB~({IJE3LIZzFsDwUH$VdY^_bk+}z~UVV z?4cuG>`RGm-qj-+nPp0XNI1Y_&-i}WAtROh-qQV9^T54EKeohjT+eq z*E3BktQV4yc*Enl*;-?6Ghz4h5`xs=#hlhIW@8lOj_De zti8l8R`k~;^?r3|iO2wicO*h@xPEoM!czJyI`&2I_I(_}PQcLJ$P3`ruG)R=_-@%j zKBt-RTO0?x?&;W$Is1(QE6PhlqR}~cEyXv)G&RdBj2T+Z3{+O6yf!CGZb>|VSKyCr@gQ*_YlwjDTWiC!cP|?W9&6$$$^GD*QYUlOAsSg z^spRdFRt?Z(v0VnqYD#6y^3sntfKMFm4)cyy(b-0=-o?uZ(?L`_DG>IS*YIgnsG6; zIKPQb?;ipQcnYqF6IES%-A2M;)rB|E0WG>|ZU$>8s1C(_}t!?hg zY{n)UZ}w=SM)B){X-3ni%xoo&snD|*=`C-hT1$O4X$dzi?N_AO-HGX zB7S=mqJ7xDzH=G{;OK8MB2}BITmU@ft1yGc_Pei+ER; zAlIYFQX+Ex9`>=9|Ju9;5T^G`^i<-Kk#&;8z>F!G|0G%(F+7^mFkj2jF0;EXhc)D} zFv68KGLq1}{^6~?1^XeOsXlu&Ah_R()XXARX-p2fTLP*bAe%6ZM?P-?ryU$fP9$*M zmc-#Df5OH=EL7u~xB&r)qRc-QD-|WhD=A!pHXm#eDySc*Dm&nMWRcQ^(-#(!f>&S( z#h@;CXx(A-xsQ^W>}~(hV8*i)RbYA;E=kTF~IMp!EPI2=0DLgXjGZy703KN9A~03#CTC1@`n6ypw0Nd}NBP1972 zB`d+w_z4WtU_NByV(c zok=9S^tlyLemV4RR&q7^vphrJ~fc@1#`uS`xJskehRt1Iv| z>;niokcd^=BF@X~X+Nqmnf=VuBcj*fr#dUmF>DsMlXs6-stQwZ6MMTTHBWu63t-1E zWS$q4m^tW&59VQ3?FvYOCFh}+KE0PIIKm4FFC1#$BE$9QiSk)kus6s*P{of4NShk< z1%tggN35%(ePhmW^6}ls#u7BDLjh!oNgZHZC8#SN(tZoLMzl73eey;vHaVdT16&M4 za^wQiO&3m415)d)-DhXJ$@u2V!>daE*jU+;aJS000VVLnPY82T^zxCH-y5vS*6u|3 zlif_nrm@?Ug~;T@td`$*l@K_DQx#`U4Ir>_r`Vm*+Qi{u8E zX_v8O*dVQA(jgr~nu1nBmPtITYz9EytRvnRk+fVTyD9fIo%DPkYqn&n7CW#l+5*Qv*2~RBb>F!3Xvw{5WTxV+!ibK!q0uxuE9IXrOb9H4!%q`C7Ed zx_(qoGJojC+AD1n}W3$i8@~sTk+E+1k z=d23PPkYBh#kAGN`RVt4;U;kAN#BXn7CLdzo<~HNDIV!&x9p>j7hxX>0RFg{HJCwa z19|kN850Gy4IM}!X_x(<5bA}YEp~%#5`w0m*XwEW*;s^Ds>&{O%M zZ6#K-EuxwnT(bLnY{7xy10v2Sb%QoK=4$v?kd(&!hp#{#qhlA+4vShRG-I=Rz)u{PaiFUK%Y8*R* zvP?sqs7(&KecHqVx>?D7LO3;wGh)}ILDXT7OY6!t-t_$vzNbi7GDaP0^8}|Gm3Q)Zw_9W& zT!{^n!pwkgKU`7E5P-M2R>G^65pUWNOkq)|E~?M~gC%k|jj={g(=nTQbYC z$}?QV>>tkXgz)dBby-z7qdoC!XveSg&?V?F&CaOtkCs#JA^>EmTfS~57_*`-CN2SN zGJdZ`pj_UFDE1;cVV#$vN3qi>jf6Q0vI197l+sT+&Gs8~k7n{d8$W#ZzUD9T*Z@LR zR;XQC2+jQZA~ZB!)Jm>A_%YBwnx70<14d!0NnxU82vvo*yjz@_SImv3jLz z_B<10dc@U5fdIyW!mL*^DQKkq>3qC?G*A)t(Re9$S;x0iF6>6jGd}Wo?&8R+Q9^kx zKg;DJG}(eDiC>kX3apk;<}!q^hKYz9zRbRDXUrf|S})^rvJP&3_*$%9n zKj2JYQ9|p5WJp6nA`A03CH}TvwG+sBjb%Mr+0&l=f^%j0>OD5bMl}JsTJiiT{iiCX zVpk$#rz?PB4~%O0`+9aiHMj)bvfHG~No9+S-k#7GZ}|~i;M4l7px$<7*7ke2ms|7T z20PBDuPnWO{)xp1^n#VUlT`tcXpyZHAxN)23)wPHi&&Jd&7?^04<&qRDPQR?GJh@! zv|CF4J9Q|$=OJF}K|LKcASrZ&!!pj^NSij2>`x9Aawqq%c80li{!aVD+ggvqiw(VydV|dNJ=uQ3MS*UCj`JYI)MPe-H5y`-mgk2&Zoa^a z!EbGVOe)s6i^)m~_pHem;SSvVku!59^ZoBpyAP;6!n1Jl<{`KBf+OT@hnHe-impV494L_lMuQA+==mj)R$9=&4IU{JY-j+pnU3CY0VXoBr6M(pGn&SvX~|&q0vDA* ztkgdL!ak$g*`5Z}PG<~Ss!z5nBnzQV59^%<=M#sY&kiSZkl8dbJMzdDvG{S6G(ynx zA@DTfaxQ!FWd^Y8pyc%!08rs>dY^+ZGAVj|1~kZgC>Hdvr+i*dp>>>GAPy5bJ--u( zcyxdHHb@d&M0icD)}yiXLe(BVy77!1a&jMV@??IT2_(1$d?XWX3kIWVMon)$yw*t z-M*brxC>Od)4FUM7vqP8+6Fqfr5K*`Ned=%4E27dFvH~}lZLMppB~ch4{~uz?=2${ zHi%T4+ZAz;6A?!9TE@Z7k0eih$Lcd-fo(MChH^E5FB`z>VpC|G zy&8z}Tx^EV6V|B33lE$eq=Kk0?akvh+NYUu9ALhADPzSDhvbfPTd8p*h;83cO__Q` z%07|Ilm$|iX}3Bx=c@H)vSBl!Y>8rMeFCxo1wTHV>Su1EW&)P`>MmF^%V+YqBYEBv zlsZo5Q+l7C!dsIF(i50w1arc@Qw+f&9+>$eruc5jKYt`BK>1s)u-8K0gM}U{?YsVP z*6ytmP=R^H3IdekS;!EM2<`qK+zlF&4xb@#BCfqTiD<%-_yQZq zfIn`vPzomt^ELqDE>vCRyHr}jcizX`#!uG+EVI`b(&9spda5L8$l;RyB(8F31G?%f z2TL)x=n7OOoyqN9<2dO0G0RHB@=_D-W~jvcQwwtY0V_~e{MU`DcX-R$)+~&*mG>pa zxfvf>mLgPGSCRRo6bOfGi4frt5nN<+k1uj!t>4oieaixTB$mL)>KWoYdOQ((D*H;U zjo#lL(3i9d%>`SHvmIb4a*8=Dn)Q(LgA2j5<$?J^1&` z;8D%P6c(VM$)JNAdJ7mHaLH^=NI5c42;b(YAPysC>P*v-aP#UoHZW2x50OV+-zs-O z99AKvPbWHGb*7(aR+W-|^+IRSb*}7n9E4+ z!N@=_p0A&yrAdQv^J@iK-@V~_49+lFY!CoiH`g(Hs!5cG}Q69oeyL;86Sm?lThEHsbNQ&0f zOYqg{`o~#%Vs)`U{Ke(!P41>Qy}t)ZijMyh8W1OPm;8Yx4Sb%juqm|T&U@JI`9C3o z^+#94NPrbkkf5~%OQI?0ybT3ZnD=itvdQ14ERFwZWI&_*YLq6;0lz(?T-wlpN+q0# z00*I}CJyIm%lfU(y4r@8yz>5T!owX?IY*lGF(GPj(7ei9pJ9A9K5pvXbMQf8reu+% zmBMI^viP&-x5jZUU8)h03T1do04=l!q-Y%$%?NGR3|5enkHANl#dXpw$PdM)FmmxwR|K%XsNUd95)=5~dB3Jr=Fs^lh5ga*;7?Kse{BW4Zp5T6JYy zepo{!Z5A71nkn$2+U$eSIDmCCgfiZWW2)d%4msP<8kfT-i1=AY$2z$A5#a^zOu3if zYe#_EQmePSB$P{=Gq%1!cded-VwZT#9V*X}TiZ^T^H6)9Dm%yOeFe1oWBmM)-m*<7 zPZZIFOYM_sHJoF`7tV=3l2-4-K#~d8(b{aBQ^{)YWb~#z!1mXDEx<#4!tjwYN-RAU8 zk(>cFge4$6{`HFci3`zX=(uB!5W^HpBH@iab|-QfK^lQsIsT9DkfjONC3%n7(YlBU zxkxlr5hn8Sgztu^Md~P);Td~I`e?_d`G7K2AE}{-ZA#<06V3r!CIE+~`gwY82f`k7 zhorzneWQ25V}&^Pw`v$+FY$&S8zT92N^1d~v}pG(t4-j$0G!z+@hzhW2{reULB< z^E^0A7GN-k5!nR@sVPtY)IskA|L0t#4KyXIR{fP_LHhyQ|DNppJtZwnN8}Lv$IlW!S&;s$vtvhFjq6|0^gb*00M*}FY0nttt_znX69edBLhOjo-68I>OjMEYM z61ahNAFC4a5=cPs|Nb+qovbQH{Cvnb=%hXwm<<8W{QlXHh5y&v+`^Ys4nc48-Tw1! zG&yprNOzh;gP^2|uD@DCP=W6K>xT^VCl5pgWjmn|0Z08cKma)TXU8LQ;9E#=4sc$e z1td5b%wcHJC(xysUzY}fy^!EEFl%N8Lv!T7i(i*ifK*W6)c=1;0SX*PHd#qV6|eRp zHwnZi1PO}<_WK?8KxvA`_I75L<}4O0?v{2Y_U_IIf5`#{1`dV?X7SGR)`@fp{O9AE z9q7KbAR<5kGzKd~{;Md^c5Maam*##a79oO&4z?yvZY;*XlWa93tA7pZWVF%Ymr{=d zgAx`0N2ya;a;k`ctI8d8FffC6zn0?P$(k5{k%1D>;20J^SeN6~L73dw>4B>Z1NjX-RWpq8MPR~EkuN;W#i`j_@MVt#4A3mP=!T##T{##-+C zP@qjJXn+X+5^R&@FOq|ile6i+2c3i`t@}&LgPzmBq*Q_l`$NiV&>^CpIbkm{0T>vp z<^Qrz--6g$4z^CFp!QtMJevuEdy(Mr}m%`s(h0*|8fL?{k zyVn0@hHwlXS0sh;q=y&w2$$!!RvW5AE=_7lTC2&im z;=j-j-~UAa+3n?b@}W71{CDq{zhssHO~YRki-L#o_?OVG(*l2~yZ|%m4|V)%5TJ&@ zr_Br6k|5KAtm9u+2W|(9g$Kt*5jsj}`9*r$``2nw2M-g5xo`{3WJInQSUG$-i~`XCd6*70?m(cUfGb ze}mUzDnI=Km(6N|z`)L568>2^=XX%k=l|5v`Tet3ackUB0bv-RV*irwSt9-qHU29l zoBUV4+V9w$JWx;#D0$5o0r1$|t|6mdS^F8z{YX!K02u?r)TlW{k-og3bOO(%Jo8>>!0ZEWR%KJ0J<#%Zy zdwzuu{k^CCU4MBjcgnx?hr3n!m;RjMGyW0iKl1z;l=eG0e&R1OupS8<9RT^P;HM12 z^@VT!h5NekC+^Rvrr&YtQ@?QkiE;W}((ZSH*1yo+yp?~UBUiHikQDG|NXYN(hS`7E z|N2p<&2a0?kU zH|6$$I4IyG{9*XNzPCZ9RnsC-_&-zXPshmbH}kv%I-r1Kp*&*nI4Xe%J!HEev4IIF zAo|S>a0msQ6mTc-YvwP3J7E1u|MQ9GcRDgSIA|#UdH(r*FzBHEM89q~Wjpkjmc*)a z{?O9@@7(bI*}DDCPK5&cp@QSG7z#1%|1#jln!oKt>-GOp(7${@U^JlLU}P{bBxul% H1?>L-^;%Xp delta 50154 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 BubBV< diff --git a/Tools/zxcc/zxcc.exe b/Tools/zxcc/zxcc.exe index c51614dac121daab13358f7d735a921b4ffbd607..a66b00537cad6bb7802f4422c18217a63ba23e26 100644 GIT binary patch delta 28288 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 delta 39429 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