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 1ae919e4..25238d8a 100644 Binary files a/Tools/zxcc/zxcc-src.zip and b/Tools/zxcc/zxcc-src.zip differ diff --git a/Tools/zxcc/zxcc.exe b/Tools/zxcc/zxcc.exe index c51614da..a66b0053 100644 Binary files a/Tools/zxcc/zxcc.exe and b/Tools/zxcc/zxcc.exe differ diff --git a/Tools/zxcc/zxccdbg.exe b/Tools/zxcc/zxccdbg.exe index f5f2adfb..410e24ca 100644 Binary files a/Tools/zxcc/zxccdbg.exe and b/Tools/zxcc/zxccdbg.exe differ